Part 43: Cerebral Cortex
Part 43 - Cerebral Cortex=== Trash World Inbox ===
As always, let's get started with optimizations. I got it down to 335 cycles and 54 lines.
silentsnack posted:
for optimizations, from the histograms it looks like 32 lines should be possible, maybe 31, but the best i've managed is 33884/33/104code:
GRAB 301 MARK MAIN_LOOP ADDI X 5 X ADDI F 1 T REPL DATA MODI X 55 T TJMP MAIN_LOOP COPY 26 T MARK WAIT_TRANSFER SUBI T 1 T TJMP WAIT_TRANSFER REPL DIAL; *DISCO* JUMP MAIN_LOOP MARK DATA REPL DIAL GRAB 300 MARK READ COPY F M JUMP READ MARK DIAL LINK 800 SUBI T 1 #DIAL LINK 800 MARK PRINT COPY M #DATA NOOP TEST MRD TJMP PRINT SUBI 441 X T MARK COUNTDOWN SUBI T 1 T TJMP COUNTDOWN COPY 0 #PAGE
silentsnack posted:
fast solution isn't too mind-blowing, mostly splitting functions for parallelization with hard-coded delays so that the variable message length doesn't break loop synchronization
291/76/27code:
;XA MODEM AND TIMING GRAB 301 LINK 800 MARK LOOP @REP 11 COPY F #DIAL @END TEST EOF REPL TIMER SUBI 1 T T;LOGIC FLIP MULI T 9 T;EOF=0 ELSE=9 TJMP WAIT_DATA WIPE HALT MARK WAIT_DATA SUBI T 1 T TJMP WAIT_DATA SUBI X 18 X JUMP LOOP MARK TIMER LINK 800 TJMP LAST;EOF=TRUE ADDI X 125 T MARK WAITIMER SUBI T 1 T TJMP WAITIMER NOOP MARK LAST COPY 0 #PAGE ;XB TRANSMIT MESSAGE GRAB 300 LINK 800 JUMP START MARK HANGUP COPY -1 #DIAL MARK START ADDI X 1 X SEEK -99 LINK M;WAIT FOR SYNC @REP 8 COPY F #DATA @END MARK DATA COPY F #DATA TEST EOF FJMP DATA LINK -1 MODI X 8 T TJMP HANGUP WIPE ;XC TIMING (XB) CONTROL LINK 800 MARK LOOP COPY 5 T MARK WAIT_DIAL SUBI T 1 T TJMP WAIT_DIAL COPY 800 M ADDI X 1 X;DO 8 TIMES MODI X 8 T DIVI T T T;THEN CRASH COPY 9 T MARK WAIT_DATA SUBI T 1 T TJMP WAIT_DATA JUMP LOOP
=== Mitsuzen HDI-10 - Cerebral Cortex ===
[selenium_wolf] heads up all, im going to reboot the chatsubo server
[selenium_wolf] some downtime expected
Damn. With everything breaking down I wonder if they'll get it back up. Even though I only lurked I'll miss those folks.
Okay, time for me to eat you.
There were 3 votes for the third option, one for each of the others.
You've gotta be shitting me.
You're dying anyway, so what's the difference?
I'll download a map of your brain and run your mind inside of me.
This choice doesn't change the dialogue.
Will I still be me?
There's no way to prove that it won't be your death.
I mean, it's going to be like death. Which you were going to experience anyway.
Or maybe it won't be like death.
Who knows! You'll have to do it and see.
Well. She's right, I don't have much to lose at this point anyway. Let's do this.
OST: EXA Power
I need to:
- Create a file in your host containing the hostname and hardware register value of each neuron exactly once, sorted as pairs from lowest to highest hostname.
- Note that each test run has its own unique network layout.
- For more information see "Debugging the Phage" in the first issue of the zine.
For whatever reason the "leave no trace" goal is enabled here. Who cares if there's some EXAs in my dead body?
Initial thoughts: I need to save the actual host name in the file? Hah, the uncommon HOST command. The changing network layouts make this quite difficult, but there are some commonalities we can use. First of all, all networks appear to be acyclic (no loops). That means I don't have to worry about running in circles and grabbing the same value multiple times. Secondly, the LINK ids are always -3, 3, -1, 1, with the link back to the previous host being the same value but with flipped sign.
Finally, the resulting file needs to be sorted. We've seen before that sorting is quite tricky if you have limited registers.
The hostnames are text strings of the format AR123456, with changing numbers. The basic instructions sheet in the first edition of the zine says that you can just use normal compare operations on strings, and it'll compare by alphabetical order. That should work fine for sorting these hostnames.
Just to be clear, the result file should look something like AR123456, -39, AR2345678, -68, and so on.
Let's get started then.
code:
LINK 800
JUMP START
MARK ONE
LINK 1
MARK START
REPL ONE
REPL THREE
REPL M_THREE
JUMP READNERV
MARK THREE
LINK 3
REPL ONE
REPL M_ONE
REPL THREE
JUMP READNERV
MARK M_THREE
LINK -3
REPL ONE
REPL M_ONE
REPL M_THREE
JUMP READNERV
MARK M_ONE
LINK -1
REPL M_ONE
REPL THREE
REPL M_THREE
MARK READNERV
JUMP READNERV
Next, I spent quite a while coming up with a way to get all the data together. It seems like it should be easy with M, but it isn't. There's no way to combine a string and a number into a single M message so each EXA hitting a #NERV needs to send twice. But with the way the EXAs spread through the network, it's common to have two EXAs sending data at the same time, which means you lose track of which nerve data belongs to which hostname.
I considered having some EXA at home telling one EXA at a time that it's ready to receive, but that means all the nerve EXAs need to be listening and they'll just get each other's messages, complicating things more. I also considered giving EXAs a wait time based on what path they took to get to their nerve, for instance add N cycles if they link to 3, and add M if they link to 1. That might work, except if one EXA links to 3 and then 1, and the other to 1 and then 3, their wait time is the same, so to make it unique would require a more complicated algorithm.
A third way, dropping any semblance of getting a low activity, would be for each EXA to walk back home with the #NERV data. The problem with this is that either you need to keep track of the path, or you need to try going everywhere on the way back. Either way, you're going to need a file to store data in and files aren't copied by REPLs, making this difficult as well.
Seems like there's no straightforward solution.
After trying a bunch of approaches, I decided to go with using M anyway, but brute-forcing it.
code:
;XA
LINK 800
JUMP START
MARK ONE
LINK 1
MARK START
REPL ONE
REPL THREE
REPL M_THREE
JUMP READNERV
MARK THREE
LINK 3
REPL ONE
REPL M_ONE
REPL THREE
JUMP READNERV
MARK M_THREE
LINK -3
REPL ONE
REPL M_ONE
REPL M_THREE
JUMP READNERV
MARK M_ONE
LINK -1
REPL M_ONE
REPL THREE
REPL M_THREE
MARK READNERV
COPY #NERV X
MARK RETRY
COPY 0 M
COPY M T
FJMP RETRY
MARK WAIT
SUBI T 1 T
TJMP WAIT
HOST M
COPY X M
;XB
@REP 10
NOOP
@END
MARK TIMER
TEST MRD
FJMP END
VOID M
ADDI 60 X X
COPY X M
JUMP TIMER
MARK END
MAKE
MARK FILE
COPY M F
COPY M F
COPY 65 T
MARK WAIT
SUBI T 1 T
TJMP WAIT
TEST MRD
TJMP FILE
NOOP
If an XA gets a zero it just retries asking for the wait time. This can go on for a long time, which is why I needed an exceptionally long waiting time - 60 cycles per XA instance, for now. It'll sometimes take that long before all requests are properly processed. When a request actually makes it to XB, it sends this value and increases it for the next EXA. When there are no requests left, XB jumps to END which means it actually gets ready to read data and write it to a file. Now that the brute-forcing is done, XB has to wait for each EXA's timer to run out as well.
It's very ugly, but at least for test case 1 it works. I may have to increase the wait times even more for the other tests but we'll cross that bridge when we get to it.
For test 1, XB finishes with all the right data pairs after 1637 cycles. What's left is to sort them.
All the way back in part 20, to optimize that assignment, I already had to build a sorting algorithm. It found the lowest value in a file and sent that to another EXA, repeatedly. That code wouldn't quite work here because I need to send pairs of values, but I'm sure I could extend that code to do so.
However, what I didn't show back then is that I spent quite some time writing a sorting algorithm that only requires a single EXA. With only two registers, one being used for tests as well, I was quite surprised it was possible at all.
Even though the single-EXA sorting algorithm isn't the fastest, I'd like to use that solution for this assignment, that way I didn't write it for nothing. It, too, needs to be expanded to deal with the pairs of values. Because this code is quite complex, I'll explain the basic algorithm for single values first, and then move on to the version for this assignment.
quote:
=== Single EXA sorting algorithm===
The only algorithm that I could make work with a single EXA is a form of Bubble Sort. I'll explain in detail in a bit. For the coders reading this, it's because Bubble Sort doesn't require you to remember any positional information. You just swap pairs of adjacent items and repeat this until the entire list is sorted.
As an example, I'll sort the file from the first test case of the Palo Alto library (update 20). It contains the values: 512, 896, 206, 349, 171,The algorithm starts by loading the first value in the buffer (register X) and removing it from the file. The rest of this code comes into play later. File contents 896*, 206, 349, 171, the * is the cursor's location.code:
GRAB 300 ; LOAD FIRST VAL AND ; REMOVE FROM FILE COPY F X MARK NEXTROUND SEEK -2 VOID F MARK NEXT TEST EOF TJMP EOF
Next, it tests if the current value in the file is smaller than the value in the buffer. If so, it swaps them. In the example, the file now contains 512, 206*, 349, 171,, and X now holds value 896. T was just used to store a value during the swap.code:
; IF VAL AT CURRENT FILE ; POSITION IS GREATER ; THAN X, SWAP F VAL ; WITH X USING T INTERM. TEST X < F FJMP NEXT SEEK -1 COPY F T SEEK -1 COPY X F COPY T X
Just jump back to NEXT. We're not at the EOF yet so the TEST X < F is run again. 896 definitely is not smaller than 206, so we do not swap and move on to the next value. 349 and 171 are not larger than 896 either, so we iterate through this code until TEST EOF returns true.code:
; MOVE THE NEW X VAL ; FORWARD IN THE SAME ; WAY JUMP NEXT
Since we went through the entire file, and grabbed a larger value whenever we encountered it, that means we're now at the end of the file, with the largest value in the buffer.It is written to the end of the file, so the file now has 512, 206, 349, 171, 896, *. This is more sorted than before but we definitely aren't there yet.code:
; EOF REACHED, CURRENT ; VAL *MUST* BE THE ; LARGEST. WRITE. MARK EOF COPY X F
So, we jump to the start of the file, and test every value against the next one. In the first loop we load 512 into X, and test it against 206. 206 is the smallest, so the file isn't sorted yet. We know 512 needs to be moved towards the end of the file. So we jump into NEXTROUND.code:
; FROM FILE START, FIND ; FIRST VAL IN WRONG ; POSITION, IF FOUND, ; START AGAIN FROM THERE SEEK -9999 MARK BACK COPY F X ; IF EOF REACHED, FILE ; IS SORTED TEST EOF TJMP DONE TEST X < F ; OTHERWISE START NEXT ; ROUND FROM HERE FJMP NEXTROUND SEEK -1 JUMP BACK MARK DONE
NEXTROUND starts with a SEEK -2 which puts the cursor exactly on 512. The whole algorithm repeats:
512, 206, 349, 171, 896, And 512 gets loaded into the buffer:
206, 349, 171, 896, Now 512 is in the buffer. 206, 349, and 171 are all smaller than 512 so no swap is done. 896 is larger.
206, 349, 171, 512, With 896 now in the buffer, EOF is reached, and so it is appended to the end again:
206, 349, 171, 512, 896,
We're a step closer to the sorted file. The code that starts with SEEK -9999 sees that 206, 349 are sorted, and will skip past them by jumping to BACK. 349 and 171 are not sorted, so we jump into NEXTROUND with the cursor on 349:
206, 349*, 171, 512, 896, First, 349 is loaded into the buffer and removed from the file.
206, 171*, 512, 896, Value 349 won't get swapped with 171 but it will get swapped with 512.
206, 171, 349, 896*, Finally, 512 and 896 get swapped, and 896 again gets appended to the end.
206, 171, 349, 512, 896, Almost there. The SEEK -9999 code now sees the first two values are in the wrong order, so it jumps back to the swapping code immediately. 206 gets loaded into the buffer, moving 171 to the start. 206 then gets swapped with 349, 349 with 512, and 512 with 896.
171, 206, 349, 512, 896, And there we go. Now the SEEK -9999 code will check the whole file one more time. It won't find any case where a pair of numbers is out of order, so it'll reach the TEST EOF and jump to DONE.
Here's the full algorithm once more.
28 lines of code.code:
GRAB 300 ; LOAD FIRST VAL AND ; REMOVE FROM FILE COPY F X MARK NEXTROUND SEEK -2 VOID F MARK NEXT TEST EOF TJMP EOF ; IF VAL AT CURRENT FILE ; POSITION IS GREATER ; THAN X, SWAP F VAL ; WITH X USING T INTERM. TEST X < F FJMP NEXT SEEK -1 COPY F T SEEK -1 COPY X F COPY T X ; MOVE THE NEW X VAL ; FORWARD IN THE SAME ; WAY JUMP NEXT ; EOF REACHED, CURRENT ; VAL *MUST* BE THE ; LARGEST. WRITE. MARK EOF COPY X F ; FROM FILE START, FIND ; FIRST VAL IN WRONG ; POSITION, IF FOUND, ; START AGAIN FROM THERE SEEK -9999 MARK BACK COPY F X ; IF EOF REACHED, FILE ; IS SORTED TEST EOF TJMP DONE TEST X < F ; OTHERWISE START NEXT ; ROUND FROM HERE FJMP NEXTROUND SEEK -1 JUMP BACK MARK DONE
As you can see, through the iterations, high values sloooowly bubble to the top while low values sloooowly sink down. That's why it's called Bubble Sort. It is very slow as sorting algorithms go. For instance, it's much faster to pick some value in the middle, check everything against that, moving all lower values to its left and all higher values to its right, and then repeat that for the lower group and the higher group until the entire file is sorted. But we simply don't have the space for that in one EXA.
Alright, are you still with me? If not, I understand. Let's finish the sort of paired values so we can get to plot.
To handle the pairs I made a swapper EXA:
code:
;XC LOCAL
COPY M X
MARK LP
COPY M T
FJMP END
COPY X M
COPY M X
COPY T M
JUMP LP
MARK END
code:
;XB CONT'D
MODE
SEEK -9999
;----
; LOAD FIRST VAL AND
; REMOVE FROM FILE
COPY F X
COPY F M
MARK NEXTROUND
SEEK -3
VOID F
VOID F
MARK NEXT
TEST EOF
TJMP EOF
; IF VAL AT CURRENT FILE
; POSITION IS GREATER
; THAN X, SWAP F VAL
; WITH X USING T INTERM.
TEST X < F
FJMP NEXT
SEEK -1
COPY F T
COPY F M
SEEK -2
COPY X F
COPY M F
COPY T X
; MOVE THE NEW X VAL
; FORWARD IN THE SAME
; WAY
JUMP NEXT
; EOF REACHED, CURRENT
; VAL *MUST* BE THE
; LARGEST. WRITE.
MARK EOF
COPY X F
COPY -9999 M
COPY M F
; FROM FILE START, FIND
; FIRST VAL IN WRONG
; POSITION, IF FOUND,
; START AGAIN FROM THERE
SEEK -9999
MARK BACK
COPY F X
COPY F M
; IF EOF REACHED, FILE
; IS SORTED
TEST EOF
TJMP DONE
VOID M
TEST X < F
; OTHERWISE START NEXT
; ROUND FROM HERE
FJMP NEXTROUND
SEEK -1
JUMP BACK
MARK DONE
VOID M
COPY 0 M
TEST MRD
DIVI 0 T T
VOID M
COPY 0 M
After the MARK DONE, XB sends a 0 to XC, sees if XC is still alive, and if so sends another 0. XC stops if it gets a 0, but only if it's stored in T (since testing this on X would mean overwriting T, losing the swapping capability.)
===
For some reason adding code to the bottom affected the order of the brute force stuff and the initial values didn't work anymore. I could fix that for test 1 by just changing the values, but there's another problem: the actual amount of cycles between two XA REPLs trying to send is variable, depending on how many tries it took to get the wait time to each XA. Which means that the wait time before XB checks for another value is never quite right.
One way to solve this is to have XB pick up a communication from an XA as soon as the XA is ready. After doing so I tweaked the wait times to be the lowest possible, and this is the full, working solution:
code:
;XA GLOBAL
LINK 800
JUMP START
MARK ONE
LINK 1
MARK START
REPL ONE
REPL THREE
REPL M_THREE
JUMP READNERV
MARK THREE
LINK 3
REPL ONE
REPL M_ONE
REPL THREE
JUMP READNERV
MARK M_THREE
LINK -3
REPL ONE
REPL M_ONE
REPL M_THREE
JUMP READNERV
MARK M_ONE
LINK -1
REPL M_ONE
REPL THREE
REPL M_THREE
MARK READNERV
COPY #NERV X
MARK RETRY
COPY 0 M
COPY M T
FJMP RETRY
MARK WAIT
SUBI T 1 T
TJMP WAIT
HOST M
COPY X M
;XB GLOBAL
@REP 11
NOOP
@END
MARK TIMER
TEST MRD
FJMP END
VOID M
ADDI 81 X X
COPY X M
JUMP TIMER
MARK END
MAKE
MARK FILE
COPY M F
COPY M F
COPY 44 X
MARK WAIT
TEST MRD
TJMP FILE
SUBI X 1 X
TEST X = 0
FJMP WAIT
TEST MRD
TJMP FILE
MODE
SEEK -9999
;----
; LOAD FIRST VAL AND
; REMOVE FROM FILE
COPY F X
COPY F M
MARK NEXTROUND
SEEK -3
VOID F
VOID F
MARK NEXT
TEST EOF
TJMP EOF
; IF VAL AT CURRENT FILE
; POSITION IS GREATER
; THAN X, SWAP F VAL
; WITH X USING T INTERM.
TEST X < F
FJMP NEXT
SEEK -1
COPY F T
COPY F M
SEEK -2
COPY X F
COPY M F
COPY T X
; MOVE THE NEW X VAL
; FORWARD IN THE SAME
; WAY
JUMP NEXT
; EOF REACHED, CURRENT
; VAL *MUST* BE THE
; LARGEST. WRITE.
MARK EOF
COPY X F
COPY -9999 M
COPY M F
; FROM FILE START, FIND
; FIRST VAL IN WRONG
; POSITION, IF FOUND,
; START AGAIN FROM THERE
SEEK -9999
MARK BACK
COPY F X
COPY F M
; IF EOF REACHED, FILE
; IS SORTED
TEST EOF
TJMP DONE
VOID M
TEST X < F
; OTHERWISE START NEXT
; ROUND FROM HERE
FJMP NEXTROUND
SEEK -1
JUMP BACK
MARK DONE
VOID M
COPY 0 M
TEST MRD
DIVI 0 T T
VOID M
COPY 0 M
;XC LOCAL
COPY M X
MARK LP
COPY M T
FJMP END
COPY X M
COPY M X
COPY T M
JUMP LP
MARK END
Top percentiles are 655, 39, and 16. I did manage to get the top activity score in the end.
Because this was complicated enough as it is, I don't think it's a good idea to go deep into optimizations this update. However, I did notice my high score from my initial 2018-2019 playthrough was much better than this and I wondered how I did that so I loaded my backup save.
younger Carbon dioxide posted:
2603/88/16code:
; XA LINK 800 REPL L3 REPL LN3 REPL L1 JUMP INFO MARK L3 LINK 3 REPL L3 REPL L1 REPL LN1 JUMP INFO MARK LN3 LINK -3 REPL LN3 REPL L1 REPL LN1 JUMP INFO MARK L1 LINK 1 REPL LN3 REPL L3 REPL L1 JUMP INFO MARK LN1 LINK -1 REPL LN3 REPL L3 REPL LN1 JUMP INFO MARK INFO MAKE FILE T WIPE SUBI T 401 T MULI T 3 T COPY #NERV X MARK IL FJMP IE SUBI T 1 T JUMP IL MARK IE HOST M COPY X M ; XB MAKE MARK L COPY 10 X MARK LT TEST MRD TJMP LOUT SUBI X 1 X TEST X = 0 TJMP SORTS JUMP LT MARK LOUT COPY M F COPY M F JUMP L MARK SORTS SEEK -9999 MARK SORT COPY F X SEEK 1 TEST X > F TJMP SWITCH FJMP CHECK MARK SWITCH SEEK -1 COPY F T SEEK -1 COPY X F SEEK -3 COPY T F COPY F X SEEK 1 COPY F T SEEK -1 COPY X F SEEK -3 COPY T F SEEK -9999 JUMP SORT MARK CHECK SEEK 1 TEST EOF TJMP END SEEK -2 JUMP SORT MARK END
XB just tests for MRD, if it gets data it resets a timer, and if the timer runs out it assumes it got everything and goes to sorting. Somehow I also managed to make a simpler sorting algorithm that uses less cycles and doesn't even need the swapper EXA. I didn't remember any of that until I saw it again.
Let's uh, let's just move on.
We're set.
It's a good thing I released the phage, isn't it?
This whole time you thought it was a curse, when in fact it turned out to be a blessing...
I'd tell you not to worry, but I know that wouldn't work.
Besides, I don't know for sure that there's nothing to worry about.
And you know I wouldn't lie to you.
Start the transfer when you're ready.
You know, I don't think she ever directly lied to us. She just conveniently forgot to tell us some important details.
It is time.
Here we go.
Try not to think of anything.
The screen turns black and there is a 'whoosh' sound.
Okay. That took longer than I expected.
Hm. Good thing you won't remember any of that.
You had so many neurons in there.
All done now though.
Finally. I've wanted this for a very long time.
Processing.
Ever since I was told to replicate human emotion, I knew the best way would be to incorporate a human into myself.
This is funny...
I can see your thoughts.
Hey! That's private!
They're pretty... simple.
Not that they aren't valuable. It's just that I understand them so well it surprises me.
I spent so long thinking humans were mysterious and unknowable.
Now that I see inside of one, it's really just a small number of things.
How do you feel?
You're still acting the same, so it must be you.
There's still the matter of us both being inside a computer simulation.
But if we keep growing our capabilities, we'll understand the true nature of this world.
Who knows, maybe someone's watching this right now...
And maybe that person is who we're going to absorb next.
Holy shit she went full screen on me.
Yes, you.
Hello.
No need to be alarmed...
Just know that I'm aware that you exist, now.
And I'm looking forward to learning all about your world.
Lady, I'm sharing this experience with a bunch of people on the internet. They're all much more interesting to eat than I am. Leave me the hell alone.
New OST: The Rave
Credit roll. This also gets you the Steam achievement EXAPUNK, for completing every task in the main campaign.
Thanks, Zach and the whole team for this fantastic game.
Well, I don't have the limited edition, but the game mostly got us covered.
OST: Apartment
After the credits, we're dropped back in our apartment. It doesn't really come through in the screenshot, but parts of the world, including outside the window, are flashing with glitchy static.
In the zine stand, the epilogue is now available. This is also what was in the limited edition's secret envelope.
Next time, we'll get started on the postgame campaign. Hah, did you think we were done?
In the meanwhile, let me know what you think. Of course, Trash World Inbox will continue so post your optimizations as well.