# This is a widget that handles a go game # It needs a goban object, and handles it from beginning to end import os import gtk import goban class GoGame(gtk.HBox): """ This class draws a board and some information about the game (turn, captures, etc). It also handles moves and other operations on the goban. """ img_res = None def __init__(self, goban, network_mode=False, our_color=None): super(GoGame, self).__init__() self.goban = goban if GoGame.img_res is None: GoGame.img_res = _build_img_res() self.init_widgets() def init_widgets(self): # All of this is to create the info box along the right side of the board info_box = gtk.VBox() info_rows = [] for i in range(3): info_rows.append(gtk.HBox()) to_move_label = gtk.Label('To Move:') black_cap_label = gtk.Label('Black Captures:') white_cap_label = gtk.Label('White Captures:') self.to_move_value = gtk.Label('None') self.black_cap_value = gtk.Label('0') self.white_cap_value = gtk.Label('0') info_rows[0].pack_start(to_move_label, expand=False, padding=5) info_rows[1].pack_start(black_cap_label, expand=False, padding=5) info_rows[2].pack_start(white_cap_label, expand=False, padding=5) info_rows[0].pack_end(self.to_move_value, expand=False, padding=5) info_rows[1].pack_end(self.black_cap_value, expand=False, padding=5) info_rows[2].pack_end(self.white_cap_value, expand=False, padding=5) for row in info_rows: info_box.pack_start(row, expand=False) self.pass_button = gtk.Button('Pass') self.resign_button = gtk.Button('Resign') self.pass_button.connect('clicked', self.on_pass) self.resign_button.connect('clicked', self.on_resign) info_box.pack_start(self.pass_button, expand=False, padding=10) info_box.pack_start(self.resign_button, expand=False, padding=10) self.board_area = gtk.DrawingArea() self.board_area.set_size_request(750,750) self.board_area.set_events(gtk.gdk.POINTER_MOTION_MASK | gtk.gdk.POINTER_MOTION_HINT_MASK | gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK) self.board_area.connect('expose-event', self.do_board_expose) self.board_area.connect('configure-event', self.do_board_configure) self.board_area.connect('motion-notify-event', self.do_hover) self.board_area.connect('button-press-event', self.do_play) self.pack_start(self.board_area, expand=False) self.pack_end(info_box, expand=False) self.update_info() def do_hover(self, widget, event): x = event.x y = event.y width, height = widget.size_request() size = min(width, height) board_size = self.goban.board_size inc = size / board_size row = int(y / inc) col = int(x / inc) old_hover = self.goban.hover if max(row,col) < board_size: if self.goban._real_pos((row,col)) != self.goban.hover: self.goban.set_hover((row,col)) self.update_board((old_hover, self.goban.hover)) else: self.goban.clear_hover() self.update_board([old_hover]) def do_play(self, widget, event): x = event.x y = event.y width, height = widget.size_request() size = min(width, height) board_size = self.goban.board_size inc = size / board_size row = int(y / inc) col = int(x / inc) changed = self.goban.play_move((row,col)) self.update(changed) def do_board_configure(self, widget, event): gc = widget.get_style().fg_gc[gtk.STATE_NORMAL] x,y,width,height = widget.get_allocation() self.board_backbuf = gtk.gdk.Pixmap(widget.window, width, height, depth=-1) self.board_backbuf.draw_rectangle(widget.get_style().white_gc, True, 0, 0, width, height) self.update() return True def do_board_expose(self, widget, event): x , y, width, height = event.area widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL], self.board_backbuf, x, y, x, y, width, height) return False def update_board(self, to_update=None): if to_update == None: to_update = range(len(self.goban.board)) gc = self.board_area.get_style().fg_gc[gtk.STATE_NORMAL] # this is a bit of a hack... width, height = self.board_area.get_size_request() size = min(width, height) board_size = self.goban.board_size inc = size / board_size for i in to_update: if i is None: continue # Get the right image and scale it base_img = GoGame.img_res[self.goban.draw_code(i)] img = base_img.scale_simple(inc, inc, gtk.gdk.INTERP_BILINEAR) row = i / board_size col = i % board_size self.board_backbuf.draw_pixbuf(gc, img, 0, 0, inc * col, inc * row, inc, inc) if i == self.goban.hover: if self.goban.to_move == goban.Goban.BLACK: code = 'bH' elif self.goban.to_move == goban.Goban.WHITE: code = 'wH' base_img = GoGame.img_res[code] img = base_img.scale_simple(inc, inc, gtk.gdk.INTERP_BILINEAR) self.board_backbuf.draw_pixbuf(gc, img, 0, 0, inc * col, inc * row, inc, inc) self.board_area.queue_draw() def on_pass(self, widget): changed = self.goban.pass_move() self.update(changed) def on_resign(self, widget): changed = self.goban.resign() self.update(changed) # fixme: Add a widget to show the outcome def update_info(self): if self.goban.to_move == goban.Goban.BLACK: move = 'Black' elif self.goban.to_move == goban.Goban.WHITE: move = 'White' else: move = 'None' self.to_move_value.set_text(move) self.black_cap_value.set_text(str(self.goban.black_captures)) self.white_cap_value.set_text(str(self.goban.white_captures)) def update(self, changed=None): self.update_info() self.update_board(changed) def _magnitude(vector): x,y = vector return math.sqrt(x*x + y*y) def _build_img_res(): ret = {} triangle = _load_png('go_triangle.png') circle_red = _load_png('go_circle_red.png') circle_black = _load_png('go_circle_black.png') circle_white = _load_png('go_circle_white.png') square = _load_png('go_square.png') ret['wH'] = _load_png('go_wH.png') ret['bH'] = _load_png('go_bH.png') ret['wT'] = _load_png('go_w.png') width = ret['wT'].get_width() height = ret['wT'].get_height() triangle.composite(ret['wT'], 0, 0, width, height, 0, 0, 1, 1, gtk.gdk.INTERP_NEAREST, 255) ret['bT'] = _load_png('go_b.png') width = ret['bT'].get_width() height = ret['bT'].get_height() triangle.composite(ret['bT'], 0, 0, width, height, 0, 0, 1, 1, gtk.gdk.INTERP_NEAREST, 255) base = _load_png('go_corner.png') ret['ul'] = base ret['dl'] = base.copy().rotate_simple(90) ret['dr'] = base.copy().rotate_simple(180) ret['ur'] = base.copy().rotate_simple(270) base = _load_png('go_edge.png') ret['u'] = base ret['l'] = base.copy().rotate_simple(90) ret['d'] = base.copy().rotate_simple(180) ret['r'] = base.copy().rotate_simple(270) for d in ('m', 'h', 'w', 'b'): ret[d] = _load_png('go_' + d + '.png') for d in ('u', 'd', 'l', 'r', 'm', 'dl', 'dr', 'ul', 'ur', 'h', 'w', 'b'): ret[d + 'Cr'] = ret[d].copy() width = ret[d + 'Cr'].get_width() height = ret[d + 'Cr'].get_height() circle_red.composite(ret[d + 'Cr'], 0, 0, width, height, 0, 0, 1, 1, gtk.gdk.INTERP_NEAREST, 255) ret[d + 'Cw'] = ret[d].copy() width = ret[d + 'Cw'].get_width() height = ret[d + 'Cw'].get_height() circle_white.composite(ret[d + 'Cw'], 0, 0, width, height, 0, 0, 1, 1, gtk.gdk.INTERP_NEAREST, 255) ret[d + 'Cb'] = ret[d].copy() width = ret[d + 'Cb'].get_width() height = ret[d + 'Cb'].get_height() circle_black.composite(ret[d + 'Cb'], 0, 0, width, height, 0, 0, 1, 1, gtk.gdk.INTERP_NEAREST, 255) ret[d + 'S'] = ret[d].copy() width = ret[d + 'S'].get_width() height = ret[d + 'S'].get_height() square.composite(ret[d + 'S'], 0, 0, width, height, 0, 0, 1, 1, gtk.gdk.INTERP_NEAREST, 255) return ret def _load_png(name): """ Load image and return image object""" fullname = os.path.join('ui/res/', name) image = gtk.gdk.pixbuf_new_from_file(fullname) return image