#!/usr/bin/perl -w use strict; # # Repeatedly run the builder script until it produces what we want # my $parallel = 0; my $max_level = 5; my $min_level = 4; my $l_1_2_cells_wanted = 100; my $l3_cells_wanted = -1; my $l4_cells_wanted = -1; my $l5_cells_wanted = -1; my $l3_operations_wanted = -1; my $l4_operations_wanted = -1; my $irregular_sets_wanted = -1; my $backtrack_depth_wanted = -1; my $stars_wanted = -1; my $extra_args = ""; my $debug = 0; # **************************************************************** # Process arguments my $seed_fname = "blank-board.txt"; while ($_ = shift) { my $arg = $_; if ($arg =~ /^-debug/) { $debug = 1; } elsif ($arg =~ /^-p([0-9]+)/) { $parallel = $1; } elsif ($arg eq "-level") { $min_level = shift; $max_level = $min_level; } elsif ($arg eq "-min_level") { $min_level = shift; if ($max_level < $min_level) { $max_level = $min_level; } } elsif ($arg eq "-max_level") { $max_level = shift; if ($min_level > $max_level) { $min_level = $max_level; } } elsif ($arg eq "-l12_cells") { $l_1_2_cells_wanted = shift; } elsif ($arg eq "-l3_cells") { $l3_cells_wanted = shift; } elsif ($arg eq "-l4_cells") { $l4_cells_wanted = shift; } elsif ($arg eq "-l5_cells") { $l5_cells_wanted = shift; } elsif ($arg eq "-l3_operations") { $l3_operations_wanted = shift; } elsif ($arg eq "-l4_operations") { $l4_operations_wanted = shift; } elsif ($arg eq "-irregular_sets") { $irregular_sets_wanted = shift; } elsif ($arg eq "-guess_levels") { $backtrack_depth_wanted = shift; $min_level = 6; $max_level = 6; } elsif ($arg eq "-stars") { $stars_wanted = shift; } elsif ($arg eq "--") { while (my $ea = shift) { $extra_args .= " $ea"; } } elsif ($arg =~ /^-/) { die "Argument $arg not understood\n"; } else { $seed_fname = $_; } } my $won = 0; my $pass = 0; # **************************************************************** sub run_one_build ($) { my ($output_ptr) = @_; my $output = `build_sudoku ${seed_fname} -min_level ${min_level} -opt_to ${max_level} ${extra_args}`; if ($output !~ /Solver level:\s+([0-9]+)/) { print "Whoops! Output botched! No board generated.\n"; print "****************************************************************\n"; print $output; print "****************************************************************\n"; return 0; } my $found_level = $1; $$output_ptr = $output; return $found_level; } # **************************************************************** # Run a build and put the output someplace where we can find it sub run_one_subbuild ($) { my ($fn) = @_; my $output = ""; unlink $fn; # Just in case it already exists, get rid of it my $lv = run_one_build (\$output); open (F, ">$fn") or die "run_one_subbuild: Can't open the output file"; print F $output; close F or die "run_one_subbuild: Can't close the output file"; } # **************************************************************** # Run a series of subbuilds in parallel and return an array of results my @outputs; my @found_levels; sub run_parallel_subbuilds () { if ($debug) { print "run_parallel_subbuilds starting in process $$\n"; } ##my ($level_ptr, $output_ptr) = $_; my @filenames = (); foreach my $i (1..$parallel) { my $fn = "/tmp/bss.$$.$i"; if ($debug) { print "Using temp filename $fn\n"; } push @filenames, $fn; my $cpid = fork; if (! defined($cpid)) { die "Fork failed"; } if ($cpid) { # Do we need to do anything here? } else { run_one_subbuild ($fn); exit; } } while (wait != -1) { } ##my @outputs = (); ##my @found_levels = (); foreach my $fn (@filenames) { my $output = `cat $fn`; unlink $fn; my $level = 0; if ($output =~ /Solver level:\s+([0-9]+)/) { $level = $1; } if ($debug) { my $ln = length $output; print "Output file $fn: length = $ln; found level: $level\n"; } push @outputs, \$output; push @found_levels, $level; } ##@$level_ptr = @found_levels; ##@$output_ptr = @outputs; } # **************************************************************** sub run_until_win () { while (! $won) { $pass++; print "Starting pass $pass\n"; @outputs = (); @found_levels = (); if (! $parallel) { my $op; my $fl = run_one_build (\$op); push @outputs, \$op; push @found_levels, $fl; } else { if ($debug) { print "run_until_win calling run_parallel_subbuilds in process $$\n"; } run_parallel_subbuilds (); if ($debug) { my $oc = scalar(@outputs); print "Found $oc levels and output files\n"; } print "Levels found on pass $pass: "; foreach my $l (@found_levels) { print "$l "; } print "\n"; } foreach my $i (0..$#found_levels) { my $found_level = $found_levels[$i]; my $opp = $outputs[$i]; my $output = $$opp; my @irregular_multiset_hits = (0,0,0,0,0,0,0,0,0,0); my @irregular_internal_hits = (0,0,0,0,0,0,0,0,0,0); my $total_irregular_hits = 0; my $squashes = 0; my $total_l4_moves = 0; my $l_1_2_cells = 0; my $l_3_cells = 0; my $l_4_cells = 0; my $l_5_cells = 0; my $l3_actions = 0; my $build_in_pattern_leaked = 0; my $backtrack_depth = 0; my $stars_found = 0; if (! $found_level) { next; } ##print "Pass done -- level produced = $found_level\n"; if ($found_level < $min_level) { next; } if ($output =~ /build_in_pattern LEAKED/) { $build_in_pattern_leaked = 1; } my $notes = ""; if ($found_level >= 3 && ($output =~ /Optimized board is in (\S+)/ || $output =~ /Constructed board is in (\S+)/)) { my $board_location = $1; my $solution_info = `solve_sudoku -no_show $board_location`; if ($solution_info =~ /Solved cells at each level:\s+1:(\d+),\s*2:(\d+),\s*3:(\d+)/) { $l_1_2_cells = $1 + $2; $l_3_cells = $3; $notes .= " Level 1&2 cells: ${l_1_2_cells}; Level 3 cells: ${l_3_cells}\n"; } if ($solution_info =~ /Solved cells at each level:\s+1:\d+,\s*2:\d+,\s*3:\d+,\s*4:(\d+)/) { $l_4_cells = $1; $notes .= " Level 4 cells: ${l_4_cells}\n"; } if ($solution_info =~ /Solved cells at each level:\s+1:\d+,\s*2:\d+,\s*3:\d+,\s*4:\d+,\s*5:(\d+)/) { $l_5_cells = $1; $notes .= " Level 5 cells: ${l_5_cells}\n"; } if ($solution_info =~ /\n\s*l3 actions:\s+([1-9]\d*)/) { $l3_actions = $1; $notes .= " Level 3 actions: ${l3_actions}\n"; } foreach my $h (1..8) { if ($solution_info =~ /(\d+)\s*productive irregular multiset hits of size ${h}/) { $irregular_multiset_hits[$h] = $1; $total_irregular_hits += $1; $total_l4_moves += $1; } } my $emf_zaps = 0; if ($found_level >= 5) { if ($solution_info =~ /Exterior multiset freedom zaps:\s*(\d+)/) { $emf_zaps = $1; } else { die "Failed to find exterior freedom zaps line in output\n"; } } foreach my $h (1..8) { if ($solution_info =~ /(\d+)\s*productive irregular internal hits of size $h/) { $irregular_internal_hits[$h] = $1; $total_irregular_hits += $1; $total_l4_moves += $1; } } if ($emf_zaps && !$total_irregular_hits) { die "Found ${emf_zaps} freedom zaps but no irregular hits\n"; } if ($solution_info =~ /\n\s*(\d+) total squash operations/) { $squashes = $1; $total_l4_moves += $1; } $notes .= " ${squashes} squash operations\n"; for my $h (1..8) { my $eh = $irregular_multiset_hits[$h]; my $ih = $irregular_internal_hits[$h]; if ($eh) { $notes .= " $eh multiset hits of size $h\n"; } if ($ih) { $notes .= " $ih INTERNAL hits of size $h\n"; } } $notes .= " ${total_l4_moves} total level 4 and 5 moves\n"; if ($solution_info =~ /Stars:\s+([0-9]+)/) { $stars_found = $1; } $notes .= " ${stars_found} stars\n"; if ($found_level >= 6 && $solution_info =~ /Final depth:\s+(\d+)/) { $backtrack_depth = $1; $notes .= " Went ${backtrack_depth} levels deep in guessing\n"; } } if ($l_1_2_cells > $l_1_2_cells_wanted || $l_3_cells < $l3_cells_wanted || $l_4_cells < $l4_cells_wanted || $l_5_cells < $l5_cells_wanted || $l3_actions < $l3_operations_wanted || $total_l4_moves < $l4_operations_wanted || $total_irregular_hits < $irregular_sets_wanted || $backtrack_depth < $backtrack_depth_wanted || $stars_found < $stars_wanted || $build_in_pattern_leaked) { print "****************************************************************\n"; print "Achieved desired level but other stuff wasn't good enough.\n"; if ($l_1_2_cells_wanted < 100) { print " Wanted at most ${l_1_2_cells_wanted} l1 and l2 cells, found ${l_1_2_cells}\n"; } if ($l3_cells_wanted > 0) { print " Wanted at least ${l3_cells_wanted} l3 cells, found ${l_3_cells}\n"; } if ($l4_cells_wanted > 0) { print " Wanted at least ${l4_cells_wanted} l4 cells, found ${l_4_cells}\n"; } if ($l5_cells_wanted > 0) { print " Wanted at least ${l5_cells_wanted} l5 cells, found ${l_5_cells}\n"; } if ($l3_operations_wanted > 0) { print " Wanted at least ${l3_operations_wanted} l3 operations, found ${l3_actions}\n"; } if ($l4_operations_wanted > 0) { print " Wanted at least ${l4_operations_wanted} l4 moves, found ${total_l4_moves}\n"; } if ($irregular_sets_wanted > 0) { print " Wanted at least ${irregular_sets_wanted} external+internal l4 sets, found ${total_irregular_hits}\n"; } if ($backtrack_depth_wanted > 0) { print " Wanted at least ${backtrack_depth_wanted} guess levels, found ${backtrack_depth}\n"; } if ($stars_wanted > 0) { print " Wanted at least ${stars_wanted} stars, found ${stars_found}\n"; } if ($build_in_pattern_leaked) { print " And build_in_pattern LEAKED.\n"; } print "\nNotes:\n"; print $notes; print "****************************************************************\n"; } else { print "****************************************************************\n"; print "Won! Output:\n"; print "****************************************************************\n"; print $output; print "****************************************************************\n"; print "Notes:\n"; print $notes; $won = 1; } } } } run_until_win;