#!/usr/bin/pyhton

# Solver for the Lights Out game
# http://en.wikipedia.org/wiki/Lights_Out_(game)
# 
# released under the GPL

# each problem has only 2^25 potential solutions
# we can brute-force it in a few minutes (not very elegant, I admit)
# 
# furthermore, the trivial problem has exactly four solutions
# (and this program can prove it by exhaustive search)
# so every problem has exactly four solutions
# 
# to put it differently, the group of the solutions of each problem is a 
# subgroup of all possible solutions, isomorphic to the (Z/2Z)^2


# lights are numbered from 0 to 24
# any convention can be chosen, the only rule is to be consistent

# conversion between [0..24] and [0..4]x[0..4] notation
def coordsFromId(i):
	return divmod(i, 5)
def idFromCoords(x, y):
	return x * 5 + y

# I am too lazy to hardcode this
def neighbours(i):
	'''Returns a list of the neighbours of light i'''
	r = [i]
	x, y = coordsFromId(i)
	if (x > 0):
		r.append(idFromCoords(x - 1, y))
	if (x < 4):
		r.append(idFromCoords(x + 1, y))
	if (y > 0):
		r.append(idFromCoords(x, y - 1))
	if (y < 4):
		r.append(idFromCoords(x, y + 1))
	return r

# list of the lights to switch when light number i is switched, including i
nb = [neighbours(i) for i in range(25)]

def switchLight(i):
	'''Updates the board when a light is switched
	
	also updates solution'''
	for j in nb[i]:
		board[j] = not board[j]
	solution[i] = not solution[i]

# initial problem
# True = light on, False = light off
board = [False for i in range(25)]

# Set your problem here (or write an input function)
# example:
# board[idFromCoords(1, 1)] = True
# board[idFromCoords(1, 3)] = True

# solution for this problem
# True = switch this light, False = don't touch
solution = [False for i in range(25)]
# list of all possible solutions
solutions = []

def checkSolution():
	'''Tests if the problem is resolved
	
	relies on the contents of board
	adds solution to the list of known solutions if necessary'''
	if True in board:
		return False
	else:
		solutions.append([solution[i] for i in range(25)])
		return True

def iterateSearch(n):
	'''Iterates recursive search
	
	n first values of solution are fixed
	tries the two possible values for solution[n] and iterates'''
	
	if n == 25:
		# end of iteration, do we have a solution?
		checkSolution()
	else:
		# explore both branches
		iterateSearch(n+1)
		switchLight(n)
		iterateSearch(n+1)
		switchLight(n)
		# re-switching is mandatory since we use a global variable
		# that must be left unchanged after executing the function

# start search
iterateSearch(0)

# sort solutions by inscreasing length
solutions.sort(lambda x, y: len(x) - len(y))	# python 2.3
# in python 2.4, use solutions.sort(None, len)

# print results

def prettyPrint(data):
	print "+---+---+---+---+---+"
	for x in range(5):
		for y in range(5):
			if (data[idFromCoords(x, y)]):
				print "| O",
			else:
				print "|  ",
		print "|"
		print "+---+---+---+---+---+"

print "Problem"
print ""
prettyPrint(board)
print ""

print "Solutions"
for s in solutions:
	print ""
	prettyPrint(s);
