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 &#x003c+= 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>

  • botDirectory: where the bots go,
  •     <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 &#x003c;&#x003c;= (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! :)