The first part of all the problems. (Thanks, airplanes!)

This commit is contained in:
2019-12-30 11:47:24 -08:00
parent deced4e339
commit 474ad6e203
9 changed files with 1021 additions and 1 deletions

19
inputs/day20_example1 Normal file
View File

@@ -0,0 +1,19 @@
A
A
#######.#########
#######.........#
#######.#######.#
#######.#######.#
#######.#######.#
##### B ###.#
BC...## C ###.#
##.## ###.#
##...DE F ###.#
##### G ###.#
#########.#####.#
DE..#######...###.#
#.#########.###.#
FG..#########.....#
###########.#####
Z
Z

37
inputs/day20_example2 Normal file
View File

@@ -0,0 +1,37 @@
A
A
#################.#############
#.#...#...................#.#.#
#.#.#.###.###.###.#########.#.#
#.#.#.......#...#.....#.#.#...#
#.#########.###.#####.#.#.###.#
#.............#.#.....#.......#
###.###########.###.#####.#.#.#
#.....# A C #.#.#.#
####### S P #####.#
#.#...# #......VT
#.#.#.# #.#####
#...#.# YN....#.#
#.###.# #####.#
DI....#.# #.....#
#####.# #.###.#
ZZ......# QG....#..AS
###.### #######
JO..#.#.# #.....#
#.#.#.# ###.#.#
#...#..DI BU....#..LF
#####.# #.#####
YN......# VT..#....QG
#.###.# #.###.#
#.#...# #.....#
###.### J L J #.#.###
#.....# O F P #.#...#
#.###.#####.#.#####.#####.###.#
#...#.#.#...#.....#.....#.#...#
#.#####.###.###.#.#.#########.#
#...#.#.....#...#.#.#.#.....#.#
#.###.#####.###.###.#.#.#######
#.#.........#...#.............#
#########.###.###.#############
B J C
U P P

View File

@@ -18,6 +18,7 @@ pub enum Command {
Amplify(Computer), Amplify(Computer),
Image(Image), Image(Image),
Arcade(Arcade), Arcade(Arcade),
FindSanta(Computer),
} }
fn is_number(s: String) -> Result<(), String> { fn is_number(s: String) -> Result<(), String> {
@@ -120,6 +121,14 @@ impl Command {
.required(true) .required(true)
.validator(is_file)) .validator(is_file))
) )
.subcommand(SubCommand::with_name("final")
.about("run the final computer")
.arg(Arg::with_name("COMPUTER")
.index(1)
.help("The computer to run.")
.required(true)
.validator(is_file))
)
.get_matches(); .get_matches();
if let Some(problem1) = matches.subcommand_matches("fuel") { if let Some(problem1) = matches.subcommand_matches("fuel") {
@@ -188,6 +197,12 @@ impl Command {
let arcade = Arcade::new(38, 21, true, file); let arcade = Arcade::new(38, 21, true, file);
return Command::Arcade(arcade); return Command::Arcade(arcade);
} }
if let Some(fin) = matches.subcommand_matches("final") {
let file = fin.value_of("COMPUTER").expect("No final computer file!");
let comp = Computer::load(&file);
return Command::FindSanta(comp);
}
panic!("Failed to run a reasonable command."); panic!("Failed to run a reasonable command.");
} }

120
src/bugs.rs Normal file
View File

