-
Notifications
You must be signed in to change notification settings - Fork 8
/
sspush.pl
executable file
·163 lines (137 loc) · 6.25 KB
/
sspush.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/perl
# tries to top off a space station, calculating just enough to fill it at time of arrival
# doesn't take into account ships in flight (or even the cargo capacity of the ship being sent)
# so not good for higher usage or farther away stations
use strict;
use warnings;
use Carp;
use Client;
use Getopt::Long;
use IO::Handle;
use JSON::PP;
use List::Util 'sum';
autoflush STDOUT 1;
autoflush STDERR 1;
my %opt;
GetOptions( \%opt, 'config=s', 'from=s', 'to=s', 'ship=s', 'debug', 'quiet', 'log=s', 'types=s', 'send=i', 'dry-run', 'ore=s@', 'food=s@' )
or die "$0 --config=foo.json --from=PlanetBar --to=SpaceStationFoo --ship=shipname\n";
$opt{'config'} ||= 'config.json';
$opt{'types'} ||= 'food,ore,water,energy';
my $client = Client->new(config => $opt{'config'});
my $planets = $client->empire_status->{planets};
my $from_id;
my $to_id;
for my $id (keys(%$planets)) {
$from_id = $id if lc $planets->{$id} eq lc $opt{'from'};
$to_id = $id if lc $planets->{$id} eq lc $opt{'to'};
}
if ( ! ( $from_id && $to_id ) ) {
exit(1) if $opt{'quiet'};
die "No matching planet for name $opt{'from'}\n" unless $from_id;
die "No matching planet for name $opt{'to'}\n" unless $to_id;
}
my $from_name = $planets->{$from_id};
my $to_name = $planets->{$to_id};
my %resources = (
'water' => [ qw/water/ ],
'energy' => [ qw/energy/ ],
'food' => $opt{'food'} || [ qw/algae apple bean beetle bread burger cheese chip cider corn fungus lapis meal milk pancake pie potato root shake soup syrup wheat/ ],
'ore' => $opt{'ore'} || [ qw/anthracite bauxite beryl chalcopyrite chromite fluorite galena goethite gold gypsum halite kerogen magnetite methane monazite rutile sulfur trona uraninite zircon/ ],
'waste' => [ qw/waste/ ],
);
# what ships and resources are available?
my $buildings = $client->body_buildings($from_id);
my %building_types;
for my $building_id ( keys %{ $buildings->{'buildings'} } ) {
push @{ $building_types{$buildings->{'buildings'}{$building_id}{'name'}} }, $building_id;
}
my $trade_ministry = $building_types{'Trade Ministry'}[0];
if ( ! $trade_ministry ) {
exit(1) if $opt{'quiet'};
die "No trade ministry found on $from_name\n";
}
my $have_resources = $client->call('trade', 'get_stored_resources', $trade_ministry)->{'resources'};
my $ships = $client->call('trade', 'get_trade_ships', $trade_ministry, $to_id);
my @ship_list = grep $_->{'name'} eq $opt{'ship'}, @{ $ships->{'ships'} };
my ($example_ship) = @ship_list;
if ( ! $example_ship ) {
exit(1) if $opt{'quiet'};
die "Ship $opt{'ship'} not available on $from_name\n";
}
$client->cache_invalidate( type => 'body_status', id => $to_id );
$client->cache_invalidate( type => 'buildings', id => $to_id );
# what do we need to fill up (excluding supply pod capacity)?
my $to_buildings = $client->body_buildings($to_id);
my %supply_pod_capacity;
for my $supply_pod ( grep $to_buildings->{'buildings'}{$_}{'name'} eq 'Supply Pod', keys %{ $to_buildings->{'buildings'} } ) {
my $capacity = $client->call( $to_buildings->{'buildings'}{$supply_pod}{'url'}, 'view', $supply_pod )->{'building'};
for my $resource_type ( sort keys %resources ) {
$supply_pod_capacity{$resource_type} += int( ( $capacity->{ $resource_type . '_capacity' } || 0 ) * ( $capacity->{'efficiency'} / 100 ) );
}
}
my $to_status = $client->body_status($to_id);
my %need;
for my $resource_type ( split /,/, $opt{'types'} ) {
$need{$resource_type} = int( $to_status->{ $resource_type . '_capacity' } - $to_status->{ $resource_type . '_stored' } - $to_status->{ $resource_type . '_hour' } * $example_ship->{'estimated_travel_time'} / 3600 ) - ( $supply_pod_capacity{$resource_type} || 0 ) - 1;
# printf "%s\t%s\t%s/%s\t%s/hr\n", $resource_type, $need{$resource_type}, map $to_status->{$resource_type . "_$_"}, qw/stored capacity hour/;
delete $need{$resource_type} if $need{$resource_type} <= 0;
}
if ( $opt{'dry-run'} ) {
print "Resources needed:\n";
print "$_: $need{$_}\n" for sort keys %need;
print "total: " . sum( values %need ) . "\n";
}
my $ship_count = ($opt{send} || scalar(@ship_list));
my $max_send = $example_ship->{'hold_size'} * $ship_count;
if ($max_send < sum values %need) {
my $factor = $max_send / sum values %need;
$need{$_} *= $factor for keys %need;
}
if ( $opt{'dry-run'} ) {
print "Ship capacity: $max_send\n";
exit;
}
$need{$_} = int($need{$_} / $ship_count) for (keys %need);
for my $ship ( @ship_list[0 .. $ship_count - 1] ) {
my %send;
for my $resource_type ( sort keys %need ) {
# for each type, take whatever there is most of, leaving them level
my %available;
for my $resource ( @{ $resources{$resource_type} } ) {
if ( $have_resources->{$resource} ) {
push @{ $available{ $have_resources->{$resource} } }, $resource;
}
}
my $need = $need{$resource_type};
my @quantities = sort { $b <=> $a } keys %available;
while ( $need && @quantities ) {
my $quantity = shift @quantities;
my $next_quantity = $quantities[0] || 0;
my @resources = sort @{ $available{$quantity} };
# take enough of each to bump it down to the next lower occuring quantity
# (or less, if that's too much)
my $take = @resources * ( $quantity - $next_quantity );
if ( $take > $need ) { $take = $need }
my $take_per_resource = int( $take / @resources );
my $remainder = $take - @resources * $take_per_resource;
for my $resource ( @resources ) {
$send{$resource} += $take_per_resource + ( --$remainder >= 0 );
push @{ $available{$next_quantity} }, $resource;
}
$need -= $take;
}
}
my @items = map { { type => $_, quantity => $send{$_} } } keys %send;
$have_resources->{$_} -= $send{$_} for keys %send;
my $result;
eval {
$result = $client->trade_push($trade_ministry, $to_id, \@items, { ship_id => $ship->{'id'} });
} or die "error: $@\n";
if ($result && ! $opt{'quiet'}) {
$result = encode_json($result->{ship} || $result);
if ($opt{'log'}) {
open STDOUT, '>>', $opt{'log'} or die "Couldn't open logfile: $opt{'log'}\n";
}
print $result, "\n";
}
}