New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@dumpster-fire/game

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@dumpster-fire/game - npm Package Compare versions

Comparing version 1.3.1 to 1.4.0

src/util/addCardToHand.js

4

package.json
{
"name": "@dumpster-fire/game",
"version": "1.3.1",
"version": "1.4.0",
"main": "src/index",

@@ -23,3 +23,3 @@ "dependencies": {

},
"gitHead": "19fb1330a526ced3b1922555b77cf7e9f7077813"
"gitHead": "996b127dd4118fd0511f9aaa51d9f8934bb67f89"
}

@@ -1,75 +0,37 @@

import uuid from "uuid/v4";
import { Game, PlayerView } from "boardgame.io/core";
import { CardType, getCardFromType } from "@dumpster-fire/cards";
import { CardType } from "@dumpster-fire/cards";
import { MAX_HAND_SIZE, STARTING_HAND_SIZE } from "@dumpster-fire/constants";
import createDeck from "./util/createDeck";
import createPublicCard from "./util/createPublicCard";
import insertIntoDeck from "./util/insertIntoDeck";
import insertRandomlyIntoDeck from "./util/insertRandomlyIntoDeck";
import isPlayerUnderHandLimit from "./util/isPlayerUnderHandLimit";
import revealPreviewCards from "./util/revealPreviewCards";
import findCard from "./util/findCard";
import discardCard from "./util/discardCard";
import drawCard from "./util/drawCard";
import addCardToHand from "./util/addCardToHand";
import validators from "./validators";
const STARTING_HAND_SIZE = 3;
const MAX_HAND_SIZE = 3;
const DISTRIBUTION = {
4: {
[CardType.INCIDENT]: 11,
[CardType.FORCE_PUSH]: 3,
[CardType.PULL_REQUEST]: 7,
[CardType.CODE_REVIEW]: 7,
[CardType.RETRO]: 3,
[CardType.BLAME]: 4,
[CardType.QUICK_MEETING]: 3,
[CardType.MONITOR]: 4,
[CardType.REPRIORITIZE]: 3,
},
const defaultPhaseState = {
played: [],
};
const createDeck = numPlayers => {
if (!DISTRIBUTION[numPlayers]) {
throw new Error("Invalid number of players");
const doesPlayerHaveCard = (G, ctx, card, playerID) =>
G.players[playerID || ctx.currentPlayer].hand.find(findCard(card));
const checkPlayerHasCard = (...args) => {
if (!doesPlayerHaveCard(...args)) {
throw new Error("You don't own that card, cheater.");
}
const createCard = cardType => {
return { ...getCardFromType(cardType), id: uuid() };
};
const createCards = ([cardType, number]) =>
[...Array(number)].map(() => createCard(cardType));
const entries = Object.entries(DISTRIBUTION[numPlayers]);
const incidents = entries.find(([type]) => type === CardType.INCIDENT);
const actions = entries.filter(([type]) => type !== CardType.INCIDENT);
const incidentCards = createCards(incidents);
const actionCards = actions
.map(createCards)
.reduce((acc, cards) => [...acc, ...cards], []);
return [incidentCards, actionCards];
};
// Remove card from player's hand and add to discard pile
const discard = (G, ctx, card) => {
G.players[ctx.currentPlayer].hand = G.players[ctx.currentPlayer].hand.filter(
({ id }) => id !== card.id
);
G.discard.push(card);
const checkHasPlayedCard = (G, ctx, card) => {
if (G.phaseState.played.find(findCard(card))) {
throw new Error("You have already played this card, cheater.");
}
};
const isPlayerUnderHandLimit = (G, ctx) => {
// Don't count incidents as part of hand
// Don't count played cards as part of hand
console.log(G.players[ctx.currentPlayer].hand, ctx.currentPlayer);
const actions = G.players[ctx.currentPlayer].hand.filter(
({ type }) => type !== CardType.INCIDENT
);
const played = G.phaseState.played;
return actions.length - played.length <= MAX_HAND_SIZE;
};
const defaultPhaseState = {
played: [],
preview: [],
};
const game = Game({

@@ -86,29 +48,59 @@ name: "dumpster-fire",

const incidentState = {};
const publicHands = {};
[...Array(ctx.numPlayers)].forEach((_, i) => {
const incident = incidents.shift();
const hand = [
incident,
...[...Array(STARTING_HAND_SIZE)].map(() => actions.shift()),
];
const hand = [...Array(STARTING_HAND_SIZE)].map(() => actions.shift());
incidentState[`${i}`] = [incident];
playerState[`${i}`] = {
hand,
preview: [],
};
publicHands[`${i}`] = hand.map(createPublicCard);
});
const deck = ctx.random.Shuffle([...incidents, ...actions]);
const publicDeck = deck.map(createPublicCard);
return {
// Holds state for each player
players: playerState,
// Public incidents state
incidents: incidentState,
// This does not get shared to clients at all
secret: {
deck,
// Full deck details
/// Map of card id -> card
deck: new Map(deck.map(card => [card.id, card])),
},
deck,
// The public deck, is ordered and only contains the id
deck: publicDeck,
// This is a map of playerID -> their public hand (e.g. only ids)
publicHands,
// The detailed last played hand
lastPlayed: [],
// Discard pile (public)
discard: [],
// Pull request list
pullRequests: [],
// State that gets reset on every turn
phaseState: {
...defaultPhaseState,
},
// The direction of play (-1 for reverse order)
turnDirection: 1,
incidents: incidentState,
// Maximum number of cards you can have at the end of the draw phase
maxHandSize: MAX_HAND_SIZE,

@@ -120,12 +112,3 @@ };

drawCard: (G, ctx) => {
let card;
if (G.phaseState.retro) {
// Draw from discard
card = G.discard.shift();
} else {
card = G.deck.shift();
}
G.players[ctx.currentPlayer].hand.push(card);
drawCard(G, ctx, ctx.currentPlayer);
},

@@ -136,8 +119,18 @@

if (!G.phaseState.canSkipDraw) {
throw new Error("You need to draw a card first.");
throw new Error("You need to draw a card.");
}
ctx.events.endPhase();
},
endTurn: (G, ctx) => {},
discard: (G, ctx, card) => {
if (card.type === CardType.INCIDENT) {
throw new Error("You can't discard an Incident, cheater.");
}
checkPlayerHasCard(G, ctx, card);
// TODO: make sure player owns card and that it is not an incident
discardCard(G, ctx, card, ctx.currentPlayer);
},
finishAction: (G, ctx) => {

@@ -148,27 +141,85 @@ // finish playing cards, go to draw phase

reshuffle: (G, ctx, newOrder) => {
const { preview, reshuffle } = G.phaseState;
[CardType.BLAME]: (G, ctx, target) => {
if (!G.incidents.hasOwnProperty(target)) {
throw new Error("Invalid player to blame");
}
if (!reshuffle) {
throw new Error("Not allowed to reshuffle right now");
if (G.incidents[target].length > 1) {
throw new Error("You cannot blame a player with more than 1 incident.");
}
const shuffled = [
preview.find(card => card.id === newOrder.bottom),
preview.find(card => card.id === newOrder.middle),
preview.find(card => card.id === newOrder.top),
];
shuffled.forEach(card => G.deck.unshift(card));
const incident = G.incidents[ctx.currentPlayer].shift();
G.incidents[target].push(incident);
ctx.events.endPhase();
},
G.phaseState.preview = [];
G.phaseState.reshuffle = false;
[CardType.MONITOR]: (G, ctx, newOrder) => {
const { preview } = G.players[ctx.currentPlayer];
const previewCardIds = preview.map(({ id }) => id);
// make sure the shuffled cards match the old cards
if (!newOrder.every(newCard => previewCardIds.includes(newCard.id))) {
throw new Error("Stop cheating.");
}
G.deck = [...newOrder.map(({ id }) => ({ id })), ...G.deck];
// TODO: Clean preview by creating new ids for cards
G.players[ctx.currentPlayer].preview = [];
ctx.events.endPhase();
},
[CardType.QUICK_MEETING]: (G, ctx) => {
// TODO: Clean preview by creating new ids for cards
G.players[ctx.currentPlayer].preview = [];
ctx.events.endPhase();
},
[CardType.CODE_REVIEW]: (G, ctx, card, pullRequestObj, deckLocation) => {
// Choose any Pull Request in play.
// If you are the author of that Pull Request:
// - resolve the Incident by shuffling it into the deck
// If you are not the author:
// - resolve the Incident by placing it anywhere in the deck
// - take the Pull Request and put it in your hand
if (!pullRequestObj) {
throw new Error("Select a Pull Request to Code Review");
}
if (deckLocation < 0 || deckLocation >= G.deck.length) {
throw new Error("Invalid deck location");
}
const [author, originalPullRequestCard, incident] = pullRequestObj;
if (ctx.currentPlayer === author) {
insertRandomlyIntoDeck(G, ctx, incident);
discardCard(G, ctx, originalPullRequestCard, ctx.currentPlayer);
} else {
insertIntoDeck(G, ctx, incident, deckLocation);
// Player should pick up the PR into their hand
addCardToHand(G, ctx, originalPullRequestCard, ctx.currentPlayer);
}
// Remove pull request
G.pullRequests = G.pullRequests.filter(
pullRequest =>
pullRequest[1].id !== card.id && pullRequest[2].id !== incident.id
);
},
playCard: (G, ctx, card, ...args) => {
console.log(`Player #${ctx.playerID} playing: ${card.type}`);
// TODO: Check that player actually owns the card (in `flow`)
checkPlayerHasCard(G, ctx, card);
// Card gets added to `phaseState.played` at the end of the function
// so check here if card has been played this turn already
checkHasPlayedCard(G, ctx, card);
// Check validation logic first
if (validators[card.type] === "function") {
if (typeof validators[card.type] === "function") {
validators[card.type](G, ctx, card, ...args);

@@ -180,4 +231,4 @@ }

[CardType.RETRO]: (G, ctx, card) => {
G.phaseState.canDrawFromDiscard = true;
G.phaseState.retro = true;
G.phaseState.played.push(card);
},

@@ -187,3 +238,3 @@

G.turnDirection = G.turnDirection * -1;
G.phaseState.played.push(card);
ctx.events.endTurn();
},

@@ -201,66 +252,27 @@

[CardType.CODE_REVIEW]: (
G,
ctx,
[author, card, incident, turn],
deckLocation
) => {
// Choose any Pull Request in play.
// If you are the author of that Pull Request:
// - resolve the Incident by shuffling it into the deck
// If you are not the author:
// - resolve the Incident by placing it anywhere in the deck
// - take the Pull Request and put it in your hand
if (ctx.playerId === author) {
insertRandomlyIntoDeck(G, ctx, incident);
discard(G, ctx, card);
} else {
insertIntoDeck(G, ctx, incident, deckLocation);
// Player should pick up the PR into their hand
G.player[ctx.playerId].hand.push(card);
}
// Remove pull request
G.pullRequests = G.pullRequests.filter(
pullRequest =>
pullRequest[1].id !== card.id && pullRequest[2].id !== incident.id
);
[CardType.CODE_REVIEW]: (G, ctx, card) => {
ctx.events.endPhase({ next: CardType.CODE_REVIEW });
},
[CardType.BLAME]: (G, ctx, card, target) => {
G.phaseState.played.push(card);
const incident = G.incidents[ctx.playerID].shift();
// Remove incident from player's hand
G.players[ctx.playerID].hand = G.players[ctx.playerID].hand.filter(
({ id }) => id !== incident.id
);
// Add to target player
if (G.players[target]) {
G.players[target].hand.push(incident);
}
G.incidents[target].push(incident);
ctx.events.endPhase({ next: CardType.BLAME });
},
[CardType.QUICK_MEETING]: (G, ctx, card) => {
G.phaseState.played.push(card);
const nextCard = G.deck[0];
// TODO handle security with card (e.g. player can see card id and trace
// the card throughout future turns)
G.phaseState.preview.push(nextCard);
G.players[ctx.currentPlayer].preview = [nextCard];
G.phaseState.canSkipDraw = true;
ctx.events.endPhase({ next: CardType.QUICK_MEETING });
},
[CardType.MONITOR]: (G, ctx, card) => {
G.phaseState.played.push(card);
// TODO handle security with card (e.g. player can see card id and trace
// the card throughout future turns)
// Prob just needs new ids when we preview
G.phaseState.preview = G.deck.splice(0, 3);
G.players[ctx.currentPlayer].preview = G.deck.splice(0, 3);
G.phaseState.reshuffle = true;
ctx.events.endPhase({ next: "monitor" });
},

@@ -276,3 +288,3 @@

// Discard incident
discard(G, ctx, incident);
discardCard(G, ctx, incident, ctx.currentPlayer);

@@ -290,12 +302,5 @@ // Insert incident back into the deck

G.phaseState.played.push(card);
logic[card.type](G, ctx, card, ...args);
G.phaseState._cardPlayed = card.type;
},
discard: (G, ctx, card) => {
console.log("discard");
// TODO: make sure player owns card and that it is not an incident
discard(G, ctx, card);
},
},

@@ -313,8 +318,3 @@

onTurnBegin: (G, ctx) => {
// Always start in "play" phase
// if (ctx.phase !== "play") {
// ctx.events.endPhase({ next: "play" });
// }
},
onTurnBegin: (G, ctx) => {},

@@ -324,5 +324,9 @@ onTurnEnd: (G, ctx) => {

G.phaseState.played.forEach(card => {
discard(G, ctx, card);
discardCard(G, ctx, card);
// Save lastPlayed hand
G.lastPlayed.push(card);
});
// TODO: Look for `preview` hand and give them new ids so they can't be tracked
// Reset phaseState

@@ -335,20 +339,46 @@ G.phaseState = {

onMove: (G, ctx, { type, args, playerID }) => {
console.log("on move", ctx.currentPlayer, playerID, args);
if (type === "drawCard") {
const { drawnCard } = G.phaseState;
if (type === "finishAction") {
console.log("finishAction called, ending phase");
// ctx.events.endPhase();
} else if (type === "skipDraw") {
// Fetch the actual card from master deck
const fullCard =
G.secret.deck.get(drawnCard.id) ||
(G.phaseState.drawFromDiscard &&
((!!drawnCard.type && drawnCard) ||
G.discard.find(findCard(drawnCard))));
if (!fullCard) {
throw new Error("Stop cheating.");
}
const cardIndex = G.players[playerID].hand.findIndex(
findCard(fullCard)
);
// Full card not found in players hand
if (cardIndex === -1) {
throw new Error("Stop cheating.");
}
// Check if incident and add to incidents array
if (fullCard.type === CardType.INCIDENT) {
G.incidents[playerID].push(fullCard);
// Remove from hand because incidents do not reside in player's hand
G.players[playerID].hand = G.players[playerID].hand.filter(
findCard(fullCard)
);
} else {
// Update players hand with the full card
G.players[playerID].hand[cardIndex] = { ...fullCard };
}
ctx.events.endPhase();
} else if (type === "drawCard") {
ctx.events.endPhase();
} else if (type === "discard") {
} else if (type === "playCard") {
if (
G.phaseState._cardPlayed === CardType.REPRIORITIZE ||
G.phaseState._cardPlayed === CardType.PULL_REQUEST
) {
console.log(G.phaseState._cardPlayed, " end turn immediately");
ctx.events.endTurn();
const [card] = args;
if (card.type === CardType.MONITOR) {
revealPreviewCards(G, ctx, playerID);
}
if (card.type === CardType.QUICK_MEETING) {
revealPreviewCards(G, ctx, playerID);
}
}

@@ -359,3 +389,3 @@ },

play: {
allowedMoves: ["playCard", "reshuffle", "finishAction"],
allowedMoves: ["playCard", "finishAction"],
next: "draw",

@@ -384,2 +414,22 @@ onPhaseBegin: (G, ctx) => {

},
[CardType.BLAME]: {
allowedMoves: [CardType.BLAME],
next: "play",
},
[CardType.CODE_REVIEW]: {
allowedMoves: [CardType.CODE_REVIEW],
next: "play",
},
[CardType.MONITOR]: {
allowedMoves: [CardType.MONITOR],
next: "play",
},
[CardType.QUICK_MEETING]: {
allowedMoves: [CardType.QUICK_MEETING],
next: "play",
},
},

@@ -386,0 +436,0 @@

@@ -1,3 +0,5 @@

export default function insertIntoDeck(G, ctx, card, location) {
G.deck.splice(location, 0, card);
import createPublicCard from "./createPublicCard";
export default function insertIntoDeck(G, _ctx, card, location) {
G.deck.splice(location, 0, createPublicCard(card));
}

@@ -13,5 +13,5 @@ import { CardType } from "@dumpster-fire/cards";

[CardType.PULL_REQUEST]: (G, ctx, card) => {
if (G.incidents[ctx.playerID].length < 1) {
if (G.incidents[ctx.currentPlayer].length < 1) {
throw new Error(
"You can only submit a Pull Request if you have an incident"
"You can only submit a Pull Request if you have an Incident"
);

@@ -21,32 +21,18 @@ }

[CardType.CODE_REVIEW]: (
G,
ctx,
[author, card, incident, turn],
deckLocation
) => {
// Choose any Pull Request in play.
// If you are the author of that Pull Request:
// - resolve the Incident by shuffling it into the deck
// If you are not the author:
// - resolve the Incident by placing it anywhere in the deck
// - take the Pull Request and put it in your hand
if (!G.pullRequests.length) {
[CardType.CODE_REVIEW]: (G, ctx, card, pullRequestObj, deckLocation) => {
if (
!G.pullRequests.length ||
!G.pullRequests.filter(
([author, card, incident, turn]) =>
author !== ctx.currentPlayer || turn > 0
).length
) {
throw new Error("There are no Pull Requests to Code Review");
}
if (deckLocation < 0 || deckLocation >= G.deck.length) {
throw new Error("Invalid deck location");
}
},
[CardType.BLAME]: (G, ctx, card, target) => {
if (!target) {
throw new Error("You need a player to blame.");
[CardType.BLAME]: (G, ctx, card) => {
if (G.incidents[ctx.currentPlayer].length < 1) {
throw new Error("You don't have any Incidents to blame someone for");
}
if (G.incidents[target].length > 1) {
throw new Error("You cannot blame a player with more than 1 incident.");
}
},

@@ -53,0 +39,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc