Add aspirinV2: fixed-cell 15-puzzle

Cells never move; each NumericSelectLabel shows the number it holds
on white and the empty cell takes the board colour via a linked
backgroundColor. Adds the V2 generator, build step (with a direct-Java
sedonac fallback), offline verifier mode (--v2) and viewer support.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-17 19:14:16 +03:00
co-authored by Claude Opus 5.5
parent 0728acd99a
commit d1f4e87526
8 changed files with 3242 additions and 10 deletions
+24 -3
View File
@@ -15,6 +15,8 @@ public class OxViewer extends JPanel {
SoxClient client;
SoxComponent page;
SoxComponent[] tiles = new SoxComponent[16];
SoxComponent[] cells = new SoxComponent[17]; // aspirinV2: fixed cells Cell01..Cell16
boolean v2;
SoxComponent[] labels = new SoxComponent[LABELS.length];
String lastBoard = "";
@@ -22,7 +24,9 @@ public class OxViewer extends JPanel {
this.client = client;
SoxComponent app = client.loadApp();
page = load(app, "ox", "Game");
for (int t = 1; t <= 15; t++) tiles[t] = load(page, String.format("Tile%02d", t));
v2 = load(page, "Cell01") != null;
if (v2) for (int k = 1; k <= 16; k++) cells[k] = load(page, String.format("Cell%02d", k));
else for (int t = 1; t <= 15; t++) tiles[t] = load(page, String.format("Tile%02d", t));
for (int i = 0; i < LABELS.length; i++) labels[i] = load(page, LABELS[i]);
setPreferredSize(new Dimension(800, 480));
}
@@ -40,6 +44,7 @@ public class OxViewer extends JPanel {
void poll() throws Exception {
for (SoxComponent c : tiles) if (c != null) client.update(c, 0x06);
for (SoxComponent c : cells) if (c != null) client.update(c, 0x06);
for (SoxComponent c : labels) if (c != null) client.update(c, 0x06);
String b = boardText();
if (!b.equals(lastBoard)) {
@@ -77,12 +82,19 @@ public class OxViewer extends JPanel {
String boardText() {
String[] cells = new String[16];
java.util.Arrays.fill(cells, " .");
for (int t = 1; t <= 15; t++) {
int whites = 0;
if (v2) for (int k = 1; k <= 16; k++) {
int n = i(this.cells[k], "in");
if (n != 0) cells[k - 1] = String.format("%2d", n);
if ((i(this.cells[k], "backgroundColor") & 0xFFFF) == 0xFFFF) whites++;
}
else for (int t = 1; t <= 15; t++) {
int col = (i(tiles[t], "x") - 40) / 105, row = (i(tiles[t], "y") - 40) / 105;
if (col >= 0 && col < 4 && row >= 0 && row < 4) cells[row * 4 + col] = String.format("%2d", t);
}
StringBuilder sb = new StringBuilder();
for (int r = 0; r < 4; r++) sb.append(r == 0 ? "" : " | ").append(String.join(" ", java.util.Arrays.copyOfRange(cells, r * 4, r * 4 + 4)));
if (v2) sb.append(" white=").append(whites);
return sb.toString();
}
@@ -102,7 +114,13 @@ public class OxViewer extends JPanel {
g.fillRect(0, 0, 800, 480);
g.setColor(c565(0x31A6));
g.fillRect(35, 35, 425, 425);
for (int t = 1; t <= 15; t++) {
if (v2) for (int k = 1; k <= 16; k++) {
SoxComponent w = cells[k];
String[] opts = str(w, "text").split(";");
int n = i(w, "in");
box(g, w, n < opts.length ? opts[n].trim() : "", i(w, "textColor"), i(w, "backgroundColor"), true, true);
}
else for (int t = 1; t <= 15; t++) {
SoxComponent w = tiles[t];
box(g, w, str(w, "text"), i(w, "textColor"), i(w, "backgroundColor"), true, true);
}
@@ -140,6 +158,9 @@ public class OxViewer extends JPanel {
Thread.sleep(500);
}
}
if (GraphicsEnvironment.isHeadless()) { // -Djava.awt.headless=true: console log only
while (true) { v.poll(); Thread.sleep(200); }
}
JFrame f = new JFrame("Orion Screen - aspirin (live via SOX " + host + ":" + port + ")");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(v);