All pastes #556249 Raw Edit

fk

public python v1 · immutable
·published 2007-06-10 14:11 UTC
rendered paste body
# Copyright 2007 Firas Kraiem <fkraiem@enib.fr>. All rights reserved.## Redistribution and use in source and binary forms, with or without# modification, are permitted provided that the following conditions# are met :##    1.	Redistributions of source code must retain the above copyright#	notice, this list of conditions and the following disclaimer.#    2.	Redistributions in binary form must reproduce the above copyright#	notice, this list of conditions and the following disclaimer in the#	documentation and/or other materials provided with the distribution.## THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY# AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL# I BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF# THE POSSIBILITY OF SUCH DAMAGE.### This is a Python implementation of the classic Mastermind game# made by Firas Kraiem for a project at ENIB. Please read the# README file for further information. This code is licensed under# a 2-clause BSD-style license (see above).## This is the GUI version of the game. It needs PyGTK 2.0 or greater.# See the README for information about installing PyGTK.import pygtkpygtk.require('2.0')import gtkimport masterMind.mmBasefrom functions import *class Base :		gameStarted = False		def __init__(self) :				#first, we create the main window and a VBox in it				self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)				path = self.imagePath('newBackground.png')		self.backgroundPixbuf = gtk.gdk.pixbuf_new_from_file(path)		self.backgroundPixmap = self.backgroundPixbuf.render_pixmap_and_mask()[0]		del self.backgroundPixbuf		self.window.set_app_paintable(True)		self.window.resize(470, 590)		self.window.realize()		self.window.window.set_back_pixmap(self.backgroundPixmap, False)		del self.backgroundPixmap				self.window.set_title('Mastermind')		self.window.connect("delete_event", self.quitGame)		self.window.set_resizable(True)				self.box = gtk.VBox(False, 0)		self.window.add(self.box)				#Here come the menus :		#First, the File menu, with the New Game submenu and the Save and Quit items				self.menuItems = {}		self.menus = {}				self.menus['file'] = gtk.Menu()				for i in ['New Game', 'Quit'] :			self.menuItems[i] = gtk.MenuItem(i)			self.menus['file'].append(self.menuItems[i])			self.menuItems[i].show()					#The New Game submenu, with one item for each level				self.menus['newGame'] = gtk.Menu()				j = 1		for i in ['Beginner', 'Intermediate', 'Advanced', 'Expert'] :			self.menuItems[i] = gtk.MenuItem(i)			self.menuItems[i].connect_object('activate', self.startGameClicked, j)			self.menus['newGame'].append(self.menuItems[i])			self.menuItems[i].show()			j += 1				#The Help menu, with the Highscores and About items				self.menus['help'] = gtk.Menu()				for i in ['Highscores', 'About'] :			self.menuItems[i] = gtk.MenuItem(i)			self.menus['help'].append(self.menuItems[i])			self.menuItems[i].show()				self.menuItems['About'].connect_object('activate', self.oneButtonPopup, Base, 'about')		self.menuItems['Highscores'].connect_object('activate', self.oneButtonPopup, Base, 'highScores')		self.menuItems['Quit'].connect_object('activate', self.quitGame, None)				#And we pack the menus in the menu bar				self.menuBar = gtk.MenuBar()		self.box.pack_start(self.menuBar, False, False, 0)		self.menuBar.show()				for i in ['File', 'Help'] :			self.menuItems[i] = gtk.MenuItem(i)			self.menuItems[i].show()			self.menuItems[i].set_submenu(self.menus[i.lower()])			self.menuBar.append(self.menuItems[i])				self.menuItems['New Game'].set_submenu(self.menus['newGame'])				#then we make a HBox to contain the game board and buttons				self.mainBox = gtk.HBox(False, 0)				#and another dict to contain all the image objects				self.images = {}				#we'll make two boxes for the buttons - one for the "color" buttons and the		#other for the OK and Cancel buttons - but first, we need a VBox to contain		#them both, so here goes :				self.buttonBoxes = gtk.VBox(False, 0)				self.colorButtonBox = gtk.VButtonBox()		self.colorButtonBox.set_layout(gtk.BUTTONBOX_START)		self.colorButtonBox.set_border_width(10)		self.colorButtonBox.set_spacing(20)				#we create the images for the buttons and add them to our dict		#We also create a dict for the buttons themselves.				self.buttons = {}				for i in ['r', 'g', 'o', 'b', 'y', 'w', 'e', 'q'] :			self.images[i] = gtk.Image()			self.images[i].set_from_file(self.imagePath('plot_%s.png' % i))			self.images[i].show()			if i != 'q' :				#The 'q' item is the image with a question mark, so we don't				#pack it on a button.				self.buttons[i] = gtk.Button()				self.buttons[i].add(self.images[i])				self.buttons[i].connect('clicked', self.buttonClicked, i)				self.buttons[i].show()				self.colorButtonBox.add(self.buttons[i])				self.colorButtonBox.show()				#fancy OK and Cancel buttons and a box to contain them				self.spButtonBox = gtk.VButtonBox()		self.spButtonBox.set_layout(gtk.BUTTONBOX_START)		self.spButtonBox.set_border_width(10)		self.spButtonBox.set_spacing(10)				self.buttons['ok'] = gtk.Button(stock=gtk.STOCK_OK)		self.buttons['cancel'] = gtk.Button(stock=gtk.STOCK_CANCEL)		for i in ['ok', 'cancel'] :			self.buttons[i].show()			self.buttons[i].connect('clicked', self.buttonClicked, i)			self.spButtonBox.add(self.buttons[i])				self.spButtonBox.show()				#This is the "Fixed" container, that will contain the tokens		#on the board.		self.fixed = gtk.Fixed()		self.fixed.show()				#we pack and show the last elements				self.buttonBoxes.show()		self.buttonBoxes.pack_start(self.colorButtonBox, False, False, 0)		self.buttonBoxes.pack_end(self.spButtonBox, False, False, 0)				self.mainBox.pack_start(self.buttonBoxes, False, False, 0)		self.mainBox.pack_start(self.fixed, False, False, 10)		self.box.pack_start(self.mainBox, False, False, 0)				self.mainBox.show()		self.box.show()		self.window.show()			#finally, we print a nice message in the console		print("\nGUI mode successfully started. So far, so good...\n")			def main(self) :		"""		The main GTK loop...		"""		gtk.main()			def closeGame(self, widget, data=None) :		"""		Closes the main game window.		"""		print("Good bye !")		gtk.main_quit()			def imagePath(self, s) :		"""		The location of the images will differ depending on whether the user		installed the game with distutils or not. This fuction determines the		correct path to the image file s (s is the filename, e.g. 'foo.png').		"""				#begin precondition		assert type(s) is str		#end preconditions				import os, sys				if os.path.exists(os.path.join(sys.prefix, 'share', 'masterMind')) :			path = os.path.join(sys.prefix, 'share', 'masterMind', 'graphics', s)		else :			path = os.path.join('..', 'data', 'graphics', s)					return path		def buttonClicked(self, widget, button) :		"""		Is called when a button is clicked. All the if's are here because the		behaviour of this function varies depending on the state of the game.		"""				#begin preconditions		assert type(button) is str		assert button in ['r', 'g', 'o', 'b', 'y', 'w', 'e', 'ok', 'cancel']		#end preconditions				if not self.gameStarted :			#If the game has not yet been started, print an error message			startNewGameFirst(self)		else :			#"hole" allowed only in Expert level and if we do not already have one			self.holeAllowed = (self.level == 4 and 'e' not in self.curAttempt)						#This is the "position" of the current attempt, starting from 0			#It will be used to calculate the coordinates of the new token.			posOfCurAttempt = len(self.gameImages) - 1						#This is the position of the current "colour" in the current attempt.			#It will also be used to calculate the coordinates of the token.			curPosInAttempt = len(self.curAttempt)						if posOfCurAttempt == self.maxAttempts :				#If we've reached the maximum number of attempts...				#Should never occur as the game should be stopped before				#this is reached but you never know...				noMoreAttemptAllowed(self)							elif (curPosInAttempt >= 0 and curPosInAttempt <= 6) :				#If we have the right to add or remove a colour. That's where				#the fun begins, baby !								if (button in ['r', 'g', 'o', 'b', 'y', 'w']) or \					(button == 'e' and self.holeAllowed) :					#If we clicked a "colour" button.										if curPosInAttempt < 6 :						#If we are _really_ allowed to add a new colour, then we do it :p						self.addNewColour(self, \						posOfCurAttempt, curPosInAttempt, button)					else :						#If there are already six colours, we cannot add a new one						sixColoursOnly(self)										elif button == 'e' : # "and not self.holeAllowed" is implicit										#If we clicked the "hole" button but are not allowed					#to add one					holeNotAllowed(self)									elif button == 'ok' :					#If we clicked the OK button										if curPosInAttempt != 6 :						#There must be six colours for the attempt to be valid						notSixColours(self)					else :						self.testAttempt(self, data=None)				else :					#If we clicked the Cancel button.										if curPosInAttempt == 0 :						#We cannot delete a colour if there is none !						zeroColours(self)					else :						#If we can delete a colour, first we destroy the						#image widget						self.gameImages[-1][-1].destroy()						#then we delete it from the array						self.gameImages[-1].pop()						#And from that other one, too :p						self.curAttempt.pop()			else :				print("""This definitely shouldn't have occured.					Self-destruct in 5 seconds. Have a nice day.""")				def startGameClicked(self, level) :		"""		Is called when the player clicks the "New game" menu item.		Tests if a game is already started and if so, popups a window		asking the player to confirm.		"""				#begin preconditions		assert type(level) is int		assert level in [1, 2, 3, 4]		#end preconditions				#define the level attribute at class-level so we can use it in		#other methods without bothering to pass it each time.		self.level = level				if self.gameStarted :						#popup a menu			self.twoButtonsPopup(Base, popup='newGame')		else :			self.startGame(self.level)		def reallyStartNewGamePopupClose(self, widget, newGame) :		"""		Closes the "really start a ne game ?" popup and starts or does not		start a new game, depending on the button clicked.		"""				#begin precontitions		assert type(newGame) is bool		#end preconditions				self.twoButtonsPopup.destroy()				if newGame :			self.startGame(self.level)			def startGame(self, level) :		"""		This is called when starting a new game.		"""				#begin preconditions		assert type(level) is int		assert level in [1, 2, 3, 4]		#end preconditions				#set the self.level attribute		self.level = level				#if there's already a game started, clear the board		#before starting a new one		if self.gameStarted :			for i in self.gameImages :				for j in i :					j.destroy()							#generates the new sequence to guess		self.seq = masterMind.mmBase.newGame(level)[0]			#sets the max number of attempts permitted depending on the game level		if level == 3 :			self.maxAttempts = 6		else :			self.maxAttempts = 10				#put the tokens with a question mark		self.seqImages = []		for i in range(6) :			tmpImage = gtk.Image()			tmpImage.set_from_file(self.imagePath('plot_q.png'))			self.seqImages.append(tmpImage)			x = 5 + i*48			self.fixed.put(self.seqImages[i], x, 505)			self.seqImages[i].show()				self.gameImages = [[]]		self.curAttempt = []		self.scores = [[]]		self.gameStarted = True			def addNewColour(self, widget, row, col, colour) :		"""		Adds the new token on the board.		"""				#begin preconditions		assert type(row) is int		assert type(col) is int		assert type(colour) is str		#end preconditions				#create the GTK image object using the correct image.		tmpImage = gtk.Image()		tmpImage.set_from_file(self.imagePath('plot_%s.png' % colour))				#add it to the matrix		self.gameImages[row].append(tmpImage)				#calculate the coordinates		x = 5 + col*48		y = 505 - (row+1)*48				#put it at the right place and show it		self.fixed.put(self.gameImages[row][col], x, y)		self.gameImages[row][col].show()				#finally add the colour to the array representing the current attempt		#for comparison.		self.curAttempt.append(colour)			def testAttempt(self, widget, data=None) :		"""		Is called when the player presses the OK button and there are six colours		in the current attempt.		Tests the attempt and display the results.		"""				#begin preconditions		assert len(self.curAttempt) == 6		#end preconditions				#We must first replace the colour initials with the corresponding		#numbers to allow comparison.		for i in range(6) :			if self.curAttempt[i] == 'r' : self.curAttempt[i] = 1			elif self.curAttempt[i] == 'g' : self.curAttempt[i] = 2			elif self.curAttempt[i] == 'o' : self.curAttempt[i] = 3			elif self.curAttempt[i] == 'b' : self.curAttempt[i] = 4			elif self.curAttempt[i] == 'y' : self.curAttempt[i] = 5			elif self.curAttempt[i] == 'w' : self.curAttempt[i] = 6			elif self.curAttempt[i] == 'e' : self.curAttempt[i] = 7			else : print('Seems you have done something I didn\'t think of...')				self.results = masterMind.mmBase.testAttempt(self.curAttempt, self.seq)				#In Beginner mode, we're supposed to tell the player what the coulours		#he/she correctly guessed are, so we just replace the "question mark" token		#with the correct one. And it goes like this :		if self.level == 1 and self.results[0] >= 1 :						#first, we need to know where the correct colours are, and what they are			self.correctColours = masterMind.mmBase.getCorrectColours(self.curAttempt, self.seq)			for i in self.correctColours :				if self.seq[i] == 1 : tmp = 'r'				elif self.seq[i] == 2 : tmp = 'g'				elif self.seq[i] == 3 : tmp = 'o'				elif self.seq[i] == 4 : tmp = 'b'				elif self.seq[i] == 5 : tmp = 'y'				elif self.seq[i] == 6 : tmp = 'w'				elif self.seq[i] == 7 : tmp = 'e'								#Not an integet between 1 and 7 ? God, what have you done ?				else : print('What the hell is _wrong_ with you ?')								#now that we know which token we need to change				#and to which coulour, we just do it :p								self.seqImages[i].set_from_file(self.imagePath('plot_%s.png' % tmp))						print("Your last attempt had %s correct colours and %s misplaced colours." % \			(self.results[0], self.results[1]))					#Do it graphically...				for i in range(2) :			self.scores[-1].append(gtk.Label(str(self.results[i])))					#calculate the coordinates of the upper item		x = 297		y = 507 - len(self.scores)*48				for i in range(2) :			self.fixed.put(self.scores[-1][i], x + i*20, y + i*20)			self.scores[-1][i].show()				if self.results[0] == 6 :			#If the player has correctly guessed...			score = len(self.gameImages) * self.level			youWin(self, score)						#ends the game			self.endGame(self, data=None)					elif len(self.gameImages) == self.maxAttempts :			#If this was the last allowed attempt...			print("\nSorry, you lose.")						#ends the game			self.endGame(self)		else :			self.gameImages.append([])			self.curAttempt = []			self.scores.append([])							def endGame(self, widget, data=None) :		"""		Is called when the game ends. Clears the board and ask the player		whether he/she wants to start a new game.		"""				self.gameStarted=False				#destroy all the token images on the board		for i in self.gameImages :			for j in i :				j.destroy()						for i in self.seqImages :			i.destroy()				#and print a message in the console		print("You can now start a new game or quit.")					def quitGame(self, widget, data=None) :		"""		Is called when the player tries to exit the game.		"""				if self.gameStarted :			#if there's a game started, popup a "really quit ?" window.			self.twoButtonsPopup(Base, popup='quit')			#and return True because we do not want the main window to be destroyed			return True		else :			self.closeGame(Base)							def oneButtonPopup(self, widget, popup) :		"""		Popups a window with only one button. This is used for the High Scores and About		popups.		"""				#begin preconditions		assert type(popup) is str		assert popup in ['about', 'highScores']		#end preconditions				self.oneButtonPopup = gtk.Window(gtk.WINDOW_TOPLEVEL)		self.oneButtonPopup.set_transient_for(self.window)		self.oneButtonPopup.set_modal(True)		self.oneButtonPopup.set_property('skip-taskbar-hint', True)				if popup == 'highScores' :			self.oneButtonPopup.set_title('High scores')		else :			self.oneButtonPopup.set_title('About this game')					self.oneButtonPopup.set_resizable(False)		self.oneButtonPopup.connect("delete_event", self.oneButtonPopupClose)				self.oneButtonPopupBox = gtk.VBox(False, 0)				if popup == 'highScores' :			hScoresArray = masterMind.mmBase.fetchHighScores()			text = "\nHigh Scores :\n\n"					for i in hScoresArray :				text += "%s :\t\t%s (%s)\n" % (i[0], i[1], i[2])				elif popup == 'about' :			text = "\nThis is a Python implementation of the classic Mastermind game \made by Firas Kraiem for a project at ENIB. This code is released \under a 2-clause BSD-style license. See the README file for more information.\n"			self.oneButtonPopupText = gtk.Label(text)		self.oneButtonPopupText.set_justify(gtk.JUSTIFY_CENTER)		self.oneButtonPopupText.set_line_wrap(True)		self.oneButtonPopupText.show()				self.oneButtonPopupButton = gtk.Button(stock=gtk.STOCK_CLOSE)		self.oneButtonPopupButton.connect('clicked', self.oneButtonPopupClose)		self.oneButtonPopupButton.show()				self.oneButtonPopupBox.pack_start(self.oneButtonPopupText, False, False, 0)		self.oneButtonPopupBox.pack_start(self.oneButtonPopupButton, False, False, 0)		self.oneButtonPopupBox.show()				self.oneButtonPopup.add(self.oneButtonPopupBox)		self.oneButtonPopup.show()		def oneButtonPopupClose(self, widget, data=None) :		"""		Closes the "one-button" popup.		"""				self.oneButtonPopup.destroy()				def twoButtonsPopup(self, widget, popup) :		"""		Popups a window with two buttons. This is used for the "really quit ?" and		"really start new game ?" dialogs.		"""				#begin preconditions		assert type(popup) is str		assert popup in ['quit', 'newGame']		#end preconditions				self.twoButtonsPopup = gtk.Window(gtk.WINDOW_TOPLEVEL)		self.twoButtonsPopup.set_transient_for(self.window)		self.twoButtonsPopup.set_modal(True)		self.twoButtonsPopup.set_property('skip-taskbar-hint', True)		if popup == 'quit' :			title = 'Really quit ?'		elif popup == 'newGame' :			title = 'Really start a new game ?'				self.twoButtonsPopup.set_title(title)		self.twoButtonsPopup.set_resizable(False)				self.twoButtonsPopupBox = gtk.VBox(False, 0)				if popup == 'quit' :			text = "\nThere is a game started. Are you sure you want to quit ?\n"		elif popup == 'newGame' :			text = "\nThere is a game started already.\nAre you sure you want to start a new one ?\n"		else :			text = "Error."			self.twoButtonsPopupText = gtk.Label(text)		self.twoButtonsPopupText.set_justify(gtk.JUSTIFY_CENTER)		self.twoButtonsPopupText.set_line_wrap(True)		self.twoButtonsPopupText.show()				if popup == 'quit' :			self.twoButtonsPopupYesButton = gtk.Button(stock=gtk.STOCK_QUIT)		elif popup == 'newGame' :			self.twoButtonsPopupYesButton = gtk.Button(stock=gtk.STOCK_YES)					if popup == 'quit' :			self.twoButtonsPopupYesButton.connect('clicked', self.closeGame)		elif popup == 'newGame' :			self.twoButtonsPopupYesButton.connect('clicked', self.reallyStartNewGamePopupClose, True)				self.twoButtonsPopupYesButton.show()						if popup == 'quit' :			self.twoButtonsPopupNoButton = gtk.Button(stock=gtk.STOCK_CANCEL)		elif popup == 'newGame' :			self.twoButtonsPopupNoButton = gtk.Button(stock=gtk.STOCK_NO)					if popup == 'quit' :			self.twoButtonsPopupNoButton.connect('clicked', self.twoButtonsPopupClose)		elif popup == 'newGame' :			self.twoButtonsPopupYesButton.connect('clicked', self.reallyStartNewGamePopupClose, False)					self.twoButtonsPopupNoButton.show()						self.twoButtonsPopupButtonBox = gtk.HButtonBox()		self.twoButtonsPopupButtonBox.set_layout(gtk.BUTTONBOX_SPREAD)		self.twoButtonsPopupButtonBox.set_border_width(10)		self.twoButtonsPopupButtonBox.set_spacing(20)		self.twoButtonsPopupButtonBox.add(self.twoButtonsPopupYesButton)		self.twoButtonsPopupButtonBox.add(self.twoButtonsPopupNoButton)		self.twoButtonsPopupButtonBox.show()				self.twoButtonsPopupBox.pack_start(self.twoButtonsPopupText, False, False, 0)		self.twoButtonsPopupBox.pack_start(self.twoButtonsPopupButtonBox, False, False, 0)		self.twoButtonsPopupBox.show()				self.twoButtonsPopup.add(self.twoButtonsPopupBox)		self.twoButtonsPopup.show()			def twoButtonsPopupClose(self, widget, data=None) :		"""		Closes the popup...		"""				self.twoButtonsPopup.destroy()if __name__ == "__main__" :	print("You are not supposed to execute this directly. Please run\nmastermind.py instead.")