Welcome, guest | Sign In | My Account | Store | Cart
#!/usr/bin/env python

"""
The Towers of Hanoi for wxPython
by S. Foster, Jan. 2004

    based on wxHanoi.cpp
    by Martin Bernreuther
"""

from wxPython.wx import *

import cPickle
import zlib

COLOURS = [ 'RED', 'CORAL', 'YELLOW', 'GREEN', 'BLUE', 'MAGENTA', 'PURPLE' ]

ICON = """\
\x78\x9c\x9d\x91\xbd\x0a\xc2\x30\x00\x84\xf7\x3c\x45\xc0\x21\x42\
\x21\xe4\xa7\xad\xba\x3a\x74\xb4\x43\x97\xac\xa1\x88\x83\xc5\xf4\
\xfd\x27\xef\x4c\x83\x20\x18\xc1\xa3\x3d\x9a\x8f\xef\xb2\x74\xbf\
\xac\x56\x4c\xca\x3b\x89\xa7\x97\x56\x89\xd5\x89\x38\x29\x2d\x67\
\x79\x5e\xe2\x7c\x07\xf0\x04\x0d\xc0\x6e\x18\x0c\x02\xd4\x12\x8d\
\x44\xc6\x10\x02\x75\x44\x21\x5b\x1b\xea\x89\x52\xb6\xe8\x01\x1d\
\x88\x24\xd0\x25\x3d\xae\x38\x1f\xf3\xb9\x1e\x78\x27\x11\x6f\x7f\
\xbe\xdb\xfd\xfa\x7d\x9f\xfe\xf8\xc2\xfd\xd6\x40\x66\x65\x3b\x84\
\x50\xb5\x2d\xed\x52\xaf\x49\x62\x6a\x13\x47\xbb\x14\x27\x63\x4e\
\x65\xe2\x69\x97\xc2\xa4\x29\xf9\x3e\x69\x69\x97\xc2\x6f\xfc\x11\
\x4e\x3a\xda\xa5\xf4\x13\x8d\x92\x69\x2c"""

def getIconData():
    return cPickle.loads( zlib.decompress( ICON ))

class wxHanoiDisc:
    def __init__( self, n, width, height ):
        self.width = ( n + 1 ) * width
        self.height = height
        self.n = n
        self.brush = wxBrush( wxNamedColour( COLOURS[ n % len( COLOURS )] ))

class wxHanoiFrame( wxFrame ):
    def __init__( self, title, pos, size ):
        wxFrame.__init__( self, None, -1,
                            title, pos = pos, size = size )

        icon = wxIconFromXPMData( getIconData() )
        self.SetIcon( icon )

        self.CreateMenu()
        self.CreateStatusBar();

        self.sleepTime = 3
        self.numDiscs = 4

        self.pen = wxPen( wxNamedColour( 'BLACK' ), 1, wxSOLID )

        self.Initialize()

        EVT_CLOSE( self, self.OnCloseWindow )
        EVT_PAINT( self, self.OnPaint )

    def CreateMenu( self ):

        def MenuCallback( menu, item, callback ):
            id = wxNewId()
            menu.Append( id, item )
            EVT_MENU( self, id, callback )

        menuBar = wxMenuBar()

        menuFile = wxMenu()
        MenuCallback( menuFile, 'E&xit...\tCtrl-X', self.OnQuit )
        menuBar.Append( menuFile, '&File' )

        menuPlay = wxMenu()
        MenuCallback( menuPlay, '&Number of Discs...', self.OnInpNumDiscs )
        MenuCallback( menuPlay, '&Time between Moves...', self.OnInpSleepTime )
        menuPlay.AppendSeparator()
        MenuCallback( menuPlay, '&Play', self.OnPlay )
        MenuCallback( menuPlay, '&Reset', self.OnReset )
        menuBar.Append( menuPlay, '&Play' )

        menuHelp = wxMenu()
        MenuCallback( menuHelp, '&About...', self.OnAbout )
        menuBar.Append( menuHelp, '&Help' )

        self.SetMenuBar( menuBar )

    def Initialize( self ):
        self.width, self.height = self.GetClientSize()
        maxwidth = self.width / 3
        w = self.width / 6
        self.xpos = [ i * w for i in [ 1, 3, 5 ]]

        height = self.height / self.numDiscs
        width = maxwidth / self.numDiscs
        if height > width:
            height = width
        self.discheight = height
        self.discwidthfac = width
        
        discs = range( self.numDiscs )
        discs.reverse()

        self.pegs = [[ wxHanoiDisc( i, width, height ) for i in discs ], [], []]
        self.moves = 0

        width, height = self.GetClientSize()
        self.Draw( wxClientDC( self ), width, height )

    def OnQuit( self, _event ):
        self.Close( true );

    def OnCloseWindow( self, event ):
        self.Destroy()

    def OnAbout( self, event ):
        wxMessageBox( __doc__, 'About wxHanoi', wxOK|wxICON_INFORMATION, self )

    def OnInpNumDiscs( self, _event ):
        self.numDiscs = wxGetNumberFromUser( '', 'Discs:', 'Number of Discs', 
                                                self.numDiscs, 1, 25, self )
        if self.numDiscs == -1:
            self.SetStatusText( "Invalid number entered or dialog cancelled." )
        else:
            self.Initialize()
        
    def OnInpSleepTime( self, _event ):
        self.sleepTime = wxGetNumberFromUser( '', 'Wait [sec/10]:', 'Time between Moves', 
                                                self.sleepTime, 0, 10, self )
        if self.sleepTime == -1:
            self.SetStatusText( "Invalid number entered or dialog cancelled." )
        else:
            self.Initialize()

    def OnPlay( self, _event ):
        self.Initialize()
        self.SetStatusText( 'Playing' )
        self.Move( 0, 2, 1, self.numDiscs )
        self.SetStatusText( 'Finished: %s Moves' % self.moves )
    
    def OnReset( self, event ):
        self.Initialize()

    def DrawDisc( self, dc, disc, x, y ):
        assert x <= 2
        dc.SetPen( self.pen )
        dc.SetBrush( disc.brush )
        dc.DrawRectangle(
            self.xpos[x] - ( disc.width / 2 ),
            self.height - ( y * self.discheight ),
            disc.width,
            disc.height )

    def Draw( self, dc, width, height ):
        mdc = wxMemoryDC()
        mdc.SelectObject( wxEmptyBitmap( width, height ))
        mdc.BeginDrawing()
        for x, peg in enumerate( self.pegs ):
            for y, disc in enumerate( peg ):
                self.DrawDisc( mdc, disc, x, y+1 )
        mdc.EndDrawing()
        dc.Blit( 0, 0, width, height, mdc, 0, 0 )
        
    def OnPaint( self, event ):
        width, height = self.GetClientSize()
        self.Draw( wxPaintDC( self ), width, height )

    def MoveDisc( self, src, dst ):
        disc = self.pegs[src].pop()
        self.pegs[dst].append( disc )
        self.moves += 1
        self.SetStatusText( 'Move %s' % self.moves )
        width, height = self.GetClientSize()
        self.Draw( wxClientDC( self ), width, height )
        wxUsleep( 100 * self.sleepTime )

    def Move( self, src, dst, temp, n ):
        if n == 1:
            self.MoveDisc( src, dst )
        else:
            self.Move( src, temp, dst, n-1 )
            self.MoveDisc( src, dst )
            self.Move( temp, dst, src, n-1 )

class wxHanoiApp( wxApp ):
    def OnInit( self ):
        self.frame = wxHanoiFrame(
                        'Towers of Hanoi for wxPython',
                        wxPoint( 50, 50 ),
                        wxSize( 450, 350 ))
        self.frame.Show( True )
        self.SetTopWindow( self.frame )

        return True

app = wxHanoiApp(0)
app.MainLoop()

History