Filled out the scoring calculation function

master
noah metz 2024-01-21 13:53:07 -07:00
parent 03bb8cd926
commit fdcdd59629
1 changed files with 212 additions and 96 deletions

@ -2,6 +2,7 @@ use rumqttc::{MqttOptions, Client, QoS, LastWill};
use bytes::Bytes; use bytes::Bytes;
use std::time::Duration; use std::time::Duration;
use std::thread; use std::thread;
use std::iter::Enumerate;
use std::collections::hash_map::HashMap; use std::collections::hash_map::HashMap;
use prost::Message; use prost::Message;
use std::io::Cursor; use std::io::Cursor;
@ -44,38 +45,26 @@ struct DivisionRankingInfo {
enum GameSide { enum GameSide {
Red, Red,
Blue, Blue,
} Tie,
#[derive(Serialize, Deserialize, Debug)]
enum ElevationTier {
A = 0,
B = 1,
C = 2,
D = 3,
E = 4,
F = 5,
G = 6,
H = 7,
I = 8,
J = 9,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
struct AllianceScore { struct AllianceScore {
team_goal: usize, auton_wp: bool,
team_zone: usize, team_goal: i32,
green_goal: usize, team_zone: i32,
green_zone: usize, green_goal: i32,
elevation_tiers: [Option<ElevationTier>; 2], green_zone: i32,
elevation_tiers: [i32; 2],
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
struct GameScore { struct GameScore {
autonomous_winner: Option<GameSide>, autonomous_winner: Option<GameSide>,
red_score: AllianceScore, red_score: AllianceScore,
red_total: usize, red_total: i32,
blue_score: AllianceScore, blue_score: AllianceScore,
blue_total: usize, blue_total: i32,
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -149,6 +138,7 @@ struct ArenaInfo {
match_tuple: MatchTuple, match_tuple: MatchTuple,
} }
#[derive(Debug)]
struct MQTTMessage { struct MQTTMessage {
topic: String, topic: String,
payload: String, payload: String,
@ -218,7 +208,7 @@ struct Field {
current_match: u32, current_match: u32,
} }
#[derive(Debug)] #[derive(Serialize, Deserialize, Debug)]
struct Match { struct Match {
match_tuple: MatchTuple, match_tuple: MatchTuple,
} }
@ -437,6 +427,8 @@ struct TMConnection {
requests: mpsc::Sender<Box<BackendMessage>>, requests: mpsc::Sender<Box<BackendMessage>>,
} }
const TCP_BUFFER_SIZE: usize = 10000;
impl TMClient { impl TMClient {
fn new(uuid: [u8; 16], client_name: [u8; 32], password: String, username: [u8; 16]) -> (TMClient, TMConnection) { fn new(uuid: [u8; 16], client_name: [u8; 32], password: String, username: [u8; 16]) -> (TMClient, TMConnection) {
let (notice_tx, notice_rx) = mpsc::channel(); let (notice_tx, notice_rx) = mpsc::channel();
@ -498,7 +490,7 @@ impl TMClient {
} }
} }
let mut incoming = [0; 2048]; let mut incoming = [0; TCP_BUFFER_SIZE];
match self.stream.read(&mut incoming) { match self.stream.read(&mut incoming) {
Ok(read) => { Ok(read) => {
let data = incoming[0..read].to_vec(); let data = incoming[0..read].to_vec();
@ -577,48 +569,153 @@ impl TMClient {
type NoticeCallback = fn(tm::Notice, Event, &TMConnection) -> (Vec<MQTTMessage>, Event); type NoticeCallback = fn(tm::Notice, Event, &TMConnection) -> (Vec<MQTTMessage>, Event);
fn get_game_score(scores: tm::MatchScore) -> Option<GameScore> { fn get_affected_match(notice: &tm::Notice) -> Option<MatchTuple> {
match &notice.affected_match {
None => None,
Some(affected_match) => {
Some(MatchTuple{
division: affected_match.division(),
round: int_to_round(affected_match.round() as i32),
instance: affected_match.instance(),
match_num: affected_match.r#match(),
session: affected_match.session(),
})
},
}
}
fn get_game_score(notice: &tm::Notice) -> Option<GameScore> {
match &notice.match_score {
None => None,
Some(scores) => {
if scores.alliances.len() != 2 { if scores.alliances.len() != 2 {
return None; return None;
} }
let ref _red_score = scores.alliances[0]; let ref red_score = scores.alliances[0];
let ref _blue_score = scores.alliances[1]; let ref blue_score = scores.alliances[1];
// 1) Get the autonomous winner
// 2) Get score object and fill AllianceScore struct
// 3) Compute total scores
let out = GameScore{ let mut out = GameScore{
autonomous_winner: None, autonomous_winner: None,
red_total: 0, red_total: 0,
blue_total: 0, blue_total: 0,
blue_score: AllianceScore{ blue_score: AllianceScore{
auton_wp: false,
team_goal: 0, team_goal: 0,
team_zone: 0, team_zone: 0,
green_goal: 0, green_goal: 0,
green_zone: 0, green_zone: 0,
elevation_tiers: [None, None], elevation_tiers: [0, 0],
}, },
red_score : AllianceScore{ red_score : AllianceScore{
auton_wp: false,
team_goal: 0, team_goal: 0,
team_zone: 0, team_zone: 0,
green_goal: 0, green_goal: 0,
green_zone: 0, green_zone: 0,
elevation_tiers: [None, None], elevation_tiers: [0, 0],
}, },
}; };
for symbol in red_score.score_types.iter() {
match symbol.name.as_str() {
"auto" => if symbol.val != 0 {out.autonomous_winner = Some(GameSide::Red)},
"auto_tie" => if symbol.val != 0 {out.autonomous_winner = Some(GameSide::Tie)},
"auto_wp" => out.red_score.auton_wp = symbol.val != 0,
"zone_alliance_triballs" => out.red_score.team_zone = symbol.val,
"goal_alliance_triballs" => out.red_score.team_goal = symbol.val,
"goal_triballs" => out.red_score.green_goal = symbol.val,
"zone_triballs" => out.red_score.green_zone = symbol.val,
"elevation_tier_1" => out.red_score.elevation_tiers[0] = symbol.val,
"elevation_tier_2" => out.red_score.elevation_tiers[1] = symbol.val,
_ => {},
}
}
for symbol in blue_score.score_types.iter() {
match symbol.name.as_str() {
"auto" => if symbol.val != 0 {out.autonomous_winner = Some(GameSide::Blue)},
"auto_tie" => if symbol.val != 0 {out.autonomous_winner = Some(GameSide::Tie)},
"auto_wp" => out.blue_score.auton_wp = symbol.val != 0,
"zone_alliance_triballs" => out.blue_score.team_zone = symbol.val,
"goal_alliance_triballs" => out.blue_score.team_goal = symbol.val,
"goal_triballs" => out.blue_score.green_goal = symbol.val,
"zone_triballs" => out.blue_score.green_zone = symbol.val,
"elevation_tier_1" => out.blue_score.elevation_tiers[0] = symbol.val,
"elevation_tier_2" => out.blue_score.elevation_tiers[1] = symbol.val,
_ => {},
}
}
match &out.autonomous_winner {
None => {},
Some(winner) => match winner {
GameSide::Red => {out.red_total = 8; out.blue_total = 0;},
GameSide::Blue => {out.red_total = 0; out.blue_total = 8;},
GameSide::Tie => {out.red_total = 4; out.blue_total = 4;},
},
}
out.red_total += 5 * (out.red_score.green_goal + out.red_score.team_goal);
out.red_total += 2 * (out.red_score.green_zone + out.red_score.team_zone);
out.blue_total += 5 * (out.blue_score.green_goal + out.blue_score.team_goal);
out.blue_total += 2 * (out.blue_score.green_zone + out.blue_score.team_zone);
let mut elevations = [(0, out.red_score.elevation_tiers[0]), (1, out.red_score.elevation_tiers[1]), (2, out.blue_score.elevation_tiers[0]), (3, out.blue_score.elevation_tiers[1])];
elevations.sort_by(|a, b| b.1.cmp(&a.1));
let mut elev_list = Vec::new();
let mut elev_map: HashMap<i32, Vec::<i32>> = HashMap::new();
for elevation in elevations {
match elev_list.last() {
None => elev_list.push(elevation.1),
Some(last) => if *last != elevation.1 {elev_list.push(elevation.1)},
}
match elev_map.get_mut(&elevation.1) {
None => {
let mut holders = Vec::new();
holders.push(elevation.0);
elev_map.insert(elevation.1, holders);
},
Some(cur) => cur.push(elevation.0),
}
}
for (idx, elevation) in elev_list.iter().enumerate() {
if *elevation == 0i32 {
break;
}
match elev_map.get(&elevation) {
None => {},
Some(holders) => {
for holder in holders {
match holder {
0 => out.red_total += 20 - (5*idx as i32),
1 => out.red_total += 20 - (5*idx as i32),
2 => out.blue_total += 20 - (5*idx as i32),
3 => out.blue_total += 20 - (5*idx as i32),
_ => {},
}
}
}
}
}
return Some(out); return Some(out);
},
}
} }
fn on_score_change(notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) { fn on_score_change(notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) {
match notice.match_score { match get_affected_match(&notice) {
None => return (Vec::new(), event), None => (Vec::new(), event),
Some(game_scores) => { Some(tuple) => {
match get_game_score(game_scores) { match get_game_score(&notice) {
Some(score_json) => { None => (Vec::new(), event),
let serialized = serde_json::to_string(&score_json).unwrap(); Some(score) => {
let serialized = serde_json::to_string(&score).unwrap();
let arena_topic = String::from("arena/TEST/score"); let arena_topic = String::from("arena/TEST/score");
let mut out = Vec::new(); let mut out = Vec::new();
out.push(MQTTMessage{ out.push(MQTTMessage{
@ -627,49 +724,48 @@ fn on_score_change(notice: tm::Notice, event: Event, _connection: &TMConnection)
}); });
return (out, event); return (out, event);
}, },
None => return (Vec::new(), event),
} }
}, },
} }
} }
fn on_match_start(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) { fn on_match_start(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) {
return (Vec::new(), _event); return (Vec::new(), event);
} }
fn on_match_cancel(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) { fn on_match_cancel(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) {
return (Vec::new(), _event); return (Vec::new(), event);
} }
fn on_match_reset(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) { fn on_match_reset(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) {
return (Vec::new(), _event); return (Vec::new(), event);
} }
fn on_match_assigned(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) { fn on_match_assigned(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) {
return (Vec::new(), _event); return (Vec::new(), event);
} }
fn on_active_field_changed(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) { fn on_active_field_changed(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) {
return (Vec::new(), _event); return (Vec::new(), event);
} }
fn on_rankings_updated(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) { fn on_rankings_updated(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) {
return (Vec::new(), _event); return (Vec::new(), event);
} }
fn on_event_status_updated(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) { fn on_event_status_updated(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) {
return (Vec::new(), _event); return (Vec::new(), event);
} }
fn on_elim_alliance_update(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) { fn on_elim_alliance_update(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) {
return (Vec::new(), _event); return (Vec::new(), event);
} }
fn on_elim_unavail_teams_update(_notice: tm::Notice, _event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) { fn on_elim_unavail_teams_update(_notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) {
return (Vec::new(), _event); return (Vec::new(), event);
} }
fn on_match_list_update(_notice: tm::Notice, _event: Event, connection: &TMConnection) -> (Vec<MQTTMessage>, Event) { fn _on_match_list_update(connection: &TMConnection) -> (Vec<MQTTMessage>, Event) {
let mut get_match_list_tuple = tm::MatchTuple::default(); let mut get_match_list_tuple = tm::MatchTuple::default();
get_match_list_tuple.division = Some(0); get_match_list_tuple.division = Some(0);
get_match_list_tuple.round = None; get_match_list_tuple.round = None;
@ -686,7 +782,34 @@ fn on_match_list_update(_notice: tm::Notice, _event: Event, connection: &TMConne
let get_match_list_resp = connection.responses.recv().unwrap(); let get_match_list_resp = connection.responses.recv().unwrap();
let new_event = Event::from_match_list(*get_match_list_resp); let new_event = Event::from_match_list(*get_match_list_resp);
return (Vec::new(), new_event); let mut messages = Vec::new();
for (division_id, division) in new_event.divisions.iter() {
messages.push(MQTTMessage{
topic: format!("division/{}/schedule", division_id),
payload: serde_json::to_string(&division.matches).unwrap(),
});
}
return (messages, new_event);
}
fn on_match_list_update(_notice: tm::Notice, _event: Event, connection: &TMConnection) -> (Vec<MQTTMessage>, Event) {
return _on_match_list_update(connection);
}
fn on_match_score_update(notice: tm::Notice, event: Event, _connection: &TMConnection) -> (Vec<MQTTMessage>, Event) {
match get_game_score(&notice) {
None => (Vec::new(), event),
Some(score) => {
let serialized = serde_json::to_string(&score).unwrap();
let arena_topic = String::from("arena/TEST/score");
let mut out = Vec::new();
out.push(MQTTMessage{
topic: arena_topic,
payload: serialized,
});
return (out, event);
},
}
} }
fn main() { fn main() {
@ -694,6 +817,7 @@ fn main() {
let mut callbacks: HashMap<tm::NoticeId, NoticeCallback> = HashMap::new(); let mut callbacks: HashMap<tm::NoticeId, NoticeCallback> = HashMap::new();
callbacks.insert(tm::NoticeId::NoticeRealtimeScoreChanged, on_score_change); callbacks.insert(tm::NoticeId::NoticeRealtimeScoreChanged, on_score_change);
callbacks.insert(tm::NoticeId::NoticeMatchScoreUpdated, on_match_score_update);
callbacks.insert(tm::NoticeId::NoticeFieldTimerStarted, on_match_start); callbacks.insert(tm::NoticeId::NoticeFieldTimerStarted, on_match_start);
callbacks.insert(tm::NoticeId::NoticeFieldTimerStopped, on_match_cancel); callbacks.insert(tm::NoticeId::NoticeFieldTimerStopped, on_match_cancel);
callbacks.insert(tm::NoticeId::NoticeFieldResetTimer, on_match_reset); callbacks.insert(tm::NoticeId::NoticeFieldResetTimer, on_match_reset);
@ -736,22 +860,14 @@ fn main() {
} }
); );
let mut get_match_list_tuple = tm::MatchTuple::default(); let (messages, mut event) = _on_match_list_update(&tm_connection);
get_match_list_tuple.division = Some(0); for message in messages {
get_match_list_tuple.round = None; let result = client.publish(message.topic, QoS::AtMostOnce, true, message.payload);
get_match_list_tuple.instance = Some(0); match result {
get_match_list_tuple.r#match = Some(0); Ok(_) => {},
get_match_list_tuple.session = Some(0); Err(error) => log::error!("Publish error: {}", error),
}
let mut get_match_list_data = tm::BackendMessageData::default(); }
get_match_list_data.match_tuple = Some(get_match_list_tuple);
let get_match_list_req = BackendMessage::new(1002, get_match_list_data.clone());
tm_connection.requests.send(Box::new(get_match_list_req)).unwrap();
let get_match_list_resp = tm_connection.responses.recv().unwrap();
let mut event = Event::from_match_list(*get_match_list_resp);
log::debug!("Initial event: {:?}", event); log::debug!("Initial event: {:?}", event);
while running { while running {