Skip to content

Commit

Permalink
Enable hints on Set-Chain and UltraSet (#178)
Browse files Browse the repository at this point in the history
This enables hints on all game types. I actually don't remember why this wasn't enabled from the start. Anyway because this will disrupt about 20,000 games played in the profile pages that have enableHint set to `true` but were in set-chain/ultraset, I also wrote a migration script to fix this.
  • Loading branch information
ekzhang authored Dec 29, 2024
1 parent e551587 commit a14d68a
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 50 deletions.
3 changes: 1 addition & 2 deletions functions/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ export const finishGame = functions.https.onCall(async (data, context) => {
if (
snapshot.child("enableHint").val() &&
snapshot.child("users").numChildren() === 1 &&
snapshot.child("access").val() === "private" &&
(gameMode === "normal" || gameMode === "setjr")
snapshot.child("access").val() === "private"
) {
return;
}
Expand Down
58 changes: 36 additions & 22 deletions scripts/src/fixGames.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,44 @@
// Note: This is deprecated.
import assert from "assert";
import { getDatabase } from "firebase-admin/database";
import PQueue from "p-queue";

/** Fix games that have `endedAt === 0` due to a site migration bug in v3.0.0. */
import { databaseIterator } from "./utils.js";

/**
* Fix games that have `enableHint` in game modes that did not support hints.
*
* This is a retroactive migration script for version 3.5.0, where we're
* enabling hints for all game modes and therefore need a way to track in the
* database which games actually had hints on.
*
* About 0.2% of historical games needed to be updated.
*/
export async function fixGames() {
const badGames = await getDatabase()
.ref("games")
.orderByChild("endedAt")
.equalTo(0)
.once("value");
let count = 0;
const queue = new PQueue({ concurrency: 200 });

for (const [gameId, game] of Object.entries(badGames.val())) {
console.log(`Fixing game ${gameId}...`);
console.log({ [gameId]: game });
assert.strictEqual(game.status, "done");
const events = await getDatabase()
.ref(`gameData/${gameId}/events`)
.once("value");
let lastTime = 0;
events.forEach((event) => {
lastTime = event.val().time;
});
console.log(`Setting endedAt = ${lastTime}...`);
await getDatabase().ref(`games/${gameId}/endedAt`).set(lastTime);
console.log("Done.\n");
for await (const [gameId, game] of databaseIterator("games")) {
if (++count % 100000 === 0) {
console.log(`Processed ${count} games...`);
}
const { enableHint, users, access, mode } = game.val();
// See hasHint() function in util.js
if (
enableHint &&
users &&
Object.keys(users).length === 1 &&
access === "private" &&
(mode === "setchain" || mode === "ultraset")
) {
console.log(`Fixing game ${gameId}...`);
// Change this to `true` when you're ready to do a non-dry run.
// eslint-disable-next-line no-constant-condition
if (false) {
await queue.onEmpty();
queue.add(getDatabase().ref(`games/${gameId}/enableHint`).set(false));
}
}
}

await queue.onIdle();
console.log("Completed all games!");
}
39 changes: 19 additions & 20 deletions src/components/GameSettings.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,26 +59,25 @@ function GameSettings({ game, gameId, userId }) {
</Tooltip>
))}
</RadioGroup>
{["normal", "setjr"].includes(gameMode) && (
<Tooltip arrow placement="left" title={hintTip}>
<FormControlLabel
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>
)}

<Tooltip arrow placement="left" title={hintTip}>
<FormControlLabel
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>
</div>
);
}
Expand Down
11 changes: 7 additions & 4 deletions src/pages/GamePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ function GamePage() {

const gameMode = game.mode || "normal";
const spectating = !game.users || !(user.id in game.users);
const maxHints = gameMode === "ultraset" ? 4 : 3;

const { current, scores, history, boardSize } = computeState(
gameData,
Expand Down Expand Up @@ -338,7 +339,7 @@ function GamePage() {

function handleAddHint() {
setNumHints((numHints) => {
if (numHints === 3) {
if (numHints === maxHints) {
return numHints;
}
return numHints + 1;
Expand Down Expand Up @@ -479,14 +480,16 @@ function GamePage() {
className={classes.sideColumn}
>
<GameSidebar game={game} scores={scores} leaderboard={leaderboard} />
<Box mt={1}>
<Box mt={2}>
{hasHint(game) && (
<Button
size="large"
size="medium"
variant="outlined"
color="primary"
fullWidth
disabled={numHints === 3 || !answer || game.status === "done"}
disabled={
numHints === maxHints || !answer || game.status === "done"
}
onClick={handleAddHint}
>
Add hint: {numHints}
Expand Down
3 changes: 1 addition & 2 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,6 @@ export function hasHint(game) {
game.enableHint &&
game.users &&
Object.keys(game.users).length === 1 &&
game.access === "private" &&
(game.mode === "normal" || game.mode === "setjr")
game.access === "private"
);
}

0 comments on commit a14d68a

Please sign in to comment.