@@ -0,0 +1,120 @@
use std::fs;
use std::str;
#[derive(Clone, Debug, PartialEq)]
struct BugMap {
has_bug: Vec<bool>,
width: usize,
height: usize,
}
impl BugMap {
fn new(s: &str) -> BugMap {
let mut width = 0;
let mut height = 0;
let mut has_bug = Vec::new();
println!("s length {}", s.len());
for line in s.trim().split('\n') {
println!("line: |{}|", line);
for c in line.chars() {
match c {
'.' => has_bug.push(false),
'#' => has_bug.push(true),
_ => panic!("Unexpected character in bug map"),
}
if height == 0 {
width += 1;
}
}
height += 1;
}
BugMap{ has_bug, width, height }
}
fn get(&self, x: usize, y: usize) -> bool {
self.has_bug[ (y * self.width) + x ]
}
fn set(&mut self, x: usize, y: usize, v: bool) {
self.has_bug[ (y * self.width) + x ] = v;
}
fn next(&self) -> BugMap {
let mut result = self.clone();
for x in 0..self.width {
for y in 0..self.height {
let above = if y > 0 { self.get(x, y - 1) } else { false };
let below = if y < (self.height - 1) { self.get(x, y + 1) } else { false };
let left = if x > 0 { self.get(x - 1, y) } else { false };
let right = if x < (self.width - 1) { self.get(x + 1, y) } else {false };
let bugs_nearby = count(above, below, left, right);
if self.get(x, y) {
result.set(x, y, bugs_nearby == 1);
} else {
result.set(x, y, (bugs_nearby >= 1) && (bugs_nearby <= 2));
}
}
}
result
}
fn biodiversity(&self) -> u128 {
let mut result = 0;
let mut two_power = 1;
for v in self.has_bug.iter() {
if *v {
result += two_power;
}
two_power <<= 1;
}
result
}
fn print(&self) {
for y in 0..self.height {
for x in 0..self.width {
print!("{}", if self.get(x, y) { '#' } else { '.' });
}
println!();
}
}
}
fn count(a: bool, b: bool, c: bool, d: bool) -> u8 {
(a as u8) + (b as u8) + (c as u8) + (d as u8)
}
fn find_duplicate(mut cur: BugMap) -> BugMap {
let mut steps = Vec::new();
while !steps.contains(&cur) {
steps.push(cur.clone());
cur = cur.next();
}
cur
}
#[test]
fn example() {
let map = BugMap::new("....#\n#..#.\n#..##\n..#..\n#....");
let endpoint = find_duplicate(map);
assert_eq!(2129920, endpoint.biodiversity());
}
#[test]
fn day24() {
let contents = fs::read("inputs/day24").expect("Couldn't read day 24 file");
let bugstr = str::from_utf8(&contents).expect("Couldn't read a string from day 24");
let bugmap = BugMap::new(&bugstr);
let endpoint = find_duplicate(bugmap);
assert_eq!(32776479, endpoint.biodiversity());
}

175
src/cards.rs Normal file
View File

