If I enter a single character, the message that everything is OK is not output (as it should be). But if I enter 2 correct characters, then the check says that everything is ok. Although it should only be used when all the characters are filled in and correct. I'm a beginner, I don't understand how to fix it. There is a lot of excess in the code, but it is only important for me that the check works. The entire program code is shown below.
import random, re, time, string
from copy import copy as duplicate
class Crossword(object):
def __init__(self, cols, rows, empty = '-', maxloops = 2000, available_words=[]):
self.cols = cols
self.rows = rows
self.empty = empty
self.maxloops = maxloops
self.available_words = available_words
self.randomize_word_list()
self.current_word_list = []
self.debug = 0
self.clear_grid()
def clear_grid(self):
self.grid = []
for i in range(self.rows):
ea_row = []
for j in range(self.cols):
ea_row.append(self.empty)
self.grid.append(ea_row)
def randomize_word_list(self):
temp_list = []
for word in self.available_words:
if isinstance(word, Word):
temp_list.append(Word(word.word, word.clue))
else:
temp_list.append(Word(word[0], word[1]))
random.shuffle(temp_list)
temp_list.sort(key=lambda i: len(i.word), reverse=True)
self.available_words = temp_list
def compute_crossword(self, time_permitted = 1.00, spins=2):
time_permitted = float(time_permitted)
count = 0
copy = Crossword(self.cols, self.rows, self.empty, self.maxloops, self.available_words)
start_full = float(time.time())
while (float(time.time()) - start_full) < time_permitted or count == 0:
self.debug += 1
copy.current_word_list = []
copy.clear_grid()
copy.randomize_word_list()
x = 0
while x < spins:
for word in copy.available_words:
if word not in copy.current_word_list:
copy.fit_and_add(word)
x += 1
if len(copy.current_word_list) > len(self.current_word_list):
self.current_word_list = copy.current_word_list
self.grid = copy.grid
count += 1
return
def suggest_coord(self, word):
count = 0
coordlist = []
glc = -1
for given_letter in word.word:
glc += 1
rowc = 0
for row in self.grid:
rowc += 1
colc = 0
for cell in row:
colc += 1
if given_letter == cell:
try:
if rowc - glc > 0:
if ((rowc - glc) + word.length) <= self.rows:
coordlist.append([colc, rowc - glc, 1, colc + (rowc - glc), 0])
except: pass
try:
if colc - glc > 0:
if ((colc - glc) + word.length) <= self.cols:
coordlist.append([colc - glc, rowc, 0, rowc + (colc - glc), 0])
except: pass
new_coordlist = self.sort_coordlist(coordlist, word)
return new_coordlist
def sort_coordlist(self, coordlist, word):
new_coordlist = []
for coord in coordlist:
col, row, vertical = coord[0], coord[1], coord[2]
coord[4] = self.check_fit_score(col, row, vertical, word)
if coord[4]:
new_coordlist.append(coord)
random.shuffle(new_coordlist)
new_coordlist.sort(key=lambda i: i[4], reverse=True)
return new_coordlist
def fit_and_add(self, word):
fit = False
count = 0
coordlist = self.suggest_coord(word)
while not fit and count < self.maxloops:
if len(self.current_word_list) == 0:
vertical, col, row = random.randrange(0, 2), 1, 1
if self.check_fit_score(col, row, vertical, word):
fit = True
self.set_word(col, row, vertical, word, force=True)
else:
try:
col, row, vertical = coordlist[count][0], coordlist[count][1], coordlist[count][2]
except IndexError: return
if coordlist[count][4]:
fit = True
self.set_word(col, row, vertical, word, force=True)
count += 1
return
def check_fit_score(self, col, row, vertical, word):
if col < 1 or row < 1:
return 0
count, score = 1, 1
for letter in word.word:
try:
active_cell = self.get_cell(col, row)
except IndexError:
return 0
if active_cell == self.empty or active_cell == letter:
pass
else:
return 0
if active_cell == letter:
score += 1
if vertical:
if active_cell != letter:
if not self.check_if_cell_clear(col+1, row):
return 0
if not self.check_if_cell_clear(col-1, row):
return 0
if count == 1:
if not self.check_if_cell_clear(col, row-1):
return 0
if count == len(word.word):
if not self.check_if_cell_clear(col, row+1):
return 0
else:
if active_cell != letter:
if not self.check_if_cell_clear(col, row-1):
return 0
if not self.check_if_cell_clear(col, row+1):
return 0
if count == 1:
if not self.check_if_cell_clear(col-1, row):
return 0
if count == len(word.word):
if not self.check_if_cell_clear(col+1, row):
return 0
if vertical:
row += 1
else:
col += 1
count += 1
return score
def set_word(self, col, row, vertical, word, force=False):
if force:
word.col = col
word.row = row
word.vertical = vertical
self.current_word_list.append(word)
for letter in word.word:
self.set_cell(col, row, letter)
if vertical:
row += 1
else:
col += 1
return
def set_cell(self, col, row, value):
self.grid[row-1][col-1] = value
def get_cell(self, col, row):
return self.grid[row-1][col-1]
def check_if_cell_clear(self, col, row):
try:
cell = self.get_cell(col, row)
if cell == self.empty:
return True
except IndexError:
pass
return False
def solution(self):
outStr = ""
for r in range(self.rows):
for c in self.grid[r]:
outStr += '%s ' % c
outStr += '\n'
return outStr
def word_find(self):
outStr = ""
for r in range(self.rows):
for c in self.grid[r]:
if c == self.empty:
outStr += '%s ' % string.ascii_lowercase[random.randint(0,len(string.ascii_lowercase)-1)]
else:
outStr += '%s ' % c
outStr += '\n'
return outStr
def order_number_words(self):
self.current_word_list.sort(key=lambda i: (i.col + i.row))
count, icount = 1, 1
for word in self.current_word_list:
word.number = count
if icount < len(self.current_word_list):
if word.col == self.current_word_list[icount].col and word.row == self.current_word_list[icount].row:
pass
else:
count += 1
icount += 1
def display(self, order=True):
outStr = ""
if order:
self.order_number_words()
copy = self
for word in self.current_word_list:
copy.set_cell(word.col, word.row, word.number)
for r in range(copy.rows):
for c in copy.grid[r]:
outStr += '%s ' % c
outStr += '\n'
outStr = re.sub(r'[a-z]', ' ', outStr)
return outStr
def word_bank(self):
outStr = ''
temp_list = duplicate(self.current_word_list)
random.shuffle(temp_list)
for word in temp_list:
outStr += '%s\n' % word.word
return outStr
def legend(self):
outStr = ''
for word in self.current_word_list:
outStr += '%d. (%d,%d) %s: %s\n' % (word.number, word.col, word.row, word.down_across(), word.clue )
return outStr
class Word(object):
def __init__(self, word=None, clue=None):
self.word = re.sub(r'\s', '', word.lower())
self.clue = clue
self.length = len(self.word)
self.row = None
self.col = None
self.vertical = None
self.number = None
def down_across(self):
if self.vertical:
return 'down'
else:
return 'across'
word_list = ['saffron', 'The dried, orange yellow plant used to as dye and as a cooking spice.'], \
['pumpernickel', 'Dark, sour bread made from coarse ground rye.'], \
['leaven', 'An agent, such as yeast, that cause batter or dough to rise..'], \
['coda', 'Musical conclusion of a movement or composition.'], \
['paladin', 'A heroic champion or paragon of chivalry.'], \
['syncopation', 'Shifting the emphasis of a beat to the normally weak beat.'], \
['albatross', 'A large bird of the ocean having a hooked beek and long, narrow wings.'], \
['harp', 'Musical instrument with 46 or more open strings played by plucking.'], \
['piston', 'A solid cylinder or disk that fits snugly in a larger cylinder and moves under pressure as in an engine.'], \
['caramel', 'A smooth chery candy made from suger, butter, cream or milk with flavoring.'], \
['coral', 'A rock-like deposit of organism skeletons that make up reefs.'], \
['dawn', 'The time of each morning at which daylight begins.'], \
['pitch', 'A resin derived from the sap of various pine trees.'], \
['fjord', 'A long, narrow, deep inlet of the sea between steep slopes.'], \
['lip', 'Either of two fleshy folds surrounding the mouth.'], \
['lime', 'The egg-shaped citrus fruit having a green coloring and acidic juice.'], \
['mist', 'A mass of fine water droplets in the air near or in contact with the ground.'], \
['plague', 'A widespread affliction or calamity.'], \
['yarn', 'A strand of twisted threads or a long elaborate narrative.'], \
['snicker', 'A snide, slightly stifled laugh.']
a = Crossword(17, 17, '-', 5000, word_list)
a.compute_crossword(2)
print (a.word_bank())
print (a.solution())
print (a.word_find())
print (a.display())
print (a.legend())
print (len(a.current_word_list), 'из', len(word_list))
print (a.debug)
###############################################################################
from tkinter import *
from tkinter.messagebox import showwarning, showinfo
class CellEntry(Entry):
def __init__(self, master, **kw):
self._variable = StringVar()
self._variable.trace("w", self._callback)
super().__init__(master, textvariable=self._variable, **kw)
def _callback(self, *args):
value = self._variable.get()
self._variable.set('' if not value else value[-1])
class App:
def __init__(self, crossword):
self.root = Tk()
self.root.title("Crossword")
self._crossword = crossword
self._grid = Frame(self.root)
self._grid.pack(padx=30, pady=30)
self._cells = {}
for col in range(1, crossword.cols + 1):
for row in range(crossword.rows):
c = crossword.get_cell(col, row)
if c != '-':
entry = CellEntry(self._grid, width=3, justify=CENTER)
entry.grid(row=row, column=col)
self._cells[(col, row)] = entry
for w in crossword.current_word_list:
row, col = w.row, w.col
if w.down_across() == 'down':
row -= 1
else:
col -= 1
Label(self._grid, text=str(w.number)).grid(column=col, row=row)
Button(self.root, text='Questions', command=self.questions).pack(pady=10)
Button(self.root, text='Check', command=self.check).pack(pady=10)
def check(self):
for (col, row), entry in self._cells.items():
if entry.get() == self._crossword.get_cell(col, row):
showinfo('OK!!!')
return
'''for (col, row), entry in self._cells.items():
if entry.get() != self._crossword.get_cell(col, row):
showwarning('...', 'Something went wrong')
return
else:
showinfo('...', 'Ok')'''
def questions(self):
super().__init__()
self.label = Label(text=a.legend(),justify=LEFT,font=("Arial Bold", 9))
self.label.pack(padx=20, pady=20)
def run(self):
self.root.mainloop()
App(a).run()
There are likely many areas of your code that have unexpected behaviour, and may need refactoring in other places, but to address the specific issue you're citing:
Is not quite accurate. Actually, the testing any first letter of any word in your crossword would not trigger the event. The "p" in "p u m p e r n i c k e l" for example. Conversely, a single letter anywhere else in the word will fire your alert. The "m" in "p u m p e r n i c k e l" for example.
The issue lies in your
Crossword.display
functionSpecifically these lines:
You attempt to make a copy (distinct object the is a duplicate of
self
) by runningcopy = self
, however, what you've done instead is make an alias of self.Essentially,
copy
andself
are referring to the same object in memory. For this reason, modifications made tocopy
are also made toself
.When you call
set_cell
oncopy
, you overwrite the first letter of every word in the crossword with the number of its label inself
.For example "p u m p e r n i c k e l" becomes "1 u m p e r n i c k e l"
Due to this error introduced to your data, when you go through and do your comparison, you cannot correctly assess if the value is correct, as the correct value is not present for comparison. This is the reason why your alert does not appear in these cases.
You can use copy.deepcopy to create a distinct copy of
self
.You will need to update your import statement to include
deepcopy
:General note: There is a philosophy known as the Single Responsibility Principle, which essentially recommends that every function take care of a single task and that task alone. While there are many "correct" ways to design programs, it may be helpful to keep this principle in mind as you're working through this problem.