Skip to content

Instantly share code, notes, and snippets.

@rom1dep
Created April 4, 2018 18:30
Show Gist options
  • Save rom1dep/c3b442cedd3984250f57aa6800179a10 to your computer and use it in GitHub Desktop.
Save rom1dep/c3b442cedd3984250f57aa6800179a10 to your computer and use it in GitHub Desktop.
In response to https://blog.plan99.net/reacts-tictactoe-tutorial-in-kotlin-javafx-715c75a947d2 which is a TicTacToe game written in Kotlin using JavaFX as the GUI library
import scalafx.application.JFXApp
import scalafx.application.JFXApp.PrimaryStage
import scalafx.beans.binding.Bindings
import scalafx.beans.property.ObjectProperty
import scalafx.collections.ObservableBuffer
import scalafx.scene.Scene
import scalafx.scene.control.{Button, Label}
import scalafx.scene.layout._
object Main extends JFXApp {
sealed abstract class Player(val label: String)
case object X extends Player("X")
case object O extends Player("O")
case object Nope extends Player("")
val lines = List(
(0, 1, 2),
(3, 4, 5),
(6, 7, 8),
(0, 3, 6),
(1, 4, 7),
(2, 5, 8),
(0, 4, 8),
(2, 4, 6)
)
case class Board(squares: Array[Player] = Array.fill(9)(Nope)) {
def coordinates(x: Int, y: Int): Int = x + (y * 3)
def apply(x: Int, y: Int): Player = squares(coordinates(x, y))
def copyUpdate(x: Int, y: Int, content: Player): Board = copy(squares.updated(coordinates(x, y), content))
def computeWinner: Player = {
for ((a, b, c) <- lines if squares(a) != Nope && squares(a) == squares(b) && squares(a) == squares(c)) return squares(a)
Nope
}
}
val boards = new ObservableBuffer[Board]
val gameMoves = new VBox
boards.onChange(gameMoves.children = boards.indices.map { index =>
val i = index + 1
new Button(f"Move $i") {
onAction = (_) => resetGameTo(i)
}
})
boards += new Board
private val players: Iterator[Player] = Iterator.continually(List(X, O)).flatten
val currentPlayer: ObjectProperty[Player] = ObjectProperty(players.next)
def resetGameTo(i: Int): Unit = {
currentPlayer.value = if (i % 2 == 1) X else O
boards.setAll(boards.take(i))
}
def gameGrid: GridPane = {
val grid = new GridPane()
for (x <- 0 until 3; y <- 0 until 3) {
val button = new Button {
maxHeight = Double.MaxValue; maxWidth = Double.MaxValue
hgrow = Priority.Always; vgrow = Priority.Always
text <== Bindings.createStringBinding(() => boards.last(x,y).label, boards)
disable <== Bindings.createBooleanBinding(() => {val b = boards.last; b.computeWinner != Nope || b(x,y) != Nope}, boards)
onAction = (_) => playMove(x, y)
}
grid.add(button, x, y)
}
grid
}
def playMove(x: Int, y: Int): Unit = {
boards += boards.last.copyUpdate(x, y, currentPlayer.value)
currentPlayer.value = players.next()
}
stage = new PrimaryStage {
title = "TTToe with ScalaFX"
width = 500
height = 400
scene = new Scene {
root = new BorderPane {
center = gameGrid
right = new VBox {
children = Seq(
new Label {
text <== Bindings.createStringBinding(() => boards.last.computeWinner match {
case winner@(X | O) => s"Player ${winner.label} wins!"
case Nope => s"Current player: ${currentPlayer.value.label}"
}, boards)
},
new Label("Rewind game to"),
gameMoves
)
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment