git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@41611 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
		
			
				
	
	
		
			472 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			472 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
"""Hangman.py, a simple wxPython game, inspired by the
 | 
						|
old bsd game by Ken Arnold.
 | 
						|
>From the original man page:
 | 
						|
 | 
						|
 In hangman, the computer picks a word from the on-line
 | 
						|
 word list and you must try to guess it.  The computer
 | 
						|
 keeps track of which letters have been guessed and how
 | 
						|
 many wrong guesses you have made on the screen in a
 | 
						|
 graphic fashion.
 | 
						|
 | 
						|
That says it all, doesn't it?
 | 
						|
 | 
						|
Have fun with it,
 | 
						|
 | 
						|
Harm van der Heijden (H.v.d.Heijden@phys.tue.nl)"""
 | 
						|
 | 
						|
import random,re
 | 
						|
import wx
 | 
						|
 | 
						|
 | 
						|
class WordFetcher:
 | 
						|
    builtin_words = ' albatros  banana  electrometer  eggshell'
 | 
						|
 | 
						|
    def __init__(self, filename, min_length = 5):
 | 
						|
        self.min_length = min_length
 | 
						|
        print "Trying to open file %s" % (filename,)
 | 
						|
        try:
 | 
						|
            f = open(filename, "r")
 | 
						|
        except:
 | 
						|
            print "Couldn't open dictionary file %s, using builtins" % (filename,)
 | 
						|
            self.words = self.builtin_words
 | 
						|
            self.filename = None
 | 
						|
            return
 | 
						|
        self.words = f.read()
 | 
						|
        self.filename = filename
 | 
						|
        print "Got %d bytes." % (len(self.words),)
 | 
						|
 | 
						|
    def SetMinLength(min_length):
 | 
						|
        self.min_length = min_length
 | 
						|
 | 
						|
    def Get(self):
 | 
						|
        reg = re.compile('\s+([a-zA-Z]+)\s+')
 | 
						|
        n = 50 # safety valve; maximum number of tries to find a suitable word
 | 
						|
        while n:
 | 
						|
            index = int(random.random()*len(self.words))
 | 
						|
            m = reg.search(self.words[index:])
 | 
						|
            if m and len(m.groups()[0]) >= self.min_length: break
 | 
						|
            n = n - 1
 | 
						|
        if n: return m.groups()[0].lower()
 | 
						|
        return "error"
 | 
						|
 | 
						|
 | 
						|
 | 
						|
def stdprint(x):
 | 
						|
    print x
 | 
						|
 | 
						|
 | 
						|
 | 
						|
class URLWordFetcher(WordFetcher):
 | 
						|
    def __init__(self, url):
 | 
						|
        self.OpenURL(url)
 | 
						|
        WordFetcher.__init__(self, "hangman_dict.txt")
 | 
						|
 | 
						|
    def logprint(self,x):
 | 
						|
        print x
 | 
						|
 | 
						|
    def RetrieveAsFile(self, host, path=''):
 | 
						|
        from httplib import HTTP
 | 
						|
        try:
 | 
						|
            h = HTTP(host)
 | 
						|
        except:
 | 
						|
            self.logprint("Failed to create HTTP connection to %s... is the network available?" % (host))
 | 
						|
            return None
 | 
						|
        h.putrequest('GET',path)
 | 
						|
        h.putheader('Accept','text/html')
 | 
						|
        h.putheader('Accept','text/plain')
 | 
						|
        h.endheaders()
 | 
						|
        errcode, errmsg, headers = h.getreply()
 | 
						|
        if errcode != 200:
 | 
						|
            self.logprint("HTTP error code %d: %s" % (errcode, errmsg))
 | 
						|
            return None
 | 
						|
        f = h.getfile()
 | 
						|
        return f
 | 
						|
 | 
						|
    def OpenURL(self,url):
 | 
						|
        from htmllib import HTMLParser
 | 
						|
        import formatter
 | 
						|
        self.url = url
 | 
						|
        m = re.match('http://([^/]+)(/\S*)\s*', url)
 | 
						|
        if m:
 | 
						|
            host = m.groups()[0]
 | 
						|
            path = m.groups()[1]
 | 
						|
        else:
 | 
						|
            m = re.match('http://(\S+)\s*', url)
 | 
						|
            if not m:
 | 
						|
                # Invalid URL
 | 
						|
                self.logprint("Invalid or unsupported URL: %s" % (url))
 | 
						|
                return
 | 
						|
            host = m.groups()[0]
 | 
						|
            path = ''
 | 
						|
        f = self.RetrieveAsFile(host,path)
 | 
						|
        if not f:
 | 
						|
            self.logprint("Could not open %s" % (url))
 | 
						|
            return
 | 
						|
        self.logprint("Receiving data...")
 | 
						|
        data = f.read()
 | 
						|
        tmp = open('hangman_dict.txt','w')
 | 
						|
        fmt = formatter.AbstractFormatter(formatter.DumbWriter(tmp))
 | 
						|
        p = HTMLParser(fmt)
 | 
						|
        self.logprint("Parsing data...")
 | 
						|
        p.feed(data)
 | 
						|
        p.close()
 | 
						|
        tmp.close()
 | 
						|
 | 
						|
 | 
						|
 | 
						|
