eyeprolog 1.3.18 → 1.3.20

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 CHANGED
@@ -292,7 +292,7 @@ npm run test:interop
292
292
  ```
293
293
 
294
294
  The GitHub interoperability workflow builds both implementations and runs the
295
- same portable Sudoku source under EyeProlog, Trealla, and Scryer.
295
+ same portable Towers of Hanoi source under EyeProlog, Trealla, and Scryer.
296
296
 
297
297
  ## Development
298
298
 
@@ -4,11 +4,19 @@
4
4
  % The list position is a column and its value is a row. Domains, global
5
5
  % distinctness, and delayed diagonal constraints describe the puzzle before
6
6
  % labeling searches the remaining finite alternatives.
7
+ %
8
+ % The main example records one witness for the classic eight-queens problem.
9
+ % A smaller four-queens query also checks that queens/2 still enumerates
10
+ % multiple solutions without carrying a 92-answer golden file.
7
11
 
8
- %% goal: queens(X0, X1)
12
+ %% goal: queens8_solution(X0)
13
+ %% goal: queens(4, X0)
14
+
15
+ queens8_solution(Rows) :-
16
+ queens(8, Rows),
17
+ !.
9
18
 
10
19
  queens(Size, Rows) :-
11
- Size = 6,
12
20
  length(Rows, Size),
13
21
  Rows ins 1..Size,
14
22
  all_distinct(Rows),
@@ -26,4 +34,3 @@ safe_from(Row, [Other|Rows], Distance) :-
26
34
  Row #\= Other - Distance,
27
35
  NextDistance is Distance + 1,
28
36
  safe_from(Row, Rows, NextDistance).
29
-
@@ -20,10 +20,17 @@ raw_log(l2, 'ts=2026-06-18T10:00:03Z level=error event=payment_denied user=alice
20
20
  raw_log(l3, 'ts=2026-06-18T10:01:12Z level=info event=login_success user=bob ip=198.51.100.4 traceparent=00-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bbbbbbbbbbbbbbbb-01').
21
21
  raw_log(noise, 'healthcheck ok').
22
22
 
23
+ % Walk the batch recursively so EyeProlog can table parsed_logs/3 and reuse
24
+ % each regex parse across the captured-field, event, and alert queries.
23
25
  parsed(Log, Context) :-
24
- raw_log(Log, Text),
26
+ findall(raw(Log0, Text), raw_log(Log0, Text), Logs),
27
+ parsed_logs(Logs, Log, Context).
28
+
29
+ parsed_logs([raw(Log, Text)|_], Log, Context) :-
25
30
  log_pattern(Pattern),
26
31
  matches(Text, Pattern, Context).
32
+ parsed_logs([_|Logs], Log, Context) :-
33
+ parsed_logs(Logs, Log, Context).
27
34
 
28
35
  context_member((Left, _right), Member) :- context_member(Left, Member).
29
36
  context_member((_left, Right), Member) :- context_member(Right, Member).
@@ -46,4 +53,4 @@ parsed_event(Log, Event, User, Ip, Traceid) :-
46
53
  trace_alert(User, Traceid, Ip) :-
47
54
  parsed_event(Loginlog, 'login_failed', User, Ip, Traceid),
48
55
  parsed_event(Paymentlog, 'payment_denied', User, Ip, Traceid),
49
- (Loginlog \= Paymentlog).
56
+ Loginlog \= Paymentlog.
@@ -1,4 +1,3 @@
1
- queens(6, [2, 4, 6, 1, 3, 5]).
2
- queens(6, [3, 6, 2, 5, 1, 4]).
3
- queens(6, [4, 1, 5, 2, 6, 3]).
4
- queens(6, [5, 3, 1, 6, 4, 2]).
1
+ queens8_solution([1, 5, 8, 6, 3, 7, 2, 4]).
2
+ queens(4, [2, 4, 1, 3]).
3
+ queens(4, [3, 1, 4, 2]).
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.3.18",
6
+ "version": "1.3.20",
7
7
  "description": "EyeProlog turns facts and rules into answers and proofs.",
8
8
  "type": "module",
9
9
  "main": "./index.js",
package/playground.html CHANGED
@@ -577,7 +577,6 @@
577
577
  "modal-logic-kripke",
578
578
  "modular-exponentiation",
579
579
  "monkey-bananas",
580
- "n-queens",
581
580
  "network-sla",
582
581
  "newton-raphson",
583
582
  "nixon-diamond",
@@ -629,7 +628,6 @@
629
628
  "stable-marriage",
630
629
  "statistics-summary",
631
630
  "stirling-bell-numbers",
632
- "sudoku",
633
631
  "superdense-coding",
634
632
  "symbolic-derivative",
635
633
  "takeuchi",
@@ -11,25 +11,25 @@ import { fileURLToPath } from 'node:url';
11
11
 
12
12
  const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
13
13
  const allowMissing = process.argv.includes('--allow-missing');
14
- const sudoku = path.join(root, 'examples', 'sudoku.pl');
15
- const goal = 'sudoku_solution(S), write(S), nl, fail; halt';
16
- const expected = '[[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]]';
14
+ const hanoi = path.join(root, 'examples', 'hanoi.pl');
15
+ const goal = 'hanoi(3,left,right,center,Moves), write(Moves), nl, halt';
16
+ const expected = '[[left,right],[left,center],[right,center],[left,right],[center,left],[center,right],[left,right]]';
17
17
 
18
18
  const engines = [
19
19
  {
20
20
  name: 'EyeProlog',
21
21
  command: process.execPath,
22
- args: [path.join(root, 'bin', 'eyeprolog.js'), '--portable', '-g', goal, sudoku],
22
+ args: [path.join(root, 'bin', 'eyeprolog.js'), '--portable', '-g', goal, hanoi],
23
23
  },
24
24
  {
25
25
  name: 'Trealla',
26
26
  command: process.env.TPL ?? 'tpl',
27
- args: ['-q', '-g', goal, sudoku],
27
+ args: ['-q', '-g', goal, hanoi],
28
28
  },
29
29
  {
30
30
  name: 'Scryer',
31
31
  command: process.env.SCRYER_PROLOG ?? 'scryer-prolog',
32
- args: ['-f', '-g', goal, sudoku],
32
+ args: ['-f', '-g', goal, hanoi],
33
33
  },
34
34
  ];
35
35
 
@@ -64,13 +64,13 @@ for (const engine of engines) {
64
64
  }
65
65
  const lines = String(result.stdout ?? '').split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
66
66
  if (!lines.includes(expected)) {
67
- console.error(`FAIL ${engine.name}: Sudoku output mismatch`);
67
+ console.error(`FAIL ${engine.name}: Hanoi output mismatch`);
68
68
  console.error(`stdout: ${JSON.stringify(result.stdout)}`);
69
69
  if (result.stderr) console.error(`stderr: ${result.stderr.trim()}`);
70
70
  failed = true;
71
71
  continue;
72
72
  }
73
- console.log(`OK ${engine.name}: portable Sudoku`);
73
+ console.log(`OK ${engine.name}: portable Hanoi`);
74
74
  }
75
75
 
76
76
  if (failed) process.exitCode = 1;
@@ -2189,7 +2189,7 @@ child.stdin.write(\`consult(${consultedAtom}).\\n\`);
2189
2189
  {
2190
2190
  name: 'npm exec can run package CLI bin from checkout',
2191
2191
  run: () => {
2192
- const result = spawnSync('npm', ['exec', '--loglevel=silent', '--yes', '--package=.', '--', 'eyeprolog', '--version'], {
2192
+ const result = spawnSync('npm', ['exec', '--offline', '--loglevel=silent', '--yes', '--package=.', '--', 'eyeprolog', '--version'], {
2193
2193
  cwd: packageRoot,
2194
2194
  encoding: 'utf8',
2195
2195
  env: { ...process.env, npm_config_update_notifier: 'false' },
@@ -3797,30 +3797,30 @@ answer(ok) :-
3797
3797
  `;
