This task has been flagged for clarification. Code on this page in its current state may be flagged incorrect once this task has been clarified. See this page's Talk page for discussion.
Create a simple implementation of the classic textual game Hunt The Wumpus.

The rules are:

The game is set in a cave that consists of a 20 room labyrinth.
Each room is connected to 3 other rooms (the cave is modeled after the
vertices of a dodecahedron).
The objective of the player is to find and kill the horrendous beast Wumpus that lurks in the cave.

The player has 5 arrows.
If they run out of arrows before killing the Wumpus, the player loses the game.

In the cave there are:

 One Wumpus
 Two giant bats
 Two bottomless pits
If the player enters a room with the Wumpus, he is eaten by it and the game is lost.

If the player enters a room with a bottomless pit, he falls into it and the game is lost.

If the player enters a room with a giant bat, the bat takes him and transports him into a random empty room.

Each turn the player can either walk into an adjacent room or shoot into an adjacent room.

Whenever the player enters a room, he "senses" what happens in adjacent rooms.
The messages are:

 Nearby Wumpus: "You smell something terrible nearby."
 Nearby bat: "You hear a rustling."
 Nearby pit: "You feel a cold wind blowing from a nearby cavern."
When the player shoots, he wins the game if he is shooting in the room with the Wumpus.
If he shoots into another room, the Wumpus has a 75% of chance of waking up and moving into an adjacent room: if this is the room with the player, he eats him up and the game is lost.

 
// 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 <iostream>
#include <stdlib.h>     /* srand, rand */
#include <time.h>       /* time */
#include <vector>
#include <cstring>
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();
}
 
 