class HangmanWnd(wx.Window):
 | 
						|
    def __init__(self, parent, id, pos=wx.DefaultPosition, size=wx.DefaultSize):
 | 
						|
        wx.Window.__init__(self, parent, id, pos, size)
 | 
						|
        self.SetBackgroundColour(wx.NamedColour('white'))
 | 
						|
        if wx.Platform == '__WXGTK__':
 | 
						|
            self.font = wx.Font(12, wx.MODERN, wx.NORMAL, wx.NORMAL)
 | 
						|
        else:
 | 
						|
            self.font = wx.Font(10, wx.MODERN, wx.NORMAL, wx.NORMAL)
 | 
						|
        self.SetFocus()
 | 
						|
        self.Bind(wx.EVT_PAINT, self.OnPaint)
 | 
						|
        self.Bind(wx.EVT_SIZE, self.OnSize)
 | 
						|
 | 
						|
    def OnSize(self, event):
 | 
						|
        self.Refresh()
 | 
						|
        
 | 
						|
    def StartGame(self, word):
 | 
						|
        self.word = word
 | 
						|
        self.guess = []
 | 
						|
        self.tries = 0
 | 
						|
        self.misses = 0
 | 
						|
        self.Draw()
 | 
						|
 | 
						|
    def EndGame(self):
 | 
						|
        self.misses = 7;
 | 
						|
        self.guess = map(chr, range(ord('a'),ord('z')+1))
 | 
						|
        self.Draw()
 | 
						|
 | 
						|
    def HandleKey(self, key):
 | 
						|
        self.message = ""
 | 
						|
        if self.guess.count(key):
 | 
						|
            self.message = 'Already guessed %s' % (key,)
 | 
						|
            return 0
 | 
						|
        self.guess.append(key)
 | 
						|
        self.guess.sort()
 | 
						|
        self.tries = self.tries+1
 | 
						|
        if not key in self.word:
 | 
						|
            self.misses = self.misses+1
 | 
						|
        if self.misses == 7:
 | 
						|
            self.EndGame()
 | 
						|
            return 1
 | 
						|
        has_won = 1
 | 
						|
        for letter in self.word:
 | 
						|
            if not self.guess.count(letter):
 | 
						|
                has_won = 0
 | 
						|
                break
 | 
						|
        if has_won:
 | 
						|
            self.Draw()
 | 
						|
            return 2
 | 
						|
        self.Draw()
 | 
						|
        return 0
 | 
						|
 | 
						|
    def Draw(self, dc = None):
 | 
						|
        if not dc:
 | 
						|
            dc = wx.ClientDC(self)
 | 
						|
        dc.SetFont(self.font)
 | 
						|
        dc.Clear()
 | 
						|
        (x,y) = self.GetSizeTuple()
 | 
						|
        x1 = x-200; y1 = 20
 | 
						|
        for letter in self.word:
 | 
						|
            if self.guess.count(letter):
 | 
						|
                dc.DrawText(letter, x1, y1)
 | 
						|
            else:
 | 
						|
                dc.DrawText('.', x1, y1)
 | 
						|
            x1 = x1 + 10
 | 
						|
        x1 = x-200
 | 
						|
        dc.DrawText("tries %d misses %d" % (self.tries,self.misses),x1,50)
 | 
						|
        guesses = ""
 | 
						|
        for letter in self.guess:
 | 
						|
            guesses = guesses + letter
 | 
						|
        dc.DrawText("guessed:", x1, 70)
 | 
						|
        dc.DrawText(guesses[:13], x1+80, 70)
 | 
						|
        dc.DrawText(guesses[13:], x1+80, 90)
 | 
						|
        dc.SetUserScale(x/1000.0, y/1000.0)
 | 
						|
        self.DrawVictim(dc)
 | 
						|
 | 
						|
    def DrawVictim(self, dc):
 | 
						|
        dc.SetPen(wx.Pen(wx.NamedColour('black'), 20))
 | 
						|
        dc.DrawLines([(10, 980), (10,900), (700,900), (700,940), (720,940),
 | 
						|
                      (720,980), (900,980)])
 | 
						|
        dc.DrawLines([(100,900), (100, 100), (300,100)])
 | 
						|
        dc.DrawLine(100,200,200,100)
 | 
						|
        if ( self.misses == 0 ): return
 | 
						|
        dc.SetPen(wx.Pen(wx.NamedColour('blue'), 10))
 | 
						|
        dc.DrawLine(300,100,300,200)
 | 
						|
        if ( self.misses == 1 ): return
 | 
						|
        dc.DrawEllipse(250,200,100,100)
 | 
						|
        if ( self.misses == 2 ): return
 | 
						|
        dc.DrawLine(300,300,300,600)
 | 
						|
        if ( self.misses == 3) : return
 | 
						|
        dc.DrawLine(300,300,250,550)
 | 
						|
        if ( self.misses == 4) : return
 | 
						|
        dc.DrawLine(300,300,350,550)
 | 
						|
        if ( self.misses == 5) : return
 | 
						|
        dc.DrawLine(300,600,350,850)
 | 
						|
        if ( self.misses == 6) : return
 | 
						|
        dc.DrawLine(300,600,250,850)
 | 
						|
 | 
						|
    def OnPaint(self, event):
 | 
						|
        dc = wx.PaintDC(self)
 | 
						|
        self.Draw(dc)
 | 
						|
 | 
						|
 | 
						|
 | 
						|
