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}