Hex Game with AI (Alpha–Beta Pruning)
Hex is a connection strategy game: one player links left–right edges, the other top–bottom, on an N×N hex grid. This implementation renders a 7×7 board with alternating turns and detects wins by checking path connectivity. The AI uses a Minimax search with Alpha–Beta pruning for efficient move selection.
Built in Python with pygame for graphics—simple to run locally and ideal for experimenting with search heuristics.
Features
Hex Board Rendering
Visualizes a 7×7 grid with offset columns and alternating turns.
AI Opponent
Minimax search with Alpha–Beta pruning for efficient decision-making.
PvP / PvAI
Human (Red) vs AI (Blue); extendable to PvP with minor changes.
Win Detection
Union–find (disjoint-set) checks edge-to-edge connectivity.
Move Validation
Enforces empty-cell placement to prevent illegal moves.
Heuristic Scoring
Evaluates proximity to target edge and local connectivity.
Architecture & Code
Design (separation of concerns)
- Board & Rendering: pygame window, images, piece positions, input mapping.
- AI Engine: Minimax(depth=3) with Alpha–Beta pruning; heuristic evaluation.
- Connectivity: Disjoint-set (union–find) for edge-to-edge win checks.
- Game Loop: Event handling → human move → win check → AI move → win check.
Key Parameters & Notes
- Board size:
7×7cells. - Minimax search depth:
3plies with pruning. - Heuristic: edge proximity + adjacency bonuses (Blue vs Red objectives).
- Connectivity: union neighbors on each placement; compare edge sets for a win.
Board state & positions (trimmed)
main.py# 7×7 empty board; '' means empty, 'R'/'B' are players
pieceSet = [["" for _ in range(7)] for _ in range(7)]
piecePos = [[(0, 0) for _ in range(7)] for _ in range(7)]
def setPosition():
# compute on-screen pixel coords for each hex cell (staggered)
for x in range(7):
for y in range(7):
piecePos[x][y] = 150 + (x * 70) + (y * 35), 85 + (y * 60)
Disjoint-set (union–find)
disjointSet.pyclass disjointSet:
def __init__(self, elements):
self.root = {e: e for e in elements}
def find(self, a):
if self.root[a] != a:
self.root[a] = self.find(self.root[a])
return self.root[a]
def union(self, a, b):
self.root[self.find(a)] = self.find(b)
Alpha–Beta pruning (depth=3)
main.pydef minimax(isMax, depth, alpha, beta):
if depth == 0 or gameOver:
return evaluateBoard()
if isMax: # Blue (AI)
best = -math.inf
for x in range(7):
for y in range(7):
if pieceSet[x][y] == '':
pieceSet[x][y] = 'B'
best = max(best, minimax(False, depth-1, alpha, beta))
pieceSet[x][y] = ''
alpha = max(alpha, best)
if beta <= alpha: break # prune
return best
else: # Red (human)
best = math.inf
for x in range(7):
for y in range(7):
if pieceSet[x][y] == '':
pieceSet[x][y] = 'R'
best = min(best, minimax(True, depth-1, alpha, beta))
pieceSet[x][y] = ''
beta = min(beta, best)
if beta <= alpha: break # prune
return best
Evaluation function (heuristic)
main.pydef evaluateBoard():
blueScore = redScore = 0
for x in range(7):
for y in range(7):
if pieceSet[x][y] == 'B':
blueScore += (6 - x) # closer to right edge
for dx, dy in [(1,0),(1,-1),(0,1),(0,-1),(-1,0),(-1,1)]:
nx, ny = x+dx, y+dy
if 0 <= nx < 7 and 0 <= ny < 7 and pieceSet[nx][ny]=='B':
blueScore += 2
elif pieceSet[x][y] == 'R':
redScore += (6 - y) # closer to bottom edge
for dx, dy in [(1,0),(1,-1),(0,1),(0,-1),(-1,0),(-1,1)]:
nx, ny = x+dx, y+dy
if 0 <= nx < 7 and 0 <= ny < 7 and pieceSet[nx][ny]=='R':
redScore += 2
return blueScore - redScore
Win check (Blue edge-to-edge via union–find)
main.pydef blueWinCheck(x, y):
# union with like-colored neighbors
for dx, dy in [(1,0),(1,-1),(0,1),(0,-1),(-1,0),(-1,1)]:
nx, ny = x+dx, y+dy
if 0 <= nx <= 6 and 0 <= ny <= 6 and pieceSet[nx][ny] == 'B':
dsBlue.union((x, y), (nx, ny))
# if any row index i has left and right edges in same set → win
for i in range(7):
if dsBlue.find((0, i)) == dsBlue.find((6, i)):
return True
return False