@@ -0,0 +1,175 @@
use std::fs::read;
use std::str::{FromStr, from_utf8};
enum Shuffle {
DealNew,
Deal(usize),
Cut(usize),
CutBottom(usize),
}
impl Shuffle {
fn new(s: &str) -> Shuffle {
if s.starts_with("deal with increment ") {
let amt = usize::from_str(&s[19..].trim()).expect("Couldn't parse deal with number");
return Shuffle::Deal(amt);
}
if s.starts_with("cut -") {
let amt = usize::from_str(&s[5..].trim()).expect("Couldn't parse cut with negative number");
return Shuffle::CutBottom(amt);
}
if s.starts_with("cut ") {
let amt = usize::from_str(&s[4..].trim()).expect("Couldn't parse cut with number");
return Shuffle::Cut(amt);
}
if s.starts_with("deal into new stack") {
return Shuffle::DealNew;
}
panic!("Couldn't parse shuffle mechanism")
}
fn apply(&self, mut deck: Vec<u32>) -> Vec<u32> {
match self {
Shuffle::DealNew => {
deck.reverse();
deck
}
Shuffle::Deal(amt) => {
let mut res = deck.clone();
let len = deck.len();
let mut out = 0;
for i in 0..len {
res[out] = deck[i];
out = (out + amt) % len;
}
res
}
Shuffle::Cut(place) => {
let len = deck.len();
let mut result = Vec::with_capacity(len);
let mut i = *place;
loop {
result.push(deck[i]);
i = (i + 1) % len;
if i == *place {
return result;
}
}
}
Shuffle::CutBottom(place) => {
let len = deck.len();
let mut result = Vec::with_capacity(len);
let mut i = len - *place;
loop {
result.push(deck[i]);
i = (i + 1) % len;
if i == (len - *place) {
return result;
}
}
}
}
}
}
struct ShuffleOrder {
order: Vec<Shuffle>
}
impl ShuffleOrder {
fn from_file(s: &str) -> ShuffleOrder {
let raw = read(s).expect("Couldn't read file.");
let strs = from_utf8(&raw).expect("Couldn't get string data");
ShuffleOrder::new(&strs)
}
fn new(strs: &str) -> ShuffleOrder {
let mut order = Vec::new();
for line in strs.trim().split('\n') {
order.push( Shuffle::new(line) );
}
ShuffleOrder{ order }
}
fn shuffle(&self, initial: &[u32]) -> Vec<u32> {
let mut cur = Vec::from(initial);
for shuffle in self.order.iter() {
cur = shuffle.apply(cur);
}
cur
}
}
#[test]
fn base() {
let deck1 = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
assert_eq!(vec![9, 8, 7, 6, 5, 4, 3, 2, 1, 0], Shuffle::DealNew.apply(deck1));
let deck2 = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
assert_eq!(vec![3, 4, 5, 6, 7, 8, 9, 0, 1, 2], Shuffle::Cut(3).apply(deck2));
let deck3 = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
assert_eq!(vec![6, 7, 8, 9, 0, 1, 2, 3, 4, 5], Shuffle::CutBottom(4).apply(deck3));
let deck4 = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
assert_eq!(vec![0, 7, 4, 1, 8, 5, 2, 9, 6, 3], Shuffle::Deal(3).apply(deck4));
}
#[test]
fn example1() {
let shuffles = ShuffleOrder::new("deal with increment 7\ndeal into new stack\ndeal into new stack");
let done = shuffles.shuffle(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
assert_eq!(vec![0, 3, 6, 9, 2, 5, 8, 1, 4, 7], done);
}
#[test]
fn example2() {
let shuffles = ShuffleOrder::new("cut 6\ndeal with increment 7\ndeal into new stack");
let done = shuffles.shuffle(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
assert_eq!(vec![3, 0, 7, 4, 1, 8, 5, 2, 9, 6], done);
}
#[test]
fn example3() {
let shuffles = ShuffleOrder::new("deal with increment 7\ndeal with increment 9\ncut -2");
let done = shuffles.shuffle(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
assert_eq!(vec![6, 3, 0, 7, 4, 1, 8, 5, 2, 9], done);
}
#[test]
fn example4() {
let shuffles = ShuffleOrder::new("deal into new stack\ncut -2\ndeal with increment 7\ncut 8\ncut -4\ndeal with increment 7\ncut 3\ndeal with increment 9\ndeal with increment 3\ncut -1");
let done = shuffles.shuffle(&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
assert_eq!(vec![9, 2, 5, 8, 1, 4, 7, 0, 3, 6], done);
}
fn find_card(deck: &[u32], v: u32) -> usize {
for (idx, value) in deck.iter().enumerate() {
if *value == v {
return idx;
}
}
panic!("Couldn't find card {}", v)
}
#[test]
fn day22a() {
let shuffles = ShuffleOrder::from_file("inputs/day22");
let deck: Vec<u32> = (0..10007).collect();
let result = shuffles.shuffle(&deck);
assert_eq!(2939, find_card(&result, 2019));
}

274
src/donut.rs Normal file
View File

@@ -0,0 +1,274 @@
use std::collections::VecDeque;
use std::fs;
use std::str;
struct Maze<TileType> {
data: Vec<TileType>,
width: usize,
height: usize,
}
#[derive(Clone, Debug, PartialEq)]
enum InputTile {
Wall,
Empty,
Blank,
Letter(char),
}
#[derive(Clone, Debug, PartialEq)]
enum Tile {
Wall,
Empty,
Jump(String),
}
impl<'a> From<&'a InputTile> for Tile {
fn from(x: &InputTile) -> Tile {
match x {
InputTile::Wall => Tile::Wall,
InputTile::Empty => Tile::Empty,
InputTile::Blank => Tile::Wall,
InputTile::Letter(_) => Tile::Wall,
}
}
}
impl<T: Clone> Maze<T> {
fn get(&self, x: usize, y:usize) -> T {
assert!(x < self.width);
assert!(y < self.height);
self.data[ (self.width * y) + x ].clone()
}
fn set(&mut self, x: usize, y: usize, v: T) {
self.data[ (self.width * y) + x ] = v;
}
}
impl Maze<InputTile> {
fn new(f: &str) -> Maze<InputTile> {
let contents = fs::read(f).expect("Couldn't open input donut maze");
let strmap = str::from_utf8(&contents).expect("Couldn't turn donut maze into string");
let mut data = Vec::new();
let mut width = 0;
let mut height = 0;
for line in strmap.trim_right().split('\n') {
for c in line.chars() {
match c {
' ' => data.push(InputTile::Blank),
'.' => data.push(InputTile::Empty),
'#' => data.push(InputTile::Wall),
x if x.is_ascii_alphabetic() => data.push(InputTile::Letter(x)),
_ => panic!("Unknown character {}", c),
}
if height == 0 {
width += 1;
}
}
height += 1;
}
while data.len() < (width * height) { data.push(InputTile::Blank); }
Maze{ data, width, height }
}
fn print(&self) {
for y in 0..self.height {
for x in 0..self.width {
match self.get(x, y) {
InputTile::Blank => print!(" "),
InputTile::Empty => print!("."),
InputTile::Wall => print!("#"),
InputTile::Letter(c) => print!("{}", c),
}
}
println!();
}
}
}
impl Maze<Tile> {
fn new(f: &str) -> Maze<Tile> {
let inputmap: Maze<InputTile> = Maze::<InputTile>::new(f);
let mut top = 0;
let mut left = 0;
let mut bottom = 0;
let mut right = 0;
// find the top left corner
for y in 0..inputmap.height {
for x in 0..inputmap.width {
if top == 0 && inputmap.get(x, y) == InputTile::Wall {
top = y;
left = x;
break;
}
}
}
// find the bottom right corner
for y in (0..inputmap.height).rev() {
for x in (0..inputmap.width).rev() {
if bottom == 0 && inputmap.get(x, y) == InputTile::Wall {
bottom = y;
right = x;
break;
}
}
}
let width = (right - left) + 1;
let height = (bottom - top) + 1;
let mut data = Vec::with_capacity(width * height);
// Just copy the core bits over.
for y in 0..height {
for x in 0..width {
data.push( Tile::from(&inputmap.get(x + left, y + top)) );
}
}
// now we go back through and add the jump points.
let mut res = Maze{ data, width, height };
for y in 0..inputmap.height-1 {
for x in 0..inputmap.width-1 {
if let InputTile::Letter(c1) = inputmap.get(x, y) {
let mut s = String::new();
s.push(c1);
if let InputTile::Letter(c2) = inputmap.get(x + 1, y) {
s.push(c2);
if x + 2 < inputmap.width && inputmap.get(x + 2, y) == InputTile::Empty {
res.set( (x + 2) - left, y - top, Tile::Jump(s));
} else if x > 0 && inputmap.get(x - 1, y) == InputTile::Empty {
res.set( (x - 1) - left, y - top, Tile::Jump(s));
}
} else if let InputTile::Letter(c2) = inputmap.get(x, y + 1) {
s.push(c2);
if y + 2 < inputmap.height && inputmap.get(x, y + 2) == InputTile::Empty {
res.set( x - left, (y + 2) - top, Tile::Jump(s));
} else if y > 0 && inputmap.get(x, y - 1) == InputTile::Empty {
res.set( x - left, y - 1 - top, Tile::Jump(s));
}
}
}
}
}
res
}
fn origin(&self) -> (usize, usize) {
for y in 0..self.height {
for x in 0..self.width {
if self.get(x, y) == Tile::Jump("AA".to_string()) {
return (x, y);
}
}
}
panic!("Couldn't find origin!");
}
fn jump_from(&self, sx: usize, sy: usize) -> Option<(usize, usize)> {
if let Tile::Jump(label) = self.get(sx, sy) {
for y in 0..self.height {
for x in 0..self.width {
if (sx != x) || (sy != y) {
if let Tile::Jump(label2) = self.get(x, y) {
if label == label2 {
return Some((x, y));
}
}
}
}
}
}
None
}
fn next_moves(&self, x: usize, y: usize) -> Vec<(usize, usize)> {
let mut res = Vec::new();
if x > 0 { res.push((x - 1, y)); }
if y > 0 { res.push((x, y - 1)); }
if x < (self.width - 1) { res.push((x + 1, y)); }
if y < (self.height - 1) { res.push((x, y + 1)); }
if let Some(target) = self.jump_from(x, y) { res.push(target); }
res
}
fn find_path(&self) -> Vec<(usize, usize)> {
let initial_path = vec![self.origin()];
let mut queue = VecDeque::new();
queue.push_back(initial_path);
while let Some(mut cur) = queue.pop_front() {
assert_ne!(cur.len(), 0);
let (x, y) = cur[cur.len() - 1];
let mut nexts = self.next_moves(x, y);
for next in nexts.drain(0..) {
let (nx, ny) = next;
if let Tile::Jump(lbl) = self.get(nx, ny) {
if lbl == "ZZ".to_string() {
cur.push((nx, ny));
return cur;
}
}
if cur.contains(&next) {
continue;
}
if self.get(nx, ny) == Tile::Wall {
continue;
}
let mut newcopy = cur.clone();
newcopy.push((nx, ny));
queue.push_back(newcopy);
}
}
panic!("No path found!")
}
fn print(&self) {
for y in 0..self.height {
for x in 0..self.width {
match self.get(x, y) {
Tile::Empty => print!("."),
Tile::Wall => print!("#"),
Tile::Jump(s) => print!("{}", s.chars().next().unwrap()),
}
}
println!();
}
}
}
#[test]
fn example1() {
let maze = Maze::<Tile>::new("inputs/day20_example1");
assert_eq!(None, maze.jump_from(7, 0));
assert_eq!(Some((0, 6)), maze.jump_from(7, 4));
assert_eq!(Some((7, 4)), maze.jump_from(0, 6));
assert_eq!(23, maze.find_path().len() - 1);
}
#[test]
fn example2() {
let maze = Maze::<Tile>::new("inputs/day20_example2");
let path = maze.find_path();
assert_eq!(58, path.len() - 1);
}
#[test]
fn day20() {
let maze = Maze::<Tile>::new("inputs/day20");
let path = maze.find_path();
assert_eq!(606, path.len() - 1);
}

View File

@@ -1,13 +1,21 @@
mod arcade; mod arcade;
mod args; mod args;
#[cfg(test)] #[cfg(test)]
mod bugs;
#[cfg(test)]
mod cards;
#[cfg(test)]
mod chemistry; mod chemistry;
#[cfg(test)] #[cfg(test)]
mod donut;
#[cfg(test)]
mod fft; mod fft;
mod fuel; mod fuel;
mod image; mod image;
mod machine; mod machine;
#[cfg(test)] #[cfg(test)]
mod maze;
#[cfg(test)]
mod nbody; mod nbody;
mod orbits; mod orbits;
#[cfg(test)] #[cfg(test)]
@@ -16,6 +24,7 @@ mod repair;
mod robot; mod robot;
#[cfg(test)] #[cfg(test)]
mod router; mod router;
mod santafind;
#[cfg(test)] #[cfg(test)]
mod scaffold; mod scaffold;
#[cfg(test)] #[cfg(test)]
@@ -29,6 +38,7 @@ mod wiremap;
use crate::args::Command; use crate::args::Command;
use crate::fuel::calculate_fuel; use crate::fuel::calculate_fuel;
use crate::orbits::Object; use crate::orbits::Object;
use crate::santafind::find_santa;
use crate::wiremap::WireMap; use crate::wiremap::WireMap;
use std::cmp::{max,min}; use std::cmp::{max,min};
use terminal_graphics::Display; use terminal_graphics::Display;
@@ -184,5 +194,9 @@ fn main() {
}); });
println!("Final score: {}", result.score); println!("Final score: {}", result.score);
} }
Command::FindSanta(comp) => {
find_santa(comp);
}
} }
} }

