神经网络深入 ,源码

来源:互联网 发布:惠普手机打印软件 编辑:程序博客网 时间:2024/06/07 17:48


神经网络深入 

  

源  码

  

   说明:以下为与神经网络深入》连载1-8相关的NEAT(神经网络拓扑演化)程序全部C++语言源码,不含头文件。这是《游戏编程中的人工智能技术》一书所附CD中有关第11章NEAT的全部源码,仅供已阅读《神经网络》连载1-8的读者深入阅读参考使用,我在源码中未添加任何中文解释,而且今后也不会为其中内容进行解释、答疑,甚至不关心是否有读者“到此一游”过。但可以肯定代码不会有差错,我已实际使用这些源文件编译过(用Borland C++,V3),完全能通过,并生成能正确运行的执行程序。


—— 目  录 ——

   

一共14个.cpp文件, 以下依次给出它们的源码


源  

1. C2DMatrix.cpp

#include "C2DMatrix.h"


/////////////////////////////////////////////////////////////////////
//
// Matrix functions
//
/////////////////////////////////////////////////////////////////////
//create an identity matrix
void C2DMatrix::Identity()
{
m_Matrix._11 = 1; m_Matrix._12 = 0; m_Matrix._13 = 0;


m_Matrix._21 = 0; m_Matrix._22 = 1; m_Matrix._23 = 0;


m_Matrix._31 = 0; m_Matrix._32 = 0; m_Matrix._33 = 1;


}


//create a transformation matrix
void C2DMatrix::Translate(double x, double y)
{
S2DMatrix mat;

mat._11 = 1; mat._12 = 0; mat._13 = 0;

mat._21 = 0; mat._22 = 1; mat._23 = 0;

mat._31 = x;    mat._32 = y;    mat._33 = 1;

//and multiply
  S2DMatrixMultiply(mat);
}


//create a scale matrix
void C2DMatrix::Scale(double xScale, double yScale)
{
S2DMatrix mat;

mat._11 = xScale; mat._12 = 0; mat._13 = 0;

mat._21 = 0; mat._22 = yScale; mat._23 = 0;

mat._31 = 0; mat._32 = 0; mat._33 = 1;

//and multiply
  S2DMatrixMultiply(mat);
}




//create a rotation matrix
void C2DMatrix::Rotate(double rot)
{
S2DMatrix mat;


double Sin = sin(rot);
double Cos = cos(rot);

mat._11 = Cos;  mat._12 = Sin; mat._13 = 0;

mat._21 = -Sin; mat._22 = Cos; mat._23 = 0;

mat._31 = 0; mat._32 = 0;mat._33 = 1;

//and multiply
  S2DMatrixMultiply(mat);
}


2.CController.cpp

#include "CController.h"

//these hold the geometry of the sweepers and the mines
const int NumSweeperVerts = 16;
const SPoint sweeper[NumSweeperVerts] = {SPoint(-1, -1),
                                         SPoint(-1, 1),
                                         SPoint(-0.5, 1),
                                         SPoint(-0.5, -1),


                                         SPoint(0.5, -1),
                                         SPoint(1, -1),
                                         SPoint(1, 1),
                                         SPoint(0.5, 1),
                                         
                                         SPoint(-0.5, -0.5),
                                         SPoint(0.5, -0.5),


                                         SPoint(-0.5, 0.5),
                                         SPoint(-0.25, 0.5),
                                         SPoint(-0.25, 1.75),
                                         SPoint(0.25, 1.75),
                                         SPoint(0.25, 0.5),
                                         SPoint(0.5, 0.5)};


const int NumMineVerts = 4;
const SPoint mine[NumMineVerts] = {SPoint(-1, -1),
                                   SPoint(-1, 1),
                                   SPoint(1, 1),
                                   SPoint(1, -1)};

                                      
const int NumObjectVerts = 42;
const SPoint objects[NumObjectVerts] = {SPoint(80, 60),
                                        SPoint(200,60),
                                        SPoint(200,60),
                                        SPoint(200,100),
                                        SPoint(200,100),
                                        SPoint(160,100),
                                        SPoint(160,100),
                                        SPoint(160,200),
                                        SPoint(160,200),
                                        SPoint(80,200),
                                        SPoint(80,200),
                                        SPoint(80,60),


                                        SPoint(250,100),
                                        SPoint(300,40),
                                        SPoint(300,40),
                                        SPoint(350,100),
                                        SPoint(350,100),
                                        SPoint(250, 100),


                                        SPoint(220,180),
                                        SPoint(320,180),
                                        SPoint(320,180),
                                        SPoint(320,300),
                                        SPoint(320,300),
                                        SPoint(220,300),
                                        SPoint(220,300),
                                        SPoint(220,180),


                                        SPoint(12,15),
                                        SPoint(380, 15),
                                        SPoint(380,15),
                                        SPoint(380,360),
                                        SPoint(380,360),                                
                                        SPoint(12,360),
                                        SPoint(12,360),
                                        SPoint(12,340),
                                        SPoint(12,340),
                                        SPoint(100,290),
                                        SPoint(100,290),
                                        SPoint(12,240),
                                        SPoint(12,240),
                                        SPoint(12,15)};


//---------------------------------------constructor---------------------
//
// initilaize the sweepers, their brains and the GA factory
//
//-----------------------------------------------------------------------
CController::CController(HWND hwndMain,
                         int  cxClient,
                         int  cyClient): m_NumSweepers(CParams::iNumSweepers), 
                    m_bFastRender(false),
                                         m_bRenderBest(false),
                    m_iTicks(0),
                    m_hwndMain(hwndMain),
                                         m_hwndInfo(NULL),
                    m_iGenerations(0),
                                         m_cxClient(cxClient),
                                         m_cyClient(cyClient),
                                         m_iViewThisSweeper(0)
{
  
//let's create the mine sweepers
for (int i=0; i<m_NumSweepers; ++i)
{
m_vecSweepers.push_back(CMinesweeper());
}
  
  //and the vector of sweepers which will hold the best performing sweeprs
  for (i=0; i<CParams::iNumBestSweepers; ++i)
{
m_vecBestSweepers.push_back(CMinesweeper());
}  


  m_pPop = new Cga(CParams::iNumSweepers,
                   CParams::iNumInputs,
                   CParams::iNumOutputs,
                   hwndMain,
                   10,10);  


  //create the phenotypes
   vector<CNeuralNet*> pBrains = m_pPop->CreatePhenotypes();
   
//assign the phenotypes
  for (i=0; i<m_NumSweepers; i++)
  {   
    m_vecSweepers[i].InsertNewBrain(pBrains[i]);
  }


//create a pen for the graph drawing
m_BluePen        = CreatePen(PS_SOLID, 1, RGB(0, 0, 255));
m_RedPen         = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
m_GreenPen       = CreatePen(PS_SOLID, 1, RGB(0, 255, 0));
  m_GreyPenDotted  = CreatePen(PS_DOT, 1, RGB(100, 100, 100));
  m_RedPenDotted   = CreatePen(PS_DOT, 1, RGB(200, 0, 0));


m_OldPen = NULL;


  //and the brushes
  m_BlueBrush = CreateSolidBrush(RGB(0,0,244));
  m_RedBrush = CreateSolidBrush(RGB(150,0,0));


//fill the vertex buffers
for (i=0; i<NumSweeperVerts; ++i)
{
m_SweeperVB.push_back(sweeper[i]);
}


  for (i=0; i<NumObjectVerts; ++i)
  {
    m_ObjectsVB.push_back(objects[i]);
  }
}


//--------------------------------------destructor-------------------------------------
//
//--------------------------------------------------------------------------------------
CController::~CController()
{
  if (m_pPop)
  {
    delete m_pPop;
  }
  
DeleteObject(m_BluePen);
DeleteObject(m_RedPen);
DeleteObject(m_GreenPen);
DeleteObject(m_OldPen);
  DeleteObject(m_GreyPenDotted);
  DeleteObject(m_RedPenDotted);
  DeleteObject(m_BlueBrush);
  DeleteObject(m_RedBrush);
}


//-------------------------------------Update---------------------------------------
//
// This is the main workhorse. The entire simulation is controlled from here.
//
//--------------------------------------------------------------------------------------
bool CController::Update()
{
//run the sweepers through NUM_TICKS amount of cycles. During this loop each
//sweepers NN is constantly updated with the appropriate information from its 
//surroundings. The output from the NN is obtained and the sweeper is moved. 
if (m_iTicks++ < CParams::iNumTicks)
{
for (int i=0; i<m_NumSweepers; ++i)
{
//update the NN and position
if (!m_vecSweepers[i].Update(m_ObjectsVB))
{
//error in processing the neural net
MessageBox(m_hwndMain, "Wrong amount of NN inputs!", "Error", MB_OK);


return false;
}
}


    //update the NNs of the last generations best performers
    if (m_iGenerations > 0)
    {
      for (int i=0; i<m_vecBestSweepers.size(); ++i)
      {
         //update the NN and position
  if (!m_vecBestSweepers[i].Update(m_ObjectsVB))
  {
   //error in processing the neural net
   MessageBox(m_hwndMain, "Wrong amount of NN inputs!", "Error", MB_OK);


   return false;
  }
      }
    }
}


//We have completed another generation so now we need to run the GA
else
{    
    //first add up each sweepers fitness scores. (remember for this task
    //there are many different sorts of penalties the sweepers may incur
    //and each one has a coefficient)
    for (int swp=0; swp<m_vecSweepers.size(); ++swp)
    {
        m_vecSweepers[swp].EndOfRunCalculations();
    }


//increment the generation counter
++m_iGenerations;


//reset cycles
m_iTicks = 0;


    //perform an epoch and grab the new brains
    vector<CNeuralNet*> pBrains = m_pPop->Epoch(GetFitnessScores());

//insert the new  brains back into the sweepers and reset their
    //positions
    for (int i=0; i<m_NumSweepers; ++i)
{
m_vecSweepers[i].InsertNewBrain(pBrains[i]);

m_vecSweepers[i].Reset();
}


    //grab the NNs of the best performers from the last generation
    vector<CNeuralNet*> pBestBrains = m_pPop->GetBestPhenotypesFromLastGeneration();


    //put them into our record of the best sweepers
    for (i=0; i<m_vecBestSweepers.size(); ++i)
{
m_vecBestSweepers[i].InsertNewBrain(pBestBrains[i]);

m_vecBestSweepers[i].Reset();
}


        //this will call WM_PAINT which will render our scene
 InvalidateRect(m_hwndInfo, NULL, TRUE);
UpdateWindow(m_hwndInfo);


}


return true;
}


//---------------------------------- RenderNetworks ----------------------
//
//  Renders the best four phenotypes from the previous generation
//------------------------------------------------------------------------
void CController::RenderNetworks(HDC &surface)
{
  if (m_iGenerations < 1)
  {
    return;
  }


  //draw the network of the best 4 genomes. First get the dimensions of the 
   //info window
   RECT rect;
GetClientRect(m_hwndInfo, &rect);


int cxInfo = rect.right;
int cyInfo = rect.bottom;


   //now draw the 4 best networks
   m_vecBestSweepers[0].DrawNet(surface, 0, cxInfo/2, cyInfo/2, 0);
   m_vecBestSweepers[1].DrawNet(surface, cxInfo/2, cxInfo, cyInfo/2, 0);
   m_vecBestSweepers[2].DrawNet(surface, 0, cxInfo/2, cyInfo, cyInfo/2);
   m_vecBestSweepers[3].DrawNet(surface, cxInfo/2, cxInfo, cyInfo, cyInfo/2);
}


//------------------------------------Render()--------------------------------------
//
//----------------------------------------------------------------------------------
void CController::Render(HDC &surface)
{
//do not render if running at accelerated speed
if (!m_bFastRender)
{   
    string s = "Generation: " + itos(m_iGenerations);
 TextOut(surface, 5, 0, s.c_str(), s.size());
    
    //select in the blue pen
    m_OldPen = (HPEN)SelectObject(surface, m_BluePen);
    
    if (m_bRenderBest)
    {  
      //render the best sweepers memory cells
      m_vecBestSweepers[m_iViewThisSweeper].Render(surface);


      //render the best sweepers from the last generation
      RenderSweepers(surface, m_vecBestSweepers);


      //render the best sweepers sensors
      RenderSensors(surface, m_vecBestSweepers);
    }
      
    else
    {
 //render the sweepers
      RenderSweepers(surface, m_vecSweepers);
    }


    SelectObject(surface, m_OldPen);


    //render the objects
    for (int i=0; i<NumObjectVerts; i+=2)
    {
      MoveToEx(surface, m_ObjectsVB[i].x, m_ObjectsVB[i].y, NULL);


      LineTo(surface, m_ObjectsVB[i+1].x, m_ObjectsVB[i+1].y);
    }


}//end if
 
  else
  {    
    PlotStats(surface);


    RECT sr;
    sr.top    = m_cyClient-50;
    sr.bottom = m_cyClient;
    sr.left   = 0;
    sr.right  = m_cxClient;


    //render the species chart
    m_pPop->RenderSpeciesInfo(surface, sr);


  }


}
//------------------------- RenderSweepers -------------------------------
//
//  given a vector of sweepers this function renders them to the screen
//------------------------------------------------------------------------
void CController::RenderSweepers(HDC &surface, vector<CMinesweeper> &sweepers)
{
  for (int i=0; i<sweepers.size(); ++i)
{
    
    //if they have crashed into an obstacle draw
    if ( sweepers[i].Collided())
    {
      SelectObject(surface, m_RedPen);
    }


    else
    {
      SelectObject(surface, m_BluePen);
    }
      
    //grab the sweeper vertices
 vector<SPoint> sweeperVB = m_SweeperVB;


 //transform the vertex buffer
 sweepers[i].WorldTransform(sweeperVB, sweepers[i].Scale());


 //draw the sweeper left track
MoveToEx(surface, (int)sweeperVB[0].x, (int)sweeperVB[0].y, NULL);


for (int vert=1; vert<4; ++vert)
{
 LineTo(surface, (int)sweeperVB[vert].x, (int)sweeperVB[vert].y);
}


    LineTo(surface, (int)sweeperVB[0].x, (int)sweeperVB[0].y);


    //draw the sweeper right track
 MoveToEx(surface, (int)sweeperVB[4].x, (int)sweeperVB[4].y, NULL);


for (vert=5; vert<8; ++vert)
{
   LineTo(surface, (int)sweeperVB[vert].x, (int)sweeperVB[vert].y);
}


    LineTo(surface, (int)sweeperVB[4].x, (int)sweeperVB[4].y);


    MoveToEx(surface, (int)sweeperVB[8].x, (int)sweeperVB[8].y, NULL);
    LineTo(surface, (int)sweeperVB[9].x, (int)sweeperVB[9].y);


    MoveToEx(surface, (int)sweeperVB[10].x, (int)sweeperVB[10].y, NULL);


    for (vert=11; vert<16; ++vert)
    {
  LineTo(surface, (int)sweeperVB[vert].x, (int)sweeperVB[vert].y);
}
}//next sweeper
}