class HangmanDemo(HangmanWnd):
 | 
						|
    def __init__(self, wf, parent, id, pos, size):
 | 
						|
        HangmanWnd.__init__(self, parent, id, pos, size)
 | 
						|
        self.StartGame("dummy")
 | 
						|
        self.start_new = 1
 | 
						|
        self.wf = wf
 | 
						|
        self.delay = 500
 | 
						|
        self.timer = self.PlayTimer(self.MakeMove)
 | 
						|
 | 
						|
    def MakeMove(self):
 | 
						|
        self.timer.Stop()
 | 
						|
        if self.start_new:
 | 
						|
            self.StartGame(self.wf.Get())
 | 
						|
            self.start_new = 0
 | 
						|
            self.left = list('aaaabcdeeeeefghiiiiijklmnnnoooopqrssssttttuuuuvwxyz')
 | 
						|
        else:
 | 
						|
            key = self.left[int(random.random()*len(self.left))]
 | 
						|
            while self.left.count(key): self.left.remove(key)
 | 
						|
            self.start_new = self.HandleKey(key)
 | 
						|
        self.timer.Start(self.delay)
 | 
						|
 | 
						|
    def Stop(self):
 | 
						|
        self.timer.Stop()
 | 
						|
 | 
						|
    class PlayTimer(wx.Timer):
 | 
						|
        def __init__(self,func):
 | 
						|
            wx.Timer.__init__(self)
 | 
						|
            self.func = func
 | 
						|
            self.Start(1000)
 | 
						|
 | 
						|
        def Notify(self):
 | 
						|
            apply(self.func, ())
 | 
						|
 | 
						|
 | 
						|
 | 
						|
