diff --git a/public/game.html b/public/game.html
index 5457d0b..a1032a3 100644
--- a/public/game.html
+++ b/public/game.html
@@ -50,6 +50,8 @@
const remoteSelections = {};
// track remote moves: { playerId: [moves] }
const remoteMoves = {};
+ // current boardState cached locally
+ let currentBoardState = null;
// safe logger: prefer on-page log if present, otherwise console
function log(...args){
@@ -134,6 +136,16 @@
}catch(e){ console.warn('game:select handler error', e); }
});
+ socket.on('move:moved', (data) => {
+ try{
+ log('recv move:moved', data && { from: data.from, to: data.to });
+ // clear any local selection (move completed) before re-render
+ clearSelection(false);
+ if(data && data.boardState) renderBoardFromState(data.boardState);
+ if(data && data.boardState) updateTurnBanner(data.boardState);
+ }catch(e){ console.warn('move:moved handler error', e); }
+ });
+
socket.emit('room:join', { roomId, playerId }, (resp)=>{
if(resp && resp.error){ log('join error', resp); alert(resp.error); return; }
log('Rejoint room', resp);
@@ -296,6 +308,27 @@
const square = sqEl.getAttribute('data-square');
if(!square) return;
+ // If this square contains a move-marker (either clicked marker or square area), attempt the move.
+ // Prefer the actual clicked marker if present, otherwise look for a marker element inside the square.
+ const clickedMarker = ev.target.closest && ev.target.closest('.move-marker');
+ const marker = clickedMarker || sqEl.querySelector('.move-marker');
+ if(marker){
+ // ignore remote player's markers
+ const remoteBy = marker.dataset && marker.dataset.remoteBy;
+ if(remoteBy && remoteBy !== myPlayerId){ log('marker belongs to remote player, ignoring'); return; }
+ const to = marker.dataset && marker.dataset.to;
+ const from = marker.dataset && marker.dataset.from ? marker.dataset.from : selectedSquare;
+ if(!to || !from){ log('invalid marker metadata', { from, to }); return; }
+ // only allow if it's our turn
+ const myShort = (myColor && myColor[0]) || '';
+ if(!currentBoardState || currentBoardState.turn !== myShort){ log('not your turn'); return; }
+ socket.emit('game:move', { roomId, from, to }, (resp)=>{
+ if(resp && resp.error){ log('move error', resp); alert(resp.error); }
+ else { log('move sent', resp); }
+ });
+ return;
+ }
+
// clicked a piece -> select it (no legal moves requested)
const pieceImg = ev.target.closest && ev.target.closest('img.piece');
if(pieceImg){
@@ -316,6 +349,8 @@
function renderBoardFromState(boardState){
if(!boardState) return;
+ // cache boardState for client-side checks (turn etc.)
+ currentBoardState = boardState;
// clear existing pieces but keep squares if present
const squares = boardEl.querySelectorAll('.square');
squares.forEach(s=> s.innerHTML = '');
diff --git a/public/styles/style.css b/public/styles/style.css
index 6e05f0f..56d0fa6 100644
--- a/public/styles/style.css
+++ b/public/styles/style.css
@@ -56,9 +56,10 @@ a:hover{text-decoration:underline}
.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;
+ width:18%;height:18%;border-radius:50%;pointer-events:auto;cursor:pointer;background:rgba(96,165,250,0.95);
+ box-shadow:0 2px 6px rgba(2,6,23,0.6);z-index:3;transition:transform 120ms ease, opacity 120ms ease;
}
+.square .move-marker:hover{ transform:translate(-50%,-50%) scale(1.12); opacity:0.95 }
/* highlighted/selected square when a player selects a piece */
.square.selected{
diff --git a/server.js b/server.js
index 9d5f794..3755779 100644
--- a/server.js
+++ b/server.js
@@ -237,11 +237,64 @@ io.on('connection', (socket) => {
socket.on('game:move', ({ roomId, from, to, promotion }, cb) => {
const room = rooms.get(roomId);
if (!room) return cb && cb({ error: 'room not found' });
+ // Basic move handling: validate turn, validate piece ownership, validate target is one of computeLegalMoves,
+ // apply the move to room.boardState, handle captures, flip turn and broadcast the updated board.
+ try{
+ const senderId = socket.data.playerId;
+ if(!senderId) return cb && cb({ error: 'not joined' });
+ const roomPlayer = room.players.find(p => p.id === senderId);
+ if(!roomPlayer) return cb && cb({ error: 'player not in room' });
+ if(!room.boardState) return cb && cb({ error: 'no board state' });
- // Move handling/validation is intentionally not implemented here.
- // Implement a function to validate & apply moves on the room.boardState and
- // then emit 'move:moved' with move details and updated room.boardState.
- return cb && cb({ error: 'move handling not implemented on server; implement custom engine' });
+ const board = room.boardState;
+ // determine player's color short form ('w'|'b')
+ const playerColorShort = (roomPlayer.color && roomPlayer.color[0]) || null;
+ if(!playerColorShort) return cb && cb({ error: 'invalid player color' });
+
+ // must be player's turn
+ if(board.turn !== playerColorShort) return cb && cb({ error: 'not your turn' });
+
+ // find piece at 'from'
+ const pieces = board.pieces || [];
+ const moving = pieces.find(p => p.square === from);
+ if(!moving) return cb && cb({ error: 'no piece at source' });
+ if(moving.color !== playerColorShort) return cb && cb({ error: 'not your piece' });
+
+ // validate that 'to' is among legal moves
+ const legal = computeLegalMoves(room, from) || [];
+ const ok = legal.some(m => m.to === to);
+ if(!ok) return cb && cb({ error: 'illegal move' });
+
+ // apply move: remove any piece on target (capture)
+ const targetIndex = pieces.findIndex(p => p.square === to);
+ if(targetIndex >= 0){
+ // remove captured piece
+ pieces.splice(targetIndex, 1);
+ }
+ // move the piece
+ moving.square = to;
+
+ // advance version and flip turn
+ board.version = (board.version || 0) + 1;
+ board.turn = (board.turn === 'w') ? 'b' : 'w';
+
+ // broadcast move and new board state
+ const moved = { playerId: senderId, from, to, boardState: board };
+ io.to(roomId).emit('move:moved', moved);
+ io.to(roomId).emit('room:update', {
+ boardState: board,
+ players: room.players.map(p => ({ id: p.id, color: p.color })),
+ status: room.status,
+ hostId: room.hostId,
+ size: room.size,
+ cards: Object.keys(room.cards || {})
+ });
+
+ return cb && cb({ ok: true, moved });
+ }catch(err){
+ console.error('game:move error', err);
+ return cb && cb({ error: 'server error' });
+ }
});
// Host can start the game explicitly