<?xml version="1.0" encoding="windows-1252"?>
<node id="940423" title="Re: Lights out puzzle" created="2011-11-28 13:09:27" updated="2011-11-28 13:09:27">
<type id="11">
note</type>
<author id="832495">
choroba</author>
<data>
<field name="doctext">
&lt;blockquote&gt;&lt;i&gt;Here's a quick Monday morning puzzle.&lt;/i&gt;&lt;/blockquote&gt;
Quick?? I spent several hours on it, not being able to come with anything better then this:
&lt;spoiler&gt;&lt;c&gt;
#!/usr/bin/perl

package Board;

use warnings;
use strict;

use Scalar::Util 'refaddr';

use constant _CROSS =&gt; ([0, 0], [-1, 0], [1, 0], [0, -1], [0, 1]);

my %click;

sub new {
    my ($class, $value) = @_;
    my $board = [];
    @$board = map [($value) x 14], 1 .. 13;
    push @$board, [($value) x 7];
    $click{refaddr($board)} = [map ([(0) x 14], 1 .. 13), [(0) x 7]];
    bless $board, $class;
    return $board;
} # new

sub finished {
    my $board = shift;
    return not grep {grep $_, @$_} @$board;
} # finished

sub show {
    my $board = shift;
    for my $row (@$board) {
        return unless defined $row;
        print map $_ ? '*' : defined $_ ? '.' : '', @$row;
        print "\n";
    }
} # show

sub _cross {
    my ($board, $x, $y) = @_;
    my @cross = grep {
                    $_-&gt;[0] &gt;= 0
                    and $_-&gt;[1] &gt;= 0
                    and ref $board-&gt;[$_-&gt;[1]]
                    and defined $board-&gt;[$_-&gt;[1]][$_-&gt;[0]]
                } map [$x + $_-&gt;[0], $y + $_-&gt;[1]],
                _CROSS;
    return @cross;
} # _cross

sub toggle {
    my ($board, $x, $y) = @_;
    my $old = $board-&gt;[$y][$x];
    return unless defined $old;
    $board-&gt;[$y][$x] = $old eq 1 ? 0 : 1;
} # toggle

sub at {
    my ($board, $x, $y) = @_;
    return if $x &lt; 0
        or $y &lt; 0
        or not ref $board-&gt;[$y]
        or not defined $board-&gt;[$y][$x];
    return $board-&gt;[$y][$x];
} # at

sub around {
    my ($board, $x, $y) = @_;
    return map $board-&gt;at(@$_), $board-&gt;_cross($x, $y);
} # around

sub click {
    my ($board, $x, $y) = @_;
    return unless defined $board-&gt;[$y][$x];
    $click{refaddr($board)}[$y][$x] = ! $click{refaddr($board)}[$y][$x];
    $board-&gt;toggle($_-&gt;[0], $_-&gt;[1])
        for $board-&gt;_cross($x, $y);
} # click

sub row {
    my ($board, $y) = @_;
    return @{ $board-&gt;[$y] };
} # row

sub clean {
    my $board = shift;
    for my $y (1 .. 13) {
        for my $x (0 .. 13) {
            $board-&gt;click($x, $y) if $board-&gt;at($x, $y-1);
        }
    }
} # clean

sub lastrow {
    my $board = shift;
    return map $board-&gt;at($_, 13 - ($_ &gt; 6)), (0 .. 13);
} # lastrow

sub history {
    my $board = shift;
    return unless ref $click{refaddr($board)};
    my @h = @{ $click{refaddr($board)} };
    print map ($_ ? 1 : '0', @$_),"\n" for @h;
} # history


##########################################################

package main;

use warnings;
use strict;

sub stringify {
    return join q[], map $_ ? 1 : 0, @_;
} # stringify

if (@ARGV) {
    my $b = Board-&gt;new(1);
    open my $IN, '&lt;', $ARGV[0] or die $!;
    while (&lt;$IN&gt;) {
        chomp;
        for my $i (0 .. length()-1 ) {
            $b-&gt;click($i, $.-1) if substr $_, $i, 1;
        }
        $b-&gt;show;
    }

} else {
    my %cache;
    for my $i (0 .. 13) {
        my $b = Board-&gt;new(0);
        $b-&gt;click($i, 0);
        $b-&gt;clean;
        my $k = stringify($b-&gt;lastrow);
        $cache{$k} = $i;
    }

    delete $cache{'0' x 14};

    my $board = Board-&gt;new(1);
    while (1) {
        $board-&gt;clean;
        my $last = stringify($board-&gt;lastrow);
        if (exists $cache{$last}) {
            $board-&gt;click($cache{$last}, 0);
        } elsif (not $board-&gt;finished) {
            $board-&gt;click(int rand 13, int rand 2) for 1 .. 1 + int rand 5;
        } else {
            last;
        }
    }
    $board-&gt;history;
}
&lt;/c&gt;&lt;/spoiler&gt;
It's a bit randomized, but usually runs under 2 seconds on my machine. Run it with a filename as an argument to check the solution saved in the file.
&lt;br&gt;&lt;b&gt;Update: &lt;/b&gt;Removed forgotten debugging line.
&lt;br&gt;&lt;b&gt;Update2: &lt;/b&gt;Readmore changed to spoiler.</field>
<field name="root_node">
940327</field>
<field name="parent_node">
940327</field>
</data>
</node>
