Experienced Forum User, Skilled player (1589)
Joined: 9/17/2009
Posts: 4883
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
GBA TASer of 2010
I grew up with this game, and found out apparently there's a credits that's unlocked after 170 stars. Getting 1,000 points (or 5 stars) per game take quite a while from making a test run: 02:37.73 - Donkey Kong Jr, Classic, Easy 02:51.38 - Donkey Kong Jr, Classic, Hard 07:00.37 - Donkey Kong Jr, Modern, Easy 13:20.98 - Fire, Modern, Hard 14:03.83 - Boxing, Classic, Easy 14:33.27 - Fire, Classic, Easy 15:15.95 - Fire, Classic, Hard 17:18.78 - Rainshower, Modern, Hard 17:48.52 - Fire, Modern, Easy 20:34.85 - Rainshower, Classic, Easy 20:49.52 - Rainshower, Classic, Hard 21:48.60 - Rainshower, Modern, Easy Mario's Cement Factory, Classic: 25:32.17 - Mario's Cement Factory, Classic, Hard (pause menu to manip cement) 31:36.48 - Mario's Cement Factory, Classic, Easy (pause menu to manip cement) 33:26.23 - Mario's Cement Factory, Classic, Hard (no pause menu) 41:38.57 - Mario's Cement Factory, Classic, Easy (no pause menu) From unlockables Stars - Name 5 - Chef 20 - Mario Bros. 35 - Donkey Kong 50 - Octopus 65 - Fire Attack Times: 14:15.55 - Chef, Classic, Hard 14:31.88 - Chef, Classic, Easy 10:19.82 - Chef, Modern, Easy 10:12.85 - Chef, Modern, Hard 16:47.07 - Mario Bros., Classic, Easy 13:46.77 - Mario Bros., Classic, Hard 16:29.33 - Mario Bros., Modern, Easy 12:15.23 - Mario Bros., Modern, Hard 05:44.60 - Donkey Kong, Classic, Easy 06:38.57 - Donkey Kong, Classic, Hard 05:49.98 - Octopus, Classic, Easy 05:49.98 - Octopus, Classic, Hard 08:15.55 - Octopus, Modern, Easy 08:38.45 - Octopus, Modern, Hard 12:21.70 - Fire Attack, Classic, Easy 09:18.78 - Fire Attack, Classic, Hard 14:37.90 - Fire Attack, Modern, Easy 13:04.70 - Fire Attack, Modern, Hard All of them are very quick, so that's "easy" 20 stars/game = 100 1 hour just to obtain 20 stars so far. I'm not sure how fast other games are, nor have I finished any modern version TASes since they seem more RNG based; example being the moon that gives 5 points on the "Fire" game managed to evade me for ~600 points while I was trying to manipulate it to stop yoshi egg's from spawning bombs by pausing. Boxing on Classic also required some luck, since it saves 2 seconds every round for the first 10 rounds to get knocked back before pummeling the NPC. The NPC tends to delay before punching though. I get the feeling it'll take 8.5 hours or so to see the credits, so there's that. Edit: Some Classic versions reward double points after a certain threshold if you didn't die: * Rainshower: 300 points * Mario's Cement Factory 300 points * Donkey Kong Jr. 300 points * Mario Brothers 300 points * Donkey Kong 300 points * Fire Attack Github for the scripts: https://github.com/lunjesse/GaW4-bot
Experienced Forum User, Skilled player (1589)
Joined: 9/17/2009
Posts: 4883
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
GBA TASer of 2010
Some notes: Boxing's NPC initially starts with 2 HP, and gains 1 after every 3 rounds. If said NPC was in the middle of the ring while at 0 HP, he gets knocked to the side in 1 hit. That is ~2 seconds slower than having him knock you back once, then get beaten up until the end. After 10 rounds this is not needed anymore since he gets to the side with 0 HP at the end. Chef and Fire are the ones that can be autofired for the entire time in Classic. A bot is still preferably than a very large input file when testing however. Fire can have up to 7 stickmen hopping at one time. It doesn't appear any significantly fast to obtain n1,000 points. Fire (Modern) has NPCs that spawn depends on points. For example, freezing my score to 18 spawns an egg every time, but freezing it and changing to 18 doesn't. Also gives a debug screen if score set to "270F". Rainshower takes 20:49.52 to get to 1,000 points yet after 06:17.52 it reaches 2,000 points. Reach 1,300 points triggers the "x2" points jingle, but doesn't increase the multiplier to x4. Also the sunny days from every 100 points never appear after 1,000 points.
Editor, Experienced Forum User, Publisher, Skilled player (1228)
Joined: 10/12/2011
Posts: 6014
Location: The land down under.
PSX TASer of 2016
I raise your lua script with a watch file. This is for you to to have the lua as an all in one file and also being able to detect which game and mode is set. Games
    0 Fire 1 Octopus 4 Rain Shower 5 Chef 6 Donkey Kong 7 Mario's Cement Factory 8 Boxing 9 Donkey Kong Jr. 10 Mario Bros. 11 Fire Attack 12 Donkey Kong 3
Museum
    13 Manhole 14 Parachute 15 Zelda 16 Climber 17 Tropical Fish 18 Life Boat 19 Mario's Bombs Away 20 Safe Buster 21 Bomb Sweeper
