connect-4-API / game.py
Gruhit Patel
init-backend
1fab54b verified
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