]> www.fi.muni.cz Git - aoc.git/blobdiff - 2018/47.pl
Year 2018
[aoc.git] / 2018 / 47.pl
diff --git a/2018/47.pl b/2018/47.pl
new file mode 100755 (executable)
index 0000000..1e1e594
--- /dev/null
@@ -0,0 +1,117 @@
+#!/usr/bin/perl -w
+
+use v5.36;
+use strict;
+
+my @armies_txt;
+{ local $/ = "\n\n"; chomp (@armies_txt = <>); }
+
+my @units = map { parse_army($_) } @armies_txt;
+use Data::Dumper;
+# print Dumper \@units;
+
+sub parse_army($text) {
+       my ($name, @rest) = split /\n/, $text;
+       $name =~ s/:\z//;
+       my $i = 0;
+       return ( map { parse_group($name, ++$i, $_) }
+               grep { length $_ } @rest );
+}
+
+sub parse_group($army, $id, $text) {
+       my %rv = ( army => $army, name => "$army group $id", text => $text );
+       ($rv{units}) = $text =~ /\A(\d+) unit/;
+       ($rv{hp})    = $text =~ / (\d+) hit point/;
+       ($rv{hp})    = $text =~ / (\d+) hit point/;
+       ($rv{damage}, $rv{damage_type})
+                    = $text =~ /that does (\d+) (\w+) damage /;
+       ($rv{init})  = $text =~ /at initiative (\d+)\z/;
+       $rv{weak}    = {
+               map { defined $_ ? ($_ => 1) : () }
+               $text =~ /weak to (\w+)(?:, (\w+))*/
+       };
+       $rv{immune}  = {
+               map { defined $_ ? ($_ => 1) : () }
+               $text =~ /immune to (\w+)(?:, (\w+))*/
+       };
+       return \%rv;
+}
+
+sub e_pwr($unit) { return $unit->{units} * $unit->{damage} };
+
+sub damage_to($attacker, $defender) {
+       return 0 if $defender->{immune}->{$attacker->{damage_type}};
+       my $power = e_pwr($attacker);
+       $power *= 2 if $defender->{weak}->{$attacker->{damage_type}};
+       return $power;
+}
+
+sub selection {
+       say "";
+       my @rv;
+       my %attacked;
+       for my $unit (sort {
+               $b->{units}*$b->{damage} <=> $a->{units}*$a->{damage}
+               || $b->{init} <=> $a->{init}
+       } @units) {
+               next if !$unit->{units};
+               for my $def (sort {
+                               damage_to($unit, $b) <=> damage_to($unit, $a)
+                               || e_pwr($b) <=> e_pwr($a)
+                               || $b->{init} <=> $a->{init}
+                       }
+                       grep {
+                               $_->{army} ne $unit->{army}
+                               && $_->{units}
+                               && !$attacked{$_->{name}}
+                       } @units) {
+                       my $d = damage_to($unit, $def);
+                       next if !$d;
+                       say $unit->{name}, ' => ', $def->{name}, ': ', $d;
+                       $attacked{$def->{name}} = $unit->{name};
+                       push @rv, [ $unit, $def ];
+                       last;
+               }
+       }
+       return \@rv;
+}
+
+sub attack($items) {
+       say "";
+       for my $item (sort { $b->[0]->{init} <=> $a->[0]->{init} } @$items) {
+               my ($att, $def) = @$item;
+               next if !$att->{units};
+               my $killed = int(damage_to($att, $def) / $def->{hp});
+               $killed = $def->{units} if $killed > $def->{units};
+
+               say "$att->{name} attacks $def->{name} killing $killed units";
+               $def->{units} -= $killed;
+               $def->{units} = 0 if $def->{units} < 0;
+       }
+}
+
+my $round = 0;
+while (1) {
+       say "round ", $round++;
+       my %alive;
+       my $sum;
+       for my $unit (@units) {
+               next if !$unit->{units};
+               say "$unit->{name} contains $unit->{units} units";
+               $sum += $unit->{units};
+               $alive{$unit->{army}}++;
+       }
+       if (keys(%alive) < 2) {
+               say "sum=$sum";
+               last;
+       }
+       attack(selection());
+}
+
+__END__
+Immune System:
+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
+94 units each with 1336 hit points (weak to radiation, cold) with an attack that does 127 bludgeoning damage at initiative 7
+1990 units each with 5438 hit points with an attack that does 25 slashing damage at initiative 20
+
+