diff --git a/Cargo.lock b/Cargo.lock index 215b998..4d02117 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1487,6 +1487,7 @@ version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" dependencies = [ + "cc", "pkg-config", "vcpkg", ] diff --git a/Cargo.toml b/Cargo.toml index e7848e3..290857c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ geojson = "0.24.2" r2d2 = "0.8.10" r2d2_sqlite = "0.31.0" reqwest = "0.12.24" -rusqlite = "0.37.0" +rusqlite = {version="0.37.0", features=["bundled"]} serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.145" tera = "1.20.1" diff --git a/src/data.rs b/src/data.rs index 0f336f7..73923e1 100644 --- a/src/data.rs +++ b/src/data.rs @@ -1,5 +1,7 @@ +use crate::geo_utils::GeoUtils; use serde::{Deserialize, Serialize}; use std::collections::HashMap; + #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Player { pub username: String, @@ -10,7 +12,7 @@ pub struct Team { pub name: String, pub color: String, pub players: Vec, - pub scores: HashMap, + pub scores: HashMap, // neighborhood to number of valid changes } #[derive(Debug, Clone, Deserialize, Serialize)] @@ -19,6 +21,60 @@ pub struct Game { pub teams: Vec, pub start_time: String, pub end_time: String, + pub territories: HashMap, // territory name/id to struct +} + +impl Game { + fn build_territories(&mut self, geoutils: &GeoUtils) { + let territory_json = geoutils.get_feature_collection_copy(); + for feature in &territory_json.features { + let name = feature.property("S_HOOD").unwrap_or_default(); + let name = name.as_str().unwrap_or("unknown"); + let t: Territory = Territory { + territory_name: String::from(name), + claiming_team: None, + claiming_score: 0, + }; + self.territories.insert(name.to_string(), t); + } + } + + pub fn new(code: String, teams: Vec, start_time: String, end_time: String) -> Self { + let geoutils = GeoUtils::new(); + let mut game = Game { + code: code, + teams: teams, + start_time: start_time, + end_time: end_time, + territories: HashMap::new(), + }; + game.build_territories(&geoutils); + game + } + + pub fn update_territories(&mut self) { + for (_, territory) in &mut self.territories { + let mut max_team: (Option, i32) = (None, 0); + for team in &self.teams { + let score = team.scores.get(&territory.territory_name).unwrap_or(&0); + if *score > max_team.1 { + max_team = (Some(team.clone()), *score); + } else if *score > 0 && *score == max_team.1 { + // Tie doesn't count + max_team = (None, max_team.1); + } + } + territory.claiming_team = max_team.0; + territory.claiming_score = max_team.1; + } + } +} + +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Territory { + pub territory_name: String, + pub claiming_team: Option, + pub claiming_score: i32, } impl Team { diff --git a/src/db.rs b/src/db.rs index be0cdaa..1c3f13c 100644 --- a/src/db.rs +++ b/src/db.rs @@ -66,12 +66,7 @@ fn get_all_games(conn: Connection, all_teams: HashMap) -> GamesRes .map(|part| all_teams.get(&format!("{game_name}_{part}")).unwrap()) .map(|part| part.to_owned()) .collect(); - Game { - code: game_name, - teams, - start_time: games_row.get(2)?, - end_time: games_row.get(3)?, - } + Game::new(game_name, teams, games_row.get(2)?, games_row.get(3)?) }) }) .and_then(Iterator::collect) diff --git a/src/geo_utils.rs b/src/geo_utils.rs index bbfb32e..8668831 100644 --- a/src/geo_utils.rs +++ b/src/geo_utils.rs @@ -1,4 +1,4 @@ -use crate::Team; +use crate::data::{Game, Territory}; use geo as geo_types; use geo::algorithm::contains::Contains; use geojson::{FeatureCollection, GeoJson, Geometry, Value}; @@ -29,31 +29,30 @@ impl GeoUtils { FeatureCollection::try_from(geojson).unwrap() } - pub fn get_feature_collection_copy(self) -> FeatureCollection { + pub fn get_feature_collection_copy(&self) -> FeatureCollection { self.feature_collection.clone() } - pub fn get_colored_collection_copy(self, teams: Vec) -> FeatureCollection { + pub fn get_colored_collection_copy(self, game: &Game) -> FeatureCollection { let mut copy = self.feature_collection.clone(); for feature in &mut copy.features { let name = feature.property("S_HOOD").unwrap_or_default(); let name = name.as_str().unwrap_or("unknown"); - let mut max_team: (Option, i32) = (None, 0); - for team in &teams { - let score = team.scores.get(name).unwrap_or(&0); - if *score > max_team.1 { - max_team = (Some(team.color.clone()), *score); - } else if *score > 0 && *score == max_team.1 { - // Tie doesn't count - max_team = (None, 0); - } - } - match max_team.0 { - None => feature.set_property("color", "#888888ff"), - Some(c) => { - feature.set_property("color", c); + let this_territory: Territory = game + .territories + .get(name) + .expect("Whaaa no territory found!") + .clone(); + match this_territory.claiming_team.clone() { + Some(team) => { + feature.set_property("color", team.color.clone()); + feature.set_property("claiming_team", team.name.clone()); + } + None => { + feature.set_property("color", "#888888ff"); } } + feature.set_property("claiming_score", this_territory.claiming_score); } copy } diff --git a/src/main.rs b/src/main.rs index a75392f..d72edb0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -131,6 +131,23 @@ async fn legend_data( .body("{}") } +#[get("/territories/{game_code}")] +async fn get_territories( + game_code: web::Path, + games_actor: web::Data>, +) -> impl Responder { + if let Ok(Responses::GamesResult(games)) = games_actor.send(Messages::GetGames).await + && let Some(game) = games.iter().find(|g| g.code == *game_code) + { + return HttpResponse::Ok() + .content_type("application/json") + .body(serde_json::to_string(&game.territories).unwrap()); + } + HttpResponse::InternalServerError() + .content_type("application/json") + .body("{}") +} + #[get("/geojson/{game_code}")] async fn geojson_endpoint( game_code: web::Path, @@ -139,9 +156,8 @@ async fn geojson_endpoint( if let Ok(Responses::GamesResult(games)) = games_actor.send(Messages::GetGames).await && let Some(game) = games.iter().find(|g| g.code == *game_code) { - let teams = &game.teams; let utils = geo_utils::GeoUtils::new(); - let feature_collection = utils.get_colored_collection_copy(teams.to_vec()); + let feature_collection = utils.get_colored_collection_copy(&game); return HttpResponse::Ok() .content_type("application/json") .body(serde_json::to_string(&feature_collection).unwrap()); @@ -198,7 +214,7 @@ async fn main() -> std::io::Result<()> { let games = db::games(&pool) .await .expect("retirieving games from db should work fine"); - let state = Arc::new(Mutex::new(games)); + let state = Arc::new(Mutex::new(games.clone())); let actor_addr = GamesActor { games: state.clone(), } @@ -215,6 +231,7 @@ async fn main() -> std::io::Result<()> { .service(admin) .service(admin_query) .service(list_of_games) + .service(get_territories) }) .bind(("0.0.0.0", 8080))? .run() @@ -266,6 +283,7 @@ async fn update_scores(games: Vec) -> Result, Box> { team.add_scores(&territory_scores); } } + neogame.update_territories(); result.push(neogame); } Ok(result) diff --git a/src/templates/map.tera b/src/templates/map.tera index 92c5a94..82bdaf9 100644 --- a/src/templates/map.tera +++ b/src/templates/map.tera @@ -28,52 +28,70 @@
    \ No newline at end of file