diff --git a/package-lock.json b/package-lock.json index 21bd681..8d31201 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2676,4 +2676,4 @@ } } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 656dc90..856e760 100644 --- a/package.json +++ b/package.json @@ -60,4 +60,4 @@ "@typescript-eslint/consistent-type-imports": "warn" } } -} +} \ No newline at end of file diff --git a/src/config/env.ts b/src/config/env.ts index 78129ba..313028e 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -12,7 +12,13 @@ const envSchema = z.object({ DISCORD_GUILD_ID: z.string().min(1), DISCORD_CHANNEL_ID: z.string().min(1), DISCORD_CATEGORY_ID: z.string().min(1), - GAME_NAME: z.enum(["RAPID REACT", "CHARGED UP", "CRESCENDO", "REEFSCAPE"]), + GAME_NAME: z.enum([ + "RAPID REACT", + "CHARGED UP", + "CRESCENDO", + "REEFSCAPE", + "REBUILT", + ]), TEAMS_PER_ALLIANCE: z.string().transform(Number), }); diff --git a/src/lib/field/index.ts b/src/lib/field/index.ts index 9808af1..4b1679f 100644 --- a/src/lib/field/index.ts +++ b/src/lib/field/index.ts @@ -7,6 +7,7 @@ import { getMatchData as chargedUpGetMatchData } from "./chargedUp"; import { getMatchData as crescendoGetMatchData } from "./crescendo"; import { getMatchData as rapidReactGetMatchData } from "./rapidReact"; import { getMatchData as reefscapeGetMatchData } from "./reefscape"; +import { getMatchData as rebuiltGetMatchData } from "./rebuilt"; export const PLAYOFF_MATCHES_BEFORE_FINALS = 13; @@ -56,9 +57,12 @@ switch (process.env.GAME_NAME) { gameGetMatchData = crescendoGetMatchData; break; case "REEFSCAPE": - default: gameGetMatchData = reefscapeGetMatchData; break; + case "REBUILT": + default: + gameGetMatchData = rebuiltGetMatchData; + break; } export const getMatchData = gameGetMatchData; diff --git a/src/lib/field/rebuilt.ts b/src/lib/field/rebuilt.ts new file mode 100644 index 0000000..600b4c0 --- /dev/null +++ b/src/lib/field/rebuilt.ts @@ -0,0 +1,134 @@ +import fs from "fs/promises"; +import fsSync from "fs"; +import type { GoogleSpreadsheetRow } from "google-spreadsheet"; +import type { Match } from "../match/rebuilt"; + +const ENERGIZED_RP_THRESHOLD = 360; +const SUPERCHARGED_RP_THRESHOLD = 500; +const TRAVERSAL_RP_THRESHOLD = 50; +const AUTO_CLIMB_POINTS = 15; + +export async function getMatchData( + scheduledMatch: GoogleSpreadsheetRow, + dataDirectory: string, + matchNumber: number +): Promise { + if (!fsSync.existsSync(dataDirectory)) { + throw new Error(`Data directory ${dataDirectory} does not exist`); + } + + if (!fsSync.existsSync(`${dataDirectory}/Auto_R.txt`)) { + throw new Error( + `Data directory ${dataDirectory} is not populated with data` + ); + } + + const red1 = scheduledMatch["Red 1"]; + const red2 = scheduledMatch["Red 2"]; + const red3 = scheduledMatch["Red 3"]; + const blue1 = scheduledMatch["Blue 1"]; + const blue2 = scheduledMatch["Blue 2"]; + const blue3 = scheduledMatch["Blue 3"]; + + const [ + redAutoString, + blueAutoString, + redAutoFuelString, + blueAutoFuelString, + redAutoLvl1String, + blueAutoLvl1String, + redTeleString, + blueTeleString, + redTeleFuelString, + blueTeleFuelString, + redEndString, + blueEndString, + redScoreString, + blueScoreString, + ] = await Promise.all([ + fs.readFile(`${dataDirectory}/Auto_R.txt`, "utf-8"), + fs.readFile(`${dataDirectory}/Auto_B.txt`, "utf-8"), + fs.readFile(`${dataDirectory}/Auto_Fuel_R.txt`, "utf-8"), + fs.readFile(`${dataDirectory}/Auto_Fuel_B.txt`, "utf-8"), + fs.readFile(`${dataDirectory}/Auto_lvl_1_R.txt`, "utf-8"), + fs.readFile(`${dataDirectory}/Auto_lvl_1_B.txt`, "utf-8"), + fs.readFile(`${dataDirectory}/Tele_R.txt`, "utf-8"), + fs.readFile(`${dataDirectory}/Tele_B.txt`, "utf-8"), + fs.readFile(`${dataDirectory}/Tele_Fuel_R.txt`, "utf-8"), + fs.readFile(`${dataDirectory}/Tele_Fuel_B.txt`, "utf-8"), + fs.readFile(`${dataDirectory}/End_R.txt`, "utf-8"), + fs.readFile(`${dataDirectory}/End_B.txt`, "utf-8"), + fs.readFile(`${dataDirectory}/Score_R.txt`, "utf-8"), + fs.readFile(`${dataDirectory}/Score_B.txt`, "utf-8"), + ]); + + // Parse values + const redAuto = parseInt(redAutoString.trim()); + const blueAuto = parseInt(blueAutoString.trim()); + const redAutoFuel = parseInt(redAutoFuelString.trim()); + const blueAutoFuel = parseInt(blueAutoFuelString.trim()); + const redAutoLvl1 = parseInt(redAutoLvl1String.trim()); + const blueAutoLvl1 = parseInt(blueAutoLvl1String.trim()); + const redTele = parseInt(redTeleString.trim()); + const blueTele = parseInt(blueTeleString.trim()); + const redTeleFuel = parseInt(redTeleFuelString.trim()); + const blueTeleFuel = parseInt(blueTeleFuelString.trim()); + const redEnd = parseInt(redEndString.trim()); + const blueEnd = parseInt(blueEndString.trim()); + const redScore = parseInt(redScoreString.trim()); + const blueScore = parseInt(blueScoreString.trim()); + + // Calculate total fuel (auto + tele) + const redTotalFuel = redAutoFuel + redTeleFuel; + const blueTotalFuel = blueAutoFuel + blueTeleFuel; + + // Calculate ranking points + let redBonusRP = 0; + let blueBonusRP = 0; + + // Energized RP - 360+ total fuel + if (redTotalFuel >= ENERGIZED_RP_THRESHOLD) { + redBonusRP += 1; + } + if (blueTotalFuel >= ENERGIZED_RP_THRESHOLD) { + blueBonusRP += 1; + } + + // Supercharged RP - 500+ total fuel + if (redTotalFuel >= SUPERCHARGED_RP_THRESHOLD) { + redBonusRP += 1; + } + if (blueTotalFuel >= SUPERCHARGED_RP_THRESHOLD) { + blueBonusRP += 1; + } + + // Traversal RP - 50+ climb points (end climb points + (auto climbs * 15)) + const redClimbPoints = redEnd + redAutoLvl1 * AUTO_CLIMB_POINTS; + const blueClimbPoints = blueEnd + blueAutoLvl1 * AUTO_CLIMB_POINTS; + if (redClimbPoints >= TRAVERSAL_RP_THRESHOLD) { + redBonusRP += 1; + } + if (blueClimbPoints >= TRAVERSAL_RP_THRESHOLD) { + blueBonusRP += 1; + } + + return { + matchNumber, + red1, + red2, + red3, + blue1, + blue2, + blue3, + redScore, + blueScore, + redAuto, + blueAuto, + redTele, + blueTele, + redEnd, + blueEnd, + redBonusRP, + blueBonusRP, + }; +} diff --git a/src/lib/match/index.ts b/src/lib/match/index.ts index 10e6b9c..5d15d55 100644 --- a/src/lib/match/index.ts +++ b/src/lib/match/index.ts @@ -27,6 +27,13 @@ import { saveMatchToRow as reefscapeSaveMatchToRow, } from "./reefscape"; +import { + type Match as rebuiltMatch, + headerValues as rebuiltHeaderValues, + matchToArray as rebuiltMatchToArray, + saveMatchToRow as rebuiltSaveMatchToRow, +} from "./rebuilt"; + let gameHeaderValues: string[]; let gameMatchToArray: (match: never) => (string | number)[]; let gameSaveMatchToRow: (match: never, row: GoogleSpreadsheetRow) => void; @@ -48,18 +55,24 @@ switch (process.env.GAME_NAME) { gameSaveMatchToRow = crescendoSaveMatchToRow; break; case "REEFSCAPE": - default: gameHeaderValues = reefscapeHeaderValues; gameMatchToArray = reefscapeMatchToArray; gameSaveMatchToRow = reefscapeSaveMatchToRow; break; + case "REBUILT": + default: + gameHeaderValues = rebuiltHeaderValues; + gameMatchToArray = rebuiltMatchToArray; + gameSaveMatchToRow = rebuiltSaveMatchToRow; + break; } export type Match = | chargedUpMatch | crescendoMatch | rapidReactMatch - | reefscapeMatch; + | reefscapeMatch + | rebuiltMatch; export const headerValues = gameHeaderValues; export const matchToArray = (match: Match) => { return gameMatchToArray(match as never); diff --git a/src/lib/match/rebuilt.ts b/src/lib/match/rebuilt.ts new file mode 100644 index 0000000..3f63967 --- /dev/null +++ b/src/lib/match/rebuilt.ts @@ -0,0 +1,85 @@ +import type { GoogleSpreadsheetRow } from "google-spreadsheet"; + +export interface Match { + matchNumber: number; + + red1: string; + red2: string; + red3: string; + blue1: string; + blue2: string; + blue3: string; + + redScore: number; + blueScore: number; + redAuto: number; + blueAuto: number; + redTele: number; + blueTele: number; + redEnd: number; + blueEnd: number; + redBonusRP: number; + blueBonusRP: number; +} + +export const headerValues = [ + "Match", + "Red 1", + "Red 2", + "Red 3", + "Blue 1", + "Blue 2", + "Blue 3", + "Red Score", + "Blue Score", + "Red Auto", + "Blue Auto", + "Red Tele", + "Blue Tele", + "Red End", + "Blue End", + "Red Bonus RP", + "Blue Bonus RP", +]; + +export function matchToArray(match: Match): (string | number)[] { + return [ + match.matchNumber, + match.red1, + match.red2, + match.red3, + match.blue1, + match.blue2, + match.blue3, + match.redScore, + match.blueScore, + match.redAuto, + match.blueAuto, + match.redTele, + match.blueTele, + match.redEnd, + match.blueEnd, + match.redBonusRP, + match.blueBonusRP, + ]; +} + +export function saveMatchToRow(match: Match, row: GoogleSpreadsheetRow) { + row["Match"] = match.matchNumber; + row["Red 1"] = match.red1; + row["Red 2"] = match.red2; + row["Red 3"] = match.red3; + row["Blue 1"] = match.blue1; + row["Blue 2"] = match.blue2; + row["Blue 3"] = match.blue3; + row["Red Score"] = match.redScore; + row["Blue Score"] = match.blueScore; + row["Red Auto"] = match.redAuto; + row["Blue Auto"] = match.blueAuto; + row["Red Tele"] = match.redTele; + row["Blue Tele"] = match.blueTele; + row["Red End"] = match.redEnd; + row["Blue End"] = match.blueEnd; + row["Red Bonus RP"] = match.redBonusRP; + row["Blue Bonus RP"] = match.blueBonusRP; +} diff --git a/tsconfig.json b/tsconfig.json index 282fa53..b1acab0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,4 +28,4 @@ }, "exclude": ["node_modules"], "include": ["src"] -} +} \ No newline at end of file