From c2171807d7bd306fc4e1fcb6dd8ffdf443387b9b Mon Sep 17 00:00:00 2001 From: jbock Date: Sun, 4 Aug 2024 09:11:44 +0200 Subject: [PATCH] for each move, remember dead stones --- src/main/client/src/model/PointList.js | 18 +++++++ src/main/client/src/model/base.js | 54 ++++++++++--------- src/main/client/src/store.js | 11 +++- src/main/java/com/bernd/game/MoveList.java | 38 +++++++++---- .../java/com/bernd/model/CountingMove.java | 4 +- src/main/java/com/bernd/model/Game.java | 2 +- src/main/java/com/bernd/model/GameMove.java | 4 +- src/main/java/com/bernd/model/Move.java | 2 +- .../java/com/bernd/game/MoveListTest.java | 8 +-- 9 files changed, 95 insertions(+), 46 deletions(-) diff --git a/src/main/client/src/model/PointList.js b/src/main/client/src/model/PointList.js index aed7ddf..e335327 100644 --- a/src/main/client/src/model/PointList.js +++ b/src/main/client/src/model/PointList.js @@ -7,6 +7,13 @@ export class PointList { static LO = 0xffff static HI = 0xffff0000 + static empty() { + return { + size: 0, + forEach: () => {}, + } + } + constructor(dim) { if (!dim) { throw new Error("expecting argument: dim") @@ -29,6 +36,13 @@ export class PointList { this.pos++ } + addAll(other) { + if (!other) { + return + } + other.forEach((x, y) => this.add(x, y)) + } + #get(i) { let code = this.buffer[Math.trunc(i / 2)] return i % 2 === 0 ? code & PointList.LO : (code >> 16) @@ -57,6 +71,10 @@ export class PointList { return this.pos } + isEmpty() { + return !this.pos + } + toSet() { let result = new PointSet(this.dim) this.forEach((x, y) => result.add(x, y)) diff --git a/src/main/client/src/model/base.js b/src/main/client/src/model/base.js index 825d301..319b473 100644 --- a/src/main/client/src/model/base.js +++ b/src/main/client/src/model/base.js @@ -15,36 +15,47 @@ import { export function updateBoard(board, move) { let {pass, x, y, color} = move if (pass) { - return board + return [PointList.empty(), board] } board = applyMove(board, move) let oppositeColor = color ^ (WHITE | BLACK) - board = removeDeadGroup(board, x, y - 1, oppositeColor) - board = removeDeadGroup(board, x, y + 1, oppositeColor) - board = removeDeadGroup(board, x - 1, y, oppositeColor) - board = removeDeadGroup(board, x + 1, y, oppositeColor) - return board + let dead = new PointList(board.length) + dead.addAll(findDeadStones(board, x, y - 1, oppositeColor)) + dead.addAll(findDeadStones(board, x, y + 1, oppositeColor)) + dead.addAll(findDeadStones(board, x - 1, y, oppositeColor)) + dead.addAll(findDeadStones(board, x + 1, y, oppositeColor)) + if (dead.isEmpty()) { + return [PointList.empty(), board] + } + let updated = board.slice() + dead.forEach((x, y) => { + if (updated[y] === board[y]) { + updated[y] = board[y].slice() + } + updated[y][x] = 0 + }) + return [dead, updated] } -function removeDeadGroup(board, xx, yy, color) { +function findDeadStones(board, xx, yy, color) { let dim = board.length if (Math.min(xx, yy) < 0 || Math.max(xx, yy) >= dim) { - return board + return undefined } if (board[yy][xx] !== color) { - return board + return undefined } if (yy > 0 && board[yy - 1][xx] == 0) { - return board + return undefined } if (yy < dim - 1 && board[yy + 1][xx] == 0) { - return board + return undefined } if (xx > 0 && board[yy][xx - 1] == 0) { - return board + return undefined } if (xx < dim - 1 && board[yy][xx + 1] == 0) { - return board + return undefined } let acc = new PointList(dim) let pointsChecked = new PointSet(dim) @@ -59,7 +70,7 @@ function removeDeadGroup(board, xx, yy, color) { if (y > 0) { let bpt = board[y - 1][x] if (bpt === 0) { - return board + return undefined } else if (bpt === color && !pointsChecked.has(x, y - 1)) { pointsChecked.add(x, y - 1) pointsToCheck.offer(x, y - 1) @@ -68,7 +79,7 @@ function removeDeadGroup(board, xx, yy, color) { if (y < dim - 1) { let bpt = board[y + 1][x] if (bpt === 0) { - return board + return undefined } else if (bpt === color && !pointsChecked.has(x, y + 1)) { pointsChecked.add(x, y + 1) pointsToCheck.offer(x, y + 1) @@ -77,7 +88,7 @@ function removeDeadGroup(board, xx, yy, color) { if (x > 0) { let bpt = board[y][x - 1] if (bpt === 0) { - return board + return undefined } else if (bpt === color && !pointsChecked.has(x - 1, y)) { pointsChecked.add(x - 1, y) pointsToCheck.offer(x - 1, y) @@ -86,21 +97,14 @@ function removeDeadGroup(board, xx, yy, color) { if (x < dim - 1) { let bpt = board[y][x + 1] if (bpt === 0) { - return board + return undefined } else if (bpt === color && !pointsChecked.has(x + 1, y)) { pointsChecked.add(x + 1, y) pointsToCheck.offer(x + 1, y) } } } - let result = board.slice() - acc.forEach((x, y) => { - if (result[y] === board[y]) { - result[y] = board[y].slice() - } - result[y][x] = 0 - }) - return result + return acc } function applyMove(board, {color, x, y}) { diff --git a/src/main/client/src/store.js b/src/main/client/src/store.js index 59345da..986cd23 100644 --- a/src/main/client/src/store.js +++ b/src/main/client/src/store.js @@ -8,6 +8,9 @@ import { BLACK, WHITE, } from "./util.js" +import { + PointList, +} from "./model/PointList.js" import { rehydrate, } from "./model/board.js" @@ -61,19 +64,23 @@ export const useGameStore = create((set, get) => ({ }, addMove: (move) => { set(produce(state => { + if (move.n < get().moves.length) { + return + } if (get().moves.length < move.n) { state.queueStatus = "behind" return } state.queueStatus = "up_to_date" - state.moves.push(move) if (move.counting) { + state.moves.push({...move, dead: PointList.empty()}) state.gameState.counting = true state.baseBoard = move.board state.gameState.board = rehydrate(move.board) return } - let updated = updateBoard(get().baseBoard, move) + let [dead, updated] = updateBoard(get().baseBoard, move) + state.moves.push({...move, dead}) state.baseBoard = updated state.gameState.board = rehydrate(updated) state.gameState.currentColor = get().gameState.currentColor ^ (BLACK | WHITE) diff --git a/src/main/java/com/bernd/game/MoveList.java b/src/main/java/com/bernd/game/MoveList.java index 9bdeed8..0053408 100644 --- a/src/main/java/com/bernd/game/MoveList.java +++ b/src/main/java/com/bernd/game/MoveList.java @@ -2,7 +2,6 @@ import com.bernd.model.GameMove; import com.bernd.model.Move; - import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -15,6 +14,8 @@ public final class MoveList { private static final int HI = 0xffff0000; private static final int WHITE = 0x1000; private static final int PASS = 0x2000; + private static final int COUNTING = 0x4000; + private static final int GAME_END = 0x8000; private static final int DATA = 0x0fff; private int pos; @@ -38,13 +39,14 @@ public static MoveList create(int dim) { return new MoveList(dim, new int[16]); } - public void add(int color, Move move) { - if (pos >= capacity) { - int boardSize = dim * dim; - int newCapacity = capacity < boardSize ? boardSize : capacity + boardSize; - buffer = Arrays.copyOf(buffer, divUp(newCapacity, 2)); - capacity = newCapacity; - } + public void gameEnd() { + ensureCapacity(); + set(GAME_END); + pos++; + } + + public void add(int color, Move move, boolean counting) { + ensureCapacity(); int ptId; if (move.pass()) { ptId = PASS; @@ -54,21 +56,37 @@ public void add(int color, Move move) { if (color == Board.W) { ptId |= WHITE; } + if (counting) { + ptId |= COUNTING; + } set(ptId); pos++; } + private void ensureCapacity() { + if (pos >= capacity) { + int boardSize = dim * dim; + int newCapacity = capacity < boardSize ? boardSize : capacity + boardSize; + buffer = Arrays.copyOf(buffer, divUp(newCapacity, 2)); + capacity = newCapacity; + } + } + public GameMove get(int i) { int code = buffer[i / 2]; int ptId = i % 2 == 0 ? code & LO : (code >> 16); int color = (ptId & WHITE) != 0 ? Board.W : Board.B; + if ((ptId & GAME_END) != 0) { + return new GameMove(i, 0, true, -1, -1, true, true, new int[]{-1, -1}); + } + boolean counting = (ptId & COUNTING) != 0; if ((ptId & PASS) != 0) { - return new GameMove(i, color, true, -1, -1, new int[]{-1, -1}); + return new GameMove(i, color, true, -1, -1, counting, false, new int[]{-1, -1}); } else { int data = ptId & DATA; int x = data % dim; int y = data / dim; - return new GameMove(i, color, true, x, y, new int[]{-1, -1}); + return new GameMove(i, color, false, x, y, counting, false, new int[]{-1, -1}); } } diff --git a/src/main/java/com/bernd/model/CountingMove.java b/src/main/java/com/bernd/model/CountingMove.java index 1618846..48b3155 100644 --- a/src/main/java/com/bernd/model/CountingMove.java +++ b/src/main/java/com/bernd/model/CountingMove.java @@ -5,9 +5,9 @@ public record CountingMove( int color, boolean pass, boolean counting, - int [][] board) { + int[][] board) { public static CountingMove create(int color, int n, int[][] board) { return new CountingMove(n, color, true, true, board); } -} \ No newline at end of file +} diff --git a/src/main/java/com/bernd/model/Game.java b/src/main/java/com/bernd/model/Game.java index 2f64596..59a56d9 100644 --- a/src/main/java/com/bernd/model/Game.java +++ b/src/main/java/com/bernd/model/Game.java @@ -42,6 +42,7 @@ public Game update(Move move) { } private Game updateInternal(Move move) { + moves.add(currentColor, move, counting); if (counting) { if (move.resetCounting()) { int[][] resetted = Toggle.resetCounting(board); @@ -50,7 +51,6 @@ private Game updateInternal(Move move) { int[][] toggled = Toggle.toggleStonesAt(board, move.x(), move.y()); return game(Count.count(toggled), NOT_FORBIDDEN); } - moves.add(currentColor, move); if (move.pass()) { if (opponentPassed) { return startCounting(); diff --git a/src/main/java/com/bernd/model/GameMove.java b/src/main/java/com/bernd/model/GameMove.java index 72d866f..1350222 100644 --- a/src/main/java/com/bernd/model/GameMove.java +++ b/src/main/java/com/bernd/model/GameMove.java @@ -6,5 +6,7 @@ public record GameMove( boolean pass, int x, int y, + boolean counting, + boolean end, int[] forbidden) { -} \ No newline at end of file +} diff --git a/src/main/java/com/bernd/model/Move.java b/src/main/java/com/bernd/model/Move.java index 2adc353..d7fcae7 100644 --- a/src/main/java/com/bernd/model/Move.java +++ b/src/main/java/com/bernd/model/Move.java @@ -8,6 +8,6 @@ public record Move( int y) { public GameMove toView(int color, int moveNumber, int[] forbidden) { - return new GameMove(moveNumber, color, pass, x, y, forbidden); + return new GameMove(moveNumber, color, pass, x, y, false, false, forbidden); } } diff --git a/src/test/java/com/bernd/game/MoveListTest.java b/src/test/java/com/bernd/game/MoveListTest.java index 3a1daf6..34b3cf2 100644 --- a/src/test/java/com/bernd/game/MoveListTest.java +++ b/src/test/java/com/bernd/game/MoveListTest.java @@ -10,8 +10,8 @@ class MoveListTest { @Test void testGet() { MoveList list = MoveList.create(9); - list.add(Board.B, move(0, 1)); - list.add(Board.W, move(2, 3)); + list.add(Board.B, move(0, 1), false); + list.add(Board.W, move(2, 3), false); assertEquals(2, list.size()); assertEquals(0, list.get(0).x()); assertEquals(1, list.get(0).y()); @@ -26,7 +26,7 @@ void testGrow() { MoveList list = MoveList.create(9); for (int y = 0; y < 9; y++) { for (int x = 0; x < 9; x++) { - list.add(Board.B, move(x, y)); + list.add(Board.B, move(x, y), false); } } assertEquals(81, list.size()); @@ -35,4 +35,4 @@ void testGrow() { private Move move(int x, int y) { return new Move("", false, false, x, y); } -} \ No newline at end of file +}