// constant value 2d array to represent the dodecahedron // room structure const static int adjacentRooms[20][3] = { {1, 4, 7}, {0, 2, 9}, {1, 3, 11}, {2, 4, 13}, {0, 3, 5}, {4, 6, 14}, {5, 7, 16}, {0, 6, 8}, {7, 9, 17}, {1, 8, 10}, {9, 11, 18}, {2, 10, 12}, {11, 13, 19}, {3, 12, 14}, {5, 13, 15}, {14, 16, 19}, {6, 15, 17}, {8, 16, 18}, {10, 17, 19}, {12, 15, 18} }; class WumpusGame { private: // Data Members int numRooms; int currentRoom, startingPosition; // currentRoom is an integer variable that stores the room the player is currently in (between 0-20) int wumpusRoom, batRoom1, batRoom2, pitRoom1, pitRoom2; // Stores the room numbers of the respective int wumpusStart, bat1Start, bat2Start; bool playerAlive, wumpusAlive; // Are the player and wumpus still alive? True or false. int numArrows; //store arrow count // private functions void PlacePits(); void PlaceBats(); void PlaceWumpus(); void PlacePlayer(); bool IsValidMove(int); bool IsRoomAdjacent(int, int); int Move(int); void InspectCurrentRoom(); void PerformAction(int); void MoveStartledWumpus(int); void PlayGame(); void PlayAgain(); void PrintInstructions(); // Access specifier public: //public functions void StartGame(); WumpusGame(); }; #include #include /* srand, rand */ #include /* time */ #include #include using namespace std; // default constructor WumpusGame::WumpusGame() { numRooms = 20; } // This function prints the instructions for the game // to the console void WumpusGame::PrintInstructions() { char wait; cout << " Welcome to 'Hunt the Wumpus'! " << endl; cout << " The wumpus lives in a cave of 20 rooms. Each room has 3 tunnels leading to" << endl; cout << " other rooms. (Look at a dodecahedron to see how this works - if you don't know" << endl; cout << " what a dodecahedron is, ask someone). \n" << endl; cout << " Hazards: \n" << endl; cout << " Bottomless pits - two rooms have bottomless pits in them. If you go there, you " << endl; cout << " fall into the pit (& lose!) \n" << endl; cout << " Super bats - two other rooms have super bats. If you go there, a bat grabs you" << endl; cout << " and takes you to some other room at random. (Which may be troublesome). Once the" << endl; cout << " bat has moved you, that bat moves to another random location on the map.\n\n" << endl; cout << " Wumpus" << endl; cout << " The wumpus is not bothered by hazards (he has sucker feet and is too big for a" << endl; cout << " bat to lift). Usually he is asleep. Two things wake him up: you shooting an" << endl; cout << " arrow or you entering his room. If the wumpus wakes he moves (p=.75) one room or " << endl; cout << " stays still (p=.25). After that, if he is where you are, he eats you up and you lose!\n" << endl; cout << " You \n" << endl; cout << " Each turn you may move, save or shoot an arrow using the commands move, save, & shoot." << endl; cout << " Moving: you can move one room (thru one tunnel)." << endl; cout << " Arrows: you have 3 arrows. You lose when you run out. You aim by telling the" << endl; cout << " computer the rooms you want the arrow to go to. If the arrow can't go that way" << endl; cout << " (if no tunnel), the arrow will not fire." << endl; cout << " Warnings" << endl; cout << " When you are one room away from a wumpus or hazard, the computer says:" << endl; cout << " Wumpus: 'I smell a wumpus'" << endl; cout << " Bat: 'Bats nearby'" << endl; cout << " Pit: 'I feel a draft'" << endl; cout << endl; cout << "Press Y to return to the main menu." << endl; cin >> wait; } // This function will place two bats throughout the map // this ensures that the bats will not be place in the same // room as another bat or the wumpus void WumpusGame::PlaceBats() { srand (time(NULL)); bool validRoom = false; while(!validRoom){ batRoom1 = rand() % 20 + 1; if(batRoom1 != wumpusRoom) validRoom = true; } validRoom = false; while(!validRoom){ batRoom2 = rand() % 20 + 1; if(batRoom2 != wumpusRoom && batRoom2 != batRoom1) validRoom = true; } bat1Start = batRoom1; bat2Start = batRoom2; } // this function randomly places the pits // throughout the map excluding room 0 void WumpusGame::PlacePits() { srand (time(NULL)); pitRoom1 = rand() % 20 + 1; pitRoom2 = rand() % 20 + 1; } // this function randomly places the wumpus in a room // without being in room number 0 void WumpusGame::PlaceWumpus() { srand (time(NULL)); int randomRoom = rand() % 20 + 1; wumpusRoom = randomRoom; wumpusStart = wumpusRoom; } // place the player in room 0 void WumpusGame::PlacePlayer() { startingPosition = 0; currentRoom = Move(0); } // This is a method that checks if the user inputted a valid room to move to or not. // The room number has to be between 0 and 20, but also must be adjacent to the current room. bool WumpusGame::IsValidMove(int roomID) { if (roomID < 0) return false; if (roomID > numRooms) return false; if (!IsRoomAdjacent(currentRoom, roomID)) return false; return true; } // This method returns true if roomB is adjacent to roomA, otherwise returns false. // It is a helper method that loops through the adjacentRooms array to check. // It will be used throughout the app to check if we are next to the wumpus, bats, or pits // as well as check if we can make a valid move. bool WumpusGame::IsRoomAdjacent(int roomA, int roomB) { for (int j = 0; j < 3; j++) { if (adjacentRooms[roomA][j] == roomB){ return true; } } return false; } // This method moves the player to a new room and returns the new room. It performs no checks on its own. int WumpusGame::Move(int newRoom) { return newRoom; } // Inspects the current room. // This method check for Hazards such as being in the same room as the wumpus, bats, or pits // It also checks if you are adjacent to a hazard and handle those cases // Finally it will just print out the room description void WumpusGame::InspectCurrentRoom() { srand (time(NULL)); if (currentRoom == wumpusRoom) { cout << "The Wumpus ate you!!!" << endl; cout << "LOSER!!!" << endl; PlayAgain(); } else if (currentRoom == batRoom1 || currentRoom == batRoom2) { int roomBatsLeft = currentRoom; bool validNewBatRoom = false; bool isBatRoom = false; cout << "Snatched by superbats!!" << endl; if(currentRoom == pitRoom1 || currentRoom == pitRoom2) cout << "Luckily, the bats saved you from the bottomless pit!!" << endl; while(!isBatRoom){ currentRoom = Move(rand() % 20 + 1); if(currentRoom != batRoom1 && currentRoom != batRoom2) isBatRoom = true; } cout << "The bats moved you to room "; cout << currentRoom << endl; InspectCurrentRoom(); if(roomBatsLeft == batRoom1){ while(!validNewBatRoom){ batRoom1 = rand() % 19 + 1; if(batRoom1 != wumpusRoom && batRoom1 != currentRoom) validNewBatRoom = true; } } else { while(!validNewBatRoom){ batRoom2 = rand() % 19 + 1; if(batRoom2 != wumpusRoom && batRoom2 != currentRoom) validNewBatRoom = true; } } } else if(currentRoom == pitRoom1 || currentRoom == pitRoom2) { cout << "YYYIIIIIEEEEE.... fell in a pit!!!" << endl; cout << "GAME OVER LOSER!!!" << endl; PlayAgain(); } else { cout << "You are in room "; cout << currentRoom << endl; if (IsRoomAdjacent(currentRoom, wumpusRoom)){ cout << "You smell a horrid stench..." << endl; } if (IsRoomAdjacent(currentRoom, batRoom1) || IsRoomAdjacent(currentRoom, batRoom2)){ cout << "Bats nearby..." << endl; } if (IsRoomAdjacent(currentRoom, pitRoom1) || IsRoomAdjacent(currentRoom, pitRoom2)){ cout << "You feel a draft..." << endl; } cout << "Tunnels lead to rooms " << endl; for (int j = 0; j < 3; j++) { cout << adjacentRooms[currentRoom][j]; cout << " "; } cout << endl; } } // Method accepts an int which is the command the user inputted. // This method performs the action of the command or prints out an error. void WumpusGame::PerformAction(int cmd) { int newRoom; switch (cmd) { case 1: cout << "Which room? " << endl; try { cin >> newRoom; // Check if the user inputted a valid room id, then simply tell the player to move there. if (IsValidMove(newRoom)) { currentRoom = Move(newRoom); InspectCurrentRoom(); } else { cout << "You cannot move there." << endl; } } catch (...) // Try...Catch block will catch if the user inputs text instead of a number. { cout << "You cannot move there." << endl; } break; case 2: if(numArrows > 0){ cout << "Which room? " << endl; try { cin >> newRoom; // Check if the user inputted a valid room id, then simply tell the player to move there. if (IsValidMove(newRoom)) { numArrows--; if(newRoom == wumpusRoom){ cout << "ARGH.. Splat!" << endl; cout << "Congratulations! You killed the Wumpus! You Win." << endl; cout << "Press 'Y' to return to the main menu." << endl; wumpusAlive = false; cin >> newRoom; cin.clear(); cin.ignore(10000, '\n'); } else { cout << "Miss! But you startled the Wumpus" << endl; MoveStartledWumpus(wumpusRoom); cout << "Arrows Left: "; cout << numArrows << endl; if(wumpusRoom == currentRoom){ cout << "The wumpus attacked you! You've been killed." << endl; cout << "Game Over!" << endl; PlayAgain(); } } } else { cout << "You cannot shoot there." << endl; } } catch (...) // Try...Catch block will catch if the user inputs text instead of a number. { cout << "You cannot shoot there." << endl; } } else { cout << "You do not have any arrows!" << endl; } break; case 3: cout << "Quitting the current game." << endl; playerAlive = false; break; default: cout << "You cannot do that. You can move, shoot, save or quit." << endl; break; } } // this function moves the wumpus randomly to a room that is adjacent to // the wumpus's current position void WumpusGame::MoveStartledWumpus(int roomNum){ srand (time(NULL)); int rando = rand() % 3; if(rando != 3) wumpusRoom = adjacentRooms[roomNum][rando]; } // This restarts the map from the begiinning void WumpusGame::PlayAgain(){ char reply; cout << "Would you like to replay the same map? Enter Y to play again." << endl; cin >> reply; if(reply == 'y' || reply == 'Y'){ currentRoom = startingPosition; wumpusRoom = wumpusStart; batRoom1 = bat1Start; batRoom2 = bat2Start; cout << "Try not to die this time. \n" << endl; InspectCurrentRoom(); } else { playerAlive = false; } } // PlayGame() method starts up the game. // It houses the main game loop and when PlayGame() quits the game has ended. void WumpusGame::PlayGame() { int choice; bool validChoice = false; cout << "Running the game..." << endl; // Initialize the game PlaceWumpus(); PlaceBats(); PlacePits(); PlacePlayer(); // game set up playerAlive = true; wumpusAlive = true; numArrows = 3; //Inspects the initial room InspectCurrentRoom(); // Main game loop. while (playerAlive && wumpusAlive) { cout << "Enter an action choice." << endl; cout << "1) Move" << endl; cout << "2) Shoot" << endl; cout << "3) Quit" << endl; cout << ">>> "; do { validChoice = true; cout << "Please make a selection: "; try { cin >> choice; switch (choice) { case 1: PerformAction(choice); break; case 2: PerformAction(choice); break; case 3: PerformAction(choice); break; default: validChoice = false; cout << "Invalid choice. Please try again." << endl; cin.clear(); cin.ignore(10000, '\n'); break; } } catch (...) { validChoice = false; cout << "Invalid choice. Please try again." << endl; cin.clear(); cin.ignore(10000, '\n'); } } while (validChoice == false); } } // this function begins the game loop void WumpusGame::StartGame() { srand (time(NULL)); int choice; bool validChoice; bool keepPlaying; wumpusStart = bat1Start = bat2Start = -1; do { keepPlaying = true; cout << "Welcome to Hunt The Wumpus." << endl; cout << "1) Play Game" << endl; cout << "2) Print Instructions" << endl; cout << "3) Quit" << endl; do { validChoice = true; cout << "Please make a selection: "; try { cin >> choice; switch (choice) { case 1: PlayGame(); break; case 2: PrintInstructions(); break; case 3: cout << "Quitting." << endl; keepPlaying = false; break; default: validChoice = false; cout << "Invalid choice. Please try again." << endl; cin.clear(); cin.ignore(10000, '\n'); break; } } catch (...) { validChoice = false; cout << "Invalid choice. Please try again." << endl; cin.clear(); cin.ignore(10000, '\n'); } } while (validChoice == false); } while (keepPlaying); } int main() { // create wumpus game object WumpusGame game; // start the game game.StartGame(); }