//----------------------------- RenderSensors ----------------------------
//
//  renders the sensors of a given vector of sweepers
//------------------------------------------------------------------------
void CController::RenderSensors(HDC &surface, vector<CMinesweeper> &sweepers)
{
   //render the sensors
    for (int i=0; i<sweepers.size(); ++i)
    {
      //grab each sweepers sensor data
      vector<SPoint> tranSensors    = sweepers[i].Sensors();
      vector<double> SensorReadings = sweepers[i].SensorReadings();
      vector<double> MemoryReadings = sweepers[i].MemoryReadings();


      for (int sr=0; sr<tranSensors.size(); ++sr)
      {
        if (SensorReadings[sr] > 0)
        {
          SelectObject(surface, m_RedPen);
        }


        else
        {
          SelectObject(surface, m_GreyPenDotted);
        }
        
        //make sure we clip the drawing of the sensors or we will get
        //unwanted artifacts appearing
        if (!((fabs(sweepers[i].Position().x - tranSensors[sr].x) >
              (CParams::dSensorRange+1))||
              (fabs(sweepers[i].Position().y - tranSensors[sr].y) >
              (CParams::dSensorRange+1))))
        {
        
          MoveToEx(surface,
                   (int)sweepers[i].Position().x,
                   (int)sweepers[i].Position().y,
                   NULL);


          LineTo(surface, (int)tranSensors[sr].x, (int)tranSensors[sr].y);


          //render the cell sensors
          RECT rect;
          rect.left  = (int)tranSensors[sr].x - 2;
          rect.right = (int)tranSensors[sr].x + 2;
          rect.top   = (int)tranSensors[sr].y - 2;
          rect.bottom= (int)tranSensors[sr].y + 2;
          
          if (MemoryReadings[sr] < 0)
          {


            FillRect(surface, &rect, m_BlueBrush);
          }


          else
          {
            FillRect(surface, &rect, m_RedBrush);
          }
          
        }
      }
    }   
}




//--------------------------PlotStats-------------------------------------
//
//  Given a surface to draw on this function displays some simple stats 
//------------------------------------------------------------------------
void CController::PlotStats(HDC surface)const
{
    string s = "Generation:              " + itos(m_iGenerations);
 TextOut(surface, 5, 25, s.c_str(), s.size());
    
    s = "Num Species:          " + itos(m_pPop->NumSpecies());
 TextOut(surface, 5, 45, s.c_str(), s.size());


    s = "Best Fitness so far: " + ftos(m_pPop->BestEverFitness());
    TextOut(surface, 5, 5, s.c_str(), s.size());
}


//------------------------------- GetFitnessScores -----------------------
//
//  returns a std::vector containing the genomes fitness scores
//------------------------------------------------------------------------
vector<double> CController::GetFitnessScores()const
{
  vector<double> scores;


  for (int i=0; i<m_vecSweepers.size(); ++i)
  {
     scores.push_back(m_vecSweepers[i].Fitness());
  }
  return scores;
}



3. Cga.cpp:

#include "Cga.h"

//-------------------------------------------------------------------------
// this constructor creates a base genome from supplied values and creates 
// a population of 'size' similar (same topology, varying weights) genomes
//-------------------------------------------------------------------------
Cga::Cga(int  size,
         int  inputs,
         int  outputs,
         HWND hwnd,
         int  cx,
         int  cy):  m_iPopSize(size),
                    m_iGeneration(0),
                    m_pInnovation(NULL),
                    m_iNextGenomeID(0),
                    m_iNextSpeciesID(0),
                    m_iFittestGenome(0),
                    m_dBestEverFitness(0),
                    m_dTotFitAdj(0),
                    m_dAvFitAdj(0),
                    m_hwnd(hwnd),
                    cxClient(cx),
                    cyClient(cy)
{
//create the population of genomes
for (int i=0; i<m_iPopSize; ++i)
{
m_vecGenomes.push_back(CGenome(m_iNextGenomeID++, inputs, outputs));
}


//create the innovation list. First create a minimal genome
CGenome genome(1, inputs, outputs);


//create the innovations
  m_pInnovation = new CInnovation(genome.Genes(), genome.Neurons());


  //create the network depth lookup table
  vecSplits = Split(0, 1, 0);
}


//------------------------------------- dtor -----------------------------
//
//------------------------------------------------------------------------
Cga::~Cga()
{
  if (m_pInnovation)
  {
    delete m_pInnovation;
      
    m_pInnovation = NULL;
  } 
}


//-------------------------------CreatePhenotypes-------------------------
//
// cycles through all the members of the population and creates their
//  phenotypes. Returns a vector containing pointers to the new phenotypes
//-------------------------------------------------------------------------
vector<CNeuralNet*> Cga::CreatePhenotypes()
{
vector<CNeuralNet*> networks;


  for (int i=0; i<m_iPopSize; i++)
{
    //calculate max network depth
    int depth = CalculateNetDepth(m_vecGenomes[i]);


//create new phenotype
CNeuralNet* net = m_vecGenomes[i].CreatePhenotype(depth);


    networks.push_back(net);
}


return networks;
}


//-------------------------- CalculateNetDepth ---------------------------
//
//  searches the lookup table for the dSplitY value of each node in the 
//  genome and returns the depth of the network based on this figure
//------------------------------------------------------------------------
int Cga::CalculateNetDepth(const CGenome &gen)
{
  int MaxSoFar = 0;


  for (int nd=0; nd<gen.NumNeurons(); ++nd)
  {
    for (int i=0; i<vecSplits.size(); ++i)
    {
     
      if ((gen.SplitY(nd) == vecSplits[i].val) &&
          (vecSplits[i].depth > MaxSoFar))
      {
        MaxSoFar = vecSplits[i].depth;
      }
    }
  }
  return MaxSoFar + 2;
}




//-----------------------------------AddNeuronID----------------------------
//
// just checks to see if a node ID has already been added to a vector of
//  nodes. If not then the new ID  gets added. Used in Crossover.
//------------------------------------------------------------------------
void Cga::AddNeuronID(const int nodeID, vector<int> &vec)
{
for (int i=0; i<vec.size(); i++)
{
if (vec[i] == nodeID)
{
//already added
return;
}
}


vec.push_back(nodeID);


return;
}


//------------------------------------- Epoch ----------------------------
//
//  This function performs one epoch of the genetic algorithm and returns 
//  a vector of pointers to the new phenotypes
//------------------------------------------------------------------------
vector<CNeuralNet*> Cga::Epoch(const vector<double> &FitnessScores)
{
  //first check to make sure we have the correct amount of fitness scores
  if (FitnessScores.size() != m_vecGenomes.size())
  {
    MessageBox(NULL,"Cga::Epoch(scores/ genomes mismatch)!","Error", MB_OK);
  }


  //reset appropriate values and kill off the existing phenotypes and
  //any poorly performing species
  ResetAndKill();


  //update the genomes with the fitnesses scored in the last run
  for (int gen=0; gen<m_vecGenomes.size(); ++gen)
  {
    m_vecGenomes[gen].SetFitness(FitnessScores[gen]);
  }


  //sort genomes and keep a record of the best performers
  SortAndRecord();


  //separate the population into species of similar topology, adjust
  //fitnesses and calculate spawn levels
  SpeciateAndCalculateSpawnLevels();


  //this will hold the new population of genomes
  vector<CGenome> NewPop;


  //request the offspring from each species. The number of children to 
  //spawn is a double which we need to convert to an int. 
  int NumSpawnedSoFar = 0;


  CGenome baby;


  //now to iterate through each species selecting offspring to be mated and 
  //mutated
  for (int spc=0; spc<m_vecSpecies.size(); ++spc)
  {
    //because of the number to spawn from each species is a double
    //rounded up or down to an integer it is possible to get an overflow
    //of genomes spawned. This statement just makes sure that doesn't
    //happen
    if (NumSpawnedSoFar < CParams::iNumSweepers)
    {
      //this is the amount of offspring this species is required to
      // spawn. Rounded simply rounds the double up or down.
      int NumToSpawn = Rounded(m_vecSpecies[spc].NumToSpawn());


      bool bChosenBestYet = false;


      while (NumToSpawn--)
      {
        //first grab the best performing genome from this species and transfer
        //to the new population without mutation. This provides per species
        //elitism
        if (!bChosenBestYet)
        {         
          baby = m_vecSpecies[spc].Leader();


          bChosenBestYet = true;
        }


        else
        {
          //if the number of individuals in this species is only one
          //then we can only perform mutation
          if (m_vecSpecies[spc].NumMembers() == 1)
          {         
            //spawn a child
            baby = m_vecSpecies[spc].Spawn();
          }


          //if greater than one we can use the crossover operator
          else
          {
            //spawn1
            CGenome g1 = m_vecSpecies[spc].Spawn();


            if (RandFloat() < CParams::dCrossoverRate)
            {


              //spawn2, make sure it's not the same as g1
              CGenome g2 = m_vecSpecies[spc].Spawn();


              //number of attempts at finding a different genome
              int NumAttempts = 5;


              while ( (g1.ID() == g2.ID()) && (NumAttempts--) )
              {  
                g2 = m_vecSpecies[spc].Spawn();
              }


              if (g1.ID() != g2.ID())
              {
                baby = Crossover(g1, g2);
              }
            }


            else
            {
              baby = g1;
            }
          }


          
          ++m_iNextGenomeID;


          baby.SetID(m_iNextGenomeID);


          //now we have a spawned child lets mutate it! First there is the
          //chance a neuron may be added
          if (baby.NumNeurons() < CParams::iMaxPermittedNeurons)
          {      
            baby.AddNeuron(CParams::dChanceAddNode,
                           *m_pInnovation,
                           CParams::iNumTrysToFindOldLink);
          }


          //now there's the chance a link may be added
          baby.AddLink(CParams::dChanceAddLink,
                       CParams::dChanceAddRecurrentLink,
                       *m_pInnovation,
                       CParams::iNumTrysToFindLoopedLink,
                       CParams::iNumAddLinkAttempts);


          //mutate the weights
          baby.MutateWeights(CParams::dMutationRate,
                             CParams::dProbabilityWeightReplaced,
                             CParams::dMaxWeightPerturbation);


          baby.MutateActivationResponse(CParams::dActivationMutationRate,
                                        CParams::dMaxActivationPerturbation);
        }


        //sort the babies genes by their innovation numbers
        baby.SortGenes();


        //add to new pop
        NewPop.push_back(baby);


        ++NumSpawnedSoFar;


        if (NumSpawnedSoFar == CParams::iNumSweepers)
        {        
          NumToSpawn = 0;
        }


      }//end while
       
    }//end if
     
  }//next species




  //if there is an underflow due to the rounding error and the amount
  //of offspring falls short of the population size additional children
  //need to be created and added to the new population. This is achieved
  //simply, by using tournament selection over the entire population.
  if (NumSpawnedSoFar < CParams::iNumSweepers)
  {


    //calculate amount of additional children required
    int Rqd = CParams::iNumSweepers - NumSpawnedSoFar;


    //grab them
    while (Rqd--)
    {
      NewPop.push_back(TournamentSelection(m_iPopSize/5));
    }
  }


  //replace the current population with the new one
  m_vecGenomes = NewPop;


  //create the new phenotypes
  vector<CNeuralNet*> new_phenotypes;


  for (gen=0; gen<m_vecGenomes.size(); ++gen)
  {
    //calculate max network depth
    int depth = CalculateNetDepth(m_vecGenomes[gen]);
    
    CNeuralNet* phenotype = m_vecGenomes[gen].CreatePhenotype(depth);


    new_phenotypes.push_back(phenotype);
  }


  //increase generation counter
  ++m_iGeneration;


  return new_phenotypes;
}


//--------------------------- SortAndRecord-------------------------------
//
//  sorts the population into descending fitness, keeps a record of the
//  best n genomes and updates any fitness statistics accordingly
//------------------------------------------------------------------------
void Cga::SortAndRecord()
{
  //sort the genomes according to their unadjusted (no fitness sharing)
  //fitnesses
  sort(m_vecGenomes.begin(), m_vecGenomes.end());


  //is the best genome this generation the best ever?
  if (m_vecGenomes[0].Fitness() > m_dBestEverFitness)
  {
    m_dBestEverFitness = m_vecGenomes[0].Fitness();
  }


  //keep a record of the n best genomes
  StoreBestGenomes();
}


//----------------------------- StoreBestGenomes -------------------------
//
//  used to keep a record of the previous populations best genomes so that 
//  they can be displayed if required.
//------------------------------------------------------------------------
void Cga::StoreBestGenomes()
{
  //clear old record
  m_vecBestGenomes.clear();


  for (int gen=0; gen<CParams::iNumBestSweepers; ++gen)
  {
    m_vecBestGenomes.push_back(m_vecGenomes[gen]);
  }
}


