feat: add local game and history
This commit is contained in:
@@ -114,9 +114,9 @@ const ChessBoard: React.FC<{
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative w-fit h-fit isolate border-[6px] border-black rounded-lg peer">
|
||||
<div className="relative w-fit h-fit max-w-full isolate border-[6px] border-black rounded-lg peer">
|
||||
<div
|
||||
className="grid grid-rows-8 grid-cols-8 aspect-square select-none"
|
||||
className="grid grid-rows-8 grid-cols-8 aspect-square select-none max-w-full"
|
||||
onClick={handleClick}
|
||||
style={{ width: `${gridSize}px` }}
|
||||
>
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
@import url("https://fonts.googleapis.com/css2?family=Ubuntu+Mono&display=swap");
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
font-family: "Ubuntu Mono", monospace;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
|
||||
+10
-5
@@ -6,7 +6,8 @@ import {
|
||||
useNavigate,
|
||||
} from "react-router-dom";
|
||||
import Home from "./routes/Home";
|
||||
import Game from "./routes/Game";
|
||||
import OnlineGame from "./routes/OnlineGame";
|
||||
import LocalGame from "./routes/LocalGame";
|
||||
import { useEffect } from "react";
|
||||
|
||||
const ErrorElement = () => {
|
||||
@@ -14,7 +15,7 @@ const ErrorElement = () => {
|
||||
|
||||
useEffect(() => naviagte("/"), []);
|
||||
|
||||
return <></>;
|
||||
return <h1 className="text-2xl bold loading">Redirecting to home page</h1>;
|
||||
};
|
||||
|
||||
const router = createBrowserRouter([
|
||||
@@ -23,8 +24,12 @@ const router = createBrowserRouter([
|
||||
element: <Home />,
|
||||
},
|
||||
{
|
||||
path: "/game",
|
||||
element: <Game />,
|
||||
path: "/game/online",
|
||||
element: <OnlineGame />,
|
||||
},
|
||||
{
|
||||
path: "/game/local",
|
||||
element: <LocalGame />,
|
||||
},
|
||||
{
|
||||
path: "*",
|
||||
@@ -35,7 +40,7 @@ const router = createBrowserRouter([
|
||||
// TODO: better UI
|
||||
// React's StricMode doesn't play well with how I implemented the socket connection
|
||||
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
||||
<div className="absolute top-0 left-0 right-0 bottom-0 w-full h-full bg-zinc-200 -z-10">
|
||||
<div className="absolute top-0 left-0 right-0 bottom-0 w-full h-full bg-zinc-600 -z-100">
|
||||
<RouterProvider router={router} />
|
||||
</div>
|
||||
);
|
||||
|
||||
+14
-17
@@ -25,7 +25,7 @@ const Home = () => {
|
||||
throw res;
|
||||
})
|
||||
.then((text) => {
|
||||
navigate("/game", { state: { id: text, color } });
|
||||
navigate("/game/online", { state: { id: text, color } });
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
@@ -43,7 +43,7 @@ const Home = () => {
|
||||
if (text == "invalid id") throw new Error("Invalid game ID");
|
||||
if (text == "full") throw new Error("Game already has 2 players");
|
||||
|
||||
navigate("/game", { state: { id, color: text } });
|
||||
navigate("/game/online", { state: { id, color: text } });
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
@@ -70,11 +70,16 @@ const Home = () => {
|
||||
<>
|
||||
<ErrorNorification key={errObj.error} {...errObj} />
|
||||
<div className="text-xl">
|
||||
<div className="absolute top-0 left-0 right-0 bottom-0 m-auto w-fit h-fit">
|
||||
<Modal>
|
||||
<ModalButton onClick={() => setCreate(true)}>Create Game</ModalButton>
|
||||
<ModalButton onClick={() => setJoin(true)}>Join Game</ModalButton>
|
||||
</Modal>
|
||||
<div className="absolute top-0 left-0 right-0 bottom-0 m-auto w-fit h-fit">
|
||||
<Modal>
|
||||
<ModalButton onClick={() => setCreate(true)}>
|
||||
Create Game
|
||||
</ModalButton>
|
||||
<ModalButton onClick={() => setJoin(true)}>Join Game</ModalButton>
|
||||
<ModalButton onClick={() => navigate("/game/local")}>
|
||||
Play locally
|
||||
</ModalButton>
|
||||
</Modal>
|
||||
</div>
|
||||
<Show when={create}>
|
||||
<Modal enableOverlay>
|
||||
@@ -102,16 +107,8 @@ const Home = () => {
|
||||
<label htmlFor="black">Black</label>
|
||||
</div>
|
||||
</div>
|
||||
<ModalButton
|
||||
onClick={createGame}
|
||||
>
|
||||
Start Game
|
||||
</ModalButton>
|
||||
<ModalButton
|
||||
onClick={() => setCreate(false)}
|
||||
>
|
||||
Cancel
|
||||
</ModalButton>
|
||||
<ModalButton onClick={createGame}>Start Game</ModalButton>
|
||||
<ModalButton onClick={() => setCreate(false)}>Cancel</ModalButton>
|
||||
</div>
|
||||
</Modal>
|
||||
</Show>
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { ReactNode, useState } from "react";
|
||||
import Chess, { Move } from "../../../server/src/engine";
|
||||
import ChessBoard from "../components/Chessboard";
|
||||
import { CaretRight, Plus, Repeat } from "../utils/icons";
|
||||
|
||||
const LocalGame = () => {
|
||||
const [chess] = useState(Chess.load());
|
||||
const [, rerender] = useState(false);
|
||||
|
||||
const makeMove = (move: Move) => {
|
||||
chess.makeMove(move);
|
||||
rerender((prev) => !prev);
|
||||
};
|
||||
|
||||
const history: ReactNode[] = new Array((chess.getHistory().length + 1) >> 1);
|
||||
|
||||
const chessHistory = chess.getHistory();
|
||||
|
||||
let idx = 0;
|
||||
for (let i = 1; i < chessHistory.length; i += 2)
|
||||
history[idx++] = (
|
||||
<div key={idx} className="flex flex-row gap-4">
|
||||
<span>{idx}.</span>
|
||||
<span>{chessHistory[i - 1].san}</span>
|
||||
<span>{chessHistory[i].san}</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (chessHistory.length % 2)
|
||||
history[idx++] = (
|
||||
<div key={idx} className="flex flex-row gap-4">
|
||||
<span>{idx}.</span>
|
||||
<span>{chessHistory.at(-1)?.san}</span>
|
||||
<span className="w-1"></span>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="mt-4 flex flex-row gap-4 justify-center">
|
||||
<ChessBoard chess={chess} makeMove={makeMove} />
|
||||
<div className="text-white grid grid-rows-[1fr,auto]">
|
||||
<div className="bg-zinc-800 p-4 rounded-t-lg">{history}</div>
|
||||
<div className="bg-black grid grid-cols-4 gap-5 p-4 rounded-b-lg">
|
||||
{/* TODO: */}
|
||||
<Button>
|
||||
<Plus />
|
||||
</Button>
|
||||
<Button>
|
||||
<Repeat />
|
||||
</Button>
|
||||
<Button className="rotate-180">
|
||||
<CaretRight />
|
||||
</Button>
|
||||
<Button>
|
||||
<CaretRight />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Button: React.FC<{ children: React.ReactNode; className?: string }> = ({
|
||||
children,
|
||||
className,
|
||||
}) => (
|
||||
<button
|
||||
className={
|
||||
"min-w-[1rem] mt-auto bg-zinc-800 flex justify-center items-center p-2 rounded-md " +
|
||||
(className ?? "")
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
|
||||
export default LocalGame;
|
||||
@@ -26,3 +26,42 @@ export const CircleExclamation = () => (
|
||||
<path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24V264c0 13.3-10.7 24-24 24s-24-10.7-24-24V152c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc.
|
||||
export const CaretRight = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 320 512"
|
||||
height="1em"
|
||||
>
|
||||
<path d="M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc.
|
||||
export const Repeat = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512"
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
width="1em"
|
||||
>
|
||||
<path d="M0 224c0 17.7 14.3 32 32 32s32-14.3 32-32c0-53 43-96 96-96H320v32c0 12.9 7.8 24.6 19.8 29.6s25.7 2.2 34.9-6.9l64-64c12.5-12.5 12.5-32.8 0-45.3l-64-64c-9.2-9.2-22.9-11.9-34.9-6.9S320 19.1 320 32V64H160C71.6 64 0 135.6 0 224zm512 64c0-17.7-14.3-32-32-32s-32 14.3-32 32c0 53-43 96-96 96H192V352c0-12.9-7.8-24.6-19.8-29.6s-25.7-2.2-34.9 6.9l-64 64c-12.5 12.5-12.5 32.8 0 45.3l64 64c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6V448H352c88.4 0 160-71.6 160-160z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc.
|
||||
export const Plus = () => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 448 512"
|
||||
stroke="currentColor"
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
>
|
||||
<path d="M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32V224H48c-17.7 0-32 14.3-32 32s14.3 32 32 32H192V432c0 17.7 14.3 32 32 32s32-14.3 32-32V288H400c17.7 0 32-14.3 32-32s-14.3-32-32-32H256V80z" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user