]> www.fi.muni.cz Git - aoc.git/blob - 2018/47.pl
Day 25: examining the input
[aoc.git] / 2018 / 47.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{hp})    = $text =~ / (\d+) hit point/;
26         ($rv{damage}, $rv{damage_type})
27                      = $text =~ /that does (\d+) (\w+) damage /;
28         ($rv{init})  = $text =~ /at initiative (\d+)\z/;
29         $rv{weak}    = {
30                 map { defined $_ ? ($_ => 1) : () }
31                 $text =~ /weak to (\w+)(?:, (\w+))*/
32         };
33         $rv{immune}  = {
34                 map { defined $_ ? ($_ => 1) : () }
35                 $text =~ /immune to (\w+)(?:, (\w+))*/
36         };
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         for my $item (sort { $b->[0]->{init} <=> $a->[0]->{init} } @$items) {
82                 my ($att, $def) = @$item;
83                 next if !$att->{units};
84                 my $killed = int(damage_to($att, $def) / $def->{hp});
85                 $killed = $def->{units} if $killed > $def->{units};
86
87                 say "$att->{name} attacks $def->{name} killing $killed units";
88                 $def->{units} -= $killed;
89                 $def->{units} = 0 if $def->{units} < 0;
90         }
91 }
92
93 my $round = 0;
94 while (1) {
95         say "round ", $round++;
96         my %alive;
97         my $sum;
98         for my $unit (@units) {
99                 next if !$unit->{units};
100                 say "$unit->{name} contains $unit->{units} units";
101                 $sum += $unit->{units};
102                 $alive{$unit->{army}}++;
103         }
104         if (keys(%alive) < 2) {
105                 say "sum=$sum";
106                 last;
107         }
108         attack(selection());
109 }
110
111 __END__
112 Immune System:
113 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
114 94 units each with 1336 hit points (weak to radiation, cold) with an attack that does 127 bludgeoning damage at initiative 7
115 1990 units each with 5438 hit points with an attack that does 25 slashing damage at initiative 20
116
117