class CMinesweeper { private: //the minesweeper's neural net CNeuralNet m_ItsBrain;//扫雷机的大脑,在这里就是一个神经网络 //its position in the world SVector2D m_vPosition;//扫雷机的位置,用一个二维坐标表示 //direction sweeper is facing SVector2D m_vLookAt;//扫雷机的朝向,用一个二维向量表示,注意这个向量是物理中的向量 //its rotation (surprise surprise) double m_dRotation;//扫雷机的旋转速度?? double m_dSpeed;//扫雷机的速度 //to store output from the ANN double m_lTrack, m_rTrack;//扫雷机左、右轮速度,这里即是神经网络的输出 //the sweeper's fitness score double m_dFitness;//扫雷机的适应度函数,算法好坏的关键在于适应度函数的设计! //the scale of the sweeper when drawn double m_dScale;// 变比 //index position of closest mine int m_iClosestMine;//距离扫雷机最近的地雷 public: CMinesweeper();//构造函数 //updates the ANN with information from the sweepers enviroment bool Update(vector<SVector2D> &mines);//具体见update函数 //used to transform the sweepers vertices prior to rendering void WorldTransform(vector<SPoint> &sweeper); //returns a vector to the closest mine SVector2D GetClosestMine(vector<SVector2D> &objects); //checks to see if the minesweeper has 'collected' a mine int CheckForMine(vector<SVector2D> &mines, double size); void Reset(); //-------------------accessor functions SVector2D Position()const{return m_vPosition;} void IncrementFitness(){++m_dFitness;} void DecrementFitness(){--m_dFitness;}//这个是我自己加的,现在没什么用 double Fitness()const{return m_dFitness;} void PutWeights(vector<double> &w){m_ItsBrain.PutWeights(w);} int GetNumberOfWeights()const{return m_ItsBrain.GetNumberOfWeights();} };
其中的子函数较多。现一一介绍。
(1)扫雷机的更新函数update(),主要根据扫雷机的输入经过神经网络的输出,来更新扫雷机的速度和位置信息。
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);//四个输入之一:地雷的x坐标 inputs.push_back(vClosestMine.y);//四个输入之二:地雷的y坐标 //add in sweepers look at vector inputs.push_back(m_vLookAt.x);//四个输入之三:扫雷机朝向的x坐标 inputs.push_back(m_vLookAt.y);//四个输入之四:扫雷机朝向的y坐标 //update the brain and get feedback vector<double> output = m_ItsBrain.Update(inputs);//注意, m_ItsBrain是神经网络的实例 //因此,这个update是神经网络的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; }(2)矩阵变换函数
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); }关于这方面的介绍,详见本书第六章:登月也不难。其实就是一些矩阵的变换
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; }这个函数也不难,就是通过遍历和排序找到距离最近的地雷。
(4)检测是否碰到了地雷
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; }这些都不难,随便看看都能懂,就不一一注释了。