diff --git a/projects/chuchu/bin/runjava b/projects/chuchu/bin/runjava new file mode 100755 index 00000000..29176914 --- /dev/null +++ b/projects/chuchu/bin/runjava @@ -0,0 +1,103 @@ +#!/usr/bin/perl -w + +use Getopt::Std; + +my $usage = "Usage: $0 [-p pid_file] [-r server_root] args\n"; + +# locations of stuff +chomp($location = `dirname $0`); + +# get the server root by popping the /bin off of our parent directory +@parts = split(/\//, $location); +pop(@parts); +my $root = join("/", @parts); + +my $jhome = $ENV{"JAVA_HOME"}; + +# make sure JAVA_HOME is set +if (!defined $jhome) { + warn "$0: Error: No JAVA_HOME specified!\n"; + warn "\n"; + warn "You must set your JAVA_HOME environment variable to the\n"; + warn "the absolute path of your JDK installation. For example:\n"; + warn "\n"; + warn " % JAVA_HOME=/usr/local/jdk1.2\n"; + warn " % export JAVA_HOME\n"; + die "\n"; +} + +# make sure it's set to a directory +if (! -d $jhome) { + die "$0: Can't find a java interpreter in '$jhome'.\n"; +} + +my $java = "$jhome/bin/java"; +my $jlib = "$jhome/lib/classes.zip"; + +# determine our machine architecture +my $ostype = `uname -s`; +my $machtype = `uname -m`; +chomp($ostype); +chomp($machtype); +my $arch = "$machtype-$ostype"; + +# add our native libraries to the runtime library path +my $libs = "$root/lib/$arch"; +my $libpath = $ENV{"LD_LIBRARY_PATH"}; + +if (defined $libpath) { + $ENV{"LD_LIBRARY_PATH"} = "$libs:$libpath"; +} else { + $ENV{"LD_LIBRARY_PATH"} = $libs; +} + +# put everything in our class path +my $classpath = "-classpath $jlib:$root/dist/classes"; + +# any zip or jar files in our lib/ directory get added to the class path +if (opendir(DIR, "$root/lib")) { + foreach $lib (grep { /.(zip|jar)/ && -f "$root/lib/$_" } readdir(DIR)) { + $classpath .= ":$root/lib/$lib"; + } + closedir DIR; +} + +# specify our server root (this is for server code) +my $serverroot = "-Dserver.root=$root"; + +# specify our base directory (this is for client code) +my $basedir = "-Dbase.dir=$root/src/java"; + +my $pid_file = undef; +my $i = 0; + +# strip out the -p and -r args (we'd use getopt() but the damned thing +# provides no way of escaping arguments so that we can pass args to +# runjava that get passed down to the JVM) +for ($i = 0; $i < @ARGV; $i++) { + my $arg = $ARGV[$i]; + + # stop when we see -- (and strip it out because Java don't dig --) + if ($arg eq "--") { + splice(@ARGV, $i, 1); + last; + } + + if ($arg eq "-p") { + $pid_file = $ARGV[$i+1]; + splice(@ARGV, $i, 2); + $i -= 1; # decrement i so that things stay in sync + + } elsif ($arg eq "-r") { + $serverroot = "-Dserver.root=" . $ARGV[$i+1]; + splice(@ARGV, $i, 2); + $i -= 1; # decrement i so that things stay in sync + } +} + +# log the pid file if requested to do so +print `echo $$ > $pid_file` if (defined $pid_file); + +my $cmd = "$java -mx256M $classpath $serverroot $basedir " . join(" ", @ARGV); +# print "$cmd\n"; +exec($cmd); diff --git a/projects/chuchu/build.xml b/projects/chuchu/build.xml new file mode 100644 index 00000000..15130563 --- /dev/null +++ b/projects/chuchu/build.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/projects/chuchu/src/java/com/samskivert/chuchu/Board.java b/projects/chuchu/src/java/com/samskivert/chuchu/Board.java new file mode 100644 index 00000000..19692c58 --- /dev/null +++ b/projects/chuchu/src/java/com/samskivert/chuchu/Board.java @@ -0,0 +1,583 @@ +// +// $Id: Board.java,v 1.1 2001/07/09 09:27:40 mdb Exp $ + +package com.samskivert.chuchu; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.PrintStream; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * The board contains information on wall placement, board features (holes + * and rockets) and cat and mouse coordinates, which are maintained via + * separate Piece object instances. + */ +public class Board +{ + /** The width of a standard Chu Chu board in squares. */ + public static final int WIDTH = 12; + + /** The height of a standard Chu Chu board in squares. */ + public static final int HEIGHT = 9; + + /** The total number of squares on the board. */ + public static final int SQUARES = WIDTH * HEIGHT; + + /** + * Constructs a blank board ready to be manipulated or configured from + * a file. + */ + public Board (boolean debug) + { + _debug = debug; + } + + /** + * Returns true if the specified board position is occupied by a + * feature (hole or rocket). + */ + public boolean isOccupied (int index) + { + return (_board[index] & FEATURE_MASK) != 0; + } + + /** + * Sets the board in motion with the specified set of arrow tiles. The + * board will continue executing until one of the following conditions + * occurs: + * + * + * + * If the maximum number of iterations is reached before one of these + * conditions occurs, an exception is thrown so that the caller can + * make a note of this failure to terminate and potentially mark this + * arrow configuration for a later repeat attempt with a higher number + * of iterations. + * + * @param arrows an array that is SQUARES long with a + * Piece direction constant at every coordinate where an + * arrow exists facing in the specified direction. The array should be + * set to -1 where there are no arrows. This array will be modified in + * the course of executing the board. + * @param maxiters the maximum number of iterations that the game is + * allowed to execute before aborting. + * + * @return >0 (number of iterations needed for solution) if the game + * terminated with all mice successfully reaching rockets, -1 if some + * failure condition occurred. + * + * @exception MaximumIterationsExceeded thrown if the game does not + * terminate within the maximum number of iterations. + */ + public int execute (int[] arrows, int maxiters) + throws MaximumIterationsExceeded + { + // clear out our position array + Arrays.fill(_aboard, 0); + + // duplicate our pieces and make a note of their starting positions + for (int i = 0; i < _cats.length; i++) { + Piece piece = _cats[i]; + _aboard[piece.getIndex()] = CAT_ID; + _acats[i].init(piece); + } + for (int i = 0; i < _mice.length; i++) { + Piece piece = (Piece)_mice[i].clone(); + _aboard[piece.getIndex()] = MOUSE_ID; + _amice[i].init(piece); + } + + // eliminate frozen pieces + + // adjust the positions of all of the pieces as if they just + // landed on their respective squares + for (int i = 0; i < _acats.length; i++) { + _acats[i].orientation = adjustOrientation(_acats[i], arrows, true); + } + for (int i = 0; i < _amice.length; i++) { + _amice[i].orientation = adjustOrientation(_amice[i], arrows, false); + } + + // start up the cellular automaton and see what happens + for (int iter = 0; iter < maxiters; iter++) { + // first move the cats + for (int c = 0; c < _acats.length; c++) { + Piece cat = _acats[c]; + // System.out.println("Examining cat: " + cat); + + // skip inactive cats + if (!cat.active) { + continue; + } + + // update the cat's offset + cat.offset = (cat.offset + 2) % 3; + + // if the cat was in the limbo state, he remains in the + // same square, otherwise he moves in the direction of his + // orientation + if (cat.offset == 1) { + continue; + } + + // clear out our previous board position + _aboard[cat.getIndex()] -= CAT_ID; + + // move to our new position + cat.x += X_ADJUSTS[cat.orientation]; + cat.y += Y_ADJUSTS[cat.orientation]; + + // check to see if we just moved into a hole or a rocket + int cidx = cat.getIndex(); + if ((_board[cidx] & HOLE_MASK) != 0) { + cat.active = false; + + } else if ((_board[cidx] & ROCKET_MASK) != 0) { + // too bad, so sad + return -1; + + } else { + // we need to make sure we're not swapping spots with + // any mice because a precise orientation of a cat and + // mouse face to face will result in their swapping + // positions which is illegal + for (int m = 0; m < _amice.length; m++) { + Piece mouse = _amice[m]; + if (mouse.active && (mouse.x == cat.x) && + (mouse.y == cat.y) && + (mouse.orientation == ((cat.orientation+2)%4))) { + // bang! howdy partner! + return -1; + } + } + // we didn't run into anything, so we update the board + // with our new position + _aboard[cidx] += CAT_ID; + // since we're in a new square, we need to potentially + // adjust the orientation of le chat + cat.orientation = adjustOrientation(cat, arrows, true); + } + } + + // now move the meeses + int active = 0; + for (int m = 0; m < _amice.length; m++) { + Piece mouse = _amice[m]; + // System.out.println("Examining mouse: " + mouse); + + // skip inactive mice + if (!mouse.active) { + continue; + } + + // move to our new position + mouse.x += X_ADJUSTS[mouse.orientation]; + mouse.y += Y_ADJUSTS[mouse.orientation]; + + // check to see if we just moved into a hole or a rocket + // or a cat + int midx = mouse.getIndex(); + if ((_board[midx] & HOLE_MASK) != 0) { + // too bad, so sad + return -1; + + } else if ((_board[midx] & ROCKET_MASK) != 0) { + // whee! + mouse.active = false; + + } else if ((_aboard[midx] & CAT_MASK) != 0) { + // too bad, so sad + return -1; + + } else { + // since we're in a new square, we need to potentially + // adjust the orientation of le mouse + mouse.orientation = adjustOrientation(mouse, arrows, false); + // we're still active, make a note of it + active++; + } + } + + // if we have no more active mice, the board worked. yay! + if (active == 0) { + return iter; + } + + // print out our board position + if (_debug) { + System.out.println("After iteration " + iter + "."); + writeTo(System.out, _acats, _amice); + } + } + + throw new MaximumIterationsExceeded(); + } + + /** + * Examines the walls and arrows in the vicinity of the supplied piece + * and returns the new orientation for that piece. Depending on the + * value of deduct this function will also reduce the + * potency of arrows that reflect a piece 180 degrees. + * + * @param piece the piece whose orientation will be adjusted. + * @param arrows the arrow information currently in effect. + * @param deduct if true, deduct one unit of potency from an arrow + * that reflects the piece 180 degrees. Arrows have only two units of + * potency and will thus disappear after two such reflections. + * + * @return the new orientation for the piece in question. + */ + protected int adjustOrientation (Piece piece, int[] arrows, boolean deduct) + { + int orient = piece.orientation; + int pos = piece.getIndex(); + + // if there's an arrow, we need to first account for that + int aorient = arrows[pos]; + if (aorient >= 0) { + aorient = (aorient & 0xF); + if (aorient == ((orient + 2) % 4)) { + // deduct one from the potency of this arrow + if ((arrows[pos] & 0x10) != 0) { + // we've already deducted one, wipe it out + arrows[pos] = -1; + } else { + arrows[pos] |= 0x10; + } + } + orient = aorient; + // System.out.println("Reoriented " + piece); + } + + // now account for the walls. if there's a wall in front of us, we + // need to examine things further + int fpos = (pos + FRONT_OFFSET[orient]) % SQUARES; + if ((_board[fpos] & FRONT_MASK[orient]) == 0) { + // nothing in front, we're done + return orient; + } + + int rpos = (pos + RIGHT_OFFSET[orient]) % SQUARES; + if ((_board[rpos] & RIGHT_MASK[orient]) == 0) { + // nothing on our right, so we rotate right and we're done + // System.out.println("Right turned " + piece); + return (orient + 1) % 4; + } + + int lpos = (pos + LEFT_OFFSET[orient]) % SQUARES; + if ((_board[lpos] & LEFT_MASK[orient]) == 0) { + // nothing on our left, so we rotate left and we're done + // System.out.println("Left turned " + piece); + return (orient + 3) % 4; + } + + // otherwise we turn around + // System.out.println("Reversed " + piece); + return (orient + 2) % 4; + } + + /** + * Reads in a text description of a Chu Chu board which should first + * be a grid of 12x9 characters containing the wall information, aside + * a grid of 12x9 characters containing the feature information + * (holes, rockets, cats, mice). The two grids are separated by one + * space. The wall grid contains L, | and _ to indicate left and + * bottom, left only and bottom only respectively. The feature grid + * contains H, R, N/E/W/S and n/e/w/s. Uppercase compass directions + * represent cats and lowercase directions represent mice. All + * wall-less and featureless squares are represented by a dot. For + * example, the first puzzle in the Mania stage would be represented + * like so: + * + *
+     * |._......... e...........
+     * |L|.L......| ............
+     * |._|._...L_. ............
+     * |._..||_.._. ............
+     * |L.....L|.|| ............
+     * |||....._.L| ..SNS.NSN...
+     * |L|_.....|.. ......R.....
+     * |.._|....... ............
+     * L___________ ............
+     * 
+ * + * Note that because the entire board is outlined in walls, the upper + * and right walls need not be communicated in the grid. The lower and + * left walls will automatically be added even if they are not + * specified in the input. + */ + public void readFrom (BufferedReader input) + throws IOException + { + ArrayList cats = new ArrayList(); + ArrayList mice = new ArrayList(); + String line; + + for (int h = 0; h < HEIGHT; h++) { + line = input.readLine(); + if (line == null) { + throw new IOException("Ran out of input before parsing " + + "entire board."); + } + + // parse the wall definition + for (int w = 0; w < WIDTH; w++) { + char pos = line.charAt(w); + int coord = h*WIDTH + w; + switch (pos) { + case 'L': + _board[coord] |= BOTH_WALLS_MASK; + break; + + case '|': + _board[coord] |= LEFT_WALL_MASK; + break; + + case '_': + _board[coord] |= BOTTOM_WALL_MASK; + break; + + case '.': + case ' ': + // blank space, do nothing + break; + + default: + String msg = "Unexpected wall character '" + pos + + "' at position +" + w + "+" + h + "."; + throw new IOException(msg); + } + } + + // parse the board features + for (int w = 0; w < WIDTH; w++) { + char pos = line.charAt(w + WIDTH + 1); + int coord = h*WIDTH + w; + switch (pos) { + case 'R': + _board[coord] |= ROCKET_MASK; + break; + + case 'H': + _board[coord] |= HOLE_MASK; + break; + + case 'N': + case 'E': + case 'W': + case 'S': + cats.add(new Piece(Piece.charToDirection(pos), w, h)); + break; + + case 'n': + case 'e': + case 'w': + case 's': + mice.add(new Piece(Piece.charToDirection(pos), w, h)); + break; + + case '.': + case ' ': + // blank space, do nothing + break; + + default: + String msg = "Unexpected feature character '" + pos + + "' at position +" + w + "+" + h + "."; + throw new IOException(msg); + } + } + } + + // stick our pieces into an array + _cats = new Piece[cats.size()]; + cats.toArray(_cats); + _mice = new Piece[mice.size()]; + mice.toArray(_mice); + + // we'll use these later + _acats = new Piece[cats.size()]; + for (int i = 0; i < _cats.length; i++) { + Piece piece = (Piece)_cats[i].clone(); + _acats[i] = piece; + } + _amice = new Piece[mice.size()]; + for (int i = 0; i < _mice.length; i++) { + Piece piece = (Piece)_mice[i].clone(); + _amice[i] = piece; + } + } + + /** + * Outputs the board in a format in which it can be read back in via + * readFrom(). + * + * @see #readFrom + */ + public void writeTo (PrintStream out) + { + writeTo(out, _cats, _mice); + } + + protected void writeTo (PrintStream out, Piece[] cats, Piece[] mice) + { + for (int h = 0; h < HEIGHT; h++) { + // print the wall description + for (int w = 0; w < WIDTH; w++) { + int pos = h*WIDTH + w; + switch (_board[pos] & BOTH_WALLS_MASK) { + case LEFT_WALL_MASK: + out.print("|"); + break; + case BOTTOM_WALL_MASK: + out.print("_"); + break; + case BOTH_WALLS_MASK: + out.print("L"); + break; + default: + out.print("."); + break; + } + } + + // separate the grids by a space + out.print(" "); + + // print the feature description + Piece p; + for (int w = 0; w < WIDTH; w++) { + int pos = h*WIDTH + w; + if ((_board[pos] & ROCKET_MASK) != 0) { + out.print("R"); + } else if ((_board[pos] & HOLE_MASK) != 0) { + out.print("H"); + } else if ((p = pieceAtCoordinates(cats, w, h)) != null) { + out.print(Piece.directionToChar(p.orientation, true)); + } else if ((p = pieceAtCoordinates(mice, w, h)) != null) { + out.print(Piece.directionToChar(p.orientation, false)); + } else { + out.print("."); + } + } + + // output a newline + out.println(""); + } + } + + public void dump (PrintStream out) + { + for (int h = 0; h < HEIGHT; h++) { + for (int w = 0; w < WIDTH; w++) { + out.print(HEX[_board[h*WIDTH + w]]); + } + out.println(""); + } + } + + /** + * Returns any piece located at the requested coordinates, or null if + * no piece is at those coordinates. + */ + protected Piece pieceAtCoordinates (Piece[] pieces, int x, int y) + { + for (int i = 0; i < pieces.length; i++) { + if (pieces[i].x == x && pieces[i].y == y) { + return pieces[i]; + } + } + return null; + } + + public static void main (String[] args) + { + if (args.length < 1) { + System.err.println("Usage: chuchu.Board board_file"); + System.exit(-1); + } + + try { + Board board = new Board(true); + FileReader fin = new FileReader(args[0]); + BufferedReader in = new BufferedReader(fin); + board.readFrom(in); + board.writeTo(System.out); + + int[] arrows = new int[SQUARES]; + Arrays.fill(arrows, -1); + arrows[2*WIDTH+0] = Piece.EAST; + arrows[0*WIDTH+1] = Piece.WEST; + if (board.execute(arrows, 100) >= 0) { + System.out.println("Success!"); + } else { + System.out.println("Failure!"); + } + + } catch (MaximumIterationsExceeded mie) { + System.out.println("Maximum iterations exceeded."); + + } catch (IOException ioe) { + ioe.printStackTrace(System.err); + } + } + + /** + * The board is represented by an integer for each square. Individual + * bits of that integer represent activated walls and features. + */ + protected int[] _board = new int[SQUARES]; + + // the pieces on the board (cats and mice) are maintained in these + // arrays + protected Piece[] _cats; + protected Piece[] _mice; + + // temporary copies of our board and pieces for use when executing + protected int[] _aboard = new int[SQUARES]; + protected Piece[] _acats; + protected Piece[] _amice; + + protected boolean _debug; + + protected static final int LEFT_WALL_MASK = 0x01; + protected static final int BOTTOM_WALL_MASK = 0x02; + protected static final int BOTH_WALLS_MASK = + LEFT_WALL_MASK|BOTTOM_WALL_MASK; + + protected static final int ROCKET_MASK = 0x04; + protected static final int HOLE_MASK = 0x08; + protected static final int FEATURE_MASK = ROCKET_MASK|HOLE_MASK; + + protected static final char[] HEX = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + protected static final int CAT_ID = 0x100; + protected static final int MOUSE_ID = 0x01; + protected static final int CAT_MASK = 0xFF00; + + protected static final int[] X_ADJUSTS = { 0, 1, 0, -1 }; + protected static final int[] Y_ADJUSTS = { -1, 0, 1, 0 }; + + protected static final int[] FRONT_OFFSET = { WIDTH*(HEIGHT-1), 1, 0, 0 }; + protected static final int[] FRONT_MASK = { + BOTTOM_WALL_MASK, LEFT_WALL_MASK, BOTTOM_WALL_MASK, LEFT_WALL_MASK }; + + protected static final int[] RIGHT_OFFSET = { 1, 0, 0, WIDTH*(HEIGHT-1) }; + protected static final int[] RIGHT_MASK = { + LEFT_WALL_MASK, BOTTOM_WALL_MASK, LEFT_WALL_MASK, BOTTOM_WALL_MASK }; + + protected static final int[] LEFT_OFFSET = { 0, WIDTH*(HEIGHT-1), 1, 0 }; + protected static final int[] LEFT_MASK = { + LEFT_WALL_MASK, BOTTOM_WALL_MASK, LEFT_WALL_MASK, BOTTOM_WALL_MASK }; +} diff --git a/projects/chuchu/src/java/com/samskivert/chuchu/MaximumIterationsExceeded.java b/projects/chuchu/src/java/com/samskivert/chuchu/MaximumIterationsExceeded.java new file mode 100644 index 00000000..f7a441f0 --- /dev/null +++ b/projects/chuchu/src/java/com/samskivert/chuchu/MaximumIterationsExceeded.java @@ -0,0 +1,12 @@ +// +// $Id: MaximumIterationsExceeded.java,v 1.1 2001/07/09 09:27:40 mdb Exp $ + +package com.samskivert.chuchu; + +public class MaximumIterationsExceeded extends Exception +{ + public MaximumIterationsExceeded () + { + super(""); + } +} diff --git a/projects/chuchu/src/java/com/samskivert/chuchu/Piece.java b/projects/chuchu/src/java/com/samskivert/chuchu/Piece.java new file mode 100644 index 00000000..d8ffcd3d --- /dev/null +++ b/projects/chuchu/src/java/com/samskivert/chuchu/Piece.java @@ -0,0 +1,115 @@ +// +// $Id: Piece.java,v 1.1 2001/07/09 09:27:40 mdb Exp $ + +package com.samskivert.chuchu; + +public class Piece implements Cloneable +{ + /** Compass orientation constant. */ + public static final int NORTH = 0; + + /** Compass orientation constant. */ + public static final int EAST = 1; + + /** Compass orientation constant. */ + public static final int SOUTH = 2; + + /** Compass orientation constant. */ + public static final int WEST = 3; + + /** Compass orientation of this piece. */ + public int orientation; + + /** X coordinate of this piece. */ + public int x; + + /** Y coordinate of this piece. */ + public int y; + + /** The fractional offset of a unit for this piece. */ + public int offset; + + /** Indicates whether this piece is active. */ + public boolean active = true; + + /** + * Constructs a new piece with the specified orientation and + * coordinates. + */ + public Piece (int orientation, int x, int y) + { + this.orientation = orientation; + this.x = x; + this.y = y; + } + + /** + * Returns the board index occupied by this piece. + */ + public int getIndex () + { + return y * Board.WIDTH + x; + } + + /** + * Initializes this piece with the values from the supplied piece. + */ + public final void init (Piece other) + { + this.orientation = other.orientation; + this.x = other.x; + this.y = other.y; + this.offset = 0; + this.active = true; + } + + /** + * Returns a clone of this piece instance. + */ + public Object clone () + { + try { + return super.clone(); + } catch (CloneNotSupportedException cnse) { + return null; + } + } + + public String toString () + { + return "[orient=" + orientation + ", x=" + x + ", y=" + y + + ", offset=" + offset + ", active=" + active + "]"; + } + + /** + * Converts a direction constant into a character suitable for + * printing in a board description. + */ + public static char directionToChar (int direction, boolean upper) + { + return upper ? UPPER_DIRS[direction] : LOWER_DIRS[direction]; + } + + /** + * Converts the letters n, e, w, and s into the proper direction code + * for their respective directions. + */ + public static int charToDirection (char pos) + { + if (pos == 'N' || pos == 'n') { + return Piece.NORTH; + } else if (pos == 'E' || pos == 'e') { + return Piece.EAST; + } else if (pos == 'W' || pos == 'w') { + return Piece.WEST; + } else if (pos == 'S' || pos == 's') { + return Piece.SOUTH; + } else { + String msg = "Invalid direction character '" + pos + "'."; + throw new RuntimeException(msg); + } + } + + protected static final char[] UPPER_DIRS = { 'N', 'E', 'S', 'W' }; + protected static final char[] LOWER_DIRS = { 'n', 'e', 's', 'w' }; +} diff --git a/projects/chuchu/src/java/com/samskivert/chuchu/Solver.java b/projects/chuchu/src/java/com/samskivert/chuchu/Solver.java new file mode 100644 index 00000000..253da628 --- /dev/null +++ b/projects/chuchu/src/java/com/samskivert/chuchu/Solver.java @@ -0,0 +1,122 @@ +// +// $Id: Solver.java,v 1.1 2001/07/09 09:27:40 mdb Exp $ + +package com.samskivert.chuchu; + +import java.io.*; +import java.util.Arrays; + +public class Solver +{ + public Solver (Board board, String arrows) + { + _board = board; + + // parse the arrow description + _arrows = new int[arrows.length()]; + for (int i = 0; i < _arrows.length; i++) { + _arrows[i] = Piece.charToDirection(arrows.charAt(i)); + } + } + + public void solve () + { + int[] arrows = new int[Board.SQUARES]; + Arrays.fill(arrows, -1); + solve(arrows, 0); + } + + protected void solve (int[] arrows, int aidx) + { + for (int i = 0; i < arrows.length; i++) { + // skip occupied positions + if (_board.isOccupied(i)) { + continue; + } + + // skip positions that already have an arrow in them + if (arrows[i] >= 0) { + continue; + } + + // note our arrow position + arrows[i] = _arrows[aidx]; + + // and if we placed the last arrow, execute + if (aidx+1 == _arrows.length) { + // copy out arrow configuration into a temporary array + System.arraycopy(arrows, 0, _tmparrows, 0, arrows.length); + + // execute + try { + int iters = _board.execute(_tmparrows, MAX_ITERS); + if ((_count++ % 100000) == 99999) { + System.out.print("."); + System.out.flush(); + } + if (iters >= 0) { + System.out.println("\nSOLUTION: " + iters); + printArrangement(arrows); + } + + } catch (MaximumIterationsExceeded mie) { + // System.out.println("INFINITE LOOP"); + // printArrangement(arrows); + } + + } else { + // otherwise place the next arrow + solve(arrows, aidx+1); + } + + // clear out our arrow position candidate + arrows[i] = -1; + } + } + + protected void printArrangement (int[] arrows) + { + for (int i = 0; i < arrows.length; i++) { + if (arrows[i] < 0) { + continue; + } + System.out.println(Piece.directionToChar(arrows[i], true) + + " => +" + (i % Board.WIDTH) + + "+" + (i / Board.WIDTH) + "."); + } + } + + protected void printArrows (int[] arrows) + { + } + + public static void main (String[] args) + { + if (args.length < 2) { + System.err.println("Usage: chuchu.Solver board_file arrow_desc"); + System.err.println(" where arrow_desc = [NESW]+"); + System.exit(-1); + } + + try { + Board board = new Board(false); + FileReader fin = new FileReader(args[0]); + BufferedReader in = new BufferedReader(fin); + board.readFrom(in); + + Solver solver = new Solver(board, args[1]); + solver.solve(); + + } catch (IOException ioe) { + ioe.printStackTrace(System.err); + } + } + + protected Board _board; + protected int[] _arrows; + protected int[] _tmparrows = new int[Board.SQUARES]; + protected int _count; + + // if we haven't seen a solution in this many iterations, bail + protected static final int MAX_ITERS = 150; +} diff --git a/projects/chuchu/src/java/com/samskivert/chuchu/blank.txt b/projects/chuchu/src/java/com/samskivert/chuchu/blank.txt new file mode 100644 index 00000000..7a06ec37 --- /dev/null +++ b/projects/chuchu/src/java/com/samskivert/chuchu/blank.txt @@ -0,0 +1,9 @@ +|........... ............ +|........... ............ +|........... ............ +|........... ............ +|........... ............ +|........... ............ +|........... ............ +|........... ............ +L___________ ............ diff --git a/projects/chuchu/src/java/com/samskivert/chuchu/board.txt b/projects/chuchu/src/java/com/samskivert/chuchu/board.txt new file mode 100644 index 00000000..5ff2661e --- /dev/null +++ b/projects/chuchu/src/java/com/samskivert/chuchu/board.txt @@ -0,0 +1,39 @@ +L____.|_____ e..........w N +|..|__L_ L.. H.R......R.H E +||.|..|..|.| ............ W +||....|....| ............ S +||....|....| ............ +|.....|..... ............ +|..L..|._|.. .....HH..... +|.....|..... ...HHHHHH... +L___________ HH.NNNNNN.HH + +|.........|. HHHHHHHHHH.. E +|..........| HHHHHHHHHH.. W +L__...____.| HHHHHHHHHH.. +|_.|..|._.|| E..HHH...... +|L|..._.L|.| R.......H... +L__|...L_..| .E.HHHH..... +|....____..| HHHHHHHHH... +|....____.._ HHHHHeee.... +L________L_L HHHHHHHHH..H + +|_.......... e........... E +|L|.L......| ............ W +|._|._...L_. ............ +|._..||_.._. ............ +|L.....L|.|| ............ +|||....._.L| ..SNS.NSN... +|L|_.....|.. ......R..... +|.._|....... ............ +L___________ ............ + +|...||.....| ............ N +|..||.....|| ............ W +|..||.||..|| ......S..... +|.|||||||||| ..sss.S.R... +|.|||||||||| ..sss.S..... +|.|||||||||| ..sss.S..... +L__||.....|| ............ +|___|.....|| ............ +L_________L_ ............