Compare commits
15 Commits
b9788d6a51
...
HAAAAAAAAA
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0795f9256d | ||
| a74bf42e59 | |||
| 1f92c49f3c | |||
| c262007ca8 | |||
| 73f572ee18 | |||
| ec07d88577 | |||
| 6caf5d5c54 | |||
|
|
696062e9db | ||
|
|
c825807aea | ||
| 39529e0b64 | |||
| 3fa22a4cf0 | |||
| 337911d038 | |||
| 1930bc02bd | |||
| 7789209651 | |||
| 9b776d4b0f |
BIN
app/background.png
Normal file
BIN
app/background.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 399 KiB |
@@ -11,9 +11,14 @@ plugins {
|
|||||||
id 'application'
|
id 'application'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
project.ext.os = System.properties['os.name'].toLowerCase().split(" ")[0]
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
// Use Maven Central for resolving dependencies.
|
// Use Maven Central for resolving dependencies.
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
flatDir {
|
||||||
|
dirs("$rootProject.projectDir/libs")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -31,6 +36,10 @@ dependencies {
|
|||||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.2'
|
implementation 'com.fasterxml.jackson.core:jackson-databind:2.18.2'
|
||||||
|
|
||||||
implementation "io.github.spair:imgui-java-app:1.88.0"
|
implementation "io.github.spair:imgui-java-app:1.88.0"
|
||||||
|
|
||||||
|
implementation "org.lwjgl:lwjgl-stb:3.3.4"
|
||||||
|
|
||||||
|
runtimeOnly "org.lwjgl:lwjgl-stb::natives-$os"
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
|
|||||||
572
app/save/save-1.json
Normal file
572
app/save/save-1.json
Normal file
@@ -0,0 +1,572 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"symbolIndex": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"blocks": [
|
||||||
|
{"cellIDs": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
18,
|
||||||
|
19,
|
||||||
|
20
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
21,
|
||||||
|
22,
|
||||||
|
23
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
15,
|
||||||
|
16,
|
||||||
|
17,
|
||||||
|
24,
|
||||||
|
25,
|
||||||
|
26
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
27,
|
||||||
|
28,
|
||||||
|
29,
|
||||||
|
36,
|
||||||
|
37,
|
||||||
|
38,
|
||||||
|
45,
|
||||||
|
46,
|
||||||
|
47
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
30,
|
||||||
|
31,
|
||||||
|
32,
|
||||||
|
39,
|
||||||
|
40,
|
||||||
|
41,
|
||||||
|
48,
|
||||||
|
49,
|
||||||
|
50
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
33,
|
||||||
|
34,
|
||||||
|
35,
|
||||||
|
42,
|
||||||
|
43,
|
||||||
|
44,
|
||||||
|
51,
|
||||||
|
52,
|
||||||
|
53
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
54,
|
||||||
|
55,
|
||||||
|
56,
|
||||||
|
63,
|
||||||
|
64,
|
||||||
|
65,
|
||||||
|
72,
|
||||||
|
73,
|
||||||
|
74
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
57,
|
||||||
|
58,
|
||||||
|
59,
|
||||||
|
66,
|
||||||
|
67,
|
||||||
|
68,
|
||||||
|
75,
|
||||||
|
76,
|
||||||
|
77
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
60,
|
||||||
|
61,
|
||||||
|
62,
|
||||||
|
69,
|
||||||
|
70,
|
||||||
|
71,
|
||||||
|
78,
|
||||||
|
79,
|
||||||
|
80
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
"multidoku": [{
|
||||||
|
"blockWidth": 3,
|
||||||
|
"cells": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
15,
|
||||||
|
16,
|
||||||
|
17,
|
||||||
|
18,
|
||||||
|
19,
|
||||||
|
20,
|
||||||
|
21,
|
||||||
|
22,
|
||||||
|
23,
|
||||||
|
24,
|
||||||
|
25,
|
||||||
|
26,
|
||||||
|
27,
|
||||||
|
28,
|
||||||
|
29,
|
||||||
|
30,
|
||||||
|
31,
|
||||||
|
32,
|
||||||
|
33,
|
||||||
|
34,
|
||||||
|
35,
|
||||||
|
36,
|
||||||
|
37,
|
||||||
|
38,
|
||||||
|
39,
|
||||||
|
40,
|
||||||
|
41,
|
||||||
|
42,
|
||||||
|
43,
|
||||||
|
44,
|
||||||
|
45,
|
||||||
|
46,
|
||||||
|
47,
|
||||||
|
48,
|
||||||
|
49,
|
||||||
|
50,
|
||||||
|
51,
|
||||||
|
52,
|
||||||
|
53,
|
||||||
|
54,
|
||||||
|
55,
|
||||||
|
56,
|
||||||
|
57,
|
||||||
|
58,
|
||||||
|
59,
|
||||||
|
60,
|
||||||
|
61,
|
||||||
|
62,
|
||||||
|
63,
|
||||||
|
64,
|
||||||
|
65,
|
||||||
|
66,
|
||||||
|
67,
|
||||||
|
68,
|
||||||
|
69,
|
||||||
|
70,
|
||||||
|
71,
|
||||||
|
72,
|
||||||
|
73,
|
||||||
|
74,
|
||||||
|
75,
|
||||||
|
76,
|
||||||
|
77,
|
||||||
|
78,
|
||||||
|
79,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
"blocks": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8
|
||||||
|
],
|
||||||
|
"constraints": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
572
app/save/save.json
Normal file
572
app/save/save.json
Normal file
@@ -0,0 +1,572 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 0,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 1,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 2,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 3,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 4,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 5,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 6,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 7,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"symbolIndex": -1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"blockID": 8,
|
||||||
|
"immutable": true,
|
||||||
|
"symbolIndex": 8
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"blocks": [
|
||||||
|
{"cellIDs": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
18,
|
||||||
|
19,
|
||||||
|
20
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
21,
|
||||||
|
22,
|
||||||
|
23
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
15,
|
||||||
|
16,
|
||||||
|
17,
|
||||||
|
24,
|
||||||
|
25,
|
||||||
|
26
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
27,
|
||||||
|
28,
|
||||||
|
29,
|
||||||
|
36,
|
||||||
|
37,
|
||||||
|
38,
|
||||||
|
45,
|
||||||
|
46,
|
||||||
|
47
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
30,
|
||||||
|
31,
|
||||||
|
32,
|
||||||
|
39,
|
||||||
|
40,
|
||||||
|
41,
|
||||||
|
48,
|
||||||
|
49,
|
||||||
|
50
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
33,
|
||||||
|
34,
|
||||||
|
35,
|
||||||
|
42,
|
||||||
|
43,
|
||||||
|
44,
|
||||||
|
51,
|
||||||
|
52,
|
||||||
|
53
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
54,
|
||||||
|
55,
|
||||||
|
56,
|
||||||
|
63,
|
||||||
|
64,
|
||||||
|
65,
|
||||||
|
72,
|
||||||
|
73,
|
||||||
|
74
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
57,
|
||||||
|
58,
|
||||||
|
59,
|
||||||
|
66,
|
||||||
|
67,
|
||||||
|
68,
|
||||||
|
75,
|
||||||
|
76,
|
||||||
|
77
|
||||||
|
]},
|
||||||
|
{"cellIDs": [
|
||||||
|
60,
|
||||||
|
61,
|
||||||
|
62,
|
||||||
|
69,
|
||||||
|
70,
|
||||||
|
71,
|
||||||
|
78,
|
||||||
|
79,
|
||||||
|
80
|
||||||
|
]}
|
||||||
|
],
|
||||||
|
"multidoku": [{
|
||||||
|
"blockWidth": 3,
|
||||||
|
"cells": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8,
|
||||||
|
9,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
13,
|
||||||
|
14,
|
||||||
|
15,
|
||||||
|
16,
|
||||||
|
17,
|
||||||
|
18,
|
||||||
|
19,
|
||||||
|
20,
|
||||||
|
21,
|
||||||
|
22,
|
||||||
|
23,
|
||||||
|
24,
|
||||||
|
25,
|
||||||
|
26,
|
||||||
|
27,
|
||||||
|
28,
|
||||||
|
29,
|
||||||
|
30,
|
||||||
|
31,
|
||||||
|
32,
|
||||||
|
33,
|
||||||
|
34,
|
||||||
|
35,
|
||||||
|
36,
|
||||||
|
37,
|
||||||
|
38,
|
||||||
|
39,
|
||||||
|
40,
|
||||||
|
41,
|
||||||
|
42,
|
||||||
|
43,
|
||||||
|
44,
|
||||||
|
45,
|
||||||
|
46,
|
||||||
|
47,
|
||||||
|
48,
|
||||||
|
49,
|
||||||
|
50,
|
||||||
|
51,
|
||||||
|
52,
|
||||||
|
53,
|
||||||
|
54,
|
||||||
|
55,
|
||||||
|
56,
|
||||||
|
57,
|
||||||
|
58,
|
||||||
|
59,
|
||||||
|
60,
|
||||||
|
61,
|
||||||
|
62,
|
||||||
|
63,
|
||||||
|
64,
|
||||||
|
65,
|
||||||
|
66,
|
||||||
|
67,
|
||||||
|
68,
|
||||||
|
69,
|
||||||
|
70,
|
||||||
|
71,
|
||||||
|
72,
|
||||||
|
73,
|
||||||
|
74,
|
||||||
|
75,
|
||||||
|
76,
|
||||||
|
77,
|
||||||
|
78,
|
||||||
|
79,
|
||||||
|
80
|
||||||
|
],
|
||||||
|
"blocks": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
5,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
8
|
||||||
|
],
|
||||||
|
"constraints": [
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package gui;
|
package gui;
|
||||||
|
|
||||||
import imgui.ImFont;
|
import imgui.ImFont;
|
||||||
|
import imgui.ImFontConfig;
|
||||||
|
import imgui.ImFontGlyphRangesBuilder;
|
||||||
import imgui.ImGui;
|
import imgui.ImGui;
|
||||||
|
|
||||||
public class Fonts {
|
public class Fonts {
|
||||||
@@ -10,13 +12,21 @@ public class Fonts {
|
|||||||
public static ImFont CHERI;
|
public static ImFont CHERI;
|
||||||
public static ImFont COMIC;
|
public static ImFont COMIC;
|
||||||
public static ImFont INFECTED;
|
public static ImFont INFECTED;
|
||||||
|
public static ImFont EMOJIS;
|
||||||
|
|
||||||
private static final String baseDir = "";
|
private static final String baseDir = "";
|
||||||
|
|
||||||
public static void createFonts() {
|
public static void createFonts() {
|
||||||
|
ImFontGlyphRangesBuilder builder = new ImFontGlyphRangesBuilder();
|
||||||
|
builder.addRanges(ImGui.getIO().getFonts().getGlyphRangesDefault());
|
||||||
|
builder.addRanges(ImGui.getIO().getFonts().getGlyphRangesCyrillic());
|
||||||
|
// builder.addRanges(ImGui.getIO().getFonts().getGlyphRangesChineseFull());
|
||||||
|
ImFontConfig cfg = new ImFontConfig();
|
||||||
|
cfg.setGlyphRanges(builder.buildRanges());
|
||||||
|
|
||||||
COMIC = ImGui.getIO().getFonts().addFontFromFileTTF(baseDir + "comic.ttf", 50.0f);
|
COMIC = ImGui.getIO().getFonts().addFontFromFileTTF(baseDir + "comic.ttf", 50.0f);
|
||||||
ARIAL_BOLD = ImGui.getIO().getFonts().addFontFromFileTTF(baseDir + "arial_bold.ttf", 50.0f);
|
ARIAL_BOLD = ImGui.getIO().getFonts().addFontFromFileTTF(baseDir + "arial_bold.ttf", 50.0f);
|
||||||
ARIAL = ImGui.getIO().getFonts().addFontFromFileTTF(baseDir + "arial.ttf", 50.0f);
|
ARIAL = ImGui.getIO().getFonts().addFontFromFileTTF(baseDir + "arial.ttf", 50.0f, cfg);
|
||||||
CHERI = ImGui.getIO().getFonts().addFontFromFileTTF(baseDir + "cheri.ttf", 50.0f);
|
CHERI = ImGui.getIO().getFonts().addFontFromFileTTF(baseDir + "cheri.ttf", 50.0f);
|
||||||
INFECTED = ImGui.getIO().getFonts().addFontFromFileTTF(baseDir + "INFECTED.ttf", 50.0f);
|
INFECTED = ImGui.getIO().getFonts().addFontFromFileTTF(baseDir + "INFECTED.ttf", 50.0f);
|
||||||
}
|
}
|
||||||
|
|||||||
39
app/src/main/java/gui/Images.java
Normal file
39
app/src/main/java/gui/Images.java
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package gui;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL11;
|
||||||
|
import org.lwjgl.stb.STBImage;
|
||||||
|
|
||||||
|
public class Images {
|
||||||
|
|
||||||
|
public static int BACKGROUND;
|
||||||
|
|
||||||
|
private static int loadTexture(String fileName) {
|
||||||
|
int[] width = new int[1];
|
||||||
|
int[] height = new int[1];
|
||||||
|
int[] channelCount = new int[1];
|
||||||
|
|
||||||
|
ByteBuffer pixels = STBImage.stbi_load(fileName, width, height, channelCount, 4);
|
||||||
|
|
||||||
|
int textureID = GL11.glGenTextures();
|
||||||
|
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
|
||||||
|
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
|
||||||
|
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
|
||||||
|
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1); // Not on WebGL/ES
|
||||||
|
GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_PIXELS, 0); // Not on WebGL/ES
|
||||||
|
GL11.glPixelStorei(GL11.GL_UNPACK_SKIP_ROWS, 0); // Not on WebGL/ES
|
||||||
|
GL11.glPixelStorei(GL11.GL_UNPACK_ROW_LENGTH, 0); // Not on WebGL/ES
|
||||||
|
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, width[0], height[0], 0,
|
||||||
|
GL11.GL_RGBA,
|
||||||
|
GL11.GL_UNSIGNED_BYTE, pixels);
|
||||||
|
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
|
return textureID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadImages() {
|
||||||
|
BACKGROUND = loadTexture("background.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -27,6 +27,12 @@ public class Main extends Application {
|
|||||||
stateMachine.pushState(new MainMenu(stateMachine));
|
stateMachine.pushState(new MainMenu(stateMachine));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void preRun() {
|
||||||
|
super.preRun();
|
||||||
|
Images.loadImages();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process() {
|
public void process() {
|
||||||
stateMachine.render();
|
stateMachine.render();
|
||||||
|
|||||||
7
app/src/main/java/gui/Options.java
Normal file
7
app/src/main/java/gui/Options.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package gui;
|
||||||
|
|
||||||
|
public class Options {
|
||||||
|
|
||||||
|
public static Symbols Symboles = Symbols.Numbers;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -47,8 +47,6 @@ public class RenderableMultidoku {
|
|||||||
return cells.get(index);
|
return cells.get(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static record PositionConstraint(Sudoku sudoku1, Sudoku sudoku2, Coordinate offset) {
|
private static record PositionConstraint(Sudoku sudoku1, Sudoku sudoku2, Coordinate offset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ public class SudokuRenderer {
|
|||||||
ImGui.closeCurrentPopup();
|
ImGui.closeCurrentPopup();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (ImGui.button(Integer.toString(i + 1), cellSize)) {
|
if (ImGui.button(Options.Symboles.getSymbols().get(i), cellSize)) {
|
||||||
currentCell.trySetValue(i);
|
currentCell.trySetValue(i);
|
||||||
if (this.doku.getDoku().isSolved())
|
if (this.doku.getDoku().isSolved())
|
||||||
this.onResolve.emit();
|
this.onResolve.emit();
|
||||||
@@ -88,6 +88,9 @@ public class SudokuRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void render() {
|
public void render() {
|
||||||
|
if (Options.Symboles == Symbols.Russian) {
|
||||||
|
ImGui.pushFont(Fonts.ARIAL);
|
||||||
|
}
|
||||||
final float sudokuViewWidth = cellSize.x * doku.getWidth();
|
final float sudokuViewWidth = cellSize.x * doku.getWidth();
|
||||||
final float displayWidth = ImGui.getIO().getDisplaySizeX();
|
final float displayWidth = ImGui.getIO().getDisplaySizeX();
|
||||||
float offsetX = displayWidth / 2.0f - sudokuViewWidth / 2.0f;
|
float offsetX = displayWidth / 2.0f - sudokuViewWidth / 2.0f;
|
||||||
@@ -123,7 +126,7 @@ public class SudokuRenderer {
|
|||||||
ImGui.pushStyleColor(ImGuiCol.Button, new ImVec4(blockColor.r, blockColor.g, blockColor.b, 1.0f));
|
ImGui.pushStyleColor(ImGuiCol.Button, new ImVec4(blockColor.r, blockColor.g, blockColor.b, 1.0f));
|
||||||
String cellText = "";
|
String cellText = "";
|
||||||
if (symbol != -1)
|
if (symbol != -1)
|
||||||
cellText += Integer.toString(symbol + 1);
|
cellText += Options.Symboles.getSymbols().get(symbol);
|
||||||
if (ImGui.button(cellText + "##" + index, cellSize) && cell.isMutable()) {
|
if (ImGui.button(cellText + "##" + index, cellSize) && cell.isMutable()) {
|
||||||
ImGui.openPopup("editPopup");
|
ImGui.openPopup("editPopup");
|
||||||
currentCell = cell;
|
currentCell = cell;
|
||||||
@@ -135,6 +138,9 @@ public class SudokuRenderer {
|
|||||||
ImGui.popStyleVar(2);
|
ImGui.popStyleVar(2);
|
||||||
renderPopup();
|
renderPopup();
|
||||||
ImGui.endChild();
|
ImGui.endChild();
|
||||||
|
if (Options.Symboles == Symbols.Russian) {
|
||||||
|
ImGui.popFont();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
83
app/src/main/java/gui/Symbols.java
Normal file
83
app/src/main/java/gui/Symbols.java
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package gui;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public enum Symbols {
|
||||||
|
|
||||||
|
Numbers("Nombres", getNumbers()),
|
||||||
|
Letters("Lettres", getLetters()),
|
||||||
|
Russian("Cyrilique", getRussian()),
|
||||||
|
Emojis("Emojis (Console uniquement)", getEmojis());
|
||||||
|
|
||||||
|
String displayName;
|
||||||
|
List<String> symbols;
|
||||||
|
|
||||||
|
private Symbols(String displayName, List<String> symbols) {
|
||||||
|
this.symbols = symbols;
|
||||||
|
this.displayName = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getSymbols() {
|
||||||
|
return symbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> getNumbers() {
|
||||||
|
List<String> sym = new ArrayList<>();
|
||||||
|
for (int i = 1; i <= 100; i++) {
|
||||||
|
sym.add(Integer.toString(i));
|
||||||
|
}
|
||||||
|
return sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> getLetters() {
|
||||||
|
List<String> sym = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
int currentOffset = i;
|
||||||
|
String letter = "";
|
||||||
|
while (currentOffset >= 26) {
|
||||||
|
letter += Character.toString((char) ('A' + currentOffset % 26));
|
||||||
|
currentOffset /= 26;
|
||||||
|
currentOffset--;
|
||||||
|
}
|
||||||
|
letter += Character.toString((char) ('A' + currentOffset % 26));
|
||||||
|
sym.add(new StringBuilder(letter).reverse().toString());
|
||||||
|
}
|
||||||
|
return sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> getRussian() {
|
||||||
|
List<String> sym = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
sym.add(new String(Character.toChars(0X0400 + i)));
|
||||||
|
}
|
||||||
|
return sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> getEmojis() {
|
||||||
|
List<String> sym = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
sym.add(new String(Character.toChars(0X1F600 + i)));
|
||||||
|
}
|
||||||
|
return sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String[] symbolNames;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Symbols[] symbols = Symbols.values();
|
||||||
|
symbolNames = new String[symbols.length];
|
||||||
|
for (int i = 0; i < symbols.length; i++) {
|
||||||
|
symbolNames[i] = symbols[i].getDisplayName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] getSymbolsNames() {
|
||||||
|
return symbolNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,17 +1,24 @@
|
|||||||
package gui.menu;
|
package gui.menu;
|
||||||
|
|
||||||
|
import gui.Options;
|
||||||
|
import gui.Symbols;
|
||||||
import imgui.ImGui;
|
import imgui.ImGui;
|
||||||
|
import imgui.type.ImInt;
|
||||||
|
|
||||||
public class OptionsMenu extends BaseView {
|
public class OptionsMenu extends BaseView {
|
||||||
|
|
||||||
|
private ImInt currentValue = new ImInt();
|
||||||
|
|
||||||
public OptionsMenu(StateMachine stateMachine) {
|
public OptionsMenu(StateMachine stateMachine) {
|
||||||
super(stateMachine);
|
super(stateMachine);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render() {
|
public void render() {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
ImGui.text("Options");
|
ImGui.text("Options");
|
||||||
|
if(ImGui.combo("Jeu de symboles", currentValue, Symbols.getSymbolsNames())){
|
||||||
|
Options.Symboles = Symbols.values()[currentValue.get()];
|
||||||
|
}
|
||||||
renderReturnButton();
|
renderReturnButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package gui.menu;
|
|||||||
|
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import gui.Images;
|
||||||
import imgui.ImGui;
|
import imgui.ImGui;
|
||||||
import imgui.ImVec2;
|
import imgui.ImVec2;
|
||||||
import imgui.flag.ImGuiKey;
|
import imgui.flag.ImGuiKey;
|
||||||
@@ -41,6 +42,12 @@ public class StateMachine {
|
|||||||
var displaySize = ImGui.getIO().getDisplaySize();
|
var displaySize = ImGui.getIO().getDisplaySize();
|
||||||
ImGui.setNextWindowPos(new ImVec2(0.0f, 0.0f));
|
ImGui.setNextWindowPos(new ImVec2(0.0f, 0.0f));
|
||||||
ImGui.setNextWindowSize(displaySize);
|
ImGui.setNextWindowSize(displaySize);
|
||||||
|
ImGui.begin("Background", null, ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoMove
|
||||||
|
| ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.NoBringToFrontOnFocus | ImGuiWindowFlags.NoInputs);
|
||||||
|
ImGui.image(Images.BACKGROUND, displaySize, new ImVec2(0, 0));
|
||||||
|
ImGui.end();
|
||||||
|
ImGui.setNextWindowPos(new ImVec2(0.0f, 0.0f));
|
||||||
|
ImGui.setNextWindowSize(displaySize);
|
||||||
ImGui.begin("##Main Window", null, ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoMove
|
ImGui.begin("##Main Window", null, ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoMove
|
||||||
| ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoBackground);
|
| ImGuiWindowFlags.NoSavedSettings | ImGuiWindowFlags.NoBackground);
|
||||||
menus.get(menus.size() - 1).render();
|
menus.get(menus.size() - 1).render();
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
package gui.menu;
|
package gui.menu;
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
|
|
||||||
import gui.SudokuRenderer;
|
import gui.SudokuRenderer;
|
||||||
import imgui.ImGui;
|
import imgui.ImGui;
|
||||||
import imgui.ImGuiStyle;
|
import imgui.ImGuiStyle;
|
||||||
import sudoku.io.SudokuSerializer;
|
import sudoku.io.SudokuSerializer;
|
||||||
|
import sudoku.solver.BacktrackingSolver;
|
||||||
|
import sudoku.solver.HumanSolver;
|
||||||
|
import sudoku.solver.MixedSolver;
|
||||||
import sudoku.solver.Solver;
|
import sudoku.solver.Solver;
|
||||||
import sudoku.structure.MultiDoku;
|
import sudoku.structure.MultiDoku;
|
||||||
|
|
||||||
@@ -66,34 +68,52 @@ public class SudokuView extends BaseView {
|
|||||||
stopResolve();
|
stopResolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderSolveButton() {
|
private void startSolve(Solver solver) {
|
||||||
if (resolveThread != null)
|
|
||||||
ImGui.beginDisabled();
|
|
||||||
|
|
||||||
boolean beginSolve = false;
|
|
||||||
|
|
||||||
if (!resolved && centeredButton("Résoudre")) {
|
|
||||||
beginSolve = true;
|
|
||||||
}
|
|
||||||
if (resolveThread != null)
|
|
||||||
ImGui.endDisabled();
|
|
||||||
|
|
||||||
if (beginSolve) {
|
|
||||||
resolveThread = new Thread(() -> {
|
resolveThread = new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
Random rand = new Random();
|
solver.solve(this.doku);
|
||||||
Solver.randomSolve(doku, rand);
|
} catch (CancellationException e) {
|
||||||
Thread.sleep(200);
|
|
||||||
} catch (CancellationException | InterruptedException e) {
|
|
||||||
System.out.println("The user is bored !");
|
System.out.println("The user is bored !");
|
||||||
}
|
}
|
||||||
stopResolve();
|
stopResolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resolved) {
|
private void renderSolvePopup() {
|
||||||
|
if (ImGui.beginPopup("solve")) {
|
||||||
|
if (ImGui.button("Résoudre avec backtrace")) {
|
||||||
|
startSolve(new BacktrackingSolver());
|
||||||
|
ImGui.closeCurrentPopup();
|
||||||
|
}
|
||||||
|
if (ImGui.button("Résoudre avec déduction")) {
|
||||||
|
startSolve(new HumanSolver());
|
||||||
|
ImGui.closeCurrentPopup();
|
||||||
|
}
|
||||||
|
if (ImGui.button("Résoudre avec déduction et backtrace")) {
|
||||||
|
startSolve(new MixedSolver());
|
||||||
|
ImGui.closeCurrentPopup();
|
||||||
|
}
|
||||||
|
ImGui.endPopup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderSolveButton() {
|
||||||
|
if (resolveThread != null)
|
||||||
|
ImGui.beginDisabled();
|
||||||
|
|
||||||
|
if (!this.resolved && centeredButton("Résoudre")) {
|
||||||
|
// beginSolve = true;
|
||||||
|
ImGui.openPopup("solve");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolveThread != null)
|
||||||
|
ImGui.endDisabled();
|
||||||
|
|
||||||
|
if (this.resolved) {
|
||||||
ImGui.text("Bravo !");
|
ImGui.text("Bravo !");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderSolvePopup();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderSaveButton() {
|
private void renderSaveButton() {
|
||||||
|
|||||||
@@ -3,15 +3,40 @@
|
|||||||
*/
|
*/
|
||||||
package sudoku;
|
package sudoku;
|
||||||
|
|
||||||
|
import gui.RenderableMultidoku;
|
||||||
|
import gui.Symbols;
|
||||||
import sudoku.io.ConsoleInterface;
|
import sudoku.io.ConsoleInterface;
|
||||||
|
import sudoku.io.SudokuPrinter;
|
||||||
|
import sudoku.solver.RandomSolver;
|
||||||
|
import sudoku.solver.Solver;
|
||||||
|
import sudoku.structure.MultiDoku;
|
||||||
|
import sudoku.structure.SudokuFactory;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
public String getGreeting() {
|
public String getGreeting() {
|
||||||
return "Hello World!";
|
return "Hello World!";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void voidTest(){
|
||||||
|
MultiDoku md = SudokuFactory.createBasicXShapedMultidoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
|
SudokuPrinter.printMultiDoku(RenderableMultidoku.fromMultidoku(md), Symbols.Numbers, 3, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void filledTest(){
|
||||||
|
MultiDoku md = SudokuFactory.createBasicXShapedMultidoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
|
new RandomSolver().solve(md);
|
||||||
|
SudokuPrinter.printMultiDoku(RenderableMultidoku.fromMultidoku(md), Symbols.Numbers, 3, 3);
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
ConsoleInterface console = new ConsoleInterface();
|
ConsoleInterface console = new ConsoleInterface();
|
||||||
console.start();
|
/*
|
||||||
|
voidTest();
|
||||||
|
filledTest();
|
||||||
|
filledTest();
|
||||||
|
*/
|
||||||
|
console.welcome();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,27 @@
|
|||||||
package sudoku.io;
|
package sudoku.io;
|
||||||
|
|
||||||
|
import gui.RenderableMultidoku;
|
||||||
|
import gui.Symbols;
|
||||||
import sudoku.constraint.*;
|
import sudoku.constraint.*;
|
||||||
import sudoku.solver.Solver;
|
import sudoku.solver.RandomSolver;
|
||||||
import sudoku.structure.Difficulty;
|
import sudoku.structure.Difficulty;
|
||||||
import sudoku.structure.MultiDoku;
|
import sudoku.structure.MultiDoku;
|
||||||
import sudoku.structure.Sudoku;
|
|
||||||
import sudoku.structure.SudokuFactory;
|
import sudoku.structure.SudokuFactory;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
|
||||||
public class ConsoleInterface {
|
public class ConsoleInterface {
|
||||||
public Scanner reader = new Scanner(System.in);
|
public Scanner reader = new Scanner(System.in);
|
||||||
|
|
||||||
|
public void welcome(){
|
||||||
|
System.out.println("Welcome to our Sudoku Solver!");
|
||||||
|
System.out.println("This is the project of Melvyn Bauvent, Lilas Grenier and Simon Priblyski.");
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
public void start(){
|
public void start(){
|
||||||
welcome();
|
|
||||||
System.out.println("First of all, you need to tell me the size of the sudoku you want to generate.");
|
System.out.println("First of all, you need to tell me the size of the sudoku you want to generate.");
|
||||||
int width = getBlockWidth();
|
int width = getBlockWidth();
|
||||||
int height = getBlockHeight();
|
int height = getBlockHeight();
|
||||||
@@ -28,26 +33,21 @@ public class ConsoleInterface {
|
|||||||
pickSymbols(listSymbols, numberOfSymbols);
|
pickSymbols(listSymbols, numberOfSymbols);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// TODO
|
listSymbols = Symbols.Numbers.getSymbols();
|
||||||
System.out.println("Simon doit finir sa partie.");
|
|
||||||
assert false;
|
|
||||||
}
|
}
|
||||||
List<Constraint> listConstraints = getListConstraints();
|
List<Constraint> listConstraints = getListConstraints();
|
||||||
|
|
||||||
|
|
||||||
System.out.println("Now that we have the size of our sudoku, would you rather have a single grid ('one', default), " +
|
System.out.println("Now that we have the size of our sudoku, would you rather have a single grid ('one', default), " +
|
||||||
"or a a multidoku composed of 5 subgrids ('multi') ?");
|
"or a a multidoku composed of 5 subgrids ('multi') ?");
|
||||||
List<Sudoku> subGrids = new ArrayList<>();
|
|
||||||
MultiDoku doku;
|
MultiDoku doku;
|
||||||
if (reader.next().equalsIgnoreCase("multi")) {
|
if (reader.next().equalsIgnoreCase("multi")) {
|
||||||
doku = SudokuFactory.createBasicEmptyRectangleDoku(width, height, listConstraints);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
doku = SudokuFactory.createBasicXShapedMultidoku(width, height, listConstraints);
|
doku = SudokuFactory.createBasicXShapedMultidoku(width, height, listConstraints);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
doku = SudokuFactory.createBasicEmptyRectangleDoku(width, height, listConstraints);
|
||||||
|
}
|
||||||
|
RenderableMultidoku rm = RenderableMultidoku.fromMultidoku(doku);
|
||||||
System.out.println("Your sudoku will look like this:");
|
System.out.println("Your sudoku will look like this:");
|
||||||
// TODO printMultiDoku method not yet implemented
|
SudokuPrinter.printMultiDoku(rm, listSymbols, width, height);
|
||||||
SudokuPrinter.printMultiDoku(doku, width, height);
|
|
||||||
System.out.println("We now will fill this sudoku.");
|
System.out.println("We now will fill this sudoku.");
|
||||||
System.out.println("What level of difficulty would you like? ('very easy', 'easy', 'medium' (default), 'hard', 'full' (sudoku fully completed))");
|
System.out.println("What level of difficulty would you like? ('very easy', 'easy', 'medium' (default), 'hard', 'full' (sudoku fully completed))");
|
||||||
String difficulty = reader.next().toLowerCase();
|
String difficulty = reader.next().toLowerCase();
|
||||||
@@ -58,12 +58,7 @@ public class ConsoleInterface {
|
|||||||
generatePartialDoku(doku, difficulty);
|
generatePartialDoku(doku, difficulty);
|
||||||
}
|
}
|
||||||
System.out.println("Here's your sudoku !");
|
System.out.println("Here's your sudoku !");
|
||||||
SudokuPrinter.printMultiDoku(doku, width, height);
|
SudokuPrinter.printMultiDoku(rm, listSymbols, width, height);
|
||||||
}
|
|
||||||
|
|
||||||
public void welcome(){
|
|
||||||
System.out.println("Welcome to our Sudoku Solver!");
|
|
||||||
System.out.println("This is the project of Melvyn Bauvent, Lilas Grenier and Simon Priblyski.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getBlockWidth() {
|
public int getBlockWidth() {
|
||||||
@@ -133,7 +128,7 @@ public class ConsoleInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void generateFullDoku(MultiDoku doku) {
|
private void generateFullDoku(MultiDoku doku) {
|
||||||
Solver.randomSolve(doku, new Random());
|
new RandomSolver().solve(doku);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,40 @@
|
|||||||
package sudoku.io;
|
package sudoku.io;
|
||||||
|
|
||||||
|
import gui.RenderableMultidoku;
|
||||||
|
import gui.Symbols;
|
||||||
|
import sudoku.structure.Cell;
|
||||||
import sudoku.structure.MultiDoku;
|
import sudoku.structure.MultiDoku;
|
||||||
import sudoku.structure.Sudoku;
|
import sudoku.structure.Sudoku;
|
||||||
|
|
||||||
public class SudokuPrinter {
|
import java.util.List;
|
||||||
|
|
||||||
public static void printRectangleSudoku(final Sudoku s, int blockWidth, int blockHeight) {
|
public class SudokuPrinter {
|
||||||
|
public static final String ANSI_RESET = "\u001B[0m";
|
||||||
|
public static final String ANSI_RED = "\u001B[31m";
|
||||||
|
public static final String ANSI_GREEN = "\u001B[32m";
|
||||||
|
public static final String ANSI_YELLOW = "\u001B[33m";
|
||||||
|
public static final String ANSI_BLUE = "\u001B[34m";
|
||||||
|
public static final String ANSI_PURPLE = "\u001B[35m";
|
||||||
|
public static final String ANSI_CYAN = "\u001B[36m";
|
||||||
|
|
||||||
|
public static void printRectangleSudoku(final Sudoku s, int blockWidth, int blockHeight, Symbols symbols) {
|
||||||
|
printRectangleSudoku(s, blockWidth, blockHeight, symbols.getSymbols());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printRectangleSudoku(final Sudoku s, int blockWidth, int blockHeight, List<String> listSymbols){
|
||||||
for (int y = 0; y < s.getSize(); y++) {
|
for (int y = 0; y < s.getSize(); y++) {
|
||||||
if (y % blockHeight == 0 && y > 0) {
|
if (y % blockHeight == 0 && y > 0) {
|
||||||
System.out.println();
|
System.out.println();
|
||||||
}
|
}
|
||||||
StringBuilder line = new StringBuilder("[ ");
|
StringBuilder line = new StringBuilder("[ ");
|
||||||
for (int x = 0; x < s.getSize(); x++) {
|
for (int x = 0; x < s.getSize(); x++) {
|
||||||
line.append((s.getCell(x, y).getSymbolIndex() + 1)).append(" ");
|
Cell c = s.getCell(x, y);
|
||||||
|
if (c.getSymbolIndex() == Cell.NOSYMBOL) {
|
||||||
|
line.append(" ");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
line.append(listSymbols.get(c.getSymbolIndex())).append(" ");
|
||||||
|
}
|
||||||
if (x % blockWidth == blockWidth - 1 && x != blockWidth * blockHeight - 1) {
|
if (x % blockWidth == blockWidth - 1 && x != blockWidth * blockHeight - 1) {
|
||||||
line.append("| ");
|
line.append("| ");
|
||||||
}
|
}
|
||||||
@@ -22,7 +44,46 @@ public class SudokuPrinter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String toStringRectangleSudoku(final Sudoku s, int blockWidth, int blockHeight) {
|
public static void printMultiDoku(final RenderableMultidoku rm, Symbols symbols, int blockWidth, int blockHeight) {
|
||||||
|
printMultiDoku(rm, symbols.getSymbols(), blockWidth, blockHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printMultiDoku(final RenderableMultidoku rm, List<String> listSymbols, int blockWidth, int blockHeight) {
|
||||||
|
StringBuilder line = new StringBuilder("\n");
|
||||||
|
int nBlockInWidth = rm.getWidth() / blockWidth;
|
||||||
|
for (int y = 0; y < rm.getHeight(); y++) {
|
||||||
|
if (y % blockHeight == 0) {
|
||||||
|
line.append("__".repeat(Math.max(0, rm.getWidth()+nBlockInWidth))).append("_\n");
|
||||||
|
}
|
||||||
|
line.append("[ ");
|
||||||
|
for (int x = 0; x < rm.getWidth(); x++) {
|
||||||
|
if (x % blockWidth == 0 && x > 0) {
|
||||||
|
line.append("| ");
|
||||||
|
}
|
||||||
|
Cell cell = rm.getCell(x, y);
|
||||||
|
if (cell != null) {
|
||||||
|
if (cell.getSymbolIndex() == Cell.NOSYMBOL) {
|
||||||
|
line.append("- ");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
line.append(listSymbols.get(cell.getSymbolIndex())).append(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
line.append(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line.append("]\n");
|
||||||
|
}
|
||||||
|
line.append("__".repeat(Math.max(0, rm.getWidth()+nBlockInWidth))).append("_\n");
|
||||||
|
System.out.println(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toStringRectangleSudoku(final Sudoku s, int blockWidth, int blockHeight, Symbols symbols){
|
||||||
|
return toStringRectangleSudoku(s, blockWidth, blockHeight, symbols.getSymbols());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toStringRectangleSudoku(final Sudoku s, int blockWidth, int blockHeight, List<String> listSymbols) {
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
for (int y = 0; y < s.getSize(); y++) {
|
for (int y = 0; y < s.getSize(); y++) {
|
||||||
// Ajouter une ligne vide entre les blocs horizontaux
|
// Ajouter une ligne vide entre les blocs horizontaux
|
||||||
@@ -32,7 +93,13 @@ public class SudokuPrinter {
|
|||||||
StringBuilder line = new StringBuilder("[ ");
|
StringBuilder line = new StringBuilder("[ ");
|
||||||
for (int x = 0; x < s.getSize(); x++) {
|
for (int x = 0; x < s.getSize(); x++) {
|
||||||
// Ajouter la valeur de la cellule
|
// Ajouter la valeur de la cellule
|
||||||
line.append((s.getCell(x, y).getSymbolIndex() + 1)).append(" ");
|
Cell cell = s.getCell(x, y);
|
||||||
|
if (cell.getSymbolIndex() == Cell.NOSYMBOL) {
|
||||||
|
line.append(" ");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
line.append(listSymbols.get(cell.getSymbolIndex())).append(" ");
|
||||||
|
}
|
||||||
|
|
||||||
// Ajouter un séparateur vertical entre les blocs
|
// Ajouter un séparateur vertical entre les blocs
|
||||||
if (x % blockWidth == blockWidth - 1 && x != s.getSize() - 1) {
|
if (x % blockWidth == blockWidth - 1 && x != s.getSize() - 1) {
|
||||||
@@ -45,7 +112,12 @@ public class SudokuPrinter {
|
|||||||
return result.toString();
|
return result.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void printMultiDoku(final MultiDoku doku, int blockWidth, int blockHeight){
|
public static void printMultiDoku(final MultiDoku doku, int blockWidth, int blockHeight, Symbols symbols){
|
||||||
// TODO
|
if (doku.getNbSubGrids()==1) {
|
||||||
|
printRectangleSudoku(doku.getSubGrid(0), blockWidth, blockHeight, symbols);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
printMultiDoku(RenderableMultidoku.fromMultidoku(doku), symbols, blockWidth, blockHeight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
49
app/src/main/java/sudoku/solver/BacktrackingSolver.java
Normal file
49
app/src/main/java/sudoku/solver/BacktrackingSolver.java
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package sudoku.solver;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
|
||||||
|
import sudoku.structure.Cell;
|
||||||
|
import sudoku.structure.MultiDoku;
|
||||||
|
|
||||||
|
public class BacktrackingSolver implements Solver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Résout le MultiDoku passé en paramètre, avec backtracking.
|
||||||
|
*
|
||||||
|
* @param doku MultiDoku, MultiDoku à résoudre.
|
||||||
|
* @return boolean, valant true si le MultiDoku est résolu, false sinon.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean solve(MultiDoku doku) {
|
||||||
|
if (Thread.interrupted())
|
||||||
|
throw new CancellationException("User wants to stop the solver");
|
||||||
|
|
||||||
|
if (doku.isSolved()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cell cellToFill = doku.getFirstEmptyCell();
|
||||||
|
if (cellToFill == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
|
||||||
|
if (possibleSymbols.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int symbol : possibleSymbols) {
|
||||||
|
|
||||||
|
cellToFill.setSymbolIndex(symbol);
|
||||||
|
if (this.solve(doku)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
cellToFill.setSymbolIndex(Cell.NOSYMBOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
57
app/src/main/java/sudoku/solver/HumanSolver.java
Normal file
57
app/src/main/java/sudoku/solver/HumanSolver.java
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package sudoku.solver;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import gui.Symbols;
|
||||||
|
import sudoku.io.SudokuPrinter;
|
||||||
|
import sudoku.structure.Cell;
|
||||||
|
import sudoku.structure.MultiDoku;
|
||||||
|
import sudoku.structure.Sudoku;
|
||||||
|
|
||||||
|
public class HumanSolver implements Solver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Résout le MultiDoku passé en paramètre, avec règles de déduction.
|
||||||
|
*
|
||||||
|
* @param doku MultiDoku, MultiDoku à résoudre.
|
||||||
|
* @return boolean, valant true si le MultiDoku est résolu, false sinon.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean solve(MultiDoku doku) {
|
||||||
|
if (Thread.interrupted())
|
||||||
|
throw new CancellationException("User wants to stop the solver");
|
||||||
|
|
||||||
|
Sudoku sudoku = doku.getSubGrid(0);
|
||||||
|
logger.log(Level.FINE,
|
||||||
|
'\n' + SudokuPrinter.toStringRectangleSudoku(sudoku,
|
||||||
|
sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getBlockWidth(),
|
||||||
|
sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getSize() / sudoku.getBlockWidth(),
|
||||||
|
Symbols.Numbers));
|
||||||
|
|
||||||
|
if (doku.isSolved()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Cell> cellsToFill = doku.getEmptyCells();
|
||||||
|
if (cellsToFill.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Cell cellToFill : cellsToFill) {
|
||||||
|
|
||||||
|
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
|
||||||
|
if (possibleSymbols.size() != 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cellToFill.setSymbolIndex(possibleSymbols.getFirst());
|
||||||
|
|
||||||
|
return this.solve(doku);
|
||||||
|
}
|
||||||
|
|
||||||
|
return doku.isSolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
82
app/src/main/java/sudoku/solver/MixedSolver.java
Normal file
82
app/src/main/java/sudoku/solver/MixedSolver.java
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
package sudoku.solver;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import gui.Symbols;
|
||||||
|
import sudoku.io.SudokuPrinter;
|
||||||
|
import sudoku.structure.Cell;
|
||||||
|
import sudoku.structure.MultiDoku;
|
||||||
|
import sudoku.structure.Sudoku;
|
||||||
|
|
||||||
|
public class MixedSolver implements Solver{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Résout le MultiDoku passé en paramètre, avec règles de déduction et
|
||||||
|
* backtracking.
|
||||||
|
*
|
||||||
|
* @param doku MultiDoku, MultiDoku à résoudre.
|
||||||
|
* @return boolean, valant true si le MultiDoku est résolu, false sinon.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean solve(MultiDoku doku) {
|
||||||
|
Random rand = new Random();
|
||||||
|
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
throw new CancellationException("User wants to stop the solver");
|
||||||
|
}
|
||||||
|
|
||||||
|
Sudoku sudoku = doku.getSubGrid(0);
|
||||||
|
logger.log(Level.FINE,
|
||||||
|
'\n' + SudokuPrinter.toStringRectangleSudoku(
|
||||||
|
sudoku,
|
||||||
|
sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getBlockWidth(),
|
||||||
|
sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getSize() / sudoku.getBlockWidth(),
|
||||||
|
Symbols.Numbers));
|
||||||
|
|
||||||
|
if (doku.isSolved()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Cell> cellsToFill = doku.getEmptyCells();
|
||||||
|
if (cellsToFill.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Règles de déduction
|
||||||
|
for (Cell cellToFill : cellsToFill) {
|
||||||
|
|
||||||
|
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
|
||||||
|
if (possibleSymbols.size() != 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cellToFill.setSymbolIndex(possibleSymbols.getFirst());
|
||||||
|
|
||||||
|
return this.solve(doku);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si ça ne marche pas
|
||||||
|
// On fait du backtracking
|
||||||
|
Cell cellToFill = doku.getRandomEmptyCell(rand);
|
||||||
|
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
|
||||||
|
|
||||||
|
while (!possibleSymbols.isEmpty()) {
|
||||||
|
int nextPossibleSymbolIndex = rand.nextInt(possibleSymbols.size());
|
||||||
|
int nextSymbol = possibleSymbols.get(nextPossibleSymbolIndex);
|
||||||
|
|
||||||
|
cellToFill.setSymbolIndex(nextSymbol);
|
||||||
|
if (this.solve(doku)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
cellToFill.setSymbolIndex(Cell.NOSYMBOL);
|
||||||
|
possibleSymbols.remove(nextPossibleSymbolIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
65
app/src/main/java/sudoku/solver/RandomSolver.java
Normal file
65
app/src/main/java/sudoku/solver/RandomSolver.java
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package sudoku.solver;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import gui.Symbols;
|
||||||
|
import sudoku.io.SudokuPrinter;
|
||||||
|
import sudoku.structure.Cell;
|
||||||
|
import sudoku.structure.MultiDoku;
|
||||||
|
import sudoku.structure.Sudoku;
|
||||||
|
|
||||||
|
public class RandomSolver implements Solver {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Résout, si possible, le multidoku passé en paramètre
|
||||||
|
* en testant toutes les possibilités, de manière aléatoire, avec un algorithme
|
||||||
|
* de backtracking.
|
||||||
|
*
|
||||||
|
* @param doku Multidoku, à résoudre
|
||||||
|
* @param rand Random, pour tester aléatoirement les symboles
|
||||||
|
* @return boolean, true s'il est résolu ou false s'il ne l'est pas.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean solve(MultiDoku doku) {
|
||||||
|
Random rand = new Random();
|
||||||
|
|
||||||
|
if (Thread.interrupted())
|
||||||
|
throw new CancellationException("User wants to stop the solver");
|
||||||
|
|
||||||
|
Sudoku sudoku = doku.getSubGrid(0);
|
||||||
|
logger.log(Level.FINE,
|
||||||
|
'\n' + SudokuPrinter.toStringRectangleSudoku(sudoku,
|
||||||
|
sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getBlockWidth(),
|
||||||
|
sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getSize() / sudoku.getBlockWidth(),
|
||||||
|
Symbols.Numbers));
|
||||||
|
|
||||||
|
if (doku.isSolved()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cell cellToFill = doku.getFirstEmptyCell();
|
||||||
|
if (cellToFill == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
|
||||||
|
|
||||||
|
while (!possibleSymbols.isEmpty()) {
|
||||||
|
int nextPossibleSymbolIndex = rand.nextInt(possibleSymbols.size());
|
||||||
|
int nextSymbol = possibleSymbols.get(nextPossibleSymbolIndex);
|
||||||
|
|
||||||
|
cellToFill.setSymbolIndex(nextSymbol);
|
||||||
|
if (this.solve(doku)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
cellToFill.setSymbolIndex(Cell.NOSYMBOL);
|
||||||
|
possibleSymbols.remove(nextPossibleSymbolIndex);
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,67 +1,19 @@
|
|||||||
package sudoku.solver;
|
package sudoku.solver;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
|
||||||
import java.util.concurrent.CancellationException;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import sudoku.io.SudokuPrinter;
|
|
||||||
import sudoku.structure.Cell;
|
import sudoku.structure.Cell;
|
||||||
import sudoku.structure.MultiDoku;
|
import sudoku.structure.MultiDoku;
|
||||||
import sudoku.structure.Sudoku;
|
|
||||||
|
|
||||||
public class Solver {
|
public interface Solver {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log du Solver, qui garde trace des actions réalisées.
|
* Log du Solver, qui garde trace des actions réalisées.
|
||||||
*/
|
*/
|
||||||
private static final Logger logger = Logger.getLogger("SolverLogger");
|
public static final Logger logger = Logger.getLogger("SolverLogger");
|
||||||
|
|
||||||
/**
|
boolean solve(MultiDoku doku);
|
||||||
* Résout, si possible, le multidoku passé en paramètre
|
|
||||||
* en testant toutes les possibilités, de manière aléatoire, avec un algorithme
|
|
||||||
* de backtracking.
|
|
||||||
*
|
|
||||||
* @param doku Multidoku, à résoudre
|
|
||||||
* @param rand Random, pour tester aléatoirement les symboles
|
|
||||||
* @return boolean, true s'il est résolu ou false s'il ne l'est pas.
|
|
||||||
*/
|
|
||||||
public static boolean randomSolve(MultiDoku doku, Random rand) {
|
|
||||||
if (Thread.interrupted())
|
|
||||||
throw new CancellationException("User wants to stop the solver");
|
|
||||||
|
|
||||||
Sudoku sudoku = doku.getSubGrid(0);
|
|
||||||
logger.log(Level.FINE,
|
|
||||||
'\n' + SudokuPrinter.toStringRectangleSudoku(sudoku,
|
|
||||||
sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getBlockWidth(),
|
|
||||||
sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getSize() / sudoku.getBlockWidth()));
|
|
||||||
|
|
||||||
if (doku.isSolved()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Cell cellToFill = doku.getFirstEmptyCell();
|
|
||||||
if (cellToFill == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
|
|
||||||
|
|
||||||
while (!possibleSymbols.isEmpty()) {
|
|
||||||
int nextPossibleSymbolIndex = rand.nextInt(possibleSymbols.size());
|
|
||||||
int nextSymbol = possibleSymbols.get(nextPossibleSymbolIndex);
|
|
||||||
|
|
||||||
cellToFill.setSymbolIndex(nextSymbol);
|
|
||||||
if (Solver.randomSolve(doku, rand)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
cellToFill.setSymbolIndex(Cell.NOSYMBOL);
|
|
||||||
possibleSymbols.remove(nextPossibleSymbolIndex);
|
|
||||||
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compte le nombre de solutions possibles au MultiDoku passé en paramètres.
|
* Compte le nombre de solutions possibles au MultiDoku passé en paramètres.
|
||||||
@@ -69,10 +21,9 @@ public class Solver {
|
|||||||
* @param doku MultiDoku, MultiDoku dont on veut le nombre de solutions.
|
* @param doku MultiDoku, MultiDoku dont on veut le nombre de solutions.
|
||||||
* @return int, nombre de solutions possibles.
|
* @return int, nombre de solutions possibles.
|
||||||
*/
|
*/
|
||||||
public static int countSolution(MultiDoku doku) {
|
default int countSolution(MultiDoku doku) {
|
||||||
int result = 0;
|
int result = 0;
|
||||||
|
|
||||||
|
|
||||||
if (doku.isSolved()) {
|
if (doku.isSolved()) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -85,152 +36,12 @@ public class Solver {
|
|||||||
for (int symbol : possibleSymbols) {
|
for (int symbol : possibleSymbols) {
|
||||||
doku.getStateManager().pushState();
|
doku.getStateManager().pushState();
|
||||||
cellToFill.setSymbolIndex(symbol);
|
cellToFill.setSymbolIndex(symbol);
|
||||||
if (Solver.solve(doku)) {
|
if (solve(doku)) {
|
||||||
result++;
|
result++;
|
||||||
}
|
}
|
||||||
doku.getStateManager().popState();
|
doku.getStateManager().popState();
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Résout le MultiDoku passé en paramètre, avec backtracking.
|
|
||||||
*
|
|
||||||
* @param doku MultiDoku, MultiDoku à résoudre.
|
|
||||||
* @return boolean, valant true si le MultiDoku est résolu, false sinon.
|
|
||||||
*/
|
|
||||||
public static boolean solve(MultiDoku doku) {
|
|
||||||
if (Thread.interrupted())
|
|
||||||
throw new CancellationException("User wants to stop the solver");
|
|
||||||
|
|
||||||
if (doku.isSolved()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Cell cellToFill = doku.getFirstEmptyCell();
|
|
||||||
if (cellToFill == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
|
|
||||||
if (possibleSymbols.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int symbol : possibleSymbols) {
|
|
||||||
|
|
||||||
cellToFill.setSymbolIndex(symbol);
|
|
||||||
if (Solver.solve(doku)) {
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
cellToFill.setSymbolIndex(Cell.NOSYMBOL);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Résout le MultiDoku passé en paramètre, avec règles de déduction.
|
|
||||||
*
|
|
||||||
* @param doku MultiDoku, MultiDoku à résoudre.
|
|
||||||
* @return boolean, valant true si le MultiDoku est résolu, false sinon.
|
|
||||||
*/
|
|
||||||
public static boolean humanSolve(MultiDoku doku) {
|
|
||||||
if (Thread.interrupted())
|
|
||||||
throw new CancellationException("User wants to stop the solver");
|
|
||||||
|
|
||||||
Sudoku sudoku = doku.getSubGrid(0);
|
|
||||||
logger.log(Level.FINE,
|
|
||||||
'\n' + SudokuPrinter.toStringRectangleSudoku(sudoku,
|
|
||||||
sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getBlockWidth(),
|
|
||||||
sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getSize() / sudoku.getBlockWidth()));
|
|
||||||
|
|
||||||
if (doku.isSolved()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Cell> cellsToFill = doku.getEmptyCells();
|
|
||||||
if (cellsToFill.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Cell cellToFill : cellsToFill) {
|
|
||||||
|
|
||||||
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
|
|
||||||
if (possibleSymbols.size() != 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
cellToFill.setSymbolIndex(possibleSymbols.getFirst());
|
|
||||||
|
|
||||||
return Solver.humanSolve(doku);
|
|
||||||
}
|
|
||||||
|
|
||||||
return doku.isSolved();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Résout le MultiDoku passé en paramètre, avec règles de déduction et backtracking.
|
|
||||||
*
|
|
||||||
* @param doku MultiDoku, MultiDoku à résoudre.
|
|
||||||
* @param rand Random, pour tester aléatoirement les symboles, lors du backtracking.
|
|
||||||
* @return boolean, valant true si le MultiDoku est résolu, false sinon.
|
|
||||||
*/
|
|
||||||
public static boolean mixedSolve(MultiDoku doku, Random rand) {
|
|
||||||
if (Thread.interrupted()) {
|
|
||||||
throw new CancellationException("User wants to stop the solver");
|
|
||||||
}
|
|
||||||
|
|
||||||
Sudoku sudoku = doku.getSubGrid(0);
|
|
||||||
logger.log(Level.FINE,
|
|
||||||
'\n' + SudokuPrinter.toStringRectangleSudoku(
|
|
||||||
sudoku,
|
|
||||||
sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getBlockWidth(),
|
|
||||||
sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getSize() / sudoku.getBlockWidth())
|
|
||||||
);
|
|
||||||
|
|
||||||
if (doku.isSolved()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Cell> cellsToFill = doku.getEmptyCells();
|
|
||||||
if (cellsToFill.isEmpty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Règles de déduction
|
|
||||||
for (Cell cellToFill : cellsToFill) {
|
|
||||||
|
|
||||||
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
|
|
||||||
if (possibleSymbols.size() != 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
cellToFill.setSymbolIndex(possibleSymbols.getFirst());
|
|
||||||
|
|
||||||
return Solver.mixedSolve(doku, rand);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Si ça ne marche pas
|
|
||||||
//On fait du backtracking
|
|
||||||
Cell cellToFill = doku.getRandomEmptyCell(rand);
|
|
||||||
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
|
|
||||||
|
|
||||||
while (!possibleSymbols.isEmpty()) {
|
|
||||||
int nextPossibleSymbolIndex = rand.nextInt(possibleSymbols.size());
|
|
||||||
int nextSymbol = possibleSymbols.get(nextPossibleSymbolIndex);
|
|
||||||
|
|
||||||
cellToFill.setSymbolIndex(nextSymbol);
|
|
||||||
if (Solver.mixedSolve(doku, rand)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
cellToFill.setSymbolIndex(Cell.NOSYMBOL);
|
|
||||||
possibleSymbols.remove(nextPossibleSymbolIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package sudoku.solver;
|
|||||||
|
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
|
|
||||||
import sudoku.structure.Coordinate;
|
|
||||||
import sudoku.structure.MultiDoku;
|
import sudoku.structure.MultiDoku;
|
||||||
import sudoku.structure.Sudoku;
|
import sudoku.structure.Sudoku;
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import sudoku.io.SudokuSerializer;
|
|
||||||
|
|
||||||
import sudoku.constraint.Constraint;
|
import sudoku.constraint.Constraint;
|
||||||
|
import sudoku.io.SudokuSerializer;
|
||||||
|
import sudoku.solver.RandomSolver;
|
||||||
import sudoku.solver.Solver;
|
import sudoku.solver.Solver;
|
||||||
|
|
||||||
public class SudokuFactory {
|
public class SudokuFactory {
|
||||||
@@ -116,7 +116,7 @@ public class SudokuFactory {
|
|||||||
* @return boolean, valant true si un MultiDoku de difficulté donnée peut être créée, false sinon.
|
* @return boolean, valant true si un MultiDoku de difficulté donnée peut être créée, false sinon.
|
||||||
* @throws Exception si la difficulté n'est pas compatible avec la taille du MultiDoku.
|
* @throws Exception si la difficulté n'est pas compatible avec la taille du MultiDoku.
|
||||||
*/
|
*/
|
||||||
public static boolean newDokuFromFilledOne(MultiDoku doku, int nbCellsToEmpty) throws Exception {
|
public static boolean newDokuFromFilledOne(MultiDoku doku, int nbCellsToEmpty, Solver solver) throws Exception {
|
||||||
|
|
||||||
if (nbCellsToEmpty >= doku.getCells().size()) {
|
if (nbCellsToEmpty >= doku.getCells().size()) {
|
||||||
throw new Exception();
|
throw new Exception();
|
||||||
@@ -134,9 +134,9 @@ public class SudokuFactory {
|
|||||||
|
|
||||||
int oldSymbol = cellToEmpty.empty();
|
int oldSymbol = cellToEmpty.empty();
|
||||||
|
|
||||||
int nbDokuSultions = Solver.countSolution(doku);
|
int nbDokuSultions = solver.countSolution(doku);
|
||||||
if (nbDokuSultions == 1) {
|
if (nbDokuSultions == 1) {
|
||||||
if (newDokuFromFilledOne(doku, --nbCellsToEmpty)) {
|
if (newDokuFromFilledOne(doku, --nbCellsToEmpty, solver)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,7 +145,7 @@ public class SudokuFactory {
|
|||||||
cellsThatCanBeEmptied.remove(cellToEmpty);
|
cellsThatCanBeEmptied.remove(cellToEmpty);
|
||||||
}
|
}
|
||||||
|
|
||||||
return newDokuFromFilledOne(doku, --nbCellsToEmpty);
|
return newDokuFromFilledOne(doku, --nbCellsToEmpty, solver);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -272,9 +272,10 @@ public class SudokuFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void fillDoku(MultiDoku doku, Difficulty difficulty) throws Exception {
|
public static void fillDoku(MultiDoku doku, Difficulty difficulty) throws Exception {
|
||||||
Solver.randomSolve(doku, random);
|
Solver solver = new RandomSolver();
|
||||||
|
solver.solve(doku);
|
||||||
int nbCellsToEmpty = (int) (difficulty.getFactor() * doku.getNbCells());
|
int nbCellsToEmpty = (int) (difficulty.getFactor() * doku.getNbCells());
|
||||||
boolean successfull = newDokuFromFilledOne(doku, nbCellsToEmpty);
|
boolean successfull = newDokuFromFilledOne(doku, nbCellsToEmpty, solver);
|
||||||
if (!successfull) {
|
if (!successfull) {
|
||||||
throw new Exception("Canno't create this doku with this difficulty");
|
throw new Exception("Canno't create this doku with this difficulty");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import org.json.JSONObject;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import sudoku.io.SudokuSerializer;
|
import sudoku.io.SudokuSerializer;
|
||||||
import sudoku.solver.Solver;
|
import sudoku.solver.RandomSolver;
|
||||||
import sudoku.structure.MultiDoku;
|
import sudoku.structure.MultiDoku;
|
||||||
import sudoku.structure.SudokuFactory;
|
import sudoku.structure.SudokuFactory;
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ public class SudokuSerializerTest {
|
|||||||
void testSerializeWithSize(int blockWidth, int blockHeight) {
|
void testSerializeWithSize(int blockWidth, int blockHeight) {
|
||||||
var sudoku = SudokuFactory.createBasicEmptyRectangleDoku(blockWidth, blockHeight,
|
var sudoku = SudokuFactory.createBasicEmptyRectangleDoku(blockWidth, blockHeight,
|
||||||
SudokuFactory.DEFAULT_CONSTRAINTS);
|
SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
Solver.randomSolve(sudoku, new Random());
|
new RandomSolver().solve(sudoku);
|
||||||
JSONObject data = SudokuSerializer.serializeSudoku(sudoku);
|
JSONObject data = SudokuSerializer.serializeSudoku(sudoku);
|
||||||
MultiDoku multiDoku = SudokuSerializer.deserializeSudoku(data);
|
MultiDoku multiDoku = SudokuSerializer.deserializeSudoku(data);
|
||||||
assertTrue(data.toString().equals(SudokuSerializer.serializeSudoku(multiDoku).toString()));
|
assertTrue(data.toString().equals(SudokuSerializer.serializeSudoku(multiDoku).toString()));
|
||||||
@@ -28,14 +28,15 @@ public class SudokuSerializerTest {
|
|||||||
void testSaveWithSize(int blockWidth, int blockHeight) {
|
void testSaveWithSize(int blockWidth, int blockHeight) {
|
||||||
MultiDoku doku = SudokuFactory.createBasicEmptyRectangleDoku(blockWidth, blockHeight,
|
MultiDoku doku = SudokuFactory.createBasicEmptyRectangleDoku(blockWidth, blockHeight,
|
||||||
SudokuFactory.DEFAULT_CONSTRAINTS);
|
SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
Solver.randomSolve(doku, new Random());
|
new RandomSolver().solve(doku);
|
||||||
String savePath = SudokuSerializer.saveMultiDoku(doku);
|
String savePath = SudokuSerializer.saveMultiDoku(doku);
|
||||||
MultiDoku otherDoku = null;
|
MultiDoku otherDoku = null;
|
||||||
try {
|
try {
|
||||||
otherDoku = SudokuFactory.fromfile(savePath);
|
otherDoku = SudokuFactory.fromfile(savePath);
|
||||||
assert (otherDoku != null);
|
assert (otherDoku != null);
|
||||||
|
|
||||||
assertEquals(SudokuSerializer.serializeSudoku(doku).toString(), SudokuSerializer.serializeSudoku(otherDoku).toString());
|
assertEquals(SudokuSerializer.serializeSudoku(doku).toString(),
|
||||||
|
SudokuSerializer.serializeSudoku(otherDoku).toString());
|
||||||
// clean file after test
|
// clean file after test
|
||||||
File fileToDelete = new File(savePath);
|
File fileToDelete = new File(savePath);
|
||||||
fileToDelete.delete();
|
fileToDelete.delete();
|
||||||
@@ -47,7 +48,7 @@ public class SudokuSerializerTest {
|
|||||||
|
|
||||||
void testSerializeX(int size) {
|
void testSerializeX(int size) {
|
||||||
var sudoku = SudokuFactory.createBasicXShapedMultidoku(size, SudokuFactory.DEFAULT_CONSTRAINTS);
|
var sudoku = SudokuFactory.createBasicXShapedMultidoku(size, SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
Solver.randomSolve(sudoku, new Random());
|
new RandomSolver().solve(sudoku);
|
||||||
JSONObject data = SudokuSerializer.serializeSudoku(sudoku);
|
JSONObject data = SudokuSerializer.serializeSudoku(sudoku);
|
||||||
MultiDoku multiDoku = SudokuSerializer.deserializeSudoku(data);
|
MultiDoku multiDoku = SudokuSerializer.deserializeSudoku(data);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package sudoku.solver;
|
package sudoku.solver;
|
||||||
|
|
||||||
|
import gui.Symbols;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import sudoku.io.SudokuPrinter;
|
import sudoku.io.SudokuPrinter;
|
||||||
import sudoku.io.SudokuSerializer;
|
import sudoku.io.SudokuSerializer;
|
||||||
@@ -38,7 +39,8 @@ class SolverTest {
|
|||||||
|
|
||||||
assert (sudokuToTest.setImmutableCellsSymbol(immutableCells));
|
assert (sudokuToTest.setImmutableCellsSymbol(immutableCells));
|
||||||
|
|
||||||
SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3);
|
//SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3);
|
||||||
|
SudokuPrinter.printMultiDoku(dokuToTest, 3, 3, Symbols.Numbers);
|
||||||
|
|
||||||
List<Integer> correctCells = List.of(7, 6, 0, 3, 4, 2, 8, 5, 1,
|
List<Integer> correctCells = List.of(7, 6, 0, 3, 4, 2, 8, 5, 1,
|
||||||
2, 3, 8, 1, 5, 6, 7, 0, 4,
|
2, 3, 8, 1, 5, 6, 7, 0, 4,
|
||||||
@@ -53,14 +55,15 @@ class SolverTest {
|
|||||||
sudokuResult.setCellsSymbol(correctCells);
|
sudokuResult.setCellsSymbol(correctCells);
|
||||||
|
|
||||||
System.out.println("\n****************************Doku Control\n");
|
System.out.println("\n****************************Doku Control\n");
|
||||||
SudokuPrinter.printRectangleSudoku(sudokuResult, 3, 3);
|
SudokuPrinter.printRectangleSudoku(sudokuResult, 3, 3, Symbols.Russian);
|
||||||
|
|
||||||
assert (dokuResult.isSolved());
|
assert (dokuResult.isSolved());
|
||||||
|
|
||||||
Solver.randomSolve(dokuToTest, rand);
|
new RandomSolver().solve(dokuToTest);
|
||||||
|
|
||||||
System.out.println("\n****************************\nDoku solved");
|
System.out.println("\n****************************\nDoku solved");
|
||||||
SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3);
|
//SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3);
|
||||||
|
SudokuPrinter.printMultiDoku(dokuToTest, 3, 3, Symbols.Emojis);
|
||||||
|
|
||||||
assert (dokuToTest.isSolved());
|
assert (dokuToTest.isSolved());
|
||||||
|
|
||||||
@@ -89,14 +92,15 @@ class SolverTest {
|
|||||||
5, ns, ns, ns, 3, 1, 0, ns, ns);
|
5, ns, ns, ns, 3, 1, 0, ns, ns);
|
||||||
sudokuToTest2.setImmutableCellsSymbol(immutableCells2);
|
sudokuToTest2.setImmutableCellsSymbol(immutableCells2);
|
||||||
|
|
||||||
boolean isSolved = Solver.randomSolve(dokuToTest2, rand);
|
boolean isSolved = new RandomSolver().solve(dokuToTest2);
|
||||||
|
|
||||||
assert (!isSolved);
|
assert (!isSolved);
|
||||||
|
|
||||||
MultiDoku dokuToTest3 = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
|
MultiDoku dokuToTest3 = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
|
|
||||||
Solver.randomSolve(dokuToTest3, rand);
|
new RandomSolver().solve(dokuToTest3);
|
||||||
|
|
||||||
SudokuPrinter.printRectangleSudoku(dokuToTest3.getSubGrid(0), 3, 3);
|
//SudokuPrinter.printRectangleSudoku(dokuToTest3.getSubGrid(0), 3, 3);
|
||||||
|
SudokuPrinter.printMultiDoku(dokuToTest3, 3, 3, Symbols.Letters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user