神经网络入门 ,源码2

来源:互联网 发布:阿里云服务器升级带宽 编辑:程序博客网 时间:2024/04/29 04:50


神经网络入门 源码2


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



目  录



cpp源程序

1. C2DMatrix.cpp:


#include "C2DMatrix.h"


/////////////////////////////////////////////////////////////////////
//
// Matrix methods
//
/////////////////////////////////////////////////////////////////////
//create an identity matrix
void C2DMatrix::Identity()
{
m_Matrix._11 = 1.0f; m_Matrix._12 = 0.0f; m_Matrix._13 = 0.0f;


m_Matrix._21 = 0.0f; m_Matrix._22 = 1.0f; m_Matrix._23 = 0.0f;


m_Matrix._31 = 0.0f; m_Matrix._32 = 0.0f; m_Matrix._33 = 1.0f;


}


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

mat._11 = 1.0f; mat._12 = 0.0f; mat._13 = 0.0f;

mat._21 = 0.0f; mat._22 = 1.0f; mat._23 = 0.0f;

mat._31 = x;    mat._32 = y;    mat._33 = 1.0f;

//and multiply
  S2DMatrixMultiply(mat);
}


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

mat._11 = xScale; mat._12 = 0.0f; mat._13 = 0.0f;

mat._21 = 0.0f; mat._22 = yScale; mat._23 = 0.0f;

mat._31 = 0.0f; mat._32 = 0.0f; mat._33 = 1.0f;

//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.0f;

mat._21 = -Sin; mat._22 = Cos; mat._23 = 0.0f;

mat._31 = 0.0f; mat._32 = 0.0f;mat._33 = 1.0f;

//and multiply
  S2DMatrixMultiply(mat);
}


//multiply two matrices together
void C2DMatrix::S2DMatrixMultiply(S2DMatrix &mIn)
{
S2DMatrix mat_temp;

//first row
mat_temp._11 = (m_Matrix._11*mIn._11) + (m_Matrix._12*mIn._21) + (m_Matrix._13*mIn._31);
mat_temp._12 = (m_Matrix._11*mIn._12) + (m_Matrix._12*mIn._22) + (m_Matrix._13*mIn._32);
mat_temp._13 = (m_Matrix._11*mIn._13) + (m_Matrix._12*mIn._23) + (m_Matrix._13*mIn._33);


//second
mat_temp._21 = (m_Matrix._21*mIn._11) + (m_Matrix._22*mIn._21) + (m_Matrix._23*mIn._31);
mat_temp._22 = (m_Matrix._21*mIn._12) + (m_Matrix._22*mIn._22) + (m_Matrix._23*mIn._32);
mat_temp._23 = (m_Matrix._21*mIn._13) + (m_Matrix._22*mIn._23) + (m_Matrix._23*mIn._33);


//third
mat_temp._31 = (m_Matrix._31*mIn._11) + (m_Matrix._32*mIn._21) + (m_Matrix._33*mIn._31);
mat_temp._32 = (m_Matrix._31*mIn._12) + (m_Matrix._32*mIn._22) + (m_Matrix._33*mIn._32);
mat_temp._33 = (m_Matrix._31*mIn._13) + (m_Matrix._32*mIn._23) + (m_Matrix._33*mIn._33);


m_Matrix = mat_temp;
}


//applies a 2D transformation matrix to a std::vector of SPoints
void C2DMatrix::TransformSPoints(vector<SPoint> &vPoint)
{
for (int i=0; i<vPoint.size(); ++i)
{
double tempX =(m_Matrix._11*vPoint[i].x) + (m_Matrix._21*vPoint[i].y) + (m_Matrix._31);


double tempY = (m_Matrix._12*vPoint[i].x) + (m_Matrix._22*vPoint[i].y) + (m_Matrix._32);

vPoint[i].x = tempX;


vPoint[i].y = tempY;


}
}

2. 

#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 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): m_NumSweepers(CParams::iNumSweepers), 
                    m_pGA(NULL),
                    m_bFastRender(false),
                    m_iTicks(0),
                    m_hwndMain(hwndMain),
                    m_iGenerations(0),
                                         cxClient(CParams::WindowWidth),
                                         cyClient(CParams::WindowHeight)
                                         
{
//let's create the mine sweepers
for (int i=0; i<m_NumSweepers; ++i)
{
m_vecSweepers.push_back(CMinesweeper());
}


//get the total number of weights used in the sweepers
//NN so we can initialise the GA
m_NumWeightsInNN = m_vecSweepers[0].GetNumberOfWeights();


  //calculate the neuron placement in the weight vector
  vector<int> SplitPoints = m_vecSweepers[0].CalculateSplitPoints();


//initialize the Genetic Algorithm class
m_pGA = new CGenAlg(m_NumSweepers,
                      CParams::dMutationRate,
                   CParams::dCrossoverRate,
                   m_NumWeightsInNN,
                      SplitPoints);


//Get the weights from the GA and insert into the sweepers brains
m_vecThePopulation = m_pGA->GetChromos();


for (i=0; i<m_NumSweepers; i++)
  {
m_vecSweepers[i].PutWeights(m_vecThePopulation[i].vecWeights);
  }




//create pens 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(200, 200, 200));
  m_RedPenDotted   = CreatePen(PS_DOT, 1, RGB(250, 200, 200));


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_pGA)
  {
    delete m_pGA;
  }


DeleteObject(m_BluePen);
DeleteObject(m_RedPen);
DeleteObject(m_GreenPen);
DeleteObject(m_OldPen);
  DeleteObject(m_GreyPenDotted);
  DeleteObject(m_RedPenDotted);
  DeleteObject(m_BlueBrush);
  DeleteObject(m_RedBrush);
}




//---------------------WorldTransform--------------------------------
//
// sets up the translation matrices for the mines and applies the
// world transform to each vertex in the vertex buffer passed to this
// method.
//-------------------------------------------------------------------
void CController::WorldTransform(vector<SPoint> &VBuffer,
                                 SVector2D      vPos,
                                 double         rotation,
                                 double         scale)
{
//create the world transformation matrix
C2DMatrix matTransform;

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


    //rotate
matTransform.Rotate(rotation);

//translate
matTransform.Translate(vPos.x, vPos.y);




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




//-------------------------------------Update---------------------------------------
//
// This is the main workhorse. The entire simulation is controlled from here.
//
// The comments should explain what is going on adequately.
//--------------------------------------------------------------------------------------
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
//it encounters a mine its fitness is updated appropriately,
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;
}
}
}


//**We have completed another generation.


//Time to run the GA and update the sweepers with their new NNs
else
{
   for (int swp=0; swp<m_vecSweepers.size(); ++swp)
    {
        //add up all the penalties and calculate a fitness score
        m_vecSweepers[swp].EndOfRunCalculations();


        //update the fitness score stored in the GA with this score
 m_vecThePopulation[swp].dFitness = m_vecSweepers[swp].Fitness();
    }


//update the stats to be used in our stat window
m_vecAvFitness.push_back(fabs(m_pGA->AverageRawFitness()));
m_vecBestFitness.push_back(fabs(m_pGA->BestRawFitness()));


//increment the generation counter
++m_iGenerations;


//reset cycles
m_iTicks = 0;


//insert the sweepers chromosones into the GA factory and
//run the factory to create a new population
m_vecThePopulation = m_pGA->Epoch(m_vecThePopulation);

//insert the new (hopefully)improved brains back into the sweepers
    //and reset their positions etc
    for (int i=0; i<m_NumSweepers; ++i)
{
m_vecSweepers[i].PutWeights(m_vecThePopulation[i].vecWeights);

m_vecSweepers[i].Reset();
}
}


