Skip to content

Commit

Permalink
2 string kite
Browse files Browse the repository at this point in the history
  • Loading branch information
icecream17 committed Jan 9, 2022
1 parent 7f81c3a commit 5bdc9fa
Show file tree
Hide file tree
Showing 15 changed files with 150 additions and 36 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ Note: Many earlier versions are not specified, that's too much work.

When a `@types` dependency updates, they almost always don't affect anything.

## v0.28.0

- (use) Add 2-string kite

## v0.27.2

- (deps) **I will only note major dependency updates** - not including devDependencies.
Expand Down
11 changes: 11 additions & 0 deletions Notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -699,3 +699,14 @@ TIL
You can use `this` in static class methods. (<https://discord.com/channels/508357248330760243/740274647899308052/844777609082568714>)

Only declare an inherited property if you're overriding it. (<https://github.com/microsoft/TypeScript/issues/44178>)

## abbreviations

```rust
col = column
min = minimum
str = string
args = arguments
cand = candidate
char = character
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "solver",
"version": "0.27.2",
"version": "0.28.0",
"private": true,
"homepage": "https://icecream17.github.io/solver",
"dependencies": {
Expand Down
2 changes: 1 addition & 1 deletion src/Api/Spaces/PureSudoku.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ test('getBox', () => {
test('getBoxGroup', () => {
const testSudoku = new PureSudoku()
const cellData = testSudoku.data.map((row, indexOfRow) =>
row.map((cell, indexInRow) => ({
row.map((_cell, indexInRow) => ({
position: id(indexOfRow as IndexToNine, indexInRow as IndexToNine)
}))
)
Expand Down
2 changes: 1 addition & 1 deletion src/Api/Spaces/PureSudoku.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ export default class PureSudoku {
* Removes a candidate at a cell
*
* @example
* (new PureSudoku()).toggle(7).at(3, 5)
* (new PureSudoku()).remove(7).at(3, 5)
*/
remove(candidate: SudokuDigits) {
// Using an arrow function here to use `this`
Expand Down
18 changes: 17 additions & 1 deletion src/Api/Strategies/Strategies.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -738,12 +738,28 @@ describe('strategies', () => {
expect(testSudoku.data[4][8]).toStrictEqual([7, 9])
})

test('1', () => {
test('2', () => {
const testSudoku = new PureSudoku()
testSudoku.set(2, 2).to(6, 8)
testSudoku.set(2, 3).to(3, 8, 9)
testSudoku.set(2, 5).to(3, 6)
expect(xyzWing(testSudoku).success).toBe(false)
})
})

describe('2 string kite', () => {
test('1', () => {
const testSudoku = new PureSudoku()
testSudoku.import(`000000700000006009000050000000400000003000000020000000100000000000000089000006089100000000000000080000406009000000700000006009000050000000400009003000000020000000000400009003000000020000000100000000000006009000000080000000700000050000000406009020400009020400009100000000003000000000000080000000709000006000000400709000050000000000080000000700000006009000050000000400000000006009003000000020000000100000000000050000000406009003000000020000000100000000000006709000000080000400709000400009023000000100000000000000080000000009020000700000400000000050000000006000003000700000006000000050000000400009000000080020000700003000000020400009100000000000400709023400009020400009000000700000006000000050000100000000020400009000400089003400089`)
expect(xyzWing(testSudoku).success).toBe(true)
expect(testSudoku.data[7][8]).toStrictEqual([7, 9])
})

test('2', () => {
const testSudoku = new PureSudoku()
testSudoku.import(`123456780123456780123456789123456780123456780123456780123456789123456780123456780123456780123456780123456780123456789123456789123456789123456789123456789123456789123456789123456780123456780123456789123456789123456789123456789123456789123456789123456780123456789123456789123456789123456789123456789123456789123456789123456789123456780123456789123456789123456789123456789123456789123456789123456789123456789123456780123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456780123456789123456789123456789123456789123456789123456789123456789123456789123456780123456789123456789123456789123456789123456789123456789123456789123456789`)
expect(xyzWing(testSudoku).success).toBe(true)
expect(testSudoku.data[7][8]).toStrictEqual([7, 9])
})
})
})
2 changes: 2 additions & 0 deletions src/Api/Strategies/Strategies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import pairsTriplesAndQuads from "./pairsTriplesAndQuads";
import skyscraper from "./skyscraper";
import swordfish from "./swordfish";
import twoMinusOneLines from "./twoMinusOneLines";
import twoStringKite from "./twoStringKite";
import updateCandidates from "./updateCandidates";
import xWing from "./xWing";
import xyChain from "./xyChain";
Expand All @@ -28,6 +29,7 @@ const STRATEGIES = [
swordfish,
jellyfish,
skyscraper,
twoStringKite,
yWing,
twoMinusOneLines,
xyzWing,
Expand Down
11 changes: 2 additions & 9 deletions src/Api/Strategies/hiddenSingles.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
import { ALL_CANDIDATES, IndexToNine, INDICES_TO_NINE, SudokuDigits } from "../../Types"
import PureSudoku from "../Spaces/PureSudoku"
import Sudoku from "../Spaces/Sudoku"
import { boxAt } from "../Utils"

function colorCandidate(sudoku: PureSudoku, row: IndexToNine, column: IndexToNine, candidate: SudokuDigits, color = 'blue') {
if (sudoku instanceof Sudoku) {
const element = sudoku.cells[row][column]
element?.highlight([candidate], color)
}
}
import { colorCandidateF } from "../Utils.dependent"

/**
* The state for a candidate in a group
Expand Down Expand Up @@ -90,7 +83,7 @@ export default function hiddenSingles(sudoku: PureSudoku) {
if (cell !== false && cell !== undefined) {
successcount++
sudoku.set(cell.row, cell.column).to(candidate)
colorCandidate(sudoku, cell.row, cell.column, candidate, 'solved')
colorCandidateF(sudoku, cell.row, cell.column, candidate, 'solved')
}
}
}
Expand Down
68 changes: 68 additions & 0 deletions src/Api/Strategies/twoStringKite.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { ALL_CANDIDATES, SudokuDigits } from "../../Types";
import PureSudoku from "../Spaces/PureSudoku";
import { CellID } from "../Utils";
import { colorCandidateF } from "../Utils.dependent";

type CandidateInfo = ReturnType<typeof PureSudoku.prototype.getCandidateLocations>[SudokuDigits]

/**
* Checks if two cells create a two string kite
* Maybe these checkers could be symbolized as matchers
*/
function check(cell1: CellID, cell2: CellID, candidate: SudokuDigits, candLocations: CandidateInfo, sudoku: PureSudoku) {
/* eslint-disable sonarjs/no-collapsible-if -- It's clearer */
if (cell1.row === cell2.row || cell1.column === cell2.column) {
return 0
}

const sameRowAsCell1 = candLocations.rows[cell1.row]
const sameColAsCell2 = candLocations.columns[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) {
if (cell2B !== cell2) {
// All this does is get 1b and 2b
// 1 1b
// 2
// 2b
if (sudoku.data[cell2B.row][cell1B.column].includes(candidate)) {
colorCandidateF(sudoku, cell1.row, cell1.column, candidate)
colorCandidateF(sudoku, cell2.row, cell2.column, candidate, 'green')
colorCandidateF(sudoku, cell1B.row, cell1B.column, candidate)
colorCandidateF(sudoku, cell2B.row, cell2B.column, candidate, 'green')
sudoku.remove(candidate).at(cell2B.row, cell1B.column)
return 1
}
}
}
}
}
}

return 0
}

export default function twoStringKite(sudoku: PureSudoku) {
const candidateLocations = sudoku.getCandidateLocations()

for (const candidate of ALL_CANDIDATES) {
for (const box of candidateLocations[candidate].boxes) {
if (box.size === 2) {
const [cell1, cell2] = box
const successcount =
check(cell1, cell2, candidate, candidateLocations[candidate], sudoku) +
check(cell2, cell1, candidate, candidateLocations[candidate], sudoku)
if (successcount) {
return {
success: true,
successcount
} as const
}
}
}
}

return { success: false } as const
}
4 changes: 2 additions & 2 deletions src/Api/Strategies/xyChain.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { SudokuDigits } from "../../Types";
import PureSudoku from "../Spaces/PureSudoku";
import { affects, assertGet, CandidateID, CellID, id, sharedInSets } from "../Utils";
import { getCellsWithNCandidates } from "../Utils.dependent";
import { highlightCell, colorCandidate, cellIsValidLoop } from "./xyLoop";
import { colorCandidate, getCellsWithNCandidates } from "../Utils.dependent";
import { highlightCell, cellIsValidLoop } from "./xyLoop";

// Very similar to seenByColor in xyLoop
function seenByEnd (sudoku: PureSudoku, { row, column, digit }: CandidateID) {
Expand Down
17 changes: 3 additions & 14 deletions src/Api/Strategies/xyLoop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { SudokuDigits } from "../../Types";
import PureSudoku from "../Spaces/PureSudoku";
import Sudoku from "../Spaces/Sudoku";
import { affects, assertGet, CandidateID, CellID, id, removeFromArray, sharedInSets } from "../Utils";
import { getCellsWithNCandidates } from "../Utils.dependent";
import { colorCandidate, getCellsWithNCandidates } from "../Utils.dependent";

/**
* next = has
Expand All @@ -14,23 +14,12 @@ export function cellIsValidLoop (sudoku: PureSudoku, sees: CellID, has: SudokuDi
return cell.includes(has) && !loop.includes(sees)
}

/**
* Same as {@link colorGroup}, but this time with a specific candidate
*/
export function colorCandidate (sudoku: PureSudoku, {row, column, digit}: CandidateID, color = 'blue') {
if (sudoku instanceof Sudoku) {
const element = sudoku.cells[row][column]
element?.highlight([digit], color)
}
}

/**
* Colors a group of cells', see {@link Cell#highlight}
*/
export function highlightCell (sudoku: PureSudoku, cell: CellID, color = 'blue') {
export function highlightCell (sudoku: PureSudoku, {row, column}: CellID, color = 'blue') {
if (sudoku instanceof Sudoku) {
const element = sudoku.cells[cell.row][cell.column]
element?.addClass(color)
sudoku.cells[row][column]?.addClass(color)
}
}

Expand Down
21 changes: 19 additions & 2 deletions src/Api/Utils.dependent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
* Might want to run `madge --circular --extensions ts ./src` after importing from here
*/

import { SudokuDigits, INDICES_TO_NINE } from "../Types";
import { SudokuDigits, INDICES_TO_NINE, IndexToNine } from "../Types";
import PureSudoku from "./Spaces/PureSudoku";
import Sudoku from "./Spaces/Sudoku";
import { CellID, id } from "./Utils";
import { CandidateID, CellID, id } from "./Utils";

/**
* Colors a group of cells' candidates, see {@link Cell#highlight}
Expand All @@ -26,6 +26,23 @@ export function colorGroup (sudoku: PureSudoku, group: Iterable<CellID>, candida
}
}

/**
* Same as {@link colorGroup}, but this time with a specific candidate
*/
export function colorCandidateF (sudoku: PureSudoku, row: IndexToNine, column: IndexToNine, digit: SudokuDigits, color = 'blue') {
if (sudoku instanceof Sudoku) {
sudoku.cells[row][column]?.highlight([digit], color)
}
}

/**
* Same as {@link colorGroup}, but this time with a specific candidate
*/
export function colorCandidate (sudoku: PureSudoku, { row, column, digit }: CandidateID, color = 'blue') {
colorCandidateF(sudoku, row, column, digit, color)
}


export function getCellsWithNCandidates (sudoku: PureSudoku, N: number) {
const cellsWithNCandidates = [] as CellID[]
for (const row of INDICES_TO_NINE) {
Expand Down
7 changes: 6 additions & 1 deletion src/Elems/AsideElems/StrategyList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,12 @@ export default class StrategyList extends React.Component<StrategyListProps> {
/>
<StrategyItem
name='Skyscraper'
href='http://hodoku.sourceforge.net/en/tech_sdp.php#sk'
href='https://www.sudopedia.org/wiki/Skyscraper'
{...getRepeatedProps()}
/>
<StrategyItem
name='Two string kite'
href='https://www.sudopedia.org/wiki/2-String_Kite'
{...getRepeatedProps()}
/>
<StrategyItem
Expand Down
15 changes: 12 additions & 3 deletions src/Elems/MainElems/Cell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@ type _TrackCandidateState =
previousCandidates: SudokuDigits[]
classes: null | string[]
candidateClasses: null | (Record<IndexToNine, string> & string[])
highlighted: boolean
} |
{
explaining: false
previousCandidates: null
classes: null
candidateClasses: null
highlighted: false
}

type CellState = Readonly<(
Expand Down Expand Up @@ -115,7 +117,12 @@ export default class Cell extends React.Component<CellProps, CellState> {
*
* Unchanged: numCandidates===9
*/
pretend: false
pretend: false,

/**
* Whether a candidate is being highlighted
*/
highlighted: false,
}

this.whenFocus = this.whenFocus.bind(this)
Expand Down Expand Up @@ -218,7 +225,8 @@ export default class Cell extends React.Component<CellProps, CellState> {
}

return {
candidateClasses: newCandidateClasses
candidateClasses: newCandidateClasses,
highlighted: true,
}
})
}
Expand Down Expand Up @@ -250,6 +258,7 @@ export default class Cell extends React.Component<CellProps, CellState> {
previousCandidates: null,
classes: null,
candidateClasses: null,
highlighted: false,
}, callback)
}

Expand All @@ -274,7 +283,7 @@ export default class Cell extends React.Component<CellProps, CellState> {
<CandidatesDiff previous={this.state.previousCandidates} current={this.state.candidates} classes={this.state.candidateClasses} />
</Suspense>
)
} else if (this.state.active || this.state.pretend || (this.numCandidates > 1 && this.numCandidates < 9)) {
} else if (this.state.active || this.state.highlighted || (this.numCandidates > 1 && this.numCandidates < 9)) {
// Also show candidates when editing a cell
// Also show candidates as fallback when numCandidates is in [2, 8]
content = (
Expand Down
2 changes: 1 addition & 1 deletion src/Elems/Version.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import StaticComponent from './StaticComponent';
* <Version />
*/
function Version() {
return <span className="Version">v0.27.2</span>
return <span className="Version">v0.28.0</span>
}

export default StaticComponent(Version)

0 comments on commit 5bdc9fa

Please sign in to comment.