Answered step by step
Verified Expert Solution
Link Copied!

Question

1 Approved Answer

After several disastrous events, a theme park that features living dinosaurs has bred fierce-looking carnivorous dinosaurs with such poor vision and other senses that in

After several disastrous events, a theme park that features living dinosaurs has bred fierce-looking carnivorous dinosaurs with such poor vision and other senses that in the event of yet another mishap, they would pose no danger to a person unless they were right at the same position as the person. You were flying alone over the ocean when your airplane developed a serious mechanical problem. Spotting a tiny island, you parachuted out of your plane and landed in a small valley on the island as the plane crashed into the shark-infested sea. Thanks to your GPS locator, help will find you, but wait! The valley, surrounded by sheer cliffs, is where the dinosaurs are bred. Fortunately, you are armed with a tranquilizer gun, but there are so many of them...

Well, that's the scenario for a new video game under development. Your assignment is to complete the prototype that uses character-based graphics.

If you execute this Windows program or this Mac program or this Linux program, you will see the player (indicated by @) in a rectangular valley filled with dinosaurs (usually indicated by D). At each turn, the user will select an action for the player to take. The player will take the action, and then each dinosaur will move one step in a random direction. If a dinosaur lands on the position occupied by the player, the player will be eaten.

This smaller Windows version or Mac version or Linux version of the game may help you see the operation of the game more clearly.

At each turn the player may take one of these actions:

Stand. In this case, the player does not move or shoot.

Move one step up, down, left, or right. If the player attempts to move out of the valley (e.g., down, when on the bottom row), the result is the same as standing. It is allowable for the player to move to a position currently occupied by a dinosaur. If no dinosaur occupies that position after the dinosaurs have moved, the player survived the turn.

Shoot in one of the four directions up, down, left, or right. If there is at least one dinosaur at any distance in that direction, the nearest one in that direction is the candidate victim. If more than one dinosaur is nearest (i.e., they occupy the same position), only one dinosaur at that position is the candidate victim. With 1/3 probability, the candidate victim is tranquilized; with 2/3 probability, the shot is ineffective and no dinosaur is tranquilized. A tranquilized dinosaur will sleep for the rest of the game; since it will pose no further threat to the player, it is removed from the game.

The game allows the user to select the player's action: u/d/l/r for movement, su/sd/sl/sr for shooting, and just hitting enter for standing. The user may also type q to prematurely quit the game.

When it's the dinosaurs' turn, each dinosaur picks a random direction (up, down, left, or right) with equal probability. The dinosaur moves one step in that direction if it can; if the dinosaur attempts to move out of the valley, however, (e.g., down, when on the bottom row), it does not move. More than one dinosaur may occupy the same position; in that case, instead of D, the display will show a digit character indicating the number of dinosaurs at that position (where 9 indicates 9 or more). If after the dinosaurs move, a dinosaur occupies the same position as the player, the player is eaten.

Your assignment is to complete this C++ program skeleton to produce a program that implements the described behavior. (We've indicated where you have work to do by comments containing the text TODO; remove those comments as you finish each thing you have to do.) The program skeleton you are to flesh out defines four classes that represent the four kinds of objects this program works with: Game, Valley, Dinosaur, and Player. Details of the interface to these classes are in the program skeleton, but here are the essential responsibilities of each class:

Game

To create a Game, you specify a number of rows and columns and the number of dinosaurs to start with. The Game object creates an appropriately sized Valley and populates it with a Player and the Dinosaurs.

A game may be played.

Valley

When a Valley object of a particular size is created, it has no dinosaurs or player. In the Valley coordinate system, row 1, column 1 is the upper-left-most position that can be occupied by a Dinosaur or Player. (If a Valley were created with 10 rows and 20 columns, then the lower-right-most position that could be occupied would be row 10, column 20.)

You may tell a Valley object to create or destroy a Dinosaur at a particular position.

You may tell a Valley object to create a Player at a particular position.

You may tell a Valley object to have all the dinosaurs in it make their move.

You may ask a Valley object its size, how many dinosaurs are at a particular position, and how many dinosaurs altogether are in the Valley.

You may ask a Valley object for access to its player.

A Valley object may be displayed on the screen, showing the locations of the dinosaurs and the player, along with other status information.

Player

A Player is created at some position (using the Valley coordinate system, where row 1, column 1 is the upper-left-most position, etc.).

You may tell a Player to stand, move in a direction, or shoot in a direction.

You may tell a Player it has died.

You may ask a Player for its position, alive/dead status, and age. (The age is the count of how many turns the player has survived.)

Dinosaur

A dinosaur is created at some position (using the Valley coordinate system, where row 1, column 1 is the upper-left-most position, etc.).

You may tell a Dinosaur to move.

You may ask a Dinosaur object for its position.

The skeleton program you are to complete has all of the class definitions and implementations in one source file, which is awkward. Since we haven't yet learned about separate compilation, we'll have to live with it.

Complete the implementation in accordance with the description of the game. You are allowed to make whatever changes you want to the private parts of the classes: You may add or remove private data members or private member functions, or change their types. You must notmake any deletions, additions, or changes to the public interface of any of these classes we're depending on them staying the same so that we can test your programs. You can, of course, make changes to the implementations of public member functions, since the callers of the function wouldn't have to change any of the code they write to call the function. You must not declare any public data members, nor use any global variables other than the global constants already in the skeleton code, except that you may add additional global constants if you wish. You may add additional functions that are not members of any class. The word friend must not appear in your program.

Any member functions you implement must never put an object into an invalid state, one that will cause a problem later on. (For example, bad things could come from placing a dinosaur outside a valley.) Any function that has a reasonable way of indicating failure through its return value should do so. Constructors pose a special difficulty because they can't return a value. If a constructor can't do its job, we have it write an error message and exit the program with failure by calling exit(1);. (We haven't learned about throwing an exception to signal constructor failure.)

// dinos.cpp // Portions you are to complete are marked with a TODO: comment. // We've provided some incorrect return statements (so indicated) just // to allow this skeleton program to compile and run, albeit incorrectly. // The first thing you probably want to do is implement the trivial // functions (marked TRIVIAL). Then get Valley::display going. That gives // you more flexibility in the order you tackle the rest of the functionality. // As you finish implementing each TODO: item, remove its TODO: comment. #include  #include  #include  #include  #include  using namespace std; /////////////////////////////////////////////////////////////////////////// // Manifest constants /////////////////////////////////////////////////////////////////////////// const int MAXROWS = 20; // max number of rows in a valley const int MAXCOLS = 40; // max number of columns in a valley const int MAXDINOSAURS = 170; // max number of dinosaurs allowed const int UP = 0; const int DOWN = 1; const int LEFT = 2; const int RIGHT = 3; const int NUMDIRS = 4; /////////////////////////////////////////////////////////////////////////// // Type definitions /////////////////////////////////////////////////////////////////////////// class Valley; // This is needed to let the compiler know that Valley is a // type name, since it's mentioned in the Dinosaur declaration. class Dinosaur { public: // Constructor Dinosaur(Valley* vp, int r, int c); // Accessors int row() const; int col() const; // Mutators void move(); private: Valley* m_valley; int m_row; int m_col; }; class Player { public: // Constructor Player(Valley *vp, int r, int c); // Accessors int row() const; int col() const; int age() const; bool isDead() const; // Mutators void stand(); void move(int dir); bool shoot(int dir); void setDead(); private: Valley* m_valley; int m_row; int m_col; int m_age; bool m_dead; }; class Valley { public: // Constructor/destructor Valley(int nRows, int nCols); ~Valley(); // Accessors int rows() const; int cols() const; Player* player() const; int dinosaurCount() const; int numDinosaursAt(int r, int c) const; void display(string msg) const; // Mutators bool addDinosaur(int r, int c); bool addPlayer(int r, int c); bool destroyDinosaur(int r, int c); bool moveDinosaurs(); private: int m_rows; int m_cols; Player* m_player; Dinosaur* m_dinos[MAXDINOSAURS]; int m_nDinos; }; class Game { public: // Constructor/destructor Game(int rows, int cols, int nDinos); ~Game(); // Mutators void play(); private: Valley* m_valley; }; /////////////////////////////////////////////////////////////////////////// // Auxiliary function declarations /////////////////////////////////////////////////////////////////////////// int decodeDirection(char dir); int randInt(int min, int max); void clearScreen(); /////////////////////////////////////////////////////////////////////////// // Dinosaur implementation /////////////////////////////////////////////////////////////////////////// Dinosaur::Dinosaur(Valley* vp, int r, int c) { if (vp == nullptr) { cout << "***** A dinosaur must be created in some Valley!" << endl; exit(1); } if (r < 1 || r > vp->rows() || c < 1 || c > vp->cols()) { cout << "***** Dinosaur created with invalid coordinates (" << r << "," << c << ")!" << endl; exit(1); } m_valley = vp; m_row = r; m_col = c; } int Dinosaur::row() const { return m_row; } int Dinosaur::col() const { // TODO: TRIVIAL: Return the number of the column the Dinosaur is at. return 1; // Replace this incorrect line with the correct code. } void Dinosaur::move() { // Attempt to move in a random direction; if we can't move, don't move switch (randInt(0, NUMDIRS-1)) { case UP: // TODO: Move the dinosaur up one row if possible. break; case DOWN: case LEFT: case RIGHT: // TODO: Implement the other movements. break; } } /////////////////////////////////////////////////////////////////////////// // Player implementations /////////////////////////////////////////////////////////////////////////// Player::Player(Valley* vp, int r, int c) { if (vp == nullptr) { cout << "***** The player must be created in some Valley!" << endl; exit(1); } if (r < 1 || r > vp->rows() || c < 1 || c > vp->cols()) { cout << "**** Player created with invalid coordinates (" << r << "," << c << ")!" << endl; exit(1); } m_valley = vp; m_row = r; m_col = c; m_age = 0; m_dead = false; } int Player::row() const { // TODO: TRIVIAL: Return the number of the row the player is at. return 1; // Replace this incorrect line with the correct code. } int Player::col() const { // TODO: TRIVIAL: Return the number of the column the player is at. return 1; // Replace this incorrect line with the correct code. } int Player::age() const { // TODO: TRIVIAL: Return the player's age. return 0; // Replace this incorrect line with the correct code. } void Player::stand() { m_age++; } void Player::move(int dir) { m_age++; // Attempt to move in the indicated direction; if we can't move, don't move switch (dir) { case UP: // TODO: Move the player up one row if possible. break; case DOWN: case LEFT: case RIGHT: // TODO: Implement the other movements. break; } } bool Player::shoot(int dir) { m_age++; if (randInt(1, 3) > 1) // miss with 2/3 probability return false; // TODO: Destroy the nearest dinosaur in direction dir return false; // Replace this line with the correct code. } bool Player::isDead() const { // TODO: TRIVIAL: Return whether the player is dead. return false; // Replace this incorrect line with the correct code. } void Player::setDead() { m_dead = true; } /////////////////////////////////////////////////////////////////////////// // Valley implementations /////////////////////////////////////////////////////////////////////////// Valley::Valley(int nRows, int nCols) { if (nRows <= 0 || nCols <= 0 || nRows > MAXROWS || nCols > MAXCOLS) { cout << "***** Valley created with invalid size " << nRows << " by " << nCols << "!" << endl; exit(1); } m_rows = nRows; m_cols = nCols; m_player = nullptr; m_nDinos = 0; } Valley::~Valley() { // TODO: Delete the player and all remaining dynamically allocated dinosaurs. } int Valley::rows() const { // TODO: TRIVIAL: Return the number of rows in the valley. return 1; // Replace this incorrect line with the correct code. } int Valley::cols() const { // TODO: TRIVIAL: Return the number of columns in the valley. return 1; // Replace this incorrect line with the correct code. } Player* Valley::player() const { return m_player; } int Valley::dinosaurCount() const { return m_nDinos; } int Valley::numDinosaursAt(int r, int c) const { // TODO: Return the number of dinosaurs at row r, column c. return 0; // Replace this incorrect line with the correct code. } void Valley::display(string msg) const { // Position (row,col) in the valley coordinate system is represented in // the array element grid[row-1][col-1] char grid[MAXROWS][MAXCOLS]; int r, c; // Fill the grid with dots for (r = 0; r < rows(); r++) for (c = 0; c < cols(); c++) grid[r][c] = '.'; // Indicate each dinosaur's position // TODO: If one dinosaur is at some position, set the char to 'Z'. // If it's 2 through 8, set it to '2' through '8'. // For 9 or more, set it to '9'. // Indicate player's position if (m_player != nullptr) { // Set the char to '@', unless there's also a dinosaur there, // in which case set it to '*'. char& gridChar = grid[m_player->row()-1][m_player->col()-1]; if (gridChar == '.') gridChar = '@'; else gridChar = '*'; } // Draw the grid clearScreen(); for (r = 0; r < rows(); r++) { for (c = 0; c < cols(); c++) cout << grid[r][c]; cout << endl; } cout << endl; // Write message, dinosaur, and player info cout << endl; if (msg != "") cout << msg << endl; cout << "There are " << dinosaurCount() << " dinosaurs remaining." << endl; if (m_player == nullptr) cout << "There is no player." << endl; else { if (m_player->age() > 0) cout << "The player has lasted " << m_player->age() << " steps." << endl; if (m_player->isDead()) cout << "The player is dead." << endl; } } bool Valley::addDinosaur(int r, int c) { // If MAXDINOSAURS have already been added, return false. Otherwise, // dynamically allocate a new dinosaur at coordinates (r,c). Save the // pointer to the newly allocated dinosaur and return true. // TODO: Implement this return false; // Replace this incorrect line with the correct code. } bool Valley::addPlayer(int r, int c) { // Don't add a player if one already exists if (m_player != nullptr) return false; // Dynamically allocate a new Player and add it to the valley m_player = new Player(this, r, c); return true; } bool Valley::destroyDinosaur(int r, int c) { // TODO: Destroy one dinosaur at row r, column c, and return true. // Return false is there is no dinosaur there to destroy. return false; // Replace this incorrect line with the correct code. } bool Valley::moveDinosaurs() { for (int k = 0; k < m_nDinos; k++) { // TODO: Have the k-th dinosaur in the valley make one move. // If that move results in the dinosaur being at the same // position as the player, the player dies. } // return true if the player is still alive, false otherwise return ! m_player->isDead(); } /////////////////////////////////////////////////////////////////////////// // Game implementations /////////////////////////////////////////////////////////////////////////// Game::Game(int rows, int cols, int nDinos) { if (nDinos < 0) { cout << "***** Cannot create Game with negative number of Dinosaurs!" << endl; exit(1); } if (nDinos > MAXDINOSAURS) { cout << "***** Trying to create Game with " << nDinos << " dinosaurs; only " << MAXDINOSAURS << " are allowed!" << endl; exit(1); } if (rows == 1 && cols == 1 && nDinos > 0) { cout << "***** Cannot create Game with nowhere to place the Dinosaurs!" << endl; exit(1); } // Create valley m_valley = new Valley(rows, cols); // Add player int rPlayer = randInt(1, rows); int cPlayer = randInt(1, cols); m_valley->addPlayer(rPlayer, cPlayer); // Populate with dinosaurs while (nDinos > 0) { int r = randInt(1, rows); int c = randInt(1, cols); // Don't put a dinosaur where the player is if (r == rPlayer && c == cPlayer) continue; m_valley->addDinosaur(r, c); nDinos--; } } Game::~Game() { delete m_valley; } void Game::play() { string msg = ""; m_valley->display(msg); Player* player = m_valley->player(); if (player == nullptr) return; while ( ! m_valley->player()->isDead() && m_valley->dinosaurCount() > 0) { cout << "Move (u/d/l/r/su/sd/sl/sr/q or nothing): "; string action; getline(cin,action); if (action.size() == 0) player->stand(); else { switch (action[0]) { default: // if bad move, nobody moves cout << '\a' << endl; // beep continue; case 'q': return; case 'u': case 'd': case 'l': case 'r': player->move(decodeDirection(action[0])); break; case 's': if (action.size() < 2) // if no direction, nobody moves { cout << '\a' << endl; // beep continue; } switch (action[1]) { default: // if bad direction, nobody moves cout << '\a' << endl; // beep continue; case 'u': case 'd': case 'l': case 'r': if (player->shoot(decodeDirection(action[1]))) msg = "Hit!"; else msg = "Missed!"; break; } break; } } if ( ! player->isDead()) m_valley->moveDinosaurs(); m_valley->display(msg); msg = ""; } if (m_valley->player()->isDead()) cout << "You lose." << endl; else cout << "You win." << endl; } /////////////////////////////////////////////////////////////////////////// // Auxiliary function implementations /////////////////////////////////////////////////////////////////////////// int decodeDirection(char dir) { switch (dir) { case 'u': return UP; case 'd': return DOWN; case 'l': return LEFT; case 'r': return RIGHT; } return -1; // bad argument passed in! } // Return a random int from min to max, inclusive int randInt(int min, int max) { if (max < min) swap(max, min); static random_device rd; static default_random_engine generator(rd()); uniform_int_distribution<> distro(min, max); return distro(generator); } /////////////////////////////////////////////////////////////////////////// // main() /////////////////////////////////////////////////////////////////////////// int main() { // Create a game // Use this instead to create a mini-game: Game g(3, 3, 2); Game g(15, 18, 100); // Play the game g.play(); } /////////////////////////////////////////////////////////////////////////// // clearScreen implementation /////////////////////////////////////////////////////////////////////////// // DO NOT MODIFY OR REMOVE ANY CODE BETWEEN HERE AND THE END OF THE FILE!!! // THE CODE IS SUITABLE FOR VISUAL C++, XCODE, AND g++ UNDER LINUX. // Note to Xcode users: clearScreen() will just write a newline instead // of clearing the window if you launch your program from within Xcode. // That's acceptable. (The Xcode output window doesn't have the capability // of being cleared.) #ifdef _MSC_VER // Microsoft Visual C++ #include  void clearScreen() { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_SCREEN_BUFFER_INFO csbi; GetConsoleScreenBufferInfo(hConsole, &csbi); DWORD dwConSize = csbi.dwSize.X * csbi.dwSize.Y; COORD upperLeft = { 0, 0 }; DWORD dwCharsWritten; FillConsoleOutputCharacter(hConsole, TCHAR(' '), dwConSize, upperLeft, &dwCharsWritten); SetConsoleCursorPosition(hConsole, upperLeft); } #else // not Microsoft Visual C++, so assume UNIX interface #include  #include  #include  void clearScreen() // will just write a newline in an Xcode output window { static const char* term = getenv("TERM"); if (term == nullptr || strcmp(term, "dumb") == 0) cout << endl; else { static const char* ESC_SEQ = "\x1B["; // ANSI Terminal esc seq: ESC [ cout << ESC_SEQ << "2J" << ESC_SEQ << "H" << flush; } } #endif

Step by Step Solution

There are 3 Steps involved in it

Step: 1

blur-text-image

Get Instant Access to Expert-Tailored Solutions

See step-by-step solutions with expert insights and AI powered tools for academic success

Step: 2

blur-text-image

Step: 3

blur-text-image

Ace Your Homework with AI

Get the answers you need in no time with our AI-driven, step-by-step assistance

Get Started

Recommended Textbook for

Students also viewed these Databases questions