removed chess.js to build my own engine now
This commit is contained in:
parent
09f50e2477
commit
fa9c28df05
5 changed files with 62 additions and 44 deletions
7
package-lock.json
generated
7
package-lock.json
generated
|
|
@ -9,7 +9,6 @@
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chess.js": "^1.1.2",
|
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"socket.io": "^4.7.2",
|
"socket.io": "^4.7.2",
|
||||||
"uuid": "^9.0.0"
|
"uuid": "^9.0.0"
|
||||||
|
|
@ -129,12 +128,6 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/chess.js": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/chess.js/-/chess.js-1.4.0.tgz",
|
|
||||||
"integrity": "sha512-BBJgrrtKQOzFLonR0l+k64A98NLemPwNsCskwb+29bRwobUa4iTm51E1kwGPbWXAcfdDa18nad6vpPPKPWarqw==",
|
|
||||||
"license": "BSD-2-Clause"
|
|
||||||
},
|
|
||||||
"node_modules/content-disposition": {
|
"node_modules/content-disposition": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
"author": "Didictateur",
|
"author": "Didictateur",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chess.js": "^1.1.2",
|
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"socket.io": "^4.7.2",
|
"socket.io": "^4.7.2",
|
||||||
"uuid": "^9.0.0"
|
"uuid": "^9.0.0"
|
||||||
|
|
|
||||||
|
|
@ -111,7 +111,28 @@
|
||||||
|
|
||||||
connectAndJoin();
|
connectAndJoin();
|
||||||
|
|
||||||
// allow clicking on the board to request legal moves for a square
|
// show markers for legal moves when clicking any square (any piece color)
|
||||||
|
function clearLegalMarkers(){
|
||||||
|
const prev = boardEl.querySelectorAll('.move-marker');
|
||||||
|
prev.forEach(p=>p.remove());
|
||||||
|
}
|
||||||
|
|
||||||
|
function showLegalMoves(moves){
|
||||||
|
clearLegalMarkers();
|
||||||
|
(moves||[]).forEach(m => {
|
||||||
|
const to = m.to || (m.move && m.move.to);
|
||||||
|
if(!to) return;
|
||||||
|
const target = boardEl.querySelector(`.square[data-square="${to}"]`);
|
||||||
|
if(!target) return;
|
||||||
|
const marker = document.createElement('div');
|
||||||
|
marker.className = 'move-marker';
|
||||||
|
// store metadata if needed later
|
||||||
|
marker.dataset.from = m.from || '';
|
||||||
|
marker.dataset.to = to;
|
||||||
|
target.appendChild(marker);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if(boardEl){
|
if(boardEl){
|
||||||
boardEl.addEventListener('click', (ev)=>{
|
boardEl.addEventListener('click', (ev)=>{
|
||||||
const sqEl = ev.target.closest && ev.target.closest('.square');
|
const sqEl = ev.target.closest && ev.target.closest('.square');
|
||||||
|
|
@ -120,8 +141,9 @@
|
||||||
if(!square) return;
|
if(!square) return;
|
||||||
if(socket){
|
if(socket){
|
||||||
socket.emit('game:legalMoves', { roomId, square }, (resp)=>{
|
socket.emit('game:legalMoves', { roomId, square }, (resp)=>{
|
||||||
if(resp && resp.error){ log('legalMoves error', resp); return; }
|
if(resp && resp.error){ log('legalMoves error', resp); clearLegalMarkers(); return; }
|
||||||
log('legalMoves for', square, resp.moves || resp);
|
log('legalMoves for', square, resp.moves || resp);
|
||||||
|
showLegalMoves(resp.moves || []);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,15 @@ a:hover{text-decoration:underline}
|
||||||
.square{width:100%;height:100%;display:flex;align-items:center;justify-content:center}
|
.square{width:100%;height:100%;display:flex;align-items:center;justify-content:center}
|
||||||
.square img, .piece{max-width:78%;max-height:78%;user-select:none}
|
.square img, .piece{max-width:78%;max-height:78%;user-select:none}
|
||||||
|
|
||||||
|
/* marker shown on target squares for legal moves */
|
||||||
|
.square{position:relative}
|
||||||
|
.square .move-marker{
|
||||||
|
position:absolute;
|
||||||
|
left:50%;top:50%;transform:translate(-50%,-50%);
|
||||||
|
width:18%;height:18%;border-radius:50%;pointer-events:none;background:rgba(96,165,250,0.95);
|
||||||
|
box-shadow:0 2px 6px rgba(2,6,23,0.6);z-index:3
|
||||||
|
}
|
||||||
|
|
||||||
/* basic checkerboard coloring */
|
/* basic checkerboard coloring */
|
||||||
.square.light{background: linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01));}
|
.square.light{background: linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01));}
|
||||||
.square.dark{background: linear-gradient(180deg, rgba(0,0,0,0.18), rgba(0,0,0,0.12));}
|
.square.dark{background: linear-gradient(180deg, rgba(0,0,0,0.18), rgba(0,0,0,0.12));}
|
||||||
|
|
|
||||||
63
server.js
63
server.js
|
|
@ -1,7 +1,6 @@
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const http = require('http');
|
const http = require('http');
|
||||||
const { Server } = require('socket.io');
|
const { Server } = require('socket.io');
|
||||||
const { Chess } = require('chess.js');
|
|
||||||
const { v4: uuidv4 } = require('uuid');
|
const { v4: uuidv4 } = require('uuid');
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
@ -15,24 +14,36 @@ app.use(express.static('public'));
|
||||||
app.use('/assets', express.static(path.join(__dirname, 'assets')));
|
app.use('/assets', express.static(path.join(__dirname, 'assets')));
|
||||||
|
|
||||||
// In-memory rooms store. For production replace with persistent store.
|
// In-memory rooms store. For production replace with persistent store.
|
||||||
// room = { id, chess: Chess, players: [{id, socketId, color}], status }
|
// room = { id, boardFEN: string|null, turn: 'w'|'b'|null, players: [{id, socketId, color}], status }
|
||||||
const rooms = new Map();
|
const rooms = new Map();
|
||||||
|
|
||||||
|
// Placeholder: compute legal moves for a square in the given room.
|
||||||
|
// This is intentionally a stub — you will implement the full rules engine.
|
||||||
|
// (fields like { from, to, piece, color, flags, captured, san } are typical).
|
||||||
|
function computeLegalMoves(room, square){
|
||||||
|
// TODO: implement rule engine. For now return empty array so client shows nothing.
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
app.post('/rooms', (req, res) => {
|
app.post('/rooms', (req, res) => {
|
||||||
const id = uuidv4().slice(0, 8);
|
const id = uuidv4().slice(0, 8);
|
||||||
// allow optional board size in request body (default 8)
|
// allow optional board size in request body (default 8)
|
||||||
const size = (req.body && parseInt(req.body.size, 10)) || 8;
|
const size = (req.body && parseInt(req.body.size, 10)) || 8;
|
||||||
// For standard 8x8 chess we use chess.js. For other sizes we keep a placeholder state
|
// For standard 8x8 we initialize a FEN string as the canonical board state.
|
||||||
const chess = size === 8 ? new Chess() : null;
|
// manually (or by a separate engine). This server stores the board FEN and
|
||||||
|
// the active turn so custom logic can operate on them.
|
||||||
|
const startingFEN = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
|
||||||
|
const boardFEN = size === 8 ? startingFEN : null;
|
||||||
|
const turn = size === 8 ? 'w' : null; // 'w' or 'b'
|
||||||
// hostId will be set when the first player joins
|
// hostId will be set when the first player joins
|
||||||
rooms.set(id, { id, chess, players: [], status: 'waiting', hostId: null, size, cards: {}, playedCards: [], removalTimers: new Map() });
|
rooms.set(id, { id, boardFEN, turn, players: [], status: 'waiting', hostId: null, size, cards: {}, playedCards: [], removalTimers: new Map() });
|
||||||
res.json({ roomId: id, size });
|
res.json({ roomId: id, size });
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/rooms/:id', (req, res) => {
|
app.get('/rooms/:id', (req, res) => {
|
||||||
const room = rooms.get(req.params.id);
|
const room = rooms.get(req.params.id);
|
||||||
if (!room) return res.status(404).json({ error: 'room not found' });
|
if (!room) return res.status(404).json({ error: 'room not found' });
|
||||||
const fen = room.chess ? room.chess.fen() : null;
|
const fen = room.boardFEN || null;
|
||||||
res.json({ id: room.id, fen, size: room.size, players: room.players.map(p => ({ id: p.id, color: p.color })), status: room.status, hostId: room.hostId, cards: Object.keys(room.cards || {}) });
|
res.json({ id: room.id, fen, size: room.size, players: room.players.map(p => ({ id: p.id, color: p.color })), status: room.status, hostId: room.hostId, cards: Object.keys(room.cards || {}) });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -70,7 +81,7 @@ io.on('connection', (socket) => {
|
||||||
|
|
||||||
|
|
||||||
io.to(roomId).emit('room:update', {
|
io.to(roomId).emit('room:update', {
|
||||||
fen: room.chess ? room.chess.fen() : null,
|
fen: room.boardFEN || null,
|
||||||
players: room.players.map(p => ({ id: p.id, color: p.color })),
|
players: room.players.map(p => ({ id: p.id, color: p.color })),
|
||||||
status: room.status,
|
status: room.status,
|
||||||
hostId: room.hostId,
|
hostId: room.hostId,
|
||||||
|
|
@ -85,26 +96,10 @@ io.on('connection', (socket) => {
|
||||||
const room = rooms.get(roomId);
|
const room = rooms.get(roomId);
|
||||||
if (!room) return cb && cb({ error: 'room not found' });
|
if (!room) return cb && cb({ error: 'room not found' });
|
||||||
|
|
||||||
const chess = room.chess;
|
// Move handling/validation is intentionally not implemented here because
|
||||||
|
// Implement a function to validate & apply moves on the room state and
|
||||||
// If we have a chess.js instance (standard 8x8) use it for validation
|
// then emit 'move:moved' with move details and updated room.boardFEN.
|
||||||
if (!chess) return cb && cb({ error: 'custom board sizes not yet supported for moves' });
|
return cb && cb({ error: 'move handling not implemented on server; implement custom engine' });
|
||||||
|
|
||||||
// Validate move
|
|
||||||
const move = chess.move({ from, to, promotion });
|
|
||||||
if (!move) return cb && cb({ error: 'invalid move' });
|
|
||||||
|
|
||||||
io.to(roomId).emit('move:moved', { move, fen: chess.fen() });
|
|
||||||
|
|
||||||
if (chess.isGameOver()) {
|
|
||||||
room.status = 'finished';
|
|
||||||
let result = 'draw';
|
|
||||||
if (chess.isCheckmate()) result = 'checkmate';
|
|
||||||
else if (chess.isStalemate()) result = 'stalemate';
|
|
||||||
io.to(roomId).emit('game:over', { result, fen: chess.fen() });
|
|
||||||
}
|
|
||||||
|
|
||||||
cb && cb({ ok: true, move, fen: chess.fen() });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Host can start the game explicitly
|
// Host can start the game explicitly
|
||||||
|
|
@ -120,9 +115,9 @@ io.on('connection', (socket) => {
|
||||||
if (!room.hostId || room.hostId !== senderId) return cb && cb({ error: 'only host can start' });
|
if (!room.hostId || room.hostId !== senderId) return cb && cb({ error: 'only host can start' });
|
||||||
|
|
||||||
room.status = 'playing';
|
room.status = 'playing';
|
||||||
io.to(roomId).emit('game:started', { roomId, fen: room.chess.fen() });
|
io.to(roomId).emit('game:started', { roomId, fen: room.boardFEN });
|
||||||
io.to(roomId).emit('room:update', {
|
io.to(roomId).emit('room:update', {
|
||||||
fen: room.chess ? room.chess.fen() : null,
|
fen: room.boardFEN || null,
|
||||||
players: room.players.map(p => ({ id: p.id, color: p.color })),
|
players: room.players.map(p => ({ id: p.id, color: p.color })),
|
||||||
status: room.status,
|
status: room.status,
|
||||||
hostId: room.hostId,
|
hostId: room.hostId,
|
||||||
|
|
@ -149,7 +144,7 @@ io.on('connection', (socket) => {
|
||||||
}
|
}
|
||||||
if (room.players.length < 2 && room.status === 'playing') room.status = 'waiting';
|
if (room.players.length < 2 && room.status === 'playing') room.status = 'waiting';
|
||||||
io.to(roomId).emit('room:update', {
|
io.to(roomId).emit('room:update', {
|
||||||
fen: room.chess ? room.chess.fen() : null,
|
fen: room.boardFEN || null,
|
||||||
players: room.players.map(p => ({ id: p.id, color: p.color })),
|
players: room.players.map(p => ({ id: p.id, color: p.color })),
|
||||||
status: room.status,
|
status: room.status,
|
||||||
hostId: room.hostId,
|
hostId: room.hostId,
|
||||||
|
|
@ -167,7 +162,7 @@ io.on('connection', (socket) => {
|
||||||
room.hostId = room.players[0] ? room.players[0].id : null;
|
room.hostId = room.players[0] ? room.players[0].id : null;
|
||||||
}
|
}
|
||||||
io.to(roomId).emit('room:update', {
|
io.to(roomId).emit('room:update', {
|
||||||
fen: room.chess ? room.chess.fen() : null,
|
fen: room.boardFEN || null,
|
||||||
players: room.players.map(p => ({ id: p.id, color: p.color })),
|
players: room.players.map(p => ({ id: p.id, color: p.color })),
|
||||||
status: room.status,
|
status: room.status,
|
||||||
hostId: room.hostId,
|
hostId: room.hostId,
|
||||||
|
|
@ -177,15 +172,15 @@ io.on('connection', (socket) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Provide legal moves for a given square (for standard 8x8 via chess.js)
|
// delegates to `computeLegalMoves` which is a stub you should implement.
|
||||||
socket.on('game:legalMoves', ({ roomId, square }, cb) => {
|
socket.on('game:legalMoves', ({ roomId, square }, cb) => {
|
||||||
const room = rooms.get(roomId);
|
const room = rooms.get(roomId);
|
||||||
if (!room) return cb && cb({ error: 'room not found' });
|
if (!room) return cb && cb({ error: 'room not found' });
|
||||||
if (!room.chess) return cb && cb({ error: 'legal moves not supported for custom board sizes yet', moves: [] });
|
|
||||||
try{
|
try{
|
||||||
const moves = room.chess.moves({ square, verbose: true }) || [];
|
const moves = computeLegalMoves(room, square) || [];
|
||||||
return cb && cb({ ok: true, moves });
|
return cb && cb({ ok: true, moves });
|
||||||
}catch(e){
|
}catch(e){
|
||||||
|
console.error('game:legalMoves error', e);
|
||||||
return cb && cb({ error: 'invalid square', moves: [] });
|
return cb && cb({ error: 'invalid square', moves: [] });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue