Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI

on:
pull_request:
branches:
- main
push:
branches:
- main

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
cache: npm

- name: Install dependencies
run: npm ci

- name: Run lint
run: npm run lint

- name: Run unit tests
run: npm run test:run

- name: Build
run: npm run build
2 changes: 1 addition & 1 deletion .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
node-version-file: .nvmrc
cache: npm

- name: Install dependencies
Expand Down
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20
13 changes: 11 additions & 2 deletions src/domain/rules/slither/rules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1899,6 +1899,10 @@ describe('slither strong inference rule', () => {
if (!colorAssumptionRule) {
throw new Error('Expected color-assumption-inference rule')
}
const unboundedColorAssumptionRule = createColorAssumptionInferenceRule(
() => slitherRules.filter((rule) => rule.id !== 'color-assumption-inference' && rule.id !== 'strong-inference'),
{ maxMs: Number.POSITIVE_INFINITY },
)
const strongRule = slitherRules.find((rule) => rule.id === 'strong-inference')
if (!strongRule) {
throw new Error('Expected strong-inference rule')
Expand Down Expand Up @@ -2112,7 +2116,7 @@ describe('slither strong inference rule', () => {
current = nextPuzzle
}

const result = colorAssumptionRule.apply(current)
const result = unboundedColorAssumptionRule.apply(current)
expect(result).not.toBeNull()
expect(result?.diffs).toEqual([{ kind: 'cell', cellKey: cellKey(2, 12), fromFill: null, toFill: 'green' }])

Expand All @@ -2121,7 +2125,12 @@ describe('slither strong inference rule', () => {
...(targetBranch.cells[cellKey(7, 0)] ?? {}),
fill: 'yellow',
}
const targetResult = runTrialUntilFixpoint(targetBranch, rulesBeforeColorAssumption, 120, Date.now() + 2000)
const targetResult = runTrialUntilFixpoint(
targetBranch,
rulesBeforeColorAssumption,
120,
Number.POSITIVE_INFINITY,
)
expect(targetResult.contradiction).toBe(true)
})

Expand Down
32 changes: 27 additions & 5 deletions src/domain/rules/slither/rules/colorAssumptionInference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ const COLOR_ASSUMPTION_MAX_CANDIDATES = 120
const COLOR_ASSUMPTION_MAX_TRIAL_STEPS = 120
const COLOR_ASSUMPTION_MAX_MS = 2000

type ColorAssumptionInferenceOptions = {
maxCandidates?: number
maxTrialSteps?: number
maxMs?: number
}

type ColorAssumptionCandidate = {
cellKey: string
row: number
Expand Down Expand Up @@ -99,17 +105,23 @@ const getCellAssumptionDiff = (
},
]

export const createColorAssumptionInferenceRule = (getDeterministicRules: () => Rule[]): Rule => ({
export const createColorAssumptionInferenceRule = (
getDeterministicRules: () => Rule[],
options: ColorAssumptionInferenceOptions = {},
): Rule => ({
id: 'color-assumption-inference',
name: 'Color Assumption Inference',
apply: (puzzle: PuzzleIR): RuleApplication | null => {
const deterministicRules = getDeterministicRules()
const candidates = collectColorAssumptionCandidates(puzzle, COLOR_ASSUMPTION_MAX_CANDIDATES)
const candidates = collectColorAssumptionCandidates(
puzzle,
options.maxCandidates ?? COLOR_ASSUMPTION_MAX_CANDIDATES,
)
if (candidates.length === 0) {
return null
}

const deadlineMs = Date.now() + COLOR_ASSUMPTION_MAX_MS
const deadlineMs = Date.now() + (options.maxMs ?? COLOR_ASSUMPTION_MAX_MS)
for (const candidate of candidates) {
if (Date.now() > deadlineMs) {
break
Expand All @@ -121,10 +133,20 @@ export const createColorAssumptionInferenceRule = (getDeterministicRules: () =>
const yellowSetupOk = applyCellAssumption(yellowBranch, candidate.cellKey, 'yellow')

const greenResult = greenSetupOk
? runTrialUntilFixpoint(greenBranch, deterministicRules, COLOR_ASSUMPTION_MAX_TRIAL_STEPS, deadlineMs)
? runTrialUntilFixpoint(
greenBranch,
deterministicRules,
options.maxTrialSteps ?? COLOR_ASSUMPTION_MAX_TRIAL_STEPS,
deadlineMs,
)
: { contradiction: true, timedOut: false, exhausted: false, puzzle: greenBranch }
const yellowResult = yellowSetupOk
? runTrialUntilFixpoint(yellowBranch, deterministicRules, COLOR_ASSUMPTION_MAX_TRIAL_STEPS, deadlineMs)
? runTrialUntilFixpoint(
yellowBranch,
deterministicRules,
options.maxTrialSteps ?? COLOR_ASSUMPTION_MAX_TRIAL_STEPS,
deadlineMs,
)
: { contradiction: true, timedOut: false, exhausted: false, puzzle: yellowBranch }

if (greenResult.timedOut || yellowResult.timedOut) {
Expand Down
Loading