Back to index

gcompris  8.2.2
connect4.py
Go to the documentation of this file.
00001 #  gcompris - connect4
00002 #
00003 # Time-stamp:
00004 #
00005 # Copyright (C) 2005 Laurent Lacheny
00006 #
00007 #   This program is free software; you can redistribute it and/or modify
00008 #   it under the terms of the GNU General Public License as published by
00009 #   the Free Software Foundation; either version 2 of the License, or
00010 #   (at your option) any later version.
00011 #
00012 #   This program is distributed in the hope that it will be useful,
00013 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 #   GNU General Public License for more details.
00016 #
00017 #   You should have received a copy of the GNU General Public License
00018 #   along with this program; if not, write to the Free Software
00019 #   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00020 
00021 #
00022 # The IA code comes from the project 4stattack by Jeroen Vloothuis
00023 # http://forcedattack.sourceforge.net/
00024 #
00025 
00026 import gobject
00027 import gnomecanvas
00028 import gcompris
00029 import gcompris.utils
00030 import gcompris.skin
00031 import gcompris.bonus
00032 import gcompris.anim
00033 import gtk
00034 import gtk.gdk
00035 from gettext import gettext as _
00036 
00037 import types
00038 from  connect4p import rules
00039 from  connect4p import human
00040 from  connect4p import minmax
00041 from  connect4p import board
00042 # ----------------------------------------
00043 #
00044 
00045 class Gcompris_connect4:
00046   """Connect 4 stones"""
00047 
00048 
00049   def __init__(self, gcomprisBoard):
00050     self.gcomprisBoard = gcomprisBoard
00051 
00052   def start(self):
00053     self.boardSize = 490.0
00054     self.nbColumns = 7
00055     self.nbLines = 6
00056     self.stoneSize = 70.0
00057     self.maxVictory = 3
00058     self.maxLevel = 4
00059     self.firstPlayer = False
00060     self.boardItem = None
00061     self.timerAnim = 0
00062     self.timerMachine = 0
00063     self.humanVictory = 0
00064     self.machineHasPlayed = True
00065     self.endAnimCallback = None
00066     self.gcomprisBoard.level=1
00067     self.gcomprisBoard.maxlevel=9
00068     self.gcomprisBoard.sublevel=1
00069     self.gcomprisBoard.number_of_sublevel=1
00070     self.winnercall = None
00071     gcompris.bar_set(gcompris.BAR_LEVEL)
00072     gcompris.bar_set_level(self.gcomprisBoard)
00073 
00074     # Create our rootitem. We put each canvas item in it so at the end we
00075     # only have to kill it. The canvas deletes all the items it contains automaticaly.
00076     self.rootitem = self.gcomprisBoard.canvas.root().add(
00077       gnomecanvas.CanvasGroup,
00078       x=0.0,
00079       y=0.0
00080       )
00081 
00082     pixmap = gcompris.utils.load_pixmap(gcompris.skin.image_to_skin("button_reload.png"))
00083     if(pixmap):
00084       gcompris.bar_set_repeat_icon(pixmap)
00085       gcompris.bar_set(gcompris.BAR_LEVEL|gcompris.BAR_REPEAT_ICON)
00086     else:
00087       gcompris.bar_set(gcompris.BAR_LEVEL|gcompris.BAR_REPEAT)
00088 
00089     selector = self.rootitem.add(
00090      gnomecanvas.CanvasPixbuf,
00091       pixbuf = gcompris.utils.load_pixmap("connect4/back.png"),
00092       x=0.0,
00093       y=0.0
00094       )
00095     selector.connect("event", self.columnItemEvent, 0)
00096 
00097     self.prof = self.rootitem.add(
00098      gnomecanvas.CanvasPixbuf,
00099       pixbuf = gcompris.utils.load_pixmap("connect4/tux-teacher.png"),
00100       x=10,
00101       y=350.0
00102       )
00103     self.prof.connect("event", self.profItemEvent, 0)
00104     self.prof.connect("event", gcompris.utils.item_event_focus)
00105 
00106     self.timericon = gcompris.anim.CanvasItem( gcompris.anim.Animation("connect4/sablier.txt"),
00107                                                self.rootitem )
00108     self.timericon.gnomecanvas.hide()
00109 
00110     self.newGame()
00111 
00112   def end(self):
00113     if self.timerAnim:
00114       gtk.timeout_remove(self.timerAnim)
00115     if self.timerMachine:
00116       gtk.timeout_remove(self.timerMachine)
00117 
00118     del self.timericon
00119 
00120     # Remove the root item removes all the others inside it
00121     self.rootitem.destroy()
00122     self.boardItem.destroy()
00123 
00124 
00125   def ok(self):
00126     pass
00127 
00128   def repeat(self):
00129     if self.humanVictory >= self.maxVictory:
00130       if self.gcomprisBoard.level < self.maxLevel:
00131         self.set_level(self.gcomprisBoard.level+1)
00132       else:
00133         self.end()
00134         gcompris.end_board()
00135     else:
00136       self.newGame()
00137 
00138   def config(self):
00139     pass
00140 
00141 
00142   def key_press(self, keyval, commit_str, preedit_str):
00143     self.timericon.gnomecanvas.hide()
00144     return False
00145 
00146   def pause(self, pause):
00147     pass
00148 
00149   # Called by gcompris when the user click on the level icon
00150   def set_level(self, level):
00151     if level > self.maxLevel: level = 1
00152     self.gcomprisBoard.level=level
00153     self.gcomprisBoard.sublevel=1
00154     gcompris.bar_set_level(self.gcomprisBoard)
00155     self.player1.setDifficulty(level)
00156     self.player2.setDifficulty(level)
00157     self.humanVictory = 0
00158     self.newGame()
00159 
00160   # End of Initialisation
00161   # ---------------------
00162 
00163   def newGame(self):
00164     if self.boardItem != None:
00165       self.boardItem.destroy()
00166     self.boardItem = self.gcomprisBoard.canvas.root().add(
00167       gnomecanvas.CanvasGroup,
00168       x=(gcompris.BOARD_WIDTH-self.boardSize)/2.0,
00169       y=50.0
00170       )
00171     self.player1 = human.Human(self.gcomprisBoard.level)
00172     self.player2 = minmax.MinMax(self.gcomprisBoard.level, self.refreshScreen)
00173     self.board = board.Board()
00174     self.gamewon = False
00175     self.machineHasPlayed = True
00176     self.winLine = None
00177     try:
00178       del self.redLine
00179     except:
00180       pass
00181     self.firstPlayer = False
00182     self.prof.show()
00183 
00184   def columnItemEvent(self, widget, event, column):
00185     if event.type == gtk.gdk.BUTTON_PRESS:
00186       if event.button == 1 and self.gamewon == False and self.machineHasPlayed:
00187         column = int((event.x - (gcompris.BOARD_WIDTH-self.boardSize)/2.0) // self.stoneSize)
00188         if not (column < 0 or column > (self.nbColumns-1)):
00189           gcompris.bar_hide(True)
00190           if self.play(self.player1, 1, column):
00191             if self.winnercall == None:
00192               self.timericon.gnomecanvas.show()
00193               self.endAnimCallback = self.machinePlay
00194               self.machineHasPlayed = False
00195 
00196     return False
00197 
00198   def profItemEvent(self, widget, event, column):
00199     #if event.type == gtk.gdk.BUTTON_PRESS and self.firstPlayer == False:
00200     if event.type == gtk.gdk.BUTTON_PRESS:
00201       self.firstPlayer = True
00202       self.machinePlay()
00203 
00204   def machinePlay(self):
00205     # ai starts
00206     self.play(self.player2, 2, 0)
00207     # ai ends
00208     self.timericon.gnomecanvas.hide()
00209     self.prof.hide()
00210     self.endAnimCallback = self.machinePlayed
00211 
00212   def machinePlayed(self):
00213     self.machineHasPlayed = True
00214     gcompris.bar_hide(False)
00215 
00216   def refreshScreen(self):
00217     gtk.main_iteration(block=False)
00218     pass
00219 
00220   def play(self, player, numPlayer, column):
00221     move = player.doMove(self.board, numPlayer, column)
00222 
00223     if type(move) is types.IntType and rules.isMoveLegal(self.board, move):
00224 #      self.firstPlayer = True
00225       self.board.move(move, numPlayer)
00226       self.drawBoard(self.board)
00227       self.winLine = rules.isWinner(self.board, numPlayer)
00228 
00229       if self.winLine:
00230         self.winnercall = lambda : self.winner(numPlayer)
00231       elif rules.isBoardFull(self.board):
00232         self.winnercall = lambda : self.winner(0)
00233       return True
00234 
00235     gcompris.bar_hide(False)
00236     return False
00237 
00238   def drawBoard(self, board):
00239     stone = self.board.state[self.board.last_move][-1]
00240     x = self.board.last_move
00241     y = len(self.board.state[self.board.last_move])-1
00242     file = "connect4/stone_%d.png" % stone
00243 
00244     self.stone = self.boardItem.add(
00245       gnomecanvas.CanvasPixbuf,
00246        pixbuf = gcompris.utils.load_pixmap(file),
00247        x=x*(self.boardSize/self.nbColumns),
00248        #y=(self.boardSize/self.nbColumns)*(self.nbLines-1-y)
00249        y=0
00250        )
00251     self.stone.connect("event", self.columnItemEvent, 0)
00252     self.countAnim = self.nbLines-y
00253     self.timerAnim = gtk.timeout_add(200, self.animTimer)
00254 
00255   def animTimer(self):
00256     self.countAnim -= 1
00257     if self.countAnim > 0:
00258       y= self.stone.get_property('y')
00259       self.stone.set_property('y', y + (self.boardSize/self.nbColumns))
00260       self.timerAnim = gtk.timeout_add(200, self.animTimer)
00261     else:
00262       if self.winnercall:
00263         self.winnercall()
00264         self.winnercall=None
00265         self.endAnimCallback=None
00266       if self.endAnimCallback:
00267         self.endAnimCallback()
00268 
00269   def winner(self, player):
00270     self.gamewon = True
00271 
00272     # It's a draw, no line to draw
00273     if player == 0:
00274       gcompris.bonus.display(gcompris.bonus.DRAW, gcompris.bonus.FLOWER)
00275       return
00276 
00277     # Display the winner line
00278     if ((self.firstPlayer and (player==2)) or
00279         ((not self.firstPlayer) and (player==1))):
00280       self.humanVictory += 1
00281     else:
00282       self.humanVictory = 0
00283     points = ( self.winLine[0][0]*(self.boardSize/self.nbColumns)+self.stoneSize/2,
00284                (self.boardSize/self.nbColumns)*(self.nbLines-1-self.winLine[0][1])+self.stoneSize/2,
00285                self.winLine[1][0]*(self.boardSize/self.nbColumns)+self.stoneSize/2,
00286                (self.boardSize/self.nbColumns)*(self.nbLines-1-self.winLine[1][1])+self.stoneSize/2
00287                )
00288 
00289     self.redLine = self.boardItem.add(
00290       gnomecanvas.CanvasLine,
00291        fill_color_rgba=0xFF0000FFL,
00292        points=points,
00293        width_pixels = 8
00294        )
00295     self.redLine.set_property("cap-style", gtk.gdk.CAP_ROUND)
00296     if player == 1:
00297       gcompris.bonus.display(gcompris.bonus.WIN, gcompris.bonus.FLOWER)
00298     elif player == 2:
00299       gcompris.bonus.display(gcompris.bonus.LOOSE, gcompris.bonus.FLOWER)
00300