#![feature(proc_macro_hygiene, decl_macro)] #[macro_use] extern crate rocket; use rocket::State; use rocket_contrib::json::Json; use std::collections::HashMap; use std::sync::Mutex; mod battlesnake; type Cache = Mutex>; #[derive(Debug, Clone)] pub struct GameState { path: Option>, target: battlesnake::Coord, } #[get("/")] fn index() -> &'static str { "Hello, world!" } #[get("/ping")] fn ping() -> &'static str { "OK - Pneuma online" } #[post("/start", format = "json", data = "")] fn start( cache: State, msg: Json, ) -> Json { let head = msg.you.body[0]; let target = find_target(&msg); let path = find_path(&msg, &head, &target); let gs = GameState { target: *target, path: path, }; cache .lock() .expect("wanted to lock cache") .insert(msg.game.id.clone(), gs); Json(battlesnake::StartResponse { color: "#5ce8c3".to_string(), head_type: "beluga".to_string(), tail_type: "skinny".to_string(), }) } fn find_path( msg: &battlesnake::SnakeRequest, head: &battlesnake::Coord, target: &battlesnake::Coord, ) -> Option> { let path = pathfinding::directed::astar::astar( head, |n| msg.board.safe_neighbors(n).into_iter(), |n| (battlesnake::manhattan(n, &target) as usize), |n| n == target, ); match path { None => return None, Some(x) => { return Some(x.0.iter().rev().cloned().collect()); } } } #[post("/end", format = "json", data = "")] fn end(cache_state: State, msg: Json) -> String { cache_state .lock() .expect("wanted cache to be lockable") .remove(&msg.game.id); "OK".to_string() } #[post("/move", format = "json", data = "")] fn make_move( cache_state: State, msg: Json, ) -> Json { let head = msg.you.body[0]; let mut cache = cache_state.lock().expect("wanted cache to be unlockable"); let gs = cache.get_mut(&msg.game.id).unwrap(); if gs.path.is_none() { println!("recalculating path"); gs.target = *find_target(&msg); gs.path = find_path(&msg, &head, &gs.target); } match gs.path.as_mut().unwrap().pop() { None => { gs.path = None; let target = msg.board.safe_neighbors(&head)[0].0; let next_move = battlesnake::Line { start: &head, end: &target, } .direction() .to_string(); println!("moving to {}, target: {:?}", next_move, target); Json(battlesnake::MoveResponse { move_field: next_move, }) } Some(next) => { let next_move = battlesnake::Line { start: &head, end: &next, } .direction() .to_string(); println!( "moving to {} {:?}, target: {:?}", next_move, next, gs.target ); Json(battlesnake::MoveResponse { move_field: next_move, }) } } } fn find_target<'a>(gs: &'a battlesnake::SnakeRequest) -> &'a battlesnake::Coord { let head = &gs.you.body[0]; if gs.you.health > 30 { let mut lowest_score: u32 = 99999; let mut coord: &battlesnake::Coord = &gs.you.body.last().unwrap(); for food in &gs.board.food { let score = battlesnake::manhattan(&head, &food); if score < lowest_score { lowest_score = score; coord = food; } } return coord; } return gs.you.body.last().unwrap(); } fn main() { let map = HashMap::::new(); let mutex_map = Mutex::from(map); let prometheus = rocket_prometheus::PrometheusMetrics::new(); rocket::ignite() .attach(prometheus.clone()) .mount("/metrics", prometheus) .mount("/", routes![index, start, ping, make_move, end,]) .manage(mutex_map) .launch(); }