File size: 4,447 Bytes
1fab54b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
from copy import deepcopy
import numpy as np
from typing import Union

class Connect4:
    def __init__(self, board:'Connect4'=None, row:int = 6, col:int =7):
        self.row = row
        self.col = col
        self.player_1 = 1
        self.player_2 = -1

        self.board = np.zeros((self.row, self.col))

        self.winning_start = None
        self.winning_end = None


        if board is not None:
            self.__dict__ = deepcopy(board.__dict__)

    def drop_piece(self, action: int) -> 'Connect4':

        board = Connect4(board=self)

        # Find the row in that column which is valid to drop piece
        valid_row_idx = sum(board.board[:, action] == 0) - 1
        board.board[valid_row_idx, action] = self.player_1
        (board.player_1, board.player_2) = (self.player_2, self.player_1)

        return board, board.is_win()

    # Get the encoded state for the board
    def get_state(self) -> np.ndarray:
        # Create a layer to state the player turn
        turn = np.ones_like(self.board) if self.player_1 == 1 else np.zeros_like(self.board)
        enc_state = np.stack(
            (self.board == -1, self.board == 0, self.board == 1, turn)
        ).astype(np.int32)

        return enc_state

    # check if the board results in a draw state
    def is_draw(self):
        return (self.board != 0).all()

    def is_win(self) -> Union[None, int]:
        # Initially no one is winner
        winner = None

        # Check for columns
        if self.col_win():
            winner = self.player_2
        # Check for rows
        elif self.row_win():
            winner = self.player_2
        # Check for diagonals
        elif self.diag_win():
            winner = self.player_2
        return winner

    # Check for column win
    def col_win(self) -> bool:
        # Iterate over each column
        for c in range(self.col):
            # for 4 consequtive rows
            for r in range(self.row-3):
                # if the the all 4 element are of player who made move then its win
                if sum(self.board[r:r+4, c] == self.player_2) == 4:
                    self.winning_start = (c, r)
                    self.winning_end = (c, r+3)
                    return True

        return False

    # check for win in row
    def row_win(self) -> bool:
        # Iterate over each row
        for r in range(self.row):
            # For 4 consequtive cols
            for c in range(self.col-3):
                # If all of 4 elements are of player who made move then its win
                if sum(self.board[r, c:c+4] == self.player_2) == 4:
                    self.winning_start = (c, r)
                    self.winning_end = (c+3, r)
                    return True

        return False

    # check for win in diagonal
    def diag_win(self) -> bool:
        # For a window of 4x4 if the main diag or other diag has
        # same disc of player who made move then its a win
        for r in range(self.row-3):
            for c in range(self.col-3):
                # Get a window of size 4x4
                window = self.board[r:r+4, c:c+4]

                # If all 4 element of main diag(/) is player who made move then its win
                if sum(np.diag(window) == self.player_2) == 4:
                    self.winning_start = (c+3, r)
                    self.winning_end = (c, r+3)
                    # print("WinningMain Diag: ", self.winning_start, " - ", self.winning_end)
                    return True

                # If all 4 element of other diag(\) is player who made move then its win
                if sum(np.diag(window[:, ::-1]) == self.player_2) == 4:
                    self.winning_start = (c, r)
                    self.winning_end = (c+3, r+3)
                    # print("WinningMain Diag: ", self.winning_start, " - ", self.winning_end)
                    return True

        return False

    # get a list of valid move that can be played by the current player
    def get_valid_moves(self) -> np.array:
        valid_cols = [False]*self.col
        for c in range(self.col):
            if self.board[0, c] == 0:
                valid_cols[c] = True

        return np.array(valid_cols, dtype=bool)

    def __str__(self) -> str:
        print_str = ""
        for r in range(self.row):
            for c in range(self.col):
                print_str += f"{self.board[r, c]:>3.0f}"

            print_str += "\n"

        return print_str