Skip to content

Instantly share code, notes, and snippets.

@piton4k
Created September 16, 2019 12:46
Show Gist options
  • Save piton4k/b83b784fe216a9b398e63b00f7612f8e to your computer and use it in GitHub Desktop.
Save piton4k/b83b784fe216a9b398e63b00f7612f8e to your computer and use it in GitHub Desktop.
import Board._
import scala.annotation.tailrec
case class Position(
row: Int,
column: Int
)
sealed trait Player {
def next: Player = this match {
case Player.X => Player.O
case Player.O => Player.X
}
}
object Player {
case object X extends Player
case object O extends Player
val players: List[Player] = List(X, O)
}
class Board(cells: Cells) {
def get(row: Int, column: Int): Option[Player] =
cells(row)(column)
def get(position: Position): Option[Player] =
get(position.row, position.column)
def put(position: Position, token: Player): Board =
new Board(cells.updated(position.row, cells(position.row).updated(position.column, Some(token))))
def winner: Option[Player] = {
Player.players.find { player =>
shapes.exists { shape =>
shape.forall { position =>
get(position).contains(player)
}
}
}
}
def println(): Unit = {
scala.Predef.println(cells.map(_.map(_.fold(".")(_.toString)).mkString(" ")).mkString("\n"))
}
}
object Board {
type Cells = Vector[Vector[Option[Player]]]
def mkShape(from: Position, step: Position): List[Position] = {
(0 until 3)
.map { i =>
Position(from.row + step.row * i, from.column + step.column * i)
}
.toList
}
val shapes: List[List[Position]] = List(
(0 until 3).map { r => mkShape(Position(r, 0), Position(0, 1)) },
(0 until 3).map { c => mkShape(Position(0, c), Position(1, 0)) },
List(mkShape(Position(0, 0), Position(1, 1)), mkShape(Position(2, 2), Position(-1, -1)))
).flatten
}
case class GameState(
board: Board,
nextPlayer: Player
)
object GameState {
val empty = GameState(
new Board(Vector.fill(3)(Vector.fill(3)(None))),
Player.X
)
}
object TicTacToe {
def main(args: Array[String]): Unit = {
loop(GameState.empty)
}
@tailrec
def loop(state: GameState): Unit = {
state.board.println()
val position = readInput(state.nextPlayer)
val newBoard = state.board.put(position, state.nextPlayer)
newBoard.winner match {
case Some(winner) =>
newBoard.println()
println(s"$winner wins the game")
case None =>
val newState = state.copy(newBoard, state.nextPlayer.next)
loop(newState)
}
}
def readInput(player: Player): Position = {
val input = scala.io.StdIn.readLine(s"$player> ")
val Array(row, column) = input.split(" ").map(_.toInt)
Position(row, column)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment