For our first project, we're going to be implementing the game Mancala. Mancala has been discovered in the excavated ruins of a Roman bathhouse built in the 2nd or 3rd century AD.
In the 21st century, however, we have Python so we'll write a little version of the game ourselves.
The game is played with 'stones' which can be glass bubbles or stones, and there are generally two rows of 6 circular "cups" or pits, and then there's a double length pit on both sides, which we will also call a "mancala."
1. The board is formed by the two long pits or cups, and 12 cups divided into two rows, like in the picture.
2. To set up the game, place four stones in each of the 12 regular cups.
3. Players alternate by taking turns.
4. A turn consists of selecting a non-empty non-mancala cup and taking all of the stones from it, and then starting in the next cup, place a stone, and then advance to the next cup and place another stone, repeating this until all of the stones have been moved.
5. The game ends when one of the rows of regular cups is empty, so when either the "top" row or the "bottom" row is empty, the game is over.
6. The winner when the game ends is the player with the most stones in their mancala.
7. The board is numbered in the following order. Cups 0 and 7 are mancalas whereas the rest are "regular."
8. Player 1's mancala is at position 7, and player 2's mancala is at position 0.
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
13 | 12 | 11 | 10 | 9 | 8 |
This is a specification for how the project should be implemented.
1. Get the names of players.
2. Begin the cycle of turns by alternating back and forth from player 1 to player 2 and back until the game is over.
3. When a player takes a turn, move the stones for them, starting in the next cup and placing a stone in each cup until the stones run out.
4. If a user enters an invalid cup number, make them try again.
5. If a turn ends in a mancala (the last stone is placed there), then that player gets a second turn.
6. When one of the two rows (positions 1 to 6 or positions 8 to 13) are empty, then the game ends.
7. Tell the players who has won based on the number of stones in each of the mancalas.
8. After each move, reprint the board and ask for the next user input.
Your project should include at least the functions:
1.) This function should be the entry point for your program. Your if __name__ == "__main__": should be exactly one line, which calls this function, unless you want to create the players before you run the game.
def run_game():
2.) The function get_player should be something like:
def get_player():
3.) You should create a function:
def take_turn(player, game_cups):
4.)You must create at least 2 more functions to help you finish the project. You can choose what they do, and what exactly their signatures should be. If you ask the question "but what if I don't need 2 more functions?" then you definitely have too much functionality going on in any single function. I created about 5 helper functions in my implementation.
I have provided a starter file on the GL server at: /afs/umbc.edu/users/e/r/eric8/pub/cs201/spring21/mancala_starter.py
You should copy it to your directory using the command: cp /afs/umbc.edu/users/e/r/eric8/pub/cs201/spring21/mancala_starter.py mancala.py
There are three functions in the file as well as some constants.
draw_board(top_cups, bottom_cups, mancala_a, mancala_b):
The draw board function is the function you should be calling. You should send data to this function in the following way:
Top_cups and bottom_cups should be a 2-d list of strings. The outer list for both of these variables should be length 6, because that's the number of cups.
The inner list should be a list of strings which is of length BLOCK_HEIGHT, because that's the number of lines.
You can use slices like this: my_string[0: BLOCK_WIDTH] to chop off any strings you print, and str.rjust(BLOCK_WIDTH) to give extra space. The draw board function will try to do this for you, but if you end up with IndexErrors, then you should use these methods to force the string to be the right length.
Mancala_a and mancala_b are lists of strings of length 2 * BLOCK_HEIGHT + 1 because there is the midline asterisk which takes up another space. The strings should all be of length BLOCK_WIDTH, but my code tries to help you out as well.
I am providing minimal documentation, i.e. nearly nothing about the following two functions. The reason for this is that you don't need to call these functions, and shouldn't modify them. You can read them of course and though they are documented, they are not terribly complex, but just call draw_board with the right data.
draw_mancala(fore_or_aft, mancala_data, the_board):
This function draws out a mancala, but you should not call this function directly. It's a helper function for my draw_board function.
draw_block(the_board, pos_x, pos_y, block_data):
Similarly, this is also a function you shouldn't have to call. It's another helper function for the draw_board function.
1. At least one inline comment per function explaining something about your code.
2. Constants above your function definitions, outside of the "if __name__ == '__main__':" block.
3. Previously checked coding standards involving:
This is not a complete listing, but it includes:
BLOCK_WIDTH = 6
BLOCK_HEIGHT = 5
BLOCK_SEP = "*"
SPACE = ' '
def draw_board(top_cups, bottom_cups, mancala_a, mancala_b):
"""
draw_board is the function that you should call in order to draw the board.
top_cups and bottom_cups are 2d lists of strings. Each string should be
length BLOCK_WIDTH and each list should be of length BLOCK_HEIGHT.
mancala_a and mancala_b should be 2d lists of strings. Each string should be
BLOCK_WIDTH in length, and each list should be 2 * BLOCK_HEIGHT + 1
:param top_cups: This should be a list of strings that represents cups 1 to 6
(Each list should be at least BLOCK_HEIGHT in length, since each string in the list is
a line.)
:param bottom_cups: This should be a list of strings that represents cups 8 to 13
(Each list should be at least BLOCK_HEIGHT in length, since each string in the list is
a line.)
:param mancala_a: This should be a list of 2 * BLOCK_HEIGHT + 1 in length which
represents the mancala at position 7.
:param mancala_b: This should be a list of 2 * BLOCK_HEIGHT + 1 in length which
represents the mancala at position 0.
"""
board = [[SPACE for _ in range((BLOCK_WIDTH + 1) * (len(top_cups) + 2) + 1)] for _
in range(BLOCK_HEIGHT * 2 + 3)]
for p in range(len(board)):
board[p][0] = BLOCK_SEP
board[p][len(board[0]) - 1] = BLOCK_SEP
for q in range(len(board[0])):
board[0][q] = BLOCK_SEP
board[len(board) - 1][q] = BLOCK_SEP
# draw midline
for p in range(BLOCK_WIDTH + 1, (BLOCK_WIDTH + 1) * (len(top_cups) + 1) + 1):
board[BLOCK_HEIGHT + 1][p] = BLOCK_SEP
for i in range(len(top_cups)):
for p in range(len(board)):
board[p][(1 + i) * (1 + BLOCK_WIDTH)] = BLOCK_SEP
for p in range(len(board)):
board[p][1 + BLOCK_WIDTH] = BLOCK_SEP
board[p][len(board[0]) - BLOCK_WIDTH - 2] = BLOCK_SEP
for i in range(len(top_cups)):
draw_block(board, i, 0, top_cups[i])
draw_block(board, i, 1, bottom_cups[i])
draw_mancala(0, mancala_a, board)
draw_mancala(1, mancala_b, board)
print('\n'.join([''.join(board[i]) for i in range(len(board))]))
def draw_mancala(fore_or_aft, mancala_data, the_board):
"""
Draw_mancala is a helper function for the draw_board function.
:param fore_or_aft: front or back (0, or 1)
:param mancala_data: a list of strings of length 2 * BLOCK_HEIGHT + 1 each string
of length BLOCK_WIDTH
:param the_board: a 2d-list of characters which we are creating to print the
board.
"""
if fore_or_aft == 0:
for i in range(len(mancala_data)):
data = mancala_data[i][0: BLOCK_WIDTH].rjust(BLOCK_WIDTH)
for j in range(len(mancala_data[0])):
the_board[1 + i][1 + j] = data[j]
else:
for i in range(len(mancala_data)):
data = mancala_data[i][0: BLOCK_WIDTH].rjust(BLOCK_WIDTH)
for j in range(len(mancala_data[0])):
the_board[1 + i][len(the_board[0]) - BLOCK_WIDTH - 1 + j] = data[j]
def draw_mancala(fore_or_aft, mancala_data, the_board):
"""
Draw_mancala is a helper function for the draw_board function.
:param fore_or_aft: front or back (0, or 1)
:param mancala_data: a list of strings of length 2 * BLOCK_HEIGHT + 1 each string
of length BLOCK_WIDTH
:param the_board: a 2d-list of characters which we are creating to print the
board.
"""
if fore_or_aft == 0:
for i in range(len(mancala_data)):
data = mancala_data[i][0: BLOCK_WIDTH].rjust(BLOCK_WIDTH)
for j in range(len(mancala_data[0])):
the_board[1 + i][1 + j] = data[j]
else:
for i in range(len(mancala_data)):
data = mancala_data[i][0: BLOCK_WIDTH].rjust(BLOCK_WIDTH)
for j in range(len(mancala_data[0])):
the_board[1 + i][len(the_board[0]) - BLOCK_WIDTH - 1 + j] = data[j]
def draw_block(the_board, pos_x, pos_y, block_data):
"""
Draw block is a helper function for the draw_board function.
:param the_board: the board is the 2d grid of characters we're filling in
:param pos_x: which cup it is
:param pos_y: upper or lower
:param block_data: the list of strings to put into the block.
"""
for i in range(BLOCK_HEIGHT):
data = block_data[i][0:BLOCK_WIDTH].rjust(BLOCK_WIDTH)
for j in range(BLOCK_WIDTH):
the_board[1 + pos_y * (BLOCK_HEIGHT + 1) + i][1 + (pos_x + 1) *
(BLOCK_WIDTH + 1) + j] = data[j]
def get_player():
pass
def take_turn(player, cups):
pass
def run_game():
pass
if __name__ == "__main__":
run_game()