#!/usr/bin/perl use strict; use warnings; use FindBin; use lib "$FindBin::Bin/../perllib"; use Games::Affenspiel::Board; use Getopt::Long; no warnings 'recursion'; my $max_level = undef; my $board_num = undef; my $policy = 0; my $all = 0; my $replay = 0; sub show_help { my $text = qq{ Affenspiel Game Solver. Usage: $0 [OPTIONS] Options: -h --help show this help and exit -b --board N different initial board configuration -p --policy N change policy for choosing moves -a --all search among all solutions rather then the first -m --max-level N limit solutions to this level -r --replay replay the solution (one second between boards) -T --dumb-term do not position terminal cursor -C --dumb-chars do not use fancy drawing characters }; $text =~ s/^\n//; $text =~ s/\t$//; $text =~ s/^\t\t//mg; print $text; exit 0; } sub wrong_usage { die "Try '$0 --help' for more information.\n"; } GetOptions( "h|help" => \&show_help, "b|board=i" => \$board_num, "p|policy=i" => \$policy, "a|all" => \$all, "m|max-level=s" => \$max_level, "r|replay" => \$replay, "D|dumb-term!" => \$ENV{DUMB_TERM}, "C|dumb-chars!" => \$ENV{DUMB_CHARS}, ) || wrong_usage(); $| = 1; my %boards = (); my %board_solutions = (); my %visited_boards = (); sub find_board_solution ($;$$) { my $board = shift; my $level = shift || 0; return undef if $max_level && $level > $max_level; my $hash = shift || $board->hash; $boards{$hash} ||= $board; my $solution = $board_solutions{$hash}; my $visited_level = $visited_boards{$hash}; if (defined $visited_level) { if (!$all || $visited_level <= $level) { return $solution; } elsif ($solution) { $visited_boards{$hash} = $level; if (!$max_level || $level + $solution->[0] <= $max_level) { return $solution; } } elsif (!$max_level) { return undef; } } $visited_boards{$hash} = $level; if ($ENV{DEBUG_BOARDS}) { print "Level $level\n"; $board->show; } if ($board->is_final) { $solution = [ 0, undef ]; goto GOT_SOLUTION; } my $move_infos = $board->expand_valid_moves; foreach my $move_info (@$move_infos) { my ($bar, $gap_position, $direction, $next_board) = @$move_info; my $next_hash = $next_board->hash; printf "%s%4d | bar%s %s -> %s | %s |\n", "" x $level, $level, $bar, $direction, $board->stringify_position($gap_position), $next_hash if $ENV{DEBUG_MOVES}; my $next_solution = &find_board_solution( $next_board, $level + 1, $next_hash ); if ($next_solution) { my $num_moves = $next_solution->[0] + 1; $max_level = $num_moves + $level unless $max_level && $max_level < $num_moves + $level; next if $num_moves + $level > $max_level; # should not happen print "Got solution for board $hash in $num_moves moves\n" if $ENV{DEBUG}; $solution = [ $num_moves, $next_hash ] if !$solution || $solution->[0] > $num_moves; last unless $all; } } GOT_SOLUTION: $board_solutions{$hash} = $solution; return $solution; } sub find_solution () { my $board = Games::Affenspiel::Board->new($board_num); Games::Affenspiel::Board::set_policy($policy); print "\e[1;1H\e[J" if $replay && !$ENV{DUMB_TERM}; $board->show; my $solution = find_board_solution($board); die "No solution found\n" unless $solution; my $num_moves = $solution->[0]; while (my $hash = $solution->[1]) { my $board = $boards{$hash}; sleep(1) if $replay; print "\e[1;1H" if $replay && !$ENV{DUMB_TERM}; $board->show; $solution = $board_solutions{$hash}; } print STDERR "Solved in $num_moves moves\n"; } find_solution();