//----------------- GetBestPhenotypesFromLastGeneration ------------------
//
//  returns a std::vector of the n best phenotypes from the previous
//  generation
//------------------------------------------------------------------------
vector<CNeuralNet*> Cga::GetBestPhenotypesFromLastGeneration()
{
  vector<CNeuralNet*> brains;


  for (int gen=0; gen<m_vecBestGenomes.size(); ++gen)
  {
    //calculate max network depth
    int depth = CalculateNetDepth(m_vecBestGenomes[gen]);


    brains.push_back(m_vecBestGenomes[gen].CreatePhenotype(depth));
  }


  return brains;
}


//--------------------------- AdjustSpecies ------------------------------
//
//  this functions simply iterates through each species and calls 
//  AdjustFitness for each species
//------------------------------------------------------------------------
void Cga::AdjustSpeciesFitnesses()
{
  for (int sp=0; sp<m_vecSpecies.size(); ++sp)
  {
    m_vecSpecies[sp].AdjustFitnesses();
  }
}


//------------------ SpeciateAndCalculateSpawnLevels ---------------------
//
//  separates each individual into its respective species by calculating
//  a compatibility score with every other member of the population and 
//  niching accordingly. The function then adjusts the fitness scores of
//  each individual by species age and by sharing and also determines
//  how many offspring each individual should spawn.
//------------------------------------------------------------------------
void Cga::SpeciateAndCalculateSpawnLevels()
{
  bool bAdded = false;


  //iterate through each genome and speciate
  for (int gen=0; gen<m_vecGenomes.size(); ++gen)
  {
    //calculate its compatibility score with each species leader. If
    //compatible add to species. If not, create a new species
    for (int spc=0; spc<m_vecSpecies.size(); ++spc)
    {
      double compatibility = m_vecGenomes[gen].GetCompatibilityScore(m_vecSpecies[spc].Leader());


      //if this individual is similar to this species add to species
      if (compatibility <= CParams::dCompatibilityThreshold)
      {
        m_vecSpecies[spc].AddMember(m_vecGenomes[gen]);


        //let the genome know which species it's in
        m_vecGenomes[gen].SetSpecies(m_vecSpecies[spc].ID());


        bAdded = true;


        break;
      }
    }
    
    if (!bAdded)
    {
      //we have not found a compatible species so let's create a new one
      m_vecSpecies.push_back(CSpecies(m_vecGenomes[gen], m_iNextSpeciesID++));
    }


    bAdded = false;
  }


  //now all the genomes have been assigned a species the fitness scores 
  //need to be adjusted to take into account sharing and species age.
  AdjustSpeciesFitnesses();
  
  //calculate new adjusted total & average fitness for the population
  for (gen=0; gen<m_vecGenomes.size(); ++gen)
  {   
     m_dTotFitAdj += m_vecGenomes[gen].GetAdjFitness();
  }


  m_dAvFitAdj = m_dTotFitAdj/m_vecGenomes.size();


  //calculate how many offspring each member of the population
  //should spawn
  for (gen=0; gen<m_vecGenomes.size(); ++gen)
  {   
     double ToSpawn = m_vecGenomes[gen].GetAdjFitness() / m_dAvFitAdj;


     m_vecGenomes[gen].SetAmountToSpawn(ToSpawn);
  }


  //iterate through all the species and calculate how many offspring
  //each species should spawn
  for (int spc=0; spc<m_vecSpecies.size(); ++spc)
  {
    m_vecSpecies[spc].CalculateSpawnAmount();
  }
}




//--------------------------- TournamentSelection ------------------------
//
//------------------------------------------------------------------------
CGenome Cga::TournamentSelection(const int NumComparisons)
{
   double BestFitnessSoFar = 0;
  
   int ChosenOne = 0;


   //Select NumComparisons members from the population at random testing  
   //against the best found so far
   for (int i=0; i<NumComparisons; ++i)
   {
     int ThisTry = RandInt(0, m_vecGenomes.size()-1);


     if (m_vecGenomes[ThisTry].Fitness() > BestFitnessSoFar)
     {
       ChosenOne = ThisTry;


       BestFitnessSoFar = m_vecGenomes[ThisTry].Fitness();
     }
   }


   //return the champion
   return m_vecGenomes[ChosenOne];
}
                                 
//-----------------------------------Crossover----------------------------
//
//------------------------------------------------------------------------
CGenome Cga::Crossover(CGenome& mum, CGenome& dad)
{


  //helps make the code clearer
  enum parent_type{MUM, DAD,};
  
  //first, calculate the genome we will using the disjoint/excess
  //genes from. This is the fittest genome.
  parent_type best;


  //if they are of equal fitness use the shorter (because we want to keep
  //the networks as small as possible)
  if (mum.Fitness() == dad.Fitness())
  {
    //if they are of equal fitness and length just choose one at
    //random
    if (mum.NumGenes() == dad.NumGenes())
    {
      best = (parent_type)RandInt(0, 1);
    }


    else
    {
      if (mum.NumGenes() < dad.NumGenes())
      {
        best = MUM;
      }
 
      else
      {
        best = DAD;
      }
    }
  }


  else 
  {
    if (mum.Fitness() > dad.Fitness())
    {
      best = MUM;
    }


    else
    {
      best = DAD;
    }
  }


  //these vectors will hold the offspring's nodes and genes
  vector<SNeuronGene>  BabyNeurons;
  vector<SLinkGene>    BabyGenes;


  //temporary vector to store all added node IDs
  vector<int> vecNeurons;


  //create iterators so we can step through each parents genes and set
  //them to the first gene of each parent
  vector<SLinkGene>::iterator curMum = mum.StartOfGenes();
  vector<SLinkGene>::iterator curDad = dad.StartOfGenes();


  //this will hold a copy of the gene we wish to add at each step
  SLinkGene SelectedGene;


  //step through each parents genes until we reach the end of both
  while (!((curMum == mum.EndOfGenes()) && (curDad == dad.EndOfGenes())))
  {

    //the end of mum's genes have been reached
    if ((curMum == mum.EndOfGenes())&&(curDad != dad.EndOfGenes()))
    {
      //if dad is fittest
      if (best == DAD)
      {
        //add dads genes
        SelectedGene = *curDad;
      }


      //move onto dad's next gene
      ++curDad;
    }


    //the end of dads's genes have been reached
    else if ( (curDad == dad.EndOfGenes()) && (curMum != mum.EndOfGenes()))
    {
      //if mum is fittest
      if (best == MUM)
      {
        //add mums genes
        SelectedGene = *curMum;
      }

      //move onto mum's next gene
      ++curMum;
    }


    //if mums innovation number is less than dads
    else if (curMum->InnovationID < curDad->InnovationID)
    {
      //if mum is fittest add gene
      if (best == MUM)
      {
        SelectedGene = *curMum;
      }


      //move onto mum's next gene
      ++curMum;
    }


    //if dads innovation number is less than mums
    else if (curDad->InnovationID < curMum->InnovationID)
    {
      //if dad is fittest add gene
      if (best = DAD)
      {
        SelectedGene = *curDad;
      }


      //move onto dad's next gene
      ++curDad;
    }


    //if innovation numbers are the same
    else if (curDad->InnovationID == curMum->InnovationID)
    {
      //grab a gene from either parent
      if (RandFloat() < 0.5f)
      {
        SelectedGene = *curMum;
      }


      else
      {
        SelectedGene = *curDad;
      }


      //move onto next gene of each parent
      ++curMum;
      ++curDad;
    }

    //add the selected gene if not already added
    if (BabyGenes.size() == 0)
    {
      BabyGenes.push_back(SelectedGene);
    }


    else
    {
      if (BabyGenes[BabyGenes.size()-1].InnovationID !=
          SelectedGene.InnovationID)
      {
        BabyGenes.push_back(SelectedGene);
      }
    }   


    //Check if we already have the nodes referred to in SelectedGene.
    //If not, they need to be added.
    AddNeuronID(SelectedGene.FromNeuron, vecNeurons);
    AddNeuronID(SelectedGene.ToNeuron, vecNeurons);

  }//end while


  //now create the required nodes. First sort them into order
  sort(vecNeurons.begin(), vecNeurons.end());
  
  for (int i=0; i<vecNeurons.size(); i++)
  {
    BabyNeurons.push_back(m_pInnovation->CreateNeuronFromID(vecNeurons[i]));
  }


  //finally, create the genome
  CGenome babyGenome(m_iNextGenomeID++,
                     BabyNeurons,
                     BabyGenes,
                     mum.NumInputs(),
                     mum.NumOutputs());


  return babyGenome;
}




//--------------------------- ResetAndKill -------------------------------
//
//  This function resets some values ready for the next epoch, kills off
//  all the phenotypes and any poorly performing species.
//------------------------------------------------------------------------
void Cga::ResetAndKill()
{
  m_dTotFitAdj = 0;
  m_dAvFitAdj  = 0;


  //purge the species
  vector<CSpecies>::iterator curSp = m_vecSpecies.begin();


  while (curSp != m_vecSpecies.end())
  {
    curSp->Purge();


    //kill off species if not improving and if not the species which contains 
    //the best genome found so far
    if ( (curSp->GensNoImprovement() > CParams::iNumGensAllowedNoImprovement) &&
         (curSp->BestFitness() < m_dBestEverFitness) )
    {
     curSp = m_vecSpecies.erase(curSp);
     --curSp;
    }


    ++curSp;
  }


  //we can also delete the phenotypes
  for (int gen=0; gen<m_vecGenomes.size(); ++gen)
  {
    m_vecGenomes[gen].DeletePhenotype();
  }
}


//------------------------------- Split ----------------------------------
//
//  this function is used to create a lookup table that is used to
//  calculate the depth of the network. 
//------------------------------------------------------------------------
vector<SplitDepth> Cga::Split(double low, double high, int depth)
{
  static vector<SplitDepth> vecSplits;


  double span = high-low;


  vecSplits.push_back(SplitDepth(low + span/2, depth+1));


  if (depth > 6)
  {
    return vecSplits;
  }


  else
  {
    Split(low, low+span/2, depth+1);
    Split(low+span/2, high, depth+1);


    return vecSplits;
  }
}


//--------------------------- RenderSpeciesInfo --------------------------
//
//  does what it says on the tin 
//------------------------------------------------------------------------
void Cga::RenderSpeciesInfo(HDC &surface, RECT db)
{
  if (m_vecSpecies.size() < 1) return;
  
  int numColours = 255/m_vecSpecies.size();


  double SlicePerSweeper = (double)(db.right-db.left)/(double)(CParams::iNumSweepers-1);


  double left = db.left;


  //now draw a different colored rectangle for each species
  for (int spc=0; spc<m_vecSpecies.size(); ++spc)
  {


     //choose a brush to draw with
     HBRUSH PieBrush = CreateSolidBrush(RGB(numColours*spc, 255, 255 - numColours*spc));


     HBRUSH OldBrush = (HBRUSH)SelectObject(surface, PieBrush);
     
     if (spc == m_vecSpecies.size()-1)
     {
       Rectangle(surface, 
                  left,
                  db.top,
                  db.right,
                  db.bottom);
     }


     else
     {
       Rectangle(surface, 
                  left,
                  db.top,
                  left+SlicePerSweeper*m_vecSpecies[spc].NumMembers(),
                  db.bottom);
     }


     left += SlicePerSweeper * m_vecSpecies[spc].NumMembers();


     SelectObject(surface, OldBrush);
     DeleteObject(PieBrush);


     //display best performing species stats in the same color as displayed
     //in the distribution bar
     if ( m_vecSpecies[spc].BestFitness() == m_dBestEverFitness)
     {
       string s = "Best Species ID: " + itos(m_vecSpecies[spc].ID());
       TextOut(surface, 5, db.top - 80, s.c_str(), s.size());
       
       s = "Species Age: " + itos(m_vecSpecies[spc].Age());          
       TextOut(surface, 5, db.top - 60, s.c_str(), s.size());


       s = "Gens no improvement: " + itos(m_vecSpecies[spc].GensNoImprovement());
       TextOut(surface, 5, db.top - 40, s.c_str(), s.size());
     }
  }
  
  string s = "Species Distribution Bar";
TextOut(surface, 5, db.top - 20, s.c_str(), s.size());
}


4. CInnovation.cpp

#include "CInnovation.h"


//---------------------------------- ctor --------------------------------
//
//  given a series of start genes and start neurons this ctor adds
//  all the appropriate innovations.
//------------------------------------------------------------------------
CInnovation::CInnovation(vector<SLinkGene>   start_genes,
                         vector<SNeuronGene> start_neurons)
{
m_NextNeuronID = 0;
m_NextInnovationNum = 0;


//add the neurons
  for (int nd=0; nd<start_neurons.size(); ++nd)
  {
    m_vecInnovs.push_back(SInnovation(start_neurons[nd],
                                      m_NextInnovationNum++,
                                      m_NextNeuronID++));
  }


  //add the links
  for (int cGen = 0; cGen<start_genes.size(); ++cGen) 
{
SInnovation NewInnov(start_genes[cGen].FromNeuron,
                         start_genes[cGen].ToNeuron,
                         new_link,
                         m_NextInnovationNum);


m_vecInnovs.push_back(NewInnov);


    ++m_NextInnovationNum;


}
}


//---------------------------CheckInnovation------------------------------
//
// checks to see if this innovation has already occurred. If it has it
// returns the innovation ID. If not it returns a negative value. 
//------------------------------------------------------------------------
int CInnovation::CheckInnovation(int in, int out, innov_type type)
{
  //iterate through the innovations looking for a match on all
  //three parameters
for (int inv=0; inv<m_vecInnovs.size(); ++inv)
{
    if ( (m_vecInnovs[inv].NeuronIn == in)   && 
         (m_vecInnovs[inv].NeuronOut == out) && 
         (m_vecInnovs[inv].InnovationType == type))
{
//found a match so assign this innovation number to id
return m_vecInnovs[inv].InnovationID;
}
}

//if no match return a negative value
  return -1;
}


