Solution Explorer

main.cpp

programs.cpp

code.cpp

resume.cpp

aboutMe.cpp

struct AI_Code {

Welcome to my AI Code page. Here you can see a few snippets of code that runs my AI, mainly for my current project Akimbo 2.

My AI system is heavily based off of Mat Buckland, an amazing coder and author of books such as "AI Techniques for Game Programming". You can learn more about Buckland at ai-junkie.com.

The AI system I went with is a simple finite state machine system. This system uses a series of singleton classes that AI agents can point to and pass themselves into to get instructions. Each of these state classes inherit from a base class that has the virtual functions enter, execute, and exit. Notice in the AI_CODE_1 (below), every function takes in a pointer to an AI object. This is the agent, who simply passes it's this pointer into here. All the functionality for the AI is implemented into this AI object, and the state machine is simply in charge of calling the correct AI functions for its particular state.

My AI System also deals with my AI Pathing system. Every AI Object has a pointer to the waypoint graph that they are following, and implement convenient functions to search that graph for a destination and follow a found path.

In AI_CODE_2 (below) you can see the A* algorithm that I use to find the shortest path between the passed in start and end nodes. I use an adjacency matrix class to store all the connections and weights between nodes. This allows quick look ups, and I don’t have to recalculate the weight between nodes since they never move. My implantation for a path is to just use the stl stack class. The nodes are pushed onto the stack in order, and when an agent traverses it, it simply pops off the top node when it reaches it. The agent will continue this “move to top node and then pop it” until the stack is empty or is told to stop by the state machine, in which case the stack is flushed.

AI_CODE_1: A single example of a state class that tells the AI how to Attack.


///////////////////////////////////////////////////////////////////////////////
// ATTACK STATE

AttackState* AttackState::getSingleton()
{
    static AttackState *singleton = new AttackState();
    return singleton;
}

void AttackState::enter( AI *ai )
{
    if(ai->getGraph() != NULL)
    {
        Waypoint *wp = ai->findClosestPoint(ai->pos,WAYPOINT_OFFENSE);
        if(wp == NULL)
            wp = ai->findClosestPoint(ai->pos,WAYPOINT_NOTYPE);
        
        if(wp != NULL)
        {
            ai->findAndFollowPath(wp);
            ai->changeActiveAnimation("walk",0.1);
        }
    }
    else
    {
        qePrintf("%s: graph not set", ai->getName());
        BRK();
    }
}

void AttackState::execute( AI *ai )
{
#if DEBUG_PRINT_STATEMENTS
    qePrintf("%s - execute AttackState\n",ai->name);
#endif
    const Player *player = LevelManager::getActiveLevel()->getPlayer();

    if(ai->finishedPath 
        || ai->distanceToPlayer(player) <= sqr(AI_THREAT_DISTANCE))
    {
        ai->setState(WaitState::getSingleton());
    }
}

void AttackState::exit( AI *ai )
{
    ai->stopFollowingPath();
}

AI_CODE_2: A* algorithm use to find the shortest path between startNode and endNode.

stack< Waypoint* > WaypointGraph::AStarAlgorithm( GraphNode* startNode, GraphNode* endNode )
{
    vector< GraphNode* > closedSet;
    vector< GraphNode* > openSet;
    stack<Waypoint*> ret;
    float currentGScore = 0;

    //initial set-up

    //add starting node to openSet
    //add all starting nodes neihbors to openSet with starting node as parent
    for(int i=0; i<nodes.size(); ++i)
    {
        //if is a neighbor node
        if(adjMat[startNode->label][nodes[i]->label] != -1)
        {
            //set parent

            nodes[i]->parent = startNode;

            //add to openSet
            openSet.push_back(nodes[i]);
        }
    }

    while(!openSet.empty())
    {
        // cycle through all the openSet nodes
        // find lowest f_score
        float lowestFScore = -1;
        GraphNode* lowestFScoreNode;
        for(int i=0; i<openSet.size(); ++i)
        {
            Point currentPos = openSet[i]->value->pos;
            openSet[i]->g_score = adjMat[openSet[i]->label][(openSet[i]->parent)->label] + currentGScore;
            openSet[i]->h_score = currentPos.distanceTo(endNode->value->pos);
            openSet[i]->f_score = openSet[i]->g_score + openSet[i]->h_score;

            if(openSet[i]->f_score  < lowestFScore || lowestFScore == -1)
            {
                lowestFScore = openSet[i]->f_score;
                lowestFScoreNode = openSet[i];
                currentGScore = openSet[i]->g_score;
            }
        }

        //drop lowestFScoreNode from openSet and add to closedSet

        closedSet.push_back(lowestFScoreNode);
        for(int i=0; i<openSet.size(); ++i)
        {
            if(lowestFScoreNode == openSet[i])
                openSet.erase(openSet.begin()+i);
        }

        // cycle through all lowestFScoreNode neihbors
        for(int j=0; j<nodes.size(); ++j)
        {
            //if node is neihbor

            if(adjMat[lowestFScoreNode->label][nodes[j]->label] != -1)
            {
                //if not in openet or closedSet
                if(!isNodeInSet(openSet, nodes[j]) &&
                    !isNodeInSet(closedSet, nodes[j]))
                {
                    //set parent and add to openSet
                    nodes[j]->parent = lowestFScoreNode;
                    openSet.push_back(nodes[j]);
                }
                //if in openSet already

                else if(isNodeInSet(openSet, nodes[j]))
                {
                    //if g_score of neihbor is less then current g_score of current to neigbor
                    float newGScore = lowestFScoreNode->g_score + adjMat[lowestFScoreNode->label][nodes[j]->label];
                    if(newGScore < nodes[j]->g_score)
                    {
                        nodes[j]->parent = lowestFScoreNode;
                        nodes[j]->g_score = adjMat[lowestFScoreNode->label][nodes[j]->label];
                        nodes[j]->f_score = nodes[j]->g_score + nodes[j]->h_score;
                    }
                }
            }
        } //for j

    }//while

    //check to see if we made it to the endNode
    if(!isNodeInSet(closedSet, endNode))
        return ret; // returns empty stack

    //make path
    GraphNode* currentNode = endNode;
    ret.push(currentNode->value);
    while(currentNode != startNode)
    {
        currentNode = currentNode->parent;
        ret.push(currentNode->value);
    }
    return ret;
}

}//end AI_Code