Last active
December 23, 2015 01:59
-
-
Save chirlo/6564276 to your computer and use it in GitHub Desktop.
TicTacToe with Either
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
sealed trait Result | |
case object X extends Result | |
case object O extends Result | |
case object Tie extends Result | |
object Game { | |
type Player = (Result, Set[Int]) | |
private val emptySet: Set[Int] = Set() | |
private val availableSquares = (1 to 9).toSet | |
private val newGame = Game((X -> emptySet, O -> emptySet), availableSquares) | |
private val winningCombinations = | |
Seq( | |
(1, 2, 3), (4, 5, 6), (7, 8, 9), //rows | |
(1, 4, 7), (2, 5, 8), (3, 6, 9), //columns | |
(1, 5, 9), (3, 5, 7) //diagonals | |
).map { case (x, y, z) => Set(x, y, z) } | |
def apply(moves: Int*): Either[Game, Result] = { | |
def rec(e: Game, moves: List[Int]): Either[Game, Result] = moves match { | |
case Nil => Left(e) | |
case x :: xs => e mark (x) fold (rec(_, xs), Right(_)) | |
} | |
rec(newGame, moves.toList) | |
} | |
//Entry point: just call Game.start(). The REPL doesn't echo back the input you give however | |
def start(g: Game = newGame) { | |
print(s"${g.players._2._1}'s turn, choose a square:") | |
val square = readInt | |
g mark (square) fold (start, printResult) | |
} | |
private def printResult(r: Result) { | |
val message = r match { | |
case Tie => "We have a tie" | |
case p => s"$p wins" | |
} | |
print(message) | |
} | |
} | |
import Game._ | |
case class Game(players: (Player, Player), freeSquares: Set[Int]) { | |
def mark(i: Int): Either[Game, Result] = { | |
val ((p1Sign, p1Squares), p2) = players swap //swap to alternate players on each round | |
val updatedFree = freeSquares - i | |
if (updatedFree isEmpty) | |
Right(Tie) | |
else { | |
val updatedP1Squares = p1Squares + i | |
val hasP1Won = winningCombinations exists (_ subsetOf (updatedP1Squares)) | |
if (hasP1Won) | |
Right(p1Sign) | |
else | |
Left(Game(((p1Sign, updatedP1Squares), p2), updatedFree)) | |
} | |
} | |
} |
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 org.scalatest.FunSuite | |
class GameTest extends FunSuite { | |
test(s"a game should not be over before 4 moves") { | |
for { | |
moves <- (1 to 9).toList.combinations(4).toList | |
} assert(Game(moves: _*).isLeft) | |
} | |
val cases = List(O -> Seq(1, 4, 2, 5, 3), X -> Seq(1, 4, 2, 5, 9, 6)) | |
for ((sign, seq) <- cases) { | |
test(s"$sign wins when he puts 3 in a row first") { | |
val result = Game(seq: _*) | |
assert(result.right.get == sign) | |
} | |
} | |
test("Tie when all squares are full and nobody has 3 in a row") { | |
val result = Game(1, 4, 7, 5, 2, 8, 6, 3, 9) | |
assert(result.right.get == Tie) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment