Skip to content

Instantly share code, notes, and snippets.

@xianny
Created September 5, 2016 13:27
Show Gist options
  • Save xianny/06382e3f63037fbc5f514c5d5b7ed2b0 to your computer and use it in GitHub Desktop.
Save xianny/06382e3f63037fbc5f514c5d5b7ed2b0 to your computer and use it in GitHub Desktop.
import scala.io.StdIn
object Game {
val cols = Seq("1", "2", "3")
val rows = Seq("A", "B", "C")
val default = "_"
def token(player: Boolean) = if (player) "X" else "O"
def main(args: Array[String]) = {
val gameState = squares.map((_, default)).toMap
prettyPrintGame(gameState)
run(true, gameState)
}
def run(player: Boolean, currState: Map[String, String]): Unit = {
val nextState = playTurn(player, currState)
prettyPrintGame(nextState)
findWinner(nextState) match {
case None => run(!player, nextState)
case Some("ERR") => println("Illegal game state. Ending game.")
case Some("DRAW") => println("Draw! Ending game.")
case Some(token) => println(s"Game over, player `$token` wins")
}
}
def playTurn(player: Boolean, currState: Map[String, String]): Map[String, String] = {
println("Enter square to fill, e.g. A1")
val input = readLine(s"Player `${token(player)}`: ")
if (squares.contains(input) && currState.get(input) == Some(default)) {
currState + (input -> token(player))
} else {
println("Invalid input")
playTurn(player, currState)
}
}
def findWinner(state: Map[String, String]): Option[String] = {
val xs = state.filter(_._2 == "X").keys.toSet
val os = state.filter(_._2 == "O").keys.toSet
val xWin = wins.exists(set => set.toSet.subsetOf(xs))
val oWin = wins.exists(set => set.toSet.subsetOf(os))
if (xWin && oWin) {
Some("ERR")
} else if (xWin) {
Some("X")
} else if (oWin) {
Some("O")
} else if (xs.size + os.size == state.size) {
Some("DRAW")
} else {
None
}
}
def crossX(as: Seq[String], bs: Seq[String]): Seq[String] = {
for (a <- as;
b <- bs) yield
a+b
}
def inverse(as: Seq[String], bs: Seq[String]) = {
for (i <- 0 until as.length;
a = as(i);
b = bs(bs.length - 1 - i)) yield
a+b
}
val squares = crossX(rows, cols)
val colSquares = cols.map(c => crossX(rows, Seq(c)))
val rowSquares = rows.map(r => crossX(Seq(r), cols))
val diagonals = Seq(inverse(rows, cols), inverse(rows.reverse, cols))
val wins: Seq[Seq[String]] = colSquares ++ rowSquares ++ diagonals
def prettyPrintGame(state: Map[String, String]) = {
val _rows = for (
(sqs, i) <- squares.sliding(rows.length, rows.length).zipWithIndex;
values = sqs.map(sq => state.get(sq).getOrElse("_"));
label = rows(i)) yield label ++ values
val _cols = " " ++ cols
println(_cols.mkString(" | "))
_rows.foreach(r => println(r.mkString(" | ")))
}
}
@xianny
Copy link
Author

xianny commented Sep 5, 2016

to run:

$ scala tictactoe.scala

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment