Skip to content

Commit

Permalink
General improvements (#151)
Browse files Browse the repository at this point in the history
Note: Somehow I duplicated my commits while trying to squash them??????????????????????????????????????

* groups [skip ci]

* getBoxGroup test [skip ci]

* slightly optimise test [skip ci]

* Use getGroups

* fix lint

* fix typescript lint

* Fix syntax

move assertion

* remove `candidates` property [skip ci]

* Use groups, fix export types

* remove candidates

* fix types [skip ci]

* fix types (2)

* remove unused imports [skip ci]

* remove unused imports 2

* remove whitespace [skip ci]

* fix test

* same format as old

* Don't differentiate groups unnecessarily

* Fix types

* more typing fix

* even more typing

* refactor

* fix typescript typing

* fix typing again

* fix typing 3

* remove unused imports 1 [skip ci]

* remove unused imports 2

* make faster, fix bad errors

* remove changes

* oops fix test too [skip ci]

* groups [skip ci]

* getBoxGroup test [skip ci]

* slightly optimise test [skip ci]

* Use getGroups

* fix lint

* fix typescript lint

* Fix syntax

move assertion

* remove `candidates` property [skip ci]

* Use groups, fix export types

* remove candidates

* fix types [skip ci]

* fix types (2)

* remove unused imports [skip ci]

* remove unused imports 2

* remove whitespace [skip ci]

* fix test

* same format as old

* Don't differentiate groups unnecessarily

* Fix types

* more typing fix

* even more typing

* refactor

* fix typescript typing

* fix typing again

* fix typing 3

* remove unused imports 1 [skip ci]

* remove unused imports 2

* make faster, fix bad errors

* remove changes

* oops fix test too [skip ci]

* unused import [skip ci]

* consistent location [skip ci]

* ???? [skip ci]

* more consistency [skip ci]
  • Loading branch information
icecream17 authored Dec 17, 2021
1 parent 0db1635 commit 0595693
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 33 deletions.
3 changes: 3 additions & 0 deletions .gitpod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
tasks:
- init: yarn install && yarn run build
command: yarn run start
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject",
"test-with-coverage": "npm test -- --coverage"
"test-with-coverage": "yarn test --coverage"
},
"browserslist": {
"production": [
Expand Down
5 changes: 3 additions & 2 deletions src/Api/Spaces/PureSudoku.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ test('it imports', () => {
expect(testSudoku.data[4][2]).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 9])

for (const board of Object.values(BOARDS)) {
if (!testSudoku.import(board).success) {
const importSuccess = testSudoku.import(board).success
if (!importSuccess) {
console.debug(board)
}
expect(testSudoku.import(board).success).toBe(true)
expect(importSuccess).toBe(true)
expect(testSudoku.to81()).toBe(new PureSudoku(board).to81())
}

Expand Down
4 changes: 4 additions & 0 deletions src/Api/Spaces/PureSudoku.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
import { ALL_CANDIDATES, IndexToNine, INDICES_TO_NINE, SudokuDigits, ThreeDimensionalArray } from "../../Types"
import { boxAt, CellID, id, to9by9 } from "../Utils"

/**
* Defines base sudoku methods
* Should I move these to utils?
*/
export default class PureSudoku {
data: ThreeDimensionalArray<SudokuDigits>
constructor(representation?: string) {
Expand Down
5 changes: 4 additions & 1 deletion src/Api/Strategies/Strategies.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -375,7 +375,7 @@ describe('strategies', () => {
})
})

test('Examples 7, 8, and 9 (should error)', () => {
test('Examples 7, 8, 9, and 10 (should error)', () => {
const testSudoku = setupSudoku(`
123......
...123...
Expand Down Expand Up @@ -416,6 +416,9 @@ describe('strategies', () => {
`)
updateCandidates(testSudoku)
expect(hiddenPairsTriplesAndQuads(testSudoku).successcount).toBe(SuccessError)

testSudoku.import('020000700020006700000006700020000700000400089000400089000400009100000080100000089123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789')
expect(hiddenPairsTriplesAndQuads(testSudoku).successcount).toBe(SuccessError)
})
});

Expand Down
53 changes: 36 additions & 17 deletions src/Api/Strategies/hiddenPairsTriplesAndQuads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { convertArrayToEnglishList } from "../../utils";
import PureSudoku from "../Spaces/PureSudoku";
import { SuccessError } from "../Types";
import { algebraic, CellID, getIDFromIndexWithinBox, id, removeFromArray } from "../Utils";
import { CellInfo, colorConjugate, combinations, _CellInfoList } from "./pairsTriplesAndQuads";
import { CellInfo, colorConjugate, combinations, CellGroup } from "./pairsTriplesAndQuads";

/**
* Returns an array of all the cells which contain at least one of the candidates
*/
function getConjugateFromCandidates (cells: _CellInfoList, candidates: SudokuDigits[]) {
function getConjugateFromCandidates (cells: CellGroup, candidates: SudokuDigits[]) {
return cells.filter(cell =>
candidates.some(candidate => cell.candidates.includes(candidate))
)
Expand All @@ -35,7 +35,7 @@ function __errorHandling (candidatesOfConjugate: SudokuDigits[], conjugate: Cell
}
}

function __filterPossibleCandidates (groupCopy: SudokuDigits[][], maxSize: number, possibleCandidates: Set<1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9>) {
function __filterPossibleCandidates (groupCopy: SudokuDigits[][], maxSize: number, possibleCandidates: Set<SudokuDigits>) {

function removeCandidate (candidate: SudokuDigits) {
possibleCandidates.delete(candidate)
Expand All @@ -57,11 +57,13 @@ function __filterPossibleCandidates (groupCopy: SudokuDigits[][], maxSize: numbe
for (const candidate of possibleCandidates) {
if (occurances[candidate] > maxSize) {
removeCandidate(candidate)
} else if (occurances[candidate] === 0) {
return `There is nowhere to put ${candidate}!` as const
}
}

// b. Remove candidates that are alone in a cell
// Also maxSize become possibleCandidates.size
// maxSize is now possibleCandidates.size
let keepGoing = true
while (keepGoing) {
keepGoing = false
Expand Down Expand Up @@ -106,17 +108,19 @@ function findHiddenConjugatesOfGroup(
indexToPosition: (index: IndexToNine) => CellID,
maxSize = 4 as 2 | 3 | 4
) {

// Copy the group
const groupCopy = group.map(cell => cell.slice())

// 1. Filter the possible candidates
// 1. Filter the possible candidates (return if error)
const possibleCandidates = new Set([1, 2, 3, 4, 5, 6, 7, 8, 9] as const)
__filterPossibleCandidates(groupCopy, maxSize, possibleCandidates)
const __result = __filterPossibleCandidates(groupCopy, maxSize, possibleCandidates)
if (typeof __result === "string") {
return __result
}

// c. Filter out cells that have too few candidates
// (No limit on max candidates)
const possibleCells = [] as _CellInfoList
const possibleCells = [] as CellGroup

for (let index: IndexToNine = 0; index < 9; index = index + 1 as IndexToNine) {
const candidates = groupCopy[index]
Expand All @@ -139,21 +143,36 @@ function findHiddenConjugatesOfGroup(
maxSize = Math.min(maxSize, possibleCells.length, possibleCandidates.size) as 2 | 3 | 4

const conjugates = []
const conjugateCands = [] // Only used in one location

for (const candidatesOfConjugate of combinations(Array.from(possibleCandidates), 2, maxSize)) {
const conjugate = getConjugateFromCandidates(possibleCells, candidatesOfConjugate)

// if (candidatesOfConjugate.some(candidate => conjugate.every(cell => !cell.candidates.includes(candidate)))) {
// throw new TypeError(JSON.stringify([group, conjugate, candidatesOfConjugate]))
// }

// e.g.: 3 candidates must be in 2 cells
if (conjugate.length < candidatesOfConjugate.length) {
if (candidatesOfConjugate.length > conjugate.length) {
return __errorHandling(candidatesOfConjugate, conjugate)
} else if (conjugate.length === candidatesOfConjugate.length) {
conjugates.push(conjugate)

// Remove extra candidates - a conjugate was found!
for (const cell of conjugate) {
cell.candidates = cell.candidates.filter(
} else if (candidatesOfConjugate.length === conjugate.length) {
// Filter extra candidates - a conjugate was found!
const filteredConjugate = conjugate.map(cell => ({
candidates: cell.candidates.filter(
candidate => candidatesOfConjugate.includes(candidate)
)
),
position: cell.position,
}))

// Check if this conjugate exactly overlaps a previous one
// If so, error just like above
for (const [i, prevConjugate] of conjugates.entries()) {
if (prevConjugate.length === conjugate.length && prevConjugate.every(cell => conjugate.some(cell2 => cell.position === cell2.position))) {
return __errorHandling([...new Set(candidatesOfConjugate.concat(conjugateCands[i]))], conjugate)
}
}
conjugates.push(filteredConjugate)
conjugateCands.push(candidatesOfConjugate)
}
}

Expand All @@ -162,7 +181,7 @@ function findHiddenConjugatesOfGroup(


function findHiddenConjugatesOfSudoku(sudoku: PureSudoku, maxSize = 4 as 2 | 3 | 4) {
const conjugates = [] as _CellInfoList[]
const conjugates = [] as CellGroup[]
for (const i of INDICES_TO_NINE) {
const resultRow = findHiddenConjugatesOfGroup(sudoku.data[i], index => id(i, index), maxSize)
if (typeof resultRow === "string") {
Expand Down
22 changes: 10 additions & 12 deletions src/Api/Strategies/pairsTriplesAndQuads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,16 @@ export function combinations<T>(array: T[], min = 1, max = array.length, current
}

export type CellInfo = {
position: CellID
candidates: SudokuDigits[]
position: CellID
}

/** Really just a conjugate */
export type _CellInfoList = CellInfo[]
export type CellGroup = CellInfo[]

/**
* Return a set of unique candidates in a conjugate
*/
function getCandidatesOfConjugate(conjugate: _CellInfoList) {
function getCandidatesOfConjugate(conjugate: CellGroup) {
// Array from the values of a set
// The set is the accumulated candidates
return conjugate.reduce(
Expand All @@ -71,7 +70,7 @@ function getCandidatesOfConjugate(conjugate: _CellInfoList) {
}

// Inner inner function to make things look nicer below
function __errorHandling (conjugate: CellInfo[], invalidGroupCandidates: Set<SudokuDigits>) {
function __errorHandling (conjugate: CellGroup, invalidGroupCandidates: Set<SudokuDigits>) {
const invalidGroupNames = convertArrayToEnglishList(
conjugate.map(someCell => algebraic(someCell.position.row, someCell.position.column))
)
Expand Down Expand Up @@ -110,8 +109,8 @@ function findConjugatesOfGroup(
maxSize = 4 as 2 | 3 | 4
) {
// 1. Filter the possible cells
// Each possible cell must have 2 to maxSize candidates
const possibleCells = [] as _CellInfoList
// Each possible cell must have from 2 to maxSize candidates
const possibleCells = [] as CellGroup

for (const index of INDICES_TO_NINE) {
const candidates = group[index]
Expand All @@ -125,7 +124,7 @@ function findConjugatesOfGroup(
}

// 2. Now that the cells are filtered actually find the conjugates
const conjugates = [] as _CellInfoList[]
const conjugates = [] as CellGroup[]
for (const conjugate of combinations(possibleCells, 2, maxSize)) {
const candidatesOfConjugate = getCandidatesOfConjugate(conjugate)

Expand Down Expand Up @@ -171,7 +170,7 @@ function findConjugatesOfSudoku(sudoku: PureSudoku, maxSize = 4 as 2 | 3 | 4) {
/**
* Colors a conjugate, see Cell#highlight
*/
export function colorConjugate(sudoku: PureSudoku, conjugate: _CellInfoList, color = 'blue') {
export function colorConjugate(sudoku: PureSudoku, conjugate: CellGroup, color = 'blue') {
if (sudoku instanceof Sudoku) {
for (const cell of conjugate) {
const element = sudoku.cells[cell.position.row][cell.position.column]
Expand All @@ -182,7 +181,7 @@ export function colorConjugate(sudoku: PureSudoku, conjugate: _CellInfoList, col

function eliminateUsingConjugateGroup(
sudoku: PureSudoku,
conjugateGroup: _CellInfoList[][],
conjugateGroup: CellGroup[][],
toGroupIndex: (id: CellID) => IndexToNine,
toGroup: (sudoku: PureSudoku, index: IndexToNine) => SudokuDigits[][],
toPosition: (indexInGroup: IndexToNine, indexOfGroup: IndexToNine) => CellID,
Expand Down Expand Up @@ -223,8 +222,7 @@ function eliminateUsingConjugateGroup(
return successcount
}


function eliminateUsingConjugateGroups(sudoku: PureSudoku, conjugateGroups: readonly [_CellInfoList[][], _CellInfoList[][], _CellInfoList[][]]) {
function eliminateUsingConjugateGroups(sudoku: PureSudoku, conjugateGroups: readonly [CellGroup[][], CellGroup[][], CellGroup[][]]) {
const [resultRows, resultColumns, resultBoxes] = conjugateGroups

let successcount = 0;
Expand Down

0 comments on commit 0595693

Please sign in to comment.