#! /usr/bin/perl -w
#
##### Program description
#
# Title: lotto.pl
#
# Author(s): Mattias Ohlsson
#
# Description:
#    This program will estimate the probability of winning a game of
#    Lotto. The method used here is a Monte Carlo simulation of the
#    lottery. This means that 7 numbers are randomly generated from the
#    interval [1,35] without doublets and then 7+4 winner
#    numbers are randomly generated from the same interval. According to
#    the rules of Lotto the following situations will result in a winnings: 
#
#    4   correct numbers
#    5   correct numbers
#    6   correct numbers
#    6+1 correct numbers
#    7   correct numbers
#
#
# List of subroutines:
#
#    iRnd: This function takes two integers, i1 and i2, as
#       arguments and returns random integer in the interval [i1, i2].
#
#    GetNumbers: This function returns a vector of random numbers
#       in the interval [1,35], with all numbers different from each other.
#       The length of this vector is given as an argument and must be <= 35.
#
# Procedure: 
#    1. Get the number of Monte Carlo runs to perform from STDIN. 
#    2. Start the main Monte Carlo loop
#       2a. Get an array of 7 numbers (my Lotto row) and one with 11 numbers
#           (the "winner numbers"). 
#       2b. Make a loop over all the 7 numbers and check if each of them are
#           present in the array of 7+4 winner numbers. Add hits to the two variables
#           $Corr7 and $Corr4, representing the real hits or 
#           "tilläggsnummer" hits.
#       2c. Check the possible combinations for a winnings and record such an
#           event. Also record the different combinations.
#    3. Print a summary of the results found.
#
# Usage:
#        ./lotto.pl [number of MC runs]
# 
#
##################################

use strict;
use IO::Handle;
use feature ':5.10';
autoflush STDOUT 1;

#### Main program ####
my(%Stat);

# Get the number of Monte Carlo runs
my $Runs = 50000;
if ( scalar @ARGV >= 1 && $ARGV[0] =~ /^\d+$/ ) {
   $Runs = $ARGV[0];
}

# Zero the number of winnings variable
my $Nwin = 0;

# Main Monte Carlo loop
for ( my $i = 1; $i <= $Runs; $i++ ) {

   # "Fill in the Lotto sheet"
   my @Student = GetNumbers(7);

   # "Watch the Lotto on TV at Saturday evening"
   my @Teacher = GetNumbers(11);

   # Divide into 7 + 4 numbers
   my @Teacher7 = splice @Teacher, 0, 7;
   my @Teacher4 = @Teacher;
 
   # Check the Student for winnings
   my $Corr7 = 0;
   my $Corr4 = 0;
   foreach my $num (@Student) {
      if ($num ~~ @Teacher7) {
	 $Corr7++;
      } elsif ($num ~~ @Teacher4) {
	 $Corr4++;
      }
   }

   # Determine if it was a win!
   if ( $Corr7 >= 4 || ($Corr7 == 6 && $Corr4 > 0) ) {
      $Nwin++;
   }

   # Collect statistics
   $Stat{"$Corr7-$Corr4"}++;

   # Some nice online printing during the MC simulation
   printf "Probability of winnings: %5.2f %%  game $i out of $Runs\r",  
   100 * ($Nwin / $i) if($i % 10000 == 0);

}

# Print the summary
print "\n\n";
print "Summary after $Runs simulated games of lotto:\n\n";

my $mp = 0;
my $idx = '';
for ( my $i = 0; $i <= 7; $i++ ) {
   for ( my $j = 0; $j <= 4; $j++ ) {
      if ( defined $Stat{"$i-$j"} ) {
	 printf "Got $i + $j correct numbers in %7d games\n", $Stat{"$i-$j"};
	 if ( $Stat{"$i-$j"} > $mp ) {
	    $mp = $Stat{"$i-$j"};
	    $idx = "$i + $j";
	 }
      }
   }
}
printf "\nThe most probable outcome is: $idx correct numbers occuring $mp times\n";
printf "\nProbability of winnings: %5.2f %%\n", 100 * ($Nwin / $Runs);


## Subroutines follows here

sub iRnd {
   my ($i1, $i2) = @_;
   
   # A random integer between i1 and i2
   return int( rand($i2 - $i1 + 1) ) + $i1;
   
} # End of Rnd


sub GetNumbers {
   
   # How many numbers to generate, given as an argument
   my ($HowMany) = @_;
   
   # Check that it is <= 35
   if ( $HowMany > 35 ) {
      print "Error in GetNumbers: Lenght of random array must be <= 35\n";
      exit(-1);
   }
   
   my @Tmp;
   foreach my $i (1..35) {
      $Tmp[$i] = $i;
   }
   
   my $len = 35;
   my @Numbers;
   for ( my $i = 0; $i < $HowMany; $i++ ) {
      
      # Get random index
      my $idx = iRnd(1, $len);
      
      # Add to the Numbers array
      my $num = $Tmp[$idx];
      push @Numbers, $num;
      
      # Rearrange the Tmp array
      my $dum = $Tmp[$len];
      $Tmp[$len] = $Tmp[$idx];
      $Tmp[$idx] = $dum;
      $len -= 1;

   }
   
   # Return the array of random numbers
   return @Numbers;
   
} # End of GetNumbers
