Wednesday 4 March 2009

Ayo and Scala 4

Ayo is a game that played in various forms across parts of west africa. I learnt it as a kid growing up in Benin City but with a different set of rules from what the way it's played in large parts of the yoruba people. The Yorubas call it Ayo but you've probably heard of it by the more popular name of mencala. Needless to say I'm implementing the game as explained here. Find out more about the game here

This is the last installment before I switch to building a fancy interface. Today I'll go over the main class of the program - aptly called 'Game'. The game class makes use of virtually all the scala features that I've talked about so far and it is a rather long class by the standard of the other classes/objects so I'll look at it one interesting method at a time.


def start = {
val s = List(0,0)
val h = List(Set(0,1,2,3,4,5), Set(6,7,8,9,10,11))
val l = List(4,4,4,4,4,4,4,4,4,4,4,4)

move(Config(s, h, 0, l))
}


The 'start' method basically starts the ball rolling. It's the only method an external entity should ever have to call and hence it should be the only public class of the method (but what the heck). You'll notice that the method has no parameter list or even brackets (a scala feature we've mentioned in the past but didnt show an example of). Also the method calls the move method.



def move(cfg:Config):Unit = {
if(!Rules.hasHouse(cfg.getHouses)){
//print message alerting player of change of turn
move(cfg.changeTurn)
}

Board.draw(cfg.list)

println("\nPlayer "+(cfg.turn+1)+"\n"+cfg)
//get current player's choice... player can only select his own house and if it has seeds
val choice = getInput(cfg.getHouses.filter(cfg.list(_) != 0))
val (list, index) = plant(choice, cfg.list) //playout the choice

val ncfg =
if(Rules.canKeep(list(index)))
cfg.setList(list).plusStone(4).updateList(index,0)
else
cfg.setList(list)

val ocfg = houseKeeping(0, ncfg)
val pcfg = houseKeeping(1, ocfg)

if(!Rules.gameOver(pcfg.list))
move(pcfg.changeTurn)
else
println("Game Over: \n"+pcfg)
}


This 'move' method is the most important (depending on where you stand with the whole chicken before or after the egg issue). The method runs recursively until there's no move left and the game ends (if it ever really ends). First there's a check to see if the current player is in any position to make a move (that is if he has any house he can start a move from). The Board is then drawn on the screen and the user input is collected by called 'getInput' (another recursive function by the way). Once the user input is validated, the move is initiated proper by calling the 'plant' function which actually called the board class' roll function. Once the 'plant' function returns the rules are applied to see if any player can claim any seeds and then the game continues - the game attempts to switch play to the next player.

The only other function that's important at this stage is the 'housekeeping' method which is used to actually apply the rules i.e. assign users won seeds and, perhaps in the far inconceivable future, assign houses too. The fact that this method is pretty easy on the eye is one of the reeeally nice things about scala. You can create monstrousities with scala but elegant gamine (you can tell I just heard that word today) beauties.



def houseKeeping(turn:Int, cfg:Config, houses:List[Int]):Config={
//Board.draw(cfg.list)
//println(">>> "+houses)

if(houses.isEmpty)
return cfg
else{
var nConfig =
if(Rules.canKeep(cfg.list(houses.head)))
cfg.updateList(houses.head, 0).plusStone(4, turn)
else
cfg

houseKeeping(turn, nConfig, houses.tail)Mankala
}
}



On a personal note I'm somewhat completed something for once :| Seriously, this is perhaps the first thing apart from work and school that I think I've done for myself in a long time. I like it I think I'll continue. Since I'm just learning lift I dont know how much I'll be able to cook up for next week but we'll see.

Ciao!

No comments: