mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 21:56:50 -04:00
Refactor Rules to have an ID that is referenced instead of copying details everywhere.
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
49
game/foul.go
49
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
|
||||
|
||||
62
game/rule.go
Normal file
62
game/rule.go
Normal file
@@ -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
|
||||
}
|
||||
24
game/rule_test.go
Normal file
24
game/rule_test.go
Normal file
@@ -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])
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -96,7 +96,9 @@
|
||||
<h4>Fouls</h4>
|
||||
{{"{{#each fouls}}"}}
|
||||
<div class="row">
|
||||
<div class="col-lg-3 col-lg-offset-1">{{"{{#if IsTechnical}}"}}Tech {{"{{/if}}"}}Foul</div>
|
||||
<div class="col-lg-3 col-lg-offset-1">
|
||||
{{"{{#if IsTechnical}}"}}Tech {{"{{/if}}"}}Foul{{"{{#if IsRankingPoint}}"}}+RP{{"{{/if}}"}}
|
||||
</div>
|
||||
<div class="col-lg-3">Team {{"{{TeamId}}"}}</div>
|
||||
<div class="col-lg-3" data-toggle="tooltip" title="{{"{{Description}}"}}">{{"{{RuleNumber}}"}}</div>
|
||||
</div>
|
||||
|
||||
@@ -158,22 +158,16 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-4 control-label">Rule Violated</label>
|
||||
<div class="col-lg-3">
|
||||
<input type="text" class="form-control input-sm"
|
||||
name="{{"{{../alliance}}"}}Foul{{"{{@index}}"}}RuleNumber">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-4 control-label">Is Technical</label>
|
||||
<div class="col-lg-3">
|
||||
<input type="checkbox" class="input-sm" name="{{"{{../alliance}}"}}Foul{{"{{@index}}"}}IsTechnical">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-lg-4 control-label">Free Ranking Point</label>
|
||||
<div class="col-lg-3">
|
||||
<input type="checkbox" class="input-sm" name="{{"{{../alliance}}"}}Foul{{"{{@index}}"}}IsRankingPoint">
|
||||
<label class="col-lg-4 control-label">Rule</label>
|
||||
<div class="col-lg-7">
|
||||
<select class="form-control" name="{{"{{../alliance}}"}}Foul{{"{{@index}}"}}RuleId">
|
||||
{{range $rule := .Rules}}
|
||||
<option value="{{$rule.Id}}">{{$rule.RuleNumber}}
|
||||
[{{if $rule.IsTechnical}}Tech {{end}}Foul{{if $rule.IsRankingPoint}}+RP{{end}}]:
|
||||
{{$rule.Description}}
|
||||
</option>
|
||||
{{end}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
<h4>Fouls</h4>
|
||||
<table class="table">
|
||||
{{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}}
|
||||
</table>
|
||||
<h4>Yellow/Red Cards</h4>
|
||||
@@ -58,13 +58,10 @@
|
||||
</div>
|
||||
<div class="row">
|
||||
{{range $rule := .Rules}}
|
||||
<a class="btn btn-sm
|
||||
{{if $rule.IsTechnical}}btn-danger{{else if $rule.IsRankingPoint}}btn-primary
|
||||
{{else}}btn-warning{{end}}
|
||||
btn-referee btn-rule"
|
||||
data-rule="{{$rule.RuleNumber}}" data-is-technical="{{$rule.IsTechnical}}"
|
||||
data-is-ranking-point="{{$rule.IsRankingPoint}}" onclick="setFoulRule(this);"
|
||||
data-toggle="tooltip" title="{{$rule.Description}}">
|
||||
<a class="btn btn-sm {{if $rule.IsTechnical}}btn-danger{{else if $rule.IsRankingPoint}}btn-primary
|
||||
{{else}}btn-warning{{end}} btn-referee btn-rule"
|
||||
data-rule-id="{{$rule.Id}}" onclick="setFoulRule(this);" data-toggle="tooltip"
|
||||
title="{{$rule.Description}}">
|
||||
{{$rule.RuleNumber}}{{if $rule.IsTechnical}}<sup>T</sup>
|
||||
{{else if $rule.IsRankingPoint}}<sup>RP</sup>{{end}}
|
||||
</a>
|
||||
@@ -108,10 +105,13 @@
|
||||
<tr class="row-{{.color}}">
|
||||
<td>{{.foul.TeamId}}</td>
|
||||
<td>
|
||||
{{.foul.RuleNumber}}{{if .foul.IsTechnical}}<sup>T</sup>{{else if .foul.IsRankingPoint}}<sup>RP</sup>{{end}}
|
||||
{{$rule := index .rules .foul.RuleId}}
|
||||
{{$rule.RuleNumber}}{{if $rule.IsTechnical}}<sup>T</sup>{{else if $rule.IsRankingPoint}}<sup>RP</sup>{{end}}
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn-sm btn-danger" onclick="deleteFoul('{{.color}}', {{.foul.TeamId}}, '{{.foul.RuleId}}',
|
||||
{{.foul.TimeInMatchSec}});">Delete</a>
|
||||
</td>
|
||||
<td><a class="btn btn-sm btn-danger" onclick="deleteFoul('{{.color}}', {{.foul.TeamId}}, '{{.foul.RuleNumber}}',
|
||||
{{.foul.IsTechnical}}, {{.foul.IsRankingPoint}}, {{.foul.TimeInMatchSec}});">Delete</a></td>
|
||||
</tr>
|
||||
{{end}}
|
||||
{{define "card"}}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user