2 # This is part of ypp-sc-tools, a set of third-party tools for assisting
3 # players of Yohoho Puzzle Pirates.
5 # Copyright (C) 2009 Ian Jackson <ijackson@chiark.greenend.org.uk>
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 # Yohoho and Puzzle Pirates are probably trademarks of Three Rings and
21 # are used without permission. This program is not endorsed or
22 # sponsored by Three Rings.
30 .../ypp-restock-rum [<information> ...]
32 where <information> is
34 have [<swill> <grog>] <fine> [<hold_shot>+<loaded_shot>]
35 want [<swill> <grog>] <fine> [<shot>]
36 price <swill> <grog> <fine> [<shot>]
38 Each of which may appear only once, except \`have' which may appear
39 more than once which case we calculate the financial implications
42 Missing entries count as zero (and zeroes are not displayed in the output).
43 In price, \`x' means the commodity is unavailable.
45 The intended use is that at the start of the pillage you run:
46 ypp-restock-rum have ... want ...
47 to check that stocks are sufficient, or perhaps:
48 ypp-restock-rum have ... want ... price ...
49 if you want to stock up some more. At the end of the pillage,
50 run the same thing again with some extra parameters:
51 ypp-restock-rum have .. want ... have ... price ...
52 and it will tell you how you did and what the restock should be like.
56 our (@kinds) = qw(Swill Grog Fine Shot);
57 our (@proofs) = qw(40 60 100);
59 sub parse_info ($$$\@$) {
60 my ($omitswgok,$default,$multishot, $storeary, $what) = @_;
61 @ARGV or badusage("missing value for information argument \`$_'");
62 badusage("$what: specified more than once")
63 if defined $storeary->[2];
65 while (@ARGV and $ARGV[0] =~ m/^\d|^x$/) {
70 badusage("$what: swill and grog amounts must be specified")
72 @v=($default,$default,@v);
74 if ($multishot and @v==4 and length $v[3]) {
75 $v[3] =~ m/^0*(\d+)\+0*(\d+)$/ or
76 badusage("$what: shot must be specified as <hold>+<loaded>");
83 badusage("$what: invalid syntax (wrong number of parameters)");
87 if ($default>0 and m/^x$/) {
89 } elsif (m/^0*(\d+)$/) {
92 badusage("$what: $kinds[$i] \`$_': bad syntax");
99 our (@have,@want,@price);
103 print STDERR <<END or die $!;
105 ypp-restock-rum is part of ypp-sc-tools Copyright (C) 2009 Ian Jackson
106 This program comes with ABSOLUTELY NO WARRANTY; this is free software,
107 and you are welcome to redistribute it under certain conditions.
108 For details, read the top of the ypp-restock-rum file.
110 badusage("need some information to go on");
115 parse_info(1,0,1, @{ $have[@have] }, 'have');
116 } elsif (m/^want$/) {
117 parse_info(1,0,0, @want, 'want');
118 } elsif (m/^prices?$|^costs?$/) {
119 parse_info(0,1e7,0, @price, 'price');
121 badusage("unknown information argument \`$_'");
128 print STDERR "\nbad usage: $m\n\n$usage\n";
136 defined $x and $x and $x<1e5;
140 my ($what, $format, $ary, $unit) = @_;
141 printf("%-25s", "$what:");
142 for my $i (qw(0 1 2 3)) {
144 my $y= valid($x) ? sprintf $format, $x : ' ';
147 printf " %s\n", $unit;
151 my ($what, $ary, $unit) = @_;
152 prvff($what, '%4d ', @$ary, $unit);
156 my ($what, $ary, $unit) = @_;
157 prvff($what, $ff, @$ary, $unit);
162 printf "%-20s %s\n", "$k:", $v;
164 sub fmt_stock_index ($) {
166 @have==1 ? '' : ' #'.$si;
172 foreach my $i (qw(0 1 2)) {
173 $total += $rums->[$i] * $proofs[$i] / 100;
179 our ($best, $best_norm_price);
181 sub print_inputs () {
183 map { printf " %5s ", $_ } @kinds;
185 pr('prices', @price, 'poe ea.') if valid(@price);
186 pr('target stocks', @want, 'units') if valid(@want);
187 my $si=0; for my $stocks (@have) {
188 pr('actual stocks'.fmt_stock_index(++$si),
194 sub compute_cheapest_rum() {
195 return unless @price;
197 my (@perorder) = map { $_*10 } @price;
198 prf('equiv. ordering price', @perorder, 'poe/order');
201 $best_norm_price= 1e6;
202 for my $i (qw(0 1 2)) {
203 next unless $price[$i];
204 $norm_price[$i] = $price[$i] * 100 / $proofs[$i];
205 if ($norm_price[$i] <= $best_norm_price) {
207 $best_norm_price= $norm_price[$i];
210 prf('normalised prices', @norm_price, 'poe/fine');
213 printf "best is %-10s%*s^^\n",
216 my (@bestperorder) = map {
217 $best_norm_price * $proofs[$_] / 100 * 10;
219 #push @bestperorder, $perorder[3];
220 prf('best is equiv. ordering', @bestperorder, 'poe/order');
234 sub compute_stock_values() {
236 print @have>1 ? <<END
238 _____Rum_____ ___Shot___ total _____Profit___
239 fine equiv. value qty value value per leg total
242 Rum Rum Shot Shot total
243 equiv. value stocks value value
249 my $si=0; for my $stocks (@have) {
250 my $stock_rum = rum_total(@$stocks);
251 my $rum_value= defined($best) ? $stock_rum * $best_norm_price : 0;
252 my $shot_value= valid($price[3]) ? $stocks->[3] * $price[3] : 0;
253 my $total_value= $rum_value + $shot_value;
255 printf "%-10s ", 'stocks'.fmt_stock_index(++$si).':';
256 print ' ' if @have==1;
259 print ' ' if @have==1;
260 printf "%6s", $stocks->[3] ? $stocks->[3] : '';
264 if (defined $last_value) {
265 printf(" %10.1f %10.1f",
266 $total_value - $last_value,
267 $total_value - $initial_value);
269 $initial_value= $total_value unless defined $initial_value;
270 $last_value= $total_value;
273 print "\n" if @have>1;
278 my ($k,$v1,$v2) = @_;
279 printf "%-25s %-23s %s\n", "$k:", $v1, $v2;
283 my ($k,$rum,$shot) = @_;
285 valid($rum) ? sprintf("%12.1f fine equiv", $rum) : '',
286 valid($shot) ? sprintf("%10d shot", $shot) : '');
289 sub compute_restock_requirements () {
292 my $rum_want= rum_total(@want);
294 my $stocks= @have ? $have[-1] : [qw(0 0 0 0)];
295 my $rum_have= rum_total(@$stocks);
297 pr2rs('desired stock level', $rum_want, $want[3]);
299 my $rum_need = $rum_want - $rum_have;
300 my $shot_need = $want[3] - $stocks->[3];
303 pr2rs('current stock', $rum_have, $stocks->[3]);
304 pr2rs('restock requirement', $rum_need, $shot_need);
309 my ($rum_buy,$shot_buy) = ('','');
310 my ($rum_bill,$shot_bill) = qw(0 0);
314 my $rum_qty= $rum_need * 100 / $proofs[$best];
315 $rum_qty= ceil($rum_qty);
316 $rum_buy= sprintf('%12s %-11s ',
317 "** $rum_qty","$kinds[$best] **");
318 $rum_bill= $rum_qty * $price[$best];
320 $rum_buy= ' (rum unavailable)';
324 if ($shot_need > 0) {
325 if (valid($price[3])) {
326 $shot_buy= sprintf('%7s shot **', "** $shot_need");
327 $shot_bill= $shot_need * $price[3];
329 $shot_buy= ' (shot unavailable)';
333 if (length($rum_buy) or length($shot_buy)) {
335 ? sprintf('for a total of %d poe', $rum_bill + $shot_bill)
337 $rum_bill ? sprintf("%12d poe ", $rum_bill) : '',
338 $shot_bill ? sprintf("%10d poe", $shot_bill) : '');
339 pr2('BUY', $rum_buy, $shot_buy);
341 print "stocks are sufficient.\n";
349 compute_cheapest_rum();
350 compute_stock_values();
351 compute_restock_requirements();
353 STDOUT->error and die $!;
354 close STDOUT or die $!;