]>
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)""" 
  18 from wxPython
.wx 
import * 
  23     builtin_words 
= ' albatros  banana  electrometer  eggshell' 
  25     def __init__(self
, filename
, min_length 
= 5): 
  26         self
.min_length 
= min_length
 
  27         print "Trying to open file %s" % (filename
,) 
  29             f 
= open(filename
, "r") 
  31             print "Couldn't open dictionary file %s, using builtins" % (filename
,) 
  32             self
.words 
= self
.builtin_words
 
  36         self
.filename 
= filename
 
  37         print "Got %d bytes." % (len(self
.words
),) 
  39     def SetMinLength(min_length
): 
  40         self
.min_length 
= min_length
 
  43         reg 
= re
.compile('\s+([a-zA-Z]+)\s+') 
  44         n 
= 50 # safety valve; maximum number of tries to find a suitable word 
  46             index 
= int(random
.random()*len(self
.words
)) 
  47             m 
= reg
.search(self
.words
[index
:]) 
  48             if m 
and len(m
.groups()[0]) >= self
.min_length
: break 
  50         if n
: return m
.groups()[0].lower() 
  60 class URLWordFetcher(WordFetcher
): 
  61     def __init__(self
, url
): 
  63         WordFetcher
.__init
__(self
, "hangman_dict.txt") 
  68     def RetrieveAsFile(self
, host
, path
=''): 
  69         from httplib 
import HTTP
 
  73             self
.logprint("Failed to create HTTP connection to %s... is the network available?" % (host
)) 
  75         h
.putrequest('GET',path
) 
  76         h
.putheader('Accept','text/html') 
  77         h
.putheader('Accept','text/plain') 
  79         errcode
, errmsg
, headers 
= h
.getreply() 
  81             self
.logprint("HTTP error code %d: %s" % (errcode
, errmsg
)) 
  86     def OpenURL(self
,url
): 
  87         from htmllib 
import HTMLParser
 
  90         m 
= re
.match('http://([^/]+)(/\S*)\s*', url
) 
  95             m 
= re
.match('http://(\S+)\s*', url
) 
  98                 self
.logprint("Invalid or unsupported URL: %s" % (url
)) 
 102         f 
= self
.RetrieveAsFile(host
,path
) 
 104             self
.logprint("Could not open %s" % (url
)) 
 106         self
.logprint("Receiving data...") 
 108         tmp 
= open('hangman_dict.txt','w') 
 109         fmt 
= formatter
.AbstractFormatter(formatter
.DumbWriter(tmp
)) 
 111         self
.logprint("Parsing data...") 
 118 class HangmanWnd(wxWindow
): 
 119     def __init__(self
, parent
, id, pos
=wxDefaultPosition
, size
=wxDefaultSize
): 
 120         wxWindow
.__init
__(self
, parent
, id, pos
, size
) 
 121         self
.SetBackgroundColour(wxNamedColour('white')) 
 122         if wxPlatform 
== '__WXGTK__': 
 123             self
.font 
= wxFont(12, wxMODERN
, wxNORMAL
, wxNORMAL
) 
 125             self
.font 
= wxFont(10, wxMODERN
, wxNORMAL
, wxNORMAL
) 
 127         EVT_PAINT(self
, self
.OnPaint
) 
 130     def StartGame(self
, word
): 
 139         self
.guess 
= map(chr, range(ord('a'),ord('z')+1)) 
 142     def HandleKey(self
, key
): 
 144         if self
.guess
.count(key
): 
 145             self
.message 
= 'Already guessed %s' % (key
,) 
 147         self
.guess
.append(key
) 
 149         self
.tries 
= self
.tries
+1 
 150         if not key 
in self
.word
: 
 151             self
.misses 
= self
.misses
+1 
 156         for letter 
in self
.word
: 
 157             if not self
.guess
.count(letter
): 
 166     def Draw(self
, dc 
= None): 
 168             dc 
= wxClientDC(self
) 
 169         dc
.SetFont(self
.font
) 
 171         (x
,y
) = self
.GetSizeTuple() 
 173         for letter 
in self
.word
: 
 174             if self
.guess
.count(letter
): 
 175                 dc
.DrawText(letter
, x1
, y1
) 
 177                 dc
.DrawText('.', x1
, y1
) 
 180         dc
.DrawText("tries %d misses %d" % (self
.tries
,self
.misses
),x1
,50) 
 182         for letter 
in self
.guess
: 
 183             guesses 
= guesses 
+ letter
 
 184         dc
.DrawText("guessed:", x1
, 70) 
 185         dc
.DrawText(guesses
[:13], x1
+80, 70) 
 186         dc
.DrawText(guesses
[13:], x1
+80, 90) 
 187         dc
.SetUserScale(x
/1000.0, y
/1000.0) 
 190     def DrawVictim(self
, dc
): 
 191         dc
.SetPen(wxPen(wxNamedColour('black'), 20)) 
 192         dc
.DrawLines([(10, 980), (10,900), (700,900), (700,940), (720,940), 
 193                       (720,980), (900,980)]) 
 194         dc
.DrawLines([(100,900), (100, 100), (300,100)]) 
 195         dc
.DrawLine(100,200,200,100) 
 196         if ( self
.misses 
== 0 ): return 
 197         dc
.SetPen(wxPen(wxNamedColour('blue'), 10)) 
 198         dc
.DrawLine(300,100,300,200) 
 199         if ( self
.misses 
== 1 ): return 
 200         dc
.DrawEllipse(250,200,100,100) 
 201         if ( self
.misses 
== 2 ): return 
 202         dc
.DrawLine(300,300,300,600) 
 203         if ( self
.misses 
== 3) : return 
 204         dc
.DrawLine(300,300,250,550) 
 205         if ( self
.misses 
== 4) : return 
 206         dc
.DrawLine(300,300,350,550) 
 207         if ( self
.misses 
== 5) : return 
 208         dc
.DrawLine(300,600,350,850) 
 209         if ( self
.misses 
== 6) : return 
 210         dc
.DrawLine(300,600,250,850) 
 212     def OnPaint(self
, event
): 
 218 class HangmanDemo(HangmanWnd
): 
 219     def __init__(self
, wf
, parent
, id, pos
, size
): 
 220         HangmanWnd
.__init
__(self
, parent
, id, pos
, size
) 
 221         self
.StartGame("dummy") 
 225         self
.timer 
= self
.PlayTimer(self
.MakeMove
) 
 230             self
.StartGame(self
.wf
.Get()) 
 232             self
.left 
= list('aaaabcdeeeeefghiiiiijklmnnnoooopqrssssttttuuuuvwxyz') 
 234             key 
= self
.left
[int(random
.random()*len(self
.left
))] 
 235             while self
.left
.count(key
): self
.left
.remove(key
) 
 236             self
.start_new 
= self
.HandleKey(key
) 
 237         self
.timer
.Start(self
.delay
) 
 242     class PlayTimer(wxTimer
): 
 243         def __init__(self
,func
): 
 244             wxTimer
.__init
__(self
) 
 253 class HangmanDemoFrame(wxFrame
): 
 254     def __init__(self
, wf
, parent
, id, pos
, size
): 
 255         wxFrame
.__init
__(self
, parent
, id, "Hangman demo", pos
, size
) 
 256         self
.demo 
= HangmanDemo(wf
, self
, -1, wxDefaultPosition
, wxDefaultSize
) 
 257         EVT_CLOSE(self
, self
.OnCloseWindow
) 
 259     def OnCloseWindow(self
, event
): 
 260         self
.demo
.timer
.Stop() 
 265 class AboutBox(wxDialog
): 
 266     def __init__(self
, parent
,wf
): 
 267         wxDialog
.__init
__(self
, parent
, -1, "About Hangman", wxDefaultPosition
, wxSize(350,450)) 
 268         self
.wnd 
= HangmanDemo(wf
, self
, -1, wxPoint(1,1), wxSize(350,150)) 
 269         self
.static 
= wxStaticText(self
, -1, __doc__
, wxPoint(1,160), wxSize(350, 250)) 
 270         self
.button 
= wxButton(self
, 2001, "OK", wxPoint(150,420), wxSize(50,-1)) 
 271         EVT_BUTTON(self
, 2001, self
.OnOK
) 
 273     def OnOK(self
, event
): 
 275         self
.EndModal(wxID_OK
) 
 279 class MyFrame(wxFrame
): 
 280     def __init__(self
, parent
, wf
): 
 282         wxFrame
.__init
__(self
, parent
, -1, "hangman", wxDefaultPosition
, wxSize(400,300)) 
 283         self
.wnd 
= HangmanWnd(self
, -1) 
 285         menu
.Append(1001, "New") 
 286         menu
.Append(1002, "End") 
 287         menu
.AppendSeparator() 
 288         menu
.Append(1003, "Reset") 
 289         menu
.Append(1004, "Demo...") 
 290         menu
.AppendSeparator() 
 291         menu
.Append(1005, "Exit") 
 292         menubar 
= wxMenuBar() 
 293         menubar
.Append(menu
, "Game") 
 295         #menu.Append(1010, "Internal", "Use internal dictionary", True) 
 296         menu
.Append(1011, "ASCII File...") 
 297         urls 
= [ 'wxPython home', 'http://wxPython.org/', 
 298                  'slashdot.org', 'http://slashdot.org/', 
 299                  'cnn.com', 'http://cnn.com', 
 300                  'The New York Times', 'http://www.nytimes.com', 
 301                  'De Volkskrant', 'http://www.volkskrant.nl/frameless/25000006.html', 
 302                  'Gnu GPL', 'http://www.fsf.org/copyleft/gpl.html', 
 303                  'Bijbel: Genesis', 'http://www.coas.com/bijbel/gn1.htm'] 
 305         for item 
in range(0,len(urls
),2): 
 306             urlmenu
.Append(1020+item
/2, urls
[item
], urls
[item
+1]) 
 307         urlmenu
.Append(1080, 'Other...', 'Enter an URL') 
 308         menu
.AppendMenu(1012, 'URL', urlmenu
, 'Use a webpage') 
 309         menu
.Append(1013, 'Dump', 'Write contents to stdout') 
 310         menubar
.Append(menu
, "Dictionary") 
 312         self
.urloffset 
= 1020 
 314         menu
.Append(1090, "About...") 
 315         menubar
.Append(menu
, "Help") 
 316         self
.SetMenuBar(menubar
) 
 317         self
.CreateStatusBar(2) 
 318         EVT_MENU(self
, 1001, self
.OnGameNew
) 
 319         EVT_MENU(self
, 1002, self
.OnGameEnd
) 
 320         EVT_MENU(self
, 1003, self
.OnGameReset
) 
 321         EVT_MENU(self
, 1004, self
.OnGameDemo
) 
 322         EVT_MENU(self
, 1005, self
.OnWindowClose
) 
 323         EVT_MENU(self
, 1011, self
.OnDictFile
) 
 324         EVT_MENU_RANGE(self
, 1020, 1020+len(urls
)/2, self
.OnDictURL
) 
 325         EVT_MENU(self
, 1080, self
.OnDictURLSel
) 
 326         EVT_MENU(self
, 1013, self
.OnDictDump
) 
 327         EVT_MENU(self
, 1090, self
.OnHelpAbout
) 
 328         EVT_CHAR(self
.wnd
, self
.OnChar
) 
 331     def OnGameNew(self
, event
): 
 334         self
.SetStatusText("",0) 
 335         self
.wnd
.StartGame(word
) 
 337     def OnGameEnd(self
, event
): 
 338         self
.UpdateAverages(0) 
 340         self
.SetStatusText("",0) 
 343     def OnGameReset(self
, event
=None): 
 350     def OnGameDemo(self
, event
): 
 351         frame 
= HangmanDemoFrame(self
.wf
, self
, -1, wxDefaultPosition
, self
.GetSize()) 
 354     def OnDictFile(self
, event
): 
 355         fd 
= wxFileDialog(self
) 
 356         if (self
.wf
.filename
): 
 357             fd
.SetFilename(self
.wf
.filename
) 
 358         if fd
.ShowModal() == wxID_OK
: 
 360             self
.wf 
= WordFetcher(file) 
 362     def OnDictURL(self
, event
): 
 363         item 
= (event
.GetId() - self
.urloffset
)*2 
 364         print "Trying to open %s at %s" % (self
.urls
[item
], self
.urls
[item
+1]) 
 365         self
.wf 
= URLWordFetcher(self
.urls
[item
+1]) 
 367     def OnDictURLSel(self
, event
): 
 368         msg 
= wxTextEntryDialog(self
, "Enter the URL of the dictionary document", "Enter URL") 
 369         if msg
.ShowModal() == wxID_OK
: 
 371             self
.wf 
= URLWordFetcher(url
) 
 372     def OnDictDump(self
, event
): 
 375     def OnHelpAbout(self
, event
): 
 376         about 
= AboutBox(self
, self
.wf
) 
 378         about
.wnd
.Stop() # that damn timer won't stop! 
 380     def UpdateAverages(self
, has_won
): 
 382             self
.won 
= self
.won 
+ 1 
 383         self
.played 
= self
.played
+1 
 384         self
.history
.append(self
.wnd
.misses
) # ugly 
 386         for m 
in self
.history
: 
 388         self
.average 
= float(total
/len(self
.history
)) 
 390     def OnChar(self
, event
): 
 391         if not self
.in_progress
: 
 395         key 
= event
.KeyCode(); 
 397         if key 
>= ord('A') and key 
<= ord('Z'): 
 398             key 
= key 
+ ord('a') - ord('A') 
 400         if key 
< 'a' or key 
> 'z': 
 403         res 
= self
.wnd
.HandleKey(key
) 
 405             self
.SetStatusText(self
.wnd
.message
) 
 407             self
.UpdateAverages(0) 
 408             self
.SetStatusText("Too bad, you're dead!",0) 
 412             self
.UpdateAverages(1) 
 413             self
.SetStatusText("Congratulations!",0) 
 415             percent 
= (100.*self
.won
)/self
.played
 
 418         self
.SetStatusText("p %d, w %d (%g %%), av %g" % (self
.played
,self
.won
, percent
, self
.average
),1) 
 420     def OnWindowClose(self
, event
): 
 427         if wxPlatform 
== '__WXGTK__': 
 428             defaultfile 
= "/usr/share/games/hangman-words" 
 429         elif wxPlatform 
== '__WXMSW__': 
 430             defaultfile 
= "c:\\windows\\hardware.txt" 
 433         wf 
= WordFetcher(defaultfile
) 
 434         frame 
= MyFrame(None, wf
) 
 435         self
.SetTopWindow(frame
) 
 441 if __name__ 
== '__main__': 
 446 #---------------------------------------------------------------------- 
 451 def runTest(frame
, nb
, log
): 
 452     if wxPlatform 
== '__WXGTK__' or wxPlatform 
== '__WXMOTIF__': 
 453         defaultfile 
= "/usr/share/games/hangman-words" 
 454     elif wxPlatform 
== '__WXMSW__': 
 455         defaultfile 
= "c:\\windows\\hardware.txt" 
 458     wf 
= WordFetcher(defaultfile
) 
 459     win 
= MyFrame(frame
, wf
) 
 464 #----------------------------------------------------------------------