Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Puzzle.solve Tests #12

Closed
wants to merge 20 commits into from
Closed
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
2 changes: 2 additions & 0 deletions elm-package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
"./src"
],
"exposed-modules": [],
"native-modules": true,
"dependencies": {
"elm-community/elm-test": "2.0.0 <= v < 3.0.0",
"elm-community/list-extra": "3.1.0 <= v < 4.0.0",
"elm-lang/core": "4.0.0 <= v < 5.0.0",
"elm-lang/trampoline": "1.0.0 <= v < 2.0.0",
"rtfeldman/node-test-runner": "2.0.0 <= v < 3.0.0"
},
"elm-version": "0.17.0 <= v < 0.18.0"
Expand Down
20 changes: 20 additions & 0 deletions src/Native/Random.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
var _jehoshua02$elm_sudoku$Native_Random = function() {

function percentage()
{
return Math.random();
}

function int(min, max)
{
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}

return {
percentage: percentage,
int: F2(int),
};

}();
163 changes: 163 additions & 0 deletions src/Sudoku/EvilPuzzleTests.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
port module Sudoku.EvilPuzzleTests exposing (tests)

import Test exposing (..)
import Expect
import Sudoku.Puzzle as Puzzle exposing (Error(..))
import Sudoku.Possible as Possible
import Util exposing (set)