WebNations/Sabih wrote:
+fsvgm777 never censoring anything.
Disables Comments and Ratings for the YouTube account. These colours are pretty neato, and also these.
Experienced Forum User, Skilled player (1589)
Joined: 9/17/2009
Posts: 4883
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
GBA TASer of 2010
Hey thanks for the attempt. It doesn't take account into Fire for some reason, and the latter one seems to only work for menus (it shows 0 regardless for in a game). 3A04C appears to be the icon for modern mode, and 414A8 seems to be the difficulty that applies for both versions and seemingly all games. Thanks for your idea for an AIO script btw. :) Edit: Oops, seems like it does.
Fortranm
He/Him
Editor, Experienced Forum User, Experienced player (523)
Joined: 10/19/2013
Posts: 1060
Do the first three GWG games have any sort of credits?
Experienced Forum User, Skilled player (1589)
Joined: 9/17/2009
Posts: 4883
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
GBA TASer of 2010
Fortranm wrote:
Do the first three GWG games have any sort of credits?
Credits appear at 170 stars. You get 5 stars at 1,000 points in most of the games. According to gamefaqs, 220 stars is 100%. 20 stars are inaccessible in single player, which means I can have 6 games modes skippable. Unfortunately I do not know which is the slowest, so I'm making test runs of all of them to check and skip the 6 slowest. Oh, and if anyone wonders if this is too "trivial", that's mostly the case for the Classic games, but the Modern counterparts have more luck involved (such as moon spawns on Fire). Edit: Games that I can't seem to bot easily: *Boxing (Classic) *DK Jr *DK *DK 3 Not sure how to approach Cement Factory. :| Edit2: Cement factory notes: 0x03C184 - 0x03C1B0 are cements; 4 bytes long and up to 12 at a time. 0x0 offset seems to be "X"; 5 for left machines and 7 for right. 0 means gone 0x1 no idea 0x2 is "Y" 0x3 is destination Lifts are 0x03C14C- 0x03C160; also 4 bytes long for up to 6 lifts. 0x0 determines whether you're on or off it 0x2 determines the position as follows: Your position is however 0x03C13B and determined like this: 0x03C12E is the lift timer with 0x03C124 determining it's countdown length. Every loop it changes 0x03C12F from 0 to 1 and vice versa, 0 moves the right elevator and 1 moves the left. 0x03C130 is the conveyor belt. 0x03C137 is the amount of cement for the upper left machine 0x03C138 is the amount of cement for the lower left machine 0x03C139 and 0x03C13A is the same but for the upper/lower right respectively.
Experienced Forum User, Skilled player (1589)
Joined: 9/17/2009
Posts: 4883
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
GBA TASer of 2010
Hey, managed to get 200 points in classic cement factory hard mode after over 45,000 frames. This makes it one of the slowest game modes, and most likely be skipped along with the easy version assuming 6 other games arent slower. http://tasvideos.org/userfiles/info/24247709336349349 Here's a rough bot that attempts the game; it's posted here in case anyone in the future wants to do a 100%. Edit: I'm still really curious about the final times, so I might try 1000 points later. Edit2: I used cheats to quickly test Octopus. It seems on modern, the max amount of treasures you can carry at one time is 50. I can't seem to use pause to manipulate the tentacles, but I can use that to affect when it will shoot ink. Edit3: It's way slower to hand in 50 points all at once; it's faster to hand in chunks of 9 points (10+ slows you down a lot). 0x03C1D2 is Mario's "timer" for movement; once it hits 0 you can move again. 0x03C1F2 is the points stored on bag. It affects the above as follows: 10+ - advances 0.5 unit per frame 7-9 - advances 1 unit per frame 4-6 - advances 2 units per frame 0-3 - advances 4 units per frame Each tentacle has it's own addresses; however for pretty much the entire time, only the one closest to the treasure is of concern. 0x03C6C0 is the timer for when it strikes, and 0x03C6B2 is the cooldown which determines how long before the next attack. The cooldown timer is one of the reasons having 10+ loot is slower; by 150 points or so, the only counts up to ~100, and given your slow movement, you can't make it back in time to do anything. Even worse at 800+ points when it counts to 16 frames and strikes. Edit4: Classic Octopus is completely different in scoring. Every time you get 1 loot and go back, 0x03A446 adds by 3. This slowly counts down, and 1 point is added to 0x03A3C8. Unfortunately, this is at a fixed rate, so unless I mess around a bit, chances are, there will be 3+ minutes of down time for BOTH classic octopus difficulties. 0x03A87D is the tentacle timer, and 0x03AECC is the speed which it ticks. Here's a lua script that displays information regarding the classic version:
Language: lua