3798
3798
  const script = `
3799
3799
  import { Program, Solver, Env, parseGoalText, getEyePrologRegistry } from ${JSON.stringify(engineUrl)};
3800
- const program = Program.parse(${JSON.stringify(programText)}, { autoloadGoals: ['ti(R,3,0)'] });
3800
+ const program = Program.parse(${JSON.stringify(programText)}, { autoloadGoals: ['ti(R,1,0)'] });
3801
3801
  const listTailGroup = program.findGroup('...', 2, 'user');
3802
3802
  if (listTailGroup?.listTailRecursive !== true) {
3803
3803
  throw new Error('recursive DCG tail-consumption was not recognized');
3804
3804
  }
3805
3805
  const solver = new Solver(program, { registry: getEyePrologRegistry(), maxMemoryBytes: Infinity });
3806
- const goal = parseGoalText('ti(R,3,0)', {
3806
+ const goal = parseGoalText('ti(R,1,0)', {
3807
3807
  doubleQuotes: 'chars',
3808
3808
  operatorDefinitions: [...program.operators.values()],
3809
3809
  });
3810
3810
  let count = 0;
3811
3811
  for (const _ of solver.solve([goal], new Env(), 0)) {
3812
- if (++count === 20) break;
3812
+ if (++count === 10) break;
3813
3813
  }
3814
- if (count !== 20) throw new Error('unexpected checkpoint count: ' + count);
3814
+ if (count !== 10) throw new Error('unexpected checkpoint count: ' + count);
3815
3815
  const scope = solver.innerTableScopes.get('phrase');
3816
3816
  if (scope == null) throw new Error('missing phrase table scope');
3817
3817
  if (scope.memo.size !== 0) throw new Error('distinct tail-DCG inputs should not retain phrase tables: ' + scope.memo.size);
3818
3818
  process.stdout.write(count + ':' + scope.memo.size);
3819
3819
  `;
