main
Brett 2025-01-28 21:23:17 -05:00
parent 20360912dd
commit a3b5939ec0
9 changed files with 375 additions and 9 deletions

View File

@ -2,21 +2,20 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" /> <mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug-addrsan/_deps/freetype-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug-addrsan/_deps/freetype-src/subprojects/dlg" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug-addrsan/_deps/imgui-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/freetype-src" vcs="Git" /> <mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/freetype-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/freetype-src/subprojects/dlg" vcs="Git" /> <mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/freetype-src/subprojects/dlg" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/glfw3-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/imgui-src" vcs="Git" /> <mapping directory="$PROJECT_DIR$/cmake-build-debug/_deps/imgui-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-release/_deps/freetype-src" vcs="Git" /> <mapping directory="$PROJECT_DIR$/cmake-build-release/_deps/freetype-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-release/_deps/freetype-src/subprojects/dlg" vcs="Git" /> <mapping directory="$PROJECT_DIR$/cmake-build-release/_deps/freetype-src/subprojects/dlg" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-release/_deps/glfw3-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-release/_deps/imgui-src" vcs="Git" /> <mapping directory="$PROJECT_DIR$/cmake-build-release/_deps/imgui-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-relwithdebinfo-addrsan/_deps/freetype-src" vcs="Git" /> <mapping directory="$PROJECT_DIR$/cmake-build-relwithdebinfo-addrsan/_deps/freetype-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-relwithdebinfo-addrsan/_deps/freetype-src/subprojects/dlg" vcs="Git" /> <mapping directory="$PROJECT_DIR$/cmake-build-relwithdebinfo-addrsan/_deps/freetype-src/subprojects/dlg" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-relwithdebinfo-addrsan/_deps/glfw3-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-relwithdebinfo-addrsan/_deps/imgui-src" vcs="Git" /> <mapping directory="$PROJECT_DIR$/cmake-build-relwithdebinfo-addrsan/_deps/imgui-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-relwithdebinfo/_deps/freetype-src" vcs="Git" /> <mapping directory="$PROJECT_DIR$/cmake-build-relwithdebinfo/_deps/freetype-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-relwithdebinfo/_deps/freetype-src/subprojects/dlg" vcs="Git" /> <mapping directory="$PROJECT_DIR$/cmake-build-relwithdebinfo/_deps/freetype-src/subprojects/dlg" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-relwithdebinfo/_deps/glfw3-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/cmake-build-relwithdebinfo/_deps/imgui-src" vcs="Git" /> <mapping directory="$PROJECT_DIR$/cmake-build-relwithdebinfo/_deps/imgui-src" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/blt-with-graphics" vcs="Git" /> <mapping directory="$PROJECT_DIR$/lib/blt-with-graphics" vcs="Git" />
<mapping directory="$PROJECT_DIR$/lib/blt-with-graphics/libraries/BLT" vcs="Git" /> <mapping directory="$PROJECT_DIR$/lib/blt-with-graphics/libraries/BLT" vcs="Git" />

View File

@ -49,7 +49,7 @@ macro(blt_add_project name source type)
project(skyscrapers-ga) project(skyscrapers-ga)
endmacro() endmacro()
project(skyscrapers-ga VERSION 0.0.4) project(skyscrapers-ga VERSION 0.0.5)
option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF) option(ENABLE_ADDRSAN "Enable the address sanitizer" OFF)
option(ENABLE_UBSAN "Enable the ub sanitizer" OFF) option(ENABLE_UBSAN "Enable the ub sanitizer" OFF)

View File

@ -0,0 +1,73 @@
#pragma once
/*
* Copyright (C) 2024 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef GENETIC_ALGORITHM_H
#define GENETIC_ALGORITHM_H
#include <limits>
#include <utility>
#include <skyscrapers.h>
namespace sky
{
struct individual_t
{
solution_t solution;
blt::i32 fitness = std::numeric_limits<blt::i32>::max();
individual_t(solution_t solution, const blt::i32 fitness): solution(std::move(solution)), fitness(fitness)
{
}
void replace(const problem_t& problem, const solution_t& new_solution)
{
solution = new_solution;
fitness = solution.fitness(problem);
}
};
class genetic_algorithm
{
public:
genetic_algorithm(problem_t problem, const blt::i32 individual_count, const double crossover_rate = 0.8, const double mutation_rate = 0.1):
crossover_rate(crossover_rate), mutation_rate(mutation_rate), m_problem(std::move(problem))
{
for (blt::i32 i = 0; i < individual_count; i++)
{
solution_t solution{problem.board_size};
solution.init(problem);
individuals.emplace_back(solution, solution.fitness(m_problem));
}
}
void run_step();
[[nodiscard]] solution_t select(blt::i32 k = 5) const;
static std::pair<solution_t, solution_t> crossover(const individual_t& first, const individual_t& second);
static solution_t mutate(const individual_t& individual);
private:
double crossover_rate, mutation_rate;
problem_t m_problem;
std::vector<individual_t> individuals;
};
}
#endif //GENETIC_ALGORITHM_H

View File

@ -47,7 +47,7 @@ namespace sky
right.reserve(board_size); right.reserve(board_size);
} }
void print(); void print() const;
[[nodiscard]] blt::i32 min() const // NOLINT [[nodiscard]] blt::i32 min() const // NOLINT
{ {
@ -56,11 +56,45 @@ namespace sky
[[nodiscard]] blt::i32 max() const [[nodiscard]] blt::i32 max() const
{ {
return board_size + 1; return board_size;
} }
}; };
blt::expected<problem_t, problem_t::error_t> problem_from_file(std::string_view path); blt::expected<problem_t, problem_t::error_t> problem_from_file(std::string_view path);
struct solution_t
{
blt::i32 board_size;
std::vector<blt::i32> board_data;
explicit solution_t(const blt::i32 board_size): board_size(board_size)
{
board_data.resize(board_size * board_size);
}
void init(const problem_t& problem);
// checks to see if the row contains duplicates. zero means all good
[[nodiscard]] blt::i32 row_incorrect_count(blt::i32 row) const;
// checks to see if the arrows are correct for this row
[[nodiscard]] blt::i32 row_view_count(const problem_t& problem, blt::i32 row) const;
[[nodiscard]] blt::i32 column_incorrect_count(blt::i32 column) const;
[[nodiscard]] blt::i32 column_view_count(const problem_t& problem, blt::i32 column) const;
[[nodiscard]] blt::i32 fitness(const problem_t& problem) const;
[[nodiscard]] blt::i32 get(const blt::i32 row, const blt::i32 column) const
{
return board_data[row * board_size + column];
}
};
problem_t make_test_problem();
solution_t make_test_solution();
solution_t make_test_solution_bad1();
solution_t make_test_solution_bad2();
solution_t make_test_solution_bad3();
} }
#endif //SKYSCRAPERS_H #endif //SKYSCRAPERS_H

7
skyscraper3x3.txt Normal file
View File

@ -0,0 +1,7 @@
BOARD_SIZE: 3
2 1 2
2 2
3 1
1 2
1 3 2

61
src/genetic_algorithm.cpp Normal file
View File

@ -0,0 +1,61 @@
/*
* <Short Description>
* Copyright (C) 2025 Brett Terpstra
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <genetic_algorithm.h>
#include <blt/std/hashmap.h>
#include <blt/std/random.h>
namespace sky {
void genetic_algorithm::run_step()
{
}
solution_t genetic_algorithm::select(const blt::i32 k) const
{
thread_local blt::hashset_t<blt::size_t> selected_indexes;
thread_local blt::random::random_t random{std::random_device{}()};
selected_indexes.clear();
blt::size_t index = 0;
blt::i32 best_fitness = std::numeric_limits<blt::i32>::max();
for (blt::i32 i = 0; i < k; ++i)
{
blt::size_t point;
do
{
point = random.get_u64(0, individuals.size());
} while (selected_indexes.contains(point));
selected_indexes.insert(point);
if (individuals[point].fitness < best_fitness)
{
index = point;
best_fitness = individuals[point].fitness;
}
}
return individuals[index].solution;
}
std::pair<solution_t, solution_t> genetic_algorithm::crossover(const individual_t& first, const individual_t& second)
{
}
solution_t genetic_algorithm::mutate(const individual_t& individual)
{
}
}

View File

@ -67,5 +67,16 @@ int main(int argc, const char** argv)
auto& problem_d = problem.value(); auto& problem_d = problem.value();
problem_d.print(); problem_d.print();
blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, destroy}.setSyncInterval(1)); const auto test = sky::make_test_problem();
auto sol = sky::make_test_solution();
test.print();
BLT_TRACE(sol.fitness(test));
BLT_TRACE(sky::make_test_solution_bad1().fitness(test));
BLT_TRACE(sky::make_test_solution_bad2().fitness(test));
BLT_TRACE(sky::make_test_solution_bad3().fitness(test));
// blt::gfx::init(blt::gfx::window_data{"My Sexy Window", init, update, destroy}.setSyncInterval(1));
} }

View File

@ -18,11 +18,13 @@
#include <skyscrapers.h> #include <skyscrapers.h>
#include <blt/fs/loader.h> #include <blt/fs/loader.h>
#include <blt/std/hashmap.h>
#include <blt/std/logging.h> #include <blt/std/logging.h>
#include <blt/std/random.h>
namespace sky namespace sky
{ {
void problem_t::print() void problem_t::print() const
{ {
BLT_TRACE("Board Size: %d", board_size); BLT_TRACE("Board Size: %d", board_size);
BLT_TRACE_STREAM << "\t"; BLT_TRACE_STREAM << "\t";
@ -107,4 +109,183 @@ namespace sky
return problem; return problem;
} }
void solution_t::init(const problem_t& problem)
{
blt::random::random_t random{std::random_device{}()};
for (auto& v : board_data)
v = random.get_i32(problem.min(), problem.max() + 1);
}
blt::i32 solution_t::row_incorrect_count(const blt::i32 row) const
{
thread_local std::vector<blt::i32> value_counts;
value_counts.resize(board_size);
std::memset(value_counts.data(), 0, sizeof(blt::i32) * board_size);
for (blt::i32 column = 0; column < board_size; column++)
++value_counts[get(row, column) - 1];
blt::i32 sum = 0;
for (const auto v : value_counts)
sum += std::abs(v - 1);
return sum;
}
blt::i32 solution_t::row_view_count(const problem_t& problem, const blt::i32 row) const
{
blt::i32 sees_left = 0;
blt::i32 sees_right = 0;
blt::i32 highest_left = 0;
blt::i32 highest_right = 0;
for (blt::i32 column = 0; column < board_size; column++)
{
if (get(row, column) > highest_left)
{
++sees_left;
highest_left = get(row, column);
}
}
for (blt::i32 column = board_size - 1; column >= 0; column--)
{
if (get(row, column) > highest_right)
{
++sees_right;
highest_right = get(row, column);
}
}
const auto left = problem.left[row];
const auto right = problem.right[row];
return std::abs(left - sees_left) + std::abs(right - sees_right);
}
blt::i32 solution_t::column_view_count(const problem_t& problem, blt::i32 column) const
{
blt::i32 sees_top = 0;
blt::i32 sees_bottom = 0;
blt::i32 highest_top = 0;
blt::i32 highest_bottom = 0;
for (blt::i32 row = 0; row < board_size; row++)
{
if (get(row, column) > highest_top)
{
++sees_top;
highest_top = get(row, column);
}
}
for (blt::i32 row = board_size - 1; row >= 0; row--)
{
if (get(row, column) > highest_bottom)
{
++sees_bottom;
highest_bottom = get(row, column);
}
}
const auto top = problem.top[column];
const auto bottom = problem.bottom[column];
return std::abs(top - sees_top) + std::abs(bottom - sees_bottom);
}
blt::i32 solution_t::column_incorrect_count(const blt::i32 column) const
{
thread_local std::vector<blt::i32> value_counts;
value_counts.resize(board_size);
std::memset(value_counts.data(), 0, sizeof(blt::i32) * board_size);
for (blt::i32 row = 0; row < board_size; row++)
++value_counts[get(row, column) - 1];
blt::i32 sum = 0;
for (const auto v : value_counts)
sum += std::abs(v - 1);
return sum;
}
blt::i32 solution_t::fitness(const problem_t& problem) const
{
blt::i32 fitness = 0;
for (blt::i32 i = 0; i < board_size; i++)
{
fitness += row_incorrect_count(i);
fitness += row_view_count(problem, i);
fitness += column_incorrect_count(i);
fitness += column_view_count(problem, i);
}
return fitness;
}
problem_t make_test_problem()
{
problem_t problem{3};
problem.top = {2, 1, 2};
problem.bottom = {1, 3, 2};
problem.left = {2, 3, 1};
problem.right = {2, 1, 2};
return problem;
}
solution_t make_test_solution()
{
solution_t solution{3};
solution.board_data = {
2, 3, 1,
1, 2, 3,
3, 1, 2
};
return solution;
}
solution_t make_test_solution_bad1()
{
solution_t solution{3};
solution.board_data = {
2, 3, 1,
1, 2, 3,
1, 1, 2
};
return solution;
}
solution_t make_test_solution_bad2()
{
solution_t solution{3};
solution.board_data = {
1, 3, 2,
1, 2, 3,
3, 1, 2
};
return solution;
}
solution_t make_test_solution_bad3()
{
solution_t solution{3};
solution.board_data = {
3, 2, 1,
1, 3, 2,
3, 2, 1
};
return solution;
}
} }