Пример #1
0
    def new_game(self, np=2, nai=0):
        """Destroy old board, draw new board, update object state with new board
        """
        if self.tk_root:
            self.tk_root.destroy()
            
        self.tk_root = Tk()
        self.tk_root.bind("<Escape>",   lambda e: self.handle_quit())
        self.tk_root.bind("<Motion>",   lambda e: self.handle_mouse_motion(e.x, e.y))
        self.thread_kill = False
        
        self.time_stats = []
    
        # margin - space/2 - square - space - square - ... - square - space/2 - margin - panel
        total_height = 9*self.SQUARE_SIZE + 9*self.SQUARE_SPACING + 2*self.MARGIN
        total_width = total_height + self.PANEL_WIDTH
        self.canvas_dims = (total_width, total_height)
    
        self.tk_canv = Canvas(self.tk_root, width=total_width, height=total_height, background=self.DEFAULT_COLORS['bg'])
        self.tk_canv.pack()
        
        self.draw_squares()
        self.generate_walls()
        
        self.game_stack = GameStack(np, nai)
        self.update_gs()
        self.players = [(None, None)]*len(self.gs.players)
        self.max_walls = self.gs.current_player.num_walls
        self.wall_labels = [None]*len(self.gs.players)
        self.draw_panel()
        self.refresh(False)
        th = Thread(target = lambda : self.background_loop())
        th.start()

        self.tk_root.mainloop()
    def new_game(self, np=2, nai=0):
        """Destroy old board, draw new board, update object state with new board
        """
        if self.tk_root:
            self.tk_root.destroy()
            
        self.tk_root = Tk()
        self.tk_root.bind("<Escape>",   lambda e: self.handle_quit())
        self.tk_root.bind("<Motion>",   lambda e: self.handle_mouse_motion(e.x, e.y))
        self.thread_kill = False
        
        self.time_stats = []
    
        # margin - space/2 - square - space - square - ... - square - space/2 - margin - panel
        total_height = 9*self.SQUARE_SIZE + 9*self.SQUARE_SPACING + 2*self.MARGIN
        total_width = total_height + self.PANEL_WIDTH
        self.canvas_dims = (total_width, total_height)
    
        self.tk_canv = Canvas(self.tk_root, width=total_width, height=total_height, background=self.DEFAULT_COLORS['bg'])
        self.tk_canv.pack()
        
        self.draw_squares()
        self.generate_walls()
        
        self.game_stack = GameStack(np, nai)
        self.update_gs()
        self.players = [(None, None)]*len(self.gs.players)
        self.max_walls = self.gs.current_player.num_walls
        self.wall_labels = [None]*len(self.gs.players)
        self.draw_panel()
        self.refresh(False)
        th = Thread(target = lambda : self.background_loop())
        th.start()

        self.tk_root.mainloop()
Пример #3
0
class TkBoard():
    # CONSTANTS
    N = 13
    M = 13
    K = 14
    SQUARE_SIZE = 20
    PLAYER_SIZE = SQUARE_SIZE * 0.8
    SQUARE_SPACING = 10
    MARGIN = 20
    is_redo_m = False
    PANEL_WIDTH = 200
    ICON_MARGIN = 55
    BUTTON_Y_START = 125
    BUTTON_WIDTH = 100
    BUTTON_HEIGHT = 30
    BUTTON_MARGIN = 10
    LABEL_Y_START = 330
    LABEL_FONT_SIZE = 12
    LABEL_SPACING = 10
    LABEL_TEXT = lambda s, n, c: ("%-" + str(n + 7) + "s") % (
        "walls: " + "I" * c)  # lambda c: "Walls: {0}".format(c)
    DEFAULT_COLORS = {
        'bg': '#FFFFFF',
        'square': '#333333',
        'wall': '#DD6611',
        'wall-error': '#CC1111',
        'panel': '#333333',
        'button': '#555555',  #'#AA5303',
        'text': '#000000',
        'players': ['#11CC11', '#CC11CC', '#CC1111', '#11CCCC']
    }

    # CLASS VARIABLES - DRAWING
    tk_root = None
    tk_canv = None
    players = []
    player_ghost = None
    icon = None
    ai_label = None
    squares = [[0] * 13] * 13
    wall_labels = []
    grid = None
    canvas_dims = (0, 0)
    buttons = []  # will contain bbox and callback as tuple for each button
    walls = {
    }  # will be dictionary of name => id. all will exist, transparency toggled, colors changed for errors
    active_wall = ""
    active_move = ""
    recent_x = 0
    recent_y = 0

    # GAME-INTERACTION VARIABLES
    gs = None
    moveType = "move"
    game_over = False

    # CONTROL VARIABLES
    THREAD_SLEEP = 0.1

    def set_default_colors(new_colors_dict={}):
        """update default colors with given dictionary of new color scheme
        
        Given colors don't need to be complete - only updates those given"""
        for k in new_colors_dict.keys():
            if k in self.DEFAULT_COLORS.keys():
                self.DEFAULT_COLORS[k] = new_colors_dict[k]

    def new_game(self, np=2, nai=0):
        """Destroy old board, draw new board, update object state with new board
        """
        if self.tk_root:
            self.tk_root.destroy()

        self.tk_root = Tk()
        self.tk_root.bind("<Escape>", lambda e: self.handle_quit())
        self.tk_root.bind("<Motion>",
                          lambda e: self.handle_mouse_motion(e.x, e.y))
        self.thread_kill = False

        self.time_stats = []

        # margin - space/2 - square - space - square - ... - square - space/2 - margin - panel
        total_height = 13 * self.SQUARE_SIZE + 13 * self.SQUARE_SPACING + 2 * self.MARGIN
        total_width = total_height + self.PANEL_WIDTH
        self.canvas_dims = (total_width, total_height)

        self.tk_canv = Canvas(self.tk_root,
                              width=total_width,
                              height=total_height,
                              background=self.DEFAULT_COLORS['bg'])
        self.tk_canv.pack()

        self.draw_squares()
        self.generate_walls()

        self.game_stack = GameStack(np, nai)
        self.update_gs()
        self.players = [(None, None)] * len(self.gs.players)
        self.max_walls = self.gs.current_player.num_walls
        self.wall_labels = [None] * len(self.gs.players)
        self.draw_panel()
        self.refresh(False)
        th = Thread(target=lambda: self.background_loop())
        th.start()

        self.tk_root.mainloop()

    def update_gs(self):
        self.gs = self.game_stack.current

    def background_loop(self):
        global WON1
        global WON2
        global winners
        global el
        turn = 0
        timeout = 120.0
        trackTime1 = 120.0
        trackTime2 = 120.0
        global message
        global message2
        for player in socket_list:
            player.settimeout(timeout)

        while True:

            if turn == 0:
                try:
                    start = time.time()
                    message = socket_list[0].recv(1024)
                    end = time.time()
                    trackTime1 = trackTime1 - (end - start)
                    print trackTime1
                    msg = map(int, message.split(' '))
                    if WON1 == 1:
                        if msg[1] == 0 or (msg[2] == 0 and msg[0] == 0):
                            socket_list[0].send(message2 + " 31")
                            socket_list[1].send("0 0 0 31")
                            print "Player 1 passes. SUCCESS"
                            turn = 1
                            success = self.handle_click(msg[0], msg[1], msg[2])
                            continue
                    message2 = str(trackTime1)
                    if trackTime1 < 0:
                        raise socket.timeout
                except socket.timeout:
                    if WON1 == 1:
                        print "Timed out"
                        socket_list[0].send(message2 + " 1")
                        socket_list[1].send(message + " 2")
                        print "Player 1 timed out, But player 1 wins"
                        socket_list[0].close()
                        socket_list[1].close()
                        sys.exit(0)
                        break
                    else:
                        print "Timed out"
                        socket_list[0].send(message2 + " 2")
                        socket_list[1].send(message + " 1")
                        print "Player 1 timed out, Player 2 wins"
                        socket_list[0].close()
                        socket_list[1].close()
                        sys.exit(0)
                        break

            else:
                try:
                    start = time.time()
                    message = socket_list[1].recv(1024)
                    end = time.time()
                    trackTime2 = trackTime2 - (end - start)
                    print trackTime2
                    msg = map(int, message.split(' '))
                    if WON2 == 1:
                        if msg[1] == 0 or (msg[2] == 0 and msg[0] == 0):
                            socket_list[1].send(message2 + " 32")
                            socket_list[0].send("0 0 0 32")
                            print "Player 2 passes. SUCCESS"
                            turn = 0
                            success = self.handle_click(msg[0], msg[1], msg[2])
                            continue
                    message2 = str(trackTime2)
                    if trackTime2 < 0:
                        raise socket.timeout
                except socket.timeout:
                    if WON2:
                        print "Timed out"
                        socket_list[1].send(message2 + " 1")
                        socket_list[0].send(message + " 2")
                        print "Player 2 timed out, but Player 2 wins"
                        socket_list[0].close()
                        socket_list[1].close()
                        sys.exit(0)
                        break
                    else:
                        print "Timed out"
                        socket_list[1].send(message2 + " 2")
                        socket_list[0].send(message + " 1")
                        print "Player 2 timed out, Player 1 wins"
                        socket_list[0].close()
                        socket_list[1].close()
                        sys.exit(0)
                        break

            success = self.handle_click(msg[0], msg[1], msg[2])
            if success == 0:
                if turn == 0 and WON1 == 1:
                    socket_list[0].send(message2 + " 1")
                    socket_list[1].send(message + " 2")
                    print "Player 1 made invalid move, But, Player 1 wins"
                elif turn == 0 and WON1 == 0:
                    socket_list[0].send(message2 + " 2")
                    socket_list[1].send(message + " 1")
                    print "Player 1 made invalid move, Player 2 wins"
                elif turn == 1 and WON2 == 1:
                    socket_list[1].send(message2 + " 1")
                    socket_list[0].send(message + " 2")
                    print "Player 2 made invalid move, But, Player 2 wins"
                elif turn == 1 and WON2 == 0:
                    socket_list[1].send(message2 + " 2")
                    socket_list[0].send(message + " 1")
                    print "Player 2 made invalid move, Player 1 wins"
                socket_list[0].close()
                socket_list[1].close()
                sys.exit(0)
                break
            if success == 2:
                if WON1 == 1:
                    if turn == 0:
                        socket_list[0].send(message2 + " 1")
                        socket_list[1].send(message + " 2")
                        print "Player 1 wins"
                    if turn == 1:
                        socket_list[0].send(message + " 1")
                        socket_list[1].send(message2 + " 2")
                        print "Player 1 wins"
                if WON2 == 1:
                    if turn == 1:
                        socket_list[1].send(message2 + " 1")
                        socket_list[0].send(message + " 2")
                        print "Player 2 wins"
                    if turn == 0:
                        socket_list[1].send(message + " 1")
                        socket_list[0].send(message2 + " 2")
                        print "Player 2 wins"
                socket_list[0].close()
                socket_list[1].close()
                sys.exit(0)
                break
            if success == 1:
                if turn == 0:
                    socket_list[0].send(message2 + " 3")
                    socket_list[1].send(message + " 3")
                if turn == 1:
                    socket_list[1].send(message2 + " 3")
                    socket_list[0].send(message + " 3")
            if success == 31:
                if turn == 0:
                    socket_list[0].send(message2 + " 31")
                    socket_list[1].send(message + " 31")
                if turn == 1:
                    socket_list[1].send(message2 + " 31")
                    socket_list[0].send(message + " 31")
            if success == 32:
                if turn == 0:
                    socket_list[0].send(message2 + " 32")
                    socket_list[1].send(message + " 32")
                if turn == 1:
                    socket_list[1].send(message2 + " 32")
                    socket_list[0].send(message + " 32")
            print "here: ", success
            if self.thread_kill:
                break
            turn = 1 - turn

        for player in socket_list:
            player.close()
        print "--- END BACKGROUND LOOP ---"

    def handle_quit(self):
        self.thread_kill = True
        for p in self.gs.players:
            if p.ai:
                p.ai.kill_thread()
        self.tk_root.destroy()

    def refresh(self, check_ai=True):
        self.update_gs()
        self.clear_ghost()
        self.handle_mouse_motion(self.recent_x, self.recent_y)
        self.active_wall = ""
        self.active_move = ""
        self.draw_players()
        self.redraw_walls(False)
        self.draw_current_player_icon()
        self.draw_wall_counts()

    def draw_current_player_icon(self):
        width, height = self.canvas_dims
        midx = width - self.PANEL_WIDTH / 2
        radius = self.PLAYER_SIZE / 2
        x0, x1 = midx - radius, midx + radius
        y0, y1 = self.ICON_MARGIN - radius, self.ICON_MARGIN + radius
        c = self.DEFAULT_COLORS['players'][self.gs.current_player_num - 1]
        oval = self.tk_canv.create_oval(x0, y0, x1, y1, fill=c, outline="")
        if self.icon:
            self.tk_canv.delete(self.icon)
        self.icon = oval
        text = None
        if self.gs.current_player.ai:
            text = self.tk_canv.create_text((midx, self.ICON_MARGIN),
                                            text="AI",
                                            font=("Arial", 10, "bold"))
        if self.ai_label:
            self.tk_canv.delete(self.ai_label)
        self.ai_label = text

    def new_rect_button(self, text, fill, x0, y0, x1, y1, callback):
        hover_lighten = TkBoard.alpha_hax(fill, "#FFFFFF", 0.25)
        self.tk_canv.create_rectangle(x0,
                                      y0,
                                      x1,
                                      y1,
                                      fill=fill,
                                      activefill=hover_lighten,
                                      outline="")
        midx = (x0 + x1) / 2
        midy = (y0 + y1) / 2
        self.tk_canv.create_text((midx, midy),
                                 text=text,
                                 font=("Arial", 10, "bold"))
        self.buttons.append(((x0, y0, x1, y1), callback))

    def set_movetype(self, type):
        self.moveType = type
        self.refresh()

    def toggle_movetype(self):
        if self.moveType == "wall":
            self.set_movetype("move")
        elif self.moveType == "move":
            self.set_movetype("wall")
        self.refresh()

    def draw_panel(self):
        # panel bg
        width, height = self.canvas_dims
        midx = width - self.PANEL_WIDTH / 2
        c = self.DEFAULT_COLORS['panel']
        self.tk_canv.create_rectangle(width - self.PANEL_WIDTH,
                                      0,
                                      width,
                                      height,
                                      fill=c)
        # current-player icon @ top
        self.draw_current_player_icon()
        # buttons!
        c = self.DEFAULT_COLORS['button']
        x0, x1 = midx - self.BUTTON_WIDTH / 2, midx + self.BUTTON_WIDTH / 2
        y0, y1 = self.BUTTON_Y_START, self.BUTTON_Y_START + self.BUTTON_HEIGHT
        self.new_rect_button("Move", c, x0, y0, x1, y1,
                             lambda: self.set_movetype("move"))
        yshift = self.BUTTON_HEIGHT + self.BUTTON_MARGIN
        y0 += yshift
        y1 += yshift
        self.new_rect_button("Wall", c, x0, y0, x1, y1,
                             lambda: self.set_movetype("wall"))
        y0 += yshift
        y1 += yshift
        self.new_rect_button("undo", c, x0, y0, x1, y1, lambda: self.undo())
        y0 += yshift
        y1 += yshift
        self.new_rect_button("redo", c, x0, y0, x1, y1, lambda: self.redo())
        # "walls: IIII" text
        self.draw_wall_counts()

    def undo(self):
        self.game_stack.undo()
        self.refresh(False)
        self.game_over = False

    def redo(self):
        self.game_stack.redo()
        self.refresh()

    def draw_wall_counts(self):
        width, height = self.canvas_dims
        midx = width - self.PANEL_WIDTH / 2
        y = self.LABEL_Y_START
        for i in range(len(self.gs.players)):
            p = self.gs.players[i]
            text = self.LABEL_TEXT(self.max_walls, p.num_walls)
            c = self.DEFAULT_COLORS['players'][i]
            l = self.wall_labels[i]
            if not l:
                l = self.tk_canv.create_text(
                    (midx, y),
                    text=text,
                    font=("Arial", self.LABEL_FONT_SIZE, "bold"),
                    fill=c)
                self.wall_labels[i] = l
            else:
                self.tk_canv.itemconfigure(l, text=text)
            y += self.LABEL_SPACING + self.LABEL_FONT_SIZE

    def handle_mouse_motion(self, x, y):
        if self.game_over or self.gs.current_player.ai:
            return
        self.recent_x = x
        self.recent_y = y
        grid = self.point_to_grid((x, y))
        if grid and self.moveType == "move":
            move_str = h.point_to_notation(grid)
            if move_str != self.active_move:
                self.active_move = move_str
                if self.gs.turn_is_valid(move_str, "move"):
                    self.draw_player(grid, self.gs.current_player_num - 1,
                                     True)
                elif self.player_ghost:
                    self.tk_canv.delete(self.player_ghost)
                    self.player_ghost = None

        elif grid and self.moveType == "wall":
            orient, topleft = self.xy_to_wall_spec(grid, x, y)
            pos = h.point_to_notation(topleft)
            wall_str = orient + pos
            if wall_str != self.active_wall:
                self.active_wall = wall_str
                active_error = not self.gs.turn_is_valid(wall_str, "wall")
                self.redraw_walls(active_error)

    def handle_click(self, m, xi, yi):
        x = 10
        y = 20
        print self.gs.current_player.position[0]
        for b in self.buttons:
            (x0, y0, x1, y1), callback = b
            if (x0 <= x <= x1) and (y0 <= y <= y1):
                callback()
                return

        if self.game_over or self.gs.current_player.ai:
            return
        print self.moveType
        # check for turn execution
        global is_redo_m
        is_redo_m = False
        if WON1 == 1 and self.gs.current_player_num == 1 and m == 0:
            grid = self.gs.current_player.position
            is_redo_m = True
        elif WON2 == 1 and self.gs.current_player_num == 2 and m == 0:
            grid = self.gs.current_player.position
            is_redo_m = True
        else:
            grid = (xi, yi)
        if m == 1:
            self.moveType = "wall"
        elif m == 2:
            self.moveType = "wall"
        else:
            self.moveType = "move"
        if grid and self.moveType == "move":
            move_str = h.point_to_notation(grid)
            success = self.exec_wrapper(move_str)
        elif grid and self.moveType == "wall":
            orient, topleft = self.xy_to_wall_spec(grid, x, y)
            if m == 1:
                orient = 'H'
            if m == 2:
                orient = 'V'
            pos = h.point_to_notation(topleft)
            wall_str = orient + pos
            success = self.exec_wrapper(wall_str)
            print success
        if success:
            self.refresh()
        return success

    def handle_keypress(self, key):
        (cr, cc) = self.gs.current_player.position
        if key == "L":
            cc -= 1
        elif key == "R":
            cc += 1
        elif key == "U":
            cr -= 1
        elif key == "D":
            cr += 1
        move_str = h.point_to_notation((cr, cc))
        success = self.exec_wrapper(move_str)
        if success:
            self.refresh()

    def wall_on(self, wall_str, error=False):
        color = self.DEFAULT_COLORS[
            'wall'] if not error else self.DEFAULT_COLORS['wall-error']
        if wall_str in self.walls:
            box_id = self.walls[wall_str]
            if not error:
                self.tk_canv.itemconfigure(box_id, fill=color)
            else:
                # instead of above: changing color, delete and redraw it
                #   so it's the topmost element
                self.tk_canv.delete(box_id)
                (x0, y0, x1, y1) = self.wall_str_to_coords(wall_str)
                self.walls[wall_str] = self.tk_canv.create_rectangle(
                    x0, y0, x1, y1, fill=color, outline="")

    def wall_off(self, wall_str):
        if wall_str in self.walls:
            box_id = self.walls[wall_str]
            self.tk_canv.itemconfigure(box_id, fill="")

    def redraw_walls(self, active_error=True):
        for w in self.walls.keys():
            self.wall_off(w)
        for w in self.gs.walls:
            self.wall_on(w)
        if self.active_wall:
            self.wall_on(self.active_wall, active_error)

    def exec_wrapper(self, turn_str, from_ai=False):
        global WON1
        global WON2
        global winners
        global el
        global is_redo_m
        is_ai = self.gs.current_player.ai is not None
        if from_ai != is_ai:
            return 0
        print "EXECUTING %s TURN" % ("AI" if is_ai else "HUMAN")
        success = self.game_stack.execute_turn(turn_str, is_redo_m)
        print success
        is_redo_m = False
        self.update_gs()
        if success == 1:
            if WON1 == 1:
                print "\tSUCCESS_1"
                self.moveType = "move"
                self.refresh(False)
                return 31
            if WON2 == 1:
                print "\tSUCCESS_2"
                self.moveType = "move"
                self.refresh(False)
                return 32
            print "\tSUCCESS"
            self.moveType = "move"
            self.refresh(False)
            if WON1 == 1:
                return 31
            if WON2 == 1:
                return 32
            return 1
        elif success == 2:
            # winner!
            print "Winner!!"
            winners = winners + 1
            print "Player", self.gs.current_player_num
            if winners == 1:
                el = 0
                if self.gs.current_player_num == 2:
                    WON1 = 1
                    return 31
                else:
                    WON2 = 1
                    return 32
            if winners == 2:
                self.game_over = True
                return 2
        print "\tFAILED"
        return 0

    def draw_squares(self):
        import random
        for r in range(13):
            for c in range(13):
                x = self.MARGIN + self.SQUARE_SPACING / 2 + (
                    self.SQUARE_SIZE + self.SQUARE_SPACING) * c
                y = self.MARGIN + self.SQUARE_SPACING / 2 + (
                    self.SQUARE_SIZE + self.SQUARE_SPACING) * r
                color = self.DEFAULT_COLORS['square']
                sq = self.tk_canv.create_rectangle(x,
                                                   y,
                                                   x + self.SQUARE_SIZE,
                                                   y + self.SQUARE_SIZE,
                                                   fill=color,
                                                   outline="")
                self.squares[r][c] = sq

    def generate_walls(self):
        for w in h.all_walls():
            (x0, y0, x1, y1) = self.wall_str_to_coords(w)
            # regular wall
            r = self.tk_canv.create_rectangle(x0,
                                              y0,
                                              x1,
                                              y1,
                                              fill="",
                                              outline="")
            self.walls[w] = r

    def xy_to_wall_spec(self, grid, x, y):
        cx, cy = self.grid_to_point(grid)
        dx = x - cx
        dy = y - cy
        # wall orientation - I'll explain this when you're older
        r2 = 2**0.5
        rotx = r2 * dx - r2 * dy
        roty = r2 * dx + r2 * dy
        if rotx * roty >= 0:
            orient = 'V'
        else:
            orient = 'H'
        # wall position (top-left)
        gr, gc = grid
        if dx < 0:
            gc -= 1
        if dy < 0:
            gr -= 1
        return (orient, (gr, gc))

    def wall_str_to_coords(self, wall_str):
        grid_pos = h.notation_to_point(wall_str[1:])
        orient = wall_str[0]
        cx, cy = self.grid_to_point(grid_pos)
        wall_len = 2 * self.SQUARE_SIZE + self.SQUARE_SPACING
        wall_wid = self.SQUARE_SPACING
        halfwidth = self.SQUARE_SIZE / 2
        if orient == 'V':
            x0 = cx + halfwidth
            y0 = cy - halfwidth
            x1 = x0 + wall_wid
            y1 = y0 + wall_len
        elif orient == 'H':
            x0 = cx - halfwidth
            y0 = cy + halfwidth
            x1 = x0 + wall_len
            y1 = y0 + wall_wid
        return (x0, y0, x1, y1)

    def draw_players(self):
        game_state = self.gs
        # draw new ones
        for i in range(len(game_state.players)):
            p = game_state.players[i]
            self.draw_player(p.get_pos(), i)

    def draw_player(self, center, num, ghost=False):
        xy = self.grid_to_point(center)
        if not xy:
            return
        x, y = xy
        # remove old ovals from the board
        oval, text = self.players[num]
        if not ghost and oval:
            self.tk_canv.delete(oval)
            if text:
                self.tk_canv.delete(text)
        elif ghost and self.player_ghost:
            self.tk_canv.delete(self.player_ghost)
        # draw new
        c = self.DEFAULT_COLORS['players'][num]
        if ghost:
            bg = self.DEFAULT_COLORS['square']
            c = TkBoard.alpha_hax(bg, c, 0.4)
        radius = self.PLAYER_SIZE / 2
        oval = self.tk_canv.create_oval(x - radius,
                                        y - radius,
                                        x + radius,
                                        y + radius,
                                        fill=c,
                                        outline="")
        text = None
        if self.gs.players[num].ai:
            text = self.tk_canv.create_text((x, y),
                                            text="AI",
                                            font=("Arial", 11, "bold"))
        if not ghost:
            self.players[num] = (oval, text)
        else:
            self.player_ghost = oval

    def clear_ghost(self):
        if self.player_ghost:
            self.tk_canv.delete(self.player_ghost)
            self.player_ghost = None

    def grid_to_point(self, grid_pt):
        """given (row, col), return centerpoint of that square on the canvas
        
        If not a valid grid point, return None"""
        r, c = grid_pt
        if (1 <= r <= 13) and (1 <= c <= 13):
            x = self.MARGIN + self.SQUARE_SPACING / 2 + (
                self.SQUARE_SIZE + self.SQUARE_SPACING) * (c - 1)
            y = self.MARGIN + self.SQUARE_SPACING / 2 + (
                self.SQUARE_SIZE + self.SQUARE_SPACING) * (r - 1)
            halfsquare = self.SQUARE_SIZE / 2
            return (x + halfsquare, y + halfsquare)
        else:
            return None

    def point_to_grid(self, xy):
        """given (x, y), return (row, col) of corresponding grid space.
        
        If off the grid or one row of spacing on outside, returns None"""
        x, y = xy
        x -= self.MARGIN
        y -= self.MARGIN
        full_space = self.SQUARE_SIZE + self.SQUARE_SPACING
        r = int(floor(y / full_space) + 1)
        c = int(floor(x / full_space) + 1)
        if (1 <= r <= 13) and (1 <= c <= 13):
            return (r, c)
        else:
            return None

    @staticmethod
    def alpha_hax(back, front, alpha):
        """since tkinter doesnt support alpha channels as far as I can tell,
        this function does 2-color blending on hex strings, returning blended hex string"""

        # get numeric values
        b_r = int(back[1:3], 16)
        b_g = int(back[3:5], 16)
        b_b = int(back[5:7], 16)

        f_r = int(front[1:3], 16)
        f_g = int(front[3:5], 16)
        f_b = int(front[5:7], 16)

        # combine 'em
        new_r = int(b_r * (1 - alpha) + f_r * alpha)
        new_g = int(b_g * (1 - alpha) + f_g * alpha)
        new_b = int(b_b * (1 - alpha) + f_b * alpha)

        # get hex versions, take off leading '0x' and pad with "0" when len() < 2
        hex_r = hex(new_r)[2:].rjust(2, "0")
        hex_g = hex(new_g)[2:].rjust(2, "0")
        hex_b = hex(new_b)[2:].rjust(2, "0")

        return "#" + hex_r + hex_g + hex_b

    def disp_time_stats(self):
        print self.time_stats

    def __init__(self, n, ai):
        self.new_game(n, ai)
Пример #4
0
class TkBoard:
    # CONSTANTS

    SQUARE_SIZE = 50
    PLAYER_SIZE = SQUARE_SIZE * 0.8
    SQUARE_SPACING = 10
    MARGIN = 20
    is_redo_m = False
    PANEL_WIDTH = 200
    ICON_MARGIN = 55
    BUTTON_Y_START = 125
    BUTTON_WIDTH = 100
    BUTTON_HEIGHT = 30
    BUTTON_MARGIN = 10
    LABEL_Y_START = 330
    LABEL_FONT_SIZE = 26
    LABEL_SPACING = 10
    LABEL_TEXT = lambda s, n, c: ("%-" + str(n + 7) + "s") % ("walls: " + "I" * c)  # lambda c: "Walls: {0}".format(c)
    DEFAULT_COLORS = {
        "bg": "#FFFFFF",
        "square": "#333333",
        "wall": "#DD6611",
        "wall-error": "#CC1111",
        "panel": "#333333",
        "button": "#555555",  #'#AA5303',
        "text": "#000000",
        "players": ["#11CC11", "#CC11CC", "#CC1111", "#11CCCC"],
    }

    # CLASS VARIABLES - DRAWING
    tk_root = None
    tk_canv = None
    players = []
    player_ghost = None
    icon = None
    ai_label = None
    squares = [[0] * 9] * 9
    wall_labels = []
    grid = None
    canvas_dims = (0, 0)
    buttons = []  # will contain bbox and callback as tuple for each button
    walls = {}  # will be dictionary of name => id. all will exist, transparency toggled, colors changed for errors
    active_wall = ""
    active_move = ""
    recent_x = 0
    recent_y = 0

    # GAME-INTERACTION VARIABLES
    gs = None
    moveType = "move"
    game_over = False

    # CONTROL VARIABLES
    THREAD_SLEEP = 0.1

    def set_default_colors(new_colors_dict={}):
        """update default colors with given dictionary of new color scheme
        
        Given colors don't need to be complete - only updates those given"""
        for k in new_colors_dict.keys():
            if k in self.DEFAULT_COLORS.keys():
                self.DEFAULT_COLORS[k] = new_colors_dict[k]

    def new_game(self, np=2, nai=0):
        """Destroy old board, draw new board, update object state with new board
        """
        if self.tk_root:
            self.tk_root.destroy()

        self.tk_root = Tk()
        self.tk_root.bind("<Escape>", lambda e: self.handle_quit())
        self.tk_root.bind("<Motion>", lambda e: self.handle_mouse_motion(e.x, e.y))
        self.thread_kill = False

        self.time_stats = []

        # margin - space/2 - square - space - square - ... - square - space/2 - margin - panel
        total_height = 9 * self.SQUARE_SIZE + 9 * self.SQUARE_SPACING + 2 * self.MARGIN
        total_width = total_height + self.PANEL_WIDTH
        self.canvas_dims = (total_width, total_height)

        self.tk_canv = Canvas(
            self.tk_root, width=total_width, height=total_height, background=self.DEFAULT_COLORS["bg"]
        )
        self.tk_canv.pack()

        self.draw_squares()
        self.generate_walls()

        self.game_stack = GameStack(np, nai)
        self.update_gs()
        self.players = [(None, None)] * len(self.gs.players)
        self.max_walls = self.gs.current_player.num_walls
        self.wall_labels = [None] * len(self.gs.players)
        self.draw_panel()
        self.refresh(False)
        th = Thread(target=lambda: self.background_loop())
        th.start()

        self.tk_root.mainloop()

    def update_gs(self):
        self.gs = self.game_stack.current

    def background_loop(self):
        global WON1
        global WON2
        global winners
        global el
        turn = 0
        timeout = 6000000.0
        trackTime1 = 60.0
        trackTime2 = 60.0
        global message
        global message2
        for player in socket_list:
            player.settimeout(timeout)

        while True:

            if turn == 0:
                try:
                    start = time.time()
                    message = socket_list[0].recv(1024)
                    end = time.time()
                    trackTime1 = trackTime1 - (end - start)
                    print trackTime1
                    msg = map(int, message.split(" "))
                    if WON1 == 1:
                        if msg[1] == 0 or (msg[2] == 0 and msg[0] == 0):
                            socket_list[0].send(message2 + " 31")
                            socket_list[1].send("0 0 0 31")
                            print "Player 1 passes. SUCCESS"
                            turn = 1
                            success = self.handle_click(msg[0], msg[1], msg[2])
                            continue
                    message2 = str(trackTime1)
                    if trackTime1 < 0:
                        raise socket.timeout
                except socket.timeout:
                    if WON1 == 1:
                        print "Timed out"
                        socket_list[0].send(message2 + " 1")
                        socket_list[1].send(message + " 2")
                        print "Player 1 timed out, But player 1 wins"
                        socket_list[0].close()
                        socket_list[1].close()
                        sys.exit(0)
                        break
                    else:
                        print "Timed out"
                        socket_list[0].send(message2 + " 2")
                        socket_list[1].send(message + " 1")
                        print "Player 1 timed out, Player 2 wins"
                        socket_list[0].close()
                        socket_list[1].close()
                        sys.exit(0)
                        break

            else:
                try:
                    start = time.time()
                    message = socket_list[1].recv(1024)
                    end = time.time()
                    trackTime2 = trackTime2 - (end - start)
                    print trackTime2
                    msg = map(int, message.split(" "))
                    if WON2 == 1:
                        if msg[1] == 0 or (msg[2] == 0 and msg[0] == 0):
                            socket_list[1].send(message2 + " 32")
                            socket_list[0].send("0 0 0 32")
                            print "Player 2 passes. SUCCESS"
                            turn = 0
                            success = self.handle_click(msg[0], msg[1], msg[2])
                            continue
                    message2 = str(trackTime2)
                    if trackTime2 < 0:
                        raise socket.timeout
                except socket.timeout:
                    if WON2:
                        print "Timed out"
                        socket_list[1].send(message2 + " 1")
                        socket_list[0].send(message + " 2")
                        print "Player 2 timed out, but Player 2 wins"
                        socket_list[0].close()
                        socket_list[1].close()
                        sys.exit(0)
                        break
                    else:
                        print "Timed out"
                        socket_list[1].send(message2 + " 2")
                        socket_list[0].send(message + " 1")
                        print "Player 2 timed out, Player 1 wins"
                        socket_list[0].close()
                        socket_list[1].close()
                        sys.exit(0)
                        break

            success = self.handle_click(msg[0], msg[1], msg[2])
            if success == 0:
                if turn == 0 and WON1 == 1:
                    socket_list[0].send(message2 + " 1")
                    socket_list[1].send(message + " 2")
                    print "Player 1 made invalid move, But, Player 1 wins"
                elif turn == 0 and WON1 == 0:
                    socket_list[0].send(message2 + " 2")
                    socket_list[1].send(message + " 1")
                    print "Player 1 made invalid move, Player 2 wins"
                elif turn == 1 and WON2 == 1:
                    socket_list[1].send(message2 + " 1")
                    socket_list[0].send(message + " 2")
                    print "Player 2 made invalid move, But, Player 2 wins"
                elif turn == 1 and WON2 == 0:
                    socket_list[1].send(message2 + " 2")
                    socket_list[0].send(message + " 1")
                    print "Player 2 made invalid move, Player 1 wins"
                socket_list[0].close()
                socket_list[1].close()
                sys.exit(0)
                break
            if success == 2:
                if WON1 == 1:
                    if turn == 0:
                        socket_list[0].send(message2 + " 1")
                        socket_list[1].send(message + " 2")
                        print "Player 1 wins"
                    if turn == 1:
                        socket_list[0].send(message + " 1")
                        socket_list[1].send(message2 + " 2")
                        print "Player 1 wins"
                if WON2 == 1:
                    if turn == 1:
                        socket_list[1].send(message2 + " 1")
                        socket_list[0].send(message + " 2")
                        print "Player 2 wins"
                    if turn == 0:
                        socket_list[1].send(message + " 1")
                        socket_list[0].send(message2 + " 2")
                        print "Player 2 wins"
                socket_list[0].close()
                socket_list[1].close()
                sys.exit(0)
                break
            if success == 1:
                if turn == 0:
                    socket_list[0].send(message2 + " 3")
                    socket_list[1].send(message + " 3")
                if turn == 1:
                    socket_list[1].send(message2 + " 3")
                    socket_list[0].send(message + " 3")
            if success == 31:
                if turn == 0:
                    socket_list[0].send(message2 + " 31")
                    socket_list[1].send(message + " 31")
                if turn == 1:
                    socket_list[1].send(message2 + " 31")
                    socket_list[0].send(message + " 31")
            if success == 32:
                if turn == 0:
                    socket_list[0].send(message2 + " 32")
                    socket_list[1].send(message + " 32")
                if turn == 1:
                    socket_list[1].send(message2 + " 32")
                    socket_list[0].send(message + " 32")
            print "here: ", success
            if self.thread_kill:
                break
            turn = 1 - turn

        for player in socket_list:
            player.close()
        print "--- END BACKGROUND LOOP ---"

    def handle_quit(self):
        self.thread_kill = True
        for p in self.gs.players:
            if p.ai:
                p.ai.kill_thread()
        self.tk_root.destroy()

    def refresh(self, check_ai=True):
        self.update_gs()
        self.clear_ghost()
        self.handle_mouse_motion(self.recent_x, self.recent_y)
        self.active_wall = ""
        self.active_move = ""
        self.draw_players()
        self.redraw_walls(False)
        self.draw_current_player_icon()
        self.draw_wall_counts()
        if check_ai:
            self.get_ai_move()

    def get_ai_move(self, verbose=True):
        ai = self.gs.current_player.ai
        if ai:
            if verbose:
                print "ai exists for player", self.gs.current_player_num,
            if ai.thread_started:
                if verbose:
                    print "and thread",
                turn, time = ai.get_threaded_move()
                if turn:
                    self.time_stats.append(time)
                    if verbose:
                        print "is done!"
                    if not self.exec_wrapper(turn, True):
                        print "\n################"
                        print "### AI ERROR ###"
                        print "################\n"
                        self.handle_quit()
                    else:
                        self.refresh()
                elif verbose:
                    print "has started but hasn't finished yet"
            else:
                if verbose:
                    print "but thread hasn't started. starting now."
                ai.get_move_thread_start(self.game_stack.duplicate())

    def draw_current_player_icon(self):
        width, height = self.canvas_dims
        midx = width - self.PANEL_WIDTH / 2
        radius = self.PLAYER_SIZE / 2
        x0, x1 = midx - radius, midx + radius
        y0, y1 = self.ICON_MARGIN - radius, self.ICON_MARGIN + radius
        c = self.DEFAULT_COLORS["players"][self.gs.current_player_num - 1]
        oval = self.tk_canv.create_oval(x0, y0, x1, y1, fill=c, outline="")
        if self.icon:
            self.tk_canv.delete(self.icon)
        self.icon = oval
        text = None
        if self.gs.current_player.ai:
            text = self.tk_canv.create_text((midx, self.ICON_MARGIN), text="AI", font=("Arial", 14, "bold"))
        if self.ai_label:
            self.tk_canv.delete(self.ai_label)
        self.ai_label = text

    def new_rect_button(self, text, fill, x0, y0, x1, y1, callback):
        hover_lighten = TkBoard.alpha_hax(fill, "#FFFFFF", 0.25)
        self.tk_canv.create_rectangle(x0, y0, x1, y1, fill=fill, activefill=hover_lighten, outline="")
        midx = (x0 + x1) / 2
        midy = (y0 + y1) / 2
        self.tk_canv.create_text((midx, midy), text=text, font=("Arial", 14, "bold"))
        self.buttons.append(((x0, y0, x1, y1), callback))

    def set_movetype(self, type):
        self.moveType = type
        self.refresh()

    def toggle_movetype(self):
        if self.moveType == "wall":
            self.set_movetype("move")
        elif self.moveType == "move":
            self.set_movetype("wall")
        self.refresh()

    def draw_panel(self):
        # panel bg
        width, height = self.canvas_dims
        midx = width - self.PANEL_WIDTH / 2
        c = self.DEFAULT_COLORS["panel"]
        self.tk_canv.create_rectangle(width - self.PANEL_WIDTH, 0, width, height, fill=c)
        # current-player icon @ top
        self.draw_current_player_icon()
        # buttons!
        c = self.DEFAULT_COLORS["button"]
        x0, x1 = midx - self.BUTTON_WIDTH / 2, midx + self.BUTTON_WIDTH / 2
        y0, y1 = self.BUTTON_Y_START, self.BUTTON_Y_START + self.BUTTON_HEIGHT
        self.new_rect_button("Move", c, x0, y0, x1, y1, lambda: self.set_movetype("move"))
        yshift = self.BUTTON_HEIGHT + self.BUTTON_MARGIN
        y0 += yshift
        y1 += yshift
        self.new_rect_button("Wall", c, x0, y0, x1, y1, lambda: self.set_movetype("wall"))
        y0 += yshift
        y1 += yshift
        self.new_rect_button("undo", c, x0, y0, x1, y1, lambda: self.undo())
        y0 += yshift
        y1 += yshift
        self.new_rect_button("redo", c, x0, y0, x1, y1, lambda: self.redo())
        # "walls: IIII" text
        self.draw_wall_counts()

    def undo(self):
        self.game_stack.undo()
        self.refresh(False)
        self.game_over = False

    def redo(self):
        self.game_stack.redo()
        self.refresh()

    def draw_wall_counts(self):
        width, height = self.canvas_dims
        midx = width - self.PANEL_WIDTH / 2
        y = self.LABEL_Y_START
        for i in range(len(self.gs.players)):
            p = self.gs.players[i]
            text = self.LABEL_TEXT(self.max_walls, p.num_walls)
            c = self.DEFAULT_COLORS["players"][i]
            l = self.wall_labels[i]
            if not l:
                l = self.tk_canv.create_text((midx, y), text=text, font=("Arial", self.LABEL_FONT_SIZE, "bold"), fill=c)
                self.wall_labels[i] = l
            else:
                self.tk_canv.itemconfigure(l, text=text)
            y += self.LABEL_SPACING + self.LABEL_FONT_SIZE

    def handle_mouse_motion(self, x, y):
        if self.game_over or self.gs.current_player.ai:
            return
        self.recent_x = x
        self.recent_y = y
        grid = self.point_to_grid((x, y))
        if grid and self.moveType == "move":
            move_str = h.point_to_notation(grid)
            if move_str != self.active_move:
                self.active_move = move_str
                if self.gs.turn_is_valid(move_str, "move"):
                    self.draw_player(grid, self.gs.current_player_num - 1, True)
                elif self.player_ghost:
                    self.tk_canv.delete(self.player_ghost)
                    self.player_ghost = None

        elif grid and self.moveType == "wall":
            orient, topleft = self.xy_to_wall_spec(grid, x, y)
            pos = h.point_to_notation(topleft)
            wall_str = orient + pos
            if wall_str != self.active_wall:
                self.active_wall = wall_str
                active_error = not self.gs.turn_is_valid(wall_str, "wall")
                self.redraw_walls(active_error)

    def handle_click(self, m, xi, yi):
        x = 10
        y = 20
        print self.gs.current_player.position[0]
        for b in self.buttons:
            (x0, y0, x1, y1), callback = b
            if (x0 <= x <= x1) and (y0 <= y <= y1):
                callback()
                return

        if self.game_over or self.gs.current_player.ai:
            return
        print self.moveType
        # check for turn execution
        global is_redo_m
        is_redo_m = False
        if WON1 == 1 and self.gs.current_player_num == 1 and m == 0:
            grid = self.gs.current_player.position
            is_redo_m = True
        elif WON2 == 1 and self.gs.current_player_num == 2 and m == 0:
            grid = self.gs.current_player.position
            is_redo_m = True
        else:
            grid = (xi, yi)
        if m == 1:
            self.moveType = "wall"
        elif m == 2:
            self.moveType = "wall"
        else:
            self.moveType = "move"
        if grid and self.moveType == "move":
            move_str = h.point_to_notation(grid)
            success = self.exec_wrapper(move_str)
        elif grid and self.moveType == "wall":
            orient, topleft = self.xy_to_wall_spec(grid, x, y)
            if m == 1:
                orient = "H"
            if m == 2:
                orient = "V"
            pos = h.point_to_notation(topleft)
            wall_str = orient + pos
            success = self.exec_wrapper(wall_str)
            print success
        if success:
            self.refresh()
        return success

    def handle_keypress(self, key):
        (cr, cc) = self.gs.current_player.position
        if key == "L":
            cc -= 1
        elif key == "R":
            cc += 1
        elif key == "U":
            cr -= 1
        elif key == "D":
            cr += 1
        move_str = h.point_to_notation((cr, cc))
        success = self.exec_wrapper(move_str)
        if success:
            self.refresh()

    def wall_on(self, wall_str, error=False):
        color = self.DEFAULT_COLORS["wall"] if not error else self.DEFAULT_COLORS["wall-error"]
        if wall_str in self.walls:
            box_id = self.walls[wall_str]
            if not error:
                self.tk_canv.itemconfigure(box_id, fill=color)
            else:
                # instead of above: changing color, delete and redraw it
                #   so it's the topmost element
                self.tk_canv.delete(box_id)
                (x0, y0, x1, y1) = self.wall_str_to_coords(wall_str)
                self.walls[wall_str] = self.tk_canv.create_rectangle(x0, y0, x1, y1, fill=color, outline="")

    def wall_off(self, wall_str):
        if wall_str in self.walls:
            box_id = self.walls[wall_str]
            self.tk_canv.itemconfigure(box_id, fill="")

    def redraw_walls(self, active_error=True):
        for w in self.walls.keys():
            self.wall_off(w)
        for w in self.gs.walls:
            self.wall_on(w)
        if self.active_wall:
            self.wall_on(self.active_wall, active_error)

    def exec_wrapper(self, turn_str, from_ai=False):
        global WON1
        global WON2
        global winners
        global el
        global is_redo_m
        is_ai = self.gs.current_player.ai is not None
        if from_ai != is_ai:
            return 0
        print "EXECUTING %s TURN" % ("AI" if is_ai else "HUMAN")
        success = self.game_stack.execute_turn(turn_str, is_redo_m)
        print success
        is_redo_m = False
        self.update_gs()
        if success == 1:
            if WON1 == 1:
                print "\tSUCCESS_1"
                self.moveType = "move"
                self.refresh(False)
                return 31
            if WON2 == 1:
                print "\tSUCCESS_2"
                self.moveType = "move"
                self.refresh(False)
                return 32
            print "\tSUCCESS"
            self.moveType = "move"
            self.refresh(False)
            if WON1 == 1:
                return 31
            if WON2 == 1:
                return 32
            return 1
        elif success == 2:
            # winner!
            print "Winner!!"
            winners = winners + 1
            print "Player", self.gs.current_player_num
            if winners == 1:
                el = 0
                if self.gs.current_player_num == 2:
                    WON1 = 1
                    return 31
                else:
                    WON2 = 1
                    return 32
            if winners == 2:
                self.game_over = True
                return 2
        print "\tFAILED"
        return 0

    def draw_squares(self):
        import random

        for r in range(9):
            for c in range(9):
                x = self.MARGIN + self.SQUARE_SPACING / 2 + (self.SQUARE_SIZE + self.SQUARE_SPACING) * c
                y = self.MARGIN + self.SQUARE_SPACING / 2 + (self.SQUARE_SIZE + self.SQUARE_SPACING) * r
                color = self.DEFAULT_COLORS["square"]
                sq = self.tk_canv.create_rectangle(
                    x, y, x + self.SQUARE_SIZE, y + self.SQUARE_SIZE, fill=color, outline=""
                )
                self.squares[r][c] = sq

    def generate_walls(self):
        for w in h.all_walls():
            (x0, y0, x1, y1) = self.wall_str_to_coords(w)
            # regular wall
            r = self.tk_canv.create_rectangle(x0, y0, x1, y1, fill="", outline="")
            self.walls[w] = r

    def xy_to_wall_spec(self, grid, x, y):
        cx, cy = self.grid_to_point(grid)
        dx = x - cx
        dy = y - cy
        # wall orientation - I'll explain this when you're older
        r2 = 2 ** 0.5
        rotx = r2 * dx - r2 * dy
        roty = r2 * dx + r2 * dy
        if rotx * roty >= 0:
            orient = "V"
        else:
            orient = "H"
        # wall position (top-left)
        gr, gc = grid
        if dx < 0:
            gc -= 1
        if dy < 0:
            gr -= 1
        return (orient, (gr, gc))

    def wall_str_to_coords(self, wall_str):
        grid_pos = h.notation_to_point(wall_str[1:])
        orient = wall_str[0]
        cx, cy = self.grid_to_point(grid_pos)
        wall_len = 2 * self.SQUARE_SIZE + self.SQUARE_SPACING
        wall_wid = self.SQUARE_SPACING
        halfwidth = self.SQUARE_SIZE / 2
        if orient == "V":
            x0 = cx + halfwidth
            y0 = cy - halfwidth
            x1 = x0 + wall_wid
            y1 = y0 + wall_len
        elif orient == "H":
            x0 = cx - halfwidth
            y0 = cy + halfwidth
            x1 = x0 + wall_len
            y1 = y0 + wall_wid
        return (x0, y0, x1, y1)

    def draw_players(self):
        game_state = self.gs
        # draw new ones
        for i in range(len(game_state.players)):
            p = game_state.players[i]
            self.draw_player(p.get_pos(), i)

    def draw_player(self, center, num, ghost=False):
        xy = self.grid_to_point(center)
        if not xy:
            return
        x, y = xy
        # remove old ovals from the board
        oval, text = self.players[num]
        if not ghost and oval:
            self.tk_canv.delete(oval)
            if text:
                self.tk_canv.delete(text)
        elif ghost and self.player_ghost:
            self.tk_canv.delete(self.player_ghost)
        # draw new
        c = self.DEFAULT_COLORS["players"][num]
        if ghost:
            bg = self.DEFAULT_COLORS["square"]
            c = TkBoard.alpha_hax(bg, c, 0.4)
        radius = self.PLAYER_SIZE / 2
        oval = self.tk_canv.create_oval(x - radius, y - radius, x + radius, y + radius, fill=c, outline="")
        text = None
        if self.gs.players[num].ai:
            text = self.tk_canv.create_text((x, y), text="AI", font=("Arial", 14, "bold"))
        if not ghost:
            self.players[num] = (oval, text)
        else:
            self.player_ghost = oval

    def clear_ghost(self):
        if self.player_ghost:
            self.tk_canv.delete(self.player_ghost)
            self.player_ghost = None

    def grid_to_point(self, grid_pt):
        """given (row, col), return centerpoint of that square on the canvas
        
        If not a valid grid point, return None"""
        r, c = grid_pt
        if (1 <= r <= 9) and (1 <= c <= 9):
            x = self.MARGIN + self.SQUARE_SPACING / 2 + (self.SQUARE_SIZE + self.SQUARE_SPACING) * (c - 1)
            y = self.MARGIN + self.SQUARE_SPACING / 2 + (self.SQUARE_SIZE + self.SQUARE_SPACING) * (r - 1)
            halfsquare = self.SQUARE_SIZE / 2
            return (x + halfsquare, y + halfsquare)
        else:
            return None

    def point_to_grid(self, xy):
        """given (x, y), return (row, col) of corresponding grid space.
        
        If off the grid or one row of spacing on outside, returns None"""
        x, y = xy
        x -= self.MARGIN
        y -= self.MARGIN
        full_space = self.SQUARE_SIZE + self.SQUARE_SPACING
        r = int(floor(y / full_space) + 1)
        c = int(floor(x / full_space) + 1)
        if (1 <= r <= 9) and (1 <= c <= 9):
            return (r, c)
        else:
            return None

    @staticmethod
    def alpha_hax(back, front, alpha):
        """since tkinter doesnt support alpha channels as far as I can tell,
        this function does 2-color blending on hex strings, returning blended hex string"""

        # get numeric values
        b_r = int(back[1:3], 16)
        b_g = int(back[3:5], 16)
        b_b = int(back[5:7], 16)

        f_r = int(front[1:3], 16)
        f_g = int(front[3:5], 16)
        f_b = int(front[5:7], 16)

        # combine 'em
        new_r = int(b_r * (1 - alpha) + f_r * alpha)
        new_g = int(b_g * (1 - alpha) + f_g * alpha)
        new_b = int(b_b * (1 - alpha) + f_b * alpha)

        # get hex versions, take off leading '0x' and pad with "0" when len() < 2
        hex_r = hex(new_r)[2:].rjust(2, "0")
        hex_g = hex(new_g)[2:].rjust(2, "0")
        hex_b = hex(new_b)[2:].rjust(2, "0")

        return "#" + hex_r + hex_g + hex_b

    def disp_time_stats(self):
        print self.time_stats

    def __init__(self, n, ai):
        self.new_game(n, ai)