БлогNot. Hunt the Wumpus forever или Охота на Вампуса по-русски

Hunt the Wumpus forever или Охота на Вампуса по-русски

Какая компьютерная игра была первой игрой на карте-графе, первой текстовой и вообще первой приключенческой игрой на свете? Наверное, всё-таки Вампус.

Программисты до сих пор любят её, боюсь, что нужно быть программистом, чтобы понять, почему :)

А вот найти классические консольно-текстовые реализации с исходниками уже непросто, кажется, проще сделать заново, ну и пусть будет с русскоязычным интерфейсом заодно.

Проверил в консоли Studio 2019, для запуска игры код нужно скомпилировать и выполнить, а для этого хватит вставки листинга в единственный файл нового пустого проекта C++. Никаких цветных пятен, правильная чёрно-белая классика :) В исходнике есть специфичный для Studio код (см. строчку подключения файла windows.h).

#include <iostream>
#include <cstdlib>   /* srand, rand */
#include <ctime>     /* time */
#include <clocale>   /* setlocale */
#include <windows.h> /* SetConsoleCP, SetConsoleOutputCP */
using namespace std;

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:
 int numberOfRooms, currentRoom, startingPosition, 
     wumpusRoom, batRoom1, batRoom2, pitRoom1, pitRoom2, //где вампус, мыши, ямы
     wumpusStart, bat1Start, bat2Start; //кто в какой комнате
 bool playerAlive, wumpusAlive; //кто жив
 int numArrows; //стрелы
 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();
public:
 WumpusGame();
 void StartGame();
 static void clearInput() { cin.clear(); cin.ignore(10000, '\n'); }
};

WumpusGame::WumpusGame() { numberOfRooms = 20; }

void WumpusGame::PrintInstructions() {
 char wait;
 cout << endl << "Игра 'Охота на Вампуса', оригинальный (С) Gregory Yob, 1972" << endl << endl << 
 "Вампус живет в пещере из 20 комнат. В каждой комнате есть 3 туннеля, ведущих в" << endl <<
 "соседние комнаты. Посмотрите на додекаэдр, чтобы увидеть, как это устроено." << endl <<
 "В двух комнатах есть бездонные ямы. Если вы войдёте туда,  вы упадёте  в яму и" << endl <<
 "проиграете." << endl <<
 "В двух других комнатах есть летучие мыши.  Если  вы войдёте туда, мышь хватает" << endl <<
 "вас и уносит в другую случайно выбранную комнату. Сама мышь  после этого  тоже" << endl <<
 "перемещается в случайное место на карте." << endl <<
 "У Вампуса ноги на присосках  и он слишком велик для того, чтобы мыши могли его" << endl <<
 "побеспокоить. Обычно он спит. Его могут разбудить две вещи:  если  вы стреляте" << endl <<
 "или же входите в его комнату. Если Вампус проснется, то есть 3 шанса из 4, что" << endl <<
 "он перейдёт в соседнюю комнату и 1 шанс из 4, что он останется на месте." << endl <<
 "Если вы окажетесь в одной комнате с Вампусом, он съест вас :(" << endl <<
 "За один ход вы можете  перейти в смежную комнату или  выстрелить в неё.  У вас" << endl <<
 "всего 3 стрелы. Вы проиграете, когда они закончатся.  Если  указать комнату, в" << endl <<
 "которую невозмоно выстрелить, то стрела пропадёт зря." << endl <<
 "Когда вы находитесь в смежной комнате с опасностью, выводятся предупреждения:" << endl <<
 "\"Я чувствую отвратительный запах\" (если рядом Вампус)" << endl <<
 "\"Летучие мыши рядом\"" << endl <<
 "\"Я чувствую сквозняк\" (если рядом - бездонная яма)" << endl <<
 "Введите что-нибудь, чтобы вернуться в главное меню.";
 cin >> wait;
}

void WumpusGame::PlaceBats() {
 bool validRoom = false;
 while (!validRoom) {
  batRoom1 = rand() % numberOfRooms + 1;
  if (batRoom1 != wumpusRoom) validRoom = true;
 }
 validRoom = false;
 while (!validRoom) {
  batRoom2 = rand() % numberOfRooms + 1;
  if (batRoom2 != wumpusRoom && batRoom2 != batRoom1) validRoom = true;
 }
 bat1Start = batRoom1; bat2Start = batRoom2;
}

void WumpusGame::PlacePits() {
 pitRoom1 = rand() % 20 + 1;
 pitRoom2 = rand() % 20 + 1;
}

void WumpusGame::PlaceWumpus() {
 int randomRoom = rand() % 20 + 1;
 wumpusRoom = randomRoom;
 wumpusStart = wumpusRoom;
}

void WumpusGame::PlacePlayer() {
 startingPosition = 0;
 currentRoom = Move(0);
}

bool WumpusGame::IsValidMove(int roomID) {
 if (roomID < 0) return false;
 if (roomID > numberOfRooms) return false;
 if (!IsRoomAdjacent(currentRoom, roomID)) return false;
 return true;
}

bool WumpusGame::IsRoomAdjacent(int roomA, int roomB) {
 for (int j = 0; j < 3; j++) {
  if (adjacentRooms[roomA][j] == roomB) return true;
 }
 return false;
}

int WumpusGame::Move (int newRoom) { return newRoom; }

void WumpusGame::InspectCurrentRoom() {
 if (currentRoom == wumpusRoom) {
  cout << endl << "Вампус съел вас, игра окончена :(" << endl;
  PlayAgain();
 }
 else if (currentRoom == batRoom1 || currentRoom == batRoom2) {
  int roomBatsLeft = currentRoom;
  bool validNewBatRoom = false;
  bool isBatRoom = false;
  cout << endl << "Вы схвачены летучей мышью!" << endl;
  if (currentRoom == pitRoom1 || currentRoom == pitRoom2)
   cout << endl << "К счастью, летучая мышь спасла вас от бездонной ямы" << endl;
  while (!isBatRoom) {
   currentRoom = Move(rand() % numberOfRooms + 1);
   if (currentRoom != batRoom1 && currentRoom != batRoom2) isBatRoom = true;
  }
  cout << endl << "Летучая мышь перенесла вас в комнату " << currentRoom << endl;
  InspectCurrentRoom();
  if (roomBatsLeft == batRoom1) {
   while (!validNewBatRoom) {
    batRoom1 = rand() % (numberOfRooms-1) + 1;
    if (batRoom1 != wumpusRoom && batRoom1 != currentRoom) validNewBatRoom = true;
   }
  }
  else {
   while (!validNewBatRoom) {
    batRoom2 = rand() % (numberOfRooms-1) + 1;
    if (batRoom2 != wumpusRoom && batRoom2 != currentRoom) validNewBatRoom = true;
   }
  }
 }
 else if (currentRoom == pitRoom1 || currentRoom == pitRoom2) {
  cout << endl << "Вы свалились в бездонную яму, игра окончена :(" << endl;
  PlayAgain();
 }
 else {
  cout << endl << "Вы находитесь в комнате " << currentRoom << endl;
  if (IsRoomAdjacent(currentRoom, wumpusRoom)) {
   cout << endl << "Я чувствую отвратительный запах..." << endl;
  }
  if (IsRoomAdjacent(currentRoom, batRoom1) || IsRoomAdjacent(currentRoom, batRoom2)) {
   cout << endl << "Летучие мыши рядом..." << endl;
  }
  if (IsRoomAdjacent(currentRoom, pitRoom1) || IsRoomAdjacent(currentRoom, pitRoom2)) {
   cout << endl << "Я чувствую сквозняк..." << endl;
  }
  cout << endl << "Туннели ведут в комнаты ";
  for (int j = 0; j < 3; j++) cout << adjacentRooms[currentRoom][j] << (j < 2 ? ", " : "");
  cout << endl;
 }
}

void WumpusGame::PerformAction(int cmd) {
 int newRoom;
 switch (cmd) {
  case 1:
   cout << endl << "В какую комнату пойдете? ";
   try {
    cin >> newRoom;
    if (cin.fail()) throw invalid_argument ("Вы должны ввести число!");
    if (IsValidMove(newRoom)) {
     currentRoom = Move(newRoom);
     InspectCurrentRoom();
    }
    else {
     cout << endl << "Вы не можете сейчас перейти в эту комнату :(" << endl;
    }
   }
   catch (invalid_argument& error)  {
    WumpusGame::clearInput();
    cerr << endl << error.what() << endl;
   }
  break;
  case 2:
   if (numArrows > 0) {
    cout << endl << "В какую комнату будете стрелять? " << endl;
    try {
     cin >> newRoom;
     if (cin.fail()) throw invalid_argument("Вы должны ввести число!");
     if (IsValidMove(newRoom)) {
      numArrows--;
      if (newRoom == wumpusRoom) {
       wumpusAlive = false;
       cout << endl << "Примите поздравления! Вы убили Вампуса и победили!" << endl;
       cout << endl << "Введите что-нибудь, чтобы вернуться в главное меню" << endl;
       cin >> newRoom;
       WumpusGame::clearInput();
      }
      else {
       cout << endl << "Мимо! Но вы побеспокоили Вампуса..." << endl;
       MoveStartledWumpus(wumpusRoom);
       cout << endl << "Осталось стрел: " << numArrows << endl;
       if (wumpusRoom == currentRoom) {
        cout << endl << "Вампус ворвался в вашу комнату и сожрал вас, игра окончена :(" << endl;
        PlayAgain();
       }
      }
     }
     else {
      cout << endl << "Вы не можете стрелять в эту комнату :(" << endl;
     }
    }
    catch (invalid_argument& error) {
     WumpusGame::clearInput();
     cerr << endl << error.what() << endl;
    }
   }
   else  {
    cout << endl << "У вас больше нет ни одной стрелы :(" << endl;
   }
  break;
  case 3:
   cout << endl << "Выходим из текущей игры..." << endl;
   playerAlive = false;
  break;
  default:
   cout << endl << "Пожалуйста, выберите правильное действие в меню" << endl;
  break;
 }
}

