For this assignment, we are going to be creating a program that lets you play a game of Onitama.
You will be graded on:
If you are unfamiliar with this game, make sure to check out these links first:
https://www.youtube.com/watch?v=IFRewjcngwU
https://www.arcanewonders.com/resources/Onitama_Rulebook.PDF
We are going to follow test-driven development for this assignment to introduce you to good coding practices.
This means, that before we even start coding anything for our game, we will make test cases to ensure that when we start writing our code for the main program, we will know if it is working as expected or not.
Although you may see a lot of files in the starter code and may get flustered, most of them are for the GUI program, and the others have the code completed for you.
You will only be working on OnitamaGame.py and OnitamaBoard.py. Hence, you are only
required to make test cases for these two files.
As far as testing goes, we want rigerous tests for the following methods in the respective files:
other_playerget_tokenis_legal_movemoveget_winnerundoconstruct_stylesexchange_Stylevalid_coordinateget_tokenset_tokenWhat does rigerous testing mean?????
It means that you are required to do two things.
Tips:
get_token, you
may want to
test for:
hypothesis module useful, here is the documentation for
it, so
you can learn how to use external modules for your own use!
Pieces.pyTo practice good code design, we have declared some constants that you must use when completing this assignment.
You will find them in Pieces.py and one of them in ImageGenerator.py, which is the super
class of
Pieces (so using inheritance, all of them are accessible in Pieces).
These are the five that you need to worry about:
With that out of the way, you are ready to start working on the code for Assignment 1!
OnitamaBoard.pyComplete the easy methods in OnitamaBoard.py:
valid_coordinate, get_token, set_token,Complete the method construct_styles in OnitamaBoard.py, based on the method
documentation.
In a normal Onitama game, there are 16 different styles that the players choose from. However, for simplicity we will only be using 5 of these styles (Crab, Horse, Mantis, Rooster, and Dragon), as you can see below.
Each style represents a unique movement pattern. In this pictures, the black square is the starting position, and the other shaded squares are the destinations that the token is able to reach using the respective movement style.
For example, let's consider the Crab style.
Let (r, c) represent the black square, the starting position, where r is the row, and c is the column.
Then, we are able to reach the following positions, relative to the starting position:





This method will distribute the five styles that we are using to the two players.
Normally, players would pick the styles randomly, however for this assignment, we will distribute them consistently each time.
This is so we can guarantee consistency when testing the program.
Complete the OnitamaBoard constructor in OnitamaBoard.py, based on the method documentation.
You'll notice that we are using some optional parameters in this initializer.
__init__(self, size: int, player1: Player, player2: Player, board: Union[List[List[str]], None] =
None)
For this constructor, if we have a board parameter we want to construct a preset Onitama board!
Use the given board parameter which is a size x size List of strings to initialize this OnitamaBoard.
For this constructor, we want to construct an empty Onitama board with dimension size x size.
In addition to this, we want to place the four monks and the 1 grandmaster on opposite sides of the board
for each player. We want the board to look like the following (for size = 5):
0 1 2 3 4
+-+-+-+-+-+
0|x|x|X|x|x|0
+-+-+-+-+-+
1| | | | | |1
+-+-+-+-+-+
2| | | | | |2
+-+-+-+-+-+
3| | | | | |3
+-+-+-+-+-+
4|y|y|Y|y|y|4
+-+-+-+-+-+
0 1 2 3 4

