#!/usr/bin/perl -w
#This program will evolve digital organisms in a way specified by the user

use strict;
use warnings;


my($separator) = "*" x 50;
print "\n" . $separator . "\n" . "Welcome to Darwin's World!!\nThis is Evolution version1\nAuthor: Mario A. Fares\nDepartment of Genetics, University of Dublin, TCD\n$separator\n\n";
print "--Your mission is to evolve an organism to adapt to an environment created by you\n";
print "To create your environment, set the parameters below and choose your preferred organism\nto start your evolution with\n\n";
my($pop_size, $temperature, $oxygen, $food, $mutation_rate, $number_generations, $bottleneck, $initial_organism);
print "Population size: ";
$pop_size = <STDIN>;
chomp $pop_size;
if(($pop_size <1) || ($pop_size >100000000))
  {
    print "What if you try to use a sensible size!!\n";
    exit;
  }
print "\nTemperature of your environment (1 to 9) (1: cold; 9: hot):";
$temperature = <STDIN>;
chomp $temperature;
if(($temperature < 1) || ($temperature > 9))
  {
    print "\nYea yea, very funny but you have to follow the rules!!!\nTry again ;)\n\n";
    exit;
  }
print "\nOxygen in the air (1 to 9) (1: little oxygen; 9: very oxygenized):";
$oxygen = <STDIN>;
chomp $oxygen;
if(($oxygen < 1) || ($oxygen > 9))
  {
    print "\nYea yea, very funny but you have to follow the rules!!!\nTry again ;)\n\n";
    exit;
  }
print "\nAmount of available food (1 to 9) (1: very scarse; 9: abundant):";
$food = <STDIN>;
chomp $food;
if(($food < 1) || ($food > 9))
  {
    print "\nYea yea, very funny but you have to follow the rules!!!\nTry again ;)\n\n";
    exit;
  }
print "\nMutation rate (this is the proportion of individuals in the population that will mutate):";
$mutation_rate = <STDIN>;
chomp $mutation_rate;
print"\nNumber of generations of replicative population:";
$number_generations = <STDIN>;
chomp $number_generations;
if($number_generations < 2)
  {
    print "You don't seem up for multiplying do you :D\nTry again, don't be lazy :D\n\n";
    exit;
  }
print "\nBottleneck (example: 0.1 means 90% of the population will pass to next generation):";
$bottleneck = <STDIN>;
chomp $bottleneck;
if(($bottleneck < 0) || ($bottleneck > 1))
{
  print "Bad choice!! Bottleneck should be a number ranging between 0 and 1\nTry again\n\n";
  exit;
}
print "\nInitial organism:";
$initial_organism = <STDIN>;
chomp $initial_organism;
if(length($initial_organism) == 2)
  {
    my(@a_org);
    $a_org[0] = substr($initial_organism, 0, 1);
    $a_org[1] = substr($initial_organism, 1, 1);
    if(($a_org[0] <= 0) && ($a_org[1] <= 0))
      {
	print "Bad choice!! Organism should be composed of two digits, each ranging between 1 and 9\nTry again\n\n";
	exit;
      }
  }else
  {
    print "Bad choice!! Organism should be composed of two digits, each ranging between 1 and 9\nTry again\n\n";
    exit;
  }
my($prediction);
print "\nWhat is your predicted organism? ";
$prediction = <STDIN>;
chomp $prediction;
if(length($prediction) == 2)
  {
    my(@a_or);
    $a_or[0] = substr($prediction, 0, 1);
    $a_or[1] = substr($prediction, 1, 1);
    if(($a_or[0] <= 0) && ($a_or[1] <= 0))
      {
	print "Bad choice!! Organism should be composed of two digits, each ranging between 1 and 9\nTry again\n\n";
	exit;
      }
  }else
  {
    print "Bad choice!! Organism should be composed of two digits, each ranging between 1 and 9\nTry again\n\n";
    exit;
  }
#We will define an ecological landscape with three parameters, including temperature, oxygen and food availability
#We define an organism whith two main charactreistics, which are body size and speed
my($student_number);
print "\nIntroduce your student number: ";
$student_number = <STDIN>;
chomp $student_number;
my($out) = "Results" . "_$student_number";
print "\n*****Your evolved population will be sent to $out*****\n";
open(O, ">$out");


srand(time|$$);
#my(@frequencies) = (0.05, 0.1, 0.15, 0.2, 0.25);
my($i, $j) = (0,0);
my(@population_values, @population);
#while($i < scalar(@frequencies))
#  {
 #   my($num) = $frequencies[$i] * $pop_size;
 #   my($valA) = $i + 1; my($valB) = 9 - $i;
 #   my($k) = 0;
 #   for($k = 0; $k < $num; $k++)
 #     {
#	$population_values[$j] = $valA;
#	$j += 1;
#	$population_values[$j] = $valB;
#	$j += 1;
 #     }
 #   $i += 1;
 # }
while($i < $pop_size)
  {
  #  $population_values[$i] = $initial_organism;
    $population[$i] = $initial_organism;
     $i += 1;
  }
#my(@sorted_population) = sort{$a <=> $b} @population_values;
###Now we are going to generate our organisms
$i = 0;
#my(@population);
#while($i < $pop_size)
#  {
#    my($body_size, $move);
#    $body_size = $sorted_population[int(rand(scalar(@sorted_population)))];
#    $move =  $sorted_population[int(rand(scalar(@sorted_population)))];
#    $population[$i] = $body_size . $move;
#    $i += 1;
#  }
###We are starting with the evolutionary dynamic now with the mutation process
my($generations) = 0;
my(@population_grown);
my(%Frequencies);
while($generations < $number_generations)
  {
print "\rGeneration: " . ($generations + 1);
my($number_mutations) = 0.1 * ($pop_size);
my(@chosen_organisms);
$i = 0;
while($i < $number_mutations)
  {
    my($orden, $sense);
    $orden = int(rand(2));
    $sense = int(rand(2));
    if($sense < 2)
      {
	$sense = 1;
      }else
	{
	  $sense = -1;
	}
    my($organism) = int(rand(scalar(@population)));
    if($orden < 2)
      {
	$orden = 0;
      }else
	{
	  $orden = 1;
	}
    my($first, $second);
    $first = substr($population[$organism], 0, 1);
    $second = substr($population[$organism], 1, 1);
    if($orden == 0)
      {
	if(($first > 1) && ($first < 9))
	  {
	    $first += $sense;
	  }elsif($first == 1)
	    {
	      $first += 1;
	    }elsif($first == 9)
	      {
		$first -= 1;
	      }
	$population[$organism] = $first . $second;
      }else
	{
	  if(($second > 1) && ($second < 9))
	  {
	    $second += $sense;
	  }elsif($second == 1)
	    {
	      $second += 1;
	    }elsif($second == 9)
	      {
		$second -= 1;
	      }
	  $population[$organism] = $first . $second;
	}
    $i += 1;
  }

###Now each one of the organisms will compete based on their biological fitnesses and 90% of those with better fitness will pass to next generations and new organisms will duplicate until completing population size.
my(%Fitness);
@population_grown = ();
$i = 0; $j = 0; my($k) = 0;
########################################################################################################################
###Estimating Fitness of organisms in population
$i = 0;my($sum_fitness) = 0;
while($i < scalar(@population))
  {
    $Fitness{$population[$i]} = fitness($population[$i], $temperature, $oxygen, $food);
    $sum_fitness += $Fitness{$population[$i]};
    $i += 1;
  }
###########################################################################################################################
###First we make organisms replicate
my($total_number_offspring) = $pop_size;

my($aver_fitness) = $sum_fitness/(scalar(@population));
my(%pop_filter); my($num_individuals) = 0;
my(@values, @sorted_values);
foreach my $filt(keys(%Fitness))
  {
  ###  if($Fitness{$filt} > 0.4)
    ###  {
	$pop_filter{$filt} = $Fitness{$filt};
	$values[$num_individuals] = $Fitness{$filt};
	$num_individuals += 1;
###      }
  }
@sorted_values = sort{$a <=> $b} @values;
my($individuals_to_distribute) = $pop_size - $num_individuals;

my($detected) = 0;
foreach my $organism(keys(%pop_filter))
  {
    if(($pop_filter{$organism} == $sorted_values[(scalar(@sorted_values)) - 1]) && ($detected == 0))
      {
	for($j = 0; $j <= $individuals_to_distribute; $j++)
	  {
	    $population_grown[$k] = $organism;
	    $k += 1;
	  }
	$detected += 1;
      }else
	{
	  for($j = 0; $j < 2; $j++)
	    {
	      $population_grown[$k] = $organism;
	      $k += 1;
	    }
	}
  }
my($size_of_population) = scalar(@population_grown);
###Now, we make them mutate at the rate pre-especified by the user
my($mutants) = int($mutation_rate * (scalar(@population_grown)));
$i = 0;
my($point_to_number, $Fit);
while($i < $mutants)
  {
    do
      {
    my($random_number) = int(rand(scalar(@population_grown)));
    my($random_organism) = $population_grown[$random_number];
    my($feature1, $feature2) = (substr($random_organism, 0, 1), substr($random_organism, 1, 1));
    my($choice) = int(rand(10));
    my($mutation);
    do
      {
	$mutation = int(rand(10));
      }until($mutation != 0);
    if($choice <= 5)
      {
	$feature1 = $mutation;
      }else
	{
	  $feature2 = $mutation;
	}
    $point_to_number = $random_number - 1;
    $population_grown[$random_number - 1] = $feature1 . $feature2;
}until(($Fit = fitness($population_grown[$point_to_number], $temperature, $oxygen, $food)) > 0.4);
    $i += 1;
  }
#############################################################################################################################################
###Now, we will calculate the fitness of the organisms in the grown population
$i = 0;
my(%population_transferred);
$sum_fitness = 0;
my(@fitnesses);
while($i < scalar(@population_grown))
  {
    $Fitness{$population_grown[$i]} = fitness($population_grown[$i], $temperature, $oxygen, $food);
    $sum_fitness += $Fitness{$population_grown[$i]};
    $fitnesses[$i] = $Fitness{$population_grown[$i]};
    $i += 1;
  }
my($population_transfer_next_generation) = $pop_size - ($bottleneck*$pop_size);
my($pop) = 0;
@population = ();
my(%fitness_final_population);
while($pop < $population_transfer_next_generation)
  {
    $population[$pop] = $population_grown[int(rand(scalar(@population_grown)))];
    $fitness_final_population{$population[$pop]} = $Fitness{$population[$pop]};
    $pop += 1;
  }
my($add_rest_ind) = $pop_size - $population_transfer_next_generation;
my($add) = 0;

while($add < $add_rest_ind)
  {
    $population[$pop] = $population[int(rand(scalar(@population)))];
    $pop += 1;
    $add += 1;
  }
$add = 0;
while($add < (scalar(@population)))
  {
    $fitness_final_population{$population[$add]} = $Fitness{$population[$add]};
    $add += 1;
  }
foreach my $fr(@population)
  {
   $Frequencies{$fr} = 0;
 }
foreach my $fre(@population)
  {
    $Frequencies{$fre} += 1;
  }
foreach my $FR(keys(%Frequencies))  
{
    $Frequencies{$FR} /= (scalar(@population));
    $Frequencies{$FR} *= 100;
  }
	
#############################################################################################################################################
####We are now going to print the frequency of the organisms with the highest fitness

$generations += 1;
}
print "\n";
print O "Student: $student_number\n------------------------\n\n";
print O "Predicted Organism: $prediction\n";
print O "Initial organism: $initial_organism\nPopulation_size: $pop_size\nMutation Rates: $mutation_rate\nBottleneck to population size: $bottleneck\n";
print O "\nEnvironment Settings\n---------------------\n";
if($temperature < 3)
{
  print O "Temperature ($temperature): Cold\n";
}elsif(($temperature >= 4) && ($temperature < 7))
  {
    print O "Temperature ($temperature): mild\n";
  }elsif($temperature >=7)
  {
    print O "Temperature ($temperature): Hot\n";
  }
