X-Git-Url: https://harishankar.org/repos/?p=getaclue.git;a=blobdiff_plain;f=crosswordpuzzle.py;h=628392098506ae21438d7e8b561f1cd0ae869148;hp=a02f469d21521818f2c4028966049cf8bbe0c81e;hb=HEAD;hpb=9e110bd4d38e7bd3d78f1cda65244e6b036f32dd diff --git a/crosswordpuzzle.py b/crosswordpuzzle.py index a02f469..6283920 100644 --- a/crosswordpuzzle.py +++ b/crosswordpuzzle.py @@ -93,6 +93,11 @@ class TooLongWordException (Exception): self.word = word self.length = length +# exception for words containing non-alpha characters +class WordCharsException (Exception): + def __init__ (self, word): + self.word = word + # exception for intersecting words class IntersectWordException (Exception): def __init__ (self, word, length): @@ -109,11 +114,21 @@ class NoWordException (Exception): def __init__ (self, row, col): self.pos = (row, col) +# exception when a word number is not found in the grid +class NoNumberException (Exception): + def __init__ (self, num): + self.num = num + # exception when no words are present in the grid class NoWordsException (Exception): def __init__ (self): self.msg = "No words in grid" +# exception to raise when solution is imcomplete when trying to verify it +class IncompleteSolutionException (Exception): + def __init__ (self): + self.msg = "Solution incomplete" + class CrosswordPuzzle: def __init__ (self, rows, cols): # define number of rows and columns @@ -136,8 +151,7 @@ class CrosswordPuzzle: def export_image (self, pngfile, htmlfile=None, puztitle="Crossword Puzzle", solution=True): # don't export if grid is not frozen - if self.frozen_grid is False: - raise FrozenGridException + self.assert_frozen_grid () # create cairo image surface and context px = 30 @@ -226,8 +240,7 @@ class CrosswordPuzzle: # get the AcrossLite(TM) data for exporting def export_acrosslite (self, title, author, copyright): # don't export if grid is not frozen - if self.frozen_grid is False: - raise FrozenGridException + self.assert_frozen_grid () across_data = [] across_data.append ("\r\n") @@ -301,6 +314,53 @@ class CrosswordPuzzle: return clues + # getting an across word at a number (note that the grid should be + # frozen for calling this otherwise a FrozenGridException will be raised) + def get_word_across_at_num (self, num): + # assert that the grid is frozen + self.assert_frozen_grid () + + # traverse the grid + for row in range (self.rows): + for col in range (self.cols): + if self.data[row][col].numbered == num: + word = self.get_word_across (row, col) + return word + + # if number is not found + raise NoNumberException (num) + + # getting a down word at a number (note that the grid should be frozen + # for calling this otherwise a FrozenGridException will be raised) + def get_word_down_at_num (self, num): + # assert that the grid is frozen + self.assert_frozen_grid () + + # traverse the grid + for row in range (self.rows): + for col in range (self.cols): + if self.data[row][col].numbered == num: + word = self.get_word_down (row, col) + return word + + # if number is not found + raise NoNumberException (num) + + # getting the position of a number on the grid (note that the grid should + # be frozen for calling this otherwise a FrozenGridException will be raised) + def get_position_of_num (self, num): + # assert that the grid is frozen + self.assert_frozen_grid () + + # traverse the grid + for row in range (self.rows): + for col in range (self.cols): + if self.data[row][col].numbered == num: + return (row, col) + + # if number is not found + raise NoNumberException (num) + # getting a down word at a position def get_word_down (self, row, col): # if index is out of bounds @@ -373,9 +433,12 @@ class CrosswordPuzzle: # setting a down word def set_word_down (self, row, col, word): - # if the grid is frozen the abort - if self.frozen_grid is True: - raise FrozenGridException + # if the grid is frozen then abort + self.assert_unfrozen_grid () + + # if the word has non-alphabetic characters + if not word.isalpha (): + raise WordCharsException (word) # if the word length greater than totalrows - startrow if len(word) > self.rows - row: @@ -428,9 +491,12 @@ class CrosswordPuzzle: # setting an across word def set_word_across (self, row, col, word): - # if the grid is frozen the abort - if self.frozen_grid is True: - raise FrozenGridException + # if the grid is frozen then abort + self.assert_unfrozen_grid () + + # if the word has non-alphabetic characters + if not word.isalpha (): + raise WordCharsException (word) # is the word length greater than totalcols - startcol? if len(word) > self.cols - col: @@ -511,6 +577,16 @@ class CrosswordPuzzle: self.frozen_grid = False + # raise an exception if the grid is frozen + def assert_unfrozen_grid (self): + if self.frozen_grid is True: + raise FrozenGridException + + # raise an exception if the grid is NOT frozen + def assert_frozen_grid (self): + if self.frozen_grid is False: + raise FrozenGridException + # reset the entire grid def reset_grid (self): # run through the grid @@ -524,14 +600,13 @@ class CrosswordPuzzle: # remove an across word at position def remove_word_across (self, row, col): # if grid is frozen don't allow removal of word - if self.frozen_grid is True: - raise FrozenGridException + self.assert_unfrozen_grid () word, brow, bcol, l = self.get_word_across (row, col) # traverse from the beginning to end of the word and erase it c = bcol - while True: + while c < self.cols: if self.data[brow][c].occupied_across is True: self.data[brow][c].clear_across_data () else: @@ -541,18 +616,76 @@ class CrosswordPuzzle: # remove a down word at position def remove_word_down (self, row, col): # if grid is frozen don't allow removal of word - if self.frozen_grid is True: - raise FrozenGridException + self.assert_unfrozen_grid () word, brow, bcol, l = self.get_word_down (row, col) # traverse from the beginn to end of the word and erase it r = brow - while True: + while r < self.rows: if self.data[r][bcol].occupied_down is True: self.data[r][bcol].clear_down_data () else: break r += 1 + # reveal/unreveal a word at position + def reveal_word_across (self, row, col, revealed=True): + # set the revealed flag for the word at the position + word= self.get_word_across (row, col) + + c = word[2] + while c < self.cols: + if self.data[word[1]][c].occupied_across is True: + self.data[word[1]][c].revealed = revealed + else: + break + c += 1 + + # reveal/unreveal a word at position + def reveal_word_down (self, row, col, revealed=True): + # set the revealed flag for the word at the position + word = self.get_word_down (row, col) + + r = word[1] + while r < self.rows: + if self.data[r][word[2]].occupied_down is True: + self.data[r][word[2]].revealed = revealed + else: + break + r += 1 + + # reveal/hide the entire solution by resetting revealed flag at all cells + def reveal_solution (self, revealed=True): + # run through the grid and set revealed to False + for row in range (self.rows): + for col in range (self.cols): + self.data[row][col].revealed = revealed + # clear the guesses for the board + def clear_guesses (self): + # run through the grid and set the guesses to None + for row in range (self.rows): + for col in range (self.cols): + self.data[row][col].guess = None + + # verify the solution - return True if all guessed characters are correct + # return False if some of them are wrong. + # if the board is not completed as yet, raise a IncompleteSolutionException + def is_solution_correct (self): + # run through the grid and check for each character in occupied cells + flag = True + for row in range (self.rows): + for col in range (self.cols): + if (self.data[row][col].occupied_across is True or + self.data[row][col].occupied_down is True): + # if there is no guess at a particular location raise + # the incomplete solution exception + if not self.data[row][col].guess: + raise IncompleteSolutionException + # if a character doesn't match, return False + if self.data[row][col].char <> self.data[row][col].guess: + flag = False + + # finally return result + return flag