feos

Red Face Optimal Script

Aug 16th, 2020
2,113
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- Written by Lobsterzelda in 2020 for Battletoads (NES on BizHawk)
  2.  
  3. -- This is the best script that I've written for reducing lag in the red faces sections of level 12 of Battletoads
  4. -- This script attempts to make RNG-address 2 (0X26) have a value which, when bitwise-ANDed with 7, has a value of 7.
  5. -- More specifically, this script only tries to manipulate what value RNG-2 has when a new object tries to load.
  6. -- If a new object is written to the object table and RNG-2 doesn't have a correct value, then an earlier save state is loaded.
  7. -- Random inputs are applied throughout this process, and the reloading of save states continues until a favorable RNG-2 value is reached.
  8. -- The above process continues until either 10 objects have loaded into memory or 200 frames have passed, at which point the total number of lag frames
  9. -- that occured is calculated to determine if this try had less lag frames than the previous best.
  10. -- 2500 trials are run using this process, and whenever an attempt generates a new lowest number of lag frames,
  11. -- the button presses that occured on the attempt are written to a file named "resultsOfRedFaceScriptFile.txt"
  12. -- The button presses are written to the file in a format that will let them be copy and pasted directly to the "Input Log.txt" file of a bk2 file.
  13. -- The bk2 file that they are copied to can then be used to create a new tasproj file,
  14. -- and the inputs from that tasproj file can be copied directly to the tasproj file where your movie is.
  15. -- This way, you don't have to manually enter in the button presses for each frame.
  16.  
  17. -- IMPORTANT NOTE: Save state 1 must be set by the user to store where the script should start testing from BEFORE
  18. -- this script is run for the first time (otherwise, an exception will occur)
  19. -- Save state 5 is updated after every frame that a new object is created (save state 5 is also what's loaded when an attempt fails)
  20.  
  21.  
  22. -- initializing RNG for random button presses
  23. math.randomseed( os.time() )
  24.  
  25. -- to change the name of the output file, change the name "resultsOfRedFaceScriptFile.txt" on the line below
  26. -- to whatever you would like the output file to be called.
  27. file = io.open("resultsOfRedFaceScriptFile.txt", "w")
  28. io.output(file)
  29. io.write("Starting up battletoads lag script: ")
  30. io.flush()
  31.  
  32. NeedToLoad = false
  33. NeedToSave = false
  34. earlierBetterSave = 0
  35. needToExtraSave = false
  36.  
  37. -- attemptsInRow counts the number of times that a state has been loaded in a row without a new state being saved
  38. attemptsInRow = 0
  39.  
  40.  
  41. -- buttonsPressed is an array which stores the button presses on player 1's controller for 200 frames.
  42. buttonsPressed = {}
  43.  
  44. for i = 0, 200 do
  45.     buttonsPressed[i] = {A = false, B = false, Select = false, Start = false, Right = false, Left = false, Up = false, Down = false}
  46. end
  47.  
  48.  
  49. numTrials = 0
  50. currentFrame = 0
  51. frameOfLastSave = 0
  52. numObjectsLoaded = 0
  53. savestate.loadslot(1)
  54. savestate.saveslot(5)
  55. currentNumLagBeforeSave = 0
  56. currentNumLagAfterSave = 0
  57. currentBestLag = 999
  58.  
  59.  
  60. -- the following 15 functions are called by event.onmemorywrite() whenever a new value is written to one of the 15 object ID slots (which go from 0X3C1 to 0X3CF)
  61. -- each function checks to see if a non-zero value was written to the ID section. If the value written was non-zero, then the function goBackwardsOrForwards()
  62. -- is called. Otherwise, if zero was written, that means the object was deleted, in which case goBackwardsOrForwards() is not called
  63.  
  64. -- function called whenever 0X3C1 is written to
  65. function firstFunc()
  66.     if memory.readbyte(0X3C1) ~= 0 then
  67.         goBackwardsOrForwards()
  68.     end
  69. end
  70.  
  71.  
  72. -- function called whenever 0X3C2 is written to
  73. function secondFunc()
  74.     if memory.readbyte(0X3C2) ~= 0 then
  75.         goBackwardsOrForwards()
  76.     end
  77. end
  78.  
  79. -- function called whenever 0X3C3 is written to
  80. function thirdFunc()
  81.     if memory.readbyte(0X3C3) ~= 0 then
  82.         goBackwardsOrForwards()
  83.     end
  84. end
  85.  
  86. -- function called whenever 0X3C4 is written to
  87. function fourthFunc()
  88.     if memory.readbyte(0X3C4) ~= 0 then
  89.         goBackwardsOrForwards()
  90.     end
  91. end
  92.  
  93. -- function called whenever 0X3C5 is written to
  94. function fifthFunc()
  95.     if memory.readbyte(0X3C5) ~= 0 then
  96.         goBackwardsOrForwards()
  97.     end
  98. end
  99.  
  100. -- function called whenever 0X3C6 is written to
  101. function sixthFunc()
  102.     if memory.readbyte(0X3C6) ~= 0 then
  103.         goBackwardsOrForwards()
  104.     end
  105. end
  106.  
  107. -- function called whenever 0X3C7 is written to
  108. function seventhFunc()
  109.     if memory.readbyte(0X3C7) ~= 0 then
  110.         goBackwardsOrForwards()
  111.     end
  112. end
  113.  
  114. -- function called whenever 0X3C8 is written to
  115. function eighthFunc()
  116.     if memory.readbyte(0X3C8) ~= 0 then
  117.         goBackwardsOrForwards()
  118.     end
  119. end
  120.  
  121. -- function called whenever 0X3C9 is written to
  122. function ninthFunc()
  123.     if memory.readbyte(0X3C9) ~= 0 then
  124.         goBackwardsOrForwards()
  125.     end
  126. end
  127.  
  128.  
  129. -- function called whenever 0X3CA is written to
  130. function tenthFunc()
  131.     if memory.readbyte(0X3CA) ~= 0 then
  132.         goBackwardsOrForwards()
  133.     end
  134. end
  135.  
  136. -- function called whenever 0X3CB is written to
  137. function eleventhFunc()
  138.     if memory.readbyte(0X3CB) ~= 0 then
  139.         goBackwardsOrForwards()
  140.     end
  141. end
  142.  
  143. -- function called whenever 0X3CC is written to
  144. function twelthFunc()
  145.     if memory.readbyte(0X3CC) ~= 0 then
  146.         goBackwardsOrForwards()
  147.     end
  148. end
  149.  
  150. -- function called whenever 0X3CD is written to
  151. function thirteenthFunc()
  152.     if memory.readbyte(0X3CD) ~= 0 then
  153.         goBackwardsOrForwards()
  154.     end
  155. end
  156.  
  157. -- function called whenever 0X3CE is written to
  158. function fourteenthFunc()
  159.     if memory.readbyte(0X3CE) ~= 0 then
  160.         goBackwardsOrForwards()
  161.     end
  162. end
  163.  
  164. -- function called whenever 0X3CF is written to
  165. function fifteenthFunc()
  166.     if memory.readbyte(0X3CF) ~= 0 then
  167.         goBackwardsOrForwards()
  168.     end
  169. end
  170.  
  171.  
  172. -- This function is called whenever a new object is created. The function checks to see if the value of RNG_2 & 7 is 7.
  173. -- If RNG_2 & 7 is 7, then the function sets needToSave to true to signal that a savestate should be written into slot 5 at the end of this frame,
  174. -- adds the lag that occured since the last save state was made to the running total, increases the number of loaded objects by 1 and effectively "moves forwards".
  175. -- If RNG_2 & 7 wasn't 7, then needToLoad is set to true to signal that savestate 5 should be re-loaded at the end of this frame,
  176. -- the counter for lag that occured since the last save state is set to 0,
  177. -- and the value for current frame is set to the frame number of the last save (in effect, "going backwards")
  178.  
  179. -- Note: if there were more than 8 frames b/w two objects loading and RNG_2 & 7 wasn't 7, then needToExtraSave will be set to true,
  180. -- earlierBetterSave will be set to currentFrame - 8 and save state 5 will be reloaded.
  181. -- When current frame equals earlierBetterSave for the next time, then a new save state will be written to slot 5.
  182. -- This cuts down on how long the script runs, since, for example, if there were 100 frames b/w two objects loading and it took 30 attempts
  183. -- to get RNG_2 to have the right value, then this would require running through 3,000 frames to get the right value,
  184. -- while if a new save state was made on the 92nd frame on the second try, then only 416 frames would need to be run through to get the right value.
  185.  
  186. -- Other Note: If an object loads on the same frame that the last save state was made on and RNG_2 & 7 wasn't 7,
  187. -- then there is no way to manipulate RNG_2 to have the right value, since there are 0 frames of input to work with.
  188. -- As such, this attempt is considered a failure, all values for lag and frame numbers are reset,
  189. -- save state 1 is reloaded, and a new trial begins right after this.
  190.  
  191. function goBackwardsOrForwards()
  192.  
  193.     RNG_2_Byte = memory.readbyte(0X26)
  194.  
  195.     -- if RNG_2_Byte & 7 wasn't equal to 7, then we need to either load an earlier save state or set isFailure to true if the attempt failed.
  196.     if bit.band(RNG_2_Byte, 7) ~= 7 then
  197.  
  198.         --if more than 8 frames have passed since the last save and RNG_2 didn't have a correct value on this frame,
  199.         --then we set an extra save to occur on currentFrame - 8
  200.         if currentFrame - frameOfLastSave > 8 then
  201.             needToExtraSave = true
  202.             earlierBetterSave = currentFrame - 8
  203.         end
  204.  
  205.         -- if the current frame is the same frame that the last save state was made on, then an error occured,
  206.         -- and we set isFailure to true and move on to the next trial
  207.         if frameOfLastSave + 1 >= currentFrame then
  208.             isFailure = true
  209.             io.write("Attempt failed!\n")
  210.             io.flush()
  211.         end
  212.  
  213.         -- resetting the lag counter for the section after the last save state, and setting current frame back to the frame of the earlier save
  214.         currentNumLagAfterSave = 0
  215.         currentFrame = frameOfLastSave
  216.         attemptsInRow = attemptsInRow + 1
  217.  
  218.         -- if 5000 or more attempts to get a favorable value of RNG_2 have happened in a row since the last save state was saved to,
  219.         -- then the attempt is considered to have failed, and we move on to the next trial.
  220.         if attemptsInRow >= 5000 then
  221.             isFailure = true
  222.             io.write(" Attempt failed!\n")
  223.             io.flush()
  224.         end
  225.  
  226.         -- signals to load the savestate in slot 5 when the emu.frameadvance() function finishes
  227.         needToLoad = true
  228.  
  229.  
  230.     -- this branch is reached when RNG_2 & 7 IS equal to 7, which is a success.
  231.     -- in this case, we add the lag since the last save state to the running lag total, set the lag after save state counter to 0,
  232.     -- set the frame number that the last save occured on to the value of the current frame, increase the count of the number of objects loaded by 1,
  233.     -- and set needToSave to true to signal that we need to save state to slot 5 when the emu.frameadvance() function finishes
  234.     else
  235.         currentNumLagBeforeSave = currentNumLagBeforeSave + currentNumLagAfterSave
  236.         attemptsInRow = 0
  237.         currentNumLagAfterSave = 0
  238.         frameOfLastSave = currentFrame
  239.         needToSave = true
  240.         numObjectsLoaded = numObjectsLoaded + 1
  241.  
  242.     end
  243. end
  244.  
  245.  
  246. -- setting the 15 functions described above to be called whenever the memory addresses 0X3C1 to 0X3CF are written to.
  247. event.onmemorywrite(firstFunc, 0X3C1)
  248. event.onmemorywrite(secondFunc, 0X3C2)
  249. event.onmemorywrite(thirdFunc, 0X3C3)
  250. event.onmemorywrite(fourthFunc, 0X3C4)
  251. event.onmemorywrite(fifthFunc, 0X3C5)
  252. event.onmemorywrite(sixthFunc, 0X3C6)
  253. event.onmemorywrite(seventhFunc, 0X3C7)
  254. event.onmemorywrite(eighthFunc, 0X3C8)
  255. event.onmemorywrite(ninthFunc, 0X3C9)
  256. event.onmemorywrite(tenthFunc, 0X3CA)
  257. event.onmemorywrite(eleventhFunc, 0X3CB)
  258. event.onmemorywrite(twelthFunc, 0X3CC)
  259. event.onmemorywrite(thirteenthFunc, 0X3CD)
  260. event.onmemorywrite(fourteenthFunc, 0X3CE)
  261. event.onmemorywrite(fifteenthFunc, 0X3CF)
  262.  
  263.  
  264.  
  265. -- the "main function" for the program
  266. while numTrials < 2500 do
  267.  
  268.     needToLoad = false
  269.     needToSave = false
  270.     needToExtraSave = false
  271.     currentFrame = 0
  272.     frameOfLastSave = 0
  273.     currentNumLagBeforeSave = 0
  274.     currentNumLagAfterSave = 0
  275.  
  276.     savestate.loadslot(1)
  277.     savestate.saveslot(5)
  278.     io.write("Try: ", numTrials, "\n")
  279.     io.flush()
  280.     isFailure = false
  281.     attemptsInRow = 0
  282.     numObjectsLoaded = 0
  283.  
  284.     -- the process below repeats while less than 10 objects have loaded, the current attempt wasn't a failure,
  285.     -- the number of lag frames that have occured up to the last save state is less than the current lowest number of total lag frames for a trial,
  286.     -- and the number of frames advanced from the starting frame is less than 200
  287.     while numObjectsLoaded < 10 and isFailure == false and currentNumLagBeforeSave < currentBestLag and currentFrame < 200 do
  288.  
  289.  
  290.         -- randomly deciding what buttons to press on player 1's controller for this frame
  291.         -- the A, B, Left, Right, Up, and Select buttons each have a random 50-50 chance of being set to true or false for player 1's controller.
  292.         -- Down and Start on player 1's controller are always set to false (not pressed), and no buttons on player 2's controller are pressed
  293.         if math.random(0, 1) == 0 and needToExtraSave == false then
  294.             buttonsPressed[currentFrame]["A"] = true
  295.         else
  296.             buttonsPressed[currentFrame]["A"] = false
  297.         end
  298.  
  299.         -- button B isn't pressed if it has been 10 frames or less since the starting frame,
  300.         -- since this sometimes causes the toad to punch left and miss the pole
  301.         if math.random(0, 1) == 0 and needToExtraSave == false and currentFrame >= 10 then
  302.             buttonsPressed[currentFrame]["B"] = true
  303.         else
  304.             buttonsPressed[currentFrame]["B"] = false
  305.         end
  306.  
  307.  
  308.         if math.random(0, 1) == 0 and needToExtraSave == false then
  309.             buttonsPressed[currentFrame]["Left"] = true
  310.         else
  311.             buttonsPressed[currentFrame]["Left"] = false
  312.         end
  313.  
  314.  
  315.         if math.random(0, 1) == 0 and needToExtraSave == false then
  316.             buttonsPressed[currentFrame]["Right"] = true
  317.         else
  318.             buttonsPressed[currentFrame]["Right"] = false
  319.         end
  320.  
  321.  
  322.         if math.random(0, 1) == 0 and needToExtraSave == false then
  323.             buttonsPressed[currentFrame]["Up"] = true
  324.         else
  325.             buttonsPressed[currentFrame]["Up"] = false
  326.         end
  327.  
  328.  
  329.         if math.random(0, 1) == 0 and needToExtraSave == false then
  330.             buttonsPressed[currentFrame]["Select"] = true
  331.         else
  332.             buttonsPressed[currentFrame]["Select"] = false
  333.         end
  334.  
  335.  
  336.         joypad.set(buttonsPressed[currentFrame], 1)
  337.  
  338.         -- if needToExtraSave was set to true and the current frame equals the frame that the save is supposed to happen on,
  339.         -- then we save to slot 5, add the number of lag frames that happened since the last save to the running total,
  340.         -- set the number of lag frames after the last save to 0, set the frame of the last save to be the current frame,
  341.         -- and set needToExtraSave to false
  342.         if needToExtraSave and currentFrame == earlierBetterSave then
  343.             savestate.saveslot(5)
  344.             currentNumLagBeforeSave = currentNumLagBeforeSave + currentNumLagAfterSave
  345.             currentNumLagAfterSave = 0
  346.             frameOfLastSave = currentFrame
  347.             needToExtraSave = false
  348.         end
  349.  
  350.         currentFrame = currentFrame + 1
  351.         --advancing forwards 1 frame (if goBackwardsOrForwards() is going to be called,
  352.         --it will happen in the middle of the emu.frameadvance() function executing)
  353.         emu.frameadvance()
  354.    
  355.         --if a lag frame occured, then we increase the count for the number of lag frames that occured since the last save state.
  356.         if emu.islagged() then
  357.             currentNumLagAfterSave = currentNumLagAfterSave + 1
  358.         end
  359.  
  360.  
  361.         --if needToLoad is true, then we load the savestate in slot 5, and set needToLoad to false
  362.         if needToLoad == true then
  363.             currentNumLagAfterSave = 0
  364.             savestate.loadslot(5)
  365.             needToLoad = false
  366.         end
  367.  
  368.  
  369.         --if needToSave is true, then we savestate to slot 5, and set needToSave to false
  370.         if needToSave == true then
  371.             currentNumLagBeforeSave = currentNumLagBeforeSave + currentNumLagAfterSave
  372.             currentNumLagAfterSave = 0
  373.             savestate.saveslot(5)
  374.             needToSave = false
  375.         end
  376.     end
  377.  
  378.     --if the last try wasn't a failure, then we print out how many lag frames occured
  379.     if isFailure == false then
  380.         io.write(" (lag of ", currentNumLagBeforeSave + currentNumLagAfterSave, ")\n")
  381.         io.flush()
  382.     end
  383.  
  384.  
  385.     numTrials = numTrials + 1
  386.  
  387.  
  388.     --if we had a new best for least number of lag frames, then we write out all 200 frames of player 1's input to the output file in a format
  389.     --that will let it be copy and pasted directly into the "Input Log.txt" file of a 2 player bk2 file
  390.     if isFailure == false and currentNumLagBeforeSave + currentNumLagAfterSave < currentBestLag then
  391.         io.write("New best of ", currentNumLagBeforeSave + currentNumLagAfterSave, " frames of lag!\n\n")
  392.         currentBestLag = currentNumLagBeforeSave + currentNumLagAfterSave
  393.  
  394.  
  395.         for tempCount = 0, 200 do
  396.    
  397.             io.write("|..|")
  398.    
  399.  
  400.             if buttonsPressed[tempCount]["Up"] == true then
  401.                 io.write("U.")
  402.             else
  403.                 io.write("..")
  404.             end
  405.  
  406.  
  407.             if buttonsPressed[tempCount]["Left"] == true then
  408.                 io.write("L")
  409.             else
  410.                 io.write(".")
  411.             end
  412.  
  413.  
  414.             if buttonsPressed[tempCount]["Right"] == true then
  415.                 io.write("R.")
  416.             else
  417.                 io.write("..")
  418.             end
  419.  
  420.  
  421.             if buttonsPressed[tempCount]["Select"] == true then
  422.                 io.write("s")
  423.             else
  424.                 io.write(".")
  425.             end
  426.  
  427.  
  428.             if buttonsPressed[tempCount]["B"] == true then
  429.                 io.write("B")
  430.             else
  431.                 io.write(".")
  432.             end
  433.  
  434.  
  435.  
  436.             if buttonsPressed[tempCount]["A"] == true then
  437.                 io.write("A|........|\n")
  438.             else
  439.                 io.write(".|........|\n")
  440.             end
  441.  
  442.         end
  443.  
  444.         io.write("\nEnd of sequence for ", currentNumLagBeforeSave + currentNumLagAfterSave, " frames of lag input\n")
  445.         io.flush()
  446.  
  447.     end
  448.  
  449. end
RAW Paste Data