Skip to content

Commit

Permalink
Merge pull request #32 from Acr515/development
Browse files Browse the repository at this point in the history
Version 2025.0.0
  • Loading branch information
Acr515 authored Mar 8, 2025
2 parents 02bc289 + 47cc5d6 commit df309e5
Show file tree
Hide file tree
Showing 32 changed files with 2,721 additions and 395 deletions.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "fortyeight",
"version": "3.1.2",
"version": "4.0.0",
"description": "",
"homepage": "./",
"main": "index.js",
"scripts": {
"start": "webpack serve --mode development --env game_year=2024",
"start": "webpack serve --mode development --env game_year=2025",
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --env game_year=2024",
"build-ghp": "webpack --env game_year=2024 ghp",
"build": "webpack --env game_year=2025",
"build-ghp": "webpack --env game_year=2025 ghp",
"deploy": "gh-pages -d build"
},
"keywords": [],
Expand Down
147 changes: 147 additions & 0 deletions src/components/game_specific/GameDataInputs/2025.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import React, { useState } from 'react';
import Input from 'components/Input';
import { EndgameResult } from 'data/game_specific/performanceObject/2025';

/**
* An array that stores all categories as strings that have data
*/
export const GameDataCategories = [
"auto", "teleop", "endgame"
];

/**
* A dictionary of form inputs that scouts fill with data. The HTML ID of each element is named very carefully:
* * The first phrase should be "Form_"
* * ... followed by the section of the performance object to populate which will generally be "auto," "teleop," or "endgame," and another score
* * ... followed lastly by the scoring section to fill (i.e. if data should go into the "upperGoal" area, that should be the final section of the ID)
*
* Additionally contains a boolean property `defenseFields` that decides whether or not the generic defense fields should appear.
*/
export const GameDataInputs = {
AutonomousSection: ({edit}) => <>
<Input
label="Leave (Autocross)"
id="Form_auto_leave"
isCheckbox={true}
prefill={edit.isEdit ? edit.data.performance.auto.leave : undefined}
/>
<h3>Algae</h3>
<Input
label="Algae in Processor (Low)"
id="Form_auto_algaeLow"
isNumerical={true}
prefill={edit.isEdit ? edit.data.performance.auto.algaeLow : undefined}
/>
<Input
label="Algae in Net (High)"
id="Form_auto_algaeHigh"
isNumerical={true}
prefill={edit.isEdit ? edit.data.performance.auto.algaeHigh : undefined}
/>
<h3>Coral</h3>
<Input
label="Coral in Trough (Level 1)"
id="Form_auto_coralL1"
isNumerical={true}
prefill={edit.isEdit ? edit.data.performance.auto.coralL1 : undefined}
/>
<Input
label="Coral on Level 2"
id="Form_auto_coralL2"
isNumerical={true}
prefill={edit.isEdit ? edit.data.performance.auto.coralL2 : undefined}
/>
<Input
label="Coral on Level 3"
id="Form_auto_coralL3"
isNumerical={true}
prefill={edit.isEdit ? edit.data.performance.auto.coralL3 : undefined}
/>
<Input
label="Coral on Level 4"
id="Form_auto_coralL4"
isNumerical={true}
prefill={edit.isEdit ? edit.data.performance.auto.coralL4 : undefined}
/>
</>,
TeleopSection: ({edit}) => <>
<h3>Algae</h3>
<Input
label="Algae in Processor (Low)"
id="Form_teleop_algaeLow"
isNumerical={true}
prefill={edit.isEdit ? edit.data.performance.teleop.algaeLow : undefined}
/>
<Input
label="Algae in Net (High)"
id="Form_teleop_algaeHigh"
isNumerical={true}
prefill={edit.isEdit ? edit.data.performance.teleop.algaeHigh : undefined}
/>
<h3>Coral</h3>
<Input
label="Coral in Trough (Level 1)"
id="Form_teleop_coralL1"
isNumerical={true}
prefill={edit.isEdit ? edit.data.performance.teleop.coralL1 : undefined}
/>
<Input
label="Coral on Level 2"
id="Form_teleop_coralL2"
isNumerical={true}
prefill={edit.isEdit ? edit.data.performance.teleop.coralL2 : undefined}
/>
<Input
label="Coral on Level 3"
id="Form_teleop_coralL3"
isNumerical={true}
prefill={edit.isEdit ? edit.data.performance.teleop.coralL3 : undefined}
/>
<Input
label="Coral on Level 4"
id="Form_teleop_coralL4"
isNumerical={true}
prefill={edit.isEdit ? edit.data.performance.teleop.coralL4 : undefined}
/>
</>,
EndgameSection: ({edit}) => {
const [endgameState, setEndgameState] = useState(edit.isEdit ? (edit.data.performance.endgame.state == EndgameResult.HARMONIZED ? EndgameResult.ONSTAGE : edit.data.performance.endgame.state) : undefined);

return <>
<Input
label="Climb"
id="Form_endgame_state"
optionList={[
{ value: EndgameResult.NONE, label: EndgameResult.NONE },
{ value: EndgameResult.PARK, label: EndgameResult.PARK },
{ value: EndgameResult.SHALLOW_CAGE, label: EndgameResult.SHALLOW_CAGE },
{ value: EndgameResult.DEEP_CAGE, label: EndgameResult.DEEP_CAGE },
]}
onInput={e => setEndgameState(e.target.value)}
required={true}
prefill={endgameState}
/>
<Input
label="Tried to climb but failed?"
id="Form_endgame_failedAttempt"
isCheckbox={true}
prefill={edit.isEdit ? edit.data.performance.endgame.failedAttempt : undefined}
/>
</>
},
NotesSection: ({edit}) => <>
<Input
label="This team missed or dropped a majority of the game pieces they attempted to score"
id="Form_notes_misses"
isCheckbox={true}
prefill={edit.isEdit ? edit.data.performance.notes.misses : undefined}
/>
<Input
label="This team picked coral up off the floor"
id="Form_notes_floorPickup"
isCheckbox={true}
prefill={edit.isEdit ? edit.data.performance.notes.floorPickup : undefined}
/>
</>,
defenseFields: true
};
75 changes: 75 additions & 0 deletions src/components/game_specific/GraphTogglerSet/2025.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React from "react";
import GraphTogglerSet_Universal, { GraphToggler, GraphInfo } from "./_Universal";
import ScoreCalculator from "data/game_specific/ScoreCalculator/2025";
import { getTeamData } from "data/SearchData";
import { Method, sortTeamData } from "util/sortData";

