diff options
author | Bruno Coudoin <bcoudoin@src.gnome.org> | 2006-11-12 15:29:20 (GMT) |
---|---|---|
committer | Bruno Coudoin <bcoudoin@src.gnome.org> | 2006-11-12 15:29:20 (GMT) |
commit | a947a5b18956e90878ced860dd9d4b94da9a432e (patch) | |
tree | 78312f875daf23cd9fc2682d11fe91b6b93b8bda | |
parent | cf3c2ae09f6a4c6465a96202cad5ad43e10594c6 (diff) |
Added a connect4 two player mode
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | boards/Makefile.am | 1 | ||||
-rw-r--r-- | boards/connect4-2players.xml.in | 18 | ||||
-rw-r--r-- | boards/connect4.xml.in | 2 | ||||
-rw-r--r-- | po/POTFILES.in | 1 | ||||
-rw-r--r-- | src/boards/python/connect4.py | 557 |
6 files changed, 339 insertions, 250 deletions
@@ -1,5 +1,15 @@ 2006-11-12 Bruno coudoin <bruno.coudoin@free.fr> + - By Miguel DE IZARRA, added a 2 player mode in connect4. + + * boards/Makefile.am: + * boards/connect4-2players.xml.in: + * boards/connect4.xml.in: + * po/POTFILES.in: + * src/boards/python/connect4.py: + +2006-11-12 Bruno coudoin <bruno.coudoin@free.fr> + * src/gcompris/menu.c: (_add_xml_to_data): no more save translated text in the base. save the english version instead. diff --git a/boards/Makefile.am b/boards/Makefile.am index f09f68e..9e0cc8a 100644 --- a/boards/Makefile.am +++ b/boards/Makefile.am @@ -32,6 +32,7 @@ xml_in_files = \ colors.xml.in \ computer.xml.in \ connect4.xml.in \ + connect4-2players.xml.in \ crane.xml.in \ discovery.xml.in \ doubleentry.xml.in \ diff --git a/boards/connect4-2players.xml.in b/boards/connect4-2players.xml.in new file mode 100644 index 0000000..9dd29f3 --- /dev/null +++ b/boards/connect4-2players.xml.in @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<GCompris> + <Board type="python:connect4" + section="/strategy" + name="connect4-2players" + icon="boardicons/connect4.png" + difficulty="2" + boarddir="connect4" + mode="2players" + author="Laurent Lacheny (laurent.lacheny@wanadoo.fr) / Miguel de Izarra (miguel2i@free.fr)"> + <_title>Connect 4 (2 Players)</_title> + <_description>Arrange four coins in a row</_description> + <_prerequisite>Mouse-manipulation</_prerequisite> + <_goal>Create a line of 4 pieces either horizontally (lying down) or vertically (standing up)</_goal> + <_manual>Click anywhere in the line where you want to drop a piece. Alternatively, you can use the arrow keys to move and the down, or space key to drop a piece.</_manual> + <_credit>The original code was written in 2005 by Laurent Lacheny. In 2006, Miguel de Izarra made the two players game. Images and Artificial Intelligence taken from project 4stattack by Jeroen Vloothuis. The original project can be found on <http://forcedattack.sourceforge.net></_credit> + </Board> +</GCompris> diff --git a/boards/connect4.xml.in b/boards/connect4.xml.in index 86894dd..731cb46 100644 --- a/boards/connect4.xml.in +++ b/boards/connect4.xml.in @@ -11,7 +11,7 @@ <_description>Arrange four coins in a row</_description> <_prerequisite>Mouse-manipulation</_prerequisite> <_goal>Create a line of 4 pieces either horizontally (lying down) or vertically (standing up)</_goal> - <_manual>Click anywhere in the line where you want to drop a piece</_manual> + <_manual>Click anywhere in the line where you want to drop a piece. Alternatively, you can use the arrow keys to move and the down, or space key to drop a piece.</_manual> <_credit>Laurent Lacheny. Images and Artificial Intelligence taken from project 4stattack by Jeroen Vloothuis. The original project can be found on <http://forcedattack.sourceforge.net></_credit> </Board> </GCompris> diff --git a/po/POTFILES.in b/po/POTFILES.in index b40cf67..b63ce1a 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -67,6 +67,7 @@ boards/colors.xml.in boards/colors_group.xml.in boards/computer.xml.in boards/connect4.xml.in +boards/connect4-2players.xml.in boards/crane.xml.in boards/discovery.xml.in boards/doubleentry.xml.in diff --git a/src/boards/python/connect4.py b/src/boards/python/connect4.py index 77c112e..7f25441 100644 --- a/src/boards/python/connect4.py +++ b/src/boards/python/connect4.py @@ -34,7 +34,6 @@ import gtk import gtk.gdk from gettext import gettext as _ -import types from connect4p import rules from connect4p import human from connect4p import minmax @@ -43,258 +42,318 @@ from connect4p import board # class Gcompris_connect4: - """Connect 4 stones""" - - - def __init__(self, gcomprisBoard): - self.gcomprisBoard = gcomprisBoard - - def start(self): - self.boardSize = 490.0 - self.nbColumns = 7 - self.nbLines = 6 - self.stoneSize = 70.0 - self.maxVictory = 3 - self.maxLevel = 4 - self.firstPlayer = False - self.boardItem = None - self.timerAnim = 0 - self.timerMachine = 0 - self.humanVictory = 0 - self.machineHasPlayed = True - self.endAnimCallback = None - self.gcomprisBoard.level=1 - self.gcomprisBoard.maxlevel=9 - self.gcomprisBoard.sublevel=1 - self.gcomprisBoard.number_of_sublevel=1 - self.winnercall = None - gcompris.bar_set(gcompris.BAR_LEVEL) - gcompris.bar_set_level(self.gcomprisBoard) - - # Create our rootitem. We put each canvas item in it so at the end we - # only have to kill it. The canvas deletes all the items it contains automaticaly. - self.rootitem = self.gcomprisBoard.canvas.root().add( - gnomecanvas.CanvasGroup, - x=0.0, - y=0.0 - ) - - pixmap = gcompris.utils.load_pixmap(gcompris.skin.image_to_skin("button_reload.png")) - if(pixmap): - gcompris.bar_set_repeat_icon(pixmap) - gcompris.bar_set(gcompris.BAR_LEVEL|gcompris.BAR_REPEAT_ICON) - else: - gcompris.bar_set(gcompris.BAR_LEVEL|gcompris.BAR_REPEAT) - - selector = self.rootitem.add( - gnomecanvas.CanvasPixbuf, - pixbuf = gcompris.utils.load_pixmap("connect4/back.png"), - x=0.0, - y=0.0 - ) - selector.connect("event", self.columnItemEvent, 0) - - self.prof = self.rootitem.add( - gnomecanvas.CanvasPixbuf, - pixbuf = gcompris.utils.load_pixmap("connect4/tux-teacher.png"), - x=10, - y=350.0 - ) - self.prof.connect("event", self.profItemEvent, 0) - self.prof.connect("event", gcompris.utils.item_event_focus) - - self.timericon = gcompris.anim.CanvasItem( gcompris.anim.Animation("connect4/sablier.txt"), - self.rootitem ) - self.timericon.gnomecanvas.hide() - - self.newGame() - - def end(self): - if self.timerAnim: - gtk.timeout_remove(self.timerAnim) - if self.timerMachine: - gtk.timeout_remove(self.timerMachine) - - del self.timericon - - # Remove the root item removes all the others inside it - self.rootitem.destroy() - self.boardItem.destroy() - - - def ok(self): - pass - - def repeat(self): - if self.humanVictory >= self.maxVictory: - if self.gcomprisBoard.level < self.maxLevel: - self.set_level(self.gcomprisBoard.level+1) - else: - self.end() - gcompris.end_board() - else: - self.newGame() - - def config(self): - pass - - - def key_press(self, keyval, commit_str, preedit_str): - self.timericon.gnomecanvas.hide() - return False - - def pause(self, pause): - pass + """Connect 4 stones""" + + + def __init__(self, gcomprisBoard): + self.gcomprisBoard = gcomprisBoard + + def start(self): + self.boardSize = 490.0 + self.nbColumns = 7 + self.nbLines = 6 + self.stoneSize = 70.0 + self.maxVictory = 3 + self.maxLevel = 4 + self.boardItem = None + self.timerAnim = 0 + self.humanVictory = 0 + self.endAnimCallback = None + self.gcomprisBoard.level=1 + self.gcomprisBoard.maxlevel=9 + self.gcomprisBoard.sublevel=1 + self.gcomprisBoard.number_of_sublevel=1 + self.mode = (self.gcomprisBoard.mode == "2players") and 2 or 1 + if self.mode == 1: + board_bar = gcompris.BAR_LEVEL + gcompris.bar_set_level(self.gcomprisBoard) + else: + board_bar = 0 + # Create our rootitem. We put each canvas item in it so at the end we + # only have to kill it. The canvas deletes all the items it contains automaticaly. + self.rootitem = self.gcomprisBoard.canvas.root().add( + gnomecanvas.CanvasGroup, + x=0.0, + y=0.0 + ) + + pixmap = gcompris.utils.load_pixmap(gcompris.skin.image_to_skin("button_reload.png")) + if(pixmap): + gcompris.bar_set_repeat_icon(pixmap) + board_bar = board_bar | gcompris.BAR_REPEAT_ICON + else: + board_bar = board_bar | gcompris.BAR_REPEAT + gcompris.bar_set(board_bar) + + selector = self.rootitem.add( + gnomecanvas.CanvasPixbuf, + pixbuf = gcompris.utils.load_pixmap("connect4/back.png"), + x=0.0, + y=0.0 + ) + selector.connect("event", self.columnItemEvent, 0) + + if self.mode == 1: + self.prof = self.rootitem.add( + gnomecanvas.CanvasPixbuf, + pixbuf = gcompris.utils.load_pixmap("connect4/tux-teacher.png"), + x=10, + y=350.0 + ) + self.prof.connect("event", self.profItemEvent, 0) + self.prof.connect("event", gcompris.utils.item_event_focus) + + self.timericon = gcompris.anim.CanvasItem( gcompris.anim.Animation("connect4/sablier.txt"), + self.rootitem ) + self.timericon.gnomecanvas.hide() + + self.player_stone = None + + for i in range(2): + self.rootitem.add(gnomecanvas.CanvasPixbuf, + pixbuf = gcompris.utils.load_pixmap("connect4/stone_%d.png" % (i+1)), + x= 25, + y=175 + i * 73 ) + self.score_item = self.rootitem.add( + gnomecanvas.CanvasText, + font=gcompris.skin.get_font("gcompris/board/huge bold"), + x=60, + y=250, + justification=gtk.JUSTIFY_CENTER, + fill_color_rgba=0xFF0000FFL) + + self.update_scores((0, 0)) + + self.newGame() + + def end(self): + if self.timerAnim: + gtk.timeout_remove(self.timerAnim) + + del self.timericon + + # Remove the root item removes all the others inside it + self.rootitem.destroy() + self.boardItem.destroy() + + def ok(self): + pass + + def repeat(self): + if max(self.scores) >= self.maxVictory: + if self.scores[0] >= self.maxVictory and self.mode == 1: + if self.gcomprisBoard.level < self.maxLevel: + self.set_level(self.gcomprisBoard.level+1) + else: + self.end() + gcompris.end_board() + else: + self.update_scores([0, 0]) + self.newGame() + else: + self.newGame() + + def config(self): + pass + def update_scores(self, scores=None): + if scores: self.scores = list(scores) + txt = str(self.scores[0]) + "\n\n" + str(self.scores[1]) + self.score_item.set(text=txt) + + def stone_init(self): + if self.player_stone: + self.player_stone.destroy() + self.player_stone = self.rootitem.add( + gnomecanvas.CanvasPixbuf, + pixbuf = gcompris.utils.load_pixmap("connect4/stone_%d.png" % self.cur_player), + x=0, + y=-20 ) + self.update_stone2() + def update_stone2(self): + self.player_stone.set_property("x", (gcompris.BOARD_WIDTH - self.boardSize) /2 + + self.keyb_column * self.boardSize / self.nbColumns) + + def key_press(self, keyval, commit_str, preedit_str): + if self.mode == 1 and self.cur_player == 2: # AI playing + return False + if self.cur_player == 0 or self.timerAnim: # Game over or Timer animate + return False + key = gtk.gdk.keyval_name(keyval) + if key == "Left": + self.keyb_column = max(self.keyb_column-1, 0) + self.update_stone2() + elif key == "Right": + self.keyb_column = min(self.keyb_column+1, self.nbColumns-1) + self.update_stone2() + elif key =="Down" or commit_str == " ": + if self.play(self.player1, self.cur_player, self.keyb_column): + self.player_stone.hide() + return False + + def pause(self, pause): + pass # Called by gcompris when the user click on the level icon - def set_level(self, level): - if level > self.maxLevel: level = 1 - self.gcomprisBoard.level=level - self.gcomprisBoard.sublevel=1 - gcompris.bar_set_level(self.gcomprisBoard) - self.player1.setDifficulty(level) - self.player2.setDifficulty(level) - self.humanVictory = 0 - self.newGame() + def set_level(self, level): + if level > self.maxLevel: level = 1 + self.gcomprisBoard.level=level + self.gcomprisBoard.sublevel=1 + gcompris.bar_set_level(self.gcomprisBoard) + self.player1.setDifficulty(level) + self.player2.setDifficulty(level) + self.update_scores([0, 0]) + self.newGame() # End of Initialisation # --------------------- - def newGame(self): - if self.boardItem != None: - self.boardItem.destroy() - self.boardItem = self.gcomprisBoard.canvas.root().add( - gnomecanvas.CanvasGroup, - x=(gcompris.BOARD_WIDTH-self.boardSize)/2.0, - y=50.0 - ) - self.player1 = human.Human(self.gcomprisBoard.level) - self.player2 = minmax.MinMax(self.gcomprisBoard.level, self.refreshScreen) - self.board = board.Board() - self.gamewon = False - self.machineHasPlayed = True - self.winLine = None - try: - del self.redLine - except: - pass - self.firstPlayer = False - self.prof.show() - - def columnItemEvent(self, widget, event, column): - if event.type == gtk.gdk.BUTTON_PRESS: - if event.button == 1 and self.gamewon == False and self.machineHasPlayed: - column = int((event.x - (gcompris.BOARD_WIDTH-self.boardSize)/2.0) // self.stoneSize) - if not (column < 0 or column > (self.nbColumns-1)): - gcompris.bar_hide(True) - if self.play(self.player1, 1, column): - if self.winnercall == None: - self.timericon.gnomecanvas.show() - self.endAnimCallback = self.machinePlay - self.machineHasPlayed = False - - return False - - def profItemEvent(self, widget, event, column): - #if event.type == gtk.gdk.BUTTON_PRESS and self.firstPlayer == False: - if event.type == gtk.gdk.BUTTON_PRESS: - self.firstPlayer = True - self.machinePlay() - - def machinePlay(self): - # ai starts - self.play(self.player2, 2, 0) - # ai ends - self.timericon.gnomecanvas.hide() - self.prof.hide() - self.endAnimCallback = self.machinePlayed - - def machinePlayed(self): - self.machineHasPlayed = True - gcompris.bar_hide(False) - - def refreshScreen(self): - gtk.main_iteration(block=False) - pass - - def play(self, player, numPlayer, column): - move = player.doMove(self.board, numPlayer, column) - - if type(move) is types.IntType and rules.isMoveLegal(self.board, move): -# self.firstPlayer = True - self.board.move(move, numPlayer) - self.drawBoard(self.board) - self.winLine = rules.isWinner(self.board, numPlayer) - - if self.winLine: - self.winnercall = lambda : self.winner(numPlayer) - elif rules.isBoardFull(self.board): - self.winnercall = lambda : self.winner(0) - return True - - gcompris.bar_hide(False) - return False - - def drawBoard(self, board): - stone = self.board.state[self.board.last_move][-1] - x = self.board.last_move - y = len(self.board.state[self.board.last_move])-1 - file = "connect4/stone_%d.png" % stone - - self.stone = self.boardItem.add( - gnomecanvas.CanvasPixbuf, - pixbuf = gcompris.utils.load_pixmap(file), - x=x*(self.boardSize/self.nbColumns), - #y=(self.boardSize/self.nbColumns)*(self.nbLines-1-y) - y=0 - ) - self.stone.connect("event", self.columnItemEvent, 0) - self.countAnim = self.nbLines-y - self.timerAnim = gtk.timeout_add(200, self.animTimer) - - def animTimer(self): - self.countAnim -= 1 - if self.countAnim > 0: - y= self.stone.get_property('y') - self.stone.set_property('y', y + (self.boardSize/self.nbColumns)) - self.timerAnim = gtk.timeout_add(200, self.animTimer) - else: - if self.winnercall: - self.winnercall() - self.winnercall=None - self.endAnimCallback=None - if self.endAnimCallback: - self.endAnimCallback() - - def winner(self, player): - self.gamewon = True - - # It's a draw, no line to draw - if player == 0: - gcompris.bonus.display(gcompris.bonus.DRAW, gcompris.bonus.FLOWER) - return - - # Display the winner line - if ((self.firstPlayer and (player==2)) or - ((not self.firstPlayer) and (player==1))): - self.humanVictory += 1 - else: - self.humanVictory = 0 - points = ( self.winLine[0][0]*(self.boardSize/self.nbColumns)+self.stoneSize/2, - (self.boardSize/self.nbColumns)*(self.nbLines-1-self.winLine[0][1])+self.stoneSize/2, - self.winLine[1][0]*(self.boardSize/self.nbColumns)+self.stoneSize/2, - (self.boardSize/self.nbColumns)*(self.nbLines-1-self.winLine[1][1])+self.stoneSize/2 - ) - - self.redLine = self.boardItem.add( - gnomecanvas.CanvasLine, - fill_color_rgba=0xFF0000FFL, - points=points, - width_pixels = 8 - ) - self.redLine.set_property("cap-style", gtk.gdk.CAP_ROUND) - if player == 1: - gcompris.bonus.display(gcompris.bonus.WIN, gcompris.bonus.FLOWER) - elif player == 2: - gcompris.bonus.display(gcompris.bonus.LOOSE, gcompris.bonus.FLOWER) + def newGame(self): + if self.timerAnim: + gtk.timeout_remove(self.timerAnim) + self.timerAnim = None + if self.boardItem: + self.boardItem.destroy() + self.boardItem = self.gcomprisBoard.canvas.root().add( + gnomecanvas.CanvasGroup, + x=(gcompris.BOARD_WIDTH-self.boardSize)/2.0, + y=50.0 + ) + self.player1 = human.Human(self.gcomprisBoard.level) + if self.mode == 1: + self.player2 = minmax.MinMax(self.gcomprisBoard.level, self.refreshScreen) + self.board = board.Board() + self.gamewon = False + self.winLine = None + try: + del self.redLine + except: + pass + self.keyb_column = 0 + self.cur_player = 1 + self.stone_init() + self.update_stone2() + + self.timericon.gnomecanvas.hide() + if self.mode == 1: + self.prof.show() + + def columnItemEvent(self, widget, event, columns): + if self.mode == 1 and self.cur_player == 2: # AI playing + return False + if self.cur_player == 0 or self.timerAnim: # Game over or Timer animate + return False + if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1: + column = int((event.x - (gcompris.BOARD_WIDTH-self.boardSize)/2.0) // self.stoneSize) + if 0 <= column < self.nbColumns: + if self.play(self.player1, self.cur_player, column): + self.timericon.gnomecanvas.show() + self.player_stone.hide() + elif event.type == gtk.gdk.MOTION_NOTIFY: + column = int((event.x - (gcompris.BOARD_WIDTH-self.boardSize)/2.0) // self.stoneSize) + if 0 <= column < self.nbColumns: + self.keyb_column = column + self.update_stone2() + return False + + def profItemEvent(self, widget, event, column): + if event.type == gtk.gdk.BUTTON_PRESS: + self.cur_player=1 + self.prof.hide() + self.player_stone.hide() + self.play_next() + + def play_next(self): + if self.cur_player == 1: + self.cur_player = 2 + if self.mode == 1: + # IA play + self.timericon.gnomecanvas.show() + self.play(self.player2, 2, 0) + else: + # player 2 + self.stone_init() + else: + # player 1 + self.cur_player = 1 + self.stone_init() + + def refreshScreen(self): + gtk.main_iteration(block=False) + pass + + def play(self, player, numPlayer, column): + if self.mode == 1: + self.prof.hide() + move = player.doMove(self.board, numPlayer, column) + + if isinstance(move, int) and rules.isMoveLegal(self.board, move): + self.board.move(move, numPlayer) + self.drawBoard(self.board) + return True + return False + + def drawBoard(self, board): + stone = self.board.state[self.board.last_move][-1] + x = self.board.last_move + y = len(self.board.state[self.board.last_move])-1 + file = "connect4/stone_%d.png" % stone + + self.stone = self.boardItem.add( + gnomecanvas.CanvasPixbuf, + pixbuf = gcompris.utils.load_pixmap(file), + x=x*(self.boardSize/self.nbColumns), + y=0 + ) + self.stone.connect("event", self.columnItemEvent, 0) + self.countAnim = self.nbLines-y + self.timerAnim = gtk.timeout_add(200, self.animTimer) + self.timericon.gnomecanvas.show() + + def animTimer(self): + self.countAnim -= 1 + if self.countAnim > 0: + y= self.stone.get_property('y') + self.stone.set_property('y', y + (self.boardSize/self.nbColumns)) + self.timerAnim = gtk.timeout_add(200, self.animTimer) + else: + self.timerAnim = None + self.timericon.gnomecanvas.hide() + self.winLine = rules.isWinner(self.board, self.cur_player) + if self.winLine: + self.winner(self.cur_player) + elif rules.isBoardFull(self.board): + self.winner(0) + else: + self.play_next() + + def winner(self, player): + self.gamewon = True + self.cur_player = 0 + + # It's a draw, no line to draw + if player == 0: + gcompris.bonus.display(gcompris.bonus.DRAW, gcompris.bonus.FLOWER) + return + + # Display the winner line + self.scores[player-1] += 1 + self.update_scores() + + points = ( self.winLine[0][0]*(self.boardSize/self.nbColumns)+self.stoneSize/2, + (self.boardSize/self.nbColumns)*(self.nbLines-1-self.winLine[0][1])+self.stoneSize/2, + self.winLine[1][0]*(self.boardSize/self.nbColumns)+self.stoneSize/2, + (self.boardSize/self.nbColumns)*(self.nbLines-1-self.winLine[1][1])+self.stoneSize/2 + ) + + self.redLine = self.boardItem.add( + gnomecanvas.CanvasLine, + fill_color_rgba=0xFF0000FFL, + points=points, + width_pixels = 8 + ) + self.redLine.set_property("cap-style", gtk.gdk.CAP_ROUND) + if player == 1: + gcompris.bonus.display(gcompris.bonus.WIN, gcompris.bonus.FLOWER) + elif player == 2: + gcompris.bonus.display(gcompris.bonus.LOOSE, gcompris.bonus.FLOWER) |