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.