# # makemovie.pl # # This program reads a big data file and produces an # interactive CGI script from it. The scripts are # tailored to make bridge movies, but hopefully are # fairly flexible. # Please do not distribute this software without # appropriate headers and appelations. Any commercial # application must get express permission, but free # distribution is welcome. Feel free to use this for # movies on subjects other than bridge. # Thank you to the alpha testers: # Danil Suits # Brian "Binkley" Oxley # Don Kersey # Mike Albert # Author: Jeff Goldsmith # Version 0.1 April 30, 1996 # Initial Implemntation # Begin alpha test # Version 0.2 May 2, 1996 # Added globals: # BOARDS, NAME, DATE, EMAIL, HOMEPAGE, PERL/ENDPERL # Fixed gross quoted strings bug (Thanks, Danil.) # Version 0.3 May 3, 1996 # Tinkered with date # Can handle lines ending in \\\s*\n # Added HAND directive # Added BIDDING,BID2,BID4 directives # Moved some stuff into header file # Did some fixes to the bml stuff # BOARDS takes 1 or 2 arguments # Added IMP scoring/scorecard # Version 0.4 May 7, 1996 # Allowed bidding to end with END # Tinkered with date # Improved CARD macro # IMP Scorecard improved # DEBUG mode # entered via DEBUG in file # or in QUERY_STRING # Bug fixes: BIDDING now ignores blank lines # SAVE, USE, ELSE, ENDUSE # Version 0.5 May 9, 1996 # BOARD directives # Bug fixes # HANDRECORDS # More on that pesky date... # Anyone know how to generate a # good date string out of ctime # that works all the time? # Macros with or without arguments # Version 0.6 May 13, 1996 # For some reason, ELSE got torqued. Fixed. # Polished scorecards # TOP, OPPONENTS # Page (::) operator # Optional argument to NEXT, CHOICE # secure flag # Version 0.7 May 15, 1996 # Refixed IMP scorecard # Put an extra space between radio choices # Cleaned up junk in (thanks Binkley!) # Cleaned up scorecard code a little # Text-only version # Fixed error routine # Clickable client-side image maps # Version 1.0 May 16, 1996 # BODY, LAYOUT # First public release # Version 1.01 May 27, 1996 # redirect (http:) operator in page names # Bug fix: vertical lists of choices had all but 1st indented # End BOARD, BIDDING on new directive # Don't end BID2 with one pass if it's the first call # or BIDDING if three passes are the first three. # Version 1.02 June 24, 1996 # Put border = 0 tag in all IMG tags so that # links with suit symbols look OK. # Auto IMPing (Good idea, Roberto!) # Version 1.03 Dec. 17, 1996 # Added IMPPAIRS # Added VP20 # Bug fix for "Passed Out" in SCORE # Added CARRYOVER # Leaderboard stuff # Version 1.04 Feb. 11, 1997 # Changed QUERY_STRING parsing to handle # broken Microsoft Explorer behavior. MSE # keeps appending new QUERY_STRINGS to the # end of the old ones. # Expressly specified method and action in # forms for MSE. # Version 1.05 June 30, 1997 # Hacked in PC date tinker # Version 1.06 Dec. 3, 1997 # Fixed Perl5 semantics bug in bidding macros # Fixed "Double" in bidding not going to diamonds # The script contains pages delimited by PAGE directives # and page text mixed with other directives. The current # version handles the following directives: @directives = ( "BID2", "BID4", "BIDDING", "BOARD", "BOARDS", "BODY", "CARRYOVER", "CASE", "CHOICE", "DATE", "DEBUG", "DEFINE", "ELSE", "EMAIL", "ENDCASE", "ENDPERL", "ENDUSE", "HAND", "HANDRECORDS", "HOMEPAGE", "IMP", "IMPPAIRS", "LAYOUT", "LEADER", "LEADERBOARD", "MAP", "NAME", "NEXT", "OPPONENTS", "PAGE", "PERL", "RESULTS", "SAVE", "SCORE", "TEXTVERSION", "TITLE", "TOP", "USE", "VP20", ); # Plus, Comments (begin with #) # It also does bridge typesetting using the BML macro system. # Known failures and limitations: # # - BML syntax is hard to learn...improving # - movie.dat is hardcoded # - SCORE syntax inflexible # - Better error detection. I ought to have warnings # - improvments to DEBUG mode, catch: # - Runaway bidding # - Runaway hands # - SCORE format errors. # - CASES without CHOICE/NEXT # - PAGEs named but not built # - Needs a way to produce all the pages at once # - macros only one level deep; parens in general are not handled well # - timeouts might be useful # The main routine is a finite state machine with the # following states: # 1---rejecting input; this is the initial state # changes to state 2 upon seeing directive that # matches ^PAGE\s*$page # Accepts: # PAGE if match --> state 2 # BOARD --> state 11 # DATE # NAME # HOMEPAGE # EMAIL # BOARDS # PERL --> state 6 # DEBUG --> state 9 # IMP # IMPPAIRS # LEADER # DEFINE # OPPONENTS # TOP # BODY # LAYOUT # RESULTS # VP20 # 2---Accepting text and directives # upon matching ^PAGE, exits # Accepts: # PAGE --> exit # TITLE # NEXT # SCORE # CHOICE # CASE if not match --> state 4 # ENDCASE # PERL --> state 5 # ENDPERL # HAND # BIDDING # BID4 # BID2 # SAVE # USE if not match --> state 10 # ENDUSE # ELSE # BOARD # TEXTVERSION # MAP # LEADERBOARD # text # 3---Accepting a hand # After correct number of arguments --> state 2 # If directive appears early --> state 0 # 4---Ignoring incorrect CASE # goes to state 2 upon CASE directive # that matches answer variable. Goes # to state 2 upon finding ENDCASE. # Accepts: # PAGE --> exit # CASE if match --> state 2 # ENDCASE --> state 2 # 5---Accepting Perl # goes to state 2 upon ENDPERL directive # Accepts: # ENDPERL --> state 2 # 6---Accepting perl from state 1 # Accepts: # ENDPERL --> state 1 # 7---Accepting four-player bidding # Upon "All Pass" or 3 passes, --> state 2 # END --> state 2 # 8---Accepting two-player bidding # Pass --> state 2 # END --> state 2 # 9---Check mode. Terminal # Only prints possible syntax errors # 10--Rejecting USE block # ENDUSE --> state 2 # 11--Accepting BOARD input # after 16 tokens --> state 1 # 12--Ignoring wrong PAGEs # 13--getting IMP results # 0---Error # Print message and exit. eval {require "ctime.pl";} || &error("Can't find perl library. Sorry."); # Globals # leader stuff uses two associative arrays, # keyed by OPPONENT/LEADER number. # %opponent{name} is the team name # %teamscore{number} is the team's score $datafile = "movie.dat"; $date = &lmdate($datafile); # default is last modified date of file $secure = 0; # if 1, PERL blocks are disallowed. $unix = 1; # set to 0 if date looks like "Fri 03-22-1996" # who am I; where are my gifs? eval {require "movie.h";} || &error("Can't find movie.h. Sorry."); # Initial values of stuff $state = 1; # rejecting state $text = ""; # page text $title = ""; # page title $answer = ""; # current answer value $case = ""; # no case to find $firstboard = 1; # for scorecard $lastboard = 26; # for scorecard $perlcode = ""; # no code $mpscoring = 1; # assume matchpoints unless IMP directive $scores = ""; # no scores yet $handrecords = 0; # no handrecords unless requested $top = 12; # top on board $nextstring = 'Next Hand'; # stuff printed in NEXT submit button $choicestring = 'Onward'; # stuff printed in CHOICE submit button $textonly = 0; # special text mode for non-netscape browsers (feh) $textbutton = 0; # button to text only version $nextmap = 0; # keep track of client-side image maps $body = ""; # no body tag ancillary goop $layout = ""; # no global layout goop $htmldebug = 0; # not debug mode $useresults = 0; # no user-supplied results $vp20 = 0; # not short matches $imppairs = 0; # not imppairs $carryover = 0; # no carryover @month = ("none","January","February","March","April", "May","June","July","August","September", "October","November","December"); # where to put hands in diagram $location{"N"} = 0; $location{"W"} = 1; $location{"E"} = 2; $location{"S"} = 3; # Parse QUERY_STRING # These three lines use the supported unescape code that's # normally shipped with httpd software. It can be # commented out with some loss in security. # If you use these, comment out the following line. #$ENV{'PATH'} = '/usr/graphics/etc/httpd/support'; #$string = $ENV{`unescape "QUERY_STRING"`}; #@pairs = split(/&/, $string); # There seems to be a bug in Microsoft Explorer in # which it doesn't understand that goop?junk is # two different parts of the path. Get rid of everything # before the last question mark. $string = $ENV{'QUERY_STRING'}; $string =~ s/.*\?//; @pairs = split(/&/, $string); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $name =~ tr/+/ /; $name =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; # If they try to include server side includes, erase them, so they # arent a security risk if the html gets returned. Another # security hole plugged up. $value =~ s///g; $vars{$name} = $value; } # maintain the score variable $scores = $vars{Score}; # grab answer $answer = $vars{Answer}; # if the page variable is set, state 1 is looking for it, # otherwise, assume the page is "Welcome". if ($vars{Page}) { $page = $vars{Page}; } else { $page = "Welcome"; } # check for page overrride via the page operator (::) if ($answer =~ '::') { $page = $answer; $page =~ s/::.*//; $answer =~ s/.*:://; } elsif ($scores =~ '::') { if ($scores =~ /;([^;]*)::/) { $page = $1; $scores =~ s/;[^;]*:://g; } else { $scores =~ s/(.*)::;//; $page = $1; } } # if the page for which we are looking begins with http:, # then we are being redirected out of the movie. if ($page =~ /^http:/) { &html_redirect($page); exit(0); } # check for debug mode if ($ENV{QUERY_STRING} =~ "DEBUG") { $state = 9; $htmldebug = 1; } # Check hidden variable "Tables" if (($vars{Tables} eq "No") || ($vars{Submit} eq "No Graphics")) { $textonly = 1; } # open datafile open (MOVIEFILE, $datafile) || &error("Could not open movie file $datafile\n"); # main loop: read input from datafile while () { # handle continuation lines # for debugging: print "state = $state, input = $_"; while (/\\\s*$/) { $_ .= ; s/\\\s*\n//; # remove \ and newline } if ($state == 1) { # # This is the initial state # Globals are set here at the top of the file # # we also check for scorecard entry here, # but only after we have read all globals. # if (/^PAGE/ && $vars{Submit} eq "Scorecard") { if ($mpscoring) {&scorecard($firstboard,$lastboard);} else {&imp_scorecard($firstboard,$lastboard);} exit(0); } elsif (/^PAGE/ && $vars{Submit} eq "Hand Records") { &handrecords; exit(0); } elsif (/^PAGE\s*$page/) { $state = 2; } elsif (/^PAGE/) {$state = 12;} elsif (/^DATE/) { s/^DATE\s*//; s/\s*$//; $date = $_; } elsif (/^NAME/) { s/^NAME\s*//; s/\s*$//; $myname = $_; } elsif (/^HOMEPAGE/) { s/^HOMEPAGE\s*//; s/\s*$//; $homepage = $_; } elsif (/^EMAIL/) { s/^EMAIL\s*//; s/\s*$//; $email = $_; } elsif (/^BOARDS/) { s/^BOARDS\s*//; s/\s*$//; if (/\s+/) { # two arguments split; $firstboard = $_[0]; $lastboard = $_[1]; } else { # one argument $firstboard = 1; $lastboard = $_[0]; } } # in state 1 (preamble) the BOARD command saves # a board. In state 2, it retrieves it. Hand # records will use them, too. elsif (/^BOARD/) { $state = 11; s/^BOARD\s*//; s/\s*$//; $board = $_; # remember board number/title $savedboards{$board} = ""; $handtokens = 0; } elsif (/^PERL/) { $state = 6; $perlcode = ""; } elsif (/^IMPPAIRS/) { $mpscoring = 0; $imppairs = 1; } elsif (/^IMP/) { $mpscoring = 0; $imppairs = 0; } elsif (/^DEBUG/) { $state = 9; } elsif (/^DEFINE/) { # this sets up a simple macro...no arguments yet chop; s/^DEFINE\s*//; s/\s*$//; ($name,$replacement) = &split_args($_); $macros{$name} = $replacement; } elsif (/^OPPONENTS/) { chop; s/^OPPONENTS\s*//; s/\s*$//; @opp = &split_args($_); } elsif (/^TOP/) { chop; s/^TOP\s*//; s/\s*$//; $top = $_; if ($top <= 0) {$top = 12;} } elsif (/^BODY/) { chop; s/^BODY\s*//; s/\s*$//; $body = $_; } elsif (/^LAYOUT/) { chop; s/^LAYOUT\s*//; s/\s*$//; $layout = $_; } elsif (/^RESULTS/) { s/^RESULTS\s*//; s/\s*$//; $resultstring = $_; $state = 13; } elsif (/^VP20/) { s/^VP20\s*//; s/\s*$//; $vp20 = $_; # number of boards in match } elsif (/^CARRYOVER/) { s/^CARRYOVER\s*//; s/\s*$//; $carryover = $_; } elsif (/^LEADER/) { chop; s/\\ /SPACEXXX/g; split; for ($i=0;$i<@_;$i++) {$_[$i] =~ s/SPACEXXX/ /g;} # looks like LEADER 1 Begley 112.4 $opponent{$_[1]} = $_[2]; $teamscore{$_[1]} = $_[3]; } else { next; } } # State 2 elsif ($state == 2) { # PAGE directive if (/^PAGE/) { &print_page; &done; } # TITLE directive elsif (/^TITLE(\*?)/) { $star = $1; $title = $_; $title =~ s/TITLE\*?\s*//; $title =~ s/\s*$//; if (!$star) { $text .= "

$title

\n"; } } # HANDRECORDS directive elsif (/^HANDRECORDS/) { $handrecords = 1; } # TEXTVERSION directive elsif (/^TEXTVERSION/) { $textbutton = 1; } # NEXT directive elsif (/^NEXT(\*?)/) { $star = $1; s/\*//; # check for optional argument if (/^NEXT\{(.*)\}/) { $nextstring = $1; s/NEXT\{.*\}\s*(.*)\s*/$1/; } else { s/NEXT\s*(.*)\s*/$1/; } if ($star) { $text .= &end_of_hand_no_sb($_); } else { $text .= &end_of_hand($_); } } # SCORE directive elsif (/^SCORE/) { # score looks like # SCORE board contract declarer result score matchpoints/IMPs chop; ($junk,$board,$contract,$declarer,$result,$score,$matchpoints) = &split_args($_); # if user-results exist, IMP against them. if ($useresults) { $tempresult = $results[$board-$firstboard]; $matchpoints = &imps($score,$tempresult); } # print/save results $text .= "CARD($contract) $result $score\n"; $scores .= "$board:$contract.$declarer.$score.$matchpoints;"; } # CHOICE directive elsif (/^CHOICE/) { chop; # check for optional argument if (/^CHOICE\{(.*)\}/) { $choicestring = $1; s/CHOICE\{.*\}\s*(.*)\s*$/$1/; } else {s/^CHOICE\s*//; s/\s*$//;} @args = &split_args($_); $text .= &html_radio(@args); } # CASE directive elsif (/^CASE/) { # is the answer right? s/CASE\s*//; s/\s*$//; if ($_ ne $answer) { $state = 4; } } # ENDCASE directive elsif (/^ENDCASE/) { # not a problem } # HAND directive elsif (/^HAND/ && !/^HAND\(/) # HAND( is BML, not movie directive { # check for optional arguments if (/^HAND\*/) {$fullhand = 1;} else {$fullhand = 0;} if (/^HAND\*?{(.*)}/) { $arglist = $1; $arglist =~ tr/a-z/A-Z/; @whichhands = split(/[\s,]+/,$arglist); } else { @whichhands = ("N","W","E","S"); } $hand = ""; $handtokens = 0; $state = 3; } # BIDDING/BID4 directive elsif ((/^BIDDING/ || /^BID4/) && !/BIDDING\(/ && !/BID4\(/ ) { $state = 7; $bidding = ""; } # BID2 directive elsif (/^BID2/) { $state = 8; $bidding = ""; } # PERL directive elsif (/^PERL/) { $state = 5; $perlcode = ""; } # ENDPERL directive elsif (/^ENDPERL/) { # do nothing } # SAVE directive elsif (/^SAVE/) { chop; s/^SAVE\s*//; s/\s*$//; $scores .= "$_;"; } # USE directive elsif (/^USE/) { # nab string chop; s/^USE\s*//; s/\s*$//; # check it? if ($scores =~ /$_/) # it gets used up (all copies) {$scores =~ s/$_;//g;} else {$state = 10;} } # ENDUSE directive elsif (/^ENDUSE/) { # do nothing } # ELSE directive elsif (/^ELSE/) { $state = 10; } # BOARD directive elsif (/^BOARD/) { chop; # is variant? if (/^BOARD\*/) {$variant = 1; s/\*//;} else {$variant = 0;} # save optional args if (/^BOARD{/) { $arglist = $_; $arglist =~ s/.*{(.*)}.*/$1/; $arglist =~ tr/a-z/A-Z/; s/{.*}//; } else {$arglist = "N,W,E,S";} @arglist = split(/,/,$arglist); # find hand title s/^BOARD\s*//; s/\s*$//; # $savedboards{$_} is sixteen tokens. # build HAND macro @_ = split(/\s+/,$savedboards{$_}); $h[0] = "VHAND($_[0],$_[1],$_[2],$_[3])"; $h[1] = "VHAND($_[4],$_[6],$_[8],$_[10])"; $h[2] = "VHAND($_[5],$_[7],$_[9],$_[11])"; $h[3] = "VHAND($_[12],$_[13],$_[14],$_[15])"; if (!$variant) { $text .= "HAND("; if ($arglist =~ /N/) {$text .= $h[0].",\n";} else {$text .= ",";} if ($arglist =~ /W/) {$text .= $h[1].",\n";} else {$text .= ",";} if ($arglist =~ /E/) {$text .= $h[2].",\n";} else {$text .= ",";} if ($arglist =~ /S/) {$text .= $h[3].")\n";} else {$text .= ")\n";} } else { $text .= "HAND("; # argument 0-3 if ($arglist[0]) { $text .= $h[$location{$arglist[0]}]; } $text .= ","; if ($arglist[1]) { $text .= $h[$location{$arglist[1]}]; } $text .= ","; if ($arglist[2]) { $text .= $h[$location{$arglist[2]}]; } $text .= ","; if ($arglist[3]) { $text .= $h[$location{$arglist[3]}]; } $text .= ")\n"; } } # MAP directive elsif (/^MAP/) { # syntax: MAP image page tag x y x y tag x y x y ... chop; s/^MAP\s*//; s/\s*$//; @args = &split_args($_); $text .= &html_imagemap(@args); } # LEADERBOARD elsif (/^LEADERBOARD/) { $text .= &printleaders; } # comments elsif (/^#/) { # do nothing } else { # substitutions: # (1) blank line --->

