diff --git a/packages/play-node-go/src/components/GameUI/Kifu/Kifu.js b/packages/play-node-go/src/components/GameUI/Kifu/Kifu.js index a494739..c1a8e5c 100644 --- a/packages/play-node-go/src/components/GameUI/Kifu/Kifu.js +++ b/packages/play-node-go/src/components/GameUI/Kifu/Kifu.js @@ -1,9 +1,12 @@ import React from "react"; import "./Kifu.scss"; -const Kifu = () => { +const Kifu = ({ clickKifu }) => { return (
+

+ Show Menu? +

); diff --git a/packages/play-node-go/src/components/GameUI/Kifu/Kifu.scss b/packages/play-node-go/src/components/GameUI/Kifu/Kifu.scss index a529d41..8d8c141 100644 --- a/packages/play-node-go/src/components/GameUI/Kifu/Kifu.scss +++ b/packages/play-node-go/src/components/GameUI/Kifu/Kifu.scss @@ -1,9 +1,27 @@ +@import '../../../../public/stylesheets/partials/mixins'; + div.Kifu { order: 0; height: 10vh; width: 8vh; background-color: #FFF; transform: rotate(-20deg); + + p.Kifu__show-menu { + display: none; + margin: 3vh auto; + position: absolute; + transform: rotate(20deg); + width: fit-content; + } + &:hover { + p.Kifu__show-menu { + @include gameViewLabel; + cursor: pointer; + display: inline-block; + } + } + div.Kifu__board { border: 0.25vh solid #444; width: 5vh; diff --git a/packages/play-node-go/src/components/GameUI/Menu/Menu.js b/packages/play-node-go/src/components/GameUI/Menu/Menu.js new file mode 100644 index 0000000..ca9fadc --- /dev/null +++ b/packages/play-node-go/src/components/GameUI/Menu/Menu.js @@ -0,0 +1,177 @@ +import React, { useEffect, useRef } from "react"; +import "./Menu.scss"; + +const Menu = ({ showMenu, clickClose, ...props }) => { + const { active, meta } = props.state; // active.game.boardSize, meta.gameRecord + const boardSize = active.game.boardSize; + const handleBackgroundClick = (e) => { + if (e.target.className === "Game__Menu-container") clickClose(); + }; + const canvasRef = useRef(); + const overflowRef = useRef(); + const drawGameRecord = () => { + return ( + <> + + + + ); + }; + + useEffect(() => { + const canvas = canvasRef.current; + const ctx = canvas.getContext("2d"); + const scale = Math.min(window.innerWidth * 0.75, 500); + canvas.height = scale; + canvas.width = scale; + const space = scale / boardSize; + const offset = space / 2; + for (let i = 0; i < boardSize; i++) { + const start = i * space + offset; + const end = scale - offset; + ctx.beginPath(); + ctx.moveTo(start, offset); + ctx.lineTo(start, end); + ctx.closePath(); + ctx.stroke(); + ctx.beginPath(); + ctx.moveTo(offset, start); + ctx.lineTo(end, start); + ctx.closePath(); + ctx.stroke(); + } + if (!meta?.gameRecord) return; + ctx.textAlign = "center"; + ctx.textBaseline = "middle"; + const { overflow } = meta.gameRecord.reduce( + (dict, { player, pos }, index) => { + const past = dict[`${pos.x}-${pos.y}`]; + if (past) { + // overflow: [ { move:#, player:'color', subsequentMoves: [ { move: #, player: 'color' } ] } ] + if (dict.overflow) { + const indexOfPrior = dict.overflow.findIndex( + ({ move }) => move === past + ); + if (indexOfPrior !== -1) { + // if multiple past moves at this point exist + dict.overflow[indexOfPrior].subsequentMoves.push({ + move: index + 1, + player, + }); + return dict; + } + // if a second move at this point has not yet been encountered + // prior move will be black if no active handicap and move is odd or if active handicap and move is even + const playerPrior = + (active.handicap && !(past % 2)) || past % 2 ? "black" : "white"; + return { + ...dict, + overflow: [ + ...dict.overflow, + { + move: past, + player: playerPrior, + subsequentMoves: [{ move: index + 1, player }], + }, + ], + }; + } + // if no move has yet been encountered at a previously made move + return { + ...dict, + overflow: [ + { move: past, subsequentMoves: [{ move: index + 1, player }] }, + ], + }; + } + ctx.beginPath(); + ctx.arc( + (pos.y - 1) * space + offset, + (pos.x - 1) * space + offset, + offset * 0.95, + 0, + Math.PI * 2, + true + ); + ctx.stroke(); + ctx.fillStyle = player === "white" ? "#fff" : "#000"; + ctx.fill(); + ctx.fillStyle = player === "white" ? "#000" : "#fff"; + + ctx.fillText( + index + 1, + (pos.y - 1) * space + offset, + (pos.x - 1) * space + offset + ); + return { ...dict, [`${pos.x}-${pos.y}`]: index + 1 }; + }, + {} + ); + if (!overflow?.length) return; + // Draw Overflow Moves (moves made at prior points) + const canvas2 = overflowRef.current; + const ctx2 = canvas2.getContext("2d"); + canvas2.width = scale; + canvas2.height = space * overflow.length; + ctx2.textAlign = "center"; + ctx2.textBaseline = "middle"; + overflow.forEach(({ move, subsequentMoves, player }, index) => { + subsequentMoves.forEach(({ player, move }, subIndex) => { + ctx2.beginPath(); + ctx2.arc( + subIndex * space + offset, + index * space + offset, + offset * 0.95, + 0, + Math.PI * 2, + true + ); + ctx2.stroke(); + ctx2.fillStyle = player === "white" ? "#fff" : "#000"; + ctx2.fill(); + ctx2.fillStyle = player === "white" ? "#000" : "#fff"; + ctx2.fillText(move, subIndex * space + offset, index * space + offset); + }); + ctx2.fillStyle = "#000"; + ctx2.fillText( + "at", + subsequentMoves.length * space + offset, + index * space + offset + ); + ctx2.fillStyle = player === "white" ? "#fff" : "#000"; + ctx2.beginPath(); + ctx2.arc( + (subsequentMoves.length + 1) * space + offset, + index * space + offset, + offset * 0.95, + 0, + Math.PI * 2, + true + ); + ctx2.fill(); + ctx2.stroke(); + ctx2.fillStyle = player === "white" ? "#000" : "#fff"; + ctx2.fillText( + move, + (subsequentMoves.length + 1) * space + offset, + index * space + offset + ); + }); + }, [showMenu, meta, active.handicap, boardSize]); + + return ( +
handleBackgroundClick(e)} + > +
+ +
+ {drawGameRecord()} +
+
+
+ ); +}; + +export default Menu; diff --git a/packages/play-node-go/src/components/GameUI/Menu/Menu.scss b/packages/play-node-go/src/components/GameUI/Menu/Menu.scss new file mode 100644 index 0000000..05bf39e --- /dev/null +++ b/packages/play-node-go/src/components/GameUI/Menu/Menu.scss @@ -0,0 +1,29 @@ +div.Game__Menu-container { + &--hidden { + display: none; + } + + background: rgba(0,0,0,0.5); + display: flex; + flex-flow: column nowrap; + justify-content: center; + align-items: center; + height: 100vh; + position: absolute; + width: 100vw; + z-index: 2; + + .Game__Menu-container__Menu { + background: #eef; + max-height: 90vh; + max-width: 80vw; + padding: 2vh; + overflow: scroll; + } + + div.Game__Menu__game-record-container { + display: flex; + flex-flow: column nowrap; + align-items: center; + } +} \ No newline at end of file diff --git a/packages/play-node-go/src/pages/Game/Game.js b/packages/play-node-go/src/pages/Game/Game.js index fab84af..a7abdf8 100644 --- a/packages/play-node-go/src/pages/Game/Game.js +++ b/packages/play-node-go/src/pages/Game/Game.js @@ -6,12 +6,14 @@ import Logo from "../../components/Display/Logo/Logo"; import Board from "../../components/GameUI/Board/Board"; import PlayerArea from "../../components/GameUI/PlayerArea/PlayerArea"; import Kifu from "../../components/GameUI/Kifu/Kifu"; +import Menu from "../../components/GameUI/Menu/Menu"; const Game = (props) => { const { state, dispatch } = props; const gameId = parseInt(useParams().id) || 0; const [playerBlackMeta, setPlayerBlackMeta] = useState({}); const [playerWhiteMeta, setPlayerWhiteMeta] = useState({}); + const [showMenu, setShowMenu] = useState(false); const playerState = state?.meta?.playerState; const game = state.active?.game; @@ -91,6 +93,11 @@ const Game = (props) => { return (
+ setShowMenu(false)} + {...props} + />
{state.socket ? "✓" : " ⃠"} @@ -140,7 +147,7 @@ const Game = (props) => { ? playerWhiteMeta : playerBlackMeta } - Kifu={} + Kifu={ setShowMenu(true)} />} turn={state?.meta?.turn} />