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

Keywords : Tetris, Memory game, Reversi, The Towers of Hanoi, Game, Pygame.


Demonstrating :

Tested py3.x, wx4.x and Win10.

Are you ready to use some samples ? ;)

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


Tetris

First example

img_tetris_1.png

   1 # tetris.py
   2 
   3 """
   4 ZetCode wxPython tutorial.
   5 
   6 This is Tetris game clone in wxPython.
   7 
   8 Author : Jan Bodnar
   9 Website : www.zetcode.com
  10 Link : http://zetcode.com/wxpython/thetetrisgame/
  11 Last modified : May 2018
  12 """
  13 
  14 import wx
  15 import random
  16 
  17 # class Tetris
  18 # class Board
  19 # class Tetrominoes
  20 # class Shape
  21 
  22 #---------------------------------------------------------------------------
  23 
  24 class Tetris(wx.Frame):
  25     """
  26     ...
  27     """
  28     def __init__(self, parent):
  29         style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER ^ wx.MAXIMIZE_BOX
  30         wx.Frame.__init__(self, parent, size=(220, 420), style=style)
  31 
  32         self.initFrame()
  33 
  34     #-----------------------------------------------------------------------
  35 
  36     def initFrame(self):
  37         """
  38         ...
  39         """
  40 
  41         self.statusbar = self.CreateStatusBar()
  42         self.statusbar.SetStatusText('0')
  43         self.board = Board(self)
  44         self.board.SetFocus()
  45         self.board.start()
  46 
  47         #------------
  48 
  49         # Creating the menubar.
  50         menu_bar = wx.MenuBar()
  51 
  52         # Setting up the menu.
  53         file_menu = wx.Menu()
  54 
  55         # wx.ID_ABOUT and wx.ID_EXIT are standard IDs provided by wxWidgets.
  56         item = file_menu.Append(wx.ID_ABOUT, "&About", "Information about Tetris.")
  57         self.Bind(wx.EVT_MENU, self.OnAbout, item)
  58         file_menu.AppendSeparator()
  59         item = file_menu.Append(wx.ID_EXIT, "E&xit", "Exit Tetris.")
  60         self.Bind(wx.EVT_MENU, self.OnQuit, item)
  61 
  62         # Create the "Help" top menu.
  63         help_menu = wx.Menu()
  64         item = help_menu.Append(wx.ID_HELP, "&Shortcuts", "Shortcuts for Tetris.")
  65         self.Bind(wx.EVT_MENU, self.OnHelp, item)
  66 
  67         # Adding the "file_menu" to the menu bar.
  68         menu_bar.Append(file_menu, "&File")
  69         menu_bar.Append(help_menu, "&Help")
  70 
  71         # Adding the menu bar to the frame content.
  72         self.SetMenuBar(menu_bar)
  73 
  74         #------------
  75 
  76         self.SetIcon(wx.Icon(".\icons\wxwin.ico"))
  77         self.SetTitle("Tetris")
  78 
  79         #------------
  80 
  81         self.CenterOnScreen(wx.BOTH)
  82 
  83 
  84     def OnQuit(self, event):
  85         self.Destroy()
  86 
  87 
  88     def OnAbout(self, event):
  89         dlg = wx.MessageDialog(self,
  90                                "This is a small game.\n",
  91                                "About Tetris",
  92                                wx.OK | wx.CENTRE | wx.ICON_INFORMATION)
  93         dlg.ShowModal()
  94         dlg.Destroy()
  95 
  96 
  97     def OnHelp(self, event):
  98         dlg = wx.MessageDialog(self,
  99                                "AZERTY keyboard :\n"
 100                                "--------------------\n"
 101                                "P \t---------> \tPause\n"
 102                                "S \t---------> \tLEFT\n"
 103                                "F \t---------> \tRIGHT\n"
 104                                "D \t---------> \tDOWN (rotated right)\n"
 105                                "E \t----------> \tUP (rotated left)\n"
 106                                "SPACE \t----> \tAccelerate\n"
 107                                "D \t---------> \tOne line down\n",
 108                                "Shortcuts",
 109                                wx.OK | wx.CENTRE | wx.ICON_INFORMATION)
 110         dlg.ShowModal()
 111         dlg.Destroy()
 112 
 113 #---------------------------------------------------------------------------
 114 
 115 class Board(wx.Panel):
 116     """
 117     ...
 118     """
 119 
 120     BoardWidth = 10
 121     BoardHeight = 22
 122     Speed = 300
 123     ID_TIMER = 1
 124 
 125     def __init__(self, *args, **kw):
 126         super(Board, self).__init__(*args, **kw)
 127 
 128         self.initBoard()
 129 
 130         #------------
 131 
 132         # Delete flickers.
 133         if wx.Platform == "__WXMSW__":
 134             self.SetDoubleBuffered(True)
 135 
 136     #-----------------------------------------------------------------------
 137 
 138     def initBoard(self):
 139         """
 140         ...
 141         """
 142 
 143         self.timer = wx.Timer(self, Board.ID_TIMER)
 144         self.isWaitingAfterLine = False
 145         self.curPiece = Shape()
 146         self.nextPiece = Shape()
 147         self.curX = 0
 148         self.curY = 0
 149         self.numLinesRemoved = 0
 150         self.board = []
 151 
 152         self.isStarted = False
 153         self.isPaused = False
 154 
 155         self.Bind(wx.EVT_PAINT, self.OnPaint)
 156         self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
 157         self.Bind(wx.EVT_TIMER, self.OnTimer, id=Board.ID_TIMER)
 158 
 159         self.clearBoard()
 160 
 161 
 162     def shapeAt(self, x, y):
 163         """
 164         ...
 165         """
 166 
 167         return self.board[(y * Board.BoardWidth) + x]
 168 
 169 
 170     def setShapeAt(self, x, y, shape):
 171         """
 172         ...
 173         """
 174 
 175         self.board[(y * Board.BoardWidth) + x] = shape
 176 
 177 
 178     def squareWidth(self):
 179         """
 180         ...
 181         """
 182 
 183         return self.GetClientSize().GetWidth() // Board.BoardWidth
 184 
 185 
 186     def squareHeight(self):
 187         """
 188         ...
 189         """
 190 
 191         return self.GetClientSize().GetHeight() // Board.BoardHeight
 192 
 193 
 194     def start(self):
 195         """
 196         ...
 197         """
 198 
 199         if self.isPaused:
 200             return
 201 
 202         self.isStarted = True
 203         self.isWaitingAfterLine = False
 204         self.numLinesRemoved = 0
 205         self.clearBoard()
 206 
 207         self.newPiece()
 208         self.timer.Start(Board.Speed)
 209 
 210 
 211     def pause(self):
 212         """
 213         ...
 214         """
 215 
 216         if not self.isStarted:
 217             return
 218 
 219         self.isPaused = not self.isPaused
 220         statusbar = self.GetParent().statusbar
 221 
 222         if self.isPaused:
 223             self.timer.Stop()
 224             statusbar.SetStatusText('paused')
 225         else:
 226             self.timer.Start(Board.Speed)
 227             statusbar.SetStatusText(str(self.numLinesRemoved))
 228 
 229         self.Refresh()
 230 
 231 
 232     def clearBoard(self):
 233         """
 234         ...
 235         """
 236 
 237         for i in range(Board.BoardHeight * Board.BoardWidth):
 238             self.board.append(Tetrominoes.NoShape)
 239 
 240 
 241     def OnPaint(self, event):
 242         """
 243         ...
 244         """
 245 
 246         dc = wx.PaintDC(self)
 247 
 248         size = self.GetClientSize()
 249         boardTop = size.GetHeight() - Board.BoardHeight * self.squareHeight()
 250 
 251         for i in range(Board.BoardHeight):
 252             for j in range(Board.BoardWidth):
 253 
 254                 shape = self.shapeAt(j, Board.BoardHeight - i - 1)
 255 
 256                 if shape != Tetrominoes.NoShape:
 257                     self.drawSquare(dc,
 258                         0 + j * self.squareWidth(),
 259                         boardTop + i * self.squareHeight(), shape)
 260 
 261         if self.curPiece.shape() != Tetrominoes.NoShape:
 262 
 263             for i in range(4):
 264 
 265                 x = self.curX + self.curPiece.x(i)
 266                 y = self.curY - self.curPiece.y(i)
 267 
 268                 self.drawSquare(dc, 0 + x * self.squareWidth(),
 269                     boardTop + (Board.BoardHeight - y - 1) * self.squareHeight(),
 270                     self.curPiece.shape())
 271 
 272 
 273     def OnKeyDown(self, event):
 274         """
 275         ...
 276         """
 277 
 278         if not self.isStarted or self.curPiece.shape() == Tetrominoes.NoShape:
 279             event.Skip()
 280             return
 281 
 282         keycode = event.GetKeyCode()
 283         print("Keycode :", keycode)
 284 
 285         if keycode == ord('P') or keycode == ord('p'):
 286             self.pause()
 287             return
 288 
 289         if self.isPaused:
 290             return
 291 
 292         #-----------------
 293         # AZERTY keyboard.
 294         #-----------------
 295         # elif keycode == wx.WXK_LEFT:
 296         elif keycode == ord('S') or keycode == ord('s'):
 297             self.tryMove(self.curPiece, self.curX - 1, self.curY)
 298 
 299         # elif keycode == wx.WXK_RIGHT:
 300         elif keycode == ord('F') or keycode == ord('f'):
 301             self.tryMove(self.curPiece, self.curX + 1, self.curY)
 302 
 303         # elif keycode == wx.WXK_DOWN:
 304         elif keycode == ord('D') or keycode == ord('d'):
 305             self.tryMove(self.curPiece.rotatedRight(), self.curX, self.curY)
 306 
 307         # elif keycode == wx.WXK_UP:
 308         elif keycode == ord('E') or keycode == ord('e'):
 309             self.tryMove(self.curPiece.rotatedLeft(), self.curX, self.curY)
 310 
 311         elif keycode == wx.WXK_SPACE:
 312             self.dropDown()
 313 
 314         elif keycode == ord('R') or keycode == ord('r'):
 315             self.oneLineDown()
 316 
 317         else:
 318             event.Skip()
 319 
 320 
 321     def OnTimer(self, event):
 322         """
 323         ...
 324         """
 325 
 326         if event.GetId() == Board.ID_TIMER:
 327 
 328             if self.isWaitingAfterLine:
 329                 self.isWaitingAfterLine = False
 330                 self.newPiece()
 331 
 332             else:
 333                 self.oneLineDown()
 334 
 335         else:
 336             event.Skip()
 337 
 338 
 339     def dropDown(self):
 340         """
 341         ...
 342         """
 343 
 344         newY = self.curY
 345 
 346         while newY > 0:
 347             if not self.tryMove(self.curPiece, self.curX, newY - 1):
 348                 break
 349             newY -= 1
 350 
 351         self.pieceDropped()
 352 
 353 
 354     def oneLineDown(self):
 355         """
 356         ...
 357         """
 358 
 359         if not self.tryMove(self.curPiece, self.curX, self.curY - 1):
 360             self.pieceDropped()
 361 
 362 
 363     def pieceDropped(self):
 364         """
 365         ...
 366         """
 367 
 368         for i in range(4):
 369 
 370             x = self.curX + self.curPiece.x(i)
 371             y = self.curY - self.curPiece.y(i)
 372             self.setShapeAt(x, y, self.curPiece.shape())
 373 
 374         self.removeFullLines()
 375 
 376         if not self.isWaitingAfterLine:
 377             self.newPiece()
 378 
 379 
 380     def removeFullLines(self):
 381         """
 382         ...
 383         """
 384 
 385         numFullLines = 0
 386 
 387         statusbar = self.GetParent().statusbar
 388 
 389         rowsToRemove = []
 390 
 391         for i in range(Board.BoardHeight):
 392             n = 0
 393             for j in range(Board.BoardWidth):
 394                 if not self.shapeAt(j, i) == Tetrominoes.NoShape:
 395                     n = n + 1
 396 
 397             if n == 10:
 398                 rowsToRemove.append(i)
 399 
 400         rowsToRemove.reverse()
 401 
 402         for m in rowsToRemove:
 403             for k in range(m, Board.BoardHeight):
 404                 for l in range(Board.BoardWidth):
 405                         self.setShapeAt(l, k, self.shapeAt(l, k + 1))
 406 
 407             numFullLines = numFullLines + len(rowsToRemove)
 408 
 409             if numFullLines > 0:
 410 
 411                 self.numLinesRemoved = self.numLinesRemoved + numFullLines
 412                 statusbar.SetStatusText(str(self.numLinesRemoved))
 413                 self.isWaitingAfterLine = True
 414                 self.curPiece.setShape(Tetrominoes.NoShape)
 415                 self.Refresh()
 416 
 417 
 418     def newPiece(self):
 419         """
 420         ...
 421         """
 422 
 423         self.curPiece = self.nextPiece
 424         statusbar = self.GetParent().statusbar
 425         self.nextPiece.setRandomShape()
 426 
 427         self.curX = Board.BoardWidth // 2 + 1
 428         self.curY = Board.BoardHeight - 1 + self.curPiece.minY()
 429 
 430         if not self.tryMove(self.curPiece, self.curX, self.curY):
 431 
 432             self.curPiece.setShape(Tetrominoes.NoShape)
 433             self.timer.Stop()
 434             self.isStarted = False
 435             statusbar.SetStatusText('Game over')
 436 
 437 
 438     def tryMove(self, newPiece, newX, newY):
 439         """
 440         ...
 441         """
 442 
 443         for i in range(4):
 444 
 445             x = newX + newPiece.x(i)
 446             y = newY - newPiece.y(i)
 447 
 448             if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight:
 449                 return False
 450 
 451             if self.shapeAt(x, y) != Tetrominoes.NoShape:
 452                 return False
 453 
 454         self.curPiece = newPiece
 455         self.curX = newX
 456         self.curY = newY
 457         self.Refresh()
 458 
 459         return True
 460 
 461 
 462     def drawSquare(self, dc, x, y, shape):
 463         """
 464         ...
 465         """
 466 
 467         colors = ['#000000', '#CC6666', '#66CC66', '#6666CC',
 468                   '#CCCC66', '#CC66CC', '#66CCCC', '#DAAA00']
 469 
 470         light = ['#000000', '#F89FAB', '#79FC79', '#7979FC',
 471                  '#FCFC79', '#FC79FC', '#79FCFC', '#FCC600']
 472 
 473         dark = ['#000000', '#803C3B', '#3B803B', '#3B3B80',
 474                  '#80803B', '#803B80', '#3B8080', '#806200']
 475 
 476         pen = wx.Pen(light[shape])
 477         pen.SetCap(wx.CAP_PROJECTING)
 478         dc.SetPen(pen)
 479 
 480         dc.DrawLine(x, y + self.squareHeight() - 1, x, y)
 481         dc.DrawLine(x, y, x + self.squareWidth() - 1, y)
 482 
 483         darkpen = wx.Pen(dark[shape])
 484         darkpen.SetCap(wx.CAP_PROJECTING)
 485         dc.SetPen(darkpen)
 486 
 487         dc.DrawLine(x + 1, y + self.squareHeight() - 1,
 488             x + self.squareWidth() - 1, y + self.squareHeight() - 1)
 489         dc.DrawLine(x + self.squareWidth() - 1,
 490         y + self.squareHeight() - 1, x + self.squareWidth() - 1, y + 1)
 491 
 492         dc.SetPen(wx.TRANSPARENT_PEN)
 493         dc.SetBrush(wx.Brush(colors[shape]))
 494         dc.DrawRectangle(x + 1, y + 1, self.squareWidth() - 2,
 495         self.squareHeight() - 2)
 496 
 497 #---------------------------------------------------------------------------
 498 
 499 class Tetrominoes(object):
 500     """
 501     ...
 502     """
 503 
 504     NoShape = 0
 505     ZShape = 1
 506     SShape = 2
 507     LineShape = 3
 508     TShape = 4
 509     SquareShape = 5
 510     LShape = 6
 511     MirroredLShape = 7
 512 
 513 #---------------------------------------------------------------------------
 514 
 515 class Shape(object):
 516     """
 517     ...
 518     """
 519 
 520     coordsTable = (
 521         ((0, 0),     (0, 0),     (0, 0),     (0, 0)),
 522         ((0, -1),    (0, 0),     (-1, 0),    (-1, 1)),
 523         ((0, -1),    (0, 0),     (1, 0),     (1, 1)),
 524         ((0, -1),    (0, 0),     (0, 1),     (0, 2)),
 525         ((-1, 0),    (0, 0),     (1, 0),     (0, 1)),
 526         ((0, 0),     (1, 0),     (0, 1),     (1, 1)),
 527         ((-1, -1),   (0, -1),    (0, 0),     (0, 1)),
 528         ((1, -1),    (0, -1),    (0, 0),     (0, 1))
 529     )
 530 
 531     def __init__(self):
 532         self.coords = [[0,0] for i in range(4)]
 533         self.pieceShape = Tetrominoes.NoShape
 534 
 535         self.setShape(Tetrominoes.NoShape)
 536 
 537     #-----------------------------------------------------------------------
 538 
 539     def shape(self):
 540         """
 541         ...
 542         """
 543 
 544         return self.pieceShape
 545 
 546 
 547     def setShape(self, shape):
 548         """
 549         ...
 550         """
 551 
 552         table = Shape.coordsTable[shape]
 553         for i in range(4):
 554             for j in range(2):
 555                 self.coords[i][j] = table[i][j]
 556 
 557         self.pieceShape = shape
 558 
 559 
 560     def setRandomShape(self):
 561         """
 562         ...
 563         """
 564 
 565         self.setShape(random.randint(1, 7))
 566 
 567 
 568     def x(self, index):
 569         """
 570         ...
 571         """
 572 
 573         return self.coords[index][0]
 574 
 575 
 576     def y(self, index):
 577         """
 578         ...
 579         """
 580 
 581         return self.coords[index][1]
 582 
 583 
 584     def setX(self, index, x):
 585         """
 586         ...
 587         """
 588 
 589         self.coords[index][0] = x
 590 
 591 
 592     def setY(self, index, y):
 593         """
 594         ...
 595         """
 596 
 597         self.coords[index][1] = y
 598 
 599 
 600     def minX(self):
 601         """
 602         ...
 603         """
 604 
 605         m = self.coords[0][0]
 606         for i in range(4):
 607             m = min(m, self.coords[i][0])
 608 
 609         return m
 610 
 611 
 612     def maxX(self):
 613         """
 614         ...
 615         """
 616 
 617         m = self.coords[0][0]
 618         for i in range(4):
 619             m = max(m, self.coords[i][0])
 620 
 621         return m
 622 
 623 
 624     def minY(self):
 625         """
 626         ...
 627         """
 628 
 629         m = self.coords[0][1]
 630         for i in range(4):
 631             m = min(m, self.coords[i][1])
 632 
 633         return m
 634 
 635 
 636     def maxY(self):
 637         """
 638         ...
 639         """
 640 
 641         m = self.coords[0][1]
 642 
 643         for i in range(4):
 644             m = max(m, self.coords[i][1])
 645 
 646         return m
 647 
 648 
 649     def rotatedLeft(self):
 650         """
 651         ...
 652         """
 653 
 654         if self.pieceShape == Tetrominoes.SquareShape:
 655             return self
 656 
 657         result = Shape()
 658         result.pieceShape = self.pieceShape
 659 
 660         for i in range(4):
 661             result.setX(i, self.y(i))
 662             result.setY(i, -self.x(i))
 663 
 664         return result
 665 
 666 
 667     def rotatedRight(self):
 668         """
 669         ...
 670         """
 671 
 672         if self.pieceShape == Tetrominoes.SquareShape:
 673             return self
 674 
 675         result = Shape()
 676         result.pieceShape = self.pieceShape
 677 
 678         for i in range(4):
 679             result.setX(i, -self.y(i))
 680             result.setY(i, self.x(i))
 681 
 682         return result
 683 
 684 #---------------------------------------------------------------------------
 685 
 686 def main():
 687     app = wx.App()
 688     ex = Tetris(None)
 689     ex.Show(True)
 690     app.MainLoop()
 691 
 692 #---------------------------------------------------------------------------
 693 
 694 if __name__ == '__main__':
 695     main()


Second example

img_tetris_2.png

   1 # tetris_bis.py
   2 
   3 """
   4 wxPython Tetris game.
   5 
   6 Author : Hanming Zhang
   7 Link : https://github.com/HanmingZhang/Tetris
   8 """
   9 
  10 import wx
  11 import random
  12 
  13 # class SplitterFrame
  14 # class Info
  15 # class Board
  16 # class Tetrominoes
  17 # class Shape
  18 
  19 #---------------------------------------------------------------------------
  20 
  21 class SplitterFrame(wx.Frame):
  22     def __init__(self, parent, id, title):
  23         style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER ^ wx.MAXIMIZE_BOX
  24         wx.Frame.__init__(self, parent, id, title, size=(500, 600), style=style)
  25 
  26         #------------
  27 
  28         self.SetIcon(wx.Icon(".\icons\wxwin.ico"))
  29 
  30         #------------
  31 
  32         self.initpos = 600
  33 
  34         #------------
  35 
  36         self.sp = wx.SplitterWindow(self)
  37         self.board = Board(self.sp, style=wx.SUNKEN_BORDER)
  38         self.board.SetBackgroundColour("pink")
  39 
  40         self.info = Info(self.sp, style=wx.SUNKEN_BORDER)
  41         self.info.SetBackgroundColour("sky blue")
  42 
  43         self.sp.SplitVertically(self.board, self.info, self.initpos)
  44         self.sp.SetMinimumPaneSize(210)
  45 
  46         #------------
  47 
  48         self.ShowNextPiece = Shape()
  49         # self.board.SetFocus()
  50 
  51         #------------
  52 
  53         # self.Music = wx.Sound('E:\Lab7\s.mp3')
  54 
  55         #------------
  56 
  57         self.board.Start()
  58 
  59         #------------
  60 
  61         self.CenterOnScreen(wx.BOTH)
  62         self.Show(True)
  63 
  64 #---------------------------------------------------------------------------
  65 
  66 class Info(wx.Panel):
  67     BoardWidth = 6
  68     BoardHeight = 25
  69     def __init__(self, parent, style):
  70         wx.Panel.__init__(self, parent)
  71 
  72         #------------
  73 
  74         self.upspeedButton = wx.Button(self, -1, "Up Speed", pos=(15, 500))
  75         self.lowerButton = wx.Button(self, -1, "Lower Speed", pos=(100, 500))
  76         self.Bind(wx.EVT_BUTTON, self.OnUpSpeed, self.upspeedButton)
  77         self.Bind(wx.EVT_BUTTON, self.OnLowerSpeed, self.lowerButton)
  78         self.Bind(wx.EVT_PAINT, self.OnPaint)
  79 
  80         #------------
  81 
  82         temp = wx.StaticText(self, -1,"Coming next :", pos=(20, 40))
  83         temp.SetFont(wx.Font(18, wx.SCRIPT, wx.NORMAL, wx.NORMAL, False))
  84         temp.SetForegroundColour(wx.Colour(255, 255, 255))
  85         temp = wx.StaticText(self, -1, "Number of lines removed :", pos=(20, 360))
  86         temp.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD, False))
  87         temp = wx.StaticText(self, -1, "Your Score :", pos=(20, 420))
  88         temp.SetFont(wx.Font(10, wx.SWISS, wx.NORMAL, wx.BOLD, False))
  89 
  90     #-----------------------------------------------------------------------
  91 
  92     def OnUpSpeed(self,event):
  93          if Board.Speed >= 100:
  94               Board.Speed = Board.Speed -50
  95               self.GetParent().GetParent().board.timer.Start(Board.Speed)
  96 
  97 
  98     def OnLowerSpeed(self,event):
  99          Board.Speed = Board.Speed +50
 100          self.GetParent().GetParent().board.timer.Start(Board.Speed)
 101 
 102 
 103     def SquareWidth(self):
 104         return self.GetClientSize().GetWidth() / Info.BoardWidth
 105 
 106 
 107     def SquareHeight(self):
 108         return self.GetClientSize().GetHeight() / Info.BoardHeight
 109 
 110 
 111     def OnPaint(self, event):
 112         dc = wx.PaintDC(self)
 113         size = self.GetClientSize()
 114         boardTop = size.GetHeight() - Info.BoardHeight * self.SquareHeight()
 115 
 116         # dc.DrawText("Coming next:", 20, 40)
 117 
 118         # temp = wx.StaticText(self, -1, "Coming next:", pos=(20, 40))
 119         # temp.SetFont(wx.Font(5, wx.SWISS, wx.NORMAL, wx.BOLD, False))
 120 
 121         # dc.DrawText("Number of lines removed:", 5, 380)
 122         dc.DrawText("%d" %(self.GetParent().GetParent().board.numLinesRemoved), 90, 400)
 123         # dc.DrawText("Your Score:", 5, 420)
 124         dc.DrawText("%d" %(self.GetParent().GetParent().board.Score), 90, 460)
 125         #linesRemoved=wx.StaticText(self, -1, "", pos=(100, 400))
 126         #linesRemoved.SetFont(wx.Font(15, wx.SWISS, wx.NORMAL, wx.BOLD, False))
 127         #linesRemoved.SetForegroundColour(wx.Colour(0, 0, 0, 255))
 128         #linesRemoved.SetLabel("%d" %(self.GetParent().GetParent().board.numLinesRemoved))
 129 
 130 
 131         if self.GetParent().GetParent().ShowNextPiece.shape() != Tetrominoes.NoShape:
 132             for i in range(4):
 133                 x = self.GetParent().GetParent().ShowNextPiece.x(i)
 134                 y = self.GetParent().GetParent().ShowNextPiece.y(i)
 135                 self.DrawSquare(dc, 0 + x * self.SquareWidth()+80,
 136                     boardTop + (Info.BoardHeight + y - 1) * self.SquareHeight()-400,
 137                     self.GetParent().GetParent().ShowNextPiece.shape())
 138 
 139 
 140     def DrawSquare(self, dc, x, y, shape):
 141         colors = ['#000000', '#CC6666', '#66CC66', '#6666CC',
 142                   '#CCCC66', '#CC66CC', '#66CCCC', '#DAAA00']
 143 
 144         light = ['#000000', '#F89FAB', '#79FC79', '#7979FC',
 145                  '#FCFC79', '#FC79FC', '#79FCFC', '#FCC600']
 146 
 147         dark = ['#000000', '#803C3B', '#3B803B', '#3B3B80',
 148                 '#80803B', '#803B80', '#3B8080', '#806200']
 149 
 150         pen = wx.Pen(light[shape])
 151         pen.SetCap(wx.CAP_PROJECTING)
 152         dc.SetPen(pen)
 153 
 154         dc.DrawLine(x, y + self.SquareHeight() - 1, x, y)
 155         dc.DrawLine(x, y, x + self.SquareWidth() - 1, y)
 156 
 157         darkpen = wx.Pen(dark[shape])
 158         darkpen.SetCap(wx.CAP_PROJECTING)
 159         dc.SetPen(darkpen)
 160 
 161         dc.DrawLine(x + 1, y + self.SquareHeight() - 1,
 162             x + self.SquareWidth() - 1, y + self.SquareHeight() - 1)
 163         dc.DrawLine(x + self.SquareWidth() - 1,
 164         y + self.SquareHeight() - 1, x + self.SquareWidth() - 1, y + 1)
 165 
 166         dc.SetPen(wx.TRANSPARENT_PEN)
 167         dc.SetBrush(wx.Brush(colors[shape]))
 168         dc.DrawRectangle(x + 1, y + 1, self.SquareWidth() - 2,
 169                          self.SquareHeight() - 2)
 170 
 171 #---------------------------------------------------------------------------
 172 
 173 class Board(wx.Panel):
 174     BoardWidth = 12
 175     BoardHeight = 20
 176     Speed = 300
 177     ID_TIMER = 1
 178     def __init__(self, parent, style):
 179         wx.Panel.__init__(self, parent, style=wx.WANTS_CHARS)
 180         # You can use arrow keys after adding style = wx.WANTS_CHARS.
 181 
 182 
 183         #------------
 184 
 185         # Delete flickers.
 186         if wx.Platform == "__WXMSW__":
 187             self.SetDoubleBuffered(True)
 188 
 189         #------------
 190 
 191         self.timer = wx.Timer(self, Board.ID_TIMER)
 192         self.isWaitingAfterLine = False
 193         self.curPiece = Shape()
 194         self.nextPiece = Shape()
 195         self.nextPiece.SetRandomShape()
 196         self.curX = 0
 197         self.curY = 0
 198         self.numLinesRemoved = 0
 199         self.Score = 0
 200         self.board = []
 201 
 202         #------------
 203 
 204         self.isStarted = False
 205         self.isPaused = False
 206 
 207         #------------
 208 
 209         self.Bind(wx.EVT_PAINT, self.OnPaint)
 210         self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
 211         self.Bind(wx.EVT_TIMER, self.OnTimer, id=Board.ID_TIMER)
 212 
 213         #------------
 214 
 215         self.ClearBoard()
 216 
 217     #-----------------------------------------------------------------------
 218 
 219     def ShapeAt(self, x, y):
 220         return self.board[(int(y) * int(Board.BoardWidth)) + int(x)]
 221 
 222 
 223     def SetShapeAt(self, x, y, shape):
 224         self.board[(int(y) * int(Board.BoardWidth)) + int(x)] = shape
 225 
 226 
 227     def SquareWidth(self):
 228         return self.GetClientSize().GetWidth() / Board.BoardWidth
 229 
 230 
 231     def SquareHeight(self):
 232         return self.GetClientSize().GetHeight() / Board.BoardHeight
 233 
 234 
 235     def Start(self):
 236         if self.isPaused:
 237             return
 238 
 239         self.isStarted = True
 240         self.isWaitingAfterLine = False
 241         self.numLinesRemoved = 0
 242         self.Score = 0
 243         self.ClearBoard()
 244 
 245         wx.MessageBox("""Hello !\n"""
 246                       """Welcome to Tetris World.\n\n"""
 247                       """Here are some information you may need.\n\n"""
 248                       """Azerty keyboard :\n"""
 249                       """--------------------\n"""
 250                       """Z --> Rotate\n"""
 251                       """Q/D --> Left or Right move\n"""
 252                       """S --> One line down\n"""
 253                       """Space --> Drop down\n"""
 254                       """P --> Pause\n\n"""
 255                       """Hope you enjoy ! ^.^""",
 256                       "Tetris",
 257                       wx.OK | wx.ICON_INFORMATION,
 258                       self)
 259 
 260         # self.GetParent().GetParent().Music.Play(wx.SOUND_ASYNC)
 261 
 262         self.NewPiece()
 263         self.timer.Start(Board.Speed)
 264 
 265 
 266     def Pause(self):
 267         if not self.isStarted:
 268             return
 269 
 270         self.isPaused = not self.isPaused
 271         # statusbar = self.GetParent().statusbar
 272 
 273         if self.isPaused:
 274             self.timer.Stop()
 275             # statusbar.SetStatusText('Paused')
 276         else:
 277             self.timer.Start(Board.Speed)
 278             # statusbar.SetStatusText(str(self.numLinesRemoved))
 279         self.Refresh()
 280 
 281 
 282     def ClearBoard(self):
 283         for i in range(Board.BoardHeight * Board.BoardWidth):
 284             self.board.append(Tetrominoes.NoShape)
 285 
 286 
 287     def OnPaint(self, event):
 288         dc = wx.PaintDC(self)
 289 
 290         size = self.GetClientSize()
 291         boardTop = size.GetHeight() - Board.BoardHeight * self.SquareHeight()
 292 
 293         for i in range(Board.BoardHeight):
 294             for j in range(Board.BoardWidth):
 295                 shape = self.ShapeAt(j, Board.BoardHeight - i - 1)
 296                 if shape != Tetrominoes.NoShape:
 297                     self.DrawSquare(dc,
 298                         0 + j * self.SquareWidth(),
 299                         boardTop + i * self.SquareHeight(), shape)
 300 
 301         if self.curPiece.shape() != Tetrominoes.NoShape:
 302             for i in range(4):
 303                 x = self.curX + self.curPiece.x(i)
 304                 y = self.curY - self.curPiece.y(i)
 305                 self.DrawSquare(dc, 0 + x * self.SquareWidth(),
 306                     boardTop + (Board.BoardHeight - y - 1) * self.SquareHeight(),
 307                     self.curPiece.shape())
 308 
 309 
 310     def OnKeyDown(self, event):
 311         if not self.isStarted or self.curPiece.shape() == Tetrominoes.NoShape:
 312             event.Skip()
 313             return
 314 
 315         keycode = event.GetKeyCode()
 316 
 317         if keycode == ord('P') or keycode == ord('p'):
 318             self.Pause()
 319             return
 320         if self.isPaused:
 321             return
 322         elif keycode == ord('Q') or keycode == ord('q'):
 323             self.TryMove(self.curPiece, self.curX - 1, self.curY)
 324         elif keycode == ord('D') or keycode == ord('d'):
 325             self.TryMove(self.curPiece, self.curX + 1, self.curY)
 326         #elif keycode == wx.WXK_DOWN:
 327             #self.TryMove(self.curPiece.rotatedRight(), self.curX, self.curY)
 328         elif keycode == ord('Z') or keycode == ord('z'):
 329             self.TryMove(self.curPiece.RotatedLeft(), self.curX, self.curY)
 330         elif keycode == ord('S') or keycode == ord('s'):
 331             self.OneLineDown()
 332         elif keycode == wx.WXK_SPACE:
 333             self.DropDown()
 334         else:
 335             event.Skip()
 336 
 337 
 338     def OnTimer(self, event):
 339         if event.GetId() == Board.ID_TIMER:
 340             if self.isWaitingAfterLine:
 341                 self.isWaitingAfterLine = False
 342                 self.NewPiece()
 343             else:
 344                 self.OneLineDown()
 345         else:
 346             event.Skip()
 347 
 348 
 349     def DropDown(self):
 350         newY = self.curY
 351         while newY > 0:
 352             if not self.TryMove(self.curPiece, self.curX, newY - 1):
 353                 break
 354             newY -= 1
 355 
 356         self.PieceDropped()
 357 
 358 
 359     def OneLineDown(self):
 360         if not self.TryMove(self.curPiece, self.curX, self.curY - 1):
 361             self.PieceDropped()
 362 
 363 
 364     def PieceDropped(self):
 365         for i in range(4):
 366             x = self.curX + self.curPiece.x(i)
 367             y = self.curY - self.curPiece.y(i)
 368             self.SetShapeAt(x, y, self.curPiece.shape())
 369 
 370         self.RemoveFullLines()
 371 
 372         if not self.isWaitingAfterLine:
 373             self.NewPiece()
 374 
 375 
 376     def RemoveFullLines(self):
 377         numFullLines = 0
 378 
 379         # statusbar = self.GetParent().statusbar
 380 
 381         rowsToRemove = []
 382 
 383         for i in range(Board.BoardHeight):
 384             n = 0
 385             for j in range(Board.BoardWidth):
 386                 if not self.ShapeAt(j, i) == Tetrominoes.NoShape:
 387                     n = n + 1
 388 
 389             if n == 12:
 390                 rowsToRemove.append(i)
 391 
 392         rowsToRemove.reverse()
 393 
 394         for m in rowsToRemove:
 395             for k in range(m, Board.BoardHeight):
 396                 for l in range(Board.BoardWidth):
 397                         self.SetShapeAt(l, k, self.ShapeAt(l, k + 1))
 398 
 399             numFullLines = numFullLines + len(rowsToRemove)
 400 
 401 
 402             if numFullLines > 0:
 403                 #if numFullLines == 1:
 404                     #self.Score += 10
 405                 #else :
 406                     #self.Score += numFullLines*20
 407                 #self.numLinesRemoved += numFullLines
 408                 # statusbar.SetStatusText(str(self.numLinesRemoved))
 409                 self.isWaitingAfterLine = True
 410                 self.curPiece.SetShape(Tetrominoes.NoShape)
 411                 self.Refresh()
 412 
 413         if len(rowsToRemove) != 0:
 414             self.numLinesRemoved += len(rowsToRemove)
 415             if len(rowsToRemove) == 1:
 416                 self.Score += 10
 417             else:
 418                 self.Score += len(rowsToRemove)*20
 419             self.GetParent().GetParent().info.Refresh()
 420 
 421 
 422     def NewPiece(self):
 423         # self.curPiece = self.nextPiece (it's wrong !)
 424         # The original one has problem !!
 425         # self.curPiece will always be the same as self.nextPiece
 426         # This is the proper way !
 427         self.curPiece.SetShape(self.nextPiece.shape())
 428         # statusbar = self.GetParent().statusbar
 429         self.nextPiece.SetRandomShape()
 430 
 431         self.GetParent().GetParent().ShowNextPiece = self.nextPiece
 432         # self.GetParent is a SpiltterWindow, and SpiltterWIndow's parent is SplitterFrame !
 433         self.GetParent().GetParent().info.Refresh()
 434 
 435         self.curX = Board.BoardWidth / 2 + 1
 436         self.curY = Board.BoardHeight - 1 + self.curPiece.MinY()
 437 
 438         if not self.TryMove(self.curPiece, self.curX, self.curY):
 439             self.curPiece.SetShape(Tetrominoes.NoShape)
 440             self.timer.Stop()
 441             self.isStarted = False
 442             wx.MessageBox("Sorry ! Game Over !", "Game Over",
 443                           wx.OK | wx.ICON_INFORMATION, self)
 444             # Once the game is over, the program will quit.
 445             self.GetParent().GetParent().Close()
 446 
 447 
 448     def TryMove(self, NewPiece, newX, newY):
 449         for i in range(4):
 450             x = newX + NewPiece.x(i)
 451             y = newY - NewPiece.y(i)
 452             if x < 0 or x >= Board.BoardWidth or y < 0 or y >= Board.BoardHeight:
 453                 return False
 454             if self.ShapeAt(x, y) != Tetrominoes.NoShape:
 455                 return False
 456 
 457         self.curPiece = NewPiece
 458         self.curX = newX
 459         self.curY = newY
 460         self.Refresh()
 461         return True
 462 
 463 
 464     def DrawSquare(self, dc, x, y, shape):
 465         colors = ['#000000', '#CC6666', '#66CC66', '#6666CC',
 466                   '#CCCC66', '#CC66CC', '#66CCCC', '#DAAA00']
 467 
 468         light = ['#000000', '#F89FAB', '#79FC79', '#7979FC',
 469                  '#FCFC79', '#FC79FC', '#79FCFC', '#FCC600']
 470 
 471         dark = ['#000000', '#803C3B', '#3B803B', '#3B3B80',
 472                  '#80803B', '#803B80', '#3B8080', '#806200']
 473 
 474         pen = wx.Pen(light[shape])
 475         pen.SetCap(wx.CAP_PROJECTING)
 476         dc.SetPen(pen)
 477 
 478         dc.DrawLine(x, y + self.SquareHeight() - 1, x, y)
 479         dc.DrawLine(x, y, x + self.SquareWidth() - 1, y)
 480 
 481         darkpen = wx.Pen(dark[shape])
 482         darkpen.SetCap(wx.CAP_PROJECTING)
 483         dc.SetPen(darkpen)
 484 
 485         dc.DrawLine(x + 1, y + self.SquareHeight() - 1,
 486             x + self.SquareWidth() - 1, y + self.SquareHeight() - 1)
 487         dc.DrawLine(x + self.SquareWidth() - 1,
 488         y + self.SquareHeight() - 1, x + self.SquareWidth() - 1, y + 1)
 489 
 490         dc.SetPen(wx.TRANSPARENT_PEN)
 491         dc.SetBrush(wx.Brush(colors[shape]))
 492         dc.DrawRectangle(x + 1, y + 1, self.SquareWidth() - 2,
 493         self.SquareHeight() - 2)
 494 
 495 #---------------------------------------------------------------------------
 496 
 497 class Tetrominoes(object):
 498     NoShape = 0
 499     ZShape = 1
 500     SShape = 2
 501     LineShape = 3
 502     TShape = 4
 503     SquareShape = 5
 504     LShape = 6
 505     MirroredLShape = 7
 506 
 507 #---------------------------------------------------------------------------
 508 
 509 class Shape(object):
 510     coordsTable = (
 511         ((0, 0),     (0, 0),     (0, 0),     (0, 0)),
 512         ((0, -1),    (0, 0),     (-1, 0),    (-1, 1)),
 513         ((0, -1),    (0, 0),     (1, 0),     (1, 1)),
 514         ((0, -1),    (0, 0),     (0, 1),     (0, 2)),
 515         ((-1, 0),    (0, 0),     (1, 0),     (0, 1)),
 516         ((0, 0),     (1, 0),     (0, 1),     (1, 1)),
 517         ((-1, -1),   (0, -1),    (0, 0),     (0, 1)),
 518         ((1, -1),    (0, -1),    (0, 0),     (0, 1))
 519     )
 520     def __init__(self):
 521         self.coords = [[0,0] for i in range(4)]
 522         self.pieceShape = Tetrominoes.NoShape
 523         self.SetShape(Tetrominoes.NoShape)
 524 
 525     #-----------------------------------------------------------------------
 526 
 527     def shape(self):
 528         return self.pieceShape
 529 
 530 
 531     def SetShape(self, shape):
 532         table = Shape.coordsTable[shape]
 533         for i in range(4):
 534             for j in range(2):
 535                 self.coords[i][j] = table[i][j]
 536 
 537         self.pieceShape = shape
 538 
 539 
 540     def SetRandomShape(self):
 541         self.SetShape(random.randint(1, 7))
 542 
 543 
 544     def x(self, index):
 545         return self.coords[index][0]
 546 
 547 
 548     def y(self, index):
 549         return self.coords[index][1]
 550 
 551 
 552     def SetX(self, index, x):
 553         self.coords[index][0] = x
 554 
 555 
 556     def SetY(self, index, y):
 557         self.coords[index][1] = y
 558 
 559 
 560     def MinX(self):
 561         m = self.coords[0][0]
 562         for i in range(4):
 563             m = min(m, self.coords[i][0])
 564         return m
 565 
 566 
 567     def MaxX(self):
 568         m = self.coords[0][0]
 569         for i in range(4):
 570             m = max(m, self.coords[i][0])
 571         return m
 572 
 573 
 574     def MinY(self):
 575         m = self.coords[0][1]
 576         for i in range(4):
 577             m = min(m, self.coords[i][1])
 578         return m
 579 
 580 
 581     def MaxY(self):
 582         m = self.coords[0][1]
 583         for i in range(4):
 584             m = max(m, self.coords[i][1])
 585         return m
 586 
 587 
 588     def RotatedLeft(self):
 589         if self.pieceShape == Tetrominoes.SquareShape:
 590             return self
 591 
 592         result = Shape()
 593         result.pieceShape = self.pieceShape
 594         for i in range(4):
 595             result.SetX(i, self.y(i))
 596             result.SetY(i, -self.x(i))
 597 
 598         return result
 599 
 600 #---------------------------------------------------------------------------
 601 
 602 def main():
 603     app = wx.App()
 604     SplitterFrame(None, -1, 'Tetris')
 605     app.MainLoop()
 606 
 607 #---------------------------------------------------------------------------
 608 
 609 if __name__ == '__main__':
 610     main()


Memory game

img_memory_game.png

Readme file : memory_README.md

   1 # memory_game.py
   2 
   3 """
   4 wxPython Memory game.
   5 All images should be in folder named "Images".
   6 Images used are W : 100px H : 150px, though any size works if all images same size.
   7 Line 32 has some commented out code to print the key to terminal for testing.
   8 
   9 Author : Zoenberger
  10 Link : https://github.com/zoenberger/Memory-Game-using-wxPython
  11 """
  12 
  13 import wx
  14 import os
  15 import random
  16 import time
  17 
  18 # class MemoryGame
  19 # def Winner
  20 # def GetJpgList
  21 
  22 #---------------------------------------------------------------------------
  23 
  24 class MemoryGame(wx.Frame):
  25     def __init__(self):
  26         wx.Frame.__init__(self, None, title="")
  27 
  28         #------------
  29 
  30         self.SetIcon(wx.Icon(".\icons\wxwin.ico"))
  31         self.SetTitle("Memory game")
  32         self.SetSize((900,700))
  33 
  34         #------------
  35 
  36         self.Move((50,25))
  37 
  38         #------------
  39 
  40         self.panel = wx.Panel(self)
  41 
  42         #------------
  43 
  44         # Define how big game is...can be useful
  45         # for making skill options later.
  46         self.numPairs = 12
  47 
  48         # Get all images in directory called
  49         # "Images" & shuffle order
  50         self.imageArray = GetJpgList("./images")
  51         random.shuffle(self.imageArray)
  52 
  53         # Create array with how many cards needed
  54         # and double it to make matched pairs.
  55         self.imagePairs = self.imageArray[0:self.numPairs]
  56         self.imagePairs = self.imagePairs * 2
  57 
  58         # Because we doubled, we need to re-shuffle order.
  59         random.shuffle(self.imagePairs)
  60 
  61         #------------
  62 
  63         # PRINT KEY TO TERMINAL SO YOU CAN QUICKLY SOLVE.
  64         # countrow=0
  65         # for card in self.imagePairs:
  66         #     countrow +=1
  67         #     if countrow%6 == 0:
  68         #         print card
  69         #     else:
  70         #         print card,
  71 
  72         # Create blank card and give name of file name.
  73         card = wx.Image('./images/main_card.jpg',wx.BITMAP_TYPE_ANY).ConvertToBitmap()
  74         self.blankCards =[]
  75         for i in range(len(self.imagePairs)):
  76             self.blankCards.append(wx.StaticBitmap(self.panel,wx.ID_ANY, card,name=self.imagePairs[i]))
  77 
  78         # Bind left click to each card that calls check function.
  79         for img in self.blankCards:
  80             img.Bind(wx.EVT_LEFT_DOWN, self.OnClick)
  81 
  82         #------------
  83 
  84         # Visual Layout.
  85         hbox = wx.BoxSizer(wx.HORIZONTAL)
  86         gs = wx.GridSizer(4, 6, 15, 15)
  87         gs.AddMany(self.blankCards)
  88         hbox.Add(gs, proportion=0, flag = wx.LEFT | wx.TOP, border = 10)
  89 
  90         title = wx.StaticText(self.panel, label="Memory Game")
  91         hbox.Add(title,proportion=1, flag = wx.LEFT | wx.TOP | wx.RIGHT | wx.EXPAND, border = 10)
  92 
  93         self.panel.SetSizer(hbox)
  94         self.Show()
  95 
  96         #------------
  97 
  98         # Keeps track to see if you've won.
  99         self.foundMatches= 0
 100         # Keeps track of 1st or second click.
 101         self.clickCount = 0
 102         # Holding spot if it's first click.
 103         self.card1 = ''
 104         self.totalTries = 0
 105 
 106     #-----------------------------------------------------------------------
 107 
 108     def OnClick(self, event):
 109         self.clickCount += 1
 110 
 111         #------------
 112 
 113         # Get card clicked on, swap out blank
 114         # image with image by filename.
 115         newCard = event.GetEventObject()
 116         img = wx.Image(newCard.GetName(), wx.BITMAP_TYPE_ANY)
 117         newCard.SetBitmap(wx.Bitmap(img))
 118 
 119         #------------
 120 
 121         if self.clickCount == 1:
 122             # Put into holding space if 1st click.
 123             self.card1 = newCard
 124             self.card1.Unbind(wx.EVT_LEFT_DOWN)
 125 
 126         else:
 127             # FOUND MATCH : unbind click events. Update match tracker.
 128             self.totalTries += 1
 129             if (newCard.GetName() == self.card1.GetName()):
 130                 for findItem in self.blankCards:
 131                     if findItem.GetName() == newCard.GetName():
 132                         findItem.Unbind(wx.EVT_LEFT_DOWN)
 133                 self.foundMatches += 1
 134                 print(self.foundMatches)
 135             else:
 136                 # NO MATCH : wait then hide both cards again.
 137                 # This basically freezes screen, but clicks still captured.
 138                 time.sleep(1)
 139                 blankCard = wx.Image('./images/main_card.jpg', wx.BITMAP_TYPE_ANY)
 140                 newCard.SetBitmap(wx.Bitmap(blankCard))
 141                 self.card1.SetBitmap(wx.Bitmap(blankCard))
 142                 self.card1.Bind(wx.EVT_LEFT_DOWN, self.OnClick)
 143             self.clickCount = 0
 144 
 145         if self.foundMatches == self.numPairs:
 146             Winner()
 147             print("Total Tries = " + str(self.totalTries))
 148             total = ("Total Tries = " + str(self.totalTries))
 149 
 150             dlg = wx.MessageDialog(self,
 151                                    total,
 152                                    "Winner !",
 153                                    wx.OK | wx.CENTRE | wx.ICON_INFORMATION)
 154             dlg.ShowModal()
 155             dlg.Destroy()
 156 
 157 #---------------------------------------------------------------------------
 158 
 159 # NOTE : make the winning more exciting. Hahahaha.
 160 def Winner():
 161     print("WINNER WINNER WINNER !")
 162 
 163 #---------------------------------------------------------------------------
 164 
 165 # Get all JPEGs in a directory that is passed and return image names array.
 166 # Note I found this code snippet here :
 167 # http ://wiki.wxpython.org/wxStaticBitmap
 168 def GetJpgList(loc):
 169     jpgs = [f for f in os.listdir(loc) if f[-4:] == ".jpg"]
 170     #print "JPGS are:", jpgs
 171 
 172     return [os.path.join(loc, f) for f in jpgs]
 173 
 174 #---------------------------------------------------------------------------
 175 
 176 def main():
 177     app = wx.App(False)
 178     frame = MemoryGame()
 179     app.MainLoop()
 180 
 181 #---------------------------------------------------------------------------
 182 
 183 if __name__ == '__main__':
 184     main()


