Joined: 4/17/2010
Posts: 11495
Location: Lake Chargoggagoggmanchauggagoggchaubunagungamaugg
Bird's behavior looks VERY random and smart, but in fact it's plain stupid:
If above Ryu, decrement Y, else increment Y.
If on the left from Ryu, increment X subpixel speed, else decrement it.
Here's the code. I will then make a script that predicts bird trajectory based on the current Ryu's position. Stay tuna.
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Joined: 4/17/2010
Posts: 11495
Location: Lake Chargoggagoggmanchauggagoggchaubunagungamaugg
Language: lua
curves = {}
function PredictBird()
-- feos, 2014
-- draws birds trajectories
-- color marks direction
for slot = 0, 7 do
if (memory.readbyte(0x400 + slot) ~= 11) or (memory.readbyte(0x498 + slot) == 0) then
curves.slot = nil
else
if (curves.slot == nil) then curves.slot = {} end
local ryuY = memory.readbyte(0x8A)
local ryuX = memory.readbyte(0x86)
local birdY = memory.readbyte(0x480 + slot)
local birdX = memory.readbyte(0x460 + slot) + memory.readbyte(0x458 + slot)/256
local birdSpeed = memory.readbytesigned(0x450 + slot) + memory.readbyte(0x448 + slot)/256
local newY = 0
local newX = 0
local newSpeed = 0
while (#curves.slot <= 200) do
if (#curves.slot == 0) then
if (birdY > ryuY)
then newY = birdY - 1
else newY = birdY + 1
end
if (birdX > ryuX)
then newSpeed = birdSpeed - 16/256
else newSpeed = birdSpeed + 16/256
end
newX = birdX + newSpeed
else
local index = #curves.slot
local tempY = curves.slot[index].oldY
local tempX = curves.slot[index].oldX
local tempSpeed = curves.slot[index].oldSpeed
if (tempY > ryuY)
then newY = tempY - 1
else newY = tempY + 1
end
if (tempX > ryuX)
then newSpeed = tempSpeed - 16/256
else newSpeed = tempSpeed + 16/256
end
newX = tempX + newSpeed
end
table.insert(curves.slot, {oldY = newY, oldX = newX, oldSpeed = newSpeed})
end
if (#curves.slot == 200) then table.remove(curves.slot, 1) end
for index = 1, #curves.slot do
local color = nil
if (curves.slot[index].oldSpeed < 0) then color = "#008800" else color = "#0000ff" end
gui.box (curves.slot[index].oldX - 1, curves.slot[index].oldY - 1,
curves.slot[index].oldX + 1, curves.slot[index].oldY + 1, color)
end
for index = 1, #curves.slot do
gui.pixel(curves.slot[index].oldX, curves.slot[index].oldY, "white")
end
end
end
end
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Joined: 4/17/2010
Posts: 11495
Location: Lake Chargoggagoggmanchauggagoggchaubunagungamaugg
When working on knife thrower, I found out knife timeout decrements NOT every frame (sometimes it doesn't). Will debug tomorrow.
EDIT: When new level block set is loaded into video RAM, object attributes such as timers don't count. Object positioning is still counted in a temp loop. So, since you move at constant speed, you can't manipulate these knife timers by skipping their routines deliberately. Which is sad, will need to get around tossers the hard way...
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Joined: 4/17/2010
Posts: 11495
Location: Lake Chargoggagoggmanchauggagoggchaubunagungamaugg
Yeah, now I need to find the mood to finish levels 2 and 3.
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Joined: 4/17/2010
Posts: 11495
Location: Lake Chargoggagoggmanchauggagoggchaubunagungamaugg
Close enough
Old style
Language: lua
function GetCell(X,Y)
local temp = memory.readbyte(0xE7CC+SHIFT(X,4))+memory.readbyte(0x5F)
if (temp >= 0xC0) then temp = temp-0xC0 end
Y = Y-0x40
if (Y < 0) then Y = 0 end
temp = SHIFT(Y,5)+temp
return temp
end
function DrawBG(arg,offset,x,y)
local color2 = "#00ff00ff"
local function box(color,text)
gui.box(x,y+offset,x+16,y+offset+16,color)
if (text == 1) then
gui.text(x+1,y+offset+1,string.format("%d",arg))
end
end
local function line(up,down,left,right)
if (up == 1) then gui.line(x ,y+offset ,x+16,y+offset ,color2) end
if (down == 1) then gui.line(x ,y+offset+16,x+16,y+offset ,color2) end
if (left == 1) then gui.line(x ,y+offset ,x ,y+offset+16,color2) end
if (right == 1) then gui.line(x+16,y+offset ,x+16,y+offset+16,color2) end
end
if (arg ~= 0) then
if (arg == 1) then line(0,0,0,1) -- right wall
elseif (arg == 2) then line(0,0,1,0) -- left wall
elseif (arg == 3) then line(0,0,1,1) -- two-sided wall
elseif (arg == 4) then line(1,0,0,1) -- right corner
elseif (arg == 5) then line(1,0,1,0) -- left corner
elseif (arg == 6) then line(1,0,1,1) -- two-sided corner
elseif (arg == 7) then line(1,0,0,0) -- floor
elseif (arg == 8) then box("#ff000066",0) -- ejecting block
elseif (arg == 9) then box("#00ff0066",0) -- ladder
elseif (arg >= 12) and (arg <= 15)
then box("#ffffff66",0) -- exits
else box("#00ff0066",1)
end
end
end
function ViewBG(style)
-- feos, 2014
-- style: 0=none, 1=new, 2=old, 3=both
local base = 0x300
local RyuX = memory.readbyte(0x86)
local RyuY = memory.readbyte(0x8A)
local RyuYspeed = memory.readbytesigned(0x89)
local RyuXspeed = memory.readbytesigned(0xAD)+memory.readbyte(0xAC)/256
if (AND(memory.readbyte(0x84),4) == 0) then RyuYspeed = 0 end
local RyuCell = GetCell(RyuX, RyuY+RyuYspeed)
local RyuRow = math.floor(RyuCell/6)
local Screen = memory.readwordsigned(0x51)
if (AND(style,1) == 1)
and (memory.readbyte(0x1FC) == 0x87)
or (memory.readbyte(0x1F3) == 0xD8) then
for tRow = RyuRow-14, RyuRow+14 do
for tLine = 0,5 do
local address = base+((tRow*6+tLine)%0xC0)
local hi = SHIFT(memory.readbyte(address), 4)
local lo = AND(memory.readbyte(address),0xF)
local x = (tRow-RyuRow)*16+RyuX-RyuX%0x10-Screen%0x10
local y = tLine*32+64
DrawBG(hi, 0,x,y)
DrawBG(lo,16,x,y)
end
end
gui.box(xpos-9,ypos+RyuYspeed-1,xpos+5,ypos+RyuYspeed-5,"#0000ff66")
end
if (AND(style,2) == 2) then
for cell = 0,191 do
local hi = SHIFT(memory.readbyte(base+cell), 4)
local lo = AND(memory.readbyte(base+cell),0xF)
local bX = math.floor(cell/6)
local bY = cell%6
local rX = (RyuRow%32)*6-1
local rY = math.floor(RyuY/16)*8-32
if (hi == 0) then hi = " " else hi = string.format("%X",hi) end
if (lo == 0) then lo = " " else lo = string.format("%X",lo) end
gui.text(bX*6,bY*16+9,hi.."\n"..lo)
gui.box(rX,rY,rX+6,rY+8,"#00ff0000")
end
end
end
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Sorry you had to figure that out yourself, I could have sworn I'd put something about that in this thread already or on the game resources page, but hey, debugging is fun. There are a few opportunities to manipulate $B5 with that too, but I never found one that saved any time. This run takes advantage of it to manipulate the level timer in 4-2 and to freeze some running dudes while climbing a wall in 5-3C.
Awesome work with those scripts. Have either of them helped any with the pacifist thing?
The basic (NGRAMview()) lua script: yes
User movie #13843139913122407User movie #13843227963546744
edit: it looks like that saving a half pixel loses a frame on boss 1, I've tried my movie and Scumtron's "any%" movie, same result.
And my movie is also improvable with using the "preserve speed while airborne", but I don't understand which half pixels are good.
edit2: (asking feos:) what's the "restrictions" for the pacifist version? I see that Scumtron picks powerups to "instakill" first boss. Is it possible to implement it and improve level 1+boss with that route? (it requires atleast +1 damage take if I'm right)
PhD in TASing 🎓 speedrun enthusiast ❤🚷🔥 white hat hacker ▓ black box tester ░ censorships and rules...
Joined: 4/17/2010
Posts: 11495
Location: Lake Chargoggagoggmanchauggagoggchaubunagungamaugg
Powerups require using the sword outside boss rooms. And a run with them would look just similar to the any%. Se here we go for more variety over it, and for more clean pacifist concept.
EDIT:
While resyncing your improvements (that are hilarious), I got stuck at nearly every other tyson, because they changed their behavior. And you know what? I must debug this crap to understand what they really depend on.
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Resyncing?
Well it looked like they only depend on Ryu's X position. Maybe they have timers too (when to punch)?
edit: changing Ryu's X position only manipultes Tyson if they are spawned (first frame to use is when Tyson is drawn on screen).
PhD in TASing 🎓 speedrun enthusiast ❤🚷🔥 white hat hacker ▓ black box tester ░ censorships and rules...
Unlike all the other bosses, boss1 inherits subpixel pos and being that half-pixel ahead throws the manipulation off (one of the boxers at the end of 1-1). Every time I looked at saving that half-pixel, I decided against it because it doesn't save any time and I think it looks worse.
Not sure I get your meaning, but most of the time it just doesn't matter, unfortunately.
IIRC, they do have a timer set when they spawn, but there's no randomness to it.
I referred to my earlier uploaded movie about "not perfect" speed preserving (U+D+L etc), but already fixed it on the last one I think (required to minimize recovery after being hit).
Thanks for the informations, I will test it without that half pixel then.
edit2: taking away half pixel here makes the next "recovery walljump" 3 X pos lower (2 frames slower)
And also removing half pixel at the end of 1-1 doesn't change boss 1. So I'm out of ideas to improve the pacifist 1-1 and boss (I don't see the possibility either).
edit: also I'm curious why specific "sticky floors" can be avoided why some of them always "sticks"?
For example, first one avoidable, second one isn't (in the pacifist one)
PhD in TASing 🎓 speedrun enthusiast ❤🚷🔥 white hat hacker ▓ black box tester ░ censorships and rules...
Joined: 4/17/2010
Posts: 11495
Location: Lake Chargoggagoggmanchauggagoggchaubunagungamaugg
I commented out the BG collision hitbox. If it's inside the block with it's first corner, you will be stuck for 1 frame. Jump so that your hitbox beginning is outside that block. It isn't possible on that wall platform, since corners seem to stick you for a frame as well, and you don't have enough room to go past it with your hitbox.
Updated script that shows the hitbox, and also the bits of your BG collision state.
http://pastebin.com/Qv7hXviv
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
(reworded edit2:)
Tysons manipulation requires variable X positions to sacrifice few frames before he executes an attack, where this number depends on the time of positioning.
For example, sometimes you can avoid sacrificing 0.5 or even 1.0 X position if you do it at correct time (obviously it's also depends on other factors like "possibility" of pulling out this).
(My guess is the game calculates the distance between the enemy and Ryu)
This can be optimized with left button while airborne (squeezing out pixels).
Also worth to note Pacifist tests done:
Dog RWJ can't be improved with 0.5px less X position beforehand (same result)
Tyson manipulation after dog doesn't seems to be improvable
Tyson RWJ already fastest
Both level 1 and boss looks OK.
edit3: Uploaded User movie #13885349170462746, which tries to recreate feos marx version but 3 Tysons are a bit unregretful in level 2
PhD in TASing 🎓 speedrun enthusiast ❤🚷🔥 white hat hacker ▓ black box tester ░ censorships and rules...
Joined: 4/17/2010
Posts: 11495
Location: Lake Chargoggagoggmanchauggagoggchaubunagungamaugg
Tyson spawns on the ground ($488+slot = 0x30). Then he keeps jumping at some rate until Ryu is right there. He can change his action ($410+slot) only before every jump. When Ryu is within 0x20 pixels, Tyson's next jump will set his activity to "ready" (3). The next jump he will be "steady" (4). Then next jump he will attack (5). Direction he flies depends on where Ryu is.
The only inconsistent thing here is jumping rate (9, 10, 11 frames per jump). Will investigate tomorrow.
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
Joined: 3/23/2012
Posts: 296
Location: In X position=50 y position=20
0017 has something to do with tyson's behavior . when he punch the value becomes 4 and when there is no tyson on the screen the value is 123 and 13 if ryu moves to right.
Few more tests about Tysons.
1. Losing 1.5px on first 3 enemies: Tyson still needs an empty frame.
Losing 2.0px on first 3 enemies (L): Tyson doesn't needs the empty frame. However still -0.5px behind which loses 2 frames overall (since doesn't affects "future" Tysons)
2. Losing additional 1 HP doesn't save nor lose a frame (~516 RWJ, first L for sign jump, R for next Tyson, half pixel for last Tyson manipulation)
edit2: it's alters the 2nd Tyson on level 2, uploading it :User movie #13933509768180001 so it's currently loses 1 frame additionally. Boss 1 is same because I guess the game only takes away points for 2 healths lost not 1.always forget that stupid "health to score points" is instant.
3. I've tried to juggle with Interrupts (you can infinitely freeze enemies at correct position with constant wiggling), but I don't see any way to implement it as a time saving trick.
edit: there's a lot of other zero page addresses.
PhD in TASing 🎓 speedrun enthusiast ❤🚷🔥 white hat hacker ▓ black box tester ░ censorships and rules...
Joined: 4/17/2010
Posts: 11495
Location: Lake Chargoggagoggmanchauggagoggchaubunagungamaugg
Each time Tyson starts jumping, his subpixel Y speed is nullified, and then 0x37 is added to it. Two factors matter here:
- Interrupt, when the new block set is loaded, freezes him completely for 1 frame. So his jumps may last longer by that frame.
- Subpixel Y position is never initialized, it's inherited from the previous enemy in that slot. So it may also alter what frame he reaches the block border by 1 frame.
Interrupts may or may not help, but the inherited Y subpixels from the previous enemy can be adjusted. And these 2 seem the only ways to affect his jumps, and therefore his actions.
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.
I interpret interrupts like a frame rule. Yes, it's good if you get it early after a Tyson who needs to be manipulated, but that's all I think.
Differences between WIP5 and WIP6 (from level 2 first frame with walls in lua), one of them manipulates Tysons:
Lot of values around 0x20B~0x2FB are greater with 1 value in WIP 5
0x20B,0x20F,
0x21F,0x223,
0x233,0x237,
0x247,0x24B... (not pattern recognized)
0x47E == DE for a 1 frame better tyson for 2nd Tyson, == 02 for the slower one.
I've tried to improve a few frames, but they get "nullified" since I'm more "beyond" the bullets. Also "interrupt" juggling is good for single enemies, but since it's also freezes bullets...
So currently we get a few more enemies that can "desync" my route from 7900:
- the knife throwers, who sometimes throw themself or get rage and throw you
- the green soldier at the very end who can catch us
- the bullets from the SMG guy
I'm pretty sure my WIP is improvable by few frames, but it might be a good idea to continue the WIP and start optimizing/throwing in delays later (I guess it's make it easier to connect different levels with good (enemy sub y pos) values rather than saving every frame and get pwned by another NPC). Opinions, ideas?
edit: I will watch Scumtron's NG1 TAS sinister1's pacifist RTA and translate at east the 2nd boss to pacifist version. Well I optimized simple things, and will upload it once I find fastest strategy for boss 2 (jumping with burst damage (bd, empty, bd, empty, bd) or only for first and last time?))
edit2: any fancy thing about the sprite limits?
edit3: I've used boss hp monitor addresses:
0x66 boss hp
0x497 boss hp (1 remains)
I guess 0x496 is the "graphical" value and can be decreased without the two other value decrease. Why? (2-3 boss, attack a bit earlier for 1 time)
edit4: the bird trajectory script is awesome. the walls lua should be updated, at least I found an invisible sticky floor:
PhD in TASing 🎓 speedrun enthusiast ❤🚷🔥 white hat hacker ▓ black box tester ░ censorships and rules...
So... 2-2 1st screen looks like unimprovable, because the bullets goes too fast to have enough time to delay jumping to avoid hitting the green soldier in front of us (the 3 jump presented here)
2-2 2nd screen RWJ is improvable with 1 frame, but gives a much worse knife thrower (hitting himself). Tried some delays, nothing good. And everytime I tried to save somehow a frame on 2-2 last (3rd) screen, I was always much frames behind.
Currently getting a wrong knife thrower at 3-1 2nd one. Trying to sacrifice as few frames as possible. edit: Currently I need to sacrifice 2.5~7 X position (desync comes from much faster boss battle).
(he should stop at midway and think about his life for a few seconds)
PhD in TASing 🎓 speedrun enthusiast ❤🚷🔥 white hat hacker ▓ black box tester ░ censorships and rules...
Joined: 4/17/2010
Posts: 11495
Location: Lake Chargoggagoggmanchauggagoggchaubunagungamaugg
The thing is, these 2 are different kinds of blocks. Red block pushes you away, and you can't grab it, corners you can grab, but yeah, they are "sticky" too.
As for myself, I will debug enemy spawn time and position. Will be obvious why.
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.