However, the grid should not be all hardcoded!
Remember, that there is a size parameter, so what would you do if size > 5?
We require that you satisfy the following 4 conditions:
Keep these things in mind, when implementing the constructor!
Lastly, we want to distribute the 5 styles to the players.
Hint: You just completed a helper for this... USE IT!!
Complete the method exchange_style in OnitamaBoard.py, based on the method
documentation.
In Onitama, when a player does a move with a certain style (in our case it will be one of Crab, Horse, Mantis, Rooster or Dragon), they swap the style they just used with the EMPTY/Unowned style.
Remember, that each player has 2 styles, but we have a total of 5 styles, so one of these styles will NOT have an owner (G1 or G2), rather, their owner will be EMPTY.
For this method, we want to exchange the given style with the EMPTY/Unowned style. This involves swapping their owners if possible.
Fix any issues you find, clean up your code, avoiding repeated code, using helper functions appropriately, etc.).
You will be required to implement the Stack ADT for this assignment.
Where is the starter code for this?????????????
Non-existent. This is for you to implement however you want by reading the code inside of
OnitamaGame.py.
You will see the relevant type annotations inside of this class which will help you name the class you are required to implement.
Also look out for where that attribute is being used inside of the code and how it is being used to figure out which methods you will need.
Complete the easy methods in OnitamaGame.py based on the method documentation:
other_player, get_token,Hint: For get_token do not repeat the code from OnitamaBoard.py, you can instead
use that
method as a helper!
Complete the method is_legal_move in OnitamaGame.py, based on the method
documentation.
This method is going to be a helper for the next task.
It checks if a move with the starting position (row_o, col_o) to the target position
(row_d,
col_d) is valid.
For a move to be valid, it must satisfy the following criteria:
IMPORTANT: This method does not check if the move follows a valid movement style
Complete the move method in OnitamaGame.py, based on the method documentation and
the
hints provided within the comments in the file. Try to keep your code short
and clean, using helper methods from this class and the OnitamaBoard class as appropriate.
This method takes in the starting position (row_o, col_o) and the target/destination position
(rowD, col_d) that a player is going
to 'move to' (move from starting position to the destination position), and the name of the style
that they
are using (styleName).
If the move is not valid from the conditions in is_legal_move or the move does not follow the
movement
pattern from the given style (remember each style has a unique movement pattern), do nothing to the board
and
return False.
If the move is valid, then we need to do the following (the order is not necessarily the same order that you should implement it in):
OnitamaStack.
For example, if my board looked like below:
0 1 2 3 4
+-+-+-+-+-+
0|x|x|X| |x|0
+-+-+-+-+-+
1| | | | | |1
+-+-+-+-+-+
2| | | |x| |2
+-+-+-+-+-+
3| | | | |y|3
+-+-+-+-+-+
4|y|y|Y| |y|4
+-+-+-+-+-+
0 1 2 3 4

Then if I wanted to make a move (row_o = 3, col_o = 4, row_d = 2, col_d = 3, style_name =
"mantis") with player 'Y', then this would be a valid move and we would
eliminate player
'X's monk at the position (2, 3) and move player 'Y's monk to the position (2, 3).

The updated board would look like the following:
0 1 2 3 4
+-+-+-+-+-+
0|x|x|X| |x|0
+-+-+-+-+-+
1| | | | | |1
+-+-+-+-+-+
2| | | |y| |2
+-+-+-+-+-+
3| | | | | |3
+-+-+-+-+-+
4|y|y|Y| |y|4
+-+-+-+-+-+
0 1 2 3 4

Complete the get_winner method in OnitamaGame.py, based on the method
documentation.
Again, keep
your code short and clean. Use the G1, G2 and EMPTY variables from the OnitamaBoard
class to access the needed character tokens.
There are 2 winning conditions:
Here are 4 very basic game states to represent player victories.
X wins by condition 1.
0 1 2 3 4
+-+-+-+-+-+
0| | | | | |0
+-+-+-+-+-+
1| |y| | |x|1
+-+-+-+-+-+
2| | |X| | |2
+-+-+-+-+-+
3| | | | |o|3
+-+-+-+-+-+
4| | | | | |4
+-+-+-+-+-+
0 1 2 3 4
X wins by condition 2.
0 1 2 3 4
+-+-+-+-+-+
0| | | | | |0
+-+-+-+-+-+
1| | | | | |1
+-+-+-+-+-+
2| | |Y|y| |2
+-+-+-+-+-+
3| | | | | |3
+-+-+-+-+-+
4| | |X| | |4
+-+-+-+-+-+
0 1 2 3 4
Y wins by condition 1.
0 1 2 3 4
+-+-+-+-+-+
0| | | | | |0
+-+-+-+-+-+
1| |x| | | |1
+-+-+-+-+-+
2| | |Y| | |2
+-+-+-+-+-+
3| | | |x| |3
+-+-+-+-+-+
4| |y| | | |4
+-+-+-+-+-+
0 1 2 3 4
O wins by condition 2.
0 1 2 3 4
+-+-+-+-+-+
0| | |Y| | |0
+-+-+-+-+-+
1| | | | | |1
+-+-+-+-+-+
2| |x|X| | |2
+-+-+-+-+-+
3| | | | | |3
+-+-+-+-+-+
4| | | | | |4
+-+-+-+-+-+
0 1 2 3 4
After the previous tasks have all been completed, you should be able to run main.py to run the
GUI so
you can play the game!
Fix any issues you find, clean up your code, avoiding repeated code, using helper functions appropriately, etc.)

There are 3 steps to making a move on the gui.
1. Choose a piece.
2. Choose a style.
3. Choose a destination.
Note: Steps 1 and 2 are interchangeable with the caveat that after selecting a style, the destination tiles (blue highlighted tiles which you can move to), will be filtered for that tile only.
On the right side of the board, you will see 5 buttons.
The first 3 buttons, HvH, HvR, RvR represent the 3 game modes.
By default the game mode is set to HvH.
The current game mode will be highlighted green.
This is the default game mode and is Human vs Human.
The name should be self-explanatory, it sets both of the players to human controlled players.
This is the Human vs Random game mode.
This sets the other player to a PlayerRandom and they will make random moves.
Note: This is relative to the current player. So if the current move is for Player G1, then Player G2 will become random. On the otherhand, if the current move is for Player G2, then Player G1 will become random.
This is the Random vs Random game mode.
This sets both players to PlayerRandom and they will make random moves.
Essentially, this runs a simulation of the game with 0.5 seconds of delay per move.
To stop this at any time, you can switch the game mode and proceed.
The last 2 buttons represent actions you can do to change the state of the board.
This uses the stack that you implemented to revert to the previous state of the game. If you try using this at the beginning of the game, nothing should happen because there are no previous states. Notably, this should not crash the program, if it does, there might be an error in your implementation of the stack.
This resets the game to the initial state and sets the game mode to HvH.
Essentially, use this button to restart the game after the game is over or if you are tilted and are losing badly.
When the game ends, you will not be able to make any moves on the board. The game mode gets switched to HvH if you press the undo move to allow you to take control again and play the game.
Watch a short demo of the GUI features!
There are quite a few classes which the code is complete for, however it is missing documentation (Deval forgot to document his code?!??!?! #BlameDeval)!
It's time for you to use your code reading skills and complete the documentation and type annotations for the following classes and all of the methods inside of them.
Note: You are not required to add doctests for these classes and methods.
Please complete the documentation and type annotations for the following classes (and their attributes) and all methods inside of them by following the function design recipe and the class design recipe:
Turn.pyPlayer.py
set_onitama method, as it will require you to import OnitamaGame which
results in a
circular import and python will cry.Style.pyNote: You are not required to write docstrings to any of the other classes in this assignment.
This section should not be too difficult considering everything previous to this, however documenting code is very important and will be a significant portion of your mark for this assignment!
Congratulations on finishing A1!!!

Take some time to relax and do some things you enjoy, since we know that university is stressful at times (we were all in your position not too long ago).
Go play some games, watch shows/anime, read a book, draw something, or do literally anything that you love to do so you can de-stress!
Here's a great video I watch to celebrate completing assignments!