code wiki / (root) / nx_games_js.nx

nx_games_js.nx source

↩ module page · 123 lines · 10193 B

1// nx_games_js.nx -- LIB (no main): the /games portal's shared emit helpers plus the whole client 2// script block. Split out of nx_games_page.nx 2026-08-10. 3// 4// WHY THE SPLIT. Two reasons, in order of merit: 5// 1. SINGLE RESPONSIBILITY (rule 9). The page organ authors document structure, tokens and copy; 6// this organ authors game behaviour. One file was doing both and had grown past 31 KB. 7// 2. It also happens to fix a transport ceiling: the estate's nx_fs_write lane could not carry a 8// 31 KB payload (eight verified attempts, artifact unchanged every time), while both halves of 9// the split carry first try. That is a side benefit, NOT the justification -- a split made only 10// to dodge a transport limit would be a hack; this one is the decomposition the file was owed. 11// 12// SUDOKU IS NO LONGER HERE (2026-08-10). It became a sovereign NishiLang module compiled to 13// WebAssembly (nx_sudoku_wasm) and served at /games/sudoku, where the page holds no game logic at 14// all. Its JS, its bank parser and the GJS_ROW record constant went with it: keeping a second 15// implementation of the same ruleset in JavaScript would have been the duplicate-ruler defect. 16// Connect Four and Tic-Tac-Toe are still JS here and are owed the same treatment. 17// license_tier: ORIGINAL 18// 19// genealogy_id: nishi_games_portal 20// lineage_id: nx_games_js_v2 21import "nx_syscalls.nx" 22 23// append a NUL-terminated string into dst at off; returns new off. 24func gm_cat(dst: *u8, off: i64, s: *u8) -> i64 { 25 var o: i64 = off 26 var i: i64 = 0 27 while s[i] != 0 as u8 { dst[o] = s[i]; o = o + 1; i = i + 1 } 28 return o 29} 30 31// append a signed decimal into dst at off; returns new off. 32func gm_catn(dst: *u8, off: i64, v: i64) -> i64 { 33 var o: i64 = off 34 var n: i64 = v 35 if n < 0 { dst[o] = 45 as u8; o = o + 1; n = 0 - n } 36 let tmp: *u8 = sys_mmap(32) 37 var k: i64 = 0 38 if n == 0 { tmp[0] = 48 as u8; k = 1 } 39 while n > 0 { tmp[k] = (48 + (n % 10)) as u8; n = n / 10; k = k + 1 } 40 var j: i64 = k - 1 41 while j >= 0 { dst[o] = tmp[j]; o = o + 1; j = j - 1 } 42 return o 43} 44 45// gm_slurp was removed with the bank: nothing on this page reads a data file any more, and a defined 46// function nobody calls is what nx_unwired exists to catch. 47 48// one inventory card. state: 0 playable-here 1 engine-demo 2 not-started 49func gm_card(dst: *u8, off: i64, name: *u8, note: *u8, href: *u8, state: i64) -> i64 { 50 var w: i64 = off 51 if state == 1 { w = gm_cat(dst, w, "<a class='card' href='" as *u8); w = gm_cat(dst, w, href); w = gm_cat(dst, w, "'>" as *u8) } 52 if state != 1 { w = gm_cat(dst, w, "<div class='card'>" as *u8) } 53 w = gm_cat(dst, w, "<h3>" as *u8); w = gm_cat(dst, w, name); w = gm_cat(dst, w, "</h3>" as *u8) 54 w = gm_cat(dst, w, "<div class='s'>" as *u8); w = gm_cat(dst, w, note); w = gm_cat(dst, w, "</div>" as *u8) 55 if state == 0 { w = gm_cat(dst, w, "<span class='tag live'>Playable here</span>" as *u8) } 56 if state == 1 { w = gm_cat(dst, w, "<span class='tag eng'>Engine demo &rarr;</span>" as *u8) } 57 if state == 2 { w = gm_cat(dst, w, "<span class='tag soon'>Not started</span>" as *u8) } 58 if state == 1 { w = gm_cat(dst, w, "</a>" as *u8) } 59 if state != 1 { w = gm_cat(dst, w, "</div>" as *u8) } 60 w = gm_cat(dst, w, "\n" as *u8) 61 return w 62} 63 64// the remaining client script block: Connect Four and Tic-Tac-Toe only. 65// single-quoted JS only, no backslashes (the nx lexer is happiest that way). 66func gjs_scripts(h: *u8, w0: i64) -> i64 { 67 var w: i64 = w0 68 69 // ---------------- connect four ---------------- 70 w = gm_cat(h, w, "var CB=[],CO=false;\n" as *u8) 71 w = gm_cat(h, w, "function cidx(r,c){return r*7+c;}\n" as *u8) 72 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) 73 w = gm_cat(h, w, "function cwin(b,p){var r,c;\n" as *u8) 74 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) 75 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) 76 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) 77 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) 78 w = gm_cat(h, w, "return false;}\n" as *u8) 79 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) 80 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) 81 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) 82 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) 83 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) 84 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) 85 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) 86 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) 87 w = gm_cat(h, w, "var CORD=[3,2,4,1,5,0,6];\n" as *u8) 88 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) 89 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) 90 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) 91 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) 92 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) 93 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('button');\n" as *u8) 94 w = gm_cat(h, w, "d.className='disc'+(CB[i]=='r'?' r':'')+(CB[i]=='y'?' y':'');d.setAttribute('aria-label','column '+((i%7)+1));\n" as *u8) 95 w = gm_cat(h, w, "(function(col){d.onclick=function(){cplay(col);};})(i%7);e.appendChild(d);}}\n" as *u8) 96 w = gm_cat(h, w, "function cstat(t){document.getElementById('c4-status').textContent=t;}\n" as *u8) 97 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) 98 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) 99 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) 100 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) 101 w = gm_cat(h, w, "var rr=cdrop(CB,m);if(rr>=0)CB[cidx(rr,m)]='y';if(!ccheck())cstat('Your move - tap a column to drop a red disc.');},30);}\n" as *u8) 102 w = gm_cat(h, w, "function creset(){CB=[];for(var i=0;i<42;i++)CB.push('');CO=false;crender();cstat('Your move - tap a column to drop a red disc (you are red).');}\n" as *u8) 103 104 // ---------------- tic tac toe ---------------- 105 w = gm_cat(h, w, "var TB=[],TO=false;\n" as *u8) 106 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) 107 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) 108 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) 109 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) 110 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) 111 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) 112 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) 113 w = gm_cat(h, w, "c.className='cell';c.textContent=TB[i];c.setAttribute('aria-label','square '+(i+1));(function(k){c.onclick=function(){tplay(k);};})(i);e.appendChild(c);}}\n" as *u8) 114 w = gm_cat(h, w, "function tstat(t){document.getElementById('ttt-status').textContent=t;}\n" as *u8) 115 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) 116 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) 117 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) 118 w = gm_cat(h, w, "if(!tcheck())tstat('Your move (X). The AI is unbeatable - aim for a draw.');}\n" as *u8) 119 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) 120 121 w = gm_cat(h, w, "treset();creset();\n" as *u8) 122 return w 123}