-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.cpp
157 lines (122 loc) · 4.71 KB
/
main.cpp
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
154
155
156
157
#include "include/configuration.hpp"
int main() {
fmt::print(fmt::fg(fmt::color::orange_red) | fmt::emphasis::bold,
"Welcome to Flappy Bird AI!\n");
librapid::setNumThreads(1);
#if defined(LIBRAPID_HAS_OPENCL)
fmt::print(fmt::fg(fmt::color::lime_green) | fmt::emphasis::bold, "OpenCL is enabled.\n");
librapid::configureOpenCL(true);
#else
fmt::print(fmt::fg(fmt::color::red) | fmt::emphasis::bold, "OpenCL is disabled.\n");
#endif
#if defined(LIBRAPID_HAS_CUDA)
fmt::print(fmt::fg(fmt::color::lime_green) | fmt::emphasis::bold, "CUDA is enabled.\n");
#else
fmt::print(fmt::fg(fmt::color::red) | fmt::emphasis::bold, "CUDA is disabled.\n");
#endif
// Configure the window
surge::Window mainWindow(librapid::Vec2i(1000, 600), "Flappy Bird AI");
// Configure the walls
std::vector<Wall> walls(NUM_WALLS);
resetWalls(walls);
// The bird population
generationStartTime = librapid::now();
std::vector<Bird> birds(NUM_BIRDS);
// Configure each bird
for (auto &bird : birds) {
bird = createBird();
bird.brain() = createBirdBrain();
}
// Information about the generations and birds
std::vector<double> wallDistances;
std::vector<double> generationBirdsAlive;
std::vector<double> generationBirdsAliveDistance;
// Style settings
surge::Font textFont("Arial", 20);
surge::Font mathFont("Cambria", 20);
ImGui::SetFont(textFont);
ImGui::GetStyle().WindowPadding = {16, 12};
// The main loop
while (!mainWindow.shouldClose()) {
// Begin a drawing and clear the screen
mainWindow.beginDrawing();
mainWindow.clear(surge::Color::veryDarkGray);
// Update the birds and walls
updateWalls(walls);
wallDistance += 0.1; // Arbitrary. So long as it's increasing, it's fine.
int64_t alive = updateBirds(birds, walls);
// Occasionally log some information about the current generation
if (mainWindow.frameCount() % 10 == 0) {
fmt::print(fmt::fg(fmt::color::purple) | fmt::emphasis::bold,
"Alive: {:>7} / {:>7}\r",
alive,
NUM_BIRDS);
generationBirdsAlive.emplace_back(((double)alive / (double)NUM_BIRDS) * 100.0);
generationBirdsAliveDistance.emplace_back(wallDistance);
}
if (alive == 0) {
// All birds are dead, so start a new generation
++generationNumber;
double generationTime = librapid::now() - generationStartTime;
fmt::print(fmt::fg(fmt::color::orange_red) | fmt::emphasis::bold,
"\n\nGeneration {} lasted {}.\n",
generationNumber,
librapid::formatTime(generationTime));
wallDistances.emplace_back(wallDistance);
// Reset the walls before the birds, since they may collide with "ghost" walls
// and cause some strange bugs
resetWalls(walls);
// Create the next generation of mutated bird brains
std::vector<std::pair<Bird::BirdBrain, double>> birdBrains;
birdBrains.reserve(birds.size());
for (auto &bird : birds) { birdBrains.emplace_back(bird.brain(), bird.fitness()); }
std::vector<Bird::BirdBrain> nextGeneration = newGeneration(birdBrains);
for (int64_t i = 0; i < NUM_BIRDS; ++i) {
birds[i] = createBird();
birds[i].brain() = nextGeneration[i];
}
generationBirdsAlive.clear();
generationBirdsAliveDistance.clear();
wallDistance = 0;
generationStartTime = librapid::now();
}
mainWindow.drawFPS(librapid::Vec2i(20, 20));
mainWindow.drawFrameTime(librapid::Vec2i(20, 40));
mainWindow.drawTime(librapid::Vec2i(20, 60));
if (ImGui::Begin("Statistics")) {
ImGui::Text("%s", fmt::format("Generation: {}", generationNumber).c_str());
ImGui::Text("%s", fmt::format("Alive: {}", alive).c_str());
ImGui::Text(
"%s",
fmt::format("Time: {}", librapid::formatTime(librapid::now() - generationStartTime))
.c_str());
ImGui::Separator();
ImGui::SliderFloat("Learning Rate", &mutationRate, 0.0f, 0.2f);
ImGui::Separator();
ImGui::PushFont(mathFont);
if (ImPlot::BeginSubplots("", 2, 1, ImVec2(-1, -1))) {
ImPlot::SetNextAxesLimits(0, wallDistance, 0, 100, ImPlotCond_Always);
if (ImPlot::BeginPlot("Birds Alive", ImVec2(0, 0))) {
ImPlot::SetupAxis(ImAxis_X1, "Time/s");
ImPlot::SetupAxis(ImAxis_Y1, "Alive %");
ImPlot::PlotLine(
"Birds Alive", generationBirdsAliveDistance, generationBirdsAlive);
ImPlot::EndPlot();
}
if (ImPlot::BeginPlot("Survival Distance", ImVec2(0, 0))) {
ImPlot::SetupAxis(ImAxis_X1, "Generation", ImPlotAxisFlags_AutoFit);
ImPlot::SetupAxis(ImAxis_Y1, "Distance", ImPlotAxisFlags_AutoFit);
ImPlot::PlotLine("Survival Distance", wallDistances);
ImPlot::PlotInfLines(
"Current Distance", &wallDistance, 1, ImPlotInfLinesFlags_Horizontal);
ImPlot::EndPlot();
}
ImPlot::EndSubplots();
}
ImGui::PopFont();
ImGui::End();
}
mainWindow.endDrawing();
}
return 0;
}