Difference between revisions of "Talk:Other JVM Languages"
(→Robocode in Other Languages: Added a response) |
Skilgannon (talk | contribs) (→Script Engine: speed and compatibility) |
||
(15 intermediate revisions by 6 users not shown) | |||
Line 5: | Line 5: | ||
I haven't been following his work on that front, but if his work is concentrated on loading .NET assemblies, then the comment is still valid. For non-compiled languages, you would still need some custom code for each language to load the interpreter and parse each source file to find classes which extend Robot.--[[User:Duyn|Duyn]] 02:35, 25 January 2010 (UTC) | I haven't been following his work on that front, but if his work is concentrated on loading .NET assemblies, then the comment is still valid. For non-compiled languages, you would still need some custom code for each language to load the interpreter and parse each source file to find classes which extend Robot.--[[User:Duyn|Duyn]] 02:35, 25 January 2010 (UTC) | ||
− | : No, basic | + | : No, basic Scala has already been implemented, as I write below. Sorry =) --[[User:Nat|<span style="color:#099;">Nat</span>]] [[User talk:Nat|<span style="color:#0a5;">Pavasant</span>]] 10:35, 25 January 2010 (UTC) |
== Robocode in Other Languages == | == Robocode in Other Languages == | ||
Line 16: | Line 16: | ||
* Python and Ruby can be implemented via Jython and JRuby, but nothing has done yet. | * Python and Ruby can be implemented via Jython and JRuby, but nothing has done yet. | ||
− | Note that | + | Note that people have tried to use Python for Robocode before too, but experience problem. And those robot cannot run in Competition until it make official. --[[User:Nat|<span style="color:#099;">Nat</span>]] [[User talk:Nat|<span style="color:#0a5;">Pavasant</span>]] 10:35, 25 January 2010 (UTC) |
---- | ---- | ||
Line 22: | Line 22: | ||
# I think the information is useful even if people can't enter their robots into any official leagues. Robocode gives a nice practical way to learn a new language, and this page can help people get up and running quickly. The alternative would be having people find this stuff out themselves (the clojure stuff was especially unpleasant). | # I think the information is useful even if people can't enter their robots into any official leagues. Robocode gives a nice practical way to learn a new language, and this page can help people get up and running quickly. The alternative would be having people find this stuff out themselves (the clojure stuff was especially unpleasant). | ||
# Scala robots run fine. I have coded a basic guess factor targeting bot (similar to the algorithm used in the [[GuessFactor Targeting Tutorial]]) in Scala which is putting my pattern matchers to shame. I have not yet encountered a feature blocked by Robocode, though I don't use structural types because they require reflection and are thus slow. | # Scala robots run fine. I have coded a basic guess factor targeting bot (similar to the algorithm used in the [[GuessFactor Targeting Tutorial]]) in Scala which is putting my pattern matchers to shame. I have not yet encountered a feature blocked by Robocode, though I don't use structural types because they require reflection and are thus slow. | ||
+ | : I tried out the Scala demo robot today. It runs great in the first battle, but in the next battle it is crippled by a NullPointerException apparently caused by Math.abs(). I read somewhere that Scala doesn't have static/class methods; so the Math functions are implemented as instance methods on a singleton object. Presumably, the singleton Math object is getting destroyed at the end of the first battle and not properly reinitialized for the second. Does anyone else have the same problem, or maybe even know how to fix it? Thanks. I'm running robocode 1.7.2.0 Beta 3. | ||
There was a time when Scala robots required specific changes to Robocode, back in the days when Robocode would only let you load classes from the CLASSPATH if they were in the java.* or robocode.* packages. That is no longer true now that Robocode seems to let you load any class you want from the CLASSPATH. —[[User:Duyn|Duyn]] 13:53, 25 January 2010 (UTC) | There was a time when Scala robots required specific changes to Robocode, back in the days when Robocode would only let you load classes from the CLASSPATH if they were in the java.* or robocode.* packages. That is no longer true now that Robocode seems to let you load any class you want from the CLASSPATH. —[[User:Duyn|Duyn]] 13:53, 25 January 2010 (UTC) | ||
+ | |||
+ | : Well, you could have help us in Scala implementation. Contact me if you interested. | ||
+ | : What I was worry at is when Robocode released plug-in which implement those languages, and if some newbie who are coding in, for example, Scala come across this page too, they might confused. --[[User:Nat|<span style="color:#099;">Nat</span>]] [[User talk:Nat|<span style="color:#0a5;">Pavasant</span>]] 14:06, 25 January 2010 (UTC) | ||
+ | |||
+ | :: For Scala, you don't need any extra effort. The Scala library just has to be on the CLASSPATH that Robocode is run with. Once that is done, everything just works (tested on Robocode 1.7.1.6). | ||
+ | :: Clojure requires disabling the security manager because (on Windows) it tries to read the file <code>'''\'''C:\...\clojure.jar</code> when the correct path to its runtime is <code>C:\...\clojure.jar</code> (no initial slash). I'm guessing the problem is with Clojure, since Scala doesn't have the same issue. | ||
+ | :: As for any upcoming plugin, we can update the information when that happens. Until then, this page can help them get a start now.—[[User:Duyn|Duyn]] 01:30, 26 January 2010 (UTC) | ||
+ | |||
+ | ::: Well, developing Scala implementation including making sure that all features work, and document for features that don't work. And installer, and automatically merge in Robocode without needing user to add the line, and so... --[[User:Nat|<span style="color:#099;">Nat</span>]] [[User talk:Nat|<span style="color:#0a5;">Pavasant</span>]] 12:10, 26 January 2010 (UTC) | ||
+ | |||
+ | :::: If you change the startup script so it doesn't clobber the system CLASSPATH, i.e. (on Windows): | ||
+ | :::: <pre>java -Xmx512M -Dsun.io.useCanonCaches=false -cp libs/robocode.jar;%CLASSPATH% robocode.Robocode %*</pre> | ||
+ | :::: then Robocode will automatically pick up language runtimes on the CLASSPATH and enable robots compiled in those languages. After that, you can just tell users to add their (compiled) language's runtime to their CLASSPATH if they want to use it with Robocode.—[[User:Duyn|duyn]] 02:10, 27 January 2010 (UTC) | ||
+ | |||
+ | ::::: What I mean is to make this completely tuso., i.e., without needing users to edit the startup file. --[[User:Nat|<span style="color:#099;">Nat</span>]] [[User talk:Nat|<span style="color:#0a5;">Pavasant</span>]] 05:22, 27 January 2010 (UTC) | ||
+ | |||
+ | :::::: If the distributed scripts were changed in this way, the user would not have to do anything other than ensure their language's runtime is on their CLASSPATH.—[[User:Duyn|duyn]] 10:10, 27 January 2010 (UTC) | ||
+ | |||
+ | It would be not secure to distribute Robocode with Scala on CLASSPATH because scala is not secure. We will not do that until the security of scala is solved by scala community. Probably never. Please read this [http://stackoverflow.com/questions/2263010/security-of-scala-runtime] [[User:Pavelsavara|Pavel]] 18th Feb 2010 | ||
+ | |||
+ | : Hi Pavel, you say in that link in solution #1 that reflection doesn't work in the Robocode sandbox. I've found that this is not true, at least for some of the simpler varieties of reflection. I've successfully tested a robot with simple reflection in both Robocode 1.6.x and 1.7.x with no issues at all. Is it only some aspects of reflection that are disabled? Should I post the reflection code that is working inside a bot? --[[User:Rednaxela|Rednaxela]] 22:15, 18 February 2010 (UTC) | ||
+ | |||
+ | :: I haven't tested yet but [http://robocode.googlecode.com/svn/robocode/trunk/robocode.tests.robots/src/main/java/tested/robots/ReflectionAttack.java there is a test that does Reflection and blocked by the security manager (or else the surefire test will fail)]. But I remember sometimes I am able to use Reflection too. --[[User:Nat|<span style="color:#099;">Nat</span>]] [[User talk:Nat|<span style="color:#0a5;">Pavasant</span>]] 13:47, 19 February 2010 (UTC) | ||
+ | |||
+ | ::: I've tested that specific one myself and it *only* blocks the reflection in that example if it has the setAccessible(true) calls in there, which are unnecessary in that example, and so far as I know unnecessary in most saner use of reflection. This is safe because it doesn't allow any more access to things via refection than it would have without reflection. It is an issue for some dynamic JVM languages however because apparently some call setAccessible(true) for things that they already have access to, plus some things like PicoContainer have a couple modes of operation that involve writing to private fields (not that I like those modes of operation personally), which are not a security issue if it can be restricted to only allowing access to private methods/fields that are defined within the robot jar itself. I currently have an ongoing email discussion with Pavel about this. Apparently Google's app engine has a [http://code.google.com/appengine/docs/java/runtime.html#The_Sandbox java sandbox] that manages to allow setAccessible() only on classes within the app itself, apparently with the purpose of improving support for various JVM languages. I currently wonder if a similar approach would be possible with Robocode. --[[User:Rednaxela|Rednaxela]] 13:59, 19 February 2010 (UTC) | ||
+ | |||
+ | == Script Engine == | ||
+ | |||
+ | <syntaxhighlight> | ||
+ | package cs; | ||
+ | |||
+ | import javax.script.ScriptEngine; | ||
+ | import javax.script.ScriptEngineManager; | ||
+ | import javax.script.ScriptException; | ||
+ | |||
+ | import robocode.AdvancedRobot; | ||
+ | import robocode.BulletHitBulletEvent; | ||
+ | import robocode.BulletHitEvent; | ||
+ | import robocode.Event; | ||
+ | import robocode.HitByBulletEvent; | ||
+ | import robocode.ScannedRobotEvent; | ||
+ | |||
+ | /** | ||
+ | * Converting all this code was more painful then I at first thought it would be. | ||
+ | * | ||
+ | * @author Chase | ||
+ | */ | ||
+ | public final class Rhino extends AdvancedRobot { | ||
+ | private static ScriptEngine rhino = (new ScriptEngineManager()).getEngineByName("rhino"); | ||
+ | static { | ||
+ | try { | ||
+ | rhino.eval("" + | ||
+ | "importPackage(java.awt.geom);" + | ||
+ | "importPackage(java.awt);" + | ||
+ | "importPackage(java.lang);" + | ||
+ | "importPackage(java.util);" + | ||
+ | "importClass(Packages.robocode.util.Utils);" + | ||
+ | "importClass(Packages.robocode.Rules);" + | ||
+ | |||
+ | "var MAX_MATCHES = 50; var hitScans = new ArrayList(); var myLocation = new Point2D.Double(); var enemyLocation = new Point2D.Double(); | ||
+ | var enemyWaves = new ArrayList(); var pastWaves = new ArrayList(); var _surfWave; var enemyEnergy = 100.0; var fieldRect = new java.awt.geom.Rectangle2D.Double(18, 18, | ||
+ | 764, 564); var WALL_STICK = 160; var data = new StringBuilder(); var lastLatVel;" + | ||
+ | |||
+ | "function EnemyWaveDC() { this.fireLocation = new Point2D.Double(); this.bulletVelocity = 0; this.directAngle = 0; this.distanceTraveled | ||
+ | = 0; this.direction = 0; this.scan = java.lang.reflect.Array.newInstance(java.lang.Double, 5); }" + | ||
+ | |||
+ | "function project(sourceLocation, angle, length) { return new Point2D.Double(sourceLocation.x + Math.sin(angle) * length, sourceLocation.y | ||
+ | + Math.cos(angle) * length); }" + | ||
+ | |||
+ | "function wallSmoothing(botLocation, angle, orientation) { do { angle += orientation*0.05; } while (!fieldRect.contains(project(botLocation, | ||
+ | angle, WALL_STICK))); return angle; }" + | ||
+ | |||
+ | "function absoluteBearing(source,target) { return Math.atan2(target.x - source.x, target.y - source.y); }" + | ||
+ | |||
+ | "function posNegLimit(value, max) { return Math.max(-max, Math.min(value, max)); }" + | ||
+ | |||
+ | "function offset(distance){ return (Math.PI/2.0 - 0.4) + (0.4/475.0)*distance; }" + | ||
+ | |||
+ | "function getFactor(ew, targetLocation) { return (Utils.normalRelativeAngle(absoluteBearing(ew.fireLocation, targetLocation) - | ||
+ | ew.directAngle) * ew.direction) / Math.asin(8.0/ew.bulletVelocity); }" + | ||
+ | |||
+ | "function getDanger(GF, location) { var danger = 0; var i = hitScans.iterator(); while(i.hasNext()){ var scan = i.next(); var dist | ||
+ | = 0; for(var j = 1; j < location.length; j++) dist += Math.abs(location[j] - scan[j]); danger += 1.0/dist/((1.0/50.0) + Math.abs(GF - scan[0])); } return danger; }" + | ||
+ | |||
+ | "function checkDanger(direction) { var predictedPosition = myLocation; var predictedVelocity = bot.getVelocity(); var predictedHeading | ||
+ | = bot.getHeadingRadians(); var moveAngle, moveDir; var counter = 0; do { moveAngle = wallSmoothing(predictedPosition, absoluteBearing(enemyLocation, predictedPosition) | ||
+ | + (direction * (offset(predictedPosition.distance(enemyLocation)))), direction) - predictedHeading; moveDir = 1; if(Math.cos(moveAngle) < 0) { moveAngle += Math.PI; | ||
+ | moveDir = -1; } predictedPosition = project(predictedPosition, predictedHeading = Utils.normalRelativeAngle(predictedHeading + posNegLimit(Utils.normalRelativeAngle( | ||
+ | moveAngle),Rules.getTurnRateRadians(Math.abs(predictedVelocity)))), predictedVelocity = posNegLimit(predictedVelocity += (((predictedVelocity * moveDir) < 0) ? 2*moveDir | ||
+ | : moveDir), 8) ); } while(_surfWave != null && predictedPosition.distance(_surfWave.fireLocation) - 18 > _surfWave.distanceTraveled + ((++counter) * _surfWave.bulletVelocity)); | ||
+ | predictedVelocity = 1; if(_surfWave != null) predictedVelocity = getDanger( getFactor(_surfWave,predictedPosition), _surfWave.scan); return predictedVelocity/ | ||
+ | predictedPosition.distanceSq(enemyLocation); }" + | ||
+ | |||
+ | "function updateWaves() { var minDist = Double.NEGATIVE_INFINITY; _surfWave = null; var it = enemyWaves.iterator(); while(it.hasNext()) { | ||
+ | var ew; var dist; if ((dist = ((ew = it.next()).distanceTraveled += ew.bulletVelocity ) - myLocation.distance(ew.fireLocation)) > 50) { it.remove(); continue; } if(dist | ||
+ | > minDist && dist < -18) { _surfWave = ew; minDist = dist; } } }" + | ||
+ | |||
+ | "function addWave(deltaEnergy) { if (pastWaves.size() > 2 && deltaEnergy <= 3 && deltaEnergy > 0.009){ var ew = pastWaves.get(2); | ||
+ | ew.bulletVelocity = Rules.getBulletSpeed(deltaEnergy); ew.fireLocation = enemyLocation; enemyWaves.add(ew); } }" + | ||
+ | |||
+ | "function logEnemyBullet(b){ var hitLocation = new Point2D.Double(b.getX(), b.getY()); var it = enemyWaves.iterator(); while (it.hasNext()) | ||
+ | { var ew = it.next(); if (Math.abs(ew.distanceTraveled - hitLocation.distance(ew.fireLocation)) <= 40) { ew.scan[0] = getFactor(ew,hitLocation); hitScans.add(ew.scan); | ||
+ | it.remove(); } } }" + | ||
+ | |||
+ | "function signum(value) { if(value < 0) return -1; if(value > 0) return 1; return 0; }" + | ||
+ | |||
+ | "function start() { bot.setBodyColor(Color.black); bot.setGunColor(Color.black); bot.setRadarColor(Color.black); enemyWaves = new ArrayList(); | ||
+ | pastWaves = new ArrayList(); bot.setAdjustGunForRobotTurn(true); bot.setAdjustRadarForGunTurn(true); while (true) { bot.turnRadarRightRadians(Double.POSITIVE_INFINITY); } }" + | ||
+ | |||
+ | "function scanned() {" + | ||
+ | "var ew; var absBearing; var latVel = (bot.getVelocity()*Math.sin(absBearing = e.getBearingRadians())); var advVel = (bot.getVelocity() | ||
+ | *Math.cos(absBearing)); bot.setTurnRadarRightRadians(Utils.normalRelativeAngle((absBearing += bot.getHeadingRadians()) - bot.getRadarHeadingRadians()) * 2);" + | ||
+ | "myLocation = new Point2D.Double(bot.getX(), bot.getY()); (ew = new EnemyWaveDC()).direction = signum(latVel + 1E-10); ew.directAngle = | ||
+ | absBearing + Math.PI; var eDistance; ew.scan[0] = 0; ew.scan[1] = (eDistance = e.getDistance())*(1/1000.0); ew.scan[2] = advVel/2; ew.scan[3] = lastLatVel/2; ew.scan[4] | ||
+ | = lastLatVel = Math.abs(latVel); pastWaves.add(0,ew); addWave(enemyEnergy - (enemyEnergy = e.getEnergy())); enemyLocation = project(myLocation, absBearing, eDistance); | ||
+ | updateWaves(); var direction = 1; if (checkDanger(-1) < checkDanger(1)) direction = -1; var goAngle = wallSmoothing(myLocation, ew.directAngle + direction*offset(eDistance), | ||
+ | direction) - bot.getHeadingRadians(); bot.setAhead(Math.cos(goAngle)*Double.POSITIVE_INFINITY); bot.setTurnRightRadians(Math.tan(goAngle));" + | ||
+ | |||
+ | "var bulletPower = ((eDistance < 150) ?3:Math.min(2,Math.min(bot.getEnergy()/16, enemyEnergy/2))); bot.setFire(bulletPower); " + | ||
+ | "var enemyHeading = e.getHeadingRadians(); var enemyVelocity = e.getVelocity(); var deltaTime = 0; var predicted = enemyLocation.clone(); | ||
+ | while((++deltaTime) * (20.0 - 3.0 * bulletPower) < myLocation.distance(predicted)) { predicted = project(predicted, enemyHeading, enemyVelocity); if(!fieldRect.contains( | ||
+ | predicted)){ predicted.x = Math.min(Math.max(fieldRect.x, predicted.x), fieldRect.width - 18.0); predicted.y = Math.min(Math.max(fieldRect.y, predicted.y), fieldRect.height | ||
+ | - 18.0); break; } } var theta = Utils.normalAbsoluteAngle(Math.atan2(predicted.x - myLocation.x, predicted.y - myLocation.y)); bot.setTurnGunRightRadians(Utils.normalRelativeAngle( | ||
+ | theta - bot.getGunHeadingRadians()));" + | ||
+ | "}" + | ||
+ | |||
+ | "function onhitb() { logEnemyBullet(e.getBullet()); enemyEnergy += 3*e.getBullet().getPower(); }" + | ||
+ | "function onbhit() { enemyEnergy -= Rules.getBulletDamage(e.getBullet().getPower()); }" + | ||
+ | "function onbhitb() { logEnemyBullet(e.getHitBullet()); } "); | ||
+ | } catch (ScriptException e) {} | ||
+ | } | ||
+ | public void run() { | ||
+ | rhino.put("bot", this); | ||
+ | try { | ||
+ | rhino.eval("start();"); | ||
+ | } catch (ScriptException e) { } | ||
+ | } | ||
+ | public void onScannedRobot(ScannedRobotEvent e) { | ||
+ | try { | ||
+ | rhino.put("e", e); | ||
+ | rhino.eval("scanned();"); | ||
+ | } catch (ScriptException e1) {} | ||
+ | } | ||
+ | public void onHitByBullet(HitByBulletEvent e) { | ||
+ | try { | ||
+ | rhino.put("e", e); | ||
+ | rhino.eval("onhitb();"); | ||
+ | } catch (ScriptException e1) { } | ||
+ | } | ||
+ | public void onBulletHitBullet(BulletHitBulletEvent e){ | ||
+ | try { | ||
+ | rhino.put("e", e); | ||
+ | rhino.eval("onbhitb();"); | ||
+ | } catch (ScriptException e1) { } | ||
+ | } | ||
+ | public void onBulletHit(BulletHitEvent e) { | ||
+ | try { | ||
+ | rhino.put("e", e); | ||
+ | rhino.eval("onbhit();"); | ||
+ | } catch (ScriptException e1) { } | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | I gave converting CunobelinDC a try into javascript, it works, except for the gun, for which for the lack of casting, made rather hard (could have done some extra trickery to get that to work, but it would have been a pain ). | ||
+ | |||
+ | Please excuse the crappy wrapping (I did it really quick so it wouldn't kill your scrollbar), but the code is more or less identical to CunobelinDC's (so look there if you want to know how that all works). This compiles to 171 bytes. While not in the spirit of the code size limitations, it is very interesting none the less. However it is very slow, it skips countless rounds as is. | ||
+ | |||
+ | While I don't expect this to be used (seriously anyway), I thought some might like to see it anyway. | ||
+ | |||
+ | — <span style="font-family: monospace">[[User:Chase-san|Chase]]-[[User_talk:Chase-san|san]]</span> 15:11, 16 January 2011 (UTC) | ||
+ | |||
+ | Pretty cool! I think there are 2 big things which prevent the usage of scripts to get around codesize in Robocode. The first is the execution speed that you commented on. The second is that script engines are a feature, but not mandated by the Java API. If I try to run this on my machine, which runs OpenJDK instead of the SunJDK (Oracle now I guess...), the script engine 'rhino' is missing. As such, your code throws exceptions =) Still, nice work. --[[User:Skilgannon|Skilgannon]] 15:06, 17 January 2011 (UTC) |
Latest revision as of 16:06, 17 January 2011
Non-.class languages
"Support for languages which don't have a compiler will require changes to Robocode which nobody has yet volunteered to implement." I don't think this is entirely correct. Pavel has been hard at work adding support for .NET to Robocode, which I believe would add support for some other languages. Someone who knows more about it should add a note about that, though. --Voidious 16:22, 24 January 2010 (UTC)
I haven't been following his work on that front, but if his work is concentrated on loading .NET assemblies, then the comment is still valid. For non-compiled languages, you would still need some custom code for each language to load the interpreter and parse each source file to find classes which extend Robot.--Duyn 02:35, 25 January 2010 (UTC)
- No, basic Scala has already been implemented, as I write below. Sorry =) --Nat Pavasant 10:35, 25 January 2010 (UTC)
Robocode in Other Languages
I don't believe this page is good idea. From what I heard, many features of Scala require feature that is blocked by Robocode's Sandbox. Pavel has implemented a basic Scala Robot already (see /plugins folder in Robocode's source), but some feature of Scala require Reflection, which is blocked by Robocode's Sandbox. So he is in need of Scala wizard who can help him done this. He said that if he done this himself, it may take months, but a scala wizard could have done this in just a couple of hours. I don't know abotu Clojure, but we already have plan for Ruby (via JRuby), Python (via Jython), PHP, and Javascript (Rhino Engine).
- .NET Robocode is almost completed. I was just reqested by Pavel to do QA on it. It should be beta release soon. For those who is interested in, Pavel has created jni4net library that connect .NET CLR and JVM through JNI. Earlier he code it just for Robocode, but he and Fnl decided to separated the project for other to use it too.
- Javascript via Rhino Engine has already been basically implemented too, but the fact the Jacascript is not really powerful, it hasn't been mentioned much.
- I've requested PHP for Robocode too. Take a time search, PHP-Java bridge seems to do well.
- Python and Ruby can be implemented via Jython and JRuby, but nothing has done yet.
Note that people have tried to use Python for Robocode before too, but experience problem. And those robot cannot run in Competition until it make official. --Nat Pavasant 10:35, 25 January 2010 (UTC)
- I think the information is useful even if people can't enter their robots into any official leagues. Robocode gives a nice practical way to learn a new language, and this page can help people get up and running quickly. The alternative would be having people find this stuff out themselves (the clojure stuff was especially unpleasant).
- Scala robots run fine. I have coded a basic guess factor targeting bot (similar to the algorithm used in the GuessFactor Targeting Tutorial) in Scala which is putting my pattern matchers to shame. I have not yet encountered a feature blocked by Robocode, though I don't use structural types because they require reflection and are thus slow.
- I tried out the Scala demo robot today. It runs great in the first battle, but in the next battle it is crippled by a NullPointerException apparently caused by Math.abs(). I read somewhere that Scala doesn't have static/class methods; so the Math functions are implemented as instance methods on a singleton object. Presumably, the singleton Math object is getting destroyed at the end of the first battle and not properly reinitialized for the second. Does anyone else have the same problem, or maybe even know how to fix it? Thanks. I'm running robocode 1.7.2.0 Beta 3.
There was a time when Scala robots required specific changes to Robocode, back in the days when Robocode would only let you load classes from the CLASSPATH if they were in the java.* or robocode.* packages. That is no longer true now that Robocode seems to let you load any class you want from the CLASSPATH. —Duyn 13:53, 25 January 2010 (UTC)
- Well, you could have help us in Scala implementation. Contact me if you interested.
- What I was worry at is when Robocode released plug-in which implement those languages, and if some newbie who are coding in, for example, Scala come across this page too, they might confused. --Nat Pavasant 14:06, 25 January 2010 (UTC)
- For Scala, you don't need any extra effort. The Scala library just has to be on the CLASSPATH that Robocode is run with. Once that is done, everything just works (tested on Robocode 1.7.1.6).
- Clojure requires disabling the security manager because (on Windows) it tries to read the file
\C:\...\clojure.jar
when the correct path to its runtime isC:\...\clojure.jar
(no initial slash). I'm guessing the problem is with Clojure, since Scala doesn't have the same issue. - As for any upcoming plugin, we can update the information when that happens. Until then, this page can help them get a start now.—Duyn 01:30, 26 January 2010 (UTC)
- If you change the startup script so it doesn't clobber the system CLASSPATH, i.e. (on Windows):
java -Xmx512M -Dsun.io.useCanonCaches=false -cp libs/robocode.jar;%CLASSPATH% robocode.Robocode %*
- then Robocode will automatically pick up language runtimes on the CLASSPATH and enable robots compiled in those languages. After that, you can just tell users to add their (compiled) language's runtime to their CLASSPATH if they want to use it with Robocode.—duyn 02:10, 27 January 2010 (UTC)
- If the distributed scripts were changed in this way, the user would not have to do anything other than ensure their language's runtime is on their CLASSPATH.—duyn 10:10, 27 January 2010 (UTC)
It would be not secure to distribute Robocode with Scala on CLASSPATH because scala is not secure. We will not do that until the security of scala is solved by scala community. Probably never. Please read this [1] Pavel 18th Feb 2010
- Hi Pavel, you say in that link in solution #1 that reflection doesn't work in the Robocode sandbox. I've found that this is not true, at least for some of the simpler varieties of reflection. I've successfully tested a robot with simple reflection in both Robocode 1.6.x and 1.7.x with no issues at all. Is it only some aspects of reflection that are disabled? Should I post the reflection code that is working inside a bot? --Rednaxela 22:15, 18 February 2010 (UTC)
- I haven't tested yet but there is a test that does Reflection and blocked by the security manager (or else the surefire test will fail). But I remember sometimes I am able to use Reflection too. --Nat Pavasant 13:47, 19 February 2010 (UTC)
- I've tested that specific one myself and it *only* blocks the reflection in that example if it has the setAccessible(true) calls in there, which are unnecessary in that example, and so far as I know unnecessary in most saner use of reflection. This is safe because it doesn't allow any more access to things via refection than it would have without reflection. It is an issue for some dynamic JVM languages however because apparently some call setAccessible(true) for things that they already have access to, plus some things like PicoContainer have a couple modes of operation that involve writing to private fields (not that I like those modes of operation personally), which are not a security issue if it can be restricted to only allowing access to private methods/fields that are defined within the robot jar itself. I currently have an ongoing email discussion with Pavel about this. Apparently Google's app engine has a java sandbox that manages to allow setAccessible() only on classes within the app itself, apparently with the purpose of improving support for various JVM languages. I currently wonder if a similar approach would be possible with Robocode. --Rednaxela 13:59, 19 February 2010 (UTC)
Script Engine
package cs;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import robocode.AdvancedRobot;
import robocode.BulletHitBulletEvent;
import robocode.BulletHitEvent;
import robocode.Event;
import robocode.HitByBulletEvent;
import robocode.ScannedRobotEvent;
/**
* Converting all this code was more painful then I at first thought it would be.
*
* @author Chase
*/
public final class Rhino extends AdvancedRobot {
private static ScriptEngine rhino = (new ScriptEngineManager()).getEngineByName("rhino");
static {
try {
rhino.eval("" +
"importPackage(java.awt.geom);" +
"importPackage(java.awt);" +
"importPackage(java.lang);" +
"importPackage(java.util);" +
"importClass(Packages.robocode.util.Utils);" +
"importClass(Packages.robocode.Rules);" +
"var MAX_MATCHES = 50; var hitScans = new ArrayList(); var myLocation = new Point2D.Double(); var enemyLocation = new Point2D.Double();
var enemyWaves = new ArrayList(); var pastWaves = new ArrayList(); var _surfWave; var enemyEnergy = 100.0; var fieldRect = new java.awt.geom.Rectangle2D.Double(18, 18,
764, 564); var WALL_STICK = 160; var data = new StringBuilder(); var lastLatVel;" +
"function EnemyWaveDC() { this.fireLocation = new Point2D.Double(); this.bulletVelocity = 0; this.directAngle = 0; this.distanceTraveled
= 0; this.direction = 0; this.scan = java.lang.reflect.Array.newInstance(java.lang.Double, 5); }" +
"function project(sourceLocation, angle, length) { return new Point2D.Double(sourceLocation.x + Math.sin(angle) * length, sourceLocation.y
+ Math.cos(angle) * length); }" +
"function wallSmoothing(botLocation, angle, orientation) { do { angle += orientation*0.05; } while (!fieldRect.contains(project(botLocation,
angle, WALL_STICK))); return angle; }" +
"function absoluteBearing(source,target) { return Math.atan2(target.x - source.x, target.y - source.y); }" +
"function posNegLimit(value, max) { return Math.max(-max, Math.min(value, max)); }" +
"function offset(distance){ return (Math.PI/2.0 - 0.4) + (0.4/475.0)*distance; }" +
"function getFactor(ew, targetLocation) { return (Utils.normalRelativeAngle(absoluteBearing(ew.fireLocation, targetLocation) -
ew.directAngle) * ew.direction) / Math.asin(8.0/ew.bulletVelocity); }" +
"function getDanger(GF, location) { var danger = 0; var i = hitScans.iterator(); while(i.hasNext()){ var scan = i.next(); var dist
= 0; for(var j = 1; j < location.length; j++) dist += Math.abs(location[j] - scan[j]); danger += 1.0/dist/((1.0/50.0) + Math.abs(GF - scan[0])); } return danger; }" +
"function checkDanger(direction) { var predictedPosition = myLocation; var predictedVelocity = bot.getVelocity(); var predictedHeading
= bot.getHeadingRadians(); var moveAngle, moveDir; var counter = 0; do { moveAngle = wallSmoothing(predictedPosition, absoluteBearing(enemyLocation, predictedPosition)
+ (direction * (offset(predictedPosition.distance(enemyLocation)))), direction) - predictedHeading; moveDir = 1; if(Math.cos(moveAngle) < 0) { moveAngle += Math.PI;
moveDir = -1; } predictedPosition = project(predictedPosition, predictedHeading = Utils.normalRelativeAngle(predictedHeading + posNegLimit(Utils.normalRelativeAngle(
moveAngle),Rules.getTurnRateRadians(Math.abs(predictedVelocity)))), predictedVelocity = posNegLimit(predictedVelocity += (((predictedVelocity * moveDir) < 0) ? 2*moveDir
: moveDir), 8) ); } while(_surfWave != null && predictedPosition.distance(_surfWave.fireLocation) - 18 > _surfWave.distanceTraveled + ((++counter) * _surfWave.bulletVelocity));
predictedVelocity = 1; if(_surfWave != null) predictedVelocity = getDanger( getFactor(_surfWave,predictedPosition), _surfWave.scan); return predictedVelocity/
predictedPosition.distanceSq(enemyLocation); }" +
"function updateWaves() { var minDist = Double.NEGATIVE_INFINITY; _surfWave = null; var it = enemyWaves.iterator(); while(it.hasNext()) {
var ew; var dist; if ((dist = ((ew = it.next()).distanceTraveled += ew.bulletVelocity ) - myLocation.distance(ew.fireLocation)) > 50) { it.remove(); continue; } if(dist
> minDist && dist < -18) { _surfWave = ew; minDist = dist; } } }" +
"function addWave(deltaEnergy) { if (pastWaves.size() > 2 && deltaEnergy <= 3 && deltaEnergy > 0.009){ var ew = pastWaves.get(2);
ew.bulletVelocity = Rules.getBulletSpeed(deltaEnergy); ew.fireLocation = enemyLocation; enemyWaves.add(ew); } }" +
"function logEnemyBullet(b){ var hitLocation = new Point2D.Double(b.getX(), b.getY()); var it = enemyWaves.iterator(); while (it.hasNext())
{ var ew = it.next(); if (Math.abs(ew.distanceTraveled - hitLocation.distance(ew.fireLocation)) <= 40) { ew.scan[0] = getFactor(ew,hitLocation); hitScans.add(ew.scan);
it.remove(); } } }" +
"function signum(value) { if(value < 0) return -1; if(value > 0) return 1; return 0; }" +
"function start() { bot.setBodyColor(Color.black); bot.setGunColor(Color.black); bot.setRadarColor(Color.black); enemyWaves = new ArrayList();
pastWaves = new ArrayList(); bot.setAdjustGunForRobotTurn(true); bot.setAdjustRadarForGunTurn(true); while (true) { bot.turnRadarRightRadians(Double.POSITIVE_INFINITY); } }" +
"function scanned() {" +
"var ew; var absBearing; var latVel = (bot.getVelocity()*Math.sin(absBearing = e.getBearingRadians())); var advVel = (bot.getVelocity()
*Math.cos(absBearing)); bot.setTurnRadarRightRadians(Utils.normalRelativeAngle((absBearing += bot.getHeadingRadians()) - bot.getRadarHeadingRadians()) * 2);" +
"myLocation = new Point2D.Double(bot.getX(), bot.getY()); (ew = new EnemyWaveDC()).direction = signum(latVel + 1E-10); ew.directAngle =
absBearing + Math.PI; var eDistance; ew.scan[0] = 0; ew.scan[1] = (eDistance = e.getDistance())*(1/1000.0); ew.scan[2] = advVel/2; ew.scan[3] = lastLatVel/2; ew.scan[4]
= lastLatVel = Math.abs(latVel); pastWaves.add(0,ew); addWave(enemyEnergy - (enemyEnergy = e.getEnergy())); enemyLocation = project(myLocation, absBearing, eDistance);
updateWaves(); var direction = 1; if (checkDanger(-1) < checkDanger(1)) direction = -1; var goAngle = wallSmoothing(myLocation, ew.directAngle + direction*offset(eDistance),
direction) - bot.getHeadingRadians(); bot.setAhead(Math.cos(goAngle)*Double.POSITIVE_INFINITY); bot.setTurnRightRadians(Math.tan(goAngle));" +
"var bulletPower = ((eDistance < 150) ?3:Math.min(2,Math.min(bot.getEnergy()/16, enemyEnergy/2))); bot.setFire(bulletPower); " +
"var enemyHeading = e.getHeadingRadians(); var enemyVelocity = e.getVelocity(); var deltaTime = 0; var predicted = enemyLocation.clone();
while((++deltaTime) * (20.0 - 3.0 * bulletPower) < myLocation.distance(predicted)) { predicted = project(predicted, enemyHeading, enemyVelocity); if(!fieldRect.contains(
predicted)){ predicted.x = Math.min(Math.max(fieldRect.x, predicted.x), fieldRect.width - 18.0); predicted.y = Math.min(Math.max(fieldRect.y, predicted.y), fieldRect.height
- 18.0); break; } } var theta = Utils.normalAbsoluteAngle(Math.atan2(predicted.x - myLocation.x, predicted.y - myLocation.y)); bot.setTurnGunRightRadians(Utils.normalRelativeAngle(
theta - bot.getGunHeadingRadians()));" +
"}" +
"function onhitb() { logEnemyBullet(e.getBullet()); enemyEnergy += 3*e.getBullet().getPower(); }" +
"function onbhit() { enemyEnergy -= Rules.getBulletDamage(e.getBullet().getPower()); }" +
"function onbhitb() { logEnemyBullet(e.getHitBullet()); } ");
} catch (ScriptException e) {}
}
public void run() {
rhino.put("bot", this);
try {
rhino.eval("start();");
} catch (ScriptException e) { }
}
public void onScannedRobot(ScannedRobotEvent e) {
try {
rhino.put("e", e);
rhino.eval("scanned();");
} catch (ScriptException e1) {}
}
public void onHitByBullet(HitByBulletEvent e) {
try {
rhino.put("e", e);
rhino.eval("onhitb();");
} catch (ScriptException e1) { }
}
public void onBulletHitBullet(BulletHitBulletEvent e){
try {
rhino.put("e", e);
rhino.eval("onbhitb();");
} catch (ScriptException e1) { }
}
public void onBulletHit(BulletHitEvent e) {
try {
rhino.put("e", e);
rhino.eval("onbhit();");
} catch (ScriptException e1) { }
}
}
I gave converting CunobelinDC a try into javascript, it works, except for the gun, for which for the lack of casting, made rather hard (could have done some extra trickery to get that to work, but it would have been a pain ).
Please excuse the crappy wrapping (I did it really quick so it wouldn't kill your scrollbar), but the code is more or less identical to CunobelinDC's (so look there if you want to know how that all works). This compiles to 171 bytes. While not in the spirit of the code size limitations, it is very interesting none the less. However it is very slow, it skips countless rounds as is.
While I don't expect this to be used (seriously anyway), I thought some might like to see it anyway.
— Chase-san 15:11, 16 January 2011 (UTC)
Pretty cool! I think there are 2 big things which prevent the usage of scripts to get around codesize in Robocode. The first is the execution speed that you commented on. The second is that script engines are a feature, but not mandated by the Java API. If I try to run this on my machine, which runs OpenJDK instead of the SunJDK (Oracle now I guess...), the script engine 'rhino' is missing. As such, your code throws exceptions =) Still, nice work. --Skilgannon 15:06, 17 January 2011 (UTC)