#!/usr/bin/perl -w
-use strict qw (vars);
+use strict qw(vars);
use IO::Handle;
use POSIX;
-our ($ship, %have, %price);
-
my $usage = <<END
usage:
- .../ypp-restock-rum [<prices>] [<ship> <stocks>]
-where
- <prices> is three arguments: the prices for swill, grog and fine rum
- <ship> is a ship name or an amount of fine rum
- <stocks> are amounts on board for swill, grog and fine rum
+ .../ypp-restock-rum [<information> ...]
+
+where <information> is
+
+ have [<swill> <grog>] <fine> [<hold_shot>+<loaded_shot>]
+ want [<swill> <grog>] <fine> [<shot>]
+ price <swill> <grog> <fine> [<shot>]
+
+Each of which may appear only once, except \`have' which may appear
+more than once which case we calculate the differences and the profit
+for each one.
+
+If /<shot> is not specified at all, relevant information about shot is
+not reported. For have and want, specifying an empty string means
+zero. Entirely Missing <swill>/<grog>/ is taken as if both were
+specified and zero.
+
+In price, missing entries mean the commodity is unavailable.
END
;
-sub main () {
- if (@ARGV==4) {
- ($ship, $have{Swill}, $have{Grog}, $have{Fine}) = @ARGV;
- print_inputs();
- compute_restock_requirements();
- } elsif (@ARGV==3) {
- ($price{Swill}, $price{Grog}, $price{Fine}) = @ARGV;
- print_inputs();
- compute_cheapest_rum();
- } elsif (@ARGV==7) {
- ($price{Swill}, $price{Grog}, $price{Fine},
- $ship,
- $have{Swill}, $have{Grog}, $have{Fine}) = @ARGV;
- print_inputs();
- compute_cheapest_rum();
- compute_restock_requirements();
- compute_restock_cheapest_rum();
- } else {
- die $usage;
+our (@kinds) = qw(Swill Grog Fine Shot);
+our (@proofs) = qw(40 60 100);
+
+sub parse_info ($$$\@$) {
+ my ($omitswgok,$default,$multishot, $storeary, $what) = @_;
+ @ARGV or badusage("missing value for information argument \`$_'");
+ badusage("$what: specified more than once")
+ if defined $storeary->[2];
+ my (@v) = ();
+ while (@ARGV and $ARGV[0] =~ m/^\d/) {
+ $_ = shift @ARGV;
+ push @v, $_;
}
+ if (@v==1 or @v==2) {
+ badusage("$what: swill and grog amounts must be specified")
+ unless $omitswgok;
+ @v=($default,$default,@v);
+ }
+ if ($multishot and @v==4 and length $v[3]) {
+ $v[3] =~ m/^0*(\d+)\+0*(\d+)$/ or
+ badusage("$what: shot must be specified as <hold>+<loaded>");
+ $v[3] = $1 + $2;
+ }
+ if (@v==3) {
+ push @v, $default;
+ }
+ if (@v != 4) {
+ badusage("$what: invalid syntax (wrong number of /s)");
+ }
+ my $i=0;
+ foreach $_ (@v) {
+ $_ = $default if !length;
+ m/^0*(\d+)$/ or badusage("$what: $kinds[$i] \`$_': bad syntax");
+ $_= $1;
+ $i++;
+ }
+ @$storeary = @v;
}
-our @rums= qw(Swill Grog Fine);
-our %proof= qw(Swill 40
- Grog 60
- Fine 100);
+our (@have,@want,@price);
+
+sub parse_args () {
+ @ARGV or badusage("need some information to go on");
+ while (@ARGV) {
+ $_ = shift @ARGV;
+ if (m/^have$/) {
+ parse_info(1,0,1, @{ $have[@have] }, 'have');
+ } elsif (m/^want$/) {
+ parse_info(1,0,0, @want, 'want');
+ } elsif (m/^price$/) {
+ parse_info(0,1e6,0, @price, 'price');
+ } else {
+ badusage("unknown information argument \`$_'");
+ }
+ }
+}
+
+sub badusage ($) {
+ my ($m) = @_;
+ print STDERR "\nbad usage: $m\n\n$usage\n";
+ exit 16;
+}
our $ff = '%6.1f';
-sub prvff ($$\%$) {
+sub valid ($) {
+ my ($x) = @_;
+ defined $x and $x>0 and $x<1e4;
+}
+
+sub prvff ($$\@$) {
my ($what, $format, $ary, $unit) = @_;
- printf("%-40s", "$what:");
- map {
- my $x= $ary->{$_};
- my $y= defined $x ? sprintf $format, $x : ' x';
- printf " %-10s", $y;
- } @rums;
+ printf("%-25s", "$what:");
+ for my $i (qw(0 1 2 3)) {
+ my $x= $ary->[$i];
+ my $y= valid($x) ? sprintf $format, $x : ' ';
+ printf " %-9s", $y;
+ }
printf " %s\n", $unit;
}
-sub pr ($\%$) {
+sub pr ($\@$) {
my ($what, $ary, $unit) = @_;
- prvff($what, '%4d ', %$ary, $unit);
+ prvff($what, '%4d ', @$ary, $unit);
}
-sub prf ($\%$) {
+sub prf ($\@$) {
my ($what, $ary, $unit) = @_;
- prvff($what, $ff, %$ary, $unit);
+ prvff($what, $ff, @$ary, $unit);
}
sub pr1 ($$) {
my ($k,$v) = @_;
printf "%-20s %s\n", "$k:", $v;
}
+sub fmt_stock_index ($) {
+ my ($si) = @_;
+ @have==1 ? '' : ' #'.$si;
+}
-our ($best_kind);
+our @norm_price;
+our ($best, $best_norm_price);
+
+sub print_inputs () {
+ printf("%25s",'');
+ map { printf " %5s ", $_ } @kinds;
+ print "\n\n";
+ pr('prices', @price, 'poe ea.') if valid(@price);
+ pr('target stocks', @want, 'units') if valid(@want);
+ my $si=0; for my $stocks (@have) {
+ pr('actual stocks'.fmt_stock_index(++$si),
+ @$stocks, 'units');
+ }
+ print "\n";
+}
sub compute_cheapest_rum() {
- $best_kind= undef;
- my %norm_price;
- my $best_norm_price= 1e6;
- foreach $_ (@rums) {
- next unless defined $price{$_};
- $norm_price{$_} = $price{$_} * $proof{Fine} / $proof{$_};
- if ($norm_price{$_} <= $best_norm_price) {
- $best_kind= $_;
- $best_norm_price= $norm_price{$_};
+ return unless @price;
+
+ my (@perorder) = map { $_*10 } @price;
+ prf('equiv. ordering price', @perorder, 'poe/order');
+
+ $best= undef;
+ $best_norm_price= 1e5;
+ for my $i (qw(0 1 2)) {
+ next unless $price[$i];
+ $norm_price[$i] = $price[$i] * 100 / $proofs[$i];
+ if ($norm_price[$i] <= $best_norm_price) {
+ $best= $i;
+ $best_norm_price= $norm_price[$i];
}
};
- prf('normalised prices', %norm_price, 'poe/fine');
- if (defined $best_kind) {
- printf "best is %s\n\n", $best_kind;
+ prf('normalised prices', @norm_price, 'poe/fine');
+
+ if (defined $best) {
+ printf "best is %-10s%*s^^\n",
+ $kinds[$best],
+ $best*10+10, '';
+ my (@bestperorder) = map {
+ $best_norm_price * $proofs[$_] / 100 * 10;
+ } qw(0 1 2);
+ #push @bestperorder, $perorder[3];
+ prf('best is equiv. ordering', @bestperorder, 'poe/order');
+ }
+ print "\n";
+}
+
+sub pr1s ($) {
+ my ($x) = @_;
+ if (defined $x and $x) {
+ printf ' %9.1f', $x;
} else {
- die "no rum available ?\n";
+ printf " ";
}
}
-our $have_proof;
+sub compute_stock_values() {
+ return unless @have;
+ print <<END
+ Rum Rum Shot Shot total Profit Profit
+ equiv. value stocks value value this leg total
+END
+;
-sub print_inputs () {
- printf("%40s",'');
- map { printf " %5s ", $_ } @rums;
- print "\n\n";
- if (defined $price{Swill}) {
- map { $price{$_}= undef if $price{$_} eq 'x' } @rums;
- pr('prices', %price, 'poe ea.');
- }
- if (defined $have{Swill}) {
- $have_proof= 0;
- map { $have_proof += $have{$_} * $proof{$_} } @rums;
- pr('stock on board', %have, 'rum');
+ my $initial_value;
+ my $last_value;
+ my $si=0; for my $stocks (@have) {
+ my $stock_rum= 0;
+ foreach my $i (qw(0 1 2)) {
+ $stock_rum += $stocks->[$i] * $proofs[$i] / 100;
+ }
+ my $rum_value= defined($best) ? $stock_rum * $best_norm_price : 0;
+ my $shot_value= valid($price[3]) ? $stocks->[3] * $price[3] : 0;
+ my $total_value= $rum_value + $shot_value;
+
+ printf "%-10s ", 'stocks'.fmt_stock_index(++$si).':';
+ pr1s($stock_rum);
+ pr1s($rum_value);
+ printf "%6d", $stocks->[3];
+ pr1s($shot_value);
+ pr1s($total_value);
+
+ if (defined $last_value) {
+ printf(" %10.1f %10.1f",
+ $total_value - $last_value,
+ $total_value - $initial_value);
+ }
+ $initial_value= $total_value unless defined $initial_value;
+ $last_value= $total_value;
+ print "\n";
}
+ print <<END
+ fine poe units poe poe delta-poe poe
+
+END
+;
}
+parse_args();
+print_inputs();
+compute_cheapest_rum();
+compute_stock_values();
+
+__DATA__
+
+# if (defined $price{Swill}) {
+# map { $price{$_}= undef if $price{$_} eq 'x' } @rums;
+# }
+# if (defined $have{Swill}) {
+# $have_proof= 0;
+# map { $have_proof += $have{$_} * $proof{$_} } @rums;
+# }
+
+our ($best);
+
+our $have_proof;
+
our ($need_proof, %need, %buy);
sub compute_restock_requirements () {
if ($ship =~ m/^\d+/) {
$need{Fine} = $ship;
} else {
- $ship =~ y/_/ /;
- open F, "/home/ian/private/puzzle-pirates" or die $!;
- my $this_ship= 0;
- my $the_ship;
- while (<F>) {
- if (!m/\S/ || m/^\s*\#/) {
- $this_ship= 0;
- next;
- }
- if (!m/^\@/) {
- next;
- }
- if (m/^\@(( [A-Z][-a-z]+){2,})\s*$/) {
- $this_ship= (uc $1 eq uc " $ship" or
- uc $+ eq uc " $ship");
- $the_ship= $1;
- next;
- }
- next unless $this_ship;
- if (m/^\@\s+(\d+)\s+fine\s*/) {
- $need{Fine} = $1;
- last;
- }
- }
- die $! if F->error;
- die "unknown ship $ship" unless defined $need{Fine};
- if (defined $ship) {
- pr1("vessel",$the_ship);
- }
}
+
pr1('desired stock level', sprintf("%4d fine rum", $need{Fine}));
$need_proof= $need{Fine} * $proof{Fine} - $have_proof;
map {
if ($need_proof < 0) {
printf "stocks are sufficient";
} else {
- my $buy= ceil($buy{$best_kind});
+ my $buy= ceil($buy{$best});
printf "buy %d %s at %d poe each for %d poe",
- $buy, $best_kind, $price{$best_kind}, $buy * $price{$best_kind};
+ $buy, $best, $price{$best}, $buy * $price{$best};
}
print "\n\n";
}