Code Issues Solver

Zombie Game Enhancements in C++

This document details improvements to a console-based zombie game in C++, addressing input handling, zombie movement logic, game restart functionality, and code duplication. The revised code enhances player experience while maintaining


Empty image or helper icon

Prompt

#include 
#include 
#include 
#include 
#include 

using namespace std;

const int WIDTH = 20;
const int HEIGHT = 10;
const char PLAYER_CHAR = 'P';
const char ZOMBIE_CHAR = 'Z';
const char EMPTY_CHAR = '.';

struct Entity {
    int x, y;
};

class Game {
public:
    Game() {
        player.x = WIDTH / 2;
        player.y = HEIGHT / 2;
        srand(static_cast(time(0)));
        spawnZombies(3);
    }

    void run() {
        char input;
        while (true) {
            clearScreen();
            printBoard();
            cout << "Move (WASD): ";
            cin >> input;
            if (!handleInput(input)) {
                cout << "Invalid input. Please use 'W', 'A', 'S', or 'D'." << endl;
                continue;
            }
            updateZombies();
            if (checkCollision()) {
                cout << "Game Over!" << endl;
                break;
            }
        }
    }

private:
    Entity player;
    vector zombies;

    void spawnZombies(int count) {
        zombies.clear(); // Clear existing zombies
        for (int i = 0; i < count; i++) {
            Entity zombie;
            // Ensure zombies do not spawn at the player's location
            do {
                zombie.x = rand() % WIDTH;
                zombie.y = rand() % HEIGHT;
            } while (zombie.x == player.x && zombie.y == player.y);
            zombies.push_back(zombie);
        }
    }

    void printBoard() {
        char board[HEIGHT][WIDTH];

        for (int i = 0; i < HEIGHT; i++) {
            for (int j = 0; j < WIDTH; j++) {
                board[i][j] = EMPTY_CHAR;
            }
        }

        board[player.y][player.x] = PLAYER_CHAR;
        for (const auto& zombie : zombies) {
            board[zombie.y][zombie.x] = ZOMBIE_CHAR;
        }

        for (int i = 0; i < HEIGHT; i++) {
            for (int j = 0; j < WIDTH; j++) {
                cout << board[i][j] << ' ';
            }
            cout << endl;
        }
    }

    bool handleInput(char input) {
        switch (input) {
            case 'w': if (player.y > 0) --player.y; break;
            case 's': if (player.y < HEIGHT - 1) ++player.y; break;
            case 'a': if (player.x > 0) --player.x; break;
            case 'd': if (player.x < WIDTH - 1) ++player.x; break;
            default: return false; // Invalid input
        }
        return true; // Valid input
    }

    void updateZombies() {
        for (auto& zombie : zombies) {
            // Random movement logic for more unpredictability
            int direction = rand() % 4;
            switch (direction) {
                case 0: // Move up
                    if (zombie.y > 0) --zombie.y;
                    break;
                case 1: // Move down
                    if (zombie.y < HEIGHT - 1) ++zombie.y;
                    break;
                case 2: // Move left
                    if (zombie.x > 0) --zombie.x;
                    break;
                case 3: // Move right
                    if (zombie.x < WIDTH - 1) ++zombie.x;
                    break;
            }
        }
    }

    bool checkCollision() {
        for (const auto& zombie : zombies) {
            if (zombie.x == player.x && zombie.y == player.y) {
                return true;
            }
        }
        return false;
    }

    void clearScreen() {
        // Cross-platform clear screen
#ifdef _WIN32
        system("cls");
#else
        system("clear");
#endif
    }
};

int main() {
    Game game;
    game.run();
    return 0;
}

Answer

Code Review and Solution for Zombie Game in C++

The provided code implements a simple console-based game where a player (represented by 'P') moves around a 20x10 grid and tries to avoid moving zombies (represented by 'Z'). The player can move using the WASD keys, and the game ends if the player collides with a zombie.

Issues Identified

  1. Input Handling: The code allows for only lowercase input ('w', 'a', 's', 'd'). Any other input, even if it is uppercase, will not allow the player to move.

  2. Zombie Movement Logic: The zombies move randomly but have the potential to overlap with the player. The code could benefit from ensuring that zombies do not move onto the same space occupied by the player.

  3. Game State Management: Lack of a clean exit strategy or game restart prompt after a collision.

  4. Code Duplication: The srand function could be called elsewhere to avoid repetitive initialization.

Proposed Solutions

The following enhancements will be implemented:

  1. Case-Insensitive Input Handling: Convert input to lowercase to accept both upper and lower cases.
  2. Enhanced Zombie Movement: Ensure zombies do not spawn or move onto the player's location.
  3. Game Over Handling: Provide an option to restart the game without having to relaunch.
  4. Refactoring: Optimize srand to ensure it's only called once at the beginning of the game.

Updated Code

Here is the revised version with the suggested improvements:

#include 
#include 
#include 
#include 

using namespace std;

const int WIDTH = 20;
const int HEIGHT = 10;
const char PLAYER_CHAR = 'P';
const char ZOMBIE_CHAR = 'Z';
const char EMPTY_CHAR = '.';

struct Entity {
    int x, y;
};

class Game {
public:
    Game() {
        player.x = WIDTH / 2;
        player.y = HEIGHT / 2;
        srand(static_cast(time(0))); // Seed random number generator
        spawnZombies(3);
    }

    void run() {
        char input;
        while (true) {
            clearScreen();
            printBoard();
            cout << "Move (WASD): ";
            cin >> input;
            input = tolower(input); // Convert to lowercase for uniformity
            
            if (!handleInput(input)) {
                cout << "Invalid input. Please use 'W', 'A', 'S', or 'D'." << endl;
                continue;
            }

            updateZombies();
            if (checkCollision()) {
                cout << "Game Over! Do you want to restart? (Y/N): ";
                cin >> input;
                if (tolower(input) == 'y') {
                    Game newGame; // Create a new game instance
                    newGame.run(); // Restart game loop
                }
                break;
            }
        }
    }

private:
    Entity player;
    vector zombies;

    void spawnZombies(int count) {
        zombies.clear(); // Clear existing zombies
        for (int i = 0; i < count; i++) {
            Entity zombie;
            // Ensure zombies do not spawn at the player's location
            do {
                zombie.x = rand() % WIDTH;
                zombie.y = rand() % HEIGHT;
            } while ((zombie.x == player.x && zombie.y == player.y) || (isZombieAt(zombie)));
            zombies.push_back(zombie);
        }
    }

    bool isZombieAt(const Entity& position) {
        for (const auto& zombie : zombies) {
            if (zombie.x == position.x && zombie.y == position.y) {
                return true;
            }
        }
        return false;
    }

    void printBoard() {
        char board[HEIGHT][WIDTH];

        for (int i = 0; i < HEIGHT; i++)
            for (int j = 0; j < WIDTH; j++)
                board[i][j] = EMPTY_CHAR;

        board[player.y][player.x] = PLAYER_CHAR;
        for (const auto& zombie : zombies) {
            board[zombie.y][zombie.x] = ZOMBIE_CHAR;
        }

        for (int i = 0; i < HEIGHT; i++) {
            for (int j = 0; j < WIDTH; j++) {
                cout << board[i][j] << ' ';
            }
            cout << endl;
        }
    }

    bool handleInput(char input) {
        switch (input) {
            case 'w': if (player.y > 0) --player.y; break;
            case 's': if (player.y < HEIGHT - 1) ++player.y; break;
            case 'a': if (player.x > 0) --player.x; break;
            case 'd': if (player.x < WIDTH - 1) ++player.x; break;
            default: return false; // Invalid input
        }
        return true; // Valid input
    }

    void updateZombies() {
        for (auto& zombie : zombies) {
            // Random movement logic for more unpredictability, ensure no overlap with player
            int direction = rand() % 4;
            Entity newPosition = zombie;

            switch (direction) {
                case 0: if (zombie.y > 0) --newPosition.y; break; // Move up
                case 1: if (zombie.y < HEIGHT - 1) ++newPosition.y; break; // Move down
                case 2: if (zombie.x > 0) --newPosition.x; break; // Move left
                case 3: if (zombie.x < WIDTH - 1) ++newPosition.x; break; // Move right
            }
            // Only update if position is not occupied
            if (!isZombieAt(newPosition) && !(newPosition.x == player.x && newPosition.y == player.y)) {
                zombie = newPosition;
            }
        }
    }

    bool checkCollision() {
        for (const auto& zombie : zombies) {
            if (zombie.x == player.x && zombie.y == player.y) {
                return true;
            }
        }
        return false;
    }

    void clearScreen() {
#ifdef _WIN32
        system("cls");
#else
        system("clear");
#endif
    }
};

int main() {
    Game game;
    game.run();
    return 0;
}

Key Changes Explained

  1. Input Handling: The input is converted to lowercase using tolower(), allowing 'W', 'A', 'S', 'D' in addition to 'w', 'a', 's', 'd'.

  2. Zombie Movement: The updateZombies function now checks if moving a zombie would cause it to overlap with the player or existing zombies.

  3. Game Restart Feature: After a game over, the user is prompted to restart the game, creating a new instance of Game.

  4. Helper Function: isZombieAt() is created to check if a position is currently occupied by any zombie, aiding in both spawning and updating.

Conclusion

The revised code resolves the issues with the original implementation and enhances the gaming experience. The changes ensure better handling of inputs, zombie movements, and game state management. This version of the game allows for straightforward play and demonstrates improved design with potential for future expansions.

Create your Thread using our flexible tools, share it with friends and colleagues.

Your current query will become the main foundation for the thread, which you can expand with other tools presented on our platform. We will help you choose tools so that your thread is structured and logically built.

Description

This document details improvements to a console-based zombie game in C++, addressing input handling, zombie movement logic, game restart functionality, and code duplication. The revised code enhances player experience while maintaining simplicity.