3820
3820
  const result = spawnSync(process.execPath, [
3821
- // This corresponds to 20,000 accepted number candidates. Before the
3822
- // phrase-local cache, every distinct recursive DCG tail stayed rooted
3823
- // in the caller memo and this workload exhausted a 32 MiB old space.
3821
+ // This covers 100 accepted number candidates, enough to exercise
3822
+ // many distinct recursive DCG inputs while keeping this a focused
3823
+ // bounded-cache regression rather than a memory stress benchmark.
3824
3824
  '--max-old-space-size=32',
3825
3825
  '--input-type=module',
3826
3826
  '--eval',
@@ -3829,7 +3829,7 @@ answer(ok) :-
3829
3829
  if (result.error) throw result.error;
3830
3830
  assertEqual(result.status, 0, `bounded phrase-table child status; stderr=${result.stderr}`);
3831
3831
  const [countText, cacheText] = result.stdout.trim().split(':');
3832
- assertEqual(countText, '20', 'recursive phrase checkpoints');
3832
+ assertEqual(countText, '10', 'recursive phrase checkpoints');
3833
3833
  assertEqual(cacheText, '0', 'distinct tail-DCG phrase cache');
3834
3834
  },
3835
3835
  },
@@ -4886,10 +4886,22 @@ path(X, Z) :- edge(X, Y), path(Y, Z).
4886
4886
  },
4887
4887
  },
