[Tutor] Crossword program

David Holland davholla2002 at yahoo.co.uk
Sun Jan 9 22:44:03 CET 2005


I wrote a program to create crosswords in python.
It is not perfect but it works, is there any open
source place I can put this for it to be used by
anyone who wants it ?  (Subject to the gpl licence).
Here is the code in case anyone is interested.

from Tkinter import *
#Crossword program David Holland
class Crossword(Frame):

    def __init__(self, master):
        '''Default arguments'''
        Frame.__init__(self, master)
        self.grid()
        self.create_widgets()
        NUM_SQUARES = 169
        EMPTY = " "
        global questionanswer
        questionanswer = {}
        global dictshort
        dictshort = {}
        
    def readdict(self):
        #get the dictionary from a file
        try:
            pickle_file = open(dictfile, "rb")
            dictread = cPickle.load(pickle_file)
        except:
            dictread = {}
        return dictread

    def writedict(self, dictent):
        #write dictionary to file
        pickle_file = open(dictfile, "wb")
        cPickle.dump(dictent, pickle_file)
        pickle_file.close()

    def showclues(self):
        #show clues on the screen
        questionanswer = self.readdict()
        textent = self.showinfo(questionanswer)
        #textent = questionanswer
        self.putinfo(textent)

    def showinfo(self, dictent):
        #make the clues look readable
        i = 0
        listkeys = dictent.keys()
        #listkeys = listkeys.sort()
        textdisp = ''
        for item in listkeys:
            i = i+1
            istr = str(i) + " "
            question = dictent[item]
            textdisp = textdisp + istr + " the
question is " + question + " the answer is " + item +
"\n"
        return textdisp
    
    def newcrosswordboard(self):
        #create a crossword board
        board = []
        for square in range (NUM_SQUARES):
            board.append(EMPTY)
        return board
    
#    def newcrosswordboard(self):
#        #this is create a board with the numbers
#        board = []
#        for square in range (NUM_SQUARES):
#            text = str(square)
#            board.append(text)
#        return board

    def deleteclue(self):
        #delete a clue
        try:
            numberentered = self.delete_ent.get()
            dictent = self.readdict()
            numberentered = int(numberentered)
            listkeys = dictent.keys()
            i = 1
            clue = ''
            for item in listkeys:
                if numberentered == i:
                    del dictent[item]
                    clue = item
                    break
                i = i +1
            text = "Clue " + clue + " has been removed
the list of clues now is :-" + "\n"
            self.writedict(dictent)
            moretext = self.showinfo(dictent)
            text = text + moretext
        except:
            text = "Please enter a number as a figure"
        self.putinfo(text)
            
    def create_widgets(self):
        #create GUI
        self.question_lbl = Label(self, text = "Enter
the question ")
        self.question_lbl.grid(row = 0, column = 0,
columnspan = 2, sticky = W)
        self.answer_lbl = Label(self, text = "Enter
the answer")
        self.answer_lbl.grid(row = 1, column = 0,
columnspan = 2, sticky = W)
        self.delete_lbl = Label(self, text = "Enter
the number of a clue you want deleted")
        self.delete_lbl.grid(row = 2, column = 0,
columnspan = 2, sticky = W)
         #entry widget for the question
        self.question_ent = Entry(self)
        self.question_ent.grid(row=0, column = 2,
columnspan = 1, sticky = W)
        self.answer_ent = Entry(self)
        self.answer_ent.grid(row=1, column = 2,
columnspan = 2, sticky = W)
        self.delete_ent = Entry(self)
        self.delete_ent.grid(row=2, column = 2,
columnspan = 1, sticky = W)
        
        #button to add entries
        Button(self, text = "Click to add clues ",
command = self.create_questionsanswer).grid(row = 0,
column = 3, columnspan = 3)
        Button(self, text = "Click to show clues ",
command = self.showclues).grid(row = 1, column = 3,
columnspan = 3)
        Button(self, text = "Click to delete clue ",
command = self.deleteclue).grid(row = 2, column = 3,
columnspan = 3)
        #button to create the cross word
        Button(self, text = "Click to create crossword
", command = self.crosswordcreation).grid(row = 16,
column = 0, columnspan = 4)
        Button(self, text = "Click to get help ",
command = self.helpme).grid(row = 16, column = 3,
columnspan = 4)
        self.info_txt = Text(self, width = 80, height
= 30, wrap = WORD)
        self.info_txt.grid(row = 20, column = 0,
columnspan = 4)
        #show help
        self.helpme()

    def helpme(self):
        #get help
        helptext = 'To create crosswords add words by
entering where it says enter the question and answer,
then press the button "click to add clues." ' + '\n'
        helptext = helptext + 'To see all clues click
"show all clues".'
        helptext = helptext + 'to delete a clue enter
its number and then click delete clue. '
        helptext = helptext + 'To create a crossword
click create crossword.  The crossword will be saved
in a file called cross.txt. '
        helptext = helptext + '\n' + 'If you like the
crossword save the file with a different name, create
another crossword. '
        helptext = helptext + '\n' +'Author David
Richard Holland, this is released under the GPL
licence'
        self.putinfo(helptext)

    def create_questionsanswer(self):
        #this creates the question
        questionanswer = self.readdict()
        question = self.question_ent.get()
        question = self.converttoupper(question)
        answer = self.answer_ent.get()
        answer = self.converttoupper(answer)
        textent = "This has been added to list"
        #the code below does not work properly
        if answer not in questionanswer and question
not in questionanswer:
            questionanswer[answer] = question
            self.writedict(questionanswer)
            self.putinfo(textent)
        elif answer in questionanswer:
            self.termexists(answer)
            #########print "line 38"
        elif question in questionanswer:
            self.termexists(question)



    def converttoupper(self, texttoupper):
        #conver string to upper text
        try:
            texttoupper = str(texttoupper)
            texttoupper = string.upper(texttoupper)
        except:
            texttoupper = texttoupper
        return texttoupper

    def termexists(self, answer):
        textent = answer, 'is already an answer for
the crossword enter another'
        self.putinfo(textent)

    def putinfo(self, textent):
        #put the info to the GUI
        self.info_txt.delete(0.0, END)
        self.info_txt.insert(0.0,textent)

    def savecross(self, textent):
        file = open('cross.txt','w')
        file.write(textent)
        file.close()

    def crosswordcreation(self):
        #get the board info from create crossword
        board = self.createcrossword()
        board2 = self.createcrossword()
        numhori = 0
        numvert = 0
        #########print "crossword creationcalled"
        questionanswer = self.readdict()
        #here put in code to put the answers from the
dictionary in the board
        #code goes here
        #first sort the dictionary into 2 one with
values > 4 in len the other one everything else
        dictshort = {}
        dictshort, dictlong =
self.changedict(questionanswer, dictshort)
        #########print "dictionary changed"
        #variables for the questions
        diagtext = 'Across ' + '\n'
        verttext = 'Down '+ '\n'
        badquestion = 'These could not be asked' +
'\n'
        #code to populate the board
        board, board2,numhori, numvert, diagtext,
verttext, badquestion = self.putwordboard(dictlong,
board, board2, numhori, numvert, diagtext, verttext,
badquestion)
        board2, board2, numhori, numvert, diagtext,
verttext, badquestion = self.putwordboard(dictshort,
board, board2, numhori, numvert, diagtext, verttext,
badquestion)
        #code to show get the board as a picture
        text = self.display_board(board)
        text = text + "\n"
        textnew = self.display_board(board2)
        text = text + textnew
        text = text + diagtext + verttext +
badquestion #+ str(board)
        #board2 is the board that shows the clues
        #text2 = self.display_board(board3)
        #text = text + text2
        #save the cross word
        self.savecross(text)
        #display to screen
        self.putinfo(text)
        print "finished"


    def putwordboard(self, dict, board, board2,
numhori, numvert, diagtext, verttext, badquestion):
        listkeys = dict.keys()
        #this takes the words and puts them in the
crossword via sub functions
        item = random.choice(listkeys)
        while len(listkeys) > 0:
#        for item in listkeys:
            print item
#            emptylist, fulllist =
self.createlist(board)
            board, board2, canbeentered, numhori,
numvert, numbermove = self.putcharboard(item, board,
board2, numhori, numvert)
            question = dict[item]
            question = string.capitalize(question)
            question = ' ' + question 
            lenquestion = len(item)
            lenquestion = str(lenquestion)
            lenquestion = ' ' + lenquestion + "
letters long" + '\n'
            if numbermove == 13 and canbeentered ==
'Yes':
                #numvert = int(numvert)
                #numvert = numvert + 1
                numvert = str(numvert)
                
                verttext = verttext + numvert +
question + lenquestion
                numvert = int(numvert)
                textent = item + " has been entered"
            elif numbermove == 1 and canbeentered ==
'Yes':
                #numhori = int(numhori)
                #numhori = numhori + 1
                numhori = str(numhori)
                diagtext = diagtext + numhori +
question + lenquestion
                numhori = int(numhori)
                textent = item + " has been entered"
            else:
                badquestion = badquestion + question

            #self.putinfo(textent)
            #get a new random item and remove the old
one from the list
            item, listkeys =
self.changenummove(listkeys, item)
        return board, board2, numhori, numvert,
diagtext, verttext, badquestion

    def createlist(self, board):
        emptylist = []
        fulllist = []
        #create a list of squares which are empty and
square which are not
        #######print NUM_SQUARES
        for square in range (NUM_SQUARES):
            if board[square] == EMPTY:
                emptylist.append(square)
            else:
                fulllist.append(square)
        return emptylist, fulllist


    def createnumbermove(self):
        #create the number in which direction to move
        listchoice = [1,13]
        numbermove = random.choice(listchoice)
        return listchoice, numbermove

    def changenummove(self, listchoice, numbermove):
        #change the number in which direction should
be moved
        #make the list of options smaller
        #a = str(numbermove)
        #listchoice.remove(a)
        #the above does not work so this loop should
do
        i = 0
        for item in listchoice:
            if item == numbermove:
                break
            else:
                i = i+1
        del listchoice[i]
        if len(listchoice) >0:
            numbermove = random.choice(listchoice)
        return numbermove, listchoice

    def putcharboard(self, word, board, board2,
numhori, numvert):
        #see where the word can be put in the board
        #get a list of possible numbers that it can
move and choose one randomly
        listchoice, numbermove =
self.createnumbermove()
        j = 0
        i = 0
        lengthofword = len(word)
        canbeentered = 'No'
        ##########print "line 113"
        if len(USED_SQUARES) == 0:
            j = lengthofword
        #code to choose between going up or down
        #while canbeentered == 'No' and j
<lengthofword:
        #while canbeentered == 'No' and j
<lengthofword:
        #call a function that will see if it can be
entered where that a letter in the word already is
        emptylist, full_list = self.createlist(board)
        #create a variable which means that it loops
for the number of different options in numbermove
        list_choice_var = len(listchoice) +1
        
        while list_choice_var >0 and canbeentered ==
'No':
            ##print word, numbermove
            if len(full_list) > 0:
                try:
                    canbeentered, char, j, square  =
self.canbeenteredparta( word, numbermove,
canbeentered, lengthofword, board)
                except:
                    square = 0
            #as the word can not be entered change the
direction of move
            if canbeentered == 'No' and
len(listchoice) > 0:
                numbermove, listchoice =
self.changenummove(listchoice, numbermove)
            list_choice_var = list_choice_var -1
        if canbeentered == 'No':   
            listchoice, numbermove =
self.createnumbermove()
            ##print "listchoice is", listchoice,
"numbermove is", numbermove
            j = 0
            list_choice_var = len(listchoice)
            ##print "line 308 "
        #a loop to change numbermove randomly so that
all possible  positions are entered a sub function
sees if this can be entered
        #this code is to get find an empty square
randomly and see if the word can be entered
        k = 0
        while list_choice_var >0 and canbeentered ==
'No':
            canbeentered, word, char, j, square =
self.canbeenteredpartb(j, word, numbermove,
canbeentered, lengthofword, board)
            k = k +1
            #print word, numbermove, "line 305",
canbeentered
            if canbeentered == 'No' and
len(listchoice) > 0:
                #if you can not enter it get a new
value for the position it can be in
                ##print "line 316"
                numbermove, listchoice =
self.changenummove(listchoice, numbermove)
                #print "line 318"
            #print "line 308", numbermove
            #reduce the variable by 1
            list_choice_var = list_choice_var -1
        #print "k is", k
        if canbeentered == 'Yes':
             #calls a function that puts the word in
the board   
            board = self.putwordinboard(word, board,
char, square, j, numbermove, lengthofword)

            if numbermove == 1:
                numhori = numhori +1
                newword =
self.createword(lengthofword, j, numhori)
                #create a new word ie a blank word
numhori and numvert are the numbers of the questions
                board2 = self.putwordinboard(newword,
board2, numhori, square, j, numbermove, lengthofword)
            else:
                numvert = numvert + 1
                newword =
self.createword(lengthofword, j, numvert)
                board2 = self.putwordinboard(newword,
board2, numvert, square, j, numbermove, lengthofword)

        return board, board2, canbeentered, numhori,
numvert, numbermove


    def makecharpretty(self, char):
        #make char go in the crossword better in one
place so that the code can be maintained better
        char = ' '+char+' '
        return char

    def canbeenteredparta(self, word, numbermove,
canbeentered, lengthofword, board):
        #this sees if the word can be enterd somewhere
it already exists
        word = str(word)
        lengthofwordnew = lengthofword
        #is a variable to show what letter in the word
is the one to start from
        j = 0
        for char in word:
            if canbeentered == 'Yes':
                break
            emptylist, full_list =
self.createlist(board)
            if len(full_list) > 0:
                square = random.choice(full_list)
            else:
                break
            while len(full_list) > 0:
                if canbeentered == 'Yes':
                    break
                letter = self.makecharpretty(char)
                if board[square] == letter:
                    #####print    "square is the same
as letter", char
                    canbeentered =
self.canbeentered(word, board, char, square, j,
numbermove)
                #if it can be entered break the while
loop
                if canbeentered == 'Yes':
                        break
                        ######print "line 364"
                elif canbeentered == 'No':
                    square, full_list =
self.changenummove(full_list, square)
                    #######print "line 365"
            if canbeentered == 'Yes':
                break
            #it can be entered so break the for loop
as well as the while otherwise increase j
            else:
                j = j +1
        return canbeentered, char,  j, square

    def canbeenteredpartb(self, j, word, numbermove,
canbeentered, lengthofword, board):
        #initialise variables of course these will not
be created if the loop is not called
        char = ''
        #i = 0
        square = ''
        for char in word:
            i = 0
            #####print "char", char, "is being
tested", "canbeentered = ", canbeentered
            copyemptylist, fullist =
self.createlist(board)
            #######print "char is", char,"word is",
word
            #######print "empty list is",
copyemptylist
            if len(copyemptylist) > 0:
                square = random.choice(copyemptylist)
            #get a random square then start the loop  
             
            while len(copyemptylist) > 0:
                canbeentered = self.canbeentered(word,
board, char, square, j, numbermove)
                            #########print
canbeentered
                if canbeentered == 'Yes':
                    break
                    ######print "canbeentered is",
canbeentered
                else:
                #if it can be entered we break other
wise get a new square
                    #######print "line 428", square
                    square, copyemptylist =
self.changenummove(copyemptylist, square)
                    i = i +1
            ##print i
            #if this char can be entered break the for
loop as well
            if canbeentered == 'Yes':
                    break
                    return canbeentered, word, char,
j, square
            else:
                j = j +1
            #break
        return canbeentered, word, char, j, square


    def createword(self, lengthofword, numinword,
char):
        numforward = (lengthofword - numinword) -1
        i = 0
        char = str(char)
        charnew = '-'
        while i < numforward:
            char = char + charnew
            i = i + 1
        i = 0
        numback = numinword
        if numback > 0:
            while i < numback:
                char = char + charnew
                i = i +1
                ###print "line 423"
        newword = char
        ###print "lengthofword is", lengthofword,
"numinword", numinword, "new word", newword
        ###print "lenght of new word", len(newword)
        return newword


            
            
    def putwordinboard(self, word, board, char,
numsquare, numinword, numbermove, lengthofword):
        #we know that this can be entered or it would
not be called
        #enter the code
        #start from here
        #get the lengths
        #########print "putwordinboard called
numbermove is", numbermove
        numforward = lengthofword - numinword
        numback = numinword
        board = self.putwordinboardpart2(word, board,
numsquare, numinword, numbermove, numforward, numback)
        #########print "line 167"
        return board

    def putwordinboardpart2(self, word, board,
numsquare, numinword, numbermove, numforward,
numback):
        i = 0
#        numinword = (int(numinword)) -1
        numinword = (int(numinword)) 
        originalnumberinword = numinword
        numbermove = int(numbermove)
        #########print "numbermove is", numbermove
        numsquareorig = numsquare
        #########print "putwordinboardpart2 called"
        while i < numforward:
            try:
                char = word[numinword]
            except:
                print "char", "numinword is",
numinword, "lengtho of word", len(word), "word is", 
word
            char = self.makecharpretty(char)
            #if board[numsquare] == EMPTY:
            board[numsquare] = char
            numinword = numinword +1
            i = i+1
            global USED_SQUARES
            USED_SQUARES.append(numsquare)
            numsquare = numsquare + numbermove
        
        i = -1
        numinword = originalnumberinword
        numsquare = numsquareorig
        if numback > 0:
            while i < numback:
                char = word[numinword]
                char = ' '+char+' '
                numinword = numinword -1
                board[numsquare] = char
                numsquare = numsquare - numbermove
                global USED_SQUARES
                USED_SQUARES.append(numsquare)
                #the above is to record all squares
that have been used
                i = i+1
        return board
    
    def canbeentered(self, word, board, char,
numsquare, numinword, numbermove):
        #this is designed to move find out if the word
can be entered in a straight
        #horozontial or vertical line
        #numbermove decides if it is horozontial or
vertical, if it is 1 it is horozontial
        #13 if it is vertical
        #this is the brains of the crossword creation
if this is fine everything else should be
        lengthofword = len(word)
#        positivemove = lengthofword - numinword this
was changed because the first char is number 0
therefore
#       only have to move the same length -1 in v46
        positivemove = (lengthofword - numinword)-1
        negativemove = numinword
        #we do not want the first one to be empty
because it might already be occupied
        numsquarenew = numsquare + numbermove
        #if it is horozontial we do not want it to be
anything but a straight line therefore it
        #should only work if the first number is an
exact multiple of 13 and the last number the first
number + 12
        if numbermove == 1:
            moveback = numsquare /13
            moveback = moveback * 13
            moveforward= moveback + 12
            #changed movefoward to moveback + 12 in
v30 and before to allow 13 letter words to be added 
        else:
            moveback = 0
            #moveforward should be changed
            moveforward = 168
        enteredyesno = 'Yes'
        if numsquarenew > moveforward:
            enteredyesno = 'No'
        i = 0
        if enteredyesno == 'Yes':
            enteredyesno =
self.canbeenteredpart2(numsquare, numbermove,
moveback, moveforward, positivemove, board)
        #if this function shows that going forward is
okay
        #try going back back
        if enteredyesno == 'Yes' and negativemove > 0:
            numbermove = - numbermove
            try:
                enteredyesno =
self.canbeenteredpart2(numsquare, numbermove,
moveback, moveforward, negativemove, board)
            except:
                enteredyesno = 'No'
        #the constant enteredyesno means that it can
or not be entered or
        rangesquares = NUM_SQUARES -1
        numsquare = numsquare + numbermove
        
        return enteredyesno         


    def canbeenteredpart2(self, numsquare, numbermove,
moveback, moveforward, amounttomove, board):
        #i and j are constants that increase in the
loop, everytime the loop goes i increases
        #j only increases if it meets certain
conditions
        i = 0
        j = 0
        #code to ensure space
        if board[numsquare-1] != EMPTY:
            j = 3
        numsquare = numsquare + numbermove
        enteredyesno = 'No'
        while i == j and i < amounttomove:
            i = i+1
            #in case of error set j to 0 so the
program can continue
            try:
                if board[numsquare] == EMPTY:
                    j = j+1
                #the line above is to see if it can be
entered
                #below is to give space
                if board[numsquare+1] != EMPTY:
                    j = 0
             
#                if board[numsquare+13] != EMPTY:
#                    j = 0
            except:
                j = 0
                #######print "error a mistake"
            if numsquare > moveforward:
                j = 0
            if numsquare < moveback:
                j = 0
            #look for the new square
            numsquare = numsquare + numbermove
        
        if i == j and i == amounttomove:
            enteredyesno = 'Yes'
        #code to ensure space
        try:
            if board[numsquare+1] != EMPTY and
enteredyesno == 'Yes':
                enteredyesno = 'Yes'
        except:
            #print "errored line 578"
            enteredyesno = 'No'
        return enteredyesno
                          
    def changedict(self, questionanswer, dictshort):
        #split the dictionary into 2 one with long
answers one with short answers
        #this ensures the long ones get put in first
        lengthofdict = len(questionanswer)
        dictlong = questionanswer
        i = 0
        #for key in dictlong:
        listkeys = dictlong.keys()
       #while i < lengthofdict:
        for item in listkeys:
            #######print "the loop is called"
            if len(item) < 10:
                definition = dictlong[item]
                #to short dictionary
                dictshort[item] = definition
                del dictlong[item]
            i = i + 1
        return dictshort, dictlong
    
    def createcrossword(self):
        #this creates the crossword
        #first create a board
        board = self.newcrosswordboard()
        #return the board
        return board


    def display_board(self, board):
       #this creates the text to be displayed
        textline = ''
        i = 0
        while i < 168:
            textline, i =
self.displayboardpart2(board, i, textline)
            #textline = textline 
        return textline

    def displayboardpart2(self, board, i, text):
        k = i
        j = i+13
        while i < j:
           text = text + board[i] + "|"
           i = i+1
        text = text + "\n"
        while k <j:
            text = text + "----"
            k = k +1
        text = text + "\n"
        return text, i
        


import random
import cPickle, shelve
import string
dictfile = "crossworddict.dat"
NUM_SQUARES = 169
EMPTY = "   "
global USED_SQUARES
USED_SQUARES = []
root = Tk()
root.title('Create crosswords')
app = Crossword(root)
root.mainloop()



	
	
		
___________________________________________________________ 
ALL-NEW Yahoo! Messenger - all new features - even more fun! http://uk.messenger.yahoo.com


More information about the Tutor mailing list