def endGame(self, success): """ Finish the game. """ self.gameOver = True # avoids recursion issues during cleardown # clear down the grid for button in self.buttons: if button.exposed: continue elif button.flagged: if button.mined: # button correctly flagged button.set_sensitive(False) else: # incorrect flag - remove it button.flagged = False button.set_active(True) self.updateFlags(False) button.leftMouse(button) else: # 'press' the button button.leftMouse(button) # update the start button image if success: updateImage(self.parent.startImage, 'Win', TOOL_SIZE) else: updateImage(self.parent.startImage, 'Lose', TOOL_SIZE) # update the sensitivity of the other toolbar buttons self.parent.hintButton.set_sensitive(False) self.parent.pbcButton.set_sensitive(True) self.parent.configurationBox.set_button_sensitivity( Gtk.SensitivityType.ON)
def resize(self, imageSize): """ Resize the grid button's child (image or text) to match the current window size. (The button looks after itself.) """ self.imageSize = imageSize updateImage(self.image, self.imageKey, self.imageSize)
def on_button_press_event(self, widget, event): """ Event handler for button presses. The handler must decide which mouse button was clicked. We are only interested in leftMouse clicks. """ # determine left- or right- click, feed the appropriate method. if event.get_button()[1] == 1: # left-mouse updateImage(self.parent.parent.startImage, 'Click', TOOL_SIZE)
def on_pbcButton_toggled(self, widget): """ Handler for the periodic boundary condition button. """ if self.pbcButton.get_active(): updateImage(self.pbcImage, 'PBC_On', TOOL_SIZE) else: updateImage(self.pbcImage, 'PBC_Off', TOOL_SIZE)
def setupStatusBarIcon(self, iconName, imageName): """ Add an icon image to a status bar button. """ icon = self.builder.get_object(iconName) image = getImage(imageName) updateImage(image, imageName, STATUS_SIZE) icon.add(image) return icon
def start(self): """ Start a new game. """ self.saveConfiguration() # reset the start button image updateImage(self.startImage, 'Start', TOOL_SIZE) # get the grid information from the configuration box. activeConfiguration = self.configurationBox.get_active_iter() cols, rows, nMines = tuple(self.configurations[activeConfiguration])[2:] mines = [True] * nMines mines.extend([False] * (cols * rows - nMines)) random.shuffle(mines) # reset the status bar self.exposedCount.set_text('0') self.exposedLabel.set_text('/ ' + str(cols * rows - nMines)) self.hintCount.set_text('0') self.flagCount.set_text('0') self.flagLabel.set_text('/ ' + str(nMines)) # destroy any pre-existing game if self.gameGrid != None: self.gameGrid.destroy() # 'force' re-size at the start of the game self.previousAllocation = None # make the new game self.gameGrid = GridWindow(parent=self, cols=cols, rows=rows, mines=mines) self.gridContainer.add_with_viewport(self.gameGrid) # configure the toolbar widgets sensitivity self.hintButton.set_sensitive(True) # enable hints during a game self.pbcButton.set_sensitive(False) # can't change pbc during a game self.configurationBox.set_button_sensitivity(Gtk.SensitivityType.OFF) # start the game self.gameGrid.start() self.gameGrid.giveHint() self.window.show_all()
def __init__(self, parent=None, pos=None, mined=False): """ Do the class initialization and prepare the game-specific attributes. """ super().__init__() # the game grid parent self.parent = parent # give the button a name for css styling self.set_name("gridButton") # this button's grid position. self.pos = pos # is this button mined? self.mined = mined self.exploded = False # is this button flagged? - initialize to False self.flagged = False # initialize the mine count to zero self.neighbourMines = 0 # initialize the neighbour flag count to zero self.neighbourFlags = 0 # flag to emulate disabled when mines are exposed self.exposed = False # initialize the image to empty at 20 pixels self.imageKey = 'Empty' self.imageSize = GRID_SIZE self.image = getImage(self.imageKey) self.add(self.image) updateImage(self.image, self.imageKey, self.imageSize) # set up the GTK event handlers self.connect("button_press_event", self.on_button_press_event) self.connect("button_release_event", self.on_button_release_event)
def explodeGame(self): """ Make an explosion effect on the screen as the grid is destroyed. """ # obtain the size of the playing area size = (self.gridContainer.get_allocated_width(), self.gridContainer.get_allocated_height()) # make the explosion flashImage flashImage = getImage('Explosion') updateImage(flashImage, 'Explosion', size) self.gridContainer.add_with_viewport(flashImage) # flash the explosion 5 times for i in range(5): # display the explosion updateImage(self.startImage, 'Click', TOOL_SIZE) flashImage.show() pause(100) # wait 200ms without blocking the Gtk event loop # hide the explosion updateImage(self.startImage, 'Lose', TOOL_SIZE) flashImage.hide() pause(100) # wait 200ms without blocking the Gtk event loop # ... then destroy the explosion image flashImage.destroy()
def rightMouse(self): """ Right-Mouse handler. We use this to toggle mine flags. """ # action exclusions if not self.get_sensitive(): return if self.exposed: return # toggle the flag state self.flagged = not self.flagged self.set_active(self.flagged) # update the button image if self.flagged: self.imageKey = 'Flag' else: self.imageKey = 'Empty' updateImage(self.image, self.imageKey, self.imageSize) # notify neighbours and parent of the change self.parent.updateFlags(self.flagged) for neighbour in self.neighbourList: neighbour.updateNeighbourFlags(self.flagged)
def leftMouse(self, widget): """ Left-Mouse handler. We use this to clear the area. """ # action exclusions if self.flagged: if widget == self and not self.parent.exploded: self.set_active(False) # TODO: not sure why this works updateImage(self.parent.parent.startImage, 'Start', TOOL_SIZE) return False if self.exposed or self.exploded: return False # disable the button and change its colour once it has been left-clicked self.set_sensitive(False) if not self.parent.exploded: self.set_active(True) exposedNeighbours = 0 # end game - we hit a mine - lose if self.mined: self.exploded = True # choose the image if self.parent.exploded: # cleardown mode self.imageKey = 'UXB' else: # normal mode self.set_active(True) self.imageKey = 'Explosion' # lose updateImage(self.image, self.imageKey, self.imageSize) # short pause to let gtk events sort themselves out if not self.parent.exploded: pause(200) self.parent.exploded = True # notify end-game else: # expose the button, display the number of neighbour mines self.exposed = True # update the image self.imageKey = 'Empty' if self.neighbourMines > 0: self.imageKey = str(self.neighbourMines) updateImage(self.image, self.imageKey, self.imageSize) # propagate exposure to the neighbours if mines = flags if self.neighbourFlags == self.neighbourMines: for neighbour in self.neighbourList: exposedNeighbours += neighbour.leftMouse(widget) # update count of exposed buttons - potential win end game exposedNeighbours += 1 # add self to the count if widget == self: self.parent.incrementExposedCount(exposedNeighbours) # reset the start button image if not self.parent.gameOver: updateImage(self.parent.parent.startImage, 'Start', TOOL_SIZE) else: return exposedNeighbours
def initializeGUI(self): """ Load the main GUI elements from the .glade file. """ self.builder = Gtk.Builder() self.builder.add_from_file(UI_BUILD_FILE) self.builder.connect_signals(self) # these are the toolbar widgets... # game start self.startButton = self.builder.get_object('startButton') self.startImage = getImage('Start') updateImage(self.startImage, 'Start', TOOL_SIZE) self.startButton.add(self.startImage) # hint request self.hintButton = self.builder.get_object('hintButton') self.hintImage = getImage('Hint') updateImage(self.hintImage, 'Hint', TOOL_SIZE) self.hintButton.add(self.hintImage) self.hintButton.set_sensitive(False) # periodic boundary condition toggle self.pbcButton = self.builder.get_object('pbcButton') self.pbcImage = getImage('PBC_Off') updateImage(self.pbcImage, 'PBC_Off', TOOL_SIZE) self.pbcButton.add(self.pbcImage) self.pbcButton.set_sensitive(True) # the configurationBox and its model self.configurations = self.builder.get_object('configurations') self.configurationBox = self.builder.get_object('configurationBox') # an alternative quit button self.resetButton = self.builder.get_object('resetButton') self.resetImage = getImage('Reset') updateImage(self.resetImage, 'Reset', TOOL_SIZE) self.resetButton.add(self.resetImage) # these are the status bar widgets... # ...exposed count self.exposedIcon = self.setupStatusBarIcon('exposedIcon', 'Exposed') self.exposedCount = self.builder.get_object('exposedCount') self.exposedLabel = self.builder.get_object('exposedLabel') # ...hint count self.hintIcon = self.setupStatusBarIcon('hintIcon', 'Query') self.hintCount = self.builder.get_object('hintCount') # ...flag count self.flagIcon = self.setupStatusBarIcon('flagIcon', 'Flag') self.flagCount = self.builder.get_object('flagCount') self.flagLabel = self.builder.get_object('flagLabel') # the game grid (blank for now) self.gridContainer = self.builder.get_object('gridContainer') self.gameGrid = None self.previousAllocation = self.gridContainer.get_allocation() # get references to the toolbar and status bar for size data. self.toolbar = self.builder.get_object('toolBox') self.statusbar = self.builder.get_object('statusBox') # get a reference to the main window itself and display the window self.window = self.builder.get_object('window')