Algorithm-MasterMind

 view release on metacpan or  search on metacpan

lib/Algorithm/MasterMind/EvoRank.pm  view on Meta::CPAN

			       Individual::String );

use Clone::Fast qw(clone);

# ---------------------------------------------------------------------------
use constant { MAX_CONSISTENT_SET => 20, # This number 20 was computed in NICSO paper, valid for default 4-6 mastermind
	       MAX_GENERATIONS_RESET => 50,
	       MAX_GENERATIONS_EQUAL => 3} ;

sub initialize {
  my $self = shift;
  my $options = shift;
  for my $o ( keys %$options ) {
    $self->{"_$o"} = clone($options->{$o});
  }
  croak "No population" if $self->{'_pop_size'} == 0;
  # Variation operators
  my $mutation_rate = $options->{'mutation_rate'} || 1;
  my $xover_rate = $options->{'xover_rate'} || 2;
  my $permutation_rate = $options->{'permutation_rate'} || 0;
  my $max_number_of_consistent = $options->{'consistent_set_card'} || MAX_CONSISTENT_SET;   
  my $m = new Algorithm::Evolutionary::Op::String_Mutation $mutation_rate ; # Rate = 1
  my $c = Algorithm::Evolutionary::Op::QuadXOver->new( 1, $xover_rate ); 
  my $operators = [$m,$c];
  if ( $permutation_rate > 0 ) {
    my $p =  new Algorithm::Evolutionary::Op::Permutation $permutation_rate; 
    push @$operators, $p;
  }
  if (! $self->{'_ga'} ) { # Not given as an option
    $self->{'_ga'} = new Algorithm::Evolutionary::Op::Canonical_GA_NN( $options->{'replacement_rate'},
								       $operators );    
  }

  if (!$self->{'_distance'}) {
    $self->{'_distance'} = 'distance_taxicab';
  }
  $self->{'_max_consistent'} = $max_number_of_consistent;
}

sub compute_fitness {
  my $pop = shift;
  #Compute min
  my $min_distance = 0;
  for my $p ( @$pop ) {
    $min_distance = ( $p->{'_distance'} < $min_distance )?
      $p->{'_distance'}:
	$min_distance;
  }

  for my $p ( @$pop ) {
    $p->Fitness( $p->{'_distance'}+
		 ($p->{'_partitions'}?$p->{'_partitions'}:0)-
		 $min_distance + 1);
  }
}

sub issue_next {
  my $self = shift;
  my $rules =  $self->number_of_rules();
  my @alphabet = @{$self->{'_alphabet'}};
  my $length = $self->{'_length'};
  my $pop = $self->{'_pop'};
  my $ga = $self->{'_ga'};
  my $max_number_of_consistent  = $self->{'_max_consistent'};

  #Recalculate distances, new game
  my (%consistent );
  my $partitions;
  my $distance = $self->{'_distance'};
  for my $p ( @$pop ) {
      ($p->{'_distance'}, $p->{'_matches'}) = @{$self->$distance( $p->{'_str'} )};
#      ($p->{'_distance'}, $p->{'_matches'}) = @{$self->distance( $p )};
      $consistent{$p->{'_str'}} = $p if ($p->{'_matches'} == $rules);
  }

  my $number_of_consistent = keys %consistent;
  if ( $number_of_consistent > 1 ) {
    $partitions = partitions( keys %consistent );      
    for my $c ( keys %$partitions ) {
      $consistent{$c}->{'_partitions'} = scalar (keys  %{$partitions->{$c}});
    }
  }
  my $generations_equal = 0;
  my $this_number_of_consistent = $number_of_consistent;
  
  while ( $this_number_of_consistent < $max_number_of_consistent ) {  
     
    #Compute fitness
    compute_fitness( $pop );
    #      print join( " - ", map( $_->{'_fitness'}, @$pop )), "\n";
    
    #Apply GA
    $ga->apply( $pop );
    
    #Compute new distances
    %consistent = ();  # Empty to avoid problems
    for my $p ( @$pop ) {
      ($p->{'_distance'}, $p->{'_matches'}) = @{$self->$distance( $p->{'_str'} )};
#	($p->{'_distance'}, $p->{'_matches'}) = @{$self->distance( $p )};
	if ($p->{'_matches'} == $rules) {
	    $consistent{$p->{'_str'}} = $p;
	    #	print $p->{'_str'}, " -> ", $p->{'_distance'}, " - ";
	} else {
	    $p->{'_partitions'} = 0;
	}
    }
    
    #Check termination again, and reset
    if ($generations_equal == MAX_GENERATIONS_RESET ) {
	$ga->reset( $pop );
	for my $p ( @$pop ) {
	($p->{'_distance'}, $p->{'_matches'}) = @{$self->$distance( $p->{'_str'} )};
#	    ($p->{'_distance'}, $p->{'_matches'}) = @{$self->distance( $p )};
	}
	$generations_equal = 0;
    }

    #Check termination conditions
    $this_number_of_consistent =  keys %consistent;
    if ( $this_number_of_consistent == $number_of_consistent ) {
      $generations_equal++;



( run in 0.660 second using v1.01-cache-2.11-cpan-140bd7fdf52 )