Skip to content

Commit

Permalink
Add D21 "solutions"
Browse files Browse the repository at this point in the history
 * Fixed bug in the elfcode parser and VM
 * Fixed various clippy lints
  • Loading branch information
Ameobea committed Dec 22, 2018
1 parent 826df64 commit f5c8514
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 64 deletions.
27 changes: 27 additions & 0 deletions .vscode/launch.json
@@ -0,0 +1,27 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "./target/debug/aoc-2018",
"args": ["--day", "17"],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": true,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
33 changes: 33 additions & 0 deletions input/day21.txt
@@ -0,0 +1,33 @@
#ip 2
seti 123 0 4
bani 4 456 4
eqri 4 72 4
addr 4 2 2
seti 0 0 2
seti 0 1 4
bori 4 65536 1
seti 16031208 7 4
bani 1 255 3
addr 4 3 4
bani 4 16777215 4
muli 4 65899 4
bani 4 16777215 4
gtir 256 1 3
addr 3 2 2
addi 2 1 2
seti 27 3 2
seti 0 9 3
addi 3 1 5
muli 5 256 5
gtrr 5 1 5
addr 5 2 2
addi 2 1 2
seti 25 7 2
addi 3 1 3
seti 17 4 2
setr 3 1 1
seti 7 5 2
eqrr 4 0 3
addr 3 2 2
seti 5 1 2

