mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-12-24 01:00:25 +00:00
434 lines
12 KiB
C++
434 lines
12 KiB
C++
|
// https://www.quora.com/Are-there-variations-of-Conways-Game-of-Life-based-upon-a-triangular-or-hexagonal-grid
|
||
|
// https://arunarjunakani.github.io/HexagonalGameOfLife/
|
||
|
// B2 S34
|
||
|
|
||
|
#include "../rogueviz/rogueviz.h"
|
||
|
|
||
|
namespace hr {
|
||
|
|
||
|
namespace ads {
|
||
|
|
||
|
vector<hyperpoint> hs;
|
||
|
|
||
|
int sq = 1;
|
||
|
ld sqf = sq;
|
||
|
|
||
|
int lim = 10;
|
||
|
|
||
|
struct star {
|
||
|
hyperpoint a;
|
||
|
hyperpoint b;
|
||
|
color_t col;
|
||
|
};
|
||
|
|
||
|
vector<star> stars;
|
||
|
|
||
|
bool ord(hyperpoint a, hyperpoint b) {
|
||
|
for(int i=0; i<4; i++) if(a[i] != b[i]) return a[i] > b[i];
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void gen_get() {
|
||
|
for(auto g: gmatrix) hs.push_back(unshift(g.second*C0));
|
||
|
}
|
||
|
|
||
|
void gen_rational() {
|
||
|
dynamicval<eGeometry> g(geometry, gCubeTiling);
|
||
|
|
||
|
map<int, vector<pair<int, int>>> roots;
|
||
|
for(int t=-lim; t<=lim; t++)
|
||
|
for(int u=-lim; u<=lim; u++) roots[t*t+u*u].emplace_back(t, u);
|
||
|
|
||
|
for(int x=-lim; x<=lim; x++)
|
||
|
for(int y=-lim; y<=lim; y++)
|
||
|
/* for(int u=-lim; u<=lim; u++)
|
||
|
if(t*t+u*u == x*x+y*y+sq*sq) */
|
||
|
for(auto [t, u]: roots[x*x+y*y+sq*sq])
|
||
|
hs.push_back(hyperpoint(x/sqf, y/sqf, t/sqf, u/sqf));
|
||
|
println(hlog, "hs size = ", isize(hs));
|
||
|
}
|
||
|
|
||
|
void gen_stars() {
|
||
|
map<hyperpoint, color_t> cols;
|
||
|
for(auto h: hs) cols[h] = 0x000000FF | (hrand(0x1000000) << 8);
|
||
|
|
||
|
for(auto h1: hs) if(ord(h1, Hypc))
|
||
|
for(auto h2: hs) if(ord(h2, Hypc)) {
|
||
|
hyperpoint h = h1 - h2;
|
||
|
if(h[0]*h[0]+h[1]*h[1]-h[2]*h[2]-h[3]*h[3] == -2) if(hrand(100) < 100) {
|
||
|
stars.emplace_back(star{h1, h2, gradient(cols[h1], cols[h2], 0, .5, 1)});
|
||
|
|
||
|
hyperpoint hm = h1 * h2[3] - h2 * h1[3];
|
||
|
ld d = hypot(h2[3], h1[3]);
|
||
|
if(d == 0) continue;
|
||
|
hm /= d;
|
||
|
if(hm[2] < 0) hm = -hm;
|
||
|
// println(hlog, hm);
|
||
|
if(abs(hm[0]) < .1 && abs(hm[1]) < .1 && abs(hm[2]-1) < .1 && abs(hm[3]) < .1)
|
||
|
println(hlog, h1, " & ", h2, " -> ", hm);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
println(hlog, "stars size = ", isize(stars));
|
||
|
}
|
||
|
|
||
|
void init_ads() {
|
||
|
}
|
||
|
|
||
|
color_t color_of(ld z) {
|
||
|
z *= 2;
|
||
|
if(z < -1) return 0;
|
||
|
if(z > 1) return 0;
|
||
|
if(z > 0) return gradient(0xFFFFFFFF, 0xFF000000, 0, z, 1);
|
||
|
return gradient(0xFFFFFFFF, 0xFF00, 0, z, -1);
|
||
|
}
|
||
|
|
||
|
transmatrix lorentz(int a, int b, ld v) {
|
||
|
transmatrix T = Id;
|
||
|
T[a][a] = T[b][b] = cosh(v);
|
||
|
T[a][b] = T[b][a] = sinh(v);
|
||
|
return T;
|
||
|
}
|
||
|
|
||
|
transmatrix current = Id;
|
||
|
|
||
|
void draw_ads() {
|
||
|
ld t = ticks / anims::period * 2 * M_PI;
|
||
|
static ld last_t = t;
|
||
|
no_find_player = true;
|
||
|
|
||
|
if(1) {
|
||
|
dynamicval<eGeometry> g(geometry, gCubeTiling);
|
||
|
current = cspin(2, 3, t - last_t) * current;
|
||
|
}
|
||
|
last_t = t;
|
||
|
|
||
|
if(stars.empty()) for(hyperpoint h: hs) {
|
||
|
hyperpoint h1;
|
||
|
if(1) {
|
||
|
dynamicval<eGeometry> g(geometry, gCubeTiling);
|
||
|
h1 = current * h;
|
||
|
}
|
||
|
hyperpoint h2 = h1; h2[2] = sqrt(h2[2] * h2[2] + h2[3] * h2[3]);
|
||
|
if(h1[2] < 0) continue;
|
||
|
color_t c = color_of(h1[3]);
|
||
|
if(c)
|
||
|
queuepoly(shiftless(rgpushxto0(h2)), cgi.shGem[0], c);
|
||
|
}
|
||
|
|
||
|
for(auto st: stars) {
|
||
|
|
||
|
hyperpoint hm;
|
||
|
|
||
|
if(1) {
|
||
|
dynamicval<eGeometry> g(geometry, gCubeTiling);
|
||
|
hyperpoint h1 = current * st.a;
|
||
|
hyperpoint h2 = current * st.b;
|
||
|
hm = h1 * h2[3] - h2 * h1[3];
|
||
|
ld d = hypot(h2[3], h1[3]);
|
||
|
if(d == 0) continue;
|
||
|
hm /= d;
|
||
|
if(hm[2] < 0) hm = -hm;
|
||
|
// ld ii = hm[2]*hm[2] - hm[0]*hm[0] - hm[1]*hm[1];
|
||
|
}
|
||
|
|
||
|
queuepoly(shiftless(rgpushxto0(hm)), cgi.shGem[0], st.col);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool handleKey(int sym, int uni) {
|
||
|
dynamicval<eGeometry> g(geometry, gCubeTiling);
|
||
|
if(uni == 'a') { current = lorentz(0, 3, +.1) * current; return true; }
|
||
|
if(uni == 'd') { current = lorentz(0, 3, -.1) * current; return true; }
|
||
|
if(uni == 'w') { current = lorentz(1, 3, +.1) * current; return true; }
|
||
|
if(uni == 's') { current = lorentz(1, 3, -.1) * current; return true; }
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void run_ads() {
|
||
|
|
||
|
rogueviz::rv_hook(hooks_frame, 100, draw_ads);
|
||
|
rogueviz::rv_hook(hooks_handleKey, 0, handleKey);
|
||
|
}
|
||
|
|
||
|
void auto_shift() {
|
||
|
current = spin(30*degree) * lorentz(0, 3, 1) * spin(-30*degree) * current;
|
||
|
}
|
||
|
|
||
|
map<cell*, vector<pair<cell*, int>> > causality;
|
||
|
|
||
|
map<cell*, int> cell_id;
|
||
|
|
||
|
set<cell*> cs;
|
||
|
|
||
|
int ms = 4;
|
||
|
int srange = 7;
|
||
|
|
||
|
map<cell*, shiftmatrix> where_matrix;
|
||
|
|
||
|
void check(cell *c, const shiftmatrix& S) {
|
||
|
auto p = hybrid::get_where(c);
|
||
|
if(causality.count(p.first)) return;
|
||
|
auto& caus = causality[p.first];
|
||
|
auto c1 = c;
|
||
|
shiftmatrix S1 = S;
|
||
|
for(int i=0; i<ms; i++) {
|
||
|
c1 = c1->cmove(c->type - 2);
|
||
|
S1 = S1 * currentmap->adj(c1, c->type-2);
|
||
|
}
|
||
|
|
||
|
where_matrix[p.first] = S;
|
||
|
|
||
|
hybrid::in_underlying_geometry([&] {
|
||
|
setdist(p.first, 4, nullptr);
|
||
|
p.first->land = laIce;
|
||
|
});
|
||
|
|
||
|
hybrid::test_at(p.first);
|
||
|
|
||
|
// time increases
|
||
|
auto p1 = hybrid::get_where(c1);
|
||
|
println(hlog, p.second, " -> ", p1.second);
|
||
|
for(int i=0; i<c->type-2; i++) {
|
||
|
cell *c2 = c1->cmove(i);
|
||
|
auto p2 = hybrid::get_where(c2);
|
||
|
bool ok = cs.count(p2.first);
|
||
|
hybrid::in_underlying_geometry([&] {
|
||
|
ok = celldist(p2.first) <= srange;
|
||
|
});
|
||
|
if(!ok) continue;
|
||
|
caus.emplace_back(p2.first, p.second - p2.second);
|
||
|
// (p,x) (p2,0) -> okay
|
||
|
// minimum p1 above p2
|
||
|
check(c2, S1 * currentmap->adj(c1, i));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
map<cell*, int> noncausal;
|
||
|
|
||
|
bool randlive() { return hrand(100) < 20 ? 1 : 0; }
|
||
|
|
||
|
vector<cell*> compute_order;
|
||
|
map<cell*, int> index;
|
||
|
|
||
|
void fine() {
|
||
|
compute_order.clear();
|
||
|
index.clear();
|
||
|
set<cell*> unknown;
|
||
|
for(auto& p: causality) unknown.insert(p.first);
|
||
|
while(true) {
|
||
|
vector<cell*> to_remove;
|
||
|
println(hlog, "unknown = ", isize(unknown));
|
||
|
if(unknown.empty()) break;
|
||
|
for(auto u: unknown) {
|
||
|
bool ok = true;
|
||
|
for(auto pp: causality[u]) {
|
||
|
int need = noncausal[u] + 1 - pp.second;
|
||
|
if(noncausal[pp.first] < need) ok = false;
|
||
|
}
|
||
|
if(ok) {
|
||
|
to_remove.push_back(u);
|
||
|
noncausal[u]++;
|
||
|
}
|
||
|
}
|
||
|
if(to_remove.empty()) { println(hlog, "failed to remove"); return; }
|
||
|
for(auto r: to_remove) unknown.erase(r), compute_order.push_back(r);
|
||
|
}
|
||
|
|
||
|
int ids = 0;
|
||
|
for(auto& p: compute_order) index[p] = ids++;
|
||
|
vector<vector<pair<int, int>>> relative(ids);
|
||
|
for(auto p: compute_order) {
|
||
|
int i = index[p];
|
||
|
for(auto& pp: causality[p]) {
|
||
|
int j = index[pp.first];
|
||
|
int k = noncausal[p] - pp.second - noncausal[pp.first];
|
||
|
relative[i].emplace_back(j, k);
|
||
|
}
|
||
|
}
|
||
|
println(hlog, relative);
|
||
|
|
||
|
vector<int> unknown_neighbors(ids);
|
||
|
for(auto& p: compute_order) unknown_neighbors[index[p]] = p->type - isize(relative[index[p]]);
|
||
|
|
||
|
vector<int> noncausal_indexed(ids);
|
||
|
for(auto& p: compute_order) noncausal_indexed.push_back(noncausal[p]);
|
||
|
|
||
|
vector<vector<int>> ca_states;
|
||
|
|
||
|
println(hlog, "generating initial random");
|
||
|
for(int t=0; t<ms; t++) {
|
||
|
ca_states.emplace_back();
|
||
|
ca_states.back().resize(ids);
|
||
|
for(int j=0; j<ids; j++) ca_states[t][j] = randlive();
|
||
|
}
|
||
|
|
||
|
vector<int> lifecounts;
|
||
|
|
||
|
int steps = 50;
|
||
|
for(int t=ms; t<steps; t++) {
|
||
|
println(hlog, "generating step t=", t);
|
||
|
ca_states.emplace_back();
|
||
|
ca_states.back().resize(ids);
|
||
|
int countlife = 0;
|
||
|
for(int j=0; j<ids; j++) {
|
||
|
int lives = 0;
|
||
|
bool early = false;
|
||
|
for(auto [id, d]: relative[j]) {
|
||
|
if(t+d < 0) early = true;
|
||
|
else lives += ca_states[t+d][id];
|
||
|
}
|
||
|
if(early) {
|
||
|
ca_states[t][j] = randlive();
|
||
|
continue;
|
||
|
}
|
||
|
for(int i=0; i<unknown_neighbors[j]; i++) lives += randlive();
|
||
|
int old = ca_states[t-ms][j];
|
||
|
int newstate;
|
||
|
if(old == 0) newstate = lives == 2 ? 1 : 0;
|
||
|
if(old == 1) newstate = (lives == 3 || lives == 4) ? 1 : 0;
|
||
|
ca_states[t][j] = newstate;
|
||
|
countlife += newstate;
|
||
|
}
|
||
|
lifecounts.push_back(countlife);
|
||
|
}
|
||
|
|
||
|
for(int t=0; t<steps; t++)
|
||
|
for(int j=0; j<ids; j++) {
|
||
|
cell *h = hybrid::get_at(compute_order[j], noncausal_indexed[j] + t);
|
||
|
setdist(h, 7, nullptr);
|
||
|
if(t < ms) h->land = laIce;
|
||
|
else if(t < 2*ms) h->land = laJungle;
|
||
|
else h->land = laAlchemist;
|
||
|
h->wall = ca_states[t][j] ? waPlatform : waNone;
|
||
|
}
|
||
|
|
||
|
println(hlog, lifecounts);
|
||
|
}
|
||
|
|
||
|
void reset_to_underlying(cell *where) {
|
||
|
hrmap *m;
|
||
|
hybrid::in_underlying_geometry([&] { m = currentmap; });
|
||
|
currentmap = m;
|
||
|
geometry = hybrid::underlying;
|
||
|
pmodel = mdDisk;
|
||
|
cwt = centerover = where; View = Id;
|
||
|
check_cgi();
|
||
|
resetview();
|
||
|
}
|
||
|
|
||
|
void ads_ca_check() {
|
||
|
|
||
|
auto pc = hybrid::get_where(cwt.at).first;
|
||
|
hybrid::in_underlying_geometry([&] {
|
||
|
cs.insert(pc);
|
||
|
cs.insert(pc->cmove(0));
|
||
|
cs.insert(pc->cmove(1));
|
||
|
});
|
||
|
for(auto cc: cs)
|
||
|
println(hlog, "SET = ", cc);
|
||
|
|
||
|
check(cwt.at, shiftless(Id));
|
||
|
|
||
|
println(hlog, causality);
|
||
|
// at step t, for (c,i) in noncausal, change (c,i+t)
|
||
|
for(auto p: causality) noncausal[p.first] = 0;
|
||
|
vector<int> ncs;
|
||
|
map<cell*, cell*> source;
|
||
|
vector<int> changelist;
|
||
|
for(int it=0; it<1000; it++) {
|
||
|
int changes = 0;
|
||
|
for(auto p: causality)
|
||
|
for(auto pp: p.second) {
|
||
|
auto& here = noncausal[p.first];
|
||
|
auto cand = noncausal[pp.first] + pp.second;
|
||
|
if(cand < here) {
|
||
|
source[p.first] = pp.first;
|
||
|
here = cand;
|
||
|
changes++;
|
||
|
}
|
||
|
}
|
||
|
ncs.push_back(noncausal[pc]);
|
||
|
if(it == 998) println(hlog, noncausal);
|
||
|
if(it == 999) println(hlog, noncausal);
|
||
|
changelist.push_back(changes);
|
||
|
if(!changes) break;
|
||
|
}
|
||
|
println(hlog, ncs);
|
||
|
|
||
|
cell *x = pc;
|
||
|
for(int it=0; it<1000; it++) if(x) x = source[x];
|
||
|
if(!x) { println(hlog, "all fine!"); fine(); return; }
|
||
|
auto x1 = x;
|
||
|
vector<cell*> circ;
|
||
|
int dsum = 0;
|
||
|
while(true) {
|
||
|
x->item = itDiamond;
|
||
|
for(auto pp: causality[x]) if(pp.first == source[x]) {
|
||
|
dsum += pp.second - ms;
|
||
|
println(hlog, "delta = ", pp.second);
|
||
|
}
|
||
|
circ.push_back(x);
|
||
|
x = source[x];
|
||
|
if(x == x1) break;
|
||
|
}
|
||
|
int circum = isize(circ);
|
||
|
println(hlog, "circum = ", circum);
|
||
|
int anglesum = 0;
|
||
|
for(int i=0; i<circum; i++) {
|
||
|
int d1 = neighborId(circ[i], circ[gmod(i+1, circum)]);
|
||
|
int d2 = neighborId(circ[i], circ[gmod(i-1, circum)]);
|
||
|
int d = gmod(d2 - d1, circ[i]->type);
|
||
|
// println(hlog, "angle = ", d);
|
||
|
anglesum += d;
|
||
|
}
|
||
|
println(hlog, "anglesum = ", anglesum);
|
||
|
int area = 7 * (circum - 2) - 2 * anglesum;
|
||
|
println(hlog, "area = ", area);
|
||
|
println(hlog, "dsum = ", dsum);
|
||
|
|
||
|
reset_to_underlying(x1);
|
||
|
}
|
||
|
|
||
|
hyperpoint findflat(shiftpoint S0) {
|
||
|
hyperpoint h = S0.h;
|
||
|
ld at = atan2(h[3], h[2]);
|
||
|
h = cspin(2, 3, at) * cspin(0, 1, at) * h;
|
||
|
return h;
|
||
|
}
|
||
|
|
||
|
bool view_ads_ca() {
|
||
|
if(1) {
|
||
|
dynamicval<eGeometry> g(geometry, gNormal);
|
||
|
dynamicval<eModel> p(pmodel, mdDisk);
|
||
|
check_cgi();
|
||
|
initquickqueue();
|
||
|
for(int i=0; i<isize(compute_order); i++) {
|
||
|
hyperpoint h;
|
||
|
if(1) {
|
||
|
dynamicval<eGeometry> b(geometry, gCubeTiling);
|
||
|
cell *c = compute_order[i];
|
||
|
shiftmatrix S = where_matrix[c];
|
||
|
h = findflat(S * C0);
|
||
|
}
|
||
|
queuepoly(shiftless(rgpushxto0(h)), cgi.shGem[0], 0xFFD500FF);
|
||
|
}
|
||
|
quickqueue();
|
||
|
}
|
||
|
check_cgi();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void run_ads_ca_view() {
|
||
|
nomap = true;
|
||
|
no_find_player = true;
|
||
|
rogueviz::rv_hook(hooks_prestats, 100, view_ads_ca);
|
||
|
}
|
||
|
|
||
|
auto shot_hooks = arg::add3("-ads", run_ads) + arg::add3("-ads-rat", gen_rational) + arg::add3("-ads-stars", gen_stars) + arg::add3("-ads-get", gen_get) + arg::add3("-ads-shift", auto_shift)
|
||
|
+ arg::add3("-ads-ca-check", ads_ca_check) + arg::add3("-ads-ca-view", run_ads_ca_view);
|
||
|
|
||
|
}
|
||
|
}
|