class HangmanDemoFrame(wx.Frame):
 | 
						|
    def __init__(self, wf, parent, id, pos, size):
 | 
						|
        wx.Frame.__init__(self, parent, id, "Hangman demo", pos, size)
 | 
						|
        self.demo = HangmanDemo(wf, self, -1, wx.DefaultPosition, wx.DefaultSize)
 | 
						|
        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
 | 
						|
 | 
						|
    def OnCloseWindow(self, event):
 | 
						|
        self.demo.timer.Stop()
 | 
						|
        self.Destroy()
 | 
						|
 | 
						|
 | 
						|
 | 
						|
class AboutBox(wx.Dialog):
 | 
						|
    def __init__(self, parent,wf):
 | 
						|
        wx.Dialog.__init__(self, parent, -1, "About Hangman", wx.DefaultPosition, (350,450))
 | 
						|
        self.wnd = HangmanDemo(wf, self, -1, (1,1), (350,150))
 | 
						|
        self.static = wx.StaticText(self, -1, __doc__, (1,160), (350, 250))
 | 
						|
        self.button = wx.Button(self, 2001, "OK", (150,420), (50,-1))
 | 
						|
        self.Fit()
 | 
						|
        self.button.Bind(wx.EVT_BUTTON, self.OnOK)
 | 
						|
 | 
						|
    def OnOK(self, event):
 | 
						|
        self.wnd.Stop()
 | 
						|
        self.EndModal(wx.ID_OK)
 | 
						|
 | 
						|
 | 
						|
 | 
						|
