Skip to content

Commit dd19feb

Browse files
Add Reefscape game support
1 parent b6dbc94 commit dd19feb

File tree

7 files changed

+466
-5
lines changed

7 files changed

+466
-5
lines changed

src/config/env.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const envSchema = z.object({
1212
DISCORD_GUILD_ID: z.string().min(1),
1313
DISCORD_CHANNEL_ID: z.string().min(1),
1414
DISCORD_CATEGORY_ID: z.string().min(1),
15-
GAME_NAME: z.enum(["RAPID REACT", "CHARGED UP", "CRESCENDO"]),
15+
GAME_NAME: z.enum(["RAPID REACT", "CHARGED UP", "CRESCENDO", "REEFSCAPE"]),
1616
TEAMS_PER_ALLIANCE: z.string().transform(Number),
1717
});
1818

src/lib/field/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import fsSync from "fs";
44
import { getMatchData as chargedUpGetMatchData } from "./chargedUp";
55
import { getMatchData as crescendoGetMatchData } from "./crescendo";
66
import { getMatchData as rapidReactGetMatchData } from "./rapidReact";
7+
import { getMatchData as reefscapeGetMatchData } from "./reefscape";
78

89
export const PLAYOFF_MATCHES_BEFORE_FINALS = 13;
910

@@ -33,9 +34,12 @@ switch (process.env.GAME_NAME) {
3334
gameGetMatchData = chargedUpGetMatchData;
3435
break;
3536
case "CRESCENDO":
36-
default:
3737
gameGetMatchData = crescendoGetMatchData;
3838
break;
39+
case "REEFSCAPE":
40+
default:
41+
gameGetMatchData = reefscapeGetMatchData;
42+
break;
3943
}
4044

4145
export const getMatchData = gameGetMatchData;

src/lib/field/reefscape.ts

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import fs from "fs/promises";
2+
import fsSync from "fs";
3+
import type { GoogleSpreadsheetRow } from "google-spreadsheet";
4+
import type { Match } from "../match/reefscape";
5+
6+
const AUTO_RP_CORAL_SCORED = 6;
7+
const AUTO_RP_LEAVE_ROBOTS = 3;
8+
const CORAL_RP_CORAL_PER_LEVEL = 9;
9+
const BARGE_RP_BARGE_POINTS = 24;
10+
11+
export async function getMatchData(
12+
scheduledMatch: GoogleSpreadsheetRow,
13+
dataDirectory: string,
14+
matchNumber: number
15+
): Promise<Match> {
16+
if (!fsSync.existsSync(dataDirectory)) {
17+
throw new Error(`Data directory ${dataDirectory} does not exist`);
18+
}
19+
20+
if (!fsSync.existsSync(`${dataDirectory}/Auto_leave_R.txt`)) {
21+
throw new Error(
22+
`Data directory ${dataDirectory} is not populated with data`
23+
);
24+
}
25+
26+
const red1 = scheduledMatch["Red 1"];
27+
const red2 = scheduledMatch["Red 2"];
28+
const red3 = scheduledMatch["Red 3"];
29+
const blue1 = scheduledMatch["Blue 1"];
30+
const blue2 = scheduledMatch["Blue 2"];
31+
const blue3 = scheduledMatch["Blue 3"];
32+
33+
// Read all the necessary files
34+
const [
35+
redAutoLeave,
36+
blueAutoLeave,
37+
redAutoL1,
38+
redAutoL2,
39+
redAutoL3,
40+
redAutoL4,
41+
blueAutoL1,
42+
blueAutoL2,
43+
blueAutoL3,
44+
blueAutoL4,
45+
redNet,
46+
blueNet,
47+
redEndFile,
48+
blueEndFile,
49+
redMajFouls,
50+
blueMajFouls,
51+
redMinFouls,
52+
blueMinFouls,
53+
redResets,
54+
blueResets,
55+
] = await Promise.all([
56+
fs.readFile(`${dataDirectory}/Auto_leave_R.txt`, "utf-8"),
57+
fs.readFile(`${dataDirectory}/Auto_leave_B.txt`, "utf-8"),
58+
fs.readFile(`${dataDirectory}/Auto_l1_R.txt`, "utf-8"),
59+
fs.readFile(`${dataDirectory}/Auto_l2_R.txt`, "utf-8"),
60+
fs.readFile(`${dataDirectory}/Auto_l3_R.txt`, "utf-8"),
61+
fs.readFile(`${dataDirectory}/Auto_l4_R.txt`, "utf-8"),
62+
fs.readFile(`${dataDirectory}/Auto_l1_B.txt`, "utf-8"),
63+
fs.readFile(`${dataDirectory}/Auto_l2_B.txt`, "utf-8"),
64+
fs.readFile(`${dataDirectory}/Auto_l3_B.txt`, "utf-8"),
65+
fs.readFile(`${dataDirectory}/Auto_l4_B.txt`, "utf-8"),
66+
fs.readFile(`${dataDirectory}/Tele_net_R.txt`, "utf-8"),
67+
fs.readFile(`${dataDirectory}/Tele_net_B.txt`, "utf-8"),
68+
fs.readFile(`${dataDirectory}/End_R.txt`, "utf-8"),
69+
fs.readFile(`${dataDirectory}/End_B.txt`, "utf-8"),
70+
fs.readFile(`${dataDirectory}/MajFouls_R.txt`, "utf-8"),
71+
fs.readFile(`${dataDirectory}/MajFouls_B.txt`, "utf-8"),
72+
fs.readFile(`${dataDirectory}/MinFouls_R.txt`, "utf-8"),
73+
fs.readFile(`${dataDirectory}/MinFouls_B.txt`, "utf-8"),
74+
fs.readFile(`${dataDirectory}/Resets_R.txt`, "utf-8"),
75+
fs.readFile(`${dataDirectory}/Resets_B.txt`, "utf-8"),
76+
]);
77+
78+
// Parse the values
79+
const redLeave = parseInt(redAutoLeave.trim());
80+
const blueLeave = parseInt(blueAutoLeave.trim());
81+
const redAutoCoral = [
82+
parseInt(redAutoL1.trim()),
83+
parseInt(redAutoL2.trim()),
84+
parseInt(redAutoL3.trim()),
85+
parseInt(redAutoL4.trim()),
86+
].reduce((a, b) => a + b, 0);
87+
const blueAutoCoral = [
88+
parseInt(blueAutoL1.trim()),
89+
parseInt(blueAutoL2.trim()),
90+
parseInt(blueAutoL3.trim()),
91+
parseInt(blueAutoL4.trim()),
92+
].reduce((a, b) => a + b, 0);
93+
const redAlgae = parseInt(redNet.trim());
94+
const blueAlgae = parseInt(blueNet.trim());
95+
const redBarge = parseInt(redEndFile.trim());
96+
const blueBarge = parseInt(blueEndFile.trim());
97+
const redMajFoulsCount = parseInt(redMajFouls.trim());
98+
const blueMajFoulsCount = parseInt(blueMajFouls.trim());
99+
const redMinFoulsCount = parseInt(redMinFouls.trim());
100+
const blueMinFoulsCount = parseInt(blueMinFouls.trim());
101+
const redResetsCount = parseInt(redResets.trim());
102+
const blueResetsCount = parseInt(blueResets.trim());
103+
104+
// Calculate penalties
105+
const redPenalty =
106+
redMajFoulsCount * 6 + redMinFoulsCount * 2 + redResetsCount * 6;
107+
const bluePenalty =
108+
blueMajFoulsCount * 6 + blueMinFoulsCount * 2 + blueResetsCount * 6;
109+
110+
// Calculate scores
111+
const redAuto = redAutoCoral;
112+
const blueAuto = blueAutoCoral;
113+
const redScore = redAuto + redBarge - redPenalty;
114+
const blueScore = blueAuto + blueBarge - bluePenalty;
115+
116+
// Calculate ranking points
117+
let redRP = 0;
118+
let blueRP = 0;
119+
let redBonusRP = 0;
120+
let blueBonusRP = 0;
121+
122+
// Win RP
123+
if (redScore > blueScore) redRP += 3;
124+
else if (blueScore > redScore) blueRP += 3;
125+
else {
126+
redRP += 1;
127+
blueRP += 1;
128+
}
129+
130+
// Auto RP - All robots leave and 6+ coral in auto
131+
if (
132+
redLeave === AUTO_RP_LEAVE_ROBOTS &&
133+
redAutoCoral >= AUTO_RP_CORAL_SCORED
134+
) {
135+
redRP += 1;
136+
redBonusRP += 1;
137+
}
138+
if (
139+
blueLeave === AUTO_RP_LEAVE_ROBOTS &&
140+
blueAutoCoral >= AUTO_RP_CORAL_SCORED
141+
) {
142+
blueRP += 1;
143+
blueBonusRP += 1;
144+
}
145+
146+
// Coral RP - 9+ coral on each level
147+
const redCoralRP = [redAutoL1, redAutoL2, redAutoL3, redAutoL4].every(
148+
(level) => parseInt(level.trim()) >= CORAL_RP_CORAL_PER_LEVEL
149+
);
150+
const blueCoralRP = [blueAutoL1, blueAutoL2, blueAutoL3, blueAutoL4].every(
151+
(level) => parseInt(level.trim()) >= CORAL_RP_CORAL_PER_LEVEL
152+
);
153+
if (redCoralRP) {
154+
redRP += 1;
155+
redBonusRP += 1;
156+
}
157+
if (blueCoralRP) {
158+
blueRP += 1;
159+
blueBonusRP += 1;
160+
}
161+
162+
// Barge RP - 24+ barge points
163+
if (redBarge >= BARGE_RP_BARGE_POINTS) {
164+
redRP += 1;
165+
redBonusRP += 1;
166+
}
167+
if (blueBarge >= BARGE_RP_BARGE_POINTS) {
168+
blueRP += 1;
169+
blueBonusRP += 1;
170+
}
171+
172+
return {
173+
matchNumber,
174+
red1,
175+
red2,
176+
red3,
177+
blue1,
178+
blue2,
179+
blue3,
180+
redScore,
181+
blueScore,
182+
redPenalty,
183+
bluePenalty,
184+
redAuto,
185+
blueAuto,
186+
redLeave,
187+
blueLeave,
188+
redCoral: redAutoCoral,
189+
blueCoral: blueAutoCoral,
190+
redAlgae,
191+
blueAlgae,
192+
redBarge,
193+
blueBarge,
194+
redRP,
195+
blueRP,
196+
redTiebreaker: redScore - redPenalty,
197+
blueTiebreaker: blueScore - bluePenalty,
198+
redBonusRP,
199+
blueBonusRP,
200+
};
201+
}

src/lib/match/index.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ import {
2020
saveMatchToRow as rapidReactSaveMatchToRow,
2121
} from "./rapidReact";
2222

23+
import {
24+
type Match as reefscapeMatch,
25+
headerValues as reefscapeHeaderValues,
26+
matchToArray as reefscapeMatchToArray,
27+
saveMatchToRow as reefscapeSaveMatchToRow,
28+
} from "./reefscape";
29+
2330
let gameHeaderValues: string[];
2431
let gameMatchToArray: (match: never) => (string | number)[];
2532
let gameSaveMatchToRow: (match: never, row: GoogleSpreadsheetRow) => void;
@@ -36,14 +43,23 @@ switch (process.env.GAME_NAME) {
3643
gameSaveMatchToRow = chargedUpSaveMatchToRow;
3744
break;
3845
case "CRESCENDO":
39-
default:
4046
gameHeaderValues = crescendoHeaderValues;
4147
gameMatchToArray = crescendoMatchToArray;
4248
gameSaveMatchToRow = crescendoSaveMatchToRow;
4349
break;
50+
case "REEFSCAPE":
51+
default:
52+
gameHeaderValues = reefscapeHeaderValues;
53+
gameMatchToArray = reefscapeMatchToArray;
54+
gameSaveMatchToRow = reefscapeSaveMatchToRow;
55+
break;
4456
}
4557

46-
export type Match = chargedUpMatch | crescendoMatch | rapidReactMatch;
58+
export type Match =
59+
| chargedUpMatch
60+
| crescendoMatch
61+
| rapidReactMatch
62+
| reefscapeMatch;
4763
export const headerValues = gameHeaderValues;
4864
export const matchToArray = (match: Match) => {
4965
return gameMatchToArray(match as never);

0 commit comments

Comments
 (0)