Reversi

img_reversi.png

   1 # reversi.py
   2 
   3 """
   4 wxPython Reversi game.
   5 
   6 Author : Hatenablog
   7 Link : https://yatt.hatenablog.jp/entry/20100129/1264791420
   8 """
   9 
  10 import wx
  11 from copy import deepcopy
  12 
  13 WHITE = -1
  14 BLANK = 0
  15 BLACK = 1
  16 
  17 # def Notify
  18 # class Reversi
  19 # class Frame
  20 
  21 #---------------------------------------------------------------------------
  22 
  23 def Notify(caption, message):
  24     dialog = wx.MessageDialog(None,
  25                               message=message,
  26                               caption=caption,
  27                               style=wx.OK)
  28     dialog.ShowModal()
  29     dialog.Destroy()
  30 
  31 #---------------------------------------------------------------------------
  32 
  33 class Reversi(object):
  34     def __init__(self):
  35 
  36         #------------
  37 
  38         self.board = [[0]*8 for x in range(8)]
  39         self.board[3][3] = WHITE
  40         self.board[4][4] = WHITE
  41         self.board[3][4] = BLACK
  42         self.board[4][3] = BLACK
  43         self.turns = 1              # Turn counter.
  44         self.current = BLACK        # Current player.
  45         self.nstone = [2, 60, 2]    # white, blank, black.
  46         self.nreversed = 0
  47         self.passed = None
  48         self.over = False
  49 
  50     #-----------------------------------------------------------------------
  51 
  52     def __getitem__(self, i):
  53         return self.board[i]
  54 
  55 
  56     def Stones(self, s):
  57         return self.nstone[1 + s]
  58 
  59 
  60     def Feverseline(self, x, y, dx, dy):
  61         if not (0<=x<8) or not (0<=y<8):
  62             return False
  63 
  64         elif self[x][y] == BLANK:
  65             return False
  66 
  67         elif self[x][y] == self.current:
  68             return True
  69 
  70         elif self[x][y] == -self.current:
  71             ret = self.Feverseline(x+dx, y+dy, dx, dy)
  72             if ret:
  73                 self[x][y] *= -1 # Reverse.
  74                 self.nreversed += 1
  75             return ret
  76 
  77 
  78     def Put(self, x, y, validation=False):
  79         if self.over or self[x][y] != BLANK:
  80             return False
  81 
  82         backup = self[x][y]
  83         self[x][y] = self.current
  84         self.nreversed = 0
  85 
  86         for dx in [-1, 0, 1]:
  87             for dy in [-1, 0, 1]:
  88                 self.Feverseline(x+dx, y+dy, dx, dy)
  89 
  90         if self.nreversed == 0:
  91             self[x][y] = backup
  92             return False
  93 
  94         if validation:
  95             # Despress changing state but only self.board.
  96             return True
  97 
  98         self.nstone[1 + BLANK] -= 1
  99         self.nstone[1 + self.current] += self.nreversed + 1
 100         self.nstone[1 - self.current] -= self.nreversed
 101         self.turns += 1
 102         self.current *= -1 # Change turn.
 103 
 104         # Check game is over or not.
 105         for s in [WHITE, BLANK, BLACK]:
 106             if self.Stones(s) == 0:
 107                 self.over = True
 108                 return True
 109 
 110         for i in range(8):
 111             for j in range(8):
 112                 if self.Available(i, j):
 113                     self.passed = None
 114                     return True
 115 
 116         self.passed = self.current
 117         self.current *= -1
 118 
 119         for i in range(8):
 120             for j in range(8):
 121                 if self.Available(i, j):
 122                     return True
 123         self.current *= -1
 124         self.over = True
 125         return True
 126 
 127 
 128     def Available(self, x, y):
 129         if self.over:
 130             return False
 131 
 132         # Backup board.
 133         backup = deepcopy(self.board)
 134         ret = self.Put(int(x), int(y), True)
 135         # Restore board.
 136         self.board = backup
 137         return ret
 138 
 139 #---------------------------------------------------------------------------
 140 
 141 class Frame(wx.Frame):
 142     def __init__(self):
 143         style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER ^ wx.MAXIMIZE_BOX
 144         wx.Frame.__init__(self, None, -1, "Reversi", style=style)
 145 
 146         #------------
 147 
 148         self.SetIcon(wx.Icon(".\icons\wxwin.ico"))
 149 
 150         #------------
 151 
 152         # Panel.
 153         self.panel = wx.Panel(self)
 154         self.panel.Bind(wx.EVT_LEFT_DOWN, self.TryPut)
 155         self.panel.Bind(wx.EVT_PAINT, self.Refresh)
 156 
 157         #------------
 158 
 159         self.Newgame(None)
 160 
 161         #------------
 162 
 163         # Menubar.
 164         menu = wx.Menu()
 165         menu.Append(1, "New game")
 166         menu.AppendSeparator()
 167         menu.Append(2, "Quit")
 168         menubar = wx.MenuBar()
 169         menubar.Append(menu, "Menu")
 170         self.SetMenuBar(menubar)
 171 
 172         self.Bind(wx.EVT_MENU, self.Newgame, id=1)
 173         self.Bind(wx.EVT_MENU, self.Quit, id=2)
 174 
 175         #------------
 176 
 177         # Status bar.
 178         self.CreateStatusBar()
 179 
 180         #------------
 181 
 182         # Ban resize.
 183         # self.Bind(wx.EVT_SIZE, lambda e:e)
 184 
 185         #------------
 186 
 187         self.Show()
 188 
 189     #-----------------------------------------------------------------------
 190 
 191     def Quit(self, event):
 192         self.Close()
 193 
 194 
 195     def Newgame(self, event):
 196         # Initialize reversi and Refresh screen.
 197         self.reversi = Reversi()
 198         self.panel.Refresh()
 199 
 200 
 201     def TryPut(self, event):
 202         if self.reversi.over:
 203             return
 204 
 205         # Calculate coordinate from window coordinate.
 206         winx,winy = event.GetX(), event.GetY()
 207         w,h = self.panel.GetSize()
 208         x = winx / (w/8)
 209         y = winy / (h/8)
 210 
 211         if not self.reversi.Available(int(x), int(y)):
 212             return
 213 
 214         ret = self.reversi.Put(int(x), int(y))
 215         self.panel.Refresh()
 216 
 217         # If game is over then display dialog.
 218         if self.reversi.over:
 219             black = self.reversi.Stones(BLACK)
 220             white = self.reversi.Stones(WHITE)
 221             mes = "Black : %d\nWhite : %d\n" % (black, white)
 222             if black == white:
 223                 mes += "** draw **"
 224             else:
 225                 mes += "Winner : %s" % ["Black", "White"][black < white]
 226             Notify("Game is over !", mes)
 227 
 228         elif self.reversi.passed != None:
 229             Notify("Passing turn", "Pass")
 230 
 231 
 232     def Refresh(self, event):
 233         dc = wx.AutoBufferedPaintDCFactory(self.panel)
 234         dc = wx.GCDC(dc)
 235         self.SetStatusText("Current player is " + ["White", "Black"][self.reversi.current==BLACK])
 236         w,h = self.panel.GetSize()
 237 
 238         # Background.
 239         dc.SetBrush(wx.Brush("#228b22"))
 240         dc.DrawRectangle(0,0,w,h)
 241         # Grid.
 242         dc.SetBrush(wx.Brush("black"))
 243         px,py = w/8,h/8
 244 
 245         for i in range(8):
 246             dc.DrawLine(i*px,0, i*px, h)
 247             dc.DrawLine(0, i*py, w, i*py)
 248         dc.DrawLine(w-1, 0, w-1, h-1)
 249         dc.DrawLine(0, h-1, w-1, h-1)
 250 
 251         # Stones.
 252         brushes = {WHITE: wx.Brush("white"), BLACK: wx.Brush("black")}
 253         for i in range(8):
 254             for j in range(8):
 255                 c = self.reversi[i][j]
 256                 if c != BLANK:
 257                     dc.SetBrush(brushes[c])
 258                     dc.DrawEllipse(i*px, j*py, px, py)
 259 
 260                 elif self.reversi.Available(i, j):
 261                     dc.SetBrush(wx.Brush("red"))
 262                     dc.DrawCircle(i*px+px/2, j*py+py/2, 3)
 263 
 264 #---------------------------------------------------------------------------
 265 
 266 def main():
 267     app = wx.App(False)
 268     frame = Frame()
 269     frame.SetSize((400, 400))
 270     app.MainLoop()
 271 
 272 #---------------------------------------------------------------------------
 273 
 274 if __name__ == '__main__':
 275     main()


Towers of hanoi

First example

img_towers_of_hanoi_1.png

Readme file : hanoi_README.md

License file : hanoi_LICENSE.md

   1 # towers_of_hanoi.py
   2 
   3 """
   4 The Towers of Hanoi for wxPython
   5 by S. Foster, Jan. 2004
   6 Link : https://code.activestate.com/recipes/577511-hanoi-towers-solver-wxpython/
   7 
   8 Based on wxHanoi.cpp
   9 By Martin Bernreuther
  10 """
  11 
  12 import wx
  13 
  14 COLOURS = ['RED', 'CORAL', 'YELLOW', 'GREEN', 'BLUE', 'MAGENTA', 'PURPLE']
  15 
  16 # class wxHanoiDisc
  17 # class wxHanoiFrame
  18 # class wxHanoiApp
  19 
  20 #---------------------------------------------------------------------------
  21 
  22 class wxHanoiDisc:
  23     """
  24     ...
  25     """
  26     def __init__(self, n, width, height):
  27 
  28         #------------
  29 
  30         self.width = (n + 1) * width
  31         self.height = height
  32         self.n = n
  33         self.brush = wx.Brush(wx.Colour(COLOURS[n % len(COLOURS)]))
  34 
  35 #---------------------------------------------------------------------------
  36 
  37 class wxHanoiFrame(wx.Frame):
  38     """
  39     ...
  40     """
  41     def __init__(self, title, size):
  42         style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER ^ wx.MAXIMIZE_BOX
  43         wx.Frame.__init__(self, None, -1,
  44                           title, size=size,
  45                           style=style)
  46 
  47         #------------
  48 
  49         self.SetIcon(wx.Icon(".\icons\wxwin.ico"))
  50 
  51         #------------
  52 
  53         self.sleepTime = 3
  54         self.numDiscs = 4
  55 
  56         self.pen = wx.Pen(wx.Colour('BLACK'), 1, wx.SOLID)
  57 
  58         #------------
  59 
  60         self.CreateMenu()
  61         self.CreateStatusBar()
  62         self.Initialize()
  63         self.BindEvents()
  64 
  65         #------------
  66 
  67         self.CenterOnScreen(wx.BOTH)
  68         self.Show(True)
  69 
  70     #-----------------------------------------------------------------------
  71 
  72     def CreateMenu(self):
  73         def MenuCallback(menu, item, callback):
  74             id = wx.NewIdRef()
  75             menu.Append(id, item)
  76             self.Bind(wx.EVT_MENU, callback, id)
  77 
  78         menuBar = wx.MenuBar()
  79 
  80         menuFile = wx.Menu()
  81         MenuCallback(menuFile, 'E&xit...\tCtrl-X', self.OnQuit)
  82         menuBar.Append(menuFile, '&File')
  83 
  84         menuPlay = wx.Menu()
  85         MenuCallback(menuPlay, '&Number of Discs...', self.OnInpNumDiscs)
  86         MenuCallback(menuPlay, '&Time between Moves...', self.OnInpSleepTime)
  87         menuPlay.AppendSeparator()
  88         MenuCallback(menuPlay, '&Play', self.OnPlay)
  89         MenuCallback(menuPlay, '&Reset', self.OnReset)
  90         menuBar.Append(menuPlay, '&Play')
  91 
  92         menuHelp = wx.Menu()
  93         MenuCallback(menuHelp, '&About...', self.OnAbout)
  94         menuBar.Append(menuHelp, '&Help')
  95 
  96         self.SetMenuBar(menuBar)
  97 
  98 
  99     def Initialize(self):
 100         self.width, self.height = self.GetClientSize()
 101         maxwidth = self.width / 3
 102         w = self.width / 6
 103         self.xpos = [i * w for i in [1, 3, 5]]
 104 
 105         height = self.height / self.numDiscs
 106         width = maxwidth / self.numDiscs
 107         if height > width:
 108             height = width
 109         self.discheight = height
 110         self.discwidthfac = width
 111 
 112         discs = list(range(self.numDiscs))
 113         discs.reverse()
 114 
 115         self.pegs = [[wxHanoiDisc(i, width, height) for i in discs], [], []]
 116         self.moves = 0
 117 
 118         width, height = self.GetClientSize()
 119         self.Draw(wx.ClientDC(self), width, height)
 120 
 121 
 122     def BindEvents(self):
 123         self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
 124         self.Bind(wx.EVT_PAINT, self.OnPaint)
 125 
 126 
 127     def OnQuit(self, event):
 128         self.Close(True)
 129 
 130 
 131     def OnCloseWindow(self, event):
 132         self.Destroy()
 133 
 134 
 135     def OnAbout(self, event):
 136         wx.MessageBox(__doc__, 'About wxHanoi', wx.OK|wx.ICON_INFORMATION, self)
 137 
 138 
 139     def OnInpNumDiscs(self, _event):
 140         self.numDiscs = wx.GetNumberFromUser('', 'Discs:', 'Number of Discs',
 141                                              self.numDiscs, 1, 25, self)
 142         if self.numDiscs == -1:
 143             self.SetStatusText("Invalid number entered or dialog cancelled.")
 144         else:
 145             self.Initialize()
 146 
 147 
 148     def OnInpSleepTime(self, event):
 149         self.sleepTime = wx.GetNumberFromUser('', 'Wait [sec/10]:', 'Time between Moves',
 150                                               self.sleepTime, 0, 10, self)
 151         if self.sleepTime == -1:
 152             self.SetStatusText("Invalid number entered or dialog cancelled.")
 153         else:
 154             self.Initialize()
 155 
 156 
 157     def OnPlay(self, event):
 158         self.Initialize()
 159         self.SetStatusText('Playing')
 160         self.Move(0, 2, 1, self.numDiscs)
 161         self.SetStatusText('Finished: %s Moves' % self.moves)
 162 
 163 
 164     def OnReset(self, event):
 165         self.Initialize()
 166 
 167 
 168     def DrawDisc(self, dc, disc, x, y):
 169         assert x <= 2
 170         dc.SetPen(self.pen)
 171         dc.SetBrush(disc.brush)
 172         dc.DrawRectangle(self.xpos[x] - (disc.width / 2),
 173                          self.height - (y * self.discheight),
 174                          disc.width,
 175                          disc.height)
 176 
 177 
 178     def Draw(self, dc, width, height):
 179         mdc = wx.MemoryDC()
 180         mdc.SelectObject(wx.Bitmap(width, height))
 181         for x, peg in enumerate(self.pegs):
 182             for y, disc in enumerate(peg):
 183                 self.DrawDisc(mdc, disc, x, y+1)
 184         dc.Blit(0, 0, width, height, mdc, 0, 0)
 185 
 186 
 187     def OnPaint(self, event):
 188         width, height = self.GetClientSize()
 189         self.Draw(wx.PaintDC(self), width, height)
 190 
 191 
 192     def MoveDisc(self, src, dst):
 193         disc = self.pegs[src].pop()
 194         self.pegs[dst].append(disc)
 195         self.moves += 1
 196         self.SetStatusText('Move %s' % self.moves)
 197         width, height = self.GetClientSize()
 198         self.Draw(wx.ClientDC(self), width, height)
 199         wx.MilliSleep(10 * self.sleepTime)
 200 
 201 
 202     def Move(self, src, dst, temp, n):
 203         if n == 1:
 204             self.MoveDisc(src, dst)
 205         else:
 206             self.Move(src, temp, dst, n-1)
 207             self.MoveDisc(src, dst)
 208             self.Move(temp, dst, src, n-1)
 209 
 210 #---------------------------------------------------------------------------
 211 
 212 class wxHanoiApp(wx.App):
 213     """
 214     ...
 215     """
 216     def OnInit(self):
 217 
 218         #------------
 219 
 220         self.frame = wxHanoiFrame('Towers of Hanoi',
 221                                   wx.Size(450, 350))
 222         self.SetTopWindow(self.frame)
 223 
 224         return True
 225 
 226 #---------------------------------------------------------------------------
 227 
 228 def main():
 229     app = wxHanoiApp()
 230     app.MainLoop()
 231 
 232 #---------------------------------------------------------------------------
 233 
 234 if __name__ == '__main__':
 235     main()


