Created
April 22, 2017 13:24
-
-
Save GuillaumeGomez/46fdd03845f1e5e247c527f266db4e27 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::io; | |
use std::collections::{HashSet, HashMap}; | |
macro_rules! print_err { | |
($($arg:tt)*) => ( | |
{ | |
use std::io::Write; | |
writeln!(&mut ::std::io::stderr(), $($arg)*).ok(); | |
} | |
) | |
} | |
macro_rules! parse_input { | |
($x:expr, $t:ident) => ($x.trim().parse::<$t>().unwrap()) | |
} | |
macro_rules! impl_pos { | |
($struct_name:ident) => { | |
impl Pos for $struct_name { | |
fn get_x(&self) -> i32 { self.x } | |
fn get_y(&self) -> i32 { self.y } | |
} | |
} | |
} | |
trait Pos { | |
fn get_x(&self) -> i32; | |
fn get_y(&self) -> i32; | |
} | |
struct Ship { | |
x: i32, | |
y: i32, | |
direction: i32, | |
speed: i32, | |
target: Option<Barrel>, | |
road: Vec<(i32, i32)>, | |
} | |
impl Ship { | |
fn new(x: i32, y: i32, direction: i32, speed: i32) -> Ship { | |
Ship { | |
x: x, | |
y: y, | |
direction: direction, | |
speed: speed, | |
target: None, | |
road: Vec::new(), | |
} | |
} | |
fn update(&mut self, x: i32, y: i32, direction: i32, speed: i32) { | |
self.x = x; | |
self.y = y; | |
self.direction = direction; | |
self.speed = speed; | |
} | |
fn need_x_turn(&self) -> bool { | |
((self.direction == 0 || self.direction == 1 || self.direction == 5) && self.x > 20) || | |
((self.direction == 2 || self.direction == 3 || self.direction == 4) && self.x < 3) | |
} | |
fn need_y_turn(&self) -> bool { | |
((self.direction == 2 || self.direction == 1) && self.y < 3) || | |
((self.direction == 4 || self.direction == 5) && self.y > 18) || | |
self.y < 3 | |
} | |
fn set_target(&mut self, target: Option<(Barrel, Vec<(i32, i32)>)>) { | |
if let Some((target, road)) = target { | |
self.target = Some(target); | |
self.road = road; | |
} else { | |
self.target = None; | |
} | |
} | |
fn get_next_step(&mut self) -> Option<(i32, i32)> { | |
self.road.pop() | |
} | |
} | |
#[derive(Clone, Copy, PartialEq, Debug)] | |
struct Barrel { | |
x: i32, | |
y: i32, | |
} | |
impl Barrel { | |
fn new(x: i32, y: i32) -> Barrel { | |
Barrel { | |
x: x, | |
y: y, | |
} | |
} | |
} | |
#[derive(Clone, Copy, PartialEq)] | |
struct Mine { | |
x: i32, | |
y: i32, | |
} | |
impl Mine { | |
fn new(x: i32, y: i32) -> Mine { | |
Mine { | |
x: x, | |
y: y, | |
} | |
} | |
} | |
impl_pos!(Ship); | |
impl_pos!(Barrel); | |
impl_pos!(Mine); | |
impl Pos for (i32, i32) { | |
fn get_x(&self) -> i32 { self.0 } | |
fn get_y(&self) -> i32 { self.1 } | |
} | |
fn compute_distance<T: Pos, U: Pos>(t: &T, u: &U) -> i32 { | |
((t.get_x() - u.get_x()).pow(2) as f32 + (t.get_y() - u.get_y()).pow(2) as f32).sqrt() as i32 | |
} | |
const MAX_TURN: u32 = 15; | |
const MINE_DETECTION_DISTANCE: i32 = 5; | |
const directions: [(i32, i32); 6] = [ | |
( 1, 0), (1, -1), (0, -1), | |
(-1, 0), (1, 1), (0, 1) | |
]; | |
fn hex_direction(direction: i32) -> (i32, i32) { | |
directions[direction as usize] | |
} | |
fn is_neighbor(a: (i32, i32), b: (i32, i32)) -> bool { | |
//print_err!("is_neighbor: {:?} / {:?}", a, b); | |
for i in 0..6 { | |
let dir = hex_direction(i); | |
if a.0 + dir.0 == b.0 && a.1 + dir.1 == b.1 { | |
return true; | |
} | |
} | |
false | |
} | |
fn neighbor(hex: &(i32, i32), direction: i32) -> (i32, i32) { | |
let dir = hex_direction(direction); | |
(hex.0 + dir.0, hex.1 + dir.1) | |
} | |
fn blocked<T: Pos>(pos: &(i32, i32), entities: &[T]) -> bool { | |
if pos.0 > 22 || pos.0 < 0 || pos.1 > 20 || pos.1 < 0 { | |
return false; | |
} | |
for entity in entities { | |
if pos.0 == entity.get_x() && pos.1 == entity.get_y() { | |
return true; | |
} | |
} | |
false | |
} | |
fn get_road(positions: &[Vec<(i32, i32)>], end: (i32, i32), ret: (i32, i32)) -> Option<Vec<(i32, i32)>> { | |
if positions.is_empty() { | |
return None; | |
} | |
let mut res: Option<Vec<(i32, i32)>> = None; | |
let mut pos: Option<usize> = None; | |
for y in 0..positions[0].len() - 1 { | |
if is_neighbor(positions[0][y], ret) { | |
//print_err!("end: {:?} / current: {:?}", end, positions[0][y]); | |
if positions[0][y].0 == end.0 && positions[0][y].1 == end.1 { | |
return Some(vec![positions[0][y]]); | |
} else { | |
let c = positions[0][y]; | |
if let Some(tmp) = get_road(&positions[1..], end, c) { | |
if match res { | |
None => true, | |
Some(ref v) => v.len() > tmp.len(), | |
} { | |
res = Some(tmp); | |
pos = Some(y); | |
} | |
} | |
} | |
} | |
} | |
if let Some(ref mut res) = res { | |
res.push(positions[0][pos.unwrap()]); | |
} | |
res | |
} | |
fn create_road<T: Pos, U: Pos>(ship: &Ship, entities: &[T], target: &U) -> Option<Vec<(i32, i32)>> { | |
let mut visited: HashSet<(i32, i32)> = HashSet::new(); | |
visited.insert((ship.x, ship.y)); | |
let mut fringes: Vec<Vec<(i32, i32)>> = Vec::new(); | |
fringes.push(vec![(ship.x, ship.y)]); | |
let mut current = 1; | |
let mut limit = 40; | |
'main: while limit > 0 { | |
fringes.push(Vec::new()); | |
for pos in 0..fringes[current - 1].len() { | |
//print_err!("new: {:?} {:?}", (ship.x, ship.y), (target.get_x(), target.get_y())); | |
for dir in 0..6 { | |
let neighbor = neighbor(&fringes[current - 1][pos], dir); | |
//print_err!("neighbor: {:?} {:?}", fringes[current - 1][pos], neighbor); | |
if !visited.contains(&neighbor) && !blocked(&neighbor, entities) { | |
//print_err!("accepted: {:?}", neighbor); | |
fringes[current].push(neighbor); | |
visited.insert(neighbor); | |
if neighbor.0 == target.get_x() && neighbor.1 == target.get_y() { | |
break 'main | |
} | |
} | |
} | |
} | |
let empty = fringes[current].is_empty(); | |
if empty { | |
fringes.pop(); | |
current -= 1; | |
} | |
current += 1; | |
limit -= 1; | |
} | |
if limit == 0 { | |
return None; | |
} | |
print_err!("limit: {} {:?}", limit, fringes); | |
get_road(&fringes[1..], (target.get_x(), target.get_y()), (ship.get_x(), ship.get_y())) | |
} | |
fn get_best_target<T: Pos>(entities: &[Barrel], bad: &[T], ship: &Ship) -> Option<(Barrel, Vec<(i32, i32)>)> { | |
//print_err!("nb barrels: {}", entities.len()); | |
if entities.is_empty() { | |
None | |
} else if entities.len() == 1 { | |
let barrel = *entities.get(0).unwrap(); | |
if let Some(road) = create_road(ship, bad, &(barrel.get_x(), barrel.get_y())) { | |
Some((*entities.get(0).unwrap(), road)) | |
} else { | |
None | |
} | |
} else { | |
let mut three_best: Vec<(i32, Barrel)> = Vec::new(); | |
for entity in entities { | |
let distance = compute_distance(ship, entity); | |
if three_best.len() < 3 { | |
three_best.push((distance, *entity)); | |
three_best.sort_by(|x, y| x.0.cmp(&y.0)); | |
} else { | |
if three_best[2].0 > distance { | |
three_best[2] = (distance, *entity); | |
three_best.sort_by(|x, y| x.0.cmp(&y.0)); | |
} | |
} | |
} | |
let mut best: Option<(Barrel, Vec<(i32, i32)>)> = None; | |
for besty in three_best { | |
if let Some(road) = create_road(ship, bad, &besty.1) { | |
if match best { | |
None => true, | |
Some(ref inner) => inner.1.len() > road.len(), | |
} { | |
best = Some((besty.1, road)) | |
} | |
} | |
} | |
best | |
} | |
} | |
fn main() { | |
let mut ships = HashMap::new(); | |
let mut turn_count = MAX_TURN; | |
// game loop | |
loop { | |
let add_ships = ships.is_empty(); | |
if turn_count > 0 { | |
turn_count -= 1; | |
} | |
let mut input_line = String::new(); | |
io::stdin().read_line(&mut input_line).unwrap(); | |
let my_ship_count = parse_input!(input_line, i32); // the number of remaining ships | |
let mut input_line = String::new(); | |
io::stdin().read_line(&mut input_line).unwrap(); | |
let entity_count = parse_input!(input_line, usize); // the number of entities (e.g. ships, mines or cannonballs) | |
let mut entities = Vec::with_capacity(entity_count); | |
let mut mines = Vec::with_capacity(entity_count); | |
for i in 0..entity_count as usize { | |
let mut input_line = String::new(); | |
io::stdin().read_line(&mut input_line).unwrap(); | |
let inputs = input_line.split(" ").collect::<Vec<_>>(); | |
let entity_id = parse_input!(inputs[0], i32); | |
let entity_type = inputs[1].trim().to_string(); | |
let x = parse_input!(inputs[2], i32); | |
let y = parse_input!(inputs[3], i32); | |
if entity_type == "SHIP" { | |
let arg_1 = parse_input!(inputs[4], i32); | |
let arg_2 = parse_input!(inputs[5], i32); | |
let arg_3 = parse_input!(inputs[6], i32); | |
let arg_4 = parse_input!(inputs[7], i32); | |
if add_ships && arg_4 == 1 { | |
ships.insert(entity_id, Ship::new(x, y, arg_1, arg_2)); | |
} else if arg_4 == 1 { | |
ships.get_mut(&entity_id).unwrap().update(x, y, arg_1, arg_2); | |
} | |
} else if entity_type == "BARREL" { | |
entities.push(Barrel::new(x, y)); | |
} else if entity_type == "MINE" { | |
mines.push(Mine::new(x, y)); | |
} | |
} | |
for (_, ship) in &mut ships { | |
if if let Some(ref target) = ship.target { | |
entities.iter().find(|&x| x == target).is_none() | |
} else { false } { | |
ship.target = None | |
} | |
if ship.target.is_none() { | |
let target = get_best_target(&entities, &mines, ship); | |
ship.set_target(target); | |
} | |
if ship.target.is_none() { | |
let need_x_turn = ship.need_x_turn(); | |
let need_y_turn = ship.need_y_turn(); | |
if need_x_turn || need_y_turn || ship.speed < 2 { | |
println!("MOVE {} {}", | |
if need_x_turn { | |
if ship.x > 20 { 0 } else { 22 } | |
} else { | |
if ship.direction == 0 || ship.direction == 1 || ship.direction == 5 { | |
22 | |
} else { | |
0 | |
} | |
}, | |
if need_y_turn { | |
if ship.y > 18 { 0 } else { 20 } | |
} else { | |
if ship.direction == 2 || ship.direction == 1 { | |
20 | |
} else { | |
0 | |
} | |
}); | |
} else { | |
println!("WAIT"); | |
} | |
} else { | |
print_err!("target: {:?}", ship.target); | |
if let Some((x, y)) = ship.get_next_step() { | |
println!("MOVE {} {}", x, y); | |
} else { | |
println!("WAIT"); | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment