This forum post gives the following puzzle. You have a square chess board of sides n with a piece in each of three corner squares. In each step you can push a piece in one of the four directions parallel to the sides and then it slides till it hits another piece or the side of the board. Get a piece to the center in the fewest steps possible.

The perl script posted here computes the answer by brute force if invoked with n as the argument.

use warnings; use strict; #use Data::Dump::Streamer; our $N = int($ARGV[0]) || 5; sub zip { my($v,$x,$y,@z) = @_; @$x == @$y or die "length error in zip"; [map { &$v($$x[$_], $$y[$_], @z) } 0 .. @$x-1]; } sub show { my($a) = @_; "[" . join(",", map { "[" . join(",", @$_) . "]" } @$a) . "]\n"; } sub show1 { my($p) = @_; my @o = (" " . "."x$N . "\n") x $N; for my $i (0 .. @$p-1) { my $c = $$p[$i]; substr($o[$$c[0]], 1 + $$c[1], 1) = chr(ord("A") + $i); } print @o; } sub add { my($m, $p) = @_; zip sub { my($m1, $p1) = @_; zip sub { my($m2, $p2) = @_; $m2 + $p2 }, $ +m1, $p1 }, $m, $p; } sub check { my($p) = @_; my %h; for my $c (@$p) { 0 <= $_ && $_ < $N or return for @$c; $h{join(",",@$c)}++ and return; } 1; } sub kick { my($m, $p) = @_; while (1) { my $q = add($m, $p); check($q) or last; $p = $q; } $p; } my $state0 = [[0,0],[0,$N-1],[$N-1,$N-1]]; my @move = map { my $m = $_; map { my @t = map { [0,0] } 0 .. 2; $t[$_] = $m; [@t] } 0 .. 2 } [0,1],[1,0],[0,-1],[-1,0]; sub kickall { my($p) = @_; map { kick $_, $p } @move; } sub goal { my($p) = @_; grep { int(($N-1)/2) <= $$_[0] && $$_[0] <= int($N/2) && int(($N-1)/2 +) <= $$_[1] && $$_[1] <= int($N/2) } @$p; } my @poss = [$state0, undef, 0]; my %found = (show($state0), 1); SEARCH: { for (my $k = 0; $k < @poss; $k++) { my($state, $_prev, $depth) = @{$poss[$k]}; for my $next (kickall $state) { $found{show($next)}++ and next; #print "(" . (1 + $depth) . " " . (0+@poss) . ") " . show $nex +t; push @poss, [$next, $k, 1 + $depth]; if (0 == @poss % 2000) { warn "(" . (1 + $depth) . " " . (0+@p +oss) . ")\n"; } goal($next) and last SEARCH; } } print "no solution found for N = $N (number of states is " . (0+@poss) + . ", max depth is " . ${$poss[-1]}[2] . ").\n\n"; exit 1; } print "solution for N = $N:\n"; my $k = @poss - 1; while (defined($k)) { my($state, $prev, $depth) = @{$poss[$k]}; print $depth . " " . $k . ":\n"; show1 $state; $k = $prev; } print "\n"; __END__

And here's the output for n = 5. The steps are listed in reverse order.

solution for N = 5: 11 4992: ..... ..... .CA.. ....B ..... 10 4095: ..... ..... .C..A ....B ..... 9 3209: ....A ..... .C... ....B ..... 8 2327: A.... ..... .C... ....B ..... 7 1479: ..... ..... AC... ....B ..... 6 792: ..... ..... A...C ....B ..... 5 380: ....C ..... A.... ....B ..... 4 150: ....C ..... A.... B.... ..... 3 50: ..... ..... A.... B.... ....C 2 16: A.... ..... ..... B.... ....C 1 3: A.... ..... ..... ....B ....C 0 0: A...B ..... ..... ..... ....C

Replies are listed 'Best First'.
Re: Block-sliding puzzle
by MidLifeXis (Monsignor) on Nov 12, 2009 at 15:41 UTC
Re: Block-sliding puzzle
by Anonymous Monk on Nov 17, 2009 at 16:38 UTC
    Interesting. Is there any better algorithm compared to brute-force? Can we predict number of moves in advance? How about genetic algorithms?

      If you run this for larger boards (say 13 or 15, which takes quite some time) then you can easily see the general pattern of solutions you get (7n-3 steps for board size 2n+1 or 2n+2). You could write a program that generates that solution, that way you'd get a simple program to generate a solution that's quite good. If you actually wanted to prove that that solution is optimal, I don't know an algorithm that's much better than this brute force one. You could of course optimize this brute force solution to run a few orders of magnitude faster if you really needed to run it for large boards.

        It looks like the optimal solution is kinda straight forward. while I realize it's isomorphic I'm going to frame it in a static form. Step 1 is to move the B( corner uniq ) along the side. down assuming the upper right B position. C which blocked B can loop to the other side of B in 3 moves. B and C can continue until either B or C reaches the N/2 -1 position (below the center). We will now call B the one in position, c is now the other. Then B crosses Horizontally to the A column, A moves down, C moves up (to corner), B horizontally to C's column. C moves down.

        now A and C are in vertical position. A at the edge A must now take 3 moves to the other side of C, using B as a stopBlock. then C and A continue taking turns of three to inch tward the horizontal center until the goal state is reached. There might be a trick to handle moving up the sides a bit more efficiently.. but the horizontal steps are pretty well stuck.