memory.usememorydomain("Combined WRAM") posX = {[0]=30,[1]=25,[2]=75,[3]=120,[4]=160} posY = {[0]=45,[1]=105,[2]=110,[3]=110,[4]=110} full = {[0]=3,[1]=4,[2]=5,[3]=4,[4]=3} --full "length" of tentacles while true do gui.drawText(175,0,"+"..memory.read_u16_le(0x03A446),'BLACK') --Score saved up gui.drawText(130,15,"Total:"..memory.read_u16_le(0x03A3C8)+memory.read_u16_le(0x03A446),'Black') -- and total gui.drawText(0,145,"Timer:"..memory.readbyte(0x03A87D).."/"..(memory.readbyte(0x03AECC)+2),'Black') --Speed appears -2 than timer limit for i = 0x03AACA, 0x03ACBA, 0x7C do --First tentacle and offsets index = (i-0x03AACA)/0x7C --to convert i into natural numbers --offset 0xA is state; 0 = DNE; 2 = Attack; 6 = Retreat if memory.readbyte(i+0xA) == 2 then gui.drawText(posX[index],posY[index],memory.readbyte(i).."/"..full[index].."\n"..memory.readbyte(i+0xA),'Maroon') elseif memory.readbyte(i+0xA) == 6 and memory.readbyte(i) == full[index] then gui.drawText(posX[index],posY[index],memory.readbyte(i).."/"..full[index].."\n"..memory.readbyte(i+0xA),'Red') elseif (memory.readbyte(i+0xA) == 6 and memory.readbyte(i) ~= full[index]) then gui.drawText(posX[index],posY[index],memory.readbyte(i).."/"..full[index].."\n"..memory.readbyte(i+0xA),'Orange') elseif memory.readbyte(i+0xA) == 0 then gui.drawText(posX[index],posY[index],memory.readbyte(i).."/"..full[index].."\n"..memory.readbyte(i+0xA),'Green') end end emu.frameadvance() end
Experienced Forum User, Skilled player (1589)
Joined: 9/17/2009
Posts: 4883
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
GBA TASer of 2010
Rainshower (Modern) Easy mode took me 21:48.60 to get 1000 points. Hard mode took me 17:18.78. 0x3DDEC is Mario's spot; there are 4 possibilities Balloons are like this: Colors - X values occupied (left, right) green - 87, 155 red - 71, 176 black - 50, 202 blue - 29, 220 Seems to start at 0x03C7E4 with offset for each balloon being 0x7c at balloon+0x54, its the "state" in binary; in an attempt to describe it better, I'll use lua:
Language: lua

local i = 0x03C7E4 if bit.check(memory.readbyte(i+0x54),0) then gui.drawText(0,10, "???") end if bit.check(memory.readbyte(i+0x54),1) then gui.drawText(0,10, "Hit an NPC") end if bit.check(memory.readbyte(i+0x54),2) then gui.drawText(0,10, "1st set of ropes passed") end if bit.check(memory.readbyte(i+0x54),3) then gui.drawText(0,10, "2nd set of ropes passed") end if bit.check(memory.readbyte(i+0x54),4) then gui.drawText(0,10, "???") end if bit.check(memory.readbyte(i+0x54),5) then gui.drawText(0,10, "Hit the ground") end if bit.check(memory.readbyte(i+0x54),6) then gui.drawText(0,10, "Not a balloon; its a moon. Oddly never used") end if bit.check(memory.readbyte(i+0x54),7) then gui.drawText(0,10, "About to hit an NPC") end
No idea what address determine's it's color yet. Offset 0x1B seems to be it's speed; sadly it only increases after like 600 or so points scored. Balloon sprite seems to be at offset 0x11. Green - 0x3B,0x3C Red - 0x2B,0x2C Black - 0x33,0x34 Blue - 0x1E, 0x1F Moon - 0x19 There's a speed address for each balloon at offset 0x1A. It's a 2 byte unsigned value that starts at 80, 112, and 320 for Easy. Hard and Very Hard respectively. No idea why the numbers are that large though. 320 seems the highest; didn't managed to get a speed over that even on Very Hard. Address 0x03E456 affects the speed itself; it starts at 5,7 and 20 for Easy. Hard and Very Hard respectively and corresponds to the above values by a factor of 16. Anyways, here's a script to display all the balloons at once:
Language: lua

memory.usememorydomain("Combined WRAM") local balloon = 0x03C7E4 while true do local p = 10 for i = balloon, 0x03D85C, 0x7c do --First Balloon's X pos; offset of others is 7C; enough for 35 balloons if memory.read_u16_le(i+0x1A) == 0 then else if memory.readbyte(i+0x11) == 0x3B or memory.readbyte(i+0x11) == 0x3C then gui.drawText(memory.readbyte(i)-29,memory.readbyte(i+2)-10, memory.read_u16_le(i+0x1A),'LawnGreen') gui.drawText(memory.readbyte(i)-29,memory.readbyte(i+2), memory.readbyte(i)..","..memory.readbyte(i+2).."("..((i-balloon)/0x7c)..")",'LawnGreen') elseif memory.readbyte(i+0x11) == 0x2B or memory.readbyte(i+0x11) == 0x2C then gui.drawText(memory.readbyte(i)-29,memory.readbyte(i+2)-10, memory.read_u16_le(i+0x1A),'Red') gui.drawText(memory.readbyte(i)-29,memory.readbyte(i+2), memory.readbyte(i)..","..memory.readbyte(i+2).."("..((i-balloon)/0x7c)..")",'Red') elseif memory.readbyte(i+0x11) == 0x33 or memory.readbyte(i+0x11) == 0x34 then gui.drawText(memory.readbyte(i)-29,memory.readbyte(i+2)-10, memory.read_u16_le(i+0x1A),'Black') gui.drawText(memory.readbyte(i)-29,memory.readbyte(i+2), memory.readbyte(i)..","..memory.readbyte(i+2).."("..((i-balloon)/0x7c)..")",'Black') elseif memory.readbyte(i+0x11) == 0x1E or memory.readbyte(i+0x11) == 0x1F then gui.drawText(memory.readbyte(i)-29,memory.readbyte(i+2)-10, memory.read_u16_le(i+0x1A),'Blue') gui.drawText(memory.readbyte(i)-29,memory.readbyte(i+2), memory.readbyte(i)..","..memory.readbyte(i+2).."("..((i-balloon)/0x7c)..")",'Blue') elseif memory.readbyte(i+0x11) == 0x19 then gui.drawText(memory.readbyte(i)-29,memory.readbyte(i+2)-10, memory.read_u16_le(i+0x1A),'Gold') gui.drawText(memory.readbyte(i)-29,memory.readbyte(i+2), memory.readbyte(i)..","..memory.readbyte(i+2).."("..((i-balloon)/0x7c)..")",'Gold') end end end gui.drawText(0,0,"Speed: "..memory.readbyte(0x03E456),'Black') emu.frameadvance() end
Bot done http://tasvideos.org/userfiles/info/27198392647936026 For those who don't care about TASing and only want to get points, have fun with 9,999 points using this. Edit: Fixed some bugs regarding if statements and grabbing the moons. Now also adjusted for 17 objects. Changed to 34 objects. Seriously what the hell Edit2: This bot still fails at the worse case scenario where 3+ balloons are about to hit at 15+ speed and/or with waluigi pulling on one of them. Here's how NPCs join the group along with difficulty:
Easy -	0 points
	Toad
			DK

	50 points
	Toad
			DK,Peach

	350 points
	Toad
	Wario		DK,Peach

	550 points
	Toad		Yoshi
	Wario		DK,Peach

	680 points
	Luigi,Toad	Yoshi
	Wario		DK,Peach


Hard - 	0 points
	Luigi		Yoshi
	Wario

	150 points
	Luigi		Yoshi
	Wario		DK

	280 points
	Luigi,Toad	Yoshi
	Wario		DK

	450 points
	Luigi,Toad	Yoshi
	Wario		DK,Peach


Very Hard
	Luigi,Toad	Yoshi
	Wario		DK,Peach
This is important since grabbing the moon 5 times in a row gives 20 points. For both 280 and 680, if you managed to grab a moon during the x80th point, you'll knock Bowser down, gain 20 points, then immediately have BOTH the 100 point "cutscene" along with Luigi/Toad joining you at the same time. This saves a bit of time.
Experienced Forum User, Skilled player (1589)
Joined: 9/17/2009
Posts: 4883
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
GBA TASer of 2010
So...DK3. I'm tackling the Classic version first, because that's the only version I managed to get 1,000 points in real time by spamming bubbles. In a TAS where the purpose is to obtain 1,000 points, the method appears to be like this: Shoot 3 bubbles at the top, middle then bottom such that the left bee would reach DK before he can shoot it away. During the "downtime" refuel and gain points by shooting the right bee. Repeat this 100 times. I wished I knew how to approach making a script for this, since it's very tedious. In the case anyone can do this, here's the addresses in ERAM: 0x3A5BC - Left Bee's position 0x3A5C4 - Left Bee's position 0x3A5C8 - Left Bee's movement timer 0x3A5F4 - Right Bee's position 0x3A5FC - Right Bee's position 0x3A600 - Right Bee's movement timer 0x3B32C - Mario's position 0x3B334 - DK's position 0x3B358 - DK's bubble movement timer 0x3B359 - DK's bubble amount Bee positions: Edit: You cannot splice the rounds, since the bee timer doesn't match. Given ~1000 frames per match, which gives 5 + 10 points, that's 67,000 frames, or around 20 minutes for 5 stars.
Experienced Forum User, Skilled player (1589)
Joined: 9/17/2009
Posts: 4883
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
GBA TASer of 2010
I believe even the Classic versions have an element of RNG in them. I recorded a movie back then using BizHawk 1.10.0. of Fire (Easy) using the VBA-next core: http://tasvideos.org/userfiles/info/29836974854669639 And I made another now since mgba can sync with savefiles; still in VBA-next core to showcase the RNG: http://tasvideos.org/userfiles/info/29837167840737636 They both sync at 1.11.6 and use VBA-next; and the former is 52776 frames, while the latter is 55901 frames. Clearly, there's some element of randomness going on unless some bizarre glitch was triggered by the script. This means a TAS of this game won't be as easy as I thought. :/ Hey, at least it'll beat all known records (maybe) and be somewhat more different that a real time run.
Experienced Forum User, Skilled player (1589)
Joined: 9/17/2009
Posts: 4883
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
GBA TASer of 2010
Download Chef modern.lua
Language: lua

memory.usememorydomain("Combined WRAM") food_address = {[1] = 0x03C748, [2] = 0x03C75C, [3] = 0x03C770, [4] = 0x03C784} x_yoshi = 0x03C358 --[[ Peach 03C351 Peach direction 03C352 0 is left, 1 is right If food is at col 0, peach should be at col 0 and 0 direction If food is at col 1, peach should be at col 0 and 1 direction If food is at col 1, peach should be at col 1 and 0 direction If food is at col 2, peach should be at col 1 and 1 direction If food is at col 2, peach should be at col 2 and 0 direction If food is at col 3, peach should be at col 2 and 1 direction If food is at col 3, peach should be at col 3 and 0 direction If food is at col 4, peach should be at col 3 and 1 direction Yoshi should be at whatever col food is if its the target ]]-- local function move(direction) if direction == "right" then joypad.set({Right = true}) emu.frameadvance() emu.frameadvance() emu.frameadvance() elseif direction == "left" then joypad.set({Left = true}) emu.frameadvance() emu.frameadvance() emu.frameadvance() end end local function travel(destination) local peach = memory.readbyte(0x03C351) local direction = memory.readbyte(0x03C352) local left = {[0]=true,[2]=true,[8]=true,[12]=true} local right = {[1]=true,[3]=true,[9]=true,[13]=true} if destination == 0 then --food is far left side if peach > 0 then repeat move("left") until memory.readbyte(0x03C351) == 0 --otherwise "peach" never updates elseif peach == 0 then if left[direction] == nil then joypad.set({A = true}) emu.frameadvance() end end elseif destination == 4 then --food is far right side if peach <3> 1 then repeat move("left") until memory.readbyte(0x03C351) == 1 --otherwise "peach" never updates end elseif destination == 2 then --food is 2 steps to the right if peach > 2 then repeat move("left") until memory.readbyte(0x03C351) == 2 --otherwise "peach" never updates end if peach < 1 then repeat move("right") until memory.readbyte(0x03C351) == 1 --otherwise "peach" never updates end if peach == 1 then if right[direction] == nil then joypad.set({A = true}) emu.frameadvance() end end if peach == 2 then if left[direction] == nil then joypad.set({A = true}) emu.frameadvance() end end elseif destination == 3 then --food is 3 steps to the right if peach <2> 0 then repeat move("left") until memory.readbyte(0x03C351) == 0 --otherwise "peach" never updates elseif peach == 0 then if right[direction] == nil then joypad.set({A = true}) emu.frameadvance() end end elseif destination == 4 then --food is far right side if peach <3> 1 then repeat move("left") until memory.readbyte(0x03C351) == 1 --otherwise "peach" never updates end elseif destination == 2 then --food is 2 steps to the right if peach > 2 then repeat move("left") until memory.readbyte(0x03C351) == 2 --otherwise "peach" never updates end if peach < 1 then repeat move("right") until memory.readbyte(0x03C351) == 1 --otherwise "peach" never updates end if peach == 1 then if left[direction] == nil then joypad.set({A = true}) emu.frameadvance() end end if peach == 2 then if right[direction] == nil then joypad.set({A = true}) emu.frameadvance() end end elseif destination == 3 then --food is 3 steps to the right if peach <2> 0 and food[nearest_i].state == 1 then travel2(nearest_x) end gui.drawText(0,85,nearest_i..": Time:"..nearest_y.." X:"..nearest_x) gui.drawText(0,100,"Yoshi:"..memory.readbyte(x_yoshi).."Food:"..memory.readbyte(0x03C35B)) gui.drawText(0,115,"Peach:"..memory.readbyte(0x03C351)) emu.frameadvance() end
My attempt at botting Chef (Modern). In comparison, the classic script looks like this: Download Chef Classic.lua
Language: lua

memory.usememorydomain("EWRAM") local m = {[0]=0x03C62A,[1]=0x03C6A6,[2]=0x03C722,[3]=0x03C79E} while true do for i = 0, 3 do if (memory.readbyte(m[i]) == 0) then local p = memory.readbyte(0x03C1D8) if (i == 0) and (p == 1) then joypad.set({Left = 1}) elseif (i == 0) and (p == 2) then joypad.set({B = 1}) elseif (i == 0) and (p == 3) then joypad.set({Left = 1}) elseif (i == 1) and (p == 0) then joypad.set({Right = 1}) elseif (i == 1) and (p == 2) then joypad.set({B = 1}) elseif (i == 1) and (p == 3) then joypad.set({Left = 1}) elseif (i == 2) and (p == 0) then joypad.set({Right = 1}) elseif (i == 2) and (p == 1) then joypad.set({A = 1}) elseif (i == 2) and (p == 3) then joypad.set({Left = 1}) elseif (i == 3) and (p == 0) then joypad.set({Right = 1}) elseif (i == 3) and (p == 1) then joypad.set({A = 1}) elseif (i == 3) and (p == 2) then joypad.set({Right = 1}) end end end emu.frameadvance() end
Experienced Forum User, Skilled player (1589)
Joined: 9/17/2009
Posts: 4883
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
GBA TASer of 2010
blocks to move when value right char (03C138) 2 (bottom) 20 (middle) 38 (top) left char (03C139) 11 (bottom) 29 (middle) 47 (top) Format for block address 03C140 - state; 0 == done; 1 == alive; 5 == carried 03C141 - convoyer belt array pos 03C142 - 1 if moving right, 0 if moving left 03C140 block 1 03C144 block 2 03C148 block 3 etc. use 03C12E and a for loop to get all blocks without listing them explicitly 03C12D seems to be tick length; ie. how fast the game is the blocks move alternately, determined by 03C12B Easy mode starts at 2 blocks, and reaches up to 6 blocks by 900 points onward. Hard mode starts at 3 blocks, and reaches up to 6 blocks by 600 points onward. It reaches 7 blocks by 780 points onward. Script for classic version: Download mariobros(classic).lua
Language: lua

memory.usememorydomain("Combined WRAM") --[[ 6 cases: right char 2 (bottom) 20 (middle) 38 (top) left char 11 (bottom) 29 (middle) 47 (top) ]]-- while true do local start_address = 0x03C140 local block_state = 0 local block_pos = 0 local right_char = memory.readbyte(0x03C138) --move with a/b local left_char = memory.readbyte(0x03C139) --move with up/down for i = 0, 7 do block_state = memory.readbyte(start_address+(i*4)) if (block_state == 1) then --it exists, and is not being carried to next section block_pos = memory.readbyte(start_address+(i*4)+1) if (block_pos == 2) then if (right_char > 0) then joypad.set({B = true}) end elseif (block_pos == 20) then if (right_char > 1) then joypad.set({B = true}) else joypad.set({A = true}) end elseif (block_pos == 38) then if (right_char <2> 0) then joypad.set({Down = true}) end elseif (block_pos == 29) then if (left_char > 1) then joypad.set({Down = true}) else joypad.set({Up = true}) end elseif (block_pos == 47) then if (left_char < 2) then joypad.set({Up = true}) end end emu.frameadvance() end end emu.frameadvance() end
Also added a github link to past bots in first post, if anyone wants.
Experienced Forum User, Skilled player (1589)
Joined: 9/17/2009
Posts: 4883
Location: ̶C̶a̶n̶a̶d̶a̶ "Kanatah"
GBA TASer of 2010
Fire Attack: So...I accidentally deleted my notes for Classic, so instead, I'll just post this code, and figure out what I wrote: Download Fire Attack (Classic).lua
Language: lua

memory.usememorydomain("Combined WRAM") function move_fire(dest) local player = memory.readbyte(0x03C130) local hit = memory.readbyte(0x03C132) -- console.log(dest) if (player == dest) then if (hit == 0) then joypad.set({A = true}) return end else if (player == 0 and dest == 1) then joypad.set({Right = true}) elseif (player == 0 and dest == 2) then joypad.set({Down = true}) elseif (player == 0 and dest == 3) then joypad.set({Down = true, Right = true}) elseif (player == 1 and dest == 0) then joypad.set({Left = true}) elseif (player == 1 and dest == 2) then joypad.set({Down = true, Left = true}) elseif (player == 1 and dest == 3) then joypad.set({Down = true}) elseif (player == 2 and dest == 0) then joypad.set({Up = true}) elseif (player == 2 and dest == 1) then joypad.set({Up = true, Right = true}) elseif (player == 2 and dest == 3) then joypad.set({Right = true}) elseif (player == 3 and dest == 0) then joypad.set({Up = true, Left = true}) elseif (player == 3 and dest == 1) then joypad.set({Up = true}) elseif (player == 3 and dest == 2) then joypad.set({Left = true}) end end end while true do local fire_address = 0x03AB18 local fire local pos = {[4] = 0, [10] = 1, [16] = 2, [23] = 3} --positions of fire, and where the player should be at for i = 0, 7 do fire = memory.readbyte(fire_address+(0x7C*i)) --4 cases: top left, top right, bottom left, bottom right gui.text(0,60+15*i,i.." f "..fire) if (pos[fire] ~= nil) then --this means the positions exists move_fire(pos[fire]) end end emu.frameadvance() console.clear() end
Edit: The positions correspond to this: For the Modern version, I can't seem to figure out bomb "states"; ie. when does it become explosive. I did figure out the following tho:
Fire attack modern
Combined WRAM

03AAA4	1 byte	x mirror?
03AAA6	1 byte	y mirror?
03AAAC	1 byte	1 to appear in foreground 2 for background
03AAB4	1 byte	x, but changing the 2nd byte also affects x
03AAB6	1 byte	same as above
03AAC5	1 byte	sprite 
	14	chicken (face left)
	15	chicken (face left)
	16	chicken flattened (face left)
	17	chicken (face right)
	18	chicken (face right)
	19	chicken flattened (face right)
	20	bobomb walking (face left)
	21	bobomb walking (face left)
	22	bobomb flattened (face left)
	23	bobomb walking (face right)
	24	bobomb walking (face right)
	25	bobomb flattened (face right)
	26	bullet bill (face left)
	27	bullet bill (face left)
	28	bullet bill flattened (face left)
	29	bullet bill (face right)
	30	bullet bill (face right)
	31	bullet bill flattened (face right)
	32	apple
	33	apple flattened
	34	explode graphics
	35	explode graphics
	36	explode graphics
	37	explode graphics
	38	heart (left?)
	39	heart (left?)
	40	heart (left?)
	41	heart (left?)
	42	heart (left?)
	43	heart (left?)
	44	heart (left?)
	45	heart (left?)
	46	statue explosion effects
	47	statue explosion effects
	48	statue explosion effects
	49	statue explosion effects
	50	statue explosion effects
	51	statue explosion effects
	52	statue explosion effects
	53	statue explosion effects
	54	statue explosion effects
	55	statue explosion effects
	61	heart flattened (left?)
	63	apple flattened (after 33)
	64	bobomb glowing (face left)
	65	bobomb glowing (face right)
	66	bullet bill glowing (face left)
	67	bullet bill glowing (face right)
	70	bobomb glowing (face left)
	71	bobomb glowing (face right)
	72	bullet bill glowing (face left)
	73	bullet bill glowing (face right)
	74	chicken fleeing (was face right)
	75	chicken fleeing (was face right)
	78	chicken fleeing (was face left)
	79	chicken fleeing (was face left)
	80	chicken lay egg (face left)
	81	chicken lay egg (face left)
	82	chicken lay egg (face left)
	83	chicken lay egg (face left)
	84	chicken lay egg (face right)
	85	chicken lay egg (face right)
	86	chicken lay egg (face right)
	87	chicken lay egg (face right)
03AACC	2 byte	x speed; not sure how it works
03AACE	2 byte	y speed; not sure how it works
03AB02	1 byte	bomb timer
03AB20	1 byte	bomb 2 x mirror

each bomb offset each other by 0x7C

can hit bobomb after y <116>= 60 or x >= 179
bobomb seems to be triggered by its y address
enemy x of > 247 count as left of the screen for some reason

03CF67	wario pos
03D054	wario x mirror?
03D064	wario x
Wario can attack if x is 92, 148 (left), 80, 160 (right)


03CF62	1 byte	game "state"
	0	normal
	1	bomb explodes
	2	obtain heart
03CF67	1 byte 	wario's location
	0	top left
	1	top right
	2	bottom left
	3	bottom right
03CF68	1 byte	hammer hitting boolean (1 means it will hit something if said thing wanders to it)
03D075	1 byte	warior sprite
	0	wario facing right
	1	wario facing right leg out
	2	wario facing left
	3	wario facing left leg out
	4	wario facing right hammer up	
	5	wario facing right hammer down	
	6	wario facing left hammer up	
	7	wario facing left hammer down
	56	wario moving diagonally to bottom right
	57	wario moving diagonally to bottom left
	58	warior facing left climbing ladder
	59	warior facing right climbing ladder
	68	wario moving diagonally to top right
	69	wario moving diagonally to top left
