pushbox-quest/optimizer/box-eater.cpp
2024-01-18 20:41:33 +08:00

360 lines
8.6 KiB
C++

#include <bits/stdc++.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 float temperature_initial = 60;
const float temperature_low = 10;
const float temperature_delta = .97;
const int species_limit = 5;
const int population_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;
float T, eT;
char name[SPECIES_NAME_LEN + 1];
int age, tactic;
Species() : population(), best_w(-1), T(1.), eT(-1), name{"NULL"}, age(0) {}
int size() const {
return population.size();
}
bool empty() const {
return population.empty();
}
bool operator<(const Species &o) const {
return eT < o.eT;
}
};
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 %.12f %s %d %d\n", s.size(), s.best_w, s.T, s.name, 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 %f %s %d %d", &n, &res.best_w, &res.T, res.name, &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');
}
}
static 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 mutationLineCl(Species &res, const Gene &sc) {
Gene ac = sc;
int r = rng0N(rng);
for (int i = 0; i < N; ++i) {
ac.m.set(r, i, 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) {
Gene ac = sc;
int c = rng0N(rng);
for (int i = 0; i < N; ++i) {
ac.m.set(i, c, 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;
int cx = rng0N(rng), cy = rng0N(rng);
int sz2 = sz / 2;
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, 6);
Species res;
randomName(SPECIES_NAME_LEN, res.name);
res.T = temperature_initial;
do {
int tactic = rngTacs(rng);
res.tactic = tactic;
switch (tactic) {
case 0:
mutationRandomHole(res, sc.population[0], 60);
break;
case 1:
mutationRandomHole(res, sc.population[0], 40);
break;
case 2:
mutationReselectPOI(res, sc.population[0]);
break;
case 3:
mutationLineCl(res, sc.population[0]);
break;
case 4:
mutationColCl(res, sc.population[0]);
break;
case 5:
mutationSqrCl(res, sc.population[0], 3);
mutationSqrCl(res, sc.population[0], 3);
break;
case 6:
mutationSqrCl(res, sc.population[0], 5);
break;
}
} while (res.size() == 0);
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();
for (const auto &g : nxt) {
s.best_w = std::max(s.best_w, g.w);
}
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);
}
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);
for (int iter_id = 1; ; ++iter_id) {
int best_w = 0, hbest_w = 0;
for (auto &s : biosphere) {
inherit(s);
best_w = std::max(best_w, s.best_w);
if (s.T > temperature_low)
hbest_w = std::max(hbest_w, s.best_w);
}
std::vector<Species> Hnxt, Lnxt;
for (auto &s : biosphere) {
const bool is_ht = s.T > temperature_low;
const float dE = (is_ht ? hbest_w : best_w) - s.best_w;
s.eT = exp(-dE / s.T);
if (s.eT >= d01(rng) || s.age <= 15) {
if (is_ht)
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) {
s.age += 1;
s.T = std::max(s.T * temperature_delta, temperature_low);
biosphere.push_back(std::move(s));
}
Hnxt.clear();
bool broke_record = best_w > history_best_w;
if (broke_record) {
history_best_w = best_w;
printf("New Record: %d!\n", best_w);
std::FILE *f = fopen("best.txt", "w");
writeBiosphere(biosphere, best_w, f);
fclose(f);
}
if (true) {
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) {
printf(" - %s[%d]: best=%d T=%.3f age=%d.\n", s.name, s.tactic, s.best_w, s.T, s.age);
}
std::FILE *f = fopen("current.txt", "w");
writeBiosphere(biosphere, best_w, f);
fclose(f);
}
}
return 0;
}