In this article, I’ll share my approach to solving a challenging programming question posed during the Zoho 3rd-round technical interview. The task?
Develop a robust n x n Tic Tac Toe game that runs in the command line, adheres to coding standards, and handles all edge cases independently.
Let’s dive into the requirements, design considerations, and the Java implementation for this program.
Table of Contents
The Question: Build an n x n Tic Tac Toe Game
Requirements:
- Dynamic grid size: The game should work for any grid size
n
. - Two-player functionality: Players alternate turns, marking
X
orO
. - Win detection: Determine the winner based on row, column, or diagonal matches.
- Edge cases: Handle invalid moves, boundary conditions, and draw scenarios.
- Clean, optimized code: Follow coding standards and close resources to prevent memory leaks.
Approach and Design
Before writing the code, I focused on structuring the solution logically:
- Board Initialization: Set up an
n x n
board initialized with-
. - Player Interaction: Alternate between two players (
X
andO
) and validate moves. - Win Check: Implement efficient methods to check rows, columns, and diagonals for a win condition.
- Game States: Track the number of moves, detect invalid moves, and determine draw conditions.
- Edge Cases: Handle scenarios like out-of-bound inputs, overwriting existing moves, and ensuring diagonal checks are robust.
The Java Code: Step-by-Step Implementation
Here’s the complete Java program for the n x n Tic Tac Toe game. I’ve added comments to explain the logic behind each block:
import java.util.Scanner;
public class TicTacToe {
// Board to hold the game state
private static char[][] board;
private static int n; // Size of the grid
private static char currentPlayer; // Current player's symbol ('X' or 'O')
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Ask user for grid size
System.out.print("Enter the size of the grid (n x n): ");
n = scanner.nextInt();
// Initialize the board
board = new char[n][n];
initializeBoard();
// Start with player 'X'
currentPlayer = 'X';
int moves = 0;
boolean gameWon = false;
// Game loop: Continue until all moves are played or a player wins
while (moves < n * n && !gameWon) {
printBoard(); // Show the current board to players
System.out.printf("Player %c, enter your move (row and column): ", currentPlayer);
int row = scanner.nextInt();
int col = scanner.nextInt();
// Validate the move
if (isValidMove(row, col)) {
board[row][col] = currentPlayer; // Mark the board
moves++; // Increment move count
// Check if the current player wins
if (checkWin(row, col)) {
gameWon = true;
printBoard();
System.out.printf("Player %c wins!%n", currentPlayer);
} else {
// Switch to the next player
currentPlayer = (currentPlayer == 'X') ? 'O' : 'X';
}
} else {
System.out.println("Invalid move. Try again.");
}
}
// If no one wins, it's a draw
if (!gameWon) {
printBoard();
System.out.println("The game is a draw!");
}
scanner.close(); // Close the scanner to prevent resource leaks
}
// Initialize the board with empty cells
private static void initializeBoard() {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
board[i][j] = '-';
}
}
}
// Print the current state of the board
private static void printBoard() {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
System.out.print(board[i][j] + " ");
}
System.out.println();
}
}
// Validate if a move is within bounds and the cell is empty
private static boolean isValidMove(int row, int col) {
return row >= 0 && row < n && col >= 0 && col < n && board[row][col] == '-';
}
// Check if the current move results in a win
private static boolean checkWin(int row, int col) {
return checkRow(row) || checkColumn(col) || checkDiagonals();
}
// Check if all cells in the row belong to the current player
private static boolean checkRow(int row) {
for (int col = 0; col < n; col++) {
if (board[row][col] != currentPlayer) {
return false;
}
}
return true;
}
// Check if all cells in the column belong to the current player
private static boolean checkColumn(int col) {
for (int row = 0; row < n; row++) {
if (board[row][col] != currentPlayer) {
return false;
}
}
return true;
}
// Check the two diagonals for a win condition
private static boolean checkDiagonals() {
boolean diag1 = true, diag2 = true;
for (int i = 0; i < n; i++) {
if (board[i][i] != currentPlayer) {
diag1 = false;
}
if (board[i][n - i - 1] != currentPlayer) {
diag2 = false;
}
}
return diag1 || diag2;
}
}
Key Features
1. Dynamic Grid Size
- The grid size is determined at runtime, allowing flexibility.
2. Efficient Win Checks
- Row, column, and diagonal checks are implemented with minimal iteration.
3. Edge Case Handling
- Prevents moves outside the grid or on already occupied cells.
- Detects draw scenarios after all moves are exhausted.
4. Optimized Logic
- Logical separation of concerns: Initialization, printing, validation, and game logic.
Conclusion
This implementation demonstrates a clean and efficient solution for the n x n Tic Tac Toe game in Java. It highlights the importance of proper coding standards, handling edge cases, and maintaining clarity in logic — critical for acing coding interviews like Zoho’s.
Let me know your thoughts in the comments. Have you encountered similar challenges in interviews? Let’s discuss!