return true;
}
//------------------------------------Render()--------------------------------------
//
//----------------------------------------------------------------------------------
void CController::Render(HDC surface)
{
//render the stats
string s = "Generation: " + itos(m_iGenerations);
TextOut(surface, 5,0, s.c_str(), s.size());




//do not render if running at accelerated speed
if (!m_bFastRender)
{
//display the penalties
    m_vecSweepers[0].RenderPenalties(surface);
    
    //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);
    }


   
    //record the current pen
    m_OldPen = (HPEN)SelectObject(surface, m_GreyPenDotted);


//render the sweepers
for (i=0; i<m_vecSweepers.size(); i++)
{
if (i < CParams::iNumElite)
      {
        SelectObject(surface, m_OldPen);        
      }


      else
      {
        SelectObject(surface, m_GreyPenDotted);
      }


       //render red if collided and elite
      if ( m_vecSweepers[i].Collided() && (i < CParams::iNumElite) )
      {
        SelectObject(surface, m_RedPen);
      }
      
      //render dotted red if collided and not elite
      if ( m_vecSweepers[i].Collided() && (i > CParams::iNumElite) )
      {
        SelectObject(surface, m_RedPenDotted);
      }
      
      //grab the sweeper vertices
vector<SPoint> sweeperVB = m_SweeperVB;


//transform the vertex buffer
m_vecSweepers[i].WorldTransform(sweeperVB, m_vecSweepers[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);
}


}


    //render the sensors
    for (i=0; i<CParams::iNumElite; ++i)
    {
      //grab each sweepers sensor data
      vector<SPoint> tranSensors    = m_vecSweepers[i].Sensors();
      vector<double>   SensorReadings = m_vecSweepers[i].SensorReadings();


      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(m_vecSweepers[i].Position().x - tranSensors[sr].x) >
              (CParams::dSensorRange+1))||
              (fabs(m_vecSweepers[i].Position().y - tranSensors[sr].y) >
              (CParams::dSensorRange+1))))
        {
        
          MoveToEx(surface,
                   (int)m_vecSweepers[i].Position().x,
                   (int)m_vecSweepers[i].Position().y,
                   NULL);


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


    SelectObject(surface, m_OldPen);


}//end if


  else
  {
    PlotStats(surface);
  }


}
//--------------------------PlotStats-------------------------------------
//
//  Given a surface to draw on this function displays stats and a crude
//  graph showing best and average fitness
//------------------------------------------------------------------------
void CController::PlotStats(HDC surface)
{
   
    string s = "Best Fitness:       " + ftos(m_pGA->BestRawFitness());
 TextOut(surface, 5, 20, s.c_str(), s.size());


     s = "Average Fitness: " + ftos(m_pGA->AverageRawFitness());
 TextOut(surface, 5, 40, s.c_str(), s.size());
    
    //render the graph
    float HSlice = (float)cxClient/(m_iGenerations+1);
    float VSlice = (float)cyClient/((m_pGA->BestRawFitness()+1)*1.5);


    //plot the graph for the best fitness
    float x = 0;
    
    m_OldPen = (HPEN)SelectObject(surface, m_RedPen);


    MoveToEx(surface, 0, cyClient, NULL);
    
    for (int i=0; i<m_vecBestFitness.size(); ++i)
    {
       LineTo(surface, x, (cyClient - VSlice*m_vecBestFitness[i]));


       x += HSlice;
    }


    //plot the graph for the average fitness
    x = 0;


    SelectObject(surface, m_BluePen);


    MoveToEx(surface, 0, cyClient, NULL);
    
    for (i=0; i<m_vecAvFitness.size(); ++i)
    {
       LineTo(surface, x, (cyClient - VSlice*m_vecAvFitness[i]));


       x += HSlice;
    }


    //replace the old pen
    SelectObject(surface, m_OldPen);
}


3.

#include "CGenAlg.h"

//-----------------------------------constructor-------------------------
//
// sets up the population with random floats
//
//-----------------------------------------------------------------------
CGenAlg::CGenAlg(int  popsize,
                 double MutRat,
                 double CrossRat,
                 int  numweights,
                 vector<int> splits) :m_iPopSize(popsize),
                                      m_dMutationRate(MutRat),
                 m_dCrossoverRate(CrossRat),
                 m_iChromoLength(numweights),
                 m_dTotalFitness(0),
                 m_cGeneration(0),
                 m_iFittestGenome(0),
                 m_dBestFitness(0),
                 m_dWorstFitness(99999999),
                 m_dAverageFitness(0),
                                      m_vecSplitPoints(splits)
{
//initialise population with chromosomes consisting of random
//weights and all fitnesses set to zero
for (int i=0; i<m_iPopSize; ++i)
{
m_vecPop.push_back(SGenome());


for (int j=0; j<m_iChromoLength; ++j)
{
m_vecPop[i].vecWeights.push_back(RandomClamped());
}
}
}


