Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Set Junior mode #177

Merged
merged 1 commit into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion database.rules.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
},
"mode": {
".write": "auth != null && auth.uid == data.parent().child('host').val() && newData.exists()",
".validate": "newData.isString()&& (newData.val() == 'normal' || newData.val() == 'ultraset' || newData.val() == 'setchain')"
".validate": "newData.isString() && (newData.val() == 'normal' || newData.val() == 'setjr' || newData.val() == 'ultraset' || newData.val() == 'setchain')"
},
"enableHint": {
".write": "auth != null && auth.uid == data.parent().child('host').val() && newData.exists()",
Expand Down
27 changes: 24 additions & 3 deletions functions/src/game.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ interface GameEvent {
c4?: string;
}

export type GameMode = "normal" | "setchain" | "ultraset";
export type GameMode = "normal" | "setjr" | "setchain" | "ultraset";

/** Generates a random 81-card deck using a Fisher-Yates shuffle. */
export function generateDeck() {
Expand Down Expand Up @@ -71,6 +71,7 @@ export function findSet(deck: string[], gameMode: GameMode, old?: string[]) {
const c = conjugateCard(deck[i], deck[j]);
if (
gameMode === "normal" ||
gameMode === "setjr" ||
(gameMode === "setchain" && old!.length === 0)
) {
if (deckSet.has(c)) {
Expand All @@ -91,6 +92,21 @@ export function findSet(deck: string[], gameMode: GameMode, old?: string[]) {
return null;
}

/**
* Initialize the deck to its starting cards based on the game mode.
*
* It starts with a shuffled 81-card deck according to database state. Usually
* this would be a no-op, but for Set Junior, we need to remove some subset of
* the cards before the game starts.
*/
function initializeDeck(deck: string[], gameMode: GameMode) {
if (gameMode === "setjr") {
// Remove all cards except those with solid shading.
return deck.filter((card) => card[2] === "0");
}
return deck;
}

/** Check if cards are valid (all distinct and exist in deck) */
function isValid(deck: Set<string>, cards: string[]) {
for (let i = 0; i < cards.length; i++) {
Expand Down Expand Up @@ -162,13 +178,18 @@ export function replayEvents(
// Array.sort() is guaranteed to be stable in Node.js, and the latest ES spec
events.sort((e1, e2) => e1.time - e2.time);

const deck: Set<string> = new Set(gameData.child("deck").val());
const deck: Set<string> = new Set(
initializeDeck(gameData.child("deck").val(), gameMode),
);
const history: GameEvent[] = [];
const scores: Record<string, number> = {};
let finalTime = 0;
for (const event of events) {
let eventValid = false;
if (gameMode === "normal" && replayEventNormal(deck, event))
if (
(gameMode === "normal" || gameMode === "setjr") &&
replayEventNormal(deck, event)
)
eventValid = true;
if (gameMode === "setchain" && replayEventChain(history, deck, event))
eventValid = true;
Expand Down
2 changes: 1 addition & 1 deletion functions/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export const finishGame = functions.https.onCall(async (data, context) => {
snapshot.child("enableHint").val() &&
snapshot.child("users").numChildren() === 1 &&
snapshot.child("access").val() === "private" &&
gameMode === "normal"
(gameMode === "normal" || gameMode === "setjr")
) {
return;
}
Expand Down
221 changes: 0 additions & 221 deletions scripts/src/calcStats.js

This file was deleted.

10 changes: 1 addition & 9 deletions scripts/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,12 @@ import { cert, initializeApp } from "firebase-admin/app";
import inquirer from "inquirer";
import process from "node:process";

import { calcStats } from "./calcStats.js";
import { fixGames } from "./fixGames.js";
import { sanitizeNames } from "./sanitizeNames.js";
import { listAdmins, listPatrons, toggleAdmin } from "./users.js";

// Add scripts as functions to this array
const scripts = [
listAdmins,
listPatrons,
toggleAdmin,
sanitizeNames,
fixGames,
calcStats,
];
const scripts = [listAdmins, listPatrons, toggleAdmin, sanitizeNames, fixGames];

initializeApp({
credential: cert("./credential.json"),
Expand Down
4 changes: 3 additions & 1 deletion src/components/ChatCards.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ function ChatCards({ item, gameMode, startedAt }) {
id={item.user}
/>
</div>
{(gameMode === "normal" || gameMode === "setchain") && (
{(gameMode === "normal" ||
gameMode === "setjr" ||
gameMode === "setchain") && (
<div>
<SetCard size="sm" value={item.c1} />
<SetCard size="sm" value={item.c2} />
Expand Down
31 changes: 25 additions & 6 deletions src/components/GameSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import firebase from "../firebase";
import { hasHint, modes } from "../util";

const useStyles = makeStyles(() => ({
settings: { display: "flex", flexDirection: "column", alignItems: "center" },
settings: {
display: "flex",
flexDirection: "column",
alignItems: "center",
paddingInline: 4,
},
}));

const hintTip =
Expand All @@ -31,8 +36,13 @@ function GameSettings({ game, gameId, userId }) {

return (
<div className={classes.settings}>
<RadioGroup row value={gameMode} onChange={handleChangeMode}>
{["normal", "setchain", "ultraset"].map((mode) => (
<RadioGroup
row
sx={{ justifyContent: "center" }}
value={gameMode}
onChange={handleChangeMode}
>
{Object.keys(modes).map((mode) => (
<Tooltip
key={mode}
arrow
Expand All @@ -41,22 +51,31 @@ function GameSettings({ game, gameId, userId }) {
>
<FormControlLabel
value={mode}
control={<Radio />}
control={<Radio size="small" sx={{ width: 32, height: 32 }} />}
disabled={userId !== game.host}
label={modes[mode].name}
slotProps={{ typography: { variant: "body2" } }}
/>
</Tooltip>
))}
</RadioGroup>
{gameMode === "normal" && (
{["normal", "setjr"].includes(gameMode) && (
<Tooltip arrow placement="left" title={hintTip}>
<FormControlLabel
control={<Switch checked={hasHint(game)} onChange={toggleHint} />}
control={
<Switch
size="small"
checked={hasHint(game)}
onChange={toggleHint}
/>
}
label="Enable Hints"
disabled={
Object.keys(game.users || {}).length > 1 ||
game.access !== "private"
}
slotProps={{ typography: { variant: "body2" } }}
sx={{ my: 0.25 }}
/>
</Tooltip>
)}
Expand Down
Loading
Loading