1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
|
/// --- Part Two ---
///
/// It seems like the X register controls the horizontal position of a sprite. Specifically, the
/// sprite is 3 pixels wide, and the X register sets the horizontal position of the middle of that
/// sprite. (In this system, there is no such thing as "vertical position": if the sprite's
/// horizontal position puts its pixels where the CRT is currently drawing, then those pixels will
/// be drawn.)
///
/// You count the pixels on the CRT: 40 wide and 6 high. This CRT screen draws the top row of
/// pixels left-to-right, then the row below that, and so on. The left-most pixel in each row is
/// in position 0, and the right-most pixel in each row is in position 39.
///
/// Like the CPU, the CRT is tied closely to the clock circuit: the CRT draws a single pixel during
/// each cycle. Representing each pixel of the screen as a #, here are the cycles during which the
/// first and last pixel in each row are drawn:
///
/// ```
/// Cycle 1 -> ######################################## <- Cycle 40
/// Cycle 41 -> ######################################## <- Cycle 80
/// Cycle 81 -> ######################################## <- Cycle 120
/// Cycle 121 -> ######################################## <- Cycle 160
/// Cycle 161 -> ######################################## <- Cycle 200
/// Cycle 201 -> ######################################## <- Cycle 240
/// ```
///
/// So, by carefully timing the CPU instructions and the CRT drawing operations, you should be able
/// to determine whether the sprite is visible the instant each pixel is drawn. If the sprite is
/// positioned such that one of its three pixels is the pixel currently being drawn, the screen
/// produces a lit pixel (#); otherwise, the screen leaves the pixel dark (.).
///
/// The first few pixels from the larger example above are drawn as follows:
///
/// ```
/// Sprite position: ###.....................................
///
/// Start cycle 1: begin executing addx 15
/// During cycle 1: CRT draws pixel in position 0
/// Current CRT row: #
///
/// During cycle 2: CRT draws pixel in position 1
/// Current CRT row: ##
/// End of cycle 2: finish executing addx 15 (Register X is now 16)
/// Sprite position: ...............###......................
///
/// Start cycle 3: begin executing addx -11
/// During cycle 3: CRT draws pixel in position 2
/// Current CRT row: ##.
///
/// During cycle 4: CRT draws pixel in position 3
/// Current CRT row: ##..
/// End of cycle 4: finish executing addx -11 (Register X is now 5)
/// Sprite position: ....###.................................
///
/// Start cycle 5: begin executing addx 6
/// During cycle 5: CRT draws pixel in position 4
/// Current CRT row: ##..#
///
/// During cycle 6: CRT draws pixel in position 5
/// Current CRT row: ##..##
/// End of cycle 6: finish executing addx 6 (Register X is now 11)
/// Sprite position: ..........###...........................
///
/// Start cycle 7: begin executing addx -3
/// During cycle 7: CRT draws pixel in position 6
/// Current CRT row: ##..##.
///
/// During cycle 8: CRT draws pixel in position 7
/// Current CRT row: ##..##..
/// End of cycle 8: finish executing addx -3 (Register X is now 8)
/// Sprite position: .......###..............................
///
/// Start cycle 9: begin executing addx 5
/// During cycle 9: CRT draws pixel in position 8
/// Current CRT row: ##..##..#
///
/// During cycle 10: CRT draws pixel in position 9
/// Current CRT row: ##..##..##
/// End of cycle 10: finish executing addx 5 (Register X is now 13)
/// Sprite position: ............###.........................
///
/// Start cycle 11: begin executing addx -1
/// During cycle 11: CRT draws pixel in position 10
/// Current CRT row: ##..##..##.
///
/// During cycle 12: CRT draws pixel in position 11
/// Current CRT row: ##..##..##..
/// End of cycle 12: finish executing addx -1 (Register X is now 12)
/// Sprite position: ...........###..........................
///
/// Start cycle 13: begin executing addx -8
/// During cycle 13: CRT draws pixel in position 12
/// Current CRT row: ##..##..##..#
///
/// During cycle 14: CRT draws pixel in position 13
/// Current CRT row: ##..##..##..##
/// End of cycle 14: finish executing addx -8 (Register X is now 4)
/// Sprite position: ...###..................................
///
/// Start cycle 15: begin executing addx 13
/// During cycle 15: CRT draws pixel in position 14
/// Current CRT row: ##..##..##..##.
///
/// During cycle 16: CRT draws pixel in position 15
/// Current CRT row: ##..##..##..##..
/// End of cycle 16: finish executing addx 13 (Register X is now 17)
/// Sprite position: ................###.....................
///
/// Start cycle 17: begin executing addx 4
/// During cycle 17: CRT draws pixel in position 16
/// Current CRT row: ##..##..##..##..#
///
/// During cycle 18: CRT draws pixel in position 17
/// Current CRT row: ##..##..##..##..##
/// End of cycle 18: finish executing addx 4 (Register X is now 21)
/// Sprite position: ....................###.................
///
/// Start cycle 19: begin executing noop
/// During cycle 19: CRT draws pixel in position 18
/// Current CRT row: ##..##..##..##..##.
/// End of cycle 19: finish executing noop
///
/// Start cycle 20: begin executing addx -1
/// During cycle 20: CRT draws pixel in position 19
/// Current CRT row: ##..##..##..##..##..
///
/// During cycle 21: CRT draws pixel in position 20
/// Current CRT row: ##..##..##..##..##..#
/// End of cycle 21: finish executing addx -1 (Register X is now 20)
/// Sprite position: ...................###..................
/// ```
///
/// Allowing the program to run to completion causes the CRT to produce the following image:
///
/// ```
/// ##..##..##..##..##..##..##..##..##..##..
/// ###...###...###...###...###...###...###.
/// ####....####....####....####....####....
/// #####.....#####.....#####.....#####.....
/// ######......######......######......####
/// #######.......#######.......#######.....
/// ```
///
/// Render the image given by your program. What eight capital letters appear on your CRT?
use clap::Parser;
use std::fmt::Write;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::path::PathBuf;
const FILEPATH: &'static str = "examples/input.txt";
const XINIT: i32 = 1;
const SPRITE_WIDTH: u8 = 3;
const CRT_WIDTH: i32 = 40;
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Cli {
#[clap(short, long, default_value = FILEPATH)]
file: PathBuf,
}
#[derive(Copy, Clone, Debug)]
enum Command {
Addx(i32),
Noop,
}
#[derive(Clone, Debug)]
struct State {
x: i32,
cycle: u32,
res: String,
}
impl State {
fn new() -> Self {
State {
x: XINIT,
cycle: 0,
res: String::new(),
}
}
fn process(&mut self) {
self.cycle += 1;
let pos = (self.cycle as i32 - 1) % CRT_WIDTH;
if (pos + 1) < self.x || pos + 1 >= (self.x + SPRITE_WIDTH as i32) {
self.res += " ";
} else {
self.res += "█";
}
if self.cycle as i32 % CRT_WIDTH == 0 {
writeln!(&mut self.res).unwrap();
}
}
}
impl Command {
fn parse(s: &str) -> Self {
let split_s = s.split_whitespace().collect::<Vec<&str>>();
match split_s.len() {
1 => Command::Noop,
2 => Command::Addx(split_s[1].parse::<i32>().unwrap()),
_ => panic!(),
}
}
}
fn main() {
let args = Cli::parse();
let file = File::open(&args.file).unwrap();
let reader = BufReader::new(file);
let res = reader
.lines()
.map(|l| Command::parse(l.unwrap().as_str()))
.scan(State::new(), |state, command| {
state.process();
match command {
Command::Noop => (),
Command::Addx(value) => {
state.process();
state.x += value;
}
};
Some(state.res.clone())
})
.last()
.unwrap();
// "ZGCJZJFL"
println!("{res}");
}
|