Skip to content

Commit

Permalink
Merge pull request #57 from sownfam/add-forfeit
Browse files Browse the repository at this point in the history
feat: add simple forfeit
  • Loading branch information
nullchilly authored Dec 23, 2023
2 parents 9e4a376 + f790d07 commit 47ab7d2
Show file tree
Hide file tree
Showing 11 changed files with 240 additions and 7 deletions.
1 change: 1 addition & 0 deletions alembic/versions/7ce64b27e610_.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ def upgrade():
sa.Column('created_at', sa.DateTime(), server_default=sa.text('CURRENT_TIMESTAMP')),
sa.Column('end_at', sa.DateTime(), nullable=True),
sa.Column('status', sa.Boolean(), nullable=False),
# 2: Black win, 1: White win, 0: Draw
sa.Column('result', sa.Integer(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=False, server_default=sa.text('CURRENT_TIMESTAMP')),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
Expand Down
19 changes: 17 additions & 2 deletions api/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ async def play_chess(sid, msg):
await sio.emit("play-chess", json.dumps(message), room=sid)
return

if (game_states[gameID]['status'] is True):
message = {"ok": False, "error": "Game ended"}
await sio.emit("play-chess", json.dumps(message), room=sid)
return

# Switch timer
game_states[gameID]['time']['wPlayer'].pause_time()
game_states[gameID]['time']['bPlayer'].run_clock()
Expand All @@ -108,8 +113,6 @@ async def play_chess(sid, msg):
await sio.emit("play-chess", json.dumps(message), room=sid)

# Clean up after game is ended, TODO: Check auth-user?


@sio.on("end-game")
async def end_game(sid, msg):
data = json.loads(msg)
Expand All @@ -122,6 +125,16 @@ async def end_game(sid, msg):
message = "Successfully cleaned up"
await sio.emit("end-game", message, room=sid)

@sio.on("user-forfeit")
async def user_forfeit(sid, msg):
data = json.loads(msg)
gameID = data["id"]
if (gameID in game_states):
game_states[gameID]['status'] = True
game_states[gameID]['result'] = 2 # Black win

message = "White forfeited" # TODO: Return match info for modal rendering?
await sio.emit("user-forfeit", message, room=sid)

@sio.on("start-game")
async def start_game(sid, msg):
Expand All @@ -142,6 +155,8 @@ async def start_game(sid, msg):
current_state['engine'] = engine
current_state['board'] = board
current_state['limit'] = limit
current_state['status'] = False # Unfinished game
current_state['result'] = 3 # Unknown result

timer_dict = dict()
timer_dict['wPlayer'] = wTimer
Expand Down
2 changes: 1 addition & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const Layout = ({ children }: LayoutType) => {
<html lang="en">
<body>
<ThemeProvider attribute="class" defaultTheme="light">
<div style={{display: 'flex', height: '100vh'}}>
<div style={{display: 'flex', minHeight: '100vh'}}>
<Navbar />
<div style={{flexGrow: 9, backgroundColor: '#57903C'}}>
{children}
Expand Down
12 changes: 12 additions & 0 deletions src/components/ChessGame/ChessGame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { useEffect, useState } from "react";
import { BotProps } from "@/types";

import PlayerTimer from "@/components/PlayerTimer/PlayerTimer";
import ModalEndGame from "../Modal/ModalEndGame";

type ChessGameType = {
id: string;
Expand All @@ -24,6 +25,7 @@ const ChessGame = ({ id, type }: ChessGameType) => {
// moves,
playing,
customSquares,
winner,

wPlayerTimeLeft,
isWPlayerActive,
Expand All @@ -35,10 +37,12 @@ const ChessGame = ({ id, type }: ChessGameType) => {
undoMove,
startGame,
resetGame,
forfeitGame,
} = useChessSocket({ type, id });

const [botList, setBotList] = useState<BotProps[]>([]);
const [bot, setBot] = useState<BotProps>({ id: "0", name: "shark" });
const [isModalEndGameOpen, setOpenModalEndGame] = useState<boolean>(true);

const onSelectBot = (bot: BotProps) => {
setBot(bot);
Expand Down Expand Up @@ -111,10 +115,18 @@ const ChessGame = ({ id, type }: ChessGameType) => {
<Controls
startGame={startGame}
resetGame={resetGame}
forfeitGame={forfeitGame}
undoMove={undoMove}
/>
</div>
</div>
{winner === "unknown" ? null : (
<ModalEndGame
winner={winner}
isOpen={isModalEndGameOpen}
setOpen={setOpenModalEndGame}
/>
)}
</div>
);
};
Expand Down
9 changes: 7 additions & 2 deletions src/components/Controls/Controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,27 @@ import { Button } from '../ui/button';

type ControlsType = {
startGame: () => void;
resetGame: () => void;
resetGame: () => void; // TODO: Delete
undoMove: () => void;
forfeitGame: () => void;
};

const Controls = ({
startGame,
resetGame,
undoMove,
forfeitGame,
}: ControlsType) => {
return (
<div className="flex gap-4 mt-4">
<Button className="w-full" onClick={() => startGame()}>
Start Game
</Button>
<Button className="w-full" onClick={() => resetGame()}>
{/* <Button className="w-full" onClick={() => resetGame()}>
Reset Game
</Button> */}
<Button className="w-full" onClick={() => forfeitGame()}>
Forfeit
</Button>
<Button className="w-full" onClick={() => undoMove()}>
Undo
Expand Down
73 changes: 73 additions & 0 deletions src/components/Modal/ModalEndGame.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Button, Modal } from "antd";
import { useState } from "react";
import Checkmate from "../icons/Checkmate";
import { useRouter } from "next/navigation";
import Crown from "../icons/Crown";
import { WINNER } from "@/helpers/types";
import Handshake from "../icons/Handshake";

type Props = {
winner: WINNER;
isOpen: boolean;
setOpen: (isOpen: boolean) => void;
};

export default function ModalEndGame({ winner, isOpen, setOpen }: Props) {
const router = useRouter();
const [loading, setLoading] = useState(false);
// const [open, setOpen] = useState(false);

const handleOk = () => {
router.push("/");
};

const handleCancel = () => {
setOpen(false);
};

return (
<>
<Modal
open={isOpen}
// Taking for granted that user always play as white
title={
winner === "black" ? <span> You lost </span> : <span> You won </span>
}
onOk={handleOk}
onCancel={handleCancel}
footer={[
<Button key="back" onClick={handleCancel}>
Review
</Button>,
<Button
key="link"
type="primary"
loading={loading}
onClick={handleOk}
className="bg-blue-500"
>
Home
</Button>,
]}
>
<div className="flex flex-col justify-center items-center">
{winner === "black" ? (
<>
<Checkmate />
<span> Try again next time!</span>
</>
) : winner === "draw" ? (
<>
<Handshake /> <span> What a game! </span>
</>
) : (
<>
<Crown />
<span> Congratulations!!! </span>
</>
)}
</div>
</Modal>
</>
);
}
29 changes: 29 additions & 0 deletions src/components/icons/Checkmate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from "react";

type Props = {
width?: string;
height?: string;
};

function Checkmate({ height, width }: Props) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="200"
height="200"
fill="#a20c02"
stroke="#a20c02"
version="1.1"
viewBox="-5.93 -5.93 71.19 71.19"
xmlSpace="preserve"
>
<g>
<path d="M26.377 17.74a.997.997 0 00.668-.256l3.326-2.989a1 1 0 10-1.336-1.488l-3.326 2.989a1 1 0 00.668 1.744zM32.999 11.362a.997.997 0 00.668-.256l3.326-2.989a1 1 0 10-1.336-1.488l-3.326 2.989a1 1 0 00.668 1.744zM17.999 11.352a1 1 0 00.678-.265l3.676-3.39a1 1 0 00-1.355-1.471l-3.676 3.39a1 1 0 00.677 1.736zM19.47 15.917a.998.998 0 001.41.118l3.818-3.229a.999.999 0 10-1.291-1.527l-3.818 3.229a1 1 0 00-.119 1.409zM27.108 10.461a.995.995 0 00.645-.236l1.527-1.291a.999.999 0 10-1.291-1.527l-1.527 1.291a.999.999 0 00.646 1.763zM30.699 19.939a.996.996 0 001.411.091l3.757-3.3a1 1 0 10-1.32-1.502l-3.757 3.3a1 1 0 00-.091 1.411zM37.461 14a.996.996 0 001.411.091l1.113-.978a1 1 0 10-1.32-1.502l-1.113.978A.997.997 0 0037.461 14zM36.365 20.733a1 1 0 101.336 1.488l3.326-2.989a1 1 0 10-1.336-1.488l-3.326 2.989z"></path>
<path d="M37.682 27.244L32.159 40.118 33.925 41.074 39.592 27.866z"></path>
<path d="M57.507 29.225a2.999 2.999 0 00-3.941 1.574l-.219.511a3.963 3.963 0 00-1.618-1.3 3.966 3.966 0 00-1.575-.325c-.898 0-1.739.307-2.422.829l-6.23-2.027-5.81 13.542 5.77 3.122c.04.355.109.708.246 1.048a3.967 3.967 0 002.137 2.191 3.966 3.966 0 001.575.325c.161 0 .319-.016.476-.035l-.214.498a2.984 2.984 0 00-.029 2.297 2.983 2.983 0 001.604 1.644 2.998 2.998 0 003.94-1.574l7.886-18.38a3.005 3.005 0 00-1.576-3.94zM35.772 26.623l-7.638-2.486.16-.372a3.503 3.503 0 00-3.216-4.88 3.489 3.489 0 00-2.852 1.486l-5.034-3.625a2.489 2.489 0 00-3.742 1.039l-.845 1.97-1.556-.668.247-.576a2.432 2.432 0 00.032-1.854 2.208 2.208 0 00-1.181-1.221l-1.913-.821c-1.127-.482-2.515.113-3.02 1.286l-.247.577-.463-.198c-1.127-.48-2.516.112-3.02 1.286L.201 20.557a2.416 2.416 0 00-.065 1.767 2.202 2.202 0 001.213 1.308l.463.198-.247.577c-.516 1.202-.001 2.581 1.148 3.074l1.913.82c.273.117.563.177.862.177.929 0 1.776-.573 2.157-1.462l.247-.577 1.557.668-.845 1.969c-.3.697-.267 1.482.09 2.151.357.671.99 1.136 1.736 1.276l6.09 1.15a3.474 3.474 0 00.181 1.977 3.477 3.477 0 001.87 1.918 3.497 3.497 0 004.596-1.836l.16-.373 7.065 3.822 5.38-12.538zM6.844 23.812l-1.036 2.415c-.074.173-.218.251-.319.251l-1.987-.835c-.116-.05-.186-.246-.099-.447l1.036-2.415-2.302-.987a.222.222 0 01-.115-.138.435.435 0 01.018-.31l1.283-2.992a.365.365 0 01.351-.238l.041.002 2.301.987 1.036-2.415a.378.378 0 01.393-.237l1.914.821c.05.021.087.063.111.125a.42.42 0 01-.013.322L8.42 20.136l3.395 1.457-.788 1.838-.788 1.838-3.395-1.457zM19.36 35.711a1.491 1.491 0 01-.801-.822 1.486 1.486 0 01.013-1.145l5.126-11.95a1.5 1.5 0 012.757 1.184l-.231.539h0l-2.332 5.435-2.563 5.974a1.523 1.523 0 01-1.969.785z"></path>
</g>
</svg>
);
}

export default Checkmate;
37 changes: 37 additions & 0 deletions src/components/icons/Crown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from "react";

type Props = {
width?: string;
height?: string;
};

function Crown({ width, height }: Props) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="200"
height="200"
fill="none"
stroke="#d4de17"
viewBox="-2.4 -2.4 28.8 28.8"
>
<rect
width="28.8"
height="28.8"
x="-2.4"
y="-2.4"
fill="#fafafa"
strokeWidth="0"
rx="0"
></rect>
<path
fill="#7c9405"
fillRule="evenodd"
d="M11 5a1 1 0 112 0 1 1 0 01-2 0zm2.327 2.691a3 3 0 10-2.654 0L8.506 13.47l-3.44-2.294a3 3 0 10-2.874.714L3.969 19l-.628 2.515A2 2 0 005.28 24h13.438a2 2 0 001.94-2.485L20.032 19l1.777-7.11a3.001 3.001 0 10-2.874-.715l-3.44 2.294-2.167-5.778zM21 10s0 0 0 0a1 1 0 100-2 1 1 0 000 2zM4 9a1 1 0 01-1 1h0a1 1 0 111-1zm1.28 13l.5-2h12.44l.5 2H5.28zm14.122-8.733L18.22 18H5.781l-1.184-4.733 3.848 2.565a1 1 0 001.491-.48L12 9.847l2.064 5.503a1 1 0 001.49.481l3.848-2.565z"
clipRule="evenodd"
></path>
</svg>
);
}

export default Crown;
42 changes: 42 additions & 0 deletions src/components/icons/Handshake.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from "react";

function Handshake() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="200"
height="200"
aria-hidden="true"
className="iconify iconify--emojione"
viewBox="0 0 64 64"
>
<g>
<path
fill="#ffdd67"
d="M56.3 33.4l-1.2-18.1-8.3 5.1c-4.1-.7-5.2-.7-8.4-2.2-1.9-.9-4.3-.3-5.5.4-1.8-1.9-13.5.9-14.8 1.5C14.6 19 9.4 15 9.4 15L7.6 35.4l2.4.6c.2.7.8 2.4.8 2.4-2.2 2.8.4 6.1 2.9 5-1.1 2.8 1.8 5.4 4.1 3.6-.3 2.6 2.4 4.5 4.4 2.7-.9 3.1 2.6 5.4 4.7 2.9 1.5 1 2.9 1.8 4.1 2.2 2.1.9 4.4-1.1 4-3.6 2.4 2.8 6 .1 5.2-3 2.7 2.1 6-1.4 4.3-4.5 3 1.7 5.6-2.9 2.7-5.9l9.1-4.4"
></path>
<g fill="#eba352">
<path d="M13.9 36.3c1.5.7 2.5 3 .9 5l-.7.8c-.8 1-1.6 1.3-2.4 1.3.6.3 1.3.3 2 0 .1-.3.3-.7.6-1l.7-.8c.4-.5.9-.9 1.3-1.1.8-2.1-.6-4.1-2.4-4.2M25.3 46.2c1.6.4 2.8 2.5 1.6 4.8l-.6.9c-.6 1.1-1.4 1.6-2.2 1.7 1.1.3 2.3-.1 3.1-1.6l.6-.9c1.5-2.8-.4-5.1-2.5-4.9M18.3 45.4l1.6-2.5c-.1-1.4-1.3-2.6-2.5-2.7 1.5.7 2.5 3 .9 5l-.7.8c-.8 1-1.6 1.3-2.5 1.3.8.4 1.7.3 2.6-.3.1-.5.3-1.1.6-1.6M22.6 40.9c1.6.5 2.7 2.7 1.3 4.9l-1.9 3c-.7 1.1-1.5 1.5-2.3 1.5 1 .3 2.3 0 3.2-1.4l1.9-3c1.6-2.6-.2-5.1-2.2-5M35.9 43.4c.1 0 1.3-.2 1.7-.2-1.5-1.5-3.9-.3-5.3-1.1 1 1.7 3.1 1.3 3.3 1.3l2.4 3.1c2 2.5.4 5.4-1.7 5.7 2.2.7 4.4-1.6 3.7-4.1-.1-.1-4.1-4.7-4.1-4.7M38.3 37.6s1.5-.3 2.8-.3c-1.5-1.2-3.8-.1-5.3-1.1.6 1.3 1.9 1.3 2.3 1.4l4.6 5.3c2.1 2.4.6 5.3-1.5 5.8 2.4.7 4.7-2.4 3.2-5.1-1-.4-6.1-6-6.1-6"></path>
<path d="M35.1 49.1c-1.5-1.2-3.6-.3-4.5-.9.5 1.4 2.4 1.1 2.5 1.1 1.7 2.6-.1 5.3-2.2 5.5 2.1.9 4.4-1.1 4-3.6-.1-.1-1.5-1.9-1.5-1.9.2-.1 1.3-.2 1.7-.2M55.2 31.7s-1.7 3.5-8.3 5.4c-.9-1.1-2.8-3.3-5-5.2 1.4-.3 2.1-1.4 3.2-1.6-.4-.4-1.8-.2-2.9.2-2 .7-6.6-2.5-6.6-2.5l-3.8.5c-2.7 2.6-8.5 4-8.8 0-.2-2.8 5.9-4.7 7.8-6.5 2.4-2.3 4.5-5.2 8.4-3.2 3.2 1.6 4.5 1.5 8.9 2.3 1.5.3 2.5 1 3.6 2.5-.9-2.1-2.2-3-3.8-3.3l7-3.3.1-1.7-8.3 4.7c-3.8-.6-5.2-.6-8.3-2.1-2-1-3.6-.7-4.9.1-2.4-1.4-3.8-1.1-4.7-.9-2.8.9-7.3 1-11.3 2.6.7.2 1.4.4 1.9.4 3.9-1.2 8-1.1 9.9-1.8.6-.2 1.8-.2 3.1.5-.8.7-1.5 1.5-2.3 2.3-1.9 1.8-7.7 4.4-7.3 7.7.2 1.6 2.4 5.8 9.7 1.1l3.1-.4s5.7 2.7 11 8.9c2.1 2.4.8 5-1.3 5.6 2.7.5 5-3.3 2.4-5.9l6.3-3.4 1.2-3"></path>
</g>
<path
fill="#3b946f"
d="M53.4 16.3l3.3 17.1-2.7 1.2c-1.4-3.7-3.5-12.6-3-16.9l2.4-1.4"
></path>
<path
fill="#eba352"
d="M11.1 38c-.5-1.2-.8-2.8-.8-2.8l-2.5-1.7-.2 1.9 2.2.6c.3 1 .6 1.8 1 2.4l.3-.4"
></path>
<path
fill="#3b946f"
d="M6.1 35.1l5.4-18.7 2.5 1.7c.4 2.8-4.2 15.6-5.9 17.5l-2-.5"
></path>
<g fill="#47b892">
<path d="M53.3 16.2c-.8.5 2.1 17.8 3.3 17.3 3.8-1.7 5.5-2.2 5.5-2.2V10.7c-.1 0-2.4 1.7-8.8 5.5M2 9v25.5s1.8.3 4.1.8c1.3.3 6-18.5 5.7-18.8C8.4 14.2 2 9 2 9"></path>
</g>
</g>
</svg>
);
}

export default Handshake;
4 changes: 3 additions & 1 deletion src/helpers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,6 @@ export type PuzzleInfo = {
passRate: number;
attempts: number;
themes: string[];
};
};

export type WINNER = "black" | "white" | "draw" | "unknown";
Loading

0 comments on commit 47ab7d2

Please sign in to comment.