Post subject: LUA script to allow interaction between emulator instances?
Player (202)
Joined: 1/24/2011
Posts: 108
When I'm working on a run, I like to have two instances of an emulator open at once - one with the current run-in-progress, and one with a test run or older run with which to compare my progress. I go frame-by-frame, making sure that my current run is doing better than the older one. I just had an idea that could potentially make this easier, but I'm having trouble writing the LUA scripts to implement it. My idea is that the two instances could use a file as an intermediary for sharing data, such as screen position, subpixels, character velocity, et cetera. The older run's emulator could write the values to the file, and the current run's emulator could read the data and turn the output text red if it's lagging behind the older run, white if it's the same, or green if it's doing better. I've been trying to teach myself LUA, but the guide linked in the articles page is not particularly good at that. If someone could give me an idea of how this would be set up (of if it's even possible to implement), I'd really appreciate it.
Rayas wrote:
Dunno if I'm really clear. I need to drink more.
<br>
adelikat wrote:
The idea was to kill off my family to avoid lost time to them getting sick and other inconvenient things.
Patashu
He/Him
Joined: 10/2/2005
Posts: 4042
My Chiptune music, made in Famitracker: http://soundcloud.com/patashu My twitch. I stream mostly shmups & rhythm games http://twitch.tv/patashu My youtube, again shmups and rhythm games and misc stuff: http://youtube.com/user/patashu
Emulator Coder, Site Developer, Former player
Joined: 11/6/2004
Posts: 833
You'd probably be better using sockets. Find the socket library for lua and install it. You can send packets to each instance over TCP or UDP on the 127.0.0.1 loopback interface, or even between several PCs on a network (or the internet). UDP throws data around in discrete lumps, and will be reliable over the loopback address. TCP acts like a serial cable, but beware of trying to read from a TCP socket with no data available because you'll jam up the program.
Patashu
He/Him
Joined: 10/2/2005
Posts: 4042
Oh, that's a good idea. You'd make it advance frame when sent a packet, then send back the frame number and RAM values of the new frame?
My Chiptune music, made in Famitracker: http://soundcloud.com/patashu My twitch. I stream mostly shmups & rhythm games http://twitch.tv/patashu My youtube, again shmups and rhythm games and misc stuff: http://youtube.com/user/patashu
Skilled player (1651)
Joined: 11/15/2004
Posts: 2202
Location: Killjoy
To be honest, this seems like a waste of extra effort (Hear me out, please). Since the 'older run' is done, you could easily do what I do for comparison -Have a dump script, and a read script. First, the dump script starts at a known location - beginning of a level, battle, whatever - and then dumps all of the relevant stuff to a text or binary file (very easy in lua). Next, the read script grabs that information (on load), and does a comparison for you, frame by frame. No need to have crazy socket-based conversations over lua, which, for what you are trying to achieve, sounds like overkill.
Sage advice from a friend of Jim: So put your tinfoil hat back in the closet, open your eyes to the truth, and realize that the government is in fact causing austismal cancer with it's 9/11 fluoride vaccinations of your water supply.
Player (202)
Joined: 1/24/2011
Posts: 108
DarkKobold wrote:
To be honest, this seems like a waste of extra effort (Hear me out, please). Since the 'older run' is done, you could easily do what I do for comparison -Have a dump script, and a read script. First, the dump script starts at a known location - beginning of a level, battle, whatever - and then dumps all of the relevant stuff to a text or binary file (very easy in lua). Next, the read script grabs that information (on load), and does a comparison for you, frame by frame. No need to have crazy socket-based conversations over lua, which, for what you are trying to achieve, sounds like overkill.
That's exactly what I'm looking to do. My first thought was to have this be done on the fly, frame-by-frame with two runs open at once, but this sounds more reasonable. I'm just starting to figure out lua - I can draw values on the screen, but I can't get them to output into an external file. Could you give me an idea of how I should be doing this?
Rayas wrote:
Dunno if I'm really clear. I need to drink more.
<br>
adelikat wrote:
The idea was to kill off my family to avoid lost time to them getting sick and other inconvenient things.
Skilled player (1651)
Joined: 11/15/2004
Posts: 2202
Location: Killjoy
Sort of different, but same concept. Here is the one I used for shining soul to dump all the possible RNG values to a file.
Language: lua

Max = 32767 --Read in the next bunch of RNG addresses RNG1 = 0x03001438; RNG2 = 0x0300143A; RNG3 = 0x0300143C; RNG4 = 0x0300143E; RNGFile = io.open('SS2RNG.txt','w'); repeater = false Ra1 = memory.readword(RNG1); Ra2 = memory.readword(RNG2); Ra3 = memory.readword(RNG3); Ra4 = memory.readword(RNG4); s = 0; vba.frameadvance(); while not repeater do s = s+1; R1 = memory.readword(RNG1); R2 = memory.readword(RNG2); R3 = memory.readword(RNG3); R4 = memory.readword(RNG4); outs = string.format('%d %d %d %d\n',R1,R2,R3,R4); RNGFile:write(outs); vba.frameadvance(); if (R1 == Ra1) and (R2 == Ra2) and (R3 == Ra3) and (R4 == Ra4) then repeater = true; end; end; print('done'); print(s); RNGFile:close();
And then, to play them back, and tell me my position in the loop
Language: lua

Max = 100000; --Track RNG Movement RNG1 = 0x03001D28; RNG2 = 0x03001D2A; RNG3 = 0x03001D2C; RNG4 = 0x03001D2E; RNGFile = io.open('SS2RNG.txt','r'); RL1 = {}; RL2 = {}; RL3 = {}; RL4 = {}; F = 0; Exe = 0; for i = 1,Max,1 do RL1[i] = RNGFile:read("*n"); RL2[i] = RNGFile:read("*n"); RL3[i] = RNGFile:read("*n"); RL4[i] = RNGFile:read("*n"); end; curpos = 1; while (curpos ~= Max) and not found do if (memory.readword(RNG1) == RL1[curpos]) and (memory.readword(RNG2) == RL2[curpos]) and (memory.readword(RNG3) == RL3[curpos]) and (memory.readword(RNG4) == RL4[curpos]) then found = true; else curpos = curpos + 1; end; end; Bad = false; while not Bad do found = false; state = curpos; cnt = 0; curpos = curpos - 100; while (cnt ~= 2) and not found do cnt = cnt + 1; if cnt == 2 then curpos = 1; end; while (curpos ~= Max) and not found do if (memory.readword(RNG1) == RL1[curpos]) and (memory.readword(RNG2) == RL2[curpos]) and (memory.readword(RNG3) == RL3[curpos]) and (memory.readword(RNG4) == RL4[curpos]) then found = true; else curpos = curpos + 1; end; end; end; if not found then Bad = true; else F = F + 1; Exe = (curpos-state) + Exe; gui.text(1,1,string.format('Exec = %d Pos = %d Avg = %f1.3',curpos-state, curpos, Exe/F)); end; vba.frameadvance(); end; print('RNG not found, script exiting.'); print(string.format('Expected: %d %d %d %d', RL1[state+1],RL2[state+1],RL3[state+1],RL4[state+1])); print(string.format('Got: %d %d %d %d', memory.readword(RNG1), memory.readword(RNG2),memory.readword(RNG3),memory.readword(RNG4))); RNGFile:close();
Sage advice from a friend of Jim: So put your tinfoil hat back in the closet, open your eyes to the truth, and realize that the government is in fact causing austismal cancer with it's 9/11 fluoride vaccinations of your water supply.
Player (202)
Joined: 1/24/2011
Posts: 108
Okay, I've been working on this for much of the day and I still can't get it to work. I keep on getting errors related to the frame counter, so I guess that I'm doing it wrong, but I can't seem to find any documentation on movie.framecount() anywhere so I'm not sure what I'm missing. Even with the frame variable removed from the string, nothing actually gets written to the document. Here's what I'm trying to work with:
local XPosVar=0x0012
local YPosVar=0x0011
local Max=100
local Cur=0
local Rest
local Frame
local ScreenVar=0x0040
local PixelVar=0x0041
local SubPixelVar=0x0042
local SpeedVar=0x0046
local SpeedSubVar=0x0047
OutputFile=io.open('data.txt', 'w')

 while Cur <= Max do
  Frame=int movie.framecount()
  XPos=memory.readbyte(XPosVar)
  YPos=memory.readbyte(YPosVar)
  Screen=memory.readbyte(ScreenVar)
  Pixel=memory.readbyte(PixelVar)
  SubPixel=memory.readbyte(SubPixelVar)
  Speed=memory.readbyte(SpeedVar)
  SpeedSub=memory.readbyte(SpeedSubVar)
  outs = string.format('%d %d %d %d %d %d %d %d\n',Frame,XPos,YPos,Screen,Pixel,SubPixel,Speed,SpeedSub)
  OutputFile:write(outs)
  Cur=Cur+1
  Rest=Max-Cur
  --gui.text(0,0, "Dumping Frame " .. Frame)
  --gui.text(0,10, "Frames to Go: " .. Rest)
  emu.frameadvance()
 end
The goal here is to grab various values (screen position, position within the level, speed, etc) from the memory and write them to an external file, along with the frame number. I feel like I'm missing something very obvious, but I'm not sure what it is...
Rayas wrote:
Dunno if I'm really clear. I need to drink more.
<br>
adelikat wrote:
The idea was to kill off my family to avoid lost time to them getting sick and other inconvenient things.
Patashu
He/Him
Joined: 10/2/2005
Posts: 4042
Is it because you're not putting ;s after your statements?
My Chiptune music, made in Famitracker: http://soundcloud.com/patashu My twitch. I stream mostly shmups & rhythm games http://twitch.tv/patashu My youtube, again shmups and rhythm games and misc stuff: http://youtube.com/user/patashu
Player (202)
Joined: 1/24/2011
Posts: 108
Patashu wrote:
Is it because you're not putting ;s after your statements?
It doesn't appear to make a difference here. The examples at http://tasvideos.org/LuaScripting/Display.html don't use semicolons either and they seem to work okay. Just to make sure I put them in and ran into the same problems.
Rayas wrote:
Dunno if I'm really clear. I need to drink more.
<br>
adelikat wrote:
The idea was to kill off my family to avoid lost time to them getting sick and other inconvenient things.
Skilled player (1651)
Joined: 11/15/2004
Posts: 2202
Location: Killjoy
Tanooki Teabag wrote:
local Frame
....
  Frame=int movie.framecount()
This is probably your only problem - lua doesn't need casts. Also, you don't need to declare everything as locals - you are probably better just typecasting them.
int Frame = 0; 
...
  Frame= movie.framecount()
or just
Frame = 0; 
...
  Frame= movie.framecount()
lua is probably getting all screwed up, because it wants to do a variable deceleration, not a cast.
Sage advice from a friend of Jim: So put your tinfoil hat back in the closet, open your eyes to the truth, and realize that the government is in fact causing austismal cancer with it's 9/11 fluoride vaccinations of your water supply.
Player (202)
Joined: 1/24/2011
Posts: 108
DarkKobold wrote:
Tanooki Teabag wrote:
local Frame
....
  Frame=int movie.framecount()
This is probably your only problem - lua doesn't need casts. Also, you don't need to declare everything as locals - you are probably better just typecasting them.
int Frame = 0; 
...
  Frame= movie.framecount()
or just
Frame = 0; 
...
  Frame= movie.framecount()
lua is probably getting all screwed up, because it wants to do a variable deceleration, not a cast.
That was it! I got th "int movie.framecount()" from the list of functions that came with FCEUX 2.1.4a but I didn't know that I needed to remove it. Thanks - writing to the file works now. Time to figure out how to read it...
Rayas wrote:
Dunno if I'm really clear. I need to drink more.
<br>
adelikat wrote:
The idea was to kill off my family to avoid lost time to them getting sick and other inconvenient things.
Skilled player (1651)
Joined: 11/15/2004
Posts: 2202
Location: Killjoy
Tanooki Teabag wrote:
That was it! I got th "int movie.framecount()" from the list of functions that came with FCEUX 2.1.4a but I didn't know that I needed to remove it. Thanks - writing to the file works now. Time to figure out how to read it...
ah, ok. So, "int movie.framecount()" is the function declaration, which tells you what variables to send (pass) it, surrounded by the parenthesizes. The variable type that proceeds it, tells you what it returns. So, for example, "string func_z(int x, int y)", means that it takes two integers, x and y, and returns a string.
Sage advice from a friend of Jim: So put your tinfoil hat back in the closet, open your eyes to the truth, and realize that the government is in fact causing austismal cancer with it's 9/11 fluoride vaccinations of your water supply.
Player (202)
Joined: 1/24/2011
Posts: 108
[URL=http://img841.imageshack.us/i/cocoron.jpg/][/URL] It works! Thanks for the help, DarkKobold! BTW, expect to see a run of Cocoron sometime in May.
Rayas wrote:
Dunno if I'm really clear. I need to drink more.
<br>
adelikat wrote:
The idea was to kill off my family to avoid lost time to them getting sick and other inconvenient things.
zwataketa
He/Him
Joined: 9/1/2012
Posts: 309
Does it work for other games and other emulators?
I quit TASing.