Skip to content

Commit

Permalink
Add Rapid React game support
Browse files Browse the repository at this point in the history
  • Loading branch information
NicholasBottone committed Jul 12, 2024
1 parent e71d2c5 commit 7f2941e
Show file tree
Hide file tree
Showing 7 changed files with 403 additions and 2 deletions.
2 changes: 1 addition & 1 deletion src/config/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ 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(["CHARGED UP", "CRESCENDO"]),
GAME_NAME: z.enum(["RAPID REACT", "CHARGED UP", "CRESCENDO"]),
TEAMS_PER_ALLIANCE: z.string().transform(Number),
});

Expand Down
4 changes: 4 additions & 0 deletions src/lib/field/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import fsSync from "fs";

import { getMatchData as chargedUpGetMatchData } from "./chargedUp";
import { getMatchData as crescendoGetMatchData } from "./crescendo";
import { getMatchData as rapidReactGetMatchData } from "./rapidReact";

export const PLAYOFF_MATCHES_BEFORE_FINALS = 13;

Expand All @@ -25,6 +26,9 @@ export async function setMatchNumber(matchType: string, matchNumber: number) {
let gameGetMatchData;

switch (process.env.GAME_NAME) {
case "RAPID REACT":
gameGetMatchData = rapidReactGetMatchData;
break;
case "CHARGED UP":
gameGetMatchData = chargedUpGetMatchData;
break;
Expand Down
146 changes: 146 additions & 0 deletions src/lib/field/rapidReact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import fs from "fs/promises";
import fsSync from "fs";
import type { GoogleSpreadsheetRow } from "google-spreadsheet";
import type { Match } from "../match/crescendo";

const CARGO_BONUS_RP = 60;
const HANGAR_BONUS_RP = 22;

export async function getMatchData(
scheduledMatch: GoogleSpreadsheetRow,
dataDirectory: string,
matchNumber: number
) {
if (!fsSync.existsSync(dataDirectory)) {
throw new Error(`Data directory ${dataDirectory} does not exist`);
}

if (!fsSync.existsSync(`${dataDirectory}/Score_R.txt`)) {
throw new Error(
`Data directory ${dataDirectory} is not populated with data`
);
}

const redAlliance = [
scheduledMatch["Red 1"],
scheduledMatch["Red 2"],
scheduledMatch["Red 3"],
];
const blueAlliance = [
scheduledMatch["Blue 1"],
scheduledMatch["Blue 2"],
scheduledMatch["Blue 3"],
];

// // Sort player contributions (OPR)
// const redAlphabetized = redAlliance.slice().sort();
// const blueAlphabetized = blueAlliance.slice().sort();

// const contribAlphabetized = fs
// .readFileSync(`${dataDirectory}/OPR.txt`, "utf8")
// .split("\n")
// .map((line) => line.split(": ")[1]);
// const unsortedContribRed = contribAlphabetized.slice(0, 3);
// const unsortedContribBlue = contribAlphabetized.slice(3, 6);
// const contribRed = unsortedContribRed.slice();
// const contribBlue = unsortedContribBlue.slice();

// for (let i = 0; i < 3; i++) {
// const redIndex = redAlliance.indexOf(redAlphabetized[i]);
// const blueIndex = blueAlliance.indexOf(blueAlphabetized[i]);
// contribRed[redIndex] = unsortedContribRed[i];
// contribBlue[blueIndex] = unsortedContribBlue[i];
// }

// Count game pieces (cargo)
const piecesRed =
parseInt(await fs.readFile(`${dataDirectory}/C_H_R.txt`, "utf8")) +
parseInt(await fs.readFile(`${dataDirectory}/C_L_R.txt`, "utf8")) +
parseInt(await fs.readFile(`${dataDirectory}/Auto_C_H_R.txt`, "utf8")) +
parseInt(await fs.readFile(`${dataDirectory}/Auto_C_L_R.txt`, "utf8"));
const piecesBlue =
parseInt(await fs.readFile(`${dataDirectory}/C_H_B.txt`, "utf8")) +
parseInt(await fs.readFile(`${dataDirectory}/C_L_B.txt`, "utf8")) +
parseInt(await fs.readFile(`${dataDirectory}/Auto_C_H_B.txt`, "utf8")) +
parseInt(await fs.readFile(`${dataDirectory}/Auto_C_L_B.txt`, "utf8"));

// Calculate endgame points
const endRed = parseInt(
await fs.readFile(`${dataDirectory}/EndR.txt`, "utf8")
);
const endBlue = parseInt(
await fs.readFile(`${dataDirectory}/EndB.txt`, "utf8")
);

// Calculate auto points
const autoRed = parseInt(
await fs.readFile(`${dataDirectory}/AutoR.txt`, "utf8")
);
const autoBlue = parseInt(
await fs.readFile(`${dataDirectory}/AutoB.txt`, "utf8")
);

// Calculate ranking points
const scoreRed = parseInt(
await fs.readFile(`${dataDirectory}/ScoreR.txt`, "utf8")
);
const scoreBlue = parseInt(
await fs.readFile(`${dataDirectory}/ScoreB.txt`, "utf8")
);

const rpRedBonus =
(piecesRed >= CARGO_BONUS_RP ? 1 : 0) + (endRed >= HANGAR_BONUS_RP ? 1 : 0);
const rpRed =
rpRedBonus + (scoreRed > scoreBlue ? 2 : scoreRed === scoreBlue ? 1 : 0);

const rpBlueBonus =
(piecesBlue >= CARGO_BONUS_RP ? 1 : 0) +
(endBlue >= HANGAR_BONUS_RP ? 1 : 0);
const rpBlue =
rpBlueBonus + (scoreBlue > scoreRed ? 2 : scoreBlue === scoreRed ? 1 : 0);

// Calculate tiebreakers
const penaltyRed = parseInt(
await fs.readFile(`${dataDirectory}/PenR.txt`, "utf8")
);
const penaltyBlue = parseInt(
await fs.readFile(`${dataDirectory}/PenB.txt`, "utf8")
);

const tiebreakerRed = scoreRed - penaltyRed;
const tiebreakerBlue = scoreBlue - penaltyBlue;

const match: Match = {
matchNumber,
red1: redAlliance[0],
red2: redAlliance[1],
red3: redAlliance[2],
blue1: blueAlliance[0],
blue2: blueAlliance[1],
blue3: blueAlliance[2],
redScore: scoreRed,
blueScore: scoreBlue,
redPenalty: penaltyRed,
bluePenalty: penaltyBlue,
redAuto: autoRed,
blueAuto: autoBlue,
redTeleop: parseInt(
await fs.readFile(`${dataDirectory}/TeleR.txt`, "utf8")
),
blueTeleop: parseInt(
await fs.readFile(`${dataDirectory}/TeleB.txt`, "utf8")
),
redEnd: endRed,
blueEnd: endBlue,
redGamePieces: piecesRed,
blueGamePieces: piecesBlue,
redRP: rpRed,
blueRP: rpBlue,
redTiebreaker: tiebreakerRed,
blueTiebreaker: tiebreakerBlue,
redBonusRP: rpRedBonus,
blueBonusRP: rpBlueBonus,
};

return match;
}
14 changes: 13 additions & 1 deletion src/lib/match/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,23 @@ import {
saveMatchToRow as crescendoSaveMatchToRow,
} from "./crescendo";

import {
type Match as rapidReactMatch,
headerValues as rapidReactHeaderValues,
matchToArray as rapidReactMatchToArray,
saveMatchToRow as rapidReactSaveMatchToRow,
} from "./rapidReact";

let gameHeaderValues: string[];
let gameMatchToArray: (match: never) => (string | number)[];
let gameSaveMatchToRow: (match: never, row: GoogleSpreadsheetRow) => void;

switch (process.env.GAME_NAME) {
case "RAPID REACT":
gameHeaderValues = rapidReactHeaderValues;
gameMatchToArray = rapidReactMatchToArray;
gameSaveMatchToRow = rapidReactSaveMatchToRow;
break;
case "CHARGED UP":
gameHeaderValues = chargedUpHeaderValues;
gameMatchToArray = chargedUpMatchToArray;
Expand All @@ -31,7 +43,7 @@ switch (process.env.GAME_NAME) {
break;
}

export type Match = chargedUpMatch | crescendoMatch;
export type Match = chargedUpMatch | crescendoMatch | rapidReactMatch;
export const headerValues = gameHeaderValues;
export const matchToArray = (match: Match) => {
return gameMatchToArray(match as never);
Expand Down
120 changes: 120 additions & 0 deletions src/lib/match/rapidReact.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
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;

redPenalty: number;
bluePenalty: number;
redAuto: number;
blueAuto: number;
redTeleop: number;
blueTeleop: number;
redEnd: number;
blueEnd: number;

redGamePieces: number;
blueGamePieces: number;

redRP: number;
blueRP: number;
redTiebreaker: number;
blueTiebreaker: number;
redBonusRP: number;
blueBonusRP: number;
}

export function matchToArray(match: Match) {
return [
match.matchNumber,
match.red1,
match.red2,
match.red3,
match.blue1,
match.blue2,
match.blue3,
match.redScore,
match.blueScore,
match.redPenalty,
match.bluePenalty,
match.redAuto,
match.blueAuto,
match.redTeleop,
match.blueTeleop,
match.redEnd,
match.blueEnd,
match.redGamePieces,
match.blueGamePieces,
match.redRP,
match.blueRP,
match.redTiebreaker,
match.blueTiebreaker,
match.redBonusRP,
match.blueBonusRP,
];
}

export const headerValues = [
"Match Number",
"Red 1",
"Red 2",
"Red 3",
"Blue 1",
"Blue 2",
"Blue 3",
"Red Score",
"Blue Score",
"Red Penalty",
"Blue Penalty",
"Red Auto",
"Blue Auto",
"Red Teleop",
"Blue Teleop",
"Red End",
"Blue End",
"Red Game Pieces",
"Blue Game Pieces",
"Red RP",
"Blue RP",
"Red Tiebreaker",
"Blue Tiebreaker",
"Red Bonus RP",
"Blue Bonus RP",
];

export function saveMatchToRow(match: Match, row: GoogleSpreadsheetRow) {
row["Match Number"] = 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 Penalty"] = match.redPenalty;
row["Blue Penalty"] = match.bluePenalty;
row["Red Auto"] = match.redAuto;
row["Blue Auto"] = match.blueAuto;
row["Red Teleop"] = match.redTeleop;
row["Blue Teleop"] = match.blueTeleop;
row["Red End"] = match.redEnd;
row["Blue End"] = match.blueEnd;
row["Red Game Pieces"] = match.redGamePieces;
row["Blue Game Pieces"] = match.blueGamePieces;
row["Red RP"] = match.redRP;
row["Blue RP"] = match.blueRP;
row["Red Tiebreaker"] = match.redTiebreaker;
row["Blue Tiebreaker"] = match.blueTiebreaker;
row["Red Bonus RP"] = match.redBonusRP;
row["Blue Bonus RP"] = match.blueBonusRP;
}
8 changes: 8 additions & 0 deletions src/lib/resultEmbed/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,21 @@ import {
sendQualMatchEmbed as crescendoSendQualMatchEmbed,
sendPlayoffMatchEmbed as crescendoSendPlayoffMatchEmbed,
} from "./crescendo";
import {
sendQualMatchEmbed as rapidReactSendQualMatchEmbed,
sendPlayoffMatchEmbed as rapidReactSendPlayoffMatchEmbed,
} from "./rapidReact";

import type { Match } from "../match";

let gameSendQualMatchEmbed: (guild: Guild, match: never) => void;
let gameSendPlayoffMatchEmbed: (guild: Guild, match: never) => void;

switch (process.env.GAME_NAME) {
case "RAPID REACT":
gameSendQualMatchEmbed = rapidReactSendQualMatchEmbed;
gameSendPlayoffMatchEmbed = rapidReactSendPlayoffMatchEmbed;
break;
case "CHARGED UP":
gameSendQualMatchEmbed = chargedUpSendQualMatchEmbed;
gameSendPlayoffMatchEmbed = chargedUpSendPlayoffMatchEmbed;
Expand Down
Loading

0 comments on commit 7f2941e

Please sign in to comment.