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 { () => 
              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:

  • botDirectory: where the bots go,
  • name: in order to name my bot in the bot directory,
  • javaOptions: so that I can configure memory settings and the like for the simulation,
  • unmanagedClasspath in Compile: to retrieve the Scalatron.jar from and finally
  • Keys.`package` in Compile: to retrieve my bot’s jar from.

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,
        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! :)

Posted in Scala at March 31st, 2012. 4 Comments.