Skip to content

Commit 64a38f3

Browse files
committed
Extended the logic in GapsAroundBiggestSolverMethod to also mark clues as solved.
1 parent 73d4790 commit 64a38f3

File tree

7 files changed

+189
-74
lines changed

7 files changed

+189
-74
lines changed

FinModelUtility/Fin/Fin.Picross Tests/CluesTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ public void TestAllEmpty() {
1616
for (var x = 0; x < picrossDefinition.Width; x++) {
1717
var column = columns[x];
1818
Assert.AreEqual(1, column.Count);
19-
Assert.AreEqual(0, column[0]);
19+
Assert.AreEqual(0, column[0].Length);
2020
}
2121

2222
var rows = clues.Rows;
2323
Assert.AreEqual(picrossDefinition.Height, rows.Count);
2424
for (var y = 0; y < picrossDefinition.Height; y++) {
2525
var row = rows[y];
2626
Assert.AreEqual(1, row.Count);
27-
Assert.AreEqual(0, row[0]);
27+
Assert.AreEqual(0, row[0].Length);
2828
}
2929
}
3030

@@ -40,15 +40,15 @@ public void TestAllFull() {
4040
for (var x = 0; x < picrossDefinition.Width; x++) {
4141
var column = columns[x];
4242
Assert.AreEqual(1, column.Count);
43-
Assert.AreEqual(picrossDefinition.Height, column[0]);
43+
Assert.AreEqual(picrossDefinition.Height, column[0].Length);
4444
}
4545

4646
var rows = clues.Rows;
4747
Assert.AreEqual(picrossDefinition.Height, rows.Count);
4848
for (var y = 0; y < picrossDefinition.Height; y++) {
4949
var row = rows[y];
5050
Assert.AreEqual(1, row.Count);
51-
Assert.AreEqual(picrossDefinition.Width, row[0]);
51+
Assert.AreEqual(picrossDefinition.Width, row[0].Length);
5252
}
5353
}
5454
}

FinModelUtility/Fin/Fin.Picross/src/PicrossSolver.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,10 @@ private static readonly IReadOnlyList<IPicrossSolverMethod> SOLVER_METHODS_
9999
new ExtendLastClueSolverMethod(),
100100
new FillSmallestUnknownsBetweenEmptiesSolverMethod(),
101101
new GapsAroundFirstClueSolverMethod(),
102-
new GapsAroundBiggestSolverMethod(),
102+
new GapsAroundKnownCluesSolverMethod(),
103103
new GapsBetweenNeighboringCluesSolverMethod(),
104104
new GapsBetweenNeighboringShortCluesSolverMethod(),
105+
new MatchingBiggestOrUniqueLengthSolverMethod(),
105106
];
106107

