#!/usr/bin/perl -w use strict qw(vars); use IO::Handle; use POSIX; my $usage = < ...] where is have [ ] [+] want [ ] [] price [] 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 is not specified at all, relevant information about shot is not reported. For have and want, missing entries count as zero. In price, \`x' means the commodity is unavailable. END ; 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|^x$/) { $_ = 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 +"); $v[3] = $1 + $2; } if (@v==3) { push @v, $default; } if (@v != 4) { badusage("$what: invalid syntax (wrong number of parameters)"); } my $i=0; foreach $_ (@v) { if ($default>0 and m/^x$/) { $_ = $default; } elsif (m/^0*(\d+)$/) { $_= $1; } else { badusage("$what: $kinds[$i] \`$_': bad syntax"); } $i++; } @$storeary = @v; } 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,1e7,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 valid ($) { my ($x) = @_; defined $x and $x and $x<1e5; } sub prvff ($$\@$) { my ($what, $format, $ary, $unit) = @_; 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 ($\@$) { my ($what, $ary, $unit) = @_; prvff($what, '%4d ', @$ary, $unit); } sub prf ($\@$) { my ($what, $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; } sub rum_total (\@) { my ($rums) = @_; my $total= 0; foreach my $i (qw(0 1 2)) { $total += $rums->[$i] * $proofs[$i] / 100; } return $total; } 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() { return unless @price; my (@perorder) = map { $_*10 } @price; prf('equiv. ordering price', @perorder, 'poe/order'); $best= undef; $best_norm_price= 1e6; 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) { 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 (valid($x)) { printf ' %9.1f', $x; } else { printf " "; } } sub compute_stock_values() { return unless @have; print @have>1 ? <[3] * $price[3] : 0; my $total_value= $rum_value + $shot_value; printf "%-10s ", 'stocks'.fmt_stock_index(++$si).':'; print ' ' if @have==1; pr1s($stock_rum); pr1s($rum_value); print ' ' if @have==1; printf "%6s", $stocks->[3] ? $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 "\n" if @have>1; print "\n"; } sub pr2 ($$$) { my ($k,$v1,$v2) = @_; printf "%-25s %-23s %s\n", "$k:", $v1, $v2; } sub pr2rs ($$$) { my ($k,$rum,$shot) = @_; pr2($k, valid($rum) ? sprintf("%12.1f fine equiv", $rum) : '', valid($shot) ? sprintf("%10d shot", $shot) : ''); } sub compute_restock_requirements () { return unless @want; my $rum_want= rum_total(@want); my $stocks= @have ? $have[-1] : [qw(0 0 0 0)]; my $rum_have= rum_total(@$stocks); pr2rs('desired stock level', $rum_want, $want[3]); my $rum_need = $rum_want - $rum_have; my $shot_need = $want[3] - $stocks->[3]; if (@have) { pr2rs('current stock', $rum_have, $stocks->[3]); pr2rs('restock requirement', $rum_need, $shot_need); print "\n"; } if (@price) { my ($rum_buy,$shot_buy) = ('',''); my ($rum_bill,$shot_bill) = qw(0 0); my $ok=1; if ($rum_need > 0) { if (defined $best) { my $rum_qty= $rum_need * 100 / $proofs[$best]; $rum_qty= ceil($rum_qty); $rum_buy= sprintf('%12s %-11s ', "** $rum_qty","$kinds[$best] **"); $rum_bill= $rum_qty * $price[$best]; } else { $rum_buy= ' (rum unavailable)'; $ok= 0; } } if ($shot_need > 0) { if (valid($price[3])) { $shot_buy= sprintf('%7s shot **', "** $shot_need"); $shot_bill= $shot_need * $price[3]; } else { $shot_buy= ' (shot unavailable)'; $ok= 0; } } if (length($rum_buy) or length($shot_buy)) { pr2($ok ? sprintf('for a total of %d poe', $rum_bill + $shot_bill) : 'for', $rum_bill ? sprintf("%12d poe ", $rum_bill) : '', $shot_bill ? sprintf("%10d poe", $shot_bill) : ''); pr2('BUY', $rum_buy, $shot_buy); } else { print "stocks are sufficient.\n"; } print "\n"; } } parse_args(); print_inputs(); compute_cheapest_rum(); compute_stock_values(); compute_restock_requirements(); STDOUT->error and die $!; close STDOUT or die $!;