//--------------------------CreateNewInnovation---------------------------
//
// creates a new innovation and returns its ID
//------------------------------------------------------------------------
int CInnovation::CreateNewInnovation(int in, int out, innov_type type)
{
SInnovation new_innov(in, out, type, m_NextInnovationNum);

if (type == new_neuron)
{
new_innov.NeuronID = m_NextNeuronID;

++m_NextNeuronID;
}


  m_vecInnovs.push_back(new_innov);


  ++m_NextInnovationNum;

return (m_NextNeuronID-1);
}


//------------------------------------------------------------------------
//
//  as above but includes adding x/y position of new neuron
//------------------------------------------------------------------------
int CInnovation::CreateNewInnovation(int          from,
                                     int          to,
                                     innov_type   InnovType,
                                     neuron_type  NeuronType,
                                     double       x,
                                     double       y)



SInnovation new_innov(from, to, InnovType, m_NextInnovationNum, NeuronType, x, y);

if (InnovType == new_neuron)
{
new_innov.NeuronID = m_NextNeuronID;

++m_NextNeuronID;
}


  m_vecInnovs.push_back(new_innov);


  ++m_NextInnovationNum;

return (m_NextNeuronID-1);


}


//------------------------------- CreateNeuronFromID -----------------------
//
//  given a neuron ID this function returns a clone of that neuron
//------------------------------------------------------------------------
SNeuronGene CInnovation::CreateNeuronFromID(int NeuronID)
{
  SNeuronGene temp(hidden,0,0,0);


  for (int inv=0; inv<m_vecInnovs.size(); ++inv)
  {
    if (m_vecInnovs[inv].NeuronID == NeuronID)
    {
      temp.NeuronType = m_vecInnovs[inv].NeuronType;
      temp.iID      = m_vecInnovs[inv].NeuronID;
      temp.dSplitY  = m_vecInnovs[inv].dSplitY;
      temp.dSplitX  = m_vecInnovs[inv].dSplitX;


      return temp;
    }
  }


  return temp;
}



5.CMapper.cpp

.#include "CMapper.h"


//--------------------------- Init ---------------------------------------
//
//  This method needs to be called before you can use the instance.
//------------------------------------------------------------------------
void CMapper::Init(int MaxRangeX, int MaxRangeY)
{
  //if already initialized return
  if(m_NumCellsX) return;


  m_dCellSize = CParams::dCellSize;
  
  //first calculate how many segments we will require
  m_NumCellsX = (int)(MaxRangeX/m_dCellSize)+1;
  m_NumCellsY = (int)(MaxRangeY/m_dCellSize)+1;
  
  //create the 2d vector of blank segments
  for (int x=0; x<m_NumCellsX; ++x)
  {
    vector<SCell> temp;


    for (int y=0; y<m_NumCellsY; ++y)
    {
      temp.push_back(SCell(x*m_dCellSize, (x+1)*m_dCellSize, y*m_dCellSize, (y+1)*m_dCellSize));
    }


    m_2DvecCells.push_back(temp);
  }


  m_iTotalCells = m_NumCellsX * m_NumCellsY;


}
//-------------------------------------------------------------
void CMapper::Update(double xPos, double yPos)
{
    //check to make sure positions are within range
  if ( (xPos < 0) || (xPos > CParams::WindowWidth) || 
       (yPos < 0) || (yPos > CParams::WindowHeight) )
  {
    return;
  }
  
  int cellX = (int)(xPos / m_dCellSize );
  int cellY = (int)(yPos / m_dCellSize );


  m_2DvecCells[cellX][cellY].Update();


  return;
}


//---------------------------------------------------------------
int CMapper::TicksLingered(double xPos, double yPos)const
{
    //bounds check the incoming values
  if ( (xPos > CParams::WindowWidth) || (xPos < 0) ||
       (yPos > CParams::WindowHeight)|| (yPos < 0))
  {
    return 999;
  }
  
  int cellX = (int)(xPos / m_dCellSize);
  int cellY = (int)(yPos / m_dCellSize);


  return m_2DvecCells[cellX][cellY].iTicksSpentHere;
}


//------------------------- Visited --------------------------------------
//
//------------------------------------------------------------------------
bool CMapper::BeenVisited(double xPos, double yPos)const
{
  int cellX = (int)(xPos / m_dCellSize);
  int cellY = (int)(yPos / m_dCellSize);


  if (m_2DvecCells[cellX][cellY].iTicksSpentHere > 0)
  {
    return true;
  }


  else
  {
    return false;
  }
}
//--------------------------------- Render -------------------------------
//
//  renders the visited cells. The color gets darker the more frequently
//  the cell has been visited.
//------------------------------------------------------------------------
void CMapper::Render(HDC surface)
{


  for (int x=0; x<m_NumCellsX; ++x)
  {
    for (int y=0; y<m_NumCellsY; ++y)
    {
      if (m_2DvecCells[x][y].iTicksSpentHere > 0)
      {
        int shading = 2 * m_2DvecCells[x][y].iTicksSpentHere;


        if (shading >220)
        {
          shading = 220;
        }


        
        HBRUSH lightbrush = CreateSolidBrush(RGB(240,220-shading,220-shading));
        
        FillRect(surface, &m_2DvecCells[x][y].Cell, lightbrush); 


        DeleteObject(lightbrush);
      }
    }
  } 
}


//----------------------------------- Reset ------------------------------
void CMapper::Reset()
{


  for (int x=0; x<m_NumCellsX; ++x)
  {
    for (int y=0; y<m_NumCellsY; ++y)
    {
     m_2DvecCells[x][y].Reset();
    }
  }
}




int CMapper::NumCellsVisited() const
{
  int total = 0;


  for (int x=0; x<m_NumCellsX; ++x)
  {
    for (int y=0; y<m_NumCellsY; ++y)
    {
      if (m_2DvecCells[x][y].iTicksSpentHere > 0)
      {
        ++total;
      }
    }
  }


  return total;
}




6.CMinesweeper.cpp

#include "CMinesweeper.h"


//-----------------------------------constructor-------------------------
//
//-----------------------------------------------------------------------
CMinesweeper::CMinesweeper():
                             m_dRotation(0),
                             m_lTrack(0),
                             m_rTrack(0),
                             m_dFitness(0),
              m_dScale(CParams::iSweeperScale),
                             m_bCollided(false)
                             
 
{
  //create a static start position
  m_vPosition = SVector2D(180, 200);


   //create the sensors
  CreateSensors(m_Sensors, CParams::iNumSensors, CParams::dSensorRange); 


  //initialize its memory
  m_MemoryMap.Init(CParams::WindowWidth,
                   CParams::WindowHeight);


}




//-------------------------------- CreateSensors ------------------------
//
// This function returns a vector of points which make up the segments of
//  the sweepers sensors.
//------------------------------------------------------------------------
void CMinesweeper::CreateSensors(vector<SPoint> &sensors,
                                 int            NumSensors,
                                 double         range)
{
  //make sure vector of sensors is empty before proceeding
  sensors.clear();


  double SegmentAngle = CParams::dPi / (NumSensors-1);


//going clockwise from 90deg left of position calculate the fan of
//points radiating out (not including the origin)
for (int i=0; i<CParams::iNumSensors; i++)
{
//calculate vertex position
SPoint point;


    point.x = -sin(i * SegmentAngle - CParams::dHalfPi) * range;


 point.y = cos(i * SegmentAngle - CParams::dHalfPi) * range;

sensors.push_back(point);

}//next segment


}
//-----------------------------Reset()------------------------------------
//
// Resets the sweepers position, fitness and rotation
//
//------------------------------------------------------------------------
void CMinesweeper::Reset()
{
//reset the sweepers positions
m_vPosition = SVector2D(180, 200);

//and the fitness
m_dFitness = 0;


  //and the rotation
  m_dRotation = 0;


  //reset its memory
  m_MemoryMap.Reset();


}


//------------------------- RenderMemory ---------------------------------
//
//------------------------------------------------------------------------
void CMinesweeper::Render(HDC surface)
{
  //render the memory
  m_MemoryMap.Render(surface);


  string s = itos(m_MemoryMap.NumCellsVisited());
  s = "Num Cells Visited: " + s;
  TextOut(surface, 220,0,s.c_str(), s.size());


}
//---------------------WorldTransform--------------------------------
//
// sets up a translation matrix for the sweeper according to its
//  scale, rotation and position. Returns the transformed vertices.
//-------------------------------------------------------------------
void CMinesweeper::WorldTransform(vector<SPoint> &sweeper, double scale)
{
//create the world transformation matrix
C2DMatrix matTransform;

//scale
matTransform.Scale(scale, scale);

//rotate
matTransform.Rotate(m_dRotation);

//and translate
matTransform.Translate(m_vPosition.x, m_vPosition.y);

//now transform the ships vertices
matTransform.TransformSPoints(sweeper);
}


//-------------------------------Update()--------------------------------
//
// First we take sensor readings and feed these into the sweepers brain.
//
// The inputs are:
//
//  The readings from the minesweepers sensors
//
// We receive two outputs from the brain.. lTrack & rTrack.
// So given a force for each track we calculate the resultant rotation 
// and acceleration and apply to current velocity vector.
//
//-----------------------------------------------------------------------
bool CMinesweeper::Update(vector<SPoint> &objects)
{

//this will store all the inputs for the NN
vector<double> inputs;


  //grab sensor readings
  TestSensors(objects);




  //input sensors into net
  for (int sr=0; sr<m_vecdSensors.size(); ++sr)
  {
    inputs.push_back(m_vecdSensors[sr]);


    inputs.push_back(m_vecFeelers[sr]);
  }

  inputs.push_back(m_bCollided);
  
//update the brain and get feedback
vector<double> output = m_pItsBrain->Update(inputs, CNeuralNet::active);


//make sure there were no errors in calculating the 
//output
if (output.size() < CParams::iNumOutputs) 
  {
    return false;
  }
  
//assign the outputs to the sweepers left & right tracks
m_lTrack = output[0];
m_rTrack = output[1];


//calculate steering forces
double RotForce = m_lTrack - m_rTrack;


  //clamp rotation
Clamp(RotForce, -CParams::dMaxTurnRate, CParams::dMaxTurnRate);

m_dRotation += RotForce;


//update Look At 
m_vLookAt.x = -sin(m_dRotation);
m_vLookAt.y = cos(m_dRotation);


  //if the sweepers haven't collided with an obstacle
  //update their position
  if (!m_bCollided)
  {
    m_dSpeed = m_lTrack + m_rTrack;


   //update position
   m_vPosition += (m_vLookAt * m_dSpeed);


   //test range of x,y values - because of 'cheap' collision detection
   //this can go into error when using < 4 sensors
   TestRange();
  }


  //update the memory map
  m_MemoryMap.Update(m_vPosition.x, m_vPosition.y);
  
return true;
}




//----------------------- TestSensors ------------------------------------
//
//  This function checks for any intersections between the sweeper's 
//  sensors and the objects in its environment
//------------------------------------------------------------------------
void CMinesweeper::TestSensors(vector<SPoint> &objects)
{
  m_bCollided = false;  
  
  //first we transform the sensors into world coordinates
  m_tranSensors = m_Sensors;


  WorldTransform(m_tranSensors, 1);  //scale is 1


  //flush the sensors
  m_vecdSensors.clear();
  m_vecFeelers.clear();


  //now to check each sensor against the objects in the world
  for (int sr=0; sr<m_tranSensors.size(); ++sr)
  {
    bool bHit = false;


    double dist = 0;


    for (int seg=0; seg<objects.size(); seg+=2)
    {
      if (LineIntersection2D(SPoint(m_vPosition.x, m_vPosition.y),
                             m_tranSensors[sr],
                             objects[seg],
                             objects[seg+1],
                             dist))
      {
        bHit = true;


        break;        
      }
    }
      
    if (bHit)
    {
      m_vecdSensors.push_back(dist);


      //implement very simple collision detection
      if (dist < CParams::dCollisionDist)
      {
        m_bCollided = true;
      }
    }
    
    else
    {
      m_vecdSensors.push_back(-1);
    } 
    
    //check how many times the minesweeper has visited the cell
    //at the current position
    int HowOften = m_MemoryMap.TicksLingered(m_tranSensors[sr].x,
                                             m_tranSensors[sr].y);


    
    //Update the memory info according to HowOften. The maximum
    //value is 1 (because we want all the inputs into the
    //ANN to be scaled between -1 < n < 1)
    if (HowOften == 0)
    {
      m_vecFeelers.push_back(-1);


      continue;
    }
    
    if (HowOften < 10) 
    {
      m_vecFeelers.push_back(0);


      continue;
    }


    if (HowOften < 20)
    {
      m_vecFeelers.push_back(0.2);


      continue;
    }


    if (HowOften < 30)
    {
      m_vecFeelers.push_back(0.4);


      continue;
    }


    if (HowOften < 50)
    {
      m_vecFeelers.push_back(0.6);


      continue;
    }


    if (HowOften < 80) 
    {
      m_vecFeelers.push_back(0.8);


      continue;
    }


     m_vecFeelers.push_back(1);   


  }//next sensor
}


//-------------------------------- TestRange -----------------------------
//
//------------------------------------------------------------------------
void CMinesweeper::TestRange()
{
  if (m_vPosition.x < 0)
  {
    m_vPosition.x = 0;
  }


  if (m_vPosition.x > CParams::WindowWidth)
  {
    m_vPosition.x = CParams::WindowWidth;
  }


  if (m_vPosition.y < 0)
  {
    m_vPosition.y = 0;
  }


  if (m_vPosition.y > CParams::WindowHeight)
  {
    m_vPosition.y = CParams::WindowHeight;
  }
}




//------------------------- EndOfRunCalculations() -----------------------
//
//------------------------------------------------------------------------
void CMinesweeper::EndOfRunCalculations()
{
  m_dFitness += m_MemoryMap.NumCellsVisited();
}



7.cpp

#include "collision.h"


8.CParams.cpp

.#include "CParams.h"


double CParams::dPi                       = 0;
double CParams::dHalfPi                   = 0;
double CParams::dTwoPi                    = 0;
int CParams::WindowWidth                  = 400;
int CParams::WindowHeight                 = 400;
int CParams::iFramesPerSecond             = 0;
int CParams::iNumInputs                   = 0;
int CParams::iNumOutputs                  = 0;
double CParams::dBias                     = -1;
double CParams::dMaxTurnRate              = 0;
int CParams::iSweeperScale                = 0;
int CParams::iNumSensors                  = 0;
double CParams::dSensorRange              = 0;
int CParams::iNumSweepers                 = 0;
int CParams::iNumTicks                    = 0;
double CParams::dCollisionDist            = 0;
double CParams::dCellSize                 = 0;
double CParams::dSigmoidResponse          = 1;
int CParams::iNumAddLinkAttempts          = 0;
int CParams::iNumTrysToFindLoopedLink     = 5;
int CParams::iNumTrysToFindOldLink        = 5;
double CParams::dYoungFitnessBonus        = 0;
int CParams::iYoungBonusAgeThreshhold     = 0;
double CParams::dSurvivalRate             = 0;
int CParams::InfoWindowWidth              = 400;
int CParams::InfoWindowHeight             = 400;
int CParams::iNumGensAllowedNoImprovement = 0;
int CParams::iMaxPermittedNeurons         = 0;
double CParams::dChanceAddLink            = 0;
double CParams::dChanceAddNode            = 0;
double CParams::dChanceAddRecurrentLink   = 0;
double CParams::dMutationRate             = 0;
double CParams::dMaxWeightPerturbation    = 0;
double CParams::dProbabilityWeightReplaced= 0;


double CParams::dActivationMutationRate   = 0;
double CParams::dMaxActivationPerturbation= 0;


double CParams::dCompatibilityThreshold   = 0;
int CParams::iNumBestSweepers             = 4;
int CParams::iOldAgeThreshold             = 0;
double CParams::dOldAgePenalty            = 0;
double CParams::dCrossoverRate            = 0;


//this function loads in the parameters from a given file name. Returns
//false if there is a problem opening the file.
bool CParams::LoadInParameters(char* szFileName)
{
  ifstream grab(szFileName);


  //check file exists
  if (!grab)
  {
    return false;
  }


  //load in from the file
  char ParamDescription[40];


  grab >> ParamDescription;
  grab >> iFramesPerSecond;
  grab >> ParamDescription;
  grab >> dMaxTurnRate;
  grab >> ParamDescription;
  grab >> iSweeperScale;
  grab >> ParamDescription;
  grab >> iNumSensors;
  grab >> ParamDescription;
  grab >> dSensorRange;
  grab >> ParamDescription;
  grab >> iNumSweepers;
  grab >> ParamDescription;
  grab >> iNumTicks;
  grab >> ParamDescription;
  grab >> dCellSize;
  grab >> ParamDescription;
  grab >> iNumAddLinkAttempts;
  grab >> ParamDescription;
  grab >> dSurvivalRate;
  grab >> ParamDescription;
  grab >> iNumGensAllowedNoImprovement;
  grab >> ParamDescription;
  grab >> iMaxPermittedNeurons;
  grab >> ParamDescription;
  grab >> dChanceAddLink;
  grab >> ParamDescription;
  grab >> dChanceAddNode;
  grab >> ParamDescription;
  grab >> dChanceAddRecurrentLink;
  grab >> ParamDescription;
  grab >> dMutationRate;
  grab >> ParamDescription;
  grab >> dMaxWeightPerturbation;
  grab >> ParamDescription;
  grab >> dProbabilityWeightReplaced;
  grab >> ParamDescription;
  grab >> dActivationMutationRate;
  grab >> ParamDescription;
  grab >> dMaxActivationPerturbation;
  grab >> ParamDescription;
  grab >> dCompatibilityThreshold;
  grab >> ParamDescription;
  grab >>iOldAgeThreshold;
  grab >> ParamDescription;
  grab >>dOldAgePenalty;
  grab >> ParamDescription;
  grab >> dYoungFitnessBonus;
  grab >> ParamDescription;
  grab >> iYoungBonusAgeThreshhold;
  grab >> ParamDescription;
  grab >>dCrossoverRate;

  return true;
}

  
  

9.CSpecies.cpp

.#include "CSpecies.h"

//------------------------------------------------------------------------
//
//  this ctor creates an instance of a new species. A local copy of 
//  the initializing genome is kept in m_Leader and the first element
//  of m_vecMembers is a pointer to that genome.
//------------------------------------------------------------------------
CSpecies::CSpecies(CGenome  &FirstOrg,
                   int      SpeciesID):m_iSpeciesID(SpeciesID),
                                       m_dBestFitness(FirstOrg.Fitness()),
                                       m_iGensNoImprovement(0),
                                       m_iAge(0),
                                       m_Leader(FirstOrg),
                                       m_dSpawnsRqd(0)
                                     
{
  m_vecMembers.push_back(&FirstOrg);
  
  m_Leader = FirstOrg;
}


//------------------------ AddMember -------------------------------------
//
//  this function adds a new member to this species and updates the member
//  variables accordingly
//------------------------------------------------------------------------
void CSpecies::AddMember(CGenome &NewMember)
{
 
   //is the new member's fitness better than the best fitness?
  if (NewMember.Fitness() > m_dBestFitness)
  {
    m_dBestFitness = NewMember.Fitness();


    m_iGensNoImprovement = 0;


    m_Leader = NewMember;
  }



  m_vecMembers.push_back(&NewMember);


}


//-------------------------- Purge ---------------------------------------
//
//  this functions clears out all the members from the last generation,
//  updates the age and gens no improvement.
//------------------------------------------------------------------------
void CSpecies::Purge()
{
  m_vecMembers.clear();


  //update age etc
  ++m_iAge;


  ++m_iGensNoImprovement;


  m_dSpawnsRqd = 0;
}


//--------------------------- AdjustFitness ------------------------------
//
//  This function adjusts the fitness of each individual by first
//  examining the species age and penalising if old, boosting if young.
//  Then we perform fitness sharing by dividing the fitness by the number
//  of individuals in the species. This ensures a species does not grow 
//  too large
//------------------------------------------------------------------------
void CSpecies::AdjustFitnesses()
{
  double total = 0;


  for (int gen=0; gen<m_vecMembers.size(); ++gen)
  {
    double fitness = m_vecMembers[gen]->Fitness();


    //boost the fitness scores if the species is young
    if (m_iAge < CParams::iYoungBonusAgeThreshhold)
    {
      fitness *= CParams::dYoungFitnessBonus;
    }


    //punish older species
    if (m_iAge > CParams::iOldAgeThreshold)
    {
      fitness *= CParams::dOldAgePenalty;
    }


    total += fitness;


    //apply fitness sharing to adjusted fitnesses
    double AdjustedFitness = fitness/m_vecMembers.size();


    m_vecMembers[gen]->SetAdjFitness(AdjustedFitness);


  }
}


//------------------------ CalculateSpawnAmount --------------------------
//
//  Simply adds up the expected spawn amount for each individual in the 
//  species to calculate the amount of offspring this species should
//  spawn
//------------------------------------------------------------------------
void CSpecies::CalculateSpawnAmount()
{
  for (int gen=0; gen<m_vecMembers.size(); ++gen)
  {
    m_dSpawnsRqd += m_vecMembers[gen]->AmountToSpawn();


  }
}



//------------------------ Spawn -----------------------------------------
//
//  Returns a random genome selected from the best individuals
//------------------------------------------------------------------------
CGenome CSpecies::Spawn()
{
  CGenome baby;
  
  if (m_vecMembers.size() == 1)
  {
    baby = *m_vecMembers[0];    
  }
  
  else
  {
    int MaxIndexSize = (int) (CParams::dSurvivalRate * m_vecMembers.size())+1;


    int TheOne = RandInt(0, MaxIndexSize);


    baby = *m_vecMembers[TheOne];
  }


  return baby;
}


10.CTimer.cpp

#include "CTimer.h"


//---------------------- default constructor ------------------------------
//
//-------------------------------------------------------------------------


CTimer::CTimer(): m_FPS(0),
         m_TimeElapsed(0.0f),
         m_FrameTime(0),
         m_LastTime(0),
         m_PerfCountFreq(0)
{
//how many ticks per sec do we get
QueryPerformanceFrequency( (LARGE_INTEGER*) &m_PerfCountFreq);

m_TimeScale = 1.0f/m_PerfCountFreq;
}


//---------------------- constructor -------------------------------------
//
// use to specify FPS
//
//-------------------------------------------------------------------------


CTimer::CTimer(float fps): m_FPS(fps),
              m_TimeElapsed(0.0f),
              m_LastTime(0),
              m_PerfCountFreq(0)
{


//how many ticks per sec do we get
QueryPerformanceFrequency( (LARGE_INTEGER*) &m_PerfCountFreq);


m_TimeScale = 1.0f/m_PerfCountFreq;


//calculate ticks per frame
m_FrameTime = (LONGLONG)(m_PerfCountFreq / m_FPS);
}




//------------------------Start()-----------------------------------------
//
// call this immediately prior to game loop. Starts the timer (obviously!)
//
//--------------------------------------------------------------------------
void CTimer::Start()
{
//get the time
QueryPerformanceCounter( (LARGE_INTEGER*) &m_LastTime);


//update time to render next frame
m_NextTime = m_LastTime + m_FrameTime;


return;
}


//-------------------------ReadyForNextFrame()-------------------------------
//
// returns true if it is time to move on to the next frame step. To be used if
// FPS is set.
//
//----------------------------------------------------------------------------
bool CTimer::ReadyForNextFrame()
{
if (!m_FPS)
  {
    MessageBox(NULL, "No FPS set in timer", "Doh!", 0);


    return false;
  }
  
  QueryPerformanceCounter( (LARGE_INTEGER*) &m_CurrentTime);


if (m_CurrentTime > m_NextTime)
{


m_TimeElapsed = (m_CurrentTime - m_LastTime) * m_TimeScale;
m_LastTime = m_CurrentTime;


//update time to render next frame
m_NextTime = m_CurrentTime + m_FrameTime;


return true;
}


return false;
}


//--------------------------- TimeElapsed --------------------------------
//
// returns time elapsed since last call to this function. Use in main
// when calculations are to be based on dt.
//
//-------------------------------------------------------------------------
double CTimer::TimeElapsed()
{
QueryPerformanceCounter( (LARGE_INTEGER*) &m_CurrentTime);

m_TimeElapsed = (m_CurrentTime - m_LastTime) * m_TimeScale;

m_LastTime = m_CurrentTime;


return m_TimeElapsed;

}



11.genotype.cpp

.#include "genotype.h"

//------------------------------------------------------------------------
//
//  default ctor
//------------------------------------------------------------------------
CGenome::CGenome():m_pPhenotype(NULL),
                   m_GenomeID(0),
                   m_dFitness(0),
                   m_dAdjustedFitness(0),
                   m_iNumInputs(0),
                   m_iNumOutPuts(0),
                   m_dAmountToSpawn(0)
{}


//-----------------------------constructor--------------------------------
// this constructor creates a minimal genome where there are output +
// input neurons and each input neuron is connected to each output neuron.
//------------------------------------------------------------------------
CGenome::CGenome(int id, int inputs, int outputs):m_pPhenotype(NULL),
                                                  m_GenomeID(id),
                                                  m_dFitness(0),
                                                  m_dAdjustedFitness(0),
                                                  m_iNumInputs(inputs),
                                                  m_iNumOutPuts(outputs),
                                                  m_dAmountToSpawn(0),
                                                  m_iSpecies(0)
 
{
  //create the input neurons
  double InputRowSlice = 1/(double)(inputs+2);


for (int i=0; i<inputs; i++)
{
m_vecNeurons.push_back(SNeuronGene(input, i, 0, (i+2)*InputRowSlice));
}


  //create the bias
  m_vecNeurons.push_back(SNeuronGene(bias, inputs, 0, InputRowSlice));


//create the output neurons
  double OutputRowSlice = 1/(double)(outputs+1);


for (i=0; i<outputs; i++)
{
m_vecNeurons.push_back(SNeuronGene(output, i+inputs+1, 1, (i+1)*OutputRowSlice));
}


//create the link genes, connect each input neuron to each output neuron and 
//assign a random weight -1 < w < 1
for (i=0; i<inputs+1; i++)
{
for (int j=0; j<outputs; j++)
{
m_vecLinks.push_back(SLinkGene(m_vecNeurons[i].iID,
                                         m_vecNeurons[inputs+j+1].iID,
                                         true,
                                         inputs+outputs+1+NumGenes(),
                                         RandomClamped()));
}
}
 
}


//------------------------------------------------------------------------
//
//  this constructor creates a genome from a vector of SLinkGenes, a 
//  vector of SNeuronGenes and an ID number.
//------------------------------------------------------------------------
CGenome::CGenome(int                 id,
                 vector<SNeuronGene> neurons,
                 vector<SLinkGene>   genes,
                 int                 inputs,
                 int                 outputs):m_GenomeID(id),
                                                  m_pPhenotype(NULL),
                                                  m_vecLinks(genes),
                                                  m_vecNeurons(neurons),
                                                  m_dAmountToSpawn(0),
                                                  m_dFitness(0),
                                                  m_dAdjustedFitness(0),
                                                  m_iNumInputs(inputs),
                                                  m_iNumOutPuts(outputs)
                                           
{}


//-------------------------------dtor-----------------------------------------------------
//
//----------------------------------------------------------------------------------------
CGenome::~CGenome()
{  
  if (m_pPhenotype)
  {
delete m_pPhenotype;


    m_pPhenotype = NULL;
  }
}


//---------------------------------copy ctor---------------------------------------------
//
//---------------------------------------------------------------------------------------
CGenome::CGenome(const CGenome& g)
{
    m_GenomeID   = g.m_GenomeID;
    m_vecNeurons   = g.m_vecNeurons;
    m_vecLinks   = g.m_vecLinks;
    m_pPhenotype = NULL;              //no need to perform a deep copy
    m_dFitness   = g.m_dFitness;
    m_dAdjustedFitness = g.m_dAdjustedFitness;
    m_iNumInputs  = g.m_iNumInputs;
    m_iNumOutPuts = g.m_iNumOutPuts;
    m_dAmountToSpawn = g.m_dAmountToSpawn;
}


//---------------------------------assignment operator-----------------------------------
//
//----------------------------------------------------------------------------------------
CGenome& CGenome::operator =(const CGenome& g)
{
    //self assignment guard
 if (this != &g)
 {
      m_GenomeID         = g.m_GenomeID;
      m_vecNeurons         = g.m_vecNeurons;
      m_vecLinks         = g.m_vecLinks;
      m_pPhenotype       = NULL;        //no need to perform a deep copy
      m_dFitness         = g.m_dFitness;
      m_dAdjustedFitness = g.m_dAdjustedFitness;
      m_iNumInputs        = g.m_iNumInputs;
      m_iNumOutPuts       = g.m_iNumOutPuts;
      m_dAmountToSpawn   = g.m_dAmountToSpawn;
    }


    return *this;
}


//-------------------------------CreatePhenotype--------------------------
//
// Creates a neural network based upon the information in the genome.
// Returns a pointer to the newly created ANN
//------------------------------------------------------------------------
CNeuralNet* CGenome::CreatePhenotype(int depth)
{
  //first make sure there is no existing phenotype for this genome
  DeletePhenotype();

  //this will hold all the neurons required for the phenotype
  vector<SNeuron*>  vecNeurons;


  //first, create all the required neurons
  for (int i=0; i<m_vecNeurons.size(); i++)
  {
    SNeuron* pNeuron = new SNeuron(m_vecNeurons[i].NeuronType,
                                   m_vecNeurons[i].iID,
                                   m_vecNeurons[i].dSplitY,
                                   m_vecNeurons[i].dSplitX,
                                   m_vecNeurons[i].dActivationResponse);
    
    vecNeurons.push_back(pNeuron);
  }

  //now to create the links. 
  for (int cGene=0; cGene<m_vecLinks.size(); ++cGene)
  {
    //make sure the link gene is enabled before the connection is created
    if (m_vecLinks[cGene].bEnabled)
    {
      //get the pointers to the relevant neurons
      int element         = GetElementPos(m_vecLinks[cGene].FromNeuron);
      SNeuron* FromNeuron = vecNeurons[element];


      element           = GetElementPos(m_vecLinks[cGene].ToNeuron);
      SNeuron* ToNeuron = vecNeurons[element];


      //create a link between those two neurons and assign the weight stored
      //in the gene
      SLink tmpLink(m_vecLinks[cGene].dWeight,
                    FromNeuron,
                    ToNeuron,
                    m_vecLinks[cGene].bRecurrent);

      //add new links to neuron
      FromNeuron->vecLinksOut.push_back(tmpLink);
      ToNeuron->vecLinksIn.push_back(tmpLink);
    }
  }


  //now the neurons contain all the connectivity information, a neural
  //network may be created from them.
  m_pPhenotype = new CNeuralNet(vecNeurons, depth);

  return m_pPhenotype;
}


//--------------------------- DeletePhenotype ----------------------------
//
//------------------------------------------------------------------------
void CGenome::DeletePhenotype()
{
  if (m_pPhenotype)
  {
    delete m_pPhenotype;
  }


  m_pPhenotype = NULL;
}


//---------------------------- GetElementPos -----------------------------
//
// given a neuron ID this little function just finds its position in 
//  m_vecNeurons
//------------------------------------------------------------------------
int CGenome::GetElementPos(int neuron_id)
{  
  for (int i=0; i<m_vecNeurons.size(); i++)
{
if (m_vecNeurons[i].iID == neuron_id)
    {
      return i;
    }
}


  MessageBox(NULL, "Error in CGenome::GetElementPos", "Problem!", MB_OK);


return -1;
}


//------------------------------DuplicateLink-----------------------------
//
// returns true if the link is already part of the genome
//------------------------------------------------------------------------
bool CGenome::DuplicateLink(int NeuronIn, int NeuronOut)
{
for (int cGene = 0; cGene < m_vecLinks.size(); ++cGene)
{
if ((m_vecLinks[cGene].FromNeuron == NeuronIn) && 
        (m_vecLinks[cGene].ToNeuron == NeuronOut))
{
//we already have this link
return true;
}
}


return false;
}


//--------------------------------AddLink---------------------------------
//
// create a new link with the probability of CParams::dChanceAddLink
//------------------------------------------------------------------------
void CGenome::AddLink(double       MutationRate,
                      double       ChanceOfLooped,
                      CInnovation  &innovation,
                      int          NumTrysToFindLoop,
                      int          NumTrysToAddLink)
{
  //just return dependent on the mutation rate
  if (RandFloat() > MutationRate) return;


  //define holders for the two neurons to be linked. If we have find two 
  //valid neurons to link these values will become >= 0.
  int ID_neuron1 = -1;
  int ID_neuron2 = -1;


  //flag set if a recurrent link is selected (looped or normal)
  bool bRecurrent = false;


  //first test to see if an attempt shpould be made to create a 
  //link that loops back into the same neuron
  if (RandFloat() < ChanceOfLooped)
  {
    //YES: try NumTrysToFindLoop times to find a neuron that is not an
    //input or bias neuron and that does not already have a loopback
    //connection
    while(NumTrysToFindLoop--)
    {      
      //grab a random neuron
      int NeuronPos = RandInt(m_iNumInputs+1, m_vecNeurons.size()-1);


      //check to make sure the neuron does not already have a loopback 
      //link and that it is not an input or bias neuron
      if (!m_vecNeurons[NeuronPos].bRecurrent && 
         (m_vecNeurons[NeuronPos].NeuronType != bias) && 
         (m_vecNeurons[NeuronPos].NeuronType != input))
      {
        ID_neuron1 = ID_neuron2 = m_vecNeurons[NeuronPos].iID;


        m_vecNeurons[NeuronPos].bRecurrent = true;


        bRecurrent = true;


        NumTrysToFindLoop = 0;
      }
    }
  }


  else
  {
    //No: try to find two unlinked neurons. Make NumTrysToAddLink
    //attempts
    while(NumTrysToAddLink--)
    {
      //choose two neurons, the second must not be an input or a bias
      ID_neuron1 = m_vecNeurons[RandInt(0, m_vecNeurons.size()-1)].iID;
      
      ID_neuron2 =
      m_vecNeurons[RandInt(m_iNumInputs+1, m_vecNeurons.size()-1)].iID;


      if (ID_neuron2 == 2)
      {
        continue;
      }


      //make sure these two are not already linked and that they are
      //not the same neuron
      if ( !( DuplicateLink(ID_neuron1, ID_neuron2) ||
              (ID_neuron1 == ID_neuron2)))
      {
        NumTrysToAddLink = 0;
      }


      else
      {
        ID_neuron1 = -1;
        ID_neuron2 = -1;
      }
    }
  }


  //return if unsuccessful in finding a link
  if ( (ID_neuron1 < 0) || (ID_neuron2 < 0) )
  {
    return;
  }
  
  //check to see if we have already created this innovation
  int id = innovation.CheckInnovation(ID_neuron1, ID_neuron2, new_link);


  //is this link recurrent?
  if (m_vecNeurons[GetElementPos(ID_neuron1)].dSplitY > 
      m_vecNeurons[GetElementPos(ID_neuron2)].dSplitY)
  {
    bRecurrent = true;
  }


  if ( id < 0)
  {
    //we need to create a new innovation
    innovation.CreateNewInnovation(ID_neuron1, ID_neuron2, new_link);


    //then create the new gene
    int id = innovation.NextNumber() - 1;


    SLinkGene NewGene(ID_neuron1,
                          ID_neuron2,
                          true,
                          id,
                          RandomClamped(),
                          bRecurrent);

    m_vecLinks.push_back(NewGene);
  }


  else
  {
    //the innovation has already been created so all we need to
    //do is create the new gene using the existing innovation ID
    SLinkGene NewGene(ID_neuron1,
                          ID_neuron2,
                          true,
                          id,
                          RandomClamped(),
                          bRecurrent);

    m_vecLinks.push_back(NewGene);
  }


  return;
}


//---------------------------------AddNeuron------------------------------
//
// this function adds a neuron to the genotype by examining the network, 
// splitting one of the links and inserting the new neuron.
//------------------------------------------------------------------------
void CGenome::AddNeuron(double       MutationRate,
                        CInnovation &innovations,
                        int          NumTrysToFindOldLink)
{
  //just return dependent on mutation rate
  if (RandFloat() > MutationRate) return;
  
  //if a valid link is found into which to insert the new neuron
  //this value is set to true.
  bool bDone = false;


  //this will hold the index into m_vecLinks of the chosen link gene
  int  ChosenLink = 0;


  //first a link is chosen to split. If the genome is small the code makes 
  //sure one of the older links is split to ensure a chaining effect does
  //not occur. Here, if the genome contains less than 5 hidden neurons it
  //is considered to be too small to select a link at random
  const int SizeThreshold = m_iNumInputs + m_iNumOutPuts + 5;
  
  if (m_vecLinks.size() < SizeThreshold)
  {    
    while(NumTrysToFindOldLink--)
    {
      //choose a link with a bias towards the older links in the genome 
      ChosenLink = RandInt(0, NumGenes()-1-(int)sqrt(NumGenes()));


      //make sure the link is enabled and that it is not a recurrent link 
      //or has a bias input
      int FromNeuron = m_vecLinks[ChosenLink].FromNeuron;
    
      if ( (m_vecLinks[ChosenLink].bEnabled)    && 
           (!m_vecLinks[ChosenLink].bRecurrent) &&
           (m_vecNeurons[GetElementPos(FromNeuron)].NeuronType != bias)) 
       {
          bDone = true;


          NumTrysToFindOldLink = 0;
        }
    }


    if (!bDone)
    {
      //failed to find a decent link
      return;
    }
  }


  else
  {
    //the genome is of sufficient size for any link to be acceptable
    while (!bDone)
    {
      ChosenLink = RandInt(0, NumGenes()-1);


      //make sure the link is enabled and that it is not a recurrent link 
      //or has a BIAS input
      int FromNeuron = m_vecLinks[ChosenLink].FromNeuron;
    
      if ( (m_vecLinks[ChosenLink].bEnabled) && 
           (!m_vecLinks[ChosenLink].bRecurrent) &&
           (m_vecNeurons[GetElementPos(FromNeuron)].NeuronType != bias)) 
      {
        bDone = true;
      }
    }
  }
  
  //disable this gene
  m_vecLinks[ChosenLink].bEnabled = false;


  //grab the weight from the gene (we want to use this for the weight of
  //one of the new links so that the split does not disturb anything the 
  //NN may have already learned...
  double OriginalWeight = m_vecLinks[ChosenLink].dWeight;


  //identify the neurons this link connects
  int from =  m_vecLinks[ChosenLink].FromNeuron;
  int to   =  m_vecLinks[ChosenLink].ToNeuron;


  //calculate the depth and width of the new neuron. We can use the depth
  //to see if the link feeds backwards or forwards
  double NewDepth = (m_vecNeurons[GetElementPos(from)].dSplitY + 
                     m_vecNeurons[GetElementPos(to)].dSplitY) /2;


  double NewWidth = (m_vecNeurons[GetElementPos(from)].dSplitX + 
                     m_vecNeurons[GetElementPos(to)].dSplitX) /2;


  //Now to see if this innovation has been created previously by
  //another member of the population
  int id = innovations.CheckInnovation(from,
                                       to,
                                       new_neuron);


  
  
  /*it is possible for NEAT to repeatedly do the following:
  
      1. Find a link. Lets say we choose link 1 to 5
      2. Disable the link,
      3. Add a new neuron and two new links
      4. The link disabled in Step 2 maybe re-enabled when this genome
         is recombined with a genome that has that link enabled.
      5  etc etc


  Therefore, this function must check to see if a neuron ID is already 
  being used. If it is then the function creates a new innovation
  for the neuron. */
  if (id >= 0)
  {
    int NeuronID = innovations.GetNeuronID(id);


    if (AlreadyHaveThisNeuronID(NeuronID))
    {
      id = -1;
    }
  }


  if (id < 0)
  {
    //add the innovation for the new neuron
    int NewNeuronID = innovations.CreateNewInnovation(from,
                                                      to,
                                                      new_neuron,
                                                      hidden,
                                                      NewWidth,
                                                      NewDepth);
    
    //create the new neuron gene and add it.
    m_vecNeurons.push_back(SNeuronGene(hidden,
                                       NewNeuronID,
                                       NewDepth,
                                       NewWidth));

    //Two new link innovations are required, one for each of the 
    //new links created when this gene is split.


    //-----------------------------------first link


    //get the next innovation ID
    int idLink1 = innovations.NextNumber();

    //create the new innovation
    innovations.CreateNewInnovation(from,
                                    NewNeuronID,
                                    new_link);


    //create the new link gene
    SLinkGene link1(from,
                        NewNeuronID,
                        true,
                        idLink1,
                        1.0);


    m_vecLinks.push_back(link1);


    //-----------------------------------second link


    //get the next innovation ID
    int idLink2 = innovations.NextNumber();

    //create the new innovation
    innovations.CreateNewInnovation(NewNeuronID,
                                    to,
                                    new_link);

    //create the new gene
    SLinkGene link2(NewNeuronID,
                        to,
                        true,
                        idLink2,
                        OriginalWeight);
    
    m_vecLinks.push_back(link2);
  }


  else
  {
    //this innovation has already been created so grab the relevant neuron 
    //and link info from the innovation database
    int NewNeuronID = innovations.GetNeuronID(id);

    //get the innovation IDs for the two new link genes.
    int idLink1 = innovations.CheckInnovation(from, NewNeuronID, new_link);
    int idLink2 = innovations.CheckInnovation(NewNeuronID, to, new_link);


    //this should never happen because the innovations *should* have already 
    //occurred
    if ( (idLink1 < 0) || (idLink2 < 0) )
    {
      MessageBox(NULL, "Error in CGenome::AddNeuron", "Problem!", MB_OK);

      return;
    }


    //now we need to create 2 new genes to represent the new links
    SLinkGene link1(from, NewNeuronID, true, idLink1, 1.0);
    SLinkGene link2(NewNeuronID, to, true, idLink2, OriginalWeight);


    m_vecLinks.push_back(link1);
    m_vecLinks.push_back(link2);


    //create the new neuron
    SNeuronGene NewNeuron(hidden, NewNeuronID, NewDepth, NewWidth);

    //and add it
    m_vecNeurons.push_back(NewNeuron);
  }


  return;
}




//--------------------------- AlreadyHaveThisNeuronID ----------------------
// 
// tests to see if the parameter is equal to any existing neuron ID's. 
// Returns true if this is the case.
//------------------------------------------------------------------------
bool CGenome::AlreadyHaveThisNeuronID(const int ID)
{
  for (int n=0; n<m_vecNeurons.size(); ++n)
  {
    if (ID == m_vecNeurons[n].iID)
    {
      return true;
    }
  }


  return false;
}
//------------------------------- MutateWeights---------------------------
// Iterates through the genes and purturbs the weights given a 
//  probability mut_rate.
//
// prob_new_mut is the chance that a weight may get replaced by a
//  completely new weight.
//
// dMaxPertubation is the maximum perturbation to be applied.
//
// type is the type of random number algorithm we use
//------------------------------------------------------------------------
void CGenome::MutateWeights(double mut_rate,
                            double prob_new_mut,
                            double MaxPertubation)
{
for (int cGen=0; cGen<m_vecLinks.size(); ++cGen)
{
//do we mutate this gene?
if (RandFloat() < mut_rate)
{
//do we change the weight to a completely new weight?
if (RandFloat() < prob_new_mut)
{
//change the weight using the random distribtion defined by 'type'
m_vecLinks[cGen].dWeight = RandomClamped();
}


else
{
//perturb the weight
m_vecLinks[cGen].dWeight += RandomClamped() * MaxPertubation;                                            
}
}
}


return;
}


void CGenome::MutateActivationResponse(double mut_rate,
                                       double MaxPertubation)
{
  for (int cGen=0; cGen<m_vecNeurons.size(); ++cGen)
  {
    if (RandFloat() < mut_rate)
    {
      m_vecNeurons[cGen].dActivationResponse += RandomClamped() * MaxPertubation;
    }
  }
}
//------------------------- GetCompatibilityScore ------------------------
//
//  this function returns a score based on the compatibility of this
//  genome with the passed genome
//------------------------------------------------------------------------
double CGenome::GetCompatibilityScore(const CGenome &genome)
{
  //travel down the length of each genome counting the number of 
  //disjoint genes, the number of excess genes and the number of
  //matched genes
  double NumDisjoint = 0;
  double NumExcess   = 0; 
  double NumMatched  = 0;


  //this records the summed difference of weights in matched genes
  double WeightDifference = 0;


  //position holders for each genome. They are incremented as we
  //step down each genomes length.
  int g1 = 0;
  int g2 = 0;


  while ( (g1 < m_vecLinks.size()-1) || (g2 < genome.m_vecLinks.size()-1) )
  {
    //we've reached the end of genome1 but not genome2 so increment
    //the excess score
    if (g1 == m_vecLinks.size()-1)
    {
      ++g2;
      ++NumExcess;


      continue;
    }


    //and vice versa
    if (g2 == genome.m_vecLinks.size()-1)
    {
      ++g1;
      ++NumExcess;
      
      continue;
    }

    //get innovation numbers for each gene at this point
    int id1 = m_vecLinks[g1].InnovationID;
    int id2 = genome.m_vecLinks[g2].InnovationID;


    //innovation numbers are identical so increase the matched score
    if (id1 == id2)
    {
      ++g1;
      ++g2;
      ++NumMatched;


      //get the weight difference between these two genes
      WeightDifference += fabs(m_vecLinks[g1].dWeight - genome.m_vecLinks[g2].dWeight);
    }


    //innovation numbers are different so increment the disjoint score
    if (id1 < id2)
    {
      ++NumDisjoint;
      ++g1;
    }
    
    if (id1 > id2)
    {
      ++NumDisjoint;
      ++g2;
    }

  }//end while


  //get the length of the longest genome
  int longest = genome.NumGenes();
  
  if (NumGenes() > longest)
  {
    longest = NumGenes();
  }


  //these are multipliers used to tweak the final score.
  const double mDisjoint = 1;
  const double mExcess   = 1;
  const double mMatched  = 0.4;

  //finally calculate the scores 
  double score = (mExcess * NumExcess/(double)longest) + 
                 (mDisjoint * NumDisjoint/(double)longest) + 
                 (mMatched * WeightDifference / NumMatched);


    
  return score;
}


//--------------------------- SortGenes ----------------------------------
//
//  does exactly that
//------------------------------------------------------------------------
void CGenome::SortGenes()
{
  sort (m_vecLinks.begin(), m_vecLinks.end());
}



12.main.cpp

.#include <windows.h>   
#include <time.h>


#include "utils.h"
#include "CController.h"
#include "CTimer.h"
#include "resource.h"
#include "CParams.h"


///////////////////////GLOBALS ////////////////////////////////////


char* szApplicationName     = "Chapter 11 - NEAT evolution";
char* szWindowClassName     = "sweeper";
char*     szInfoWindowClassName = "Info Window";


//The controller class for this simulation
CController* g_pController= NULL; 


CParams   g_Params;


//global handle to the info window
HWND g_hwndInfo = NULL;


//global handle to the main window
HWND g_hwndMain = NULL;


