Hunt The Wumpus/C++: Difference between revisions

From Rosetta Code
Content added Content deleted
mNo edit summary
No edit summary
 
(2 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{collection|Hunt_The_Wumpus}}
This version is based on the original game, but it diverges a little from the task!

=={{header|C++}}==
<lang cpp>
#include <time.h>
#include <iostream>
#include <iostream>
#include <random>
#include <string>
#include <sstream>
#include <sstream>


#include "wumpus.h"
typedef unsigned char byte;


namespace wumpus {
enum gameResult { FINISH, PLAY_NEW, PLAY_SAME };
enum object : byte { NOTHING, WUMPUS = 1, BAT = 2, PIT = 4, PLAYER = 8 };


Dungeon::Dungeon()
const unsigned S_PLAYER = 0, S_WUMPUS = 1, S_BAT1 = 2, S_BAT2 = 3, S_PIT1 = 4, S_PIT2 = 5,
{
MAX_ROOMS = 20, SAVED = 6, MAX_EXITS = 3, A_PATH_LEN = 5, MAX_ARROWS = 5;
// create room numbers
std::array<Room_number,20> random_room_numbers;


for (size_t i = 0; i < rooms.size(); ++i) {

random_room_numbers[i] = i + 1;
class cave
{
public:
cave()
{
int con[] = { 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 };
for( int x = 0, r = 0; x < MAX_ROOMS; x++, r = x * MAX_EXITS )
{
for( unsigned c = r, d = 0; c < r + MAX_EXITS; c++, d++ )
rooms[x].setExit( d, con[c] );
}
}
clear();
}


//generate random numbers to use to put room numbers random
void clear()
std::random_device rd;
{
std::mt19937 g(rd());
for( int x = 0; x < MAX_ROOMS; x++ )
std::shuffle(random_room_numbers.begin(), random_room_numbers.end(),g);
rooms[x].clearRoom();
}


room* getRoom( int i ) { return &rooms[i]; }
// add room numbers randomly
for (size_t i = 0; i < rooms.size(), i < random_room_numbers.size(); ++i) {
rooms[i].room_number = random_room_numbers[i];
}


std::size_t i{ 0 };
private:
room rooms[MAX_ROOMS];
rooms[i++].has_player = true;
rooms[i++].has_wumpus = true;
};


for (auto pits{ count_of_pits }; pits; --pits) {
class wumpus
rooms[i++].has_pit = true;
{
}
private:
inOut inOut;
cave theCave;
unsigned playerPos, wumpusPos, pathLen, arrowsCnt, exits[MAX_EXITS], arrowPath[A_PATH_LEN], saved[SAVED];
bool gameOver, playerWins;


for (auto bats{ count_of_bats }; bats; --bats) {
void look()
rooms[i++].has_bat = true;
{
room* r = theCave.getRoom( playerPos );
inOut.msg( "\n\n-----------------------------------\n" );
inOut.msg( "You are in room #" ); inOut.msg( playerPos + 1 );
inOut.msg( "\nTunnels lead to rooms #: " );
for( int x = 0; x < MAX_EXITS; x++ )
{
inOut.msg( ( 1 + r->getExit( x ) ) );
inOut.msg( " " );
}
}


lookAround( r );
std::shuffle(rooms.begin(), rooms.end(), g);
}
}


void shoot()
void Dungeon::indicate_hazards()
{
{
room* r = theCave.getRoom( playerPos );
bool is_first_bat = true;
unsigned e;
bool is_first_pit = true;


for( unsigned x = 0; x < pathLen; x++ )
// find the player
auto player_room{ std::find_if(rooms.begin(), rooms.end(), [](const Room &r) { return r.has_player; }) };
{
for( e = 0; e < MAX_EXITS; e++ )
if( r->getExit( e ) == arrowPath[x] ) break;
if( e < MAX_EXITS ) r = theCave.getRoom( arrowPath[x] );
else r = theCave.getRoom( r->getExit( rand() % MAX_EXITS ) );


byte o = r->contains();
for (auto& x : player_room->neighbors) {
if (x->has_wumpus) {

if( WUMPUS & o ) { gameOver = playerWins = true; return; }
std::cout << "I smell the wumpus\n";
if( PLAYER & o )
}
{
if (is_first_pit && x->has_pit) {
gameOver = true; playerWins = false;
is_first_pit = false;
inOut.msg( "\n OUCH! Arrow got you!\n" ); return;
std::cout << "I feel a breeze\n";
}
if (is_first_bat && x->has_bat) {
is_first_bat = false;
std::cout << "I hear a bat\n";
}
}
}
inOut.msg( "\n Missed!\n\n" );
if( --arrowsCnt == 0 )
{
inOut.msg( "\n You run out of arrows...\n" );
gameOver = true; playerWins = false;
return;
}
}


std::cout << "You are in room " << player_room->room_number << "\n"
wumpusMove( playerPos );
<< "You have "<<arrows<< " arrow(s) left\n"
<< "Tunnels lead to rooms "
<< player_room->neighbors[0]->room_number << ", "
<< player_room->neighbors[1]->room_number << " and "
<< player_room->neighbors[2]->room_number << "\n"
<< "what do you want to do? (M)ove or (S)hoot?\n";
}
}


bool Dungeon::shoot_arrow(std::vector<int> target_rooms)
gameResult showResult( bool pw )
//trys to shoot in the supplied tar rooms an arrow
//if the wumpus is hit returns true to indicate victory
//moves the wumpus on fail
{
{
--arrows;
if( pw ) inOut.msg( "\n AHA! You got the Wumpus!\n HEE HEE HEE - The Wumpus'll getcha next time!!\n\n" );
else inOut.msg( " HA HA HA - You lose!\n\n" );


// find the player
if( inOut.getLetter( "Play again (Y/N)? ", 'Y', 'N' ) == 'Y' )
auto player_room{ std::find_if(rooms.begin(), rooms.end(), [](const Room &r) { return r.has_player; }) };
{
if( inOut.getLetter( "Same setup (Y/N)? ", 'Y', 'N' ) == 'Y' ) return PLAY_SAME;
return PLAY_NEW;
}


for (const auto& target : target_rooms){
return FINISH;

bool room_reached = false;

for (const auto& neigbour : player_room->neighbors) {

if (neigbour->room_number == target) {
room_reached = true;

if (rooms[neigbour->room_number - 1].has_wumpus) {
std::cout << "!!!!!!YOU WON!!!!!!: You killed the Wumpus in room " << rooms[neigbour->room_number - 1].room_number << "\n";
return true;
}
break;
}
}
if (!room_reached) {
std::cout << "Room " << target << " could not be reached from arrow\n";
return false;
}
}
if (arrows == 0) {
std::cout << "You lost: You ran out of arrows";
return true;
}
return false;
}
}


bool Dungeon::move_wumpus()
void lookAround( room* r )
{
{
byte msg = 0, o;
auto direction = get_random(0, 3);
if (direction == 3) { // 25% chance that wumpus won't move
for( int x = 0; x < MAX_EXITS; x++ )
{
return false;
o = theCave.getRoom( r->getExit( x ) )->contains();
msg += ( ( WUMPUS & o ) + ( BAT & o ) + ( PIT & o ) );
}
}


// find the wumpus
if( msg & WUMPUS ) inOut.msg( "\nYou smell something terrible nearby." );
auto wumpus_room{ std::find_if(rooms.begin(), rooms.end(), [](const Room &r) { return r.has_wumpus; }) };
if( msg & PIT ) inOut.msg( "\nYou feel a cold wind blowing from a nearby cavern." );

if( msg & BAT ) inOut.msg( "\nYou hear a rustling." );
// move him
wumpus_room->has_wumpus = false;
auto new_room = wumpus_room->neighbors[direction];
new_room->has_wumpus = true;

if (new_room->has_player) {
std::cout << "You lost: Wumpus enters your room and eats you\n";
return true;
}
return false;
}
}


bool Dungeon::move_player(Room_number target_room_number)
bool checkExits( int e )
//trys to move player to the selected room
//if deadly hazard like pit or wumpus is found return game over = true;
//if bat is found choose new random room free from hazards to put the player
{
{

for( int x = 0; x < MAX_EXITS; x++ )
if( e == exits[x] ) return true;
// find the player
auto player_room{ std::find_if(rooms.begin(), rooms.end(), [](const Room &r) { return r.has_player; }) };

for (auto& x : player_room->neighbors) {

if (x->room_number == target_room_number) {
if (x->has_wumpus) {
std::cout << "You lost: You got eaten by the Wumpus\n";
return true;
}
else if (x->has_pit) {
std::cout << "You lost: You fell in a bottomless pit\n";
return true;
}
else if (x->has_bat) {
std::cout << "Gigantic bat appeared!!!\n";
std::cout << "You got dragged to a new room\n";

//Only put player in empty room
Room* bat_destionation_room = nullptr;
do{
bat_destionation_room = &rooms[get_random(0, rooms.size() - 1)];
} while (bat_destionation_room->has_wumpus || bat_destionation_room->has_pit || bat_destionation_room->has_bat || bat_destionation_room->has_player);

player_room->has_player = false;
bat_destionation_room->has_player = true;
return false;
}
else {
player_room->has_player = false;
auto target_room = &rooms[target_room_number];
target_room->has_player = true;
return false;
}
}
}
std::cerr << "Dungeon::move_player: Unknown target room entered";
return false;
return false;
}
}


Room_number Dungeon::select_room_to_move()
void getInput()
{
{
for (;;) {
if( inOut.getLetter( "\n\nShoot or Move (S/M)? ", 'S', 'M' ) == 'M' )
{
int e = inOut.getNumber( "Where to? " ) - 1;
if( checkExits( e ) ) setPlayer( e );
else inOut.msg( "\nArrggh! --- You cannot go there!\n\n" );
}
else
{
do
pathLen = inOut.getNumber( "\nNumber of rooms (1-5)? " );
while( pathLen < 1 || pathLen > A_PATH_LEN );


for( unsigned i = 0; i < pathLen; i++ )
std::cout << "To where??\n";

{
arrowPath[i] = inOut.getNumber( "Room #" ) - 1;
Room_number target = 0;
std::cin >> target;
if( i <= 1 || arrowPath[i] != arrowPath[i - 2]) continue;

inOut.msg( "\nArrows aren't that crooked! - Please, try another room.\n\n" );
i--;
if (std::cin.fail()) {
std::cin.clear();
std::cin.ignore(999, '\n');
continue;
}
}

shoot();
auto neighbor = get_neighbour_rooms();

if (target == neighbor[0] || target == neighbor[1] || target == neighbor[2])
return target;
}
}
}
}


std::array<Room_number, 3> Dungeon::get_neighbour_rooms() const
void setPlayer( int pos )
{
{
if( playerPos < MAX_ROOMS )
// find the player
auto player_room{ std::find_if(rooms.begin(), rooms.end(), [](const Room &r) { return r.has_player; }) };
theCave.getRoom( playerPos )->clear( PLAYER );

if( hazards( pos ) ) return;


playerPos = pos;
return std::array<Room_number, 3>{
player_room->neighbors[0]->room_number,
room* r = theCave.getRoom( playerPos );
player_room->neighbors[1]->room_number,
r->populate( PLAYER );
player_room->neighbors[2]->room_number
for( int x = 0; x < MAX_EXITS; x++ )
};
exits[x] = r->getExit( x );
}
}


void Dungeon::show_state_of_dungeon()
bool hazards( int pos )
{
{
room* r = theCave.getRoom( pos );
auto print_rooms = rooms;
byte o = r->contains();


std::sort(print_rooms.begin(), print_rooms.end(), [](const Room &a, const Room &b) { return b.room_number > a.room_number; });
if( WUMPUS & o )

{
for (const auto&room : print_rooms) {
inOut.msg( "\n ...OOPS! Bumped a Wumpus!\n\n" );
std::cout << "Room " << room.room_number << " connects to: ";
if( wumpusMove( pos ) )

{
for (const auto&neighbor : room.neighbors) {
inOut.msg( "\n TSK TSK TSK - Wumpus got you!\n" );
gameOver = true; playerWins = false;
if (neighbor != nullptr) {
return true;
std::cout << neighbor->room_number << " ";
}
else {
std::cout << "np" << " ";
}
}
}
}


if( PIT & o )
std::cout << " ";
{
if (room.has_wumpus) {
inOut.msg( "\n YYYYIIIIEEEE!!!! Fell in pit!\n");
std::cout << "wumpus:" << room.has_wumpus << " ";
gameOver = true; playerWins = false;
}
return true;
if (room.has_pit) {
std::cout << "pit:" << room.has_pit << " ";
}
if (room.has_bat) {
std::cout << "bat:" << room.has_bat << " ";
}
if (room.has_player) {
std::cout << "player:" << room.has_player << " ";
}
std::cout << "\n";
}
}

if( BAT & o )
{
inOut.msg( "\n ZAP -- Super bat snatch! Elsewhereville for you!\n\n" );
setPlayer( rand() % MAX_ROOMS );
return true;
}
return false;
}
}


//-------------------------------------------------------------
bool wumpusMove( int pos )
//Helper functions
//-------------------------------------------------------------

int get_random(int min, int max)
{
{
if( rand() % 100 < 75 )
static std::random_device rd;
{
static std::mt19937 mt(rd());
std::uniform_int_distribution<int> distribution(min, max);
room* r = theCave.getRoom( wumpusPos );
r->clear( WUMPUS );
return distribution(mt);
wumpusPos = r->getExit( rand() % MAX_EXITS );
theCave.getRoom( wumpusPos )->populate( WUMPUS );
}
return ( pos == wumpusPos );
}
}


void initGame( gameResult gr )
void hunt_the_wumpus()
{
{
instructions();
inOut.msg( "\n\n\n\nHUNT THE WUMPUS\n---------------\n" );
theCave.clear(); gameOver = false; arrowsCnt = MAX_ARROWS;


if( gr == PLAY_NEW )
for (;;) // restart game
{
{
saved[S_PLAYER] = rand() % MAX_ROOMS;
Dungeon dungeon;

setPlayer( saved[S_PLAYER] );
dungeon.show_state_of_dungeon();
saved[S_BAT1] = fillRoom( BAT ); saved[S_BAT2] = fillRoom( BAT );

saved[S_PIT1] = fillRoom( PIT ); saved[S_PIT2] = fillRoom( PIT );
wumpusPos = saved[S_WUMPUS] = fillRoom( WUMPUS );
for (;;) { // current room handle

}
dungeon.indicate_hazards();
else

{
setPlayer( saved[S_PLAYER] ); wumpusPos = saved[S_WUMPUS];
std::string in;
theCave.getRoom( wumpusPos )->populate( WUMPUS );
std::cin >> in;
theCave.getRoom( saved[S_BAT1] )->populate( BAT );
if (std::cin.fail()) {
theCave.getRoom( saved[S_BAT2] )->populate( BAT );
std::cin.clear();
std::cin.ignore(999, '\n');
theCave.getRoom( saved[S_PIT1] )->populate( PIT );
theCave.getRoom( saved[S_PIT2] )->populate( PIT );
continue;
}

bool game_over = false;

if (in == "m" || in == "M" || in == "Move" || in == "move") {
game_over = dungeon.move_player(dungeon.select_room_to_move());
}
else if (in == "s" || in == "S" || in == "Shoot" || in == "shoot") {

game_over = dungeon.shoot_arrow(select_rooms_to_shoot());

if (game_over == true) {
break;
}
game_over = dungeon.move_wumpus();
}
else if (in == "cheat") { // secret menue to show dungeon state
dungeon.show_state_of_dungeon();
}
if (game_over == true) {
break;
}
}

std::cout << "Press any key to start a new game or (q)uit to end game\n";
std::string in;
std::cin >> in;

if (in == "q" || in == "Q" || in == "Quit" || in == "quit")
break;
}
}
}
}


int fillRoom( object c )
void instructions()
{
{
std::cout <<R"(Welcome to "Hunt the Wumpus"!
int i; room* r;
The wumpus lives in a cave of rooms.Each room has 3 tunnels leading to
do
other rooms. (Look at a dodecahedron to see how this works - if you don't know
{
what a dodecahedron is, ask someone).
i = rand() % MAX_ROOMS;

r = theCave.getRoom( i );
Hazards
}
Bottomless pits - two rooms have bottomless pits in them. If you go there, you
while( r->contains() );
fall into the pit(and lose!)
Super bats - two other rooms have super bats.If you go there, a bat grabs you
r->populate( c );
and takes you to some other room at random. (Which may be troublesome).
return i;

Wumpus
The wumpus is not bothered by hazards(he has sucker feet and is too big for a
bat to lift).Usually he is asleep.Two things wake him up : you shooting an
arrow or you entering his room."

If the wumpus wakes he moves(p = .75) one room or stays still(p = .25).After
that, if he is where you are, he eats you up and you lose!"

Each turn you may move or shoot a crooked arrow.
Moving: you can move one room(thru one tunnel).
Arrows : you have 5 arrows.You lose when you run out.Each arrow can go from 1
to 3 rooms.You aim by telling the computer the rooms you want the arrow to go
to.If the arrow can't go that way (if no tunnel) it moves at random to the
next room.If the arrow hits the wumpus, you win.If the arrow hits you, you lose.

Warnings
When you are one room away from a wumpus or hazard, the computer says :

Wumpus: "I smell the wumpus"
Bat : "I hear a bat"
Pit : "I feel a breeze"


"Press any key to start")";

char c;
std::cin.get(c);
}
}


std::vector<Room_number> select_rooms_to_shoot()
void printInstructions()
{
{
for(;;){
if( inOut.getLetter( "Instructions (Y/N)? ", 'Y', 'N' ) == 'N' ) return;
std::cout << "Enter the rooms you want to shoot the arrow (e.g. 2-3-12, e.g. 4-5, e.g. 2)\n";
inOut.msg( "\n\nWelcome to 'HUNT THE WUMPUS'\n\nThe Wumpus lives in a cave of 20 rooms: each room has 3 tunnels leading to\n"
"other rooms. (Look at a Dodecahedron to see how this works, if you don't\nknow what a dodecahedron is, ask someone)\n"
"\n\n HAZARDS:\n --------\n\nBottomless pits:\n----------------\nTwo rooms have bottomless pits in them.\n"
"If you go there, you fall into the pit and lose!\n\nSuper bats:\n-----------\nTwo other rooms have super bats.\nIf you go there, "
"a bat grabs you and takes you to some other room at random,\nwhich might be troublesome.\n\nWumpus:\n-------\nThe Wumpus is not "
"bothered by the hazards, he has sucker feet and is too big\nfor a bat to lift.\nUsually he is asleep.\nTwo things wake him up: "
"your entering his room or your shooting an arrow.\nIf the Wumpus wakes, he has 75% chance to move one room or 25% chance to stay\n"
"still.\nAfter that, if he is where you are, he eats you up and you lose!\n\nYou:\n----\nEach turn you may move or shoot a crooked arrow.\n"
"- Moving: you can move one room (thru one tunnel)\n- Arrows: you have 5 arrows. You lose when you run out.\n Each arrow can go from "
"1 to 5 rooms, you aim by telling the computer the\n rooms #s you want the arrow to go to.\n If the arrow can't go that way (if no tunnel) "
"it moves at random to the \n next room.\n If the arrow hits the Wumpus: you win, if the arrow hits you: you lose.\n"
"\n\n WARNINGS:\n --------\nWhen you are one room away from Wumpus or any other hazard, the computer says:\nWumpus: 'You smell something "
"terrible nearby.'\nBat: 'You hear a rustling.'\nPit: 'You feel a cold wind blowing from a nearby cavern.'\n\n\n\nPress return to play..." );
inOut.wait();
}
public:
void play()
{
playerPos = MAX_ROOMS;
gameResult gr = PLAY_NEW;
printInstructions();


while( gr != FINISH )
std::string input;
{
std::cin >> input;

initGame( gr );
while( !gameOver ) { look(); getInput(); }
std::istringstream ist{ input };

gr = showResult( playerWins );
std::vector<int> target_rooms;

bool bad_input = false;

while (!ist.eof()) {

int room_number;
ist >> room_number;

if (ist.fail()) {
bad_input = true;
break;
}

target_rooms.push_back(room_number);

if (target_rooms.size() == 3 || ist.eof())
break;

char seperator;
ist >> seperator;

if (ist.fail()) {
bad_input = true;
break;
}
if ((seperator != '-') || (target_rooms.size() > 3)) {
bad_input = true;
break;
}
}

if (bad_input) {
continue;
}
else {
return target_rooms;
}
}
}
}
}
};

