From 794a32963b6ed7b43c209ca5fc3f049414add2a9 Mon Sep 17 00:00:00 2001 From: jbock Date: Tue, 6 Aug 2024 09:04:47 +0200 Subject: [PATCH] game end --- src/main/client/src/Game.jsx | 16 ++-- src/main/client/src/component/Button.jsx | 4 +- src/main/client/src/feature/GamePanel.jsx | 38 ++++++--- src/main/client/src/store.js | 78 +++++++++++++------ src/main/java/com/bernd/GameController.java | 7 +- src/main/java/com/bernd/LobbyController.java | 1 + src/main/java/com/bernd/game/MoveList.java | 2 +- src/main/java/com/bernd/model/Game.java | 23 +++++- .../java/com/bernd/model/GameEndMove.java | 8 ++ src/main/java/com/bernd/model/Move.java | 5 ++ src/main/java/com/bernd/model/OpenGame.java | 1 + src/main/java/com/bernd/model/ViewGame.java | 10 --- .../java/com/bernd/game/MoveListTest.java | 2 +- 13 files changed, 137 insertions(+), 58 deletions(-) create mode 100644 src/main/java/com/bernd/model/GameEndMove.java 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); } }