chiark / gitweb /
Merge ../ypp-sc-tools.pctb-dict-test
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Wed, 9 Sep 2009 13:23:49 +0000 (14:23 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Wed, 9 Sep 2009 13:23:49 +0000 (14:23 +0100)
13 files changed:
yarrg/TODO
yarrg/ocean-topology-graph [new file with mode: 0755]
yarrg/source-info.txt
yarrg/web/check_capacitystring [new file with mode: 0644]
yarrg/web/check_lossperleague [new file with mode: 0644]
yarrg/web/docs
yarrg/web/footer
yarrg/web/intro [new file with mode: 0755]
yarrg/web/lookup
yarrg/web/qtextstring
yarrg/web/qtextstringcheck
yarrg/web/query_route
yarrg/web/routetrade

index a2e2f34d1af632b2fb186b8fefb2dbcfce4e1136..6b227ebe739dae3f4283f233c218c5fbead918c6 100644 (file)
@@ -1,6 +1,8 @@
 UPLOADER
 --------
 
+       sometimes fails to work on Sage - sunshine widget resets or something
+
        detect all unexpected mouse movements
 
        more flexible installation arrangements
diff --git a/yarrg/ocean-topology-graph b/yarrg/ocean-topology-graph
new file mode 100755 (executable)
index 0000000..55d15d9
--- /dev/null
@@ -0,0 +1,37 @@
+#!/usr/bin/perl -w
+
+use strict (qw(vars));
+
+use CommodsDatabase;
+
+my $ocean= shift @ARGV;
+
+db_setocean($ocean);
+db_connect();
+my $islands= $dbh->selectall_arrayref('
+       SELECT islandid,islandname FROM islands;
+');
+my $routes= $dbh->selectall_arrayref('
+       SELECT aiid, biid, dist FROM routes;
+');
+$dbh->disconnect();
+
+#use Data::Dumper;
+#print Dumper($results);
+
+print "strict graph $ocean {\n";
+#print "    nodesep=10;\n";
+
+foreach my $row (@$islands) {
+    my ($id,$str) = @$row;
+    $str =~ s/[\"\\]/\\$&/g;
+    print "    n$id [ label=\"$str\" ];\n";
+}
+foreach my $row (@$routes) {
+    my ($ia,$ib,$dist) = @$row;
+    print "    n$ia -- n$ib [ len=2, label=$dist ];\n";
+    #len=$dist, minlen=$dist, weight=".(1.0/$dist).", len=".($dist*0.25+1).",
+    #w=".(1.0/$dist).", 
+}
+
+print "}\n";
index c0e1deb851db5d4e4947bba021094f331d3f46d1..b7d285b620601fbe29ce39fb5416287a3cb9cc4b 100644 (file)
@@ -145,6 +145,7 @@ ocean Ice
 ocean Hunter
  Eagle
   Ix Chel
+  Manu Island
 
 ocean Malachite
  Draco
diff --git a/yarrg/web/check_capacitystring b/yarrg/web/check_capacitystring
new file mode 100644 (file)
index 0000000..3d8f7a5
--- /dev/null
@@ -0,0 +1,88 @@
+<%doc>
+
+ This is part of the YARRG website.  YARRG is a tool and website
+ for assisting players of Yohoho Puzzle Pirates.
+
+ Copyright (C) 2009 Ian Jackson <ijackson@chiark.greenend.org.uk>
+ Copyright (C) 2009 Clare Boothby
+
+  YARRG's client code etc. is covered by the ordinary GNU GPL (v3 or later).
+  The YARRG website is covered by the GNU Affero GPL v3 or later, which
+   basically means that every installation of the website will let you
+   download the source.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+ Yohoho and Puzzle Pirates are probably trademarks of Three Rings and
+ are used without permission.  This program is not endorsed or
+ sponsored by Three Rings.
+
+
+ This Mason component simply defines how to interpret capacities.
+
+</%doc>
+
+<%attr>
+</%attr>
+
+<%method preparse>
+<%args>
+$h
+</%args>
+<%perl>
+
+my $def= sub {
+       my ($what,$val) = @_;
+       if (defined $h->{$what}) {
+               $h->{Emsg}= "Multiple definitions of maximum $what.";
+       }
+       print STDERR "SET $what $val\n";
+       $h->{$what}= $val;
+};
+
+foreach $_ (split /\s+/, ${ $h->{String} }) {
+       print STDERR "ITEM \`$_'\n";
+       next unless length;
+       if (m/^([1-9]\d{0,8})l$/) {
+               $def->('volume', $1);
+       } elsif (m/^([1-9]\d{0,8})kg$/) {
+               $def->('mass', $1);
+       } elsif (m/^([1-9]\d{0,5}(?:\.\d{0,3})?)kl/) {
+               $def->('volume', $1 * 1000);
+       } elsif (m/^([1-9]\d{0,5}(?:\.\d{0,3})?)t/) {
+               $def->('mass', $1 * 1000);
+       } else {
+               ${ $h->{Emsg} }= "Cannot understand capacity \`$_'.";
+               last;
+       }
+}
+</%perl>
+</%method>
+
+<%method postquery>
+<%args>
+$h
+</%args>
+<%perl>
+
+if (defined $h->{'mass'} or defined $h->{'volume'}) {
+       @{ $h->{Results} } = [ $h->{'mass'}, $h->{'volume'} ];
+
+       ${ $h->{Canon} }=
+ 'mass limit: '.(defined $h->{'mass'} ? $h->{'mass'} .'kg' : 'none').'; '.
+ 'volume limit: '.(defined $h->{'volume'} ? $h->{'volume'} .'l' : 'none').'.';
+}
+
+</%perl>
+</%method>
diff --git a/yarrg/web/check_lossperleague b/yarrg/web/check_lossperleague
new file mode 100644 (file)
index 0000000..5994f6f
--- /dev/null
@@ -0,0 +1,65 @@
+<%doc>
+
+ This is part of the YARRG website.  YARRG is a tool and website
+ for assisting players of Yohoho Puzzle Pirates.
+
+ Copyright (C) 2009 Ian Jackson <ijackson@chiark.greenend.org.uk>
+ Copyright (C) 2009 Clare Boothby
+
+  YARRG's client code etc. is covered by the ordinary GNU GPL (v3 or later).
+  The YARRG website is covered by the GNU Affero GPL v3 or later, which
+   basically means that every installation of the website will let you
+   download the source.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+ Yohoho and Puzzle Pirates are probably trademarks of Three Rings and
+ are used without permission.  This program is not endorsed or
+ sponsored by Three Rings.
+
+
+ This Mason component simply defines how to interpret capacities.
+
+</%doc>
+
+<%attr>
+</%attr>
+
+<%method preparse>
+<%args>
+$h
+</%args>
+<%perl>
+
+$_= ${ $h->{String} };
+s/^\s+//; s/\s+$//;
+
+my $res= sub {
+       my ($pct,$str) = @_;
+       push @{ $h->{Results} }, [ $pct ];
+       ${ $h->{Canon} }= "$str per league";
+};
+
+if (!m/\S/) {
+} elsif (m/^(\d{1,2}(?:\.\d{0,5})?)\%$/) {
+       $res->( $1 * 1.0, sprintf("%g%%", $1) );
+} elsif (m/^1\s*\/\s*([1-9]\d{0,4})/) {
+       $res->( 100.0/$1, sprintf("1/%d", $1) );
+} else {
+       ${ $h->{Emsg} }= "Cannot understand loss per league \`$_'.";
+       return;
+}
+
+</%perl>
+</%method>
index 0ea31cfcc35a9cb8a79facad29dfee2fb17a04bf..1c1ce69805fcaddcb15099b842b47746b656f585 100755 (executable)
 
 
 </%doc>
-<html><head><title>YARRG (Yet Another Revenue Research Gatherer)</title>
+<html><head><title>Website documentation - YARRG</title>
 </head><body>
 
-<h1>Introduction to YARRG</h1>
+<a href="lookup">YARRG</a> -
+ Yet Another Revenue Research Gatherer
+|
+<a href="intro">introduction</a>
+|
+<b>documentation</b>
+
+<h1>Looking up data in YARRG</h1>
 
 YARRG (Yet Another Revenue Research Gatherer) is a third-party tool
 for helping find profitable trades and trade routes in Yohoho Puzzle
-Pirates.  It was inspired by
-<a href="http://pctb.crabdance.com/">PCTB</a>.
+Pirates.  See the <a href="intro">Introduction</a> for more details.
 
 <p>
 
-The system has two main parts: this website which maintains a
-searchable database of commodity prices, and an upload client, which
-screenscrapes the commodity data from the Puzzle Pirates game client
-and uploads it to the database.
+The <a href="lookup">Market prices database</a> is the main output
+from YARRG.  It offers a variety of enquiry options.
 
-<h2><a href="lookup">Market prices database</a></h2>
+<p>
 
-The <a href="lookup">lookup page</a> gives access to the uploaded data.
+This website documentation page contains information about the
+database website which you may not be able to divine from the online
+user interface.
 
-<h2>Uploading from Linux</h2>
+<h2>Bookmarkable URLs</h2>
 
-The YARRG upload client uploads both to YARRG and to the
-<a href="pctb.ilk.org">PCTB testing server</a>.
+Mostly, you can bookmark the specific pages and queries.  Select the
+ocean, query page, and other combinations of options, as you wish, and
+perhaps fill in the actual data fields too, and bookmark the resulting
+URL.
 
 <p>
 
-The current official version of YARRG for use as an upload
-client can browsed here:
- <a href="/~ijackson/ypp-sc-tools/master/">http://www.chiark.greenend.org.uk/~ijackson/ypp-sc-tools/master/</a>
-See particularly the
-<a href="/~ijackson/ypp-sc-tools/master/yarrg/README">YARRG README</a>.
+(An exception to this is if you select the `Update' option from the
+`Trades for route' lookup; the list of (de)selected stalls is too long
+to fit in a URL.)
+
+<h2>Dynamic confirmation of meaning of text entry boxes</h2>
+
+If you have Javascript enabled, the various text entry boxes will be
+annotated with a brief explanation of the system's interpretation of
+your current entry string.  To get the actual results updated, you
+must still hit `Go' or `Update'.
+
+<h2>Trades for route</h2>
+
+Given a list of islands (or archipelagoes), provides a list of
+potentially profitable trades.  If the route is suitable for the trade
+route optimiser, it will generate a complete voyage plan, telling you
+which goods to buy and sell where at which stalls and prices.
 
 <p>
-To install the client, install the `git' version control system
-and the other dependencies listed in the `Installation requirements'
-section of the README and then run:
-<pre>
-git-clone http://www.chiark.greenend.org.uk/~ijackson/ypp-sc-tools/master/.git ypp-sc-tools
-cd ypp-sc-tools
-cd yarrg
-make
-</pre>
-this will download the code into the directory <code>ypp-sc-tools</code>,
-and build the software.
+
+If you specify only one island or one archipelago, the site shows only
+arbitrage trades.  If you want single-hop trades within an
+archipelago, you must enter the archipelago name twice.
 
 <p>
 
-When new versions of the upload client are released, you can:
-<pre>
-cd .../ypp-sc-tools
-git-pull
-cd yarrg
-make
-</pre>
-to fetch the new version.
+After getting the results, you can untick various trades individually,
+and select `Update' to get a new plan.  The unticked trades will be
+excluded from the voyage plan (if any) and also from the totals.
 
-<h2>Uploading from Windows</h2>
+<h3>Vessel capacity</h3>
 
-There is not currently an upload client for Windows which feeds data
-into YARRG.  It would probably be straightforward to modify the
-Windows PCTB v5 upload client to upload to YARRG as well.  The
-mechanism and format for uploading is documented in
-<a href="/~ijackson/ypp-sc-tools/master/yarrg/README.devel">README.devel</a>.
+If you don't specify a vessel or a vessel capacity, the trading plan
+will not take into account the fact that your voyage will be on a ship
+with a limited size.  This will probably result in a plan
+which trades excessively cumbersome goods (eg. hemp, wood, iron).
 
-<h1>YARRG development, contribution and troubleshooting</h1>
+<p>
 
-<h2>Free Software (aka Open Source)</h2>
+So you should specify your vessel capacity.  Currently you must
+specify the actual mass and volume, as two numbers each with units.
+The system understands the units t (tonnes), kg, l and kl
+(kilolitres).  There should be a space between the two limits, and no
+space before the unit.
 
-YARRG is Free Software - you may share and modify it.  See the
-licences for details.
+<h3>Expected losses</h3>
+
+In theory if you were guaranteed to have a trouble-free voyage it
+would be worth trading goods at very low margins.  However, in
+practice problems can arise: you may be attacked and lose your stock,
+or market conditions may change between your collection and delivery
+of the goods.
 
 <p>
 
-Not only the client but also the webserver code is Free.  The website
-code can be found in the same tree as above, in the <code>web</code>
-directory.
+We model this by pretending that you expect to lose a fixed proportion
+of your stock each league you sail.  This expected loss does not
+appear in the trade tables (although the distance does), but it does
+affect the way the voyage trading plan optimiser chooses which trades
+to do.
 
 <p>
 
-But in case we have made changes but not yet pushed them
-(perhaps because we haven't done a release), and to make it easy for
-anyone else who runs a copy of the website to provide everyone with
-the source for their version, the website code itself lets you download
-an up-to-date <a href="source.tar.gz">tarball</a> of its
-actually-running source code.
+Trades whose margin is less than the expected loss are never selected.
+For example, if you select 1% loss per league, and plan a voyage of 5
+leagues, then any trade with a margin of less than 5.15% would be
+completely excluded (5.15% not 5% because the loss works like compound
+interest).  Theoretically very profitable trades which are close to
+the expected break-even point because of the distance can also be
+rejected by the optimiser in favour of shorter distance trades with
+theoretically smaller margins.
 
 <p>
 
-If you would like to run a (perhaps modified) copy of the YARRG
-website it would be very easy for us to make our system send you
-copies of updates submitted by users of the official YARRG client, in
-the format expected by the code you'll be running.  Please just ask
-us.
+As a guide: you may expect to lose between 0.1% and 1% per league.
+0.1% would correspond, for example, to losing one fight to brigands
+every ten 10-league voyages.
 
-<h2>Contacting the YARRG developers</h2>
+<p>
 
-Email Ian Jackson ijackson (at) chiark.greenend.org.uk.  Or talk to
-any Fleet Officer or above of the crew Special Circumstances on the
-Midnight Ocean.
+You can enter the value in the box either as a percentage, or as a
+fraction 1/<em>divisor</em>, eg 1/200 is the same as 0.5%; in each
+case it is taken as the loss for each league of the voyage.
 
-<& footer, isdocs => 1 &>
+<& footer &>
index 8af45ad7dcaeb530245fc9b081d4c166c3be8995..9c892fc5b00b38ee3d8004f59dd18a5b43a0b8fb 100644 (file)
@@ -48,7 +48,7 @@ You may share and modify the code and the
 website, according to the terms of the GNU General Public Licence and
 the GNU Affero General Public Licence respectively (v3 or later).
 % if (!$isdocs) {
-Please see the <a href="docs">YARRG documentation webpage</a> for
+Please see the <a href="intro">YARRG introduction webpage</a> for
 details of how to obtain the client and server code and full details
 of the licences.
 % }
diff --git a/yarrg/web/intro b/yarrg/web/intro
new file mode 100755 (executable)
index 0000000..3088cce
--- /dev/null
@@ -0,0 +1,147 @@
+<%doc>
+
+ This is part of the YARRG website.  YARRG is a tool and website
+ for assisting players of Yohoho Puzzle Pirates.
+
+ Copyright (C) 2009 Ian Jackson <ijackson@chiark.greenend.org.uk>
+ Copyright (C) 2009 Clare Boothby
+
+  YARRG's client code etc. is covered by the ordinary GNU GPL (v3 or later).
+  The YARRG website is covered by the GNU Affero GPL v3 or later, which
+   basically means that every installation of the website will let you
+   download the source.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+ Yohoho and Puzzle Pirates are probably trademarks of Three Rings and
+ are used without permission.  This program is not endorsed or
+ sponsored by Three Rings.
+
+
+ This Mason component generates the introduction.
+
+
+</%doc>
+<html><head><title>YARRG (Yet Another Revenue Research Gatherer)</title>
+</head><body>
+
+<a href="lookup">YARRG</a> -
+ Yet Another Revenue Research Gatherer
+|
+<b>introduction</b>
+|
+<a href="docs">documentation</a>
+
+<h1>Introduction to YARRG</h1>
+
+YARRG (Yet Another Revenue Research Gatherer) is a third-party tool
+for helping find profitable trades and trade routes in Yohoho Puzzle
+Pirates.  It was inspired by
+<a href="http://pctb.crabdance.com/">PCTB</a>.
+
+<p>
+
+The system has two main parts: this website which maintains a
+searchable database of commodity prices, and an upload client, which
+screenscrapes the commodity data from the Puzzle Pirates game client
+and uploads it to the database.
+
+<h2><a href="lookup">Market prices database</a></h2>
+
+The <a href="lookup">lookup page</a> gives access to the uploaded data;
+there is also <a href="docs">documentation</a> to help with using that
+website.
+
+<h2>Uploading from Linux</h2>
+
+The YARRG upload client uploads both to YARRG and to the
+<a href="pctb.ilk.org">PCTB testing server</a>.
+
+<p>
+
+The current official version of YARRG for use as an upload
+client can browsed here:
+ <a href="/~ijackson/ypp-sc-tools/master/">http://www.chiark.greenend.org.uk/~ijackson/ypp-sc-tools/master/</a>
+See particularly the
+<a href="/~ijackson/ypp-sc-tools/master/yarrg/README">YARRG README</a>.
+
+<p>
+To install the client, install the `git' version control system
+and the other dependencies listed in the `Installation requirements'
+section of the README and then run:
+<pre>
+git-clone http://www.chiark.greenend.org.uk/~ijackson/ypp-sc-tools/master/.git ypp-sc-tools
+cd ypp-sc-tools
+cd yarrg
+make
+</pre>
+this will download the code into the directory <code>ypp-sc-tools</code>,
+and build the software.
+
+<p>
+
+When new versions of the upload client are released, you can:
+<pre>
+cd .../ypp-sc-tools
+git-pull
+cd yarrg
+make
+</pre>
+to fetch the new version.
+
+<h2>Uploading from Windows</h2>
+
+There is not currently an upload client for Windows which feeds data
+into YARRG.  It would probably be straightforward to modify the
+Windows PCTB v5 upload client to upload to YARRG as well.  The
+mechanism and format for uploading is documented in
+<a href="/~ijackson/ypp-sc-tools/master/yarrg/README.devel">README.devel</a>.
+
+<h1>YARRG development, contribution and troubleshooting</h1>
+
+<h2>Free Software (aka Open Source)</h2>
+
+YARRG is Free Software - you may share and modify it.  See the
+licences for details.
+
+<p>
+
+Not only the client but also the webserver code is Free.  The website
+code can be found in the same tree as above, in the <code>web</code>
+directory.
+
+<p>
+
+But in case we have made changes but not yet pushed them
+(perhaps because we haven't done a release), and to make it easy for
+anyone else who runs a copy of the website to provide everyone with
+the source for their version, the website code itself lets you download
+an up-to-date <a href="source.tar.gz">tarball</a> of its
+actually-running source code.
+
+<p>
+
+If you would like to run a (perhaps modified) copy of the YARRG
+website it would be very easy for us to make our system send you
+copies of updates submitted by users of the official YARRG client, in
+the format expected by the code you'll be running.  Please just ask
+us.
+
+<h2>Contacting the YARRG developers</h2>
+
+Email Ian Jackson ijackson (at) chiark.greenend.org.uk.  Or talk to
+any Fleet Officer or above of the crew Special Circumstances on the
+Midnight Ocean.
+
+<& footer, isdocs => 1 &>
index 8fb3bb142a077ce644fd37a3efb1be25046f7390..448ad6ec3441d38171735cbcbb0546b9a235ed0c 100755 (executable)
@@ -124,8 +124,8 @@ body {
   color: #000000;
   background: #ffffff;
 }
-tr.datarow0 { background: #ffffff; }
-tr.datarow1 { background: #e3e3e3; }
+tr.datarow0 { background: #e3e3e3; }
+tr.datarow1 { background: #ffffff; }
 </style>
 <&| script &>
   function register_onload(f) {
@@ -141,6 +141,8 @@ tr.datarow1 { background: #e3e3e3; }
 <a href="<% $m->current_comp()->name() |u %>">YARRG</a> -
  Yet Another Revenue Research Gatherer
 |
+<a href="intro">introduction</a>
+|
 <a href="docs">documentation</a>
 <p>
 <%perl>
@@ -153,7 +155,8 @@ foreach my $var (@vars) {
 
 foreach my $var (keys %ARGS) {
        next unless $var =~
-               m/^(?: (?:route|commod)string |
+               m/^(?: (?:route|commod|capacity)string |
+                       lossperleague |
                        commodid |
                        islandid \d |
                        archipelago \d |
index 84564df55205125a2bbad3deac5611b256167582..639e9abf25bc3a3db06a091f776138e856c32092 100644 (file)
@@ -41,62 +41,65 @@ $dbh
 $thingstring
 $emsgstore
 $perresult
+$prefix => 'ts';
 </%args>
 <%perl>
 my $stringval= $qa->{$thingstring};
 $stringval='' if !defined $stringval;
+
+my $p= $prefix.'_';
 </%perl>
 
 <&| script &>
-ts_uri= "qtextstringcheck?format=application/json&ctype=text/xml"
+<%$p%>uri= "qtextstringcheck?format=application/json&ctype=text/xml"
                + "&what=<% $thingstring %>"
                + "&ocean=<% uri_escape($qa->{Ocean}) %>";
 
-ts_timeout=false;
-ts_request=false;
-ts_done='';
-ts_needed='';
-function ts_Later(){
-  window.clearTimeout(ts_timeout);
-  ts_timeout = window.setTimeout(ts_Needed, 500);
+<%$p%>timeout=false;
+<%$p%>request=false;
+<%$p%>done='';
+<%$p%>needed='';
+function <%$p%>Later(){
+  window.clearTimeout(<%$p%>timeout);
+  <%$p%>timeout = window.setTimeout(<%$p%>Needed, 500);
 }
-function ts_Needed(){
-  window.clearTimeout(ts_timeout);
-  ts_element= document.getElementById('<% $thingstring %>');
-  ts_needed= ts_element.value;
-  ts_Request();
+function <%$p%>Needed(){
+  window.clearTimeout(<%$p%>timeout);
+  <%$p%>element= document.getElementById('<% $thingstring %>');
+  <%$p%>needed= <%$p%>element.value;
+  <%$p%>Request();
 }
-function ts_Request(){
-  if (ts_request || ts_needed==ts_done) return;
-  ts_done= ts_needed;
-  ts_request= new XMLHttpRequest();
-  uri= ts_uri+'&string='+encodeURIComponent(ts_needed);
-  ts_request.open('GET', uri);
-  ts_request.onreadystatechange= ts_Ready;
-  ts_request.send(null);
+function <%$p%>Request(){
+  if (<%$p%>request || <%$p%>needed==<%$p%>done) return;
+  <%$p%>done= <%$p%>needed;
+  <%$p%>request= new XMLHttpRequest();
+  uri= <%$p%>uri+'&string='+encodeURIComponent(<%$p%>needed);
+  <%$p%>request.open('GET', uri);
+  <%$p%>request.onreadystatechange= <%$p%>Ready;
+  <%$p%>request.send(null);
 }
-function ts_Ready() {
-  if (ts_request.readyState != 4) return;
-  if (ts_request.status == 200) {
-    response= ts_request.responseText;
-    debug('got '+response);
+function <%$p%>Ready() {
+  if (<%$p%>request.readyState != 4) return;
+  if (<%$p%>request.status == 200) {
+    response= <%$p%>request.responseText;
+    debug('<%$p%> got '+response);
     eval('results='+response);
-    toedit= document.getElementById('ts_results');
+    toedit= document.getElementById('<%$p%>results');
     toedit.innerHTML= results.show;
   }
-  ts_request= false;
-  ts_Request();
+  <%$p%>request= false;
+  <%$p%>Request();
 }
-register_onload(ts_Needed);
+register_onload(<%$p%>Needed);
 </&script>
 
 <input type="text" <% $m->content %>
  id="<% $thingstring %>" name="<% $thingstring %>"
- onchange="ts_Needed();" onkeyup="ts_Later();"
+ onchange="<%$p%>Needed();" onkeyup="<%$p%>Later();"
  value="<% $stringval |h %>"
  >
 <br>
-<div id="ts_results">&nbsp;</div><br>
+<div id="<%$p%>results">&nbsp;</div><br>
 
 <%perl>
 if (length $thingstring) {
@@ -106,9 +109,13 @@ if (length $thingstring) {
                string => $stringval,
                format => 'return'
        );
-       $$emsgstore= $emsg;
+       if (defined $emsg and length $emsg) {
+               $$emsgstore='' unless defined $$emsgstore;
+               $$emsgstore .= $emsg. ' ';
+       }
 
        foreach my $entry (@$results) {
+#print STDERR "qts entry perresult \`@$entry'\n";
                $perresult->(@$entry);
        }
 }
index b2c101390589b01daf00d858c60f9d205d05ce83..a489d8e1232940e6c603c44e05f43eed3add47b9 100755 (executable)
@@ -62,16 +62,29 @@ my $chk= $m->fetch_comp("check_${what}");
 my $mydbh;
 $dbh ||= ($mydbh= dbw_connect($ocean));
 
-my $sqlstmt= $chk->scall_method("sqlstmt");
-my $sth= $dbh->prepare($sqlstmt);
-my @sqlstmt_qs= $sqlstmt =~ m/\?/g;
+#print STDERR "qtsc string=\`$string'\n";
 
-#die "$sqlstmt @sqlstmt_qs";
+my ($sth, @sqlstmt_qs);
+if ($chk->method_exists('sqlstmt')) {
+       my $sqlstmt= $chk->scall_method("sqlstmt");
+       $sth= $dbh->prepare($sqlstmt);
+       @sqlstmt_qs= $sqlstmt =~ m/\?/g;
+}
 
 my $emsg= '';
 my @results;
-
-my @specs= $chk->attr('multiple') ? (split m#[/|,]#, $string) : ($string);
+my @specs;
+my $canontext;
+my $hooks = {  Emsg => \$emsg,         String => \$string,
+               Results => \@results,   Specs => \@specs,
+               Canon => \$canontext
+           };
+
+if ($chk->method_exists('preparse')) {
+       $chk->call_method('preparse', h => $hooks);
+} else {
+       @specs= $chk->attr('multiple') ? (split m#[/|,]#, $string) : ($string);
+}
 
 no warnings qw(exiting);
 
@@ -103,8 +116,17 @@ foreach my $each (@specs) {
        push @results, $results->[0];
 };
 
+if (!defined $canontext) {
+       $canontext= join ' | ', map { $_->[0] } @results;
+}
+if ($chk->method_exists('postquery')) {
+       $chk->call_method('postquery', h => $hooks);
+}
+
 $emsg='' if !defined $emsg;
-my $canontext= join ' | ', map { $_->[0] } @results;
+@results=() if length $emsg;
+
+#print STDERR "qtsc emsg=\`$emsg' results=\`@results'\n";
 
 if ($format =~ /json/) {
        $r->content_type($ctype or $format);
index 393e7a668755145d05262a56b56ed005330669c1..ea483578d565d6df529dffcfddf7d1c6a5c1e9cf 100644 (file)
@@ -38,6 +38,8 @@ $quri
 $dbh
 $prselector
 $routestring => '';
+$capacitystring => '';
+$lossperleague => '';
 $someresults
 $emsgokorprint
 </%args>
@@ -47,6 +49,8 @@ my $emsg;
 my @archipelagoes;
 my @islandids;
 my %islandid2;
+my ($max_volume, $max_mass);
+my $lossperleaguepct;
 
 my $qa= \%ARGS;
 
@@ -84,6 +88,38 @@ Enter route (islands, or archipelagoes, separated by |s or commas;
  size=80
 </&>
 
+<strong>Advanced options - you may leave these blank:</strong>
+<p>
+<table>
+<tr>
+<td>
+
+Vessel or capacity:
+<&| qtextstring, qa => $qa, dbh => $dbh, prefix => 'cs',
+    thingstring => 'capacitystring', emsgstore => \$emsg,
+    perresult => sub {
+        ($max_volume,$max_mass) = @_;
+    }
+ &>
+ size=30
+</&>
+
+<td>
+&nbsp;
+&nbsp;
+
+<td>
+Expected losses:
+
+<&| qtextstring, qa => $qa, dbh => $dbh, prefix => 'll',
+    thingstring => 'lossperleague', emsgstore => \$emsg,
+    perresult => sub { ($lossperleaguepct)= @_; }
+ &>
+ size=10
+</&>
+
+</table>
+
 % } else { #---------- dropdowns, user selects from menus ----------
 
 % $startform->(grep {
@@ -181,7 +217,9 @@ function ms_Setarch(dd) {
 % } #---------- end of dropdowns, now common middle of page code ----------
 
 <input type=submit name=submit value="<% $goupdate->() %>">
-% my $ours= sub { $_[0] =~ m/^island|^archipelago|^routestring|^[RT]/; };
+% my $ours= sub { $_[0] =~
+%  m/^island|^archipelago|^routestring|^capacitystring|^lossperleague|^[RT]/;
+% };
 <& "lookup:formhidden", ours => $ours &>
 
 <%perl>
@@ -225,7 +263,10 @@ for my $dd (0..$qa->{Dropdowns}-1) {
    dbh => $dbh,
    islandids => \@islandids,
    archipelagoes => \@archipelagoes,
-   qa => $qa
+   qa => $qa,
+   max_mass => $max_mass,
+   max_volume => $max_volume,
+   lossperleaguepct => $lossperleaguepct
  &>
 </form>
 % }
index 4885782e58a440b7a773510a049daf363e5e0838..397e3854372787170afe7e1861de55150aa6495a 100644 (file)
@@ -38,6 +38,9 @@ $dbh
 @islandids
 @archipelagoes
 $qa
+$max_mass
+$max_volume
+$lossperleaguepct
 </%args>
 <&| script &>
   da_pageload= Date.now();
@@ -45,8 +48,9 @@ $qa
 
 <%perl>
 
+my $loss_per_league= defined $lossperleaguepct ? $lossperleaguepct*0.01 : 1e-7;
+
 my $now= time;
-my $loss_per_league= 1e-7;
 
 my @flow_conds;
 my @query_params;
@@ -205,9 +209,12 @@ $addcols->({ DoReverse => 1, SortColKey => 'MarginSortKey' },
        qw(     Margin
        ));
 $addcols->({ DoReverse => 1 },
-       qw(     unitprofit dist MaxQty
-               MaxCapital MaxProfit
+       qw(     unitprofit MaxQty MaxCapital MaxProfit dist
        ));
+foreach my $v (qw(MaxMass MaxVolume)) {
+   $addcols->({
+       DoReverse => 1, Total => 0, SortColKey => "${v}SortKey" }, $v);
+}
 
 </%perl>
 
@@ -269,6 +276,12 @@ foreach my $f (@flows) {
        $f->{MaxProfit}= $f->{MaxQty} * $f->{'unitprofit'};
        $f->{MaxCapital}= $f->{MaxQty} * $f->{'org_price'};
 
+       $f->{MaxMassSortKey}= $f->{MaxQty} * $f->{'unitmass'};
+       $f->{MaxVolumeSortKey}= $f->{MaxQty} * $f->{'unitvolume'};
+       foreach my $v (qw(Mass Volume)) {
+               $f->{"Max$v"}= sprintf "%.1f", $f->{"Max${v}SortKey"} * 1e-6;
+       }
+
        $f->{MarginSortKey}= sprintf "%d",
                $f->{'dst_price'} * 10000 / $f->{'org_price'};
        $f->{Margin}= sprintf "%3.1f%%",
@@ -356,7 +369,7 @@ die "$cmpu $uue ?" if length $cmpu > 20;
 
 <p>
 % if (@islandids<=1) {
-Route is trivial.
+Route contains only one location.
 % }
 % if (!$specific) {
 Route contains archipelago(es), not just specific islands.
@@ -374,9 +387,9 @@ my $cplex= "
 Maximize
 
   totalprofit:
-                  ".(join " +
+                  ".(join "
                   ", map {
-                       sprintf "%.20f %s", $_->{ExpectedUnitProfit}, $_->{Var}
+                       sprintf "%+.20f %s", $_->{ExpectedUnitProfit}, $_->{Var}
                        } @flows)."
 
 Subject To
@@ -407,16 +420,50 @@ foreach my $flow (@flows) {
 foreach my $cstname (sort keys %avail_csts) {
        my $c= $avail_csts{$cstname};
        $cplex .= "
-   ".  sprintf("%-30s","$cstname:")." ".
+   ". sprintf("%-30s","$cstname:")." ".
        join("+", @{ $c->{Flows} }).
        " <= ".$c->{Qty}."\n";
 }
 
+foreach my $ci (0..($#islandids-1)) {
+       my @rel_flows;
+       foreach my $f (@flows) {
+               next if $f->{Suppress};
+               next if $f->{'org_id'} == $f->{'dst_id'};
+               next unless grep { $f->{'org_id'} == $_ }
+                       @islandids[0..$ci];
+               next unless grep { $f->{'dst_id'} == $_ }
+                       @islandids[$ci+1..@islandids-1];
+               push @rel_flows, $f;
+#print " RELEVANT $ci $f->{Ix}  ";
+       }
+#print " RELEVANT $ci COUNT ".scalar(@rel_flows)."  ";
+       next unless @rel_flows;
+       foreach my $mv (qw(mass volume)) {
+               my $max_vn= "max_$mv";
+               my $max= $mv eq 'mass' ? $max_mass : $max_volume;
+               next unless defined $max;
+#print " DEFINED MAX $mv $max ";
+               $cplex .= "
+   ". sprintf("%-10s","${mv}_$ci:")." ".
+       join(" + ", map { ($_->{"unit$mv"}*1e-3).' f'.$_->{Ix} } @rel_flows).
+       " <= $max";
+       }
+       $cplex.= "\n";
+}
+
 $cplex.= "
 Bounds
         ".(join "
         ", map { "$_->{Var} >= 0" } @flows)."
 
+";
+
+$cplex.= "
+Integer
+       ".(join "
+       ", map { "f$_" } (0..$#flows))."
+
 End
 ";
 
@@ -439,7 +486,7 @@ if ($qa->{'debug'}) {
        while (<$output>) {
                $glpsol_out.= $_;
                print encode_entities($_) if $qa->{'debug'};
-               if (m/^\s*No\.\s+Column name\s+St\s+Activity\s/) {
+               if (m/^\s*No\.\s+Column name\s+(?:St\s+)?Activity\s/) {
                        die if $found_section>0;
                        $found_section= 1;
                        next;
@@ -463,7 +510,14 @@ if ($qa->{'debug'}) {
        die $prerr unless $found_section;
 };
 
-$addcols->({ DoReverse => 1 }, qw(
+$addcols->({ DoReverse => 1, Special => sub {
+       my ($flow,$col,$v,$spec) = @_;
+       if ($flow->{ExpectedUnitProfit} < 0) {
+               $spec->{Span}= 3;
+               $spec->{String}= '(Small margin)';
+               $spec->{Align}= 'align=center';
+       }
+} }, qw(
                OptQty
        ));
 $addcols->({ Total => 0, DoReverse => 1 }, qw(
@@ -486,7 +540,7 @@ $addcols->({ Total => 0, DoReverse => 1 }, qw(
 <colgroup span=2>
 <colgroup span=2>
 <colgroup span=2>
-<colgroup span=1>
+<colgroup span=3>
 <colgroup span=3>
 %      if ($optimise) {
 <colgroup span=3>
@@ -499,8 +553,9 @@ $addcols->({ Total => 0, DoReverse => 1 }, qw(
 <th colspan=2>Collect
 <th colspan=2>Deliver
 <th colspan=2>Profit
-<th colspan=1>
 <th colspan=3>Max
+<th colspan=1>
+<th colspan=2>Max
 %      if ($optimise) {
 <th colspan=3>Planned
 %      }
@@ -516,10 +571,12 @@ $addcols->({ Total => 0, DoReverse => 1 }, qw(
 <th>Qty
 <th>Margin
 <th>Unit
-<th>Dist
 <th>Qty
 <th>Capital
 <th>Profit
+<th>Dist
+<th>Mass
+<th>Vol
 %      if ($optimise) {
 <th>Qty
 <th>Capital
@@ -539,15 +596,25 @@ $addcols->({ Total => 0, DoReverse => 1 }, qw(
 <td><input type=hidden   name=R<% $flow->{UidShort} %> value="">
     <input type=checkbox name=T<% $flow->{UidShort} %> value=""
        <% $flow->{Suppress} ? '' : 'checked' %> >
-%      foreach my $ci (1..$#cols) {
+%      my $ci= 1;
+%      while ($ci < @cols) {
 %              my $col= $cols[$ci];
+%              my $spec= {
+%                      Span => 1,
+%                      Align => ($col->{Text} ? '' : 'align=right')
+%              };
 %              my $v= $flow->{$col->{Name}};
-%              $col->{Total} += $v if defined $col->{Total};
+%              if ($col->{Special}) { $col->{Special}($flow,$col,$v,$spec); }
+%              $col->{Total} += $v
+%                      if defined $col->{Total} and not $flow->{Suppress};
 %              $v='' if !$col->{Text} && !$v;
 %              my $sortkey= $col->{SortColKey} ?
 %                      $flow->{$col->{SortColKey}} : $v;
 %              $ts_sortkeys{$ci}{$rowid}= $sortkey;
-<td <% $col->{Text} ? '' : 'align=right' %>><% $v |h %>
+<td <% $spec->{Span} ? "colspan=$spec->{Span}" : ''
+ %> <% $spec->{Align}
+ %>><% exists $spec->{String} ? $spec->{String} : $v |h %>
+%              $ci += $spec->{Span};
 %      }
 % }
 <tr id="trades_total">
@@ -584,11 +651,11 @@ $addcols->({ Total => 0, DoReverse => 1 }, qw(
 <tr><td colspan=3>
 %      $iquery->execute($islandids[$i]);
 %      my ($islandname) = $iquery->fetchrow_array();
-%      my $this_dist= $distance->($islandids[$i-1],$islandids[$i]);
-%      $total_dist += $this_dist;
 %      if (!$i) {
 <strong>Start at <% $islandname |h %></strong>
 %      } else {
+%              my $this_dist= $distance->($islandids[$i-1],$islandids[$i]);
+%              $total_dist += $this_dist;
 <strong>Sail to <% $islandname |h %></strong>
 - <% $this_dist |h %> leagues </td>
 %      }