]>
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
.KeyCode(); 
 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 #----------------------------------------------------------------------