Showing posts with label ayo. Show all posts
Showing posts with label ayo. Show all posts

Friday, 23 July 2010

Ayo game using scala actors


Disclaimer:
This post is an introduction to Scala Actors. It's written in such a way that you'll need very little knowledge of scala and you don't really have to know how to play the game 'ayo' either (a variation of this game is also called mancala - I think). But if you want to know more about the game before starting visit this bbc post. If you want an introduction to scala, you are better off visiting scala language's home page




First off, the scala eclipse ide (for scala 2.8) has taken a HUGE leap forward since the last time I tried it. It's now good enough, I think, to be used in place of intellij's idea - the only thing I have against idea is that it is plain ugly. Even NetBeans looks like a pretty bride in mini-skirt beside it. In any case let's get serious...


Actors are scala response to multi-threaded programming. I hear they were borrowed from haskell or some other fancy language but if that's why you are here... there's an... a google for it. The cool thing about Actors is that it takes away the abstraction of threads and leaves you with fancy-dandy sending and receiving of messages.


package actor {

import scala.actors.Actor
import Actor._


The first thing to note about actors is the classes to import. Basically you import scala.actor.Actor and then you import the methods and classes within the 'Actor' class itself.


//messages to be sent to Actors
case object StartGame
case class Play(board:List[Int])
case class Stoned(count:Int)
case class Move(startAt:Int, player:Player)
case class GameOver(message:String)


Next, we declare the messages that we'll be sending to and from Actors. The usual thing is to use case classes/objects as Messages because the plumbing for pattern matching is built into them (Any class - string, int, etc - that supports case matching would work though). So we've got, for instance, 'StartGame' that represents a message we'll be sending to Referee to start the game. The others should be easy to guess. Alternatively, we might talk about them later.


class Player(val name:String) extends Actor{
var stones = 0

def act = loop {
react {
case Play(board) => reply(Move(getUserInput(board), this))
case Stoned(count) => stones += count
}
}

def getUserInput(board:List[Int]):Int = {
val choices = board.zipWithIndex.filter(_._1 != 0).map(_._2)
print(
"Player "+name+"'s choice "+choices.toString+": ")
val choice = readInt

//verify that the user hasn't select a 'zero' hole
if(!choices.filter(_ == choice).isEmpty)
choice
else{
println(
"Invalid choice :(")
getUserInput(board)
}
}
}


This perhaps is where the real fun begins - We define a Player. A player is used to getting user input and then sending a message to the Referee containing the player's input. The player would of course only get input when a Referee demands it. To turn a player into an Actor we simply extend 'Actor'. The next thing is that we have to write an 'act' function. In the 'act' function you would need a loop of some sort so that the 'Actor' doesn't quit listening after processing just one message. Fortunately, there's a built in 'loop' construct (note, I'm saying construct because I don't know if it's a function or an object or some other voodoo scala thingy) that we can use. In side the 'loop' you define a 'react' function with matching cases for each message that you want the actor to handle. In 'Player' we want to handle messages to increase the number of stones that the player has captured (Stoned) and we want the 'Player' to know when to make a choice on where to start his next move from (Play).


'reply' is a function that sends a response back to the Sender of a message. So we don't really have to know who sent the message, we can simply process the message and use the 'reply' function to send a response back to the whoever sent the message in the first place. For instance, when the 'Player' gets a 'Play' message from the 'Ref' the 'Player' simply calls 'reply(Move(getUserInput(board), ...))' so that a 'Move' message, containing the player's choice, is sent back to the 'Ref'.


As an aside, you'll notice that there's a 'getUserInput' function that actually does the job of getting the user's input. This method receives a list of stones corresponding to holes that the user currently owns (no pun intended). Basically, we keep requesting for the user's input until the user enters a choice that's valid. This way we get players that are honest to a fault - no Thierry Henry scoring hand goals or Luis Suarez' stopping clear cut goals with their hands - and apparently we can have even Graham Poll as the ref since we've removed the risk of a player receiving 1, talk less of 3, yellow cards.


Below we have the Referee or 'Ref'. I'll include comments within the class definition (remember that you can hide all the other with that link above) just so I don't lose my mind before this is all done.


