Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Friday, 22 October 2010

I love to...

I've learnt some really painful lessons these last few weeks. I grew up programming in turbo C and debugging was just one of those things I picked up naturally and ran with. Then I made then switch to java with it's fancy IDEs and convoluted 'frameworks' and I gave up on debugging. I still occasionally used the IDEs debugger but most of the time I just put print statements all over the place, sat back and watch the code spew out lines of messages as it self destructed. About 3 years ago, I picked up unit testing as a by product of an encryption project I worked on in software engineering class. Unit testing was easy and cool when it's a new project I'm working on and not some 10 year old architectural master piece... or so I thought.

So a little over a 6 weeks long ago my boss says dude I've got the coolest project for you. Fix up this synchronous module so it's asynchronous using JMS and using the approach taken by 'other existing asynchronous modules'. If you don't know it yet that, there, is the perfect recipe for a project from hell.

Anyhow, I spend the first one week or so reading the existing code. The next one week creating class diagrams and sequence diagrams Those 2 there (class and sequence diagrams before actually writing one line of code) make up a perfect reason to be thankful that I was too busy growing up in the 80s not dable into coding (not that there were computers or the internet available where I grew up anyway). Remember I've got about 4 weeks - and I've spent the first 2 or 3 doing nothing... well almost nothing. So, I finally start tweaking the interface. Let me just say right now that working with old code makes you appreciate senior programmers more - how they kept their sanity while managing to construe such insanity is purely beyond me. But I digress. So I finished tweaking the interface, what with sessions and requests, data objects put into memory in java code and accessed from the JSP (cue utter and complete mayhem), in about a week and was now left with the code itself.

JMS is a cool technology. It would have been cooler though were it not tied to the JEE framework somewhat (you know, kinda like the way scala actors are written in a self contained scala library). So now I had to practically create a temporary cache in memory that manage insertion and retrieval with some complex voodoo logic because the manager is a singleton amongst other reasons. I've also got to accomodate the existing code (you remember that part earlier where I said something about making sure my code changes followed the existing pattern).

After fooling myself into believing I knew what I was doing for all of one week I finally was humble enough to learn. I realized in hindsight that writing unit tests for the existing code was the smartest way to not only learn but to also make sure down the road that my changes were good enough. The nature of writing unit tests is that you end up breaking up the code into small compartments (apparently that's one of the reasons it's called unit testing - duh). Smaller compartments mean you understand the code better. I also learnt that debugging isn't a dead act after all- it's good programmers that are reducing in number. Old code will always exist... cos some smart dude will come up with a better why to write it (writing intelligent software that can create software faster would fall under this category) and there'll always be the need to maintain it (except of course you also write sofware to maintain the code in which case you'd have to maintain the software that maintains the software that maintains...). Reminds me of that one time we had to write a compiler in college. I had classmates who complained because it was a waste of time which is wierd cos I enjoyed the entire course (I didn't read for the exam and I didn't get an A because I was too busy trying to get my compiler to compile).

These last few weeks we've been talking about humility in church. Last week I told a church member, in jest, that I'm sure God was opposing me because of my pride. I guess there's some truth to that especially since he gives grace (I like to read this as style) to the humble. Humility is accepting your failures/foibles and then submitting what (who) you do not fully understand because you realize and accept that you are flawed. I finished my project 2 weeks behind schedule. I now have to struggle to meet up other goals but it's been worth it in the end. I love programming.

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 ;)