-
-
Save wmhtet/1335454 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import io.Source | |
import scala.util.control.Breaks._ | |
/** | |
* Scala TicTacToe game without any side effects | |
* | |
* http://blog.aunndroid.com/2011/11/chewy-code-scala-tictactoe-part-1.html | |
*/ | |
object TicTacToe { | |
val WinCount = 3 | |
sealed trait Move | |
case object O extends Move | |
case object X extends Move | |
sealed abstract class Game[T](val board: T) | |
case class InProgress[T](override val board: T) extends Game[T](board) | |
case class Finished[T](override val board: T) extends Game[T](board) | |
case class Broken[T](override val board: T, val problem: String) extends Game[T](board) | |
case class Position(x: Int, y: Int) | |
type Board = Seq[Seq[Option[Move]]] | |
def createGame(width: Int, height: Int): InProgress[Board] | |
= InProgress(for (x <- 0 until width) yield for(y <- 0 until height) yield None) | |
def move(game: InProgress[Board], p: Position, turn:Move): Game[Board] | |
={ println("\nmove : Making a move for :"+turn+"\n==========================") | |
(game.board(p.x)(p.y), placeMove(game.board, p,turn )) match { | |
case (Some(move), board) => Broken(board, "Position was already taken by " + move) | |
case (None, board) if finished_?(board,turn) => Finished(board) | |
case (None, board) => InProgress(board) | |
} | |
} | |
def whoseTurn(game: InProgress[Board]): Move | |
={println("\nwhoseTurn : Function called\n-----------------------------") | |
game.board.flatMap(_.flatten).foldLeft((0, 0)) { | |
case ((x, o), X) => (x + 1, o) | |
case ((x, o), O) => (x, o + 1) | |
} match { | |
case (x, o) if x - o <= 0 => X | |
case _ => O | |
} | |
} | |
def whoWon(game: Finished[Board],turn:Move): Option[Move] | |
={println("\nwhoWon game turn : Function called\n==========================") | |
//val forYield= | |
(for { | |
x <- 0 until game.board.size | |
y <- 0 until game.board(0).size | |
curr <- game.board(x)(y) | |
if curr==turn | |
if won_?(game.board, curr, x, y) | |
} return Some(curr)); return None | |
//println("forYield : "+forYield) | |
//forYield.find (_.isDefined) getOrElse None//return Some(curr)); return None//yield Some(curr)) find (_.isDefined) getOrElse None | |
} | |
def playerAt(game: Game[Board], p: Position): Option[Move] = game.board(p.x)(p.y) | |
def draw(game: Game[Board]) | |
={println("\ndraw : Drawing the Board\n==========================") | |
(for (y <- 0 until game.board(0).size) yield horizMoves(game.board, 0, y) map { | |
case Some(m) => m.toString | |
case None => " " | |
} mkString " | ") mkString ("\n" + ("-" * game.board.size).mkString("-+-") + "\n") | |
} | |
private def won_?(board: Board, m: Move, x: Int, y: Int): Boolean | |
={println(m+":"+x+","+y) | |
won_?(horizMoves(board, x, y), m) || won_?(vertMoves(board, x, y), m) || | |
won_?(diagRightMoves(board, x, y), m) || won_?(diagLeftMoves(board, x, y), m) | |
} | |
private def won_?(moves: Seq[Option[Move]], m: Move): Boolean | |
= {println(m+":"+moves) | |
if (moves.size<WinCount) return false | |
moves.foldLeft(List(0)) { | |
case (count :: rest, Some(`m`)) =>println(count+"::"+rest); count + 1 :: rest | |
case (counts, _) =>println("counts : "+counts); 0 :: counts | |
}.max >= WinCount} | |
private def horizMoves(board: Board, x: Int, y: Int) | |
={println("horizMoves:"+x+","+y); for (xx <- x until board.size) yield board(xx)(y)} | |
private def vertMoves(board: Board, x: Int, y: Int) | |
={println("vertMoves:"+x+","+y); for (yy <- y until board(x).size) yield board(x)(yy)} | |
private def diagRightMoves(board: Board, x: Int, y: Int) | |
={println("diagRightMoves:"+x+","+y); | |
for ((xx, yy) <- (x until board.size) zip (y until board(x).size)) yield board(xx)(yy)} | |
private def diagLeftMoves(board: Board, x: Int, y: Int) | |
={println("diagLeftMoves:"+x+","+y); | |
for ((xx, yy) <- (0 to x by -1) zip (y until board(x).size)) yield board(xx)(yy)} | |
private def placeMove(board: Board, p: Position, m: Move) | |
= board.updated(p.x, board(p.x).updated(p.y, Some(m))) | |
private def finished_?(board: Board,turn:Move) = whoWon(Finished(board),turn).isDefined | |
} | |
object Int { | |
def unapply(s: String): Option[Int] | |
= try { | |
Some(s.toInt) | |
} catch { | |
case _ : java.lang.NumberFormatException => None | |
} | |
} | |
object TicTacToeGame extends App { | |
import TicTacToe._ | |
val game = createGame(3, 3) | |
Console.println("Welcome to Tic Tac Toe!") | |
prompt(game) | |
breakable { | |
Source.stdin.getLines.map(_.split("\\s*,\\s*").toList match { | |
case List(Int(x), Int(y)) if (0 until 3 contains x) && (0 until 3 contains y) => 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) =>{ | |
println("***") | |
val turn=whoseTurn(g) | |
move(g, p, turn) 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,turn) + " won!"); break; game | |
} | |
} | |
case _ => println("Wrong state!"); game | |
} | |
} | |
println("Bye!") | |
def prompt(game: InProgress[Board]): InProgress[Board] = { | |
print("\n" + draw(game) + "\n" + whoseTurn(game) + "> "); | |
game | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment