#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); void extend(wfc_data& res, vector v, int val, int sides) { if(isize(v) == sides+1) res[v] += val; else if(isize(v) < sides + 1) { for(int i=1; i(decompress_string(eclectic_c))); if(geometry == gNormal && !PURE) return d; if(geometry == gNormal && PURE) { static wfc_data dpure = renumerate(d, 7); return dpure; } if(geometry == gEuclid) { static wfc_data deuc = renumerate(d, 6); return deuc; } 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 } }