if (/^\s*$/) { $text .= "

\n"; } else { $text .= $_; } } } # accepting hand elsif ($state == 3) { # other directives are errors if (/^PAGE/ || /^CASE/ || /^ENDCASE/ || /^SCORE/ || /^TITLE/ || /^PERL/ || /^ENDPERL/ || /^NEXT/ || /^CHOICE/ || /^BOARDS/ || /^NAME/ || /^DATE/ || /^EMAIL/ || /^HAND/ || /^HOMEPAGE/) {&error("Incomplete HAND\n");} # we shall read a total of 16 tokens chop; s/^\s*//; s/\s*$//; $hand .= $_." "; split; $handtokens += @_; if ($fullhand) {$ntokens = 16;} else {$ntokens = 4 * @whichhands;} if ($handtokens >= $ntokens) { split(/\s+/,$hand); # if one hand, it's simple # if two hands, then the suits are all # in sequence for a hand unless the # two hands are E/W, then they are interleaved # if three hands, E/W are interleaved if both there # if four hands, E/W always interleaved # I don't see an intelligent way to compress # this code, so it's getting cased out. $h[0] = ""; $h[1] = ""; $h[2] = ""; $h[3] = ""; if ($fullhand) # HAND* directive { $_ = join("",@whichhands); if (/N/) {$h[0] = "VHAND($_[0],$_[1],$_[2],$_[3])";} if (/W/) {$h[1] = "VHAND($_[4],$_[6],$_[8],$_[10])";} if (/E/) {$h[2] = "VHAND($_[5],$_[7],$_[9],$_[11])";} if (/S/) {$h[3] = "VHAND($_[12],$_[13],$_[14],$_[15])";} } elsif (@whichhands == 4) { $h[$location{$whichhands[0]}] = "VHAND($_[0],$_[1],$_[2],$_[3])"; $h[$location{$whichhands[1]}] = "VHAND($_[4],$_[6],$_[8],$_[10])"; $h[$location{$whichhands[2]}] = "VHAND($_[5],$_[7],$_[9],$_[11])"; $h[$location{$whichhands[3]}] = "VHAND($_[12],$_[13],$_[14],$_[15])"; } elsif (@whichhands == 3) { $_ = join("",@whichhands); if (/N/ && /S/) # only one E/W, so no interleaving { $h[$location{$whichhands[0]}] = "VHAND($_[0],$_[1],$_[2],$_[3])"; $h[$location{$whichhands[1]}] = "VHAND($_[4],$_[5],$_[6],$_[7])"; $h[$location{$whichhands[2]}] = "VHAND($_[8],$_[9],$_[10],$_[11])"; } else { $h[$location{$whichhands[0]}] = "VHAND($_[0],$_[1],$_[2],$_[3])"; $h[$location{$whichhands[1]}] = "VHAND($_[4],$_[6],$_[8],$_[10])"; $h[$location{$whichhands[2]}] = "VHAND($_[5],$_[7],$_[9],$_[11])"; } } elsif (@whichhands == 2) { $_ = join("",@whichhands); if (/E/ && /W/) # only E/W, so interleave { $h[$location{$whichhands[0]}] = "VHAND($_[0],$_[2],$_[4],$_[6])"; $h[$location{$whichhands[1]}] = "VHAND($_[1],$_[3],$_[5],$_[7])"; } else { $h[$location{$whichhands[0]}] = "VHAND($_[0],$_[1],$_[2],$_[3])"; $h[$location{$whichhands[1]}] = "VHAND($_[4],$_[5],$_[6],$_[7])"; } } elsif (@whichhands == 1) { $h[$location{$whichhands[0]}] = "VHAND($_[0],$_[1],$_[2],$_[3])"; } # use BML macro $text .= "HAND(".$h[0].",\n".$h[1].",\n".$h[2].",\n".$h[3].")\n"; $state = 2; } } # rejecting bad case elsif ($state == 4) { if (/^PAGE/) { &print_page; &done; } elsif (/^CASE/) { # is the answer right? s/CASE\s*//; s/\s*$//; if ($_ eq $answer) { $state = 2; } } elsif (/^ENDCASE/) { $state = 2; } else { # ignore input } } # accepting perl code elsif ($state == 5) { if (/^ENDPERL/) { if (!$secure) {eval $perlcode;} $state = 2; } else { $perlcode .= $_; } } # accepting perl code elsif ($state == 6) { if (/^ENDPERL/) { if (!$secure) {eval $perlcode;} $state = 1; } else { $perlcode .= $_; } } # accepting 4-player bidding elsif ($state == 7) { # end on new directive $skip = 0; for ($i=0;$i<@directives;$i++) { if (/^$directives[$i]/) { @_ = &split_args($bidding); $text .= "BID4(".join(",",@_).")\n"; $state = 2; $skip = 1; } } if ($skip) {redo;} chop; s/^\s*//; s/\s*$//; if (/^\s*$/) {next;} $bidding .= $_." "; # end is 3 consecutive passes or All Pass $done = 0; if (/All\s*Pass/i || /END/) { $done = 1; $bidding =~ s/END//; } elsif (/Pass/i) { $_ = $bidding; split; $passes = 0; $nobid = 1; for ($i=0;$i<@_;$i++) { # ignore first pass unless bid has occurred if ($_[$i] =~ /[1234567]/) {$nobid = 0;} if ($_[$i] =~ /Pass/i) {$passes++;} else {$passes = 0;} if ((!$nobid && $passes >= 3) || ($passes >= 4)) {$done = 1; last;} } } if ($done) { @_ = &split_args($bidding); $text .= "BID4(".join(",",@_).")\n"; $state = 2; } } # accepting 2-player bidding elsif ($state == 8) { # end on new directive $skip = 0; for ($i=0;$i<@directives;$i++) { if (/^$directives[$i]/) { @_ = &split_args($bidding); $text .= "BID4(".join(",",@_).")\n"; $state = 2; $skip = 1; } } if ($skip) {redo;} chop; s/^\s*//; s/\s*$//; if (/^\s*$/) {next;} $bidding .= $_." "; if (/END/ || $bidding =~ /[1234567].*Pass/i) { $bidding =~ s/END//; @_ = &split_args($bidding); $text .= "BIDDING(".join(",",@_).")\n"; $state = 2; } } # Syntax check state elsif ($state == 9) { # mixed/lower case directive? for ($i=0;$i<@directives;$i++) { if (/^$directives[$i]/i && !/^$directives[$i]/) { $text .= "Possible mixed/lower case directive:
\n"; $text .= " Line $.:
\n"; $text .= " $_
"; } } } # ELSE blocks elsif ($state == 10) { # exit if (/^ENDUSE/ || /^ELSE/) { $state = 2; } } # reading boards in during preamble elsif ($state == 11) { # end on new directive $skip = 0; for ($i=0;$i<@directives;$i++) { if (/^$directives[$i]/) { $state = 1; $skip = 1; } } if ($skip) {redo;} chop; s/^\s*//; s/\s*$//; split; $handtokens += @_; $_ = join(" ",@_); $savedboards{$board} .= $_." "; if ($handtokens >= 16) { $state = 1; } } # reading in results elsif ($state == 13) { #end on new directive $skip = 0; for ($i=0;$i<@directives;$i++) { if (/^$directives[$i]/) { $state = 1; $skip = 1; } } if (/^\s*$/) {$state = 1; $skip = 1}; if ($skip) { # convert into assoc. array. $resultstring =~ s/^\s*//; @results = split(/\s+/,$resultstring); $useresults = 1; # reprocess directive redo; } else { chop; $resultstring .= " ".$_; } } # ignoring wrong pages elsif ($state == 12) { if (/^PAGE\s*$page/) { $state = 2; } } } # end while <> # On end of file: if ($state == 9) { if ($htmldebug) { &print_page; &done; } else { $text =~ s/\$//g; print $text; } } elsif ($state == 1) { # didn't find the page &html_header("Error"); print "

Error

\n"; print "Could not find page $page.\n"; &html_trailer; &done; } else { &print_page; &done; } sub print_page { if ($title) { &html_header($title); } else { &html_header($page); } &bridge_trans($text); print $text; &html_trailer; } # header sub html_header { print "Content-type: text/html\n\n"; print "\n"; print "\n"; print "$_[0]\n"; print "\n"; print "\n"; print "$layout\n"; } # trailer sub html_trailer { print "
\n"; print "
\n"; print "© $myname,\n"; print "$email,\n"; print "$date\n"; print "
\n"; print "\n"; print "\n"; } # redirection sub html_redirect { print "Location: $_[0]\n\n"; } # This sets up the go to scorecard boxes sub end_of_hand { $script = $ENV{SCRIPT_NAME}; $xxx .= "
\n"; $xxx .= "\n"; $xxx .= ""; if ($textonly) {$xxx .= "\n";} $xxx .= ""; $xxx .= "\n"; $xxx .= ''; if ($handrecords) {$xxx .= '\n"; $xxx .= "\n"; $xxx .= ""; if ($textonly) {$xxx .= "\n";} $xxx .= ""; if ($handrecords) {$xxx .= '';} if ($textbutton) {$xxx .= '';} $xxx .= "\n
\n"; $xxx; } # radio boxes # input 1: hand number (hidden var) # input 2-3: name of and tag for first (checked) box # input 4-6: name of and tag for 2nd box, ... # it'll also assign $scores to the Scores hidden variable sub html_radio { @args = @_; $script = $ENV{SCRIPT_NAME}; $xxx .= "
\n"; $xxx .= "\n"; $xxx .= "\n"; if ($textonly) {$xxx .= "\n";} for ($i=1;$i<@args;$i+=2) { if (@args == 2) { $xxx .= "\  $args[$i+1]
\n";} elsif ($i == 1) { $xxx .= "\  $args[$i+1]\n";} elsif ($i >= @args-2) { $xxx .= "\  $args[$i+1]
\n";} else { $xxx .= "\  $args[$i+1]\n";} } $xxx .= ""; $xxx .= "\n
\n"; $xxx; # return massive string } # This generates an image map call and stuffs all useful data into the URL sub html_imagemap { @args = @_; $file = $ENV{'SCRIPT_NAME'}; $args[1] =~ tr/ /+/; if (@args > 3) { $xxx = "\n"; $xxx .= "\n"; for ($i=2;$i<@args;$i+=5) { $xxx .= "\n"; } $xxx .= "\n"; $xxx; } else { $xxx = "\n"; $xxx .= "\n"; $xxx; } } # This prints a scorecard sub scorecard { local($first,$last); # save arguments $first = $_[0]; $last = $_[1]; # errors if ($first > $last) {$first = 1; $last = 26;} # header &html_header("Private Score"); # handle scores $_ = $scores; s/Appeal;//g; # don't worry about appeals @s = split(/;/,$_); for ($i=0;$i<@s;$i++) { $_ = $s[$i]; ($h,$r) = /(.*):(.*)/; $scores[$h] = $r; } # build next box $page = $vars{"Page"}; $temp = "

Private Score

\n"; $script = $ENV{SCRIPT_NAME}; $temp .= "
\n"; $temp .= "\n"; if ($textonly) {$xxx .= "\n";} $temp .= "\n"; $temp .= "
\n"; $temp .= "
\n"; # table header line if (@opp) { if (!$textonly) { $temp .= "\n"; $temp .= "\n"; $temp .= "\n"; } else { $temp .= "
\n";
	    $temp .= " Hand     Opp    Contract Decl. Score   MPs\n";
	    }
	}
     else
	{
	if (!$textonly)
	    {
            $temp .= "
\n"; $temp .= "\n"; } else { $temp .= "
\n";
	    $temp .= " Hand   Contract Decl. Score   MPs\n";
	    }
	}

     # Each board
     $nhands = 0;
     $totalmp = 0;
     for ($i=$first;$i<=$last;$i++)
        {
        $op = $i-$first;

        # no score on the board
        if ($scores[$i] eq "") 
	     {
	     if ($textonly)	
	         { $temp .= sprintf("   %s\n",$i); }
	     else
	         {
                 $temp .= "
\n"; } } # board with score else { $_ = $scores[$i]; ($c,$d,$r,$mp) = split(/\./); if ($textonly) { if (@opp) { $temp .= sprintf(" %3s %3s %-6s %2s %6s %5s\n",$i,$opp[$op],$c,$d,$r,$mp); } else { $temp .= sprintf(" %3s %-6s %2s %6s %5s\n",$i,$c,$d,$r,$mp); } } else { if (@opp) { $temp .= ""; $temp .= ""; $temp .= "\n"; } else { $temp .= ""; $temp .= "\n"; } } $nhands++; if ($mp =~ /\+/) {$mp =~ s/\+//; $mp += .5;} $totalmp += $mp; } } # end each board loop # end of hand record if (!$textonly) { $temp .= "
HandOppContractDeclarerScoreMPs
HandContractDeclarerScoreMPs
$i "; $temp .= "
$i $opp[$op]CARD($c)$d$r$mp
$i CARD($c)$d$r$mp
\n"; &bridge_trans($temp); } else {$temp .= "\n";} print $temp; # summary if ($nhands > 0) { $game = $totalmp / $nhands / $top * 100; if ($nhands == 1) {$junk = "board";} else {$junk = "boards";} printf "After %d %s, you have a %.1f%% game.\n",$nhands,$junk,$game; if ($carryover) {printf "
With carryover, you have %g matchpoints.\n",$totalmp+$carryover;} } # end of page &html_trailer; } # This prints an IMP scorecard sub imp_scorecard { local($first,$last); # save arguments $first = $_[0]; $last = $_[1]; # errors if ($first > $last) {$first = 1; $last = 26;} # header &html_header("Private Score"); # handle scores $_ = $scores; s/Appeal;//g; # don't worry about appeals split(/;/,$_); for ($i=0;$i<@_;$i++) { $_ = $_[$i]; ($h,$r) = /(.*):(.*)/; $scores[$h] = $r; } # next button $page = $vars{"Page"}; $temp = "

Private Score

\n"; $script = $ENV{SCRIPT_NAME}; $temp .= "
\n"; $temp .= "\n"; if ($textonly) {$temp .= "\n";} $temp .= "\n"; $temp .= "
\n"; $temp .= "
\n"; # for each hand $nhands = 0; $totalmp = 0; $plusimp = 0; $minusimp = 0; $vps = 0; for ($i=$first;$i<=$last;$i++) { # start new match if ((!$vp20 && $i == $first) || ($vp20 && (($i-$first) % $vp20) == 0)) { # header if ($textonly) { $temp .= "
\n";
		$temp .= " Hand   Contract Decl. Score  +IMPs -IMPs\n";
		}
     	    else
		{
        	$temp .= "\n";
        	$temp .= "";
        	$temp .= "\n";
		}
  	    }
	# if no score, print an empty line
        if ($scores[$i] eq "") 
	    {
	    if ($textonly)
		{ $temp .= sprintf("   %s\n",$i); }
	    else
		{
                $temp .= "\n";
		}
	    }
	# otherwise, print a line with the score on it
        else
            {
            $_ = $scores[$i];
            ($c,$d,$r,$mp) = split(/\./);
	    if ($textonly)
		{
		if ($mp < 0)
		    {
	            $temp .= sprintf(" %3s       %-6s %2s  %6s       %4s\n",$i,$c,$d,$r,-$mp); 
		    }
	  	else
		    {
	            $temp .= sprintf(" %3s       %-6s %2s  %6s  %4s\n",$i,$c,$d,$r,$mp); 
		    }
		}
	    else
		{
	        if ($mp < 0)
		    {
		    $mp = -$mp;
		    $temp .= "";
                    $temp .= "";
	            $temp .= "\n";
		    $mp = -$mp;
		    }
	        else
		    {
		    $temp .= "";
                    $temp .= "";
	            $temp .= "\n";
		    }
		}
	    # compute total score
            $nhands++;
            $totalmp += $mp;
	    if ($mp > 0) {$plusimp += $mp;} else {$minusimp -= $mp;}
	    }
        # is this the end of a match?
	if ((!$vp20 && $i == $last) || (($vp20 && (($i - $first) % $vp20) == $vp20-1) && $nhands > 0))
	    {
	    # add trailer
     	    if ($textonly)
		{
		$temp .= sprintf("Total:                        %4s %4s\n",$plusimp,$minusimp);
		$temp .= "\n";
		}
     	    else
		{
        	$temp .= "";
        	$temp .= "";
		$temp .= "\n";
        	$temp .= "
HandContractDeclarerScoreIMPs
$i "; $temp .= "
$i CARD($c)$d$r $mp
$i CARD($c)$d$r$mp 
Total:$plusimp$minusimp
\n"; &bridge_trans($temp); } print $temp; $temp = ""; if (!$vp20 && $nhands > 0) { $zzz = $totalmp; if ($nhands == 1) {$junk = "board";} else {$junk = "boards";} $junk2 = "ahead"; if ($totalmp == 1 || $totalmp == -1) {$junk3 = "";} else {$junk3 = "s";} if ($totalmp == 0) {printf "After %d %s, you are exactly tied.\n",$nhands,$junk;} else { if ($totalmp < 0) {$totalmp = -$totalmp; $junk2 = "behind";} printf "After %d %s, you are %d IMP%s %s.\n",$nhands,$junk,$totalmp,$junk3,$junk2; } } # add VP computation if ($vp20) { print "You get ",&vp20($totalmp)," victory points in this match.

\n"; $vps += &vp20($totalmp); $totalmp = 0; $plusimp = 0; $minusimp = 0; $nhands = 0; } } } if ($vp20) { print "You have $vps victory points so far.\n"; if ($carryover) {printf "
With carryover, you have %g VPs.\n",$vps+$carryover;} } &html_trailer; } # This produces a hand records page sub handrecords { &html_header("Hand Records"); $page = $vars{"Page"}; $text = <<"END";

Hand Records

If you go to a board via the links on this page, you might not be able to get back here easily, so I suggest that you place a bookmark here for easy reference.


END if ($textonly) {$text .= "\n";} $text .= '
'."\n"; # insert the hands for ($i=$firstboard; $i<=$lastboard; $i += 2) { $iplusone = $i + 1; if ($iplusone > $lastboard) { $text .= "
\n"; $text .= "Board $i:
\n"; @_ = split(/\s+/,$savedboards{$i}); $h[0] = "VHAND($_[0],$_[1],$_[2],$_[3])"; $h[1] = "VHAND($_[4],$_[6],$_[8],$_[10])"; $h[2] = "VHAND($_[5],$_[7],$_[9],$_[11])"; $h[3] = "VHAND($_[12],$_[13],$_[14],$_[15])"; $text .= "HAND($h[0],\n$h[1],\n$h[2],\n$h[3])\n"; } else { $text .= "TWOHANDS(Board $i:, Board $iplusone:,\n"; @_ = split(/\s+/,$savedboards{$i}); $h[0] = "VHAND($_[0],$_[1],$_[2],$_[3])"; $h[1] = "VHAND($_[4],$_[6],$_[8],$_[10])"; $h[2] = "VHAND($_[5],$_[7],$_[9],$_[11])"; $h[3] = "VHAND($_[12],$_[13],$_[14],$_[15])"; $text .= "$h[0],\n$h[1],\n$h[2],\n$h[3],\n\n"; @_ = split(/\s+/,$savedboards{$iplusone}); $h[0] = "VHAND($_[0],$_[1],$_[2],$_[3])"; $h[1] = "VHAND($_[4],$_[6],$_[8],$_[10])"; $h[2] = "VHAND($_[5],$_[7],$_[9],$_[11])"; $h[3] = "VHAND($_[12],$_[13],$_[14],$_[15])"; $text .= "$h[0],\n$h[1],\n$h[2],\n$h[3])\n\n"; } } # add the links # find filename $file = $ENV{'SCRIPT_NAME'}; if ($textonly) { for ($i=$firstboard;$i<=$lastboard;$i++) { $text =~ s|(Board $i):|$1:|; } } else { for ($i=$firstboard;$i<=$lastboard;$i++) { $text =~ s|(Board $i):|$1:|; } } &bridge_trans($text); print $text; &html_trailer; } # Bridge formatting stuff # hide_parens converts \( and \) into QUOTED_PAREN and QUOTED_THESIS sub hide_parens { $_[0] =~ s/\\\(/__QUOTED_PAREN__/g; $_[0] =~ s/\\\)/__QUOTED_THESIS__/g; } # replace_parens reverses above and removes escapes sub replace_parens { $_[0] =~ s/__QUOTED_PAREN__/(/g; $_[0] =~ s/__QUOTED_THESIS__/)/g; } # this takes a string and does replacement sub bridge_trans { &hide_parens($_[0]); # user-defined macros foreach $key (keys %macros) { $_[0] =~ s/($key)\(([^\)]*)\)/¯o_sub($macros{$key},$2)/ge; $_[0] =~ s/$key/$macros{$key}/g; } $_[0] =~ s/SUIT\(([^)]*)\)/&suit($1)/ge; $_[0] =~ s/CARD\(([^)]*)\)/&card($1)/ge; $_[0] =~ s/FOOTNOTE\(([^)]*)\)/&footnote($1)/ge; $_[0] =~ s/HHAND\(([^)]*)\)/&hhand($1)/ge; $_[0] =~ s/BIDDING\(([^)]*)\)/&bidding($1)/ge; $_[0] =~ s/BID4\(([^)]*)\)/&bid4($1)/ge; $_[0] =~ s/VHAND\(([^)]*)\)/&vhand($1)/ge; $_[0] =~ s/HAND\(([^)]*)\)/&hand($1)/ge; $_[0] =~ s/TWOHANDS\(([^)]*)\)/&twohands($1)/ge; $_[0] =~ s/PANELIST\(([^)]*)\)/&panelist($1)/ge; $_[0] =~ s/\\'/'/g; &replace_parens($_[0]); } # this replaces 1H, 2S, CA, etc. with appropriate stuff sub card { $_ = $_[0]; if ($textonly) {return $_;} if (/Dbl/ || /Double/) { ''.$_.''; } elsif (/^S/) { s/^.//; 'S:'.$_.''; } elsif (/^H/) { s/^.//; 'H:'.$_.''; } elsif (/^D/) { s/^.//; 'D:'.$_.''; } elsif (/^C/) { s/^.//; 'C:'.$_.''; } elsif (/S$/) { s/.$//; ''.$_.'S:'; } elsif (/H$/) { s/.$//; ''.$_.'H:'; } elsif (/D$/) { s/.$//; ''.$_.'D:'; } elsif (/C$/) { s/.$//; ''.$_.'C:'; } elsif (/Sx$/ || /SX$/) { s/..$//; ''.$_.'S:x'; } elsif (/Hx$/ || /HX$/) { s/..$//; ''.$_.'H:x'; } elsif (/Dx$/ || /DX$/) { s/..$//; ''.$_.'D:x'; } elsif (/Cx$/ || /CX$/) { s/..$//; ''.$_.'C:x'; } elsif (/Sxx$/ || /SXX$/) { s/...$//; ''.$_.'S:xx'; } elsif (/Hxx$/ || /HXX$/) { s/...$//; ''.$_.'H:xx'; } elsif (/Dxx$/ || /DXX$/) { s/...$//; ''.$_.'D:xx'; } elsif (/Cxx$/ || /CXX$/) { s/...$//; ''.$_.'C:xx'; } elsif (/^[1234567][Ss]/) { s/(.)./$1S:/; ''.$_.''; } elsif (/^[1234567][Hh]/) { s/(.)./$1H:/; ''.$_.''; } elsif (/^[1234567][Dd]/) { s/(.)./$1D:/; ''.$_.''; } elsif (/^[1234567][Cc]/) { s/(.)./$1C:/; ''.$_.''; } else { ''.$_.''; } } # hhand draws a horizontal hand # it takes four arguments: spades, hearts, diamonds, clubs sub hhand { @args = split(/,/,$_[0]); if ($textonly) { return ' '. "$args[0]  $args[1]  $args[2]  $args[3]". ''; } ' S:'.$args[0]. ' H:'.$args[1]. ' D:'.$args[2]. ' C:'.$args[3].''; } # vhand draws a vertical hand # it takes four arguments: spades, hearts, diamonds, clubs sub vhand { @args = split(/,/,$_[0]); if ($textonly) { return "$args[0]
$args[1]
$args[2]
$args[3]"; } 'S: '.$args[0].'
'. 'H: '.$args[1].'
'. 'D: '.$args[2].'
'. 'C: '.$args[3]; } # hand draws a full hand diagram # it takes four arguments, North, West, East, South sub hand { local(@north,@south,@east,@west,$argument); $argument = $_[0]; $argument =~ s/\n//g; @args = split(/,/,$argument); if ($textonly) { @north = split(/\/,$args[0]); @west = split(/\/,$args[1]); @east = split(/\/,$args[2]); @south = split(/\/,$args[3]); if (@north) {$north = "North";} else {$north = "";}; if (@west ) {$west = "West ";} else {$west = " ";}; if (@east ) {$east = "East ";} else {$east = "";}; if (@south) {$south = "South";} else {$south = "";}; $temp = "
\n";
	$temp .= "           $north\n";
	$temp .= "           $north[0]\n";
	$temp .= "           $north[1]\n";
	$temp .= "           $north[2]\n";
	$temp .= "           $north[3]\n";
	$temp .= "$west                  $east\n";
	$temp .= sprintf("%-14s         %-14s\n",$west[0],$east[0]);
	$temp .= sprintf("%-14s         %-14s\n",$west[1],$east[1]);
	$temp .= sprintf("%-14s         %-14s\n",$west[2],$east[2]);
	$temp .= sprintf("%-14s         %-14s\n",$west[3],$east[3]);
	$temp .= "           $south\n";
	$temp .= "           $south[0]\n";
	$temp .= "           $south[1]\n";
	$temp .= "           $south[2]\n";
	$temp .= "           $south[3]\n";
	$temp .= "
\n"; $temp; } else { "\n". "\n". "\n". "\n". "
".$args[0]."
".$args[1]."".$args[2]."
".$args[3]."
\n"; } } # twohands draws two full hand diagrams, horizontally on the page # it takes 10 arguments: words, words, hand, hand sub twohands { local(@north,@south,@east,@west,$argument); $argument = $_[0]; $argument =~ s/\n//g; @args = split(/,/,$argument); if ($textonly) { @north1 = split(/\/,$args[2]); @west1 = split(/\/,$args[3]); @east1 = split(/\/,$args[4]); @south1 = split(/\/,$args[5]); @north2 = split(/\/,$args[6]); @west2 = split(/\/,$args[7]); @east2 = split(/\/,$args[8]); @south2 = split(/\/,$args[9]); $north = "North"; $west = "West "; $east = "East "; $south = "South"; $temp = "
\n"; $temp .= "
\n";
	$temp .= sprintf("%-25s                                |",$args[0]);
	$temp .= sprintf("%-25s         \n",$args[1]);
	$temp .= "           $north                        |           $north\n";
	$temp .= sprintf("           %-14s               |",$north1[0]);
	$temp .= sprintf("           %-14s              \n",$north2[0]);
	$temp .= sprintf("           %-14s               |",$north1[1]);
	$temp .= sprintf("           %-14s              \n",$north2[1]);
	$temp .= sprintf("           %-14s               |",$north1[2]);
	$temp .= sprintf("           %-14s              \n",$north2[2]);
	$temp .= sprintf("           %-14s               |",$north1[3]);
	$temp .= sprintf("           %-14s              \n",$north2[3]);
	$temp .= "$west                  $east            |  ";
	$temp .= "$west                  $east\n";
	$temp .= sprintf("%-14s         %-14s   |  ",$west1[0],$east1[0]);
	$temp .= sprintf("%-14s         %-14s \n"   ,$west2[0],$east2[0]);
	$temp .= sprintf("%-14s         %-14s   |  ",$west1[1],$east1[1]);
	$temp .= sprintf("%-14s         %-14s \n"   ,$west2[1],$east2[1]);
	$temp .= sprintf("%-14s         %-14s   |  ",$west1[2],$east1[2]);
	$temp .= sprintf("%-14s         %-14s \n"   ,$west2[2],$east2[2]);
	$temp .= sprintf("%-14s         %-14s   |  ",$west1[3],$east1[3]);
	$temp .= sprintf("%-14s         %-14s \n"   ,$west2[3],$east2[3]);
	$temp .= "           $south                        |           $south\n";
	$temp .= sprintf("           %-14s               |",$south1[0]);
	$temp .= sprintf("           %-14s              \n",$south2[0]);
	$temp .= sprintf("           %-14s               |",$south1[1]);
	$temp .= sprintf("           %-14s              \n",$south2[1]);
	$temp .= sprintf("           %-14s               |",$south1[2]);
	$temp .= sprintf("           %-14s              \n",$south2[2]);
	$temp .= sprintf("           %-14s               |",$south1[3]);
	$temp .= sprintf("           %-14s              \n",$south2[3]);
	$temp .= "
\n"; $temp; } else { "\n". "\n". " \n". "\n". "\n". " \n". " \n". "\n". " \n". " \n". "\n". " \n". " \n". "


$args[0]$args[1]
".$args[2]."      ".$args[6]."
".$args[3]."".$args[4]."      ".$args[7]."".$args[8]."
".$args[5]."      ".$args[9]."
\n"; } } # Two player bidding sub bidding { @args = split(/,/,$_[0]); if ($textonly) { $output = "
\n";
	$max = $#args+1;
	for ($i = 0; $i <$max;$i+= 2)
	    {
	    $output .= sprintf("%-8s %-8s\n",$args[$i],$args[$i+1]);
	    }
        $output .= "
\n"; $output; } else { $output = "\n"; # first two args are players $output .= '\n"; # remaining args are bids $max = $#args+1; for ($i=2;$i<$max;$i+=2) { $output .= '\n"; } $output .= "
'; $output .= $args[0]; $output .= ''; $output .= $args[1]; $output .= "
'.&card($args[$i]). ''.&card($args[$i+1])."
\n"; $output; } } # four player bidding sub bid4 { local($td1,$td2,$td3); @args = split(/,/,$_[0]); if ($textonly) { $output = "
\n";
	$max = $#args+3;
        for ($i=0;$i<$max;$i+=4)
	    {
	    $output .= sprintf("%-8s %-8s %-8s %-8s\n",$args[$i],$args[$i+1],$args[$i+2],$args[$i+3]);
	    }
	$output .= "
\n"; $output; } else { $output = "\n"; # first four args are players $output .= '\n"; $td1 = "td"; $td2 = "td"; $td3 = "td"; # remaining args are bids $max = $#args+3; for ($i=4;$i<$max;$i+=4) { # handle "All pass" well. if ($args[$i ] =~ /All\s*Pass/i) {$td1 = "td colspan = 4";} if ($args[$i+1] =~ /All\s*Pass/i) {$td2 = "td colspan = 3";} if ($args[$i+2] =~ /All\s*Pass/i) {$td3 = "td colspan = 2";} $output .= "<$td1>". &card($args[$i]). "<$td2>".&card($args[$i+1]). "<$td3>".&card($args[$i+2]). "\n"; } $output .= "
'; $output .= $args[0]; $output .= ''; $output .= $args[1]; $output .= ''; $output .= $args[2]; $output .= ''; $output .= $args[3]; $output .= "
".&card($args[$i+3]). "
\n"; $output; } } # suit draws a two-player suit combination sub suit { @args = split(/,/,$_[0]); if ($textonly) { $output = "
\n";
	$output .= "Dummy\n";
	$output .= "$args[0]\n";
	$output .= "

\n"; $output .= "Declarer\n"; $output .= "$args[1]\n"; $output .= "

\n"; $output; } else { "\n". "\n". "\n". "\n". "\n". "\n". "
Dummy
CARD(".$args[0].")
Declarer
CARD(".$args[1].")
\n"; } } # Footnote draws superscripts sub footnote { if ($textonly) { # kludge, but works "__QUOTED_PAREN__$_[0]__QUOTED_THESIS__ "; } else { "".$_[0].""; } } # Panelist is strange sub panelist { $_[0] =~ tr/a-z/A-Z/; # upcase $init = $_[0]; $rest = $_[0]; $init =~ s/(.).*/$1/; $rest =~ s/.//; "
".$init."".$rest."
\n"; } # split arguments # Remove quotes, but keep spaces in quotes sub split_args { # this works by changing quotes in strings to __SP__ # then changing them back after the split. local(@args,$string,$qlevel); # are we in a string? $qlevel = 0; # 0 = no # by character @args = split(//,$_[0]); for ($i=0;$i<@args;$i++) # for each character { if ($args[$i] eq '"') { $qlevel = !$qlevel; } elsif ($args[$i] eq ' ') { if ($qlevel) {$args[$i] = "__SP__" ;} } } # stick it all back together $string = join("",@args); # remove quotes $string =~ s/"//g; # split on white space @args = split(/\s+/,$string); for ($i=0;$i<@args;$i++) { $args[$i] =~ s/__SP__/ /g; } @args; } # clean up sub done { close(MOVIEFILE); exit(0); } # errors sub error { &html_header("Error"); print "Sorry, an unrecoverable error occurred:\n"; print $_[0]; &html_trailer; exit(0); } # reasonably pretty date for last creation time. # This might be different from system to system. sub lmdate { $_ = &ctime((stat($_[0]))[9]); # default is last modified date of file split(/[\s-]+/,$_); # PC date # Fri 03-22-1996 # UNIX date # Fri Mar 22 10:16:38 PST 1996 if ($unix == 0) { $_[1] = $month[$_[1]]; } if ($_[5] =~ /19/ || $_[5] =~ /20/) { &fullday($_[0]).", ".$_[1]." ".$_[2].", ".$_[5]; } elsif ($_[4] =~ /19/ || $_[4] =~ /20/) # some ctimes don't supply time zone { &fullday($_[0]).", ".$_[1]." ".$_[2].", ".$_[4]; } elsif ($_[3] =~ /19/ || $_[3] =~ /20/) { &fullday($_[0]).", ".$_[1]." ".$_[2].", ".$_[3]; } else {$_;} } sub fullday { if ($_[0] =~ /Mon/i) { "Monday";} elsif ($_[0] =~ /Tue/i) { "Tuesday";} elsif ($_[0] =~ /Wed/i) { "Wednesday";} elsif ($_[0] =~ /Thu/i) { "Thursday";} elsif ($_[0] =~ /Fri/i) { "Friday";} elsif ($_[0] =~ /Sat/i) { "Saturday";} elsif ($_[0] =~ /Sun/i) { "Sunday";} else { $_[0]; } } # this does macro substitution # The arguments are the replacement string and # argument list. sub macro_sub { local($replacement,@args,$i); $replacement = $_[0]; @args = split(/,/,$_[1]); unshift(@args,""); for ($i=1;$i<=@args;$i++) { $replacement =~ s/\$$i/$args[$i]/g; } $replacement; } # Compute imp results diff = imps(a,b) sub imps { local($diff); if ($imppairs) # subtract datum score {$diff = $_[0] - $_[1];} else # add teammates' result {$diff = $_[0] + $_[1];} if ($diff == 0) { 0; } elsif ($diff > 0) { if ($diff < 20 ) { 0; } elsif ($diff < 50 ) { 1; } elsif ($diff < 90 ) { 2; } elsif ($diff < 130 ) { 3; } elsif ($diff < 170 ) { 4; } elsif ($diff < 220 ) { 5; } elsif ($diff < 270 ) { 6; } elsif ($diff < 320 ) { 7; } elsif ($diff < 370 ) { 8; } elsif ($diff < 430 ) { 9; } elsif ($diff < 500 ) { 10; } elsif ($diff < 600 ) { 11; } elsif ($diff < 750 ) { 12; } elsif ($diff < 900 ) { 13; } elsif ($diff < 1100) { 14; } elsif ($diff < 1300) { 15; } elsif ($diff < 1500) { 16; } elsif ($diff < 1750) { 17; } elsif ($diff < 2000) { 18; } elsif ($diff < 2250) { 19; } elsif ($diff < 2500) { 20; } elsif ($diff < 3000) { 21; } elsif ($diff < 3500) { 22; } elsif ($diff < 4000) { 23; } else { 24; } } else { $diff = -$diff; if ($diff < 20 ) { -0; } elsif ($diff < 50 ) { -1; } elsif ($diff < 90 ) { -2; } elsif ($diff < 130 ) { -3; } elsif ($diff < 170 ) { -4; } elsif ($diff < 220 ) { -5; } elsif ($diff < 270 ) { -6; } elsif ($diff < 320 ) { -7; } elsif ($diff < 370 ) { -8; } elsif ($diff < 430 ) { -9; } elsif ($diff < 500 ) { -10; } elsif ($diff < 600 ) { -11; } elsif ($diff < 750 ) { -12; } elsif ($diff < 900 ) { -13; } elsif ($diff < 1100) { -14; } elsif ($diff < 1300) { -15; } elsif ($diff < 1500) { -16; } elsif ($diff < 1750) { -17; } elsif ($diff < 2000) { -18; } elsif ($diff < 2250) { -19; } elsif ($diff < 2500) { -20; } elsif ($diff < 3000) { -21; } elsif ($diff < 3500) { -22; } elsif ($diff < 4000) { -23; } else { -24; } } } # compute VPs on the standard 20 VP scale sub vp20 { local($diff); $diff = $_[0]; if ($vp20 >= 9 && $vp20 <= 11) { if ($diff <= -36) {0;} elsif ($diff <= -31) {1;} elsif ($diff <= -26) {2;} elsif ($diff <= -22) {3;} elsif ($diff <= -18) {4;} elsif ($diff <= -14) {5;} elsif ($diff <= -10) {6;} elsif ($diff <= -6 ) {7;} elsif ($diff <= -3 ) {8;} elsif ($diff <= -1 ) {9;} elsif ($diff <= 0 ) {10;} elsif ($diff <= 2 ) {11;} elsif ($diff <= 5 ) {12;} elsif ($diff <= 9 ) {13;} elsif ($diff <= 13) {14;} elsif ($diff <= 17) {15;} elsif ($diff <= 21) {16;} elsif ($diff <= 25) {17;} elsif ($diff <= 30) {18;} elsif ($diff <= 35) {19;} else {20;} } else { if ($diff <= -28) {0;} elsif ($diff <= -24) {1;} elsif ($diff <= -20) {2;} elsif ($diff <= -17) {3;} elsif ($diff <= -14) {4;} elsif ($diff <= -11) {5;} elsif ($diff <= -8 ) {6;} elsif ($diff <= -5 ) {7;} elsif ($diff <= -3 ) {8;} elsif ($diff <= -1 ) {9;} elsif ($diff <= 0 ) {10;} elsif ($diff <= 2 ) {11;} elsif ($diff <= 4 ) {12;} elsif ($diff <= 7 ) {13;} elsif ($diff <= 10) {14;} elsif ($diff <= 13) {15;} elsif ($diff <= 16) {16;} elsif ($diff <= 19) {17;} elsif ($diff <= 23) {18;} elsif ($diff <= 27) {19;} else {20;} } } sub printleaders { local($name,$xxx,$index,$score,@leaders); local($first,$last); $xxx = ""; # we need to compute scores; remember, we may have played any of these teams if ($vp20) { # save arguments $first = $firstboard; $last = $lastboard; # errors if ($first > $last) {$first = 1; $last = 26;} # handle scores $_ = $scores; s/Appeal;//g; # don't worry about appeals split(/;/,$_); for ($i=0;$i<@_;$i++) { $_ = $_[$i]; ($h,$r) = /(.*):(.*)/; $scores[$h] = $r; } # for each hand $nhands = 0; $totalmp = 0; $vps = 0; for ($i=$first;$i<=$last;$i++) { # compute score if ($scores[$i] ne "") { $_ = $scores[$i]; ($c,$d,$r,$mp) = split(/\./); $nhands++; $totalmp += $mp; } # is this the end of a match? if (((($i - $first) % $vp20) == $vp20-1) && $nhands > 0) { # add VP computation if ($vp20) { $vps += &vp20($totalmp); # add remainder of 20 to opponent $thisopp = $opp[$i-$first]; $theirscore = 20 - &vp20($totalmp); $teamscore{$thisopp} += $theirscore; $totalmp = 0; $nhands = 0; } } } } # add us in to the leader list $opponent{"0"} = "You"; $teamscore{"0"} = $vps + $carryover; # table header $xxx .= "\n"; # sort by score @leaders = reverse sort {$teamscore{$a} <=> $teamscore{$b}} keys(%teamscore); for ($i = 0;$i < @leaders; $i++) { $index = $i + 1; $name = $opponent{$leaders[$i]}; $score = $teamscore{$leaders[$i]}; $xxx .= sprintf("\n", $index,$name,$score); } # table trailer $xxx .= "
%d%s%g
\n"; # add congrats if he won if ($leaders[0] == 0) { $xxx .= '

Congratulations!!!

You Won!!!

'; $xxx .= "\n"; } $xxx; }