Implementation of A* Pathfinding

Sorry in advance for this being a long post.

I am making (for my degree) a snake game (you know the classic one that you used to have on mobile phones). However the classic rules of the game ares slightly changed and I am required to implement a computer controlled opponent for the player to play against.

I (having coded most of the rest of the game) have finally come to create my AIController class which can be created as a part of the "snake" class to control a snake.

I have decided to use a basic state machine to control what the computer does. He will go round collecting food until the risk from the extra length is deemed too high at which point he returns to his base (yes in this version i am required to have bases) to drop off his loot.

His navigation around the map I have planned to use the A* path finding algorithm which I had heard about before but never got around to implementing it.

The basic idea I have started with is that I have a start square and a target square and a function that builds a path between the two taking into account walls. (For now it ignores the possibility of hitting the human controlled snake). Anyway, this is what I have so far. It does work, sometimes, but others it gets stuck. I am mainly looking to see if I am (or am not) going about this in the right way especially with regards to the containers I use.

walls_ = pointer to array of position vectors that represent un-walkable locations.

AIPathData = structure made of a Vector3 class and an integer F value;

Vector3 = custom vector class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
void AIController::Update()
{
    if (updateTimerCounter_ == updateTimer_)
    {
        switch (state_)
        {
            case PICKUP:
                if (pathCompleted_)
                {
                    while (!CheckWalls(target_ = Level::GetRandomVector())){}
                    path_ = FindPath(owner_->GetPlayerFront());
                    pathCompleted_ = false;
                }
                Move();
                break;
        }
        updateTimerCounter_ = 0;
    }
    updateTimerCounter_++;
}
void AIController::Move()
{
    int pathSize = path_.size();

    if (path_.size() <= 0)
    {
        pathCompleted_ = true;
        return;
    }

    Vector3 direction = Vector3::Distance(owner_->GetPlayerFront(), path_.front());
    owner_->MoveNodes(direction);
    path_.pop_front();
}
std::list<Vector3> AIController::FindPath(Vector3 start)
{
    std::list<AIPathData> openList; //posible move locations that need to be evaluated
    std::list<Vector3> closedList; //position vectors making up the final path
    int numViableBounds = 0; //the number of bounding cells that are not walls or otherwise invalid
    Vector3 boundingCell; //the position vector of a viable bounding cell
    closedList.push_back(start);

    while (closedList.back() != target_)
    {
        boundingCell = closedList.back() + Vector3::North2D;
        if (CheckVector(boundingCell, closedList))
        {
            openList.push_front(GetPathData(boundingCell));
        }
        boundingCell = closedList.back() + Vector3::South2D;
        if (CheckVector(boundingCell, closedList))
        {
            openList.push_front(GetPathData(boundingCell));
        }
        boundingCell = closedList.back() + Vector3::East2D;
        if (CheckVector(boundingCell, closedList))
        {
            openList.push_front(GetPathData(boundingCell));
        }
        boundingCell = closedList.back() + Vector3::West2D;
        if (CheckVector(boundingCell, closedList))
        {
            openList.push_front(GetPathData(boundingCell));
        }

        Vector3 bestCell = GetBestCell(openList);
        closedList.push_back(bestCell);
        openList = ResetOpenList(openList);
    }

    closedList.pop_front(); //we are already at the start point so remove it from the path

    return closedList;
}
bool AIController::CheckVector(Vector3 cell, std::list<Vector3>& cl)
{
    return CheckWalls(cell) && CheckLists(cell, cl);
}
bool AIController::CheckWalls(Vector3 cell)
{
    for (int i = 0; i < walls_->size(); i++)
    {
        if ((*walls_)[i] == cell)
        {
            return false;
        }
    }
    return true;
}
bool AIController::CheckLists(Vector3 cell, std::list<Vector3>& cl) //as the open list grows this could be come more intensive, for that reason I push all new items onto the front of the vector this means I only need to test the first 4
{
    std::list<Vector3>::iterator i = cl.begin();
    if (cl.size() != 0)
    {
        while (i != cl.end())
        {
            if (*i == cell)
            {
                return false;
            }
            i++;
        }
    }
    return true;
}
AIPathData AIController::GetPathData(Vector3 cell)
{
    Vector3 difference = Vector3::Distance(cell, target_);
    int x = difference.getX();
    int y = difference.getY();

    if (x < 0)
    {
        x*= -1;
    }
    if (y < 0)
    {
        y *= -1;
    }

    AIPathData ret;
    ret.cell = cell;
    ret.fValue = x * 10 + y * 10;

    return ret;
}
Vector3 AIController::GetBestCell(std::list<AIPathData>& ol)
{
    std::list<AIPathData>::iterator j = ol.begin();
    AIPathData bestF = *j;
    AIPathData debug = *j;
    std::cout << "Values:" << std::endl;
    while (j != ol.end())
    {
        std::cout << j->fValue << std::endl;
        AIPathData debug = *j;
        if (j->fValue < bestF.fValue)
            bestF = *j;
        j++;
    }
    std::cout << "Best Value: "<< bestF.fValue << std::endl;
    return bestF.cell;

}
std::list<AIPathData>& AIController::ResetOpenList(std::list<AIPathData>& ol)
{
    while (ol.size() > 0)
    {
        ol.pop_front();
    }

    return ol;
}


So far it works fine as long as there are not too many obstacles (walls) but some times it can get into a position where every surrounding square has a worse value than the one its in resulting in an infinite loop where it constantly moves back and forth between the best and second best squares.

This basically occurs when it gets to a wall and the cell on the left and on the right have the same value so it just picks the first one. However one direction results in the wall ending the other doesn't. This can also make the snake go over every cell until it "realizes" its mistake.

If anyone could shed some light on what might be going wrongs, ways around my problems or alternative strategies I would appreciate it.

Thanks.
Topic archived. No new replies allowed.