4888
4888
  {
4889
- name: 'n-queens example keeps recursive search predicates tabled',
4889
+ name: 'recursive search pattern keeps scan and search predicates tabled',
4890
4890
  run: () => {
4891
- const text = fs.readFileSync(path.join(packageRoot, 'examples', 'n-queens.pl'), 'utf8');
4892
- const program = Program.parseSources([{ text, filename: 'n-queens.pl' }]);
4891
+ const text = `
4892
+ queens([], Qs, Qs).
4893
+ queens(Us, Ps, Qs) :-
4894
+ select(Q, Us, Us1),
4895
+ \+ attack(Q, 1, Ps),
4896
+ queens(Us1, [Q|Ps], Qs).
4897
+
4898
+ attack(X, N, [Y|_]) :- X is Y + N.
4899
+ attack(X, N, [Y|_]) :- X is Y - N.
4900
+ attack(X, N, [_|Ys]) :-
4901
+ N1 is N + 1,
4902
+ attack(X, N1, Ys).
4903
+ `;
4904
+ const program = Program.parseSources([{ text, filename: 'recursive-search.pl' }]);
4893
4905
  const attack = program.findGroup('attack', 3);
4894
4906
  assertEqual(Boolean(attack), true, 'attack/3 group exists');
4895
4907
  assertEqual(attack.tabled, true, 'attack/3 tabled');
@@ -4076,8 +4076,8 @@ generation contains `n^k` leaves. If order does not matter, permutations may
4076
4076
  be redundant. If partial choices already violate a constraint, pruning saves
4077
4077
  an entire subtree.
4078
4078
 
4079
- This is why combinatorial examples are not toys. `n-queens.pl` exposes the
4080
- complete 92-solution search. Together with `send-more-money.pl`,
4079
+ This is why combinatorial examples are not toys. `clpz-n-queens.pl` exposes
4080
+ the classic eight-queens search through finite-domain constraints. Together with `send-more-money.pl`,
4081
4081
  `integer-partitions.pl`, `stirling-bell-numbers.pl`,
4082
4082
  and `weighted-interval-scheduling.pl`, they show different geometries of
4083
4083
  choice: permutations, digit assignments, recursive decompositions, set
@@ -6301,7 +6301,7 @@ library autoloading.
6301
6301
  calls to non-profile predicates from otherwise common modules. `--portable`
6302
6302
  turns those diagnostics into a failing run, making the conservative profile
6303
6303
  suitable for continuous integration. `npm run test:interop` executes the same
6304
- portable Sudoku source under EyeProlog, Trealla, and Scryer when those commands
6304
+ portable Towers of Hanoi source under EyeProlog, Trealla, and Scryer when those commands
6305
6305
  are installed; the repository interoperability workflow installs them and runs
6306
6306
  that check.
6307
6307
 
@@ -6769,7 +6769,7 @@ For example:
6769
6769
  eyeprolog --goal 'ancestor(X, Y)' examples/ancestor.pl
6770
6770
  eyeprolog --proof --goal 'type(X, Y)' examples/socrates.pl
6771
6771
  eyeprolog --warnings --goal 'answer(X)' test/conformance/warnings/negation/unstratified_mutual.pl
6772
- eyeprolog --portable --goal 'sudoku_solution(S)' examples/sudoku.pl
6772
+ eyeprolog --portable --goal 'sudoku9_solution(S)' examples/clpz-sudoku-9x9.pl
6773
6773
  eyeprolog --stats --goal 'path(a, X)' examples/path-discovery.pl > answers.pl 2> run.stats
6774
6774
  ```
6775
6775
 
@@ -6951,7 +6951,7 @@ Review questions:
6951
6951
  </figure>
6952
6952
 
6953
6953
  The [examples directory](https://github.com/eyereasoner/eyeprolog/tree/main/examples/) is the book's executable companion. The
6954
- top-level directory contains **212 self-contained runnable programs**. Every
6954
+ top-level directory contains **210 self-contained runnable programs**. Every
6955
6955
  source program has an exact answer file under
6956
6956
  [examples/output](https://github.com/eyereasoner/eyeprolog/tree/main/examples/output/), and **61 selected programs** have a checked
6957
6957
  explanation under [examples/proof](https://github.com/eyereasoner/eyeprolog/tree/main/examples/proof/). The thematic tables below link every top-level program and open the program
@@ -7001,7 +7001,7 @@ mode at a time.
7001
7001
  | --- | --- | --- |
7002
7002
  | [CLP(Z) factorial](https://github.com/eyereasoner/eyeprolog/blob/main/examples/clpz-factorial.pl) | Declarative predecessor and product constraints propagate a factorial without mode-sensitive `is/2`. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/clpz-factorial.pl) |
7003
7003
  | [CLP(Z) global constraints](https://github.com/eyereasoner/eyeprolog/blob/main/examples/clpz-global-constraints.pl) | Compatibility tables, lexicographic and serialized schedules, global cardinality with costs, circuits, value counting, and integer comparison. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/clpz-global-constraints.pl) |
7004
- | [CLP(Z) N-queens](https://github.com/eyereasoner/eyeprolog/blob/main/examples/clpz-n-queens.pl) | Six-queens using finite domains, delayed diagonal constraints, `all_distinct/1`, and first-fail labeling. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/clpz-n-queens.pl) |
7004
+ | [CLP(Z) N-queens](https://github.com/eyereasoner/eyeprolog/blob/main/examples/clpz-n-queens.pl) | Eight-queens using finite domains, delayed diagonal constraints, `all_distinct/1`, and first-fail labeling, plus a small multi-solution enumeration check. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/clpz-n-queens.pl) |
7005
7005
  | [CLP(Z) resource allocation](https://github.com/eyereasoner/eyeprolog/blob/main/examples/clpz-resource-allocation.pl) | Resource assignment using `element/3`, `sum/3`, `scalar_product/4`, reification, labeling options, and domain reflection. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/clpz-resource-allocation.pl) |
7006
7006
  | [CLP(Z) Sudoku 9×9](https://github.com/eyereasoner/eyeprolog/blob/main/examples/clpz-sudoku-9x9.pl) | The difficult AI Escargot puzzle modeled with finite domains, 27 all-distinct constraints, and first-fail labeling. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/clpz-sudoku-9x9.pl) |
7007
7007
  | [Combinatorics Findall Sort](https://github.com/eyereasoner/eyeprolog/blob/main/examples/combinatorics-findall-sort.pl) | Eyelet-inspired combinations example using `findall/3` and ISO `sort/2`. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/combinatorics-findall-sort.pl) |
@@ -7097,11 +7097,9 @@ finite search space, and which constraint removes which branches?
7097
7097
  | [Map Four Color Search](https://github.com/eyereasoner/eyeprolog/blob/main/examples/map-four-color-search.pl) | Four-colour search for the European Union neighbour graph. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/map-four-color-search.pl) |
7098
7098
  | [Markov Logic Network](https://github.com/eyereasoner/eyeprolog/blob/main/examples/markov-logic-network.pl) | Markov Logic Network style scoring over a tiny finite domain. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/markov-logic-network.pl) |
7099
7099
  | [Matrix Chain Order](https://github.com/eyereasoner/eyeprolog/blob/main/examples/matrix-chain-order.pl) | Matrix-chain multiplication order by automatically tabled interval dynamic programming. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/matrix-chain-order.pl) |
7100
- | [N-Queens enumeration](https://github.com/eyereasoner/eyeprolog/blob/main/examples/n-queens.pl) | `select/3` chooses each row and diagonal checks prune partial placements; the query enumerates all 92 eight-queen solutions in the default runtime. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/n-queens.pl) |
7101
7100
  | [Register allocation](https://github.com/eyereasoner/eyeprolog/blob/main/examples/register-allocation.pl) | Interference constraints turn compiler allocation into graph coloring. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/register-allocation.pl) |
7102
7101
  | [SEND + MORE = MONEY](https://github.com/eyereasoner/eyeprolog/blob/main/examples/send-more-money.pl) | Digit assignments are generated under distinctness, leading-zero, and column constraints. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/send-more-money.pl) |
7103
7102
  | [Stable marriage](https://github.com/eyereasoner/eyeprolog/blob/main/examples/stable-marriage.pl) | Preference data, matching generation, and the absence of blocking pairs define stability. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/stable-marriage.pl) |
7104
- | [Sudoku 9×9](https://github.com/eyereasoner/eyeprolog/blob/main/examples/sudoku.pl) | Minimum-remaining-values search fills a standard grid while enforcing row, column, and 3×3 block constraints. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/sudoku.pl) |
7105
7103
  | [Weighted interval scheduling](https://github.com/eyereasoner/eyeprolog/blob/main/examples/weighted-interval-scheduling.pl) | Compatibility constraints and an ordered objective select a maximum-value schedule. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/weighted-interval-scheduling.pl) · [proof](https://github.com/eyereasoner/eyeprolog/blob/main/examples/proof/weighted-interval-scheduling.pl) |
7106
7104
  | [Zebra puzzle](https://github.com/eyereasoner/eyeprolog/blob/main/examples/zebra.pl) | House records, adjacency relations, and clue constraints jointly determine the famous solution. | [answers](https://github.com/eyereasoner/eyeprolog/blob/main/examples/output/zebra.pl) |
7107
7105
 
@@ -7354,7 +7352,7 @@ hand.
7354
7352
 
7355
7353
  #### Running and extending the corpus
7356
7354
 
7357
- Run all 212 normal answer goldens and the 61 selected proof goldens with:
7355
+ Run all 210 normal answer goldens and the 61 selected proof goldens with:
7358
7356
 
7359
7357
  ```sh
7360
7358
  npm run test:examples
@@ -7418,7 +7416,7 @@ The complete suite must pass before release. The file-based conformance corpus
7418
7416
  contains 791 cases, including 386 focused ISO
7419
7417
  cases derived from the success, failure, mode, and error behavior in
7420
7418
  ISO/IEC 13211-1 clauses 7 and 8, Part 2 modules, and Part 3 grammar rules.
7421
- Separate exact-output suites check 212 normal
7419
+ Separate exact-output suites check 210 normal
7422
7420
  examples and 61 proof examples; all extracted book programs are parsed and
7423
7421
  their declared goals are executed. The eight-case
7424
7422
  playground contract suite imports the production worker, sends real reasoning
@@ -1,36 +0,0 @@
1
- :- use_module(library(lists)).
2
-
3
- % N-Queens puzzle
4
- % See https://en.wikipedia.org/wiki/Eight_queens_puzzle
5
- % Original code at https://hanslen.github.io/2016/05/02/AI-problem-N-queens-problem-%E2%80%93-solved-in-prolog/
6
-
7
- queens(N, Qs) :-
8
- range(1, N, Us),
9
- queens(Us, [], Qs).
10
-
11
- % queens(+Unplaced, ?Placed, ?Queens)
12
- queens([], Qs, Qs).
13
- queens(Us, Ps, Qs) :-
14
- select(Q, Us, Us1),
15
- \+attack(Q, 1, Ps),
16
- queens(Us1, [Q|Ps], Qs).
17
-
18
- % range(+I, +J, -Ns): Ns is the list of integers between I and J inclusive
19
- range(J, J, [J]).
20
- range(I, J, [I|Ns]) :-
21
- I < J,
22
- I1 is I+1,
23
- range(I1, J, Ns).
24
-
25
- % attack(+Q, +N, +Qs): queen in row Q attacks from distance N one or more of the queens in rows Qs on a diagonal
26
- attack(X, N, [Y|_]) :-
27
- X is Y+N.
28
- attack(X, N, [Y|_]) :-
29
- X is Y-N.
30
- attack(X, N, [_|Ys]) :-
31
- N1 is N+1,
32
- attack(X, N1, Ys).
33
-
34
- % query
35
- %% goal: queens(8, _)
36
-
@@ -1,92 +0,0 @@
1
- queens(8, [4, 2, 7, 3, 6, 8, 5, 1]).
2
- queens(8, [5, 2, 4, 7, 3, 8, 6, 1]).
3
- queens(8, [3, 5, 2, 8, 6, 4, 7, 1]).
4
- queens(8, [3, 6, 4, 2, 8, 5, 7, 1]).
5
- queens(8, [5, 7, 1, 3, 8, 6, 4, 2]).
6
- queens(8, [4, 6, 8, 3, 1, 7, 5, 2]).
7
- queens(8, [3, 6, 8, 1, 4, 7, 5, 2]).
8
- queens(8, [5, 3, 8, 4, 7, 1, 6, 2]).
9
- queens(8, [5, 7, 4, 1, 3, 8, 6, 2]).
10
- queens(8, [4, 1, 5, 8, 6, 3, 7, 2]).
11
- queens(8, [3, 6, 4, 1, 8, 5, 7, 2]).
12
- queens(8, [4, 7, 5, 3, 1, 6, 8, 2]).
13
- queens(8, [6, 4, 2, 8, 5, 7, 1, 3]).
14
- queens(8, [6, 4, 7, 1, 8, 2, 5, 3]).
15
- queens(8, [1, 7, 4, 6, 8, 2, 5, 3]).
16
- queens(8, [6, 8, 2, 4, 1, 7, 5, 3]).
17
- queens(8, [6, 2, 7, 1, 4, 8, 5, 3]).
18
- queens(8, [4, 7, 1, 8, 5, 2, 6, 3]).
19
- queens(8, [5, 8, 4, 1, 7, 2, 6, 3]).
20
- queens(8, [4, 8, 1, 5, 7, 2, 6, 3]).
21
- queens(8, [2, 7, 5, 8, 1, 4, 6, 3]).
22
- queens(8, [1, 7, 5, 8, 2, 4, 6, 3]).
23
- queens(8, [2, 5, 7, 4, 1, 8, 6, 3]).
24
- queens(8, [4, 2, 7, 5, 1, 8, 6, 3]).
25
- queens(8, [5, 7, 1, 4, 2, 8, 6, 3]).
26
- queens(8, [6, 4, 1, 5, 8, 2, 7, 3]).
27
- queens(8, [5, 1, 4, 6, 8, 2, 7, 3]).
28
- queens(8, [5, 2, 6, 1, 7, 4, 8, 3]).
29
- queens(8, [6, 3, 7, 2, 8, 5, 1, 4]).
30
- queens(8, [2, 7, 3, 6, 8, 5, 1, 4]).
31
- queens(8, [7, 3, 1, 6, 8, 5, 2, 4]).
32
- queens(8, [5, 1, 8, 6, 3, 7, 2, 4]).
33
- queens(8, [1, 5, 8, 6, 3, 7, 2, 4]).
34
- queens(8, [3, 6, 8, 1, 5, 7, 2, 4]).
35
- queens(8, [6, 3, 1, 7, 5, 8, 2, 4]).
36
- queens(8, [7, 5, 3, 1, 6, 8, 2, 4]).
37
- queens(8, [7, 3, 8, 2, 5, 1, 6, 4]).
38
- queens(8, [5, 3, 1, 7, 2, 8, 6, 4]).
39
- queens(8, [2, 5, 7, 1, 3, 8, 6, 4]).
40
- queens(8, [3, 6, 2, 5, 8, 1, 7, 4]).
41
- queens(8, [6, 1, 5, 2, 8, 3, 7, 4]).
42
- queens(8, [8, 3, 1, 6, 2, 5, 7, 4]).
43
- queens(8, [2, 8, 6, 1, 3, 5, 7, 4]).
44
- queens(8, [5, 7, 2, 6, 3, 1, 8, 4]).
45
- queens(8, [3, 6, 2, 7, 5, 1, 8, 4]).
46
- queens(8, [6, 2, 7, 1, 3, 5, 8, 4]).
47
- queens(8, [3, 7, 2, 8, 6, 4, 1, 5]).
48
- queens(8, [6, 3, 7, 2, 4, 8, 1, 5]).
49
- queens(8, [4, 2, 7, 3, 6, 8, 1, 5]).
50
- queens(8, [7, 1, 3, 8, 6, 4, 2, 5]).
51
- queens(8, [1, 6, 8, 3, 7, 4, 2, 5]).
52
- queens(8, [3, 8, 4, 7, 1, 6, 2, 5]).
53
- queens(8, [6, 3, 7, 4, 1, 8, 2, 5]).
54
- queens(8, [7, 4, 2, 8, 6, 1, 3, 5]).
55
- queens(8, [4, 6, 8, 2, 7, 1, 3, 5]).
56
- queens(8, [2, 6, 1, 7, 4, 8, 3, 5]).
57
- queens(8, [2, 4, 6, 8, 3, 1, 7, 5]).
58
- queens(8, [3, 6, 8, 2, 4, 1, 7, 5]).
59
- queens(8, [6, 3, 1, 8, 4, 2, 7, 5]).
60
- queens(8, [8, 4, 1, 3, 6, 2, 7, 5]).
61
- queens(8, [4, 8, 1, 3, 6, 2, 7, 5]).
62
- queens(8, [2, 6, 8, 3, 1, 4, 7, 5]).
63
- queens(8, [7, 2, 6, 3, 1, 4, 8, 5]).
64
- queens(8, [3, 6, 2, 7, 1, 4, 8, 5]).
65
- queens(8, [4, 7, 3, 8, 2, 5, 1, 6]).
66
- queens(8, [4, 8, 5, 3, 1, 7, 2, 6]).
67
- queens(8, [3, 5, 8, 4, 1, 7, 2, 6]).
68
- queens(8, [4, 2, 8, 5, 7, 1, 3, 6]).
69
- queens(8, [5, 7, 2, 4, 8, 1, 3, 6]).
70
- queens(8, [7, 4, 2, 5, 8, 1, 3, 6]).
71
- queens(8, [8, 2, 4, 1, 7, 5, 3, 6]).
72
- queens(8, [7, 2, 4, 1, 8, 5, 3, 6]).
73
- queens(8, [5, 1, 8, 4, 2, 7, 3, 6]).
74
- queens(8, [4, 1, 5, 8, 2, 7, 3, 6]).
75
- queens(8, [5, 2, 8, 1, 4, 7, 3, 6]).
76
- queens(8, [3, 7, 2, 8, 5, 1, 4, 6]).
77
- queens(8, [3, 1, 7, 5, 8, 2, 4, 6]).
78
- queens(8, [8, 2, 5, 3, 1, 7, 4, 6]).
79
- queens(8, [3, 5, 2, 8, 1, 7, 4, 6]).
80
- queens(8, [3, 5, 7, 1, 4, 2, 8, 6]).
81
- queens(8, [5, 2, 4, 6, 8, 3, 1, 7]).
82
- queens(8, [6, 3, 5, 8, 1, 4, 2, 7]).
83
- queens(8, [5, 8, 4, 1, 3, 6, 2, 7]).
84
- queens(8, [4, 2, 5, 8, 6, 1, 3, 7]).
85
- queens(8, [4, 6, 1, 5, 2, 8, 3, 7]).
86
- queens(8, [6, 3, 1, 8, 5, 2, 4, 7]).
87
- queens(8, [5, 3, 1, 6, 8, 2, 4, 7]).
88
- queens(8, [4, 2, 8, 6, 1, 3, 5, 7]).
89
- queens(8, [6, 3, 5, 7, 1, 4, 2, 8]).
90
- queens(8, [6, 4, 7, 1, 3, 5, 2, 8]).
91
- queens(8, [4, 7, 5, 2, 6, 1, 3, 8]).
92
- queens(8, [5, 7, 2, 6, 3, 1, 4, 8]).
@@ -1 +0,0 @@
1
- sudoku_solution([[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]]).
@@ -1,113 +0,0 @@
1
- :- use_module(library(lists)).
2
-
3
- % A deterministic 9x9 Sudoku solver.
4
- %
5
- % Zero marks an empty cell. The solver first takes any forced move; otherwise
6
- % it selects the cell with the fewest candidates (minimum remaining values).
7
- %% goal: sudoku_solution(_)
8
-
9
- puzzle([
10
- [0,3,4,6,7,8,9,1,0],
11
- [6,0,2,1,9,5,3,0,8],
12
- [1,9,0,3,4,2,0,6,7],
13
- [8,5,9,0,6,1,4,0,3],
14
- [4,2,6,8,0,3,7,9,0],
15
- [0,1,3,9,2,0,8,5,6],
16
- [9,6,0,5,3,7,0,8,4],
17
- [2,0,7,4,1,9,6,0,5],
18
- [0,4,5,2,8,6,1,7,0]
19
- ]).
20
-
21
- sudoku_solution(Solution) :-
22
- puzzle(Puzzle),
23
- solve(Puzzle, Solution),
24
- !.
25
-
26
- solve(Grid, Grid) :-
27
- \+ empty_cell(Grid, _Row, _Column),
28
- !.
29
- solve(Grid, Solution) :-
30
- best_choice(Grid, Row, Column, Candidates),
31
- member(Digit, Candidates),
32
- set_cell(Grid, Row, Column, Digit, Next),
33
- solve(Next, Solution).
34
-
35
- % A singleton candidate is already optimal, so take it immediately.
36
- best_choice(Grid, Row, Column, Candidates) :-
37
- empty_cell(Grid, Row, Column),
38
- candidates(Grid, Row, Column, Candidates),
39
- Candidates = [_Only],
40
- !.
41
- best_choice(Grid, Row, Column, Candidates) :-
42
- findall(
43
- choice(Count, R, C, Values),
44
- (
45
- empty_cell(Grid, R, C),
46
- candidates(Grid, R, C, Values),
47
- length(Values, Count)
48
- ),
49
- Choices
50
- ),
51
- sort(Choices, [choice(_Count, Row, Column, Candidates)|_]).
52
-
53
- empty_cell(Grid, Row, Column) :-
54
- between(0, 8, Row),
55
- nth0(Row, Grid, Cells),
56
- between(0, 8, Column),
57
- nth0(Column, Cells, 0).
58
-
59
- candidates(Grid, Row, Column, Candidates) :-
60
- findall(
61
- Digit,
62
- (
63
- between(1, 9, Digit),
64
- allowed(Grid, Row, Column, Digit)
65
- ),
66
- Candidates
67
- ).
68
-
69
- allowed(Grid, Row, Column, Digit) :-
70
- row_unused(Grid, Row, Digit),
71
- column_unused(Grid, Column, Digit),
72
- block_unused(Grid, Row, Column, Digit).
73
-
74
- row_unused(Grid, Row, Digit) :-
75
- nth0(Row, Grid, Cells),
76
- \+ member(Digit, Cells).
77
-
78
- column_unused([], _Column, _Digit).
79
- column_unused([Row|Rows], Column, Digit) :-
80
- nth0(Column, Row, Existing),
81
- Digit \= Existing,
82
- column_unused(Rows, Column, Digit).
83
-
84
- block_unused(Grid, Row, Column, Digit) :-
85
- StartRow is (Row // 3) * 3,
86
- EndRow is StartRow + 2,
87
- StartColumn is (Column // 3) * 3,
88
- block_rows_unused(Grid, StartRow, EndRow, StartColumn, Digit).
89
-
90
- block_rows_unused(_Grid, Row, EndRow, _StartColumn, _Digit) :-
91
- Row > EndRow,
92
- !.
93
- block_rows_unused(Grid, Row, EndRow, StartColumn, Digit) :-
94
- nth0(Row, Grid, Cells),
95
- segment_unused(Cells, StartColumn, Digit),
96
- NextRow is Row + 1,
97
- block_rows_unused(Grid, NextRow, EndRow, StartColumn, Digit).
98
-
99
- segment_unused(Cells, StartColumn, Digit) :-
100
- nth0(StartColumn, Cells, A),
101
- Middle is StartColumn + 1,
102
- nth0(Middle, Cells, B),
103
- End is StartColumn + 2,
104
- nth0(End, Cells, C),
105
- Digit \= A,
106
- Digit \= B,
107
- Digit \= C.
108
-
109
- set_cell(Grid, Row, Column, Digit, Result) :-
110
- nth0(Row, Grid, Cells, OtherRows),
111
- nth0(Column, Cells, _Old, OtherCells),
112
- nth0(Column, UpdatedCells, Digit, OtherCells),
113
- nth0(Row, Result, UpdatedCells, OtherRows).