Beispiel #1
0
def test_board_defaults():
    """Test Board constructor uses default location value."""
    b1 = Board(3)
    for row in range(b1.side_len()):
        for col in range(b1.side_len()):
            assert b1.get(row, col) == None    

    b2 = _board_3x3_Indexes()
    for row in range(b2.side_len()):
        for col in range(b2.side_len()):
            assert b2.get(row, col) == row * b2.side_len() + col    

    b3 = Board(3, [])
    for row in range(b3.side_len()):
        for col in range(b3.side_len()):
            assert b3.get(row, col) == []    
Beispiel #2
0
def test_get_raises_ValueError():
    """Test Board.get() validates arguments."""
    b = Board(3)
    with pytest.raises(ValueError):
        b.get(-1, 0)
    with pytest.raises(ValueError):
        b.get(3, 0)
    with pytest.raises(ValueError):
        b.get(0, -1)
    with pytest.raises(ValueError):
        b.get(0, 3)
Beispiel #3
0
def test_set_immutable():
    """Test Board.set() preserves immutable board."""
    b1 = Board(3, 0)
    b2 = b1
    for row in range(b2.side_len()):
        for col in range(b2.side_len()):
            b2 = b2.set(row, col, row*b2.side_len() + col)

    for row in range(b1.side_len()):
        for col in range(b1.side_len()):
            assert b1.get(row, col) == 0
Beispiel #4
0
    def move(self, board: Board):
        """
        Ask for the user's input until a valid one appears, and then update the board
        :param board:
        :return:
        """
        while 1:
            coord = input(">")
            coord = coord.split(" ")
            row = coord[0]
            try:
                col = coord[1]
            except IndexError:
                print("col is invalid")
                continue
            try:
                row = int(row)
            except ValueError:
                print("row is not an int")
                continue
            try:
                col = int(col)
            except ValueError:
                print("col is not an int")
                continue
            if row < 0 or row >= board.rows:
                print("row out of bound")
                continue
            if col < 0 or col >= board.cols:
                print("col out of bound")
                continue

            if board.get(row, col) != 0:
                print(f"({row}, {col}) has been occupied")
                continue
            return row, col
Beispiel #5
0
class Simulation:
    '''
    Handle IO logic for simulation. Simulation objects function as iterable
    finite state machines.
    '''

    # State variables
    INIT = 0
    PROMPT_TEAM = 1
    PLAYER_MOVE = 2
    CPU_MOVE = 3
    PROMPT_RESTART = 4
    FINISHED = 5

    @staticmethod
    def get_input(prompt, restrictions):
        '''
        Get input from user while applying given constraints

        Parameters
            prompt: str, message to guide user
            restrictions: str[], list of valid input options

        Return
            str, input from user

        '''
        # keep requesting until valid input received
        while True:
            result = input(prompt)
            if result in restrictions:
                return result
            else:
                print(static.UTIL['input_error'])

    def __init__(self):
        '''
        Initialize fieleds.

        '''
        self._solver = Solver()
        self._board = Board()
        self._state = Simulation.INIT

    def __iter__(self):
        '''
        Mark Simulation objects as iterable

        Return
            Simulation, this object

        '''
        return self

    def __next__(self):
        '''
        Continue simulation until next piece of output is available

        Return
            str, output from game since last call to next()

        '''
        if self._state == Simulation.INIT:
            return self._state_init()

        elif self._state == Simulation.PROMPT_TEAM:
            return self._state_prompt_team()

        elif self._state == Simulation.CPU_MOVE:
            return self._state_cpu_move()

        elif self._state == Simulation.PLAYER_MOVE:
            return self._state_player_move()

        elif self._state == Simulation.PROMPT_RESTART:
            return self._state_prompt_restart()

        else:  # self._state == Simulation.FINISHED
            raise StopIteration

    def _state_init(self):
        '''
        Update state to PROMPT_TEAM

        Return
            str, rules for simulation

        '''
        self._state = Simulation.PROMPT_TEAM
        return '\n%s\n' % static.INFO['man']

    def _state_prompt_team(self):
        '''
        Determine teams and update state to either CPU_MOVE or PLAYER_MOVE

        Return
            str, board representation

        '''
        # ask user is they would like to go first
        choice = Simulation.get_input(
            static.UTIL['team_prompt'], static.BINARY)
        if choice in static.YES:
            self._state = Simulation.PLAYER_MOVE
        else:
            self._state = Simulation.CPU_MOVE

        return str(self._board)

    def _state_cpu_move(self):
        '''
        Make cpu move and update state to either PROMPT_RESTART or
        PLAYER_MOVE

        Return
            str, board representation and optional end of game message

        '''
        move = self._solver.get_next_move(self._board)
        turn = str(self._board.turn())
        self._board = self._board.move(move)

        # result is cpu move and string representation of board
        result = ['%s >>> %d' % (turn, move), str(self._board)]

        # if game is over, append game over message
        if self._board.game_over():
            result.append(static.UTIL['lose_game']
                          if self._board.winner() else static.UTIL['tie_game'])
            self._state = Simulation.PROMPT_RESTART
        else:
            self._state = Simulation.PLAYER_MOVE

        return '\n'.join(result)

    def _state_player_move(self):
        '''
        Request player move and update state to either PROMPT_RESTART or
        PLAYER_MOVE

        Return
            str, board representation and optional end of game message

        '''
        # commands include available spaces, an action, or a help command
        options = [str(x) for x in self._board.get(Team.NEITHER)] + \
            static.ACTIONS + list(static.INFO.keys())
        prompt = '%s >>> ' % str(self._board.turn())
        command = Simulation.get_input(prompt, options)

        if command in static.INFO:
            # print help message
            return static.INFO[command]

        elif command == 'undo':
            if self._board.turn() in self._board:
                # check that player has a move that can be undone
                # undo twice to undo cpu's move as well
                self._board = self._board.undo().undo()
                return str(self._board)
            else:
                return static.UTIL['undo_error']

        elif command == 'print':
            return str(self._board)

        elif command == 'quit':
            self._state = Simulation.PROMPT_RESTART
            return ''  # return empty line to print

        else:  # integer coordinate
            self._board = self._board.move(int(command))
            result = [str(self._board)]

            # if game is over, append game over message
            if self._board.game_over():
                result.append(static.UTIL['tie_game'])
                self._state = Simulation.PROMPT_RESTART
            else:
                self._state = Simulation.CPU_MOVE

            return '\n'.join(result)

    def _state_prompt_restart(self):
        '''
        Determine whether to re-run simulation and update state to either
        PROMPT_TEAM of FINISHED

        Return
            str, board representation

        '''
        # ask whether player wants to play again
        choice = Simulation.get_input(
            static.UTIL['retry_prompt'], static.BINARY)
        if choice in static.YES:
            self._board = Board()
            self._state = Simulation.PROMPT_TEAM
        else:
            self._state = Simulation.FINISHED

        return ''  # return empty line to print

    def board(self):
        '''
        Return
            Board, current board for this simulation

        '''
        return self._board

    def state(self):
        '''
        Return
            int, current Simulation state constant for this simulation

        '''
        return self._state
Beispiel #6
0
def test_get(side, ival):
    """Test Board.get()."""
    b = Board(side, ival)
    for row in range(b.side_len()):
        for col in range(b.side_len()):
            assert b.get(row, col) == ival
Beispiel #7
0
class GUI:
    def __init__(self):
        self.win = Tk()
        self.win.title("Gomoku")

        self.buttons = []
        self.turn = "black"
        self.display()

        self.p1 = PlayerLV4(1, "White")
        self.p2 = Player(2, "Black")
        self.board = Board(15, 15)

        self.win.mainloop()

    def display(self):
        f1 = Frame(self.win, padx=10, pady=10)
        f1.grid(row=1, column=1)

        row = []

        for i in range(15):
            for j in range(15):
                button = Button(f1, text="", width=2, heigh=1, command=lambda i=i, j=j: self.click_button(i, j))
                # button.pack()
                button.grid(row=i, column=j)
                row.append(button)
            self.buttons.append(row)
            row = []

        # for i in range(len(self.buttons)):
        #     for j in range(len(row)):
        #         print(i, j)
        #         button = self.buttons[i][j]
        #         button.bind("<Button>", lambda i=i, j=j: self.click_button(i, j))
        #         button.bind("<ButtonRelease>", self.put_white)

    def click_button(self, x, y):
        print("click button")

        if self.buttons[x][y]["text"] == "":
            if self.turn == "black":
                self.buttons[x][y]["text"] = "⚫"
                self.board.put(2, x, y)
                if self.is_win(2, (x, y)):
                    messagebox.showinfo("Black wins!", "Do you want to play again?")
                else:
                    self.turn = "white"
            self.put_white()

    def put_white(self):
        print("put white")
        print(self.board.board)
        x, y = self.p1.move(self.board)

        print(f"x: {x}, y: {y}")
        self.board.put(1, x, y)
        self.buttons[x][y]["text"] = "⚪️"
        if self.is_win(1, (x, y)):
            messagebox.showinfo("White wins!", "Do you want to play again?")
        else:
            self.turn = "black"

    def is_win(self, stone_num: int, coord: tuple) -> bool:
        """
        check whether the player wins the game when put a stone at the coord
        me: 1 for black stone, 2 for white stone
        coord: (row, col)
        return: true if wins, else false
        """

        def is_chain(stone_num: int, coord: tuple, step: tuple):
            """
            Check whether there is an unbreakable chain of 5 stones at coord such as
            the coordinates of the adjacent stone is the coordinate of the stone +/- step
            :return: true if there is a chain of 5 stones, else false
            """
            total = 0
            row, col = coord

            for i in range(5):
                if total >= 5:
                    return True
                try:
                    if self.board.get(row, col) == stone_num:
                        total += 1
                    else:
                        break
                except IndexError:
                    break
                row += step[0]
                col += step[1]

            row, col = coord
            row -= step[0]
            col -= step[1]

            for i in range(5):
                if total >= 5:
                    return True
                try:
                    if self.board.get(row, col) == stone_num:
                        total += 1
                    else:
                        break
                except IndexError:
                    break
                row -= step[0]
                col -= step[1]

            return False

        #       row      col     diagonal
        steps = [(0, 1), (1, 0), (1, -1), (1, 1)]
        for step in steps:
            if is_chain(stone_num, coord, step):
                return True
Beispiel #8
0
class GUI:
    def __init__(self):
        self.width = 675
        self.height = 700
        self.screen = pygame.display.set_mode(
            (self.width + GRID_WIDTH, self.height + GRID_WIDTH))
        pygame.display.set_caption("Gomoku")
        self.screen.fill(COLOR_BOARD)

        self.turn = "black"  # black goes first
        self.cur_player = 2

        self.ai = PlayerLV3(1, "White")

        self.steps = {  # keep track of the steps of each stone
            "white": [],
            "black": []
        }

        self.board = Board(15, 15)

        self.highlight = None

    def draw_board(self):
        # for i in range(1, 16):
        #     pygame.font.init()
        #     my_font = pygame.font.SysFont("Arial", 12)
        #     text_surface = my_font.render(f"{i}", True, COLOR_BLACK)
        #     text_rect = text_surface.get_rect(center=(self.width / 2, self.height))
        #     self.screen.blit(text_surface, text_rect)

        for i in range(1, 16):
            pygame.draw.line(self.screen, COLOR_BLACK,
                             [GRID_WIDTH * i, GRID_WIDTH],
                             [GRID_WIDTH * i, self.width], 2)
            pygame.draw.line(self.screen, COLOR_BLACK,
                             [GRID_WIDTH, GRID_WIDTH * i],
                             [self.width, GRID_WIDTH * i], 2)

        pygame.draw.circle(self.screen, COLOR_BLACK,
                           [GRID_WIDTH * 8, GRID_WIDTH * 8], 8)

    @staticmethod
    def get_draw_pos(x, y):
        draw_x, draw_y = x - x % GRID_WIDTH, y - y % GRID_WIDTH
        if x % GRID_WIDTH > GRID_WIDTH / 2:  # close to the right point
            draw_x += GRID_WIDTH

        if y % GRID_WIDTH > GRID_WIDTH / 2:  # close to the bottom point
            draw_y += GRID_WIDTH
        return draw_x, draw_y

    def draw_stone(self, x: int, y: int):
        # Do not put stone in occupied position
        if (x, y) in self.steps["white"]:
            return
        if (x, y) in self.steps["black"]:
            return

        if self.turn == "white":
            img_path = path.abspath("imgs/white.png")
            img = pygame.image.load(img_path)
        else:
            img_path = path.abspath("imgs/black.png")
            img = pygame.image.load(img_path)

        scaled_img = pygame.transform.smoothscale(
            img, (GRID_WIDTH // 3 * 2, GRID_WIDTH // 3 * 2))

        self.screen.blit(scaled_img,
                         (x - GRID_WIDTH // 3, y - GRID_WIDTH // 3))

        self.steps[self.turn].append((x, y))

        i, j = x // GRID_WIDTH - 1, y // GRID_WIDTH - 1  # index of the stone in the board
        self.board.put(self.cur_player, i, j)

        # Switch the turn
        if self.turn == "white":
            self.turn = "black"
        else:
            self.turn = "white"

        # Switch the current player
        if self.cur_player == 1:
            self.cur_player = 2
        elif self.cur_player == 2:
            self.cur_player = 1

    def highlight_stone(self, x, y):
        # delete the previous highlight
        # if self.highlight:
        #     self.highlight.move(x, y)
        # if self.highlight:
        #     self.highlight.fill((0, 0, 0))
        #     self.highlight.set_alpha(255)
        #     self.screen.blit(self.highlight, (0, 0), special_flags=(pygame.BLEND_RGBA_ADD))
        # self.highlight = pygame.Surface(((GRID_WIDTH // 3 + 4) * 2, (GRID_WIDTH // 3 + 4) * 2), pygame.SRCALPHA, 32)
        # self.highlight = highlight.convert_alpha(highlight)
        # pygame.gfxdraw.aacircle(self.highlight, (GRID_WIDTH // 3 + 4), (GRID_WIDTH // 3 + 4), GRID_WIDTH // 3 + 3, COLOR_RED)
        # circle = pygame.gfxdraw.filled_circle(self.screen, x, y, GRID_WIDTH // 3 + 3, COLOR_RED)
        pass
        if self.highlight:
            self.highlight.move_ip(10, 10)
            # self.highlight.center = (x, y)
            pygame.display.update(self.highlight)
        else:
            self.highlight = pygame.draw.circle(self.screen, COLOR_RED, (x, y),
                                                GRID_WIDTH // 3 + 4)

        # highlight_rect = self.highlight.get_rect(center=(x, y))
        # self.screen.blit(self.highlight, highlight_rect)

    def ai_move(self):
        # Assume ai is 1 by default
        if self.cur_player == 1:
            i, j = self.ai.move(self.board)
            x, y = (i + 1) * GRID_WIDTH, (j + 1) * GRID_WIDTH
            self.draw_stone(x, y)
            return i, j

    def is_win(self, stone_num: int, coord: tuple) -> bool:
        """
        check whether the player wins the game when put a stone at the coord
        me: 1 for black stone, 2 for white stone
        coord: (row, col)
        return: true if wins, else false
        """
        def is_chain(stone_num: int, coord: tuple, step: tuple):
            """
            Check whether there is an unbreakable chain of 5 stones at coord such as
            the coordinates of the adjacent stone is the coordinate of the stone +/- step
            :return: true if there is a chain of 5 stones, else false
            """
            total = 0
            row, col = coord

            for i in range(5):
                if total >= 5:
                    return True
                try:
                    if self.board.get(row, col) == stone_num:
                        total += 1
                    else:
                        break
                except IndexError:
                    break
                row += step[0]
                col += step[1]

            row, col = coord
            row -= step[0]
            col -= step[1]

            for i in range(5):
                if total >= 5:
                    return True
                try:
                    if self.board.get(row, col) == stone_num:
                        total += 1
                    else:
                        break
                except IndexError:
                    break
                row -= step[0]
                col -= step[1]

            return False

        #       row      col     diagonal
        steps = [(0, 1), (1, 0), (1, -1), (1, 1)]
        for step in steps:
            if is_chain(stone_num, coord, step):
                return True

    def show_win_msg(self, win_stone: str):
        pygame.font.init()
        my_font = pygame.font.SysFont("Arial", 30)
        text_surface = my_font.render(f"{win_stone} Wins!", True, COLOR_BLACK)
        text_rect = text_surface.get_rect(center=(self.width / 2, self.height))
        self.screen.blit(text_surface, text_rect)