Time for an 2014 update…

This commit is contained in:
Ingy döt Net 2014-01-17 05:32:22 +00:00
parent 372c577f83
commit 09687c4926
2520 changed files with 34227 additions and 7318 deletions

View file

@ -1,36 +1,44 @@
import std.string, std.typecons, std.exception, std.algorithm;
import queue_usage2; // No queue in Phobos 2.063.
import queue_usage2; // No queue in Phobos 2.064.
const struct Board {
private enum El : char { floor=' ', wall='#', goal='.',
box='$', player='@', boxOnGoal='*' }
private alias string CTable;
private enum El { floor = ' ', wall = '#', goal = '.',
box = '$', player = '@', boxOnGoal='*' }
private alias CTable = string;
private immutable size_t ncols;
private immutable CTable sData, dData;
private immutable int playerx, playery;
this(in string[] board) pure nothrow const
in {
foreach (row; board) {
foreach (const row; board) {
assert(row.length == board[0].length,
"Unequal board rows.");
foreach (c; row)
foreach (immutable c; row)
assert(c.inPattern(" #.$@*"), "Not valid input");
}
} body {
immutable sMap=[' ':' ', '.':'.', '@':' ', '#':'#', '$':' '];
immutable dMap=[' ':' ', '.':' ', '@':'@', '#':' ', '$':'*'];
immutable sMap = [' ':' ', '.':'.', '@':' ', '#':'#', '$':' '];
immutable dMap = [' ':' ', '.':' ', '@':'@', '#':' ', '$':'*'];
ncols = board[0].length;
foreach (r, row; board)
foreach (c, ch; row) {
sData ~= sMap[ch];
dData ~= dMap[ch];
int plx = 0, ply = 0;
CTable sDataBuild, dDataBuild;
foreach (immutable r, const row; board)
foreach (immutable c, const ch; row) {
sDataBuild ~= sMap[ch];
dDataBuild ~= dMap[ch];
if (ch == El.player) {
playerx = c;
playery = r;
plx = c;
ply = r;
}
}
this.sData = sDataBuild;
this.dData = dDataBuild;
this.playerx = plx;
this.playery = ply;
}
private bool move(in int x, in int y, in int dx,
@ -72,7 +80,7 @@ const struct Board {
string solve() pure {
bool[immutable CTable] visitedSet = [dData: true];
alias Tuple!(CTable, string, int, int) Four;
alias Four = Tuple!(CTable, string, int, int);
GrowableCircularQueue!Four open;
open.push(Four(dData, "", playerx, playery));
@ -84,16 +92,16 @@ const struct Board {
while (open.length) {
//immutable (cur, cSol, x, y) = open.pop;
immutable item = open.pop;
immutable CTable cur = item[0];
immutable string cSol = item[1];
immutable int x = item[2];
immutable int y = item[3];
immutable cur = item[0];
immutable cSol = item[1];
immutable x = item[2];
immutable y = item[3];
foreach (di; dirs) {
foreach (immutable di; dirs) {
CTable temp = cur;
//immutable (dx, dy) = di[0 .. 2];
immutable int dx = di[0];
immutable int dy = di[1];
immutable dx = di[0];
immutable dy = di[1];
if (temp[(y + dy) * ncols + x + dx] == El.boxOnGoal) {
if (push(x, y, dx, dy, temp) && temp !in visitedSet) {

View file

@ -1,66 +1,83 @@
import core.stdc.stdio: printf, puts, fflush, stdout, putchar;
import core.stdc.stdlib: malloc, calloc, realloc, free, alloca, exit;
import core.stdc.string: memcpy, memset, memcmp;
alias ushort cidx_t;
alias uint hash_t;
enum Cell : ubyte { space, wall, player, box }
alias CellIndex = ushort;
alias Thash = uint;
// Board configuration is represented by an array of cell
// indices of player and boxes.
struct State {
hash_t h;
State* prev, next, qnext;
cidx_t[0] c;
}
__gshared int w, h, n_boxes;
__gshared ubyte* board, goals, live;
__gshared size_t state_size, block_size = 32;
__gshared State* block_root, block_head;
/// Board configuration is represented by an array of cell
/// indices of player and boxes.
struct State { // Variable length struct.
Thash h;
State* prev, next, qNext;
CellIndex[0] c_;
State* newState(State* parent) nothrow {
static State* next_of(State *s) nothrow {
return cast(State*)(cast(ubyte*)s + state_size);
CellIndex get(in size_t i) inout pure nothrow {
return c_.ptr[i];
}
State *ptr;
if (!block_head) {
block_size *= 2;
State* p = cast(State*)malloc(block_size * state_size);
if (p == null) exit(1);
State* q;
p.next = block_root;
block_root = p;
ptr = cast(State*)(cast(ubyte*)p + state_size * block_size);
p = block_head = next_of(p);
for (q = next_of(p); q < ptr; p = q, q = next_of(q))
void set(in size_t i, in CellIndex v) pure nothrow {
c_.ptr[i] = v;
}
CellIndex[] slice(in size_t i, in size_t j) pure nothrow {
return c_.ptr[i .. j];
}
}
__gshared Cell[] board;
__gshared bool[] goals, live;
__gshared size_t w, h, nBoxes, stateSize, blockSize = 32;
__gshared State* blockRoot, blockHead, nextLevel, done;
__gshared State*[] buckets;
__gshared Thash hashSize, fillLimit, filled;
State* newState(State* parent) nothrow {
static State* nextOf(State *s) nothrow {
return cast(State*)(cast(ubyte*)s + stateSize);
}
State* ptr;
if (!blockHead) {
blockSize *= 2;
auto p = cast(State*)malloc(blockSize * stateSize);
if (p == null)
exit(1);
p.next = blockRoot;
blockRoot = p;
ptr = cast(State*)(cast(ubyte*)p + stateSize * blockSize);
p = blockHead = nextOf(p);
for (auto q = nextOf(p); q < ptr; p = q, q = nextOf(q))
p.next = q;
p.next = null;
}
ptr = block_head;
block_head = block_head.next;
ptr = blockHead;
blockHead = blockHead.next;
ptr.prev = parent;
ptr.h = 0;
return ptr;
}
void unNewState(State* p) nothrow {
p.next = block_head;
block_head = p;
p.next = blockHead;
blockHead = p;
}
enum Cell { space, wall, player, box }
// mark up positions where a box definitely should not be
void markLive(in int c) nothrow {
immutable int y = c / w;
immutable int x = c % w;
/// Mark up positions where a box definitely should not be.
void markLive(in size_t c) nothrow {
immutable y = c / w;
immutable x = c % w;
if (live[c])
return;
live[c] = 1;
live[c] = true;
if (y > 1 && board[c - w] != Cell.wall &&
board[c - w * 2] != Cell.wall)
markLive(c - w);
@ -75,120 +92,100 @@ void markLive(in int c) nothrow {
markLive(c + 1);
}
State* parseBoard(in int y, in int x, const char* s) nothrow {
w = x, h = y;
board = cast(ubyte*)calloc(w * h, ubyte.sizeof);
if (board == null) exit(2);
goals = cast(ubyte*)calloc(w * h, ubyte.sizeof);
if (goals == null) exit(3);
live = cast(ubyte*)calloc(w * h, ubyte.sizeof);
if (live == null) exit(4);
n_boxes = 0;
State* parseBoard(in size_t y, in size_t x, in char* s) nothrow {
static T[] myCalloc(T)(in size_t n) nothrow {
auto ptr = cast(T*)calloc(n, T.sizeof);
if (ptr == null)
exit(1);
return ptr[0 .. n];
}
w = x, h = y;
board = myCalloc!Cell(w * h);
goals = myCalloc!bool(w * h);
live = myCalloc!bool(w * h);
nBoxes = 0;
for (int i = 0; s[i]; i++) {
switch(s[i]) {
case '#':
board[i] = Cell.wall;
continue;
case '.', '+':
goals[i] = 1;
goals[i] = true;
goto case;
case '@':
continue;
case '*':
goals[i] = 1;
goals[i] = true;
goto case;
case '$':
n_boxes++;
nBoxes++;
continue;
default:
continue;
}
}
enum int int_size = int.sizeof;
state_size = (State.sizeof +
(1 + n_boxes) * cidx_t.sizeof +
int_size - 1)
/ int_size * int_size;
enum int intSize = int.sizeof;
stateSize = (State.sizeof +
(1 + nBoxes) * CellIndex.sizeof +
intSize - 1)
/ intSize * intSize;
State* state = newState(null);
auto state = null.newState;
for (cidx_t i = 0, j = 0; i < w * h; i++) {
for (CellIndex i = 0, j = 0; i < w * h; i++) {
if (goals[i])
markLive(i);
i.markLive;
if (s[i] == '$' || s[i] == '*')
(cast(cidx_t*)&state.c)[++j] = i;
state.set(++j, i);
else if (s[i] == '@' || s[i] == '+')
(cast(cidx_t*)&state.c)[0] = i;
state.set(0, i);
}
return state;
}
void showBoard(const State* s) nothrow {
static immutable char*[] glyph1 = [" ", "#", "@", "$"];
static immutable char*[] glyph2 = [".", "#", "@", "$"];
auto ptr = cast(char*)alloca(w * h * char.sizeof);
if (ptr == null) exit(5);
auto b = ptr[0 .. w * h];
memcpy(b.ptr, board, w * h);
b[(cast(cidx_t*)&s.c)[0]] = Cell.player;
for (int i = 1; i <= n_boxes; i++)
b[(cast(cidx_t*)&s.c)[i]] = Cell.box;
for (int i = 0; i < w * h; i++) {
printf((goals[i] ? glyph2 : glyph1)[b[i]]);
if (!((1 + i) % w))
putchar('\n');
}
}
// K&R hash function
void hash(State* s) nothrow {
/// K&R hash function.
void hash(State* s, in size_t nBoxes) pure nothrow {
if (!s.h) {
hash_t ha = 0;
cidx_t* p = cast(cidx_t*)&s.c;
for (int i = 0; i <= n_boxes; i++)
ha = p[i] + 31 * ha;
Thash ha = 0;
foreach (immutable i; 0 .. nBoxes + 1)
ha = s.get(i) + 31 * ha;
s.h = ha;
}
}
__gshared State** buckets;
__gshared hash_t hash_size, fill_limit, filled;
void extendTable() nothrow {
int old_size = hash_size;
int oldSize = hashSize;
if (!old_size) {
hash_size = 1024;
if (!oldSize) {
hashSize = 1024;
filled = 0;
fill_limit = hash_size * 3 / 4; // 0.75 load factor
fillLimit = hashSize * 3 / 4; // 0.75 load factor.
} else {
hash_size *= 2;
fill_limit *= 2;
hashSize *= 2;
fillLimit *= 2;
}
buckets = cast(State**)realloc(buckets,
(State*).sizeof * hash_size);
if (buckets == null) exit(6);
auto ptr = cast(State**)realloc(buckets.ptr,
(State*).sizeof * hashSize);
if (ptr == null)
exit(6);
buckets = ptr[0 .. hashSize];
buckets[oldSize .. hashSize] = null;
// rehash
memset(buckets + old_size,
0,
(State*).sizeof * (hash_size - old_size));
immutable hash_t bits = hash_size - 1;
for (int i = 0; i < old_size; i++) {
State *head = buckets[i];
immutable Thash bits = hashSize - 1;
foreach (immutable i; 0 .. oldSize) {
auto head = buckets[i];
buckets[i] = null;
while (head) {
State* next = head.next;
immutable int j = head.h & bits;
auto next = head.next;
immutable j = head.h & bits;
head.next = buckets[j];
buckets[j] = head;
head = next;
@ -196,44 +193,47 @@ void extendTable() nothrow {
}
}
State* lookup(State *s) nothrow {
hash(s);
State *f = buckets[s.h & (hash_size - 1)];
s.hash(nBoxes);
auto f = buckets[s.h & (hashSize - 1)];
for (; f; f = f.next) {
if (!memcmp((cast(cidx_t*)&s.c), (cast(cidx_t*)&f.c),
cidx_t.sizeof * (1 + n_boxes)))
if (s.slice(0, nBoxes + 1) == f.slice(0, nBoxes + 1))
break;
}
return f;
}
bool addToTable(State* s) nothrow {
if (lookup(s)) {
unNewState(s);
if (s.lookup) {
s.unNewState;
return false;
}
if (filled++ >= fill_limit)
extendTable();
if (filled++ >= fillLimit)
extendTable;
immutable hash_t i = s.h & (hash_size - 1);
immutable Thash i = s.h & (hashSize - 1);
s.next = buckets[i];
buckets[i] = s;
return true;
}
bool success(const State* s) nothrow {
for (int i = 1; i <= n_boxes; i++)
if (!goals[(cast(cidx_t*)&s.c)[i]])
bool success(in State* s) nothrow {
foreach (immutable i; 1 .. nBoxes + 1)
if (!goals[s.get(i)])
return false;
return true;
}
State* moveMe(State* s, int dy, int dx) nothrow {
immutable int y = (cast(cidx_t*)&s.c)[0] / w;
immutable int x = (cast(cidx_t*)&s.c)[0] % w;
State* moveMe(State* s, in int dy, in int dx) nothrow {
immutable int y = s.get(0) / w;
immutable int x = s.get(0) % w;
immutable int y1 = y + dy;
immutable int x1 = x + dx;
immutable int c1 = y1 * w + x1;
@ -241,41 +241,40 @@ State* moveMe(State* s, int dy, int dx) nothrow {
if (y1 < 0 || y1 > h || x1 < 0 || x1 > w || board[c1] == Cell.wall)
return null;
int at_box = 0;
for (int i = 1; i <= n_boxes; i++) {
if ((cast(cidx_t*)&s.c)[i] == c1) {
at_box = i;
int atBox = 0;
foreach (immutable i; 1 .. nBoxes + 1)
if (s.get(i) == c1) {
atBox = i;
break;
}
}
int c2;
if (at_box) {
if (atBox) {
c2 = c1 + dy * w + dx;
if (board[c2] == Cell.wall || !live[c2])
return null;
for (int i = 1; i <= n_boxes; i++)
if ((cast(cidx_t*)&s.c)[i] == c2)
foreach (immutable i; 1 .. nBoxes + 1)
if (s.get(i) == c2)
return null;
}
State* n = newState(s);
memcpy((cast(cidx_t*)&n.c) + 1,
(cast(cidx_t*)&s.c) + 1,
cidx_t.sizeof * n_boxes);
auto n = s.newState;
n.slice(1, nBoxes + 1)[] = s.slice(1, nBoxes + 1);
cidx_t* p = (cast(cidx_t*)&n.c);
p[0] = cast(cidx_t)c1;
n.set(0, cast(CellIndex)c1);
if (at_box)
p[at_box] = cast(cidx_t)c2;
if (atBox)
n.set(atBox, cast(CellIndex)c2);
// Bubble sort
for (int i = n_boxes; --i; ) {
cidx_t t = 0;
for (int j = 1; j < i; j++) {
if (p[j] > p[j + 1])
t = p[j], p[j] = p[j+1], p[j+1] = t;
// Bubble sort.
for (size_t i = nBoxes; --i; ) {
CellIndex t = 0;
foreach (immutable j; 1 .. i) {
if (n.get(j) > n.get(j + 1)) {
t = n.get(j);
n.set(j, n.get(j + 1));
n.set(j + 1, t);
}
}
if (!t)
break;
@ -284,42 +283,65 @@ State* moveMe(State* s, int dy, int dx) nothrow {
return n;
}
__gshared State* next_level, done;
bool queueMove(State *s) nothrow {
if (!s || !addToTable(s))
if (!s || !s.addToTable)
return false;
if (success(s)) {
puts("\nSuccess!");
if (s.success) {
"\nSuccess!".puts;
done = s;
return true;
}
s.qnext = next_level;
next_level = s;
s.qNext = nextLevel;
nextLevel = s;
return false;
}
bool do_move(State* s) nothrow {
return queueMove(moveMe(s, 1, 0)) ||
queueMove(moveMe(s, -1, 0)) ||
queueMove(moveMe(s, 0, 1)) ||
queueMove(moveMe(s, 0, -1));
bool doMove(State* s) nothrow {
return s.moveMe( 1, 0).queueMove ||
s.moveMe(-1, 0).queueMove ||
s.moveMe( 0, 1).queueMove ||
s.moveMe( 0, -1).queueMove;
}
void showBoard(in State* s) nothrow {
static immutable glyphs1 = " #@$", glyphs2 = ".#@$";
auto ptr = cast(ubyte*)alloca(w * h * ubyte.sizeof);
if (ptr == null)
exit(5);
auto b = ptr[0 .. w * h];
b[] = cast(typeof(b))board[];
b[s.get(0)] = Cell.player;
foreach (immutable i; 1 .. nBoxes + 1)
b[s.get(i)] = Cell.box;
foreach (immutable i, immutable bi; b) {
putchar((goals[i] ? glyphs2 : glyphs1)[bi]);
if (!((1 + i) % w))
'\n'.putchar;
}
}
void showMoves(in State* s) nothrow {
if (s.prev)
showMoves(s.prev);
printf("\n");
showBoard(s);
s.prev.showMoves;
"\n".printf;
s.showBoard;
}
int main() nothrow {
enum BIG = 0;
static if (BIG == 0) {
State* s = parseBoard(8, 7,
int main() nothrow {
enum uint problem = 0;
static if (problem == 0) {
auto s = parseBoard(8, 7,
"#######"~
"# #"~
"# #"~
@ -329,16 +351,16 @@ int main() nothrow {
"#.# @#"~
"#######");
} else static if (BIG == 1) {
State* s = parseBoard(5, 13,
} else static if (problem == 1) {
auto s = parseBoard(5, 13,
"#############"~
"# # #"~
"# $$$$$$$ @#"~
"#....... #"
"#############");
} else {
State* s = parseBoard(11, 19,
} else static if (problem == 2) {
auto s = parseBoard(11, 19,
" ##### "~
" # # "~
" # # "~
@ -350,37 +372,39 @@ int main() nothrow {
"##### ### #@## .#"~
" # #########"~
" ####### ");
} else {
asset(0, "Not present problem.");
}
showBoard(s);
extendTable();
queueMove(s);
s.showBoard;
extendTable;
s.queueMove;
for (int i = 0; !done; i++) {
printf("depth %d\r", i);
fflush(stdout);
stdout.fflush;
State *head = next_level;
for (next_level = null; head && !done; head = head.qnext)
do_move(head);
auto head = nextLevel;
for (nextLevel = null; head && !done; head = head.qNext)
head.doMove;
if (!next_level) {
puts("No solution?");
if (!nextLevel) {
"No solution?".puts;
return 1;
}
}
showMoves(done);
done.showMoves;
version (none) {
free(buckets);
free(board);
free(goals);
free(live);
version (none) { // Free all allocated memory.
buckets.ptr.free;
board.ptr.free;
goals.ptr.free;
live.ptr.free;
while (block_root) {
auto tmp = block_root.next;
free(block_root);
block_root = tmp;
while (blockRoot) {
auto tmp = blockRoot.next;
blockRoot.free;
blockRoot = tmp;
}
}

View file

@ -9,7 +9,7 @@ let cols = ref 0
let delta = function U -> -(!cols) | D -> !cols | L -> -1 | R -> 1
let store = Hashtbl.create 251
let mark t = Hashtbl.add store t true
let mark t = Hashtbl.replace store t ()
let marked t = Hashtbl.mem store t
let show ml =

View file

@ -0,0 +1,101 @@
sub MAIN() {
my $level = q:to//;
#######
# #
# #
#. # #
#. $$ #
#.$$ #
#.# @#
#######
say 'level:';
print $level;
say 'solution:';
say solve($level);
}
class State {
has Str $.board;
has Str $.sol;
has Int $.pos;
method move(Int $delta --> Str) {
my $new = $!board;
if $new.substr($!pos,1) eq '@' {
substr-rw($new,$!pos,1) = ' ';
} else {
substr-rw($new,$!pos,1) = '.';
}
my $pos := $!pos + $delta;
if $new.substr($pos,1) eq ' ' {
substr-rw($new,$pos,1) = '@';
} else {
substr-rw($new,$pos,1) = '+';
}
return $new;
}
method push(Int $delta --> Str) {
my $pos := $!pos + $delta;
my $box := $pos + $delta;
return '' unless $!board.substr($box,1) eq ' ' | '.';
my $new = $!board;
if $new.substr($!pos,1) eq '@' {
substr-rw($new,$!pos,1) = ' ';
} else {
substr-rw($new,$!pos,1) = '.';
}
if $new.substr($pos,1) eq '$' {
substr-rw($new,$pos,1) = '@';
} else {
substr-rw($new,$pos,1) = '+';
}
if $new.substr($box,1) eq ' ' {
substr-rw($new,$box,1) = '$';
} else {
substr-rw($new,$box,1) = '*';
}
return $new;
}
}
sub solve(Str $start --> Str) {
my $board = $start;
my $width = $board.lines[0].chars + 1;
my @dirs =
["u", "U", -$width],
["r", "R", 1],
["d", "D", $width],
["l", "L", -1];
my %visited = $board => True;
my $pos = $board.index('@');
my @open = State.new(:$board, :sol(''), :$pos);
while @open {
my $state = @open.shift;
for @dirs -> [$move, $push, $delta] {
my $board;
my $sol;
my $pos = $state.pos + $delta;
given $state.board.substr($pos,1) {
when '$' | '*' {
$board = $state.push($delta);
next if $board eq "" || %visited{$board};
$sol = $state.sol ~ $push;
return $sol unless $board ~~ /<[ . + ]>/;
}
when ' ' | '.' {
$board = $state.move($delta);
next if %visited{$board};
$sol = $state.sol ~ $move;
}
default { next }
}
@open.push: State.new: :$board, :$sol, :$pos;
%visited{$board} = True;
}
}
return "No solution";
}

View file

@ -0,0 +1,204 @@
#!perl
use strict;
use warnings qw(FATAL all);
my @initial = split /\n/, <<'';
#############
# # #
# $$$$$$$ @#
#....... #
#############
#######
# #
# #
#. # #
#. $$ #
#.$$ #
#.# @#
#######
=for
space is an empty square
# is a wall
@ is the player
$ is a box
. is a goal
+ is the player on a goal
* is a box on a goal
=cut
my $cols = length($initial[0]);
my $initial = join '', @initial;
my $size = length($initial);
die unless $size == $cols * @initial;
sub WALL() { 1 }
sub PLAYER() { 2 }
sub BOX() { 4 }
sub GOAL() { 8 }
my %input = (
' ' => 0, '#' => WALL, '@' => PLAYER, '$' => BOX,
'.' => GOAL, '+' => PLAYER|GOAL, '*' => BOX|GOAL,
);
my %output = reverse(%input);
sub packed_initial {
my $ret = '';
vec( $ret, $_, 4 ) = $input{substr $initial, $_, 1}
for( 0 .. $size-1 );
$ret;
}
sub printable_board {
my $board = shift;
my @c = @output{map vec($board, $_, 4), 0 .. $size-1};
my $ret = '';
while( my @row = splice @c, 0, $cols ) {
$ret .= join '', @row, "\n";
}
$ret;
}
my $packed = packed_initial();
my @udlr = qw(u d l r);
my @UDLR = qw(U D L R);
my @deltas = (-$cols, +$cols, -1, +1);
my %fseen;
INIT_FORWARD: {
$initial =~ /(\@|\+)/ or die;
use vars qw(@ftodo @fnext);
@ftodo = (["", $packed, $-[0]]);
$fseen{$packed} = '';
}
my %rseen;
INIT_REVERSE: {
my $goal = $packed;
vec($goal, $ftodo[0][2], 4) -= PLAYER;
my @u = grep { my $t = vec($goal, $_, 4); $t & GOAL and not $t & BOX } 0 .. $size-1;
my @b = grep { my $t = vec($goal, $_, 4); $t & BOX and not $t & GOAL } 0 .. $size-1;
die unless @u == @b;
vec($goal, $_, 4) += BOX for @u;
vec($goal, $_, 4) -= BOX for @b;
use vars qw(@rtodo @rnext);
FINAL_PLACE: for my $player (0 .. $size-1) {
next if vec($goal, $player, 4);
FIND_GOAL: {
vec($goal, $player + $_, 4) & GOAL and last FIND_GOAL for @deltas;
next FINAL_PLACE;
}
my $a_goal = $goal;
vec($a_goal, $player, 4) += PLAYER;
push @rtodo, ["", $a_goal, $player ];
$rseen{$a_goal} = '';
#print printable_board($a_goal);
}
}
my $movelen = -1;
my ($solution);
MAIN: while( @ftodo and @rtodo ) {
FORWARD: {
my ($moves, $level, $player) = @{pop @ftodo};
die unless vec($level, $player, 4) & PLAYER;
for my $dir_num (0 .. 3) {
my $delta = $deltas[$dir_num];
my @loc = map $player + $delta * $_, 0 .. 2;
my @val = map vec($level, $_, 4), @loc;
next if $val[1] & WALL or ($val[1] & BOX and $val[2] & (BOX|WALL));
my $new = $level;
vec($new, $loc[0], 4) -= PLAYER;
vec($new, $loc[1], 4) += PLAYER;
my $nmoves;
if( $val[1] & BOX ) {
vec($new, $loc[1], 4) -= BOX;
vec($new, $loc[2], 4) += BOX;
$nmoves = $moves . $UDLR[$dir_num];
} else {
$nmoves = $moves . $udlr[$dir_num];
}
next if exists $fseen{$new};
$fseen{$new} = $nmoves;
push @fnext, [ $nmoves, $new, $loc[1] ];
exists $rseen{$new} or next;
#print(($val[1] & BOX) ? "Push $UDLR[$dir_num]\n" : "Fwalk $udlr[$dir_num]\n");
$solution = $new;
last MAIN;
}
last FORWARD if @ftodo;
use vars qw(*ftodo *fnext);
(*ftodo, *fnext) = (\@fnext, \@ftodo);
} # end FORWARD
BACKWARD: {
my ($moves, $level, $player) = @{pop @rtodo};
die "<$level>" unless vec($level, $player, 4) & PLAYER;
for my $dir_num (0 .. 3) {
my $delta = $deltas[$dir_num];
# look behind and in front of the player.
my @loc = map $player + $delta * $_, -1 .. 1;
my @val = map vec($level, $_, 4), @loc;
# unlike the forward solution, we cannot push boxes
next if $val[0] & (WALL|BOX);
my $new = $level;
vec($new, $loc[0], 4) += PLAYER;
vec($new, $loc[1], 4) -= PLAYER;
# unlike the forward solution, if we have a box behind us
# we can *either* pull it or not. This means there are
# two "successors" to this board.
if( $val[2] & BOX ) {
my $pull = $new;
vec($pull, $loc[2], 4) -= BOX;
vec($pull, $loc[1], 4) += BOX;
goto RWALK if exists $rseen{$pull};
my $pmoves = $UDLR[$dir_num] . $moves;
$rseen{$pull} = $pmoves;
push @rnext, [$pmoves, $pull, $loc[0]];
goto RWALK unless exists $fseen{$pull};
print "Doing pull\n";
$solution = $pull;
last MAIN;
}
RWALK:
next if exists $rseen{$new}; # next direction.
my $wmoves = $udlr[$dir_num] . $moves;
$rseen{$new} = $wmoves;
push @rnext, [$wmoves, $new, $loc[0]];
next unless exists $fseen{$new};
print "Rwalk\n";
$solution = $new;
last MAIN;
}
last BACKWARD if @rtodo;
use vars qw(*rtodo *rnext);
(*rtodo, *rnext) = (\@rnext, \@rtodo);
} # end BACKWARD
}
if( $solution ) {
my $fmoves = $fseen{$solution};
my $rmoves = $rseen{$solution};
print "Solution found!\n";
print "Time: ", (time() - $^T), " seconds\n";
print "Moves: $fmoves $rmoves\n";
print "Move Length: ", length($fmoves . $rmoves), "\n";
print "Middle Board: \n", printable_board($solution);
} else {
print "No solution found!\n";
}
__END__