Our neighbour country has decleared a war agaist us and layed a minefield along the current border. We need to train some fools... *ahem*, pioneers to stomp... err, sweep the field so our forces can advance freely. And because field training is so 90s we want to be able to train our pioneers with a high tech minesweeper simulator. Your commanding officer has told us that you know Python so your task is to make that simulator for us. Your country needs you soldier, are you ready?
The minefield is a two dimensional rectangle shaped grid that contains mines. The player must be able to determine the dimensions of the field and the amount of mines on the field. All tiles on the field are initially unrevealed and the mines are on randomly selected tiles. Other tile types include a number tile, which displays the number of mines surrounding it, and an empty tile.
The player can choose one tile at once by clicking it with the mouse. The chosen tile is opened and its content is shown to the player. Then one of the following happens:
- If the opened tile contains a mine the game is over and the player lost.
- If one or more of the tiles next to (i.e. horizontally, vertically or diagonally) the opened tile contains a mine, the number of the mines in the surrounding tiles is shown in the opened tile.
- If there aren't any mines in any of the tiles next to the opened tile (i.e. it is an empty tile), all the surrounding tiles are opened and all the tiles next to those tiles are opened and so on until the border of the minefield is reached or the opened tile is next to a mine (i.e. is a number tile). The tiles next to mines are also opened but not the tiles beyond them, regardless of their contents.
The game can end two different ways:
- If the opened tile contains a mine the player lost.
- If all mineless tiles on the minefield are opened, the player won.
You might want to do a little more research on the said game before you rush into making one yourself. For example: playing it for yourself certainly wouldn't hurt, Google could help you with that. The following instructions may also contain some important information regarding how you can pass this course, so keep on reading.
In order for your project to be accepted, it needs to show the understanding of the couse contents. Below is a checklist you can use to determine whether you've fulfilled the requirements.
- The game correctly implements the minesweeper rules described above.
- The game works with user-selected dimensions - make sure to test rectangles that are not squares (i.e. it should work even when length and width are not equal)
- The game situation is shown to the player with a clear graphical representation
- The player can make moves with the mouse; the canonicality of the moves is checked and illegal moves are prevented
- The game ends when it should (review the "Ending" section)
- The game keeps a record of the played games: when the game was played (the date and time), the duration (in minutes and turns) and the outcome (was the game won or lost and how many mines there were left on the field).
- The game contains a main menu where one can choose to play a new game, quit or look at the statistics - note! the menu can be text-based in the terminal window, only the game window itself needs to use graphics.
Missing or errors in any of these will bounce the course project back to you for improvements.
We've created a small helper library for creating graphics in a game window with more or less the same means as everything else we've done in the course - by calling functions. You don't have to use this library but the game must have graphics, implemented one way or another - the reign of terminal minesweepers is over! The library module has rather extensive docstrings that should provide assistance about its use. Some additional instructions can be found below.
Download the library and graphics zip from above. The images should be placed into a folder called sprites inside your project folder - it's a zipped folder so don't create another sprites folder when extracting it. You need to also install Pyglet because our small library is built on top of it. Pyglet is a very neat library in general for making 2D games with Python. If you want, you can also download the script that was used to generate the graphics in the zip (requires PyCairo).
Game Libraries 101¶
Whereas programs so far have had their own main loop (usually with
while True), game libraries typically hide the main loop under the hood, and it is usually much more complex. It's been hidden so that you don't need to worry about it. When using a library, the game is usually implemented through handler functions that the main loop calls when certain triggers happen. For instance, mouse clicks can have a handler attached - this handler function will then be called whenever the user clicks a mouse button. Another very common handler is a drawing handler to draw the game's graphics, which is called whenever the game screen needs to be updated.
You will not be creating a main loop in your own program. Instead, you will be implementing handler functions. These handlers must be attached to events. This can be done with functions in our library (named like: set_some_handler). The docstring for each of these describes what kind of a handler function is required. The general workflow of using the library is:
- load game graphics
- create game window
- define and register handler functions
- start the game
After this the game will run in the handler functions. There's a very simple example of drawing graphics at the end of the library module. The main program is a small piece of code that draws all sprites in some order on one row inside the window.
The most important fact that needs to be accounted for is that handlers are called by the main loop running out of your reach. This means you cannot cotrol what arguments your functions are given. As a result, the game's state (like the field) must be smuggled to the handlers in some other way. The preferred way to do this is to create a main program level dictionary for the state. A dictionary defined in the main program can be accessed and modified in any and all functions of the program due to its mutable nature.
We like collaboration on this course. These weekly nice little exercises are actually made in cooperation with several different assistants. It's also probably much more pleasant to solve them together. Your friend may have a better understanding about something, and two pairs of eyes spot bugs more easily anyway. In most cases you realize your mistake when you start to complain to someone that your program doesn't work. Usually things work more smoothly in your head, compared to what it sounds like when you start to explain it to someone else. That's why we don't by any means want to take away the joy of collaboration from you and we actually encourage you to ask help not only from assistants, but your friends as well. We are quite good in giving advice, but your friend is more likely on the same wavelength with you and may be able to give you even better advice than we can.
However, on this course it's required that every student learns to write programs independently. This aspect is good to keep in mind when you are writing code with one or more friends. You shouldn't use code in your program if you don't know what it does. Ask your friend to explain if you don't understand the given piece of advice! Of course, if your friend is a good team player and has read this guide, then maybe he or she can instruct you in a way which you too understand what the code is doing. When you copy someone else's code everyone loses. The one who copies doesn't get any smarter when they can't understand what the copied part of the code does, and the one who allowed their code to be copied can't get to deepen their understanding of the issue by shaping it to a more comprehensible format.
Unfortunately these cases are encountered annually, so along with the beautiful thoughts we also have explicit and easily understandable rules concerning collaboration on this course. By following these rules you avoid problems - problems usually means, that your course credit can be postponed to the undefined future while the case is being resolved. In the worst case you may have to retake the whole course. These rules can be wrapped up to a few sections:
- Don't copy code from anywhere or anyone and make sure you understand everything you write to your program
- Always report if you have collaborated with someone. There's an individual text field for this in file tasks
- If you take an example from somewhere else (internet), tell this in comments above the code and tell where you found it
Group projects are allowed in the course (max group size 3). However if you do so, it's your collective responsibility to keep everyone in the group on the same page about how the code works, and that everyone contributes. Project review is an evaluation of how well you learned programming and show it in your work. Even the greatest code ever written will be rejected if it was clearly made by someone else.
All projects are ran through a tool that inspects code similarity rather extensively. If we find too high similarity between two or more projects, all involved parties will be questioned before deciding how to proceed.
Your course work will be accepted if it fulfills the core requirements specified in this document. Unless your code is extremely obnoxious, any fully functional programs are accepted, and there is a chance to improve your work after review. In addition to submitting a functional program, you are also required to reserve a 15 minute review session with a course assistant.
Projects are not given a grade. We've left the old code quality scoring criteria visible below as guidelines for you. You can use them to evaluate whether your code is suitable for someone else's eyes.
Quality of the code¶
A good overview of the quality of your code can be obtained from the Pylint review which has been included in the project return box. Some further notes have been written below.
Variables and naming¶
- bad: It is impossible to deduce what the variables and functions are for or what they contain based on their names.
- good: The names for variables and functions are usually informative about what they are used for, although some of the names can be a bit off. Variables can be named with several different languages and/or the naming convention is not very consistent.
- excellent: The program has a logical and consistent naming convention. The number of variables is low, and assignment to a variable is not used where it's not needed. Similarly, assignment to a variable has been used to simplify long lines of code.
- bad: No use of
else-clauses or their use is very lacking.
oroperators are not used or understood. As a result, the use of conditional statements are very shaky and might work more by accident than by intent.
- good: Conditional statements are mostly sensible. Proper use of
else, nested conditionals, and logical operators. Conditional statements can however be a bit shaky or be needlessly complex or long.
- excellent: Conditionals are pretty and succinct. All the previously mentioned tricks are used in a way that they improve the readability of code. They make it possible to detect different states so that the user can be told what is happening in the game.
Data structures (tuples, lists, and dictionaries)¶
- bad: No use or very little use of data structures. This is apparent in the code by having a lot of numbered variables. The few data structures are misused in horrible and naughty ways.
- good: Lists and such are used to create sensible data structures in places where they are obviously needed. In places where the need is not as obvious, there might be use of numbered variables or other comparable means.
- excellent: Data structures have been used in places where they are obviously needed and in places where they make the code clearer and more succinct. Nested data structures are also clearly understood.
Loops and iteration¶
- bad: All loops are made in the same way, sometimes by brute force. For example all loops might be made with a
while True, even if the loop clearly iterates over a list.
- good: The student is able to recognize when to use
whileand when to use
for. The use of loop variables in for loops might be a bit unclear, or loop creation in general is not optimal.
- excellent: Loops are efficient and the number of iterations in them is as low as possible. Loop variables are used well, especially in the case of nested datastructures. Tricks such as enumerate are used where proper.
- bad: No functions at all, or used just for the sake of using functions. Code is not divided into logical pieces. Using global variables instead of proper arguments for functions.
- good: Functions divide the program into logical segments, even if the segments can be a bit too large at times. The use of arguments and return values is correct. The use of global variables is low and only in places where it makes sense.
- excellent: Functions are divided so that they are all short and limited in their purpose. Only the main function can be a bit longer and fatter, but even it should be within sensible limits. The passing of arguments is sensible in the way that everything does not have to be passed everywhere. If there is any use of global variables, all of them are contained in one or two dictionaries.
- bad: No modules, the program does not function the proper way. Zero points can also come from using
from importeven when it does not make any sense.
- good: The required modules have been found and are used properly.
- excellent: The program uses some more complicated features of modules (for example the formatting in time) or a module has been used to create additional features. The program has been divided into modules of its own, if needed (in the case of clear and succinct code, usually not needed).
- bad: Program has no error handling, and every time something goes wrong, the Python stack trace is shown to the user. The program also doesn't solve erroneous states without breaking, which can result in a locked up or inescapable state in addition to crashing.
- good: Error are handled properly with the exception of some exotic cases. The user always has some sort of clue how to act so that the program does not report an error. Error handling methods might not be the most optimal.
- excellent: Error handling methods have been selected so that the user always gets the best possible information in the case of an error.
try-dblocks mostly contain a minimal amount of code. The program can deal with even the exotic error cases.
- bad: No use of files or reading files is done with some odd way (that was not taught on this course). Alternatively, the code only writes files but does not read them.
- good: Files are read and written in some way that the statistics requirement is filled. Alternatively the game has some other way to use files, like saving and loading.
- excellent: Files are used intelligently so that anyone can deduce just by looking at the files how it should be interpreted by the program.
In order to pass the course the student or group must undergo a 15-minute evaluation session with the course assistant who reviewed their work. You can book your evaluation time with a form which will be added to this site at the end of the course.