mirror of
https://github.com/InternLM/InternBootcamp.git
synced 2026-04-27 17:23:17 +00:00
init-commit
This commit is contained in:
commit
18a552597a
3461 changed files with 1150579 additions and 0 deletions
174
internbootcamp/libs/campsite/campsite_generator.py
Executable file
174
internbootcamp/libs/campsite/campsite_generator.py
Executable file
|
|
@ -0,0 +1,174 @@
|
|||
|
||||
import random
|
||||
|
||||
def generate_campsite(n, m, k, seed=None, random_rate=0.2):
|
||||
"""
|
||||
Generate an n×m Campsite puzzle with at least one valid solution.
|
||||
|
||||
1) Randomly place k tents in the grid with no two tents adjacent
|
||||
orthogonally or diagonally.
|
||||
2) Place trees so that each tent is orthogonally adjacent to at least one tree.
|
||||
(We can add extra random trees to keep the puzzle interesting.)
|
||||
3) Fill remaining cells with 'X'.
|
||||
4) Compute row and column constraints, i.e., how many tents in each row/column.
|
||||
5) Return the puzzle grid (with 'T' = tree, 'X' = empty) and the row/column constraints.
|
||||
|
||||
:param n: number of rows
|
||||
:param m: number of columns
|
||||
:param k: number of tents to place
|
||||
:param seed: random seed for reproducibility (optional)
|
||||
:return: (grid, row_constraints, col_constraints)
|
||||
where grid is an n×m list of lists with 'T' or 'X'.
|
||||
row_constraints is a list of length n,
|
||||
col_constraints is a list of length m.
|
||||
"""
|
||||
if seed is not None:
|
||||
random.seed(seed)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# STEP 1: Randomly place k tents with no adjacency among them
|
||||
# -------------------------------------------------------------------------
|
||||
# We'll build our final "solution" using 'C' for tents, 'T' for trees, 'X' for empty.
|
||||
solution = [['X'] * m for _ in range(n)]
|
||||
|
||||
# Keep track of tent locations
|
||||
tent_positions = []
|
||||
|
||||
# We want to place k tents so that no two tents touch (8-direction adjacency).
|
||||
# We'll keep trying random positions until we place all k (or we exhaust attempts).
|
||||
# In a real puzzle generator, you often do more sophisticated logic to ensure
|
||||
# you can place all k tents in large grids, but let's keep this straightforward.
|
||||
|
||||
# A function to check if a tent can be placed at (r, c)
|
||||
def can_place_tent(r, c):
|
||||
if solution[r][c] != 'X':
|
||||
return False
|
||||
# Check 8-direction adjacency
|
||||
for dr in (-1, 0, 1):
|
||||
for dc in (-1, 0, 1):
|
||||
rr, cc = r + dr, c + dc
|
||||
if 0 <= rr < n and 0 <= cc < m:
|
||||
if solution[rr][cc] == 'C':
|
||||
return False
|
||||
return True
|
||||
|
||||
all_cells = [(r, c) for r in range(n) for c in range(m)]
|
||||
random.shuffle(all_cells)
|
||||
|
||||
placed = 0
|
||||
idx = 0
|
||||
# Try to place k tents in random positions
|
||||
while placed < k and idx < len(all_cells):
|
||||
r, c = all_cells[idx]
|
||||
if can_place_tent(r, c):
|
||||
solution[r][c] = 'C'
|
||||
tent_positions.append((r, c))
|
||||
placed += 1
|
||||
idx += 1
|
||||
|
||||
# If we failed to place k tents, raise an error
|
||||
if placed < k:
|
||||
#raise ValueError(f"Could not place {k} tents without adjacency conflicts. Try a smaller k.")
|
||||
#return False, f"Could not place {k} tents without adjacency conflicts. Try a smaller k."
|
||||
print( f"Could not place {k} tents without adjacency conflicts. This case place {placed} tents")
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# STEP 2: Place trees so that each tent has at least one tree neighbor
|
||||
# -------------------------------------------------------------------------
|
||||
# For each tent, check if it already has a tree neighbor. If not, place a tree
|
||||
# in at least one valid neighbor cell. We can optionally place more trees to
|
||||
# make the puzzle more interesting.
|
||||
directions_orth = [(-1, 0), (1, 0), (0, -1), (0, 1)]
|
||||
|
||||
for (r, c) in tent_positions:
|
||||
# Check if there's already at least one tree neighbor
|
||||
has_tree_neighbor = False
|
||||
for dr, dc in directions_orth:
|
||||
rr, cc = r + dr, c + dc
|
||||
if 0 <= rr < n and 0 <= cc < m:
|
||||
if solution[rr][cc] == 'T':
|
||||
has_tree_neighbor = True
|
||||
break
|
||||
|
||||
# If we don't have a tree neighbor, place at least one tree
|
||||
if not has_tree_neighbor:
|
||||
# Gather valid spots around (r, c) where a tree can be placed
|
||||
valid_spots = []
|
||||
for dr, dc in directions_orth:
|
||||
rr, cc = r + dr, c + dc
|
||||
if 0 <= rr < n and 0 <= cc < m:
|
||||
# "T" or "X" or "C" – if there's a tent, we can't override it.
|
||||
if solution[rr][cc] == 'X':
|
||||
valid_spots.append((rr, cc))
|
||||
# If valid_spots is empty, we have an impossible puzzle. Normally,
|
||||
# it shouldn't be empty because the tent wasn't originally placed
|
||||
# adjacent to another tent.
|
||||
if not valid_spots:
|
||||
raise RuntimeError("No valid spot to place a required tree! Puzzle generation error.")
|
||||
# Place a tree at exactly one of these spots (or random choice)
|
||||
rr, cc = random.choice(valid_spots)
|
||||
solution[rr][cc] = 'T'
|
||||
|
||||
# Optionally: place extra random trees for puzzle variety
|
||||
# For demonstration, 20% chance to place a tree on any empty X,
|
||||
# so that the puzzle looks less minimal. Comment out if undesired.
|
||||
for r in range(n):
|
||||
for c in range(m):
|
||||
if solution[r][c] == 'X':
|
||||
if random.random() < random_rate:
|
||||
solution[r][c] = 'T'
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# STEP 3: Now we have a "solution" with tents (C), trees (T), and some X's
|
||||
# We want to produce the puzzle's T/X grid (the solver must find
|
||||
# where 'C' should go).
|
||||
# -------------------------------------------------------------------------
|
||||
# To create the puzzle for the end user, we remove 'C' from the final puzzle
|
||||
# and replace them with 'X'. So the puzzle that the user sees only has 'T' and 'X'.
|
||||
|
||||
puzzle = []
|
||||
for r in range(n):
|
||||
row = []
|
||||
for c in range(m):
|
||||
if solution[r][c] == 'C':
|
||||
# Remove the tent from the puzzle the user sees
|
||||
# so the user can solve for the tents
|
||||
row.append('X')
|
||||
else:
|
||||
row.append(solution[r][c])
|
||||
puzzle.append(row)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# STEP 4: Compute row/col constraints from the actual solution with tents
|
||||
# -------------------------------------------------------------------------
|
||||
row_constraints = [0] * n
|
||||
col_constraints = [0] * m
|
||||
for r in range(n):
|
||||
for c in range(m):
|
||||
if solution[r][c] == 'C':
|
||||
row_constraints[r] += 1
|
||||
col_constraints[c] += 1
|
||||
|
||||
# Return puzzle (trees + X), row/col constraints,
|
||||
# and optionally the *solution* grid if you want to verify.
|
||||
return puzzle, row_constraints, col_constraints, solution
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# EXAMPLE USAGE
|
||||
# ------------------------------------------------------------------------------
|
||||
if __name__ == "__main__":
|
||||
# Example: Generate a 5×7 puzzle with 6 tents, with a fixed random seed for reproducibility
|
||||
gen_puzzle, row_cons, col_cons, solution = generate_campsite(n=5, m=7, k=6, seed=None, random_rate=0.2)
|
||||
|
||||
# Print puzzle in a readable way
|
||||
print("Puzzle Layout (T/X):")
|
||||
for row in gen_puzzle:
|
||||
print(" ".join(row))
|
||||
|
||||
print("\nRow Constraints:", row_cons)
|
||||
print("Col Constraints:", col_cons)
|
||||
|
||||
print("\n(For debugging) One valid solution used to generate the puzzle (C = tent):")
|
||||
for row in solution:
|
||||
print(" ".join(row))
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue