diff --git a/webapp/src/api/games.js b/webapp/src/api/games.js
index dc8ee8c..ff93fa7 100644
--- a/webapp/src/api/games.js
+++ b/webapp/src/api/games.js
@@ -30,43 +30,49 @@ export default function useGamesApi(gamesReducer, config) {
pushGameProposalCancel: ({ uuid }) => ifNot(games.isPushingGameProposalCancel) &&
doPushing(`/api/gameproposal/${uuid}/cancel`, 'PUT', null, {
onPushing: (isPushingGameProposalCancel) => dispatchGames({ type: 'next', isPushingGameProposalCancel }),
- onSuccess: (canceledGame) => dispatchGames({ type: 'next', gamesList: games.nextGameList(canceledGame), proposal: gamesInitialState.proposal })
+ onSuccess: (canceledGame) => dispatchGames({ type: 'next', gamesList: games.nextGame(canceledGame), proposal: gamesInitialState.proposal })
}),
pushGameProposalReject: ({ uuid }) => ifNot(games.isPushingGameProposalReject) &&
doPushing(`/api/gameproposal/${uuid}/reject`, 'PUT', null, {
onPushing: (isPushingGameProposalReject) => dispatchGames({ type: 'next', isPushingGameProposalReject }),
- onSuccess: (rejectedGame) => dispatchGames({ type: 'next', gamesList: games.nextGameList(rejectedGame), proposal: gamesInitialState.proposal })
+ onSuccess: (rejectedGame) => dispatchGames({ type: 'next', gamesList: games.nextGame(rejectedGame), proposal: gamesInitialState.proposal })
}),
pushGameProposalAccept: ({ uuid }) => ifNot(games.isPushingGameProposalAccept) &&
doPushing(`/api/gameproposal/${uuid}/accept`, 'PUT', null, {
onPushing: (isPushingGameProposalAccept) => dispatchGames({ type: 'next', isPushingGameProposalAccept }),
- onSuccess: (acceptedGame) => dispatchGames({ type: 'next', gamesList: games.nextGameList(acceptedGame), proposal: gamesInitialState.proposal })
+ onSuccess: (acceptedGame) => dispatchGames({ type: 'next', gamesList: games.nextGame(acceptedGame), proposal: gamesInitialState.proposal })
}),
pushGameSurrender: ({ uuid }) => ifNot(games.isPushingGameSurrender) &&
doPushing(`/api/game/${uuid}/surrender`, 'PUT', null, {
onPushing: (isPushingGameSurrender) => dispatchGames({ type: 'next', isPushingGameSurrender }),
- onSuccess: (finishedGame) => dispatchGames({ type: 'next', gamesList: games.nextGameList(finishedGame), proposal: gamesInitialState.active })
+ onSuccess: (finishedGame) => dispatchGames({ type: 'next', gamesList: games.nextGame(finishedGame), active: gamesInitialState.active })
}),
pushGameDrawRequest: ({ uuid }) => ifNot(games.isPushingGameDrawRequest) &&
doPushing(`/api/game/${uuid}/drawreq`, 'PUT', null, {
onPushing: (isPushingGameDrawRequest) => dispatchGames({ type: 'next', isPushingGameDrawRequest }),
- onSuccess: (drawReqGame) => dispatchGames({ type: 'next', gamesList: games.nextGameList(drawReqGame), proposal: gamesInitialState.active })
+ onSuccess: (drawReqGame) => dispatchGames({ type: 'next', gamesList: games.nextGame(drawReqGame), active: gamesInitialState.active })
}),
-
+
pushGameDrawAccept: ({ uuid }) => ifNot(games.isPushingGameDrawAccept) &&
doPushing(`/api/game/${uuid}/drawacc`, 'PUT', null, {
onPushing: (isPushingGameDrawAccept) => dispatchGames({ type: 'next', isPushingGameDrawAccept }),
- onSuccess: (drawAccGame) => dispatchGames({ type: 'next', gamesList: games.nextGameList(drawAccGame), proposal: gamesInitialState.active })
+ onSuccess: (drawAccGame) => dispatchGames({ type: 'next', gamesList: games.nextGame(drawAccGame), active: gamesInitialState.active })
}),
pushGameDrawReject: ({ uuid }) => ifNot(games.isPushingGameDrawReject) &&
doPushing(`/api/game/${uuid}/drawrej`, 'PUT', null, {
onPushing: (isPushingGameDrawReject) => dispatchGames({ type: 'next', isPushingGameDrawReject }),
- onSuccess: (drawRejGame) => dispatchGames({ type: 'next', gamesList: games.nextGameList(drawRejGame), proposal: gamesInitialState.active })
+ onSuccess: (drawRejGame) => dispatchGames({ type: 'next', gamesList: games.nextGame(drawRejGame), active: gamesInitialState.active })
+ }),
+
+ pushGameMove: ({ uuid, move, message }) => ifNot(games.isPushingGameMove) &&
+ doPushing(`/api/game/${uuid}/move`, 'PUT', null, {
+ onPushing: (isPushingGameMove) => dispatchGames({ type: 'next', isPushingGameMove }),
+ onSuccess: (game) => dispatchGames({ type: 'next', gamesList: games.nextGame(game), active: gamesInitialState.active })
}),
}
}
diff --git a/webapp/src/components/Checkers.css b/webapp/src/components/Checkers.css
index 99a8455..aca0459 100644
--- a/webapp/src/components/Checkers.css
+++ b/webapp/src/components/Checkers.css
@@ -1,6 +1,7 @@
.Stone {
cursor: default; /* disable 'I beam' cursor change */
user-select: none;
+ pointer-events: none;
}
.Board {
@@ -26,4 +27,8 @@
.Tile.white {
background: white;
+}
+
+.Tile.selected {
+ color: grey;
}
\ No newline at end of file
diff --git a/webapp/src/components/Checkers.jsx b/webapp/src/components/Checkers.jsx
index d7dddf3..25c73d4 100644
--- a/webapp/src/components/Checkers.jsx
+++ b/webapp/src/components/Checkers.jsx
@@ -1,5 +1,5 @@
import './Checkers.css'
-import React from 'react'
+import React, { useState } from 'react'
export const Color = {
white: "WHITE",
@@ -18,13 +18,19 @@ export const Color = {
/*
* Stone
*/
-export function Stone({ color, type }) {
+export function Stone({ color, type, move }) {
+ const style = !move ? null : {
+ position: 'absolute',
+ left: move[0],
+ top: move[1],
+ }
+
switch (color) {
case Color.white:
- return ;
+ return ;
case Color.black:
- return ;
+ return ;
case '':
case undefined:
@@ -32,22 +38,22 @@ export function Stone({ color, type }) {
return; // no stone :)
default:
- console.warn("Unknown color: ", color)
+ console.warn('Unknown color', color)
}
}
-export function WhiteStone({ type }) {
+export function WhiteStone({ type, style }) {
if (type === 'KING')
- return ⛁
+ return ⛁
else
- return ⛀
+ return ⛀
}
-export function BlackStone({ type }) {
+export function BlackStone({ type, style }) {
if (type === 'KING')
- return ⛃
+ return ⛃
else
- return ⛂
+ return ⛂
}
/*
@@ -65,75 +71,102 @@ export function Player({ color, name }) {
/*
* Board
*/
-export function Board({ game, onClick }) {
- const board = (game && game.board && typeof game.board === 'object') ? game.board : defaultBoard;
+export function Board({ game, onStoneClick, onStoneMove }) {
+ const [[moveId, moveX, moveY], setMove] = useState([0, 0, 0]);
- const BlackTile = () => {
- return
- }
+ const board = (game && game.board && typeof game.board === 'object') ? game.board : defaultBoard;
+ const isInteractive = (typeof onStoneClick === 'function' || typeof onStoneMove === 'function') ? ' interactive' : '';
const WhiteTile = ({ id }) => {
const stone = board[id];
- const isInteractive = (typeof onClick === 'function') ? ' interactive' : '';
return (
- isInteractive && onClick(id)}>
+
onStoneClick(game.uuid, id)
+ }
+
+ onMouseDown={!onStoneMove || !stone ? null :
+ (e) => setMove([id, e.clientX, e.clientY])
+ }
+
+ onMouseUp={!onStoneMove || !moveId ? null :
+ () => { onStoneMove(game.uuid, [moveId, id]); setMove([0, 0, 0]) }
+ }
+ >
);
}
- return
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ const BlackTile = () => {
+ return
+ }
-
-
-
-
-
+ const movingStone = board[moveId];
+
+ return (
+
e.buttons ? setMove([moveId, e.clientX, e.clientY]) : setMove([0, 0, 0])
+ }
+ >
+
+ {!movingStone ? null :
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+ )
}
const blackMan = { color: Color.black, type: 'MAN' };
diff --git a/webapp/src/container/Games.jsx b/webapp/src/container/Games.jsx
index 5ec55f0..472e172 100644
--- a/webapp/src/container/Games.jsx
+++ b/webapp/src/container/Games.jsx
@@ -6,6 +6,7 @@ import NewGame from './games/view/NewGame';
import { GameProposalSelector, ActiveGameSelector, GameArchiveSelector } from './games/view/GameSelector';
import { Create, Accept, Reject, Cancel, DrawRequest, DrawAccept, DrawReject, Surrender, Backward, Forward } from './games/ActionPanel';
import GameBoard from './games/GameBoard';
+import { nextStone } from '../components/Checkers';
import Message2Opponent from './games/Message2Opponent';
import Counter from '../components/Counter';
@@ -26,11 +27,7 @@ export default function Games({ context: { gamesReducer, gamesApi }, players })
{/*
*/}
-
dispatchGames({ type: 'nextNewGame', board })}
- onActiveGameMove={(uuid, from, to) => console.log(uuid, 'move', from, '->', to)}
- />
+
@@ -113,6 +110,27 @@ function ActionPanel({ gamesApi }) {
)
}
+function GameBoardRoutes({ gamesReducer, gamesApi, username }) {
+ const [games, dispatchGames] = gamesReducer;
+
+ const onStoneClick = (uuid, cellId) => {
+ let board = { ...games.newGame.board };
+ board[cellId] = nextStone(board[cellId]);
+ dispatchGames({ type: 'nextNewGame', board });
+ }
+
+ const onStoneMove = (uuid, move) => console.log(uuid, 'move', move);
+
+ return (
+
+ } />
+ } />
+ } />
+ } />
+
+ )
+}
+
function countGames(gamesList) {
var awaiting = {
diff --git a/webapp/src/container/games/GameBoard.css b/webapp/src/container/games/GameBoard.css
index 2c90743..85bd13c 100644
--- a/webapp/src/container/games/GameBoard.css
+++ b/webapp/src/container/games/GameBoard.css
@@ -6,7 +6,6 @@
}
.GameBoard .Tile {
- font-size: 200%;
line-height: 34px;
height: 34px;
width: 34px;
@@ -14,6 +13,10 @@
margin-top: -1px;
}
+.GameBoard .Board .Stone {
+ font-size: 200%;
+}
+
.GameBoard .Tile.white.interactive:hover {
background-color:azure;
}
diff --git a/webapp/src/container/games/GameBoard.jsx b/webapp/src/container/games/GameBoard.jsx
index bd3ecb7..e1e1ffd 100644
--- a/webapp/src/container/games/GameBoard.jsx
+++ b/webapp/src/container/games/GameBoard.jsx
@@ -1,58 +1,37 @@
-import React, { useContext, useState } from 'react';
+import React, { useContext } from 'react';
import { useLocation, matchPath } from 'react-router-dom';
import { GamesContext } from '../../context/games';
-import { Color, Player, Board, nextStone } from '../../components/Checkers';
+import { Color, Player, Board } from '../../components/Checkers';
import './GameBoard.css';
-export default function GameBoard({ username, onNewGameBoardStone, onActiveGameMove }) {
+export default function GameBoard({ username, onStoneClick, onStoneMove }) {
const games = useContext(GamesContext);
const { pathname } = useLocation();
- const [startId, setStartId] = useState(null);
- const onClick_NewGame = (cellId) => {
- let nextBoard = { ...games.newGame.board };
- nextBoard[cellId] = nextStone(nextBoard[cellId]);
-
- onNewGameBoardStone(nextBoard);
- }
-
- const onClick_ActiveGame = (cellId) => {
- if (startId === cellId)
- return setStartId(null);
-
- if (startId === null)
- return setStartId(cellId);
-
- if (game?.uuid)
- onActiveGameMove(game.uuid, startId, cellId);
-
- setStartId(null);
- };
-
- const [game, onClick] = (() => {
+ const game = (() => {
if (matchPath('/games/new', pathname))
- return [games.newGame, onClick_NewGame];
+ return games.newGame;
if (matchPath('/games/proposal', pathname))
- return [games.findGame({ uuid: games.proposal.selectedUUID }), null];
- else if (matchPath('/games/active', pathname))
- return [games.findGame({ uuid: games.active.selectedUUID }), onClick_ActiveGame];
- else if (matchPath('/games/archive', pathname))
- return [games.findGame({ uuid: games.archive.selectedUUID }), null];
+ return games.findGame({ uuid: games.proposal.selectedUUID });
- return [{}, null];
+ if (matchPath('/games/active', pathname))
+ return games.findGame({ uuid: games.active.selectedUUID });
+
+ if (matchPath('/games/archive', pathname))
+ return games.findGame({ uuid: games.archive.selectedUUID });
+
+ return {};
})(); // <<-- Execute
const opponentColor = Color.opposite(game?.myColor);
const [opponentName, myName] = game?.opponentName ? [game.opponentName, username] : ['', ''];
- const optionalOnClick = (onClick && game?.board) ? (id) => onClick(id) : null;
-
return (
)
diff --git a/webapp/src/reducer/games.js b/webapp/src/reducer/games.js
index 4fb7eca..6d79c0a 100644
--- a/webapp/src/reducer/games.js
+++ b/webapp/src/reducer/games.js
@@ -38,9 +38,10 @@ export const gamesInitialState = {
isPushingGameDrawRequest: false,
isPushingGameDrawAccept: false,
isPushingGameDrawReject: false,
+ isPushingGameMove: false,
findGame,
- nextGameList,
+ nextGame,
};
function reducer(state, action) {
@@ -86,8 +87,6 @@ function findGame({ uuid }) {
return this.gamesList?.find((game) => game.uuid === uuid);
}
-function nextGameList(nextGame) {
- return this.gamesList?.map(
- (game) => (nextGame?.uuid === game.uuid) ? nextGame : game
- );
+function nextGame(nextGame) {
+ return this.gamesList?.map((game) => (game.uuid === nextGame?.uuid) ? nextGame : game);
}
\ No newline at end of file
diff --git a/webapp/src/util/StateHelper.js b/webapp/src/util/StateHelper.js
index 1cda2df..4262349 100644
--- a/webapp/src/util/StateHelper.js
+++ b/webapp/src/util/StateHelper.js
@@ -1,14 +1,14 @@
-export function nextState(state, action) {
+export function nextState(state, delta) {
const nextState = { ...state };
- Object.keys(action)
+ Object.keys(delta)
.slice(1) // skip first property i.e. 'next'
.forEach(key => {
if (Object.hasOwn(nextState, key)) {
- console.log("next [", key, "] = ", action[key]);
- nextState[key] = action[key];
+ console.log("next [", key, "] = ", delta[key]);
+ nextState[key] = delta[key];
} else {
- console.warn("nextState: bad action property\n", key + ":", action[key]);
+ console.warn("nextState: bad action property\n", key + ":", delta[key]);
}
})