]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/samples/hangman/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)"""
22 builtin_words
= ' albatros banana electrometer eggshell'
24 def __init__(self
, filename
, min_length
= 5):
25 self
.min_length
= min_length
26 print "Trying to open file %s" % (filename
,)
28 f
= open(filename
, "r")
30 print "Couldn't open dictionary file %s, using builtins" % (filename
,)
31 self
.words
= self
.builtin_words
35 self
.filename
= filename
36 print "Got %d bytes." % (len(self
.words
),)
38 def SetMinLength(min_length
):
39 self
.min_length
= min_length
42 reg
= re
.compile('\s+([a-zA-Z]+)\s+')
43 n
= 50 # safety valve; maximum number of tries to find a suitable word
45 index
= int(random
.random()*len(self
.words
))
46 m
= reg
.search(self
.words
[index
:])
47 if m
and len(m
.groups()[0]) >= self
.min_length
: break
49 if n
: return m
.groups()[0].lower()
59 class URLWordFetcher(WordFetcher
):
60 def __init__(self
, url
):
62 WordFetcher
.__init
__(self
, "hangman_dict.txt")
67 def RetrieveAsFile(self
, host
, path
=''):
68 from httplib
import HTTP
72 self
.logprint("Failed to create HTTP connection to %s... is the network available?" % (host
))
74 h
.putrequest('GET',path
)
75 h
.putheader('Accept','text/html')
76 h
.putheader('Accept','text/plain')
78 errcode
, errmsg
, headers
= h
.getreply()
80 self
.logprint("HTTP error code %d: %s" % (errcode
, errmsg
))
85 def OpenURL(self
,url
):
86 from htmllib
import HTMLParser
89 m
= re
.match('http://([^/]+)(/\S*)\s*', url
)
94 m
= re
.match('http://(\S+)\s*', url
)
97 self
.logprint("Invalid or unsupported URL: %s" % (url
))
101 f
= self
.RetrieveAsFile(host
,path
)
103 self
.logprint("Could not open %s" % (url
))
105 self
.logprint("Receiving data...")
107 tmp
= open('hangman_dict.txt','w')
108 fmt
= formatter
.AbstractFormatter(formatter
.DumbWriter(tmp
))
110 self
.logprint("Parsing data...")
117 class HangmanWnd(wx
.Window
):
118 def __init__(self
, parent
, id, pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
):
119 wx
.Window
.__init
__(self
, parent
, id, pos
, size
)
120 self
.SetBackgroundColour(wx
.NamedColour('white'))
121 if wx
.Platform
== '__WXGTK__':
122 self
.font
= wx
.Font(12, wx
.MODERN
, wx
.NORMAL
, wx
.NORMAL
)
124 self
.font
= wx
.Font(10, wx
.MODERN
, wx
.NORMAL
, wx
.NORMAL
)
126 self
.Bind(wx
.EVT_PAINT
, self
.OnPaint
)
127 self
.Bind(wx
.EVT_SIZE
, self
.OnSize
)
129 def OnSize(self
, event
):
132 def StartGame(self
, word
):
141 self
.guess
= map(chr, range(ord('a'),ord('z')+1))
144 def HandleKey(self
, key
):
146 if self
.guess
.count(key
):
147 self
.message
= 'Already guessed %s' % (key
,)
149 self
.guess
.append(key
)
151 self
.tries
= self
.tries
+1
152 if not key
in self
.word
:
153 self
.misses
= self
.misses
+1
158 for letter
in self
.word
:
159 if not self
.guess
.count(letter
):
168 def Draw(self
, dc
= None):
170 dc
= wx
.ClientDC(self
)
171 dc
.SetFont(self
.font
)
173 (x
,y
) = self
.GetSizeTuple()
175 for letter
in self
.word
:
176 if self
.guess
.count(letter
):
177 dc
.DrawText(letter
, x1
, y1
)
179 dc
.DrawText('.', x1
, y1
)
182 dc
.DrawText("tries %d misses %d" % (self
.tries
,self
.misses
),x1
,50)
184 for letter
in self
.guess
:
185 guesses
= guesses
+ letter
186 dc
.DrawText("guessed:", x1
, 70)
187 dc
.DrawText(guesses
[:13], x1
+80, 70)
188 dc
.DrawText(guesses
[13:], x1
+80, 90)
189 dc
.SetUserScale(x
/1000.0, y
/1000.0)
192 def DrawVictim(self
, dc
):
193 dc
.SetPen(wx
.Pen(wx
.NamedColour('black'), 20))
194 dc
.DrawLines([(10, 980), (10,900), (700,900), (700,940), (720,940),
195 (720,980), (900,980)])
196 dc
.DrawLines([(100,900), (100, 100), (300,100)])
197 dc
.DrawLine(100,200,200,100)
198 if ( self
.misses
== 0 ): return
199 dc
.SetPen(wx
.Pen(wx
.NamedColour('blue'), 10))
200 dc
.DrawLine(300,100,300,200)
201 if ( self
.misses
== 1 ): return
202 dc
.DrawEllipse(250,200,100,100)
203 if ( self
.misses
== 2 ): return
204 dc
.DrawLine(300,300,300,600)
205 if ( self
.misses
== 3) : return
206 dc
.DrawLine(300,300,250,550)
207 if ( self
.misses
== 4) : return
208 dc
.DrawLine(300,300,350,550)
209 if ( self
.misses
== 5) : return
210 dc
.DrawLine(300,600,350,850)
211 if ( self
.misses
== 6) : return
212 dc
.DrawLine(300,600,250,850)
214 def OnPaint(self
, event
):
215 dc
= wx
.PaintDC(self
)
220 class HangmanDemo(HangmanWnd
):
221 def __init__(self
, wf
, parent
, id, pos
, size
):
222 HangmanWnd
.__init
__(self
, parent
, id, pos
, size
)
223 self
.StartGame("dummy")
227 self
.timer
= self
.PlayTimer(self
.MakeMove
)
232 self
.StartGame(self
.wf
.Get())
234 self
.left
= list('aaaabcdeeeeefghiiiiijklmnnnoooopqrssssttttuuuuvwxyz')
236 key
= self
.left
[int(random
.random()*len(self
.left
))]
237 while self
.left
.count(key
): self
.left
.remove(key
)
238 self
.start_new
= self
.HandleKey(key
)
239 self
.timer
.Start(self
.delay
)
244 class PlayTimer(wx
.Timer
):
245 def __init__(self
,func
):
246 wx
.Timer
.__init
__(self
)
255 class HangmanDemoFrame(wx
.Frame
):
256 def __init__(self
, wf
, parent
, id, pos
, size
):
257 wx
.Frame
.__init
__(self
, parent
, id, "Hangman demo", pos
, size
)
258 self
.demo
= HangmanDemo(wf
, self
, -1, wx
.DefaultPosition
, wx
.DefaultSize
)
259 self
.Bind(wx
.EVT_CLOSE
, self
.OnCloseWindow
)
261 def OnCloseWindow(self
, event
):
262 self
.demo
.timer
.Stop()
267 class AboutBox(wx
.Dialog
):
268 def __init__(self
, parent
,wf
):
269 wx
.Dialog
.__init
__(self
, parent
, -1, "About Hangman", wx
.DefaultPosition
, (350,450))
270 self
.wnd
= HangmanDemo(wf
, self
, -1, (1,1), (350,150))
271 self
.static
= wx
.StaticText(self
, -1, __doc__
, (1,160), (350, 250))
272 self
.button
= wx
.Button(self
, 2001, "OK", (150,420), (50,-1))
274 self
.button
.Bind(wx
.EVT_BUTTON
, self
.OnOK
)
276 def OnOK(self
, event
):
278 self
.EndModal(wx
.ID_OK
)
282 class MyFrame(wx
.Frame
):
283 def __init__(self
, parent
, wf
):
285 wx
.Frame
.__init
__(self
, parent
, -1, "hangman", wx
.DefaultPosition
, (400,300))
286 self
.wnd
= HangmanWnd(self
, -1)
288 menu
.Append(1001, "New")
289 menu
.Append(1002, "End")
290 menu
.AppendSeparator()
291 menu
.Append(1003, "Reset")
292 menu
.Append(1004, "Demo...")
293 menu
.AppendSeparator()
294 menu
.Append(1005, "Exit")
295 menubar
= wx
.MenuBar()
296 menubar
.Append(menu
, "Game")
298 #menu.Append(1010, "Internal", "Use internal dictionary", True)
299 menu
.Append(1011, "ASCII File...")
300 urls
= [ 'wxPython home', 'http://wxPython.org/',
301 'slashdot.org', 'http://slashdot.org/',
302 'cnn.com', 'http://cnn.com',
303 'The New York Times', 'http://www.nytimes.com',
304 'De Volkskrant', 'http://www.volkskrant.nl/frameless/25000006.html',
305 'Gnu GPL', 'http://www.fsf.org/copyleft/gpl.html',
306 'Bijbel: Genesis', 'http://www.coas.com/bijbel/gn1.htm']
308 for item
in range(0,len(urls
),2):
309 urlmenu
.Append(1020+item
/2, urls
[item
], urls
[item
+1])
310 urlmenu
.Append(1080, 'Other...', 'Enter an URL')
311 menu
.AppendMenu(1012, 'URL', urlmenu
, 'Use a webpage')
312 menu
.Append(1013, 'Dump', 'Write contents to stdout')
313 menubar
.Append(menu
, "Dictionary")
315 self
.urloffset
= 1020
317 menu
.Append(1090, "About...")
318 menubar
.Append(menu
, "Help")
319 self
.SetMenuBar(menubar
)
320 self
.CreateStatusBar(2)
321 self
.Bind(wx
.EVT_MENU
, self
.OnGameNew
, id=1001)
322 self
.Bind(wx
.EVT_MENU
, self
.OnGameEnd
, id=1002)
323 self
.Bind(wx
.EVT_MENU
, self
.OnGameReset
, id=1003)
324 self
.Bind(wx
.EVT_MENU
, self
.OnGameDemo
, id=1004)
325 self
.Bind(wx
.EVT_MENU
, self
.OnWindowClose
, id=1005)
326 self
.Bind(wx
.EVT_MENU
, self
.OnDictFile
, id=1011)
327 self
.Bind(wx
.EVT_MENU
, self
.OnDictURL
, id=1020, id2
=1020+len(urls
)/2)
328 self
.Bind(wx
.EVT_MENU
, self
.OnDictURLSel
, id=1080)
329 self
.Bind(wx
.EVT_MENU
, self
.OnDictDump
, id=1013)
330 self
.Bind(wx
.EVT_MENU
, self
.OnHelpAbout
, id=1090)
331 self
.wnd
.Bind(wx
.EVT_CHAR
, self
.OnChar
)
334 def OnGameNew(self
, event
):
337 self
.SetStatusText("",0)
338 self
.wnd
.StartGame(word
)
340 def OnGameEnd(self
, event
):
341 self
.UpdateAverages(0)
343 self
.SetStatusText("",0)
346 def OnGameReset(self
, event
=None):
353 def OnGameDemo(self
, event
):
354 frame
= HangmanDemoFrame(self
.wf
, self
, -1, wx
.DefaultPosition
, self
.GetSize())
357 def OnDictFile(self
, event
):
358 fd
= wx
.FileDialog(self
)
359 if (self
.wf
.filename
):
360 fd
.SetFilename(self
.wf
.filename
)
361 if fd
.ShowModal() == wx
.ID_OK
:
363 self
.wf
= WordFetcher(file)
365 def OnDictURL(self
, event
):
366 item
= (event
.GetId() - self
.urloffset
)*2
367 print "Trying to open %s at %s" % (self
.urls
[item
], self
.urls
[item
+1])
368 self
.wf
= URLWordFetcher(self
.urls
[item
+1])
370 def OnDictURLSel(self
, event
):
371 msg
= wx
.TextEntryDialog(self
, "Enter the URL of the dictionary document", "Enter URL")
372 if msg
.ShowModal() == wx
.ID_OK
:
374 self
.wf
= URLWordFetcher(url
)
375 def OnDictDump(self
, event
):
378 def OnHelpAbout(self
, event
):
379 about
= AboutBox(self
, self
.wf
)
381 about
.wnd
.Stop() # that damn timer won't stop!
383 def UpdateAverages(self
, has_won
):
385 self
.won
= self
.won
+ 1
386 self
.played
= self
.played
+1
387 self
.history
.append(self
.wnd
.misses
) # ugly
389 for m
in self
.history
:
391 self
.average
= float(total
/len(self
.history
))
393 def OnChar(self
, event
):
394 if not self
.in_progress
:
398 key
= event
.GetKeyCode();
400 if key
>= ord('A') and key
<= ord('Z'):
401 key
= key
+ ord('a') - ord('A')
403 if key
< 'a' or key
> 'z':
406 res
= self
.wnd
.HandleKey(key
)
408 self
.SetStatusText(self
.wnd
.message
)
410 self
.UpdateAverages(0)
411 self
.SetStatusText("Too bad, you're dead!",0)
415 self
.UpdateAverages(1)
416 self
.SetStatusText("Congratulations!",0)
418 percent
= (100.*self
.won
)/self
.played
421 self
.SetStatusText("p %d, w %d (%g %%), av %g" % (self
.played
,self
.won
, percent
, self
.average
),1)
423 def OnWindowClose(self
, event
):
430 if wx
.Platform
== '__WXGTK__':
431 defaultfile
= "/usr/share/games/hangman-words"
432 elif wx
.Platform
== '__WXMSW__':
433 defaultfile
= "c:\\windows\\hardware.txt"
436 wf
= WordFetcher(defaultfile
)
437 frame
= MyFrame(None, wf
)
438 self
.SetTopWindow(frame
)
444 if __name__
== '__main__':
449 #----------------------------------------------------------------------
454 def runTest(frame
, nb
, log
):
455 if wx
.Platform
== '__WXGTK__' or wx
.Platform
== '__WXMOTIF__':
456 defaultfile
= "/usr/share/games/hangman-words"
457 elif wx
.Platform
== '__WXMSW__':
458 defaultfile
= "c:\\windows\\hardware.txt"
461 wf
= WordFetcher(defaultfile
)
462 win
= MyFrame(frame
, wf
)
467 #----------------------------------------------------------------------