init-commit

This commit is contained in:
lilinyang 2025-05-23 15:27:15 +08:00
commit 18a552597a
3461 changed files with 1150579 additions and 0 deletions

View file

@ -0,0 +1,140 @@
def solve_campsite(grid, row_constraints, col_constraints):
"""
Solve the 'Campsite' puzzle using backtracking.
:param grid: A list of lists of characters ('T' or 'X') of size n×m.
:param row_constraints: A list of length n with the required tent count per row.
:param col_constraints: A list of length m with the required tent count per column.
:return: A list of lists representing a solution (replacing 'X' with either 'C' or 'X'),
or None if no solution exists.
"""
n = len(grid)
m = len(grid[0])
# Copy the grid to store the solution without mutating the original
solution = [row[:] for row in grid]
# Track how many tents are currently placed in each row/column
current_row_tents = [0] * n
current_col_tents = [0] * m
# Precompute which cells are adjacent to at least one tree
# because a tent can only go orthogonally adjacent to a tree
adjacent_to_tree = [[False]*m for _ in range(n)]
directions_orth = [(-1, 0), (1, 0), (0, -1), (0, 1)]
for r in range(n):
for c in range(m):
if grid[r][c] == 'T':
for dr, dc in directions_orth:
rr, cc = r+dr, c+dc
if 0 <= rr < n and 0 <= cc < m and grid[rr][cc] == 'X':
adjacent_to_tree[rr][cc] = True
# Helper function to check if placing a tent at (r, c) violates adjacency constraints
def can_place_tent(r, c):
# If the cell is not empty or not adjacent to a tree, cannot place a tent
if solution[r][c] != 'X' or not adjacent_to_tree[r][c]:
return False
# Check row/col constraints if adding one more tent is still valid
if current_row_tents[r] + 1 > row_constraints[r]:
return False
if current_col_tents[c] + 1 > col_constraints[c]:
return False
# Tents cannot be adjacent (orth or diag) to any existing tent
# We'll check the 8 directions around (r, c)
directions_all = [
(-1, 0), (1, 0), (0, -1), (0, 1),
(-1, -1), (-1, 1), (1, -1), (1, 1)
]
for dr, dc in directions_all:
rr, cc = r+dr, c+dc
if 0 <= rr < n and 0 <= cc < m and solution[rr][cc] == 'C':
return False
return True
# A list of all grid coordinates we might try to fill
# Sorting them can sometimes help performance, but plain is fine, too.
all_cells = [(r, c) for r in range(n) for c in range(m)]
history = []
# We'll do a simple backtracking approach that tries to place a tent or not
def backtrack(idx=0):
if idx == len(all_cells):
history.append(f"All positions have been tried. Check if the current solution satisfies the row constraints and the col constraints.\n")
is_satisfy = all(current_row_tents[r] == row_constraints[r] for r in range(n)) and all(current_col_tents[c] == col_constraints[c] for c in range(m))
if is_satisfy:
history.append("The current solution satisfy all the conditions. Find the solution!\n")
else:
history.append("The current solution does not satisfy all the conditions.\n")
return is_satisfy
r, c = all_cells[idx]
history.append(f"Select ({r},{c}), check if the tent can be placed in this location.\n")
# -----------------------------------------------------------
# OPTION 1: Place a tent in (r, c) if it's a valid move
# -----------------------------------------------------------
if can_place_tent(r, c):
history.append(f"Tent can be placed at ({r},{c}). Place a tent at ({r},{c}).\n")
solution[r][c] = 'C'
current_row_tents[r] += 1
current_col_tents[c] += 1
if backtrack(idx + 1):
return True
history.append(f"Since current solution fails. Change the current location ({r},{c}) from a tent(C) to an open space(X). Then retry.\n")
# Backtrack (undo placement)
solution[r][c] = 'X'
current_row_tents[r] -= 1
current_col_tents[c] -= 1
else:
history.append(f"No tents can be placed in ({r},{c}).\n")
# -----------------------------------------------------------
# OPTION 2: Skip placing a tent at (r, c)
# -----------------------------------------------------------
if backtrack(idx + 1):
return True
history.append(f"Since there is no tent placed at the current location ({r},{c}), we backtrack to the previous location.\n")
# If neither placing nor skipping yields a solution, fail here
return False
# Kick off the backtracking
if backtrack():
history.append('\nThe final answer is: '+'{'+'\n'.join([' '.join(row) for row in solution]) + '}')
steps = ''.join(history)
return solution, steps
else:
return None
# -----------------------------------------------------------------
# Example usage:
#
# Suppose you have the puzzle:
if __name__ == "__main__":
grid = [
['X','X','X','X','X','T','X','X','T','X'],
['X','T','X','X','X','X','X','X','X','X'],
['X','X','X','T','T','X','X','X','X','X'],
['T','X','X','X','X','X','T','T','X','X'],
['X','T','X','X','T','X','X','X','X','X']
]
row_constraints = [3, 1, 3, 0, 3]
col_constraints = [2, 1, 1, 1, 1, 0, 2, 1, 0, 1]
solved, steps = solve_campsite(grid, row_constraints, col_constraints)
if solved:
# Format the output as requested
print(steps)
else:
print("No solution found (but puzzle is assumed to have a unique solution).")
#
# You can adapt this to parse your input format, then call `solve_campsite`
# to get the solution, and finally output the result in the requested
# "[[row1, row2, ...]]" string format.