Modifying Behaviors Using Decorators
13 Jul 2010
My previous post on behavior trees made use of composite and action types, this posts serves as an example on how to improve behaviors using decorators. They take their name from the software design pattern. In the context of behavior trees a decorator node is a node with a single child, it modifies the behavior of the branch in some way, i.e. you don't want your NPC to keep kicking the door forever when it is blocked or replay an animation without completing the cycle.
The problem with my previous Robocode tree was that every time we execute it, it searched through the whole tree and as a consequence it kept switching targets, using decorators we can create loops in the tree that way we don't switch targets unless the robot we are going after dies.

Kill All sequence demonstrates two decorators used for looping, since at the beginning of the match we don't have any enemies to fight we scan around until we find a robot to kill, until-success decorator will keep executing its child until it returns success meaning we found at least one robot to fight. Next in the sequence is Attack branch, using until-fail decorator it will pick a target to attack, keep attacking until the enemy is dead then move on to a new target until select-target fails which means all the robots in the arena are dead, having loops in the tree allows us to execute the tree once unlike the previous example which kept executing the tree forever in a while loop.
In order to combat robots that track our movement, two non deterministic composite nodes exists which shuffle their children prior to execution such as the one used in the Strafe branch which gives us some degree of non determinism.
Controlling Robocode Engine from Clojure
06 Jul 2010
Since Robocode is designed for Java it can get really annoying when you want to program your robots in Clojure, basically you need to keep a REPL for compiling and testing the robot and a shell for actually running Robocode. I was looking for a way to streamline the process, turns out you can control Robocode engine programmatically which allows us to run battles from REPL, this scheme isn't perfect but close.
-Xmx512M -XX:MaxPermSize=512M
Robocode needs a lot memory to operate so depending on how you start your REPL you need to add the above options or you will keep getting OutOfMemoryException every 10 minutes.
[org.nakkaya.robocode/robocode "1.7.2.0"]
I have uploaded all the required Jars to Clojars, adding the above dependency to you project is all that is needed to get all the Jars you need, then you can use the following snippet to start Robocode engine from Clojure.
(ns robocode-engine
(:use clojure.contrib.shell-out)
(:import (java.io File)
(robocode.control
RobocodeEngine BattlefieldSpecification BattleSpecification)
(robocode.control.events BattleAdaptor)))
(System/setProperty "NOSECURITY" "true")
(defn battle-console []
(proxy [BattleAdaptor] []
(onBattleMessage [e] (println "Msg> " (.getMessage e)))
(onBattleError [e] (println "Err> " (.getError e)))
(onBattleCompleted
[e]
(println "\n\n-- Battle Complete --\n")
(doseq [result (.getSortedResults e)]
(println (.getTeamLeaderName result) (.getScore result))))))
(defn engine [dir vis?]
(let [engine (doto (RobocodeEngine. (File. dir))
(.setVisible vis?)
(.addBattleListener (battle-console)))]
(sh "jar" "xf" "lib/clojure-1.1.0.jar" "clojure")
(sh "mv" "clojure" "classes/")
engine))
(defn battle [engine rounds size robots]
(let [[x y] size
field (BattlefieldSpecification. x y)]
(BattleSpecification.
rounds field
(into-array
(reduce (fn[h v]
(conj h (first (.getLocalRepository engine v))))
[] robots)))))
(defn run-battle
([engine rounds size robots & wait]
(run-battle engine (battle engine rounds size robots))
(if (not (nil? wait))
(.waitTillBattleOver engine)))
([engine battle]
(.runBattle engine battle false)))
(defn close [engine]
(.close engine)
(sh "rm" "-rf" "classes/")
(sh "mkdir" "classes"))
Now you can initialize the engine,
(def engine (engine "/Applications/robocode/" true))
then you can compile and run your robot from REPL (assuming you robot is in the namespace gez),
(do
(compile 'gez)
(run-battle engine 3 [800 600]
["sample.SittingDuck"
"sample.Fire"
"sample.Crazy"
"sample.RamFire"
"gez*"]))