Functional Scope (In-Scope)
- Scalable N×N Grid Board: Support flexible board dimensions beyond standard 3×3 setups.
- Multi-Player Integration: Handle gameplay turn management for two or more players dynamically.
- Pluggable Win Strategies: Decouple win detection patterns using a clean strategy system.
- O(1) Win Checks: Detect horizontal, vertical, and diagonal win configurations instantly in constant time.
- Draw Detection: Track moves counts to declare complete board draw games correctly.
Explicit Boundaries (Out-of-Scope)
- No Intelligent AI Bots: Out-of-scope to write Minimax or Alpha-Beta pruning move predictors.
- No Play History Replays: Focuses on active gameplay state structures rather than serializing records.
Practical, high-performance reference implementations in Java and Python:
// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
class Player {
private final String name;
private final char symbol;
public Player(String name, char symbol) {
this.name = name;
this.symbol = symbol;
}
public String getName() { return name; }
public char getSymbol() { return symbol; }
}
class Board {
private final int size;
private final char[][] grid;
private int movesCount = 0;
public Board(int size) {
this.size = size;
this.grid = new char[size][size];
for (int i = 0; i < size; i++) {
Arrays.fill(grid[i], ' ');
}
}
public boolean placeMove(int r, int c, char symbol) {
if (r < 0 || r >= size || c < 0 || c >= size || grid[r][c] != ' ') {
return false;
}
grid[r][c] = symbol;
movesCount++;
return true;
}
public boolean isFull() {
return movesCount == size * size;
}
public char getSymbolAt(int r, int c) {
return grid[r][c];
}
public int getSize() {
return size;
}
public void printBoard() {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
System.out.print(" " + grid[i][j] + " ");
if (j < size - 1) System.out.print("|");
}
System.out.println();
if (i < size - 1) {
System.out.println("-".repeat(size * 4 - 1));
}
}
System.out.println();
}
}
interface WinStrategy {
void init(int size);
void onMove(int r, int c, char symbol);
boolean checkWin(Board board, int r, int c, char symbol);
}
class StandardWinStrategy implements WinStrategy {
private int size;
private final Map<Character, int[]> rowCounts = new HashMap<>();
private final Map<Character, int[]> colCounts = new HashMap<>();
private final Map<Character, Integer> diagCounts = new HashMap<>();
private final Map<Character, Integer> antiDiagCounts = new HashMap<>();
@Override
public void init(int size) {
this.size = size;
rowCounts.clear();
colCounts.clear();
diagCounts.clear();
antiDiagCounts.clear();
}
@Override
public void onMove(int r, int c, char symbol) {
rowCounts.putIfAbsent(symbol, new int[size]);
colCounts.putIfAbsent(symbol, new int[size]);
diagCounts.putIfAbsent(symbol, 0);
antiDiagCounts.putIfAbsent(symbol, 0);
rowCounts.get(symbol)[r]++;
colCounts.get(symbol)[c]++;
if (r == c) {
diagCounts.put(symbol, diagCounts.get(symbol) + 1);
}
if (r + c == size - 1) {
antiDiagCounts.put(symbol, antiDiagCounts.get(symbol) + 1);
}
}
@Override
public boolean checkWin(Board board, int r, int c, char symbol) {
int[] rows = rowCounts.get(symbol);
int[] cols = colCounts.get(symbol);
if (rows == null || cols == null) return false;
int diag = diagCounts.getOrDefault(symbol, 0);
int antiDiag = antiDiagCounts.getOrDefault(symbol, 0);
return rows[r] == size || cols[c] == size || diag == size || antiDiag == size;
}
}
class FourCornersWinStrategy implements WinStrategy {
private int size;
@Override
public void init(int size) {
this.size = size;
}
@Override
public void onMove(int r, int c, char symbol) {}
@Override
public boolean checkWin(Board board, int r, int c, char symbol) {
if (size < 2) return false;
return board.getSymbolAt(0, 0) == symbol &&
board.getSymbolAt(0, size - 1) == symbol &&
board.getSymbolAt(size - 1, 0) == symbol &&
board.getSymbolAt(size - 1, size - 1) == symbol;
}
}
class TicTacToeGame {
private final Board board;
private final List<Player> players;
private final WinStrategy winStrategy;
private final ReentrantLock gameLock = new ReentrantLock();
private int currentPlayerIndex = 0;
private boolean isGameOver = false;
private Player winner = null;
public TicTacToeGame(int size, List<Player> players, WinStrategy winStrategy) {
this.board = new Board(size);
this.players = players;
this.winStrategy = winStrategy;
this.winStrategy.init(size);
}
public boolean makeMove(int r, int c) {
gameLock.lock();
try {
if (isGameOver) {
System.out.println("Move failed: Game is already over.");
return false;
}
Player player = players.get(currentPlayerIndex);
System.out.println("Player " + player.getName() + " (" + player.getSymbol() + ") moves to (" + r + ", " + c + ")");
if (!board.placeMove(r, c, player.getSymbol())) {
System.out.println("Move failed: Cell (" + r + ", " + c + ") is invalid or occupied.");
return false;
}
winStrategy.onMove(r, c, player.getSymbol());
board.printBoard();
if (winStrategy.checkWin(board, r, c, player.getSymbol())) {
isGameOver = true;
winner = player;
System.out.println("GAME OVER! Winner: " + player.getName() + " (" + player.getSymbol() + ")");
return true;
}
if (board.isFull()) {
isGameOver = true;
System.out.println("GAME OVER! It's a DRAW.");
return true;
}
currentPlayerIndex = (currentPlayerIndex + 1) % players.size();
return true;
} finally {
gameLock.unlock();
}
}
public boolean isGameOver() { return isGameOver; }
public Player getWinner() { return winner; }
}
public class TicTacToeDriver {
public static void main(String[] args) {
System.out.println("=== TIC-TAC-TOE GAME SIMULATION (STANDARD WIN) ===");
List<Player> players2 = Arrays.asList(
new Player("Alice", 'X'),
new Player("Bob", 'O')
);
TicTacToeGame game1 = new TicTacToeGame(3, players2, new StandardWinStrategy());
game1.makeMove(0, 0); // Alice
game1.makeMove(1, 0); // Bob
game1.makeMove(1, 1); // Alice
game1.makeMove(2, 0); // Bob
game1.makeMove(2, 2); // Alice wins (Diagonal)
System.out.println("=== TIC-TAC-TOE 3-PLAYER GAME SIMULATION ===");
List<Player> players3 = Arrays.asList(
new Player("Alice", 'X'),
new Player("Bob", 'O'),
new Player("Charlie", 'A')
);
TicTacToeGame game2 = new TicTacToeGame(4, players3, new StandardWinStrategy());
game2.makeMove(0, 0); // Alice
game2.makeMove(0, 1); // Bob
game2.makeMove(0, 2); // Charlie
game2.makeMove(1, 1); // Alice
game2.makeMove(1, 2); // Bob
game2.makeMove(1, 3); // Charlie
game2.makeMove(2, 2); // Alice
game2.makeMove(2, 3); // Bob
game2.makeMove(2, 0); // Charlie
game2.makeMove(3, 3); // Alice wins (Diagonal)
System.out.println("=== TIC-TAC-TOE FOUR CORNERS WIN SIMULATION ===");
TicTacToeGame game3 = new TicTacToeGame(3, players2, new FourCornersWinStrategy());
game3.makeMove(0, 0); // Alice
game3.makeMove(1, 1); // Bob
game3.makeMove(0, 2); // Alice
game3.makeMove(1, 0); // Bob
game3.makeMove(2, 0); // Alice
game3.makeMove(2, 1); // Bob
game3.makeMove(2, 2); // Alice wins (Four Corners!)
}
}