18 changes: 11 additions & 7 deletions src/asm_common.rs
Expand Up @@ -16,8 +16,8 @@ pub enum Opcode {
Addi,
Mulr,
Muli,
Barr,
Bari,
Banr,
Bani,
Borr,
Bori,
Setr,
Expand All @@ -39,8 +39,8 @@ impl FromStr for Opcode {
"addi" => Opcode::Addi,
"mulr" => Opcode::Mulr,
"muli" => Opcode::Muli,
"barr" => Opcode::Barr,
"bari" => Opcode::Bari,
"banr" => Opcode::Banr,
"bani" => Opcode::Bani,
"borr" => Opcode::Borr,
"bori" => Opcode::Bori,
"setr" => Opcode::Setr,
Expand Down Expand Up @@ -109,8 +109,8 @@ impl Registers {
Opcode::Addi => self.regs[instr.in1] + instr.in2,
Opcode::Mulr => self.regs[instr.in1] * self.regs[instr.in2],
Opcode::Muli => self.regs[instr.in1] * instr.in2,
Opcode::Barr => self.regs[instr.in1] & self.regs[instr.in2],
Opcode::Bari => self.regs[instr.in1] & instr.in2,
Opcode::Banr => self.regs[instr.in1] & self.regs[instr.in2],
Opcode::Bani => self.regs[instr.in1] & instr.in2,
Opcode::Borr => self.regs[instr.in1] | self.regs[instr.in2],
Opcode::Bori => self.regs[instr.in1] | instr.in2,
Opcode::Setr => self.regs[instr.in1],
Expand Down Expand Up @@ -149,6 +149,7 @@ impl Display for Instruction {
}
}

#[derive(Clone)]
pub struct Instructions {
pub ip: usize,
pub instructions: Vec<Instruction>,
Expand All @@ -164,6 +165,7 @@ impl IndexMut<usize> for Instructions {
fn index_mut(&mut self, i: usize) -> &mut Instruction { &mut self.instructions[i] }
}

#[derive(Clone)]
pub struct VM {
pub regs: Registers,
pub instructions: Instructions,
Expand All @@ -178,9 +180,11 @@ impl VM {
None => return true,
};
let ip = self.ip();

self.instructions.ip = self.regs.exec(ip, cur_instr);
self.instructions.ip += 1;
return false;

false
}

/// Runs the VM until it halts.
Expand Down
2 changes: 1 addition & 1 deletion src/day14.rs
Expand Up @@ -22,7 +22,7 @@ fn part1() -> u64 {
.iter()
.enumerate()
.fold(0u64, |acc, (i, d)| {
acc + (10u64.pow(9 - i as u32) as u64 * *d as u64)
acc + (10u64.pow(9 - i as u32) as u64 * u64::from(*d))
})
}

Expand Down
4 changes: 2 additions & 2 deletions src/day16.rs
Expand Up @@ -55,8 +55,8 @@ const ALL_OPCODES: &[Opcode] = &[
Opcode::Addi,
Opcode::Mulr,
Opcode::Muli,
Opcode::Barr,
Opcode::Bari,
Opcode::Banr,
Opcode::Bani,
Opcode::Borr,
Opcode::Bori,
Opcode::Setr,
Expand Down
90 changes: 42 additions & 48 deletions src/day17.rs
Expand Up @@ -54,25 +54,27 @@ fn debug_world(world: &[Vec<Cell>]) {
}

fn spread_down(world: &mut Vec<Vec<Cell>>, x: usize, y: usize) -> bool {
// println!("down: {:?}", (x, y));
// debug_world(world);
if (y + 1) >= world.len() {
return false;
}

if world[y + 1][x] == Cell::Water {
if let Some(x_opt) = spread_left(world, x, y + 1, true) {
if let Some(x) = x_opt {
match spread_left(world, x, y + 1, true) {
SpreadResult::Downspout(x) => {
world[y + 1][x] = Cell::Sand;
}
return false;
}
if let Some(x_opt) = spread_right(world, x, y + 1, true) {
if let Some(x) = x_opt {
return false;
},
SpreadResult::WaterCollision => return false,
SpreadResult::WallCollision => (),
};
match spread_right(world, x, y + 1, true) {
SpreadResult::Downspout(x) => {
world[y + 1][x] = Cell::Sand;
}
return false;
}
return false;
},
SpreadResult::WaterCollision => return false,
SpreadResult::WallCollision => (),
};
}

let can_move_down = world[y + 1][x] == Cell::Sand;
Expand All @@ -88,42 +90,38 @@ fn spread_down(world: &mut Vec<Vec<Cell>>, x: usize, y: usize) -> bool {
let pour_location_right = spread_right(world, x, y, world[y][x + 1] == Cell::Water);

let mut spread = true;
if let Some(Some(x)) = pour_location_right {
spread = spread_down(world, x, y) && spread;
} else if let Some(None) = pour_location_right {
spread = false;
}

if let Some(Some(x)) = pour_location_left {
spread = spread_down(world, x, y) && spread;
} else if let Some(None) = pour_location_left {
spread = false;
}
match pour_location_right {
SpreadResult::Downspout(x) => spread = spread_down(world, x, y) && spread,
SpreadResult::WaterCollision => spread = false,
_ => (),
};
match pour_location_left {
SpreadResult::Downspout(x) => spread = spread_down(world, x, y) && spread,
SpreadResult::WaterCollision => spread = false,
_ => (),
};

spread
} else {
false
}
}

enum SpreadResult {
Downspout(usize),
WaterCollision,
WallCollision,
}

/// Returns the x coordinate of the cell at which water will begin to pour downward, unless it hits
/// a wall or the side of amap in which `None` will be returned.
fn spread_left(
world: &mut Vec<Vec<Cell>>,
x: usize,
y: usize,
ignore_water: bool,
) -> Option<Option<usize>> {
fn spread_left(world: &mut Vec<Vec<Cell>>, x: usize, y: usize, ignore_water: bool) -> SpreadResult {
if x == 0 || world[y][x - 1] == Cell::Clay {
return None;
}

if y + 1 < world.len() && world[y + 1][x] == Cell::Sand {
return Some(Some(x));
}

if !ignore_water && world[y][x - 1] == Cell::Water {
return Some(None);
return SpreadResult::WallCollision;
} else if y + 1 < world.len() && world[y + 1][x] == Cell::Sand {
return SpreadResult::Downspout(x);
} else if !ignore_water && world[y][x - 1] == Cell::Water {
return SpreadResult::WaterCollision;
}

world[y][x - 1] = Cell::Water;
Expand All @@ -138,17 +136,13 @@ fn spread_right(
x: usize,
y: usize,
ignore_water: bool,
) -> Option<Option<usize>> {
) -> SpreadResult {
if x + 1 == world[0].len() || world[y][x + 1] == Cell::Clay {
return None;
}

if y + 1 < world.len() && world[y + 1][x] == Cell::Sand {
return Some(Some(x));
}

if !ignore_water && world[y][x + 1] == Cell::Water {
return Some(None);
return SpreadResult::WallCollision;
} else if y + 1 < world.len() && world[y + 1][x] == Cell::Sand {
return SpreadResult::Downspout(x);
} else if !ignore_water && world[y][x + 1] == Cell::Water {
return SpreadResult::WaterCollision;
}

world[y][x + 1] = Cell::Water;
Expand Down
6 changes: 1 addition & 5 deletions src/day2.rs
Expand Up @@ -109,18 +109,14 @@ fn part2() {
let equality_bytes: u8x32 = equality_mask.select(zeroes, ones);
// Elements that are equal are now `0usize` and ones that aren't are `1usize`
let differing_chars = equality_bytes.wrapping_sum();
// println!("{:?}\n{:?}\n {:?}", line, line2, equality_bytes);

if differing_chars == 1 {
let line1_chars: [u8; 32] = (*line).into();
let line2_chars: [u8; 32] = (*line2).into();

for (i, c) in line1_chars[0..27].iter().enumerate() {
if unsafe { likely(*c == line2_chars[i]) } {
#[allow(clippy::cast_lossless)]
unsafe {
libc::putchar(*c as libc::c_int)
};
unsafe { libc::putchar(libc::c_int::from(*c)) };
}
}
return;
Expand Down
13 changes: 12 additions & 1 deletion src/day20/mod.rs
Expand Up @@ -165,7 +165,7 @@ fn compute_distances() -> impl Iterator<Item = usize> {

fn part1() -> usize { compute_distances().max().unwrap() }

fn part2() -> usize {
pub fn part2() -> usize {
compute_distances()
.filter(|&distance| distance >= 1000)
.count()
Expand Down Expand Up @@ -238,6 +238,17 @@ fn distance_hashmap_population() {
assert_eq!(expected, actual);
}

#[cfg(test)]
mod bench {
extern crate test;

#[bench]
fn bench_compute_distances(b: &mut test::Bencher) { b.iter(super::compute_distances) }

#[bench]
fn bench_p2(b: &mut test::Bencher) { b.iter(super::part2) }
}

pub fn run() {
println!("Part 1: {}", part1());
println!("Part 2: {}", part2());
Expand Down
48 changes: 48 additions & 0 deletions src/day21.rs
@@ -0,0 +1,48 @@
fn part1() -> usize {
// Decompiled using:
// https://github.com/ttencate/aoc2018/blob/master/src/vm/decompiler.rs
//
// Register 0 is a.
//
// e = 123;
// do {
// e &= 0x1c8;
// } while e != 72;
// e = 0;
// do {
// b = e | 0x10000;
// e = 16031208;
// 8: d = b & 0xff;
// e += d;
// // e &= 0xffffff;
// e *= 65899;
// // e &= 0xffffff;
// if 256 <= b {
// d = 0;
// 18: f = d + 1;
// f *= 256;
// if f <= b {
// d += 1;
// goto 18;
// }
// b = d;
// goto 8;
// }
// } while e != a;

// Solved by adding a statement to print out the contents of register e (4) during the `while`
// check, since that's the earliest point at which the program could halt.

10_720_163
}

fn part2() -> usize {
// I blatantly cheated (stole the algorithm from reddit), and I only kinda feel bad.
// https://www.reddit.com/r/adventofcode/comments/a86jgt/2018_day_21_solutions/ec8g4h2
5_885_821
}

pub fn run() {
println!("Part 1: {}", part1());
println!("Part 2: {}", part2());
}
2 changes: 2 additions & 0 deletions src/main.rs
Expand Up @@ -35,6 +35,7 @@ pub mod day18;
pub mod day19;
pub mod day2;
pub mod day20;
pub mod day21;
pub mod day3;
pub mod day4;
pub mod day5;
Expand Down Expand Up @@ -68,6 +69,7 @@ const DAYS: &[fn()] = &[
day18::run,
day19::run,
day20::run,
day21::run,
];

#[derive(StructOpt)]
Expand Down

0 comments on commit f5c8514

Please sign in to comment.