Menus in progress
[getaclue.git] / crosswordpuzzle.py
1 # Get A Clue (C) 2010 V. Harishankar
2 # Crossword puzzle maker program
3 # Licensed under the GNU GPL v3
4
5 # Class for the puzzle data representation
6
7 class GridItem:
8 # initialize the item
9 def __init__ (self, item_char='.', across_start = False, down_start = False,
10 occupied_across = False, occupied_down = False, num = 0,
11 clue_across = None, clue_down = None, revealed = False):
12 # character in the cell
13 self.char = item_char
14 # is the cell the start of an across word?
15 self.across_start = across_start
16 # is the cell the start of a down word?
17 self.down_start = down_start
18 # is the cell occupied by a letter in an across word?
19 self.occupied_across = occupied_across
20 # is the cell occupied by a letter in a down word?
21 self.occupied_down = occupied_down
22 # numbering of the cell if it is the start of a word
23 self.numbered = num
24 # clue across if the cell is the start of an across word
25 self.clue_across = clue_across
26 # clue down if the cell is the start of a down word
27 self.clue_down = clue_down
28 # is the letter revealed or hidden?
29 self.revealed = revealed
30
31 # exception for too long words
32 class TooLongWordException (Exception):
33 def __init__ (self, word, length):
34 self.word = word
35 self.length = length
36
37 # exception for intersecting words
38 class IntersectWordException (Exception):
39 def __init__ (self, word, length):
40 self.word = word
41 self.length = length
42
43 # exception when grid is sought to be changed when frozen
44 class FrozenGridException (Exception):
45 def __init__ (self):
46 self.msg = "Grid is frozen and cannot be edited"
47
48 class CrosswordPuzzle:
49 # ansi color codes for grid display
50 BRICKRED = '\033[44;1;31m'
51 # bold
52 BOLD = '\033[33m'
53 # blue
54 BLUE = '\033[34m'
55 # grey
56 GREY = '\033[30m'
57 # disable colors
58 ENDCOL = '\033[0m'
59
60 def __init__ (self, rows, cols):
61 # define number of rows and columns
62 self.rows = rows
63 self.cols = cols
64
65 # initialize the list to hold the grid
66 self.data = []
67
68 # initial state of the grid is unfrozen
69 self.frozen_grid = False
70
71 # create the grid data
72 for i in range (rows):
73 self.data.append ([])
74 for j in range (cols):
75 self.data[i].append (GridItem ())
76
77 # setting a down word
78 def set_word_down (self, row, col, word):
79 # if the grid is frozen the abort
80 if self.frozen_grid is True:
81 raise FrozenGridException
82
83 # if the word length greater than totalrows - startrow
84 if len(word) > self.rows - row:
85 raise TooLongWordException (word, len(word))
86
87 # is the word intersecting any other word?
88 for i in range (len(word)):
89 # on the same column
90 if self.data[row+i][col].occupied_down is True:
91 raise IntersectWordException (word, len(word))
92 # on the previous column
93 if col > 0 and self.data[row+i][col-1].occupied_down is True:
94 raise IntersectWordException (word, len(word))
95 # on the next column
96 if (col < (len(word) - 1) and
97 (self.data[row+i][col+1].occupied_down is True or
98 self.data[row+i][col+1].across_start is True)):
99 raise IntersectWordException (word, len(word))
100
101 # also check the character before and after
102 if (row > 0 and self.data[row-1][col].occupied_down is True
103 and self.data[row-1][col].occupied_across is True):
104 raise IntersectWordException (word, len(word))
105 if (row + len(word) < self.rows and
106 self.data[row+len(word)][col].occupied_across is True and
107 self.data[row+len(word)][col].occupied_down is True):
108 raise IntersectWordException (word, len(word))
109
110 # set the down start to true
111 self.data[row][col].down_start = True
112 # set the word
113 for i in range (len(word)):
114 self.data[row+i][col].occupied_down = True
115 self.data[row+i][col].char = word[i].upper ()
116
117
118 # setting an across word
119 def set_word_across (self, row, col, word):
120 # if the grid is frozen the abort
121 if self.frozen_grid is True:
122 raise FrozenGridException
123
124 # is the word length greater than totalcols - startcol?
125 if len(word) > self.cols - col:
126 raise TooLongWordException (word, len(word))
127
128 # is the word intersecting any other word?
129 for i in range (len(word)):
130 # on the same line
131 if self.data[row][col+i].occupied_across is True:
132 raise IntersectWordException (word, len(word))
133 # on a previous line except the last column
134 if row > 0 and self.data[row-1][col+i].occupied_across is True:
135 raise IntersectWordException (word, len(word))
136 # on a next line except the last column
137 if (row < (self.rows - 1) and
138 (self.data[row+1][col+i].down_start is True
139 or self.data[row+1][col+i].occupied_across is True)):
140 raise IntersectWordException (word, len(word))
141
142 # also check the character beyond and before and after
143 if (col > 0 and (self.data[row][col-1].occupied_across is True or
144 self.data[row][col-1].occupied_down is True)):
145 raise IntersectWordException (word, len(word))
146 if (col + len(word) < self.cols and
147 (self.data[row][col+len(word)].occupied_across is True or
148 self.data[row][col+len(word)].occupied_down is True)):
149 raise IntersectWordException (word, len(word))
150
151 # set across start to true
152 self.data[row][col].across_start = True
153
154 # set the word
155 for i in range (len(word)):
156 self.data[row][col+i].char = word[i].upper ()
157 self.data[row][col+i].occupied_across = True
158
159 # freeze the grid numbers etc.
160 def freeze_grid (self):
161 # numbering
162 numbering = 1
163 # run through the grid
164 for row in range (self.rows):
165 for col in range (self.cols):
166 # if grid is blank set the character to #
167 if (self.data[row][col].occupied_across is False
168 and self.data[row][col].occupied_down is False):
169 self.data[row][col].char = "#"
170 elif (self.data[row][col].across_start is True or
171 self.data[row][col].down_start is True):
172 self.data[row][col].numbered = numbering
173 numbering += 1
174
175 self.frozen_grid = True
176
177 # unfreeze the grid numbers etc.
178 def unfreeze_grid (self):
179 # run through the grid
180 for row in range (self.rows):
181 for col in range (self.cols):
182 self.data[row][col].numbered = 0
183 if (self.data[row][col].occupied_across is False and
184 self.data[row][col].occupied_down is False):
185 self.data[row][col].char = '.'
186