2016 day 11 part 2 and day 19
This commit is contained in:
parent
3f0b985190
commit
e25b97eaa7
7 changed files with 197 additions and 90 deletions
3
2016/resources/day20.input
Normal file
3
2016/resources/day20.input
Normal file
|
@ -0,0 +1,3 @@
|
|||
5-8
|
||||
0-2
|
||||
4-7
|
|
@ -78,7 +78,7 @@ fn load_state() -> (Vec<(Option<i32>, Option<i32>)>, Vec<Option<(bool, usize, bo
|
|||
match bots[bot] {
|
||||
(Some(first), None) => bots[bot] = (Some(min(first, value)), Some(max(first, value))),
|
||||
(None, None) => bots[bot] = (Some(value), None),
|
||||
_ => panic!(format!("Bot {} in invalid state, trying to add {} to {:?}", bot, value, bots[bot]))
|
||||
_ => panic!("Bot {} in invalid state, trying to add {} to {:?}", bot, value, bots[bot])
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
|
@ -101,7 +101,7 @@ fn distribute_values(bots: &mut Vec<(Option<i32>, Option<i32>)>, outputs: &mut V
|
|||
give_value_to_bot(bots, outputs, rules, high_to, high);
|
||||
}
|
||||
},
|
||||
None => panic!(format!("Bot {} has two values and no rule", bot))
|
||||
None => panic!("Bot {} has two values and no rule", bot)
|
||||
}
|
||||
bots[bot] = (None, None);
|
||||
}
|
||||
|
|
|
@ -6,14 +6,14 @@ use crate::pathfinding::Edge;
|
|||
|
||||
#[derive(Clone, Eq, Hash, PartialEq, Ord, PartialOrd)]
|
||||
struct FloorObject {
|
||||
material: String,
|
||||
material: usize,
|
||||
generator: bool,
|
||||
floor: i8
|
||||
floor: u8
|
||||
}
|
||||
|
||||
impl fmt::Display for FloorObject {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.material == "elevator" {
|
||||
if self.material == 0 {
|
||||
f.write_fmt(format_args!("(elevator, floor {})", self.floor + 1))
|
||||
} else if self.generator {
|
||||
f.write_fmt(format_args!("({} generator, floor {})", self.material, self.floor + 1))
|
||||
|
@ -31,10 +31,10 @@ impl fmt::Debug for FloorObject {
|
|||
|
||||
pub fn part1() {
|
||||
println!("Day 11 part 1");
|
||||
let floors = load_initial_state();
|
||||
let (floors, materials) = load_initial_state();
|
||||
let mut goal = Vec::new();
|
||||
for item in floors.iter() {
|
||||
goal.push(FloorObject{material: item.material.clone(), generator: item.generator, floor: 3})
|
||||
goal.push(FloorObject{material: item.material, generator: item.generator, floor: 3})
|
||||
}
|
||||
match pathfinding::dijkstra_single_goal(floors, goal, neighbours) {
|
||||
Some((cost, path)) => {
|
||||
|
@ -44,12 +44,12 @@ pub fn part1() {
|
|||
print!("{} ", floor + 1);
|
||||
for item in step.iter() {
|
||||
if item.floor == floor {
|
||||
if item.material == "elevator" {
|
||||
if item.material == 0 {
|
||||
print!(" E ");
|
||||
} else if item.generator {
|
||||
print!("{}{}G ", item.material[0..1].to_uppercase(), &item.material[1..2]);
|
||||
print!("{}{}G ", materials[item.material][0..1].to_uppercase(), &materials[item.material][1..2]);
|
||||
} else {
|
||||
print!("{}{}M ", item.material[0..1].to_uppercase(), &item.material[1..2]);
|
||||
print!("{}{}M ", materials[item.material][0..1].to_uppercase(), &materials[item.material][1..2]);
|
||||
}
|
||||
} else {
|
||||
print!(" . ");
|
||||
|
@ -67,16 +67,17 @@ pub fn part1() {
|
|||
|
||||
pub fn part2() {
|
||||
println!("Day 11 part 2");
|
||||
let mut floors = load_initial_state();
|
||||
floors.push(FloorObject{material: String::from("elerium"), generator: false, floor: 0});
|
||||
floors.push(FloorObject{material: String::from("elerium"), generator: true, floor: 0});
|
||||
floors.push(FloorObject{material: String::from("dilithium"), generator: false, floor: 0});
|
||||
floors.push(FloorObject{material: String::from("dilithium"), generator: true, floor: 0});
|
||||
let (mut floors, materials) = load_initial_state();
|
||||
let material_count = materials.len() as usize;
|
||||
floors.push(FloorObject{material: material_count, generator: false, floor: 0});
|
||||
floors.push(FloorObject{material: material_count, generator: true, floor: 0});
|
||||
floors.push(FloorObject{material: material_count + 1, generator: false, floor: 0});
|
||||
floors.push(FloorObject{material: material_count + 1, generator: true, floor: 0});
|
||||
let mut goal = Vec::new();
|
||||
for item in floors.iter() {
|
||||
goal.push(FloorObject{material: item.material.clone(), generator: item.generator, floor: 3})
|
||||
goal.push(FloorObject{material: item.material, generator: item.generator, floor: 3})
|
||||
}
|
||||
match pathfinding::a_star(floors, goal, neighbours, h) {
|
||||
match pathfinding::dijkstra_single_goal(floors, goal, neighbours) {
|
||||
Some((cost, _)) => println!("{}", cost),
|
||||
None => println!("No solution found")
|
||||
}
|
||||
|
@ -95,23 +96,55 @@ fn neighbours(floor_contents: &Vec<FloorObject>) -> Vec<Edge<Vec<FloorObject>>>
|
|||
movable.push(i);
|
||||
}
|
||||
}
|
||||
// Elevator can only move 1 step at a time
|
||||
if elevator.floor > 0 {
|
||||
result.append(&mut generate_moves(floor_contents, &movable, elevator.floor - 1));
|
||||
|
||||
// If on the top floor, only consider splitting one chip-generator pair
|
||||
if elevator.floor == 3 {
|
||||
let mut generators: HashSet<usize> = HashSet::new();
|
||||
let mut chips = HashSet::new();
|
||||
for i in movable.iter() {
|
||||
let item = &floor_contents[*i];
|
||||
if item.generator {
|
||||
generators.insert(item.material);
|
||||
} else {
|
||||
chips.insert(item.material);
|
||||
}
|
||||
}
|
||||
|
||||
let pairs: Vec<usize> = generators.intersection(&chips).map(|x| *x).collect();
|
||||
if pairs.len() > 1 {
|
||||
let (_,ban) = pairs.split_at(1);
|
||||
movable.retain(|i| !ban.contains(&floor_contents[*i].material));
|
||||
}
|
||||
}
|
||||
|
||||
// Elevator can only move 1 step at a time
|
||||
if elevator.floor < 3 {
|
||||
result.append(&mut generate_moves(floor_contents, &movable, elevator.floor + 1));
|
||||
}
|
||||
if elevator.floor > 0 {
|
||||
// Don't bother putting anything back on the bottom floor if it's empty
|
||||
if elevator.floor != 1 || floor_zero_count(floor_contents) {
|
||||
result.append(&mut generate_moves(floor_contents, &movable, elevator.floor - 1));
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn generate_moves(floor_contents: &Vec<FloorObject>, movable: &Vec<usize>, to_floor: i8) -> Vec<Edge<Vec<FloorObject>>> {
|
||||
fn floor_zero_count(floor_contents: &Vec<FloorObject>) -> bool {
|
||||
for item in floor_contents.iter() {
|
||||
if item.floor == 0 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn generate_moves(floor_contents: &Vec<FloorObject>, movable: &Vec<usize>, to_floor: u8) -> Vec<Edge<Vec<FloorObject>>> {
|
||||
let mut result = vec![];
|
||||
// Elevator must contain exactly 1 or 2 objects
|
||||
'outer: for (i, opt) in combinations(movable) {
|
||||
let mut contents_after_move = floor_contents.clone();
|
||||
contents_after_move[0] =
|
||||
FloorObject { material: String::from("elevator"), generator: false, floor: to_floor };
|
||||
contents_after_move[0].floor = to_floor;
|
||||
move_to_floor(floor_contents, &mut contents_after_move, to_floor, movable[i]);
|
||||
if let Some(j) = opt {
|
||||
move_to_floor(floor_contents, &mut contents_after_move, to_floor, movable[j]);
|
||||
|
@ -125,14 +158,14 @@ fn generate_moves(floor_contents: &Vec<FloorObject>, movable: &Vec<usize>, to_fl
|
|||
continue;
|
||||
}
|
||||
if item.generator {
|
||||
generators.insert(item.material.as_str());
|
||||
generators.insert(item.material);
|
||||
} else {
|
||||
chips.insert(item.material.as_str());
|
||||
chips.insert(item.material);
|
||||
}
|
||||
}
|
||||
if !generators.is_empty() {
|
||||
for chip in chips {
|
||||
if !generators.contains(chip) {
|
||||
if !generators.contains(&chip) {
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +176,7 @@ fn generate_moves(floor_contents: &Vec<FloorObject>, movable: &Vec<usize>, to_fl
|
|||
result
|
||||
}
|
||||
|
||||
fn move_to_floor(floor_contents: &Vec<FloorObject>, contents_after_move: &mut Vec<FloorObject>, to_floor: i8, i: usize) {
|
||||
fn move_to_floor(floor_contents: &Vec<FloorObject>, contents_after_move: &mut Vec<FloorObject>, to_floor: u8, i: usize) {
|
||||
let mut item = floor_contents[i].clone();
|
||||
item.floor = to_floor;
|
||||
contents_after_move[i] = item;
|
||||
|
@ -160,19 +193,20 @@ fn combinations(movable: &Vec<usize>) -> Vec<(usize, Option<usize>)> {
|
|||
result
|
||||
}
|
||||
|
||||
fn load_initial_state() -> Vec<FloorObject> {
|
||||
fn load_initial_state() -> (Vec<FloorObject>, Vec<String>) {
|
||||
let mut floor_contents = vec![];
|
||||
let mut materials = vec![String::from("elevator")];
|
||||
|
||||
for line in read_input_file("day11.input").split_terminator('\n') {
|
||||
if let Some(first_space) = line.find(' ') {
|
||||
let ord_start = first_space + 1;
|
||||
let ord_end = ord_start + line[ord_start..].find(' ').unwrap();
|
||||
let floor: i8 = match &line[ord_start .. ord_end] {
|
||||
let floor: u8 = match &line[ord_start .. ord_end] {
|
||||
"first" => 0,
|
||||
"second" => 1,
|
||||
"third" => 2,
|
||||
"fourth" => 3,
|
||||
_ => panic!(format!("Unhandled ordinal \"{}\" on line \"{}\"", &line[ord_start .. ord_end], line))
|
||||
_ => panic!("Unhandled ordinal \"{}\" on line \"{}\"", &line[ord_start .. ord_end], line)
|
||||
};
|
||||
let contents_start = line.find("contains ").unwrap() + "contains ".len();
|
||||
for chunk in line[contents_start..line.len()-1].split(", ").flat_map(|chunk| chunk.split(" and ")) {
|
||||
|
@ -186,16 +220,23 @@ fn load_initial_state() -> Vec<FloorObject> {
|
|||
content_string = &chunk["a ".len()..];
|
||||
}
|
||||
let (description, thing) = content_string.split_at(content_string.len() - 9);
|
||||
let material = match thing {
|
||||
let material_str = String::from(match thing {
|
||||
"generator" => &description[..description.len() - 1],
|
||||
"microchip" => &description[..description.len() - 12],
|
||||
_ => panic!(format!("I don't know what a {} is", &thing))
|
||||
_ => panic!("I don't know what a {} is", &thing)
|
||||
});
|
||||
let material_idx = match materials.iter().position(|m| *m == material_str) {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
materials.push(material_str);
|
||||
materials.len() - 1
|
||||
}
|
||||
};
|
||||
floor_contents.push(FloorObject{ material: String::from(material), generator: thing == "generator", floor });
|
||||
floor_contents.push(FloorObject{ material: material_idx, generator: thing == "generator", floor });
|
||||
}
|
||||
}
|
||||
}
|
||||
floor_contents.sort();
|
||||
floor_contents.insert(0, FloorObject {material: String::from("elevator"), generator: false, floor: 0});
|
||||
floor_contents
|
||||
floor_contents.insert(0, FloorObject {material: 0, generator: false, floor: 0});
|
||||
(floor_contents, materials)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use std::str::FromStr;
|
||||
use crate::file_input::read_input_file;
|
||||
use std::collections::VecDeque;
|
||||
use std::time::Instant;
|
||||
|
||||
pub fn part1() {
|
||||
let mut n = usize::from_str(read_input_file("day19.input").as_str()).unwrap();
|
||||
|
@ -19,19 +17,80 @@ pub fn part1() {
|
|||
}
|
||||
|
||||
pub fn part2() {
|
||||
// This will work, but is hideously slow due to vector resizing
|
||||
let n = usize::from_str(read_input_file("day19.input").as_str()).unwrap();
|
||||
let mut table = VecDeque::new();
|
||||
for i in 0..n {
|
||||
table.push_back(i);
|
||||
for total in 1 ..= 250 {
|
||||
assert_eq!(part2_naive(total), part2_math(total), "{}", total);
|
||||
}
|
||||
let mut current_player = 0;
|
||||
while table.len() > 1 {
|
||||
let out_index = (current_player + (table.len() / 2)) % table.len();
|
||||
table.remove(out_index);
|
||||
if out_index > current_player {
|
||||
current_player = (current_player + 1) % table.len();
|
||||
}
|
||||
}
|
||||
println!("{} wins!", table[0]+1);
|
||||
|
||||
let n = u32::from_str(read_input_file("day19.input").as_str()).unwrap();
|
||||
println!("{}", part2_math(n));
|
||||
}
|
||||
|
||||
/*
|
||||
Every round, 2/3 of participants are eliminated, and 1/3 remain. Therefore, for
|
||||
n participants, there will be ⌈log₃(n)⌉ rounds of play. Notably, the special
|
||||
case of the 1-player game plays 0 rounds as the sole player automatically wins
|
||||
right at the start.
|
||||
|
||||
Player counts may be divided up into brackets based on the number of rounds
|
||||
played r, with the bracket for r being defined as all games with player count n
|
||||
such that:
|
||||
3^(r-1) < n ≤ 3^r for all r in the natural numbers
|
||||
|
||||
Within a bracket r, the player that wins follows a specific pattern. Let the
|
||||
start of the bracket b be defined as
|
||||
b = 3^(r-1)
|
||||
For the first game in the bracket, n = b + 1.
|
||||
For the first third of games in the bracket, such that n ≤ 2b, the winning
|
||||
player number p starts at 1 and increases by 1 for every extra player. After n
|
||||
passes this threshold, p starts to increase by 2 for every extra player, such
|
||||
that in the last game in the bracket (n = 3^r), p = n.
|
||||
|
||||
Therefore, the winning player p may be calculated as follows:
|
||||
p = n - b if p ≤ 2b
|
||||
(n-b)+(n-2b) = 2n - 3b otherwise
|
||||
*/
|
||||
fn part2_math(total: u32) -> u32 {
|
||||
if total < 2 {
|
||||
return total;
|
||||
}
|
||||
let rounds = (total as f32).log(3.0).ceil() as u32;
|
||||
let start_of_bracket = 3_u32.pow(rounds - 1);
|
||||
if total <= start_of_bracket {
|
||||
total - start_of_bracket
|
||||
} else {
|
||||
2 * total - 3 * start_of_bracket
|
||||
}
|
||||
}
|
||||
|
||||
fn part2_naive(total: u32) -> u32 {
|
||||
let mut n = total;
|
||||
let mut table = vec![true;total as usize];
|
||||
let mut current = 0;
|
||||
while n > 1 {
|
||||
let mut steps = n / 2;
|
||||
let mut i = current;
|
||||
while steps > 0 {
|
||||
i += 1;
|
||||
if i >= table.len() {
|
||||
i = 0;
|
||||
}
|
||||
if table[i] {
|
||||
steps -= 1;
|
||||
}
|
||||
}
|
||||
assert!(table[i]);
|
||||
table[i] = false;
|
||||
n -= 1;
|
||||
loop {
|
||||
current += 1;
|
||||
if current >= table.len() {
|
||||
current = 0;
|
||||
}
|
||||
if table[current] {
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert!(table[current]);
|
||||
}
|
||||
(current+1) as u32
|
||||
}
|
||||
|
|
32
2016/src/day20.rs
Normal file
32
2016/src/day20.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
use crate::file_input::read_input_file;
|
||||
use std::str::FromStr;
|
||||
|
||||
pub fn part1() {
|
||||
println!("{}", "Day 20 part 1");
|
||||
for line in read_input_file("day20.input").split_terminator('\n') {
|
||||
let (start_s, end_s) = line.split_once('-').unwrap();
|
||||
let blacklist_range = Range { start: u32::from_str(start_s).unwrap(), end: u32::from_str(end_s).unwrap() };
|
||||
println!("{:?}", blacklist_range);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
pub fn part2() {
|
||||
println!("{}", "Day 20 part 2");
|
||||
}
|
||||
|
||||
struct MultiRange {
|
||||
pub mut ranges: Vec<Range>
|
||||
}
|
||||
|
||||
impl MultiRange {
|
||||
fn merge(self, new_range: Range) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, Hash, PartialEq, Ord, PartialOrd, Debug)]
|
||||
struct Range {
|
||||
pub start: u32,
|
||||
pub end: u32
|
||||
}
|
|
@ -23,6 +23,7 @@ mod day16;
|
|||
mod day17;
|
||||
mod day18;
|
||||
mod day19;
|
||||
mod day20;
|
||||
|
||||
fn main() {
|
||||
let mut selection= String::new();
|
||||
|
@ -129,6 +130,11 @@ fn main() {
|
|||
Some("2") => day19::part2(),
|
||||
_ => ()
|
||||
},
|
||||
Some("20") => match choices.next() {
|
||||
Some("1") => day20::part1(),
|
||||
Some("2") => day20::part2(),
|
||||
_ => ()
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
let run_time = Instant::now() - start;
|
||||
|
|
|
@ -46,7 +46,12 @@ pub fn dijkstra_multi_goal<Node: Hash + Clone + Ord, EdgeFunction: Fn(&Node) ->
|
|||
let mut heap: BinaryHeap<State<Node>> = BinaryHeap::new();
|
||||
distances.insert(start.clone(), 0);
|
||||
heap.push(State { cost: 0, node: start });
|
||||
let mut current_depth = 0;
|
||||
while let Some(current) = heap.pop() {
|
||||
if current.cost > current_depth {
|
||||
println!("{} {}", current_depth, heap.len());
|
||||
current_depth = current.cost;
|
||||
}
|
||||
if goals.contains(¤t.node) {
|
||||
let mut path_el = Some(¤t.node);
|
||||
let mut path = VecDeque::new();
|
||||
|
@ -78,42 +83,3 @@ pub fn dijkstra_multi_goal<Node: Hash + Clone + Ord, EdgeFunction: Fn(&Node) ->
|
|||
}
|
||||
results
|
||||
}
|
||||
|
||||
pub fn a_star<Node: Hash + Clone + Ord, EdgeFunction: Fn(&Node) -> Vec<Edge<Node>>, Heuristic: Fn(&Node) -> u32>
|
||||
(start: Node, goal: Node, edges: EdgeFunction, h: Heuristic) -> Option<(u32, VecDeque<Node>)> {
|
||||
let mut open_set: HashSet<Node> = HashSet::new();
|
||||
open_set.insert(start.clone());
|
||||
let mut came_from: HashMap<Node, Node> = HashMap::new();
|
||||
let mut f: BinaryHeap<State<Node>> = BinaryHeap::new();
|
||||
f.push(State{node: start.clone(), cost: 0});
|
||||
let mut g: HashMap<Node, u32> = HashMap::new();
|
||||
g.insert(start, 0);
|
||||
|
||||
while let Some(state) = f.pop() {
|
||||
let current = &state.node;
|
||||
if state.node == goal {
|
||||
let mut path_el = Some(current);
|
||||
let mut path = VecDeque::new();
|
||||
while let Some(el) = path_el {
|
||||
path_el = came_from.get(el);
|
||||
path.push_front(el.to_owned());
|
||||
}
|
||||
return Some((*g.get(current).unwrap(), path));
|
||||
}
|
||||
let g_current = *g.get(current).unwrap();
|
||||
for edge in edges(current) {
|
||||
let neighbour= edge.node;
|
||||
let tentative_g = g_current + edge.cost;
|
||||
if let Some(existing_g) = g.get(&neighbour) {
|
||||
if tentative_g >= *existing_g {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
g.insert(neighbour.clone(), tentative_g);
|
||||
open_set.insert(neighbour.clone());
|
||||
came_from.insert(neighbour.clone(), current.clone());
|
||||
f.push(State{node: neighbour.clone(), cost: tentative_g + h(&neighbour)});
|
||||
}
|
||||
}
|
||||
return None
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue