How to create a game for wxPython - Part 4 (Phoenix)

Keywords : Break-wall, Othello, Chess, Snake.


Demonstrating :

Tested py3.x, wx4.x and Win10.

Are you ready to use some samples ? ;)

Test, modify, correct, complete, improve and share your discoveries ! (!)


Break-wall

img_breakwall.png

   1 # break-wall.py
   2 
   3 """
   4 
   5 File   : break-wall.py
   6 Author : Masaharu Kanda - @kanlkan
   7 Link   : https://qiita.com/kanlkan/items/5b8d2c5b567a8d5ae6bf
   8 Link   : https://github.com/kanlkan/break-wall
   9 Tested : LinuxMint 17.3 (python 2.7.6 + wxpython 3.0.3) (Classic)
  10 Tested : Windows 10 (python 2.7.11 + wxpython 3.0.2 (Classic)
  11 Tested : Windows 10 (python 3.8.5 + wxpython 4.1.1 (Phoenix))
  12 
  13 """
  14 
  15 import wx
  16 import random
  17 import math
  18 
  19 #---------------------------------------------------------------------------
  20 
  21 gVersion = "v0.8.0"
  22 gGameState = ("INIT", "PLAYING", "END")
  23 gTimerInterval = 5 # [msec]
  24 gFieldWidth = 652
  25 gFieldHeight = 720
  26 gFieldColor = (230, 230, 160)
  27 gBarIniPos = (300, 640)
  28 gBarSize = (120, 20)
  29 gBarColor = (90, 170, 90)
  30 gBarSpeed = 60
  31 gBallRadius = 8
  32 gBallColor = (0, 0, 255)
  33 gIniBallSpeed = 5
  34 gBlkTop = 120
  35 gBlkVCnt = 8
  36 gBlkHCnt = 3
  37 gBlkSize = (80, 40)
  38 gBlkColor = ((255 ,0, 0),(0, 255 ,0),(0, 0, 255))
  39 
  40 #---------------------------------------------------------------------------
  41 
  42 # class MyFrame
  43 # class MyFieldPanel
  44 # class MyBar
  45 # class MyBall
  46 # class MyBlock
  47 
  48 #---------------------------------------------------------------------------
  49 
  50 class MyFrame(wx.Frame):
  51     def __init__(self):
  52         wx.Frame.__init__(self, None, wx.ID_ANY, "Break wall " + gVersion,
  53                           size=(gFieldWidth + 4, gFieldHeight + 4),
  54                           style=wx.DEFAULT_FRAME_STYLE & ~(wx.RESIZE_BORDER |
  55                                                            wx.MAXIMIZE_BOX))
  56 
  57         self.SetIcon(wx.Icon('./icons/wxwin.ico', wx.BITMAP_TYPE_ICO))
  58 
  59         #------------
  60 
  61         self.main_field = MyFieldPanel(self)
  62 
  63         self.Bind(wx.EVT_CLOSE, self.onClose)
  64         self.Bind(wx.EVT_TIMER, self.onTimer)
  65 
  66         self.timer = wx.Timer(self)
  67         self.timer.Start(gTimerInterval)
  68 
  69     #-----------------------------------------------------------------------
  70 
  71     def onClose(self, event):
  72         self.timer.Stop()
  73         self.Destroy()
  74 
  75 
  76     def onTimer(self, event):
  77         self.main_field.update()
  78 
  79 #---------------------------------------------------------------------------
  80 
  81 class MyFieldPanel(wx.Panel):
  82     def __init__(self, parent, pos=(2, 2), size=(gFieldWidth, gFieldHeight)):
  83         wx.Panel.__init__(self, parent, pos=pos, size=size)
  84 
  85         self.pos = pos
  86         self.size = size
  87 
  88         self.SetBackgroundColour(gFieldColor)
  89         self.SetBackgroundStyle(wx.BG_STYLE_PAINT)
  90 
  91         self.bar = MyBar(self, gBarIniPos[0], gBarIniPos[1])
  92         self.ball = MyBall(self, gBarIniPos[0], (gBarIniPos[1] - gBallRadius))
  93         self.blocks = [[MyBlock(i*gBlkSize[0], gBlkTop+j*gBlkSize[1], \
  94                               gBlkColor[(gBlkVCnt*j+i)%gBlkHCnt]) \
  95                         for i in range(gBlkVCnt)] for j in range(gBlkHCnt)]
  96         self.block_exist = [[1 for i in range(gBlkVCnt)] for j in range(gBlkHCnt)]
  97         self.state = gGameState[0]
  98 
  99         self.Bind(wx.EVT_PAINT, self.onPaint)
 100         self.Bind(wx.EVT_KEY_DOWN, self.onKey)
 101 
 102     #-----------------------------------------------------------------------
 103 
 104     def update(self):
 105         self.ball.move()
 106         self.Refresh()
 107 
 108 
 109     def onPaint(self, event):
 110         # wx.AutoBufferedPaintDC.
 111         pdc = wx.AutoBufferedPaintDCFactory(self)
 112         pdc.Clear()
 113         dc = wx.GCDC(pdc)
 114 
 115         # Paint Bar.
 116         dc.SetPen(wx.Pen(self.bar.color))
 117         dc.SetBrush(wx.Brush(self.bar.color))
 118         dc.DrawRectangle(self.bar.x, self.bar.y, self.bar.size[0], self.bar.size[1])
 119 
 120         # Paint Ball.
 121         dc.SetPen(wx.Pen(self.ball.color))
 122         dc.SetBrush(wx.Brush(self.ball.color))
 123         dc.DrawCircle(int(self.ball.x), int(self.ball.y), self.ball.radius)
 124 
 125         # Paint Blocks.
 126         for (i, b_list) in enumerate(self.blocks):
 127             for (j, b) in enumerate(b_list):
 128                 if self.block_exist[i][j] == 1:
 129                     dc.SetPen(wx.Pen(b.color,1))
 130                     dc.SetBrush(wx.Brush(b.color))
 131                     dc.DrawRectangle(b.x, b.y, b.size[0], b.size[1])
 132 
 133 
 134     def onKey(self, event):
 135         keycode = event.GetKeyCode()
 136         if keycode == wx.WXK_SPACE:
 137             if self.state == "INIT":
 138                 self.ball.shoot()
 139                 self.state = gGameState[1]
 140             elif self.state == "END":
 141                 self.initialize()
 142                 self.state = gGameState[0]
 143         elif (keycode == wx.WXK_LEFT or keycode == ord('Q')) and self.state != "END":
 144             self.bar.move(-1)
 145         elif (keycode == wx.WXK_RIGHT or keycode == ord('D')) and self.state != "END":
 146             self.bar.move(1)
 147 
 148 
 149     def initialize(self):
 150         self.bar.x = gBarIniPos[0]
 151         self.bar.y = gBarIniPos[1]
 152         self.ball.x = self.bar.x
 153         self.ball.y = self.bar.y - self.ball.radius
 154         self.ball.vec = [0, 0]
 155         self.block_exist = [[1 for i in range(gBlkVCnt)] for j in range(gBlkHCnt)]
 156 
 157 #---------------------------------------------------------------------------
 158 
 159 class MyBar(object):
 160     def __init__(self, parent, x, y, size=gBarSize, color=gBarColor, speed=gBarSpeed):
 161 
 162         self.parent = parent
 163         self.x = x
 164         self.y = y
 165         self.size = size
 166         self.color = color
 167         self.speed = speed
 168 
 169     #-----------------------------------------------------------------------
 170 
 171     def move(self, direction):
 172         if direction >= 0:
 173             self.x += self.speed
 174         elif direction < 0:
 175             self.x -= self.speed
 176 
 177         if self.x < self.parent.pos[0]:
 178             self.x = self.parent.pos[0]
 179         elif self.x > (self.parent.pos[0] + self.parent.size[0] - self.size[0]):
 180             self.x = self.parent.pos[0] + self.parent.size[0] - self.size[0]
 181 
 182 #---------------------------------------------------------------------------
 183 
 184 class MyBall(object):
 185     def __init__(self, parent, x, y, radius=gBallRadius, vec=[0.0,0.0],
 186                  color=gBallColor, speed=gIniBallSpeed):
 187 
 188         self.parent = parent
 189         self.x = x
 190         self.y = y
 191         self.radius = radius
 192         self.vec = vec
 193         self.color = color
 194         self.speed = speed
 195 
 196     #-----------------------------------------------------------------------
 197 
 198     def shoot(self):
 199         self.parent.state = gGameState[1]
 200         rand_x = random.randrange(1000, 5000)
 201         rand_y = random.randrange(2000, 5000)
 202         self.vec = (-1.0 * float(rand_x)/1000.0, -1.0 * float(rand_y)/1000.0)
 203         pass
 204 
 205 
 206     def move(self):
 207         if self.parent.state == "INIT":
 208             self.x = self.parent.bar.x
 209             self.y = self.parent.bar.y - self.radius
 210         elif self.parent.state == "END":
 211             pass
 212         else:
 213             unit = self.speed / math.sqrt(pow(self.vec[0],2) + pow(self.vec[1],2))
 214             self.x += (self.vec[0] * unit)
 215             self.y += (self.vec[1] * unit)
 216             self.collision()
 217 
 218 
 219     def collision(self):
 220         # Game over.
 221         if (self.y + self.radius) >= self.parent.size[1]:
 222             self.parent.state = gGameState[2]
 223             return
 224 
 225         # Bound at bar.
 226         if self.x >= self.parent.bar.x and \
 227            self.x <= (self.parent.bar.x + self.parent.bar.size[0]) and \
 228            self.y >= (self.parent.bar.y - self.radius) and \
 229            self.y <  (self.parent.bar.y + self.radius) and \
 230            self.vec[1] > 0:
 231 
 232             self.vec = [self.vec[0], -1*self.vec[1]]
 233             return
 234 
 235         # Bound at frame.
 236         #### Left.
 237         if self.vec[0] < 0 and \
 238            self.x <= (self.parent.pos[0] + self.radius):
 239             self.vec = [-1*self.vec[0], self.vec[1]]
 240             return
 241 
 242         #### Right.
 243         if self.vec[0] > 0 and \
 244            self.x >= (self.parent.pos[0] + self.parent.size[0] - self.radius):
 245             self.vec = [-1*self.vec[0], self.vec[1]]
 246             return
 247 
 248         #### Ceiling.
 249         if self.y <= (self.parent.pos[1] + self.radius):
 250             self.vec = [self.vec[0], -1*self.vec[1]]
 251             return
 252 
 253         # Bound at Blocks.
 254         no_block = 1
 255         for (i, b_exist_list) in enumerate(self.parent.block_exist):
 256             for (j, b_exist) in enumerate(b_exist_list):
 257                 if b_exist == 1:
 258                     no_block = 0
 259                     tgt_block = self.parent.blocks[i][j]
 260 
 261                     # Ball cross the block edge.
 262                     if self.x > (tgt_block.x - self.radius) and \
 263                        self.x < (tgt_block.x + tgt_block.size[0] + self.radius) and \
 264                        self.y > (tgt_block.y - self.radius) and \
 265                        self.y < (tgt_block.y + tgt_block.size[1] + self.radius):
 266 
 267                         self.parent.block_exist[i][j] = 0
 268                         # Update vec.
 269                         x0 = tgt_block.x + tgt_block.size[0] / 2
 270                         y0 = tgt_block.y + tgt_block.size[1] / 2
 271                         k = float(tgt_block.size[1]) / float(tgt_block.size[0])
 272                         #### Top.
 273                         if (self.vec[1] > 0) and \
 274                            (self.x < x0 and \
 275                             self.y < (k * (self.x - x0) + y0 - self.radius)) or \
 276                            (self.x > x0 and \
 277                             self.y < (-1 * k * (self.x - x0) + y0 - self.radius)):
 278 
 279                             self.vec = [self.vec[0], -1*self.vec[1]]
 280                         #### Bottom.
 281                         elif (self.vec[1] < 0) and \
 282                              (self.x < x0 and \
 283                               self.y > (-1 * k * (self.x - x0) + y0 + self.radius)) or \
 284                              (self.x > x0 and \
 285                               self.y > (k * (self.x - x0) + y0 + self.radius)):
 286 
 287                             self.vec = [self.vec[0], -1*self.vec[1]]
 288                         #### Left.
 289                         elif (self.vec[0] > 0) and (self.x < x0):
 290                             self.vec = [-1*self.vec[0], self.vec[1]]
 291                         ### Right.
 292                         elif (self.vec[0] < 0) and (self.x > x0):
 293                             self.vec = [-1*self.vec[0], self.vec[1]]
 294 
 295                         return
 296 
 297         if no_block == 1:
 298             # Game clear.
 299             self.parent.state = gGameState[2]
 300 
 301 #---------------------------------------------------------------------------
 302 
 303 class MyBlock(object):
 304     def __init__(self, x, y, color, size=gBlkSize):
 305 
 306         self.x = x
 307         self.y = y
 308         self.color = color
 309         self.size = size
 310 
 311 #---------------------------------------------------------------------------
 312 
 313 def main():
 314     app = wx.App()
 315     frame = MyFrame()
 316     frame.Show(True)
 317     app.MainLoop()
 318 
 319 #---------------------------------------------------------------------------
 320 
 321 if __name__ == '__main__':
 322     main()


Othello

img_othello.png

   1 # othello.py
   2 
   3 """
   4 
   5 File   : othello.py
   6 Author : Keames
   7 Link   : https://python-forum.io/Thread-WxPython-Polishing-an-Othello-game-I-wrote-in-wx
   8 
   9 """
  10 
  11 import wx
  12 import os
  13 
  14 #---------------------------------------------------------------------------
  15 
  16 # The wxPython implementation of an Othello game I wrote for my mother.
  17 # These are displayed in help windows when an item from the help menu is selected :
  18 
  19 gameRules = """Game Rules
  20 
  21 Othello is a game played on a board with an 8x8 grid on it. There are 2 players and 64 gamepieces, enough to fill the entire gameboard. Each piece has a black side and a white side and each player plays as either black or white. The goal is when either the board is filled or their is no valid move available to either player for the majority of the pieces on the board to have your color upturned.
  22 
  23 The application will automatically assess whether an attempted move is valid but to alleviate the frustration of guessing where one can place a gamepiece, here is a brief explanation:
  24 
  25 If a space is already occupied, the move is invalid.
  26 
  27 If there are no adjacent pieces, the move is invalid.
  28 
  29 When there are adjacent pieces, their must be at least one vertical, horizontal or diagonal row of your opponents pieces with another of your pieces on the other end.
  30 
  31 When you place a gamepiece, all adjacent vertical, horizontal or diagonal rows of your opponents pieces with another of your pieces at the other end will be flipped.
  32 
  33 You may occaisionally find yourself in a situation in which there is no valid move available to you but your opponent has a valid move. You can give your opponent your move by clicking their player button. The text in their button will turn red, indicating that it is their turn. If there are no valid moves available to you, it will say so in the status bar.
  34 """
  35 
  36 applicationHelp = """Application Help
  37 
  38 Options Menu:
  39 
  40     The options menu provides options for starting a new game,
  41     saving a game, opening an existing game, saving and quitting and
  42     quit without saving. A single player gamemode is also listed in
  43     this menu, but it is not yet implemented.
  44 
  45 Give Move to Opponent:
  46 
  47     There are situations in which the current player has no valid
  48     move, but their opponent does. In this situation, you give your
  49     move to your opponent by clicking their player button (one of the
  50     large buttons at the top or bottom of the window.) The text in
  51     their button will turn red indicating that it's their move.
  52 
  53 Options Menu Items:
  54 
  55     New Game -- Start a new game.
  56 
  57     New Single Player Game -- coming soon
  58 
  59     Save Game -- Displays a file navigator to locate and select a save
  60         file to save the current game. The destination file must be a
  61         text file (*.txt). The application will remember the path of a
  62         current saved game until it is closed, so you won't be prompted
  63         for a save file if you have specified one already. When 'new
  64         game' is clicked, the path of any previously specified save file is
  65         forgotten to avoid overwriting a pre-existing saved game.
  66 
  67     Save Game As -- Allows you to save a previously saved game
  68         under a different name. This feature can be used for among
  69         other things, creating back-ups of your saves.
  70 
  71     Open Game -- Displays a file navigator to locate the a save file
  72         (*.txt) to open a pre-existing game. The filepath of the
  73         selected game is remembered until the application is closed,
  74         so you won't be prompted to re-select the file when saving.
  75 
  76     Save and Quit -- Automatically saves the game before exiting. If
  77         no file has been previously specified, the user will be prompted
  78         for a save file.
  79 
  80     Quit Without Saving -- Quits the game without saving.
  81 """
  82 
  83 # class OthelloGameFrame
  84 # class HelpWindow
  85 
  86 #---------------------------------------------------------------------------
  87 
  88 # The program itself is implemented inside a class for encapsulation and to allow import,
  89 # ease of implementation, and readability.
  90 
  91 class OthelloGameFrame(wx.Frame):
  92     """
  93     The Othello game proper. Almost all of the code that makes this app work is in here.
  94     It uses a pretty standard constructor for a class inheriting from Frame:
  95 
  96     __init__(self, *args, **kwArgs) Passing in the size parameter is recommended.
  97     """
  98 
  99     # I know it is not recommended to create custom IDs but I didn't find wx IDs which
 100     # corresponded to the actions of these four menu items.
 101     ID_NEW_SINGLE_PLAYER = wx.NewIdRef(1)
 102     ID_SAVE_AND_QUIT = wx.NewIdRef(1)
 103     ID_GAMERULES = wx.NewIdRef(1)
 104     ID_QUIT_WITHOUT_SAVING = wx.NewIdRef(1)
 105 
 106     # Constants for buttons:
 107     PLAYER1BUTT = wx.NewIdRef(1)
 108     PLAYER2BUTT = wx.NewIdRef(1)
 109 
 110     # I couldn't use wx.NewIDRef here because it is important that these numerical IDs be 64
 111     # sequential integers.
 112     GAMEBOARDBUTTS_LOWER = 5929 # GAMEBOARDBUTTS_UPPER - 64
 113     GAMEBOARDBUTTS_UPPER = 5993
 114 
 115     # Color Constants:
 116     bgAndBoardColor = wx.Colour(0x41, 0xA1, 0x23)
 117     player1Color = wx.Colour(0x00, 0x00, 0x00)
 118     player2Color = wx.Colour(0xFF, 0xFF, 0xFF)
 119 
 120     def __init__(self, *args, **kwArgs):
 121         wx.Frame.__init__(self, *args, **kwArgs)
 122         """
 123         OthelloGameFrame.__init__(self, *args, *kwArgs)
 124         Returns an object representing the main window of the Othello game app.
 125         Specifying the size parameter in the arg list is recommended. Otherwise, it
 126         works just like a normal wx.Frame.
 127         """
 128 
 129         self.SetIcon(wx.Icon('./icons/wxwin.ico', wx.BITMAP_TYPE_ICO))
 130 
 131         #------------
 132 
 133         # non-GUI related instance variables:
 134         self.saveFile = ""
 135         self.firstPlayersMove = True
 136         self.gameAltered = False
 137 
 138         self.SetBackgroundColour(wx.Colour(0x30, 0x30, 0x30))
 139         self.CreateStatusBar()
 140 
 141         # Creating an options menu with all the non-gameplay related options
 142         # I want to make available to the user.
 143         self.optionsMenu = wx.Menu()
 144         menuNewGame = self.optionsMenu.Append(wx.ID_NEW, "&New Game", "Start a new game.")
 145         menuNewSinglePlayer = self.optionsMenu.Append(self.ID_NEW_SINGLE_PLAYER, "New Single &Player Game (Not yet implemented)", "Start a game against the AI. This feature is currently unavailable.")
 146         self.optionsMenu.AppendSeparator()
 147         menuSaveGame = self.optionsMenu.Append(wx.ID_SAVE, "&Save Game", "Save the current game and remember the filename for future saves.")
 148         menuSaveAs = self.optionsMenu.Append(wx.ID_SAVEAS, "Save Game &As...", "Save a previously save game under a new name.")
 149         menuOpenGame = self.optionsMenu.Append(wx.ID_OPEN, "&Open Game", "Open a previously saved game.")
 150         menuSaveAndQuit = self.optionsMenu.Append(self.ID_SAVE_AND_QUIT, "Sa&ve and Quit", "Save the game and quit.")
 151         menuQuitWithoutSaving = self.optionsMenu.Append(self.ID_QUIT_WITHOUT_SAVING, "Quit &Without Saving", "Close the application without saving the game.")
 152 
 153         # The help menu will display instances of HelpWindow containing a helptext
 154         # appropriate to the menu item selected.
 155         self.helpMenu = wx.Menu()
 156         menuShowGamerules = self.helpMenu.Append(self.ID_GAMERULES, "Game&rules", "Explains Othello game rules.")
 157         menuShowAppHelp = self.helpMenu.Append(wx.ID_HELP, "Application &Help", "How to use this software")
 158 
 159         # Create the toolbar.
 160         self.toolbar = wx.MenuBar()
 161         self.toolbar.Append(self.optionsMenu, "&Options")
 162         self.toolbar.Append(self.helpMenu, "&Help")
 163         self.SetMenuBar(self.toolbar)
 164 
 165         # Add Widgets
 166         player1ButtonFont = wx.Font(30, wx.ROMAN, wx.NORMAL, wx.NORMAL)
 167         player2ButtonFont = wx.Font(30, wx.ROMAN, wx.NORMAL, wx.NORMAL)
 168         gameboardButtonFont = wx.Font(12, wx.ROMAN, wx.NORMAL, wx.NORMAL)
 169         # The gameboard buttons are added iteratively. (There are 64 of them. If anybody
 170         # knows a better way to do this, I'm all ears. This code is rather messy.)
 171         throwaway = []
 172         for i in range(64):
 173             # That's a tuple being appended to the list. I know a tuple literal as the
 174             # single argument to a function is a little unreadable.
 175             gameboardButton = wx.Button(self, self.GAMEBOARDBUTTS_UPPER - i, "", style = wx.NO_BORDER)
 176             gameboardButton.SetFont(gameboardButtonFont)
 177             gameboardButton.SetBackgroundColour(self.bgAndBoardColor)
 178             gameboardButton.SetForegroundColour(wx.Colour(0xFF, 0x00, 0x00))
 179             throwaway.append(  (gameboardButton, 0, wx.EXPAND)  )
 180 
 181         buttonGrid = wx.GridSizer(8, 8, 0, 0)
 182         buttonGrid.SetHGap(2)
 183         buttonGrid.SetVGap(2)
 184         buttonGrid.AddMany(throwaway)
 185         self.gameboard = [[]]
 186         thrIndex = 0
 187         for row in range(8):
 188             for col in range(8):
 189                 self.gameboard[-1].append(throwaway[thrIndex][0])
 190                 thrIndex += 1
 191             self.gameboard.append([])
 192         del self.gameboard[-1]
 193 
 194         # Creating the layout:
 195         self.player1Butt = wx.Button(self, self.PLAYER1BUTT, "Player 1", style = wx.NO_BORDER)
 196         self.player1Butt.SetFont(player1ButtonFont)
 197         self.player1Butt.SetBackgroundColour(self.player1Color)
 198         self.player1Butt.SetForegroundColour(wx.Colour(0xFF, 0x00, 0x00))
 199         self.player2Butt = wx.Button(self, self.PLAYER2BUTT, "Player 2", style = wx.NO_BORDER)
 200         self.player2Butt.SetFont(player2ButtonFont)
 201         self.player2Butt.SetBackgroundColour(self.player2Color)
 202         self.layout = wx.BoxSizer(wx.VERTICAL)
 203         self.layout.Add(self.player1Butt, 1, wx.EXPAND)
 204         self.layout.Add(buttonGrid, 8, wx.CENTRE)
 205         self.layout.Add(self.player2Butt, 1, wx.EXPAND)
 206 
 207         self.SetSizer(self.layout)
 208 
 209         # Bind the menu events to their respective callbacks.
 210         self.Bind(wx.EVT_MENU, self.newGame, menuNewGame)
 211         self.Bind(wx.EVT_MENU, self.newSinglePlayer, menuNewSinglePlayer)
 212 
 213         self.Bind(wx.EVT_MENU, self.saveGame, menuSaveGame)
 214         self.Bind(wx.EVT_MENU, self.saveAs, menuSaveAs)
 215         self.Bind(wx.EVT_MENU, self.openGame, menuOpenGame)
 216         self.Bind(wx.EVT_MENU, self.saveAndQuit, menuSaveAndQuit)
 217         self.Bind(wx.EVT_MENU, self.quitWithoutSaving, menuQuitWithoutSaving)
 218 
 219         self.Bind(wx.EVT_MENU, self.showGameRules, menuShowGamerules)
 220         self.Bind(wx.EVT_MENU, self.showAppHelp, menuShowAppHelp)
 221 
 222         # Bind the player buttons to callbacks
 223         self.Bind(wx.EVT_BUTTON, self.player1ButtonClicked, id = self.PLAYER1BUTT)
 224         self.Bind(wx.EVT_BUTTON, self.player2ButtonClicked, id = self.PLAYER2BUTT)
 225 
 226         # Bind all close events to self.quitWithoutSaving to ensure the user is always
 227         # asked whether they're sure they want to quit without saving their game.
 228         self.Bind(wx.EVT_CLOSE, self.quitWithoutSaving)
 229 
 230         # Bind the gameboard button events to lambdas which pass row and col values to
 231         # self.gameboardButtonClicked appropriate to their position on the board.
 232         for row in range(8):
 233             for col in range(8):
 234                 callbackLambda = eval(
 235                     "lambda evt: self.gameboardButtonClicked(evt, {}, {})".format(row, col),
 236                     {"self": self})
 237                 self.Bind(wx.EVT_BUTTON, callbackLambda, self.gameboard[row][col])
 238 
 239         self.Show(True)
 240         self.newGame()
 241 
 242     #-----------------------------------------------------------------------
 243 
 244     def newGame(self, event=None):
 245         """
 246         OthelloGameFrame.newGame(self, event = None)
 247         Resets the gameboard and resets the saveFile field to prevent
 248         an existing game from being overwritten.
 249         """
 250 
 251         self.saveFile = ""
 252         self.gameAltered = False
 253         for row in range(8):
 254             for col in range(8):
 255                 self.gameboard[row][col].SetBackgroundColour(self.bgAndBoardColor)
 256 
 257         self.gameboard[3][3].SetBackgroundColour(self.player2Color)
 258         self.gameboard[3][4].SetBackgroundColour(self.player1Color)
 259         self.gameboard[4][3].SetBackgroundColour(self.player1Color)
 260         self.gameboard[4][4].SetBackgroundColour(self.player2Color)
 261 
 262         # For testing purposes:
 263         #self.gameboard[2][2].SetBackgroundColour(self.player2Color)
 264         #self.gameboard[2][5].SetBackgroundColour(self.player1Color)
 265         self.firstPlayersMove = True
 266         self.player1Butt.SetForegroundColour("RED")
 267         self.player2Butt.SetForegroundColour("BLACK")
 268 
 269 
 270     def newSinglePlayer(self, event=None):
 271         """
 272         OthelloGameFrame.newSinglePlayer(self, event = None)
 273         This feature is not yet implemented.
 274         """
 275 
 276         print("I am a computer. A smart, handsome programmer has to teach me Othello.")
 277 
 278 
 279     def saveGame(self, event=None):
 280         """
 281         OthelloGameFrame.saveGame(self, event = None)
 282         Prompt the user for a file in which to save the game if none has been selected yet
 283         and save the game
 284         """
 285 
 286         saveList = [[]]
 287         for row in range(8):
 288             for col in range(8):
 289                 # Map each gameboard color to a number: 0 for empty space, 1 for player 1 (black), and 2 for player 2.
 290                 saveList[-1].append(
 291                     {str(self.bgAndBoardColor): 0, str(self.player1Color): 1, str(self.player2Color):2}[str(self.gameboard[row][col].GetBackgroundColour())])
 292             if row != 7: saveList.append([])
 293 
 294         saveDict = {"saveList": saveList, "firstPlayersMove": self.firstPlayersMove} # Save everything in a dictionary.
 295 
 296         # If no file has been previously selected, use a wx.FileDialog to get the
 297         # path of the file in which the user wants to save their game.
 298         if self.saveFile == "":
 299 
 300             fd = wx.FileDialog(self, "Select a file", os.getcwd(), "", "*.txt", wx.FD_OPEN)
 301             if fd.ShowModal() == wx.ID_OK:
 302                 self.saveFile = fd.GetPath()
 303             else:
 304                 fd.Destroy()
 305                 return
 306             fd.Destroy()
 307 
 308         # Save the game as a string representation of saveDict.
 309         with open(self.saveFile, "w") as f:
 310             try:
 311                 f.write(repr(saveDict))
 312             except FileNotFoundError:
 313                 mdlg = wx.MessageDialog(self, "The currently selected file could not be accessed at this time. Please try again.", "wxOthello", wx.OK)
 314                 mdlg.ShowModal()
 315                 mdlg.Destroy()
 316                 self.saveFile = ""
 317         self.gameAltered = False
 318 
 319 
 320     def saveAs(self, event=None):
 321         """
 322         OthelloGameFrame.saveAs(self, event = None)
 323         Save a previously saved game under a different filename.
 324         """
 325 
 326         self.saveFile = ""
 327         self.saveGame()
 328 
 329 
 330     def openGame(self, event=None):
 331         """
 332         OthelloGameFrame.openGame(self, event = None)
 333         Open a previously saved game stored in the format described in the saveGame method
 334         {"saveList": [Nested lists containing integers mapped to gameboard colors], "firstPlayersMove": True/False}
 335         """
 336 
 337         # Use wx.FileDialog to get the save file to open.
 338         fd = wx.FileDialog(self, "Select a file", os.getcwd(), "", "*.txt", wx.FD_OPEN)
 339         if fd.ShowModal() == wx.ID_OK:
 340             self.saveFile = fd.GetPath()
 341         else:
 342             fd.Destroy()
 343             return
 344         fd.Destroy()
 345 
 346         # Open the save file and convert its contents into a dictionary.
 347         with open(self.saveFile, "r") as f:
 348             try:
 349                 saveDict = eval(f.read())
 350             except FileNotFoundError:
 351                 mdlg = wx.MessageDialog(self, "The currently selected file could not be accessed at this time. Please try again.", "wxOthello", wx.OK)
 352                 mdlg.ShowModal()
 353                 mdlg.Destroy()
 354                 self.saveFile = ""
 355                 return
 356             # If the files contents are incompatible with the attempted parse into a dictionary, inform the user.
 357             except SyntaxError:
 358                 mdlg = wx.MessageDialog(self, "The currently selected file is either corrupted or its contents incompatible with opening in this game.", "wxOthello", wx.OK)
 359                 mdlg.ShowModal()
 360                 mdlg.Destroy()
 361                 self.saveFile = ""
 362                 return
 363 
 364         # Load the dictionarys data into the relevant instance variables. When single player mode is implemented,
 365         # a check for the key "isSinglePlayer" will also need to be added here.
 366         self.firstPlayersMove = saveDict["firstPlayersMove"]
 367         for row in range(8):
 368             for col in range(8):
 369                 self.gameboard[row][col].SetBackgroundColour([self.bgAndBoardColor, self.player1Color, self.player2Color][saveDict["saveList"][row][col]])
 370         self.Refresh()
 371         self.gameAltered = False
 372 
 373 
 374     def saveAndQuit(self, event=None):
 375         """
 376         OthelloGameFrame.saveAndQuit(self, event = None)
 377         Saves the game and quits.
 378         """
 379 
 380         self.saveGame()
 381         self.Destroy()
 382         exit()
 383 
 384 
 385     def quitWithoutSaving(self, event=None):
 386         """
 387         OthelloGameFrame.quitWithoutSaving(self, event = None)
 388         If the game has been altered since last save, this function
 389         asks the user via messageBox whether they are sure they don't
 390         want to save. It exits the game if they answer yes, calls
 391         saveGame if they answer no and returns if they click cancel.
 392         """
 393 
 394         if self.gameAltered:
 395             usersFinalDecision = wx.MessageBox("All progress you've made in this game will be lost. Are you sure you want to quit without saving? Answering 'no' will open a save dialog if no file was selected previously then exit the game.",
 396                                                "wxOthello", wx.YES_NO | wx.CANCEL, self)
 397             if usersFinalDecision == wx.YES:
 398                 self.Destroy()
 399                 exit()
 400             elif usersFinalDecision == wx.NO:
 401                 self.saveGame()
 402             elif usersFinalDecision == wx.CANCEL:
 403                 return
 404         else:
 405             self.Destroy()
 406             exit()
 407 
 408 
 409     def showGameRules(self, event=None):
 410         """
 411         OthelloGameFrame.showGameRules(self, event = None)
 412         This callback displays an instance of HelpWindow that
 413         displays the rules of Othello as its help text with
 414         a call to showHelp.
 415         """
 416 
 417         global gameRules
 418         self.showHelp(gameRules)
 419 
 420 
 421     def showAppHelp(self, event=None):
 422         """
 423         OthelloGameFrame.showAppHelp(self, event = None)
 424         This callback displays an instance of HelpWindow that
 425         displays help information for this application with
 426         a call to showHelp.
 427         """
 428 
 429         global applicationHelp
 430         self.showHelp(applicationHelp)
 431 
 432 
 433     def showHelp(self, helpText):
 434         """
 435         OthelloGameFrame.showHelp(self, helpText)
 436         Displays an instance of HelpWindow displaying
 437         the value of helpText.
 438         """
 439 
 440         with HelpWindow(self, helpText) as hdlg:
 441             hdlg.ShowModal()
 442 
 443 
 444     def player1ButtonClicked(self, event=None):
 445         """
 446         OthelloGameFrame.player1ButtonClicked(self, event = None)
 447         Gives the next move to player 1. This feature is
 448         intended for use when it is player 2s turn and there are
 449         no moves available to player 2.
 450         """
 451 
 452         self.firstPlayersMove = True
 453         self.player1Butt.SetForegroundColour("RED")
 454         self.player2Butt.SetForegroundColour("BLACK")
 455 
 456 
 457     def player2ButtonClicked(self, event=None):
 458         """
 459         OthelloGameFrame.player2ButtonClicked(self, event = None)
 460         Gives the next move to player 2. This feature is
 461         intended for use when it is player 1s turn and there are
 462         no moves available to player 1.
 463         """
 464 
 465         self.firstPlayersMove = False
 466         self.player1Butt.SetForegroundColour("WHITE")
 467         self.player2Butt.SetForegroundColour("RED")
 468 
 469 
 470     def gameboardButtonClicked(self, event=None, row = 0, col = 0):
 471         """
 472         OthelloGameFrame.gameboardButtonClicked(self, event = None, row = 0, col = 0)
 473         This method is called through lambdas bound to the gameboard buttons generated
 474         in __init__. It displays an error message in the space where the move is
 475         attempted and returns if a move is invalid. Otherwise, it executes the move,
 476         checks whether somebody has won, gives the next move to the other player, and
 477         informs the next player if there is no move available to them in the status bar.
 478         """
 479 
 480         # self,firstPlayersMove is a boolean indicating whether it's player 1s turn.
 481         if self.firstPlayersMove:
 482             me = self.player1Color
 483             opponent = self.player2Color
 484 
 485         else:
 486             me = self.player2Color
 487             opponent = self.player1Color
 488 
 489         # Detect invalid move attempts, inform the user if their move is invalid and
 490         # the reason their move is invalid and return from the function.
 491         moveIsValid, message = self.isValidMove(row, col, me, opponent)
 492         if not moveIsValid:
 493             self.gameboard[row][col].SetLabel(message)
 494             wx.MilliSleep(1000)
 495             self.gameboard[row][col].SetLabel("")
 496             return
 497 
 498         # Make the move selected by the player.
 499         self.makeMove(row, col, me, opponent)
 500         self.gameAltered = True
 501 
 502         # The method detectWin returns a tuple with a boolean indicating whether there
 503         # are no valid moves available to either player and a message string appropriate to
 504         # the situation of 1: A player 1 victory, 2: A draw, or 3: A player 2 victory.
 505         winDetected, message = self.detectWin()
 506         if winDetected:
 507             m = wx.MessageDialog(self, message, "wxOthello")
 508             m.ShowModal()
 509             m.Destroy()
 510 
 511         # Invert the value of the self.firstPlayersMove flag and change the color of the
 512         # text in the player 1 and player 2 buttons in a manner according to whose turn it
 513         # is.
 514         self.firstPlayersMove = not self.firstPlayersMove
 515 
 516         if self.firstPlayersMove:
 517             self.player1Butt.SetForegroundColour("RED")
 518             self.player2Butt.SetForegroundColour("BLACK")
 519         else:
 520             self.player1Butt.SetForegroundColour("WHITE")
 521             self.player2Butt.SetForegroundColour("RED")
 522 
 523         # Inform the next player if there is no valid move available to them.
 524         if not self.moveAvailableToPlayer(opponent, me):
 525 
 526             if opponent == self.player1Color:
 527                 self.SetStatusText("No move available to player 1.")
 528             else:
 529                 self.SetStatusText("No move available to player 2.")
 530         else:
 531             self.SetStatusText("")
 532 
 533 
 534     def moveAvailableToPlayer(self, me, opponent):
 535         for row in range(8):
 536             for col in range(8):
 537                 if self.isValidMove(row, col, me, opponent)[0]: return True
 538         return False
 539 
 540 
 541     def isValidMove(self, row, col, me, opponent):
 542         """
 543         OthelloGameFrame.isValidMove(self, row, col, me, opponent)
 544         This method returns the tuple (isValidMove, messaage). It tests
 545         whether a move at a specified position on the gameboard is valid
 546         for a specific player and if the move is invalid, returns a
 547         message explaining why the move is invalid.
 548         """
 549 
 550         # Check whether the grid space is empty
 551         if self.gameboard[row][col].GetBackgroundColour() != self.bgAndBoardColor:
 552             return False, "This Space\nIs Occupied!"
 553 
 554         # A series of scanning vectors for the 8 scanning directions: up, down, left, right and the four diagonal directions
 555         scanningDirections = ((-1, 0), (0, 1), (1, 0), (0, -1),
 556                               (-1, -1), (-1, 1), (1, 1), (1, -1))
 557 
 558         # Iterate over the diffetent scanning directions, return True if the move is valid and set message to a message string
 559         # that explains why the move is invalid, if the move is invalid.
 560         message = "No Adjacent\nGamepieces!"
 561         for SDRow, SDCol in scanningDirections:
 562             currentRow = row + SDRow
 563             currentCol = col + SDCol
 564             sawOpponent = False
 565             while currentRow in range(0, 8) and currentCol in range(0, 8):
 566                 if self.gameboard[currentRow][currentCol].GetBackgroundColour() == self.bgAndBoardColor:
 567                     break
 568                 else:
 569                     message = "No Pieces\nTo Flip!"
 570                 if self.gameboard[currentRow][currentCol].GetBackgroundColour() == opponent: sawOpponent = True
 571                 if self.gameboard[currentRow][currentCol].GetBackgroundColour() == me and sawOpponent:
 572                     return True, "You won't see this message!"
 573                 if self.gameboard[currentRow][currentCol].GetBackgroundColour() == me and not sawOpponent: break
 574 
 575                 currentRow += SDRow
 576                 currentCol += SDCol
 577 
 578         return False, message
 579 
 580 
 581     def makeMove(self, row, col, me, opponent):
 582         """
 583         OthelloGameFrame.makeMove(self, row, col, me, opponent)
 584         Performs a move for a specified player at a specified position.
 585         """
 586 
 587         # Put down the players gamepiece
 588         self.gameboard[row][col].SetBackgroundColour(me)
 589 
 590         # A series of scanning vectors for the 8 scanning directions: up, down, left, right and the four diagonal directions
 591         scanningDirections = ((-1, 0), (0, 1), (1, 0), (0, -1),
 592                               (-1, -1), (-1, 1), (1, 1), (1, -1))
 593 
 594         # Iterate over the scanning vectors.
 595         for SDRow, SDCol in scanningDirections:
 596             currentRow = row + SDRow
 597             currentCol = col + SDCol
 598             sawOpponent = False
 599             canFlipPieces = False
 600             # Check whether gamepieces can be flipped in the current scanning direction.
 601             while currentRow in range(0, 8) and currentCol in range(0, 8):
 602                 if self.gameboard[currentRow][currentCol].GetBackgroundColour() == self.bgAndBoardColor: break
 603                 if self.gameboard[currentRow][currentCol].GetBackgroundColour() == opponent: sawOpponent = True
 604                 if self.gameboard[currentRow][currentCol].GetBackgroundColour() == me and sawOpponent:
 605                     canFlipPieces = True
 606                     break
 607                 if self.gameboard[currentRow][currentCol].GetBackgroundColour() == me and not sawOpponent: break
 608                 currentRow += SDRow
 609                 currentCol += SDCol
 610 
 611             # If gamepieces can be flipped in the current scanning direction, flip the pieces.
 612             currentRow = row + SDRow
 613             currentCol = col + SDCol
 614             while canFlipPieces and currentRow in range(0, 8) and currentCol in range(0, 8):
 615                 if self.gameboard[currentRow][currentCol].GetBackgroundColour() == opponent:
 616                     self.gameboard[currentRow][currentCol].SetBackgroundColour(me)
 617                 elif self.gameboard[currentRow][currentCol].GetBackgroundColour() == me:
 618                     break
 619                 else:
 620                     print("Kyle, you have some debugging to do! This else clause is never supposed to execute. Something has gone horribly wrong!")
 621                 currentRow += SDRow
 622                 currentCol += SDCol
 623 
 624 
 625     def detectWin(self):
 626         """
 627         OthelloGameFrame.detectWin(self)
 628         This method returns a the tuple (noValidMoves, message), where noValidMoves is a boolean indicating whether there are no more valid moves
 629         available to either player and message is one of the the strings "The winner is player 1!", if the majority of the pieces on the board are
 630         black, "This game is a draw!" if player 1 and player 2 have equal numbers of pieces on the board, or "The winner is player 2!" if the
 631         majority of the pieces on the board are white.
 632         """
 633 
 634         noValidMoves = True # We begin by assuming that neither player has a valid move available to them.
 635         player1Count = 0    # Counters for the number of spaces each player has captured.
 636         player2Count = 0
 637         # Iterate over the gameboard. Check whether there is a valid move available to either player and
 638         # count the number of spaces captured by each player.
 639         for row in range(8):
 640             for col in range(8):
 641                 if self.isValidMove(row, col, self.player1Color, self.player2Color)[0] or self.isValidMove(row, col, self.player2Color, self.player1Color)[0]: noValidMoves = False
 642                 if self.gameboard[row][col].GetBackgroundColour() == self.player1Color: player1Count += 1
 643                 if self.gameboard[row][col].GetBackgroundColour() == self.player2Color: player2Count += 1
 644 
 645         if noValidMoves:
 646             # Return True and a message indicating who won
 647             if player1Count > player2Count:
 648                 return True, "The winner is player 1!"
 649             elif player1Count == player2Count:
 650                 return True, "This game is a draw!"
 651             elif player1Count < player2Count:
 652                 return True, "The winner is player 2!"
 653 
 654         else:
 655             return False, "You're not supposed to see this message."
 656 
 657 #---------------------------------------------------------------------------
 658 
 659 class HelpWindow(wx.Dialog):
 660     """
 661     A simple dialog class for displaying help information to the user.
 662     """
 663     def __init__ (self, parent, helpText):
 664         wx.Dialog.__init__(self, parent, -1, helpText.split("\n")[0])
 665 
 666         self.SetMinSize(wx.Size(400, 400))
 667         self.topExitButton = wx.Button(self, wx.ID_CLOSE, "Close Help")
 668         self.helpDisplay = wx.TextCtrl(parent = self, id = wx.ID_ANY, value = helpText,
 669                                        style = wx.TE_MULTILINE | wx.TE_READONLY)
 670         self.bottomExitButton = wx.Button(self, wx.ID_CLOSE, "Close Help")
 671 
 672         self.layout = wx.BoxSizer(wx.VERTICAL)
 673         self.layout.Add(self.topExitButton, 1, wx.EXPAND)
 674         self.layout.Add(self.helpDisplay, 10, wx.EXPAND)
 675         self.layout.Add(self.bottomExitButton, 1, wx.EXPAND)
 676         self.SetSizer(self.layout)
 677 
 678         self.Fit()
 679         self.Bind(wx.EVT_BUTTON, self.closeHelp, id = wx.ID_CLOSE)
 680 
 681     #-----------------------------------------------------------------------
 682 
 683     def closeHelp(self, event=None):
 684         self.EndModal(wx.ID_ANY)
 685 
 686 #---------------------------------------------------------------------------
 687 
 688 if __name__ == "__main__":
 689     app = wx.App()
 690     theApp = OthelloGameFrame(parent=None, id=wx.ID_ANY, size=(700, 800), title="wxOthello")
 691     app.MainLoop()


Chess

img_chess.png

   1 # 8queens_wx_pos.py
   2 
   3 """
   4 
   5 File   : 8queens_wx_pos.py
   6 Author : Shido
   7 Link   : http://www.shido.info/py/python5.html
   8 
   9 Eight queens, whose gui uses wx absolute positioning
  10 
  11 """
  12 
  13 import wx
  14 import queen as q
  15 
  16 # Global parameter
  17 Q_font = ("Times", 14)
  18 X_Margin = 20
  19 Y_Margin = 20
  20 
  21 #---------------------------------------------------------------------------
  22 
  23 # class Board
  24 # class Queen
  25 
  26 #---------------------------------------------------------------------------
  27 
  28 class Board(wx.Panel):
  29     """
  30     Gui
  31     """
  32     def init_title(self):
  33         self.i_title = wx.Image('8qsubtitle.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap()
  34         wx.StaticBitmap(self, -1, self.i_title, \
  35             (5, 5), (self.i_title.GetWidth(), self.i_title.GetHeight()))
  36 
  37 
  38     def init_board(self):
  39         self.cell_images = [wx.Image(png, wx.BITMAP_TYPE_PNG).ConvertToBitmap() \
  40                                     for png in ('bw.png', 'bg.png', 'qw.png', 'qg.png')]
  41         answer = self.q_answers[0]
  42         self.i_width = self.cell_images[0].GetWidth()
  43         self.i_height = self.cell_images[0].GetHeight()
  44         for i in range(64):
  45             j = (int(q.qmod(i)) + (((i in answer) and 2) or 0))
  46             print(j)
  47             wx.StaticBitmap(self, -1, self.cell_images[j],
  48                             (int(self.i_width*(i%8))+int(self.xmargin),
  49                              int(self.i_height*(i/8))+int(self.ymargin)),
  50                             (int(self.i_width),  int(self.i_height)))
  51 
  52 
  53     def init_footer(self):
  54         self.counter = 0
  55         btn1 = wx.Button(self, 10, "&Forward", (20, 420))
  56         self.Bind(wx.EVT_BUTTON, self.show_next, btn1)
  57         btn2 = wx.Button(self, 20, "&Backward", (100, 420))
  58         self.Bind(wx.EVT_BUTTON, self.show_prev, btn2)
  59         self.label = wx.StaticText(self, -1, ("%d/12" % (self.counter+1)), (180, 430))
  60 
  61 
  62     def init_all(self):
  63         self.q_answers = q.eight_queens()
  64         self.init_title()
  65         self.init_board()
  66         self.init_footer()
  67 
  68     #-----------------------------------------------------------------------
  69 
  70     def __init__(self, frame):
  71         wx.Panel.__init__(self, frame, style=wx.NO_FULL_REPAINT_ON_RESIZE)
  72         self.xmargin = 40
  73         self.ymargin = 60
  74         self.init_all()
  75         self.Layout()
  76 
  77     def refresh(self, forward):
  78         """
  79         Refresh board and counter.
  80         """
  81 
  82         i_now = self.counter
  83         self.counter += forward and 1 or -1
  84         self.label.SetLabel("%d/12" % (1 + self.counter))
  85         a_next = self.q_answers[self.counter]
  86         a_now  = self.q_answers[i_now]
  87         q_or_b=0
  88         for cells in [q.set_difference(a_now, a_next), q.set_difference(a_next, a_now)]:
  89             for i in cells:
  90                 j = int(q.qmod(i)) + int(q_or_b)
  91                 print(j)
  92                 wx.StaticBitmap(self, -1, self.cell_images[j],
  93                                 (int(self.i_width*(i%8))+int(self.xmargin),
  94                                  int(self.i_height*(i/8))+int(self.ymargin)),
  95                                 (int(self.i_width),  int(self.i_height)))
  96             q_or_b += 2
  97         self.Refresh()
  98 
  99 
 100     def show_next(self, event):
 101         if(self.counter < 11):
 102             self.refresh(True)
 103 
 104 
 105     def show_prev(self, event):
 106         if(self.counter > 0):
 107             self.refresh(False)
 108 
 109 #---------------------------------------------------------------------------
 110 
 111 class Queen(wx.App):
 112     def OnInit(self):
 113 
 114         frame = wx.Frame(None, -1, "8 queens", pos=(150, 150), size=(420, 500))
 115         frame.SetIcon(wx.Icon('./icons/wxwin.ico', wx.BITMAP_TYPE_ICO))
 116         Board(frame)
 117         self.SetTopWindow(frame)
 118         frame.Show(True)
 119 
 120         return True
 121 
 122 #---------------------------------------------------------------------------
 123 
 124 if __name__ == "__main__":
 125     que = Queen(redirect=False)
 126     que.MainLoop()


Snake

img_snake.png

   1 # snake.py
   2 
   3 """
   4 
   5 Created on 2011-03-23
   6 Classic game - Snake
   7 Author : 123
   8 Website : https://www.programmersought.com/article/9994279643/
   9 
  10 """
  11 
  12 import wx
  13 import random,time
  14 
  15 # class MyWesticeSnakeBody
  16 # class MyWesticeSnake
  17 # class MyFrame
  18 
  19 #---------------------------------------------------------------------------
  20 
  21 class MyWesticeSnakeBody():
  22     def __init__(self):
  23 
  24         randnum=int(random.random()*4)
  25         imagename='snakeskin'+str(randnum)+'.jpg'
  26         img1=wx.Image(imagename,wx.BITMAP_TYPE_ANY)
  27         self.img1=img1.Scale(30, 30) # Reduce the image.
  28         self.x=-1
  29         self.y=-1
  30 
  31 #---------------------------------------------------------------------------
  32 
  33 class MyWesticeSnake():
  34     def __init__(self):
  35 
  36         self.bodys=[]
  37         self.headx=int(random.random()*(10))+5
  38         self.heady=int(random.random()*(10))+5
  39         self.direct=int(random.random()*4)
  40         self.img1=wx.Image('snakehead.jpg',wx.BITMAP_TYPE_ANY)
  41         self.headimg90=self.img1.Rotate90(True)
  42         self.headimg180=self.headimg90.Rotate90(True)
  43         self.headimg_90=self.img1.Rotate90(False)
  44         self.headimg1=self.img1
  45         self.prev_snakex=self.headx
  46         self.prev_snakey=self.heady
  47 
  48     #-----------------------------------------------------------------------
  49 
  50     def update(self):
  51         """
  52         ...
  53         """
  54 
  55         # Save the last position of the snake before the moment.
  56         if len(self.bodys)!=0:
  57             self.prev_snakex=self.bodys[len(self.bodys)-1].x
  58             self.prev_snakey=self.bodys[len(self.bodys)-1].y
  59         else:
  60             self.prev_snakex=self.headx
  61             self.prev_snakey=self.heady
  62 
  63         if len(self.bodys)!=0:
  64             for index in range(len(self.bodys)-1,0,-1):
  65                 self.bodys[index].x=self.bodys[index-1].x
  66                 self.bodys[index].y=self.bodys[index-1].y
  67             self.bodys[0].x=self.headx
  68             self.bodys[0].y=self.heady
  69 
  70         if self.direct==0:
  71                          self.headx+=1 # To the right.
  72         if self.direct==1:
  73                          self.heady+=1 # Down.
  74         if self.direct==2:
  75                          self.headx-=1 # To the left.
  76         if self.direct==3:
  77                          self.heady-=1 # Up.
  78 
  79         self.headimg1=self.img1
  80         if self.direct==0:
  81             self.headimg1=self.headimg90
  82         if self.direct==1:
  83             self.headimg1=self.headimg180
  84         if self.direct==2:
  85             self.headimg1=self.headimg_90
  86         self.headimg1=self.headimg1.Scale(30, 30)
  87 
  88 #---------------------------------------------------------------------------
  89 
  90 class MyFrame(wx.Frame):
  91     gridwidth=20
  92     gridheight=20
  93     landsqurelist=[]
  94     first=True
  95     def __init__(self):
  96         wx.Frame.__init__(self, None, -1,
  97                           "Snake game",
  98                           style=wx.SYSTEM_MENU |
  99                                 wx.MINIMIZE_BOX |
 100                                 wx.CLOSE_BOX |
 101                                 wx.CAPTION)
 102 
 103         self.SetIcon(wx.Icon('./icons/wxwin.ico', wx.BITMAP_TYPE_ICO))
 104 
 105         #------------
 106 
 107         self.InitUI()
 108 
 109         #------------
 110 
 111         # wx.Font(pointSize, family, style, weight, underline, faceName)
 112         font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
 113         font.SetWeight(wx.BOLD)
 114 
 115         #------------
 116 
 117         self.panel=wx.Panel(self,-1)
 118         self.panel.SetBackgroundColour("#696767")
 119         self.score=0
 120 
 121         self.command0 = wx.StaticText(self.panel,-1,'Commands (AZERTY) :',pos=(630,20))
 122         self.command0.SetFont(font)
 123         self.command1 = wx.StaticText(self.panel,-1,'(Q) Left',pos=(630,50))
 124         self.command2 = wx.StaticText(self.panel,-1,'(D) Right',pos=(630,80))
 125         self.command3 = wx.StaticText(self.panel,-1,'(Z) Top',pos=(630,110))
 126         self.command4 = wx.StaticText(self.panel,-1,'(S) Bottom',pos=(630,140))
 127         self.command0.SetForegroundColour("#f38e3c")
 128         self.command1.SetForegroundColour(wx.WHITE)
 129         self.command2.SetForegroundColour(wx.WHITE)
 130         self.command3.SetForegroundColour(wx.WHITE)
 131         self.command4.SetForegroundColour(wx.WHITE)
 132 
 133         self.scoretxt=wx.StaticText(self.panel,-1,'Score : '+str(self.score),pos=(630,300))
 134         self.gamestatustxt=wx.StaticText(self.panel, -1, 'Game status : Running', pos=(630,350))
 135         self.scoretxt.SetForegroundColour(wx.WHITE)
 136         self.gamestatustxt.SetForegroundColour(wx.WHITE)
 137 
 138         fgs=wx.FlexGridSizer(cols=self.gridwidth,hgap=0,vgap=0)
 139 
 140 
 141         self.panel.SetFocus()
 142 
 143         for col in range(self.gridwidth):
 144             random.seed(time.time())
 145             for row in range(self.gridheight):
 146 
 147                 # Loading grass.
 148                 randnum=int(random.random()*4)
 149 
 150                 if random.random()<0.02:
 151                     randnum=-1
 152                 imagename='land'+str(randnum)+'.jpg'
 153                 # print(imagename)
 154                 img1=wx.Image(imagename,wx.BITMAP_TYPE_ANY)
 155                 img1=img1.Scale(30, 30)# Reduce the image.
 156                 sb1=wx.StaticBitmap(self.panel,-1,wx.Bitmap(img1))
 157                 sb1.randnum=randnum # Save the image name.
 158                 fgs.Add(sb1)
 159                 self.landsqurelist.append(sb1)
 160         self.panel.SetSizerAndFit(fgs)
 161 
 162         # Initialize position.
 163         self.westicesnake=MyWesticeSnake()
 164 
 165         self.timer1=wx.Timer(self)
 166         self.Bind(wx.EVT_TIMER,self.frameUpdate,self.timer1)
 167         self.timer1.Start(300) # Can set the speed here.
 168 
 169         # wx.EVT_KEY_DOWN, self.OnKeyDown
 170         self.panel.Bind(wx.EVT_KEY_UP,self.OnKeyUp)
 171 
 172     #-----------------------------------------------------------------------
 173 
 174     def InitUI(self):
 175         """
 176         ...
 177         """
 178 
 179         menuBar = wx.MenuBar()
 180         filemenu = wx.Menu()
 181 
 182         qmi = wx.MenuItem(filemenu, wx.ID_EXIT, "&Quit\tCtrl+Q")
 183         qmi.SetBitmap(wx.Bitmap("./images/exit.png"))
 184         filemenu.Append(qmi)
 185 
 186         self.Bind(wx.EVT_MENU, self.OnQuit, id=wx.ID_EXIT)
 187 
 188         menuBar.Append(filemenu, '&File')
 189         self.SetMenuBar(menuBar)
 190 
 191         self.statusbar = self.CreateStatusBar()
 192         self.statusbar.SetStatusText('Ready')
 193 
 194         self.SetSize((800, 683))
 195         self.Centre()
 196 
 197 
 198     def OnKeyUp(self,event):
 199         """
 200         ...
 201         """
 202 
 203         if ord('Q')==event.GetKeyCode()and self.westicesnake.direct!=0:
 204             # To the left.
 205             self.westicesnake.direct=2
 206         if ord('D')==event.GetKeyCode()and self.westicesnake.direct!=2:
 207             # To the right.
 208             self.westicesnake.direct=0
 209         if ord('S')==event.GetKeyCode()and self.westicesnake.direct!=3:
 210             # To the bottom.
 211             self.westicesnake.direct=1
 212         if ord('Z')==event.GetKeyCode()and self.westicesnake.direct!=1:
 213             # To the top.
 214             self.westicesnake.direct=3
 215 
 216 
 217     def frameUpdate(self,event):
 218         """
 219         ...
 220         """
 221 
 222         if self.westicesnake.headx>=0and self.westicesnake.headx<self.gridwidth\
 223            and self.westicesnake.heady>=0and self.westicesnake.heady<self.gridheight:
 224             tempindex=self.getIndex(self.westicesnake.headx,self.westicesnake.heady)
 225 
 226             self.landsqurelist[tempindex].SetBitmap(wx.Bitmap(self.westicesnake.headimg1))
 227 
 228             # Show the snake body.
 229             for body in self.westicesnake.bodys:
 230                 tempindex=self.getIndex(body.x,body.y)
 231                 self.landsqurelist[tempindex].SetBitmap(wx.Bitmap(body.img1))
 232 
 233             # Show the last vacated location.
 234             if not self.first:
 235                 tempindex=self.getIndex(self.westicesnake.prev_snakex,self.westicesnake.prev_snakey)
 236                 tempimagenum=self.landsqurelist[tempindex].randnum
 237                 tempimagename='land'+str(tempimagenum)+'.jpg'
 238                 img1=wx.Image(tempimagename,wx.BITMAP_TYPE_ANY)
 239                 img1=img1.Scale(30, 30)
 240                 self.landsqurelist[tempindex].SetBitmap(wx.Bitmap(img1))
 241             else:
 242                 self.first=False
 243 
 244             # Long snake body, replace the cake with land.
 245             if self.landsqurelist[tempindex].randnum==(-1):
 246                 # print('Eat the cake')
 247                 self.score+=1
 248                 self.scoretxt.SetLabel('Score : '+str(self.score))
 249                 self.landsqurelist[tempindex].randnum=int(random.random()*4)
 250 
 251                 # Body, set the coordinates, it should be added in the last vacated place.
 252                 snakebody=MyWesticeSnakeBody()
 253                 snakebody.x=self.westicesnake.prev_snakex
 254                 snakebody.y=self.westicesnake.prev_snakey
 255                 self.westicesnake.bodys.append(snakebody)
 256 
 257             # Randomly generate cakes in the land.
 258             tempindex=int(random.random()*self.gridwidth*self.gridheight)
 259 
 260             if self.landsqurelist[tempindex].randnum==-1:
 261                 randnum=int(random.random()*4)
 262                 imagename='land'+str(randnum)+'.jpg'
 263                 self.landsqurelist[tempindex].randnum=randnum
 264             else:
 265                 imagename='land-1.jpg'
 266                 self.landsqurelist[tempindex].randnum=-1
 267 
 268             img1=wx.Image(imagename,wx.BITMAP_TYPE_ANY)
 269             img1=img1.Scale(30, 30)
 270             self.landsqurelist[tempindex].SetBitmap(wx.Bitmap(img1))
 271         else:
 272             self.gamestatustxt.SetLabel('Game status : Game over !')
 273 
 274         self.westicesnake.update()
 275 
 276 
 277     def getX_Y(self,imageindex):
 278         """
 279         Get the x,y coordinates of the image.
 280         """
 281 
 282         x=imageindex%self.gridwidth
 283         y=imageindex/self.gridwidth
 284 
 285         return [x,y]
 286 
 287 
 288     def getIndex(self,x,y):
 289         """
 290         ...
 291         """
 292 
 293         return y*self.gridwidth+x
 294 
 295 
 296     def OnQuit(self, event):
 297         self.Close()
 298 
 299 #---------------------------------------------------------------------------
 300 
 301 def main():
 302     app = wx.App()
 303     frame = MyFrame()
 304     frame.Show(True)
 305     app.MainLoop()
 306 
 307 #---------------------------------------------------------------------------
 308 
 309 if __name__ == '__main__':
 310     main()


Download source

source.zip


Additional Information

Link :

https://wiki.python.org/moin/GameProgramming

http://mientki.ruhosting.nl/data_www/pylab_works/pw_bricks_2d_scene.html

- - - - -

https://wiki.wxpython.org/TitleIndex

https://docs.wxpython.org/


Thanks to

Masaharu Kanda (break-wall.py coding), Keamest (othello.py coding), Shido (chess.py coding), 123 (snake.py coding), the wxPython community...


About this page

Date(d/m/y) Person (bot) Comments :

05/04/21 - Ecco (Created page for wxPython Phoenix).


Comments

- blah, blah, blah...

How to create a game for wxPython - Part 4 (Phoenix) (last edited 2021-04-05 18:04:45 by Ecco)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.