Skip to content

Commit 39b2aa2

Browse files
committed
Code for matchups.
1 parent 3b8fdf6 commit 39b2aa2

28 files changed

+994
-63
lines changed

bun.lockb

1.05 KB
Binary file not shown.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
},
3636
"devDependencies": {
3737
"@biomejs/biome": "^1.9.4",
38-
"@types/node": "^20.8.10",
38+
"@types/bun": "^1.2.2",
3939
"barely-a-dev-server": "^0.6.0",
4040
"typescript": "^5.2.2"
4141
}

script/dev.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import { barelyServe } from "barely-a-dev-server";
2+
import { serve } from "../src/server/main";
23
import { esbuildOptions } from "./esbuildOptions.js";
34

4-
await barelyServe({
5-
entryRoot: "src/dev",
6-
port: 3338,
7-
esbuildOptions,
8-
});
5+
serve();
6+
7+
await Promise.all([
8+
barelyServe({
9+
entryRoot: "src/dev",
10+
port: 3338,
11+
esbuildOptions,
12+
}),
13+
]);

src/dev/head-to-head/index.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
:root {
2+
color-scheme: light dark;
3+
}
4+
5+
html,
6+
body,
7+
scramble-table {
8+
height: 100%;
9+
width: 100%;
10+
margin: 0;
11+
}

src/dev/head-to-head/index.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!DOCTYPE html>
2+
<html>
3+
4+
<head>
5+
<meta charset="utf8">
6+
<title>scramble-table | Dev App</title>
7+
<link rel="stylesheet" href="./index.css">
8+
<!-- Note: the source file is `main.ts`, but here we use the transpiled file name it will have on the web server. -->
9+
<script src="./main.js" type="module"></script>
10+
</head>
11+
12+
<body>
13+
</body>
14+
15+
</html>

src/dev/head-to-head/main.ts

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import type { ExperimentalMillisecondTimestamp } from "cubing/twisty";
2+
import { ScrambleTable } from "../../lib";
3+
import type { AttemptScrambleInfo } from "../../lib/AttemptScrambleInfo";
4+
import type {
5+
MatchupCallbackIdentifyingInfo,
6+
MatchupID,
7+
ResultForTimedAttempt,
8+
} from "../../lib/elements/SharedState";
9+
import {
10+
type ResultForTimedAttemptWithPenalty,
11+
formatResultForTimedAttempt,
12+
} from "../../lib/vendor/timer.cubing.net/stats";
13+
14+
declare global {
15+
interface globalThis {
16+
app: ScrambleTable;
17+
}
18+
}
19+
20+
async function simulateLatency() {
21+
await new Promise((resolve) => setTimeout(resolve, Math.random() * 250));
22+
}
23+
24+
async function refreshCurrentMatchupsCallback() {
25+
await simulateLatency();
26+
return {
27+
"matchup-23": "Minh Thai vs. Abraham Lincoln",
28+
"matchup-24": "Gilgamesh vs. Enkidu",
29+
};
30+
}
31+
32+
function matchupSelectedCallback(matchupID: MatchupID) {
33+
switch (matchupID) {
34+
case "matchup-23": {
35+
app.displays[0].setScramble({
36+
competitorName: "Minh Thai",
37+
eventID: "333",
38+
matchupID: "matchup-23",
39+
competitorMatchupID: "minh-thai",
40+
attemptID: "1",
41+
scrambleString:
42+
"B2 D F2 D2 B2 L2 B2 U B2 U L2 F2 L' F D' B2 D' L D R' U",
43+
});
44+
app.displays[0].setScramble({
45+
competitorName: "Abraham Lincoln",
46+
eventID: "333",
47+
matchupID: "matchup-23",
48+
competitorMatchupID: "abe",
49+
attemptID: "1",
50+
scrambleString:
51+
"B2 D F2 D2 B2 L2 B2 U B2 U L2 F2 L' F D' B2 D' L D R' U",
52+
});
53+
return;
54+
}
55+
case "matchup-24": {
56+
app.displays[0].setScramble({
57+
competitorName: "Gilgamesh",
58+
eventID: "333",
59+
matchupID: "matchup-24",
60+
competitorMatchupID: "gilgamesh",
61+
attemptID: "1",
62+
scrambleString:
63+
"D2 F U R' U' D F L U' R' L2 B D2 R2 L2 F D2 F' L2 F R2",
64+
});
65+
app.displays[0].setScramble({
66+
competitorName: "Enkidu",
67+
eventID: "333",
68+
matchupID: "matchup-24",
69+
competitorMatchupID: "enkidu",
70+
attemptID: "1",
71+
scrambleString:
72+
"D2 F U R' U' D F L U' R' L2 B D2 R2 L2 F D2 F' L2 F R2",
73+
});
74+
return;
75+
}
76+
}
77+
}
78+
79+
const localResultDB: Record<
80+
number /* display number */,
81+
ResultForTimedAttemptWithPenalty
82+
> = {};
83+
84+
function randomNewResult(): ResultForTimedAttemptWithPenalty {
85+
if (Math.random() < 0.05) {
86+
return {
87+
resultForTimedAttempt: "DNF",
88+
penaltySeconds: 0,
89+
};
90+
}
91+
const resultForTimedAttempt =
92+
Math.floor(Math.random() * 5000) + Math.floor(Math.random() * 5000) + 5000;
93+
return {
94+
resultForTimedAttempt,
95+
penaltySeconds: 0,
96+
};
97+
}
98+
99+
function accessResult(
100+
identifyingInfo: MatchupCallbackIdentifyingInfo,
101+
): ResultForTimedAttemptWithPenalty {
102+
const uniqueAttemptIdentifier = `${identifyingInfo.matchupID}###${identifyingInfo.competitorMatchupID}###${identifyingInfo.attemptID}`;
103+
// biome-ignore lint/suspicious/noAssignInExpressions: DRY pattern
104+
return (localResultDB[uniqueAttemptIdentifier] ??= randomNewResult());
105+
}
106+
107+
async function matchupGetResultCallback(
108+
identifyingInfo: MatchupCallbackIdentifyingInfo,
109+
): Promise<ResultForTimedAttemptWithPenalty> {
110+
await simulateLatency();
111+
return structuredClone(accessResult(identifyingInfo));
112+
}
113+
114+
async function matchupAdjustPenaltyCallback(
115+
identifyingInfo: MatchupCallbackIdentifyingInfo,
116+
deltaSeconds: number,
117+
): Promise<ResultForTimedAttemptWithPenalty> {
118+
await simulateLatency();
119+
const result = accessResult(identifyingInfo);
120+
121+
result.penaltySeconds += deltaSeconds;
122+
if (result.penaltySeconds < 0) {
123+
result.penaltySeconds = 0;
124+
}
125+
126+
return structuredClone(result);
127+
}
128+
129+
async function matchupFinishAttemptCallback(
130+
identifyingInfo: MatchupCallbackIdentifyingInfo,
131+
): Promise<void> {
132+
const scrambleInfo = await (async (): Promise<AttemptScrambleInfo> => {
133+
throw new Error("TODO: implement using a proper backend");
134+
})();
135+
app.displays[identifyingInfo.displayNumber].setScramble(scrambleInfo);
136+
}
137+
138+
const app = document.body.appendChild(
139+
new ScrambleTable({
140+
competitorScrambleDisplayOptions: {
141+
resultInput: "show",
142+
},
143+
callbacks: {
144+
matchupGetResultCallback,
145+
matchupAdjustPenaltyCallback,
146+
matchupFinishAttemptCallback,
147+
refreshCurrentMatchupsCallback,
148+
resetMatchupCallback: async (matchupID) =>
149+
console.log("Reset matchup:", { matchupID }),
150+
matchupSelectedCallback,
151+
},
152+
showMatchupsSelection: "show",
153+
}),
154+
);
155+
globalThis.app = app;
156+
157+
app.addEventListener(
158+
"scramble-cleared",
159+
(e: CustomEvent<{ displayIndex: number }>) => {
160+
console.log(`Scramble cleared for display index: ${e.detail.displayIndex}`);
161+
},
162+
);
163+
164+
matchupSelectedCallback("matchup-23");

