Llogiq on stuff

Java Code Golf

WARNING: DON'T DO THIS AT WORK!

If you’ve read other entries on this blog, you’ve probably noticed that I get my jollies in strange places (as, for example the Rust type system). Today at the JavaLand conference, I took part in the Code Golf contest and won with two of my three Java entries.

Java? Yes, you read that right. The contest was split into Java and other JVM languages, because as everyone and their cat knows, Java is very chatty, so a direct comparison, to JavaScript or Groovy would be unfair.

Setting that aside, if we restrict ourselves to a Java solution, how bad is it actually as a golfing language? How good can we get? Turns out it isn’t as bad as most make it out to be – yes, you’ll have some boilerplate, but you still can twitter some Java programs that do interesting things.

For example, Who’d have thought that one could write a complete Java program to write the Dates of all Fridays in 2016 in 112 bytes, including 5 spaces (which weren’t counted to allow people to send in formatted code, so this would’ve clocked in at 107 non-space chars).

Here’s the code (co-produced with Mark Lorenz, who shaved off eignt bytes from my original submission):

interface D{static void main(String[]a){for(int i=372;i>7;)System.out.println(new java.sql.Date(116,0,i-=7));}}

What’s that? An interface? Are we even allowed to do that? Shouldn’t the Java police come and revoke our JVM licenses now? Well, actually no. I started out with class D{public static void main(..., but using an interface, we can omit the public visibility specifier, because all interface members are public by default, saving us 2 characters. The next trick we pulled was to use a deprecated constructor of java.sql.Date that takes year-1900, month-1 and day (of month), but is quite lenient in the actual values it takes.

Update: An earlier version counted from 1 upwards, but Mark saved another char by counting downwards, because we can put the step into the Date constructor.

Another surprising one was Mark’s entry (which unsurprisingly won) for the Fibonacci sequence from 1..50:

interface F{static void main(String[]v){for(long a=1,b=1;a<2e10;b=a+(a=b))System.out.println(a);}}

This clocks in at 98 chars including 4 spaces, so 94 non-whitespace chars. Apart from the interface trick to reduce boilerplate, it pulls three ingenious tricks to get away with only two variables and a curly-brace-less for loop:

  1. The loop comparison compares a with 2e10. If you look at the Fibonacci sequence, you’ll notice that the fifteeth fibonacci number is 12,586,269,025, while the fifty-first is 20,365,011,074 (or roughly 2*1010 plus a few). So by comparing to 2e10, we get away without an extra loop variable.
  2. The loop update is used to actually do the calculation, so we can use the single-statement loop body for printing the value, getting rid of two curly braces.
  3. That calculation is one combined assignment of a and b: b=a+(a=b) Let’s pull it apart and see what it means: The (a=b) returns b and sets a to b, whereas the outer b=a+(b) uses the fact that Java’s operator argument evaluation order is defined letf-to-right to add the former value of a to b, (while setting a to the former value of b. Even a scripting language with multiple assignment would come out worse here.

Kudos to Mark for this masterpiece.

The last of the contests tasks was to produce a minimal program that takes a value from the program arguments and write out the factorial (n!) of this number. After the contest, I sat down with Mark to shave off some characters from my entry, bringing it to 99 non-whitespace characters:

interface I{static void main(String[]v){int a=1,i=new Byte(v[0]);for(;i>0;a*=i--);System.out.print(a);}}

This uses two neat tricks to reduce the length:

  1. Parsing the input into a number: We make use of the fact that 16! or fully written 2004189184 is the greatest factorial number that fits into an int. So we get much shorter than Integer.parseInt(v[0]) which others would have used by creating a Byte instance with the value, which is then auto-unboxed into a byte automatically, which is then automatically widened to an int by the assignment to i.
  2. As with Mark’s Fibonacci program, we do the computation the update statement of the for loop – it multiplies our accumulator a with the current i and decreases the latter by one. The result is that we can get by without curlies for the loop.

As a farewell present I offer you an adaptation of the “labyrinth” that used to be popular on the Commodore C64:

interface L{static void main(String[]a){for(;;)System.out.print(Math.random()>.5?"/ ":"\\ ");}}

This is 95 characters, including 5 spaces (to be fair, two of those are part of string constants, on the other hand, the chars on the C64 would make it look good without the spaces), so we clock in at 90 non-whitespace characters.

This shows two techniques to keep the codebase lean (if you can speak of a code base here):

  1. The for(;;) loop for an infinite loop. Usually, I’d write this as while(true), so this shaves off 4 bytes.
  2. We need a random boolean to print either slashes or backslashes, so we’ll just check Math.random(), which returns a double between 0 and 1 if the result is larger than ½. This is obviously shorter than new Random().nextBoolean() or ThreadLocalRandom.getCurrent().nextBoolean().

Unsurprisingly, all of those examples could be a lot shorter using a scripting language. In fact the winning entries (in Groovy and Javascript) of the contest were between 35 and 42 non-whitespace characters. Still, with a number of tricks one can write a lot of interesting Java programs that will fit in a tweet.


Think that code golfing is an evil distraction? Or want to share a technique I missed? Then discuss over at /r/java.