Tuesday, November 1, 2011

Chewy code : scala TicTacToe part 2


Authored by Win Myo Htet


We have not got far in the previous blog, part 1, have we? We have not even made a move yet! In the previous post, we get the object Move from  whoseTurn to be printed as a turn prompt below the board, after that we return game. We will enter the breakable now. breakable(Java's break) is not part of the Scala language but included in the Scala standard library because of the popular demand (so the story goes) starting from Scala 2.8. The lack of break in Scala before has some impact on the developers who have to code with the knowledge that there is no break (no pun intended). We will even see such impact here later.

breakable {
    Source.stdin.getLines.map(_.split("\\s*,\\s*").toList 
    match {
      case List(Int(x), Int(y)) if x < 3 && y < 3 
           => Some(Position(x, y))
      case _ => println("Invalid position, should be: x, y");
           None
    }).filter(_.isDefined).map(_.get)
      .foldLeft(game: Game[Board]) {
      case (g @ InProgress(_), p) =>
        move(g, p) match {
          case game @ InProgress(_) => prompt(game)
          case game @ Broken(_, problem) 
          => print("Problem: " + problem); prompt(g)
          case game @ Finished(_) =>
            println(draw(game) +"\n" + 
            "Game finished, " + whoWon(game) + " won!");
           break; game
        }
      case _ => println("Wrong state!"); game
    }
  }

The program is running and waiting for the input from the prompt. Once the input data in the format x,y is read from the stdin, it is converted to a collection of List type. Then, do the match to see if the List is of a collection of 2 integers where both integers are to be below 3. (Oh! I just notice that there is a bug. If the coordinates are below 0, it will crash with exception, I have looked up and found the fix in SO! I will include the fix in the code at the end of the blog.) The valid entries are used to create an object Position which is boxed in Some and returned. Anything else will result in println("Invalid position, should be: x, y") and return None(Option, Some and None is used there to avoid "the billion dollar mistake", null. Here is some reading on Option.) The line with the keyword filter said that if the valid data boxed in Option is defined, (_.get) will unbox object Position from Option and feed it into foldLeft. If the Game[Board] g, seed value, is of InProgress type and foldLeft element p of Position object, we will do the move!

def move(game: InProgress[Board], p: Position): Game[Board] 
   =(game.board(p.x)(p.y),
    placeMove(game.board, p, whoseTurn(game))) 
    match {
      case (Some(move),  board) 
      => Broken(board,"Position was already taken by "+move)
      case (None, board) if finished_?(board) 
      => Finished(board)
      case (None, board)  => InProgress(board)
}

Method move is a one liner code, sort of, because the return data is executed by the very first line where the keyword match is. The rest of the lines are part of that match's scope only. So what is match matching here? match is matching the tuple of (Option[Move], Game[Board]). (More on tuple here) placeMove, which return Game[Board], is used as a function literal (delegate in C#, no direct Java equivalent but Java's interface+anonymous inner class comes close) here.  From here, the next method to read is finished_?, which lead to whoWon. So, I have been able to follow the methods flow quite nicely and quite happy about it. Yup, the time has come and whoWon stops my progress for another day.

http://blog.aunndroid.com/2011/11/chewy-code-scala-tictactoe-part-3.html



Authored by Win Myo Htet

No comments:

Post a Comment