3
# Copyright (C) 2005 Laurent Lacheny
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, see <http://www.gnu.org/licenses/>.
19
# The IA code comes from the project 4stattack by Jeroen Vloothuis
20
# http://forcedattack.sourceforge.net/
32
from gcompris import gcompris_gettext as _
34
from connect4p import rules
35
from connect4p import human
36
from connect4p import minmax
37
from connect4p import board
38
# ----------------------------------------
41
class Gcompris_connect4:
42
"""Connect 4 stones"""
45
def __init__(self, gcomprisBoard):
46
self.gcomprisBoard = gcomprisBoard
49
self.boardSize = 490.0
58
self.endAnimCallback = None
59
self.gcomprisBoard.level=1
60
self.gcomprisBoard.maxlevel=9
61
self.gcomprisBoard.sublevel=1
62
self.gcomprisBoard.number_of_sublevel=1
63
self.mode = (self.gcomprisBoard.mode == "2players") and 2 or 1
65
board_bar = gcompris.BAR_LEVEL
66
gcompris.bar_set_level(self.gcomprisBoard)
69
# Create our rootitem. We put each canvas item in it so at the end we
70
# only have to kill it. The canvas deletes all the items it contains automaticaly.
71
self.rootitem = self.gcomprisBoard.canvas.root().add(
72
gnomecanvas.CanvasGroup,
77
pixmap = gcompris.utils.load_pixmap(gcompris.skin.image_to_skin("button_reload.png"))
79
gcompris.bar_set_repeat_icon(pixmap)
80
board_bar = board_bar | gcompris.BAR_REPEAT_ICON
82
board_bar = board_bar | gcompris.BAR_REPEAT
83
gcompris.bar_set(board_bar)
85
selector = self.rootitem.add(
86
gnomecanvas.CanvasPixbuf,
87
pixbuf = gcompris.utils.load_pixmap("connect4/back.png"),
91
selector.connect("event", self.columnItemEvent, 0)
94
self.prof = self.rootitem.add(
95
gnomecanvas.CanvasPixbuf,
96
pixbuf = gcompris.utils.load_pixmap("connect4/tux-teacher.png"),
100
self.prof.connect("event", self.profItemEvent, 0)
101
self.prof.connect("event", gcompris.utils.item_event_focus)
103
self.timericon = gcompris.anim.CanvasItem( gcompris.anim.Animation("connect4/sablier.txt"),
105
self.timericon.gnomecanvas.hide()
107
self.player_stone = None
110
self.rootitem.add(gnomecanvas.CanvasPixbuf,
111
pixbuf = gcompris.utils.load_pixmap("connect4/stone_%d.png" % (i+1)),
114
self.score_item = self.rootitem.add(
115
gnomecanvas.CanvasText,
116
font=gcompris.skin.get_font("gcompris/board/huge bold"),
119
justification=gtk.JUSTIFY_CENTER,
120
fill_color_rgba=0xFF0000FFL)
122
self.update_scores((0, 0))
128
gtk.timeout_remove(self.timerAnim)
132
# Remove the root item removes all the others inside it
133
self.rootitem.destroy()
134
self.boardItem.destroy()
140
if max(self.scores) >= self.maxVictory:
141
if self.scores[0] >= self.maxVictory and self.mode == 1:
142
if self.gcomprisBoard.level < self.maxLevel:
143
self.set_level(self.gcomprisBoard.level+1)
148
self.update_scores([0, 0])
155
def update_scores(self, scores=None):
156
if scores: self.scores = list(scores)
157
txt = str(self.scores[0]) + "\n\n" + str(self.scores[1])
158
self.score_item.set(text=txt)
160
def stone_init(self):
161
if self.player_stone:
162
self.player_stone.destroy()
163
self.player_stone = self.rootitem.add(
164
gnomecanvas.CanvasPixbuf,
165
pixbuf = gcompris.utils.load_pixmap("connect4/stone_%d.png" % self.cur_player),
169
def update_stone2(self):
170
self.player_stone.set_property("x", (gcompris.BOARD_WIDTH - self.boardSize) /2 +
171
self.keyb_column * self.boardSize / self.nbColumns)
173
def key_press(self, keyval, commit_str, preedit_str):
174
if self.mode == 1 and self.cur_player == 2: # AI playing
176
if self.cur_player == 0 or self.timerAnim: # Game over or Timer animate
178
key = gtk.gdk.keyval_name(keyval)
180
self.keyb_column = max(self.keyb_column-1, 0)
183
self.keyb_column = min(self.keyb_column+1, self.nbColumns-1)
185
elif key =="Down" or commit_str == " ":
186
if self.play(self.player1, self.cur_player, self.keyb_column):
187
self.player_stone.hide()
190
def pause(self, pause):
193
# Called by gcompris when the user click on the level icon
194
def set_level(self, level):
195
if level > self.maxLevel: level = 1
196
self.gcomprisBoard.level=level
197
self.gcomprisBoard.sublevel=1
198
gcompris.bar_set_level(self.gcomprisBoard)
199
self.player1.setDifficulty(level)
200
self.player2.setDifficulty(level)
201
self.update_scores([0, 0])
204
# End of Initialisation
205
# ---------------------
209
gtk.timeout_remove(self.timerAnim)
210
self.timerAnim = None
212
self.boardItem.destroy()
213
self.boardItem = self.gcomprisBoard.canvas.root().add(
214
gnomecanvas.CanvasGroup,
215
x=(gcompris.BOARD_WIDTH-self.boardSize)/2.0,
218
self.player1 = human.Human(self.gcomprisBoard.level)
220
self.player2 = minmax.MinMax(self.gcomprisBoard.level, self.refreshScreen)
221
self.board = board.Board()
233
self.timericon.gnomecanvas.hide()
237
def columnItemEvent(self, widget, event, columns):
238
if self.mode == 1 and self.cur_player == 2: # AI playing
240
if self.cur_player == 0 or self.timerAnim: # Game over or Timer animate
242
if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1:
243
column = int((event.x - (gcompris.BOARD_WIDTH-self.boardSize)/2.0) // self.stoneSize)
244
if 0 <= column < self.nbColumns:
245
if self.play(self.player1, self.cur_player, column):
246
self.timericon.gnomecanvas.show()
247
self.player_stone.hide()
248
elif event.type == gtk.gdk.MOTION_NOTIFY:
249
column = int((event.x - (gcompris.BOARD_WIDTH-self.boardSize)/2.0) // self.stoneSize)
250
if 0 <= column < self.nbColumns:
251
self.keyb_column = column
255
def profItemEvent(self, widget, event, column):
256
if event.type == gtk.gdk.BUTTON_PRESS:
259
self.player_stone.hide()
263
if self.cur_player == 1:
267
self.timericon.gnomecanvas.show()
268
self.play(self.player2, 2, 0)
277
def refreshScreen(self):
278
gtk.main_iteration(block=False)
281
def play(self, player, numPlayer, column):
284
move = player.doMove(self.board, numPlayer, column)
286
if isinstance(move, int) and rules.isMoveLegal(self.board, move):
287
self.board.move(move, numPlayer)
288
self.drawBoard(self.board)
292
def drawBoard(self, board):
293
stone = self.board.state[self.board.last_move][-1]
294
x = self.board.last_move
295
y = len(self.board.state[self.board.last_move])-1
296
file = "connect4/stone_%d.png" % stone
298
self.stone = self.boardItem.add(
299
gnomecanvas.CanvasPixbuf,
300
pixbuf = gcompris.utils.load_pixmap(file),
301
x=x*(self.boardSize/self.nbColumns),
304
self.stone.connect("event", self.columnItemEvent, 0)
305
self.countAnim = self.nbLines-y
306
self.timerAnim = gtk.timeout_add(200, self.animTimer)
307
self.timericon.gnomecanvas.show()
311
if self.countAnim > 0:
312
y= self.stone.get_property('y')
313
self.stone.set_property('y', y + (self.boardSize/self.nbColumns))
314
self.timerAnim = gtk.timeout_add(200, self.animTimer)
316
self.timerAnim = None
317
self.timericon.gnomecanvas.hide()
318
self.winLine = rules.isWinner(self.board, self.cur_player)
320
self.winner(self.cur_player)
321
elif rules.isBoardFull(self.board):
326
def winner(self, player):
330
# It's a draw, no line to draw
332
gcompris.bonus.display(gcompris.bonus.DRAW, gcompris.bonus.FLOWER)
335
# Display the winner line
336
self.scores[player-1] += 1
339
points = ( self.winLine[0][0]*(self.boardSize/self.nbColumns)+self.stoneSize/2,
340
(self.boardSize/self.nbColumns)*(self.nbLines-1-self.winLine[0][1])+self.stoneSize/2,
341
self.winLine[1][0]*(self.boardSize/self.nbColumns)+self.stoneSize/2,
342
(self.boardSize/self.nbColumns)*(self.nbLines-1-self.winLine[1][1])+self.stoneSize/2
345
self.redLine = self.boardItem.add(
346
gnomecanvas.CanvasLine,
347
fill_color_rgba=0xFF0000FFL,
351
self.redLine.set_property("cap-style", gtk.gdk.CAP_ROUND)
352
if player == 1 or self.mode == 2:
353
gcompris.bonus.display(gcompris.bonus.WIN, gcompris.bonus.FLOWER)
355
gcompris.bonus.display(gcompris.bonus.LOOSE, gcompris.bonus.FLOWER)