Skip to content

Commit

Permalink
Added spectator support
Browse files Browse the repository at this point in the history
Added support for spectators to view puzzle and Ai games.
This required a 1 second delay, so the games feel a bit weird, but they work.
Without it, the spectators would error because they had too many move commands at once.
  • Loading branch information
ymmot239 committed Nov 9, 2024
1 parent 5191b5f commit f21eb54
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 49 deletions.
4 changes: 1 addition & 3 deletions src/client/game/game.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,10 @@ export function Game(): JSX.Element {
return (
<>
<NavbarMenu

sendMessage={sendMessage}

side={side}
difficulty={data.difficulty}

AiDifficulty={data.AiDifficulty}
setRotation={setRotation}
/>
<div id="body-container">
Expand Down
80 changes: 50 additions & 30 deletions src/client/game/navbar-menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import { Dispatch } from "react";
interface NavbarMenuProps {
sendMessage: SendMessage;
side: Side;
difficulty?: number;
difficulty?: string;
AiDifficulty?: number;
setRotation: Dispatch<React.SetStateAction<number>>; //set state type
}

Expand All @@ -33,6 +34,16 @@ export function NavbarMenu(props: NavbarMenuProps): JSX.Element {
<Button minimal disabled text={"rating: " + props.difficulty} />
: null;

const AiArray = ["Baby", "Beginner", "Intermediate", "Advances"];
const AiDifficultyButton =
props.AiDifficulty ?
<Button
minimal
disabled
text={"AI Difficulty: " + AiArray[props.AiDifficulty]}
/>
: null;

/** create navbar rotate button */
const rotateButton =
props.side === Side.SPECTATOR ?
Expand All @@ -46,44 +57,53 @@ export function NavbarMenu(props: NavbarMenuProps): JSX.Element {
});
}}
/>
: "";
: undefined;

const resignButton =
props.side === Side.SPECTATOR ?
undefined
: <Button
icon="flag"
minimal
text="Resign"
intent="danger"
onClick={async () => {
props.sendMessage(
new GameInterruptedMessage(
props.side === Side.WHITE ?
GameInterruptedReason.WHITE_RESIGNED
: GameInterruptedReason.BLACK_RESIGNED,
),
);
}}
/>;

const drawButton =
props.side === Side.SPECTATOR ?
undefined
: <Button
icon="pause"
minimal
text="Draw"
intent="danger"
onClick={async () => {
props.sendMessage(
new GameHoldMessage(GameHoldReason.DRAW_CONFIRMATION),
);
}}
/>;

