[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