// In our world, you cannot tell whether you are moving or not. // In the world of HyperRogue, // 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 // Creating a roguelike in 2+1D anti-de Sitter spacetime seems hard, so let's start with a cellular automaton. The rules are like Conway's Game of life, except that hex tile are born at 2 neighbors and overcrowded at 3. (1/5) // ./ht srange=7 -shothud 1 -csc .4 -rotspace -canvas-random 10 -prodperiod 0 -ads-ca-check -ads-ca-view -noscr simspeed=3 -ads-keys -ads-keylist xaxwxsxdx -ads-keylist 0 -zoom .95 -shot-1000 -animvideo 1800 ads-ca.mp4 // However, it takes 4 iterations for neighbors (brown) to affect a cell (red). (Information cannot travel faster than c -- with <4 iterations cells would depend on their own future.) The blue 'plague' shows which cells are affected by the original source. (2/5) // ./ht plague=221 srange=7 -shothud 1 -csc .4 -rotspace -canvas-random 10 -prodperiod 0 -ads-ca-check -ads-ca-view -noscr simspeed=3 -ads-keys -ads-keylist xaxwxsxdx -ads-keylist 0 -zoom .95 -shot-1000 -ads-highlight -animvideo 1800 ads-ca-plague.mp4 -exit // Here is how the situation changes when we don't just sit on a star and try to travel. In this viz we see the situation "at the current time" (of course IRL we would see the past states). (It is more fun to play with when you can control it manually, of course.) (3/5) // (Why would we want a roguelike in adS spacetime? In our world we do not feel whether we are moving on not; in the world of HyperRogue, this is not the case. https://twitter.com/ZenoRogue/status/1418957609269440512) (4/5) // (This why in HyperRogue we have long but narrow snakes, and ivies which grow, but nothing wider than a Kraken could move. This is because in HyperRogue time is added in in Euclidean way; anti-de Sitter spacetime adds time to the hyperbolic plane in a "better" way.) (5/5) #include "../rogueviz/rogueviz.h" namespace hr { namespace ads { shiftpoint kz(shiftpoint x) { x.h = hr::kz(x.h); x.shift = hr::kz(x.shift); return x; } shiftmatrix kz(shiftmatrix x) { x.T = hr::kz(x.T); x.shift = hr::kz(x.shift); return x; } vector hs; int sq = 1; ld sqf = sq; int lim = 10; struct star { hyperpoint a; hyperpoint b; color_t col; }; vector 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 g(geometry, gCubeTiling); map>> 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 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 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 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 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 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> > causality; map cell_id; set cs; int ms = 4; int srange = 7; map 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; icmove(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; itype-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 noncausal; bool randlive() { return hrand(100) < 20 ? 1 : 0; } vector compute_order; map index; void fine() { compute_order.clear(); index.clear(); set unknown; for(auto& p: causality) unknown.insert(p.first); while(true) { vector 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>> 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 unknown_neighbors(ids); for(auto& p: compute_order) unknown_neighbors[index[p]] = p->type - isize(relative[index[p]]); vector noncausal_indexed(ids); for(auto& p: compute_order) noncausal_indexed.push_back(noncausal[p]); vector> ca_states; println(hlog, "generating initial random"); for(int t=0; t lifecounts; int steps = 50; for(int t=ms; t 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; itype); // 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 g(geometry, gNormal); dynamicval p(pmodel, mdDisk); check_cgi(); initquickqueue(); for(int i=0; i 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); } }