eyeling 1.33.4 → 1.33.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -105
- package/docs/eyelang-guide.md +551 -0
- package/{examples/eyelang/SPEC.md → docs/eyelang-language-reference.md} +6 -10
- package/lib/eyelang/builtins/arithmetic.js +1 -3
- package/lib/eyelang/builtins/formula.js +0 -16
- package/lib/eyelang/builtins/lists.js +0 -5
- package/lib/eyelang/builtins/registry.js +1 -2
- package/lib/eyelang/builtins/search.js +1 -61
- package/lib/eyelang/builtins/strings.js +10 -14
- package/package.json +2 -1
- package/test/eyelang/conformance/README.md +7 -7
- package/test/eyelang/conformance/cases/core/001_fact_output.pl +1 -1
- package/test/eyelang/conformance/cases/core/002_rule_recursion.pl +1 -1
- package/test/eyelang/conformance/cases/core/003_terms_and_readback.pl +1 -1
- package/test/eyelang/conformance/cases/core/004_conjunction_and_parentheses.pl +1 -1
- package/test/eyelang/conformance/cases/core/005_list_deconstruction.pl +1 -1
- package/test/eyelang/conformance/cases/core/006_comma_formula_data.pl +1 -1
- package/test/eyelang/conformance/cases/core/007_anonymous_variables.pl +1 -1
- package/test/eyelang/conformance/cases/core/008_graphic_atoms.pl +1 -1
- package/test/eyelang/conformance/cases/core/009_comments_and_whitespace.pl +1 -1
- package/test/eyelang/conformance/cases/core/010_variable_scope_and_reuse.pl +1 -1
- package/test/eyelang/conformance/cases/core/011_predicate_arity.pl +1 -1
- package/test/eyelang/conformance/cases/core/012_nested_compound_unification.pl +1 -1
- package/test/eyelang/conformance/cases/core/013_multiple_clauses_order.pl +1 -1
- package/test/eyelang/conformance/cases/core/014_failure_filters_answers.pl +1 -1
- package/test/eyelang/conformance/cases/core/015_improper_list_unification.pl +1 -1
- package/test/eyelang/conformance/cases/core/016_zero_arity_compound.pl +1 -1
- package/test/eyelang/conformance/cases/core/017_three_step_recursion.pl +1 -1
- package/test/eyelang/conformance/cases/core/018_quoted_atom_readback.pl +1 -1
- package/test/eyelang/conformance/cases/core/019_parenthesized_three_conjuncts.pl +1 -1
- package/test/eyelang/conformance/cases/core/020_nested_list_terms.pl +1 -1
- package/test/eyelang/conformance/cases/core/021_repeated_variable_head.pl +1 -1
- package/test/eyelang/conformance/cases/core/022_rule_head_structure.pl +1 -1
- package/test/eyelang/conformance/cases/core/023_quoted_escapes_readback.pl +1 -1
- package/test/eyelang/conformance/cases/core/024_numeric_literal_readback.pl +1 -1
- package/test/eyelang/conformance/cases/core/025_body_parentheses_with_formula_data.pl +1 -1
- package/test/eyelang/conformance/cases/core/026_underscore_named_variable_reuse.pl +1 -1
- package/test/eyelang/conformance/cases/extension/001_default_derived_output.pl +1 -1
- package/test/eyelang/conformance/cases/extension/002_materialize_focus.pl +1 -1
- package/test/eyelang/conformance/cases/extension/003_arithmetic_and_comparison.pl +1 -2
- package/test/eyelang/conformance/cases/extension/004_strings_and_atoms.pl +1 -3
- package/test/eyelang/conformance/cases/extension/005_lists_aggregation_ordering.pl +1 -1
- package/test/eyelang/conformance/cases/extension/006_formula_terms.pl +1 -2
- package/test/eyelang/conformance/cases/extension/007_negation_once_generators.pl +1 -1
- package/test/eyelang/conformance/cases/extension/008_equality_and_inequality.pl +1 -1
- package/test/eyelang/conformance/cases/extension/009_list_relations.pl +1 -2
- package/test/eyelang/conformance/cases/extension/010_append_splits.pl +1 -1
- package/test/eyelang/conformance/cases/extension/011_matching_and_comparison.pl +1 -1
- package/test/eyelang/conformance/cases/extension/012_memoize_declaration.pl +1 -1
- package/test/eyelang/conformance/cases/extension/013_numeric_functions.pl +1 -1
- package/test/eyelang/conformance/cases/extension/014_between_enumeration.pl +1 -1
- package/test/eyelang/conformance/cases/extension/015_smallest_divisor.pl +1 -1
- package/test/eyelang/conformance/cases/extension/016_negation_filter.pl +1 -1
- package/test/eyelang/conformance/cases/extension/017_once_user_predicate.pl +1 -1
- package/test/eyelang/conformance/cases/extension/018_findall_user_goal.pl +1 -1
- package/test/eyelang/conformance/cases/extension/019_sort_deduplicates_atoms.pl +1 -1
- package/test/eyelang/conformance/cases/extension/020_append_bound_prefix_suffix.pl +1 -1
- package/test/eyelang/conformance/cases/extension/021_nth0_index_generation.pl +1 -1
- package/test/eyelang/conformance/cases/extension/022_set_nth0_edges.pl +1 -1
- package/test/eyelang/conformance/cases/extension/023_select_duplicate_occurrences.pl +1 -1
- package/test/eyelang/conformance/cases/extension/024_not_member_filter.pl +1 -1
- package/test/eyelang/conformance/cases/extension/026_nested_formula_terms.pl +1 -2
- package/test/eyelang/conformance/cases/extension/027_materialize_excludes_source_fact.pl +1 -1
- package/test/eyelang/conformance/cases/extension/028_numeric_and_lexical_comparison.pl +1 -1
- package/test/eyelang/conformance/cases/extension/029_string_matching_filters.pl +1 -1
- package/test/eyelang/conformance/cases/extension/030_string_and_atom_concat.pl +1 -2
- package/test/eyelang/conformance/cases/extension/035_date_difference.pl +1 -1
- package/test/eyelang/conformance/cases/extension/036_extended_gcd.pl +1 -1
- package/test/eyelang/conformance/cases/extension/037_collatz_trajectory.pl +1 -1
- package/test/eyelang/conformance/cases/extension/038_kaprekar_steps.pl +1 -1
- package/test/eyelang/conformance/cases/extension/039_goldbach_pair.pl +1 -1
- package/test/eyelang/conformance/cases/extension/040_matrix_operations.pl +1 -1
- package/test/eyelang/conformance/cases/extension/042_n_queens_small.pl +1 -1
- package/test/eyelang/conformance/cases/extension/043_cnf_model.pl +1 -1
- package/test/eyelang/conformance/cases/extension/045_alphametic_sum_small.pl +1 -1
- package/test/eyelang/conformance/cases/extension/046_bounded_subset.pl +1 -1
- package/test/eyelang/conformance/expected/extension/003_arithmetic_and_comparison.out +0 -1
- package/test/eyelang/conformance/expected/extension/004_strings_and_atoms.out +0 -2
- package/test/eyelang/conformance/expected/extension/006_formula_terms.out +0 -2
- package/test/eyelang/conformance/expected/extension/009_list_relations.out +0 -1
- package/test/eyelang/conformance/expected/extension/026_nested_formula_terms.out +0 -3
- package/test/eyelang/conformance/expected/extension/030_string_and_atom_concat.out +0 -1
- package/test/eyelang/run-regression.mjs +2 -2
- package/test/packlist.test.js +2 -0
- package/examples/eyelang/INTEGRATION.md +0 -20
- package/examples/eyelang/README.md +0 -567
- package/examples/eyelang/exact-cover-sudoku.pl +0 -113
- package/examples/eyelang/output/exact-cover-sudoku.pl +0 -3
- package/examples/eyelang/output/sudoku.pl +0 -2
- package/examples/eyelang/sudoku.pl +0 -20
- package/lib/eyelang/builtins/sudoku.js +0 -141
- package/test/eyelang/conformance/cases/extension/025_is_list_filter.pl +0 -5
- package/test/eyelang/conformance/cases/extension/025_is_list_filter.query +0 -1
- package/test/eyelang/conformance/cases/extension/041_atom_range_generators.pl +0 -5
- package/test/eyelang/conformance/cases/extension/044_cover9_filter.pl +0 -6
- package/test/eyelang/conformance/expected/extension/025_is_list_filter.out +0 -1
- package/test/eyelang/conformance/expected/extension/041_atom_range_generators.out +0 -8
- package/test/eyelang/conformance/expected/extension/044_cover9_filter.out +0 -2
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
% Exact-cover-style 9x9 Sudoku without cut.
|
|
2
|
-
%
|
|
3
|
-
% Instead of generating every row permutation, each row has three candidate
|
|
4
|
-
% rows that already satisfy the visible givens from a classic Sudoku puzzle.
|
|
5
|
-
% The solver then chooses one candidate per row and checks the exact-cover
|
|
6
|
-
% constraints for all columns and 3x3 boxes.
|
|
7
|
-
|
|
8
|
-
% Output declarations: materialize/2 selects the relations written to this example's golden output.
|
|
9
|
-
materialize(status, 2).
|
|
10
|
-
materialize(solution, 2).
|
|
11
|
-
materialize(firstRow, 2).
|
|
12
|
-
|
|
13
|
-
% Program structure: facts set up the scenario, and rules derive the materialized conclusions.
|
|
14
|
-
% Derivation rules: each rule below contributes one logical step toward the displayed results.
|
|
15
|
-
cover9(Cells) :-
|
|
16
|
-
sort(Cells, [1, 2, 3, 4, 5, 6, 7, 8, 9]).
|
|
17
|
-
|
|
18
|
-
% Puzzle givens, with dots shown in the comment for readability:
|
|
19
|
-
% 53..7....
|
|
20
|
-
% 6..195...
|
|
21
|
-
% .98....6.
|
|
22
|
-
% 8...6...3
|
|
23
|
-
% 4..8.3..1
|
|
24
|
-
% 7...2...6
|
|
25
|
-
% .6....28.
|
|
26
|
-
% ...419..5
|
|
27
|
-
% ....8..79
|
|
28
|
-
|
|
29
|
-
row_candidate(1, [5, 3, 6, 4, 7, 8, 9, 1, 2]).
|
|
30
|
-
row_candidate(1, [5, 3, 8, 6, 7, 4, 9, 1, 2]).
|
|
31
|
-
row_candidate(1, [5, 3, 4, 6, 7, 8, 9, 1, 2]).
|
|
32
|
-
|
|
33
|
-
row_candidate(2, [6, 2, 7, 1, 9, 5, 3, 4, 8]).
|
|
34
|
-
row_candidate(2, [6, 3, 2, 1, 9, 5, 7, 4, 8]).
|
|
35
|
-
row_candidate(2, [6, 7, 2, 1, 9, 5, 3, 4, 8]).
|
|
36
|
-
|
|
37
|
-
row_candidate(3, [3, 9, 8, 1, 4, 2, 5, 6, 7]).
|
|
38
|
-
row_candidate(3, [4, 9, 8, 3, 1, 2, 5, 6, 7]).
|
|
39
|
-
row_candidate(3, [1, 9, 8, 3, 4, 2, 5, 6, 7]).
|
|
40
|
-
|
|
41
|
-
row_candidate(4, [8, 9, 5, 7, 6, 1, 4, 2, 3]).
|
|
42
|
-
row_candidate(4, [8, 7, 9, 5, 6, 1, 4, 2, 3]).
|
|
43
|
-
row_candidate(4, [8, 5, 9, 7, 6, 1, 4, 2, 3]).
|
|
44
|
-
|
|
45
|
-
row_candidate(5, [4, 6, 2, 8, 5, 3, 7, 9, 1]).
|
|
46
|
-
row_candidate(5, [4, 5, 6, 8, 2, 3, 7, 9, 1]).
|
|
47
|
-
row_candidate(5, [4, 2, 6, 8, 5, 3, 7, 9, 1]).
|
|
48
|
-
|
|
49
|
-
row_candidate(6, [7, 3, 1, 9, 2, 4, 8, 5, 6]).
|
|
50
|
-
row_candidate(6, [7, 9, 3, 1, 2, 4, 8, 5, 6]).
|
|
51
|
-
row_candidate(6, [7, 1, 3, 9, 2, 4, 8, 5, 6]).
|
|
52
|
-
|
|
53
|
-
row_candidate(7, [1, 6, 9, 5, 3, 7, 2, 8, 4]).
|
|
54
|
-
row_candidate(7, [5, 6, 1, 9, 3, 7, 2, 8, 4]).
|
|
55
|
-
row_candidate(7, [9, 6, 1, 5, 3, 7, 2, 8, 4]).
|
|
56
|
-
|
|
57
|
-
row_candidate(8, [8, 2, 7, 4, 1, 9, 6, 3, 5]).
|
|
58
|
-
row_candidate(8, [7, 8, 2, 4, 1, 9, 6, 3, 5]).
|
|
59
|
-
row_candidate(8, [2, 8, 7, 4, 1, 9, 6, 3, 5]).
|
|
60
|
-
|
|
61
|
-
row_candidate(9, [4, 3, 5, 2, 8, 6, 1, 7, 9]).
|
|
62
|
-
row_candidate(9, [5, 4, 3, 2, 8, 6, 1, 7, 9]).
|
|
63
|
-
row_candidate(9, [3, 4, 5, 2, 8, 6, 1, 7, 9]).
|
|
64
|
-
|
|
65
|
-
sudoku9([
|
|
66
|
-
[A1, A2, A3, A4, A5, A6, A7, A8, A9],
|
|
67
|
-
[B1, B2, B3, B4, B5, B6, B7, B8, B9],
|
|
68
|
-
[C1, C2, C3, C4, C5, C6, C7, C8, C9],
|
|
69
|
-
[D1, D2, D3, D4, D5, D6, D7, D8, D9],
|
|
70
|
-
[E1, E2, E3, E4, E5, E6, E7, E8, E9],
|
|
71
|
-
[F1, F2, F3, F4, F5, F6, F7, F8, F9],
|
|
72
|
-
[G1, G2, G3, G4, G5, G6, G7, G8, G9],
|
|
73
|
-
[H1, H2, H3, H4, H5, H6, H7, H8, H9],
|
|
74
|
-
[I1, I2, I3, I4, I5, I6, I7, I8, I9]
|
|
75
|
-
]) :-
|
|
76
|
-
row_candidate(1, [A1, A2, A3, A4, A5, A6, A7, A8, A9]),
|
|
77
|
-
row_candidate(2, [B1, B2, B3, B4, B5, B6, B7, B8, B9]),
|
|
78
|
-
row_candidate(3, [C1, C2, C3, C4, C5, C6, C7, C8, C9]),
|
|
79
|
-
row_candidate(4, [D1, D2, D3, D4, D5, D6, D7, D8, D9]),
|
|
80
|
-
row_candidate(5, [E1, E2, E3, E4, E5, E6, E7, E8, E9]),
|
|
81
|
-
row_candidate(6, [F1, F2, F3, F4, F5, F6, F7, F8, F9]),
|
|
82
|
-
row_candidate(7, [G1, G2, G3, G4, G5, G6, G7, G8, G9]),
|
|
83
|
-
row_candidate(8, [H1, H2, H3, H4, H5, H6, H7, H8, H9]),
|
|
84
|
-
row_candidate(9, [I1, I2, I3, I4, I5, I6, I7, I8, I9]),
|
|
85
|
-
|
|
86
|
-
cover9([A1, B1, C1, D1, E1, F1, G1, H1, I1]),
|
|
87
|
-
cover9([A2, B2, C2, D2, E2, F2, G2, H2, I2]),
|
|
88
|
-
cover9([A3, B3, C3, D3, E3, F3, G3, H3, I3]),
|
|
89
|
-
cover9([A4, B4, C4, D4, E4, F4, G4, H4, I4]),
|
|
90
|
-
cover9([A5, B5, C5, D5, E5, F5, G5, H5, I5]),
|
|
91
|
-
cover9([A6, B6, C6, D6, E6, F6, G6, H6, I6]),
|
|
92
|
-
cover9([A7, B7, C7, D7, E7, F7, G7, H7, I7]),
|
|
93
|
-
cover9([A8, B8, C8, D8, E8, F8, G8, H8, I8]),
|
|
94
|
-
cover9([A9, B9, C9, D9, E9, F9, G9, H9, I9]),
|
|
95
|
-
|
|
96
|
-
cover9([A1, A2, A3, B1, B2, B3, C1, C2, C3]),
|
|
97
|
-
cover9([A4, A5, A6, B4, B5, B6, C4, C5, C6]),
|
|
98
|
-
cover9([A7, A8, A9, B7, B8, B9, C7, C8, C9]),
|
|
99
|
-
cover9([D1, D2, D3, E1, E2, E3, F1, F2, F3]),
|
|
100
|
-
cover9([D4, D5, D6, E4, E5, E6, F4, F5, F6]),
|
|
101
|
-
cover9([D7, D8, D9, E7, E8, E9, F7, F8, F9]),
|
|
102
|
-
cover9([G1, G2, G3, H1, H2, H3, I1, I2, I3]),
|
|
103
|
-
cover9([G4, G5, G6, H4, H5, H6, I4, I5, I6]),
|
|
104
|
-
cover9([G7, G8, G9, H7, H8, H9, I7, I8, I9]).
|
|
105
|
-
|
|
106
|
-
status(exact_cover_sudoku, solved) :-
|
|
107
|
-
once(sudoku9(_Grid)).
|
|
108
|
-
|
|
109
|
-
solution(exact_cover_sudoku, Grid) :-
|
|
110
|
-
once(sudoku9(Grid)).
|
|
111
|
-
|
|
112
|
-
firstRow(exact_cover_sudoku, Row) :-
|
|
113
|
-
once(sudoku9([Row|_Rows])).
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
status(exact_cover_sudoku, solved).
|
|
2
|
-
solution(exact_cover_sudoku, [[5, 3, 4, 6, 7, 8, 9, 1, 2], [6, 7, 2, 1, 9, 5, 3, 4, 8], [1, 9, 8, 3, 4, 2, 5, 6, 7], [8, 5, 9, 7, 6, 1, 4, 2, 3], [4, 2, 6, 8, 5, 3, 7, 9, 1], [7, 1, 3, 9, 2, 4, 8, 5, 6], [9, 6, 1, 5, 3, 7, 2, 8, 4], [2, 8, 7, 4, 1, 9, 6, 3, 5], [3, 4, 5, 2, 8, 6, 1, 7, 9]]).
|
|
3
|
-
firstRow(exact_cover_sudoku, [5, 3, 4, 6, 7, 8, 9, 1, 2]).
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
solution(classic, [[1, 6, 2, 8, 5, 7, 4, 9, 3], [5, 3, 4, 1, 2, 9, 6, 7, 8], [7, 8, 9, 6, 4, 3, 5, 2, 1], [4, 7, 5, 3, 1, 2, 9, 8, 6], [9, 1, 3, 5, 8, 6, 7, 4, 2], [6, 2, 8, 7, 9, 4, 1, 3, 5], [3, 5, 6, 4, 7, 8, 2, 1, 9], [2, 4, 1, 9, 3, 5, 8, 6, 7], [8, 9, 7, 2, 6, 1, 3, 5, 4]]).
|
|
2
|
-
solution(third, [[4, 7, 8, 2, 6, 9, 1, 3, 5], [2, 9, 5, 1, 7, 3, 4, 8, 6], [1, 3, 6, 4, 5, 8, 7, 9, 2], [9, 4, 2, 6, 3, 5, 8, 7, 1], [8, 1, 7, 9, 4, 2, 5, 6, 3], [6, 5, 3, 8, 1, 7, 2, 4, 9], [7, 2, 1, 3, 9, 4, 6, 5, 8], [5, 6, 9, 7, 8, 1, 3, 2, 4], [3, 8, 4, 5, 2, 6, 9, 1, 7]]).
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
% Generic 9x9 Sudoku example using the compiled sudoku/2 builtin.
|
|
2
|
-
%
|
|
3
|
-
% Puzzles are 81-character strings read row-major. Digits 1..9 are givens;
|
|
4
|
-
% 0, dot, or underscore marks a blank. The builtin also accepts a 9x9 list
|
|
5
|
-
% of integers in the same representation.
|
|
6
|
-
|
|
7
|
-
% Output declarations: materialize/2 selects the relations written to this example's golden output.
|
|
8
|
-
materialize(solution, 2).
|
|
9
|
-
|
|
10
|
-
% Program structure: facts set up the scenario, and rules derive the materialized conclusions.
|
|
11
|
-
puzzle(classic,
|
|
12
|
-
"100007090030129008009600500005300900010080002600794000350408219240005867897201304").
|
|
13
|
-
|
|
14
|
-
puzzle(third,
|
|
15
|
-
"078200000005000400100400092000035070007000500050810000720004008009000300000006910").
|
|
16
|
-
|
|
17
|
-
% Derivation rules: each rule below contributes one logical step toward the displayed results.
|
|
18
|
-
solution(Name, Rows) :-
|
|
19
|
-
puzzle(Name, Grid),
|
|
20
|
-
sudoku(Grid, Rows).
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
// Sudoku-specific helpers used by the example suite.
|
|
2
|
-
// The cover9/1 optimization prunes invalid row blocks early while leaving the declarative fallback available.
|
|
3
|
-
import { deref, lexicalValue, listFromItems, numberTerm, properListItems, unify } from '../term.js';
|
|
4
|
-
|
|
5
|
-
export const sudokuBuiltins = {
|
|
6
|
-
register(registry) {
|
|
7
|
-
registry.add('sudoku', 2, sudoku);
|
|
8
|
-
registry.add('cover9', 1, cover9, { deterministic: true, ready: cover9Ready });
|
|
9
|
-
}
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
function cover9Ready(goal, env) {
|
|
14
|
-
const items = properListItems(goal.args[0], env);
|
|
15
|
-
if (!items || items.length !== 9) return false;
|
|
16
|
-
for (const item of items) {
|
|
17
|
-
const text = lexicalValue(item, env);
|
|
18
|
-
if (!/^[1-9]$/.test(text ?? '')) return false;
|
|
19
|
-
}
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function* cover9({ goal, env }) {
|
|
24
|
-
// cover9/1 succeeds exactly when a nine-cell block contains each digit once.
|
|
25
|
-
// The bit mask makes this a constant-time duplicate check after the list is ground.
|
|
26
|
-
const items = properListItems(goal.args[0], env);
|
|
27
|
-
if (!items || items.length !== 9) return;
|
|
28
|
-
let mask = 0;
|
|
29
|
-
for (const item of items) {
|
|
30
|
-
const text = lexicalValue(item, env);
|
|
31
|
-
if (!/^[1-9]$/.test(text ?? '')) return;
|
|
32
|
-
const bit = 1 << (Number(text) - 1);
|
|
33
|
-
if (mask & bit) return;
|
|
34
|
-
mask |= bit;
|
|
35
|
-
}
|
|
36
|
-
if (mask === 0x1ff) yield env;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function* sudoku({ solver, goal, env }) {
|
|
40
|
-
const cells = parseGrid(goal.args[0], env);
|
|
41
|
-
if (!cells) return;
|
|
42
|
-
const masks = initMasks(cells);
|
|
43
|
-
if (!masks) return;
|
|
44
|
-
for (const solved of search(cells, masks)) {
|
|
45
|
-
const next = env.clone();
|
|
46
|
-
if (unify(goal.args[1], solutionTerm(solved), next)) yield next;
|
|
47
|
-
if (solver.solutionsSeen >= solver.solutionLimit) return;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function parseGrid(term, env) {
|
|
52
|
-
const resolved = deref(term, env);
|
|
53
|
-
if (['atom', 'string', 'number'].includes(resolved.type)) return parseGridString(resolved.name);
|
|
54
|
-
const rows = properListItems(term, env);
|
|
55
|
-
if (!rows || rows.length !== 9) return null;
|
|
56
|
-
const cells = [];
|
|
57
|
-
for (const row of rows) {
|
|
58
|
-
const cols = properListItems(row, env);
|
|
59
|
-
if (!cols || cols.length !== 9) return null;
|
|
60
|
-
for (const cell of cols) {
|
|
61
|
-
const text = lexicalValue(cell, env);
|
|
62
|
-
if (!/^\d+$/.test(text ?? '')) return null;
|
|
63
|
-
const value = Number(text);
|
|
64
|
-
if (value < 0 || value > 9) return null;
|
|
65
|
-
cells.push(value);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return cells;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function parseGridString(text) {
|
|
72
|
-
if (!text || text.length !== 81) return null;
|
|
73
|
-
const cells = [];
|
|
74
|
-
for (const ch of text) {
|
|
75
|
-
if (ch === '.' || ch === '_') cells.push(0);
|
|
76
|
-
else if (ch >= '0' && ch <= '9') cells.push(Number(ch));
|
|
77
|
-
else return null;
|
|
78
|
-
}
|
|
79
|
-
return cells;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function initMasks(cells) {
|
|
83
|
-
const row = Array(9).fill(0), col = Array(9).fill(0), box = Array(9).fill(0);
|
|
84
|
-
for (let i = 0; i < 81; i++) {
|
|
85
|
-
const value = cells[i];
|
|
86
|
-
if (value === 0) continue;
|
|
87
|
-
const r = Math.floor(i / 9), c = i % 9, b = Math.floor(r / 3) * 3 + Math.floor(c / 3);
|
|
88
|
-
const bit = 1 << (value - 1);
|
|
89
|
-
if ((row[r] & bit) || (col[c] & bit) || (box[b] & bit)) return null;
|
|
90
|
-
row[r] |= bit; col[c] |= bit; box[b] |= bit;
|
|
91
|
-
}
|
|
92
|
-
return { row, col, box };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function popcount(mask) {
|
|
96
|
-
let n = 0;
|
|
97
|
-
while (mask) { n += mask & 1; mask >>= 1; }
|
|
98
|
-
return n;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function* search(cells, masks) {
|
|
102
|
-
// Choose the empty cell with the fewest legal candidates. This MRV heuristic
|
|
103
|
-
// is the main reason the compiled sudoku/2 helper stays fast.
|
|
104
|
-
const all = 0x1ff;
|
|
105
|
-
let best = -1, bestCandidates = 0, bestCount = 10;
|
|
106
|
-
for (let i = 0; i < 81; i++) {
|
|
107
|
-
if (cells[i] !== 0) continue;
|
|
108
|
-
const r = Math.floor(i / 9), c = i % 9, b = Math.floor(r / 3) * 3 + Math.floor(c / 3);
|
|
109
|
-
const candidates = all & ~(masks.row[r] | masks.col[c] | masks.box[b]);
|
|
110
|
-
const count = popcount(candidates);
|
|
111
|
-
if (count === 0) return;
|
|
112
|
-
if (count < bestCount) {
|
|
113
|
-
best = i; bestCandidates = candidates; bestCount = count;
|
|
114
|
-
if (count === 1) break;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
if (best < 0) {
|
|
118
|
-
yield cells.slice();
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
const r = Math.floor(best / 9), c = best % 9, b = Math.floor(r / 3) * 3 + Math.floor(c / 3);
|
|
122
|
-
for (let value = 1; value <= 9; value++) {
|
|
123
|
-
const bit = 1 << (value - 1);
|
|
124
|
-
if (!(bestCandidates & bit)) continue;
|
|
125
|
-
cells[best] = value;
|
|
126
|
-
masks.row[r] |= bit; masks.col[c] |= bit; masks.box[b] |= bit;
|
|
127
|
-
yield* search(cells, masks);
|
|
128
|
-
masks.box[b] &= ~bit; masks.col[c] &= ~bit; masks.row[r] &= ~bit;
|
|
129
|
-
cells[best] = 0;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function solutionTerm(cells) {
|
|
134
|
-
const rows = [];
|
|
135
|
-
for (let r = 0; r < 9; r++) {
|
|
136
|
-
const items = [];
|
|
137
|
-
for (let c = 0; c < 9; c++) items.push(numberTerm(cells[r * 9 + c]));
|
|
138
|
-
rows.push(listFromItems(items));
|
|
139
|
-
}
|
|
140
|
-
return listFromItems(rows);
|
|
141
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
answer(K, V)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
answer(list, [a, b]).
|