My current code implementing a depth-first search works well for puzzle 1. But my code is too slow; I think I use too many loops and I am not sure I am using the right data structure. So I have a problem in puzzle 3,4,5. Please help me to make my code run faster.
Description: The crossword file begins with a grid showing where the words go (represented by the * character) and the spaces around the words (represented by dots). Then there is a list of words, one per line, all in caps, that are to be placed into the crossword grid. The words should be placed in the grid so that they fit the number of asterisks and so that anywhere that words intersect each other, they share a letter. ( you can run puzzle1.txt to see the result)
puzzle1.txt
..........
.******...
...*......
...*......
...*****..
...*..*...
......*...
......*...
......*...
..........
POLAND
LHASA
SPAIN
INDIA
puzzle3.txt
.*.*.*.
.*.*.*.
.*.*.*.
*******
*.*.*.*
*.*.*.*
*.*.*.*
ABCDEFG
AAAA
BBBB
CCCC
DDDD
EEEE
FFFF
GGGG
puzzle4.txt
...........**********......
...*.......*......*........
...*...******.....*........
...*.......*......*........
...*****...*......*........
...*..*....*......*........
......*******.....*........
......*..*........*...*....
......*..*........*********
.........*............*....
......******..........*....
......*...........*****....
......*...............*....
......*...............*....
......*....................
......*....................
......*....................
......*....................
AUSTRALIA
GUATEMALA
PANAMA
PORTUGAL
IRELAND
MYANMAR
LHASA
MONTENEGRO
MALTA
CHINA
SPAIN
BHUTAN
DENMARK
INDIA
Source code
from collections import defaultdict
import os
import copy
import time
class search:
# Constructor
def __init__(self, file_name):
self.filename = file_name
self.board = [] ## list of board *..**
self.rows = 0
self.cols = 0
self.blank_tiles=[] ##pair row and col that have valid answer
self.answerlist =[] ## list of answer
self.count = 0
self.possibleboard = []
self.checkanswerlist = []
def addanswer(self,word):
self.answerlist.append(word)
def printverhorlist(self):
print(self.blank_tiles)
def verhorlist(self):
for j in range(self.rows):
col_lis = []
for i in range(self.cols):
# if current node is * append to row list , if rowlist >1 ( which mean it is a word) append row col to blank_tiles
if self.board[j][i] == "*":
col_lis.append((j, i))
elif len(col_lis) == 1:
col_lis = []
elif len(col_lis) > 1:
self.blank_tiles.append(col_lis)
col_lis = []
if j == self.cols-1 and len(col_lis) > 1:
self.blank_tiles.append(col_lis)
col_lis = []
for j in range(self.cols):
row_lis = []
for i in range(self.rows):
# if current node is * append to row list , if rowlist >1 ( which mean it is a word) append row col to blank_tiles
if self.board[i][j] == "*":
row_lis.append((i, j))
elif len(row_lis) == 1:
row_lis = []
elif len(row_lis) > 1:
self.blank_tiles.append(row_lis)
row_lis = []
if i == self.rows-1 and len(row_lis) > 1: # end of a column
self.blank_tiles.append(row_lis)
row_lis = []
def printboard(self,board):
print("#"*self.cols)
print("\n")
for row in board:
print("".join(row))
def printanswer(self):
for a in self.answerlist:
print(a, end = " ")
print("\n")
def openfile(self):
file = open(self.filename, "r")
a = file.readlines() # read in whole file as 1D list of strings
try:
for i in range(len(a)):
if a[i][0] == "*" or a[i][0] == ".":
self.board.append(a[i].strip())
else:
self.answerlist.append(a[i].strip())
except:
print("Cannot open file")
self.cols = len(self.board[0])
self.rows = len(self.board)
print(self.rows,self.cols)
def checkfill(self,board):
for m in range(self.rows):
for n in range(self.cols):
if board[m][n] == "*":
return 0
return 1
def startfind(self):
start = time.time()
self.find_match(self.board, self.blank_tiles, self.answerlist)
stop = time.time()
times = (stop - start) * 1000
print("Run time takes %d miliseconds" % times)
if not self.checkfill(self.board):
print("Cannot solve this puzzle")
else:
print("It takes %d step to solve this puzzle" % self.count)
print("There are %d possible board"%len(self.possibleboard))
def find_match(self,board, blank_tiles, lis):
if (self.checkfill(board) and len(blank_tiles) == 0): ## check no ** in board, and all the anwer in check answerlist
if board not in self.possibleboard:
self.board = board
self.possibleboard.append(board)
self.printboard(board)
# if no tiles left, print the board
if len(blank_tiles) == 0:
self.board = board
# p***
else:
for tile in blank_tiles: ##pair row and col that have valid answer
for word in lis: ## answerword
# check all word can place to the space or the space is all ***
#board[tile[i][0]][tile[i][1]] is current node
lengthanswer = len(tile)
if len(word) == len(tile) and all( board[tile[i][0]][tile[i][1]] == "*" or board[tile[i][0]][tile[i][1]] == word[i] for i in range(lengthanswer)):
new_board = copy.deepcopy(board) #create newboard
# if word not in self.checkanswerlist:
# self.checkanswerlist.append(word)
for i in range(len(tile)): #fill the word in a row and array
new_lis1 = list(new_board[tile[i][0]])
new_lis1[tile[i][1]] = word[i]
new_board[tile[i][0]] = "".join(new_lis1)
new_blank_tiles = blank_tiles[:] # remove an row and col already place in list
new_blank_tiles.remove(tile)
new_lis2 = lis[:] # remove word already place in
new_lis2.remove(word)
# self.printboard(new_board)
self.count = self.count +1
self.find_match(new_board, new_blank_tiles, new_lis2)
if __name__ == "__main__":
g = search("puzzle1.txt")
g.openfile()
g.printboard(g.board)
g.verhorlist()
g.printverhorlist()
g.printanswer()
g.startfind()
#g.printanswer()