init: initial commit
This commit is contained in:
commit
b5868dfdec
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
.clangd
|
||||||
|
*.o
|
||||||
|
bin/swarm
|
30
Makefile
Normal file
30
Makefile
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
SHELL = /bin/bash
|
||||||
|
|
||||||
|
SRC_DIR = ./src
|
||||||
|
INC_DIR = ./include
|
||||||
|
OBJ_DIR = ./obj
|
||||||
|
|
||||||
|
HEADERS = $(wildcard $(INC_DIR)/*.hpp)
|
||||||
|
SRC_FILES = $(wildcard $(SRC_DIR)/*.cpp)
|
||||||
|
OBJ_FILES = $(addprefix $(OBJ_DIR)/,$(notdir $(SRC_FILES:.cpp=.o)))
|
||||||
|
|
||||||
|
CXXFLAGS += -Wall -std=c++20 -O2
|
||||||
|
|
||||||
|
all: bin/swarm
|
||||||
|
|
||||||
|
$(OBJ_DIR)/%.o: src/%.cpp
|
||||||
|
$(CXX) -I $(INC_DIR) $(CXXFLAGS) -c -o $@ $^
|
||||||
|
|
||||||
|
bin/swarm: $(OBJ_FILES)
|
||||||
|
@mkdir -p $(OBJ_DIR)
|
||||||
|
@mkdir -p bin
|
||||||
|
$(CXX) -I $(INC_DIR) $(CXXFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
.PHONY: debug
|
||||||
|
debug: CXXFLAGS += -O0 -g
|
||||||
|
debug: clean bin/swarm
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
$(RM) obj/*.o
|
||||||
|
$(RM) bin/swarm
|
51
include/screen.hpp
Normal file
51
include/screen.hpp
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <termios.h>
|
||||||
|
|
||||||
|
struct Screen {
|
||||||
|
char *buf;
|
||||||
|
std::size_t w, h;
|
||||||
|
|
||||||
|
float dx = 0, dy = 0;
|
||||||
|
float sx = 1, sy = 1;
|
||||||
|
|
||||||
|
void draw();
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
char &at(std::size_t n);
|
||||||
|
char &at(std::size_t x, std::size_t y);
|
||||||
|
|
||||||
|
void resize(std::size_t n);
|
||||||
|
void resize(std::size_t x, std::size_t y);
|
||||||
|
|
||||||
|
Screen(std::size_t n) : buf(nullptr) { resize(n); }
|
||||||
|
Screen(std::size_t x, std::size_t y) : buf(nullptr) { resize(x, y); }
|
||||||
|
~Screen() { delete[] buf; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
char _dummy;
|
||||||
|
|
||||||
|
static inline void gotoxy(int x, int y)
|
||||||
|
{
|
||||||
|
std::printf("\033[%d;%dH", y, x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static termios old, current;
|
||||||
|
static inline void enter_noncanonical_mode(void)
|
||||||
|
{
|
||||||
|
tcgetattr(STDIN_FILENO, &old);
|
||||||
|
current = old;
|
||||||
|
current.c_lflag &= ~ICANON; /* disable buffered i/o */
|
||||||
|
current.c_lflag &= ~ECHO; /* set no echo mode */
|
||||||
|
current.c_cc[VMIN] = 0;/* no wait */
|
||||||
|
current.c_cc[VTIME] = 0;/* no wait */
|
||||||
|
|
||||||
|
tcsetattr(STDIN_FILENO, TCSANOW, ¤t);
|
||||||
|
}
|
||||||
|
static inline void enter_canonical_mode(void){ tcsetattr(0, TCSANOW, &old); }
|
111
include/swarm.hpp
Normal file
111
include/swarm.hpp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <utility>
|
||||||
|
#include <vec.hpp>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct Agent {
|
||||||
|
using F = std::function<float(T)>;
|
||||||
|
|
||||||
|
virtual void move(float dt) = 0;
|
||||||
|
virtual void step(float dt) = 0;
|
||||||
|
|
||||||
|
virtual std::pair<float, T> best() const = 0;
|
||||||
|
|
||||||
|
Agent(F f) : f(f) {}
|
||||||
|
protected:
|
||||||
|
F f;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
struct Particle : public Agent<vec<N>> {
|
||||||
|
std::pair<float, vec<N>> best() const override { return {pb, pb_pos}; };
|
||||||
|
|
||||||
|
void move(float dt=1) override { position = position + velocity * dt; }
|
||||||
|
|
||||||
|
void step(float dt=1) override {
|
||||||
|
velocity = kviscosity * velocity
|
||||||
|
+ knostalgia * (pb_pos - position)
|
||||||
|
+ kpeer_pressure * (peer.best().second - position);
|
||||||
|
|
||||||
|
float y = f(position);
|
||||||
|
if(y < pb) {
|
||||||
|
pb = y;
|
||||||
|
pb_pos = position;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
float kviscosity = 0.9f;
|
||||||
|
float knostalgia = 2.5f;
|
||||||
|
float kpeer_pressure = 0.5f;
|
||||||
|
|
||||||
|
Particle(Agent<vec<N>>::F f, const Agent<vec<N>>& peer,
|
||||||
|
vec<N> position, vec<N> velocity)
|
||||||
|
: Agent<vec<N>>(f), position(position), velocity(velocity),
|
||||||
|
pb_pos(position), peer(peer) {
|
||||||
|
pb = f(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
const vec<N> &get_position() const { return position; };
|
||||||
|
|
||||||
|
private:
|
||||||
|
vec<N> position;
|
||||||
|
vec<N> velocity;
|
||||||
|
|
||||||
|
vec<N> pb_pos;
|
||||||
|
float pb;
|
||||||
|
|
||||||
|
const Agent<vec<N>>& peer;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
struct Swarm : public Agent<vec<N>> {
|
||||||
|
std::pair<float, vec<N>> best() const override { return {best_val, best_pos}; }
|
||||||
|
|
||||||
|
void move(float dt=1) override {
|
||||||
|
for(auto &p : particles)
|
||||||
|
p.move(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void step(float dt=1) override {
|
||||||
|
if(particles.empty()) return;
|
||||||
|
|
||||||
|
// find best before step() on each particle
|
||||||
|
// otherwise, if this were in best(), each p.step() would update it
|
||||||
|
const Particle<N> *b = &particles[0];
|
||||||
|
for(const auto &p : particles) {
|
||||||
|
if(b->best().first > p.best().first) b = &p;
|
||||||
|
}
|
||||||
|
|
||||||
|
best_val = b->best().first;
|
||||||
|
best_pos = b->best().second;
|
||||||
|
|
||||||
|
for(auto &p : particles)
|
||||||
|
p.step(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_particle(std::size_t n=1) {
|
||||||
|
for(std::size_t i=0; i <= n; ++i)
|
||||||
|
particles.push_back(
|
||||||
|
Particle<N>(this->_f, *this, vec<N>(0), vec<N>(10))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_particle(const vec<N> &pos, const vec<N> &vel) {
|
||||||
|
particles.push_back(
|
||||||
|
Particle<N>(this->f, *this, pos, vel)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<Particle<N>>& get_particles() { return particles; };
|
||||||
|
|
||||||
|
Swarm(Agent<vec<N>>::F f) : Agent<vec<N>>(f) {}
|
||||||
|
private:
|
||||||
|
vec<N> best_pos;
|
||||||
|
float best_val;
|
||||||
|
std::vector<Particle<N>> particles;
|
||||||
|
};
|
153
include/vec.hpp
Normal file
153
include/vec.hpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#define do_op(o, r, i) \
|
||||||
|
inline void operator o##= (const r & rhs) { for(unsigned n=0; n<(i); n++) c[n] o##= rhs.c[n]; } \
|
||||||
|
inline void operator o##= (T d) { for(unsigned n=0; n<(i); n++) c[n] o##= d; } \
|
||||||
|
inline r operator o (const r & rhs) const { r tmp(*this); tmp o##= rhs; return tmp; } \
|
||||||
|
inline r operator o (T d) const { r tmp(*this); tmp o##= d; return tmp; }
|
||||||
|
|
||||||
|
#define do_ops(c, i) \
|
||||||
|
do_op(*, c, i); \
|
||||||
|
do_op(/, c, i); \
|
||||||
|
do_op(+, c, i); \
|
||||||
|
do_op(-, c, i);
|
||||||
|
|
||||||
|
template <std::size_t n, std::floating_point T = float>
|
||||||
|
struct vec {
|
||||||
|
T c[n];
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t n, std::floating_point T>
|
||||||
|
vec<n, T> operator*(const T lhs, const vec<n, T>& rhs)
|
||||||
|
{ return rhs * lhs; }
|
||||||
|
|
||||||
|
template<std::floating_point T>
|
||||||
|
struct vec<2, T> {
|
||||||
|
union {
|
||||||
|
T c[2];
|
||||||
|
struct {
|
||||||
|
T x, y;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
vec(T x, T y) : c{x,y} {}
|
||||||
|
vec(T f) : vec(f,f) {}
|
||||||
|
vec() : vec(0,0) {}
|
||||||
|
|
||||||
|
do_ops(vec, 2);
|
||||||
|
|
||||||
|
inline vec<2,T> operator-(void) const { return {-x, -y}; }
|
||||||
|
|
||||||
|
inline T operator^(const vec<2,T>& rhs) const
|
||||||
|
{ return x * rhs.x + y * rhs.y; }
|
||||||
|
|
||||||
|
/* Return normalised (unit) vector. */
|
||||||
|
inline vec<2,T> norm() const
|
||||||
|
{ return *this / len(); }
|
||||||
|
|
||||||
|
inline T len() const
|
||||||
|
{ return sqrtf(x*x + y*y); }
|
||||||
|
|
||||||
|
inline T lensquared() const
|
||||||
|
{ return x*x + y*y; }
|
||||||
|
|
||||||
|
/* Return vector reflected around `n'. Expects `this' and `n' to be unit. */
|
||||||
|
inline vec<2,T> ref(const vec<2,T>& n) const
|
||||||
|
{ return n * ((*this ^ n) * 2) - *this;}
|
||||||
|
|
||||||
|
inline vec<2,T> refract(const vec<2,T>& n, T ior_ratio) const
|
||||||
|
{
|
||||||
|
return n * sqrtf(1 - powf(ior_ratio, 2) * (1 - powf(*this ^ n, 2))) + (*this - n * (*this ^ n)) * ior_ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline vec<2,T> refract(const vec<2,T>& n, T ior_incident, T ior_refract) const
|
||||||
|
{ return refract(n, ior_incident / ior_refract); }
|
||||||
|
|
||||||
|
inline vec<2,T> clamp(const vec<2,T>& from, const vec<2,T>& to) const
|
||||||
|
{ return { fmin(fmax(x, from.x), to.x),
|
||||||
|
fmin(fmax(y, from.y), to.y) }; }
|
||||||
|
|
||||||
|
inline vec<2,T> exp() const
|
||||||
|
{ return { ::exp(x), ::exp(y) }; }
|
||||||
|
|
||||||
|
inline vec<2,T> htan(T exposure = 0) const
|
||||||
|
{
|
||||||
|
return { powf((tanh(x + exposure) + 1) / 2, 2.2), powf((tanh(y + exposure) + 1) / 2, 2.2) };
|
||||||
|
return { tanh(x + exposure), tanh(y + exposure) };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<std::floating_point T>
|
||||||
|
struct vec<3, T> {
|
||||||
|
union {
|
||||||
|
T c[3];
|
||||||
|
struct {
|
||||||
|
T x, y, z;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
vec(T x, T y, T z) : c{x,y,z} {}
|
||||||
|
vec(T f) : vec(f,f,f) {}
|
||||||
|
vec() : vec(0,0,0) {}
|
||||||
|
|
||||||
|
do_ops(vec, 3);
|
||||||
|
|
||||||
|
inline vec<3,T> operator-(void) const { return {-x, -y, -z}; }
|
||||||
|
|
||||||
|
inline T operator^(const vec& rhs) const
|
||||||
|
{ return x * rhs.x + y * rhs.y + z * rhs.z; }
|
||||||
|
|
||||||
|
/* Return normalised (unit) vector. */
|
||||||
|
inline vec<3,T> norm() const
|
||||||
|
{ return *this / len(); }
|
||||||
|
|
||||||
|
inline T len() const
|
||||||
|
{ return sqrtf(x*x + y*y + z*z); }
|
||||||
|
|
||||||
|
inline T lensquared() const
|
||||||
|
{ return x*x + y*y + z*z; }
|
||||||
|
|
||||||
|
/* Return vec<3,T>tor reflected around `n'. Expects `this' and `n' to be unit. */
|
||||||
|
inline vec<3,T> ref(const vec<3,T>& n) const
|
||||||
|
{ return n * ((*this ^ n) * 2) - *this;}
|
||||||
|
|
||||||
|
/* Cross product. */
|
||||||
|
inline vec<3,T> cross(const vec<3,T>& b) const
|
||||||
|
{ return { y*b.z - z*b.y,
|
||||||
|
x*b.z - z*b.x,
|
||||||
|
x*b.y - y*b.x }; }
|
||||||
|
|
||||||
|
inline vec<3,T> refract(const vec<3,T>& n, T ior_ratio) const
|
||||||
|
{
|
||||||
|
return n * sqrtf(1 - powf(ior_ratio, 2) * (1 - powf(*this ^ n, 2))) + (*this - n * (*this ^ n)) * ior_ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline vec<3,T> refract(const vec<3,T>& n, T ior_incident, T ior_refract) const
|
||||||
|
{ return refract(n, ior_incident / ior_refract); }
|
||||||
|
|
||||||
|
inline vec<3,T> clamp(const vec<3,T>& from, const vec<3,T>& to) const
|
||||||
|
{ return { fmin(fmax(x, from.x), to.x),
|
||||||
|
fmin(fmax(y, from.y), to.y),
|
||||||
|
fmin(fmax(z, from.z), to.z) }; }
|
||||||
|
|
||||||
|
inline vec<3,T> exp() const
|
||||||
|
{ return { ::exp(x), ::exp(y), ::exp(z) }; }
|
||||||
|
|
||||||
|
inline vec<3,T> htan(T exposure = 0) const
|
||||||
|
{
|
||||||
|
return { powf((tanh(x + exposure) + 1) / 2, 2.2), powf((tanh(y + exposure) + 1) / 2, 2.2), powf((tanh(z + exposure) + 1) / 2, 2.2) };
|
||||||
|
return { tanh(x + exposure), tanh(y + exposure), tanh(z + exposure) };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#undef do_ops
|
||||||
|
#undef do_op
|
||||||
|
|
||||||
|
#define V3_FMT "v3(%.2f, %.2f, %.2f)"
|
||||||
|
#define V3_ARG(v) (v).x, (v).y, (v).z
|
||||||
|
|
||||||
|
#define V2_FMT "v2(%.2f, %.2f)"
|
||||||
|
#define V2_ARG(v) (v).x, (v).y
|
84
src/main.cpp
Normal file
84
src/main.cpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#include "screen.hpp"
|
||||||
|
#include "vec.hpp"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <swarm.hpp>
|
||||||
|
|
||||||
|
#define FPS 60
|
||||||
|
#define DT (1.0 / FPS)
|
||||||
|
|
||||||
|
float f(vec<2> x) {
|
||||||
|
return (x.x - 49)*(x.x - 49) + (x.y - 13)*(x.y - 13);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv) {
|
||||||
|
int iter_count;
|
||||||
|
if(argc < 2) iter_count = 100;
|
||||||
|
else sscanf(argv[1], "%d", &iter_count);
|
||||||
|
|
||||||
|
Swarm<2> swarm(f);
|
||||||
|
Screen scr(80, 24);
|
||||||
|
|
||||||
|
swarm.add_particle({0, 0}, {7, 3});
|
||||||
|
swarm.add_particle({4, 1}, {1, 5});
|
||||||
|
swarm.add_particle({16, 10}, {9, 10});
|
||||||
|
swarm.add_particle({7, 4}, {4, -3});
|
||||||
|
swarm.add_particle({14, 1}, {1, 5});
|
||||||
|
swarm.add_particle({1, 10}, {9, 10});
|
||||||
|
swarm.add_particle({5, 4}, {5, -3});
|
||||||
|
swarm.add_particle({4, 6}, {-1, 5});
|
||||||
|
swarm.add_particle({2, 6}, {9, 0});
|
||||||
|
swarm.add_particle({7, 7}, {4, -3});
|
||||||
|
|
||||||
|
enter_noncanonical_mode();
|
||||||
|
printf("\e[?1049h");
|
||||||
|
|
||||||
|
bool pause = false;
|
||||||
|
bool frame_step = false;
|
||||||
|
|
||||||
|
for(int i = 0; i < iter_count * FPS/4; ++i) {
|
||||||
|
if(!pause) {
|
||||||
|
scr.clear();
|
||||||
|
for(const auto &p : swarm.get_particles()) {
|
||||||
|
const auto & pos = p.get_position();
|
||||||
|
scr.at(pos.x, pos.y) = '#';
|
||||||
|
}
|
||||||
|
scr.draw();
|
||||||
|
|
||||||
|
swarm.move(DT * 4);
|
||||||
|
if(i % (FPS/4) == (FPS/4)-1)
|
||||||
|
swarm.step();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(frame_step) {
|
||||||
|
pause = true;
|
||||||
|
frame_step = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct {char buf[8]; int size;} inbuf = {{}, 0};
|
||||||
|
inbuf.size = read(STDIN_FILENO, inbuf.buf, 7);
|
||||||
|
switch (*inbuf.buf) {
|
||||||
|
case 'q':
|
||||||
|
goto cleanup;
|
||||||
|
case ' ':
|
||||||
|
pause = !pause;
|
||||||
|
break;
|
||||||
|
case '.':
|
||||||
|
pause = false;
|
||||||
|
frame_step = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep(1000 * 1000 / FPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
printf("\e[?1049l");
|
||||||
|
enter_canonical_mode();
|
||||||
|
|
||||||
|
auto [y, x] = swarm.best();
|
||||||
|
|
||||||
|
printf("Best: f(" V2_FMT ") = %.3f", V2_ARG(x), y);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
34
src/screen.cpp
Normal file
34
src/screen.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
#include <screen.hpp>
|
||||||
|
|
||||||
|
void Screen::resize(std::size_t n) {
|
||||||
|
w = n; h = 1;
|
||||||
|
buf = (char *)realloc(buf, n * sizeof(char));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Screen::resize(std::size_t x, std::size_t y) {
|
||||||
|
w = x; h = y;
|
||||||
|
buf = (char *)realloc(buf, x * y * sizeof(char));
|
||||||
|
}
|
||||||
|
|
||||||
|
char &Screen::at(std::size_t n) { return buf[n]; }
|
||||||
|
char &Screen::at(std::size_t x, std::size_t y) {
|
||||||
|
auto idx = (int)(((y-dy)/sy) * w) + (int)((x-dx)/sx);
|
||||||
|
if(idx >= w *h) return _dummy;
|
||||||
|
return buf[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
void Screen::clear() {
|
||||||
|
memset(buf, ' ', w*h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Screen::draw() {
|
||||||
|
gotoxy(0, 0);
|
||||||
|
for(int i = 0; i < h; ++i) {
|
||||||
|
for(int j = 0; j < w; ++j) {
|
||||||
|
std::fputc(at(j, i), stdout);
|
||||||
|
}
|
||||||
|
std::fputc('\n', stdout);
|
||||||
|
}
|
||||||
|
}
|
1
src/swarm.cpp
Normal file
1
src/swarm.cpp
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include <swarm.hpp>
|
Loading…
Reference in New Issue
Block a user