Skip to content

Commit f4a7a77

Browse files
committed
fix: flocking algorithm
1 parent fc672c3 commit f4a7a77

7 files changed

Lines changed: 96 additions & 148 deletions

File tree

docs/algorithms/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Students compare and contrast a variety of data structures. Students compare alg
1010
### Textbook
1111

1212
- Grokking Algorithms, Aditya Bhargava, Manning Publications, 2016. ISBN 978-1617292231
13+
- [Champ link](https://library.champlain.edu/record=b2471451~S1)
14+
- [Amazon](https://a.co/d/1g39FBY)
1315

1416
## Student-centered Learning Outcomes
1517

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Week 01 - Introduction to Artificial Intelligence Assignments
2+
3+
1. [Read the Syllabus](README.md);
4+
2. [Read Notes on plagiarism](blog/posts/NotesOnSubmissions/NotesOnSubmissions.md);
5+
3. [FERPA Consent Form](blog/posts/FerpaCompliance/FerpaCompliance.md);
6+
4. [Quiz 01](quiz/quiz01/quiz01.md);
7+
5. [Flocking Simulation](../assignments/flocking/README.md);

docs/artificialintelligence/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,4 @@ add_subdirectory(assignments/flocking)
3535
add_subdirectory(assignments/maze)
3636
add_subdirectory(assignments/life)
3737
add_subdirectory(assignments/rng)
38-
add_subdirectory(assignments/catchthecat)
38+
#add_subdirectory(assignments/catchthecat)

docs/artificialintelligence/README.md

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,43 @@ Upon completion of the Advanced AI for Games, students should be able to:
3131
- **Integrate** advanced AI seamlessly into game systems for cohesive environments;
3232
- **Consider** societal impact and consequences of AI applications in gaming;
3333

34-
## Schedule for Spring 2024
35-
3634
!!! warning
37-
35+
3836
This is a work in progress, and the schedule is subject to change. Every change will be communicated in class. Use the github repo as the source of truth for the schedule and materials. The materials provided in canvas are just a copy for archiving purposes and might be outdated.
3937

38+
## Schedule for Fall 2024
39+
40+
College dates for the Fall 2024 semester:
41+
42+
| Event | Date |
43+
|-------------------------------------------|-------------------|
44+
| Classes Begin | Aug. 26 |
45+
| Add/Drop | Aug. 26 - 30 |
46+
| No Classes - College remains open | Sept. 20 |
47+
| Indigenous Peoples Day Holiday Observance | Oct. 14 |
48+
| Registration for Spring Classes | Oct. 28 - Nov. 8 |
49+
| Last Day to Withdraw | Nov. 8 |
50+
| Thanksgiving Break | Nov. 25 - Nov. 29 |
51+
| Last Day of Classes | Dec. 6 |
52+
| Finals | Dec. 9 - Dec. 13 |
53+
| Winter Break | Dec. 16 - Jan. 10 |
54+
55+
56+
<div class="grid cards" markdown>
57+
58+
- ### :beginner:{ .lg .middle } __Introduction__
59+
<hr>
60+
- Week 1. 2024/01/15
61+
- Topic: AI for games, review of basic AI techniques
62+
- [Assignments](01-introduction/assignments.md)
63+
64+
</div>
65+
66+
---
67+
Old schedules for reference
68+
69+
## Schedule for Spring 2024
70+
4071
College dates for the Spring 2024 semester:
4172

4273
| Date | Event |
@@ -59,7 +90,6 @@ College dates for the Spring 2024 semester:
5990
- ### :beginner:{ .lg .middle } __Introduction__
6091

6192
---
62-
6393
- Week 1. 2024/01/15
6494
- Topic: AI for games, review of basic AI techniques
6595
- Activities:
@@ -174,8 +204,7 @@ College dates for the Spring 2024 semester:
174204
</div>
175205

176206

177-
---
178-
Old schedule for reference
207+
179208

180209
## Schedule for Fall 2023
181210

@@ -193,7 +222,7 @@ Relevant dates for the Fall 2023 semester:
193222

194223
- Week 1. 2023/08/28
195224
- Topic: Introduction
196-
- Formal Assignment: [Flocking at Beecrowd](assignments/flocking/README.md)
225+
- Formal Assignment: [Flocking Formal](assignments/flocking/README.md)
197226
- Interactive Assignment: [Flocking at MoBaGEn](https://github.com/InfiniBrains/mobagen/tree/master/examples/flocking)
198227

199228
- ### :robot:{ .lg .middle } __Behavioral Agents__
@@ -325,4 +354,3 @@ Relevant dates for the Fall 2023 semester:
325354
slide test: [test](slides/test.md)
326355

327356

328-
LangChain
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
add_executable(flocking flocking.cpp)
1+
add_executable(ai-flocking flocking.cpp)
22

33
file(GLOB TEST_INPUT_FILES ${CMAKE_CURRENT_SOURCE_DIR}/tests/*.in)
44
file(GLOB TEST_OUTPUT_FILES ${CMAKE_CURRENT_SOURCE_DIR}/tests/*.out)
55

6-
add_custom_test(flocking-test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/flocking "${TEST_INPUT_FILES}" "${TEST_OUTPUT_FILES}")
6+
add_custom_test(ai-flocking-test ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/flocking "${TEST_INPUT_FILES}" "${TEST_OUTPUT_FILES}")
77

docs/artificialintelligence/assignments/flocking/README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
1-
# Flocking agents behavior assignment
1+
# Flocking agents behavior formal assignment
22

33
You are in charge of implementing some functions to make some AI agents flock together in a game. After finishing it, you will be one step further to render it in a game engine, and start making reactive NPCs and enemies. You will learn all the basic concepts needed to code and customize your own AI behaviors.
44

5+
You can code this assignment in any language and/or game engine you want. But I already crafted some boilerplates to maximize your efficiency.
6+
7+
- (Preferred) Formal and automatically tested: [This current repo](https://github.com/InfiniBrains/Awesome-GameDev-Resources/tree/main/docs/artificialintelligence/assignments/flocking)
8+
- (Funnier) Interactive with SDL2: [MoBaGEn](https://github.com/InfiniBrains/mobagen/tree/master/examples/flocking/behaviours)
9+
- (Hard-core) C++ with CMake: [SDL2-CPM-CMake-Example](https://github.com/InfiniBrains/SDL2-CPM-CMake-Example)
10+
- I don't recommend using Game Engines for this specific assignment. Historically, students fail on the implementation of the double buffering and the math operations. But if you are confident, go ahead.
11+
12+
!!! danger "Notes on imprecision"
13+
14+
The automated tests of the formal assignment might differ somehow because of floating point imprecison, so don't worry much. If you cannot make it pass 100% of the tests, explain how you tried to solve it and what you think is wrong. I will evaluate your code based on your explanation.
15+
If you find an issue on my formal description or on the tests, send a PR and I will give you extra points.
16+
517
## What is flocking?
618

719
Flocking is a behavior that is observed in birds, fish and other animals that move in groups. It is a very simple behavior that can be implemented with a few lines of code. The idea is that each agent will try to move towards the center of mass of the group (cohesion), and will try to align its velocity with the average velocity of the group (AKA alignment). In addition, each agent will try to avoid collisions with other agents (AKA avoidance).

docs/artificialintelligence/assignments/flocking/flocking.cpp

Lines changed: 35 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -98,49 +98,21 @@ struct Cohesion {
9898
double radius;
9999
double k;
100100

101+
Cohesion() = default;
102+
101103
Vector2 ComputeForce(const vector<Boid>& boids, int boidAgentIndex) {
102-
auto agent = boids[boidAgentIndex];
103-
Vector2 centerOfMass = {0,0};
104-
int numberOfNeighbors = 0;
105-
for(int i = 0; i < boids.size(); i++){
106-
if(i==boidAgentIndex)
107-
continue;
108-
auto vec = boids[i].position - agent.position;
109-
auto magnitude = vec.getMagnitude();
110-
if(magnitude < radius && vec != Vector2::zero) {
111-
centerOfMass += vec;
112-
numberOfNeighbors++;
113-
}
114-
}
115-
if(numberOfNeighbors>0){
116-
return k * ((centerOfMass/numberOfNeighbors)/radius);
117-
}
118-
else
119-
return {};
104+
return {};
120105
}
121106
};
122107

123108
struct Alignment {
124109
double radius;
125110
double k;
126111

112+
Alignment() = default;
113+
127114
Vector2 ComputeForce(const vector<Boid>& boids, int boidAgentIndex) {
128-
auto agent = boids[boidAgentIndex];
129-
Vector2 avgVelocity = {0,0};
130-
int numberOfNeighbors = 0;
131-
for(const auto & boid : boids){
132-
auto displacementVec = boid.position - agent.position;
133-
auto magnitude = displacementVec.getMagnitude();
134-
if(magnitude < radius) {
135-
avgVelocity += boid.velocity;
136-
numberOfNeighbors++;
137-
}
138-
}
139-
if(numberOfNeighbors>0){
140-
return k * (avgVelocity/numberOfNeighbors);
141-
}
142-
else
143-
return {};
115+
return {};
144116
}
145117
};
146118

@@ -149,36 +121,24 @@ struct Separation {
149121
double k;
150122
double maxForce;
151123

124+
Separation() = default;
125+
152126
Vector2 ComputeForce(const vector<Boid>& boids, int boidAgentIndex) {
153-
auto agent = boids[boidAgentIndex];
154-
Vector2 avg = {0,0};
155-
int numberOfNeighbors = 0;
156-
for(int i=0;i<boids.size();i++){
157-
if(i==boidAgentIndex)
158-
continue;
159-
auto displacementVec = boids[i].position - agent.position;
160-
auto magnitude = displacementVec.getMagnitude();
161-
if(magnitude < radius && displacementVec != Vector2::zero) {
162-
auto displacementVecNorm = displacementVec / magnitude;
163-
avg += displacementVecNorm;
164-
numberOfNeighbors++;
165-
}
166-
}
167-
if(avg.getMagnitude() > maxForce)
168-
return avg.normalized() * maxForce;
169-
else
170-
return avg;
127+
return {};
171128
}
172129
};
173130

131+
// feel free to edit this main function to meet your needs
174132
int main() {
175133
// Variable declaration
176-
double rc, rs, Fsmax, ra, Kc, Ks, Ka;
134+
Separation separation{};
135+
Alignment alignment{};
136+
Cohesion cohesion{};
177137
int numberOfBoids;
178138
string line; // for reading until EOF
179139
vector<Boid> currentState, newState;
180140
// Input Reading
181-
cin >> rc >> rs >> Fsmax >> ra >> Kc >> Ks >> Ka >> numberOfBoids;
141+
cin >> cohesion.radius >> separation.radius >> separation.maxForce >> alignment.radius >> cohesion.k >> separation.k >> alignment.k >> numberOfBoids;
182142
for (int i = 0; i < numberOfBoids; i++)
183143
{
184144
Boid b;
@@ -188,103 +148,42 @@ int main() {
188148
newState.push_back(b);
189149
}
190150
// Final input reading and processing
151+
// todo: edit this. probably my code will be different than yours.
191152
while (getline(cin, line)) { // game loop
153+
// Use double buffer! you should read from the current and store changes in the new state.
192154
currentState = newState;
193155
double deltaT = stod(line);
194-
// cout << "== Start Line Read. deltaT: " << deltaT << " ==" << endl;
195-
vector<Vector2> allForces; // a vector of the sum of forces for each boid.
156+
// a vector of the sum of forces for each boid.
157+
vector<Vector2> allForces = vector<Vector2>(numberOfBoids, {0, 0});
196158
// Compute Forces
197159
for (int i = 0; i < numberOfBoids; i++) // for every boid
198160
{
199-
// cout << ">-> For Every Boid " << i << endl;
200-
double totalForceX = 0.0, totalForceY = 0.0, cohesionTotalX = 0.0, cohesionTotalY = 0.0,
201-
separationTotalVX = 0.0, separationTotalVY = 0.0, alignmentTotalVX = 0.0,
202-
alignmentTotalVY = 0.0;
203-
int cohesionTotalBoids = 0, alignmentTotalBoids = 0;
204-
for (int j = 0; j < N; j++) // for every boid combination. Pre-processing loop.
161+
for (int j = 0; j < numberOfBoids; j++) // for every boid combination. Pre-processing loop.
205162
{
206-
// cout << ">-> >-> For Every Boid Combination " << j << endl;
207-
// Pre-Process Cohesion Forces
208-
if (i != j
209-
&& get_distance(allBoids[i].x, allBoids[i].y, allBoids[j].x, allBoids[j].y) <= rc) {
210-
// cout << "Cohesion Force Found" << endl;
211-
cohesionTotalX += allBoids[j].x;
212-
cohesionTotalY += allBoids[j].y;
213-
cohesionTotalBoids++;
163+
// Process Cohesion Forces
164+
auto dist = (currentState[i].position-currentState[j].position).getMagnitude();
165+
if (i != j && dist <= cohesion.radius) {
166+
allForces[i] += cohesion.ComputeForce(currentState, i);
214167
}
215-
// Pre-Process Separation Forces
216-
if (i != j
217-
&& get_distance(allBoids[j].x, allBoids[j].y, allBoids[i].x, allBoids[i].y) <= rs) {
218-
// cout << "Separation Force Found" << endl;
219-
pair<double, double> nvANi
220-
= get_normalized_vector(allBoids[j].x, allBoids[j].y, allBoids[i].x, allBoids[i].y);
221-
pair<double, double> vANi
222-
= get_vector(allBoids[j].x, allBoids[j].y, allBoids[i].x, allBoids[i].y);
223-
// cout << "nvANI. x: " << nvANi.first << " y: " << nvANi.second << " vANI. x: " << vANi.first << " y: " << vANi.second << endl;
224-
if (vANi.first != 0) {
225-
separationTotalVX += nvANi.first / abs(vANi.first);
226-
}
227-
if (vANi.second != 0) {
228-
separationTotalVY += nvANi.second / abs(vANi.second);
229-
}
230-
// cout << "nvANi.first: " << nvANi.first << " vANi.first: " << vANi.first << " nvANi.first/vANi.first: " << nvANi.first/vANi.first << endl;
168+
// Process Separation Forces
169+
if (i != j && dist <= separation.radius) {
170+
allForces[i] += separation.ComputeForce(currentState, i);
231171
}
232-
// Pre-Process Alignment Forces
233-
if (get_distance(allBoids[i].x, allBoids[i].y, allBoids[j].x, allBoids[j].y) <= ra) {
234-
// cout << "Alignment Force Found" << endl;
235-
alignmentTotalVX += allBoids[j].vx;
236-
alignmentTotalVY += allBoids[j].vy;
237-
alignmentTotalBoids++;
238-
// cout << "alignmentTotalVX: " << alignmentTotalVX << endl;
172+
// Process Alignment Forces
173+
if (i != j && dist <= alignment.radius) {
174+
allForces[i] += alignment.ComputeForce(currentState, i);
239175
}
240176
}
241-
// Process Cohesion Forces
242-
if (cohesionTotalBoids > 0) { // If a cohesion force was found
243-
pair<double, double> cohesionVector
244-
= get_vector(allBoids[i].x, allBoids[i].y, cohesionTotalX / cohesionTotalBoids,
245-
cohesionTotalY / cohesionTotalBoids);
246-
totalForceX += (cohesionVector.first / rc) * Kc;
247-
totalForceY += (cohesionVector.second / rc) * Kc;
248-
// cout << "* cohesion force Y: " << ((cohesionVector.second/rc) * Kc) << endl;
249-
}
250-
// Process Separation Forces
251-
if (sqrt(pow(separationTotalVX, 2) * pow(separationTotalVY, 2))
252-
<= Fsmax) // if total force is NOT greater than limit, use as is.
253-
{
254-
totalForceX += (separationTotalVX * Ks);
255-
// cout << "S totalForceY: " << totalForceY << endl;
256-
totalForceY += (separationTotalVY * Ks);
257-
// cout << "* (1)separation Force Y: " << totalForceY << endl;
258-
} else { // else normalize and multiply by limit
259-
pair<double, double> normalized = normalize_vector(separationTotalVX, separationTotalVY);
260-
totalForceX += normalized.first * Fsmax * Ks;
261-
totalForceY += normalized.second * Fsmax * Ks;
262-
// cout << "* (2)separation Force Y: " << (normalized.second * Fsmax * Ks) << endl;
263-
}
264-
// Process Alignment Forces
265-
if (alignmentTotalBoids > 0) {
266-
totalForceX += alignmentTotalVX / alignmentTotalBoids * Ka;
267-
// cout << "(A) totalForceX: " << totalForceX << endl;
268-
totalForceY += alignmentTotalVY / alignmentTotalBoids * Ka;
269-
// cout << "* total Alignment Force X: " << (alignmentTotalVX/alignmentTotalBoids * Ka) << " totalForceX " << totalForceX << endl;
270-
}
271-
// cout << "total Force X in the end: " << totalForceX << endl;
272-
// cout << "* total Force Y in the end: " << totalForceY << endl;
273-
// Add total forces of 1 boid to forces vector.
274-
allForcesX.push_back(totalForceX);
275-
allForcesY.push_back(totalForceY);
276177
}
277178
// Tick Time and Output
179+
// todo: edit this. probably my code will be different than yours.
278180
cout << fixed << setprecision(3); // set 3 decimal places precision for output
279-
for (int i = 0; i < N; i++) // for every boid
181+
for (int i = 0; i < numberOfBoids; i++) // for every boid
280182
{
281-
// cout << "FORCES X: " << allForcesX[i] << " Y: " << allForcesY[i] << endl;
282-
allBoids[i].vx += allForcesX[i] * deltaT;
283-
allBoids[i].vy += allForcesY[i] * deltaT;
284-
allBoids[i].x += allBoids[i].vx * deltaT;
285-
allBoids[i].y += allBoids[i].vy * deltaT;
286-
cout << allBoids[i].x << " " << allBoids[i].y << " " << allBoids[i].vx << " "
287-
<< allBoids[i].vy << endl;
183+
newState[i].velocity += allForces[i] * deltaT;
184+
newState[i].position += currentState[i].velocity * deltaT;
185+
cout << newState[i].position.x << " " << newState[i].position.y << " "
186+
<< newState[i].velocity.x << " " << newState[i].velocity.y << endl;
288187
}
289188
}
290189

0 commit comments

Comments
 (0)