5 use experimental 'multidimensional';
7 my @map = map { chomp; [ split // ] } <>;
8 my $xmax = $#{ $map[0] };
12 for my $y (0 .. $ymax) {
13 for my $x (0 .. $xmax) {
14 if ($map[$y][$x] eq 'G') {
15 push @units, [$x, $y, 'G', 200];
16 } elsif ($map[$y][$x] eq 'E') {
17 push @units, [$x, $y, 'E', 200];
22 sub enemy_of($unit) { $unit->[2] eq 'G' ? 'E' : 'G' }
24 my @neighbors = ([0, -1], [-1, 0], [1, 0], [0, 1]);
27 return sort { $a->[1] == $b->[1] ? $a->[0] <=> $b->[0] : $a->[1] <=> $b->[1] } @_;
31 for my $y (0 .. $ymax) {
32 for my $x (0 .. $xmax) {
37 for my $unit (grep { $_->[3] } reading_sort(@units)) {
38 say "$unit->[2] at $unit->[0],$unit->[1] ($unit->[3])"
42 sub neigh_squares($unit, $type) {
44 for my $n (@neighbors) {
48 if ($map[$dy][$dx] eq $type) {
49 push @rv, [ $dx, $dy ];
55 sub enemy_within_range($unit) {
56 return neigh_squares($unit, enemy_of($unit));
60 for my $unit (@units) {
62 if $unit->[0] == $loc->[0] && $unit->[1] == $loc->[1];
64 die "Can't locate unit at ", join(',', @$loc);
67 sub attack($unit, $enemies) {
68 say "Attacking unit: ", join(',', @$unit);
71 for my $enemy (@$enemies) {
72 say "Enemy: ", join(',', @$enemy);
73 my $enemy_unit = loc2unit($enemy);
74 if (!defined $min_hp || $min_hp > $enemy_unit->[3]) {
75 $min_hp = $enemy_unit->[3];
78 if ($min_hp == $enemy_unit->[3]) {
79 push @min_hp, $enemy_unit;
83 @min_hp = reading_sort(@min_hp);
84 my $enemy = shift @min_hp;
87 if ($enemy->[3] <= 0) {
89 $map[$enemy->[1]][$enemy->[0]] = '.';
91 $enemy->[3] = 0 if $enemy->[3] < 0;
92 say "attacked ", join(',', @$enemy);
95 sub dump_path($path) {
96 join(' ', map { "[$_->[0],$_->[1]]" } @$path);
101 for my $enemy (grep { $_->[2] ne $unit->[2] } @units) {
102 for my $sq (neigh_squares($enemy, '.')) {
103 if (!$target_sq{$sq->[0],$sq->[1]}++) {
104 # say "target square $sq->[0],$sq->[1]";
109 my @q = [ [ $unit->[0], $unit->[1] ] ];
115 # say "walking path ", dump_path($path);
116 last if $found_at_dist && @$path > $found_at_dist;
117 for my $sq (neigh_squares($path->[-1], '.')) {
118 next if $visited{$sq->[0],$sq->[1]}++;
120 if ($target_sq{$sq->[0],$sq->[1]}) {
121 $found_at_dist //= @$path;
122 my $path1 = [ @$path, [ @$sq ] ];
123 # say "Found square $sq->[0],$sq->[1] through ",
125 push @reachable, [ @$sq, @{ $path1->[1] } ];
127 push @q, [ @$path, [ @$sq ] ];
133 # say "no reachable squares";
137 @reachable = reading_sort(@reachable);
138 my $target = $reachable[0];
139 # say "moving to $target->[0],$target->[1] via $target->[2],$target->[3]";
140 $map[$unit->[1]][$unit->[0]] = '.';
141 $map[$unit->[1] = $target->[3]][$unit->[0] = $target->[2]] = $unit->[2];
144 sub unit_round($unit) {
145 return if !$unit->[3]; # are we alive?
146 # say "\nunit_round ", join(',', @$unit);
148 my @enemies = enemy_within_range($unit);
151 @enemies = enemy_within_range($unit);
154 attack($unit, \@enemies);
162 say "After $round rounds:";
165 @units = reading_sort(grep { $_->[3] } @units);
167 for my $unit (@units) {
171 $total_hp{$u->[2]} += $u->[3]
174 if (keys %total_hp < 2) {
183 say "After $round rounds:";
186 say "ended after round $round with hp ", join('=>', %total_hp);
187 say $round * (%total_hp)[1];