From 1640d57a05e8883b6f86fca5e8421b1781225a35 Mon Sep 17 00:00:00 2001 From: Neel Kapse Date: Thu, 15 Jun 2023 07:16:02 -0400 Subject: [PATCH] WIP --- .../components/game-online/GameOnline.tsx | 2 + js/frontend/pages/game/[gameid].tsx | 19 ++-- js/frontend/state/game-online/selectors.ts | 6 +- js/frontend/state/game-online/slice.ts | 10 ++- js/hive-db/src/game/game.ts | 52 ++++++++++- js/hive-db/src/game/state.ts | 1 - js/hive-lib/src/board.ts | 2 - js/hive-lib/src/notation.test.ts | 2 +- js/hive-lib/src/notation.ts | 89 ++++++++++--------- js/hive-lib/src/types.ts | 6 ++ 10 files changed, 129 insertions(+), 60 deletions(-) diff --git a/js/frontend/components/game-online/GameOnline.tsx b/js/frontend/components/game-online/GameOnline.tsx index f89d241..e4de9ce 100644 --- a/js/frontend/components/game-online/GameOnline.tsx +++ b/js/frontend/components/game-online/GameOnline.tsx @@ -30,6 +30,8 @@ const GameOnline = ({ uid, game }: { uid: string | null; game: Game }) => { const boardCentered = useGameSelector(selectBoardCentered); const { authToken } = usePlayer(); + console.log(ghosts); + // TODO: Neel: probably not the right place to do this useEffect(() => dispatch(authTokenAdded(authToken))); diff --git a/js/frontend/pages/game/[gameid].tsx b/js/frontend/pages/game/[gameid].tsx index d54d07e..9c59e80 100644 --- a/js/frontend/pages/game/[gameid].tsx +++ b/js/frontend/pages/game/[gameid].tsx @@ -1,7 +1,7 @@ import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; import { Provider } from 'react-redux'; -import { Game, usePlayer } from 'hive-db'; +import { Game, usePlayer, getGame, newGameFromBackendGame } from 'hive-db'; import { GameOnline } from '../../components/game-online/GameOnline'; import { GameOnlineSidebar } from '../../components/game-online/GameOnlineSidebar'; import { NavBar } from '../../components/nav/NavBar'; @@ -62,10 +62,13 @@ const Game = () => { useEffect(() => { // TODO: should this be a strict type check while enforcing that gameid is a string? - setGame(activeGames.find((g) => g.gid == gameid)); - }, [activeGames, gameid]); - - console.log(game); + if (gameid) { + getGame(gameid).then((game) => { + console.log(game); + setGame(newGameFromBackendGame(game)); + }); + } + }, [gameid]); return ( <> @@ -75,7 +78,11 @@ const Game = () => {
- {game ? : 'Loading...'} + {game && user ? ( + + ) : ( + 'Loading...' + )}
diff --git a/js/frontend/state/game-online/selectors.ts b/js/frontend/state/game-online/selectors.ts index 454a7d9..f5a78b6 100644 --- a/js/frontend/state/game-online/selectors.ts +++ b/js/frontend/state/game-online/selectors.ts @@ -39,7 +39,7 @@ export const selectGame = (state: GameState): Game | null => state.game; /** * Get the set of valid next moves */ -export const selectValidMoves = (state: GameState): Move[] | null => +export const selectValidMoves = (state: GameState): PossibleMove[] | null => state.validNextMoves; /** @@ -143,7 +143,9 @@ export const selectGameBoard = createSelector( */ export const selectDisplayGameBoard = createSelector( [selectDisplayMoves, selectDisplayUpTo], - (moves, upTo): GameBoard => buildBoard(moves, upTo) + (moves, upTo): GameBoard => { + return buildBoard(moves, upTo); + } ); /** diff --git a/js/frontend/state/game-online/slice.ts b/js/frontend/state/game-online/slice.ts index 709804a..65da003 100644 --- a/js/frontend/state/game-online/slice.ts +++ b/js/frontend/state/game-online/slice.ts @@ -39,6 +39,15 @@ const slice = createSlice({ if (oldNotation !== newNotation && state.upTo !== -1) { state.newMovesToView = true; } + // TODO: Neel: populate this from game.validMoves and game.validSpawns + let validNextMoves = []; + for (const move of game.validMoves) { + validNextMoves.push(move); + } + for (const spawn of game.validSpawns) { + validNextMoves.push(spawn); + } + state.validNextMoves = validNextMoves; } state.game = game; state.proposedMove = null; @@ -60,7 +69,6 @@ const slice = createSlice({ if (game.state.moveCount === 0) { // The first move is being played state.selectedTileId = null; - console.log(state.authToken); playGameMove(game, state.proposedMove, state.authToken) .then(({ game, validNextMoves }) => { state.game = game; diff --git a/js/hive-db/src/game/game.ts b/js/hive-db/src/game/game.ts index bfc087b..2b6344b 100644 --- a/js/hive-db/src/game/game.ts +++ b/js/hive-db/src/game/game.ts @@ -1,4 +1,4 @@ -import type { ColorKey, GameOptions } from 'hive-lib'; +import type { ColorKey, GameOptions, HexCoordinate } from 'hive-lib'; import type { GameMeta } from './meta'; import type { GamePlayers } from './players'; import type { GameState } from './state'; @@ -23,9 +23,11 @@ export interface BackendGame { ranked: boolean; tournament_queen_rule: boolean; turn: number; + moves: string; + spawns: HexCoordinate[]; } -export interface Game { +export interface GameOverview { gid: string; meta: GameMeta; options: GameOptions; @@ -33,6 +35,11 @@ export interface Game { state: GameState; } +export type Game = GameOverview & { + validMoves: string[]; + validSpawns: string[]; +}; + /** * Create a new game object. * @@ -46,7 +53,7 @@ export function newGame( players: GamePlayers, options: GameOptions, isPublic: boolean -): Game { +): OverviewGame { return { gid: '', options, @@ -158,6 +165,35 @@ function getStateFromBackendGame(backendGame: BackendGame): GameState { return newGameState(backendGame.history); } +function getValidMovesFromBackendGame( + backendGame: BackendGame +): PossibleMove[] { + console.log(backendGame.moves); + return backendGame.moves; +}wekj + +function convertReserveToPieceSymbol(colorSymbol: string, pieceName: string, reserveSize: number): string { + const pieceNumber = reserveSize + return colorSymbol + pieceName[0].toUpperCase() + ; + +function getValidSpawnsFromBackendGame( + backendGame: BackendGame +): PossibleMove[] { + const reserve = backendGame.turn % 2 == 0 ? backendGame.reserve_white : backendGame.reserve_black; + const colorSymbol = backendGame.turn % 2 == 0 ? 'w' : 'b'; + const spawnablePieces = []; + for (let [key, value] of reserve) { + if (value === 0) + continue; + spawnablePieces.push(convertReserveToPieceSymbol(colorSymbol, key, value)) + console.log(key + ' = ' + value); + } + for (const spawn of backendGame.spawns) { + console.log(spawn); + } + return backendGame.spawns; +} + /** * Create a new game object from the backend's representation of a game. * @@ -172,13 +208,17 @@ export function newGameFromBackendGame(backendGame: BackendGame): Game { const players = getPlayersFromBackendGame(backendGame); const meta = getMetaFromBackendGame(backendGame); const state = getStateFromBackendGame(backendGame); + const validMoves = getValidMovesFromBackendGame(backendGame); + const validSpawns = getValidSpawnsFromBackendGame(backendGame); return { gid: backendGame.id, options, players, meta, - state + state, + validMoves, + validSpawns }; } @@ -216,6 +256,10 @@ export function getUserGames(user: UserData): Promise { }); } +export function getGame(uid: string): Promise { + return getJSON(`/api/game/${uid}`); +} + /** * Create a new partial game object, allowing for FieldValue objects to be used * as field values. Objects created using this method can be used in Firestore diff --git a/js/hive-db/src/game/state.ts b/js/hive-db/src/game/state.ts index 7c1f0e6..c65d8ef 100644 --- a/js/hive-db/src/game/state.ts +++ b/js/hive-db/src/game/state.ts @@ -18,7 +18,6 @@ export interface GameState { export const newGameState = (notation?: string): GameState => { if (!notation) notation = ''; const moves = getGameMoves(notation); - console.log(moves); return { notation: notation, turn: moves.length % 2 === 0 ? 'w' : 'b', diff --git a/js/hive-lib/src/board.ts b/js/hive-lib/src/board.ts index c7836d0..a58cb54 100644 --- a/js/hive-lib/src/board.ts +++ b/js/hive-lib/src/board.ts @@ -40,8 +40,6 @@ export function buildBoard(moves: Move[], upTo?: number): GameBoard { // Extract the data we need from the move object const { tileId, refId, dir } = move; - console.log(draft); - if (isEmpty(draft)) { // The first tile placed on the board is always at (0, 0) _placeTile(draft, tileId, { q: 0, r: 0 }); diff --git a/js/hive-lib/src/notation.test.ts b/js/hive-lib/src/notation.test.ts index 70c507e..303502d 100644 --- a/js/hive-lib/src/notation.test.ts +++ b/js/hive-lib/src/notation.test.ts @@ -56,7 +56,7 @@ describe('notation parsing', () => { }); describe('_parseMoveNotation', () => { - test('undefined move', () => expect(_parseMoveNotation()).toBeUndefined()); + // test('undefined move', () => expect(_parseMoveNotation()).toBeUndefined()); test('passing move', () => expect(_parseMoveNotation('x')).toEqual({ notation: 'x', diff --git a/js/hive-lib/src/notation.ts b/js/hive-lib/src/notation.ts index 902d4dd..e046e1a 100644 --- a/js/hive-lib/src/notation.ts +++ b/js/hive-lib/src/notation.ts @@ -53,12 +53,8 @@ export function buildMoveNotation( * @return An array of *Move* objects. */ export function getGameMoves(notation: string): Move[] { - const turns = _parseGameNotation(notation); - return flatten( - turns.map((turn) => - turn.white ? (turn.black ? [turn.white, turn.black] : [turn.white]) : [] - ) - ); + const turns = _parseMoves(notation); + return turns; } /** @@ -126,9 +122,27 @@ export function _buildTurnNotation( * @return An array of *Turn* objects. */ export function _parseGameNotation(notation: string): Turn[] { - return notation + return []; + // return notation + // .split(';') + // .filter((s) => s) + // .map((turnNotation) => _parseMoveNotation(turnNotation)); +} + +/** + * Create an ordered array of *Move* objects by parsing a game notation string. + * + * @param notation A game notation string. + * @return An array of *Move* objects. + */ +export function _parseMoves(notation: string): Move[] { + if (!notation) { + return []; + } + return (notation + '') .split(';') - .map((turnNotation) => _parseTurnNotation(turnNotation)); + .filter((s) => s) + .map((moveNotation) => _parseMoveNotation(moveNotation)); } /** @@ -137,27 +151,27 @@ export function _parseGameNotation(notation: string): Turn[] { * @param notation A turn notation string. * @return A *Turn* object. */ -// export function _parseTurnNotation(notation: string, index: number): Turn { -// const moves = notation.split(' '); -// return { -// notation, -// index: index + 1, -// white: _parseMoveNotation(moves[0]), -// black: _parseMoveNotation(moves[1]) -// }; -// } -export function _parseTurnNotation(notation: string): Turn { - const sepLocation = notation.indexOf('.'); - const indexString = notation.slice(0, sepLocation); - const placementsString = notation.slice(sepLocation + 1); - const placements = placementsString.split(' '); +export function _parseTurnNotation(notation: string, index: number): Turn { + const moves = notation.split(' '); return { notation, - index: parseInt(indexString), - white: _parseMoveNotation(placements[0]), - black: _parseMoveNotation(placements[1]) + index: index + 1, + white: _parseMoveNotation(moves[0]), + black: _parseMoveNotation(moves[1]) }; } +// export function _parseTurnNotation(notation: string): Turn { +// const sepLocation = notation.indexOf('.'); +// const indexString = notation.slice(0, sepLocation); +// const placementsString = notation.slice(sepLocation + 1); +// const placements = placementsString.split(' '); +// return { +// notation, +// index: parseInt(indexString), +// white: _parseMoveNotation(placements[0]), +// black: _parseMoveNotation(placements[1]) +// }; +// } /** * Create a *Move* object by parsing a move notation string. @@ -165,15 +179,12 @@ export function _parseTurnNotation(notation: string): Turn { * @param notation A move notation string. * @return A *Move* object. */ -export function _parseMoveNotation(notation?: string): Move | undefined { - if (!notation) return undefined; - +export function _parseMoveNotation(notation: string): Move { // Split notation into moving tile and reference tile portions notation = notation.trim(); - let [tileId, refNotation] = notation.split(/\s/g); - console.log(notation); - console.log(tileId); - console.log(refNotation); + + // TODO: Neel: figure out why this is getting called twice, once with commas as separators and once with spaces + let [tileId, refNotation] = notation.split(/\s|,/g); // Check for and return a passing move if (tileId === 'x') @@ -186,17 +197,9 @@ export function _parseMoveNotation(notation?: string): Move | undefined { // Parse the reference notation to get the reference tile and direction let { refId, dir, end } = - refNotation !== undefined - ? _parseReferenceNotation(refNotation) // only when there is one - : { refId: tileId, dir: 0, end: false }; // first move notation - - // // TODO: is this the right way to do this? - // // handle first move notation - // if (tileId === '.') { - // refId = tileId; - // dir = 0; - // end = false; - // } + refNotation === '.' + ? { refId: tileId, dir: 0, end: false } // first move notation + : _parseReferenceNotation(refNotation); // only when there is one // Return a playing move return { diff --git a/js/hive-lib/src/types.ts b/js/hive-lib/src/types.ts index 697e531..61b3807 100644 --- a/js/hive-lib/src/types.ts +++ b/js/hive-lib/src/types.ts @@ -70,6 +70,12 @@ export type GameOptions = { mosquito: boolean; }; +export type PossibleMove = { + qCoordinate: number; + rCoordinate: number; + tileId: TileId; +}; + /** * An object describing a player's move, which can either be a pass or move or * place a tile.