403 lines
9.3 KiB
C++
403 lines
9.3 KiB
C++
#include <bits/stdc++.h>
|
|
#include <omp.h>
|
|
#include "pushbox.h"
|
|
#include "box-solver.h"
|
|
|
|
std::mt19937 rng(std::random_device{}());
|
|
|
|
#define compiler_assume(condition) \
|
|
do { \
|
|
if (!(condition)) \
|
|
__builtin_unreachable(); \
|
|
} while (false); \
|
|
|
|
const int species_limit = 16;
|
|
const int population_limit = 10;
|
|
const int protect_age = 30;
|
|
const int stable_limit = 10;
|
|
const int SPECIES_NAME_LEN = 4;
|
|
|
|
struct Gene {
|
|
Maze m;
|
|
int w;
|
|
Gene() {}
|
|
Gene(const Maze &m, int w) : m(m), w(w) {}
|
|
};
|
|
|
|
struct Species {
|
|
std::vector<Gene> population;
|
|
int best_w;
|
|
char name[SPECIES_NAME_LEN + 1];
|
|
int stable_age, age, tactic;
|
|
|
|
Species() : population(), best_w(-1), name{"NULL"}, stable_age(0), age(0), tactic(-1) {}
|
|
|
|
void tick() {
|
|
age += 1;
|
|
stable_age += 1;
|
|
}
|
|
|
|
int size() const {
|
|
return population.size();
|
|
}
|
|
|
|
bool empty() const {
|
|
return population.empty();
|
|
}
|
|
|
|
bool operator<(const Species &o) const {
|
|
return best_w < o.best_w;
|
|
}
|
|
};
|
|
|
|
inline void writeGene(const Gene &g, std::FILE *f) {
|
|
std::fprintf(f, "%d\n", g.w);
|
|
writeMaze(g.m, f);
|
|
}
|
|
|
|
inline Gene loadGene(std::FILE *f) {
|
|
Gene g;
|
|
std::fscanf(f, "%d", &g.w);
|
|
g.m = loadMaze(f);
|
|
return g;
|
|
}
|
|
|
|
inline void writeSpecies(const Species &s, std::FILE *f) {
|
|
std::fprintf(f, "%d %d %s %d %d %d\n", (int)s.size(), s.best_w, s.name
|
|
, s.stable_age, s.age, s.tactic);
|
|
for (const auto &g : s.population) {
|
|
writeGene(g, f);
|
|
}
|
|
}
|
|
|
|
inline Species loadSpecies(std::FILE *f) {
|
|
Species res;
|
|
int n;
|
|
std::fscanf(f, "%d %d %s %d %d %d", &n, &res.best_w, res.name
|
|
, &res.stable_age, &res.age, &res.tactic);
|
|
res.population.reserve(n);
|
|
for (int i = 0; i < n; ++i) {
|
|
res.population.push_back(loadGene(f));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
inline void loadBiosphere(std::vector<Species> &species, int &best_w, std::FILE *f) {
|
|
int n;
|
|
std::fscanf(f, "%d %d", &n, &best_w);
|
|
species.reserve(n);
|
|
for (int i = 0; i < n; ++i)
|
|
species.push_back(loadSpecies(f));
|
|
}
|
|
|
|
inline void writeBiosphere(const std::vector<Species> &species, int &best_w, std::FILE *f) {
|
|
int n = species.size();
|
|
std::fprintf(f, "%d %d\n", n, best_w);
|
|
for (int i = 0; i < n; ++i)
|
|
writeSpecies(species[i], f);
|
|
}
|
|
|
|
inline void randomName(int n, char res[]) {
|
|
static std::uniform_int_distribution<int> ld(0, 35);
|
|
for (int i = 0; i < n; ++i) {
|
|
int c = ld(rng);
|
|
res[i] = (c < 26 ? c + 'a' : c - 26 + '0');
|
|
}
|
|
}
|
|
|
|
thread_local std::uniform_int_distribution<int> rng0N(0, N - 1);
|
|
|
|
inline void mutationRandomHole(Species &res, const Gene &sc, int clear_cnt) {
|
|
Gene ac = sc;
|
|
for (int i = 0; i < clear_cnt; ++i) {
|
|
int x = rng0N(rng), y = rng0N(rng);
|
|
ac.m.set(x, y, SQR_SPACE);
|
|
}
|
|
|
|
ac.w = Solve::solve(ac.m);
|
|
res.best_w = ac.w;
|
|
res.population.push_back(std::move(ac));
|
|
}
|
|
|
|
inline void mutationReselectPOI(Species &res, const Gene &sc) {
|
|
Gene ac = sc;
|
|
for (int i = 0; i < POI_EXCEED; ++i) {
|
|
int x = rng0N(rng), y = rng0N(rng);
|
|
while (true) {
|
|
bool flag = false;
|
|
for (int j = 0; j < POI_EXCEED; ++j)
|
|
flag |= (x == ac.m.poi[j][0] && y == ac.m.poi[j][1]);
|
|
|
|
if (!flag)
|
|
break;
|
|
x = rng0N(rng);
|
|
y = rng0N(rng);
|
|
}
|
|
|
|
ac.m.poi[i][0] = x;
|
|
ac.m.poi[i][1] = y;
|
|
for (int dx = -1; dx <= 1; ++dx) {
|
|
for (int dy = -1; dy <= 1; ++dy) {
|
|
int xx = x + dx, yy = y + dy;
|
|
if (0 <= xx && xx < N && 0 <= yy && yy < N)
|
|
ac.m.set(xx, yy, SQR_SPACE);
|
|
}
|
|
}
|
|
}
|
|
|
|
ac.w = Solve::solve(ac.m);
|
|
if (ac.w != -1) {
|
|
res.best_w = ac.w;
|
|
res.population.push_back(std::move(ac));
|
|
}
|
|
}
|
|
|
|
inline void mutationRowCl(Species &res, const Gene &sc, int rep = 1) {
|
|
Gene ac = sc;
|
|
|
|
std::uniform_int_distribution<int> rngRow(0, N - rep);
|
|
int r = rngRow(rng);
|
|
for (int i = 0; i < rep; ++i) {
|
|
for (int j = 0; j < N; ++j)
|
|
ac.m.set(r + i, j, SQR_SPACE);
|
|
}
|
|
|
|
ac.w = Solve::solve(ac.m);
|
|
res.best_w = ac.w;
|
|
res.population.push_back(std::move(ac));
|
|
}
|
|
|
|
inline void mutationColCl(Species &res, const Gene &sc, int rep = 1) {
|
|
Gene ac = sc;
|
|
|
|
std::uniform_int_distribution<int> rngCol(0, N - rep);
|
|
int c = rngCol(rng);
|
|
for (int i = 0; i < rep; ++i) {
|
|
for (int j = 0; j < N; ++j)
|
|
ac.m.set(j, c + i, SQR_SPACE);
|
|
}
|
|
|
|
ac.w = Solve::solve(ac.m);
|
|
res.best_w = ac.w;
|
|
res.population.push_back(std::move(ac));
|
|
}
|
|
|
|
inline void mutationSqrCl(Species &res, const Gene &sc, int sz) {
|
|
Gene ac = sc;
|
|
const int sz2 = sz / 2;
|
|
int cx = rng0N(rng), cy = rng0N(rng);
|
|
for (int dx = -sz2; dx <= sz2; ++dx) {
|
|
for (int dy = -sz2; dy <= sz2; ++dy) {
|
|
int xx = cx + dx, yy = cy + dy;
|
|
if (0 <= xx && xx < N && 0 <= yy && yy < N)
|
|
ac.m.set(xx, yy, SQR_SPACE);
|
|
}
|
|
}
|
|
|
|
ac.w = Solve::solve(ac.m);
|
|
res.best_w = ac.w;
|
|
res.population.push_back(std::move(ac));
|
|
}
|
|
|
|
inline Species mutation(const Species &sc) {
|
|
static std::uniform_int_distribution<int> rngTacs(0, 4);
|
|
Species res;
|
|
randomName(SPECIES_NAME_LEN, res.name);
|
|
|
|
do {
|
|
int tactic = rngTacs(rng);
|
|
res.tactic = tactic;
|
|
switch (tactic) {
|
|
case 0:
|
|
mutationRandomHole(res, sc.population[0], 80);
|
|
break;
|
|
case 1:
|
|
mutationRowCl(res, sc.population[0], 3);
|
|
break;
|
|
case 2:
|
|
mutationColCl(res, sc.population[0], 3);
|
|
break;
|
|
case 3:
|
|
mutationSqrCl(res, sc.population[0], 5);
|
|
break;
|
|
case 4:
|
|
mutationSqrCl(res, sc.population[0], 7);
|
|
break;
|
|
}
|
|
} while (res.empty());
|
|
return res;
|
|
}
|
|
|
|
inline void expand(const Maze &q, std::vector<Gene> &res) {
|
|
int s = Solve::solve(q);
|
|
if (s != -1)
|
|
res.emplace_back(q, s);
|
|
}
|
|
|
|
inline void inheritGene(Maze q, std::vector<Gene> &res) {
|
|
for (int i = 0; i < POI_EXCEED; ++i) {
|
|
for (int j : {0, 1}) {
|
|
for (int d : {-1, 1}) {
|
|
compiler_assume(q.poi[i][j] >= 0 && q.poi[i][j] < N);
|
|
|
|
q.poi[i][j] += d;
|
|
if (0 <= q.poi[i][j] && q.poi[i][j] < N) {
|
|
bool flag = false;
|
|
for (int k = 0; k < POI_EXCEED; ++k)
|
|
flag |= (q.poi[i][0] == q.poi[k][0] && q.poi[i][1] == q.poi[k][1]);
|
|
|
|
if (!flag)
|
|
expand(q, res);
|
|
}
|
|
q.poi[i][j] -= d;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 25; i++) {
|
|
compiler_assume(q.isValid());
|
|
int x = rng0N(rng), y = rng0N(rng);
|
|
q.flip(x, y);
|
|
if (q.isValid())
|
|
expand(q, res);
|
|
q.flip(x, y);
|
|
}
|
|
}
|
|
|
|
inline void inherit(Species &s) {
|
|
std::vector<Gene> nxt;
|
|
for (auto &g : s.population) {
|
|
inheritGene(g.m, nxt);
|
|
nxt.push_back(std::move(g));
|
|
}
|
|
s.population.clear();
|
|
|
|
int ori_best_w = s.best_w;
|
|
for (const auto &g : nxt) {
|
|
s.best_w = std::max(s.best_w, g.w);
|
|
}
|
|
|
|
if (ori_best_w != s.best_w)
|
|
s.stable_age = 0;
|
|
|
|
for (auto &g : nxt) {
|
|
if (g.w == s.best_w)
|
|
s.population.push_back(std::move(g));
|
|
}
|
|
nxt.clear();
|
|
|
|
std::shuffle(s.population.begin(), s.population.end(), rng);
|
|
if (s.population.size() > population_limit)
|
|
s.population.resize(population_limit);
|
|
}
|
|
|
|
namespace term {
|
|
|
|
inline void clear()
|
|
{
|
|
printf("\033[2J\033[0;0H");
|
|
}
|
|
|
|
inline void setColor(int color)
|
|
{
|
|
printf("\033[%dm", color);
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
std::vector<Species> biosphere;
|
|
int history_best_w;
|
|
|
|
auto frecord = std::fopen("current.txt", "r");
|
|
loadBiosphere(biosphere, history_best_w, frecord);
|
|
std::fclose(frecord);
|
|
|
|
std::uniform_real_distribution<float> d01(0, 1);
|
|
int new_record_counter = 0;
|
|
for (int iter_id = 1; ; ++iter_id) {
|
|
int best_w = 0;
|
|
|
|
# pragma omp parallel for num_threads(8)
|
|
for (auto &s : biosphere)
|
|
inherit(s);
|
|
|
|
for (auto &s : biosphere)
|
|
best_w = std::max(best_w, s.best_w);
|
|
|
|
std::vector<Species> Hnxt, Lnxt;
|
|
for (auto &s : biosphere) {
|
|
if (s.stable_age < std::max(3, stable_limit * s.best_w / best_w)
|
|
|| s.best_w == best_w)
|
|
{
|
|
if (s.best_w < best_w || s.age < protect_age)
|
|
Hnxt.push_back(std::move(s));
|
|
else
|
|
Lnxt.push_back(std::move(s));
|
|
}
|
|
}
|
|
biosphere.clear();
|
|
if (Lnxt.size()) {
|
|
biosphere.push_back(std::move(*std::max_element(Lnxt.begin(), Lnxt.end())));
|
|
}
|
|
|
|
while (Hnxt.size() + biosphere.size() < species_limit) {
|
|
if (biosphere.size()) {
|
|
Hnxt.push_back(mutation(biosphere[0]));
|
|
} else {
|
|
int sc = std::uniform_int_distribution<int>{0, (int)Hnxt.size() - 1}(rng);
|
|
Hnxt.push_back(mutation(Hnxt[sc]));
|
|
}
|
|
}
|
|
|
|
for (auto &s : Hnxt)
|
|
biosphere.push_back(std::move(s));
|
|
Hnxt.clear();
|
|
|
|
for (auto &s : biosphere)
|
|
s.tick();
|
|
|
|
bool broke_record = best_w > history_best_w;
|
|
if (broke_record) {
|
|
history_best_w = best_w;
|
|
new_record_counter = 5;
|
|
std::FILE *f = fopen("best.txt", "w");
|
|
writeBiosphere(biosphere, best_w, f);
|
|
fclose(f);
|
|
}
|
|
|
|
if (true) {
|
|
term::clear();
|
|
printf("Iterated for %d times.\n", iter_id);
|
|
printf("Current Best=%d, History Best=%d, Species:\n", best_w, history_best_w);
|
|
for (auto &s : biosphere) {
|
|
if (s.best_w == best_w)
|
|
term::setColor(35); // purple
|
|
else if (s.stable_age > 5)
|
|
term::setColor(31); // red
|
|
else if (s.age <= 2)
|
|
term::setColor(34); // blue
|
|
else if (s.best_w >= best_w * 95 / 100)
|
|
term::setColor(32); // green
|
|
|
|
printf(" - %s[%d]: best=%d\tstable=%d\tage=%d.\n", s.name
|
|
, s.tactic, s.best_w, s.stable_age, s.age);
|
|
term::setColor(0);
|
|
}
|
|
|
|
if (new_record_counter > 0) {
|
|
term::setColor(32);
|
|
printf("New Record: %d\n", best_w);
|
|
term::setColor(0);
|
|
new_record_counter -= 1;
|
|
}
|
|
fflush(stdout);
|
|
|
|
std::FILE *f = fopen("current.txt", "w");
|
|
writeBiosphere(biosphere, best_w, f);
|
|
fclose(f);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|