Javascript cleanup
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
const ChessAPI = {
|
||||
async joinGame() {
|
||||
const response = await fetch('/api/chess/JoinGame');
|
||||
return await response.json();
|
||||
},
|
||||
|
||||
async createCPUGame(difficulty) {
|
||||
const response = await fetch(`/api/chess/new/${difficulty}`);
|
||||
return await response.json();
|
||||
},
|
||||
|
||||
async getGameState(gameId) {
|
||||
const response = await fetch(`/api/chess/${gameId}`);
|
||||
return await response.json();
|
||||
},
|
||||
|
||||
async getLegalMoves(pieceId) {
|
||||
const response = await fetch(`/api/chess/${GameState.currentGameId}/legalMoves/${pieceId}`);
|
||||
if (!response.ok) throw new Error("API failed");
|
||||
return await response.json();
|
||||
},
|
||||
|
||||
async makeMove(moveDto) {
|
||||
const response = await fetch("/api/chess/move", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(moveDto)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (!response.ok || !result.success) {
|
||||
throw new Error(result.message || "Invalid move or not your turn.");
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
async handleMove(targetRow, targetCol) {
|
||||
if (!GameState.selectedPiece) return;
|
||||
|
||||
const isPawn = GameState.selectedPiece.type === 0;
|
||||
const reachedEnd = (Number(GameState.selectedPiece.color) === 0 && Number(targetRow) === 0) ||
|
||||
(Number(GameState.selectedPiece.color) === 1 && Number(targetRow) === 0);
|
||||
|
||||
let choice = null;
|
||||
|
||||
if (isPawn && reachedEnd) {
|
||||
choice = await ChessModals.promptPromotion();
|
||||
if (!choice) return;
|
||||
}
|
||||
|
||||
[targetRow, targetCol] = ChessUtils.flipCoordinates(targetRow, targetCol);
|
||||
|
||||
const moveDto = {
|
||||
GameId: GameState.currentGameId,
|
||||
PlayerId: GameState.currentPlayerId,
|
||||
PieceId: GameState.selectedPiece.id,
|
||||
SourceRow: GameState.selectedPiece.row,
|
||||
SourceCol: GameState.selectedPiece.col,
|
||||
TargetRow: targetRow,
|
||||
TargetCol: targetCol,
|
||||
PromotionChoice: choice
|
||||
};
|
||||
|
||||
GameState.setPreviousMove([GameState.selectedPiece.row, GameState.selectedPiece.col], [targetRow, targetCol]);
|
||||
|
||||
try {
|
||||
const moveResult = await this.makeMove(moveDto);
|
||||
|
||||
const updatedGame = await this.getGameState(GameState.currentGameId);
|
||||
ChessBoard.renderPieces(updatedGame.pieces);
|
||||
|
||||
GameState.clearSelection();
|
||||
ChessInteractions.clearHighlights();
|
||||
|
||||
await ChessSignalR.notifyMoveMade(moveDto, moveResult);
|
||||
this.alertGameStatusChange(moveResult);
|
||||
|
||||
} catch (error) {
|
||||
alert("❌ " + error.message);
|
||||
}
|
||||
},
|
||||
|
||||
alertGameStatusChange(moveResult) {
|
||||
setTimeout(async () => {
|
||||
let gameOver = false;
|
||||
|
||||
if (moveResult.isCheck) {
|
||||
console.log("🛑 Check!");
|
||||
}
|
||||
|
||||
if (moveResult.isCheckmate) {
|
||||
alert("♟️ Checkmate!");
|
||||
gameOver = true;
|
||||
} else if (moveResult.isStalemate) {
|
||||
alert("🤝 Stalemate!");
|
||||
gameOver = true;
|
||||
}
|
||||
|
||||
if (gameOver) {
|
||||
await ChessSignalR.leaveGame();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
};
|
||||
|
||||
console.log("ChessAPI.js loaded");
|
||||
@@ -0,0 +1,112 @@
|
||||
const ChessBoard = {
|
||||
renderPieces(pieces) {
|
||||
this.clearAllSquares();
|
||||
this.placePieces(pieces);
|
||||
this.setupSquareEventHandlers();
|
||||
this.highlightPreviousMove();
|
||||
},
|
||||
|
||||
clearAllSquares() {
|
||||
for (let i = 0; i < 64; i++) {
|
||||
const square = document.getElementById(`square-${i}`);
|
||||
square.innerHTML = "";
|
||||
}
|
||||
},
|
||||
|
||||
placePieces(pieces) {
|
||||
pieces.forEach(piece => {
|
||||
const [r, c] = ChessUtils.flipCoordinates(piece.row, piece.col);
|
||||
const index = r * 8 + c;
|
||||
const square = document.getElementById(`square-${index}`);
|
||||
if (!square) return;
|
||||
|
||||
const img = document.createElement("img");
|
||||
img.src = ChessUtils.getPieceImageUrl(piece);
|
||||
img.alt = piece.type;
|
||||
img.classList.add("chessPiece");
|
||||
|
||||
this.setupPieceDragEvents(img, piece);
|
||||
this.setupPieceClickEvent(img, piece);
|
||||
|
||||
square.appendChild(img);
|
||||
});
|
||||
},
|
||||
|
||||
setupPieceDragEvents(img, piece) {
|
||||
img.draggable = true;
|
||||
img.ondragstart = async (e) => {
|
||||
GameState.setSelectedPiece(piece);
|
||||
try {
|
||||
const moves = await ChessAPI.getLegalMoves(piece.id);
|
||||
GameState.setLegalMoves(moves);
|
||||
ChessInteractions.highlightSelected(piece.row, piece.col);
|
||||
ChessInteractions.highlightLegalMoves(moves);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
e.dataTransfer.setData("text/plain", JSON.stringify({
|
||||
srcRow: piece.Row,
|
||||
srcCol: piece.Col
|
||||
}));
|
||||
};
|
||||
|
||||
img.ondragend = () => ChessInteractions.clearHighlights();
|
||||
},
|
||||
|
||||
setupPieceClickEvent(img, piece) {
|
||||
img.onclick = (e) => {
|
||||
e.stopPropagation();
|
||||
ChessInteractions.handlePieceClick(piece);
|
||||
console.log("Clicked on:", piece);
|
||||
};
|
||||
},
|
||||
|
||||
setupSquareEventHandlers() {
|
||||
for (let i = 0; i < 64; i++) {
|
||||
const square = document.getElementById(`square-${i}`);
|
||||
square.classList.remove("previous-start", "previous-end");
|
||||
|
||||
if (!square.classList.contains("legal")) {
|
||||
square.onclick = () => ChessInteractions.clearHighlights();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
highlightPreviousMove() {
|
||||
if (GameState.previousMoveStart && GameState.previousMoveEnd) {
|
||||
const [startRow, startCol] = ChessUtils.flipCoordinates(...GameState.previousMoveStart);
|
||||
const [endRow, endCol] = ChessUtils.flipCoordinates(...GameState.previousMoveEnd);
|
||||
|
||||
const startIndex = startRow * 8 + startCol;
|
||||
const endIndex = endRow * 8 + endCol;
|
||||
|
||||
document.getElementById(`square-${startIndex}`)?.classList.add("previous-start");
|
||||
document.getElementById(`square-${endIndex}`)?.classList.add("previous-end");
|
||||
}
|
||||
},
|
||||
|
||||
setupDragAndDrop() {
|
||||
for (let i = 0; i < 64; i++) {
|
||||
const square = document.getElementById(`square-${i}`);
|
||||
|
||||
square.ondragover = (e) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
square.ondrop = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!GameState.selectedPiece) return;
|
||||
|
||||
const index = parseInt(square.id.split('-')[1], 10);
|
||||
const targetRow = Math.floor(index / 8);
|
||||
const targetCol = index % 8;
|
||||
|
||||
ChessAPI.handleMove(targetRow, targetCol);
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
console.log("ChessBoard.js loaded");
|
||||
@@ -0,0 +1,59 @@
|
||||
const ChessInteractions = {
|
||||
async handlePieceClick(piece) {
|
||||
this.clearHighlights();
|
||||
GameState.setSelectedPiece(piece);
|
||||
|
||||
try {
|
||||
const moves = await ChessAPI.getLegalMoves(piece.id);
|
||||
GameState.setLegalMoves(moves);
|
||||
this.highlightSelected(piece.row, piece.col);
|
||||
this.highlightLegalMoves(moves);
|
||||
} catch (err) {
|
||||
console.error("Error in handlePieceClick:", err);
|
||||
}
|
||||
},
|
||||
|
||||
highlightSelected(row, col) {
|
||||
const [r, c] = ChessUtils.flipCoordinates(row, col);
|
||||
const index = r * 8 + c;
|
||||
document.getElementById(`square-${index}`).classList.add("selected");
|
||||
},
|
||||
|
||||
highlightLegalMoves(moves) {
|
||||
moves.forEach(move => {
|
||||
const [r, c] = ChessUtils.flipCoordinates(move.row, move.col);
|
||||
const index = r * 8 + c;
|
||||
const square = document.getElementById(`square-${index}`);
|
||||
square.classList.add("legal");
|
||||
|
||||
const img = square.querySelector("img");
|
||||
if (img) img.onclick = null;
|
||||
|
||||
let overlay = document.createElement("div");
|
||||
overlay.className = "legalOverlay";
|
||||
square.appendChild(overlay);
|
||||
|
||||
square.onclick = () => ChessAPI.handleMove(r, c);
|
||||
});
|
||||
},
|
||||
|
||||
clearHighlights() {
|
||||
for (let i = 0; i < 64; i++) {
|
||||
const square = document.getElementById(`square-${i}`);
|
||||
|
||||
const overlay = square.querySelector(".legalOverlay");
|
||||
if (overlay) square.removeChild(overlay);
|
||||
|
||||
if (square.classList.contains("legal")) {
|
||||
square.classList.remove("legal");
|
||||
square.onclick = null;
|
||||
}
|
||||
|
||||
square.classList.remove("selected");
|
||||
}
|
||||
|
||||
GameState.clearSelection();
|
||||
}
|
||||
};
|
||||
|
||||
console.log("ChessInteractions.js loaded");
|
||||
@@ -0,0 +1,29 @@
|
||||
const ChessModals = {
|
||||
promptPromotion() {
|
||||
return new Promise(resolve => {
|
||||
let color = 1;
|
||||
if (GameState.currentPlayerIsWhite)
|
||||
color = 0;
|
||||
|
||||
ChessUtils.updatePromotionModalImages(color);
|
||||
|
||||
document.getElementById("promotionModal").style.display = "block";
|
||||
window.selectPromotion = (piece) => {
|
||||
document.getElementById("promotionModal").style.display = "none";
|
||||
resolve(piece);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
promptDifficulty() {
|
||||
return new Promise(resolve => {
|
||||
document.getElementById("difficultyModal").style.display = "block";
|
||||
window.selectDifficulty = (difficulty) => {
|
||||
document.getElementById("difficultyModal").style.display = "none";
|
||||
resolve(difficulty);
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
console.log("ChessModals.js loaded");
|
||||
@@ -0,0 +1,59 @@
|
||||
const ChessSignalR = {
|
||||
connection: null,
|
||||
|
||||
async setupConnection() {
|
||||
this.connection = new signalR.HubConnectionBuilder()
|
||||
.withUrl("/chessHub")
|
||||
.configureLogging(signalR.LogLevel.Information)
|
||||
.build();
|
||||
|
||||
this.connection.onclose(err => {
|
||||
console.error("❌ SignalR connection closed:", err?.message);
|
||||
});
|
||||
|
||||
this.connection.on("ReceiveMoveUpdate", async (gameId, moveDto, moveResultDto) => {
|
||||
await this.handleMoveUpdate(gameId, moveDto, moveResultDto);
|
||||
});
|
||||
|
||||
try {
|
||||
await this.connection.start();
|
||||
console.log("✅ SignalR connected");
|
||||
await this.connection.invoke("JoinWebsocketGroup", GameState.currentGameId);
|
||||
} catch (err) {
|
||||
console.error("❌ SignalR failed to start or join:", err);
|
||||
}
|
||||
},
|
||||
|
||||
async handleMoveUpdate(gameId, moveDto, moveResultDto) {
|
||||
if (gameId !== GameState.currentGameId) return;
|
||||
|
||||
const gameState = await ChessAPI.getGameState(gameId);
|
||||
GameState.setPreviousMove([moveDto.sourceRow, moveDto.sourceCol], [moveDto.targetRow, moveDto.targetCol]);
|
||||
ChessBoard.renderPieces(gameState.pieces);
|
||||
|
||||
ChessAPI.alertGameStatusChange(moveResultDto);
|
||||
},
|
||||
|
||||
async notifyMoveMade(moveDto, moveResult) {
|
||||
if (this.connection) {
|
||||
await this.connection.invoke("MoveMade", GameState.currentGameId, moveDto, moveResult);
|
||||
}
|
||||
},
|
||||
|
||||
async leaveGame() {
|
||||
if (this.connection) {
|
||||
await this.connection.invoke("LeaveWebsocketGroup", GameState.currentGameId);
|
||||
await this.connection.stop();
|
||||
this.connection = null;
|
||||
}
|
||||
},
|
||||
|
||||
async stopConnection() {
|
||||
if (this.connection) {
|
||||
await this.connection.stop();
|
||||
this.connection = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
console.log("ChessSignalR.js loaded");
|
||||
@@ -0,0 +1,44 @@
|
||||
const ChessUtils = {
|
||||
flipCoordinates(row, col) {
|
||||
if (!GameState.currentPlayerIsWhite) {
|
||||
return [7 - row, 7 - col];
|
||||
}
|
||||
return [row, col];
|
||||
},
|
||||
|
||||
getCookie(name) {
|
||||
const value = document.cookie.split('; ')
|
||||
.find(row => row.startsWith(name + '='));
|
||||
return value ? value.split('=')[1] : null;
|
||||
},
|
||||
|
||||
setCookie(name, value, maxAge = 86400) {
|
||||
document.cookie = `${name}=${value}; path=/; max-age=${maxAge}`;
|
||||
},
|
||||
|
||||
getPieceImageUrl(piece) {
|
||||
const basePath = "/images/Chess Images/";
|
||||
const color = piece.color === 0 ? "White" : "Black";
|
||||
const typeMap = {
|
||||
0: "Pawn",
|
||||
1: "Rook",
|
||||
2: "Knight",
|
||||
3: "Bishop",
|
||||
4: "Queen",
|
||||
5: "King"
|
||||
};
|
||||
|
||||
return basePath + color + typeMap[piece.type] + ".svg";
|
||||
},
|
||||
|
||||
updatePromotionModalImages(color) {
|
||||
const pieceNames = ["Queen", "Rook", "Bishop", "Knight"];
|
||||
const buttons = document.querySelectorAll("#promotionModal button img");
|
||||
|
||||
buttons.forEach((img, index) => {
|
||||
img.src = `/images/Chess Images/${color === 0 ? "White" : "Black"}${pieceNames[index]}.svg`;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
console.log("ChessUtils.js loaded");
|
||||
@@ -0,0 +1,50 @@
|
||||
const GameState = {
|
||||
currentGameId: null,
|
||||
currentPlayerId: null,
|
||||
currentPlayerIsWhite: null,
|
||||
selectedPiece: null,
|
||||
legalMoves: [],
|
||||
previousMoveStart: null,
|
||||
previousMoveEnd: null,
|
||||
|
||||
reset() {
|
||||
this.currentGameId = null;
|
||||
this.currentPlayerId = null;
|
||||
this.currentPlayerIsWhite = null;
|
||||
this.selectedPiece = null;
|
||||
this.legalMoves = [];
|
||||
this.previousMoveStart = null;
|
||||
this.previousMoveEnd = null;
|
||||
},
|
||||
|
||||
setGameInfo(gameId, playerId, isWhite) {
|
||||
this.currentGameId = gameId;
|
||||
this.currentPlayerId = playerId;
|
||||
this.currentPlayerIsWhite = isWhite;
|
||||
},
|
||||
|
||||
setSelectedPiece(piece) {
|
||||
this.selectedPiece = piece;
|
||||
},
|
||||
|
||||
clearSelection() {
|
||||
this.selectedPiece = null;
|
||||
this.legalMoves = [];
|
||||
},
|
||||
|
||||
setLegalMoves(moves) {
|
||||
this.legalMoves = moves;
|
||||
},
|
||||
|
||||
setPreviousMove(startPos, endPos) {
|
||||
this.previousMoveStart = startPos;
|
||||
this.previousMoveEnd = endPos;
|
||||
},
|
||||
|
||||
clearPreviousMove() {
|
||||
this.previousMoveStart = null;
|
||||
this.previousMoveEnd = null;
|
||||
}
|
||||
};
|
||||
|
||||
console.log("GameState.js loaded");
|
||||
@@ -0,0 +1,82 @@
|
||||
async function startNewGame() {
|
||||
await ChessSignalR.stopConnection();
|
||||
|
||||
try {
|
||||
const gameData = await ChessAPI.joinGame();
|
||||
|
||||
GameState.setGameInfo(gameData.gameId, gameData.id, gameData.isWhite);
|
||||
GameState.clearPreviousMove();
|
||||
|
||||
ChessUtils.setCookie('chessGameId', gameData.gameId);
|
||||
ChessUtils.setCookie('chessPlayerId', gameData.id);
|
||||
ChessUtils.setCookie('chessPlayerIsWhite', gameData.isWhite);
|
||||
|
||||
console.log("🆕 Game started:", gameData.gameId);
|
||||
|
||||
await ChessSignalR.setupConnection();
|
||||
|
||||
const gameState = await ChessAPI.getGameState(gameData.gameId);
|
||||
ChessBoard.renderPieces(gameState.pieces);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Failed to start new game:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function startCPUGame() {
|
||||
await ChessSignalR.stopConnection();
|
||||
|
||||
try {
|
||||
const difficulty = await ChessModals.promptDifficulty();
|
||||
const gameData = await ChessAPI.createCPUGame(difficulty);
|
||||
|
||||
GameState.setGameInfo(gameData.gameId, gameData.id, gameData.isWhite);
|
||||
GameState.clearPreviousMove();
|
||||
|
||||
ChessUtils.setCookie('chessGameId', gameData.gameId);
|
||||
ChessUtils.setCookie('chessPlayerId', gameData.id);
|
||||
ChessUtils.setCookie('chessPlayerIsWhite', gameData.isWhite);
|
||||
|
||||
console.log("🆕 CPU Game started:", gameData.gameId);
|
||||
|
||||
await ChessSignalR.setupConnection();
|
||||
|
||||
const gameState = await ChessAPI.getGameState(gameData.gameId);
|
||||
ChessBoard.renderPieces(gameState.pieces);
|
||||
|
||||
} catch (error) {
|
||||
console.error("Failed to start CPU game:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function resumeSavedGame() {
|
||||
const savedGameId = ChessUtils.getCookie("chessGameId");
|
||||
const savedPlayerId = ChessUtils.getCookie("chessPlayerId");
|
||||
const savedPlayerIsWhite = ChessUtils.getCookie("chessPlayerIsWhite");
|
||||
|
||||
if (savedGameId && savedPlayerId) {
|
||||
try {
|
||||
const gameState = await ChessAPI.getGameState(savedGameId);
|
||||
|
||||
console.log("🧠 Rejoining saved game...");
|
||||
GameState.setGameInfo(savedGameId, savedPlayerId, savedPlayerIsWhite === "true");
|
||||
|
||||
await ChessSignalR.setupConnection();
|
||||
ChessBoard.renderPieces(gameState.pieces);
|
||||
|
||||
} catch (err) {
|
||||
console.warn("Saved game not found or expired.", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.startNewGame = startNewGame;
|
||||
window.startCPUGame = startCPUGame;
|
||||
|
||||
window.addEventListener('load', resumeSavedGame);
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
ChessBoard.setupDragAndDrop();
|
||||
});
|
||||
|
||||
console.log("chessMain.js loaded");
|
||||
Reference in New Issue
Block a user