CoinStrip Game Implementation

Overview
The CoinStrip Game simulates a two-player board game where players take turns moving coins leftward under specific rules. The game is implemented in Java using the structure5.Vector<E> class and follows the TwoPlayerGame interface.
Code Review and Analysis
1. Game Initialization
Code Section:
public CoinStrip(int numTiles, int numCoins) {
    numberOfTiles = numTiles;
    currentPlayer = 1;
    tiles = new Vector<>(numberOfTiles);
    for (int m = 0; m < numberOfTiles; m++) {
        tiles.add(-1);
    }
    generateBoard();
    while (coinCount() < 3) {
        generateBoard();
    }
}
Strengths:
- The constructor ensures the board is initialized with the required number of tiles.
- The generateBoard()method randomly places coins, ensuring variation across games.
- The while loop ensures at least 3 coins are present, satisfying game rules.
Opportunities for Improvement:
Optimization: The generateBoard() method might run multiple times before reaching a valid state. Instead, generate coins while creating the board to avoid redundant iterations.
Code Comments: Add comments to explain the initialization logic for better readability.
2. Board Representation
Code Section:
tiles = new Vector<>(numberOfTiles);
for (int m = 0; m < numberOfTiles; m++) {
    tiles.add(-1);
}
Strengths:
- The use of Vector<Integer>from thestructure5library allows dynamic resizing and easy manipulation of the board.
- The -1placeholder makes it clear that a tile is unoccupied.
Considerations for Engineers:
- This design prioritizes simplicity but requires frequent iterations (e.g., checking occupied tiles). A sparse representation (e.g., Map<Integer, Integer>where key = position, value = coin) might be more efficient.
3. Move Validation
Code Section:
public boolean isValidMove(int resource, int updatedValue) {
    if (!tiles.contains(resource)) {
        System.out.println("\nThere is no such coin as [" + resource + "]. Try again!");
        return false;
    }
    if (updatedValue < 1 || updatedValue > numberOfTiles) {
        System.out.println("\nYou can't move this coin [" + updatedValue + "] steps. Try again!");
        return false;
    }
    for (int i = 1; i <= updatedValue; i++) {
        if (tiles.get(getResource(resource) - i) > -1) {
            System.out.println("\nYou can't move this coin. Try again!");
            return false;
        }
    }
    return true;
}
Strengths:
- Thorough validation of moves, including checks for out-of-bounds and illegal placements.
- Prevents moves that violate the game's rules, such as jumping over other coins or occupying the same tile.
Opportunities for Improvement:
Error Messages: Consolidate error messages for better clarity. Currently, multiple checks may confuse the user.
Code Efficiency: The loop for checking tiles (for (int i = 1; i <= updatedValue; i++)) could be replaced with a single range check.
Example Optimization:
if (tiles.subList(getResource(resource) - updatedValue, getResource(resource)).contains(resource)) {
    return false;
}
4. Displaying the Board
public void displayBoard() {
    for (int i = 0; i < numberOfTiles; i++) {
        if (tiles.get(i) > 0) {
            System.out.print(tiles.get(i) + " ");
        } else {
            System.out.print("- ");
        }
    }
}
Strengths:
- Clear and minimalistic visualization of the board.
- Helps players easily identify coin positions and empty tiles.
Suggestions:
Add column indices for better player guidance, especially on larger boards.
Use a newline at the end to avoid cluttered output.
Enhanced Example:
public void displayBoard() {
    System.out.print("Index: ");
    for (int i = 0; i < numberOfTiles; i++) {
        System.out.print(i + " ");
    }
    System.out.println();
    System.out.print("Tiles: ");
    for (int i = 0; i < numberOfTiles; i++) {
        System.out.print((tiles.get(i) > 0 ? tiles.get(i) : "-") + " ");
    }
    System.out.println("\n");
}
5. Turn Management
public void takeATurn() {
    Scanner sc = new Scanner(System.in);
    System.out.print("\nPlayer: [" + getPlayer() + "]\n");
    displayBoard();
    System.out.print("\n[1] Select the coin and [2] How many tiles you want to move: ");
    try {
        userCoinNumber = sc.nextInt();
        userTileNumber = sc.nextInt();
    } catch (Exception e) {
        System.out.println("\nInvalid input. Please put an integer number.\n");
        takeATurn();
    }
    if (isValidMove(userCoinNumber, userTileNumber)) {
        setResource(userCoinNumber, userTileNumber);
        if (isGameOver()) {
            displayBoard();
            System.out.println("\nPlayer " + getPlayer() + " is the winner!");
        } else {
            setPlayer(getPlayer());
            takeATurn();
        }
    } else {
        takeATurn();
    }
}
Strengths:
- Recursive turn handling is clean and avoids unnecessary loops.
- Input validation ensures the game flow remains smooth.
Opportunities for Improvement:
Avoid Infinite Recursion: Recursive calls in takeATurn() could lead to stack overflow on invalid inputs. Replace with a loop.
Error Handling: Catch input exceptions more gracefully and prompt users again without repeating the entire method.
Improved Code:
public void takeATurn() {
    Scanner sc = new Scanner(System.in);
    while (true) {
        System.out.print("\nPlayer: [" + getPlayer() + "]\n");
        displayBoard();
        System.out.print("\n[1] Select the coin and [2] How many tiles you want to move: ");
        try {
            userCoinNumber = sc.nextInt();
            userTileNumber = sc.nextInt();
        } catch (Exception e) {
            System.out.println("\nInvalid input. Please enter integers only.");
            sc.nextLine(); // Clear the scanner buffer
            continue;
        }
        if (isValidMove(userCoinNumber, userTileNumber)) {
            setResource(userCoinNumber, userTileNumber);
            break;
        }
    }
    if (isGameOver()) {
        displayBoard();
        System.out.println("\nPlayer " + getPlayer() + " is the winner!");
    } else {
        setPlayer(getPlayer());
        takeATurn();
    }
}
Engineering Principles I tried to follow
Modularity: Implements the TwoPlayerGame interface, promoting clean, reusable code.
Dynamic Representation: The use of Vector<E> ensures flexibility in board design and coin placement.
Game Logic: Comprehensive validation prevents illegal moves and ensures a fair game.
Opportunities for Scalability: The current design can be easily extended with additional features (e.g., AI opponent).
Reflection and Recommendations
- Strengths: The implementation demonstrates clean game logic and proper use of Java's data structures. Recursive methods simplify logic but may need optimization for edge cases.
- Improvements:- Replace recursion in takeATurn()with loops for better scalability.
- Add comments to explain critical logic blocks for readability.
- Optimize board generation to avoid redundant iterations.
 
- Replace recursion in 
- Potential Enhancements: Add an AI player with heuristic-based moves or a graphical interface for a more interactive experience.
