您的位置:首页 > 理论基础 > 计算机网络

神经网络入门 ,源码1

2017-03-17 17:53 295 查看

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



目 录





       以下给出所有程序的清单,但将main.cpp及utils.cpp放在最前面了,其余按次序给出。



1.main.cpp


#define WIN32_LEAN_AND_MEAN

#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 = "Chapter7 - Smart Sweepers v1.0";
char*szWindowClassName = "sweeper";

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

//create an instance of the parameter class.
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;

        //reset the demo
        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



2. 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, double min, double max)

{
if (arg < min)
{
arg = min;
}

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

}


3.[b]C2DMatrix.cpp[/b]
#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;

}
}

4.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)};

//---------------------------------------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_NumMines(CParams::iNumMines),
                    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();

//initialize the Genetic Algorithm class
m_pGA = new CGenAlg(m_NumSweepers,

                      CParams::dMutationRate,
                   CParams::dCrossoverRate,
                   m_NumWeightsInNN);

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

//initialize mines in random positions within the application window
for (i=0; i<m_NumMines; ++i)
{
m_vecMines.push_back(SVector2D(RandFloat() * cxClient,

                                   RandFloat() * cyClient));
}

//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, 150, 0));

m_OldPen
= NULL;

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

for (i=0; i<NumMineVerts; ++i)
{
m_MineVB.push_back(mine[i]);
}

}

//--------------------------------------destructor-------------------------------------

//

//--------------------------------------------------------------------------------------

CController::~CController()

{
if(m_pGA)

  {

    delete m_pGA;

  }

DeleteObject(m_BluePen);
DeleteObject(m_RedPen);
DeleteObject(m_GreenPen);
DeleteObject(m_OldPen);

}

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

{
//create the world transformation matrix
C2DMatrix matTransform;

//scale
matTransform.Scale(CParams::dMineScale, CParams::dMineScale);

//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 CParams::iNumTicks 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_vecMines))
{
//error in processing the neural net
MessageBox(m_hwndMain, "Wrong amount of NN inputs!", "Error", MB_OK);

return false;
}

//see if it's found a mine

      int GrabHit = m_vecSweepers[i].CheckForMine(m_vecMines,

                                                  CParams::dMineScale);

if (GrabHit >= 0)

      {

        //we have discovered a mine so increase fitness

        m_vecSweepers[i].IncrementFitness();

        //mine found so replace the mine with another at a random 
 //position
 m_vecMines[GrabHit] = SVector2D(RandFloat() * cxClient,

                                        RandFloat() * cyClient);

      }

//update the chromos fitness score
m_vecThePopulation[i].dFitness = m_vecSweepers[i].Fitness();

}
}

//Another generation has been completed.

  
//Time to run the GA and update the sweepers with their new NNs
else
{
//update the stats to be used in our stat window
m_vecAvFitness.push_back(m_pGA->AverageFitness());
m_vecBestFitness.push_back(m_pGA->BestFitness());

//increment the generation counter
++m_iGenerations;

//reset cycles
m_iTicks = 0;

//run the GA 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)
{
//keep a record of the old pen

     m_OldPen = (HPEN)SelectObject(surface, m_GreenPen);

    //render the mines
for (int i=0; i<m_NumMines; ++i)
{
//grab the vertices for the mine shape
vector<SPoint> mineVB = m_MineVB;

WorldTransform(mineVB, m_vecMines[i]);

//draw the mines
MoveToEx(surface, (int)mineVB[0].x, (int)mineVB[0].y, NULL);

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

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

}

       

    //we want the fittest displayed in red

    SelectObject(surface, m_RedPen);

//render the sweepers
for (i=0; i<m_NumSweepers; i++)
{
if (i == CParams::iNumElite)

      {

        SelectObject(surface, m_OldPen);

      }

      

      //grab the sweeper vertices
vector<SPoint> sweeperVB = m_SweeperVB;

//transform the vertex buffer
m_vecSweepers[i].WorldTransform(sweeperVB);

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

}

    //put the old pen back

    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->BestFitness());
 TextOut(surface, 5, 20, s.c_str(), s.size());

     s = "Average Fitness: " + ftos(m_pGA->AverageFitness());
 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->BestFitness()+1)*2);

    //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, (int)x, (int)(cyClient - VSlice*m_vecAvFitness[i]));

       x += HSlice;

    }

    //replace the old pen

    SelectObject(surface, m_OldPen);

}


5.CGenAlg.cpp
#include "CGenAlg.h"

//-----------------------------------constructor-------------------------

//

// sets up the population with random floats

//

//-----------------------------------------------------------------------

CGenAlg::CGenAlg(int  popsize,

                 double MutRat,

                 double CrossRat,

                 int  numweights) :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)

{
//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 CParams::dMaxPerturbation

//-----------------------------------------------------------------------

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 or subtract a small value to the weight
chromo[i] += (RandomClamped() * CParams::dMaxPerturbation);
}
}

}

//----------------------------------GetChromoRoulette()------------------

//

// returns a chromo based on roulette wheel sampling

//

//-----------------------------------------------------------------------

SGenome CGenAlg::GetChromoRoulette()

{
//generate a random number between 0 & total fitness count
double Slice = (double)(RandFloat() * m_dTotalFitness);

//this will be set to the chosen chromosome
SGenome TheChosenOne;

//go through the chromosones adding up the fitness so far
double FitnessSoFar = 0;

for (int i=0; i<m_iPopSize; ++i)
{
FitnessSoFar += m_vecPop[i].dFitness;

//if the fitness so far > random number return the chromo at 
//this point
if (FitnessSoFar >= Slice)
{
TheChosenOne = m_vecPop[i];

      break;
}

}

return TheChosenOne;

}

//-------------------------------------Crossover()-----------------------

//

//  given parents and storage for the offspring this method performs

// crossover according to the GAs crossover rate

//-----------------------------------------------------------------------

void CGenAlg::Crossover(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 a crossover point
int cp = RandInt(0, m_iChromoLength - 1);

//create the offspring
for (int i=0; i<cp; ++i)
{
baby1.push_back(mum[i]);
baby2.push_back(dad[i]);
}

for (i=cp; i<mum.size(); ++i)
{
baby1.push_back(dad[i]);
baby2.push_back(mum[i]);
}

return;

}

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

  //sort the population (for scaling and elitism)

  sort(m_vecPop.begin(), m_vecPop.end());

  //calculate best, worst, average and total fitness
CalculateBestWorstAvTot();

  

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

//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 we enter the GA loop