class MyFrame(wx.Frame):
 | 
						|
    def __init__(self, parent, wf):
 | 
						|
        self.wf = wf
 | 
						|
        wx.Frame.__init__(self, parent, -1, "hangman", wx.DefaultPosition, (400,300))
 | 
						|
        self.wnd = HangmanWnd(self, -1)
 | 
						|
        menu = wx.Menu()
 | 
						|
        menu.Append(1001, "New")
 | 
						|
        menu.Append(1002, "End")
 | 
						|
        menu.AppendSeparator()
 | 
						|
        menu.Append(1003, "Reset")
 | 
						|
        menu.Append(1004, "Demo...")
 | 
						|
        menu.AppendSeparator()
 | 
						|
        menu.Append(1005, "Exit")
 | 
						|
        menubar = wx.MenuBar()
 | 
						|
        menubar.Append(menu, "Game")
 | 
						|
        menu = wx.Menu()
 | 
						|
        #menu.Append(1010, "Internal", "Use internal dictionary", True)
 | 
						|
        menu.Append(1011, "ASCII File...")
 | 
						|
        urls = [ 'wxPython home', 'http://wxPython.org/',
 | 
						|
                 'slashdot.org', 'http://slashdot.org/',
 | 
						|
                 'cnn.com', 'http://cnn.com',
 | 
						|
                 'The New York Times', 'http://www.nytimes.com',
 | 
						|
                 'De Volkskrant', 'http://www.volkskrant.nl/frameless/25000006.html',
 | 
						|
                 'Gnu GPL', 'http://www.fsf.org/copyleft/gpl.html',
 | 
						|
                 'Bijbel: Genesis', 'http://www.coas.com/bijbel/gn1.htm']
 | 
						|
        urlmenu = wx.Menu()
 | 
						|
        for item in range(0,len(urls),2):
 | 
						|
            urlmenu.Append(1020+item/2, urls[item], urls[item+1])
 | 
						|
        urlmenu.Append(1080, 'Other...', 'Enter an URL')
 | 
						|
        menu.AppendMenu(1012, 'URL', urlmenu, 'Use a webpage')
 | 
						|
        menu.Append(1013, 'Dump', 'Write contents to stdout')
 | 
						|
        menubar.Append(menu, "Dictionary")
 | 
						|
        self.urls = urls
 | 
						|
        self.urloffset = 1020
 | 
						|
        menu = wx.Menu()
 | 
						|
        menu.Append(1090, "About...")
 | 
						|
        menubar.Append(menu, "Help")
 | 
						|
        self.SetMenuBar(menubar)
 | 
						|
        self.CreateStatusBar(2)
 | 
						|
        self.Bind(wx.EVT_MENU, self.OnGameNew, id=1001)
 | 
						|
        self.Bind(wx.EVT_MENU, self.OnGameEnd, id=1002)
 | 
						|
        self.Bind(wx.EVT_MENU, self.OnGameReset, id=1003)
 | 
						|
        self.Bind(wx.EVT_MENU, self.OnGameDemo, id=1004)
 | 
						|
        self.Bind(wx.EVT_MENU, self.OnWindowClose, id=1005)
 | 
						|
        self.Bind(wx.EVT_MENU, self.OnDictFile, id=1011)
 | 
						|
        self.Bind(wx.EVT_MENU, self.OnDictURL, id=1020, id2=1020+len(urls)/2)
 | 
						|
        self.Bind(wx.EVT_MENU, self.OnDictURLSel, id=1080)
 | 
						|
        self.Bind(wx.EVT_MENU, self.OnDictDump, id=1013)
 | 
						|
        self.Bind(wx.EVT_MENU, self.OnHelpAbout, id=1090)
 | 
						|
        self.wnd.Bind(wx.EVT_CHAR, self.OnChar)
 | 
						|
        self.OnGameReset()
 | 
						|
 | 
						|
    def OnGameNew(self, event):
 | 
						|
        word = self.wf.Get()
 | 
						|
        self.in_progress = 1
 | 
						|
        self.SetStatusText("",0)
 | 
						|
        self.wnd.StartGame(word)
 | 
						|
 | 
						|
    def OnGameEnd(self, event):
 | 
						|
        self.UpdateAverages(0)
 | 
						|
        self.in_progress = 0
 | 
						|
        self.SetStatusText("",0)
 | 
						|
        self.wnd.EndGame()
 | 
						|
 | 
						|
    def OnGameReset(self, event=None):
 | 
						|
        self.played = 0
 | 
						|
        self.won = 0
 | 
						|
        self.history = []
 | 
						|
        self.average = 0.0
 | 
						|
        self.OnGameNew(None)
 | 
						|
 | 
						|
    def OnGameDemo(self, event):
 | 
						|
        frame = HangmanDemoFrame(self.wf, self, -1, wx.DefaultPosition, self.GetSize())
 | 
						|
        frame.Show(True)
 | 
						|
 | 
						|
    def OnDictFile(self, event):
 | 
						|
        fd = wx.FileDialog(self)
 | 
						|
        if (self.wf.filename):
 | 
						|
            fd.SetFilename(self.wf.filename)
 | 
						|
        if fd.ShowModal() == wx.ID_OK:
 | 
						|
            file = fd.GetPath()
 | 
						|
            self.wf = WordFetcher(file)
 | 
						|
 | 
						|
    def OnDictURL(self, event):
 | 
						|
        item = (event.GetId() - self.urloffset)*2
 | 
						|
        print "Trying to open %s at %s" % (self.urls[item], self.urls[item+1])
 | 
						|
        self.wf = URLWordFetcher(self.urls[item+1])
 | 
						|
 | 
						|
    def OnDictURLSel(self, event):
 | 
						|
        msg = wx.TextEntryDialog(self, "Enter the URL of the dictionary document", "Enter URL")
 | 
						|
        if msg.ShowModal() == wx.ID_OK:
 | 
						|
            url = msg.GetValue()
 | 
						|
            self.wf = URLWordFetcher(url)
 | 
						|
    def OnDictDump(self, event):
 | 
						|
        print self.wf.words
 | 
						|
 | 
						|
    def OnHelpAbout(self, event):
 | 
						|
        about = AboutBox(self, self.wf)
 | 
						|
        about.ShowModal()
 | 
						|
        about.wnd.Stop() # that damn timer won't stop!
 | 
						|
 | 
						|
    def UpdateAverages(self, has_won):
 | 
						|
        if has_won:
 | 
						|
            self.won = self.won + 1
 | 
						|
        self.played = self.played+1
 | 
						|
        self.history.append(self.wnd.misses) # ugly
 | 
						|
        total = 0.0
 | 
						|
        for m in self.history:
 | 
						|
            total = total + m
 | 
						|
        self.average = float(total/len(self.history))
 | 
						|
 | 
						|
    def OnChar(self, event):
 | 
						|
        if not self.in_progress:
 | 
						|
            #print "new"
 | 
						|
            self.OnGameNew(None)
 | 
						|
            return
 | 
						|
        key = event.GetKeyCode();
 | 
						|
        #print key
 | 
						|
        if key >= ord('A') and key <= ord('Z'):
 | 
						|
            key = key + ord('a') - ord('A')
 | 
						|
        key = chr(key)
 | 
						|
        if key < 'a' or key > 'z':
 | 
						|
            event.Skip()
 | 
						|
            return
 | 
						|
        res = self.wnd.HandleKey(key)
 | 
						|
        if res == 0:
 | 
						|
            self.SetStatusText(self.wnd.message)
 | 
						|
        elif res == 1:
 | 
						|
            self.UpdateAverages(0)
 | 
						|
            self.SetStatusText("Too bad, you're dead!",0)
 | 
						|
            self.in_progress = 0
 | 
						|
        elif res == 2:
 | 
						|
            self.in_progress = 0
 | 
						|
            self.UpdateAverages(1)
 | 
						|
            self.SetStatusText("Congratulations!",0)
 | 
						|
        if self.played:
 | 
						|
            percent = (100.*self.won)/self.played
 | 
						|
        else:
 | 
						|
            percent = 0.0
 | 
						|
        self.SetStatusText("p %d, w %d (%g %%), av %g" % (self.played,self.won, percent, self.average),1)
 | 
						|
 | 
						|
    def OnWindowClose(self, event):
 | 
						|
        self.Destroy()
 | 
						|
 | 
						|
 | 
						|
 | 
						|
