Download docs/index.html from Snapkitty/bob-ide: direct link, hf CLI and curl.
- Browser
- Download file 25.4 kB
-
https://huggingface.co/Snapkitty/bob-ide/resolve/main/docs/index.html
- Command line
-
hf download hf://Snapkitty/bob-ide/docs/index.html
-
curl -L -o index.html https://huggingface.co/Snapkitty/bob-ide/resolve/main/docs/index.html
25.4 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>BOB IDE β Vortex Civilization</title> | |
| <style> | |
| *{margin:0;padding:0;box-sizing:border-box} | |
| :root{ | |
| --bg:#040810;--bg2:#080f1a;--bg3:#0c1520; | |
| --border:#0f2030;--accent:#00d4cc;--purple:#7c3aed; | |
| --gold:#ffd700;--green:#00e87a;--red:#f43; | |
| --text:#c8e0ec;--text2:#4a6878; | |
| } | |
| html,body{height:100%;background:var(--bg);color:var(--text); | |
| font-family:'SF Mono','Fira Code','Consolas',monospace;overflow:hidden} | |
| /* TITLEBAR */ | |
| #bar{height:34px;background:var(--bg2);border-bottom:1px solid var(--border); | |
| display:flex;align-items:center;padding:0 12px;gap:8px;flex-shrink:0} | |
| .dot{width:11px;height:11px;border-radius:50%} | |
| .dr{background:#ff5f57}.dy{background:#febc2e}.dg{background:#28c840} | |
| #bar-title{flex:1;text-align:center;font-size:11px;color:var(--text2); | |
| letter-spacing:2px;text-transform:uppercase} | |
| .vtab{height:34px;padding:0 14px;background:transparent;border:none; | |
| border-bottom:2px solid transparent;color:var(--text2);font-size:11px; | |
| font-family:inherit;cursor:pointer;letter-spacing:.5px;text-transform:uppercase} | |
| .vtab:hover{color:var(--text);background:var(--bg3)} | |
| .vtab.on{color:var(--accent);border-bottom-color:var(--accent);background:var(--bg3)} | |
| #hud{display:flex;gap:10px;margin-left:auto;font-size:10px} | |
| .hi{color:var(--text2)}.hi b{font-size:12px;margin-left:2px} | |
| /* LAYOUT */ | |
| #app{display:flex;height:calc(100vh - 56px)} | |
| #center{flex:1;min-width:0;position:relative} | |
| canvas#cv{display:block;width:100%;height:100%;cursor:crosshair} | |
| /* RIGHT PANEL */ | |
| #right{width:260px;flex-shrink:0;background:var(--bg2); | |
| border-left:1px solid var(--border);display:flex;flex-direction:column;overflow:hidden} | |
| .shdr{height:22px;padding:0 8px;font-size:9px;color:var(--text2); | |
| text-transform:uppercase;letter-spacing:1.5px;border-bottom:1px solid var(--border); | |
| display:flex;align-items:center;background:var(--bg3);flex-shrink:0} | |
| canvas#mini{width:100%;display:block;flex-shrink:0;border-bottom:1px solid var(--border)} | |
| #agents{padding:5px 8px;border-bottom:1px solid var(--border);flex-shrink:0} | |
| .arow{display:flex;align-items:center;gap:5px;padding:2px 0;font-size:10px} | |
| .adot{width:7px;height:7px;border-radius:50%;flex-shrink:0} | |
| .aname{color:var(--text);font-weight:600;width:52px} | |
| .arole{color:var(--text2);font-size:9px} | |
| #radio{height:80px;overflow-y:auto;padding:5px 8px; | |
| border-bottom:1px solid var(--border);flex-shrink:0} | |
| #radio::-webkit-scrollbar{width:2px} | |
| #radio::-webkit-scrollbar-thumb{background:var(--border)} | |
| .rline{font-size:10px;line-height:1.4;margin-bottom:2px; | |
| padding:2px 4px;border-radius:2px;color:var(--text2)} | |
| .rline.good{color:var(--green);border-left:2px solid var(--green);background:rgba(0,232,122,.03)} | |
| .rline.bad{color:var(--red);border-left:2px solid var(--red);background:rgba(255,68,51,.04)} | |
| #chat{flex:1;overflow-y:auto;padding:8px;display:flex;flex-direction:column;gap:5px;background:var(--bg)} | |
| #chat::-webkit-scrollbar{width:2px} | |
| .cmsg{font-size:11px;line-height:1.55;word-break:break-word;white-space:pre-wrap} | |
| .cmsg.bot{color:var(--text)}.cmsg.user{color:var(--accent)} | |
| #cinput-row{display:flex;gap:4px;padding:6px;background:var(--bg2); | |
| border-top:1px solid var(--border);flex-shrink:0} | |
| #cinput{flex:1;background:var(--bg3);border:1px solid var(--border);color:var(--text); | |
| padding:5px 7px;font-size:11px;font-family:inherit;border-radius:3px; | |
| outline:none;resize:none;height:52px} | |
| #cinput:focus{border-color:var(--accent)} | |
| #csend{width:32px;background:var(--accent);border:none;color:#000; | |
| font-size:16px;font-weight:900;cursor:pointer;border-radius:3px} | |
| /* STATUSBAR */ | |
| #sbar{height:22px;background:var(--bg2);border-top:1px solid var(--border); | |
| display:flex;align-items:center;padding:0 10px;gap:12px;flex-shrink:0; | |
| font-size:9px;color:var(--text2)} | |
| </style> | |
| </head> | |
| <body> | |
| <div id="bar"> | |
| <div class="dot dr"></div><div class="dot dy"></div><div class="dot dg"></div> | |
| <div id="bar-title">BOB IDE β Vortex Civilization</div> | |
| <button class="vtab on" id="tab-world" onclick="setView('world')">β¬ World</button> | |
| <button class="vtab" id="tab-quantum" onclick="setView('quantum')">β©Ο Quantum</button> | |
| <div id="hud"> | |
| <span class="hi">HP <b id="h-hp" style="color:#00e87a">100</b></span> | |
| <span class="hi">EVD <b id="h-evd" style="color:#00d4cc">0/6</b></span> | |
| <span class="hi">SEALS <b id="h-seals" style="color:#ffd700">0</b></span> | |
| <span class="hi">COH <b id="h-coh" style="color:#d36bff">0.00</b></span> | |
| <span class="hi">FPS <b id="h-fps" style="color:#4a6878">0</b></span> | |
| </div> | |
| </div> | |
| <div id="app"> | |
| <div id="center"> | |
| <canvas id="cv"></canvas> | |
| </div> | |
| <div id="right"> | |
| <div class="shdr">Map</div> | |
| <canvas id="mini" width="260" height="160"></canvas> | |
| <div class="shdr">Agents</div> | |
| <div id="agents"></div> | |
| <div class="shdr">Agent Radio</div> | |
| <div id="radio"></div> | |
| <div class="shdr" style="justify-content:space-between"> | |
| <span>BOB Oracle</span> | |
| <select id="provider" style="background:var(--bg3);border:none;color:var(--text2);font-size:9px;outline:none;font-family:inherit"> | |
| <option value="local">Local</option> | |
| <option value="openrouter">OpenRouter</option> | |
| </select> | |
| </div> | |
| <div id="chat"> | |
| <div class="cmsg bot">β (boot VORTEX_IDE "civilization engine online")\n\nWASM quantum engine loaded. Walk the world with WASD. Space to pulse. M to seal WORM.</div> | |
| </div> | |
| <div id="cinput-row"> | |
| <textarea id="cinput" placeholder="Ask the oracle..."></textarea> | |
| <button id="csend">β</button> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="sbar"> | |
| <span>BLAKE3Β·WORM</span> | |
| <span id="worm-hash" style="color:var(--accent)">GENESIS</span> | |
| <span style="margin-left:auto;color:var(--text2)">WASD move Β· Arrows turn Β· Space pulse Β· M seal Β· Tab switch</span> | |
| </div> | |
| <!-- Monaco CDN --> | |
| <script>var require={paths:{vs:'https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs'}};</script> | |
| <script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs/loader.js"></script> | |
| <script type="module"> | |
| // ββ LOAD WASM (S-AUTOCODE pattern) βββββββββββββββββββββββββββββββββββββ | |
| let SIM = null; | |
| async function loadWasm() { | |
| try { | |
| const mod = await import('./quantum_wasm.js'); | |
| await mod.default(); | |
| // Create simulation: 8 qubits, 16x16 lattice, J=0.4, h=0.2, coupling=0.3, dt=0.01, seed=42 | |
| SIM = new mod.Simulation(8, 16, 0.4, 0.2, 0.3, 0.01, 42n); | |
| say('(wasm QUANTUM_ENGINE "loaded β 44KB compiled from bob_*.f90")', 'good'); | |
| say(`(sim "8 qubits | 16Γ16 lattice | Trotter+Josephson")`,'good'); | |
| } catch(e) { | |
| say(`(wasm FAILED "${e.message}" β running JS fallback)`, 'bad'); | |
| SIM = null; | |
| } | |
| } | |
| // ββ QRNG (real OS entropy) ββββββββββββββββββββββββββββββββββββββββββββββ | |
| function qrng() { | |
| const b = new Uint8Array(8); | |
| crypto.getRandomValues(b); | |
| return (b[0]*0x1000000+b[1]*65536+b[2]*256+b[3]) / 4294967296; | |
| } | |
| // ββ BLAKE3-inspired WORM (same as sov_monster_kernel.f90) βββββββββββββββ | |
| let wormHash = 'GENESIS'; | |
| let wormSeals = 0; | |
| function seal(evt) { | |
| let h = 0x811c9dc5; | |
| const s = `${wormHash}|${evt}|${Date.now()}`; | |
| for (let i=0;i<s.length;i++){h^=s.charCodeAt(i);h=Math.imul(h,0x01000193)>>>0;} | |
| const rnd = Math.floor(qrng()*0xFFFFFFFF)>>>0; | |
| wormHash = (h^rnd).toString(16).padStart(8,'0')+(Math.imul(h,rnd)>>>0).toString(16).padStart(8,'0'); | |
| wormSeals++; | |
| document.getElementById('worm-hash').textContent = wormHash.slice(0,24)+'β¦'; | |
| document.getElementById('h-seals').textContent = String(wormSeals); | |
| return wormHash; | |
| } | |
| // ββ MAP βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const MAP = [ | |
| '########################', | |
| '#..e......#.....e......#', | |
| '#..###....#..######..#.#', | |
| '#....#....#.......#..#.#', | |
| '#....#..n....e....#....#', | |
| '#..#######..#######.####', | |
| '#.........a............#', | |
| '#.###.#######.#######..#', | |
| '#...#.....#.....#......#', | |
| '#...#..e..#..f..#..e...#', | |
| '###.####..#..####..#####', | |
| '#......#.....#.........#', | |
| '#..r...#..p..#..c......#', | |
| '#......#######......e..#', | |
| '####......#......#######', | |
| '#..e..b...#...x.......o#', | |
| '#.........#............#', | |
| '#..######...######..####', | |
| '#......#.....#.........#', | |
| '#..e...#..v..#...e.....#', | |
| '#......#.....#.........#', | |
| '#..######..######..##..#', | |
| '#......................#', | |
| '########################', | |
| ]; | |
| const MW=MAP[0].length, MH=MAP.length; | |
| const tileAt=(x,y)=>(x<0||y<0||x>=MW||y>=MH)?'#':MAP[Math.floor(y)][Math.floor(x)]; | |
| const solid=t=>'#nafcpbxr'.includes(t); | |
| const wallCol=t=>({'#':['#1b3340','#0b151d','#3c7684'],n:['#05363a','#03181b','#00d4cc'], | |
| a:['#2b1d05','#120c03','#ffd700'],f:['#3a2107','#140903','#ff8c1a'], | |
| c:['#241232','#0d0614','#d36bff'],p:['#173016','#071308','#00ff88'], | |
| b:['#302606','#120d02','#ffd700'],x:['#341111','#130606','#ff4444'], | |
| r:['#06333a','#031316','#5df7ff']}[t]??['#26333a','#0b1116','#7aa8b4']); | |
| // ββ AGENTS ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const AGENTS=[ | |
| {id:'CARTO',role:'Mayor', c:'#ffd700',x:15.5,y:12.5,msg:'(rule CARTO "Trust Deed β vortex stable")'}, | |
| {id:'NOVA', role:'Artist', c:'#d36bff',x:4.5, y:4.5, msg:'(paint NOVA "vortex chronicle sealed")'}, | |
| {id:'FORGE',role:'Architect',c:'#ff8c1a',x:13.5,y:9.5, msg:'(build FORGE "portal buttress")'}, | |
| {id:'FLUX', role:'Trader', c:'#00d4cc',x:7.5, y:15.5,msg:'(trade FLUX "evidence shard β entropy low")'}, | |
| {id:'PHANTOM',role:'Sheriff',c:'#00ff88',x:10.5,y:12.5,msg:'(audit PHANTOM "no agent acts without evidence")'}, | |
| {id:'ROBOB',role:'Oracle', c:'#5df7ff',x:3.5, y:15.5,msg:'(verdict ROBOB "EVIDENCE or SILENCE")'}, | |
| ]; | |
| // Render agent list | |
| const agDiv=document.getElementById('agents'); | |
| AGENTS.forEach(ag=>{ | |
| agDiv.innerHTML+=`<div class="arow"> | |
| <div class="adot" style="background:${ag.c}"></div> | |
| <span class="aname">${ag.id}</span> | |
| <span class="arole">${ag.role}</span> | |
| </div>`; | |
| }); | |
| // ββ GAME STATE βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const G={ | |
| px:2.8,py:2.8,pa:0,hp:100,evidence:0,won:false,dead:false,cool:0,tick:0, | |
| shards:[],enemies:[ | |
| {x:19.5,y:4.5,hp:3,alive:true}, | |
| {x:18.5,y:16.5,hp:3,alive:true}, | |
| {x:6.5,y:20.5,hp:3,alive:true}, | |
| ], | |
| keys:{},lastT:0,fps:0,frames:0,fpsT:0, | |
| }; | |
| MAP.forEach((row,y)=>[...row].forEach((c,x)=>{if(c==='e')G.shards.push({x:x+.5,y:y+.5,alive:true});})); | |
| // ββ DIALOGUE βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const radioEl=document.getElementById('radio'); | |
| function say(text,cls=''){ | |
| const d=document.createElement('div'); | |
| d.className='rline '+(cls||''); | |
| d.textContent=text; | |
| radioEl.prepend(d); | |
| while(radioEl.children.length>12)radioEl.removeChild(radioEl.lastChild); | |
| } | |
| // ββ INPUT ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| window.addEventListener('keydown',e=>{ | |
| G.keys[e.code]=true; | |
| if(e.code==='Space'){e.preventDefault();pulse();} | |
| if(e.code==='KeyM')seal('MANUAL'); | |
| if(e.code==='Tab'){e.preventDefault();setView(currentView==='world'?'quantum':'world');} | |
| }); | |
| window.addEventListener('keyup',e=>G.keys[e.code]=false); | |
| function pulse(){ | |
| if(G.cool>0||G.hp<=0)return; | |
| G.cool=18; | |
| let best=null,bd=999; | |
| for(const e of G.enemies)if(e.alive){ | |
| const dx=e.x-G.px,dy=e.y-G.py,d=Math.hypot(dx,dy); | |
| const da=Math.atan2(dy,dx)-G.pa,n=Math.atan2(Math.sin(da),Math.cos(da)); | |
| if(d<6&&Math.abs(n)<0.26&&d<bd){best=e;bd=d;} | |
| } | |
| if(best){ | |
| best.hp--; | |
| say(`(pulse ROBOB β SILENCE d=${bd.toFixed(1)})`, 'good'); | |
| seal('PULSE'); | |
| if(best.hp<=0){best.alive=false;say('(SILENCE COLLAPSED)','good');} | |
| } else { | |
| say('(empty corridor)','bad'); | |
| } | |
| } | |
| // ββ GAME UPDATE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function move(dt){ | |
| const k=G.keys; | |
| G.pa+=((k.ArrowLeft?-1:0)+(k.ArrowRight?1:0))*dt*2.3; | |
| const f=(k.KeyW||k.ArrowUp?1:0)-(k.KeyS||k.ArrowDown?1:0); | |
| const st=(k.KeyD?1:0)-(k.KeyA?1:0); | |
| const sp=dt*3.0; | |
| const nx=G.px+Math.cos(G.pa)*f*sp+Math.cos(G.pa+Math.PI/2)*st*sp; | |
| const ny=G.py+Math.sin(G.pa)*f*sp+Math.sin(G.pa+Math.PI/2)*st*sp; | |
| if(!solid(tileAt(nx,G.py)))G.px=nx; | |
| if(!solid(tileAt(G.px,ny)))G.py=ny; | |
| G.px=Math.max(1.1,Math.min(MW-1.1,G.px)); | |
| G.py=Math.max(1.1,Math.min(MH-1.1,G.py)); | |
| } | |
| function update(dt){ | |
| G.tick++;if(G.cool>0)G.cool--; | |
| // Step quantum simulation | |
| if(SIM){ | |
| SIM.step_n(2); | |
| } | |
| // Get coherence for physics β from WASM or fallback | |
| const coh = SIM ? SIM.mean_coherence() : 0.5; | |
| // Collect shards | |
| for(const sh of G.shards)if(sh.alive&&Math.hypot(sh.x-G.px,sh.y-G.py)<0.55){ | |
| sh.alive=false;G.evidence++; | |
| say(`(collect SHARD-${G.evidence})`, 'good'); | |
| seal('SHARD'); | |
| } | |
| // Agent dialogue β Born rule weighted by coherence + proximity | |
| for(const ag of AGENTS){ | |
| const d=Math.hypot(ag.x-G.px,ag.y-G.py); | |
| if(Math.random()<coh*(1/(1+d*d))*0.06)say(ag.msg,'good'); | |
| } | |
| // Enemies β speed modulated by vortex coherence | |
| for(const e of G.enemies)if(e.alive){ | |
| const d=Math.hypot(e.x-G.px,e.y-G.py); | |
| if(d<8){const dx=(G.px-e.x)/d,dy=(G.py-e.y)/d,spd=dt*0.55*(0.5+coh*0.5); | |
| const nx=e.x+dx*spd,ny=e.y+dy*spd; | |
| if(!solid(tileAt(nx,e.y)))e.x=nx;if(!solid(tileAt(e.x,ny)))e.y=ny;} | |
| if(d<0.75){G.hp=Math.max(0,G.hp-dt*18);if(Math.random()<0.02)say('(attack SILENCE)','bad');} | |
| } | |
| if(G.evidence>=6&&!G.won&&Math.hypot(12.5-G.px,19.5-G.py)<1.1){ | |
| G.won=true;say('(seal TRUST_DEED "VICTORY")','good');seal('VICTORY'); | |
| } | |
| if(G.hp<=0&&!G.dead){G.dead=true;say('(SILENCE β human review)','bad');seal('DEATH');} | |
| // HUD | |
| document.getElementById('h-hp').textContent=Math.ceil(G.hp); | |
| document.getElementById('h-hp').style.color=G.hp>50?'#00e87a':G.hp>25?'#fa0':'#f43'; | |
| document.getElementById('h-evd').textContent=`${G.evidence}/6`; | |
| document.getElementById('h-coh').textContent=coh.toFixed(3); | |
| document.getElementById('h-fps').textContent=G.fps; | |
| } | |
| // ββ RAYCASTER ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const cv=document.getElementById('cv'); | |
| const ctx=cv.getContext('2d',{alpha:false}); | |
| const mini=document.getElementById('mini'); | |
| const mctx=mini.getContext('2d'); | |
| function castRay(a){ | |
| let x=G.px,y=G.py,d=0,t='.'; | |
| const sin=Math.sin(a),cos=Math.cos(a); | |
| while(d<22){x+=cos*.035;y+=sin*.035;d+=.035;t=tileAt(x,y);if(solid(t))break;} | |
| return{d,t,x,y}; | |
| } | |
| function getSimCoh(fx, fy) { | |
| if(!SIM)return 0.5; | |
| try{return SIM.vortex_coherence(Math.floor(fx*16)%16 + Math.floor(fy*16)%16 * 16);}catch{return 0.5;} | |
| } | |
| function getSimPhase(fx, fy) { | |
| if(!SIM)return 0; | |
| try{return SIM.vortex_phase(Math.floor(fx*16)%16 + Math.floor(fy*16)%16 * 16);}catch{return 0;} | |
| } | |
| function renderWorld(){ | |
| const W=cv.width,H=cv.height,strip=W>1200?2:3; | |
| const sky=ctx.createLinearGradient(0,0,0,H*.52); | |
| sky.addColorStop(0,'#020610');sky.addColorStop(.55,'#071827');sky.addColorStop(1,'#10111b'); | |
| ctx.fillStyle=sky;ctx.fillRect(0,0,W,H/2); | |
| const fl=ctx.createLinearGradient(0,H/2,0,H); | |
| fl.addColorStop(0,'#090b10');fl.addColorStop(1,'#020204'); | |
| ctx.fillStyle=fl;ctx.fillRect(0,H/2,W,H/2); | |
| const fov=Math.PI/3,zbuf=[]; | |
| for(let x=0;x<W;x+=strip){ | |
| const a=G.pa-fov/2+(x/W)*fov,r=castRay(a); | |
| const cor=r.d*Math.cos(a-G.pa),h=Math.min(H,Math.floor(H/(cor*.62))),top=(H-h)/2; | |
| const coh=getSimCoh(r.x/MW,r.y/MH); | |
| const ph=getSimPhase(r.x/MW,r.y/MH); | |
| const wc=wallCol(r.t),shade=Math.max(.24,1-cor/18)*(0.7+coh*0.3); | |
| const side=Math.abs(r.x-Math.round(r.x))<.04?1:0; | |
| const bc=(side?wc[1]:wc[2]).match(/\w\w/g).map(v=>parseInt(v,16)); | |
| const tr=Math.cos(ph)*20,tg=Math.sin(ph)*10; | |
| ctx.fillStyle=`rgb(${Math.floor(bc[0]*shade+tr)},${Math.floor(bc[1]*shade+tg)},${Math.floor(bc[2]*shade)})`; | |
| ctx.fillRect(x,top,strip+1,h); | |
| if(coh>0.75&&'napc'.includes(r.t)){ | |
| ctx.fillStyle=`rgba(0,212,204,${(coh-.75)*.5+.04*Math.sin(G.tick*.05+x*.02)})`; | |
| ctx.fillRect(x,top,strip+1,h); | |
| } | |
| zbuf[x]=cor; | |
| } | |
| // Sprites | |
| const all=[ | |
| ...G.shards.filter(s=>s.alive).map(s=>({...s,kind:'shard',c:'#00ff88',id:''})), | |
| ...AGENTS.map(a=>({...a,kind:'agent'})), | |
| ...G.enemies.filter(e=>e.alive).map(e=>({...e,kind:'enemy',c:'#ff4444',id:'SILENCE'})), | |
| {x:12.5,y:19.5,kind:'portal',c:'#00d4ff',id:''}, | |
| ]; | |
| all.sort((a,b)=>Math.hypot(b.x-G.px,b.y-G.py)-Math.hypot(a.x-G.px,a.y-G.py)); | |
| for(const sp of all){ | |
| const dx=sp.x-G.px,dy=sp.y-G.py,dist=Math.hypot(dx,dy); | |
| const ang=Math.atan2(dy,dx)-G.pa,norm=Math.atan2(Math.sin(ang),Math.cos(ang)); | |
| if(Math.abs(norm)>Math.PI/3||dist<.2)continue; | |
| const sx=(norm/(Math.PI/3)+.5)*W,sz=Math.min(H*.75,H/(dist*.9)),top=H/2-sz/2; | |
| const zi=Math.max(0,Math.min(W-1,Math.floor(sx/3)*3)); | |
| if(zbuf[zi]&&zbuf[zi]<dist-.25)continue; | |
| ctx.save();ctx.globalAlpha=Math.max(.35,1-dist/18); | |
| if(sp.kind==='shard'){ | |
| ctx.fillStyle='#00ff88';ctx.translate(sx,top+sz/2);ctx.rotate(G.tick*.04); | |
| ctx.fillRect(-sz*.16,-sz*.16,sz*.32,sz*.32); | |
| }else if(sp.kind==='portal'){ | |
| const p=1+Math.sin(G.tick*.07)*.12; | |
| ctx.strokeStyle='#00d4ff';ctx.lineWidth=5;ctx.shadowColor='#00d4ff';ctx.shadowBlur=25; | |
| ctx.beginPath();ctx.ellipse(sx,top+sz/2,sz*.32*p,sz*.48*p,0,0,Math.PI*2);ctx.stroke(); | |
| ctx.fillStyle='#000';ctx.beginPath();ctx.ellipse(sx,top+sz/2,sz*.18,sz*.28,0,0,Math.PI*2);ctx.fill(); | |
| }else{ | |
| ctx.shadowColor=sp.c;ctx.shadowBlur=16;ctx.fillStyle=sp.c; | |
| ctx.fillRect(sx-sz*.25,top+sz*.22,sz*.5,sz*.58); | |
| ctx.beginPath();ctx.arc(sx,top+sz*.18,sz*.18,0,Math.PI*2);ctx.fill(); | |
| ctx.fillStyle=sp.kind==='enemy'?'#220000':'#020204'; | |
| ctx.fillRect(sx-sz*.09,top+sz*.15,sz*.05,sz*.04); | |
| ctx.fillRect(sx+sz*.04,top+sz*.15,sz*.05,sz*.04); | |
| ctx.fillStyle=sp.c;ctx.font=`bold ${Math.max(9,sz*.09)}px monospace`; | |
| ctx.textAlign='center';ctx.fillText(sp.id||'',sx,top+sz*.92); | |
| } | |
| ctx.restore(); | |
| } | |
| // Crosshair | |
| ctx.strokeStyle='rgba(255,255,255,.7)';ctx.lineWidth=1.5; | |
| ctx.beginPath();ctx.moveTo(W/2-14,H/2);ctx.lineTo(W/2+14,H/2);ctx.stroke(); | |
| ctx.beginPath();ctx.moveTo(W/2,H/2-14);ctx.lineTo(W/2,H/2+14);ctx.stroke(); | |
| if(G.won){ctx.fillStyle='rgba(0,255,136,.15)';ctx.fillRect(0,0,W,H); | |
| ctx.fillStyle='#ffd700';ctx.font='bold 34px monospace';ctx.textAlign='center'; | |
| ctx.fillText('KITTYVERSE TRUST DEED SEALED',W/2,H/2);} | |
| if(G.hp<=0){ctx.fillStyle='rgba(255,0,0,.25)';ctx.fillRect(0,0,W,H); | |
| ctx.fillStyle='#f43';ctx.font='bold 34px monospace';ctx.textAlign='center'; | |
| ctx.fillText('SILENCE β HUMAN REVIEW REQUIRED',W/2,H/2);} | |
| } | |
| function renderMini(){ | |
| const W=mini.width,H=mini.height,sw=W/MW,sh=H/MH; | |
| mctx.clearRect(0,0,W,H); | |
| for(let y=0;y<MH;y++)for(let x=0;x<MW;x++){ | |
| const t=MAP[y][x]; | |
| if(solid(t)){mctx.fillStyle=wallCol(t)[2];mctx.fillRect(x*sw,y*sh,sw,sh);} | |
| else{ | |
| let coh=0.5; | |
| if(SIM){try{coh=SIM.vortex_coherence((x%16)+(y%16)*16);}catch{}} | |
| mctx.fillStyle=`hsl(${200+coh*60},70%,${8+coh*12}%)`;mctx.fillRect(x*sw,y*sh,sw,sh); | |
| } | |
| } | |
| G.shards.filter(s=>s.alive).forEach(s=>{mctx.fillStyle='#00ff88';mctx.fillRect((s.x-.1)*sw,(s.y-.1)*sh,sw*.2,sh*.2);}); | |
| AGENTS.forEach(a=>{mctx.fillStyle=a.c;mctx.beginPath();mctx.arc(a.x*sw,a.y*sh,sw*.28,0,Math.PI*2);mctx.fill();}); | |
| G.enemies.filter(e=>e.alive).forEach(e=>{mctx.fillStyle='#f43';mctx.beginPath();mctx.arc(e.x*sw,e.y*sh,sw*.22,0,Math.PI*2);mctx.fill();}); | |
| mctx.strokeStyle='#ffd700';mctx.lineWidth=1; | |
| mctx.beginPath();mctx.arc(G.px*sw,G.py*sh,sw*.35,0,Math.PI*2);mctx.stroke(); | |
| mctx.beginPath();mctx.moveTo(G.px*sw,G.py*sh);mctx.lineTo((G.px+Math.cos(G.pa)*1.2)*sw,(G.py+Math.sin(G.pa)*1.2)*sh);mctx.stroke(); | |
| } | |
| // ββ QUANTUM VIEW βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| let currentView='world'; | |
| window.setView=function(v){ | |
| currentView=v; | |
| ['world','quantum'].forEach(k=>{document.getElementById('tab-'+k).classList.toggle('on',k===v);}); | |
| cv.style.display=v==='world'?'block':'none'; | |
| }; | |
| function renderQuantum(){ | |
| if(currentView!=='quantum'||!SIM)return; | |
| const W=cv.clientWidth||800,H=cv.clientHeight||600; | |
| cv.width=W;cv.height=H;cv.style.display='block'; | |
| const n=16,cw=W/n,ch=H/n; | |
| for(let i=0;i<n*n;i++){ | |
| const ix=i%n,iy=Math.floor(i/n); | |
| let ph=0,coh=0.5,wind=0; | |
| try{ph=SIM.vortex_phase(i);coh=SIM.vortex_coherence(i);wind=SIM.vortex_winding(i);}catch{} | |
| const hue=(ph/(2*Math.PI)*360)|0; | |
| const lum=(15+coh*30)|0; | |
| ctx.fillStyle=`hsl(${hue},85%,${lum}%)`; | |
| ctx.fillRect(ix*cw,iy*ch,cw,ch); | |
| if(wind!==0){ | |
| ctx.strokeStyle=wind>0?'rgba(0,212,204,.7)':'rgba(255,60,60,.7)'; | |
| ctx.lineWidth=1;ctx.beginPath();ctx.arc(ix*cw+cw/2,iy*ch+ch/2,Math.min(cw,ch)*.3,0,Math.PI*2);ctx.stroke(); | |
| } | |
| } | |
| } | |
| // ββ CHAT βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| const chatEl=document.getElementById('chat'); | |
| function chatAppend(role,text){ | |
| const d=document.createElement('div'); | |
| d.className='cmsg '+role; | |
| d.textContent=(role==='bot'?'β ':'β― ')+text; | |
| chatEl.appendChild(d); | |
| chatEl.scrollTop=chatEl.scrollHeight; | |
| } | |
| document.getElementById('csend').onclick=sendChat; | |
| document.getElementById('cinput').addEventListener('keydown',e=>{ | |
| if(e.key==='Enter'&&(e.ctrlKey||e.metaKey)){e.preventDefault();sendChat();} | |
| }); | |
| async function sendChat(){ | |
| const inp=document.getElementById('cinput'); | |
| const text=inp.value.trim();if(!text)return; | |
| inp.value='';chatAppend('user',text); | |
| const prov=document.getElementById('provider').value; | |
| if(prov==='openrouter'){ | |
| const key=localStorage.getItem('or_key')||prompt('OpenRouter API key:'); | |
| if(key){localStorage.setItem('or_key',key); | |
| try{ | |
| const r=await fetch('https://openrouter.ai/api/v1/chat/completions',{ | |
| method:'POST',headers:{'Content-Type':'application/json','Authorization':'Bearer '+key}, | |
| body:JSON.stringify({model:'anthropic/claude-3-haiku',messages:[ | |
| {role:'system',content:'You are BOB, oracle of the Kittyverse sovereign civilization. Concise, technical, sovereign.'}, | |
| {role:'user',content:text} | |
| ]}) | |
| }); | |
| const j=await r.json(); | |
| chatAppend('bot',j.choices?.[0]?.message?.content||'(no response)'); | |
| }catch(e){chatAppend('bot','Error: '+e.message);} | |
| } | |
| }else{ | |
| const coh=SIM?SIM.mean_coherence().toFixed(3):'?'; | |
| const topo=SIM?SIM.topological_charge():0; | |
| chatAppend('bot',`coherence=${coh} topo=${topo} seals=${wormSeals}\n\nVortex engine running. Set OpenRouter for full AI responses.`); | |
| } | |
| } | |
| // ββ MAIN LOOP ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function resize(){cv.width=cv.offsetWidth||800;cv.height=cv.offsetHeight||600;} | |
| window.addEventListener('resize',resize);resize(); | |
| function loop(now){ | |
| requestAnimationFrame(loop); | |
| const dt=Math.min(.05,(now-(G.lastT||now))/1000);G.lastT=now; | |
| G.frames++;if(now-G.fpsT>1000){G.fps=G.frames;G.frames=0;G.fpsT=now;} | |
| move(dt);update(dt); | |
| if(currentView==='world')renderWorld(); | |
| else renderQuantum(); | |
| renderMini(); | |
| } | |
| // ββ BOOT βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| await loadWasm(); | |
| seal('BOOT'); | |
| requestAnimationFrame(loop); | |
| </script> | |
| </body> | |
| </html> | |