Skip to content

Instantly share code, notes, and snippets.

@lamdor
Created October 2, 2013 17:08
Show Gist options
  • Select an option

  • Save lamdor/6796988 to your computer and use it in GitHub Desktop.

Select an option

Save lamdor/6796988 to your computer and use it in GitHub Desktop.
import org.scalacheck._
object TicTackToePrintArbGame extends App {
import TicTacToeArbitraries._
import Arbitrary._
import TicTacToe._
val Some(Game9(game)) = arbitrary[Game9].apply(Gen.Params())
def show(game: Game): String = { // really ugly way
val arr = Array.ofDim[String](3,3)
def updateColumnOfRow(row: Array[String], col: Fin3, player: Player) = col match {
case I1 => row(0) = player.toString
case I2 => row(1) = player.toString
case I3 => row(2) = player.toString
}
game.moves.foreach { case Move((row, col), player) =>
row match {
case I1 => updateColumnOfRow(arr(0), col, player)
case I2 => updateColumnOfRow(arr(1), col, player)
case I3 => updateColumnOfRow(arr(2), col, player)
}
}
arr.map(_.mkString).mkString("\n")
}
println(show(game))
}
object TicTacToe {
sealed trait Player
case object X extends Player
case object O extends Player
sealed trait Fin3
case object I1 extends Fin3
case object I2 extends Fin3
case object I3 extends Fin3
type Position = (Fin3, Fin3)
case class Move(position: Position, player: Player)
case class Game(moves: Seq[Move]) {
lazy val takenPositions = moves.map(_.position)
}
@annotation.tailrec
def isValidGame(game: Game): Boolean = game match {
case Game(head :: moves) =>
val previousGame = Game(moves)
isValidMove(previousGame, head.position) &&
previousMoveIsNotSamePlayer(head.player, moves.headOption) &&
isValidGame(previousGame)
case Game(Nil) => true
}
def isValidMove(game: Game, position: Position): Boolean =
game.moves.size != 9 && !game.takenPositions.contains(position)
def previousMoveIsNotSamePlayer(player: Player, previousMove: Option[Move]) =
previousMove.map(_.player != player) getOrElse true
}
import org.scalacheck._
import org.scalacheck.Prop.forAll
object TicTacToeSpecification extends Properties("TicTacToe") {
import TicTacToe._
import TicTacToeArbitraries._
property("game of 9 moves, has 9 moves") =
forAll { game9: Game9 =>
game9.game.moves.size == 9
}
property("game of 9 moves already, has no valid moves") =
forAll { (g9: Game9, p: Position) =>
!isValidMove(g9.game, p)
}
property("for an empty game, every position is valid") =
forAll { p: Position =>
isValidMove(Game(Nil), p)
}
property("for an empty game, every move added makes a valid game") =
forAll { m: Move =>
isValidGame(Game(m :: Nil))
}
property("for different moves, every move added still is a valid game") =
forAll { dm: DifferingMoves =>
isValidGame(Game(dm.m1 :: dm.m2 :: Nil))
}
property("the same move twice, is not a valid game") =
forAll { m: Move =>
!isValidGame(Game(m :: m :: Nil))
}
property("a game of 9 valid moves") =
forAll((game: Game9) => isValidGame(game.game))
property("empty game is a valid game") = isValidGame(Game(Nil))
property("a move by the same player twice, is not a valid game") =
forAll { dmbsp: DifferingMovesBySamePlayer =>
!isValidGame(Game(dmbsp.m1 :: dmbsp.m2 :: Nil))
}
}
object TicTacToeArbitraries {
import TicTacToe._
import Arbitrary._
implicit val arbPlayer: Arbitrary[Player] = Arbitrary {
Gen.oneOf(Seq(X,O))
}
implicit val fin3Arb: Arbitrary[Fin3] = Arbitrary {
Gen.oneOf(Seq(I1,I2,I3))
}
implicit val arbMove: Arbitrary[Move] = Arbitrary {
arbitrary[Position].map2(arbitrary[Player])(Move.apply)
}
implicit val arbGame: Arbitrary[Game] = Arbitrary {
for {
moves <- arbitrary[List[Move]]
game = Game(moves)
if isValidGame(game)
} yield game
}
val allPositions: Set[Position] =
Seq(I1,I1,I2,I2,I3,I3).permutations.foldLeft(Set.empty[Position]) { (s, pos) =>
s ++ pos.combinations(2).map {
case p1 :: p2 :: Nil => (p1, p2)
}
}
def genNValidMoves(n: Int): Gen[List[Move]] = {
def go(left: Int, player: Player,
availablePositions: Set[Position] = allPositions): Gen[List[Move]] = {
if (left > 0) {
for {
nextPosition <- Gen.oneOf(availablePositions.toSeq)
nextMove = Move(nextPosition, player)
nextPlayer = if (player == X) O else X
nextAvailablePositions = availablePositions - nextPosition
nextMoves <- go(left - 1, nextPlayer, nextAvailablePositions)
} yield (nextMoves :+ nextMove)
} else Gen.value(Nil)
}
arbitrary[Player].flatMap(player => go(n, player))
}
case class Game9(game: Game)
implicit val arbGame9: Arbitrary[Game9] = Arbitrary {
for {
moves <- genNValidMoves(9)
game = Game(moves)
} yield Game9(game)
}
case class DifferingMoves(m1: Move, m2: Move)
implicit val arbDifferingMoves: Arbitrary[DifferingMoves] = Arbitrary {
for {
m1 <- arbitrary[Move]
m2 <- arbitrary[Move]
if m1.position != m2.position
if m1.player != m2.player
} yield DifferingMoves(m1, m2)
}
case class DifferingMovesBySamePlayer(m1: Move, m2: Move)
implicit val arbDifferingMovesBySamePlayer: Arbitrary[DifferingMovesBySamePlayer] = Arbitrary {
for {
p <- arbitrary[Player]
p1 <- arbitrary[Position]
p2 <- arbitrary[Position]
if p1 != p2
} yield DifferingMovesBySamePlayer(Move(p1, p), Move(p2, p))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment