Created
April 4, 2018 18:30
-
-
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
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 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