The program allows you to play 1 to 10 spot games. You may select
the spots on your own or let the program select numbers randomly.
Buttons allow you to play a single game, a round of 5 games where the same
spots are retained from game to game, 20 5-game rounds to simulate 100
games at a time, or 100,000 games to help check statistical results.
For all except the 100,000 game button, result of each individual game are
listed including the payout and total payout for all games since the last
time statistics were cleared.
The initial payout table is a "fair" table with the payout
based on the probability of each outcome and no profit for the
house. A separate tab sheet allows you to modify the table and save
it or to reload saved payout tables. Several sample tables found on the
web are included.
A little math
The most interesting part of the project was
determining the probabilities for each possible outcome. The
probability for any event is defined as the number of successful outcomes
for the event divided by the total number of possible
outcomes. Say we are playing a 5-spot game and want to
calculate the probability that 3 catches occur. The total number of
successful outcomes is the number of ways we can select our 3 catches from
the 20 numbers drawn multiplied by the number of ways that the 2
non-matching numbers could have been drawn from the other 60 numbers not
in the draw. The " combinations" function gives these
numbers. If we use the common terminology C(N,R)
to represent choosing R of N items, then C(20,3) x C(60,2)
represents the number of possible outcomes. The total number of outcomes is
and the probability will be C(20,3) x C(60,2) /C(80,5).
Since order does not matter by
definition,C(20,3), is the product of the 20 choices for the first number,
19 choices for the second and 18 choices for the 3rd all
divided by the number of orderings of the 3 numbers (denoted as 3
factorial = 3! = 3x2x1 = 6). So C(20,3) =
20x19x18/6=1140. Similarly C(60,2) = 60x59/2 = 1770 and the total
possible successful out comes = 1140 x 1770 =
2,017,800. The divisor C(5,80) is a computer job and equals
24,040,016. So C(20,3) x C(60,2) / C(80,5) = 2017800 / 24040016 =
0.08393505 or about 8 games out of every hundred
played. Whew! Do that 65 more times and
we'll have the probabilities for all outcomes for 0 to N catches for all N
from 1 to 10.
Non-programmers are welcome to read on, but
may want to skip to the bottom of this page to
download executable version of the program.
Notes for Programmers
Except for figuring out the math, not much new here.
I used a, 10x8 TStringGrid for the Keno card and used an OnSelect
exit to identify user clicks. OnSelect has the advantage over OnClick
because it provides the column and row directly. Successive
clicks will select then deselect a number. Selected numbers ,
the "spots" are tracked two ways: Entries in
a 10X8 Boolean array, Board, are set to true for selected
numbers. Selected numbers are also kept in a separate Selected
;array to simplify checking during the playing phase. An
OnDrawCell exit for the Stringgrid handles coloring the background
to visually identify the spots.
The random draw during play-in is accomplished by "shuffling"
an array containing the integers 1 through 80. Procedure Shuffle
swaps positions from 80 down to 2 with the number from a random position
less than or equal to the position being set. I do this 3
times although some studies say that 6 or 7 such shuffles are required to
ensure true randomness. After the shuffle, the first 20
numbers constitute the "Draw".
In counting matches after the draw, I pass the 20 numbers in the draw
against the against the selected spots array. It seems to me
to be a wash whether spots are checked against the draw or the draw
numbers are checked against the spots, but I haven't really checked that.
The PayOutTable is a two dimensional array filled
initially with probabilities and "fair" payout for all 10
choices for number selected, and for number matched from 0 to the
max for that size game. The "fair" payout is simply
1/probability for each outcome. The probabilities are fixed ,
but the payout values may be modified by the user.
A separate tabsheet manages the Payout table. Users
can load or save a set of records representing the non-zero payouts for
each (spots, catches) combination. Entries are displayed
in a TMemo which contains the 3 values, spots, catches and payout on each
line. The Save button saves these line without further
checking. At Load time, simple checking ensures that
two positive integers plus a valid floating point number are contained on
each line and the payout table is modified
My StrToFloatDef function included here which is one of the
"missing" standard Delphi functions, at least in Delphi 5.
StrToFloatDef tries to convert a string to an extended type
floating point value and returns a specified default value if the
conversion fails. I use it here to return -1.0 if the string is not
a valid payout value. A check for negative payout at load time
can trigger an error message and skip the record.
Keno uses our UComboV2 unit to calculate combinations for the
outcomes. UComboV2 is used in many programs here on DFF and, to make
my maintenance job easier, it is contained on a Library zip file
along with other commonly used routines. To recompile Keno you will
need to do a one-time download of DFFVLib04 or later versions, if
you have not already done so.
Addendum February 25, 2008: It has been three
years since we visited the world of Keno. A recent email from a
fellow in Brazil prompted Version 2 posted today. The
Brazilian national lottery is a version of Keno which they call "Lotomania".
In this game the Pool is 100 numbers, 20 Spots are selected and the Draw
is 50 numbers. I expanded Keno V1 to allow user selection of
Pool, Draw, and Spots values. I also expanded the Payout
results table to include theoretical and observed odds as well as
probability for the number of "catches".
The new version with the larger Pool uses needed a routine borrowed
from TBigCombos to calculate payout probabilities. I also
added more flexibility and significant speed increase in running game
simulations. A 100% payout table file matching the payouts for
Lotomania (for 0, 16, 17, 18, 19, and 20 catches) is
I have not yet generalized the decimal separator/thousands separator
values to handle the European/Brazilian standard, so you may have to
manually modify the provided payout files if you live in one of those
countries that refuse to "do it our way". There is a page
documenting the code changes required on this
International Separator page.
Running/Exploring the Program