I have been really busy in the past week(s) making progress with this project and making it better by rewriting some of the backend functions as well as adding further rules to the game. What I had before was a working version of a somewhat simplified version of the game, ignoring various types of action cards. Now, I have finally added one action card and I'm working on the rest. Time, the lack of it at least, forbids me to work on this project as much as I want to and I'd like to thank you the people who keep on reading the article series for their patience.
The game itself will allow a number of action cards to be played, which are: "2", "Ace" and "King". The game has multiple variations and permutations so in each country there are different rules for different cards, therefore I do need to explain the actions behind the cards for my version:
I had to 'code' these requests which could have been a challenge itself but this game actually also encourages cheating. Let's assume you have an "Ace" in your hand and you could override someone's request but you can decide to draw a card instead - to keep the "Ace" for later. Therefore I can't force people to play a particular card. Also, imagine the situation where you play a "King" and you have a "5" in your hand. You can make a request for "8" because maybe the next player also has a "King" and they'd override your request. So there's a slight chance that they'll override it and maybe request a "5". Again, I can't force the player to request the card that he/she has in his/her hand.
Let's have a look at the only rule that I have create so far - that is for "2". There are two different scenarios that I had to take into consideration: At the start of the game
The first card placed automatically onto the table is completely random and of course, it can be the card number "2", therefore:
During gameplay
This scenario is exactly the same as mentioned above.
Okay, that was enough gibberish for the time being, let's do some coding shall we? First things first, I have extended the variables for my table.js
file and I have added the following fields that are related to the action cards: 'actionCard', 'requestActionCard', 'penalisingAction', 'forcedDraw', 'suiteRequest' and 'numberRequest', so the file looks like this (Of course it still has the methods explained in the previous 5 articles):
function Table(tableID) {
this.id = tableID;
this.name = '';
this.status = 'available';
this.players = [];
this.playersID = [];
this.readyToPlayCounter = 0;
this.playerLimit = 2;
this.pack = [];
this.cardsOnTable = [];
this.actionCard = false;
this.requestActionCard = false;
this.penalisingActionCard = false;
this.forcedDraw = 0;
this.suiteRequest = '';
this.numberRequest = '';
this.gameObj = null;
}
I have also added a function to the main server.js
called 'preliminaryRoundCheck', here's the full code:
socket.on("preliminaryRoundCheck", function(data) {
console.log("preliminary round check called.");
var player = room.getPlayer(socket.id);
var table = room.getTable(data.tableID);
var last = table.gameObj.lastCardOnTable(table.cardsOnTable); //last card on Table
console.log('Last card on table ==>' + last);
if (table.gameObj.isActionCard(last) && table.actionCard) { //Is the card on the table an action card?
if (table.gameObj.isActionCard(last, true)) { //Is the first card on the table a penalising card? (2*) (checked by the true flag)
table.forcedDraw += 2; //add 2 cards to the forcedDraw function
table.penalisingActionCard = true;
console.log("FORCED DRAW ==>" + table.forcedDraw);
console.log("it's a penalising card");
if (table.gameObj.isInHand(last, player.hand)) { //Does the starting player have a response in hand?
console.log("I have a 2, optionally i can play it"); //GIVE OPTIONS
socket.emit("playOption", { message: "You have a 2 card in your hand, you can either play it or take " + table.forcedDraw + " cards.", value: true}); //OPTION - TRUE
} else {
console.log("no 2 in hand, force me to draw"); //No penalising action card in hand, force draw
console.log("HAND ==> " + player.hand);
socket.emit("playOption", { value: false }); //OPTION - TRUE
table.gameObj.drawCard(table.pack, table.forcedDraw, player.hand, 0);
socket.emit("play", { hand: player.hand }); //send the card in hands to player
io.sockets.emit('updatePackCount', {packCount: table.pack.length});
table.forcedDraw = 0; //reset forced draw variable
table.actionCard = false; //set the action card to false
table.penalisingActionCard = false; //reset the penalising action card variable
/*PROGRESS ROUND*/
table.progressRound(player); //end of turn
socket.emit("turn", {myturn: false}); //end of my turn
messaging.sendEventToAllPlayersButPlayer("turn", {myturn: true}, io, table.players, player); //add turn to next player
messaging.sendEventToAllPlayersButPlayer("cardInHandCount", {cardsInHand: player.hand.length}, io, table.players, player); //update cards count for other players
}
} else { //Is the first card on the table a request card (1*, 13*)
console.log("it is a request card, player to make a request"); //SHOW REQUEST WINDOW
//TO BE IMPLEMENTED LATER
}
} else { //The first card on the table is not an action card at all
console.log(last + " is not an action card or we don't care about it anymore");
}
});
The most important thing to take away from the code above is the way that I'm actually setting/reading the actionCard
variable as well as the forcedDraw variable. Every time a person plays a "2", table.forcedDraw =+ 2;
is called.
As you can see, I have a new method 'isActionCard' as well as 'isInHand', both are part of game.js
, let's have a look at them:
/* checking if card is an action card */
Game.prototype.isActionCard = function (card, penalising) {
penalising = typeof penalising === 'undefined' ? false : penalising;
if (card && !penalising) {
var cardNumber = parseInt(card);
console.log(cardNumber);
if (cardNumber in utils.has(['1', '2', '13'])) {
return true;
} else {
return false;
}
}
if (card && penalising) {
var cardNumber = parseInt(card);
if (cardNumber === 2) {
return true;
} else {
return false;
}
}
};
Game.prototype.isInHand = function (card, hand) {
//checks whether there's a card in our hand
if (card) {
cardNumber = parseInt(card);
//parse numbers in hand
var numbersInHand = [];
for (var i = 0; i < hand.length; i++) {
numbersInHand.push(parseInt(hand[i]));
}
if (utils.indexOf(numbersInHand, cardNumber) > -1) {
return true; //I can play a card if I want to
} else {
return false; //I can't play, force me to draw.
}
}
};
I also added a function to check force a player to play a penalising action card on top of another penalising action card. In other words - a player has to play "2" if there is a "2" on the top of the pile (and if the actionCard
variable is set to true:
Game.prototype.isPenalisingActionCardPlayable = function(card, lastCardOnTable) {
if (card) {
var cardNumber = parseInt(card);
var lastCardNumber = parseInt(lastCardOnTable);
if (cardNumber === 2 && lastCardNumber === 2) {
return true;
} else {
return false;
}
}
}
I did introduce this function before in a previous post, but I need to talk about it again. The force draw essentially calls the regular 'drawCard' function from game.js
but sends in a different parameter for the 'amount':
Game.prototype.drawCard = function (pack, amount, hand, initial) {
var cards = [];
cards = pack.slice(0, amount);
pack.splice(0, amount);
if (!initial) {
hand.push.apply(hand, cards);
}
return cards;
};
In action (see full example below in the 'penalisingTaken' function), note that I am actually passing in the table.forcedDraw variable (that holds the count for the amount of "2" card that were played):
table.gameObj.drawCard(table.pack, table.forcedDraw, player.hand, 0);
At the moment, on the front-end I have a "Draw a card" button, which, allows you to draw one card from the pack. When a player plays "2" and the next player has "2" in his/her hand as well, I place a new button that simply says "Penalising cards". This means that you have two options: play the "2" in your hand or take two cards from the pack. This functionality is enabled by the socket.emit("playOption", { value: true });
bit from the code above and the front-end code looks like this:
socket.on('playOption', function (data) {
$('#playOption').html(data.message);
if (data.value) {
$('#penalising').show();
} else {
$('#penalising').hide();
$('#playOption').hide();
}
});
#penalising
is of course, as mentioned before, a button:
<button id="penalising" class="btn btn-warning">Penalising cards</button>
Clicking this button calls the 'penalisingTaken' function on the server:
$('#penalising').click(function () {
socket.emit('penalisingTaken', { tableID: 1 });
$('#penalising').hide();
});
socket.on('penalisingTaken', function (data) {
var player = room.getPlayer(socket.id);
var table = room.getTable(data.tableID);
if (table.actionCard) {
table.gameObj.drawCard(table.pack, table.forcedDraw, player.hand, 0);
socket.emit('play', { hand: player.hand }); //send the card in hands to player
io.sockets.emit('updatePackCount', { packCount: table.pack.length });
table.forcedDraw = 0; //reset forced Draw variable
table.actionCard = false; //set the action card to false
table.penalisingActionCard = false; //set the penalising action card to false;
/*PROGRESS ROUND*/
table.progressRound(player); //end of turn
socket.emit('turn', { myturn: false });
messaging.sendEventToAllPlayersButPlayer(
'turn',
{ myturn: true },
io,
table.players,
player
);
messaging.sendEventToAllPlayersButPlayer(
'cardInHandCount',
{ cardsInHand: player.hand.length },
io,
table.players,
player
);
}
});
The logic applied to the 'drawCard' method is exactly the same as shown in the 'preliminaryRoundCheck' function.
That's it for this time. As mentioned before I'm still working on the two other request cards - they are slightly more complicated than the one explained in the post as players can make various requests and they can also override each other's requests so I have to make sure that all combination of requests are programmed properly.
Stay tuned ...