if($oxygen < 3)
{
  print O "Oxygen ($oxygen): rare\n";
}elsif(($oxygen >= 4) && ($oxygen < 7))
  {
    print O "Oxygen ($oxygen): oxygen based atmosphere\n";
  }elsif($oxygen >=7)
  {
    print O "Oxygen ($oxygen): High oxygen pressure\n";
  }
if($food < 3)
{
  print O "Food resources ($food): scarse\n";
}elsif(($food >= 4) && ($food < 7))
  {
    print O "Food resources  ($food): medium\n";
  }elsif($food >=7)
  {
    print O "food  ($food): optimal environment with excess of resources\n";
  }
print O "\nFinal Population Frequencies:\n--------------------------------\n\n";
print O "Phenotype\t\tFrequency in the Population (%)\n---------\t\t--------------------------\n";
foreach my $Fr(sort{$Frequencies{$a} <=> $Frequencies{$b}} (keys(%Frequencies)))
  {
     print O "$Fr\t\t\t$Frequencies{$Fr}%\n";
  }

exit;


#############################################################################################################################################
##############################################################Subroutines####################################################################


sub fitness
  {
    my($organism_code, $temp, $oxy, $resources) = @_;
    my($body_size_code) = substr($organism_code, 0, 1);
    my($move_code) = substr($organism_code, 1, 1);
    my($fitnessfraction_food);
    if($resources < 4)
      {
	$fitnessfraction_food = (((2*$resources)/($body_size_code + (2*$resources)))+((2*$move_code)/((2*$move_code)+$resources)))/2;
      }else
	{
	  $fitnessfraction_food = (((2*$body_size_code)/((2*$body_size_code) + $resources))+((2*$move_code)/((2*$move_code)+$resources)))/2;
	}
    my($fitnessfraction_Temp);
    if($temp < 4)
      {
	$fitnessfraction_Temp = (((2*$body_size_code)/((2*$body_size_code) + $temp))+((2*$move_code)/($temp + (2*$move_code))))/2;
      }else
	{
	  $fitnessfraction_Temp = (((2*$temp)/($body_size_code + (2*$temp)))+((2*$temp)/($move_code + (2*$temp))))/2;
	}
    my($fitnessfraction_Ox) = (((2*$oxy)/($body_size_code + (2*$oxy)))+((2*$oxy)/($move_code+(2*$oxy))))/2;
    my($final_fitness) = ((2*$fitnessfraction_food)+(2*$fitnessfraction_Temp)+$fitnessfraction_Ox)/5;
    return $final_fitness;
  }

    