object Ref extends Actor {
import scala.collection.mutable.Buffer
var players:List[Player] = List(
new Player("one"), new Player("two")) //always assumes 2 players
var holes:Buffer[(Int, Player)] = {0 to 11}.map(i => (4, players((i/6).toInt))).toList.toBuffer //always assume players have half the board to start with



We declare/define the total number of players as well as the holes (no pun intended... again!) here. Nothing special there, move along.



def act = {
loop {
react {
case Move(startAt, player) => { //TODO logic to carry out actual move and to keep score
move(startAt, -1, player)
val p = nextPlayer(player)
p ! Play(board(p))
}
case StartGame => {
players.foreach(_.start)
val p = nextPlayer(
null)
p ! Play(board(p))
}
case GameOver(s) => exit
}
}
}




So, the Ref is also an 'Actor'. We got that by having the 'Ref' extend an 'Actor'. This also means the 'Ref' has to have an 'act' method which we have duely done above. The most important thing here is that the Ref can respond to three messages - StartGame (which the application itself sends to it), GameOver - which the Ref sends to himself (ahhh... the thought of female referes), and Move - which is the response a player sends to the 'Ref' after you, the end user, have made up your mind what hole to start your move from.


It's easy at this point to see that this game is not complete yet. What's important is to keep the goal before your eyes and not succomb to the whole moving target chirade. In our case, the goal is understanding 'Actor' concept. Having a complete game is a side effect - a bonus at best! That said, the game - work!, it should! But I digress... again

    
/****
*
@params stonesLeft -1 means start counting, 0 means stop counting, > 0 means keep counting
****/

def move(startAt:Int, stonesLeft:Int, player:Player){
val hole = holes(startAt)
val next = (startAt + 1)%12

if(stonesLeft == 0){
var start =
if(startAt - 1 < 0) 11 else startAt - 1
var h = holes(start)

if(h._1 == 1)
Unit
else if(h._1 == 4){
player ! Stoned(4)
holes(start) = (0, player)
}
else
move(start, -1, player)
}
else if(stonesLeft == -1){
val stones = hole._1
holes(startAt) = (0, hole._2)
move(next, stones, player)
}
else{
holes(startAt) = (hole._1 + 1, hole._2)
move(next, stonesLeft - 1, player)
}
}



As far as the game is concerned, 'move' is the most important function of the game. It contains logic about carrying out the counting of the game, and capturing houses or holes (now that I think about it houses is a better word that holes but the damage is done).

stonesLeft is actually the number stones the player currently has in his/her hands. So if the user has 0 stones in her hands it's time to take stock

  • if where she stopped has 4 stones then she captures that hole as well as the 4 stones in that hole

  • if where she stopped had no stones then her move is complete and the next player gets to play

  • if where she stopped has neither 0 nor 4 stones then she pick up the stones from where she stopped and starting another round of counting


Actually, the explanation above is a little misleading because it's the Ref that does the counting on behalf of the 'Player'. The 'Ref' can send messages back to the Player like 'Stoned(4)' to tell the player that she has captured 4 more stones.



def nextPlayer(player:Player) = {
draw
scores
players.filter(_ != player)(0)
}

//board is a list of user stones in holes i.e. if the user doesn't own a hole, zero stones appear
def board(player:Player):List[Int] = holes.toList.map(x => { if(x._2 == player && x._1 != 0) x._1 else 0 })

def draw(){
holes.takeRight(6).reverse.foreach(hole => print(hole._1 +
"\t"))
println
holes.take(6).foreach(hole => print(hole._1 +
"\t"))
println
}

def scores(){
players.foreach(p => println(
"Player "+p.name+" has "+p.stones+" stones"))
println
}
}

object Ayo
extends Application{
Ref.start
Ref ! StartGame
}
}



One of the most important bits to the whole 'Actor' magic is the 'start' function call. You've got to call start on an 'Actor' in order to get the party rolling. In the main class 'Ayo' above we not only start the 'Ref' actor using 'start' but we also send a 'StartGame' message to the 'Ref' actor. When the 'Ref' receives the 'StartGame' message, it in turn calls 'start' on all the 'Player' instances and then sends a 'Play' message to the first Player instance. All in all, Actors sure bits Threads in java. One last thing though, all the messages we've been sending were done using the ! (bang). Messages sent in this way are asynchronous. In other words, the 'Ref' doesn't block for a response from the 'Player'. Instead he continues doing whatever else he can do (in our case mostly nothing) until he receives the response from the 'Player'. To send synchronous messages use !? instead of !.




And that's all folks.

