code wiki / (root) / nx_games_page.nx

nx_games_page.nx source

↩ module page · 181 lines · 17034 B

1// nx_games_page.nx -- EMIT the live nishifamily.com/games portal page. 2// A sovereign games portal: classic ABSTRACT/board games built from scratch 3// (rules are not copyrightable -> clean original implementations, no ROMs 4// rehosted, no third-party code). Rung 1 ships two fully-playable in-browser 5// games: Tic-Tac-Toe (unbeatable minimax) and Connect Four (alpha-beta AI), 6// plus an honest roadmap grid. Publishing is the separate sovereign step 7// nx_aw_push (additive, vault-backed) -- the shared loop every property reuses. 8// No JS/HTML is hand-written elsewhere; this organ authors the page. 9// license_tier: ORIGINAL 10// 11// genealogy_id: nishi_games_portal 12// lineage_id: nx_games_page_v1 13 14import "nx_syscalls.nx" 15 16const GM_OUT: *u8 = "web_assets/games.html" 17const GM_CAP: i64 = 262144 18 19// append a NUL-terminated string into dst at off; returns new off. 20func gm_cat(dst: *u8, off: i64, s: *u8) -> i64 { 21 var o: i64 = off 22 var i: i64 = 0 23 while s[i] != 0 as u8 { dst[o] = s[i]; o = o + 1; i = i + 1 } 24 return o 25} 26 27// append a signed decimal into dst at off; returns new off. 28func gm_catn(dst: *u8, off: i64, v: i64) -> i64 { 29 var o: i64 = off 30 var n: i64 = v 31 if n < 0 { dst[o] = 45 as u8; o = o + 1; n = 0 - n } 32 let tmp: *u8 = sys_mmap(32) 33 var k: i64 = 0 34 if n == 0 { tmp[0] = 48 as u8; k = 1 } 35 while n > 0 { tmp[k] = (48 + (n % 10)) as u8; n = n / 10; k = k + 1 } 36 var j: i64 = k - 1 37 while j >= 0 { dst[o] = tmp[j]; o = o + 1; j = j - 1 } 38 return o 39} 40 41func main() -> i64 { 42 let now: i64 = sys_now_realtime_sec() 43 let h: *u8 = sys_mmap(GM_CAP) 44 var w: i64 = 0 45 46 // ---------- head + style ---------- 47 w = gm_cat(h, w, "<!doctype html><html lang='en'><head><meta charset='utf-8'>\n" as *u8) 48 w = gm_cat(h, w, "<meta name='viewport' content='width=device-width,initial-scale=1'>\n" as *u8) 49 w = gm_cat(h, w, "<title>Nishi Games &mdash; sovereign games portal</title>\n" as *u8) 50 w = gm_cat(h, w, "<style>\n" as *u8) 51 w = gm_cat(h, w, ":root{--nx-color-bg:#0b0f14;--nx-color-ink:#e6edf3;--nx-color-primary:#4cc2ff;--nx-color-line:#243140;--nx-color-muted:#8aa0b4;--bg:var(--nx-color-bg);--panel:#131b24;--ink:var(--nx-color-ink);--mut:var(--nx-color-muted);--acc:var(--nx-color-primary);--good:#3fb950;--bad:#f85149;--line:var(--nx-color-line)}\n" as *u8) 52 w = gm_cat(h, w, "*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.55 system-ui,Segoe UI,Roboto,sans-serif}\n" as *u8) 53 w = gm_cat(h, w, ":focus-visible{outline:2px solid var(--nx-color-primary);outline-offset:2px}@media(max-width:640px){header,main,footer{padding-left:16px;padding-right:16px}#ttt{grid-template-columns:repeat(3,60px)}#ttt .cell{width:60px;height:60px}}@media(prefers-reduced-motion:reduce){*{transition:none}}\n" as *u8) 54 w = gm_cat(h, w, "header{padding:32px 20px 6px;max-width:980px;margin:0 auto}h1{margin:0;font-size:30px;letter-spacing:.3px}\n" as *u8) 55 w = gm_cat(h, w, ".sub{color:var(--mut);margin:.5em 0 0;max-width:74ch}.stamp{color:var(--mut);font-size:12.5px;margin:.6em 0 0}\n" as *u8) 56 w = gm_cat(h, w, ".note{background:#0e1722;border:1px solid var(--line);border-left:3px solid var(--acc);border-radius:8px;padding:10px 14px;margin:14px 0;color:var(--mut);font-size:13px;max-width:82ch}\n" as *u8) 57 w = gm_cat(h, w, "main{max-width:980px;margin:0 auto;padding:0 20px 60px}section{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:18px 20px;margin:18px 0}\n" as *u8) 58 w = gm_cat(h, w, "h2{margin:.1em 0 .4em;font-size:20px}code{color:var(--acc);font-size:.92em}.muted{color:var(--mut);font-weight:400;font-size:14px}\n" as *u8) 59 w = gm_cat(h, w, "button{font:inherit;cursor:pointer}.btn{background:#1b2733;color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:7px 14px}.btn:hover{border-color:var(--acc)}\n" as *u8) 60 w = gm_cat(h, w, ".status{margin:8px 0 0;color:var(--mut);min-height:1.4em}\n" as *u8) 61 w = gm_cat(h, w, "#ttt{display:grid;grid-template-columns:repeat(3,72px);grid-gap:6px;margin:10px 0}\n" as *u8) 62 w = gm_cat(h, w, "#ttt .cell{width:72px;height:72px;font-size:34px;font-weight:700;background:#0e1722;color:var(--ink);border:1px solid var(--line);border-radius:8px}#ttt .cell:hover{border-color:var(--acc)}\n" as *u8) 63 w = gm_cat(h, w, "#c4{display:inline-grid;grid-template-columns:repeat(7,46px);grid-gap:5px;background:#0e2a4a;padding:8px;border-radius:10px;margin:10px 0}\n" as *u8) 64 w = gm_cat(h, w, "#c4 .disc{width:46px;height:46px;border-radius:50%;background:#0b0f14;border:1px solid #16385e;cursor:pointer}#c4 .disc:hover{border-color:var(--acc)}\n" as *u8) 65 w = gm_cat(h, w, "#c4 .r{background:var(--bad);border-color:var(--bad)}#c4 .y{background:#f5c518;border-color:#f5c518}\n" as *u8) 66 w = gm_cat(h, w, ".grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));grid-gap:12px;margin:10px 0}\n" as *u8) 67 w = gm_cat(h, w, ".card{background:#0e1722;border:1px solid var(--line);border-radius:10px;padding:14px}.card h3{margin:0 0 4px;font-size:15px}.card .s{font-size:12px;color:var(--mut)}\n" as *u8) 68 w = gm_cat(h, w, ".tag{display:inline-block;font-size:11px;padding:2px 8px;border-radius:20px;margin-top:9px}.live{background:#10261a;color:var(--good)}.soon{background:#1b2230;color:var(--mut)}.eng{background:#0e2233;color:var(--acc)}\n" as *u8) 69 w = gm_cat(h, w, "footer{max-width:980px;margin:0 auto;padding:8px 20px 40px;color:var(--mut);font-size:12.5px}a{color:var(--acc)}\n" as *u8) 70 w = gm_cat(h, w, "</style></head><body>\n" as *u8) 71 72 // ---------- header ---------- 73 w = gm_cat(h, w, "<header><h1>Nishi Games</h1>\n" as *u8) 74 w = gm_cat(h, w, "<p class='sub'>A sovereign games portal &mdash; classic abstract and board games, built from scratch on the Nishi stack and playable right here in your browser. No accounts, no trackers, no third-party code.</p>\n" as *u8) 75 w = gm_cat(h, w, "<div class='note'><b>How these are built:</b> every game here is an original implementation. The <i>rules</i> of chess, go, checkers, tic-tac-toe and Chinese checkers are not copyrightable, so we build them clean &mdash; no ROMs are rehosted and no third-party code runs. Genuinely free / public-domain / freely-licensed ROMs may be added later, but only behind a verified-license provenance gate: &ldquo;available on archive.org&rdquo; is not the same as &ldquo;legal to rehost,&rdquo; so nothing ships here without a confirmed free license.</div>\n" as *u8) 76 w = gm_cat(h, w, "<p class='stamp'>Page emitted live by <code>nx_games_page</code> at epoch " as *u8) 77 w = gm_catn(h, w, now) 78 w = gm_cat(h, w, " &middot; additive publish via <code>nx_aw_push</code>.</p></header>\n" as *u8) 79 80 w = gm_cat(h, w, "<main>\n" as *u8) 81 82 // ---------- Tic-Tac-Toe ---------- 83 w = gm_cat(h, w, "<section><h2>Tic-Tac-Toe <span class='muted'>&middot; vs unbeatable AI</span></h2>\n" as *u8) 84 w = gm_cat(h, w, "<p class='muted'>You are X and move first. The AI plays a full minimax search, so it never loses &mdash; the best you can force is a draw.</p>\n" as *u8) 85 w = gm_cat(h, w, "<div id='ttt'></div>\n" as *u8) 86 w = gm_cat(h, w, "<div class='status' id='ttt-status'>Your move (X).</div>\n" as *u8) 87 w = gm_cat(h, w, "<p><button class='btn' onclick='treset()'>New game</button></p></section>\n" as *u8) 88 89 // ---------- Connect Four ---------- 90 w = gm_cat(h, w, "<section><h2>Connect Four <span class='muted'>&middot; vs alpha-beta AI</span></h2>\n" as *u8) 91 w = gm_cat(h, w, "<p class='muted'>You are red. Click any cell in a column to drop a disc; get four in a row &mdash; horizontal, vertical or diagonal. The AI searches 5 plies with alpha-beta pruning.</p>\n" as *u8) 92 w = gm_cat(h, w, "<div id='c4'></div>\n" as *u8) 93 w = gm_cat(h, w, "<div class='status' id='c4-status'>Your move &mdash; you are red.</div>\n" as *u8) 94 w = gm_cat(h, w, "<p><button class='btn' onclick='creset()'>New game</button></p></section>\n" as *u8) 95 96 // ---------- roadmap grid ---------- 97 w = gm_cat(h, w, "<section><h2>On the board &amp; coming next</h2>\n" as *u8) 98 w = gm_cat(h, w, "<div class='grid'>\n" as *u8) 99 w = gm_cat(h, w, "<div class='card'><h3>Tic-Tac-Toe</h3><div class='s'>Unbeatable minimax</div><span class='tag live'>Playable</span></div>\n" as *u8) 100 w = gm_cat(h, w, "<div class='card'><h3>Connect Four</h3><div class='s'>Alpha-beta, 5-ply</div><span class='tag live'>Playable</span></div>\n" as *u8) 101 w = gm_cat(h, w, "<div class='card'><h3>Checkers</h3><div class='s'>Engine exists (nx_checkers) &mdash; wiring to the browser next</div><span class='tag eng'>Engine ready</span></div>\n" as *u8) 102 w = gm_cat(h, w, "<div class='card'><h3>Chess</h3><div class='s'>Sovereign engine + board UI</div><span class='tag soon'>Coming soon</span></div>\n" as *u8) 103 w = gm_cat(h, w, "<div class='card'><h3>Go</h3><div class='s'>9&times;9 then 19&times;19, scoring + bot</div><span class='tag soon'>Coming soon</span></div>\n" as *u8) 104 w = gm_cat(h, w, "<div class='card'><h3>Chinese Checkers</h3><div class='s'>2&ndash;6 players</div><span class='tag soon'>Coming soon</span></div>\n" as *u8) 105 w = gm_cat(h, w, "<div class='card'><h3>Reversi / Othello</h3><div class='s'>Minimax bot</div><span class='tag soon'>Coming soon</span></div>\n" as *u8) 106 w = gm_cat(h, w, "</div></section>\n" as *u8) 107 108 w = gm_cat(h, w, "</main>\n" as *u8) 109 w = gm_cat(h, w, "<footer>Sovereign Nishi stack &middot; emitted by <code>nx_games_page</code> &middot; published via <code>nx_aw_push</code> &middot; original implementations, no ROMs &middot; nishifamily.com/games</footer>\n" as *u8) 110 111 // ---------- game scripts (single-quoted JS, no backslashes) ---------- 112 w = gm_cat(h, w, "<script>\n" as *u8) 113 // --- tic tac toe --- 114 w = gm_cat(h, w, "var TB=[],TO=false,CB=[],CO=false;\n" as *u8) 115 w = gm_cat(h, w, "var TW=[[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]];\n" as *u8) 116 w = gm_cat(h, w, "function twin(b,p){for(var i=0;i<8;i++){var l=TW[i];if(b[l[0]]==p&&b[l[1]]==p&&b[l[2]]==p)return true;}return false;}\n" as *u8) 117 w = gm_cat(h, w, "function tfull(b){for(var i=0;i<9;i++)if(b[i]=='')return false;return true;}\n" as *u8) 118 w = gm_cat(h, w, "function tmm(b,pl){if(twin(b,'O'))return{s:10};if(twin(b,'X'))return{s:-10};if(tfull(b))return{s:0};\n" as *u8) 119 w = gm_cat(h, w, "var best=pl=='O'?{s:-999}:{s:999};for(var i=0;i<9;i++){if(b[i]==''){b[i]=pl;var r=tmm(b,pl=='O'?'X':'O');b[i]='';\n" as *u8) 120 w = gm_cat(h, w, "if(pl=='O'){if(r.s>best.s)best={s:r.s,m:i};}else{if(r.s<best.s)best={s:r.s,m:i};}}}return best;}\n" as *u8) 121 w = gm_cat(h, w, "function trender(){var e=document.getElementById('ttt');e.innerHTML='';for(var i=0;i<9;i++){var c=document.createElement('button');\n" as *u8) 122 w = gm_cat(h, w, "c.className='cell';c.textContent=TB[i];(function(k){c.onclick=function(){tplay(k);};})(i);e.appendChild(c);}}\n" as *u8) 123 w = gm_cat(h, w, "function tstat(t){document.getElementById('ttt-status').textContent=t;}\n" as *u8) 124 w = gm_cat(h, w, "function tcheck(){trender();if(twin(TB,'X')){tstat('You win! The AI blundered.');TO=true;return true;}\n" as *u8) 125 w = gm_cat(h, w, "if(twin(TB,'O')){tstat('AI wins.');TO=true;return true;}if(tfull(TB)){tstat('Draw - perfect play.');TO=true;return true;}return false;}\n" as *u8) 126 w = gm_cat(h, w, "function tplay(i){if(TO||TB[i]!='')return;TB[i]='X';if(tcheck())return;var m=tmm(TB.slice(),'O').m;if(m!=undefined)TB[m]='O';\n" as *u8) 127 w = gm_cat(h, w, "if(!tcheck())tstat('Your move (X). The AI is unbeatable - aim for a draw.');}\n" as *u8) 128 w = gm_cat(h, w, "function treset(){TB=['','','','','','','','',''];TO=false;trender();tstat('Your move (X). The AI is unbeatable - aim for a draw.');}\n" as *u8) 129 // --- connect four --- 130 w = gm_cat(h, w, "function cidx(r,c){return r*7+c;}\n" as *u8) 131 w = gm_cat(h, w, "function cdrop(b,c){for(var r=5;r>=0;r--){if(b[cidx(r,c)]=='')return r;}return -1;}\n" as *u8) 132 w = gm_cat(h, w, "function cwin(b,p){var r,c;\n" as *u8) 133 w = gm_cat(h, w, "for(r=0;r<6;r++)for(c=0;c<4;c++)if(b[cidx(r,c)]==p&&b[cidx(r,c+1)]==p&&b[cidx(r,c+2)]==p&&b[cidx(r,c+3)]==p)return true;\n" as *u8) 134 w = gm_cat(h, w, "for(c=0;c<7;c++)for(r=0;r<3;r++)if(b[cidx(r,c)]==p&&b[cidx(r+1,c)]==p&&b[cidx(r+2,c)]==p&&b[cidx(r+3,c)]==p)return true;\n" as *u8) 135 w = gm_cat(h, w, "for(r=0;r<3;r++)for(c=0;c<4;c++)if(b[cidx(r,c)]==p&&b[cidx(r+1,c+1)]==p&&b[cidx(r+2,c+2)]==p&&b[cidx(r+3,c+3)]==p)return true;\n" as *u8) 136 w = gm_cat(h, w, "for(r=3;r<6;r++)for(c=0;c<4;c++)if(b[cidx(r,c)]==p&&b[cidx(r-1,c+1)]==p&&b[cidx(r-2,c+2)]==p&&b[cidx(r-3,c+3)]==p)return true;\n" as *u8) 137 w = gm_cat(h, w, "return false;}\n" as *u8) 138 w = gm_cat(h, w, "function cfull(b){for(var c=0;c<7;c++)if(cdrop(b,c)>=0)return false;return true;}\n" as *u8) 139 w = gm_cat(h, w, "function cwscore(a,p){var o=p=='y'?'r':'y',cp=0,co=0,ce=0,i;for(i=0;i<4;i++){if(a[i]==p)cp++;else if(a[i]==o)co++;else ce++;}\n" as *u8) 140 w = gm_cat(h, w, "if(cp>0&&co>0)return 0;if(cp==4)return 1000;if(co==4)return -1000;if(cp==3&&ce==1)return 50;if(cp==2&&ce==2)return 8;if(co==3&&ce==1)return -75;if(co==2&&ce==2)return -6;return 0;}\n" as *u8) 141 w = gm_cat(h, w, "function cheur(b,p){var s=0,r,c;for(r=0;r<6;r++)if(b[cidx(r,3)]==p)s+=6;\n" as *u8) 142 w = gm_cat(h, w, "for(r=0;r<6;r++)for(c=0;c<4;c++)s+=cwscore([b[cidx(r,c)],b[cidx(r,c+1)],b[cidx(r,c+2)],b[cidx(r,c+3)]],p);\n" as *u8) 143 w = gm_cat(h, w, "for(c=0;c<7;c++)for(r=0;r<3;r++)s+=cwscore([b[cidx(r,c)],b[cidx(r+1,c)],b[cidx(r+2,c)],b[cidx(r+3,c)]],p);\n" as *u8) 144 w = gm_cat(h, w, "for(r=0;r<3;r++)for(c=0;c<4;c++)s+=cwscore([b[cidx(r,c)],b[cidx(r+1,c+1)],b[cidx(r+2,c+2)],b[cidx(r+3,c+3)]],p);\n" as *u8) 145 w = gm_cat(h, w, "for(r=3;r<6;r++)for(c=0;c<4;c++)s+=cwscore([b[cidx(r,c)],b[cidx(r-1,c+1)],b[cidx(r-2,c+2)],b[cidx(r-3,c+3)]],p);return s;}\n" as *u8) 146 w = gm_cat(h, w, "var CORD=[3,2,4,1,5,0,6];\n" as *u8) 147 w = gm_cat(h, w, "function cmm(b,d,al,be,mx){if(cwin(b,'y'))return{s:100000-(5-d)};if(cwin(b,'r'))return{s:-100000+(5-d)};if(cfull(b)||d==0)return{s:cheur(b,'y')};\n" as *u8) 148 w = gm_cat(h, w, "var k,c,r,v;if(mx){var best={s:-1e9,m:-1};for(k=0;k<7;k++){c=CORD[k];r=cdrop(b,c);if(r<0)continue;b[cidx(r,c)]='y';v=cmm(b,d-1,al,be,false).s;b[cidx(r,c)]='';\n" as *u8) 149 w = gm_cat(h, w, "if(v>best.s)best={s:v,m:c};if(v>al)al=v;if(al>=be)break;}return best;}\n" as *u8) 150 w = gm_cat(h, w, "var bes={s:1e9,m:-1};for(k=0;k<7;k++){c=CORD[k];r=cdrop(b,c);if(r<0)continue;b[cidx(r,c)]='r';v=cmm(b,d-1,al,be,true).s;b[cidx(r,c)]='';\n" as *u8) 151 w = gm_cat(h, w, "if(v<bes.s)bes={s:v,m:c};if(v<be)be=v;if(al>=be)break;}return bes;}\n" as *u8) 152 w = gm_cat(h, w, "function crender(){var e=document.getElementById('c4');e.innerHTML='';for(var i=0;i<42;i++){var d=document.createElement('div');\n" as *u8) 153 w = gm_cat(h, w, "d.className='disc'+(CB[i]=='r'?' r':'')+(CB[i]=='y'?' y':'');(function(col){d.onclick=function(){cplay(col);};})(i%7);e.appendChild(d);}}\n" as *u8) 154 w = gm_cat(h, w, "function cstat(t){document.getElementById('c4-status').textContent=t;}\n" as *u8) 155 w = gm_cat(h, w, "function ccheck(){crender();if(cwin(CB,'r')){cstat('Red (you) win! Nicely done.');CO=true;return true;}\n" as *u8) 156 w = gm_cat(h, w, "if(cwin(CB,'y')){cstat('Yellow (AI) wins.');CO=true;return true;}if(cfull(CB)){cstat('Board full - a draw.');CO=true;return true;}return false;}\n" as *u8) 157 w = gm_cat(h, w, "function cplay(c){if(CO)return;var r=cdrop(CB,c);if(r<0)return;CB[cidx(r,c)]='r';if(ccheck())return;cstat('AI thinking...');\n" as *u8) 158 w = gm_cat(h, w, "setTimeout(function(){var m=cmm(CB.slice(),5,-1e9,1e9,true).m;if(m<0){for(var c2=0;c2<7;c2++)if(cdrop(CB,c2)>=0){m=c2;break;}}\n" as *u8) 159 w = gm_cat(h, w, "var rr=cdrop(CB,m);if(rr>=0)CB[cidx(rr,m)]='y';if(!ccheck())cstat('Your move - click a column to drop a red disc.');},30);}\n" as *u8) 160 w = gm_cat(h, w, "function creset(){CB=[];for(var i=0;i<42;i++)CB.push('');CO=false;crender();cstat('Your move - click a column to drop a red disc (you are red).');}\n" as *u8) 161 w = gm_cat(h, w, "treset();creset();\n" as *u8) 162 w = gm_cat(h, w, "</script>\n" as *u8) 163 w = gm_cat(h, w, "</body></html>\n" as *u8) 164 165 // ---------- write ---------- 166 let fd: i64 = sys_openat_wr(GM_OUT, 0x1a4) 167 if fd < 0 { sys_write(2, "FATAL: cannot open web_assets/games.html\n" as *u8, 41); sys_exit(2); return 2 } 168 sys_write(fd, h, w) 169 sys_close(fd) 170 sys_write(1, "[nx_games_page] wrote games.html bytes=" as *u8, 39) 171 let nb: *u8 = sys_mmap(28) 172 var k2: i64 = 0 173 var m2: i64 = w 174 if m2 == 0 { nb[0] = 48 as u8; k2 = 1 } 175 while m2 > 0 { nb[k2] = (48 + (m2 % 10)) as u8; m2 = m2 / 10; k2 = k2 + 1 } 176 var qd: i64 = k2 - 1 177 while qd >= 0 { let o1: *u8 = sys_mmap(1); o1[0] = nb[qd]; sys_write(1, o1, 1); qd = qd - 1 } 178 sys_write(1, "\n" as *u8, 1) 179 sys_exit(0) 180 return 0 181}