#!perl use strict; use warnings qw(FATAL all); my $n = shift(@ARGV) || 4; if( $n < 2 or $n > 26 ) { die "You can't play a size $n game\n"; } my $n2 = $n*$n; my (@rows, @cols); for my $i ( 0 .. $n-1 ) { my $row = my $col = "\x00" x $n2; vec($row, $i * $n + $_, 8) ^= 1 for 0 .. $n-1; vec($col, $i + $_ * $n, 8) ^= 1 for 0 .. $n-1; push @rows, $row; push @cols, $col; } my $goal = "0" x $n2; int(rand(2)) or (vec($goal, $_, 8) ^= 1) for 0 .. $n2-1; my $start = $goal; { for(@rows, @cols) { $start ^= $_ if int rand 2; } redo if $start eq $goal; } my @letters = ('a'..'z')[0..$n-1]; sub to_strings { my $board = shift; my @result = join(" ", " ", @letters); for( 0 .. $n-1 ) { my $res = sprintf("%2d ",$_+1); $res .= join " ", split //, substr $board, $_*$n, $n; push @result, $res; } \@result; } my $fmt; my ($stext, $etext) = ("Starting board", "Ending board"); my $re = join "|", reverse 1 .. $n, @letters; my $moves_so_far = 0; while( 1 ) { my ($from, $to) = (to_strings($start), to_strings($goal)); unless( $fmt ) { my $len = length $from->[0]; $len = length($stext) if $len < length $stext; $fmt = join($len, "%", "s%", "s\n"); } printf $fmt, $stext, $etext; printf $fmt, $from->[$_], $to->[$_] for 0 .. $n; last if $start eq $goal; INPUT_LOOP: { printf "Move #%s: Type one or more row numbers and/or column letters: ", $moves_so_far+1; my $input = <>; die unless defined $input; my $did_one; for( $input =~ /($re)/gi ) { $did_one = 1; if( /\d/ ) { $start ^= $rows[$_-1]; } else { $_ = ord(lc) - ord('a'); $start ^= $cols[$_]; } ++$moves_so_far; } redo INPUT_LOOP unless $did_one; } } print "You won after $moves_so_far moves.\n";