diff --git a/inputs/day17 b/inputs/day17 new file mode 100644 index 0000000..eaf052e --- /dev/null +++ b/inputs/day17 @@ -0,0 +1 @@ +1,330,331,332,109,4014,1101,1182,0,15,1102,1,1429,24,1001,0,0,570,1006,570,36,1002,571,1,0,1001,570,-1,570,1001,24,1,24,1106,0,18,1008,571,0,571,1001,15,1,15,1008,15,1429,570,1006,570,14,21101,58,0,0,1106,0,786,1006,332,62,99,21101,333,0,1,21101,0,73,0,1106,0,579,1101,0,0,572,1102,1,0,573,3,574,101,1,573,573,1007,574,65,570,1005,570,151,107,67,574,570,1005,570,151,1001,574,-64,574,1002,574,-1,574,1001,572,1,572,1007,572,11,570,1006,570,165,101,1182,572,127,1001,574,0,0,3,574,101,1,573,573,1008,574,10,570,1005,570,189,1008,574,44,570,1006,570,158,1105,1,81,21101,0,340,1,1105,1,177,21101,0,477,1,1105,1,177,21101,514,0,1,21102,176,1,0,1106,0,579,99,21102,1,184,0,1105,1,579,4,574,104,10,99,1007,573,22,570,1006,570,165,102,1,572,1182,21101,375,0,1,21101,211,0,0,1106,0,579,21101,1182,11,1,21102,222,1,0,1105,1,979,21101,0,388,1,21102,1,233,0,1105,1,579,21101,1182,22,1,21102,1,244,0,1106,0,979,21101,401,0,1,21101,255,0,0,1106,0,579,21101,1182,33,1,21102,1,266,0,1106,0,979,21102,1,414,1,21102,277,1,0,1106,0,579,3,575,1008,575,89,570,1008,575,121,575,1,575,570,575,3,574,1008,574,10,570,1006,570,291,104,10,21101,0,1182,1,21102,1,313,0,1105,1,622,1005,575,327,1102,1,1,575,21102,1,327,0,1106,0,786,4,438,99,0,1,1,6,77,97,105,110,58,10,33,10,69,120,112,101,99,116,101,100,32,102,117,110,99,116,105,111,110,32,110,97,109,101,32,98,117,116,32,103,111,116,58,32,0,12,70,117,110,99,116,105,111,110,32,65,58,10,12,70,117,110,99,116,105,111,110,32,66,58,10,12,70,117,110,99,116,105,111,110,32,67,58,10,23,67,111,110,116,105,110,117,111,117,115,32,118,105,100,101,111,32,102,101,101,100,63,10,0,37,10,69,120,112,101,99,116,101,100,32,82,44,32,76,44,32,111,114,32,100,105,115,116,97,110,99,101,32,98,117,116,32,103,111,116,58,32,36,10,69,120,112,101,99,116,101,100,32,99,111,109,109,97,32,111,114,32,110,101,119,108,105,110,101,32,98,117,116,32,103,111,116,58,32,43,10,68,101,102,105,110,105,116,105,111,110,115,32,109,97,121,32,98,101,32,97,116,32,109,111,115,116,32,50,48,32,99,104,97,114,97,99,116,101,114,115,33,10,94,62,118,60,0,1,0,-1,-1,0,1,0,0,0,0,0,0,1,18,18,0,109,4,2101,0,-3,586,21001,0,0,-1,22101,1,-3,-3,21101,0,0,-2,2208,-2,-1,570,1005,570,617,2201,-3,-2,609,4,0,21201,-2,1,-2,1105,1,597,109,-4,2106,0,0,109,5,1201,-4,0,630,20102,1,0,-2,22101,1,-4,-4,21102,1,0,-3,2208,-3,-2,570,1005,570,781,2201,-4,-3,653,20102,1,0,-1,1208,-1,-4,570,1005,570,709,1208,-1,-5,570,1005,570,734,1207,-1,0,570,1005,570,759,1206,-1,774,1001,578,562,684,1,0,576,576,1001,578,566,692,1,0,577,577,21102,1,702,0,1106,0,786,21201,-1,-1,-1,1105,1,676,1001,578,1,578,1008,578,4,570,1006,570,724,1001,578,-4,578,21101,731,0,0,1105,1,786,1105,1,774,1001,578,-1,578,1008,578,-1,570,1006,570,749,1001,578,4,578,21102,756,1,0,1106,0,786,1106,0,774,21202,-1,-11,1,22101,1182,1,1,21101,0,774,0,1105,1,622,21201,-3,1,-3,1106,0,640,109,-5,2106,0,0,109,7,1005,575,802,21002,576,1,-6,20101,0,577,-5,1105,1,814,21101,0,0,-1,21102,1,0,-5,21101,0,0,-6,20208,-6,576,-2,208,-5,577,570,22002,570,-2,-2,21202,-5,47,-3,22201,-6,-3,-3,22101,1429,-3,-3,2101,0,-3,843,1005,0,863,21202,-2,42,-4,22101,46,-4,-4,1206,-2,924,21102,1,1,-1,1106,0,924,1205,-2,873,21102,1,35,-4,1105,1,924,1202,-3,1,878,1008,0,1,570,1006,570,916,1001,374,1,374,2102,1,-3,895,1101,2,0,0,2101,0,-3,902,1001,438,0,438,2202,-6,-5,570,1,570,374,570,1,570,438,438,1001,578,558,921,21002,0,1,-4,1006,575,959,204,-4,22101,1,-6,-6,1208,-6,47,570,1006,570,814,104,10,22101,1,-5,-5,1208,-5,55,570,1006,570,810,104,10,1206,-1,974,99,1206,-1,974,1101,0,1,575,21101,973,0,0,1105,1,786,99,109,-7,2106,0,0,109,6,21101,0,0,-4,21101,0,0,-3,203,-2,22101,1,-3,-3,21208,-2,82,-1,1205,-1,1030,21208,-2,76,-1,1205,-1,1037,21207,-2,48,-1,1205,-1,1124,22107,57,-2,-1,1205,-1,1124,21201,-2,-48,-2,1105,1,1041,21102,-4,1,-2,1105,1,1041,21102,-5,1,-2,21201,-4,1,-4,21207,-4,11,-1,1206,-1,1138,2201,-5,-4,1059,1201,-2,0,0,203,-2,22101,1,-3,-3,21207,-2,48,-1,1205,-1,1107,22107,57,-2,-1,1205,-1,1107,21201,-2,-48,-2,2201,-5,-4,1090,20102,10,0,-1,22201,-2,-1,-2,2201,-5,-4,1103,1202,-2,1,0,1105,1,1060,21208,-2,10,-1,1205,-1,1162,21208,-2,44,-1,1206,-1,1131,1105,1,989,21101,439,0,1,1105,1,1150,21102,1,477,1,1106,0,1150,21101,0,514,1,21102,1,1149,0,1106,0,579,99,21102,1157,1,0,1105,1,579,204,-2,104,10,99,21207,-3,22,-1,1206,-1,1138,1201,-5,0,1176,1201,-4,0,0,109,-6,2105,1,0,14,11,36,1,9,1,36,1,9,1,9,7,20,1,9,1,9,1,5,1,20,1,9,1,9,1,5,1,20,1,9,1,9,1,5,1,20,1,9,1,9,1,5,1,20,1,9,1,9,1,5,1,20,1,9,11,5,1,20,1,25,1,20,11,15,7,24,1,21,1,20,7,19,1,20,1,3,1,1,1,19,1,20,1,3,1,1,1,19,1,20,1,3,1,1,1,19,1,20,1,3,1,1,1,11,9,20,1,3,1,1,1,11,1,26,11,9,1,28,1,3,1,1,1,1,1,9,1,28,1,3,7,7,1,28,1,5,1,1,1,1,1,7,1,18,11,5,11,1,1,18,1,17,1,1,1,5,1,1,1,18,1,17,1,1,1,5,1,1,1,18,1,17,1,1,1,5,1,1,1,18,1,17,11,18,1,19,1,5,1,12,9,19,7,12,1,46,1,46,1,46,1,46,1,46,7,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,38,9,38,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,11,36 diff --git a/src/main.rs b/src/main.rs index 76841b9..5f682a6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,6 +8,7 @@ mod orbits; mod repair; #[cfg(test)] mod robot; +mod scaffold; mod wiremap; use crate::args::Command; diff --git a/src/scaffold.rs b/src/scaffold.rs new file mode 100644 index 0000000..506cfa2 --- /dev/null +++ b/src/scaffold.rs @@ -0,0 +1,519 @@ +use crate::endchannel::channel; +use crate::machine::Computer; +use std::cmp::min; +use std::fmt; +use std::thread; + +struct ScaffoldMap { + data: Vec, + width: usize, + height: usize, +} + +impl ScaffoldMap { + fn new(intcode: &str) -> ScaffoldMap { + let mut computer = Computer::load(intcode, 0); + let (mut mysend, mut corecv) = channel(); + let (mut cosend, mut myrecv) = channel(); + + thread::spawn(move || computer.run(&mut corecv, &mut cosend) ); + let mut data = Vec::new(); + let mut width = 0; + let mut height = 0; + let mut got_width = false; + while let Some(c64) = myrecv.next() { + let c = c64 as u8 as char; + + if c == '\n' { + height += 1; + got_width = true; + continue; + } + if !got_width { + width += 1; + } + data.push(Tile::from(c)); + } + height -= 1; + assert_eq!(height, (data.len() / width)); + + ScaffoldMap{ data, width, height } + } + + fn get(&self, x: usize, y: usize) -> Tile { + assert!(x < self.width); + assert!(y < self.height); + self.data[ (y * self.width) + x ].clone() + } + + fn is_cross(&self, x: usize, y: usize) -> bool { + (x > 0) && (y > 0) && (x < (self.width - 1)) && (y < (self.height - 1)) && + (self.get(x, y) == Tile::Scaffold) && + (self.get(x - 1, y) == Tile::Scaffold) && + (self.get(x + 1, y) == Tile::Scaffold) && + (self.get(x, y - 1) == Tile::Scaffold) && + (self.get(x, y + 1) == Tile::Scaffold) + } + + fn join_points(&self) -> Vec<(usize, usize)> { + let mut res = vec![]; + + for y in 0..self.height { + for x in 0..self.width { + if self.is_cross(x, y) { + res.push((x, y)); + } + } + } + + res + } + + fn print(&self) { + let mut i = 0; + + println!("Scaffold is {} x {}", self.width, self.height); + for y in 0..self.height { + for x in 0..self.width { + print!("{}", char::from(self.get(x, y))); + } + println!(); + } + } + + fn find_robot(&self) -> (Direction, usize, usize) { + for y in 0..self.height { + for x in 0..self.width { + if let Tile::Robot(d) = self.get(x, y) { + return (d, x, y); + } + } + } + panic!("No robot found!!") + } + + fn go(&self, dir: Direction, x: usize, y: usize) -> Option<(usize, usize)> { + match dir { + Direction::Right if x < (self.width - 1) => + Some((x + 1, y)), + Direction::Left if x > 0 => + Some((x - 1, y)), + Direction::Up if y > 0 => + Some((x, y - 1)), + Direction::Down if y < (self.height - 1) => + Some((x, y + 1)), + _ => + None + } + } + + fn next_directions(&self, x: usize, y: usize, pointing: Direction) -> Option { + if (pointing != Direction::Down) && y > 0 && self.get(x, y - 1) == Tile::Scaffold { + return Some(Direction::Up); + } + + if (pointing != Direction::Left) && x < (self.width - 1) && self.get(x + 1, y) == Tile::Scaffold { + return Some(Direction::Right); + } + + if (pointing != Direction::Up) && y < (self.height - 1) && self.get(x, y + 1) == Tile::Scaffold { + return Some(Direction::Down); + } + + if (pointing != Direction::Right) && x > 0 && self.get(x - 1, y) == Tile::Scaffold { + return Some(Direction::Left); + } + + None + } + + fn num_moves(&self, mut x: usize, mut y: usize, dir: Direction) -> usize { + let mut count = 0; + + while let Some((newx, newy)) = self.go(dir, x, y) { + if self.get(newx, newy) != Tile::Scaffold { + break; + } else { + count += 1; + x = newx; + y = newy; + } + } + + count + } + + fn trace_path(&self) -> Vec { + let mut res = vec![]; + let (mut dir, mut x, mut y) = self.find_robot(); + + while let Some(next_dir) = self.next_directions(x, y, dir) { + println!("Now at ({}, {})", x, y); + println!("next_dir: {:?}", next_dir); + res.extend(dir.moves_to(next_dir)); + let moves = self.num_moves(x, y, next_dir); + res.push(Move::Forward(moves)); + + dir = next_dir; + match dir { + Direction::Up => { x = x; y = y - moves; } + Direction::Down => { x = x; y = y + moves; } + Direction::Left => { x = x - moves; y = y; } + Direction::Right => { x = x + moves; y = y; } + } + assert!(x < self.width); + assert!(y < self.height); + } + + res + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +enum Move { + Right, + Left, + Forward(usize), +} + +impl Move { + fn encode(&self) -> String { + match self { + Move::Right => "R".to_string(), + Move::Left => "L".to_string(), + Move::Forward(s) => s.to_string(), + } + } +} + +#[derive(Clone, Debug, PartialEq)] +struct Path { + moves: Vec +} + +impl Path { + fn new(moves: &[Move]) -> Option { + let res = Path{ moves: Vec::from(moves) }; + + if res.encode().len() > 20 { + return None + } + + Some(res) + } + + fn encode(&self) -> String { + let mut res = String::new(); + + for mv in self.moves.iter() { + if res.len() != 0 { + res.push(','); + } + res.push_str(&mv.encode()); + } + + res + } +} + +fn generate_subparts(paths: &[Move]) -> Vec { + let mut res = vec![]; + + for i in 2..paths.len() { + for piece in paths.windows(i) { + if let Some(good) = Path::new(piece) { + if !res.contains(&good) { + res.push(good); + } + } + } + } + + res +} + +struct TripleIterator { + i: usize, + j: usize, + k: usize, + paths: Vec, +} + +impl TripleIterator { + fn new(paths: &[Path]) -> TripleIterator { + TripleIterator { + i: 0, + j: 0, + k: 0, + paths: Vec::from(paths), + } + } +} + +impl Iterator for TripleIterator { + type Item = (Path, Path, Path); + + fn next(&mut self) -> Option { + if self.i == self.paths.len() { + self.i = 0; + self.j += 1; + } + + if self.j == self.paths.len() { + self.j = 0; + self.k += 1; + } + + if self.k >= self.paths.len() { + return None; + } + + let res = (self.paths[self.i].clone(), + self.paths[self.j].clone(), + self.paths[self.k].clone()); + self.i += 1; + + Some(res) + } +} + +#[test] +fn day17a() { + let scaffold = ScaffoldMap::new("inputs/day17"); + scaffold.print(); + let joins = scaffold.join_points(); + println!("joins: {:?}", joins); + let target: usize = 5620; + assert_eq!(target, joins.iter().map(|(x, y)| x * y).sum()); +} + +#[derive(Debug)] +struct Answer { + main: Vec, + a: Path, + b: Path, + c: Path, +} + +trait ToInput { + fn to_input(&self) -> Vec; +} + +#[derive(Debug)] +enum Trigger{ A, B, C } + +impl ToInput for Trigger { + fn to_input(&self) -> Vec { + match self { + Trigger::A => vec![65], + Trigger::B => vec![66], + Trigger::C => vec![67], + } + } +} + +impl ToInput for Move { + fn to_input(&self) -> Vec { + match self { + Move::Forward(x) => x.to_string().chars().map(|x| x as u8 as i64).collect(), + Move::Left => vec![76], + Move::Right => vec![82], + } + } +} + +fn to_inputs(v: &[T]) -> Vec { + let mut res = vec![]; + let mut viter = v.iter().peekable(); + + while let Some(x) = viter.next() { + res.extend(x.to_input()); + if viter.peek().is_some() { + res.push(44); + } else { + res.push(10); + } + } + + res +} + +impl Answer { + fn to_path(&self) -> Vec { + let mut res = vec![]; + + for t in self.main.iter() { + match t { + Trigger::A => res.extend(&self.a.moves), + Trigger::B => res.extend(&self.b.moves), + Trigger::C => res.extend(&self.c.moves), + } + } + + res + } + + fn to_inputs(&self) -> Vec { + let mut res = vec![]; + + println!("main: {:?}", self.main); + println!("main': {:?}", to_inputs(&self.main)); + res.extend(to_inputs(&self.main)); + println!("a: {:?}", self.a); + println!("a': {:?}", to_inputs(&self.a.moves)); + res.extend(to_inputs(&self.a.moves)); + println!("b: {:?}", self.b); + println!("b': {:?}", to_inputs(&self.b.moves)); + res.extend(to_inputs(&self.b.moves)); + println!("c: {:?}", self.c); + println!("c': {:?}", to_inputs(&self.c.moves)); + res.extend(to_inputs(&self.c.moves)); + + res + } +} + +fn answers(full: &[Move], a: &Path, b: &Path, c: &Path) -> Vec { + let mut res = vec![]; + + if full.len() == 0 { + return vec![Answer{ + main: vec![], + a: a.clone(), + b: b.clone(), + c: c.clone() + }] + } + + if full.starts_with(&a.moves) { + for mut rest in answers(&full[a.moves.len()..], a, b, c).drain(0..) { + rest.main.insert(0, Trigger::A); + if rest.main.len() <= 20 { + res.push(rest); + } + } + } + + if full.starts_with(&b.moves) { + for mut rest in answers(&full[b.moves.len()..], a, b, c).drain(0..) { + rest.main.insert(0, Trigger::B); + if rest.main.len() <= 20 { + res.push(rest); + } + } + } + + if full.starts_with(&c.moves) { + for mut rest in answers(&full[c.moves.len()..], a, b, c).drain(0..) { + rest.main.insert(0, Trigger::C); + if rest.main.len() <= 20 { + res.push(rest); + } + } + } + + res +} + +#[test] +fn day17b() { + let scaffold = ScaffoldMap::new("inputs/day17"); + scaffold.print(); + let path = scaffold.trace_path(); + println!("path: {:?}", path); + let parts = generate_subparts(&path); + println!("# of possible parts: {}", parts.len()); + let triples = TripleIterator::new(&parts); + for (a, b, c) in triples { + for answer in answers(&path, &a, &b, &c).iter() { + assert_eq!(answer.to_path(), path); + let mut comp = Computer::load("inputs/day17", 0); + let (mut mysend, mut corecv) = channel(); + let (mut cosend, mut myrecv) = channel(); + + assert_eq!(comp.read(0), 1); + comp.write(0, 2); + thread::spawn(move || comp.run(&mut corecv, &mut cosend) ); + let inputs = answer.to_inputs(); + println!("Going to send: {:?}", inputs); + for x in answer.to_inputs() { mysend.send(x); } + mysend.send('n' as u8 as i64); + mysend.send('\n' as u8 as i64); + for x in myrecv { + if x < 256 { + print!("{}", x as u8 as char); + } else { + assert_eq!(768115, x); + } + } + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +enum Tile { + Empty, + Scaffold, + Robot(Direction), + Tumbler, +} + +impl From for Tile { + fn from(c: char) -> Tile { + match c { + '#' => Tile::Scaffold, + '.' => Tile::Empty, + '^' => Tile::Robot(Direction::Up), + '>' => Tile::Robot(Direction::Right), + 'v' => Tile::Robot(Direction::Down), + '<' => Tile::Robot(Direction::Left), + 'X' => Tile::Tumbler, + _ => panic!("Unknown tile: {}", c), + } + } +} + +impl From for char { + fn from(t: Tile) -> char { + match t { + Tile::Empty => '.', + Tile::Scaffold => '#', + Tile::Robot(Direction::Up) => '^', + Tile::Robot(Direction::Right) => '>', + Tile::Robot(Direction::Down) => 'v', + Tile::Robot(Direction::Left) => '<', + Tile::Tumbler => 'X', + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +enum Direction { + Up, + Down, + Left, + Right, +} + +impl Direction { + fn moves_to(&self, newd: Direction) -> Vec { + match (self, newd) { + (Direction::Up , Direction::Up ) => vec![], + (Direction::Up , Direction::Down ) => vec![Move::Right, Move::Right], + (Direction::Up , Direction::Left ) => vec![Move::Left], + (Direction::Up , Direction::Right) => vec![Move::Right], + (Direction::Down , Direction::Up ) => vec![Move::Right, Move::Right], + (Direction::Down , Direction::Down ) => vec![], + (Direction::Down , Direction::Left ) => vec![Move::Right], + (Direction::Down , Direction::Right) => vec![Move::Left], + (Direction::Left , Direction::Up ) => vec![Move::Right], + (Direction::Left , Direction::Down ) => vec![Move::Left], + (Direction::Left , Direction::Left ) => vec![], + (Direction::Left , Direction::Right) => vec![Move::Right, Move::Right], + (Direction::Right, Direction::Up ) => vec![Move::Left], + (Direction::Right, Direction::Down ) => vec![Move::Right], + (Direction::Right, Direction::Left ) => vec![Move::Right, Move::Right], + (Direction::Right, Direction::Right) => vec![], + } + } +} \ No newline at end of file