Skip to content

Commit

Permalink
feat(web): implement simplified free mode logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Neosoulink committed Dec 9, 2024
1 parent 1c07351 commit 2a10b4a
Show file tree
Hide file tree
Showing 5 changed files with 216 additions and 171 deletions.
49 changes: 27 additions & 22 deletions apps/web/src/routes/_components/main-menu/new-section.component.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { FC } from "react";
import { Link } from "react-router";
import { Link, useNavigate } from "react-router";

import { GameMode, MainMenuSection } from "../../../shared/enum";
import { useMainMenuStore } from "../../_stores";
import { useGameStore, useMainMenuStore } from "../../_stores";

export const NewGameSection: FC = () => {
const navigate = useNavigate();

const { reset: resetGame } = useGameStore();
const { setSection } = useMainMenuStore();

const GameModeOptions: {
label: string;
title: string;
mode: keyof typeof GameMode;
action?: () => void;
action: () => void;
}[] = [
{
label: "AI",
Expand All @@ -25,7 +28,15 @@ export const NewGameSection: FC = () => {
title: "Play against another human player",
action: () => setSection(MainMenuSection.newGameHuman)
},
{ label: "Free Mode", mode: "free", title: "Play against yourself" },
{
label: "Free Mode",
mode: "free",
title: "Play against yourself",
action: () => {
navigate("/play?mode=free");
resetGame();
}
},
{
label: "Simulation",
mode: "simulation",
Expand All @@ -40,24 +51,18 @@ export const NewGameSection: FC = () => {
<h2 className="text-xl mb-2">Choose your game mode:</h2>

<div className="flex flex-wrap gap-4 text-xl">
{GameModeOptions.map((option) => {
const Component = option.action ? "button" : Link;

return (
<Component
key={option.mode}
{...{
...(option.action ? {} : { viewTransition: true }),
to: `/play?mode=${option.mode}`,
title: option.title,
onClick: option.action,
className: "p-5 rounded shadow-md hover:bg-gray-100"
}}
>
{option.label}
</Component>
);
})}
{GameModeOptions.map((option) => (
<button
key={option.mode}
{...{
title: option.title,
onClick: option.action,
className: "p-5 rounded shadow-md hover:bg-gray-100"
}}
>
{option.label}
</button>
))}
</div>
</div>

Expand Down
129 changes: 0 additions & 129 deletions apps/web/src/routes/_hooks/use-game.hook.ts

This file was deleted.

6 changes: 4 additions & 2 deletions apps/web/src/routes/_stores/game.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import { Properties } from "@quick-threejs/utils";

export interface GameStore {
app?: RegisterModule;
setApp: (app?: RegisterModule) => void;
reset: () => void;
setApp: (app: RegisterModule | undefined) => void;
}

export const gameInitialState: Properties<GameStore> = {};
export const gameInitialState: Properties<GameStore> = { app: undefined };

export const useGameStore = create<GameStore>((set) => ({
...gameInitialState,
reset: () => set(() => ({ ...gameInitialState })),
setApp: (app?: RegisterModule) => set(() => ({ app }))
}));
131 changes: 130 additions & 1 deletion apps/web/src/routes/play/_components/free-mode.component.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,134 @@
import { FC, Fragment } from "react";
import { Move } from "chess.js";
import { FC, Fragment, useCallback, useEffect } from "react";
import { merge } from "rxjs";

import { useGameStore } from "../../_stores";
import { PlayerModel } from "../../../shared/models";
import {
GAME_UPDATED_TOKEN,
PIECE_WILL_MOVE_TOKEN
} from "../../../shared/tokens";
import {
EngineGameUpdatedMessageEventPayload,
MessageEventPayload
} from "../../../shared/types";
import { ColorSide, PlayerEntity } from "@chess-d/shared";

export const FreeModeComponent: FC = () => {
const { app } = useGameStore();

const performPieceMove = useCallback(
(move: Move) => {
app?.worker()?.postMessage?.({
token: PIECE_WILL_MOVE_TOKEN,
value: move
} satisfies MessageEventPayload<Move>);
},
[app]
);

const createPlayer = useCallback((identity: PlayerEntity) => {
const player = new PlayerModel();
player.color = identity.color as ColorSide;

return player;
}, []);

useEffect(() => {
const appGui = app?.gui();
const guiFreeMode = appGui.addFolder("Free Mode");

const _players = [
createPlayer({ color: ColorSide.white }),
createPlayer({ color: ColorSide.black })
];

if (import.meta.env?.DEV) {
const guiPlayer1 = guiFreeMode.addFolder("Player 1");
const player1Positions = { piece: "p", from: "a2", to: "a3" };
guiPlayer1.add(player1Positions, "piece");
guiPlayer1.add(player1Positions, "from");
guiPlayer1.add(player1Positions, "to");
guiPlayer1.add(
{
"Perform Move": () => {
const move = {
color: _players[0]?.color,
from: player1Positions.from,
to: player1Positions.to,
piece: player1Positions.piece
} as Move;

_players[0]?.next({
token: "PLACED_PIECE",
value: { move }
});
}
},
"Perform Move"
);

const guiPlayer2 = guiFreeMode.addFolder("Player 2");
const player2Positions = { piece: "p", from: "a7", to: "a6" };
guiPlayer2.add(player2Positions, "piece");
guiPlayer2.add(player2Positions, "from");
guiPlayer2.add(player2Positions, "to");
guiPlayer2.add(
{
"Perform Move": () => {
const move = {
color: _players[1]?.color,
from: player2Positions.from,
to: player2Positions.to,
piece: player2Positions.piece
} as Move;

_players[1]?.next({
token: "PLACED_PIECE",
value: { move }
});
}
},
"Perform Move"
);
}

const handleMessages = (
payload: MessageEvent<EngineGameUpdatedMessageEventPayload>
) => {
if (!payload.data?.token) return;

if (
payload.data.token === GAME_UPDATED_TOKEN &&
payload.data?.value?.fen
) {
_players.forEach((player) => {
player.next({
token: "NOTIFIED",
value: payload.data.value
});
});
}
};

const playersSubscription = merge(..._players).subscribe((payload) => {
if (payload.token === "PLACED_PIECE" && payload.value?.move)
return performPieceMove(payload.value?.move);
});

app?.worker()?.addEventListener("message", handleMessages);

return () => {
playersSubscription.unsubscribe();
_players.forEach((player) => {
player.complete();
player.unsubscribe();
_players.shift();
});
app?.worker()?.removeEventListener("message", handleMessages);
guiFreeMode.destroy();
};
}, [app, createPlayer, performPieceMove]);

return <Fragment />;
};
Loading

0 comments on commit 2a10b4a

Please sign in to comment.