Java Code Golf
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:
- The loop comparison compares
a
with2e10
. 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 to2e10
, we get away without an extra loop variable. - 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.
- 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)
returnsb
and setsa
tob
, whereas the outerb=a+(b)
uses the fact that Java’s operator argument evaluation order is defined letf-to-right to add the former value ofa
tob
, (while settinga
to the former value ofb
. 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:
- 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 thanInteger.parseInt(v[0])
which others would have used by creating aByte
instance with the value, which is then auto-unboxed into abyte
automatically, which is then automatically widened to anint
by the assignment toi
. - As with Mark’s Fibonacci program, we do the computation the update statement
of the
for
loop – it multiplies our accumulatora
with the currenti
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):
- The
for(;;)
loop for an infinite loop. Usually, I’d write this aswhile(true)
, so this shaves off 4 bytes. - 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 thannew Random().nextBoolean()
orThreadLocalRandom.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.