Skip to content

Instantly share code, notes, and snippets.

@wmhtet
Forked from OlegIlyenko/TicTacToe.scala
Created November 3, 2011 00:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wmhtet/1335454 to your computer and use it in GitHub Desktop.
Save wmhtet/1335454 to your computer and use it in GitHub Desktop.
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