Two Guys Arguing

Java 6 Scripting is no js.jar

Posted in java, javascript by benjaminplee on 11.28.10

 

Monkey Riding Dog

QUnit-CLI riding a Dog

Today I wanted to put together a QUnit-CLI example that leveraged Java 6’s included scripting features.  Seeing as the Java 6 JDK includes a recent version of Rhino as its primary JavaScript engine, I thought this would be a piece of cake.  Wrong.

 

To the javax.script package’s credit, Creating a new scripting engine and evaluating some script code is dead simple.  Example below from Oracle’s own pages

import javax.script.*;

public class EvalFile {
  public static void main(String[] args) throws Exception {
    // create a script engine manager
    ScriptEngineManager factory = new ScriptEngineManager();

    // create JavaScript engine
    ScriptEngine engine = factory.getEngineByName("JavaScript");

    // evaluate JavaScript code from given file - specified by first argument
    engine.eval(new java.io.FileReader(args[0]));
  }
}

The trouble came into play when I ran QUnit-CLI ‘s Rhino based suite.js file.   Ka-blew-ey!

Exception in thread "main" javax.script.ScriptException:
    sun.org.mozilla.javascript.internal.EcmaError: 
    ReferenceError: "load" is not defined. (#1) in  at line number 1
  at com.sun.script.javascript.RhinoScriptEngine.eval(RhinoScriptEngine.java:110)
  at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:232)
  at Java6RhinoRunner.load(Java6RhinoRunner.java:29)
  at Java6RhinoRunner.main(Java6RhinoRunner.java:14)

It turns out that scripts running from in a Rhino Shell environment have access to extra functions that are not directly provided when executing embedded from Java.  The top-level “load” function which is used to load additional JavaScript files from the Rhino Shell is not directly available when running from the scripting engine.

It turns out, I am not the only one with this problem.  My solution was to bind an additional object with a Java-based “load” function and add a new top-level “load” function to the JavaScript scope that invokes the Java code.

import javax.script.*;
import java.io.*;

public class Java6RhinoRunner {
  public static void main(String[] args) throws ScriptException {
    new Java6RhinoRunner().load(args[0]);
  }

  private final ScriptEngine engine;

  public Java6RhinoRunner() throws ScriptException {
    ScriptEngineManager factory = new ScriptEngineManager();
    this.engine = factory.getEngineByName("JavaScript");

    this.engine.put("Java6RhinoRunner", this);
    this.engine.eval("function load(filename) { Java6RhinoRunner.load(filename); }");
  }

  public void load(String filename) throws ScriptException {
    try {
      this.engine.eval(new FileReader(filename));
    }
    catch(FileNotFoundException e) {
      throw new RuntimeException("Error loading javascript file: " + filename, e);
    }
  }
}

To make matters worse, from the Rhino Shell, the “print” function will output the given text to standard output with a newline character.  As far as I can tell there isn’t a non-newline print command available on the Shell.  Annoyingly, when running from Java both print and println are available and tied to their common java behaviors.  This means that my suite.js code which uses “print” needs to use “println” when running from Java.  My first thought was to override print to execute println from my Java runner, but it looks like these basic top-level functions can’ be redefined from JavaScript.

this.engine.eval("function print(message) { println(message); }");

(no errors at runtime, but didn’t work)

The solution was to use add a level of indirection to my suite.js

var out = (typeof println !== "undefined") ? println : print;
out("FAIL - " + name); // used to be a call to print directly

Now we have suit.js running from Rhino embedded within the Java 6 JDK … but things are perfect.  The current code throws “Inappropriate array length” errors for a few QUnit tests.  I will be looking into these next.

Probability of Konquest

Posted in Uncategorized by youngnh on 11.28.10

I really like the KDE game, Konquest. It reminds me of another game I like, KDice. KDice has a handy table reference to tell what the odds of an attack succeeding are. Calculating the odds of winning a confrontation in Konquest is, undeniably, more complex.

First, a little about the calculation of a winner in Konquest. There are two sides to every confrontation, an attacker and a defender. Each has a number of ships, and each has a “Kill Percentage”, a measure of the probability that they will destroy an enemy ship. The game conducts the battle by having each side “roll” in turn to determine if an opposing ship is destroyed. First the defender rolls to see whether they destroyed a ship or not, then the attacker. When one side is destroyed, the remaining side is the victor.

It’s pretty easy to determine the probability of successfully attacking an undefended planet. Likewise, there’s no battle if no fleet attacks. So we’ll start with a 1 v 1 battle where both sides have a 50% kill percentage. The ways in which the battle could be resolved are:

d kill, a doesn’t roll = d wins 0.5000000
d miss, a kills = a wins 0.2500000
d miss, a miss, d kill = d wins 0.1250000
d miss, a miss, d miss, a kill = a wins 0.0625000
d miss x N, a miss x N, d kill = d wins (1-dkp)^N * (1-akp)^N * dkp
d miss x N+1, a miss x N, a kill = a wins (1-dkp)^N+1 * (1-akp)^N * akp

N doesn’t have to get very large before the probability of those outcomes quickly reaches zero.

Rounding to 4 digits, I found that the odds of a 1v1 matchup going 22 rolls or more is 0%.
Adding together the probabilities of all outcomes, I got 66.6667% chance that the defense would hold against the attacker.

So there’s only a 1 in 3 chance of an attacker winning an otherwise even matchup. First strike is a valuable advantage in Konquest.

If the attacker sends forces with a better kill percentage, say 60% against a defending force of 50%, I calculate the odds of attacker winning at 43.75%.

If the attacker doubles his forces, 2 attackers versus 1 defender, the situation changes a bit. The ways in which the battle could be resolved are:

d kill, a kill = a wins 0.250000
d kill, a miss, d wins the 1v1 = d wins 0.166667
d kill, a miss, a wins 1v1 = a wins 0.083334
d miss, a kill = a wins 0.250000

and for each of the above outcomes, there could be N misses before them:

d miss x N, a miss x N, d kill, a kill = a wins
d miss x N, a miss x N, d kill, a miss, d wins the 1v1 = d wins
d miss x N, a miss x N, d kill, a miss, a wins the 1v1 = a wins
d miss x N, a miss x N, d miss, a kill = a wins

This drops the percentage significantly, but it’s midnight and this post is due, so check back later to see how much.

Follow

Get every new post delivered to your Inbox.