tashrique-ahmed

Nim - Game Implementation

Nim - Game Implementation

Project: Nim Game Implementation

Requirements

  • Create a two-player game, Nim, where players take turns removing matchsticks from randomly generated piles.
  • Players can only remove matchsticks from a single pile, and at least one matchstick must be removed per turn.
  • The player forced to remove the last matchstick loses.
  • Include proper validation for user input and game rules.
  • Use an interface (TwoPlayerGame) to ensure modular design and extensibility.

Implementation

Tech Stack: Java

  • Libraries Used:
    • Scanner for user input.
    • Random for initializing matchstick piles.

1. Game Initialization

  • Used the constructor to randomly initialize piles based on given parameters (numPiles, minMatches, maxMatches).

Code Snippet:

public Nim(int numPiles, int minMatches, int maxMatches) {
    numberOfPiles = numPiles;
    currentPlayer = 1; // Player 1 starts the game
    piles = new int[numberOfPiles];

    Random r = new Random();
    for (int i = 0; i < numberOfPiles; i++) {
        piles[i] = r.nextInt((maxMatches - minMatches) + 1) + minMatches;
    }
}

2. Player Input Validation

  • Ensured players can only select valid piles and remove an appropriate number of matchsticks.

Code Snippet:

public boolean isValidMove(int pile, int matches) {
    if (pile < 1 || pile > numberOfPiles) return false; // Pile out of range
    if (matches <= 0 || matches > piles[pile - 1]) return false; // Invalid match count
    return true;
}

3. Board Display

  • Visualized the board with pile numbers and matchsticks dynamically updated after each turn.

Code Snippet:

public void displayBoard() {
    for (int i = 1; i <= numberOfPiles; i++) {
        System.out.print("[" + i + "] ");
        for (int j = 0; j < piles[i - 1]; j++) {
            System.out.print("| ");
        }
        System.out.println();
    }
}


[1] | | |
[2] | | | | | |
[3] | | |

4. Player Turn Logic

  • Managed each player's turn recursively, validating inputs and updating the game state.

Code Snippet:

public void takeATurn() {
    Scanner sc = new Scanner(System.in);
    System.out.println("\nPlayer: " + getPlayer());
    displayBoard();

    // Get valid pile input
    System.out.print("Select a pile [1-" + numberOfPiles + "]: ");
    int pile = sc.nextInt();
    if (pile < 1 || pile > numberOfPiles || piles[pile - 1] == 0) {
        System.out.println("Invalid pile. Try again.");
        takeATurn();
        return;
    }

    // Get valid matchstick input
    System.out.print("Select number of matches [1-" + piles[pile - 1] + "]: ");
    int matches = sc.nextInt();
    if (!isValidMove(pile, matches)) {
        System.out.println("Invalid move. Try again.");
        takeATurn();
        return;
    }

    // Update the pile
    piles[pile - 1] -= matches;

    // Check if game is over
    if (isGameOver()) {
        System.out.println("\nPlayer " + getPlayer() + " wins!");
        return;
    }

    // Switch player and continue the game
    currentPlayer = (currentPlayer == 1) ? 2 : 1;
    takeATurn();
}

5. End-of-Game Check

  • Determined if all piles were empty, signaling the end of the game.

Code Snippet:

public boolean isGameOver() {
    for (int pile : piles) {
        if (pile > 0) return false;
    }
    return true;
}

Key Decisions

Randomized Piles: Used java.util.Random to ensure each game setup is unique, enhancing replayability.

Recursive Turn Management: Simplified the flow of the game by recursively calling the takeATurn() method for the next player.

Interface Usage: Implemented the TwoPlayerGame interface to enforce a modular and extensible design.

Input Validation: Included checks for invalid pile/matchstick inputs to prevent crashes or undefined behavior.

Challenges

  • Recursive Function Calls: Faced potential infinite loops on invalid input but resolved by adding appropriate return statements.
  • Dynamic Validation: Needed robust checks for varying pile sizes and matchstick limits, especially for edge cases.

Results

  • Fully functional two-player console game.
  • Code adheres to clean design principles with modular methods.
  • Dynamically updates the board and ensures fair play for both players.

Sample Gameplay:

Reflection

  • What I Learned:
    • Practical use of interfaces in Java.
    • Managing user input and dynamic game states in console applications.
    • Implementing recursion to simplify game logic.
  • Future Improvements:
    • Add an AI opponent with a winning strategy.
    • Implement a graphical interface using JavaFX for a better user experience.