A simple SBT build for your Scalatron bot
Recently, the Scala world got richer again - we now have our own educational robot wars hacking environment: Scalatron.
Unfortunately, it’s not yet well documented or obvious how to set that environment up, except for a huuuge instructions document for IntelliJ IDEA. I mean, 13 pages?! - That just has to be doable in an easier way!
As I’m an Eclipse user and somewhat fluent with SBT, I thought I’d try to setup Scalatron with an SBT build. With the sbteclipse (and sbt-idea) plugins, I’ll then get the IDE setup for free.
So, let’s create a basic SBT build file. As I already suspect that I’ll need a build that’s a bit more complicated than a few lines of configuring standard settings, let’s start with a Build.scala file:
import sbt._
import Keys._
object Build extends Build {
val bot = Project(
id = "mybot",
base = file("."),
settings = Project.defaultSettings ++ botSettings)
val botSettings = Seq[Setting[_]](
organization := "de.johoop",
name := "my-scalatron-bot",
version := "1.0.0-SNAPSHOT",
scalaVersion := "2.9.1",
scalacOptions ++= Seq("-deprecation", "-unchecked"))
}
This simply initializes my bot project and adds a few standard Scala options I usually like to use.
Next, I want to add the dependency to the Scalatron jar file to the project. As Scalatron isn’t published anywhere, I’ll just create a lib directory and put the jar file from the Scalatron distribution in there.
Another basic thing that’s still missing from the build is the configuration of some kind of tests. I absolutely want to write tests against my bot’s functionality, as it’s very difficult to debug a bot during a running bot war. I’ll use Specs2 for that, of course, because it’s all kinds of genius. We’ll add the following lines to our botSettings:
libraryDependencies ++= Seq(
"org.specs2" %% "specs2" % "1.8.2" % "test",
"org.pegdown" % "pegdown" % "1.0.2" % "test",
"junit" % "junit" % "4.7" % "test"),
testOptions := Seq(
Tests.Filter(_ == "de.johoop.scalatron.BotSpec"),
Tests.Argument("html", "console")),
testOptions <+= crossTarget map { ct =>
Tests.Setup { () =>
System.setProperty("specs2.outDir",
new File(ct, "specs2").getAbsolutePath)
}
},
The first few lines add all required dependencies: pegdown for the nice HTML reports, junit for running the tests from within Eclipse using JUnit.
Then I tell SBT to just execute my main test specification called BotSpec and ignore any sub specifications. And I tell it to create HTML reports, too, and where to put them.
I can now already hack and test my bot to my heart’s content. However, I can’t yet try it out in the Scalatron environment. Let’s do something about this. I need a way to start the Scalatron simulation, and I probably want to configure the directory where all the bots are kept (the enemy bots as well as my own).
For this, I first create two simple SBT keys and add them to the Build class:
val botDirectory = SettingKey[File]("bot-directory")
val play = TaskKey[Unit]("play")
The botDirectory setting will simply be initialized to where I want to keep the bots (in the botSettings sequence):
botDirectory := file("bots"),
The play task will have the job to take my bot jar and deploy it into the bot directory, and then to start a (forked) Scalatron simulation. In order to be able to do this, it will require quite a few dependencies as inputs:<ul>
<li><tt>name</tt>: in order to name my bot in the bot directory,</li>
<li><tt>javaOptions</tt>: so that I can configure memory settings and the like for the simulation,</li>
<li><tt>unmanagedClasspath in Compile</tt>: to retrieve the Scalatron.jar from and finally</li>
<li><tt>Keys.`package` in Compile</tt>: to retrieve my bot's jar from.</li></ul>
So, here we go (again, at this to the botSettings):
play <<= (botDirectory, name, javaOptions,
unmanagedClasspath in Compile,
Keys.`package` in Compile) map {
(bots, name, javaOptions, ucp, botJar) =>
IO createDirectory (bots / name)
IO copyFile (botJar, bots / name / "ScalatronBot.jar")
val cmd = "java %s -cp %s scalatron.main.Main -plugins %s" format (
javaOptions mkString " ",
Seq(ucp.files.head, botJar).absString,
bots.absolutePath)
cmd run
}
And that’s it!
I can now deploy my bot to the bot directory and run the simulation from within SBT just by typing play into the SBT console.
Except… except that I noticed that the simulation seems to use a lot of memory, so I added a java option for this:
javaOptions += "-Xmx1g"
And also, the Eclipse project I generate via the sbteclipse plugin unfortunately warns me about two Scala runtimes (one generated by the plugin, one inside the Scalatron jar). I won’t do anything against this, hoping that we’ll soon get a clean published Scalatron jar that doesn’t automatically include the Scala runtime…
Here’s my complete build file as a gist.
Now go and create your own Scalatron bot! :)