diff --git a/field/arena_notifiers.go b/field/arena_notifiers.go index 61f5b73..83babed 100644 --- a/field/arena_notifiers.go +++ b/field/arena_notifiers.go @@ -201,14 +201,16 @@ func (arena *Arena) generateScorePostedMessage() interface{} { BlueScoreSummary *game.ScoreSummary RedFouls []game.Foul BlueFouls []game.Foul + RulesViolated map[int]*game.Rule RedCards map[string]string BlueCards map[string]string SeriesStatus string SeriesLeader string }{arena.SavedMatch.CapitalizedType(), arena.SavedMatch, arena.SavedMatchResult.RedScoreSummary(), - arena.SavedMatchResult.BlueScoreSummary(), populateFoulDescriptions(arena.SavedMatchResult.RedScore.Fouls), - populateFoulDescriptions(arena.SavedMatchResult.BlueScore.Fouls), arena.SavedMatchResult.RedCards, - arena.SavedMatchResult.BlueCards, seriesStatus, seriesLeader} + arena.SavedMatchResult.BlueScoreSummary(), arena.SavedMatchResult.RedScore.Fouls, + arena.SavedMatchResult.BlueScore.Fouls, + getRulesViolated(arena.SavedMatchResult.RedScore.Fouls, arena.SavedMatchResult.BlueScore.Fouls), + arena.SavedMatchResult.RedCards, arena.SavedMatchResult.BlueCards, seriesStatus, seriesLeader} } func (arena *Arena) generateScoringStatusMessage() interface{} { @@ -236,17 +238,14 @@ func getAudienceAllianceScoreFields(allianceScore *RealtimeScore, return fields } -// Copy the description from the rules to the fouls so that they are available to the announcer. -func populateFoulDescriptions(fouls []game.Foul) []game.Foul { - foulsCopy := make([]game.Foul, len(fouls)) - copy(foulsCopy, fouls) - for i := range foulsCopy { - for _, rule := range game.Rules { - if foulsCopy[i].RuleNumber == rule.RuleNumber { - foulsCopy[i].Description = rule.Description - break - } - } +// Produce a map of rules that were violated by either alliance so that they are available to the announcer. +func getRulesViolated(redFouls, blueFouls []game.Foul) map[int]*game.Rule { + rules := make(map[int]*game.Rule) + for _, foul := range redFouls { + rules[foul.RuleId] = game.GetRuleById(foul.RuleId) } - return foulsCopy + for _, foul := range blueFouls { + rules[foul.RuleId] = game.GetRuleById(foul.RuleId) + } + return rules } diff --git a/game/foul.go b/game/foul.go index 4f82748..7b54412 100644 --- a/game/foul.go +++ b/game/foul.go @@ -1,56 +1,27 @@ // Copyright 2017 Team 254. All Rights Reserved. // Author: pat@patfairbank.com (Patrick Fairbank) // -// Model of a foul and game-specific rules. +// Model of a foul. package game type Foul struct { - Rule + RuleId int TeamId int TimeInMatchSec float64 } -type Rule struct { - RuleNumber string - IsTechnical bool - IsRankingPoint bool - Description string -} - -// All rules from the 2018 game that carry point penalties. -var Rules = []Rule{ - {"S6", false, false, "DRIVE TEAMS may not extend any body part into the CARGO Chute. Momentary encroachment into the Chute is an exception to this rule."}, - {"C8", false, false, "Strategies clearly aimed at forcing the opposing ALLIANCE to violate a rule are not in the spirit of FIRST Robotics Competition and not allowed."}, - {"G3", true, false, "During the SANDSTORM PERIOD, a ROBOT may not cross the FIELD such that its BUMPERS break the plane defined by their opponent’s CARGO SHIP LINE."}, - {"G4", false, false, "ROBOTS may not have greater-than-momentary or repeated control, i.e. exercise greater-than-momentary or repeated influence, of more than one (1) GAME PIECE at a time, either directly or transitively through other objects."}, - {"G5", false, true, "A ROBOT may not remove a GAME PIECE from an opponents’ ROCKET/CARGO SHIP."}, - {"G7", false, false, "ROBOTS may not intentionally eject GAME PIECES from the FIELD."}, - {"G8", false, false, "ROBOTS may not deliberately use GAME PIECES in an attempt to ease or amplify the challenge associated with FIELD elements."}, - {"G9", false, false, "No more than one ROBOT may be positioned such that its BUMPERS are completely beyond the opponent’s CARGO SHIP LINE."}, - {"G9", true, false, "No more than one ROBOT may be positioned such that its BUMPERS are completely beyond the opponent’s CARGO SHIP LINE."}, - {"G10", false, false, "No part of a ROBOT, except its BUMPERS, may be outside its FRAME PERIMETER if its BUMPERS are completely beyond its opponent’s CARGO SHIP LINE."}, - {"G10", true, false, "No part of a ROBOT, except its BUMPERS, may be outside its FRAME PERIMETER if its BUMPERS are completely beyond its opponent’s CARGO SHIP LINE."}, - {"G12", false, false, "A ROBOT may not break the vertical plane above the ALLIANCE STATION WALL or damage the SANDSTORM."}, - {"G13", false, false, "A ROBOT may not contact an opponent ROBOT if that opponent ROBOT’S BUMPERS are fully in their HAB ZONE."}, - {"G15", false, false, "DRIVE TEAMS, ROBOTS, and OPERATOR CONSOLES are prohibited from the following actions with regards to interaction with ARENA elements: grabbing, grasping, attaching to, hanging, deforming, becoming entangled, damaging, and repositioning GAME PIECE holders."}, - {"G16", false, true, "During Qualification MATCHES, ROBOTS may not contact opponents’ ROCKETS starting at T-minus 20s."}, - {"G17", false, false, "Fallen (i.e. tipped over) ROBOTS attempting to right themselves (either by themselves or with assistance from a partner ROBOT) have one ten (10) second grace period in which they may not be contacted by an opponent ROBOT."}, - {"G18", false, false, "ROBOTS may not pin an opponent’s ROBOT for more than five (5) seconds."}, - {"G18", true, false, "ROBOTS may not pin an opponent’s ROBOT for more than five (5) seconds."}, - {"G19", true, false, "Strategies aimed at the destruction or inhibition of ROBOTS via attachment, damage, tipping, or entanglements are not allowed."}, - {"G20", true, false, "Initiating deliberate or damaging contact with an opponent ROBOT on or inside the vertical extension of its FRAME PERIMETER, including transitively through a GAME PIECE, is not allowed."}, - {"G23", false, false, "BUMPERS must be in the BUMPER ZONE during the MATCH unless a ROBOT is completely in its HAB ZONE or supported by a ROBOT completely in its HAB ZONE."}, - {"G24", false, false, "ROBOTS may not extend more than 30 in (~76 cm). beyond their FRAME PERIMETER."}, - {"H6", false, false, "During the MATCH, DRIVERS, COACHES, and HUMAN PLAYERS may not contact anything outside the ALLIANCE STATION and TECHNICIANS may not contact anything outside their designated area."}, - {"H7", false, false, "During the MATCH, team members may only enter GAME PIECES on to the FIELD through their LOADING STATIONS."}, - {"H8", false, false, "During a MATCH, COACHES may not touch GAME PIECES unless for safety purposes."}, - {"H9", true, false, "During the SANDSTORM PERIOD, COACHES, DRIVERS, HUMAN PLAYERS, and any part of the OPERATOR CONSOLE may not break the vertical planes defined by the STARTING LINES, unless for safety purposes."}, - {"H10", true, false, "During the SANDSTORM PERIOD, COACHES, DRIVERS, and HUMAN PLAYERS may not look over the top of the ALLIANCE WALL down to the FIELD to overcome the effect of the SANDSTORM."}, +// Returns the rule for which the foul was assigned. +func (foul *Foul) Rule() *Rule { + return GetRuleById(foul.RuleId) } +// Returns the number of points that the foul adds to the opposing alliance's score. func (foul *Foul) PointValue() int { - if foul.IsTechnical { + if foul.Rule() == nil { + return 0 + } + if foul.Rule().IsTechnical { return 10 } else { return 3 diff --git a/game/rule.go b/game/rule.go new file mode 100644 index 0000000..7ef608b --- /dev/null +++ b/game/rule.go @@ -0,0 +1,62 @@ +// Copyright 2020 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) +// +// Model of a game-specific rule. + +package game + +type Rule struct { + Id int + RuleNumber string + IsTechnical bool + IsRankingPoint bool + Description string +} + +// All rules from the 2018 game that carry point penalties. +var rules = []*Rule{ + {1, "S6", false, false, "DRIVE TEAMS may not extend any body part into the CARGO Chute. Momentary encroachment into the Chute is an exception to this rule."}, + {2, "C8", false, false, "Strategies clearly aimed at forcing the opposing ALLIANCE to violate a rule are not in the spirit of FIRST Robotics Competition and not allowed."}, + {3, "G3", true, false, "During the SANDSTORM PERIOD, a ROBOT may not cross the FIELD such that its BUMPERS break the plane defined by their opponent’s CARGO SHIP LINE."}, + {4, "G4", false, false, "ROBOTS may not have greater-than-momentary or repeated control, i.e. exercise greater-than-momentary or repeated influence, of more than one (1) GAME PIECE at a time, either directly or transitively through other objects."}, + {5, "G5", false, true, "A ROBOT may not remove a GAME PIECE from an opponents’ ROCKET/CARGO SHIP."}, + {6, "G7", false, false, "ROBOTS may not intentionally eject GAME PIECES from the FIELD."}, + {7, "G8", false, false, "ROBOTS may not deliberately use GAME PIECES in an attempt to ease or amplify the challenge associated with FIELD elements."}, + {8, "G9", false, false, "No more than one ROBOT may be positioned such that its BUMPERS are completely beyond the opponent’s CARGO SHIP LINE."}, + {9, "G9", true, false, "No more than one ROBOT may be positioned such that its BUMPERS are completely beyond the opponent’s CARGO SHIP LINE."}, + {10, "G10", false, false, "No part of a ROBOT, except its BUMPERS, may be outside its FRAME PERIMETER if its BUMPERS are completely beyond its opponent’s CARGO SHIP LINE."}, + {11, "G10", true, false, "No part of a ROBOT, except its BUMPERS, may be outside its FRAME PERIMETER if its BUMPERS are completely beyond its opponent’s CARGO SHIP LINE."}, + {12, "G12", false, false, "A ROBOT may not break the vertical plane above the ALLIANCE STATION WALL or damage the SANDSTORM."}, + {13, "G13", false, false, "A ROBOT may not contact an opponent ROBOT if that opponent ROBOT’S BUMPERS are fully in their HAB ZONE."}, + {14, "G15", false, false, "DRIVE TEAMS, ROBOTS, and OPERATOR CONSOLES are prohibited from the following actions with regards to interaction with ARENA elements: grabbing, grasping, attaching to, hanging, deforming, becoming entangled, damaging, and repositioning GAME PIECE holders."}, + {15, "G16", false, true, "During Qualification MATCHES, ROBOTS may not contact opponents’ ROCKETS starting at T-minus 20s."}, + {16, "G17", false, false, "Fallen (i.e. tipped over) ROBOTS attempting to right themselves (either by themselves or with assistance from a partner ROBOT) have one ten (10) second grace period in which they may not be contacted by an opponent ROBOT."}, + {17, "G18", false, false, "ROBOTS may not pin an opponent’s ROBOT for more than five (5) seconds."}, + {18, "G18", true, false, "ROBOTS may not pin an opponent’s ROBOT for more than five (5) seconds."}, + {19, "G19", true, false, "Strategies aimed at the destruction or inhibition of ROBOTS via attachment, damage, tipping, or entanglements are not allowed."}, + {20, "G20", true, false, "Initiating deliberate or damaging contact with an opponent ROBOT on or inside the vertical extension of its FRAME PERIMETER, including transitively through a GAME PIECE, is not allowed."}, + {21, "G23", false, false, "BUMPERS must be in the BUMPER ZONE during the MATCH unless a ROBOT is completely in its HAB ZONE or supported by a ROBOT completely in its HAB ZONE."}, + {22, "G24", false, false, "ROBOTS may not extend more than 30 in (~76 cm). beyond their FRAME PERIMETER."}, + {23, "H6", false, false, "During the MATCH, DRIVERS, COACHES, and HUMAN PLAYERS may not contact anything outside the ALLIANCE STATION and TECHNICIANS may not contact anything outside their designated area."}, + {24, "H7", false, false, "During the MATCH, team members may only enter GAME PIECES on to the FIELD through their LOADING STATIONS."}, + {25, "H8", false, false, "During a MATCH, COACHES may not touch GAME PIECES unless for safety purposes."}, + {26, "H9", true, false, "During the SANDSTORM PERIOD, COACHES, DRIVERS, HUMAN PLAYERS, and any part of the OPERATOR CONSOLE may not break the vertical planes defined by the STARTING LINES, unless for safety purposes."}, + {27, "H10", true, false, "During the SANDSTORM PERIOD, COACHES, DRIVERS, and HUMAN PLAYERS may not look over the top of the ALLIANCE WALL down to the FIELD to overcome the effect of the SANDSTORM."}, +} +var ruleMap map[int]*Rule + +// Returns the rule having the given ID, or nil if no such rule exists. +func GetRuleById(id int) *Rule { + return GetAllRules()[id] +} + +// Returns a slice of all defined rules that carry point penalties. +func GetAllRules() map[int]*Rule { + if ruleMap == nil { + ruleMap = make(map[int]*Rule, len(rules)) + for _, rule := range rules { + ruleMap[rule.Id] = rule + } + } + return ruleMap +} diff --git a/game/rule_test.go b/game/rule_test.go new file mode 100644 index 0000000..e7e4734 --- /dev/null +++ b/game/rule_test.go @@ -0,0 +1,24 @@ +// Copyright 2020 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) + +package game + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func TestGetRuleById(t *testing.T) { + assert.Nil(t, GetRuleById(0)) + assert.Equal(t, rules[0], GetRuleById(1)) + assert.Equal(t, rules[20], GetRuleById(21)) + assert.Nil(t, GetRuleById(1000)) +} + +func TestGetAllRules(t *testing.T) { + allRules := GetAllRules() + assert.Equal(t, len(rules), len(allRules)) + for _, rule := range rules { + assert.Equal(t, rule, allRules[rule.Id]) + } +} diff --git a/game/score.go b/game/score.go index b4e4c34..d95d9ef 100644 --- a/game/score.go +++ b/game/score.go @@ -96,7 +96,10 @@ func (score *Score) Summarize(opponentFouls []Foul) *ScoreSummary { } else { // Check for the opponent fouls that automatically trigger the ranking point. for _, foul := range opponentFouls { - if foul.IsRankingPoint { + if foul.Rule() == nil { + continue + } + if foul.Rule().IsRankingPoint { summary.CompleteRocket = true break } diff --git a/game/score_test.go b/game/score_test.go index 707485d..64505e9 100644 --- a/game/score_test.go +++ b/game/score_test.go @@ -32,6 +32,10 @@ func TestScoreSummary(t *testing.T) { assert.Equal(t, false, blueSummary.CompleteRocket) assert.Equal(t, true, blueSummary.HabDocking) + // Test invalid foul. + redScore.Fouls[0].RuleId = 0 + assert.Equal(t, 13, blueScore.Summarize(redScore.Fouls).FoulPoints) + // Test rocket completion boundary conditions. assert.Equal(t, true, redScore.Summarize(blueScore.Fouls).CompleteRocket) redScore.RocketFarLeftBays[1] = BayHatch @@ -41,7 +45,7 @@ func TestScoreSummary(t *testing.T) { assert.Equal(t, true, redScore.Summarize(blueScore.Fouls).CompleteRocket) redScore.RocketNearLeftBays[2] = BayHatch assert.Equal(t, false, redScore.Summarize(blueScore.Fouls).CompleteRocket) - redScore.Fouls[1].IsRankingPoint = true + redScore.Fouls[2].RuleId = 15 assert.Equal(t, true, redScore.Summarize(redScore.Fouls).CompleteRocket) // Test hab docking boundary conditions. @@ -119,12 +123,7 @@ func TestScoreEquals(t *testing.T) { assert.False(t, score2.Equals(score1)) score2 = TestScore1() - score2.Fouls[0].RuleNumber = "G1000" - assert.False(t, score1.Equals(score2)) - assert.False(t, score2.Equals(score1)) - - score2 = TestScore1() - score2.Fouls[0].IsTechnical = !score2.Fouls[0].IsTechnical + score2.Fouls[0].RuleId = 1 assert.False(t, score1.Equals(score2)) assert.False(t, score2.Equals(score1)) diff --git a/game/test_helpers.go b/game/test_helpers.go index aa474bf..8c6b665 100644 --- a/game/test_helpers.go +++ b/game/test_helpers.go @@ -7,9 +7,9 @@ package game func TestScore1() *Score { fouls := []Foul{ - {Rule{"G18", true, false, ""}, 25, 150}, - {Rule{"G20", true, false, ""}, 1868, 0}, - {Rule{"G22", false, false, ""}, 25, 25.2}, + {18, 25, 150}, + {20, 1868, 0}, + {21, 25, 25.2}, } return &Score{ RobotStartLevels: [3]int{2, 1, 2}, diff --git a/static/js/announcer_display.js b/static/js/announcer_display.js index 71a167a..aa889b9 100644 --- a/static/js/announcer_display.js +++ b/static/js/announcer_display.js @@ -50,11 +50,18 @@ var handleRealtimeScore = function(data) { // Handles a websocket message to populate the final score data. var handleScorePosted = function(data) { + $.each(data.RedFouls, function(i, foul) { + Object.assign(foul, data.RulesViolated[foul.RuleId]); + }); + $.each(data.BlueFouls, function(i, foul) { + Object.assign(foul, data.RulesViolated[foul.RuleId]); + }); + $("#scoreMatchName").text(data.MatchType + " Match " + data.Match.DisplayName); $("#redScoreDetails").html(matchResultTemplate({score: data.RedScoreSummary, fouls: data.RedFouls, - cards: data.RedCards})); + rulesViolated: data.RulesViolated, cards: data.RedCards})); $("#blueScoreDetails").html(matchResultTemplate({score: data.BlueScoreSummary, fouls: data.BlueFouls, - cards: data.BlueCards})); + rulesViolated: data.RulesViolated, cards: data.BlueCards})); $("#matchResult").modal("show"); // Activate tooltips above the foul listings. diff --git a/static/js/match_review.js b/static/js/match_review.js index 0afc095..ea17034 100644 --- a/static/js/match_review.js +++ b/static/js/match_review.js @@ -51,9 +51,7 @@ var renderResults = function(alliance) { if (result.score.Fouls != null) { $.each(result.score.Fouls, function(k, v) { getInputElement(alliance, "Foul" + k + "Team", v.TeamId).prop("checked", true); - getInputElement(alliance, "Foul" + k + "RuleNumber").val(v.RuleNumber); - getInputElement(alliance, "Foul" + k + "IsTechnical").prop("checked", v.IsTechnical); - getInputElement(alliance, "Foul" + k + "IsRankingPoint").prop("checked", v.IsRankingPoint); + getSelectElement(alliance, "Foul" + k + "RuleId").val(v.RuleId); getInputElement(alliance, "Foul" + k + "Time").val(v.TimeInMatchSec); }); } @@ -99,9 +97,7 @@ var updateResults = function(alliance) { result.score.Fouls = []; for (var i = 0; formData[alliance + "Foul" + i + "Time"]; i++) { var prefix = alliance + "Foul" + i; - var foul = {TeamId: parseInt(formData[prefix + "Team"]), RuleNumber: formData[prefix + "RuleNumber"], - IsTechnical: formData[prefix + "IsTechnical"] === "on", - IsRankingPoint: formData[prefix + "IsRankingPoint"] === "on", + var foul = {TeamId: parseInt(formData[prefix + "Team"]), RuleId: parseInt(formData[prefix + "RuleId"]), TimeInMatchSec: parseFloat(formData[prefix + "Time"])}; result.score.Fouls.push(foul); } diff --git a/static/js/referee_panel.js b/static/js/referee_panel.js index 4b4f5d1..e72bc55 100644 --- a/static/js/referee_panel.js +++ b/static/js/referee_panel.js @@ -46,15 +46,13 @@ var clearFoul = function() { // Sends the foul to the server to add it to the list. var commitFoul = function() { websocket.send("addFoul", {Alliance: foulTeamButton.attr("data-alliance"), - TeamId: parseInt(foulTeamButton.attr("data-team")), Rule: foulRuleButton.attr("data-rule"), - IsTechnical: foulRuleButton.attr("data-is-technical") === "true", - IsRankingPoint: foulRuleButton.attr("data-is-ranking-point") === "true"}); + TeamId: parseInt(foulTeamButton.attr("data-team")), RuleId: parseInt(foulRuleButton.attr("data-rule-id"))}); }; // Removes the foul with the given parameters from the list. -var deleteFoul = function(alliance, team, rule, isTechnical, isRankingPoint, timeSec) { - websocket.send("deleteFoul", {Alliance: alliance, TeamId: parseInt(team), Rule: rule, - IsTechnical: isTechnical, IsRankingPoint: isRankingPoint, TimeInMatchSec: timeSec}); +var deleteFoul = function(alliance, team, ruleId, timeSec) { + websocket.send("deleteFoul", {Alliance: alliance, TeamId: parseInt(team), RuleId: parseInt(ruleId), + TimeInMatchSec: timeSec}); }; // Cycles through no card, yellow card, and red card. diff --git a/templates/announcer_display.html b/templates/announcer_display.html index 556ea11..a96a4cb 100644 --- a/templates/announcer_display.html +++ b/templates/announcer_display.html @@ -96,7 +96,9 @@

Fouls

{{"{{#each fouls}}"}}
-
{{"{{#if IsTechnical}}"}}Tech {{"{{/if}}"}}Foul
+
+ {{"{{#if IsTechnical}}"}}Tech {{"{{/if}}"}}Foul{{"{{#if IsRankingPoint}}"}}+RP{{"{{/if}}"}} +
Team {{"{{TeamId}}"}}
{{"{{RuleNumber}}"}}
diff --git a/templates/edit_match_result.html b/templates/edit_match_result.html index bf1c1e2..d8f4bc0 100644 --- a/templates/edit_match_result.html +++ b/templates/edit_match_result.html @@ -158,22 +158,16 @@
- -
- -
-
-
- -
- -
-
-
- -
- + +
+
diff --git a/templates/referee_panel.html b/templates/referee_panel.html index 91a1391..aca0ca8 100644 --- a/templates/referee_panel.html +++ b/templates/referee_panel.html @@ -26,10 +26,10 @@

Fouls

{{range $foul := .RedFouls}} - {{template "foul" dict "foul" $foul "color" "red"}} + {{template "foul" dict "foul" $foul "color" "red" "rules" $.Rules}} {{end}} {{range $foul := .BlueFouls}} - {{template "foul" dict "foul" $foul "color" "blue"}} + {{template "foul" dict "foul" $foul "color" "blue" "rules" $.Rules}} {{end}}

Yellow/Red Cards

@@ -58,13 +58,10 @@
{{range $rule := .Rules}} - + {{$rule.RuleNumber}}{{if $rule.IsTechnical}}T {{else if $rule.IsRankingPoint}}RP{{end}} @@ -108,10 +105,13 @@ {{.foul.TeamId}} - {{.foul.RuleNumber}}{{if .foul.IsTechnical}}T{{else if .foul.IsRankingPoint}}RP{{end}} + {{$rule := index .rules .foul.RuleId}} + {{$rule.RuleNumber}}{{if $rule.IsTechnical}}T{{else if $rule.IsRankingPoint}}RP{{end}} + + + Delete - Delete {{end}} {{define "card"}} diff --git a/web/match_play_test.go b/web/match_play_test.go index 56eeeaa..a9fbee1 100644 --- a/web/match_play_test.go +++ b/web/match_play_test.go @@ -178,7 +178,7 @@ func TestCommitEliminationTie(t *testing.T) { MatchId: match.Id, RedScore: &game.Score{ RocketFarRightBays: [3]game.BayStatus{game.BayHatchCargo, game.BayHatch, game.BayHatch}, - Fouls: []game.Foul{{}, {}, {}}}, + Fouls: []game.Foul{{RuleId: 1}, {RuleId: 2}, {RuleId: 4}}}, BlueScore: &game.Score{}, } err := web.commitMatchScore(match, matchResult, false) diff --git a/web/match_review.go b/web/match_review.go index 9a5a888..2256074 100644 --- a/web/match_review.go +++ b/web/match_review.go @@ -7,6 +7,7 @@ package web import ( "fmt" + "github.com/Team254/cheesy-arena/game" "github.com/Team254/cheesy-arena/model" "github.com/gorilla/mux" "net/http" @@ -91,7 +92,8 @@ func (web *Web) matchReviewEditGetHandler(w http.ResponseWriter, r *http.Request *model.EventSettings Match *model.Match MatchResultJson *model.MatchResultDb - }{web.arena.EventSettings, match, matchResultJson} + Rules map[int]*game.Rule + }{web.arena.EventSettings, match, matchResultJson, game.GetAllRules()} err = template.ExecuteTemplate(w, "base", data) if err != nil { handleWebErr(w, err) diff --git a/web/match_review_test.go b/web/match_review_test.go index e3f1260..9b3ec36 100644 --- a/web/match_review_test.go +++ b/web/match_review_test.go @@ -63,7 +63,7 @@ func TestMatchReviewEditExistingResult(t *testing.T) { // Update the score to something else. postBody := "redScoreJson={\"RobotEndLevels\":[0,3,0]}&blueScoreJson={\"CargoBays\":[0,2,1,2,2,0,1]," + - "\"Fouls\":[{\"TeamId\":973,\"Rule\":\"G22\"}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}" + "\"Fouls\":[{\"TeamId\":973,\"RuleId\":1}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}" recorder = web.postHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id), postBody) assert.Equal(t, 303, recorder.Code) @@ -95,7 +95,7 @@ func TestMatchReviewCreateNewResult(t *testing.T) { // Update the score to something else. postBody := "redScoreJson={\"RocketNearLeftBays\":[1,0,2]}&blueScoreJson={\"RocketFarRightBays\":[2,2,2]," + - "\"Fouls\":[{\"TeamId\":973,\"Rule\":\"G22\"}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}" + "\"Fouls\":[{\"TeamId\":973,\"RuleId\":1}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}" recorder = web.postHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id), postBody) assert.Equal(t, 303, recorder.Code) diff --git a/web/referee_panel.go b/web/referee_panel.go index 6985212..98b172c 100644 --- a/web/referee_panel.go +++ b/web/referee_panel.go @@ -70,11 +70,11 @@ func (web *Web) refereePanelHandler(w http.ResponseWriter, r *http.Request) { BlueFouls []game.Foul RedCards map[string]string BlueCards map[string]string - Rules []game.Rule + Rules map[int]*game.Rule EntryEnabled bool }{web.arena.EventSettings, matchType, match.DisplayName, red1, red2, red3, blue1, blue2, blue3, web.arena.RedRealtimeScore.CurrentScore.Fouls, web.arena.BlueRealtimeScore.CurrentScore.Fouls, - web.arena.RedRealtimeScore.Cards, web.arena.BlueRealtimeScore.Cards, game.Rules, + web.arena.RedRealtimeScore.Cards, web.arena.BlueRealtimeScore.Cards, game.GetAllRules(), !(web.arena.RedRealtimeScore.FoulsCommitted && web.arena.BlueRealtimeScore.FoulsCommitted)} err = template.ExecuteTemplate(w, "referee_panel.html", data) if err != nil { @@ -114,11 +114,9 @@ func (web *Web) refereePanelWebsocketHandler(w http.ResponseWriter, r *http.Requ switch messageType { case "addFoul": args := struct { - Alliance string - TeamId int - Rule string - IsTechnical bool - IsRankingPoint bool + Alliance string + TeamId int + RuleId int }{} err = mapstructure.Decode(data, &args) if err != nil { @@ -127,9 +125,7 @@ func (web *Web) refereePanelWebsocketHandler(w http.ResponseWriter, r *http.Requ } // Add the foul to the correct alliance's list. - foul := game.Foul{Rule: game.Rule{RuleNumber: args.Rule, IsTechnical: args.IsTechnical, - IsRankingPoint: args.IsRankingPoint}, - TeamId: args.TeamId, TimeInMatchSec: web.arena.MatchTimeSec()} + foul := game.Foul{RuleId: args.RuleId, TeamId: args.TeamId, TimeInMatchSec: web.arena.MatchTimeSec()} if args.Alliance == "red" { web.arena.RedRealtimeScore.CurrentScore.Fouls = append(web.arena.RedRealtimeScore.CurrentScore.Fouls, foul) @@ -142,9 +138,7 @@ func (web *Web) refereePanelWebsocketHandler(w http.ResponseWriter, r *http.Requ args := struct { Alliance string TeamId int - Rule string - IsTechnical bool - IsRankingPoint bool + RuleId int TimeInMatchSec float64 }{} err = mapstructure.Decode(data, &args) @@ -154,9 +148,7 @@ func (web *Web) refereePanelWebsocketHandler(w http.ResponseWriter, r *http.Requ } // Remove the foul from the correct alliance's list. - deleteFoul := game.Foul{Rule: game.Rule{RuleNumber: args.Rule, IsTechnical: args.IsTechnical, - IsRankingPoint: args.IsRankingPoint}, - TeamId: args.TeamId, TimeInMatchSec: args.TimeInMatchSec} + deleteFoul := game.Foul{RuleId: args.RuleId, TeamId: args.TeamId, TimeInMatchSec: args.TimeInMatchSec} var fouls *[]game.Foul if args.Alliance == "red" { fouls = &web.arena.RedRealtimeScore.CurrentScore.Fouls diff --git a/web/referee_panel_test.go b/web/referee_panel_test.go index 0a03fd4..957bf58 100644 --- a/web/referee_panel_test.go +++ b/web/referee_panel_test.go @@ -37,13 +37,12 @@ func TestRefereePanelWebsocket(t *testing.T) { foulData := struct { Alliance string TeamId int - Rule string - IsTechnical bool + RuleId int TimeInMatchSec float64 - }{"red", 256, "G22", false, 0} + }{"red", 256, 1, 0} ws.Write("addFoul", foulData) foulData.TeamId = 359 - foulData.IsTechnical = true + foulData.RuleId = 3 ws.Write("addFoul", foulData) foulData.Alliance = "blue" foulData.TeamId = 1680 @@ -53,17 +52,14 @@ func TestRefereePanelWebsocket(t *testing.T) { readWebsocketType(t, ws, "reload") if assert.Equal(t, 2, len(web.arena.RedRealtimeScore.CurrentScore.Fouls)) { assert.Equal(t, 256, web.arena.RedRealtimeScore.CurrentScore.Fouls[0].TeamId) - assert.Equal(t, "G22", web.arena.RedRealtimeScore.CurrentScore.Fouls[0].RuleNumber) - assert.Equal(t, false, web.arena.RedRealtimeScore.CurrentScore.Fouls[0].IsTechnical) + assert.Equal(t, 1, web.arena.RedRealtimeScore.CurrentScore.Fouls[0].RuleId) assert.Equal(t, 0.0, web.arena.RedRealtimeScore.CurrentScore.Fouls[0].TimeInMatchSec) assert.Equal(t, 359, web.arena.RedRealtimeScore.CurrentScore.Fouls[1].TeamId) - assert.Equal(t, "G22", web.arena.RedRealtimeScore.CurrentScore.Fouls[1].RuleNumber) - assert.Equal(t, true, web.arena.RedRealtimeScore.CurrentScore.Fouls[1].IsTechnical) + assert.Equal(t, 3, web.arena.RedRealtimeScore.CurrentScore.Fouls[1].RuleId) } if assert.Equal(t, 1, len(web.arena.BlueRealtimeScore.CurrentScore.Fouls)) { assert.Equal(t, 1680, web.arena.BlueRealtimeScore.CurrentScore.Fouls[0].TeamId) - assert.Equal(t, "G22", web.arena.BlueRealtimeScore.CurrentScore.Fouls[0].RuleNumber) - assert.Equal(t, true, web.arena.BlueRealtimeScore.CurrentScore.Fouls[0].IsTechnical) + assert.Equal(t, 3, web.arena.BlueRealtimeScore.CurrentScore.Fouls[0].RuleId) assert.Equal(t, 0.0, web.arena.BlueRealtimeScore.CurrentScore.Fouls[0].TimeInMatchSec) } assert.False(t, web.arena.RedRealtimeScore.FoulsCommitted)