Skip to content

Commit

Permalink
Add placeholder cards for player picks
Browse files Browse the repository at this point in the history
  • Loading branch information
noahm committed Mar 9, 2024
1 parent 916d25d commit 02be671
Show file tree
Hide file tree
Showing 12 changed files with 207 additions and 73 deletions.
1 change: 1 addition & 0 deletions src/assets/i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"drawerTitle": "Settings",
"hideShowFilters": "Chart Filters",
"chartCount": "Number to draw",
"playerPicks": "Free picks",
"upperBoundLvl": "Lvl Max",
"lowerBoundLvl": "Lvl Min",
"upperBoundTier": "Tier Max",
Expand Down
38 changes: 30 additions & 8 deletions src/card-draw.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@ import { chunkInPieces, pickRandomItem, rangeI, shuffle, times } from "./utils";
import { CountingSet } from "./utils/counting-set";
import { DefaultingMap } from "./utils/defaulting-set";
import { Fraction } from "./utils/fraction";
import { DrawnChart, EligibleChart, Drawing } from "./models/Drawing";
import {
DrawnChart,
EligibleChart,
Drawing,
PlayerPickPlaceholder,
CHART_PLACEHOLDER,
CHART_DRAWN,
} from "./models/Drawing";
import { ConfigState } from "./config-state";
import { getDifficultyColor } from "./hooks/useDifficultyColor";
import {
Expand Down Expand Up @@ -322,6 +329,7 @@ export function draw(gameData: GameData, configData: ConfigState): Drawing {
...randomChart,
// Give this random chart a unique id within this drawing
id: `drawn_chart-${nanoid(5)}`,
type: CHART_DRAWN,
});
// remove drawn chart from deck so it cannot be re-drawn
selectableCharts.splice(randomIndex, 1);
Expand All @@ -342,15 +350,29 @@ export function draw(gameData: GameData, configData: ConfigState): Drawing {
}
}

const charts: Drawing["charts"] = configData.sortByLevel
? drawnCharts.sort(
(a, b) =>
chartLevelOrTier(a, useGranularLevels, false) -
chartLevelOrTier(b, useGranularLevels, false),
)
: shuffle(drawnCharts);

if (configData.playerPicks) {
charts.unshift(
...times(
configData.playerPicks,
(): PlayerPickPlaceholder => ({
id: `pick_placeholder-` + nanoid(5),
type: CHART_PLACEHOLDER,
}),
),
);
}

return {
id: `draw-${nanoid(10)}`,
charts: configData.sortByLevel
? drawnCharts.sort(
(a, b) =>
chartLevelOrTier(a, useGranularLevels, false) -
chartLevelOrTier(b, useGranularLevels, false),
)
: shuffle(drawnCharts),
charts,
players: times(defaultPlayersPerDraw, () => ""),
bans: [],
protects: [],
Expand Down
2 changes: 2 additions & 0 deletions src/config-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createWithEqualityFn } from "zustand/traditional";

export interface ConfigState {
chartCount: number;
playerPicks: number;
upperBound: number;
lowerBound: number;
useWeights: boolean;
Expand All @@ -28,6 +29,7 @@ export interface ConfigState {
export const useConfigState = createWithEqualityFn<ConfigState>(
(set) => ({
chartCount: 5,
playerPicks: 0,
upperBound: 0,
lowerBound: 0,
useWeights: false,
Expand Down
106 changes: 65 additions & 41 deletions src/controls/controls-drawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
People,
CaretDown,
CaretRight,
Plus,
} from "@blueprintjs/icons";
import { useMemo, useState } from "react";
import { shallow } from "zustand/shallow";
Expand Down Expand Up @@ -171,6 +172,7 @@ function GeneralSettings() {
chartCount,
sortByLevel,
useGranularLevels,
playerPicks,
} = configState;
const availableDifficulties = useMemo(() => {
if (!gameData) {
Expand Down Expand Up @@ -241,7 +243,7 @@ function GeneralSettings() {
<Divider />
</>
)}
<div className={isNarrow ? undefined : styles.inlineControls}>
<div className={styles.inlineControls}>
<FormGroup
label={t("controls.chartCount")}
contentClassName={styles.narrowInput}
Expand All @@ -263,46 +265,68 @@ function GeneralSettings() {
}}
/>
</FormGroup>
<div className={styles.inlineControls}>
<FormGroup
label={
usesDrawGroups
? t("controls.lowerBoundTier")
: t("controls.lowerBoundLvl")
}
contentClassName={styles.narrowInput}
>
<NumericInput
large
fill
type="number"
inputMode="numeric"
value={useGranularLevels ? lowerBound.toFixed(2) : lowerBound}
min={availableLevels[0]}
max={Math.max(upperBound, lowerBound, 1)}
onValueChange={handleLowerBoundChange}
/>
</FormGroup>
<FormGroup
label={
usesDrawGroups
? t("controls.upperBoundTier")
: t("controls.upperBoundLvl")
}
contentClassName={styles.narrowInput}
>
<NumericInput
large
fill
type="number"
inputMode="numeric"
value={useGranularLevels ? upperBound.toFixed(2) : upperBound}
min={lowerBound}
max={availableLevels[availableLevels.length - 1]}
onValueChange={handleUpperBoundChange}
/>
</FormGroup>
</div>
<Plus className={styles.plus} size={20} />
<FormGroup
label={t("controls.playerPicks")}
contentClassName={styles.narrowInput}
>
<NumericInput
large
fill
type="number"
inputMode="numeric"
value={playerPicks}
min={0}
clampValueOnBlur
onValueChange={(playerPicks) => {
if (!isNaN(playerPicks)) {
updateState(() => {
return { playerPicks };
});
}
}}
/>
</FormGroup>
</div>
<div className={styles.inlineControls}>
<FormGroup
label={
usesDrawGroups
? t("controls.lowerBoundTier")
: t("controls.lowerBoundLvl")
}
contentClassName={styles.narrowInput}
>
<NumericInput
large
fill
type="number"
inputMode="numeric"
value={useGranularLevels ? lowerBound.toFixed(2) : lowerBound}
min={availableLevels[0]}
max={Math.max(upperBound, lowerBound, 1)}
onValueChange={handleLowerBoundChange}
/>
</FormGroup>
<FormGroup
label={
usesDrawGroups
? t("controls.upperBoundTier")
: t("controls.upperBoundLvl")
}
contentClassName={styles.narrowInput}
>
<NumericInput
large
fill
type="number"
inputMode="numeric"
value={useGranularLevels ? upperBound.toFixed(2) : upperBound}
min={lowerBound}
max={availableLevels[availableLevels.length - 1]}
onValueChange={handleUpperBoundChange}
/>
</FormGroup>
</div>
<Button
alignText="left"
Expand Down
8 changes: 7 additions & 1 deletion src/controls/controls.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,16 @@
margin-bottom: 0;
}

.inlineControls > * {
.inlineControls > div {
margin-right: 25px;
}

.plus {
margin-top: 30px;
margin-left: -23px;
margin-right: 1px;
}

.narrowInput {
width: 110px;
}
Expand Down
5 changes: 3 additions & 2 deletions src/controls/degrs-tester.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ import {
import { SongCard, SongCardProps } from "../song-card/song-card";
import { useState } from "react";
import { Rain, Repeat, WarningSign } from "@blueprintjs/icons";
import { EligibleChart, PlayerPickPlaceholder } from "../models/Drawing";

export function isDegrs(thing: { name: string; artist: string }) {
return thing.name.startsWith('DEAD END("GROOVE');
export function isDegrs(thing: EligibleChart | PlayerPickPlaceholder) {
return "name" in thing && thing.name.startsWith('DEAD END("GROOVE');
}

function* oneMillionDraws() {
Expand Down
40 changes: 31 additions & 9 deletions src/drawing-context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useConfigState } from "./config-state";
import { createContextualStore } from "./zustand/contextual-zustand";
import { useDrawState } from "./draw-state";
import {
CHART_PLACEHOLDER,
Drawing,
EligibleChart,
PlayerActionOnChart,
Expand Down Expand Up @@ -130,19 +131,35 @@ const {
const charts = drawing.charts.slice();
const key = keyFromAction(action);
const arr = drawing[key].slice() as PlayerActionOnChart[] | PocketPick[];
const targetChartIdx = charts.findIndex((chart) => chart.id === chartId);
const targetChart = charts[targetChartIdx];

if (useConfigState.getState().orderByAction) {
const indexToCut = charts.findIndex((chart) => chart.id === chartId);
const [shiftedChart] = charts.splice(indexToCut, 1);
if (
useConfigState.getState().orderByAction &&
targetChart?.type !== CHART_PLACEHOLDER
) {
charts.splice(targetChartIdx, 1);
if (action === "ban") {
// insert at tail of list
const insertPoint = charts.length;
charts.splice(insertPoint, 0, shiftedChart);
charts.splice(insertPoint, 0, targetChart);
} else {
// insert at head of list, behind other picks
const insertPoint =
drawing.protects.length + drawing.pocketPicks.length;
charts.splice(insertPoint, 0, shiftedChart);
const frontLockedCardCount =
// number of placeholder cards total (picked and unpicked)
charts.reduce<number>(
(total, curr) =>
total + (curr.type === CHART_PLACEHOLDER ? 1 : 0),
0,
) +
// number of protects
drawing.protects.length +
// number of picks NOT targeting placeholder cards
drawing.pocketPicks.filter(
(p) => p.targetType !== CHART_PLACEHOLDER,
).length;

// insert at head of list, behind other picks/placeholders
charts.splice(frontLockedCardCount, 0, targetChart);
}
set({
charts,
Expand All @@ -154,7 +171,12 @@ const {
arr.splice(existingIndex, 1);
} else {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
arr.push({ player, pick: newChart!, chartId });
arr.push({
player,
pick: newChart!,
chartId,
targetType: targetChart.type,
});
}
set({
[key]: arr,
Expand Down
12 changes: 11 additions & 1 deletion src/models/Drawing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,17 @@ export interface EligibleChart {
song: Song;
}

export const CHART_PLACEHOLDER = Symbol("Player Pick Placeholder");

export interface PlayerPickPlaceholder {
id: string;
type: typeof CHART_PLACEHOLDER;
}

export const CHART_DRAWN = Symbol("Drawn Chart");
export interface DrawnChart extends EligibleChart {
id: string;
type: typeof CHART_DRAWN;
}

export interface PlayerActionOnChart {
Expand All @@ -28,13 +37,14 @@ export interface PlayerActionOnChart {

export interface PocketPick extends PlayerActionOnChart {
pick: EligibleChart;
targetType: typeof CHART_PLACEHOLDER | typeof CHART_DRAWN;
}

export interface Drawing {
id: string;
title?: string;
players: string[];
charts: DrawnChart[];
charts: Array<DrawnChart | PlayerPickPlaceholder>;
bans: Array<PlayerActionOnChart>;
protects: Array<PlayerActionOnChart>;
winners: Array<PlayerActionOnChart>;
Expand Down
7 changes: 6 additions & 1 deletion src/song-card/card-label.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import classNames from "classnames";
import React from "react";
import { Intent, Tag } from "@blueprintjs/core";
import styles from "./card-label.css";
import { Inheritance, BanCircle, Lock, Crown } from "@blueprintjs/icons";
import { Inheritance, BanCircle, Lock, Crown, Draw } from "@blueprintjs/icons";
import { usePlayerLabel } from "./use-player-label";

export enum LabelType {
Protect = 1,
Ban,
Pocket,
Winner,
FreePick,
}

interface Props {
Expand All @@ -28,6 +29,8 @@ function getIntent(type: LabelType) {
return Intent.SUCCESS;
case LabelType.Winner:
return Intent.WARNING;
case LabelType.FreePick:
return Intent.NONE;
}
}

Expand All @@ -41,6 +44,8 @@ function getIcon(type: LabelType) {
return Lock;
case LabelType.Winner:
return Crown;
case LabelType.FreePick:
return Draw;
}
}

Expand Down
Loading

0 comments on commit 02be671

Please sign in to comment.