p2-rps
EECS 183 Project 2: Rock-Paper-Scissors
Due Friday, September 27th at 8:00 p.m. (accepted until 11:59:59 p.m.)
Direct autograder link
Overview
In this project, you will be implementing a rock-paper-scissors game! Rock-paper-scissors is a hand game played between two people, in which each player simultaneously forms one of three shapes with an outstretched hand. These shapes are “rock” (a simple fist), “paper” (a flat hand), and “scissors” (a fist with the index and middle fingers extended, forming a V).
A player who plays rock will beat another player who has chosen scissors, but will lose to one who has played paper; a play of paper will lose to a play of scissors. If both players choose the same shape, the game is tied.
This project is significantly more difficult than Project 1 and you can expect it to take 2 to 3 times longer to complete.
See the image below for a visual representation of the rules of rock-paper-scissors:
Objectives
By completing this project, you will learn to:
- Develop programs that are divided into functions
- Compile programs that are not yet complete by using stubs
- Verify the correctness of functions by writing test cases
- Implement functions based on a specification and RME comment
- Use existing operators to perform interactive I/O using C++
- Write logical expressions and selection statements in C++
- Create algorithms that use conditionals and loops
- An important objective is to have fun!
You will apply the following skills you learned in lecture:
- Lecture 3
- Call math functions like
floor()
andceil()
.
- Call math functions like
- Lecture 4
- Call functions with multiple parameters
- Use
return
statements to provide a result from a function
- Lecture 5
- Create function declarations to allow calls to a function before its code is provided in a function definition
- Interpret RME comments
- Use the iterative development cycle for writing your functions
- Lecture 6
- Use
if
,else
, andelse if
to conditionally execute code - Execute the cin algorithm to determine how a given user input will be stored into variables
- Use
- Lecture 7
- Write event-controlled loops using
while
- Combine loops with
cin
to continue reading until the user is finished
- Write event-controlled loops using
- Lecture 8
- Write count-controlled loops using
for
- Write count-controlled loops using
Getting Started
Starter Files
You can download the starter files using this link.
Creating a Project
The IDE setup tutorials for Visual Studio and XCode include a video about how to set up a project using the starter files. You can access the tutorials here:
Once you create a project, you’ll need to import rps.cpp
, test.cpp
,
and start.cpp
.
Xcode
-
There are a couple of ways to import a file in Xcode. The easiest way, perhaps, is to drag and drop that file next to
main.cpp
in the Navigator area on the left side of the Xcode window.Check the checkbox next to Destination: Copy items if needed, make sure that the checkbox next to Add to targets is checked and click Finish.
-
Alternatively, you can choose File > Add Files to… in the menu bar, navigate to your file, select it and click Add (or press Option-Command-A). Make sure that the checkbox next to Add to targets is checked when you are adding
.cpp
files. -
Now there is a problem: there are two
main()
functions in your project: one inmain.cpp
that was automatically inserted by Xcode when you created a new project and another one written by the staff instart.cpp
that you’ve just imported. Recall that a C++ program must have one and only onemain()
function, so your code will not compile. But not to worry! Just deletemain.cpp
that was automatically created by Xcode by right-clicking (or Control-clicking) onmain.cpp
in the Navigator area on the left side of the Xcode window and choose Delete.
Visual Studio
-
To add a new file to your Visual Studio project, right-click on Source Files in the Solution Explorer and choose Add > Existing Item… (or press Shift-Alt-A).
Navigate to
rps.cpp
, select it and click Add. -
You should now see the contents of
rps.cpp
in the Code pane (editing area).
IMPORTANT: Repeat the process to add
test.cpp
andstart.cpp
to your project.
NOTE: Be sure to add your name, your uniqname and a small description of the program to the header comments at the top of
rps.cpp
andtest.cpp
.
Submission and Grading
Submit your code to https://autograder.io/web/project/2682. You receive 4 submits each day and your best overall submission counts as your score. You will submit two files, which must be called rps.cpp
and test.cpp
.
This project must be completed individually, i.e., no partners.
Here is a grade breakdown:
- 60 points: correctness. Implement functions in
rps.cpp
and play the game of rock, paper, scissors. To what extent does your code implement the features required by our specification? To what extent is your code consistent with our specifications and free of bugs? - 10 points: testing. To what extent is your code tested? Implement the testing functions and submit them via the
test.cpp
file. See the Testing section for more details. - 10 points: style. To what extent is your code written well? To what extent is your code readable? We will only look at your
rps.cpp
when determining your style grade. Consult EECS 183 Style Guide and check the Style Checklist at the end of this project’s specification for some tips!
The deadline is Friday, September 27 at 11:59PM Eastern. If your last submission is on Wednesday, September 25 by 11:59PM, you will receive a 5% bonus. If your last submission is on Thursday, September 26 by 11:59PM, you will receive a 2.5% bonus.
You have 3 late days that you can use any time during the semester for projects. There are 3 late days total, not 3 per project. To use a late day, submit to the autograder after the deadline. It will prompt you about using one of your late day tokens. There are more details about late days in the syllabus.
Remember that we grade your BEST submission for style. If multiple submissions are tied in score, we take the last of those.
WARNING: Beware the autograder’s ability to detect cheating! See Collaboration Policy for further information.
Understanding the Distribution Code
- rps.cpp: Starter code for the application you will write in this project. Holds the definitions of required functions and the implementations of a couple functions. We have stubbed all required functions for you.
- test.cpp: Testing functions for your rps.cpp implementation. Holds the declarations of required testing functions. We have stubbed all required functions for you.
- start.cpp: This file contains the main() function for your program, which allows you to run either the rps application or your test suite. You do not need to modify this file or submit it to the autograder.
Stubbing functions means adding the minimal necessary code to make a function compile. For example, some of the functions in rps.cpp
have return types of bool
. We have added return false
in those functions so that they will compile even if you have not implemented all the functions yet. Be sure to remove our return statements when you write your own implementation of the function.
Testing Your Setup
Once you have created a project, you should be able to compile and run the distribution code. We have included a main()
function in start.cpp
which will allow you to run either your tests or the rps application.
------------------------------- EECS 183 Project 2 Menu Options ------------------------------- 1) Execute testing functions in test.cpp 2) Execute rps() function to play game Choice --> 1 Executing your test cases Now testing function isMoveGood() 'r': Expected: 1, Actual: 1 'q': Expected: 0, Actual: 1 ...
The second test case currently fails because the isMoveGood()
function is not fully implemented yet.
How to get help
Most students in EECS 183 need help from staff and faculty multiple times each project. We’re here for you! Many more people need help with Project 2 than with Project 1.
If your question is about the specification or about something about the project in general, Piazza is the fastest place to get help.
Office Hours: All office hours and instructions for signing up/attending can be found under the Office Hours tab on the course website.
Individual office hours: You can get 1-1 help over a video call by signing up for office hours at eecsoh.org. You can find instructions here.
Collaboration Policy and the Honor Code
All students in the class are presumed to be decent and honorable, and all students in the class are bound by the College of Engineering Honor Code.
We want students to learn from and with each other, and we encourage you to collaborate. We also want to encourage you to reach out and get help when you need it.
You are encouraged to:
- Give or receive help in understanding course concepts covered in lecture or lab.
- Practice and study with other students to prepare for assessments or exams.
- Consult with other students to better understand project specifications.
- Discuss general design principles or ideas as they relate to projects.
- Help others understand compiler errors or how to debug parts of their code.
To clarify the last item, you are permitted to look at another student’s code to help them understand what is going on with their code. You are not allowed to tell them what to write for their code, and you are not allowed to copy their work to use in your own solution. If you are at all unsure whether your collaboration is allowed, please contact the course staff via the admin form before you do anything. We will help you determine if what you’re thinking of doing is in the spirit of collaboration for EECS 183.
The following are considered Honor Code violations:
- Submitting others’ work as your own.
- Copying or deriving portions of your code from others’ solutions.
- Collaborating to write your code so that your solutions are identifiably similar.
- Sharing your code with others to use as a resource when writing their code.
- Receiving help from others to write your code.
- Sharing test cases with others if they are turned in as part of your solution.
- Sharing your code in any way, including making it publicly available in any form (e.g. a public GitHub repository or personal website).
The full collaboration policy can be found in the syllabus.
We run every submission against every other submission and determine similarities. All projects that are “too similar” are forwarded to the Engineering Honor Council. This happened to numerous students last semester. Also know that it takes months to get a resolution from the Honor Council. Discussing the project with other students will NOT be an issue. Sharing code between students, even if it’s just one function, will likely cause the cheating detector to identify both programs as “too similar”. We also search the web for solutions that may be posted online and add these into the mix of those checked for similarities. Searching the web, by the way, is something that we are very good at.
Any violation of the honor policies appropriate to each piece of course work will be reported to the Honor Council, and if guilt is established, penalties may be imposed by the Honor Council and Faculty Committee on Discipline. Such penalties can include, but are not limited to, letter grade deductions or expulsion from the University.
Also note that on all cases forwarded to the Engineering Honor Council the LSA Dean of Academic Affairs is also notified. Furthermore, the LSA rule is students involved in honor violations cannot withdraw from nor drop the course.
Problem Statement
Your task for this project is to create a program for playing a game of rock-paper-scissors between two players. The game will consist of exactly three rounds of rock-paper-scissors. A player will need to win more of the three rounds than their opponent to win the game. The game will result in a tie if no player wins more rounds than the other player. For example: if Player 1 wins the first round while rounds 2 and 3 both result in a draw, Player 1 would win the game since they have won more rounds than Player 2.
IMPORTANT: So that we can automate the testing of your code, the output messages for the rock-paper-scissors game must be exactly the same as presented in this specification and the function RMEs. Making sure they are exact is critical to passing the autograder. Check the Sample Output below for the exact prompts. To avoid spelling errors, simply copy/paste the appropriate prompt into your code. Note that a spelling error will cause you to fail almost every test case from the autograder. Use of a diffchecker can easily catch such errors.
Here is an example of what the execution of your final application will look like:
---------------------------------------- EECS 183 Rock-Paper-Scissors ---------------------------------------- Player 1, enter your name: Rana Makki Player 2, enter your name: David Cao Menu Options ------------ 1) Play rock, paper, scissors 2) Play rock, paper, scissors, lizard, spock 3) Quit Choice --> 1 Rana Makki, enter your move: r David Cao, enter your move: p David Cao wins the round! Rana Makki, enter your move: r David Cao, enter your move: r This round is a draw! Rana Makki, enter your move: p David Cao, enter your move: s David Cao wins the round! Congratulations David Cao! You won EECS 183 Rock-Paper-Scissors! Menu Options ------------ 1) Play rock, paper, scissors 2) Play rock, paper, scissors, lizard, spock 3) Quit Choice --> 3 ---------------------------------------- Thanks for playing Rock-Paper-Scissors! ----------------------------------------
Development Cycle with Functions
In Project 1, you had to divide the program into pieces so you could test each part individually. In Project 2 and later, the program is already divided into functions in the starter code which you can use as the parts to work on. You should write, test, and debug one function at a time. See Lecture 5 for a recap of how to implement the iterative development cycle for functions.
The functions in a program call each other, and it is easiest to start with the functions that do not call any other functions. For example, in this project, getMove()
will call isMoveGood()
, so it makes sense to complete isMoveGood()
before getMove()
. We will be able to test isMoveGood()
before we write the code that actually uses it in our program.
The order you write the functions will be different than the order they will appear in rps.cpp. We provide a suggested order for writing your functions in the Suggested Timeline.
Solution Overview
- Your program will provide a menu, implemented using a loop, to obtain user input and play rock-paper-scissors games.
NOTE: Even though we are going over what happens in
rps()
early in the specification, this does NOT mean you should begin your implementation withrps()
. Actually, you should writerps()
last, after you have implemented all of the other functions, sincerps()
will work by calling all of the other functions.
- To begin, your program will call
printInitialHeader()
, which will print this heading:
----------------------------------------
EECS 183
Rock-Paper-Scissors
----------------------------------------
- Next, your program will obtain the two player names, which will not change for the duration of the program. First, Player 1 will be asked to input their name:
----------------------------------------
EECS 183
Rock-Paper-Scissors
----------------------------------------
Player 1, enter your name:
Once Player 1’s name is entered, the program will prompt Player 2 for their name:
----------------------------------------
EECS 183
Rock-Paper-Scissors
----------------------------------------
Player 1, enter your name: Rana Makki
Player 2, enter your name:
Note that “Rana Makki” in the sample output above is user-entered text, and therefore is not included in the output prompt. Also note that player names can contain whitespace.
- Next, your program will call
getMenuChoice()
, which will print the menu (by calling another function that does this) and will obtain user input for menu selection. -
The menu options are:
Menu Options ------------ 1) Play rock, paper, scissors 2) Play rock, paper, scissors, lizard, spock 3) Quit Choice -->
- Depending upon the value the user enters for “Choice”, the program will either initiate a game of rock-paper-scissors, initiate a game of rock-paper-scissors-lizard-spock, or quit.
- Your program shall continue to play games and prompt the user for another choice until a user input of ‘Quit’ (user choice 3) is entered.
-
When the user input indicates that they’ve had enough and want out of EECS 183 Rock-Paper-Scissors, your program will call
printCloser()
, which will print this heading:---------------------------------------- Thanks for playing Rock-Paper-Scissors! ----------------------------------------
and the program naturally finishes.
- There are many functions that assist in making all of this happen. Below is a brief overview of the functions you will use in this project. For in-depth descriptions of the functions’ behavior, please read the RME’s within rps.cpp.
IMPORTANT: For the functions you will implement, write their definitions below
rps()
, as indicated in rps.cpp.
printInitialHeader()
- This function has been implemented for you.
printMenu()
- This function has been implemented for you.
printErrorMessage()
- This function has been implemented for you.
printCloser()
- This function has been implemented for you.
getName()
- This function prompts a player for their name and handles the input accordingly.
-
This function utilizes the following function(s):
printErrorMessage()
getMenuChoice()
- This function will handle printing the menu and reading in the user’s menu choice from input.
- Make sure to print an extra newline right after every call to cin. This is so it can behave correctly on the autograder.
- This function utilizes the following function(s):
printMenu()
- Invalid input: You cannot depend upon users to get the input
within range. You also need to handle out of range input. If the
user enters a menu option other than
1
,2
, or3
you need to- print
Invalid menu choice
- re-print the menu, and
- get another menu choice
- print
- This needs to be repeated until a valid menu choice is entered.
- BE CAREFUL: this function’s purpose is solely to get a single menu choice from the user and return this choice. This function should not be for playing the actual game of rock-paper-scissors and should not call additional functions beyond
printMenu()
.
isMoveGood()
- This function determines whether or not a player’s move is valid. A valid move consists of an ‘r’, ‘p’ or ‘s’ character, corresponding to “rock”, “paper”, and “scissors”, respectively. The uppercase versions of these characters are also considered valid. Any other characters a user enters will be considered invalid.
IMPORTANT: Note that for this project you can assume user input will always be of the data type expected (which is
char
for moves). In other words, as the programmer, you only need to check that values entered are valid moves - you do not need to worry about users enteringintegers
,doubles
,strings
,bools
or any combination of these. Those are bad input and prohibited by the REQUIRES clause.
- This function utilizes no other functions.
getMove()
- This function prompts a player to enter their move during a round, and handles the input accordingly.
- Make sure to print an extra newline before the function returns. This is so it can behave correctly on the autograder.
- This function utilizes the following function(s):
printErrorMessage()
isMoveGood()
There are two things that could happen depending on the character that the user inputs for their move:
- If valid move: The function returns the move.
David Cao, enter your move: p
- If invalid move: The program will print an error message as shown below, and the function will assign (and return) a “default” move for the user that entered invalid input. Note that the default move is ‘r’.
David Cao, enter your move: a
ERROR: Illegal move given, using default
isRoundWinner()
- This function determines if the player made a winning move. Note: a move that results in a tie is not a winning move.
- This function utilizes no other functions.
announceRoundWinner()
- This function announces the name of the round winner. If there is no round winner, it outputs that the round resulted in a draw.
- This function utilizes no other functions.
doRound()
- This function will simulate one round of the game.
- This function utilizes the following function(s):
getMove()
isRoundWinner()
announceWinner()
- This function announces the name of the game winner. If there is no winner of the game, it outputs that there was no winner.
- This function utilizes no other functions.
doGame()
- This is the function that plays three rounds of rock-paper-scissors, announces round winners, and keeps track of the number of rounds each player has won. A player receives a point if they win a round. If the round is a draw, no player receives a point. The full game will always consist of three rounds. This means that even if the same player wins the first two rounds, the third round will always be played.
- Hint: Make use of the
MAX_ROUNDS
constant defined and initialized at the top of rps.cpp when implementing this function. - This function utilizes the following function(s):
doRound()
announceRoundWinner()
-
For the base project, if the game_type provided to this function is 2 (indicating that the user selected rock-paper-scissors-lizard-spock), the only thing this function needs to do is return an empty string after printing the following message:
Under Construction
Only the S’more version needs to handle a game_type of 2 and implement rock-paper-scissors-lizard-spock.
Putting it Together: Writing rps()
- Once you have written and tested each of the above functions, it is
time to combine everything in
rps()
and do further testing with your new debugging skills. Be sure that your program behaves as illustrated in the Sample Output. - Now that you have a new project created and
rps.cpp
in front of you, it’s time to begin! The game is managed in therps()
function — but do not write code there before you have thought about the problem and implemented all of the other functions. -
You can find some starter pseudocode for implementing the rock-paper-scissors game below to help get you started:
/* * Starter algorithm pseudocode: * 1. Print the header * 2. Get player 1 name * 3. Get player 2 name * 4. Repeat the following until the user quits the program: * * Get the menu choice * * Play the game and announce the winner, * or quit the program (according to user's menu choice) * 5. Print the closing message */
- In programming, when you start by outlining the problem at hand, add in one small piece at a time, and test as you go, implementing a solution is much more manageable.
Function Table
The table below provides an outline of which other functions each function should call, if any. You should not be using other functions if not specified.
Function | Other functions it should call |
---|---|
getName() |
printErrorMessage() |
getMenuChoice() |
printMenu() |
isMoveGood() |
Does not utilize any other functions |
getMove() |
printErrorMessage() , isMoveGood() |
isRoundWinner() |
Does not utilize any other functions |
announceRoundWinner() |
Does not utilize any other functions |
doRound() |
getMove() , isRoundWinner() |
announceWinner() |
Does not utilize any other functions |
doGame() |
doRound() , announceRoundWinner() |
rps() |
printInitialHeader() , getName() , getMenuChoice() , doGame() , announceWinner() , printCloser() |
Here is an example of how to read the table:
getName()
should callprintErrorMessage()
Suggested Timeline
Note: It’s common to not completely understand the project after reading through the spec. This is okay! You can start working on the project without fully understanding the spec. In fact, working on the project will likely help you better understand the spec.
As an approximate timeline, you will be on track if by:
- September 16: Starter code downloaded and new project set up in IDE. Starter code submitted to autograder. You’ve read through the spec.
- September 18:
isMoveGood()
,getMove()
,isRoundWinner()
,announceRoundWinner()
, andannounceWinner()
implemented, fully tested, and passing autograder. - September 20:
getName()
,getMenuChoice()
,doRound()
implemented, fully tested, and passing autograder. - September 22:
doGame()
implemented, fully tested, and passing autograder. Start work onrps()
. - September 23:
rps()
code written, but may have bugs. Debugging in progress, passing all individual function tests, 80% or higher on autograder - September 25: Make last submission to autograder for 5% extra credit
- September 26: Make last submission to autograder for 2.5% extra credit
- September 27: Project due date.
Running the Program
Once you have created a project, you should be able to compile and run
the distribution code. We have included a main()
function in
start.cpp
which will allow you to select exectuing your test cases or
executing the game Rock Paper Scissors. When executing your project, you
should see the following message:
-
Enter 1 to select exectuing your test cases starting with the
startTests()
function intest.cpp
, enter 2 to select executing your Rock Paper Scissors game starting with therps()
function inrps.cpp
(red text represents your input)Select 1 for test cases
------------------------------- EECS 183 Project 2 Menu Options ------------------------------- 1) Execute testing functions in test.cpp 2) Execute rps() function to play game Choice --> 1 Executing your test cases Now testing function isMoveGood() 'r': Expected: 1, Actual: 1 'q': Expected: 0, Actual: 1
Testing
- As part of this project, you will also submit a test suite for
testing some of the functions you implement in
rps.cpp
. It is important to note that you will only be testing your program for invalid input rather than bad input. For the purposes of this document, “bad input” will refer to input whose type (char
,int
,double
, etc.) is different than what is expected by your program. So if your program is expecting that the user will input an integer — you are guaranteed to always receive input of typeint
and will not receive something likechar
, which would be considered bad input. On the other hand, you will be testing for “invalid input,” which has the correct type but is considered invalid given the program specification. The RMEs will be clear about what is considered to be invalid input for a specific function. - To help you with writing your tests, you can take advantage of
test.cpp
and the autograder. You will write your test functions intest.cpp
and submit it to the autograder. (See How to Submit section.) The autograder will run your test suite against buggy programs in order to see if your tests can expose enough bugs. The nature of the bugs will be hidden from you so you will have to think about how invalid input may affect your program and cause it to produce incorrect behavior given specifications. - When thinking about writing tests, it is helpful to examine the RME for the functions and figure out what the function is actually supposed to do. Then, write tests that target each step in that process for potential bugs. One question you might have is: how do I know that I have enough
tests? In practice, this question is usually not helpful and isn’t the correct way to think about writing tests. ?This is because a common pitfall for students is to write a lot of tests that all fundamentally test the same functionality of a function. This isn’t useful when trying to expose bugs in other parts of a function’s implementation, and so in this case more tests are not necessarily better.
- For example, if you are trying to test a function that divides two integers and returns the result, you could write an infinite number of test cases that divide positive integers by positive integers and never expose a bug with negative integers or dividing by zero.
-
In the recent lab, you will write test cases for some of the functions in this project. The test cases you come up with for lab are meant to help you with this project and you are meant to submit them along with your test cases for the remaining functions to the autograder. It is not an Honor Code violation to submit tests that you created with in your group for the lab. It is expected that you will reuse these test cases.
NOTE: Remember, you only have to think about exposing bugs related to invalid input and not bad input.
Testing Overview
-
It is considered good practice to write the test suite BEFORE you implement a function. The idea is to know if you are correct or not without using a submit to the autograder. This is also a very good way to cut coding time by a significant amount.
NOTE: The basic idea of testing is you start small and build. Start with the obvious inputs needed then expand to the boundary conditions, and then expand further into the what else category.
-
Remember, a computer does not interpret. If you misspell a word, you will fail all autograder test cases. If you omit punctuation, you will fail all autograder test cases. We strongly suggest you test your code. Use
diff
tools to compare the Sample Output against the output generated by your code using the same input. Some easy-to-use diff websites that we recommend are:
Testing getName() and getMenuChoice()
- To test
getName()
andgetMenuChoice()
, you do NOT need any of the other functions implemented. You should test these functions thoroughly now, so if something goes wrong with your code in the future you will know it is NOT these functions. This method of testing will save you mega-time. -
To test
getName()
andgetMenuChoice()
, you need to check them against good input and invalid input. Therefore, to test these functions, call them withinstartTests()
and make sure you get the output and action you expect.void test_getName(); void test_getMenuChoice(); void startTests() { cout << "\nExecuting your test cases\n"; test_isMoveGood(); // TODO: call more test functions here test_getName(); test_getMenuChoice(); return; } void test_getName() { cout << "Now testing function getName()\n"; cout << "testing good input" << endl; // a valid input would be Rana cout << getName(1) << endl; cout << getName(2) << endl; cout << "testing invalid input" << endl; // an invalid input would be just pressing enter cout << getName(1) << endl; // more test cases... } void test_getMenuChoice() { cout << "Now testing function getMenuChoice()\n"; cout << "testing good input" << endl; // good inputs would be 1, 2, or 3 cout << getMenuChoice() << endl; cout << getMenuChoice() << endl; cout << getMenuChoice() << endl; cout << "testing invalid input" << endl; // invalid input would be other numbers, like 5 or -1 cout << getMenuChoice() << endl; // more test cases... }
- For “good input” in
getName()
, you want to check valid player names (valid names have at least one character). For “good input” ingetMenuChoice()
, you should try the valid menu choices1
,2
, and3
. Run your code. Make absolutely sure that the functions output the values you expect for the different inputs you provide. - Now that we have laid out the testing approach for
getName()
andgetMenuChoice()
, take the concepts and apply them to the other functions. You can do this. Just follow the same line of thinking.
Implementing Test Functions for Submission
- The file
test.cpp
includes function stubs fortest_isMoveGood()
andtest_isRoundWinner()
. Your task is to implement these functions, which should callisMoveGood()
andisRoundWinner()
, respectively, to test their behavior. - The test functions you write should reveal incorrect implementations
of
isMoveGood()
andisRoundWinner()
. Your job is to create a thorough test suite, calling the RPS functions with a variety of valid and invalid inputs (not bad inputs, as noted above). -
Keep in mind that you should only test values that adhere to the Requires clauses of functions. For example, when testing
isRoundWinner()
, note thatbool isRoundWinner(char move, char opponent_move)
“Requires” that “both move and opponent_move are valid moves”.What this means is you can — and should — test:
// because 'r' and 's' are valid moves cout << "'r' and 's' Expected: 1, Actual: " << isRoundWinner('r', 's') << endl;
However, a move like ‘x’ is invalid. Therefore, even though it is “legal” within C++ to write
// INVALID test case - this would violate the RME of isRoundWinner() cout << "'r' and 'x' Expected: 1, Actual: " << isRoundWinner('r', 'x') << endl;
It violates the Requires clause and the programmer should not do this. It is the responsibility of the coder to not violate the Requires clauses of the functions they call.
- Note that the functions stubbed in
test.cpp
are the only test functions you will need to submit to the autograder. - However, you should still write tests for
getName()
,getMenuChoice()
, and the functions involved in game play to ensure that your individual functions behave as expected before writing themain()
function for your program. You can call these test functions inmain()
and remove them before submission. Do not include these functions when you submit to the autograder. -
When you submit
test.cpp
, we will compile and run it with our correct implementation ofrps.cpp
and with our buggy implementation ofrps.cpp
, so as to generate two different outputs. We’ll then compare two outputs. If there is any difference, you’ve successfully exposed a bug! The autograder does not go into the details of what the difference is, it only sees if there exists a difference. -
Remember that some functions don’t print anything on their own; we have to print their return value, as with the function
isMoveGood()
:cout << "'r': Expected: 1, Actual: " << isMoveGood('r') << endl; cout << "'q': Expected: 0, Actual: " << isMoveGood('q') << endl;
(Keep in mind that
bool
values are printed as0
or1
.) -
After you submit your test suite, you might see output that looks like this:
That means that your test suite exposed 1 out of 8 bugs in the staff’s “buggy” implementations of
rps.cpp
and your score for the test suite is 1.5 out of 10 points.
Bugs To Expose
There are a total of 8 unique bugs to find in our implementations. Your tests do not need to expose all of the bugs to receive full points for this part of the project. The autograder will tell you the names of the bugs that you have exposed, from the following set:
- CHECK_ISMOVEGOOD_1
- CHECK_ISMOVEGOOD_2
- CHECK_ANNOUNCEROUNDWINNER_1
- CHECK_ANNOUNCEROUNDWINNER_2
- CHECK_ANNOUNCEWINNER_1
- CHECK_ANNOUNCEWINNER_2
- CHECK_ISROUNDWINNER_1
- CHECK_ISROUNDWINNER_2
Some general hints that apply to all of the bugs to catch:
- Something has to be printed for a test to expose the bug.
- If the return type for a function is anything other than
void
, you need to print the value returned by the call to the function. - If the return type for a function is
void
, you cannot print the return value since there is no value returned.
Preparing your test.cpp for Submission
-
You will write all your tests and execute them from
startTests()
in yourtest.cpp
file. This will include:-
The function bodies the test functions (e.g.,
test_isMoveGood()
andtest_isRoundWinner()
). -
The code you wrote in
startTests()
to call the test functions.
-
-
At any time you can submit your
test.cpp
to the autograder (How to Submit) to see how many bugs your tests exposed. However, remember you only have four submissions each day with feedback.
How to Submit
- Write your program using Visual Studio or Xcode as described in lecture and discussion section. Your program must be written and documented to comply with the Style Guidelines provided for EECS 183.
-
You must submit both
rps.cpp
andtest.cpp
for grading.If you’re using Xcode and don’t know where exactly
rps.cpp
is located on your computer, right-click (or click while holding down the Command key) on the file in the Navigator area on the left side of the Xcode window and choose Show in Finder.If you’re using Visual Studio and would like to know where
rps.cpp
is on your computer, right-click onrps.cpp
in the tab above the Code pane and choose Open Containing Folder. - When ready to submit to the Autograder:
- To submit both
rps.cpp
andtest.cpp
, visit https://autograder.io/web/project/2682.
- To submit both
- On the new page, click Choose file and navigate to your file.
The files you submit to the autograder MUST be called
rps.cpp
andtest.cpp
. - If confident that you’ve selected the correct file, click Submit to submit your code to the Autograder.
- For this project:
- You have 4 submissions with feedback per day.
- You also have one additional “wildcard” submission with feedback. This can be used once for an additional submit with feedback.
- We provide some information regarding the tests that you do not pass. However, it is our intent that you learn to develop code independently. It’s important to have tests to tell you whether or not your code works, but real life does not have an autograder. Providing complete feedback from our own tests will hurt your ability to learn to test.
- You technically have an infinite number of submits per day, but only the first four submits per day return autograder feedback and a score. Any additional submits are “blind submits,” which do not return autograder feedback — not even a score. Be very cautious with these or you may be surprised by your grade.
- Remember: your best submit is the one we grade. We do not accept submissions via email.
- You will soon develop a “love/hate” relationship with the autograder. You will “love” that you can check your code to see your score. You will “love” that it gives you feedback. You will “hate” that it tells you that you are not totally correct on the first submit.
Where to Begin
- The best approach is always to start small and build your solution up one piece at a time. Never try and do the entire thing at once.
- First, start with writing out some logic on a piece of paper. What needs to happen first?
- Implement one function at a time, thinking about what this function needs as input, what it should return, and how it should be used.
- We suggest starting with pseudocode, remember Lecture 01. Pseudocode is a way to explain what needs to happen in English before having to figure out how to do it in code. It is a way to lay out what is needed logically without the overhead of C++ code.
- Only after writing out the pseudocode of what is described above is it time to then translate it to C++. This will save you hours when you start coding, trust us!
- If your code is not working as intended checkout the troubleshooting checklist
Style Checklist
To maximize your style points, be sure to follow this non-exhaustive checklist:
-
Review the EECS 183 Style Guide. Pay particular attention to Comments, Whitespace and Indentation and Variables and Constants.
- Be sure you’ve included your name, your uniqname and a small description of the program in the header comments.
- Be sure to use descriptive variable names. The more descriptive the variable names, the less you need to comment.
- Be sure to use consistent variable naming style. Use either
camelCase
orsnake_case
, but do not alternate between them. Do not change the case of the variable and function names provided. - Be sure that your code is well-commented. It might at first seem that the algorithm is straight-forward and self-explanatory, but would you remember all the details a month from now? Pro Tip: It’s much easier for the staff to help you with your code if you have comments!
- Remember to avoid magic numbers.
- Don’t use global variables. However, global constants are OK and should be used.
- Remember that all lines must be 80 characters or less.
- Check out the Style Rubric to avoid any deductions. This is the rubric that will be used for style grading.
Sample Output
Here are a few examples of the way your program output should look, wherein text in red represents some user’s input.
NOTE: The following sample runs do not include the menu selection detailed in Running the Program.
Sample Run 1
---------------------------------------- EECS 183 Rock-Paper-Scissors ---------------------------------------- Player 1, enter your name: Rana Makki Player 2, enter your name: David Cao Menu Options ------------ 1) Play rock, paper, scissors 2) Play rock, paper, scissors, lizard, spock 3) Quit Choice --> 1 Rana Makki, enter your move: r David Cao, enter your move: p David Cao wins the round! Rana Makki, enter your move: r David Cao, enter your move: r This round is a draw! Rana Makki, enter your move: p David Cao, enter your move: s David Cao wins the round! Congratulations David Cao! You won EECS 183 Rock-Paper-Scissors! Menu Options ------------ 1) Play rock, paper, scissors 2) Play rock, paper, scissors, lizard, spock 3) Quit Choice --> 3 ---------------------------------------- Thanks for playing Rock-Paper-Scissors! ----------------------------------------
Sample Run 2
---------------------------------------- EECS 183 Rock-Paper-Scissors ---------------------------------------- Player 1, enter your name: Rana Makki Player 2, enter your name: David Cao Menu Options ------------ 1) Play rock, paper, scissors 2) Play rock, paper, scissors, lizard, spock 3) Quit Choice --> 4 Invalid menu choice Menu Options ------------ 1) Play rock, paper, scissors 2) Play rock, paper, scissors, lizard, spock 3) Quit Choice --> 2 Under Construction No winner! Menu Options ------------ 1) Play rock, paper, scissors 2) Play rock, paper, scissors, lizard, spock 3) Quit Choice --> 3 ---------------------------------------- Thanks for playing Rock-Paper-Scissors! ----------------------------------------
Sample Run 3
---------------------------------------- EECS 183 Rock-Paper-Scissors ---------------------------------------- Player 1, enter your name: ERROR: Illegal name given, using default Player 2, enter your name: ERROR: Illegal name given, using default Menu Options ------------ 1) Play rock, paper, scissors 2) Play rock, paper, scissors, lizard, spock 3) Quit Choice --> 1 Rocky, enter your move: R Creed, enter your move: p Creed wins the round! Rocky, enter your move: s Creed, enter your move: S This round is a draw! Rocky, enter your move: S Creed, enter your move: P Rocky wins the round! No winner! Menu Options ------------ 1) Play rock, paper, scissors 2) Play rock, paper, scissors, lizard, spock 3) Quit Choice --> 1 Rocky, enter your move: S Creed, enter your move: p Rocky wins the round! Rocky, enter your move: r Creed, enter your move: A ERROR: Illegal move given, using default This round is a draw! Rocky, enter your move: S Creed, enter your move: P Rocky wins the round! Congratulations Rocky! You won EECS 183 Rock-Paper-Scissors! Menu Options ------------ 1) Play rock, paper, scissors 2) Play rock, paper, scissors, lizard, spock 3) Quit Choice --> 3 ---------------------------------------- Thanks for playing Rock-Paper-Scissors! ----------------------------------------
S’More Functionality
OPTIONAL: For more practice and fun, check out the S’More for Project 2. With this optional challenge you can update your project to play rock, paper, scissors, lizard, spock.
Style
Your code must follow the EECS 183 style guide.
Style Rubric
Top Comment
Must have name, uniqname, program name, and project description at the top of the file.
If all or part of the top comment is missing, take 1 point off.
Readability violations
-1 for each of the following:
Indentations
- Not using a consistent number of spaces for each level of code indentation
- This includes using tabs on some lines and spaces on others
- Not indenting lines at all
- Failing to indent the blocks of code inside curly braces
Spacing
- Not putting a space around operators (e.g.,
5*7
instead of5 * 7
orcount=0;
instead ofcount = 0;
)- Includes stream insertion (
<<
) and extraction (>>
) operators
- Includes stream insertion (
- Not putting a space between if, while, or for and the condition to be evaluated
- Putting a space between a function name and the opening parenthesis
Bracing
- Using a mix of Egyptian-style and hanging braces
- Egyptian-style: ‘{‘ at the end of a statement
- Hanging: ‘{‘ on its own line
-
Braces should always be used for conditionals, loops, and functions
- Examples:
// good if (x == 1) { return false; } if (x == 2) { return true; } // bad if (x == 1) return false; if (x == 2) return true;
- Examples:
Variables
- Variable names not meaningful
- Inconsistent variable naming style (
camelCase
vs.snake_case
)- Excluding const variables, which are always
SNAKE_CASE
- Excluding const variables, which are always
- Not declaring const variables as
const
- Not using all uppercase
SNAKE_CASE
forconst
variable names - Using variable types that do not make sense in context
- e.g. using double instead of int for number of people
Line limit
- Going over 80 characters on a line
- Includes lines of comments and lines of code
Statements
- More than one statement on a single line
- A statement ends in a semicolon
- Do not count off for multiple statements as part of a for loop declaration
Comments
-
Commenting on the end of a line of code
// A comment should be placed before a line of code int count = 0; // not on the same line as the code
-
Insufficient comments or excessive comments
- Code should be thoroughly commented such that lines’ functionality is apparent from comments alone or from quickly glancing at code
-
Example of appropriate comment:
// convert cups of flour to bags of flour int bagFlour = ceil((CUPS_FLOUR * numBatches) / CUPS_IN_LB_FLOUR);
-
Example of excessive comments:
// declare variable int bagFlour;
-
Unneeded comments left in the code:
// your code goes here // TODO: implement // this function doesn't work // FIXED
-
Commented out code:
// int numBatches = people / 12; int numBatches = ceil(people / NUM_IN_BATCH);
RMEs
- Missing RMEs for any of the defined functions, except for main. This includes functions from the distribution code and any functions created by the student
- Not having RMEs for tester functions is okay.
- Repeating RMEs for the same function.
Coding quality
-2 for each of the following:
Global variables
- Global variables not declared as const
Magic numbers
- Integer and/or double literals used such that it is unclear to a reasonable reader what the literals represent
- If there is a comment explaining the number/calculation, then it is okay
- 0, 1, and 2 never count as magic numbers
-
Egregious code
- Logic that is clearly too involved or incorrect
-
e.g. instead of basing numbers on conversions, writing:
if (year >= 1700 && year < 1800) { century = 17; } else if (year >= 1800 && year < 1900) { century = 18; }
and so on
-
Function misuse
- Not calling helper functions where appropriate
- Not calling
printMenu()
insidegetMenuChoice()
- Not calling
isMoveGood()
insidegetMove()
- Not calling
- Having tester functions in rps.cpp
bools
- Only deduct 1 point for this category
- Returning
0
and1
instead oftrue
andfalse
for a bool functions