//---------------------------------Mutate--------------------------------
//
// mutates a chromosome by perturbing its weights by an amount not 
// greater than MAX_PERTURBATION
//-----------------------------------------------------------------------
void CGenAlg::Mutate(vector<double> &chromo)
{
//traverse the chromosome and mutate each weight dependent
//on the mutation rate
for (int i=0; i<chromo.size(); ++i)
{
//do we perturb this weight?
if (RandFloat() < m_dMutationRate)
{
//add a small value to the weight
chromo[i] += (RandomClamped() * CParams::dMaxPerturbation);
}


}
}




//---------------------------- CrossoverAtSplits -------------------------
//


//-------------------------------------------------------------------------
void CGenAlg::CrossoverAtSplits(const vector<double> &mum,
                                const vector<double> &dad,
                                vector<double>       &baby1,
                                vector<double>       &baby2)
{
  //just return parents as offspring dependent on the rate
//or if parents are the same
if ( (RandFloat() > m_dCrossoverRate) || (mum == dad)) 
{
baby1 = mum;
baby2 = dad;


return;
}


//determine two crossover points
  int Index1 = RandInt(0, m_vecSplitPoints.size()-2);
  int Index2 = RandInt(Index1, m_vecSplitPoints.size()-1);


int cp1 = m_vecSplitPoints[Index1];
  int cp2 = m_vecSplitPoints[Index2];


//create the offspring
for (int i=0; i<mum.size(); ++i)
{
    if ( (i<cp1) || (i>=cp2) )
    {
 //keep the same genes if outside of crossover points
      baby1.push_back(mum[i]);
 baby2.push_back(dad[i]);
    }


    else
    {
      //switch over the belly block
      baby1.push_back(dad[i]);
 baby2.push_back(mum[i]);
    }
      
}

return;
}


//---------------------------- TournamentSelection -----------------
//
//  performs standard tournament selection given a number of genomes to
//  sample from each try.
//------------------------------------------------------------------------
SGenome CGenAlg::TournamentSelection(int N)
{
  double BestFitnessSoFar = -999999;
  
  int ChosenOne = 0;


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


    if (m_vecPop[ThisTry].dFitness > BestFitnessSoFar)
    {
      ChosenOne = ThisTry;


      BestFitnessSoFar = m_vecPop[ThisTry].dFitness;
    }
  }


  //return the champion
  return m_vecPop[ChosenOne];
}
//-----------------------------------Epoch()-----------------------------
//
// takes a population of chromosones and runs the algorithm through one
// cycle.
// Returns a new population of chromosones.
//
//-----------------------------------------------------------------------
vector<SGenome> CGenAlg::Epoch(vector<SGenome> &old_pop)
{
//assign the given population to the classes population
  m_vecPop = old_pop;


  //reset the appropriate variables
  Reset();


  //create a temporary vector to store new chromosones
vector <SGenome> vecNewPop;


  CalculateBestWorstAvTot();
  
  //sort the population (for scaling and elitism)
  sort(m_vecPop.begin(), m_vecPop.end());




//Now to add a little elitism we shall add in some copies of the
//fittest genomes. Make sure we add an EVEN number or the roulette
  //wheel sampling will crash
if (!(CParams::iNumCopiesElite * CParams::iNumElite % 2))
{
GrabNBest(CParams::iNumElite, CParams::iNumCopiesElite, vecNewPop);
}


 //--------------now to enter the GA loop


//repeat until a new population is generated
while (vecNewPop.size() < m_iPopSize)
{


    //select using tournament selection for a change
    SGenome mum = TournamentSelection(4);
SGenome dad = TournamentSelection(4);


//create some offspring via crossover
vector<double>baby1, baby2;


CrossoverAtSplits(mum.vecWeights, dad.vecWeights, baby1, baby2);


//now we mutate
Mutate(baby1);
Mutate(baby2);


//now copy into vecNewPop population
vecNewPop.push_back( SGenome(baby1, 0) );
vecNewPop.push_back( SGenome(baby2, 0) );
}


//finished so assign new pop back into m_vecPop
m_vecPop = vecNewPop;


return m_vecPop;
}


 
//-------------------------GrabNBest----------------------------------
//
// This works like an advanced form of elitism by inserting NumCopies
//  copies of the NBest most fittest genomes into a population vector
//--------------------------------------------------------------------
void CGenAlg::GrabNBest(int            NBest,
                        const int      NumCopies,
                        vector<SGenome> &Pop)
{
  //add the required amount of copies of the n most fittest 
//to the supplied vector
while(NBest--)
{
for (int i=0; i<NumCopies; ++i)
{
Pop.push_back(m_vecPop[(m_iPopSize - 1) - NBest]);
 }
}
}


//-----------------------CalculateBestWorstAvTot-----------------------
//
// calculates the fittest and weakest genome and the average/total 
// fitness scores
//---------------------------------------------------------------------
void CGenAlg::CalculateBestWorstAvTot()
{
m_dTotalFitness = 0;

double HighestSoFar = 0;
double LowestSoFar  = 9999999;

for (int i=0; i<m_iPopSize; ++i)
{
//update fittest if necessary
if (m_vecPop[i].dFitness > HighestSoFar)
{
HighestSoFar = m_vecPop[i].dFitness;

m_iFittestGenome = i;


m_dBestFitness= HighestSoFar;
}

//update worst if necessary
if (m_vecPop[i].dFitness < LowestSoFar)
{
LowestSoFar = m_vecPop[i].dFitness;

m_dWorstFitness = LowestSoFar;
}

m_dTotalFitness+= m_vecPop[i].dFitness;


}//next chromo

m_dAverageFitness = m_dTotalFitness / m_iPopSize;
}


//-------------------------Reset()------------------------------
//
// resets all the relevant variables ready for a new generation
//--------------------------------------------------------------
void CGenAlg::Reset()
{
m_dTotalFitness= 0;
m_dBestFitness= 0;
m_dWorstFitness= 9999999;
m_dAverageFitness= 0;
}

 

4CMinesweeper.cpp

#include "CMinesweeper.h"


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


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




//-------------------------------- 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, energy level and rotation
//
//------------------------------------------------------------------------
void CMinesweeper::Reset()
{
//reset the sweepers positions
m_vPosition = SVector2D(180, 200);

//and the energy level
m_dFitness = 0;


  //and the rotation
  m_dRotation = RandFloat() * CParams::dTwoPi;


  //and the bonuses
  m_dSpinBonus        = 0;
  m_dCollisionBonus   = 0;


}


//------------------------- RenderMemory ---------------------------------
//
//------------------------------------------------------------------------
void CMinesweeper::RenderPenalties(HDC surface)
{
  string s = ftos(m_dSpinBonus);
  s = "Spin: " + s;
  TextOut(surface, 200,0,s.c_str(), s.size());


  s = ftos(m_dCollisionBonus);
  s = "Collision: " + s;
  TextOut(surface, 300, 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]);
  }

  
//update the brain and get feedback
vector<double> output = m_ItsBrain.Update(inputs);


//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);
  }


    


  //-----------------------adjust the fitness bonuses


  //reward for not turning too quickly
  const float RotationTolerance = 0.03f;


  if (fabs(RotForce) < RotationTolerance)
  {
    m_dSpinBonus += 1;
  }


  //reward for not colliding with an object
  if (!m_bCollided)
  {
    m_dCollisionBonus += 1;
  }


  
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);


  //flush the sensors
  m_vecdSensors.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);
    }    
  }//next sensor
}


//------------------------- EndOfRunCalculations() -----------------------
//
//------------------------------------------------------------------------
void CMinesweeper::EndOfRunCalculations()
{
  m_dFitness += m_dSpinBonus;
  m_dFitness += m_dCollisionBonus;
}




