From 65884d06c39bde9e109c6f1235623f35f97f7950 Mon Sep 17 00:00:00 2001 From: Andrés Ambrois Date: Fri, 15 Aug 2008 06:29:34 +0000 Subject: Added new v2 code. New Features: - Cleaner code - Save and resume games from Journal - Added a toolbar with options to set board size & restart the game. - Better collaboration (turn enforcement, notifications) - Pass and Undo in hotseat (not shared) mode. - Full Spanish translation --- (limited to 'activity.py') diff --git a/activity.py b/activity.py new file mode 100644 index 0000000..f95ae67 --- /dev/null +++ b/activity.py @@ -0,0 +1,288 @@ +# -*- coding: UTF-8 -*- +# Copyright 2007-2008 One Laptop Per Child +# Copyright 2007 Gerard J. Cerchio +# Copyright 2008 Andrés Ambrois +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import logging +import sugar.logger + +from gettext import gettext as _ + +import cPickle +import gtk +from sugar.activity.activity import Activity, ActivityToolbox + +from gametoolbar import GameToolbar +from gogame import GoGame +import boardwidget +import infopanel +from collaboration import CollaborationWrapper + + +logger = logging.getLogger('PlayGo') + +DEFAULT_SIZE = 19 + +class PlayGo(Activity): + def __init__(self, handle): + # Initialize the parent + Activity.__init__(self, handle) + logger.debug('Initiating PlayGo') + + self.size = DEFAULT_SIZE + + # Set the activity toolbox + toolbox = ActivityToolbox(self) + self.set_toolbox(toolbox) + self.gameToolbar = GameToolbar(self) + toolbox.add_toolbar(_('Game'), self.gameToolbar) + self.gameToolbar.connect('game-restart', self.restart_game) + self.gameToolbar.connect('game-board-size', self.board_size_change) + self.gameToolbar.show() + + # Initialize the game + self.game = GoGame(self.size) + self.CurrentColor = 'B' + self.PlayerColor = 'B' + self.set_up_ui() + + if not handle.object_id: + self.infopanel.show(_('Welcome to PlayGo!')) + else: + self.show_score() + self.lastX = -1 + self.lastY = -1 + + #Set up collaboration + self.collaboration = CollaborationWrapper(self, + self.buddy_joined, + self.buddy_left, + self.Play, + self.game.undostack, + self.bootstrap) + + self.connect('shared', self.collaboration._shared_cb) + if self._shared_activity: + # We are joining the activity + self.connect('joined', self.collaboration._joined_cb) + if self.get_shared(): + # We've already joined + self.collaboration._joined_cb() + + def set_up_ui(self): + self.board = boardwidget.GoBoardWidget(self.game.get_status(), self.size) + self.board.connect('motion-notify-event', self.board_motion_cb) + self.board.connect('insert-requested', self.insert_cb) + + self.main_view = gtk.VBox() + + self.board_aspect = gtk.AspectFrame(None, .5, .5, 1, False) + self.board_aspect.add(self.board) + self.main_view.pack_start(self.board_aspect) + + self.buttons_box = gtk.HBox() + self.buttons_alignment = gtk.Alignment(0.5, 1, 0.5, 1) + #Pass button + self.pass_button = gtk.Button(_('Pass')) + self.pass_button.connect("clicked", self.pass_cb) + self.buttons_box.pack_start(self.pass_button, True, True, 10) + + #Undo button + self.undo_button = gtk.Button(_('Undo')) + self.undo_button.connect("clicked", self.undo_cb) + self.buttons_box.pack_start(self.undo_button, True, True, 10) + + self.buttons_alignment.add(self.buttons_box) + self.main_view.pack_start(self.buttons_alignment, False, padding=10) + + self.infopanel = infopanel.InfoPanel() + self.main_view.pack_start(self.infopanel, False) + + self.set_canvas(self.main_view) + self.show_all() + + def insert_cb(self, widget, x, y, announce=True): + if announce and self.get_currentcolor() != self.get_playercolor(): + logger.debug('Play at %s x %s was out-of-turn!', x, y) + self.infopanel.show('It\'s not your turn!') + return False + # Make the play only if it wasn't a pass move. + if x != -1: + error = self.game.illegal(x, y, self.get_currentcolor()) + if error: + self.infopanel.show(error) + return False + # Make the play + captures = self.game.play((x, y), self.get_currentcolor()) + self.gameToolbar.grey_out_size_change() + if captures: self.redraw_captures(captures) + self.show_score() + self.board.draw_stone(x, y, self.get_currentcolor(), widget) + # Announce the local play + if self.get_shared() and announce: + self.collaboration.Play(x, y) + self.change_turn() + if not self.get_shared(): self.change_player_color() + + def undo_cb(self, widget, data=None): + if self.game.undo(): + self.board.queue_draw() + self.change_turn() + if not self.get_shared(): self.change_player_color() + + def pass_cb(self, widget, data=None): + if self.get_shared(): + if self.get_currentcolor() == self.get_playercolor(): + self.collaboration.Play(-1, -1) + else: + self.infopanel.show('It\'s not your turn!') + return + else: + self.change_player_color() + self.change_turn() + + def write_file(self, file_path): + logger.debug('Writing file: %s', file_path) + # Strip the undostack + undostack = self.game.undostack[:] + strippedstack = [] + for pos, color, captures in undostack: + strippedstack.append(pos) + f = open(file_path, 'w') + try: + cPickle.dump(strippedstack, f, cPickle.HIGHEST_PROTOCOL) + finally: + f.close() + self.metadata['our-color'] = self.get_playercolor() + self.metadata['shared'] = str(self.get_shared()) + self.metadata['size'] = str(self.size) + + def read_file(self, file_path): + logger.debug('Reading file: %s', file_path) + f = open(file_path, 'r') + try: + newstack = cPickle.load(f) + finally: + f.close() + if self.get_shared(): + logger.debug('The game we are loading is shared!') + self.PlayerColor = self.metadata.get('our-color', 'B') + if self.size != self.metadata.get('size', DEFAULT_SIZE): + self.board_size_change(None, int(self.metadata.get('size', DEFAULT_SIZE))) + self.bootstrap(newstack) + + def board_motion_cb(self, widget, event): + x, y = self.board.get_mouse_event_xy(event) + if x == self.lastX and y == self.lastY: + return + self.lastX = x + self.lastY = y + if not self.game.is_occupied(x, y) and self.game.legal((x, y), self.get_playercolor()): + self.board.draw_ghost_stone(x, y, self.get_playercolor()) + + def invert_color(self, color): + if color == 'B': return 'W' + return 'B' + + def get_currentcolor(self): + return self.CurrentColor + + def change_turn(self): + # It's the other guy's turn now + if self.CurrentColor == 'B': + self.infopanel.show('White\'s turn') + else: + self.infopanel.show('Black\'s turn') + self.CurrentColor = self.invert_color(self.get_currentcolor()) + + def get_playercolor(self): + return self.PlayerColor + + def change_player_color(self): + self.PlayerColor = self.invert_color(self.get_playercolor()) + + def set_player_color(self, color): + self.PlayerColor = color + + def redraw_captures(self, captures): + for x in captures: + self.board.redraw_area(x[0], x[1]) + + def bootstrap(self, plays): + ''' Take our game to the state it would have if @plays were manually played''' + logger.debug('Bootstraping...') + self.board.do_expose_event() # HACK: Looks like read_file is called before the board is exposed + for pos in plays: + logger.debug('Playing at %s with color %s', pos, self.get_currentcolor()) + captures = self.game.play((pos[0], pos[1]), self.get_currentcolor()) + if captures: self.redraw_captures(captures) + self.change_turn() + self.change_player_color() + logger.debug('Color after bootstraping is %s', self.get_currentcolor()) + self.show_score() + self.board.do_expose_event() + + def restart_game(self, widget=None): + logger.debug('Received restart signal!') + self.game.clear() + self.board.status = self.game.status + self.board.do_expose_event() + self.show_score() + + def board_size_change(self, widget, size): + if size == self.size: + return + self.size = size + del self.game + self.game = GoGame(size) + self.board_aspect.remove(self.board) + del self.board + self.board = boardwidget.GoBoardWidget(self.game.get_status(), int(size)) + self.board_aspect.add(self.board) + self.board.connect('motion-notify-event', self.board_motion_cb) + self.board.connect('insert-requested', self.insert_cb) + self.board.show() + + def show_score(self): + self.infopanel.show_score(_("Score is: Whites %(W)d - Blacks %(B)d" % self.game.get_score())) + + def _alert(self, title, text=None): + from sugar.graphics.alert import NotifyAlert + alert = NotifyAlert(timeout=5) + alert.props.title = title + alert.props.msg = text + self.add_alert(alert) + alert.connect('response', self._alert_cancel_cb) + alert.show() + + def _alert_cancel_cb(self, alert, response_id): + self.remove_alert(alert) + + # ------- Callbacks for Collaboration -------- # + def buddy_joined(self, buddy): + self._alert(_('Buddy joined'), _('%s joined' % buddy.props.nick)) + + def buddy_left(self, buddy): + self._alert(_('Buddy left'), _('%s left' % buddy.props.nick)) + + def Play(self, x, y, sender=None): + ''' Called when a stone was placed at x,y by sender''' + # Discard a pass move received in our turn. Do it here for extra security + if x == -1 and self.get_currentcolor() == self.get_playercolor(): + return + self.insert_cb(None, x, y, False) + -- cgit v0.9.1