Wrote a program to solve Chu Chu Rocket boards. It's not super efficient
and gets pretty slow with four tiles to place, but it's a start. git-svn-id: https://samskivert.googlecode.com/svn/trunk@163 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
Executable
+103
@@ -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);
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
<!-- build configuration -->
|
||||||
|
<project name="chuchu" default="compile" basedir=".">
|
||||||
|
|
||||||
|
<!-- configuration parameters -->
|
||||||
|
<property name="app.name" value="chuchu"/>
|
||||||
|
<property name="deploy.home" value="dist"/>
|
||||||
|
<property name="dist.home" value="${deploy.home}"/>
|
||||||
|
<property name="dist.jar" value="${app.name}.jar"/>
|
||||||
|
<property name="javadoc.home" value="${deploy.home}/docs"/>
|
||||||
|
<property name="build.compiler" value="jikes"/>
|
||||||
|
<property name="java.libraries" value="/usr/share/java"/>
|
||||||
|
|
||||||
|
<!-- prepares the application directories -->
|
||||||
|
<target name="prepare">
|
||||||
|
<mkdir dir="${deploy.home}"/>
|
||||||
|
<mkdir dir="${deploy.home}/classes"/>
|
||||||
|
<mkdir dir="${javadoc.home}"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!-- cleans out the installed application -->
|
||||||
|
<target name="clean">
|
||||||
|
<delete dir="${deploy.home}"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!-- build the java class files -->
|
||||||
|
<target name="compile" depends="prepare">
|
||||||
|
<javac srcdir="src/java" destdir="${deploy.home}/classes"
|
||||||
|
debug="off" optimize="on" deprecation="off">
|
||||||
|
<classpath>
|
||||||
|
<fileset dir="${java.libraries}" includes="**/*.jar"/>
|
||||||
|
<fileset dir="lib" includes="**/*.jar"/>
|
||||||
|
<pathelement location="${deploy.home}/classes"/>
|
||||||
|
</classpath>
|
||||||
|
</javac>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!-- build the javadoc documentation -->
|
||||||
|
<target name="javadoc" depends="prepare">
|
||||||
|
<javadoc sourcepath="src/java"
|
||||||
|
packagenames="com.samskivert.*"
|
||||||
|
classpath="${deploy.home}/classes"
|
||||||
|
destdir="${javadoc.home}"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!-- the default target is to rebuild everything -->
|
||||||
|
<target name="all" depends="clean,prepare,compile,javadoc"/>
|
||||||
|
|
||||||
|
<!-- builds our distribution files (war and jar) -->
|
||||||
|
<target name="dist" depends="prepare,compile">
|
||||||
|
<jar jarfile="${dist.home}/${dist.jar}"
|
||||||
|
basedir="${deploy.home}/classes"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -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 <code>Piece</code> 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:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>All mice have reached rockets.
|
||||||
|
* <li>A mouse collides with a cat.
|
||||||
|
* <li>A cat reaches a rocket.
|
||||||
|
* <li>A mouse falls into a hole.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* 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 <code>SQUARES</code> long with a
|
||||||
|
* <code>Piece</code> 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 <code>deduct</code> 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:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* |._......... e...........
|
||||||
|
* |L|.L......| ............
|
||||||
|
* |._|._...L_. ............
|
||||||
|
* |._..||_.._. ............
|
||||||
|
* |L.....L|.|| ............
|
||||||
|
* |||....._.L| ..SNS.NSN...
|
||||||
|
* |L|_.....|.. ......R.....
|
||||||
|
* |.._|....... ............
|
||||||
|
* L___________ ............
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* <code>readFrom()</code>.
|
||||||
|
*
|
||||||
|
* @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 };
|
||||||
|
}
|
||||||
@@ -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("");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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' };
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
|........... ............
|
||||||
|
|........... ............
|
||||||
|
|........... ............
|
||||||
|
|........... ............
|
||||||
|
|........... ............
|
||||||
|
|........... ............
|
||||||
|
|........... ............
|
||||||
|
|........... ............
|
||||||
|
L___________ ............
|
||||||
@@ -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_ ............
|
||||||
Reference in New Issue
Block a user