240
src/maze.rs Normal file
View File

@@ -0,0 +1,240 @@
use std::collections::{HashMap, HashSet, VecDeque};
use std::fs;
use std::str;
struct Maze {
data: Vec<Tile>,
keyset: HashSet<char>,
width: usize,
height: usize,
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum Tile {
Wall,
Empty,
Door(char),
Key(char),
Entrance,
}
impl Tile {
fn is_key(&self) -> bool {
match self {
Tile::Key(_) => true,
_ => false,
}
}
}
impl Maze {
fn new(s: &str) -> Maze {
let mut data = Vec::new();
let mut keyset = HashSet::new();
let mut width = 0;
let mut height = 0;
for line in s.trim().split('\n') {
for c in line.chars() {
match c {
'#' => data.push(Tile::Wall),
'.' => data.push(Tile::Empty),
'@' => data.push(Tile::Entrance),
kd if kd.is_ascii_lowercase() => {
data.push(Tile::Key(kd));
keyset.insert(kd);
}
kd if kd.is_ascii_uppercase() => data.push(Tile::Door(kd.to_ascii_lowercase())),
kd => panic!("Unrecognized character: {}", kd),
}
if height == 0 { width += 1 }
}
height += 1;
}
Maze{ data, keyset, width, height }
}
fn get(&self, x: usize, y: usize) -> Tile {
self.data[ (y * self.width) + x ]
}
fn origin(&self) -> (usize, usize) {
for x in 0..self.width {
for y in 0..self.height {
if self.get(x, y) == Tile::Entrance {
return (x, y);
}
}
}
panic!("No origin found?!")
}
fn next_steps(&self, x: usize, y: usize) -> Vec<(usize, usize)> {
let mut initial_res = Vec::new();
if x > 0 { initial_res.push((x - 1, y)); }
if y > 0 { initial_res.push((x, y - 1)); }
if x < (self.width - 1) { initial_res.push((x + 1, y)); }
if y < (self.height - 1) { initial_res.push((x, y + 1)); }
let mut res = Vec::new();
for (x, y) in initial_res {
if self.get(x, y) != Tile::Wall {
res.push((x, y));
}
}
res
}
}
#[derive(Clone)]
struct SearchState {
collected_keys: HashSet<char>,
path: Vec<(usize, usize)>,
}
impl SearchState {
fn new(maze: &Maze) -> SearchState {
SearchState {
collected_keys: HashSet::new(),
path: vec![maze.origin()],
}
}
fn should_prune(&self, best_cases: &mut HashMap<(usize,usize),Vec<HashSet<char>>>) -> bool {
// let mut history = vec![];
//
// for (x, y) in self.path.iter().rev() {
// if maze.get(*x, *y).is_key() {
// break;
// }
//
// if history.contains(&(x, y)) {
// return true;
// }
//
// history.push((x, y));
// }
assert_ne!(self.path.len(), 0);
let lastpos = self.path[self.path.len() - 1];
match best_cases.get_mut(&lastpos) {
None => {
let _ = best_cases.insert(lastpos, vec![self.collected_keys.clone()]);
}
Some(seen) => {
for previous in seen.iter_mut() {
if self.collected_keys.is_subset(previous) {
return true;
}
if previous.is_subset(&self.collected_keys) {
*previous = self.collected_keys.clone();
break;
}
}
seen.push(self.collected_keys.clone());
}
}
false
}
}
fn find_keys(maze: &Maze) -> Vec<(usize, usize)> {
let initial_state = SearchState::new(maze);
let mut queue = VecDeque::new();
let mut best_states = HashMap::new();
queue.push_back(initial_state);
while let Some(state) = queue.pop_front() {
// println!("path length {} [queue length {}, have {:?}, want {:?}]", state.path.len(), queue.len(), state.collected_keys, maze.keyset);
assert_ne!(state.path.len(), 0);
let (x, y) = state.path[state.path.len() - 1];
let mut new_items = Vec::new();
for (newx, newy) in maze.next_steps(x, y).drain(0..) {
match maze.get(newx, newy) {
Tile::Wall => continue,
Tile::Empty => {
let mut newstate = state.clone();
newstate.path.push((newx, newy));
new_items.push(newstate);
}
Tile::Door(k) => {
if state.collected_keys.contains(&k) {
let mut newstate = state.clone();
newstate.path.push((newx, newy));
new_items.push(newstate);
}
}
Tile::Key(k) => {
let mut newstate = state.clone();
newstate.path.push((newx, newy));
newstate.collected_keys.insert(k);
if newstate.collected_keys == maze.keyset {
return newstate.path;
}
new_items.push(newstate);
}
Tile::Entrance => {
let mut newstate = state.clone();
newstate.path.push((newx, newy));
new_items.push(newstate);
}
}
}
for newstate in new_items.drain(0..) {
if !newstate.should_prune(&mut best_states) {
queue.push_back(newstate);
}
}
}
panic!("Gave up finding all the keys")
}
#[test]
fn example1() {
let example1 = Maze::new("#########\n#b.A.@.a#\n#########\n");
assert_eq!((5, 1), example1.origin());
let target1 = vec![(5, 1), (6, 1), (7, 1), (6, 1), (5, 1), (4, 1), (3, 1), (2, 1), (1, 1)];
assert_eq!(target1, find_keys(&example1));
}
#[test]
fn example2() {
let example2 = Maze::new("########################\n#f.D.E.e.C.b.A.@.a.B.c.#\n######################.#\n#d.....................#\n########################");
assert_eq!(86, find_keys(&example2).len() - 1);
}
#[test]
fn example3() {
let example3 = Maze::new("########################\n#...............b.C.D.f#\n#.######################\n#.....@.a.B.c.d.A.e.F.g#\n########################");
assert_eq!(132, find_keys(&example3).len() - 1);
}
#[test]
fn example4() {
let example4 = Maze::new("#################\n#i.G..c...e..H.p#\n########.########\n#j.A..b...f..D.o#\n########@########\n#k.E..a...g..B.n#\n########.########\n#l.F..d...h..C.m#\n#################");
assert_eq!(136, find_keys(&example4).len() - 1);
}
#[test]
fn example5() {
let example5 = Maze::new("########################\n#@..............ac.GI.b#\n###d#e#f################\n###A#B#C################\n###g#h#i################\n########################");
assert_eq!(81, find_keys(&example5).len() - 1);
}
#[test]
fn day18a() {
let day18_contents = fs::read("inputs/day18").expect("Couldn't open day18 problem");
let day18_str = str::from_utf8(&day18_contents).expect("Couldn't decode day18 problem");
let maze = Maze::new(&day18_str);
assert_eq!(6098, find_keys(&maze).len() - 1);
}

126
src/santafind.rs Normal file
View File

@@ -0,0 +1,126 @@
use crate::machine::{Computer, RunResult};
const GATHER_STEPS: [&'static str; 34] = [
"south",
"take monolith",
"east",
"take asterisk",
"west",
"north",
"west",
"take coin",
"north",
"east",
"take astronaut ice cream",
"west",
"south",
"east",
"north",
"north",
"take mutex",
"west",
"take astrolabe",
"west",
"take dehydrated water",
"west",
"take wreath",
"east",
"south",
"east",
"north",
"drop astronaut ice cream",
"drop wreath",
"drop coin",
"drop dehydrated water",
"drop asterisk",
"drop astrolabe",
"drop mutex",
];
const THINGS: [&'static str; 8] = [
"astronaut ice cream",
"wreath",
"coin",
"dehydrated water",
"asterisk",
"astrolabe",
"mutex",
"monolith",
];
fn combine_commands(cmds: &[&str]) -> String {
let mut res = String::new();
for cmd in cmds.iter() {
res.push_str(cmd);
res.push_str("\n");
}
res
}
fn select_things(c: u16) -> String {
let mut res = String::new();
for bit in 0..8 {
if (c >> bit) & 0x1 == 1 {
res.push_str("take ");
res.push_str(THINGS[bit]);
res.push_str("\n");
}
}
res
}
fn run_computer(mut comp: Computer, buffer: &mut String) -> (Box<dyn FnOnce(i64) -> Computer>, String) {
let mut outbuf = String::new();
loop {
match comp.run() {
RunResult::Continue(comp2) => comp = comp2,
RunResult::Halted(_) => panic!("Machine halted in run_computer: {}", outbuf),
RunResult::Output(c, comp2) => {
outbuf.push(c as u8 as char);
comp = comp2;
}
RunResult::Input(f) => {
if buffer.len() == 0 {
return (f, outbuf);
}
let c = buffer.remove(0);
comp = f(c as u8 as i64);
}
}
}
}
fn gather_everything(comp: Computer) -> Computer {
let mut gather_buffer = combine_commands(&GATHER_STEPS);
let (res, outb) = run_computer(comp, &mut gather_buffer);
println!("{}", outb);
res('\n' as u8 as i64)
}
fn combination_works(comp: Computer, code: u16) -> bool {
let mut get_buffer = select_things(code);
let (next, _) = run_computer(comp, &mut get_buffer);
let mut north = "nv\nnorth\n".to_string();
let (_after, outbuf) = run_computer(next('i' as u8 as i64), &mut north);
println!("outbuf: {}", outbuf);
!outbuf.contains("heavier") && !outbuf.contains("lighter")
}
pub fn find_santa(base_computer: Computer) {
let at_checkpoint = gather_everything(base_computer);
for code in 1..256 {
println!("------------------------------------------");
println!("Trying code: {}", code);
if combination_works(at_checkpoint.clone(), code) {
println!("Combination {} worked.", code);
break;
}
}
}