Skip to content

Commit ceb8188

Browse files
committed
used bitwise operations
1 parent 4535147 commit ceb8188

File tree

3 files changed

+170
-76
lines changed

3 files changed

+170
-76
lines changed

src/bin/21.rs

Lines changed: 78 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
advent_of_code::solution!(21);
22

3-
use std::collections::HashSet;
3+
use advent_of_code::util::bignumbers::U1024;
44

5-
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
6-
struct Location {
7-
x: usize,
8-
y: usize,
9-
}
5+
use std::collections::HashSet;
106

117
struct Data {
12-
rock_locations: HashSet<Location>,
8+
rock_locations: HashSet<(usize, usize)>,
139
len_x: usize,
1410
len_y: usize,
1511
}
@@ -23,7 +19,7 @@ fn parse_data(input: &str) -> Data {
2319
for (y, line) in input.lines().enumerate() {
2420
for (x, v) in line.chars().enumerate() {
2521
if v == '#' {
26-
rock_locations.insert(Location { x, y });
22+
rock_locations.insert((x, y));
2723
}
2824
}
2925
}
@@ -35,78 +31,55 @@ fn parse_data(input: &str) -> Data {
3531
}
3632
}
3733

38-
// def do_magic(my_positions_bits):
39-
// new_position_bits = [x for x in my_positions_bits]
40-
// for y in range(1, len_y - 1):
41-
// left = my_positions_bits[y] << 1
42-
// right = my_positions_bits[y] >> 1
43-
// up = my_positions_bits[y-1]
44-
// down = my_positions_bits[y+1]
34+
fn run_step(my_positions_bits: &[U1024], rocks_bits: &[U1024]) -> Vec<U1024> {
35+
let mut new_positions_bits = my_positions_bits.iter().copied().collect::<Vec<_>>();
4536

46-
// new_position_bits[y] = (new_position_bits[y] | left | right | up | down) & ~my_positions_bits[y] & ~rocks_bits[y]
37+
for y in 1..my_positions_bits.len() - 1 {
38+
let left = my_positions_bits[y] << 1;
39+
let right = my_positions_bits[y] >> 1;
40+
let up = my_positions_bits[y - 1];
41+
let down = my_positions_bits[y + 1];
4742

48-
// return new_position_bits
49-
50-
fn run_step(
51-
my_locations: HashSet<Location>,
52-
rock_locations: &HashSet<Location>,
53-
len_x: usize,
54-
len_y: usize,
55-
) -> HashSet<Location> {
56-
let mut new_locations = HashSet::with_capacity(my_locations.len() * 4);
57-
58-
for loc in my_locations.into_iter() {
59-
let left = Location {
60-
x: loc.x.wrapping_sub(1),
61-
y: loc.y,
62-
};
63-
64-
let right = Location {
65-
x: loc.x + 1,
66-
y: loc.y,
67-
};
68-
69-
let up = Location {
70-
x: loc.x,
71-
y: loc.y.wrapping_sub(1),
72-
};
73-
74-
let down = Location {
75-
x: loc.x,
76-
y: loc.y + 1,
77-
};
78-
79-
new_locations.extend([left, right, up, down].iter().filter(|l| {
80-
!rock_locations.contains(&Location {
81-
x: l.x % len_x,
82-
y: l.y % len_y,
83-
})
84-
}));
43+
new_positions_bits[y] = (new_positions_bits[y] | left | right | up | down)
44+
& !my_positions_bits[y]
45+
& !rocks_bits[y]
8546
}
8647

87-
new_locations
48+
new_positions_bits
8849
}
8950

90-
pub fn part_one(input: &str) -> Option<u32> {
51+
pub fn part_one(input: &str) -> Option<u64> {
9152
let Data {
9253
rock_locations,
9354
len_x,
9455
len_y,
9556
} = parse_data(input);
9657

97-
let start_location = Location {
98-
x: len_x / 2,
99-
y: len_y / 2,
100-
};
58+
let mut rocks_bits = vec![!U1024::ZERO];
59+
for y in 0..len_y {
60+
let mut rocks_bits_line = U1024::ZERO;
61+
for x in 0..len_x {
62+
if rock_locations.contains(&(x, y)) {
63+
rocks_bits_line |= U1024::ONE << (x + 1)
64+
}
65+
rocks_bits_line |= U1024::ONE;
66+
rocks_bits_line |= U1024::ONE << (len_x + 1)
67+
}
68+
rocks_bits.push(rocks_bits_line);
69+
}
70+
rocks_bits.push(!U1024::ZERO);
10171

102-
let my_locations = (0..64).fold(HashSet::from([start_location]), |acc, _| {
103-
run_step(acc, &rock_locations, len_x, len_y)
104-
.into_iter()
105-
.filter(|l| (0..len_x).contains(&l.x) && (0..len_y).contains(&l.y))
106-
.collect()
107-
});
72+
let mut my_positions_bits = (0..rocks_bits.len())
73+
.map(|_| U1024::ZERO)
74+
.collect::<Vec<_>>();
75+
my_positions_bits[(len_y + 2) / 2] |= U1024::ONE << ((len_x + 2) / 2);
10876

109-
let result = my_locations.len() as u32;
77+
let my_positions_bits = (0..64).fold(my_positions_bits, |acc, _| run_step(&acc, &rocks_bits));
78+
79+
let result = my_positions_bits
80+
.iter()
81+
.map(|x| x.count_ones() as u64)
82+
.sum();
11083

11184
Some(result)
11285
}
@@ -118,26 +91,55 @@ pub fn part_two(input: &str) -> Option<u64> {
11891
len_y,
11992
} = parse_data(input);
12093

121-
let start_location = Location {
122-
x: len_x * 2 + len_x / 2,
123-
y: len_y * 2 + len_y / 2,
124-
};
94+
let mut rocks_bits = vec![];
95+
for y in 0..len_y {
96+
let mut rocks_bits_line = U1024::ZERO;
97+
for x in 0..len_x {
98+
if rock_locations.contains(&(x, y)) {
99+
rocks_bits_line |= U1024::ONE << x
100+
}
101+
}
102+
rocks_bits.push(rocks_bits_line);
103+
}
104+
105+
const GRID_MULTIPLIER: usize = 7;
106+
107+
let original_rocks_bits = rocks_bits;
108+
let mut rocks_bits = Vec::with_capacity(original_rocks_bits.len() * GRID_MULTIPLIER);
109+
for _ in 0..GRID_MULTIPLIER {
110+
rocks_bits.extend(original_rocks_bits.iter().copied());
111+
}
112+
113+
for l_x_m in 1..GRID_MULTIPLIER {
114+
for r in rocks_bits.iter_mut() {
115+
*r |= *r << (l_x_m * len_x);
116+
}
117+
}
118+
119+
let mut my_positions_bits = (0..rocks_bits.len())
120+
.map(|_| U1024::ZERO)
121+
.collect::<Vec<_>>();
122+
my_positions_bits[len_y * GRID_MULTIPLIER / 2] |= U1024::ONE << (len_x * GRID_MULTIPLIER / 2);
125123

126124
let mut results = vec![];
127-
let mut my_locations = HashSet::from([start_location]);
128125

129-
let magic_number_1 = len_x / 2;
130-
let magic_number_2 = magic_number_1 + len_x;
131-
let magic_number_3 = magic_number_2 + len_x;
126+
let magic_number_1 = len_x as i64 / 2;
127+
let magic_number_2 = magic_number_1 + len_x as i64;
128+
let magic_number_3 = magic_number_2 + len_x as i64;
132129

133130
let mut i = 0;
134131
while results.len() < 3 {
135-
my_locations = run_step(my_locations, &rock_locations, len_x, len_y);
132+
my_positions_bits = run_step(&my_positions_bits, &rocks_bits);
136133

137134
i += 1;
138135

139136
if i == magic_number_1 || i == magic_number_2 || i == magic_number_3 {
140-
results.push((i as i64, my_locations.len() as i64));
137+
let i_result = my_positions_bits
138+
.iter()
139+
.map(|x| x.count_ones() as i64)
140+
.sum::<i64>();
141+
142+
results.push((i, i_result));
141143
}
142144
}
143145

src/util/bignumbers.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#[derive(Debug, Clone, Copy)]
2+
pub struct U1024([u64; 16]);
3+
4+
impl U1024 {
5+
pub fn count_ones(&self) -> u32 {
6+
self.0.iter().map(|x| x.count_ones()).sum()
7+
}
8+
9+
pub const ZERO: U1024 = U1024([0; 16]);
10+
pub const ONE: U1024 = U1024([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
11+
}
12+
13+
impl std::ops::Shl<usize> for U1024 {
14+
type Output = U1024;
15+
16+
fn shl(self, rhs: usize) -> Self::Output {
17+
let mut result = U1024::ZERO;
18+
let word_shift = rhs / 64;
19+
let bit_shift = rhs % 64;
20+
for i in 0..16 {
21+
if bit_shift < 64 && i + word_shift < 16 {
22+
result.0[i + word_shift] += self.0[i] << bit_shift;
23+
}
24+
if bit_shift > 0 && i + word_shift + 1 < 16 {
25+
result.0[i + word_shift + 1] += self.0[i] >> (64 - bit_shift);
26+
}
27+
}
28+
result
29+
}
30+
}
31+
32+
impl std::ops::Shr<usize> for U1024 {
33+
type Output = U1024;
34+
35+
fn shr(self, shift: usize) -> U1024 {
36+
let mut result = U1024::ZERO;
37+
let word_shift = shift / 64;
38+
let bit_shift = shift % 64;
39+
for i in word_shift..16 {
40+
result.0[i - word_shift] += self.0[i] >> bit_shift;
41+
if bit_shift > 0 && i < 16 - 1 {
42+
result.0[i - word_shift] += self.0[i + 1] << (64 - bit_shift);
43+
}
44+
}
45+
result
46+
}
47+
}
48+
49+
impl std::ops::BitAnd for U1024 {
50+
type Output = U1024;
51+
52+
fn bitand(self, rhs: Self) -> Self::Output {
53+
let mut result = U1024::ZERO;
54+
for i in 0..16 {
55+
result.0[i] = self.0[i] & rhs.0[i];
56+
}
57+
result
58+
}
59+
}
60+
61+
impl std::ops::BitOr for U1024 {
62+
type Output = U1024;
63+
64+
fn bitor(self, rhs: Self) -> Self::Output {
65+
let mut result = U1024::ZERO;
66+
for i in 0..16 {
67+
result.0[i] = self.0[i] | rhs.0[i];
68+
}
69+
result
70+
}
71+
}
72+
73+
impl std::ops::BitOrAssign for U1024 {
74+
fn bitor_assign(&mut self, rhs: Self) {
75+
for i in 0..16 {
76+
self.0[i] = self.0[i] | rhs.0[i];
77+
}
78+
}
79+
}
80+
81+
impl std::ops::Not for U1024 {
82+
type Output = U1024;
83+
84+
fn not(self) -> Self::Output {
85+
let mut result = U1024::ZERO;
86+
for i in 0..16 {
87+
result.0[i] = !self.0[i];
88+
}
89+
result
90+
}
91+
}

src/util/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
pub mod bignumbers;
12
pub mod grid;
23
pub mod list;
34
pub mod parse;

0 commit comments

Comments
 (0)