Your task this week is to implement some of the logic and process the user inputs for a code-breaking game. The codebreaking game is very similar to the board game Mastermind, but played with
numbers from 1-6.
To play the game, the user first enters a “seed” value, which is an integer. This seed value initializes a pseudo-random number generator. Using the pseudo-random number generator, a solution code
sequence of four numbers from 1 to 6 is chosen at “random.” This solution is kept hidden from the player.
The player then has 6 guesses to get the sequence of four numbers correct. Guesses are typed into the terminal. After each guess, the player is given feedback on the number of perfect matches and
misplaced matches, which is printed to the screen.
The values of the guess that appear in the same place in the solution code are called perfect matches. The number of values that appear somewhere in the solution code but in a different place in the
solution code are called misplaced matches. If the player manages to guess the correct sequence in twelve or fewer guesses, they win the game. Otherwise, they lose.
The objective for this week is for you to gain some experience with basic I/O, to implement code using library functions, practice using pointers, and to solve a problem that requires moderately
sophisticated reasoning and control logic. If you want to read ahead and use arrays, feel free to do so, but the code should be fairly manageable without using arrays (we wrote the solution that way,
just to check).
Here’s an example of the output of the game when the solution is 6 4 2 6
Please enter a seed: 12321
Enter your guess (#1 #2 #3 #4): 2 1 2 3
With guess 1, you got 1 perfect matches and 0 misplaced matches.
Enter your guess (#1 #2 #3 #4): 2 1 2 4
With guess 2, you got 1 perfect matches and 1 misplaced matches.
Enter your guess (#1 #2 #3 #4): 6 4 2 6
With guess 3, you got 4 perfect matches and 0 misplaced matches.
You guessed correctly in 3 guesses.
In the text-examples folder, you will find a working version of the game “gold-version”, which you can use to better understand how the game is supposed to work (run as ./gold-version). Three example
input and output files are also given.
This week you will write all your code in prog5.c. We’ve provided some additional files, however, that you may want to look at.
Let’s discuss each of these in a little more detail:
prog5.h – This header file provides function declarations and descriptions of the functions that you must write for this assignment.
prog5.c – The source file for your code. Function headers for all functions are provided to help you get started. The functions are detailed below and will be reviewed in the programming studio on
main.c – This file uses #include to pull in prog5.h, and provides the logic to execute the game.
test.c – This file uses #include to pull in prog5.h, and runs a number of unit tests on each function you will write. See the Testing section
text-examples- This folder contains a working version of the game (gold_version), which you can use to compare your code against. We have provided some test files as well, as described in the
We have also included some additional files for testing, but we leave discussion of those files for the Testing section of this document.
In programming studio, we will discuss pseudo-random number generation and the idea of error-checking user input.
You will note that there are some static variables defined near the top of prog5.c which are static ints. These are integers which are allocated memory space outside of any specific functions in the
Intro- you got 5 points if you had an intro paragraph in prog5.c. If you put the intro somewhere else, we may have missed it and may submit a regrade. If you’re missing 5 points (for example
95/100), check that you have an intro paragraph.
Errors and warnings- If you had a very low score (say 10/100), your code likely had compilation errors or warnings.
Your are provided with test cases and a reference solution in text-examples folder. You can check if your output matches with the reference solution.
Note we will be testing with additional test cases, so you are encouraged to write your own tests.
You are given a Feedback folder on your svn directory. Once your program passes the provided test cases in text-example folder, you may commit your changes, wait for 5 minutes,
and do ‘svn up’ in your Feedback folder. The numbered directory represents your submission revision number. The larger number the newer submission. If there exists .test files, your code
is very likely to contain bugs in it. You may find useful feedback under these folders. The feedback is generation system is now in experimental stage so if you find the message hard to
understand you can post on piazza/go to OH for help.
If there is a gdbsuspiciousInstList.txt under the Feedback/MP5/* folder, the file shows a ranked list of locations of your prog5.c file. The locations toward the head of the file are more likely to be
the cause of your error. You may use gdb to debug your code, and set break points at these locations using the ‘break’ command. NOTE: There is NO guarantee that these lines ARE the cause
of your bug. Use these information only as a reference.
gdb is a debugger for c programs. There are plenty of tutorials online. You are encouraged to learn it.
runtime stack. Therefore, these static variables can be called in any function in prog5.c.
In your code, use the variables solution1, solution2, solution3, and solution4 to track the four numbers of the solution code generated by start_game (see below). The variable guess_number should be
used to track which guess the user is on (starting at 1 and possibly going to 6).
Feel free to change these definitions if necessary.
Pseudo-random Number Generation
For this MP, we need to generate a random solution code to keep the game interesting. We don’t have the ability to generate truly random numbers in C. Instead, we generate something called pseudorandom numbers, which are numbers which look random but are in fact calculated using a mathematical function.
Generating pseudo-random numbers requires an initial value, which we call the “seed”. Given this seed, the number generator will create a sequence of numbers which looks random, but is not truly
Different seed values produce different ‘random’ sequences. If the seed value is the same, it will generate the same sequence. This is important, as it allows us to test our code by setting the seed
value to be the same each time.
We use the following two library calls (http://www.tutorialspoint.com/c_standard_library/c_function_rand.htm and http://www.tutorialspoint.com/c_standard_library/c_function_srand.htm) to produce
sequences of pseudo-random numbers. The function definitions for these are
The function srand accepts an integer as an argument and sets the seed. It returns nothing and generates no random numbers.
The function rand generates a random integer between 0 and the value RAND_MAX and returns it.
Example usage would be:
After execution, the value of x should be 162313199 and the value of y 1042925187.
Random number generation is operating system dependent! Be sure to do your testing on an EWS machine, though you may develop your code on another machine.
Processing User Input
The user will be typing characters into the terminal, for instance “1 2 3 4”. Code is already given in main.c to process this input and store them as a string.
A string is an array of characters typed in by the user. You will use the function sscanf (http://www.cplusplus.com/reference/cstdio/sscanf/) to turn the characters into integers and recognize if there is an
The behavior of sscanf is almost identical to scanf, but with a string as an input. Some example code for processing the inputs will be discussed in the programming studio and is provided in the
comments of the set_seed and make_guess functions.
What You Need to Do
You will implement the functions set_seed, start_game, and make_guess in prog5.c. This will complete the code for the game. You will then need to test your code to make sure it is working as
intended. You should read the descriptions of the functions in the comments of the source file before you begin coding to get a feel for the project.
INPUT: String seed_str (an array of characters, the qualifier const indicates that it cannot be changed). The user has typed characters into the terminal, which is stored by main.c into seed_str before
RETURN: Returns 1 if the user entered exactly one integer in the string, Returns 0 if the user enters no integers, more than one integer, or anything other than an integer.
SIDE EFFECT: If the user enters exactly one integer, set_seed sets the seed value for random number generation using srand, using the user’s integer as an argument to srand. If it is not a valid string
(a 0 will be returned), srand is not called and an error message is printed.
The function receives a string (an array of characters) as its input. This string contains characters typed in by the user. You will check to see if this string contains one integer number. If it does, this
number will be used as the seed for pseudo-random number generation. The “const ” qualifier means that the routine is not allowed to change the contents of the string. You need to check if the string
contains only one integer number, which we will use to seed a random number generator (use the function srand to set the seed).
The return value from the function set_seed indicates whether the input string did in fact correspond to a number. When the string represents a single integer number (and only a number), the
set_seed should call srand (using the integer as an argument), then return 1. Otherwise, the function should not call srand, print “set_seed: invalid seed\n”, and return 0.
You will find the function sscanf helpful here (http://www.cplusplus.com/reference/cstdio/sscanf/) to check the string for a number and for any formatting errors. Check the comments for the function in
prog5.c, an example function call to sscanf is provided for you (sscanf (seed_str, “%d%1s”, &seed, post)). This call reads one integer (and stores it in seed) and one string (and stores it in post) from
the string seed_str. If the user input is correct, one integer is read into seed and nothing is read into post.
To check if the user entered one number, check the return value of sscanf. The return value indicates the number of items were read. If the return value of the function is 0, no items were read, if the
return value was 1 the single integer was read. If the return value is 2 then both the integer and a string were read. The function sscanf should do most of the work of checking if the string is valid or
Examples of valid strings: “123” ” 1 ” ” 12321″. Examples of invalid strings: “a string” “123abc” “123 123”
INPUT: None (one, two, three, and four will be used as outputs).
SIDE EFFECTS: A solution code of four random numbers is generated. The values at the four integer pointers (one, two, three, and four) should be assigned to the new values. The four values of the
solution code should also be saved using the static integers in prog5.c (solution1, etc).
This function is called after set_seed to generate the four random numbers of the solution code.
The start_game function selects the solution code at random, as a set of four numbers from 1 to 6. This is the number your user will try to guess. To ensure consistency between your program’s
output and ours, you must use the algorithm shown below for generating the solution code.
void srand (unsigned int seed);
int rand ();
int x = rand();
int y = rand();
int set_seed (const char seed_str);
void start_game (int* one, int* two, int* three, int* four);
Step 1: Starting with the first value in the solution code sequence, generate a random integer in the range 0 to 5 inclusive using a single call to rand() and a single modulus (%) operator
Step 2: Add 1 to the resulting number to get a value in the range 1 to 6.
Step 3: Repeat steps 1-2 for the other three solution code values (in order)
Step 4: set the static variable guess_number to 1. You must also make your own copy of the solution code using static variables solution1, solution2, solution3, and solution4. These variables
will be necessary when you implement make_guess. Set the values at one, two, three and four.
Be sure not to call srand outside of the set_seed function and not to call rand outside of the start_game function. Calling either of these disrupts the sequence of random numbers and will cause
your output to differ from ours.
INPUT: String guess_str (an array of characters, the qualifier const indicates that it cannot be changed). The user has typed characters into the terminal, which is stored by main.c into guess_str before
RETURN: Returns 1 if the user entered a valid guess (exactly four numbers from 1-6, separated by spaces), Returns 0 if the guess is invalid (user enters no integers, more than four integers, or
anything other than an integer)
SIDE EFFECTS: If the guess is valid, the number of perfect and misplaced matches are calculated and printed, and the guess_number is incremented. If the guess is valid, the values at the pointers
one, two, three, and four should be set to the numbers guessed by the user. If the guess is invalid, it prints an error message and does not increment the guess_number
You must write the make_guess routine that compares a player’s guess with the solution code. This function must compute the number of perfect and misplaced matches. The function must also
check if the guess is valid (four numbers between 1-6) or not. This function is called by main.c every time the user types a guess.
The inputs to this routine is a string guess_str (containing the player’s typed input). Your routine must validate the string in the same way that we did for set_seed. An example call to sscanf is
included in the comments, using a format string of “%d%d%d%d%1s” to read four integers and possible a string “post”. A valid string contains exactly four numbers (and no extra garbage at the end).
All four numbers in the string must be between 1 and 6. Your code must check these cases, using sscanf and other logic as necessary.
If the string is invalid, your routine must print an error message, “make_guess: invalid guess\n”, then return 0. Don’t worry about the values of the integer pointers in this case.
Examples of valid strings: “1 2 3 4″ ” 1 2 3 4″ ” 1 2 3 4 “. Examples of invalid strings: “1” “1 2 3a 4” “1 2 3 4 5” “abc 1 2 3 4” “1 2 3 4 abc”
If the string is valid, you must store a copy of the guessed code in order in the four addresses provided as input parameters (one, two, three, four). Your routine must then compare the guessed code
(the four numbers from the user) with the solution code (which should be stored in static variables, solution1, solution2, solution3, and solution4 done in the start_game function) to count the number of
perfect and misplaced matches, then print a message informing the player of the results, using the format “With guess 1, you got 1 perfect matches and 2 misplaced matches.\n”,
substituting the values you’ve computed. Do not adjust the word “matches” for subject-verb
agreement. After printing, increment the static variable for the guess_number.
To compute the number of perfect and misplaced matches, you can follow this rough outline:
1. Check the guess number against the corresponding solution number to find perfect matches (correct number in the correct location). Do this for all four numbers. Count the number of perfect
matches. If there is a perfect match, mark the guess and solution as “paired” (possibly using another variable).
2. If the first guess number is not paired yet, check it against the second solution number (if it is also not paired). If it matches, count a misplaced match and mark the second solution number as
3. If the second number did not match, repeat Step 2 for the third solution number. If the third solution number does not match, try the fourth.
4. Repeat 2-3 for the second guess number (if it is not already paired), then the third guess number, then the fourth.
Let’s consider some examples. Imagine that the solution code is 1 1 2 3. If the player guesses 1 2 2 4, the first (leftmost) and third code values match perfectly, so they have two perfect matches.
How many misplaced matches does this guess have? Think of the solution and guess code values as being paired with one another. If we pair two values as a perfect match, neither of these values
can be paired with another value. So the 1 in the guess does not produce a misplaced match: the 1 in the guess has already been paired with the first 1 in the solution. Neither does the first (left) 2 in
the guess produce a misplaced match: the 2 in the solution has already been paired with the other 2 in the guess.
So this example has 2 perfect matches and 0 misplaced matches. Perfect matches are always paired before misplaced matches, so there is never any ambiguity. If you are in doubt, use the test
code that we have provided to check your answers.
What happens if (using the same solution code: 1 1 2 3) the user guesses 4 4 1 1? Here they have no perfect matches. How many misplaced matches does the guess code have? Keep the
pairing idea in mind. We can pair either of the 1’s in the guess with the 1’s in the solution, but we have exactly two pairs, and thus we must report exactly two misplaced matches and zero perfect
Your code must be written in C and must be contained in the prog5.c file provide to you – we will not grade files with another name.
You must implement set_seed, start_game, and make_guess correctly.
Don’t change the function definitions. You may write additional functions if you wish.
Your function’s return values and outputs must match the gold version’s exactly for full credit (see the Building and Testing).
Your code must be well-commented. You may use either C-style (/* can span multiple lines */) or C++ style (// comment to end of line) comments, as you prefer. Follow the
commenting style of the code examples provides in class, in the textbook, and on the wiki.
Building and Testing
We suggest that you begin by developing your code on the Linux machines in the lab (remote access is fine). Definitely do your final testing on the EWS machines, or the pseudo-random numbers may
not match. All operations described here should be run in the MP5 folder.
You should test your program thoroughly before handing in your solution. We have provided you a set of tests. You should also get in the habit of writing your own tests, and you may want to think
about how you would modify test.c to further test your program. To compile the program, we compile main.c, type
If successful, the compiler produces an executable called mp5,
which you can execute by typing “./mp5” (no quotes). You can also run the code with the gdb debugger (or a GUI interface that uses it, such as DDD) by typing:
As an example, to set a breakpoint on line 40 in prog5.c once you’ve started gdb, type “break prog5.c:40”. You can start the program in gdb by typing “run”
When developing code with many different components (such as different functions), it is often critical to test the individual parts, or units of your code. In this case, it makes sense to write some code
to test each function independently before running the whole thing. Although we’ve provided some tests for you (in test.c), you should start thinking about writing your own testing code. This is a critical
testing and debugging skill for a programmer to develop.
int make_guess (const char guess_str, int* one, int* two, int* three, int* four);
gcc -g -std=c99 -Wall -Werror main.c prog5.c -o mp5
gcc -g -std=c99 -Wall -Werror test.c prog5.c -o test5
You can run the tests as
./test5 0 (this is for set_seed, test0output)
./test5 1 (this is for start_game, test1output)
./test5 2 (this is for make_guess, test2output)
Remember that these tests contain only a few test cases- they do not 100% guarantee that your code is working when you test the whole thing.
Testing the Whole Thing
The text-examples subdirectory contains a gold version of the game along with some sample input and output files. These are not scripts, but direct input to the program. You’ll want to test these to
make sure you exactly match the expected output.
We will use the linux input ‘<' and output '>‘ redirection commands to get input from a file and write output to a file.
The input file shows exactly what you should type to get the desired output. If you want to produce another copy of output1, you can execute the gold version (from within the text-examples
directory) by typing (be sure to put the < and > in the correct spots):
Please use the following command if your terminal says permission denied.
The gold_output1 file will then match the gold_output1 file provided to you on svn.
You can, of course, run your own program in the same way (from the MP5 directory)
and then use the command line tool diff to compare outputs (diff my_output1 ./text-examples/gold_output1) line by line.
If you see nothing printed after the diff, they match exactly, otherwise you will see the lines that differ printed for you. Check these lines for formatting errors and other bugs. Be sure to eliminate all the
output errors to get full credit.
You can also run the gold program on different inputs, if you are concerned about a particular test case. If you set your random seed correctly, you should not have difficulty getting the gold version to
produce a solution code that you know in advance (the solution is printed at the end of the game).
20% – set_seed function works correctly
20% – start_game function works correctly
40% – make_guess function works correctly
10% – all given outputs match exactly (diff command produces no output for gold_output1, gold_output2, and gold_output3)
5% – code is clear and well-commented, and compilation generates no warnings (note: any warning means 0 points here)
Comments, clarity, and write-up (5%)
5% – introductory paragraph explaining what you did (even if it’s just the required work)
Note that some point categories in the rubric may depend on other categories. If your code does not compile, you may receive a score close to 0 points.
./gold-version < input1 1> gold_output1
chmod +x gold-version
./mp5 < ./text_examples/input1 1> my_output1
diff my_output1 ./text_examples/gold_output1