]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/tests/hangman.py
1 """Hangman.py, a simple wxPython game, inspired by the
2 old bsd game by Ken Arnold.
3 From the original man page:
5 In hangman, the computer picks a word from the on-line
6 word list and you must try to guess it. The computer
7 keeps track of which letters have been guessed and how
8 many wrong guesses you have made on the screen in a
11 That says it all, doesn't it?
15 Harm van der Heijden (H.v.d.Heijden@phys.tue.nl)"""
17 import random
,re
,string
18 from wxPython
.wx
import *
21 def __init__(self
, filename
, min_length
= 5):
22 self
.min_length
= min_length
23 print "Trying to open file %s" % (filename
,)
25 f
= open(filename
, "r")
27 print "Couldn't open dictionary file %s, using build-ins" % (filename
,)
28 self
.words
= self
.builtin_words
32 self
.filename
= filename
33 print "Got %d bytes." % (len(self
.words
),)
34 def SetMinLength(min_length
):
35 self
.min_length
= min_length
37 reg
= re
.compile('\s+([a-zA-Z]+)\s+')
38 n
= 50 # safety valve; maximum number of tries to find a suitable word
40 index
= int(random
.random()*len(self
.words
))
41 m
= reg
.search(self
.words
[index
:])
42 if m
and len(m
.groups()[0]) >= self
.min_length
: break
44 if n
: return string
.lower(m
.groups()[0])
46 builtin_words
= ' albatros banana electrometer eggshell'
51 class URLWordFetcher(WordFetcher
):
52 def __init__(self
, url
):
54 WordFetcher
.__init
__(self
, "hangman_dict.txt")
57 def RetrieveAsFile(self
, host
, path
=''):
58 from httplib
import HTTP
62 self
.logprint("Failed to create HTTP connection to %s... is the network available?" % (host
))
64 h
.putrequest('GET',path
)
65 h
.putheader('Accept','text/html')
66 h
.putheader('Accept','text/plain')
68 errcode
, errmsg
, headers
= h
.getreply()
70 self
.logprint("HTTP error code %d: %s" % (errcode
, errmsg
))
74 def OpenURL(self
,url
):
75 from htmllib
import HTMLParser
78 m
= re
.match('http://([^/]+)(/\S*)\s*', url
)
83 m
= re
.match('http://(\S+)\s*', url
)
86 self
.logprint("Invalid or unsupported URL: %s" % (url
))
90 f
= self
.RetrieveAsFile(host
,path
)
92 self
.logprint("Could not open %s" % (url
))
94 self
.logprint("Receiving data...")
96 tmp
= open('hangman_dict.txt','w')
97 fmt
= formatter
.AbstractFormatter(formatter
.DumbWriter(tmp
))
99 self
.logprint("Parsing data...")
104 class HangmanWnd(wxWindow
):
105 def __init__(self
, parent
, id, pos
=wxDefaultPosition
, size
=wxDefaultSize
):
106 wxWindow
.__init
__(self
, parent
, id, pos
, size
)
107 self
.SetBackgroundColour(wxNamedColour('white'))
108 if wxPlatform
== '__WXGTK__':
109 self
.font
= wxFont(12, wxMODERN
, wxNORMAL
, wxNORMAL
)
111 self
.font
= wxFont(10, wxMODERN
, wxNORMAL
, wxNORMAL
)
113 def StartGame(self
, word
):
121 self
.guess
= map(chr, range(ord('a'),ord('z')+1))
123 def HandleKey(self
, key
):
125 if self
.guess
.count(key
):
126 self
.message
= 'Already guessed %s' % (key
,)
128 self
.guess
.append(key
)
130 self
.tries
= self
.tries
+1
131 if not key
in self
.word
:
132 self
.misses
= self
.misses
+1
137 for letter
in self
.word
:
138 if not self
.guess
.count(letter
):
146 def Draw(self
, dc
= None):
148 dc
= wxClientDC(self
)
149 dc
.SetFont(self
.font
)
151 (x
,y
) = self
.GetSizeTuple()
153 for letter
in self
.word
:
154 if self
.guess
.count(letter
):
155 dc
.DrawText(letter
, x1
, y1
)
157 dc
.DrawText('.', x1
, y1
)
160 dc
.DrawText("tries %d misses %d" % (self
.tries
,self
.misses
),x1
,50)
162 for letter
in self
.guess
:
163 guesses
= guesses
+ letter
164 dc
.DrawText("guessed:", x1
, 70)
165 dc
.DrawText(guesses
[:13], x1
+80, 70)
166 dc
.DrawText(guesses
[13:], x1
+80, 90)
167 dc
.SetUserScale(x
/1000., y
/1000.)
169 def DrawVictim(self
, dc
):
170 dc
.SetPen(wxPen(wxNamedColour('black'), 20))
171 dc
.DrawLines([(10, 980), (10,900), (700,900), (700,940), (720,940),
172 (720,980), (900,980)])
173 dc
.DrawLines([(100,900), (100, 100), (300,100)])
174 dc
.DrawLine(100,200,200,100)
175 if ( self
.misses
== 0 ): return
176 dc
.SetPen(wxPen(wxNamedColour('blue'), 10))
177 dc
.DrawLine(300,100,300,200)
178 if ( self
.misses
== 1 ): return
179 dc
.DrawEllipse(250,200,100,100)
180 if ( self
.misses
== 2 ): return
181 dc
.DrawLine(300,300,300,600)
182 if ( self
.misses
== 3) : return
183 dc
.DrawLine(300,300,250,550)
184 if ( self
.misses
== 4) : return
185 dc
.DrawLine(300,300,350,550)
186 if ( self
.misses
== 5) : return
187 dc
.DrawLine(300,600,350,850)
188 if ( self
.misses
== 6) : return
189 dc
.DrawLine(300,600,250,850)
190 def OnPaint(self
, event
):
194 class HangmanDemo(HangmanWnd
):
195 def __init__(self
, wf
, parent
, id, pos
, size
):
196 HangmanWnd
.__init
__(self
, parent
, id, pos
, size
)
197 self
.StartGame("dummy")
201 self
.timer
= self
.PlayTimer(self
.MakeMove
)
205 self
.StartGame(self
.wf
.Get())
207 self
.left
= list('aaaabcdeeeeefghiiiiijklmnnnoooopqrssssttttuuuuvwxyz')
209 key
= self
.left
[int(random
.random()*len(self
.left
))]
210 while self
.left
.count(key
): self
.left
.remove(key
)
211 self
.start_new
= self
.HandleKey(key
)
212 self
.timer
.Start(self
.delay
)
215 class PlayTimer(wxTimer
):
216 def __init__(self
,func
):
217 wxTimer
.__init
__(self
)
223 class HangmanDemoFrame(wxFrame
):
224 def __init__(self
, wf
, parent
, id, pos
, size
):
225 wxFrame
.__init
__(self
, parent
, id, "Hangman demo", pos
, size
)
226 self
.demo
= HangmanDemo(wf
, self
, -1, wxDefaultPosition
, wxDefaultSize
)
227 def OnCloseWindow(self
, event
):
228 self
.demo
.timer
.Stop()
231 class AboutBox(wxDialog
):
232 def __init__(self
, parent
,wf
):
233 wxDialog
.__init
__(self
, parent
, -1, "About Hangman", wxDefaultPosition
, wxSize(350,450))
234 self
.wnd
= HangmanDemo(wf
, self
, -1, wxPoint(1,1), wxSize(350,150))
235 self
.static
= wxStaticText(self
, -1, __doc__
, wxPoint(1,160), wxSize(350, 250))
236 self
.button
= wxButton(self
, 2001, "OK", wxPoint(150,420), wxSize(50,-1))
237 EVT_BUTTON(self
, 2001, self
.OnOK
)
238 def OnOK(self
, event
):
240 self
.EndModal(wxID_OK
)
242 class MyFrame(wxFrame
):
243 def __init__(self
, wf
):
245 wxFrame
.__init
__(self
, NULL
, -1, "hangman", wxDefaultPosition
, wxSize(400,300))
246 self
.wnd
= HangmanWnd(self
, -1)
248 menu
.Append(1001, "New")
249 menu
.Append(1002, "End")
250 menu
.AppendSeparator()
251 menu
.Append(1003, "Reset")
252 menu
.Append(1004, "Demo...")
253 menu
.AppendSeparator()
254 menu
.Append(1005, "Exit")
255 menubar
= wxMenuBar()
256 menubar
.Append(menu
, "Game")
258 #menu.Append(1010, "Internal", "Use internal dictionary", TRUE)
259 menu
.Append(1011, "ASCII File...")
260 urls
= [ 'wxPython home', 'http://208.240.253.245/wxPython/main.html',
261 'slashdot.org', 'http://slashdot.org/',
262 'cnn.com', 'http://cnn.com',
263 'The New York Times', 'http://www.nytimes.com',
264 'De Volkskrant', 'http://www.volkskrant.nl/frameless/25000006.html',
265 'Gnu GPL', 'http://www.fsf.org/copyleft/gpl.html',
266 'Bijbel: Genesis', 'http://www.coas.com/bijbel/gn1.htm']
268 for item
in range(0,len(urls
),2):
269 urlmenu
.Append(1020+item
/2, urls
[item
], urls
[item
+1])
270 urlmenu
.Append(1080, 'Other...', 'Enter an URL')
271 menu
.AppendMenu(1012, 'URL', urlmenu
, 'Use a webpage')
272 menu
.Append(1013, 'Dump', 'Write contents to stdout')
273 menubar
.Append(menu
, "Dictionary")
275 self
.urloffset
= 1020
277 menu
.Append(1090, "About...")
278 menubar
.Append(menu
, "Help")
279 self
.SetMenuBar(menubar
)
280 self
.CreateStatusBar(2)
281 EVT_MENU(self
, 1001, self
.OnGameNew
)
282 EVT_MENU(self
, 1002, self
.OnGameEnd
)
283 EVT_MENU(self
, 1003, self
.OnGameReset
)
284 EVT_MENU(self
, 1004, self
.OnGameDemo
)
285 EVT_MENU(self
, 1005, self
.OnWindowClose
)
286 EVT_MENU(self
, 1011, self
.OnDictFile
)
287 EVT_MENU_RANGE(self
, 1020, 1020+len(urls
)/2, self
.OnDictURL
)
288 EVT_MENU(self
, 1080, self
.OnDictURLSel
)
289 EVT_MENU(self
, 1013, self
.OnDictDump
)
290 EVT_MENU(self
, 1090, self
.OnHelpAbout
)
291 EVT_CHAR(self
.wnd
, self
.OnChar
)
293 def OnGameNew(self
, event
):
296 self
.SetStatusText("",0)
297 self
.wnd
.StartGame(word
)
298 def OnGameEnd(self
, event
):
299 self
.UpdateAverages(0)
301 self
.SetStatusText("",0)
303 def OnGameReset(self
, event
=None):
309 def OnGameDemo(self
, event
):
310 frame
= HangmanDemoFrame(self
.wf
, self
, -1, wxDefaultPosition
, self
.GetSize())
312 def OnDictFile(self
, event
):
313 fd
= wxFileDialog(self
)
314 if (self
.wf
.filename
):
315 fd
.SetFilename(self
.wf
.filename
)
316 if fd
.ShowModal() == wxID_OK
:
318 self
.wf
= WordFetcher(file)
319 def OnDictURL(self
, event
):
320 item
= (event
.GetId() - self
.urloffset
)*2
321 print "Trying to open %s at %s" % (self
.urls
[item
], self
.urls
[item
+1])
322 self
.wf
= URLWordFetcher(self
.urls
[item
+1])
323 def OnDictURLSel(self
, event
):
324 msg
= wxTextEntryDialog(self
, "Enter the URL of the dictionary document", "Enter URL")
325 if msg
.ShowModal() == wxID_OK
:
327 self
.wf
= URLWordFetcher(url
)
328 def OnDictDump(self
, event
):
330 def OnHelpAbout(self
, event
):
331 about
= AboutBox(self
, self
.wf
)
333 about
.wnd
.Stop() # that damn timer won't stop!
334 def UpdateAverages(self
, has_won
):
336 self
.won
= self
.won
+ 1
337 self
.played
= self
.played
+1
338 self
.history
.append(self
.wnd
.misses
) # ugly
340 for m
in self
.history
:
342 self
.average
= float(total
/len(self
.history
))
343 def OnChar(self
, event
):
344 if not self
.in_progress
:
347 key
= event
.KeyCode();
348 if key
>= ord('A') and key
<= ord('Z'):
349 key
= key
+ ord('a') - ord('A')
351 if key
< 'a' or key
> 'z':
354 res
= self
.wnd
.HandleKey(key
)
356 self
.SetStatusText(self
.wnd
.message
)
358 self
.UpdateAverages(0)
359 self
.SetStatusText("Too bad, you're dead!",0)
363 self
.UpdateAverages(1)
364 self
.SetStatusText("Congratulations!",0)
366 percent
= (100.*self
.won
)/self
.played
369 self
.SetStatusText("p %d, w %d (%g %%), av %g" % (self
.played
,self
.won
, percent
, self
.average
),1)
371 def OnWindowClose(self
, event
):
376 if wxPlatform
== '__WXGTK__':
377 defaultfile
= "/usr/share/games/hangman-words"
378 elif wxPlatform
== '__WXMSW__':
379 defaultfile
= "c:\\windows\\hardware.txt"
382 wf
= WordFetcher(defaultfile
)
384 self
.SetTopWindow(frame
)
388 if __name__
== '__main__':