This is meant to be a comprehensive list of commands in Deal which are not native to Tcl. If you want to learn Tcl, I will be providing a set of pointers later. |
Text in blue is meant for Tcl afficianado
- reading this text without knowledge of Tcl might confuse more than
enlighten.
Text in green is meant for programmers
interested in finding code definitions.
|
north
, east
, south
, west
, hand
Usageeast [ -void string] [ suitname ... ] hand {hand string} [ -void string] [ suitname ... ] south subcommand [ ... args ... ] hand {hand string} subcommand [ ... args ... ]These are very strange commands - they really have too much stuffed into them. That's a historical anomoly which I'm reluctant to abandon. With no subcommands, the routine is used for formatting the hand as a string. For example, if south is: S: AJ5432 H: KT94 D: void C: K93Then the results of various commands are: south => {AJ5432 KT94 {} K93} south spades => AJ5432 south hearts clubs => {KT94 K93} south -void - => {AJ5432 KT94 - K93} south -void --- diamonds => --- set h {AK4 {} A95432 JT98} hand $h => {AK4 {} A95432 JT98} hand $h spades => AK4 hand $h hearts clubs => {{} JT98} hand $h -void - => {AK4 - A95432 JT98}The -void switch exists precisely for formatting the output.
The various subcommands will be treated in seperate sections.
The |
For Programmers
|
Subcommand:
|
accept
and reject
Usageaccept [ [ if | unless ] expr expr ...] reject [ [ if | unless ] expr expr ...] SummaryWithout arguments,accept and reject are the
same as return 1 and return
0 , respectively.
With arguments it's a little more obscure. accept if {$h>4}This returns 1 if the expression matches. It is equivalent to: if {$h>4} { return 1 }The code: accept if {$h>4} {$s>4}Is logically equivalent to: if {$h>4 || $s>4} { return 1}That is, the values are accepted if either of the expressions evaluates as true.
The accept unless {$h>4} {$s>4}This is equivalent to: if {!($h>4) || !($s>4)} { return 1 } The means we return with a true value unless one of the expressions is true. If one of the values is true, we simply go on to the next line.
ExamplesVirtually all of the examples included in the release contain an instance of "accept" or "reject." |
For Programmers
This construct is borrowed from the Perl programming language. Originally, I added it to Deal 2.0 for performance reasons. Those reasons are obsolote with Tcl 8.x, but I like the mnemonic enough to keep it, and removing it might confuse old users.
For Tcl Experts
There actually is one subtle and occasionally useful distinction
between if {...} { return ... } version. In reality: accept if {$h>4} {$s>4} is equivalent to: if {$h>4||$s>4} { return 1 } expr 0This only matters when the command is the last command in a procedure. The two procedures: proc A {h s} { if {$h>4||$s>4} { return 1 } } proc B {h s} { accept if {$h>4} {$s>4} } are slightly different. A call of |
main
Usagemain { block of code } SummaryThis defines the primary block of code evaluated after each deal is generated. If the code returns true, Deal will treat the deal as a "match" and call write_deal. It will also increment its count of deals found and exit if the limit is reached. |
For Programmers
|
whogets
Usagewhogets cardnameThis returns the name of the person holding the card cardname. |
For Programmers
|
deal_finished
Usagedeal_finished { block of code }This defines the code called at the completion of generating all deals. This is code you would use to dump statistics, for example. |
For Programmers
|
Weak2Bid handname Weak2Bid hand {AKQxxx xxx xxx x}The handname parameter can be one of
north
, south
, east
, west
.
Shape procedures and holding
procedures fit the hand procedure calling method, along with other
options.
balanced handname balanced hand {AJxx xxx AKxx Kx} balanced eval 4 3 4 2 balanced shape {4 3 4 2} semibalanced handname semibalanced hand {AJ432 AKx Kx xxx} semibalanced eval 5 3 2 3 semibalanced shape {5 3 2 3}This follows the hand procedure outline with the addition of the
eval
option.
defvector
and
holdingProc
.
This is an
abstraction of a very common bridge evaluation technique. The most
commonly used holding functions are high card points,
losing trick count, controls. For example, when
counting the losers in the hand AKxx Axx KQJxxx x
, we
find 1 loser in spades, 2 losers in hearts, 1 loser in diamonds,
and one loser in clubs. We define the total losers to be the sum
across all suits, and get 5 losers, total.
The interface lets you evaluate the entire hand, or only selected suits in a hand, or a specific list of holdings.
hcp handname [ suitname ... ] hcp hand {AKJxxx K xxx xxx} [ suitname ... ] hcp holding AK43 [ ... ]In the first two cases, if no suits are named, all suits are evaluated, and the results are accumulated. In the case of the
holding
call,
the specific holdings passed in are evaluated.
Note, I've been saying "accumulated" rather than added. In some cases, you might have an accumulation method different from addition. For example, you might return the standard opening leads from a holding:
openingLead holding KQJ7 => K openingLead south => {K} {7 2} {4} {J}Each suit would create a list of proposed leads, and the result would be a list of lists of all standard leads from all holdings. Here, the accumulation is to simply make a list.
balanced
and semibalanced
Usagebalanced handname balanced hand {text hand} balanced eval s h d c SummaryThese are both shape procedures.
|
For Programmers
These use to be hard-coded in the C code, but it made more sense to add them to the library code - the user can change the definitions as he likes. |
clubs
, diamonds
, hearts
, spades
UsageThese implement the hand procedure interface, and return the suit lengths for the appropriate suit. |
For Programmers
These could be shape procedures but for speed reasons, I've left them this way. |
hcp
Usagehcp handname hcp handname suitname [suitname ...] hcp hand {s h d c} hcp hand {s h d c} suitname [suitname ...]This procedure computes standard high card points - ace is 4, king is 3, queen is 2, jack is 1. The procedure implements the holding procedure interface. |
For Programmers
|
newLTC
UsagenewLTC handname newLTC handname suitname [suitname ...] newLTC hand {s h d c} newLTC hand {s h d c} suitname [suitname ...] Rex Livingston provided this implementation of the New Losing Trick Count, from a 2003 Bridge World article. It looks to me much like a 3-2-1 count with some adjustments, and it seems odd to me that a stiff singleton would count as 1.5 losers. Somehow, doesn't match the intuitive meaning of "loser" to me. |
For Programmers
|
losers
Usagelosers handname losers handname suitname [suitname ...] losers hand {s h d c} losers hand {s h d c} suitname [suitname ...]This implements a holding procedure which computes the number of half losers. This is for historical reasons - the only holding functions allowed in past releases returned integer values, but I wanted to have some refinements over raw losing trick count. |
For Programmers
Tcl using
holdingProc .
|
Binky Points are described in my hand evaluations articles.
Binky Points are designed so that sum of your partnership's Binky Point values comes as close as possible to the number of tricks that your partnership can make, double dummy.
binky::suit
and binky::nt
Usagesource lib/binky.tcl binky::nt handname binky::nt hand {s h d c} binky::suit handname binky::suit hand {s h d c}These two functions are hand procedures which computes the Binky Point values for a hand. |
For Programmers
|
lho
, partner
, rho
Usagelho handname rho handname partner handname SummaryThese routines accept one hand name and return another. For example, "lho north " returns "east ," and
"partner east "
returns "west ."
|
For Programmers
|
holding
Usageholding length AJxx => 4 holding contains AJ64 A6 => 1 (true) holding contains AJ64 A65 => 0 (false) holding encode AJ4 => 4612 (binary: 1001000000100 ) holding decode 4612 => AJ4 holding matches AKxx AK42 => 1 (true) holding disjoint AJ65 KT82 => 1 (true) holding disjoint A65 A32 => 0 (false) holding index AKJ4 0 => A holding index AKJ4 1 => K holding index AKJ4 3 => 4 holding index AKJ4 -2 => J holding index AKJ4 10 => "" Summary |
For Programmers
|
score
Usagesource lib/score.tcl ... score contract vulnerability tricks SummaryThis routine computes the standard duplicate bridge score for a contract from the point of view of declarer, if declaring side takes the given number of tricks.The contract is passed as a list: 2 spades 2 spades undoubled 4 clubs doubled 6 notrump redoubledThe vulnerablity is one of the words vul or
nonvul .
The tricks is the number of tricks taken by declarer. Examplesscore {2 spades} nonvul 8 => 110 score {2 spades doubled} nonvul 8 => 470 score {2 spades redoubled} nonvul 8 => 640 score {2 spades doubled} vul 8 => 670 score {2 spades} nonvul 7 => -50 score {2 spades doubled} nonvul 7 => -100 score {2 spades doubled} vul 7 => -200 |
For Programmers
|
Starting with Deal 3.1, I've included simple access to Bo Haglund's double-dummy solver library.
deal::tricks
Usagedeal::tricks declarer denomination Summary
declarer must be one of "north", "east", "south", or "west." By default, it is "south." denomination must be a suit name or "notrump." The default value is "notrump." By default, deal::tricks uses the However, if you include "lib/gib.tcl",
Bo Haglund's solver has a feature which makes it faster if the next double dummy solver call is in the same denomination as the previous call. That means that if you need all double-dummy values, you would want to write your code as: foreach denom {clubs diamonds hearts spades notrump} { foreach hand {north east south west} { set tricks [deal::tricks $hand $denom] ... } } |
For Programmers
|
Usagetricks [ declarer [ denomination [ goal ] ] ] SummaryUnless you are using the goal parameter, you will probably want to use the When no goal is passed, "tricks" returns the maximum number of tricks makable by declarer in the given denomination. If a (positive) goal number of tricks is set, "tricks" returns 1 if declarer can make that many tricks, and 0 if the defense can always hold declarer to fewer tricks. declarer must be one of "north", "east", "south", or "west." By default, it is "south." denomination must be a suit name or "notrump." The default value is "notrump." goal must be an integer value, either -1 for "best play" or a value from 1 to 13 equal to the goal number of tricks for declarer. Default is -1. |
For Programmers
|
Usagedds [-reuse | -noreuse] [-diagram diagram] [-goal goal] [-leader leader] [-trick list of cards [-trick cards ] ...] [ declarer [ denomination ] ] SummaryThe The The The The The For example, to solve George Coffin's "Avoidance" end position from his Great 88, you would write: set diagram { {A987 - A -} {Q5 987 - -} {64 K2 2 -} {K2 AQ3 - -} } set tricks [dds -leader south -diagram $diagram south notrump] ... If you wanted to see how many tricks are made if south plays a particular card, say the two of clubs, you would use the -trick flag: set tricks [dds -leader south -diagram $diagram -trick 4S south notrump]You can also pass full tricks, as in: set tricks [dds -leader west -diagram $diagram -trick {4S KS 7S 5S} south notrump] Note that the -leader option always refers to the leader to the current trick, not the leader of earlier completed tricks passed with -trick. Also, the number of tricks returned is the number of tricks after all completed tricks. There are example tests of all the Great 88 in the file |
For Programmers
|
parscore
Usagesource lib/parscore.tcl ... set result [parscore dealer vul] set contract [lindex $result 0] set declarer [lindex $result 1] set score [lindex $result 2] set tricks [lindex $result 3] set auction [lindex $result 4] SummaryThis computes the double-dummy par result for the current deal.
The parscore routine returns a list of five values - the contract (in
the same form that is used by the Dealer is relevant on a few occasions. If both south and west can make 1NT, for example, and no 2-level contracts make, then "par" is 1NT by whomever gets a chance to bid it first. | For Programmers
|
gib::directory
Usagesource lib/gib.tcl ... gib::directory path SummaryThis tells the GIB-related routines where GIB is installed.
If this routine is not called, it defaults to GIB's default install
directory,
Note: your path should include forward slashes ' | For Programmers
|
gib::tricks
Usagesource lib/gib.tcl ... gib::tricks declarer denomination SummaryYou probably want to use the Denomination can be a suit name or " This routine returns the double-dummy number of tricks available in this deal, in the given denomination by the given declarer.
If GIB is installed anywhere unusual, you will need to call
| For Programmers
|
A number of common types of bridge functions can easily be implemented to run quickly via lookup tables, including holding and shape procedures. Deal lets the user take advantage of these two sorts of lookup procedures relatively easily
defvector
Usagedefvector vec aceVal [ kingVal ... ] vec handname vec SummaryThis defines a holding procedure which assigns integer values to the various cards in the hand. For exampledefvector hcp6421 6 4 2 1Defines a holding procedure which counts the ace as worth 6 points, the king as worth 4, the queen as 2, and the jack as 1.
Vectors are limited to being integer-valued. For more complicated
holding procedures, use |
For Programmers
Vectors are slighly faster than their equivalent
|
holdingProc
UsageTemporarily, see the seperate documentation. | For Programmers
|
shapeclass
, shapecond
, and shapefunc
Usageshapeclass name {... code ...} shapecond name {expr} shapefunc name {... code ...} name [ north | south | east | west ] name eval s h d c name shape {s h d c}A shape class (or equivalently, a shape condition) also has the list subcommand: name list SummaryThese commands create new procedures which fit the shape function interface.
shapecond name {expr}is the equivalent of: shapeclass name { if {expr} { return 1 } else { return 0 }The code or expr is allowed to use the variables s , h , d , or c .
More details can be found in the Advanced Guide.
The Why are there two subcommands, "eval" and "shape" which do the roughly the same things? Let's say you have a class named "SemiBalanced" already defined, which includes 5-card majors. You want to define a "Balanced" class which excludes the 5-card majors. You can do this with the call: shapecond Balanced {[SemiBalanced eval $s $h $d $c] && $s<5 && $h<5}On the other hand, if you have a shape - output by the, say, a call to [north shape] you can evaluate it as: SemiBalanced shape [north shape]In fact, this is exactly equivalent to calling SemiBalanced northonly slightly slower. Since these routines cache results in memory and use lookup tables, you should not use any external state or global variables inside them. |
For Programmers
|
patternclass
, patterncond
, and patternfunc
Usagepatternclass name {... code ...} patterncond name {expr} patternfunc name {... code ...} name [ north | south | east | west ] name eval s h d c name shape {s h d c}A pattern class (or equivalently, a shape condition) also has the list subcommand: name list SummaryThese commands create new procedures which fit the shape function interface.
patterncond name {expr}is the equivalent of: patternclass name { if {expr} { return 1 } else { return 0 }The code or expr is allowed to use the variables l1 , l2 , l3 , or l4 , the suit lengths in descending order.
The Why are there two subcommands, "eval" and "shape" which do the roughly the same things? Let's say you have a class named "SemiBalanced" already defined, which includes 5-card majors. You want to define a "Balanced" class which excludes the 5-card majors. You can do this with the call: shapecond Balanced {[SemiBalanced eval $s $h $d $c] && $s<5 && $h<5}On the other hand, if you have a shape - output by the, say, a call to [north shape] you can evaluate it as: SemiBalanced shape [north shape]In fact, this is exactly equivalent to calling SemiBalanced northonly slightly slower. Since these routines cache results in memory and use lookup tables, you should not use any external state or global variables inside them. |
For Programmers
|
sdev
Usagesdev name name add data [ data ... ] name count name average name sdev name rms SummaryThe "sdev" command defines a new Tcl procedure with the given name, which behaves as a data collection object. You can add data points to it, or you can retrieve the current number of data points, the average of the data points, the standard deviation of the data points, or the "root mean square" of the data points.
|
For Programmers
This was implemented in C for efficiency reasons - most real number computations really need to be done in C if they are going to be done frequently, and here, the "add" command is called quite often in normal usage. |
correlation
Usagecorrelation name name add xval yval [ xval yval ... ] name count name correlate name average [ x | y ] name sdev [ x | y ] name rms [ x | y ] SummaryThecorrelation declaration defines a routine much like
the sdev command, but each datapoint is a pair of values,
and it computes the linear correlation between the x and y
values.
You can also get individual data for the
If there is no data, it returns an error on
If there is only one pair entered, it will throw an error on
|
For Programmers
This was implemented in C for efficiency reasons - most real number computations really need to be done in C if they are going to be done frequently, and here, the "add" command is called quite often in normal usage. |
Usagedeal -x filename The '-x' flag tells deal to source the file and then exit. Before doing so, Deal puts remaining arguments into the variables This is useful if you want to skip the entire deal loop, and process stuff on your own. For example, the code in |
full_deal
Usageset deal [full_deal] This simple utility command returns the deal as a list of hands in the order of north, east, south, and west. Suitable for psassing into |
seed_deal
Usageseed_deal number Command Line: -s$ deal -s number ... Summary
Care has to be taken if you want to make sure that you are generating the same sequence of deals. For one thing, if you have a main { finish_deal ... } That bypasses some optimizations and forces an entire deal to be generated each time through |
deal_deck
Usagedeal_deck Summarydeal_deck is called at every new deal,
even when all 52 cards are specified (stacked) precisely.
Imagine stacking cards as stacking them in an undealt deck,
but that the cards are only distributed to the hands when
deal_deck is called.
Most of the time, you won't need to call deal_deck directly, but it is useful to understand its behavior if you are going to write advanced procedures like input formats. Stacked cards are permanently stacked until the deck is reset. In this code: stack_hand south {AKQ32 AJ53 K54 2} deal_deck deal_deck puts [south spades]The output is "AKQ32". Use reset_deck
to undo card stackings.
The
| For Programmers
|
reset_deck
Usagereset_deck SummaryThis forgets about all stacked cards. The deck, from Deal's point of view, plus a list of cards which must go to specific hands. The cards which are assigned to specific hands are "stacked." The cards which are not stacked can go anywhere at the next call to deal_deck. | For Programmers
|
stack_hand
and stack_cards
Usagestack_hand handname hand stack_cards handname suitname holding [...] SummaryBy default, these two routines just call deck_stack_hand and deck_stack_cards, respectively - that is, they forcibly place cards in the deck.But these routines are meant to be overridden. For example, when using one of the input formats which reads deals from a file, the format will redefine these two procedures to give errors. % deal -I "line foo.txt" -e "stack_cards south spades AJ" Tcl stack dump of error info: No card stacking with input format ::line ...A more complicated re-definition occurs in the smartstack input format, which alters its hand
generation depending on what cards are stacked where.
| For Programmers
|
deck_stack_hand
and deck_stack_cards
Usagedeck_stack_hand handname hand deck_stack_cards handname suitname holding [...] SummaryThese routines are the "underlying" deck-stacking routines. Any cards stacked with these routines remain stacked until the next call toreset_deck
| For Programmers
|
stacked
Usagestacked handname SummaryDetermines what cards are stacked to this hand, returning them as a list of holdings:south gets AS KH 3H QD JD TC 9C 8C puts [stacked south]writes out the list: A K3 QJ T98This is useful for the smartstacker, because we don't want to force the user to stack cards *after* specifying conditions. stack_hand north {AJT KT3 KJ 75432} deal::input smartstack south balanced hcp 20 21The call to stack_hand occurs before smartstack
is loaded, so stack_hand has not been redefined. So what
smartstack does is read the cards already stacked, and
use that for its initial conditions.
| For Programmers
|
deal::nostacking
Usage::deal::nostacking SummaryThis procedure redefines the stack_hand and stack_cards procedures to generate error messages, and generates an error if any cards are currently stacked.This is used in all of the input formats which read complete deals from files, and thus are incompatible with stacking. |
For Programmers
|
deal_reset_cmds
Usagedeal_reset_cmds {...code...} [ ... ] SummaryThis adds a set of commands that are to be called before the next deal. The code is executed at the next call to thedeal_deck procedure, which, unless
you are doing something complicated, means it is called at the beginning
of each time through the evaluation loop.
It is also used for defining input formats, so that deals can be read from files. For example, the "line" input format has the following routine declared: namespace eval line { #.... proc next {} { variable handle set length -1 catch { set length [gets $handle line] } # ... process the line, or handle oef stuff ... # ... deal_reset_cmds {::line::next} } }
The key is that when
The code passed in to | For Programmers
|
deal::metadata
Usagedeal::metadata name {...code...} SummaryCurrently, this is just a peculiar way of caching slow evaluation routines. At the moment, the only place it is used is indeal::tricks .
When you call This isn't really necessary or efficient in most cases, but with evaluations which take some time, e.g. GIB calls, it improves things. | For Programmers
In later releases, metadata read from input streams (say, the fields from a PBN file) will also be stored here.
This procedure uses
|
deal::input
Usagedeal::input formatName args SummaryThedeal::input command is used to define an input source
for deals. It works as follows:
|
For Programmers
|
giblib
Usagedeal::input giblib [filename]or on the command line: % deal -I giblibor % deal -I "giblib filename" SummarySpecifies that deals are read from the specified file in the format
of Matt Ginsberg's library.dat file. This includes double-dummy tricks
data, so that later calls to If no filename is given, the library tries to read from a file called "library.dat" in the current directory.
The |
For Programmers
This procedure uses deal_reset_cmds. |
line
Usagedeal::input line [filename] SummarySpecifies that deals are read from the specified file in the format written by Deal's "-l" option. If no filename is given, the library reads from standard input. This way, you can create a single data file and then run several different queries against it: % deal -l 100000 > sample % deal -e "deal::input line sample" -i query1 % deal -e "deal::input line sample" -i query2[ The -e option just evaluates the code in the next
argument as Tcl code. Alternative, you can use the -I
option, which is shorthand for input formats:
% deal -I "line sample" -i query1The -I args option is exactly the same as -e "deal::input args".]
|
For Programmers
|
ddline
Usagedeal::input line [filename] SummarySpecifies that deals are read from the specified file in the format
written by Deal's The ddline format include doubled-dummy tricks data, so when using |
For Programmers
|
smartstack
Usagedeal::input smartstack hand shapeclass [holdproc min max]or on the command line: % deal -I "smartstack shapeclass ..." SummaryThis is the most complicated Deal input method in the current release. It does not read deals from a file - it is, instead, a different path to generation of deals. The purpose of this format is to allow fast generations of deals where one hand fits a very specific description. For example, if you want to find hands where south has a balanced 27-count, you could write:deal::input smartstack south balanced hcp 27 27On my computer, that returns the first deal in 14 seconds, and every deal after that takes a tiny fraction of a second. That's because smartstack
first builds a large "factory" in memory, but once it is built, the
factory spits out hands matching this condition very quickly.
By contrast, a standard "deal" script to find balanced 27-counts
takes about 2.8 seconds to find each deal. That means that if you only
want to find about five deals of this sort, the old style program
is faster, but if you want a lot more, use
One interesting feature of
The |
For Programmers
|
write_deal
Usagewrite_deal SummaryThis is the the name of a procedure which is called when deals are accepted. By default, it writes the result in the format:S: 98632 H: A4 D: AJ754 C: J S: K S: AJT7 H: J3 H: QT95 D: T98 D: Q2 C: AKT7532 C: 984 S: Q54 H: K8762 D: K63 C: Q6 --------------------------New output formats are defined by redefining this routine. | For Programmers
|
flush_deal
Usageflush_deal SummaryThis routine is called at the very end of a run for deal. It does nothing, by default, but some output formats may require it if data is cached. | For Programmers
|
format/par
Usagesource format/par SummaryBy including this file, you are setting the output format to write the results in PBN format, with results set to the double dummy theoretical par. $ deal -i format/par 1 [Date "2008.05.19"] [Board "1"] [West "West"] [North "North"] [East "East"] [South "South"] [Dealer "N"] [Vulnerable "None"] [Deal "N:K.A7.QJT9865.AK7 T94.K98532.2.943 A872.QT4.AK4.862 QJ653.J6.73.QJT5"] [Contract "6N"] [Declarer "S"] [Result "12"] [Score "NS 990"] { S: K H: A7 D: QJT9865 C: AK7 S: QJ653 S: T94 H: J6 H: K98532 D: 73 D: 2 C: QJT5 C: 943 S: A872 H: QT4 D: AK4 C: 862 north makes 9 tricks in clubs north makes 12 tricks in diamonds north makes 8 tricks in hearts north makes 7 tricks in spades north makes 12 tricks in notrump east makes 3 tricks in clubs east makes 1 tricks in diamonds east makes 5 tricks in hearts east makes 4 tricks in spades east makes 1 tricks in notrump south makes 8 tricks in clubs south makes 12 tricks in diamonds south makes 8 tricks in hearts south makes 7 tricks in spades south makes 12 tricks in notrump west makes 3 tricks in clubs west makes 1 tricks in diamonds west makes 5 tricks in hearts west makes 4 tricks in spades west makes 1 tricks in notrump } [Auction "N"] Pass Pass 6N [Play "W"] * | For Programmers
|
format/ddline
Usagesource format/ddline SummaryBy including this file, you are redefining the output format to the form "ddline", which writes out the deal on a single line, as with the '-l' flag, but then adds complete double-dummy data to the deal as well. If you write the output to a file: $ deal -i format/ddline 100 > doubledummy.txt then you can use the $ deal -I "ddline doubledummy.txt" -i query.tcl 100 And any call to | For Programmers
|
Thomas Andrews
(deal@thomasoandrews.com)
Copyright 1996-2010. Deal is covered by the
GNU General Public License.
Plane Dealing graphic above created using POV-Ray. |