tests : Test
tests =
describe "EvilPuzzle"
[ describe "Puzzle.valid" <|
[ test "should say this puzzle is valid" <|
\() ->
Expect.equal True (Puzzle.valid evil.puzzle)
]
, describe "Possible.initialize"
[ test "should initialize correctly" <|
\() ->
let
actual =
evil.puzzle
|> Possible.initialize

expected =
evil.puzzle
|> List.map
(\n ->
if n == 0 then
[1..9]
else
[ n ]
)
in
Expect.equal expected actual
]
, describe "Possible.eliminateUsed"
[ test "should eliminate used" <|
\() ->
let
possible =
evil.puzzle
|> Possible.initialize

actual =
possible
|> Possible.eliminateUsed

expected =
[ [ 3, 4, 6, 9 ], [ 3, 4, 8, 9 ], [ 6, 8, 9 ], [ 2, 6, 8 ], [ 7 ], [ 3, 8 ], [ 2, 8 ], [ 5 ], [ 1 ], [ 1, 4, 5, 7 ], [ 4, 7, 8 ], [ 2 ], [ 9 ], [ 1, 5, 8 ], [ 1, 5, 8 ], [ 6 ], [ 3 ], [ 4, 8 ], [ 1, 3, 5, 6, 9 ], [ 3, 8, 9 ], [ 1, 6, 8, 9 ], [ 4 ], [ 1, 2, 3, 5, 6, 8 ], [ 1, 3, 5, 8 ], [ 2, 8 ], [ 2, 8, 9 ], [ 7 ], [ 3, 7 ], [ 1 ], [ 5 ], [ 2, 7, 8 ], [ 2, 4, 8 ], [ 6 ], [ 9 ], [ 2, 4, 7, 8 ], [ 2, 3, 4, 8 ], [ 3, 6, 7, 9 ], [ 2, 3, 7, 8, 9 ], [ 6, 7, 8, 9 ], [ 1, 2, 5, 7, 8 ], [ 1, 2, 4, 5, 8 ], [ 1, 4, 5, 8, 9 ], [ 2, 3, 7, 8 ], [ 2, 4, 6, 7, 8 ], [ 2, 3, 4, 6, 8 ], [ 6, 7, 9 ], [ 2, 7, 8, 9 ], [ 4 ], [ 3 ], [ 2, 8 ], [ 8, 9 ], [ 5 ], [ 1 ], [ 2, 6, 8 ], [ 2 ], [ 4, 9 ], [ 1, 9 ], [ 1, 5, 6, 8 ], [ 1, 3, 4, 5, 6, 8 ], [ 7 ], [ 1, 3, 8 ], [ 6, 8, 9 ], [ 3, 5, 6, 8, 9 ], [ 1, 7, 9 ], [ 5 ], [ 3 ], [ 1, 6, 8 ], [ 1, 6, 8 ], [ 2 ], [ 4 ], [ 6, 7, 8, 9 ], [ 6, 8, 9 ], [ 8 ], [ 6 ], [ 1, 7 ], [ 1, 5 ], [ 9 ], [ 1, 3, 4, 5 ], [ 1, 2, 3, 7 ], [ 2, 7 ], [ 2, 3, 5 ] ]
in
Expect.equal expected actual
]
, describe "Possible.eliminateCrowds"
[ test "should eliminate crowds" <|
\() ->
let
possible =
evil.puzzle
|> Possible.initialize
|> Possible.eliminateUsed

actual =
possible
|> Possible.eliminateCrowds

expected =
[ [ 3, 4, 6, 9 ], [ 3, 4, 8, 9 ], [ 6, 8, 9 ], [ 2, 6, 8 ], [ 7 ], [ 3, 8 ], [ 2, 8 ], [ 5 ], [ 1 ], [ 1, 4, 5, 7 ], [ 4, 7, 8 ], [ 2 ], [ 9 ], [ 1, 5, 8 ], [ 1, 5, 8 ], [ 6 ], [ 3 ], [ 4 ], [ 1, 3, 5, 6, 9 ], [ 3, 8, 9 ], [ 1, 6, 8, 9 ], [ 4 ], [ 1, 2, 3, 5, 6, 8 ], [ 1, 3, 5, 8 ], [ 2, 8 ], [ 9 ], [ 7 ], [ 3, 7 ], [ 1 ], [ 5 ], [ 2, 7, 8 ], [ 2, 4, 8 ], [ 6 ], [ 9 ], [ 2, 4, 7, 8 ], [ 2, 3, 4, 8 ], [ 3, 6, 7, 9 ], [ 2, 3, 7, 8, 9 ], [ 6, 7, 8, 9 ], [ 1, 2, 5, 7, 8 ], [ 1, 2, 4, 5, 8 ], [ 1, 4, 5, 8, 9 ], [ 2, 3, 7, 8 ], [ 2, 4, 6, 7, 8 ], [ 2, 3, 4, 6, 8 ], [ 6, 7, 9 ], [ 2, 7, 8, 9 ], [ 4 ], [ 3 ], [ 2, 8 ], [ 8, 9 ], [ 5 ], [ 1 ], [ 2, 6, 8 ], [ 2 ], [ 4 ], [ 1, 9 ], [ 1, 5, 6, 8 ], [ 3 ], [ 7 ], [ 1, 3, 8 ], [ 6, 8, 9 ], [ 3, 5, 6, 8, 9 ], [ 1, 7, 9 ], [ 5 ], [ 3 ], [ 1, 6, 8 ], [ 1, 6, 8 ], [ 2 ], [ 4 ], [ 6, 7, 8, 9 ], [ 6, 8, 9 ], [ 8 ], [ 6 ], [ 1, 7 ], [ 1, 5 ], [ 9 ], [ 4 ], [ 1, 2, 3, 7 ], [ 2, 7 ], [ 2, 3, 5 ] ]
in
Expect.equal expected actual
]
, describe "Possible.eliminateSame"
[ test "should eliminate same" <|
\() ->
let
possible =
evil.puzzle
|> Possible.initialize
|> Possible.eliminateUsed
|> Possible.eliminateCrowds

actual =
possible
|> Possible.eliminateSame

expected =
[ [ 3, 4, 6, 9 ], [ 3, 4, 8, 9 ], [ 6, 8, 9 ], [ 2, 6, 8 ], [ 7 ], [ 3, 8 ], [ 2, 8 ], [ 5 ], [ 1 ], [ 1, 4, 5, 7 ], [ 4, 7, 8 ], [ 2 ], [ 9 ], [ 1, 5, 8 ], [ 1, 5, 8 ], [ 6 ], [ 3 ], [ 4 ], [ 1, 3, 5, 6, 9 ], [ 3, 8, 9 ], [ 1, 6, 8, 9 ], [ 4 ], [ 1, 2, 3, 5, 6, 8 ], [ 1, 3, 5, 8 ], [ 2, 8 ], [ 9 ], [ 7 ], [ 3, 7 ], [ 1 ], [ 5 ], [ 2, 7, 8 ], [ 2, 4, 8 ], [ 6 ], [ 9 ], [ 2, 4, 7, 8 ], [ 2, 3, 4, 8 ], [ 3, 6, 7, 9 ], [ 2, 3, 7, 8, 9 ], [ 6, 7, 8, 9 ], [ 1, 2, 5, 7, 8 ], [ 1, 2, 4, 5, 8 ], [ 1, 4, 5, 8, 9 ], [ 3, 7 ], [ 2, 4, 6, 7, 8 ], [ 2, 3, 4, 6, 8 ], [ 6, 7, 9 ], [ 2, 7, 8, 9 ], [ 4 ], [ 3 ], [ 2, 8 ], [ 8, 9 ], [ 5 ], [ 1 ], [ 2, 6, 8 ], [ 2 ], [ 4 ], [ 1, 9 ], [ 1, 5, 6, 8 ], [ 3 ], [ 7 ], [ 1, 3 ], [ 6, 8, 9 ], [ 3, 5, 6, 8, 9 ], [ 1, 7, 9 ], [ 5 ], [ 3 ], [ 1, 6, 8 ], [ 1, 6, 8 ], [ 2 ], [ 4 ], [ 6, 7, 8, 9 ], [ 6, 8, 9 ], [ 8 ], [ 6 ], [ 1, 7 ], [ 1, 5 ], [ 9 ], [ 4 ], [ 1, 3, 7 ], [ 2, 7 ], [ 2, 3, 5 ] ]
in
Expect.equal expected actual
]
, describe "Possible.eliminateAligned"
[ test "should eliminate aligned" <|
\() ->
let
possible =
evil.puzzle
|> Possible.initialize
|> Possible.eliminateUsed
|> Possible.eliminateCrowds
|> Possible.eliminateSame

actual =
possible
|> Possible.eliminateAligned

expected =
[ [ 3, 4, 6, 9 ], [ 3, 4, 8, 9 ], [ 6, 8, 9 ], [ 2, 6, 8 ], [ 7 ], [ 3, 8 ], [ 2, 8 ], [ 5 ], [ 1 ], [ 1, 4, 5, 7 ], [ 4, 7, 8 ], [ 2 ], [ 9 ], [ 1, 5, 8 ], [ 1, 5, 8 ], [ 6 ], [ 3 ], [ 4 ], [ 1, 3, 5, 6, 9 ], [ 3, 8, 9 ], [ 1, 6, 8, 9 ], [ 4 ], [ 1, 2, 3, 5, 6, 8 ], [ 1, 3, 5, 8 ], [ 2, 8 ], [ 9 ], [ 7 ], [ 3, 7 ], [ 1 ], [ 5 ], [ 2, 7, 8 ], [ 2, 4, 8 ], [ 6 ], [ 9 ], [ 2, 4, 7, 8 ], [ 2, 3, 4, 8 ], [ 3, 6, 7, 9 ], [ 2, 3, 7, 8, 9 ], [ 6, 7, 8, 9 ], [ 1, 2, 7, 8 ], [ 1, 2, 4, 5, 8 ], [ 1, 4, 5, 8, 9 ], [ 3, 7 ], [ 2, 4, 6, 7, 8 ], [ 2, 3, 4, 6, 8 ], [ 6, 7, 9 ], [ 2, 7, 8, 9 ], [ 4 ], [ 3 ], [ 2, 8 ], [ 8, 9 ], [ 5 ], [ 1 ], [ 2, 6, 8 ], [ 2 ], [ 4 ], [ 1, 9 ], [ 1, 5, 6, 8 ], [ 3 ], [ 7 ], [ 1, 3 ], [ 6, 8, 9 ], [ 3, 5, 6, 8, 9 ], [ 1, 7, 9 ], [ 5 ], [ 3 ], [ 1, 6, 8 ], [ 1, 6, 8 ], [ 2 ], [ 4 ], [ 6, 7, 8, 9 ], [ 6, 8, 9 ], [ 8 ], [ 6 ], [ 1, 7 ], [ 1, 5 ], [ 9 ], [ 4 ], [ 1, 3, 7 ], [ 2, 7 ], [ 2, 3, 5 ] ]
in
Expect.equal expected actual
, test "should still be a valid puzzle" <|
\() ->
let
puzzle =
evil.puzzle
|> Possible.initialize
|> Possible.eliminateUsed
|> Possible.eliminateCrowds
|> Possible.eliminateSame
|> Possible.eliminateAligned
|> Possible.toPuzzle
in
Expect.equal True (Puzzle.valid puzzle)
]
, describe "Puzzle.solve"
[ test "should solve evil puzzle (http://www.websudoku.com/?level=4&set_id=5147254317)" <|
\() ->
Expect.equal (Ok evil.solution) (Puzzle.solve evil.puzzle)
]
]


