I attempted making a "Mancala" clone that is played via a simple terminal interface. The overall game is functional in that the user may play against themselves, play against a randomized opponent, or watch the board as two random opponents play against one another.
This was an attempt to leverage my understanding of classes and objects but I feel that I may have overused the Board
class. Areas like the make_move
function are seemingly bloated but the end result was the simplest and most effective way I could perceive of doing it.
In the future I'd like to add error handling and incorrect user input. I am trying to construct this to be extendable into something like Kivy so I can make a GUI for it next. My hope was that with each portion of the game being an object it should be a simple matter to translate it into objects to display via Kivy. Am I at all along the right train of thought?
Note: The code is commented throughout, usually line-by-line for the more convoluted areas.
import random
class Cell():
'''
The object that will hold information about each cell's position
and it's contents as well as who's side of the board it's on.
'''
def __init__(self,data):
'''
Takes in a simple list item that's generated by the board.
Has three items, [side,position,contents]
'''
self.side = data[0]
self.position = data[1]
self.contents = data[2]
def take(self):
'''
Simulates a player picking up a pile of pieces from a spot on the
board, leaving none behind.
'''
pieces = self.contents
self.contents = 0
return pieces
class Board():
'''
The class that will serve as the main working area for the game
'''
def __init__(self):
'''
Starts with empty values
'''
self.cells = {}
self.player = 0
self.game_state = 0
self.want_board = 0
self.self_play = 0
def setup(self):
'''
Provide a brief explanation of the game and allow some settings
to be selected such as showing the board each turn and whether
automatic play is enabled or not.
'''
print("Welcome to Mancala")
print("A simple board game where players move stones from")
print("one cell to another in consecutive order attempting")
print("to accumulate as many pieces as possible in their")
print("respective 'end' cells.")
self.want_board = int(input("Would you like the full board to be displayed each turn?(0\\1)\n"))
self.self_play = int(input("Would you like to control both sides?(0\\1)\n"))
if self.self_play == 3 and self.want_board != 1:
self.want_board = 1
print("Forcing board to see progress.")
def build_board(self):
'''
This will make data list items to pass into the cell class
Depending on which cell it is, it will be given different values
'''
for x in range(0,14):
data = [0,x,4]
if x%7 == 0:
if x > 6:
data[0] = 1
data[2] = 0
if x > 7:
data[0] = 1
self.cells[x] = Cell(data)
def show_board(self):
'''
Prints the current board information as a crude table
'''
print("___________________")
for cell in self.cells:
side = self.cells[cell].side
contents = self.cells[cell].contents
position = self.cells[cell].position
print("s{} | p{} | c{}".format(side,position,contents))
print("___________________")
def change_player(self):
'''
Switches the current player
'''
if self.player == 1:
self.player = 0
else:
self.player = 1
def make_move(self):
'''
A ... bloated... function that handles all the game play
functionality
'''
options = []
for cell in game.cells:
if self.player == self.cells[cell].side:
#if the cell is on that player's side
if self.cells[cell].contents < 1:
#and it's empty, skip it
continue
if cell%7 == 0:
#and it's an end zone cell, skip it
continue
#otherwise add it to the options
options.append(cell)
#if there are no options
if len(options) < 1:
#The game is over, this is the "win" condition
print("Player {} Cleared.".format(self.player))
print("P0:{} , P1:{}".format(self.cells[7].contents,self.cells[0].contents))
exit()
if self.self_play == 1:
#if the user wants to play against themselves, they will
#take the turn each time
for opt in options:
print("{} : {}".format(opt,self.cells[opt].contents))
choice = int(input("Which cell?\n"))
if self.self_play != 1:
#If they don't want to play against themselves
#and it's player 0's turn
if self.player == 0:
#And they hit option 3
if self.self_play == 3:
#Both sides will play automatically
choice = random.choice(options)
#If they picked to play against an automatic opponent
if self.self_play == 1:
#They get to play their turn
for opt in options:
print("{} : {}".format(opt,self.cells[opt].contents))
choice = int(input("Which cell?\n"))
else:
#Automate the turn
choice = random.choice(options)
#Takes the pieces from that cell
pieces = self.cells[choice].take()
#While there are pieces left to distribute
while pieces >= 1:
#To move to the next space, increment the choice value
choice += 1
if choice >= 14:
#makes the board 'loop' since there are only 14 spaces
#and we're counting from 0
choice = 0
#If the target cell is empty
if self.cells[choice].contents == 0:
#and if it's the last piece in the player's hand
#and it's not an end cell
if pieces == 1 and choice%7 != 0:
#Add the piece to the cell and take another turn
self.cells[choice].contents = self.cells[choice].contents + 1
print("Additional Turn")
return
#if it's an end cell and it's the player's side
if choice%7 == 0 and self.player == self.cells[choice].side:
#And if it's the last piece
if pieces == 1:
#Add the piece and take another turn
self.cells[choice].contents = self.cells[choice].contents + 1
print("Additional Turn")
return
#If it's not the last piece, just add it like normal
self.cells[choice].contents = self.cells[choice].contents + 1
continue
#If it's an end cell and it's not the players, skip it
if choice%7 == 0 and self.player != self.cells[choice].side:
continue
#If none of those trigger, then just add a piece to the cell
#remove a piece from 'pieces' and continue the loop
self.cells[choice].contents = self.cells[choice].contents + 1
pieces -= 1
#once the loop is over, change players
self.change_player()
game = Board()
game.game_state = 1
game.build_board()
game.setup()
while game.game_state != 0:
if game.want_board == 1:
game.show_board()
game.make_move()