Clouds puzzles have the following rules: Place some clouds into the grid. Clouds are in the shape of rectangles and squares, and at least two squares wide and two squares long. The clouds cannot touch each other, not even diagonally. The numbers outside the grid indicate the total number of cells covered by clouds in the corresponding direction.
I have created a script that places rectangles into an 8x8 grid that do not touch anywhere. However, some of these puzzles have multiple solutions:
grid = [ [1., 1., 0., 0., 0., 0., 0., 0.],
[1., 1., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 1., 1., 0., 0.],
[0., 0., 0., 0., 1., 1., 0., 0.],
[0., 0., 0., 0., 0., 0., 0., 0.] ]
sumsofrows = [2,2,0,0,0,2,2,0]
sumsofcolumns = [2,2,0,0,2,2,0,0]
So I tried to create a backtracking script that checks if a certain puzzle has multiple solutions or not. I based my script on this thread. However, it does not work. (EDIT: The script always returns "0", even though there should be at least 1 solution) Step 1-4 of my script work fine, but step 5 and 6 do not. Any help would be appreciated!
import numpy
#array with three numbers instead of two: 0 = unsolved position, 1 = no cloud, 2 = cloud
gridthree = numpy.copy(grid)
for a in range(8):
for b in range(8):
gridthree[a][b] = gridthree[a][b] + 1
# STEP 1: clouds cannot form angles, they must be rectangles
# Any 2x2 grid cannot have exactly 3 pieces of cloud. The fourth cell must also contain cloud
def fill_angles(matrix):
dummy = 0
for a in range(7):
for b in range(7):
c = matrix[a][b] + matrix[a+1][b] + matrix[a][b+1] + matrix[a+1][b+1]
if c == 3:
dummy = 1
matrix[a][b] = 1
matrix[a+1][b] = 1
matrix[a+1][b+1] = 1
matrix[a][b+1] = 1
if dummy == 1:
fill_angles(matrix) #recursive
return matrix
#STEP 2
#the same function as in step 1, but for matrices with numbers 0, 1 and 2
def fill_anglesthree(matrixthree):
valid = True
for a in range(7):
for b in range(7):
if matrixthree[a][b] == 2 and matrixthree[a+1][b] == 2 and matrixthree[a][b+1] == 2 and matrixthree[a+1][b+1] != 2:
if matrixthree[a+1][b+1] == 0:
matrixthree[a+1][b+1] = 2
fill_anglesthree(matrixthree) #recursive
if matrixthree[a+1][b+1] == 1: #no cloud is allowed here
valid = False
elif matrixthree[a][b] == 2 and matrixthree[a+1][b] == 2 and matrixthree[a][b+1] != 2 and matrixthree[a+1][b+1] == 2:
if matrixthree[a][b+1] == 0:
matrixthree[a][b+1] = 2
fill_anglesthree(matrixthree) #recursive
if matrixthree[a][b+1] == 1:
valid = False
elif matrixthree[a][b] == 2 and matrixthree[a+1][b] != 2 and matrixthree[a][b+1] == 2 and matrixthree[a+1][b+1] == 2:
if matrixthree[a+1][b] == 0:
matrixthree[a+1][b] = 2
fill_anglesthree(matrixthree) #recursive
if matrixthree[a+1][b] == 1:
valid = False
elif matrixthree[a][b] != 2 and matrixthree[a+1][b] == 2 and matrixthree[a][b+1] == 2 and matrixthree[a+1][b+1] == 2:
if matrixthree[a][b] == 0:
matrixthree[a][b] = 2
fill_anglesthree(matrixthree) #recursive
if matrixthree[a][b] == 1:
valid = False
return matrixthree, valid
#STEP 3: the solved function
#checks if a binary matrix is solved
def solved(matrix):
# 1 check sum of rows
sumsofrowsmatrix = []
for i in range(len(matrix)):
sum = 0;
for j in range(len(matrix[0])):
sum = sum + matrix[i][j]
sumsofrowsmatrix.append(sum)
if numpy.array_equiv(sumsofrowsmatrix,sumsofrows) == False:
return False
# 2 check sum of columns
sumsofcolumnsmatrix = []
for i in range(len(matrix[0])):
sum = 0;
for j in range(len(matrix)):
sum = sum + matrix[j][i]
sumsofcolumnsmatrix.append(sum)
if numpy.array_equiv(sumsofcolumnsmatrix,sumsofcolumns) == False:
return False
# 3 check if the clouds form angles
for a in range(len(matrix[0])-1):
for b in range(len(matrix[0])-1):
c = matrix[a][b] + matrix[a+1][b] + matrix[a][b+1] + matrix[a+1][b+1]
if c == 3:
return False
return True
#STEP 4: the validity function
#checks if a matrix is still solved if you add i in (row,column)
def validity(matrixthree,i,row,column):
matrixthree[row][column] = i #i is equal to 1 (no cloud) or 2 (cloud)
# step 1: angles are filled up
matrixthree, valid = fill_anglesthree(matrixthree)
if valid == False:
return False
# step 2: create binary matrix
matrix = numpy.copy(matrixthree)
for row in range(8):
for column in range(8):
if matrix[row][column] != 0:
matrix[row][column] = matrix[row][column] - 1
# step 3: check sum of rows
sumsofrowsmatrix = []
for i in range(len(matrix)):
sum = 0;
for j in range(len(matrix[0])):
sum = sum + matrix[i][j]
sumsofrowsmatrix.append(sum)
for i in range(8):
if sumsofrowsmatrix[i] > sumsofrows[i]:
return False
# step 4: check sum of columns
sumsofcolumnsmatrix = []
for i in range(len(matrix[0])):
sum = 0;
for j in range(len(matrix)):
sum = sum + matrix[j][i]
sumsofcolumnsmatrix.append(sum)
for i in range(8):
if sumsofcolumnsmatrix[i] > sumsofcolumns[i]:
return False
# step 5: if grid is completely filled but solution is incorrect, then invalid
dummy = 0 #dummy becomes 1 if there are any zeros
for a in range(8):
for b in range(8):
if matrixthree[a][b] == 0:
dummy = 1
if dummy == 0: #if there are no zeros left
if solved(matrix) == False:
return False
return True
#STEP 5: find the next empty (0) spot in a matrix
def find_empty(matrixthree):
for i in range(len(matrixthree)):
for j in range(len(matrixthree[0])):
if matrixthree[i][j] == 0:
return (i, j)
return None
#STEP 6: solved function
def solve(matrixthree):
matrix = numpy.copy(matrixthree)
for row in range(8):
for column in range(8):
if matrix[row][column] != 0:
matrix[row][column] = matrix[row][column] - 1
#check if the variable count already exists
count_exists = 'count' in locals() or 'count' in globals()
if count_exists == False:
count = 0
if find_empty(matrixthree) == None and solved(matrix) == True:
count = count + 1
return count
elif find_empty(matrixthree) != None:
row, column = find_empty(matrixthree)
for num in [2,1]: #only numbers 1 and 2 can be filled in
**original = numpy.copy(matrixthree)**
if validity(matrixthree,num,row,column) == True:
matrixthree[row][column] = num
count = count + solve(matrixthree) #solve recursively
matrixthree[row][column] = 0 #if no solutions were found, set back to 0 and go back
else:
matrixthree = numpy.copy(original)
return count
matrixthree = numpy.zeros(shape=(8,8))
count = solve(matrixthree)
print(count)
EDIT: The problem is that this algorithm finds one solution and then stops.