src/dev/index.css

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
:root {
2+
color-scheme: light dark;
3+
}
4+
15
html,
26
body,
37
scramble-table {

src/dev/main.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,22 @@ app.addEventListener(
1717
);
1818

1919
app.displays[0].setScramble({
20-
competitorName: "Amelia Multicube",
20+
competitorName: "Minh Thai",
2121
competitorCompetitionID: 11,
22-
eventID: "333mbf",
23-
roundNumber: 3,
22+
eventID: "333",
23+
roundNumber: 1,
2424
scrambleSetNumber: 1,
2525
attemptID: "2",
26-
passcode: "uce4zdvm",
27-
numSubScrambles: 23,
26+
passcode: "tszgw3r9",
27+
// numSubScrambles: 23,
2828
});
2929

3030
app.displays[1].setScramble({
31-
competitorName: "Ben Streeter",
31+
competitorName: "Abraham Lincoln",
3232
competitorCompetitionID: 34,
33-
eventID: "fto",
33+
eventID: "333",
3434
roundNumber: 1,
3535
scrambleSetNumber: 1,
36-
attemptID: "3",
37-
passcode: "7dig4j6y",
36+
attemptID: "2",
37+
passcode: "tszgw3r9",
3838
});

src/lib/AttemptScrambleInfo.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,23 @@
1-
export interface AttemptScrambleInfo {
1+
interface CommonAttemptScrambleInfo {
22
competitorName: string;
33
competitorCompetitionID?: number;
44
eventID: string;
5+
}
6+
export interface WCAAttemptScrambleInfo extends CommonAttemptScrambleInfo {
57
roundNumber: number; // 1-indexed
68
scrambleSetNumber: number; // 1-indexed for the round (convert to letters as 1=A, 2=B, etc). NOT the global scramble set ID for the competition.
79
attemptID: string; // "1" through "5", "E1", etc.
810
numSubScrambles?: number; // for multi-puzzle events (e.g. Multi-Blind)
911
passcode: string; // Passcode for the scramble set (or attempt, in the case of Multi-Blind)
1012
}
13+
14+
export interface MatchupAttemptScrambleInfo extends CommonAttemptScrambleInfo {
15+
matchupID: string;
16+
competitorMatchupID: string;
17+
attemptID: string;
18+
scrambleString: string;
19+
}
20+
21+
export type AttemptScrambleInfo =
22+
| WCAAttemptScrambleInfo
23+
| MatchupAttemptScrambleInfo;

src/lib/elements/CompetitorScrambleDisplay.css

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
competitor-scramble-display {
22
display: grid;
3-
grid-template-rows: auto auto 1fr;
3+
grid-template-rows: auto auto 1fr auto auto;
44
border: 1px solid;
55
}
66

@@ -131,3 +131,10 @@ scramble-table dialog::backdrop {
131131
opacity: 0.75;
132132
z-index: 1;
133133
}
134+
135+
competitor-scramble-display.matchup .scramble-set {
136+
display: none;
137+
}
138+
competitor-scramble-display:not(.matchup) .matchup {
139+
display: none;
140+
}

0 commit comments

Comments
 (0)