]> www.fi.muni.cz Git - aoc.git/blob - 2018/48.pl
Day 25: examining the input
[aoc.git] / 2018 / 48.pl
1 #!/usr/bin/perl -w
2
3 use v5.36;
4 use strict;
5
6 my @armies_txt;
7 { local $/ = "\n\n"; chomp (@armies_txt = <>); }
8
9 my @units = map { parse_army($_) } @armies_txt;
10 use Data::Dumper;
11 print Dumper \@units;
12
13 sub parse_army($text) {
14         my ($name, @rest) = split /\n/, $text;
15         $name =~ s/:\z//;
16         my $i = 0;
17         return ( map { parse_group($name, ++$i, $_) }
18                 grep { length $_ } @rest );
19 }
20
21 sub parse_group($army, $id, $text) {
22         my %rv = ( army => $army, name => "$army group $id", text => $text );
23         ($rv{units}) = $text =~ /\A(\d+) unit/;
24         ($rv{hp})    = $text =~ / (\d+) hit point/;
25         ($rv{damage}, $rv{damage_type})
26                      = $text =~ /that does (\d+) (\w+) damage /;
27         ($rv{init})  = $text =~ /at initiative (\d+)\z/;
28         my ($l) = $text =~ /weak to ([^;\)]+)[\);]/;
29         
30         $rv{weak}    = { map { $_ => 1 } split(/, /, $l) }
31                 if length $l;
32         ($l) =  $text =~ /immune to ([^;\)]+)[\);]/;
33         $rv{immune}  = { map { $_ => 1 } split(/, /, $l) }
34                 if length $l;
35         $rv{orig_units}  = $rv{units};
36         $rv{orig_damage} = $rv{damage};
37         return \%rv;
38 }
39
40 sub e_pwr($unit) { return $unit->{units} * $unit->{damage} };
41
42 sub damage_to($attacker, $defender) {
43         return 0 if $defender->{immune}->{$attacker->{damage_type}};
44         my $power = e_pwr($attacker);
45         $power *= 2 if $defender->{weak}->{$attacker->{damage_type}};
46         return $power;
47 }
48
49 sub selection {
50         say "";
51         my @rv;
52         my %attacked;
53         for my $unit (sort {
54                 $b->{units}*$b->{damage} <=> $a->{units}*$a->{damage}
55                 || $b->{init} <=> $a->{init}
56         } @units) {
57                 next if !$unit->{units};
58                 for my $def (sort {
59                                 damage_to($unit, $b) <=> damage_to($unit, $a)
60                                 || e_pwr($b) <=> e_pwr($a)
61                                 || $b->{init} <=> $a->{init}
62                         }
63                         grep {
64                                 $_->{army} ne $unit->{army}
65                                 && $_->{units}
66                                 && !$attacked{$_->{name}}
67                         } @units) {
68                         my $d = damage_to($unit, $def);
69                         next if !$d;
70                         say $unit->{name}, ' => ', $def->{name}, ': ', $d;
71                         $attacked{$def->{name}} = $unit->{name};
72                         push @rv, [ $unit, $def ];
73                         last;
74                 }
75         }
76         return \@rv;
77 }
78
79 sub attack($items) {
80         say "";
81         my $total_killed = 0;
82         for my $item (sort { $b->[0]->{init} <=> $a->[0]->{init} } @$items) {
83                 my ($att, $def) = @$item;
84                 next if !$att->{units};
85                 my $killed = int(damage_to($att, $def) / $def->{hp});
86                 $killed = $def->{units} if $killed > $def->{units};
87                 $total_killed += $killed;
88
89                 say "$att->{name} attacks $def->{name} killing $killed units";
90                 $def->{units} -= $killed;
91                 $def->{units} = 0 if $def->{units} < 0;
92         }
93         return $total_killed;
94 }
95
96 sub battle($boost) {
97         my $round = 0;
98
99         for my $unit (@units) {
100                 $unit->{units}  = $unit->{orig_units};
101                 $unit->{damage} = $unit->{orig_damage};
102                 $unit->{damage} += $boost
103                         if $unit->{army} eq 'Immune System';
104         }
105
106         say " ################## boost $boost ##################### ";
107         while (1) {
108                 say "round ", $round++;
109                 my %alive;
110                 my $sum;
111                 for my $unit (@units) {
112                         next if !$unit->{units};
113                         say "$unit->{name} contains $unit->{units} units";
114                         $sum += $unit->{units};
115                         $alive{$unit->{army}}++;
116                 }
117                 if (keys(%alive) < 2) {
118                         say "sum=$sum";
119                         return $alive{"Immune System"} ? 1 : 0;
120                 }
121                 return 0 if !attack(selection());
122         }
123 }
124
125 my $boost = 0;
126 1 while !battle($boost++);
127
128 __END__
129 Immune System:
130 889 units each with 3275 hit points (weak to bludgeoning, radiation; immune to cold) with an attack that does 36 bludgeoning damage at initiative 12
131 94 units each with 1336 hit points (weak to radiation, cold) with an attack that does 127 bludgeoning damage at initiative 7
132 1990 units each with 5438 hit points with an attack that does 25 slashing damage at initiative 20
133
134 Infection group 4 attacks Immune System group 9 killing 0 units
135 6523