Когда я был школьником, давным-давно, самым постоянным предметом моего увлечения программированием был ИИ для «Connect Four» (помимо большого количества генетического алгоритма и нейронных сетей, вздох). Я очень хорошо помню тысячи строк кода и недели, которые я потратил, пытаясь сделать это правильно (используя Turbo Pascal), и чтобы я мог победить меня в игре. Тем не менее, я боюсь, что игра стала лучше, чем мой ИИ.
Сегодня я знаю, что «Connect Four» — это решенная проблема . Тем не менее, я думаю, что это хорошая площадка для программирования. И так как я хотел узнать, как далеко я продвинулся со своими навыками разработки, так как я был школьным, я решил проигнорировать эту исследовательскую работу и использовать «Connect Four» в качестве игровой площадки для того, чтобы немного опробовать Akka и ScalaFX праздники — если вы хотите пропустить вперед, вы найдете результаты в Bitbucket .
Итак, вот блог о том, как я начал со ScalaFX в этом маленьком проекте.
Первые шаги к началу работы с ScalaFX были не сразу понятны для меня. Существует учебное пособие о том, как начать работу с ScalaFX в Eclipse , но для его использования из SBT я, к счастью, вспомнил выступление Herbstcampus от @phdoerfler . Вы можете найти (немецкий) слайды и файл сборки SBT здесь . Оказывается, это довольно легко.
Первое, что вам нужно знать о ScalaFX, это то, что он, к сожалению, нигде не публикуется. Это означает, что вы должны опубликовать его локально, чтобы иметь возможность использовать его в качестве библиотеки.
Для этого нужно установить Mercurial, если у вас его еще нет, а затем клонировать репозиторий ScalaFX:
hg clone https://code.google.com/p/scalafx/
Затем вы должны упаковать и опубликовать ScalaFX. Если у вас также установлен git, то это должно сработать:
sbt publish-local
Однако, если у вас не установлен git, вы должны project/plugins/project/PluginBuild.scala
сначала удалить файл, который ссылается на плагин sbt (используемый для test
цели), клонированный из git. Этот плагин на самом деле не требуется для локальной публикации ScalaFX.
Теперь вы можете ссылаться на ScalaFX в вашей собственной сборке SBT — просто добавьте ScalaFX в качестве зависимости от библиотеки, а также добавьте среду выполнения JavaFX (исходя из локальной установки Java 7, если она обновлена, как и должно быть), как зависимость:
libraryDependencies += "org.scalafx" % "scalafx" % "1.0-SNAPSHOT" unmanagedJars in Compile += Attributed.blank( file(scala.util.Properties.javaHome) / "lib" / "jfxrt.jar") fork in run := true
Как вы заметили, я также сказал run
команде разветвляться, чтобы избежать « UnsatisfiedLinkError
из-за различных« магий »загрузчика классов…
Что касается кодирования: для моей платы Connect Four я хотел начать с панели внутри изменяемого размера окна, где я просто сам делаю макет, в основном рисуя плату с помощью фигур ScalaFX вместо использования Graphics2D. Сначала я думал, что это будет легко, но потом я трачу много времени на одну деталь: а именно, как заставить пользовательский интерфейс ScalaFX изменять размеры с окном. Кстати, именно это породило этот пост в блоге.
Мой основной класс начинался довольно просто, как и любое приложение ScalaFX:
import scalafx.Includes._ import scalafx.application.JFXApp import scalafx.stage.Stage object Main extends JFXApp { stage = new Stage { title = "Akka Connect Four" width = 800 height = 600 } }
Это готовит почву, и это все. — Затем вы должны добавить граф сцены на сцену. Обычно вы делаете это, просто добавляя scene = new Scene { ... }
на сцену. Но это именно то, что сломало мне шею, пытаясь изменить размер сцены вместе с окном. И причина этого в том, что ScalaFX внутренне помещает JavaFX «группу» в качестве корня сцены здесь, а не в отдельный компонент.
Использование одного компонента в качестве корневого для графа сцены ScalaFX, к сожалению, не очень хорошо поддерживается ScalaFX. Единственный способ, которым я смог заставить это работать, это сделать это так, напрямую создавая сцену через JavaFX:
import scalafx.Includes._ import scalafx.application.JFXApp import scalafx.stage.Stage import scalafx.scene.Scene import scalafx.scene.layout.BorderPane object Main extends JFXApp { stage = new Stage { title = "Akka Connect Four" width = 800 height = 600 scene = new Scene(new javafx.scene.Scene(root)) } lazy val root = new BorderPane {} }
Я использую BorderPane
здесь в качестве контейнера, чтобы позже я мог разместить дополнительные вещи вокруг своей платы Connect Four.
Далее, давайте добавим прямоугольник основной платы в центр корневой панели.
import scalafx.Includes._ import scalafx.application.JFXApp import scalafx.stage.Stage import scalafx.scene.Scene import scalafx.scene.layout.{ Pane, BorderPane } import scalafx.scene.shape.Rectangle import javafx.scene.paint.{ Color => JFXColor } object Main extends JFXApp { stage = new Stage { title = "Akka Connect Four" width = 800 height = 600 scene = new Scene(new javafx.scene.Scene(root)) } lazy val root = new BorderPane { center = gamePane } lazy val gamePane: Pane = new Pane { content = showBoard(784.0, 562.0) } def showBoard(paneWidth: Double, paneHeight: Double) = { val offX = 50.0 val offY = 50.0 val boardWidth = paneWidth - offX * 2 val boardHeight = paneHeight - offY * 2 new Rectangle { x = offX y = offY width = boardWidth height = boardHeight fill = JFXColor.DEEPSKYBLUE } } }
Таким образом, мы получим красивый центрированный синий прямоугольник (я не буду добавлять больше деталей для этого поста в блоге). Тем не менее, вы, возможно, уже заметили, что при изменении размера окна плата по-прежнему не меняет себя. По крайней мере, сама панель изменяет свой размер благодаря нашему трюку с корневым компонентом выше (вы можете проверить это, установив цвет фона, например, или используя Scenic View — который является отличным инструментом для отладки пользовательских интерфейсов JavaFX, между прочим ).
Чтобы получить панель для изменения размера, нам все еще нужно привязать ширину и высоту игровой панели к перерисовке нашей доски. Теперь, если в ScalaFX есть что-то действительно замечательное, то насколько просто это сделать, используя «выражения связывания» . В нашем случае мы просто хотим показывать обновленную доску каждый раз, когда изменяется ширина или высота окна. Поэтому мы хотим установить width.onChange()
и height.onChange()
.
Вот что я сделал:
import scalafx.Includes._ import scalafx.application.JFXApp import scalafx.stage.Stage import scalafx.scene.Scene import scalafx.scene.layout.{ Pane, BorderPane } import scalafx.scene.shape.Rectangle import javafx.scene.paint.{ Color => JFXColor } object Main extends JFXApp { stage = new Stage { title = "Akka Connect Four" width = 800 height = 600 scene = new Scene(new javafx.scene.Scene(root)) width onChange show height onChange show } lazy val root = new BorderPane { center = gamePane } lazy val gamePane: Pane = new Pane { content = showBoard(784.0, 562.0) } def show: Unit = { gamePane.content = showBoard(gamePane.width.get, gamePane.height.get) } def showBoard(paneWidth: Double, paneHeight: Double) = { val offX = 50.0 val offY = 50.0 val boardWidth = paneWidth - offX * 2 val boardHeight = paneHeight - offY * 2 new Rectangle { x = offX y = offY width = boardWidth height = boardHeight fill = JFXColor.DEEPSKYBLUE } } }
И это уже все: изменяемый размер игрового поля для моего интерфейса Connect Four.
И так как я все еще чертовски новичок в ScalaFX: Если вы знаете способ сделать что-либо из перечисленного выше, пожалуйста, напишите комментарий ниже!
You can also check out the source code of the Connect Four application at BitBucket. – That said, it’s not complete in any way (no parallelism at all yet, no permanent brain, nearly completely unoptimized for performance, for example).
However, I got it to being able to play on par with me within a few days of eating christmas cookies and casually hacking, and that’s awesome and depressing at the same time!