Data update
This commit is contained in:
parent
29a5eea0d4
commit
5c1bb7bfa9
2011 changed files with 35081 additions and 3229 deletions
255
Task/Nonogram-solver/Jq/nonogram-solver.jq
Normal file
255
Task/Nonogram-solver/Jq/nonogram-solver.jq
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
### For the sake of gojq
|
||||
# This def of transpose can be omitted if using the C implementation of jq.
|
||||
# Rows are padded with nulls so the result is always rectangular.
|
||||
def transpose: [range(0; map(length)|max // 0) as $i | [.[][$i]]];
|
||||
|
||||
###############################################################################
|
||||
### Generic functions
|
||||
|
||||
def array($n): [range(0;$n) as $i | .];
|
||||
|
||||
def inform(msg):
|
||||
. as $in
|
||||
| ("\(msg)\n" | stderr)
|
||||
| $in;
|
||||
|
||||
def sum(stream): reduce stream as $x (0; . + $x);
|
||||
|
||||
def count(stream): sum(stream|1);
|
||||
|
||||
# null-based elementwise "or" for arrays
|
||||
def or_array($b):
|
||||
. as $a
|
||||
| reduce range(0;length) as $i ([];
|
||||
. + [if $a[$i] == null then $b[$i] else $a[$i] end]);
|
||||
|
||||
# null-based elementwise "or" for matrices
|
||||
def or_matrix($b):
|
||||
. as $a
|
||||
| reduce range(0;length) as $i ([];
|
||||
. + [$a[$i] | or_array($b[$i])] );
|
||||
|
||||
# Pretty-print the input, which must be an array but is normally a matrix.
|
||||
# Notice that null nows and null values are handled specially.
|
||||
def pp:
|
||||
.[]
|
||||
| if . == null then ""
|
||||
else
|
||||
reduce .[] as $x ("";
|
||||
. + if $x == 0 then " ."
|
||||
elif $x == null then " "
|
||||
else " 1"
|
||||
end )
|
||||
end;
|
||||
|
||||
|
||||
### Nonogram Solver
|
||||
|
||||
## Building blocks:
|
||||
|
||||
# $a and $b are normally arrays with $a | length <= $b | length
|
||||
# Output: an array of the same length as $a, such that
|
||||
# the element at $i is ( a[$i] == $b[$i] ? a[$i] : null )
|
||||
def common($a; $b):
|
||||
[ range(0;$a|length) as $i
|
||||
| $a[$i]
|
||||
| if . == $b[$i] then . else null end ];
|
||||
|
||||
# If `stream` is a stream of arrays, the output will be the reduction
|
||||
# of the stream based on common/2, except that the output is null if
|
||||
# the arrays have no elements in common.
|
||||
# The implementation is intended to serve as the complete specification.
|
||||
# Notice in particular the "short-circuit" semantics, and
|
||||
# that an occurrence of null or [] in the stream will produce `null`
|
||||
def common(stream):
|
||||
def count: sum( .common[] | if . == null then 0 else 1 end );
|
||||
first(
|
||||
foreach (stream, null) as $x ({common: null, emit: 0};
|
||||
if $x == null then .emit = .common
|
||||
elif .common == null then .common = $x
|
||||
else .common |= common(.; $x)
|
||||
| if count == 0 then .emit=null end
|
||||
end)
|
||||
| select(.emit != 0).emit);
|
||||
|
||||
# $common should be null or an array. If $common is null, any input
|
||||
# is agreeable. Otherwise, if the input is an array, then the output
|
||||
# is true or false as the input is in point-wise agreement with
|
||||
# $common in the sense that an occurrence of `null` in $common imposes
|
||||
# no constraint on the corresponding item in the input.
|
||||
# The implementation is intended to serve as the complete specification.
|
||||
def agreeable($common):
|
||||
if $common == null then true
|
||||
else . as $in
|
||||
| all( range(0; $common|length);
|
||||
. as $i
|
||||
| if $common[$i] == null then true
|
||||
else $common[$i] == $in[$i]
|
||||
end)
|
||||
end;
|
||||
|
||||
# Convert an alphabetic specification of a sequence of runs
|
||||
# to an array of integer arrays, e.g. "AB A" => [[1,2], [1]]
|
||||
# Use _ for 0
|
||||
def alphabet2runs:
|
||||
split(" ")
|
||||
| map( [explode[] | if . == 95 then 0 else . - 64 end]);
|
||||
|
||||
# A recursive helper function for runs2rows/2.
|
||||
# Output: a stream of the rows with the required runs.
|
||||
# Each row begins with a 0 for ensuring separation between the runs.
|
||||
def genSequence($ones; $numZeros):
|
||||
if $ones|length == 0 then (0|array($numZeros))
|
||||
else range(1; $numZeros - ($ones|length) + 2) as $x
|
||||
| genSequence($ones[1:]; $numZeros - $x) as $tail
|
||||
| (0|array($x)) + $ones[0] + $tail
|
||||
end;
|
||||
|
||||
# $rows should be an array of integers specifying the run lengths in a row of length $len.
|
||||
# Output: a stream of arrays of length $len with the specified runs
|
||||
def runs2rows($runs; $len):
|
||||
($runs|map(select(. != 0))) as $runs # ignore 0s
|
||||
| if ($runs|length) == 1 and $runs[0] == $len then 1|array($len)
|
||||
else sum($runs[]) as $sum
|
||||
| if $len - $sum <= 0
|
||||
then empty
|
||||
else
|
||||
(reduce ($runs|map(select(. != 0)))[] as $run ([]; . + [1|array($run)] )) as $data
|
||||
| genSequence($data; $len - $sum + 1)[1:]
|
||||
end
|
||||
end;
|
||||
|
||||
# Check the acceptability of . based on $common
|
||||
def acceptable($common):
|
||||
. as $in
|
||||
| $common == null
|
||||
or all( range(0;length); . as $i | $common[$i] | IN(null, $in[$i])) ;
|
||||
|
||||
# The rows that are acceptable w.r.t. $common
|
||||
def runs2rows($runs; $len; $common):
|
||||
runs2rows($runs; $len)
|
||||
| select(acceptable($common));
|
||||
|
||||
# Setup {rows, cols, common} based on $rowruns and $colruns
|
||||
def setup($rowruns; $colruns):
|
||||
($rowruns | length) as $nrows
|
||||
| ($colruns | length) as $ncols
|
||||
|
||||
| ($rowruns | map(common(runs2rows(.; $ncols)) ) ) as $commonRows
|
||||
|
||||
| ($colruns | map(common(runs2rows(.; $nrows)) ) ) as $commonCols
|
||||
| ($commonCols | transpose | or_matrix($commonRows)) as $common
|
||||
| ($common | transpose) as $ct
|
||||
| { rows: [range(0; $nrows) as $i | [runs2rows($rowruns[$i]; $ncols; $common[$i]) ]],
|
||||
cols: [range(0; $ncols) as $i | [runs2rows($colruns[$i]; $nrows; $ct[$i]) ]],
|
||||
common: $common,
|
||||
matrix: [],
|
||||
};
|
||||
|
||||
# Winnow .rows and .cols based on point-wise commonality
|
||||
def winnow:
|
||||
.reduction = 0
|
||||
| (.rows|length) as $nrows
|
||||
| (.cols|length) as $ncols
|
||||
# Winnow each row that has not already been fixed
|
||||
| reduce range(0; $nrows) as $i (.;
|
||||
if (.rows[$i] | length) != 1
|
||||
then common(.rows[$i][]) as $common
|
||||
| (.rows[$i] | length) as $before
|
||||
| .rows[$i] |= map(select(agreeable($common)))
|
||||
| .reduction += ((.rows[$i]|length) - $before)
|
||||
end )
|
||||
# Winnow each col that has not already been fixed
|
||||
| reduce range(0; $ncols) as $i (.;
|
||||
if (.cols[$i] | length) != 1
|
||||
then common(.cols[$i][]) as $common
|
||||
| (.cols[$i] | length) as $before
|
||||
| .cols[$i] |= map(select(agreeable($common)))
|
||||
| .reduction += ((.cols[$i]|length) - $before )
|
||||
end)
|
||||
| inform("reduction by \(.reduction)")
|
||||
| if .reduction != 0 then winnow end ;
|
||||
|
||||
|
||||
# Input: {rows, cols}
|
||||
# If setting .matrix[$i] to $row is feasible,
|
||||
# then winnow .cols accordingly and finally set .matrix[$i] to $row
|
||||
def set($i; $row):
|
||||
(.cols | length) as $ncols
|
||||
| .cols as $cols
|
||||
| if all(range(0; $ncols);
|
||||
. as $j
|
||||
| any( range(0; $cols[$j]|length);
|
||||
$cols[$j][.][$i] == $row[$j] ) )
|
||||
then foreach range(0; $ncols) as $j (.;
|
||||
.cols[$j][] |= select( .[$i] == $row[$j] ) )
|
||||
else empty
|
||||
end
|
||||
| .matrix[$i] = $row;
|
||||
|
||||
# Input: {rows, cols, matrix} where .matrix is the candidate matrix constructed so far
|
||||
# After all the columnwise- and rowwise-constraints have been examined...
|
||||
def solve($colruns):
|
||||
(.rows|length) as $nrows
|
||||
| (.cols|length) as $ncols
|
||||
| (.matrix|length) as $m
|
||||
| if $m == $nrows
|
||||
then .
|
||||
else range(0; .rows[$m]|length) as $p
|
||||
| .rows[$m][$p] as $row
|
||||
| set($m; $row)
|
||||
| solve($colruns)
|
||||
end ;
|
||||
|
||||
# Generate all solutions
|
||||
def generate( $rowruns; $colruns ):
|
||||
($rowruns | length) as $nrows
|
||||
| ($colruns | length) as $ncols
|
||||
| setup( $rowruns; $colruns )
|
||||
| winnow
|
||||
| solve($colruns);
|
||||
|
||||
# Generate a single solution
|
||||
# Top-level: $p should be an array of two alphabetic ([A-Z_]) strings representing
|
||||
# the row-wise and columnwise run lengths, respectively,
|
||||
# with _ signifying 0, A signifying 1, etc.
|
||||
def generate($p):
|
||||
($p[0] | alphabet2runs) as $rowruns
|
||||
| ($p[1] | alphabet2runs) as $colruns
|
||||
| first( generate($rowruns; $colruns ) ) ;
|
||||
|
||||
# Top-level convenience function, where p is a puzzle specification in alphabetic form.
|
||||
def puzzle($title; p):
|
||||
$title,
|
||||
( generate(p)
|
||||
| .matrix
|
||||
| pp ),
|
||||
"";
|
||||
|
||||
def p0: [ "B A A", "B A A"];
|
||||
|
||||
def p1:
|
||||
["C BA CB BB F AE F A B", "AB CA AE GA E C D C"];
|
||||
|
||||
def p2: [
|
||||
"F CAC ACAC CN AAA AABB EBB EAA ECCC HCCC",
|
||||
"D D AE CD AE A DA BBB CC AAB BAA AAB DA AAB AAA BAB AAA CD BBA DA"
|
||||
];
|
||||
|
||||
def p3: [
|
||||
"CA BDA ACC BD CCAC CBBAC BBBBB BAABAA ABAD AABB BBH " +
|
||||
"BBBD ABBAAA CCEA AACAAB BCACC ACBH DCH ADBE ADBB DBE ECE DAA DB CC",
|
||||
"BC CAC CBAB BDD CDBDE BEBDF ADCDFA DCCFB DBCFC ABDBA BBF AAF BADB DBF " +
|
||||
"AAAAD BDG CEF CBDB BBB FC"
|
||||
];
|
||||
|
||||
def p4: [
|
||||
"E BCB BEA BH BEK AABAF ABAC BAA BFB OD JH BADCF Q Q R AN AAN EI H G",
|
||||
"E CB BAB AAA AAA AC BB ACC ACCA AGB AIA AJ AJ " +
|
||||
"ACE AH BAF CAG DAG FAH FJ GJ ADK ABK BL CM"
|
||||
];
|
||||
|
||||
puzzle("p1"; p1),
|
||||
puzzle("p2"; p2),
|
||||
puzzle("p3"; p3),
|
||||
puzzle("p4"; p4)
|
||||
Loading…
Add table
Add a link
Reference in a new issue