Sunday, March 16, 2008

Unicode and custom characters on a console display.

On my way to Las Vegas, I read the in-flight Southwest magazine which had a puzzle called Shinro. Apparently, I'm not the only one who found it interesting enough to blog about. The game is played on an 8x8 grid partly covered with arrows, and a series of numbers along the rows and columns. Something like this:
Never mind what they mean, that's not important. What's important is that I decided to write a solver for this type of puzzle. I wrote a sudoku puzzle solver a few years ago and this would likely be easier to write than a Sudoku solver due to its rules.

The first thing I did was write the data model and a builder. For example, the grid above was written with this piece of code:

ShinroGrid puzzle = new ShinroBuilder(8, 8)
.setRows(3, 1, 2, 1, 1, 1, 1, 2)
.setColumns(2, 1, 2, 1, 2, 1, 1, 2)
.setArrow(0, 7, SW)
.setArrow(2, 0, N)
.setArrow(2, 3, SW)
.setArrow(2, 4, W)
.setArrow(2, 6, SW)
.setArrow(3, 5, E)
.setArrow(5, 0, S)
.setArrow(5, 6, NW)
.setArrow(6, 4, N)
.setArrow(6, 5, W)
.setArrow(7, 2, E)
.build();

The next thing was to write something to print the grid, which I also did:

new Printer(puzzle).print(System.out);

Now, how do you print information like this using ASCII? Most of the grid was easy to do:

|2|1|2|1|2|1|1|2|
-+-+-+-+-+-+-+-+-+
3|.|.|.|.|.|.|.|.|
-+-+-+-+-+-+-+-+-+
1|.|.|.|.|.|.|.|.|
-+-+-+-+-+-+-+-+-+
2|.|.|.|.|.|.|.|.|
-+-+-+-+-+-+-+-+-+
1|.|.|.|.|.|.|.|.|
-+-+-+-+-+-+-+-+-+
1|.|.|.|.|.|.|.|.|
-+-+-+-+-+-+-+-+-+
1|.|.|.|.|.|.|.|.|
-+-+-+-+-+-+-+-+-+
1|.|.|.|.|.|.|.|.|
-+-+-+-+-+-+-+-+-+
2|.|.|.|.|.|.|.|.|
-+-+-+-+-+-+-+-+-+

but then it came time to print the arrows. I didn't want to use digits, or the letters 'NE', I wanted something visually recognizable - arrows. For instance, isn't first diagram of this post nice and readable? Sure it is, I made it with OmniGraffle. No way I'm going to integrate that with Java. I checked the unicode charts, and found what I wanted in the 2190 range. So I wrote up something to test this:

public class Demo {
private static char toChar(Direction direction) {
switch(direction) {
case E: return 'u2192';
case N: return 'u2191';
case NE: return 'u2197';
case NW: return 'u2196';
case S: return 'u2193';
case SE: return 'u2198';
case SW: return 'u2199';
case W: return 'u2190';
default: return ' ';
}
}
public static void main(String[] args) {
for(Direction direction : Direction.values()) {
System.out.print(direction + " [" + toChar(direction) + "] ");
}
System.out.println();
}
}

The output is:

NW [?] N [?] NE [?] W [?] E [?] SW [?] S [?] SE [?]

See, my console font doesn't seem to know how to interpret these symbols. It seems unreasonable to me that I would need to set the font for my output console, particularly as I hope someone else will want to run my code elsewhere. Having to rely on an AWT or Swing canvas upon which I must draw these characters also seemed unreasonable.

My very first computer was a TI-99/4a. One of the neat features of the TI was the ability to redefine characters. Since all characters were printed as fixed-width 8x8 pixels, redefining a how a character was displayed was as simple as creating a string representation where each line is the two-digit hex value, such as:

F0
1
1
1
1
0
0
0
0
C0
1
1
0
0
0
0
0
0
A0
1
0
1
0
0
0
0
0
90
1
0
0
1
0
0
0
0
08
0
0
0
0
1
0
0
0
04
0
0
0
0
0
1
0
0
02
0
0
0
0
0
0
1
0
01
0
0
0
0
0
0
0
1

which could be implemented with this code:
10 call clear
20 call char(65, "F0C0A09008040201");
30 print "A"
40 end

(Thanks to the author of http://www.theforbiddenknowledge.com/99er/index.htm for the sample code and layout)

Maybe my old games were blocky, but I found it much easier to write graphics with these primitives. What's the modern-day equivalent for these? A custom terminal font? This sounds complicated without even thinking about it. I miss CALL CHAR.

My eventual solution was to use characters that were close approximations, but at least had a chance to be drawn:

case N: return '^';
case S: return 'v';
case E: return '>';
case W: return '<';
case NE: return '¬';
case NW: return 'F';
case SE: return 'J';
case SW: return 'L';

and my view finally looks like this:

|2|1|2|1|2|1|1|2|
-+-+-+-+-+-+-+-+-+
3|.|.|.|.|.|.|.|L|
-+-+-+-+-+-+-+-+-+
1|.|.|.|.|.|.|.|.|
-+-+-+-+-+-+-+-+-+
2|^|.|.|L|<|.|L|.|
-+-+-+-+-+-+-+-+-+
1|.|.|.|.|.|>|.|.|
-+-+-+-+-+-+-+-+-+
1|.|.|.|.|.|.|.|.|
-+-+-+-+-+-+-+-+-+
1|v|.|.|.|.|.|F|.|
-+-+-+-+-+-+-+-+-+
1|.|.|.|.|^|<|.|.|
-+-+-+-+-+-+-+-+-+
2|.|.|>|.|.|.|.|.|
-+-+-+-+-+-+-+-+-+

It isn't art, but it succeeds at getting conveying information. I succeeded this time, but what if I needed something more complicated for a game like "Where in the world is The Artist (formerly known as the Artist Formerly Known as Prince)?"

There should be an easier solution. It shouldn't require a learning curve (such as managing JFrame, lines, per-character font widths, et cetera.) I just want to get visual information across, quickly.

3 comments:

lahosken said...

Maybe use gnome-terminal? I just tried an "echo ↖" and it displayed ↖ OK. I don't remember doing anything fancy to get that to work.

swankjesse said...

Terminator can display any Unicode that you throw at it, plus it's Java and actively maintained and high performance and awesome:
http://software.jessies.org/terminator/

konberg said...

Thanks lahosken & swankjesse. The problem is that this doesn't really help me viewing content on the Eclipse Console view on my Mac. :( I think there's just a general problem with unicode display reliability combined with the difficulty in introducing characters.