From fdfb69c7e6301898340c40269a5c589038719401 Mon Sep 17 00:00:00 2001 From: Halit Aksoy Date: Sun, 8 May 2022 17:13:21 +0300 Subject: [PATCH 1/3] format : BoardView --- src/components/BoardView.tsx | 286 +++++++++++++++++++++-------------- 1 file changed, 171 insertions(+), 115 deletions(-) diff --git a/src/components/BoardView.tsx b/src/components/BoardView.tsx index cd2479c..49ce4cc 100644 --- a/src/components/BoardView.tsx +++ b/src/components/BoardView.tsx @@ -1,136 +1,192 @@ -import { Bank, MancalaGame, Pit } from 'mancala.js'; -import * as React from 'react'; -import { FunctionComponent, useState } from 'react'; +import { Bank, MancalaGame, Pit } from "mancala.js"; +import * as React from "react"; +import { FunctionComponent, useState } from "react"; type Theme = { - background : string, - boardColor: string - boardColorWhenPlayerTurn: string - storeColor: string - storeColorWhenPlayerTurn: string - holeColor: string - ballColor: string -} + background: string; + boardColor: string; + boardColorWhenPlayerTurn: string; + storeColor: string; + storeColorWhenPlayerTurn: string; + holeColor: string; + ballColor: string; +}; const theme: Theme = { - background : "#EEEEEE", - boardColor: "#4D606E", - boardColorWhenPlayerTurn: "#84b8a6", - storeColor: "#3FBAC2", - storeColorWhenPlayerTurn: "#6cab94", - holeColor: "#D3D4D8", - ballColor: "#393E46", -} + background: "#EEEEEE", + boardColor: "#4D606E", + boardColorWhenPlayerTurn: "#84b8a6", + storeColor: "#3FBAC2", + storeColorWhenPlayerTurn: "#6cab94", + holeColor: "#D3D4D8", + ballColor: "#393E46", +}; -const BallView: FunctionComponent<{color : string}> = ({color}) => { - - return (
= ({ color }) => { + return ( +
-
) -} + borderRadius: "10vw", + }} + >
+ ); +}; function range(size: number) { - var ans = []; - for (let i = 0; i < size; i++) { - ans.push(i); - } - return ans; + var ans = []; + for (let i = 0; i < size; i++) { + ans.push(i); + } + return ans; } -const HoleView: FunctionComponent<{ hole: Pit, color: string, onClick: () => void }> = ({ hole, color, onClick }) => { - const balls = [...range(hole.stoneCount)].map((i) => ) +const HoleView: FunctionComponent<{ + hole: Pit; + color: string; + onClick: () => void; +}> = ({ hole, color, onClick }) => { + const balls = [...range(hole.stoneCount)].map((i) => ( + + )); - return (
- {balls} -
) -} + return ( +
+ {balls} +
+ ); +}; -const StoreView: FunctionComponent< - { store: Bank, color: string, gridColumn: string, gridRow: string }> = ({ store, color, gridColumn, gridRow }) => { - const balls = [...range(store.stoneCount)].map((i) => ) - return (
- {balls} -
) - } +const StoreView: FunctionComponent<{ + store: Bank; + color: string; + gridColumn: string; + gridRow: string; +}> = ({ store, color, gridColumn, gridRow }) => { + const balls = [...range(store.stoneCount)].map((i) => ( + + )); + return ( +
+ {balls} +
+ ); +}; -const BoardView: FunctionComponent<{ game?: MancalaGame, userKey: string, onHoleSelect: (index: number, hole: Pit) => void }> = ({ - game, userKey, onHoleSelect }) => { - - const player1Pits = game?.board.player1Pits.map((hole) => ( - { - if (game.turnPlayerId === game.player1Id) onHoleSelect(game.board.player1Pits.indexOf(hole), hole) - }} /> - )) +const BoardView: FunctionComponent<{ + game?: MancalaGame; + userKey: string; + onHoleSelect: (index: number, hole: Pit) => void; +}> = ({ game, userKey, onHoleSelect }) => { + const player1Pits = game?.board.player1Pits.map((hole) => ( + { + if (game.turnPlayerId === game.player1Id) + onHoleSelect(game.board.player1Pits.indexOf(hole), hole); + }} + /> + )); - const player2Pits = game!!.board.player2Pits.map((hole) => ( - { - if (game.turnPlayerId === game.player2Id) onHoleSelect(game.board.player2Pits.indexOf(hole), hole) - }} /> - )) + const player2Pits = game!!.board.player2Pits.map((hole) => ( + { + if (game.turnPlayerId === game.player2Id) + onHoleSelect(game.board.player2Pits.indexOf(hole), hole); + }} + /> + )); - const isUserTurn = game.checkIsPlayerTurn(userKey) + const isUserTurn = game.checkIsPlayerTurn(userKey); - const storeColor = isUserTurn ? theme.storeColor : theme.storeColorWhenPlayerTurn; + const storeColor = isUserTurn + ? theme.storeColor + : theme.storeColorWhenPlayerTurn; - return ( -
- { - userKey === game.player2Id ? ( - <> - - - {player1Pits.reverse()} - {player2Pits} - - ) : ( - <> - - - {player2Pits.reverse()} - {player1Pits} - - ) - } + return ( +
+ {userKey === game.player2Id ? ( + <> + + + {player1Pits.reverse()} + {player2Pits} + + ) : ( + <> + + + {player2Pits.reverse()} + {player1Pits} + + )} +
+ ); +}; -
- ) -} - -export default BoardView \ No newline at end of file +export default BoardView; From 290a1f5b6a8e4d72ed6bfd8067e215582f9b3c0f Mon Sep 17 00:00:00 2001 From: Halit Aksoy Date: Thu, 12 May 2022 23:41:24 +0300 Subject: [PATCH 2/3] add basic animation --- package.json | 2 +- src/Home.tsx | 308 ++++++++++++++++++++++------------- src/components/BoardView.tsx | 117 +++++++++---- yarn.lock | 8 +- 4 files changed, 284 insertions(+), 151 deletions(-) diff --git a/package.json b/package.json index 0550783..e6c02a1 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "author": "", "license": "ISC", "dependencies": { - "mancala.js": "^0.0.2-beta.0", + "mancala.js": "^0.0.2-beta.1", "react": "^17.0.2", "react-dom": "^17.0.2" }, diff --git a/src/Home.tsx b/src/Home.tsx index 7b2fc59..3e95d96 100644 --- a/src/Home.tsx +++ b/src/Home.tsx @@ -1,179 +1,255 @@ -import * as React from 'react'; -import { FunctionComponent, useState } from 'react'; -import BoardView from './components/BoardView'; -import { context } from './context' -import { RTMTWS } from './rtmt/rtmt_websocket'; -import { channel_game_move, channel_leave_game, channel_on_game_update, channel_on_game_user_leave } from './channel_names'; -import Button from './components/Button'; -import InfoPanel from './components/InfoPanel'; -import { CommonMancalaGame, MancalaGame, Pit } from 'mancala.js' -import { GameMove } from './models/GameMove'; +import * as React from "react"; +import { FunctionComponent, useEffect, useState } from "react"; +import BoardView from "./components/BoardView"; +import { context } from "./context"; +import { RTMTWS } from "./rtmt/rtmt_websocket"; +import { + channel_game_move, + channel_leave_game, + channel_on_game_update, + channel_on_game_user_leave, +} from "./channel_names"; +import Button from "./components/Button"; +import InfoPanel from "./components/InfoPanel"; +import { CommonMancalaGame, MancalaGame, Pit } from "mancala.js"; +import { GameMove } from "./models/GameMove"; -type ConnectionState = "connecting" | "error" | "connected" | "reconnecting" +type ConnectionState = "connecting" | "error" | "connected" | "reconnecting"; const Home: FunctionComponent<{ initial?: number }> = ({ initial = 0 }) => { const [userKey, setUserKey] = useState(undefined); - const [connectionState, setConnetionState] = useState("connecting") + const [connectionState, setConnetionState] = + useState("connecting"); - const [searchingOpponent, setSearchingOpponent] = useState(false) + const [searchingOpponent, setSearchingOpponent] = useState(false); - const [game, setGame] = useState(undefined) + const [game, setGame] = useState(undefined); + const gameRef = React.useRef(game); - const [crashMessage, setCrashMessage] = useState(undefined) + const [crashMessage, setCrashMessage] = useState(undefined); - const [userKeyWhoLeave, setUserKeyWhoLeave] = useState(undefined) + const [userKeyWhoLeave, setUserKeyWhoLeave] = useState(undefined); + + const [animationPitIndex, setAnimationPitIndex] = useState(-1); + + const [intervalId, setIntervalId] = useState(-1); + + useEffect(() => { + gameRef.current = game; + }); const onConnectionDone = () => { - setConnetionState("connected") - } + setConnetionState("connected"); + }; const onConnectionLost = () => { - connectToServer("reconnecting") - } + connectToServer("reconnecting"); + }; const onConnectionError = (event: Event) => { - setConnetionState("error") - } + setConnetionState("error"); + }; const connectToServer = (connectionState: ConnectionState) => { - setConnetionState(connectionState) + setConnetionState(connectionState); context.userKeyStore.getUserKey((userKey: string) => { - setUserKey(userKey) - const rtmtws = context.rtmt as RTMTWS + setUserKey(userKey); + const rtmtws = context.rtmt as RTMTWS; if (rtmtws) { - rtmtws.initWebSocket(userKey, onConnectionDone, onConnectionLost, onConnectionError) + rtmtws.initWebSocket( + userKey, + onConnectionDone, + onConnectionLost, + onConnectionError + ); } else { console.error("context.rtmt is not RTMTWS"); } - }) - } + }); + }; const listenMessages = () => { context.rtmt.listenMessage(channel_on_game_update, (message: Object) => { const newGame: CommonMancalaGame = message as CommonMancalaGame; - setGame(MancalaGame.createFromMancalaGame(newGame)) - }) + const mancalaGame = MancalaGame.createFromMancalaGame(newGame); + if (gameRef.current && mancalaGame.history.length > 0) { + const lastHistoryItem = + mancalaGame.history[mancalaGame.history.length - 1]; + if (lastHistoryItem.gameSteps.length > 0) { + let stepIndex = 0; + if (intervalId) { + clearInterval(intervalId); + } + const id = setInterval(() => { + if (stepIndex === lastHistoryItem.gameSteps.length) { + clearInterval(id); + setAnimationPitIndex(-1); + setGame(mancalaGame); + } else { + const gameStep = lastHistoryItem.gameSteps[stepIndex]; + const index = mancalaGame.board.getPitIndexCircularly( + gameStep.index + ); + setAnimationPitIndex(index); + } + stepIndex++; + }, 250); + setIntervalId(intervalId); + } + } + }); context.rtmt.listenMessage("on_game_start", (message: Object) => { const newGame: CommonMancalaGame = message as CommonMancalaGame; - setSearchingOpponent(false) - setGame(MancalaGame.createFromMancalaGame(newGame)) - }) + setSearchingOpponent(false); + setGame(MancalaGame.createFromMancalaGame(newGame)); + }); - context.rtmt.listenMessage("on_game_crashed", (message : any) => { - const newCrashMessage = message as string + context.rtmt.listenMessage("on_game_crashed", (message: any) => { + const newCrashMessage = message as string; console.error("on_game_crash"); console.error(newCrashMessage); - setCrashMessage(newCrashMessage) - }) + setCrashMessage(newCrashMessage); + }); - context.rtmt.listenMessage(channel_on_game_user_leave, (message : any) => { + context.rtmt.listenMessage(channel_on_game_user_leave, (message: any) => { const userKeyWhoLeave = message; - setUserKeyWhoLeave(userKeyWhoLeave) - }) - } + setUserKeyWhoLeave(userKeyWhoLeave); + }); + }; React.useEffect(() => { - listenMessages() - connectToServer("connecting") - }, []) + listenMessages(); + connectToServer("connecting"); + }, []); const resetGameState = () => { - setGame(undefined) - setCrashMessage(undefined) - setUserKeyWhoLeave(undefined) - } + setGame(undefined); + setCrashMessage(undefined); + setUserKeyWhoLeave(undefined); + }; const newGameClick = () => { - resetGameState() - setSearchingOpponent(true) - context.rtmt.sendMessage("new_game", {}) - } + resetGameState(); + setSearchingOpponent(true); + context.rtmt.sendMessage("new_game", {}); + }; const leaveGame = () => { - context.rtmt.sendMessage(channel_leave_game, {}) - } + context.rtmt.sendMessage(channel_leave_game, {}); + }; const onHoleSelect = (index: number, hole: Pit) => { - const gameMove: GameMove = { index: index } - context.rtmt.sendMessage(channel_game_move, gameMove) - } + const gameMove: GameMove = { index: index }; + context.rtmt.sendMessage(channel_game_move, gameMove); + }; - const showConnectionState = connectionState != "connected" + const showConnectionState = connectionState != "connected"; const connectionStateText = () => { let map: { [key: string]: string } = { - "connecting": context.texts.Connecting, - "connected": context.texts.Connected, - "error": context.texts.CannotConnect, - "reconnecting": context.texts.ConnectingAgain + connecting: context.texts.Connecting, + connected: context.texts.Connected, + error: context.texts.CannotConnect, + reconnecting: context.texts.ConnectingAgain, }; - return map[connectionState] - } + return map[connectionState]; + }; const renderNewGameButton = () => { - const newGame =