CS 204: Software Design

Due 9:50AM Monday, 11 October 2010
You may work with a partner for this assignment.

Implementing part of the Mancala engine

See the previous assignment for a description of the context of this assignment.

Your job for this assignment will be to implement the move method described in the following MancalaBoard class, and the PyUnit test infrastructure described in the listing below it. On Monday, we will meet in the lab and run your implementation against everybody else's test cases to see if we can find and fix problems in your program.

''' mancala.py This file contains a rudimentary Mancala engine (the MancalaBoard class) and lots of Exception subclasses for use by MancalaBoard. ''' class MancalaException(Exception): def __init__(self): self.errorMessage = '' def __str__(self): return self.errorMessage class WrongPlayerError(MancalaException): def __init__(self, playerNumber): self.errorMessage = 'Player moving out of turn' self.playerNumber = playerNumber class BinIndexOutOfRangeError(MancalaException): def __init__(self, binNumber): self.errorMessage = 'Bin number out of range' self.binNumber = binNumber class AttemptToMoveFromEmptyBinError(MancalaException): def __init__(self, binNumber): self.errorMessage = 'Attempt to move from empty bin' self.binNumber = binNumber class AttemptToMoveFromStoreError(MancalaException): def __init__(self, binNumber): self.errorMessage = 'Attempt to move from store' self.binNumber = binNumber class GameAlreadyOverError(MancalaException): def __init__(self): self.errorMessage = 'Game over' class MancalaBoard: def __init__(self): self.reset() def reset(self): '''Initializes the board to the starting game state. Bins 0-5 are Player 1's bins, bin 6 is Player 1's store (i.e. the big bin), bins 7-12 are Player 2's bins, and bin 13 is Player 2's store.''' self.bins = [3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 0] self.isFirstPlayerTurn = True def move(self, binIndex): '''If the move starting in the specified bin index is legal, this method adjusts the game state accordingly. If the move specified by binIndex is not legal, this method will raise one of the following types of exception: to one of the following: GameAlreadyOverError BinIndexOutOfRangeError WrongPlayerError AttemptToMoveFromStoreError AttemptToMoveFromEmptyBinError as described in the test-cases assignment. Player 1 owns bins 0-6, and Player 2 owns bins 7-13. Thus, a move from bin 6 is AttemptToMoveFromStore, not WrongPlayer. ''' pass

Here's a framework for the PyUnit test program. Here, we're assuming that the test cases as described in the previous assignment are stored in a text file called scenarios.txt.

import unittest from mancala import * class Scenario: def __init__(self, scenarioText): # Use the scenarioText (i.e. one line of the # scenarios.txt file) to initialize the following # instance variables. self.moves = a list of the bin/move numbers from the scenario self.errorName = the name of the resulting error code self.finalBins = a list of how the bins look at the end of the case self.comment = the scenario's comment class MancalaTest(unittest.TestCase): def setUp(self): self.board = MancalaBoard() self.scenarioList = a list of the Scenario objects extracted from scenarios.txt def tryMoves(self, listOfMoves): ''' You may find it helpful to use this utility method. ''' for move in listOfMoves: self.board.move(move) def testNoError(self): pass def testWrongPlayer(self): '''Something like this:''' for scenario in self.scenarioList: if this scenario is a WrongPlayer scenario: call self.tryMoves on the list of moves in the scenario and check to make sure it raises a WrongPlayerError exception. Don't forget to call self.board.reset() here or you'll be sad. def testGameAlreadyOver(self): pass def testAttemptToMoveFromStore(self): pass def testAttemptToMoveFromEmptyBin(self): pass if __name__ == '__main__': unittest.main()