1
0
Fork 0

2016 day 11 part 2 and day 19

This commit is contained in:
Terrana Ninetailed 2022-12-07 19:35:51 +00:00
parent 3f0b985190
commit e25b97eaa7
Signed by: terrana
GPG key ID: 93EB761F8AB88151
7 changed files with 197 additions and 90 deletions

View file

@ -0,0 +1,3 @@
5-8
0-2
4-7

View file

@ -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);
}

View file

@ -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)
}

View file

@ -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
View 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
}

View file

@ -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;

View file

@ -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(&current.node) {
let mut path_el = Some(&current.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
}