Second example

img_towers_of_hanoi_2.png

Readme file : hanoi1_README.md

License file : hanoi1_LICENSE.md

   1 # towers_of_hanoi_bis.py
   2 
   3 """
   4 The Towers of Hanoi
   5 
   6 Author: A. Polino
   7 Link : https://code.activestate.com/recipes/577511-hanoi-towers-solver-wxpython/
   8 """
   9 
  10 import wx
  11 import sys
  12 
  13 # def Gen_hanoi
  14 # def Create_plates
  15 # class Plate
  16 # class HanoiWindow
  17 # class HanoiFrame
  18 # class HanoiApp
  19 # class
  20 
  21 #---------------------------------------------------------------------------
  22 
  23 ## Make sure the windows is focused than press any button, and the program will move a plate. keep  ## pressing until the problem is solved
  24 ## by default it will display 5 plates. To change this, you have to call the script with a second   ## argument, wich is the number of plates (max 10)
  25 
  26 def Gen_hanoi(stack, start=1, temp=2, goal=3):
  27     """
  28     ...
  29     """
  30 
  31     if stack == 2:
  32         yield start, temp
  33         yield start, goal
  34         yield temp, goal
  35 
  36     else:
  37         for x in Gen_hanoi(stack - 1, start, goal, temp):
  38             yield x
  39         yield start, goal
  40 
  41         for x in Gen_hanoi(stack - 1, temp, start, goal):
  42             yield x
  43 
  44 #---------------------------------------------------------------------------
  45 
  46 def Create_plates(num):
  47     """
  48     ...
  49     """
  50 
  51     assert num <= 10
  52 
  53     x_start = 10
  54     x_len = 100
  55     plates = []
  56 
  57     for x in range(num):
  58         plates.append(Plate(x_len, x_start))
  59         x_len -= 10
  60         x_start += 5
  61 
  62     return plates
  63 
  64 #---------------------------------------------------------------------------
  65 
  66 class Plate(object):
  67     """
  68     ...
  69     """
  70     def __init__(self, x_len, x_start):
  71 
  72         #------------
  73 
  74         self.x_len = x_len
  75         self.x_start = x_start
  76 
  77 #---------------------------------------------------------------------------
  78 
  79 class HanoiWindow(wx.Window):
  80     """
  81     ...
  82     """
  83     def __init__(self, parent, num):
  84         wx.Window.__init__(self, parent, id=-1, pos=wx.Point(0, 0),
  85                            size=wx.DefaultSize, style=wx.SUNKEN_BORDER|
  86                            wx.WANTS_CHARS|wx.FULL_REPAINT_ON_RESIZE)
  87 
  88         #------------
  89 
  90         self.SetBackgroundColour(wx.Colour('#eceade'))
  91 
  92         #------------
  93 
  94         self.BindEvents()
  95 
  96         #------------
  97 
  98         self.towers = [Create_plates(num), [], []]
  99         self.solver = Gen_hanoi(num)
 100 
 101     #-----------------------------------------------------------------------
 102 
 103     def BindEvents(self):
 104         self.Bind(wx.EVT_PAINT, self.OnPaint)
 105         self.Bind(wx.EVT_KEY_UP, self.OnKeyUp)
 106 
 107 
 108     def OnPaint(self, evt):
 109         def draw_rect(x_len, x_start, y_start):
 110             dc = wx.PaintDC(self)
 111             font = dc.GetFont()
 112             font.SetPointSize(15)
 113             dc.SetFont(font)
 114             size, colour = 2, wx.Colour('black')
 115             dc.SetPen(wx.Pen(colour, size, wx.SOLID))
 116             point = wx.Point(x_start, y_start)
 117             dc.DrawLines([point, point + wx.Point(x_len, 0)])
 118             dc.DrawLines([point - wx.Point(0, 5), point + wx.Point(x_len, 0) - wx.Point(0, 5)])
 119             dc.DrawLines([point, point - wx.Point(0, 5)])
 120             dc.DrawLines([point + wx.Point(x_len, 0), point - wx.Point(0, 5) + wx.Point(x_len, 0)])
 121 
 122         w, h = self.GetClientSize()
 123         buffer = wx.Bitmap(w, h)
 124         dc = wx.PaintDC(self)
 125         font = dc.GetFont()
 126         font.SetPointSize(15)
 127         dc.SetFont(font)
 128         msg = 'Hanoi Towers'
 129         w, h = dc.GetTextExtent(msg)
 130         dc.DrawText(msg, 200, 20)
 131         size, colour = 8, wx.Colour('black')
 132         dc.SetPen(wx.Pen(colour, size, wx.SOLID))
 133         for num in range(len(self.towers)):
 134             y_start = 300
 135             tower = self.towers[num]
 136             num = num * 200 + 10
 137             point = wx.Point(num, y_start)
 138             dc.DrawLines([point, point + wx.Point(120, 0)]) # Base
 139             # Plates
 140             for plate in tower:
 141                 y_start -= 10
 142                 draw_rect(plate.x_len, num + plate.x_start, y_start)
 143 
 144 
 145     def OnKeyUp(self, evt):
 146         try:
 147             from_, to = next(self.solver)
 148             self.towers[to-1].append(self.towers[from_-1].pop())
 149             self.Refresh()
 150         except StopIteration:
 151             wx.MessageBox('Problem Solved !', 'Problem solved', wx.OK)
 152 
 153 #---------------------------------------------------------------------------
 154 
 155 class HanoiFrame(wx.Frame):
 156     """
 157     ...
 158     """
 159     def __init__(self, title, num):
 160         style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER ^ wx.MAXIMIZE_BOX
 161         wx.Frame.__init__(self, parent=None, id=-1,
 162                           title=title, size=(600, 500),
 163                           pos=(200, 200), style=style)
 164 
 165         #------------
 166 
 167         self.SetIcon(wx.Icon(".\icons\wxwin.ico"))
 168 
 169         #------------
 170 
 171         self.Window = HanoiWindow(self, num)
 172 
 173         #------------
 174 
 175         self.BindEvents()
 176 
 177         #------------
 178 
 179         self.CenterOnScreen(wx.BOTH)
 180         self.Show(True)
 181 
 182     #-----------------------------------------------------------------------
 183 
 184     def BindEvents(self):
 185         self.Bind(wx.EVT_CLOSE, self.close_frame)
 186 
 187 
 188     def close_frame(self, evt):
 189         # sys.exit(0)
 190         self.Destroy()
 191 
 192 #---------------------------------------------------------------------------
 193 
 194 class HanoiApp(wx.App):
 195     """
 196     ...
 197     """
 198     def OnInit(self):
 199         if len(sys.argv) < 2:
 200             num = 5
 201         else:
 202             num = int(sys.argv[1])
 203 
 204         #------------
 205 
 206         hano = HanoiFrame('Hanoi Towers', num)
 207         self.SetTopWindow(hano)
 208 
 209         return True
 210 
 211 #---------------------------------------------------------------------------
 212 
 213 def main():
 214     app = HanoiApp()
 215     app.MainLoop()
 216 
 217 #---------------------------------------------------------------------------
 218 
 219 if __name__ == '__main__':
 220     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

Jan Bodnar (tetris.py coding), Hanming Zhang (tetris_bis.py coding), Zoenberger (memory_game.py coding), Hatenablog (reversi.py coding), S. Foster (towers_of_hanoi.py coding), A. Polino (towers_of_hanoi_bis.py coding), ActiveState, the wxPython community...


About this page

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

19/07/20 - Ecco (Created page for wxPython Phoenix).


Comments

- blah, blah, blah...

How to create a game for wxPython - Part 1 (Phoenix) (last edited 2020-10-25 16:43:58 by Ecco)

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