1 #include "rogueviz.h"
2
3 // SAG visualizer (e.g. Reddit roguelikes, GitHub languages)
4 //-----------------------------------------------------------
5
6 // see: https://www.youtube.com/watch?v=mDG3_f8R2Ns (SAG boardgames)
7 // see: https://www.youtube.com/watch?v=WSyygk_3j9o (SAG roguelikes)
8 // see: https://www.youtube.com/watch?v=HWQkDkeEUeM (SAG programming languages)
9
10 namespace rogueviz {
11
12 namespace sag {
13
14 bool turn(int delta);
15
16 int sagpar = 0;
17
18 enum eSagmode { sagOff, sagHC, sagSA };
19
20 eSagmode sagmode; // 0 - off, 1 - hillclimbing, 2 - SA
21
22 const char *sagmodes[3] = {"off", "HC", "SA"};
23
24 ld temperature = -4;
25 const int INSNAKE = 117;
26 int numsnake;
27 const char *loadfname;
28
29 #define MAXSNAKETAB 1000
30 int sdist[MAXSNAKETAB][MAXSNAKETAB];
31 int insnaketab = 0;
32
33 vector<cell*> snakecells;
34 vector<int> snakefirst, snakelast;
35 vector<int> snakenode;
36 vector<int> snakeid;
37 vector<int> lpbak;
38 vector<int> wpbak;
39
40 bool snake_enabled;
41
setsnake(cellwalker & cw,int i)42 void setsnake(cellwalker& cw, int i) {
43 lpbak[i] = cw.at->landparam;
44 wpbak[i] = cw.at->wparam;
45 cw.at->landparam = i; cw.at->wparam = INSNAKE;
46 // cw.at->monst = moWormtail; cw.at->mondir = cw.spin;
47 snakecells[i] = cw.at;
48 }
49
snakeswitch()50 void snakeswitch() {
51 for(int i=0; i<numsnake; i++) {
52 cell *c = snakecells[i];
53 int x;
54 x = lpbak[i]; lpbak[i] = c->landparam; c->landparam = x;
55 x = wpbak[i]; wpbak[i] = c->wparam; c->wparam = x;
56 }
57 snake_enabled = !snake_enabled;
58 }
59
enable_snake()60 void enable_snake() { if(!snake_enabled) snakeswitch(); }
61
disable_snake()62 void disable_snake() { if(snake_enabled) snakeswitch(); }
63
snakedist(int i,int j)64 int snakedist(int i, int j) {
65 if(i < insnaketab && j < insnaketab) return sdist[i][j];
66 if(bounded) return celldistance(snakecells[i], snakecells[j]);
67 int i0 = i, i1 = i, j0 = j, j1 = j;
68 int cost = 0;
69 // intersect
70 while(true) {
71 if(j0 > i1+1) { j0 = snakefirst[j0], j1 = snakelast[j1]; cost++; }
72 else if(i0 > j1+1) { i0 = snakefirst[i0], i1 = snakelast[i1]; cost++; }
73 else if(j1+1 == i0) return cost+1;
74 else if(i1+1 == j0) return cost+1;
75 else return cost;
76 }
77 }
78
initSnake(int n)79 void initSnake(int n) {
80 if(bounded) n = isize(currentmap->allcells());
81 numsnake = n;
82 snakecells.resize(numsnake);
83 snakefirst.resize(numsnake);
84 snakelast.resize(numsnake);
85 snakenode.resize(numsnake);
86 lpbak.resize(numsnake);
87 wpbak.resize(numsnake);
88 if(bounded) {
89 for(int i=0; i<n; i++) {
90 cellwalker cw(currentmap->allcells()[i], 0);
91 setsnake(cw, i);
92 }
93 }
94 else {
95 cellwalker cw = cwt;
96 setsnake(cw, 0);
97 cw += wstep;
98 setsnake(cw, 1);
99 for(int i=2; i<=numsnake; i++) {
100 if(i == numsnake && sphere) break;
101 cw += wstep;
102 snakefirst[i-1] = cw.at->landparam;
103 while(cw.at->wparam == INSNAKE) {
104 snakelast[i-1] = cw.at->landparam;
105 cw = cw + wstep + 1 + wstep;
106 }
107 if(i == numsnake) break;
108 setsnake(cw, i); cw += 1;
109 }
110 }
111 int stab = min(numsnake, MAXSNAKETAB);
112 for(int i=0; i<stab; i++)
113 for(int j=0; j<stab; j++)
114 sdist[i][j] = snakedist(i,j);
115 insnaketab = stab;
116 snake_enabled = true;
117 }
118
119 ld hub_penalty;
120 string hub_filename;
121 vector<int> hubval;
122
costat(int vid,int sid)123 double costat(int vid, int sid) {
124 if(vid < 0) return 0;
125 double cost = 0;
126 vertexdata& vd = vdata[vid];
127 for(int j=0; j<isize(vd.edges); j++) {
128 edgeinfo *ei = vd.edges[j].second;
129 int t2 = vd.edges[j].first;
130 if(snakeid[t2] != -1) cost += snakedist(sid, snakeid[t2]) * ei->weight2;
131 }
132
133 if(!hubval.empty()) {
134 cell *c = snakecells[sid];
135 for(int i=0; i<c->type; i++) {
136 cell *c2 = c->move(i);
137 if(c2 && c2->wparam == INSNAKE) {
138 int vid2 = snakenode[c2->landparam];
139 if(vid2 >= 0 && (hubval[vid] & hubval[snakenode[c2->landparam]]) == 0)
140 cost += hub_penalty;
141 }
142 }
143 }
144
145 /* cell *c = snakecells[id];
146 for(int i=0; i<c->type; i++) {
147 cell *c2 = c->move(i);
148 if(c2 && c2->wparam == INSNAKE && snakenode[c2->landparam] >= 0)
149 cost += 100;
150 } */
151 return cost;
152 }
153
154 // std::mt19937 los;
155
156 bool infullsa;
157
158 double cost;
159 int N;
160
161 vector<double> chgs;
162
163 edgetype *sag_edge;
164
forgetedges(int id)165 void forgetedges(int id) {
166 for(int i=0; i<isize(vdata[id].edges); i++)
167 vdata[id].edges[i].second->orig = NULL;
168 }
169
chance(double p)170 bool chance(double p) {
171 p *= double(hrngen.max()) + 1;
172 auto l = hrngen();
173 auto pv = (decltype(l)) p;
174 if(l < pv) return true;
175 if(l == pv) return chance(p-pv);
176 return false;
177 }
178
saiter()179 void saiter() {
180 aiter:
181
182 int t1 = hrand(N);
183 int sid1 = snakeid[t1];
184
185 int sid2;
186
187 int s = hrand(6);
188
189 if(s == 3) s = 2;
190 if(s == 4) s = 5;
191
192 if((sagpar&1) && (s == 2 || s == 3 || s == 4)) return;
193
194 if(s == 5) sid2 = hrand(numsnake);
195
196 else {
197 cell *c;
198 if(s>=2 && isize(vdata[t1].edges)) c = snakecells[snakeid[hrand(isize(vdata[t1].edges))]];
199 else c = snakecells[sid1];
200
201 int it = s<2 ? (s+1) : s-2;
202 for(int ii=0; ii<it; ii++) {
203 int d = hrand(c->type);
204 c = c->move(d);
205 if(!c) goto aiter;
206 if(c->wparam != INSNAKE) goto aiter;
207 }
208 sid2 = c->landparam;
209 }
210 int t2 = snakenode[sid2];
211
212 snakenode[sid1] = -1; snakeid[t1] = -1;
213 snakenode[sid2] = -1; if(t2 >= 0) snakeid[t2] = -1;
214
215 double change =
216 costat(t1,sid2) + costat(t2,sid1) - costat(t1,sid1) - costat(t2,sid2);
217
218 snakenode[sid1] = t1; snakeid[t1] = sid1;
219 snakenode[sid2] = t2; if(t2 >= 0) snakeid[t2] = sid2;
220
221 if(change < 0) chgs.push_back(-change);
222
223 if(change > 0 && (sagmode == sagHC || !chance(exp(-change * exp(-temperature))))) return;
224
225 snakenode[sid1] = t2; snakenode[sid2] = t1;
226 snakeid[t1] = sid2; if(t2 >= 0) snakeid[t2] = sid1;
227 if(vdata[t1].m) vdata[t1].m->base = snakecells[sid2];
228 if(t2 >= 0 && vdata[t2].m) vdata[t2].m->base = snakecells[sid1];
229 cost += 2*change;
230
231 if(t1 >= 0) forgetedges(t1);
232 if(t2 >= 0) forgetedges(t2);
233 }
234
organize()235 void organize() {
236 for(int i=0; i<numsnake; i++) snakenode[i] = -1;
237 vector<int> freenodes;
238 for(int i=0; i<N; i++)
239 if(snakeid[i] != -1)
240 snakenode[snakeid[i]] = i;
241
242 for(int i=0; i<N; i++)
243 if(snakeid[i] != -1)
244 if(snakenode[snakeid[i]] != i)
245 snakeid[i] = -1;
246
247 for(int i=0; i<numsnake; i++)
248 if(snakenode[i] == -1)
249 freenodes.push_back(i);
250
251 int j = 0;
252 for(int i=0; i<N; i++)
253 if(snakeid[i] == -1) {
254 snakeid[i] = freenodes[j];
255 snakenode[freenodes[j]] = i;
256 j++;
257 }
258 cost = 0; for(int i=0; i<N; i++) cost += costat(i, i);
259 }
260
loadsnake(const string & fname)261 void loadsnake(const string& fname) {
262 printf("Loading the sag from: %s\n", fname.c_str());
263 FILE *sf = fopen(fname.c_str(), "rt");
264 if(!sf) { printf("Failed to open file.\n"); exit(1); }
265 if(sf) while(true) {
266 string lab;
267 while(true) {
268 int c = fgetc(sf);
269 if(c == EOF) goto afterload;
270 else if(c == ',' || c == ';') break;
271 else if(rv_ignore(c)) ;
272 else lab += c;
273 }
274 int sid = -1;
275 int err = fscanf(sf, "%d", &sid);
276 if(sid < 0 || sid >= numsnake || err < 1) sid = -1;
277 if(!labeler.count(lab)) {
278 printf("unknown vertex: %s\n", lab.c_str());
279 }
280 else {
281 int id = getid(lab);
282 snakeid[id] = sid;
283 }
284 }
285 afterload:
286 if(sf) fclose(sf);
287
288 organize();
289 for(int i=0; i<N; i++) {
290 if(vdata[i].m) vdata[i].m->base = snakecells[sag::snakeid[i]];
291 forgetedges(i);
292 }
293
294 shmup::fixStorage();
295 }
296
297 vector<edgeinfo> sagedges;
298
299 /* bool totcmp(int i, int j) {
300 return totwei[i] > totwei[j];
301 } */
302
303 int ipturn = 100;
304 int numiter = 0;
305
306 int hightemp = 10;
307 int lowtemp = -15;
308
dofullsa(int satime)309 void dofullsa(int satime) {
310 sagmode = sagSA;
311 enable_snake();
312 int t1 = SDL_GetTicks();
313
314 while(true) {
315 int t2 = SDL_GetTicks();
316 double d = (t2-t1) / (1000. * satime);
317 if(d > 1) break;
318 temperature = hightemp - (d*(hightemp-lowtemp));
319 chgs.clear();
320 for(int i=0; i<50000; i++) {
321 numiter++;
322 sag::saiter();
323 }
324 DEBB(DF_LOG, (format("it %8d temp %6.4f [1/e at %13.6f] cost = %f ",
325 numiter, double(sag::temperature), (double) exp(sag::temperature),
326 double(sag::cost))));
327
328 sort(chgs.begin(), chgs.end());
329 int cc = chgs.size() - 1;
330 DEBB(DF_LOG, (format("%9.4f .. %9.4f .. %9.4f .. %9.4f .. %9.4f\n",
331 double(chgs[0]), double(chgs[cc/4]), double(chgs[cc/2]), double(chgs[cc*3/4]), double(chgs[cc]))));
332 fflush(stdout);
333 }
334
335 temperature = -5;
336 disable_snake();
337 sagmode = sagOff;
338 }
339
iterate()340 void iterate() {
341 if(!sagmode) return;
342 int t1 = SDL_GetTicks();
343 enable_snake();
344 for(int i=0; i<ipturn; i++) {
345 numiter++;
346 sag::saiter();
347 }
348 disable_snake();
349 int t2 = SDL_GetTicks();
350 int t = t2 - t1;
351 if(t < 50) ipturn *= 2;
352 else if(t > 200) ipturn /= 2;
353 else ipturn = ipturn * 100 / t;
354 DEBB(DF_LOG, ("it %8d temp %6.4f [2:%8.6f,10:%8.6f,50:%8.6f] cost = %f\n",
355 numiter, double(sag::temperature),
356 (double) exp(-2 * exp(-sag::temperature)),
357 (double) exp(-10 * exp(-sag::temperature)),
358 (double) exp(-50 * exp(-sag::temperature)),
359 (double) sag::cost));
360 }
361
savesnake(const string & fname)362 void savesnake(const string& fname) {
363 FILE *f = fopen(fname.c_str(), "wt");
364 for(int i=0; i<N; i++)
365 fprintf(f, "%s;%d\n", vdata[i].name.c_str(), snakeid[i]);
366 fclose(f);
367 }
368
loglik()369 void loglik() {
370 int indist[30], pedge[30];
371 for(int d=0; d<30; d++) indist[d] = 0, pedge[d] = 0;
372
373 for(int i=0; i<N; i++)
374 for(int j=0; j<i; j++)
375 indist[snakedist(snakeid[i], snakeid[j])]++;
376
377 for(int i=0; i<isize(sagedges); i++) {
378 edgeinfo& ei = sagedges[i];
379 if(snakedist(snakeid[ei.i], snakeid[ei.j]) == 0) {
380 printf("zero between %d (%s) and %d (%s)\n",
381 snakeid[ei.i], vdata[ei.i].name.c_str(),
382 snakeid[ei.j], vdata[ei.j].name.c_str());
383 }
384 if(ei.weight >= sag_edge->visible_from)
385 pedge[snakedist(snakeid[ei.i], snakeid[ei.j])]++;
386 }
387
388 for(int d=0; d<30; d++)
389 if(indist[d])
390 printf("%2d: %7d/%7d %7.3lf\n",
391 d, pedge[d], indist[d], double(pedge[d] * 100. / indist[d]));
392
393 ld loglik = 0;
394 for(int d=0; d<30; d++) {
395 int p = pedge[d], pq = indist[d];
396 int q = pq - p;
397 if(p && q)
398 loglik += p * log(p) + q * log(q) - pq * log(pq);
399 }
400
401 println(hlog, "loglikelihood = ", fts(loglik));
402 }
403
read_hubs(const string & fname)404 void read_hubs(const string& fname) {
405 hubval.resize(isize(vdata), -1);
406 fhstream f(fname, "rt");
407 if(!f.f) { printf("Failed to open hub file: %s\n", fname.c_str()); exit(1); }
408 println(hlog, "loading hubs: ", fname);
409 while(!feof(f.f)) {
410 string l1, l2;
411 while(true) {
412 int c = fgetc(f.f);
413 if(c == EOF) return;
414 else if(c == ';') break;
415 else if(rv_ignore(c)) ;
416 else l1 += c;
417 }
418 while(true) {
419 int c = fgetc(f.f);
420 if(c == EOF) return;
421 else if(c == ';') return;
422 else if(rv_ignore(c)) break;
423 else l2 += c;
424 }
425 if(!id_known(l1)) {
426 printf("label unknown: %s\n", l1.c_str());
427 exit(1);
428 }
429 hubval[getid(l1)] = atoi(l2.c_str());
430 }
431 }
432
readsag(const char * fname)433 void readsag(const char *fname) {
434 maxweight = 0;
435 sag_edge = add_edgetype("SAG edge");
436 fhstream f(fname, "rt");
437 if(!f.f) { printf("Failed to open SAG file: %s\n", fname); exit(1); }
438 // while(fgetc(f) != 10 && fgetc(f) != 13 && !feof(f)) ;
439 while(!feof(f.f)) {
440 string l1, l2;
441 while(true) {
442 int c = fgetc(f.f);
443 if(c == EOF) return;
444 else if(c == ';') break;
445 else if(rv_ignore(c)) ;
446 else l1 += c;
447 }
448 while(true) {
449 int c = fgetc(f.f);
450 if(c == EOF) return;
451 else if(c == ';') break;
452 else if(rv_ignore(c)) ;
453 else l2 += c;
454 }
455 ld wei;
456 if(!scan(f, wei)) continue;
457 edgeinfo ei(sag_edge);
458 ei.i = getid(l1);
459 ei.j = getid(l2);
460 ei.weight = wei;
461 sagedges.push_back(ei);
462 }
463 }
464
465 ld edgepower=1, edgemul=1;
466
read(string fn)467 void read(string fn) {
468 fname = fn;
469 init(RV_GRAPH | RV_WHICHWEIGHT | RV_AUTO_MAXWEIGHT | RV_HAVE_WEIGHT);
470
471 rv_hook(rogueviz::hooks_close, 100, [] { sag::sagedges.clear(); });
472 rv_hook(shmup::hooks_turn, 100, turn);
473 rv_hook(rogueviz::hooks_rvmenu, 100, [] {
474 dialog::addSelItem(XLAT("temperature"), fts(sag::temperature), 't');
475 dialog::add_action([] {
476 dialog::editNumber(sag::temperature, sag::lowtemp, sag::hightemp, 1, 0, XLAT("temperature"), "");
477 });
478 dialog::addSelItem(XLAT("SAG mode"), sag::sagmodes[sag::sagmode], 'm');
479 dialog::add_action([] { sag::sagmode = sag::eSagmode( (1+sag::sagmode) % 3 ); });
480 });
481
482 weight_label = "min weight";
483 temperature = 0; sagmode = sagOff;
484 readsag(fname.c_str());
485 if(hub_filename != "")
486 read_hubs(hub_filename);
487
488 N = isize(vdata);
489 // totwei.resize(N);
490 // for(int i=0; i<N; i++) totwei[i] = 0;
491
492 for(int i=0; i<N; i++) vdata[i].data = 0;
493 /* for(int i=0; i<isize(sagedges); i++) {
494 edgeinfo& ei = sagedges[i];
495 // maxwei[ei.i] = max(maxwei[ei.i], ei.weight);
496 // maxwei[ei.j] = max(maxwei[ei.j], ei.weight);
497 // totwei[ei.i] += ei.weight;
498 // totwei[ei.j] += ei.weight;
499 } */
500 for(int i=0; i<isize(sagedges); i++) {
501 edgeinfo& ei = sagedges[i];
502 // (ei.weight >= maxwei[ei.i] / 5 || ei.weight >= maxwei[ei.j] / 5);
503
504 ei.weight2 = pow((double) ei.weight, (double) edgepower) * edgemul;
505 // LANG:: pow(ei.weight, .4) / 50;
506
507 // ei.weight2 = 0; int w = ei.weight; while(w) { w >>= 1; ei.weight2++; }
508 /* if(totwei[ei.i] <= 0 || totwei[ei.j] <= 0) {
509 printf("BAD TOTWEI\n");
510 exit(1);
511 }
512 ei.weight2 = 3 * (
513 sqrt(ei.weight * 1. / totwei[ei.i]) * log(totwei[ei.i]) * log(totwei[ei.i]) +
514 sqrt(ei.weight * 1. / totwei[ei.j]) * log(totwei[ei.j]) * log(totwei[ei.j])); */
515 // printf("%f\n", ei.weight2);
516 addedge0(ei.i, ei.j, &ei);
517 }
518
519 initSnake(N*2);
520 printf("numsnake = %d\n", numsnake);
521 if(numsnake < N) {
522 printf("Error: snake does not fit\n");
523 exit(1);
524 }
525 snakeid.resize(N);
526 for(int i=0; i<N; i++) snakeid[i] = -1;
527 organize();
528 disable_snake();
529
530 for(int i=0; i<N; i++) {
531 int ii = i;
532 vertexdata& vd = vdata[ii];
533 vd.cp = colorpair(dftcolor);
534 createViz(ii, sag::snakecells[sag::snakeid[i]], Id);
535 }
536
537 storeall();
538 }
539
readArgs()540 int readArgs() {
541 #if CAP_COMMANDLINE
542 using namespace arg;
543
544 if(0) ;
545
546 else if(argis("-sagmin")) {
547 shift_arg_formula(default_edgetype.visible_from);
548 default_edgetype.visible_from_hi = default_edgetype.visible_from;
549 default_edgetype.visible_from_help = default_edgetype.visible_from;
550 }
551 else if(argis("-sagminhi")) {
552 shift_arg_formula(default_edgetype.visible_from_hi);
553 }
554 else if(argis("-sagminhelp")) {
555 shift_arg_formula(default_edgetype.visible_from_help);
556 }
557
558 // (1) configure edge weights
559 else if(argis("-edgepower")) {
560 shift_arg_formula(sag::edgepower);
561 shift_arg_formula(sag::edgemul);
562 }
563 // (1) configure temperature (high, low)
564 else if(argis("-sagtemp")) {
565 shift(); sag::hightemp = argi();
566 shift(); sag::lowtemp = argi();
567 }
568 // (2) read the edge data
569 else if(argis("-sagpar")) {
570 PHASE(3);
571 shift();
572 sag::sagpar = argi();
573 }
574 else if(argis("-sag")) {
575 PHASE(3);
576 shift(); sag::read(args());
577 }
578 else if(argis("-saghubs")) {
579 println(hlog, "HUBS");
580 PHASE(3);
581 shift_arg_formula(sag::hub_penalty);
582 shift(); hub_filename = args();
583 }
584 // (3) load the initial positioning
585 else if(argis("-gload")) {
586 PHASE(3); shift(); sag::loadsnake(args());
587 }
588 // (4) perform simulated annealing: -fullsa <time in seconds>
589 else if(argis("-fullsa")) {
590 shift(); sag::dofullsa(argi());
591 }
592 // (5) save the positioning
593 else if(argis("-gsave")) {
594 PHASE(3); shift(); sag::savesnake(args());
595 }
596 // (6) output loglikelihood
597 else if(argis("-lik")) {
598 sag::loglik();
599 }
600 else return 1;
601 #endif
602 return 0;
603 }
604
turn(int delta)605 bool turn(int delta) {
606 return false;
607 // shmup::pc[0]->rebase();
608 }
609
cname()610 string cname() {
611 if(euclid) return "coord-6.txt";
612 if(PURE) return "coord-7.txt";
613 return "coord-67.txt";
614 }
615
616 int ah = addHook(hooks_args, 100, readArgs)
__anon888a3ae20502(string s, vector<tour::slide>& v) 617 + addHook_rvslides(120, [] (string s, vector<tour::slide>& v) {
618 if(s != "data") return;
619 using namespace pres;
620 string sagf = "SAG/";
621 v.push_back(
622 slide{sagf+"Roguelikes", 63, LEGAL::UNLIMITED | QUICKGEO,
623 "A visualization of roguelikes, based on discussion on /r/reddit. "
624 "See: http://www.roguetemple.com/z/hyper/reddit.php",
625 roguevizslide('0', [] () {
626 rogueviz::dftcolor = 0x282828FF;
627
628 rogueviz::showlabels = true;
629 part(rogueviz::default_edgetype.color, 0) = 181;
630 rogueviz::sag::edgepower = 1;
631 rogueviz::sag::edgemul = 1;
632
633 gmatrix.clear();
634 drawthemap();
635 gmatrix0 = gmatrix;
636
637 rogueviz::sag::read(RVPATH "roguelikes/edges.csv");
638 rogueviz::readcolor(RVPATH "roguelikes/color.csv");
639 rogueviz::sag::loadsnake(RVPATH "roguelikes/" + cname());
640 })
641 }
642 );
643 v.push_back(slide {sagf+"Programming languages of GitHub", 64, LEGAL::UNLIMITED | QUICKGEO,
644 "A visualization of programming languages.",
645 roguevizslide('0', [] () {
646 rogueviz::dftcolor = 0x282828FF;
647
648 rogueviz::showlabels = true;
649 part(rogueviz::default_edgetype.color, 0) = 128;
650 rogueviz::sag::edgepower = .4;
651 rogueviz::sag::edgemul = .02;
652
653 gmatrix.clear();
654 drawthemap();
655 gmatrix0 = gmatrix;
656
657 rogueviz::sag::read(RVPATH "lang/edges.csv");
658 rogueviz::readcolor(RVPATH "lang/color.csv");
659 rogueviz::sag::loadsnake(RVPATH "lang/" + cname());
660 if(euclid) rogueviz::legend.clear();
661 })
662 });
663
664 v.push_back(slide {sagf+"Boardgames", 62, LEGAL::UNLIMITED | QUICKGEO,
665 "A visualization of board games, based on discussions on Reddit.",
666 roguevizslide('0', [] () {
667 rogueviz::dftcolor = 0x282828FF;
668
669 rogueviz::showlabels = true;
670 part(rogueviz::default_edgetype.color, 0) = 157;
671 rogueviz::sag::edgepower = 1;
672 rogueviz::sag::edgemul = 1;
673
674 gmatrix.clear();
675 drawthemap();
676 gmatrix0 = gmatrix;
677
678 rogueviz::sag::read(RVPATH "boardgames/edges.csv");
679 rogueviz::readcolor(RVPATH "boardgames/color.csv");
680 rogueviz::sag::loadsnake(RVPATH "boardgames/" + cname());
681 })
682 });
683
684 });
685
686 EX }
687
688 }
689