--- /dev/null
+# Get A Clue (C) 2010 V. Harishankar
+# Crossword puzzle maker program
+# Licensed under the GNU GPL v3
+
+# Class for the puzzle data representation
+
+class GridItem:
+ # initialize the item
+ def __init__ (self, item_char='.', across_start = False, down_start = False,
+ occupied_across = False, occupied_down = False, num = 0,
+ clue_across = None, clue_down = None, revealed = False):
+ # character in the cell
+ self.char = item_char
+ # is the cell the start of an across word?
+ self.across_start = across_start
+ # is the cell the start of a down word?
+ self.down_start = down_start
+ # is the cell occupied by a letter in an across word?
+ self.occupied_across = occupied_across
+ # is the cell occupied by a letter in a down word?
+ self.occupied_down = occupied_down
+ # numbering of the cell if it is the start of a word
+ self.numbered = num
+ # clue across if the cell is the start of an across word
+ self.clue_across = clue_across
+ # clue down if the cell is the start of a down word
+ self.clue_down = clue_down
+ # is the letter revealed or hidden?
+ self.revealed = revealed
+
+# exception for too long words
+class TooLongWordException (Exception):
+ def __init__ (self, word, length):
+ self.word = word
+ self.length = length
+
+# exception for intersecting words
+class IntersectWordException (Exception):
+ def __init__ (self, word, length):
+ self.word = word
+ self.length = length
+
+# exception when grid is sought to be changed when frozen
+class FrozenGridException (Exception):
+ def __init__ (self):
+ self.msg = "Grid is frozen and cannot be edited"
+
+class CrosswordPuzzle:
+ # ansi color codes for grid display
+ BRICKRED = '\033[44;1;31m'
+ # bold
+ BOLD = '\033[33m'
+ # blue
+ BLUE = '\033[34m'
+ # grey
+ GREY = '\033[30m'
+ # disable colors
+ ENDCOL = '\033[0m'
+
+ def __init__ (self, rows, cols):
+ # define number of rows and columns
+ self.rows = rows
+ self.cols = cols
+
+ # initialize the list to hold the grid
+ self.data = []
+
+ # initial state of the grid is unfrozen
+ self.frozen_grid = False
+
+ # create the grid data
+ for i in range (rows):
+ self.data.append ([])
+ for j in range (cols):
+ self.data[i].append (GridItem ())
+
+ # 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 word length greater than totalrows - startrow
+ if len(word) > self.rows - row:
+ raise TooLongWordException (word, len(word))
+
+ # is the word intersecting any other word?
+ for i in range (len(word)):
+ # on the same column
+ if self.data[row+i][col].occupied_down is True:
+ raise IntersectWordException (word, len(word))
+ # on the previous column
+ if col > 0 and self.data[row+i][col-1].occupied_down is True:
+ raise IntersectWordException (word, len(word))
+ # on the next column
+ if (col < (len(word) - 1) and
+ (self.data[row+i][col+1].occupied_down is True or
+ self.data[row+i][col+1].across_start is True)):
+ raise IntersectWordException (word, len(word))
+
+ # also check the character before and after
+ if (row > 0 and self.data[row-1][col].occupied_down is True
+ and self.data[row-1][col].occupied_across is True):
+ raise IntersectWordException (word, len(word))
+ if (row + len(word) < self.rows and
+ self.data[row+len(word)][col].occupied_across is True and
+ self.data[row+len(word)][col].occupied_down is True):
+ raise IntersectWordException (word, len(word))
+
+ # set the down start to true
+ self.data[row][col].down_start = True
+ # set the word
+ for i in range (len(word)):
+ self.data[row+i][col].occupied_down = True
+ self.data[row+i][col].char = word[i].upper ()
+
+
+ # 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
+
+ # is the word length greater than totalcols - startcol?
+ if len(word) > self.cols - col:
+ raise TooLongWordException (word, len(word))
+
+ # is the word intersecting any other word?
+ for i in range (len(word)):
+ # on the same line
+ if self.data[row][col+i].occupied_across is True:
+ raise IntersectWordException (word, len(word))
+ # on a previous line except the last column
+ if row > 0 and self.data[row-1][col+i].occupied_across is True:
+ raise IntersectWordException (word, len(word))
+ # on a next line except the last column
+ if (row < (self.rows - 1) and
+ (self.data[row+1][col+i].down_start is True
+ or self.data[row+1][col+i].occupied_across is True)):
+ raise IntersectWordException (word, len(word))
+
+ # also check the character beyond and before and after
+ if (col > 0 and (self.data[row][col-1].occupied_across is True or
+ self.data[row][col-1].occupied_down is True)):
+ raise IntersectWordException (word, len(word))
+ if (col + len(word) < self.cols and
+ (self.data[row][col+len(word)].occupied_across is True or
+ self.data[row][col+len(word)].occupied_down is True)):
+ raise IntersectWordException (word, len(word))
+
+ # set across start to true
+ self.data[row][col].across_start = True
+
+ # set the word
+ for i in range (len(word)):
+ self.data[row][col+i].char = word[i].upper ()
+ self.data[row][col+i].occupied_across = True
+
+ # display the grid with words
+ def print_grid (self, no_words=False):
+ # get row, col and print them (with grid number if set)
+ for col in range (self.cols):
+ # print the first row as column headers
+ print self.BLUE + ' ' + str (col) + self.ENDCOL,
+ print
+
+ for row in range (self.rows):
+ for col in range (self.cols):
+ # print the data
+ # if the cell is numbered i.e. start of a word
+ if self.data[row][col].numbered != 0:
+ print self.BRICKRED + str(self.data[row][col].numbered) + self.ENDCOL,
+ # print a space
+ else:
+ print ' ',
+ # if the character is not a blank or a block
+ if self.data[row][col].char <> "." and self.data[row][col].char <> "#":
+ # if words are to be shown regardless of hidden/revealed state
+ if no_words is False:
+ print self.BOLD + self.data[row][col].char + self.ENDCOL,
+ else:
+ # display only revealed
+ if self.data[row][col].revealed is True:
+ print self.BOLD + self.data[row][col].char + self.ENDCOL,
+ # else print a block
+ else:
+ print self.GREY + '.' + self.ENDCOL,
+ else:
+ print self.GREY + self.data[row][col].char + self.ENDCOL,
+
+ print ' ' + self.BLUE + str(row) + self.ENDCOL
+
+ # freeze the grid numbers etc.
+ def freeze_grid (self):
+ # numbering
+ numbering = 1
+ # run through the grid
+ for row in range (self.rows):
+ for col in range (self.cols):
+ # if grid is blank set the character to #
+ if (self.data[row][col].occupied_across is False
+ and self.data[row][col].occupied_down is False):
+ self.data[row][col].char = "#"
+ elif (self.data[row][col].across_start is True or
+ self.data[row][col].down_start is True):
+ self.data[row][col].numbered = numbering
+ numbering += 1
+
+ self.frozen_grid = True
+
--- /dev/null
+# Get A Clue (C) 2010 V. Harishankar
+# Crossword puzzle maker program
+# Licensed under the GNU GPL v3
+
+# Cross puzzle creator class
+
+import crosswordpuzzle
+
+class CrosswordPuzzleCreator:
+ # ansi color codes for grid display
+ BRICKRED = '\033[31m'
+ # bold
+ BOLD = '\033[33m'
+ # blue
+ BLUE = '\033[34m'
+ # grey
+ GREY = '\033[30m'
+ # disable colors
+ ENDCOL = '\033[0m'
+
+ def __init__ (self):
+ self.do_main_loop ()
+ self.current_file = None
+ self.puzzle = None
+
+ # display the grid with words
+ def print_puzzle (self, no_words=False):
+ # if self.puzzle is not none
+ if self.puzzle:
+ # get row, col and print them (with grid number if set)
+ for col in range (self.puzzle.cols):
+ # print the first row as column headers
+ print self.BLUE + ' ' + str (col) + self.ENDCOL,
+ print
+
+ for row in range (self.puzzle.rows):
+ for col in range (self.puzzle.cols):
+ # print the data
+ # if the cell is numbered i.e. start of a word
+ if self.puzzle.data[row][col].numbered != 0:
+ print self.BRICKRED + str(self.puzzle.data[row][col].numbered) + self.ENDCOL,
+ # print a space
+ else:
+ print ' ',
+ # if the character is not a blank or a block
+ if self.puzzle.data[row][col].char <> "." and self.puzzle.data[row][col].char <> "#":
+ # if words are to be shown regardless of hidden/revealed state
+ if no_words is False:
+ print self.BOLD + self.puzzle.data[row][col].char + self.ENDCOL,
+ else:
+ # display only revealed
+ if self.puzzle.data[row][col].revealed is True:
+ print self.BOLD + self.puzzle.data[row][col].char + self.ENDCOL,
+ # else print a block
+ else:
+ print self.GREY + '.' + self.ENDCOL,
+ else:
+ print self.GREY + self.puzzle.data[row][col].char + self.ENDCOL,
+
+ print ' ' + self.BLUE + str(row) + self.ENDCOL
+ raw_input (self.GREY + "Press <return> to continue" + self.ENDCOL)
+
+ # Puzzle loop
+ def do_puzzle_loop (self):
+ # there is a current file
+ while True:
+ if self.current_file and self.puzzle:
+ print self.BOLD + "-----------------------------------"
+ print "Puzzle: " + self.current_file
+ print "-----------------------------------" + self.ENDCOL
+ print self.BLUE + "1. Display grid"
+ print "2. Add across word"
+ print "3. Add down word"
+ print "4. Freeze grid"
+ print "5. Unfreeze grid"
+ print "6. Save puzzle"
+ print "X. Exit to main menu" + self.ENDCOL
+ ch = raw_input (self.BRICKRED + "Your choice: " + self.ENDCOL)
+ if ch == "1":
+ self.print_puzzle ()
+ elif ch == "X" or ch == "x":
+ break
+
+ # when user chooses new puzzle
+ def on_new_puzzle (self):
+ self.current_file = raw_input ("New puzzle file name: ")
+ srows = raw_input ("Number of rows: ")
+ scols = raw_input ("Number of cols: ")
+ try:
+ rows = int (srows)
+ cols = int (scols)
+ except ValueError:
+ print "Invalid number of rows/columns"
+ return
+ self.puzzle = crosswordpuzzle.CrosswordPuzzle (rows, cols)
+ self.do_puzzle_loop ()
+
+ # Main application loop
+ def do_main_loop (self):
+ # display the menu
+ while True:
+ print
+ print self.BOLD + "-----------------------------------"
+ print "Get A Clue - Crossword Puzzle Maker"
+ print "-----------------------------------" + self.ENDCOL
+ print self.BLUE + "1. Start a new puzzle"
+ print "2. Open an existing puzzle"
+ print "X. Exit" + self.ENDCOL
+ ch = raw_input (self.BRICKRED + "Your choice: " + self.ENDCOL)
+ if ch == '1':
+ self.on_new_puzzle ()
+ if ch == 'x' or ch == 'X':
+ break