int main( int argc, char* argv[] )
{
srand( static_cast<unsigned>( time( NULL ) ) );
wumpus hw; hw.play();
return 0;
}
}
</lang>

Latest revision as of 15:15, 22 October 2018

  1. include <iostream>
  2. include <random>
  3. include <string>
  4. include <sstream>
  1. include "wumpus.h"

namespace wumpus {

   Dungeon::Dungeon()
   {
       // create room numbers
       std::array<Room_number,20> random_room_numbers;
       for (size_t i = 0; i < rooms.size(); ++i) {
           random_room_numbers[i] = i + 1;
       }
       //generate random numbers to use to put room numbers random
       std::random_device rd;
       std::mt19937 g(rd());
       std::shuffle(random_room_numbers.begin(), random_room_numbers.end(),g);
       // add room numbers randomly
       for (size_t i = 0; i < rooms.size(), i < random_room_numbers.size(); ++i) {
           rooms[i].room_number = random_room_numbers[i];
       }
       std::size_t i{ 0 };
       rooms[i++].has_player = true;
       rooms[i++].has_wumpus = true;
       for (auto pits{ count_of_pits }; pits; --pits) {
           rooms[i++].has_pit = true;
       }
       for (auto bats{ count_of_bats }; bats; --bats) {
           rooms[i++].has_bat = true;
       }
       std::shuffle(rooms.begin(), rooms.end(), g);
   }
   void Dungeon::indicate_hazards()
   {
       bool is_first_bat = true;
       bool is_first_pit = true;
       // find the player
       auto player_room{ std::find_if(rooms.begin(), rooms.end(), [](const Room &r) { return r.has_player; }) };
       for (auto& x : player_room->neighbors) {
           if (x->has_wumpus) { 
               std::cout << "I smell the wumpus\n";
           }
           if (is_first_pit && x->has_pit) {
               is_first_pit = false;
               std::cout << "I feel a breeze\n";
           }
           if (is_first_bat && x->has_bat) {
               is_first_bat = false;
               std::cout << "I hear a bat\n";
           }
       }
       std::cout   << "You are in room " << player_room->room_number << "\n"
                   << "You have "<<arrows<< " arrow(s) left\n"
                   << "Tunnels lead to rooms " 
                   << player_room->neighbors[0]->room_number << ", "
                   << player_room->neighbors[1]->room_number << " and "
                   << player_room->neighbors[2]->room_number << "\n"
                   << "what do you want to do? (M)ove or (S)hoot?\n";
   }
   bool Dungeon::shoot_arrow(std::vector<int> target_rooms)
       //trys to shoot in the supplied tar rooms an arrow
       //if the wumpus is hit returns true to indicate victory
       //moves the wumpus on fail
   {
       --arrows;
       // find the player
       auto player_room{ std::find_if(rooms.begin(), rooms.end(), [](const Room &r) { return r.has_player; }) };
       for (const auto& target : target_rooms){
           bool room_reached = false;
           for (const auto& neigbour : player_room->neighbors) {
               if (neigbour->room_number == target) {
                   room_reached = true;
                   if (rooms[neigbour->room_number - 1].has_wumpus) {
                       std::cout << "!!!!!!YOU WON!!!!!!: You killed the Wumpus in room " << rooms[neigbour->room_number - 1].room_number << "\n";
                       return true;
                   }
                   break;
               }
           }
           if (!room_reached) {    
               std::cout << "Room " << target << " could not be reached from arrow\n";
               return false;
           }           
       }
       if (arrows == 0) {
           std::cout << "You lost: You ran out of arrows";
           return true;
       }
       return false;
   }
   bool Dungeon::move_wumpus() 
   {
       auto direction = get_random(0, 3);
       if (direction == 3) {               // 25% chance that wumpus won't move
           return false;
       }
       // find the wumpus
       auto wumpus_room{ std::find_if(rooms.begin(), rooms.end(), [](const Room &r) { return r.has_wumpus; }) };
       // move him
       wumpus_room->has_wumpus = false;
       auto new_room = wumpus_room->neighbors[direction];
       new_room->has_wumpus = true;
       if (new_room->has_player) {
           std::cout << "You lost: Wumpus enters your room and eats you\n";
           return true;
       }
       return false;
   }
   bool Dungeon::move_player(Room_number target_room_number)
       //trys to move player to the selected room
       //if deadly hazard like pit or wumpus is found return game over = true;
       //if bat is found choose new random room free from hazards to put the player
   {
       // find the player
       auto player_room{ std::find_if(rooms.begin(), rooms.end(), [](const Room &r) { return r.has_player; }) };
       for (auto& x : player_room->neighbors) {
           if (x->room_number == target_room_number) {
               if (x->has_wumpus) {
                   std::cout << "You lost: You got eaten by the Wumpus\n";
                   return true;
               }
               else if (x->has_pit) {
                   std::cout << "You lost: You fell in a bottomless pit\n";
                   return true;
               }
               else if (x->has_bat) {
                   std::cout << "Gigantic bat appeared!!!\n";
                   std::cout << "You got dragged to a new room\n";
                   //Only put player in empty room
                   Room* bat_destionation_room = nullptr;
                   do{
                       bat_destionation_room = &rooms[get_random(0, rooms.size() - 1)];
                   } while (bat_destionation_room->has_wumpus || bat_destionation_room->has_pit || bat_destionation_room->has_bat || bat_destionation_room->has_player);
                   player_room->has_player = false;
                   bat_destionation_room->has_player = true;
                   return false;
               }
               else {
                   player_room->has_player = false;
                   auto target_room = &rooms[target_room_number];
                   target_room->has_player = true;
                   return false;
               }
           }
       }
       std::cerr << "Dungeon::move_player: Unknown target room entered";
       return false;
   }
   Room_number Dungeon::select_room_to_move()
   {
       for (;;) {
           std::cout << "To where??\n";
           Room_number target = 0;
           std::cin >> target;
           if (std::cin.fail()) {
               std::cin.clear();
               std::cin.ignore(999, '\n');
               continue;
           }
           auto neighbor = get_neighbour_rooms();
           if (target == neighbor[0] || target == neighbor[1] || target == neighbor[2])
               return target;
       }
   }
   std::array<Room_number, 3> Dungeon::get_neighbour_rooms() const
   {
       // find the player
       auto player_room{ std::find_if(rooms.begin(), rooms.end(), [](const Room &r) { return r.has_player; }) };
       return std::array<Room_number, 3>{
           player_room->neighbors[0]->room_number,
           player_room->neighbors[1]->room_number,
           player_room->neighbors[2]->room_number
       };
   }
   void Dungeon::show_state_of_dungeon()
   {
       auto print_rooms = rooms;
       std::sort(print_rooms.begin(), print_rooms.end(), [](const Room &a, const Room &b) { return b.room_number > a.room_number; });
       for (const auto&room : print_rooms) {
           std::cout << "Room " << room.room_number << " connects to: ";
           for (const auto&neighbor : room.neighbors) {
               if (neighbor != nullptr) {
                   std::cout << neighbor->room_number << " ";
               }
               else {
                   std::cout << "np" << " ";
               }
           }
           std::cout << " ";
           if (room.has_wumpus) {
               std::cout << "wumpus:" << room.has_wumpus << " ";
           }
           if (room.has_pit) {
               std::cout << "pit:" << room.has_pit << " ";
           }
           if (room.has_bat) {
               std::cout << "bat:" << room.has_bat << " ";
           }
           if (room.has_player) {
               std::cout << "player:" << room.has_player << " ";
           }
           std::cout << "\n";
       }
   }
   //-------------------------------------------------------------
   //Helper functions
   //-------------------------------------------------------------
   int get_random(int min, int max)
   {
       static std::random_device rd;
       static std::mt19937 mt(rd());
       std::uniform_int_distribution<int> distribution(min, max);
       return distribution(mt);
   }
   void hunt_the_wumpus()
   {
       instructions();
       for (;;)        // restart game
       {
           Dungeon dungeon;
           dungeon.show_state_of_dungeon();
           for (;;) {      // current room handle
               dungeon.indicate_hazards();
               std::string in;
               std::cin >> in;
               if (std::cin.fail()) {
                   std::cin.clear();
                   std::cin.ignore(999, '\n');
                   continue;
               }
               bool game_over = false;
               if (in == "m" || in == "M" || in == "Move" || in == "move") {
                   game_over = dungeon.move_player(dungeon.select_room_to_move());
               }
               else if (in == "s" || in == "S" || in == "Shoot" || in == "shoot") {
                   game_over = dungeon.shoot_arrow(select_rooms_to_shoot());
                   if (game_over == true) { 
                       break; 
                   }
                   game_over = dungeon.move_wumpus();
               }
               else if (in == "cheat") {       // secret menue to show dungeon state 
                   dungeon.show_state_of_dungeon();
               }
               if (game_over == true) {
                   break;
               }
           }
           std::cout << "Press any key to start a new game or (q)uit to end game\n";
           std::string in;
           std::cin >> in;
           if (in == "q" || in == "Q" || in == "Quit" || in == "quit")
               break;
       }
   }
   void instructions()
   {
       std::cout <<R"(Welcome to "Hunt the Wumpus"!

The wumpus lives in a cave of rooms.Each room has 3 tunnels leading to other rooms. (Look at a dodecahedron to see how this works - if you don't know what a dodecahedron is, ask someone).

Hazards Bottomless pits - two rooms have bottomless pits in them. If you go there, you fall into the pit(and lose!) Super bats - two other rooms have super bats.If you go there, a bat grabs you and takes you to some other room at random. (Which may be troublesome).

Wumpus The wumpus is not bothered by hazards(he has sucker feet and is too big for a bat to lift).Usually he is asleep.Two things wake him up : you shooting an arrow or you entering his room."

If the wumpus wakes he moves(p = .75) one room or stays still(p = .25).After that, if he is where you are, he eats you up and you lose!"

Each turn you may move or shoot a crooked arrow. Moving: you can move one room(thru one tunnel). Arrows : you have 5 arrows.You lose when you run out.Each arrow can go from 1 to 3 rooms.You aim by telling the computer the rooms you want the arrow to go to.If the arrow can't go that way (if no tunnel) it moves at random to the next room.If the arrow hits the wumpus, you win.If the arrow hits you, you lose.

Warnings When you are one room away from a wumpus or hazard, the computer says :

Wumpus: "I smell the wumpus" Bat : "I hear a bat" Pit : "I feel a breeze"


"Press any key to start")";

       char c;
       std::cin.get(c);
   }
   std::vector<Room_number> select_rooms_to_shoot()
   {
       for(;;){
           std::cout << "Enter the rooms you want to shoot the arrow (e.g. 2-3-12, e.g. 4-5, e.g. 2)\n";
           std::string input;
           std::cin >> input;
           std::istringstream ist{ input };
           std::vector<int> target_rooms;
           bool bad_input = false;
           while (!ist.eof()) {
               int room_number;
               ist >> room_number;
               if (ist.fail()) {
                   bad_input = true;
                   break;
               }
               target_rooms.push_back(room_number);
               if (target_rooms.size() == 3 || ist.eof())
                   break;
               char seperator;
               ist >> seperator;
               if (ist.fail()) {
                   bad_input = true;
                   break;
               }
               if ((seperator != '-')   || (target_rooms.size() > 3)) {
                   bad_input = true; 
                   break; 
               }
           }
           if (bad_input) {
               continue;
           }
           else {
               return target_rooms;
           }
       }
   }

}