return (
<Navbar>
<NavbarGroup>
<NavbarHeading>ChessBot</NavbarHeading>
<NavbarDivider />
<Button
icon="flag"
minimal
text="Resign"
intent="danger"
onClick={async () => {
props.sendMessage(
new GameInterruptedMessage(
props.side === Side.WHITE ?
GameInterruptedReason.WHITE_RESIGNED
: GameInterruptedReason.BLACK_RESIGNED,
),
);
}}
/>
<Button
icon="pause"
minimal
text="Draw"
intent="danger"
onClick={async () => {
props.sendMessage(
new GameHoldMessage(
GameHoldReason.DRAW_CONFIRMATION,
),
);
}}
/>
{resignButton}
{drawButton}
</NavbarGroup>
<NavbarGroup align="right">
{difficultyButton}
{AiDifficultyButton}
{rotateButton}
<h3>{props.side}</h3>
<Button icon="cog" minimal onClick={() => navigate("/debug")} />
Expand Down
6 changes: 3 additions & 3 deletions src/client/setup/setup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ export function Setup(): JSX.Element {
/>
: null}
{setupType === SetupType.PUZZLE ?
<SetupPuzzle />
: null}
</SetupBase>
<SetupPuzzle />
: null}
</SetupBase>
);
} else {
return <Navigate to="/lobby" />;
Expand Down
2 changes: 2 additions & 0 deletions src/common/chess-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ export class ChessEngine {
* @returns the move that was made.
*/
makeMove(move: Move): Move {
//const yay = this.chess.history[this.chess.history.length-1];
//if(yay!==undefined && !(move.from === yay.from || move.to === yay.to))
this.chess.move(move);
return move;
}
Expand Down
58 changes: 45 additions & 13 deletions src/server/api/game-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ export abstract class GameManager {
protected hostSide: Side,
//true if host and client get reversed
protected reverse: boolean,
) {}
) {
socketManager.sendToAll(new GameStartedMessage());
}

public isGameEnded(): boolean {
return (
Expand Down Expand Up @@ -83,8 +85,8 @@ export class HumanGameManager extends GameManager {
) {
super(chess, socketManager, hostSide, reverse);
// Notify other client the game has started
clientManager.sendToClient(new GameStartedMessage());
clientManager.sendToSpectators(new GameStartedMessage());
//clientManager.sendToClient(new GameStartedMessage());
//clientManager.sendToSpectators(new GameStartedMessage());
}

public handleMessage(message: Message, id: string): void {
Expand Down Expand Up @@ -176,7 +178,7 @@ export class HumanGameManager extends GameManager {

export class ComputerGameManager extends GameManager {
// The minimum amount of time to wait responding with a move.
MINIMUM_DELAY = 500;
MINIMUM_DELAY = 600;

constructor(
chess: ChessEngine,
Expand All @@ -195,7 +197,9 @@ export class ComputerGameManager extends GameManager {

public handleMessage(message: Message, id: string): void {
if (message instanceof MoveMessage) {
this.socketManager.sendToAll(new MoveMessage(message.move));
this.chess.makeMove(message.move);

SaveManager.saveGame(
id,
"ai",
Expand All @@ -215,8 +219,10 @@ export class ComputerGameManager extends GameManager {
const move = this.chess.makeAiMove(this.difficulty);
const elapsedTime = Date.now() - startTime;
// If elapsed time is less than minimum delay, timeout is set to 1ms
new MoveMessage(move);
setTimeout(() => {
this.socketManager.sendToSocket(id, new MoveMessage(move));
//this.socketManager.sendToSocket(id, new MoveMessage(move));
this.socketManager.sendToAll(new MoveMessage(move));
}, this.MINIMUM_DELAY - elapsedTime);
if (this.isGameEnded()) {
SaveManager.endGame(id, "ai");
Expand All @@ -225,13 +231,22 @@ export class ComputerGameManager extends GameManager {
this.gameInterruptedReason = message.reason;
SaveManager.endGame(id, "ai");
// Reflect end game reason back to client
this.socketManager.sendToSocket(id, message);
//this.socketManager.sendToSocket(id, message);
this.socketManager.sendToAll(message);
}
}

public getGameState(clientType: ClientType): object {
return {
...super.getGameState(clientType),
AiDifficulty: this.difficulty,
};
}
}

export class PuzzleGameManager extends GameManager {
private moveNumber: number = 0;
MINIMUM_DELAY = 600;

constructor(
chess: ChessEngine,
Expand All @@ -253,51 +268,68 @@ export class PuzzleGameManager extends GameManager {
return this.difficulty;
}

public handleMessage(message: Message, id: string): void {
public handleMessage(message: Message): void {
if (message instanceof MoveMessage) {
//if the move is correct
if (
this.moves[this.moveNumber].from === message.move.from &&
this.moves[this.moveNumber].to === message.move.to
) {
this.socketManager.sendToAll(new MoveMessage(message.move));
this.chess.makeMove(message.move);
this.moveNumber++;

//if there is another move, make it
if (this.moves[this.moveNumber]) {
this.chess.makeMove(this.moves[this.moveNumber]);

/*
this.socketManager.sendToSocket(
id,
new MoveMessage(this.moves[this.moveNumber]),
);
);*/
setTimeout(()=>{
this.socketManager.sendToAll(
new MoveMessage(this.moves[this.moveNumber]),
);
this.moveNumber++;
},this.MINIMUM_DELAY)

}
else{
this.moveNumber++;
}
this.moveNumber++;
}

//send an undo message
else {
/*
this.socketManager.sendToSocket(
id,
new SetChessMessage(this.chess.fen),
);*/
this.socketManager.sendToAll(
new SetChessMessage(this.chess.fen),
);
}

//send a finished message
if (this.isGameEnded()) {
const gameEnd = this.getGameEndReason();
if (gameEnd) {
this.socketManager.sendToSocket(
/*this.socketManager.sendToSocket(
id,
new GameEndMessage(gameEnd),
);
);*/
this.socketManager.sendToAll(new GameEndMessage(gameEnd));
}
}
} else if (
message instanceof (GameInterruptedMessage || GameEndMessage)
) {
this.gameInterruptedReason = message.reason;
// Reflect end game reason back to client
this.socketManager.sendToSocket(id, message);
//this.socketManager.sendToSocket(id, message);
this.socketManager.sendToAll(message);
}
}

Expand Down

0 comments on commit f21eb54

Please sign in to comment.