Part 30: King's Ransom Online
Part 30 - King's Ransom Online=== Trash World Inbox ===
Last time, we had to write an algorithm to calculate the supposedly winning player in Extreme League Baseball.
It was quite a hard one to optimize, but as always you people came up with some ideas I'd never thought of.
GuavaMoment posted:
86/47/3.code:
XA: MAKE VOID M COPY M X COPY M F JUMP START MARK STUFF VOID M VOID M MARK START TEST M > X FJMP STUFF COPY M X SEEK -1 COPY M F JUMP START XB: LINK 800 GRAB 199 MARK START COPY F T REPL CALC NOOP *delays the next calculating EXA from coming out too soon* NOOP NOOP TEST EOF FJMP START DROP LINK -1 COPY 4 T *a delay loop to slow down the upcoming kill command until everything is done* MARK LOOP SUBI T 1 T TJMP LOOP KILL MARK CALC GRAB T SEEK 1 ADDI F F X ADDI F X X DIVI X 3 X MULI F F T DIVI T F T ADDI X T X SUBI F F T MULI T 20 T ADDI X T M SEEK -999 ADDI X T M COPY F M
silentsnack posted:
Similar to the Wonderdisc puzzle, we can reduce cycles for logic-jumps by @REPing some of the slowest and most repetitive processing
79/85/7code:
;XA LINK 800 GRAB 199 @REP 10 COPY F T REPL DO_STUFF @END MARK DO_STUFF GRAB T SEEK 1 ADDI F F X ADDI X F X DIVI X 3 X MULI F F T DIVI T F T ADDI X T X SUBI F F T MULI T 20 T ADDI X T X TEST X > 341 DIVI X T M MODE SEEK -9 COPY F M ;XB LINK 800 MAKE COPY M X MODE JUMP COPY MARK MAX SEEK -2 MARK COPY COPY M F COPY X F MARK NEXT @REP 3 MODE SEEK -1 COPY M X TEST X > F MODE TJMP MAX VOID M @END JUMP NEXT ;XC COPY 34 T MARK WAIT SUBI T 1 T TJMP WAIT LINK 800 NOOP KILL KILL KILL GRAB 400 LINK -1 SEEK 1 VOID F
As a side effect, this makes the timing independent of the number of EXAs, meaning that TEST X > 341 trick can be used so EXAs with a score that never wins die immediately. I actually thought of that but couldn't get it to work because it kept throwing off the timing.
silentsnack posted:
As for size optimization, what if we compared scores directly instead of sending a bunch of busywork transmissions and VOIDing them?
110/32/2code:
;XA -- LOCAL LINK 800 GRAB 199 MARK ARTICHOKE COPY F T REPL BROCCOLI JUMP ARTICHOKE MARK DISJUNCTION MODE COPY F M MARK BROCCOLI GRAB T SEEK 1 ADDI F F X ADDI X F X DIVI X 3 X MULI F F T DIVI T F T ADDI X T X SUBI F F T MULI T 20 T ADDI X T X MARK CHIPOTLE COPY X M SEEK -9 TEST MRD FJMP DISJUNCTION TEST X > M TJMP CHIPOTLE ;XB -- GLOBAL MAKE COPY M F ;XC -- LOCAL LINK 800 VOID M
Joking aside, this solution hurts my head. After running the algorithm, each EXA sends the result on local M. The very first one gets voided by XC to prevent the solution from getting stuck. It then checks if another one is sending on M. The timing works out quite precisely - if there's nothing on M this is the last remaining EXA, and the DISJUNCTION jump makes sure the name is written to the file by XB.
If there a value on M, this EXA tests if it has a greater algorithm result than the incoming value. If not, it dies. Otherwise, it jumps back to CHIPOTLE and sends its high score again. At that point the other EXA will be waiting to TEST and since it will have the smaller value, it will die. Another EXA will be ready to send its M and the pattern repeats.
So the EXAs are literally playing an elimination tournament against each other, the last one standing will have the high score. Very clever.
I'm happy to see that nobody changed the core math code. That means I didn't miss any obvious optimizations in there.
=== King's Ransom Online ===
Okay, uh. Hmm.
Processing.
It turns out sports betting is not, in fact, a good way to make money.
You should have said something.
Four votes for each between the threads! It comes down to a coin toss this time.
I thought you'd be smarter than that.
What does that mean?
Don't put this on me.
This is all happening because I'm trying to help you.
Let's continue.
Don't gaslight me, lady.
Next up is King's Ransom Online, the massive multiplayer game that's all the rage right now.
Did you know people pay real money for items in online games?
One vote for "it's pretty common", three for "Real money..." and four for "Losers". Feeling snarky, I see.
Losers...
It's apparently a booming business.
Some people even make their living from games this way.
Normally, it takes a lot of skill and perseverance.
But if someone were able to change the ownership of these digital objects...
Theoretically, that person could become very wealthy overnight.
I'm not suggesting that you do that, of course.
But I am suggesting you try.
I mean, I do need the money. And it's not like I'm stealing real objects, right?
Let's jack in.
OST: EXA Power
The assignment:
- Reset the ownership of all castles and sub-buildings to P00000 (file 300), the player ID for unowned buildings.
- To ensure that there are no witnesses you must first disconnect all connected players. Terminate every EXA in every host before changing any castle or sub-building files anywhere in the network. If you leave an EXA alive in one host while changing a file in another you will fail the task.
- For more information see "Network Exploration: King's Ransom Online" in the second issue of the zine.
More information in the zine.
Alright, ignoring all the old-timey text and that ad for a wristwatch, looks like players are represented by EXAs, and there's two main types of files: ones representing buildings and ones representing weapons.
Each host has a building 200 which is a castle, and a bunch of sub-buildings. Generally, the file IDs of sub buildings are in the castle file:
However, there are cases where sub-buildings have their own sub-buildings so I will need to check for those as well.
I can't see the details of the weapons yet because they're all being held by foreign EXAs. What I do see is two counters in the Goals: there are 14 EXAs to terminate and 26 buildings to clear. This differs a bit between test runs. What seems consistent is there's always between 2 and 6 buildings per host, including the castle, and between 1 and 3 EXAs.
Well, let's start by terminating the EXAs.
code:
COPY 800 X
LINK X
@REP 5
REPL LINK
ADDI X 1 X
@END
MARK LINK
LINK X
KILL
KILL
KILL
You can see the EXAs do represent players - killing them makes their characters disappear from the screen in the top right. The weapons are as described in the zine. Unfortunately, both the weapons and buildings have IDs in the 200-300 range. I won't be able to just loop over all file IDs to only change the buildings.
I'll just start with the obvious solution then, grabbing the castle, updating its ownership, and grabbing any sub-buildings from its list.
I already got an EXA in every host, and the assignment says I can't start messing with the buildings until the foreign EXAs are gone, so I'll just use the same ones.
code:
;XA
COPY 800 X
LINK X
COPY M T
@REP 5
REPL LINK
ADDI X 1 X
@END
MARK LINK
LINK X
KILL
KILL
KILL
COPY 200 X
MARK GRABSUBFILE
GRAB X
SEEK 2
COPY T F
MARK SUBFILELP
COPY F X
REPL GRABSUBFILE
JUMP SUBFILELP
;XB
GRAB 300
COPY F M
After the KILLs, the castle file ID (200) is copied into X, and then my EXA grabs the file, seeks to the player ID position, and overwrites it with the null id. For each following sub-building id, it loads the value into X and makes a new EXA that grabs it. This recursive code should get all buildings, no matter how deep it has to go.
However, this solution doesn't quite work. The first XA already starts messing with the buildings before the last one has killed all the foreign EXAs.
I can just start with an ugly workaround, just have everything wait until all foreign EXAs are gone.
Six NOOPs or a short loop do the trick.
48/35/25 as an initial score. The top percentiles are 32, 26 and 25.
What's also interesting is the huge distribution in size. I can imagine that this puzzle is much more devious if you can't figure out recursion. Luckily, I'm quite familiar with it.
Let's look at optimizations. First of all, to lower the cycle count, instead of having that whole list of NOOPs I can make it conditional - earlier EXAs have to wait longer.
code:
; XA
COPY 800 T
LINK T
COPY M X
@REP 5
REPL LINK
ADDI T 1 T
@END
MARK LINK
LINK T
KILL
KILL
KILL
SUBI 807 T T
DIVI T 2 T
MARK WAIT
SUBI T 1 T
TJMP WAIT
COPY 200 T
MARK GRABSUBFILE
GRAB T
SEEK 2
COPY X F
MARK SUBFILELP
COPY F T
REPL GRABSUBFILE
JUMP SUBFILELP
;XB
GRAB 300
COPY F M
I can shorten the REPL loop by reading hardcoded LINK ids from M instead.
code:
;XA
LINK 800
COPY M X
@REP 5
REPL LINK
@END
MARK LINK
LINK M
KILL
KILL
KILL
NOOP
NOOP
COPY 200 T
MARK GRABSUBFILE
GRAB T
SEEK 2
COPY X F
MARK SUBFILELP
COPY F T
REPL GRABSUBFILE
JUMP SUBFILELP
;XB
GRAB 300
COPY F M
;XC
REPL Y
REPL X
COPY 800 M
HALT
MARK X
COPY 801 M
HALT
MARK Y
NOOP
COPY 802 M
;XD
REPL Y
REPL X
COPY 803 M
HALT
MARK X
COPY 804 M
HALT
MARK Y
NOOP
COPY 805 M
Sending the player ID over M costs two cycles at the top, while we're wasting cycles with NOOPs further down. If I send that message later, I need to send it 6 times but with some smart timing I can save two cycles.
code:
;XA
LINK 800
@REP 5
REPL LINK
@END
MARK LINK
LINK M
KILL
KILL
KILL
NOOP
COPY 200 T
COPY M X
MARK GRABSUBFILE
GRAB T
SEEK 2
COPY X F
MARK SUBFILELP
COPY F T
REPL GRABSUBFILE
JUMP SUBFILELP
;XB
GRAB 300
COPY F X
NOOP
NOOP
NOOP
@REP 5
REPL SEND
@END
MARK SEND
NOOP ; For some reason this timing is fastest
NOOP
COPY X M
;XC and XD same as before.
Starting from the lowest size code above (the 34 LoC one), the first thing I noticed is that the two lines SUBI 807 T T, DIVI T 2 T aren't strictly necessary. As long as the file fiddling doesn't happen too early it's fine. Leaving those out just makes the WAIT loop run 800-odd times and that makes it slow, but it still works.
And since I need that wait loop anyway, why not put the KILL in there? That way it gets executed plenty of times but it saves two additional KILL lines.
code:
;XA
COPY 800 T
LINK T
COPY M X
@REP 5
REPL LINK
ADDI T 1 T
@END
MARK LINK
LINK T
MARK WAIT
KILL
DIVI T 5 T
TJMP WAIT
COPY 200 T
MARK GRABSUBFILE
GRAB T
SEEK 2
COPY X F
MARK SUBFILELP
COPY F T
REPL GRABSUBFILE
JUMP SUBFILELP
;XB
GRAB 300
COPY F M
I tried some more things, such as putting the REPL LINK in a loop. That didn't work out because T and X are already occupied and adding a counter in some other way costs a lot of lines.
It's also possible to get rid of getting the subfile ids from each file. Use a simple loop counting down from file id 300 and trying every id in turn. However, you do need an extra check to not write to the weapon files. That's doable - just check if the second value is a number or not. But that check made it longer than the current solution. So this is the best I got.
---
Finally, there's activity. I already have top percentile activity, but could I go beyond? Well, no matter what I need 7 LINKs to get the EXAs in position. Add to that 3 KILLs in each of the 6 hosts for the 25 score.
However, the test with the highest number of foreign EXAs has 17 of them. That means that theoretically, you could save one more activity point.
To do so, I'd need to figure out how many foreign EXAs there are per host. How would you do that? You can't communicate with them. The only thing I can think of is fill the hosts with your own EXAs. Once a host is full, both LINK and REPL instructions block until a space is freed.
I spent some time (actually, way too much time) attempting to build a solution. It couldn't quite get it working, but I feel I'm quite close. I'll share what I have, maybe someone in the thread has the missing link.
code:
;XA LOCAL
COPY 800 X
LINK X
@REP 5
REPL LINK
ADDI X 1 X
@END
MARK LINK
LINK X
; There's a single EXA in each host now.
REPL COUNTER ; This one starts by waiting
REPL FILLER ; also starts by waiting
COPY 200 X
MARK GRABSUBFILE
; Grab all the files recursively. Once you have them jump to END. Do not modify files yet.
GRAB X
SEEK 3
MARK SUBFILELP
TEST EOF
TJMP END
COPY F X
REPL GRABSUBFILE
JUMP SUBFILELP
MARK FILLER
; Wait until all files are grabbed
COPY 20 T
MARK WAIT
SUBI T 1 T
TJMP WAIT
; Replicate until all squares are filled, but not more than 8 times. After replicating, each EXA goes to END.
COPY 8 T
MARK FILLRUP
SUBI T 1 T
NOOP
FJMP END
REPL FILLRUP ; This will block once the squares are filled.
JUMP END
MARK COUNTER
; Wait until GRABSUBFILE and FILLER are done.
COPY 36 T
MARK WAIT2
SUBI T 1 T
TJMP WAIT2
COPY 0 X ; Add all local M values together. There are sent from END, below.
MARK NEXTM
TEST MRD
FJMP NEXTVOID
ADDI M X X
JUMP NEXTM
; After reading from M, the EXA who sent it halts, freeing up a space. That causes FILLRUP to continue until it's at 8 repl steps.
; This voids their M messages so they stop immediately.
MARK NEXTVOID
NOOP
TEST MRD
FJMP ENDVOID
VOID M
JUMP NEXTVOID
MARK ENDVOID
; At this point all EXAs except this one are gone from the host. This one has the total of the M messages in X.
; X = 12 squares minus this EXA, minus the EXA where REPL was blocking, minus the number of foreign EXAs
; so 10 - X = number of foreign EXAs.
SUBI 10 X T
; Kill em.
MARK KILLOOP
KILL
SUBI T 1 T
TJMP KILLOOP
; Switch to global mode, grab the files again, request the player ID and update the files.
COPY 200 X
MODE
MARK GRABSUBFILE2
GRAB X
SEEK 2
COPY M F
MARK SUBFILELP2
COPY F X
REPL GRABSUBFILE2
JUMP SUBFILELP2
MARK END
COPY 1 M
; ---- XB ---- (global)
; Wait until all foreign EXAs are killed (has to be timing based, limited options here)
; Then start sending the player ID on global M. 36 times = the maximum number of files in any test.
GRAB 300
COPY F X
COPY 88 T
MARK WAIT
SUBI T 1 T
TJMP WAIT
COPY 36 T
MARK LOOP
COPY X M
SUBI T 1 T
TJMP LOOP
; ---- XC ---- (global)
; Wait until all files are updated, then VOID M any remaining sends from XB.
; KILL is not an option.
COPY 155 T
MARK LP
SUBI T 1 T
TJMP LP
MARK ENDLOOP
VOID M
NOOP
NOOP
TEST MRD
TJMP ENDLOOP
If you replace SUBI 10 X T with COPY 3 T to hardcode the number of kills, it does run with an activity of 25, proving that the logic is sound.
It's necessary to create file grabbing EXAs twice, because KILLs prefer own EXAs.
The one problem I couldn't resolve is the fact that as soon as the XA counter starts reading from M, those file grab and filler EXAs start dying, so the blocked REPL one starts running again, and sends to M before the counter is done counting, causing a wrong total. I tried a lot of different ways to slow them down, but nothing quite worked.
That's it for optimizations. But there's one more thing. This level has a Steam achievement tied to it, but it's quite tricky to get. It is called KLEPTOMANCER, and you might figure out it's for this level by its description, Verily hath every item been unduly purloined. What you need to do is move all weapons into the home host while leaving buildings alone. Since sub-buildings aren't locked to a host this takes a bit of work.
At least for an achievement none of the regular rules apply - the EXAs don't need to finish, for instance.
code:
;XA
COPY 800 X
LINK X
@REP 5
REPL LINK
ADDI X 1 X
@END
MARK LINK
LINK X
KILL
KILL
KILL
COPY 199 X
MARK GRABFILES
ADDI X 1 X
REPL GRABAFILE
JUMP GRABFILES
MARK GRABAFILE
GRAB X
SEEK 1
TEST F < 9999
DIVI 1 T T
LINK -1
LINK -1
;XB
GRAB 300
WIPE
I think this is right. Steam achievements don't trigger a second time after unlocking them in a previous playthrough, so I hope I understood the unlock requirements correctly.
Moving on...
Oh. They just shut the whole game down.
Something about "hackers" or something?
I guess that means you.
People are taking this so seriously.
The first vote.
You know what really moves people?
Art.
And the vote for the intro for next time.
A fun fact to end with: there's an annoying little bug in the game, where if you beat a level, and then directly quit out of the game without going to the 'desktop' (level select with Ember etc.), the next part of the CHATSUBO chat doesn't trigger. I have no idea if it catches up eventually or not.
While working on this update I shut down the game without properly exiting, and the bug triggered. Since I like to keep the chat in sync with the rest of the story, I had no choice but to resort to hacking the save files to "unsolve" the level so I could re-trigger the chat. It feels extremely meta, but there you have it.