Project 2
The Game of Ur
Due Date: Friday, November 13th, 2020 by 11:59:59 PM
Value: 100 points
This assignment falls under the standard cmsc201 academic integrity policy. This means you should not discuss/show/copy/distribute your solutions, your code or your main ideas for the solutions to any other student. Also, you should not post these problems on any forum, internet solutions website, etc.
Make sure that you have a complete file header comment at the top of each file, and that all of the information is correctly filled out.
“””
File: FILENAME.py
Author: YOUR NAME
Date: THE DATE
Section: YOUR DISCUSSION SECTION NUMBER
E-mail: YOUR_EMAIL@umbc.edu
Description:
DESCRIPTION OF WHAT THE PROGRAM DOES
“””
Submission Details
Submit the files under the following title:
(These are case sensitive as usual. )
submit cmsc201 PROJECT2 royal_game_of_ur.py board_square.py
Note here that there are two files you need to submit.
This is a board for the Royal Game of Ur. The rosettes are the blue and red flower shapes. The black stone is one place away from the starting position and the white stone is at the end (one move away from leaving the board).
Project Description
For this project you will be implementing an old game, the Royal Game of Ur. The game itself dates back to about 2600 BCE, and is a predecessor of Backgammon.
This is a racing game in the sense that two players compete to be the first to complete a task. The game involves two players, each moving seven stones (black or white) from their start position to the end of the board, and then back off the board. Once all of one player’s stones are off the end of the board, that player wins.
The game rules are here:
- There are two players, each who have seven stones, off the board.
- First the white player moves, then black, and alternate that way until the game is over.
- During a player’s turn, you will roll 4 tetrahedral dice. The dice have two of the corners with a white dot, the other two without. Therefore there is a 50% chance for each die to come up with the dot. This leads to a binomial distribution for the dice roll values.
- The number of dots facing up on the die will be the number of spaces that one can move a piece.
- When a piece is ready to enter the board, it can be moved onto the board at the starting location for a price of one movement dot (and then continue moving that piece).
- You may also move a piece already on the board the number of spaces as dots that are pointing up on the die.
- You cannot move a piece when it would end up on top of one of your own pieces.
- You can move a piece if it will land on an opponent’s piece, unless that player is on a rosette.
- If you land on an opposing piece (they are not on a rosette), then you knock their piece off the board and they have to start again.
- Rosettes are special spaces which do two things:
- If a player lands on a rosette, that player gets to roll again.
- If a player lands on a rosette and their stone remains there, it cannot be knocked off the board (back to start).
- If a player cannot move because their pieces are all blocked, or they get a zero total on their dice, they don’t move and their turn ends.
- In order to go off the board, you must roll exactly 1 more than the number required to get to the last space. If you are at the last space you must roll a 1 to leave the board.
- If you are two places from the end, for instance, and roll a 4, then that move is not possible.
- If you are two places from the end and roll a 3, then you can move off the board.
- The game ends when all 7 of one player’s pieces have traveled the path.
Another way to learn about the game is to watch this video from the British Museum.
Here’s an example of what a tetrahedral die would look like:
Note that there are two dots and two non-dots. The one that lands “up” is the one that counts.
Implementation Details
You must implement a class called:
class RoyalGameOfUr:
This class must implement whatever methods you require to support a play_game(self) function, which will play the game.
You should create at least 4 helper functions for this main play_game function. They should be part of the RoyalGameofUr class. Don’t view this requirement as “I need 4 helper functions, and can shove the rest of the code into play_game.” View this requirement as “I should create probably 6-8 helper functions which do small tasks to help me play the game.”
The other required method is the constructor for the class, which must accept a file name, and load that board. You should declare
def __init__(self, board_file_name):
self.board = None
self.load_board(board_file_name)
# more code should be added to the function
Some code has been provided to you at:
/afs/umbc.edu/users/e/r/eric8/pub/cs201/fall20/royal_ur_starter.py
/afs/umbc.edu/users/e/r/eric8/pub/cs201/fall20/board_square_starter.py
You must complete the implementation of the UrPiece class, and the RoyalGameofUr class. You can add functions to BoardSquare, UrPiece, and RoyalGameofUr, as well as constants there.
Documentation
Some functions within the RoyalGameofUr class have been written for you:
load_board(self, board_file_name):
This function takes a file name and sets the self.board member within the class. The self.board member will be a 2d list of BoardSquare objects.
draw_block(self, output, i, j, square):
This is a helper function which draws the information into the (i, j) block of the board output. output is the 2d list of characters that gets joined to become the displayed board, and square should be a BoardSquare object. You should never have to call this function.
display_board(self):
Calling this function should display the board with the pieces at their current positions, rosettes are represented by asterisks in the corners of the blocks. You should call this function for all of your drawing needs.
roll_d4_dice(self, n=4):
Because I want to ensure that all of us are using the same function to roll dice. The reason for this is that we can ensure that the random rolls that come out are the same if we adjust the seed properly.
play_game(self):
This is the function you should implement to start and play the game. It shouldn’t take any additional arguments.
Documentation for: class UrPiece
There are a number of data elements in UrPiece, and you are permitted to add more.
class UrPiece:
def __init__(self, color, symbol):
self.color = color
self.position = None
self.complete = False
self.symbol = symbol
def can_move(self, num_moves):
pass
The color should be either black or white, you should create constant strings to represent these colors throughout your program.
Position represents the position on the board of that piece. There are many different ways to represent this.
Complete was added to ensure that if the piece leaves the board, it’s recorded as finished.
Symbol is a short 1 or 2 character marker that will be placed on the board wherever that stone is. It should be unique, for instance W0, W1, … W6 for the seven stones. That way you can tell which stone you’re moving.
The can_move(self, num_moves): function is the subject of your homework 6 part 5 problem piece_of_project2.py. The goal is to determine if that stone can move that number of moves. If it’s off the board, can it enter the board and move, if it’s near the exit, can it get to the exit, and if it’s in the middle, will it hit another stone? Can it capture that stone, etc?
Documentation for: class BoardSquare:
Looking at the constructor for the BoardSquare class:
def __init__(self, x, y, entrance=False, _exit=False, rosette=False, forbidden=False):
self.piece = None
self.position = (x, y)
self.next_white = None
self.next_black = None
self.exit = _exit
self.entrance = entrance
self.rosette = rosette
self.forbidden = forbidden
Piece represents the UrPiece at that location. Since there can be only one, it should either be None or the UrPiece class.
Position should be a tuple or list representing the (x, y) coordinate of the board position.
Since white and black can diverge on different paths, we need a next_white and next_black variable which tracks both of the next paths from the current BoardSquare.
Exit represents a string, if it is a white exit position, then it will say “White” and if it is a black exit position it will be “Black” and if neither it should be an empty string or None.
Entrance represents a string, if it is a white entrance position, then it will say “White” and if it is a black entrance position it will be “Black” and if neither it should be an empty string or None.
Rosette is a boolean representing whether the space is a rosette.
Forbidden is a boolean representing whether the space is “forbidden” meaning it won’t be drawn and no paths lead to it on the board.
There are two functions within the BoardSquare class.
load_from_json(self, json_string)
This is a helper function for the load board class. It shouldn’t be necessary to modify or touch this function in any way.
def jsonify(self):
This is a helper function for the board creator, and ideally you shouldn’t have to ever call or modify this function in any way.
Documentation for: main
There are three driver lines in the provided main. You should assume that this is basically what our test drivers will start with, perhaps with an additional seed value. You shouldn’t really modify the main since doing so will cause your project not to run with our driver code.
Provided Files
For this project, the provided files are:
All of these files are simply given by path, you need to add the cp command for the copy and a dot afterward for “current location.”
Starter code:
/afs/umbc.edu/users/e/r/eric8/pub/cs201/fall20/board_square.py
/afs/umbc.edu/users/e/r/eric8/pub/cs201/fall20/royal_ur_starter.py
(Remember you’ll have to rename this royal_game_of_ur.py)
Test file for HW6, Project 2:
/afs/umbc.edu/users/e/r/eric8/pub/cs201/fall20/project_piece_test.py
Compiled Project for Sample Ouput:
/afs/umbc.edu/users/e/r/eric8/pub/cs201/fall20/royal_game_of_ur.pyc
(compiled on the GL server, should run there successfully)
/afs/umbc.edu/users/e/r/eric8/pub/cs201/fall20/royal_ur_win_3.7.pyc
(compiled on windows 10, python 3.7)
Board Creator File:
/afs/umbc.edu/users/e/r/eric8/pub/cs201/fall20/board_creator.py
Sample Boards:
/afs/umbc.edu/users/e/r/eric8/pub/cs201/fall20/original_board.ur
/afs/umbc.edu/users/e/r/eric8/pub/cs201/fall20/two_lines.ur
I’ve created two_lines.ur as a test for basic movement. Once that works you can try the more complex board. I may add additional boards, or you can do so with the board creator. Remember that the board creator cannot be run on the GL server. If you do this, it’ll have some exceptions because it cannot open a GUI in an ssh text interface.
The Historical Board
Sample Output
I won’t provide full sample output because it would take many pages for a single game, but I will give you a pyc file to run like last time.
linux3[71]% python3 royal_game_of_ur.py What is the file name of the board json? test.ur What is your name? Eric Eric you will play as white. What is your name? James James you will play as black. ++++++++++++++++++++++++ +* *++ ++* *+ + ++ ++ + +* *++ ++* *+ ++++++++++++++++++++++++ ++++++++++++++++++++++++ + ++ ++ + + ++ ++ + + ++ ++ + ++++++++++++++++++++++++ ++++++++++++++++++++++++ + ++ ++ + + ++ ++ + + ++ ++ + ++++++++++++++++++++++++ ++++++++++++++++++++++++ + ++* *++ + + ++ ++ + + ++* *++ + ++++++++++++++++++++++++ ++++++++ + + + + + + ++++++++ ++++++++ + + + + + + ++++++++ ++++++++++++++++++++++++ + ++ ++ + + ++ ++ + + ++ ++ + ++++++++++++++++++++++++ ++++++++++++++++++++++++ +* *++ ++* *+ + ++ ++ + +* *++ ++* *+ ++++++++++++++++++++++++ You rolled 2 1 W0 currently off the board 2 W1 currently off the board 3 W2 currently off the board 4 W3 currently off the board 5 W4 currently off the board 6 W5 currently off the board 7 W6 currently off the board Which move do you wish to make? 1 No moves are possible with the current dice roll. ++++++++++++++++++++++++ +* *++ ++* *+ + ++ ++ + +* *++ ++* *+ ++++++++++++++++++++++++ ++++++++++++++++++++++++ + ++ ++ + + ++ ++ + + ++ ++ + ++++++++++++++++++++++++ ++++++++++++++++++++++++ + ++ ++ + + W0 ++ ++ + + ++ ++ + ++++++++++++++++++++++++ ++++++++++++++++++++++++ + ++* *++ + + ++ ++ + + ++* *++ + ++++++++++++++++++++++++ ++++++++ + + + + + + ++++++++ ++++++++ + + + + + + ++++++++ ++++++++++++++++++++++++ + ++ ++ + + ++ ++ + + ++ ++ + ++++++++++++++++++++++++ ++++++++++++++++++++++++ +* *++ ++* *+ + ++ ++ + +* *++ ++* *+ ++++++++++++++++++++++++ You rolled 1 1 B0 currently off the board 2 B1 currently off the board 3 B2 currently off the board 4 B3 currently off the board 5 B4 currently off the board 6 B5 currently off the board 7 B6 currently off the board Which move do you wish to make? 2 No moves are possible with the current dice roll. ++++++++++++++++++++++++ +* *++ ++* *+ + ++ ++ + +* *++ ++* *+ ++++++++++++++++++++++++ ++++++++++++++++++++++++ + ++ ++ + + ++ ++ + + ++ ++ + ++++++++++++++++++++++++ ++++++++++++++++++++++++ + ++ ++ + + W0 ++ ++ + + ++ ++ + ++++++++++++++++++++++++ ++++++++++++++++++++++++ + ++* *++ + + ++ ++ B1 + + ++* *++ + ++++++++++++++++++++++++ ++++++++ + + + + + + ++++++++ ++++++++ + + + + + + ++++++++ ++++++++++++++++++++++++ + ++ ++ + + ++ ++ + + ++ ++ + ++++++++++++++++++++++++ ++++++++++++++++++++++++ +* *++ ++* *+ + ++ ++ + +* *++ ++* *+ ++++++++++++++++++++++++ You rolled 2 1 W0 (2, 0) … |
PYC File
I’ve uploaded the pyc file for the game at:
/afs/umbc.edu/users/e/r/eric8/pub/cs201/fall20/royal_game_of_ur.pyc
I’ll put the windows version that I compile on my own computer (in python 3.7) in the discord announcements.
The pyc file is a compiled python file, into python bytecode, which means that you should be able to play the game, and run it to see various aspects of the game.
Board Creator
The board creator is a small program I wrote to be able to create these boards. You CANNOT run this on the GL server. This code has a graphical interface, so it can’t be run without that.
The boards you can create are more general than what is expected, you can for instance create boards with more starts and ends than just one each for each color. You can have dead ends, weird shapes, etc.
All boards we test on will have one start, one end, and guaranteed paths from the start to the end.
In order to access the position, you should right click on a place.
Currently the load board menu option doesn’t do anything, I may implement that if I have time (probably won’t).
This is how it looks by default, to access a position to do anything, including setting rosettes, setting forbidden positions (undrawn places), setting start and end positions, and selecting white and black next positions, access it via the right click menu on the place.
The first option is “Do nothing” mainly to prevent accidental clicks on the set next. It really does do nothing.
After you are finished, you can save it to a board. It won’t automatically add the .ur but you can do that yourself. Either way, your program doesn’t care if the file has the extension or not.
Generalized Rubric
I’ve listed the approximate point values for each part of functionality in the project. They are subject to change slightly but this will generally be the rubric that we adhere to. We will break each one of these down into sub-parts which will have point values which add up to close to the approximate point value.
Functionality | Apprx. Point Value |
Setting up the game with players | 10 |
Displaying pieces on the board | 10 |
Pieces enter the board properly | 10 |
Pieces complete the path properly | 10 |
Pieces move properly on the board | 20 |
Knocking opponent pieces off | 15 |
Rosettes as Safe | 15 |
Coding Standards, Comments, etc | 10 |
Extra Credit: Rosettes give another roll | 15 |
Coding Standard: Global Variables
Global variables are not permitted for this project beyond a creation of a RoyalGameofUr class in the main.
A global variable is anything declared outside of a class, outside of a method, i.e. not in a class scope or method scope.
Global constants are permitted for this project beyond a creation of a RoyalGameofUr class in the main.
This means you can still use global constants at the top of your program, which is recommended.
Coding Standards
Coding standards can be found here.
We will be looking for:
- At least one inline comment per program explaining something about your code.
- Constants above your function definitions, outside of the “if __name__ == ‘__main__’:” block.
- A magic value is a string which is outside of a print or input statement, but is used to check a variable, so for instance:
- print(first_creature_name, ‘has died in the fight. ‘) does not involve magic values.
- However, if my_string == ‘EXIT’: exit is a magic value since it’s being used to compare against variables within your code, so it should be:
EXIT_STRING = ‘EXIT’
- A magic value is a string which is outside of a print or input statement, but is used to check a variable, so for instance:
…
if my_string == EXIT_STRING:
- A number is a magic value when it is not 0, 1, and if it is not 2 being used to test parity (even/odd).
- A number is magic if it is a position in an array, like my_array[23], where we know that at the 23rd position, there is some special data. Instead it should be
USERNAME_INDEX = 23
my_array[USERNAME_INDEX]
- Constants in mathematical formulas can either be made into official constants or kept in a formula.
- Previously checked coding standards involving:
- snake_case_variable_names
- CAPITAL_SNAKE_CASE_CONSTANT_NAMES
- Use of whitespace (2 before and after a function, 1 for readability.)
Allowed Built-ins/Methods/etc
- Declaring and assigning variables, ints, floats, bools, strings, lists, dicts.
- Using +, -, *, /, //, %, **; +=, -=, *=, /=, //=, %=, **= where appropriate
- Comparisons ==, <=, >=, >, <, !=, in
- Logical and, or, not
- if/elif/else, nested if statements
- Casting int(x), str(x), float(x), (technically bool(x))
- For loops, both for i and for each type.
- While loops
- sentinel values, boolean flags to terminate while loops
- Lists, list(), indexing, i.e. my_list[i] or my_list[3]
- 2d-lists if you want them/need them my_2d[i][j]
- Append, remove
- list slicing
- If you have read this section, then you know the secret word is: createous.
- String operations, concatenation +, +=, split(), strip(), join(), upper(), lower(), isupper(), islower()
- string slicing
- Print, with string formatting, with end= or sep=:
- ”.format(var), ‘%d’ % some_int, f-strings
- Really the point is that we don’t care how you format strings in Python
- Ord, chr, but you won’t need them this time.
- Input, again with string formatting in the prompt, casting the returned value.
- Dictionaries
- creation using dict(), or , copying using dict(other_dict)
- .get(value, not_found_value) method
- accessing, inserting elements, removing elements.
- Using the functions provided to you in the starter code.
- Using import with libraries and specific functions as allowed by the project/homework.
- Recursion – allowed but totally unnecessary.
- Tuples are allowed, as they are immutable lists.
Forbidden Built-ins/Methods/etc
This is not a complete listing, but it includes:
- break, continue
- methods outside those permitted within allowed types
- for instance str.endswith
- list.index, list.count, etc.
- Keywords you definitely don’t need: await, as, assert, async, class, except, finally, global, lambda, nonlocal, raise, try, yield
- The is keyword is forbidden, not because it’s necessarily bad, but because it doesn’t behave as you might expect (it’s not the same as ==).
- built in functions: any, all, breakpoint, callable, classmethod, compile, exec, delattr, divmod, enumerate, filter, map, max, min, isinstance, issubclass, iter, locals, oct, next, memoryview, property, repr, reversed, round, set, setattr, sorted, staticmethod, sum, super, type, vars, zip
- If you have read this section, then you know the secret word is: argumentative.
- exit() or quit()
- If something is not on the allowed list, not on this list, then it is probably forbidden.
- The forbidden list can always be overridden by a particular problem, so if a problem allows something on this list, then it is allowed for that problem.