From de4840470b8d27be26a4fccf0d72973219832f08 Mon Sep 17 00:00:00 2001 From: Andrés Ambrois Date: Thu, 28 Aug 2008 19:09:30 +0000 Subject: It plays!: Add support for GnuGo AI --- diff --git a/MANIFEST b/MANIFEST index 68f8d82..f08db2a 100644 --- a/MANIFEST +++ b/MANIFEST @@ -4,6 +4,7 @@ collaboration.py boardwidget.py gogame.py infopanel.py +gtp.py activity/activity-go.svg activity/activity.info images/b.gif diff --git a/activity.py b/activity.py index eaf8339..c0a49a2 100644 --- a/activity.py +++ b/activity.py @@ -31,6 +31,7 @@ from gogame import GoGame import boardwidget import infopanel from collaboration import CollaborationWrapper +from gtp import gnugo logger = logging.getLogger('PlayGo') @@ -52,12 +53,15 @@ class PlayGo(Activity): 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.connect('ai-activated', self.ai_activated_cb) + self.gameToolbar.connect('ai-deactivated', self.ai_deactivated_cb) self.gameToolbar.show() # Initialize the game self.game = GoGame(self.size) self.CurrentColor = 'B' self.PlayerColor = 'B' + self.ai_activated = False self.set_up_ui() if not handle.object_id: @@ -115,7 +119,18 @@ class PlayGo(Activity): self.set_canvas(self.main_view) self.show_all() - def insert_cb(self, widget, x, y, announce=True): + def insert_cb(self, widget, x, y, announce=True, ai_play=False): + ''' The insert function. It makes the play and manages turn changing + stone drawing, etc. + + Parameters x and y are the coordinates of the play ((0,0) is top left), + widget points to the widget that emitted the signal connected to this + function, announce is True when we need to announce this play to + other people collaborating, and ai_play is True when this is called + by the AI, so we know not to ask for an AI play again ''' + + # Check if it's our turn only if it's a local play (announce is True) + # Calls by other players will always be out of turn for us. 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!') @@ -128,6 +143,8 @@ class PlayGo(Activity): return False # Make the play captures = self.game.play((x, y), self.get_currentcolor()) + if self.ai_activated and not ai_play: + self.notify_ai(x, y, self.get_currentcolor()) self.gameToolbar.grey_out_size_change() if captures: self.redraw_captures(captures) self.show_score() @@ -136,14 +153,28 @@ class PlayGo(Activity): if self.get_shared() and announce: self.collaboration.Play(x, y) self.change_turn() - if not self.get_shared(): self.change_player_color() + # If we are playing a local game with AI turned off, change the color + if not self.get_shared() and not self.ai_activated: + self.change_player_color() + # Else, if the AI is on, and this wasn't played by it, request a play by it. + elif self.ai_activated: + self.change_player_color() + if not ai_play: + self.play_ai() def undo_cb(self, widget, data=None): if self.game.undo(): self.board.queue_draw() + # If playing against AI undo twice + if self.ai_activated: + self.ai.undo() + self.game.undo() + self.ai.undo() + else: + self.change_turn() + if not self.get_shared() and not self.ai_activated: + self.change_player_color() self.show_score() - self.change_turn() - if not self.get_shared(): self.change_player_color() def pass_cb(self, widget, data=None): if self.get_shared(): @@ -243,6 +274,8 @@ class PlayGo(Activity): self.board.status = self.game.status self.board.do_expose_event() self.show_score() + if self.ai_activated: + self.ai.clear() def board_size_change(self, widget, size): if size == self.size: @@ -257,6 +290,32 @@ class PlayGo(Activity): self.board.connect('motion-notify-event', self.board_motion_cb) self.board.connect('insert-requested', self.insert_cb) self.board.show() + if self.ai_activated: + del self.ai + self.ai = gnugo(boardsize=self.size) + + def ai_activated_cb(self, widget): + self.restart_game() + self.ai_activated = True + self.ai = gnugo(boardsize=self.size) + self._alert(_('AI'), _('PlayGo AI Activated')) + + def ai_deactivated_cb(self, widget): + self.ai_activated = False + del self.ai + self._alert(_('AI'), _('PlayGo AI Deactivated')) + + def notify_ai(self, x, y, color): + if color == self.get_playercolor(): + logger.debug('Notifying AI of play by %s at %s x %s', color, x, y) + self.ai.make_play(color, x, y) + + def play_ai(self): + if self.get_currentcolor() == self.get_playercolor(): + x, y = self.ai.get_move(self.get_currentcolor()) + logger.debug('Got play %s x %s from AI', x, y) + self.insert_cb(None, x, y, ai_play=True) + #logger.debug('Dumping board: %s', self.ai.dump_board()) def show_score(self): self.infopanel.show_score(_("Score is: Whites %(W)d - Blacks %(B)d" % self.game.get_score())) diff --git a/collaboration.py b/collaboration.py index 38f926e..789eb42 100644 --- a/collaboration.py +++ b/collaboration.py @@ -49,6 +49,7 @@ class CollaborationWrapper(ExportedGObject): def _shared_cb(self, activity): self.activity.gameToolbar.grey_out_size_change() self.activity.gameToolbar.grey_out_restart() + self.activity.gameToolbar.grey_out_ai() self._sharing_setup() self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( SERVICE, {}) @@ -58,6 +59,7 @@ class CollaborationWrapper(ExportedGObject): def _joined_cb(self, activity): self.activity.gameToolbar.grey_out_size_change() self.activity.gameToolbar.grey_out_restart() + self.activity.gameToolbar.grey_out_ai() self._sharing_setup() self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( reply_handler=self._list_tubes_reply_cb, diff --git a/gametoolbar.py b/gametoolbar.py index 834b1e0..d512593 100644 --- a/gametoolbar.py +++ b/gametoolbar.py @@ -27,6 +27,8 @@ from sugar.graphics.objectchooser import ObjectChooser import logging from gobject import SIGNAL_RUN_FIRST, TYPE_PYOBJECT, TYPE_NONE, TYPE_INT +from gtp import search_for_gnugo + logger = logging.getLogger('PlayGo') class GameToolbar(gtk.Toolbar): @@ -34,6 +36,8 @@ class GameToolbar(gtk.Toolbar): __gsignals__ = { 'game-restart': (SIGNAL_RUN_FIRST, TYPE_NONE, []), + 'ai-activated': (SIGNAL_RUN_FIRST, TYPE_NONE, []), + 'ai-deactivated': (SIGNAL_RUN_FIRST, TYPE_NONE, []), 'game-board-size': (SIGNAL_RUN_FIRST, TYPE_NONE, [TYPE_INT]), } @@ -67,6 +71,22 @@ class GameToolbar(gtk.Toolbar): self._add_widget(self._size_combo) self._size_combo.combo.set_active(0) + # Separator + separator = gtk.SeparatorToolItem() + separator.set_draw(True) + self.insert(separator, -1) + + # Artificial Intelligence Button + self._ai_button = gtk.ToggleToolButton() + if search_for_gnugo(): + self._ai_button.connect('toggled', self._ai_toggled_cb) + self._ai_button.set_label(_('Play against PlayGo!')) + else: + self._ai_button.set_label(_('You need to install gnugo to play against PlayGo')) + self._ai_button.set_sensitive(False) + self.insert(self._ai_button, -1) + self._ai_button.show() + def _add_widget(self, widget, expand=False): tool_item = gtk.ToolItem() tool_item.set_expand(expand) @@ -95,3 +115,12 @@ class GameToolbar(gtk.Toolbar): size_index = self._sizes.index(size+' X '+size) self._size_combo.combo.set_active(int(size_index)) self._size_combo.combo.handler_unblock(self.size_handle_id) + + def _ai_toggled_cb(self, widget): + if widget.get_active(): + self.emit('ai-activated') + else: + self.emit('ai-deactivated') + + def grey_out_ai(self): + self._ai_button.set_sensitive(False) diff --git a/gtp.py b/gtp.py index 4f028e1..7484658 100644 --- a/gtp.py +++ b/gtp.py @@ -49,13 +49,15 @@ class gnugo: self.stdout = self.gnugo.stdout def __del__(self): + logger.debug('Closing gnugo') self.stdin.write('quit \n') + self.stdin.flush() def _xy_to_coords(self, x, y): - return dict(zip(range(0, 25), 'ABCDEFGHJKLMNOPQRSTUVWXYZ'))[x] + str(self.size - y) + return dict(zip(range(25), 'ABCDEFGHJKLMNOPQRSTUVWXYZ'))[x] + str(self.size - y) def _coords_to_xy(self, coords): - return int(dict(zip('ABCDEFGHJKLMNOPQRSTUVWXYZ', range(0, 25)))[coords[0]]), self.size - int(coords[1:]) + return int(dict(zip('ABCDEFGHJKLMNOPQRSTUVWXYZ', range(25)))[coords[0]]), self.size - int(coords[1:]) def short_to_long_colors(self, short_color): if short_color == 'B': @@ -88,15 +90,21 @@ class gnugo: return self._coords_to_xy(output[2:]) def undo(self): - self.stdin.write('undo \n') + self.stdin.write('undo\n') + self.stdin.flush() + self.stdout.readline() + self.stdout.readline() + + def clear(self): + self.stdin.write('clear_board\n') self.stdin.flush() self.stdout.readline() self.stdout.readline() def dump_board(self): - self.stdin.write('showboard \n') + self.stdin.write('showboard\n') self.stdin.flush() output = '' for i in range(0, self.size+4): output = output + self.stdout.readline() - logger.debug('%s', output) + return output -- cgit v0.9.1