class Kakuro(Frame): def __init__(self, master): Frame.__init__(self, master) self.pack(expand = YES, fill = BOTH) self.timer = StopWatch(self) self.solver = Solver(self, self.timer) self.player = Player(self) self.timer.grid(row = 0, column = 0, sticky = 'ew') self.player.grid(row = 1, column = 0, sticky = 'news') self.player.grid_remove() self.solver.grid(row = 1, column = 0, sticky = 'news') self.rowconfigure(0, weight = 0) self.rowconfigure(1, weight = 1) self.columnconfigure(0, weight = 1) self.fileSaveDir = '.' # directory for saving puzzles self.fileOpenDir = '.' # directory for saved puzzles self.menu = self.makeMenu() self.solver.menu = self.menu self.enableSolver() def makeMenu(self): def notdone(): showerror('Not implemented', 'Not yet available') win = self.winfo_toplevel() top = Menu(win) win.config(menu=top) file = top.file = Menu(top, tearoff = 0) file.add_command(label='New', command=self.dimensionDialog, underline=0) file.add_command(label='Open', command = self.openFile, underline = 0) file.add_command(label='Save', command=self.savePuzzleKro, underline=0) file.add_command(label='Clear',command = self.clearSolution, underline=0) file.add_command(label='Print', command=self.solver.printBoard, underline=0) file.add_command(label='Exit', command=self.wrapup, underline=1) file.entryconfigure('Save', state = DISABLED) puzzle = top.puzzle = Menu(top, tearoff = 0) puzzle.add_command(label='Open',command = self.loadPuzzle,underline = 0) puzzle.add_command(label='Save',command=self.savePuzzleKak,underline=0) puzzle.add_command(label='Clear',command = notdone, underline=0) puzzle.entryconfigure('Save', state = DISABLED) puzzle.entryconfigure('Clear', state = DISABLED) top.add_cascade(label='File', menu=file, underline=0) top.add_cascade(label='Puzzle', menu=puzzle, underline=0) return top def dimensionDialog(self): win = self.winDim = Toplevel() win.withdraw() # Remain invisible while we figure out the geometry self.rowVar = StringVar() self.colVar = StringVar() rowFrame = LabelFrame(win, text = "Rows") colFrame = LabelFrame(win, text = "Columns") rowEntry = Scale(rowFrame, variable=self.rowVar, orient = VERTICAL, from_ = 4, to = 40) colEntry = Scale(colFrame, variable = self.colVar, orient = VERTICAL, from_ = 4, to = 40) rowEntry.set(self.solver.rows-1) colEntry.set(self.solver.cols-1) rowEntry.pack() colEntry.pack() ok = Button(win, text = 'Okay', command = self.okayDim) cancel = Button(win, text = 'Cancel', command = win.destroy) ok.grid(row = 1, column = 0) rowFrame.grid(row = 0, column = 0) colFrame.grid(row = 0, column = 1) cancel.grid(row = 1, column = 1) displayDialog(win, self.winfo_toplevel(), 'Dimension', True) def okayDim(self): self.winDim.destroy() rows = int(self.rowVar.get()) cols = int(self.colVar.get()) self.drawNew(rows, cols, 'Solver') self.timer.pause(reset=True) def drawNew(self, rows, cols, mode): self.menu.file.entryconfigure('Save', state = DISABLED) if mode == 'Solver': self.solver.drawNew(rows, cols) self.enableSolver() else: self.player.drawNew(rows, cols) self.enablePlayer() def openFile(self): # Mainly for development, to avoid having to enter puzzles # over and over fname = askopenfile( filetypes = [('Kakuro Files', '.kro')], title = 'Open Puzzle File', defaultextension = 'kro', initialdir = self.fileOpenDir) if not fname: return self.timer.pause(reset = True) self.fileOpenDir = os.path.dirname(fname.name) text = fname.read() dimPattern = re.compile(r'(\d+) by (\d+)') rows, cols = dimPattern.search(text).groups() self.drawNew(int(rows), int(cols), 'Solver') cluePattern = re.compile(r'\d+ +\d+ +\d+ +\d+.*\n') clues = cluePattern.findall(text) self.solver.displayClues(clues) def savePuzzleKro(self): # Save puzzle in .kro format # Menu item is enabled if and only if the puzzle has been solved # and has exactly one solution. # If there are more rows than columns, the puzzle is transposed to # better fit a computer screen. board = self.solver rows = board.rows cols = board.cols fname = asksaveasfilename( filetypes = [('Kakuro Files', '.kro')], title = 'Save Puzzle', defaultextension = 'kro', initialdir = self.fileSaveDir) if not fname: return # Force a .kro extension in linux if not fname.endswith('.kro'): fname = fname[:-3] + '.kro' self.fileSaveDir = os.path.split(fname[0]) fout = file(fname, 'w') fout.write('# %s\n' % os.path.split(fname)[1]) fout.write('# %s\n' % time.strftime("%A, %d %B %Y %H:%M:%S")) if rows <= cols: fout.write('dim %d by %d\n' % (rows-1, cols-1)) else: fout.write('#Transposed from data entry.\n') fout.write('dim %d by %d\n' % (cols-1, rows-1)) fout.write('\nBlack Squares\n') fout.write('Row Col Acr Dwn\n\n') blacks = board.getClues() for b in sorted(blacks): clues = blacks[b] fout.write('%3s %3s %3s %3s\n' % (b[0], b[1], clues[0], clues[1])) fout.write('\nSolution\n') fout.write('Row Col Ans\n\n') soln = self.solver.getSolution() if rows > cols: soln = {(k[1], k[0]):soln[k] for k in soln} for white in sorted(soln): fout.write('%3s %3s %3s\n' % (white[0], white[1], soln[white])) fout.close() self.menu.file.entryconfigure('Save', state = DISABLED) def loadPuzzle(self): fname = askopenfile( filetypes = [('Kakuro Files', '.kro')], title = 'Open Puzzle File', defaultextension = 'kro', initialdir = self.fileOpenDir) if not fname: return self.fileOpenDir = os.path.dirname(fname.name) text = fname.read() dimPattern = re.compile(r'(\d+) by (\d+)') rows, cols = dimPattern.search(text).groups() self.drawNew(int(rows), int(cols), 'Player') cluePattern = re.compile(r'\d+ +\d+ +\d+ +\d+.*\n') clues = cluePattern.findall(text) self.player.displayClues(clues) self.timer.pause(reset = True) def savePuzzleKak(self): pass def clearSolution(self): self.solver.clearSolution() self.menu.file.entryconfigure('Clear', state = DISABLED) def wrapup(self): if self.menu.file.entrycget('Save', 'state') == NORMAL: if askyesno('Puzzle Not Saved', "Save Current Puzzle?"): self.savePuzzleKro() self.master.destroy() def enableSolver(self): if self.player.winfo_ismapped(): self.player.grid_remove() self.player.disable() self.solver.grid() self.solver.enable() def enablePlayer(self): if self.solver.winfo_ismapped(): self.solver.grid_remove() self.solver.disable() self.player.grid() self.player.enable()