1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
|
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <sstream>
#include <string>
int constexpr n_rooms = 20;
int constexpr n_pits = 2;
int constexpr n_bats = 2;
int constexpr max_args = 5;
int constexpr arrow_range = 3;
struct room { int a, b, c; enum { empty, wumpus, bat, pit } content = empty; };
struct command { char c; int n_args; int args[max_args]; };
room cave[n_rooms + 1] =
{ { 0, 0, 0 }, { 14, 17, 20 }, { 3, 9, 6 },
{ 2, 4, 11 }, { 3, 5, 13 }, { 4, 6, 15 },
{ 2, 5, 7 }, { 6, 8, 16 }, { 7, 9, 18 },
{ 2, 8, 10 }, { 9, 11, 19 }, { 3, 10, 12 },
{ 11, 13, 20 }, { 4, 12, 14 }, { 13, 15, 1 },
{ 5, 14, 16 }, { 7, 15, 17 }, { 16, 18, 1 },
{ 8, 17, 19 }, { 10, 18, 20 }, { 12, 19, 1 } };
[[nodiscard]] static room& random_room() { return cave[1 + (rand() % n_rooms)]; }
[[nodiscard]] static room& random_empty_room()
{
room* r; do r = &random_room(); while (r->content != room::empty); return *r;
}
[[nodiscard]] static room& random_adjacent_room(room const& r)
{
switch (rand() % 3) { case 0: return cave[r.a]; case 1: return cave[r.b]; default: return cave[r.c]; };
}
[[nodiscard]] static bool adjacent(room const& r, int n)
{ return (r.a == n) || (r.b == n) || (r.c == n); }
[[nodiscard]] static int count_adjacent_rooms_with_content(room const& r, int c)
{
return (cave[r.a].content == c) + (cave[r.b].content == c) + (cave[r.c].content == c);
}
[[nodiscard]] static command prompt_read_command()
{
command result {};
for (std::string line; ;)
{
std::cout << "Move or shoot (m-s)? ";
std::getline(std::cin, line);
std::istringstream iss{line};
if (std::string word; iss >> word)
{
result.c = word[0];
while (result.n_args < max_args && iss >> result.args[result.n_args])
++result.n_args;
break; // succeed, ignoring string->int conversion failures
}
}
return result;
}
int main() try
{
#ifdef DETERMINISTIC_RANDOM_SEED
std::srand(42);
#else
std::srand(static_cast<unsigned>(std::time(0)));
#endif
std::cin.exceptions(std::ios::failbit | std::ios::badbit | std::ios::eofbit);
int n_arrows = 5;
int wumpus_turns_awake = 0;
bool wumpus_killed = false;
bool player_killed = false;
room* wumpus_room = &random_room();
for (int i = 0; i < n_pits; ++i) random_empty_room().content = room::pit;
for (int i = 0; i < n_bats; ++i) random_empty_room().content = room::bat;
room* player_room = &random_empty_room();
while (true)
{
// Is it "wumpuses" or "wumpi"?
auto const [a, b, c, _] = *player_room;
std::cout << "You're in room " << player_room - cave << ".\n";
std::cout << "There are tunnels leading to rooms "
<< a << ", " << b << ", and " << c << ".\n";
if (n_arrows > 0) std::cout << "You have " << n_arrows
<< (n_arrows == 1? " arrow": " arrows") << " left.\n";
else std::cout << "You're entirely out of arrows. It's probably time to escape.\n";
int const nearby_wumpuses = count_adjacent_rooms_with_content(*player_room, room::wumpus);
int const nearby_pits = count_adjacent_rooms_with_content(*player_room, room::pit);
int const nearby_bats = count_adjacent_rooms_with_content(*player_room, room::bat);
if (nearby_pits == 1) std::cout << "You feel a cold draught in the air.\n";
if (nearby_pits >= 2) std::cout << "An icy wind blows from adjacent rooms. Your torch flickers.\n";
if (nearby_bats == 1) std::cout << "You hear a light rustle in the air.\n";
if (nearby_bats >= 2) std::cout << "Quiet clicking can be heard through two of the nearby tunnels.\n";
if (nearby_wumpuses >= 1) std::cout << "It smells somewhat. Maybe a wumpus is near.\n";
command const cmd = prompt_read_command();
if (cmd.c == 'q') { std::cout << "bye...\n"; return 0; }
if (cmd.c == 'm')
{
int const r = cmd.n_args? cmd.args[rand() % cmd.n_args]: 0;
if (cmd.n_args && adjacent(*player_room, r))
{
player_room = cave + r;
}
else
{
std::cout << "Disorientation has taken hold. You squeeze into a nearby tunnel at random.\n";
player_room = &random_adjacent_room(*player_room);
}
}
if (cmd.c == 's')
{
if (++wumpus_turns_awake == 1)
std::cout << "A thunderous roar echoes through the tunnels.\n";
if (n_arrows == 0)
{
std::cout << "You reach for an arrow, but you have none.\n";
}
else
{
std::cout << "You loose an arrow.\n";
--n_arrows;
room* arrow_room = player_room;
for (int i = 0; i < arrow_range; ++i)
{
arrow_room = (i < cmd.n_args && adjacent(*arrow_room, cmd.args[i]))
? cave + cmd.args[i]: &random_adjacent_room(*arrow_room);
if (arrow_room == player_room) { player_killed = true; break; }
if (arrow_room == wumpus_room) { wumpus_killed = true; break; }
}
}
}
if (wumpus_turns_awake >= 1)
{
wumpus_room->content = room::empty;
wumpus_room = &random_adjacent_room(*wumpus_room);
wumpus_room->content = room::wumpus;
}
if (player_room->content == room::bat)
{
std::cout << "Claws dig into your shoulders: a huge creature grabs you and carries you through the cave.\n";
player_room->content = room::empty;
player_room = &random_room();
random_empty_room().content = room::bat;
}
if (player_room->content == room::pit)
{
std::cout << "The ground gives out, and you plummet to your death.\n";
return 0;
}
if (player_room == wumpus_room)
{
std::cout << "The wumpus is upon you. Your death is painful and swift.\n";
return 0;
}
if (player_killed)
{
std::cout << "Your own arrow strikes you in the back.\nYou fall to the ground, dead.\n";
return 0;
}
if (wumpus_killed)
{
std::cout << "You hear a mighty scream as your arrow strikes the wumpus. You have slain your prey.\n";
return 0;
}
std::cout << '\n';
}
} catch (std::ios::failure const&) { return 0; }
|