Turns out when it explodes is based on X/Y; I made a script that bots the modern version of Fire Attack: https://github.com/lunjesse/GaW4-bot/blob/master/Fire%20Attack%20(Modern).lua
Editor, Experienced Forum User
Joined: 8/8/2019
Posts: 93
This is my master-post for Game & Watch Gallery 4 analysis. As I discover more, I will link to separate post detailing the specific games. Index Randomness General operation The game appears to use a linear congruential generator for its randomness. Here is some Python code that shows its basic operation when called:
Language: python

# The RNG; an LCG def rand(): LCG_STORAGE = (LCG_STORAGE * 0x5D588B65 + 1) & 0xFFFFFFFF return LCG_STORAGE >> 0x10
When rand() is used in the pseudocode of later posts, it refers to this. The variable LCG_STORAGE is stored at the four-byte address 0x030014D0. It doesn't appear to explicity set a seed, so it simply begins operation with the default 0 stored in memory. LCG update scenarios The LCG is updated in a variety of situations. It updates once-per-frame frame on the title screen. It is updated 2-4 times when transitioning between screens (it varies a bit depending on the screen, and I will probably provide more precise details later). It updates when changing the selected game on the Game Select screen. It updates once when pausing, and once when unpausing (perhaps not consistently? More investigation required.) Each game will use the LCG for its operations, usually when deciding how long to delay certain events or which things to spawn. Practical use Without game-specific context, the value stored at this address is not very useful. However, once a specific game is better understood, the value of this address will become extremely useful in optimizing luck manipulation for that game. One important detail about the LCG storage address for TASing: the address always contains the value of the previous generated random number. So to set it up to generate a specific value, your goal is to have it be equal to the value that precedes the desired value in the LCG cycle. In general, the goal is to delay the LCG until it arrives at a useful number. There are two direct ways to affect this in all games: pausing and rebooting. Rebooting is an easy way to set the LCG to a precise frame by waiting a specific number of frames on the title screen. However, the downside is that rebooting is expensive, costing at least 491 frames plus the additional frames on the menu to cycle the LCG to the desired position. Thanks to a suggestion by jlun2, rebooting is probably unnecessary. Soft-resetting through Start-Select-A-B is sufficient, and net saves 244 frames over rebooting, totalling in to a mere 247 frames. Pausing advances the LCG two cycles and costs 97 frames. Therefore, at baseline, soft-resetting is a bit more than 2.5 times more expensive than pausing, but the extra frames to manipulate the LCG precisely may significantly worsen that ratio. For instance, in Fire, it costs an additional 283 frames to advance the LCG enough to spawn a star in the worst conditions (which the game starts near). Physics (UNFINISHED) Displayed character format More details to come about Fire Modern displayed object formats, which has a lot of potential to be generally useful in understanding all of the games if they all use the same format for organizing objects displayed on the screen.
Editor, Experienced Forum User
Joined: 8/8/2019
Posts: 93
Fire Modern Introduction In Fire Modern, there are two ways to improve the speed: spawning characters well through RNG manipulation, and bouncing characters as early as possible to allow more characters to spawn. These are sometimes at odds. Eggs appear to give two points on their initial hatch bounce, regardless of whether they hatch as a Moon or Bob-omb, and Moons give 5 points on the two subsequent bounces, totaling to 12 points. Regular characters give only 1 points per bounce, so moons are basically four times as worthwhile as regular characters. Through reboot-based RNG manipulation, a moon can be spawned at any point where a character spawns, but the frame cost may be more expensive in some cases than simply allowing less favorable spawns to occur. I generally understand how the spawning working in Fire Modern. I still have yet to completely explore the physics (and lag, which can occur if there are multiple characters on the screen). Once I understand the physics enough to reliably predict where characters will be with precise timing, I will write some scripts to automate the process of TASing. I currently have a short luck-manipulation proof-of-concept movie, and a related watchfile. Spawn mechanics part I: when & who The Python-esque pseudocode below covers the basics of spawning. However, there is one component I will explain in more detail below: character-specific spawn chances (I previously referred to this as the characters' "spawn determinant".)
Language: python

TOAD = 0 YOSHI = 1 BABY_DK = 2 EGG = 3 MOON = 4 BOB_OMB = 5 # Called every frame def attempt_spawn(): # 49-frame timer spawn_timer += 1 if (spawn_timer == 49) or (num_characters == 0): spawn_timer = 0 if ( # 41/90 (45.55%) chance to spawn ((rand() % 90) <= 40) or \ (num_characters == 0) or \ (difficulty == 2) ) and (num_characters != max_characters): num_characters += 1 character = find_available_character_slot() character.type = TOAD character_type_chosen = False if random_spawn(yoshi_spawn_chance) or (yoshi_spawn_chance == 0xFF): character.type = YOSHI character_type_chosen = True if (random_spawn(baby_dk_spawn_chance) or (baby_dk_spawn_chance == 0xFF))) and (not character_type_chosen): character.type = BABY_DK character_type_chosen = True if (random_spawn(egg_spawn_chance) or (egg_spawn_chance == 0xFF)) and (not character_type_chosen): character.type = EGG # Sub-part of attempt_spawn above def random_spawn(spawn_chance): if rand() <= math.floor(spawn_chance * 2/3): return True else: return False def find_available_character_slot(): # TODO - not analyzed in depth pass # Called whenever an egg collides with the trampoline (the first bounce) def hatch_egg(character): # 31/64 (48.4%) chance to be a moon if ((rand() & 0x3F) > 0x20): character.type = MOON else: character.type = BOB_OMB
Basically, a character will spawn whenever there are 0 characters in play, or with a 41/90 (45.55%) when the 49-frame spawn timer runs out (assuming the number of characters isn't maxed out). When it's time to spawn a character, Toad is the default, and then three checks are made in succession to determine whether the character should be converted to a Yoshi, Baby DK, or Egg. There's an important detail here: all three spawn checks are always run, but if an earlier check succeeds, later checks cannot succeed because there's a character_type_chosen. Therefore, spawning an egg is doubly complicated: not only does the random egg spawning check have to succeed, but both of the prior alternate character's checks have to fail. Spawn mechanics part 2: character spawn chances One important detail is missing from the above code: how are the character spawn chances set? For Easy and Hard mode, the spawn chances are updated at specific score thresholds. When the score is >= the score threshold, multiple variables are updated, including the spawn chances, the max_characters variable and two other currently unknown variables. The score-threshold and related data are hardcoded in 9-byte blocks starting at 0x080d389c for Easy mode and 0x080d3b12 for Hard mode. There are 70 such blocks for Easy mode, and 65 blocks for Hard mode. The block format is as follows: Byte 0-2: score threshold, byte 3:speed , byte 4: max_characters, byte 5: baby_dk_spawn_chance, byte 6: yoshi_spawn_chance, byte 7: egg_spawn_chance, byte 8: unknown. The score threshold is one base-10 score digit per byte in big endian. The speed controls the magnitude by which individual character movement timers will increase per frame. Once a character's movement timer reaches >= 180, the character will have the movement physics applied; otherwise it will just stay in place for another frame. Therefore, the practical update speed can be calculated as a movement update every 180 / speed frames (e.g. with a speed of 180 falling characters will move every frame, with 90 every two frames, etc.) Below I have included a selection from the spawn probabilities in the format <score threshold>: <yoshi_spawn_chance>, <baby_dk_spawn_chance>, <egg_spawn_chance> (max: <max_characters>, speed: <speed>) Note that although the score thresholds stop at 999, specific score pre-filtering code prevents further advancement after the 999 score values are reached for each difficulty.
Easy:

0: 0x0, 0x0, 0x0 (max: 1, speed: 66),   6: 0xff, 0x0, 0x0 (max: 1, speed: 66),   12: 0x0, 0xff, 0x0 (max: 1, speed: 66),   18: 0x0, 0x0, 0xff (max: 1, speed: 66),   ...,   999: 0x40, 0x40, 0x40 (max: 6, speed: 106)

Hard:

0: 0x20, 0x0, 0x0 (max: 2, speed: 80),   10: 0x0, 0x0, 0x40 (max: 2, speed: 82),   20: 0x0, 0x0, 0xff (max: 1, speed: 82),   27: 0x50, 0x20, 0x0 (max: 4, speed: 84),   ...,   999: 0x40, 0x40, 0x20 (max: 6, speed: 116)
For Very Hard mode ("Star" mode), there is no score-based threshold, and instead the character spawn probabilities are all set to 0x2a, max_characters is set to 7, and speed is set to 120. Physics (UNFINISHED) This script is the living document for Fire's physics. The first "fc-" functions show offsets for important values. My eventual goal is to create a script that can accurately simulate score-related behavior for Fire given a starting seed and inputs. I still have one major detail missing, which is the collision handling for bounces. Besides that, I've made fairly good progress, though there's still a few details that I will probably need to understand before a full simulation will be possible. For instance, while I understand when characters will spawn in theory, there are sometimes longer delays where a character slot is sitting in the spawn-location that I don't understand. Also, the variable I'm using to check if a character is active activates position-tracking a bit early and may need to be better specified (the current variable activates after attempt_spawn() has been successful, so it raises the question of how the delay before the character actually starts moving is calculated). (Old tidbit: the only difference between the different falling character types is how the velocity and velocity_max variables get set when a bounce occurs. Otherwise, the characters are basically treated identically with regards to physics, sharing the same acceleration value and position/velocity/etc. updating code.)
Darkman425
He/They
Editor, Experienced Forum User, Player (200)
Joined: 9/19/2021
Posts: 86
Location: Texas
I made a verification movie that generates save data with the Zelda Game & Watch unlocked at 170 160 stars. I only used rewinds making this, so I bet a good 20 minutes could be lopped off the 7.5 hour recording with proper TASing. User movie #637950635588411093 I mostly made this for anyone who wants to TAS Zelda but don't want to go through the hours of earning the 170 160 stars needed. EDIT: Dangit, wrong star count. Bleh.
Editor, Experienced Forum User
Joined: 8/8/2019
Posts: 93
Thanks for sharing! I personally have no plans currently to TAS Zelda, but having a 170-star movie will be extremely useful for comparison with a 170-star TAS (one of my long-term goals). In my personal ideal world, we would be able to use multiplayer and achieve the full 220-star completion (maybe, multiplayer could also save time in the 170-star goal). But, GBA multiplayer is not supported by BizHawk and TASing is not currently supported by mGBA upstream. Also, encoding would be nicer without multiplayer, because only a relatively small portion of the movie actually requires multiplayer. Maybe the encoder can take that into account or maybe I can self-encode!
Darkman425
He/They
Editor, Experienced Forum User, Player (200)
Joined: 9/19/2021
Posts: 86
Location: Texas
I'd like to issue a correction about my user file. It takes 160 stars to unlock Zelda, so my user file only has 160 stars. That being said it only needs 10 more stars for the credits. All of Cement Factory is untouched though and I still don't see that one ever being played in 170 stars.