evil : { puzzle : List Int, solution : List Int }
evil =
{ puzzle =
{-
[0,0,0,0,7,0,0,5,1
,0,0,2,9,0,0,6,3,0
,0,0,0,4,0,0,0,0,7
,0,1,5,0,0,6,9,0,0
,0,0,0,0,0,0,0,0,0
,0,0,4,3,0,0,5,1,0
,2,0,0,0,0,7,0,0,0
,0,5,3,0,0,2,4,0,0
,8,6,0,0,9,0,0,0,0
]
-}
[ 0, 0, 0, 0, 7, 0, 0, 5, 1, 0, 0, 2, 9, 0, 0, 6, 3, 0, 0, 0, 0, 4, 0, 0, 0, 0, 7, 0, 1, 5, 0, 0, 6, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 3, 0, 0, 5, 1, 0, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 5, 3, 0, 0, 2, 4, 0, 0, 8, 6, 0, 0, 9, 0, 0, 0, 0 ]
, solution =
{-
[4,9,8,6,7,3,2,5,1
,5,7,2,9,1,8,6,3,4
,6,3,1,4,2,5,8,9,7
,3,1,5,7,4,6,9,8,2
,9,8,6,2,5,1,7,4,3
,7,2,4,3,8,9,5,1,6
,2,4,9,5,3,7,1,6,8
,1,5,3,8,6,2,4,7,9
,8,6,7,1,9,4,3,2,5
]
-}
[ 4, 9, 8, 6, 7, 3, 2, 5, 1, 5, 7, 2, 9, 1, 8, 6, 3, 4, 6, 3, 1, 4, 2, 5, 8, 9, 7, 3, 1, 5, 7, 4, 6, 9, 8, 2, 9, 8, 6, 2, 5, 1, 7, 4, 3, 7, 2, 4, 3, 8, 9, 5, 1, 6, 2, 4, 9, 5, 3, 7, 1, 6, 8, 1, 5, 3, 8, 6, 2, 4, 7, 9, 8, 6, 7, 1, 9, 4, 3, 2, 5 ]
}
125 changes: 83 additions & 42 deletions src/Sudoku/Possible.elm
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ module Sudoku.Possible
, eliminateCrowds
, eliminateSame
, eliminateAligned
, used
, unused
)

