-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPipsGame.java
More file actions
216 lines (189 loc) · 7.12 KB
/
PipsGame.java
File metadata and controls
216 lines (189 loc) · 7.12 KB
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
import java.util.*;
import static processing.core.PApplet.max;
/**
* Manages the core state and logic for the Pips puzzle game.
* Tracks grid layout, regional constraints, and the available supply of dominoes.
*/
public class PipsGame {
// Identity and Metadata
public int id;
public String backendId;
public String[] constructors;
public String JSONSolution = "";
// Global Pip Supply Tracking
public int activePipsMask = 0; // Bitmask representing which pip values are still available
public long totalPipSum = 0; // Running sum of all available pip values
int[] pipSupply = new int[PipsConstants.MAX_PIP + 1]; // Count of each specific pip value
// Global Constraint Tracking
public int remainingSumSlots; // Total empty slots across all 'SUM' type regions
public int remainingSumTarget; // The total remaining value needed to satisfy all 'SUM' regions
// Grid Components
int rows;
int cols;
Cell[][] cells;
ArrayList<Region> regions = new ArrayList<>();
ArrayList<Domino> dominoes = new ArrayList<>();
// -------------------------------------------------------------------------
// Initialization Logic
// -------------------------------------------------------------------------
/**
* Builds the 2D grid array based on the cells contained within the regions.
*/
void finaliseGrid() {
int maxR = 0, maxC = 0;
for (Region r : regions) {
for (Cell c : r.cells) {
maxR = max(maxR, c.r);
maxC = max(maxC, c.c);
}
}
rows = maxR + 1;
cols = maxC + 1;
cells = new Cell[rows][cols];
for (Region r : regions) {
for (Cell c : r.cells) {
cells[c.r][c.c] = c;
}
}
}
/**
* Populates the initial pip supply based on the list of available dominoes.
*/
void initialSupply() {
Arrays.fill(pipSupply, 0);
activePipsMask = 0;
totalPipSum = 0;
for (Domino d : this.dominoes) {
addPipToTracking(d.a);
addPipToTracking(d.b);
}
}
private void addPipToTracking(int v) {
if (pipSupply[v] == 0) {
activePipsMask |= (1 << v); // Set bit if this is the first pip of this value
}
pipSupply[v]++;
totalPipSum += v;
}
private void removePipFromTracking(int v) {
pipSupply[v]--;
if (pipSupply[v] == 0) {
activePipsMask &= ~(1 << v); // Clear bit if no pips of this value remain
}
totalPipSum -= v;
}
/**
* Resets the regional states and calculates the global sum targets.
*/
void initialSumState() {
remainingSumSlots = 0;
remainingSumTarget = 0;
for (Region r : regions) {
r.remainingSlots = r.cells.size();
r.remainingTarget = r.target;
r.filledCount = 0;
r.requiredValue = -1;
if (r.constraintTypeId == PipsConstants.TYPE_SUM) {
remainingSumSlots += r.remainingSlots;
remainingSumTarget += r.remainingTarget;
}
}
}
// -------------------------------------------------------------------------
// Solver Support Methods (Apply/Undo)
// -------------------------------------------------------------------------
/**
* Updates game state when a domino is placed on the grid.
* @param p The placement details containing cells, values, and regions.
*/
public void applyPlacement(DominoPlacement p) {
// 1. Remove used pips from the global supply
removePipFromTracking(p.v1);
removePipFromTracking(p.v2);
Region r1 = p.region1;
Region r2 = p.region2;
// 2. Update Cell 1 and its Region
p.cell1.value = p.v1;
if (r1.constraintTypeId == PipsConstants.TYPE_SUM) {
this.remainingSumSlots--;
this.remainingSumTarget -= p.v1;
}
r1.remainingSlots--;
r1.remainingTarget -= p.v1;
r1.filledCount++;
// If 'ALL EQUAL' constraint, the first placement defines the required value
if (r1.constraintTypeId == PipsConstants.TYPE_ALL_EQUAL && r1.filledCount == 1) {
r1.requiredValue = p.v1;
}
// 3. Update Cell 2 and its Region
p.cell2.value = p.v2;
if (p.sameRegion) {
// Optimization: Avoid double-fetching region if domino is entirely within one region
if (r1.constraintTypeId == PipsConstants.TYPE_SUM) {
this.remainingSumSlots--;
this.remainingSumTarget -= p.v2;
}
r1.remainingSlots--;
r1.remainingTarget -= p.v2;
r1.filledCount++;
} else {
// Update the second region independently
if (r2.constraintTypeId == PipsConstants.TYPE_SUM) {
this.remainingSumSlots--;
this.remainingSumTarget -= p.v2;
}
r2.remainingSlots--;
r2.remainingTarget -= p.v2;
r2.filledCount++;
if (r2.constraintTypeId == PipsConstants.TYPE_ALL_EQUAL && r2.filledCount == 1) {
r2.requiredValue = p.v2;
}
}
}
/**
* Reverts game state to the point before a specific placement.
* Used for backtracking in the solver.
*/
public void undoPlacement(DominoPlacement p) {
// 1. Restore pips to the global supply
addPipToTracking(p.v1);
addPipToTracking(p.v2);
Region r1 = p.region1;
Region r2 = p.region2;
// 2. Revert Cell 2 and its Region
if (p.sameRegion) {
if (r1.constraintTypeId == PipsConstants.TYPE_SUM) {
this.remainingSumSlots++;
this.remainingSumTarget += p.v2;
}
r1.remainingSlots++;
r1.remainingTarget += p.v2;
r1.filledCount--;
} else {
if (r2.constraintTypeId == PipsConstants.TYPE_SUM) {
this.remainingSumSlots++;
this.remainingSumTarget += p.v2;
}
r2.remainingSlots++;
r2.remainingTarget += p.v2;
r2.filledCount--;
// If region becomes empty, reset the required value for 'ALL EQUAL'
if (r2.constraintTypeId == PipsConstants.TYPE_ALL_EQUAL && r2.filledCount == 0) {
r2.requiredValue = -1;
}
}
p.cell2.value = -1;
// 3. Revert Cell 1 and its Region
if (r1.constraintTypeId == PipsConstants.TYPE_SUM) {
this.remainingSumSlots++;
this.remainingSumTarget += p.v1;
}
r1.remainingSlots++;
r1.remainingTarget += p.v1;
r1.filledCount--;
if (r1.constraintTypeId == PipsConstants.TYPE_ALL_EQUAL && r1.filledCount == 0) {
r1.requiredValue = -1;
}
p.cell1.value = -1;
}
}