Skip to content

Commit b559893

Browse files
committed
Pulled out the gap filling logic from LastClueSolverMethod and used it in another solver method for when there's 2 clues each in their own gap.
1 parent 958873b commit b559893

File tree

4 files changed

+220
-120
lines changed

4 files changed

+220
-120
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ private static readonly IReadOnlyList<IPicrossSolverMethod> SOLVER_METHODS_
132132
new GapsBetweenNeighboringShortCluesSolverMethod(),
133133
new LastClueSolverMethod(),
134134
new MatchingBiggestOrUniqueLengthSolverMethod(),
135+
new TwoSeparatedCluesSolverMethod(),
135136
];
136137

137138
private static IEnumerable<IPicrossMove1d> CheckClues_(

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

Lines changed: 6 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -18,126 +18,12 @@ var lastUnsolvedClue
1818
var cellStates = lineState.CellStates;
1919
var length = cellStates.Count;
2020

21-
int? firstUnclaimedFilledCellIndex = null;
22-
int? lastUnclaimedFilledCellIndex = null;
23-
24-
// Find first and last unclaimed filled cells in row
25-
for (var i = 0; i < length; ++i) {
26-
var cellState = cellStates[i];
27-
var isUnclaimed = cellState.Status == PicrossCellStatus.KNOWN_FILLED &&
28-
(lineState.IsColumn
29-
? cellState.ColumnClue == null
30-
: cellState.RowClue == null);
31-
32-
if (!isUnclaimed) {
33-
continue;
34-
}
35-
36-
firstUnclaimedFilledCellIndex ??= i;
37-
lastUnclaimedFilledCellIndex = i;
38-
}
39-
40-
// If there are no unclaimed cells, there's nothing we can do
41-
if (firstUnclaimedFilledCellIndex == null ||
42-
lastUnclaimedFilledCellIndex == null) {
43-
yield break;
44-
}
45-
46-
// Fill in any cells between the first and last filled unclaimed cell
47-
for (var i = firstUnclaimedFilledCellIndex.Value + 1;
48-
i < lastUnclaimedFilledCellIndex.Value;
49-
++i) {
50-
if (cellStates[i].Status == PicrossCellStatus.UNKNOWN) {
51-
yield return new PicrossCellMove1d(
52-
PicrossCellMoveType.MARK_FILLED,
53-
PicrossCellMoveSource.WITHIN_LAST_CLUE,
54-
i);
55-
}
56-
}
57-
58-
// Get the total accounted for length of the clue
59-
var clueLength = lastUnsolvedClue.Length;
60-
var filledLength = lastUnclaimedFilledCellIndex.Value -
61-
firstUnclaimedFilledCellIndex.Value +
62-
1;
63-
64-
// If it's already the full length, we're done!
65-
if (clueLength == filledLength) {
66-
yield break;
67-
}
68-
69-
// Otherwise, we need to fill in cells that are too far away
70-
var remainingClueLength = clueLength - filledLength;
71-
72-
var remainingClueLengthBefore = remainingClueLength;
73-
var knownCellsAfter = 0;
74-
for (var i = firstUnclaimedFilledCellIndex.Value - 1; i >= 0; --i) {
75-
var cellStatus = cellStates[i].Status;
76-
if (cellStatus == PicrossCellStatus.UNKNOWN) {
77-
if (remainingClueLengthBefore-- <= 0) {
78-
yield return new PicrossCellMove1d(
79-
PicrossCellMoveType.MARK_EMPTY,
80-
PicrossCellMoveSource.TOO_FAR_FROM_KNOWN,
81-
i);
82-
}
83-
} else {
84-
if (remainingClueLengthBefore > 0) {
85-
knownCellsAfter = remainingClueLengthBefore;
86-
}
87-
88-
remainingClueLengthBefore = 0;
89-
}
90-
}
91-
92-
if (remainingClueLengthBefore > 0) {
93-
knownCellsAfter = remainingClueLengthBefore;
94-
}
95-
96-
97-
var remainingClueLengthAfter = remainingClueLength;
98-
var knownCellsBefore = 0;
99-
for (var i = lastUnclaimedFilledCellIndex.Value + 1; i < length; ++i) {
100-
var cellStatus = cellStates[i].Status;
101-
if (cellStatus == PicrossCellStatus.UNKNOWN) {
102-
if (remainingClueLengthAfter-- <= 0) {
103-
yield return new PicrossCellMove1d(
104-
PicrossCellMoveType.MARK_EMPTY,
105-
PicrossCellMoveSource.TOO_FAR_FROM_KNOWN,
106-
i);
107-
}
108-
} else {
109-
if (remainingClueLengthAfter > 0) {
110-
knownCellsBefore = remainingClueLengthAfter;
111-
}
112-
113-
remainingClueLengthAfter = 0;
114-
}
115-
}
116-
117-
if (remainingClueLengthAfter > 0) {
118-
knownCellsBefore = remainingClueLengthAfter;
119-
}
120-
121-
// If not enough gap before/after, we can fill cells in on the other side
122-
for (var ii = 0; ii < knownCellsBefore; ++ii) {
123-
var i = firstUnclaimedFilledCellIndex.Value - 1 - ii;
124-
if (cellStates[i].Status == PicrossCellStatus.UNKNOWN) {
125-
yield return new PicrossCellMove1d(
126-
PicrossCellMoveType.MARK_FILLED,
127-
PicrossCellMoveSource.NOWHERE_ELSE_TO_GO,
128-
i);
129-
}
130-
}
131-
132-
for (var ii = 0; ii < knownCellsAfter; ++ii) {
133-
var i = lastUnclaimedFilledCellIndex.Value + 1 + ii;
134-
var cellStatus = cellStates[i].Status;
135-
if (cellStatus == PicrossCellStatus.UNKNOWN) {
136-
yield return new PicrossCellMove1d(
137-
PicrossCellMoveType.MARK_FILLED,
138-
PicrossCellMoveSource.NOWHERE_ELSE_TO_GO,
139-
i);
140-
}
21+
foreach (var move in PicrossMultiMoveUtil.AlignGapToClue(
22+
lineState,
23+
lastUnsolvedClue,
24+
0,
25+
length)) {
26+
yield return move;
14127
}
14228
}
14329
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
using fin.picross.moves;
2+
3+
namespace fin.picross.solver;
4+
5+
public static class PicrossMultiMoveUtil {
6+
public static IEnumerable<IPicrossMove1d> AlignGapToClue(
7+
IPicrossLineState lineState,
8+
IReadOnlyPicrossClueState clueState,
9+
int startIndex,
10+
int length) {
11+
var cellStates = lineState.CellStates;
12+
13+
// Find first and last unclaimed filled cells in row
14+
int? firstUnclaimedFilledCellIndex = null;
15+
int? lastUnclaimedFilledCellIndex = null;
16+
for (var i = startIndex; i < length; ++i) {
17+
var cellState = cellStates[i];
18+
var isUnclaimed = cellState.Status == PicrossCellStatus.KNOWN_FILLED &&
19+
(lineState.IsColumn
20+
? cellState.ColumnClue == null
21+
: cellState.RowClue == null);
22+
23+
if (!isUnclaimed) {
24+
continue;
25+
}
26+
27+
firstUnclaimedFilledCellIndex ??= i;
28+
lastUnclaimedFilledCellIndex = i;
29+
}
30+
31+
// If there are no unclaimed cells, there's nothing we can do
32+
if (firstUnclaimedFilledCellIndex == null ||
33+
lastUnclaimedFilledCellIndex == null) {
34+
yield break;
35+
}
36+
37+
// Fill in any cells between the first and last filled unclaimed cell
38+
for (var i = firstUnclaimedFilledCellIndex.Value + 1;
39+
i < lastUnclaimedFilledCellIndex.Value;
40+
++i) {
41+
if (cellStates[i].Status == PicrossCellStatus.UNKNOWN) {
42+
yield return new PicrossCellMove1d(
43+
PicrossCellMoveType.MARK_FILLED,
44+
PicrossCellMoveSource.WITHIN_LAST_CLUE,
45+
i);
46+
}
47+
}
48+
49+
// Get the total accounted for length of the clue
50+
var clueLength = clueState.Length;
51+
var filledLength = lastUnclaimedFilledCellIndex.Value -
52+
firstUnclaimedFilledCellIndex.Value +
53+
1;
54+
55+
// If it's already the full length, we're done!
56+
if (clueLength == filledLength) {
57+
yield break;
58+
}
59+
60+
// Otherwise, we need to fill in cells that are too far away
61+
var remainingClueLength = clueLength - filledLength;
62+
63+
var remainingClueLengthBefore = remainingClueLength;
64+
var knownCellsAfter = 0;
65+
for (var i = firstUnclaimedFilledCellIndex.Value - 1;
66+
i >= startIndex;
67+
--i) {
68+
var cellStatus = cellStates[i].Status;
69+
if (cellStatus == PicrossCellStatus.UNKNOWN) {
70+
if (remainingClueLengthBefore-- <= 0) {
71+
yield return new PicrossCellMove1d(
72+
PicrossCellMoveType.MARK_EMPTY,
73+
PicrossCellMoveSource.TOO_FAR_FROM_KNOWN,
74+
i);
75+
}
76+
} else {
77+
if (remainingClueLengthBefore > 0) {
78+
knownCellsAfter = remainingClueLengthBefore;
79+
}
80+
81+
remainingClueLengthBefore = 0;
82+
}
83+
}
84+
85+
if (remainingClueLengthBefore > 0) {
86+
knownCellsAfter = remainingClueLengthBefore;
87+
}
88+
89+
var remainingClueLengthAfter = remainingClueLength;
90+
var knownCellsBefore = 0;
91+
for (var i = lastUnclaimedFilledCellIndex.Value + 1; i < length; ++i) {
92+
var cellStatus = cellStates[i].Status;
93+
if (cellStatus == PicrossCellStatus.UNKNOWN) {
94+
if (remainingClueLengthAfter-- <= 0) {
95+
yield return new PicrossCellMove1d(
96+
PicrossCellMoveType.MARK_EMPTY,
97+
PicrossCellMoveSource.TOO_FAR_FROM_KNOWN,
98+
i);
99+
}
100+
} else {
101+
if (remainingClueLengthAfter > 0) {
102+
knownCellsBefore = remainingClueLengthAfter;
103+
}
104+
105+
remainingClueLengthAfter = 0;
106+
}
107+
}
108+
109+
if (remainingClueLengthAfter > 0) {
110+
knownCellsBefore = remainingClueLengthAfter;
111+
}
112+
113+
// If not enough gap before/after, we can fill cells in on the other side
114+
for (var ii = 0; ii < knownCellsBefore; ++ii) {
115+
var i = firstUnclaimedFilledCellIndex.Value - 1 - ii;
116+
if (cellStates[i].Status == PicrossCellStatus.UNKNOWN) {
117+
yield return new PicrossCellMove1d(
118+
PicrossCellMoveType.MARK_FILLED,
119+
PicrossCellMoveSource.NOWHERE_ELSE_TO_GO,
120+
i);
121+
}
122+
}
123+
124+
for (var ii = 0; ii < knownCellsAfter; ++ii) {
125+
var i = lastUnclaimedFilledCellIndex.Value + 1 + ii;
126+
var cellStatus = cellStates[i].Status;
127+
if (cellStatus == PicrossCellStatus.UNKNOWN) {
128+
yield return new PicrossCellMove1d(
129+
PicrossCellMoveType.MARK_FILLED,
130+
PicrossCellMoveSource.NOWHERE_ELSE_TO_GO,
131+
i);
132+
}
133+
}
134+
}
135+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using fin.picross.moves;
2+
3+
namespace fin.picross.solver;
4+
5+
public class TwoSeparatedCluesSolverMethod : IPicrossSolverMethod {
6+
public IEnumerable<IPicrossMove1d>
7+
TryToFindMoves(IPicrossLineState lineState) {
8+
var clueStates = lineState.ClueStates;
9+
if (clueStates is not [
10+
{ Solved: false } firstClue,
11+
{ Solved: false } secondClue
12+
]) {
13+
yield break;
14+
}
15+
16+
var cellStates = lineState.CellStates;
17+
var length = cellStates.Count;
18+
19+
// Find first and last unclaimed filled cells in row
20+
int? lastCellOfFirstClue = null;
21+
int? firstEmptyCellOfGap = null;
22+
int? lastEmptyCellOfGap = null;
23+
int? firstCellOfSecondClue = null;
24+
for (var i = 0; i < length; ++i) {
25+
var cellStatus = cellStates[i].Status;
26+
27+
if (lastEmptyCellOfGap == null &&
28+
cellStatus == PicrossCellStatus.KNOWN_FILLED) {
29+
lastCellOfFirstClue = i;
30+
}
31+
32+
if (lastCellOfFirstClue != null &&
33+
cellStatus == PicrossCellStatus.KNOWN_EMPTY) {
34+
firstEmptyCellOfGap ??= i;
35+
lastEmptyCellOfGap = i;
36+
}
37+
38+
if (lastEmptyCellOfGap != null &&
39+
cellStatus == PicrossCellStatus.KNOWN_FILLED) {
40+
firstCellOfSecondClue = i;
41+
break;
42+
}
43+
}
44+
45+
// If there are not two separate clues, there's nothing we can do.
46+
if (lastCellOfFirstClue == null || firstCellOfSecondClue == null) {
47+
yield break;
48+
}
49+
50+
// Mark cells within the gap as empty
51+
for (var i = firstEmptyCellOfGap.Value + 1;
52+
i < lastEmptyCellOfGap.Value;
53+
++i) {
54+
if (cellStates[i].Status == PicrossCellStatus.UNKNOWN) {
55+
yield return new PicrossCellMove1d(
56+
PicrossCellMoveType.MARK_EMPTY,
57+
PicrossCellMoveSource.EMPTY_BETWEEN_CLUES,
58+
i);
59+
}
60+
}
61+
62+
foreach (var move in PicrossMultiMoveUtil.AlignGapToClue(
63+
lineState,
64+
firstClue,
65+
0,
66+
firstEmptyCellOfGap.Value)) {
67+
yield return move;
68+
}
69+
70+
foreach (var move in PicrossMultiMoveUtil.AlignGapToClue(
71+
lineState,
72+
secondClue,
73+
lastEmptyCellOfGap.Value + 1,
74+
length)) {
75+
yield return move;
76+
}
77+
}
78+
}

0 commit comments

Comments
 (0)