From d1bbc6e83e78470c1b427ae2f8007a18ee28fe9a Mon Sep 17 00:00:00 2001 From: Zeno Rogue Date: Wed, 26 Feb 2020 01:31:02 +0100 Subject: [PATCH] wfcgen, and generating laEclectic --- hyper.cpp | 1 + landgen.cpp | 57 +++++++++++- wfcgen.cpp | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 300 insertions(+), 2 deletions(-) create mode 100644 wfcgen.cpp diff --git a/hyper.cpp b/hyper.cpp index cb488eba..e668f104 100644 --- a/hyper.cpp +++ b/hyper.cpp @@ -68,6 +68,7 @@ #include "monstergen.cpp" #include "landlock.cpp" #include "landgen.cpp" +#include "wfcgen.cpp" #include "orbs.cpp" #include "inventory.cpp" #include "system.cpp" diff --git a/landgen.cpp b/landgen.cpp index 5d636da0..02ca652e 100644 --- a/landgen.cpp +++ b/landgen.cpp @@ -2480,6 +2480,59 @@ EX void giantLandSwitch(cell *c, int d, cell *from) { #endif } + case laEclectic: { + + if(d >= 9) c->wall = waChasm; + + if(d == 8) wfc::schedule(c); + + if(d == 7) { + if(c->wall == waRose) c->wall = waNone; + if(c->wall == waPalace && hrand(100) < 50) { + bool ok = true; + forCellEx(c2, c) if(among(c2->wall, waClosedGate, waOpenGate)) + ok = false; + if(ok) c->wall = waNone; + } + + wfc::invoke(); + bool locked = true; + forCellEx(c1, c) if(!c1->wall) locked = false; + if(locked) c->item = itEclectic; + + if(c->wall == waNone && hrand_monster(2500) < 30 + items[itEclectic] + yendor::hardness() && !safety) { + cell *c2 = c->move(hrand(c->type)); + if(c2->wall == waRed1 || c2->wall == waRed2 || c2->wall == waRed3) + c->monst = moRedTroll; + + else if(c2->wall == waDeadwall) + c->monst = pick(moMiner, moTroll); + + else if(c2->wall == waSmallTree || c2->wall == waBigTree) + c->monst = moFireFairy; + + else if(c2->wall == waSandstone || c2->wall == waCharged) + c->monst = moMetalBeast, c->hitpoints = 2; + + else if(c2->wall == waPalace) + c->monst = moPalace, c->hitpoints = 2; + + else if(c2->wall == waIcewall) + c->monst = pick(moWolf, moIceGolem); + + else if(c2->wall == waNone && !c2->monst && hrand(100) < 5) { + cell *c1 = c; + c1->monst = moPair; + c2->monst = moPair; + c1->mondir = neighborId(c1, c2); + c2->mondir = neighborId(c2, c1); + } + } + } + + break; + } + case laNone: case laBarrier: case laOceanWall: @@ -2592,10 +2645,10 @@ EX void repairLandgen(cell *c) { if(c->land == laAlchemist && c->wall != waFloorA && c->wall != waFloorB) c->wall = waFloorA; - if(c->wall == waIcewall && c->land != laIce && c->land != laCocytus && c->land != laBlizzard) + if(c->wall == waIcewall && !among(c->land, laIce, laCocytus, laBlizzard, laEclectic)) c->wall = waNone; - if(c->wall == waRed3 && c->land != laRedRock && c->land != laSnakeNest && c->land != laBrownian) + if(c->wall == waRed3 && c->land != laRedRock && c->land != laSnakeNest && c->land != laBrownian && c->land != laEclectic) c->wall = waNone; if(c->item == itRedGem && c->land != laRedRock) diff --git a/wfcgen.cpp b/wfcgen.cpp new file mode 100644 index 00000000..6724a65e --- /dev/null +++ b/wfcgen.cpp @@ -0,0 +1,244 @@ +#include "hyper.h" + +namespace hr { + +EX namespace wfc { + +typedef map, short> wfc_data; + +wfc_data probs; + +typedef pair, short> probdata; + +wfc_data gen_decompressed(const wfc_data& data) { + wfc_data res; + for(auto& d: data) { + auto cur = d.first; + for(int i=0; i cur = d.first; + vector best = d.first; + for(int i=0; i(hs, isize(p.first)); + for(auto c: p.first) hr::hwrite(hs, c); + hr::hwrite(hs, p.second); + } + } + +void hread(hstream& hs, wfc_data& data) { + data.clear(); + int qty = hs.get(); + for(int i=0; i w; + w.resize(hs.get()); + for(auto& c: w) c = eWall(hs.get()); + data[w] = hs.get(); + } + } + +EX void load_probs() { + start_game(); + manual_celllister cl; + cl.add(cwt.at); + for(int i=0; iwall == waChasm || c->mpdist > 7) goto next; + forCellEx(c1, c) if(c1->wall == waChasm || c1->mpdist > 7) goto next; + + for(int i=0; itype; i++) { + vector s; + if(c->type == 6 && (i&1)) continue; + s.push_back(c->wall); + for(int j=0; jtype; j++) s.push_back(c->modmove(i+j)->wall); + bool interesting = false; + for(eWall w: s) if(w != waNone) interesting = true; + if(interesting) probs[s] += 42 / c->type; + } + + forCellEx(c1, c) cl.add(c1); + next: ; + } + } + +bool agree(cell *c, probdata& p) { + // println(hlog, p); + if(isize(p.first) != c->type + 1) return false; + int idx = 1; + if(c->wall != waChasm && c->wall != p.first[0]) return false; + forCellEx(c1, c) { + eWall found = p.first[idx++]; + if(c1->wall != found && c1->wall != waChasm) return false; + } + return true; + } + +vector gen_picks(cell *c, int& total, wfc_data& data) { + vector picks; + total = 0; + + for(auto& wp: data) { + if(!agree(c, wp)) continue; + picks.push_back(&wp); + total += wp.second; + } + + return picks; + } + +vector centers; + +EX void schedule(cell *c) { + centers.push_back(c); + } + +void generate_all() { + start_game(); + manual_celllister cl; + cl.add(cwt.at); + vector centers; + for(int i=0; impdist <= 7) + cl.add(c1); + else + incenter = false; + } + if(incenter) + centers.push_back(c); + } + + for(cell *c: cl.lst) c->wall = waChasm; + } + +string eclectic_c = string("\x78\xda\x8d\x99\x3b\x72\xa4\x30\x10\x86\x51\x24\xd2\x0d\x60\x12\xc5\x1b\x08\x12\x09\x88\x89\x18\xa5\x5b\x2e\x5f\xc2\xc9\x1c\xc1\x97\xf0\x25\x7c\x85\x0d\xf6\x12\x3e\x8f\x83\x9d\x31\x30\xe8\xd1\xfd\x37\x53\xa5\x82\x5a\x7f\xfb\x77\xeb\xd1\x4d\xd3\xfc\x52\x55\x55\x57\xf8\x77\x47\xaa\x4f\x91\x6a\xef\xc3\x8a\xd4\xe5\x14\x65\x4e\x51\xf6\x3e\xbe\x45\xaa\x3b\xa5\xe5\x4e\xcd\x71\x3a\x45\x2d\xa7\x2c\x5e\x4f\x69\x85\xfb\xf8\x12\xa9\xdb\x0f\xa5\x85\x3d\xfc\x07\x75\xd4\x36\x2c\xd4\x69\x45\xaf\xdb\x6d\x7c\x43\x9d\xc7\x39\x78\x83\x84\xf9\x21\x6a\x81\x30\xa2\xc7\x56\xd4\xb1\xcf\x53\xa2\x85\x73\xf4\x01\x75\xba\x4d\xcb\x8a\xd4\x63\xbc\x8a\x54\xff\x43\x21\x9f\x7a\xd1\xa7\xfe\x54\x04\xf4\xa7\xac\x3d\x62\xe4\xaf\xaa\x05\xe2\x31\xfe\x28\xa4\xe3\xef\xa3\x81\xfe\xf8\x6d\xe0\x1d\x19\x45\x8f\x1f\xf1\x3a\x43\x62\x11\xbd\xbd\x8a\x27\x55\x8e\xd1\xb0\x0d\xec\xed\xcb\xcf\x6e\x6a\x21\xce\x71\xf4\xdd\xb6\xc1\xaf\xdc\x1e\xe7\xbf\xd9\x9d\x54\xa7\xb2\xc1\x1e\xe7\xef\xac\x3f\x6d\x34\xf8\x99\x5f\x9e\xcf\x0e\x2d\xc4\x39\x1f\x31\xe6\x54\x36\x30\xdb\x89\x68\x58\x42\xce\x04\x7b\x8c\x37\x90\xe8\xe0\xb9\xdb\x63\x9b\x8f\x81\x2e\x8a\x49\xde\x97\x3d\xb6\x79\x9d\x3e\xc9\x4a\x5a\x8c\xfe\x5a\x20\x70\x26\xe9\x13\xaf\x6b\x90\x23\x8e\x4a\x40\x0b\x99\xe4\x03\xea\xb8\x6d\x47\xb1\x8e\x87\xfb\xe5\xb6\x4c\x82\x35\xf0\xb9\xf1\x1b\x85\x09\x9c\xf7\xc6\xe4\x0c\x23\x6a\x9f\x39\xef\xf1\x28\x7a\x3c\x3d\x3d\xae\x05\x02\x5b\x9a\x36\x6b\xbc\xa5\x65\xcb\x7d\x33\x6b\xe9\x1a\x65\x49\x2b\x52\x38\xbb\x85\xcd\xe2\x0c\x89\x00\x67\x1e\x22\x1d\x2b\x52\x01\x46\xce\x4d\xac\x09\x55\x51\x39\x6a\x40\x29\xd6\x73\x15\x51\x7c\xbc\xc7\x39\x99\xd6\x31\xa7\xaa\xab\x38\xe3\xd2\x7b\x6f\x93\x4c\xa7\x41\xa6\xb4\x22\xc1\x67\xb9\xfe\x54\x96\x8b\x33\x13\x6d\xc9\x45\x91\xc3\x13\xfb\xa0\x9f\x66\x3e\x22\x68\x8d\x21\x21\x6a\x86\x18\xc4\xc8\x1b\x23\x9f\x68\x4b\x4b\x74\x4a\x31\x71\x63\x7d\x59\x8a\x9a\xa2\x06\x71\x59\x41\x2a\x64\xff\x83\x9e\x57\x10\x33\x41\xc8\xa2\x8f\xd7\x59\x60\x9c\x87\x6c\xd0\x3a\x71\xf4\x36\x90\xb8\x92\x84\x8a\xf6\x69\x60\x89\x38\xb6\xcb\x2c\x99\x13\x74\x55\x16\xc7\xf5\x85\xb4\xb4\xff\xa5\x62\xcf\x9f\x21\xae\xa5\x25\x39\x3f\x98\x8c\x6a\x20\xc1\xfb\x62\x44\x5f\x6c\xb6\x2e\xa5\xce\x9e\x85\x70\x86\xcd\x89\xd2\x63\x17\xe5\x08\x43\x5a\x8a\xf3\xc3\xf1\xfe\x82\xa8\x3d\xd6\x79\xca\x47\x4f\xd9\x92\x9a\x88\xbc\x53\xce\x6d\x8a\x3c\x57\xe4\xdc\x96\x2c\x2e\x68\x22\x7d\x2a\x6a\x90\x05\x68\x8d\x10\x45\xef\x95\x5c\xc1\x20\xc6\xa5\x22\xae\xe9\xba\x50\x84\x22\x75\x72\xe2\x9d\x24\x14\x9b\x69\xa9\x68\xe1\x89\x7d\xcc\x90\xa0\xad\xe4\x54\x9a\x23\x0c\x11\x4f\xae\x58\x97\x9d\x88\xeb\xc7\x32\xa6\x5c\x76\xd2\xcb\x1a\x32\x3f\x99\x63\xb1\x93\x79\x0c\x54\x64\x8d\x1e\x57\xef\x74\x1d\xe1\x33\x5f\x46\x48\xd0\x35\xba\xcf\x3c\xc5\xc4\x44\xce\xc7\x13\xbe\x94\xf3\x99\x88\x99\xa7\x3a\x4b\x16\x03\xe5\x09\x57\xcc\x38\x76\xb2\x05\xe3\xd0\x71\x99\xd7\xa5\x3f\x01\x8c\xd5\x9a\x12\x3a\x85\xab\x35\x05\x88\x86\xd5\x89\x23\xef\x8b\xa4\x54\x76\xff\x45\x5a\x8b\x75\x3e\xa0\x8e\x62\xbd\xce\xef\xdf\x48\xa2\xac\xbe\x5b\xa6\xb6\xae\x92\xdd\x68\x01\xf1\x4a\xea\xb4\xd9\x4e\x5b\x92\xba\x14\x7d\x77\x0d\xa8\xf6\x79\xea\x5b\xc2\xd2\x7e\x3f\xb3\x44\x0b\x35\x5a\xd6\x0a\x75\x5d\x57\xef\x22\x7e\x47\xd0\x80\x3a\x74\x8c\xf8\x0d\x41\x03\xea\x78\xd3\x36\xe0\x3d\x68\x82\x3a\x26\xa9\x07\x0c\xa3\xb1\xdf\x7f\x92\x3a\x71\x7e\x9f\x0b\xc2\x88\x1e\x1b\xb6\x27\x6e\xc4\xaa\xcc\xc0\x1a\x47\x33\xcf\xb9\x34\xf3\xdb\x53\xdd\x34\x0b\x7a\x5d\x6f\x2c\x91\xbe\x1f\x76\xa0\xf3\xb4\xfa\xd2\x09\xbd\x29\x4b\x52\xb6\xb0\xa8\x01\xc5\x5b\xdb\xdf\x77\xd3\x77\xf0\x4e\x7c\x1b\xed\x80\x2f\xa5\x25\x9b\x55\xad\xb4\xc7\x1d\x9c\x93\xcd\xe6\x73\x58\xea\x85\xef\x04\xab\x4e\x0f\x3a\x96\x0d\x49\xf4\x89\x25\x44\xd0\x1a\x5d\xb1\xdf\x14\xd1\x89\x44\x6a\x25\x9f\x71\x4f\x9e\x19\xcd\xfc\xa5\x12\x09\x7a\xc6\x1d\x39\xe3\x3a\xe2\x7b\x66\xac\xe7\xd7\x09\xdf\x60\xd6\x5a\xd6\x01\x62\x66\x89\xf4\x4d\xc2\xc1\x6e\x27\xe5\x8b\x89\xfe\x8d\xd7\x49\x89\x9a\x21\x86\xa4\x3f\x4b\x51\x53\x56\xf3\xf0\x94\x8b\x3a\x2c\xa5\x4f\xf9\x3d\x4f\xf8\xe7\x0a\xd6\x02\x41\x7b\x9d\x53\x13\xb4\x36\xb0\x6b\xed\x88\x15\xa0\x75\x4c\xf1\x0e\xe3\x98\x7a\x9d\x27\x7c\x72\x76\x6a\x40\x78\xe8\x4b\xd9\x23\x73\x44\xa7\x9d\xf6\x96\xba\xd2\xc4\x58\x74\xfe\x39\x62\x60\x89\x29\xd1\xa8\x33\xc2\x11\x6f\x4c\xe9\x8c\x3d\xd1\x13\x58\x75\x3c\x11\x4f\x65\xe4\x79\xe6\xc9\x7c\x9c\x72\x0f\xde\xdc\x3c\xa9\x93\xdf\x37\x2c\xe1\x92\xbd\x46\xc4\xe1\xf1\xc0\xc4\xd3\xb1\xc6\x03\xe3\xed\xf0\x5c\x9b\x11\x64\xb6\xe9\xd9\x79\x1d\x41\xde\x6a\x0a\x82\xae\x6b\x26\x90\x1d\x31\x71\x7c\x2d\x9a\xc4\xfc\x39\x31\xb5\xde\x11\xd3\x4b\x85\x7f\x56\xa4\x8e\x2f\xcb\x0b\x20\x66\x56\x27\xed\xba\xea\xac\x9b\x93\xff\xd6\x27\x47\x00\x44\x03\x89\xc3\x97\x20\xf4\xad\x2d\x4b\x85\xa2\x9b\x17\xc4\xfe\x77\x00\x1a\x98\x38\xd6\x37\x80\xae\x59\xea\xc7\x42\x68\xa4\x56\xa8\xeb\xba\x2e\x2f\xe2\x89\xd0\x51\xe7\x99\xdf\xa3\x1b\xf8\x26\x65\xab\xff\xa2\xf7\xca\x99", 1190); + +wfc_data& eclectic_data() { + static wfc_data d = gen_decompressed(deserialize(decompress_string(eclectic_c))); + return d; + } + +EX bool use_eclectic = true; + +EX void invoke() { + wfc_data& d = use_eclectic ? eclectic_data() : probs; + + while(isize(centers)) { + int pos = -1; + ld best_entropy = 1e9; + for(int p=0; psecond * log(total * 1. / p->second) / total; + + if(entropy < best_entropy) best_entropy = entropy, pos = p; + } + + cell *c = centers[pos]; + centers[pos] = centers.back(); + centers.pop_back(); + + // println(hlog, "chosen ", c, " at entropy ", best_entropy, " in distance ", c->mpdist); + + int total; + auto picks = gen_picks(c, total, d); + + if(total) total = hrand(total); + for(auto pp: picks) { + auto& p = *pp; + total -= p.second; + if(total < 0) { + int idx = 1; + c->wall = p.first[0]; + forCellEx(c1, c) c1->wall = p.first[idx++]; + break; + } + } + + } + + } + +void use_probs() { + stop_game(); + use_eclectic = false; + firstland = specialland = laEclectic; + start_game(); + } + +EX void wfc_menu() { + cmode = sm::SIDE | sm::MAYDARK; + gamescreen(1); + dialog::init(XLAT("Wave Function Collapse")); + + dialog::addSelItem(XLAT("import the current map"), XLAT("rules: %1", its(isize(probs))), 'i'); + dialog::add_action(load_probs); + + dialog::addBoolItem(XLAT("generate a map using WFC"), !use_eclectic, 'g'); + if(probs.empty()) dialog::lastItem().value = "(no rules)"; + + dialog::add_action([] { + if(probs.empty()) return; + if(use_eclectic) { + use_probs(); + } + else { + stop_game(); + use_eclectic = true; + start_game(); + } + }); + + dialog::addBack(); + dialog::display(); + } + +auto wfc_hook = + // addHook(hooks_handleKey, 100, wfc_handleKey) + + addHook(hooks_args, 100, [] { + using namespace arg; + + if(0) ; + else if(argis("-wfc-load")) { + load_probs(); + } + else if(argis("-wfc-c")) { + println(hlog, as_cstring(compress_string(serialize(gen_compressed(probs))))); + } + else if(argis("-wfc-use")) { + use_probs(); + } + + else return 1; + return 0; + }); + +EX } + +}