Skip to content

Commit

Permalink
fix ci bug, convert strategy data into table
Browse files Browse the repository at this point in the history
  • Loading branch information
icecream17 authored Aug 24, 2024
1 parent 89ceb37 commit 817166c
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 68 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: 'lts'
node-version: 'lts/*'
cache: yarn

- run: yarn install
Expand Down
76 changes: 31 additions & 45 deletions Strategies.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,57 +390,43 @@ About that XY Loop stuff... yeah, XY Chain is special enough to get it's own fun

## Strategy speed

Here's a list of a bunch of strategies, ranked in order of how fast they currently are in my solver, and accompanied with some educated guesses on why they're slow/fast.
Here's a list of a bunch of strategies, ranked in order of how fast they currently are in my solver, and accompanied with some educated guesses on why they're slow/fast. Note that _a strategy is only tried if the previous strategies fail_.

"Has dual" just notes that it can happen twice with an almost similar setup (e.g. same base or something)

I'm as surprised with the results as you are - this is definitely not how easy it is for humans

Ranking is ln random sudokus proccessed per ms.
So strategies that error or finish early get an advantage.
Ironically, some of the "easiest" strategies for humans, like intersection removal, are the hardest so far.
That's mostly because I haven't gotten to the actually complicated strategies.

Some easy strategies find all instances within a sudoku = much slower

Tries: 2

- [x] Y wing / XY wing / Bent triple (11.71 - 11.77)
- [] Has dual (multi coloring). There's also 3D medusa
- [] This limited form is a subset of X chains
- [x] XY Loop (11.63 - 11.76)
- [x] Update candidates (11.62 - 11.76)
- n^3 (n for each cell, n for each affects(cell), n for checking if affects(cell) contains that solved candidate)
- [x] XYZ Wing (11.64 - 11.73)
- [x] XY Chain (11.64 - 11.71)
- [x] Hidden singles (11.28 - 11.46)
- n^3 (n for each candidate, n for each group, n for each cell in group (checking single))
- Easier than update candidates because humans
- Despite this, hidden singles doesn't work if the candidates are not updated
- [x] Check for solved (11.26 - 11.36)
- n
- But this also calls "checkValidity" which is about n^3
- [x] Skyscraper (Subset of wing/coloring) (10.81 - 10.92)
- 2 lines - 1 line = extra
- n^6 ({fish note} - but instead I spend n^3 * {n counting pend lines, n^3 to get attacks(cell of line), n^3 per attacks to get shared} = n^6)
- [x] X wing (really a fish) (10.64 - 10.84)
- 2 lines have a candidate in only 2 crosslines
- n^6 (see {fish note})
- I have no idea why skyscraper would be faster
- [x] Swordfish (10.17 - 10.33)
- 3 lines have a candidate in only 3 crosslines
- n^6 (see {fish note})
- [x] Hidden pairs, triples, and quads (10.01 - 10.26)
- N candidates are in N cells (which all see each other)
- n^5 (Pretty much the same as the non hidden strategy.)
- [x] Pairs, triples, and quads (9.51 - 9.61)
- N cells have N candidates (and all see each other)
- n^5 (I have no idea how I did this. My code is quite big)
- [x] Intersection Removal (9.32 - 9.41)
- All candidates in a box see OR All candidates in a line see
- n^4 (n for each candidate, n^2 for box + line, n for each cell in (box - line) or (line - box))
- [x] Jellyfish (8.99 - 9.11)
- 4 lines have a candidate in only 4 crosslines
- n^6 (see {fish note})
- [x] Two minus one lines (8.14 - 8.15)
Experimental evidence suggests there are generally only 1 to 2 digits of accuracy for really fast strategies,
but more accuracy for less fast strategies. I tried to estimate digits of accuracy by `log10(time / stdev)`,
but take it with a pinch of salt.

n is generally 9, so constants are rather important.

Tries: 1

| Strategy | Usefulness | Time^-1 | Estimated digits of accuracy | Complexity | Explanation |
|---------------------------|:------------:|:-------:|:----------------------------:|:------------:|---------------------------------------------------------------------------------------------------------------------------------------------|
| Check for solved | 819543/25790 | 22.2519 | 4.15 | n^3 | n, but it includes "checkValidity" with is n^3 |
| Update candidates | 499816/24325 | 56.8341 | 4.30 | 3n^4 | n^2 cells, 3n affects(cell), n in cell |
| Hidden singles | 17844/12399 | 46.7887 | 4.02 | 3n^3 | Avoids the "n" because the target is a single cell, and instead of deleting candidates one by one, just set the cell to a single candidate. |
| Intersection removal | 8506/7059 | 1.3822 | 3.95 | 4(n^4-n^3.5) | n candidates, n\*2n box*line, 2(n-sqrt(n)) differences (probably did too much optimization here) |
| Pairs, triples, and quads | 12808/4264 | 1.9241 | 3.73 | | |
| Hidden "" | 236/2543 | 3.4062 | 3.45 | | |
| X wing (fish) | 82/2348 | 7.5987 | 3.43 | n^6 | wing note |
| Swordfish | 42/2282 | 3.1475 | 3.41 | n^6 | wing note |
| Jellyfish | 3/2259 | 1.3203 | 3.18 | n^6 | wing note |
| Skyscraper | 205/2257 | 5.1063 | 3.30 | | |
| 2 string kite | 119/2053 | 20.53 | 3.33 | | |
| Y wing | 102/1935 | 46.0714 | 3.30 | | |
| 2-1 lines | 559/1834 | 0.2586 | 3.31 | | |
| W wing | 152/1337 | 8.6258 | 3.17 | | |
| XYZ wing | 72/1192 | 30.5641 | 2.91 | | |
| Pair covers group | 35/1120 | 3.5668 | 3.05 | | |
| XY Loop | 30/1092 | 9.2542 | 2.24 | | |
| XY Chain | 93/1062 | 7.4266 | 2.37 | | |

> {wing note}: Instead of nesting loops for each line in a wing, I do the following:
>
Expand Down
11 changes: 11 additions & 0 deletions Todo
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,17 @@
[ ] Exocet
[ ] Wow

### Speed

Premature optimization is bad, but this is the most exciting part of the code.

[ ] Generalize strategies with a better API
[ ] Switch ordering of loops: I think having the candidate loop be in the inner loop is much faster
[ ] Hidden pairs, triples, and quads is much faster than pairs, triples, and quads
[ ] Make XY Loop and XY Chain be able to short circuit (i.e. not include the starting cell).
This would allow us to skip cells that are already connected, bounding the strategies to n^4
The strategies are already really fast, so do some testing first.

## Code

[ ] Migrate undo functionality completely to the solver
Expand Down
5 changes: 5 additions & 0 deletions src/Api/Spaces/PureSudoku.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,11 @@ export default class PureSudoku {
return data.slice(startRow, startRow + 3).flatMap(row => row.slice(startColumn, startColumn + 3))
}

/**
* A group is a set of maximally mutually exclusive set of cells.
*
* Currently, we assume this is just the rows, columns, and boxes, but this will be changed later on.
*/
getGroups() {
const groups = []
const cellData = this.data.map((row, indexOfRow) =>
Expand Down
44 changes: 23 additions & 21 deletions src/Api/Strategies/timeStrategies.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ function main () {
processed: number
finds: number
errors: number
totalspeed: number
totaldeviation: number
timetaken: number
speeds: number[]
}>

let done = 0
Expand All @@ -40,15 +38,17 @@ function main () {
processed: 0,
finds: 0,
errors: 0,
get totalspeed() {return this.processed / this.timetaken},
totaldeviation: 0,
timetaken: 0,
speeds: [],
}
const start = Date.now()

// Do strategy
// @ts-expect-error - bruh
const {successcount = 0} = Strategy(sudoku, { solved: 0 })
const timetaken = Date.now() - start

// Update speed
results[Strategy.name].speeds.push(timetaken)

// Update process / error
let stratSuccess = false
Expand All @@ -63,7 +63,7 @@ function main () {
if (successcount > 0) {
const isDone = sudoku.data.every(row => row.every(cell => cell.length === 1))
if (isDone) {
console.log('solved ' + i)
// console.log('solved ' + i)
solved.add(i)
stratSuccess = true
} else {
Expand All @@ -77,16 +77,6 @@ function main () {
}
}

// Update speed
const oldtotalspeed = results[Strategy.name].totalspeed
const timetaken = Date.now() - start
results[Strategy.name].timetaken += timetaken
results[Strategy.name].totaldeviation += Math.abs(oldtotalspeed - results[Strategy.name].totalspeed)
// const averagedeviation = results[Strategy.name].totaldeviation / results[Strategy.name].processed
// if (averagedeviation / results[Strategy.name].processed < 0.0001 && timetaken > 100 && results[Strategy.name].processed > 100) {
// break
// }

if (done % 0x1000 === 0) {
// unless there's a bug, done is at most 1465*729 = 1067985
// In practice, it currently finishes at (solved: 495, done: 122941)
Expand All @@ -107,10 +97,22 @@ function main () {
}
}

console.log(solved.size, done)

for (const key in results) { // @ts-ignore intentional
results[key].ts = results[key].totalspeed
console.log(`Solved ${solved.size} out of ${mtBoards.length} (~${100 * solved.size / mtBoards.length}%)`, done)

for (const key in results) {
// @ts-expect-error -- intentional
results[key].ts = results[key].speeds.reduce((a, b) => a + b)
// @ts-expect-error -- intentional
results[key].stdev = results[key].speeds.map(a => (results[key].ts / results[key].processed - a) ** 2).reduce((a, b) => a + b) / results[key].processed
// @ts-expect-error -- intentional
results[key].accuracy = Math.log10(results[key].ts / results[key].stdev)
// @ts-expect-error -- intentional
results[key].hertz = results[key].processed / results[key].ts

// @ts-expect-error
delete results[key].ts
// @ts-expect-error
delete results[key].speeds
}
console.log(results)

Expand Down
1 change: 0 additions & 1 deletion src/Api/Strategies/twoStringKite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ function check(cell1: CellID, cell2: CellID, candidate: SudokuDigits, candLocati
const sameRowAsCell1 = candLocations.row[cell1.row]
const sameColAsCell2 = candLocations.column[cell2.column]
if (sameRowAsCell1.size === 2 && sameColAsCell2.size === 2) {
// Looks big nesting but not really
for (const cell1B of sameRowAsCell1) {
if (cell1B !== cell1) {
for (const cell2B of sameColAsCell2) {
Expand Down

0 comments on commit 817166c

Please sign in to comment.