|
10 | 10 | //! these maximum values. This is the turn that the entire board will win.
|
11 | 11 | //!
|
12 | 12 | //! Filtering the board numbers by turn and a reverse lookup from turn to number gives the
|
13 |
| -//! score for each board. Sort each result by turn and the answers for part 1 and part1 are the |
| 13 | +//! score for each board. Sort each result by turn and the answers for part one and two are the |
14 | 14 | //! first and last values respectively.
|
15 | 15 | use crate::util::parse::*;
|
16 | 16 | use std::array::from_fn;
|
17 | 17 |
|
18 |
| -pub struct Input { |
| 18 | +const BOARD_SIZE: usize = 25; |
| 19 | +const ROWS_AND_COLS: [(usize, usize); 10] = |
| 20 | + [(0, 1), (5, 1), (10, 1), (15, 1), (20, 1), (0, 5), (1, 5), (2, 5), (3, 5), (4, 5)]; |
| 21 | + |
| 22 | +pub struct Board { |
19 | 23 | turn: usize,
|
20 | 24 | score: usize,
|
21 | 25 | }
|
22 | 26 |
|
23 |
| -pub fn parse(input: &str) -> Vec<Input> { |
24 |
| - let mut to_turn = [0; 100]; |
25 |
| - let mut from_turn = [0; 100]; |
| 27 | +pub fn parse(input: &str) -> Vec<Board> { |
| 28 | + let (prefix, suffix) = input.split_once("\n\n").unwrap(); |
| 29 | + let boards: Vec<_> = suffix.iter_unsigned().collect(); |
26 | 30 |
|
27 |
| - let mut chunks = input.split("\n\n"); |
| 31 | + let mut number_to_turn = [0; 100]; |
| 32 | + let mut turn_to_number = [0; 100]; |
28 | 33 |
|
29 |
| - for (i, n) in chunks.next().unwrap().iter_unsigned().enumerate() { |
30 |
| - to_turn[n] = i; |
31 |
| - from_turn[i] = n; |
| 34 | + for (i, n) in prefix.iter_unsigned().enumerate() { |
| 35 | + number_to_turn[n] = i; |
| 36 | + turn_to_number[i] = n; |
32 | 37 | }
|
33 | 38 |
|
34 |
| - let score = |chunk: &str| { |
35 |
| - let mut iter = chunk.iter_unsigned(); |
36 |
| - let board: [usize; 25] = from_fn(|_| iter.next().unwrap()); |
37 |
| - let turns: [usize; 25] = from_fn(|i| to_turn[board[i]]); |
38 |
| - |
39 |
| - let row_and_cols = [ |
40 |
| - turns[0..5].iter().max().unwrap(), |
41 |
| - turns[5..10].iter().max().unwrap(), |
42 |
| - turns[10..15].iter().max().unwrap(), |
43 |
| - turns[15..20].iter().max().unwrap(), |
44 |
| - turns[20..25].iter().max().unwrap(), |
45 |
| - turns.iter().step_by(5).max().unwrap(), |
46 |
| - turns.iter().skip(1).step_by(5).max().unwrap(), |
47 |
| - turns.iter().skip(2).step_by(5).max().unwrap(), |
48 |
| - turns.iter().skip(3).step_by(5).max().unwrap(), |
49 |
| - turns.iter().skip(4).step_by(5).max().unwrap(), |
50 |
| - ]; |
51 |
| - let winning_turn = **row_and_cols.iter().min().unwrap(); |
52 |
| - let unmarked: usize = board.iter().filter(|&&n| to_turn[n] > winning_turn).sum(); |
53 |
| - let just_called = from_turn[winning_turn]; |
| 39 | + boards |
| 40 | + .chunks_exact(BOARD_SIZE) |
| 41 | + .map(|board| { |
| 42 | + let turns: [usize; BOARD_SIZE] = from_fn(|i| number_to_turn[board[i]]); |
| 43 | + let max = |&(skip, step)| *turns.iter().skip(skip).step_by(step).take(5).max().unwrap(); |
54 | 44 |
|
55 |
| - Input { turn: winning_turn, score: unmarked * just_called } |
56 |
| - }; |
| 45 | + let winning_turn = ROWS_AND_COLS.iter().map(max).min().unwrap(); |
| 46 | + let unmarked: usize = board.iter().filter(|&&n| number_to_turn[n] > winning_turn).sum(); |
| 47 | + let just_called = turn_to_number[winning_turn]; |
57 | 48 |
|
58 |
| - let mut scores: Vec<_> = chunks.map(score).collect(); |
59 |
| - scores.sort_unstable_by_key(|s| s.turn); |
60 |
| - scores |
| 49 | + Board { turn: winning_turn, score: unmarked * just_called } |
| 50 | + }) |
| 51 | + .collect() |
61 | 52 | }
|
62 | 53 |
|
63 |
| -pub fn part1(input: &[Input]) -> usize { |
64 |
| - input.first().unwrap().score |
| 54 | +pub fn part1(input: &[Board]) -> usize { |
| 55 | + input.iter().min_by_key(|b| b.turn).unwrap().score |
65 | 56 | }
|
66 | 57 |
|
67 |
| -pub fn part2(input: &[Input]) -> usize { |
68 |
| - input.last().unwrap().score |
| 58 | +pub fn part2(input: &[Board]) -> usize { |
| 59 | + input.iter().max_by_key(|b| b.turn).unwrap().score |
69 | 60 | }
|
0 commit comments