diff --git a/components/stats/PredictionScreen.tsx b/components/stats/PredictionScreen.tsx new file mode 100644 index 00000000..42309467 --- /dev/null +++ b/components/stats/PredictionScreen.tsx @@ -0,0 +1,120 @@ +import { Game, Report } from "@/lib/Types"; +import { useEffect, useState } from "react"; +import { Bar } from "react-chartjs-2"; + +function AllianceBuilder(props: { + teams: number[], + alliance: (number | undefined)[], + update: (index: number, team: number) => void, + name: string, + color: string + }) { + return ( +
+

{props.name} Alliance

+
+ {props.alliance.map((team, index) => + + )} +
+
+ ); +} + +export default function PredictionScreen(props: { reports: Report[], teams: number[], game: Game }) { + const [reportsByTeam, setReportsByTeam] = useState>({}); + + // Array(length) constructor doesn't actually fill the array with undefined, so we have to do it manually + const [blueAlliance, setBlueAlliance] = useState<(number | undefined)[]>(Array(props.game.allianceSize).fill(undefined)); + const [redAlliance, setRedAlliance] = useState<(number | undefined)[]>(Array(props.game.allianceSize).fill(undefined)); + + useEffect(() => { + const reportsByTeam = props.reports.reduce((acc, report) => { + if (!acc[report.robotNumber]) { + acc[report.robotNumber] = []; + } + + acc[report.robotNumber].push(report); + return acc; + }, {} as Record); + + setReportsByTeam(reportsByTeam); + }, [props.reports]); + + function updateAlliance(setAlliance: (alliance: (number | undefined)[]) => void, alliance: (number | undefined)[], + index: number, team: number) { + alliance[index] = team; + setAlliance([...alliance]); // We have to create a new array for the update to work + } + + const blueAllianceFilled = blueAlliance.filter((team) => team !== undefined); + const redAllianceFilled = redAlliance.filter((team) => team !== undefined); + + const avgPointsBlueAllianceIndividual = blueAllianceFilled.map((team) => props.game.getAvgPoints(reportsByTeam[team!])); + const avgPointsRedAllianceIndividual = redAllianceFilled.map((team) => props.game.getAvgPoints(reportsByTeam[team!])); + + const totalPointsBlueAlliance = avgPointsBlueAllianceIndividual.reduce((acc, points) => acc + points, 0); + const totalPointsRedAlliance = avgPointsRedAllianceIndividual.reduce((acc, points) => acc + points, 0); + + const datasets = []; + for (let i = 0; i < Math.max(avgPointsBlueAllianceIndividual.length, avgPointsRedAllianceIndividual.length); i++) { + const blue = avgPointsBlueAllianceIndividual[i] || undefined; + const red = avgPointsRedAllianceIndividual[i] || undefined; + + datasets.push({ + data: [blue, red], + label: `Team ${blueAllianceFilled[i] || "Empty"} vs Team ${redAllianceFilled[i] || "Empty"}`, + backgroundColor: [`rgba(0, ${i * 255 / 2}, 235, 1)`, `rgba(235, ${i * 255 / 2}, 0, 1)`], + }); + } + + const pointDiff = totalPointsBlueAlliance - totalPointsRedAlliance; + let winner = "Tie"; + let color = ""; + if (pointDiff > 0) { + winner = "Blue Alliance"; + color = "blue-500"; + } else if (pointDiff < 0) { + winner = "Red Alliance"; + color = "red-500"; + } + + return ( +
+
+ updateAlliance(setBlueAlliance, blueAlliance, index, team)} name="Blue" color="blue-500" /> + updateAlliance(setRedAlliance, redAlliance, index, team)} name="Red" color="red-500" /> +
+

+ {pointDiff != 0 ? `${winner} wins by ${Math.abs(pointDiff)} points` : winner} ({totalPointsBlueAlliance} - {totalPointsRedAlliance}) +

+ {/* If we don't have bar graph in a div, it will vertically expand into infinity. Don't question it. - Renato */ } +
+ +
+
+ ); +} \ No newline at end of file diff --git a/lib/Types.ts b/lib/Types.ts index bb91852c..2867e2d4 100644 --- a/lib/Types.ts +++ b/lib/Types.ts @@ -131,7 +131,7 @@ export enum League { FTC = "FTC", FRC = "FRC" } -export class Game { +export class Game { name: string; year: number; diff --git a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx index c6f38f47..10cec442 100644 --- a/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx +++ b/pages/[teamSlug]/[seasonSlug]/[competitonSlug]/stats.tsx @@ -14,6 +14,8 @@ import ClientAPI from "@/lib/client/ClientAPI"; import { team } from "slack"; import { NotLinkedToTba } from "@/lib/client/ClientUtils"; import { defaultGameId } from "@/lib/client/GameId"; +import PredictionScreen from "@/components/stats/PredictionScreen"; +import { games } from "@/lib/games"; const api = new ClientAPI("gearboxiscool"); @@ -110,18 +112,18 @@ export default function Stats(props: StatsPageProps) { setPage(1); }} > - Picklist (Beta) + Picklist { - // setPage(2); + setPage(2); }} > - Prediction (Coming Soon!) + Prediction {/* Resync {" "} @@ -138,11 +140,14 @@ export default function Stats(props: StatsPageProps) { )} - {page === 1 - ? - : <> + } + { + page === 2 && + } );