Add 2024 Quest 12

This commit is contained in:
Chris Alge 2024-11-20 11:35:14 +01:00
parent 9ad4df7eca
commit fe3134d10e
9 changed files with 1115 additions and 0 deletions

View file

@ -0,0 +1,6 @@
[package]
name = "day12_desert_shower"
version = "0.1.0"
edition = "2021"
[dependencies]

View file

@ -0,0 +1,392 @@
Part I
The afternoon sun warms the faces of the weary knights in the tournament. The next round of challenges is scheduled to take place on the Memory Stack Desert, a flat surface of terrain which stretches out far into the horizon. Despite the late hour, it is still quite hot. Fortunately, the sun will soon set.
The brave contestants arrive at a row of catapults. However, these are not the standard catapults used in other kingdoms. These machines have been engineered by the knightly Order, making them far more powerful!
Each catapult consists of three segments stacked on top of one another. From bottom to top, they are labelled A, B, and C. Each segment can launch projectiles independently, making it possible to fire up to three projectiles simultaneously!
Aiming such a catapult is also incredibly effective, as the projectiles are completely unaffected by the wind, flying steadily and at a constant speed according to the specified shooting power, which can be set independently for each segment using a dial on the side of the machine.
Upon being launched, a projectile first travels diagonally upward at a 45-degree angle for as many segments as the set power allows. It then travels the same number of segments horizontally before descending diagonally downward, again at a 45-degree angle, until it reaches the ground.
An example projectile path, launched from segment B with a shooting power of 5, looks like this:
....................
......↗→→→→→........
.....↗......↘.......
....↗........↘......
...↗..........↘.....
.C↗............↘....
.B..............↘...
.A...............↘..
====================
Another example projectile, launched from segment C with a shooting power of 3, is illustrated as follows:
....................
....................
....↗→→→............
...↗....↘...........
..↗......↘..........
.C........↘.........
.B.........↘........
.A..........↘.......
====================
Each knight has several targets in front of them (your notes), marked on the map with the letter T . The task is to destroy all the targets, with each projectile only destroying the first target it hits. It is also advisable to start from the top blocks of the target structure to prevent any unpredictable fall.
For each shot, a ranking value can be calculated, which is the segment number (A:1, B:2, C:3) multiplied by the shooting power of the projectile. For the example shot above, the ranking value is: 2 (for segment B) * 5 (shooting power) = 10.
You start to calculate from which segments and with what power the projectiles should be fired to destroy all the targets without wasting any shots. As your score, provide the sum of the rankings of all fired projectiles.
Example based on the following notes:
.............
.C...........
.B......T....
.A......T.T..
=============
A projectile launched from segment C with a shooting power of 2:
...↗→→.......
..↗...↘......
.C.....↘.....
.B......X....
.A......T.T..
=============
Ranking value: 3 * 2 = 6.
A projectile launched from segment B with a shooting power of 2:
.............
...↗→→.......
.C↗...↘......
.B.....↘.....
.A......X.T..
=============
Ranking value: 2 * 2 = 4.
A projectile launched from segment A with a shooting power of 3:
.............
....↗→→→.....
.C.↗....↘....
.B↗......↘...
.A........X..
=============
Ranking value: 1 * 3 = 3.
The total ranking value for destroying all target blocks is 6 + 4 + 3 = 13
What is the ranking value of destroying your targets?
Part II
In the second round, the rules remain unchanged. However, the targets are now the ruins of a tower (your notes), which once served as an observation post for the desert. Since so little happens there, the structure has been abandoned and is now useless. Some of the ruin's blocks are made of Hard Rocks H , which require two projectiles to destroy.
Example based on the following notes:
.............
.C...........
.B......H....
.A......T.H..
=============
The situation is almost the same as in the previous part, but some of the blocks (marked with H ) need two projectiles to destroy.
The total ranking value for destroying all target blocks is (2 * 6) + 4 + (2 * 3) = 22
What is the ranking value of destroying all target blocks?
Part III
The sun has long set, and night has fallen. The stars twinkle pleasantly, but some of them seem to be... moving? They are also growing brighter and larger! Of course! They're not stars - it's a meteor shower! Fortunately, everything is heading towards the desert, so the people are safe.
The catapult competition has been perfectly planned because the final round involves defending against moving targets. Has anyone ever tried shooting at falling meteors with a catapult? Luckily, all the meteors are traveling at a constant speed, identical to the catapult's projectiles, at an exact 45-degree angle to the ground, in the direction of the knights.
Each projectile can destroy exactly one meteor, but all the knights will collaborate on executing a single plan, making it possible to fire multiple projectiles from the same segments simultaneously. The knights quickly note all current meteor coordinates relative to segment A (your notes), indicating that a sample space rock with coordinates 3 5 is 3 segments to the right of A and 5 segments above it:
............
....#.......
............
............
.C..........
.B..........
.A..........
============
The knights mission is to devise a plan that will allow to shoot down the meteors as high in the air as possible without wasting any projectiles. If it is possible to shoot a meteor at the same altitude in more than one way, the one with the lower ranking score is chosen.
Shooting power is always an integer value, and collisions can only occur at discrete points in time. Projectiles can be launched at any discrete time, not just at time 0.
Example based on the following notes:
6 5
6 7
10 5
For the noted meteor positions, the initial situation looks like this:
.......#......
..............
.......#...#..
..............
..............
.C............
.B............
.A............
==============
Let's start with the first meteor, which has initial coordinates 6 5 and moves as follows:
..............
..............
.......#......
..............
..............
.C............
.B............
.A............
==============
time: 0
..............
..............
.......↙......
......#.......
..............
.C............
.B............
.A............
==============
time: 1
..............
..............
.......↙......
......↙.......
.....#........
.C............
.B............
.A............
==============
time: 2
..............
..............
.......↙......
......↙.......
.....↙........
.C..#.........
.B............
.A............
==============
time: 3
..............
..............
.......↙......
......↙.......
.....↙........
.C..↙.........
.B.#..........
.A............
==============
time: 4
If the knights decide to launch a projectile from segment C with a shooting power of 1 at time 0, it will hit the target at time 3:
..............
..............
.......#......
..............
..............
.C............
.B............
.A............
==============
time: 0
..............
..............
.......↙......
......#.......
..↗...........
.C............
.B............
.A............
==============
time: 1
..............
..............
.......↙......
......↙.......
..↗→.#........
.C............
.B............
.A............
==============
time: 2
..............
..............
.......↙......
......↙.......
..↗→.↙........
.C..X.........
.B............
.A............
==============
time: 3
However, launching a projectile from segment A with a shooting power of 2 at time 0 hits the target at the same altitude:
..............
..............
.......#......
..............
..............
.C............
.B............
.A............
==============
time: 0
..............
..............
.......↙......
......#.......
..............
.C............
.B↗...........
.A............
==============
time: 1
..............
..............
.......↙......
......↙.......
.....#........
.C.↗..........
.B↗...........
.A............
==============
time: 2
..............
..............
.......↙......
......↙.......
.....↙........
.C.↗X.........
.B↗...........
.A............
==============
time: 3
The ranking score of that shoot is 2, which is lower compared to the use of segment C, so the knights will follow that option.
The initial coordinates of the second meteor are 6 7 , and it moves as follows:
.......#......
..............
..............
..............
..............
.C............
.B............
.A............
==============
time: 0
.......↙......
......#.......
..............
..............
..............
.C............
.B............
.A............
==============
time: 1
.......↙......
......↙.......
.....#........
..............
..............
.C............
.B............
.A............
==============
time: 2
.......↙......
......↙.......
.....↙........
....#.........
..............
.C............
.B............
.A............
==============
time: 3
.......↙......
......↙.......
.....↙........
....↙.........
...#..........
.C............
.B............
.A............
==============
time: 4
It can be hit at altitude 4, by a projectile shot from segment B with a shooting power of 3, or from segment C with a shooting power of 2, both resulting with a ranking score of 6.
The last meteor, which has initial coordinates 10 5 , is barely reachable, just above the ground, from segment C with a shooting power of 1, and this is the only way to destroy it.
..............
..............
...........#..
..............
..............
.C............
.B............
.A............
==============
time: 0
..............
..............
...........↙..
..........#...
..↗...........
.C............
.B............
.A............
==============
time: 1
..............
..............
...........↙..
..........↙...
..↗→.....#....
.C............
.B............
.A............
==============
time: 2
..............
..............
...........↙..
..........↙...
..↗→.....↙....
.C..↘...#.....
.B............
.A............
==============
time: 3
..............
..............
...........↙..
..........↙...
..↗→.....↙....
.C..↘...↙.....
.B...↘.#......
.A............
==============
time: 4
..............
..............
...........↙..
..........↙...
..↗→.....↙....
.C..↘...↙.....
.B...↘.↙......
.A....X.......
==============
time: 5
The ranking score of that shoot is 3.
The total ranking value is 2 + 6 + 3 = 11, and this is the best way to destroy all the meteors.
Another example of when the launch of the projectile needs to be delayed:
5 5
For the noted meteor position, the initial situation looks like this:
..............
..............
......#.......
..............
..............
.C............
.B............
.A............
==============
Shooting the projectile from segment A at time 0 would cause a collision at a non-discrete time, which is not allowed. The knights need to wait for the meteor to move 1 unit horizontally and vertically towards them:
..............
..............
......↙.......
.....#........
..............
.C............
.B............
.A............
==============
time: 1
Then it is possible to launch the projectile from segment A with a power of 2 to destroy the meteor at a discrete time. The ranking value for this example is: 2.
What is the lowest possible ranking score for the shots required to destroy all meteors at the highest altitudes possible?

View file

@ -0,0 +1,175 @@
use core::fmt::Display;
use std::num::ParseIntError;
#[derive(Debug, PartialEq, Eq)]
pub enum ParseError<'a> {
LineMalformed(&'a str),
ParseCharError(char),
ParseIntError(ParseIntError),
}
impl Display for ParseError<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::LineMalformed(e) => write!(f, "Unable to parse line: {e}. It should be formatted like \"23 42\""),
Self::ParseCharError(e) => write!(f, "Unable to parse item: {e}"),
Self::ParseIntError(e) => write!(f, "Error while trying to parse an integer: {e:?}"),
}
}
}
impl From<ParseIntError> for ParseError<'_> {
fn from(value: ParseIntError) -> Self {
Self::ParseIntError(value)
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Phase{ Ascend, Glide, Descend, }
#[derive(PartialEq, Eq, Clone, Copy)]
struct Coordinates{
x: usize,
y: usize,
}
impl<'a> TryFrom<&'a str> for Coordinates {
type Error = ParseError<'a>;
fn try_from(value: &'a str) -> Result<Self, Self::Error> {
if let Some((x, y)) = value.split_once(' ') {
Ok(Self { x: x.parse()?, y: y.parse()? })
} else {
Err(Self::Error::LineMalformed(value))
}
}
}
struct Catapult {
coordinates: Coordinates,
segment_number: usize,
}
impl Catapult {
fn can_hit(&self, target: Coordinates) -> Option<Phase> {
if target.x <= self.coordinates.x {
None
} else if target.y <= self.coordinates.y {
if (target.x + target.y - (self.coordinates.x + self.coordinates.y)) % 3 == 0 {
Some(Phase::Descend)
} else {
None
}
} else {
match (target.x - self.coordinates.x) / (target.y - self.coordinates.y) {
0 => if target.y - self.coordinates.y == target.x - self.coordinates.x {
Some(Phase::Ascend)
} else {
None
},
1 => Some(Phase::Glide),
_ => if (target.x + target.y - (self.coordinates.x + self.coordinates.y)) % 3 == 0 {
Some(Phase::Descend)
} else {
None
},
}
}
}
fn power_to_hit(&self, target: Coordinates) -> Option<usize> {
match self.can_hit(target) {
Some(Phase::Ascend) | Some(Phase::Glide) => Some(target.y - self.coordinates.y),
Some(Phase::Descend) => Some((target.x + target.y - (self.coordinates.x + self.coordinates.y)) / 3),
None => None,
}
}
}
fn try_parse(input: &str) -> Result<(Vec<Catapult>, Vec<Coordinates>), ParseError> {
let lines: Vec<_> = input.lines().collect();
let height = lines.len()-1;
let mut catapults = Vec::new();
let mut targets = Vec::new();
for (line_num, line) in lines.iter().enumerate() {
let y = height - line_num;
for (x, c) in line.chars().enumerate() {
match c {
'.' | '=' => (),
'T' => targets.push(Coordinates { x, y }),
'H' => targets.append(&mut vec![Coordinates { x, y }; 2]),
c if ['A', 'B', 'C'].contains(&c) => catapults.push(
Catapult { coordinates: Coordinates { x, y }, segment_number: c as usize - b'@' as usize }),
e => return Err(ParseError::ParseCharError(e)),
}
}
}
Ok((catapults, targets))
}
pub fn run(input: &str, part: usize) -> Result<usize, ParseError> {
match part {
1 | 2 => {
let (catapults, targets) = try_parse(input)?;
let score = (0..targets.len())
.map(|shot| {
let target = targets[shot];
let catapult = catapults.iter().find(|c| c.can_hit(target).is_some()).unwrap();
catapult.segment_number * catapult.power_to_hit(target).unwrap()
}).sum();
Ok(score)
},
3 => {
let catapults = [
Catapult { coordinates: Coordinates { x: 0, y: 0 }, segment_number: 1 },
Catapult { coordinates: Coordinates { x: 0, y: 1 }, segment_number: 2 },
Catapult { coordinates: Coordinates { x: 0, y: 2 }, segment_number: 3 },
];
let meteors = input.lines().map(Coordinates::try_from).collect::<Result<Vec<_>, _>>()?;
let score = meteors.iter().map(|meteor| {
for time in meteor.x.div_ceil(2)..(meteor.x).max(meteor.y) {
let target = Coordinates { x: meteor.x - time, y: meteor.y - time };
let shooters: Vec<_> = catapults.iter().filter(|c| c.can_hit(target).is_some()).collect();
if !shooters.is_empty() {
return shooters.iter().map(|c|
c.segment_number * c.power_to_hit(target).unwrap()
).min().unwrap();
}
}
panic!("A meteor could not be hit by any catapult");
}).sum();
Ok(score)
},
_ => panic!("Illegal part number"),
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::read_to_string;
fn read_file(name: &str) -> String {
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
}
#[test]
fn test_sample() {
let expected = [13, 22, 13];
for part in 1..=expected.len() {
let sample_input = read_file(&format!("tests/sample{part}"));
assert_eq!(run(&sample_input, part), Ok(expected[part-1]));
}
}
#[test]
fn test_challenge() {
let expected = [203, 20075, 718678];
for part in 1..=expected.len() {
let challenge_input = read_file(&format!("tests/challenge{part}"));
assert_eq!(run(&challenge_input, part), Ok(expected[part-1]));
}
}
}

View file

@ -0,0 +1,5 @@
.................................
.C...........T...T..T......T...T.
.B...........T...T..T......T...T.
.A...........T...T..T......T...T.
=================================

View file

@ -0,0 +1,21 @@
...................................................................................................
...............................................................................TT..TT..TTT..TT..TT.
...............................................................................TT..TT..TTT..TT..TT.
..............................................................................TTTTTTTTTTTTTTTTTTTTT
...............................................................................TTTTTTTTTTTTTTTTTTT.
...............................................................................TTHTTTTTT.TTTTTT.TT.
...............................................................................TTTTTTTT...TTTTT.TT.
...............................................................................TTTTTTTT...TTTTTHTT.
..............................................................................TTTTTTTTTTTTTTTTTTTTT
...............................................................................TTHT..TTH.T..HTTTTT.
...............................................................................TTHHTHTT...TT.H.TTT.
...............................................................................TTH.....HHH.TT.TTTT.
...............................................................................TTHT.HTTHH.HH.T..TT.
...............................................................................TT.T...H.THHT.T..TT.
...............................................................................TT..TTTHHHTH..HTHTT.
...............................................................................TT.T.T.HHTT..HTTHTT.
..............................................................................TTTTTTTTTTTTTTTTTTTTT
.C.............................................................................TTHTTTTT...TTTTTHTT.
.B.............................................................................TTTTTTT.....TTTT.TT.
.A.............................................................................TT.TTTT.....TTTTHTT.
===================================================================================================

View file

@ -0,0 +1,502 @@
3761 2787
3949 3132
3635 3093
3557 3106
3857 2570
3914 2719
3587 2856
3984 3245
3551 2897
3929 2814
3625 3030
3538 3379
3649 2753
3643 3204
3797 3079
3534 2778
3698 3077
3779 3263
3886 3432
3950 2579
3882 3233
3623 3206
3719 3071
3807 2597
3603 3491
3547 3041
3510 2816
3652 3219
3552 3310
3701 3466
3588 2506
3970 2587
3780 2502
3745 3037
3876 2829
3840 3358
3773 2900
3751 3315
3915 2729
3572 3482
3796 2503
3945 2555
3833 3281
3888 2955
3813 3237
3672 3102
3611 3351
3947 2962
3631 3300
3732 2881
3562 2681
3936 3369
3965 2501
3867 2867
3976 2770
3533 3426
3539 2633
3644 3500
3529 3333
3795 2886
3711 3209
3506 2782
3731 3034
3996 2861
3569 2813
3817 3453
3801 2956
3501 2527
3932 2841
3902 3073
3893 2850
3918 3148
3523 3214
3505 3116
3696 3413
3990 2676
3715 3406
3714 2624
3666 2849
3856 3348
3748 3385
3976 1989
3872 2884
3898 2976
3700 3700
3925 3275
3545 2896
3737 3023
3778 2866
3852 2643
3978 3439
3759 2775
3897 3016
3988 3151
3707 3011
3973 3404
3575 3449
3777 2738
3604 2846
3687 3411
3878 2909
3642 3386
3960 2796
3775 3347
3612 2831
3959 2546
3717 3455
3735 3494
3513 3126
3961 3146
3846 2562
3771 3135
3614 3024
3691 3274
3508 2531
3565 3371
3835 2663
3889 3327
3705 3014
3726 3359
3862 3033
3874 3337
3758 3184
3754 3063
3601 2874
3670 2535
3875 3125
3912 3052
3610 2923
3669 2522
3839 2641
3787 2876
3791 3128
3662 3345
3829 2614
3861 2940
3727 2926
3838 3164
3571 3340
3804 3210
3530 3226
3627 2808
3810 2610
3518 2726
3577 3294
3789 3010
3528 2640
3673 3131
3948 2750
3828 2795
3722 3117
3823 3205
3682 3124
3664 3308
3542 3261
3790 3235
3883 2865
3520 2863
3636 3355
3615 2520
3681 3062
3952 3450
3834 3441
3899 2626
3962 3246
3584 2648
3819 3045
3922 3322
3939 3437
3589 2634
3908 3290
3798 3000
3622 3075
3716 2988
3981 2918
3591 2656
3736 2544
3594 2791
3517 2944
3570 3438
3927 2704
3586 2758
3744 3293
3566 3208
3848 2622
3639 2891
3561 3361
3617 3099
3983 3367
3657 2603
3740 3454
3700 3701
3738 3288
3549 3182
3532 3417
3963 3238
3921 2727
3613 2504
3871 3137
3651 2987
3573 2691
3992 3262
3887 2938
3579 3341
3661 2872
3855 2679
3580 2952
7198 3600
3788 3356
3741 3435
3660 3430
3974 3169
3776 3434
3766 2827
3765 3311
3975 3292
3956 2860
3812 3442
3955 2810
3654 3069
3507 2859
3991 3284
3910 3476
3514 3309
3892 2576
3998 3026
3854 2635
3836 3277
3818 2763
3920 2800
3619 2557
10 10
3555 3433
3762 3225
3606 3465
3650 3383
3911 2930
3767 3252
3590 2921
3665 2514
3543 3414
3548 2708
3618 2695
3550 2551
3712 3331
3792 2903
3830 2586
3815 2542
3556 2840
3850 2986
3553 3217
3800 2717
3913 3202
3941 3390
3607 2734
3993 2878
3877 3080
3858 2659
3593 3236
3919 2768
3822 2839
3576 3264
3690 2869
3620 3428
3982 2757
3971 3422
3864 3221
3873 2588
3906 3089
3680 3382
3930 2703
3851 3133
3725 3403
3917 2547
3985 2885
3901 2604
3916 3050
3720 2871
3794 2995
3730 3370
3519 2963
3703 2760
3825 3429
3695 3188
3708 3167
3774 2644
3597 2949
3786 2661
3763 2888
3999 2978
3564 3218
3997 2538
3891 2943
3536 3463
3934 3027
3713 2842
3560 3213
3686 2532
3540 2765
3739 3499
3964 3171
3658 3058
3718 2595
3849 3130
3869 3150
3831 2895
3679 3187
3585 2818
3942 3448
3600 2945
3638 2989
3884 2911
3980 2561
3633 3009
3645 2541
3806 3017
3626 2953
3677 2566
3937 2609
3647 2966
3511 2591
3808 2580
3900 2879
3865 3283
3581 2823
3753 3059
3700 2598
3859 2941
3972 3399
3752 3489
3671 3287
3637 3349
3541 3303
3782 2568
3832 2809
3676 2623
3957 3144
3583 2836
3802 2638
3700 3702
3809 2599
3641 3039
3951 3346
3624 2937
3605 3484
3729 2733
3946 3270
3907 2982
3659 2639
3728 3459
3781 3330
3793 3387
3592 3339
3515 2692
3924 2991
3630 2677
3646 3452
3709 2847
3685 2948
3979 2745
3896 3273
3689 2567
3693 3074
3756 3051
3609 3198
3986 2521
3772 2553
3692 3028
3905 2767
3826 2706
3977 2678
3598 3436
3844 3352
3621 3407
3841 2592
3860 2545
3721 3242
3675 2799
3769 3185
3742 2601
3903 3372
3558 3244
3537 3257
3674 2950
3554 2583
3935 2631
3574 3181
3764 2828
3820 3004
3629 2777
3694 3195
3684 2584
3770 3141
3880 3384
3842 3230
3546 3258
3595 2821
3582 3161
3843 3342
3755 2883
3954 2632
3827 2852
3509 2752
3747 3278
3750 2811
3783 3072
3938 2857
3995 3446
3527 2534
3967 3147
3512 3388
3863 3443
3890 3091
3760 3375
3599 2713
3568 2754
3502 2558
3944 2875
3653 2650
3683 2554
3602 3424
3655 3298
3702 2762
3749 3477
3894 2904
3966 3269
3746 3064
3525 3412
3799 3464
3837 2801
3958 2746
3640 3168
3885 3431
3656 3031
3928 2919
3866 2826
3940 3216
3516 3316
3968 2721
3923 2851
3632 2516
3524 3005
3881 2716
3688 2946
3596 3473
3847 2667
3803 2781
3811 3486
3805 2981
3768 2518
3663 3368
3699 2646
3668 3461
3503 2959
3504 3097
3853 2942
3667 2783
3926 2873
3814 2606
3535 3286
3989 3391
3678 2894
3710 3123
3824 3153
3969 2602
3563 3096
3870 2550
3943 3223
3909 2802
3987 3122
3879 3249
3648 2997
3616 3212
3724 2578
3522 3393
3567 3376
3816 3425
4000 3166
3578 2974
7202 3602
3526 2688
3559 3084
3634 3066
3953 2533
3608 2749
3697 3232
3734 2564
3544 3134
3994 2701
3784 2843
3931 3174
3628 2613
3704 3215
7200 3601
3733 2569
3895 3301
3743 2785
3845 3046
3904 2766
3531 2890
3757 2887
3785 3297

View file

@ -0,0 +1,5 @@
.............
.C...........
.B......T....
.A......T.T..
=============

View file

@ -0,0 +1,5 @@
.............
.C...........
.B......H....
.A......T.H..
=============

View file

@ -0,0 +1,4 @@
6 5
6 7
10 5
5 5