From 814d847f553df818b63b2f33a90d270b11f9be88 Mon Sep 17 00:00:00 2001 From: Ariel Gerstein Date: Tue, 19 Mar 2019 20:19:47 -0300 Subject: [PATCH] Set winning team for each hand and round, set next player to move - Add hands key to match model - Set next player to move based on previous hand result (the winner player of the prev hand should start) - Set played card as played - Set round winner when ending - At the end of each hand set hand winner and next hand initial player (to determine the ending of the hand) --- server/src/data/cards.js | 26 ---------- server/src/database/models/match.js | 25 ++++++++- server/src/datasources/match.js | 78 +++++++++++++++++++++++------ server/src/utils/cards.js | 64 +++++++++++++++++++++++ server/src/utils/round.js | 27 ++++++++++ 5 files changed, 178 insertions(+), 42 deletions(-) delete mode 100644 server/src/data/cards.js create mode 100644 server/src/utils/cards.js create mode 100644 server/src/utils/round.js diff --git a/server/src/data/cards.js b/server/src/data/cards.js deleted file mode 100644 index d8787e2..0000000 --- a/server/src/data/cards.js +++ /dev/null @@ -1,26 +0,0 @@ -const createCardsOfAllSets = ({ number, score }) => - ["SWORD", "BASTO", "GOLD", "CUP"].map(set => ({ - card: `${number}-${set}`, - score - })); - -const cards = [ - { card: "1-SWORD", score: 14 }, - { card: "1-BASTO", score: 13 }, - { card: "7-SWORD", score: 12 }, - { card: "7-GOLD", score: 11 }, - ...createCardsOfAllSets({ number: 3, score: 10 }), - ...createCardsOfAllSets({ number: 2, score: 9 }), - { card: "1-GOLD", score: 8 }, - { card: "1-CUP", score: 8 }, - ...createCardsOfAllSets({ number: 12, score: 7 }), - ...createCardsOfAllSets({ number: 11, score: 6 }), - ...createCardsOfAllSets({ number: 10, score: 5 }), - { card: "7-BASTO", score: 4 }, - { card: "7-CUP", score: 4 }, - ...createCardsOfAllSets({ number: 6, score: 3 }), - ...createCardsOfAllSets({ number: 5, score: 2 }), - ...createCardsOfAllSets({ number: 4, score: 1 }) -]; - -module.exports = cards; diff --git a/server/src/database/models/match.js b/server/src/database/models/match.js index ebdb3a9..57965fd 100644 --- a/server/src/database/models/match.js +++ b/server/src/database/models/match.js @@ -1,7 +1,7 @@ const mongoose = require("mongoose"); const shortid = require("shortid"); const mongooseHidden = require("mongoose-hidden")(); -const cards = require("../../data/cards"); +const { cards } = require("../../utils/cards"); const cardValidator = { validator: card => cards.includes(card), @@ -9,6 +9,29 @@ const cardValidator = { }; const roundSchema = new mongoose.Schema({ + winner: { + type: String, + enum: ["first", "second"] + }, + hands: { + type: [ + { + winnerTeam: { + type: String, + enum: ["first", "second", "tie"], + required: true + }, + initialPlayerIndex: { + type: Number, + required: true + } + } + ], + validate: { + validator: val => val.length <= 3, + message: props => `${props.value} length is greater than 3` + } + }, moves: [ { user: { diff --git a/server/src/datasources/match.js b/server/src/datasources/match.js index 48759b4..076605d 100644 --- a/server/src/datasources/match.js +++ b/server/src/datasources/match.js @@ -4,7 +4,8 @@ const { DataSource } = require("apollo-datasource"); const { pubsub, events } = require("../subscriptions"); const pickRandom = require("pick-random"); -const cards = require("../data/cards"); +const { cards, getHandTeamWinner } = require("../utils/cards"); +const getRoundWinnerTeam = require("../utils/round"); const Match = mongoose.model("Match"); @@ -68,6 +69,11 @@ const getNewRoundUpdate = playersIds => { playerId, cards: [] })), + hands: [ + { + initialPlayerIndex: 0 + } + ], nextPlayer: R.head(playersIds) } }; @@ -277,15 +283,47 @@ class MatchAPI extends DataSource { throw new Error(`The card with the id ${cardId} has already been played`); } - const lastRoundIndex = match.rounds.length - 1; - const playerIndex = R.findIndex(({ data }) => data.equals(userId))( - match.players - ); - const nextPlayerId = R.pipe( - R.inc, - R.modulo(R.__, match.playersCount), - nextPlayerIndex => match.players[nextPlayerIndex].data - )(playerIndex); + const currentRoundIndex = match.rounds.length - 1; + const currentRound = R.last(match.rounds); + const currentHandIndex = currentRound.hands.length - 1; + const currentHand = R.last(currentRound.hands); + + const playersIds = R.map(player => player.data.toString(), match.players); + const playerIndex = R.findIndex(R.equals(userId))(playersIds); + + const lastPlayerIndex = + currentHand.initialPlayerIndex - 1 < 0 + ? match.playersCount - 1 + : currentHand.initialPlayerIndex - 1; + const lastPlayerId = playersIds[lastPlayerIndex]; + const isLastPlayerOfHand = userId === lastPlayerId; + const { handWinnerTeam, handStarterPlayerIndex } = + isLastPlayerOfHand && + getHandTeamWinner( + R.pipe( + R.map(R.path(["cards", currentHandIndex, "card"])), + R.map(R.when(R.isNil, R.always(selectedCard.card))) + )(currentRound.cardsPlayedByPlayer) + ); + + const nextPlayerIndex = + R.type(handStarterPlayerIndex) === "Number" + ? handStarterPlayerIndex + : R.pipe( + R.inc, + R.modulo(R.__, match.playersCount) + )(playerIndex); + + const nextPlayerId = match.players[nextPlayerIndex].data; + + const handsWinnerTeam = + isLastPlayerOfHand && + R.pipe( + R.map(R.prop("winnerTeam")), + R.filter(Boolean), + R.append(handWinnerTeam) + )(currentRound.hands); + const roundWinnerTeam = getRoundWinnerTeam(handsWinnerTeam); const updatedMatch = R.pipe( match => match.toObject(), @@ -296,15 +334,25 @@ class MatchAPI extends DataSource { await Match.findByIdAndUpdate( matchId, { + $set: { + [`rounds.${currentRoundIndex}.nextPlayer`]: nextPlayerId, + [`rounds.${currentRoundIndex}.cardsByPlayer.${playerIndex}.cards.${selectedCardIndex}.played`]: true, + [`rounds.${currentRoundIndex}.winner`]: roundWinnerTeam || null, + ...(isLastPlayerOfHand + ? { + [`rounds.${currentRoundIndex}.hands.${currentHandIndex}.winnerTeam`]: handWinnerTeam, + [`rounds.${currentRoundIndex}.hands.${currentHandIndex + + 1}`]: { + initialPlayerIndex: nextPlayerIndex + } + } + : {}) + }, $push: { - [`rounds.${lastRoundIndex}.cardsPlayedByPlayer.${playerIndex}.cards`]: { + [`rounds.${currentRoundIndex}.cardsPlayedByPlayer.${playerIndex}.cards`]: { id: cardId, card: selectedCard.card } - }, - $set: { - [`rounds.${lastRoundIndex}.nextPlayer`]: nextPlayerId, - [`rounds.${lastRoundIndex}.cardsByPlayer.${playerIndex}.cards.${selectedCardIndex}.played`]: true } }, { diff --git a/server/src/utils/cards.js b/server/src/utils/cards.js new file mode 100644 index 0000000..2796462 --- /dev/null +++ b/server/src/utils/cards.js @@ -0,0 +1,64 @@ +const R = require("ramda"); + +const getHandTeamWinner = handCards => { + const cardsScores = R.pipe( + R.map(card => + R.pipe( + R.find(R.propEq("card", card)), + R.prop("score") + )(cards) + ) + )(handCards); + + const highestScoreIndex = cardsScores.reduce( + (highestScoreIndexes, currentScore, i, scores) => { + if (Math.max(...scores) === currentScore) { + return highestScoreIndexes.concat(i); + } + return highestScoreIndexes; + }, + [] + ); + + const handWinnerTeam = + highestScoreIndex.length > 1 + ? "tie" + : highestScoreIndex[0] % 2 === 0 + ? "first" + : "second"; + + // Get index of first player with highest score + const nextPlayerIndex = handWinnerTeam !== "tie" && highestScoreIndex[0]; + + return { handWinnerTeam, handStarterPlayerIndex: nextPlayerIndex }; +}; + +const createCardsOfAllSets = ({ number, score }) => + ["SWORD", "BASTO", "GOLD", "CUP"].map(set => ({ + card: `${number}-${set}`, + score + })); + +const cards = [ + { card: "1-SWORD", score: 14 }, + { card: "1-BASTO", score: 13 }, + { card: "7-SWORD", score: 12 }, + { card: "7-GOLD", score: 11 }, + ...createCardsOfAllSets({ number: 3, score: 10 }), + ...createCardsOfAllSets({ number: 2, score: 9 }), + { card: "1-GOLD", score: 8 }, + { card: "1-CUP", score: 8 }, + ...createCardsOfAllSets({ number: 12, score: 7 }), + ...createCardsOfAllSets({ number: 11, score: 6 }), + ...createCardsOfAllSets({ number: 10, score: 5 }), + { card: "7-BASTO", score: 4 }, + { card: "7-CUP", score: 4 }, + ...createCardsOfAllSets({ number: 6, score: 3 }), + ...createCardsOfAllSets({ number: 5, score: 2 }), + ...createCardsOfAllSets({ number: 4, score: 1 }) +]; + +module.exports = { + getHandTeamWinner, + cards +}; diff --git a/server/src/utils/round.js b/server/src/utils/round.js new file mode 100644 index 0000000..a020212 --- /dev/null +++ b/server/src/utils/round.js @@ -0,0 +1,27 @@ +const R = require("ramda"); + +const isTeamWinner = (handsResults, team, isFirstTeam) => + R.anyPass([ + // If the team won 2 or more hands + R.pipe( + R.filter(R.equals(team)), + R.length, + winningRounds => winningRounds >= 2 + ), + // If there is one tie and won 1 hand + R.both(R.find(R.equals("tie")), R.find(R.equals(team))), + // If it's the first team and all hands are a tie + R.both(R.always(isFirstTeam), R.equals(["tie", "tie", "tie"])) + ])(handsResults); + +const getRoundWinnerTeam = handsResults => { + if (isTeamWinner(handsResults, "first", true)) { + return "first"; + } + if (isTeamWinner(handsResults, "second")) { + return "second"; + } + return false; +}; + +module.exports = getRoundWinnerTeam;