Post subject: Help with Lua scripts
Joined: 9/21/2014
Posts: 13
Hey, I couldn't find a good Lua tutorial for snes9x, and I can't figure out how to do it. Can anybody help me with this? P.S. I dunno if I placed this in the wrong category. If I did, moderator please move it.
Editor, Skilled player (1537)
Joined: 7/9/2010
Posts: 1319
Favorite animal: STOCK Gt(ROSA)26Sortm1.1(rtTA,EGFP)Nagy Grm7Tg(SMN2)89Ahmb Smn1tm1Msd Tg(SMN2*delta7)4299Ahmb Tg(tetO-SMN2,-luc)#aAhmb/J YouTube Twitch
Editor, Player (54)
Joined: 12/25/2004
Posts: 634
Location: Aguascalientes, Mexico
I was going to create a new topic, but it's about the same subject (help on LUA, also for SNES9x). On the Lua Scripting page, there's a TODO section to cover about joypad.set (which is what I'm interested on). I want to create a script for Final Fantasy V where you press every single combination of buttons (all 12: up, down, left, right, select, start, B, A, X, Y, L, R) in a single frame to see how the RNG advances when entering a battle. When I try this manually, it doesn't seem to act like each button advances the RNG by determined amount, so I can't calculate it and must go with trial and error (hence a need for a script to automate this part). I want to try to see if I can get a lucky path to beat Omniscient as Solo Berserker using Mage Masher to silence him. Since Berserkers doesn't need any input when playing (and doing so doesn't affect the RNG in any way), only part you can manipulate is the startup (which any other Job setup can benefit from, so the script could be used later for any other battle in particular and not just for this case...) Here's the steps I want to try:
1. Load save state
2. Press new combination of buttons for 1 frame
3. Advance (say 30 frames) and check values of 2 addresses
4. Send information to a file so I can analyze
5. Repeat steps
If the output is to big to try manually, then it'll be both good and bad news. (Good because fights can have many paths, hence more outcomes; Bad because it wouldn't be practical to test manually, so the script would need to updated, but I suppose that would be easy - just need to wait more time before trying a new input...) I know these are some of the instructions, but I'm not sure how to use them: 1. savestate.load(object savestate) -- <-- Guess I replace with the name of the save 2. Update "ButtonSequence" -- <-- This part is where I'm most lost Joypad.set(1, ButtonSequence) 3. emu.frameadvance() -- <-- Place it inside a FOR to advance X frames? A = memory.readbyte( 0x7E003A ) B = memory.readbyte( 0x7E003B ) 4. print (ButtonSequence, A, B) -- <-- Since BS is likely a table and not a number, this isn't straightforward I would later check the first instance of A,B having the same value in different button sequence, or maybe could be included first and print the results at the end... 5. Repeat until every button sequence is covered (2^12 times). Any help or better suggestion would be appreciated.
I'm the best in the Universe! Remember that!
Player (80)
Joined: 8/5/2007
Posts: 865
samurai goroh wrote:
I was going to create a new topic, but it's about the same subject (help on LUA, also for SNES9x). On the Lua Scripting page, there's a TODO section to cover about joypad.set (which is what I'm interested on). I want to create a script for Final Fantasy V where you press every single combination of buttons (all 12: up, down, left, right, select, start, B, A, X, Y, L, R) in a single frame to see how the RNG advances when entering a battle. When I try this manually, it doesn't seem to act like each button advances the RNG by determined amount, so I can't calculate it and must go with trial and error (hence a need for a script to automate this part). I want to try to see if I can get a lucky path to beat Omniscient as Solo Berserker using Mage Masher to silence him. Since Berserkers doesn't need any input when playing (and doing so doesn't affect the RNG in any way), only part you can manipulate is the startup (which any other Job setup can benefit from, so the script could be used later for any other battle in particular and not just for this case...) Here's the steps I want to try:
1. Load save state
2. Press new combination of buttons for 1 frame
3. Advance (say 30 frames) and check values of 2 addresses
4. Send information to a file so I can analyze
5. Repeat steps
If the output is to big to try manually, then it'll be both good and bad news. (Good because fights can have many paths, hence more outcomes; Bad because it wouldn't be practical to test manually, so the script would need to updated, but I suppose that would be easy - just need to wait more time before trying a new input...) I know these are some of the instructions, but I'm not sure how to use them: 1. savestate.load(object savestate) -- <-- Guess I replace with the name of the save 2. Update "ButtonSequence" -- <-- This part is where I'm most lost Joypad.set(1, ButtonSequence) 3. emu.frameadvance() -- <-- Place it inside a FOR to advance X frames? A = memory.readbyte( 0x7E003A ) B = memory.readbyte( 0x7E003B ) 4. print (ButtonSequence, A, B) -- <-- Since BS is likely a table and not a number, this isn't straightforward I would later check the first instance of A,B having the same value in different button sequence, or maybe could be included first and print the results at the end... 5. Repeat until every button sequence is covered (2^12 times). Any help or better suggestion would be appreciated.
I'm not going to test this script and it's probably not the most efficient way to do things, but here's a quick stab at it:
Language: lua

local function TestBit(num,whichbit) num = math.floor(num/2^whichbit) num = num%2 if num==1 then return true else return false end end local ButtonsPointer = {[0]='A','B','X','Y','select','start','up','down','left','right','L','R'} local ButtonsPressed = {} local A = {} local B = {} local state1 = savestate.create() for ButtonIndex = 0,2^12-1 do savestate.load(state1) for b = 0,11 do ButtonsPressed[ButtonsPointer[b]] = TestBit(ButtonIndex,b) end input.set(1,ButtonsPressed) -- CUSTOMIZE THE CODE BELOW TO SUIT YOUR NEEDS emu.frameadvance() -- Frame advance once? -- Does BizHawk have memory.readword? You may want to use that. A = memory.readbyte(0x7E003A) B = memory.readbyte(0x7E003B) -- BizHawk SHOULD be able to output tables to the console, but in case it can't like you suggest, you can just output ButtonIndex and manually check which buttons it corresponds to. print(ButtonIndex,A,B) end
Hopefully you can debug any problems with that code. Good luck!
AntyMew
It/Its
Encoder, Player (35)
Joined: 10/22/2014
Posts: 425
Bobo the King wrote:
Does BizHawk have memory.readword?
Bizhawk uses, for example, memory.read_u16_le for reading an unsigned little endian word
Just a Mew! 〜 It/She ΘΔ 〜
Player (146)
Joined: 7/16/2009
Posts: 686
For speed!
Language: Lua

-- Readable version function TestBit(number, bit) local flag = bit.lshift(1, bit); local test = bit.band(number, flag); return (test > 0); end -- Less readable version function TestBit(number, bit) return (bit.band(number, bit.lshift(1, bit)) > 0); end
This should provide a slight speedup by using BizHawk's built-in bit handling operations to test for bits being set. There's probably a cleverer way to iterate all possible input states with something like Gray codes or lazy generation of the power set (generating it all from the get-go would probably be overkill), but I'll leave that to people better at it.
Editor, Player (54)
Joined: 12/25/2004
Posts: 634
Location: Aguascalientes, Mexico
Thanks for the code Bobo the King, it worked just as I needed to. It seems (well with a sample of just 1 battle, but I'm positive to assume is the same case for all battles) that the RNG doesn't change much and there are a lot of repeated cases (only around 40 of 4096 different cases or ~1%). Guess that's good news to test by hand, but unfortunate for manipulating RNG as pleased. Edit: A bit hasty with that conclusion. It seems that while counters A & B are the same; there are other variables, such as enemy's initial attack gauge and some unidentified enemy addresses, that will change therefore affecting the outcome a tiny bit. Guess some more research on battle addresses will be needed for my purposes...
I'm the best in the Universe! Remember that!