- implementing software given a design/specification
- programming algorithms using lists and tuples
- working with unit tests and test-driven developement
- devising your own mathematical model/solution of a problem and implementing it
- refactoring existing designs/specifications
Write a program to play the game of Three Musketeers--human against computer.
You have probably never heard of this game. That's okay, the rules are simple -- that's why we chose it. The link above is to a Wikipedia article that explains the game, and we can answer questions if necessary.
Get the filesthree_musketeers.py
and test_three_musketeers.py
to your computer and push them to the repository that you created and set up (see the
guiding docs) Remember that you should follow the TDD approach in this project. Thus, first insert some initial
tests for every (fruitful) function. Simply go through the file test_three_musketeers.py
and make sure that there is one test everywhere you see # Replace with tests
. You will
add more tests at the later stages of the project.
Go through the file three_musketeers.py
and create initial implementation for each
function. Essentially, you need to create a stub for each function, i.e., a return value that makes
sense but may be incorrect. (For example, where an integer should be returned, return 0, and where
a list should be returned, return [].)
You should not spend much time on the two stages above. You will provide more detailed tests and correct implementation later. The results of your work here should be committed in your repository by the deadline of Session 7.
Gradually increase the number of tests and improve your implementations. You have time until the
project deadline to do that, however, we recommend you to have all the tests and the program
correctly working at most one month before that so that you have sufficient time to work on
strategies and extention with files (if you do not want to skip them). The results of your work
should be stored in the file three_musketeers.py
and the tests in test_three_musketeers.py
.
The computer does not have to play a great game--it does have to play legally. However, if the computer plays stupidly, it won't be much fun. If you would like to implement a simple strategy, here's a start:
- The three Musketeers should try to stay as far away from one another as possible.
- Cardinal Richelieu's men should try to all move in the same direction.
three_musketeers_with_strategies.py
and the (additional) tests in test_three_musketeers_with_strategies.py
.
As you see, the original specification does not include an option for a user to save
the game state and to start playing from it later. Therefore, you can add the functionality to the
system that would allow to ask of a user at any point when its his/her move, whether he/she wants
to save the game to a file and exit. On the other hand, before the beginning of a game, the system
can ask the user whether he/she wants to play from scratch, or load a previous game. You are free
to define yourself how this should work and change any part of the original specification to
achieve your goals. The results of your work
after this stage should be stored in the file three_musketeers_with_files.py
. New tests are
not required for this stage of the project.
We understand that you may be short of time during the term to do all of the above. Therefore, we only require that you complete "Get starter files and put them in a GitHub repository", "Write few initial tests first", "Write initial implementation" and "Add more tests and function implementations in the TDD loop" stages of the project. With them you will get up to 70% of the full project mark. Then, you can chose whether to add "Extend your program with strategies" (adds up to 30%) or "Extend your program with files" (adds up to 30%). Extending your program with either of the latter two features in optional (but required if you want to get more than 70% of the maximal project mark). Of course, you are welcome to do both extentions. In that case, we will provide feedback on both of them but will consider only one of them for marking (the one that is better in our opinion). Therefore, it is better to implement one extension very well than two of them weakly.
Firstly, we use the common criteria for marking code:
- Readability and Clarity (use comments when your code does something non-trivial)
- Completeness of the Implementation and Tests (every required function and feature must be implemented)
- Correctness (everything your implement must return correct and expected results)
- Consistency of commit history in git (you must start working on the project by the deadline of Session 7 and do some commits every week or two; the projects where the first and the last commits are a couple of days before the deadline are unlikely to be given a high mark)
See the Wikipedia article for the rules of the game.
The "board" is represented as a list of five lists; each of these lists
represents a row, and each list contains five elements. The first list
represents the first row; the first element in each list represents the
first column. The values in the list must each be one of three things: an
'M'
, representing a Musketeer, an 'R'
,
representing one of Cardinal Richelieu's men; and a '-'
,
representing an empty space. board
is a
global variable in this program.
Directions are given as one of the four strings 'left'
, 'right'
,
'up
', and 'down'
.
Your job is to complete the program, both three_musketeers.py
and test_three_musketeers.py
.
- In
three_musketeers.py
,- We use a global variable
board
to store the current state of the game, and all the functions have access to it. - For some functions, we indicate that their supplied arguments will not necessarily be in correct value ranges (for the other functions, you can assume that they are in the correct ranges). In this case, you must check for the correctness of the arguments and raise an exception if this is violated. Also, the corresponding test methods must test the calls with the incorrect arguments and verify if the exceptions are raised.
- The
pass
statement does nothing. You should replace everypass
statement with actual code. In the beginning, provide stubs, i.e., return some syntactically correct possibly constant values that do not need to be correct semantically. Further, as you develop your project, you will replace them with the code that works better and better and covers more and more test cases.
- We use a global variable
- In
test_three_musketeers.py
,- Call the function
set_board(new_board)
to get a fresh newboard
before every test. Do that so that the result of one test does not depend on what other tests may have done. (You shoud use at least two initial test board.) - The test functions are not complete (some of them are empty). It is your job to fill them in with reasonable tests. In the beginning, just make sure that there is at least one test in each of the test functions. Further, as you develop the project, increase the number of tests. It is your decision which tests and how many to include, but the tests for each function must reflect the variety of its use cases (including exceptions if we indicated that the function must raise them). I would expect between 5 and 10 tests for each function, depending on how much the function does.
- Call the function
- Try not to work on
three_musketeers.py
andtest_three_musketeers.py
in isolation and follow the TDD approach. After improving your code, increase test cases for it, then improve it further, etc.
Here are some Python things to remember:
- Methods can call other methods. Sometimes this means that a fairly complex job can be accomplished in just a line or two.
- It's easy to get things in and out of tuples. If
location
is a two-tuple, all of the following work:(row, column) = location
location = (row, column)
column = location[1]
- Although you can sometimes leave the parentheses off a tuple, you must have them when calling a function: do_something((3, 4))
- Sequences of the same type can be "added." For example,
[1, 2] + [3, 4]
gives[1, 2, 3, 4]
. append
adds an element to a sequence. For example, ifa = [1, 2]
,a.append(3)
changesa
to[1, 2, 3]
.- If
b
is a list of lists,b[0]
is the first list, andb[0][0]
is the first element in the first list. For example, ifc
is[[1, 2], [3, 4, 5]]
, thenc[1][2]
is5
.
We do not require any documentation from you but you should make sure we understand
what your code does. Therefore, use the comments inside your code. Comment on every piece of
code that does something not trivial. It is sufficient that you submit the files specified above,
however, if you wish, you can supply other (textual) files or overwrite this readme.md
file with any description you consider useful.
You will be working in Git and your last commit before the project submission deadline will be considered as your final submission. If we see that you have commits after the deadline, by default we will mark the last commit you made before the deadline. If you prefer us to mark your work as a late submission instead (possibly providing mitigating circumstances to avoid the cap on the mark), contact us to tell about that.
As with the exercise sheets, for the accountancy reasons, we require that you "back up" your project files on Moodle via an assignment link provided on the project Moodle page. If it is not there yet, it will appear in a couple of days. You simply need to upload the final version of all your files there. You need to do it only once when your project is in the final version and before the submittion deadline.