Hunt The Wumpus/C++: Difference between revisions
Content added Content deleted
mNo edit summary |
mNo edit summary |
||
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(); |
|||
} |
|||
// 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: |
|||
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( " " ); |
|||
} |
} |
||
std::shuffle(rooms.begin(), rooms.end(), g); |
|||
} |
} |
||
void |
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( 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 ) ); |
|||
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"; |
|||
} |
} |
||
} |
|||
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 ) |
|||
{ |
{ |
||
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++ ) |
|||
// 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 ); |
|||
std::cout << "To where??\n"; |
|||
{ |
|||
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" ); |
|||
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 ) |
|||
{ |
{ |
||
// 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; |
|||
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 |
|||
}; |
|||
exits[x] = r->getExit( x ); |
|||
} |
} |
||
void Dungeon::show_state_of_dungeon() |
|||
bool hazards( int 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" ); |
|||
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"; |
|||
} |
} |
||
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) |
|||
{ |
{ |
||
static std::random_device rd; |
|||
static std::mt19937 mt(rd()); |
|||
std::uniform_int_distribution<int> distribution(min, max); |
|||
room* r = theCave.getRoom( wumpusPos ); |
|||
return distribution(mt); |
|||
wumpusPos = r->getExit( rand() % MAX_EXITS ); |
|||
theCave.getRoom( wumpusPos )->populate( WUMPUS ); |
|||
} |
|||
return ( pos == wumpusPos ); |
|||
} |
} |
||
void |
void hunt_the_wumpus() |
||
{ |
{ |
||
instructions(); |
|||
inOut.msg( "\n\n\n\nHUNT THE WUMPUS\n---------------\n" ); |
|||
theCave.clear(); gameOver = false; arrowsCnt = MAX_ARROWS; |
|||
for (;;) // restart game |
|||
{ |
{ |
||
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 ); |
|||
for (;;) { // current room handle |
|||
} |
|||
dungeon.indicate_hazards(); |
|||
else |
|||
{ |
|||
std::string in; |
|||
std::cin >> in; |
|||
if (std::cin.fail()) { |
|||
std::cin.clear(); |
|||
std::cin.ignore(999, '\n'); |
|||
theCave.getRoom( saved[S_PIT1] )->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; |
|||
} |
} |
||
} |
} |
||
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(); |
|||
std::string input; |
|||
std::cin >> input; |
|||
initGame( gr ); |
|||
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> |