File Coverage

blib/lib/Games/Lacuna/Task/Action/Mining.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Games::Lacuna::Task::Action::Mining;
2              
3 1     1   2297 use 5.010;
  1         4  
  1         57  
4             our $VERSION = $Games::Lacuna::Task::VERSION;
5              
6 1     1   411 use Moose;
  0            
  0            
7             extends qw(Games::Lacuna::Task::Action);
8             with qw(Games::Lacuna::Task::Role::Stars
9             Games::Lacuna::Task::Role::Ships
10             Games::Lacuna::Task::Role::PlanetRun);
11              
12             use List::Util qw(sum);
13             use Games::Lacuna::Client::Types qw(ore_types);
14              
15             sub description {
16             return q[Deploy mining platforms to best available asteroids];
17             }
18              
19             sub process_planet {
20             my ($self,$planet_stats) = @_;
21            
22             # Get observatory
23             my $mining = $self->find_building($planet_stats->{id},'MiningMinistry');
24            
25             # Get space port
26             my $spaceport = $self->find_building($planet_stats->{id},'SpacePort');
27            
28             return
29             unless $mining && $spaceport;
30            
31             # Get observatory probed stars
32             my $mining_object = $self->build_object($mining);
33             my $mining_data = $self->request(
34             object => $mining_object,
35             method => 'view_platforms',
36             );
37            
38             # Check if we can have more platforms
39             my $available_platforms = ($mining_data->{max_platforms} - scalar @{$mining_data->{platforms}});
40            
41             return
42             if $available_platforms == 0;
43            
44             my $spaceport_object = $self->build_object($spaceport);
45            
46             # Get available mining ships
47             my @avaliable_miningships = $self->get_ships(
48             planet => $planet_stats,
49             quantity => $available_platforms,
50             type => 'mining_platform_ship',
51             travelling => 1,
52             );
53            
54             return
55             unless scalar @avaliable_miningships;
56            
57             my %ores_production;
58             my %ores_coeficient;
59             my %asteroids;
60             my $ores_planet_total = sum(values %{$planet_stats->{ore}});
61            
62             # Get planet ore production
63             while (my ($ore,$quantity) = each %{$planet_stats->{ore}}) {
64             $ores_production{$ore} ||= 0;
65             $ores_production{$ore} += int(($quantity / $ores_planet_total) * $planet_stats->{ore_hour});
66             }
67            
68             # Get platforms ore production
69             foreach my $platform (@{$mining_data->{platforms}}) {
70             my $asteroid_id = $platform->{asteroid}{id};
71             $asteroids{$asteroid_id} ||= 0;
72             $asteroids{$asteroid_id} ++;
73             foreach my $ore (ore_types()) {
74             my $quantity = $platform->{$ore.'_hour'};
75             $ores_production{$ore} += $quantity;
76             }
77             }
78             # Total ore production
79             my $ores_production_total = sum(values %ores_production);
80            
81             # Calc which ores are underrepresented
82             my $ore_type_count = scalar ore_types();
83             foreach my $ore (ore_types()) {
84             $ores_coeficient{$ore} = -1*( ($ores_production{$ore} / $ores_production_total) - (1/$ore_type_count));
85             }
86            
87             # Get closest asteroids
88             my @asteroids = $self->closest_asteroids($planet_stats->{x},$planet_stats->{y},25);
89            
90             foreach my $asteroid (@asteroids) {
91             my $asteroid_quality = 1;
92             my $asteroid_id = $asteroid->{id};
93             while (my ($ore,$quantity) = each %{$asteroid->{ore}}) {
94             next
95             if $quantity <= 1;
96             $asteroid_quality += $ores_coeficient{$ore} * ($quantity*2)
97             if $ores_coeficient{$ore} > 0;
98             }
99            
100             # Calc asteroid quality based on ore quantity, number of different ores and exclusive ores (TODO make this better)
101             my $ore_count = $asteroid->{ore_total};
102             $ore_count /= 2;
103             $ore_count = 1
104             if $ore_count < 1;
105             $asteroid->{quality} = int($asteroid_quality * $asteroid->{ore_total} * $asteroid->{ore_count});
106             $asteroid->{quality} *= (1 - (0.1 * $asteroids{$asteroid_id}))
107             if defined $asteroids{$asteroid_id};
108             $self->log('debug','Calculated asteroid quality for %s is %i',$asteroid->{name},$asteroid->{quality})
109             }
110            
111             my @asteroids_sorted = sort { $b->{quality} <=> $a->{quality} } @asteroids;
112            
113             # Get all minings ships
114             MINING_SHIP:
115             foreach my $mining_ship (@avaliable_miningships) {
116             # Find best asteroid
117             ASTEROID_CANDIDATE:
118             while (scalar @asteroids_sorted) {
119             my $asteroid = shift(@asteroids_sorted);
120             my $asteroid_data = $self->request(
121             object => $spaceport_object,
122             method => 'get_ships_for',
123             params => [ $planet_stats->{id}, { "body_id" => $asteroid->{id} } ],
124             );
125            
126             next ASTEROID_CANDIDATE
127             if scalar(@{$asteroid_data->{incoming}}) > 0;
128             next ASTEROID_CANDIDATE
129             if scalar(@{$asteroid_data->{available}}) == 0;
130             next ASTEROID_CANDIDATE
131             if defined $asteroid_data->{mining_platforms}
132             && scalar(@{$asteroid_data->{mining_platforms}}) == $asteroid->{size};
133            
134             $self->log('notice',"Sending mining platform to %s",$asteroid->{name});
135            
136             # Send mining platform to best asteroid
137            
138             my $send_data = $self->request(
139             object => $spaceport_object,
140             method => 'send_ship',
141             params => [ $mining_ship,{ "body_id" => $asteroid->{id} } ],
142             );
143            
144             next MINING_SHIP;
145             }
146             }
147            
148             # TODO check transport ships
149              
150             return;
151             }
152              
153             sub closest_asteroids {
154             my ($self,$x,$y,$limit) = @_;
155            
156             $limit //= 1;
157            
158             my @asteroids;
159            
160             $self->search_stars_callback(
161             sub {
162             my ($star_data) = @_;
163            
164             foreach my $body (@{$star_data->{bodies}}) {
165             next
166             unless $body->{type} eq 'asteroid';
167             $body->{ore_total} = sum(values %{$body->{ore}});
168             $body->{ore_count} = scalar(grep { $_ > 1 } values %{$body->{ore}});
169             push(@asteroids,$body);
170             }
171            
172             return 0
173             if scalar(@asteroids) >= $limit;
174            
175             return 1;
176             },
177             x => $x,
178             y => $y,
179             is_known => 1,
180             distance => 1,
181             );
182              
183             return @asteroids;
184             }
185              
186             __PACKAGE__->meta->make_immutable;
187             no Moose;
188             1;