I'm convinced that I should create a second blog for posts like this but I'm too lazy to maintain 2 blogs. Time (and hits - anything more than 5 hits and I'm creating a new blog) would tell - this here blog is kind of sacred to me, you know ;)

Saturday, 28 February 2009

Ayo and Scala 3.5

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.

Last time around I gave a quick overview of the scala implementation. Today I'll be looking at each component in a little more detail starting with the board. The board of ayo is made up of 12 holes, 6 on each side. Each one of these 12 holes has 4 seeds to start with. Each player also 'owns' all the houses (6) on one side of the board.

Here's a list of what I've design the board to be able to do:

- draw itself out to the console
- carry out a complete sequence of moves

So how do we implement this in scala?


object Board {


A scala object is a singleton class. So one rather crude and incorrect way of looking at a scala object is that you are looking at a java static class.


Declarations in scala are done using the def, val, and var keywords among other stuff. 'def' is used to define a method. In the case where a method takes no parameters you can leave the () brackets off the definition of the method. 'val' defines what can be called, in java parlance, a constant. Variables defined with 'val' can never be reassigned to another value. Then there's the 'var' keyword that defines regular good old fashion variables. These can be reassigned and all that...

There'd be no point in using scala if it didnt have a good enough library of containers among other things. The cool thing about scala is that it not only has libraries of its own but it also can leverage java libraries transparently - kind of like what python is to c/c++. In scala though, and largely because of its cool functionaly idiosyncresis, there're 2 types of container objects - mutable and immutable (actually there are 3 packages but that's besides the point here). A very popular example of a mutable object is java string class. Nothing can be done to the class itself that doesn't result in the creation of a brand new string. Well in scala you have lists and iterable elements that have this characteristic (we'll talk about this a little more later). I've digressed... let's take a look at the code again.


def roll(index:Int, stoneBuffer:Int, list:Array[Int]):(List[Int],Int) =
if(stoneBuffer==0)
(list.toList, position(index-1))
else{
list(position(index)) += 1; draw(list.toList)
roll(index+1, stoneBuffer-1, list)
}


The roll function is a recursive one. The signature of the method is that had to grasp. Where java would say int i, scala would say i:Int. So this is a method that takes 3 parameters. The method returns a 2 tuple containning an array and an integer. The contents of the tuple actually are the stones as they are arranged on the board after a sequence of moves and the position where the sequence of moves ended.
You'd also notice that the method contains just one if block and that there are no return statements. Well, that's because in scala all expressions are ... ok I dont know how to put it but by default the last expression in a method is returned. So one of the last statement in each of 'if' block's conditional subunits gets returned when the method is called.

What the roll function actually does is to check if there are any stones left to move about, if there isn't it returns the board and the current location. Otherwise, it adds one stone to the current location, reduces the number of stones left to move by one and then calls itself again... simple!

Next stop is the Rules object.

object Rules {
def canMove(plantedSeeds:Int) = plantedSeeds!=0
def canKeep(plantedSeeds:Int) = plantedSeeds==4
def hasHouse(houses:Set[Int]) = !houses.isEmpty
def gameOver(list:List[Int]) = list.reduceLeft(_+_) < 4
}

One thing you may have noticed is that I appear to be passing around a lot of excess luggage from function to function. Well that, I've heard, is one of the features of functional programming. Functional programming avoids side effects - which means I only deal with what the function returns and not with any changes that the function may have made to the parameters that I passed to it. One way of implementing this is to make sure that only immutable objects are used and passed along from method to method. That way I'm sure that if I sent you a parameter you wont be able to change it and all i'll have to worry about is what you've passed back to me. Consequently it's becomes pretty easy to test methods as everything is easily compartmentalized.

The Rules object basically just contains the rules of the game. For instance a player can only start a move (canMove) when there are seeds in the hole from which he wants to start the move. But this is an incomplete rule because a player cannot start a move from a house he doesn't own (Perhaps this is a design flaw).

Now it makes perfect sense to use singletons for both the Rules and the Board classes because both classes dont really have any fields. In essence, I'm trying my best to make this a fully working functional game (I think I wont succeed though, it's so paaaaainful). The other methods in the Rules object appear to be straight forward or in the words of one of my fav college teachers - "think small" (go figure)

Now let's talk interesting stuff

There's a class that stores the current configuration of the game. It's called Config. This class is a case class (what?). Well, in scala there's this cool type of class you can create that will authomatically create a toString method and comparison (equals/compareTo) method that works off of the toString method (I may be wrong here but's that how I understand it). The major advantage of this is that you can do some pattern matching on this class and generally write less code. So we define a case class that actually stores the seeds won by each player, the houses won by each player, the player whose turn it is to play and the arrangement of the stones left over on the board.
Now you cant change the values of the fields of a case class without really running into trouble since the toString class is created at class initialization (I need to verify this... but when I started learning scala way back on 1.6 or thereabout this appears to have been the case). In any case, each method of this config class that appears to modify any of its fields actually creates and returns a brand new Config object (cool eh?).

The constructor of a class in scala is the same of what appears to be the parameter list of the class itself, so in our case stones:List[Int], houses:List[Set[Int]], turn:Int, list:List[Int] are the parameters of the constructor of the Config class. The methods of this class are pretty straight forward so I wont be talking about them at all.


case class Config(stones:List[Int], houses:List[Set[Int]], turn:Int, list:List[Int]){

def plusStone(count:Int):Config = plusStone(count, turn)
def plusStone(count:Int, player:Int) = {
val s = stones.toArray
s(player) += count
Config(s.toList, houses, turn, list)
}
.
.
.
.


Ha! That's all for this week folks. I'll talk about how it all comes together when I look at the last part of the game (an object called 'Game') next week maybe.

Monday, 28 July 2008

AYO in SCALA 2

In case you have not noticed I've technically blogged as many times as I did last year so I guess congrats are appropriate. Ok, on to more serious things. Today, like I promised yesterday, I'll be going over the ayo code that I pasted yesterday. I'll start with a brief explanation copied verbatim from http://www.bbc.co.uk/dna/h2g2/A833816 below. One more thing, I'm still learning the whole blogging idea (the only reason I'll do something as crazy as blogging is b/c I've heard that I might become a better person by blogging) so please forgive the issues with the fonts and text-alignment.


The Objective of the Game

At the start of the game, each player 'owns' the six houses on his side of the board, and 24 tokens, distributed four to each house.

Player 2

House 11

House

10

House

9

House 8

House 7

House 6

4

4

4

4

4

4


4

4

4

4

4

4

House 0

House 1

House 2

House 3

House 4

House 5

Player 1

During a round of play, the players can win and withdraw from play tokens in groups of four, each group gaining him possession of a house for the next round. At the end of the round, a player who has increased his share of the tokens, has an increased number of houses under his control and has therefore gained an advantage.

Any player who wins all of the tokens during a round has won the game.

Rules of Play

Players draw lots to see who will start the first round; subsequently they alternate.

At his turn, if a player has no tokens in any of his houses, play reverts to his opponent. Otherwise, he must select one of his houses and pick all the tokens in it. Proceeding anti-clockwise, one token is placed in each of the succeeding houses. Excepting the play of the final token, if the number of tokens in any house is made up to four, those four tokens are won and withdrawn by the player owning the house. At the play of the final token, there are three possibilities:

  • If the house was empty, the turn is ended.

  • If the house is made up to four tokens, the player wins and withdraws those tokens irrespective of who owns the house. Again play reverts to the opponent.

  • Excepting the above, the player picks the contents of the last house and continues his play.

The player who wins the penultimate (11th) group of four tokens also wins the last.




Ok, now if you still don't know the details of the game, well, click on that link above and read the entire write-up. Otherwise, let's get our hands with some scala and ayo shall we?


Initialization

  • select '0' as the current player

  • make sure each player has no captured stones to start with

  • arbitrarily give the names: “him” and “her” to players 0 and 1 resp.

  • put 4 stones in each hole.

The main algorithm is ...


var holes = List(4,4,4,4,4,4,4,4,4,4,4,4)
var capturedStones = Array(0,0)
var names = Array("him", "her")
var turn = 0

while(canMove(turn, holes)){ //while the player can move
println //print out the the board
prnt(holes, names, capturedStones) //get player's move
print(names(turn)+": ")
val pos = readInt
if(isValidMove1(turn, pos)){ //if the move is invalid goto step 1
var result1 = move1(turn, pos, holes) //make the move
var result2 = cleanup(turn, result1._1, result1._2) //check for any captured stones
capturedStones(0) += result2._1 //store the result of the move
capturedStones(1) += result2._2
holes = result2._3
turn = nextPlayer(turn) //switch players
}
}


I've added comments at the appropriate positions and I assume this part should be easy enough to follow, so I'll not go into any details here. Instead I'll pick the important parts of the code from now on.

  • canMove...

    Each move starts from one of the holes that belong to a player. So for a player to be able to move, he has to have at least one non-empty pocket. canMove is the function that implements this check. It determines the pockets that belong to a user and then uses the filter method on the List of pockets that remain. Since I'm trying to stick more to the functional paradigm in this code I've decided to stick with immutable List - this means, among many other things, that there will be no side effects in any of my functions (did I hear somebody mutter something about Demeter?). One of the benefits of using List in scala is that there are all these cool methods that the List class has that allows you to perform operations on all the elements of a list without changing the contents of the list itself. In canMove, I use the 'filter' method. Like the names says filter will return all the elements of a list that meet a criteria (the criteria is defined in a function that takes one argument – remember that functions are first class objects in scala). Since we want to check to see if there a non-empty pockets, we pass an anonymous function that simply checks each passed in value to see if it is greater than zero. 'x=>x>0' is the anonymous passed in function in question. Scala purist will be quick to point out that I could have typed '0<' as the function. But I'm from a java background and I still crave some form of verbosity in my code. However for kicks here is an alternative to the canMove function in the code below:

  • def canMove(player:Int, holes:List[Int]) = playerHoles(player,holes).filter(0<).isEmpty


    • prnt

      This function prints out the board of the game. This method makes use of reverse function of scala's List class. A more interesting method though is foreach. This method carries out a specific operation (defined in a method) to each element of the List. In our case, the operation is a simple print.

      PROGRAM CORE

      The next 3 methods are the most important as far as this program is concerned. The first thing to notice in these method is that each statement in scala potentially returns something. In fact, but for the fact that I'm scared that I might be wrong I'll say every statement in scala returns something – of course that something can be 'Unit' a value that can be thought of as 'void' for those of us java dudes. The logical conclusion of the above statements is that 'if' statements can return values. This infact is one of the reasons there's no ternary operator in scala: the 'if' statement in scala returns a value. Extensive use of this fact is made in the following 3 methods...

    • move1

      move1 is the first move made by a player when her turn comes around. So basically we'll check to make sure the player is starting from a pocket that belongs to her.

      Once this is verified, we make sure that we remove all the stones in the pocket we are starting from and start putting the stones, one at a time, into the succeeding pockets. This second type of move – putting the stones one at a time into succeeding pockets – is refered to as move2

    • move2

      move2, the second type of move, simply puts one stone in the a succeeding pocket and calls itself if there's still a stone left. However, at the commencement of this function, the board can be in 3 states.

    1. There are no stones left and the last pocket in which a seed/stone was dropped contains exactly either 4 or 1 stone (read the rules of the game at the start of this...). In this case, the method returns the a Tuple that contains the last position a stone was dropped in and the current configuration of the board

    2. There are no stones left but the last pocket in which a seed/stone was dropped contains neither 4 nor 1 stone. In this case, we pick all the stones in this previous pocket and use it to continue the move2 process.

    3. There are stones left. This obviously is the simplest case and we simply continue doing what we've been doing before ;)

      Before we go to the last function I'll talk about here, let's talk a little about what a Tuple is. A tuple is like a list but obviously it's not a List. I don't know that I can explain what it is in full but I'll say here that 2 very important differences between a list and a Tuple is that a tuple can be created by simply listing the items you want in the tuple within a bracket e.g. ('mi','shel') and, second, you access the items in the tuple using a syntax like tupleName._1, tupleName._2 (note here that the first element of a tuple is index with _1 not _0).

    • cleanup (please read the disclaimer above before you read this section)

      This function checks to see if the players have any stones that they can capture using the rules explained above. The interesting scala function here is the foldRight function. In the example, there's a little of currying (which I wont talk about), but I'll explain what a foldRight basically does (an almost impossible task without explaining currying). Well, foldRight function basically performs a operation on a pair of values. It then repeats that operation on the next value in the list and the result of the previous iteration. The first bracket pair, (0), passes the initial value that will be used as the first call of foldRight since of course there isnt a previous iteration result. But I'm not been totally truthful here. The fact that the first bracket pair, (0), is followed by another bracket pair, ((x,y)=>{ if(x==4) x+y else y}), simply shows that the foldRight(0) returns a function (this in essence is what I think currying is). This second bracket pair therefore is us passing a function literal, '(x,y)=>{ if(x==4) x+y else y}', to the function returned by foldRight(0). This returned function takes a function that accepts two parameters as it's argument and returns a value of the same type as the passed in parameters. In our case, the passed in function literal returns the sum of the passed in variables. In summary our foldRight is used here to sum the elements of the List. Doubtless, there are many ways to catch a fish (well many ways to perform the sum operation) and foldRight is just one of them

    • That's all folks! I'll do a part 3 if ever I decide to make a web-based fancy port and also there are still one or two more steps left. For instance “The player who wins the penultimate (11th) group of four tokens also wins the last”.

    • On a final note, here's the updated code from yesterday. Once I understand how blogger supports uploads I'll remove the code and put it as an upload.



    package ayo;

    object Game {
    def nextPlayer(player:Int) = (player+1)%2
    def playerHoles(player:Int, holes:List[Int]) = if (player==0) holes.slice(0, 6) else holes.slice(6, 12)
    def canMove(player:Int, holes:List[Int]) = playerHoles(player,holes).filter(x => x>0).length > 0
    def isValidMove1(player:Int, position:Int) = (player==0 && position<6) player="="1">5)
    def nextPos(pos:Int) = (pos+1)%12
    def previousPos(pos:Int) = (pos+11)%12

    def map(holes:List[Int], changes:List[Int], nuValue:Int=>Int):List[Int] =
    {for(i <- 0 until holes.length) yield if(changes.contains(i)) nuValue(i) else holes(i)}.toList def move1(player:Int, pos:Int, holes:List[Int]):Tuple2[Int, List[Int]] = if(!isValidMove1(player:Int, pos:Int)) (pos, Nil) else move2(player, nextPos(pos), holes(pos), map(holes, List(pos), x=>0))

    def move2(player:Int, pos:Int, stones:Int, holes:List[Int]):Tuple2[Int, List[Int]] =
    if(stones == 0 && (holes(previousPos(pos))<=1 || holes(previousPos(pos))==4)) (previousPos(pos), holes) else{ if(stones==0 && holes(previousPos(pos))>1)
    move2(player, pos, holes(previousPos(pos)), map(holes, List(previousPos(pos)), x=>0))
    else
    move2(player, nextPos(pos), stones-1, map(holes, List(pos), holes(_) +1))
    }

    def prnt(holes:List[Int], names:Array[String], stones:Array[Int]){
    print("\n|")
    playerHoles(1, holes).reverse.foreach(x => print("\t"+x+"\t|"))
    print(" "+names(1)+": "+stones(1)+"\n|")
    playerHoles(0, holes).foreach(x => print("\t"+x+"\t|"))
    print(" "+names(0)+": "+stones(0)+"\n")
    }

    def cleanup(player:Int, end:Int, holes:List[Int]):Tuple3[Int, Int, List[Int]] = {
    var playerCount = if (holes(end)==4) 4 else 0
    val nuHoles = {for(i<-0 until holes.length) yield if(end==i && playerCount!=0) 0 else holes(i)}.toList playerCount += playerHoles(player, nuHoles).foldRight(0)((x,y)=>{ if(x==4) x+y else y})
    val nextPlayerCount = playerHoles(nextPlayer(player), nuHoles).foldRight(0)((x,y)=>{ if(x==4) x+y else y})

    if(player==0)
    (playerCount, nextPlayerCount, nuHoles.map(x => if(4==x) 0 else x))
    else
    (nextPlayerCount, playerCount, nuHoles.map(x => if(4==x) 0 else x))
    }

    def main(args : Array[String]) : Unit = {
    var holes = List(4,4,4,4,4,4,4,4,4,4,4,4)
    var capturedStones = Array(0,0)
    var names = Array("him", "her")
    var turn = 0

    while(canMove(turn, holes)){ //while the player can move
    println //print out the the board
    prnt(holes, names, capturedStones) //get player's move

    print(names(turn)+": ")
    val pos = readInt

    if(isValidMove1(turn, pos)){ //if the move is invalid goto step 1
    var result1 = move1(turn, pos, holes) //make the move
    var result2 = cleanup(turn, result1._1, result1._2) //check for any captured stones

    capturedStones(0) += result2._1 //store the result of the move
    capturedStones(1) += result2._2
    holes = result2._3
    turn = nextPlayer(turn) //switch players
    }
    }
    }
    }