Created
September 16, 2019 12:46
-
-
Save piton4k/b83b784fe216a9b398e63b00f7612f8e to your computer and use it in GitHub Desktop.
This file contains hidden or 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 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