#!/usr/bin/env python3
import itertools
def cycle_once(val):
"""
Cycle a cell one step according to the rule:
0 -> -1,
-1 -> +1,
+1 -> 0.
"""
if val == 0:
return -1
elif val == -1:
return 1
elif val == 1:
return 0
else:
raise ValueError("Invalid cell value; must be -1, 0, or 1.")
def apply_cycles(val, n):
"""
Apply n one-cycle moves to a cell.
(Since three moves return the cell to its original state, we use n mod 3.)
"""
for _ in range(n % 3):
val = cycle_once(val)
return val
def apply_moves(grid, row_moves, col_moves):
"""
Given a grid and tuples for row_moves and col_moves (each move is 0, 1, or 2),
return the new grid after applying the moves.
Each cell (i,j) gets cycled (row_moves[i] + col_moves[j]) mod 3 times.
"""
new_grid = []
for i in range(4):
new_row = []
for j in range(4):
net_moves = (row_moves[i] + col_moves[j]) % 3
new_val = apply_cycles(grid[i][j], net_moves)
new_row.append(new_val)
new_grid.append(new_row)
return new_grid
def compute_score(grid):
"""Compute the total score (sum of all cell values)."""
return sum(sum(row) for row in grid)
def print_grid(grid):
"""Print the grid"""
for row in grid:
print(" ".join(f"{x:2d}" for x in row))
def input_grid():
grid = []
print("\nEnter your 4×4 grid (one row per line) with no spaces:")
print("Use '-' for -1, '0' for 0, and '1' for +1.")
for i in range(4):
while True:
row_input = input(f"Row {i+1} (4 characters): ").strip()
if len(row_input) != 4:
print("Please enter exactly 4 characters (no spaces).")
continue
try:
row = []
for char in row_input:
if char == '-':
row.append(-1)
elif char == '0':
row.append(0)
elif char == '1':
row.append(1)
else:
raise ValueError
grid.append(row)
break
except ValueError:
print("Invalid input. Use '-' for -1, '0' for 0, and '1' for +1.")
return grid
def print_moves(row_moves, col_moves):
"""
Prints the optimal move notation in clean format.
For example, if the row moves are (2, 0, 1, 0) and the column moves are (0, 1, 2, 0),
the moves list would be:
["R1", "R1", "R3", "C2", "C3", "C3"]
and the printed output would be:
R1 - R1 - R3 - C2
C3 - C3
"""
moves = []
for i, m in enumerate(row_moves):
for _ in range(m): # add "R{i+1}" m times
moves.append(f"R{i+1}")
for j, m in enumerate(col_moves):
for _ in range(m):
moves.append(f"C{j+1}")
if moves:
print("Optimal move notation:")
# Wrap every 4 moves per line.
for i in range(0, len(moves), 4):
group = moves[i:i+4]
print(" - ".join(group))
else:
print("No moves needed; the puzzle is already optimal.")
def solve_puzzle():
grid = input_grid()
print("\nInitial grid:")
print_grid(grid)
initial_score = compute_score(grid)
print("Initial score:", initial_score)
best_score = -float('inf')
best_row_moves = None
best_col_moves = None
best_grid = None
# There are 3^8 = 6561 possible combinations of moves.
for row_moves in itertools.product(range(3), repeat=4):
for col_moves in itertools.product(range(3), repeat=4):
new_grid = apply_moves(grid, row_moves, col_moves)
score = compute_score(new_grid)
if score > best_score:
best_score = score
best_row_moves = row_moves
best_col_moves = col_moves
best_grid = new_grid
print("\nOptimal final grid:")
print_grid(best_grid)
print("Best score:", best_score)
print()
print_moves(best_row_moves, best_col_moves)
print()
def main():
while True:
solve_puzzle()
# Ask the user whether to quit or solve a new puzzle.
choice = input("Enter 'N' for a new puzzle or 'Q' to quit: ").strip().upper()
while choice not in ('N', 'Q'):
choice = input("Invalid input. Please enter 'N' for new or 'Q' to quit: ").strip().upper()
if choice == 'Q':
print("Goodbye!")
break
if __name__ == "__main__":
main()