Would you like to inspect the original subtitles? These are the user uploaded subtitles that are being translated:
0
1
00:00:00,030 --> 00:00:02,790
Welcome to the Data Structures Bonus Section.
1
2
00:00:02,820 --> 00:00:09,000
In this module, we will look at various data structures that are useful in game AI algorithms.
2
3
00:00:09,150 --> 00:00:16,530
In order to implement the game AI, we need to store information and we do this by using variables or lists.
3
4
00:00:16,650 --> 00:00:22,950
But for the algorithms, sensors, pathfinding and other things that are required for game AI
4
5
00:00:22,950 --> 00:00:26,520
algorithms, well, we need more data structures.
5
6
00:00:26,580 --> 00:00:31,440
Let's look at some other options that are out there and how can we use them.
6
7
00:00:31,620 --> 00:00:33,660
Let's start by looking at the queue.
7
8
00:00:33,690 --> 00:00:35,850
The queue is very similar to a list.
8
9
00:00:35,910 --> 00:00:41,670
That means that it does contain data stored in an array-like format similar to a list.
9
10
00:00:41,670 --> 00:00:46,860
It's an abstract type, which means that it doesn't have a specific implementation.
10
11
00:00:46,860 --> 00:00:53,460
But in most game engines and in most languages, it's already implemented in a way or another.
11
12
00:00:53,490 --> 00:01:00,000
The main difference from a list is the fact that the elements in the queue get added only at the end
12
13
00:01:00,000 --> 00:01:05,250
of it as here, and they are removed only from the beginning of it as here.
13
14
00:01:06,220 --> 00:01:09,970
And this is very similar to a cinema queue in real life.
14
15
00:01:10,660 --> 00:01:15,130
Now let's look at the implementation of the queue in GDScript.
15
16
00:01:15,190 --> 00:01:23,440
Here we have a queue, which is basically a list in Godot, and we can use the append function, which is
16
17
00:01:23,440 --> 00:01:25,360
the same as for a regular array.
17
18
00:01:25,390 --> 00:01:31,270
And the result is of course, [1], [1, 2], [1, 2, 3], and actually append at the end of
18
19
00:01:31,270 --> 00:01:31,600
the list.
19
20
00:01:31,600 --> 00:01:33,640
So it's really good for us.
20
21
00:01:33,760 --> 00:01:37,050
If we print this out, then this will be the answer.
21
22
00:01:37,060 --> 00:01:42,220
And as I mentioned, we only need to remove elements from the beginning.
22
23
00:01:42,250 --> 00:01:43,590
In order to do this.
23
24
00:01:43,600 --> 00:01:50,140
We can use only this function called pop front, which will give us the first element in this case is
24
25
00:01:50,140 --> 00:01:54,070
1 and the queue will remain with just 2 and 3.
25
26
00:01:54,070 --> 00:02:02,350
The queue is very valuable in algorithms for pathfinding, but not only. Let's now look at the graph. (the graph) is also
26
27
00:02:02,380 --> 00:02:03,850
an abstract data structure.
27
28
00:02:03,850 --> 00:02:06,460
That means it doesn't have a strict implementation.
28
29
00:02:06,910 --> 00:02:14,050
The user can implement it as they see fit, but a graph's main feature is its nodes, which can store
29
30
00:02:14,050 --> 00:02:19,840
data as data containers and they can be linked together with edges.
30
31
00:02:20,020 --> 00:02:26,950
Of course, if we arrange them in a grid shape and we put edges between all the neighbors, we end up
31
32
00:02:26,950 --> 00:02:28,210
with a grid-like shape.
32
33
00:02:28,210 --> 00:02:32,460
So basically a grid is just the specific case of a graph.
33
34
00:02:32,470 --> 00:02:36,790
Please keep that in mind because we use also grids in this course.
34
35
00:02:36,880 --> 00:02:41,800
So how can we implement graphs? Well, there are actually a couple of different ways:
35
36
00:02:41,830 --> 00:02:48,220
This first implementation, while it is simple, I don't think it's really that much used maybe in some
36
37
00:02:48,220 --> 00:02:51,250
particular cases, but it's really easy to understand.
37
38
00:02:51,280 --> 00:02:55,540
So just imagine that each node has a specific ID.
38
39
00:02:56,050 --> 00:03:02,350
In this case, it's 1, 2, 3, 4, 5, and we have a matrix or a 2D array that has
39
40
00:03:02,350 --> 00:03:04,030
indices for each row and column.
40
41
00:03:04,120 --> 00:03:06,790
And if we take a pair, let's say [1, 2].
41
42
00:03:07,450 --> 00:03:11,770
If the value here is 1, that means that there is an edge between them.
42
43
00:03:11,770 --> 00:03:14,230
And if it's 0, then it means it's not.
43
44
00:03:14,830 --> 00:03:22,060
So with this same logic, then of course, this diagonal is 1 - 1 - 1 ... because it's for the same element.
44
45
00:03:22,180 --> 00:03:27,280
But then for [1, 2], [1, 3], [1, 4], [1, 5], we have the relationships.
45
46
00:03:27,280 --> 00:03:29,490
So basically only [1, 2] has it.
46
47
00:03:29,500 --> 00:03:35,890
Please note that also if we cut this diagonal, all of these values are mirrored, and this is because
47
48
00:03:36,070 --> 00:03:38,740
this graph can work in both directions.
48
49
00:03:38,740 --> 00:03:43,660
So for example, from 1 I can go to 2, but from 2 I can go to 1.
49
50
00:03:43,660 --> 00:03:46,960
So if I query instead from 2 to 1, it's also 1.
50
51
00:03:47,230 --> 00:03:49,990
So this is the first graph implementation.
51
52
00:03:50,020 --> 00:03:52,990
Let's look how this can be made in code.
52
53
00:03:53,530 --> 00:03:57,760
And for this, I use a particular functionality called array2D.
53
54
00:03:58,210 --> 00:04:02,530
Please note that Godot by default doesn't support 2D arrays.
54
55
00:04:03,040 --> 00:04:06,820
So this is actually from a library called GodotNext.
55
56
00:04:06,850 --> 00:04:09,520
You can find it by quickly searching on Google.
56
57
00:04:09,610 --> 00:04:16,120
And the first thing of course is to create a variable called grid, then to give it a size, which
57
58
00:04:16,120 --> 00:04:24,250
in this case is 6x6. These two FORs are just to set everything to 0 and this is just to
58
59
00:04:24,250 --> 00:04:30,400
make sure that everything is clean and then we can set up the relationships between them.
59
60
00:04:30,400 --> 00:04:32,500
So from 1 to 2, we have 1.
60
61
00:04:32,920 --> 00:04:35,020
From 2 to 1, we have 1 as well.
61
62
00:04:35,500 --> 00:04:41,050
And if we move down for each pair of nodes, we have the edges.
62
63
00:04:41,110 --> 00:04:45,760
So basically this is how we can implement the graph using this kind of structure.
63
64
00:04:45,970 --> 00:04:52,060
Now one common issue is: okay, we have a graph, but we need to actually go from element to element
64
65
00:04:52,240 --> 00:04:53,560
and how can we do this?
65
66
00:04:53,770 --> 00:05:00,610
There are a couple of ways how we can implement this, and the most common ones are breadth first search (BFS)
66
67
00:05:00,610 --> 00:05:02,110
and depth first search (DFS).
67
68
00:05:02,680 --> 00:05:06,940
And in fact, these are also used for pathfinding algorithms.
68
69
00:05:07,840 --> 00:05:12,160
In the example in this session we will discuss the depth first search.
69
70
00:05:12,250 --> 00:05:18,190
But if you check the section "Anatomy of a Pathfinder", we will discuss the breadth first search (BFS) as well.
70
71
00:05:18,640 --> 00:05:20,080
So make sure to check that.
71
72
00:05:20,080 --> 00:05:22,930
But how is the depth first search implemented?
72
73
00:05:23,050 --> 00:05:28,960
It's a recursive function if you don't know what the recursive function it's in the previous module.
73
74
00:05:28,990 --> 00:05:32,590
Quickly, just to sum it up, it's a function that calls itself.
74
75
00:05:32,830 --> 00:05:33,760
So that's one point.
75
76
00:05:34,180 --> 00:05:36,340
And the other it needs an exit condition.
76
77
00:05:36,340 --> 00:05:41,770
That means a way for the function to actually not call itself to infinity.
77
78
00:05:41,770 --> 00:05:47,410
And we do this by using something called was node visited.
78
79
00:05:47,950 --> 00:05:55,660
So we will use a secondary array that will store the IDs of the nodes that we already parsed.
79
80
00:05:55,660 --> 00:05:58,150
So how does this parsing work?
80
81
00:05:58,510 --> 00:06:05,170
Basically we have a function, let's say parse graph it receives a node ID, let's say 1.
81
82
00:06:05,950 --> 00:06:06,730
It prints that
82
83
00:06:06,730 --> 00:06:07,450
node ID
83
84
00:06:07,930 --> 00:06:14,440
And then it puts the node ID in the list. In the visited list. And then for all the other IDs,
84
85
00:06:14,440 --> 00:06:19,360
and we remember that in the previous one, the grid was of length 6.
85
86
00:06:19,720 --> 00:06:26,410
So in this case, for every possible neighbor, we can check if the cell from the current node to the
86
87
00:06:26,410 --> 00:06:28,420
other value is different than 0.
87
88
00:06:28,540 --> 00:06:30,940
Then this means there is a connection between them.
88
89
00:06:31,090 --> 00:06:35,260
But of course "i" here and current_node might be the same node.
89
90
00:06:35,770 --> 00:06:37,630
So they are always 1.
90
91
00:06:37,630 --> 00:06:40,870
So we need to make sure that is out of the question.
91
92
00:06:40,870 --> 00:06:42,300
But luckily we have this.
92
93
00:06:42,310 --> 00:06:46,630
So this node needs to not be visited already.
93
94
00:06:46,720 --> 00:06:48,190
Let's see if it's the same node.
94
95
00:06:48,200 --> 00:06:52,360
So it's 1 then this would be false because it's already visited.
95
96
00:06:52,390 --> 00:06:53,170
This also works
96
97
00:06:53,170 --> 00:06:57,640
for example, we are in 1 and then we move to 2 and then we go back.
97
98
00:06:58,060 --> 00:07:01,960
We cannot actually go back because 1 is already visited. If we are here,
98
99
00:07:01,990 --> 00:07:07,210
so okay, we are in 1 and we find out that the next node available is 2.
99
100
00:07:07,720 --> 00:07:09,670
Then we call this for 2.
100
101
00:07:09,730 --> 00:07:14,290
I actually have an example, so don't worry about not understanding this code properly.
101
102
00:07:14,500 --> 00:07:19,600
Basically it runs until there is no other neighbor for it.
102
103
00:07:19,750 --> 00:07:22,720
Let's find out exactly how it works in an example.
103
104
00:07:22,960 --> 00:07:25,150
So here we have a little different graph.
104
105
00:07:25,330 --> 00:07:30,190
I want to run the algorithm, with you, starting from the first point.
105
106
00:07:30,370 --> 00:07:36,880
A depth first search (DFS) would go first in the depth of the graph, hence the name depth, and then go back when
106
107
00:07:36,880 --> 00:07:43,120
it's no longer possible to go forward and then try another path. And, in the end go to the first node
107
108
00:07:43,120 --> 00:07:44,470
and finish the algorithm.
108
109
00:07:45,070 --> 00:07:46,510
So let's start: 1
109
110
00:07:46,780 --> 00:07:50,420
The first node available is 2, as noted here.
110
111
00:07:50,440 --> 00:07:51,550
Then we have 3.
111
112
00:07:52,090 --> 00:07:54,850
And then, as I mentioned, the next one is in depth.
112
113
00:07:54,850 --> 00:07:56,710
So it would be 6, not 4.
113
114
00:07:57,100 --> 00:07:58,630
So it's 6. Then,
114
115
00:07:58,720 --> 00:08:04,570
this node has no other neighbors, so that means that the parsing here is finished and then we move
115
116
00:08:04,570 --> 00:08:05,320
back to 3.
116
117
00:08:05,420 --> 00:08:10,330
It's also noted here, but with red. 3 is also finished because it doesn't have any other neighbor.
117
118
00:08:10,360 --> 00:08:13,000
It goes to 2. 2 as another neighbor.
118
119
00:08:13,000 --> 00:08:15,820
So it goes to 4, then 7, then 8.
119
120
00:08:15,820 --> 00:08:22,000
Then it goes back to 7, goes back to 4. 4 still has one more neighbor 5, 9.
120
121
00:08:22,630 --> 00:08:25,390
So these are the actual nodes that get parsed.
121
122
00:08:25,390 --> 00:08:31,380
And then going back 5 again, 4 to 1 and the algorithm finishes.
122
123
00:08:31,390 --> 00:08:35,860
So what we will see on the screen are these numbers with the red ones missing.
123
124
00:08:35,890 --> 00:08:39,220
Basically this is a graph traversal using DFS.
124
125
00:08:39,490 --> 00:08:45,430
BFS works a little differently because it will parse 1, 2 and then 3, 4, 5 and then
125
126
00:08:45,430 --> 00:08:46,780
6, 7, 8.
126
127
00:08:46,790 --> 00:08:50,680
So see, it's more like a spread instead of going in depth first.
127
128
00:08:50,980 --> 00:08:51,370
Okay.
128
129
00:08:51,730 --> 00:08:55,960
Now let's move on with the different implementation for the graph.
129
130
00:08:55,960 --> 00:08:59,140
And this is implementing via classes.
130
131
00:08:59,560 --> 00:09:07,450
So basically that means to have another object of (data) structure that has the list of its neighbors included.
131
132
00:09:07,450 --> 00:09:14,740
And then we can have multiple nodes that specify their own neighbors instead of matrix that actually
132
133
00:09:14,740 --> 00:09:15,760
contains all of this.
133
134
00:09:15,940 --> 00:09:20,350
This is actually more useful in a lot of real cases.
134
135
00:09:20,440 --> 00:09:27,820
For example, in the case of intersections in a city, for other cases where we need more flexibility
135
136
00:09:28,150 --> 00:09:31,090
because with the matrix we are kind of dependent on the size.
136
137
00:09:31,240 --> 00:09:34,610
But with this one, we actually use all the possible elements.
137
138
00:09:34,660 --> 00:09:41,590
One special case for the graph is called directed graph, and this means that once we go from 1 to 2,
138
139
00:09:41,590 --> 00:09:44,830
in this case, we can no longer go back to 1.
139
140
00:09:44,830 --> 00:09:50,090
Because while 1 does have a connection to 2, 2 does not have a connection to 1.
140
141
00:09:50,110 --> 00:09:55,390
If we were to implement this with the array2D method, then that would mean that we have a connection
141
142
00:09:55,390 --> 00:09:56,320
from 1 to 2.
142
143
00:09:56,470 --> 00:09:59,440
It would be 1, but from 2 to 1 it would be 0.
143
144
00:10:00,040 --> 00:10:06,190
And in the neighbor method, we would have probably an object to the ID being 1. One of its neighbors
144
145
00:10:06,190 --> 00:10:11,620
being 2, and then the object to the ID 2 that doesn't have the neighbor 1, but has the neighbor
145
146
00:10:11,620 --> 00:10:12,700
3 and 4.
146
147
00:10:12,970 --> 00:10:17,380
This is it with the common data structures in game AI algorithms.
147
148
00:10:17,470 --> 00:10:21,340
Of course there might be others, but these are the most common ones.
15285
Can't find what you're looking for?
Get subtitles in any language from opensubtitles.com, and translate them here.