chiark / gitweb /
98910ebc63581b6cb00d24fa436c75ffc9631d00
[ypp-sc-tools.db-test.git] / ypp-restock-rum
1 #!/usr/bin/perl -w
2 use strict qw(vars);
3 use IO::Handle;
4 use POSIX;
5
6 my $usage = <<END
7 usage:
8   .../ypp-restock-rum [<information> ...]
9
10 where <information> is
11
12   have  [<swill> <grog>] <fine> [<hold_shot>+<loaded_shot>]
13   want  [<swill> <grog>] <fine> [<shot>]
14   price  <swill> <grog>  <fine> [<shot>]
15
16 Each of which may appear only once, except \`have' which may appear
17 more than once which case we calculate the differences and the profit
18 for each one.
19
20 If /<shot> is not specified at all, relevant information about shot is
21 not reported.  For have and want, specifying an empty string means
22 zero.  Entirely Missing <swill>/<grog>/ is taken as if both were
23 specified and zero.
24
25 In price, missing entries mean the commodity is unavailable.
26 END
27 ;
28
29 our (@kinds) = qw(swill grog fine shot);
30 our (@proofs) = qw(40 60 100);
31
32 sub parse_info ($$$\@$) {
33     my ($omitswgok,$default,$multishot, $storeary, $what) = @_;
34     @ARGV or badusage("missing value for information argument \`$_'");
35     badusage("$what: specified more than once")
36         if defined $storeary->[2];
37     my (@v) = ();
38     while (@ARGV and $ARGV[0] =~ m/^\d/) {
39         $_ = shift @ARGV;
40         push @v, $_;
41     }
42     if (@v==1 or @v==2) {
43         badusage("$what: swill and grog amounts must be specified")
44             unless $omitswgok;
45         @v=($default,$default,@v);
46     }
47     if ($multishot and @v==4 and length $v[3]) {
48         $v[3] =~ m/^0*(\d+)\+0*(\d+)$/ or
49             badusage("$what: shot must be specified as <hold>+<loaded>");
50         $v[3] = $1 + $2;
51     }
52     if (@v==3) {
53         push @v, $default;
54     }
55     if (@v != 4) {
56         badusage("$what: invalid syntax (wrong number of /s)");
57     }
58     my $i=0;
59     foreach $_ (@v) {
60         $_ = $default if !length;
61         m/^0*(\d+)$/ or badusage("$what: $kinds[$i] \`$_': bad syntax");
62         $_= $1;
63         $i++;
64     }
65     @$storeary = @v;
66 }
67
68 our (@haves,@want,@price);
69
70 sub parse_args () {
71     @ARGV or badusage("need some information to go on");
72     while (@ARGV) {
73         $_ = shift @ARGV;
74         if (m/^have$/) {
75             parse_info(1,0,1, @{ $haves[@haves] }, 'BUG');
76         } elsif (m/^want$/) {
77             parse_info(1,0,0, @want, 'want');
78         } elsif (m/^price$/) {
79             parse_info(0,1e6,0, @price, 'price');
80         } else {
81             badusage("unknown information argument \`$_'");
82         }
83     }
84 }
85
86 sub badusage ($) {
87     my ($m) = @_;
88     print STDERR "bad usage: $m\n\n$usage\n";
89     exit 16;
90 }
91
92 our $ff = '%6.1f';
93
94 sub prvff ($$\@$) {
95     my ($what, $format, $ary, $unit) = @_;
96     printf("%-40s", "$what:");
97     for my $i (qw(0 1 2)) {
98         my $x= $ary->[$i];
99         my $y= $x>0 && $x<1e5 ? sprintf $format, $x : '   x';
100         printf " %-10s", $y;
101     }
102     printf "  %s\n", $unit;
103 }
104
105 sub pr ($\@$) {
106     my ($what, $ary, $unit) = @_;
107     prvff($what, '%4d  ', @$ary, $unit);
108 }
109
110 sub prf ($\@$) {
111     my ($what, $ary, $unit) = @_;
112     prvff($what, $ff, @$ary, $unit);
113 }
114
115 sub pr1 ($$) {
116     my ($k,$v) = @_;
117     printf "%-20s %s\n", "$k:", $v;
118 }
119
120
121 our @norm_price;
122 our $best;
123
124 sub compute_cheapest_rum() {
125     return unless @price;
126
127     $best= undef;
128     my $best_norm_price= 1e5;
129     for my $i (qw(0 1 2)) {
130         next unless $price[$i];
131         $norm_price[$i] = $price[$i] * $proofs[$i] / $proofs[$i];
132         if ($norm_price[$i] <= $best_norm_price) {
133             $best= $i;
134         }
135     };
136     prf('normalised prices', @norm_price, 'poe/fine');
137     if (defined $best) {
138         printf "best is %s\n\n", $kinds[$best];
139     } else {
140         die "no rum available ?\n";
141     }
142 }
143
144 parse_args();
145 use Data::Dumper;
146 print Dumper({ Have => \@haves, Want => \@want, Price => \@price });
147 compute_cheapest_rum();
148
149 __DATA__
150
151 our ($best);
152
153 our $have_proof;
154
155 sub print_inputs () {
156     printf("%40s",'');
157     map { printf " %5s     ", $_ } @rums;
158     print "\n\n";
159     if (defined $price{Swill}) {
160         map { $price{$_}= undef if $price{$_} eq 'x' } @rums;
161         pr('prices', %price, 'poe ea.');
162     }
163     if (defined $have{Swill}) {
164         $have_proof= 0;
165         map { $have_proof += $have{$_} * $proof{$_} } @rums;
166         pr('stock on board', %have, 'rum');
167     }
168 }
169
170 our ($need_proof, %need, %buy);
171
172 sub compute_restock_requirements () {
173     if ($ship =~ m/^\d+/) {
174         $need{Fine} = $ship;
175     } else {
176         $ship =~ y/_/ /;
177         open F, "/home/ian/private/puzzle-pirates" or die $!;
178         my $this_ship= 0;
179         my $the_ship;
180         while (<F>) {
181             if (!m/\S/ || m/^\s*\#/) {
182                 $this_ship= 0;
183                 next;
184             }
185             if (!m/^\@/) {
186                 next;
187             }
188             if (m/^\@(( [A-Z][-a-z]+){2,})\s*$/) {
189                 $this_ship= (uc $1 eq uc " $ship" or
190                              uc $+ eq uc " $ship");
191                 $the_ship= $1;
192                 next;
193             }
194             next unless $this_ship;
195             if (m/^\@\s+(\d+)\s+fine\s*/) {
196                 $need{Fine} = $1;
197                 last;
198             }
199         }
200         die $! if F->error;
201         die "unknown ship $ship" unless defined $need{Fine};
202         if (defined $ship) {
203             pr1("vessel",$the_ship);
204         }
205     }
206     pr1('desired stock level', sprintf("%4d fine rum", $need{Fine}));
207     $need_proof= $need{Fine} * $proof{Fine} - $have_proof;
208     map {
209         $buy{$_} = $need_proof / $proof{$_};
210     } @rums;
211     pr1("stock equivalent", sprintf "$ff fine rum", $have_proof / $proof{Fine});
212     pr1("restock equivalent", sprintf "$ff fine rum", $need_proof / $proof{Fine});
213     prf('would need', %buy, 'rum');
214 }
215
216 sub compute_restock_cheapest_rum() {
217     my %bill;
218     map {
219         $bill{$_} = $buy{$_} * $price{$_} if defined $price{$_};
220     } @rums;
221     prf('nominal bill', %bill, 'poe');
222     print "\n";
223     if ($need_proof < 0) {
224         printf "stocks are sufficient";
225     } else {
226         my $buy= ceil($buy{$best});
227         printf "buy %d %s at %d poe each for %d poe",
228             $buy, $best, $price{$best}, $buy * $price{$best};
229     }
230     print "\n\n";
231 }
232
233 main();