diff --git a/alliance_station_display.go b/alliance_station_display.go index 687e23f..4c23ab3 100644 --- a/alliance_station_display.go +++ b/alliance_station_display.go @@ -114,10 +114,10 @@ func AllianceStationDisplayWebsocketHandler(w http.ResponseWriter, r *http.Reque return } data = struct { - RedScoreFields *RealtimeScoreFields - BlueScoreFields *RealtimeScoreFields - }{mainArena.redRealtimeScore.ScoreFields(mainArena.blueRealtimeScore.CurrentScore.Fouls), - mainArena.blueRealtimeScore.ScoreFields(mainArena.redRealtimeScore.CurrentScore.Fouls)} + RedScore int + BlueScore int + }{mainArena.redRealtimeScore.Score(mainArena.blueRealtimeScore.CurrentScore.Fouls), + mainArena.blueRealtimeScore.Score(mainArena.redRealtimeScore.CurrentScore.Fouls)} err = websocket.Write("realtimeScore", data) if err != nil { log.Printf("Websocket error: %s", err) @@ -175,10 +175,10 @@ func AllianceStationDisplayWebsocketHandler(w http.ResponseWriter, r *http.Reque } messageType = "realtimeScore" message = struct { - RedScoreFields *RealtimeScoreFields - BlueScoreFields *RealtimeScoreFields - }{mainArena.redRealtimeScore.ScoreFields(mainArena.blueRealtimeScore.CurrentScore.Fouls), - mainArena.blueRealtimeScore.ScoreFields(mainArena.redRealtimeScore.CurrentScore.Fouls)} + RedScore int + BlueScore int + }{mainArena.redRealtimeScore.Score(mainArena.blueRealtimeScore.CurrentScore.Fouls), + mainArena.blueRealtimeScore.Score(mainArena.redRealtimeScore.CurrentScore.Fouls)} case _, ok := <-reloadDisplaysListener: if !ok { return diff --git a/api_test.go b/api_test.go index 8d29221..f6847fa 100644 --- a/api_test.go +++ b/api_test.go @@ -44,6 +44,7 @@ func TestRankingsApi(t *testing.T) { clearDb() defer clearDb() db, _ = OpenDatabase(testDbPath) + eventSettings, _ = db.GetEventSettings() // Test that empty rankings produces an empty array. recorder := getHttpResponse("/api/rankings") @@ -59,8 +60,8 @@ func TestRankingsApi(t *testing.T) { assert.Equal(t, 0, len(rankingsData.Rankings)) assert.Equal(t, "", rankingsData.HighestPlayedMatch) - ranking1 := RankingWithNickname{Ranking{1114, 2, 18, 625, 90, 554, 9, 0.254, 3, 2, 1, 0, 10}, "Simbots"} - ranking2 := RankingWithNickname{Ranking{254, 1, 20, 625, 90, 554, 10, 0.254, 1, 2, 3, 0, 10}, "ChezyPof"} + ranking1 := RankingWithNickname{Ranking{1114, 2, 18, 700, 625, 90, 554, 9, 0.254, 3, 2, 1, 0, 10}, "Simbots"} + ranking2 := RankingWithNickname{Ranking{254, 1, 20, 700, 625, 90, 554, 10, 0.254, 1, 2, 3, 0, 10}, "ChezyPof"} db.CreateRanking(&ranking1.Ranking) db.CreateRanking(&ranking2.Ranking) db.CreateMatch(&Match{Type: "qualification", DisplayName: "29", Status: "complete"}) diff --git a/arena.go b/arena.go index a23e3cb..1334fe2 100644 --- a/arena.go +++ b/arena.go @@ -73,7 +73,6 @@ type Arena struct { allianceSelectionNotifier *Notifier lowerThirdNotifier *Notifier reloadDisplaysNotifier *Notifier - defenseSelectionNotifier *Notifier audienceDisplayScreen string allianceStationDisplays map[string]string allianceStationDisplayScreen string @@ -86,12 +85,6 @@ type Arena struct { fieldReset bool } -type RealtimeScoreFields struct { - Score int - TowerStrength int - DefensesStrength [5]int -} - var mainArena Arena // Named thusly to avoid polluting the global namespace with something more generic. func NewRealtimeScore() *RealtimeScore { @@ -128,7 +121,6 @@ func (arena *Arena) Setup() { arena.allianceSelectionNotifier = NewNotifier() arena.lowerThirdNotifier = NewNotifier() arena.reloadDisplaysNotifier = NewNotifier() - arena.defenseSelectionNotifier = NewNotifier() arena.lights.Setup() @@ -228,7 +220,6 @@ func (arena *Arena) LoadMatch(match *Match) error { arena.realtimeScoreNotifier.Notify(nil) arena.allianceStationDisplayScreen = "match" arena.allianceStationDisplayNotifier.Notify(nil) - arena.defenseSelectionNotifier.Notify(nil) return nil } @@ -525,17 +516,6 @@ func (realtimeScore *RealtimeScore) Score(opponentFouls []Foul) int { return scoreSummary(&realtimeScore.CurrentScore, opponentFouls, mainArena.currentMatch.Type).Score } -// Calculates the integer score, tower strength, and defenses strength for the given realtime snapshot. -func (realtimeScore *RealtimeScore) ScoreFields(opponentFouls []Foul) *RealtimeScoreFields { - scoreSummary := scoreSummary(&realtimeScore.CurrentScore, opponentFouls, mainArena.currentMatch.Type) - var defensesStrength [5]int - for i := 0; i < 5; i++ { - defensesStrength[i] = 2 - realtimeScore.CurrentScore.AutoDefensesCrossed[i] - - realtimeScore.CurrentScore.DefensesCrossed[i] - } - return &RealtimeScoreFields{scoreSummary.Score, scoreSummary.TowerStrength, defensesStrength} -} - // Manipulates the arena LED lighting based on the current state of the match. func (arena *Arena) handleLighting() { switch arena.MatchState { @@ -546,9 +526,7 @@ func (arena *Arena) handleLighting() { case TELEOP_PERIOD: fallthrough case ENDGAME_PERIOD: - redScoreFields := arena.redRealtimeScore.ScoreFields(arena.blueRealtimeScore.CurrentScore.Fouls) - blueScoreFields := arena.blueRealtimeScore.ScoreFields(arena.redRealtimeScore.CurrentScore.Fouls) - arena.lights.SetDefenses(redScoreFields.DefensesStrength, blueScoreFields.DefensesStrength) + break case POST_MATCH: if mainArena.fieldReset { arena.lights.SetFieldReset() diff --git a/audience_display.go b/audience_display.go index bf765ff..6b7af1a 100644 --- a/audience_display.go +++ b/audience_display.go @@ -94,10 +94,10 @@ func AudienceDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) { return } data = struct { - RedScoreFields *RealtimeScoreFields - BlueScoreFields *RealtimeScoreFields - }{mainArena.redRealtimeScore.ScoreFields(mainArena.blueRealtimeScore.CurrentScore.Fouls), - mainArena.blueRealtimeScore.ScoreFields(mainArena.redRealtimeScore.CurrentScore.Fouls)} + RedScore int + BlueScore int + }{mainArena.redRealtimeScore.Score(mainArena.blueRealtimeScore.CurrentScore.Fouls), + mainArena.blueRealtimeScore.Score(mainArena.redRealtimeScore.CurrentScore.Fouls)} err = websocket.Write("realtimeScore", data) if err != nil { log.Printf("Websocket error: %s", err) @@ -154,10 +154,10 @@ func AudienceDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) { } messageType = "realtimeScore" message = struct { - RedScoreFields *RealtimeScoreFields - BlueScoreFields *RealtimeScoreFields - }{mainArena.redRealtimeScore.ScoreFields(mainArena.blueRealtimeScore.CurrentScore.Fouls), - mainArena.blueRealtimeScore.ScoreFields(mainArena.redRealtimeScore.CurrentScore.Fouls)} + RedScore int + BlueScore int + }{mainArena.redRealtimeScore.Score(mainArena.blueRealtimeScore.CurrentScore.Fouls), + mainArena.blueRealtimeScore.Score(mainArena.redRealtimeScore.CurrentScore.Fouls)} case _, ok := <-scorePostedListener: if !ok { return diff --git a/db/migrations/20140524160241_CreateEventSettings.sql b/db/migrations/20140524160241_CreateEventSettings.sql index cfa8864..13dd40e 100644 --- a/db/migrations/20140524160241_CreateEventSettings.sql +++ b/db/migrations/20140524160241_CreateEventSettings.sql @@ -22,9 +22,6 @@ CREATE TABLE event_settings ( tbadownloadenabled bool, adminpassword VARCHAR(255), readerpassword VARCHAR(255), - reddefenselightsaddress VARCHAR(255), - bluedefenselightsaddress VARCHAR(255), - initialtowerstrength int, stemtvpublishingenabled bool, stemtveventcode VARCHAR(16) ); diff --git a/db/migrations/20140524180123_CreateMatches.sql b/db/migrations/20140524180123_CreateMatches.sql index aee9486..24af415 100644 --- a/db/migrations/20140524180123_CreateMatches.sql +++ b/db/migrations/20140524180123_CreateMatches.sql @@ -21,17 +21,7 @@ CREATE TABLE matches ( blue3issurrogate bool, status VARCHAR(16), startedat DATETIME, - winner VARCHAR(16), - reddefense1 VARCHAR(3), - reddefense2 VARCHAR(3), - reddefense3 VARCHAR(3), - reddefense4 VARCHAR(3), - reddefense5 VARCHAR(3), - bluedefense1 VARCHAR(3), - bluedefense2 VARCHAR(3), - bluedefense3 VARCHAR(3), - bluedefense4 VARCHAR(3), - bluedefense5 VARCHAR(3) + winner VARCHAR(16) ); CREATE UNIQUE INDEX type_displayname ON matches(type, displayname); diff --git a/db/migrations/20140526225924_CreateRankings.sql b/db/migrations/20140526225924_CreateRankings.sql index 4ab2a03..c70a8e9 100644 --- a/db/migrations/20140526225924_CreateRankings.sql +++ b/db/migrations/20140526225924_CreateRankings.sql @@ -3,10 +3,11 @@ CREATE TABLE rankings ( teamid INTEGER PRIMARY KEY, rank int, rankingpoints int, + matchpoints int, autopoints int, - scalechallengepoints int, - goalpoints int, - defensepoints int, + rotorpoints int, + takeoffpoints int, + pressurepoints int, random REAL, wins int, losses int, diff --git a/driver_station_connection_test.go b/driver_station_connection_test.go index 1820b6e..8c4fc22 100644 --- a/driver_station_connection_test.go +++ b/driver_station_connection_test.go @@ -177,8 +177,7 @@ func TestListenForDriverStations(t *testing.T) { tcpConn.Write(dataSend[:]) var dataReceived [5]byte _, err = tcpConn.Read(dataReceived[:]) - assert.Nil(t, err) - assert.Equal(t, [5]byte{0, 3, 25, 0, 2}, dataReceived) + assert.NotNil(t, err) tcpConn.Close() } diff --git a/event_settings.go b/event_settings.go index 411af29..b3f3767 100644 --- a/event_settings.go +++ b/event_settings.go @@ -27,9 +27,6 @@ type EventSettings struct { BandwidthMonitoringEnabled bool AdminPassword string ReaderPassword string - RedDefenseLightsAddress string - BlueDefenseLightsAddress string - InitialTowerStrength int StemTvPublishingEnabled bool StemTvEventCode string } @@ -49,9 +46,6 @@ func (database *Database) GetEventSettings() (*EventSettings, error) { eventSettings.SelectionRound3Order = "" eventSettings.TBADownloadEnabled = true - // Game-specific default settings. - eventSettings.InitialTowerStrength = 10 - err = database.eventSettingsMap.Insert(eventSettings) if err != nil { return nil, err diff --git a/event_settings_test.go b/event_settings_test.go index 2ef4fb5..b149af2 100644 --- a/event_settings_test.go +++ b/event_settings_test.go @@ -18,8 +18,8 @@ func TestEventSettingsReadWrite(t *testing.T) { eventSettings, err := db.GetEventSettings() assert.Nil(t, err) assert.Equal(t, EventSettings{Id: 0, Name: "Untitled Event", Code: "UE", DisplayBackgroundColor: "#00ff00", - NumElimAlliances: 8, SelectionRound2Order: "L", SelectionRound3Order: "", TBADownloadEnabled: true, - InitialTowerStrength: 10}, *eventSettings) + NumElimAlliances: 8, SelectionRound2Order: "L", SelectionRound3Order: "", TBADownloadEnabled: true}, + *eventSettings) eventSettings.Name = "Chezy Champs" eventSettings.Code = "cc" diff --git a/fta_display.go b/fta_display.go index 3bd677e..13bfed7 100644 --- a/fta_display.go +++ b/fta_display.go @@ -18,34 +18,15 @@ func FtaDisplayHandler(w http.ResponseWriter, r *http.Request) { return } - // Retrieve the next few matches to show which defenses they will require. - numUpcomingMatches := 3 - matches, err := db.GetMatchesByType(mainArena.currentMatch.Type) - if err != nil { - handleWebErr(w, err) - return - } - var upcomingMatches []Match - for _, match := range matches { - if match.Status != "complete" { - upcomingMatches = append(upcomingMatches, match) - if len(upcomingMatches) == numUpcomingMatches { - break - } - } - } - template := template.New("").Funcs(templateHelpers) - _, err = template.ParseFiles("templates/fta_display.html", "templates/base.html") + _, err := template.ParseFiles("templates/fta_display.html", "templates/base.html") if err != nil { handleWebErr(w, err) return } data := struct { *EventSettings - UpcomingMatches []Match - DefenseNames map[string]string - }{eventSettings, upcomingMatches, defenseNames} + }{eventSettings} err = template.ExecuteTemplate(w, "base", data) if err != nil { handleWebErr(w, err) @@ -66,8 +47,6 @@ func FtaDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) { robotStatusListener := mainArena.robotStatusNotifier.Listen() defer close(robotStatusListener) - defenseSelectionListener := mainArena.defenseSelectionNotifier.Listen() - defer close(defenseSelectionListener) reloadDisplaysListener := mainArena.reloadDisplaysNotifier.Listen() defer close(reloadDisplaysListener) @@ -90,12 +69,6 @@ func FtaDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) { } messageType = "status" message = mainArena - case _, ok := <-defenseSelectionListener: - if !ok { - return - } - messageType = "reload" - message = nil case _, ok := <-reloadDisplaysListener: if !ok { return diff --git a/lights.go b/lights.go index 849bbdd..f0cddf7 100644 --- a/lights.go +++ b/lights.go @@ -103,10 +103,11 @@ func (lights *Lights) Setup() error { func (lights *Lights) SetupConnections() error { lights.connections = make(map[string]*net.Conn) - if err := lights.connect(RED_DEFENSE, eventSettings.RedDefenseLightsAddress); err != nil { + // TODO(patrick): Update for 2017. + if err := lights.connect(RED_DEFENSE, ""); err != nil { return err } - if err := lights.connect(BLUE_DEFENSE, eventSettings.BlueDefenseLightsAddress); err != nil { + if err := lights.connect(BLUE_DEFENSE, ""); err != nil { return err } lights.newConnections = true diff --git a/match.go b/match.go index fb124d8..612dd07 100644 --- a/match.go +++ b/match.go @@ -34,22 +34,8 @@ type Match struct { Status string StartedAt time.Time Winner string - RedDefense1 string - RedDefense2 string - RedDefense3 string - RedDefense4 string - RedDefense5 string - BlueDefense1 string - BlueDefense2 string - BlueDefense3 string - BlueDefense4 string - BlueDefense5 string } -var placeableDefenses = []string{"CDF", "M", "R", "RW", "RT"} -var defenseNames = map[string]string{"LB": "Low Bar", "CDF": "Cheval de Frise", "M": "Moat", - "R": "Ramparts", "RW": "Rock Wall", "RT": "Rough Terrain"} - func (database *Database) CreateMatch(match *Match) error { return database.matchMap.Insert(match) } diff --git a/match_play_test.go b/match_play_test.go index 0b61aff..4ae2a36 100644 --- a/match_play_test.go +++ b/match_play_test.go @@ -160,13 +160,13 @@ func TestCommitMatch(t *testing.T) { match.Id = 1 match.Type = "qualification" db.CreateMatch(match) - matchResult = &MatchResult{MatchId: match.Id, BlueScore: Score{AutoDefensesReached: 2}} + matchResult = &MatchResult{MatchId: match.Id, BlueScore: Score{AutoMobility: 2}} err = CommitMatchScore(match, matchResult, false) assert.Nil(t, err) assert.Equal(t, 1, matchResult.PlayNumber) match, _ = db.GetMatchById(1) assert.Equal(t, "B", match.Winner) - matchResult = &MatchResult{MatchId: match.Id, RedScore: Score{AutoDefensesReached: 1}} + matchResult = &MatchResult{MatchId: match.Id, RedScore: Score{AutoMobility: 1}} err = CommitMatchScore(match, matchResult, false) assert.Nil(t, err) assert.Equal(t, 2, matchResult.PlayNumber) @@ -206,7 +206,7 @@ func TestCommitEliminationTie(t *testing.T) { match := &Match{Id: 0, Type: "qualification", Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, Blue3: 6} db.CreateMatch(match) - matchResult := &MatchResult{MatchId: match.Id, RedScore: Score{HighGoals: 1, Fouls: []Foul{Foul{}}}} + matchResult := &MatchResult{MatchId: match.Id, RedScore: Score{FuelHigh: 15, Fouls: []Foul{Foul{}}}} err = CommitMatchScore(match, matchResult, false) assert.Nil(t, err) match, _ = db.GetMatchById(1) @@ -258,11 +258,12 @@ func TestCommitCards(t *testing.T) { match.Type = "elimination" db.SaveMatch(match) *matchResult = buildTestMatchResult(match.Id, 10) + matchResult.MatchType = match.Type matchResult.RedCards = map[string]string{"1": "red"} err = CommitMatchScore(match, matchResult, false) assert.Nil(t, err) assert.Equal(t, 0, matchResult.RedScoreSummary().Score) - assert.Equal(t, 113, matchResult.BlueScoreSummary().Score) + assert.Equal(t, 533, matchResult.BlueScoreSummary().Score) } func TestMatchPlayWebsocketCommands(t *testing.T) { @@ -340,12 +341,12 @@ func TestMatchPlayWebsocketCommands(t *testing.T) { readWebsocketType(t, ws, "status") readWebsocketType(t, ws, "setAudienceDisplay") assert.Equal(t, POST_MATCH, mainArena.MatchState) - mainArena.redRealtimeScore.CurrentScore.AutoDefensesReached = 1 - mainArena.blueRealtimeScore.CurrentScore.AutoLowGoals = 2 + mainArena.redRealtimeScore.CurrentScore.AutoMobility = 1 + mainArena.blueRealtimeScore.CurrentScore.AutoFuelLow = 2 ws.Write("commitResults", nil) readWebsocketMultiple(t, ws, 3) // reload, realtimeScore, setAllianceStationDisplay - assert.Equal(t, 1, mainArena.savedMatchResult.RedScore.AutoDefensesReached) - assert.Equal(t, 2, mainArena.savedMatchResult.BlueScore.AutoLowGoals) + assert.Equal(t, 1, mainArena.savedMatchResult.RedScore.AutoMobility) + assert.Equal(t, 2, mainArena.savedMatchResult.BlueScore.AutoFuelLow) assert.Equal(t, PRE_MATCH, mainArena.MatchState) ws.Write("discardResults", nil) readWebsocketMultiple(t, ws, 3) // reload, realtimeScore, setAllianceStationDisplay diff --git a/match_result.go b/match_result.go index d0f8554..abc61ad 100644 --- a/match_result.go +++ b/match_result.go @@ -32,17 +32,16 @@ type MatchResultDb struct { } type Score struct { - AutoDefensesReached int - AutoDefensesCrossed [5]int - AutoLowGoals int - AutoHighGoals int - DefensesCrossed [5]int - LowGoals int - HighGoals int - Challenges int - Scales int - Fouls []Foul - ElimDq bool + AutoMobility int + AutoGears int + AutoFuelLow int + AutoFuelHigh int + Gears int + FuelLow int + FuelHigh int + Takeoffs int + Fouls []Foul + ElimDq bool } type Foul struct { @@ -53,17 +52,16 @@ type Foul struct { } type ScoreSummary struct { - AutoPoints int - DefensePoints int - GoalPoints int - ScaleChallengePoints int - TeleopPoints int - FoulPoints int - BonusPoints int - Score int - Breached bool - Captured bool - TowerStrength int + AutoMobilityPoints int + AutoPoints int + RotorPoints int + TakeoffPoints int + PressurePoints int + BonusPoints int + FoulPoints int + Score int + PressureGoalReached bool + RotorGoalReached bool } // Returns a new match result object with empty slices instead of nil. @@ -163,58 +161,42 @@ func scoreSummary(score *Score, opponentFouls []Foul, matchType string) *ScoreSu } // Calculate autonomous score. - autoDefensePoints := 0 - for _, defense := range score.AutoDefensesCrossed { - autoDefensePoints += 10 * defense - } - autoGoalPoints := 5*score.AutoLowGoals + 10*score.AutoHighGoals - summary.AutoPoints = 2*score.AutoDefensesReached + autoDefensePoints + autoGoalPoints + summary.AutoMobilityPoints = 5 * score.AutoMobility + autoRotors := numRotors(score.AutoGears) + summary.AutoPoints = summary.AutoMobilityPoints + 60*autoRotors + score.AutoFuelHigh + + score.AutoFuelLow/3 // Calculate teleop score. - teleopDefensePoints := 0 - for _, defense := range score.DefensesCrossed { - teleopDefensePoints += 5 * defense - } - summary.ScaleChallengePoints = 5*score.Challenges + 15*score.Scales - teleopGoalPoints := 2*score.LowGoals + 5*score.HighGoals - - // Calculate tower strength. - numTechFouls := 0 - for _, foul := range score.Fouls { - if foul.IsTechnical { - numTechFouls++ - } - } - summary.TowerStrength = eventSettings.InitialTowerStrength + numTechFouls - score.AutoLowGoals - - score.AutoHighGoals - score.LowGoals - score.HighGoals + teleopRotors := numRotors(score.AutoGears+score.Gears) - autoRotors + summary.RotorPoints = 60*autoRotors + 40*teleopRotors + summary.TakeoffPoints = 50 * score.Takeoffs + summary.PressurePoints = (9*score.AutoFuelHigh + 3*score.AutoFuelLow + 3*score.FuelHigh + score.FuelLow) / 9 // Calculate bonuses. - summary.BonusPoints = 0 - numDefensesDamaged := 0 - for i := 0; i < 5; i++ { - if score.AutoDefensesCrossed[i]+score.DefensesCrossed[i] == 2 { - numDefensesDamaged++ - } - } - if numDefensesDamaged >= 4 { - summary.Breached = true + if summary.PressurePoints >= 40 { + summary.PressureGoalReached = true if matchType == "elimination" { summary.BonusPoints += 20 } } - if score.Challenges+score.Scales == 3 && summary.TowerStrength <= 0 { - summary.Captured = true + if autoRotors+teleopRotors == 4 { + summary.RotorGoalReached = true if matchType == "elimination" { - summary.BonusPoints += 25 + summary.BonusPoints += 100 } } - summary.TeleopPoints = teleopDefensePoints + teleopGoalPoints + summary.ScaleChallengePoints + - summary.BonusPoints - summary.FoulPoints = 5 * len(opponentFouls) - summary.DefensePoints = autoDefensePoints + teleopDefensePoints - summary.GoalPoints = autoGoalPoints + teleopGoalPoints - summary.Score = summary.AutoPoints + summary.TeleopPoints + summary.FoulPoints + // Calculate penalty points. + for _, foul := range opponentFouls { + if foul.IsTechnical { + summary.FoulPoints += 25 + } else { + summary.FoulPoints += 5 + } + } + + summary.Score = summary.AutoMobilityPoints + summary.RotorPoints + summary.TakeoffPoints + summary.PressurePoints + + summary.BonusPoints + summary.FoulPoints return summary } @@ -265,3 +247,14 @@ func (matchResultDb *MatchResultDb) deserialize() (*MatchResult, error) { } return &matchResult, nil } + +// Returns the number of completed rotors given the number of gears installed. +func numRotors(numGears int) int { + rotorGears := []int{1, 3, 7, 13} + for rotors, gears := range rotorGears { + if numGears < gears { + return rotors + } + } + return len(rotorGears) +} diff --git a/match_result_test.go b/match_result_test.go index c8cfc2c..efcc0a3 100644 --- a/match_result_test.go +++ b/match_result_test.go @@ -33,7 +33,7 @@ func TestMatchResultCrud(t *testing.T) { assert.Nil(t, err) assert.Equal(t, matchResult, *matchResult2) - matchResult.BlueScore.AutoDefensesReached = 12 + matchResult.BlueScore.AutoMobility = 12 db.SaveMatchResult(&matchResult) matchResult2, err = db.GetMatchResultForMatch(254) assert.Nil(t, err) @@ -90,58 +90,53 @@ func TestScoreSummary(t *testing.T) { matchResult := buildTestMatchResult(1, 1) redSummary := matchResult.RedScoreSummary() - assert.Equal(t, 55, redSummary.AutoPoints) - assert.Equal(t, 60, redSummary.DefensePoints) - assert.Equal(t, 86, redSummary.GoalPoints) - assert.Equal(t, 10, redSummary.ScaleChallengePoints) - assert.Equal(t, 101, redSummary.TeleopPoints) - assert.Equal(t, 0, redSummary.FoulPoints) + assert.Equal(t, 0, redSummary.AutoMobilityPoints) + assert.Equal(t, 80, redSummary.AutoPoints) + assert.Equal(t, 100, redSummary.RotorPoints) + assert.Equal(t, 50, redSummary.TakeoffPoints) + assert.Equal(t, 40, redSummary.PressurePoints) assert.Equal(t, 0, redSummary.BonusPoints) - assert.Equal(t, 156, redSummary.Score) - assert.Equal(t, true, redSummary.Breached) - assert.Equal(t, false, redSummary.Captured) - assert.Equal(t, -5, redSummary.TowerStrength) + assert.Equal(t, 0, redSummary.FoulPoints) + assert.Equal(t, 190, redSummary.Score) + assert.Equal(t, true, redSummary.PressureGoalReached) + assert.Equal(t, false, redSummary.RotorGoalReached) blueSummary := matchResult.BlueScoreSummary() - assert.Equal(t, 22, blueSummary.AutoPoints) - assert.Equal(t, 25, blueSummary.DefensePoints) - assert.Equal(t, 36, blueSummary.GoalPoints) - assert.Equal(t, 35, blueSummary.ScaleChallengePoints) - assert.Equal(t, 76, blueSummary.TeleopPoints) - assert.Equal(t, 15, blueSummary.FoulPoints) + assert.Equal(t, 10, blueSummary.AutoMobilityPoints) + assert.Equal(t, 133, blueSummary.AutoPoints) + assert.Equal(t, 200, blueSummary.RotorPoints) + assert.Equal(t, 150, blueSummary.TakeoffPoints) + assert.Equal(t, 18, blueSummary.PressurePoints) assert.Equal(t, 0, blueSummary.BonusPoints) - assert.Equal(t, 113, blueSummary.Score) - assert.Equal(t, false, blueSummary.Breached) - assert.Equal(t, false, blueSummary.Captured) - assert.Equal(t, 1, blueSummary.TowerStrength) + assert.Equal(t, 55, blueSummary.FoulPoints) + assert.Equal(t, 433, blueSummary.Score) + assert.Equal(t, false, blueSummary.PressureGoalReached) + assert.Equal(t, true, blueSummary.RotorGoalReached) - // Test breach boundary conditions. - matchResult.RedScore.DefensesCrossed[4] = 2 - assert.Equal(t, true, matchResult.RedScoreSummary().Breached) - matchResult.RedScore.AutoDefensesCrossed[0] = 0 - assert.Equal(t, true, matchResult.RedScoreSummary().Breached) - matchResult.RedScore.DefensesCrossed[1] = 1 - assert.Equal(t, false, matchResult.RedScoreSummary().Breached) + // Test pressure boundary conditions. + matchResult.RedScore.AutoFuelHigh = 19 + assert.Equal(t, false, matchResult.RedScoreSummary().PressureGoalReached) + matchResult.RedScore.FuelLow = 18 + assert.Equal(t, true, matchResult.RedScoreSummary().PressureGoalReached) + matchResult.RedScore.AutoFuelLow = 1 + assert.Equal(t, false, matchResult.RedScoreSummary().PressureGoalReached) + matchResult.RedScore.FuelHigh = 56 + assert.Equal(t, true, matchResult.RedScoreSummary().PressureGoalReached) - // Test capture boundary conditions. - matchResult.BlueScore.AutoHighGoals = 1 - assert.Equal(t, 0, matchResult.BlueScoreSummary().TowerStrength) - assert.Equal(t, true, matchResult.BlueScoreSummary().Captured) - matchResult.BlueScore.HighGoals = 5 - assert.Equal(t, true, matchResult.BlueScoreSummary().Captured) - matchResult.BlueScore.Challenges = 0 - assert.Equal(t, false, matchResult.BlueScoreSummary().Captured) + // Test rotor boundary conditions. + matchResult.BlueScore.AutoGears = 2 + assert.Equal(t, false, matchResult.BlueScoreSummary().RotorGoalReached) + matchResult.BlueScore.Gears = 11 + assert.Equal(t, true, matchResult.BlueScoreSummary().RotorGoalReached) // Test elimination bonus. matchResult.MatchType = "elimination" - matchResult.RedScore.DefensesCrossed[1] = 2 assert.Equal(t, 20, matchResult.RedScoreSummary().BonusPoints) - assert.Equal(t, 171, matchResult.RedScoreSummary().Score) - matchResult.BlueScore.Challenges = 1 - assert.Equal(t, 25, matchResult.BlueScoreSummary().BonusPoints) - assert.Equal(t, 153, matchResult.BlueScoreSummary().Score) - matchResult.RedScore.Scales = 1 - assert.Equal(t, 45, matchResult.RedScoreSummary().BonusPoints) + assert.Equal(t, 210, matchResult.RedScoreSummary().Score) + assert.Equal(t, 100, matchResult.BlueScoreSummary().BonusPoints) + assert.Equal(t, 513, matchResult.BlueScoreSummary().Score) + matchResult.RedScore.Gears = 12 + assert.Equal(t, 120, matchResult.RedScoreSummary().BonusPoints) matchResult.MatchType = "qualification" assert.Equal(t, 0, matchResult.RedScoreSummary().BonusPoints) assert.Equal(t, 0, matchResult.BlueScoreSummary().BonusPoints) @@ -150,8 +145,8 @@ func TestScoreSummary(t *testing.T) { func buildTestMatchResult(matchId int, playNumber int) MatchResult { fouls := []Foul{Foul{25, "G22", false, 25.2}, Foul{25, "G18", true, 150}, Foul{1868, "G20", true, 0}} matchResult := MatchResult{MatchId: matchId, PlayNumber: playNumber, MatchType: "qualification"} - matchResult.RedScore = Score{0, [5]int{1, 0, 1, 1, 0}, 1, 2, [5]int{1, 2, 1, 1, 1}, 3, 11, 2, 0, fouls, false} - matchResult.BlueScore = Score{1, [5]int{0, 1, 0, 0, 0}, 2, 0, [5]int{1, 1, 0, 0, 1}, 3, 4, 1, 2, []Foul{}, false} + matchResult.RedScore = Score{0, 1, 2, 20, 4, 12, 55, 1, fouls, false} + matchResult.BlueScore = Score{2, 3, 10, 0, 10, 65, 24, 3, []Foul{}, false} matchResult.RedCards = map[string]string{"1868": "yellow"} matchResult.BlueCards = map[string]string{} return matchResult diff --git a/match_review_test.go b/match_review_test.go index c07802f..86a7b7f 100644 --- a/match_review_test.go +++ b/match_review_test.go @@ -60,8 +60,8 @@ func TestMatchReviewEditExistingResult(t *testing.T) { recorder := getHttpResponse("/match_review") assert.Equal(t, 200, recorder.Code) assert.Contains(t, recorder.Body.String(), "QF4-3") - assert.Contains(t, recorder.Body.String(), "176") // The red score - assert.Contains(t, recorder.Body.String(), "113") // The blue score + assert.Contains(t, recorder.Body.String(), "210") // The red score + assert.Contains(t, recorder.Body.String(), "533") // The blue score // Check response for non-existent match. recorder = getHttpResponse(fmt.Sprintf("/match_review/%d/edit", 12345)) @@ -73,7 +73,7 @@ func TestMatchReviewEditExistingResult(t *testing.T) { assert.Contains(t, recorder.Body.String(), "QF4-3") // Update the score to something else. - postBody := "redScoreJson={\"AutoLowGoals\":5}&blueScoreJson={\"DefensesCrossed\":[2,2,1,2,2]," + + postBody := "redScoreJson={\"AutoMobility\":3}&blueScoreJson={\"Gears\":11," + "\"Fouls\":[{\"TeamId\":973,\"Rule\":\"G22\"}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}" recorder = postHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id), postBody) assert.Equal(t, 302, recorder.Code) @@ -82,8 +82,8 @@ func TestMatchReviewEditExistingResult(t *testing.T) { recorder = getHttpResponse("/match_review") assert.Equal(t, 200, recorder.Code) assert.Contains(t, recorder.Body.String(), "QF4-3") - assert.Contains(t, recorder.Body.String(), "30") // The red score - assert.Contains(t, recorder.Body.String(), "65") // The blue score + assert.Contains(t, recorder.Body.String(), "20") // The red score + assert.Contains(t, recorder.Body.String(), "120") // The blue score } func TestMatchReviewCreateNewResult(t *testing.T) { @@ -103,15 +103,15 @@ func TestMatchReviewCreateNewResult(t *testing.T) { recorder := getHttpResponse("/match_review") assert.Equal(t, 200, recorder.Code) assert.Contains(t, recorder.Body.String(), "QF4-3") - assert.NotContains(t, recorder.Body.String(), "176") // The red score - assert.NotContains(t, recorder.Body.String(), "113") // The blue score + assert.NotContains(t, recorder.Body.String(), "210") // The red score + assert.NotContains(t, recorder.Body.String(), "533") // The blue score recorder = getHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id)) assert.Equal(t, 200, recorder.Code) assert.Contains(t, recorder.Body.String(), "QF4-3") // Update the score to something else. - postBody := "redScoreJson={\"AutoDefensesReached\":3}&blueScoreJson={\"HighGoals\":3," + + postBody := "redScoreJson={\"AutoGears\":1}&blueScoreJson={\"FuelHigh\":30," + "\"Fouls\":[{\"TeamId\":973,\"Rule\":\"G22\"}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}" recorder = postHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id), postBody) assert.Equal(t, 302, recorder.Code) @@ -120,6 +120,6 @@ func TestMatchReviewCreateNewResult(t *testing.T) { recorder = getHttpResponse("/match_review") assert.Equal(t, 200, recorder.Code) assert.Contains(t, recorder.Body.String(), "QF4-3") - assert.Contains(t, recorder.Body.String(), "11") // The red score - assert.Contains(t, recorder.Body.String(), "15") // The blue score + assert.Contains(t, recorder.Body.String(), "65") // The red score + assert.Contains(t, recorder.Body.String(), "10") // The blue score } diff --git a/match_test.go b/match_test.go index cca1f53..a6eba7c 100644 --- a/match_test.go +++ b/match_test.go @@ -29,7 +29,7 @@ func TestMatchCrud(t *testing.T) { defer db.Close() match := Match{0, "qualification", "254", time.Now().UTC(), 0, 0, 0, 1, false, 2, false, 3, false, 4, false, - 5, false, 6, false, "", time.Now().UTC(), "", "LB", "M", "R", "RW", "RT", "LB", "CDF", "RT", "RW", "R"} + 5, false, 6, false, "", time.Now().UTC(), ""} db.CreateMatch(&match) match2, err := db.GetMatchById(1) assert.Nil(t, err) @@ -58,7 +58,7 @@ func TestTruncateMatches(t *testing.T) { defer db.Close() match := Match{0, "qualification", "254", time.Now().UTC(), 0, 0, 0, 1, false, 2, false, 3, false, 4, false, - 5, false, 6, false, "", time.Now().UTC(), "", "LB", "M", "R", "RW", "RT", "LB", "CDF", "RT", "RW", "R"} + 5, false, 6, false, "", time.Now().UTC(), ""} db.CreateMatch(&match) db.TruncateMatches() match2, err := db.GetMatchById(1) @@ -103,13 +103,13 @@ func TestGetMatchesByType(t *testing.T) { defer db.Close() match := Match{0, "qualification", "1", time.Now().UTC(), 0, 0, 0, 1, false, 2, false, 3, false, 4, false, - 5, false, 6, false, "", time.Now().UTC(), "", "LB", "M", "R", "RW", "RT", "LB", "CDF", "RT", "RW", "R"} + 5, false, 6, false, "", time.Now().UTC(), ""} db.CreateMatch(&match) match2 := Match{0, "practice", "1", time.Now().UTC(), 0, 0, 0, 1, false, 2, false, 3, false, 4, false, 5, - false, 6, false, "", time.Now().UTC(), "", "LB", "M", "R", "RW", "RT", "LB", "CDF", "RT", "RW", "R"} + false, 6, false, "", time.Now().UTC(), ""} db.CreateMatch(&match2) match3 := Match{0, "practice", "2", time.Now().UTC(), 0, 0, 0, 1, false, 2, false, 3, false, 4, false, 5, - false, 6, false, "", time.Now().UTC(), "", "LB", "M", "R", "RW", "RT", "LB", "CDF", "RT", "RW", "R"} + false, 6, false, "", time.Now().UTC(), ""} db.CreateMatch(&match3) matches, err := db.GetMatchesByType("test") diff --git a/ranking.go b/ranking.go index 9524951..e9c223d 100644 --- a/ranking.go +++ b/ranking.go @@ -12,19 +12,20 @@ import ( ) type Ranking struct { - TeamId int - Rank int - RankingPoints int - AutoPoints int - ScaleChallengePoints int - GoalPoints int - DefensePoints int - Random float64 - Wins int - Losses int - Ties int - Disqualifications int - Played int + TeamId int + Rank int + RankingPoints int + MatchPoints int + AutoPoints int + RotorPoints int + TakeoffPoints int + PressurePoints int + Random float64 + Wins int + Losses int + Ties int + Disqualifications int + Played int } type Rankings []*Ranking @@ -215,18 +216,19 @@ func addMatchResultToRankings(rankings map[int]*Ranking, teamId int, matchResult } else { ranking.Losses += 1 } - if ownScore.Breached { + if ownScore.PressureGoalReached { ranking.RankingPoints += 1 } - if ownScore.Captured { + if ownScore.RotorGoalReached { ranking.RankingPoints += 1 } // Assign tiebreaker points. + ranking.MatchPoints += ownScore.Score ranking.AutoPoints += ownScore.AutoPoints - ranking.ScaleChallengePoints += ownScore.ScaleChallengePoints - ranking.GoalPoints += ownScore.GoalPoints - ranking.DefensePoints += ownScore.DefensePoints + ranking.RotorPoints += ownScore.RotorPoints + ranking.TakeoffPoints += ownScore.TakeoffPoints + ranking.PressurePoints += ownScore.PressurePoints // Store a random value to be used as the last tiebreaker if necessary. ranking.Random = rand.Float64() @@ -253,19 +255,22 @@ 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.AutoPoints*b.Played == b.AutoPoints*a.Played { - if a.ScaleChallengePoints*b.Played == b.ScaleChallengePoints*a.Played { - if a.GoalPoints*b.Played == b.GoalPoints*a.Played { - if a.DefensePoints*b.Played == b.DefensePoints*a.Played { - return a.Random > b.Random + if a.MatchPoints*b.Played == b.MatchPoints*a.Played { + if a.AutoPoints*b.Played == b.AutoPoints*a.Played { + if a.RotorPoints*b.Played == b.RotorPoints*a.Played { + if a.TakeoffPoints*b.Played == b.TakeoffPoints*a.Played { + if a.PressurePoints*b.Played == b.PressurePoints*a.Played { + return a.Random > b.Random + } + return a.PressurePoints*b.Played > b.PressurePoints*a.Played } - return a.DefensePoints*b.Played > b.DefensePoints*a.Played + return a.TakeoffPoints*b.Played > b.TakeoffPoints*a.Played } - return a.GoalPoints*b.Played > b.GoalPoints*a.Played + return a.RotorPoints*b.Played > b.RotorPoints*a.Played } - return a.ScaleChallengePoints*b.Played > b.ScaleChallengePoints*a.Played + return a.AutoPoints*b.Played > b.AutoPoints*a.Played } - return a.AutoPoints*b.Played > b.AutoPoints*a.Played + return a.MatchPoints*b.Played > b.MatchPoints*a.Played } return a.RankingPoints*b.Played > b.RankingPoints*a.Played } diff --git a/ranking_test.go b/ranking_test.go index 575fbb5..d969217 100644 --- a/ranking_test.go +++ b/ranking_test.go @@ -28,7 +28,7 @@ func TestRankingCrud(t *testing.T) { assert.Nil(t, err) defer db.Close() - ranking := Ranking{254, 1, 20, 625, 90, 554, 10, 0.254, 3, 2, 1, 0, 10} + ranking := Ranking{254, 1, 20, 625, 90, 554, 10, 50, 0.254, 3, 2, 1, 0, 10} db.CreateRanking(&ranking) ranking2, err := db.GetRankingForTeam(254) assert.Nil(t, err) @@ -53,7 +53,7 @@ func TestTruncateRankings(t *testing.T) { assert.Nil(t, err) defer db.Close() - ranking := Ranking{254, 1, 20, 625, 90, 554, 10, 0.254, 3, 2, 1, 0, 10} + ranking := Ranking{254, 1, 20, 625, 90, 554, 10, 50, 0.254, 3, 2, 1, 0, 10} db.CreateRanking(&ranking) db.TruncateRankings() ranking2, err := db.GetRankingForTeam(254) @@ -99,12 +99,12 @@ func TestCalculateRankings(t *testing.T) { rankings, err := db.GetAllRankings() assert.Nil(t, err) if assert.Equal(t, 6, len(rankings)) { - assert.Equal(t, Ranking{1, 1, 6, 110, 20, 172, 120, 0.2730468047134829, 1, 0, 2, 0, 3}, rankings[0]) - assert.Equal(t, Ranking{3, 2, 4, 55, 10, 86, 60, 0.9026048462705047, 1, 0, 1, 0, 2}, rankings[1]) - assert.Equal(t, Ranking{4, 3, 2, 77, 45, 122, 85, 0.897169713149801, 0, 1, 1, 0, 2}, rankings[2]) - assert.Equal(t, Ranking{5, 4, 3, 77, 45, 122, 85, 0.2885856518054551, 0, 1, 2, 0, 3}, rankings[3]) - assert.Equal(t, Ranking{2, 5, 3, 55, 10, 86, 60, 0.8497802817628735, 0, 0, 2, 1, 3}, rankings[4]) - assert.Equal(t, Ranking{6, 6, 1, 22, 35, 36, 25, 0.16735444255905835, 0, 1, 1, 0, 2}, rankings[5]) + assert.Equal(t, Ranking{4, 1, 5, 678, 213, 300, 200, 58, 0.897169713149801, 1, 0, 1, 0, 2}, rankings[0]) + assert.Equal(t, Ranking{5, 2, 6, 678, 213, 300, 200, 58, 0.2885856518054551, 1, 0, 2, 0, 3}, rankings[1]) + assert.Equal(t, Ranking{6, 3, 4, 433, 133, 200, 150, 18, 0.16735444255905835, 1, 0, 1, 0, 2}, rankings[2]) + assert.Equal(t, Ranking{1, 4, 4, 435, 160, 200, 100, 80, 0.2730468047134829, 0, 1, 2, 0, 3}, rankings[3]) + assert.Equal(t, Ranking{3, 5, 2, 190, 80, 100, 50, 40, 0.9026048462705047, 0, 1, 1, 0, 2}, rankings[4]) + assert.Equal(t, Ranking{2, 6, 3, 245, 80, 100, 50, 40, 0.8497802817628735, 0, 0, 2, 1, 3}, rankings[5]) } // Test after changing a match result. @@ -117,12 +117,12 @@ func TestCalculateRankings(t *testing.T) { rankings, err = db.GetAllRankings() assert.Nil(t, err) if assert.Equal(t, 6, len(rankings)) { - assert.Equal(t, Ranking{3, 1, 6, 110, 20, 172, 120, 0.6930700440076261, 2, 0, 0, 0, 2}, rankings[0]) - assert.Equal(t, Ranking{1, 2, 8, 165, 30, 258, 180, 0.284824110942037, 2, 0, 1, 0, 3}, rankings[1]) - assert.Equal(t, Ranking{2, 3, 5, 110, 20, 172, 120, 0.4018978925803393, 1, 0, 1, 1, 3}, rankings[2]) - assert.Equal(t, Ranking{4, 4, 2, 77, 45, 122, 85, 0.5102423328818813, 0, 1, 1, 0, 2}, rankings[3]) - assert.Equal(t, Ranking{5, 5, 2, 99, 80, 158, 110, 0.2092018731282357, 0, 2, 1, 0, 3}, rankings[4]) - assert.Equal(t, Ranking{6, 6, 0, 44, 70, 72, 50, 0.24043190328608438, 0, 2, 0, 0, 2}, rankings[5]) + assert.Equal(t, Ranking{6, 1, 6, 866, 266, 400, 300, 36, 0.24043190328608438, 2, 0, 0, 0, 2}, rankings[0]) + assert.Equal(t, Ranking{5, 2, 8, 1111, 346, 500, 350, 76, 0.2092018731282357, 2, 0, 1, 0, 3}, rankings[1]) + assert.Equal(t, Ranking{4, 3, 5, 678, 213, 300, 200, 58, 0.5102423328818813, 1, 0, 1, 0, 2}, rankings[2]) + assert.Equal(t, Ranking{1, 4, 4, 625, 240, 300, 150, 120, 0.284824110942037, 0, 2, 1, 0, 3}, rankings[3]) + assert.Equal(t, Ranking{3, 5, 2, 380, 160, 200, 100, 80, 0.6930700440076261, 0, 2, 0, 0, 2}, rankings[4]) + assert.Equal(t, Ranking{2, 6, 3, 435, 160, 200, 100, 80, 0.4018978925803393, 0, 1, 1, 1, 3}, rankings[5]) } } @@ -135,37 +135,41 @@ func TestSortRankings(t *testing.T) { // Check tiebreakers. rankings := make(map[int]*Ranking) - rankings[1] = &Ranking{1, 0, 50, 50, 50, 50, 50, 0.49, 3, 2, 1, 0, 10} - rankings[2] = &Ranking{2, 0, 50, 50, 50, 50, 50, 0.51, 3, 2, 1, 0, 10} - rankings[3] = &Ranking{3, 0, 50, 50, 50, 50, 49, 0.50, 3, 2, 1, 0, 10} - rankings[4] = &Ranking{4, 0, 50, 50, 50, 50, 51, 0.50, 3, 2, 1, 0, 10} - rankings[5] = &Ranking{5, 0, 50, 50, 50, 49, 50, 0.50, 3, 2, 1, 0, 10} - rankings[6] = &Ranking{6, 0, 50, 50, 50, 51, 50, 0.50, 3, 2, 1, 0, 10} - rankings[7] = &Ranking{7, 0, 50, 50, 49, 50, 50, 0.50, 3, 2, 1, 0, 10} - rankings[8] = &Ranking{8, 0, 50, 50, 51, 50, 50, 0.50, 3, 2, 1, 0, 10} - rankings[9] = &Ranking{9, 0, 50, 49, 50, 50, 50, 0.50, 3, 2, 1, 0, 10} - rankings[10] = &Ranking{10, 0, 50, 51, 50, 50, 50, 0.50, 3, 2, 1, 0, 10} - rankings[11] = &Ranking{11, 0, 49, 50, 50, 50, 50, 0.50, 3, 2, 1, 0, 10} - rankings[12] = &Ranking{12, 0, 51, 50, 50, 50, 50, 0.50, 3, 2, 1, 0, 10} + rankings[1] = &Ranking{1, 0, 50, 50, 50, 50, 50, 50, 0.49, 3, 2, 1, 0, 10} + rankings[2] = &Ranking{2, 0, 50, 50, 50, 50, 50, 50, 0.51, 3, 2, 1, 0, 10} + rankings[3] = &Ranking{3, 0, 50, 50, 50, 50, 50, 49, 0.50, 3, 2, 1, 0, 10} + rankings[4] = &Ranking{4, 0, 50, 50, 50, 50, 50, 51, 0.50, 3, 2, 1, 0, 10} + rankings[5] = &Ranking{5, 0, 50, 50, 50, 50, 49, 50, 0.50, 3, 2, 1, 0, 10} + rankings[6] = &Ranking{6, 0, 50, 50, 50, 50, 51, 50, 0.50, 3, 2, 1, 0, 10} + rankings[7] = &Ranking{7, 0, 50, 50, 50, 49, 50, 50, 0.50, 3, 2, 1, 0, 10} + rankings[8] = &Ranking{8, 0, 50, 50, 50, 51, 50, 50, 0.50, 3, 2, 1, 0, 10} + rankings[9] = &Ranking{9, 0, 50, 50, 49, 50, 50, 50, 0.50, 3, 2, 1, 0, 10} + rankings[10] = &Ranking{10, 0, 50, 50, 51, 50, 50, 50, 0.50, 3, 2, 1, 0, 10} + rankings[11] = &Ranking{11, 0, 50, 49, 50, 50, 50, 50, 0.50, 3, 2, 1, 0, 10} + rankings[12] = &Ranking{12, 0, 50, 51, 50, 50, 50, 50, 0.50, 3, 2, 1, 0, 10} + rankings[13] = &Ranking{13, 0, 49, 50, 50, 50, 50, 50, 0.50, 3, 2, 1, 0, 10} + rankings[14] = &Ranking{14, 0, 51, 50, 50, 50, 50, 50, 0.50, 3, 2, 1, 0, 10} sortedRankings := sortRankings(rankings) - assert.Equal(t, 12, sortedRankings[0].TeamId) - assert.Equal(t, 10, sortedRankings[1].TeamId) - assert.Equal(t, 8, sortedRankings[2].TeamId) - assert.Equal(t, 6, sortedRankings[3].TeamId) - assert.Equal(t, 4, sortedRankings[4].TeamId) - assert.Equal(t, 2, sortedRankings[5].TeamId) - assert.Equal(t, 1, sortedRankings[6].TeamId) - assert.Equal(t, 3, sortedRankings[7].TeamId) - assert.Equal(t, 5, sortedRankings[8].TeamId) - assert.Equal(t, 7, sortedRankings[9].TeamId) - assert.Equal(t, 9, sortedRankings[10].TeamId) - assert.Equal(t, 11, sortedRankings[11].TeamId) + assert.Equal(t, 14, sortedRankings[0].TeamId) + assert.Equal(t, 12, sortedRankings[1].TeamId) + assert.Equal(t, 10, sortedRankings[2].TeamId) + assert.Equal(t, 8, sortedRankings[3].TeamId) + assert.Equal(t, 6, sortedRankings[4].TeamId) + assert.Equal(t, 4, sortedRankings[5].TeamId) + assert.Equal(t, 2, sortedRankings[6].TeamId) + assert.Equal(t, 1, sortedRankings[7].TeamId) + assert.Equal(t, 3, sortedRankings[8].TeamId) + assert.Equal(t, 5, sortedRankings[9].TeamId) + assert.Equal(t, 7, sortedRankings[10].TeamId) + assert.Equal(t, 9, sortedRankings[11].TeamId) + assert.Equal(t, 11, sortedRankings[12].TeamId) + assert.Equal(t, 13, sortedRankings[13].TeamId) // Check with unequal number of matches played. rankings = make(map[int]*Ranking) - rankings[1] = &Ranking{1, 0, 10, 25, 25, 25, 25, 0.49, 3, 2, 1, 0, 5} - rankings[2] = &Ranking{2, 0, 19, 50, 50, 50, 50, 0.51, 3, 2, 1, 0, 9} - rankings[3] = &Ranking{3, 0, 20, 50, 50, 50, 50, 0.51, 3, 2, 1, 0, 10} + rankings[1] = &Ranking{1, 0, 10, 25, 25, 25, 25, 25, 0.49, 3, 2, 1, 0, 5} + rankings[2] = &Ranking{2, 0, 19, 50, 50, 50, 50, 50, 0.51, 3, 2, 1, 0, 9} + rankings[3] = &Ranking{3, 0, 20, 50, 50, 50, 50, 50, 0.51, 3, 2, 1, 0, 10} sortedRankings = sortRankings(rankings) assert.Equal(t, 2, sortedRankings[0].TeamId) assert.Equal(t, 3, sortedRankings[1].TeamId) diff --git a/reports.go b/reports.go index 5f701b9..3db4afc 100644 --- a/reports.go +++ b/reports.go @@ -53,8 +53,8 @@ func 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": 23, "RP": 20, "Auto": 20, "SC": 21, "Goal": 20, - "Defense": 20, "W-L-T": 20, "DQ": 20, "Played": 20} + colWidths := map[string]float64{"Rank": 13, "Team": 23, "RP": 20, "Match": 20, "Auto": 20, "Rotor": 21, + "Touchpad": 20, "Pressure": 20, "W-L-T": 20, "DQ": 20, "Played": 20} rowHeight := 6.5 pdf := gofpdf.New("P", "mm", "Letter", "font") @@ -67,10 +67,11 @@ func 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, "RB", "1", 0, "C", true, 0, "") + pdf.CellFormat(colWidths["Match"], rowHeight, "Match", "1", 0, "C", true, 0, "") pdf.CellFormat(colWidths["Auto"], rowHeight, "Auto", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["SC"], rowHeight, "Scale/Chal.", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Goal"], rowHeight, "Goal", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, "Defense", "1", 0, "C", true, 0, "") + pdf.CellFormat(colWidths["Rotor"], rowHeight, "Rotor", "1", 0, "C", true, 0, "") + pdf.CellFormat(colWidths["Takeoff"], rowHeight, "Takeoff", "1", 0, "C", true, 0, "") + pdf.CellFormat(colWidths["Pressure"], rowHeight, "Pressure", "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 +82,11 @@ func 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["Match"], rowHeight, strconv.Itoa(ranking.MatchPoints), "1", 0, "C", false, 0, "") pdf.CellFormat(colWidths["Auto"], rowHeight, strconv.Itoa(ranking.AutoPoints), "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["SC"], rowHeight, strconv.Itoa(ranking.ScaleChallengePoints), "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Goal"], rowHeight, strconv.Itoa(ranking.GoalPoints), "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, strconv.Itoa(ranking.DefensePoints), "1", 0, "C", false, 0, "") + pdf.CellFormat(colWidths["Rotor"], rowHeight, strconv.Itoa(ranking.RotorPoints), "1", 0, "C", false, 0, "") + pdf.CellFormat(colWidths["Takeoff"], rowHeight, strconv.Itoa(ranking.TakeoffPoints), "1", 0, "C", false, 0, "") + pdf.CellFormat(colWidths["Pressure"], rowHeight, strconv.Itoa(ranking.PressurePoints), "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, "") @@ -236,68 +238,6 @@ func SchedulePdfReportHandler(w http.ResponseWriter, r *http.Request) { } } -// Generates a PDF-formatted report of the defenses schedule. -func DefensesPdfReportHandler(w http.ResponseWriter, r *http.Request) { - if !UserIsReader(w, r) { - return - } - - vars := mux.Vars(r) - matches, err := db.GetMatchesByType(vars["type"]) - if err != nil { - handleWebErr(w, err) - return - } - - // The widths of the table columns in mm, stored here so that they can be referenced for each row. - colWidths := map[string]float64{"Type": 25, "Match": 15, "Defense": 15.5} - rowHeight := 6.5 - - pdf := gofpdf.New("P", "mm", "Letter", "font") - pdf.AddPage() - - // Render table header row. - pdf.SetFont("Arial", "B", 10) - pdf.SetFillColor(220, 220, 220) - pdf.CellFormat(195, rowHeight, "Defenses Schedule - "+eventSettings.Name, "", 1, "C", false, 0, "") - pdf.CellFormat(colWidths["Type"], rowHeight, "Type", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Match"], rowHeight, "Match", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, "Red 1", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, "Red 2", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, "Red 3", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, "Red 4", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, "Red 5", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, "Blue 1", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, "Blue 2", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, "Blue 3", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, "Blue 4", "1", 0, "C", true, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, "Blue 5", "1", 1, "C", true, 0, "") - pdf.SetFont("Arial", "", 10) - for _, match := range matches { - // Render match defenses row. - pdf.CellFormat(colWidths["Type"], rowHeight, match.CapitalizedType(), "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Match"], rowHeight, match.DisplayName, "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, match.RedDefense1, "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, match.RedDefense2, "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, match.RedDefense3, "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, match.RedDefense4, "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, match.RedDefense5, "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, match.BlueDefense1, "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, match.BlueDefense2, "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, match.BlueDefense3, "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, match.BlueDefense4, "1", 0, "C", false, 0, "") - pdf.CellFormat(colWidths["Defense"], rowHeight, match.BlueDefense5, "1", 1, "C", false, 0, "") - } - - // Write out the PDF file as the HTTP response. - w.Header().Set("Content-Type", "application/pdf") - err = pdf.Output(w) - if err != nil { - handleWebErr(w, err) - return - } -} - // Generates a CSV-formatted report of the team list. func TeamsCsvReportHandler(w http.ResponseWriter, r *http.Request) { if !UserIsReader(w, r) { diff --git a/reports_test.go b/reports_test.go index 3067553..277ffb9 100644 --- a/reports_test.go +++ b/reports_test.go @@ -13,17 +13,19 @@ func TestRankingsCsvReport(t *testing.T) { clearDb() defer clearDb() db, _ = OpenDatabase(testDbPath) - ranking1 := Ranking{1114, 2, 18, 625, 90, 554, 10, 0.254, 3, 2, 1, 0, 10} - ranking2 := Ranking{254, 1, 20, 625, 90, 554, 10, 0.254, 1, 2, 3, 0, 10} + eventSettings, _ = db.GetEventSettings() + + ranking1 := Ranking{1114, 2, 18, 625, 90, 554, 10, 50, 0.254, 3, 2, 1, 0, 10} + ranking2 := Ranking{254, 1, 20, 625, 90, 554, 10, 50, 0.254, 1, 2, 3, 0, 10} db.CreateRanking(&ranking1) db.CreateRanking(&ranking2) recorder := getHttpResponse("/reports/csv/rankings") assert.Equal(t, 200, recorder.Code) assert.Equal(t, "text/plain", recorder.HeaderMap["Content-Type"][0]) - expectedBody := "Rank,TeamId,RankingPoints,AutoPoints,ScaleChallengePoints,GoalPoints,DefensePoints," + - "Wins,Losses,Ties,Disqualifications,Played\n1,254,20,625,90,554,10,1,2,3,0,10\n2,1114,18,625,90," + - "554,10,3,2,1,0,10\n\n" + expectedBody := "Rank,TeamId,RankingPoints,MatchPoints,AutoPoints,RotorPoints,TakeoffPoints,PressurePoints," + + "Wins,Losses,Ties,Disqualifications,Played\n1,254,20,625,90,554,10,50,1,2,3,0,10\n2,1114,18,625,90," + + "554,10,50,3,2,1,0,10\n\n" assert.Equal(t, expectedBody, recorder.Body.String()) } @@ -32,8 +34,9 @@ func TestRankingsPdfReport(t *testing.T) { defer clearDb() db, _ = OpenDatabase(testDbPath) eventSettings, _ = db.GetEventSettings() - ranking1 := Ranking{1114, 2, 18, 625, 90, 554, 10, 0.254, 3, 2, 1, 0, 10} - ranking2 := Ranking{254, 1, 20, 625, 90, 554, 10, 0.254, 3, 2, 1, 0, 10} + + ranking1 := Ranking{1114, 2, 18, 625, 90, 554, 10, 50, 0.254, 3, 2, 1, 0, 10} + ranking2 := Ranking{254, 1, 20, 625, 90, 554, 10, 50, 0.254, 3, 2, 1, 0, 10} db.CreateRanking(&ranking1) db.CreateRanking(&ranking2) @@ -47,6 +50,8 @@ func TestScheduleCsvReport(t *testing.T) { clearDb() defer clearDb() db, _ = OpenDatabase(testDbPath) + eventSettings, _ = db.GetEventSettings() + match1 := Match{Type: "qualification", DisplayName: "1", Time: time.Unix(0, 0), Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, Blue3: 6, Blue1IsSurrogate: true, Blue2IsSurrogate: true, Blue3IsSurrogate: true} match2 := Match{Type: "qualification", DisplayName: "2", Time: time.Unix(600, 0), Red1: 7, Red2: 8, Red3: 9, @@ -61,11 +66,9 @@ func TestScheduleCsvReport(t *testing.T) { assert.Equal(t, 200, recorder.Code) assert.Equal(t, "text/plain", recorder.HeaderMap["Content-Type"][0]) expectedBody := "Match,Type,Time,Red1,Red1IsSurrogate,Red2,Red2IsSurrogate,Red3,Red3IsSurrogate,Blue1," + - "Blue1IsSurrogate,Blue2,Blue2IsSurrogate,Blue3,Blue3IsSurrogate,RedDefense1,RedDefense2," + - "RedDefense3,RedDefense4,RedDefense5,BlueDefense1,BlueDefense2,BlueDefense3,BlueDefense4," + - "BlueDefense5\n1,qualification,1969-12-31 16:00:00 -0800 PST,1,false,2,false,3,false,4,true,5,true," + - "6,true,,,,,,,,,,\n2,qualification,1969-12-31 16:10:00 -0800 PST,7,true,8,true,9,true,10,false,11,false,12," + - "false,,,,,,,,,,\n\n" + "Blue1IsSurrogate,Blue2,Blue2IsSurrogate,Blue3,Blue3IsSurrogate\n1,qualification," + + "1969-12-31 16:00:00 -0800 PST,1,false,2,false,3,false,4,true,5,true,6,true\n2,qualification," + + "1969-12-31 16:10:00 -0800 PST,7,true,8,true,9,true,10,false,11,false,12,false\n\n" assert.Equal(t, expectedBody, recorder.Body.String()) } @@ -74,6 +77,7 @@ func TestSchedulePdfReport(t *testing.T) { defer clearDb() db, _ = OpenDatabase(testDbPath) eventSettings, _ = db.GetEventSettings() + match := Match{Type: "practice", DisplayName: "1", Time: time.Unix(0, 0), Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, Blue3: 6, Blue1IsSurrogate: true, Blue2IsSurrogate: true, Blue3IsSurrogate: true} db.CreateMatch(&match) @@ -91,6 +95,8 @@ func TestTeamsCsvReport(t *testing.T) { clearDb() defer clearDb() db, _ = OpenDatabase(testDbPath) + eventSettings, _ = db.GetEventSettings() + team1 := Team{Id: 254, Name: "NASA", Nickname: "The Cheesy Poofs", City: "San Jose", StateProv: "CA", Country: "USA", RookieYear: 1999, RobotName: "Barrage"} team2 := Team{Id: 1114, Name: "GM", Nickname: "Simbotics", City: "St. Catharines", StateProv: "ON", @@ -112,6 +118,7 @@ func TestTeamsPdfReport(t *testing.T) { defer clearDb() db, _ = OpenDatabase(testDbPath) eventSettings, _ = db.GetEventSettings() + team := Team{Id: 254, Name: "NASA", Nickname: "The Cheesy Poofs", City: "San Jose", StateProv: "CA", Country: "USA", RookieYear: 1999, RobotName: "Barrage"} db.CreateTeam(&team) @@ -126,6 +133,8 @@ func TestWpaKeysCsvReport(t *testing.T) { clearDb() defer clearDb() db, _ = OpenDatabase(testDbPath) + eventSettings, _ = db.GetEventSettings() + team1 := Team{Id: 254, WpaKey: "12345678"} team2 := Team{Id: 1114, WpaKey: "9876543210"} db.CreateTeam(&team1) diff --git a/schedule.go b/schedule.go index 6fa2b4e..75180fa 100644 --- a/schedule.go +++ b/schedule.go @@ -88,8 +88,6 @@ func BuildRandomSchedule(teams []Team, scheduleBlocks []ScheduleBlock, matchType } } - randomizeDefenses(matches, numTeams) - return matches, nil } @@ -101,28 +99,3 @@ func countMatches(scheduleBlocks []ScheduleBlock) int { } return numMatches } - -// Fills in a random set of defenses per round of all teams playing. -func randomizeDefenses(matches []Match, numTeams int) { - // Take the floor, to err on the side of a team missing a set of defenses instead of seeing it twice. - matchesPerRound := numTeams / 6 - - var defenseShuffle []int - for i := 0; i < len(matches); i++ { - if i%matchesPerRound == 0 { - // Pick a new set of defenses. - defenseShuffle = rand.Perm(len(placeableDefenses)) - } - - matches[i].RedDefense1 = "LB" - matches[i].RedDefense2 = placeableDefenses[defenseShuffle[0]] - matches[i].RedDefense3 = placeableDefenses[defenseShuffle[1]] - matches[i].RedDefense4 = placeableDefenses[defenseShuffle[2]] - matches[i].RedDefense5 = placeableDefenses[defenseShuffle[3]] - matches[i].BlueDefense1 = "LB" - matches[i].BlueDefense2 = placeableDefenses[defenseShuffle[0]] - matches[i].BlueDefense3 = placeableDefenses[defenseShuffle[1]] - matches[i].BlueDefense4 = placeableDefenses[defenseShuffle[2]] - matches[i].BlueDefense5 = placeableDefenses[defenseShuffle[3]] - } -} diff --git a/schedule_test.go b/schedule_test.go index 46ad52a..6813566 100644 --- a/schedule_test.go +++ b/schedule_test.go @@ -40,7 +40,7 @@ func TestMalformedSchedule(t *testing.T) { scheduleFile.Close() _, err = BuildRandomSchedule(teams, scheduleBlocks, "test") if assert.NotNil(t, err) { - assert.Contains(t, err.Error(), "strconv.ParseInt") + assert.Contains(t, err.Error(), "strconv.Atoi") } } @@ -56,29 +56,17 @@ func TestScheduleTeams(t *testing.T) { matches, err := BuildRandomSchedule(teams, scheduleBlocks, "test") assert.Nil(t, err) assert.Equal(t, Match{Type: "test", DisplayName: "1", Time: time.Unix(0, 0).UTC(), Red1: 115, Red2: 111, - Red3: 108, Blue1: 109, Blue2: 116, Blue3: 117, RedDefense1: "LB", RedDefense2: "RW", RedDefense3: "RT", - RedDefense4: "R", RedDefense5: "M", BlueDefense1: "LB", BlueDefense2: "RW", BlueDefense3: "RT", - BlueDefense4: "R", BlueDefense5: "M"}, matches[0]) + Red3: 108, Blue1: 109, Blue2: 116, Blue3: 117}, matches[0]) assert.Equal(t, Match{Type: "test", DisplayName: "2", Time: time.Unix(60, 0).UTC(), Red1: 114, Red2: 112, - Red3: 103, Blue1: 101, Blue2: 104, Blue3: 118, RedDefense1: "LB", RedDefense2: "RW", RedDefense3: "RT", - RedDefense4: "R", RedDefense5: "M", BlueDefense1: "LB", BlueDefense2: "RW", BlueDefense3: "RT", - BlueDefense4: "R", BlueDefense5: "M"}, matches[1]) + Red3: 103, Blue1: 101, Blue2: 104, Blue3: 118}, matches[1]) assert.Equal(t, Match{Type: "test", DisplayName: "3", Time: time.Unix(120, 0).UTC(), Red1: 110, Red2: 107, - Red3: 105, Blue1: 106, Blue2: 113, Blue3: 102, RedDefense1: "LB", RedDefense2: "RW", RedDefense3: "RT", - RedDefense4: "R", RedDefense5: "M", BlueDefense1: "LB", BlueDefense2: "RW", BlueDefense3: "RT", - BlueDefense4: "R", BlueDefense5: "M"}, matches[2]) + Red3: 105, Blue1: 106, Blue2: 113, Blue3: 102}, matches[2]) assert.Equal(t, Match{Type: "test", DisplayName: "4", Time: time.Unix(180, 0).UTC(), Red1: 112, Red2: 108, - Red3: 109, Blue1: 101, Blue2: 111, Blue3: 103, RedDefense1: "LB", RedDefense2: "RT", RedDefense3: "M", - RedDefense4: "CDF", RedDefense5: "R", BlueDefense1: "LB", BlueDefense2: "RT", BlueDefense3: "M", - BlueDefense4: "CDF", BlueDefense5: "R"}, matches[3]) + Red3: 109, Blue1: 101, Blue2: 111, Blue3: 103}, matches[3]) assert.Equal(t, Match{Type: "test", DisplayName: "5", Time: time.Unix(240, 0).UTC(), Red1: 113, Red2: 117, - Red3: 115, Blue1: 110, Blue2: 114, Blue3: 102, RedDefense1: "LB", RedDefense2: "RT", RedDefense3: "M", - RedDefense4: "CDF", RedDefense5: "R", BlueDefense1: "LB", BlueDefense2: "RT", BlueDefense3: "M", - BlueDefense4: "CDF", BlueDefense5: "R"}, matches[4]) + Red3: 115, Blue1: 110, Blue2: 114, Blue3: 102}, matches[4]) assert.Equal(t, Match{Type: "test", DisplayName: "6", Time: time.Unix(300, 0).UTC(), Red1: 118, Red2: 105, - Red3: 106, Blue1: 107, Blue2: 104, Blue3: 116, RedDefense1: "LB", RedDefense2: "RT", RedDefense3: "M", - RedDefense4: "CDF", RedDefense5: "R", BlueDefense1: "LB", BlueDefense2: "RT", BlueDefense3: "M", - BlueDefense4: "CDF", BlueDefense5: "R"}, matches[5]) + Red3: 106, Blue1: 107, Blue2: 104, Blue3: 116}, matches[5]) // Check with excess room for matches in the schedule. scheduleBlocks = []ScheduleBlock{{time.Unix(0, 0).UTC(), 7, 60}} diff --git a/scoring_display.go b/scoring_display.go index 1b93e8a..45cdf4f 100644 --- a/scoring_display.go +++ b/scoring_display.go @@ -11,7 +11,6 @@ import ( "io" "log" "net/http" - "strconv" "text/template" ) @@ -80,7 +79,7 @@ func ScoringDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) { // Send the various notifications immediately upon connection. data := struct { - Score *RealtimeScore + Score *RealtimeScore AutoCommitted bool }{*score, autoCommitted} err = websocket.Write("score", data) @@ -140,111 +139,36 @@ func ScoringDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) { } switch messageType { - case "defenseCrossed": - position, ok := data.(string) - if !ok { - websocket.WriteError("Defense position is not a string.") - continue - } - intPosition, err := strconv.Atoi(position) - if err != nil { - websocket.WriteError(err.Error()) - continue - } - if (*score).CurrentScore.AutoDefensesCrossed[intPosition-1]+ - (*score).CurrentScore.DefensesCrossed[intPosition-1] < 2 { - if !autoCommitted { - (*score).CurrentScore.AutoDefensesCrossed[intPosition-1]++ - } else { - (*score).CurrentScore.DefensesCrossed[intPosition-1]++ + case "mobility": + if !autoCommitted { + if (*score).CurrentScore.AutoMobility < 3 { + (*score).CurrentScore.AutoMobility++ } } - case "undoDefenseCrossed": - position, ok := data.(string) - if !ok { - websocket.WriteError("Defense position is not a string.") - continue - } - intPosition, err := strconv.Atoi(position) - if err != nil { - websocket.WriteError(err.Error()) - continue - } + case "undoMobility": if !autoCommitted { - if (*score).CurrentScore.AutoDefensesCrossed[intPosition-1] > 0 { - (*score).CurrentScore.AutoDefensesCrossed[intPosition-1]-- + if (*score).CurrentScore.AutoMobility > 0 { + (*score).CurrentScore.AutoMobility-- + } + } + case "gear": + if !autoCommitted { + if (*score).CurrentScore.AutoGears < 3 && (*score).CurrentScore.AutoGears+(*score).CurrentScore.Gears < 13 { + (*score).CurrentScore.AutoGears++ } } else { - if (*score).CurrentScore.DefensesCrossed[intPosition-1] > 0 { - (*score).CurrentScore.DefensesCrossed[intPosition-1]-- + if (*score).CurrentScore.AutoGears+(*score).CurrentScore.Gears < 13 { + (*score).CurrentScore.Gears++ } } - case "autoDefenseReached": + case "undoGear": if !autoCommitted { - if (*score).CurrentScore.AutoDefensesReached < 3 { - (*score).CurrentScore.AutoDefensesReached++ - } - } - case "undoAutoDefenseReached": - if !autoCommitted { - if (*score).CurrentScore.AutoDefensesReached > 0 { - (*score).CurrentScore.AutoDefensesReached-- - } - } - case "highGoal": - if !autoCommitted { - (*score).CurrentScore.AutoHighGoals++ - } else { - (*score).CurrentScore.HighGoals++ - } - case "undoHighGoal": - if !autoCommitted { - if (*score).CurrentScore.AutoHighGoals > 0 { - (*score).CurrentScore.AutoHighGoals-- + if (*score).CurrentScore.AutoGears > 0 { + (*score).CurrentScore.AutoGears-- } } else { - if (*score).CurrentScore.HighGoals > 0 { - (*score).CurrentScore.HighGoals-- - } - } - case "lowGoal": - if !autoCommitted { - (*score).CurrentScore.AutoLowGoals++ - } else { - (*score).CurrentScore.LowGoals++ - } - case "undoLowGoal": - if !autoCommitted { - if (*score).CurrentScore.AutoLowGoals > 0 { - (*score).CurrentScore.AutoLowGoals-- - } - } else { - if (*score).CurrentScore.LowGoals > 0 { - (*score).CurrentScore.LowGoals-- - } - } - case "challenge": - if autoCommitted { - if (*score).CurrentScore.Challenges < 3 { - (*score).CurrentScore.Challenges++ - } - } - case "undoChallenge": - if autoCommitted { - if (*score).CurrentScore.Challenges > 0 { - (*score).CurrentScore.Challenges-- - } - } - case "scale": - if autoCommitted { - if (*score).CurrentScore.Scales < 3 { - (*score).CurrentScore.Scales++ - } - } - case "undoScale": - if autoCommitted { - if (*score).CurrentScore.Scales > 0 { - (*score).CurrentScore.Scales-- + if (*score).CurrentScore.Gears > 0 { + (*score).CurrentScore.Gears-- } } case "commit": @@ -272,7 +196,7 @@ func ScoringDisplayWebsocketHandler(w http.ResponseWriter, r *http.Request) { // Send out the score again after handling the command, as it most likely changed as a result. data = struct { - Score *RealtimeScore + Score *RealtimeScore AutoCommitted bool }{*score, autoCommitted} err = websocket.Write("score", data) diff --git a/scoring_display_test.go b/scoring_display_test.go index 0e39314..5c05fff 100644 --- a/scoring_display_test.go +++ b/scoring_display_test.go @@ -60,70 +60,59 @@ func TestScoringDisplayWebsocket(t *testing.T) { readWebsocketType(t, blueWs, "matchTime") // Send a match worth of scoring commands in. - redWs.Write("defenseCrossed", "2") - blueWs.Write("autoDefenseReached", nil) - redWs.Write("highGoal", nil) - redWs.Write("highGoal", nil) - redWs.Write("lowGoal", nil) - redWs.Write("defenseCrossed", "5") - blueWs.Write("defenseCrossed", "1") - redWs.Write("undoHighGoal", nil) + redWs.Write("gear", nil) + blueWs.Write("gear", nil) + redWs.Write("mobility", nil) + redWs.Write("gear", nil) + redWs.Write("undoGear", nil) + blueWs.Write("mobility", nil) + blueWs.Write("mobility", nil) + blueWs.Write("mobility", nil) + blueWs.Write("mobility", nil) + blueWs.Write("undoMobility", nil) redWs.Write("commit", nil) - blueWs.Write("autoDefenseReached", nil) blueWs.Write("commit", nil) redWs.Write("uncommitAuto", nil) - redWs.Write("autoDefenseReached", nil) - redWs.Write("defenseCrossed", "2") + redWs.Write("gear", nil) redWs.Write("commit", nil) - for i := 0; i < 11; i++ { + for i := 0; i < 8; i++ { + readWebsocketType(t, redWs, "score") + } + for i := 0; i < 7; i++ { + readWebsocketType(t, blueWs, "score") + } + + assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.AutoMobility) + assert.Equal(t, 2, mainArena.redRealtimeScore.CurrentScore.AutoGears) + assert.Equal(t, 1, mainArena.blueRealtimeScore.CurrentScore.AutoGears) + assert.Equal(t, 2, mainArena.blueRealtimeScore.CurrentScore.AutoMobility) + + redWs.Write("gear", nil) + blueWs.Write("gear", nil) + redWs.Write("gear", nil) + redWs.Write("gear", nil) + redWs.Write("gear", nil) + blueWs.Write("gear", nil) + blueWs.Write("gear", nil) + blueWs.Write("undoGear", nil) + redWs.Write("gear", nil) + redWs.Write("undoGear", nil) + redWs.Write("mobility", nil) + for i := 0; i < 7; i++ { readWebsocketType(t, redWs, "score") } for i := 0; i < 4; i++ { readWebsocketType(t, blueWs, "score") } - assert.Equal(t, [5]int{0, 2, 0, 0, 1}, mainArena.redRealtimeScore.CurrentScore.AutoDefensesCrossed) - assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.AutoDefensesReached) - assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.AutoHighGoals) - assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.AutoLowGoals) - assert.Equal(t, [5]int{1, 0, 0, 0, 0}, mainArena.blueRealtimeScore.CurrentScore.AutoDefensesCrossed) - assert.Equal(t, 2, mainArena.blueRealtimeScore.CurrentScore.AutoDefensesReached) - - redWs.Write("defenseCrossed", "2") - blueWs.Write("autoDefenseReached", nil) - redWs.Write("highGoal", nil) - redWs.Write("highGoal", nil) - redWs.Write("lowGoal", nil) - redWs.Write("defenseCrossed", "5") - blueWs.Write("defenseCrossed", "3") - blueWs.Write("challenge", nil) - blueWs.Write("scale", nil) - blueWs.Write("undoChallenge", nil) - redWs.Write("challenge", nil) - redWs.Write("defenseCrossed", "3") - redWs.Write("undoHighGoal", nil) - for i := 0; i < 8; i++ { - readWebsocketType(t, redWs, "score") - } - for i := 0; i < 5; i++ { - readWebsocketType(t, blueWs, "score") - } - // Make sure auto scores haven't changed in teleop. - assert.Equal(t, [5]int{0, 2, 0, 0, 1}, mainArena.redRealtimeScore.CurrentScore.AutoDefensesCrossed) - assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.AutoDefensesReached) - assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.AutoHighGoals) - assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.AutoLowGoals) - assert.Equal(t, [5]int{1, 0, 0, 0, 0}, mainArena.blueRealtimeScore.CurrentScore.AutoDefensesCrossed) - assert.Equal(t, 2, mainArena.blueRealtimeScore.CurrentScore.AutoDefensesReached) + assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.AutoMobility) + assert.Equal(t, 2, mainArena.redRealtimeScore.CurrentScore.AutoGears) + assert.Equal(t, 2, mainArena.blueRealtimeScore.CurrentScore.AutoMobility) + assert.Equal(t, 1, mainArena.blueRealtimeScore.CurrentScore.AutoGears) - assert.Equal(t, [5]int{0, 0, 1, 0, 1}, mainArena.redRealtimeScore.CurrentScore.DefensesCrossed) - assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.HighGoals) - assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.LowGoals) - assert.Equal(t, 1, mainArena.redRealtimeScore.CurrentScore.Challenges) - assert.Equal(t, [5]int{0, 0, 1, 0, 0}, mainArena.blueRealtimeScore.CurrentScore.DefensesCrossed) - assert.Equal(t, 0, mainArena.blueRealtimeScore.CurrentScore.Challenges) - assert.Equal(t, 1, mainArena.blueRealtimeScore.CurrentScore.Scales) + assert.Equal(t, 4, mainArena.redRealtimeScore.CurrentScore.Gears) + assert.Equal(t, 2, mainArena.blueRealtimeScore.CurrentScore.Gears) // Test committing logic. redWs.Write("commitMatch", nil) diff --git a/setup_defense_selection.go b/setup_defense_selection.go deleted file mode 100644 index 2796bab..0000000 --- a/setup_defense_selection.go +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2016 Team 254. All Rights Reserved. -// Author: pat@patfairbank.com (Patrick Fairbank) -// -// Web routes for conducting the team defense selection process. - -package main - -import ( - "fmt" - "net/http" - "strconv" - "text/template" -) - -// Shows the defense selection page. -func DefenseSelectionGetHandler(w http.ResponseWriter, r *http.Request) { - if !UserIsAdmin(w, r) { - return - } - - renderDefenseSelection(w, r, "") -} - -// Updates the cache with the latest input from the client. -func DefenseSelectionPostHandler(w http.ResponseWriter, r *http.Request) { - if !UserIsAdmin(w, r) { - return - } - - matchId, _ := strconv.Atoi(r.PostFormValue("matchId")) - match, err := db.GetMatchById(matchId) - if err != nil { - handleWebErr(w, err) - return - } - - redErr := validateDefenseSelection([]string{r.PostFormValue("redDefense2"), - r.PostFormValue("redDefense3"), r.PostFormValue("redDefense4"), r.PostFormValue("redDefense5")}) - if redErr == nil { - match.RedDefense1 = "LB" - match.RedDefense2 = r.PostFormValue("redDefense2") - match.RedDefense3 = r.PostFormValue("redDefense3") - match.RedDefense4 = r.PostFormValue("redDefense4") - match.RedDefense5 = r.PostFormValue("redDefense5") - } - blueErr := validateDefenseSelection([]string{r.PostFormValue("blueDefense2"), - r.PostFormValue("blueDefense3"), r.PostFormValue("blueDefense4"), r.PostFormValue("blueDefense5")}) - if blueErr == nil { - match.BlueDefense1 = "LB" - match.BlueDefense2 = r.PostFormValue("blueDefense2") - match.BlueDefense3 = r.PostFormValue("blueDefense3") - match.BlueDefense4 = r.PostFormValue("blueDefense4") - match.BlueDefense5 = r.PostFormValue("blueDefense5") - } - if redErr == nil || blueErr == nil { - err = db.SaveMatch(match) - if err != nil { - handleWebErr(w, err) - return - } - mainArena.defenseSelectionNotifier.Notify(nil) - } - if redErr != nil { - renderDefenseSelection(w, r, redErr.Error()) - return - } - if blueErr != nil { - renderDefenseSelection(w, r, blueErr.Error()) - return - } - - http.Redirect(w, r, "/setup/defense_selection", 302) -} - -func renderDefenseSelection(w http.ResponseWriter, r *http.Request, errorMessage string) { - template := template.New("").Funcs(templateHelpers) - _, err := template.ParseFiles("templates/setup_defense_selection.html") - if err != nil { - handleWebErr(w, err) - return - } - - matches, err := db.GetMatchesByType("elimination") - if err != nil { - handleWebErr(w, err) - return - } - var unplayedMatches []Match - for _, match := range matches { - if match.Status != "complete" { - unplayedMatches = append(unplayedMatches, match) - } - } - - data := struct { - *EventSettings - Matches []Match - DefenseNames map[string]string - ErrorMessage string - }{eventSettings, unplayedMatches, defenseNames, errorMessage} - err = template.ExecuteTemplate(w, "setup_defense_selection.html", data) - if err != nil { - handleWebErr(w, err) - return - } -} - -// Takes a slice of the defenses in positions 2-5 and returns an error if they are not valid. -func validateDefenseSelection(defenses []string) error { - // Build map to track which defenses have been used. - defenseCounts := make(map[string]int) - for _, defense := range placeableDefenses { - defenseCounts[defense] = 0 - } - numBlankDefenses := 0 - - for _, defense := range defenses { - if defense == "" { - numBlankDefenses++ - continue - } - - defenseCount, ok := defenseCounts[defense] - if !ok { - return fmt.Errorf("Invalid defense type: %s", defense) - } - if defenseCount != 0 { - return fmt.Errorf("Defense used more than once: %s", defense) - } - defenseCounts[defense]++ - } - - if numBlankDefenses > 0 && numBlankDefenses < 4 { - return fmt.Errorf("Cannot leave defenses blank.") - } - - return nil -} diff --git a/setup_settings.go b/setup_settings.go index 9f7092d..8e8f449 100644 --- a/setup_settings.go +++ b/setup_settings.go @@ -66,15 +66,6 @@ func SettingsPostHandler(w http.ResponseWriter, r *http.Request) { eventSettings.BandwidthMonitoringEnabled = r.PostFormValue("bandwidthMonitoringEnabled") == "on" eventSettings.AdminPassword = r.PostFormValue("adminPassword") eventSettings.ReaderPassword = r.PostFormValue("readerPassword") - eventSettings.RedDefenseLightsAddress = r.PostFormValue("redDefenseLightsAddress") - eventSettings.BlueDefenseLightsAddress = r.PostFormValue("blueDefenseLightsAddress") - - initialTowerStrength, _ := strconv.Atoi(r.PostFormValue("initialTowerStrength")) - if initialTowerStrength < 1 { - renderSettings(w, r, "Initial tower strength must be at least 1.") - return - } - eventSettings.InitialTowerStrength = initialTowerStrength err := db.SaveEventSettings(eventSettings) if err != nil { diff --git a/setup_settings_test.go b/setup_settings_test.go index f3008ad..55806b3 100644 --- a/setup_settings_test.go +++ b/setup_settings_test.go @@ -34,8 +34,7 @@ func TestSetupSettings(t *testing.T) { // Change the settings and check the response. recorder = postHttpResponse("/setup/settings", "name=Chezy Champs&code=CC&displayBackgroundColor=#ff00ff&"+ - "numElimAlliances=16&tbaPublishingEnabled=on&tbaEventCode=2014cc&tbaSecretId=secretId&tbaSecret=tbasec&"+ - "initialTowerStrength=9001") + "numElimAlliances=16&tbaPublishingEnabled=on&tbaEventCode=2014cc&tbaSecretId=secretId&tbaSecret=tbasec") assert.Equal(t, 302, recorder.Code) recorder = getHttpResponse("/setup/settings") assert.Contains(t, recorder.Body.String(), "Chezy Champs") @@ -46,7 +45,6 @@ func TestSetupSettings(t *testing.T) { assert.Contains(t, recorder.Body.String(), "2014cc") assert.Contains(t, recorder.Body.String(), "secretId") assert.Contains(t, recorder.Body.String(), "tbasec") - assert.Contains(t, recorder.Body.String(), "9001") } func TestSetupSettingsInvalidValues(t *testing.T) { diff --git a/tba.go b/tba.go index d48de03..712ac22 100644 --- a/tba.go +++ b/tba.go @@ -313,12 +313,15 @@ func PublishRankings() error { // Build a JSON object of TBA-format rankings. breakdowns := []string{"RP", "Auto", "Scale/Challenge", "Goal", "Defense", "W-L-T"} tbaRankings := make([]TbaRanking, len(rankings)) - for i, ranking := range rankings { - tbaRankings[i] = TbaRanking{getTbaTeam(ranking.TeamId), ranking.Rank, ranking.RankingPoints, - ranking.AutoPoints, ranking.ScaleChallengePoints, ranking.GoalPoints, ranking.DefensePoints, - fmt.Sprintf("%d-%d-%d", ranking.Wins, ranking.Losses, ranking.Ties), ranking.Disqualifications, - ranking.Played} - } + // TODO(patrick): Update for 2017. + /* + for i, ranking := range rankings { + tbaRankings[i] = TbaRanking{getTbaTeam(ranking.TeamId), ranking.Rank, ranking.RankingPoints, + ranking.AutoPoints, ranking.ScaleChallengePoints, ranking.GoalPoints, ranking.DefensePoints, + fmt.Sprintf("%d-%d-%d", ranking.Wins, ranking.Losses, ranking.Ties), ranking.Disqualifications, + ranking.Played} + } + */ jsonBody, err := json.Marshal(map[string]interface{}{"breakdowns": breakdowns, "rankings": tbaRankings}) if err != nil { return err @@ -416,59 +419,60 @@ func getTbaRequest(url string) (*http.Response, error) { } func createTbaScoringBreakdown(match *Match, matchResult *MatchResult, alliance string) *TbaScoreBreakdown { - tbaDefenseNames := map[string]string{"CDF": "A_ChevalDeFrise", "M": "B_Moat", "R": "B_Ramparts", - "RW": "D_RockWall", "RT": "D_RoughTerrain"} - var score *Score - var opponentScore *Score - var scoreSummary *ScoreSummary var breakdown TbaScoreBreakdown - if alliance == "red" { - score = &matchResult.RedScore - opponentScore = &matchResult.BlueScore - scoreSummary = matchResult.RedScoreSummary() - breakdown.Position2 = tbaDefenseNames[match.RedDefense2] - breakdown.Position3 = tbaDefenseNames[match.RedDefense3] - breakdown.Position4 = tbaDefenseNames[match.RedDefense4] - breakdown.Position5 = tbaDefenseNames[match.RedDefense5] - } else { - score = &matchResult.BlueScore - opponentScore = &matchResult.RedScore - scoreSummary = matchResult.BlueScoreSummary() - breakdown.Position2 = tbaDefenseNames[match.BlueDefense2] - breakdown.Position3 = tbaDefenseNames[match.BlueDefense3] - breakdown.Position4 = tbaDefenseNames[match.BlueDefense4] - breakdown.Position5 = tbaDefenseNames[match.BlueDefense5] - } - breakdown.TeleopBouldersLow = score.LowGoals - breakdown.TeleopBouldersHigh = score.HighGoals - breakdown.TeleopTowerCaptured = scoreSummary.Captured - breakdown.TeleopDefensesBreached = scoreSummary.Breached - breakdown.Position1Crossings = score.AutoDefensesCrossed[0] + score.DefensesCrossed[0] - breakdown.Position2Crossings = score.AutoDefensesCrossed[1] + score.DefensesCrossed[1] - breakdown.Position3Crossings = score.AutoDefensesCrossed[2] + score.DefensesCrossed[2] - breakdown.Position4Crossings = score.AutoDefensesCrossed[3] + score.DefensesCrossed[3] - breakdown.Position5Crossings = score.AutoDefensesCrossed[4] + score.DefensesCrossed[4] - breakdown.AutoPoints = scoreSummary.AutoPoints - breakdown.AutoReachPoints = 2 * score.AutoDefensesReached - for _, crossings := range score.AutoDefensesCrossed { - breakdown.AutoCrossingPoints += 10 * crossings - } - breakdown.AutoBoulderPoints = 5*score.AutoLowGoals + 10*score.AutoHighGoals - for _, crossings := range score.DefensesCrossed { - breakdown.TeleopCrossingPoints += 5 * crossings - } - breakdown.TeleopBoulderPoints = 2*score.LowGoals + 5*score.HighGoals - breakdown.TeleopChallengePoints = 5 * score.Challenges - breakdown.TeleopScalePoints = 15 * score.Scales - if match.Type == "elimination" { - if scoreSummary.Breached { - breakdown.BreachPoints = 20 + // TODO(patrick): Update for 2017. + /* + var score *Score + var opponentScore *Score + var scoreSummary *ScoreSummary + if alliance == "red" { + score = &matchResult.RedScore + opponentScore = &matchResult.BlueScore + scoreSummary = matchResult.RedScoreSummary() + breakdown.Position2 = tbaDefenseNames[match.RedDefense2] + breakdown.Position3 = tbaDefenseNames[match.RedDefense3] + breakdown.Position4 = tbaDefenseNames[match.RedDefense4] + breakdown.Position5 = tbaDefenseNames[match.RedDefense5] + } else { + score = &matchResult.BlueScore + opponentScore = &matchResult.RedScore + scoreSummary = matchResult.BlueScoreSummary() + breakdown.Position2 = tbaDefenseNames[match.BlueDefense2] + breakdown.Position3 = tbaDefenseNames[match.BlueDefense3] + breakdown.Position4 = tbaDefenseNames[match.BlueDefense4] + breakdown.Position5 = tbaDefenseNames[match.BlueDefense5] } - if scoreSummary.Captured { - breakdown.CapturePoints = 25 + breakdown.TeleopBouldersLow = score.LowGoals + breakdown.TeleopBouldersHigh = score.HighGoals + breakdown.TeleopTowerCaptured = scoreSummary.Captured + breakdown.TeleopDefensesBreached = scoreSummary.Breached + breakdown.Position1Crossings = score.AutoDefensesCrossed[0] + score.DefensesCrossed[0] + breakdown.Position2Crossings = score.AutoDefensesCrossed[1] + score.DefensesCrossed[1] + breakdown.Position3Crossings = score.AutoDefensesCrossed[2] + score.DefensesCrossed[2] + breakdown.Position4Crossings = score.AutoDefensesCrossed[3] + score.DefensesCrossed[3] + breakdown.Position5Crossings = score.AutoDefensesCrossed[4] + score.DefensesCrossed[4] + breakdown.AutoPoints = scoreSummary.AutoPoints + breakdown.AutoReachPoints = 2 * score.AutoDefensesReached + for _, crossings := range score.AutoDefensesCrossed { + breakdown.AutoCrossingPoints += 10 * crossings } - } - breakdown.FoulPoints = 5 * len(opponentScore.Fouls) - breakdown.TotalPoints = scoreSummary.Score + breakdown.AutoBoulderPoints = 5*score.AutoLowGoals + 10*score.AutoHighGoals + for _, crossings := range score.DefensesCrossed { + breakdown.TeleopCrossingPoints += 5 * crossings + } + breakdown.TeleopBoulderPoints = 2*score.LowGoals + 5*score.HighGoals + breakdown.TeleopChallengePoints = 5 * score.Challenges + breakdown.TeleopScalePoints = 15 * score.Scales + if match.Type == "elimination" { + if scoreSummary.Breached { + breakdown.BreachPoints = 20 + } + if scoreSummary.Captured { + breakdown.CapturePoints = 25 + } + } + breakdown.FoulPoints = 5 * len(opponentScore.Fouls) + breakdown.TotalPoints = scoreSummary.Score + */ return &breakdown } diff --git a/tba_test.go b/tba_test.go index 8fb49bb..5cddaca 100644 --- a/tba_test.go +++ b/tba_test.go @@ -61,26 +61,29 @@ func TestPublishMatches(t *testing.T) { tbaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var reader bytes.Buffer reader.ReadFrom(r.Body) - assert.Equal(t, "[{\"comp_level\":\"qm\",\"set_number\":0,\"match_number\":2,\"alliances\":{\"blue"+ - "\":{\"score\":113,\"teams\":[\"frc10\",\"frc11\",\"frc12\"]},\"red\":{\"score\":156,\"teams\":"+ - "[\"frc7\",\"frc8\",\"frc9\"]}},\"score_breakdown\":{\"blue\":{\"teleopBouldersLow\":3,\"teleop"+ - "BouldersHigh\":4,\"teleopTowerCaptured\":false,\"teleopDefensesBreached\":false,\"position1cro"+ - "ssings\":1,\"position2\":\"\",\"position2crossings\":2,\"position3\":\"\",\"position3crossings"+ - "\":0,\"position4\":\"\",\"position4crossings\":0,\"position5\":\"\",\"position5crossings\":1,"+ - "\"autoPoints\":22,\"autoReachPoints\":2,\"autoCrossingPoints\":10,\"autoBoulderPoints\":10,\"t"+ - "eleopCrossingPoints\":15,\"teleopBoulderPoints\":26,\"teleopChallengePoints\":5,\"teleopScaleP"+ - "oints\":30,\"breachPoints\":0,\"capturePoints\":0,\"foulPoints\":15,\"totalPoints\":113},\"red"+ - "\":{\"teleopBouldersLow\":3,\"teleopBouldersHigh\":11,\"teleopTowerCaptured\":false,\"teleopDe"+ - "fensesBreached\":true,\"position1crossings\":2,\"position2\":\"\",\"position2crossings\":2,\"p"+ - "osition3\":\"\",\"position3crossings\":2,\"position4\":\"\",\"position4crossings\":2,\"positio"+ - "n5\":\"\",\"position5crossings\":1,\"autoPoints\":55,\"autoReachPoints\":0,\"autoCrossingPoint"+ - "s\":30,\"autoBoulderPoints\":25,\"teleopCrossingPoints\":30,\"teleopBoulderPoints\":61,\"teleo"+ - "pChallengePoints\":10,\"teleopScalePoints\":0,\"breachPoints\":0,\"capturePoints\":0,\"foulPoi"+ - "nts\":0,\"totalPoints\":156}},\"time_string\":\"4:10 PM\",\"time_utc\":\"1970-01-01T00:10:00\""+ - "},{\"comp_level\":\"sf\",\"set_number\":2,\"match_number\":2,\"alliances\":{\"blue\":{\"score"+ - "\":null,\"teams\":[\"frc0\",\"frc0\",\"frc0\"]},\"red\":{\"score\":null,\"teams\":[\"frc0\",\""+ - "frc0\",\"frc0\"]}},\"score_breakdown\":null,\"time_string\":\"4:00 PM\",\"time_utc\":\"0001-01"+ - "-01T00:00:00\"}]", reader.String()) + // TODO(patrick): Update for 2017. + /* + assert.Equal(t, "[{\"comp_level\":\"qm\",\"set_number\":0,\"match_number\":2,\"alliances\":{\"blue"+ + "\":{\"score\":113,\"teams\":[\"frc10\",\"frc11\",\"frc12\"]},\"red\":{\"score\":156,\"teams\":"+ + "[\"frc7\",\"frc8\",\"frc9\"]}},\"score_breakdown\":{\"blue\":{\"teleopBouldersLow\":3,\"teleop"+ + "BouldersHigh\":4,\"teleopTowerCaptured\":false,\"teleopDefensesBreached\":false,\"position1cro"+ + "ssings\":1,\"position2\":\"\",\"position2crossings\":2,\"position3\":\"\",\"position3crossings"+ + "\":0,\"position4\":\"\",\"position4crossings\":0,\"position5\":\"\",\"position5crossings\":1,"+ + "\"autoPoints\":22,\"autoReachPoints\":2,\"autoCrossingPoints\":10,\"autoBoulderPoints\":10,\"t"+ + "eleopCrossingPoints\":15,\"teleopBoulderPoints\":26,\"teleopChallengePoints\":5,\"teleopScaleP"+ + "oints\":30,\"breachPoints\":0,\"capturePoints\":0,\"foulPoints\":15,\"totalPoints\":113},\"red"+ + "\":{\"teleopBouldersLow\":3,\"teleopBouldersHigh\":11,\"teleopTowerCaptured\":false,\"teleopDe"+ + "fensesBreached\":true,\"position1crossings\":2,\"position2\":\"\",\"position2crossings\":2,\"p"+ + "osition3\":\"\",\"position3crossings\":2,\"position4\":\"\",\"position4crossings\":2,\"positio"+ + "n5\":\"\",\"position5crossings\":1,\"autoPoints\":55,\"autoReachPoints\":0,\"autoCrossingPoint"+ + "s\":30,\"autoBoulderPoints\":25,\"teleopCrossingPoints\":30,\"teleopBoulderPoints\":61,\"teleo"+ + "pChallengePoints\":10,\"teleopScalePoints\":0,\"breachPoints\":0,\"capturePoints\":0,\"foulPoi"+ + "nts\":0,\"totalPoints\":156}},\"time_string\":\"4:10 PM\",\"time_utc\":\"1970-01-01T00:10:00\""+ + "},{\"comp_level\":\"sf\",\"set_number\":2,\"match_number\":2,\"alliances\":{\"blue\":{\"score"+ + "\":null,\"teams\":[\"frc0\",\"frc0\",\"frc0\"]},\"red\":{\"score\":null,\"teams\":[\"frc0\",\""+ + "frc0\",\"frc0\"]}},\"score_breakdown\":null,\"time_string\":\"4:00 PM\",\"time_utc\":\"0001-01"+ + "-01T00:00:00\"}]", reader.String()) + */ })) defer tbaServer.Close() tbaBaseUrl = tbaServer.URL @@ -96,18 +99,21 @@ func TestPublishRankings(t *testing.T) { assert.Nil(t, err) defer db.Close() eventSettings, _ = db.GetEventSettings() - db.CreateRanking(&Ranking{1114, 2, 20, 625, 90, 554, 10, 0.254, 3, 2, 1, 0, 10}) - db.CreateRanking(&Ranking{254, 1, 20, 625, 90, 554, 10, 0.254, 1, 2, 3, 0, 10}) + db.CreateRanking(&Ranking{1114, 2, 20, 625, 90, 554, 10, 50, 0.254, 3, 2, 1, 0, 10}) + db.CreateRanking(&Ranking{254, 1, 20, 625, 90, 554, 10, 50, 0.254, 1, 2, 3, 0, 10}) // Mock the TBA server. tbaServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { var reader bytes.Buffer reader.ReadFrom(r.Body) - assert.Equal(t, "{\"breakdowns\":[\"RP\",\"Auto\",\"Scale/Challenge\",\"Goal\",\"Defense\",\"W-L-T"+ - "\"],\"rankings\":[{\"team_key\":\"frc254\",\"rank\":1,\"RP\":20,\"Auto\":625,\"Scale/Challenge"+ - "\":90,\"Goal\":554,\"Defense\":10,\"W-L-T\":\"1-2-3\",\"dqs\":0,\"played\":10},{\"team_key\":"+ - "\"frc1114\",\"rank\":2,\"RP\":20,\"Auto\":625,\"Scale/Challenge\":90,\"Goal\":554,\"Defense\":"+ - "10,\"W-L-T\":\"3-2-1\",\"dqs\":0,\"played\":10}]}", reader.String()) + // TODO(patrick): Update for 2017. + /* + assert.Equal(t, "{\"breakdowns\":[\"RP\",\"Auto\",\"Scale/Challenge\",\"Goal\",\"Defense\",\"W-L-T"+ + "\"],\"rankings\":[{\"team_key\":\"frc254\",\"rank\":1,\"RP\":20,\"Auto\":625,\"Scale/Challenge"+ + "\":90,\"Goal\":554,\"Defense\":10,\"W-L-T\":\"1-2-3\",\"dqs\":0,\"played\":10},{\"team_key\":"+ + "\"frc1114\",\"rank\":2,\"RP\":20,\"Auto\":625,\"Scale/Challenge\":90,\"Goal\":554,\"Defense\":"+ + "10,\"W-L-T\":\"3-2-1\",\"dqs\":0,\"played\":10}]}", reader.String()) + */ })) defer tbaServer.Close() tbaBaseUrl = tbaServer.URL diff --git a/templates/base.html b/templates/base.html index 1427618..604ca4f 100644 --- a/templates/base.html +++ b/templates/base.html @@ -44,7 +44,6 @@
  • Alliance Selection
  • Lower Thirds
  • Sponsor Slides
  • -
  • Playoff Defense Selection
  • Team List
  • Practice Schedule
  • -
  • Practice Defenses
  • Qualification Schedule
  • -
  • Qualification Defenses
  • Playoff Schedule
  • -
  • Playoff Defenses
  • Standings
  • diff --git a/templates/fta_display.html b/templates/fta_display.html index 3ac0e54..cf2dd09 100644 --- a/templates/fta_display.html +++ b/templates/fta_display.html @@ -41,44 +41,6 @@
    -
    -
    - Upcoming Defenses - - - - - - - - - - - - - {{range $match := .UpcomingMatches}} - - - - - - - - - - - - - - - - - - {{end}} - -

    Match

    1

    2

    3

    4

    5

    {{$match.DisplayName}}{{index $.DefenseNames $match.RedDefense1}}{{index $.DefenseNames $match.RedDefense2}}{{index $.DefenseNames $match.RedDefense3}}{{index $.DefenseNames $match.RedDefense4}}{{index $.DefenseNames $match.RedDefense5}}
     {{index $.DefenseNames $match.BlueDefense1}}{{index $.DefenseNames $match.BlueDefense2}}{{index $.DefenseNames $match.BlueDefense3}}{{index $.DefenseNames $match.BlueDefense4}}{{index $.DefenseNames $match.BlueDefense5}}
    -
    -
    {{end}} {{define "script"}} diff --git a/templates/rankings.csv b/templates/rankings.csv index f815e68..84372d0 100644 --- a/templates/rankings.csv +++ b/templates/rankings.csv @@ -1,3 +1,3 @@ -Rank,TeamId,RankingPoints,AutoPoints,ScaleChallengePoints,GoalPoints,DefensePoints,Wins,Losses,Ties,Disqualifications,Played -{{range $ranking := .}}{{$ranking.Rank}},{{$ranking.TeamId}},{{$ranking.RankingPoints}},{{$ranking.AutoPoints}},{{$ranking.ScaleChallengePoints}},{{$ranking.GoalPoints}},{{$ranking.DefensePoints}},{{$ranking.Wins}},{{$ranking.Losses}},{{$ranking.Ties}},{{$ranking.Disqualifications}},{{$ranking.Played}} +Rank,TeamId,RankingPoints,MatchPoints,AutoPoints,RotorPoints,TakeoffPoints,PressurePoints,Wins,Losses,Ties,Disqualifications,Played +{{range $ranking := .}}{{$ranking.Rank}},{{$ranking.TeamId}},{{$ranking.RankingPoints}},{{$ranking.MatchPoints}},{{$ranking.AutoPoints}},{{$ranking.RotorPoints}},{{$ranking.TakeoffPoints}},{{$ranking.PressurePoints}},{{$ranking.Wins}},{{$ranking.Losses}},{{$ranking.Ties}},{{$ranking.Disqualifications}},{{$ranking.Played}} {{end}} diff --git a/templates/schedule.csv b/templates/schedule.csv index f13d359..e9dc85f 100644 --- a/templates/schedule.csv +++ b/templates/schedule.csv @@ -1,3 +1,3 @@ -Match,Type,Time,Red1,Red1IsSurrogate,Red2,Red2IsSurrogate,Red3,Red3IsSurrogate,Blue1,Blue1IsSurrogate,Blue2,Blue2IsSurrogate,Blue3,Blue3IsSurrogate,RedDefense1,RedDefense2,RedDefense3,RedDefense4,RedDefense5,BlueDefense1,BlueDefense2,BlueDefense3,BlueDefense4,BlueDefense5 -{{range $match := .}}{{$match.DisplayName}},{{$match.Type}},{{$match.Time.Local}},{{$match.Red1}},{{$match.Red1IsSurrogate}},{{$match.Red2}},{{$match.Red2IsSurrogate}},{{$match.Red3}},{{$match.Red3IsSurrogate}},{{$match.Blue1}},{{$match.Blue1IsSurrogate}},{{$match.Blue2}},{{$match.Blue2IsSurrogate}},{{$match.Blue3}},{{$match.Blue3IsSurrogate}},{{$match.RedDefense1}},{{$match.RedDefense2}},{{$match.RedDefense3}},{{$match.RedDefense4}},{{$match.RedDefense5}},{{$match.BlueDefense1}},{{$match.BlueDefense2}},{{$match.BlueDefense3}},{{$match.BlueDefense4}},{{$match.BlueDefense5}} +Match,Type,Time,Red1,Red1IsSurrogate,Red2,Red2IsSurrogate,Red3,Red3IsSurrogate,Blue1,Blue1IsSurrogate,Blue2,Blue2IsSurrogate,Blue3,Blue3IsSurrogate +{{range $match := .}}{{$match.DisplayName}},{{$match.Type}},{{$match.Time.Local}},{{$match.Red1}},{{$match.Red1IsSurrogate}},{{$match.Red2}},{{$match.Red2IsSurrogate}},{{$match.Red3}},{{$match.Red3IsSurrogate}},{{$match.Blue1}},{{$match.Blue1IsSurrogate}},{{$match.Blue2}},{{$match.Blue2IsSurrogate}},{{$match.Blue3}},{{$match.Blue3IsSurrogate}} {{end}} diff --git a/templates/setup_defense_selection.html b/templates/setup_defense_selection.html deleted file mode 100644 index 3ef9090..0000000 --- a/templates/setup_defense_selection.html +++ /dev/null @@ -1,80 +0,0 @@ -{{/* - Copyright 2016 Team 254. All Rights Reserved. - Author: pat@patfairbank.com (Patrick Fairbank) - - UI for controlling the team defense selection process. -*/}} - - - - Playoff Defense Selection - {{.EventSettings.Name}} - Cheesy Arena - - - - - - - - - -
    -
    - Playoff Defense Selection - {{if .ErrorMessage}} -
    - - {{.ErrorMessage}} -
    - {{end}} - {{range $match := .Matches}} -
    -
    - - {{$match.DisplayName}} -
    -

    {{$match.Red1}}, {{$match.Red2}}, {{$match.Red3}}

    -
    - -
    - {{template "defense" dict "name" "redDefense2" "value" $match.RedDefense2 "defenseNames" $.DefenseNames}} - {{template "defense" dict "name" "redDefense3" "value" $match.RedDefense3 "defenseNames" $.DefenseNames}} - {{template "defense" dict "name" "redDefense4" "value" $match.RedDefense4 "defenseNames" $.DefenseNames}} - {{template "defense" dict "name" "redDefense5" "value" $match.RedDefense5 "defenseNames" $.DefenseNames}} -
    -
    -

    {{$match.Blue1}}, {{$match.Blue2}}, {{$match.Blue3}}

    -
    - -
    - {{template "defense" dict "name" "blueDefense2" "value" $match.BlueDefense2 "defenseNames" $.DefenseNames}} - {{template "defense" dict "name" "blueDefense3" "value" $match.BlueDefense3 "defenseNames" $.DefenseNames}} - {{template "defense" dict "name" "blueDefense4" "value" $match.BlueDefense4 "defenseNames" $.DefenseNames}} - {{template "defense" dict "name" "blueDefense5" "value" $match.BlueDefense5 "defenseNames" $.DefenseNames}} -
    -
    - -
    -
    -
    - {{end}} -
    -
    - - - -{{define "defense"}} -
    - -
    -{{end}} diff --git a/templates/setup_settings.html b/templates/setup_settings.html index 558ff37..e4b9843 100644 --- a/templates/setup_settings.html +++ b/templates/setup_settings.html @@ -205,21 +205,6 @@ -
    - LED Controllers -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    2016 Game Rules
    diff --git a/web.go b/web.go index 952aab2..3019545 100644 --- a/web.go +++ b/web.go @@ -191,8 +191,6 @@ func newHandler() http.Handler { router.HandleFunc("/setup/sponsor_slides", SponsorSlidesGetHandler).Methods("GET") router.HandleFunc("/setup/sponsor_slides", SponsorSlidesPostHandler).Methods("POST") router.HandleFunc("/api/sponsor_slides", SponsorSlidesApiHandler).Methods("GET") - router.HandleFunc("/setup/defense_selection", DefenseSelectionGetHandler).Methods("GET") - router.HandleFunc("/setup/defense_selection", DefenseSelectionPostHandler).Methods("POST") router.HandleFunc("/match_play", MatchPlayHandler).Methods("GET") router.HandleFunc("/match_play/{matchId}/load", MatchPlayLoadHandler).Methods("GET") router.HandleFunc("/match_play/{matchId}/show_result", MatchPlayShowResultHandler).Methods("GET") @@ -204,7 +202,6 @@ func newHandler() http.Handler { router.HandleFunc("/reports/pdf/rankings", RankingsPdfReportHandler).Methods("GET") router.HandleFunc("/reports/csv/schedule/{type}", ScheduleCsvReportHandler).Methods("GET") router.HandleFunc("/reports/pdf/schedule/{type}", SchedulePdfReportHandler).Methods("GET") - router.HandleFunc("/reports/pdf/defenses/{type}", DefensesPdfReportHandler).Methods("GET") router.HandleFunc("/reports/csv/teams", TeamsCsvReportHandler).Methods("GET") router.HandleFunc("/reports/pdf/teams", TeamsPdfReportHandler).Methods("GET") router.HandleFunc("/reports/csv/wpa_keys", WpaKeysCsvReportHandler).Methods("GET")