diff --git a/db/migrations/20140524160241_CreateEventSettings.sql b/db/migrations/20140524160241_CreateEventSettings.sql index b75106b..2f1a597 100644 --- a/db/migrations/20140524160241_CreateEventSettings.sql +++ b/db/migrations/20140524160241_CreateEventSettings.sql @@ -22,7 +22,8 @@ CREATE TABLE event_settings ( plcaddress VARCHAR(255), tbadownloadenabled bool, adminpassword VARCHAR(255), - readerpassword VARCHAR(255) + readerpassword VARCHAR(255), + habdockingthreshold int ); -- +goose Down diff --git a/field/arena.go b/field/arena.go index 730a085..ed49f0e 100644 --- a/field/arena.go +++ b/field/arena.go @@ -141,6 +141,8 @@ func (arena *Arena) LoadSettings() error { } } + game.HabDockingThreshold = settings.HabDockingThreshold + return nil } diff --git a/field/arena_notifiers.go b/field/arena_notifiers.go index 8260f5c..b0089be 100644 --- a/field/arena_notifiers.go +++ b/field/arena_notifiers.go @@ -48,10 +48,6 @@ type MatchTimeMessage struct { type audienceAllianceScoreFields struct { Score int RealtimeScore *RealtimeScore - ForceState game.PowerUpState - LevitateState game.PowerUpState - BoostState game.PowerUpState - SwitchOwnedBy game.Alliance } // Instantiates notifiers and configures their message producing methods. diff --git a/game/foul.go b/game/foul.go index d1f0196..6fb3167 100644 --- a/game/foul.go +++ b/game/foul.go @@ -12,49 +12,53 @@ type Foul struct { } type Rule struct { - RuleNumber string - IsTechnical bool - Description string + RuleNumber string + IsTechnical bool + IsRankingPoint bool + Description string } // All rules from the 2018 game that carry point penalties. var Rules = []Rule{ - {"S06", false, "DRIVE TEAMS may not extend any body part into the RETURN chute, the PORTAL chute, or the EXCHANGE tunnel."}, - {"C07", false, "Strategies clearly aimed at forcing the opposing ALLIANCE to violate a rule are not in the spirit of FIRST® Robotics Competition and not allowed."}, - {"C07", true, "Strategies clearly aimed at forcing the opposing ALLIANCE to violate a rule are not in the spirit of FIRST® Robotics Competition and not allowed."}, - {"G05", false, "ROBOTS may not extend more than 16 in (41 cm). beyond their FRAME PERIMETER."}, - {"G07", false, "ROBOTS must be in compliance with BUMPER rules throughout the MATCH."}, - {"G10", false, "Strategies aimed at the destruction or inhibition of ROBOTS via attachment, damage, tipping, or entanglements are not allowed."}, - {"G11", false, "Initiating deliberate or damaging contact with an opponent ROBOT on or inside the vertical extension of its FRAME PERIMETER, including transitively through a POWER CUBE, is not allowed."}, - {"G13", false, "Fallen (i.e. tipped over) ROBOTS attempting to right themselves (either by themselves or with assistance from a partner ROBOT) have one ten (10) second grace period in which they may not be contacted by an opponent ROBOT."}, - {"G14", false, "ROBOTS may not pin an opponent’s ROBOT for more than five (5) seconds."}, - {"G15", false, "A ROBOT may not block their opponent’s EXCHANGE ZONE for more than five (5) seconds."}, - {"G16", true, "A ROBOT whose BUMPERS are breaking the plane of or completely contained by its NULL TERRITORY and not breaking the plane of the opponent’s PLATFORM ZONE may not be contacted by an opposing ROBOT either directly or transitively through a POWER CUBE, regardless of who initiates the contact."}, - {"G17", true, "Unless during the ENDGAME, or attempting to right a fallen (i.e. tipped over) ALLIANCE partner, ROBOTS may neither fully nor partially strategically support the weight of partner ROBOTS."}, - {"G19", false, "DRIVE TEAMS, ROBOTS, and OPERATOR CONSOLES are prohibited from the following actions with regards to interaction with ARCADE elements: grabbing, grasping, attaching to, hanging, deforming, becoming entangled, and damaging."}, - {"G20", true, "With the exception of placing a POWER CUBES on PLATES, ROBOTS may not deliberately use POWER CUBES in an attempt to ease or amplify the challenge associated with FIELD elements."}, - {"G21", false, "With the exception of feeding POWER CUBES through the lower opening of the EXCHANGE, ROBOTS may not intentionally eject POWER CUBES from the FIELD."}, - {"G22", false, "ROBOTS may not control more than one (1) POWER CUBE at a time, except when breaking the plane of their own EXCHANGE ZONE."}, - {"G23", false, "ROBOTS may not remove POWER CUBES, or cause POWER CUBES to be removed, from the opponent’s POWER CUBE ZONE."}, - {"G24", true, "Strategies aimed at removing POWER CUBES from PLATES are prohibited."}, - {"G25", false, "Except via the weight of placed POWER CUBES, ROBOTS may not directly or transitively cause or prevent the movement of PLATES to their ALLIANCE's advantage."}, - {"G25", true, "Except via the weight of placed POWER CUBES, ROBOTS may not directly or transitively cause or prevent the movement of PLATES to their ALLIANCE's advantage."}, - {"A01", false, "During AUTO, DRIVE TEAM members in ALLIANCE STATIONS and PORTALS may not contact anything in front of the STARTING LINES, unless for personal or equipment safety."}, - {"A02", false, "During AUTO, DRIVE TEAMS may not directly or indirectly interact with ROBOTS or OPERATOR CONSOLES unless for personal safety, OPERATOR CONSOLE safety, or pressing an E-Stop for ROBOT safety."}, - {"A03", false, "During AUTO, any control devices worn or held by the DRIVERS and/or HUMAN PLAYERS must be disconnected from the OPERATOR CONSOLE."}, - {"A04", false, "During AUTO, no part of a ROBOT’S BUMPERS may pass from the NULL TERRITORY to the opponent’s side of the FIELD."}, - {"A04", true, "During AUTO, no part of a ROBOT’S BUMPERS may pass from the NULL TERRITORY to the opponent’s side of the FIELD."}, - {"A05", false, "During AUTO, DRIVE TEAMS may not contact any POWER CUBES, unless for personal safety."}, - {"H06", false, "DRIVE TEAM members may not contact anything outside the zone in which they started the MATCH (e.g. the ALLIANCE STATION, PORTAL, designated area for the TECHNICIAN) during the MATCH."}, - {"H11", true, "During a MATCH, COACHES may not touch POWER CUBES unless for safety purposes."}, - {"H12", true, "During a MATCH, COACHES may not touch any component of the VAULT (including the buttons) unless for safety purposes."}, - {"H13", false, "DRIVE TEAMS may only deliberately cause POWER CUBES to leave an ALLIANCE STATION or PORTAL during TELEOP, by a HUMAN PLAYER or DRIVER, and through a PORTAL wall or the RETURN."}, - {"H14", false, "POWER CUBES may not be removed from the VAULT."}, + {"S06", false, false, "DRIVE TEAMS may not extend any body part into the RETURN chute, the PORTAL chute, or the EXCHANGE tunnel."}, + {"C07", false, false, "Strategies clearly aimed at forcing the opposing ALLIANCE to violate a rule are not in the spirit of FIRST® Robotics Competition and not allowed."}, + {"C07", true, false, "Strategies clearly aimed at forcing the opposing ALLIANCE to violate a rule are not in the spirit of FIRST® Robotics Competition and not allowed."}, + {"G05", false, false, "ROBOTS may not extend more than 16 in (41 cm). beyond their FRAME PERIMETER."}, + {"G07", false, false, "ROBOTS must be in compliance with BUMPER rules throughout the MATCH."}, + {"G10", false, false, "Strategies aimed at the destruction or inhibition of ROBOTS via attachment, damage, tipping, or entanglements are not allowed."}, + {"G11", false, false, "Initiating deliberate or damaging contact with an opponent ROBOT on or inside the vertical extension of its FRAME PERIMETER, including transitively through a POWER CUBE, is not allowed."}, + {"G13", false, false, "Fallen (i.e. tipped over) ROBOTS attempting to right themselves (either by themselves or with assistance from a partner ROBOT) have one ten (10) second grace period in which they may not be contacted by an opponent ROBOT."}, + {"G14", false, false, "ROBOTS may not pin an opponent’s ROBOT for more than five (5) seconds."}, + {"G15", false, false, "A ROBOT may not block their opponent’s EXCHANGE ZONE for more than five (5) seconds."}, + {"G16", true, false, "A ROBOT whose BUMPERS are breaking the plane of or completely contained by its NULL TERRITORY and not breaking the plane of the opponent’s PLATFORM ZONE may not be contacted by an opposing ROBOT either directly or transitively through a POWER CUBE, regardless of who initiates the contact."}, + {"G17", true, false, "Unless during the ENDGAME, or attempting to right a fallen (i.e. tipped over) ALLIANCE partner, ROBOTS may neither fully nor partially strategically support the weight of partner ROBOTS."}, + {"G19", false, false, "DRIVE TEAMS, ROBOTS, and OPERATOR CONSOLES are prohibited from the following actions with regards to interaction with ARCADE elements: grabbing, grasping, attaching to, hanging, deforming, becoming entangled, and damaging."}, + {"G20", true, false, "With the exception of placing a POWER CUBES on PLATES, ROBOTS may not deliberately use POWER CUBES in an attempt to ease or amplify the challenge associated with FIELD elements."}, + {"G21", false, false, "With the exception of feeding POWER CUBES through the lower opening of the EXCHANGE, ROBOTS may not intentionally eject POWER CUBES from the FIELD."}, + {"G22", false, false, "ROBOTS may not control more than one (1) POWER CUBE at a time, except when breaking the plane of their own EXCHANGE ZONE."}, + {"G23", false, false, "ROBOTS may not remove POWER CUBES, or cause POWER CUBES to be removed, from the opponent’s POWER CUBE ZONE."}, + {"G24", true, false, "Strategies aimed at removing POWER CUBES from PLATES are prohibited."}, + {"G25", false, false, "Except via the weight of placed POWER CUBES, ROBOTS may not directly or transitively cause or prevent the movement of PLATES to their ALLIANCE's advantage."}, + {"G25", true, false, "Except via the weight of placed POWER CUBES, ROBOTS may not directly or transitively cause or prevent the movement of PLATES to their ALLIANCE's advantage."}, + {"A01", false, false, "During AUTO, DRIVE TEAM members in ALLIANCE STATIONS and PORTALS may not contact anything in front of the STARTING LINES, unless for personal or equipment safety."}, + {"A02", false, false, "During AUTO, DRIVE TEAMS may not directly or indirectly interact with ROBOTS or OPERATOR CONSOLES unless for personal safety, OPERATOR CONSOLE safety, or pressing an E-Stop for ROBOT safety."}, + {"A03", false, false, "During AUTO, any control devices worn or held by the DRIVERS and/or HUMAN PLAYERS must be disconnected from the OPERATOR CONSOLE."}, + {"A04", false, false, "During AUTO, no part of a ROBOT’S BUMPERS may pass from the NULL TERRITORY to the opponent’s side of the FIELD."}, + {"A04", true, false, "During AUTO, no part of a ROBOT’S BUMPERS may pass from the NULL TERRITORY to the opponent’s side of the FIELD."}, + {"A05", false, false, "During AUTO, DRIVE TEAMS may not contact any POWER CUBES, unless for personal safety."}, + {"H06", false, false, "DRIVE TEAM members may not contact anything outside the zone in which they started the MATCH (e.g. the ALLIANCE STATION, PORTAL, designated area for the TECHNICIAN) during the MATCH."}, + {"H11", true, false, "During a MATCH, COACHES may not touch POWER CUBES unless for safety purposes."}, + {"H12", true, false, "During a MATCH, COACHES may not touch any component of the VAULT (including the buttons) unless for safety purposes."}, + {"H13", false, false, "DRIVE TEAMS may only deliberately cause POWER CUBES to leave an ALLIANCE STATION or PORTAL during TELEOP, by a HUMAN PLAYER or DRIVER, and through a PORTAL wall or the RETURN."}, + {"H14", false, false, "POWER CUBES may not be removed from the VAULT."}, } func (foul *Foul) PointValue() int { if foul.IsTechnical { return 25 + } else if foul.IsRankingPoint { + return 0 + } else { + return 5 } - return 5 } diff --git a/game/game_specific_data.go b/game/game_specific_data.go deleted file mode 100644 index 4a7356b..0000000 --- a/game/game_specific_data.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2018 Team 254. All Rights Reserved. -// Author: pat@patfairbank.com (Patrick Fairbank) -// -// Logic to generate the 2018 game-specific data. - -package game - -import "math/rand" - -var validGameSpecificDatas = []string{"RRR", "LLL", "RLR", "LRL"} - -// Returns a random configuration. -func GenerateGameSpecificData() string { - return validGameSpecificDatas[rand.Intn(len(validGameSpecificDatas))] -} - -// Returns true if the given game specific data is valid. -func IsValidGameSpecificData(gameSpecificData string) bool { - for _, data := range validGameSpecificDatas { - if data == gameSpecificData { - return true - } - } - return false -} diff --git a/game/game_specific_data_test.go b/game/game_specific_data_test.go deleted file mode 100644 index dc224d1..0000000 --- a/game/game_specific_data_test.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2018 Team 254. All Rights Reserved. -// Author: pat@patfairbank.com (Patrick Fairbank) - -package game - -import ( - "github.com/stretchr/testify/assert" - "math/rand" - "testing" -) - -func TestGenerateGameSpecificData(t *testing.T) { - rand.Seed(0) - - // Make sure all possibilities are hit at least twice. - assert.Equal(t, "RLR", GenerateGameSpecificData()) - assert.Equal(t, "RLR", GenerateGameSpecificData()) - assert.Equal(t, "LLL", GenerateGameSpecificData()) - assert.Equal(t, "RLR", GenerateGameSpecificData()) - assert.Equal(t, "LRL", GenerateGameSpecificData()) - assert.Equal(t, "RRR", GenerateGameSpecificData()) - assert.Equal(t, "LRL", GenerateGameSpecificData()) - assert.Equal(t, "LLL", GenerateGameSpecificData()) - assert.Equal(t, "RRR", GenerateGameSpecificData()) - assert.Equal(t, "RRR", GenerateGameSpecificData()) -} - -func TestIsValidGameSpecificData(t *testing.T) { - for _, data := range validGameSpecificDatas { - assert.True(t, IsValidGameSpecificData(data)) - } - - assert.False(t, IsValidGameSpecificData("")) - assert.False(t, IsValidGameSpecificData("R")) - assert.False(t, IsValidGameSpecificData("RL")) - assert.False(t, IsValidGameSpecificData("RRL")) - assert.False(t, IsValidGameSpecificData("RRRL")) -} diff --git a/game/power_up.go b/game/power_up.go deleted file mode 100644 index ae0ce7c..0000000 --- a/game/power_up.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2018 Team 254. All Rights Reserved. -// Author: pat@patfairbank.com (Patrick Fairbank) -// -// Scoring logic for the 2018 power ups. - -package game - -import ( - "time" -) - -const powerUpDurationSec = 10 - -// Power up type/effect enum. -type PowerUpEffect int - -const ( - Force PowerUpEffect = iota - Boost -) - -// Power up state enum. -type PowerUpState int - -const ( - Unplayed PowerUpState = iota - Queued - Active - Expired -) - -type PowerUp struct { - Alliance - Effect PowerUpEffect - Level int - startTime time.Time -} - -var powerUpUses []*PowerUp - -func ResetPowerUps() { - powerUpUses = powerUpUses[:0] -} - -func (powerUp *PowerUp) GetState(currentTime time.Time) PowerUpState { - if powerUp.startTime.After(currentTime) { - return Queued - } - if powerUp.getEndTime().After(currentTime) { - return Active - } - return Expired -} - -func (powerUp *PowerUp) getEndTime() time.Time { - return powerUp.startTime.Add(powerUpDurationSec * time.Second) -} - -// Returns the current active power up, or nil if there isn't one. -func GetActivePowerUp(currentTime time.Time) *PowerUp { - for _, powerUp := range powerUpUses { - if powerUp.GetState(currentTime) == Active { - return powerUp - } - } - return nil -} - -// Activates the given power up if it can be played, or if not, queues it if the active power up belongs to the other -// alliance. Returns the power up if successful and nil if it cannot be played. -func maybeActivatePowerUp(powerUp *PowerUp, currentTime time.Time) *PowerUp { - canActivate := false - if len(powerUpUses) == 0 { - canActivate = true - powerUp.startTime = currentTime - } else { - lastPowerUp := powerUpUses[len(powerUpUses)-1] - lastPowerUpState := lastPowerUp.GetState(currentTime) - if lastPowerUpState == Expired { - canActivate = true - powerUp.startTime = currentTime - } else if lastPowerUpState == Active && lastPowerUp.Alliance != powerUp.Alliance { - canActivate = true - powerUp.startTime = lastPowerUp.getEndTime() - } - } - - if canActivate { - powerUpUses = append(powerUpUses, powerUp) - return powerUp - } - - return nil -} diff --git a/game/power_up_test.go b/game/power_up_test.go deleted file mode 100644 index 38717b2..0000000 --- a/game/power_up_test.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2018 Team 254. All Rights Reserved. -// Author: pat@patfairbank.com (Patrick Fairbank) - -package game - -import ( - "github.com/stretchr/testify/assert" - "testing" - "time" -) - -var matchStartTime = time.Unix(10, 0) - -func TestPowerUpGetState(t *testing.T) { - powerUp := PowerUp{startTime: timeAfterStart(30)} - assert.Equal(t, Queued, powerUp.GetState(timeAfterStart(25))) - assert.Equal(t, Queued, powerUp.GetState(timeAfterStart(29.9))) - assert.Equal(t, Active, powerUp.GetState(timeAfterStart(30.1))) - assert.Equal(t, Active, powerUp.GetState(timeAfterStart(39.9))) - assert.Equal(t, Expired, powerUp.GetState(timeAfterStart(40.1))) -} - -func TestPowerUpActivate(t *testing.T) { - powerUp1 := new(PowerUp) - if assert.NotNil(t, maybeActivatePowerUp(powerUp1, timeAfterStart(30))) { - assert.Equal(t, timeAfterStart(30), powerUp1.startTime) - } - - powerUp2 := new(PowerUp) - if assert.NotNil(t, maybeActivatePowerUp(powerUp2, timeAfterStart(45))) { - assert.Equal(t, timeAfterStart(45), powerUp2.startTime) - } - - assert.Nil(t, GetActivePowerUp(timeAfterStart(29.9))) - assert.Equal(t, powerUp1, GetActivePowerUp(timeAfterStart(30.1))) - assert.Equal(t, powerUp1, GetActivePowerUp(timeAfterStart(39.9))) - assert.Nil(t, GetActivePowerUp(timeAfterStart(42))) - assert.Equal(t, powerUp2, GetActivePowerUp(timeAfterStart(45.1))) - assert.Equal(t, powerUp2, GetActivePowerUp(timeAfterStart(54.9))) - assert.Nil(t, GetActivePowerUp(timeAfterStart(55.1))) -} - -func TestPowerUpQueue(t *testing.T) { - ResetPowerUps() - - powerUp1 := &PowerUp{Alliance: RedAlliance} - assert.NotNil(t, maybeActivatePowerUp(powerUp1, timeAfterStart(60))) - - powerUp2 := &PowerUp{Alliance: RedAlliance} - assert.Nil(t, maybeActivatePowerUp(powerUp2, timeAfterStart(65))) - powerUp2.Alliance = BlueAlliance - if assert.NotNil(t, maybeActivatePowerUp(powerUp2, timeAfterStart(65))) { - assert.Equal(t, timeAfterStart(70), powerUp2.startTime) - } - - powerUp3 := &PowerUp{Alliance: RedAlliance} - assert.NotNil(t, maybeActivatePowerUp(powerUp3, timeAfterStart(81))) - - assert.Equal(t, powerUp1, GetActivePowerUp(timeAfterStart(69.9))) - assert.Equal(t, powerUp2, GetActivePowerUp(timeAfterStart(70.1))) -} - -func timeAfterStart(sec float32) time.Time { - return matchStartTime.Add(time.Duration(1000*sec) * time.Millisecond) -} - -func timeAfterEnd(sec float32) time.Time { - matchDuration := time.Duration(MatchTiming.AutoDurationSec+MatchTiming.PauseDurationSec+ - MatchTiming.TeleopDurationSec) * time.Second - return matchStartTime.Add(matchDuration).Add(time.Duration(1000*sec) * time.Millisecond) -} diff --git a/game/ranking_fields.go b/game/ranking_fields.go index 00d936b..746d57b 100644 --- a/game/ranking_fields.go +++ b/game/ranking_fields.go @@ -8,17 +8,17 @@ package game import "math/rand" type RankingFields struct { - RankingPoints int - ParkClimbPoints int - AutoPoints int - OwnershipPoints int - VaultPoints int - Random float64 - Wins int - Losses int - Ties int - Disqualifications int - Played int + RankingPoints int + CargoPoints int + HatchPanelPoints int + HabClimbPoints int + SandstormBonusPoints int + Random float64 + Wins int + Losses int + Ties int + Disqualifications int + Played int } type Ranking struct { @@ -48,18 +48,18 @@ func (fields *RankingFields) AddScoreSummary(ownScore *ScoreSummary, opponentSco } else { fields.Losses += 1 } - if ownScore.AutoQuest { + if ownScore.CompleteRocket { fields.RankingPoints += 1 } - if ownScore.FaceTheBoss { + if ownScore.HabDocking { fields.RankingPoints += 1 } // Assign tiebreaker points. - fields.ParkClimbPoints += ownScore.ParkClimbPoints - fields.AutoPoints += ownScore.AutoPoints - fields.OwnershipPoints += ownScore.OwnershipPoints - fields.VaultPoints += ownScore.VaultPoints + fields.CargoPoints += ownScore.CargoPoints + fields.HatchPanelPoints += ownScore.HatchPanelPoints + fields.HabClimbPoints += ownScore.HabClimbPoints + fields.SandstormBonusPoints += ownScore.SandstormBonusPoints // Store a random value to be used as the last tiebreaker if necessary. fields.Random = rand.Float64() @@ -77,19 +77,19 @@ func (rankings Rankings) Less(i, j int) bool { // Use cross-multiplication to keep it in integer math. if a.RankingPoints*b.Played == b.RankingPoints*a.Played { - if a.ParkClimbPoints*b.Played == b.ParkClimbPoints*a.Played { - if a.AutoPoints*b.Played == b.AutoPoints*a.Played { - if a.OwnershipPoints*b.Played == b.OwnershipPoints*a.Played { - if a.VaultPoints*b.Played == b.VaultPoints*a.Played { + if a.CargoPoints*b.Played == b.CargoPoints*a.Played { + if a.HatchPanelPoints*b.Played == b.HatchPanelPoints*a.Played { + if a.HabClimbPoints*b.Played == b.HabClimbPoints*a.Played { + if a.SandstormBonusPoints*b.Played == b.SandstormBonusPoints*a.Played { return a.Random > b.Random } - return a.VaultPoints*b.Played > b.VaultPoints*a.Played + return a.SandstormBonusPoints*b.Played > b.SandstormBonusPoints*a.Played } - return a.OwnershipPoints*b.Played > b.OwnershipPoints*a.Played + return a.HabClimbPoints*b.Played > b.HabClimbPoints*a.Played } - return a.AutoPoints*b.Played > b.AutoPoints*a.Played + return a.HatchPanelPoints*b.Played > b.HatchPanelPoints*a.Played } - return a.ParkClimbPoints*b.Played > b.ParkClimbPoints*a.Played + return a.CargoPoints*b.Played > b.CargoPoints*a.Played } return a.RankingPoints*b.Played > b.RankingPoints*a.Played } diff --git a/game/ranking_fields_test.go b/game/ranking_fields_test.go index 65129d2..255911e 100644 --- a/game/ranking_fields_test.go +++ b/game/ranking_fields_test.go @@ -20,19 +20,19 @@ func TestAddScoreSummary(t *testing.T) { // Add a loss. rankingFields.AddScoreSummary(redSummary, blueSummary, false) - assert.Equal(t, RankingFields{1, 90, 17, 59, 15, 0.9451961492941164, 0, 1, 0, 0, 1}, rankingFields) + assert.Equal(t, RankingFields{1, 30, 20, 12, 9, 0.9451961492941164, 0, 1, 0, 0, 1}, rankingFields) // Add a win. rankingFields.AddScoreSummary(blueSummary, redSummary, false) - assert.Equal(t, RankingFields{4, 125, 52, 152, 45, 0.24496508529377975, 1, 1, 0, 0, 2}, rankingFields) + assert.Equal(t, RankingFields{4, 42, 20, 27, 15, 0.24496508529377975, 1, 1, 0, 0, 2}, rankingFields) // Add a tie. rankingFields.AddScoreSummary(redSummary, redSummary, false) - assert.Equal(t, RankingFields{6, 215, 69, 211, 60, 0.6559562651954052, 1, 1, 1, 0, 3}, rankingFields) + assert.Equal(t, RankingFields{6, 72, 40, 39, 24, 0.6559562651954052, 1, 1, 1, 0, 3}, rankingFields) // Add a disqualification. rankingFields.AddScoreSummary(blueSummary, redSummary, true) - assert.Equal(t, RankingFields{6, 215, 69, 211, 60, 0.6559562651954052, 1, 1, 1, 1, 4}, rankingFields) + assert.Equal(t, RankingFields{6, 72, 40, 39, 24, 0.6559562651954052, 1, 1, 1, 1, 4}, rankingFields) } func TestSortRankings(t *testing.T) { diff --git a/game/score.go b/game/score.go index 75cd53c..2dfa731 100644 --- a/game/score.go +++ b/game/score.go @@ -6,40 +6,42 @@ package game type Score struct { - AutoRuns int - AutoSwitchOwnershipSec float64 - AutoScaleOwnershipSec float64 - AutoEndSwitchOwnership bool - TeleopScaleOwnershipSec float64 - TeleopScaleBoostSec float64 - TeleopSwitchOwnershipSec float64 - TeleopSwitchBoostSec float64 - ForceCubes int - ForceCubesPlayed int - LevitateCubes int - LevitatePlayed bool - BoostCubes int - BoostCubesPlayed int - Climbs int - Parks int - Fouls []Foul - ElimDq bool + RobotStartLevels [3]int + SandstormBonuses [3]bool + CargoBaysPreMatch [8]BayStatus + CargoBays [8]BayStatus + RocketNearLeftBays [3]BayStatus + RocketNearRightBays [3]BayStatus + RocketFarLeftBays [3]BayStatus + RocketFarRightBays [3]BayStatus + RobotEndLevels [3]int + Fouls []Foul + ElimDq bool } type ScoreSummary struct { - AutoRunPoints int - AutoOwnershipPoints int - AutoPoints int - TeleopOwnershipPoints int - OwnershipPoints int - VaultPoints int - ParkClimbPoints int - FoulPoints int - Score int - AutoQuest bool - FaceTheBoss bool + CargoPoints int + HatchPanelPoints int + HabClimbPoints int + SandstormBonusPoints int + FoulPoints int + Score int + CompleteRocket bool + HabDocking bool } +// Represents the state of a cargo ship or rocket bay. +type BayStatus int + +const ( + BayEmpty BayStatus = iota + BayHatch + BayHatchCargo + BayCargo +) + +var HabDockingThreshold = 15 + // Calculates and returns the summary fields used for ranking and display. func (score *Score) Summarize(opponentFouls []Foul) *ScoreSummary { summary := new(ScoreSummary) @@ -49,51 +51,59 @@ func (score *Score) Summarize(opponentFouls []Foul) *ScoreSummary { return summary } - // Calculate autonomous score. - autoRuns := score.AutoRuns - if autoRuns > 3 { - autoRuns = 3 + // Calculate sandstorm bonus points. + for i, robotStartLevel := range score.RobotStartLevels { + if score.SandstormBonuses[i] { + if robotStartLevel == 1 { + summary.SandstormBonusPoints += 3 + } else if robotStartLevel == 2 { + summary.SandstormBonusPoints += 6 + } + } } - summary.AutoRunPoints = 5 * autoRuns - summary.AutoOwnershipPoints = int(2 * (score.AutoScaleOwnershipSec + score.AutoSwitchOwnershipSec)) - summary.AutoPoints = summary.AutoRunPoints + summary.AutoOwnershipPoints - // Calculate teleop score. - summary.TeleopOwnershipPoints = int(score.TeleopScaleOwnershipSec + score.TeleopScaleBoostSec + - score.TeleopSwitchOwnershipSec + score.TeleopSwitchBoostSec) - summary.OwnershipPoints = summary.AutoOwnershipPoints + summary.TeleopOwnershipPoints - forceCubes := score.ForceCubes - if forceCubes > 3 { - forceCubes = 3 + // Calculate cargo and hatch panel points. + for i, bayStatus := range score.CargoBays { + if bayStatus == BayHatchCargo { + summary.CargoPoints += 3 + if score.CargoBaysPreMatch[i] != BayHatch { + summary.HatchPanelPoints += 2 + } + } else if bayStatus == BayHatch && score.CargoBaysPreMatch[i] != BayHatch { + summary.HatchPanelPoints += 2 + } } - levitateCubes := score.LevitateCubes - if levitateCubes > 3 { - levitateCubes = 3 - } - boostCubes := score.BoostCubes - if boostCubes > 3 { - boostCubes = 3 - } - summary.VaultPoints = 5 * (forceCubes + levitateCubes + boostCubes) - climbs := score.Climbs - if climbs > 3 { - climbs = 3 - } - if score.LevitatePlayed && score.Climbs < 3 { - climbs++ - } - parks := score.Parks - if parks+climbs > 3 { - parks = 3 - climbs - } - summary.ParkClimbPoints = 5*parks + 30*climbs + summary.addRocketHalfPoints(score.RocketNearLeftBays) + summary.addRocketHalfPoints(score.RocketNearRightBays) + summary.addRocketHalfPoints(score.RocketFarLeftBays) + summary.addRocketHalfPoints(score.RocketFarRightBays) - // Calculate bonuses. - if autoRuns == 3 && score.AutoEndSwitchOwnership { - summary.AutoQuest = true + // Calculate hab climb points. + for _, level := range score.RobotEndLevels { + switch level { + case 1: + summary.HabClimbPoints += 3 + case 2: + summary.HabClimbPoints += 6 + case 3: + summary.HabClimbPoints += 12 + } } - if climbs == 3 { - summary.FaceTheBoss = true + + // Calculate bonus ranking points. + if score.isLevelComplete(0) && score.isLevelComplete(1) && score.isLevelComplete(2) { + summary.CompleteRocket = true + } else { + // Check for the opponent fouls that automatically trigger the ranking point. + for _, foul := range opponentFouls { + if foul.IsRankingPoint { + summary.CompleteRocket = true + break + } + } + } + if summary.HabClimbPoints >= HabDockingThreshold { + summary.HabDocking = true } // Calculate penalty points. @@ -101,25 +111,24 @@ func (score *Score) Summarize(opponentFouls []Foul) *ScoreSummary { summary.FoulPoints += foul.PointValue() } - summary.Score = summary.AutoRunPoints + summary.OwnershipPoints + summary.VaultPoints + summary.ParkClimbPoints + - summary.FoulPoints + summary.Score = summary.CargoPoints + summary.HatchPanelPoints + summary.HabClimbPoints + + summary.SandstormBonusPoints + summary.FoulPoints return summary } func (score *Score) Equals(other *Score) bool { - if score.AutoRuns != other.AutoRuns || score.AutoEndSwitchOwnership != other.AutoEndSwitchOwnership || - score.AutoScaleOwnershipSec != other.AutoScaleOwnershipSec || - score.AutoSwitchOwnershipSec != other.AutoSwitchOwnershipSec || - score.TeleopScaleOwnershipSec != other.TeleopScaleOwnershipSec || - score.TeleopScaleBoostSec != other.TeleopScaleBoostSec || - score.TeleopSwitchOwnershipSec != other.TeleopSwitchOwnershipSec || - score.TeleopSwitchBoostSec != other.TeleopSwitchBoostSec || - score.ForceCubes != other.ForceCubes || - score.ForceCubesPlayed != other.ForceCubesPlayed || score.LevitateCubes != other.LevitateCubes || - score.LevitatePlayed != other.LevitatePlayed || score.BoostCubes != other.BoostCubes || - score.BoostCubesPlayed != other.BoostCubesPlayed || score.Parks != other.Parks || - score.Climbs != other.Climbs || score.ElimDq != other.ElimDq || len(score.Fouls) != len(other.Fouls) { + if score.RobotStartLevels != other.RobotStartLevels || + score.SandstormBonuses != other.SandstormBonuses || + score.CargoBaysPreMatch != other.CargoBaysPreMatch || + score.CargoBays != other.CargoBays || + score.RocketNearLeftBays != other.RocketNearLeftBays || + score.RocketNearRightBays != other.RocketNearRightBays || + score.RocketFarLeftBays != other.RocketFarLeftBays || + score.RocketFarRightBays != other.RocketFarRightBays || + score.RobotEndLevels != other.RobotEndLevels || + score.ElimDq != other.ElimDq || + len(score.Fouls) != len(other.Fouls) { return false } @@ -131,3 +140,21 @@ func (score *Score) Equals(other *Score) bool { return true } + +// Calculates the cargo and hatch panel points for the given rocket half and adds them to the summary. +func (summary *ScoreSummary) addRocketHalfPoints(rocketHalf [3]BayStatus) { + for _, bayStatus := range rocketHalf { + if bayStatus == BayHatchCargo { + summary.CargoPoints += 3 + summary.HatchPanelPoints += 2 + } else if bayStatus == BayHatch { + summary.HatchPanelPoints += 2 + } + } +} + +// Returns true if the level is complete for at least one rocket. +func (score *Score) isLevelComplete(level int) bool { + return score.RocketNearLeftBays[level] == BayHatchCargo && score.RocketNearRightBays[level] == BayHatchCargo || + score.RocketFarLeftBays[level] == BayHatchCargo && score.RocketFarRightBays[level] == BayHatchCargo +} diff --git a/game/score_test.go b/game/score_test.go index 2aa9ddb..efd6fad 100644 --- a/game/score_test.go +++ b/game/score_test.go @@ -13,64 +13,43 @@ func TestScoreSummary(t *testing.T) { blueScore := TestScore2() redSummary := redScore.Summarize(blueScore.Fouls) - assert.Equal(t, 5, redSummary.AutoRunPoints) - assert.Equal(t, 17, redSummary.AutoPoints) - assert.Equal(t, 59, redSummary.OwnershipPoints) - assert.Equal(t, 15, redSummary.VaultPoints) - assert.Equal(t, 90, redSummary.ParkClimbPoints) + assert.Equal(t, 30, redSummary.CargoPoints) + assert.Equal(t, 20, redSummary.HatchPanelPoints) + assert.Equal(t, 12, redSummary.HabClimbPoints) + assert.Equal(t, 9, redSummary.SandstormBonusPoints) assert.Equal(t, 0, redSummary.FoulPoints) - assert.Equal(t, 169, redSummary.Score) - assert.Equal(t, false, redSummary.AutoQuest) - assert.Equal(t, true, redSummary.FaceTheBoss) + assert.Equal(t, 71, redSummary.Score) + assert.Equal(t, true, redSummary.CompleteRocket) + assert.Equal(t, false, redSummary.HabDocking) blueSummary := blueScore.Summarize(redScore.Fouls) - assert.Equal(t, 15, blueSummary.AutoRunPoints) - assert.Equal(t, 35, blueSummary.AutoPoints) - assert.Equal(t, 93, blueSummary.OwnershipPoints) - assert.Equal(t, 30, blueSummary.VaultPoints) - assert.Equal(t, 35, blueSummary.ParkClimbPoints) + assert.Equal(t, 12, blueSummary.CargoPoints) + assert.Equal(t, 0, blueSummary.HatchPanelPoints) + assert.Equal(t, 15, blueSummary.HabClimbPoints) + assert.Equal(t, 6, blueSummary.SandstormBonusPoints) assert.Equal(t, 55, blueSummary.FoulPoints) - assert.Equal(t, 228, blueSummary.Score) - assert.Equal(t, true, blueSummary.AutoQuest) - assert.Equal(t, false, blueSummary.FaceTheBoss) + assert.Equal(t, 88, blueSummary.Score) + assert.Equal(t, false, blueSummary.CompleteRocket) + assert.Equal(t, true, blueSummary.HabDocking) - // Test limits on fields with a natural cap. - blueScore.AutoRuns = 5 - assert.Equal(t, 15, blueScore.Summarize(redScore.Fouls).AutoRunPoints) - redScore.ForceCubes = 20 - assert.Equal(t, 30, redScore.Summarize(blueScore.Fouls).VaultPoints) - redScore.LevitatePlayed = false - redScore.Climbs = 3 - assert.Equal(t, 90, redScore.Summarize(blueScore.Fouls).ParkClimbPoints) - redScore.LevitatePlayed = true - assert.Equal(t, 90, redScore.Summarize(blueScore.Fouls).ParkClimbPoints) - redScore.Climbs = 4 - assert.Equal(t, 90, redScore.Summarize(blueScore.Fouls).ParkClimbPoints) - redScore.Climbs = 50 - assert.Equal(t, 90, redScore.Summarize(blueScore.Fouls).ParkClimbPoints) - redScore.Parks = 2 - assert.Equal(t, 90, redScore.Summarize(blueScore.Fouls).ParkClimbPoints) - redScore.Parks = 25 - assert.Equal(t, 90, redScore.Summarize(blueScore.Fouls).ParkClimbPoints) - redScore.Climbs = 0 - assert.Equal(t, 40, redScore.Summarize(blueScore.Fouls).ParkClimbPoints) + // Test rocket completion boundary conditions. + assert.Equal(t, true, redScore.Summarize(blueScore.Fouls).CompleteRocket) + redScore.RocketFarLeftBays[1] = BayHatch + assert.Equal(t, false, redScore.Summarize(blueScore.Fouls).CompleteRocket) + redScore.RocketNearLeftBays[1] = BayHatchCargo + redScore.RocketNearRightBays[1] = BayHatchCargo + assert.Equal(t, true, redScore.Summarize(blueScore.Fouls).CompleteRocket) + redScore.RocketNearLeftBays[2] = BayHatch + assert.Equal(t, false, redScore.Summarize(blueScore.Fouls).CompleteRocket) + redScore.Fouls[1].IsRankingPoint = true + assert.Equal(t, true, redScore.Summarize(redScore.Fouls).CompleteRocket) - // Test Auto Quest boundary conditions. - assert.Equal(t, true, blueScore.Summarize(redScore.Fouls).AutoQuest) - blueScore.AutoEndSwitchOwnership = false - assert.Equal(t, false, blueScore.Summarize(redScore.Fouls).AutoQuest) - blueScore.AutoEndSwitchOwnership = true - blueScore.AutoRuns = 2 - assert.Equal(t, false, blueScore.Summarize(redScore.Fouls).AutoQuest) - - // Test Face the Boss boundary conditions. - redScore.LevitatePlayed = false - assert.Equal(t, false, redScore.Summarize(blueScore.Fouls).FaceTheBoss) - redScore.Climbs = 3 - assert.Equal(t, true, redScore.Summarize(blueScore.Fouls).FaceTheBoss) - redScore.Climbs = 1 - redScore.Parks = 2 - assert.Equal(t, false, redScore.Summarize(blueScore.Fouls).FaceTheBoss) + // Test hab docking boundary conditions. + assert.Equal(t, true, blueScore.Summarize(redScore.Fouls).HabDocking) + HabDockingThreshold = 24 + assert.Equal(t, false, blueScore.Summarize(redScore.Fouls).HabDocking) + blueScore.RobotEndLevels[0] = 3 + assert.Equal(t, true, blueScore.Summarize(redScore.Fouls).HabDocking) // Test elimination disqualification. redScore.ElimDq = true @@ -90,82 +69,47 @@ func TestScoreEquals(t *testing.T) { assert.False(t, score1.Equals(score3)) assert.False(t, score3.Equals(score1)) - score2.AutoRuns += 1 + score2.RobotStartLevels[2] = 3 assert.False(t, score1.Equals(score2)) assert.False(t, score2.Equals(score1)) score2 = TestScore1() - score2.AutoEndSwitchOwnership = !score2.AutoEndSwitchOwnership + score2.SandstormBonuses[0] = false assert.False(t, score1.Equals(score2)) assert.False(t, score2.Equals(score1)) score2 = TestScore1() - score2.AutoScaleOwnershipSec += 1 + score2.CargoBaysPreMatch[7] = BayCargo assert.False(t, score1.Equals(score2)) assert.False(t, score2.Equals(score1)) score2 = TestScore1() - score2.AutoSwitchOwnershipSec += 1 + score2.CargoBays[5] = BayHatchCargo assert.False(t, score1.Equals(score2)) assert.False(t, score2.Equals(score1)) score2 = TestScore1() - score2.TeleopScaleOwnershipSec += 1 + score2.RocketNearLeftBays[0] = BayEmpty assert.False(t, score1.Equals(score2)) assert.False(t, score2.Equals(score1)) score2 = TestScore1() - score2.TeleopScaleBoostSec += 1 + score2.RocketNearRightBays[1] = BayHatchCargo assert.False(t, score1.Equals(score2)) assert.False(t, score2.Equals(score1)) score2 = TestScore1() - score2.TeleopSwitchOwnershipSec += 1 + score2.RocketFarLeftBays[2] = BayCargo assert.False(t, score1.Equals(score2)) assert.False(t, score2.Equals(score1)) score2 = TestScore1() - score2.TeleopSwitchBoostSec += 1 + score2.RocketFarRightBays[0] = BayHatch assert.False(t, score1.Equals(score2)) assert.False(t, score2.Equals(score1)) score2 = TestScore1() - score2.ForceCubes += 1 - assert.False(t, score1.Equals(score2)) - assert.False(t, score2.Equals(score1)) - - score2 = TestScore1() - score2.ForceCubesPlayed = 1 - assert.False(t, score1.Equals(score2)) - assert.False(t, score2.Equals(score1)) - - score2 = TestScore1() - score2.LevitateCubes += 1 - assert.False(t, score1.Equals(score2)) - assert.False(t, score2.Equals(score1)) - - score2 = TestScore1() - score2.LevitatePlayed = !score2.LevitatePlayed - assert.False(t, score1.Equals(score2)) - assert.False(t, score2.Equals(score1)) - - score2 = TestScore1() - score2.BoostCubes += 1 - assert.False(t, score1.Equals(score2)) - assert.False(t, score2.Equals(score1)) - - score2 = TestScore1() - score2.BoostCubesPlayed = 2 - assert.False(t, score1.Equals(score2)) - assert.False(t, score2.Equals(score1)) - - score2 = TestScore1() - score2.Parks += 1 - assert.False(t, score1.Equals(score2)) - assert.False(t, score2.Equals(score1)) - - score2 = TestScore1() - score2.Climbs += 1 + score2.RobotEndLevels[1] = 2 assert.False(t, score1.Equals(score2)) assert.False(t, score2.Equals(score1)) diff --git a/game/seesaw.go b/game/seesaw.go deleted file mode 100644 index 403f7c3..0000000 --- a/game/seesaw.go +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2018 Team 254. All Rights Reserved. -// Author: pat@patfairbank.com (Patrick Fairbank) -// -// Scoring logic for the 2018 scale and switch elements. - -package game - -import ( - "time" -) - -type Alliance int - -const ( - NeitherAlliance Alliance = iota - RedAlliance - BlueAlliance -) - -type Seesaw struct { - Kind Alliance // Red or blue indicates that it is a switch; neither indicates the scale. - NearIsRed bool - ownerships []*Ownership -} - -type Ownership struct { - seesaw *Seesaw - ownedBy Alliance - startTime time.Time - endTime *time.Time -} - -// Updates the internal timing state of the scale or switch given the current state of the sensors. Returns true if -// ownership has changed since the last cycle. -func (seesaw *Seesaw) UpdateState(state [2]bool, currentTime time.Time) bool { - ownedBy := NeitherAlliance - - // Check if there is an active force power up for this seesaw. - currentPowerUp := GetActivePowerUp(currentTime) - if currentPowerUp != nil && currentPowerUp.Effect == Force && - (seesaw.Kind == NeitherAlliance && currentPowerUp.Level >= 2 || - (seesaw.Kind == currentPowerUp.Alliance && (currentPowerUp.Level == 1 || currentPowerUp.Level == 3))) { - ownedBy = currentPowerUp.Alliance - } else { - // Determine current ownership from sensor state. - if state[0] && !state[1] && seesaw.NearIsRed || state[1] && !state[0] && !seesaw.NearIsRed { - ownedBy = RedAlliance - } else if state[0] && !state[1] && !seesaw.NearIsRed || state[1] && !state[0] && seesaw.NearIsRed { - ownedBy = BlueAlliance - } - } - - // Update data if ownership has changed since last cycle. - currentOwnership := seesaw.getCurrentOwnership() - if currentOwnership != nil && ownedBy != currentOwnership.ownedBy || - currentOwnership == nil && ownedBy != NeitherAlliance { - if currentOwnership != nil { - currentOwnership.endTime = ¤tTime - } - - if ownedBy != NeitherAlliance { - newOwnership := &Ownership{seesaw: seesaw, ownedBy: ownedBy, startTime: currentTime} - seesaw.ownerships = append(seesaw.ownerships, newOwnership) - } - return true - } - return false -} - -func (seesaw *Seesaw) GetOwnedBy() Alliance { - ownership := seesaw.getCurrentOwnership() - if ownership == nil { - return NeitherAlliance - } else { - return ownership.ownedBy - } -} - -// Returns the total seconds of ownership and boost score accumulation for the red alliance. -func (seesaw *Seesaw) GetRedSeconds(startTime, endTime time.Time) (float64, float64) { - return seesaw.getAllianceSeconds(RedAlliance, startTime, endTime) -} - -// Returns the total seconds of ownership and boost score accumulation for the blue alliance. -func (seesaw *Seesaw) GetBlueSeconds(startTime, endTime time.Time) (float64, float64) { - return seesaw.getAllianceSeconds(BlueAlliance, startTime, endTime) -} - -func (seesaw *Seesaw) getCurrentOwnership() *Ownership { - if len(seesaw.ownerships) > 0 { - lastOwnership := seesaw.ownerships[len(seesaw.ownerships)-1] - if lastOwnership.endTime == nil { - return lastOwnership - } - } - return nil -} - -func (seesaw *Seesaw) getAllianceSeconds(ownedBy Alliance, startTime, endTime time.Time) (float64, float64) { - var ownershipSec, boostSec float64 - for _, ownership := range seesaw.ownerships { - if ownership.ownedBy == ownedBy { - ownership, boost := ownership.getSeconds(startTime, endTime) - ownershipSec += ownership - boostSec += boost - } - } - return ownershipSec, boostSec -} - -// Returns the regular and boost scoring values for the ownership period, whether it is past or current. -func (ownership *Ownership) getSeconds(startTime, endTime time.Time) (float64, float64) { - var ownershipStartTime, ownershipEndTime time.Time - if ownership.startTime.Before(startTime) { - ownershipStartTime = startTime - } else { - ownershipStartTime = ownership.startTime - } - if ownership.endTime == nil || ownership.endTime.After(endTime) { - ownershipEndTime = endTime - } else { - ownershipEndTime = *ownership.endTime - } - - if ownershipStartTime.After(ownershipEndTime) { - return 0, 0 - } - ownershipSec := ownershipEndTime.Sub(ownershipStartTime).Seconds() - - // Find the boost power up applicable to this seesaw and alliance, if it exists. - var boostPowerUp *PowerUp - for _, powerUp := range powerUpUses { - if powerUp.Effect == Boost && ownership.ownedBy == powerUp.Alliance { - if ownership.seesaw.Kind == NeitherAlliance && powerUp.Level >= 2 || - ownership.seesaw.Kind != NeitherAlliance && (powerUp.Level == 1 || powerUp.Level == 3) { - boostPowerUp = powerUp - break - } - } - } - - var boostSec float64 - if boostPowerUp != nil { - // Adjust for the boost. - var boostStartTime, boostEndTime time.Time - if boostPowerUp.startTime.Before(ownershipStartTime) { - boostStartTime = ownershipStartTime - } else { - boostStartTime = boostPowerUp.startTime - } - if boostPowerUp.getEndTime().After(ownershipEndTime) { - boostEndTime = ownershipEndTime - } else { - boostEndTime = boostPowerUp.getEndTime() - } - if boostEndTime.After(boostStartTime) { - boostSec = boostEndTime.Sub(boostStartTime).Seconds() - } - } - - return ownershipSec, boostSec -} diff --git a/game/seesaw_test.go b/game/seesaw_test.go deleted file mode 100644 index db491a3..0000000 --- a/game/seesaw_test.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2018 Team 254. All Rights Reserved. -// Author: pat@patfairbank.com (Patrick Fairbank) - -package game - -import ( - "github.com/stretchr/testify/assert" - "testing" - "time" -) - -func TestOwnership(t *testing.T) { - ownership := &Ownership{nil, RedAlliance, timeAfterStart(1), nil} - assertSeconds(t, 0.0, 0.0, ownership, timeAfterStart(0), timeAfterStart(0)) - assertSeconds(t, 0.0, 0.0, ownership, timeAfterStart(0), timeAfterStart(0)) - assertSeconds(t, 0.5, 0.0, ownership, timeAfterStart(0), timeAfterStart(1.5)) - assertSeconds(t, 8.75, 0.0, ownership, timeAfterStart(0), timeAfterStart(9.75)) - - // Check with truncated start. - assertSeconds(t, 2.5, 0.0, ownership, timeAfterStart(1.5), timeAfterStart(4)) - assertSeconds(t, 5.0, 0.0, ownership, timeAfterStart(5), timeAfterStart(10)) - - // Check with end time. - endTime := timeAfterStart(13.5) - ownership.endTime = &endTime - assertSeconds(t, 12.5, 0.0, ownership, timeAfterStart(0), timeAfterStart(15)) - assertSeconds(t, 4.0, 0.0, ownership, timeAfterStart(9.5), timeAfterStart(20)) - - // Check invalid/corner cases. - assertSeconds(t, 0.0, 0.0, ownership, timeAfterStart(2), timeAfterStart(1)) -} - -func TestSecondCounting(t *testing.T) { - ResetPowerUps() - - redSwitch := &Seesaw{Kind: RedAlliance} - redSwitch.NearIsRed = true - - // Test that there is no accumulation before the start of the match. - redSwitch.UpdateState([2]bool{true, false}, timeAfterStart(-20)) - redSwitch.UpdateState([2]bool{false, false}, timeAfterStart(-12)) - redSwitch.UpdateState([2]bool{false, true}, timeAfterStart(-9)) - redSwitch.UpdateState([2]bool{false, false}, timeAfterStart(-3)) - assertRedSeconds(t, 0.0, 0.0, redSwitch, timeAfterStart(0), timeAfterStart(0)) - assertBlueSeconds(t, 0.0, 0.0, redSwitch, timeAfterStart(0), timeAfterStart(0)) - - // Test autonomous. - redSwitch.UpdateState([2]bool{true, false}, timeAfterStart(1)) - assertRedSeconds(t, 1.0, 0.0, redSwitch, timeAfterStart(0), timeAfterStart(2)) - assertRedSeconds(t, 5.5, 0.0, redSwitch, timeAfterStart(0), timeAfterStart(6.5)) - redSwitch.UpdateState([2]bool{false, false}, timeAfterStart(8.1)) - assertRedSeconds(t, 7.1, 0.0, redSwitch, timeAfterStart(0), timeAfterStart(8.5)) - assertRedSeconds(t, 7.1, 0.0, redSwitch, timeAfterStart(0), timeAfterStart(10)) - redSwitch.UpdateState([2]bool{false, true}, timeAfterStart(10)) - assertRedSeconds(t, 7.1, 0.0, redSwitch, timeAfterStart(0), timeAfterStart(13)) - redSwitch.UpdateState([2]bool{false, false}, timeAfterStart(13.5)) - redSwitch.UpdateState([2]bool{true, false}, timeAfterStart(13.9)) - assertRedSeconds(t, 8.2, 0.0, redSwitch, timeAfterStart(0), timeAfterStart(15)) - - // Test teleop. - assertRedSeconds(t, 3.0, 0.0, redSwitch, timeAfterStart(17), timeAfterStart(20)) - redSwitch.UpdateState([2]bool{false, false}, timeAfterStart(30.8)) - assertRedSeconds(t, 13.8, 0.0, redSwitch, timeAfterStart(17), timeAfterStart(34)) - redSwitch.UpdateState([2]bool{false, true}, timeAfterStart(35)) - assertRedSeconds(t, 13.8, 0.0, redSwitch, timeAfterStart(17), timeAfterEnd(-10)) - redSwitch.UpdateState([2]bool{true, false}, timeAfterEnd(-5.1)) - assertRedSeconds(t, 18.9, 0.0, redSwitch, timeAfterStart(17), timeAfterEnd(0)) - assertBlueSeconds(t, 109.9, 0.0, redSwitch, timeAfterStart(17), timeAfterEnd(0)) -} - -func TestForce(t *testing.T) { - ResetPowerUps() - - blueSwitch := &Seesaw{Kind: BlueAlliance} - blueSwitch.NearIsRed = true - scale := &Seesaw{Kind: NeitherAlliance} - scale.NearIsRed = true - - // Force switch only. - blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(0)) - scale.UpdateState([2]bool{true, false}, timeAfterStart(0)) - powerUp := &PowerUp{Alliance: BlueAlliance, Effect: Force, Level: 1} - maybeActivatePowerUp(powerUp, timeAfterStart(2.5)) - blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(2.5)) - scale.UpdateState([2]bool{true, false}, timeAfterStart(2.5)) - assertBlueSeconds(t, 2.5, 0.0, blueSwitch, timeAfterStart(0), timeAfterStart(5)) - assertBlueSeconds(t, 0.0, 0.0, scale, timeAfterStart(0), timeAfterStart(5)) - assertBlueSeconds(t, 10.0, 0.0, blueSwitch, timeAfterStart(0), timeAfterStart(12.5)) - assertBlueSeconds(t, 0.0, 0.0, scale, timeAfterStart(0), timeAfterStart(12.5)) - blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(12.5)) - scale.UpdateState([2]bool{true, false}, timeAfterStart(12.5)) - assertBlueSeconds(t, 10.0, 0.0, blueSwitch, timeAfterStart(0), timeAfterStart(15)) - assertBlueSeconds(t, 0.0, 0.0, scale, timeAfterStart(0), timeAfterStart(15)) - - // Force scale only. - powerUp = &PowerUp{Alliance: BlueAlliance, Effect: Force, Level: 2} - maybeActivatePowerUp(powerUp, timeAfterStart(20)) - blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(20)) - scale.UpdateState([2]bool{true, false}, timeAfterStart(20)) - blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(30)) - scale.UpdateState([2]bool{true, false}, timeAfterStart(30)) - assertBlueSeconds(t, 0.0, 0.0, blueSwitch, timeAfterStart(20), timeAfterStart(40)) - assertBlueSeconds(t, 10.0, 0.0, scale, timeAfterStart(20), timeAfterStart(40)) - - // Force both switch and scale. - powerUp = &PowerUp{Alliance: BlueAlliance, Effect: Force, Level: 3} - maybeActivatePowerUp(powerUp, timeAfterStart(50)) - blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(50)) - scale.UpdateState([2]bool{true, false}, timeAfterStart(50)) - blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(60)) - scale.UpdateState([2]bool{true, false}, timeAfterStart(60)) - assertBlueSeconds(t, 10.0, 0.0, blueSwitch, timeAfterStart(50), timeAfterStart(70)) - assertBlueSeconds(t, 10.0, 0.0, scale, timeAfterStart(50), timeAfterStart(70)) -} - -func TestBoost(t *testing.T) { - ResetPowerUps() - - blueSwitch := &Seesaw{Kind: BlueAlliance} - blueSwitch.NearIsRed = true - scale := &Seesaw{Kind: NeitherAlliance} - scale.NearIsRed = false - - // Test within continuous ownership period. - blueSwitch.UpdateState([2]bool{false, true}, timeAfterStart(20)) - scale.UpdateState([2]bool{true, false}, timeAfterStart(20)) - powerUp := &PowerUp{Alliance: BlueAlliance, Effect: Boost, Level: 2} - maybeActivatePowerUp(powerUp, timeAfterStart(25)) - assertBlueSeconds(t, 5.0, 0.0, scale, timeAfterStart(0), timeAfterStart(25)) - assertBlueSeconds(t, 5.5, 0.5, scale, timeAfterStart(0), timeAfterStart(25.5)) - assertBlueSeconds(t, 6.25, 1.25, scale, timeAfterStart(0), timeAfterStart(26.25)) - assertBlueSeconds(t, 10.0, 5.0, scale, timeAfterStart(0), timeAfterStart(30)) - assertBlueSeconds(t, 15.0, 10.0, scale, timeAfterStart(0), timeAfterStart(35)) - assertBlueSeconds(t, 20.0, 10.0, scale, timeAfterStart(0), timeAfterStart(40)) - assertBlueSeconds(t, 20.0, 0.0, blueSwitch, timeAfterStart(0), timeAfterStart(40)) - - // Test with no ownership at the start. - ResetPowerUps() - blueSwitch.UpdateState([2]bool{false, false}, timeAfterStart(44)) - scale.UpdateState([2]bool{false, false}, timeAfterStart(44)) - powerUp = &PowerUp{Alliance: BlueAlliance, Effect: Boost, Level: 3} - maybeActivatePowerUp(powerUp, timeAfterStart(45)) - assertBlueSeconds(t, 0.0, 0.0, blueSwitch, timeAfterStart(45), timeAfterStart(50)) - assertBlueSeconds(t, 0.0, 0.0, scale, timeAfterStart(45), timeAfterStart(50)) - blueSwitch.UpdateState([2]bool{false, true}, timeAfterStart(50)) - scale.UpdateState([2]bool{true, false}, timeAfterStart(50)) - assertBlueSeconds(t, 5.0, 5.0, blueSwitch, timeAfterStart(45), timeAfterStart(55)) - assertBlueSeconds(t, 10.0, 5.0, blueSwitch, timeAfterStart(45), timeAfterStart(60)) - assertBlueSeconds(t, 5.0, 5.0, scale, timeAfterStart(45), timeAfterStart(55)) - assertBlueSeconds(t, 10.0, 5.0, scale, timeAfterStart(45), timeAfterStart(60)) - - // Test with interrupted ownership. - ResetPowerUps() - scale.UpdateState([2]bool{false, true}, timeAfterStart(65)) - assertRedSeconds(t, 5.0, 0.0, scale, timeAfterStart(65), timeAfterStart(70)) - powerUp = &PowerUp{Alliance: RedAlliance, Effect: Boost, Level: 2} - maybeActivatePowerUp(powerUp, timeAfterStart(70)) - scale.UpdateState([2]bool{false, false}, timeAfterStart(72.5)) - assertRedSeconds(t, 7.5, 2.5, scale, timeAfterStart(65), timeAfterStart(72.5)) - assertRedSeconds(t, 7.5, 2.5, scale, timeAfterStart(65), timeAfterStart(77.5)) - scale.UpdateState([2]bool{false, true}, timeAfterStart(77.5)) - assertRedSeconds(t, 10.0, 5.0, scale, timeAfterStart(65), timeAfterStart(80)) - assertRedSeconds(t, 15.0, 5.0, scale, timeAfterStart(65), timeAfterStart(85)) - - // Test with just the switch. - blueSwitch.UpdateState([2]bool{false, true}, timeAfterStart(100)) - scale.UpdateState([2]bool{true, false}, timeAfterStart(100)) - powerUp = &PowerUp{Alliance: BlueAlliance, Effect: Boost, Level: 1} - maybeActivatePowerUp(powerUp, timeAfterStart(100)) - assertBlueSeconds(t, 10.0, 10.0, blueSwitch, timeAfterStart(100), timeAfterStart(110)) - assertBlueSeconds(t, 10.0, 0.0, scale, timeAfterStart(100), timeAfterStart(110)) -} - -func assertSeconds(t *testing.T, expectedOwnership, expectedBoost float64, ownership *Ownership, startTime, - endTime time.Time) { - actualOwnership, actualBoost := ownership.getSeconds(startTime, endTime) - assert.Equal(t, expectedOwnership, actualOwnership) - assert.Equal(t, expectedBoost, actualBoost) -} - -func assertRedSeconds(t *testing.T, expectedOwnership, expectedBoost float64, seesaw *Seesaw, startTime, - endTime time.Time) { - actualOwnership, actualBoost := seesaw.GetRedSeconds(startTime, endTime) - assert.Equal(t, expectedOwnership, actualOwnership) - assert.Equal(t, expectedBoost, actualBoost) -} - -func assertBlueSeconds(t *testing.T, expectedOwnership, expectedBoost float64, seesaw *Seesaw, startTime, - endTime time.Time) { - actualOwnership, actualBoost := seesaw.GetBlueSeconds(startTime, endTime) - assert.Equal(t, expectedOwnership, actualOwnership) - assert.Equal(t, expectedBoost, actualBoost) -} diff --git a/game/test_helpers.go b/game/test_helpers.go index 77e581e..ffeaabe 100644 --- a/game/test_helpers.go +++ b/game/test_helpers.go @@ -6,13 +6,44 @@ package game func TestScore1() *Score { - fouls := []Foul{{Rule{"G22", false, ""}, 25, 25.2}, {Rule{"G18", true, ""}, 25, 150}, - {Rule{"G20", true, ""}, 1868, 0}} - return &Score{1, 1.5, 4.5, true, 25.4, 0, 21.6, 0, 0, 0, 3, true, 0, 0, 2, 0, fouls, false} + fouls := []Foul{ + {Rule{"G18", true, false, ""}, 25, 150}, + {Rule{"G20", true, false, ""}, 1868, 0}, + {Rule{"G22", false, false, ""}, 25, 25.2}, + } + return &Score{ + RobotStartLevels: [3]int{2, 1, 2}, + SandstormBonuses: [3]bool{true, true, false}, + CargoBaysPreMatch: [8]BayStatus{BayHatch, BayEmpty, BayEmpty, BayCargo, BayHatch, BayCargo, BayHatch, + BayHatch}, + CargoBays: [8]BayStatus{BayHatchCargo, BayHatch, BayEmpty, BayHatchCargo, BayHatchCargo, BayEmpty, + BayHatch, BayHatchCargo}, + RocketNearLeftBays: [3]BayStatus{BayHatchCargo, BayEmpty, BayHatchCargo}, + RocketNearRightBays: [3]BayStatus{BayHatchCargo, BayHatch, BayHatchCargo}, + RocketFarLeftBays: [3]BayStatus{BayEmpty, BayHatchCargo, BayHatch}, + RocketFarRightBays: [3]BayStatus{BayEmpty, BayHatchCargo, BayEmpty}, + RobotEndLevels: [3]int{0, 0, 3}, + Fouls: fouls, + ElimDq: false, + } } func TestScore2() *Score { - return &Score{3, 4, 6, true, 33, 10, 20, 10, 3, 3, 0, false, 3, 3, 1, 1, []Foul{}, false} + return &Score{ + RobotStartLevels: [3]int{1, 2, 1}, + SandstormBonuses: [3]bool{false, true, false}, + CargoBaysPreMatch: [8]BayStatus{BayEmpty, BayEmpty, BayHatch, BayHatch, BayHatch, BayHatch, BayHatch, + BayHatch}, + CargoBays: [8]BayStatus{BayEmpty, BayEmpty, BayHatchCargo, BayHatchCargo, BayHatchCargo, BayHatch, BayHatch, + BayHatchCargo}, + RocketNearLeftBays: [3]BayStatus{BayEmpty, BayEmpty, BayEmpty}, + RocketNearRightBays: [3]BayStatus{BayEmpty, BayEmpty, BayEmpty}, + RocketFarLeftBays: [3]BayStatus{BayEmpty, BayEmpty, BayEmpty}, + RocketFarRightBays: [3]BayStatus{BayEmpty, BayEmpty, BayEmpty}, + RobotEndLevels: [3]int{1, 2, 2}, + Fouls: []Foul{}, + ElimDq: false, + } } func TestRanking1() *Ranking { diff --git a/game/vault.go b/game/vault.go deleted file mode 100644 index ca3efcf..0000000 --- a/game/vault.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2018 Team 254. All Rights Reserved. -// Author: pat@patfairbank.com (Patrick Fairbank) -// -// Scoring logic for the 2018 vault element. - -package game - -import ( - "time" -) - -type Vault struct { - Alliance - ForceCubes int - ForceCubesPlayed int - LevitateCubes int - LevitatePlayed bool - BoostCubes int - BoostCubesPlayed int - ForcePowerUp *PowerUp - BoostPowerUp *PowerUp - newlyPlayedPowerUp string -} - -// Updates the state of the vault given the state of the individual power cube sensors. -func (vault *Vault) UpdateCubes(forceDistance, levitateDistance, boostDistance uint16) { - vault.ForceCubes = countCubes(forceDistance) - vault.LevitateCubes = countCubes(levitateDistance) - vault.BoostCubes = countCubes(boostDistance) -} - -// Updates the state of the vault given the state of the power up buttons. -func (vault *Vault) UpdateButtons(forceButton, levitateButton, boostButton bool, currentTime time.Time) { - if levitateButton && vault.LevitateCubes == 3 && !vault.LevitatePlayed { - vault.LevitatePlayed = true - vault.newlyPlayedPowerUp = "levitate" - } - - if forceButton && vault.ForceCubes > 0 && vault.ForcePowerUp == nil { - vault.ForcePowerUp = maybeActivatePowerUp(&PowerUp{Effect: Force, Alliance: vault.Alliance, - Level: vault.ForceCubes}, currentTime) - if vault.ForcePowerUp != nil { - vault.ForceCubesPlayed = vault.ForceCubes - vault.newlyPlayedPowerUp = "force" - } - } - - if boostButton && vault.BoostCubes > 0 && vault.BoostPowerUp == nil { - vault.BoostPowerUp = maybeActivatePowerUp(&PowerUp{Effect: Boost, Alliance: vault.Alliance, - Level: vault.BoostCubes}, currentTime) - if vault.BoostPowerUp != nil { - vault.BoostCubesPlayed = vault.BoostCubes - vault.newlyPlayedPowerUp = "boost" - } - } -} - -// Returns the name of the newly-played power up if there is one, or an empty string otherwise, and resets the state. -func (vault *Vault) CheckForNewlyPlayedPowerUp() string { - powerUp := vault.newlyPlayedPowerUp - vault.newlyPlayedPowerUp = "" - return powerUp -} - -func countCubes(distance uint16) int { - // Ed Jordan's measurements: - // Empty 125 - // 1 Short 98 - // 1 Tall 92 - // 2 Short 68 - // 2 Tall 58 - // 3 Short 43 - // 3 Tall 26 - if distance <= 15 { - // The sensor is probably disconnected or obstructed; this is too tall to be a cube stack. - return 0 - } - if distance <= 50 { - return 3 - } - if distance <= 75 { - return 2 - } - if distance <= 105 { - return 1 - } - return 0 -} diff --git a/game/vault_test.go b/game/vault_test.go deleted file mode 100644 index 17f399d..0000000 --- a/game/vault_test.go +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2018 Team 254. All Rights Reserved. -// Author: pat@patfairbank.com (Patrick Fairbank) - -package game - -import ( - "github.com/stretchr/testify/assert" - "testing" - "time" -) - -const ( - zeroCubeDistance = 125 - oneCubeDistance = 98 - twoCubeDistance = 58 - threeCubeDistance = 43 -) - -func TestVaultNumCubes(t *testing.T) { - vault := Vault{} - assert.Equal(t, 0, vault.ForceCubes) - assert.Equal(t, 0, vault.LevitateCubes) - assert.Equal(t, 0, vault.BoostCubes) - - vault.UpdateCubes(oneCubeDistance, zeroCubeDistance, zeroCubeDistance) - assert.Equal(t, 1, vault.ForceCubes) - assert.Equal(t, 0, vault.LevitateCubes) - assert.Equal(t, 0, vault.BoostCubes) - - vault.UpdateCubes(zeroCubeDistance, oneCubeDistance, oneCubeDistance) - assert.Equal(t, 0, vault.ForceCubes) - assert.Equal(t, 1, vault.LevitateCubes) - assert.Equal(t, 1, vault.BoostCubes) - - vault.UpdateCubes(zeroCubeDistance, zeroCubeDistance, twoCubeDistance) - assert.Equal(t, 0, vault.ForceCubes) - assert.Equal(t, 0, vault.LevitateCubes) - assert.Equal(t, 2, vault.BoostCubes) - - vault.UpdateCubes(twoCubeDistance, twoCubeDistance, threeCubeDistance) - assert.Equal(t, 2, vault.ForceCubes) - assert.Equal(t, 2, vault.LevitateCubes) - assert.Equal(t, 3, vault.BoostCubes) - - vault.UpdateCubes(threeCubeDistance, threeCubeDistance, threeCubeDistance) - assert.Equal(t, 3, vault.ForceCubes) - assert.Equal(t, 3, vault.LevitateCubes) - assert.Equal(t, 3, vault.BoostCubes) - - assert.Equal(t, 0, vault.ForceCubesPlayed) - assert.Equal(t, 0, vault.BoostCubesPlayed) -} - -func TestVaultLevitate(t *testing.T) { - vault := Vault{} - - vault.UpdateCubes(zeroCubeDistance, zeroCubeDistance, zeroCubeDistance) - vault.UpdateButtons(false, true, false, time.Now()) - assert.False(t, vault.LevitatePlayed) - - vault.UpdateCubes(zeroCubeDistance, oneCubeDistance, zeroCubeDistance) - vault.UpdateButtons(false, true, false, time.Now()) - assert.False(t, vault.LevitatePlayed) - - vault.UpdateCubes(zeroCubeDistance, twoCubeDistance, zeroCubeDistance) - vault.UpdateButtons(false, true, false, time.Now()) - assert.False(t, vault.LevitatePlayed) - - vault.UpdateCubes(zeroCubeDistance, threeCubeDistance, zeroCubeDistance) - vault.UpdateButtons(true, false, true, time.Now()) - assert.False(t, vault.LevitatePlayed) - - vault.UpdateCubes(zeroCubeDistance, threeCubeDistance, zeroCubeDistance) - vault.UpdateButtons(false, true, false, time.Now()) - assert.True(t, vault.LevitatePlayed) - - vault.UpdateCubes(zeroCubeDistance, threeCubeDistance, zeroCubeDistance) - vault.UpdateButtons(false, false, false, time.Now()) - assert.True(t, vault.LevitatePlayed) -} - -func TestVaultForce(t *testing.T) { - vault := Vault{Alliance: BlueAlliance} - ResetPowerUps() - - vault.UpdateCubes(zeroCubeDistance, zeroCubeDistance, zeroCubeDistance) - vault.UpdateButtons(true, false, false, time.Now()) - assert.Nil(t, vault.ForcePowerUp) - - vault.UpdateCubes(threeCubeDistance, zeroCubeDistance, zeroCubeDistance) - vault.UpdateButtons(false, true, true, time.Now()) - assert.Nil(t, vault.ForcePowerUp) - - // Activation with one cube. - vault.UpdateCubes(oneCubeDistance, zeroCubeDistance, zeroCubeDistance) - vault.UpdateButtons(true, false, false, time.Now()) - if assert.NotNil(t, vault.ForcePowerUp) { - assert.Equal(t, BlueAlliance, vault.ForcePowerUp.Alliance) - assert.Equal(t, Force, vault.ForcePowerUp.Effect) - assert.Equal(t, 1, vault.ForcePowerUp.Level) - } - vault.UpdateCubes(zeroCubeDistance, zeroCubeDistance, zeroCubeDistance) - assert.Equal(t, 1, vault.ForceCubesPlayed) - - // Activation with two cubes. - vault = Vault{Alliance: RedAlliance} - ResetPowerUps() - vault.UpdateCubes(twoCubeDistance, zeroCubeDistance, zeroCubeDistance) - vault.UpdateButtons(true, false, false, time.Now()) - if assert.NotNil(t, vault.ForcePowerUp) { - assert.Equal(t, RedAlliance, vault.ForcePowerUp.Alliance) - assert.Equal(t, Force, vault.ForcePowerUp.Effect) - assert.Equal(t, 2, vault.ForcePowerUp.Level) - assert.Equal(t, 2, vault.ForceCubesPlayed) - } - vault.UpdateCubes(threeCubeDistance, zeroCubeDistance, zeroCubeDistance) - assert.Equal(t, 2, vault.ForceCubesPlayed) - - // Activation with three cubes. - vault = Vault{Alliance: BlueAlliance} - ResetPowerUps() - vault.UpdateCubes(threeCubeDistance, zeroCubeDistance, zeroCubeDistance) - vault.UpdateButtons(true, false, false, time.Now()) - assert.NotNil(t, vault.ForcePowerUp) - if assert.NotNil(t, vault.ForcePowerUp) { - assert.Equal(t, BlueAlliance, vault.ForcePowerUp.Alliance) - assert.Equal(t, Force, vault.ForcePowerUp.Effect) - assert.Equal(t, 3, vault.ForcePowerUp.Level) - } - vault.UpdateCubes(zeroCubeDistance, zeroCubeDistance, zeroCubeDistance) - assert.Equal(t, 3, vault.ForceCubesPlayed) - - vault.UpdateCubes(threeCubeDistance, zeroCubeDistance, zeroCubeDistance) - vault.UpdateButtons(false, false, false, time.Now()) - assert.NotNil(t, vault.ForcePowerUp) -} - -func TestVaultBoost(t *testing.T) { - vault := Vault{Alliance: BlueAlliance} - ResetPowerUps() - - vault.UpdateCubes(zeroCubeDistance, zeroCubeDistance, zeroCubeDistance) - vault.UpdateButtons(false, false, true, time.Now()) - assert.Nil(t, vault.BoostPowerUp) - - vault.UpdateCubes(zeroCubeDistance, zeroCubeDistance, threeCubeDistance) - vault.UpdateButtons(true, true, false, time.Now()) - assert.Nil(t, vault.BoostPowerUp) - - // Activation with one cube. - vault.UpdateCubes(zeroCubeDistance, zeroCubeDistance, oneCubeDistance) - vault.UpdateButtons(false, false, true, time.Now()) - if assert.NotNil(t, vault.BoostPowerUp) { - assert.Equal(t, BlueAlliance, vault.BoostPowerUp.Alliance) - assert.Equal(t, Boost, vault.BoostPowerUp.Effect) - assert.Equal(t, 1, vault.BoostPowerUp.Level) - } - vault.UpdateCubes(zeroCubeDistance, twoCubeDistance, zeroCubeDistance) - assert.Equal(t, 1, vault.BoostCubesPlayed) - - // Activation with two cubes. - vault = Vault{Alliance: RedAlliance} - ResetPowerUps() - vault.UpdateCubes(zeroCubeDistance, zeroCubeDistance, twoCubeDistance) - vault.UpdateButtons(false, false, true, time.Now()) - if assert.NotNil(t, vault.BoostPowerUp) { - assert.Equal(t, RedAlliance, vault.BoostPowerUp.Alliance) - assert.Equal(t, Boost, vault.BoostPowerUp.Effect) - assert.Equal(t, 2, vault.BoostPowerUp.Level) - } - vault.UpdateCubes(zeroCubeDistance, zeroCubeDistance, zeroCubeDistance) - assert.Equal(t, 2, vault.BoostCubesPlayed) - - // Activation with three cubes. - vault = Vault{Alliance: BlueAlliance} - ResetPowerUps() - vault.UpdateCubes(zeroCubeDistance, zeroCubeDistance, threeCubeDistance) - vault.UpdateButtons(false, false, true, time.Now()) - assert.NotNil(t, vault.BoostPowerUp) - if assert.NotNil(t, vault.BoostPowerUp) { - assert.Equal(t, BlueAlliance, vault.BoostPowerUp.Alliance) - assert.Equal(t, Boost, vault.BoostPowerUp.Effect) - assert.Equal(t, 3, vault.BoostPowerUp.Level) - } - vault.UpdateCubes(zeroCubeDistance, zeroCubeDistance, zeroCubeDistance) - assert.Equal(t, 3, vault.BoostCubesPlayed) - - vault.UpdateCubes(zeroCubeDistance, zeroCubeDistance, threeCubeDistance) - vault.UpdateButtons(false, false, false, time.Now()) - assert.NotNil(t, vault.BoostPowerUp) -} - -func TestVaultMultipleActivations(t *testing.T) { - redVault := Vault{Alliance: RedAlliance} - redVault.UpdateCubes(oneCubeDistance, threeCubeDistance, oneCubeDistance) - blueVault := Vault{Alliance: BlueAlliance} - blueVault.UpdateCubes(oneCubeDistance, threeCubeDistance, oneCubeDistance) - ResetPowerUps() - - redVault.UpdateButtons(true, false, false, timeAfterStart(0)) - redVault.UpdateButtons(false, false, false, timeAfterStart(1)) - if assert.NotNil(t, redVault.ForcePowerUp) { - assert.Equal(t, Active, redVault.ForcePowerUp.GetState(timeAfterStart(0.5))) - } - assert.Equal(t, "force", redVault.CheckForNewlyPlayedPowerUp()) - assert.Equal(t, "", redVault.CheckForNewlyPlayedPowerUp()) - - redVault.UpdateButtons(false, true, false, timeAfterStart(2)) - redVault.UpdateButtons(false, false, false, timeAfterStart(3)) - assert.True(t, redVault.LevitatePlayed) - assert.Equal(t, "levitate", redVault.CheckForNewlyPlayedPowerUp()) - assert.Equal(t, "", redVault.CheckForNewlyPlayedPowerUp()) - - blueVault.UpdateButtons(false, false, true, timeAfterStart(4)) - blueVault.UpdateButtons(false, false, false, timeAfterStart(5)) - if assert.NotNil(t, blueVault.BoostPowerUp) { - assert.Equal(t, Queued, blueVault.BoostPowerUp.GetState(timeAfterStart(4.5))) - } - assert.Equal(t, "boost", blueVault.CheckForNewlyPlayedPowerUp()) - assert.Equal(t, "", blueVault.CheckForNewlyPlayedPowerUp()) - assert.Equal(t, Expired, redVault.ForcePowerUp.GetState(timeAfterStart(11))) - assert.Equal(t, Active, blueVault.BoostPowerUp.GetState(timeAfterStart(11))) - assert.Equal(t, Expired, blueVault.BoostPowerUp.GetState(timeAfterStart(21))) - - redVault.UpdateButtons(false, false, true, timeAfterStart(25)) - redVault.UpdateButtons(false, false, false, timeAfterStart(26)) - if assert.NotNil(t, redVault.BoostPowerUp) { - assert.Equal(t, Active, redVault.BoostPowerUp.GetState(timeAfterStart(25.5))) - } - assert.Equal(t, "boost", redVault.CheckForNewlyPlayedPowerUp()) - assert.Equal(t, "", redVault.CheckForNewlyPlayedPowerUp()) -} diff --git a/model/event_settings.go b/model/event_settings.go index 456400d..eeb1a4a 100644 --- a/model/event_settings.go +++ b/model/event_settings.go @@ -28,6 +28,7 @@ type EventSettings struct { PlcAddress string AdminPassword string ReaderPassword string + HabDockingThreshold int } const eventSettingsId = 0 @@ -45,6 +46,7 @@ func (database *Database) GetEventSettings() (*EventSettings, error) { eventSettings.ApTeamChannel = 157 eventSettings.ApAdminChannel = 0 eventSettings.ApAdminWpaKey = "1234Five" + eventSettings.HabDockingThreshold = 15 err = database.eventSettingsMap.Insert(eventSettings) if err != nil { diff --git a/model/event_settings_test.go b/model/event_settings_test.go index c5990cf..06376c1 100644 --- a/model/event_settings_test.go +++ b/model/event_settings_test.go @@ -15,7 +15,7 @@ func TestEventSettingsReadWrite(t *testing.T) { assert.Nil(t, err) assert.Equal(t, EventSettings{Id: 0, Name: "Untitled Event", NumElimAlliances: 8, SelectionRound2Order: "L", SelectionRound3Order: "", TBADownloadEnabled: true, ApTeamChannel: 157, ApAdminChannel: 0, - ApAdminWpaKey: "1234Five"}, *eventSettings) + ApAdminWpaKey: "1234Five", HabDockingThreshold: 15}, *eventSettings) eventSettings.Name = "Chezy Champs" eventSettings.NumElimAlliances = 6 diff --git a/model/match_result_test.go b/model/match_result_test.go index cdc2105..872f3dc 100644 --- a/model/match_result_test.go +++ b/model/match_result_test.go @@ -25,7 +25,7 @@ func TestMatchResultCrud(t *testing.T) { assert.Nil(t, err) assert.Equal(t, matchResult, matchResult2) - matchResult.BlueScore.AutoRuns = 12 + matchResult.BlueScore.RobotEndLevels = [3]int{3, 3, 3} db.SaveMatchResult(matchResult) matchResult2, err = db.GetMatchResultForMatch(254) assert.Nil(t, err) diff --git a/partner/tba.go b/partner/tba.go index 1898aed..b4fc4a5 100644 --- a/partner/tba.go +++ b/partner/tba.go @@ -11,7 +11,6 @@ import ( "encoding/base64" "encoding/json" "fmt" - "github.com/Team254/cheesy-arena/game" "github.com/Team254/cheesy-arena/model" "io/ioutil" "net/http" @@ -360,8 +359,8 @@ func (client *TbaClient) PublishRankings(database *model.Database) error { tbaRankings := make([]TbaRanking, len(rankings)) for i, ranking := range rankings { tbaRankings[i] = TbaRanking{getTbaTeam(ranking.TeamId), ranking.Rank, - float32(ranking.RankingPoints) / float32(ranking.Played), ranking.ParkClimbPoints, ranking.AutoPoints, - ranking.OwnershipPoints, ranking.VaultPoints, + float32(ranking.RankingPoints) / float32(ranking.Played), ranking.CargoPoints, ranking.HatchPanelPoints, + ranking.HabClimbPoints, ranking.SandstormBonusPoints, fmt.Sprintf("%d-%d-%d", ranking.Wins, ranking.Losses, ranking.Ties), ranking.Disqualifications, ranking.Played} } @@ -504,50 +503,53 @@ func createTbaAlliance(teamIds [3]int, surrogates [3]bool, score *int, cards map func createTbaScoringBreakdown(match *model.Match, matchResult *model.MatchResult, alliance string) *TbaScoreBreakdown { var breakdown TbaScoreBreakdown - var score *game.Score - var scoreSummary, opponentScoreSummary *game.ScoreSummary - if alliance == "red" { - score = matchResult.RedScore - scoreSummary = matchResult.RedScoreSummary() - opponentScoreSummary = matchResult.BlueScoreSummary() - } else { - score = matchResult.BlueScore - scoreSummary = matchResult.BlueScoreSummary() - opponentScoreSummary = matchResult.RedScoreSummary() - } + // TODO(pat): Update for 2019. + /* + var score *game.Score + var scoreSummary, opponentScoreSummary *game.ScoreSummary + if alliance == "red" { + score = matchResult.RedScore + scoreSummary = matchResult.RedScoreSummary() + opponentScoreSummary = matchResult.BlueScoreSummary() + } else { + score = matchResult.BlueScore + scoreSummary = matchResult.BlueScoreSummary() + opponentScoreSummary = matchResult.RedScoreSummary() + } - breakdown.AutoRunPoints = 5 * score.AutoRuns - breakdown.AutoScaleOwnershipSec = int(score.AutoScaleOwnershipSec) - breakdown.AutoSwitchOwnershipSec = int(score.AutoSwitchOwnershipSec) - breakdown.AutoOwnershipPoints = scoreSummary.AutoOwnershipPoints - breakdown.AutoPoints = scoreSummary.AutoPoints - breakdown.TeleopScaleOwnershipSec = int(score.TeleopScaleOwnershipSec) - breakdown.TeleopScaleBoostSec = int(score.TeleopScaleBoostSec) - breakdown.TeleopSwitchOwnershipSec = int(score.TeleopSwitchOwnershipSec) - breakdown.TeleopSwitchBoostSec = int(score.TeleopSwitchBoostSec) - breakdown.TeleopOwnershipPoints = scoreSummary.TeleopOwnershipPoints - breakdown.VaultForceTotal = score.ForceCubes - breakdown.VaultForcePlayed = score.ForceCubesPlayed - breakdown.VaultLevitateTotal = score.LevitateCubes - if score.LevitatePlayed { - breakdown.VaultLevitatePlayed = score.LevitateCubes - } - breakdown.VaultBoostTotal = score.BoostCubes - breakdown.VaultBoostPlayed = score.BoostCubesPlayed - breakdown.VaultPoints = scoreSummary.VaultPoints - breakdown.EndgamePoints = scoreSummary.ParkClimbPoints - breakdown.TeleopPoints = scoreSummary.Score - scoreSummary.AutoPoints - scoreSummary.FoulPoints - breakdown.AutoQuestRankingPoint = scoreSummary.AutoQuest - breakdown.FaceTheBossRankingPoint = scoreSummary.FaceTheBoss - breakdown.FoulPoints = scoreSummary.FoulPoints - breakdown.TotalPoints = scoreSummary.Score - if match.ShouldUpdateRankings() { - // Calculate and set the ranking points for the match. - var ranking game.Ranking - ranking.AddScoreSummary(scoreSummary, opponentScoreSummary, false) - breakdown.RP = ranking.RankingPoints - } - breakdown.TbaGameData = match.GameSpecificData + breakdown.AutoRunPoints = 5 * score.AutoRuns + breakdown.AutoScaleOwnershipSec = int(score.AutoScaleOwnershipSec) + breakdown.AutoSwitchOwnershipSec = int(score.AutoSwitchOwnershipSec) + breakdown.AutoOwnershipPoints = scoreSummary.AutoOwnershipPoints + breakdown.AutoPoints = scoreSummary.AutoPoints + breakdown.TeleopScaleOwnershipSec = int(score.TeleopScaleOwnershipSec) + breakdown.TeleopScaleBoostSec = int(score.TeleopScaleBoostSec) + breakdown.TeleopSwitchOwnershipSec = int(score.TeleopSwitchOwnershipSec) + breakdown.TeleopSwitchBoostSec = int(score.TeleopSwitchBoostSec) + breakdown.TeleopOwnershipPoints = scoreSummary.TeleopOwnershipPoints + breakdown.VaultForceTotal = score.ForceCubes + breakdown.VaultForcePlayed = score.ForceCubesPlayed + breakdown.VaultLevitateTotal = score.LevitateCubes + if score.LevitatePlayed { + breakdown.VaultLevitatePlayed = score.LevitateCubes + } + breakdown.VaultBoostTotal = score.BoostCubes + breakdown.VaultBoostPlayed = score.BoostCubesPlayed + breakdown.VaultPoints = scoreSummary.VaultPoints + breakdown.EndgamePoints = scoreSummary.ParkClimbPoints + breakdown.TeleopPoints = scoreSummary.Score - scoreSummary.AutoPoints - scoreSummary.FoulPoints + breakdown.AutoQuestRankingPoint = scoreSummary.AutoQuest + breakdown.FaceTheBossRankingPoint = scoreSummary.FaceTheBoss + breakdown.FoulPoints = scoreSummary.FoulPoints + breakdown.TotalPoints = scoreSummary.Score + if match.ShouldUpdateRankings() { + // Calculate and set the ranking points for the match. + var ranking game.Ranking + ranking.AddScoreSummary(scoreSummary, opponentScoreSummary, false) + breakdown.RP = ranking.RankingPoints + } + breakdown.TbaGameData = match.GameSpecificData + */ return &breakdown } diff --git a/templates/rankings.csv b/templates/rankings.csv index 8ccfda1..ca1ecf8 100644 --- a/templates/rankings.csv +++ b/templates/rankings.csv @@ -1,3 +1,3 @@ -Rank,TeamId,RankingPoints,ParkClimbPoints,AutoPoints,OwnershipPoints,VaultPoints,Wins,Losses,Ties,Disqualifications,Played -{{range $ranking := .}}{{$ranking.Rank}},{{$ranking.TeamId}},{{$ranking.RankingPoints}},{{$ranking.ParkClimbPoints}},{{$ranking.AutoPoints}},{{$ranking.OwnershipPoints}},{{$ranking.VaultPoints}},{{$ranking.Wins}},{{$ranking.Losses}},{{$ranking.Ties}},{{$ranking.Disqualifications}},{{$ranking.Played}} +Rank,TeamId,RankingPoints,CargoPoints,HatchPanelPoints,HabClimbPoints,SandstormBonusPoints,Wins,Losses,Ties,Disqualifications,Played +{{range $ranking := .}}{{$ranking.Rank}},{{$ranking.TeamId}},{{$ranking.RankingPoints}},{{$ranking.CargoPoints}},{{$ranking.HatchPanelPoints}},{{$ranking.HabClimbPoints}},{{$ranking.SandstormBonusPoints}},{{$ranking.Wins}},{{$ranking.Losses}},{{$ranking.Ties}},{{$ranking.Disqualifications}},{{$ranking.Played}} {{end}} diff --git a/templates/setup_settings.html b/templates/setup_settings.html index 5ef1fee..9a46be2 100644 --- a/templates/setup_settings.html +++ b/templates/setup_settings.html @@ -211,6 +211,15 @@ +
+ Game-Specific +
+ +
+ +
+
+
diff --git a/web/match_play_test.go b/web/match_play_test.go index bb37e63..6075fc0 100644 --- a/web/match_play_test.go +++ b/web/match_play_test.go @@ -133,7 +133,7 @@ func TestCommitMatch(t *testing.T) { web.arena.Database.CreateMatch(match) matchResult = model.NewMatchResult() matchResult.MatchId = match.Id - matchResult.BlueScore = &game.Score{AutoRuns: 2} + matchResult.BlueScore = &game.Score{RobotEndLevels: [3]int{3, 3, 3}} err = web.commitMatchScore(match, matchResult, false) assert.Nil(t, err) assert.Equal(t, 1, matchResult.PlayNumber) @@ -142,7 +142,7 @@ func TestCommitMatch(t *testing.T) { matchResult = model.NewMatchResult() matchResult.MatchId = match.Id - matchResult.RedScore = &game.Score{AutoRuns: 1} + matchResult.RedScore = &game.Score{RobotEndLevels: [3]int{2, 2, 2}} err = web.commitMatchScore(match, matchResult, false) assert.Nil(t, err) assert.Equal(t, 2, matchResult.PlayNumber) @@ -174,8 +174,13 @@ func TestCommitEliminationTie(t *testing.T) { match := &model.Match{Id: 0, Type: "qualification", Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, Blue3: 6} web.arena.Database.CreateMatch(match) - matchResult := &model.MatchResult{MatchId: match.Id, RedScore: &game.Score{ForceCubes: 1, Fouls: []game.Foul{{}}}, - BlueScore: &game.Score{}} + matchResult := &model.MatchResult{ + MatchId: match.Id, + RedScore: &game.Score{ + RocketFarRightBays: [3]game.BayStatus{game.BayHatchCargo, game.BayEmpty, game.BayEmpty}, + Fouls: []game.Foul{{}}}, + BlueScore: &game.Score{}, + } err := web.commitMatchScore(match, matchResult, false) assert.Nil(t, err) match, _ = web.arena.Database.GetMatchById(1) @@ -301,12 +306,12 @@ func TestMatchPlayWebsocketCommands(t *testing.T) { readWebsocketType(t, ws, "audienceDisplayMode") readWebsocketType(t, ws, "allianceStationDisplayMode") assert.Equal(t, field.PostMatch, web.arena.MatchState) - web.arena.RedRealtimeScore.CurrentScore.AutoRuns = 1 - web.arena.BlueRealtimeScore.CurrentScore.BoostCubes = 2 + web.arena.RedRealtimeScore.CurrentScore.RobotEndLevels = [3]int{1, 2, 3} + web.arena.BlueRealtimeScore.CurrentScore.SandstormBonuses = [3]bool{true, false, true} ws.Write("commitResults", nil) readWebsocketMultiple(t, ws, 3) // reload, realtimeScore, setAllianceStationDisplay - assert.Equal(t, 1, web.arena.SavedMatchResult.RedScore.AutoRuns) - assert.Equal(t, 2, web.arena.SavedMatchResult.BlueScore.BoostCubes) + assert.Equal(t, [3]int{1, 2, 3}, web.arena.SavedMatchResult.RedScore.RobotEndLevels) + assert.Equal(t, [3]bool{true, false, true}, web.arena.SavedMatchResult.BlueScore.SandstormBonuses) assert.Equal(t, field.PreMatch, web.arena.MatchState) ws.Write("discardResults", nil) readWebsocketMultiple(t, ws, 3) // reload, realtimeScore, setAllianceStationDisplay diff --git a/web/match_review_test.go b/web/match_review_test.go index c9b6bc5..322fa99 100644 --- a/web/match_review_test.go +++ b/web/match_review_test.go @@ -49,8 +49,8 @@ func TestMatchReviewEditExistingResult(t *testing.T) { recorder := web.getHttpResponse("/match_review") assert.Equal(t, 200, recorder.Code) assert.Contains(t, recorder.Body.String(), "QF4-3") - assert.Contains(t, recorder.Body.String(), "169") // The red score - assert.Contains(t, recorder.Body.String(), "228") // The blue score + assert.Contains(t, recorder.Body.String(), "71") // The red score + assert.Contains(t, recorder.Body.String(), "88") // The blue score // Check response for non-existent match. recorder = web.getHttpResponse(fmt.Sprintf("/match_review/%d/edit", 12345)) @@ -67,12 +67,13 @@ func TestMatchReviewEditExistingResult(t *testing.T) { recorder = web.postHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id), postBody) assert.Equal(t, 303, recorder.Code) + // TODO(pat): Update for 2019. // Check for the updated scores back on the match list page. recorder = web.getHttpResponse("/match_review") assert.Equal(t, 200, recorder.Code) assert.Contains(t, recorder.Body.String(), "QF4-3") - assert.Contains(t, recorder.Body.String(), "20") // The red score - assert.Contains(t, recorder.Body.String(), "15") // The blue score + assert.Contains(t, recorder.Body.String(), "5") // The red score + assert.Contains(t, recorder.Body.String(), "0") // The blue score } func TestMatchReviewCreateNewResult(t *testing.T) { diff --git a/web/reports.go b/web/reports.go index 353c1ef..a65267d 100644 --- a/web/reports.go +++ b/web/reports.go @@ -53,8 +53,8 @@ func (web *Web) rankingsPdfReportHandler(w http.ResponseWriter, r *http.Request) } // The widths of the table columns in mm, stored here so that they can be referenced for each row. - colWidths := map[string]float64{"Rank": 13, "Team": 20, "RP": 20, "Park/Climb": 21, "Auto": 20, "Ownership": 21, - "Vault": 20, "W-L-T": 21, "DQ": 20, "Played": 20} + colWidths := map[string]float64{"Rank": 13, "Team": 20, "RP": 20, "Cargo": 21, "Hatch": 20, "Hab Climb": 21, + "Sandstorm": 20, "W-L-T": 21, "DQ": 20, "Played": 20} rowHeight := 6.5 pdf := gofpdf.New("P", "mm", "Letter", "font") @@ -67,10 +67,10 @@ func (web *Web) rankingsPdfReportHandler(w http.ResponseWriter, r *http.Request) pdf.CellFormat(colWidths["Rank"], rowHeight, "Rank", "1", 0, "C", true, 0, "") pdf.CellFormat(colWidths["Team"], rowHeight, "Team", "1", 0, "C", true, 0, "") pdf.CellFormat(colWidths["RP"], rowHeight, "RP", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Park/Climb"], rowHeight, "Park/Climb", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Auto"], rowHeight, "Auto", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Ownership"], rowHeight, "Ownership", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Vault"], rowHeight, "Vault", "1", 0, "C", true, 0, "") + pdf.CellFormat(colWidths["Cargo"], rowHeight, "Cargo", "1", 0, "C", true, 0, "") + pdf.CellFormat(colWidths["Hatch"], rowHeight, "Hatch", "1", 0, "C", true, 0, "") + pdf.CellFormat(colWidths["Hab Climb"], rowHeight, "Hab Climb", "1", 0, "C", true, 0, "") + pdf.CellFormat(colWidths["Sandstorm"], rowHeight, "Sandstorm", "1", 0, "C", true, 0, "") pdf.CellFormat(colWidths["W-L-T"], rowHeight, "W-L-T", "1", 0, "C", true, 0, "") pdf.CellFormat(colWidths["DQ"], rowHeight, "DQ", "1", 0, "C", true, 0, "") pdf.CellFormat(colWidths["Played"], rowHeight, "Played", "1", 1, "C", true, 0, "") @@ -81,10 +81,12 @@ func (web *Web) rankingsPdfReportHandler(w http.ResponseWriter, r *http.Request) pdf.SetFont("Arial", "", 10) pdf.CellFormat(colWidths["Team"], rowHeight, strconv.Itoa(ranking.TeamId), "1", 0, "C", false, 0, "") pdf.CellFormat(colWidths["RP"], rowHeight, strconv.Itoa(ranking.RankingPoints), "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Park/Climb"], rowHeight, strconv.Itoa(ranking.ParkClimbPoints), "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Auto"], rowHeight, strconv.Itoa(ranking.AutoPoints), "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Ownership"], rowHeight, strconv.Itoa(ranking.OwnershipPoints), "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Vault"], rowHeight, strconv.Itoa(ranking.VaultPoints), "1", 0, "C", false, 0, "") + pdf.CellFormat(colWidths["Cargo"], rowHeight, strconv.Itoa(ranking.CargoPoints), "1", 0, "C", false, 0, "") + pdf.CellFormat(colWidths["Hatch"], rowHeight, strconv.Itoa(ranking.HatchPanelPoints), "1", 0, "C", false, 0, "") + pdf.CellFormat(colWidths["Hab Climb"], rowHeight, strconv.Itoa(ranking.HabClimbPoints), "1", 0, "C", false, 0, + "") + pdf.CellFormat(colWidths["Sandstorm"], rowHeight, strconv.Itoa(ranking.SandstormBonusPoints), "1", 0, "C", + false, 0, "") record := fmt.Sprintf("%d-%d-%d", ranking.Wins, ranking.Losses, ranking.Ties) pdf.CellFormat(colWidths["W-L-T"], rowHeight, record, "1", 0, "C", false, 0, "") pdf.CellFormat(colWidths["DQ"], rowHeight, strconv.Itoa(ranking.Disqualifications), "1", 0, "C", false, 0, "") @@ -196,7 +198,8 @@ func (web *Web) schedulePdfReportHandler(w http.ResponseWriter, r *http.Request) } // Render match info row. - pdf.CellFormat(colWidths["Time"], height, match.Time.Local().Format("Mon 1/02 03:04 PM"), borderStr, 0, alignStr, false, 0, "") + pdf.CellFormat(colWidths["Time"], height, match.Time.Local().Format("Mon 1/02 03:04 PM"), borderStr, 0, + alignStr, false, 0, "") pdf.CellFormat(colWidths["Type"], height, matchType, borderStr, 0, alignStr, false, 0, "") pdf.CellFormat(colWidths["Match"], height, match.DisplayName, borderStr, 0, alignStr, false, 0, "") pdf.CellFormat(colWidths["Team"], height, formatTeam(match.Red1), borderStr, 0, alignStr, false, 0, "") @@ -212,12 +215,18 @@ func (web *Web) schedulePdfReportHandler(w http.ResponseWriter, r *http.Request) pdf.CellFormat(colWidths["Time"], height, "", "LBR", 0, "C", false, 0, "") pdf.CellFormat(colWidths["Type"], height, "", "LBR", 0, "C", false, 0, "") pdf.CellFormat(colWidths["Match"], height, "", "LBR", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Red1IsSurrogate), "LBR", 0, "CT", false, 0, "") - pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Red2IsSurrogate), "LBR", 0, "CT", false, 0, "") - pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Red3IsSurrogate), "LBR", 0, "CT", false, 0, "") - pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Blue1IsSurrogate), "LBR", 0, "CT", false, 0, "") - pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Blue2IsSurrogate), "LBR", 0, "CT", false, 0, "") - pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Blue3IsSurrogate), "LBR", 1, "CT", false, 0, "") + pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Red1IsSurrogate), "LBR", 0, "CT", false, 0, + "") + pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Red2IsSurrogate), "LBR", 0, "CT", false, 0, + "") + pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Red3IsSurrogate), "LBR", 0, "CT", false, 0, + "") + pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Blue1IsSurrogate), "LBR", 0, "CT", false, 0, + "") + pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Blue2IsSurrogate), "LBR", 0, "CT", false, 0, + "") + pdf.CellFormat(colWidths["Team"], height, surrogateText(match.Blue3IsSurrogate), "LBR", 1, "CT", false, 0, + "") pdf.SetFont("Arial", "", 10) } } diff --git a/web/reports_test.go b/web/reports_test.go index 0ba2f05..499ac84 100644 --- a/web/reports_test.go +++ b/web/reports_test.go @@ -22,9 +22,9 @@ func TestRankingsCsvReport(t *testing.T) { recorder := web.getHttpResponse("/reports/csv/rankings") assert.Equal(t, 200, recorder.Code) assert.Equal(t, "text/plain", recorder.HeaderMap["Content-Type"][0]) - expectedBody := "Rank,TeamId,RankingPoints,ParkClimbPoints,AutoPoints,OwnershipPoints,VaultPoints,Wins," + - "Losses,Ties,Disqualifications,Played\n1,254,20,625,90,554,10,3,2,1,0,10\n2,1114,18,700,625,90,554,1,3," + - "2,0,10\n\n" + expectedBody := "Rank,TeamId,RankingPoints,CargoPoints,HatchPanelPoints,HabClimbPoints,SandstormBonusPoints,Wins," + + "Losses,Ties,Disqualifications,Played\n1,254,20,625,90,554,10,3,2,1,0,10\n2,1114,18,700,625,90,554,1,3,2,0,10" + + "\n\n" assert.Equal(t, expectedBody, recorder.Body.String()) } diff --git a/web/scoring_panel.go b/web/scoring_panel.go index 141bceb..f0c4344 100644 --- a/web/scoring_panel.go +++ b/web/scoring_panel.go @@ -89,48 +89,6 @@ func (web *Web) scoringPanelWebsocketHandler(w http.ResponseWriter, r *http.Requ scoreChanged := false switch messageType { - case "r": - if !(*score).AutoCommitted { - if (*score).CurrentScore.AutoRuns < 3 { - (*score).CurrentScore.AutoRuns++ - scoreChanged = true - } - } - case "R": - if !(*score).AutoCommitted { - if (*score).CurrentScore.AutoRuns > 0 { - (*score).CurrentScore.AutoRuns-- - scoreChanged = true - } - } - case "c": - if (*score).AutoCommitted { - if (*score).CurrentScore.Climbs+(*score).CurrentScore.Parks < 3 { - (*score).CurrentScore.Climbs++ - scoreChanged = true - } - } - case "C": - if (*score).AutoCommitted { - if (*score).CurrentScore.Climbs > 0 { - (*score).CurrentScore.Climbs-- - scoreChanged = true - } - } - case "p": - if (*score).AutoCommitted { - if (*score).CurrentScore.Climbs+(*score).CurrentScore.Parks < 3 { - (*score).CurrentScore.Parks++ - scoreChanged = true - } - } - case "P": - if (*score).AutoCommitted { - if (*score).CurrentScore.Parks > 0 { - (*score).CurrentScore.Parks-- - scoreChanged = true - } - } case "\r": if (web.arena.MatchState != field.PreMatch && web.arena.MatchState != field.TimeoutActive && web.arena.MatchState != field.PostTimeout || web.arena.CurrentMatch.Type == "test") && diff --git a/web/scoring_panel_test.go b/web/scoring_panel_test.go index 31ed1f6..d07bb7c 100644 --- a/web/scoring_panel_test.go +++ b/web/scoring_panel_test.go @@ -47,34 +47,37 @@ func TestScoringPanelWebsocket(t *testing.T) { readWebsocketType(t, blueWs, "matchTime") readWebsocketType(t, blueWs, "realtimeScore") - // Send a match worth of scoring commands in. - redWs.Write("r", nil) - blueWs.Write("r", nil) - blueWs.Write("r", nil) - blueWs.Write("r", nil) - blueWs.Write("r", nil) - blueWs.Write("R", nil) - for i := 0; i < 5; i++ { - readWebsocketType(t, redWs, "realtimeScore") - readWebsocketType(t, blueWs, "realtimeScore") - } - redWs.Write("\r", nil) - blueWs.Write("\r", nil) - redWs.Write("a", nil) - redWs.Write("\r", nil) - for i := 0; i < 4; i++ { - readWebsocketType(t, redWs, "realtimeScore") - readWebsocketType(t, blueWs, "realtimeScore") - } + // TODO(pat): Update for 2019. + /* + // Send a match worth of scoring commands in. + redWs.Write("r", nil) + blueWs.Write("r", nil) + blueWs.Write("r", nil) + blueWs.Write("r", nil) + blueWs.Write("r", nil) + blueWs.Write("R", nil) + for i := 0; i < 5; i++ { + readWebsocketType(t, redWs, "realtimeScore") + readWebsocketType(t, blueWs, "realtimeScore") + } + redWs.Write("\r", nil) + blueWs.Write("\r", nil) + redWs.Write("a", nil) + redWs.Write("\r", nil) + for i := 0; i < 4; i++ { + readWebsocketType(t, redWs, "realtimeScore") + readWebsocketType(t, blueWs, "realtimeScore") + } - assert.Equal(t, 1, web.arena.RedRealtimeScore.CurrentScore.AutoRuns) - assert.Equal(t, 2, web.arena.BlueRealtimeScore.CurrentScore.AutoRuns) + assert.Equal(t, 1, web.arena.RedRealtimeScore.CurrentScore.AutoRuns) + assert.Equal(t, 2, web.arena.BlueRealtimeScore.CurrentScore.AutoRuns) - redWs.Write("r", nil) + redWs.Write("r", nil) - // Make sure auto scores haven't changed in teleop. - assert.Equal(t, 1, web.arena.RedRealtimeScore.CurrentScore.AutoRuns) - assert.Equal(t, 2, web.arena.BlueRealtimeScore.CurrentScore.AutoRuns) + // Make sure auto scores haven't changed in teleop. + assert.Equal(t, 1, web.arena.RedRealtimeScore.CurrentScore.AutoRuns) + assert.Equal(t, 2, web.arena.BlueRealtimeScore.CurrentScore.AutoRuns) + */ // Test committing logic. redWs.Write("commitMatch", nil) diff --git a/web/setup_settings.go b/web/setup_settings.go index d0e9ff6..0fc4f6e 100644 --- a/web/setup_settings.go +++ b/web/setup_settings.go @@ -66,6 +66,7 @@ func (web *Web) settingsPostHandler(w http.ResponseWriter, r *http.Request) { eventSettings.PlcAddress = r.PostFormValue("plcAddress") eventSettings.AdminPassword = r.PostFormValue("adminPassword") eventSettings.ReaderPassword = r.PostFormValue("readerPassword") + eventSettings.HabDockingThreshold, _ = strconv.Atoi(r.PostFormValue("habDockingThreshold")) err := web.arena.Database.SaveEventSettings(eventSettings) if err != nil {