5CNeuralNet.cpp

#include "CNeuralNet.h"


//*************************** methods for Neuron **********************
//
//---------------------------------------------------------------------
SNeuron::SNeuron(int NumInputs): m_NumInputs(NumInputs+1)

{
//we need an additional weight for the bias hence the +1
for (int i=0; i<NumInputs+1; ++i)
{
//set up the weights with an initial random value
m_vecWeight.push_back(RandomClamped());
}
}


//************************ methods for NeuronLayer **********************


//-----------------------------------------------------------------------
//ctor creates a layer of neurons of the required size by calling the 
//SNeuron ctor the rqd number of times
//-----------------------------------------------------------------------
SNeuronLayer::SNeuronLayer(int NumNeurons, 
                           int NumInputsPerNeuron):m_NumNeurons(NumNeurons)
{
for (int i=0; i<NumNeurons; ++i)


m_vecNeurons.push_back(SNeuron(NumInputsPerNeuron));
}


//************************ methods forCNeuralNet ************************


//------------------------------default ctor ----------------------------
//
//creates a ANN based on the default values in defines.h
//-----------------------------------------------------------------------
CNeuralNet::CNeuralNet() 
{
m_NumInputs         =CParams::iNumInputs;
m_NumOutputs     =CParams::iNumOutputs;
m_NumHiddenLayers   =CParams::iNumHidden;
m_NeuronsPerHiddenLyr =CParams::iNeuronsPerHiddenLayer;


CreateNet();


}


//------------------------------createNet()------------------------------
//
//this method builds the ANN. The weights are all initially set to 
//random values -1 < w < 1
//------------------------------------------------------------------------
void CNeuralNet::CreateNet()
{
//create the layers of the network
if (m_NumHiddenLayers > 0)
{
//create first hidden layer
 m_vecLayers.push_back(SNeuronLayer(m_NeuronsPerHiddenLyr, m_NumInputs));
    
    for (int i=0; i<m_NumHiddenLayers-1; ++i)
    {


m_vecLayers.push_back(SNeuronLayer(m_NeuronsPerHiddenLyr,
                                         m_NeuronsPerHiddenLyr));
    }


    //create output layer
 m_vecLayers.push_back(SNeuronLayer(m_NumOutputs, m_NeuronsPerHiddenLyr));
}


  else
  {
 //create output layer
 m_vecLayers.push_back(SNeuronLayer(m_NumOutputs, m_NumInputs));
  }
}


//---------------------------------GetWeights-----------------------------
//
//returns a vector containing the weights
//
//------------------------------------------------------------------------
vector<double> CNeuralNet::GetWeights() const
{
//this will hold the weights
vector<double> weights;

//for each layer
for (int i=0; i<m_NumHiddenLayers + 1; ++i)
{


//for each neuron
for (int j=0; j<m_vecLayers[i].m_NumNeurons; ++j)
{
//for each weight
for (int k=0; k<m_vecLayers[i].m_vecNeurons[j].m_NumInputs; ++k)
{
weights.push_back(m_vecLayers[i].m_vecNeurons[j].m_vecWeight[k]);
}
}
}


return weights;
}


//-----------------------------------PutWeights---------------------------
//
//given a vector of doubles this function replaces the weights in the NN
//  with the new values
//
//------------------------------------------------------------------------
void CNeuralNet::PutWeights(vector<double> &weights)
{
int cWeight = 0;

//for each layer
for (int i=0; i<m_NumHiddenLayers + 1; ++i)
{


//for each neuron
for (int j=0; j<m_vecLayers[i].m_NumNeurons; ++j)
{
//for each weight
for (int k=0; k<m_vecLayers[i].m_vecNeurons[j].m_NumInputs; ++k)
{
m_vecLayers[i].m_vecNeurons[j].m_vecWeight[k] = weights[cWeight++];
}
}
}


return;
}


//---------------------------------GetNumberOfWeights---------------------
//
//returns the total number of weights needed for the net
//
//------------------------------------------------------------------------
int CNeuralNet::GetNumberOfWeights() const
{
int weights = 0;

//for each layer
for (int i=0; i<m_NumHiddenLayers + 1; ++i)
{


//for each neuron
for (int j=0; j<m_vecLayers[i].m_NumNeurons; ++j)
{
//for each weight
for (int k=0; k<m_vecLayers[i].m_vecNeurons[j].m_NumInputs; ++k)

weights++;
}
}


return weights;
}


//--------------------------- CalculateSplitPoints -----------------------
//
//  this method calculates all points in the vector of weights which 
//  represent the start and end points of individual neurons
//------------------------------------------------------------------------
vector<int> CNeuralNet::CalculateSplitPoints() const
{
vector<int> SplitPoints;


  int WeightCounter = 0;

//for each layer
for (int i=0; i<m_NumHiddenLayers + 1; ++i)
{
//for each neuron
for (int j=0; j<m_vecLayers[i].m_NumNeurons; ++j)
{
//for each weight
for (int k=0; k<m_vecLayers[i].m_vecNeurons[j].m_NumInputs; ++k)
      {
++WeightCounter;
      }


      SplitPoints.push_back(WeightCounter - 1);
}
}


return SplitPoints;
}


//-------------------------------Update-----------------------------------
//
//given an input vector this function calculates the output vector
//
//------------------------------------------------------------------------
vector<double> CNeuralNet::Update(vector<double> &inputs)
{
//stores the resultant outputs from each layer
vector<double> outputs;


int cWeight = 0;

//first check that we have the correct amount of inputs
if (inputs.size() != m_NumInputs)
  {
//just return an empty vector if incorrect.
return outputs;
  }

//For each layer....
for (int i=0; i<m_NumHiddenLayers + 1; ++i)
{
if ( i > 0 )
    {
inputs = outputs;
    }


outputs.clear();

cWeight = 0;


//for each neuron sum the (inputs * corresponding weights).Throw 
//the total at our sigmoid function to get the output.
for (int j=0; j<m_vecLayers[i].m_NumNeurons; ++j)
{


double netinput = 0.0f;


intNumInputs = m_vecLayers[i].m_vecNeurons[j].m_NumInputs;

//for each weight
for (int k=0; k<NumInputs - 1; ++k)
{
//sum the weights x inputs
netinput += m_vecLayers[i].m_vecNeurons[j].m_vecWeight[k] * 
                    inputs[cWeight++];
}


//add in the bias
netinput += m_vecLayers[i].m_vecNeurons[j].m_vecWeight[NumInputs-1] * 
                  CParams::dBias;


//we can store the outputs from each layer as we generate them. 
      //The combined activation is first filtered through the sigmoid 
      //function
outputs.push_back(Sigmoid(netinput, CParams::dActivationResponse));


cWeight = 0;
}
}


return outputs;
}


//-------------------------------Sigmoid function-------------------------
//
//------------------------------------------------------------------------
double CNeuralNet::Sigmoid(double netinput, double response)
{
return ( 1 / ( 1 + exp(-netinput / response)));
}




6collision.cpp

#include "collision.h"

//--------------------2LinesIntersection2D-------------------------
//
// Given 2 lines in 2D space AB, CD this returns true if an 
// intersection occurs and sets dist to the distance the intersection
//  occurs along AB
//
//----------------------------------------------------------------- 
bool LineIntersection2D(const SPoint A,
                        const SPoint B,
                        const SPoint C, 
                        const SPoint D,
                        double &dist)
{


  double rTop = (A.y-C.y)*(D.x-C.x)-(A.x-C.x)*(D.y-C.y);
double rBot = (B.x-A.x)*(D.y-C.y)-(B.y-A.y)*(D.x-C.x);


double sTop = (A.y-C.y)*(B.x-A.x)-(A.x-C.x)*(B.y-A.y);
double sBot = (B.x-A.x)*(D.y-C.y)-(B.y-A.y)*(D.x-C.x);


if ( (rBot == 0) || (sBot == 0))
{
//lines are parallel
return false;
}


double r = rTop/rBot;
double s = sTop/sBot;


if( (r > 0) && (r < 1.0f) && (s > 0) && (s < 1.0f) )
  {
  dist = r;


    return true;
  }


else
  {
dist = 0;


    return false;
  }
}