//---------------------------- Cleanup ----------------------------------
//
// simply cleans up any memory issues when the application exits
//-----------------------------------------------------------------------
void Cleanup()
{
if (g_pController) 


delete g_pController;
}
//-----------------------------------WinProc-----------------------------
//
//-----------------------------------------------------------------------
LRESULT CALLBACK WindowProc(HWND hwnd, 
               UINT msg, 
                            WPARAM wparam, 
                            LPARAM lparam)
{
//these hold the dimensions of the client window area
static int cxClient, cyClient;


//used to create the back buffer
static HDC  hdcBackBuffer;
static HBITMAPhBitmap;
static HBITMAPhOldBitmap; 


switch(msg)
{
case WM_CREATE: 
{
//seed the random number generator
srand((unsigned) time(NULL));


//get the size of the client window
RECT rect;
GetClientRect(hwnd, &rect);


cxClient = rect.right;
cyClient = rect.bottom;


//setup the controller
g_pController = new CController(hwnd, cxClient, cyClient);


//create a surface for us to render to(backbuffer)
hdcBackBuffer = CreateCompatibleDC(NULL);


HDC hdc = GetDC(hwnd);


hBitmap = CreateCompatibleBitmap(hdc,
               cxClient,
               cyClient);
ReleaseDC(hwnd, hdc);


hOldBitmap = (HBITMAP)SelectObject(hdcBackBuffer, hBitmap); 


break;

//check key press messages
case WM_KEYUP:
{
switch(wparam)
{


case VK_ESCAPE:
{
PostQuitMessage(0);
}


break;


case 'F':
{
g_pController->FastRenderToggle();
}

break;


        case 'B':
{
g_pController->RenderBestToggle();
}

break;
          
        case 'R':
          {
             if (g_pController)
             {
               delete g_pController;
             }


             //setup the new controller
      g_pController = new CController(hwnd, cxClient, cyClient);


             //give the info window's handle to the controller
             g_pController->PassInfoHandle(g_hwndInfo); 


             //clear info window
             InvalidateRect(g_hwndInfo, NULL, TRUE);
      UpdateWindow(g_hwndInfo);
          }


          break;


        case '1':
          {
            g_pController->ViewBest(1);
          }

        break;


        case '2':
          {
            g_pController->ViewBest(2);
          }

        break;


        case '3':
          {
            g_pController->ViewBest(3);
          }

        break;


        case '4':
          {
            g_pController->ViewBest(4);
          }

        break;

}//end WM_KEYUP switch
}


break;


//has the user resized the client area?
case WM_SIZE:
{         
cxClient = LOWORD(lparam);
cyClient = HIWORD(lparam);



break;


case WM_PAINT: 
{
      PAINTSTRUCT ps;
      
 BeginPaint(hwnd, &ps);

//fill our backbuffer with white
BitBlt(hdcBackBuffer,
             0,
             0,
             cxClient,
             cyClient,
             NULL,
             NULL,
             NULL,
             WHITENESS);

//render the sweepers
g_pController->Render(hdcBackBuffer);

//now blit backbuffer to front
BitBlt(ps.hdc, 0, 0, cxClient, cyClient, hdcBackBuffer, 0, 0, SRCCOPY); 


EndPaint(hwnd, &ps);


break;


case WM_DESTROY: 
{
SelectObject(hdcBackBuffer, hOldBitmap);

//clean up our backbuffer objects
DeleteDC(hdcBackBuffer);
DeleteObject(hBitmap); 


      // kill the application, this sends a WM_QUIT message 
PostQuitMessage(0);
 

break;


default:break;


}//end switch


// default msg handler 
return (DefWindowProc(hwnd, msg, wparam, lparam));


}//end WinProc


//-----------------------------------InfoWinProc-----------------------------
//
//-----------------------------------------------------------------------
LRESULT CALLBACK InfoWindowProc(HWND hwnd, 
                   UINT msg, 
                                WPARAM wparam, 
                                LPARAM lparam)
{
//these hold the dimensions of the client window area
static int cxClient, cyClient;


switch(msg)
{
case WM_CREATE: 
{


//get the size of the client window
RECT rect;
GetClientRect(hwnd, &rect);


cxClient = rect.right;
cyClient = rect.bottom;


break;


//has the user resized the client area?
case WM_SIZE:
{
  cxClient = LOWORD(lparam);
cyClient = HIWORD(lparam);



break;


case WM_PAINT: 
{
      PAINTSTRUCT ps;
      
 BeginPaint(hwnd, &ps);
      
      g_pController->RenderNetworks(ps.hdc);


EndPaint(hwnd, &ps);


break;




default:break;


}//end switch


// default msg handler 
return (WindowProc(hwnd, msg, wparam, lparam));


}//end WinProc
//---------------------------------CreateInfoWindow---------------------------
//
// creates and displays the info window
//
//----------------------------------------------------------------------------
void CreateInfoWindow(HWND hwndParent)
{
// Create and register the window class
  WNDCLASSEX wcInfo = {sizeof(WNDCLASSEX), 
                       CS_HREDRAW | CS_VREDRAW,
                       InfoWindowProc,
                       0,
                       0, 
                       GetModuleHandle(NULL),
          NULL,
          NULL,
          (HBRUSH)(GetStockObject(WHITE_BRUSH)),
          NULL,
          "Info",
          NULL }; 

  RegisterClassEx( &wcInfo );

// Create the application's info window
  g_hwndInfo = CreateWindow("Info",
                            "Previous generation's best four phenotypes", 
         WS_OVERLAPPED | WS_VISIBLE | WS_CAPTION | WS_SYSMENU,
                            GetSystemMetrics(SM_CXSCREEN)/2,
                            GetSystemMetrics(SM_CYSCREEN)/2 - CParams::WindowHeight/2,
                            CParams::InfoWindowWidth,
         CParams::InfoWindowHeight,
         hwndParent,
         NULL,
         wcInfo.hInstance,
         NULL );

// Show the info
ShowWindow(g_hwndInfo, SW_SHOWDEFAULT);
UpdateWindow(g_hwndInfo);


  //give the info window's handle to the controller
  g_pController->PassInfoHandle(g_hwndInfo);

return;
}


//-----------------------------------WinMain-----------------------------------------
// Entry point for our windows application
//-----------------------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hinstance,
         HINSTANCE hprevinstance,
         LPSTR lpcmdline,
         int ncmdshow)
{


WNDCLASSEX winclass; 
HWND   hwnd; 
MSG   msg;
  
  //load in the parameters for the program
  if (!g_Params.Initialize())
  {
    return false;
  }


// first fill in the window class stucture
winclass.cbSize       = sizeof(WNDCLASSEX);
winclass.style = CS_HREDRAW | CS_VREDRAW;
winclass.lpfnWndProc= WindowProc;
winclass.cbClsExtra= 0;
winclass.cbWndExtra= 0;
winclass.hInstance= hinstance;
winclass.hIcon = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_ICON1));
winclass.hCursor = LoadCursor(NULL, IDC_ARROW); 
winclass.hbrBackground= NULL; 
winclass.lpszMenuName= NULL;
winclass.lpszClassName= szWindowClassName;
winclass.hIconSm      = LoadIcon(hinstance, MAKEINTRESOURCE(IDI_ICON1));




// register the window class
if (!RegisterClassEx(&winclass))
{
MessageBox(NULL, "Error Registering Class!", "Error", 0);
    return 0;
}


// create the window (one that cannot be resized)
if (!(hwnd = CreateWindowEx(NULL,
             szWindowClassName,
             szApplicationName,
             WS_OVERLAPPED | WS_VISIBLE | WS_CAPTION | WS_SYSMENU,
             GetSystemMetrics(SM_CXSCREEN)/2 - CParams::WindowWidth,
                              GetSystemMetrics(SM_CYSCREEN)/2 - CParams::WindowHeight/2,
             CParams::WindowWidth,
                              CParams::WindowHeight,
             NULL,
             NULL,
             hinstance,
             NULL)))
{
    MessageBox(NULL, "Error Creating Window!", "Error", 0);
return 0;
}
  
  //keep a global record of the window handle
  g_hwndMain = hwnd;


  //create and show the info window
  CreateInfoWindow(hwnd);


  //Show the window
ShowWindow(hwnd, SW_SHOWDEFAULT );
UpdateWindow(hwnd);


//create a timer
CTimer timer(CParams::iFramesPerSecond);


//start the timer
timer.Start();


// Enter the message loop
bool bDone = FALSE;


while(!bDone)
{

while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) 
{
if( msg.message == WM_QUIT ) 
{
// Stop loop if it's a quit message
bDone = TRUE;



else 
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}

if (timer.ReadyForNextFrame() || g_pController->FastRender())
{
 if(!g_pController->Update())
{
//we have a problem, end app
bDone = TRUE;
}


//this will call WM_PAINT which will render our scene
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
    }

}//end while



    // Clean up everything and exit the app
    Cleanup();
    UnregisterClass( szWindowClassName, winclass.hInstance );

return 0;


} // end WinMain



13. phenotype.cpp


..#include "phenotype.h"


//------------------------------------Sigmoid function------------------------
//
//----------------------------------------------------------------------------


float Sigmoid(float netinput, float response)
{
return ( 1 / ( 1 + exp(-netinput / response)));
}



//--------------------------------- ctor ---------------------------------
//
//------------------------------------------------------------------------
CNeuralNet::CNeuralNet(vector<SNeuron*> neurons,
                       int              depth):m_vecpNeurons(neurons),
                                               m_iDepth(depth)
{}


//--------------------------------- dtor ---------------------------------
//
//------------------------------------------------------------------------
CNeuralNet::~CNeuralNet()
{
  //delete any live neurons
  for (int i=0; i<m_vecpNeurons.size(); ++i)
  {
    if (m_vecpNeurons[i])
    {
      delete m_vecpNeurons[i];


      m_vecpNeurons[i] = NULL;
    }
  }
}


//----------------------------------Update--------------------------------
// takes a list of doubles as inputs into the network then steps through 
//  the neurons calculating each neurons next output.
//
// finally returns a std::vector of doubles as the output from the net.
//------------------------------------------------------------------------
vector<double> CNeuralNet::Update(const vector<double> &inputs,
                                  const run_type        type)
{
  //create a vector to put the outputs into
  vector<double> outputs;


  //if the mode is snapshot then we require all the neurons to be
  //iterated through as many times as the network is deep. If the 
  //mode is set to active the method can return an output after
  //just one iteration
  int FlushCount = 0;
  
  if (type == snapshot)
  {
    FlushCount = m_iDepth;
  }
  else
  {
    FlushCount = 1;
  }


  //iterate through the network FlushCount times
  for (int i=0; i<FlushCount; ++i)
  {
    //clear the output vector
    outputs.clear();
   
    //this is an index into the current neuron
    int cNeuron = 0;


    //first set the outputs of the 'input' neurons to be equal
    //to the values passed into the function in inputs
    while (m_vecpNeurons[cNeuron]->NeuronType == input)
    {
      m_vecpNeurons[cNeuron]->dOutput = inputs[cNeuron];


      ++cNeuron;
    }


    //set the output of the bias to 1
    m_vecpNeurons[cNeuron++]->dOutput = 1;


    //then we step through the network a neuron at a time
    while (cNeuron < m_vecpNeurons.size())
    {
      //this will hold the sum of all the inputs x weights 
      double sum = 0;


      //sum this neuron's inputs by iterating through all the links into
      //the neuron
      for (int lnk=0; lnk<m_vecpNeurons[cNeuron]->vecLinksIn.size(); ++lnk)
      {
        //get this link's weight
        double Weight = m_vecpNeurons[cNeuron]->vecLinksIn[lnk].dWeight;


        //get the output from the neuron this link is coming from
        double NeuronOutput =
        m_vecpNeurons[cNeuron]->vecLinksIn[lnk].pIn->dOutput;


        //add to sum
        sum += Weight * NeuronOutput;
      }


      //now put the sum through the activation function and assign the 
      //value to this neuron's output
      m_vecpNeurons[cNeuron]->dOutput = 
      Sigmoid(sum, m_vecpNeurons[cNeuron]->dActivationResponse);


      if (m_vecpNeurons[cNeuron]->NeuronType == output)
      {
        //add to our outputs
        outputs.push_back(m_vecpNeurons[cNeuron]->dOutput);
      }


      //next neuron
      ++cNeuron;
    }


  }//next iteration through the network


  //the network needs to be flushed if this type of update is performed
  //otherwise it is possible for dependencies to be built on the order
  //the training data is presented
  if (type == snapshot)
  {
    for (int n=0; n<m_vecpNeurons.size(); ++n)
    {
      m_vecpNeurons[n]->dOutput = 0;
    }
  }


  //return the outputs
  return outputs;
}


//----------------------------- TidyXSplits -----------------------------
//
//  This is a fix to prevent neurons overlapping when they are displayed
//-----------------------------------------------------------------------
void TidyXSplits(vector<SNeuron*> &neurons)
{
  //stores the index of any neurons with identical splitY values 
  vector<int>    SameLevelNeurons;


  //stores all the splitY values already checked
  vector<double> DepthsChecked;




  //for each neuron find all neurons of identical ySplit level
  for (int n=0; n<neurons.size(); ++n)
  {
    double ThisDepth = neurons[n]->dSplitY;


    //check to see if we have already adjusted the neurons at this depth
    bool bAlreadyChecked = false;


    for (int i=0; i<DepthsChecked.size(); ++i)
    {
      if (DepthsChecked[i] == ThisDepth)
      {
        bAlreadyChecked = true;


        break;
      }
    }


    //add this depth to the depths checked.
    DepthsChecked.push_back(ThisDepth);


    //if this depth has not already been adjusted
    if (!bAlreadyChecked)
    {
      //clear this storage and add the neuron's index we are checking
      SameLevelNeurons.clear();
      SameLevelNeurons.push_back(n);
      
      //find all the neurons with this splitY depth
      for (int i=n+1; i<neurons.size(); ++i)
      {
        if (neurons[i]->dSplitY == ThisDepth)
        {
          //add the index to this neuron
          SameLevelNeurons.push_back(i);
        }
      }


      //calculate the distance between each neuron
      double slice = 1.0/(SameLevelNeurons.size()+1);
  


      //separate all neurons at this level
      for (i=0; i<SameLevelNeurons.size(); ++i)
      {
        int idx = SameLevelNeurons[i];


        neurons[idx]->dSplitX = (i+1) * slice;
      }
    }


  }//next neuron to check


}
//----------------------------- DrawNet ----------------------------------
//
//  creates a representation of the ANN on a device context
//
//------------------------------------------------------------------------
void CNeuralNet::DrawNet(HDC &surface, int Left, int Right, int Top, int Bottom)
{
  //the border width
  const int border = 10;
    
  //max line thickness
  const int MaxThickness = 5;


  TidyXSplits(m_vecpNeurons);


  //go through the neurons and assign x/y coords
  int spanX = Right - Left;
  int spanY = Top - Bottom - (2*border);


  for (int cNeuron=0; cNeuron<m_vecpNeurons.size(); ++cNeuron)
  {
    m_vecpNeurons[cNeuron]->iPosX = Left + spanX*m_vecpNeurons[cNeuron]->dSplitX;
    m_vecpNeurons[cNeuron]->iPosY = (Top - border) - (spanY * m_vecpNeurons[cNeuron]->dSplitY);
  }


  //create some pens and brushes to draw with
  HPEN GreyPen  = CreatePen(PS_SOLID, 1, RGB(200, 200, 200));
  HPEN RedPen   = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
  HPEN GreenPen = CreatePen(PS_SOLID, 1, RGB(0, 200, 0));
  HPEN OldPen   = NULL;


  //create a solid brush
  HBRUSH RedBrush = CreateSolidBrush(RGB(255, 0, 0));
  HBRUSH OldBrush = NULL;


  OldPen =   (HPEN)  SelectObject(surface, RedPen);
  OldBrush = (HBRUSH)SelectObject(surface, GetStockObject(HOLLOW_BRUSH));




  //radius of neurons
  int radNeuron = spanX/60;
  int radLink = radNeuron * 1.5;
  
  //now we have an X,Y pos for every neuron we can get on with the
  //drawing. First step through each neuron in the network and draw
  //the links
  for (cNeuron=0; cNeuron<m_vecpNeurons.size(); ++cNeuron)
  {
    //grab this neurons position as the start position of each
    //connection
    int StartX = m_vecpNeurons[cNeuron]->iPosX;
    int StartY = m_vecpNeurons[cNeuron]->iPosY;


    //is this a bias neuron? If so, draw the link in green
    bool bBias = false;


    if (m_vecpNeurons[cNeuron]->NeuronType == bias)
    {
      bBias = true;
    }


    //now iterate through each outgoing link to grab the end points
    for (int cLnk=0; cLnk<m_vecpNeurons[cNeuron]->vecLinksOut.size(); ++ cLnk)
    {
      int EndX = m_vecpNeurons[cNeuron]->vecLinksOut[cLnk].pOut->iPosX;
      int EndY = m_vecpNeurons[cNeuron]->vecLinksOut[cLnk].pOut->iPosY;


      //if link is forward draw a straight line
      if( (!m_vecpNeurons[cNeuron]->vecLinksOut[cLnk].bRecurrent) && !bBias)
      {
        int thickness = (int)(fabs(m_vecpNeurons[cNeuron]->vecLinksOut[cLnk].dWeight));        
        
        Clamp(thickness, 0, MaxThickness);


        HPEN Pen;


        //create a yellow pen for inhibitory weights
        if (m_vecpNeurons[cNeuron]->vecLinksOut[cLnk].dWeight <= 0)
        {
          Pen  = CreatePen(PS_SOLID, thickness, RGB(240, 230, 170));
        }


        //grey for excitory
        else
        {
          Pen  = CreatePen(PS_SOLID, thickness, RGB(200, 200, 200));
        }
        
        HPEN tempPen = (HPEN)SelectObject(surface, Pen);
        
        //draw the link
        MoveToEx(surface, StartX, StartY, NULL);
        LineTo(surface, EndX, EndY);


        SelectObject(surface, tempPen);


        DeleteObject(Pen);
      }


      else if( (!m_vecpNeurons[cNeuron]->vecLinksOut[cLnk].bRecurrent) && bBias)
      {
        SelectObject(surface, GreenPen);
        
        //draw the link
        MoveToEx(surface, StartX, StartY, NULL);
        LineTo(surface, EndX, EndY);
      }


      //recurrent link draw in red
      else
      {
        if ((StartX == EndX) && (StartY == EndY))
        {


          int thickness = (int)(fabs(m_vecpNeurons[cNeuron]->vecLinksOut[cLnk].dWeight));


          Clamp(thickness, 0, MaxThickness);
          
          HPEN Pen;


          //blue for inhibitory
          if (m_vecpNeurons[cNeuron]->vecLinksOut[cLnk].dWeight <= 0)
          {
            Pen  = CreatePen(PS_SOLID, thickness, RGB(0,0,255));
          }


          //red for excitory
          else
          {
            Pen  = CreatePen(PS_SOLID, thickness, RGB(255, 0, 0));
          }


          HPEN tempPen = (HPEN)SelectObject(surface, Pen);
          
          //we have a recursive link to the same neuron draw an ellipse
          int x = m_vecpNeurons[cNeuron]->iPosX ; 
          int y = m_vecpNeurons[cNeuron]->iPosY - (1.5 * radNeuron);


          Ellipse(surface, x-radLink, y-radLink, x+radLink, y+radLink);
          
          SelectObject(surface, tempPen);
          
          DeleteObject(Pen);
        }


        else
        {
          int thickness = (int)(fabs(m_vecpNeurons[cNeuron]->vecLinksOut[cLnk].dWeight));


          Clamp(thickness, 0, MaxThickness);


          HPEN Pen;


          //blue for inhibitory
          if (m_vecpNeurons[cNeuron]->vecLinksOut[cLnk].dWeight <= 0)
          {
            Pen  = CreatePen(PS_SOLID, thickness, RGB(0,0,255));
          }


          //red for excitory
          else
          {
            Pen  = CreatePen(PS_SOLID, thickness, RGB(255, 0, 0));
          }
          
        
          HPEN tempPen = (HPEN)SelectObject(surface, Pen);


          //draw the link
          MoveToEx(surface, StartX, StartY, NULL);
          LineTo(surface, EndX, EndY);


          SelectObject(surface, tempPen);
          
          DeleteObject(Pen);
        }
      }


    }
  }


  //now draw the neurons and their IDs
  SelectObject(surface, RedBrush);
  SelectObject(surface, GetStockObject(BLACK_PEN));


  for (cNeuron=0; cNeuron<m_vecpNeurons.size(); ++cNeuron)
  {
    int x = m_vecpNeurons[cNeuron]->iPosX; 
    int y = m_vecpNeurons[cNeuron]->iPosY;


    //display the neuron
    Ellipse(surface, x-radNeuron, y-radNeuron, x+radNeuron, y+radNeuron); 
  }


  //cleanup
  SelectObject(surface, OldPen);
  SelectObject(surface, OldBrush);
  
  DeleteObject(RedPen);
  DeleteObject(GreyPen);
  DeleteObject(GreenPen);
  DeleteObject(OldPen);
  DeleteObject(RedBrush);
  DeleteObject(OldBrush);
}



14.utils.cpp

#include "utils.h"
#include <math.h>


[完]













0 0
原创粉丝点击