added validation for moves

This commit is contained in:
Wenszel 2022-06-10 18:29:09 +02:00
parent 4bd50f4a6f
commit 70442125b3
6 changed files with 75 additions and 92 deletions

View File

@ -1,40 +1,24 @@
const RoomModel = require('../schemas/room');
const { getPositionAfterMove } = require('../utils/functions');
/*
Function handle all requests
file constains functions:
1. roll
2. move
*/
const { getPawnPositionAfterMove } = require('../utils/functions');
module.exports = (io, socket) => {
const req = socket.request;
const roll = async () => {
/*
Function responsible for drawing number in range from 1 to 6 and returning it to the player
if current player can move with drawed number allow the player to make a move
else skip player's turn
*/
const rollDiceNumber = async () => {
const rolledNumber = Math.ceil(Math.random() * 6);
const room = await RoomModel.findOne({ _id: req.session.roomId }).exec();
let room = await RoomModel.findOne({ _id: req.session.roomId }).exec();
room.rolledNumber = rolledNumber;
await RoomModel.findOneAndUpdate({ _id: req.session.roomId }, room).exec();
io.to(req.session.roomId.toString()).emit('game:roll', rolledNumber);
const isPossible = await isMovePossible(req.session.roomId, req.session.color, rolledNumber);
const isPossible = await canPlayerMoveAnyPawn(req.session.roomId, req.session.color, rolledNumber);
if (!isPossible) {
const room = await RoomModel.findOne({ _id: req.session.roomId }).exec();
const playerIndex = room.players.findIndex(player => player.nowMoving === true);
const roomSize = room.players.length;
room.players[playerIndex].nowMoving = false;
if (playerIndex + 1 === roomSize) {
room.players[0].nowMoving = true;
} else {
room.players[playerIndex + 1].nowMoving = true;
}
// Updating timer
room.nextMoveTime = Date.now() + 15000;
room.rolledNumber = null;
// Pushing above data to database
RoomModel.findOneAndUpdate({ _id: req.session.roomId }, room, err => {
if (err) return err;
io.to(req.session.roomId.toString()).emit('room:data', JSON.stringify(room));
});
room = changeMovingPlayer(room);
await RoomModel.findOneAndUpdate({ _id: req.session.roomId }, room).exec();
io.to(req.session.roomId.toString()).emit('room:data', JSON.stringify(room));
}
};
/*
@ -45,7 +29,7 @@ module.exports = (io, socket) => {
2) (if player's pawn is near finish line) if the move does not go beyond the win line
Returns boolean
*/
const isMovePossible = async (roomId, playerColor, rolledNumber) => {
const canPlayerMoveAnyPawn = async (roomId, playerColor, rolledNumber) => {
let isMovePossible = false;
const room = await RoomModel.findOne({ _id: roomId.toString() }).exec();
const playerPawns = room.pawns.filter(pawn => pawn.color === playerColor);
@ -56,66 +40,77 @@ module.exports = (io, socket) => {
isMovePossible = true;
}
// Checking the second condition
if (pawn.position !== getPositionAfterMove(rolledNumber, pawn) && pawn.position !== pawn.basePos) {
if (pawn.position !== getPawnPositionAfterMove(rolledNumber, pawn) && pawn.position !== pawn.basePos) {
isMovePossible = true;
}
}
return isMovePossible;
};
const skip = async () => {
const room = await RoomModel.findOne({ _id: req.session.roomId }).exec();
if (room.nextMoveTime >= Date.now()) return;
// Updating moving player
const playerIndex = room.players.findIndex(player => player.nowMoving === true);
const roomSize = room.players.length;
room.players[playerIndex].nowMoving = false;
if (playerIndex + 1 === roomSize) {
room.players[0].nowMoving = true;
} else {
room.players[playerIndex + 1].nowMoving = true;
const isMoveValid = async (pawn, room) => {
if (req.session.color !== pawn.color) {
return false;
}
// Updating timer
room.nextMoveTime = Date.now() + 15000;
room.rolledNumber = null;
setTimeout(skip, 15000);
// Pushing above data to database
RoomModel.findOneAndUpdate({ _id: req.session.roomId }, room, err => {
if (err) return err;
io.to(req.session.roomId.toString()).emit('room:data', JSON.stringify(room));
});
currentlyMovingPlayer = room.players.filter(player => player.nowMoving === true);
if (req.session.playerId !== currentlyMovingPlayer._id) {
return false;
}
return true;
};
const move = async ({ pawnId }) => {
const room = await RoomModel.findOne({ _id: req.session.roomId }).exec();
const pawnIndex = room.pawns.findIndex(pawn => pawn._id == pawnId);
room.pawns[pawnIndex].position = getPositionAfterMove(room.rolledNumber, room.pawns[pawnIndex]);
const pawnsOnPos = room.pawns.filter(pawn => pawn.position == room.pawns[pawnIndex].position);
pawnsOnPos.forEach(pawn => {
/*
Function responsible for skipping a player's turn, if he did not move within the allotted time
Function is used in timeouts that start after a player's move or after skipping his turn
*/
const skipPlayerTurn = async () => {
let room = await RoomModel.findOne({ _id: req.session.roomId }).exec();
room = changeMovingPlayer(room);
await RoomModel.findOneAndUpdate({ _id: req.session.roomId }, room).exec();
io.to(req.session.roomId.toString()).emit('room:data', JSON.stringify(room));
};
/*
Function responsible for moving the pawn by the number of spots that have been drawn
Props: pawnId - Id which is needed to find a pawn in the board. Id is the only thing that distinguishes pawns of the same color.
*/
const movePawn = async ({ pawnId }) => {
let room = await RoomModel.findOne({ _id: req.session.roomId }).exec();
const indexOfMovedPawn = room.pawns.findIndex(pawn => pawn._id == pawnId);
const newPositionOfMovedPawn = getPawnPositionAfterMove(room.rolledNumber, room.pawns[indexOfMovedPawn]);
if (!isMoveValid(room.pawns[indexOfMovedPawn], room)) return;
room.pawns[indexOfMovedPawn].position = newPositionOfMovedPawn;
// Looking for pawns in the same position as the new position of the pawn
const pawnsInTheSamePosition = room.pawns.filter(pawn => pawn.position === newPositionOfMovedPawn);
// Each pawn in this position is checked to see if it has the same color as the pawn that has now moved to this position, if so, it is moved to the base (captured)
pawnsInTheSamePosition.forEach(pawn => {
if (pawn.color !== req.session.color) {
const index = room.pawns.findIndex(i => i._id === pawn._id);
room.pawns[index].position = room.pawns[index].basePos;
}
});
// Updating moving player
room = changeMovingPlayer(room);
await RoomModel.findOneAndUpdate({ _id: req.session.roomId }, room).exec();
io.to(req.session.roomId.toString()).emit('room:data', JSON.stringify(room));
};
/*
Function responsible for changing the currently moving player in room object.
It changes the value of nowMoving for both players and sets a new turn-end time and erases the currently drawn number.
The function is used as an auxiliary function in other functions because many of them perform the operation of changing the currently moving player.
Args: room (object) from mongoDB
Returns: room object after changes
*/
const changeMovingPlayer = room => {
const playerIndex = room.players.findIndex(player => player.nowMoving === true);
const roomSize = room.players.length;
room.players[playerIndex].nowMoving = false;
if (playerIndex + 1 === roomSize) {
if (playerIndex + 1 === room.players.length) {
room.players[0].nowMoving = true;
} else {
room.players[playerIndex + 1].nowMoving = true;
}
// Updating timer
room.nextMoveTime = Date.now() + 15000;
room.rolledNumber = null;
setTimeout(skip, 15000);
// Pushing above data to database
RoomModel.findOneAndUpdate({ _id: req.session.roomId }, room, (err, updatedRoom) => {
if (!updatedRoom) return err;
io.to(req.session.roomId.toString()).emit('room:data', JSON.stringify(room));
});
setTimeout(skipPlayerTurn, 15000);
return room;
};
socket.on('game:roll', roll);
socket.on('game:move', move);
socket.on('game:skip', skip);
socket.on('game:roll', rollDiceNumber);
socket.on('game:move', movePawn);
socket.on('game:skip', skipPlayerTurn);
};

View File

@ -1,15 +1,7 @@
const RoomModel = require('../schemas/room');
const { colors } = require('../utils/constants');
const { getStartPositions } = require('../utils/functions');
/*
Function handle all player's requests to server
file constains functions:
1. login
with helper functions:
- addPlayerToExistingRoom
- createNewRoom
2. ready
*/
module.exports = (io, socket) => {
const req = socket.request;

View File

@ -13,7 +13,7 @@ function getStartPositions() {
}
return startPositions;
}
function getPositionAfterMove(rolledNumber, pawn) {
function getPawnPositionAfterMove(rolledNumber, pawn) {
const { position, color } = pawn;
switch (color) {
case 'red':
@ -72,4 +72,4 @@ function getPositionAfterMove(rolledNumber, pawn) {
}
}
}
module.exports = { getStartPositions, getPositionAfterMove };
module.exports = { getStartPositions, getPawnPositionAfterMove };

View File

@ -7,7 +7,7 @@ import Navbar from './Navbar';
const Gameboard = () => {
// Context data
const socket = useContext(SocketContext);
const player = useContext(PlayerDataContext);
const context = useContext(PlayerDataContext);
// Render data
const [pawns, setPawns] = useState([]);
const [players, setPlayers] = useState([]);
@ -32,7 +32,7 @@ const Gameboard = () => {
}
}, [pawns]);
useEffect(() => {
socket.emit('room:data', player.roomId);
socket.emit('room:data', context.roomId);
socket.on('room:data', data => {
data = JSON.parse(data);
// Filling navbar with empty player nick container
@ -42,15 +42,14 @@ const Gameboard = () => {
// Checks if client is currently moving player by session ID
const nowMovingPlayer = data.players.find(player => player.nowMoving === true);
if (nowMovingPlayer) {
if (nowMovingPlayer._id === player.playerId) {
if (nowMovingPlayer._id === context.playerId) {
setNowMoving(true);
} else {
setNowMoving(false);
}
setMovingPlayer(nowMovingPlayer.color);
}
const currentPlayer = data.players.find(player => player._id === player.playerId);
const currentPlayer = data.players.find(player => player._id === context.playerId);
checkWin();
setIsReady(currentPlayer.ready);
setRolledNumber(data.rolledNumber);

View File

@ -10,7 +10,7 @@ const Navbar = ({ players, started, time, isReady, rolledNumber, nowMoving, roll
<div className='navbar-container'>
{players.map((player, index) => (
<div className={`player-container ${colors[index]}`} key={index}>
<NameContainer player={player} time={time} containerColor={colors[index]} />
<NameContainer player={player} time={time} />
<Dice
movingPlayer={movingPlayer}
rolledNumber={rolledNumber}

View File

@ -6,7 +6,6 @@ const Map = ({ pawns, nowMoving, rolledNumber }) => {
const context = useContext(PlayerDataContext);
const socket = useContext(SocketContext);
const [hintPawn, setHintPawn] = useState();
const [blinking, setBlinking] = useState(false);
const paintPawn = (context, x, y, color) => {
const circle = new Path2D();
circle.arc(x, y, 12, 0, 2 * Math.PI);
@ -150,7 +149,6 @@ const Map = ({ pawns, nowMoving, rolledNumber }) => {
checkIfPawnCanMove(pawn)
) {
const pawnPosition = getHintPawnPosition(pawn);
setBlinking(false);
// Checks if pawn can make a move
if (pawnPosition) {
canvas.style.cursor = 'pointer';
@ -172,7 +170,7 @@ const Map = ({ pawns, nowMoving, rolledNumber }) => {
image.onload = function () {
ctx.drawImage(image, 0, 0);
pawns.forEach((pawn, index) => {
if (nowMoving && rolledNumber && blinking && pawn.color === context.color && checkIfPawnCanMove(pawn)) {
if (nowMoving && rolledNumber && pawn.color === context.color && checkIfPawnCanMove(pawn)) {
pawns[index].circle = paintPawn(
ctx,
positions[pawn.position].x,
@ -187,7 +185,6 @@ const Map = ({ pawns, nowMoving, rolledNumber }) => {
pawn.color
);
}
setBlinking(!blinking);
});
if (hintPawn) {
paintPawn(ctx, positions[hintPawn.position].x, positions[hintPawn.position].y, hintPawn.color);