Skip to content
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
5 changes: 3 additions & 2 deletions biome.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.4/schema.json",
"vcs": {
"enabled": false,
"enabled": true,
"clientKind": "git",
"useIgnoreFile": false
"useIgnoreFile": true,
"defaultBranch": "main"
},
"files": {
"ignoreUnknown": false,
Expand Down
172 changes: 23 additions & 149 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,155 +1,31 @@
import { useState } from "react";
import "./App.css";
import BracketDescription from "./components/BracketDescription";
import Footer from "./components/Footer";
import ScaleSelection from "./components/ScaleSelection";
import ScoreSelection from "./components/ScoreSelection";
import { scales } from "./data/scales";
import type { Bracket, JudgeBracket, ScaleType, SpeakerBracket } from "./types";
import { useBinarySearch } from "./hooks/useBinarySearch";
import { isSpeakerBracket } from "./utils";

function App() {
const [selectedScale, setSelectedScale] = useState<ScaleType>("speaker");
const [low, setLow] = useState(0);
const [high, setHigh] = useState(scales[selectedScale].length - 1);
const [mid, setMid] = useState(
Math.floor((0 + scales[selectedScale].length - 1) / 2),
);
const [phase, setPhase] = useState<"search" | "select" | "done" | "between">(
"search",
);
const [selectedBracketIndex, setSelectedBracketIndex] = useState<
number | null
>(null);
const [exactScore, setExactScore] = useState<number | null>(null);
const [showScale, setShowScale] = useState(false);
const [betweenBracketIndices, setBetweenBracketIndices] = useState<
[number, number] | null
>(null);

const currentScale = scales[selectedScale] as Bracket[];

const handleScaleChange = (newScale: ScaleType) => {
setSelectedScale(newScale);
setLow(0);
setHigh(scales[newScale].length - 1);
setMid(Math.floor((0 + scales[newScale].length - 1) / 2));
setPhase("search");
setSelectedBracketIndex(null);
setExactScore(null);
setBetweenBracketIndices(null);
setShowScale(false);
};

const handleBetter = () => {
const newHigh = mid - 1;
if (newHigh < low) {
// No more higher brackets.
if (mid === 0) {
// At the top bracket.
setSelectedBracketIndex(mid);
const bracket = currentScale[mid];
if (!isSpeakerBracket(bracket)) {
setExactScore((bracket as JudgeBracket).score);
setPhase("done");
} else {
setPhase("select");
}
} else {
// Between mid-1 and mid.
setBetweenBracketIndices([mid - 1, mid]);
setPhase("between");
}
} else {
setHigh(newHigh);
const newMid = Math.floor((low + newHigh) / 2);
setMid(newMid);
}
};

const handleWorse = () => {
const newLow = mid + 1;
if (newLow > high) {
// No more lower brackets.
if (mid === currentScale.length - 1) {
// At the lowest bracket.
setSelectedBracketIndex(mid);
const bracket = currentScale[mid];
if (!isSpeakerBracket(bracket)) {
setExactScore((bracket as JudgeBracket).score);
setPhase("done");
} else {
setPhase("select");
}
} else {
// Between mid and mid+1.
setBetweenBracketIndices([mid, mid + 1]);
setPhase("between");
}
} else {
setLow(newLow);
const newMid = Math.floor((newLow + high) / 2);
setMid(newMid);
}
};

const handleMatched = () => {
setSelectedBracketIndex(mid);
const bracket = currentScale[mid];
if (!isSpeakerBracket(bracket)) {
setExactScore((bracket as JudgeBracket).score);
setPhase("done");
} else {
setPhase("select");
}
};

const handleSelectScore = (position: string) => {
if (selectedBracketIndex !== null) {
const bracket = currentScale[selectedBracketIndex];
if (isSpeakerBracket(bracket)) {
let score: number;

if (position === "lower") {
score = bracket.minScore;
} else if (position === "higher") {
score = bracket.maxScore;
} else {
score = Math.floor((bracket.minScore + bracket.maxScore) / 2);
}

setExactScore(score);
setPhase("done");
}
}
};

const handleSelectExactScore = (score: number) => {
setExactScore(score);
setPhase("done");
};

const selectBracket = (index: number) => {
setSelectedBracketIndex(index);
const bracket = currentScale[index];
if (!isSpeakerBracket(bracket)) {
setExactScore((bracket as JudgeBracket).score);
setPhase("done");
} else {
setPhase("select");
}
};

const reset = () => {
setLow(0);
setHigh(currentScale.length - 1);
setMid(Math.floor((0 + currentScale.length - 1) / 2));
setPhase("search");
setSelectedBracketIndex(null);
setExactScore(null);
setBetweenBracketIndices(null);
setShowScale(false);
};
const {
selectedScale,
handleScaleChange,
mid,
phase,
selectedBracketIndex,
exactScore,
betweenBracketIndices,
showScale,
currentScale,
handleBetter,
handleWorse,
handleMatched,
handleSelectScore,
handleSelectExactScore,
selectBracket,
reset,
toggleShowScale,
} = useBinarySearch();

return (
<div className="App">
Expand Down Expand Up @@ -228,13 +104,11 @@ function App() {
{phase === "select" && selectedBracketIndex !== null && (
<div>
<h2>Selected Bracket:</h2>
<BracketDescription
description={currentScale[selectedBracketIndex].description}
/>
<BracketDescription description={currentScale[mid].description} />
{isSpeakerBracket(currentScale[selectedBracketIndex]) && (
<ScoreSelection
selectedBracketIndex={selectedBracketIndex}
currentScale={currentScale as SpeakerBracket[]}
scaleLength={currentScale.length}
handleSelectScore={handleSelectScore}
handleSelectExactScore={handleSelectExactScore}
/>
Expand Down Expand Up @@ -262,7 +136,7 @@ function App() {

<div>
<div className="button-container">
<button type="button" onClick={() => setShowScale(!showScale)}>
<button type="button" onClick={toggleShowScale}>
{showScale ? "Hide Scale" : "Just show me the scale"}
</button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type React from "react";

const Footer: React.FC = () => {
return (
<footer className="footer" id="binary-search">
<footer className="footer">
<p>
Credit to the Panama WUDC 2025 team (and WUDC and EUDC teams previous)
for the scales. View the{" "}
Expand Down
48 changes: 9 additions & 39 deletions src/components/ScoreSelection.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,25 @@
import type React from "react";
import type { PositionOption, ScoreOption } from "../data/bracketOptions";
import {
bottomBracketOptions,
defaultBracketOptions,
tenthBracketOptions,
topBracketOptions,
} from "../data/bracketOptions";
import type { SpeakerBracket } from "../types";

interface DisplayOption {
label: string;
onClick: () => void;
key: string | number;
}
import { useScoreOptions } from "../hooks/useScoreOptions";

interface ScoreSelectionProps {
selectedBracketIndex: number;
currentScale: SpeakerBracket[];
scaleLength: number;
handleSelectScore: (position: string) => void;
handleSelectExactScore: (score: number) => void;
}

const ScoreSelection: React.FC<ScoreSelectionProps> = ({
selectedBracketIndex,
currentScale,
scaleLength,
handleSelectScore,
handleSelectExactScore,
}) => {
const bracketOptionsMap: { [index: number]: ScoreOption[] } = {
0: topBracketOptions,
5: tenthBracketOptions,
[currentScale.length - 1]: bottomBracketOptions,
};

const getOptions = (): DisplayOption[] => {
const specialOptions = bracketOptionsMap[selectedBracketIndex];
if (specialOptions) {
return specialOptions.map((option: ScoreOption) => ({
label: option.label,
onClick: () => handleSelectExactScore(option.score),
key: option.score,
}));
}
return defaultBracketOptions.map((option: PositionOption) => ({
label: option.label,
onClick: () => handleSelectScore(option.position),
key: option.position,
}));
};

const options = getOptions();
const options = useScoreOptions({
selectedBracketIndex,
scaleLength,
handleSelectScore,
handleSelectExactScore,
});

return (
<div>
Expand Down
Loading