/**
* A set of buttons used to toggle which performance graph is being shown on a team's details page.
*/
export default function GraphTogglerSet({activeIndex, stateFuncs, teamNumber}) {
let data = sortTeamData(getTeamData(teamNumber).data, Method.MatchAscending);

// Calculate cycles per game
let piecesLabels = [];
data.forEach(form => { piecesLabels.push(form.matchNumber); });
let piecesData = [];
data.forEach(form => { piecesData.push(ScoreCalculator.Auto.getPieces(form) + ScoreCalculator.Teleop.getPieces(form)); });
let piecesGraphInfo = new GraphInfo(
piecesData, piecesLabels,
{
title: { text: "Match #" }
},
{
suggestedMin: 0,
suggestedMax: 12,
title: { text: "Cycles" }
},
[ 177, 65, 73 ]
);

// Calculate auto scores per game
let autoLabels = [];
data.forEach(form => { autoLabels.push(form.matchNumber); });
let autoData = [];
data.forEach(form => { autoData.push((ScoreCalculator.Auto.getScore(form))); });
let autocrossGraphInfo = new GraphInfo(
autoData, autoLabels,
{
title: { text: "Match #" }
},
{
suggestedMin: 0,
suggestedMax: 8,
ticks: {
precision: 0
},
title: { text: "Auto score" }
},
[ 204, 201, 57 ]
);

return (
<>
<GraphTogglerSet_Universal
activeIndex={activeIndex}
stateFuncs={stateFuncs}
teamNumber={teamNumber}
/>
<GraphToggler
graphInfo={piecesGraphInfo}
label="Cycles/game"
index={1}
activeIndex={activeIndex}
stateFuncs={stateFuncs}
/>
<GraphToggler
graphInfo={autocrossGraphInfo}
label="Auto/game"
index={2}
activeIndex={activeIndex}
stateFuncs={stateFuncs}
/>
</>
)
}
25 changes: 25 additions & 0 deletions src/components/game_specific/PlayoffHelperTeamCell/2025.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from "react";
import PlayoffHelperTeamCell from "./_Universal";
import { getOrdinalSuffix } from "util/getOrdinalSuffix";

/**
* Renders a game-specific set of cells to be populated under every team who is a candidate to be picked.
* @param {PlayoffTeam} team The team who the cells belong to
*/
export default function PlayoffHelperTeamCellSet({ team }) {

const defenseScore = team.powerScores.WellRounded.Defense == 0 ? "--" : team.powerScores.Defensive.Defense;
const defenseRanking = team.powerScores.WellRounded.Defense == 0 ? "" : getOrdinalSuffix(team.powerScoreRankings.Defense);

return (
<>
<PlayoffHelperTeamCell value={team.rpi.RPI} place={getOrdinalSuffix(team.rpi.ranking)} label={`RPI (${team.rpi.rating})`} />
<PlayoffHelperTeamCell value={team.powerScores.WellRounded.Autonomous} place={getOrdinalSuffix(team.powerScoreRankings.Autonomous)} label={"Autonomous"} />
<PlayoffHelperTeamCell value={team.powerScores.WellRounded.Coral} place={getOrdinalSuffix(team.powerScoreRankings.Coral)} label={"Coral Scoring"} />
<PlayoffHelperTeamCell value={team.powerScores.WellRounded.Algae} place={getOrdinalSuffix(team.powerScoreRankings.Algae)} label={"Algae Scoring"} />
<PlayoffHelperTeamCell value={team.powerScores.WellRounded.Endgame} place={getOrdinalSuffix(team.powerScoreRankings.Endgame)} label={"Endgame"} />
<PlayoffHelperTeamCell value={defenseScore} place={defenseRanking} label={"Defense"} />
<PlayoffHelperTeamCell value={team.cycleRate} place={getOrdinalSuffix(team.cycleRateRanking)} label={`Cycles / Game`} />
</>
)
}
37 changes: 37 additions & 0 deletions src/components/game_specific/SimulatorInsightRowSet/2025.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from "react";
import SimulatorInsightRow from "components/SimulatorInsightRow";
import ScoreCalculator from "data/game_specific/ScoreCalculator/2025";

export default function SimulatorInsightRowSet({sim, winner, loser}) {

// Find best game piece scorers
let winnerBestScorer = -1, loserBestScorer = -1;
const getBestPieceScorers = teamColor => {
let bestTeleopScore = -100;
let bestScorer = -1;
sim.averageMatch[teamColor].teamPerformances.forEach(team => {
let score = ScoreCalculator.Teleop.getScore({ performance: team });

// Normally this section would decide to pick whoever climbs the highest on average if the two best robots score the same # of points
// But there's no endgame
if (score > bestTeleopScore) {
bestTeleopScore = score;
bestScorer = team.teamNumber;
}
});
return bestScorer;
};
winnerBestScorer = getBestPieceScorers(winner.colorName);
loserBestScorer = getBestPieceScorers(loser.colorName);

return <>
<SimulatorInsightRow
label="Strongest Piece Scorer"
winnerValue={winnerBestScorer}
winnerColor={winner.color}
loserValue={loserBestScorer}
loserColor={loser.color}
hyperlinkTeams
/>
</>
}
Loading

0 comments on commit df309e5

Please sign in to comment.