Advertisement
Guest User

Advent of Code 2018/15

a guest
Dec 15th, 2018
200
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Rust 5.50 KB | None | 0 0
  1. use std::io::prelude::*;
  2. use std::io::BufReader;
  3. use std::fs::File;
  4.  
  5. const FIELD_SIZE: usize = 32;
  6.  
  7. type BoolField = [[bool; FIELD_SIZE]; FIELD_SIZE];
  8.  
  9. #[derive(Debug,PartialEq,Eq,Clone,Copy)]
  10. enum Faction {
  11.   Elf,
  12.   Goblin,
  13. }
  14.  
  15. #[derive(Debug,Clone)]
  16. struct Unit {
  17.   faction: Faction,
  18.   hp: i32,
  19.   attack: i32,
  20.   pos: (usize, usize), // (y, x)
  21. }
  22.  
  23. fn neighbours(pos: (usize, usize)) -> [(usize, usize); 4] {
  24.   [(pos.0, pos.1 + 1), (pos.0, pos.1 - 1), (pos.0 + 1, pos.1), (pos.0 - 1, pos.1)]
  25. }
  26.  
  27. // Returns true if a fight happened.
  28. fn fight(active: usize, units: &mut Vec<Unit>) -> bool {
  29.   let faction = units[active].faction;
  30.   let mut enemies = Vec::new();
  31.   for &pos in &neighbours(units[active].pos) {
  32.     for (j, u) in units.iter().enumerate() {
  33.       if u.hp > 0 && u.faction != faction && u.pos == pos {
  34.         enemies.push(j);
  35.       }
  36.     }
  37.   }
  38.   if let Some(enemy) = enemies.into_iter().min_by_key(|&j| (units[j].hp, units[j].pos)) {
  39.     units[enemy].hp -= units[active].attack;
  40.     true
  41.   }
  42.   else {
  43.     false
  44.   }
  45. }
  46.  
  47. // Returns true if combat is done.
  48. fn step(active: usize, walls: &BoolField, units: &mut Vec<Unit>) -> bool {
  49.  
  50.   if units[active].hp <= 0 {
  51.     return false;
  52.   }
  53.  
  54.   if fight(active, units) {
  55.     return false;
  56.   }
  57.  
  58.   let mut occupied = walls.clone();
  59.   for u in units.iter() {
  60.     if u.hp > 0 {
  61.       occupied[u.pos.0][u.pos.1] = true;
  62.     }
  63.   }
  64.  
  65.   // Find all candidate target squares.
  66.   let mut target_squares = Vec::new();
  67.   let mut have_enemies = false;
  68.   for u in units.iter() {
  69.     if u.faction == units[active].faction || u.hp <= 0 {
  70.       continue;
  71.     }
  72.     have_enemies = true;
  73.     for (y, x) in &neighbours(u.pos) {
  74.       if !occupied[*y][*x] {
  75.         target_squares.push((*y, *x));
  76.       }
  77.     }
  78.   }
  79.   if !have_enemies {
  80.     return true;
  81.   }
  82.   if target_squares.is_empty() {
  83.     return false;
  84.   }
  85.   target_squares.sort();
  86.   target_squares.dedup();
  87.  
  88.   // Find the distance from the active unit to each square until a target square is found.
  89.   let mut distances = [[None; FIELD_SIZE]; FIELD_SIZE];
  90.   distances[units[active].pos.0][units[active].pos.1] = Some(0);
  91.   let mut prev_boundary = vec![units[active].pos];
  92.   let mut new_boundary;
  93.   let mut found_squares = Vec::new();
  94.   let mut distance = 0;
  95.   while !prev_boundary.is_empty() && found_squares.is_empty() {
  96.     new_boundary = Vec::new();
  97.     distance += 1;
  98.     for pos in prev_boundary {
  99.       for &pos2 in &neighbours(pos) {
  100.         if !occupied[pos2.0][pos2.1] && distances[pos2.0][pos2.1].is_none() {
  101.           distances[pos2.0][pos2.1] = Some(distance);
  102.           new_boundary.push(pos2);
  103.           if target_squares.contains(&pos2) {
  104.             found_squares.push(pos2);
  105.           }
  106.         }
  107.       }
  108.     }
  109.     prev_boundary = new_boundary;
  110.   }
  111.   if found_squares.is_empty() {
  112.     return false;
  113.   }
  114.   let target_square = found_squares.into_iter().min().unwrap();
  115.  
  116.   // Go along all shortest paths to find the move square.
  117.   prev_boundary = vec![target_square];
  118.   while distance > 1 {
  119.     new_boundary = Vec::new();
  120.     distance -= 1;
  121.     for pos in prev_boundary {
  122.       for &pos2 in &neighbours(pos) {
  123.         if distances[pos2.0][pos2.1] == Some(distance) {
  124.           new_boundary.push(pos2);
  125.         }
  126.       }
  127.     }
  128.     prev_boundary = new_boundary;
  129.   }
  130.   units[active].pos = prev_boundary.into_iter().min().unwrap();
  131.  
  132.   fight(active, units);
  133.   false
  134. }
  135.  
  136. fn read_field() -> (BoolField, Vec<Unit>) {
  137.   let file = File::open("i15").unwrap();
  138.   let lines = BufReader::new(file).lines().map(|l| l.unwrap());
  139.  
  140.   let mut walls = [[false; FIELD_SIZE]; FIELD_SIZE];
  141.   let mut units = Vec::new();
  142.  
  143.   for (y, line) in lines.enumerate() {
  144.     for (x, c) in line.chars().enumerate() {
  145.       match c {
  146.         '#' => walls[y][x] = true,
  147.         'E' => units.push(
  148.             Unit {
  149.               faction: Faction::Elf,
  150.               hp: 200,
  151.               attack: 3,
  152.               pos: (y, x),
  153.             }
  154.           ),
  155.         'G' => units.push(
  156.             Unit {
  157.               faction: Faction::Goblin,
  158.               hp: 200,
  159.               attack: 3,
  160.               pos: (y, x),
  161.             }
  162.           ),
  163.         '.' => {},
  164.         _ => panic!("Bad input!"),
  165.       }
  166.     }
  167.   }
  168.  
  169.   return (walls, units);
  170. }
  171.  
  172. // Returns true if elves win without losses.
  173. fn run_combat(elf_attack: i32, walls: BoolField, mut units: Vec<Unit>) -> bool {
  174.   let num_elves = units.iter().filter(|u| u.faction == Faction::Elf).count();
  175.   for u in &mut units {
  176.     if u.faction == Faction::Elf {
  177.       u.attack = elf_attack;
  178.     }
  179.   }
  180.   let winners;
  181.   let mut iteration = 0;
  182.   'outer: loop {
  183.    units.sort_by_key(|u| u.pos);
  184.    for i in 0 .. units.len() {
  185.      if step(i, &walls, &mut units) {
  186.        winners = units[i].faction;
  187.        break 'outer;
  188.       }
  189.     }
  190.     units = units.into_iter().filter(|u| u.hp > 0).collect();
  191.     iteration += 1;
  192.   }
  193.   units = units.into_iter().filter(|u| u.hp > 0).collect();
  194.   let survivors = units.len();
  195.   let score = iteration * units.iter().map(|u| u.hp).sum::<i32>();
  196.   println!("Elf attack: {:?}", elf_attack);
  197.   println!("{:?} victory!", winners);
  198.   println!("{:?} survivors", survivors);
  199.   println!("Score: {:?}", score);
  200.   println!("--------------------");
  201.  
  202.   if winners == Faction::Elf && survivors == num_elves {
  203.     true
  204.   }
  205.   else {
  206.     false
  207.   }
  208. }
  209.  
  210. fn main() {
  211.   let (walls, units) = read_field();
  212.   for i in 3.. {
  213.     if run_combat(i, walls.clone(), units.clone()) {
  214.       break;
  215.     }
  216.   }
  217. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement