Skip to content

Commit 48628c0

Browse files
committed
intcode-rs computer v0.1
1 parent 38b29c3 commit 48628c0

File tree

9 files changed

+309
-4
lines changed

9 files changed

+309
-4
lines changed

2019/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ members = [
66
"day2",
77
"day3",
88
"day4",
9+
"day5",
910
"day6",
1011
"day8",
1112
"day10",
@@ -16,6 +17,7 @@ members = [
1617
"day20",
1718
"day22",
1819
"day24",
20+
"intcode-rs",
1921
]
2022

2123
resolver = "2"

2019/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
![AoC2019](https://img.shields.io/badge/Advent_of_Code-2019-8A2BE2)
44
![Stars: 50](https://img.shields.io/badge/Stars-50⭐-blue)
5-
![Rust: 14](https://img.shields.io/badge/Rust-14-cyan?logo=Rust)
5+
![Rust: 15](https://img.shields.io/badge/Rust-15-cyan?logo=Rust)
66
![Python: 23](https://img.shields.io/badge/Python-23-cyan?logo=Python)
77

88
## 2019 ([Calendar](https://adventofcode.com/2019)) ([Solutions](../2019/)) : 50⭐
@@ -13,7 +13,7 @@ Puzzle
1313
[Day 2: 1202 Program Alarm](https://adventofcode.com/2019/day/2) | ⭐⭐ | [![Rust](../scripts/assets/rust.png)](../2019/day2/day2.rs) [![Python](../scripts/assets/python.png)](../2019/day2/day2.py)
1414
[Day 3: Crossed Wires](https://adventofcode.com/2019/day/3) | ⭐⭐ | [![Rust](../scripts/assets/rust.png)](../2019/day3/day3.rs) [![Python](../scripts/assets/python.png)](../2019/day3/day3.py)
1515
[Day 4: Secure Container](https://adventofcode.com/2019/day/4) | ⭐⭐ | [![Rust](../scripts/assets/rust.png)](../2019/day4/day4.rs) [![Python](../scripts/assets/python.png)](../2019/day4/day4.py)
16-
[Day 5: Sunny with a Chance of Asteroids](https://adventofcode.com/2019/day/5) | ⭐⭐ | [![Python](../scripts/assets/python.png)](../2019/day5/day5.py)
16+
[Day 5: Sunny with a Chance of Asteroids](https://adventofcode.com/2019/day/5) | ⭐⭐ | [![Rust](../scripts/assets/rust.png)](../2019/day5/day5.rs) [![Python](../scripts/assets/python.png)](../2019/day5/day5.py)
1717
[Day 6: Universal Orbit Map](https://adventofcode.com/2019/day/6) | ⭐⭐ | [![Rust](../scripts/assets/rust.png)](../2019/day6/day6.rs) [![Python](../scripts/assets/python.png)](../2019/day6/day6.py)
1818
[Day 7: Amplification Circuit](https://adventofcode.com/2019/day/7) | ⭐⭐ | [![Python](../scripts/assets/python.png)](../2019/day7/day7.py)
1919
[Day 8: Space Image Format](https://adventofcode.com/2019/day/8) | ⭐⭐ | [![Rust](../scripts/assets/rust.png)](../2019/day8/day8.rs) [![Python](../scripts/assets/python.png)](../2019/day8/day8.py)

2019/day5/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "day5"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
aoc = { path = "../../aoc" }
8+
intcode = { path = "../intcode-rs" }
9+
10+
[[bin]]
11+
name = "day5"
12+
path = "day5.rs"

2019/day5/day5.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//! [Day 5: Sunny with a Chance of Asteroids](https://adventofcode.com/2019/day/5)
2+
3+
use intcode::{Computer, State};
4+
5+
fn main() {
6+
let args = aoc::parse_args();
7+
8+
let mut program = Computer::parse(&args.input);
9+
10+
let mut run = |init| {
11+
program.reset();
12+
program.push(init);
13+
let mut result = 0;
14+
while let State::Output(value) = program.run() {
15+
result = value;
16+
}
17+
result
18+
};
19+
20+
println!("{}", run(1)); // part 1
21+
println!("{}", run(5)); // part 2
22+
}

2019/intcode-rs/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[package]
2+
name = "intcode"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
aoc = { path = "../../aoc" }
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,
2+
1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,
3+
999,1105,1,46,1101,1000,1,20,4,20,1105,1,46,98,99

2019/intcode-rs/src/lib.rs

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
use std::collections::VecDeque;
2+
3+
// addressint mode
4+
const POSITION_MODE: i64 = 0; // https://adventofcode.com/2019/day/5
5+
const IMMEDIATE_MODE: i64 = 1; // https://adventofcode.com/2019/day/5
6+
const RELATIVE_MODE: i64 = 2; // https://adventofcode.com/2019/day/9
7+
8+
#[derive(Copy, Clone)]
9+
struct Address(i64);
10+
11+
#[derive(Debug, PartialEq, Eq)]
12+
pub enum State {
13+
Halted,
14+
Input,
15+
Output(i64),
16+
}
17+
18+
pub struct Computer {
19+
program: Vec<i64>,
20+
mem: Vec<i64>,
21+
ip: i64,
22+
relbase: i64,
23+
input: VecDeque<i64>,
24+
}
25+
26+
impl Computer {
27+
#[must_use]
28+
pub const fn new() -> Self {
29+
Self {
30+
program: Vec::new(),
31+
mem: Vec::new(),
32+
ip: 0,
33+
relbase: 0,
34+
input: VecDeque::new(),
35+
}
36+
}
37+
}
38+
39+
impl Default for Computer {
40+
fn default() -> Self {
41+
Self::new()
42+
}
43+
}
44+
45+
impl Computer {
46+
#[must_use]
47+
pub fn parse(data: &str) -> Self {
48+
let mut computer = Self::new();
49+
50+
for line in data.lines() {
51+
let mut line = line.trim_ascii();
52+
53+
// remove "[nnn] " at the beginning
54+
if line.starts_with('[') {
55+
if let Some(p) = line.find(']') {
56+
//
57+
line = line[(p + 1)..].trim_ascii_start();
58+
} else {
59+
continue;
60+
}
61+
}
62+
63+
for num in line.split(',') {
64+
if let Ok(num) = num.trim().parse::<i64>() {
65+
computer.program.push(num);
66+
} else {
67+
break;
68+
}
69+
}
70+
}
71+
72+
computer.mem = computer.program.clone();
73+
74+
computer
75+
}
76+
}
77+
78+
impl std::fmt::Display for Computer {
79+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80+
for num in &self.program {
81+
write!(f, "{num},")?;
82+
}
83+
Ok(())
84+
}
85+
}
86+
87+
impl Computer {
88+
pub fn reset(&mut self) {
89+
self.mem = self.program.clone();
90+
self.ip = 0;
91+
self.relbase = 0;
92+
self.input.clear();
93+
}
94+
95+
pub fn push(&mut self, value: i64) {
96+
self.input.push_back(value);
97+
}
98+
99+
/// # Panics
100+
pub fn run(&mut self) -> State {
101+
loop {
102+
let opcode = self.peek(Address(self.ip));
103+
104+
match opcode % 100 {
105+
0 => {
106+
// nop (extension)
107+
self.ip += 1;
108+
}
109+
99 => break State::Halted,
110+
111+
1 => {
112+
// addition
113+
let a = self.address((opcode / 100) % 10, 1);
114+
let b = self.address((opcode / 1000) % 10, 2);
115+
let c = self.address((opcode / 10000) % 10, 3);
116+
self.poke(c, self.peek(a) + self.peek(b));
117+
self.ip += 4;
118+
}
119+
120+
2 => {
121+
// multiplication
122+
let a = self.address((opcode / 100) % 10, 1);
123+
let b = self.address((opcode / 1000) % 10, 2);
124+
let c = self.address((opcode / 10000) % 10, 3);
125+
self.poke(c, self.peek(a) * self.peek(b));
126+
self.ip += 4;
127+
}
128+
129+
3 => {
130+
// input
131+
if let Some(value) = self.input.pop_front() {
132+
// println!("got input {value}");
133+
134+
let a = self.address((opcode / 100) % 10, 1);
135+
self.poke(a, value);
136+
self.ip += 2;
137+
} else {
138+
// println!("waiting for input");
139+
break State::Input;
140+
}
141+
}
142+
143+
4 => {
144+
// output
145+
let a = self.address((opcode / 100) % 10, 1);
146+
let value = self.peek(a);
147+
self.ip += 2;
148+
149+
// println!("output {value}");
150+
break State::Output(value);
151+
}
152+
153+
5 => {
154+
// jump-if-true
155+
156+
let a = self.address((opcode / 100) % 10, 1);
157+
158+
self.ip = if self.peek(a) == 0 {
159+
self.ip + 3
160+
} else {
161+
let b = self.address((opcode / 1000) % 10, 2);
162+
self.peek(b)
163+
}
164+
}
165+
166+
6 => {
167+
// jump-if-false
168+
169+
let a = self.address((opcode / 100) % 10, 1);
170+
171+
self.ip = if self.peek(a) != 0 {
172+
self.ip + 3
173+
} else {
174+
let b = self.address((opcode / 1000) % 10, 2);
175+
self.peek(b)
176+
}
177+
}
178+
179+
7 => {
180+
let a = self.address((opcode / 100) % 10, 1);
181+
let b = self.address((opcode / 1000) % 10, 2);
182+
let c = self.address((opcode / 10000) % 10, 3);
183+
184+
self.poke(c, i64::from(self.peek(a) < self.peek(b)));
185+
186+
self.ip += 4;
187+
}
188+
189+
8 => {
190+
let a = self.address((opcode / 100) % 10, 1);
191+
let b = self.address((opcode / 1000) % 10, 2);
192+
let c = self.address((opcode / 10000) % 10, 3);
193+
194+
self.poke(c, i64::from(self.peek(a) == self.peek(b)));
195+
196+
self.ip += 4;
197+
}
198+
199+
_ => panic!("opcode {opcode} not implemented"),
200+
}
201+
}
202+
}
203+
204+
fn peek(&self, address: Address) -> i64 {
205+
usize::try_from(address.0).map_or_else(
206+
|_| {
207+
panic!("segmentation fault at {}", address.0);
208+
},
209+
|a| self.mem[a],
210+
)
211+
}
212+
213+
fn poke(&mut self, address: Address, num: i64) {
214+
if let Ok(a) = usize::try_from(address.0) {
215+
self.mem[a] = num;
216+
} else {
217+
panic!("segmentation fault at {}", address.0);
218+
}
219+
}
220+
221+
fn address(&self, mode: i64, offset: i64) -> Address {
222+
match mode {
223+
POSITION_MODE => Address(self.peek(Address(self.ip + offset))),
224+
IMMEDIATE_MODE => Address(self.ip + offset),
225+
RELATIVE_MODE => Address(self.relbase + self.peek(Address(self.ip + offset))),
226+
_ => panic!("invalid mode {mode}"),
227+
}
228+
}
229+
}
230+
231+
/// Test from puzzle input
232+
#[cfg(test)]
233+
mod test {
234+
use super::*;
235+
236+
/// Tests from day5
237+
#[test]
238+
fn test_intcode() {
239+
let mut program = Computer::parse(include_str!("day5_compare.intcode"));
240+
241+
program.push(7);
242+
assert_eq!(program.run(), State::Output(999));
243+
244+
program.reset();
245+
program.push(8);
246+
assert_eq!(program.run(), State::Output(1000));
247+
248+
program.reset();
249+
program.push(9);
250+
assert_eq!(program.run(), State::Output(1001));
251+
}
252+
}

2019/intcode-rs/src/main.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
fn main() {
2+
let args = aoc::parse_args();
3+
4+
let program = intcode::Computer::parse(&args.input);
5+
6+
println!("{program}");
7+
}

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# [Advent of Code](https://adventofcode.com) in Rust 🦀
22

33
![Stars: 500](https://img.shields.io/badge/Stars-500⭐-blue)
4-
![Rust: 239](https://img.shields.io/badge/Rust-239-cyan?logo=Rust)
4+
![Rust: 240](https://img.shields.io/badge/Rust-240-cyan?logo=Rust)
55
![Python: 123](https://img.shields.io/badge/Python-123-cyan?logo=Python)
66

77
<img src="./scripts/assets/christmas_ferris_2015_2024.png" alt="Christmas Ferris" width="164" />
@@ -49,7 +49,7 @@ Calendar | Solutions | Stars | Rust | Python | 🎁
4949
[Advent of Code 2022](https://adventofcode.com/2022) | [Solutions](2022/README.md) | 50⭐ | 25 | 18 | 1
5050
[Advent of Code 2021](https://adventofcode.com/2021) | [Solutions](2021/README.md) | 50⭐ | 25 | 12 |
5151
[Advent of Code 2020](https://adventofcode.com/2020) | [Solutions](2020/README.md) | 50⭐ | 25 | 23 |
52-
[Advent of Code 2019](https://adventofcode.com/2019) | [Solutions](2019/README.md) | 50⭐ | 14 | 23 | 2
52+
[Advent of Code 2019](https://adventofcode.com/2019) | [Solutions](2019/README.md) | 50⭐ | 15 | 23 | 2
5353
[Advent of Code 2018](https://adventofcode.com/2018) | [Solutions](2018/README.md) | 50⭐ | 25 | 4 | 1
5454
[Advent of Code 2017](https://adventofcode.com/2017) | [Solutions](2017/README.md) | 50⭐ | 25 | 17 |
5555
[Advent of Code 2016](https://adventofcode.com/2016) | [Solutions](2016/README.md) | 50⭐ | 25 | 0 |

0 commit comments

Comments
 (0)