Download controller.js from orcnd/field-orders-40v40-ppo-v2: direct link, hf CLI and curl.
- Browser
- Download file 7.17 kB
-
https://huggingface.co/orcnd/field-orders-40v40-ppo-v2/resolve/main/controller.js
- Command line
-
hf download hf://orcnd/field-orders-40v40-ppo-v2/controller.js
-
curl -L -o controller.js https://huggingface.co/orcnd/field-orders-40v40-ppo-v2/resolve/main/controller.js
7.17 kB
| import {soldierFeatures,commanderFeatures,COMMANDS,FORMATIONS,STRATEGIES} from './sensors.js'; | |
| import {behaviorCatalog as C} from './catalog.js'; | |
| import {NEUTRAL_INPUT,validInput,applyCharacterInput} from './controls.js'; | |
| import {ObservationHistory,flagSignature,squadIndex} from './temporal.js'; | |
| import {availableSquads,applyMacroOrder,expireMacroOrders} from './macro.js'; | |
| const orderToken=(state,u)=>JSON.stringify([Boolean(u.commandOverride),u.commandOverride||state.commands[u.team]]); | |
| export function createNeuralController({state,stats,model,worker,sensors,onError=()=>{},onReady=()=>{},onOrder=()=>{}}){ | |
| let sequence=0,pending=null,decisionClock=.1,commanderClock=2.5;const inputs=new Map(),requestedCommanders=new Set(),history=new ObservationHistory(),commanderSignatures=new Map(); | |
| function fail(message){model.ready=false;model.busy=false;model.disabled=true;inputs.clear();pending=null;state.paused=true;onError(message);} | |
| function reset(){sequence++;pending=null;inputs.clear();requestedCommanders.clear();history.reset();commanderSignatures.clear();decisionClock=.1;commanderClock=2.5;model.busy=false;} | |
| function tick(dt){ | |
| if(!model.ready||!worker)return;decisionClock+=dt;commanderClock+=dt;if(model.models?.commander?.inputDim===116)expireMacroOrders(state); | |
| if(pending){if(performance.now()-pending.sent>3000)fail('Model yanıtı zaman aşımına uğradı.');return;} | |
| if(decisionClock<.1)return;decisionClock%=.1;const start=performance.now(); | |
| sensors.shareIntel('viking');sensors.shareIntel('mongol'); | |
| if(model.models?.commander?.inputDim===116)for(const team of ['viking','mongol']){ | |
| const signature=flagSignature(state,sensors,team); | |
| if(state.commanders[team].mode==='ai'&&commanderSignatures.get(team)!==signature)requestedCommanders.add(team); | |
| } | |
| const units=state.units.filter(u=>u.alive),commanders=[]; | |
| if(commanderClock>=2.5||requestedCommanders.size){commanderClock%=2.5;for(const team of ['viking','mongol'])if(state.commanders[team].mode==='ai'||requestedCommanders.has(team)){ | |
| commanders.push({team,manual:requestedCommanders.has(team),generation:state.commanders[team].generation,revision:state.commands[team].revision,row:model.models?.commander?.inputDim===116?history.commander(state,sensors,team):commanderFeatures(state,sensors,team),squadMask:availableSquads(state,team)}); | |
| commanderSignatures.set(team,flagSignature(state,sensors,team)); | |
| }} | |
| requestedCommanders.clear(); | |
| pending={matchId:stats.matchId,sequence:++sequence,sent:performance.now()};model.busy=true; | |
| worker.postMessage({type:'infer',matchId:stats.matchId,sequence,ids:units.map(u=>u.id),revisions:units.map(u=>orderToken(state,u)),rows:units.map(u=>model.models?.soldier?.inputDim===523?history.soldier(state,sensors,u):soldierFeatures(state,sensors,u)),commanders}); | |
| stats.workloadSamples.push({t:performance.now(),ms:performance.now()-start}); | |
| } | |
| function receive(data){ | |
| if(data.type==='ready'){model.ready=true;model.disabled=false;model.models=data.models;model.label=data.label;model.method=data.method;onReady(data);return;} | |
| if(data.type==='error'){if(data.matchId&&data.matchId!==stats.matchId)return;if(data.sequence&&data.sequence!==pending?.sequence)return;fail(data.message);return;} | |
| if(data.type!=='inputs'||data.matchId!==stats.matchId||data.sequence!==pending?.sequence)return; | |
| if(data.inputs.length!==data.ids.length||data.inputs.some(i=>!validInput(i))){fail('Geçersiz model girdisi.');return;} | |
| model.busy=false;pending=null;stats.decisions++;stats.aiMs+=data.elapsed;stats.workloadSamples.push({t:performance.now(),ms:data.elapsed}); | |
| data.ids.forEach((id,i)=>{const u=state.units.find(u=>u.id===id&&u.alive);if(!u)return;if(orderToken(state,u)!==data.revisions[i])return;inputs.set(id,{input:data.inputs[i],at:state.elapsed,order:data.revisions[i]});u.reason='Eğitilmiş sinir ağı · yön / nişan / ateş';u.intent=data.inputs[i].fire?'ateş et':'hareket girdisi';u.decisionMs=data.elapsed;model.latency[u.team].push(data.elapsed);model.latency[u.team]=model.latency[u.team].slice(-30);}); | |
| if(stats.decisions%10===0){stats.examples ||= [];data.ids.forEach((id,i)=>stats.examples.push({time:state.elapsed,id,input:data.inputs[i],model:model.models?.soldier?.name||'soldier-v1'}));if(stats.examples.length>2000)stats.examples.splice(0,stats.examples.length-2000);} | |
| for(const c of data.commanders){const old=state.commands[c.team];if((state.commanders[c.team].mode!=='ai'&&!c.manual)||state.commanders[c.team].generation!==c.generation||old.revision!==c.revision)continue; | |
| if(c.macro){try{const result=applyMacroOrder(state,c.team,c.macro);if(result.count)onOrder(c.team,result.command);}catch(error){fail(error.message);}continue;} | |
| const commandId=COMMANDS[c.command],formation=FORMATIONS[c.formation],strategyId=STRATEGIES[c.strategy];if(!C.COMMAND_CATALOG[commandId]||!formation||!strategyId){fail('Geçersiz komutan çıktısı.');return;} | |
| const preset=C.COMMAND_CATALOG[commandId],changed=old.commandId!==commandId||old.formation!==C.FORMATION_LABELS[formation]||old.strategyId!==strategyId; | |
| Object.assign(old,preset,{commandId,formation:C.FORMATION_LABELS[formation],strategyId:strategyId==='none'?null:strategyId,combatMode:C.STRATEGIES[strategyId]?.combatMode||old.combatMode,source:'neural',strategySource:'neural',note:'Eğitilmiş komutan ağı',revision:old.revision+(changed?1:0)}); | |
| if(changed)onOrder(c.team,old); | |
| } | |
| } | |
| function apply(u,dt){const entry=inputs.get(u.id),input=entry&&entry.order===orderToken(state,u)&&state.elapsed-entry.at<=.6?entry.input:NEUTRAL_INPUT;applyCharacterInput(state,stats,u,input,dt);} | |
| function recordHumanOrder(team,order,unitIds,source='human-live'){ | |
| const selected=state.units.filter(u=>u.alive&&u.team===team&&unitIds.includes(u.id));if(!selected.length)return; | |
| const allies=state.units.filter(u=>u.alive&&u.team===team),equal=a=>a.length===selected.length&&a.every(u=>unitIds.includes(u.id)); | |
| let group=equal(allies)?0:-1;for(let i=0;i<8&&group<0;i++)if(equal(allies.filter(u=>squadIndex(u)===i)))group=i+1; | |
| const task=order.task==='hold'?'defend':order.task==='capture_return'?'objective':order.task||'objective',commandId=order.commandId||(task==='defend'?'defend_base':'attack_flag'); | |
| const action={command:COMMANDS.indexOf(commandId),formation:FORMATIONS.indexOf(order.formation||'line'),strategy:0,group,task:['objective','move','assault','defend','heal'].indexOf(commandId==='heal_at_base'?'heal':task),x:order.x??state.flags[team].home.x,y:order.y??state.flags[team].home.y,ttl:10}; | |
| stats.commanderDemonstrations ||= [];stats.commanderDemonstrations.push({schema:'field-orders.commander-demonstration.v1',source,time:state.elapsed,team,unitIds:selected.map(u=>u.id),observation:history.commander(state,sensors,team),action,trainable:group>=0&&action.command>=0&&action.formation>=0&&action.task>=0}); | |
| if(stats.commanderDemonstrations.length>2000)stats.commanderDemonstrations.shift(); | |
| } | |
| return {recordHumanOrder,requestCommand(team){requestedCommanders.add(team);decisionClock=.1;},reset,tick,receive,apply,fail,get inputs(){return inputs;}}; | |
| } | |