diff --git a/src/main/client/src/Game.jsx b/src/main/client/src/Game.jsx
index 02cae00..5a64fe9 100644
--- a/src/main/client/src/Game.jsx
+++ b/src/main/client/src/Game.jsx
@@ -40,7 +40,7 @@ export const Game = () => {
let lastStoneXref = useRef(-1)
let lastStoneYref = useRef(-1)
let [zoom, setZoom] = useState(0)
- let { gameId } = useParams()
+ let {gameId} = useParams()
let stompClient = useContext(StompContext)
let auth = useAuthStore(state => state.auth)
let setGameState = useGameStore(state => state.setGameState)
@@ -50,11 +50,11 @@ export const Game = () => {
let currentPlayer = useGameStore(state => state.currentPlayer)
let counting = useGameStore(state => state.counting)
let currentColor = useGameStore(state => state.currentColor)
- let { board, forbidden } = useGameStore(state => state.gameState)
+ let {board, forbidden, gameHasEnded} = useGameStore(state => state.gameState)
let [forbidden_x, forbidden_y] = forbidden
let initialized = useRef()
let canvasRef = useRef()
- let countingGroup = counting() ? getCountingGroup(board, cursor_x, cursor_y) : undefined
+ let countingGroup = !gameHasEnded && counting() ? getCountingGroup(board, cursor_x, cursor_y) : undefined
let context = useMemo(() => {
let dim = board.length
@@ -118,6 +118,9 @@ export const Game = () => {
}, [board.length, canvasRef, zoom])
let onMouseMove = useCallback((e) => {
+ if (gameHasEnded) {
+ return
+ }
if (!board.length) {
return
}
@@ -128,9 +131,12 @@ export const Game = () => {
let cursor_y = Math.round((e.nativeEvent.offsetY - context.margin) / context.step)
setCursor_x(cursor_x + 0)
setCursor_y(cursor_y + 0)
- }, [context, currentPlayer, auth, board.length, counting])
+ }, [context, currentPlayer, auth, board.length, counting, gameHasEnded])
let onClick = useCallback((e) => {
+ if (gameHasEnded) {
+ return
+ }
if (!board.length) {
return
}
@@ -165,7 +171,7 @@ export const Game = () => {
y: cursor_y,
}),
})
- }, [context, currentPlayer, currentColor, auth, board, gameId, stompClient, counting, forbidden_x, forbidden_y, queueLength])
+ }, [context, currentPlayer, currentColor, auth, board, gameId, stompClient, counting, forbidden_x, forbidden_y, queueLength, gameHasEnded])
useEffect(() => {
if (!board.length) {
diff --git a/src/main/client/src/component/Button.jsx b/src/main/client/src/component/Button.jsx
index 7437624..ada5f5f 100644
--- a/src/main/client/src/component/Button.jsx
+++ b/src/main/client/src/component/Button.jsx
@@ -1,9 +1,9 @@
import {
- twJoin,
+ twMerge,
} from "tailwind-merge"
export const Button = ({ type, children, disabled, className, onClick, ...rest }) => {
- let classes = twJoin(
+ let classes = twMerge(
"border-2 border-slate-600 rounded-lg px-8 py-2",
disabled && "text-slate-500 bg-slate-200 border-slate-400 border-2",
!disabled && "hover:text-white text-slate-200 hover:border-sky-700",
diff --git a/src/main/client/src/feature/GamePanel.jsx b/src/main/client/src/feature/GamePanel.jsx
index 2c35513..e4ba171 100644
--- a/src/main/client/src/feature/GamePanel.jsx
+++ b/src/main/client/src/feature/GamePanel.jsx
@@ -50,8 +50,9 @@ function Panel({zoom, setZoom}) {
let white = useGameStore(state => state.white)
let queueLength = useGameStore(state => state.queueLength)
let counting = useGameStore(state => state.counting)
+ let countingComplete = useGameStore(state => state.countingComplete)
let currentPlayer = useGameStore(state => state.currentPlayer)
- let { board } = useGameStore(state => state.gameState)
+ let { board, gameHasEnded } = useGameStore(state => state.gameState)
let navigate = useNavigate()
let onExit = useCallback(() => {
navigate(base + "/lobby")
@@ -76,10 +77,20 @@ function Panel({zoom, setZoom}) {
}),
})
}, [stompClient, gameId, queueLength])
+ let onCountingAgree = useCallback(() => {
+ stompClient.publish({
+ destination: "/app/game/move",
+ body: JSON.stringify({
+ id: gameId,
+ n: queueLength(),
+ agreeCounting: true,
+ }),
+ })
+ }, [stompClient, gameId, queueLength])
if (!board.length) {
return Loading...
}
- let result = counting() ? getScore(board) : undefined
+ let result = gameHasEnded ? getScore(board) : undefined
return (
<>
@@ -123,21 +134,33 @@ function Panel({zoom, setZoom}) {
vs
{black.name}
+ Move {queueLength()}
- {counting() && (
+ {counting() && <>
- )}
+
+
+
+ >}
{result && (
@@ -151,11 +174,6 @@ function Panel({zoom, setZoom}) {
)}
- {!counting() && (
-
- {currentPlayer() + " ist dran..."}
-
- )}
>
)
}
diff --git a/src/main/client/src/store.js b/src/main/client/src/store.js
index 89317f1..c4b0ef0 100644
--- a/src/main/client/src/store.js
+++ b/src/main/client/src/store.js
@@ -4,6 +4,9 @@ import {
import {
produce,
} from "immer"
+import {
+ persist,
+} from "zustand/middleware"
import {
BLACK,
WHITE,
@@ -23,29 +26,35 @@ import {
resetCounting,
} from "./model/count.js"
-export const useAuthStore = create((set) => ({
- auth: {
- name: "",
- state: "anonymous",
- token: "",
- },
- setAuth: (payload) => {
- set(produce(state => {
- state.auth.name = payload.name
- state.auth.state = "authenticated"
- state.auth.token = payload.token
- }))
- },
- setPending: (b) => {
- set(produce(state => {
- state.auth.state = b ? "pending" : "anonymous"
- }), true)
- },
-}))
+export const useAuthStore = create(
+ persist(
+ (set) => ({
+ auth: {
+ name: "",
+ state: "anonymous",
+ token: "",
+ },
+ setAuth: (payload) => {
+ set(produce(state => {
+ state.auth.name = payload.name
+ state.auth.state = "authenticated"
+ state.auth.token = payload.token
+ }))
+ },
+ setPending: (b) => {
+ set(produce(state => {
+ state.auth.state = b ? "pending" : "anonymous"
+ }), true)
+ },
+ }),
+ { name: "auth-storage" },
+ ),
+)
export const useGameStore = create((set, get) => ({
moves: [],
baseBoard: [],
+ dim: 0,
queueStatus: "behind",
black: {
name: "",
@@ -53,11 +62,20 @@ export const useGameStore = create((set, get) => ({
white: {
name: "",
},
- isInCountingGroup: undefined,
- setIsInCountingGroup: (has) => {
- set(produce(state => {
- state.isInCountingGroup = has
- }))
+ countingComplete: () => {
+ if (!get().counting()) {
+ return false
+ }
+ let baseBoard = get().baseBoard
+ let dim = get().dim
+ for (let y = 0; y < dim; y++) {
+ for (let x = 0; x < dim; x++) {
+ if (!baseBoard[y][x]) {
+ return false
+ }
+ }
+ }
+ return true
},
currentPlayer: () => {
let moves = get().moves
@@ -88,9 +106,15 @@ export const useGameStore = create((set, get) => ({
gameState: {
board: [],
forbidden: [-1, -1],
+ gameHasEnded: false,
},
addMove: (move) => {
set(produce(state => {
+ if (move.end) {
+ state.moves.push(move)
+ state.gameState.gameHasEnded = true
+ return
+ }
let moves = get().moves
let baseBoard = get().baseBoard
if (move.n < moves.length) {
@@ -119,11 +143,17 @@ export const useGameStore = create((set, get) => ({
let moves = []
let forbidden = [-1, -1]
for (let move of game.moves) {
+ if (move.end) {
+ moves.push(move)
+ state.gameState.gameHasEnded = true
+ break
+ }
let [storedMove, updated, newForbidden] = createMoveData(baseBoard, moves, move)
moves.push(storedMove)
forbidden = newForbidden
baseBoard = updated
}
+ state.dim = game.dim
state.baseBoard = baseBoard
state.moves = moves
state.gameState.board = rehydrate(baseBoard)
diff --git a/src/main/java/com/bernd/GameController.java b/src/main/java/com/bernd/GameController.java
index e0ffe7a..8ecab01 100644
--- a/src/main/java/com/bernd/GameController.java
+++ b/src/main/java/com/bernd/GameController.java
@@ -3,7 +3,6 @@
import com.bernd.game.Board;
import com.bernd.model.AcceptRequest;
import com.bernd.model.ActiveGame;
-import com.bernd.model.CountingMove;
import com.bernd.model.Game;
import com.bernd.model.Move;
import com.bernd.model.OpenGame;
@@ -68,7 +67,11 @@ public void action(Move move, Principal principal) {
}
Game updated = game.update(move);
games.put(updated);
- operations.convertAndSend("/topic/move/" + game.id(), move.toView(color, updated.counting()));
+ if (updated.gameHasEnded()) {
+ operations.convertAndSend("/topic/move/" + game.id(), move.gameEnd(color, updated.counting()));
+ } else if (!move.agreeCounting()) {
+ operations.convertAndSend("/topic/move/" + game.id(), move.toView(color, updated.counting()));
+ }
}
@ResponseBody
diff --git a/src/main/java/com/bernd/LobbyController.java b/src/main/java/com/bernd/LobbyController.java
index 4cc017e..205d9f0 100644
--- a/src/main/java/com/bernd/LobbyController.java
+++ b/src/main/java/com/bernd/LobbyController.java
@@ -76,6 +76,7 @@ public ViewGame startEdit(@RequestBody MatchRequest request) {
user,
user,
false,
+ 0,
principal,
B,
false,
diff --git a/src/main/java/com/bernd/game/MoveList.java b/src/main/java/com/bernd/game/MoveList.java
index 86356b7..ec5b78d 100644
--- a/src/main/java/com/bernd/game/MoveList.java
+++ b/src/main/java/com/bernd/game/MoveList.java
@@ -39,7 +39,7 @@ public static MoveList create(int dim) {
return new MoveList(dim, new int[16]);
}
- public void gameEnd() {
+ public void addGameEndMarker() {
ensureCapacity();
set(GAME_END);
pos++;
diff --git a/src/main/java/com/bernd/model/Game.java b/src/main/java/com/bernd/model/Game.java
index d546eb6..1843d89 100644
--- a/src/main/java/com/bernd/model/Game.java
+++ b/src/main/java/com/bernd/model/Game.java
@@ -19,6 +19,7 @@ public record Game(
User black,
User white,
boolean counting,
+ int countingAgreed,
String currentPlayer,
int currentColor,
boolean opponentPassed,
@@ -42,6 +43,12 @@ public Game update(Move move) {
}
private Game updateInternal(Move move) {
+ if (move.agreeCounting()) {
+ if ((countingAgreed | currentColor()) == COLORS) {
+ moves.addGameEndMarker();
+ }
+ return countingAgreed(countingAgreed | currentColor());
+ }
moves.add(currentColor, move, counting);
if (counting) {
if (move.resetCounting()) {
@@ -55,7 +62,7 @@ private Game updateInternal(Move move) {
if (opponentPassed) {
return startCounting();
}
- return game(board, counting, true, NOT_FORBIDDEN);
+ return game(board, counting, 0, true, NOT_FORBIDDEN);
}
int x = move.x();
int y = move.y();
@@ -124,6 +131,7 @@ private Direction getDirection(
private Game game(
int[][] board,
boolean counting,
+ int countingAgreed,
boolean opponentPassed,
int[] forbidden) {
return new Game(
@@ -131,6 +139,7 @@ private Game game(
black,
white,
counting,
+ countingAgreed,
nextPlayer(),
nextColor(),
opponentPassed,
@@ -142,11 +151,11 @@ private Game game(
}
private Game game(int[][] board, int[] forbidden) {
- return game(board, counting, false, forbidden);
+ return game(board, counting, 0, false, forbidden);
}
private Game startCounting() {
- return game(Count.count(board), true, true, NOT_FORBIDDEN);
+ return game(Count.count(board), true, 0, true, NOT_FORBIDDEN);
}
private String nextPlayer() {
@@ -163,4 +172,12 @@ private int nextColor() {
public ViewGame toView() {
return ViewGame.fromGame(this);
}
+
+ private Game countingAgreed(int countingAgreed) {
+ return game(board, counting, countingAgreed, opponentPassed, forbidden);
+ }
+
+ public boolean gameHasEnded() {
+ return countingAgreed == COLORS;
+ }
}
diff --git a/src/main/java/com/bernd/model/GameEndMove.java b/src/main/java/com/bernd/model/GameEndMove.java
new file mode 100644
index 0000000..f022ce5
--- /dev/null
+++ b/src/main/java/com/bernd/model/GameEndMove.java
@@ -0,0 +1,8 @@
+package com.bernd.model;
+
+public record GameEndMove(boolean gameHasEnded) {
+
+ public static GameEndMove create() {
+ return new GameEndMove(true);
+ }
+}
diff --git a/src/main/java/com/bernd/model/Move.java b/src/main/java/com/bernd/model/Move.java
index f2ff8da..d85e70c 100644
--- a/src/main/java/com/bernd/model/Move.java
+++ b/src/main/java/com/bernd/model/Move.java
@@ -5,10 +5,15 @@ public record Move(
int n,
boolean pass,
boolean resetCounting,
+ boolean agreeCounting,
int x,
int y) {
public GameMove toView(int color, boolean counting) {
return new GameMove(n, color, pass, x, y, counting, resetCounting, false);
}
+
+ public GameMove gameEnd(int color, boolean counting) {
+ return new GameMove(n, color, pass, x, y, counting, resetCounting, true);
+ }
}
diff --git a/src/main/java/com/bernd/model/OpenGame.java b/src/main/java/com/bernd/model/OpenGame.java
index daafc07..2cccc6c 100644
--- a/src/main/java/com/bernd/model/OpenGame.java
+++ b/src/main/java/com/bernd/model/OpenGame.java
@@ -27,6 +27,7 @@ public Game accept(String opponent, AcceptRequest acceptRequest) {
userBlack,
userWhite,
false,
+ 0,
userBlack.name(),
B,
false,
diff --git a/src/main/java/com/bernd/model/ViewGame.java b/src/main/java/com/bernd/model/ViewGame.java
index d1a1003..7f8c587 100644
--- a/src/main/java/com/bernd/model/ViewGame.java
+++ b/src/main/java/com/bernd/model/ViewGame.java
@@ -7,12 +7,7 @@ public record ViewGame(
User black,
User white,
int dim,
- boolean counting,
- String currentPlayer,
- int currentColor,
- boolean opponentPassed,
int handicap,
- int[] forbidden,
List moves
) {
@@ -22,12 +17,7 @@ static ViewGame fromGame(Game game) {
game.black(),
game.white(),
game.dim(),
- game.counting(),
- game.currentPlayer(),
- game.currentColor(),
- game.opponentPassed(),
game.handicap(),
- game.forbidden(),
game.moves().asList());
}
}
diff --git a/src/test/java/com/bernd/game/MoveListTest.java b/src/test/java/com/bernd/game/MoveListTest.java
index 4ad74cc..2daf8d2 100644
--- a/src/test/java/com/bernd/game/MoveListTest.java
+++ b/src/test/java/com/bernd/game/MoveListTest.java
@@ -33,6 +33,6 @@ void testGrow() {
}
private Move move(int x, int y) {
- return new Move("", 0, false, false, x, y);
+ return new Move("", 0, false, false, false, x, y);
}
}