7CParams.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::iNumHidden             = 0;
int CParams::iNeuronsPerHiddenLayer = 0;
int CParams::iNumOutputs            = 0;
double CParams::dActivationResponse = 0;
double CParams::dBias               = -1;
double CParams::dMaxTurnRate        = 0;
double CParams::dMaxSpeed           = 0;
int CParams::iSweeperScale          = 0;
int CParams::iNumSensors            = 0;
double CParams::dSensorRange        = 0;
int CParams::iNumSweepers           = 0;
int CParams::iNumTicks              = 0;
double CParams::dMineScale          = 0;
double CParams::dCrossoverRate      = 0;
double CParams::dMutationRate       = 0;
double CParams::dMaxPerturbation    = 0;
int CParams::iNumElite              = 0;
int CParams::iNumCopiesElite        = 0;
double CParams::dCollisionDist      = 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 >> iNumInputs;
  grab >> ParamDescription;
  grab >> iNumHidden;
  grab >> ParamDescription;
  grab >> iNeuronsPerHiddenLayer;
  grab >> ParamDescription;
  grab >> iNumOutputs;
  grab >> ParamDescription;
  grab >> dActivationResponse;
  grab >> ParamDescription;
  grab >> dMaxTurnRate;
  grab >> ParamDescription;
  grab >> dMaxSpeed;
  grab >> ParamDescription;
  grab >> iSweeperScale;
  grab >> ParamDescription;
  grab >> iNumSensors;
  grab >> ParamDescription;
  grab >> dSensorRange;
  grab >> ParamDescription;
  grab >> iNumSweepers;
  grab >> ParamDescription;
  grab >> iNumTicks;
  grab >> ParamDescription;
  grab >> dMineScale;
  grab >> ParamDescription;
  grab >> dCrossoverRate;
  grab >> ParamDescription;
  grab >> dMutationRate;
  grab >> ParamDescription;
  grab >> dMaxPerturbation;
  grab >> ParamDescription;
  grab >> iNumElite;
  grab >> ParamDescription;
  grab >> iNumCopiesElite;
 
  return true;
}
 

8CTimer.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;


9. main.cpp

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

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


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


char*szApplicationName = "Chapter8 - Obstacle Avoidance v2.1";
char*szWindowClassName = "sweeper";


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


CParams   g_Params;


//---------------------------- 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);


//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 'R':
          {
             if (g_pController)
             {
               delete g_pController;
             }


             //setup the new controller
      g_pController = new CController(hwnd);
          }


          break;




}//end WM_KEYUP switch
}


break;


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


//resize the backbuffer accordingly
SelectObject(hdcBackBuffer, hOldBitmap);


HDC hdc = GetDC(hwnd);


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

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



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 mines and 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




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


WNDCLASSEX winclass; 
HWND  hwnd; 
MSG  msg; 


// 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/2,
                              GetSystemMetrics(SM_CYSCREEN)/2 - CParams::WindowHeight/2,
             CParams::WindowWidth,
                              CParams::WindowHeight,
             NULL,
             NULL,
             hinstance,
             NULL)))
{
    MessageBox(NULL, "Error Creating Window!", "Error", 0);
return 0;
}

// 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



10. utils.cpp

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

//--------------------------itos------------------------------------
// converts an integer to a string
//------------------------------------------------------------------
string itos(int arg)
{
    ostringstream buffer;

//send the int to the ostringstream
    buffer << arg;

//capture the string
    return buffer.str();
}


//--------------------------ftos------------------------------------
// converts a float to a string
//------------------------------------------------------------------
string ftos(float arg)
{
    ostringstream buffer;

//send the int to the ostringstream
    buffer << arg;

//capture the string
    return buffer.str();
}
//-------------------------------------Clamp()-----------------------------------------
//
// clamps the first argument between the second two
//
//-------------------------------------------------------------------------------------
void Clamp( double &arg, float min, float max)
{
if (arg < min)
{
arg = min;
}


if (arg > max)
{
arg = max;
}
}






0 0