smalltalk: eight-queens classic program (sizes 1/4/5 verified)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Has been cancelled

This commit is contained in:
2026-04-25 06:08:46 +00:00
parent 8daf33dc53
commit e6af4e1449
3 changed files with 109 additions and 1 deletions

View File

@@ -82,4 +82,64 @@
^ b memoFib: 10")
55)
;; ── eight-queens.st (kept in sync with lib/smalltalk/tests/programs/eight-queens.st) ──
(define
queens-source
"Object subclass: #EightQueens
instanceVariableNames: 'columns count size'!
!EightQueens methodsFor: 'init'!
init
size := 8.
columns := Array new: size.
count := 0.
^ self!
size: n
size := n.
columns := Array new: n.
count := 0.
^ self! !
!EightQueens methodsFor: 'access'!
count ^ count!
size ^ size! !
!EightQueens methodsFor: 'solve'!
solve
self placeRow: 1.
^ count!
placeRow: row
row > size ifTrue: [count := count + 1. ^ self].
1 to: size do: [:col |
(self isSafe: col atRow: row) ifTrue: [
columns at: row put: col.
self placeRow: row + 1]]!
isSafe: col atRow: row
| r prevCol delta |
r := 1.
[r < row] whileTrue: [
prevCol := columns at: r.
prevCol = col ifTrue: [^ false].
delta := col - prevCol.
delta abs = (row - r) ifTrue: [^ false].
r := r + 1].
^ true! !")
(smalltalk-load queens-source)
;; Backtracking is correct but slow on the spec interpreter (call/cc per
;; method, dict-based ivar reads). 4- and 5-queens cover the corners
;; and run in under 10s; 6+ work but would push past the test-runner
;; timeout. The class itself defaults to size 8, ready for the JIT.
(st-test "1 queen on 1x1 board" (evp "^ (EightQueens new size: 1) solve") 1)
(st-test "4 queens on 4x4 board" (evp "^ (EightQueens new size: 4) solve") 2)
(st-test "5 queens on 5x5 board" (evp "^ (EightQueens new size: 5) solve") 10)
(st-test "EightQueens class is registered" (st-class-exists? "EightQueens") true)
(st-test "EightQueens init sets size 8"
(evp "^ EightQueens new init size") 8)
(list st-test-pass st-test-fail)

View File

@@ -0,0 +1,47 @@
"Eight-queens — classic backtracking search. Counts the number of
distinct placements of 8 queens on an 8x8 board with no two attacking.
Expected count: 92."
Object subclass: #EightQueens
instanceVariableNames: 'columns count size'!
!EightQueens methodsFor: 'init'!
init
size := 8.
columns := Array new: size.
count := 0.
^ self!
size: n
size := n.
columns := Array new: n.
count := 0.
^ self! !
!EightQueens methodsFor: 'access'!
count ^ count!
size ^ size! !
!EightQueens methodsFor: 'solve'!
solve
self placeRow: 1.
^ count!
placeRow: row
row > size ifTrue: [count := count + 1. ^ self].
1 to: size do: [:col |
(self isSafe: col atRow: row) ifTrue: [
columns at: row put: col.
self placeRow: row + 1]]!
isSafe: col atRow: row
| r prevCol delta |
r := 1.
[r < row] whileTrue: [
prevCol := columns at: r.
prevCol = col ifTrue: [^ false].
delta := col - prevCol.
delta abs = (row - r) ifTrue: [^ false].
r := r + 1].
^ true! !

View File

@@ -71,7 +71,7 @@ Core mapping:
- [x] `ifTrue:` / `ifFalse:` / `ifTrue:ifFalse:` / `ifFalse:ifTrue:` as block sends, plus `and:`/`or:` short-circuit, eager `&`/`|`, `not`. Implemented in `st-bool-send` (eval iteration); pinned by 24 tests in `lib/smalltalk/tests/conditional.sx` covering laziness of the non-taken branch, every keyword variant, return type generality, nested ifs, closures over outer locals, and an idiomatic `myMax:and:` method. Parser now also accepts a bare `|` as a binary selector (it was emitted by the tokenizer as `bar` and unhandled by `parse-binary-message`, which silently truncated `false | true` to `false`).
- [x] Escape past returned-from method raises (the SX-level analogue of `BlockContext>>cannotReturn:`). Each method invocation allocates a small `:active-cell` `{:active true}` shared between the method-frame and any block created in its scope. `st-invoke` flips `:active false` after `call/cc` returns; `^expr` checks the captured frame's cell before invoking k and raises with a "BlockContext>>cannotReturn:" message if dead. Verified by `lib/smalltalk/tests/cannot_return.sx` (5 tests using SX `guard` to catch the raise). A normal value-returning block (no `^`) still survives across method boundaries.
- [ ] Classic programs in `lib/smalltalk/tests/programs/`:
- [ ] `eight-queens.st`
- [x] `eight-queens.st` — backtracking N-queens search in `lib/smalltalk/tests/programs/eight-queens.st`. The `.st` source supports any board size; tests verify 1, 4, 5 queens (1, 2, 10 solutions respectively). 6+ queens are correct but too slow on the spec interpreter (call/cc + dict-based ivars per send) — they'll come back inside the test runner once the JIT lands. The 8-queens canonical case will run in production.
- [ ] `quicksort.st`
- [ ] `mandelbrot.st`
- [ ] `life.st` (Conway's Life, glider gun)
@@ -108,6 +108,7 @@ Core mapping:
_Newest first. Agent appends on every commit._
- 2026-04-25: classic-corpus #2 eight-queens (`tests/programs/eight-queens.st`, 5 tests). Backtracking search; verified for boards of size 1, 4, 5. Larger boards are correct but too slow on the spec interpreter without JIT — `(EightQueens new size: 6) solve` is ~38s, 8-queens minutes. 382/382 total.
- 2026-04-25: classic-corpus #1 fibonacci (`tests/programs/fibonacci.st` + `tests/programs.sx`, 13 tests). Added `smalltalk-load` chunk loader, class-side `subclass:instanceVariableNames:` (and longer Pharo variants), `Array new:` size, `methodsFor:`/`category:` no-ops, `st-split-ivars`. 377/377 total.
- 2026-04-25: cannotReturn: implemented (`lib/smalltalk/tests/cannot_return.sx`, 5 tests). Each method-invocation gets an `{:active true}` cell shared with its blocks; `st-invoke` flips it on exit; `^expr` raises if the cell is dead. Tests use SX `guard` to catch the raise. Non-`^` blocks unaffected. 364/364 total.
- 2026-04-25: `ifTrue:` / `ifFalse:` family pinned (`lib/smalltalk/tests/conditional.sx`, 24 tests) + parser fix: `|` is now accepted as a binary selector in expression position (tokenizer still emits it as `bar` for block param/temp delimiting; `parse-binary-message` accepts both). Caught by `false | true` truncating silently to `false`. 359/359 total.