So far you’ve been deep in the trenches of implementing a functional game that interacts with a user. Now, it’s time to pivot and think as the user of this game. Specifically, what kind of decisions should you make as a player of this game to get the best performance from the elevators?
NOTE: This part of the specification is
tentative and subject to change up until the due date of the Core now final.
Play through the game, and for each move you make, ask yourself why you decided to do the thing you did. What kind of rules come to mind?
Understanding the Metrics
What does it mean to be the “best” elevator? To answer this question, we will resort to defining a
SatisfactionIndex. Make sure to read through and understand what each of these values represent. If you had to play the game trying to get the best possible values for these, what would you do?
The AI Move Framework
Among the functionality we’ve built for you, we’ve created the files
AI.h, which are provided with the same information that is provided to the Player via the terminal and should create valid
Moves. For the reach, you will be responsible for implementing the functions in
AI.cpp in such a way that gets good performance. More concretely, we will evaluate 2 things:
- Validity: Your AI should only produce valid moves (from
getAIMoveString) and valid pickup lists (from
- Performance: Your AI should try and optimize the metrics in the
SatisfactionIndexon certain game files. It is up to you as to what benchmark you’d like to optimize!
You are encouraged to devise helper functions to aid you in writing the functions in
AI.cpp; you can add these directly to
AI.cpp when you submit. You are also permitted to include any libraries present in the C++11 standard template library, provided that you don’t have to modify your build process to get them to work. Do not edit AI.h. We should be able to run your code immediately when we make a project in XCode or Visual Studio.
Testing your AI
As always, it is imperative to test your code well. Elevators reach is different from the other 183 projects in that your grade is determined by performance for
AI.cpp. So, now your tests are not only concerned with correctness, but also with benchmarking the power of your AI. You should be constantly testing whether your AI performs well on different inputs.
There are two ways to test your AI:
- When running your implementation for the Core, select either Load saved game (once you have completed the function
Game::playGame) or Start new game. Then enter 2 for Watch AI play. As you play the game, when you are prompted to Enter move: or Choices: for a pickup list, just press enter. Your AI should provide the move or pickup list.
- Submit your AI to the autograder.
You will submit your files to the autograder for the Reach. You will submit both
AI.cpp to the autograder.
Game.cpp functions will be graded for correctness, similar to previous projects and the files submitted for the core.
Your AI, the functions in
AI.cpp, will be graded by the performance of your AI with a variety of input events:
- We will compile your AI.cpp file with the staff implementation for the Core and
Game.cpp. Your AI will not be tested with your implementation of
Game.cpp, or your solution files for the Core.
- We will execute your AI repeatedly with different game input files. Game input files will be a long list of timestamped events, similar to what is shown in
save.infound in the starter files.
- The input files will be of differing difficulty, for example by varying the number of events and starting
- The input files will have different patterns. Examples are:
- A larger potion of
Persons entering the bottom/lower floors with a higher target floor. These tests are labeled “Morning” on the autograder.
Persons with a lower target floor. These tests are labeled “Evening” on the autograder.
- Or a random mix. These tests are labeled “Random” on the autograder.
- A larger potion of
- The performance of your AI will be measured using a subset of the metrics in
SatisfactionIndex, recorded at the end of each game. The final values will be compared to the passing thresholds determined by staff. The autograder will show you which test cases you pass and which you do not, along with your
SatisfactionIndexmetrics, for each test.
This section may seem fairly open-ended, and you may feel slightly confused about what your AI is responsible for doing. This is by design. Often times, real-world “specs” are vague about a lot of implementation details. It becomes the responsibility of the programmer to think about the problem and decide on what’s best for the project. Think! Discuss with your groupmates! Think some more! Try things! Fail! Try again! Have fun!
The functions in
AI.h are not part of a class.
You will be writing the implementations for the following functions:
/* * Requires: buildingState is a valid state representing the current building * Modifies: Nothing * Effects: Returns a string that represents the decision the AI makes * given the current state of the building, which it reads * from buildingState. * The string should share the exact format as a * human player's move input. */ string getAIMoveString(const BuildingState& buildingState);
This function should determine the next move when playing the game.
const BuildingState& buildingStateparameter is a BuildingState object. Your
getAIMoveStringfunction can use the public member functions to check the current state of the building to determine what moves to make.
stringreturn value will be a valid move command string, such as
"e1p", as determined by this function.
/* * Requires: buildingState is a valid state representing the current building * move is a pickup move that was generated by getAIMoveString() * floorToPickup represents the floor the pickup move occurred. * Modifies: move * Effects: Returns a string representing which people indices * should be picked up. The string should share the exact format * as a human player's pickup list input. */ string getAIPickupList(const Move& move, const BuildingState& buildingState, const Floor& floorToPickup);
This function should determine the pickup list based on the move and state of the building and floor.
moveis a string representing the move, such as
const BuildingState& buildingStateparameter is a BuildingState object. See
AI.cpp(Reach) for additional information about BuildingState.
const Floor& floorToPickupparameter is a Floor object. Your
getAIPickupListfunction can use the public member functions to check the current state of the Floor (i.e., Person instances waiting to be picked up) to determine what pickup list would be optimal.
stringreturn value will be a valid pickup list string, such as
"2689", as determined by this function. Because there are a maximum of 10 people per floor, every index will be a single digit.
“I choose the people at indices 0, 1, 3, and 5 to pickup”
pickupList is valid if it meets the following conditions:
- There are no duplicate indices present in
- Each element of
pickupListis a non-negative digit
- The length of the
pickupListis less than or equal to the capacity of an elevator
- The maximum value pointed to by an index of
pickupListmust be strictly less than the number of people on the floor pointed to by
- Each person represented by an index in
pickupListmust be going in the same direction relative to