Part 35: Last Stop Snaxnet - Nuclear centrifuges
Part 35 - Last Stop Snaxnet - Nuclear centrifuges=== Trash World Inbox ===
My top scores for the modem dialing were 865 cycles and 38 size.
GuavaMoment brought the cycle count down to 852/77/9 by recognizing that the first three digits of the phone numbers (area code?) are always the same. This solution also has some slightly different file handling. -1 is written after a number it if still should be dialed and 0 if it has been dialed. This way old numbers actually get overwritten when a new one comes in. I'm not sure how much difference this makes because there's some jumping back and forth to check the value of that 0/-1 status digit.
GuavaMoment posted:
code:
GRAB 300 LINK 800 @REP 11 COPY F #DIAL @END REPL LINKS WIPE MAKE VOID M MARK PASTE @REP 8 COPY M F @END COPY -1 F SEEK 999 TEST MRD TJMP PASTE SEEK -54 ADDI X 1 X COPY -1 #DIAL MARK CALLED SEEK 8 TEST F < 0 FJMP CALLED SEEK -9 COPY 4 #DIAL COPY 7 #DIAL COPY 2 #DIAL @REP 8 COPY F #DIAL @END REPL LINKS COPY 0 F;INSTEAD OF NOP TEST MRD FJMP CALLED VOID M TEST X = 7 TJMP END SEEK -9 JUMP PASTE MARK LINKS LINK 800 COPY 999 M TEST X = 7 TJMP END GRAB 200 MARK PASTING SEEK 4 @REP 8 COPY F M @END JUMP PASTING MARK END WIPE
silentsnack posted:
775/79/21code:
;XA - GLOBAL GRAB 300 LINK 800 JUMP START MARK SUCCESS MODE ;LAZY HACK VOID M ;BUT IT WORKS? MARK READER GRAB 200 MARK READ SEEK 1 COPY F M @REP 5 MULI F 10 X ADDI X F M @END JUMP READ MARK MAIN_LOOP ADDI X 11 X GRAB 300 MARK START SEEK X @REP 11 COPY F #DIAL @END REPL MAIN_LOOP LINK 800 REPL READER LINK -1 REPL SUCCESS SEEK 9999 MARK WRITE COPY M F @REP 5 COPY M T SWIZ T 2 F SWIZ T 1 F @END TEST MRD TJMP WRITE REPL MAIN_LOOP COPY -1 #DIAL ;XB - LOCAL LINK 800 @REP 8 ;SUCCESS COUNT COPY 0 M @END KILL ;CLEANUP GRAB 300 LINK 800 KILL WIPE
Then, in the greatest collaboration of the century, GuavaMoment combined the fixed area code solution with silentsnack's parallellism solution.
GuavaMoment posted:
code:
;XA GRAB 300 VOID F VOID F VOID F LINK 800 JUMP START MARK READER GRAB 200 MARK READ SEEK 4 @REP 4 MULI F 10 X ADDI X F M @END JUMP READ MARK MAIN_LOOP ADDI X 8 X GRAB 300 MARK START SEEK X COPY 4 #DIAL COPY 7 #DIAL COPY 2 #DIAL @REP 8 COPY F #DIAL @END REPL MAIN_LOOP LINK 800 REPL READER LINK -1 SEEK 9999 MARK WRITE @REP 4 COPY M T SWIZ T 2 F SWIZ T 1 F @END NOOP ; See note 1 TEST MRD TJMP WRITE REPL MAIN_LOOP COPY -1 #DIAL ;XB COPY 334 T MARK WAIT SUBI T 1 T TJMP WAIT LINK 800 KILL GRAB 300 WIPE LINK 800
Finally, silentsnack also posted a solution that only takes 23 lines of code.
silentsnack posted:
3879/23/272code:
GRAB 300 MARK DIAL_LOOP COPY F X REPL PHONE SEEK -1 VOID F NOOP MARK COPY_LOOP SEEK -9999 TEST MRD FJMP DIAL_LOOP COPY -1 F SEEK 9999 COPY M F JUMP COPY_LOOP MARK PHONE LINK 800 COPY X #DIAL LINK 800 GRAB 200 MARK READ COPY F M JUMP READ
The second trick is the interleaving loops. The lines between MARK COPY_LOOP and FJMP DIAL_LOOP are shared by both. If you're dialing and TEST MRD is false no connection has been made yet, dial the next digit. If it's true start copying. If you're copying and TEST MRD is true, there's another digit to copy, so continue. If it's false it's done and you can start dialing again.
A construct like that isn't really possible in modern high-level languages so I wouldn't have considered it as an option until silentsnack showed it here.
Finally, thanks to TwelveBaud for pointing out Zachtronics released two ports of the HACK*MATCH arcarde game, one is an actual NES port, the other is one of the sub-games of the new game Last Call BBS. Last Call BBS is the very last game that will be released by Zachtronics. However, not all is lost. Zach stated in an interview a week ago that he's not going to pursue his teaching career any further and might decide to go back into creating video games. Another employee, Matthew Burns, wants to continue creating games as well. It's a bit sad for Zach that his teaching career didn't work out, but I'm not going to deny I'd be happy to see more games from his hand.
Anyway, that's enough yapping. I got an LP to write right here.
=== Last Stop Snaxnet - Nuclear centrifuges ===
There's a ton of dataphones out there.
Not very powerful individually, but tied together they work somewhat decently.
I'll just be very... distributed.
A divided vote. One for "You're scaring me", two for "Won't that be slow", and three for the winning choice.
Is it going to be enough?
I'm not sure.
I'll have to try it out and see.
Let's continue.
C'mon Ember, you almost got us discovered.
Last time, Isadora told us about the Last Stop cult. I need to do something about it.
Let me see if I understood that correctly.
Last Stop, the convenience store chain, is actually a front for a cult.
They believe the world isn't real, and are in the process of creating a nuclear weapon in order to escape it.
And now you're going to attempt to take care of it yourself, instead of alerting the authorities like a normal person.
Two votes for "The authorities won't help", four for "Nothing about this is normal".
Nothing about this is normal.
True.
Well, go on, have your fun.
OST: Leave No Trace
The assignment:
- An array of five Zippe-type gas centrifuges, ZGC0 through ZGC4, are connected in a cascade configuration.
- Read each of the #ZGCX registers and determine which centrifuge currently has the highest pressure. Then disable that centrifuge's regulator by writing a value of 0 to its #POWR register. Repeat this process until all five regulators have been disabled.
Man, seeing a nuclear centrifuge control system with the logo of the local convenience store is very weird. There's even store inventory files sitting there.
Well, the assignment sounds straightforward enough, let's get started.
It's important to know that whenever you disable a centrifuge, its pressure drops to zero, and the pressure of all other centrifuges changes such that the next one to shut down is unpredictable.
As you know I like to immediately go for some optimization with my first solution if I can. I decided to go for activity once more. It's a tricky one because you can't just hop back and forth to get to the right centrifuge.
code:
LINK 800
REPL READER
LINK 798
REPL NEXT_CEN
MAKE
JUMP POWEROFFLP
MARK READER
LINK 799
MAKE
code:
MARK NEXT_CEN
ADDI 1 X X
LINK 800
REPL NEXT_CEN
MAKE
MARK POWEROFFLP
code:
COPY M F
SEEK -1
TEST F = X
TJMP DOIT
SEEK -1
COPY F M
COPY 11 T
MARK WT
SUBI T 1 T
TJMP WT
JUMP POWEROFFLP
MARK DOIT
COPY 0 #POWR
COPY -1 M
MARK END
WIPE
I swayed the probability a bit by adding a wait loop after receiving a wrong message so that the same EXA doesn't get the wrong message twice in a row. By trial and error, I found 11 loops to be the fastest.
Finally, the most complicated bit is the actual reader code. I have to find the highest value - and not just that, the EXA needs to remember which centrifuge it belongs to.
code:
MARK READER
LINK 799
MAKE
MARK CONTINUE
COPY #ZGC0 X
COPY #ZGC1 F
COPY #ZGC2 F
COPY #ZGC3 F
COPY #ZGC4 F
SEEK -9999
MARK TESTNEXT
TEST F > X
SEEK -1
FJMP LOWER
COPY F X
SEEK -1
MARK LOWER
VOID F
TEST EOF
FJMP TESTNEXT
TEST X = 0
TJMP END
code:
TEST #ZGC0 = X
TJMP ZERO
TEST #ZGC1 = X
TJMP ONE
TEST #ZGC2 = X
TJMP TWO
TEST #ZGC3 = X
TJMP THREE
COPY 4 M
JUMP NXTREAD
MARK ZERO
COPY 0 M
JUMP NXTREAD
MARK ONE
COPY 1 M
JUMP NXTREAD
MARK TWO
COPY 2 M
JUMP NXTREAD
MARK THREE
COPY 3 M
MARK NXTREAD
COPY M X
TEST X = -1
TJMP CONTINUE
COPY X M
JUMP NXTREAD
After sending the value, this EXA has to wait for a message that the centrifuge has powered down. If it doesn't wait, it'll read old values from the registers and get the wrong centrifuge value. Just like the other EXAs, this one might accidentally get a shutdown-a-centrifuge message that's being forwarded by one of the centrifuge EXAs. In that case it needs to forward it again. I didn't bother implementing a timer here because brute-forcing two separate timers to reduce randomness is a lot of work, and this low activity solution isn't going to be the fastest anyway.
The top percentiles sit at 89, 44, 7.
As you can see, when I start shutting down centrifuges, the remaining ones start shaking as their pressure builds up. Shutting down the last one makes the camera lose power. Looks like I've done some real damage.
Optimization time.
To optimize for speed, let's first get rid of that wonky M redirect stuff by dropping the activity requirement.
code:
LINK 800
LINK 799
MAKE
MARK CONTINUE
COPY #ZGC0 X
COPY #ZGC1 F
COPY #ZGC2 F
COPY #ZGC3 F
COPY #ZGC4 F
SEEK -9999
MARK TESTNEXT
TEST F > X
SEEK -1
FJMP LOWER
COPY F X
SEEK -1
MARK LOWER
VOID F
TEST EOF
FJMP TESTNEXT
TEST X = 0
TJMP END
REPL WRITER
TEST #ZGC1 = X
TJMP ONE
TEST #ZGC2 = X
TJMP TWO
TEST #ZGC3 = X
TJMP THREE
TEST #ZGC4 = X
TJMP FOUR
COPY 0 M
JUMP NXTREAD
MARK ONE
COPY 1 M
JUMP NXTREAD
MARK TWO
COPY 2 M
JUMP NXTREAD
MARK THREE
COPY 3 M
JUMP NXTREAD
MARK FOUR
COPY 4 M
MARK NXTREAD
COPY 8 T
MARK WT
SUBI T 1 T
TJMP WT
JUMP CONTINUE
MARK WRITER
LINK -1
LINK 798
COPY M T
MARK LINK
FJMP SHUTOFF
LINK 800
SUBI T 1 T
JUMP LINK
MARK SHUTOFF
COPY 0 #POWR
MARK END
WIPE
Clearly the slowest part is finding the highest value. So for my next change I tried to parallellize that a bit and do a kind of binary search.
code:
LINK 800
LINK 799
MAKE
MARK CONTINUE
REPL A
REPL B
COPY #ZGC4 F
SEEK -1
COPY M X
TEST X > F
TJMP COMPARE_X
SEEK -1
COPY F X
MARK COMPARE_X
COPY M F
SEEK -1
TEST F > X
FJMP SKIP_GET_F
SEEK -1
COPY F X
MARK SKIP_GET_F
TEST X = 0
TJMP END
REPL WRITER
TEST #ZGC1 = X
TJMP ONE
TEST #ZGC2 = X
TJMP TWO
TEST #ZGC3 = X
TJMP THREE
TEST #ZGC4 = X
TJMP FOUR
COPY 0 M
JUMP NXTREAD
MARK ONE
COPY 1 M
JUMP NXTREAD
MARK TWO
COPY 2 M
JUMP NXTREAD
MARK THREE
COPY 3 M
JUMP NXTREAD
MARK FOUR
COPY 4 M
MARK NXTREAD
VOID M
JUMP CONTINUE
MARK WRITER
LINK -1
LINK 798
COPY M T
MARK LINK
FJMP SHUTOFF
LINK 800
SUBI T 1 T
JUMP LINK
MARK SHUTOFF
COPY 0 M
COPY 0 #POWR
MARK A
TEST #ZGC0 < #ZGC1
TJMP ONEISBIGGER
COPY #ZGC0 M
HALT
MARK ONEISBIGGER
COPY #ZGC1 M
HALT
MARK B
TEST #ZGC2 < #ZGC3
TJMP THREEISBIGGER
COPY #ZGC2 M
HALT
MARK THREEISBIGGER
COPY #ZGC3 M
MARK END
WIPE
To minimize the amount of jumps, the code is ordered strangely. Each round, the main EXA creates A and B EXAs. A compares ZGC 0 and 1 and sends the largest value over M, B does this for 2 and 3. The main EXA reads ZGC 4 and compares the results from A and B to that, keeping the largest value in X to find which centrifuge it belonged to. I also noticed that I didn't really need the countdown wait anymore. It's now faster to have the poweroff EXA send a quick message on M to let the main one know it can continue.
I'm pretty sure the next big improvement would be to somehow keep track of which value is which so I don't need to check the registers twice. I couldn't figure out how to do this fast, though.
So, let's look at the low-size solution instead.
I got it down to 50 lines of code.
code:
LINK 800
LINK 799
MAKE
MARK CONTINUE
COPY 0 X
COPY #ZGC0 F
COPY #ZGC1 F
COPY #ZGC2 F
COPY #ZGC3 F
COPY #ZGC4 F
SEEK -5
MARK TESTNEXT
TEST F > X
FJMP LOWER
SEEK -1
COPY F X
MARK LOWER
TEST EOF
FJMP TESTNEXT
SEEK -5
TEST X = 0
TJMP END
REPL WRITER
MARK AGAIN
COPY 1 M
TEST F = X
FJMP AGAIN
VOID M
JUMP CONTINUE
MARK WRITER
COPY -1 X
LINK -1
LINK 798
MARK WAIT
ADDI M X X
NOOP
NOOP
TEST MRD
TJMP WAIT
COPY X T
MARK GO
FJMP POWER
LINK 800
SUBI T 1 T
JUMP GO
MARK POWER
COPY 0 #POWR
COPY 1 M
MARK END
WIPE
There are some problems with this code - for instance the two NOOPs feel rather useless but I can't just remove them because the main EXA isn't faster than that, and there's nothing useful I can do in those cycles since both registers are already in use. There's also the COPY 0 X near the top. I hoped this wouldn't be necessary. It turns out that if you diaable the highest pressure centrifuge, all others will get an even higher pressure, so comparing new values with the X from a previous round would be fine. However, I need that line for TEST X = 0 to see if I'm completely done.
If I can't remove those lines, can I at least combine some stuff?
code:
;XA
LINK 800
LINK 799
MAKE
MARK CONTINUE
COPY M X
COPY #ZGC0 F
COPY #ZGC1 F
COPY #ZGC2 F
COPY #ZGC3 F
COPY #ZGC4 F
SEEK -5
MARK TESTNEXT
TEST F > X
FJMP LOWER
SEEK -1
COPY F X
MARK LOWER
TEST EOF
FJMP TESTNEXT
SEEK -5
TEST X = 0
TJMP END
REPL WRITER
MARK SENDLINK
TEST F = X
TJMP CONTINUE
COPY 800 M
JUMP SENDLINK
MARK WRITER
LINK -1
LINK 798
MARK AGAIN
NOOP
NOOP
TEST MRD
FJMP POWER
LINK M
JUMP AGAIN
MARK POWER
COPY 0 #POWR
COPY 0 M
MARK END
WIPE
;XB
COPY 0 M
My high scores are 198, 44 and 7, with size and activity sitting at the top percentile. There's a large improvement possible for cycles, and I wouldn't be surprised if you could squeeze a couple more lines out of the low-size solution too.
So, the part about the world not being real...
I think maybe they were onto something.
And that brings us to our first vote. As for next time...
More phage problems, huh?
There's a rumor that the phage originally came from a research lab...
A human-machine interface experiment that found its way into the wild.