From c21feb5d9a28d75515e2eb16b179b74b13b6b55c Mon Sep 17 00:00:00 2001 From: szdytom Date: Thu, 18 Jan 2024 20:41:16 +0800 Subject: [PATCH] add box-eater optimzier --- optimizer/box-eater.cpp | 359 ++++++++++++++++++++++++++++++++++++++++ optimizer/box-solver.h | 72 ++++++++ optimizer/pushbox.h | 92 ++++++++++ 3 files changed, 523 insertions(+) create mode 100644 optimizer/box-eater.cpp create mode 100644 optimizer/box-solver.h create mode 100644 optimizer/pushbox.h diff --git a/optimizer/box-eater.cpp b/optimizer/box-eater.cpp new file mode 100644 index 0000000..cb9e4a5 --- /dev/null +++ b/optimizer/box-eater.cpp @@ -0,0 +1,359 @@ +#include +#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 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, 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, 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 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 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 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 &res) { + int s = Solve::solve(q); + if (s != -1) + res.emplace_back(q, s); +} + +inline void inheritGene(Maze q, std::vector &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 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 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 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 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{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; +} diff --git a/optimizer/box-solver.h b/optimizer/box-solver.h new file mode 100644 index 0000000..497bf64 --- /dev/null +++ b/optimizer/box-solver.h @@ -0,0 +1,72 @@ +#pragma once +#include +#include +#include +#include +#include "pushbox.h" + +namespace Solve { +std::bitset mp[N]; +int dis[N][N][N][N]; +std::queue> qu; + +inline int bfs(int px, int py, int bx, int by, int tx, int ty) { + std::memset(dis, 127, sizeof(dis)); + while (!qu.empty()) + qu.pop(); + + auto expand = [&](int npx, int npy, int nbx, int nby, int d) { + if (npx < 0 || npx >= N) + return; + if (npy < 0 || npy >= N) + return; + if (nbx < 0 || nbx >= N) + return; + if (nby < 0 || nby >= N) + return; + if (mp[npx][npy] == 0) + return; + if (mp[nbx][nby] == 0) + return; + if (npx == nbx && npy == nby) + return; + + if (dis[npx][npy][nbx][nby] <= d) + return; + dis[npx][npy][nbx][nby] = d; + qu.emplace(npx, npy, nbx, nby); + }; + + expand(px, py, bx, by, 0); + + while (!qu.empty()) { + auto [npx, npy, nbx, nby] = qu.front(); + int d = dis[npx][npy][nbx][nby]; + qu.pop(); + + if (nbx == tx && nby == ty) + return d; + + expand(npx + 1, npy, nbx, nby, d + 1); + expand(npx - 1, npy, nbx, nby, d + 1); + expand(npx, npy + 1, nbx, nby, d + 1); + expand(npx, npy - 1, nbx, nby, d + 1); + if (npx + 1 == nbx && npy == nby) + expand(npx + 1, npy, nbx + 1, nby, d + 1); + if (npx - 1 == nbx && npy == nby) + expand(npx - 1, npy, nbx - 1, nby, d + 1); + if (npx == nbx && npy + 1 == nby) + expand(npx, npy + 1, nbx, nby + 1, d + 1); + if (npx == nbx && npy - 1 == nby) + expand(npx, npy - 1, nbx, nby - 1, d + 1); + } + return -1; +} + +inline int solve(Maze q) { + for (int i = 0; i < 20; ++i) + mp[i] = q.M[i]; + return bfs(q.poi[POI_PERSON][0], q.poi[POI_PERSON][1] + , q.poi[POI_BOX][0], q.poi[POI_BOX][1], q.poi[POI_TARGET][0], q.poi[POI_TARGET][1]); +} +}; // namespace Solve diff --git a/optimizer/pushbox.h b/optimizer/pushbox.h new file mode 100644 index 0000000..416d58e --- /dev/null +++ b/optimizer/pushbox.h @@ -0,0 +1,92 @@ +#pragma once +#include + +const int N = 20; + +enum { + POI_PERSON, + POI_BOX, + POI_TARGET, + POI_EXCEED, +}; + +enum { + SQR_WALL = 0, + SQR_SPACE = 1, + SQR_EXCEED = 2, +}; + +struct Maze { + std::bitset M[N]; + int poi[3][2]; + + int at(int x, int y) const { + return M[x][y]; + } + + void set(int x, int y, int v) { + M[x][y] = v; + } + + void flip(int x, int y) { + M[x].flip(y); + } + + bool isValid() const { + for (int i = 0; i < POI_EXCEED; ++i) { + for (int j : {0, 1}) { + if (poi[i][j] < 0 || poi[i][j] >= N) + return false; + } + + if (at(poi[i][0], poi[i][1]) == 0) + return false; + } + return true; + } +}; + +inline Maze loadMaze(std::FILE *f) { + char *s = new char[N + 5]; + Maze res; + for (int i = 0; i < N; ++i) { + std::fscanf(f, "%s", s); + for (int j = 0; j < N; ++j) { + res.set(i, j, s[j] != '#'); + switch (s[j]) { + case 'P': + res.poi[POI_PERSON][0] = i; + res.poi[POI_PERSON][1] = j; + break; + case '*': + res.poi[POI_BOX][0] = i; + res.poi[POI_BOX][1] = j; + break; + case 'O': + res.poi[POI_TARGET][0] = i; + res.poi[POI_TARGET][1] = j; + break; + } + } + } + delete[] s; + return res; +} + +inline void writeMaze(const Maze &m, std::FILE *f) { + for (int i = 0; i < N; ++i) { + for (int j = 0; j < N; ++j) { + if (i == m.poi[POI_PERSON][0] && j == m.poi[POI_PERSON][1]) + std::fputc('P', f); + else if (i == m.poi[POI_BOX][0] && j == m.poi[POI_BOX][1]) + std::fputc('*', f); + else if (i == m.poi[POI_TARGET][0] && j == m.poi[POI_TARGET][1]) + std::fputc('O', f); + else if (m.at(i, j) == 0) + std::fputc('#', f); + else + std::fputc('.', f); + } + std::fputc('\n', f); + } +} \ No newline at end of file