Your goal is pretty simply stated:
Demonstrate the extent of what you've learned in the class by creating the most flexible, feature-rich, readable implementation of the game of Nim that you can.
It's possible to make a basic working game of Nim (where the players have to enforce some of the rules) in 20 lines of C code, using only a few of the tools we've introduced (variable declaration, arithmetic, assignment, printf, scanf, and while loops). In fact, just such code is provided to you as a sample you may start from (see below)
If the documentation and code style (formatting, comments, magic numbers, naming) aren't horrible, that'd be enough for a bare minimum passing grade.
It's also possible to add a huge range of features, taking advantage of all sorts of loops, arrays, structures, pointers, file I/O, string buffers, boolean variables and operations, switch statements, memory allocation, you name it.
If you construct a feature-rich implementation of Nim that puts almost all of the content from the course to good use, including clean style, good documentation, and an informative Git commit history, you'd be looking at an A of some sort.
Here's a link to documentation (minus actual code) for a very feature-rich game of Nim.
Between "almost everything, done well" and "almost nothing, done shoddily", there are many combinations of "more or less things, done more or less well". Having a few features decently done is C-ish, having many features is B-ish. Using about half of our tools is C-ish, using about three-quarters of them is B-ish.
We've played it in class, you can look it up on Wikipedia, and I'll provide starter code with a basic version. The quick idea is that you have some number (by default, 3) of piles of beans (by default, 3 in first pile, 4 in second, 5 in third), and the players (by default, 2 of them) take turns picking a pile and taking some number (by default, between 1 and 3) of beans. Whoever takes the last bean (by the default misère rules) loses.
How can you expand on the basic game? Here are some ideas:
- Enforce the rules of the game (if someone wants to take -2 beans, don't accept that input and force them to enter something valid)
- Reduce weird input glitches (e.g. you scan for a number but a user enters a letter instead)
- Be flexible in your input style (don't just accept "A3", also accept "a3" and "A 3" and "3A")
- Allow users to select different numbers of players
- Store players names and display them on the players turns (rather than just an impersonal number)
- Allow users to select different numbers of piles
- Allow users to select different numbers of beans in each pile
- Allow users to change the number of beans that can be taken
- Allow users to change the rule for who wins/loses
- Allow the above user selections to be saved to file and loaded from file
- Allow a game in progress to be saved to file and loaded from file
- Display the board state graphically (e.g. stacks of *s) rather than textualy (pile A: 1 bean, pile B: 4 beans)
If you've got other ideas, you are welcome to run them by me for a "how hard is that likely to be" sanity check.
Don't know how you'd get started implementing a game? There are a lot of ways to do it.
In a design setting, you'd think about top-down vs bottom-up design choices: whether you try to break the big idea into more manageable medium ideas, and break those into even more manageable smaller ideas, and keep on breaking until you've got a huge number of trivially easy things (top-down) or if you start combining your basic building blocks in useful-seeming ways to create bigger more useful building blocks, and combine those to make even bigger blocks, and keep combining until you have a single block that does exactly what you want (bottom-up).
Whichever way you prefer to tackle the rest of the problem, you'll probably want to begin with this starter code. It compiles, it runs, it lets you play Nim. It also lets you cheat and is horribly formatted, completely undocumented, feature-poor, magic-number ridden, non-robust (bad inputs aren't checked), and unconfigurable. So, while turning it in as-is wouldn't even qualify for a D-, it does gives you an already-working point from which a passing grade is not far off.
Also, starting here gives you a lot of specific next-step goals you could aim for:
Forget bad indentation, forget even no indentation, this thing is all on one line! Which is completely legal and valid C code, but is really, really, really bad style. Your first task should be to put in a bunch of newlines (press enter at the right spots) and fix the indentation. I highly recommend you consider using XCode in Mac110 for this: once you've got the newlines figured out, just do command-A (select all) command-X (cut all) command-V (paste all) and the indentation should be automagically fixed!
As you add new code, remember to keep the formatting up to par.
Even with only one file and one function (main
) this thing has no documentation! Of course, main
does so much that it's a bit hard to give it any brief documentation, and a bunch of things really should be broken out into their own functions. Nonetheless, documentation should be added to this, even in its current benighted state.
As you add new functions, constants, and structs, remember to keep on top of your documentation.
This is not a robust program. There are all sorts of screwy things a user could do that would make the program go in weird directions, crash, or loop. Some of them are:
- Take a negative number of beans
- Take from bean pile 8 (there is no such pile, right?)
- Take more beans than there are in a pile
- Take more than 3 beans
At least some of these should be fixed.
As you add new code, keep in mind stupid/unexpected things a user might do (weird inputs, nonexistent files...) and think about how to make your program handle them gracefully (prompt for better input, explain what went wrong...)
There's a lot of hardcoded literals sprinkled through the code. These should be replaced by named constants.
As you add new code, keep an eye out for values that should have names as opposed to being weird random anonymous numbers (or characters)
Nim is an N-player game with M piles of beans, the smallest pile having K beans and the largest having L beans. It uses the X rules for determining a winner, and allows players to take between Y and Z beans on their turn.
This program is stuck having those values at N=2, M=2, K=3, L=5, X='normal play', Y=1, and Z=3. And if you want to play a different game? You have to rewrite the program. It's certainly not an option for a user to select any of those values at the start of the game.
Once you've figured out all the whitespace, you'll notice everything is lumped into one big main function. This makes it harder to document code well, it makes it harder to add error checking, it makes it harder to be flexible about the rules being used, it makes it harder to add features, and it makes it harder to read and understand.
It's not at all evident that the person who wrote this program knows anything about functions, logical operators, switch statements, for loops, file input/output, nested conditionals, pointers, structures, memory allocation, string handling, arrays, booleans, or the occasional need to import a few more libraries (.h files). It's certainly not evident that they know how to use any of those things well.
If you want to see Nim in action, either in basic form or with a bunch of bells and whistles, compiled executables of both the starter code (for which you have the source) and advanced (for which you have the documentation) are available on Eclipse. To get a copy, type: cp ~/cs131_inclass_files/bin/Nim_advanced ~/cs131
or cp ../cs131_inclass_files/bin/Nim_starter ~/cs131
. To run your copy, type ~/cs131/Nim_advanced
or ~/cs131/Nim_starter
.