Skip to content

Commit 1c478fe

Browse files
committed
Faster approach by increasing array size to power of two
1 parent 7ce8a26 commit 1c478fe

File tree

2 files changed

+24
-27
lines changed

2 files changed

+24
-27
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ Performance is reasonable even on older hardware, for example a 2011 MacBook Pro
224224
| 19 | [Monster Messages](https://adventofcode.com/2020/day/19) | [Source](src/year2020/day19.rs) | 362 |
225225
| 20 | [Jurassic Jigsaw](https://adventofcode.com/2020/day/20) | [Source](src/year2020/day20.rs) | 42 |
226226
| 21 | [Allergen Assessment](https://adventofcode.com/2020/day/21) | [Source](src/year2020/day21.rs) | 45 |
227-
| 22 | [Crab Combat](https://adventofcode.com/2020/day/22) | [Source](src/year2020/day22.rs) | 5911 |
227+
| 22 | [Crab Combat](https://adventofcode.com/2020/day/22) | [Source](src/year2020/day22.rs) | 5333 |
228228
| 23 | [Crab Cups](https://adventofcode.com/2020/day/23) | [Source](src/year2020/day23.rs) | 109000 |
229229
| 24 | [Lobby Layout](https://adventofcode.com/2020/day/24) | [Source](src/year2020/day24.rs) | 330 |
230230
| 25 | [Combo Breaker](https://adventofcode.com/2020/day/25) | [Source](src/year2020/day25.rs) | 20 |

src/year2020/day22.rs

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ type Input = (Deck, Deck);
6262
type Cache = Vec<FastSet<(usize, usize)>>;
6363

6464
enum Winner {
65-
Player1,
66-
Player2,
65+
Player1(Deck),
66+
Player2(Deck),
6767
}
6868

6969
#[derive(Clone, Copy)]
@@ -72,33 +72,33 @@ pub struct Deck {
7272
score: usize,
7373
start: usize,
7474
end: usize,
75-
cards: [u8; 50],
75+
cards: [u8; 64],
7676
}
7777

7878
impl Deck {
7979
fn new() -> Deck {
80-
Deck { sum: 0, score: 0, start: 0, end: 0, cards: [0; 50] }
80+
Deck { sum: 0, score: 0, start: 0, end: 0, cards: [0; 64] }
8181
}
8282

8383
// To make things easier, `start` and `end` never wrap around, so that `end` is always
8484
// greater than or equal to `start`.
8585
fn pop_front(&mut self) -> usize {
86-
let card = self.cards[self.start % 50] as usize;
86+
let card = self.cards[self.start % 64] as usize;
8787
self.sum -= card;
8888
self.score -= self.size() * card;
8989
self.start += 1;
9090
card
9191
}
9292

9393
fn push_back(&mut self, card: usize) {
94-
self.cards[self.end % 50] = card as u8;
94+
self.cards[self.end % 64] = card as u8;
9595
self.sum += card;
9696
self.score += self.sum;
9797
self.end += 1;
9898
}
9999

100100
fn max(&self) -> u8 {
101-
(self.start..self.end).map(|i| self.cards[i % 50]).max().unwrap()
101+
(self.start..self.end).map(|i| self.cards[i % 64]).max().unwrap()
102102
}
103103

104104
fn non_empty(&self) -> bool {
@@ -109,17 +109,15 @@ impl Deck {
109109
self.end - self.start
110110
}
111111

112-
// Sneaky trick here to speed things up a little. We don't recalculate the score properly,
113-
// so it will be too high by a constant amount. This doesn't matter for recursive games as
114-
// we only need the winner, not the exact score.
115112
fn copy(&self, amount: usize) -> Deck {
116-
let mut copy = *self;
117-
copy.end = copy.start + amount;
118-
copy.sum = 0;
113+
let mut copy = Deck::new();
114+
copy.end = amount;
119115

120116
for i in 0..amount {
121-
let card = copy.cards[(copy.start + i) % 50] as usize;
122-
copy.sum += card;
117+
let card = self.cards[(self.start + i) % 64];
118+
copy.cards[i] = card;
119+
copy.sum += card as usize;
120+
copy.score += copy.sum;
123121
}
124122

125123
copy
@@ -155,18 +153,17 @@ pub fn part1(input: &Input) -> usize {
155153
}
156154

157155
pub fn part2(input: &Input) -> usize {
158-
let (mut deck1, mut deck2) = *input;
156+
let (deck1, deck2) = *input;
159157

160-
match combat(&mut deck1, &mut deck2, &mut Vec::new(), 0) {
161-
Winner::Player1 => deck1.score,
162-
Winner::Player2 => deck2.score,
158+
match combat(deck1, deck2, &mut Vec::new(), 0) {
159+
Winner::Player1(deck) | Winner::Player2(deck) => deck.score,
163160
}
164161
}
165162

166-
fn combat(deck1: &mut Deck, deck2: &mut Deck, cache: &mut Cache, depth: usize) -> Winner {
163+
fn combat(mut deck1: Deck, mut deck2: Deck, cache: &mut Cache, depth: usize) -> Winner {
167164
// Player 1 always wins recursive games if they have the high card.
168165
if depth > 0 && deck1.max() > deck2.max() {
169-
return Winner::Player1;
166+
return Winner::Player1(deck1);
170167
}
171168

172169
// Speed things up by re-using previously created caches, avoiding slow extra heap allocations.
@@ -179,7 +176,7 @@ fn combat(deck1: &mut Deck, deck2: &mut Deck, cache: &mut Cache, depth: usize) -
179176
while deck1.non_empty() && deck2.non_empty() {
180177
// This will *very probably* work! Not 100% deterministic.
181178
if !cache[depth].insert((deck1.score, deck2.score)) {
182-
return Winner::Player1;
179+
return Winner::Player1(deck1);
183180
}
184181

185182
let (card1, card2) = (deck1.pop_front(), deck2.pop_front());
@@ -193,18 +190,18 @@ fn combat(deck1: &mut Deck, deck2: &mut Deck, cache: &mut Cache, depth: usize) -
193190
deck2.push_back(card1);
194191
}
195192
} else {
196-
match combat(&mut deck1.copy(card1), &mut deck2.copy(card2), cache, depth + 1) {
197-
Winner::Player1 => {
193+
match combat(deck1.copy(card1), deck2.copy(card2), cache, depth + 1) {
194+
Winner::Player1(_) => {
198195
deck1.push_back(card1);
199196
deck1.push_back(card2);
200197
}
201-
Winner::Player2 => {
198+
Winner::Player2(_) => {
202199
deck2.push_back(card2);
203200
deck2.push_back(card1);
204201
}
205202
}
206203
}
207204
}
208205

209-
if deck1.non_empty() { Winner::Player1 } else { Winner::Player2 }
206+
if deck1.non_empty() { Winner::Player1(deck1) } else { Winner::Player2(deck2) }
210207
}

0 commit comments

Comments
 (0)