import Set
Expand Down Expand Up @@ -78,18 +80,24 @@ eliminateUsed possible =
let
puzzle =
toPuzzle possible
in
possible
|> List.indexedMap
(\i xs ->
( i, used i puzzle )
)
|> flip List.foldl
possible
(\( i, xs ) possible ->

before =
possible

after =
before
|> List.indexedMap
(\i xs ->
( i, used i puzzle )
)
|> flip List.foldl
possible
|> eliminate xs [ i ]
)
(\( i, xs ) possible ->
possible
|> eliminate xs [ i ]
)
in
after


used : Int -> Puzzle -> List Int
Expand Down Expand Up @@ -119,6 +127,12 @@ used i puzzle =
row ++ column ++ group |> List.filter ((/=) 0) |> unique


unused : Int -> Puzzle -> List Int
unused i puzzle =
used i puzzle
|> diff [1..9]


eliminateCrowds : Possible -> Possible
eliminateCrowds possible =
possible
Expand Down Expand Up @@ -211,47 +225,74 @@ eliminateAligned possible =
group
|> findIndices (List.member n)

head =
is
|> List.head
|> Maybe.withDefault 0

same =
unique >> List.length >> (==) 1

row =
rowInGroup =
(flip (//) 3)

column =
rowInPuzzle =
(+) ((g // 3) * 3)

indexInRow =
(\i -> i % 3 + (g % 3) * 3)

columnInGroup =
(flip (%) 3)

columnInPuzzle =
(+) ((g % 3) * 3)

indexInColumn =
(\i -> i // 3 + (g // 3) * 3)

sameRow =
is |> List.map rowInGroup |> same

sameColumn =
is |> List.map columnInGroup |> same
in
if List.length is == 0 then
if List.length is <= 1 then
-- eliminateUsed or eliminateCrowds will handle these
[]
else if is |> List.map row |> same then
let
y =
is
|> List.head
|> Maybe.withDefault 0
|> row
|> (+) ((g // 3) * 3)
in
is
|> List.map
(\i -> i % 3 + g % 3 * 3)
|> diff [0..8]
|> List.map (flip coordToIndex y)
else if is |> List.map column |> same then
else
let
x =
is
|> List.head
|> Maybe.withDefault 0
|> column
|> (+) (g // 3 * 3)
rowEliminations =
if not sameRow then
[]
else
let
y =
head
|> rowInGroup
|> rowInPuzzle
in
is
|> List.map indexInRow
|> diff [0..8]
|> List.map (flip coordToIndex y)

columnEliminations =
if not sameColumn then
[]
else
let
x =
head
|> columnInGroup
|> columnInPuzzle
in
is
|> List.map indexInColumn
|> diff [0..8]
|> List.map (coordToIndex x)
in
is
|> List.map
(\i -> i // 3 + g // 3 * 3)
|> diff [0..8]
|> List.map (coordToIndex x)
else
[]
rowEliminations ++ columnEliminations
)
|> flip List.foldl
possible
Expand Down
Loading