//repeat until a new population is generated
while (vecNewPop.size() < m_iPopSize)
{
//grab two chromosones
SGenome mum = GetChromoRoulette();
SGenome dad = GetChromoRoulette();

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

Crossover(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;

}

//-----------------------------FitnessScaleRank----------------------

//

// This type of fitness scaling sorts the population into ascending

// order of fitness and then simply assigns a fitness score based 

// on its position in the ladder. (so if a genome ends up last it

// gets score of zero, if best then it gets a score equal to the size

// of the population. You can also assign a multiplier which will

// increase the 'seperation' of genomes on the ladder and allow the 

// population to converge much quicker

//---------------------------------------------------------------------

void CGenAlg::FitnessScaleRank()

{
const int FitnessMultiplier = 1;

//assign fitness according to the genome's position on
//this new fitness 'ladder'
for (int i=0; i<m_iPopSize; i++)
{
m_vecPop[i].dFitness = i * FitnessMultiplier;
}

//recalculate values used in selection
CalculateBestWorstAvTot();



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

}


6. CMinesweeper.cpp
AC#include "CMinesweeper.h"

//-----------------------------------constructor-------------------------

//

//-----------------------------------------------------------------------

CMinesweeper::CMinesweeper():

                             m_dRotation(RandFloat()*CParams::dTwoPi),

                             m_lTrack(0.16),

                             m_rTrack(0.16),

                             m_dFitness(0),
              m_dScale(CParams::iSweeperScale),

                             m_iClosestMine(0)
 

{
//create a random start position
m_vPosition = SVector2D((RandFloat() * CParams::WindowWidth), 
               (RandFloat() * CParams::WindowHeight));

  

}

//-------------------------------------------Reset()--------------------

//

// Resets the sweepers position, fitness and rotation

//

//----------------------------------------------------------------------

void CMinesweeper::Reset()

{
//reset the sweepers positions
m_vPosition = SVector2D((RandFloat() * CParams::WindowWidth), 
               (RandFloat() * CParams::WindowHeight));

//and the fitness
m_dFitness = 0;

  //and the rotation

  m_dRotation = RandFloat()*CParams::dTwoPi;

return;

}

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

{
//create the world transformation matrix
C2DMatrix matTransform;

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

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

//

// A vector to the closest mine (x, y)

// The sweepers 'look at' vector (x, y)

//

// 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<SVector2D> &mines)

{

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

//get vector to closest mine
SVector2D vClosestMine = GetClosestMine(mines);

  
//normalise it

  Vec2DNormalize(vClosestMine);

  

  //add in vector to closest mine
inputs.push_back(vClosestMine.x);
inputs.push_back(vClosestMine.y);

//add in sweepers look at vector
inputs.push_back(m_vLookAt.x);
inputs.push_back(m_vLookAt.y);

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

m_dSpeed = (m_lTrack + m_rTrack);

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

//update position

  m_vPosition += (m_vLookAt * m_dSpeed);

//wrap around window limits
if (m_vPosition.x > CParams::WindowWidth) m_vPosition.x = 0;
if (m_vPosition.x < 0) m_vPosition.x = CParams::WindowWidth;
if (m_vPosition.y > CParams::WindowHeight) m_vPosition.y = 0;
if (m_vPosition.y < 0) m_vPosition.y = CParams::WindowHeight;

return true;

}

//----------------------GetClosestObject()---------------------------------

//

// returns the vector from the sweeper to the closest mine

//

//-----------------------------------------------------------------------

SVector2D CMinesweeper::GetClosestMine(vector<SVector2D> &mines)

{
double
closest_so_far = 99999;

SVector2D
vClosestObject(0, 0);

//cycle through mines to find closest
for (int i=0; i<mines.size(); i++)
{
double len_to_object = Vec2DLength(mines[i] - m_vPosition);

if (len_to_object < closest_so_far)
{
closest_so_far= len_to_object;

vClosestObject= m_vPosition - mines[i];

      m_iClosestMine = i;
}
}

return vClosestObject;

}

//----------------------------- CheckForMine -----------------------------

//

//  this function checks for collision with its closest mine (calculated

//  earlier and stored in m_iClosestMine)

//-----------------------------------------------------------------------

int CMinesweeper::CheckForMine(vector<SVector2D> &mines, double size)

{

  SVector2D DistToObject = m_vPosition - mines[m_iClosestMine];

if (Vec2DLength(DistToObject) < (size + 5))
{
return m_iClosestMine;

  }

  return -1;

}


7. CNeuralNet.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 params.ini

//-----------------------------------------------------------------------

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;

}

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

int NumInputs = 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)));

}


8.[b]CParams.cpp[/b]
#include "CParams.h"

//because we will always be loading in the settings from an ini file

//we can just initialize everything to zero

double CParams::dPi                 = 3.14159265358979;

double CParams::dHalfPi             = dPi / 2;

double CParams::dTwoPi              = dPi * 2;

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               = 0;

double CParams::dMaxTurnRate        = 0;

double CParams::dMaxSpeed           = 0;

int CParams::iSweeperScale          = 0;

int CParams::iNumSweepers           = 0;

int CParams::iNumMines              = 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;

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

  grab >> ParamDescription;

  grab >> dMaxTurnRate;

  grab >> ParamDescription;

  grab >> dMaxSpeed;

  grab >> ParamDescription;

  grab >> iSweeperScale;

  grab >> ParamDescription;

  grab >> iNumMines;

  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;

}


  

9.[b]CTimer.cpp[/b]

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

}

【完】
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息