107108
private static IEnumerable<IPicrossMove1d> CheckClues_(

FinModelUtility/Fin/Fin.Picross/src/moves/ClueMoves.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public enum PicrossClueMoveSource {
88
FREEBIE_PERFECT_FIT,
99
FIRST_CLUE,
1010
ALL_CLUES_SOLVED,
11+
ONLY_MATCHING_CLUE,
1112
}
1213

1314
[Equatable]

FinModelUtility/Fin/Fin.Picross/src/solver/GapsAroundBiggestSolverMethod.cs

Lines changed: 0 additions & 49 deletions
This file was deleted.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using fin.picross.moves;
2+
3+
namespace fin.picross.solver;
4+
5+
public class GapsAroundKnownCluesSolverMethod : IPicrossSolverMethod {
6+
public IEnumerable<IPicrossMove1d> TryToFindMoves(
7+
IPicrossLineState lineState) {
8+
var clueStates = lineState.ClueStates;
9+
var cellStates = lineState.CellStates;
10+
var length = cellStates.Count;
11+
12+
foreach (var clueState in clueStates) {
13+
if (!clueState.Used) {
14+
continue;
15+
}
16+
17+
var startI = clueState.StartIndex.Value;
18+
var beforeI = startI - 1;
19+
var afterI = startI + clueState.Length;
20+
21+
if (beforeI >= 0 &&
22+
cellStates[beforeI].Status == PicrossCellStatus.UNKNOWN) {
23+
yield return new PicrossCellMove1d(
24+
PicrossCellMoveType.MARK_EMPTY,
25+
PicrossCellMoveSource.EMPTY_AROUND_KNOWN_CLUE,
26+
beforeI);
27+
}
28+
29+
if (afterI < length &&
30+
cellStates[afterI].Status == PicrossCellStatus.UNKNOWN) {
31+
yield return new PicrossCellMove1d(
32+
PicrossCellMoveType.MARK_EMPTY,
33+
PicrossCellMoveSource.EMPTY_AROUND_KNOWN_CLUE,
34+
afterI);
35+
}
36+
}
37+
}
38+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using fin.picross.moves;
2+
using fin.util.enumerables;
3+
4+
namespace fin.picross.solver;
5+
6+
public class MatchingBiggestOrUniqueLengthSolverMethod : IPicrossSolverMethod {
7+
public IEnumerable<IPicrossMove1d> TryToFindMoves(
8+
IPicrossLineState lineState) {
9+
var clueStates = lineState.ClueStates;
10+
var cellStates = lineState.CellStates;
11+
var length = cellStates.Count;
12+
13+
var biggestUnusedLength = clueStates.Where(c => !c.Used).Max(c => c.Length);
14+
15+
int startIndex = -1;
16+
var inClue = false;
17+
18+
for (var i = 0; i < length; ++i) {
19+
var cell = cellStates[i].Status == PicrossCellStatus.KNOWN_FILLED;
20+
21+
if (cell && !inClue) {
22+
startIndex = i;
23+
}
24+
25+
if (!cell && inClue) {
26+
var clueLength = i - startIndex;
27+
28+
var beforeI = startIndex - 1;
29+
var afterI = i;
30+
31+
foreach (var move in CheckIfMatchingClue_(
32+
clueStates,
33+
biggestUnusedLength,
34+
startIndex,
35+
clueLength,
36+
beforeI < 0 ||
37+
cellStates[beforeI].Status ==
38+
PicrossCellStatus.KNOWN_EMPTY,
39+
cellStates[afterI].Status ==
40+
PicrossCellStatus.KNOWN_EMPTY)) {
41+
yield return move;
42+
}
43+
}
44+
45+
inClue = cell;
46+
}
47+
48+
if (inClue) {
49+
var clueLength = length - startIndex;
50+
var beforeI = startIndex - 1;
51+
foreach (var move in CheckIfMatchingClue_(
52+
clueStates,
53+
biggestUnusedLength,
54+
startIndex,
55+
clueLength,
56+
cellStates[beforeI].Status == PicrossCellStatus.KNOWN_EMPTY,
57+
true)) {
58+
yield return move;
59+
}
60+
}
61+
}
62+
63+
private static IEnumerable<IPicrossMove1d> CheckIfMatchingClue_(
64+
IReadOnlyList<IReadOnlyPicrossClueState> clueStates,
65+
int biggestUnusedClueLength,
66+
int startI,
67+
int clueLength,
68+
bool knownEmptyBefore,
69+
bool knownEmptyAfter) {
70+
var firstClueWithExactLength
71+
= clueStates.Where(c => !c.Used)
72+
.FirstOrDefaultAndCount(c => c.Length == clueLength,
73+
out var matchingClueCount);
74+
if (firstClueWithExactLength == null) {
75+
yield break;
76+
}
77+
78+
var isBiggest = clueLength == biggestUnusedClueLength;
79+
80+
if (matchingClueCount == 1 &&
81+
(isBiggest || (knownEmptyBefore && knownEmptyAfter))) {
82+
yield return new PicrossClueMove(
83+
PicrossClueMoveSource.ONLY_MATCHING_CLUE,
84+
firstClueWithExactLength.Clue,
85+
startI);
86+
}
87+
88+
if (isBiggest) {
89+
if (!knownEmptyBefore) {
90+
yield return new PicrossCellMove1d(
91+
PicrossCellMoveType.MARK_EMPTY,
92+
PicrossCellMoveSource.EMPTY_AROUND_KNOWN_CLUE,
93+
startI - 1);
94+
}
95+
96+
if (!knownEmptyAfter) {
97+
yield return new PicrossCellMove1d(
98+
PicrossCellMoveType.MARK_EMPTY,
99+
PicrossCellMoveSource.EMPTY_AROUND_KNOWN_CLUE,
100+
startI + clueLength);
101+
}
102+
}
103+
}
104+
}

FinModelUtility/Fin/Fin/src/util/enumerables/EnumerableExtensions.cs

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -131,30 +131,50 @@ public static void AddTo<T>(this IEnumerable<T> src,
131131

132132

133133
public static bool SequenceEqualOrBothEmpty<T>(
134-
this IEnumerable<T>? lhs,
135-
IEnumerable<T>? rhs) {
136-
if (lhs == null && rhs == null) {
137-
return true;
134+
this IEnumerable<T>? lhs,
135+
IEnumerable<T>? rhs) {
136+
if (lhs == null && rhs == null) {
137+
return true;
138+
}
139+
140+
return (lhs ?? []).SequenceEqual(rhs ?? []);
141+
}
142+
143+
public static IEnumerable<T[]> SplitByNull<T>(
144+
this IEnumerable<Nullable<T>> impl) where T : struct {
145+
var current = new LinkedList<T>();
146+
foreach (var value in impl) {
147+
if (value == null) {
148+
yield return current.ToArray();
149+
current.Clear();
150+
continue;
138151
}
139152

140-
return (lhs ?? []).SequenceEqual(rhs ?? []);
153+
current.AddLast(value.Value);
141154
}
142155

143-
public static IEnumerable<T[]> SplitByNull<T>(
144-
this IEnumerable<Nullable<T>> impl) where T : struct {
145-
var current = new LinkedList<T>();
146-
foreach (var value in impl) {
147-
if (value == null) {
148-
yield return current.ToArray();
149-
current.Clear();
150-
continue;
151-
}
152-
153-
current.AddLast(value.Value);
154-
}
156+
yield return current.ToArray();
157+
}
155158

156-
yield return current.ToArray();
159+
public static bool AnyTrue(this IEnumerable<bool> impl) => impl.Any(b => b);
160+
161+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
162+
public static T? FirstOrDefaultAndCount<T>(this IEnumerable<T> enumerable,
163+
Func<T, bool> selector,
164+
out int count)
165+
=> enumerable.Where(selector).FirstOrDefaultAndCount(out count);
166+
167+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
168+
public static T? FirstOrDefaultAndCount<T>(this IEnumerable<T> enumerable,
169+
out int count) {
170+
T? firstValue = default!;
171+
count = 0;
172+
foreach (var value in enumerable) {
173+
if (count++ == 0) {
174+
firstValue = value;
175+
}
157176
}
158177

159-
public static bool AnyTrue(this IEnumerable<bool> impl) => impl.Any(b => b);
160-
}
178+
return firstValue;
179+
}
180+
}

0 commit comments

Comments
 (0)