Skip to content

Commit

Permalink
Adding a Resign button
Browse files Browse the repository at this point in the history
  • Loading branch information
etnt committed Oct 11, 2024
1 parent e70d43e commit 8d94584
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 85 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ a web app with these technologies.

Start the server:

cd server
npm start

Start the client:
Expand Down
3 changes: 2 additions & 1 deletion client/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@

.new-game-button,
.suggest-button,
.go-back-button {
.go-back-button,
.resign-button {
padding: 10px 15px;
font-size: 16px;
cursor: pointer;
Expand Down
64 changes: 35 additions & 29 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ const App: React.FC = () => {
suggestedMove,
startNewGame,
undoLastMove,
setSuggestedMove
setSuggestedMove,
onPieceDrop,
onSquareClick,
gameStatus,
resign
} = useChessGame();
const { opponent, setOpponent, searchDepth, setSearchDepth, setStockfishDepth } = useOpponent();
const { ws } = useWebSocket(user);
Expand Down Expand Up @@ -140,17 +144,10 @@ const App: React.FC = () => {
}
};

const onPieceDrop = (sourceSquare: string, targetSquare: string) => {
// Implement move logic here
// This function should interact with your useChessGame hook to make a move
// For example:
// makeAMove(sourceSquare, targetSquare);
// Ensure that your hooks provide the necessary functions
return true;
};

const onSquareClick = (square: string) => {
// Implement any additional logic for square clicks here
const handleResign = () => {
if (gameStatus === 'active' && game.turn() === 'w') {
resign();
}
};

return (
Expand Down Expand Up @@ -192,6 +189,7 @@ const App: React.FC = () => {
}}>New Game</button>
<button className="suggest-button" onClick={requestSuggestion}>Suggest</button>
<button className="go-back-button" onClick={undoLastMove}>Go Back</button>
<button className="resign-button" onClick={handleResign} disabled={gameStatus !== 'active' || game.turn() !== 'w'}>Resign</button>
</div>
<div className="board-evaluation-history">
<div className="board-and-evaluation">
Expand All @@ -216,24 +214,32 @@ const App: React.FC = () => {
/>
</div>
</div>
<div className="online-players">
<h3>Online Players</h3>
{onlineUsers.length > 0 ? (
<ul>
{onlineUsers
.filter((onlineUser: OnlineUser) => onlineUser.username !== user.username)
.map((onlineUser: OnlineUser) => (
<li className="challenge-user" key={onlineUser.id}>
{onlineUser.username}
<button className="challenge-button" onClick={() => handleChallenge(onlineUser.username)}>Challenge</button>
</li>
))}
</ul>
) : (
<p>No other players online</p>
)}
<div className="game-status">
{gameStatus === 'active' && <p>Game in progress</p>}
{gameStatus === 'resigned' && <p>White resigned. Black wins!</p>}
{gameStatus === 'checkmate' && <p>Checkmate! {game.turn() === 'w' ? 'Black' : 'White'} wins!</p>}
{gameStatus === 'draw' && <p>Game ended in a draw</p>}
</div>
{incomingChallenges.length > 0 && (
{opponent !== 'stockfish' && (
<div className="online-players">
<h3>Online Players</h3>
{onlineUsers.length > 0 ? (
<ul>
{onlineUsers
.filter((onlineUser: OnlineUser) => onlineUser.username !== user.username)
.map((onlineUser: OnlineUser) => (
<li className="challenge-user" key={onlineUser.id}>
{onlineUser.username}
<button className="challenge-button" onClick={() => handleChallenge(onlineUser.username)}>Challenge</button>
</li>
))}
</ul>
) : (
<p>No other players online</p>
)}
</div>
)}
{incomingChallenges.length > 0 && opponent !== 'stockfish' && (
<div className="incoming-challenges">
<h4>Incoming Challenges:</h4>
{incomingChallenges.map((challenger, index) => (
Expand Down
86 changes: 32 additions & 54 deletions client/src/hooks/useChessGame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,17 @@ import { useState, useEffect } from 'react';
import { Chess, Square } from 'chess.js';
import { GetMoveRequest, GetMoveResponse, MoveRequest, MoveResponse } from '../../../shared/types';

/**
* Custom React hook for managing a chess game.
*
* This hook encapsulates the logic for:
* - Managing the game state
* - Handling user moves
* - Interacting with an AI opponent
* - Updating the game history
* - Managing the game's evaluation
*
* @returns An object containing the game state and functions to interact with the game.
*/
export function useChessGame() {
// The current state of the chess game
const [game, setGame] = useState(new Chess());

// The current board position in Forsyth-Edwards Notation (FEN)
const [fen, setFen] = useState(game.fen());

// The currently selected piece on the board (if any)
const [selectedPiece, setSelectedPiece] = useState<Square | null>(null);

// A formatted string representation of the move history
const [moveHistory, setMoveHistory] = useState('');

// An array of all moves made in the game in Standard Algebraic Notation (SAN)
const [fullHistory, setFullHistory] = useState<string[]>([]);

// The current evaluation of the board position (positive favors white, negative favors black)
const [evaluation, setEvaluation] = useState(0);

// A suggested move for the current player (if any)
const [suggestedMove, setSuggestedMove] = useState(null);

// The type of opponent (e.g., 'stockfish' for AI, 'human' for human player)
const [opponent, setOpponent] = useState<string>('stockfish');
const [gameStatus, setGameStatus] = useState<'active' | 'resigned' | 'checkmate' | 'draw'>('active');

/**
* Effect hook to update the game state and request moves from the AI opponent.
*
* This effect runs whenever the game state or move history changes.
* It updates the FEN representation of the board and the move history.
* If it's black's turn and the opponent is not human, it requests a move from the AI.
*/
useEffect(() => {
setFen(game.fen());
updateMoveHistory();
Expand All @@ -54,14 +21,8 @@ export function useChessGame() {
}
}, [game, fullHistory, opponent]);

/**
* Attempts to make a move on the chess board.
*
* @param from - The starting square of the move
* @param to - The ending square of the move
* @returns The move in Standard Algebraic Notation (SAN) if successful, null otherwise
*/
const makeAMove = async (from: Square, to: Square) => {
const makeAMove = (from: Square, to: Square) => {
console.log('makeAMove called with:', from, to);
const gameCopy = new Chess(game.fen());
try {
const result = gameCopy.move({ from, to, promotion: 'q' });
Expand All @@ -72,6 +33,7 @@ export function useChessGame() {
setFullHistory(prevHistory => [...prevHistory, result.san]);
setSuggestedMove(null);
setSelectedPiece(null);
updateGameStatus(gameCopy);
return result.san;
}
} catch (error) {
Expand All @@ -80,16 +42,6 @@ export function useChessGame() {
return null;
};

/**
* Handles the click event on a chess square.
*
* This function manages the piece selection and move execution process:
* - If no piece is selected, it selects a piece of the current player's color.
* - If a piece is already selected, it attempts to make a move to the clicked square.
* - If the move is invalid, it either selects a new piece or deselects the current piece.
*
* @param square - The clicked square on the chess board
*/
const onSquareClick = (square: Square) => {
if (selectedPiece === null) {
const piece = game.get(square);
Expand All @@ -112,14 +64,19 @@ export function useChessGame() {
};

const onPieceDrop = (sourceSquare: string, targetSquare: string) => {
console.log('onPieceDrop called with:', sourceSquare, targetSquare);
if (selectedPiece !== null) {
console.log('Piece already selected, returning false');
return false;
}

if (isValidSquare(sourceSquare) && isValidSquare(targetSquare)) {
console.log('Valid squares, attempting to make move');
const result = makeAMove(sourceSquare as Square, targetSquare as Square);
console.log('Move result:', result);
return result !== null;
}
console.log('Invalid squares, returning false');
return false;
};

Expand Down Expand Up @@ -160,6 +117,7 @@ export function useChessGame() {
setFen(newGame.fen());
setFullHistory(prevHistory => [...prevHistory, result.san]);
setEvaluation(evaluation);
updateGameStatus(newGame);

console.log('Move applied, new FEN:', newGame.fen());

Expand Down Expand Up @@ -190,8 +148,8 @@ export function useChessGame() {
setSuggestedMove(null);
setSelectedPiece(null);
setOpponent(selectedOpponent);
setGameStatus('active');

// If the opponent is not 'human', request a move for black
if (selectedOpponent !== 'human' && newGame.turn() === 'b') {
setTimeout(() => requestMove(), 500);
}
Expand All @@ -215,6 +173,7 @@ export function useChessGame() {
setSuggestedMove(null);
setSelectedPiece(null);
updateMoveHistory();
updateGameStatus(newGame);

if (newHistory.length > 0) {
const newEvaluation = await requestEvaluation(newGame.fen());
Expand All @@ -229,6 +188,23 @@ export function useChessGame() {
}
};

const resign = () => {
if (game.turn() === 'w') {
setGameStatus('resigned');
console.log('White resigned');
} else {
console.log('Only White can resign');
}
};

const updateGameStatus = (currentGame: Chess) => {
if (currentGame.isCheckmate()) {
setGameStatus('checkmate');
} else if (currentGame.isDraw()) {
setGameStatus('draw');
}
};

return {
game,
fen,
Expand All @@ -238,14 +214,16 @@ export function useChessGame() {
evaluation,
suggestedMove,
opponent,
gameStatus,
makeAMove,
onSquareClick,
onPieceDrop,
requestMove,
startNewGame,
undoLastMove,
setSuggestedMove,
setOpponent
setOpponent,
resign
};
}

Expand Down
4 changes: 4 additions & 0 deletions shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,8 @@ export type WebSocketMessage =
| {
type: 'start_game';
opponent: string;
}
| {
type: 'onlineUsers';
users: OnlineUser[];
};

0 comments on commit 8d94584

Please sign in to comment.