class MyApp(wx.App):
 | 
						|
    def OnInit(self):
 | 
						|
        if wx.Platform == '__WXGTK__':
 | 
						|
            defaultfile = "/usr/share/games/hangman-words"
 | 
						|
        elif wx.Platform == '__WXMSW__':
 | 
						|
            defaultfile = "c:\\windows\\hardware.txt"
 | 
						|
        else:
 | 
						|
            defaultfile = ""
 | 
						|
        wf = WordFetcher(defaultfile)
 | 
						|
        frame = MyFrame(None, wf)
 | 
						|
        self.SetTopWindow(frame)
 | 
						|
        frame.Show(True)
 | 
						|
        return True
 | 
						|
 | 
						|
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    app = MyApp(0)
 | 
						|
    app.MainLoop()
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------
 | 
						|
 | 
						|
overview = __doc__
 | 
						|
 | 
						|
 | 
						|
def runTest(frame, nb, log):
 | 
						|
    if wx.Platform == '__WXGTK__' or wx.Platform == '__WXMOTIF__':
 | 
						|
        defaultfile = "/usr/share/games/hangman-words"
 | 
						|
    elif wx.Platform == '__WXMSW__':
 | 
						|
        defaultfile = "c:\\windows\\hardware.txt"
 | 
						|
    else:
 | 
						|
        defaultfile = ""
 | 
						|
    wf = WordFetcher(defaultfile)
 | 
						|
    win = MyFrame(frame, wf)
 | 
						|
    frame.otherWin = win
 | 
						|
    win.Show(True)
 | 
						|
 | 
						|
 | 
						|
#----------------------------------------------------------------------
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 |