2012-04-17 05:01:34 +00:00
|
|
|
# This is a widget that handles a go game
|
|
|
|
# It needs a goban object, and handles it from beginning to end
|
|
|
|
|
2012-04-17 05:21:39 +00:00
|
|
|
import os
|
2012-04-17 05:01:34 +00:00
|
|
|
import gtk
|
2012-04-17 05:21:39 +00:00
|
|
|
|
|
|
|
import goban
|
2012-04-17 05:01:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
"""
|
|
|
|
|
2012-04-17 05:46:55 +00:00
|
|
|
img_res = None
|
2012-04-17 05:01:34 +00:00
|
|
|
|
2012-04-17 18:59:46 +00:00
|
|
|
def __init__(self, goban, network_mode=False, our_color=None):
|
2012-04-17 05:21:39 +00:00
|
|
|
super(GoGame, self).__init__()
|
2012-04-17 05:01:34 +00:00
|
|
|
|
|
|
|
self.goban = goban
|
|
|
|
|
2012-04-17 05:46:55 +00:00
|
|
|
if GoGame.img_res is None:
|
|
|
|
GoGame.img_res = _build_img_res()
|
|
|
|
self.init_widgets()
|
2012-04-17 05:01:34 +00:00
|
|
|
|
|
|
|
|
2012-04-17 05:46:55 +00:00
|
|
|
def init_widgets(self):
|
2012-04-17 05:21:39 +00:00
|
|
|
# All of this is to create the info box along the right side of the board
|
2012-04-17 05:01:34 +00:00
|
|
|
info_box = gtk.VBox()
|
2012-04-17 05:21:39 +00:00
|
|
|
info_rows = []
|
2012-04-24 01:30:31 +00:00
|
|
|
for i in range(2):
|
2012-04-17 05:39:16 +00:00
|
|
|
info_rows.append(gtk.HBox())
|
2012-04-17 05:21:39 +00:00
|
|
|
|
2012-04-17 05:01:34 +00:00
|
|
|
to_move_label = gtk.Label('To Move:')
|
|
|
|
black_cap_label = gtk.Label('Black Captures:')
|
|
|
|
white_cap_label = gtk.Label('White Captures:')
|
|
|
|
|
2012-04-17 05:39:16 +00:00
|
|
|
self.black_cap_value = gtk.Label('0')
|
|
|
|
self.white_cap_value = gtk.Label('0')
|
2012-04-17 05:01:34 +00:00
|
|
|
|
2012-04-24 01:30:31 +00:00
|
|
|
self.to_move_box = gtk.HBox()
|
|
|
|
self.to_move_value = gtk.Label('None')
|
|
|
|
self.to_move_box.pack_start(to_move_label, expand=False, padding=5)
|
|
|
|
self.to_move_box.pack_end(self.to_move_value, expand=False, padding=5)
|
2012-04-17 05:01:34 +00:00
|
|
|
|
2012-04-21 06:50:52 +00:00
|
|
|
self.winner_box = gtk.HBox()
|
|
|
|
self.winner_value = gtk.Label('None')
|
|
|
|
self.winner_box.pack_start(gtk.Label('Winner:'), expand=False, padding=5)
|
|
|
|
self.winner_box.pack_end(self.winner_value, expand=False, padding=5)
|
|
|
|
|
|
|
|
info_box.pack_start(self.winner_box, expand=False)
|
2012-04-24 01:30:31 +00:00
|
|
|
info_box.pack_start(self.to_move_box, expand=False)
|
|
|
|
|
|
|
|
info_rows[0].pack_start(black_cap_label, expand=False, padding=5)
|
|
|
|
info_rows[1].pack_start(white_cap_label, expand=False, padding=5)
|
|
|
|
info_rows[0].pack_end(self.black_cap_value, expand=False, padding=5)
|
|
|
|
info_rows[1].pack_end(self.white_cap_value, expand=False, padding=5)
|
|
|
|
|
|
|
|
for row in info_rows:
|
|
|
|
info_box.pack_start(row, expand=False)
|
2012-04-21 06:50:52 +00:00
|
|
|
|
2012-04-17 05:01:34 +00:00
|
|
|
self.pass_button = gtk.Button('Pass')
|
|
|
|
self.resign_button = gtk.Button('Resign')
|
2012-04-17 05:46:55 +00:00
|
|
|
self.pass_button.connect('clicked', self.on_pass)
|
|
|
|
self.resign_button.connect('clicked', self.on_resign)
|
2012-04-17 05:01:34 +00:00
|
|
|
|
2012-04-17 05:39:16 +00:00
|
|
|
info_box.pack_start(self.pass_button, expand=False, padding=10)
|
|
|
|
info_box.pack_start(self.resign_button, expand=False, padding=10)
|
2012-04-17 05:01:34 +00:00
|
|
|
|
2012-04-17 22:07:04 +00:00
|
|
|
self.board_area = gtk.DrawingArea()
|
2012-04-19 05:46:09 +00:00
|
|
|
self.board_area.set_size_request(750,750)
|
2012-04-18 01:59:40 +00:00
|
|
|
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)
|
|
|
|
|
2012-04-18 05:04:36 +00:00
|
|
|
self.board_area.connect('expose-event', self.do_board_expose)
|
|
|
|
self.board_area.connect('configure-event', self.do_board_configure)
|
2012-04-18 01:59:40 +00:00
|
|
|
self.board_area.connect('motion-notify-event', self.do_hover)
|
|
|
|
self.board_area.connect('button-press-event', self.do_play)
|
2012-04-17 22:07:04 +00:00
|
|
|
|
2012-04-18 05:04:36 +00:00
|
|
|
self.pack_start(self.board_area, expand=False)
|
2012-04-17 05:39:16 +00:00
|
|
|
self.pack_end(info_box, expand=False)
|
2012-04-17 05:01:34 +00:00
|
|
|
|
2012-04-26 04:31:26 +00:00
|
|
|
|
|
|
|
nav_row = gtk.HBox()
|
|
|
|
fwd_button = gtk.Button(stock=gtk.STOCK_GO_FORWARD)
|
|
|
|
back_button = gtk.Button(stock=gtk.STOCK_GO_BACK)
|
|
|
|
first_button = gtk.Button(stock=gtk.STOCK_GOTO_FIRST)
|
|
|
|
last_button = gtk.Button(stock=gtk.STOCK_GOTO_LAST)
|
|
|
|
|
|
|
|
nav_row.pack_start(first_button)
|
|
|
|
nav_row.pack_start(back_button)
|
|
|
|
nav_row.pack_start(fwd_button)
|
|
|
|
nav_row.pack_start(last_button)
|
|
|
|
|
|
|
|
first_button.connect('clicked', self.do_first_move)
|
|
|
|
back_button.connect('clicked', self.do_back_move)
|
|
|
|
fwd_button.connect('clicked', self.do_forward_move)
|
|
|
|
last_button.connect('clicked', self.do_last_move)
|
|
|
|
|
|
|
|
info_box.pack_start(nav_row, expand=False)
|
|
|
|
|
2012-04-18 03:11:49 +00:00
|
|
|
self.update_info()
|
|
|
|
|
2012-04-17 05:01:34 +00:00
|
|
|
|
2012-04-18 01:59:40 +00:00
|
|
|
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)
|
|
|
|
|
2012-04-18 05:04:36 +00:00
|
|
|
old_hover = self.goban.hover
|
2012-04-18 01:59:40 +00:00
|
|
|
if max(row,col) < board_size:
|
|
|
|
if self.goban._real_pos((row,col)) != self.goban.hover:
|
|
|
|
self.goban.set_hover((row,col))
|
2012-04-18 05:04:36 +00:00
|
|
|
self.update_board((old_hover, self.goban.hover))
|
2012-04-18 01:59:40 +00:00
|
|
|
else:
|
|
|
|
self.goban.clear_hover()
|
2012-04-18 05:04:36 +00:00
|
|
|
self.update_board([old_hover])
|
2012-04-18 01:59:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
def do_play(self, widget, event):
|
2012-04-18 03:11:49 +00:00
|
|
|
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)
|
|
|
|
|
2012-04-18 15:33:20 +00:00
|
|
|
changed = self.goban.play_move((row,col))
|
|
|
|
self.update(changed)
|
2012-04-18 01:59:40 +00:00
|
|
|
|
|
|
|
|
2012-04-26 04:31:26 +00:00
|
|
|
def do_back_move(self, widget):
|
|
|
|
self.goban.play_to_move_n(self.goban.move - 1)
|
|
|
|
self.update()
|
|
|
|
|
|
|
|
|
|
|
|
def do_forward_move(self, widget):
|
|
|
|
self.goban.play_to_move_n(self.goban.move + 1)
|
|
|
|
self.update()
|
|
|
|
|
|
|
|
|
|
|
|
def do_first_move(self, widget):
|
|
|
|
self.goban.soft_reset()
|
|
|
|
self.update()
|
|
|
|
|
|
|
|
|
|
|
|
def do_last_move(self, widget):
|
|
|
|
self.goban.play_to_move_n()
|
|
|
|
self.update()
|
|
|
|
|
|
|
|
|
2012-04-18 05:04:36 +00:00
|
|
|
def do_board_configure(self, widget, event):
|
2012-04-17 22:07:04 +00:00
|
|
|
gc = widget.get_style().fg_gc[gtk.STATE_NORMAL]
|
2012-04-18 05:04:36 +00:00
|
|
|
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)
|
2012-04-18 15:37:06 +00:00
|
|
|
self.update()
|
|
|
|
|
2012-04-18 05:04:36 +00:00
|
|
|
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()
|
2012-04-17 22:07:04 +00:00
|
|
|
|
|
|
|
size = min(width, height)
|
|
|
|
board_size = self.goban.board_size
|
|
|
|
inc = size / board_size
|
|
|
|
|
2012-04-18 05:04:36 +00:00
|
|
|
for i in to_update:
|
|
|
|
if i is None:
|
|
|
|
continue
|
|
|
|
|
2012-04-17 22:07:04 +00:00
|
|
|
# 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
|
2012-04-18 05:04:36 +00:00
|
|
|
self.board_backbuf.draw_pixbuf(gc, img, 0, 0, inc * col, inc * row, inc, inc)
|
2012-04-17 05:01:34 +00:00
|
|
|
|
2012-04-18 03:11:49 +00:00
|
|
|
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'
|
2012-04-25 16:15:41 +00:00
|
|
|
else:
|
|
|
|
continue
|
2012-04-18 03:11:49 +00:00
|
|
|
|
|
|
|
base_img = GoGame.img_res[code]
|
|
|
|
img = base_img.scale_simple(inc, inc, gtk.gdk.INTERP_BILINEAR)
|
2012-04-18 05:04:36 +00:00
|
|
|
self.board_backbuf.draw_pixbuf(gc, img, 0, 0, inc * col, inc * row, inc, inc)
|
2012-04-18 03:11:49 +00:00
|
|
|
|
2012-04-18 05:04:36 +00:00
|
|
|
self.board_area.queue_draw()
|
2012-04-18 03:11:49 +00:00
|
|
|
|
2012-04-17 05:39:16 +00:00
|
|
|
|
2012-04-17 05:46:55 +00:00
|
|
|
def on_pass(self, widget):
|
2012-04-18 15:33:20 +00:00
|
|
|
changed = self.goban.pass_move()
|
|
|
|
self.update(changed)
|
2012-04-17 05:39:16 +00:00
|
|
|
|
|
|
|
|
2012-04-17 05:46:55 +00:00
|
|
|
def on_resign(self, widget):
|
2012-04-18 15:33:20 +00:00
|
|
|
changed = self.goban.resign()
|
|
|
|
self.update(changed)
|
2012-04-18 03:11:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
# fixme: Add a widget to show the outcome
|
|
|
|
def update_info(self):
|
|
|
|
if self.goban.to_move == goban.Goban.BLACK:
|
|
|
|
move = 'Black'
|
2012-04-24 01:30:31 +00:00
|
|
|
self.to_move_box.show()
|
2012-04-18 03:11:49 +00:00
|
|
|
elif self.goban.to_move == goban.Goban.WHITE:
|
|
|
|
move = 'White'
|
2012-04-24 01:30:31 +00:00
|
|
|
self.to_move_box.show()
|
2012-04-18 03:11:49 +00:00
|
|
|
else:
|
|
|
|
move = 'None'
|
2012-04-24 01:30:31 +00:00
|
|
|
self.to_move_box.hide()
|
2012-04-21 06:50:52 +00:00
|
|
|
|
|
|
|
if self.goban.winner != goban.Goban.EMPTY:
|
|
|
|
if self.goban.winner == goban.Goban.BLACK:
|
|
|
|
self.winner_value.set_text('Black')
|
|
|
|
elif self.goban.winner == goban.Goban.WHITE:
|
|
|
|
self.winner_value.set_text('White')
|
|
|
|
elif self.goban.winner == goban.Goban.SCORING:
|
|
|
|
self.winner_value.set_text('Scoring')
|
|
|
|
self.winner_box.show()
|
|
|
|
|
2012-04-18 03:11:49 +00:00
|
|
|
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))
|
2012-04-17 05:39:16 +00:00
|
|
|
|
2012-04-17 05:01:34 +00:00
|
|
|
|
2012-04-18 15:35:38 +00:00
|
|
|
def update(self, changed=None):
|
2012-04-18 05:04:36 +00:00
|
|
|
self.update_info()
|
2012-04-18 15:33:20 +00:00
|
|
|
self.update_board(changed)
|
2012-04-18 05:04:36 +00:00
|
|
|
|
|
|
|
|
2012-04-17 05:01:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
def _magnitude(vector):
|
|
|
|
x,y = vector
|
|
|
|
return math.sqrt(x*x + y*y)
|
|
|
|
|
|
|
|
|
|
|
|
def _build_img_res():
|
|
|
|
ret = {}
|
|
|
|
|
2012-04-19 05:46:09 +00:00
|
|
|
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')
|
2012-04-21 06:50:52 +00:00
|
|
|
bs = _load_png('go_bs.png')
|
|
|
|
ws = _load_png('go_ws.png')
|
2012-04-17 05:01:34 +00:00
|
|
|
|
2012-04-18 03:11:49 +00:00
|
|
|
ret['wT'] = _load_png('go_w.png')
|
2012-04-18 01:59:40 +00:00
|
|
|
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)
|
|
|
|
|
2012-04-17 05:01:34 +00:00
|
|
|
ret['bT'] = _load_png('go_b.png')
|
2012-04-18 01:59:40 +00:00
|
|
|
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)
|
2012-04-17 05:01:34 +00:00
|
|
|
|
2012-04-19 04:28:45 +00:00
|
|
|
base = _load_png('go_corner.png')
|
|
|
|
ret['ul'] = base
|
|
|
|
ret['ur'] = base.copy().rotate_simple(270)
|
2012-04-24 01:19:56 +00:00
|
|
|
ret['dr'] = base.copy().rotate_simple(180)
|
|
|
|
ret['dl'] = base.copy().rotate_simple(90)
|
2012-04-19 04:28:45 +00:00
|
|
|
|
|
|
|
base = _load_png('go_edge.png')
|
|
|
|
ret['u'] = base
|
|
|
|
ret['r'] = base.copy().rotate_simple(270)
|
2012-04-24 01:19:56 +00:00
|
|
|
ret['d'] = base.copy().rotate_simple(180)
|
|
|
|
ret['l'] = base.copy().rotate_simple(90)
|
2012-04-19 04:28:45 +00:00
|
|
|
|
2012-04-21 06:50:52 +00:00
|
|
|
for d in ('m', 'h', 'w', 'b', 'wH', 'bH', 'ws', 'bs'):
|
2012-04-17 05:01:34 +00:00
|
|
|
ret[d] = _load_png('go_' + d + '.png')
|
2012-04-18 01:59:40 +00:00
|
|
|
|
2012-04-19 04:28:45 +00:00
|
|
|
for d in ('u', 'd', 'l', 'r', 'm', 'dl', 'dr', 'ul', 'ur', 'h', 'w', 'b'):
|
2012-04-19 05:46:09 +00:00
|
|
|
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)
|
|
|
|
|
2012-04-21 06:50:52 +00:00
|
|
|
ret[d + 'ws'] = ret[d].copy()
|
|
|
|
width = ret[d + 'ws'].get_width()
|
|
|
|
height = ret[d + 'ws'].get_height()
|
|
|
|
ws.composite(ret[d + 'ws'], 0, 0, width, height, 0, 0, 1, 1, gtk.gdk.INTERP_NEAREST, 255)
|
|
|
|
|
|
|
|
ret[d + 'bs'] = ret[d].copy()
|
|
|
|
width = ret[d + 'bs'].get_width()
|
|
|
|
height = ret[d + 'bs'].get_height()
|
|
|
|
bs.composite(ret[d + 'bs'], 0, 0, width, height, 0, 0, 1, 1, gtk.gdk.INTERP_NEAREST, 255)
|
|
|
|
|
|
|
|
|
2012-04-19 04:28:45 +00:00
|
|
|
|
2012-04-17 05:01:34 +00:00
|
|
|
return ret
|
|
|
|
|
|
|
|
|
2012-04-18 03:11:49 +00:00
|
|
|
def _load_png(name):
|
2012-04-17 05:01:34 +00:00
|
|
|
""" Load image and return image object"""
|
|
|
|
fullname = os.path.join('ui/res/', name)
|
|
|
|
image = gtk.gdk.pixbuf_new_from_file(fullname)
|
|
|
|
|
|
|
|
return image
|