From 8f15d59e4b4319b2e256bef5ad4e179cbc894b0a Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Thu, 17 Sep 2026 18:04:32 -0700 Subject: [PATCH] Give Jev fighters precise movement and usable combat choices --- system/backend/tests/oskiewar-jev.test.mjs | 48 ++++++++++++- xbox/live/jev-vs-jev/README.md | 8 ++- xbox/live/jev-vs-jev/demo.mjs | 17 +++-- xbox/live/jev-vs-jev/model.mjs | 77 +++++++++++++++++---- xbox/live/oskiewar.js | 10 +++ xbox/live/social/manifest.json | 2 +- xbox/live/social/oskiewar-title.jpg | Bin 56838 -> 59339 bytes xbox/live/social/oskiewar-title.mp4 | Bin 239841 -> 238470 bytes xbox/live/tests/oskiewar.test.mjs | 22 ++++++ 9 files changed, 160 insertions(+), 24 deletions(-) diff --git a/system/backend/tests/oskiewar-jev.test.mjs b/system/backend/tests/oskiewar-jev.test.mjs index 77b7543e3..1abc4136e 100644 --- a/system/backend/tests/oskiewar-jev.test.mjs +++ b/system/backend/tests/oskiewar-jev.test.mjs @@ -1,7 +1,7 @@ import test from 'node:test'; import assert from 'node:assert/strict'; import { createHandler } from '../oskiewar-jev.mjs'; -import { cleanScene, buttons } from '../../../xbox/live/jev-vs-jev/model.mjs'; +import { cleanScene, buttons, controlPlan, controlsAt, decisionRequest } from '../../../xbox/live/jev-vs-jev/model.mjs'; const ticket='12345678-1234-1234-1234-123456789abc'; const event=body=>({httpMethod:'POST',headers:{},body:JSON.stringify(body)}); const scene={self:{x:100,y:200},private:'SECRET',opponent:{x:200,y:200,dx:100}}; @@ -28,3 +28,49 @@ test('model input and outputs are bounded',()=>{ assert.throws(()=>buttons('toward','kick')); assert.deepEqual(buttons('still','wait'),[]); }); +test('Jev can face and strike without a full walk carrying it through the target',()=>{ + const turn=controlPlan('face_left','punch'); + assert.deepEqual(turn.down,['ArrowLeft','B']); + assert.equal(turn.motionMs,20); + assert.deepEqual(controlsAt(turn,0),['ArrowLeft','B']); + assert.deepEqual(controlsAt(turn,25),['B']); + assert.deepEqual(controlsAt(turn,220),[]); + assert.deepEqual(controlsAt(null,0),[]); + assert.ok(turn.actionMs>turn.motionMs); + assert.ok(turn.motionMs*880/1000<20,'turn travels less than a fighter width'); + assert.equal(controlPlan('step_right','kick').motionMs,55); + assert.equal(controlPlan('still','jump').actionMs,450); + for(const [move,action] of [['still','punch'],['left','jump'],['face_right','kick']]){ + const p=controlPlan(move,action); + assert.ok(p.releaseMs-Math.max(p.motionMs,p.actionMs)>=60,'next decision follows a real button release'); + } +}); +test('a chosen platform drop supplies two down presses inside the combo window',()=>{ + const p=controlPlan('still','drop'); + assert.deepEqual(controlsAt(p,30),['ArrowDown']); + assert.deepEqual(controlsAt(p,90),[]); + assert.deepEqual(controlsAt(p,160),['ArrowDown']); + assert.deepEqual(controlsAt(p,230),[]); +}); +test('observation distinguishes airborne opponents and missing attack limbs',()=>{ + const value=cleanScene({self:{x:10,y:100,combat:{punch:false,kickLeft:true,kickRight:false,secret:'PRIVATE'}}, + opponent:{x:80,y:50,dx:70,dy:50,grounded:false,vx:20,vy:-300}}); + assert.equal(value.self.combat.punch,false); + assert.equal(value.self.combat.kickLeft,true); + assert.equal(value.opponent.grounded,false); + assert.equal(value.opponent.vy,-300); + assert.doesNotMatch(JSON.stringify(value),/PRIVATE|secret/); +}); +test('Jev chooses from usable attacks after dismemberment',()=>{ + const s={self:{x:10,y:100,facing:1,combat:{punch:true,kickLeft:true,kickRight:false}}, + opponent:{x:80,y:100,dx:70,dy:0}}; + let choices=decisionRequest(s).questions.action.criteria; + assert.ok(choices.punch);assert.equal(choices.kick,undefined);assert.equal(choices.item,undefined); + s.opponent.dx=-70; + choices=decisionRequest(s).questions.action.criteria; + assert.ok(choices.kick); + s.self.combat={headOnly:true}; + choices=decisionRequest(s).questions.action.criteria; + assert.match(choices.punch,/Spit/);assert.match(choices.kick,/Spit/); + assert.equal(choices.block,undefined); +}); diff --git a/xbox/live/jev-vs-jev/README.md b/xbox/live/jev-vs-jev/README.md index 178128c88..178f5c944 100644 --- a/xbox/live/jev-vs-jev/README.md +++ b/xbox/live/jev-vs-jev/README.md @@ -3,7 +3,13 @@ `https://oskiewar.com/jev-vs-jev` runs the real Oskiewar simulation with two independent Jev controllers. Each chooses a direction and action from its own visible-world snapshot. The built-in bot never supplies input on this route. -Responses hold the chosen buttons for 250 ms (450 ms for a full jump), then release. No response means +Jev chooses long walks (180 ms), short steps (55 ms), face taps (20 ms), or +standing still independently of its attack. Action buttons hold for 150 ms +(450 ms for a full jump); movement releases at its own deadline so a punch +does not force a fighter to run past its target. Observations include airborne +state and available attack limbs; unusable attacks are excluded from Jev's choices. +Jev can also choose a double-down drop, a fixed two-press sequence within the +game's combo window, to descend through platforms. No response means neutral input; stale responses and old-round responses are discarded. Transient provider failures leave a fighter neutral while it requests a fresh decision; three consecutive failures stop the match. diff --git a/xbox/live/jev-vs-jev/demo.mjs b/xbox/live/jev-vs-jev/demo.mjs index 3498b15cb..c9c0be43a 100644 --- a/xbox/live/jev-vs-jev/demo.mjs +++ b/xbox/live/jev-vs-jev/demo.mjs @@ -1,6 +1,6 @@ -import { buttons } from './model.mjs'; +import { controlPlan, controlsAt } from './model.mjs'; const $ = id => document.getElementById(id); -const empty = () => ({ scene: null, at: 0, down: [], until: 0, count: 0, cost: 0, input: 0, output: 0, latency: 0, move: 'Waiting', failures: 0 }); +const empty = () => ({ scene: null, at: 0, plan: null, issuedAt: 0, count: 0, cost: 0, input: 0, output: 0, latency: 0, move: 'Waiting', failures: 0 }); let fighters = [empty(), empty()], running = false, ticket = '', endAt = 0, pending = 0; let frame = null; const sleep = ms => new Promise(resolve => setTimeout(resolve, ms)); @@ -9,7 +9,8 @@ globalThis.__jevArena = { const f = fighters[seat]; if (!f) return []; f.scene = scene; f.at = performance.now(); - return running && scene.alive && performance.now() < f.until ? f.down : []; + if (!running || !scene.alive) return []; + return controlsAt(f.plan, performance.now() - f.issuedAt); }, frame(value) { frame = value; }, inspect() { return { running, frame, fighters: fighters.map(f => ({ ...f, scene: f.scene })) }; }, @@ -27,7 +28,7 @@ function render() { } function stop(message = 'Stopped') { running = false; - for (const f of fighters) { f.down = []; f.until = 0; } + for (const f of fighters) f.plan = null; $('status').textContent = message; render(); } @@ -43,6 +44,7 @@ async function drive(seat, session) { const f = fighters[seat]; if (!f.scene?.alive || performance.now()-f.at > 250 || frame?.phase !== 'fight') { await sleep(100); continue; } const started = performance.now(), round = f.scene.round; + let releaseMs = 210; pending++; try { const result = await post({ ticket: session, seat, scene: f.scene }); @@ -52,15 +54,16 @@ async function drive(seat, session) { f.latency = Math.round(performance.now() - started); f.move = result.motion+' + '+result.action; if (running && ticket === session && round === f.scene?.round && f.scene.alive && frame?.phase === 'fight' && performance.now() - started < 1500) { - f.down = buttons(result.motion, result.action); f.until = performance.now() + (result.action === 'jump' ? 450 : 250); + f.plan = controlPlan(result.motion, result.action); f.issuedAt = performance.now(); + releaseMs = f.plan.releaseMs; } } catch (error) { - f.down = []; f.until = 0; f.failures++; f.move = 'Waiting for Jev'; + f.plan = null; f.failures++; f.move = 'Waiting for Jev'; if (error.status !== 503 || f.failures >= 3) stop(error.message); } finally { pending--; render(); } // Let a button release before its next press; all chosen holds are bounded. - await sleep(f.down.includes('ArrowUp') ? 500 : 300); + await sleep(releaseMs); } } $('start').addEventListener('click', async () => { diff --git a/xbox/live/jev-vs-jev/model.mjs b/xbox/live/jev-vs-jev/model.mjs index c89555bc3..bea954cf6 100644 --- a/xbox/live/jev-vs-jev/model.mjs +++ b/xbox/live/jev-vs-jev/model.mjs @@ -1,18 +1,54 @@ // The model chooses every direction and button. No pursuit/attack heuristic. -export const motion = { left: 'Move left', right: 'Move right', still: 'Do not walk' }; -export const actions = { kick: 'Kick (A), effective within about 165 units', - punch: 'Punch (B), effective within about 130 units', block: 'Shield (X)', - jump: 'Jump (up)', duck: 'Crouch (down)', item: 'Use held item (Y)', wait: 'No action button' }; +export const motion = { + left: 'Walk left for 180 ms (about 158 units). Close a large gap.', + right: 'Walk right for 180 ms (about 158 units). Close a large gap.', + step_left: 'Step left for 55 ms (about 48 units). Close a small gap.', + step_right: 'Step right for 55 ms (about 48 units). Close a small gap.', + face_left: 'Tap left for 20 ms (about 18 units). Face left to strike without running past.', + face_right: 'Tap right for 20 ms (about 18 units). Face right to strike without running past.', + still: 'Stay put and keep facing the same way. Attack from here when already close.', +}; +export const actions = { kick: 'Attack with A: kick, reach about 165 units. Can kick while moving toward the opponent.', + punch: 'Attack with B: punch, reach about 130 units. Can punch while moving toward the opponent.', block: 'Shield (X); cannot move or attack while shielding', + jump: 'Jump (up)', duck: 'Crouch (down)', + drop: 'Double tap down: drop through the platform you stand on to reach an opponent below, or ground pound while airborne', + item: 'Use held item (Y)', wait: 'No action button' }; export function decisionRequest(scene) { + const state = cleanScene(scene); + const criteria = { ...actions }; + const combat = state.self.combat; + if (combat) { + if (combat.headOnly) { + criteria.kick = 'Spit a fast projectile with A toward the opponent.'; + criteria.punch = 'Spit a heavy projectile with B toward the opponent.'; + delete criteria.block; + } else { + if (!combat.punch) delete criteria.punch; + const side = state.relative?.opponentSide || (state.self.facing < 0 ? 'left' : 'right'); + if (!(side === 'left' ? combat.kickLeft : combat.kickRight)) delete criteria.kick; + if (combat.pogo) { + delete criteria.punch; delete criteria.kick; + criteria.duck = 'Pogo dive: while airborne press down to attack the opponent below.'; + } + } + if (!combat.item) delete criteria.item; + } const instructions = 'You control one fighter in Oskiewar. Win by landing attacks while avoiding the opponent. ' + - 'Choose actual controller input for the next 250 milliseconds (450 milliseconds for a full jump). No other bot helps you. ' + - 'Coordinates: x increases right, y increases down; opponent.dx is opponent.x minus self.x. ' + - 'Walk toward the opponent to enter striking range. Shield can block attacks but cannot attack simultaneously. ' + - 'Use visible platforms/options to reach an opponent above you. Do not walk off your footing. ' + + 'Choose a direction pulse and an action together. The next decision arrives roughly 400 ms later. No other bot helps you. ' + + 'Coordinates: x increases right, y increases down; opponent.dx = opponent.x - self.x, opponent.dy = self.y - opponent.y (positive means opponent above). ' + + 'Fight actively: close the gap, face the opponent, and punch or kick repeatedly when near. ' + + 'At distance under 100, stay still if facing the opponent, or choose a face tap if facing away; attack instead of walking through them. ' + + 'At distance 100 to 210, use a short step and attack together. At larger gaps, walk toward them. ' + + 'Attacks last 220 ms, so a strike while closing can connect even if just outside its range now. ' + + 'Use self.combat to avoid attacks with missing limbs. Prefer an available punch or kick over waiting. ' + + 'Do not jump just because an airborne opponent is passing overhead: wait on the ground for them to land and strike. ' + + 'Jump to reach a grounded opponent on a higher platform, not as a default dodge. ' + + 'Shield briefly only against an imminent strike. Shielding and retreating do not deal damage. ' + + 'Use visible platforms/options to reach an opponent above you. Choose drop to descend through a platform when the opponent is below; one crouch does not drop through. ' + 'Both fighters run the same model and receive the same rules.'; - return { state: cleanScene(scene), questions: { + return { state, questions: { motion: { type: 'choice', criteria: motion, instructions }, - action: { type: 'choice', criteria: actions, instructions }, + action: { type: 'choice', criteria, instructions }, } }; } const number = value => Number.isFinite(value) ? Math.max(-100000, Math.min(100000, Math.round(value))) : 0; @@ -27,14 +63,27 @@ export function cleanScene(scene) { punchInRange: Math.abs(dx) < 130 && Math.abs(dy) < 90, kickInRange: Math.abs(dx) < 165 && Math.abs(dy) < 90 } : null, self: { ...point(scene.self), grounded: scene.self.grounded === true, - footing: scene.self.footing ? point(scene.self.footing) : null }, - opponent: scene.opponent ? { ...point(scene.opponent), attacking: scene.opponent.attacking === true } : null, + footing: scene.self.footing ? point(scene.self.footing) : null, + combat: scene.self.combat ? Object.fromEntries(['punch','kickLeft','kickRight','item','stunned','headOnly','pogo'].map(key => [key, scene.self.combat[key] === true])) : null }, + opponent: scene.opponent ? { ...point(scene.opponent), grounded: scene.opponent.grounded === true, attacking: scene.opponent.attacking === true } : null, floor: scene.floor ? point(scene.floor) : null, rungs: (Array.isArray(scene.rungs) ? scene.rungs : []).slice(0, 8).map(point), options: (Array.isArray(scene.options) ? scene.options : []).slice(0, 8).map(o => ({ ...point(o), kind: ['walk','jump','sink'].includes(o.kind) ? o.kind : 'walk' })) }; } export function buttons(move, action) { if (!Object.hasOwn(motion, move) || !Object.hasOwn(actions, action)) throw new Error('Invalid controller choice'); - return [move === 'left' ? 'ArrowLeft' : move === 'right' ? 'ArrowRight' : null, - { kick:'A', punch:'B', block:'X', jump:'ArrowUp', duck:'ArrowDown', item:'Y' }[action]].filter(Boolean); + return [move.endsWith('left') ? 'ArrowLeft' : move.endsWith('right') ? 'ArrowRight' : null, + { kick:'A', punch:'B', block:'X', jump:'ArrowUp', duck:'ArrowDown', drop:'ArrowDown', item:'Y' }[action]].filter(Boolean); +} +export function controlPlan(move, action) { + const down = buttons(move, action); + const motionMs = move === 'still' ? 0 : move.startsWith('face_') ? 20 : move.startsWith('step_') ? 55 : 180; + const actionMs = action === 'jump' ? 450 : action === 'drop' ? 220 : action === 'wait' ? 0 : 150; + return { down, motionMs, actionMs, actionWindows: action === 'drop' ? [[0,60],[130,220]] : [[0,actionMs]], + releaseMs: Math.max(motionMs, actionMs, 150) + 60 }; +} +export function controlsAt(plan, elapsed) { + if (!plan || elapsed < 0) return []; + return plan.down.filter(key => key === 'ArrowLeft' || key === 'ArrowRight' + ? elapsed < plan.motionMs : plan.actionWindows.some(([from,to])=>elapsed >= from && elapsed < to)); } diff --git a/xbox/live/oskiewar.js b/xbox/live/oskiewar.js index 953c135ed..b180061f0 100644 --- a/xbox/live/oskiewar.js +++ b/xbox/live/oskiewar.js @@ -9195,6 +9195,16 @@ function highestJump(options) { function botPad(player, opponent, now) { if (selfPlay && typeof globalThis.__oskiewarJevPad === 'function') { const scene = botScene(player, opponent, now); + scene.self.combat = { + punch: Boolean(availableArm(player)), + kickLeft: hasPart(player, 'left-leg'), kickRight: hasPart(player, 'right-leg'), + item: Boolean(heldItem(player)), stunned: now < player.hitStunUntil, + headOnly: isHeadOnly(player), pogo: isPogo(player), + }; + if (scene.opponent) { + scene.opponent.grounded = opponent.grounded; + scene.opponent.vx = opponent.vx; scene.opponent.vy = opponent.vy; + } const down = globalThis.__oskiewarJevPad(player.pad, { ...scene, round: matchName, alive: player.alive && opponent.alive }); return { connected: true, down: Array.isArray(down) ? down.filter(button => diff --git a/xbox/live/social/manifest.json b/xbox/live/social/manifest.json index bbec8bcad..5acb5d065 100644 --- a/xbox/live/social/manifest.json +++ b/xbox/live/social/manifest.json @@ -1,7 +1,7 @@ { "format": "ac.oskiewar.social-preview", "version": 2, - "build": "3fcb9b9d1e9e2acb", + "build": "049308608f0d462e", "theme": "light", "imageWidth": 1200, "imageHeight": 630, diff --git a/xbox/live/social/oskiewar-title.jpg b/xbox/live/social/oskiewar-title.jpg index d3386a0ed0d0e1be73f0237f9709c91dd6957eba..cfc93d6bcf3294f3118ed597443f85b14797c989 100644 GIT binary patch literal 59339 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