Compare commits
3 commits
Author | SHA1 | Date | |
---|---|---|---|
074d8df6c5 | |||
b7025c1dc2 | |||
e8894ad31d |
16 changed files with 75 additions and 1965 deletions
32
Cargo.lock
generated
32
Cargo.lock
generated
|
@ -3,35 +3,3 @@
|
|||
[[package]]
|
||||
name = "aoc2019"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ncurses",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
|
||||
|
||||
[[package]]
|
||||
name = "ncurses"
|
||||
version = "5.99.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15699bee2f37e9f8828c7b35b2bc70d13846db453f2d507713b758fabe536b82"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
|
||||
|
|
23
Cargo.toml
23
Cargo.toml
|
@ -4,8 +4,7 @@ version = "0.1.0"
|
|||
authors = ["Stefan Schwarz <stefan@f2o.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
ncurses = "5.99.0"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[[bin]]
|
||||
name = "day1"
|
||||
|
@ -38,23 +37,3 @@ path = "src/day9/main.rs"
|
|||
[[bin]]
|
||||
name = "day10"
|
||||
path = "src/day10/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "day11"
|
||||
path = "src/day11/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "day12"
|
||||
path = "src/day12/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "day13"
|
||||
path = "src/day13/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "day14"
|
||||
path = "src/day14/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "day15"
|
||||
path = "src/day15/main.rs"
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
# Advent of Code 2019
|
1
input11
1
input11
|
@ -1 +0,0 @@
|
|||
3,8,1005,8,320,1106,0,11,0,0,0,104,1,104,0,3,8,1002,8,-1,10,101,1,10,10,4,10,1008,8,1,10,4,10,102,1,8,29,2,1005,1,10,1006,0,11,3,8,1002,8,-1,10,101,1,10,10,4,10,108,0,8,10,4,10,102,1,8,57,1,8,15,10,1006,0,79,1,6,3,10,3,8,102,-1,8,10,101,1,10,10,4,10,108,0,8,10,4,10,101,0,8,90,2,103,18,10,1006,0,3,2,105,14,10,3,8,102,-1,8,10,1001,10,1,10,4,10,108,0,8,10,4,10,101,0,8,123,2,9,2,10,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,1,10,4,10,1001,8,0,150,1,2,2,10,2,1009,6,10,1,1006,12,10,1006,0,81,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,1,10,4,10,102,1,8,187,3,8,102,-1,8,10,1001,10,1,10,4,10,1008,8,0,10,4,10,101,0,8,209,3,8,1002,8,-1,10,1001,10,1,10,4,10,1008,8,1,10,4,10,101,0,8,231,1,1008,11,10,1,1001,4,10,2,1104,18,10,3,8,102,-1,8,10,1001,10,1,10,4,10,108,1,8,10,4,10,1001,8,0,264,1,8,14,10,1006,0,36,3,8,1002,8,-1,10,1001,10,1,10,4,10,108,0,8,10,4,10,101,0,8,293,1006,0,80,1006,0,68,101,1,9,9,1007,9,960,10,1005,10,15,99,109,642,104,0,104,1,21102,1,846914232732,1,21102,1,337,0,1105,1,441,21102,1,387512115980,1,21101,348,0,0,1106,0,441,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,3,10,104,0,104,1,3,10,104,0,104,0,3,10,104,0,104,1,21102,209533824219,1,1,21102,1,395,0,1106,0,441,21101,0,21477985303,1,21102,406,1,0,1106,0,441,3,10,104,0,104,0,3,10,104,0,104,0,21101,868494234468,0,1,21101,429,0,0,1106,0,441,21102,838429471080,1,1,21102,1,440,0,1106,0,441,99,109,2,21201,-1,0,1,21101,0,40,2,21102,472,1,3,21101,0,462,0,1106,0,505,109,-2,2106,0,0,0,1,0,0,1,109,2,3,10,204,-1,1001,467,468,483,4,0,1001,467,1,467,108,4,467,10,1006,10,499,1102,1,0,467,109,-2,2106,0,0,0,109,4,2101,0,-1,504,1207,-3,0,10,1006,10,522,21101,0,0,-3,21202,-3,1,1,22101,0,-2,2,21102,1,1,3,21102,541,1,0,1106,0,546,109,-4,2105,1,0,109,5,1207,-3,1,10,1006,10,569,2207,-4,-2,10,1006,10,569,22102,1,-4,-4,1105,1,637,22102,1,-4,1,21201,-3,-1,2,21202,-2,2,3,21102,588,1,0,1105,1,546,22101,0,1,-4,21102,1,1,-1,2207,-4,-2,10,1006,10,607,21101,0,0,-1,22202,-2,-1,-2,2107,0,-3,10,1006,10,629,21201,-1,0,1,21102,629,1,0,105,1,504,21202,-2,-1,-2,22201,-4,-2,-4,109,-5,2105,1,0
|
1
input13
1
input13
File diff suppressed because one or more lines are too long
56
input14
56
input14
|
@ -1,56 +0,0 @@
|
|||
18 FHWM => 1 XGQNZ
|
||||
4 FVPWN, 9 CQGW => 7 QGHT
|
||||
22 KZMS, 1 DMCJL => 8 TWGCK
|
||||
1 LMGQN, 1 DSWDM, 1 GKGZ => 1 TGPH
|
||||
22 WCSW => 1 LVTG
|
||||
13 JSWR => 4 GKGZ
|
||||
162 ORE => 3 FVPWN
|
||||
59 CQGW, 15 MSNG, 6 XGKRF, 10 LJRQ, 1 HRKGV, 15 RKVC => 1 FUEL
|
||||
5 DMCJL => 1 QBLH
|
||||
2 XDRJ, 2 RKVC => 8 CTCNL
|
||||
1 QXHX => 5 GFPSK
|
||||
22 QGHT, 6 GFPSK, 5 DHTPL => 3 CSDR
|
||||
4 QGHT, 2 HFXD => 4 XDRJ
|
||||
10 WQCGV, 1 JSWR, 21 RHTLN => 7 VTPC
|
||||
11 CQGW, 1 FVPWN => 3 HFXD
|
||||
5 VTPC => 2 NCXW
|
||||
8 LDZVS => 6 DQLH
|
||||
117 ORE => 2 KWZNB
|
||||
3 TGPH, 1 JPFQ, 2 WHWLK, 5 RKVC, 16 DQLH => 9 LJRQ
|
||||
14 KWZNB, 2 CQGW => 8 MLPK
|
||||
6 LDZVS => 2 JSWR
|
||||
1 RKVC, 8 HCGT, 9 DHTPL => 6 FHWM
|
||||
3 DHTPL, 1 HWSR, 36 LDZVS => 6 DSWDM
|
||||
5 WHWLK, 1 LJHWT, 8 HSTHS => 7 VMPX
|
||||
22 ZJCDZ, 3 WQCGV => 5 DHTPL
|
||||
10 LJHWT, 32 GFPSK, 2 RHTLN => 4 HFRMP
|
||||
2 FKVD, 3 TWGCK, 1 HWSR => 1 RNLZW
|
||||
2 CSDR, 3 DQLH, 2 HSTHS => 9 JPFQ
|
||||
1 JSWR, 1 PCWS, 1 HFRMP => 3 XGKRF
|
||||
2 QGHT, 9 LVTG, 3 QBLH => 7 RHTLN
|
||||
10 LJHWT, 4 CTCNL => 8 QXHX
|
||||
16 MLPK, 1 HFXD => 9 ZJCDZ
|
||||
6 QGHT => 9 WCSW
|
||||
4 HWSR, 4 MLPK, 1 KZMS => 3 BGZHQ
|
||||
12 MLPK => 8 RKVC
|
||||
1 HWSR, 1 VNWFS => 7 BGFJ
|
||||
7 FHWM, 11 CTDF, 1 LDZVS => 2 VNWFS
|
||||
4 CTDF => 4 HSTHS
|
||||
2 ZJCDZ => 6 LJHWT
|
||||
1 VMPX, 1 NCXW, 1 HSTHS, 41 XGKRF, 30 HLNG, 1 GKGZ => 7 HRKGV
|
||||
1 XGQNZ, 10 PCWS, 3 BGFJ => 8 FKVD
|
||||
1 GFPSK, 1 DMCJL, 1 LVTG => 5 XDTZB
|
||||
3 WCSW => 5 KZMS
|
||||
6 TWGCK, 1 QXHX, 4 BGFJ => 2 LMGQN
|
||||
1 WCSW => 7 LDZVS
|
||||
1 XDTZB, 9 VNWFS => 3 WHWLK
|
||||
3 HFXD, 4 WCSW, 1 MLPK => 5 WQCGV
|
||||
2 BGFJ, 1 HSTHS, 22 MDCB, 10 HWSR, 6 RNLZW, 8 GKGZ => 5 MSNG
|
||||
4 QGHT, 1 FKVD => 7 MDCB
|
||||
9 MLPK, 3 LJHWT => 7 DMCJL
|
||||
121 ORE => 2 CQGW
|
||||
9 DHTPL, 2 BGZHQ => 8 CTDF
|
||||
2 JSWR, 30 RHTLN => 7 HLNG
|
||||
2 QBLH => 7 PCWS
|
||||
14 LVTG => 8 HWSR
|
||||
7 DMCJL => 1 HCGT
|
1
input15
1
input15
|
@ -1 +0,0 @@
|
|||
3,1033,1008,1033,1,1032,1005,1032,31,1008,1033,2,1032,1005,1032,58,1008,1033,3,1032,1005,1032,81,1008,1033,4,1032,1005,1032,104,99,1002,1034,1,1039,102,1,1036,1041,1001,1035,-1,1040,1008,1038,0,1043,102,-1,1043,1032,1,1037,1032,1042,1106,0,124,1002,1034,1,1039,1001,1036,0,1041,1001,1035,1,1040,1008,1038,0,1043,1,1037,1038,1042,1105,1,124,1001,1034,-1,1039,1008,1036,0,1041,1001,1035,0,1040,1002,1038,1,1043,101,0,1037,1042,1105,1,124,1001,1034,1,1039,1008,1036,0,1041,102,1,1035,1040,1001,1038,0,1043,101,0,1037,1042,1006,1039,217,1006,1040,217,1008,1039,40,1032,1005,1032,217,1008,1040,40,1032,1005,1032,217,1008,1039,1,1032,1006,1032,165,1008,1040,9,1032,1006,1032,165,1102,1,2,1044,1105,1,224,2,1041,1043,1032,1006,1032,179,1102,1,1,1044,1106,0,224,1,1041,1043,1032,1006,1032,217,1,1042,1043,1032,1001,1032,-1,1032,1002,1032,39,1032,1,1032,1039,1032,101,-1,1032,1032,101,252,1032,211,1007,0,35,1044,1106,0,224,1101,0,0,1044,1105,1,224,1006,1044,247,102,1,1039,1034,1002,1040,1,1035,1002,1041,1,1036,102,1,1043,1038,101,0,1042,1037,4,1044,1105,1,0,1,5,41,19,22,1,39,81,29,20,15,82,33,18,45,30,32,55,28,26,70,13,56,32,28,18,3,59,90,11,95,15,85,8,61,25,59,24,34,1,85,5,25,54,57,18,20,54,80,91,28,65,36,12,44,36,13,92,24,56,13,39,69,29,79,10,41,27,23,25,72,20,3,61,15,51,11,12,12,48,10,45,13,29,49,90,30,17,9,41,21,18,7,30,48,17,83,71,4,10,31,10,96,81,77,9,50,39,21,36,33,72,12,3,23,79,18,4,75,17,58,64,8,7,97,60,72,72,1,94,55,42,2,94,2,21,88,19,82,57,96,19,25,27,41,62,15,40,23,61,86,27,73,61,13,46,52,81,12,34,23,73,23,59,1,30,47,9,99,10,37,17,28,98,5,92,73,8,63,4,86,76,79,7,30,68,28,91,12,12,98,74,4,22,44,10,23,45,37,16,90,76,23,74,75,12,21,38,14,15,76,28,49,71,7,6,6,71,53,33,12,87,15,92,66,21,38,13,53,92,34,49,25,6,67,21,27,89,24,61,25,30,41,30,99,28,19,41,90,51,74,14,33,54,48,10,14,42,2,67,76,10,21,2,67,43,27,69,11,16,78,7,36,9,24,48,63,81,53,29,94,34,25,99,66,47,17,97,33,52,11,62,22,52,30,23,89,95,15,13,50,48,26,10,6,69,78,13,6,94,1,28,67,10,70,16,50,19,24,15,79,50,27,3,19,62,4,31,83,20,17,83,67,5,80,26,36,62,87,3,10,80,22,65,60,10,78,4,20,60,30,11,7,83,10,13,72,81,37,22,14,55,63,51,27,32,77,52,20,50,16,48,2,55,10,53,26,84,6,87,43,37,26,3,85,62,25,78,50,16,10,37,22,54,5,80,24,7,32,49,18,27,12,41,70,82,20,34,91,15,98,77,22,6,79,3,8,54,17,32,4,44,2,97,14,15,65,30,97,14,79,75,11,77,5,61,37,20,91,20,45,74,19,40,2,41,89,12,34,44,18,62,57,17,68,22,96,7,59,63,2,60,70,2,26,75,26,3,53,19,80,16,97,7,34,58,52,66,24,75,25,30,75,42,13,12,89,13,3,84,92,1,75,30,54,43,2,56,15,1,15,84,99,6,98,42,17,29,1,18,26,70,71,29,91,23,21,87,66,18,38,32,18,81,65,2,58,99,12,4,84,24,32,88,30,67,49,29,59,64,18,70,10,24,56,5,27,97,50,4,28,85,65,16,67,83,15,16,61,18,86,8,36,25,36,29,97,45,19,81,41,29,45,30,69,26,57,93,27,72,34,30,99,61,2,48,16,12,76,98,28,14,32,32,90,48,10,30,57,23,39,2,8,39,33,13,88,34,31,74,15,60,8,47,60,31,5,79,1,98,86,33,3,99,33,62,11,96,25,22,38,98,84,3,56,70,49,3,8,56,87,4,29,59,65,26,34,77,7,14,78,26,25,70,49,3,31,45,92,24,95,17,4,9,4,96,64,92,27,67,4,99,6,44,7,16,86,2,75,1,6,68,81,4,1,44,49,7,92,8,40,36,25,81,13,56,99,10,2,30,72,6,43,30,12,43,93,19,20,23,95,10,19,66,63,28,96,40,50,8,15,56,38,13,93,42,71,12,18,87,8,4,21,85,9,2,66,77,10,80,26,61,9,43,20,88,10,39,67,55,31,49,17,58,26,80,20,84,54,49,5,73,11,52,15,63,7,62,24,57,92,61,25,87,56,37,31,38,14,99,0,0,21,21,1,10,1,0,0,0,0,0,0
|
|
@ -1,3 +1,4 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
@ -45,6 +46,18 @@ impl Sub for &XY {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for XY {
|
||||
fn partial_cmp(&self, other: &XY) -> Option<Ordering> {
|
||||
self.angle().partial_cmp(&other.angle())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for XY {
|
||||
fn cmp(&self, other: &XY) -> Ordering {
|
||||
self.angle().partial_cmp(&other.angle()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl XY {
|
||||
fn direction(&self) -> Self {
|
||||
let d = gcd(self.y, self.x).abs();
|
||||
|
@ -58,88 +71,70 @@ impl XY {
|
|||
}
|
||||
}
|
||||
|
||||
fn distance(&self) -> f64 {
|
||||
let sum = (self.x.pow(2) + self.y.pow(2)) as f64;
|
||||
(sum as f64).sqrt()
|
||||
}
|
||||
|
||||
fn angle(&self) -> f64 {
|
||||
let a = (-self.y as f64).atan2(-self.x as f64) * (180 as f64 / std::f64::consts::PI)
|
||||
- 90 as f64;
|
||||
if a < 0 as f64 {
|
||||
a + 360 as f64
|
||||
} else {
|
||||
a
|
||||
}
|
||||
(self.y as f64).atan2(self.x as f64) * (180 as f64 / std::f64::consts::PI)
|
||||
}
|
||||
}
|
||||
|
||||
struct MonitoringStation {
|
||||
map: Vec<XY>,
|
||||
map: HashMap<XY, Position>,
|
||||
size_x: usize,
|
||||
size_y: usize,
|
||||
}
|
||||
|
||||
impl MonitoringStation {
|
||||
fn laser(&self, root: &XY, skip: usize, n: usize) -> Vec<XY> {
|
||||
let mut map: HashMap<XY, Vec<&XY>> = HashMap::new();
|
||||
// collect vec for each direction
|
||||
fn laser(&self, root: &XY) -> impl Iterator<Item = XY> + '_ {
|
||||
let mut map: HashMap<XY, Vec<XY>> = HashMap::new();
|
||||
map = self
|
||||
.map
|
||||
.iter()
|
||||
.filter_map(|xy| {
|
||||
if xy == root {
|
||||
.filter_map(|(xy, pos)| {
|
||||
if *pos == Position::Empty {
|
||||
return None;
|
||||
}
|
||||
let rel = xy - &root;
|
||||
Some((xy, rel.direction()))
|
||||
})
|
||||
.fold(map, |mut acc, (xy, dir)| {
|
||||
acc.entry(dir).or_insert(vec![]).push(xy);
|
||||
acc.entry(dir).or_insert(vec![]).push(xy.clone());
|
||||
acc
|
||||
});
|
||||
|
||||
// order angle vecs by distance
|
||||
map.iter_mut().for_each(|(_, val)| {
|
||||
val.sort_by(|a, b| {
|
||||
((root - b).distance().partial_cmp(&(root - a).distance())).unwrap()
|
||||
});
|
||||
});
|
||||
|
||||
// lets iterate over sorted angles
|
||||
let mut angles = map.keys().cloned().collect::<Vec<XY>>();
|
||||
angles.sort_by(|a, b| a.angle().partial_cmp(&b.angle()).unwrap());
|
||||
angles.sort();
|
||||
|
||||
// cycle around and return first element of each vec
|
||||
angles
|
||||
.iter()
|
||||
.cycle()
|
||||
.map(|xy| match map.get_mut(xy) {
|
||||
let mut popped = true;
|
||||
let mut cycles = 0;
|
||||
while popped {
|
||||
popped = false;
|
||||
cycles += 1;
|
||||
angles.iter().for_each(|xy| match map.get_mut(xy) {
|
||||
Some(xy) => {
|
||||
if let Some(xy) = xy.pop() {
|
||||
Some(xy)
|
||||
} else {
|
||||
None
|
||||
popped = true;
|
||||
println!("{:?}", xy)
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
})
|
||||
.flatten()
|
||||
.skip(skip)
|
||||
.take(n)
|
||||
.cloned()
|
||||
.collect::<Vec<XY>>()
|
||||
None => (),
|
||||
});
|
||||
}
|
||||
println!("cycles: {}", cycles);
|
||||
self.map.iter().map(|(xy, _)| xy.clone())
|
||||
}
|
||||
|
||||
fn find(&self) -> (usize, XY) {
|
||||
let best = (0, &XY::from((0, 0)));
|
||||
let best = self
|
||||
.map
|
||||
let best = (0, XY::from((0, 0)));
|
||||
self.map
|
||||
.iter()
|
||||
.map(|root| {
|
||||
.filter(|(_, pos)| **pos == Position::Astroid)
|
||||
.map(|(root, _)| {
|
||||
let insight = self
|
||||
.map
|
||||
.iter()
|
||||
// get direction to any of the astroids
|
||||
.filter_map(|xy| {
|
||||
.filter_map(|(xy, pos)| {
|
||||
if *pos == Position::Empty {
|
||||
return None;
|
||||
}
|
||||
let rel = xy - root;
|
||||
Some(rel.direction())
|
||||
})
|
||||
|
@ -152,17 +147,12 @@ impl MonitoringStation {
|
|||
- 1;
|
||||
(insight, root)
|
||||
})
|
||||
.fold(
|
||||
best,
|
||||
|acc, astroid| {
|
||||
.fold(best, |acc, astroid| {
|
||||
if astroid.0 > acc.0 {
|
||||
astroid
|
||||
} else {
|
||||
acc
|
||||
return (astroid.0, astroid.1.clone());
|
||||
}
|
||||
},
|
||||
);
|
||||
(best.0, best.1.clone())
|
||||
acc
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,23 +161,33 @@ where
|
|||
T: BufRead,
|
||||
{
|
||||
fn from(reader: T) -> Self {
|
||||
let map: HashMap<XY, Position> = HashMap::new();
|
||||
let mut size_y = 0;
|
||||
let map = reader
|
||||
.split(b'\n')
|
||||
.enumerate()
|
||||
.flat_map(|(y, line)| {
|
||||
size_y += 1;
|
||||
line.unwrap()
|
||||
.into_iter()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(move |(x, c)| {
|
||||
.map(|(x, c)| {
|
||||
let xy = XY::from((x, y));
|
||||
match Position::from(&c) {
|
||||
Position::Astroid => Some(xy),
|
||||
Position::Empty => None,
|
||||
let p = Position::from(c);
|
||||
(xy, p)
|
||||
})
|
||||
.collect::<Vec<(XY, Position)>>()
|
||||
})
|
||||
.fold(map, |mut map, (xy, p)| {
|
||||
map.insert(xy.clone(), p.clone());
|
||||
map
|
||||
});
|
||||
let size_x = map.iter().count() / size_y;
|
||||
MonitoringStation {
|
||||
map,
|
||||
size_x,
|
||||
size_y,
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Vec<XY>>();
|
||||
MonitoringStation { map }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,10 +196,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let reader = BufReader::new(f);
|
||||
let station = MonitoringStation::from(reader);
|
||||
let best = station.find();
|
||||
let test = station.laser(&best.1).next();
|
||||
println!("best: {:?}", best);
|
||||
let lasered = station.laser(&best.1, 199, 1);
|
||||
let lasered = lasered.iter().next().unwrap();
|
||||
println!("lasered: {:?}", lasered);
|
||||
println!("test: {:?}", test);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -323,62 +322,4 @@ mod tests {
|
|||
assert_eq!(xy, XY { x: 11, y: 13 });
|
||||
assert_eq!(count, 210);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn example6() {
|
||||
let reader = BufReader::new(
|
||||
"
|
||||
.#....#####...#..
|
||||
##...##.#####..##
|
||||
##...#...#.#####.
|
||||
..#.....#...###..
|
||||
..#.#.....#....##
|
||||
"
|
||||
.trim()
|
||||
.as_bytes(),
|
||||
);
|
||||
let ms = MonitoringStation::from(reader);
|
||||
ms.find();
|
||||
assert_eq!(
|
||||
ms.laser(&XY::from((8, 3)), 0, 36),
|
||||
vec![
|
||||
XY { x: 8, y: 1 },
|
||||
XY { x: 9, y: 0 },
|
||||
XY { x: 9, y: 1 },
|
||||
XY { x: 10, y: 0 },
|
||||
XY { x: 9, y: 2 },
|
||||
XY { x: 11, y: 1 },
|
||||
XY { x: 12, y: 1 },
|
||||
XY { x: 11, y: 2 },
|
||||
XY { x: 15, y: 1 },
|
||||
XY { x: 12, y: 2 },
|
||||
XY { x: 13, y: 2 },
|
||||
XY { x: 14, y: 2 },
|
||||
XY { x: 15, y: 2 },
|
||||
XY { x: 12, y: 3 },
|
||||
XY { x: 16, y: 4 },
|
||||
XY { x: 15, y: 4 },
|
||||
XY { x: 10, y: 4 },
|
||||
XY { x: 4, y: 4 },
|
||||
XY { x: 2, y: 4 },
|
||||
XY { x: 2, y: 3 },
|
||||
XY { x: 0, y: 2 },
|
||||
XY { x: 1, y: 2 },
|
||||
XY { x: 0, y: 1 },
|
||||
XY { x: 1, y: 1 },
|
||||
XY { x: 5, y: 2 },
|
||||
XY { x: 1, y: 0 },
|
||||
XY { x: 5, y: 1 },
|
||||
XY { x: 6, y: 1 },
|
||||
XY { x: 6, y: 0 },
|
||||
XY { x: 7, y: 0 },
|
||||
XY { x: 8, y: 0 },
|
||||
XY { x: 10, y: 1 },
|
||||
XY { x: 14, y: 0 },
|
||||
XY { x: 16, y: 1 },
|
||||
XY { x: 13, y: 3 },
|
||||
XY { x: 14, y: 3 }
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,186 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
|
||||
use aoc2019::intcode;
|
||||
|
||||
#[derive(Hash, Default, PartialEq, Eq, Clone)]
|
||||
struct Panel {
|
||||
x: isize,
|
||||
y: isize,
|
||||
}
|
||||
|
||||
enum Color {
|
||||
Black,
|
||||
White,
|
||||
}
|
||||
|
||||
impl From<isize> for Color {
|
||||
fn from(color: isize) -> Self {
|
||||
match color {
|
||||
0 => Color::Black,
|
||||
1 => Color::White,
|
||||
_ => panic!("invalid color"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<isize> for &Color {
|
||||
fn into(self) -> isize {
|
||||
match self {
|
||||
Color::Black => 0,
|
||||
Color::White => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Color {
|
||||
fn char(&self) -> char {
|
||||
match self {
|
||||
Color::Black => ' ',
|
||||
Color::White => '\u{2588}',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
enum Turn {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl From<isize> for Turn {
|
||||
fn from(turn: isize) -> Self {
|
||||
match turn {
|
||||
0 => Turn::Left,
|
||||
1 => Turn::Right,
|
||||
_ => panic!("invalid turn direction"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Robot {
|
||||
panel: Panel,
|
||||
direction: Direction,
|
||||
}
|
||||
|
||||
impl Robot {
|
||||
fn turn(&mut self, direction: Turn) {
|
||||
self.direction = match self.direction {
|
||||
Direction::Up => match direction {
|
||||
Turn::Left => Direction::Left,
|
||||
Turn::Right => Direction::Right,
|
||||
},
|
||||
Direction::Right => match direction {
|
||||
Turn::Left => Direction::Up,
|
||||
Turn::Right => Direction::Down,
|
||||
},
|
||||
Direction::Down => match direction {
|
||||
Turn::Left => Direction::Right,
|
||||
Turn::Right => Direction::Left,
|
||||
},
|
||||
Direction::Left => match direction {
|
||||
Turn::Left => Direction::Down,
|
||||
Turn::Right => Direction::Up,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn step(&mut self) {
|
||||
self.panel = match self.direction {
|
||||
Direction::Up => Panel {
|
||||
x: self.panel.x,
|
||||
y: self.panel.y + 1,
|
||||
},
|
||||
Direction::Left => Panel {
|
||||
x: self.panel.x + 1,
|
||||
y: self.panel.y,
|
||||
},
|
||||
Direction::Down => Panel {
|
||||
x: self.panel.x,
|
||||
y: self.panel.y - 1,
|
||||
},
|
||||
Direction::Right => Panel {
|
||||
x: self.panel.x - 1,
|
||||
y: self.panel.y,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> io::Result<()> {
|
||||
let filename = env::args().nth(1).expect("provide file as first param");
|
||||
let mut computer = intcode::Computer::from(File::open(filename)?);
|
||||
|
||||
let mut canvas: HashMap<Panel, Color> = HashMap::new();
|
||||
canvas.insert(Panel { x: 0, y: 0 }, Color::White);
|
||||
let mut robot = Robot {
|
||||
panel: Panel::default(),
|
||||
direction: Direction::Up,
|
||||
};
|
||||
|
||||
let mut input: isize = canvas
|
||||
.get(&robot.panel)
|
||||
.or_else(|| Some(&Color::Black))
|
||||
.unwrap()
|
||||
.into();
|
||||
while let Some(output) = computer.run_io(input) {
|
||||
canvas.insert(robot.panel.clone(), Color::from(output));
|
||||
|
||||
let output = computer.run_must_output();
|
||||
robot.turn(Turn::from(output));
|
||||
robot.step();
|
||||
|
||||
input = canvas
|
||||
.get(&robot.panel)
|
||||
.or_else(|| Some(&Color::Black))
|
||||
.unwrap()
|
||||
.into();
|
||||
}
|
||||
|
||||
let mut xmin: isize = 0;
|
||||
let mut xmax: isize = 0;
|
||||
let mut ymin: isize = 0;
|
||||
let mut ymax: isize = 0;
|
||||
let painted = canvas
|
||||
.iter()
|
||||
.map(|(k, _)| {
|
||||
if k.x < xmin {
|
||||
xmin = k.x;
|
||||
}
|
||||
if k.x > xmax {
|
||||
xmax = k.x;
|
||||
}
|
||||
if k.y < ymin {
|
||||
ymin = k.y;
|
||||
}
|
||||
if k.y > ymax {
|
||||
ymax = k.y;
|
||||
}
|
||||
})
|
||||
.count();
|
||||
println!("painted: {}", painted);
|
||||
println!("canvas: x:{}-{} y:{}-{}", xmin, xmax, ymin, ymax);
|
||||
|
||||
for y in (ymin..=ymax).rev() {
|
||||
for x in (xmin..=xmax).rev() {
|
||||
print!(
|
||||
"{}",
|
||||
canvas
|
||||
.get(&Panel { x, y })
|
||||
.unwrap_or_else(|| &Color::Black)
|
||||
.char()
|
||||
)
|
||||
}
|
||||
println!()
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,332 +0,0 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::collections::HashMap;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::iter::FromIterator;
|
||||
|
||||
#[derive(PartialEq, Debug, Default, Clone, Hash)]
|
||||
struct Object {
|
||||
x: isize,
|
||||
y: isize,
|
||||
z: isize,
|
||||
}
|
||||
|
||||
impl From<(isize, isize, isize)> for Object {
|
||||
fn from(o: (isize, isize, isize)) -> Self {
|
||||
Object {
|
||||
x: o.0,
|
||||
y: o.1,
|
||||
z: o.2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Object {
|
||||
fn attract(&self, other: &Object, v: &mut Object) {
|
||||
v.x += Object::cmp(self.x, other.x);
|
||||
v.y += Object::cmp(self.y, other.y);
|
||||
v.z += Object::cmp(self.z, other.z);
|
||||
}
|
||||
|
||||
fn apply(&mut self, v: &Object) {
|
||||
self.x += v.x;
|
||||
self.y += v.y;
|
||||
self.z += v.z;
|
||||
}
|
||||
|
||||
fn cmp(a: isize, b: isize) -> isize {
|
||||
match b.cmp(&a) {
|
||||
Ordering::Less => -1,
|
||||
Ordering::Equal => 0,
|
||||
Ordering::Greater => 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn energy(&self) -> usize {
|
||||
return (self.x.abs() + self.y.abs() + self.z.abs()) as usize;
|
||||
}
|
||||
}
|
||||
|
||||
struct Space {
|
||||
iteration: u128,
|
||||
moons: Vec<Object>,
|
||||
velocy: Vec<Object>,
|
||||
}
|
||||
|
||||
impl From<Vec<Object>> for Space {
|
||||
fn from(moons: Vec<Object>) -> Self {
|
||||
let iteration = 0;
|
||||
let velocy = Vec::from_iter((0..moons.len()).map(|_| Object::default()));
|
||||
Space {
|
||||
iteration,
|
||||
moons,
|
||||
velocy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Space {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.moons.iter().for_each(|m| m.hash(state));
|
||||
self.velocy.iter().for_each(|v| v.hash(state));
|
||||
}
|
||||
}
|
||||
|
||||
impl Space {
|
||||
fn step(&mut self) {
|
||||
self.iteration += 1;
|
||||
let moons: &mut Vec<Object> = self.moons.as_mut();
|
||||
let velocy: &mut Vec<Object> = self.velocy.as_mut();
|
||||
|
||||
moons.iter().enumerate().for_each(|(idx, a)| {
|
||||
moons.iter().for_each(|b| {
|
||||
if a == b {
|
||||
return;
|
||||
}
|
||||
a.attract(b, velocy.get_mut(idx).unwrap())
|
||||
})
|
||||
});
|
||||
moons
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.for_each(|(idx, m)| m.apply(velocy.get(idx).unwrap()));
|
||||
}
|
||||
|
||||
fn status(&self) -> impl Iterator<Item = (&'_ Object, &'_ Object)> {
|
||||
self.moons
|
||||
.iter()
|
||||
.zip(self.velocy.iter())
|
||||
.map(|(a, b)| (a, b))
|
||||
}
|
||||
|
||||
fn iteration(&self) -> u128 {
|
||||
return self.iteration;
|
||||
}
|
||||
|
||||
fn hashed(&self) -> u64 {
|
||||
let mut hash = DefaultHasher::new();
|
||||
self.hash(&mut hash);
|
||||
hash.finish()
|
||||
}
|
||||
|
||||
fn reapeats(mut self) -> u128 {
|
||||
let mut known: HashMap<u64, u128> = HashMap::new();
|
||||
loop {
|
||||
println!("a lot");
|
||||
known.insert(self.hashed(), self.iteration());
|
||||
// 4686774924
|
||||
for _ in 0..10000000 {
|
||||
self.step();
|
||||
if let Some(iteration) = known.get(&self.hashed()) {
|
||||
let ago = self.iteration() - iteration;
|
||||
return ago;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Clone)]
|
||||
struct Axis {
|
||||
p: isize,
|
||||
}
|
||||
|
||||
impl From<isize> for Axis {
|
||||
fn from(p: isize) -> Self {
|
||||
Axis { p }
|
||||
}
|
||||
}
|
||||
|
||||
impl Axis {
|
||||
fn attract(&self, other: &Axis, v: &mut Axis) {
|
||||
v.p += Axis::cmp(self.p, other.p);
|
||||
}
|
||||
|
||||
fn apply(&mut self, v: &Axis) {
|
||||
self.p += v.p;
|
||||
}
|
||||
|
||||
fn cmp(a: isize, b: isize) -> isize {
|
||||
match b.cmp(&a) {
|
||||
Ordering::Less => -1,
|
||||
Ordering::Equal => 0,
|
||||
Ordering::Greater => 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn energy(&self) -> usize {
|
||||
return self.p.abs() as usize;
|
||||
}
|
||||
}
|
||||
|
||||
struct AxisSpace {
|
||||
iteration: usize,
|
||||
moons: Vec<Axis>,
|
||||
velocy: Vec<Axis>,
|
||||
}
|
||||
|
||||
impl From<Vec<isize>> for AxisSpace {
|
||||
fn from(moons: Vec<isize>) -> Self {
|
||||
let iteration = 0;
|
||||
let moons = Vec::from_iter(moons.iter().map(|i| Axis::from(*i)));
|
||||
let velocy = Vec::from_iter((0..moons.len()).map(|_| Axis::from(0)));
|
||||
AxisSpace {
|
||||
iteration,
|
||||
moons,
|
||||
velocy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AxisSpace {
|
||||
fn step(&mut self) {
|
||||
self.iteration += 1;
|
||||
let moons: &mut Vec<Axis> = self.moons.as_mut();
|
||||
let velocy: &mut Vec<Axis> = self.velocy.as_mut();
|
||||
moons.iter().enumerate().for_each(|(idx, a)| {
|
||||
moons.iter().for_each(|b| {
|
||||
if a == b {
|
||||
return;
|
||||
}
|
||||
a.attract(&b, &mut velocy[idx])
|
||||
})
|
||||
});
|
||||
moons
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.for_each(|(idx, m)| m.apply(velocy.get(idx).unwrap()));
|
||||
}
|
||||
|
||||
fn iteration(&self) -> usize {
|
||||
return self.iteration;
|
||||
}
|
||||
|
||||
fn reapeats(mut self) -> usize {
|
||||
let want = self.moons.clone();
|
||||
self.step();
|
||||
while self.moons != want {
|
||||
self.step();
|
||||
}
|
||||
return self.iteration() + 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn gcd(x: usize, y: usize) -> usize {
|
||||
let mut x = x;
|
||||
let mut y = y;
|
||||
while y != 0 {
|
||||
let t = y;
|
||||
y = x % y;
|
||||
x = t;
|
||||
}
|
||||
x
|
||||
}
|
||||
|
||||
fn lcm(a: usize, b: usize) -> usize {
|
||||
a * b / gcd(a, b)
|
||||
}
|
||||
|
||||
fn lcm3(a: usize, b: usize, c: usize) -> usize {
|
||||
lcm(a, lcm(b, c))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// part1
|
||||
let moons = vec![
|
||||
Object::from((17, 5, 1)),
|
||||
Object::from((-2, -8, 8)),
|
||||
Object::from((7, -6, 14)),
|
||||
Object::from((1, -10, 4)),
|
||||
];
|
||||
let mut space = Space::from(moons);
|
||||
let mut step_energy = 0;
|
||||
(1..=100).for_each(|_i| {
|
||||
(1..=10).for_each(|_| {
|
||||
space.step();
|
||||
});
|
||||
step_energy = space
|
||||
.status()
|
||||
.map(|(o, v)| o.energy() * v.energy())
|
||||
.fold(0, |mut acc, c| {
|
||||
acc += c;
|
||||
acc
|
||||
});
|
||||
});
|
||||
println!("energy: {}", step_energy);
|
||||
|
||||
// part2
|
||||
let xspace = AxisSpace::from(vec![-1, 2, 4, 3]);
|
||||
let yspace = AxisSpace::from(vec![0, -10, -8, 5]);
|
||||
let zspace = AxisSpace::from(vec![2, -7, 8, -1]);
|
||||
let total = lcm3(xspace.reapeats(), yspace.reapeats(), zspace.reapeats());
|
||||
println!("matching space found {} iterations ago", total);
|
||||
|
||||
let xspace = AxisSpace::from(vec![-8, 5, 2, 9]);
|
||||
let yspace = AxisSpace::from(vec![-10, 5, -7, -8]);
|
||||
let zspace = AxisSpace::from(vec![0, 10, 3, -3]);
|
||||
let total = lcm3(xspace.reapeats(), yspace.reapeats(), zspace.reapeats());
|
||||
println!("matching space found {} iterations ago", total);
|
||||
|
||||
let xspace = AxisSpace::from(vec![17, -2, 7, 1]);
|
||||
let yspace = AxisSpace::from(vec![5, -8, -6, -10]);
|
||||
let zspace = AxisSpace::from(vec![1, 8, 14, 4]);
|
||||
let total = lcm3(xspace.reapeats(), yspace.reapeats(), zspace.reapeats());
|
||||
println!("matching space found {} iterations ago", total);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn day12example1() {
|
||||
let space = Space::from(vec![
|
||||
Object::from((-1, 0, 2)),
|
||||
Object::from((2, -10, -7)),
|
||||
Object::from((4, -8, 8)),
|
||||
Object::from((3, 5, -1)),
|
||||
]);
|
||||
assert!(space.status().eq(vec![
|
||||
(&Object::from((-1, 0, 2)), &Object::default()),
|
||||
(&Object::from((2, -10, -7)), &Object::from((0, 0, 0))),
|
||||
(&Object::from((4, -8, 8)), &Object::from((0, 0, 0))),
|
||||
(&Object::from((3, 5, -1)), &Object::from((0, 0, 0))),
|
||||
]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn day12part1() {
|
||||
let mut space = Space::from(vec![
|
||||
Object::from((17, 5, 1)),
|
||||
Object::from((-2, -8, 8)),
|
||||
Object::from((7, -6, 14)),
|
||||
Object::from((1, -10, 4)),
|
||||
]);
|
||||
(1..=1000).for_each(|_| {
|
||||
space.step();
|
||||
});
|
||||
|
||||
let energy = space
|
||||
.status()
|
||||
.map(|(o, v)| {
|
||||
println!("{:?} {:?}", o, v);
|
||||
o.energy() * v.energy()
|
||||
})
|
||||
.fold(0, |mut acc, c| {
|
||||
acc += c;
|
||||
acc
|
||||
});
|
||||
assert_eq!(energy, 9876)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn day12example2() {
|
||||
let space = Space::from(vec![
|
||||
Object::from((-1, 0, 2)),
|
||||
Object::from((2, -10, -7)),
|
||||
Object::from((4, -8, 8)),
|
||||
Object::from((3, 5, -1)),
|
||||
]);
|
||||
assert_eq!(space.reapeats(), 2772)
|
||||
}
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
use aoc2019::intcode;
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
|
||||
use ncurses;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let f = File::open("input13")?;
|
||||
let mut computer = intcode::Computer::from(f);
|
||||
let mut grid: Grid = HashMap::new();
|
||||
while let Some(output) = computer.run_until_n_output(3) {
|
||||
let mut output = output.into_iter();
|
||||
let xy = XY {
|
||||
x: output.next().unwrap(),
|
||||
y: output.next().unwrap(),
|
||||
};
|
||||
let tile = Tile::from(output.next().unwrap());
|
||||
grid.insert(xy, tile);
|
||||
}
|
||||
|
||||
// part 1
|
||||
let blocks = grid.iter().filter(|(_, v)| **v == Tile::Block).count();
|
||||
println!("blocks: {}", blocks);
|
||||
|
||||
// part2
|
||||
ncurses::initscr();
|
||||
ncurses::noecho();
|
||||
let mut computer = computer.clone_with_modified_program(vec![(0, 2)]);
|
||||
let mut paddlex = 0;
|
||||
let mut score = 0;
|
||||
while !computer.halted {
|
||||
if let Some(output) = &computer.run_until_input() {
|
||||
output.chunks(3).for_each(|c| {
|
||||
if let &[x, y, value] = c {
|
||||
if x == -1 && y == 0 {
|
||||
score = value;
|
||||
} else {
|
||||
let tile = Tile::from(value);
|
||||
if Tile::from(value) == Tile::HorizontalPaddle {
|
||||
paddlex = x;
|
||||
}
|
||||
ncurses::mvprintw((y + 1) as i32, x as i32, tile.char());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
let np = next_pos(&mut computer.clone());
|
||||
ncurses::mvprintw(
|
||||
0,
|
||||
0,
|
||||
&format!(
|
||||
"willx: {:0>2} paddlex: {:0>2} score: {:0>7}",
|
||||
np, paddlex, score
|
||||
),
|
||||
);
|
||||
let joystick = match np.cmp(&paddlex) {
|
||||
Ordering::Less => -1,
|
||||
Ordering::Equal => 0,
|
||||
Ordering::Greater => 1,
|
||||
};
|
||||
computer.push_input_and_step(joystick);
|
||||
ncurses::refresh();
|
||||
}
|
||||
ncurses::endwin();
|
||||
println!("score: {}", score);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn next_pos(computer: &mut intcode::Computer) -> isize {
|
||||
while !computer.halted {
|
||||
if let Some(output) = computer.run_until_input() {
|
||||
for c in output.chunks(3) {
|
||||
if let &[x, y, value] = c {
|
||||
if x == -1 && y == 0 {
|
||||
// skip score
|
||||
} else if Tile::from(value) == Tile::Ball && y == 23 {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
computer.push_input_and_step(0);
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq)]
|
||||
enum Tile {
|
||||
Empty,
|
||||
Wall,
|
||||
Block,
|
||||
HorizontalPaddle,
|
||||
Ball,
|
||||
}
|
||||
|
||||
impl From<isize> for Tile {
|
||||
fn from(i: isize) -> Self {
|
||||
match i {
|
||||
0 => Tile::Empty,
|
||||
1 => Tile::Wall,
|
||||
2 => Tile::Block,
|
||||
3 => Tile::HorizontalPaddle,
|
||||
4 => Tile::Ball,
|
||||
_ => unreachable!("there are only 5 tiles..."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Tile {
|
||||
fn char(&self) -> &str {
|
||||
match self {
|
||||
Tile::Empty => " ",
|
||||
Tile::Wall => "X",
|
||||
Tile::Block => "O",
|
||||
Tile::HorizontalPaddle => "-",
|
||||
Tile::Ball => "*",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Hash)]
|
||||
struct XY {
|
||||
x: isize,
|
||||
y: isize,
|
||||
}
|
||||
|
||||
impl From<(isize, isize)> for XY {
|
||||
fn from(xy: (isize, isize)) -> Self {
|
||||
XY { x: xy.0, y: xy.1 }
|
||||
}
|
||||
}
|
||||
|
||||
type Grid = HashMap<XY, Tile>;
|
|
@ -1,385 +0,0 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::*;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
type Resource = String;
|
||||
type Pipeline = HashMap<Resource, ResourceBuilder>;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ResourceBuilder {
|
||||
requirements: Vec<Resource>,
|
||||
out_count: usize,
|
||||
}
|
||||
|
||||
impl From<(Vec<Resource>, usize)> for ResourceBuilder {
|
||||
fn from(build: (Vec<Resource>, usize)) -> Self {
|
||||
ResourceBuilder {
|
||||
requirements: build.0,
|
||||
out_count: build.1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct NanoFactory {
|
||||
pipeline: Pipeline,
|
||||
ordered: Vec<Resource>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum NanoFactoryBuildState {
|
||||
InputCount,
|
||||
InputResource,
|
||||
OutputArrow,
|
||||
OutputCount,
|
||||
OutputResource,
|
||||
}
|
||||
|
||||
impl NanoFactory {
|
||||
/// sort resources
|
||||
fn resolve_order(&mut self) {
|
||||
self.ordered = vec![Resource::from("ORE")];
|
||||
let mut sortable: HashMap<Resource, Vec<Resource>> = self
|
||||
.pipeline
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.requirements.clone()))
|
||||
.collect();
|
||||
while sortable.len() > 0 {
|
||||
sortable = sortable
|
||||
.into_iter()
|
||||
.filter_map(|(k, v)| {
|
||||
if v.iter().all(|i| self.ordered.contains(i)) {
|
||||
self.ordered.push(k);
|
||||
None
|
||||
} else {
|
||||
Some((k, v))
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
self.ordered.reverse();
|
||||
self.ordered.retain(|res| res != "ORE");
|
||||
}
|
||||
|
||||
/// calculate how much ores are required for required_fuel
|
||||
fn resource_usage(&self, required_fuel: u128) -> usize {
|
||||
// create storage to track leftover resources
|
||||
let mut storage: HashMap<Resource, u128> = HashMap::new();
|
||||
storage.insert(Resource::from("FUEL"), required_fuel);
|
||||
|
||||
// calculate resource costs
|
||||
self.ordered.iter().for_each(|res| {
|
||||
let rb = self.pipeline.get(res).unwrap();
|
||||
let count = (storage.get(res).unwrap().clone() + (rb.out_count as u128) - 1)
|
||||
/ (rb.out_count as u128);
|
||||
rb.requirements.iter().for_each(|req| {
|
||||
storage
|
||||
.entry(req.clone())
|
||||
.and_modify(|v| *v += count)
|
||||
.or_insert(count);
|
||||
storage.insert(res.clone(), 0);
|
||||
})
|
||||
});
|
||||
|
||||
// return cost for ore
|
||||
println!("{:?}", storage);
|
||||
*storage.get("ORE").unwrap() as usize
|
||||
}
|
||||
|
||||
/// generate a single fuel
|
||||
fn generate_fuel(&self) -> usize {
|
||||
let mut storage: HashMap<Resource, usize> = HashMap::new();
|
||||
self.lookup_resource(&mut storage, "FUEL")
|
||||
}
|
||||
|
||||
/// recursively calculate the cost of a resource in ores
|
||||
fn lookup_resource(&self, storage: &mut HashMap<Resource, usize>, resource: &str) -> usize {
|
||||
if resource == "ORE" {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// use from storage
|
||||
if let Some(count) = storage.get_mut(resource) {
|
||||
if *count > 0 {
|
||||
*count -= 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// initialize storage with zero
|
||||
if storage.get(resource).is_none() {
|
||||
storage.insert(resource.into(), 0);
|
||||
}
|
||||
|
||||
// build resource and and to storage
|
||||
let required = self
|
||||
.pipeline
|
||||
.get(resource)
|
||||
.expect("unable to find resource");
|
||||
let cost = required.requirements.iter().fold(0, |mut acc, c| {
|
||||
acc += self.lookup_resource(storage, c);
|
||||
acc
|
||||
});
|
||||
|
||||
// update storage
|
||||
let stored = storage.get_mut(resource).unwrap();
|
||||
*stored += required.out_count - 1;
|
||||
|
||||
cost
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for NanoFactory
|
||||
where
|
||||
T: BufRead,
|
||||
{
|
||||
fn from(bufreader: T) -> Self {
|
||||
let mut f = NanoFactory::default();
|
||||
let mut state = NanoFactoryBuildState::InputCount;
|
||||
bufreader.split(b'\n').enumerate().for_each(|(nr, line)| {
|
||||
let line = line.unwrap();
|
||||
let mut line = line.into_iter().peekable();
|
||||
let mut count_var = 0;
|
||||
let mut cur_requirements = Vec::new();
|
||||
loop {
|
||||
match state {
|
||||
// numeric input
|
||||
NanoFactoryBuildState::InputCount | NanoFactoryBuildState::OutputCount => {
|
||||
while let Some(c) = next_if_in_range(&mut line, 48..=57) {
|
||||
count_var = count_var * 10 + (c as usize) - 48;
|
||||
}
|
||||
assert_eq!(line.next().unwrap(), b' ');
|
||||
state = match state {
|
||||
NanoFactoryBuildState::InputCount => {
|
||||
NanoFactoryBuildState::InputResource
|
||||
}
|
||||
NanoFactoryBuildState::OutputCount => {
|
||||
NanoFactoryBuildState::OutputResource
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
// input resouce name
|
||||
NanoFactoryBuildState::InputResource => {
|
||||
let mut r = Resource::new();
|
||||
while let Some(c) = next_if_in_range(&mut line, 65..=90) {
|
||||
r.push(c.into());
|
||||
}
|
||||
for _ in 0..count_var {
|
||||
cur_requirements.push(r.clone());
|
||||
}
|
||||
r.truncate(0);
|
||||
count_var = 0;
|
||||
let next = line.next();
|
||||
match next {
|
||||
Some(b',') => {
|
||||
assert_eq!(line.next().unwrap(), b' ');
|
||||
state = NanoFactoryBuildState::InputCount;
|
||||
}
|
||||
Some(b' ') => state = NanoFactoryBuildState::OutputArrow,
|
||||
_ => panic!(
|
||||
"invalid format in line {}, expected ' ' or ',', got '{:?}'",
|
||||
nr, next
|
||||
),
|
||||
}
|
||||
}
|
||||
// output resource name
|
||||
NanoFactoryBuildState::OutputResource => {
|
||||
let mut r = Resource::new();
|
||||
while let Some(c) = next_if_in_range(&mut line, 65..=90) {
|
||||
r.push(c.into());
|
||||
}
|
||||
f.pipeline
|
||||
.insert(r, ResourceBuilder::from((cur_requirements, count_var)));
|
||||
state = NanoFactoryBuildState::InputCount;
|
||||
return;
|
||||
}
|
||||
// arrow between input and output ' => '
|
||||
NanoFactoryBuildState::OutputArrow => {
|
||||
assert_eq!(line.next().unwrap(), b'=');
|
||||
assert_eq!(line.next().unwrap(), b'>');
|
||||
assert_eq!(line.next().unwrap(), b' ');
|
||||
state = NanoFactoryBuildState::OutputCount
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
f
|
||||
}
|
||||
}
|
||||
|
||||
fn next_if_in_range<I, T>(
|
||||
peekable: &mut std::iter::Peekable<I>,
|
||||
range: std::ops::RangeInclusive<T>,
|
||||
) -> Option<T>
|
||||
where
|
||||
I: std::iter::Iterator<Item = T>,
|
||||
T: Copy + std::cmp::PartialOrd,
|
||||
{
|
||||
if let Some(peeked) = peekable.peek() {
|
||||
if range.contains(peeked) {
|
||||
peekable.next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let f = File::open("input14").unwrap();
|
||||
let bufreader = BufReader::new(f);
|
||||
let mut factory = NanoFactory::from(bufreader);
|
||||
|
||||
// part1
|
||||
println!("ores: {}", factory.generate_fuel());
|
||||
|
||||
// part2 as binary search
|
||||
factory.resolve_order();
|
||||
let mut min = 1;
|
||||
let mut max = 1_000_000_000_000;
|
||||
let mut fuel;
|
||||
loop {
|
||||
fuel = (min + max) / 2;
|
||||
let ore = factory.resource_usage(fuel);
|
||||
match 1_000_000_000_000.cmp(&ore) {
|
||||
Ordering::Less => max = fuel - 1,
|
||||
Ordering::Greater => {
|
||||
min = fuel + 1;
|
||||
if min > max {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ordering::Equal => {
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
println!("ammount: {}", fuel);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn example1() {
|
||||
let bufreader = BufReader::new(
|
||||
"10 ORE => 10 A
|
||||
1 ORE => 1 B
|
||||
7 A, 1 B => 1 C
|
||||
7 A, 1 C => 1 D
|
||||
7 A, 1 D => 1 E
|
||||
7 A, 1 E => 1 FUEL
|
||||
"
|
||||
.trim()
|
||||
.as_bytes(),
|
||||
);
|
||||
let mut factory = NanoFactory::from(bufreader);
|
||||
factory.resolve_order();
|
||||
assert_eq!(factory.generate_fuel(), 31);
|
||||
assert_eq!(factory.resource_usage(1), 31);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn example2() {
|
||||
let bufreader = BufReader::new(
|
||||
"
|
||||
9 ORE => 2 A
|
||||
8 ORE => 3 B
|
||||
7 ORE => 5 C
|
||||
3 A, 4 B => 1 AB
|
||||
5 B, 7 C => 1 BC
|
||||
4 C, 1 A => 1 CA
|
||||
2 AB, 3 BC, 4 CA => 1 FUEL
|
||||
"
|
||||
.trim()
|
||||
.as_bytes(),
|
||||
);
|
||||
let mut factory = NanoFactory::from(bufreader);
|
||||
factory.resolve_order();
|
||||
assert_eq!(factory.generate_fuel(), 165);
|
||||
assert_eq!(factory.resource_usage(1), 165);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn example3() {
|
||||
let bufreader = BufReader::new(
|
||||
"
|
||||
157 ORE => 5 NZVS
|
||||
165 ORE => 6 DCFZ
|
||||
44 XJWVT, 5 KHKGT, 1 QDVJ, 29 NZVS, 9 GPVTF, 48 HKGWZ => 1 FUEL
|
||||
12 HKGWZ, 1 GPVTF, 8 PSHF => 9 QDVJ
|
||||
179 ORE => 7 PSHF
|
||||
177 ORE => 5 HKGWZ
|
||||
7 DCFZ, 7 PSHF => 2 XJWVT
|
||||
165 ORE => 2 GPVTF
|
||||
3 DCFZ, 7 NZVS, 5 HKGWZ, 10 PSHF => 8 KHKGT
|
||||
"
|
||||
.trim()
|
||||
.as_bytes(),
|
||||
);
|
||||
let factory = NanoFactory::from(bufreader);
|
||||
assert_eq!(factory.generate_fuel(), 13312);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn example4() {
|
||||
let bufreader = BufReader::new(
|
||||
"
|
||||
2 VPVL, 7 FWMGM, 2 CXFTF, 11 MNCFX => 1 STKFG
|
||||
17 NVRVD, 3 JNWZP => 8 VPVL
|
||||
53 STKFG, 6 MNCFX, 46 VJHF, 81 HVMC, 68 CXFTF, 25 GNMV => 1 FUEL
|
||||
22 VJHF, 37 MNCFX => 5 FWMGM
|
||||
139 ORE => 4 NVRVD
|
||||
144 ORE => 7 JNWZP
|
||||
5 MNCFX, 7 RFSQX, 2 FWMGM, 2 VPVL, 19 CXFTF => 3 HVMC
|
||||
5 VJHF, 7 MNCFX, 9 VPVL, 37 CXFTF => 6 GNMV
|
||||
145 ORE => 6 MNCFX
|
||||
1 NVRVD => 8 CXFTF
|
||||
1 VJHF, 6 MNCFX => 4 RFSQX
|
||||
176 ORE => 6 VJHF
|
||||
"
|
||||
.trim()
|
||||
.as_bytes(),
|
||||
);
|
||||
let mut factory = NanoFactory::from(bufreader);
|
||||
factory.resolve_order();
|
||||
assert_eq!(factory.generate_fuel(), 180697);
|
||||
assert_eq!(factory.resource_usage(1), 180697);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn example5() {
|
||||
let bufreader = BufReader::new(
|
||||
"
|
||||
171 ORE => 8 CNZTR
|
||||
7 ZLQW, 3 BMBT, 9 XCVML, 26 XMNCP, 1 WPTQ, 2 MZWV, 1 RJRHP => 4 PLWSL
|
||||
114 ORE => 4 BHXH
|
||||
14 VRPVC => 6 BMBT
|
||||
6 BHXH, 18 KTJDG, 12 WPTQ, 7 PLWSL, 31 FHTLT, 37 ZDVW => 1 FUEL
|
||||
6 WPTQ, 2 BMBT, 8 ZLQW, 18 KTJDG, 1 XMNCP, 6 MZWV, 1 RJRHP => 6 FHTLT
|
||||
15 XDBXC, 2 LTCX, 1 VRPVC => 6 ZLQW
|
||||
13 WPTQ, 10 LTCX, 3 RJRHP, 14 XMNCP, 2 MZWV, 1 ZLQW => 1 ZDVW
|
||||
5 BMBT => 4 WPTQ
|
||||
189 ORE => 9 KTJDG
|
||||
1 MZWV, 17 XDBXC, 3 XCVML => 2 XMNCP
|
||||
12 VRPVC, 27 CNZTR => 2 XDBXC
|
||||
15 KTJDG, 12 BHXH => 5 XCVML
|
||||
3 BHXH, 2 VRPVC => 7 MZWV
|
||||
121 ORE => 7 VRPVC
|
||||
7 XCVML => 6 RJRHP
|
||||
5 BHXH, 4 VRPVC => 5 LTCX
|
||||
"
|
||||
.trim()
|
||||
.as_bytes(),
|
||||
);
|
||||
let mut factory = NanoFactory::from(bufreader);
|
||||
factory.resolve_order();
|
||||
assert_eq!(factory.generate_fuel(), 2210736);
|
||||
assert_eq!(factory.resource_usage(1), 2210736);
|
||||
}
|
||||
}
|
|
@ -1,205 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
|
||||
use aoc2019::intcode;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let f = File::open("input15")?;
|
||||
let computer = intcode::Computer::from(f);
|
||||
let mut map = Map::new();
|
||||
let steps = map.walk(&XY { x: 0, y: 0 }, &computer, 0).unwrap();
|
||||
|
||||
for y in map.ymin..=map.ymax {
|
||||
for x in map.xmin..=map.xmax {
|
||||
if x == 0 && y == 0 {
|
||||
print!("0");
|
||||
continue;
|
||||
}
|
||||
let status = map.m.get(&XY { x, y }).unwrap_or(&Status::Empty);
|
||||
let c = match status {
|
||||
Status::HitWall => "#",
|
||||
Status::MoveComplete => ".",
|
||||
Status::Finished => "X",
|
||||
Status::Empty => " ",
|
||||
Status::Oxigenated => "o",
|
||||
};
|
||||
print!("{}", c);
|
||||
}
|
||||
println!("");
|
||||
}
|
||||
|
||||
println!("i walked {} steps", steps);
|
||||
let steps = map.fill();
|
||||
println!("filled with oxygen after {} minutes", steps);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct Map {
|
||||
m: HashMap<XY, Status>,
|
||||
xmin: isize,
|
||||
xmax: isize,
|
||||
ymin: isize,
|
||||
ymax: isize,
|
||||
}
|
||||
|
||||
impl Map {
|
||||
fn new() -> Self {
|
||||
Map::default()
|
||||
}
|
||||
|
||||
fn walk(&mut self, start: &XY, computer: &intcode::Computer, steps: usize) -> Option<usize> {
|
||||
let steps = steps + 1;
|
||||
let mut result = None;
|
||||
for dir in 1..=4 {
|
||||
let direction = Direction::from(dir);
|
||||
let xy = start.add((&direction).into());
|
||||
if self.m.contains_key(&xy) {
|
||||
continue;
|
||||
}
|
||||
self.update_limits(&xy);
|
||||
let mut computer = computer.clone();
|
||||
if let Some(status) = computer.run_io(dir) {
|
||||
let status = Status::from(status);
|
||||
self.m.insert(xy.clone(), status.clone());
|
||||
match status {
|
||||
Status::HitWall => (),
|
||||
Status::MoveComplete => match self.walk(&xy, &computer, steps) {
|
||||
Some(i) => result = Some(i + 1),
|
||||
None => (),
|
||||
},
|
||||
Status::Finished => result = Some(1),
|
||||
_ => unreachable!("unknown status"),
|
||||
}
|
||||
} else {
|
||||
unreachable!("computer unexpectedly halted");
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn fill(&mut self) -> usize {
|
||||
let mut src: Vec<XY> = self
|
||||
.m
|
||||
.iter()
|
||||
.filter_map(|(xy, status)| match status {
|
||||
Status::Finished => Some(xy),
|
||||
_ => None,
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
let mut steps = 0;
|
||||
while src.len() > 0 {
|
||||
steps += 1;
|
||||
src.iter()
|
||||
.for_each(|xy| *self.m.get_mut(xy).unwrap() = Status::Oxigenated);
|
||||
src = src
|
||||
.into_iter()
|
||||
.map(|xy| {
|
||||
(1..=4)
|
||||
.map(Direction::from)
|
||||
.map(move |d| xy.add((&d).into()))
|
||||
})
|
||||
.flatten()
|
||||
.filter(|xy| match self.m.get(xy) {
|
||||
Some(Status::MoveComplete) => true,
|
||||
_ => false,
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
assert_eq!(
|
||||
self.m
|
||||
.values()
|
||||
.filter(|status| match status {
|
||||
Status::MoveComplete => true,
|
||||
_ => false,
|
||||
})
|
||||
.count(),
|
||||
0
|
||||
);
|
||||
steps - 1
|
||||
}
|
||||
|
||||
fn update_limits(&mut self, xy: &XY) {
|
||||
if self.xmin > xy.x {
|
||||
self.xmin = xy.x
|
||||
}
|
||||
if self.xmax < xy.x {
|
||||
self.xmax = xy.x
|
||||
}
|
||||
if self.ymin > xy.y {
|
||||
self.ymin = xy.y
|
||||
}
|
||||
if self.ymax < xy.y {
|
||||
self.ymax = xy.y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Direction {
|
||||
North = 1,
|
||||
South = 2,
|
||||
West = 3,
|
||||
East = 4,
|
||||
}
|
||||
|
||||
impl From<isize> for Direction {
|
||||
fn from(i: isize) -> Self {
|
||||
match i {
|
||||
1 => Direction::North,
|
||||
2 => Direction::South,
|
||||
3 => Direction::West,
|
||||
4 => Direction::East,
|
||||
_ => unreachable!("no status for computer output"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<(isize, isize)> for &Direction {
|
||||
fn into(self) -> (isize, isize) {
|
||||
match self {
|
||||
Direction::North => (0, 1),
|
||||
Direction::South => (0, -1),
|
||||
Direction::West => (-1, 0),
|
||||
Direction::East => (1, 0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum Status {
|
||||
HitWall = 0,
|
||||
MoveComplete = 1,
|
||||
Finished = 2,
|
||||
Empty = 3,
|
||||
Oxigenated = 4,
|
||||
}
|
||||
|
||||
impl From<isize> for Status {
|
||||
fn from(i: isize) -> Self {
|
||||
match i {
|
||||
0 => Status::HitWall,
|
||||
1 => Status::MoveComplete,
|
||||
2 => Status::Finished,
|
||||
_ => unreachable!("no status for computer output"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
struct XY {
|
||||
x: isize,
|
||||
y: isize,
|
||||
}
|
||||
|
||||
impl XY {
|
||||
fn add(&self, delta: (isize, isize)) -> XY {
|
||||
XY {
|
||||
x: self.x + delta.0,
|
||||
y: self.y + delta.1,
|
||||
}
|
||||
}
|
||||
}
|
477
src/intcode.rs
477
src/intcode.rs
|
@ -1,477 +0,0 @@
|
|||
use std::collections::VecDeque;
|
||||
use std::fs::File;
|
||||
use std::io::{BufRead, BufReader};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Var {
|
||||
A = 1,
|
||||
B,
|
||||
C,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum Mode {
|
||||
Position,
|
||||
Immediate,
|
||||
Relative,
|
||||
}
|
||||
|
||||
impl Default for Mode {
|
||||
fn default() -> Self {
|
||||
Mode::Position
|
||||
}
|
||||
}
|
||||
|
||||
impl From<isize> for Mode {
|
||||
fn from(b: isize) -> Self {
|
||||
match b {
|
||||
0 => Mode::Position,
|
||||
1 => Mode::Immediate,
|
||||
2 => Mode::Relative,
|
||||
_ => panic!("invalid mode {}", b),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
struct Modes {
|
||||
a: Mode,
|
||||
b: Mode,
|
||||
c: Mode,
|
||||
}
|
||||
|
||||
impl Modes {
|
||||
fn mode_for(&self, v: &Var) -> Mode {
|
||||
match v {
|
||||
Var::A => self.a,
|
||||
Var::B => self.b,
|
||||
Var::C => self.c,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(Mode, Mode, Mode)> for Modes {
|
||||
fn from(modes: (Mode, Mode, Mode)) -> Self {
|
||||
Modes {
|
||||
a: modes.0,
|
||||
b: modes.1,
|
||||
c: modes.2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ModedOpcode> for Modes {
|
||||
fn from(mo: &ModedOpcode) -> Self {
|
||||
mo.modes
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Opcode {
|
||||
Add,
|
||||
Multiply,
|
||||
Input,
|
||||
Output,
|
||||
JumpIfTrue,
|
||||
JumpIfFalse,
|
||||
LessThan,
|
||||
Equals,
|
||||
RelativeAdj,
|
||||
Halt,
|
||||
}
|
||||
|
||||
impl From<&ModedOpcode> for Opcode {
|
||||
fn from(mo: &ModedOpcode) -> Self {
|
||||
mo.op
|
||||
}
|
||||
}
|
||||
|
||||
impl From<isize> for Opcode {
|
||||
fn from(i: isize) -> Self {
|
||||
match i {
|
||||
1 => Opcode::Add,
|
||||
2 => Opcode::Multiply,
|
||||
3 => Opcode::Input,
|
||||
4 => Opcode::Output,
|
||||
5 => Opcode::JumpIfTrue,
|
||||
6 => Opcode::JumpIfFalse,
|
||||
7 => Opcode::LessThan,
|
||||
8 => Opcode::Equals,
|
||||
9 => Opcode::RelativeAdj,
|
||||
99 => Opcode::Halt,
|
||||
_ => panic!("invalid opcode {}", i),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ModedOpcode {
|
||||
modes: Modes,
|
||||
op: Opcode,
|
||||
}
|
||||
|
||||
impl From<isize> for ModedOpcode {
|
||||
fn from(i: isize) -> Self {
|
||||
let code = i % 100;
|
||||
let modes = i / 100;
|
||||
let a = (modes % 10) / 1;
|
||||
let b = (modes % 100) / 10;
|
||||
let c = (modes % 1000) / 100;
|
||||
let modes = (Mode::from(a), Mode::from(b), Mode::from(c));
|
||||
ModedOpcode {
|
||||
modes: Modes::from(modes),
|
||||
op: Opcode::from(code),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Computer {
|
||||
pos: usize,
|
||||
rel: isize,
|
||||
program: Vec<isize>,
|
||||
modes: Modes,
|
||||
inputs: VecDeque<isize>,
|
||||
outputs: VecDeque<isize>,
|
||||
pub halted: bool,
|
||||
}
|
||||
|
||||
impl Clone for Computer {
|
||||
fn clone(&self) -> Self {
|
||||
Computer::from(self.program.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<File> for Computer {
|
||||
fn from(f: File) -> Self {
|
||||
let f = BufReader::new(f);
|
||||
let program: Vec<isize> = f
|
||||
.split(b',')
|
||||
.map(|o| {
|
||||
let o = o.unwrap();
|
||||
let mut o = o.iter().peekable();
|
||||
let sign = if o.peek() == Some(&&b'-') { -1 } else { 1 };
|
||||
o.filter(|i| **i >= b'0' && **i <= b'9')
|
||||
.fold(0, |s, i| ((i - b'0') as isize + 10 * s))
|
||||
* sign
|
||||
})
|
||||
.collect();
|
||||
Computer::from(program)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<isize>> for Computer {
|
||||
fn from(program: Vec<isize>) -> Self {
|
||||
Computer {
|
||||
pos: 0,
|
||||
rel: 0,
|
||||
program,
|
||||
modes: Default::default(),
|
||||
inputs: VecDeque::new(),
|
||||
outputs: VecDeque::new(),
|
||||
halted: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Computer {
|
||||
pub fn get_outputs(&self) -> &VecDeque<isize> {
|
||||
&self.outputs
|
||||
}
|
||||
|
||||
pub fn pop_output(&mut self) -> Option<isize> {
|
||||
self.outputs.pop_front()
|
||||
}
|
||||
|
||||
pub fn push_input_and_step(&mut self, input: isize) {
|
||||
self.inputs.push_back(input);
|
||||
self.step()
|
||||
}
|
||||
|
||||
pub fn clone_with_modified_program(&self, u: Vec<(usize, isize)>) -> Computer {
|
||||
let mut computer = self.clone();
|
||||
for (pos, val) in u {
|
||||
computer.program[pos] = val;
|
||||
}
|
||||
computer
|
||||
}
|
||||
|
||||
pub fn clone_with_input(&self, input: Vec<isize>) -> Computer {
|
||||
let mut computer = self.clone();
|
||||
computer.inputs = VecDeque::from(input);
|
||||
computer
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
while !self.halted {
|
||||
self.step();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_until_input(&mut self) -> Option<Vec<isize>> {
|
||||
self.step_until_input();
|
||||
if self.outputs.len() > 0 {
|
||||
return Some(self.outputs.split_off(0).into_iter().collect());
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
pub fn run_until_n_output(&mut self, n: usize) -> Option<Vec<isize>> {
|
||||
loop {
|
||||
self.step();
|
||||
if self.outputs.len() >= n {
|
||||
return Some(self.outputs.split_off(0).into_iter().collect());
|
||||
}
|
||||
if self.halted {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_until_output(&mut self) -> Option<isize> {
|
||||
loop {
|
||||
self.step();
|
||||
if self.outputs.len() > 0 {
|
||||
return self.outputs.pop_front();
|
||||
}
|
||||
if self.halted {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_must_output(&mut self) -> isize {
|
||||
self.run_until_output()
|
||||
.expect("computer halted without output")
|
||||
}
|
||||
|
||||
pub fn run_io(&mut self, input: isize) -> Option<isize> {
|
||||
self.inputs.push_back(input);
|
||||
self.run_until_output()
|
||||
}
|
||||
|
||||
fn step_until_input(&mut self) {
|
||||
while !self.halted {
|
||||
let instruction = self.program[self.pos];
|
||||
let instruction = ModedOpcode::from(instruction);
|
||||
self.modes = Modes::from(&instruction);
|
||||
let advance = match Opcode::from(&instruction) {
|
||||
Opcode::Add => self.add(),
|
||||
Opcode::Multiply => self.multiply(),
|
||||
Opcode::Input => return,
|
||||
Opcode::Output => self.output(),
|
||||
Opcode::JumpIfTrue => self.jump_if_true(),
|
||||
Opcode::JumpIfFalse => self.jump_if_false(),
|
||||
Opcode::LessThan => self.less_than(),
|
||||
Opcode::Equals => self.equals(),
|
||||
Opcode::RelativeAdj => self.relative_adj(),
|
||||
Opcode::Halt => self.halt(),
|
||||
};
|
||||
self.pos += advance;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn step(&mut self) {
|
||||
let instruction = self.program[self.pos];
|
||||
let instruction = ModedOpcode::from(instruction);
|
||||
self.modes = Modes::from(&instruction);
|
||||
let advance = match Opcode::from(&instruction) {
|
||||
Opcode::Add => self.add(),
|
||||
Opcode::Multiply => self.multiply(),
|
||||
Opcode::Input => self.input(),
|
||||
Opcode::Output => self.output(),
|
||||
Opcode::JumpIfTrue => self.jump_if_true(),
|
||||
Opcode::JumpIfFalse => self.jump_if_false(),
|
||||
Opcode::LessThan => self.less_than(),
|
||||
Opcode::Equals => self.equals(),
|
||||
Opcode::RelativeAdj => self.relative_adj(),
|
||||
Opcode::Halt => self.halt(),
|
||||
};
|
||||
self.pos += advance;
|
||||
}
|
||||
|
||||
fn get_pos(&self, pos: usize) -> isize {
|
||||
*self.program.get(pos).unwrap_or(&0)
|
||||
}
|
||||
|
||||
fn get(&self, v: &Var) -> isize {
|
||||
let want = self.pos + *v as usize;
|
||||
let want = match self.modes.mode_for(v) {
|
||||
Mode::Position => self.get_pos(want as usize) as usize,
|
||||
Mode::Immediate => want,
|
||||
Mode::Relative => (self.rel + self.get_pos(want)) as usize,
|
||||
};
|
||||
self.get_pos(want)
|
||||
}
|
||||
|
||||
fn get_a(&self) -> isize {
|
||||
self.get(&Var::A)
|
||||
}
|
||||
|
||||
fn get_b(&self) -> isize {
|
||||
self.get(&Var::B)
|
||||
}
|
||||
|
||||
fn set(&mut self, v: &Var, value: isize) {
|
||||
let length = self.program.len();
|
||||
let want = self.pos + *v as usize;
|
||||
let want = match self.modes.mode_for(v) {
|
||||
Mode::Position => self.get_pos(want) as usize,
|
||||
Mode::Immediate => want,
|
||||
Mode::Relative => (self.rel + self.get_pos(want)) as usize,
|
||||
};
|
||||
if length <= want {
|
||||
self.program.resize(want + 1, 0);
|
||||
}
|
||||
self.program[want] = value;
|
||||
}
|
||||
|
||||
fn set_a(&mut self, value: isize) {
|
||||
self.set(&Var::A, value)
|
||||
}
|
||||
|
||||
fn set_c(&mut self, value: isize) {
|
||||
self.set(&Var::C, value)
|
||||
}
|
||||
|
||||
fn add(&mut self) -> usize {
|
||||
self.set_c(self.get_a() + self.get_b());
|
||||
4
|
||||
}
|
||||
|
||||
fn multiply(&mut self) -> usize {
|
||||
self.set_c(self.get_a() * self.get_b());
|
||||
4
|
||||
}
|
||||
|
||||
fn input(&mut self) -> usize {
|
||||
let value = self.inputs.pop_front().expect("no input provided");
|
||||
self.set_a(value);
|
||||
2
|
||||
}
|
||||
|
||||
fn output(&mut self) -> usize {
|
||||
self.outputs.push_back(self.get_a());
|
||||
2
|
||||
}
|
||||
|
||||
fn jump_if_true(&mut self) -> usize {
|
||||
if self.get_a() > 0 {
|
||||
self.pos = self.get_b() as usize;
|
||||
return 0;
|
||||
}
|
||||
3
|
||||
}
|
||||
|
||||
fn jump_if_false(&mut self) -> usize {
|
||||
if self.get_a() == 0 {
|
||||
self.pos = self.get_b() as usize;
|
||||
return 0;
|
||||
}
|
||||
3
|
||||
}
|
||||
|
||||
fn less_than(&mut self) -> usize {
|
||||
if self.get_a() < self.get_b() {
|
||||
self.set_c(1)
|
||||
} else {
|
||||
self.set_c(0)
|
||||
}
|
||||
4
|
||||
}
|
||||
|
||||
fn equals(&mut self) -> usize {
|
||||
if self.get_a() == self.get_b() {
|
||||
self.set_c(1)
|
||||
} else {
|
||||
self.set_c(0)
|
||||
}
|
||||
4
|
||||
}
|
||||
|
||||
fn relative_adj(&mut self) -> usize {
|
||||
self.rel = self.rel + self.get_a();
|
||||
2
|
||||
}
|
||||
|
||||
fn halt(&mut self) -> usize {
|
||||
self.halted = true;
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn example1() {
|
||||
let program = vec![
|
||||
109, 1, 204, -1, 1001, 100, 1, 100, 1008, 100, 16, 101, 1006, 101, 0, 99,
|
||||
];
|
||||
let mut computer = Computer::from(program.clone());
|
||||
computer.run();
|
||||
assert_eq!(computer.get_outputs().iter().eq(program.iter()), true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn example2() {
|
||||
assert_eq!(
|
||||
Computer::from(vec![1102, 34915192, 34915192, 7, 4, 7, 99, 0]).run_must_output(),
|
||||
1219070632396864
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn example3() {
|
||||
assert_eq!(
|
||||
Computer::from(vec![104, 1125899906842624, 99]).run_must_output(),
|
||||
1125899906842624
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn day5exampe1() {
|
||||
let computer = Computer::from(vec![3, 9, 8, 9, 10, 9, 4, 9, 99, -1, 8]);
|
||||
assert_eq!(computer.clone_with_input(vec![1]).run_must_output(), 0);
|
||||
assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn day5exampe2() {
|
||||
let computer = Computer::from(vec![3, 3, 1108, -1, 8, 3, 4, 3, 99]);
|
||||
assert_eq!(computer.clone_with_input(vec![1]).run_must_output(), 0);
|
||||
assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn day5exampe3() {
|
||||
let computer = Computer::from(vec![
|
||||
3, 12, 6, 12, 15, 1, 13, 14, 13, 4, 13, 99, -1, 0, 1, 9,
|
||||
]);
|
||||
assert_eq!(computer.clone_with_input(vec![0]).run_must_output(), 0);
|
||||
assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1);
|
||||
let computer = Computer::from(vec![3, 3, 1105, -1, 9, 1101, 0, 0, 12, 4, 12, 99, 1]);
|
||||
assert_eq!(computer.clone_with_input(vec![0]).run_must_output(), 0);
|
||||
assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn day5exampe4() {
|
||||
let computer = Computer::from(vec![
|
||||
3, 21, 1008, 21, 8, 20, 1005, 20, 22, 107, 8, 21, 20, 1006, 20, 31, 1106, 0, 36, 98, 0,
|
||||
0, 1002, 21, 125, 20, 4, 20, 1105, 1, 46, 104, 999, 1105, 1, 46, 1101, 1000, 1, 20, 4,
|
||||
20, 1105, 1, 46, 98, 99,
|
||||
]);
|
||||
assert_eq!(computer.clone_with_input(vec![0]).run_must_output(), 999);
|
||||
assert_eq!(computer.clone_with_input(vec![8]).run_must_output(), 1000);
|
||||
assert_eq!(computer.clone_with_input(vec![9]).run_must_output(), 1001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn day9part1() {
|
||||
let computer = Computer::from(File::open("input9").unwrap());
|
||||
assert_eq!(
|
||||
computer.clone_with_input(vec![1]).run_must_output(),
|
||||
3063082071
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
pub mod intcode;
|
3
src/main.rs
Normal file
3
src/main.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
fn main() {
|
||||
println!("Hello, world!");
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue