Skip to content
Snippets Groups Projects
Commit b2005c4b authored by William Billingsley's avatar William Billingsley
Browse files

Converted into a futures demo

parent b08b8a53
No related branches found
No related tags found
No related merge requests found
......@@ -2,11 +2,14 @@ lazy val root = (project in file(".")).
settings(
name := "Reversi",
version := "2023.0",
scalaVersion := "3.1.0"
scalaVersion := "3.2.0"
)
run / fork := true
libraryDependencies += "org.scalafx" %% "scalafx" % "20.0.0-R31"
libraryDependencies += "org.scalameta" %% "munit" % "0.7.29" % Test
libraryDependencies += "com.typesafe.play" %% "play-ahc-ws-standalone" % "2.2.0-M2"
testFrameworks += new TestFramework("munit.Framework")
......@@ -11,139 +11,55 @@ import scalafx.geometry.*
import scalafx.collections.*
import scalafx.animation.*
import scalafx.beans.property.*
import javafx.application.Platform
import java.util.{TimerTask, Timer}
object App extends JFXApp3 {
override def start() = {
val r = Rectangle(200, 200)
val b = Button("Click me")
var hue = 1
var rot = 0d
stage = new JFXApp3.PrimaryStage {
title.value = "Reversi"
width = 480
height = 600
scene = new Scene {
content = new UI().subscene
}
}
}
}
class UI() {
// A constant for how bix the squares are
val squareSize = 40
/** Represents a square on the board. */
class Square(col:Int, row:Int) {
private val square = new Rectangle {
width = squareSize
height = squareSize
fill = if (col + row) % 2 == 0 then Color.hsb(114, 0.85, 0.33) else Color.hsb(114, 0.65, 0.43)
}
val ui = new Group {
translateX = col * squareSize
translateY = row * squareSize
children = Seq(
square
)
}
def clear():Unit = {
ui.children = Seq(square)
}
def place(p:Player) = {
ui.children = Seq(
square,
new Circle {
centerX = squareSize / 2
centerY = squareSize / 2
radius = squareSize / 3
fill = if p == Player.White then Color.White else Color.Black
}
content = new VBox(
r, b
)
}
}
stage.setOnCloseRequest((_) => System.exit(0))
private val squares = for y <- 0 until boardSize yield
for x <- 0 until boardSize yield
new Square(x, y)
var game = newGame
private def showGameState(g:GameState):Unit = {
for
y <- 0 until boardSize
x <- 0 until boardSize
do
if g.board.contains((x, y)) then
squares(y)(x).place(g.board((x, y)))
else
squares(y)(x).clear()
}
val timerTask = new TimerTask {
override def run():Unit =
hue = (hue + 1) % 255
rot = (rot + 3) % 360
private def showGame(gs:Seq[GameState]):Unit = {
showGameState(gs.last)
history.items = ObservableBuffer((for
(g, i) <- gs.zipWithIndex
(p, loc) <- g.lastMove
yield
(i, loc, p)
)*)
Platform.runLater { () =>
r.fill = Color.hsb(hue, 1, 1)
r.rotate = rot
}
val history = new ListView[(Int, Player, Location)] {
orientation = Orientation.Vertical
cellFactory = (cell, data) => {
val (i, p, (x, y)) = data
val col = "abcdefgh"(x)
cell.text = s"$i. $col${y+1}"
cell.textFill = if p == Player.Black then Color.Black else Color.White
cell.onMouseClicked = { _ =>
lastTime = System.nanoTime()
game = rewindTo(game, i)
showGame(game)
}
val timer = new Timer()
timer.schedule(timerTask, 1000/60, 1000/60);
cell.style =
if i % 2 == 0 then "-fx-background-color: #1D7A12;"
else "-fx-background-color: #1A6E10;"
}
b.setOnAction((_) =>
new Thread(() => {
try {
Thread.sleep(10000);
} catch {
case _ => // ignore
}
}).start();
private def step():Unit = {
println(s"Lookahead ${lookAhead.value.value}")
game = play(game, lookAhead.value.value)
showGame(game)
}
private var lastTime = 0L
private val playing = BooleanProperty(false)
private val lookAhead = new Spinner[Int](0, 5, 0)
AnimationTimer({ now =>
if playing.value && (now > lastTime + 1e9) then
lastTime = now
step()
}).start()
private val startStop = new Button {
text <== (for p <- playing yield if p then "Stop" else "Start")
onAction = { _ => playing.value = !playing.value }
}
val subscene = new HBox(
new VBox(
new Group(squares.flatten.map(_.ui).toSeq*),
new HBox(5, startStop, Label("Lookahead"), lookAhead)
),
history
)
}
}
package cosc250.reversi
import scala.util.*
import scala.concurrent.*
import scala.concurrent.ExecutionContext.Implicits.global
@main def futuresDemo() = {
val promise = Promise[Int]()
val future = promise.future
println(s"Before the future is completed. Future is completed ${future.isCompleted}")
future.onComplete { case Success(v) => println(s"Future completed with value $v") }
promise.success(2)
//println(s"After the future is completed. Future is completed ${future.isCompleted}")
}
package cosc250.reversi
import scala.collection.immutable.Queue
enum Player:
case Black
case White
/** The board size is always 8 by 8 */
val boardSize = 8
/** A location on the board. Zero-indexed */
type Location = (Int, Int)
/**
* The state of the board
*
* @param lastMove - the location of the last move
* @param board - maps the locations of pieces on the board (note that if a piece has not been played in a square, it won't be in the map)
* @param turn - whose turn it is next
*/
case class GameState(lastMove:Option[(Location, Player)], board:Map[Location, Player], turn:Player) {
/** The number of black pieces */
def blackPieces:Int = ???
/** The number of white pieces */
def whitePieces:Int = ???
/** True if neither player can play a move */
def gameOver:Boolean = ???
/** Whether a particular move is valid */
def isValidMove(location:Location):Boolean =
???
/** Performs a move */
def move(location:Location):GameState =
???
// Other methods you write
}
object GameState {
def newGame = GameState(None, Map.empty, Player.Black)
}
/** A game is a sequence of game-states (so it remembers past moves). The most recent move is at the end. */
type Game = Seq[GameState]
/** Creates a new game, containing just the start game state */
def newGame:Seq[GameState] = Seq(GameState.newGame)
/** Called by the UI on each animation tick to make your AI play the game */
def play(state:Seq[GameState], lookAhead:Int):Seq[GameState] =
???
/** Called by the UI when the user clicks back in the game histry */
def rewindTo(state:Seq[GameState], move:Int):Seq[GameState] =
???
package cosc250.reversi
import akka.actor.ActorSystem
import akka.stream._
import play.api.libs.ws._
import play.api.libs.ws.ahc.StandaloneAhcWSClient
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.Random
import scala.concurrent.Future
@main def wsmain(): Unit = {
// A little boilerplate - this is needed by the web client we're using
given actorSystem:ActorSystem = ActorSystem()
given materializer:Materializer = SystemMaterializer(actorSystem).materializer
// Now we can create our web client and make a request
val wsClient = StandaloneAhcWSClient()
def zen():Future[String] =
val randomStr = Random.nextString(4) // Makes sure we don't get a cached reply
wsClient.url(s"https://api.github.com/zen?$randomStr").get().map(_.body)
def nZens(n:Int):Future[Seq[String]] = {
val futures:Seq[Future[String]] = for
i <- 1 to n
yield zen()
Future.sequence(futures)
}
val f = for
first <- zen()
second <- zen()
yield s"$first, and then $second"
// Print this statement immediately
println("This prints immediately")
}
\ No newline at end of file
......@@ -9,23 +9,4 @@ package cosc250.reversi
*/
class ReversiSuite extends munit.FunSuite {
// A place for you to write tests. Some suggested tests to start with have been sketched below
test("Counts pieces") {
assertEquals(2, GameState(None, Map((3, 3) -> Player.Black, (3, 4) -> Player.Black), Player.White).blackPieces)
assertEquals(2, GameState(None, Map((3, 3) -> Player.White, (3, 4) -> Player.White), Player.White).whitePieces)
// add some more!
}
test("Should be able to detect if a move is valid") {
???
}
test("Should be able to count the score for one player") {
???
}
// You'll need to write some additional tests
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment