mirror of
https://github.com/zenorogue/hyperrogue.git
synced 2024-10-31 19:36:16 +00:00
1097 lines
35 KiB
C++
1097 lines
35 KiB
C++
// Hyperbolic Rogue -- regular honeycombs
|
|
// Copyright (C) 2011-2019 Zeno Rogue, see 'hyper.cpp' for details
|
|
|
|
/** \file reg3.cpp
|
|
* \brief regular honeycombs
|
|
*
|
|
* works with spherical and hyperbolic ones -- Euclidean cubic tiling implemented in euclid.cpp
|
|
* includes non-quotient spaces as well as field quotient and elliptic spaces
|
|
* hyperbolic honeycombs rely on binary:: to deal with floating point errors (just like archimedean)
|
|
*/
|
|
|
|
#include "hyper.h"
|
|
namespace hr {
|
|
#if MAXMDIM >= 4
|
|
|
|
namespace binary {
|
|
void build_tmatrix();
|
|
void virtualRebaseSimple(heptagon*& base, transmatrix& at);
|
|
int celldistance3(heptagon *c1, heptagon *c2);
|
|
hyperpoint deparabolic3(hyperpoint h);
|
|
}
|
|
|
|
EX namespace reg3 {
|
|
|
|
#if HDR
|
|
inline short& altdist(heptagon *h) { return h->emeraldval; }
|
|
#endif
|
|
|
|
map<int, int> close_distances;
|
|
|
|
EX int loop;
|
|
EX int face;
|
|
|
|
EX vector<hyperpoint> cellshape;
|
|
vector<hyperpoint> vertices_only;
|
|
|
|
EX transmatrix spins[12], adjmoves[12];
|
|
|
|
EX ld adjcheck;
|
|
EX ld strafedist;
|
|
EX bool dirs_adjacent[16][16];
|
|
|
|
template<class T> ld binsearch(ld dmin, ld dmax, const T& f) {
|
|
for(int i=0; i<200; i++) {
|
|
ld d = (dmin + dmax) / 2;
|
|
if(f(d)) dmax = d;
|
|
else dmin = d;
|
|
}
|
|
return dmin;
|
|
}
|
|
|
|
EX void generate() {
|
|
|
|
if(S7 == 4) face = 3;
|
|
if(S7 == 6) face = 4;
|
|
if(S7 == 12) face = 5;
|
|
if(S7 == 8) face = 3;
|
|
/* icosahedron not implemented */
|
|
loop = ginf[geometry].tiling_name[5] - '0';
|
|
DEBB(DF_GEOM, ("face = ", face, " loop = ", loop, " S7 = ", S7));
|
|
|
|
/* dual_angle : the angle between two face centers in the dual cell */
|
|
ld dual_angle = binsearch(0, M_PI, [&] (ld d) {
|
|
hyperpoint h0 = cpush(0, 1) * C0;
|
|
hyperpoint h1 = cspin(0, 1, d) * h0;
|
|
hyperpoint h2 = cspin(1, 2, 2*M_PI/loop) * h1;
|
|
return hdist(h0, h1) > hdist(h1, h2);
|
|
});
|
|
|
|
/* angle_between_faces : the distance between two face centers of cells */
|
|
ld angle_between_faces = binsearch(0, M_PI, [&] (ld d) {
|
|
hyperpoint h0 = cpush(0, 1) * C0;
|
|
hyperpoint h1 = cspin(0, 1, d) * h0;
|
|
hyperpoint h2 = cspin(1, 2, 2*M_PI/face) * h1;
|
|
return hdist(h0, h1) > hdist(h1, h2);
|
|
});
|
|
|
|
if(S7 == 8) {
|
|
angle_between_faces = min(angle_between_faces, M_PI - angle_between_faces);
|
|
/* 24-cell is a special case because it is the only one with '4' in the middle of the Schlaefli symbol. */
|
|
/* The computations above assume 3 */
|
|
hyperpoint h1 = hpxy3(.5,.5,.5);
|
|
hyperpoint h2 = hpxy3(.5,.5,-.5);
|
|
dual_angle = hdist(h1, h2);
|
|
}
|
|
|
|
DEBB(DF_GEOM, ("angle between faces = ", angle_between_faces));
|
|
DEBB(DF_GEOM, ("dual angle = ", dual_angle));
|
|
|
|
ld inp_length = binsearch(0, 1.55, [&] (ld d) {
|
|
hyperpoint h = xpush(-d) * spin(2*M_PI/face) * xpush0(d);
|
|
ld alpha = M_PI - atan2(-h[1], h[0]);
|
|
return (alpha < dual_angle / 2) ? hyperbolic : sphere;
|
|
});
|
|
|
|
DEBB(DF_GEOM, ("inp length = ", inp_length));
|
|
|
|
ld edge_length = hdist(xpush0(inp_length), spin(2*M_PI/face) * xpush0(inp_length));
|
|
if(S7 == 8) edge_length = hdist(normalize(hpxyz3(1,1,0,0)), normalize(hpxyz3(1,0,1,0)));
|
|
DEBB(DF_GEOM, ("edge length = ", edge_length));
|
|
|
|
/* frontal face direction */
|
|
hyperpoint h0 = xtangent(1);
|
|
|
|
/* three faces adjacent to frontal face direction */
|
|
hyperpoint h1 = cspin(0, 1, angle_between_faces) * h0;
|
|
hyperpoint h2 = cspin(1, 2, 2*M_PI/face) * h1;
|
|
hyperpoint h3 = cspin(1, 2, -2*M_PI/face) * h1;
|
|
|
|
/* directions of vertices [h0,h1,h2] and [h0,h1,h3] */
|
|
hyperpoint dir_v2 = S7 == 8 ? (h1 + h2) : (h0 + h1 + h2);
|
|
hyperpoint dir_v3 = S7 == 8 ? (h1 + h3) : (h0 + h1 + h3);
|
|
|
|
DEBB(DF_GEOM, ("dir_v2 = ", dir_v2));
|
|
DEBB(DF_GEOM, ("dir_v3 = ", dir_v3));
|
|
|
|
dir_v2 = tangent_length(dir_v2, 1);
|
|
dir_v3 = tangent_length(dir_v3, 1);
|
|
|
|
DEBB(DF_GEOM, ("S7 = ", S7));
|
|
DEBB(DF_GEOM, ("dir_v2 = ", dir_v2));
|
|
DEBB(DF_GEOM, ("dir_v3 = ", dir_v3));
|
|
|
|
/* the distance from cell center to cell vertex */
|
|
ld vertex_distance;
|
|
|
|
if(cgflags & qIDEAL) {
|
|
vertex_distance = 13;
|
|
}
|
|
|
|
else {
|
|
vertex_distance = binsearch(0, M_PI, [&] (ld d) {
|
|
// sometimes breaks in elliptic
|
|
dynamicval<eGeometry> g(geometry, elliptic ? gCell120 : geometry);
|
|
hyperpoint v2 = direct_exp(dir_v2 * d, iTable);
|
|
hyperpoint v3 = direct_exp(dir_v3 * d, iTable);
|
|
return hdist(v2, v3) >= edge_length;
|
|
});
|
|
}
|
|
|
|
DEBB(DF_GEOM, ("vertex_distance = ", vertex_distance));
|
|
|
|
/* actual vertex */
|
|
hyperpoint v2 = direct_exp(dir_v2 * vertex_distance, iTable);
|
|
|
|
hyperpoint mid = Hypc;
|
|
for(int i=0; i<face; i++) mid += cspin(1, 2, 2*i*M_PI/face) * v2;
|
|
mid = normalize(mid);
|
|
ld between_centers = 2 * hdist0(mid);
|
|
DEBB(DF_GEOM, ("between_centers = ", between_centers));
|
|
|
|
if(S7 == 12 || S7 == 8) {
|
|
spins[0] = Id;
|
|
spins[1] = cspin(0, 1, angle_between_faces) * cspin(1, 2, M_PI);
|
|
for(int a=2; a<face+1; a++) spins[a] = cspin(1, 2, 2*M_PI*(a-1)/face) * spins[1];
|
|
for(int a=S7/2; a<S7; a++) spins[a] = cspin(0, 1, M_PI) * spins[a-S7/2];
|
|
if(S7 == 8) swap(spins[6], spins[7]);
|
|
if(S7 == 12) swap(spins[8], spins[11]);
|
|
if(S7 == 12) swap(spins[9], spins[10]);
|
|
}
|
|
|
|
if(S7 == 6) {
|
|
spins[0] = Id;
|
|
spins[1] = cspin(0, 1, angle_between_faces) * cspin(1, 2, M_PI);
|
|
spins[2] = cspin(1, 2, M_PI/2) * spins[1];
|
|
for(int a=S7/2; a<S7; a++) spins[a] = spins[a-S7/2] * cspin(0, 1, M_PI);
|
|
}
|
|
|
|
if(S7 == 4) {
|
|
spins[0] = Id;
|
|
spins[1] = cspin(0, 1, angle_between_faces) * cspin(1, 2, M_PI);
|
|
for(int a=2; a<face+1; a++) spins[a] = cspin(1, 2, 2*M_PI*(a-1)/face) * spins[1];
|
|
}
|
|
|
|
cellshape.clear();
|
|
for(int a=0; a<S7; a++)
|
|
for(int b=0; b<face; b++)
|
|
cellshape.push_back(spins[a] * cspin(1, 2, 2*M_PI*b/face) * v2);
|
|
|
|
adjmoves[0] = cpush(0, between_centers) * cspin(0, 2, M_PI);
|
|
for(int i=1; i<S7; i++) adjmoves[i] = spins[i] * adjmoves[0];
|
|
|
|
for(int a=0; a<S7; a++)
|
|
DEBB(DF_GEOM, ("center of ", a, " is ", tC0(adjmoves[a])));
|
|
|
|
DEBB(DF_GEOM, ("doublemove = ", tC0(adjmoves[0] * adjmoves[0])));
|
|
|
|
adjcheck = hdist(tC0(adjmoves[0]), tC0(adjmoves[1])) * 1.0001;
|
|
|
|
int numedges = 0;
|
|
for(int a=0; a<S7; a++) for(int b=0; b<S7; b++) {
|
|
dirs_adjacent[a][b] = a != b && hdist(tC0(adjmoves[a]), tC0(adjmoves[b])) < adjcheck;
|
|
if(dirs_adjacent[a][b]) numedges++;
|
|
}
|
|
DEBB(DF_GEOM, ("numedges = ", numedges));
|
|
|
|
if(loop == 4) strafedist = adjcheck;
|
|
else strafedist = hdist(adjmoves[0] * C0, adjmoves[1] * C0);
|
|
|
|
vertices_only.clear();
|
|
for(hyperpoint h: cellshape) {
|
|
bool found = false;
|
|
for(hyperpoint h2: vertices_only) if(hdist(h, h2) < 1e-6) found = true;
|
|
if(!found) vertices_only.push_back(h);
|
|
}
|
|
}
|
|
|
|
void binary_rebase(heptagon *h, const transmatrix& V) {
|
|
}
|
|
|
|
void test();
|
|
|
|
struct hrmap_quotient3 : hrmap {
|
|
vector<heptagon*> allh;
|
|
vector<vector<transmatrix>> tmatrices;
|
|
|
|
transmatrix adj(heptagon *h, int d) { return tmatrices[h->fieldval][d]; }
|
|
transmatrix adj(cell *c, int d) override { return adj(c->master, d); }
|
|
};
|
|
|
|
int encode_coord(const crystal::coord& co) {
|
|
int c = 0;
|
|
for(int i=0; i<4; i++) c |= ((co[i]>>1) & 3) << (2*i);
|
|
return c;
|
|
}
|
|
|
|
EX crystal::coord decode_coord(int a) {
|
|
crystal::coord co;
|
|
for(int i=0; i<4; i++) co[i] = (a & 3) * 2, a >>= 2;
|
|
return co;
|
|
}
|
|
|
|
struct hrmap_from_crystal : hrmap_quotient3 {
|
|
|
|
hrmap_from_crystal() {
|
|
generate();
|
|
allh.resize(256);
|
|
tmatrices.resize(256);
|
|
for(int a=0; a<256; a++) {
|
|
allh[a] = tailored_alloc<heptagon> (S7);
|
|
allh[a]->c7 = newCell(S7, allh[a]);
|
|
allh[a]->fieldval = a;
|
|
allh[a]->zebraval = 0;
|
|
allh[a]->alt = NULL;
|
|
}
|
|
if(1) {
|
|
auto m = crystal::new_map();
|
|
dynamicval<hrmap*> cm(currentmap, m);
|
|
for(int a=0; a<256; a++) {
|
|
auto co = decode_coord(a);
|
|
heptagon *h1 = get_heptagon_at(co);
|
|
for(int d=0; d<8; d++) {
|
|
int b = encode_coord(crystal::get_coord(h1->cmove(d)));
|
|
allh[a]->c.connect(d, allh[b], h1->c.spin(d), false);
|
|
tmatrices[a].push_back(crystal::get_adj(h1, d));
|
|
}
|
|
}
|
|
delete m;
|
|
}
|
|
}
|
|
};
|
|
|
|
struct hrmap_field3 : hrmap_quotient3 {
|
|
vector<cell*> acells;
|
|
|
|
int mgmul(std::initializer_list<int> v) {
|
|
int a = 0;
|
|
for(int b: v) a = a ? currfp_gmul(a, b) : b;
|
|
return a;
|
|
}
|
|
|
|
vector<transmatrix> fullmatrices;
|
|
|
|
int P, R, X;
|
|
transmatrix full_P, full_R, full_X;
|
|
|
|
vector<int> field_adjmoves;
|
|
vector<int> cyclers;
|
|
int perm_group;
|
|
|
|
vector<int> cell_to_code;
|
|
vector<int> code_to_cell;
|
|
|
|
void seek(set<int>& seen_matrices, set<int>& seen_codes, const transmatrix& at, int ccode, const hyperpoint checker) {
|
|
if(hdist0(tC0(at)) > 4) return;
|
|
int b = bucketer(tC0(at));
|
|
if(seen_matrices.count(b)) return;
|
|
seen_matrices.insert(b);
|
|
for(int a=0; a<perm_group; a++) {
|
|
transmatrix T = at * fullmatrices[a];
|
|
if(hdist(T * checker, checker) < 1e-2) {
|
|
int co = mgmul({ccode, a});
|
|
seen_codes.insert(co);
|
|
fullmatrices[co] = T;
|
|
}
|
|
}
|
|
for(int a=0; a<perm_group; a++) seek(seen_matrices, seen_codes, at * fullmatrices[a] * full_P, mgmul({ccode, a, P}), checker);
|
|
}
|
|
|
|
hrmap_field3() {
|
|
eGeometry g = geometry;
|
|
geometry = gSpace435;
|
|
reg3::generate();
|
|
R = currfp_get_R();
|
|
P = currfp_get_P();
|
|
X = currfp_get_X();
|
|
full_P = reg3::adjmoves[0] * cspin(0, 2, M_PI) * cspin(0, 1, M_PI);
|
|
full_R = spin(-2 * M_PI / 4);
|
|
full_X = cspin(1, 2, M_PI / 2);
|
|
|
|
DEBB(DF_FIELD, ("full_P = ", full_P, " / ", R));
|
|
DEBB(DF_FIELD, ("full_R = ", full_R, " / ", P));
|
|
DEBB(DF_FIELD, ("full_X = ", full_X, " / ", X));
|
|
|
|
int N = currfp_n();
|
|
|
|
perm_group = 24;
|
|
fullmatrices.resize(N);
|
|
fullmatrices[0] = Id;
|
|
vector<bool> known(perm_group, false);
|
|
known[0] = true;
|
|
for(int a=0; a<perm_group; a++)
|
|
for(int i=0; i<perm_group; i++) if(known[i]) {
|
|
int iR = currfp_gmul(i, R);
|
|
fullmatrices[iR] = fullmatrices[i] * full_R;
|
|
known[iR] = true;
|
|
int iX = currfp_gmul(i, X);
|
|
fullmatrices[iX] = fullmatrices[i] * full_X;
|
|
known[iX] = true;
|
|
}
|
|
for(int i=0; i<perm_group; i++) if(known[i]) {
|
|
DEBB(DF_FIELD, (i, ". ", fullmatrices[i]));
|
|
}
|
|
|
|
// find cav such that:
|
|
// cav * Id * C0 = corner0
|
|
// cav * adjmoves[0] * C0 = corner1
|
|
// cav * adjmoves[1] * C0 = corner3
|
|
// cav * adjmoves[2] * C0 = cornerx
|
|
|
|
hyperpoint corner0 = reg3::cellshape[0];
|
|
hyperpoint corner1 = reg3::cellshape[1];
|
|
hyperpoint corner3 = reg3::cellshape[3];
|
|
hyperpoint cornerx;
|
|
|
|
for(hyperpoint h: reg3::cellshape) DEBB(DF_FIELD, ("some corner ", h));
|
|
|
|
for(hyperpoint h: reg3::cellshape)
|
|
if(hdist(h, corner1) > .1 && hdist(h, corner3) > .1 && abs(hdist(h, corner0)-hdist(corner0, corner1)) < .1)
|
|
cornerx = h;
|
|
DEBB(DF_FIELD, ("corner0 = ", corner0));
|
|
DEBB(DF_FIELD, ("corner1 = ", corner1));
|
|
DEBB(DF_FIELD, ("corner3 = ", corner3));
|
|
DEBB(DF_FIELD, ("cornerx = ", cornerx));
|
|
|
|
transmatrix adj = Id, iadj = Id;
|
|
|
|
geometry = g;
|
|
reg3::generate();
|
|
|
|
cyclers.clear();
|
|
DEBB(DF_FIELD, ("S7 = ", S7));
|
|
if(S7 == 12) {
|
|
|
|
transmatrix resmatrix;
|
|
set_column(resmatrix, 0, corner0);
|
|
set_column(resmatrix, 1, corner1);
|
|
set_column(resmatrix, 2, corner3);
|
|
set_column(resmatrix, 3, cornerx);
|
|
|
|
transmatrix transformer;
|
|
set_column(transformer, 0, C0);
|
|
set_column(transformer, 1, tC0(reg3::adjmoves[0]));
|
|
set_column(transformer, 2, tC0(reg3::adjmoves[1]));
|
|
set_column(transformer, 3, tC0(reg3::adjmoves[2]));
|
|
|
|
transmatrix cav = resmatrix * inverse(transformer);
|
|
DEBB(DF_FIELD, ("cav = ", cav));
|
|
DEBB(DF_FIELD, ("cav * C0 = ", cav * C0));
|
|
|
|
set<int> seen_matrices;
|
|
set<int> seen_codes;
|
|
seek(seen_matrices, seen_codes, Id, 0, corner0);
|
|
|
|
for(int x: seen_codes) cyclers.push_back(x);
|
|
perm_group = isize(cyclers);
|
|
adj = cav;
|
|
iadj = inverse(cav);
|
|
}
|
|
else {
|
|
for(int i=0; i<perm_group; i++) cyclers.push_back(i);
|
|
}
|
|
|
|
field_adjmoves.resize(S7);
|
|
for(int i=0; i<S7; i++) field_adjmoves[i] = -1;
|
|
|
|
for(int i=0; i<S7; i++)
|
|
for(int a: cyclers)
|
|
for(int b: cyclers) {
|
|
transmatrix T = iadj * fullmatrices[a] * full_P * fullmatrices[b] * adj;
|
|
if(eqmatrix(T, reg3::adjmoves[i])) {
|
|
int code = mgmul({a,P,b});
|
|
field_adjmoves[i] = code;
|
|
DEBB(DF_FIELD, (i, " = ", make_tuple(a,P,b), " = ", code, " T = ", T));
|
|
}
|
|
}
|
|
|
|
DEBB(DF_FIELD, ("field_adjmoves = ", field_adjmoves));
|
|
|
|
DEBB(DF_FIELD, ("finding code_to_cell/cell_to_code..."));
|
|
|
|
cell_to_code.clear();
|
|
code_to_cell.resize(N);
|
|
for(int i=0; i<N; i++) code_to_cell[i] = -1;
|
|
for(int i=0; i<N; i++) if(code_to_cell[i] == -1) {
|
|
for(int j: cyclers) code_to_cell[currfp_gmul(i, j)] = isize(cell_to_code);
|
|
cell_to_code.push_back(i);
|
|
}
|
|
|
|
DEBB(DF_FIELD, ("building allh..."));
|
|
int cells = N / perm_group;
|
|
allh.resize(cells);
|
|
for(int i=0; i<cells; i++) {
|
|
allh[i] = tailored_alloc<heptagon> (S7);
|
|
allh[i]->c7 = newCell(S7, allh[i]);
|
|
allh[i]->fieldval = i;
|
|
allh[i]->zebraval = 0;
|
|
allh[i]->alt = NULL;
|
|
acells.push_back(allh[i]->c7);
|
|
}
|
|
|
|
DEBB(DF_FIELD, ("finding tmatrices..."));
|
|
tmatrices.resize(cells);
|
|
|
|
for(int i=0; i<cells; i++) {
|
|
for(int d=0; d<S7; d++) {
|
|
int found = 0;
|
|
int tmul = currfp_gmul(cell_to_code[i], field_adjmoves[d]);
|
|
for(int s: cyclers) {
|
|
int tmul2 = currfp_gmul(tmul, s);
|
|
if(cell_to_code[code_to_cell[tmul2]] == tmul2) {
|
|
allh[i]->move(d) = allh[code_to_cell[tmul2]];
|
|
allh[i]->c7->move(d) = allh[i]->move(d)->c7;
|
|
tmatrices[i].push_back(reg3::adjmoves[d] * iadj * fullmatrices[s] * adj);
|
|
found++;
|
|
}
|
|
}
|
|
if(found != 1) DEBB(DF_FIELD, ("bad found: ", i, "/", d, "/", found));
|
|
// println(hlog, "tmatrix(",i,",",d,") = ", tmatrices[i][d]);
|
|
}
|
|
}
|
|
|
|
DEBB(DF_FIELD, ("setting spin..."));
|
|
for(int i=0; i<cells; i++)
|
|
for(int d=0; d<S7; d++)
|
|
for(int e=0; e<S7; e++)
|
|
if(allh[i]->move(d)->move(e) == allh[i]) {
|
|
allh[i]->c.setspin(d, e, false);
|
|
allh[i]->c7->c.setspin(d, e, false);
|
|
}
|
|
|
|
DEBB(DF_FIELD, ("creating patterns..."));
|
|
create_patterns();
|
|
}
|
|
|
|
set<cellwalker> plane;
|
|
|
|
void make_plane(cellwalker cw) {
|
|
if(plane.count(cw)) return;
|
|
plane.insert(cw);
|
|
for(int i=0; i<S7; i++)
|
|
if(reg3::dirs_adjacent[i][cw.spin])
|
|
make_plane(reg3::strafe(cw, i));
|
|
}
|
|
|
|
|
|
void create_patterns() {
|
|
// change the geometry to make sure that the correct celldistance is used
|
|
dynamicval<eGeometry> g(geometry, S7 == 12 ? gField534 : gField435);
|
|
// also, strafe needs currentmap
|
|
dynamicval<hrmap*> c(currentmap, this);
|
|
|
|
if(S7 == 12) {
|
|
// Emerald in 534
|
|
cell *a = gamestart();
|
|
cell *b = a;
|
|
for(cell *c: allcells())
|
|
if(hr::celldistance(a, c) == 5) {
|
|
b = c;
|
|
break;
|
|
}
|
|
for(cell *c: allcells())
|
|
if(hr::celldistance(a, c) > hr::celldistance(b, c))
|
|
c->master->zebraval |= 1;
|
|
|
|
// Vineyard in 534
|
|
b = (cellwalker(a, 0) + wstep + rev + wstep).at;
|
|
for(cell *c: allcells())
|
|
if(hr::celldistance(a, c) == hr::celldistance(b, c))
|
|
c->master->zebraval |= 2;
|
|
}
|
|
|
|
if(S7 == 6) {
|
|
// Emerald in 534
|
|
cell *a = gamestart();
|
|
for(cell *c: allcells())
|
|
if(hr::celldistance(a, c) > 3)
|
|
c->master->zebraval |= 1;
|
|
|
|
// Vineyard in 435
|
|
make_plane(cellwalker(gamestart(), 0));
|
|
println(hlog, "plane size = ", isize(plane));
|
|
|
|
set<int> plane_indices;
|
|
for(auto cw: plane) plane_indices.insert(cw.at->master->fieldval);
|
|
|
|
set<int> nwi;
|
|
for(int i=0; i<currfp_n(); i++) {
|
|
bool ok = true;
|
|
for(auto o: plane_indices) {
|
|
int j = code_to_cell[currfp_gmul(i, cell_to_code[o])];
|
|
if(plane_indices.count(j)) ok = false;
|
|
forCellEx(c1, allcells()[j]) if(plane_indices.count(c1->master->fieldval)) ok = false;
|
|
}
|
|
if(ok) nwi.insert(i);
|
|
}
|
|
|
|
int gpow = 0;
|
|
|
|
for(int i: nwi) {
|
|
int pw = 1;
|
|
int at = i;
|
|
while(true) {
|
|
at = currfp_gmul(at, i);
|
|
if(!nwi.count(at)) break;
|
|
pw++;
|
|
}
|
|
if(pw == 4) gpow = i;
|
|
}
|
|
|
|
int u = 0;
|
|
for(int a=0; a<5; a++) {
|
|
for(int o: plane_indices) {
|
|
int j = code_to_cell[currfp_gmul(u, cell_to_code[o])];
|
|
allcells()[j]->master->zebraval |= 2;
|
|
}
|
|
u = currfp_gmul(u, gpow);
|
|
}
|
|
}
|
|
}
|
|
|
|
void draw() override {
|
|
sphereflip = Id;
|
|
|
|
// for(int i=0; i<S6; i++) queuepoly(ggmatrix(cwt.at), shWall3D[i], 0xFF0000FF);
|
|
|
|
dq::visited_by_matrix.clear();
|
|
dq::enqueue_by_matrix(centerover->master, cview());
|
|
|
|
while(!dq::drawqueue.empty()) {
|
|
auto& p = dq::drawqueue.front();
|
|
heptagon *h = get<0>(p);
|
|
transmatrix V = get<1>(p);
|
|
dynamicval<ld> b(band_shift, get<2>(p));
|
|
bandfixer bf(V);
|
|
dq::drawqueue.pop();
|
|
|
|
cell *c = h->c7;
|
|
if(!do_draw(c, V)) continue;
|
|
drawcell(c, V);
|
|
if(wallopt && isWall3(c) && isize(dq::drawqueue) > 1000) continue;
|
|
|
|
for(int d=0; d<S7; d++)
|
|
dq::enqueue_by_matrix(h->move(d), V * tmatrices[h->fieldval][d]);
|
|
}
|
|
}
|
|
|
|
transmatrix relative_matrix(heptagon *h2, heptagon *h1) override {
|
|
if(h1 == h2) return Id;
|
|
int d = hr::celldistance(h2->c7, h1->c7);
|
|
|
|
for(int a=0; a<S7; a++) if(hr::celldistance(h1->move(a)->c7, h2->c7) < d)
|
|
return adj(h1, a) * relative_matrix(h2, h1->move(a));
|
|
|
|
println(hlog, "error in hrmap_field3:::relative_matrix");
|
|
return Id;
|
|
}
|
|
|
|
heptagon *getOrigin() override { return allh[0]; }
|
|
|
|
vector<cell*>& allcells() override { return acells; }
|
|
|
|
vector<hyperpoint> get_vertices(cell* c) override {
|
|
return vertices_only;
|
|
}
|
|
};
|
|
|
|
struct hrmap_reg3 : hrmap {
|
|
|
|
heptagon *origin;
|
|
hrmap *binary_map;
|
|
hrmap_quotient3 *quotient_map;
|
|
|
|
unordered_map<heptagon*, pair<heptagon*, transmatrix>> reg_gmatrix;
|
|
unordered_map<heptagon*, vector<pair<heptagon*, transmatrix> > > altmap;
|
|
|
|
vector<cell*> spherecells;
|
|
|
|
vector<cell*>& allcells() override {
|
|
if(sphere) return spherecells;
|
|
return hrmap::allcells();
|
|
}
|
|
|
|
hrmap_reg3() {
|
|
generate();
|
|
origin = tailored_alloc<heptagon> (S7);
|
|
heptagon& h = *origin;
|
|
h.s = hsOrigin;
|
|
h.cdata = NULL;
|
|
h.alt = NULL;
|
|
h.distance = 0;
|
|
h.fieldval = 0;
|
|
h.c7 = newCell(S7, origin);
|
|
if(sphere) spherecells.push_back(h.c7);
|
|
worst_error1 = 0, worst_error2 = 0;
|
|
|
|
dynamicval<hrmap*> cr(currentmap, this);
|
|
|
|
heptagon *alt = NULL;
|
|
transmatrix T = Id;
|
|
|
|
binary_map = nullptr;
|
|
quotient_map = nullptr;
|
|
|
|
#if CAP_FIELD
|
|
if(geometry == gSpace344) {
|
|
quotient_map = new hrmap_from_crystal;
|
|
h.zebraval = quotient_map->allh[0]->zebraval;
|
|
}
|
|
if(hyperbolic && !(cgflags & qIDEAL)) {
|
|
quotient_map = new hrmap_field3;
|
|
h.zebraval = quotient_map->allh[0]->zebraval;
|
|
}
|
|
#endif
|
|
|
|
if(hyperbolic) {
|
|
dynamicval<eGeometry> g(geometry, gBinary3);
|
|
binary::build_tmatrix();
|
|
alt = tailored_alloc<heptagon> (S7);
|
|
alt->s = hsOrigin;
|
|
alt->emeraldval = 0;
|
|
alt->zebraval = 0;
|
|
alt->distance = 0;
|
|
alt->alt = alt;
|
|
alt->cdata = NULL;
|
|
alt->c7 = NULL;
|
|
binary_map = binary::new_alt_map(alt);
|
|
T = xpush(.01241) * spin(1.4117) * xpush(0.1241) * cspin(0, 2, 1.1249) * xpush(0.07) * Id;
|
|
}
|
|
|
|
reg_gmatrix[origin] = make_pair(alt, T);
|
|
altmap[alt].emplace_back(origin, T);
|
|
|
|
celllister cl(origin->c7, 4, 100000, NULL);
|
|
for(cell *c: cl.lst) {
|
|
hyperpoint h = tC0(relative_matrix(c->master, origin));
|
|
close_distances[bucketer(h)] = cl.getdist(c);
|
|
}
|
|
}
|
|
|
|
ld worst_error1, worst_error2;
|
|
|
|
heptagon *getOrigin() override {
|
|
return origin;
|
|
}
|
|
|
|
void fix_distances(heptagon *h, heptagon *h2) {
|
|
vector<heptagon*> to_fix;
|
|
|
|
auto fix_pair = [&] (heptagon *h, heptagon *h2) {
|
|
if(!h2) return;
|
|
if(h->distance > h2->distance+1) {
|
|
h->distance = h2->distance + 1;
|
|
to_fix.push_back(h);
|
|
}
|
|
else if(h2->distance > h->distance+1) {
|
|
h2->distance = h->distance + 1;
|
|
to_fix.push_back(h2);
|
|
}
|
|
if(h->alt && h->alt == h2->alt) {
|
|
if(altdist(h) > altdist(h2) + 1) {
|
|
altdist(h) = altdist(h2) + 1;
|
|
to_fix.push_back(h);
|
|
}
|
|
else if (altdist(h2) > altdist(h) + 1) {
|
|
altdist(h2) = altdist(h) + 1;
|
|
to_fix.push_back(h2);
|
|
}
|
|
}
|
|
};
|
|
|
|
if(!h2) to_fix = {h};
|
|
else fix_pair(h, h2);
|
|
|
|
for(int i=0; i<isize(to_fix); i++) {
|
|
h = to_fix[i];
|
|
for(int j=0; j<S7; j++) fix_pair(h, h->move(j));
|
|
}
|
|
}
|
|
|
|
#define DEB 0
|
|
|
|
heptagon *counterpart(heptagon *h) {
|
|
return quotient_map->allh[h->fieldval];
|
|
}
|
|
|
|
heptagon *create_step(heptagon *parent, int d) override {
|
|
auto& p1 = reg_gmatrix[parent];
|
|
if(DEB) println(hlog, "creating step ", parent, ":", d, ", at ", p1.first, tC0(p1.second));
|
|
heptagon *alt = p1.first;
|
|
#if CAP_FIELD
|
|
transmatrix T = p1.second * (quotient_map ? quotient_map->tmatrices[parent->fieldval][d] : adjmoves[d]);
|
|
#else
|
|
transmatrix T = p1.second * adjmoves[d];
|
|
#endif
|
|
transmatrix T1 = T;
|
|
if(hyperbolic) {
|
|
dynamicval<eGeometry> g(geometry, gBinary3);
|
|
dynamicval<hrmap*> cm(currentmap, binary_map);
|
|
binary_map->virtualRebase(alt, T);
|
|
}
|
|
|
|
fixmatrix(T);
|
|
auto hT = tC0(T);
|
|
|
|
if(DEB) println(hlog, "searching at ", alt, ":", hT);
|
|
|
|
if(DEB) for(auto& p2: altmap[alt]) println(hlog, "for ", tC0(p2.second), " intval is ", intval(tC0(p2.second), hT));
|
|
|
|
ld err;
|
|
|
|
for(auto& p2: altmap[alt]) if((err = intval(tC0(p2.second), hT)) < 1e-3) {
|
|
if(err > worst_error1) println(hlog, format("worst_error1 = %lg", double(worst_error1 = err)));
|
|
// println(hlog, "YES found in ", isize(altmap[alt]));
|
|
if(DEB) println(hlog, "-> found ", p2.first);
|
|
int fb = 0;
|
|
hyperpoint old = T * (inverse(T1) * tC0(p1.second));
|
|
#if CAP_FIELD
|
|
if(quotient_map) {
|
|
p2.first->c.connect(counterpart(parent)->c.spin(d), parent, d, false);
|
|
fix_distances(p2.first, parent);
|
|
return p2.first;
|
|
}
|
|
#endif
|
|
for(int d2=0; d2<S7; d2++) {
|
|
hyperpoint back = p2.second * tC0(adjmoves[d2]);
|
|
if((err = intval(back, old)) < 1e-3) {
|
|
if(err > worst_error2) println(hlog, format("worst_error2 = %lg", double(worst_error2 = err)));
|
|
if(p2.first->move(d2)) println(hlog, "error: repeated edge");
|
|
p2.first->c.connect(d2, parent, d, false);
|
|
fix_distances(p2.first, parent);
|
|
fb++;
|
|
}
|
|
}
|
|
if(fb != 1) {
|
|
println(hlog, "found fb = ", fb);
|
|
println(hlog, old);
|
|
for(int d2=0; d2<S7; d2++) {
|
|
println(hlog, p2.second * tC0(adjmoves[d2]), " in distance ", intval(p2.second * tC0(adjmoves[d2]), old));
|
|
}
|
|
parent->c.connect(d, parent, d, false);
|
|
return parent;
|
|
}
|
|
return p2.first;
|
|
}
|
|
|
|
if(DEB) println(hlog, "-> not found");
|
|
int d2 = 0, fv = isize(reg_gmatrix);
|
|
#if CAP_FIELD
|
|
if(quotient_map) {
|
|
auto cp = counterpart(parent);
|
|
d2 = cp->c.spin(d);
|
|
fv = cp->c.move(d)->fieldval;
|
|
}
|
|
#endif
|
|
heptagon *created = tailored_alloc<heptagon> (S7);
|
|
created->c7 = newCell(S7, created);
|
|
if(sphere) spherecells.push_back(created->c7);
|
|
created->alt = NULL;
|
|
created->cdata = NULL;
|
|
#if CAP_FIELD
|
|
if(quotient_map) {
|
|
created->zebraval = quotient_map->allh[fv]->zebraval;
|
|
}
|
|
else
|
|
#endif
|
|
created->zebraval = hrand(10);
|
|
created->fieldval = fv;
|
|
created->distance = parent->distance + 1;
|
|
fixmatrix(T);
|
|
reg_gmatrix[created] = make_pair(alt, T);
|
|
altmap[alt].emplace_back(created, T);
|
|
created->c.connect(d2, parent, d, false);
|
|
return created;
|
|
}
|
|
|
|
~hrmap_reg3() {
|
|
if(binary_map) {
|
|
dynamicval<eGeometry> g(geometry, gBinary3);
|
|
delete binary_map;
|
|
}
|
|
if(quotient_map) delete quotient_map;
|
|
clearfrom(origin);
|
|
}
|
|
|
|
map<heptagon*, int> reducers;
|
|
|
|
void link_alt(const cellwalker& hs) override {
|
|
auto h = hs.at->master;
|
|
altdist(h) = 0;
|
|
if(h->alt->s != hsOrigin) reducers[h] = hs.spin;
|
|
}
|
|
|
|
void generateAlts(heptagon* h, int levs, bool link_cdata) override {
|
|
if(reducers.count(h)) {
|
|
heptspin hs(h, reducers[h]);
|
|
reducers.erase(h);
|
|
hs += wstep;
|
|
hs += rev;
|
|
altdist(hs.at) = altdist(h) - 1;
|
|
hs.at->alt = h->alt;
|
|
reducers[hs.at] = hs.spin;
|
|
fix_distances(hs.at, NULL);
|
|
}
|
|
for(int i=0; i<S7; i++) {
|
|
auto h2 = h->cmove(i);
|
|
if(h2->alt == NULL) {
|
|
h2->alt = h->alt;
|
|
altdist(h2) = altdist(h) + 1;
|
|
fix_distances(h2, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
void draw() override {
|
|
sphereflip = Id;
|
|
|
|
// for(int i=0; i<S6; i++) queuepoly(ggmatrix(cwt.at), shWall3D[i], 0xFF0000FF);
|
|
|
|
dq::visited.clear();
|
|
dq::enqueue(centerover->master, cview());
|
|
|
|
while(!dq::drawqueue.empty()) {
|
|
auto& p = dq::drawqueue.front();
|
|
heptagon *h = get<0>(p);
|
|
transmatrix V = get<1>(p);
|
|
dynamicval<ld> b(band_shift, get<2>(p));
|
|
bandfixer bf(V);
|
|
dq::drawqueue.pop();
|
|
|
|
|
|
cell *c = h->c7;
|
|
if(!do_draw(c, V)) continue;
|
|
drawcell(c, V);
|
|
if(wallopt && isWall3(c) && isize(dq::drawqueue) > 1000) continue;
|
|
|
|
for(int i=0; i<S7; i++) if(h->move(i)) {
|
|
dq::enqueue(h->move(i), V * adj(h, i));
|
|
}
|
|
}
|
|
}
|
|
|
|
transmatrix adj(heptagon *h, int d) {
|
|
#if CAP_FIELD
|
|
if(quotient_map) return quotient_map->adj(h, d);
|
|
else
|
|
#endif
|
|
return relative_matrix(h->cmove(d), h);
|
|
}
|
|
|
|
transmatrix adj(cell *c, int d) override { return adj(c->master, d); }
|
|
|
|
transmatrix relative_matrix(heptagon *h2, heptagon *h1) override {
|
|
auto p1 = reg_gmatrix[h1];
|
|
auto p2 = reg_gmatrix[h2];
|
|
transmatrix T = Id;
|
|
if(hyperbolic) {
|
|
dynamicval<eGeometry> g(geometry, gBinary3);
|
|
dynamicval<hrmap*> cm(currentmap, binary_map);
|
|
T = binary_map->relative_matrix(p2.first, p1.first);
|
|
}
|
|
T = inverse(p1.second) * T * p2.second;
|
|
if(elliptic && T[LDIM][LDIM] < 0) T = centralsym * T;
|
|
return T;
|
|
}
|
|
|
|
vector<hyperpoint> get_vertices(cell* c) override {
|
|
return vertices_only;
|
|
}
|
|
};
|
|
|
|
EX hrmap* new_map() {
|
|
if(quotient && !sphere) return new hrmap_field3;
|
|
return new hrmap_reg3;
|
|
}
|
|
|
|
hrmap_reg3* regmap() {
|
|
return ((hrmap_reg3*) currentmap);
|
|
}
|
|
|
|
EX int celldistance(cell *c1, cell *c2) {
|
|
if(c1 == c2) return 0;
|
|
if(c1 == currentmap->gamestart()) return c2->master->distance;
|
|
if(c2 == currentmap->gamestart()) return c1->master->distance;
|
|
|
|
auto r = regmap();
|
|
|
|
hyperpoint h = tC0(r->relative_matrix(c1->master, c2->master));
|
|
int b = bucketer(h);
|
|
if(close_distances.count(b)) return close_distances[b];
|
|
|
|
dynamicval<eGeometry> g(geometry, gBinary3);
|
|
return 20 + binary::celldistance3(r->reg_gmatrix[c1->master].first, r->reg_gmatrix[c2->master].first);
|
|
}
|
|
|
|
EX bool pseudohept(cell *c) {
|
|
auto m = regmap();
|
|
if(sphere) {
|
|
hyperpoint h = tC0(m->relative_matrix(c->master, regmap()->origin));
|
|
if(S7 == 12) {
|
|
hyperpoint h1 = cspin(0, 1, atan2(16, 69) + M_PI/4) * h;
|
|
for(int i=0; i<4; i++) if(abs(abs(h1[i]) - .5) > .01) return false;
|
|
return true;
|
|
}
|
|
if(S7 == 8)
|
|
return h[3] >= .99 || h[3] <= -.99 || abs(h[3]) < .01;
|
|
if(loop == 3 && face == 3 && S7 == 4)
|
|
return c == m->gamestart();
|
|
if(loop == 4 && face == 3)
|
|
return abs(h[3]) > .9;
|
|
if(loop == 3 && face == 4)
|
|
return abs(h[3]) > .9;
|
|
if(loop == 5 && face == 3)
|
|
return abs(h[3]) > .99 || abs(h[0]) > .99 || abs(h[1]) > .99 || abs(h[2]) > .99;
|
|
}
|
|
// chessboard pattern in 534
|
|
if(geometry == gSpace534)
|
|
return c->master->distance & 1;
|
|
if(geometry == gField534)
|
|
return hr::celldistance(c, currentmap->gamestart()) & 1;
|
|
if(geometry == gCrystal344)
|
|
return false;
|
|
if(hyperbolic) {
|
|
heptagon *h = m->reg_gmatrix[c->master].first;
|
|
return (h->zebraval == 1) && (h->distance & 1);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
EX void generate_cellrotations() {
|
|
auto &cr = cgi.cellrotations;
|
|
if(isize(cr)) return;
|
|
|
|
for(int a=0; a<S7; a++)
|
|
for(int b=0; b<S7; b++)
|
|
for(int c=0; c<S7; c++) {
|
|
using reg3::adjmoves;
|
|
transmatrix T = build_matrix(adjmoves[a]*C0, adjmoves[b]*C0, adjmoves[c]*C0, C0);
|
|
if(abs(det(T)) < 0.001) continue;
|
|
transmatrix U = build_matrix(adjmoves[0]*C0, adjmoves[1]*C0, adjmoves[2]*C0, C0);
|
|
transmatrix S = U * inverse(T);
|
|
if(abs(det(S) - 1) > 0.01) continue;
|
|
vector<int> perm(S7);
|
|
for(int x=0; x<S7; x++) perm[x] = -1;
|
|
for(int x=0; x<S7; x++)
|
|
for(int y=0; y<S7; y++)
|
|
if(hdist(S * adjmoves[x] * C0, adjmoves[y] * C0) < .1) perm[x] = y;
|
|
bool bad = false;
|
|
for(int x=0; x<S7; x++) if(perm[x] == -1) bad = true;
|
|
if(bad) continue;
|
|
|
|
cr.emplace_back(S, perm);
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
/* More precise, but very slow distance. Not used/optimized for now */
|
|
|
|
ld adistance(cell *c) {
|
|
hyperpoint h = tC0(regmap()->reg_gmatrix[c->master].second);
|
|
h = binary::deparabolic3(h);
|
|
return regmap()->reg_gmatrix[c->master].first->distance * log(2) - h[0];
|
|
}
|
|
|
|
unordered_map<pair<cell*, cell*>, int> memo;
|
|
|
|
bool cdd;
|
|
|
|
int celldistance(cell *c1, cell *c2) {
|
|
if(memo.count(make_pair(c1, c2))) return memo[make_pair(c1, c2)];
|
|
if(c1 == c2) return 0;
|
|
vector<cell*> v[2];
|
|
v[0].push_back(c1);
|
|
v[1].push_back(c2);
|
|
|
|
int steps = 0;
|
|
|
|
map<cell*, int> visited;
|
|
visited[c1] = 1;
|
|
visited[c2] = 2;
|
|
|
|
while(true) {
|
|
if(cdd) {
|
|
println(hlog, "state ", steps, "/",isize(v[0]), "/", isize(v[1]));
|
|
println(hlog, " A: ", v[0]);
|
|
println(hlog, " B: ", v[1]);
|
|
}
|
|
for(int i: {0,1}) {
|
|
vector<cell*> new_v;
|
|
for(cell *c: v[i]) forCellCM(cn, c) if(adistance(cn) < adistance(c)) {
|
|
auto &vi = visited[cn];
|
|
if((vi&3) == 0) {
|
|
vi = 4 * (steps+1);
|
|
vi |= (1<<i);
|
|
new_v.push_back(cn);
|
|
}
|
|
else if((vi&3) == 2-i) {
|
|
vector<pair<cell*, int>> ca1, ca2;
|
|
int b1 = 4*steps-4;
|
|
int b2 = ((vi>>2)<<2) - 4;
|
|
for(auto p: visited) {
|
|
if(cdd) println(hlog, p);
|
|
int ps = p.second & 3;
|
|
if(ps == 1+i && p.second >= b1)
|
|
ca1.emplace_back(p.first, p.second/4);
|
|
if(ps == 2-i && p.second >= b2 && p.second <= b2+8)
|
|
ca2.emplace_back(p.first, p.second/4);
|
|
}
|
|
int bound = 1<<16;
|
|
for(auto p1: ca1) for(auto p2: ca2) {
|
|
hyperpoint h = tC0(relative_matrix(p1.first->master, p2.first->master));
|
|
int b = bucketer(h);
|
|
if(close_distances.count(b)) {
|
|
int d = close_distances[b] + p1.second + p2.second;
|
|
if(cdd) println(hlog, "candidate: close=", close_distances[b], p1, p2, "; h = ", h);
|
|
if(d < bound) bound = d;
|
|
}
|
|
else if(cdd) println(hlog, "bucket missing");
|
|
}
|
|
return memo[make_pair(c1, c2)] = bound;
|
|
return bound;
|
|
}
|
|
}
|
|
v[i] = std::move(new_v);
|
|
}
|
|
steps++;
|
|
}
|
|
}
|
|
|
|
cellwalker target;
|
|
int tsteps;
|
|
|
|
int dist_alt(cell *c) {
|
|
if(!target.at) {
|
|
target = cellwalker(currentmap->gamestart(), 0);
|
|
tsteps = 0;
|
|
for(int i=0; i<30; i++) target += wstep, target += rev, tsteps++;
|
|
}
|
|
if(specialland == laCamelot) return reg3::celldistance(c, target.at);
|
|
else {
|
|
int d = reg3::celldistance(c, target.at) - tsteps;
|
|
if(d < 10) target += wstep, target += rev, tsteps++;
|
|
return d;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Construct a cellwalker in direction j from cw.at, such that its direction is as close
|
|
// as possible to cw.spin. Assume that j and cw.spin are adjacent
|
|
|
|
#if MAXMDIM >= 4
|
|
EX cellwalker strafe(cellwalker cw, int j) {
|
|
hyperpoint hfront = tC0(adjmoves[cw.spin]);
|
|
transmatrix T = currentmap->adj(cw.at, j);
|
|
for(int i=0; i<S7; i++) if(i != cw.at->c.spin(j))
|
|
if(hdist(hfront, T * tC0(adjmoves[i])) < strafedist + .01)
|
|
return cellwalker(cw.at->move(j), i);
|
|
println(hlog, "incorrect strafe");
|
|
exit(1);
|
|
}
|
|
EX }
|
|
#endif
|
|
}
|
|
|