Algos directory is setup.
This commit is contained in:
parent
0064664556
commit
b91e9ffaf6
38 changed files with 46 additions and 47 deletions
221
src/algos/maze.cpp
Normal file
221
src/algos/maze.cpp
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
#include <fmt/core.h>
|
||||
#include <string>
|
||||
#include "algos/rand.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "algos/maze.hpp"
|
||||
|
||||
using std::string;
|
||||
using matrix::Matrix;
|
||||
|
||||
namespace maze {
|
||||
inline size_t rand(size_t i, size_t j) {
|
||||
if(i < j) {
|
||||
return Random::uniform(i, j);
|
||||
} else if(j < i) {
|
||||
return Random::uniform(j, i);
|
||||
} else {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline bool complete(Matrix& maze) {
|
||||
size_t width = matrix::width(maze);
|
||||
size_t height = matrix::height(maze);
|
||||
|
||||
for(size_t row = 1; row < height; row += 2) {
|
||||
for(size_t col = 1; col < width; col += 2) {
|
||||
if(maze[row][col] != 0) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<Point> neighborsAB(Matrix& maze, Point on) {
|
||||
std::vector<Point> result;
|
||||
|
||||
std::array<Point, 4> points{{
|
||||
{on.x, on.y - 2},
|
||||
{on.x, on.y + 2},
|
||||
{on.x - 2, on.y},
|
||||
{on.x + 2, on.y}
|
||||
}};
|
||||
|
||||
for(auto point : points) {
|
||||
if(matrix::inbounds(maze, point.x, point.y)) {
|
||||
result.push_back(point);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<Point> neighbors(Matrix& maze, Point on) {
|
||||
std::vector<Point> result;
|
||||
|
||||
std::array<Point, 4> points{{
|
||||
{on.x, on.y - 2},
|
||||
{on.x, on.y + 2},
|
||||
{on.x - 2, on.y},
|
||||
{on.x + 2, on.y}
|
||||
}};
|
||||
|
||||
for(auto point : points) {
|
||||
if(matrix::inbounds(maze, point.x, point.y)) {
|
||||
if(maze[point.y][point.x] == WALL_VALUE) {
|
||||
result.push_back(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::pair<Point, Point> find_coord(Matrix& maze) {
|
||||
size_t width = matrix::width(maze);
|
||||
size_t height = matrix::height(maze);
|
||||
|
||||
for(size_t y = 1; y < height; y += 2) {
|
||||
for(size_t x = 1; x < width; x += 2) {
|
||||
if(maze[y][x] == WALL_VALUE) {
|
||||
auto found = neighborsAB(maze, {x, y});
|
||||
|
||||
for(auto point : found) {
|
||||
if(maze[point.y][point.x] == 0) {
|
||||
return {{x, y}, point};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matrix::dump("BAD MAZE", maze);
|
||||
dbc::sentinel("failed to find coord?");
|
||||
}
|
||||
|
||||
void Builder::randomize_rooms() {
|
||||
// use those dead ends to randomly place rooms
|
||||
for(auto at : $dead_ends) {
|
||||
if(Random::uniform(0,1)) {
|
||||
size_t offset = Random::uniform(0,1);
|
||||
Room cur{at.x+offset, at.y+offset, 1, 1};
|
||||
$rooms.push_back(cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Builder::init() {
|
||||
matrix::assign($walls, WALL_VALUE);
|
||||
}
|
||||
|
||||
void Builder::divide(Point start, Point end) {
|
||||
for(matrix::line it{start, end}; it.next();) {
|
||||
$walls[it.y][it.x] = 0;
|
||||
$walls[it.y+1][it.x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Builder::hunt_and_kill(Point on) {
|
||||
for(auto& room : $rooms) {
|
||||
for(matrix::box it{$walls, room.x, room.y, room.width}; it.next();) {
|
||||
$walls[it.y][it.x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
while(!complete($walls)) {
|
||||
auto n = neighbors($walls, on);
|
||||
if(n.size() == 0) {
|
||||
$dead_ends.push_back(on);
|
||||
auto t = find_coord($walls);
|
||||
on = t.first;
|
||||
$walls[on.y][on.x] = 0;
|
||||
size_t row = (on.y + t.second.y) / 2;
|
||||
size_t col = (on.x + t.second.x) / 2;
|
||||
$walls[row][col] = 0;
|
||||
} else {
|
||||
auto nb = n[rand(size_t(0), n.size() - 1)];
|
||||
$walls[nb.y][nb.x] = 0;
|
||||
|
||||
size_t row = (nb.y + on.y) / 2;
|
||||
size_t col = (nb.x + on.x) / 2;
|
||||
$walls[row][col] = 0;
|
||||
on = nb;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto at : $dead_ends) {
|
||||
for(auto& room : $rooms) {
|
||||
Point room_ul{room.x - room.width - 1, room.y - room.height - 1};
|
||||
Point room_lr{room.x + room.width - 1, room.y + room.height - 1};
|
||||
|
||||
if(at.x >= room_ul.x && at.y >= room_ul.y &&
|
||||
at.x <= room_lr.x && at.y <= room_lr.y)
|
||||
{
|
||||
for(matrix::compass it{$walls, at.x, at.y}; it.next();) {
|
||||
if($walls[it.y][it.x] == 1) {
|
||||
$walls[it.y][it.x] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Builder::inner_donut(float outer_rad, float inner_rad) {
|
||||
size_t x = matrix::width($walls) / 2;
|
||||
size_t y = matrix::height($walls) / 2;
|
||||
|
||||
for(matrix::circle it{$walls, {x, y}, outer_rad};
|
||||
it.next();)
|
||||
{
|
||||
for(int x = it.left; x < it.right; x++) {
|
||||
$walls[it.y][x] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for(matrix::circle it{$walls, {x, y}, inner_rad};
|
||||
it.next();)
|
||||
{
|
||||
for(int x = it.left; x < it.right; x++) {
|
||||
$walls[it.y][x] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Builder::inner_box(size_t outer_size, size_t inner_size) {
|
||||
size_t x = matrix::width($walls) / 2;
|
||||
size_t y = matrix::height($walls) / 2;
|
||||
|
||||
for(matrix::box it{$walls, x, y, outer_size};
|
||||
it.next();)
|
||||
{
|
||||
$walls[it.y][it.x] = 0;
|
||||
}
|
||||
|
||||
for(matrix::box it{$walls, x, y, inner_size};
|
||||
it.next();)
|
||||
{
|
||||
$walls[it.y][it.x] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Builder::remove_dead_ends() {
|
||||
dbc::check($dead_ends.size() > 0, "you have to run an algo first, no dead_ends to remove");
|
||||
for(auto at : $dead_ends) {
|
||||
for(matrix::compass it{$walls, at.x, at.y}; it.next();) {
|
||||
if($walls[it.y][it.x] == 0) {
|
||||
int diff_x = at.x - it.x;
|
||||
int diff_y = at.y - it.y;
|
||||
$walls[at.y + diff_y][at.x + diff_x] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Builder::dump(const std::string& msg) {
|
||||
matrix::dump(msg, $walls);
|
||||
}
|
||||
}
|
||||
29
src/algos/maze.hpp
Normal file
29
src/algos/maze.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
#include "algos/matrix.hpp"
|
||||
#include "map.hpp"
|
||||
|
||||
namespace maze {
|
||||
|
||||
|
||||
struct Builder {
|
||||
Matrix& $walls;
|
||||
std::vector<Room>& $rooms;
|
||||
std::vector<Point>& $dead_ends;
|
||||
|
||||
Builder(Map& map) :
|
||||
$walls(map.$walls), $rooms(map.$rooms), $dead_ends(map.$dead_ends)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
void hunt_and_kill(Point on={1,1});
|
||||
|
||||
void init();
|
||||
void randomize_rooms();
|
||||
void inner_donut(float outer_rad, float inner_rad);
|
||||
void inner_box(size_t outer_size, size_t inner_size);
|
||||
void divide(Point start, Point end);
|
||||
void remove_dead_ends();
|
||||
void dump(const std::string& msg);
|
||||
};
|
||||
}
|
||||
134
src/algos/pathing.cpp
Normal file
134
src/algos/pathing.cpp
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
#include "constants.hpp"
|
||||
#include "algos/pathing.hpp"
|
||||
#include "dbc.hpp"
|
||||
#include <vector>
|
||||
|
||||
using std::vector;
|
||||
|
||||
inline void add_neighbors(PointList &neighbors, Matrix &closed, size_t y, size_t x) {
|
||||
for(matrix::box it{closed, x, y, 1}; it.next();) {
|
||||
if(closed[it.y][it.x] == 0) {
|
||||
closed[it.y][it.x] = 1;
|
||||
neighbors.emplace_back(it.x, it.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Pathing::compute_paths(Matrix &walls) {
|
||||
INVARIANT();
|
||||
dbc::check(walls[0].size() == $width,
|
||||
fmt::format("Pathing::compute_paths called with walls.width={} but paths $width={}", walls[0].size(), $width));
|
||||
|
||||
dbc::check(walls.size() == $height,
|
||||
fmt::format("Pathing::compute_paths called with walls.height={} but paths $height={}", walls[0].size(), $height));
|
||||
|
||||
// Initialize the new array with every pixel at limit distance
|
||||
matrix::assign($paths, WALL_PATH_LIMIT);
|
||||
|
||||
Matrix closed = walls;
|
||||
PointList starting_pixels;
|
||||
PointList open_pixels;
|
||||
|
||||
// First pass: Add starting pixels and put them in closed
|
||||
for(size_t counter = 0; counter < $height * $width; counter++) {
|
||||
size_t x = counter % $width;
|
||||
size_t y = counter / $width;
|
||||
if($input[y][x] == 0) {
|
||||
$paths[y][x] = 0;
|
||||
closed[y][x] = 1;
|
||||
starting_pixels.emplace_back(x,y);
|
||||
}
|
||||
}
|
||||
|
||||
// Second pass: Add border to open
|
||||
for(auto sp : starting_pixels) {
|
||||
add_neighbors(open_pixels, closed, sp.y, sp.x);
|
||||
}
|
||||
|
||||
// Third pass: Iterate filling in the open list
|
||||
int counter = 1; // leave this here so it's available below
|
||||
for(; counter < WALL_PATH_LIMIT && !open_pixels.empty(); ++counter) {
|
||||
PointList next_open;
|
||||
for(auto sp : open_pixels) {
|
||||
$paths[sp.y][sp.x] = counter;
|
||||
add_neighbors(next_open, closed, sp.y, sp.x);
|
||||
}
|
||||
open_pixels = next_open;
|
||||
}
|
||||
|
||||
// Last pass: flood last pixels
|
||||
for(auto sp : open_pixels) {
|
||||
$paths[sp.y][sp.x] = counter;
|
||||
}
|
||||
}
|
||||
|
||||
void Pathing::set_target(const Point &at, int value) {
|
||||
$input[at.y][at.x] = value;
|
||||
}
|
||||
|
||||
void Pathing::clear_target(const Point &at) {
|
||||
$input[at.y][at.x] = 1;
|
||||
}
|
||||
|
||||
PathingResult Pathing::find_path(Point &out, int direction, bool diag)
|
||||
{
|
||||
// get the current dijkstra number
|
||||
int cur = $paths[out.y][out.x];
|
||||
int target = cur;
|
||||
bool found = false;
|
||||
|
||||
// a lambda makes it easy to capture what we have to change
|
||||
auto next_step = [&](size_t x, size_t y) -> bool {
|
||||
target = $paths[y][x];
|
||||
// don't go through walls
|
||||
if(target == WALL_PATH_LIMIT) return false;
|
||||
|
||||
int weight = cur - target;
|
||||
|
||||
if(weight == direction) {
|
||||
out = {x, y};
|
||||
found = true;
|
||||
// only break if this is a lower path
|
||||
return true;
|
||||
} else if(weight == 0) {
|
||||
out = {x, y};
|
||||
found = true;
|
||||
// only found an equal path, keep checking
|
||||
}
|
||||
|
||||
// this says keep going
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
if(diag) {
|
||||
for(matrix::box it{$paths, out.x, out.y, 1}; it.next();) {
|
||||
bool should_stop = next_step(it.x, it.y);
|
||||
if(should_stop) break;
|
||||
}
|
||||
} else {
|
||||
for(matrix::compass it{$paths, out.x, out.y}; it.next();) {
|
||||
bool should_stop = next_step(it.x, it.y);
|
||||
if(should_stop) break;
|
||||
}
|
||||
}
|
||||
|
||||
if(target == 0) {
|
||||
return PathingResult::FOUND;
|
||||
} else if(!found) {
|
||||
return PathingResult::FAIL;
|
||||
} else {
|
||||
return PathingResult::CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
bool Pathing::INVARIANT() {
|
||||
using dbc::check;
|
||||
|
||||
check($paths.size() == $height, "paths wrong height");
|
||||
check($paths[0].size() == $width, "paths wrong width");
|
||||
check($input.size() == $height, "input wrong height");
|
||||
check($input[0].size() == $width, "input wrong width");
|
||||
|
||||
return true;
|
||||
}
|
||||
40
src/algos/pathing.hpp
Normal file
40
src/algos/pathing.hpp
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
#include "point.hpp"
|
||||
#include "algos/matrix.hpp"
|
||||
#include <functional>
|
||||
|
||||
using matrix::Matrix;
|
||||
|
||||
constexpr const int PATHING_TOWARD=1;
|
||||
constexpr const int PATHING_AWAY=-1;
|
||||
|
||||
enum class PathingResult {
|
||||
FAIL=0,
|
||||
FOUND=1,
|
||||
CONTINUE=2
|
||||
};
|
||||
|
||||
class Pathing {
|
||||
public:
|
||||
size_t $width;
|
||||
size_t $height;
|
||||
Matrix $paths;
|
||||
Matrix $input;
|
||||
|
||||
Pathing(size_t width, size_t height) :
|
||||
$width(width),
|
||||
$height(height),
|
||||
$paths(height, matrix::Row(width, 1)),
|
||||
$input(height, matrix::Row(width, 1))
|
||||
{}
|
||||
|
||||
void compute_paths(Matrix &walls);
|
||||
void set_target(const Point &at, int value=0);
|
||||
void clear_target(const Point &at);
|
||||
Matrix &paths() { return $paths; }
|
||||
Matrix &input() { return $input; }
|
||||
int distance(Point to) { return $paths[to.y][to.x];}
|
||||
PathingResult find_path(Point &out, int direction, bool diag);
|
||||
|
||||
bool INVARIANT();
|
||||
};
|
||||
13
src/algos/rand.cpp
Normal file
13
src/algos/rand.cpp
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
#include "algos/rand.hpp"
|
||||
|
||||
namespace Random {
|
||||
std::random_device RNG;
|
||||
std::mt19937 GENERATOR(RNG());
|
||||
|
||||
|
||||
std::chrono::milliseconds milliseconds(int from, int to) {
|
||||
int tick = Random::uniform_real(float(from), float(to));
|
||||
return std::chrono::milliseconds{tick};
|
||||
}
|
||||
|
||||
}
|
||||
31
src/algos/rand.hpp
Normal file
31
src/algos/rand.hpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
#include <random>
|
||||
#include <chrono>
|
||||
|
||||
|
||||
namespace Random {
|
||||
extern std::mt19937 GENERATOR;
|
||||
|
||||
template<typename T>
|
||||
T uniform(T from, T to) {
|
||||
std::uniform_int_distribution<T> rand(from, to);
|
||||
|
||||
return rand(GENERATOR);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T uniform_real(T from, T to) {
|
||||
std::uniform_real_distribution<T> rand(from, to);
|
||||
|
||||
return rand(GENERATOR);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T normal(T mean, T stddev) {
|
||||
std::normal_distribution<T> rand(mean, stddev);
|
||||
|
||||
return rand(GENERATOR);
|
||||
}
|
||||
|
||||
std::chrono::milliseconds milliseconds(int from, int to);
|
||||
}
|
||||
138
src/algos/spatialmap.cpp
Normal file
138
src/algos/spatialmap.cpp
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
#include "algos/spatialmap.hpp"
|
||||
#include <fmt/core.h>
|
||||
|
||||
using namespace fmt;
|
||||
|
||||
using DinkyECS::Entity;
|
||||
|
||||
void SpatialMap::insert(Point pos, Entity ent, bool has_collision) {
|
||||
if(has_collision) {
|
||||
dbc::check(!occupied(pos), "attempt to insert an entity with collision in space with collision");
|
||||
}
|
||||
|
||||
$collision.emplace(pos, CollisionData{ent, has_collision});
|
||||
}
|
||||
|
||||
CollisionData SpatialMap::remove(Point pos, Entity ent) {
|
||||
auto [begin, end] = $collision.equal_range(pos);
|
||||
for(auto it = begin; it != end; ++it) {
|
||||
if(it->second.entity == ent) {
|
||||
// does the it->second go invalid after erase?
|
||||
auto copy = it->second;
|
||||
$collision.erase(it);
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
||||
dbc::sentinel("failed to find entity to remove");
|
||||
}
|
||||
|
||||
void SpatialMap::move(Point from, Point to, Entity ent) {
|
||||
auto data = remove(from, ent);
|
||||
insert(to, ent, data.collision);
|
||||
}
|
||||
|
||||
Entity SpatialMap::occupied_by(Point at) const {
|
||||
auto [begin, end] = $collision.equal_range(at);
|
||||
for(auto it = begin; it != end; ++it) {
|
||||
if(it->second.collision) {
|
||||
return it->second.entity;
|
||||
}
|
||||
}
|
||||
|
||||
return DinkyECS::NONE;
|
||||
}
|
||||
|
||||
bool SpatialMap::occupied(Point at) const {
|
||||
return occupied_by(at) != DinkyECS::NONE;
|
||||
}
|
||||
|
||||
bool SpatialMap::something_there(Point at) const {
|
||||
return $collision.count(at) > 0;
|
||||
}
|
||||
|
||||
Entity SpatialMap::get(Point at) const {
|
||||
dbc::check($collision.contains(at), "attempt to get entity when none there");
|
||||
auto [begin, end] = $collision.equal_range(at);
|
||||
return begin->second.entity;
|
||||
}
|
||||
|
||||
void SpatialMap::find_neighbor(EntityList &result, Point at, int dy, int dx) const {
|
||||
// don't bother checking for cells out of bounds
|
||||
if((dx < 0 && at.x <= 0) || (dy < 0 && at.y <= 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Point cell = {at.x + dx, at.y + dy};
|
||||
|
||||
auto entity = find(cell, [&](auto data) {
|
||||
return data.collision;
|
||||
});
|
||||
|
||||
if(entity != DinkyECS::NONE) result.push_back(entity);
|
||||
}
|
||||
|
||||
FoundEntities SpatialMap::neighbors(Point cell, bool diag) const {
|
||||
EntityList result;
|
||||
|
||||
// just unroll the loop since we only check four directions
|
||||
// this also solves the problem that it was detecting that the cell was automatically included as a "neighbor" but it's not
|
||||
find_neighbor(result, cell, 0, 1); // north
|
||||
find_neighbor(result, cell, 0, -1); // south
|
||||
find_neighbor(result, cell, 1, 0); // east
|
||||
find_neighbor(result, cell, -1, 0); // west
|
||||
|
||||
if(diag) {
|
||||
find_neighbor(result, cell, 1, -1); // south east
|
||||
find_neighbor(result, cell, -1, -1); // south west
|
||||
find_neighbor(result, cell, 1, 1); // north east
|
||||
find_neighbor(result, cell, -1, 1); // north west
|
||||
}
|
||||
|
||||
return {!result.empty(), result};
|
||||
}
|
||||
|
||||
inline void update_sorted(SortedEntities& sprite_distance, PointEntityMap& table, Point from, int max_dist) {
|
||||
Point seen{0,0};
|
||||
float wiggle = 0.0f;
|
||||
|
||||
for(const auto &rec : table) {
|
||||
Point sprite = rec.first;
|
||||
|
||||
int inside = (from.x - sprite.x) * (from.x - sprite.x) +
|
||||
(from.y - sprite.y) * (from.y - sprite.y);
|
||||
|
||||
if(from == sprite || rec.second.collision) {
|
||||
wiggle = 0.0f;
|
||||
} else if(sprite == seen) {
|
||||
wiggle += 0.02f;
|
||||
} else {
|
||||
wiggle = 0.0f;
|
||||
seen = sprite;
|
||||
}
|
||||
|
||||
if(inside < max_dist) {
|
||||
sprite_distance.push_back({inside, rec.second.entity, wiggle});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Entity SpatialMap::find(Point at, std::function<bool(CollisionData)> cb) const {
|
||||
auto [begin, end] = $collision.equal_range(at);
|
||||
|
||||
for(auto it = begin; it != end; ++it) {
|
||||
if(cb(it->second)) return it->second.entity;
|
||||
}
|
||||
|
||||
return DinkyECS::NONE;
|
||||
}
|
||||
|
||||
void SpatialMap::distance_sorted(SortedEntities& sprite_distance, Point from, int max_dist) {
|
||||
sprite_distance.clear();
|
||||
|
||||
update_sorted(sprite_distance, $collision, from, max_dist);
|
||||
|
||||
std::sort(sprite_distance.begin(), sprite_distance.end(), [](auto &a, auto &b) {
|
||||
return a.dist_square > b.dist_square;
|
||||
});
|
||||
}
|
||||
49
src/algos/spatialmap.hpp
Normal file
49
src/algos/spatialmap.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include "map.hpp"
|
||||
#include "dinkyecs.hpp"
|
||||
#include "point.hpp"
|
||||
|
||||
struct CollisionData {
|
||||
DinkyECS::Entity entity = DinkyECS::NONE;
|
||||
bool collision = false;
|
||||
};
|
||||
|
||||
struct EntityDistance {
|
||||
int dist_square=0;
|
||||
DinkyECS::Entity entity=DinkyECS::NONE;
|
||||
float wiggle=0.0f;
|
||||
};
|
||||
|
||||
// Point's has is in point.hpp
|
||||
using EntityList = std::vector<DinkyECS::Entity>;
|
||||
using PointEntityMap = std::unordered_multimap<Point, CollisionData>;
|
||||
using SortedEntities = std::vector<EntityDistance>;
|
||||
|
||||
struct FoundEntities {
|
||||
bool found;
|
||||
EntityList nearby;
|
||||
};
|
||||
|
||||
class SpatialMap {
|
||||
public:
|
||||
SpatialMap() {}
|
||||
PointEntityMap $collision;
|
||||
|
||||
void insert(Point pos, DinkyECS::Entity obj, bool has_collision);
|
||||
void move(Point from, Point to, DinkyECS::Entity ent);
|
||||
// return value is whether the removed thing has collision
|
||||
CollisionData remove(Point pos, DinkyECS::Entity entity);
|
||||
DinkyECS::Entity occupied_by(Point pos) const;
|
||||
bool occupied(Point pos) const;
|
||||
bool something_there(Point at) const;
|
||||
DinkyECS::Entity get(Point at) const;
|
||||
DinkyECS::Entity find(Point at, std::function<bool(CollisionData)> cb) const;
|
||||
void find_neighbor(EntityList &result, Point at, int dy, int dx) const;
|
||||
|
||||
FoundEntities neighbors(Point position, bool diag=false) const;
|
||||
|
||||
void distance_sorted(SortedEntities& sorted_sprites, Point from, int max_distance);
|
||||
size_t size() { return $collision.size(); }
|
||||
};
|
||||
11
src/algos/stats.cpp
Normal file
11
src/algos/stats.cpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#include "algos/stats.hpp"
|
||||
#include <fmt/core.h>
|
||||
#include "dbc.hpp"
|
||||
|
||||
void Stats::dump(std::string msg)
|
||||
{
|
||||
dbc::log(fmt::format("{}: sum: {}, sumsq: {}, n: {}, "
|
||||
"min: {}, max: {}, mean: {}, stddev: {}",
|
||||
msg, sum, sumsq, n, min, max, mean(),
|
||||
stddev()));
|
||||
}
|
||||
59
src/algos/stats.hpp
Normal file
59
src/algos/stats.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
#include <cmath>
|
||||
#include <chrono>
|
||||
|
||||
struct Stats {
|
||||
using TimeBullshit = std::chrono::time_point<std::chrono::high_resolution_clock>;
|
||||
|
||||
double sum = 0.0;
|
||||
double sumsq = 0.0;
|
||||
double n = 0.0;
|
||||
double min = 0.0;
|
||||
double max = 0.0;
|
||||
|
||||
inline void reset() {
|
||||
sum = 0.0;
|
||||
sumsq = 0.0;
|
||||
n = 0.0;
|
||||
min = 0.0;
|
||||
max = 0.0;
|
||||
}
|
||||
|
||||
inline double mean() {
|
||||
return sum / n;
|
||||
}
|
||||
|
||||
inline double stddev() {
|
||||
return std::sqrt((sumsq - (sum * sum / n)) / (n - 1));
|
||||
}
|
||||
|
||||
inline void sample(double s) {
|
||||
sum += s;
|
||||
sumsq += s * s;
|
||||
|
||||
if (n == 0) {
|
||||
min = s;
|
||||
max = s;
|
||||
} else {
|
||||
if (min > s) min = s;
|
||||
if (max < s) max = s;
|
||||
}
|
||||
|
||||
n += 1;
|
||||
}
|
||||
|
||||
inline TimeBullshit time_start() {
|
||||
return std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
inline void sample_time(TimeBullshit start) {
|
||||
auto end = std::chrono::high_resolution_clock::now();
|
||||
auto elapsed = std::chrono::duration<double>(end - start);
|
||||
|
||||
if(elapsed.count() > 0.0) {
|
||||
sample(1.0/elapsed.count());
|
||||
}
|
||||
}
|
||||
|
||||
void dump(std::string msg="");
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue