Machine Coding Problem

Tic-Tac-Toe

maco30maco60macoAllgamesscalable-n×n-board-&-win-strategies
Commonly Asked By:GoogleMicrosoftAmazonUber

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!)
    }
}