import wx
from random import random
bombsID_NEWGAME = wx.ID_HIGHEST
bombsID_EASY = wx.ID_HIGHEST + 1
bombsID_MEDIUM = wx.ID_HIGHEST + 2
bombsID_HARD = wx.ID_HIGHEST + 3
BG_HIDDEN = 0x100
BG_BOMB = 0x200
BG_MARKED = 0x400
BG_EXPLODED = 0x800
BG_MASK = 0x0FF
PROB = 0.2
X_UNIT = 4
Y_UNIT = 4
class BombsGame:
def __init__ (self):
self.m_width = 0
self.m_height = 0
self.m_field = 0
def Init (self, aWidth, aHeight):
self.m_gridFocusX = -1
self.m_gridFocusY = -1
try:
if self.m_field:
self.m_field = []
except:
pass
self.m_field = aWidth * aHeight*[0]
self.m_width = aWidth
self.m_height = aHeight
for x in range (0, self.m_width):
for y in range (0, self.m_height):
if random() < PROB:
self.m_field [x + y * self.m_width] = BG_HIDDEN | BG_BOMB
else:
self.m_field [x + y * self.m_width] = BG_HIDDEN
self.m_numBombCells = 0
for x in range (0, self.m_width):
for y in range (0, self.m_height):
if self.m_field [x + y * self.m_width] & BG_BOMB:
self.m_numBombCells += 1
for xx in range (x-1, x + 2):
if xx >= 0 and xx < self.m_width:
for yy in range (y - 1, y + 2):
if yy >= 0 and yy < self.m_height and (yy != y or xx != x):
self.m_field [xx + yy * self.m_width] += 1
self.m_numRemainingCells = self.m_height * self.m_width - self.m_numBombCells
return True
def GetWidth (self):
return self.m_width
def GetHeight (self):
return self.m_height
def Get (self, x, y):
return self.m_field [x + y * self.m_width]
def IsFocussed (self, x, y):
return self.m_gridFocusX == x and self.m_gridFocusY == y
def IsHidden (self, x, y):
return self.Get (x, y) & BG_HIDDEN
def IsMarked (self, x, y):
return self.Get (x, y) & BG_MARKED
def IsBomb (self, x, y):
return self.Get (x, y) & BG_BOMB
def IsExploded (self, x, y):
return self.Get (x, y) & BG_EXPLODED
def GetNumBombs(self):
return self.m_numBombCells
def GetNumRemainingCells(self):
return self.m_numRemainingCells
# Marks/unmarks a cell
def Mark(self, x, y):
self.m_field [x + y * self.m_width] ^= BG_MARKED
# Unhides a cell
def Unhide(self, x, y):
if not self.IsHidden(x,y):
return
self.m_field [x + y * self.m_width] &= ~BG_HIDDEN
if not self.IsBomb(x,y):
self.m_numRemainingCells -= 1
# Makes a cell exploded
def Explode (self, x, y):
self.m_field [x + y * self.m_width] |= BG_EXPLODED
class BombsCanvas (wx.Panel):
def __init__(self, parent, game):
wx.Panel.__init__(self, parent, wx.ID_ANY)
# Cell size in pixels
self.m_game = game
dc = wx.ClientDC (self)
m_game = game
dc = wx.ClientDC (self)
font = wx.Font(14, wx.ROMAN, wx.NORMAL, wx.NORMAL)
dc.SetFont(font)
buf = "M"
chw, chh = dc.GetTextExtent(buf)
dc.SetFont(wx.NullFont)
dc.SetMapMode(wx.MM_METRIC)
xcm = dc.LogicalToDeviceX(10)
ycm = dc.LogicalToDeviceY(10)
# To have a square cell, there must be :
# sx*ycm == sy*xcm
if chw * ycm < chh * xcm:
sy = chh
sx = chh * xcm / ycm
else:
sx = chw
sy = chw * ycm / xcm
self.m_cellWidth = (sx + 3 + X_UNIT) / X_UNIT
self.m_cellHeight = (sy + 3 + Y_UNIT) / Y_UNIT
dc.SetMapMode(wx.MM_TEXT)
self.m_bmp = 0
self.Bind (wx.EVT_PAINT, self.OnPaint)
self.Bind (wx.EVT_MOUSE_EVENTS, self.OnMouseEvent)
self.Bind (wx.EVT_CHAR, self.OnChar)
def UpdateGridSize(self):
if self.m_bmp:
self.m_bmp = None
self.Refresh()
def GetGridSizeInPixels(self):
return wx.Size(self.m_cellWidth * X_UNIT * self.m_game.GetWidth(), self.m_cellHeight * Y_UNIT * self.m_game.GetHeight())
def OnPaint(self, event):
dc = wx.PaintDC (self)
numHorzCells = self.m_game.GetWidth()
numVertCells = self.m_game.GetHeight()
#// Insert your drawing code here.
if not self.m_bmp:
size = dc.GetSize()
self.m_bmp = wx.EmptyBitmap (size.GetWidth(), size.GetHeight())
if self.m_bmp:
memDC = wx.MemoryDC()
memDC.SelectObject(self.m_bmp)
self.DrawField(memDC, 0, 0, numHorzCells - 1, numVertCells - 1)
memDC.SelectObject(wx.NullBitmap)
if self.m_bmp:
memDC = wx.MemoryDC()
memDC.SelectObject(self.m_bmp)
size = dc.GetSize()
dc.Blit(0, 0, size.GetWidth(), size.GetHeight(), memDC, 0, 0, wx.COPY)
memDC.SelectObject(wx.NullBitmap)
else:
self.DrawField(dc, 0, 0, numHorzCells - 1, numVertCells - 1)
def DrawField(self, dc, xc1, yc1, xc2, yc2):
buf = ""
wx.Black = wx.TheColourDatabase.Find("BLACK")
wx.White = wx.TheColourDatabase.Find("WHITE")
wx.Red = wx.TheColourDatabase.Find("RED")
wx.Blue = wx.TheColourDatabase.Find("BLUE")
wx.Grey = wx.TheColourDatabase.Find("LIGHT GREY")
wx.Focused = wx.TheColourDatabase.Find("GREY")
wx.Green = wx.TheColourDatabase.Find("GREEN")
blackPen = wx.ThePenList.FindOrCreatePen(wx.Black, 1, wx.SOLID)
redPen = wx.ThePenList.FindOrCreatePen(wx.Red, 1, wx.SOLID)
bluePen = wx.ThePenList.FindOrCreatePen(wx.Blue, 1, wx.SOLID)
whiteBrush = wx.TheBrushList.FindOrCreateBrush(wx.White, wx.SOLID)
greyBrush = wx.TheBrushList.FindOrCreateBrush(wx.Grey, wx.SOLID)
focusedBrush = wx.TheBrushList.FindOrCreateBrush(wx.Focused, wx.SOLID)
redBrush = wx.TheBrushList.FindOrCreateBrush(wx.Red, wx.SOLID)
dc.SetPen (blackPen)
xMax = self.GetGridSizeInPixels().GetWidth()
yMax = self.GetGridSizeInPixels().GetHeight()
for x in range (xc1, xc2 + 1):
dc.DrawLine(x * self.m_cellWidth * X_UNIT, 0, x * self.m_cellWidth * X_UNIT, yMax)
for y in range (yc1, yc2 + 1):
dc.DrawLine(0, y * self.m_cellHeight * Y_UNIT, xMax, y * self.m_cellHeight *Y_UNIT)
font = wx.Font(14, wx.ROMAN, wx.NORMAL, wx.NORMAL)
dc.SetFont(font)
for x in range(xc1, xc2 + 1):
for y in range(yc1, yc2 + 1):
if self.m_game.IsMarked(x,y):
dc.SetPen (blackPen)
if self.m_game.IsFocussed(x, y):
dc.SetBrush (focusedBrush)
else:
dc.SetBrush (greyBrush)
dc.DrawRectangle (x * self.m_cellWidth * X_UNIT, y * self.m_cellHeight * Y_UNIT, self.m_cellWidth * X_UNIT + 1, self.m_cellHeight * Y_UNIT + 1)
buf = "M"
if not self.m_game.IsHidden (x, y) and self.m_game.IsBomb(x,y):
dc.SetTextForeground(wx.Blue)
else:
dc.SetTextForeground(wx.Red)
dc.SetTextBackground(wx.Grey)
chw, chh = dc.GetTextExtent(buf)
dc.DrawText (buf, x * self.m_cellWidth * X_UNIT + (self.m_cellWidth * X_UNIT-chw) / 2, y * self.m_cellHeight * Y_UNIT + (self.m_cellHeight * Y_UNIT - chh) / 2 )
if not self.m_game.IsHidden(x,y) and self.m_game.IsBomb(x,y):
dc.SetPen (redPen)
dc.DrawLine (x * self.m_cellWidth * X_UNIT, y * self.m_cellHeight * Y_UNIT, (x + 1) * self.m_cellWidth * X_UNIT, (y + 1) * self.m_cellHeight * Y_UNIT)
dc.DrawLine (x * self.m_cellWidth * X_UNIT, (y + 1) * self.m_cellHeight * Y_UNIT, (x + 1) * self.m_cellWidth * X_UNIT, y * self.m_cellHeight * Y_UNIT)
elif self.m_game.IsHidden (x, y):
dc.SetPen (blackPen)
if self.m_game.IsFocussed (x, y):
dc.SetBrush (focusedBrush)
else:
dc.SetBrush (greyBrush)
dc.DrawRectangle (x * self.m_cellWidth * X_UNIT, y * self.m_cellHeight * Y_UNIT, self.m_cellWidth * X_UNIT+1, self.m_cellHeight * Y_UNIT + 1)
elif self.m_game.IsBomb(x,y):
dc.SetPen (blackPen)
dc.SetBrush (redBrush)
dc.DrawRectangle (x * self.m_cellWidth * X_UNIT, y * self.m_cellHeight * Y_UNIT, self.m_cellWidth * X_UNIT + 1, self.m_cellHeight * Y_UNIT + 1)
buf = "B"
dc.SetTextForeground (wx.Black)
dc.SetTextBackground (wx.Red)
chw, chh = dc.GetTextExtent (buf)
dc.DrawText (buf, x * self.m_cellWidth * X_UNIT + (self.m_cellWidth * X_UNIT - chw) / 2, y * self.m_cellHeight * Y_UNIT + (self.m_cellHeight * Y_UNIT - chh) / 2)
if self.m_game.IsExploded (x, y):
dc.SetPen (bluePen)
dc.DrawLine (x * self.m_cellWidth * X_UNIT, y * self.m_cellHeight * Y_UNIT, (x + 1) * self.m_cellWidth * X_UNIT, (y + 1) * self.m_cellHeight * Y_UNIT)
dc.DrawLine (x * self.m_cellWidth * X_UNIT, (y + 1) * self.m_cellHeight * Y_UNIT, (x + 1) * self.m_cellWidth * X_UNIT, y * self.m_cellHeight * Y_UNIT)
else: # Display a digit
dc.SetPen (blackPen)
if self.m_game.IsFocussed (x, y):
dc.SetBrush (focusedBrush)
else:
dc.SetBrush (whiteBrush)
dc.DrawRectangle (x * self.m_cellWidth * X_UNIT, y * self.m_cellHeight * Y_UNIT, self.m_cellWidth * X_UNIT + 1, self.m_cellHeight * Y_UNIT + 1)
digit_value = self.m_game.Get (x, y) & BG_MASK
if digit_value == 0:
buf = "0"
dc.SetTextForeground (wx.Green)
elif digit_value == 1:
buf = "1"
dc.SetTextForeground (wx.Blue)
else:
buf = "%d" % digit_value
dc.SetTextForeground (wx.Black)
chw, chh = dc.GetTextExtent (buf)
dc.SetTextBackground(wx.White)
dc.DrawText (buf, x * self.m_cellWidth * X_UNIT + (self.m_cellWidth * X_UNIT-chw) / 2, y * self.m_cellHeight * Y_UNIT + (self.m_cellHeight * Y_UNIT-chh) / 2)
dc.SetFont (wx.NullFont)
wx.LogStatus ("%d bombs %d remaining cells" % (self.m_game.GetNumBombs(), self.m_game.GetNumRemainingCells()))
def RefreshField(self, xc1, yc1, xc2, yc2):
dc = wx.ClientDC (self)
self.DrawField (dc, xc1, yc1, xc2, yc2)
if self.m_bmp:
memDC = wx.MemoryDC()
memDC.SelectObject (self.m_bmp)
self.DrawField (memDC, xc1, yc1, xc2, yc2)
memDC.SelectObject(wx.NullBitmap)
def Uncover(self, x, y):
self.m_game.Unhide (x, y)
self.RefreshField (x, y, x, y)
gridWidth = self.m_game.GetWidth()
gridHeight = self.m_game.GetHeight()
hasWon = self.m_game.GetNumRemainingCells() == 0
if self.m_game.IsBomb (x,y) or hasWon:
wx.Bell()
if hasWon:
wx.MessageBox("Nice! You found all the bombs!", "wxWin Bombs", wx.OK | wx.CENTRE)
else: # x,y is a bomb
self.m_game.Explode(x, y)
for x in range (0, gridWidth):
for y in range (0, gridHeight):
self.m_game.Unhide (x, y)
self.RefreshField (0, 0, gridWidth - 1, gridHeight - 1)
elif not self.m_game.Get (x, y):
left = 0
if x > 0:
left = x-1
right = gridWidth - 1
if x < gridWidth - 1:
right = x + 1
top = 0
if y > 0:
top = y - 1
bottom = gridHeight - 1
if y < gridHeight - 1:
bottom = y + 1
for j in range (top, bottom + 1):
for i in range (left, right + 1):
if (i != x or j != y) and self.m_game.IsHidden (i, j) and not self.m_game.IsMarked(i, j):
self.Uncover (i, j)
def OnMouseEvent(self, event):
gridWidth = self.m_game.GetWidth()
gridHeight = self.m_game.GetHeight()
#wx.Coord fx, fy
fx, fy = event.GetPosition()
x = fx / (self.m_cellWidth * X_UNIT)
y = fy / (self.m_cellHeight * Y_UNIT)
if x < gridWidth and y < gridHeight:
if ((event.RightDown() or (event.LeftDown() and event.ShiftDown())) or (self.m_game.IsHidden(x,y) and not self.m_game.GetNumRemainingCells())):
#// store previous and current field
prevFocusX = self.m_game.m_gridFocusX
prevFocusY = self.m_game.m_gridFocusY
self.m_game.m_gridFocusX = x
self.m_game.m_gridFocusY = y
self.RefreshField (prevFocusX, prevFocusY, prevFocusX, prevFocusY)
self.m_game.Mark (x, y)
self.RefreshField (x, y, x, y)
return
elif event.LeftDown() and self.m_game.IsHidden(x,y) and not self.m_game.IsMarked(x,y):
#// store previous and current field
prevGridFocusX = self.m_game.m_gridFocusX
prevGridFocusY = self.m_game.m_gridFocusY
self.m_game.m_gridFocusX = x
self.m_game.m_gridFocusY = y
self.RefreshField (prevGridFocusX, prevGridFocusY, prevGridFocusX, prevGridFocusY)
self.Uncover(x, y)
return
def OnChar(self, event):
keyCode = event.GetKeyCode()
prevGridFocusX = self.m_game.m_gridFocusX
prevGridFocusY = self.m_game.m_gridFocusY
gridWidth = self.m_game.GetWidth()
gridHeight = self.m_game.GetHeight()
if keyCode == wx.WXK_RIGHT:
self.m_game.m_gridFocusX += 1
if self.m_game.m_gridFocusX >= gridWidth:
self.m_game.m_gridFocusX = 0
elif keyCode == wx.WXK_LEFT:
self.m_game.m_gridFocusX -= 1
if self.m_game.m_gridFocusX < 0:
self.m_game.m_gridFocusX = gridWidth - 1
elif keyCode == wx.WXK_DOWN:
self.m_game.m_gridFocusY += 1
if self.m_game.m_gridFocusY >= gridHeight:
m_game.m_gridFocusY = 0
elif keyCode == wx.WXK_UP:
self.m_game.m_gridFocusY -= 1
if self.m_game.m_gridFocusY < 0:
self.m_game.m_gridFocusY = gridHeight - 1
elif keyCode == wx.WXK_RETURN:
if ((prevGridFocusX == self.m_game.m_gridFocusX) and (prevGridFocusY == self.m_game.m_gridFocusY) and (self.m_game.IsHidden (self.m_game.m_gridFocusX, self.m_game.m_gridFocusY))):
self.m_game.Mark (self.m_game.m_gridFocusX, self.m_game.m_gridFocusY)
if not self.m_game.IsMarked (self.m_game.m_gridFocusX, self.m_game.m_gridFocusY):
self.Uncover (self.m_game.m_gridFocusX, self.m_game.m_gridFocusY)
self.RefreshField (self.m_game.m_gridFocusX, self.m_game.m_gridFocusY, self.m_game.m_gridFocusX, self.m_game.m_gridFocusY)
else:
event.Skip()
if (prevGridFocusX != self.m_game.m_gridFocusX) or (prevGridFocusY != self.m_game.m_gridFocusY):
# cause focused field to be visible after first key hit after launching new game
if self.m_game.m_gridFocusX < 0:
self.m_game.m_gridFocusX = 0
if self.m_game.m_gridFocusY < 0:
self.m_game.m_gridFocusY = 0
# refresh previous field and focused field
self.RefreshField (prevGridFocusX, prevGridFocusY, prevGridFocusX, prevGridFocusY)
self.RefreshField (self.m_game.m_gridFocusX, self.m_game.m_gridFocusY, self.m_game.m_gridFocusX, self.m_game.m_gridFocusY)
class BombsFrame (wx.Frame):
def __init__(self, game, parent = None):
wx.Frame.__init__(self, None, wx.ID_ANY, "wxBombs", wx.DefaultPosition, wx.Size(300, 300), wx.DEFAULT_DIALOG_STYLE | wx.MINIMIZE_BOX)
self.m_game = game
if parent:
try:
self.SetIcon(wx.Icon(parent.prefs.pluginsdirectory + "/bitmaps/bombs.ico", wx.BITMAP_TYPE_ICO))
except:
pass
else:
try:
self.SetIcon(wx.Icon('bombs.ico', wx.BITMAP_TYPE_ICO))
except:
pass
self.CreateStatusBar()
# Create a menu bar for the frame
self.menuBar = wx.MenuBar()
self.menuFile = wx.Menu()
self.menuLevel = wx.Menu()
self.menuLevel.AppendRadioItem(bombsID_EASY, "&Easy (10x10)\tCtrl-1")
self.menuLevel.AppendRadioItem(bombsID_MEDIUM, "&Medium (15x15)\tCtrl-2")
self.menuLevel.AppendRadioItem(bombsID_HARD, "&Hard (25x20)\tCtrl-3")
self.menuFile.AppendMenu(bombsID_NEWGAME, "&New Game", self.menuLevel, "Starts a new game")
self.menuFile.AppendSeparator()
self.menuFile.Append(wx.ID_EXIT, "E&xit", "Quits the application")
self.menuBar.Append(self.menuFile, "&File")
self.menuHelp = wx.Menu()
self.menuHelp.Append (wx.ID_ABOUT, "&About", "Displays the program information")
self.menuBar.Append(self.menuHelp, "&Help")
self.SetMenuBar(self.menuBar)
# Create child subwindows.
self.m_canvas = BombsCanvas(self, self.m_game)
# Ensure the subwindows get resized o.k.
# OnSize(width, height)
# Centre frame on the screen.
self.Centre(wx.BOTH)
# Show the frame.
self.Show()
self.Bind(wx.EVT_MENU, self.OnNewEasyGame, id = bombsID_EASY)
self.Bind(wx.EVT_MENU, self.OnNewMediumGame, id = bombsID_MEDIUM)
self.Bind(wx.EVT_MENU, self.OnNewHardGame, id = bombsID_HARD)
self.Bind(wx.EVT_MENU, self.OnExit, id = wx.ID_EXIT)
self.Bind(wx.EVT_MENU, self.OnAbout, id = wx.ID_ABOUT)
def NewGame(self, level, query):
if query:
ok = wx.MessageBox ("Start new game regardless previous board?", "Confirm", wx.YES_NO | wx.ICON_QUESTION, self)
if ok != wx.YES:
return
numHorzCells = 20
numVertCells = 20
if level == bombsID_EASY:
numHorzCells = 10
numVertCells = 10
elif level == bombsID_MEDIUM:
numHorzCells = 15
numVertCells = 15
elif level == bombsID_HARD:
numHorzCells = 25
numVertCells = 20
else:
wx.FAIL_MSG("Invalid level")
self.m_game.Init (numHorzCells, numVertCells)
self.GetMenuBar().Check(level, True)
self.m_canvas.UpdateGridSize()
self.SetClientSize(self.m_canvas.GetGridSizeInPixels())
def OnNewEasyGame(self, event):
self.NewGame(bombsID_EASY, True)
def OnNewMediumGame(self, event):
self.NewGame(bombsID_MEDIUM, True)
def OnNewHardGame(self, event):
self.NewGame(bombsID_HARD, True)
def OnExit(self, event):
self.Close()
def OnAbout(self, event):
wx.MessageBox("wxBombs (c) 1996 by P. Foggia\n<foggia@amalfi.dis.unina.it>\nTransmitted from wxWidgets to wxPython by Franz Steinhaeusler 2005", "About wxBombs")
class BombsApp (wx.App):
def OnInit(self, parent = None):
self.m_game = BombsGame()
self.m_frame = BombsFrame(self.m_game, parent)
self.m_frame.NewGame(bombsID_EASY, False)
return True
def Plugin(DrFrame):
def OnBombs (event):
m_game = BombsGame()
m_frame = BombsFrame(m_game, DrFrame)
m_frame.NewGame(bombsID_EASY, False)
try:
DrFrame.miscmenu
except:
#else create
DrFrame.miscmenu = wx.Menu()
if DrFrame.PLATFORM_IS_WIN:
menuBarName = "&Misc"
else:
menuBarName = "Misc"
DrFrame.menuBar.Insert(8, DrFrame.miscmenu, menuBarName)
ID_BOMBS = DrFrame.GetNewId()
DrFrame.Bind(wx.EVT_MENU, OnBombs, id = ID_BOMBS)
DrFrame.AddPluginShortcutFunction("Bombs", "Bombs", OnBombs)
DrFrame.AddPluginPopUpMenuFunction("Bombs", "Bombs", OnBombs)
DrFrame.LoadPluginShortcuts('Bombs')
DrFrame.miscmenu.Append(ID_BOMBS, DrFrame.GetPluginMenuLabel('Bombs', 'Bombs', 'Bombs'))
if __name__ == '__main__':
app = BombsApp(0)
app.MainLoop()