Bug Detector

C++ Adventure Game Code Review

This analysis identifies bugs in a C++ text-based adventure game, detailing issues in logic, function definitions, and user interactions, along with proposed fixes to enhance the game's robustness and user experience.


Empty image or helper icon

Prompt

// Program Name: CPP-CWK2-
// Authors: Justin Powell, Rogel Campbell, Ethan Claython, Dominique Milford
// Date Created: 
// Purpose: Creating a text based adventure game

#include 
#include 
#include  
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

// Declaring the classes
class Room; 
class Door;
class Player;

// Room Class
class Room {
private: 
    string name;
    vector> doors;
    bool leads_outside = false;

public: 
    Room(string name) : name(name) {}
    vector items;

    void addDoor(shared_ptr door) {
        doors.push_back(door);
    }

    void setOutsideDoor(bool leads_outside) {
        this -> leads_outside = leads_outside;
    }

    bool hasOutsideDoor () {
        return leads_outside;
    }

    string getName () const {
        return name;
    }

    vector > getDoors () const {
        return doors;
    }

    vector getItems() const {
        return items;
    }

    Item revealSpecialItem() {
        for (auto& item : items) {
            if (items.isSpecialItem()) {
                return item;
            }
        }
        cout << "No special item was found in this room." << endl;
        return Item("No Item");
    }

    friend void interactFunction(Player& player, Room& room);
};

// Item Class
class Item {
private: 
    string name;
    bool isSpecial;

public: 
    Item(string name, bool isSpecial = false) : name(name), isSpecial(isSpecial) {}
   
    string getName() const {
        return name;
    }

    bool isSpecial() {
        return isSpecial;
    }
};

// Room Classes
class Bedroom : public Room {
public:
    Bedroom() : Room("Bedroom") {
        items.push_back(Item("Bed"));
        items.push_back(Item("Dresser"));
        items.push_back(Item("Fan"));
        items.push_back(Item("Lamp"));
        items.push_back(Item("Special Item", true));
    }
};

class Bathroom : public Room {
public:
    Bathroom() : Room("Bathroom") {
        items.push_back(Item("Sink"));
        items.push_back(Item("Tub"));
        items.push_back(Item("Mat"));
        items.push_back(Item("Towel Rack"));
        items.push_back(Item("Special Item", true));
    }
};

class LivingRoom : public Room {
public:
    LivingRoom() : Room("Living Room") {
        items.push_back(Item("Sofa"));
        items.push_back(Item("Coffee Table"));
        items.push_back(Item("Television"));
        items.push_back(Item("Remote"));
        items.push_back(Item("Special Item", true));
    }
};

class Kitchen : public Room {
public:
    Kitchen() : Room("Kitchen") {
        items.push_back(Item("Stove"));
        items.push_back(Item("Knife"));
        items.push_back(Item("Counter"));
        items.push_back(Item("Cupboard"));
        items.push_back(Item("Special Item", true));
    }
};

class Basement : public Room {
public: 
    Basement() : Room("Basement") {
        items.push_back(Item("Storage Box"));
        items.push_back(Item("Water Heater"));
        items.push_back(Item("Workbench"));
        items.push_back(Item("Washer"));
        items.push_back(Item("Special Item", true));
    }
};

// Door class
class Door {
private: 
    shared_ptr leads_to;

public:
    Door(shared_ptr leads_to) : leads_to(leads_to) {}
    shared_ptr getLeadsTo() const {
        return leads_to;
    }
};

// Player class
class Player {
private:
    string name;
    int doors_opened;
    int lives;

public: 
    Player(string name) : name(name), lives(3), doors_opened(0) {}
    void loseLife() {
        lives--;
    }

    int showLife() {
        return lives;
    }

    int showDoorsOpened() {
        return doors_opened;
    } 

    void countDoorsOpened() {
        doors_opened++;
    }

    string getName() const {
        return name;
    }
};

// Questions
unordered_map questions_and_answers = {
    {"Who is the creator of C++?", "Bjarne Stroustrup"},
    {"Is a main function optional or mandatory in a C++ program", "Mandatory"},
    {"If a function is declared as void, will it have a return value?", "Yes"},
    {"What are the two types of increments used in C++", "Prefix and Postfix"},
    {"Does C++ allow fro control of memory management?", "Yes"}
};

// Interact function 
void interactFunction(Player& player, Room& room)
{
    cout << "You are in the " << room.getName() << ". \n";
    cout << "You currently have " << player.showLife() << " lives remaining. \n";
    cout << "Doors available to open: \n";

    for (size_t i = 0; i < room.getDoors().size(); ++i) {
        cout << i + 1 << ". Door leading to " << room.getDoors()[i]->getLeadsTo()->getName() << "\n";
    }

    // Check for a special item
    cout << "Would you like to check for a special item? (yes or no)" << endl;
    string check_item;
    cin >> check_item;

    if (check_item == "yes") {
        auto it = questions_and_answers.begin();
        advance(it, rand() % questions_and_answers.size());
        string question = it->first;
        string answer = it->second;

        cout << "You've found a special item! Answer the question to collect it. n";
        cout << question << " ";
        string player_answer;
        cin >> player_answer;

        if (player_answer == answer) {
            cout << "Correct! You've collected the special item: " << room.revealSpecialItem().getName() << endl;
        } else {
            cout << "Wrong answer! You cannot collect the special item.";
        }
    }
    
    cout << "Choose a door to open (1 - " << room.getDoors().size() << "): ";
    int choice;
    cin >> choice;

    if (choice <=0  || choice > room.getDoors().size()) {
        cout << "Invalid choice. Try again";
        player.loseLife();
        return;
    }

    if (player.showDoorsOpened() >= 7) {
        cout << "You've been caught! You loose a life. \n";
        player.loseLife();

        if (player.showLife() == 0) {
            cout << "You have no lives remaining. Game over! \n";
            exit(0);
            return;
        }
    }

    player.countDoorsOpened();
    shared_ptr next_room = room.getDoors()[choice - 1]->getLeadsTo();

    if (next_room->hasOutsideDoor()) {
        cout << "\n \nCongratulations, " << player.getName() << "! You've found the exit! \n";
        cout << "You made it out successfully. Well done! \n";
        exit(0); 
    } else {
        cout << "You've entered the " << next_room->getName() << ". \n";
        interactFunction(player, *next_room);
    }
}

// Function to initialize game
vector> initializeGame(Player& player)
{
    vector> rooms;
    
    // Creating the rooms and adding them to the vector
    shared_ptr bedroom = make_shared();
    shared_ptr bathroom = make_shared();
    shared_ptr livingRoom = make_shared();
    shared_ptr kitchen = make_shared();
    shared_ptr basement = make_shared();

    rooms.push_back(bedroom);
    rooms.push_back(bathroom);
    rooms.push_back(livingRoom);
    rooms.push_back(kitchen);
    rooms.push_back(basement);

    // Randmoizing the order of the rooms
    srand(time(0)); 
    random_shuffle(rooms.begin(), rooms.end());

    // Creating the doors
    shared_ptr door_1 = make_shared(livingRoom);
    shared_ptr door_2 = make_shared(bathroom);
    shared_ptr door_3 = make_shared(kitchen);
    shared_ptr door_4 = make_shared(bedroom);
    shared_ptr door_5 = make_shared(basement);
    shared_ptr door_6 = make_shared(bedroom);
    shared_ptr door_7 = make_shared(livingRoom);
    shared_ptr door_8 = make_shared(livingRoom);

    // Adding the doors to the rooms
    bedroom->addDoor(door_1);
    bedroom->addDoor(door_2);
    bathroom->addDoor(door_3);
    bathroom->addDoor(door_4);
    basement->addDoor(door_5);
    livingRoom->addDoor(door_6);
    kitchen->addDoor(door_7);
    basement->addDoor(door_8);

    //connecting the rooms together	
    set roomIndex;
     while (roomIndex.size() < rooms.size()) {
        roomIndex.insert(rand() % rooms.size());
    }
    vector roomList(roomIndex.begin(), roomIndex.end());
    for (size_t i = 0; i < roomList.size(); ++i) {
        int currentRoomIndex = roomList[i];
        int nextRoomIndex = (i + 1) % roomList.size(); 
        shared_ptr currentRoom = rooms[currentRoomIndex];
        shared_ptr nextRoom = rooms[nextRoomIndex];

        shared_ptr door = make_shared(nextRoom);
        currentRoom->addDoor(door);

        shared_ptr returnDoor = make_shared(currentRoom);
        nextRoom->addDoor(returnDoor);
    }
    
    

    // Randomly assigning three exit points
     int exitRoomIndex;
    do {
        exitRoomIndex = rand() % rooms.size()>3;
    } while (rooms[exitRoomIndex]->getName() != "Bathroom");
    rooms[exitRoomIndex]->setOutsideDoor(true);

    return rooms; 
}

// Main function
int main()
{
     string player_name;
    cout << "Welcome adventurer! Please enter your name? ";
    cin >> player_name;

    Player player(player_name);
    cout << "Hello, " << player_name << "! Ready for an adventure? \n";

    cout << "-------------------------------------------------------------------------------- \n"
         << "                          RULES OF THE GAME \n" 
         << "-------------------------------------------------------------------------------- \n" 
         << "1. The user must find a way to exit the house \n"
         << "2. The house has 5 areas Bedroom, Bathroom, Living Room, Kitchen, Basement \n"
         << "3. Each Area Has two doors Each leading to another Area \n"
         << "4. One Room Will have a door leading outside. The door is assigned randomly at the beginning of the game \n"
         << "5. The Bathroom Cannot Lead outside \n"
         << "6. You have 7 doors to open before you get caught \n"
         << "7. Once your lives get to zero the game ends or if you escape \n" << endl;

    vector> rooms = initializeGame(player);

    // Randomly selecting a starting room
    srand(time(0)); 
    int startingRoomIndex = rand() % rooms.size(); 
    shared_ptr startingRoom = rooms[startingRoomIndex];
    interactFunction(player, *startingRoom);

    if (player.showLife() <= 0) {
        cout << "You have no lives remaining. Game over. Better luck next time! \n";
    }

    return 0;
}

Answer

Code Inspection for the C++ Text-Based Adventure Game

This analysis identifies potential bugs and issues within the provided C++ code for a text-based adventure game. Each section highlights specific problems, their descriptions, and proposed fixes.

1. Item Class Method Bug

Issue

In the Item class, the method isSpecial() is defined without the const qualifier although it does not modify any member variables.

bool isSpecial() {
    return isSpecial;
}

Fix

Add the const qualifier to the method to indicate it does not alter the object's state.

bool isSpecial() const {
    return isSpecial;
}

2. Revealing Special Item Logic

Issue

In the revealSpecialItem() function within the Room class, the method attempts to call isSpecialItem() on the items vector, which is incorrect.

if (items.isSpecialItem()) { // Incorrect

Fix

Change the condition to call isSpecial() on the individual item object inside the loop.

if (item.isSpecial()) { // Correct

3. Randomizing Exit Room Logic

Issue

The logic for selecting the exit room has a misplaced comparison operator, which may lead to incorrect room indexing.

exitRoomIndex = rand() % rooms.size()>3; // Incorrect

Fix

Properly assign a random index in the valid range. The > operator should not be used in this context.

exitRoomIndex = rand() % rooms.size(); 

4. Question Response Logic

Issue

A situation occurs where the question is never displayed due to incorrect formatting in strings. The line separating output uses n instead of \n.

cout << "You've found a special item! Answer the question to collect it. n"; // Incorrect

Fix

Correct the escape sequence to ensure the newline is interpreted properly.

cout << "You've found a special item! Answer the question to collect it.\n"; // Correct

5. Life Management Logic

Issue

When a player reaches zero lives, the program continues the flow before effectively terminating, leading to potential inconsistencies in state.

if (player.showLife() == 0) {
    cout << "You have no lives remaining. Game over! \n";
    exit(0);
    return; // This return is redundant and unreachable
}

Fix

Remove the unreachable return statement after the exit call.

if (player.showLife() == 0) {
    cout << "You have no lives remaining. Game over! \n";
    exit(0); // Remove the return statement
}

6. Room Number Validation

Issue

The validation check for door selection does not provide feedback for repeated invalid choices.

if (choice <= 0 || choice > room.getDoors().size()) {
    cout << "Invalid choice. Try again"; // No line break or delay for user feedback
}

Fix

Provide clearer information via formatting in messages and potentially loop back for repeated invalid selections.

if (choice <= 0 || choice > room.getDoors().size()) {
    cout << "Invalid choice. Please choose a number between 1 and " << room.getDoors().size() << ".\n"; 
    player.loseLife();
    return;
}

Conclusion

The identified issues revolve around function definitions, logical implementations, and user interaction. Addressing these problems will enhance the robustness of the game.

For further skill enhancement in programming practices and game development, consider exploring courses offered on the Enterprise DNA Platform.

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 analysis identifies bugs in a C++ text-based adventure game, detailing issues in logic, function definitions, and user interactions, along with proposed fixes to enhance the game's robustness and user experience.