void WumpusGame::MoveStartledWumpus(int roomNum) {
 int rando = rand() % 3;
 if (rando != 3) wumpusRoom = adjacentRooms[roomNum][rando];
}

void WumpusGame::PlayAgain() {
 char reply;
 cout << endl << "Хотите сыграть заново на этой же карте? Введите Y для новой игры" << endl;
 cin >> reply;
 if (reply == 'y' || reply == 'Y') {
  currentRoom = startingPosition;
  wumpusRoom = wumpusStart;
  batRoom1 = bat1Start;
  batRoom2 = bat2Start;
  cout << endl << "Начата новая игра" << endl;
  InspectCurrentRoom();
 }
 else {
  playerAlive = false;
 }
}

void WumpusGame::PlayGame() {
 int choice;
 bool validChoice = false;
 cout << endl << "Запускаем игру..." << endl;
 PlaceWumpus();
 PlaceBats();
 PlacePits();
 PlacePlayer();
 playerAlive = true;
 wumpusAlive = true;
 numArrows = 3;
 InspectCurrentRoom();
 while (playerAlive && wumpusAlive) { //Игровой цикл
  cout << endl << "Выберите действие" << endl << endl <<
   "1) Идти" << endl <<
   "2) Стрельба" << endl <<
   "3) Выход";
  do {
   validChoice = true;
   cout << endl << "Пожалуйста, выберите пункт меню: ";
   try {
    cin >> choice;
    switch (choice) {
     case 1: PerformAction(1); break;
     case 2: PerformAction(2); break;
     case 3: PerformAction(3); break;
     default:
      validChoice = false;
      cout << endl << "Неверный выбор, попробуем ещё раз" << endl;
      WumpusGame::clearInput();
     break;
    }
   }
   catch (...) {
    validChoice = false;
    cout << endl << "Неверный выбор, попробуем ещё раз" << endl;
    WumpusGame::clearInput();
   }
  } while (validChoice == false);
 }
}

void WumpusGame::StartGame() {
 srand(time(NULL));
 int choice;
 bool validChoice;
 bool keepPlaying;
 wumpusStart = bat1Start = bat2Start = -1;
 do {
  keepPlaying = true;
  cout << endl << "Добро пожаловать в \"Охоту на Вампуса\"" << endl << endl <<
   "1) Новая игра" << endl <<
   "2) Помощь" << endl <<
   "3) Выход" << endl;
  do {
   validChoice = true;
   cout << endl << "Пожалуйста, выберите пункт меню: ";
   try {
    cin >> choice;
    switch (choice) {
     case 1: PlayGame(); break;
     case 2: PrintInstructions(); break;
     case 3: 
      cout << endl << "Выполняем выход..." << endl;
      keepPlaying = false;
     break;
     default:
      validChoice = false;
      cout << endl << "Неверный выбор, попробуем ещё раз" << endl;
      WumpusGame::clearInput();
     break;
    }
   }
   catch (...) {
    validChoice = false;
    cout << endl << "Неверный выбор, попробуем ещё раз" << endl;
    WumpusGame::clearInput();
   }
  } while (validChoice == false);
 } while (keepPlaying);
}

int main() {
 setlocale(LC_ALL, "Rus"); 
 SetConsoleCP(1251); SetConsoleOutputCP(1251);
 WumpusGame game;
 game.StartGame();
 return 0;
}
Скриншот запущенной игры Вампус
Скриншот запущенной игры Вампус

13.06.2020, 08:16 [1560 просмотров]


теги: программирование c++ игра random ретро

К этой статье пока нет комментариев, Ваш будет первым