diff options
Diffstat (limited to 'game.py')
-rwxr-xr-x | game.py | 434 |
1 files changed, 0 insertions, 434 deletions
diff --git a/game.py b/game.py deleted file mode 100755 index cb51a1c..0000000 --- a/game.py +++ /dev/null @@ -1,434 +0,0 @@ -import logging - -from gettext import gettext as _ -import gtk - -from dbus import Interface -from dbus.service import method, signal -from dbus.gobject_service import ExportedGObject - - -# XXX: I'm not convinced this is in the right namespace -SERVICE = "org.freedesktop.Telepathy.Tube.Connect" -IFACE = SERVICE -PATH = "/org/freedesktop/Telepathy/Tube/Connect" - - -_logger = logging.getLogger('PlayGo') - - -def redraw(grid): - """Utility function to force a redraw of a Gtk widget.""" - grid.queue_draw() - - -def dump_grid(seq): - grid = '' - for row in seq: - row_str = '' - for col in row: - if col == -1: - row_str += ' |' - else: - row_str += '%d|' % col - grid = '%s%s\n' % (grid, row_str) - _logger.debug('Grid state is now:\n%s', grid) - - -## This abstractBoard is part of Kombilo, a go database program -## It contains a class implementing an abstract go board -## Copyright (C) 2001-3 Ulrich Goertz (u@g0ertz.de) - -class abstractBoard: - """ This class administrates a go board. - It keeps track of the stones currently on the board in the dictionary self.status, - and of the moves played so far in self.undostack - - It has methods to clear the board, play a stone, undo a move. """ - - def __init__(self, boardSize = 19): - self.size = boardSize #TODO: get rid of this - self.status = {} - self.undostack = [] - self.boardSize = boardSize - self.score = {'B' : 0, 'W' : 0} - _logger.setLevel( logging.DEBUG ) - _logger.debug( "init board size %d", boardSize ) - - def increase_score(self, color): - self.score[color] = self.score[color] + 1 - - def neighbors(self,x): - """ Returns the coordinates of the 4 (resp. 3 resp. 2 at the side 1 in the corner) intersections - adjacent to the given one. """ - if x[0]== 0 : l0 = [1] - elif x[0]== self.boardSize-1 : l0 = [self.boardSize-2] - else: l0 = [x[0]-1, x[0]+1] - - if x[1]== 0 : l1 = [1] - elif x[1]== self.boardSize-1 : l1 = [self.boardSize-2] - else: l1 = [x[1]-1, x[1]+1] - - l = [] - for i in l0: l.append((i,x[1])) - for j in l1: l.append((x[0],j)) - - return l - - def clear(self): - """ Clear the board """ - self.status = {} - self.undostack=[] - - def play(self,pos,color): - """ This plays a color=black/white stone at pos, if that is a legal move - (disregarding ko), and deletes stones captured by that move. - It returns 1 if the move has been played, 0 if not. """ - if self.status.has_key(pos): # check if empty - return 0 - - if self.legal(pos,color): # legal move? - self.status[pos] = color - captures = self.get_captures(pos, color) - if captures: - for x in captures: - del self.status[x] # remove captured stones, if any - self.increase_score(self.invert(color)) - self.undostack.append((pos,color,captures)) # remember move + captured stones for easy undo - return self.score - else: - return 0 - - def get_captures(self, pos, color): - """Returns a list of captured stones resulting from placing a color stone at pos """ - c = [] # captured stones - - for x in self.neighbors(pos): - if self.status.has_key(x) and self.status[x]==self.invert(color): - c = c + self.hasNoLibExcP(x, self.invert(color), pos) - - if c: - captures = [] - for x in c: - if not x in captures: captures.append(x) - return captures - - return 0 - - def checkKo(self, pos, color): - ''' - Check if a move by color at pos would be a basic Ko infraction - ''' - # Basically what we need to check, is if the current play would undo - # all that was done by the last entry in undostack (capture what was placed - # and place what was captured). - if self.undostack: - lastpos, lastcolor, lastcaptures = self.undostack[-1] - currentcaptures = self.get_captures(pos, color) - if lastcaptures != 0 and currentcaptures != 0: - if lastcolor != color and lastcaptures[0] == pos and lastpos == currentcaptures[0]: - return 1 - return 0 - - def legal(self, pos, color): - """ Check if a play by color at pos would be a legal move. """ - if self.status.has_key(pos): - return 0 - - # If the play at pos would leave that stone without liberties, we have two possibilities: - # 1- It's a capturing move - # 2- It's an illegal move - if self.hasNoLibExcP(pos, color): - # Check if it would capture any stones - if self.get_captures(pos, color): - return 1 - # It didnt, so I guess it's illegal - return 0 - else: return 1 - - def hasNoLibExcP(self, pos, color, exc = None): - """ This function checks if the string (=solidly connected) of stones containing - the stone at pos has a liberty (resp. has a liberty besides that at exc). - If no liberties are found, a list of all stones in the string is returned. - - The algorithm is a non-recursive implementation of a simple flood-filling: - starting from the stone at pos, the main while-loop looks at the intersections - directly adjacent to the stones found so far, for liberties or other stones that belong - to the string. Then it looks at the neighbors of those newly found stones, and so - on, until it finds a liberty, or until it doesn't find any new stones belonging - to the string, which means that there are no liberties. - Once a liberty is found, the function returns immediately. """ - - st = [] # in the end, this list will contain all stones solidly connected to the - # one at pos, if this string has no liberties - newlyFound = [pos] # in the while loop, we will look at the neighbors of stones in newlyFound - foundNew = 1 - - while foundNew: - foundNew = 0 - n = [] # this will contain the stones found in this iteration of the loop - for x in newlyFound: - for y in self.neighbors(x): - if not self.status.has_key(y) and y != exc and y != pos: # found a liberty - return [] - elif self.status.has_key(y) and self.status[y]==color \ - and not y in newlyFound and not y in st: # found another stone of same color - n.append(y) - foundNew = 1 - - st[:0] = newlyFound - newlyFound = n - - return st # no liberties found, return list of all stones connected to the original one - - def undo(self, no=1): - """ Undo the last no moves. """ - for i in range(no): - if self.undostack: - pos, color, captures = self.undostack.pop() - del self.status[pos] - if captures: - for p in captures: self.status[p] = self.invert(color) - - def remove(self, pos): - """ Remove a stone form the board, and store this action in undostack. """ - - self.undostack.append(((-1,-1), self.invert(self.status[pos]), [pos])) - del self.status[pos] - - def invert(self,color): - if color == 'B': return 'W' - else: return 'B' - - def setPointi( self, x, y, value ): - - color = 'W' - if value == 1 : color = 'B' - - _logger.debug( "Setting Point %d, %d to color %s", x, y, color ) - - return self.play( (x,y), color ) - - - - -class GoGame(ExportedGObject): - - def __init__(self, tube, boardwidget, is_initiator, buddies_panel, info_panel, - owner, get_buddy, activity): - - super(GoGame, self).__init__(tube, PATH) - - self.tube = tube - self.boardWidget = boardwidget - self.is_initiator = is_initiator - self.entered = False - self.player_id = None - self.active_player = 1 - self.buddies_panel = buddies_panel - self.info_panel = info_panel - self.owner = owner - self._get_buddy = get_buddy - self.activity = activity - - boardwidget.myGame = self - - # list indexed by player ID - # 0, 1 are players 0, 1 - # 2+ are the spectator queue, 2 is to play next - self.ordered_bus_names = [] - - self.tube.watch_participants(self.participant_change_cb) - self.boardWidget.connect('insert-requested', self.insert_requested_cb) - - - def participant_change_cb(self, added, removed): - # Initiator is player 0, other player is player 1. - - _logger.debug('adding participants: %r', added) - _logger.debug('removing participants: %r', removed) - - for handle, bus_name in added: - buddy = self._get_buddy(handle) - _logger.debug('Buddy %r was added', buddy) - if buddy is not None: - self.buddies_panel.add_watcher(buddy) - - for handle in removed: - buddy = self._get_buddy(handle) - _logger.debug('Buddy %r was removed', buddy) - if buddy is not None: - self.buddies_panel.remove_watcher(buddy) - try: - self.ordered_bus_names.remove(self.tube.participants[handle]) - except ValueError: - # already absent - pass - - if not self.entered: - self.tube.add_signal_receiver(self.insert_cb, 'Insert', IFACE, - path=PATH, sender_keyword='sender') - if self.is_initiator: - _logger.debug('I am the initiator, so making myself player 0') - self.add_hello_handler() - self.ordered_bus_names = [self.tube.get_unique_name()] - self.player_id = 0 - self.buddies_panel.add_player(self.owner) - else: - _logger.debug('Hello, everyone! What did I miss?') - self.Hello() - self.entered = True - - @signal(dbus_interface=IFACE, signature='') - def Hello(self): - """Request that this player's Welcome method is called to bring it - up to date with the game state. - """ - - @method(dbus_interface=IFACE, in_signature='aanas', out_signature='') - def Welcome(self, aBoard, bus_names): - """To be called on the incoming player by the other players to - inform them of the game state. - - FIXME: nominate a "referee" (initially the initiator) responsible - for saying Welcome, elect a new referee when the current referee - leaves? This could also be used to make the protocol robust against - cheating/bugs - """ - if self.player_id is None: - _logger.debug('Welcomed to the game. Player bus names are %r', bus_names) - self.boardWidget.myBoard.board = aBoard - dump_grid( self.boardWidget.myBoard.status ) - self.ordered_bus_names = bus_names - self.player_id = bus_names.index(self.tube.get_unique_name()) - # OK, now I'm synched with the game, I can welcome others - self.add_hello_handler() - - buddy = self._get_buddy(self.tube.bus_name_to_handle[bus_names[0]]) - self.buddies_panel.add_player(buddy) - buddy = self._get_buddy(self.tube.bus_name_to_handle[bus_names[1]]) - self.buddies_panel.add_player(buddy) - - if self.get_active_player() == self.player_id: - _logger.debug("It's my turn already!") - self.change_turn() - - redraw( self.boardWidget ) - else: - _logger.debug("I've already been welcomed, doing nothing") - - def add_hello_handler(self): - self.tube.add_signal_receiver(self.hello_cb, 'Hello', IFACE, - path=PATH, sender_keyword='sender') - - @signal(dbus_interface=IFACE, signature='i') - def Insert(self, column): - """Signal that the local player has placed a disc.""" - #assert column >= self.boardWidget.myBoard.size - #assert column < self.boardWidget.myBoard.size - - def hello_cb(self, sender=None): - - """Tell the newcomer what's going on.""" - _logger.debug('Newcomer %s has joined', sender) - self.ordered_bus_names.append(sender) - - if len(self.ordered_bus_names) == 2: - buddy = self._get_buddy(self.tube.bus_name_to_handle[sender]) - self.buddies_panel.add_player(buddy) - - _logger.debug('Bus names are now: %r', self.ordered_bus_names) - _logger.debug('Welcoming newcomer and sending them the game state:') - dump_grid( self.boardWidget.myBoard.status ) - - self.tube.get_object(sender, PATH).Welcome(self.boardWidget.myBoard.status, - self.ordered_bus_names, - dbus_interface=IFACE) - - if (self.player_id == 0 and len(self.ordered_bus_names) == 2): - _logger.debug("This is my game and an opponent has joined. I go first") - self.change_turn() - - def insert_cb(self, column, sender=None): - # Someone placed a stone - handle = self.tube.bus_name_to_handle[sender] - _logger.debug('Insert(%d) from %s', column, sender) - - if self.tube.self_handle == handle: - _logger.debug('Ignoring Insert signal from myself: %d', column) - return - - try: - winner = self.boardWidget.insert(column, self.get_active_player()) - except ValueError: - return - - dump_grid(self.boardWidget.myBoard.status) - - if winner is not None: - _logger.debug('Player with handle %d wins', handle) - self.info_panel.show(_('The other player wins!')) - redraw(self.boardWidget) - return - - self.change_turn() - - def change_turn(self): - try: - bus_name = self.ordered_bus_names[self.get_active_player()] - buddy = self._get_buddy(self.tube.bus_name_to_handle[bus_name]) - self.buddies_panel.set_is_playing(buddy) - except: - _logger.error('argh!', exc_info=1) - raise - - if self.get_active_player() == self.player_id: - _logger.debug('It\'s my turn now') - self.info_panel.show(_('Your turn')) - self.activity.grab_focus() - else: - _logger.debug('It\'s not my turn') - self.boardWidget.selected_column = None - - redraw(self.boardWidget) - - def get_active_player(self): - - return 1 - - def key_press_event(self, widget, event): - - _logger.debug('Keypress: keyval %s', event.keyval) - - if event.keyval in (gtk.keysyms.Left,): - _logger.debug('<--') - if self.boardWidget.selected_column > 0: - self.boardWidget.selected_column -= 1 - redraw(self.boardWidget) - elif event.keyval in (gtk.keysyms.Right,): - _logger.debug('-->') - if self.boardWidget.selected_column < 6: - self.boardWidget.selected_column += 1 - redraw(self.boardWidget) - elif event.keyval in (gtk.keysyms.Down, gtk.keysyms.space): - _logger.debug('v') - self.insert_requested_cb(self.boardWidget, self.boardWidget.selected_column) - - def insert_requested_cb(self, grid, col): - - _logger.debug('Inserting at %d', col) - winner = grid.insert(col, self.player_id) - if winner == -1: - return - - dump_grid(grid.myBoard.status) - redraw(grid) - self.Insert(col) - - self.change_turn() - - if winner is not None: - _logger.debug("I win") - self.info_panel.show(_('You win!')) - else: - self.info_panel.show(_('Other player\'s turn')) |