Difference between revisions of "Other JVM Languages"
(→Scala: added an indented block describing a git repository that can help people package their bots for the UI or the rumble.) |
|||
Line 10: | Line 10: | ||
You also need to disable security, if your Scala program does anything that needs to actually access the Scala libraries. | You also need to disable security, if your Scala program does anything that needs to actually access the Scala libraries. | ||
+ | |||
+ | :Optional: | ||
+ | |||
+ | ::This[[https://gitlab.com/ben-horner/robocode-scala-skeleton/-/tree/master/lib]] is a project on gitlab which will package a robot into a jar (using command: <code>gradle jar</code>) file suitable to load into Robocode for fighting, or for the rumble. | ||
+ | |||
+ | ::You can clone it with git using: <code>git clone https://gitlab.com/ben-horner/robocode-scala-skeleton.git</code> | ||
+ | |||
+ | ::The README file describes the pieces necessary if you'd rather not use that project, but would like to use gradle jar. | ||
Once that is done, coding robots in Scala is like coding in Java, but nicer: | Once that is done, coding robots in Scala is like coding in Java, but nicer: |
Revision as of 23:49, 21 February 2021
Robocode is not limited to Java robots! Since Robocode allows you to load classes on the CLASSPATH, you can code robots in any language that can produce compiled CLASS files. Support for languages which don't have a compiler will require changes to Robocode which nobody has yet volunteered to implement.
The easiest way to add a language's runtime to Robocode's CLASSPATH is to edit the starter script. Make sure you add the path to your compiled files under the Development Options.
Scala
You need to add scala-library.jar
to Robocode's CLASSPATH:
java -Xmx512M -Dsun.io.useCanonCaches=false -cp libs/robocode.jar;_SCALA_HOME_/lib/scala-library.jar robocode.Robocode %*
You also need to disable security, if your Scala program does anything that needs to actually access the Scala libraries.
- Optional:
- This[[1]] is a project on gitlab which will package a robot into a jar (using command:
gradle jar
) file suitable to load into Robocode for fighting, or for the rumble.
- This[[1]] is a project on gitlab which will package a robot into a jar (using command:
- You can clone it with git using:
git clone https://gitlab.com/ben-horner/robocode-scala-skeleton.git
- You can clone it with git using:
- The README file describes the pieces necessary if you'd rather not use that project, but would like to use gradle jar.
Once that is done, coding robots in Scala is like coding in Java, but nicer:
// ScalaDemo.scala
package wiki
import robocode._
import robocode.util.Utils
/*
A small demo to test robot coding in Scala.
*/
class ScalaDemo extends AdvancedRobot {
// Main thread of robot
override def run {
setAdjustGunForRobotTurn(true)
while (true) {
// If we're not going anywhere, move randomly around battlefield
if (Math.abs(getDistanceRemaining) < Rules.MAX_VELOCITY
&& Math.abs(getTurnRemaining) < Rules.MAX_TURN_RATE)
{
setAhead((Math.random*2-1)*100)
setTurnRight(Math.random*90-45)
}
// Tell RADAR to spin
if (getRadarTurnRemaining == 0)
setTurnRadarRightRadians(4)
// Carry out pending actions
execute
}
}
// Picked up a robot on RADAR
override def onScannedRobot(e : ScannedRobotEvent) {
// Absolute bearing to detected robot
val absBearing = e.getBearingRadians + getHeadingRadians
// Tell RADAR to paint detected robot
setTurnRadarRightRadians(3*Utils.normalRelativeAngle(
absBearing - getRadarHeadingRadians ))
// Tell gun to point at detected robot
setTurnGunRightRadians(Utils.normalRelativeAngle(
absBearing - getGunHeadingRadians ))
// Tell robot to shoot with power 2
setFire(2)
}
// Robot ran into a wall
override def onHitWall(e : HitWallEvent) {
// Turn in opposite direction to wall
setTurnRightRadians(Utils.normalRelativeAngle(
e.getBearingRadians + Math.Pi))
}
}
Compile like you would in Java:
scalac -cp _PATH_TO_ROBOCODE_/libs/robocode.jar ScalaDemo.scala
Mirah
Mirah compiles to java bytecode with no runtime dependency, so the compiled .class files run in Robocode.
Here is a very basic Mirah bot:
package mirah import robocode.AdvancedRobot class MirahBot < AdvancedRobot def initialize puts 'constructor' end def run setAdjustGunForRobotTurn(true) while true do if (getRadarTurnRemaining == 0) setTurnRadarLeftRadians(2*Math.PI) end execute end end end
Compile using the Mirah compile, and include like a regular Java robot.
Clojure
Coding robots in Clojure can be frustrating. You have to compile your code, but you don't get many of the benefits of using a compiler. For example, calls to non-existent methods are not caught until runtime. Typos in rarely run code can unexpectedly disable your robot (unless you use type hints everywhere and (set! *warn-on-reflection* true)
, which is beyond the scope of this introduction).
First, you need to add clojure.jar
to Robocode's CLASSPATH. You also have to disable the security manager (by passing -DNOSECURITY=true
on the Robocode command line) or your robot will be disabled for trying to read the Clojure JAR which is outside its root directory.
java -Xmx512M -Dsun.io.useCanonCaches=false -DNOSECURITY=true -cp libs/robocode.jar;_CLOJURE_HOME_/clojure.jar robocode.Robocode %*
Note that the ';' sign must be replaced with ':' on a Unix-like OS, e.g. on Linux and Mac OS X.
A sample clojure robot:
// ClojureDemo.clj (ns wiki.ClojureDemo (:gen-class :extends robocode.AdvancedRobot)) ; Private declarations (declare normalise) (defn -run [robot] (doto robot (.setAdjustRadarForGunTurn true) (.setAdjustRadarForRobotTurn true) (.setAdjustGunForRobotTurn true)) (loop [r robot] (.setAhead r 30) (.setTurnRightRadians r 1) (when (= 0 (.getRadarTurnRemainingRadians r)) (.setTurnRadarRightRadians r 4)) (.execute r) (recur r))) (defn -onScannedRobot [robot event] (.setTurnGunRightRadians robot (normalise (- (+ (.getBearingRadians event) (.getHeadingRadians robot)) (.getGunHeadingRadians robot)))) (when (< (Math/abs (normalise (.getGunTurnRemainingRadians robot))) robocode.Rules/GUN_TURN_RATE) (.setFire robot 2)) (.setTurnRadarRightRadians robot (* (normalise (- (+ (.getBearingRadians event) (.getHeadingRadians robot)) (.getRadarHeadingRadians robot))) 1.9))) (defn- normalise [angle] (robocode.util.Utils/normalRelativeAngle angle))
Since one of the oft-touted features of LISPs (of which Clojure is one) is being so dynamic, the compiler is rather unpleasant to use. You have to follow all the following steps, or you might encounter errors:
1. Put your source code in a folder called src. Files in this directory must follow the pattern src/PACKAGENAME/CLASSNAME.clj
or the compiler won't be able to find them.
2. Create a directory named classes next to your src directory. This is where compiled files will be put, and must exist before the compiler will agree to do anything.
3. You have to pass the package qualified name of every class you want to compile to the compiler. Given a class named wiki.ClojureDemo
, the compiler will try to read src/wiki/ClojureDemo.clj
to find it.
The command to compile the demo bot is:
java -cp _CLOJURE_HOME_/clojure.jar -Dclojure.compile.path=classes clojure.lang.Compile wiki.ClojureDemo
Note that robocode.jar
was not on the compiler's CLASSPATH. That is how dynamic Clojure is.
If you plan to use multiple classes, using the compiler can become rather tedious. Below is an Apache Ant script to compile all the files in the src
sub-directory:
<project name="robocode-robots" default="compile"> <!-- Change these to reflect your own installation paths --> <property name="clojure.jar" location="_PATH_TO_CLOJURE_/clojure.jar" /> <property name="robocode.jar" location="_PATH_TO_ROBOCODE_/robocode.jar" /> <!-- Directory under which source is located --> <property name="src" location="src" /> <!-- Directory where compiled files will be put --> <property name="build" location="classes" /> <target name="compile" description="Compile robots"> <mkdir dir="${build}" /> <pathconvert pathsep=" " property="compile.namespaces"> <fileset dir="${src}" includes="**/*.clj" /> <chainedmapper> <!-- Packagemapper converts paths from PACKAGENAME/CLASSNAME.clj to fully qualified class names PACKAGE.CLASSNAME The back slash below is required for Windows. Might have to reverse it on other platforms. --> <packagemapper from="${src}\*.clj" to="*" /> <filtermapper> <!-- The Clojure compiler expects class names with hyphens to be replaced by underscores --> <replacestring from="_" to="-" /> </filtermapper> </chainedmapper> </pathconvert> <java classname="clojure.lang.Compile"> <classpath> <path location="${src}" /> <path location="${build}" /> <path location="${clojure.jar}" /> <path location="${robocode.jar}" /> </classpath> <sysproperty key="clojure.compile.path" value="${build}" /> <arg line="${compile.namespaces}" /> </java> </target> <target name="clean" description="Remove compiled files"> <delete dir="${build}" /> </target> </project>