001/*
002 *  $Source: /open/projects/WebARTS/ca/bc/webarts/widgets/LotterySimulator.java $
003 *  $Name:  $
004 *
005 *  $Date: 2018-03-02 22:29:54 -0800 (Fri, 02 Mar 2018) $
006 *  $Locker:  $
007 *
008 *
009 *  Written by Tom Gutwin - WebARTS Design.
010 *  Copyright (C) 2010 WebARTS Design, North Vancouver Canada
011 *  http://www.webarts.bc.ca
012 *
013 *  This program is free software; you can redistribute it and/or modify
014 *  it under the terms of the GNU General Public License as published by
015 *  the Free Software Foundation; either version 2 of the License, or
016 *  (at your option) any later version.
017 *
018 *  This program is distributed in the hope that it will be useful,
019 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
020 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
021 *  GNU General Public License for more details.
022 *
023 *  You should have received a copy of the GNU General Public License
024 *  along with this program; if not, write to the Free Software
025 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
026 */
027
028 package ca.bc.webarts.widgets;
029
030import ca.bc.webarts.tools.Log;
031import ca.bc.webarts.tools.StreamGobbler;
032
033import gnu.regexp.RE;
034import gnu.regexp.REException;
035
036import java.io.File;
037import java.util.Enumeration;
038import java.util.Hashtable;
039import java.util.Iterator;
040import java.util.List;
041import java.util.Vector;
042
043/**
044 *  Simulates the drawing of a 6/49 style lottery to analyze different number selection scenarios in
045*  an attempt to see what are the best sequences of numbers.
046 *
047 * @author    TGutwin
048 */
049public class LotterySimulator
050{
051  /**  A holder for this clients System File Separator.  */
052  public final static String SYSTEM_FILE_SEPERATOR = File.separator;
053
054  /**  A holder for this clients System line termination separator.  */
055  public final static String SYSTEM_LINE_SEPERATOR =
056                                           System.getProperty("line.separator");
057
058  /**
059   *  A constant to specify which dialog to open with the chooseFileDialog.
060   */
061  public final static short OPEN_DIALOG = 0;
062
063  /**
064   *  A constant to specify which dialog to open with the chooseFileDialog.
065   */
066  public final static short SAVE_DIALOG = 1;
067
068  /**  The VM classpath (used in some methods)..  */
069  public static String CLASSPATH = System.getProperty("class.path");
070
071  /**  The users home ditrectory.  */
072  public static String USERHOME = System.getProperty("user.home");
073
074  /**  The users pwd ditrectory.  */
075  public static String USERDIR = System.getProperty("user.dir");
076
077  /**  A holder This classes name (used when logging).  */
078  private static String CLASSNAME = "ca.bc.webarts.widgets.LotterySimulator";
079
080  /**  Gets a Log instance in case any calling classes are using it.  */
081  private static Log log_ = Log.getInstance();
082
083  private static boolean verbose = false;
084  private static boolean verbose2 = false;
085
086  /**  Class flag signifying if the initUtil method has been called  */
087  private static boolean initClass = false;
088
089  /** If a big win, then stop spending more money **/
090  private boolean stopOnBigWin = true;
091  private float quiteEarlyRatio = 1.4f;
092
093  private int ones, twos, twosPlus, threes, fours, fives, fivesPlus, sixes;
094  private long onesPay, twosPay, twosPlusPay, threesPay, foursPay, fivesPay, fivesPlusPay, sixesPay;
095  private float onesChance, twosChance, twosPlusChance, threesChance, foursChance, fivesChance, fivesPlusChance, sixesChance;
096  public long payout = 0;
097  private int numbers = 49;
098  private int initialPool = 30000000;
099  private int pool = initialPool;
100  private float poolIncrementPercent = 0.47f;
101  private int numTicketsSold = pool;
102  private int ticketPrice = 2;
103
104  /**
105   *  Constructor for the LotterySimulator object
106   * @param numDraws is the number of Draws to run
107   * @param tixPerDraw is the number of tix purchased per Draw
108   */
109  public LotterySimulator(int numDraws, int tixPerDraw)
110  {
111    final String methodName = CLASSNAME + ": LotterySimulator(int)";
112    initClass();
113    if (log_ != null)
114    {
115      log_.setLogLevel(Log.MAJOR);
116      log_.startMethod(methodName);
117    }
118
119    Vector<Long>[]  picks = new Vector[tixPerDraw];
120    int carries = 0;
121    int numSold = 0;
122    int invest = 0;
123    float benefitCost = 0.0f;
124    long currDrawPayout = 0;
125    int drawNum = 0;
126
127    for (drawNum = 0; drawNum < numDraws&& benefitCost < quiteEarlyRatio; drawNum++)
128    {
129      //Vector<Long>  myPicks = rollDice(6,numbers);
130      for (int pickNum = 0; pickNum < tixPerDraw; pickNum++)
131      {
132        picks[pickNum] = rollDice(6,numbers);
133
134        if (verbose) System.out.print("\nPick "+pickNum+": ");
135        for (int i = 0; i < picks[pickNum].size(); i++)
136        {
137          if (verbose) System.out.print(""+picks[pickNum].get(i)+(i<(picks[pickNum].size()-1)?", ":""));
138        }
139      }
140
141      Vector<Long>  draw = rollDice(6,49);
142      long bonus = drawBonusNumber(draw, numbers);
143      if (verbose) System.out.print("\n ** Draw ");
144      for (int i = 0; i < draw.size(); i++)
145      {
146        if (verbose) System.out.print(""+draw.get(i)+(i<(draw.size()-1)?", ":""));
147      }
148      if (verbose) System.out.println(" bonus "+ bonus);
149
150      Vector<Long> myPick = null;
151      ones = 0;
152      twos = 0;
153      twosPlus = 0;
154      threes = 0;
155      fours = 0;
156      fives = 0;
157      fivesPlus = 0;
158      sixes = 0;
159
160      for (int pickNum = 0; pickNum < tixPerDraw; pickNum++)
161      {
162        myPick = picks[pickNum];
163        switch (matches(draw, myPick))
164        {
165          case 1:
166            ones++;
167            break;
168
169          case 2:
170            if (myPick.contains(bonus))
171              twosPlus++;
172            else
173              twos++;
174            break;
175
176          case 3:
177            threes++;
178            break;
179
180          case 4:
181            fours++;
182            break;
183
184          case 5:
185            if (myPick.contains(bonus))
186              fivesPlus++;
187            else
188              fives++;
189            break;
190
191          case 6:
192            sixes++;
193            break;
194
195          default:
196        }
197
198        if (verbose) System.out.println(" matches " + matches(draw, myPick));
199      }
200
201      // Draw complete,  so calc Payouts
202      calculatePayouts(pool);
203      currDrawPayout = ones*onesPay+twos*twosPay+twosPlus*twosPlusPay+
204                       threes*threesPay+fours*foursPay+fives*fivesPay+fivesPlus*fivesPlusPay+
205                       sixes*sixesPay;
206      payout+=currDrawPayout;
207      if (!someoneWonTheJackpot() && matches(draw, myPick)<6)
208      {
209        carries++;
210        pool+=(int)(poolIncrementPercent*(float)getNumTicketsSold(pool)*(float)ticketPrice);
211        if (verbose) System.out.print(""+carries);
212        if (verbose2) System.out.println("carried "+pool);
213      }
214      else
215      {
216        pool=initialPool;
217        carries=0;
218      }
219
220      invest = ticketPrice*(drawNum+1)*tixPerDraw ;
221      benefitCost = (float)payout/(float)invest;
222      //if (verbose|| verbose2)
223        System.out.println("Current Draw:"+drawNum+"("+pool+")  currPayout=$"+currDrawPayout +"  TotInvested=$"+invest +"   TotalWON=$"+payout+"   runningBenefitCost="+benefitCost);
224      // Go back and draw again
225    }
226    if (verbose|| verbose2) System.out.println("");
227
228    // Calc my results
229    numSold = Math.round(getNumTicketsSold(pool));
230    invest = ticketPrice*drawNum*tixPerDraw ;
231    benefitCost = (float)payout/(float)invest;
232    if (drawNum<numDraws) System.out.println("****WON BIG - Stopped Early.");
233    System.out.println("With "+drawNum+" draws and "+tixPerDraw +" tickets per Draw. ");
234    if (verbose)
235    {
236      System.out.println("    ones:      "+ones+ " ($"+ones*onesPay+")");
237      System.out.println("    twos:      "+twos+ " ($"+twos*twosPay+")");
238      System.out.println("    twosPlus:  "+twosPlus+ " ($"+twosPlus*twosPlusPay+")");
239      System.out.println("    threes:    "+threes+ " ($"+threes*threesPay+
240                        // " from a share of $"+ (numSold* threesChance*threesPay)+
241                         ")");
242      System.out.println("    fours:     "+fours+ " ($"+fours*foursPay+
243                        // " from a share of $"+(numSold* foursChance*foursPay)+
244                         ")");
245      System.out.println("    fives:     "+fives+ " ($"+fives*fivesPay+
246                        // " from a share of $"+ (numSold* fivesChance*fivesPay)+
247                         ")");
248      System.out.println("    fivesPlus: "+fivesPlus+ " ($"+fivesPlus*fivesPlusPay+
249                        // " from a share of $"+ (numSold* fivesPlusChance*fivesPlusPay)+
250                         ")");
251      System.out.println("    sixes:     "+sixes+ " ($"+sixes*sixesPay+
252                        // " from a share of $"+ (numSold* sixesChance*sixesPay)+
253                         ")");
254    }
255    System.out.println("Cost: $"+invest +"  PAYOUT: $"+payout +"  net="+(payout-(ticketPrice*drawNum*tixPerDraw))+
256                       "   Benfit Cost Ratio="+benefitCost );
257  }
258
259
260  public static String printUsage()
261  {
262    final String methodName = CLASSNAME + ": printUsage()";
263    String retVal = "";
264
265    retVal += "Class ca.bc.webarts.widgets.LotterySimulator\n";
266    retVal += "\n";
267    retVal += "Usage: java ca.bc.webarts.widgets.LotterySimulator [-v[v][t tixPerDraw]] numDraws\n";
268    retVal += "      one command parameter is required... numDraws - the number of draws to make\n";
269    retVal += "Examples:\n";
270    retVal += "      LotterySimulator -h\n";
271    retVal += "      LotterySimulator -v 10\n";
272    retVal += "      LotterySimulator -t 5 10\n";
273    retVal += "      LotterySimulator -vt 10 100\n";
274    retVal += "      LotterySimulator -vv 100\n";
275    retVal += "      LotterySimulator -vvt 5 100\n";
276    System.out.println(retVal);
277    return retVal;
278  }
279
280
281  public static void main(String [] args)
282  {
283    final String methodName = CLASSNAME + ": main()";
284
285    if (args.length==0 || args[0].startsWith("-h"))
286    {
287      printUsage();
288      System.exit(1);
289    }
290    if (args[0].startsWith("-v"))  verbose=true;
291    if (args[0].startsWith("-vv"))  verbose2=true;
292    if (args[0].startsWith("-t")||args[0].endsWith("t"))
293    {
294      //verbose = true;
295        // LotterySimulator( numDraws, tixPerDraw)
296      new LotterySimulator(Integer.parseInt(args[args.length-1]),Integer.parseInt(args[1]));
297    }
298    else new LotterySimulator(Integer.parseInt(args[args.length-1]), 1);
299
300    if (log_ != null) log_.endMethod();
301  }
302
303
304  private boolean someoneWonTheJackpot()
305  {
306    boolean retVal = false;
307    int numSold = Math.round(getNumTicketsSold(pool));
308    for (int i = 0; !retVal && i< numSold; i++)
309      retVal = Math.random()<sixesChance;
310    return  retVal;
311  }
312
313
314  /** gets the number of tix sold based on the pool size. This is a non-linear relationship.
315  **/
316  private long getNumTicketsSold(int poolSize)
317  {
318    float multiplier = 3.0f;
319    long retVal = ((long)((float)poolSize * 0.75f/multiplier));
320    if  (poolSize>50000000)
321      retVal = ((long)((float)poolSize * 1.8f/multiplier));
322    else if (poolSize>45000000)
323      retVal =  ((long)((float)poolSize * 1.8f/multiplier));
324    else if (poolSize>40000000)
325      retVal =  ((long)((float)poolSize * 1.6f/multiplier));
326    else if (poolSize>30000000)
327      retVal =  ((long)((float)poolSize * 1.3f/multiplier));
328    else if (poolSize>20000000)
329      retVal =  ((long)((float)poolSize * 1.1f/multiplier));
330    else if (poolSize>10000000)
331      retVal =  ((long)((float)poolSize * 1.0f/multiplier));
332    else if (poolSize>6000000)
333      retVal =  ((long)((float)poolSize * 0.8f/multiplier));
334
335    return retVal;
336
337  }
338
339
340  /**  Inits the log and the payout amounts for the different prize levels.*/
341  private void initClass()
342  {
343    final String methodName = CLASSNAME + ": initClass()";
344
345    if (!initClass || log_ == null)
346    {
347      log_ = Log.getInstance();
348      if (log_ != null)
349      {
350        log_.setLogLevel(Log.MAJOR);
351        log_.startMethod(methodName);
352      }
353      log_.debug("Into the init block");
354      CLASSPATH = System.getProperty("java.class.path");
355      ones = 0;
356      twos = 0;
357      twosPlus = 0;
358      threes = 0;
359      fours = 0;
360      fives = 0;
361      fivesPlus = 0;
362      sixes = 0;
363
364      onesChance = 1/numbers + 1/(numbers-1) + 1/(numbers-2) + 1/(numbers-3) + 1/(numbers-4) + 1/(numbers-5);
365      twosChance = (float)(1.0/20.0);
366      twosPlusChance = (float)(1.0/81.2);
367      threesChance = (float)(1.0/56.7);
368      foursChance = (float)(1.0/1033.0);
369      fivesChance = (float)(1.0/55492.0);
370      fivesPlusChance = (float)(1.0/2330636.0);
371      sixesChance = (float)(1.0/13983816.0);
372
373      calculatePayouts(pool);
374
375      if (verbose2) System.out.println("Chance Payouts: 6("+sixesChance+") "+
376                  Math.round(0.805*pool) +"/"+ Math.round(getNumTicketsSold(pool)* sixesChance)+"="+
377                  sixesPay);
378      if (verbose2) System.out.println("Chance Payouts: 5bonus("+fivesPlusChance+") "+
379                  Math.round(0.0575*pool) +"/"+ Math.round(getNumTicketsSold(pool)* fivesPlusChance)+"="+
380                  fivesPlusPay);
381      if (verbose2) System.out.println("Chance Payouts: 5("+fivesChance+") "+
382                  Math.round(0.0475*pool) +"/"+ Math.round(getNumTicketsSold(pool)* fivesChance)+"="+
383                  fivesPay);
384      if (verbose2) System.out.println("Chance Payouts: 4("+foursChance+") "+
385                  Math.round(0.09*pool) +"/"+ Math.round(getNumTicketsSold(pool)* foursChance)+"="+
386                  foursPay);
387      if (verbose2) System.out.println("Chance Payouts: 3("+threesChance+") "+
388                  10.0 +"/"+ Math.round(getNumTicketsSold(pool)* threesChance)+"="+
389                  threesPay);
390      initClass = true;
391
392      if (log_ != null) log_.endMethod();
393    }
394  }
395
396
397  /* calculates the payouts for the various levels based on the poolsize **/
398  private void calculatePayouts(int currentPool)
399  {
400    int tixSold = Math.round(getNumTicketsSold(currentPool));
401    onesPay = 0;
402    twosPay = 0;
403    twosPlusPay = 5;
404    threesPay = 10;
405    if (currentPool < 100000000)
406    {
407      // 4/6 pays a split of 9.5% of the pool
408      foursPay = Math.round((0.095*(float)currentPool) /(float)((float)tixSold* foursChance));
409
410      // 5/6 pays a split of 5% of the currentPool
411      fivesPay = Math.round((0.05*(float)currentPool) /(float)((float)tixSold* fivesChance));
412
413      // 5/6 with the bonus pays a split of 6% of the currentPool
414      fivesPlusPay = Math.round((0.06*(float)currentPool) /(float)((float)tixSold* fivesPlusChance));
415
416      // Jackpot (6/6) pays a split of 80.5 % of the currentPool
417      sixesPay = Math.round((0.795*(float)currentPool) /(float)((float)tixSold* sixesChance));
418    }
419    else
420    {
421      // no longer used - it used to be pool >30000000
422
423      // 4/6 pays a split of 29% of the pool
424      foursPay = Math.round((0.29*(float)currentPool) /(float)((float)tixSold* foursChance));
425
426      // 5/6 pays a split of 15% of the currentPool
427      fivesPay = Math.round((0.15*(float)currentPool) /(float)((float)tixSold* fivesChance));
428
429      // 5/6 with the bonus pays a split of 16% of the currentPool
430      fivesPlusPay = Math.round((0.16*(float)currentPool) /(float)((float)tixSold* fivesPlusChance));
431
432      // Jackpot (6/6) pays a split of 40 % of the currentPool
433      sixesPay = Math.round((0.4*(float)currentPool) /(float)((float)tixSold* sixesChance));
434    }
435  }
436
437
438  /**  Counts the number of numbers in v1 match the numbers in v2. */
439  private int matches(Vector<Long> v1, Vector<Long> v2)
440  {
441    final String methodName = CLASSNAME + ": matches()";
442    if (log_ != null) log_.startMethod(methodName);
443
444    int retVal = 0;
445    for (int i = 0; i < v1.size(); i++)
446    {
447      if (v2.contains(v1.get(i))) retVal++;
448    }
449
450    if (log_ != null) log_.endMethod();
451    return retVal;
452  }
453
454
455  /**  Draws on more number as the bonus number from 'nums'
456    * numbers and excludes the numbers already drawn and passed in v1.
457    * @param v1 is a vector of numbers to exclude (nums already drawn)
458    * @param nums the sample size ... 1-nums
459    */
460  private long drawBonusNumber(Vector v1, int nums)
461  {
462    final String methodName = CLASSNAME + ": drawBonusNumber()";
463    if (log_ != null) log_.startMethod(methodName);
464
465    long randLong = Math.round(Math.random()*nums)+1;
466    while (v1.contains(randLong))
467    {
468      randLong  = Math.round(Math.random()*nums)+1;
469    }
470    if (log_ != null) log_.endMethod();
471    return randLong;
472  }
473
474
475  /**  Rolls 'numDice' each with 'nums' sides and returns the results in a Vector of Longs.*/
476  private Vector<Long> rollDice(int numDice, int nums)
477  {
478    final String methodName = CLASSNAME + ": rollDice("+numDice+", "+nums+")";
479    if (log_ != null) log_.startMethod(methodName);
480
481    Vector <Long> retVal = new Vector<Long>() ;
482    long randLong = 0;
483    int i=0;
484    while ( i< numDice )
485    {
486      randLong = Math.round(Math.random()*nums)+1;
487      if (!retVal.contains(randLong))
488      {
489        retVal.add(new Long(randLong));
490        i++;
491      }
492      else
493      {
494        //System.out.print("DUPE");
495      }
496    }
497
498    if (log_ != null) log_.endMethod();
499    return retVal;
500  }
501
502}