The connectide
module provides a class-based implementation of the Connect-4 game. This module includes various functionalities to represent the game board, manage game state, and perform operations like move generation, checking for wins, adding and undoing moves, and running performance tests.
noPce = 0
: Represents an empty spot on the board.red = 1
: Represents a red piece on the board.yellow = 2
: Represents a yellow piece on the board.
class Board:
def __init__(self, board=None, turn=red):
if board is None:
self.board = [[noPce for _ in range(7)] for _ in range(6)]
else:
self.board = board
self.turn = turn
board
: A 6x7 matrix representing the game board. Defaults to an empty board if not provided.turn
: Indicates the current player's turn. Defaults tored
.
def isConnect4(self, row: int, col: int) -> bool:
board = self.board
pce = board[row][col]
if pce is not noPce:
if row >= 3 and board[row - 1][col] == pce and board[row - 2][col] == pce and board[row - 3][col] == pce:
return True
if row <= 2 and board[row + 1][col] == pce and board[row + 2][col] == pce and board[row + 3][col] == pce:
return True
if col <= 3 and board[row][col + 1] == pce and board[row][col + 2] == pce and board[row][col + 3] == pce:
return True
if col >= 3 and board[row][col - 1] == pce and board[row][col - 2] == pce and board[row][col - 3] == pce:
return True
if row >= 3 and col <= 3 and board[row - 1][col + 1] == pce and board[row - 2][col + 2] == pce and board[row - 3][col + 3] == pce:
return True
if row <= 2 and col <= 3 and board[row + 1][col + 1] == pce and board[row + 2][col + 2] == pce and board[row + 3][col + 3] == pce:
return True
if row >= 3 and col >= 3 and board[row - 1][col - 1] == pce and board[row - 2][col - 2] == pce and board[row - 3][col - 3] == pce:
return True
if row <= 2 and col >= 3 and board[row + 1][col - 1] == pce and board[row + 2][col - 2] == pce and board[row + 3][col - 3] == pce:
return True
return False
row
: The row of the square.col
: The column of the square.- Returns
True
if the square is part of a 4-in-a-row, otherwiseFalse
.
def isCheckmate(self) -> bool:
for row in range(6):
for col in range(7):
if self.isConnect4(row, col):
return True
return False
- Returns
True
if a 4-in-a-row is present on the board, otherwiseFalse
.
def pseudoLegalMoveGen(self) -> list[int]:
moves = []
board = self.board
for col in range(7):
if board[0][col] == noPce:
moves.append(col)
return moves
- Returns a list of pseudo-legal moves (legal columns), which includes moves even after a board is in checkmate.
def moveGen(self) -> list[int]:
moves = []
if not self.isCheckmate():
board = self.board
for col in range(7):
if board[0][col] == noPce:
moves.append(col)
return moves
- Returns a list of legal moves (legal columns).
def addPiece(self, col: int):
pce = self.turn
for row in range(6):
if self.board[row][col] != noPce:
self.board[row - 1][col] = pce
self.turn = 3 - pce
break
if row == 5 and self.board[row][col] == noPce:
self.board[row][col] = pce
self.turn = 3 - pce
break
col
: The column to add a piece.
def undoMove(self, col: int):
for row in range(6):
if self.board[row][col] != noPce:
self.board[row][col] = noPce
self.turn = 3 - self.turn
break
col
: The column from which to undo a move.
def getNumMoves(self) -> int:
moves = 0
for i in range(6):
for j in range(7):
if self.board[i][j] != noPce:
moves += 1
return moves
- Returns the number of pieces on the board.
def show(self, debug: bool = False):
board = self.board
for i in range(6):
for j in range(7):
pce = board[i][j]
pceChar = "-" if pce == noPce else "R" if pce == red else "Y"
print(pceChar, end=" ")
if i != 5:
print()
print("\n-------------")
print("1 2 3 4 5 6 7 ")
if debug:
print(f"Turn: {'red' if self.turn == red else 'yellow'}")
print(f"Moves Played: {self.getNumMoves()}")
print(f"Checkmate Status: {'True' if self.isCheckmate() else 'False'}")
print()
debug
(optional): Whether to print additional debug information. Defaults toFalse
.
def chash(self) -> str:
fen = ""
for i in range(len(self.board)):
for j in range(len(self.board[0])):
fen += str(self.board[i][j])
return fen + str(self.turn)
- Returns a simple hash of the board position.
def flatten(self) -> list[int]:
flat = []
for i in range(len(self.board)):
for j in range(len(self.board[0])):
flat.append(self.board[i][j])
return flat
- Returns the flattened board array as a 1D list.
def parse(self, string: str):
i = 0
j = 0
count = 0
for char in string:
count += 1
if count == 43:
self.turn = int(char)
break
if i == 7:
i = 0
j += 1
self.board[j][i] = int(char)
i += 1
string
: The string board representation created bychash
.
def perfD(self, depth: int) -> int:
if depth == 0:
return 1
nodes = 0
legals = self.moveGen()
for col in legals:
self.addPiece(col)
nodes += self.perfD(depth - 1)
self.undoMove(col)
return nodes
depth
: The depth to search to.- Returns the number of nodes at a specified depth.
def perfT(self, maxDepth: int):
startTime = time.time()
for depth in range(1, maxDepth + 1):
nodes = self.perfD(depth)
elapsed = time.time() - startTime
print(
f"info string perft depth {depth} time {int(elapsed*1000)} nodes {nodes} nps {int(nodes / (elapsed + 0.000000001))}"
)
maxDepth
: The maximum depth to run for this test.- Initiates a performance test.
board = Board()
board.show()
board.parse("0000000000000000000000000000000000000000012")
board.perfT(7)
info string perft depth 1 time 0 nodes 7 nps 7000000000
info string perft depth 2 time 2 nodes 49 nps 16381
info string perft depth 3 time 5 nodes 343 nps 62905
info string perft depth 4 time 17 nodes 2401 nps 137464
info string perft depth 5 time 101 nodes 16807 nps 165664
info string perft depth 6 time 678 nodes 117649 nps 173408
info string perft depth 7 time 4826 nodes 823536 nps 170629
info string perft depth 8 time 34460 nodes 5673234 nps 164630