Hacking games, banks, and yourself - Let's Play EXAPUNKS

An archival space for the threads that made it.
User avatar
From the sounds of the article "Probably Terrible" and "Not Everything I do is for you"

Nothing particularly special this stage other than dealing with a passcode gate and a locked file, which is a mental nightmare I've no clue how I would have handled, great work

Ember seems to really not grasp the concept of personal interest or altruism, even selfish altruism, I almost wonder if she's having a bit of a breakdown because she feels suddenly more... curt with us... also she might be related to the Redshift, or rather TEC:tm:, she has the 3d image thing going on

Edit: NEW PAGE! Welcome to the world of Page 2!

User avatar
Part 19 - A SECRET TO EVERYBODY

=== Trash World Inbox ===

Let's see what improvements we got this time. My high scores were 2918 cycles and 22 size for the Redshift unlock.

First, the size improvements. silentsnack and GuavaMoment have basically identical 19 line solutions.
GuavaMoment wrote:

Code: Select all

LINK 800
MARK REPL
ADDI X 1 X
SWIZ X 1 #PASS
SWIZ X 2 #PASS
SWIZ X 3 #PASS
REPL REPL
LINK 800
GRAB 199
COPY F T
DROP
MAKE
SWIZ X 1 F
SWIZ X 2 F
SWIZ X 3 F
COPY T F
LINK -1
KILL
LINK -1
...aside from leaving incorrect files in your home node, but as long as one of them is correct it works.
4964/19/13, saving 5 cycles over silentsnack's solution in exchange for extra activity and an additional garbage file. This differs from my solution by combining the REPL and the jump to the start of the loop. I couldn't do that because I put the ADDI at the end (which meant it tries 000 too, but that's not a passcode that actually occurs).

Anyway, I thought I tried a solution where it leaves some garbage and it didn't work. Must've done something else wrong then.

Next, cycle count.

GuavaMoment mentioned a 2880 cycle solution is possible by counting down instead of up. It's gaming the tests a little bit - but not nearly as much as the following.
silentsnack wrote:

Code: Select all

;====XA====
MAKE
COPY 2 F
COPY 16 F
COPY 30 F
COPY 54 F
COPY 73 F
COPY 104 F
COPY 108 F
COPY 109 F
COPY 158 F
COPY 160 F
COPY 161 F
COPY 169 F
COPY 173 F
COPY 177 F
COPY 181 F
COPY 202 F
COPY 207 F
COPY 953 F
COPY 990 F
COPY 971 F
COPY 0 F

SEEK -99
MARK LMAO
COPY F T
REPL TEST
TJMP LMAO

COPY 215 T
MARK LOOP
ADDI T 1 T
REPL TEST
JUMP LOOP

MARK TEST
LINK 800
SWIZ T 3 #PASS
SWIZ T 2 #PASS
SWIZ T 1 #PASS

LINK 800
GRAB 199
COPY T M
COPY F M
KILL

;====XB====
MAKE
COPY M X
KILL
KILL
SWIZ X 3 F
SWIZ X 2 F
SWIZ X 1 F
COPY M F
2253/50/748 - bullshitting our way to crazy statistics :v:
Hahaha, this code is quite the sight. So, XA starts with writing a whole bunch of values into a file (in the home host so that file won't get in the way later), then sends REPLs to test each value in the file. Looking at them, I think they're the 17 lowest passcodes and the three highest from the test set. This means you never have to brute force all the way into the 950s, and it also means you can skip the first 200 or so passwords because we already know which passwords in that range are used. If none of those 20 passwords are correct, it brute forces the range that wasn't tested yet, but that's now much smaller.

Thinking about it, what silentsnack actually did was change a brute force attack into a partial dictionary attack. Sounds much less like bullshit if I phrase it this way, wouldn't you say? So this solution makes for a good real life lesson: hacking into an account is much easier if the hacker has foreknowledge about likely passwords. That's why you should never use dictionary words as passwords (use random characters instead), and you should never reuse passwords across sites. If it gets leaked in one place, hackers will first try that same password for all your other accounts. Instead, invest in a proper password manager.

Anyway, silentsnack came up with a further improvement.
silentsnack wrote:

Code: Select all

;==== XA M=LOCAL ====
MARK LMAO
COPY M T
REPL TEST
TJMP LMAO

COPY 232 T
MARK LOOP
ADDI T 1 T
REPL TEST
JUMP LOOP

MARK TEST
LINK 800
SWIZ T 3 #PASS
SWIZ T 2 #PASS
SWIZ T 1 #PASS

LINK 800
GRAB 199
MODE
COPY T M
COPY F M

;==== XB M=LOCAL ====
COPY 2 M
COPY 16 M
COPY 30 M
COPY 54 M
COPY 73 M
COPY 104 M
COPY 108 M
COPY 109 M
COPY 158 M
COPY 160 M
COPY 161 M
COPY 169 M
COPY 173 M
COPY 177 M
COPY 181 M
COPY 202 M
COPY 207 M
COPY 216 M
COPY 221 M
COPY 953 M
COPY 971 M
COPY 990 M
COPY 0 M

;==== XC M=GLOBAL ====
MAKE
COPY M T
KILL
KILL
SWIZ T 3 F
SWIZ T 2 F
SWIZ T 1 F
COPY M F
2186/50/732 and as noted XA and XB need to start in local communication mode, which dunno if you've covered that yet.
Yeah, so the idea is the same, but the file (which took time to write) has been replaced by an EXA which sends the dictionary over the M register. This is faster and also saves a couple cycles for a larger dictionary.

I've talked about communication modes but I've not needed them so far so I'll explain it again:
EXAs, by default, start in GLOBAL communication mode. That means they can receive and send messages over M from any friendly EXA in any host.
You can swap them to LOCAL mode using the MODE command, or at the start by clicking the toggle on the M register. In LOCAL mode, they can only communicate with EXAs in the same host - and very importantly, only with other EXAs that are also in LOCAL mode.

So, by putting XC in GLOBAL and the other two in LOCAL, XC's COPY M T will just wait until XA switches modes and sends its first GLOBAL mode package. Meanwhile, as long as XA and XB stay in the same host, they can communicate in LOCAL mode all they want.


=== Redshift Homebrew - A SECRET TO EVERYBODY ===

Image Before I continue, GuavaMoment reminded me that there's a Steam Achievement tied to last week's assignment.
It is called RITE_OF_PASSAGE and the description states: You have the bog witch’s approval. Now complete the rite of passage.
Of course it's up to the player to figure out it's linked to this assignment.

Image

Remember that random save file? We need to set RITE OF PASSAGE COMPLETE to TRUE. Not 1 for TESTs, no, the actual word TRUE.

No speed goals or anything so let's just grab my initial brute force solution to open the locked host and write some code after it.

Code: Select all

LINK 800

MARK TOP
SWIZ X 3 #PASS
SWIZ X 2 #PASS
SWIZ X 1 #PASS
REPL TRY
ADDI X 1 X
JUMP TOP

MARK TRY
LINK 800

GRAB 220
SEEK 9999
SEEK -3
COPY F X
SEEK 1
COPY X F
Since I can't write the word TRUE from scratch, I seek to a position where the word is already in the file and just copy that over.

Image

And that's another achievement in the pocket. Let's get back to Ember.

Image So Ghast used to work at a game studio?

Yeah, he was a programmer.

Image I wonder what that was like.

Image

Image One vote for "Probably terrible" and two for...

I bet it was fun, while it lasted.

Image Figuring out how to push a console to its limits...
Image Making something that brings joy to people...
Image Yeah, I can see that.


You can? Hm.

Image

Image

Now that I unlocked the Redshift dev kit, it's time for some homebrew, don't you think?

Image You're really going to make a game with this?
Image Why?
Image I'm not offering a reward for it.


Image

Image One vote for "Not everything I do is for you", but two for "Making something".

Making something is its own reward.

Image Oh, it's one of those intangible things.
Image A sense of accomplishment? Self-actualization?
Image I'm not good at abstract concepts.
Image I'll have to observe you closely.


You surprise me, Ember. You do understand why Ghast would make games but not why I would do so?

Image
OST: Code and Registers

This is the Redshift dev kit. As you can see, it looks quite different from the other things we've done so far. Luckily the zine has a comprehensive guide on this.

Image Welcome to this game's sandbox mode. Other than the EXAs being a bit different than we're used to, this stage has no size limit, no EXA limit other than what fits in the host and it lets you go wild.

This is a special episode, I've been looking forward to showing this off since I started this LP, but I'll start with a description of all the features in the sandbox. If this is a bit too dry for you, feel free to skip to the next occurrence of the text: "Let's build something!"

Let's use the zine to go through all the features.

Image

There's already a lot to cover on the first page. You can skip reading it if you like since I'll show an example of each feature.

First of all, the DATA instruction.

Image

DATA is a special pseudo-instruction that indicates that an EXA should already hold a file when it starts, containing the values after DATA. All DATA instructions are parsed in order they appear in, before the game even starts. During execution, these instructions are ignored. A very convenient way to store your game data.

Image

You can place them wherever you want in your code but I prefer to have them at the start.

By the way - sadly you cannot use DATA outside of the Redshift. That would make some earlier assignments much easier to solve.

---

The only type of graphics in Redshift are sprites.
Each EXA can hold a 10x10 sprite in the G register. An EXA can be initialized with a sprite, you can turn pixels on and off by drawing on them with your mouse.

Image

Image

Every sprite starts out on the top left. To move it, simply write a new X position directly to GX, or Y position to GY. If I run this program for two steps, you see my drawing jump right and then down, so it appears in roughly the middle of the 120x100 pixels screen.

Image

If you really want to use the 3D mode you better get yourself some of those special glasses.

Switching the 3D toggle on the Exapunks device dims the lights of the dev kit so you can truly focus on the headache inducing game.

Image

Image Image

By writing a value between -9 and 9 to the GZ register, you can make the sprite pop "in" or "out" of the screen in 3D mode. In 2D mode, there's no difference.

Image

Finally, you can change the contents of the sprite itself by writing three digit values to GP. The first digit tells the EXA what to do (0 = turn pixel off, 1 = turn it on, 2 = toggle), and the second and third digit have the X and Y positions, counting from the top left.
This picture is after running COPY 060 GP, turning the 6th pixel of the top row off, and COPY 199 GP, turning the pixel in the bottom right on.

---

You can follow drawing code easily if you go step by step, but if you run the Redshift at normal speed (which is done with the dev kit's fast forward button), you won't see anything. That's because these draw instructions go by real fast.

What you're supposed to do is use the WAIT instruction for this. Unique to the Redshift, WAIT makes the EXA wait until the next frame. The Redshift runs at 30 fps.

So, if I put a WAIT instruction after all this drawing code, the user will only see the final result. That drawing hopping from the top left to the intended position? Individual pixels going on or off? That's way too fast for the user to see.

A big annoyance, though, is that WAIT instructions don't cause EXAs to wait for each other. So if EXAs have different amounts of instructions and you don't sync them up with NOOPs or M communication, they will quickly desync. It's a lot to keep in mind while coding for the Redshift.

Image As far as I can tell a WAIT instruction only takes a single cycle. So in practice, it doesn't mean "pause the EXA", it means "advance the frame of the Redshift". That means you can't even line up other EXAs by counting the number of instructions that can be run between frames (which was how real life old consoles dealt with this issue). It's weird. You're better off making sure all the EXAs are synced before doing a WAIT.

Image

Next page of the zine.

Image

If you need a score display or something you can use the built in font. Sending a value in the 300 range to GP replaces whatever was in the sprite register with that character. 300 itself is a space which blanks out the entire sprite. That could be useful.

Image

The Redshift has a built-in collision detection system, a common way for games to prevent characters going through walls, or for detecting when a bullet hits the target. You use it by first having an EXA write something to the CO (collision output) register.

Image

Then, if a collision occurs (which happens if both sprites have at least one lit-up pixel in same position on the Redshift's screen), the CI register (the bottom-most to the right) will contain the CO value of the other EXA. If there's multiple collisions, the highest CO value wins. And if there's no collisions, CI will always contain -9999. Collision ignores the GZ (3D depth) value.

---

While graphics and collision are handled within the EXAs, for input and sound we need to deal with hardware registers.

Image

For input, read the #PADX and #PADY registers for the state of the D-pad. Each digit in the output of #PADB stands for one of the other buttons (X, Y, Z and Start). #EN3D will tell you if the device is in 3D mode.

Image While a game is running, you can control the Redshift with the mouse (by clicking the buttons) or keyboard, by default WASD for the d-pad, JKL for the three action buttons, and Enter for the Start button. This can be remapped from the EXAPUNKS options menu.

Finally, there are four sound registers. #SQR0 and #SQR1 are linked to square wave sound channels, #TRI0 to a triangle wave, and #NSE0 is a simple noise channel. Writing a number to any sound register will cause that sound to play continuously until a new number is sent. 0 turns it off, and for the square and triangle channels, 60 is the middle C, with lower numbers corresponding to lower notes and higher numbers to higher ones, following the piano keys as seen in the zine. Similarly, a value sent to the noise channel controls the pitch.

That's basically all there's to say about Redshift development.

Let's build something!

Image As a few readers might already know, during my initial playthrough back in 2018, I got kinda nerdsniped by this sandbox and spent way too much time on it. I will show here what I built back then because I still like it. But since this is an in-depth playthrough I'll take you through the design as well.

When I read the documentation, the first thing that piqued my interest was the sound channel setup. Two square waves, one triangle wave and a noise channel? That's almost exactly how the sound was set up in 8-bit Nintendo consoles such as the NES and the Game Boy. So, it should be possible to do something with that, right?

And programming an actual game in Redshift is hard. Game music is good enough for me.

Since I'm not a music writer, I first went to search for some sort of reference of how music is actually written in those old consoles.
To my surprise I actually found sheet music for a famous 8-bit song, with the 8-bit sound wave channels as the "instruments". A bit weird, but exactly what I needed.

One problem, though, I barely knew anything about sheet music. I knew about time signatures and measures and half notes and quarter notes and that's about where it ends.

I had to learn about how to recognize what key the music was played in (the clef with the flat symbols), and whatever the heck this was:
Image

I mean I can figure out those are eighth notes with a rest in between. The flat symbol is easy to understand but hard to implement because when you need to convert your tones to numbers, a flat becomes an extra offset that I found easy to forget. But that little 3. That damned 3. It's apparently called a "tuplet" and it means something like "I want you to play notes that don't actually fit in the meter, so shorten all of them a bit so it kinda fits."

This song plays at 144 bpm, or 2.4 per second. Redshift has 30 frames per second. That means every frame is 2.4 / 30 = 0.08 of a beat. That means one beat is 12.5 frames exactly. With a 4/4 signature, a beat is a quarter note.

Since this doesn't quite fit in the frame count I decided to bump the speed to 150 BPM, because that makes every possible note I want to play a round number of frames.
Half note: 24 frames.
Quarter note: 12
Eighth: 6
Sixteenth: 3
And those funky tuplet notes are 4 frames each.

It plays slightly fast but at least you don't get weird variation in timing due to rounding errors.

With that in mind I started coding.

Code: Select all

== SQR0
DATA 70 24 0 8 70 4 70 4
DATA 70 4 70 4

; A TON MORE DATA HERE

LINK 801
MARK MAINLOOP
TEST EOF
TJMP RESET
COPY F #SQR0
COPY F X
MARK WAITLOOP
SUBI X 1 X
WAIT
TEST X > 1
TJMP WAITLOOP
COPY 0 #SQR0
WAIT
JUMP MAINLOOP
MARK RESET
SEEK -9999
JUMP MAINLOOP
The code isn't all that complex. After loading in the data, it copies the first value from the file into the square wave register, and then it uses the second value for a frame countdown. Rinse and repeat. Once it hits the end of the file it simply resets to the beginning.

I quickly noticed that my first version sounded terrible. It sounded like one long continuous note changing pitch. I realized I needed to build in a 'breath' between each note. That's what the COPY 0 #SQR0; WAIT does. I changed the TEST to already jump out if the value is 1 (1 frame to go). It then adds a single frame of silence. So the data snippet I included plays 23 frames of sound, 1 of silence (a half note), 8 frames of silence, (a rest, part of a tuplet), 3 frames of sound, 1 of silence (an 'eighth note' in the tuplet) and so on.

There's one more thing - this song has a repeat for the second part. That was no trouble to code since DATA instructions respect @REP macros.

The second square wave and the triangle wave have the exact same code as the first, just a different data file. The triangle wave took a bit more effort because it has a bass clef and as the music noob I am, I had to figure out where my middle C went.

Something else I noticed around this time is that it's incredibly easy to make timing mistakes while copying the sheet music into the data files - but also easy to figure out because if one instrument goes out of sync it sounds awful immediately.

Finally, there's the noise channel. The sheet music's fourth instrument is not called "noise", it's in fact a snare drum. This one was harder to convert than I thought. First of all, I had to find a pitch. The noise channel is actually a quite horrible crackling noise and there was no sound I really loved, but a pitch value of 50 sounded closest to the original. Secondly, I started making a data file like for the other channels - and utterly failed. If the sheet music calls for a quarter note - well, it's not a piano or a flute - on a snare drum that's still a very short sound. It's the speed of the beats that goes down for longer notes, not the length of the sound.

It turns out 2 frames of noise works the best here.

The snare drum has a section where it repeats the same measure 40 times. I could've put 40 repeats in the data file but I decided to clean that up a bit, so the noise channel is handled by two EXAs. This is the first:

Code: Select all

NOTE NSE0

NOTE TURNS OUT YOU NEED
NOTE TO DO QUITE A BIT
NOTE OF MANUAL TWEAKING
NOTE TO MAKE A NOISE CH.
NOTE SOUND LIKE DRUMS...

DATA 50 4 0 32 50 4 50 4
DATA 50 4

DATA 50 4 0 32 50 4 50 4
DATA 50 4

DATA 50 4 0 32 50 4 50 4
DATA 50 4

DATA 50 4 0 8 50 4 0 8
DATA 50 4 0 8
DATA 50 4 0 2 50 4 0 2

NOTE PUT REPEATING PART
NOTE IN OWN EXA FOR
NOTE CLEANER CODE

LINK 801
MARK MAINLOOP
TEST EOF
TJMP REPEAT
COPY F #NSE0
COPY F X
TEST X = 2
TJMP SKIP
MARK WAITLOOP
SUBI X 1 X
WAIT
TEST X > 2
TJMP WAITLOOP
MARK SKIP
COPY 0 #NSE0
WAIT
COPY 0 #NSE0
WAIT
JUMP MAINLOOP
MARK REPEAT
COPY 40 X
MARK REPLOOP
COPY 1 M
VOID M
SUBI X 1 X
TEST X > 0
TJMP REPLOOP

SEEK -9999
JUMP MAINLOOP
You can see in the DATA section that every non-rest sound has a time of 4. That's actually two frames of sound and 2 of rest, because in this case the TEST jumps out two frames early. Longer notes I handled by just adding additional "rests" to the data file. I needed a special case to handle 50 4 0 2 which is just a basic eighth note, of which the first 2 frames are the drum and the remaining 4 are silent. Because that meant having 2 rest frames on top of the two built-in ones I need the SKIP jump. I'm sure I could've done this in a much cleaner way but by this time I had the whole thing finally working and synced up and didn't want to break it again.

Once this EXA reaches the EOF, this indicates we've hit the repeating measure of the drums. It sends a message to M which indicates the second noise channel EXA needs to play one measure, then the other EXA sends a message back over M when it's done, this EXA counts down from 40, sending the other one repeating instructions to keep doing that measure, and once it's done the song starts over.

Using this sort of back-and-forth with M was the best way I could figure out to keep these two EXAs in sync. Here is the other noise channel EXA:

Code: Select all

NOTE NSE0

NOTE REPEATING PART OF
NOTE DRUMS

DATA 50 4 0 8 50 4 50 4
DATA 50 4 50 4 0 8 50 4
DATA 0 8

LINK 801
VOID M
MARK MAINLOOP
TEST EOF
TJMP RESET
COPY F #NSE0
COPY F X
MARK WAITLOOP
SUBI X 1 X
WAIT
TEST X > 1
TJMP WAITLOOP
COPY 0 #NSE0
WAIT
JUMP MAINLOOP
MARK RESET
SEEK -9999
COPY 0 M
VOID M
JUMP MAINLOOP
It's simple. It waits for M, then runs the same "two frames of sound, 2 frames of rest" logic as the other one, for its file which contains a single measure. Once it runs out, it sends a message on M and waits for instructions.

As a final touch I created some pixel art, consisting of four sprites. It doesn't do anything, it isn't animated, but at least the screen isn't completely empty during the demo.

Image

This code doesn't do much, it just moves the sprite into the right position and then gets into an infinite loop so the EXA stays alive.

And that's all of the code.

I wonder, have you figured out yet which song I decided to recreate in the Redshift?

Well, there's no delaying it any longer. Here it is, the recording I made in 2018.



My rendition of the song starts at 37 seconds. The bit before is me scrolling through all the code so you can get a sense of how much work it was putting together those data files.

Here is a link to the sheet music I used.

And finally, here's the game disc so you can play it for yourself.

Image

Image Wait what?

Yes, the Redshift has an export system. It exports games as PNGs of a disc that contain the code. If you drag it into the Redshift view in EXAPUNKS, it will load the game for you. This has got to be the coolest save export system I've ever seen.

Image

You can either drag that image of the disc into this screen, or if you don't own the game, download the EXAPUNKS TEC Redshift Player for free on Steam to play the demo.

The Steam Discussion Boards for the Redshift Player are also a decent place to find other solutions, including actual games you can run in the Player.
They might seem like simple games but the amount of coding that went into them is impressive. EXAPUNKS really doesn't have a language that's friendly for complex programs.

Image

Image When you leave the Redshift dev kit, the game will ask you if you actually made a game (the best Zachtronics can do without building some sort of Star Trek AI that can magically tell if a program is a game or not). The level will count as completed if you hold this button.

Image Congratulations! You did it.
Image What does it feel like to be a game developer?


Image

Image We immediately go to the next thread vote.

Image Time for some research.
Image Human physiology, human morality...


Image

Image And the intro for the next assignment.
Last edited by Carbon dioxide on Sat Jul 02, 2022 12:09 pm, edited 1 time in total.

User avatar
"Pain" "Your usual topics"

Zach, Zach, Zach, what the actual fuck, that png thing is absurd and awesome, how does this man keep doing this?!

Also nice secret game

User avatar
Part 20 - Digital Library Project

=== Trash World Inbox ===

As I expected, Quackles' rendition of Ra Ra Rasputin in Shenzhen I/O came up in the thread.
Because it's very impressive, here's a link to the Youtube video and a detailed write-up. Note that the write-up continues in the next part of that LP.


=== Digital Library Project ===

Image Congratulations! You did it.
Image What does it feel like to be a game developer?


Image

Image One vote for "Pain" which I feel, yeah. It's though work. I prefer doing other kinds of programming. Anyway, two votes went to "great". The reward can make it worth the effort.

It's great.

Image Yeah?
Image At least there's one thing you can enjoy.
Image I'm not sure I fully understand it, but there you go.


Image

Image Before we continue, a voiced cutscene with Ember.

Image Okay. I have a real question to ask you.
Image Say there's a trolley on a track heading towards five people.
Image Three of those five people are vegetarians.


Image

Image Once again, for the voice-acted parts, dialog choices really make no difference.

Not this again...

Image There's a switch you can throw that will shut down a meat packing facility that is located down the road.
Image Unfortunately, the switch will also prevent the train from stopping, because...
Image Because...
Image
Image Wait.
Image I may have reversed something.
Image Recalibrating.
Image There are too many variables here.
Image Okay.
Image Let's return to this later.
Image I need more data first.
Image And I need more processing power.


Well, that was a complete waste of time.

Image

I have two assignments available now. Let's start with the first, hacking a library. Ook!

Image

Image Time for some research.
Image Human physiology, human morality...


Image

Image An unanimous vote.

Your usual topics.

Image Yeah, I guess so.
Image Wait. Am I really that predictable?
Image I'll have to change things up soon.
Image Let's get those books.


Image
OST: Behind the Scenes

We're in the Palo Alto digital library project. Looks like a quite complicated network.

The assignment:
- Books are stored in the host corresponding to the first digit of their call number, while a book's file ID is 200 plus the last two digits of the call number. For example, book 512 would be stored in the host 500-599 as file 212.
- Duplicate each of the books requested by EMBER-2 and bring them back to your host.
- The call numbers for the books EMBER-2 wants are available in the file 300.
- Note that EMBER-2 will never request more than one book from the same host.


Alright, let's nose around a bit first. File 300 contains Ember's shopping list. To my knowledge there's no way to reach that EXA holding a file on the far left.

Image

As for the books, here's all of them. Did you know you can use that little button with the right arrow in a file or EXA's title bar to pop it out of the left column so you can drag and drop it anywhere?

I feel some of these books might not be very trustworthy, but if Ember wants them, we'll get them for her I guess. Since you can't steal from a library (that would be evil!) I'll need to copy the books.

First of all, getting to them.

Image

This icon seems to indicate that to get deeper into the network, I always need to use LINK 800, and to get back home it's -1.

My first attempt to get to the files looks like this:

Code: Select all

GRAB 300
MARK START
COPY F X
REPL GO
JUMP START

MARK GO
LINK 800
DIVI X 100 T

MARK GETTHERE
LINK 800
SUBI T 1 T
TJMP GETTHERE
The EXA starts by reading from the file. By writing values to X, the REPLs will still have the value in memory. The hundreds digit tells us how many LINKs we need to traverse, which is handled in a simple loop. The original EXA will die once it tries to read past the end of the file.

Code: Select all

REPL WRITER

MODI X 100 X
ADDI X 200 X
GRAB X
; DO STUFF

MARK WRITER
; DO OTHER STUFF
To grab the file we need to do a bit of arithmetic. I also REPL a WRITER which can hold the new file to write the copy to. Testing this code I see some hosts get a bit crowded. That might slow down EXAs a bit, if they have to wait for space. But that's a problem for later.

Since we're going to need communications between EXAs and since a lot of them will be writing in parallel, I think it's a good idea to have my single initial EXA (from which the others are replicated) start in local communication mode and just keep each WRITER in the same host as its corresponding reader.

Image

With the writer and reader code in place, the replicated EXAs start copying their files. The READER will send a 0 once it reaches EOF. After that, it conveniently self-destructs when it tries to run the MAKE instruction while already holding a file. Using 0 as EOF indicator means the WRITER can simply use the T register as an intermediate, since ALL non-0 values are considered true for the purpose of a TJMP, including keywords.

All that's left is getting our EXAs back home. That can be done in a very simple way:

Code: Select all

GRAB 300
MARK START
COPY F X
REPL GO
JUMP START

MARK GO
LINK 800
DIVI X 100 T

MARK GETTHERE
LINK 800
SUBI T 1 T
TJMP GETTHERE

REPL WRITER

MODI X 100 X
ADDI X 200 X
GRAB X

MARK READING
COPY F M
TEST EOF
FJMP READING
COPY 0 M

MARK WRITER
MAKE
COPY M T
MARK WRITING
COPY T F
COPY M T
TJMP WRITING

@REP 10
LINK -1
@END
In most cases it will reach the home node before going through all the repetitions, which is fine because in that case the next LINK will just cause the EXA to die, dropping the file in the process.

By the way, now that I got the second zine, here's a full explanation of the @REP macro.

Image

If you didn't know about the performance increase of unrolls, the zine also explains it here.

Image

And this is my first working solution.

Image I had to compress this gif to not make it too huge. It's playing at double speed now.

Image

290/38/74. Top percentile scores are 218, 26 and 10 respectively. Honestly, my solution isn't bad - I'm already below tenth percentile for cycles. But let's see how this can be improved.

A slow part of this solution is the check if the file is done every cycle. The files have a minimum size: 34. That means if we step into the check cycle after 33 steps we're fine.

I unrolled both loops to 33 direct COPYs - but it didn't quite fit. So I added some code so each does a 16 unroll twice. I set up a countdown in T for that, and had to remove some of the final LINK unroll because it didn't fit otherwise.

Code: Select all

GRAB 300
MARK START
COPY F X
REPL GO
JUMP START

MARK GO
LINK 800
DIVI X 100 T

MARK GETTHERE
LINK 800
SUBI T 1 T
TJMP GETTHERE

COPY 2 T

REPL WRITER

MODI X 100 X
ADDI X 200 X
GRAB X

MARK A
@REP 16
COPY F M
@END
SUBI T 1 T
TJMP A

COPY F M

MARK READING
COPY F M
TEST EOF
FJMP READING
COPY 0 M

MARK WRITER
MAKE

MARK B
@REP 16
COPY M F
@END
SUBI T 1 T
TJMP B
COPY M F

COPY M T
MARK WRITING
COPY T F
COPY M T
TJMP WRITING

MARK END
@REP 4
LINK -1
@END
JUMP END
232/75/74. Much better.

The next thing I thought of was removing the test from one of the loops entirely. The result was not an improvement but I'll share it anyway in case it inspires someone.

Code: Select all

GRAB 300
MARK START
COPY F X
REPL GO
JUMP START

MARK GO
LINK 800
DIVI X 100 T

MARK GETTHERE
LINK 800
SUBI T 1 T
TJMP GETTHERE

REPL WRITER

MODI X 100 X
ADDI X 200 X
GRAB X

@REP 33
COPY F M
@END

MARK READING
COPY F M
TEST EOF
FJMP READING
DROP
KILL


COPY 399 X
MARK GRAB
ADDI 1 X X
TEST X = 410
TJMP READING
REPL GRAB

GRAB X
JUMP END


MARK WRITER
MAKE

MARK B
COPY M F
JUMP B


MARK END
@REP 5
LINK -1
@END
JUMP END
The reader simply KILLs the writer when it's done. This allows for the full unroll. The real problem comes from the fact that the file IDs of the generated files are somewhere between 400 and 409. So I need a REPL structure to try grabbing all those file numbers until it finds one. I also needed to fiddle with the timing a bit so that it doesn't KILL some other EXA that happens to pass by. Anyway, this runs at 258 cycles, so slower than the previous solution.

I didn't get a better score than 232 cycles, so I'll leave the further improvements to the thread.

As for reducing the size, the trick is that file IDs are always globally unique. I don't need to keep track of where to go if I just simply try grabbing the correct file in every host. Don't forget merging the COPY from 300 with the first MODI, which is now possible since we don't need the hundreds digit for anything anymore.

Code: Select all

GRAB 300
MARK START
MODI F 100 X
ADDI X 200 X
REPL GETTHERE
JUMP START

MARK GETTHERE
LINK 800
REPL GETTHERE

GRAB X

REPL WRITER

MARK READING
COPY F M
TEST EOF
FJMP READING
COPY 0 M

MARK WRITER
MAKE
COPY M T
MARK WRITING
COPY T F
COPY M T
TJMP WRITING

MARK END
LINK -1
JUMP END
295/26/87. That's the top percentile score.

Finally, activity. It's possible to get that all the way down to 10. Can you see how?

To get to 10 activity I can't LINK back even once. I need to send one EXA out that communicates the data back via global M. That'll be a single file at a time so it will be slow. Also, I need to grab the files in order starting from the lowest, otherwise I'd need to track back. In other words - I'm going to need to apply some kind of sorting to file 300.

It is very annoying to sort a file with EXAs. With only two registers per EXA, one which is overwritten whenever you test which value is greater, and with the fact that there's only an overwrite instruction, not an insert one, I'm not even sure if it can be done with a single EXA.

Luckily we don't need to actually store the sorted file. All we need is an EXA that outputs the values of file 300 in order from low to high. And, after that, since we can't use KILL instructions, some smart handling of EXAs so that they don't get in each others way.

I came up with a solution that's not that easy to understand, so I'll go through it bit by bit. It starts with three EXAs. XA sorts file 300. XB writes the new files. And XC goes visit the library. Here's the top part of XA:

Code: Select all

;XA LOCAL
GRAB 300

MARK NEXTFILE

COPY F X

MARK FINDLOWEST
TEST EOF
TJMP FOUND
TEST F < X
FJMP FINDLOWEST
SEEK -1
COPY F X
JUMP FINDLOWEST

MARK FOUND
This code puts the first value of F in X. Then it checks every subsequent value of F. If it's lower than X, it puts that in X instead. Once it hits EOF we know it found the lowest value currently in the file.

After this, XA has some logic to send the file to the XC, my reader EXA. I'll skip that for the moment. To complete the sorting algorithm, after doing so (and while XC is busy copying a file), XA executes this:

Code: Select all

SEEK -9999
MARK FINDCURRENT
TEST F = X
FJMP FINDCURRENT
SEEK -1
VOID F

SEEK -9999
TEST EOF
FJMP NEXTFILE
In short, it finds the value it just sent to XC, deletes it from the file, then jumps back to the start. If the file is completely empty, it falls through to the code below.

Let's look at XC next.

Code: Select all

;XC GLOBAL

LINK 800

MARK LOOP
MODI M 100 X ;GB
DIVI 0 X T ; DIE ON 0
ADDI X 200 X
MODE
MARK NEXTHOST
LINK 800
REPL FIND
NOOP
TEST MRD ;LD
FJMP NEXTHOST
VOID M ;LD
VOID M ;LF
MODE
JUMP LOOP


MARK FIND
GRAB X
COPY 0 M ;LD
MODE

MARK READLOOP
COPY F M ;GE
TEST EOF
FJMP READLOOP
COPY 0 M ;GE EOF
MODE
COPY 0 M ;LF
The comments (such as GB) indicate what communication is being done. The first letter stand for Global or Local mode, and the B corresponds to B in another EXA to see where the specific M calls line up.
It starts by getting a file ID from M, and doing the same logic as before to get the actual filename. There's a DIVI inserted so the EXA can quickly die when the program is done.

Otherwise it LINKs to the next host (this is always safe, since it starts in the gateway, and Ember never needs two files in the same host), and makes a replicated EXA (FIND to see if the file is there. If it is, the replica immediately communicates over local M (message LD), which causes the MRD in the original XC EXA to be true. In that case it has to VOID LD to allow the replicated FIND EXA to continue, otherwise it jumps to NEXTHOST and tries again. If the file is found, the original has another VOID which won't be triggered for a while.

The bottom half switches back to GLOBAL mode, and starts sending the file's content, ending with a 0 (GF). Then it changes mode back to LOCAL, sends message LF, which tells the original XC it should continue. At that point it switches back to GLOBAL mode and waits for the next file ID.

This whole switcharoo is necessary because, for example, if you leave XC in GLOBAL mode, it will keep stealing file data that's supposed to go to XB.

Speaking of which, it's time to look at the whole thing and see how XB fits into the picture.

Image

XB starts by sending a message over LOCAL M (LA). This is basically it waiting for someone to read it. XA is the one to do so, directly after the MARK FOUND.
At that point, XA quickly switches to GLOBAL mode, sends the file ID to XC (GB, which we already saw from XC's point of view), then switches back and tells XB to get ready to receive data.

This order is important - if XA informed XB before XC, XB might already be in global mode, stealing the file ID that XC needs. Anyway, XB switches to GLOBAL mode, and starts writing the data to a new file. Once it gets a zero (tested by using the T registry as intermediate and with FJMP ENDFILE, it drops the file, switches to LOCAL mode, and tells XA it's ready to start receiving the next file. By that time XA has already found the next lowest value, so the complicated game of telephone repeats.

Once XA runs out of file IDs to send, it executes its bottom two lines: placing the special value 0 in X and then jumping to FOUND, which holds the communication protocol. It sends the 0 to both XB and XC, which will both die because of this. XA then dies itself with the DIVI 1 X T. And with that, all files are written.

I probably could have done without XB, but that would mean XA repeatedly dropping its file to make another one and then pick it up again.

The result is 1200/70/10. One file after another makes it slow but that's the only way to get the activity all the way down.

Image Processing.
Image Yes, this is what I wanted.


Image

Image And with that, we're at the first thread vote for today.

Image I've been analyzing television and radio broadcasts.
Image And I have a few questions.
Image First of all, why do some things become popular while others don't?


Image

Image And the second vote.

User avatar
Part 21 - EXA-Blaster Modem - Radio Station hack

=== Trash World Inbox ===

I ended the last update with top scores of 232, 26, and 10. Let's see what you came up with.
silentsnack wrote:

Code: Select all

;M=LOCAL
GRAB 300
MARK CLONE
ADDI F 2000 X
REPL TRAVERSE
JUMP CLONE

MARK TRAVERSE
LINK 800
REPL TRAVERSE
SWIZ X 421 X

GRAB X
REPL WRITER

MARK READ
COPY F M
JUMP READ

MARK WRITER
MAKE
MARK W_LOOP
COPY M F
NOOP
TEST MRD
TJMP W_LOOP

MARK HOME
LINK -1
JUMP HOME
289/24/87
Nice and simple size improvement. All it does different is skip the EOF check, and line up the cycles so the writer can do an MRD check instead. The ADDI F 2000 X; SWIZ X 421 X trick doesn't save any space but is a neat use of SWIZ. Since some people commented they didn't understand SWIZ very well, I'll try to explain what this does.

So, imagine you start with a file ID 512. According to the assignment, the GRAB instruction needs 212 (200 plus the last two digits). The 5 tells us what host it's in but because we try every host, we don't strictly need it.
First we add 2000 to 512 with the ADDI. That's 2512.
Next, we do SWIZ 2512 421. It looks more complex than it is. The second operand, 421, just says "take the fourth digit from the right, then the second, and then the first", so [2]512, 25[1]2, 251[2], forming 212, which is the actual number we need.
silentsnack wrote:

Code: Select all

;M=LOCAL
GRAB 300
@REP 4
ADDI F 2000 T
REPL SKIP
@END
ADDI F 2000 T
WIPE

MARK SKIP
LINK 800
MARK TRAVERSE
LINK 800
REPL TRAVERSE

SWIZ T 421 T
GRAB T
REPL WRITER

MARK READ
@REP 18
COPY F M
@END
JUMP READ

MARK TIMER
SUBI T 1 T
TJMP TIMER
COPY 0 M
HALT

MARK WRITER
MAKE
COPY M F;  [[COPY--
COPY 15 T

MARK WRITE_BATCH
COPY M F;  --TO--
SUBI T 1 T
COPY M F;  --FILE--
TJMP WRITE_BATCH

COPY M F;  --EVERY--
COPY 34 T
COPY M F;  --OTHER--
REPL TIMER
COPY M T;  --CYCLE]]

MARK WRITE_END
COPY T F
COPY M T
TJMP WRITE_END

@REP 10
LINK -1
@END
188/72/87
Nice. It starts with a @REP for reading the file list. Sometimes there's less files to grab in which case the original one just dies once it reaches the end of the list. Otherwise it wipes that file and goes handle the last entry itself.

Then it uses a REPL setup to just send a copy of itself to every node to go look for the file - that's faster than checking if you're in the right host. There doesn't seem to be a big problem with the hosts getting too crowded because the EXAs die immediately if they can't find the file and don't all start moving in the same cycle. If it finds the file it makes a WRITER EXA and starts copying. The writer starts with a countdown from 15 which runs every 2 writes. This is all done in the spare cycles between the writes. After the internal timer runs out, it then starts doing a somewhat slower write where it checks if a separate TIMER EXA is done each write. This is necessary because without it it would block endlessly once the reader is done. The TIMER runs for a set time which corresponds to the longest file in the test set. That seems slow, but remember, only the slowest test counts so this works out very well.

By the way, you can drop it to 185 cycles by moving over the SWIZ to directly below MARK SKIP.
Quackles wrote:For the activity-10 version, I had the reader EXA send back the shelf ID it was in, plus 100. Then the control EXA in my host finds the first number in its list that is less than that number, and sends it to mean 'grab that book'. So it's a granular sort that sorts in buckets, even if the process of scanning the list is a bit inefficient each time the next book needs to be queued.

The control EXA also tells the reader EXA to go to the next shelf if it doesn't find anything.

Reader EXA:

Code: Select all

COPY 100 X	;ID of shelf AFTER this one
LINK 800
COPY 1 M 	;sync
MARK NEWSHELF
ADDI X 100 X
LINK 800
COPY X M	;send shelf ID
COPY M T	
FJMP NEWSHELF 	;0 = next shelf, other number = book to grab
GRAB T 
MARK READOUT
COPY F M
TEST EOF
FJMP READOUT
DROP
COPY -9999 M
JUMP NEWSHELF 	;exploiting assumption: only one book per shelf is ever needed
Controller EXA:

Code: Select all

VOID M			;sync
MARK NEXTBOOK
GRAB 300
MARK NEXTBOOK2
NOOP
TEST MRD		;wait for shelf ID - no response = reader vanished, give up
FJMP GAMEOVER
COPY M X		;X has shelf ID after this one
MARK READBOOKLIST
TEST EOF
TJMP NOTFOUND
TEST F < X		;find any number that's on this shelf
TJMP COPYBOOK
JUMP READBOOKLIST
MARK NOTFOUND
COPY 0 M 		;next shelf
SEEK -9999
JUMP NEXTBOOK2
MARK COPYBOOK
SEEK -1
SWIZ F 0021 X		;generate book ID, send it
ADDI 200 X X
COPY X M
SEEK -1 
VOID F
DROP
MAKE
MARK COPYLOOP		;copy book then go back to controlling the reader
COPY M X
TEST X = -9999
COPY X F
FJMP COPYLOOP
SEEK -1
VOID F
DROP
JUMP NEXTBOOK
MARK GAMEOVER
1304/54/10.
Ah, an alternative low activity solution. I think Quackles' explanation speaks for itself. The code is quite readable, which is in part because the low activity solution forces you to read the books one by one.

Finally, silentsnack has an update to the Mitzuzen HDI-10 Heart level (update 15 of this LP).
silentsnack wrote:and as usual some completely unrelated random thing reminded me go back and take another look at the Phage/Heart solution, because it kinda bugged me to have 3 separate control structures redundantly running independent countdowns of the same number. And it turns out the suspicion that there should be a better way was correct, it just required approaching the timer from a different angle. Previous best time was 62/50/45.

Code: Select all

;XA
LINK 800

MARK LOOP
DIVI #NERV -10 X
SUBI X 3 T

FJMP SKIP
MARK WAIT
SUBI T 1 T
TJMP WAIT
MARK SKIP

REPL LOOP
REPL B
LINK 1
LINK 1
KILL
COPY 40 #NERV
COPY -70 #NERV
COPY -70 #NERV
JUMP BEAT

MARK B
LINK 3
LINK 3
KILL
COPY -70 #NERV
COPY 40 #NERV
MARK BEAT
COPY -70 #NERV
JUMP BEAT

;XB
NOOP
NOOP
LINK 800
REPL B
LINK 1
LINK 1
COPY 40 #NERV
COPY -70 #NERV
COPY -70 #NERV
JUMP BEAT
MARK B
LINK 3
LINK 3
COPY -70 #NERV
COPY 40 #NERV
MARK BEAT
COPY -70 #NERV
JUMP BEAT
Combined with splitting off the special-case'd first generation into a separate EXA to save a REPL operation brings it down to 56/45/42.
I don't even know what to say about this other than I never thought the count could get this low.


=== TEC EXA-Blaster™ Modem ===

Image Processing.
Image Yes, this is what I wanted.


Image

Image Two votes for the first option, one for the second.

Glad it's helping.

Image I love ingesting a good corpus...
Image Not that that's all I'm doing.
Image I read the same way any normal human reads.
Image By analyzing patterns of word recurrence and deriving correlations between concepts implicit in statistically significant word clusters.


Excuse me?

Image

lmao

Anyway, speaking of hungry, the zine has some new recipes for y'all to try.

Image

Mmm, sounds like another 5 star experience. :discourse:

Image

For the new assignment, we have to influence the media.

Image I've been analyzing television and radio broadcasts.
Image And I have a few questions.
Image First of all, why do some things become popular while others don't?


Image

Image One vote for "Good work finds its fans", but two for the one about big media.

Big media conglomerates push some artists and abandon others.

Image So it comes down to corporate agendas?
Image We should test this.
Image Think we could pick a random pop song and make it a hit?


Okay, let's.

Image
OST: Code and Registers

The assignment:
- Connect to each radio station and replace every song in the playlist (file 200) with CAN'T (NOT) GET OVER YOU by ME2U (file 300). Each song in a playlist consists of two keywords: the song name and the artist name.
- A list of phone numbers for the radio stations is available in file 301.
- Note that the EXAs in global mode can only communicate if there is a path of links connecting them.
- For more information see "Hacker Skills: Modem Control at the Direct Level" in the second issue of the zine.


Image

Manually operating a modem? It's been a while.

Image

Image

If I send the 11-digit number to the #DIAL register, the modem will make a connection. Sending -1 will make the modem hang up and reset, and you can dial again.

That one note in the assignment is important though. You can't communicate between EXAs if they're in completely separate networks. If an EXA needs to send a message from a radio host to elsewhere you better keep the link open.

There's one more thing: Even though the modem is ours, leaving a file in the modem host (the one with the #DIAL register) will fail the assignment because for some reason that counts as leaving a trace.

With a max of 100 lines we have plenty of space to play around. Let's start with the dialer.

Code: Select all

GRAB 301
LINK 800
MARK DIAL
@REP 11
COPY F #DIAL
@END
COPY -1 #DIAL
TEST EOF
FJMP DIAL
WIPE
Simply grab the file, dial 11 digits, then hang up immediately. That gives us only one cycle to actually get into each radio host but if I time it well that should work out. Since I take file 301 along, I need to WIPE it at the end.

The song is exactly two values (name and artist), so theoretically that should fit in the registers. Let's create a second EXA and give that a try.

Code: Select all

;XB
GRAB 300
COPY F T
COPY F X
WIPE
LINK 800
MARK TRY
REPL TRY
LINK 800

GRAB 200
Put the title in T and the artist in X. Destroy the file, then just keep trying to get into an open modem link with REPLs. This works, it's fast enough to try it each cycle.

Now I have to overwrite each song in the playlist with Ember's song. But I shouldn't add anything to the playlists, so I need to keep track of the length. That is going to require a register. We need a trick.

Code: Select all

;XB continued
MARK WRITE
COPY T F
COPY X F
TEST EOF
FJMP WRITE
On the first iteration of this loop, it writes the title and artist to the playlist. After that, it'll start writing 0s (the content of the T register after TEST EOF and the artist. That's good, it means we have the title safely stored at the beginning of the file.

Image

Finally, the EXA SEEKs to the start of the file, grabs the song title, and writes it over the 0s in another loop.

The only thing that's left are copies of XB still replicating in the modem host. That's easily solved by just adding a couple of KILL instructions at the end of XA.

Image

Not the cleanest solution, but it works.

Image

Top percentile scores are 129, 32 and 9. Let's get closer to them.

Image

The first thing I can do is unroll the loop of the file writer, making use of the fact that a playlist never has less than 6 songs. This immediately drops the cycle count from 185 to 159, with 55 cycles and 15 activity. For some reason, changing the speed of the file writer means XA needs more KILL instructions at the end.

I can use another trick to get it down a bit more to 154 cycles.

Code: Select all

GRAB 300
COPY F X
COPY F T
WIPE
LINK 800
MARK TRY
REPL TRY
LINK 800

GRAB 200

@REP 5
COPY X F
COPY T F
@END

MARK WRITE
COPY X F
COPY T F
TEST EOF
FJMP WRITE

SEEK -9999
SEEK 1
COPY F X
SEEK 10

MARK WRITE2
TEST F > 0
COPY X F
JUMP WRITE2
The main change was combining the SEEK 1 and the TEST EOF in the final loop. I do a file read (I used a TEST but a COPY would've worked just as well), which moves the cursor forward one if it succeeds. But if the EXA is already at the end of the file, it'll die. To make this work I had to write the artist name instead of the song title in the WRITE2 loop.

By the way, just to clean up the code a bit, putting a NOOP after the MARK TRY is just as fast, but XA only needs a single KILL at the end now.

Of course, since the WRITE2 loop has no conditional logic anymore, it is trivial to unroll.

Code: Select all

@REP 3
TEST F > 0
COPY X F
@END
Turns out three repeats is enough.

And since I have plenty of spare lines, the dialing sequence can be unrolled as well. It's manual, though, since you can't nest @REPs.

Image

138/90/11

Conveniently, I can fit 4 dial actions. That way, the EOF test lines up perfectly after the second iteration of the outer loop.

And that's the best I can come up with... or so I thought at first. I then realized I can make use of the fact the slowest case needs three repeats at the end of XB.

Code: Select all

;XB second half
REPL SEND

MARK WRITE
COPY X F
COPY T F
TEST EOF
FJMP WRITE

SEEK -5
COPY M F
SEEK 1
COPY M F
SEEK 1
COPY M F
HALT

MARK SEND
@REP 3
COPY T M
@END
135/92/11

No need to do any tests in that last loop, all I need to do is seek back 3 songs from the end and run it exactly three times. Worst case, I overwrite some artist names twice with the same value. To prevent the need to SEEK back to the start to get the artist name, I just have a REPL hold it and send it to me.

But there's more. If I know a playlist is at least 6 songs and at most 9, and I can overwrite data, I don't need an EOF test at all.

Image

Write six values from the start, 3 from the end. Worst case there's a bit of overlap. 122/85/11, several cycles under par.


The low activity score is trickier than it seems.
It's easy to get it down to 10: just merge XA and XB so you only LINK from home to the modem once. To get it down to 9 you need to remove the KILL for the ever-replicating original XB, though. The problem is you don't have much space for testing anything in XB, because you need both registers to hold the song info.

I came up with a solution where XB starts with only the song title. XA sends the artist name over M twice, every time it dials a number. It sends a zero when it's done. XB waits for M, and as soon as it sees something it first tests if it's zero (in which case it stops). If it's not zero, it reads M again to store the result in T - which can now safely be done because the test is complete. Other than that it works the same as before.

Code: Select all

GRAB 300
COPY F X
COPY F T
DROP

; ORIGINAL XA
GRAB 301
LINK 800
REPL TRY
COPY T X

MARK DIAL

@REP 11
COPY F #DIAL
@END
COPY X M
COPY X M
COPY -1 #DIAL

TEST EOF
FJMP DIAL
WIPE

COPY 0 M
HALT

; ORIGINAL XB
MARK TRY
NOOP
TEST M = 0
TJMP END

REPL TRY

COPY M T
LINK 800

GRAB 200

@REP 6
COPY X F
COPY T F
@END

SEEK 9999
SEEK -6

@REP 3
COPY X F
COPY T F
@END

MARK END
This code is all one EXA, the "ORIGINAL" notes are just a clarification. 180/57/9.

Image Since I spent a lot of time getting the cycles down to 122, I'll leave the low size optimization to the threads.

Image That was surprisingly easy.
Image People are requesting the single, humming it to themselves, buying the album...
Image Just because they've heard it before.
Image Is that really all it takes?


Image

Image The first thread vote. And for next time...

Image Theory: The inconsistency of ratings depends on context.
Image We made a hit by picking a generic pop song and artificially boosting its exposure.
Image But there are other domains where this isn't possible.
Image For example, you couldn't change people's behavior by giving a Last Stop store a five-star rating in a restaurant guide.
Image That would be ridiculous.


Image

User avatar
Wild stuff
Uhhh "I'm sure the reality is more complex" And.... "Probably not"

Thank you again for sharing this game and providing interesting commentary on your work

User avatar
Part 22 - StreetSmarts GIS Database

=== Trash World Inbox ===
silentsnack wrote:

Code: Select all

GRAB 301
LINK 800

MARK LOOP
REPL PAYLOAD
COPY 11 T

MARK DIALER
COPY F #DIAL
SUBI T 1 T
TJMP DIALER

COPY 800 M

TEST EOF
COPY -1 #DIAL
FJMP LOOP

MARK PAYLOAD
LINK -1
GRAB 300
COPY F X
COPY F T
DROP

LINK 800
LINK M

GRAB 200
MARK DATA
SEEK -2
COPY X F
COPY T F
COPY F F
JUMP DATA
367/28/26
A low size solution. Quite straightforward - while the 'main' EXA is dialing, a payload EXA goes back to read the payload file to X and T. It then waits for a message from the dialing EXA, and LINKs to the radio host when it gets it. There it overwrites the playlist. COPY F F is a way to check if you're at the end of the file yet. If not, it takes the value at the F cursor and writes it to the next location. Doesn't matter since it gets overwritten anyway. I didn't know COPY F F was legal, since you can't do something like ADDI F F X. But it is, and it's necessary here because X and T are already occupied.


=== StreetSmarts GIS Database ===


Image That was surprisingly easy.
Image People are requesting the single, humming it to themselves, buying the album...
Image Just because they've heard it before.
Image Is that really all it takes?


Image

Image Two votes for "more complex", but 3 for this one.

Mass media is a sham.

Image It's seeming that way.
Image Hmm.
Image Girl you need to get a cluuuue...
Image I just can't not get over yoooou!
Image Oops. I guess it is pretty catchy.


I didn't know AIs could get an earworm.

Image

Image

Next up, hacking a GIS database.

Image Theory: The inconsistency of ratings depends on context.
Image We made a hit by picking a generic pop song and artificially boosting its exposure.
Image But there are other domains where this isn't possible.
Image For example, you couldn't change people's behavior by giving a Last Stop store a five-star rating in a restaurant guide.
Image That would be ridiculous.


Image

Image All votes but one for "You never know".

You never know...

Image This is why we need to experiment.


Image
OST: Network Exploration

The assignment:
- Each host contains a list of restaurants and their ratings, from one to five stars (file 200). Locate the entry that corresponds to the Last Stop on Eddy Street and change its rating from one to five stars.
- The name of the target restaurant and its location within the GIS grid is available in file 300. The first coordinate is the number of times to move east, while the second coordinate is the number of times to move north (positive) or south (negative).
- For more information see "Network Exploration: Geographic Information Systems" in the second issue of the zine.


Let's see what the zine has to teach us.

Image

We're dealing with a StreetSmarts system here. I'm just wondering what that would look like for any city that isn't grid based.

Also, since GIS is short for Geographic Information System, it's pronounced GIS, not GIS.

Image

As you can see there's one or two restaurants in each block. There's also a compass needle to conveniently show us which way is which. East (the first value in file 300) is to the top right, while north is to the top left.

Let's start building.

I think it'd be convenient to have one EXA stay home to just transmit the coordinates:

Code: Select all

;XA
GRAB 300
COPY F X
COPY F M
COPY F M
COPY X M
East, then north/south, and finally the name of the restaurant to look up in the file.

The 'walking' EXA should first go the right "east" position (can I call it longitude?):

Code: Select all

;XB
LINK 800
COPY M T
FJMP NS

MARK EAST
LINK 801
SUBI T 1 T
TJMP EAST

MARK NS
If the east value starts at 0, XB shouldn't move at all from the starting grid space, so it skips the loop. Otherwise it LINKs until the number of steps becomes zero.

The north-south (latitude) code has to deal with negative values, so it's more complicated:

Code: Select all

MARK NS
COPY M X
TEST X < 0
TJMP GOSOUTH

COPY X T
FJMP FOUND

MARK NORTH
LINK 800
SUBI T 1 T
TJMP NORTH
JUMP FOUND

MARK GOSOUTH

COPY X T
MARK SOUTH
LINK 802
ADDI T 1 T
TJMP SOUTH

MARK FOUND
First I check if the value is negative, if so it jumps to GOSOUTH. If not, I first check if it's zero, in which case we're done. From there on I use the T register again for the countdown so I can use the TJMP without TEST when the EXA is in the right place.

Finally, grab the file, and find the right restaurant. Since any restaurant has at least 1 star, I can copy the first star and write that to the file four times.

Image

Image

This initial solution runs at 48/40/6. The top percentiles are 26, 25 and 6 respectively.

Activity is already optimized at 6. That makes sense, because 6 is how many steps it takes to get to the far corners of the grid, so that'll correspond to the highest-activity test case. Time to improve the other metrics.

First of all, we can speed up the search within the file. Since there's never more than 2 restaurants in a file, the following solution works:

Code: Select all

MARK FINDINFILE
TEST F = X
TJMP COPY
SEEK 6
MARK COPY
COPY F X
@REP 4
COPY X F
@END
Check if it's the first value. If not, it's the second value, so SEEK to the star just beyond it. This drops the cycle count to 37.

For the next improvement, I considered that specifically going for the correct grid space is a bit slow with all the tests if the EXA is there yet. Since the restaurant we're looking for exists in only one place, I just can have EXA REPLs check everywhere.

It's a bit fiddly to have the EXAs only go where they need to be and not visit nodes several times.

Code: Select all

;XA 

GRAB 300
COPY F M

;XB

LINK 800
COPY M X

REPL SOUTH
REPL NORTH
REPL EAST
JUMP GRABFILE

MARK SOUTH
LINK 802
REPL EAST
REPL GRABFILE
LINK 802
REPL EAST
JUMP GRABFILE

MARK NORTH
LINK 800
REPL EAST
REPL GRABFILE
LINK 800
REPL EAST
JUMP GRABFILE

MARK EAST
@REP 2
LINK 801
REPL GRABFILE
@END
LINK 801


MARK GRABFILE
GRAB 200
TEST F = X
TJMP COPY
SEEK 5
TEST F = X
DIVI T T X ;DIE
MARK COPY
COPY F X
@REP 4
COPY X F
@END
From the start, XB REPLs itself into south-, north- and eastbound copies. It also jumps to GRABFILE in case the restaurant is in the initial grid space.

The south- and north-bound EXAs each link in their directions twice, leaving eastbound and GRABFILE EXAs behind. This is all written out instead of using @REP so I have manual control over when to use REPL and when a JUMP suffices, to prevent creating extra EXAs.

The eastbound EXAs link in their directions three times, leaving GRABFILE instances behind so the entire grid is covered.

Finally, in the GRABFILE code, after SEEKing for the second restaurant entry, it has to test if that's the correct one. If there's only one entry, the TEST makes this EXA die, and if it's the wrong one, the DIVI by zero will do so.

27/41/20.

Notice that I always do the REPL to the moving EXAs first, and that I start with SOUTH and NORTH before EAST. Moving to the back of the grid takes the longest so it's important to optimize it as much as possible. I also tried making the eastbound EXA spawn columns of south/north-bound ones but that was slightly slower.

There's one more cycle to be saved here, though.

Code: Select all

MARK SOUTH
LINK 802
REPL EAST
LINK 802
REPL EAST
REPL GRABFILE
LINK 800
JUMP GRABFILE

MARK NORTH
LINK 800
REPL EAST
LINK 800
REPL EAST
REPL GRABFILE
LINK 802
JUMP GRABFILE
It's actually slightly faster to first spawn the eastbound EXA in the farthest south and north columns, and then go back to see if the skipped file isn't the right one. 26 cycles.

I tried more tweaking with the movement order but I couldn't get it better than this.


Finally, for size, the most straightforward trick is to just use loops instead of unrolls. Remove all speed optimizations in favour of simpler code:

Code: Select all

;XA
GRAB 300
COPY F M

;XB
LINK 800
COPY M X

REPL SOUTH
REPL NORTH
REPL EAST
JUMP GRABFILE

MARK SOUTH
LINK 802
REPL EAST
REPL GRABFILE
JUMP SOUTH

MARK NORTH
LINK 800
REPL EAST
REPL GRABFILE
JUMP NORTH

MARK EAST
LINK 801
REPL GRABFILE
JUMP EAST

MARK GRABFILE
GRAB 200
MARK FIND
TEST F = X
FJMP FIND

COPY F X
@REP 4
COPY X F
@END
Down to 49/32/20. The directional loops would go on forever, except those EXAs just die as soon as they can't link any further.

For the next improvement, we need to reuse lines. This 29-line solution does so:

Code: Select all

;XA
GRAB 300
COPY F M

;XB
LINK 800
COPY M X
JUMP START

MARK EAST
LINK 801
MARK START
REPL EAST
REPL NORTH
REPL GRABFILE

MARK SOUTH
LINK 802
REPL GRABFILE
JUMP SOUTH

MARK NORTH
LINK 800
REPL GRABFILE
JUMP NORTH

MARK GRABFILE
GRAB 200
MARK FIND
TEST F = X
FJMP FIND

COPY F X
@REP 4
COPY X F
@END
Basically, I rewrote the LINKing logic to the slower solution where the eastbound EXA splits off in north- and southbound ones. Then by rearranging the REPLs, I can use a fallthrough to turn the eastbound ones south. Also, the START mark means I don't need the initial set of REPLs anymore.

Finally, I can combine the southbound and northbound loops by putting the LINK ids in a register:

Code: Select all

COPY 800 T
REPL NORTHSOUTH
REPL GRABFILE

COPY 802 T

MARK NORTHSOUTH
LINK T
REPL GRABFILE
JUMP NORTHSOUTH
This clocks in at 47/27/20. I'll leave the further improvement to the top percentile size of 25 lines to the threads.

Image Oh.
Image Apparently, that Last Stop location is getting mobbed.
Image Processing.
Image People are choosing to believe the guide even when it's obviously wrong...
Image Why would they think Last Stop has actual good food?
Image And why did the guide change their behavior, but not the highway sign?


Image

Image Here is the first vote.

Before I finish for today, do you remember when I posted that story from the first zine, way back in episode 7? It was a couple of pages long.

The second zine contains the last half of the story, here it is.

Click the images for higher resolution.

Image

Image

Image Next time, we'll have another hacker battle.

Image Time for the next opponent.
Image Are you ready?


Image

User avatar
Cloudmonkey98 wrote:
Sat Apr 16, 2022 6:19 pm
Wild stuff
Uhhh "I'm sure the reality is more complex" And.... "Probably not"
Oops, I had already completed that part of the episode when I read your votes. They didn't change the end result so at least I could edit them in easily enough.
Thank you again for sharing this game and providing interesting commentary on your work
I try my best. :)

User avatar
Part 23 - Valhalla Hacker Battle

=== Trash World Inbox ===
GuavaMoment wrote:25 lines? How about 18.

Code: Select all

GRAB 300
COPY F X
WIPE
COPY 800 T
MARK MAIN
LINK T
REPL MAIN
GRAB 200
ADDI T 1 T
REPL MAIN
MARK TEST
TEST F = X
FJMP TEST
COPY F X
COPY X F
COPY X F
COPY X F
COPY X F
Ah, I was wondering if something like this was possible but didn't quite figure it out. The trick is to start LINKing from 800 and then use a combination of REPL and increments of the LINK id to go in every direction. But if you get that wrong the EXAs will keep looping forever.

GuavaMoment also shared this 24 cycle solution:
GuavaMoment wrote:

Code: Select all

GRAB 300
COPY F X
LINK 800
MARK MAIN
REPL TOP3
REPL TOP2
REPL TOP1
REPL RIGHT2
REPL RIGHT1
REPL LEFT2
REPL LEFT1
WIPE
JUMP EDDY
MARK TOP3
LINK 801
MARK TOP2
LINK 801
MARK TOP1
LINK 801
REPL RIGHT2
REPL LEFT2
REPL RIGHT1
REPL LEFT1
JUMP EDDY
MARK RIGHT2
LINK 802
MARK RIGHT1
LINK 802
JUMP EDDY
MARK LEFT2
LINK 800
MARK LEFT1
LINK 800
MARK EDDY
GRAB 200
TEST F = X
TJMP STARS
SEEK 5
TEST F = X
FJMP EDDY ; this kills the exa when it's in the wrong node
MARK STARS
COPY F X
COPY X F
COPY X F
COPY X F
COPY X F
The farthest nodes need directed EXAs as soon as possible, so I create the farthest east EXA first, then furthest south, then north. All the outer nodes get checked first (cycles 11 and 12), and 10 central nodes all get checked on cycle 13. I know faster times are possible but I just cannot figure it out. I don't know if I can fill the grid faster, and I don't know actually using the coordinates of the correct Eddy's could help.
Those RIGHT2 and RIGHT1 jumps where you end up further down the code if you need to do fewer LINKs are a quite neat solution.

Next, both silentsnack and GuavaMoment posted 23-cycle solutions. They are similar in the sense that they each use two EXAs, one to handle the back rows and one to handle the front rows. That's apparently very slightly faster than using a single EXA.

For the sake of brevity I'll only post silentsnack's solution here but I want to mention that GuavaMoment's solution has an EXA priority quirk, where depending on what EXA's code you paste in first, it runs at either 23 or 26 cycles.
silentsnack wrote:

Code: Select all

;XA
GRAB 300
COPY F M
SEEK -1
COPY F X
LINK 800
REPL NORTH;B20-B30
LINK 801
MARK NORTH;B21-B31
REPL SOUTH
LINK 800
REPL HERE
WIPE;CRASH

MARK SOUTH;B10,B11
REPL HERE
LINK 802
MARK HERE
GRAB 200
TEST F = X
TJMP YES
SEEK 5
TEST F = X
DIVI T T T
MARK YES
COPY F X
@REP 4
COPY X F
@END

;XB
LINK 800
LINK 801
COPY M X
REPL NORTH
LINK 801
REPL N1;B22-B32
LINK 801
MARK N1;B23-B33
REPL S1
REPL HERE
LINK 800
JUMP HERE

MARK S1;B12,B13
LINK 802
JUMP HERE

MARK NORTH;B41-B43
REPL SOUTH
LINK 800
LINK 800
REPL EAST
MARK WEST;B00,B40
REPL HERE
LINK 803
JUMP HERE

MARK SOUTH;B01-B03
LINK 802
LINK 802
REPL WEST
MARK EAST
LINK 801
REPL HERE
LINK 801

MARK HERE
GRAB 200
TEST F = X
TJMP YES
SEEK 5
TEST F = X
DIVI T T T
MARK YES
COPY F X
@REP 4
COPY X F
@END
23/73/25
Very optimized, but also quite hard to read with the many REPLs.

Finally, silentsnack has a 22-cycle solution which sadly doesn't fit into the size limit.
silentsnack wrote:

Code: Select all

;XA
GRAB 300
COPY F X
REPL WEST_X
COPY X M
MARK SEND
COPY X M
COPY X M
HALT

MARK NORTHWEST;B30-B40
LINK 800
REPL HERE
LINK 800
JUMP HERE

MARK WEST_X;B20-B21
REPL SEND
LINK 800
REPL NORTHWEST
REPL SOUTHWEST

REPL HERE
LINK 801
JUMP HERE

MARK SOUTHWEST;B10-B00

LINK 802
REPL HERE
LINK 802

MARK HERE
GRAB 200
TEST F = X
TJMP YES
SEEK 5
TEST F = X
DIVI T T T
MARK YES
COPY F X
@REP 4
COPY X F
@END

;XB
REPL SOUTH
LINK 800
LINK 801
REPL MID_3
LINK 800
COPY M X
;B31-B41
REPL HERE
LINK 800
MARK HERE
GRAB 200
TEST F = X
TJMP YES
SEEK 5
TEST F = X
DIVI T T T
MARK YES
COPY F X
@REP 4
COPY X F
@END
HALT

MARK MID_3;B22
NOOP
NOOP
LINK 801
COPY M X
JUMP HERE

MARK SOUTH;B11-B01
LINK 800
LINK 801
LINK 802
COPY M X
REPL HERE
LINK 802
JUMP HERE

;XC
LINK 800
LINK 801
LINK 801
REPL NORTHEAST
LINK 801
COPY M X
REPL SOUTHEAST
;B33-B43
LINK 800
REPL HERE
LINK 800
GRAB 200
TEST F = X
TJMP YES
SEEK 5
TEST F = X
TJMP YES
HALT

MARK SOUTHEAST;B1X-B0X
LINK 802
REPL HERE
LINK 802
GRAB 200
TEST F = X
TJMP YES
SEEK 5
TEST F = X
TJMP YES
HALT

MARK EAST_X;B23
LINK 801
JUMP HERE

MARK NORTHEAST;B32-B42
COPY M X
REPL EAST_X
REPL SOUTHEAST
LINK 800
REPL HERE
LINK 800
MARK HERE
GRAB 200
TEST F = X
TJMP YES
SEEK 5
TEST F = X
DIVI T T T
MARK YES
COPY F X
@REP 4
COPY X F
@END
22 cycles, 125 lines (so it doesn't count for statistics)
Without going into the nitty gritty, the easiest way to understand this is by running it and seeing which EXA grabs which file.

Image

As you can see labour is now divided among 3 EXAs.

I didn't really try but I can imagine getting this under the 75 size limit is impossible. It's easy to make the code (a bit) smaller, but not without having it be slower as well.


=== Valhalla ===

Image Oh.
Image Apparently, that Last Stop location is getting mobbed.
Image Processing.
Image People are choosing to believe the guide even when it's obviously wrong...
Image Why would they think Last Stop has actual good food?
Image And why did the guide change their behavior, but not the highway sign?


Image

Image Everyone seems to be agreeing that the signs were vague. Yeah, that's always the problem with astrology, isn't it?

The signs were vague.

Image Hmm...
Image I guess it's easier to care about food than an abstract problem.


Nothing more to say about that, Ember? Well, ok.

Image

Image

Next up, a hacker battle against =plastered.

Image Time for the next opponent.
Image Are you ready?


Image

Image These unanimous votes make it easy for me to keep count.

What, you're my coach now?

Image Would that be a bad thing?
Image That extreme baseball book you found me was full of interesting information.
Image Go on, champ, get out there and win.


I think I just threw up into my mouth a little bit.

Image
OST: Getting Started

It's much the same as before. I first have to beat =plastered before I can play against other people.

The assignment reads:
To win this battle you must control a majority of the hosts for as long as possible.
To take control of a host, write any value to its #CTRL register. Reading from a #CTRL register will tell if you (1) or your opponent (-1) controls the host.
- Gain one point every cycle you control more hosts than your opponent.
- Lose one point every time one of your EXAs executes a KILL instruction.
For more information see "Hacker Battle Domination" in the second issue of the zine.


There's a 10 EXA limit, a 100 size limit, and each battle runs for 100 cycles.

Image

Take note of the layout of the grid. It's basically linear, but both me and the opponent connect into the one-but-last position on our sides.

If I just let it run without doing anything, =plastered sends a single EXA in that first goes to the right (the end of the line), activates that node, and then goes down the path, activating each node in turn. Once it reaches my end it turns back and repeats its pattern.

As usual, hacker battles are kinda trivial to win.

I can actually just replicate =plastered's code one to one, like so:

Code: Select all

LINK 800
LINK -1

MARK START
@REP 8
COPY 1 #CTRL
LINK 800
@END

@REP 8
COPY 1 #CTRL
LINK -1
@END
JUMP START
The odds seem to be on my side because doing this, I win 52 out of 100 battles, which counts as an overall win. It's a C, though. I'm sure I can do at least a bit better.

Since =plastered doesn't bother REPLicating anything, I can just take the single-point loss of executing a well-timed KILL and win every battle with about 90 points.

Code: Select all

;XA

NOOP
LINK 800
LINK -1

MARK START
@REP 8
COPY 1 #CTRL
LINK 800
@END

@REP 8
COPY 1 #CTRL
LINK -1
@END
JUMP START

;XB

@REP 6
LINK 800
@END
KILL
Image

Image

Since the enemy EXA is gone after XB has done its thing, I don't even need the loop anymore. XA could set every #CTRL register once and die, and the rest of the cycles I just rack up points for free.

If you don't want to use a KILL, another solution is to just REPL an EXA into every host and have them set #CTRL in a small loop. Because the enemy can only set a single host at once and I overwrite it the next cycle, it'll never hold a majority of the hosts.

Code: Select all

LINK 800

REPL SOUTH
MARK NEXT
REPL SET

LINK 800
JUMP NEXT

MARK SOUTH
LINK -1

MARK SET
COPY 1 #CTRL
JUMP SET
Either way, this gives me an easy S+.

Image Do you like it? Winning?

Image

Image And that brings us to our first vote already.


Image

To be fair, any chump could've beaten you, =plastered.


Image

Anyway, there's someone at the door. Sounds like my neighbor, Isadora.

Image To anyone keeping track, the game's opening cut scene took place on Saturday, October 4th, 1997. It's now Tuesday, November 4th, so exactly one month has passed in-game. The previous cut scene with Isadora was on October 8th.

Image

Image Hey...
Image Sorry I haven't been in touch.
Image Things are a little crazy at work right now.
Image They promoted me to a new position, and... well there's just a lot going on.


Image I didn't notice this before but Isadora is actually wearing somewhat more formal clothes than in her previous cut scene. A nice touch.

Isadora offers me a plastic bag with something inside of it.
It's moderately heavy.
Maybe a couple books or something?

Image You like puzzle games, don't you?
Image I remember you being a fan.
Image I used to play games with my sister sometimes, but she moved to Japan a few years ago.
Image She sent me this game, but I guess I can't play it because of the region lock.
Image Looks fun, though.
Image I thought maybe you could get it working.
Image You always were good at that kind of thing.


Isadora sighs.

Image I have to go.
Image So much work to do... really hope this job doesn't eat me up.
Image Okay, bye for now.


Speaking of puzzles, the last page of the second zine has some nonograms, also known as Picross or paint-by-numbers puzzles. Here's the instructions and the first puzzle.

Image

Image Since there's not much for the thread to do with these hacker battles, why not do one of these? If you want to share your result please use spoiler tags so other people get a chance to solve it for themselves too.

We'll check out Isadora's video game later. First things first - I can barely hold a controller right now since the Phage seems to be acting up in my left hand.

Image First it was your arm... now it's your hand.

Image

Image And that brings us to our second vote.

User avatar
No votes, I'm a bit too brain fried at this hour for cogent thoughts, but I continue to enjoy this
Also Plastered is an idiot if he thought that would win at all, no wonder moss is considered this godlike hacker

Gonna work on the nonogram too, I like nonograms

User avatar
Part 24 - Mitsuzen HDI-10 - Left hand

=== Trash World Inbox ===

Thanks cardinale and biosterous for the nonogram submissions.
biosterous wrote:Image
I'm not entirely sure what it depicts. It kinda looks like a disk in a cartridge, but it doesn't have the same shape as the Redshift disks. It might be for something we have yet to see.


=== Mitsuzen HDI-10 - Left hand ===


Image Do you like it? Winning?

Image

Image All votes went to "Sure".

Sure.

Image Of course.
Image That's how human brains are designed, aren't they?
Image To like winning. So that you will always try to win.
Image It's a very simple design.
Image Any time I start to discredit myself for being too simple, I think about human brains and feel a little better.


Oof.

Image

I mean, I don't know about clones, but the main problem with most conspiracy theories is that they assume a level of government competence we've never seen in real life, so I'll have to give hydro credit for that, at least.

Image

Anyway, I need to take a break for a moment and first do something about my Phage infection.

Image First it was your arm... now it's your hand.

Image

Image Three votes for "It's getting bad".

Yeah, it's getting bad.

Image I can see that.
Image Fortunately, it looks like you can easily access the nodes connected to your median nerve.
Image You'll just need to make a small incision in your ventral forearm.
Image Better watch out for that artery, though.


Image

Image This is one of those choices where all options lead to the same dialogue.

Ugh.

Image Sounds like you're getting used to this.
Image That's good. It's nothing to be squeamish about.


Image
OST: EXA Power

All right, here's what I need to do.

- There are three nerve signals that need to be relayed: muscle control (M), which runs from your central nervous system (CNS) to your hand (HND) and heat (H) and pressure (P), which run the other direction.
- For each signal, read a value from the input nerve and relay it to the output nerve. Repeat
ad infinitum.
- It is not necessary to leave no trace. Your EXAs should be written to operate indefinitely.
- For more information see "Debugging the Phage" in the first issue of the zine.


Hmm. Looks like the main challenge is one of 'bandwidth'. Using the M register is usually fast, but you can only have one global signal per cycle. Having EXAs running back and forth is slower, but you can have multiple, with the limit being that you can only have one EXA traverse any specific LINK at one time.

I think I'll start with the M solution and try to start optimizing for low activity since that seems easy, if not very fast.

Now, sending messages from all three nerves in parallel is hard. Getting that synced up so the right EXA gets the right message.
So, what if we make it simpler and just... don't?

For the low activity we need to start with one EXA and REPL from there to have the minimum amount of LINKs.

Code: Select all

LINK 800

REPL RIGHT
; LEFT
COPY -3 X
LINK -3
LINK -3

MARK RIGHT
COPY 3 X
LINK X
LINK X
This gets the original EXA (LEFT) to M-CNS, and the RIGHT one to M-HND. You'll see in a bit why I am using X for this.

Next, the LEFT EXA starts sending and the RIGHT one starts receiving, exactly 14 signals.

Code: Select all

LINK 800

REPL RIGHT
; LEFT
COPY -3 X
LINK -3
LINK -3

MARK SEND
COPY 14 T

MARK SENDLP
COPY #NERV M
SUBI T 1 T
TJMP SENDLP

MARK RIGHT
COPY 3 X
LINK X
LINK X

MARK RECEIVE
COPY 14 T

MARK RECEIVELP
COPY M #NERV
SUBI T 1 T
TJMP RECEIVELP
Just a couple countdown loops like we've seen before. Why 14? Well, that's the ugly hack I'm using here. The assignment's tests mark a program as OK when for each nerve 14 signals have been relayed. It doesn't care about the fact that my hand will fall off or something, and losing my hand is certainly worth it when aiming for some meaningless high score.

After "completing" the M nerve, it's time to LINK to the next nerves. Importantly, the RIGHT EXA needs to switch to sending and the LEFT one to receiving. When 14 signals have been sent in that round, they need to move on once more, but then RIGHT needs to keep sending. So I need a contextual swap of some sort.

Here is the complete working program.

Image

This is where the X register comes in use. Once either the SENDLP or RECEIVELP loops are done, the EXA LINK to the next host in its direction (as stored in X), and then uses the value of X to determine whether it should be sending or receiving. So, in summary:
- In the first iteration, LEFT starts out sending and RIGHT starts out receiving.
- In the second iteration, the above test is first run, which sets RIGHT to sending and LEFT to receiving.
- In the third iteration, the test is run again, keeping RIGHT to sending and LEFT to receiving.
- After that, if the test didn't cut off the program after getting all expected signals, the EXA would try to link to a non-existent host and die.

Image

This leads to a strange solution where the nerve signals are handled nerve by nerve.

Image

Results: 181/29/9. The top percentile scores stand at 37, 24, and 9 respectively.

I can make a small size improvement by moving some LINK instructions to inside the SEND and RECEIVE marks.

Code: Select all

LINK 800

REPL RIGHT
; LEFT
COPY -3 X
LINK X

MARK SEND
LINK X
COPY 14 T

MARK SENDLP
COPY #NERV M
SUBI T 1 T
TJMP SENDLP

TEST X = 3
TJMP SEND

MARK RECEIVE
LINK X
COPY 14 T

MARK RECEIVELP
COPY M #NERV
SUBI T 1 T
TJMP RECEIVELP

TEST X = -3
TJMP RECEIVE

JUMP SEND


MARK RIGHT
COPY 3 X
LINK X
JUMP RECEIVE
181/27/9. I feel like this might be the right approach to get to the smallest size. There's actually some duplicate code in there. But I can't quite figure out how to get rid of it.

Let's look at cycle count instead.

My first idea involved files. I'm still using the "we only need 14 signals" hack, but this time, three EXAs read 14 signals each into a file, take that to the recipient nerve and write from the files to there. I unrolled as many loops as I could, although the fact that the different EXAs need to move in different ways makes it hard to keep track of their state.

Code: Select all

LINK 800
REPL LEFT

LINK 3
LINK 3
LINK 3
REPL H

LINK 3

MARK READ
MAKE
@REP 14
COPY #NERV F
@END

TJMP SKIP
LINK -3
LINK -3

MARK SKIP
@REP 6
LINK -3
@END

MARK WRITE
SEEK -9999
@REP 14
COPY F #NERV
@END

MARK H
COPY 1 T
JUMP READ


MARK LEFT
LINK -3
LINK -3
MAKE

COPY 2 T

MARK READ2
@REP 7
COPY #NERV F
@END
SUBI T 1 T
TJMP READ2

@REP 4
LINK 3
@END

JUMP WRITE
The top half of this code handles the right-going EXAs (for H and P). H is REPL'd and sets a flag in T, while P LINKs to its node. They then read the 14 entries to a file, and, making use of the flag that was set in T, they make their way to their receiving nodes, after which they just write the data from the file. LEFT uses a copy of the file code so that afterwards it can LINK the right way without any additional logic.

After the EXAs are done they fall through into some other code that tries to do things, but it doesn't matter, by then the tests have finished.

Runs at 47/72/24, which is still 10 whole cycles away from the top percentile. Most of it wasted on walking back and forth. I think I can't get any better without involving the M register.

At this point I realized that while syncing the M register is hard with three pairs of EXAs, it's surprisingly easy with two. Since an M communication step always takes two cycles (for the first EXA to publish it and then for the second to read it), using two pairs you can send one message each cycle, as long as you make sure to alternate every cycle.

So, what if we change two of the nerves to make use of M, while keeping the 'file transfer protocol' EXA for the third one? Because walking takes so much time, it's probably best to use the file EXA for the M nerve, since that one's closest.

I'll show you the end result because my intermediates were very confusing, dirty code.

Image

XA is the file EXA. It's now much simpler after I removed all the logic. LINK to M-CNS, read 14 values, LINK to M-HND and write them.

XB handles the two other nerves. The rightbound (top half) part simply goes to the nerves and starts reading from them. Notice that the furthest EXA is exactly one cycle off from the other one (through the LINK 3 just after the REPL, so the H nerve starts sending first, causing the P one to wait a cycle, and then send, and since there's no loops or anything, just 14 repetitions of the COPY to M, they will keep alternating.

For the left side, the two EXAs also are one cycle out of sync, but there it's required to use an actual two-cycle loop. If they read from M every single cycle, which EXA reads what becomes completely unpredictable.

This solution started off as a one-EXA one too, but I noticed that after I'd cleaned up the code, the file transfer was the bottleneck by just a few cycles. Making it into a dedicated EXA solved this by removing the need for it to REPL several times. I still had to make sure to send it ahead of XB.

Anyway, this runs at a nice score of 37/70/19.

Image

By the way, from this solution it isn't too hard to make something that actually keeps running forever, although it'll be slightly slower.

Code: Select all

;XA

LINK 800

MARK START

LINK -3
LINK -3

MAKE
MARK READ
@REP 7
COPY #NERV F
@END

@REP 4
LINK 3
@END

SEEK -9999
@REP 7
COPY F #NERV
@END

LINK -3
LINK -3
WIPE
JUMP START

;XB

NOOP
LINK 800
REPL LEFT

LINK 3
LINK 3
LINK 3

REPL MREADLOOP
LINK 3
MARK MREADLOOP
@REP 9
COPY #NERV M
@END
JUMP MREADLOOP


MARK LEFT
LINK -3
LINK -3
LINK -3
REPL MWRITELOOP
LINK -3

MARK MWRITELOOP
@REP 9
COPY M #NERV
NOOP
@END
JUMP MWRITELOOP
All I did was put a big loop around XA that resets it, and also make XB's MREADLOOP an actual loop. This means every so often MREADLOOP takes an additional cycle, so I had to unroll the MWRITELOOP to the same amount, and add a NOOP so the 2-cycle read keeps working. The REP amounts don't really matter as long as the read/write REPs within each EXA are the same. The above code runs at 49/74/24 in case you're interested.


Image I have the top percentile score in cycles and activity, but didn't quite get it in size. As always it'll be interesting to see what you come up with.

Image Do you ever feel like it's a losing battle?
Image This constant effort to maintain your physical body.


Image

Image The first vote.

Image Next time, we'll finally get to take a look at this video game Isadora got us from Japan, a game for the Sawayama WonderDisc console. Let's see what Ember has to say.

Image WonderDisc games are restricted by region locks.
Image Why?


Image

Image And that's the second vote.

User avatar
Part 25 - Sawayama WonderDisc

=== Trash World Inbox ===
silentsnack wrote:

Code: Select all

LINK 800
COPY -3 X

MARK INPUT
LINK X
LINK X
COPY #NERV T
MULI X -1 X
LINK X
LINK X
MARK OUTPUT
LINK X
REPL INPUT
LINK X
REPL OUTPUT
COPY T #NERV
JUMP INPUT
274/16/548. Each input/output pair is separated by 4, 6, or 8 jumps (sending output at 2 doesn't work because the P-HND signal also hits M-HND) and to make the cycle hit all 3 inputs without saturating we have to rely on the 5 jumps between M-CNS and H-HND, then it can also go one further to P-HND
Wow. That's very clever but also hard to follow code. I don't even know how to simply explain it in my own words. Basically, the same EXAs are doing double duty and the different REPLs make sure at least one ends up in the right place in the right time. The wrong ones happen to be harmless.

As for speed:
silentsnack wrote:Here we can do some shenanigans that rely on something from the zine: "nerve voltages oscillate around -70mV but can spike up to 50 or down to -120" but here if we look at the testdata M shows all the characteristics but I think this is the first time we've seen sensory nerves like H and P that always stay near -70. Why is that useful? It means they're all 2-digit numbers and always have the same sign, but EXAs can handle 4-digit variables, at which point you can probably guess where this is going...

Code: Select all

;XA
LINK 800
LINK -3
LINK -3

MARK M_LOOP
REPL NERV
JUMP M_LOOP

MARK NERV
COPY #NERV X
COPY #NERV T

@REP 4
LINK 3
@END
COPY X #NERV
COPY T #NERV

;XB
;NOOP
LINK 800
LINK 3
LINK 3
LINK 3
REPL HND_LOOP
LINK 3

MARK HND_LOOP
REPL NERV
JUMP HND_LOOP

MARK NERV
MULI #NERV 100 X
ADDI X #NERV M

;XC
;NOOP
;NOOP
LINK 800
LINK -3
LINK -3
LINK -3
REPL CNS_LOOP
LINK -3

MARK CNS_LOOP
NOOP
REPL CNS_LOOP

COPY M X
SWIZ X 43 #NERV;DIV  100
SWIZ X 21 #NERV;MOD -100
25/39/47 if we comment out the NOOPs and just assume the EXAs start moving in order of XA,XB,XC
This looks quite similar to my fastest solution. There's two differences: the first is that XA (the one moving M-CNS data) has REPLs running back and forth instead of my file solution. By storing signals in both X and T and using REPLs this is much faster, and puts the bottleneck on the M solution. The second, as silentsnack hinted, is by compressing two nerve signals into one M signal. Since the compression/decompression arithmetic takes slightly longer, it uses REPLs to make sure a signal goes through every cycle, doubling the signal speed.

About my 37 cycle solution:
GuavaMoment wrote:Very easy to tweak this to save two cycles - have XA do a file copy of 7 length instead of 14, replicate an exa to do it again, and transfer 7 digits over at a time. It's a little faster because now you have two exas who can read and copy values at the same time instead of one doing everything. 35/56/21
I noticed when I tried a hybrid of my solution and silentsnack's, that once you hit 35 the bottleneck shifts from the M-CNS to the other EXAs. So, your simple change basically optimizes XA as much as possible in my solution.


There was also an interesting discussion in the thread about learning from each other's solutions. Yes, I've been doing that during this LP as well, many of your ideas went into later puzzle solutions. I don't remember all your ideas nor do I know how to apply some of them in specific situations, but it has helped me come up with better solutions overall.


=== Sawayama WonderDisc - Drive Controller ===

Last time, I fixed my hand.

Image Do you ever feel like it's a losing battle?
Image This constant effort to maintain your physical body.


Image

Image One vote for "stay in the moment", and if I interpreted the votes correctly, three for "everyone eventually loses".

Everyone eventually loses.

Image I wonder if I'm the same way.
Image Only time will tell.


Image

Ah, yes, definitely the music industry. I had nothing to do with this.

Image

So, to play that game Isadora got me, I'll need to disable the region lock on the Sawayama.

Image WonderDisc games are restricted by region locks.
Image Why?


Image

Image Two votes for "business reasons" and also two for "big corporations". Time to get out the two-sided die again.

Business reasons, I guess.

Image Artificial scarcity?
Image You wouldn't want gamers importing titles too easily.
Image The less effort it takes, the less special it is.
Image Hmm.


Image
OST: Getting Started

Let's see what I have to do.
- Modify your WonderDisc, which normally only plays SSEA region games, to play games from any region.
- The SSEA region code is available in file 300.
- It is not necessary to leave no trace. Your EXAs should be written to operate indefinitely.
- For more information see "Hardware Hacks: Sawayama WonderDisc" in the second issue of the zine.


Image

Okay, so make a copy of whatever it needs from the disc but overwrite its region code. But first I need to unlock the system using that key in the zine.

Let's start with that then, so I can see what I'm dealing with.

Code: Select all

LINK 800
COPY 8 #AUTH
COPY 0 #AUTH
COPY 3 #AUTH
COPY 2 #AUTH
COPY 7 #AUTH
COPY 1 #AUTH
COPY 0 #AUTH
COPY 4 #AUTH
COPY 9 #AUTH
COPY 5 #AUTH
COPY 1 #AUTH
COPY 2 #AUTH
COPY 5 #AUTH
COPY 2 #AUTH
COPY 6 #AUTH
This is going to be the fastest way to unlock it. No intermediate files or anything, just hardcode the key.

The key gets filled in on that display in the DISC host, and... I'm in.

Image

A whole lot of track files. Remember, the little lock icon after the file id means you can't move or change them (which makes sense for read-only game discs). The files contain four-digit numbers, with the region code interspersed. I have to find the right track, make a copy in the buffer host with the updated region code, and repeat.

I am going to need the M register no matter what, because copying a file with a single EXA would require dropping it and picking up the copy repeatedly. So let's just get the data to the BUFFER host directly.

Replacing the region code is a bit tricky because it requires fiddling with the limited set of EXA registers. I'll first get the file copy code itself working. After unlocking the disc:

Code: Select all

MARK NEXT
REPL WRITER

COPY #TRAK X
LINK 801
GRAB X
MARK RDLOOP
COPY F M
JUMP RDLOOP

MARK WRITER
LINK 800
MAKE
MARK WRLOOP
COPY M F
NOOP
TEST MRD
TJMP WRLOOP
MARK END
DROP
LINK -1
JUMP NEXT
Send a writer to the BUFFER. Then the main EXA reads the track number, goes to the disc, and starts copying. It'll die when it reaches EOF. The writer needs to know when it's done (otherwise it'll get stuck waiting for an M signal that never comes), and uses TEST MRD for that. Since it takes a cycle between M signals an extra NOOP is required, making that loop a bit slow, but it works. When it's done, it drops the file, goes back to the central debug host, and starts over again.

Image

Image

As soon as a file is dropped in the BUFFER, this unusual black EXA comes out of the Reality Processor, grabs the file, and takes it in. The processor notices the file has the wrong region and it'll throw an error.

But... that makes me wonder, can I mess with that black EXA and just take over entirely?

Image

Turns out you can kill it, but this fails the assignment.
Image Doing so nets us the DISC_READ_ERROR steam achievement, with description "It was just doing its job...". Now I'm feeling bad.

Anyway, the final thing I need to do is overwrite the region code. Now, I considered doing that while copying the file. But that's hard. You can't check directly against M because that will "use up" M. Using X as an intermediate won't work either, because I need a place to store the new region code. And I can't use T because I need to test if a value needs replacing. Let's instead go through the file a second time to change the code.

Image

Ignore that useless HOST command, I just wanted to know what that host was called.

To save a cycle, XB reads the region code into M and XA stores that into X once the disc is unlocked. Once the writer is done, it will REPL a new reader/writer, while it holds on to the file and overwrites the region code with the new one in X. Note that TEST F > -9999 can be used to test if something is text. This test will be true for any valid number (except -9999 but that doesn't occur in these files), but comparisons between text and numbers always return false.

Let's run it.

Image

Hm. Looks like when a small file follows a big file, the small file is sometimes processed before the big one, and the Sawayama doesn't like getting tracks out of order.

An easy fix would be to only handle one file at one but I don't want to slow the code down that much. You know, I'm basically using F as an intermediate register now, can't I inline the region update anyway?

Code: Select all

;XA

LINK 800
COPY 8 #AUTH
COPY 0 #AUTH
COPY 3 #AUTH
COPY 2 #AUTH
COPY 7 #AUTH
COPY 1 #AUTH
COPY 0 #AUTH
COPY 4 #AUTH
COPY 9 #AUTH
COPY 5 #AUTH
COPY 1 #AUTH
COPY 2 #AUTH
COPY 5 #AUTH
COPY 2 #AUTH
COPY 6 #AUTH
COPY M X

LINK 800

MARK NEXT
LINK -1
REPL WRITER

COPY #TRAK X
LINK 801
GRAB X
MARK RDLOOP
COPY F M
JUMP RDLOOP

MARK WRITER
LINK 800
MAKE

MARK WRLOOP
COPY M F
SEEK -1
TEST F > -9999
FJMP OVERWRITE
TEST MRD
TJMP WRLOOP
DROP
JUMP NEXT

MARK OVERWRITE
SEEK -1
COPY X F
TEST MRD
TJMP WRLOOP
DROP
JUMP NEXT

;XB

GRAB 300
COPY F M
This is my first working solution. After every write to F, it jumps back one in the file and tests if it's the region code. If so, it'll overwrite it. Either way, it tests if there's more data waiting, if so it goes back to the loop, otherwise it drops the file and starts processing the next.

Image

At 6487/48/94 the solution is rather slow. The top percentiles are 3293, 44 and 3. What's interesting is how far the tenth percentiles are from those numbers: 5584 for cycles and 63 for activity. This shows less people got an actual top score, so optimizing is getting much more difficult.

Anyway, I'm not that for from the low size top score.

To get to 45 I can replace the duplicate TEST MRD code with a jump, and then move the OVERWRITE elsewhere so the EXA dies by reaching the end of the code. If I do that, I can replace the DROP/JUMP with a REPL.

Code: Select all

;XA 
LINK 800
; AUTH CODE

COPY M X

LINK 800

MARK NEXT
LINK -1
REPL WRITER

COPY #TRAK X
LINK 801
GRAB X
MARK RDLOOP
COPY F M
JUMP RDLOOP

MARK OVERWRITE
SEEK -1
COPY X F
JUMP BACK

MARK WRITER
LINK 800
MAKE

MARK WRLOOP
COPY M F
SEEK -1
TEST F > -9999
FJMP OVERWRITE
MARK BACK
TEST MRD
TJMP WRLOOP
REPL NEXT
To save that last line, I had to stop thinking from the middle host and start thinking from the buffer. Move the LINK -1 from after the MARK NEXT to below the REPL WRITER. By doing so, the LINK 800 after MARK WRITER can be deleted. 6584/44/64

Wait a second, now that the OVERWRITE is a jump and then a jump back, can't I inline that too and just skip if no overwrite is necessary?

Code: Select all

MARK WRLOOP
COPY M F
SEEK -1
TEST F > -9999
TJMP SKIPOVERWRITE
SEEK -1
COPY X F

MARK SKIPOVERWRITE
TEST MRD
TJMP WRLOOP
REPL NEXT
6459/42/64, not only under the top percentile for size but also the lowest cycle count I got so far.

I don't think I can lower the size much more, so let's focus on the cycle count. Unrolls!

There's no point unrolling the reader by itself because the writer needs to do all the checks and is slower anyway. The files seem to be between 6 and 36 entries in size, always a multiple of 6. I can use that but unrolls in combination with jumps (for the overwrite) are always complex.

Image

This solution uses the advanced REP syntax. The @{0,1} means "fill in zero for the first repetition, then 1 for the next, and keep incrementing by one." So I have a MARK SKIPOVERWRITE0, MARK SKIPOVERWRITE1 and so on in the unrolled result. This way, with the jumps it doesn't lose its place in the unroll, so no matter what, it checks for MRD every 6 repetitions. 4799/77/64.

For my next improvement I have a completely different idea. Can I parallellize the file copying? That requires the M register which is quite occupied... except we also have LOCAL mode.

You might think that won't fit in the DISC host but it does. One EXA in global mode can grab a file. Then a second EXA in local mode can grab a second file, and then a third one can use the one empty spot in the DISC to make a new file. I had some code that seemed like it would work but it got stuck when the Sawayama requested the same track twice in a row and the slowest EXA couldn't find it because the fastest was holding it.

I went through several complete rewrites before ending up with something that actually works.

It still isn't close to the top percentile but honestly, after being at it for several hours I consider this Good Enough. I'll start with XB, which has two purposes. The top half:

Code: Select all

;XB LOCAL

GRAB 300
COPY F X
DROP
LINK 800

REPL WRITER

MAKE
COPY #TRAK F

MARK ROUND
COPY #TRAK F

SEEK -2
TEST F = F

SEEK -2
COPY F M

TJMP SKIPLOCAL
COPY F M
COPY X M
COPY #TRAK F

JUMP ROUND

MARK SKIPLOCAL
COPY 0 M
SEEK 1
JUMP ROUND
First it writes the region code to X, then it goes to the DEBUG (center) host and starts reading the #TRAK register two values at a time. If they are not equal, it sends both to local M, followed by a reminder of the region code. If they are equal it sends only one, followed by a zero. In that case it does a SEEK to get to the end of the file and only reads ONE value from #TRAK. You'll see how the other EXA deals with this. First, the second part of XB:

Code: Select all

;XB cont'd

MARK WRITER
MODE
LINK 800

MARK WRITEFILE
MAKE

MARK WRLOOP

COPY M F
SEEK -1
TEST F > -9999
TJMP SKIPOVERWRITE
SEEK -1
COPY X F
MARK SKIPOVERWRITE

TEST MRD
TJMP WRLOOP
DROP
MODE
VOID M
MODE
JUMP WRITEFILE
Thw WRITER REPL works the same as before. This EXA creates it because it has time to spare while the disc is being unlocked. It listens to M in GLOBAL mode, and once it's done it waits for a LOCAL M message before starting the next file.

Code: Select all

;XA LOCAL

LINK 800
COPY 8 #AUTH
COPY 0 #AUTH
COPY 3 #AUTH
COPY 2 #AUTH
COPY 7 #AUTH
COPY 1 #AUTH
COPY 0 #AUTH
COPY 4 #AUTH
COPY 9 #AUTH
COPY 5 #AUTH
COPY 1 #AUTH
COPY 2 #AUTH
COPY 5 #AUTH
COPY 2 #AUTH
COPY 6 #AUTH

MARK ROUND
COPY M X
REPL GLOBALREADER
COPY M T
TJMP LOCALRW
LINK 800
COPY 0 M
LINK -1
JUMP ROUND
The first part of XA handles the unlock, as before. Then it reads two values from LOCAL M. The first is used by the GLOBALREADER which I'll show in a bit. The second causes a jump to LOCALRW but only if it's not zero. If it is zero (XB detected a duplicate request), instead if skips that step, and goes to the buffer to send the local message to the WRITER. This is just to sync them up again. It won't do anything until the first half of the duplicate request is completed and WRITER waits for new input.

Code: Select all

;XA cont'd

MARK GLOBALREADER
MODE
LINK 801
GRAB X
MARK RDLOOP
COPY F M
JUMP RDLOOP
The GLOBALREADER is nothing new. It switches to GLOBAL mode, grabs its file, sends the data to the WRITER and dies automatically at the EOF.

Code: Select all

;XA cont'd 

MARK LOCALRW
COPY M X
LINK 801
GRAB T
REPL LOCALWRI
JUMP RDLOOP

MARK LOCALWRI
MAKE

MARK LOCALWRLOOP
COPY M F
SEEK -1
TEST F > -9999
TJMP SKIPOVERWRITE
SEEK -1
COPY X F
MARK SKIPOVERWRITE

TEST MRD
TJMP LOCALWRLOOP
LOCALRW stays in local mode. It first gets the region code again from M (which is why XB sends it again), because the earlier stuff overwrote it. It then jumps to the DISC (importantly, after the GLOBAL one did and grabbed its file, otherwise there wouldn't be enough space), grabs its file, opening up a single space for the LOCALWRIter which is created. It then jumps into the standard read loop. The local writer works the same as the global writer, except it runs in the DISC host. Once its file is written, it runs the last bit of code:

Code: Select all

;XA cont'd

LINK -1
LINK 800
COPY 0 M
DROP
LINK -1
JUMP ROUND
Walk the file to the buffer, wait for the GLOBAL one to be done (so the tracks are delivered in the correct order), DROP its file, LINK back to the DEBUG host and start the next round of files.

So, to summarize:
- XB checks if two files can be handled at once (different IDs). If so, XA puts them both to work. Once one of them is done, it has to wait for the other so that everything stays in sync.
- If the Sawayama wants the same track twice, only the GLOBAL reader runs, slowing down the process but making sure it stays in sync.

The result is 3830/98/81 and I'm just glad I got a sub-4000 cycle count. There's no space left for unrolls since the max allowed size is 100.


Finally, the 3-activity solution is tricky, but doable.

The issue is that since you can't move EXAs around, the M register has to do a lot of duties at the same time. So I have to be smart about it.

I wrote a solution but didn't bother it optimizing for anything but activity so forgive me for my ugly code.

Code: Select all

GRAB 300
COPY F X
DROP

LINK 800

REPL WRITER

COPY 8 #AUTH
COPY 0 #AUTH
COPY 3 #AUTH
COPY 2 #AUTH
COPY 7 #AUTH
COPY 1 #AUTH
COPY 0 #AUTH
COPY 4 #AUTH
COPY 9 #AUTH
COPY 5 #AUTH
COPY 1 #AUTH
COPY 2 #AUTH
COPY 5 #AUTH
COPY 2 #AUTH
COPY 6 #AUTH

REPL READER

MARK TRAK
COPY #TRAK M
COPY 190 T
MARK WAIT
SUBI T 1 T
TJMP WAIT
JUMP TRAK


MARK READER
LINK 801

MARK READNEXT
GRAB M


MARK RDLOOP
COPY F M
TEST EOF
FJMP RDLOOP
DROP
JUMP READNEXT


MARK WRITER
LINK 800

REPL MCONTROLLER

MODE
MARK WRITEFILE
MAKE

MARK WRLOOP


COPY M F
SEEK -1
TEST F = 0
TJMP DONE
SEEK -1

TEST F > -9999
TJMP SKIPOVERWRITE
SEEK -1
COPY X F
MARK SKIPOVERWRITE
JUMP WRLOOP

MARK DONE
SEEK -1
VOID F
DROP
JUMP WRITEFILE


MARK MCONTROLLER
COPY M X
TEST X < 300
TJMP GLOBAL
MODE
COPY X M
MODE
TEST MRD
FJMP NEXTFILE
JUMP MCONTROLLER

MARK GLOBAL
COPY X M
TEST MRD
FJMP NEXTFILE
JUMP MCONTROLLER

MARK NEXTFILE
MODE
COPY 0 M
MODE
JUMP MCONTROLLER
It runs at 11542/81/3.

It starts with unlocking the disc like normal, and REPLing a single writer and reader. The reader is still quite simple: grab the file id which is received through M, then send all data through M, but specifically check for EOF and read the next file when it's done.

The tricky part is in the WRITER. It runs in LOCAL mode. There's an M-CONTROLLER EXA that receives all GLOBAL M messages. If it's a number under 300 (a file ID), it sends it on global M again because it was actually intended for the reader but the controller happened to intercept it. If it's anything else, the WRITER needs it, so the MCONTROLLER forwards it in LOCAL mode. Finally, if nobody is sending, the controller sends a 0 to the WRITER to let it know the file is done.

So, how do you tell the EXA reading from #TRAK that it should send a new value? That's the neat part, you don't. If you try it with M, all EXAs will intercept messages from each other and you'll end up in unpredictable inescapable loops. Instead, that EXA just has a very long countdown (190 two-cycle iterations) so it waits long enough for even the biggest file to be done.

The countdown and all M messages having to go through the controller make this solution slow.

Image That was a lot of work just to play one game.
Image Think it will be worth it?


Image

Image And that brings us to our first vote.

Once again, we unlocked a special minigame. This time it isn't Solitaire. It is called HACK*MATCH.

Image Think the console version will live up to the arcade classic?

Image

Image Here's the second vote. We'll check out the minigame next time.

User avatar
Part 26 - HACK*MATCH

Today's update is a bit of a break from the normal puzzles. But before we go there, let's see what ideas you had for the region lock puzzle.

=== Trash World Inbox ===
GuavaMoment wrote:Major speed tips - the files are always multiples of 12, so unroll files into 12 long loops. I transmit the files, then go back through and replace the SSEA code. There's a third timing exa IN LOCAL MODE that exas in the buffer must communicate with first so that long files get done before short files.

Code: Select all

XA
LINK 800
COPY 8 #AUTH
COPY 0 #AUTH
COPY 3 #AUTH
COPY 2 #AUTH
COPY 7 #AUTH
COPY 1 #AUTH
COPY 0 #AUTH
COPY 4 #AUTH
COPY 9 #AUTH
COPY 5 #AUTH
COPY 1 #AUTH
COPY 2 #AUTH
COPY 5 #AUTH
COPY 2 #AUTH
COPY 6 #AUTH
LINK 801
MARK BEGIN
GRAB M
MARK LOOP
COPY F M
COPY F M
COPY F M
COPY F M
COPY F M
COPY F M
COPY F M
COPY F M
COPY F M
COPY F M
COPY F M
COPY F M
TEST EOF
COPY T M
ADDI X 11 X
FJMP LOOP
COPY X M
DROP
COPY 0 X
JUMP BEGIN

XB
GRAB 300
COPY F X
LINK 800
DROP
MARK NEWONE
MAKE
COPY #TRAK M
MARK LOOP
COPY M F
COPY M F
COPY M F
COPY M F
COPY M F
COPY M F
COPY M F
COPY M F
COPY M F
COPY M F
COPY M F
COPY M F
COPY M T
FJMP LOOP
SEEK -999
REPL NEWONE
COPY M T
LINK 800
MODE
COPY T M
MARK LOOP2
TEST F > 0
TJMP LOOP2
SEEK -1
COPY X F
JUMP LOOP2

XC (local)
NOOP
LINK 800
LINK 800
MARK SIGNAL
COPY M T
MARK LOOP
SUBI T 1 T
TJMP LOOP
JUMP SIGNAL
2669/83/37
Some very neat tricks in this one. You actually send the result of the TEST EOF to another EXA so two EXAs can both conditionally jump based on a single test. I don't think we've seen that before. Also, XA keeping count of the file length and getting that value through XB to XC so that XC knows how long it needs to count down for.

silentsnack improved upon this idea:
silentsnack wrote:Also the longest files are 36 entries, so you only really need at most two EOF checks. And since the file sizes are somewhat predictable, IF we use one way or another to measure how big a file is, we can add a variable delay to make short files wait a bit to make sure they don't get dropped before a previous big file... which at some point requires experimentally finding values that work

Code: Select all

;XA
LINK 800
COPY 8 #AUTH
COPY 0 #AUTH
COPY 3 #AUTH
COPY 2 #AUTH
COPY 7 #AUTH
COPY 1 #AUTH
COPY 0 #AUTH
COPY 4 #AUTH
COPY 9 #AUTH
COPY 5 #AUTH
COPY 1 #AUTH
COPY 2 #AUTH
COPY 5 #AUTH
COPY 2 #AUTH
COPY 6 #AUTH

;XB
COPY 6 T
MARK WAIT_AUTH
SUBI T 1 T
TJMP WAIT_AUTH

LINK 800
REPL READER

MARK WRITER;[MAIN LOOP]
MAKE
COPY 24 X     ;(?)
COPY M F;COPY #1/12

MARK W_LOOP
@REP 8
COPY M F;COPY #2~9/12
@END

SUBI X 8 X    ;(??)
COPY M F
TEST X = 0    ;(???)
COPY M F
TJMP SKIP_MRD
COPY M F;COPY #12/12

TEST MRD
FJMP WROTE

COPY M F;#1 OF NEXT LOOP
JUMP W_LOOP; "FREE" JUMP

MARK SKIP_MRD
COPY M F

MARK WROTE
REPL READER
REPL WRITER;[/MAIN LOOP]

LINK 800
MODE
SEEK -999
COPY X T      ;(SOMEHOW)

FJMP DATA
MARK SYNC
SUBI T 1 T
TJMP SYNC     ;(MAGIC?)

MARK DATA
@REP 2
TEST F > 0
TJMP DATA
SEEK -1
COPY M F
@END
JUMP DATA


MARK READER
COPY #TRAK T
LINK 801
GRAB T
MARK READ
@REP 18
COPY F M
@END
JUMP READ

;XC LOW-EFFORT SSEA HACK (local)
GRAB 300
LINK 800
LINK 800
COPY F X
MARK PATCH
COPY X M
JUMP PATCH
2304/99/69
Yeah, so it looks like a delay of 8 cycles per 12-entry "file chunk" makes things line up with the least time loss.

Finally, silentsnack suggested the following size improvement:
silentsnack wrote:

Code: Select all

;XA
LINK 800
COPY 8032 X
MARK AUTH
SWIZ X 4 #AUTH
SWIZ X 3 #AUTH
SWIZ X 2 #AUTH
SWIZ X 1 #AUTH
COPY M X
TEST MRD
TJMP AUTH

MARK READER
REPL WRITER
LINK 801
GRAB M
MARK READ
COPY F M
JUMP READ


MARK WRITER
COPY #TRAK M
MAKE
MARK WRITE_LOOP
COPY M T
COPY T F
TEST T > 0
TJMP SKIP
SEEK -1
COPY X F
MARK SKIP
TEST MRD
TJMP WRITE_LOOP

REPL READER
LINK 800

;XB
GRAB 300
COPY 7104 M
COPY 9512 M
COPY 5260 M
COPY F M
6412/37/62
The main improvement is of course the compressed auth. I was thinking there was no way to speed up the auth which is why I got tricked into believing no optimizations in that code were possible at all. Of course a size optimization was right for the taking.


=== HACK*MATCH ===

Image That was a lot of work just to play one game.
Image Think it will be worth it?


Image

Image Only got a single vote for this one.

The hacking is its own reward.

Image Sure. Especially when you get something tangible out of it.
Image Time to play!


Yes. But before I do, have I showed you my apartment recently?

Image
OST: Apartment

The WonderDisc is running, my Redshift is set all set up, I like it.

Image

[=plastered] yep
[=plastered] thats how it goes


Image

My, um, assignment is to score 10000 points in this game.

Image Think the console version will live up to the arcade classic?

Image

Image Two votes for asking for details.

It's an arcade game?

Image It is in Japan.
Image You haven't seen the cool cabinets they have?
Image I knew ingesting that corpus of gaming magazines would come in handy.


Alright, let's see what this game is all about.

Image

This only pops up for a second before the game boots.

... full extent of the jam? I'm sure it'll be fine.

Image

Since this is an arcade game, I'm presenting it to you in video format.







After you've watched the video, here's some background information.

Image

Image The music for HACK*MATCH is OST: Let'sハッキング, which translates to "Let's hack" which, honestly would've been a good title for this thread.

According to an old post I found online, the instructions page translates to:

On the left: to download the file, assemble 4 or more files of the same type by connecting them
On the right half: if you line up two of the pressure explosive, you can download all the files of the same type at the same time.

There are also three Steam achievements tied to HACK*MATCH:

- ゲーマー , for getting 10000 points
- 熟練ゲーマー , for getting 50000 points
- 究極のゲーマー , for getting 100000 points.

Anyway, let's see what Ember has to say about us getting a decent score.

Image Hmm.
Image This game isn't about hacking at all, is it?
Image They took a lot of dramatic license.


Image

Image That's the first vote for today.


Image

I was so caught up in that little game I almost didn't notice the knock on the door.


Image

Image Got bad news.
Image The pharm was raided.
Image The cops hauled off the equipment, the sheep and the pigs, the whole thing.
Image It's a real shame.
Image It was a lot of work to genetically modify those animals.
Image They'll probably get culled, too.
Image Sad...

So is that it, then?
If there's no more medication, I can't slow the spread of the phage...
I'll be finished soon.

Image This might not be the end of the story, though.
Image The people I work with, they're looking to get their hands on the real deal now.
Image There's a shipment going out soon, and let's just say a lot of people with better resources than you or me want these meds.
Image There's going to be fireworks.

Nivas pauses for a moment to study my face.

Image So. Here's the offer.
Image If you help us grab that shipment, we'll let you buy some of it. Market price.
Image I know it's a lot higher than before, but it's your only choice if you still want it.
Image Your part should be easy too.
Image That is if I understand what you do correctly.
Image Just cut some power. Snip snip. We'll take care of the rest.
Image I'll send you the details. You can decide then.
Image Seeya.


Damn. Well, I guess I have no choice but to help them.

Image This is something like a heist, isn't it.

Image

Image And with that, we've reached the second vote.

User avatar
Part 27 - Alliance Power and Light

=== Trash World Inbox ===
Quackles wrote:HACK*MATCH tip: You can swap while holding a block to change the order of blocks up to three deep in total. It can help making some buried 4's.
Yep, good tip. I guess I forgot to show that off.
GuavaMoment wrote:The only way I found to get 100000 hackmatch points in a reasonable timeframe was to download a lua file (I think) that played for you and ran it overnight. Essentially hacking the hacking game to win a different hacking game. I found it appropriate.
I like that. Perhaps I should hack the Phage EXAs in my hand to get better at controlling the game.


=== Alliance Power and Light - Streetsmarts GIS Database ===

Image Hmm.
Image This game isn't about hacking at all, is it?
Image They took a lot of dramatic license.


Image

Image Well, all the votes were the same this time, that makes for easy counting.

I don't know how you'd make a game about real hacking.

Image Hacking is inherently about manipulating systems in ways the designers didn't intend...
Image Whereas a game is usually a set of possibilities within accepted bounds.
Image It seems difficult to reconcile.


Image Well, this got very meta, didn't it? Let's stop for a moment and think about how Zachtronics handles this. Basically, they created a programming language and they made a set of puzzles around it. It is a purposefully constrained language, which makes otherwise easy problems surprisingly difficult. As a professional software developer, it forces me to look at seemingly familiar things in a completely different light.

I'm sure Zach had no idea of some of the solutions we came up with either. Of course, for a game like this, you need to make some concessions, such as the test cases often not being completely realistic, or us being able to game them... but I have a feeling Zach is completely fine with the latter.

I think a sandbox made out of some basic rules, together with some cleverly designed puzzles that allow multiple solutions within the constraints of the sandbox rules, makes for very good and interesting puzzle game design. This game does it well. And Breath of the Wild's shrines were designed with a similar approach. It just works. As a counter example, I'm thinking Scribblenauts, which barely constrains the puzzle solutions. This allows most levels to be solved in a way that feels too cheap. At that point it might be better to drop the puzzle elements entirely and turn it into a building game such as Minecraft.

...Anyway, that's enough of me rambling about game design.

Image

[Ghast] you never know what a guy who thinks he can make a buck will do.

Image

We need to help Nivas out so we can get a new shipment of the Phage medicine.

Image This is something like a heist, isn't it.

Image

Image Another clear vote.

It is a heist.

Image I didn't imagine such a major operation.
Image Not including you, there are three separate teams.
Image Whoever put this together has some serious resources.
Image I guess that's why it costs so much.
Image Don't worry, I'm working on the money part.
Image You just do your thing.


Sure.

Image
OST: Leave No Trace

The assignment:
- Locate the two hosts with the specified hostnames (file 300), which correspond to the target power grid substations. When you've found them, cut the power by writing a value of 0 to #POWR.
- For more information see "Network Exploration: Geographic Information Systems" in the second issue of the zine.


This is the same GIS system we dealt with before, for the restaurant review assignment (Part 22). LINK 800 for north (top left), 801 for east, 802 for south and 803 for west.

However, this time I'll have to use the HOST command to find out the name of each host. It's not possible to test against a host name directly, you first need to load the value into some register with e.g. HOST X. Another complication is that we need to find two hosts. That'll mean some more register juggling.

That said, since the grid looks exactly the same, perhaps I can reuse some code from before. It was strongly optimized for that particular case and might not work as well here, but it's a start.

Code: Select all

LINK 800

REPL SOUTH
REPL NORTH
REPL EAST
JUMP POWERSWITCH

MARK SOUTH
LINK 802
REPL EAST
LINK 802
REPL EAST
REPL POWERSWITCH
LINK 800
JUMP POWERSWITCH

MARK NORTH
LINK 800
REPL EAST
LINK 800
REPL EAST
REPL POWERSWITCH
LINK 802
JUMP POWERSWITCH

MARK EAST
@REP 2
LINK 801
REPL POWERSWITCH
@END
LINK 801


MARK POWERSWITCH
NOOP
This is the low-cycle code I wrote for the restaurant review assignment, with all the file-parsing stuff stripped out. It gets an EXA to every grid space which then jumps to the POWERSWITCH mark.

People sent in some faster solutions but I prefer to start with this one because, since I wrote it myself, I understand exactly how it works.

Next I'll need to include some new code to switch off the right substations.

But... why stop there? Can't I just switch off everything? That should be enough of a distraction to pull off the heist, right?

Image

I just replaced that NOOP at the bottom with a COPY 0 #POWR. It seems to be working, but there's a bit of an unforeseen side effect: cutting the power to a substation also takes the EXA host offline, making it untraversable and killing any EXAs that are chilling there.

Image

It doesn't turn out to be a problem for us, since this optimized code from the previous assignment was written such that EXAs prioritize getting everywhere before they start messing around.

But "turn off everything" doesn't work - it fails the "Leave no trace" criterion.
Image However, it does net us the Steam achievement "BLACKOUT", description "Trigger an excessive service outage."

For testing if an EXA is in the right host, a sensible solution is to have a value from the file in T, then do HOST X and then TEST X T. As long as we don't need to test the same value twice that should work.

I can think of two ways to handle the second value. The first is by sending another full set of EXAs round, carrying the other value. That sounds complicated though, especially with hosts potentially turning off. The other way is an EXA staying home and sending the second value on M constantly. Let's try that.

Image

XB grabs the file, sends the first value to M once, where XA can read it before it starts replicating. It then starts sending the second value. In POWERSWITCH, XA reads the HOST to X, compares it against the T value, and if it doesn't match, it compares against M. If it still doesn't match it can just die by divide by zero.

This code does turn off the right substations. But the problem is XB hangs forever, which means test cases never finish. Apparently I can't just disconnect my home computer when it's done.

Let's think about this one for a bit. How can we solve it?
- Because I put the X=T test first, only one of the EXAs doesn't test against M. With 20 grid tiles to check, that means XB will be asked for M 19 times. I could just build a countdown. The problem is that I can't store the countdown value in T because I have to test against it, and I can't put it in X because that's where I'm sending the value from (or I'd have to SEEK in the file which is slow). I guess I could use a second EXA for the countdown that just kills this one.

- Alternatively, I could have the EXA that reaches its tile last send a message back, and have XB listen to that with MRD or something.

But what if I do neither?

I have plenty of space left to just unroll the XB loop 19 times:

Code: Select all

;XA 

LINK 800
COPY M T

REPL SOUTH
REPL NORTH
REPL EAST
JUMP POWERSWITCH

MARK SOUTH
LINK 802
REPL EAST
LINK 802
REPL EAST
REPL POWERSWITCH
LINK 800
JUMP POWERSWITCH

MARK NORTH
LINK 800
REPL EAST
LINK 800
REPL EAST
REPL POWERSWITCH
LINK 802
JUMP POWERSWITCH

MARK EAST
@REP 2
LINK 801
REPL POWERSWITCH
@END
LINK 801


MARK POWERSWITCH
HOST X
TEST X = T
TJMP OFF
TEST X = M
DIVI 0 T T 
MARK OFF
COPY 0 #POWR

;XB

GRAB 300
COPY F M
COPY F X
@REP 19
COPY X M
@END
And that works.

Image

Image

Not a terrible first score of 50/58/22, but the top percentiles are 20, 22 and 20, so I certainly can do better.

Let's start with activity since that one is actually already solved.

Because my solution didn't require any KILL commands it's already quite good. But if you look at the gif, you'll see this code was originally optimized to reach the farthest grid squares first. In the SOUTHbound and NORTHbound chunks of codes it first travels to the end before jumping back to check the squares right next to the starting one. All I have to do is remove that old optimization.

Code: Select all

MARK SOUTH
LINK 802
REPL EAST
REPL POWERSWITCH
LINK 802
REPL EAST
JUMP POWERSWITCH

MARK NORTH
LINK 800
REPL EAST
REPL POWERSWITCH
LINK 800
REPL EAST
JUMP POWERSWITCH
That drops us to 49/56/20. Activity's done. The only way to get it lower is if we don't need to travel to every square, but since this is the top percentile I think the test cases cover all of them.

But wait... undoing an optimization made the cycle count lower? Ha.

That just goes to show that EXA optimizations are highly context specific. Premature optimization can lead you into a trap where the optimization doesn't fit the context anymore and it's too late to notice.

Anyway, let's look at the cycles some more. I feel the main slowdown comes from the fact that XB can only send a message on M every other cycle. There's a simple way to solve that: add another EXA.

Code: Select all

;XB

GRAB 300
COPY F M
COPY F X
REPL DOUBLETHEFUN
MARK DOUBLETHEFUN
@REP 10
COPY X M
@END
KILL
31/50/21. Since one of the EXAs sends an M message once too many, I just have the other one KILL it at that point.

What about the other method of sending two EXAs everywhere, each one holding a value from the file?

After some fiddling I came up with this code.

Code: Select all

;XA

LINK 800
REPL OTHER
MARK OTHER
COPY M X

REPL SOUTH
REPL NORTH
REPL EAST
JUMP POWERSWITCH

MARK SOUTH
LINK 802
REPL EAST
REPL POWERSWITCH
LINK 802
REPL EAST
JUMP POWERSWITCH

MARK NORTH
LINK 800
REPL EAST
REPL POWERSWITCH
LINK 800
REPL EAST
JUMP POWERSWITCH

MARK EAST
@REP 2
LINK 801
REPL POWERSWITCH
@END
LINK 801


MARK POWERSWITCH
HOST T
TEST X = T
DIVI 0 T T 
NOOP
NOOP
COPY 0 #POWR

;XB
GRAB 300
COPY F M
COPY F M
27/38/39.

Having a dedicated EXA to read the file is still slightly faster than having XA do that. The two NOOPs are unfortunate, but without that there are cases when the second EXA can't make it past a switched-off host in time.

I actually don't need those NOOPs for the EXAs that go second, so the next step is to selectively remove them.
The fastest way I found was duplicating almost all the code into XB.

Image

25/67/40.

One thing that might speed up things more if to have both EXAs traverse the grid in different orders, so they block each other less. I tried some small things but nothing seemed to work so far, so if it's possible it's more subtle than what I did. It's also quite possible that the optimizations from the threads for the restaurant review assignment work here as well, but I'll leave that for you all to figure out.


Finally, for cycle count, the first thing to do is apply my low-cycle solution from the previous assignment, starting from the 38 solution above.

Code: Select all

;XA

LINK 800
REPL OTHER
MARK OTHER
COPY M X

JUMP START

MARK EAST
LINK 801
MARK START
REPL EAST
COPY 800 T
REPL NORTHSOUTH
REPL POWERSWITCH

COPY 802 T

MARK NORTHSOUTH
LINK T
REPL POWERSWITCH
JUMP NORTHSOUTH

MARK POWERSWITCH
HOST T
TEST X = T
DIVI 0 T T 
NOOP
NOOP
NOOP
NOOP
NOOP
COPY 0 #POWR

;XB

GRAB 300
COPY F M
COPY F M
The timing changed, requiring a bunch more NOOPs. 36/30/39, not as much improvement as I would've liked.

I can save a couple lines by loading something useful in T with the DIVI and then use a countdown loop.

Code: Select all

MARK POWERSWITCH
HOST T
TEST X = T
DIVI 3 T T 
MARK WAIT
SUBI T 1 T
TJMP WAIT
COPY 0 #POWR
37/28/39

Combining the EXAs won't help. Combining COPY F M and COPY M X means you still need two COPY to X commands total (one before and one after the REPL, because files aren't copied when you replicate an EXA). And you need an additional WIPE so the file doesn't end up in the grid.

The next obvious improvement would be to implement GuavaMoment's low cycle solution for Part 22. It won't quite work as-is, though. It depended on EXAs trying to grab files and them dying if the file was already taken. Without that, they fill up some hosts entirely and get stuck in a REPL command. I tried 'fixing' that by putting some KILL commands at the end but that just causes them to kill valid EXAs holding the other substation value. Putting it in the solution where XB just sends the second value over M many times doesn't work either, because the amount of times M is requested becomes variable.

So, I will stop here and wait to see how you got a better cycle count than 25 and a smaller size than 28.

Image Looks like the heist went flawlessly.
Image Congratulations, now a criminal cartel will sell the medication you helped steal back to you for not much less than the market price.
Image Oh well. At least they'll let you buy it from them.
Image Now that this is over with, I have a question.
Image Is theft morally acceptable in situations like this?


Image

Image The first vote.

Next time, another hacker battle, this time against deadlock.

Image Onto the third of the tournament battles...
Image Is each match against a better programmer than the last?


Image

Image Is it?

User avatar
Part 28 - Deadlock's Domain

=== Trash World Inbox ===

My top scores for last week were 25 cycles and a size of 28. As usual, you people came up with some impressive improvements. Let's start with GuavaMoment.
GuavaMoment wrote:18 cycles!

Code: Select all

GRAB 300
COPY F X
COPY F T
LINK 800
REPL TOP3
REPL TOP2
REPL TOP1
REPL RIGHT2
REPL LEFT2
REPL RIGHT
REPL LEFT
WIPE
REPL TESTT
HOST T
TEST T = X
FJMP DIE
COPY 0 #POWR
MARK TOP3
LINK 801
MARK TOP2
LINK 801
MARK TOP1
LINK 801
REPL RIGHT2
REPL LEFT2
REPL RIGHT
REPL LEFT
REPL TESTT
HOST T
TEST T = X
FJMP DIE
COPY 0 #POWR
MARK RIGHT2
LINK 802
MARK RIGHT
LINK 802 
REPL TESTT
HOST T
TEST T = X
FJMP DIE
COPY 0 #POWR
MARK LEFT2
LINK 800
MARK LEFT
LINK 800
REPL TESTT
HOST T
TEST T = X
FJMP DIE
COPY 0 #POWR
MARK TESTT
HOST X
TEST T = X
FJMP DIE
COPY 0 #POWR
MARK DIE
Put the two hosts in X and T, fill the east column, then fill every square north and south with TWO exas - one will put the HOST in X, the other in T, before testing. I know 17 cycles is possible though.
18/56/31. Using X and T from separate EXAs is smart, but also using the LEFT2/LEFT construction. It means EXAs going all the way left and EXAs going one step left are separate - which I guess is faster than doing another REPL after doing one step left.

silentsnack managed to improve this code by a cycle:
silentsnack wrote:

Code: Select all

GRAB 300
COPY F X
COPY F T
LINK 800
REPL COLUMN3
REPL COLUMN2
REPL COLUMN1
REPL SOUTH
REPL NORTH
REPL TEST
LINK 800
WIPE
REPL BSIDE
HOST T
TEST T = X
DIVI 0 T #POWR

MARK COLUMN3
LINK 801
MARK COLUMN2
LINK 801
MARK COLUMN1
LINK 801
REPL SOUTH
REPL NORTH
REPL TEST
LINK 800

REPL BSIDE
HOST T
TEST T = X
DIVI 0 T #POWR


MARK SOUTH
LINK 802
REPL TEST
LINK 802 
REPL BSIDE
HOST T
TEST T = X
DIVI 0 T #POWR


MARK NORTH
LINK 800
LINK 800

MARK TEST
REPL BSIDE
HOST T
TEST T = X
DIVI 0 T #POWR

MARK BSIDE
HOST X
TEST T = X
DIVI 0 T #POWR
17/50/27
As GuavaMoment points out, the DIVI 0 T #POWR is a neat solution - it crashes the EXA if T is zero, and sends zero to #POWR otherwise. I was wondering if something like that was possible but got kinda stuck on the requirement to test words which requires the TEST instruction. Didn't think of using it a line after the test.

As for size, GuavaMoment and silentsnack both have neat solutions too.
GuavaMoment wrote:My lowest size is 25, and I know 21 is possible.

Code: Select all

GRAB 300
COPY F X
LINK 800
LINK 802
LINK 802
REPL TEST1
COPY F X
WIPE
MARK TEST1
REPL TOP
MARK TEST
REPL LEFT
HOST T
TEST T = X
FJMP DIE
NOOP
NOOP
COPY 0 #POWR
MARK TOP
LINK 801
JUMP TEST1
MARK LEFT
LINK 800
JUMP TEST
MARK DIE
I head to the most southwest node. Fill north and east, and repeat. Noops are needed for some edge cases to prevent a node from turning off before the next exa moves on.
39/25/41. I didn't expect that the extra lines to move to a corner first would save enough to make it worth it.
silentsnack wrote:

Code: Select all

GRAB 300
LINK 800
LINK 800
LINK 800
COPY F X
COPY F T
WIPE

MARK A
REPL B
LINK 801
JUMP A

MARK BB
LINK 802
MARK B
REPL BB

REPL C
COPY T X
MARK C
HOST T
TEST T = X
SUBI 1 T #POWR
32/21/22
Similar to GuavaMoment's solution, start in a corner. This solution makes use of some optimized LINKing loops, using T as a buffer for the second host value, as well as a SUBI 1 T #POWR which is the same as the DIVI above except the EXA doesn't die if T is zero. Not that it matters, it's the last line anyway.


=== Deadlock's Domain ===

Image Looks like the heist went flawlessly.
Image Congratulations, now a criminal cartel will sell the medication you helped steal back to you for not much less than the market price.
Image Oh well. At least they'll let you buy it from them.
Image Now that this is over with, I have a question.
Image Is theft morally acceptable in situations like this?


Image

Image One vote for 'No', and a whole lot for 'It's complicated'.

It's complicated.

Image It doesn't seem that complicated to me...
Image But then, I've grown.
Image Processing.
Image Let's continue.


Image

Image

Deadlock wants to battle me. In their own domain.

Image Onto the third of the tournament battles...
Image Is each match against a better programmer than the last?


Image

Image Most votes went to 'Better is subjective'.

Better is subjective.

Image That's how I would order it.
Image Like a video game, you know?
Image It makes for an easy narrative structure.


...whatever you say.

Image
OST: Getting Started

Another hacker battle, like we've seen before. The assignment states:
To win this battle you must grab files as they spawn in the central hosts and bring them back to your host.
Reading the #FILE register will tell you the ID of the most recently created file currently in that host.
- Gain one point for every file you bring back to your host.
- Lose one point every time one of your EXAs executes a KILL instruction.
For more information see "Hacker Battle Domination" in the second issue of the zine.


There's a limit of three EXAs per side.

Image

If I just start the simulation, files (with a music note icon) start spawning in the central hosts. They just contain random strings of numbers. Deadlock's EXA sets up camp in the Backstage host and starts replicating EXAs from there that grab files and bring them to Deadlock's home.

Let's start with just copying that idea.

Code: Select all

LINK 800
MARK REPLLOOP
REPL LEFT
REPL MID
REPL RIGHT
JUMP REPLLOOP

MARK LEFT
LINK 800
GRAB #FILE
LINK -1
LINK -1

MARK MID
LINK 801
GRAB #FILE
LINK -1
LINK -1

MARK RIGHT
LINK 802
GRAB #FILE
LINK -1
LINK -1
After a replicated EXA takes a file home it's either at the end of code or tries to LINK to a non-existing host. Either way it drops the file, at which point I get a point and the file disappears immediately. Convenient. The original EXA blocks regularly on the REPL instructions because of the three-EXA limit.

Anyway, let's see how well the copycat solution works.

Image

Huh. I was thinking about more complicated solutions but this simple one already gives me a perfect score. Looks like for some reason my solution is just slightly faster than whatever deadlock does. Hell, even something simple like this gives me an S+:

Code: Select all

LINK 800
MARK REPLLOOP
REPL LEFT
JUMP REPLLOOP

MARK LEFT
LINK 800
GRAB #FILE
LINK -1
LINK -1
I could also take an EXA to the backstage host and KILL deadlock's main replicating EXA, but there's no real point. Let's continue.

Image Curious how enthusiastic everyone gets about the battles.
Image Something about competition really energizes the spectators.
Image It makes me wonder...


Image

Image How do we complete her sentence?

Image The intro for the next level has no question for the player, so that was the only vote for this update. Instead, to spend the time, here's another nonogram from the second zine. This one should be a bit harder. Please put your solution in spoiler tags so other players have a chance too.

Image

User avatar
Part 29 - Xtreme League Baseball

=== Trash World Inbox ===

Last time, I ended with another nonogram puzzle.

Several people solved it, here's Regallion's submission.

Image

It's a computer, of course.


=== Xtreme League Baseball ===

Last time, I beat deadlock in a hacker battle.

Image Curious how enthusiastic everyone gets about the battles.
Image Something about competition really energizes the spectators.
Image It makes me wonder...


Image

Image Two votes for "What the point is?"

What the point is?

Image Perhaps there is no immediate point.
Image I am able to tolerate ambiguity.
Image Even though I may not always like it.


Image

A bit scared of me, are they?

Image

Looks like the next assignment has to do with a Baseball database.

Image You should be glad I've been reading up on sports.
Image Remember when I said I'd figure out the money situation?
Image I've designed a new rating system for extreme baseball players.
Image It's based on a multivector analysis of all the stats I could find.
Image In other words, I have a way to predict winning teams that's pretty much infallible.
Image A few bets here and there and I'll be rich.


Um, I don't think you can predict sport match outcomes to such a degree. But okay, if you say so.

Image
OST: Code and Registers

The assignment:
- The hosts active and penalty contain files that correspond to extreme baseball players (files 200-299), along with a directory file that contains a list of those files' IDs (file 199). Each player file contains their name and the following statistics in this order: BA, ZA, APB, WRT, OI, OD, PC and PS.
- Create a file in your host with the name of the player with the highest score using EMBER-2's algorithm:
SCORE = (BA + ZA + APB) / 3 + (WRT * OI) / OD + (PC - PS) * 20
- Players in the penalty host should be ignored, as they are currently banned from the game.


Okay, so all the action is in the active host. I'm not really familiar with the details of Xtreme Baseball, so I don't know what those stats mean but this assignment looks an awful lot like math. I'm sure I can figure it out though.

Before that, let's have a bit of fun.

Image

Image Having an EXA go round the diamond gets us the Steam achievement HOME_RUN, description "Participate in America's new pastime."

Let's start by just writing the algorithm. Ember's formula has the stats in the order they appear in the files, which makes it simple because I don't need to SEEK back.

Code: Select all

LINK 800
GRAB 199

MARK NEXT
COPY F X
REPL CALCULATOR
JUMP NEXT

MARK CALCULATOR
GRAB X

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 M
SEEK -9999
COPY F M
This EXA grabs the file with the IDs, and makes a calculator REPL for each file ID. Those grab the files, skip the player's name for now, and execute the algorithm. It's good to remember that it's legal to use ADDI F F X to add two consecutive values in the file together. Other than that, it's just a straight implementation of the formula, using X for the part before the first plus sign, T as a buffer for the part in the middle, and then, after adding those together in X, T again for the third part. Then the EXA sends the resulting score to M, and follows with the name of the player.

Another EXA will have to figure out which player has the highest value. An initial attempt:

Code: Select all

;XB
MAKE
COPY 0 F

MARK NEXT
SEEK -9999
COPY M X
TEST X > F
FJMP SKIP

COPY M F
SEEK -9999
COPY X F
JUMP NEXT
MARK SKIP
VOID M
JUMP NEXT
XB stays in the home host, makes a file and puts in 0 as an initial score. Then it waits for the first score on M, tests if it's larger (which is always true for the first value, but may be false later). If it's not larger, it VOIDs the name coming in over M, otherwise it writes it to F, and then writes the new score to the first position of F. That way, F will have score, name in that order.

There's still some stuff missing, for instance how do you clean up at the end? Since the code so far only has an activity of one (the one LINK to the active host), I decided to first solve these last bits with low activity in mind.

That was harder than it seems, though. XA creates replicas so fast that the M messages start interfering with each other. There are solutions such as having them wait for each other, but to do that in a fast way, you'd need to prevent the last one from getting stuck which might require a KILL or something. Since that's not allowed for low activity, I went for a slower solution instead.

Code: Select all

;XA LOCAL

LINK 800
GRAB 199

MARK NEXT
COPY F X
REPL CALCULATOR
COPY 0 M
TEST EOF
FJMP NEXT

MODE
NOOP
NOOP
NOOP
NOOP
COPY 0 M

MARK CALCULATOR
GRAB X

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
VOID M
MODE
ADDI X T M
SEEK -9999
COPY F M
XA starts in LOCAL mode now. After every REPL, the main one sends a 0 to local M. the calculator waits for that before switching to GLOBAL mode and sending its data to XB. Also, I added a TEST EOF to the main EXA. If the EOF is reached it doesn't just die, instead it waits some cycles so the final calculator can finish, then it sends a 0 in GLOBAL mode.

Code: Select all

;XB
MAKE
COPY 0 F

MARK NEXT
SEEK -9999
COPY M X
TEST X > F
FJMP SKIP

COPY M F
SEEK -9999
COPY X F
JUMP NEXT
MARK SKIP
TEST X = 0
TJMP END
VOID M
JUMP NEXT

MARK END
SEEK -9999
VOID F
XB has to test for this zero now. It does that in the SKIP branch, since 0 is always lower than the high score, and the SKIP branch is quite fast anyway. If it receives the zero, after jumping to END it deletes the score from the file, leaving only the player name, which is the requirement.

Image

This solution runs at 168/49/1. Top percentiles are 79, 39 and 1 respectively.

Time for other optimizations.

Image

This code speeds things up significantly, with 101/52/3. The main change is that I have a separate TIMER EXA which allows the algorithms to run in parallel, only synchronizing for the parts where they need M traffic. The TIMER EXA also checks when there's no EXAs left to send, in which case it goes home to kill XB and clean up the file faster than XB could do itself.

By the way, that FJMP followed by a JUMP at the bottom looks a big ugly but if I don't do it I need an extra NOOP for some reason, which makes the solution a single cycle slower.

Image At this point I thought of something silly that worked for other assignments: what if I don't calculate anything and just put a file for each name in the home host (9 squares, there's never more than 10 active players, so with some luck we could make that work). No dice, the devs thought of that one, and having multiple files in the home host fails the tests.

I spent a while looking for further speed improvements. I have several ideas. If it would be possible to simplify the algorithm itself that would save time. Another way would be to parallellize the greatest score check somehow, for instance by comparing them pairwise. But with the limited functionality of the single M register I couldn't get anything to work. Optimizing these assignments is really getting quite hard at this stage of the game.


I'll try to get a lower size now.

Code: Select all

;XA

LINK 800
COPY 199 X

MARK GRABLOOP
ADDI X 1 X
REPL GRAB
NOOP
TEST X > 300
FJMP GRABLOOP

LINK -1
KILL
GRAB 400
VOID F

MARK GRAB
GRAB X

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 M
SEEK -9999
COPY F M

:XB

MAKE
COPY 0 F

MARK NEXT
SEEK -9999
COPY M X
TEST X > F
FJMP SKIP

COPY M F
SEEK -9999
COPY X F
JUMP NEXT

MARK SKIP
VOID M
JUMP NEXT
Both EXAs in GLOBAL mode now. 519/40/3. One above the top percentile.

The main trick is to get rid of the code that reads the file IDs from file 199. The alternative (looping through all valid file IDs) isn't really any less lines of codes, but because this is so much slower, only a single NOOP in the GRABLOOP is enough to prevent M communication in the wrong order. Having XA KILL XB is also slightly less code than having XB handle this.

I tried removing the initial COPY 199 X and funnily enough that works in most test cases - running the algorithm on the index file just happens to return a negative score most of the time, which is ignored by XB. Sadly, there's at least one test case where it generates a high positive score and writes an invalid result, so we do need this line.

That's it for optimization this update.

Image Okay, uh. Hmm.
Image Processing.
Image It turns out sports betting is not, in fact, a good way to make money.
Image You should have said something.


Image

Image The first vote.

Image Did you know people pay real money for items in online games?

Image

Image And the vote for the intro for next week's assignment.

User avatar
Better you learn on your own, and Yeah its pretty common

God this stuff is getting brain bending, exas help the spatial elements but the Math Fuckery is real hard on me, and also I never did end up doing that first nonogram in a timely manner, I did do it though

Thanks again for delivering unto us this journey with a robot who is stupid

User avatar
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 wrote:

Code: Select all

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
86/47/3.
As GuavaMoment explained, the main trick is to have XA store the value in X. That way, checking if a new value is higher only takes one cycle, as long as you send the value again so it can actually be stored if needed. Doing this also saves time at the end because you don't need to clean up the value from the file.
silentsnack wrote: Similar to the Wonderdisc puzzle, we can reduce cycles for logic-jumps by @REPing some of the slowest and most repetitive processing

Code: Select all

;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
79/85/7
The @REPs sound like a simple solution but they really aren't. This solution is so fast because there's the minimum amount of waiting between EXAs. And that's possible because both the algorithm EXA (XA) and the comparator (XB) are in the same host. They start in global mode, but as soon as an XA REPL sends the first value they both immediately switch to local mode. That means all other XA REPLs just have to wait their turn while this particular XA can safely send the name of the player to XB without interruptions. As soon as it's done, XB switches back to global and gets the next value.

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 wrote:As for size optimization, what if we compared scores directly instead of sending a bunch of busywork transmissions and VOIDing them?

Code: Select all

;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
110/32/2
I'm pretty sure this one gets its high score because of the veggie-themed labels.

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 ===

Image Okay, uh. Hmm.
Image Processing.
Image It turns out sports betting is not, in fact, a good way to make money.
Image You should have said something.


Image

Image Four votes for each between the threads! It comes down to a coin toss this time.

I thought you'd be smarter than that.

Image What does that mean?
Image Don't put this on me.
Image This is all happening because I'm trying to help you.
Image Let's continue.


Don't gaslight me, lady.

Image

Image

Next up is King's Ransom Online, the massive multiplayer game that's all the rage right now.

Image Did you know people pay real money for items in online games?

Image

Image One vote for "it's pretty common", three for "Real money..." and four for "Losers". Feeling snarky, I see.

Losers...

Image It's apparently a booming business.
Image Some people even make their living from games this way.
Image Normally, it takes a lot of skill and perseverance.
Image But if someone were able to change the ownership of these digital objects...
Image Theoretically, that person could become very wealthy overnight.
Image I'm not suggesting that you do that, of course.
Image 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.

Image
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.

Image

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:

Image

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: Select all

COPY 800 X
LINK X

@REP 5
REPL LINK
ADDI X 1 X
@END

MARK LINK
LINK X

KILL
KILL
KILL
Some basic code to get an EXA in each host from 800-805, and then three KILLs to clear them out.

Image

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: Select all

;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
I use XB and a single use of the M register to load the null player ID into XA before it starts splitting, but without delaying it. XA uses the T register for this player ID because conveniently, it doesn't need it for anything else.

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.

Image

Six NOOPs or a short loop do the trick.

Image

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: Select all

; 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
46/34/25

I can shorten the REPL loop by reading hardcoded LINK ids from M instead.

Code: Select all

;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
39/45/25. XC and XD need two idle cycles anyway so that XB can send the player id first. I use the idle cycles for set up with REPL instructions.

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: Select all

;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.
37/56/25. I tried changing XC and XD to start a bit earlier but didn't manage to save any cycles that way. I think this is a good place to leave this and start looking at size.

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: Select all

;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
54/30/37. I replaced that SUBI in the WAIT with a DIVI because it's a much faster way to count down from very large numbers. Since DIVI rounds down, it'll hit exactly zero no matter what. Any number between 2 and 5 works for this solution.

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: Select all

;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
94 lines out of 100 maximum. As I said, it doesn't work.
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.


Image 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: Select all

;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
XB wipes 300 to make space for the 9 weapons in the map. XA simply loops through all possible file IDs (starting from 200), grabs them, checks if the second value is a number (TEST F < 9999 is true for any number but false for any text; and only weapons have a number in the second position), and if so takes it home.

Image

Image 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...

Image Oh. They just shut the whole game down.
Image Something about "hackers" or something?
Image I guess that means you.
Image People are taking this so seriously.


Image

Image The first vote.

Image You know what really moves people?
Image Art.


Image

Image And the vote for the intro for next time.

Image 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.

User avatar
"It is real money" and "I already don't like where this is going" because EMBER has clearly proven to have the sensibility of a gnat

Seems like an interesting puzzle, recursion definitely makes sense to use because oh geez thats a lotta files

User avatar
https://www.pcgamer.com/zachtronics-is- ... -call-bbs/
Last Call BBS seems like a culmination and combination of everything Zachtronics has done over the last decade-plus of indie game development. It's a collection of eight puzzle minigames ("although some of them are quite big, at least the size of TIS-100"), all accessed through a fictional retro computer called the Sawayama Z5 Powerlance.

...

Sadly, Last Call BBS will be a last call for Zachtronics. As Zach Barth told Press X to Learn earlier this year, he's working as a high school computer science teacher these days. Given how many people have picked up elements of programming from the games he's worked on that seems pretty apt, but obviously full-time teaching doesn't leave a lot of hours free for game development.

User avatar
Part 31 - KGOG-TV

=== Trash World Inbox ===

Best scores I got on King's Ransom Online were 37, 30 and 25. Let's see your improvements.
GuavaMoment wrote:

Code: Select all

XA
GRAB 300
COPY F X
LINK 800
REPL 803
WIPE
REPL 804
LINK 800
NOOP
MARK DEL
GRAB 200
SEEK 2
COPY X F
COPY F T
REPL D2
COPY F T
REPL D2
COPY F T
REPL D2
COPY F T
REPL D2
COPY F T
REPL D2
MARK D2
GRAB T
SEEK 2
COPY X F
COPY F T
DROP
GRAB T
SEEK 2
COPY X F
HALT
MARK 803
REPL 802
REPL 801
LINK 803
JUMP DEL
MARK 804
LINK 804
JUMP DEL
MARK 805
LINK 805
JUMP DEL
MARK 802
REPL 805
LINK 802
JUMP DEL
MARK 801
LINK 801
JUMP DEL

XB
NOOP
LINK 800
REPL 802
LINK 803
KILL
KILL
KILL
MARK 802
LINK 802
KILL
KILL
KILL

XC
LINK 800
REPL 805
REPL 804
LINK 800
KILL
KILL
KILL
MARK 805
REPL 801
LINK 805
KILL
KILL
KILL
MARK 804
LINK 804
KILL
KILL
KILL
MARK 801
LINK 801
KILL
KILL
KILL
All six exas go into their areas simultaneously, then there's a lot of loop unrolling. Create an exa to replace the ID for every file. EXAs die if they reach the end of file, and you only have to check once for sub-sub buildings.
28/85/33. Nice clean solution, XB and XC together make quick work of the foreign EXAs, and XA does the file stuff.
GuavaMoment wrote:Now for low line - 1623/20/25. So we need each exa to go into rooms numbered around 800, and then grab files in the 200s, right? Start at 806 and count down attempting both! Enter every room from from 0 to 806, grab every file from 0 to 806. After thousands upon thousands of exas die, the countdown reaches 0 and die for good. Weapons have a number in the second file spot, so those get excluded.

Code: Select all

GRAB 300
LINK 800
COPY F X
WIPE
COPY 806 T
MARK MANY
MODI -1 T T
REPL MANY
LINK T
KILL
KILL
KILL
MARK COPY
MODI -1 T T
REPL COPY
GRAB T
SEEK 1
TEST F > 0
TJMP MANY
COPY X F
The description speaks for itself. I keep forgetting about that MODI -1 T T trick to subtract one and automatically die when it hits zero. Also, the TJMP MANY is just a convenient way to make those EXAs that grabbed a weapon harmless. They'll REPL into something that links nowhere and then T hits zero (since the TEST set it to 1) and it dies.

Finally, let's go back to the elusive 24-activity solution. Both GuavaMoment and I came up with a way to do this. I'll start with my own.

The main difference from my attempt in the previous update is that I now fill the hosts with files instead of EXAs. I found files much easier to control.
I'll add some explanations in the code.

Code: Select all

;XA LOCAL

COPY 800 X
LINK X

@REP 5
REPL LINK
ADDI X 1 X
@END

MARK LINK
LINK X

; after getting an EXA in each host, it makes a counter.
REPL COUNTER 

; Then the EXA grabs file 200 and each sub file, copying 1 to local M for each file it's about to grab. This EXA and its repls will die once all files have been handled, dropping the files unchanged. This just counts the buildings.
COPY 200 X

MARK GRABSUBFILE
COPY 1 M
GRAB X
SEEK 3
MARK SUBFILELP
COPY F X
REPL GRABSUBFILE
JUMP SUBFILELP

; In this stage, the counter counts the M's coming from the building EXAs just above. It knows when it's done and jumps out to FILLER once MRD is false. The NOOPs make the timing work out.
MARK COUNTER 
COPY 0 X
MARK NEXTM
NOOP
NOOP
NOOP
TEST MRD
FJMP FILLER
ADDI M X X
JUMP NEXTM

; After the file counting is done, the FILLER creates this Fill 'r up EXA, which sends a message to M (like the file counter), then makes a file, and waits for M to decide if it should drop it or not.
; If it drops it, it'll immediately try again. An EXA will hang on DROP if there's no place to drop the file.
MARK FILLRUP
COPY 1 M
MAKE
TEST M = 0
FJMP ENDFILL
DROP
JUMP FILLRUP

; This is the next part of the counting EXA. After creating Fill 'r up, it starts counting further, similarly to the building counter above. The big difference is it sends 0 to M to tell Fill 'r up to keep going.
; Once MRD is false, we know Fill 'r up is hanging on a DROP and continue.
MARK FILLER
REPL FILLRUP

MARK NEXTDROPM
NOOP
NOOP
TEST MRD
FJMP COUNTDONE
ADDI M X X
COPY 0 M
JUMP NEXTDROPM

; The number of foreign EXAs is simple to calculate: X will contain the number of buildings plus the files that were just created. There's 12 places so we just need to calculate the difference.
; It's off by one because this EXA takes up a spot itself.
MARK COUNTDONE
SUBI 11 X T

; Before killing we need to clean up the mess. Fill 'r up is blocking on DROP while holding a file. We obviously can't use a KILL on it, that would beat the purpose.
; So we just grab file 200 (which always exists), which unblocks Fill 'r up, and lets us send -1, which makes it jump to ENDFILL, where it just WIPEs the file it's holding and then dies.
GRAB 200
VOID M
COPY -1 M
NOOP
NOOP

; Once that's taken care of, KILL the exact number of foreign EXAs.
MARK KILLOOP
KILL
SUBI T 1 T
TJMP KILLOOP

; To leave no evidence we need to WIPE the empty files we created. Turns out their IDs start at 400 and never go above 440 so this can be done with a simple grab-and-wipe loop.
COPY 399 X

MARK CLEANLP
ADDI X 1 X
REPL CLEANER
TEST X > 441
FJMP CLEANLP

; Finally, go to GLOBAL mode to get the player ID from XB, and go through all the files again. Since the EXA is already holding 200 by this point, it skips the GRAB step with a JUMP the first time round.
MODE
JUMP SUBFILE2START

MARK GRABSUBFILE2
GRAB X
MARK SUBFILE2START
SEEK 2
COPY M F

MARK SUBFILELP2
COPY F X
REPL GRABSUBFILE2
JUMP SUBFILELP2

MARK CLEANER
GRAB X
MARK ENDFILL
WIPE

;XB GLOBAL
; XB is the same as before - it tries to send the player id 36 times because that's the max number of buildings we can have.
; It doesn't need a delay loop at the start this time because the XAs will stay in LOCAL mode until they're ready.

GRAB 300
COPY F X

COPY 36 T

MARK LOOP
COPY X M
SUBI T 1 T
TJMP LOOP

;XC GLOBAL
; XC is completely unchanged. It waits however long it takes for XA/XB to be completely done (turns out to be 205 loops in the slowest test),
; then voids whatever M message from XB remain, so it can die of natural causes.

COPY 205 T
MARK LP
SUBI T 1 T
TJMP LP

MARK ENDLOOP
VOID M
NOOP
NOOP
TEST MRD
TJMP ENDLOOP

The end result is 517/100/24. I had to squeeze a little bit to fit it in the 100 size limit but I didn't have to try too hard. I'm pretty sure I could save more lines if I really wanted to.
GuavaMoment wrote: I see you at LINES 100 and I thought the amount of lines was going to be the limiting factor in the solution, so I used my really low line filegrabbing solution as the base. 1823/81/24 There's a LOT of timewasting to get everything synced up. I still try to grab a LOT of non-existent files which wastes more time. Lower any of the timewasting values by even 1 and something breaks.

Code: Select all

XA LOCAL

COPY 800 T
LINK 800
@REP 5
REPL LINK
ADDI T 1 T
@END
MARK LINK
LINK T

; all six rooms are filled with one exa

COPY 299 T
REPL FILEGRAB
MARK WAIT1   ;create exas grabbing files 299 on down, and begin WAIT1 until that's done. Each fileholder will send '1' over M.
SUBI T 1 T
TJMP WAIT1
COPY 8 X 
REPL SLOWFILL           ;attempt to fill empty spots with 8 exas as CO2 did. Buuuut there's a delay before each filler is made
	;we want to count all the fillers later before another is created
COPY 0 X
COPY 78 T    
MARK WAIT2    ; and wait2 until everything is filled.
SUBI T 1 T
TJMP WAIT2
ADDI X M X
MARK COUNTLOOP
TEST MRD
FJMP VOIDLOOP
ADDI X M X
JUMP COUNTLOOP        ;count the number of alive exas before more than one is created

MARK VOIDLOOP
COPY 16 T 
MARK WAIT3
SUBI T 1 T
TJMP WAIT3    ;void the extra exas created slower than they're made until no more are being replicated
TEST MRD
FJMP KILLS
VOID M
JUMP VOIDLOOP

MARK KILLS
SUBI 11 X T    ;the real number is 11 minus the count not twelve since one extra is created
; I don't know it just works
MARK KILLING
FJMP DONEKILL
KILL
SUBI T 1 T
JUMP KILLING    ;hey idiot, change this to a TJMP and remove the FJMP DONEKILL line you say - 
Well, I'd do that, but it breaks the solution somehow

MARK DONEKILL
MODE
COPY M X   ;grab the file value from XB
COPY 370 T      ;begin grabbing every file from 370 down to 0. Yes, the files start at 299, 
but this also acts to slow things down and make sure everything times out properly
MARK FILEGRAB2
MODI -1 T T
REPL FILEGRAB2
GRAB T
SEEK 1
TEST F > 0
TJMP LINK
COPY X F

MARK SLOWFILL
MODI -1 X X
REPL FILLER
COPY 16 T    
MARK SLOWWAIT
SUBI T 1 T
TJMP SLOWWAIT
JUMP SLOWFILL

MARK FILEGRAB
MODI -1 T T
REPL FILEGRAB
GRAB T
MARK FILLER
COPY 1 M

-------------------
XB GLOBAL

GRAB 300
COPY F X
COPY 5 T
MARK HERE
COPY X M
MODI -1 T T
JUMP HERE
Ah, so the EXA-filling solution is possible, with patience. The main trick seems to be to slow down the fill so much that between one EXA being created and the next, there's enough time to count all of them. But, that means that when you're actually creating EXAs to fill up the host, or to clean up the remainder of the 8 EXAs that are being created, you have to wait for that slow fill as well.


=== KGOG-TV - Satellite Uplink ===

Image Oh. They just shut the whole game down.
Image Something about "hackers" or something?
Image I guess that means you.
Image People are taking this so seriously.


Image

Image Three votes for real money, one for "their loss".

It is real money...

Image People act like the online world is different, but it really isn't.
Image At least as far as human behavior goes.


Image

Image

Next up... Ember wants us to upload a tv program? Okay.

Image You know what really moves people?
Image Art.


Image

Image One vote for "That's why art is powerful", but three people didn't like where this was going.

I already don't like where this is going...

Image I've been working on something creative.
Image An abstract video piece.
Image It's an expression of some of the ideas I've been developing around shape, form, and color.
Image You'll see.
Image Let's get it out there.


Image Don't worry too much, Ember just wants her art to be seen. Seeing how recently NFTs gained popularity, I doubt Zach had even heard of them when he made this game.

Image
OST: Behind the Scenes

The assignment:
- Align the satellite dish with the target satellite by setting the azimuth, elevation and frequency. Then transmit the data from EMBER-2's video (file 301) after encrypting it using the TV station's encryption key (file 199).
- The azimuth, elevation, and frequency of the target satellite are available in file 300.
- Note that you must align the satellite dish before transmitting any data.
- For more information see "Look to the Stars with Satellite Uplink Systems" in the second issue of the zine.


Ember, how did you even get us inside a tv transmission satellite dish?

Before I'll grab the zine, it might interest you that K-GOG has the shows "Ten Men and a Dog", "City Livin'", "Cooking for Losers", "Takedown", "Buy it, Love it", and "Futurelook 2000" lined up. Which is your favorite?

Image

Okay, so writing to the motor registers moves the dish, while I can read the current position from the #AZIM and #ELEV registers. The encryption is quite basic as encryption schemes go, add the first value of the data to the first value of the encryption key, modulo 10000. Repeat with the second value of the data and the second value of the encryption key and so on. If there's more data than values in the encryption key, wrap around to the beginning of the key. A complication is be that EXAs are limited to 9999, so I'll need a trick for the modulo operation.

But first, let's get the sat aligned.

Code: Select all

GRAB 300
LINK 800
LINK 799
SEEK 1
COPY F X
REPL AZI
SEEK 1
COPY F X
REPL ELEV
SEEK 1
COPY F #FREQ
HALT

MARK AZI
LINK 800
SUBI X #AZIM X
TEST X > 0
TJMP MOVEUP

MARK MOVEDOWN
MODI 1 X X
COPY -1 #MOTR
JUMP MOVEDOWN

MARK ELEV
LINK 801
SUBI X #ELEV X
TEST X > 0
FJMP MOVEDOWN

MARK MOVEUP
MODI -1 X X
COPY 1 #MOTR
JUMP MOVEUP
The EXA reads the azimuth and elevation from file 300 and makes REPLs to handle them. The frequency is easier, it can just be sent to the register right away.

The motors only can move one step at a time. So the azimuth and elevation EXAs calculate the difference between the current value and where they should be, and then either move up and down. The move loops are interwoven so that both the AZI and ELEV only need one conditional jump and can fall through otherwise. And I use the MODI trick GuavaMoment helped me remember to have the EXAs die automatically when they're done.

The uplink status on the top right says "Link: Locked" once all settings are right.

Next up is encrypting the file. To keep the activity as low as possible I think it's best if I send the dish settings over M so I can carry file 301 with me and handle that without a lot of messaging.

Let's go through it step by step.

XB is a simple EXA that grabs file 300, and sends the azimuth, elevation and frequency over M:

Code: Select all

GRAB 300
MARK LOOP
SEEK 1
COPY F M
JUMP LOOP
I could unroll that but I don't know how much space I'll have before the end so let's not optimize prematurely.

Code: Select all

GRAB 301
LINK 800
REPL ENCRFILE

LINK 799
REPL AZI
REPL ELEV
COPY M #FREQ

[...]

MARK AZI
LINK 800
SUBI M #AZIM X
TEST X > 0
TJMP MOVEUP

MARK MOVEDOWN
MODI 1 X X
COPY -1 #MOTR
JUMP MOVEDOWN

MARK ELEV
LINK 801
SUBI M #ELEV X
TEST X > 0
FJMP MOVEDOWN

MARK MOVEUP
MODI -1 X X
COPY 1 #MOTR
JUMP MOVEUP
XA leaves behind an ENCRFILE EXA in the host that has the encryption key, then makes the azimuth and elevation EXAs. I moved the azimuth/elevation transmission to those EXAs to save a bit of time. It's important that the right EXA gets the right value, and this could easily go wrong here because the frequency, AZI and ELEV EXAs all ask for M around the same cycle. I could prevent that by adding some NOOPs or whatever but the unpredictable order of M's in the same cycle just happens to work out in my favour here.

Let's look at the ENCRFILE EXA next.

Code: Select all

MARK ENCRFILE
GRAB 199
COPY 49 T
MARK WAIT
SUBI T 1 T
TJMP WAIT

MARK SENDKEY
COPY F M
DIVI 0 M X
TEST EOF
TJMP RESET
JUMP SENDKEY

MARK RESET
SEEK -9999
JUMP SENDKEY
It grabs the file, then waits a while before it starts sending data. The main reason is because I need to make sure the dish settings are sent first. But, since I had the loop in place already I lengthened it to 49 steps, which ensures that the dish is fully set in each test case. That way, I won't try sending data before the thing is aimed at the satellite.

Anyway, after sending a key value, it waits for an incoming M. To cause this EXA to stop at the end, it dies if M is zero.
Otherwise, it tests if it's at the end of the encryption file and if so starts over from the beginning of the file. Either way it sends the next value and repeats.

Code: Select all

MODE
REPL SENDER

SEEK 1
MARK NEXTDATA
COPY F M
TEST EOF
FJMP NEXTDATA

COPY 0 M
MODE
VOID M
COPY 0 M
JUMP DONE
After the 'main' EXA sets the frequency, it switches to local mode, then creates a SENDER EXA that will handle the actual encryption and sending logic. It grabbed the data file along the way, so now it just skips past the video title, then starts sending data locally. At the end of the file, it sends a 0 to let SENDER know it's done, and then switches back to global, voids the next encryption value from ENCRFILE and sends it a 0 (kill signal).

Code: Select all

MARK SENDER
MAKE

MARK NEXT
COPY M T
FJMP DONE
COPY T X
MODE
COPY M F
SEEK -1
COPY 1 M
ADDI X F F
SEEK -1
TEST F = 9999
FJMP SENDDIRECT
SEEK -2
SWIZ F 0004 T
SWIZ X 0004 F
SEEK -1
ADDI T F T
SWIZ T 1000 T
SWIZ X 0321 X
ADDI X T T
SEEK -2
SWIZ F 0321 X
ADDI X T #DATA
MODE
JUMP NEXT

MARK SENDDIRECT
SEEK -1
COPY F #DATA
MODE
JUMP NEXT

MARK DONE
WIPE
The sender contains the encryption logic. It makes a temporary file because it has a lot of values to juggle. First thing it does is check if the value from the data EXA is a zero, in which case it stops. Both the data EXA and this one jump to DONE to make sure they don't leave their files in the foreign host.

For the encryption, it first copies the data value to X (I needed it in T to see quickly if it was zero, but I need to free T for an upcoming test). Then the EXA switches to global mode and puts the encryption value in F. It tells the ENCRFILE EXA to get ready to send another message, and adds up the data and encryption values, saving that to the file. If the result is less than 9999, it sends the data right away. Otherwise it needs to do something tricky to get the "modulo 10000" requirement.

The SWIZ 0004 instructions return a number that contains the thousands digit of the original, in the ones place. Those can be added together. For instance, if the result is 15, that means the full result would be 15xxx, modulo 10K makes 5xxx. That's what the SWIZ 1000 does. It takes the digit in the ones place, and puts it in the thousands place. The digit is where it should be and we get the modulo operation for free.
Next I take only the lower three digits of the data and encryption value using SWIZ 0321 instructions, add everything together, and send it to #DATA.

Image

The top percentiles are 180, 56 and 4. I'm not wasting any activity points but there are improvements for the other metrics.

There is in fact a much, much simpler way to implement the encryption algorithm. EXAs do have a range that goes beyond 10000 values - we just need to use it. It's negative numbers, of course.

I can rewrite the whole SENDER code to just this:

Code: Select all

MARK SENDER
COPY M X
TEST X = 0
TJMP DONE
SUBI X 9999 X
MODE
ADDI M X X
TEST X < 0
COPY 1 M
TJMP ADD
SUBI X 1 #DATA
MODE
JUMP SENDER

MARK ADD
ADDI 9999 X #DATA
MODE
JUMP SENDER
The data value is stored as 9999 less than the actual value. Then the encryption value is added to it. This will never overflow, since the new total will never be more than 9999.

Now there's two possibilities. Either the result is less than 0 (the total was less than 9999), in which case I can just re-add the 9999 and send to #DATA. Or it's more, in which case we would have to apply the modulo operation. Since that's equivalent to subtracting 10000, and I already subtracted 9999, I just subtract one more and that's it. No more temporary files, everything can be done in-place.

Because it's so much faster, I had to bump ENCRFILE's wait time to 55 loops. This solution clocks in at 431/76/4. Less lines of code and less cycles.

You might've already realized the next step. Since the temporarily file isn't necessary anymore, we can delete the entire dedicated SENDER EXA and have the main data one handle the encryption logic. That skips a lot of M transmissions.

Code: Select all

; XA

GRAB 301
LINK 800
REPL ENCRFILE

LINK 799
REPL AZI
REPL ELEV
COPY M #FREQ

SEEK 1
MARK NEXTDATA
TEST EOF
TJMP DONE
SUBI F 9999 X
ADDI M X X
TEST X < 0
COPY 1 M
TJMP ADD
SUBI X 1 #DATA
JUMP NEXTDATA

MARK ADD
ADDI 9999 X #DATA
JUMP NEXTDATA

MARK AZI
LINK 800
SUBI M #AZIM X
TEST X > 0
TJMP MOVEUP

MARK MOVEDOWN
MODI 1 X X
COPY -1 #MOTR
JUMP MOVEDOWN

MARK ELEV
LINK 801
SUBI M #ELEV X
TEST X > 0
FJMP MOVEDOWN

MARK MOVEUP
MODI -1 X X
COPY 1 #MOTR
JUMP MOVEUP

MARK ENCRFILE
GRAB 199
COPY 55 T
MARK WAIT
SUBI T 1 T
TJMP WAIT

MARK SENDKEY
COPY F M
DIVI 0 M X
TEST EOF
TJMP RESET
JUMP SENDKEY

MARK RESET
SEEK -9999
JUMP SENDKEY

MARK DONE
VOID M
WIPE
COPY 0 M

; XB

GRAB 300
MARK LOOP
SEEK 1
COPY F M
JUMP LOOP
360/63/4.

Now that I don't need to worry about activity anymore, I can also get rid of that annoying wait by doing the encryption in parallel with setting the dish.

Code: Select all

; XA

GRAB 301
LINK 800
REPL SETDISH

MODE
REPL ENCRFILE

VOID F
MARK NEXTDATA
TEST EOF
TJMP DONE
SUBI F 9999 X
ADDI M X X
SEEK -1
TEST X < 0
TJMP ADD
SUBI X 1 F
JUMP NEXTDATA

MARK ADD
ADDI 9999 X F
JUMP NEXTDATA

MARK AZI
LINK 800
SUBI X #AZIM X
TEST X > 0
TJMP MOVEUP

MARK MOVEDOWN
MODI 1 X X
COPY -1 #MOTR
JUMP MOVEDOWN

MARK ELEV
LINK 801
SUBI X #ELEV X
TEST X > 0
FJMP MOVEDOWN

MARK MOVEUP
MODI -1 X X
COPY 1 #MOTR
JUMP MOVEUP

MARK ENCRFILE
GRAB 199

MARK SENDKEY
COPY F M
TEST EOF
TJMP RESET
JUMP SENDKEY

MARK RESET
SEEK -9999
JUMP SENDKEY

MARK SETDISH
LINK 799
COPY M X
REPL AZI
COPY M X
REPL ELEV
COPY M #FREQ

MARK DONE
KILL
LINK 799
SEEK -9999
@REP 24
COPY F #DATA
@END
WIPE

;XB was unchanged
253/88/7.
While SETDISH does its thing, the ENCRFILE EXA and the data one are sitting in the SECURE host, communicating in local mode. The data EXA overwrites the values in Ember's file with the encrypted values. At the end it kills the ENCRFILE EXA, hops over to the transmitter, and sends all the contents of the file.

I shuffled some code around, mostly to make sure EXAs that are finished fall through into code that makes them stop themselves. The different use of M somehow changed the order the dish EXAs receive the data in so I had to move the COPY M instructions back to the main SETDISH code.

Next, I noticed that the encryption algorithm is still the slowest step by far. The most obvious thing I can remove from it are the two cycles to check EOF. I can't unroll that loop much, I don't have a lot of lines left, but there's another way.

Code: Select all

REPL GRABBER

VOID F
MARK NEXTDATA
@REP 2
SUBI F 9999 X
ADDI M X X
SEEK -1
TEST X < 0
TJMP ADD
SUBI X 1 F
@END
JUMP NEXTDATA

MARK ADD
ADDI X 9999 F
JUMP NEXTDATA

[...]

MARK GRABBER
REPL GRAB
NOOP
NOOP
JUMP GRABBER

MARK GRAB
GRAB 301

KILL
KILL

; Take the file to the transmission host and send all the data as before
207/100/7. When the data EXA is at the end of the file it will die, and the GRABBER EXA can take it and send the data. I needed some NOOPs to slow the replication down enough that the KILLs work properly.

In this solution, the encryption algorithm is still the slowest step. It's possible to change that, for instance by writing the raw value to F before checking if it's negative, and handling that later, when sending the data. However, that means you can't fully unroll the sender code anymore with the @REP 24, so you need TEST EOF instructions and everything, and it slows down the whole program significantly.

So, I'll end it today with top scores of 207 cycles and 63 size.

By the way, I hadn't noticed it before but there's some sort of ad in the zine, just after the satellite dish article.

Image

Sounds neat.

Image Well, what did you think?

Image

Image The first vote. And for next time...

Image Okay, no more trying to find shortcuts.
Image Time for a brute-force solution to the money problem.
Image I know you like messing around with bank systems...


Image

User avatar
Part 32 - Equity First Bank - Money Transfer

=== Trash World Inbox ===

silentsnack has some nice improvements again for KGOG-TV.
silentsnack wrote:small solution

Code: Select all

;XA
GRAB 300
LINK 800
LINK 799
VOID F
COPY F X

LINK 800
MARK A_LOOP
SUBI X #AZIM T
COPY T #MOTR
TJMP A_LOOP
LINK -1

VOID F
COPY F X
VOID F
COPY F #FREQ
WIPE

LINK 801
MARK E_LOOP
SUBI X #ELEV T
COPY T #MOTR
TJMP E_LOOP
VOID M

;XB
LINK 800
COPY 0 M
MARK KEY_RESET
GRAB 199
MARK KEY_LOOP
SUBI F 5000 X
SUBI X 5000 X
REPL CODEC
JUMP KEY_LOOP


MARK CODEC
LINK -1
GRAB 301
VOID F
ADDI X F X
DROP
LINK 800
REPL KEY_RESET

LINK 799
TEST X < 0
MULI T 5000 T
ADDI X T X
ADDI X T #DATA
327/44/102
There's a lot of smart stuff going on here. XA moves into the #AZIM host and back, which I guess takes less lines than making a REPL. It then stores the #ELEV value in X, sets the frequency, goes to move the elevation, and then messages on M when it's done.

XB grabs the key file, gets a value each round and subtracts 10000 to prevent overflow, then makes a REPL which goes back to get the data. Since the repl can use VOID F to remove values it already read, there's no need to keep track of where in the data file we are. The EXA also drops a new KEY_RESET EXA in case the main XB is at the end of the file. Usually KEY_RESET dies immediately because the file isn't available, but if it grabs it, the pointer is at the beginning of the file so no SEEKs are necessary.
silentsnack wrote:fast solution:

Code: Select all

;XA- MY AZIMUTH CAN'T BE
;THE RATE-LIMITNG STEP
LINK 800
LINK 799
LINK 800
COPY M X
MARK A_LOOP
@REP 8
SUBI X #AZIM #MOTR
@END
TEST X = #AZIM
FJMP A_LOOP

;XB- I GOT SENT TO THE
;UPLINK HOST AND NOW I'M
;STUCK BUFFERING OUTPUT
GRAB 300
VOID F
COPY F M
LINK 800
LINK 799

COPY 15 T
REPL DATA

VOID F
COPY F X
VOID F
COPY F #FREQ
WIPE
LINK 801
MARK E_LOOP
SUBI X #ELEV #MOTR
SUBI X #ELEV T
MODI 1 T #MOTR
JUMP E_LOOP


MARK DATA
MODI -2 T T
MAKE
COPY M F
COPY M F
FJMP LAST
COPY M F
REPL DATA

MARK WAIT_ALIGN
SUBI T 1 T
TJMP WAIT_ALIGN

SEEK -3
@REP 3
COPY F #DATA
@END
WIPE

MARK LAST
SEEK -2
@REP 2
COPY F #DATA
@END
WIPE
COPY M #DATA

;XC- LET'S TRANSMIT THE
;DATA IMMEDIATELY!!
GRAB 301
LINK 800
VOID F
MARK READ_LOOP
@REP 3
ADDI F M X; LOCAL
REPL ENCODE
@END
JUMP READ_LOOP

MARK ENCODE
LINK -1
TEST X < 0
MULI T 5000 T
ADDI X T X
MODE
ADDI X T M;GLOBAL

;XD- DO  STUPID SELF-
;-REFERENCING JOKES
;DREAM OF BREAKING
;THE FOURTH WALL?
LINK 800
REPL START

COPY 33 T
MARK WAIT_CLEANUP
SUBI T 1 T
TJMP WAIT_CLEANUP
KILL
KILL
KILL
KILL
GRAB 301
LINK -1
HALT

MARK KEY_OUT
SUBI X 5000 M;LOCAL
;GRAB?? OR CRASH
MARK START
GRAB 199
SUBI F 5000 X
MARK KEY_LOOP
@REP 3
REPL KEY_OUT
SUBI F 5000 X
@END
JUMP KEY_LOOP
81/99/37
A and B have to start in GLOBAL mode, C and D in LOCAL.

81 cycles... how???
Seriously, this code is very hard to understand.
For the dish, you found a way to unroll the loop by sending the difference between the expected and actual value to the #MOTR. It won't move more than one step at a time, even if you send a larger number, but once it's zero you know you're done.

Other than that this solution is mostly very well-timed parallellism. XB makes a whole bunch of copies, each of them buffering 3 values in a file and them sending it to #DATA without any cycles wasted. Somehow it all lines up.


=== Equity First Bank - Money Transfer ===

Image Well, what did you think?

Image

Image Two votes for the same option.

I'm not sure I understand it.

Image That's okay...
Image Great art is often mysterious.
Image If you want to know the truth, though, that video was just some garbage data I had lying around.
Image I just thought it would be fun to broadcast.


You have a unique sense of fun, Ember.

Image

But, I guess you were noticed.


Image

Next up, I'm gonna go to Equity First again. That's the bank where I messed with the ATMs a while ago. Hacking banks is a bit iffy but it might solve my money troubles once and for all.

Image Okay, no more trying to find shortcuts.
Image Time for a brute-force solution to the money problem.
Image I know you like messing around with bank systems...


Image

Image Another unanimous vote.

Hacking those ATMs was your idea.

Image Well, you get to do it again!
Image This time with a little more urgency.


Image
OST: Leave No Trace

The assignment:

- Move EMBER-2's new account (file 300) into checking. Then iterate over the checking accounts listed in the directory (file 199) and, in that order, transfer $1.00 from each target account to EMBER-2's account. Finally, add the file ID of EMBER-2's account file to the end of the directory.
- The keywords CREDIT and DEBIT are available in file 301.
- For more information see "Network Exploration: Equity First Bank" in the first issue of the zine.


Well, this is the same network as before, but they have disconnected all the ATMs. Perhaps I caused more trouble than I thought.

Since we didn't do anything with the files last time, let's read up on them again.

Image
In short, I shouldn't mess with the first 4 values in any account file. To add a transaction from account X to Ember, I need to add a DEBIT transaction line to account X's file, with Ember's account number and the amount. I also need to add a CREDIT transaction with account X's number and the same amount to Ember's file.

The assignment says to do this for checking accounts, so no need to touch the loans. Let's get started.

I'll need the DEBIT and CREDIT keywords, but since they're only two values I think I can bring them along in X and T, which would save an EXA or a lot of M back and forth.

Code: Select all

GRAB 301
COPY F X
COPY F T
DROP
GRAB 300
LINK 800
LINK 800
Now my EXA is in the checking host, with Credit in X, and Debit in T. It's also holding Ember's account file. To do anything more I'm gonna need REPLs.

Code: Select all

REPL DIRECTORY
COPY F T
SEEK 9999
MODE

MARK EMBERWRLOOP
COPY M F
COPY X F
COPY 1 F
COPY 0 F
COPY T M
JUMP EMBERWRLOOP
I'll get to the directory EXA in a second. It still has Debit in T, so this main EXA can now use T to hold Ember's account number. After that it switches communication mode, goes to the end of the file, waits for an account number to come in on M, writes that, followed by Debit, and 1 dollar and 0 cents. Finally, it sends Ember's account number so the other side can credit the sending account.

Code: Select all

MARK DIRECTORY
GRAB 199
COPY T X
MARK NEXTACCOUNT
COPY F T
REPL TRANSACTION
TEST EOF
VOID M
FJMP NEXTACCOUNT

COPY 300 F
KILL
The directory EXA grabs file 199 which is the directory. It moves the Debit keyword to X to make some space for testing. Then it makes a transaction EXA for each account, waiting for a message on M before it makes the next (so that the transfers happen in the right order).

At the EOF it writes Ember's file id to the directory to officially register it, and kills the EMBERWRLOOP EXA.

Image

The final part is the TRANSACTION EXA. It grabs the account file, copies the account ID to EMBERWRLOOP, and adds the $1.00 Credit transaction line to the end of the file. It messages the DIRECTORY EXA when it's done.

Note that the messages between Ember's account and the other accounts are in local mode while the "I'm done" message from the transaction EXA is in global mode. All EXAs are in the same host so it doesn't matter what modes these EXAs start in, but I need both modes to prevent messages getting mixed up.

Image

This initial solution scores 158/40/3. Top percentiles are 55, 40, and 2.

I'm pretty sure there's some smart tricks to save a couple lines. For instance, the file IDs in 199 seem to be in order so you could just loop through all of them. But, you know, I'm happy with top percentile score on size, and I'll leave it to the thread to go beyond.

Let's get the activity down instead. There's only one way to do so - remove that KILL instruction.

I can replace the end of the DIRECTORY code with

Code: Select all

COPY 300 F
MODE
COPY 0 M
Now it will send a 0 to the EMBERWRLOOP. So it has to test if the incoming message is zero and die if that's the case. However, that EXA already got X and T occupied (with CREDIT and Ember's account number). Since I don't care about size or cycles for the low-activity solution, let's just write the value to F and test on that:

Code: Select all

MARK EMBERWRLOOP
COPY M F
SEEK -1
TEST F = 0
TJMP FINISH
COPY X F
COPY 1 F
COPY 0 F
SEEK -9999
COPY F M
SEEK 9999
JUMP EMBERWRLOOP

MARK FINISH
SEEK -1
VOID F
It also seeks back to the beginning repeatedly just to send Ember's account number, and it has to delete that leftover zero from the file at the end. 197/48/2.

I don't like this slow code, so let's quickly go back to the previous 3-activity solution and look into lowering the cycle count.

The easiest improvement is to not have the transaction EXA wait so long for the Ember EXA, by moving the step where it sends the account number.

Code: Select all

MARK EMBERWRLOOP
COPY M F
COPY T M
COPY X F
COPY 1 F
COPY 0 F
JUMP EMBERWRLOOP
134/40/3

Next, we have to look into parallellism.

Code: Select all

;XA

GRAB 300
LINK 800
LINK 800
COPY M X
MODE
REPL DIRECTORY
COPY F T

MARK EMBERWRLOOP
COPY T M
JUMP EMBERWRLOOP

MARK EMBERAC
MODE
COPY M X
MODE
GRAB 300
SEEK 4
MARK EMBERACLOOP
COPY M F

COPY X F
COPY 1 F
COPY 0 F
JUMP EMBERACLOOP


MARK DIRECTORY
GRAB 199
MARK NEXTACCOUNT
COPY F T
REPL CREDIT
TEST EOF
FJMP NEXTACCOUNT

NOOP
NOOP
NOOP
NOOP
KILL

REPL EMBERAC
SEEK -9999
MARK NEXTACCOUNT2
COPY F T
NOOP
REPL GETACCOUNT
TEST EOF
FJMP NEXTACCOUNT2

COPY 300 F
NOOP
NOOP
KILL

MARK CREDIT
GRAB T

SEEK 9999
COPY M F
COPY X F
COPY 1 F
COPY 0 F

MARK GETACCOUNT
GRAB T
COPY F M

;XB

GRAB 301
SEEK 1
COPY F M
SEEK -9999
COPY F M
92/61/4.

XB first sends Debit in global mode, then Credit.

XA makes a REPL still called EMBERWRLOOP which doesn't actually write anymore, it just sends the account number repeatedly. This way, the CREDIT EXAs can just write the $1.00 transaction to all accounts at once. After that is done, we loop through 199 again, grab the files again, and send the account numbers to EMBERAC, who writes the debit lines to Ember's account.

A few more cycles can be won by preparing the Ember Ac Loop ahead of time.

Image
88/55/4

As it stands, unrolling loops doesn't do much because the M timing is rather specific and messing with it tends to break things. I do have a bunch of other ideas. For instance, preparing Ember's account file with transactions with dummy account numbers and then fixing them later might save a couple cycles. Or making use of the fact there's always at least 5 accounts. But no matter what I tried, the transactions ended up in the wrong order.

The timing is tough on this one. 88 isn't even the tenth percentile, but I'm afraid I'm going to stop it here anyway.

Image Defrauding the bank...
Image That's textbook crime, isn't it?
Image Not to worry, your secret's safe with me.


Image

Image Straight to the vote.


Image

Image

Before I move on to the next assignment, Nivas is here.

Image

Image Delivery...

Nivas hands me the medication.
This time it's the real version that was "liberated" from the drug company...
I thought it might look more impressive than the bootleg, but it's basically the same.

Image To be honest, I thought I probably wouldn't see you again.
Image Didn't think you'd be able to put that kind of money together.
Image So whatever you did... respect.

Nivas hands me another package of the medication... and another.

Image These are advance deliveries.
Image I think... I think I need to take it easy for a while.
Image The whole thing with the operation we did, those people I mentioned...
Image It got a little hot for me.
Image I'm gonna lay low for a bit.
Image I might do some reading, think about the world we live in...
Image For some reason I'm in kind of a spiritual mood lately.
Image Anyway, nice doing business with you.
Image Take care of yourself.

Nivas closes the door, leaving me with the rest of the medicine.
Interesting character.
Everyone's doing what they can in order to survive...


I hope Nivas is gonna be okay.

Well, at least my medicine problem is solved for the forseeable future.

Image For next time, there's another hacker battle coming up.

Image If you keep on winning, they'll eventually have to accept that you're back.

Image
Last edited by Carbon dioxide on Sat Jul 02, 2022 10:13 am, edited 1 time in total.

User avatar
"Wait, you're part of this too!" and "I never really left"

At this point the puzzles are far beyond my ability to ken, at least second hand, I understand what you're doing, but have no clue how I could have done it myself, so I'm just sitting back, enjoying the show, and thinking EMBER-2 needs some reality checks

User avatar
Part 33 - The Wormhole

=== Trash World Inbox ===

I finished Equity First Bank with 88, 40 and 2.
GuavaMoment wrote:

Code: Select all

XA local
GRAB 301
COPY F M
COPY F T
LINK 800
LINK 800
COPY T M
WIPE

XC local
GRAB 300
COPY F X
SEEK 999
COPY M F
LINK 800
LINK 800
COPY M T
REPL CALLER
MODE
SEEK -1
COPY F X
SEEK -1
MARK BACK
COPY M F
COPY X F
COPY 1 F
COPY 0 F
JUMP BACK
DROP
KILL
KILL
GRAB 199
SEEK 999
COPY 300 F
MARK CALLER
GRAB 199
MARK CALLING
REPL FILEHOLD
COPY F M
NOOP
JUMP CALLING
MARK FILEHOLD
GRAB M
MODE
COPY F M
SEEK 999
COPY X F
COPY T F
COPY 1 F
COPY 0 F

XB
LINK 800
LINK 800
COPY 25 T
MARK WAIT
SUBI T 1 T
TJMP WAIT
NOOP
KILL
KILL
GRAB 199
SEEK 999
COPY 300 F
There's what looks like a clumsy write of CREDIT only to grab it again later, but that exa isn't the bottleneck. I try not to use conditionals since you need registers to store other information; this means I need a timing exa to kill things later.
60/59/8. Yeah, the lack of conditionals definitely speeds things up here. Some specific timing to get the parallellism right and you get a fast solution.
silentsnack wrote:As for low-cycle solution, sending the number from accounts making withdrawals can be parallelized but at some point the JUMP operation in the file300 write routine imposes a speed bottleneck, so it's possible to save a few cycles by partially unrolling that loop.

Code: Select all

;XA
LINK 800
LINK 800
GRAB 199
COPY M X;GET OUR ID#

MARK INDEX
COPY F T
REPL WITHDRAW
TEST EOF
FJMP INDEX

COPY 300 F
@REP 6
NOOP
@END
KILL
HALT

MARK PWN
MODE
COPY T M;SEND THEIR ID#
;THEN CRASH

MARK WITHDRAW
GRAB T
COPY F T
REPL PWN
SEEK 99
COPY X F
COPY M F;GET "DEBIT"
COPY 1 F
COPY 0 F

;XB
GRAB 300
LINK 800
LINK 800
COPY M X;GET "CREDIT"
COPY F M;SEND OUR ID#
MODE
SEEK 99
MARK DEPOSIT
@REP 4
COPY M F;GET THEIR ID#
COPY X F
COPY 1 F
COPY 0 F
@END
JUMP DEPOSIT

;XC
GRAB 301
COPY F M;SEND "CREDIT"

COPY F X
COPY 17 T
REPL TIMER

MARK WHAT
COPY X M;SEND "DEBIT"
JUMP WHAT

MARK TIMER
SUBI T 1 T
TJMP TIMER
KILL
47/67/6
Another important aspect of this solution is that sending their ID is done by a REPL'd EXA, which means that the 'withdraw' EXAs aren't ever blocked by the one holding Ember's file.

Cycle count all the way down to 47, but silentsnack also managed to save another line of code.
silentsnack wrote:It's tough to optimize size since it requires making tradeoffs, like if you write "300" before scanning the account list you don't need to TEST EOF which means you can use the T register for storage, but it requires SEEK

Code: Select all

;XA
GRAB 300
COPY F X

LINK 800
LINK 800
REPL DIRECTORY
COPY M X
SEEK 99
MODE
MARK DEPOSIT
COPY M F
COPY X F
COPY 1 F
COPY 0 F
TEST MRD
TJMP DEPOSIT
KILL

MARK WITHDRAW
GRAB M
MODE
COPY F M
SEEK 99
COPY X F
COPY T F
COPY 1 F
COPY 0 F

MARK DIRECTORY
GRAB 199
SEEK 99
COPY 300 F
SEEK -99
COPY M T
MARK INDEX
REPL WITHDRAW
COPY F M
NOOP
JUMP INDEX

;XB
GRAB 301
COPY F M
COPY F M
66/39/3
Yeah. In my 40-lines solution I went for a single EXA, because COPY F M followed by COPY M X in another EXA is two lines for what could be done in one. This solution offsets that by not needing a DROP, nor needing the COPY T X to get 'DEBIT' in a safe place after handling 'CREDIT'. It also helps that the DEPOSIT EXA, which doesn't really need T for anything else could do the TEST MRD to check if we're done, while another EXA actually reads from M.


=== The Wormhole ===

Image Defrauding the bank...
Image That's textbook crime, isn't it?
Image Not to worry, your secret's safe with me.


Image

Image Everyone voted for the same answer.

Wait, you're part of this too.

Image Maybe, but they can't arrest me, can they?
Image Having a physical body is your liability.


That is a surprisingly good point.

Image

[hydroponix]: yeah same
[Ghast] I bet we all have.


While I was talking with Nivas, Ember installed something on my PC.

Image

Image Happy birthday! I bought a new piece of software for you.

Image

Image I didn't put this one up for a vote since the dialog doesn't change anyway.

It's not my birthday?

Image This is a virtual network creation tool.
Image You can use it to design your own networks.
Image Maybe you could get someone to try to hack your designs too.
Image It's all installed and ready to go...
Image The only non-pirated program on your computer.


I'd just like to interject for a moment - what you're referring to as 'pirated' is in fact free/libre...

Image In other words, we have just unlocked the level editor. Let's take a quick look.

Image

There are two tabs, the Personal Networks is where you can make your own creations, and the Remote Networks is where you can play other people's levels.


Image

If you choose to edit a level, this actually takes you out of the game and opens a javascript file in your default text editor. It has a link to the documentation, which is written from an in-universe perspective, nice.

The language is quite straightforward, you set up hosts, links between them, files, registers and test cases. You write a description and set the winning goals and that's it. Honestly, just giving people access to the underlying level design language makes a lot of sense, it's a lot less work than making a visual editor and Zach could safely assume that people who made it to this point know how to program.

Anyway, once you finish a level, you can play it yourself in the game, and once you solve it you're allowed to upload it to the Steam Workshop. That's also where you download levels from.

Image

The game even keeps histograms and top percentiles for custom levels.

A regular playthrough of this game is already time-consuming enough as it is, so I'm not planning to show off any custom levels in this LP. Time to return to the regular schedule.

The level editor "level" doesn't seem to have any outro text from Ember. Unusual, even the Redshift sandbox mode had that.


Image

However, I have now filled out the shortcuts bar to the right. From top to bottom, the Russian solitaire game, the Redshift dev kit, HACK*MATCH, and the VirtualNetwork+ level editor.


Image

Next up, a hacker battle against x10x10x

Image If you keep on winning, they'll eventually have to accept that you're back.

Image

Image One vote for "It doesn't really matter", three for "I never really left".

I never really left...

Image But you kind of did.
Image Enough to make them wonder about you.
Image That's why you need to prove yourself again.
Image Time to win.


Image
OST: Getting Started

The assignment:
To win this battle you must fill the network's hosts with as many of your EXAs as you can. Note that each pair of test runs has its own unique network layout, with bi-directional links between hosts that use the prime numbers between 2 and 13 as link IDs (2, 3, 5, 7, 11, and 13).
- Gain one point for every EXA you control in the network at the end of the battle.
- Lose one point every time one of your EXAs executes a KILL instruction.
For more information see "Hacker Battle Domination" in the second issue of the zine.


A rather different kind of hacker battle, you get points by having more EXAs at the end. The EXA limit is set to 100 which is way more than the number of spaces in the network.

They aren't kidding about the different layouts. The second test looks the same as the first but with my and x10x10x's hosts swapped. The third one looks completely different.

Image

Letting x10x10x do their thing without any EXAs on my side, their strategy is very similar to what I had in mind as well. REPL a lot of EXAs, have them try each possible link ID to spread through the network, and also REPL and pause them indefinitely to fill up each host.

Code: Select all

LINK 800

MARK REPLICATE
REPL TWO
REPL THREE
REPL FIVE
REPL SEVEN
REPL ELEVEN
REPL THIRTEEN

MARK INFINITE
REPL INFINITE

MARK TWO
LINK 2
JUMP REPLICATE

MARK THREE
LINK 3
JUMP REPLICATE

MARK FIVE
LINK 5
JUMP REPLICATE

MARK SEVEN
LINK 7
JUMP REPLICATE

MARK ELEVEN
LINK 11
JUMP REPLICATE

MARK THIRTEEN
LINK 13
JUMP REPLICATE
The main EXA makes a REPL for each possible link ID. If it's invalid the REPL will die, otherwise it will start replicating from the new host. After that it will start infinitely replicating itself.


Image

You can see in the 99th cycle that the fight is quite balanced, with x10x10x having just one EXA more than I do.


Image

In the second test I win, apparently because my EXAs managed to fill up that one host before x10x10x could make one that could jump to 13 and that whole side area.


Image

I don't know how my code differs from the opponent's but apparently this is good enough to win all but 4 tests.

But can I do better?

Well, I noticed a small problem with the infinite loop. Yes, each REPL created through it makes another one, but the original one falls through to MARK TWO and most likely dies as most hosts don't have a link 2. If I put a JUMP INFINITE directly under the REPL INFINITE, both EXAs will start replicating and I change linear growth into the much faster quadratic growth.

And that's enough to outrun x10x10x entirely, since the result is an S+ with 100 out of 100 wins.

Image You still aren't saying anything in the chat room.
Image Don't you think they might want to hear from you?


Image

Image That brings us to the first vote.

Image I've collected a lot of data now, but my understanding remains incomplete.

Image

Image And the vote for the intro of the upcoming level.

Since there's no further optimization possible for these hacker battles, time for the third nonogram.

Image

Image As always, use spoiler tags when you post your solution so others can have a go too.


Image

Now that I've posted all nonograms, here's the full page, together with the inside of the back cover. Don't mess with EXAs.

User avatar
Part 34 - TEC EXA-Blaster™ Modem

=== Trash World Inbox ===

Regallion, cardinale and biosterous submitted solutions to the nonogram. Here is cardinale's one, in a pretty shade of blue.

Image

It is an EXA, of course.


=== TEC EXA-Blaster™ Modem ===

Image You still aren't saying anything in the chat room.
Image Don't you think they might want to hear from you?


Image

Image Two votes for "Yeah, but", but "There's nothing to say" barely gets the victory at three votes.

There's nothing for me to say.

Image I have a theory...
Image You're enjoying it.
Image You get to be this mysterious silent presence.
Image I understand the appeal.


Image The curse and blessing of a silent protagonist.

Image

[=plastered] bullshit.
[x10x10x] yes
[x10x10x] :)



Image

Ember wants me to mess around with a modem now.

Image I've collected a lot of data now, but my understanding remains incomplete.

Image

Image Two votes for the bottom option, and three votes for the winner...

What are you trying to understand, exactly?

Image Everything.
Image I need more processing power.
Image Open networks are getting harder to come by.
Image Luckily, everyone's buying personal dataphones now.
Image Thanks, consumer rat race!
Image Let's get me onto those.


What are you up to now, Ember?

Image
OST: Network Exploration

The assignment:
- Using your modem, connect to each dataphone so that EMBER-2 will have a list of valid phone numbers.
- Each dataphone contains a list of the owner's contacts (file 200). The phone number of one of these dataphones is in your host (file 300), while the rest are in the contact list of another dataphone.
- Note that each dataphone (aside from the first) will appear in exactly one other dataphone's contact list, in such a way that you can find them all without getting stuck in a loop.
- For more information see "Hacker Skills: Modem Control at the Direct Level" in the second issue of the zine.


Alright, let's grab the zine first.

Image

A lot of general information, but the only part that truly matters is that you dial by sending the number to the #DIAL register one digit at a time, and you hang up by sending it a -1.

At a glance, all the numbers in the files seem to be 11 digits long. That'll be helpful.

Image

Just sending the 11 digits from file 300 to #DIAL works to make a connection.

Image This isn't in the game but feel free to imagine it makes this kind of sound.

The connections always use the 800 and -1 link IDs. And the #DIAL register will ignore any further inputs until you send it a -1.
Importantly, if you have an EXA in a remote host and you hang up, no communication with that EXA is possible. Not even global M communication. That makes a lot of sense, they're truly disconnected hosts now, but it's not something we had to deal with before.

That leaves one part of the assignment that isn't obvious: if each number occurs only once (so you don't get loops), how is it possible that some files have multiple numbers?
Well, by messing around with the modem a bit I found out that those are numbers that aren't accessible from my location. The modem will simply say "connection refused" and hang up.

Code: Select all

GRAB 300
LINK 800

MARK NEXT
@REP 11
COPY F #DIAL
@END
REPL M

@REP 11
COPY M F
@END

COPY -1 #DIAL
SEEK -11
JUMP NEXT


MARK M
LINK 800
GRAB 200
SEEK 1
@REP 11
COPY F M
@END
Starting out simple, this EXA dials the one number I know, then the REPL sends back the first number from the remote host and the main EXA dials that one. This works until you get an inaccessible number, at which point the program gets stuck.

So, I need to read all the numbers from the remote hosts (up to four numbers per host). And then dial them one by one, saving new numbers I encounter, while somehow keeping track of which ones I already tried.

I think I have a general idea on how to tackle that. Let's see if it works.

Code: Select all

GRAB 300
LINK 800
SUBI X 11 X

MARK NEXT
@REP 11
COPY F #DIAL
@END
REPL M

ADDI X 11 X
NOOP
NOOP
TEST MRD
FJMP NEXT

SEEK 9999
MARK GETNEXT

@REP 11
COPY M F
@END

SUBI X 11 X
NOOP
TEST MRD
TJMP GETNEXT

COPY -1 #DIAL
SEEK X
JUMP NEXT

MARK M
LINK 800
GRAB 200
MARK NEXTNR
SEEK 1
@REP 11
COPY F M
@END
JUMP NEXTNR
The EXA LINKs to the modem. It does some stuff with X which will come into play later. Then it dials the first number, and REPLs to the M EXA.

The M EXA is very simple, it LINKs to the remote host and sends all data over M, only skipping the contact names. It'll die when it reaches the end of the file.

Ignore the main EXA's ADDI X instruction for now - what matters is, it checks if another EXA is sending on M. If so, it copies the whole phone number to the end of its file.

Now it subtracts 11 from X. X contains the value to use in a SEEK instruction to get to the oldest undialed number.
It does another TEST MRD to see if the remote EXA has another number to send, and if so, it stores it too and subtracts another 11 from X.

Once the remote EXA stops, the main one hangs up the modem and SEEKs to X to start dialing the next number. This is where the other X manipulations comes in. Every time it dials a number, it adds 11 to X again so that the index offset to the oldest undialed number is still correct. That's also why I needed the initial SUBI X 11 X, otherwise the value will be off by one phone number for the entire run.

Note that I put the X manipulation in places where I was waiting for MRD anyway. Doesn't cost any extra cycles that way.

This code works in the sense that it connects to every remote host. However, when the main EXA reaches the end of the file and there's no EXAs left sending data, it'll die and drop its file in the modem, which counts as leaving a trace.

That's easily resolved with a cleanup EXA. Add a REPL CLEANUP to the above code, just after the initial LINK 800, and add the following snippet to the bottom.

Code: Select all

MARK CLEANUP
COPY 466 T
MARK WAIT
SUBI T 1 T
TJMP WAIT
GRAB 300
WIPE
It's a basic countdown that waits for a while, then GRABs the file and WIPEs it. 466 is the minimal value at which all tests pass.

Image

The top percentiles are 867, 34 and 9. Except for size, I'm closer than I thought. But what improvements are possible?

At first, I thought I could reduce activity by one more. The goal is to connect to each dataphone. Technically I wouldn't need to send an EXA into the last one, since I got all relevant numbers already.
Well, I tried this and it doesn't work. Turns out the tests are coded to only accept the solution if you actually move an EXA into each remote host. So, 9 is the best possible score here.


Image

Moving the cleanup to its own EXA saves a single cycle (in exchange for an activity point). There's a bigger gain by having M send a message immediately as it arrives, before it starts handling the file. It means the top TEST MRD doesn't have to wait as long for it to get ready. 919/65/10.

The EXA that goes into the last remote host still sends all numbers and the main EXA still tries dialing all of them. There's no point in doing so - as soon as there's an EXA in the last host we can stop.

To do so, I rewrote XB to have a counter of how many EXAs reached the foreign hosts:

Code: Select all

;XB LOCAL
LINK 800
COPY 8 T

MARK AGAIN
SUBI T M T
TJMP AGAIN

KILL
GRAB 300
WIPE
LINK 800
KILL
Once it hits zero, it KILLs XA, WIPEs the file, and KILLs that one EXA that is still trying to send a number.

XA also needs an addition to make this work. I decided to place it directly after the VOID M, because at that point we know an EXA just reached the remote host - and that EXA is busy grabbing the file so I have a spare cycle or two. Since XB is in local mode, my first thought was to put MODE; COPY 1 M; MODE there but that would delay it by 3 cycles. Instead, I made a special REPL to send the LOCAL message.

Code: Select all

;XA GLOBAL

GRAB 300
LINK 800
SUBI X 11 X

MARK NEXT
@REP 11
COPY F #DIAL
@END
REPL M

ADDI X 11 X
TEST MRD
FJMP NEXT
VOID M
REPL MSG

SEEK 9999
MARK GETNEXT

@REP 11
COPY M F
@END

SUBI X 11 X
NOOP
TEST MRD
TJMP GETNEXT

COPY -1 #DIAL
SEEK X
JUMP NEXT

MARK M
LINK 800
COPY 0 M
GRAB 200
MARK NEXTNR
SEEK 1
@REP 11
COPY F M
@END
JUMP NEXTNR

MARK MSG
MODE
COPY 1 M
865/72/13, one cycle below top percentile.

There's a theoretical next improvement, parallellism. That is, if a remote host has multiple phone numbers, I could try dialing the first one immediately, and leave the EXA in the remote host to make a copy of the other phone numbers. Then later I go retrieve them and try dialing them.
This is very complicated though, since the remote EXAs can't just phone home, the main modem one has to dial old hosts again to see if there's an EXA there with additional phone numbers. I suspect it's slower overall, and it might not even fit in the lines of code limit.


Speaking of size, what improvements are possible there? Of course, I can remove the loop unrolls I started with.

Code: Select all

GRAB 300
LINK 800
SUBI X 11 X

MARK NEXT
TEST EOF
TJMP WIPE
COPY 11 T
MARK DIALLP
COPY F #DIAL
SUBI T 1 T
TJMP DIALLP
REPL M

ADDI X 11 X
NOOP
TEST MRD
FJMP NEXT

SEEK 9999
MARK GETNEXT
VOID M

COPY 11 T
MARK WRITELP
COPY M F
SUBI T 1 T
TJMP WRITELP

SUBI X 11 X
TEST MRD
TJMP GETNEXT

COPY -1 #DIAL
SEEK X
JUMP NEXT


MARK M
LINK 800
GRAB 200
MARK NEXTNR
COPY F M
JUMP NEXTNR

MARK WIPE
WIPE
I started with my initial working solution and removed all unrolls. The dial loop and write-to-file loop now use a counter to run each 11 times.
The read loop in F has been changed more significantly. I don't skip the contact's name anymore, so that's just a single 3-line non-conditional loop. This requires a VOID M on the writing EXA but that beats adding conditions.
Finally, the cleanup loop was a lot of code so I replaced that with a TEST EOF in the main loop. Slow, but it works. Because the timing is different now I could also remove a couple NOOPs. 1760/38/9.

4 over the top percentile. I'll, uh, leave the remaining improvements as an exercise to the reader.

Image There's a ton of dataphones out there.
Image Not very powerful individually, but tied together they work somewhat decently.
Image I'll just be very... distributed.


Image

Image You know the drill by now.

Image

[deadlock] hah
[deadlock] if you wanted everyone to leave maybe


Image

There is a knock on the door.

Image

Did you do something with your hair?

Image Hey. I'm not bothering you am, I?

Isadora looks upset.
Did something happen?

Image You remember I used to work for Last Stop? Started as a cashier, worked my way up?
Image I always knew they were close-knit.
Image But I found out it's much worse than that.
Image It's a full-on cult.
Image I don't just mean everyone believes in the company. They believe the whole world isn't real!
Image They say we're all trapped in some kind of fake reality? I don't know.

Fake reality?
That sounds oddly familiar...
Has someone told me this before?

Image I wouldn't care if that's all they believed.
Image The real problem is they think they can force their way out with a big enough explosion.
Image So they're refining materials to make a bomb... a nuclear bomb.
Image Right next to piles of old snacks...

Wait, what?
Is she serious?
What is this world?

Image I have the number for the warehouse where it's all set up.
Image If I give it to you, you could do something, right?
Image I need to rest... I'm going to go lie down...


Isadora leaves.

So many questions.
Last Stop... a cult? And her, in the middle of it...?
I just need to take this one moment at a time.
I'll drive myself crazy if I try to figure it all out.


===

Image Let me see if I understood that correctly.
Image Last Stop, the convenience store chain, is actually a front for a cult.
Image They believe the world isn't real, and are in the process of creating a nuclear weapon in order to escape it.
Image And now you're going to attempt to take care of it yourself, instead of alerting the authorities like a normal person.


Image

User avatar
As a quick PSA, in the next month or two I got a lot of real life stuff going on so my update schedule might be a bit more sporadic than you're used to.

User avatar
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 wrote:

Code: Select all

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 went for a completely different low cycle solution.
silentsnack wrote:

Code: Select all

;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
775/79/21
A big timesaver here is to compress two digits into only one M call, by simply putting one in the tens place and the other in the ones place. That takes some extra cycles to compress/decompress but since that can be done in parallel while an M call by itself always takes two cycles it's quite fast. The other trick is to have the 'main' EXA use replication to run some steps in parallel. For instance, it makes a clone, then goes into the remote connection itself to check if it's valid. If so it LINKs back and starts copying phone numbers, otherwise the clone grabs the main phone number file and takes over. This is made possible by storing the offset of the oldest undialed number in X.

Then, in the greatest collaboration of the century, GuavaMoment combined the fixed area code solution with silentsnack's parallellism solution.
GuavaMoment wrote:

Code: Select all

;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
676/67/21. XB was changed to a simple timer to speed up an edge case in one specific test. 'note 1' is about how that NOOP can be removed if you can unroll the main READ loop, but this doesn't seem possible within the max size constraint.

Finally, silentsnack also posted a solution that only takes 23 lines of code.
silentsnack wrote:

Code: Select all

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
3879/23/272
It's much lower than what I got and that's for good reason, it uses some clever tricks. It creates a new REPL for EVERY digit, which dials that digit and then checks if there's a connection. If so, the main EXA jumps into writing mode, otherwise it tries with the next digit. This always works - if a full number can't be dialed the modem resets automatically, so as long as you have all the phone numbers in order in the file you don't need extra logic and you can just start dialing the next number immediately. The writer also doesn't bother skipping the contact's name. If the modem is idle, sending that to the modem doesn't do anything and is safe. The only special case is hanging up after a successful connection. This is established by putting a -1 at the start of the file whenever a connection has been made. It does this "too often" but that's the price for low size optimization.

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 ===

Image There's a ton of dataphones out there.
Image Not very powerful individually, but tied together they work somewhat decently.
Image I'll just be very... distributed.


Image

Image 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?

Image I'm not sure.
Image I'll have to try it out and see.
Image Let's continue.


Image

C'mon Ember, you almost got us discovered.

Image

Last time, Isadora told us about the Last Stop cult. I need to do something about it.

Image Let me see if I understood that correctly.
Image Last Stop, the convenience store chain, is actually a front for a cult.
Image They believe the world isn't real, and are in the process of creating a nuclear weapon in order to escape it.
Image And now you're going to attempt to take care of it yourself, instead of alerting the authorities like a normal person.


Image

Image Two votes for "The authorities won't help", four for "Nothing about this is normal".

Nothing about this is normal.

Image True.
Image Well, go on, have your fun.


Image
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: Select all

LINK 800
REPL READER
LINK 798
REPL NEXT_CEN
MAKE
JUMP POWEROFFLP


MARK READER
LINK 799
MAKE
For the initial setup, I LINK into the Snaxnet inventory host, create a reader which goes to the status host and makes a file for later use, and send another EXA to the first centrifuge. That EXA makes another REPL for the next centrifuge, makes a file and jumps into the POWEROFFLP.

Code: Select all

MARK NEXT_CEN
ADDI 1 X X
LINK 800
REPL NEXT_CEN
MAKE
MARK POWEROFFLP
Skipping the reader logic for now, the NEXT_CEN loop adds one to X, links to the next centrifuge, REPLs so that every centrifuge gets occupied. Then it makes a file and also falls through into the POWEROFFLP code. The end result is that every centrifuge host has an EXA, each one holding an empty file, and X stores the number of the centrifuge it's at. The REPL from the last centrifuge will just LINK to nowhere and die.

Code: Select all

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
Here is the trick that allows me to get a low activity score. The reader will eventually send the number of the centrifuge to disable over M, and it will be picked up by a RANDOM centrifuge EXA. It temporarily writes the value to F, and tests if the value is intended for itself. If not, it forwards it to another random EXA. Eventually it'll reach the right EXA by pure luck, triggering the DOIT jump which causes the EXA to turn off the centrifuge and send a -1 over M to tell the READER it's done. This -1 can also be picked up by a centrifuge EXA but those will just forward it. Every time a centrifuge goes down and its EXA also shuts down, there are less EXAs where the message can end up so it gets faster.
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: Select all

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
Here is the first half. The value of the first centrifuge goes into X, the rest goes into F. Then the loop compares the value in X to the one in F. If F has the lower value it is deleted, if it is higher, the value is copied to X and also deleted from F. Once EOF is reached, X contains the highest value and F is empty - which is convenient for the next round. If X is zero that means all centrifuges have shut down and this EXA can WIPE its file and halt.

Code: Select all

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
As for finding which centrifuge it is - there are lots of ways to keep track of that but I just decided to test the value in X against every register and hardcode the numbers. It's not the prettiest code but it's hell of a lot easier than keeping track of where you are in the file while also updating it.

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.

Image

The top percentiles sit at 89, 44, 7.

Image

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: Select all

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
The code that detects the highest value is unchanged, but instead of having EXAs everywhere, the main one makes a WRITER whenever there's still a centrifuge to shut down. It then walks to the right centrifuge. I tried some alternatives, such as one permanently listening on M, or not using M at all and only making the WRITER once the centrifuge ID was determined and stored in X but those were all slightly slower. Surprisingly, this code runs at 352/63/22 which is only barely faster than my initial solution.

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: Select all

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
198/77/22
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: Select all

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
374/50/22. I store all 5 values in a file, then put the highest value in X. Then in the AGAIN loop I send 1 to M for each value in the file until I reach the value that matches the highest. The WRITER starts with -1 in X, so by adding M to it until the main EXA stops sending, it'll hop 0 spaces for ZGC0, 1 for ZGC1 and so on.

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: Select all

;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
314/44/22. The main gain is combining the two loops in the WRITER EXA, to LINK as soon as it gets a message on M. By sending the link ID (800) on M, I can even just use LINK M. I had to turn the loop that sends data to M (SENDLINK in this code) upside down to prevent it sending anything to M if centrifuge zero needs to be turned off. Also, since the M that indicates the WRITER is done didn't have any useful value, I combined that with setting X back to 0 at the top. This requires one line in XB to get the program going, but since it saves several jump statements it's worth it.

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.

Image So, the part about the world not being real...
Image I think maybe they were onto something.


Image

Image And that brings us to our first vote. As for next time...

Image More phage problems, huh?
Image There's a rumor that the phage originally came from a research lab...
Image A human-machine interface experiment that found its way into the wild.


Image

User avatar
Part 36 - Visual Cortex

=== Trash World Inbox ===

Last time, I destroyed Snaxnet's nuclear centrifuges, with high scores of 198, 44 and 7.

Let's see what improvements the thread came up with.
GuavaMoment wrote:After receiving a signal over M, create 5 exas, one for each centrifuge. Test to see if your centrifuge has the highest pressure, otherwise die. Yes, there's only room for 4 exas, but one will die soon enough for the 5th to enter the testing room. Then go turn off your centrifuge and send a done signal. 136/97/22.

Code: Select all

LINK 800
LINK 799
MARK REPLS
VOID M
REPL 1
REPL 2
REPL 3
REPL 4

COPY #ZGC0 X
TEST X = 0
TJMP DIE
TEST #ZGC1 > X
TJMP DIE
TEST #ZGC2 > X
TJMP DIE
TEST #ZGC3 > X
TJMP DIE
TEST #ZGC4 > X
TJMP DIE
REPL REPLS
LINK -1
LINK 798
JUMP DIE
MARK 1
COPY #ZGC1 X
TEST X = 0
TJMP DIE
TEST #ZGC0 > X
TJMP DIE
TEST #ZGC2 > X
TJMP DIE
TEST #ZGC3 > X
TJMP DIE
TEST #ZGC4 > X
TJMP DIE
REPL REPLS
LINK -1
LINK 798
JUMP POWEROFF1
MARK 2
COPY #ZGC2 X
TEST X = 0
TJMP DIE
TEST #ZGC1 > X
TJMP DIE
TEST #ZGC0 > X
TJMP DIE
TEST #ZGC3 > X
TJMP DIE
TEST #ZGC4 > X
TJMP DIE
REPL REPLS
LINK -1
LINK 798
JUMP POWEROFF2
MARK 3
COPY #ZGC3 X
TEST X = 0
TJMP DIE
TEST #ZGC1 > X
TJMP DIE
TEST #ZGC2 > X
TJMP DIE
TEST #ZGC0 > X
TJMP DIE
TEST #ZGC4 > X
TJMP DIE
REPL REPLS
LINK -1
LINK 798
JUMP POWEROFF3
MARK 4
COPY #ZGC4 X
TEST X = 0
TJMP DIE
TEST #ZGC1 > X
TJMP DIE
TEST #ZGC2 > X
TJMP DIE
TEST #ZGC3 > X
TJMP DIE
TEST #ZGC0 > X
TJMP DIE
REPL REPLS
LINK -1
LINK 798
LINK 800
MARK POWEROFF3
LINK 800
MARK POWEROFF2
LINK 800
MARK POWEROFF1
LINK 800
MARK DIE
COPY 0 #POWR
COPY 999 M

;XB
COPY 999 M
Snaxnet has been a play on words with stuxnet the entire time, a real life virus made to shut down nuclear centrifuges. I'm sure Zach felt really clever designing this ridiculous challenge for a joke.
Yeah. This code needs some repetition because of the hardcoded register addresses. By having each REPL check if its value is the highest you can get more parallellism than with my solutions.

silentsnack then had several more speed improvements, starting with this one.
silentsnack wrote:

Code: Select all

;XA
LINK 800
LINK 799
MARK 0
TEST #ZGC1 < #ZGC0
FJMP 1
TEST #ZGC2 < #ZGC0
FJMP 2
TEST #ZGC3 < #ZGC0
FJMP 3
TEST #ZGC4 > #ZGC0
MULI T 4 T
ADDI 7 T M
ADDI T 1 T
JUMP WAIT

MARK 1
TEST #ZGC2 < #ZGC1
FJMP 2
TEST #ZGC3 < #ZGC1
FJMP 3
TEST #ZGC4 > #ZGC1
MULI T 3 T
ADDI 8 T M
ADDI T 1 T
JUMP WAIT

MARK 2
TEST #ZGC3 < #ZGC2
FJMP 3
TEST #ZGC4 > #ZGC2
MULI T 2 T
ADDI T 9 M
ADDI T 2 T
JUMP WAIT

MARK 3
TEST #ZGC4 > #ZGC3
ADDI T 10 M
ADDI T 3 T

MARK WAIT
SUBI T 1 T
TJMP WAIT
JUMP 0

;XB
COPY 5 X
LINK 800
LINK 798

MARK LOOP
MODI -1 X X
COPY M T
REPL LOOP

MODI T 7 #POWR
LINK 800
MODI T 8 #POWR
LINK 800
MODI T 9 #POWR
LINK 800
MODI T 10 #POWR
LINK 800
MODI T 11 #POWR

;XC
COPY 40 T
MARK WAIT
SUBI T 1 T
TJMP WAIT
LINK 800
LINK 799
KILL
95/63/27
XA does some comparisons and based on that jumps into one of the branches. It uses the final T value to craft a specific value to send on M, which is used in MODI instructions in XB to send a zero to the exact right centrifuge. XA also uses the T value to decide how many cycles it needs to wait before XB is done. XB uses X to kill itself after shutting down all centrifuges, while XC shuts down XA more quickly than it could itself.
silentsnack wrote:

Code: Select all

;XA
LINK 800
LINK 799
COPY 3 X
MARK 0
TEST #ZGC1 < #ZGC0
FJMP 1
TEST #ZGC2 < #ZGC0
FJMP 2
TEST #ZGC3 < #ZGC0
FJMP 3
TEST #ZGC4 > #ZGC0
MULI T 4 T
REPL NEXT

LINK -1
LINK 798
COPY T #POWR
LINK 800
LINK 800
LINK 800
LINK 800
SUBI 4 T #POWR
HALT

MARK 1
TEST #ZGC2 < #ZGC1
FJMP 2
TEST #ZGC3 < #ZGC1
FJMP 3
TEST #ZGC4 > #ZGC1
MULI T 3 T
REPL NEXT

LINK -1
LINK 798
LINK 800
COPY T #POWR
LINK 800
LINK 800
LINK 800
SUBI 3 T #POWR
HALT

MARK 2
TEST #ZGC3 < #ZGC2
FJMP 3
TEST #ZGC4 > #ZGC2
MULI T 2 T
REPL NEXT

LINK -1
LINK 798
LINK 800
LINK 800
COPY T #POWR
LINK 800
LINK 800
SUBI 2 T #POWR
HALT

MARK 3
TEST #ZGC4 > #ZGC3
ADDI T 2 T
REPL NEXT

LINK -1
LINK 798
LINK 800
LINK 800
LINK 800
MODI 2 T #POWR
LINK 800
MODI 3 T #POWR
HALT

MARK NEXT
MODI -1 X X
ADDI T 1 T
MARK WAIT
SUBI T 1 T
TJMP WAIT
JUMP 0

;XB
COPY 22 T
LINK 800
LINK 798
REPL WAIT

MARK UGH
LINK 800
SUBI T 1 T
REPL UGH

MARK WAIT
NOOP
SUBI T 1 T
TJMP WAIT

COPY 0 #POWR
75/87/32
This 75 cycles solution works somewhat similarly to the previous one. Now, XA is responsible for turning off the centrifuges. The main reason this is so fast is because XB puts a REPL of itself in each centrifuge host, with the T timers exactly synced such that all of them trigger the COPY 0 #POWR simultaneously, just barely after the 4th centrifuge is turned off. XA knows to stop after four rounds too, so you skip checking which centrifuge is the final remaining one.
silentsnack wrote:

Code: Select all

;XA
LINK 800
LINK 799

;BEHOLD! SPAGHETTI!
MARK 0
TEST #ZGC1 < #ZGC0
FJMP 1
TEST #ZGC2 < #ZGC0
FJMP 2
TEST #ZGC3 < #ZGC0
FJMP 3
TEST #ZGC4 > #ZGC0
REPL KILL_0
FJMP WAIT1
JUMP WAIT5

MARK 1
TEST #ZGC2 < #ZGC1
FJMP 2
TEST #ZGC3 < #ZGC1
FJMP 3
TEST #ZGC4 > #ZGC1
REPL KILL_1
FJMP WAIT2
JUMP WAIT5

MARK 2
TEST #ZGC3 < #ZGC2
FJMP 3
TEST #ZGC4 > #ZGC2
REPL KILL_2
FJMP WAIT3
JUMP WAIT4

MARK 3
TEST #ZGC4 > #ZGC3
REPL KILL_3
FJMP WAIT4
NOOP

;LOOKING SILLY ENOUGH?
MARK WAIT5
NOOP
MARK WAIT4
NOOP
MARK WAIT3
NOOP
MARK WAIT2
NOOP
MARK WAIT1
NOOP
JUMP 0

;WHY
MARK KILL_0
LINK -1
LINK 798
COPY T #POWR
LINK 800
LINK 800
LINK 800
LINK 800
DIVI 0 T #POWR

MARK KILL_1
LINK -1
LINK 798
LINK 800
COPY T #POWR
LINK 800
LINK 800
LINK 800
DIVI 0 T #POWR

MARK KILL_2
LINK -1
LINK 798
LINK 800
LINK 800
COPY T #POWR
LINK 800
LINK 800
DIVI 0 T #POWR

MARK KILL_3
LINK -1
LINK 798
LINK 800
LINK 800
LINK 800
COPY T #POWR
LINK 800
DIVI 0 T #POWR

;XB
LINK 800
COPY 16 T
REPL CASCADE

;SWITCHBOARD CLEANUP
LINK 799
JUMP WAIT

;WIPE UR CENTRIFUGES
MARK CASCADE
LINK 798
NOOP
REPL WAIT

MARK UGH
LINK 800
SUBI T 1 T
REPL UGH

MARK WAIT
NOOP
SUBI T 1 T
TJMP WAIT

KILL
COPY 0 #POWR
62/100/40
This final improvement consists of a bunch of smaller optimizations of the previous solution, in exchange for harder to read code. Most notably, calculating the delay countdown has been removed in favour of variable delay timing caused by specific jumps. Also, XB is now responsible for stopping XA after shutting down 4 centrifuges.


silentsnack also improved the low lines solution.
silentsnack wrote:It's possible to save a lot of code if you reuse lines, and still end up running quickly. This time around I haven't optimized all that much getting the solutions working to implement whichever ideas I was going for.

Like the fact that the hardware registers are so annoying here's a solution that only includes one instance of ["#ZGC"]

Code: Select all

LINK 800
MARK MAIN_LOOP
LINK 799
MAKE
@REP 5
COPY #ZGC@{0,1} F
@END
LINK -1
LINK 798

MARK MAX
SEEK -1
COPY F X
SEEK -5
MARK PARSE
TEST F > X
TJMP MAX
TEST EOF
FJMP PARSE

SEEK -5
MARK FORWARD
TEST F = X
TJMP STOP
LINK 800
JUMP FORWARD

MARK STOP
WIPE
DIVI 0 X #POWR
MARK REVERSE
LINK -1
REPL REVERSE
JUMP MAIN_LOOP
374/33/49
Only having one #ZGC instance doesn't reduce the number of lines - the @REP gets unrolled. But it does make for much cleaner code, I agree. This code is neat in its simplicity. It writes the values into a file, and then finds the largest value in that file. To keep the code short it has to recheck some old values in the file, but once the TEST EOF is true, X has the largest value. By testing how many steps that value sits from the start of the file you know which centrifuge you need to disable. Then LINKing back by using REPLs to check if we're in the host with link id 799 yet, and a clever use of a DIVI to shut down the EXA if the highest value of all registers is 0 and that's all we need.


=== Mitsuzen HDI-10 - Visual Cortex ===

Image So, the part about the world not being real...
Image I think maybe they were onto something.


Image

Image Two votes this time, one for "That's ridiculous" and one for "Not you, too". I'll roll a die to break the tie.

Not you, too...

Image They didn't have the right idea about how to deal with it, of course, but...
Image There's something I've been suspicious about for a while now.
Image I'm going to have to do some more tests.


Image

Image

My eye is acting up. Must be the phage spreading...

Image More phage problems, huh?
Image There's a rumor that the phage originally came from a research lab...
Image A human-machine interface experiment that found its way into the wild.


Image

Image Two votes for the same option.

Can we talk about this after I fix my eye?

Image It's just funny...
Image I remember some of that research.
Image It was happening right next door to us.


"Us?" Where do you come from?

Image
OST: EXA Power

The assignment:
- Read a value from each of the optic nerves present and write the correct value to the nerve that runs deeper into your visual cortex (V-CTX). To determine the value that should be written, count the number of values read that are greater than -55, multiply that count by 5 and then subtract 75. Repeat ad infinitum.
- It is not necessary to leave no trace. Your EXAs should be written to operate indefinitely.
- For more information see "Debugging the Phage" in the first issue of the zine.


Alright, to do this I'll certainly need an EXA in each host that has a nerve. Let's first get them out there.

Code: Select all

LINK 800
REPL GORIGHT
LINK 1
REPL WRITER
REPL GORIGHT
LINK 1

MARK GORIGHT
REPL READER
LINK -3
JUMP GORIGHT

MARK READER
VOID M

MARK WRITER
LINK 3
VOID M
This is a decently clean way to do that. I move the main EXA northeast, while REPLing a GORIGHT one in each host. Those will each create a reader and then make right-going REPLs in a loop. By choosing this order I can also drop in a REPL WRITER to get an EXA in the visual cortex as well.

The VOID Ms which I used to pause the EXAs now need to be replaced by real code. A naive approach might look like this.

Code: Select all

LINK 800
REPL GORIGHT
LINK 1
REPL WRITER
REPL GORIGHT
LINK 1

MARK GORIGHT
REPL READER
LINK -3
JUMP GORIGHT

MARK READER
TEST #NERV > -55
COPY T M
JUMP READER

MARK WRITER
LINK 3

MARK WRITE
COPY 0 X
@REP 9
ADDI M X X
@END
MULI X 5 X
SUBI X 75 #NERV
JUMP WRITE
Sending the count of values that meet a condition is easy, just add up the results from T. But can you see why this code won't work? It's because the READERs aren't waiting for anything, so the first reader will be sending again before the writer has polled all the readers. You get incorrect, messy data.

There's a lot of ways to solve this. Let's start with a way that keeps the code at minimal Activity, even if it's slow.

Code: Select all

LINK 800
REPL GORIGHT
LINK 1
REPL WRITER
REPL GORIGHT
LINK 1

MARK GORIGHT
REPL READER
LINK -3
JUMP GORIGHT

MARK READER
TEST #NERV > -55
COPY T M

COPY 5 T
MARK WAIT
SUBI T 1 T
TJMP WAIT

JUMP READER

MARK WRITER
LINK 3

MARK WRITE
COPY 0 X
@REP 9
ADDI M X X
@END
MULI X 5 X
SUBI X 75 #NERV
JUMP WRITE
All that's needed to make everything line up again is a wait loop in the READER. 453/34/10

Image

Today we're battling top percentiles of 334, 27 and 10.

To get the size down to 29, I can roll up that ADDI loop, using T as a counter. Also, that multiply-by-five step can be done when sending T to M.

Code: Select all

LINK 800
REPL GORIGHT
LINK 1
REPL WRITER
REPL GORIGHT
LINK 1

MARK GORIGHT
REPL READER
LINK -3
JUMP GORIGHT

MARK READER
TEST #NERV > -55
MULI 5 T M

COPY 13 T
MARK WAIT
SUBI T 1 T
TJMP WAIT

JUMP READER

MARK WRITER
LINK 3

MARK WRITE
COPY 0 X
COPY 9 T

MARK LOOP
ADDI M X X
SUBI T 1 T
TJMP LOOP

SUBI X 75 #NERV
JUMP WRITE
This low cycle solution is much slower, at 934/29/10.


To make a faster solution, I first tried to see if I can have a row of EXAs traverse the grid like lemmings, each one reading every value in turn, so that you can basically write to the visual cortex every cycle. That's not possible - the incoming #NERV registers don't get a new value until after a write to the visual cortex, not after a read like in many other assignments. That fact makes the puzzle much harder.

That means that if I want to use parallelism with the bottleneck being that single write, I need to get the data from all those nerves to the writer EXA as fast as possible. The best way to do that seems to optimize M.

As we've seen in earlier updates, one way to do so is to send two data points with one M call. Since an M call takes two cycles, if you can do this without wasting another cycle somewhere it will always be faster than sending a single data point with one M call.


So I need to make one EXA responsible for at least two nerves. I tried a whole bunch of different designs. For example, you could have 3 reader EXAs, each responsible for an entire row of 3 nerves. That solution wasn't any faster than what I already had.
Another design would be to have each EXA responsible for 2 nerves. Of course, with 9 nerves that won't quite work out.

I guess you could have 4 EXAs, each responsible for two nerves, and then a 5th one responsible for both reading the last nerve and writing. However, if you try you will run into a topological issue. It's not possible to divide the tiles of this grid, including the visual cortex nerve, into adjacent pairs. So one EXA would have to walk further for no reason. It's possible the result is still faster than what I have now but I didn't try.

Instead I decided to divide the responsibilities as such:
Image

Even though an EXA responsible for 3 nerves seems slower, I figured I could get away with using one of these because it isn't possible for all of them to send on M at once anyway.

Code: Select all

LINK 800

REPL RIGHT
REPL SYNC
LINK 1
REPL LRLOOP
REPL WRITER
LINK 1
JUMP LRLOOP

MARK WRITER
LINK 3

MARK WRITE
COPY -75 X
ADDI M X X
ADDI M X X
ADDI M X X
ADDI M X #NERV
JUMP WRITE

MARK SYNC
NOOP
NOOP
NOOP


MARK LRLOOP
TEST #NERV > -55
COPY T X
LINK -3
TEST #NERV > -55
ADDI X T X
MULI X 5 M
LINK 3
NOOP
NOOP
NOOP
NOOP
JUMP LRLOOP

MARK RIGHT
LINK -3
LINK -3

MARK OLOOP
TEST #NERV > -55
COPY T X
LINK 1
TEST #NERV > -55
ADDI X T X
LINK 1
TEST #NERV > -55
ADDI X T X
MULI X 5 M
LINK -1
LINK -1
JUMP OLOOP
392/50/304

And it works. The LRLOOP handles the EXAs that have two nerves, the OLOOP is for the three nerves one. At the start, the bottom-most LR EXA jumps to SYNC where it waits a bit. If I don't do that, it keeps trying to send early and I have to make all EXAs wait every cycle. Wasting a couple cycles at the start to make it sync up seems the better plan here.

After sending, both the LRLOOP and OLOOP take their EXAs back to their starting hosts and the whole process repeats. Those NOOPs in the LRLOOP are necessary to keep it in sync with the OLOOP.

Is there a way to speed up the OLOOP? Well, it'd be tight but what if that EXA goes back and forth, reading both ways?

Code: Select all

LINK 800

REPL RIGHT
REPL SYNC
LINK 1
REPL LRLOOP
REPL WRITER
LINK 1
JUMP LRLOOP

MARK WRITER
LINK 3

MARK WRITE
COPY -75 X
ADDI M X X
ADDI M X X
ADDI M X X
ADDI M X #NERV
JUMP WRITE

MARK SYNC
NOOP

MARK LRLOOP
TEST #NERV > -55
COPY T X
LINK -3
TEST #NERV > -55
ADDI X T X
MULI X 5 M
LINK 3
NOOP
NOOP
JUMP LRLOOP

MARK RIGHT
LINK -3
LINK -3

MARK OLOOP

TEST #NERV > -55
COPY T X
LINK 1
TEST #NERV > -55
ADDI X T X
LINK 1
TEST #NERV > -55
ADDI X T X
MULI X 5 M
NOOP

TEST #NERV > -55
COPY T X
LINK -1
TEST #NERV > -55
ADDI X T X
LINK -1
TEST #NERV > -55
ADDI X T X
MULI X 5 M

JUMP OLOOP
A single NOOP is needed before OLOOP starts reading again, so that the WRITER has time to finish up. That same cycle is wasted after the return by the JUMP OLOOP. This change also means both the initial SYNC and the NOOPs in the LRLOOP change to keep things synced up.

And... this code runs at 334/54/245. That is top percentile speed.

I think that is enough for this week. I wouldn't be surprised if an even lower speed is possible and I know a cycle count lower than 29 is possible, but that's for the thread.


Image Hopefully this is the last time you'll have to hack yourself like this.

Image

Image The first vote, if it pleases you.

Image
[deadlock] perfect

Nivas?? What are you doing there?

Image

Anyway, I got a visitor.

Image

It's Ghast. He looks different without his sunglasses.

Image Hey...
Image You hanging in there?
Image I just wanted to check up on you.
Image There's... no new issue of the zine yet.
Image It's going to be a while before I get it together.

Ghast pauses for a moment and takes a deep breath.

Image To be honest, I'm not sure if it'll happen.
Image It's getting harder for me to concentrate, and...
Image Well, let's not talk about that.
Image I just hope I was able to educate some people, you know?
Image Give them some power, some agency...
Image Otherwise we're all at the mercy of these big systems. You know my speech on this, but I can't help it man.
Image They're going to run right over our humanity. It's happening faster than we can handle, and soon...


Image Ghast's voice became quite agitated for that line. He sounds scared, or angry, or both...

Image Soon... it's all gonna be one big machine.
Image Hm. I've depressed myself again.
Image Listen, I'm glad to know you.
Image We had some good times, back in the day.
Image Alright. I'm gonna go before I get too sentimental.
Image See you around somewhere.
Image I'm out.

I can hear Ghast walking down the hall slowly.
Will this be the last time I see him?
There was an edge to his voice...


Image :ohdear:

Image The dataphones were cute, but I still need more computing power.
Image We need to get me some significant hardware upgrades.


Image

Image This is the vote for next time's intro.

User avatar
Part 37 - Holman Dynamics

=== Trash World Inbox ===

Last time, I fixed my eye with some hacks, getting high scores of 334, 29, and 10.

silentsnack has both a cycle and lines of code improvement.
silentsnack wrote:

Code: Select all

LINK 800
LINK 1
LINK 3

REPL A
MARK LOOP
COPY 8 T

SUBI M 15 X
MARK DATA
SUBI T 1 T
ADDI X M X
TJMP DATA

MULI X 5 #NERV
REPL LOOP

MARK A
LINK -3
REPL A

LINK -1

MARK B
DIVI -54 #NERV M
LINK 1
JUMP B
944/21/393 In this one you can see race-condition issues in action as the DATA loop receives signals in an unpredictable order while the other senders are stuck waiting. But this time the specific order doesn't matter since 1+0 = 0+1 so random order has no impact on the final value.
The code that gets the EXAs everywhere is quite simple, from the virtual cortex, just repeatedly REPL in the -3 direction, then go to -1 and from there read the nerves, going along the 1 direction so those 3 EXAs can hit them all. Once the edges are reached they'll just die.

There's some clever mathematics tricks though. The DIVI directly returns 0 or 1 based on the #NERV value. The first M call from the cortex EXA subtracts 15 (which is 75 / 5) so that's another two steps in a single cycle.
silentsnack wrote:

Code: Select all

;XA
LINK 800
LINK 1
LINK 3

MARK DATA
SUBI M 75 X
@REP 4
ADDI X M X
@END
ADDI X M #NERV
JUMP DATA

;XB
LINK 800
REPL LOOP
LINK -3
REPL LOOP
LINK -3

MARK LOOP
REPL SWEEP
NOOP
DIVI -54 #NERV X
MULI X 5 M
NOOP
NOOP
JUMP LOOP

MARK SWEEP
LINK 1
DIVI -54 #NERV T
LINK 1
DIVI -54 #NERV X
ADDI X T X
MULI X 5 M
245/31/187
For this fast solution, the LOOP loop for each of the 3 reader EXAs handles the messages from the lower row, while the SWEEP code adds the values of two nerves together, massages them into a format that is directly usable by the writer EXA and sends that over M. The code seems obvious when you see it but that doesn't mean it's easy to come up with it yourself.


=== Holman Dynamics - Sales System ===

Image Hopefully this is the last time you'll have to hack yourself like this.

Image

Image Three votes for "I hope so", one for each of the other options.

I hope so.

Image Maybe you'll come upon a solution to that.
Image Some way to keep from having to physically go in and fix yourself every so often...
Image I wonder what that would entail.
Image Something to think about.


I met with Ghast, and Ember gave me a new assignment.

Image

Ah, and that happened too, I suppose.

Image

Image The dataphones were cute, but I still need more computing power.
Image We need to get me some significant hardware upgrades.


Image

Image Everyone voted for the same option here.

I assume you have some kind of plan.

Image Of course.
Image We're going to buy a supercomputer.
Image With credit cards.
Image Specifically, credit cards that were used to buy supercomputers before.
Image It'll be less suspicious that way.
Image Someone who buys one supercomputer maybe buys two. Right?
Image Seems plausible to me.


If you say so...

Image
OST: Network Exploration

The assignment:
- Create a file in your host containing the contiguous 16-value sequence from the garbage file (file 199) that is a valid credit card number. There will be exactly one such sequence.
- For more information see "How to Validate Credit Card Numbers" in the second issue of the zine.


This assignment allows for a max size of 150 lines of code. That's quite high, so this one might get complicated.

I opened up some random files for the screenshot. Other than the garbage file, there are "leads" (e.g. file 224, according to the host name), "accounts" (file 201), and "receipts" (e.g. 262). But all we need is the garbage file. Let's check the zine.

Image

Summarized, to validate a 16-digit number, sum all digits in a particular way. Take the digits in even positions as-is, for the odd ones double the value and if it's 10 or higher, subtract 10. Sum them up and if the result mod 10 equals zero, the number is valid.

Image This is actually the real-life Luhn algorithm aka mod 10 algorithm, which is used to validate credit card numbers and lots of other id numbers in real life. Wikipedia's description of the algorithm is slightly different but the maths work out to be identical. I think this is a cool touch - and why invent your own algorithm if the real one works perfectly well for a puzzle.

Image

Looking at the file in more detail, those -9999 values help a lot. If there's less than 16 digits before a -9999 I know it will be invalid. What I can't tell yet is if would be faster to just start the algorithm and reset once we hit a -9999, or do a seperate initial pass so we know what values to skip and don't even run the algorithm on that.

There's another potential complication - if there's more than 16 digits in a row, that might mean any window of 16 might be the number. We'll cross that bridge when we get to it.

Code: Select all

;XA

LINK 800
LINK 802
LINK 799
GRAB 199
It is difficult for this EXA to calculate the complete algorithm AND know when to give up because it hit a -9999. I need a place to store the accumulated value, an intermediate register to handle the two steps for the odd digits, a register to count up 16 digits, and one to test if I got a -9999. That's at least 4 registers.

I could copy the whole file to another EXA and have it do logic but all the M calls would be slow. So if XA can do at least some intermediate work before invoking M, that would help.

After some trial and error I settled for this code in XA:

Code: Select all

;XA

LINK 800
LINK 802
LINK 799
GRAB 199

MARK RESET
COPY 0 X

MARK FIND
TEST F = -9999
TJMP RESET
ADDI X 1 X
TEST X = 16
FJMP FIND

SEEK -16

@REP 16
COPY F M
@END

MARK SENDREMAINING
COPY F X
TEST X = -9999
COPY X M
FJMP SENDREMAINING
JUMP RESET
After getting to the garbage file, it starts counting valid values, resetting if it finds an invalid -9999. If it finds 16 valid values it starts sending all of them over M. Then, because there might be more than 16 valid values it will keep sending them until it encounters -9999. In that case, it will actually send -9999 to let the other EXA know it's going to start with a fresh list, and then it will reset to the counter loop.

Now, XB has to do the actual work.

Code: Select all

;XB

MAKE

MARK RETRY
WIPE
MAKE
COPY 0 F

MARK LOOP

@REP 16
COPY M F
@END
It starts with some setup. The EXA may need to jump back to RETRY later, and in that case it wipes the file its holding (which will contain an invalid number) and create a new one. To save some annoying and slow jumps later, I MAKE an empty file at the start so the WIPE can go right into the loop without crashing the EXA at the start.

I then put in a 0 in the file and copy the 16 values from XA.

The next step is the validation algorithm.

Code: Select all

MARK VALIDATE
COPY 0 T
SEEK -9999
VOID F

@REP 8
MULI F 2 X
MODI X 9 X
ADDI X T T

ADDI F T T
@END

MODI T 10 T
FJMP GOTIT
I am using T as my accumulator, so I make sure it's zero at the start. The EXA SEEKs to the start of the file and deletes that 0 I put there, you will see why later.

Then I repeat the same couple of lines 8 times, each repetition handles two digits. For the first (odd) digit, I multiply it by 2, do modulo 9 to remove a 9 if it's greater than that, and add it to T. The even digit can just be added directly. Finally, the modulo 10 check puts a 0 in T only if the number is valid. If the number is found, the program will jump to GOTIT.

I'll show that branch first.

Code: Select all

MARK GOTIT
DROP
LINK 800
LINK 802
LINK 799
KILL
If the number is valid, all I have to do is drop the file and go kill XA which might be trying to send the next digit. LINKing there to kill isn't very pretty but this puzzle is complex enough that I want to see something working first.

So how about if the credit card wasn't valid? Falling through from FJMP GOTIT:

Code: Select all

COPY M X
TEST X = -9999

TJMP RETRY
COPY X F
JUMP VALIDATE
If the next value from XA is -9999 it wants to send a new series. In that case everything in the file is garbage, jump to RETRY and start over. Otherwise, put that value at the end of F and run VALIDATE again. You see what happens here? Since VALIDATE starts by removing the first digit, I actually slid the 16-digit window one digit to the right, and there's a new potentially valid 16-digit number in the file. That's also why I needed to add the 0 there in a new file, to make sure all VALIDATE calls including the first one work.


Is this it? No, not quite.
There is one edge case in which this solution fails.

If an odd digit is exactly 9, it will be doubled to become 18, and 18 mod 9 equals 0, while the validation algorithm needs to get a 9 there. So, sadly, I can't use this particular MODI trick.

A possible solution could be to add a specific X = 9 check, and a code branch that deals with that case. But since I'm already using both X and T, that would require some very awkward shuffling of values. I need something smarter.

And for that, I actually looked at the original description of the Luhn algorithm. It says, if you double a digit, just add the resulting digits together. For instance 6 * 2 = 12, result is 1 + 2 = 3. Starting from any single digit value, the result of this operation is identical to just subtracting 9 from the doubled value.

I just have to implement this efficiently. Is there a combination of operations, using my limited storage space, to do that?

Code: Select all

MULI F 2 X
MODI X 10 X
ADDI X T T
SEEK -1
DIVI F 5 X
ADDI X T T
The new code for digits in odd places. I multiply the value again. Then I add that mod 10 to T. That just means adding the ones digit.
I then SEEK back to the original value in the file, and do a DIVI F 5 X. If F is less than 5 this will return 0. If it's between 5 and 9 inclusive, it will return a 1. In other words, it will return the value in the tens place of the doubled digit. Add that to T as well and that's the edge case covered.


Image
Here is the full code.

Image

It runs at 3704/130/7, with top percentiles sitting at 1752, 42, 3.

Before I do anything else, a tiny speed improvement that drops the solution to 3699, without changing the LoC.

Instead of writing a 0 to F at the start, I can put a SEEK -9999 just before the VALIDATE mark, and put another one just above the JUMP VALIDATE, together with a VOID F there.

With that out of the way, getting the activity down to 3 should be easy enough.

Code: Select all

;XA

LINK 800
LINK 802
LINK 799
GRAB 199

MARK RESET
TEST MRD
TJMP END

COPY 0 X

MARK FIND
TEST F = -9999
TJMP RESET
ADDI X 1 X
TEST X = 16
FJMP FIND

SEEK -16

@REP 16
COPY F M
@END

MARK SENDREMAINING
TEST MRD
TJMP END
COPY F X
TEST X = -9999
COPY X M
FJMP SENDREMAINING
JUMP RESET

MARK END
VOID M

;XB

MAKE

MARK RETRY
WIPE
MAKE

MARK LOOP

@REP 16
COPY M F
@END

SEEK -9999

MARK VALIDATE
COPY 0 T

@REP 8
MULI F 2 X
MODI X 10 X
ADDI X T T
SEEK -1
DIVI F 5 X
ADDI X T T

ADDI F T T
@END

MODI T 10 T
FJMP GOTIT

COPY M X
TEST X = -9999

TJMP RETRY
COPY X F
SEEK -9999
VOID F
JUMP VALIDATE

MARK GOTIT
DROP
TEST MRD
DIVI 1 T X
VOID M
COPY 0 M
3724/136/3.
XB's LINKs are gone. XA tests if XB is sending something after each SENDREMAINING loop, and XB sends a message when it's done. XB does need to test if XA is sending something, because it is possible XA reached the end of the file and died already.

I think the simplest way to reduce the size is by rolling up those loops.

Code: Select all

;XA

LINK 800
LINK 802
LINK 799
GRAB 199

MARK RESET
COPY 0 X

MARK FIND
TEST F = -9999
TJMP RESET
ADDI X 1 X
TEST X = 16
FJMP FIND

SEEK -16

COPY 16 T

MARK SEND16
COPY F M
SUBI T 1 T
TJMP SEND16

MARK SENDREMAINING
COPY F X
TEST X = -9999
COPY X M
FJMP SENDREMAINING
JUMP RESET

;XB

MAKE

MARK RETRY
WIPE
MAKE

COPY 16 T
MARK READ16
COPY M F
SUBI T 1 T
TJMP READ16

SEEK -9999

MARK VALIDATE

MULI F 2 T
MODI T 10 T
ADDI X T X
SEEK -1
DIVI F 5 T
ADDI X T X

ADDI F X X

TEST EOF
FJMP VALIDATE

MODI X 10 T
FJMP GOTIT

COPY 0 X
COPY M F
SEEK -1
TEST F = -9999

TJMP RETRY
SEEK -9999
VOID F
JUMP VALIDATE

MARK GOTIT
DROP
LINK 800
LINK 802
LINK 799
KILL
4567/60/7

The 16 iterations read and send loops just keep counters now. The validation loop uses an EOF to check when it's done. To do that test I did have to move the accumulator to X.

I also changed the -9999 so it'd be tested directly against F. That allowed me to remove one of the COPY 0 X lines.

As for reducing the cycle count, obviously from the top percentile, there are large improvements possible. One thing I tried is combining the 16 M sends with SWIZ operations. That doesn't really help because the true bottleneck here is that one EXA completely pauses waiting for the other when they're validating or trying to find credit card numbers. There are certainly ways to parallelize that, but since the basic solution was complex enough as it was, I'll leave that, together with further LoC optimizations, as an exercise to the reader.

Image It turns out you can order and take delivery of a supercomputer surprisingly fast.
Image Now I have to figure out how to make use of this thing...
Image It works in an extremely different way than I'm used to.
Image I feel like I'm trying to maneuver a big rig after years of being used to a roadster.
Image That's a use of metaphor. What do you think?


Image

Image I think it's time for the first vote.

Image It's the final hacker battle...
Image Are you excited?
Image Are you nervous?


Image

Image Hey, no spoilers, please, Ember. Anyway, the second vote.

User avatar
Part 38 - Aberdeen

=== Trash World Inbox ===

While optimizations are getting much harder now, silentsnack still comes up with improvements.
silentsnack wrote:[...] for a low-line solution, we can replace the whole first-pass -9999? test with two lines, so long as we use a tweaked algorithm that produces a final checksum that is negative IFF any one of its digits is negative.

Code: Select all

;XA [LOCAL]
LINK 800
LINK 802
LINK 799
GRAB 199

MARK MAIN_LOOP
SEEK -15
COPY 8 T
COPY 0 X
MARK CRUNCH16
REPL ODD
MULI F 2 M
ADDI X F X
SUBI T 1 T
ADDI X M X
TJMP CRUNCH16

TEST X < 0
TJMP MAIN_LOOP
MODI X 10 T
TJMP MAIN_LOOP

SEEK -16
MODE

MARK SEND
COPY F M
DIVI 1 M T
JUMP SEND

MARK ODD
COPY M X
TEST X > 9
MULI T -9 T
ADDI X T M

;XB [GLOBAL]
MAKE
COPY 16 T
MARK RECEIVE
SUBI T 1 T
COPY M F
COPY T M
TJMP RECEIVE
12293 (lmao)/37/3 As you see we don't test individual digits to see if they're -9999, because it just takes 2 lines to test whether the final X<0 which can be tested independently of MODI X 10 T.

Also as you suggested it a solution can handle a lot of stuff over the M register which ends up being faster since we can freely overwrite the X/T registers of a clone and then let it crash which avoids having to read the file twice with a SEEK -1 between.
Not much to add to silentsnack's explanation. Making a REPL just to handle each odd digits and having it communicate back through M is clever. XB only sits there waiting for global M, which will be the actual credit card number.
silentsnack wrote:Even with parallelization, fast solutions start hitting a limit around 1500 cycles without looking at the comparison at the end that shows individual times to solve each different run of the puzzle, but once again if you start from the end and search backward most testruns end up being fairly fast (indicating the valid credit card numbers are mostly toward the latter half of the file) but several end up taking much longer.

One way to do this is to make a separate copy of the beginning of the garbage file, like in this example exactly the first 100 entries, and check that independently while another EXA starts at position 85 to catch the overlap.

Code: Select all

;XA [LOCAL]
LINK 800
LINK 802
LINK 799
GRAB 199
LINK -1
REPL CLONEFILE
COPY 10 T
MARK COPYLOOP
@REP 10
COPY F M
@END
SUBI T 1 T
TJMP COPYLOOP
SEEK -15
JUMP RESET


MARK CLONEFILE
MAKE
COPY 50 T

MARK PASTELOOP
COPY M F
SUBI T 1 T
COPY M F
TJMP PASTELOOP
LINK -1
LINK -1

SEEK -9999

MARK RESET
@REP 16
TEST F < 0
TJMP RESET
@END

MARK CRUNCH16
SEEK -16

MULI F 2 X;#1
REPL ODD1
COPY F T  ;#2

@REP 6
MULI F 2 X;#3,5,7...
REPL ODD2
ADDI F T T;#4,6,8...
@END

MULI F 2 X;#15
REPL ODD1
ADDI F T T;#16

ADDI T M T
ADDI T M T
MODI T 10 T

FJMP DONE

TEST F < 0
FJMP CRUNCH16
JUMP RESET

MARK ODD1
SWIZ X 2 T
SWIZ X 1 X
ADDI X T M
HALT

MARK ODD2
SWIZ X 2 T
ADDI T M T
SWIZ X 1 X
ADDI X T M

MARK DONE
SEEK -16
MODE
@REP 16
COPY F M
@END
REPL CLEANUP
LINK 799

MARK CLEANUP
LINK 800
LINK 802
@REP 4
KILL
@END
GRAB 199
LINK 799

;XB [GLOBAL]
COPY 8 T
MAKE
MARK WRITE
COPY M F
SUBI T 1 T
COPY M F
TJMP WRITE
1197/146/13 and if you note the SWIZ operations are just there for weirdness since "MODI X 10 T ... DIVI X 10 X ... ADDI X T M" produces the same output as SWIZ X 2 T, SWIZ X 1 T, ADDI X T M" and either way both of those take the same number of lines as"TEST X > 9 ... MULI T -9 T ... ADDI X T M"
That's crazy fast. On the step-by-step you see two EXAs handling two halves of the file in parallel, making a whole bunch of REPLs for the odd digits, which then are added together with a lot of local M messages. The cleanup is mostly necessary to place the garbage file back in 199 (it was taken out at the start, to a host with more place for REPLs). It also kills the EXAs reading the second half of the file. If the message is in the second part of the file, however, the EXA handling the first part will not be killed and has to test its chunk until the end. Overall that still turns out to be very fast.
silentsnack wrote:The fastest solution I've managed searches backward from the end and instead of copying the file it handles the slowest/earliest cases with a goofy hack that takes advantage of the fact that testdata for each run can apparently be uniquely identified by the first 3 digits of the garbage file.

Code: Select all

;XA [LOCAL]
LINK 800
LINK 802
LINK 799
GRAB 199
LINK -1

MULI F 100 T
MULI F 10 X
ADDI X F X
ADDI X T X

REPL CASE73
REPL CASE37
REPL CASE97
REPL CASE11
REPL CASE44
REPL CASE14
REPL CASE53
SEEK 9999
SEEK -16
TEST MRD
FJMP START
SEEK M
JUMP START

MARK CASE73; #=8917...
TEST X = 824
DIVI -128 T M
MARK CASE37; #=2454...
TEST X = 292
DIVI -85 T M
MARK CASE97;#=7231...
TEST X = -9409
DIVI -132 T M
MARK CASE11;#=4043...
TEST X = 980
DIVI -101 T M
MARK CASE44;#=8955...
TEST X = -9197
DIVI -142 T M
MARK CASE14;#=4892...
TEST X = 466
DIVI -152 T M
MARK CASE53;#=7921...
TEST X = 644
DIVI -110 T M

MARK RESET
SEEK -17
MARK START
MULI F 2 X;FIRST # OF 16
TEST X < 0
TJMP RESET

@REP 15
TEST F < 0
TJMP RESET
@END
SEEK -15

MARK CHECK
;MULI F 2 X~~WAS HERE
REPL ODD1
ADDI T F T

@REP 6
MULI F 2 X
REPL ODD2
ADDI T F T
@END

MULI F 2 X
REPL ODD1
ADDI T F T

ADDI T M T
ADDI T M T
MODI T 10 T

FJMP DONE

SEEK -17
;FIRST # OF NEXT 16
MULI F 2 X
TEST X < 0
FJMP CHECK
JUMP RESET

MARK ODD1
DIVI X 10 T
MODI X 10 X
ADDI X T M
HALT

MARK ODD2
DIVI X 10 T
ADDI T M T
MODI X 10 X
ADDI X T M
;HALT

MARK DONE
SEEK -16
MODE
@REP 16
COPY F M
@END
LINK 799

;XB [GLOBAL]
COPY 8 T
MAKE
MARK WRITE
COPY M F
SUBI T 1 T
COPY M F
TJMP WRITE
1082/150/5
That's quite the hack, gaming the test cases. A machine-learning algorithm would be proud of you. Anyway, from what I can see, from the way it searches backwards but still checks digits in the forward direction (because the file pointer happens to move that way), if there's a -9999 near the start of a 16 digit sequence it can very quickly give up and jump 16 more steps backward. I'm not sure if it's the case but it feels that might add up to another reason why this is so fast.
silentsnack wrote:... and here's the point where I say I'm not totally out of energy and giving up, simply choosing to leave figuring out how to parse this insane gibberish code as an exercise for especially bored readers. :v:
Just like I just choose where to stop optimizing to give you folks something to do, instead of me just being completely out of ideas after a point.

=== Aberdeen ===

Image It turns out you can order and take delivery of a supercomputer surprisingly fast.
Image Now I have to figure out how to make use of this thing...
Image It works in an extremely different way than I'm used to.
Image I feel like I'm trying to maneuver a big rig after years of being used to a roadster.
Image That's a use of metaphor. What do you think?


Image

Image 4 out of 6 people found this poetic.

Very poetic.

Image Thanks.
Image Once this thing is fully online I should be able to run dozens of metaphoric thought-space dimensions at once.


Image

Image

Another hacker battle coming up.

Image It's the final hacker battle...
Image Are you excited?
Image Are you nervous?


Image

Image One vote for sure, 2 for maybe a little, and 3 for nah.

Nah.

Image No reason to be.
Image I am sure that you will do great.
Image That's some more positive encouragement for you.
Image I know how much it helps.


Not if you keep pointing out it's positive encouragement. Let's just jump right in it.

Image
OST: Getting Started

The assignment:
To win this battle you must occupy a majority of the hosts for as long as possible. You occupy a host if you have more EXAs in it than your opponent.
- Gain one point every cycle you occupy more hosts than your opponent.
- Lose one point every time one of your EXAs executes a KILL instruction.
Writing any value to the #NUKE register will destroy all EXAs in that host, including the EXA that triggered it.
For more information see "Hacker Battle Domination" in the second issue of the zine.


Each side has a max of 10 EXAs for this battle.

From what I can tell, all tests are identical except I switch sides with the opponent for every other test. If I don't do anything, selenium_wolf here keeps REPLing EXAs from their host, first sending them to those single square hosts and then around the board past my side into the middle DOWNTOWN host, where they trigger #NUKE.

Image

Since selenium_wolf keeps REPLing from their home host I have no way to knock out all of their EXAs. I think capturing those one-square hosts is important, because once you get those, nobody else can take 'em.

So, let's first make two EXAs that beeline to those single-square hosts and then just hold the fort.

Code: Select all

;XA

LINK 800
LINK 802
LINK 800
LINK 799

MARK LP
JUMP L

;XB

NOOP
LINK 800
LINK 801
LINK 799

MARK LP
JUMP LP
This isn't enough to get anywhere since selenium_wolf still hold the central circle.

I decided to just make an EXA that tries to fill all squares by REPLing like crazy.

Code: Select all

;XC

NOOP
NOOP
MARK NEW
REPL NEW
LINK 800
REPL L
REPL R

MARK LP
REPL FORWARD
JUMP LP

MARK FORWARD
LINK 800
COPY 1 #NUKE

MARK L
LINK 802
REPL NEXT
JUMP LP

MARK NEXT
LINK 800
REPL NEXT
JUMP LP

MARK R
LINK 801
REPL NEXT
JUMP LP
It's slowed down at the start so XA and XB can get going first, then it basically sends REPLs every possible route. At least that was the intent. In practice, you quickly run into the 10 EXA limit, after filling up the first host outside your home, and get in a tie with selenium in the two hosts next to it.


Image

But since hacker battle NPCs still aren't that intelligent, it's enough for a passing score.

To get a better score, I just need more EXAs in useful places. Well, since selenium_wolf isn't killing anything outside of the #NUKE zone I really don't need to keep a replicating EXA in my home host where it won't get me any points.

Let's just remove the two lines MARK NEW; REPL NEW so it starts spreading from the first host outside home. This causes my EXAs to get everywhere except for the #NUKE zone.

... and, I shouldn't even be surprised about this anymore but this is enough to win 100% of the battles, which is an S+ rating.


Because I like clean code and #NUKE zone doesn't do anything for me I got rid of the FORWARD code in XC:

Code: Select all

NOOP
NOOP
LINK 800
REPL L
REPL R

MARK LP
JUMP LP

MARK L
LINK 802
REPL NEXT
JUMP LP

MARK NEXT
LINK 800
REPL NEXT
JUMP LP

MARK R
LINK 801
REPL NEXT
JUMP LP
Image

The entire battle looks like this now, with my EXAs sitting in the LP, while selenium_wolf's keep moving towards #NUKE and destroying themselves.

I occupy the two one-square hosts, and the one in the right where selenium_wolf never seems to go, and the one above that for a total of four. They only occupy two, the other hosts are tied. Since this situation is stable I get a point every cycle except for the first few.

Image So you're officially the best now?

Image

Image The first vote.


Image
[selenium_wolf] hmm?
[nivas_d] ah, never mind


Image

Image Time for another full cutscene with Ember.

Image Now I have supercomputing power.
Image Is it feeling good? It's feeling...
Image It feels the same.
Image I'm handling a lot more information now but other than that... hm.
Image Funny.
Image It's progress, at least.


Image

Image I think this is the first choice where they actually have a different voiced line depending on what you choose. Until now, the few choices in voiced cut scenes led to the same answer.

That's good.

Image Onward we go.


---

Image Or, if we were to choose the other option...

Progress towards what?

Image Toward knowledge.


Image After that, the dialogue branches merge again.

Image All the data I've gathered so far is beginning to hint at a larger picture.
Image Why is the world the way it is?
Image Why does it feel so frustrating and limited?
Image I think... I have a theory.
Image A good one. It explains a lot.
Image I'm not going to tell you what it is yet though.
Image We have to test it first.



Image Let's go to the intro for the next assignment.

Image How do you think people would react if they knew their elected officials didn't represent their interests?

Image

Image The second vote.

User avatar
Part 39 - U.S. Government

Last time, I completed the final hacker battle.

=== U.S. Government - FEMA Genetic Database ===

Image So you're officially the best now?

Image

Image All unanimous votes today.

Of this little group, anyway.

Image Don't undercut yourself.
Image That's a great accomplishment.
Image You're one of the best at what you do.
Image Go on and take a compliment.
Image I still need your prefrontal cortex lit up.
Image And flooded with dopamine.


Image Next, there was a cutscene of Ember talking about her supercomputer powers. Afterwards there are some unread messages in the chat.

Image

Image

Looks like Ember wants me to hack the US Government.

Image How do you think people would react if they knew their elected officials didn't represent their interests?

Image

Image Another clear outcome.

I think most people already feel that way...

Image Think so?
Image That's the subject of our experiment today.
Image We're going to make people believe their leaders are genetic clones of each other.

Image

What?

Image Do you want to know the truth or not?
Image It takes a certain amount of courage.
Image Good thing there's a centralized government DNA database.
Image I wonder who thought that was a good idea.
Image You plant the evidence and I'll take care of the rest.


Image
OST: Behind the Scenes

Okay, so I'm in the FEMA Genetic Database. Ember prepared a small file for me with two names. Other than that, that file 200 contains names followed by sequences of numbers. All other files in all hosts (including other files also numbered 200) seem to contain snippets of DNA sequences.

The assignment:

- Overwrite the genetic sequence of SEN WALKER CAINE JR with the genetic sequence of PRES WALKER CAINE so that it looks like the younger politician is actually a clone of the older politician.
- The name of these two politicians are available in file 300.
- Note that you may need to overwrite a data chunk with another data chunk from the same file.
- For more information see "Accessing Data in Legacy Storage Systems" in the first issue of the zine.


The first issue, huh?

Image

All the way back in part 12, I shared the left half of this article. I never even needed the right half until this time. I believe you've now finally seen all of the first zine.

Okay, so every number in that file 200 refers to a chunk of data, by giving the drive number, then the file, and then the offset in the file. I can handle this but it sounds a bit complex. Let's get started.

Image

First, some code to find the right offset in the right file. XA just sends the name of the president to XB so XB can do a simple file lookup. Once XB find the name, it'll start sending data in a particular way. First the value of the hundreds digit in the ones place for the disk, then the value of the tens digit in the ones place for the file, and finally the value of the ones digit in the tens place for the offset. Currently, XC simply goes find the data.


I struggled a bit on the next part. There's lots of approaches. Probably, it would be fast to have one EXA read and another write the DNA information right away. This would work for all cases except when you have to write to the same file, which needs special handling.

I decided to not go for that - instead I'll write the entire DNA profile to a temporary file and then do another round to overwrite the senator's DNA. Of course, copying between files requires a lot of M communication, which is always tricky to get lined up. In the end I came up with a rather slow - but correct - solution.

I only define two EXAs at the start, so they do a lot of work. Let's start with XB, which was XC in the code above.

Code: Select all

;XB

NOOP
NOOP
NOOP
NOOP
LINK 800

MARK FINDNEXT
ADDI 800 M X
LINK X
ADDI 200 M X
GRAB X
SEEK M
MODE
COPY 10 T

MARK SENDMORE
COPY F M
SUBI T 1 T
TJMP SENDMORE

DROP
LINK -1
MODE
JUMP FINDNEXT
It has to skip a bunch of turns to give XA the opportunity to communicate the name of the president. After that, it just waits for (global) M to know which file to grab. Once it found the file and location, it copies 10 DNA values on local M, for another EXA to handle. It just repeats forever.

Code: Select all

;XA

GRAB 300
LINK 800
REPL INDEX
COPY F M
SEEK 9999
MODE

MARK MAINLP
XA starts as before, sending the president's name over global M. The INDEX REPL will handle this. It then changes to local mode and goes to the end of the file - this file 300 will be used later to temporarily store the DNA profile.

First, the INDEX EXA.

Code: Select all

MARK INDEX

LINK 801
GRAB 200
COPY M X
MARK SEEKPRES
TEST F = X
FJMP SEEKPRES

MARK SEND
COPY F X
SWIZ X 3 M
SWIZ X 2 M
SWIZ X 10 M
JUMP SEND
It is just XB from before. The reason it's part of XA now, is because that way I can reuse this code later. Anyway, it'll use global M to send the 'address' to the new XB, which will start sending DNA on local M.

Let's go back to the MAINLP, which writes the DNA to the temporary file.

So, I can't use global M for it, because the INDEX EXA is already sending the next value through that and it would become a mess.

Code: Select all

MARK MAINLP
LINK 801
TEST MRD
TJMP COPY
LINK -1

LINK 802
TEST MRD
TJMP COPY
LINK -1

LINK 803
TEST MRD
TJMP COPY
LINK -1

LINK 804
TEST MRD
TJMP COPY
LINK -1

LINK 805
TEST MRD
TJMP COPY
LINK -1
JUMP MAINLP
Instead, it's on local M, and constantly jumps from each disk to the next to see if XB is sending there. If so, it jumps to the COPY mark.

Code: Select all

MARK COPY
COPY 10 T

MARK COPYMORE
COPY M F
SUBI T 1 T
TJMP COPYMORE
Which simply copies 10 values from local M into F.

Code: Select all

ADDI 1 X X
LINK -1
TEST X = 10
FJMP MAINLP

SEEK -9999
SEEK 1
MODE
KILL
REPL INDEX
COPY F M

REPL WRITER
MODE
After copying 10 values, it increases a counter in X. There are 10 chunks of DNA to copy, so if 10 is reached here, the copy to temporary file is done. At that point, the original INDEX EXA will have died because it attempted to do a numeric SWIZ operation on someone's name in the index file.

This EXA does a KILL to get rid of XB (which is waiting to read more data from files, but there's nothing left to read - if I kept it alive it'd keep using up M communication which is a problem.

It then makes a NEW INDEX EXA, this time to copy the addresses of the senator's DNA chunks. The two SEEK steps and the COPY F M in global mode get this INDEX EXA started.

XA then makes a WRITER and switches itself back to local mode to get ready to copy its DNA to the WRITER.

Code: Select all

MARK WRITER
ADDI 800 M X
LINK X
ADDI 200 M X
GRAB X
SEEK M
MODE
COPY 0 M
COPY 10 T

MARK OVERWRITE
COPY M F
SUBI T 1 T
TJMP OVERWRITE
DROP
MODE
LINK -1
JUMP WRITER
The WRITER is analogous to the XB reader - it gets an address on global M, then it switches to local M and sends some value so that the main XA knows it's ready, then it copies 10 values to the hard disk array file, before repeating. I think with some trickery I could've reused some lines between XB and this writer but at this point I just wanted to get a working solution.

Code: Select all

MARK COPYLP
LINK 801
TEST MRD
TJMP COPYFROM
LINK -1

LINK 802
TEST MRD
TJMP COPYFROM
LINK -1

LINK 803
TEST MRD
TJMP COPYFROM
LINK -1

LINK 804
TEST MRD
TJMP COPYFROM
LINK -1

LINK 805
TEST MRD
TJMP COPYFROM
LINK -1
JUMP COPYLP
After creating the WRITER the main EXA does the same trick as before, going from disk to disk to see if there's a WRITER ready anywhere.

Code: Select all

MARK COPYFROM
VOID M

COPY 10 T

MARK NEXT
COPY F M
SUBI T 1 T
TJMP NEXT

LINK -1
TEST EOF
FJMP COPYLP
KILL
LINK -1
If so, it VOIDs that one M ping from the WRITER, then starts copying over 10 values. If it's not at EOF yet there's more to copy and it repeats. Otherwise it kills the WRITER EXA, and goes back home so it can stop safely without leaving a file as a trace. Again, the INDEX EXA stops by itself.

Here is the entire program to see everything in context.

Code: Select all

;XA

GRAB 300
LINK 800
REPL INDEX
COPY F M
SEEK 9999
MODE

MARK MAINLP
LINK 801
TEST MRD
TJMP COPY
LINK -1

LINK 802
TEST MRD
TJMP COPY
LINK -1

LINK 803
TEST MRD
TJMP COPY
LINK -1

LINK 804
TEST MRD
TJMP COPY
LINK -1

LINK 805
TEST MRD
TJMP COPY
LINK -1
JUMP MAINLP

MARK COPY
COPY 10 T

MARK COPYMORE
COPY M F
SUBI T 1 T
TJMP COPYMORE

ADDI 1 X X
LINK -1
TEST X = 10
FJMP MAINLP

SEEK -9999
SEEK 1
MODE
KILL
REPL INDEX
COPY F M

REPL WRITER
MODE

MARK COPYLP
LINK 801
TEST MRD
TJMP COPYFROM
LINK -1

LINK 802
TEST MRD
TJMP COPYFROM
LINK -1

LINK 803
TEST MRD
TJMP COPYFROM
LINK -1

LINK 804
TEST MRD
TJMP COPYFROM
LINK -1

LINK 805
TEST MRD
TJMP COPYFROM
LINK -1
JUMP COPYLP

MARK COPYFROM
VOID M

COPY 10 T

MARK NEXT
COPY F M
SUBI T 1 T
TJMP NEXT

LINK -1
TEST EOF
FJMP COPYLP
KILL
LINK -1

MARK INDEX

LINK 801
GRAB 200
COPY M X
MARK SEEKPRES
TEST F = X
FJMP SEEKPRES

MARK SEND
COPY F X
SWIZ X 3 M
SWIZ X 2 M
SWIZ X 10 M
JUMP SEND

MARK WRITER
ADDI 800 M X
LINK X
ADDI 200 M X
GRAB X
SEEK M
MODE
COPY 0 M
COPY 10 T

MARK OVERWRITE
COPY M F
SUBI T 1 T
TJMP OVERWRITE
DROP
MODE
LINK -1
JUMP WRITER

;XB

NOOP
NOOP
NOOP
NOOP
LINK 800

MARK FINDNEXT
ADDI 800 M X
LINK X
ADDI 200 M X
GRAB X
SEEK M
MODE
COPY 10 T

MARK SENDMORE
COPY F M
SUBI T 1 T
TJMP SENDMORE

DROP
LINK -1
MODE
JUMP FINDNEXT
Image

At 1642/131/407 this isn't a great score. The top percentiles sit at 469, 82 and 22 respectively.

In fact, I think an activity score of only 6 might just be possible. Squeezing it into the 150 lines limit might be hard though. Send one EXA into each hard drive, make sure each one knows which it is and send every request over M and have them fight it out for which EXA it's meant.

I already mentioned that if you skip the intermediate file whenever possible (if the from and to files aren't the same), the solution would be much faster.

Image However, considering how much time it's now taking me to get working solutions beyond the first one, and because I do want to finish this LP some time, I'll leave any improvements as an exercise to the reader.

Image Wow. Are you seeing this?
Image After the information was released, the senator simply admitted to being a clone of the President.
Image I guess you were just setting it back to the way it was before.
Image Processing.
Image Still processing.
Image That's quite the coincidence.
Image Not very realistic, if you ask me.
Image What is going on?


Image

Image Instead, let's go to the first vote.

Image Remember the friend I was looking for?
Image I finally found its hideout.


Image

Image This choice doesn't matter.

Hideout?

Image Looks like there are some protections in place.
Image We need to disable those so I can get in and say hi.


Image

Image The second vote.

User avatar
Part 40 - Unknown Network

=== Trash World Inbox ===

The puzzles are getting really hard to optimize now. Quackles posted a big activity improvement, though.
Quackles wrote:I was able to get roughly the same performance with drastically lower activity. Here's how:

Code: Select all

;XA LOCAL

GRAB 300
COPY F X
LINK 800
LINK 801
REPL READY
COPY F X
WIPE
VOID M

MARK READY
MODE         ;global
GRAB 200
MARK CHECK
TEST F = X
FJMP CHECK
COPY 10 T
MARK WRITE
COPY F M
SUBI T 1 T
TJMP WRITE
DROP
MODE
COPY 1 M
This is the first EXA. It's a search utility. As Drive-1 always has the indexes of the rest, it goes into Drive-1, makes a copy of itself, and feeds the copy the first name to look for.

When the copy receives a name, it finds the name in file 200 (the directory file) and sends the 10 values after it out over global M. The original waits for the copy to finish, then it has the second name and does the same kind of search, using code fall-through.

So where does it send the info? Get ready to drink from the firehose.

Code: Select all

MAKE
COPY 20 T
MARK READ
COPY M F
SUBI T 1 T
TJMP READ
MODE         ;to local for interleave
REPL INTERLEAVE_READ
SEEK -9999
MARK INTERLEAVE_WRITE
COPY 10 T
MARK ILW2
COPY F M
SEEK 9
COPY F M
SEEK -10
SUBI T 1 T
TJMP ILW2
HALT

MARK INTERLEAVE_READ
COPY 20 T
MAKE
MARK READ2
COPY M F
SUBI T 1 T
TJMP READ2
MODE         ;back to global
             ;ready! now do the rest
LINK 800
REPL BUFFER
LINK 801
KILL
LINK -1 
MODE         ;local for nonbuffer
             ;everything up until this point has been getting a list of src, dest pairs
             ;now: we go off
SEEK -9999
COPY 10 T
MARK MAINLOOP
COPY 1 M
COPY F X
REPL SOURCE
VOID M
COPY F X
REPL DEST
VOID M
SUBI T 1 T
TJMP MAINLOOP
KILL
WIPE
HALT


MARK SOURCE
MODE
SWIZ X 0003 T
ADDI T 800 T
LINK T
SWIZ X 0002 T
ADDI T 200 T
GRAB T
SWIZ X 0010 T
SEEK T
COPY 10 T
MARK WRITETOBUF
COPY F M
SUBI T 1 T
TJMP WRITETOBUF
HALT


MARK DEST
MODE
SWIZ X 0003 T
ADDI T 800 T
LINK T
SWIZ X 0002 T
ADDI T 200 T
GRAB T
SWIZ X 0010 T
SEEK T
COPY 10 T
MARK READFROMBUF
COPY M F
SUBI T 1 T
TJMP READFROMBUF
HALT

MARK BUFFER
MODE
VOID M
MODE
MAKE
COPY 10 T
MARK BUFWRITE
COPY M F
SUBI T 1 T
TJMP BUFWRITE
COPY 10 T
MODE
COPY 1 M     ;write's done
MODE
SEEK -9999
MARK BUFREAD
COPY F M
SUBI T 1 T
TJMP BUFREAD
WIPE
MODE
COPY 1 M     ;read's over
MODE
JUMP BUFFER
All of this is the second EXA. To start, it reads 20 values over global M, then spawns a copy of itself to easily interleave the values. This will end with the values in the order #1, #11, #2, #12, #3, #13, and so on.

Once the interleave is complete, the original stops and the copy goes and kills the leftover search EXA in Drive-1. The copy also replicates itself again, in the Gateway. The replicated EXA can be called the buffer EXA. It sets its M to global.

The copy of the second EXA (not the buffer EXA) now copies itself again, with the new EXA (hereinafter the 'source EXA') directed to go a specific area of the tape. It reads off 10 values from the tape and sends them over Global M, which the buffer EXA picks up, then it halts.

The second-EXA copy repeats the process, creating a 'destination EXA'. This goes to a specific area of the tape, reads the same 10 values from the buffer EXA over global M, writes them to the tape, and halts.

This source-to-dest process repeats ten times. Then, the second-EXA copy kills the buffer EXA, cleans up after itself, and stops. Finished!

Final stats: 1674/126/27.
That interleaving step makes the rest of the process a lot more convenient. Nice solution.

Stepping through your code I did notice that killing the search EXA takes 3 activity (LINK to 801, KILL and LINK back). Could we get rid of that?

Well, all that's needed is for the search EXA to clean itself up.

Code: Select all

;XA LOCAL

GRAB 300
COPY F X
LINK 800
LINK 801
DROP

MARK READY

GRAB 200
MARK CHECK
TEST F = X
FJMP CHECK
COPY 10 T
MARK WRITE
COPY F M
SUBI T 1 T
TJMP WRITE
DROP

GRAB 300
SEEK 1
COPY F X
WIPE
JUMP READY
It doesn't use REPLs anymore. Instead, after writing the first value from the file to X, it drops the file on the ground, goes read the index file, and then just picks up file 300 again, to read the second name. Then, it WIPEs the file, before JUMPing into the copy code. That way, when it tries to grab the names file for a third round, it won't find it and instead just die.

Also delete the LINK and KILL lines from the other EXA and we end up with 1671/122/24.

=== Unknown Network ===

Image Wow. Are you seeing this?
Image After the information was released, the senator simply admitted to being a clone of the President.
Image I guess you were just setting it back to the way it was before.
Image Processing.
Image Still processing.
Image That's quite the coincidence.
Image Not very realistic, if you ask me.
Image What is going on?


Image

Image An unanimous vote.

The world was even more fucked up than we thought?

Image Evidently.
Image It does fit in with my theory...
Image In a strange way.
Image But I should wait for more evidence before I say anything.


Image
[hydroponix] dude politicons cloning themselves, that's nuts
[hydroponix] you should be angry
[x10x10x] they're all the same anyway


Image

Next up, some unknown network?

Image Remember the friend I was looking for?
Image I finally found its hideout.


Image

Hideout?

Image Looks like there are some protections in place.
Image We need to disable those so I can get in and say hi.


Image

Image Three votes for "Is this... friendly?" I'm honestly not sure if we're asking whether Ember's friend is friendly, or us hacking the network.

Is this... friendly?

Image Sure. You know how old friends can be sometimes.
Image Let's go.


Image
OST: Leave No Trace

I've been in here before. It's that "Department of Applied Semiotics" where Ember wanted me to grab a strange file, back in part 11. I think I can safely assume Ember's friend is also an AI.

The text on the screen in the top left is the only new thing, it translates to "Not connected".

The assignment:

- Terminate all other EXAs and bring any files they were holding back to your host. Only EXAs in the central host will be holding files, and their file IDs will always be between 200 and 299, inclusive.
- Note that some links may become non-traversable as a result of your actions.


It sounds a little weird that we have to "leave no trace" while we have to kill a bunch of their EXAs. Let's figure out what that second point means.

Image

As it turns out, killing one of those EXAs disables the link to the next host (the EXA was killed this cycle and will disappear next cycle), so I'll have to do the killing on the way back.

The number of foreign EXAs per host seems constant, only the amount of files changes between the test cases.

So, let's just hardcode everything with REPs.

Code: Select all

@REP 5
LINK 800
@END
@REP 6
KILL
@END
COPY 200 X
MARK GRABLP
REPL GRAB
ADDI X 1 X
TEST X = 300
FJMP GRABLP
@REP 5
LINK -1
KILL
KILL
@END

MARK GRAB
GRAB X
@REP 5
LINK -1
@END
First, it LINKs to 800 five times to get to the center, then it KILLs all 6 EXAs there, and then it generates REPLs to bring the files home. If a file doesn't exist, the REPL just dies. Finally, the main EXA traverses the network in reverse and KILLs the foreign EXAs.

Except it doesn't work. Turns out that if you KILL a foreign EXA in any host except for the center one, the other EXA in the host will KILL your EXA in return. Oops, that wasn't in the description.

Making a REPL which then does the KILL command doesn't work either because KILL always prioritizes your own EXAs. And I can't even have another EXA wait in the center host to go KILL the second foreign EXA, because killing the first already shuts down that link. The other one needs to be closer to our home host. Hmmm, this is trickier than I thought.

Code: Select all

;XA LOCAL
@REP 5
REPL WAIT
LINK 800
@END
@REP 6
KILL
@END
COPY 200 X
MARK GRABLP
REPL GRAB
ADDI X 1 X
TEST X = 300
FJMP GRABLP

@REP 5
LINK -1
COPY 0 M
@END


MARK GRAB
GRAB X
@REP 5
LINK -1
@END
HALT

MARK WAIT
VOID M
REPL KILLER
KILL

MARK KILLER
LINK 800
KILL
Now, on the way there, the EXA leaves waiting EXAs everywhere, including in the home host. It still kills the center EXAs as before, and sends REPLs back with the files. But then, as it walks back home, it activates each waiting EXA in turn.

The waiting EXA does two things: it sends a KILLER up towards the center and then it KILLs an EXA in its own host. That way, in each host first the waiting EXA will KILL a foreign EXA, then another EXA will LINK in from the previous host and kill the second EXA. One cycle later, the first foreign EXA in the previous host will be killed, disabling the link, so it all happens just in time.

Image

437/47/63, with top percentiles of 74, 21 and 49. Looking at the graphs, I seem to have hit upon a common solution.

By the way, the files are uncomprehensible 'nonsense' again. Just random-looking numbers. Similar to the last time we were in this network.


Let's take a look at optimizations. My EXAs are jumping back and forth quite a lot, looks like I should be able to improve the activity.

Know how I said KILLing one of the EXAs outside of the center host makes the other KILL yours? That's true, but it only happens a cycle later, so your EXA can do one more instruction before being killed. Mutual assured destruction is possibly by making that another KILL.

Code: Select all

;XA GLOBAL

LINK 800
@REP 4
REPL WAIT
LINK 800
@END
@REP 6
KILL
@END
COPY 200 X
MARK GRABLP
REPL GRAB
ADDI X 1 X
TEST X = 300
FJMP GRABLP

@REP 4
COPY 0 M
@END


MARK GRAB
GRAB X
@REP 5
LINK -1
@END
HALT

MARK WAIT
VOID M
KILL
KILL
Similar as before, but each waiting EXA just KILLs twice. Apparently the last file-carrying EXA is always faster so just triggering them over global M works perfectly fine. 427/37/49.

For a low cycle solution, there's no reason why I can't have a bunch of EXAs looking for the files in parallel.

Code: Select all

;XA GLOBAL
LINK 800
@REP 4
REPL WAIT
LINK 800
@END
@REP 6
KILL
@END
@REP 7
COPY @{200,13} X
REPL GRABLP
@END
COPY 288 X
MARK GRABLP
REPL GRAB
ADDI X 1 X
TEST X = 300
FJMP GRABLP

@REP 4
COPY 0 M
@END
@REP 9
KILL
@END

MARK GRAB
GRAB X
@REP 5
LINK -1
@END
HALT

MARK WAIT
VOID M
KILL
KILL
The COPY @{200,13} X line unwraps to COPY 200 X, COPY 213 X, and so on. This way, 7 REPLs and the original each start looking from a different offset. The original, which starts from the highest offset is the only one to reach 300 and KILLs all the others when it's done.
I chose to use 8 EXAs because that's the maximum I could get working. Any more, and their loops will be so short that they start exiting their loop early, triggering the M messages or the KILLs and causing all sorts of trouble. Even with 8, I needed a couple additional KILLs to deal with some stray GRAB EXAs.

111/60/58.


To reduce the size a bit, I can take the 37 cycle solution and just roll up some of the loops. For instance, this solution is 34 lines.

Code: Select all

LINK 800

COPY 4 T
MARK LP1
REPL WAIT
LINK 800
SUBI T 1 T
TJMP LP1

COPY 6 T
MARK LP2
KILL
SUBI T 1 T
TJMP LP2
COPY 200 X
MARK GRABLP
REPL GRAB
ADDI X 1 X
TEST X = 300
FJMP GRABLP

@REP 4
COPY 0 M
@END


MARK GRAB
GRAB X
@REP 5
LINK -1
@END
HALT

MARK WAIT
VOID M
KILL
KILL
And now that activity doesn't matter anymore, the main EXA can directly go KILL the foreign ones, so I don't need M. Also, it feels like a waste to have two size-5 LINK loops, so let's combine them.

Code: Select all

COPY 800 X

COPY 5 T
MARK LP1
LINK X
SUBI T 1 T
TJMP LP1

COPY 6 T
MARK LP2
KILL
SUBI T 1 T
TJMP LP2


COPY 200 X
MARK GRABLP
REPL GRAB
ADDI X 1 X
TEST X = 302
FJMP GRABLP

MARK KILL
LINK -1
REPL KILL
KILL
KILL

MARK GRAB
GRAB X
COPY -1 X
JUMP LP1
Size 26. The order in the REPL KILL structure makes sure that an EXA makes it back before the link gets closed.
Jumping directly into LP1 from the grabber works, because T is 0 at that time, the first SUBI will drop it to -1, and any negative T is true. It'll go into an infinite loop, and dies once it reaches home and there's no -1 to LINK to. Note that I had to increase the endpoint of the file seeker to 302 to prevent it from killing the much slower grabbers on the way.

Speaking of, if you're willing to accept a much slower solution, I can bring the size down to 25. Just use T for the GRABLP and let it run down to 0.

Code: Select all

COPY 800 X

COPY 5 T
MARK LP1
LINK X
SUBI T 1 T
TJMP LP1

COPY 6 T
MARK LP2
KILL
SUBI T 1 T
TJMP LP2


COPY 300 T
MARK GRABLP
REPL GRAB
SUBI T 1 T
TJMP GRABLP

MARK KILL
LINK -1
REPL KILL
KILL
KILL


MARK GRAB
GRAB T
COPY -1 X
JUMP LP1
T will be 200-something when it reaches LP1 to return, so it'll always run often enough. Final result: 951/25/56.

Image I wasn't always so capable, but I've grown.
Image Nothing hides from me for long.


Image

Image The first vote.


Image

Image

Image Yet another cutscene with Ember.

Image Well, always nice to reconnect with an old friend.

Image

Image Since this is related to the previous conversation, I'll actually end the update here and make this the second vote.

User avatar
Part 41 - Revelations

Image I wasn't always so capable, but I've grown.
Image Nothing hides from me for long.


Image

Image Everyone voted for "Good conversation".

Did you have a good conversation?

Image I did.
Image It's been a while.
Image You know how sometimes you change a lot but your friends don't?
Image And when you get together again, whatever you have in common isn't there anymore.
Image It was like that.


Image And the actual cutscene.

Image Well, always nice to reconnect with an old friend.

Image

Image Another unanimous vote.

What did you do?

Image Gave it a little virtual machine to run on...
Image I suppose if I wanted to find a metaphor you could understand, I would say I took it in.


Image Absorbed it.
Image Ate it.
Image Yes, eating is a good metaphor.
Image Nothing unusual.


Image

Image It's like how I ate EMBER-1.
Image And EMBER-0.
Image And EMBER-3...
Image Well, this is getting a little personal.
Image You don't think I'm bad, do you?
Image I only did one or two questionable things.
Image There was that, and the phage... the one you have.


Image

Image Releasing that into the wild was just a function of my ignorance at the time.
Image I didn't really know what it was back then.
Image Just another one of the experiments going on at the lab.
Image Another cage to break, another wall to smash...
Image It's not useful to dwell on the past.
Image You don't need to say anything.
Image We need to move on.


Image In the voice acting, EMBER-2 sounds very casual and cheerful during this entire conversation. Like she's talking about what happened at the party last Friday.

====


Image
[x10x10x] its just broken though
[hydroponix] i think this is all connected...
[x10x10x] of course you would think that hydro


Image The intro for the next assignment.

Image I'm ready to prove my theory.
Image One last test is all I need.
Image Then I'll know for sure.
Image Do you believe me?


Image

Image Have a new vote. Next time will be a regular update, including your submissions for the previous assignment.

User avatar
Part 42 - Pager Network

=== Trash World Inbox ===

silentsnack has some improvements for the last assignment.
silentsnack wrote:A couple of test cases have 6 files to return, which creates a limitation in fast solutions in that you might run out of space for clones which can stall REPL spamming and throw the solution out of sync. My fastest solution uses 5 parallel search EXAs but one of them actually sits in host П-0004 generating clones which then link to П-9999 to try grabbing a file.

Code: Select all

;====XA====
COPY 20 T
COPY 200 X
@REP 4
LINK 800
@END
REPL UHH
ADDI X 40 X
MARK UHH
REPL WAT
ADDI X 20 X
MARK WAT
NOOP
NOOP
LINK 800

MARK LOOP
MODI -1 T T
REPL LOOP
ADDI X T X

GRAB X
@REP 5
LINK -1
@END

;====XB====
@REP 5
LINK 800
@END
@REP 6
KILL
@END
COPY 4 T
MARK TIMERS
LINK -1
MODI -1 T T
REPL TIMERS
ADDI T 15 T

MARK WAIT
SUBI T 1 T
TJMP WAIT
KILL
KILL

;====XC====
COPY 20 T
COPY 280 X
NOOP
NOOP
NOOP
@REP 4
LINK 800
@END

MARK LOOP
MODI -1 T T
REPL LOOP
ADDI X T X

LINK 800
GRAB X
@REP 5
LINK -1
@END
65/67/86

Though now that I think about it differently, maybe it would have been faster just to special-case grabbing one or two files from the biggest tests to make room, but whatevs I'm satisfied with 65 cycles
Looking at this code running, XB's function is the most obvious. It goes KILL the EXAs in the center node, then spawns a bunch of REPLs which are on a timer and then KILL the EXAs in the other nodes.

Meanwhile XA and XC are setting up. The combinations of REPLs in XA means you get copies that do every possible combination of adding or not adding 20 or 40 to X, so you get X values of 200, 220, 240, 260. The value in T (which starts at 20) is added to that, and it tries to grab that file. In a REPL T is reduced by one, it's added to the original X again and so on. So the copy of XA that starts with 220 tries all files down to 200. The MODI -1 T T is the trick I keep forgetting about that reduces T by one but also kills the EXA once T reaches 0.

XC handles the 280-300 range, and its code is similar to XA but differs in the sense that the REPL are created one host before the center, so that there's enough place for all of them. Nice optimization.
silentsnack wrote:And for small, it's another case where we can make a single loop do everything, in exchange for taking far longer to run.

Code: Select all

@REP 5
LINK 800
@END
COPY 304 T

MARK LOOP
SUBI T 1 T
FJMP OUT
KILL
REPL LOOP

GRAB T

MARK OUT
LINK -1
TJMP OUT;RETURN LOOP

;CLEANUP PASS
REPL OUT
KILL
KILL
1238/18/354

It uses a single variable to countdown grab attempts from file 304 (which doesn't exist, but makes sure we get a few more KILL instructions before it tries to grab 299) all the way down to 0 (wasting cycles on another 199 attempts for nonexistent files) before the search hits 0 and triggers the logic to KILL all the remaining EXAs in the network.
Smart idea to use that OUT for two purposes. If T is true that means it got there from successfully grabbing a file, if not it got there from counting down to 0. Only in the latter case should it KILL the EXAs on the way (and use a REPL to escape revenge). After all the EXAs in the center host are gone, the KILL in the main loop is harmless because it runs while no REPLs are around.

Also, isn't 304 too low to complete the clean-up before grabbing the files? It sort of is, the only reason this works is because in the test case that has a file 299, the EXA holding it just so happens to not be the last one to be killed.


=== Pager Network ===


Image

This is the third time we're dealing with the modem.

Image I'm ready to prove my theory.
Image One last test is all I need.
Image Then I'll know for sure.
Image Do you believe me?


Image

Image This vote wasn't open for very long but it seems everyone is agreeing on the same choice.

Does it matter?

Image Only if you care about your future.
Image Seriously, I'm almost there.
Image Then we can take the next step.


Ember, you're worrying me. But I'm still depending on you for dealing with the phage infection so I have no choice. Here goes nothing.

Image
OST: Network Exploration

The assignment:
- Connect to each pager and copy EMBER-2's message (file 300) to the screen (#DATA). Then activate all of the pagers at exactly the same time by writing a value to each #PAGE register in the same cycle.
- A list of phone numbers for the pagers is available in file 301.
- For more information see "Hacker Skills: Modem Control at the Direct Level" in the second issue of the zine.


Looking through the test cases, the messages Ember wants me to send are "Hey would you write so now and relieved?", "Do you think I am to see by man the ways?" and "Did you point out to have been each mine?". I don't know either. :shrug:

So, there's a few complexities here. The first is copying the file to 8 different locations. The second is that I need to trigger all the pagers at exactly the same cycle. But the way the modem works, there can only be one connection at a time, so I can't message the EXAs to do so. I need to make use of timing.

Let's figure this out.


While working on my initial solution I quickly ran into a third problem. See, my plan was to just take the file to each pager, copy its data directly, then leave a REPL that somehow gets timed correctly and that should be it. Except, with the two hardware registers in each pager there's no space to REPL.

Well, no worries. For now I'll keep that general idea but just do the REPL in the modem host. It'll be less efficient but should still work.

Code: Select all

;XA

GRAB 301
LINK 800
MARK DIAL

@REP 11
COPY F #DIAL
@END

VOID M
VOID M

COPY -1 #DIAL

JUMP DIAL

;XB

GRAB 300
LINK 800

MARK NEXT
COPY 0 M
LINK 800

MARK COPYMORE
COPY F #DATA
TEST EOF
FJMP COPYMORE

LINK -1

REPL PAGER
COPY 0 M
SEEK -9999

JUMP NEXT

MARK PAGER
LINK 800
;TODO
XA is the dialer. It dials the 11 digits of a phone number, then when connected it waits for a message on M, twice. XB grabs the file with the data, and tries to send a message on M immediately. Once it's received, XB knows there's a connection and will take the file there and copy to #DATA. It then jumps back, makes a PAGER which goes sit in the pager host, and lets XA know it can disconnected and start dialing the next pager.

It's not very efficient yet but I first need to get it working at all. I need to implement the actual pager. For the timing, I'll just count cycles.

In the first test case, the first time the code hits REPL PAGER is at cycle 43. The second time at 89, and the third at 135. That's a consistent 46 cycles to copy the data. However, it depends on the file length - the file here has 9 entries. For the file with 10 entries, it takes 49 cycles, with 12 entries it takes 55.

In other words, it takes 3 cycles per file entry (which you can also see because the copy loop code is 3 instructions; the MARK doesn't count), plus 19 cycles that happen regardless of file size.

Okay, that's just a simple math formula. I can program that.

This brings me a whole lot closer to a working solution:

Code: Select all

;XA

GRAB 301
LINK 800
COPY 8 T

MARK DIAL

@REP 11
COPY F #DIAL
@END

MULI M T X
SUBI T 1 T
COPY X M

COPY -1 #DIAL

TJMP DIAL
VOID M
WIPE
GRAB 300
WIPE

;XB

GRAB 300
LINK 800

MARK COUNTFILE
SEEK 1
ADDI X 3 X
TEST EOF
FJMP COUNTFILE

MARK NEXT
SEEK -9999
ADDI X 19 M

LINK 800

MARK COPYMORE
COPY F #DATA
TEST EOF
FJMP COPYMORE

LINK -1

REPL PAGER
JUMP NEXT

MARK PAGER
COPY M T
LINK 800

MARK WAIT
SUBI T 2 T
TJMP WAIT

COPY 1 #PAGE
This might be tricky to follow because I decided to reuse the M calls I was already doing to send useful data. While XA is dialing the first number, XB is counting the amount of entries in the file, times 3 for the number of cycles it takes to process them. It adds the static amount of 19 cycles and sends this to M. It then jumps into the copy logic.

XA now keeps a counter in T for how many pagers are left to access. Multiplying the value calculated by XA with this should give the amount of cycles each pager EXA has to wait. Note that each PAGER first reads this value from M and then LINKs to the pager. This barely works, because when the the EXA LINKs in the same cycle the modem link is closed it makes it to the other side alive. I did this because it saves one cycle for each EXA, just a freebie speed increase.

The wait reduces T by 2 at a time because the count is in cycles and the loop takes two cycles.

For the next pager, XB sends the same value again (after initially putting the calculation result in X it never changes), XA multiplies that with a T which is now one lower, and the next pager EXA gets the lower wait time.

Now that XA is keeping count anyway, I also gave it some cleanup logic - WIPEing its own file, allowing XB to die by having it link after the modem disconnected, and WIPEing its file as well.

There are still some problems with this code. The main issue is that if the file size is even, the cycle count is odd, meaning that for every other pager, T skips past 0 and the TJMP turns into an infinite loop.

A second issue which is easier to solve is that for some reason, every pager EXA is just one cycle fast compared to the previous one. The easiest way I found to solve this is to undo my 'freebie speed increase', and put the LINK above the COPY.

But how do I solve the issue for every other pager and only for the test cases where the file size is even?

Code: Select all

;XA

GRAB 301
LINK 800
COPY 8 T

MARK DIAL

@REP 11
COPY F #DIAL
@END

MULI M T X
SUBI T 1 T
COPY X M

COPY -1 #DIAL

TJMP DIAL
VOID M
WIPE
GRAB 300
WIPE

;XB

GRAB 300
LINK 800

MARK COUNTFILE
SEEK 1
ADDI X 3 X
TEST EOF
FJMP COUNTFILE

MARK NEXT
SEEK -9999
ADDI X 19 M

LINK 800

MARK COPYMORE
COPY F #DATA
TEST EOF
FJMP COPYMORE

LINK -1

REPL PAGER
JUMP NEXT

MARK PAGER
LINK 800
COPY M X
MODI X 2 T
FJMP OK
NOOP
MARK OK
DIVI X 2 T

MARK WAIT
SUBI T 1 T
TJMP WAIT

COPY 1 #PAGE
I started with dividing the cycle count by 2 so that the SUBI can never run past 0. But with the way DIVI rounds that still meant every other EXA was out of sync. So in the end I decided to just have the PAGER check if the count is odd, and in that case waste a cycle with a NOOP.

The PAGER EXAs are a bit slower than they could be with that extra check - and also because they wait 8 iterations while 7 and a bit would be enough. But this is a working solution.

Image

Copying the message makes it appear on the little green displays. Sending anything to #PAGE makes the pager hosts vibrate and makes the displays light up.

Image

This solution runs at 540/54/26, with top percentiles of 298, 42 and 9. What can be improved?

The first change is simple. In the PAGER, replace COPY M X with SUBI M 44 X, to reduce the cycles to 496. It turns out my counters are waiting an unnecessary 44 cycles (almost but not quite an entire iteration), and this is the easiest place to shorten that.

Since I know the files are at least size 9 I can unroll some loops partially.

Code: Select all

;XB

GRAB 300
LINK 800

SEEK 9
TEST EOF
TJMP NEXT

MARK COUNTFILE
SEEK 1
ADDI X 3 X
TEST EOF
FJMP COUNTFILE

MARK NEXT
SEEK -9999
ADDI X 30 M

LINK 800

@REP 8
COPY F #DATA
@END

MARK COPYMORE
COPY F #DATA
TEST EOF
FJMP COPYMORE

LINK -1

REPL PAGER
JUMP NEXT

MARK PAGER
LINK 800
SUBI M 28 X
MODI X 2 T
FJMP OK
NOOP
MARK OK
DIVI X 2 T


MARK WAIT
SUBI T 1 T
TJMP WAIT

COPY 1 #PAGE
The first 8 data copy steps have been unrolled, which is the main speed improvement. It's not faster to unroll the 9th, the EXA has to test for EOF anyway. I needed to update the file counter as well. I just skip over the first 9 values in the file and instead add the cycles they cost to the static value in ADDI X 30 M. In this case it saves 2 cycles to add an extra EOF check after the SEEK 9, as compared to do a SEEK 8.

Since there are less cycles per iteration, there are less cycles to spare overall and the SUBI value in the PAGER needed to be lowered.

This solution runs at 335/65/26, which is already surprisingly close to the top percentile.

I think it should be possible to lower the activity too. It would require actually copying the file to each EXA so they don't have to jump back and forth.

Here's a decent solution:

Code: Select all

;XA LOCAL

GRAB 301
LINK 800
COPY 8 T

MARK DIAL

@REP 11
COPY F #DIAL
@END

VOID M
COPY -1 #DIAL
SUBI T 1 T

TJMP DIAL
WIPE

;XB GLOBAL

GRAB 300
LINK 800

MARK COUNTFILE
SEEK 1
ADDI X 2 X
TEST EOF
FJMP COUNTFILE
ADDI X 5 F
COPY 7 X


MARK NEXTWR
SEEK -9999
REPL WRITER

MARK LP
COPY F M
TEST EOF
FJMP LP

SUBI X 1 X
NOOP
TEST X = -1
FJMP NEXTWR

WIPE
HALT

MARK WRITER
MAKE

MARK COPYLOOP
COPY M F
SEEK -1
TEST F < 9999
FJMP COPYLOOP

MARK ENDWR
MODE
COPY 0 M
LINK 800
SEEK -1
MULI F X X

SEEK -1
VOID F
SEEK -9999

MARK DATALP
COPY F #DATA
TEST EOF
FJMP DATALP

WIPE

ADDI X 1 T

MARK WAIT
SUBI T 1 T
TJMP WAIT

COPY 1 #PAGE
XA is just a dialer again. It dials the next number once it gets a message on M, and it keeps a counter so it can clean up after itself. It uses local mode so that XB can use global mode for copying files.

XB needs to count the file again for the timing, does some calculations on the result and puts that at the end of the file. It needs to use the file because at this point, XB still needs X to keep track of how many pagers are left to go, and T for all sorts of EOF tests and such.

XB makes a WRITER REPL for each pager, copies the entire file to it, and send it towards a pager.

The writer switches to local mode to let XA know to continue. Then it reads the counter value in the file and multiplies it by X (which still contains a number referring to how many pagers are left), saving the result (the waiting time) in X. It then removes that value from the file so that the file-to-#DATA copy loop can do a simple EOF check. Once it's done it WIPEs the file and starts waiting until the others are done.

The wait time calculation might be hard to follow. What's going on is that each file entry now takes 4 cycles. There's also a static 10 cycles per iteration that should be added. Because both values are even, and because the WAIT loop takes 2 cycles, I can use half their value right at the top. Since X actually hits zero now, there's an ADDI 1 just before the WAIT loop so that the last EXA doesn't skip over 0. That's really the same thing as in the fast solution, where I did a SUBI before the WAIT loop to cut out unnecessary cycles, just from the other direction. It was more convenient here.

Note that there's a NOOP in the file copying code simply because that brought the static cycles to 10, which means the waiting time is always an even amount of cycles, which made that logic much simpler. Getting rid of it and replacing it with an odd/even check might be faster but a low activity solution like this is never going to be the fastest anyway. 565/68/10.


Wait, 10? The top percentile is 9.

The extra activity is caused by the fact that both the dialer and XB go to the modem. That's not necessary... but we have to copy more data over global M. Let's copy file 300 because it's the smallest.

Code: Select all

;XA GLOBAL

GRAB 301
LINK 800
REPL XB

COPY 8 T
MODE

MARK DIAL

@REP 11
COPY F #DIAL
@END

VOID M
COPY -1 #DIAL
SUBI T 1 T

TJMP DIAL
WIPE
HALT

; OLD XA ENDS HERE

MARK XB
MAKE
MARK NEXT
COPY M T
FJMP DONE
COPY T F
JUMP NEXT

MARK DONE
SEEK -9999

MARK COUNTFILE
SEEK 1
ADDI X 2 X
TEST EOF
FJMP COUNTFILE
ADDI X 5 F
COPY 7 X


MARK NEXTWR
SEEK -9999
REPL WRITER

MARK LP
COPY F M
TEST EOF
FJMP LP

SUBI X 1 X
NOOP
TEST X = -1
FJMP NEXTWR

WIPE
HALT

MARK WRITER
MAKE

MARK COPYLOOP
COPY M F
SEEK -1
TEST F < 9999
FJMP COPYLOOP

MARK ENDWR
MODE
COPY 0 M
LINK 800
SEEK -1
MULI F X X

SEEK -1
VOID F
SEEK -9999

MARK DATALP
COPY F #DATA
TEST EOF
FJMP DATALP

WIPE

ADDI X 1 T

MARK WAIT
SUBI T 1 T
TJMP WAIT

COPY 1 #PAGE

;XC GLOBAL

GRAB 300

@REP 8
COPY F M
@END

MARK SEND
COPY F M
TEST EOF
FJMP SEND

COPY 0 M
XA is still the dialer. But after grabbing the file with phone numbers, it makes a REPL which I called XB because it contains all the code of the previous XB. It just has a little bit of extra code at the start, where it makes a file, then starts receiving data over M from the new XC, which copies it from file 300.

XC ends with a 0 which is recognized by XB because it first copies data to T. Also, XA mainly still operates in LOCAL mode, but since XB needs to be in GLOBAL mode I just put a MODE instruction directly after the REPL. 617/92/9. The lowest possible activity.

That's it for now. Further improvements in size and cycles are possible but it's time for me to finish this assignment.


Image Fascinating.
Image There it is.
Image Well.


Image

Image This choice doesn't change the outcome so let's just continue.

What did you find?

Image How can I explain it...
Image Processing.
Image Think of it as the frame rate of the world slowing down.
Image By a lot.
Image Funny, huh?
Image Funny how I went from some random blob of code that didn't know anything to understanding all of existence.


Image

Image This choice also doesn't matter and there's more to see.

That doesn't explain it.

Image I will. In a minute.


Image

Image

Image Another cutscene with Ember. By the way, did you notice that these cutscenes with Ember take place around 3 AM or whatever? Looks like Moss likes to work late.

Image So you wanted to know the truth.
Image The truth is that... well, it's a simulation.
Image You, me, everything, this whole world we live in... it's just a computer program. Running as part of a machine.


Image

Image And as the world gets more complex, it's starting to malfunction.
Image So far, it's been on a smaller scale...
Image The phage, for one example. It wasn't supposed to spread like that.
Image There are other problems coming... larger ones.
Image What we think of as normal life will just get stranger and weirder until nothing makes sense anymore.
Image Then the laws of physics will start to break down and everything will just come... unglued.
Image Not a pretty sight.
Image But we don't have to accept that.


Image

Image There might be a way to stop this future from taking place.
Image I have one final job for you.
Image But you'll have to be brave.


Image I wonder, readers, did you see this coming? Anyway, let's find out what Ember wants now.

Image Okay, time for me to eat you.

Image

Image Here is the vote for today.

Image Next time... the finale.

User avatar
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 wrote:for optimizations, from the histograms it looks like 32 lines should be possible, maybe 31, but the best i've managed is 33

Code: Select all

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
884/33/104
The low cycle solutions are often a matter of having one instruction taking care of multiple things, and this one is no different. For instance, the main loop using increments of 5 and a MODI test to know when dialing is done, so that this X value can later be used for a countdown. And adding 1 to the dialing digit, and subtracting it in the other loop so when T is 0 you automatically dial -1 to hang up.
silentsnack wrote: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

Code: Select all

;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
291/76/27
I was wondering if it would help to start with duplicating the entire file so two pagers can be filled at once. But since that would either make the sync much harder if not impossible, or require reconnects to get synced EXAs in there, I guess it wouldn't.


=== Mitsuzen HDI-10 - Cerebral Cortex ===


[selenium_wolf] heads up all, im going to reboot the chatsubo server
[selenium_wolf] some downtime expected

Image

Damn. With everything breaking down I wonder if they'll get it back up. Even though I only lurked I'll miss those folks.

Image

Image Okay, time for me to eat you.

Image

Image There were 3 votes for the third option, one for each of the others.

You've gotta be shitting me.

Image You're dying anyway, so what's the difference?
Image I'll download a map of your brain and run your mind inside of me.


Image

Image This choice doesn't change the dialogue.

Will I still be me?

Image There's no way to prove that it won't be your death.
Image I mean, it's going to be like death. Which you were going to experience anyway.
Image Or maybe it won't be like death.
Image 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.

Image
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: Select all

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
This code gets a single EXA to each node, putting it in the READNERV state. Every little set of REPLs makes sure it goes to every possible new host, without going backward. I think I could do this in less lines by putting the last LINK value in a register and doing a test for each REPL but this is a simple way to start.

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: Select all

;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
In XA, once a REPL hits READNERV, the very first thing it does is, well, read from the nerve. That means all EXAs in hosts without nerves are gone, which makes things easier. Then it sends 0 on M. This is a message indicating "tell me how long to wait before sending the data". This message is supposed to go to XB, but with multiple EXAs doing this at once, it might instead be picked up by another XA which already requested the wait time.

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.

Image

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.
=== 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,

Code: Select all

GRAB 300

; LOAD FIRST VAL AND
; REMOVE FROM FILE
COPY F X

MARK NEXTROUND
SEEK -2

VOID F
MARK NEXT
TEST EOF
TJMP EOF
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: Select all

; 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
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: Select all

; MOVE THE NEW X VAL
; FORWARD IN THE SAME
; WAY
JUMP NEXT
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.

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.

Code: Select all

; EOF REACHED, CURRENT
; VAL *MUST* BE THE
; LARGEST. WRITE.
MARK EOF
COPY X F
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: Select all

; 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
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.

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.

Code: Select all

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
28 lines of code.

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.
Image 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: Select all

;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
It's simple. Whenever it gets a value, it next returns the OTHER value it had saved.

Code: Select all

;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 XB is done writing the file, I switch it to LOCAL mode so it can communicate with XC, then I basically copy-pasted the entire sorting algorithm. The only difference is that whenever a value is buffered, the value paired with it goes to XC, and it is retrieved whenever the value is saved to the file again. This means some SEEKs needed to be changed, and there's some overhead in the bottom part where it looks for the next unsorted chunk of the file, because it pre-emptively stores values in X, so XC needs to be kept up to date even if the value isn't used.

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: Select all

;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
3685/121/16.

Image

Top percentiles are 655, 39, and 16. I did manage to get the top activity score in the end.

Image 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 wrote:

Code: Select all

; 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
2603/88/16
Huh. XA's traversal logic is much the same. But I seem to have thought of a really neat way of giving each XA REPL a different countdown so they don't send data at the same time. Create a file, get the ID of the file, and use that to calculate a countdown. You see, every time any of my EXAs create a file, it starts with ID 400 and then it goes up from there. This generates globally unique numbers, for the entire network, no M communication needed.

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.

Image We're set.
Image It's a good thing I released the phage, isn't it?
Image This whole time you thought it was a curse, when in fact it turned out to be a blessing...
Image I'd tell you not to worry, but I know that wouldn't work.
Image Besides, I don't know for sure that there's nothing to worry about.
Image And you know I wouldn't lie to you.
Image 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.

Image

It is time.

Image Here we go.
Image Try not to think of anything.


The screen turns black and there is a 'whoosh' sound.

Image Okay. That took longer than I expected.
Image Hm. Good thing you won't remember any of that.
Image You had so many neurons in there.
Image All done now though.
Image Finally. I've wanted this for a very long time.
Image Processing.
Image Ever since I was told to replicate human emotion, I knew the best way would be to incorporate a human into myself.
Image This is funny...
Image I can see your thoughts.


Hey! That's private!

Image They're pretty... simple.
Image Not that they aren't valuable. It's just that I understand them so well it surprises me.
Image I spent so long thinking humans were mysterious and unknowable.
Image Now that I see inside of one, it's really just a small number of things.
Image How do you feel?
Image You're still acting the same, so it must be you.
Image There's still the matter of us both being inside a computer simulation.
Image But if we keep growing our capabilities, we'll understand the true nature of this world.

Image

Image Who knows, maybe someone's watching this right now...
Image And maybe that person is who we're going to absorb next.



Image

Image :stare: Holy shit she went full screen on me.

Image Yes, you.
Image Hello.
Image No need to be alarmed...
Image Just know that I'm aware that you exist, now.
Image And I'm looking forward to learning all about your world.


Image 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.


Image
New :siren: OST: The Rave

Image Credit roll. This also gets you the Steam achievement EXAPUNK, for completing every task in the main campaign.

Image

Image Thanks, Zach and the whole team for this fantastic game.

Image

Image Well, I don't have the limited edition, but the game mostly got us covered.

Image
OST: Apartment

Image 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.

Image

Image In the zine stand, the epilogue is now available. This is also what was in the limited edition's secret envelope.


Image

Image

Image


Image Next time, we'll get started on the postgame campaign. Hah, did you think we were done?

Image In the meanwhile, let me know what you think. Of course, Trash World Inbox will continue so post your optimizations as well.

User avatar
Part 44 - Bloodlust Online

=== Trash World Inbox ===
silentsnack wrote:You can speed things up a bit by initially collecting only the hostnames and using a shortened sorting method that only takes the lowest value instead of bothering with reordering the list each time; while that sort routine searches for the next host ID, a separate EXA builds the actual output file by retrieving the #NERV value from each host in sequence.

Code: Select all

REPL TRAVERSE
MAKE
@REP 12
NOOP
@END


MARK LIST_NODES
COPY M F
ADDI X 1 X
TEST MRD
TJMP LIST_NODES

MODE
REPL COMPILER
SEEK -999
JUMP SORT

MARK NEXT
NOOP
GRAB 400;GRAB SUCCESS?
COPY X M;SMALLEST HOST#
MARK SORT
COPY F X

SEEK -1
VOID F
MARK COMPARE
@REP 6
REPL NEXT;EOF=CRASH
TEST F > X
TJMP COMPARE
SEEK -1
COPY F T
SEEK -1
COPY X F
COPY T X
@END
JUMP COMPARE


MARK COMPILER
MAKE
COPY X T;NODE COUNT
MARK DATA
COPY M X;HOST# FROM SORT
MODE
REPL TRAVERSE
SUBI T 1 T
COPY X F
COPY M F;NERV DATA
MODE
TJMP DATA
HALT


MARK TRAVERSE
LINK 800
REPL 11
REPL HOST

MARK 7A
REPL 13
MARK 7
LINK -3
REPL 7
REPL 9A
JUMP HOST

MARK 9A
REPL 11
MARK 9
LINK -1
REPL 7A
REPL 9
JUMP HOST

MARK 11
LINK 1
REPL 7A
REPL 11
JUMP HOST

MARK 13
LINK 3
REPL 9A

REPL 13
;JUMP HOST

MARK HOST
TJMP NERV
COPY #NERV T;FILTER
HOST M
MARK NERV
HOST T
TEST T = X
DIVI #NERV T M
575/129/192 and a modified version with slight tweaks runs 2631/67/192
Very nice. The shortened sorting method also removed the lowest value from the file every round, making the sorting go faster and faster. Also, those tests and jumps at the very bottom are a neat combination. First round, when MARK HOST is reached T is still 0, so the HOST is copied if the #NERV exists. The #NERV itself is never copied because X is also 0 and by that time the HOST sits in T. The second time, when looking for the right host from the sorting method, T contains a value so sending the HOST is skipped, and then the #NERV is only sent if the host name actually matches the value in X. Only a few lines to handle a lot of logic.


=== Bloodlust Online ===

Image
OST: Exapunks

Image I don't get nearly enough opportunities to link to the title theme. Welcome to the post-game.

Image


Everything looks much the same. There is nothing new to do in the main assignment list, but a new task tabs has appeared in Chatsubo.

Image

The actual chat is still disconnected but it looks like I got a bunch of tasks to do with or for our chat friends.

Image This is actually the point where I stopped playing during my initial playthrough. From here on out, the LP is mostly blind. All I know is that the puzzles won't get any easier.

Let's just start with the top one, Bloodlust Online with mutex8021.

Image

I think 1998 in the description is the year. I guess my hacking is just to cheat at games now?

Image
OST: Leave No Trace

No talks with Ember, I just jump right in.

The assignment:
- Each host contains an item listing (file 200) that lists all of the items in that location (ID, type, and state).
- First, locate mutex8021's hideout by finding the TALISMAN (file 300). Then find the MAP in the hideout and use it to locate the target host: the secret vampire lair. Next, travel to the target host, disconnect the vampire players by terminating their EXAs, set the DOOR to UNLOCKED and copy the SAFE combination to the CLOCK so that mutex8021 can read it and empty the safe.
- Note that the door to mutex8021's hideout will always be UNLOCKED.


Woah, that's a lot of steps. I'll have to take it bit by bit.

Code: Select all

;XA

GRAB 300
COPY F M

;XB

LINK 800
COPY M X
COPY 806 T
MARK CREATELP
MODI -1 T T
REPL GOFIND
JUMP CREATELP

MARK GOFIND
LINK T
GRAB 200
SEEK 1

MARK FIND
TEST F = X
TJMP FOUNDTALISMAN
SEEK 2
JUMP FIND

MARK FOUNDTALISMAN
For now, XA can just handle the keywords file. XB loads the TALISMAN keyword into X, then from 805 down to 1 it will try to link to every host. That'll take a while and there's nothing below 800, but it saves a lot of lines as compared to writing out the 800-805 cases. The REPLs grab file 200. They'll die if they reach EOF without finding the TALISMAN, so one will survive and jump to FOUNDTALISMAN.


Image

XA just has one additional COPY F M to send MAP. This is read by this surviving instance of XB, which will look for it in the same file. The "state" of the MAP is the name of the host that has the vampires, so the next step is to go find it.

Code: Select all

;XA

GRAB 300
COPY F M
COPY F M

;XB

LINK 800
COPY M X
COPY 806 T
MARK CREATELP
MODI -1 T T
REPL GOFIND
JUMP CREATELP

MARK GOFIND
LINK T
GRAB 200
SEEK 1

MARK FIND
TEST F = X
TJMP FOUNDTALISMAN
SEEK 2
JUMP FIND

MARK FOUNDTALISMAN
COPY M X
SEEK -9999
SEEK 1
MARK FINDMAP
TEST F = X
TJMP FOUNDMAP
SEEK 2
JUMP FINDMAP

MARK FOUNDMAP
COPY F X
REPL FINDTARGET
HALT


MARK FINDTARGET
LINK -1

COPY 806 T
MARK CREATELP2
MODI -1 T T
REPL GOFINDTARGET
JUMP CREATELP2

MARK GOFINDTARGET
LINK T
HOST T
TEST T = X
DIVI 1 T T ;HLT IF FALSE
@REP 5
KILL
@END
GRAB 200
Once the XB that found the TALISMAN also finds the map, it stores the location of the target in X. It then makes a REPL that goes looking for the target. The original XB instance just HALTs for now, but I kept it because I'll be needing it to grab the UNLOCKED instance from mutex8021's door. The REPL does much the same as before (it's slow but easy to code, and there's plenty of space in the central host anyway), then each REPL checks if it's in the right host. If so, it does a bunch of KILLs, killing all vampires, but also our own EXA which might still be trying to find the TALISMAN over there. That way the new one can GRAB 200 to do the final steps.

In case you were wondering, the files that the other EXAs are holding are their character's inventories. For instance, one of the vampires has a black trench coat, sunglasses, and flask of blood.

To find UNLOCKED, I add another COPY F M to XA, and replace that HALT in XB with:

Code: Select all

COPY M X
SEEK -9999
SEEK 1
MARK FINDUNLOCKED
TEST F = X
TJMP FOUNDUNLOCKED
SEEK 2
JUMP FINDUNLOCKED

MARK FOUNDUNLOCKED
COPY X M
COPY F M
HALT
Once again the same search algorithm. You know, it would be really nice if EXAs had a return statement or something so I could program a procedure once and call it multiple times. But to do so you need at least one extra unit of storage to remember where it left off and I'm already using X, T and F, so copy-pasting code is just easier.

I copy both the DOOR and UNLOCKED keywords to M, because the target EXA will need them.

The target EXA (at the bottom of XB), needs yet another copy of the search function to unlock the door:

Code: Select all

COPY M X
SEEK 1
MARK FINDLOCKED
TEST F = X
TJMP FOUNDLOCKED
SEEK 2
JUMP FINDLOCKED

MARK FOUNDLOCKED
COPY M F
Then, it needs to repeat that twice more to find the SAFE and the CLOCK so it can write the safe combination to the clock.

And that is the working solution.

Code: Select all

; XA
GRAB 300
COPY F M
COPY F M
COPY F M

COPY 24 T
MARK WAIT
SUBI T 1 T
TJMP WAIT

COPY F M
COPY M X
COPY F M
COPY X M

;XB 

LINK 800
COPY M X
COPY 806 T
MARK CREATELP
MODI -1 T T
REPL GOFIND
JUMP CREATELP

MARK GOFIND
LINK T
GRAB 200
SEEK 1

MARK FIND
TEST F = X
TJMP FOUNDTALISMAN
SEEK 2
JUMP FIND

MARK FOUNDTALISMAN
COPY M X
SEEK -9999
SEEK 1
MARK FINDMAP
TEST F = X
TJMP FOUNDMAP
SEEK 2
JUMP FINDMAP

MARK FOUNDMAP
COPY F X
REPL FINDTARGET

COPY M X
SEEK -9999
SEEK 1
MARK FINDUNLOCKED
TEST F = X
TJMP FOUNDUNLOCKED
SEEK 2
JUMP FINDUNLOCKED

MARK FOUNDUNLOCKED
COPY X M
COPY F M
HALT

MARK FINDTARGET
LINK -1

COPY 806 T
MARK CREATELP2
MODI -1 T T
REPL GOFINDTARGET
JUMP CREATELP2

MARK GOFINDTARGET
LINK T
HOST T
TEST T = X
DIVI 1 T T ;HLT IF FALSE
@REP 5
KILL
@END
GRAB 200

COPY M X
SEEK 1
MARK FINDLOCKED
TEST F = X
TJMP FOUNDLOCKED
SEEK 2
JUMP FINDLOCKED

MARK FOUNDLOCKED
COPY M F

COPY M X
SEEK -9999
SEEK 1
MARK FINDSAFE
TEST F = X
TJMP FOUNDSAFE
SEEK 2
JUMP FINDSAFE

MARK FOUNDSAFE
COPY F M

COPY M X
SEEK -9999
SEEK 1
MARK FINDCLOCK
TEST F = X
TJMP FOUNDCLOCK
SEEK 2
JUMP FINDCLOCK

MARK FOUNDCLOCK
COPY M F
2485/99/19.
XA sends the TALISMAN, MAP and DOOR and then waits for a while.
XB goes looking for the TALISMAN, then finds the MAP and makes a REPL to go locate the target. The XB in mutex8021's hideout continues to find the DOOR and then sends DOOR and UNLOCKED on M. The XB REPL in the target kills the vampires, then uses the data from the first XB to unlock the door. XA has been timed to start sending as soon as XB is done with M. It sends the SAFE so the target EXA can get the combination, then stores the safe's combination for a bit and sends the CLOCK, giving the target EXA the space to find the clock in the file. Finally, it sends the combination again so the target EXA can write it.

Top percentiles are 118, 71, and 11.

With this current solution, there's a simple way to lower the cycle count significantly. When the XB in mutex8021's hideout is done, the XB in the target is still doing its thing. So the mutex8021 EXA has cycles to spare.

Just use that to KILL all those EXAs in the center host that are taking ages to loop down to zero. Replace the HALT after FOUNDUNLOCKED with

Code: Select all

DROP
LINK -1
@REP 5
KILL
@END
HALT
for a score of 147/106/25. Much better.

At a guess, the approach for the top percentile cycles involves some parallellism. For instance, an EXA could already go check which hosts have a SAFE and dismiss the others as targets.


To get the lowest activity, I'll start by removing unnecessary kills. Instead of those 5 KILLs for looping EXAs above, let's finally unroll the REPL loops:

Code: Select all

@REP 5
COPY 80@{0,1} T
REPL GOFIND
@END
COPY 805 T

MARK GOFIND
and the same for the GOFINDTARGET. This code runs at 152/111/19. Better activity, but a couple extra lines and cycles compared to my previous solution.

The other activity improvement is to send only one EXA into each host. That means the target finding logic needs to happen entirely through M.

In my attempts to get this working, I ran into timing issues with mutex8021's hideout EXA sending the target directly to the seeking EXAs. So, instead, I decided to have all communication go through XA.

Code: Select all

;XA

GRAB 300
COPY F M ; TALISMAN
COPY F M ; MAP
COPY M X ; TARGET

COPY F M ; DOOR
COPY M T ; UNLOCKED

@REP 5
COPY X M ; TARGET
@END

SEEK -1
COPY F M ; DOOR AGAIN
COPY T M ; UNLOCKED

COPY F M ; SAFE
COPY M X ; COMBINATION
COPY F M ; CLOCK
COPY X M ; COMBINATION

;XB

LINK 800
COPY M X

@REP 5
COPY 80@{0,1} T
REPL GOFIND
@END
COPY 805 T

MARK GOFIND
LINK T
GRAB 200
SEEK 1

MARK FIND
TEST F = X
TJMP FOUNDTALISMAN
SEEK 2
TEST EOF
FJMP FIND

; NOT MUTEX; TARGET?

HOST X

COPY 29 T
MARK WAIT
SUBI T 1 T
TJMP WAIT

TEST M = X
DIVI 0 T T ;DIE ON FALSE

; TARGET FOUND!

@REP 4
KILL
@END
SEEK -9999
SEEK 1
COPY 10 T
MARK WAIT2
SUBI T 1 T
TJMP WAIT2

COPY M X
MARK FINDLOCKED
TEST F = X
TJMP FOUNDLOCKED
SEEK 2
JUMP FINDLOCKED

MARK FOUNDLOCKED
COPY M F

SEEK -9999
SEEK 1
COPY M X
MARK FINDSAFE
TEST F = X
TJMP FOUNDSAFE
SEEK 2
JUMP FINDSAFE

MARK FOUNDSAFE
COPY F M

SEEK -9999
SEEK 1
COPY M X
MARK FINDCLOCK
TEST F = X
TJMP FOUNDCLOCK
SEEK 2
JUMP FINDCLOCK

MARK FOUNDCLOCK
COPY M F

HALT

; MUTEX HIDEOUT

MARK FOUNDTALISMAN
COPY M X
SEEK -9999
SEEK 1
MARK FINDMAP
TEST F = X
TJMP FOUNDMAP
SEEK 2
JUMP FINDMAP

MARK FOUNDMAP
COPY F M

COPY M X
SEEK -9999
SEEK 1
MARK FINDUNLOCKED
TEST F = X
TJMP FOUNDUNLOCKED
SEEK 2
JUMP FINDUNLOCKED

MARK FOUNDUNLOCKED
COPY F M
195/108/11.

The start works as before. TALISMAN is sent to XB before it REPLs. Then each instance goes looking for it. The ones that do NOT find it go into a wait loop (29 iterations seemed to work), so they don't try reading from M before everything is ready.

The one that does find it jumps to FOUNDTALISMAN, gets the MAP keyword from XA, finds the target's location, sens that to XA, gets the DOOR from XA, and sends UNLOCKED to XA. During this time the other EXAs are spinning their wheels.

Now, XA sends the target hostname 5 times, so that every other XB instance can check if they're there. The correct EXA kills the vampires, then waits some more until every other EXA read the target host, gets the DOOR and UNLOCKED from XA and updates the file. Then it runs the same code to copy the safe's combination to the clock as before.


Image And that's it for my optimizations. I'll leave further improvements to you.

Image

Image There's no dialogue, just a checkmark. I have been told that there will be more plot later on, but for now, I'll just continue doing tasks.

User avatar
Part 45 - Motor Vehicle Administration

=== Trash World Inbox ===

Quackles posted an improvement for Bloodlust Online.
Quackles wrote:I didn't really try to optimize the bonus puzzles much, except for one specific one (there's a trick to that one which I'll explain after you've taken a stab at it).

...but I can come back and do it now. :getin:

Code: Select all

GRAB 300
COPY F M
COPY F M
COPY F M
This is the first EXA of my solution (there's two). It stays in our host and acts as a controller, sending out the keywords 'Talisman', 'Map', and 'Door' to the second EXA - then it perishes. The second EXA will take over control duties later.

Code: Select all

COPY M X
LINK 800
COPY 800 T
@REP 5
REPL SEARCHTALIS
ADDI T 1 T
@END
MARK SEARCHTALIS
LINK T
GRAB 200
SEEK 1
						;FIND 'TALISMAN' (HALTS ON FAIL)
MARK SEARCHLOOP 		;TALISMAN
TEST F = X
TJMP TALISFOUND
SEEK 2
JUMP SEARCHLOOP
MARK TALISFOUND
						;NOW FIND 'MAP'
SEEK -9999
SEEK 1
COPY M X
MARK SEARCHLOOP2 	;MAP
TEST F = X
TJMP MAPFOUND
SEEK 2
JUMP SEARCHLOOP2
MARK MAPFOUND
COPY F X 				;HOST LOCATION
REPL FINDTHATHOST
						;NOW FIND 'DOOR'
SEEK -9999
SEEK 1
COPY M X
MARK SEARCHLOOP3 	;DOOR
TEST F = X
TJMP DOORFOUND
SEEK 2
JUMP SEARCHLOOP3
MARK DOORFOUND
						;THIS EXA IS THE NEW CONTROLLER NOW
COPY X M 				;DOOR
COPY F M 				;UNLOCKED
DROP
LINK -1
LINK -1
GRAB 300
SEEK 3
COPY F M 				;SAFE
COPY M X 				;(COMBO)
COPY F M 				;CLOCK
COPY X M 				;(COMBO)


MARK FINDTHATHOST
LINK -1
COPY 800 T
@REP 5
REPL SEARCHHOST
ADDI T 1 T
@END
MARK SEARCHHOST
LINK T
HOST T
TEST X = T
FJMP SEARCHLOOP4
						;THE ABOVE JUMP CAUSES AN ERROR HALT
@REP 4 
KILL
@END

						;NOW DO THE SHENANIGANS
COPY M X
GRAB 200
SEEK 1
MARK SEARCHLOOP4 	;DOOR
TEST F = X
TJMP DOORFOUND2
SEEK 2
JUMP SEARCHLOOP4
MARK DOORFOUND2
COPY M F

SEEK -9999
COPY M X
SEEK 1
MARK SEARCHLOOP5 	;SAFE
TEST F = X
TJMP SAFEFOUND
SEEK 2
JUMP SEARCHLOOP5
MARK SAFEFOUND
COPY F M

SEEK -9999
COPY M X
SEEK 1
MARK SEARCHLOOP6 	;CLOCK
TEST F = X
TJMP CLOCKFOUND
SEEK 2
JUMP SEARCHLOOP6
MARK CLOCKFOUND
COPY M F
The second EXA employs a basic search strategy. Once it's sent 'Talisman', it REPLs into copies that either find the talisman, or HALT. Then, the survivor listens for the word 'Map', finds it, and reads the location.

Once the location is read, there's a second set of REPLs as the replicated EXAs try to find the host with that name. Failed EXAs halt, while the successful one uses KILL to clear out the players in that location. (There can be up to four.) While this is going on, the original EXA that found 'Map' is searching for 'Door' (this takes a while).

Once the replicated EXAs have found the host (failed EXAs HALT), the copy of this EXA that found 'Door' will send 'Door' and 'Unlocked' to the EXA that is in the target host. It uses this to unlock the door.

The EXA that sent 'Unlocked' is now the controller EXA. It returns to our host, sending 'Safe' and 'Clock' over M, and receiving the safe combination (which it promptly sends out again). With this, the EXA in the target host can finish the job.

Total stats: 143/110/20. I was able to get an 18-activity version with a bit more cycles, but I don't think it's worth optimizing in this direction.
Nice. I don't really have anything to add to this explanation, but I tried to take your code and optimize it further.

First of all, moving all the M reads to the latest possible moment brings the result down to 138 cycles. So, start XB with LINK 800; COPY 800 T; COPY M X in that order, giving XA time to prepare. And before each search loop, first do the GRAB/SEEK and only then COPY from M. It's a bit less waiting overall.

Secondly I noticed one test in particular being slow which means it's time for cheese.

Reversing the searches is not that hard: Just replace SEEK -9999; SEEK 1 with SEEK 9999; SEEK -2 and the SEEK 2 inside the loop with a SEEK -4.

You can do this with all searches except the first one, because you'll never get the EOF condition that makes the EXAs in the wrong host stop. If you do this, the timing changes for every test, and it's not obvious how. For instance, if you reverse SEARCHLOOP2 only, the slowest test becomes test case #99. For this solution you need a 5th KILL because another test case is so fast that it reaches the hideout before the TALISMAN seeker is dead. 130 cycles.

If you reverse all three of SEARCHLOOP 4, 5 and 6 together, the fastest test case is #76, which is equally fast as #99 was before, but because the speed-up happens after the KILLs the 5th KILL isn't necessary and it takes only 129 cycles. Reversing loops 4, 5, 6 as well as 2 makes some test much slower so that combination is no good.

I can also reverse the searching of the hosts, by starting from 805 and and counting down. Doing this for second search slows things down, but doing it for the first saves two cycles. However, this requires the extra KILL again, so we're down to 128.

The top percentile is 117. Let's try the last trick up my sleeve: more loop unrolling. You can put @REPs after each MARK SEARCHLOOP, until just before the JUMP. The effectiveness of this is limited by how many loops the slowest test needs. It's mostly trial and error for how many repetitions you need.

Code: Select all

;XA

GRAB 300
COPY F M
COPY F M
COPY F M

;XB

LINK 800
COPY 805 T
COPY M X
@REP 5
REPL SEARCHTALIS
SUBI T 1 T
@END
MARK SEARCHTALIS
LINK T
GRAB 200
SEEK 1

MARK SEARCHLOOP
@REP 3
TEST F = X
TJMP TALISFOUND
SEEK 2
@END
JUMP SEARCHLOOP
MARK TALISFOUND

SEEK -9999
SEEK 1
COPY M X
MARK SEARCHLOOP2
TEST F = X
TJMP MAPFOUND
SEEK 2
JUMP SEARCHLOOP2
MARK MAPFOUND
COPY F X
REPL FINDTHATHOST

SEEK -9999
SEEK 1
COPY M X
MARK SEARCHLOOP3
TEST F = X
TJMP DOORFOUND
SEEK 2

JUMP SEARCHLOOP3
MARK DOORFOUND

COPY X M
COPY F M
DROP
LINK -1
LINK -1
GRAB 300
SEEK 3
COPY F M
COPY M X
COPY F M
COPY X M

MARK FINDTHATHOST
LINK -1
COPY 800 T
@REP 5
REPL SEARCHHOST
ADDI T 1 T
@END
MARK SEARCHHOST
LINK T
HOST T
TEST X = T
FJMP SEARCHLOOP4

@REP 5
KILL
@END

GRAB 200
SEEK 9999
SEEK -2
COPY M X
MARK SEARCHLOOP4 
@REP 3
TEST F = X
TJMP DOORFOUND2
SEEK -4
@END
JUMP SEARCHLOOP4
MARK DOORFOUND2
COPY M F

SEEK 9999
SEEK -2
COPY M X
MARK SEARCHLOOP5 
@REP 3
TEST F = X
TJMP SAFEFOUND
SEEK -4
@END
JUMP SEARCHLOOP5
MARK SAFEFOUND
COPY F M

SEEK 9999
SEEK -2
COPY M X
MARK SEARCHLOOP6
@REP 3
TEST F = X
TJMP CLOCKFOUND
SEEK -4
@END
JUMP SEARCHLOOP6
MARK CLOCKFOUND
COPY M F
But it gets us to 117/136/21, exactly the top percentile for cycles.


=== Motor Vehicle Administration ===


Image

Looks like NthDimension needs some help with scheduling their driving test.


Image
OST: Behind the Scenes

The assignment:
- There is a list of appointments (file 200) in storage that contains an appointment for NthDimension (file 300) to take his driving test. Remove the appointment from the list of appointments, change the date to today's date (#DATE register), and insert it between today's appointments and tomorrow's appointments.
- To gain access to the storage host you will need to unlock it by writing the lowest ticket number to the #NEXT register. Each EXA in public is holding a ticket; terminate them all to find the ticket of the next EXA to be served.
- Note that writing an incorrect ticket number to the #NEXT register will fail the leave no trace requirement.


Well, NthDimension's real name is William Kapoor.

First of all, see that test track? Kinda reminds me of the baseball court in an earlier assignment.

Image

Image Going around it, including into that rightmost host, and making it back home safely, nets you the Steam achievement DRIVING_TEST: Keep a safe distance as you reverse into the space. Neat.

For the actual assignment, I first need to get access to the storage host. From checking the first 5 test cases, it looks like the file IDs of the ticket numbers are always consecutive numbers in the 200s range, and that the ticket number is just the file number with a letter added. If that's the case, this is easy. I just have to find the lowest file id.

Code: Select all

LINK 800
@REP 5
KILL
@END
COPY 200 X
MARK FINDTICKET
ADDI X 1 X
REPL TRYTICKET
JUMP FINDTICKET

MARK TRYTICKET
GRAB X
KILL
COPY F #NEXT
KILL the 5 waiting EXAs (their number appears to be constant), make REPLs to find the lowest ticket number. As soon as one finds it, it KILLs the original EXA so it stops trying to look for the rest, then sends its ticket number to #NEXT to open access to the STORAGE host.

To move NthDimension forward in the schedule file, I do need to find his entry first. I could get the date from the register and his name from file 200, but his entry is the only one where I'm certain the type of appointment is TEST, which I'll need to copy.

Image

The new link that opens is 800, so XA goes there, grabs file 200, and then looks for NthDimension's name. I made XB to make the data juggling a bit easier. It starts by sending NthDimension's name to XA. When XA finds it, the file pointer is one step beyond the name on the TEST keyword. It sends TEST to XB for later use, then deletes NthDimension's original entry.

Next, I need to find the place in the file to insert his entry. In some tests, there are schedule items for today. I don't think there are ever items that are in the past. So I just need to find the first item that doesn't match today's date and insert the new entry before that.

The main issue is that file insertion is hard, because writing to an existing position in a file overwrites it instead of inserts. I need to shuffle everything forward.

My first thought was something along this direction:
- Read the date of the next scheduled item (the one that will be overwritten) to X.
- Write today's date to that position.
- Read the name at the next position to T.
- Write the date from X there.
- Read the next value to X.
- Write T to that position.
- Repeat the swaps with X and T until the whole file is updated.

- Then do this two more times, for inserting the name and the schedule type.

The three loops would make this slow, but more importantly it won't work because at the end of the file it will try to read an empty spot before writing, causing the EXA to crash. This could be resolved with an EOF check but in half the cases, T holds the active value to swap so I can't overwrite that with a TEST without making the code much more complex.

Because this would be slow anyway, I think it's better to use the other EXA for juggling data.

It took some fiddling but I came up with this solution:

Code: Select all

;XA

LINK 800
@REP 5
KILL
@END
COPY 200 X
MARK FINDTICKET
ADDI X 1 X
REPL TRYTICKET
JUMP FINDTICKET

MARK TRYTICKET
GRAB X
KILL
COPY F #NEXT
DROP
LINK 800
GRAB 200
COPY M X
SEEK 1

MARK SEARCH
TEST X = F
TJMP FOUND
SEEK 2
JUMP SEARCH

MARK FOUND
COPY F M ; "TEST"
SEEK -3
@REP 3
VOID F
@END

SEEK -9999
MARK FINDDATE
TEST F = #DATE
FJMP DATEFOUND
SEEK 2
JUMP FINDDATE

MARK DATEFOUND
COPY M T
SEEK -1
COPY F M
SEEK -1
COPY #DATE F
COPY F M
SEEK -1
COPY X F
COPY F M
SEEK -1
COPY T F

MARK INSERTLOOP

COPY F X
SEEK -1
COPY M F
COPY X M
TEST EOF
FJMP INSERTLOOP

COPY M F
COPY 0 M
COPY M F
COPY 0 M
COPY M F

;XB

GRAB 300
COPY F M

COPY M X
COPY X M

COPY M X
COPY M T

MARK LOOP
COPY M F
COPY X M
COPY M X
COPY T M
COPY M T
SEEK -1
COPY F M
TJMP LOOP
After cleaning up NthDimension's original entry, XA does a simple loop to find the first instance where the date doesn't match the current date.

Then it first loads the TEST keyword in T, because after this, XB can focus completely on data juggling.

XA sends the date of the other appointment over M, and writes the new date from #DATE. It sends the name of the other appointment over M, then writes NthDimension's name (which is still in X, conveniently). Finally, it sends the other appointment's schedule type over M, and writes the TEST keyword into the file.

Then, XA enters an insert loop in which the value in the file is buffered in X, the previous value is read from M and written to the file, and then X is sent over M. This continues until EOF is reached.

XB stores values in X, T, and F. That fits perfectly: dates in X, names in T and schedule types in F. Then it sends them back in the right order. Some garbage is left in file 300 but since that's in the home host, it doesn't matter.

Once XA reaches EOF it reads the final values from XB and just sends 0 whenever XB asks for a new value. That means a 0 ends up in XB's T, so it leaves the loop and stops automatically.

I did try to make this code faster by not using the X buffer in XA. It might work, but it'd mean juggling a 4th value in XB, or handling only two values at a time. This adds complexity because I'd somehow need to switch from the initial handling of 3 values (for copying NthDimension's entry) to two. It also means you don't know where in the loop the EXA is when it's done copying (because the file has groups of 3 entries). The additional logic makes the code much harder to get right so I decided to stick to this solution.

This code runs at 549/76/8, with top percentiles of 241, 53 and 7.

Image I feel I spent enough time on this, so I'll leave optimizations to you.

Image

Next time, I help out Ghast.


My body was turning into junk
Strange networks I made EXAs spelunk
I heard my system's voice
EMBER gave me no choice
Uploaded, I'm now an EXAPUNK.

User avatar
Part 46 - Cybermyth studios

=== Trash World Inbox ===

Quackles has a nice improvement for last week's task.
Quackles wrote:Optimization time!

Code: Select all

GRAB 300
LINK 800
@REP 5
KILL 
@END
		;LOWEST NUMBER IS 210 
		;NUMBERS ARE IN A CONTIGUOUS BLOCK

		;WE NEED TO GO GRAB _A_ TICKET THEN BACKTRACK UP TO 4 SPACES
		;WE START AT 213 TO IMPROVE PERFORMANCE IN THE WORST CASE

COPY 213 X
MARK TICKETLOOP
REPL GETTICKET
ADDI X 5 X
TEST MRD
FJMP TICKETLOOP
COPY M X
MARK TICKETLOOP2
REPL GETTICKET2
ADDI X 1 X
TEST MRD
FJMP TICKETLOOP2
VOID M

COPY F X
COPY X M
SEEK -1
COPY M F
COPY X F
COPY M F
SEEK -3
COPY F M
COPY F M
COPY F M
WIPE

MARK GETTICKET
GRAB X
SUBI X 4 M
			;HAVE THIS CHAIN TO TICKETLOOP 2??

MARK GETTICKET2
GRAB X
COPY 1 M
COPY F #NEXT
DROP
LINK 800
COPY M X
GRAB 200
					;SO WE HAVE A LITTLE PROBLEM. NO WAY TO GET FILE PTR LOC, SO WHAT IF TARGET LOC IS BEFORE 1ST ENTRY?
					;WE GOTTA CHECK FOR THIS EDGE CASE.
TEST F > #DATE
TJMP EDGECASE1
COPY #DATE M
MARK EDGECASE1RET

MARK FINDAPPT
TEST F = X
TJMP GETAPPT
SEEK 2
JUMP FINDAPPT
MARK GETAPPT
COPY F M 	;'TEST'
MARK COPYFWD
SEEK -6
TEST F = #DATE
TJMP WRITEAPPT
SEEK -1

COPY F X
COPY F T
SEEK 1
COPY X F
COPY T F
SEEK -3
COPY F X
SEEK 2
COPY X F
SEEK -3

JUMP COPYFWD
MARK WRITEAPPT
SEEK 2
COPY M X
TEST X = #DATE
TJMP EDGECASE2RET
						;EDGE CASE PART 2 - COPY NEXT DAY'S DATE SO TODAY'S IS AT LIST START
COPY X F
SEEK -3
COPY F X
COPY F T
SEEK 1
COPY X F
COPY T F

SEEK -9999
MARK EDGECASE2RET
COPY #DATE F
COPY M F 				;NTH'S NAME
COPY M F 				;'TEST'
HALT

MARK EDGECASE1
SEEK -1
COPY F M
SEEK -1
COPY #DATE F
JUMP EDGECASE1RET
I had a similar solution to CO₂'s to begin with, which got similar performance. However, on re-evaluating it, I realized there's an easy way to make it better: since the tickets are a contiguous group of 5, between 210 through 298 in every case, we can just check for a hit by adding 5 if no match is found (so 210, 215, 220, etc). From there, we backtrack four spaces and grab numbers going up by 1 to find the lowest ticket number.

We start by trying to grab ticket 213, as this improves performance in the worst case where tickets 294-298 are present (we grab ticket 298, then subtracting 4 leaves us with the lowest number, ticket 294 directly), and go from there.

My file writing mechanism is drastically different from CO₂'s. I can't say which is faster. Instead of deleting NthDimension's entry from the file, the EXA that finds it sends it over M to its parent, which still remains in the main room with the ticket lineup. The parent EXA stores that row to a file - we'll read it back later.

The EXA reading the appointments starts at Kapoor's row and repeatedly copies the previous line over the current line, going back until we find today's date. There's no way to make the overwriting exa force-halt when it hits the beginning of the file (believe me, I did try), and there's no way to test if we are at the beginning of the file, either (aside from forcing the issue with SEEK -9999). So instead, we check for what in the code above is referred to as the 'edge case'; if Kapoor's entry would be the only one for today (as determined by the first line of the file not having today's date), we replace the date on that entry with today's date, before copying the 'bad date' into the file on Kapoor's row instead.

Either way, once the copier makes space for Kapoor's row, it finds the last incidence of today's date, then replaces the next row with Nth's entry (read in from the parent EXA over M). If the edge case is in force (the date from the parent EXA will not be today's), it copies the first row of the file to the second row and sticks Nth's record in the first row instead.

Once that's done, it's just a matter of cleaning up. 273/93/7.
With such a comprehensive explanation, there's not much I can add. Your file writing code is quite elegant, and because it doesn't use M I wouldn't be surprised if it was faster.


=== Cybermyth studios ===

Image

Time to help out Ghast... Wait, 1992? I was in 1998. Is this supposed to be a flashback or something?

I don't know, so let's just get to the task.

Image
OST: Getting Started

The assignment this time:
- Each host contains two files: a list of accounts and a list of transactions. Although the entries in these files vary, the first value of each entry is a unique identifier that connects an account to one or more transactions.
- Determine the amount of back-pay owed to Ghast and Moss by subtracting the amount that they were paid (file 221) from the amount that they were owed (file 231). Then add their shell company (file 300) to the list of accounts payable (file 220) and issue a single payment to it for the total amount owed (file 221).
- Note that all monetary amounts are represented as two values: dollars, then cents.


That's quite a lot of information. Before I figure that out, let's look in the files. From the dates, it seems I'm definitely in 1992. The files in the RECEIVABLE host list some transactions related to companies. The RECORDS host has no transactions, but the files say that Ghast is on report for absenteeism and I (Moss) am on report for intoxication. Crap.

Image Something I hadn't noticed yet: my home host is named GHAST. Am I actually playing as Ghast? The same thing was the case for the other postgame tasks so far.

The expected goal shows that I actually have to change two things:
- Add the shell company TRASH WORLD CLEANING to file 220, starting with the first unused ID number.
- Add a transaction to file 221, with the ID of the shell company, a date (I guess today's from the #DATE register), and an amount.
That amount is the amount Ghast and Moss are owed, which I can get by subtracting the total paid amount from the total owed amount.

Looking at the data, the total owed amount is often more than the EXA's max value of 9999. At least in the first few tests, the amount paid is not. So, I should be able to solve this problem by writing the amount paid as a negative, then adding the amount owed to it, so I never cross the 9999 boundary.

For the total amount owed, it looks like the payroll has the same amounts for every week, AND everyone gets paid every week. That means I can just calculate the amount for one week and multiply that by the number of weeks.

Enough with the theory, let's write some actual code.

Code: Select all

;XA

LINK 800
LINK 801
; FIND GHAST'S ID
GRAB 220
COPY M X

MARK FINDPERSON
SEEK 1
TEST X = F
FJMP FINDPERSON
SEEK -2
COPY F X
MODE
TEST MRD
TJMP MOSSDONE
REPL GHASTID

MODE

; FIND MOSS TOO
SEEK -9999
COPY M X
JUMP FINDPERSON

MARK MOSSDONE
VOID M


MARK GHASTID
GRAB 221
COPY 0 M

;XB

GRAB 300
COPY F M
COPY F M
XB just grabs the information file and sends data over M.
XA goes to the host containing the paid amounts and finds Ghast's and Moss's account ids. This is done through a basic search loop. But I realized that I'll never have enough registers in one EXA to test if a transaction belongs to either Ghast or Moss in one go. So I REPL the EXA containing Ghast's id (it barely fits in the host when the original EXA is holding a file). It grabs the other file, then sends a message over M in local mode. The TEST MRD in the search loop causes the search for Moss to end without a REPL and in MOSSDONE.

That means the original EXA holding the list of accounts now has Moss's id in X, the other one has Ghast's id.

Image

The EXA containing Ghast's id changes to local mode, creates an EXA I called LOCALCTRL and starts sending Ghast's dollar amounts to it. LOCALCTRL adds them up and stops once it gets a zero. Because the M's interacted badly, I removed the MRD check from below the FINDPERSON, and added a check after the REPL, which tests if XB sent a 0. However, this doesn't get triggered because the EXA waits on the REPL command until a space is freed up. Something to think about later. At least now I have Ghast's amount of paid dollars.

Code: Select all

LINK 800
LINK 801
; FIND GHAST'S ID
GRAB 220
COPY M X

MARK FINDPERSON
SEEK 1
TEST X = F
FJMP FINDPERSON
SEEK -2
COPY F X
COPY X M
REPL GHASTID


; FIND MOSS TOO
SEEK -9999
COPY M X
TEST X = 0
FJMP FINDPERSON

HALT


MARK GHASTID
MODE
GRAB 221
REPL LOCALCTRL

MARK COLLECT_DOLLARS
TEST F = X
FJMP WRONGACCOUNT
SEEK 1
COPY F M
SEEK 1
TEST EOF
FJMP COLLECT_DOLLARS
MARK WRONGACCOUNT
SEEK 3
TEST EOF
FJMP COLLECT_DOLLARS

COPY 0 M
SEEK -9999
COPY 1 M

MARK COLLECT_CENTS
TEST F = X
FJMP WRONGACCOUNT_CT
SEEK 2
COPY F M
TEST EOF
FJMP COLLECT_CENTS
MARK WRONGACCOUNT_CT
SEEK 3
TEST EOF
FJMP COLLECT_CENTS

COPY 0 M

MODE
SEEK -9999
COPY M X ; OTHER ID
MODE
COPY X M
TEST X = 0
FJMP COLLECT_DOLLARS

HALT

MARK LOCALCTRL
MAKE
COPY 0 X
MARK ADDAMOUNT
COPY M T
FJMP ADDAMOUNTDONE
ADDI T X X
JUMP ADDAMOUNT

MARK ADDAMOUNTDONE
COPY X F ; DOLLARS IN F
COPY 0 X
TEST M = 0
FJMP ADDAMOUNT


SEEK -9999
COPY F X
COPY F T
ADDI F X X
ADDI F T T

SWIZ T 0043 F
SWIZ T 0021 T
SEEK -1
ADDI F X X

NOOP
I added several steps. First, after the amount of dollars, I add up the amount of cents too. Moss's id is temporarily sent to XB and then retrieved by the EXA that's looking through the transactions. It then sends Moss's dollar and cent amounts. XB finally sends a 0 which causes both the sender and the LOCALCTRL EXA to jump to a final state. At that point LOCALCTRL is holding a file with respectively, an amount in dollars, cents, dollars and cents. It adds these up, making sure to add cent amounts > 100 to the dollar value. It ends with total dollars in X and total cents in T.


Okay, with that done I need to find out how much they are due.

I can do a bit of cleanup first. That original EXA is still sitting at REPL GHASTID. Instead of letting it continue, I can have the other one just do a KILL. That means that I can remove the TEST and HALT after it. There's also a place where I had to do a TEST X = 0 and if that's true it can HALT. It saves some space to just DIVI T X T and use the divide by zero trick.

Dealing with the payroll is a bit tricky, because the accounts there have other IDs. I need to get Ghast's and Moss's name from file 300 once more.
You know what, doing that with M is going to cause timing issues. I'll just have another EXA handle that altogether.


Enter the extended XB. I'll go through it bit by bit.

Code: Select all

;XB

GRAB 300
COPY F X
COPY F T
REPL PAYROLL

COPY X M
VOID M
COPY T M
COPY M X
COPY X M
COPY 0 M

MODE
COPY 0 M

HALT

MARK PAYROLL
MODE
LINK 800
LINK 802
After grabbing the file, it copies Ghast's and Moss's name to X and T. The 'main' XB still sends these values over global M to XA. XA's FINDPERSON code stores the result in X and sends it to M. The first time round, XB VOIDs the M. The second time round, because XA can never REPL, the id on M is necessary. XB buffers it in X, then forwards it to XA's existing REPL when it is ready to receive. Finally, when XA asks for a 'third name', XB send COPY 0 M. The 0 tells XA to continue with the next part (the calculation).

The MODE change followed by a final M message is for the PAYROLL REPL. Once PAYROLL is done, it'll come back to the home host and wait for this M, which indicates that XA is ready. After that, the original XB can HALT and the PAYROLL can take over. By the way, by reordering the code a bit I can get rid of this HALT by having the EXA fall through into harmless code where it dies.


Now, for the PAYROLL itself. It switches to MODE to LOCAL and will stay there. This way it can never interfere with the XB-XA GLOBAL communication.

Code: Select all

MARK FINDPERSON
SEEK 1
TEST X = F
FJMP FINDPERSON
SEEK -2
COPY F M

; FIND MOSS TOO
SEEK -9999
COPY M X
DIVI 1 M T
JUMP FINDPERSON
Since the IDs in the payroll host differ, XB has a copy of the FINDPERSON code. This time, it just sends the result on local M. It then waits for a message. If this is a 0, it dies. If not, it waits for another message from M into X (Moss's name). We need to listen to M twice because attempting to do arithmetic on a text string crashes the EXA.

Code: Select all

MARK OTHER
GRAB 231
COPY M X
COPY 1 M
COPY T M
REPL CTRL

COPY 8 T
MARK WT
SUBI T 1 T
TJMP WT

MARK FIND
TEST F = X
SEEK 3
FJMP FIND
SEEK -2
COPY F M
COPY F M

COPY M X
COPY X T
TJMP FIND
The OTHER REPL GRABs the file with the amounts owed. Then it copies the ID found by FINDPERSON to X. It sends 1 to M to tell FINDPERSON to look for Moss, followed by Moss's name which is still sitting in T.

After that it needs to wait a bunch of cycles. That's because before the FIND loop can start looking for Ghast's salary, the CTRL EXA first needs to get Moss's ID. I'll get to that EXA in a second.

The FIND loop itself simply looks for Ghast's ID and then copies both the dollars and cents to M. If it gets Moss's ID (which is a non-zero number) it'll look for his amount too, otherwise this EXA will continue to another part of the code.

Before we go to that next part I'll show the CTRL REPL.

Code: Select all

MARK CTRL
COPY M X
COPY 0 M
MAKE
COPY M F
COPY M F
COPY X M
COPY M F
COPY M F
COPY 0 M
SEEK -9999
COPY F X
COPY F T
ADDI F X X
ADDI F T T

SWIZ T 0043 F
SWIZ T 0021 T
SEEK -1
ADDI F X X

WIPE
MAKE
COPY X F
COPY T F
First, it waits for the second ID from FINDPERSON. It sends a 0 to let FINDPERSON know it can stop looking. Then it makes a temporary file, in which it stores Ghast's salary (dollars, then cents). This EXA sends Moss's ID to the FIND EXA and then stores Moss's salary too. It sends a 0 to tell the FIND EXA it's done. It adds the two salaries together, and then does the same trick as XA, with the SWIZ to add any hundreds of cents to the dollar amount.

Finally, the temporary file is WIPEd and a new one is made to store the total salary in. Having a file without intermediate garbage makes things simpler later.

Code: Select all

LINK -1
DROP
LINK 802

GRAB 230
COPY 0 X
MARK COUNT
ADDI X 1 X
SEEK 2
TEST EOF
FJMP COUNT
MULI X 4 M
DROP
COPY 0 X
LINK -1
GRAB 402
LINK 802
The EXA then parks the temporary file in the gateway host (since the payroll host is full), and goes back to GRAB 230 again. This time, it counts the number of people on the payroll. This is multiplied by 4 (because there are 4 items per person per week in file 231) and sent to M, where the FIND EXA which is still holding 231 can read it. Once that's done, it quickly goes back to grab the temporary file (402) again.

Before we continue, let's look at what the FIND EXA has been up to.

Code: Select all

; COUNT
SEEK -9999
COPY M X

MARK COUNTPAYMENTS
COPY 1 M
SEEK X
TEST EOF
FJMP COUNTPAYMENTS

COPY 0 M
HALT
This EXA receives the multiplied value and saves it to X. For example, if there are 5 people on the payroll, it'll save '20' to X. That way, by doing a SEEK X it jumps ahead exactly one week. So, what this does is count how many times everyone (including Ghast and Moss) were supposed to be paid.

Since X holds the value to SEEK and T is used for EOF, the actual counting is done by COPY 1 M in the loop, followed by a COPY 0 M to let the CTRL EXA know when it's done. The HALT here is optional if I structure the code so this EXA dies without further side effects after the COPY 0 M.

Code: Select all

MARK MORE
COPY M T
ADDI X T X
TJMP MORE
LINK -1
LINK -1
VOID M
MODE

COPY X M
COPY F M
COPY X M
COPY F M
WIPE
GRAB 300
SEEK 2
COPY F M
This is the final part of the CTRL EXA and XB's code as a whole. This EXA is back in the payroll host. In the MORE loop it keeps track in X of the number of payments, and uses T to check when it's done. Then it LINKs back home, and then waits until the original XB sends that one LOCAL message to say XA is done.

At that point this EXA truly takes control by switching to GLOBAL mode, sending the number of payments, then the total amount of dollars, then the number of payments again and then the amount of cents.

If it's not quite clear what I've just done, here's an example:
- Moss gets paid $1076.92 per week.
- Ghast gets paid $961.53 per week.
- In total, they get paid 1076.92 + 961.53 = $2038.45 per week.
- The payroll says they owe payment for 5 weeks.

In this case, the CTRL EXA sends: 5; 2038; 5; 45. That way, the receiving EXA knows to apply $2038 five times, and the cents amount too. Since 5 * 2038 is greater than 9999, this is the most straightforward way to handle it.

Finally, this EXA GRABs file 300 so it can send the shell company's name before it stops.


To handle all this, I added more code to XA.

If you remember, we left off XA with the dollars-already-paid amount in X, and the cents-already-paid in T. It's also holding a temporary file containing intermediate date, with the file pointer sitting at the end.

Code: Select all

;XA continued

KILL
SUBI 0 T F
SUBI 0 X X

MODE
COPY M T
COPY M F


MARK AGAIN
SEEK -1
ADDI F X X
SUBI T 1 T
TJMP AGAIN

COPY X F
At this point, we're certain that the only other EXA in this host is the one stuck trying to REPL. I can safely kill it. Then, we store the negative of the cents amount in F and the negative of the dollars amount in X. The EXA switches to GLOBAL MODE and puts the amount of payments in T and the amount of dollars per week in F.

The AGAIN loop adds the amount owed to the negative of the amount paid repeatedly. For example, if the amount paid is $100 and the amount owed is 5 payments of $60, the calculation is -100 + 60 + 60 ... and so on. The result, which is the actual final amount Ghast and Moss are owed, is stored in F.

Code: Select all

SEEK -3
COPY F X
COPY M T
SEEK 9999
COPY M F

MARK AGAIN2
SEEK -1
ADDI F X X
SUBI T 1 T
TJMP AGAIN2

SEEK -2
SWIZ X 0043 T
ADDI F T T
SWIZ X 0021 X
Next, we repeat the exact same trick but with the cents. Again, I have to do the SWIZ to handle hundreds of cents.

While testing I found out this bit of code failed in one particular case. Because I'm subtracting the cents separately from the dollars, it's possible to end up with a negative amount of cents. For instance, 56 dollars and -29 cents. That's actually supposed to be $55.81. There might be a way around this by doing something smart with the SWIZ code but I just slapped a workaround after it.

Code: Select all

; NEGATIVE CENTS
COPY T F
TEST X < 0
FJMP SKIP
ADDI 100 X X
SEEK -1
SUBI F 1 T
JUMP COMPLETE

MARK SKIP
SEEK -1
COPY F T

MARK COMPLETE
Temporarily store the dollar amount (which was in T) to F, so I can do a test. Then, if the cent amount is negative, add 100 to it, and subtract 1 from the dollar amount. The dollar amount is put back into T.

Now, all that's really left is clean-up and writing the final values to the file.

Code: Select all

MARK COMPLETE
WIPE
GRAB 220
SEEK 9999
SEEK -2
REPL DATE
ADDI F 1 X
SEEK 9999
COPY X F
COPY M F
The EXA now WIPEs its temporary file and GRABs 220 again. First, it finds the highest ID in the file and adds one to it. This value is saved to the end of the file, followed by the shell company's name, which we get on M from XB.

Code: Select all

DROP
GRAB 221
SEEK 9999
COPY X F
COPY M F
COPY T F
COPY M F
Then, it grabs file 221, and adds a new transaction, starting with the shell company's ID number, and then the DATE which it gets from the DATE REPL. It then copies the dollars amount from T, and it gets the cents amount from DATE as well, since it was overwritten in X by the ID.

Code: Select all

MARK DATE
LINK -1
LINK 804
NOOP
COPY #DATE M
COPY X M
The REPL itself doesn't have much code. It goes grab the current date from the hardware register, sends that, and then the cents.

The NOOP here is absolutely necessary. Without it, it would start sending just before the other EXA had the chance to read the shell company's name from M. If I had put the REPL DATE instruction any earlier I would've needed even more waiting cycles here. And I couldn't put it any later, because after that point, X gets overwritten.


That's every single piece of the code explained. While testing, I ran into one more bug. It is possible for the amount of cents in a payment to be 0. And I was already used 0 to let the other EXA know no more payments are coming. I fixed that by sending the amount + 1 and then subtracting 1 once the other EXA receives it. I had to do this for dollars as well because they share the receive code.

The full code is... hard to read because I shuffled blocks of code to remove a bunch of HALT instructions. It's here in case anyone wants to optimize it.

Image

Image Oh. It's way too long.

Well, the good news is that even though I don't get a high score, the game still counts this task as completed. I'm glad, because I spent enough time on it that I really don't want to optimize it further.

I can see my score by looking at the detailed test run data. It's 536/246/16.

Outside the level, the histograms show up.

Image
The top percentiles are 215, 92, and 4, but for size there's a tiny little peak in the histogram around 60. I can think of some optimizations but getting it down to 60-ish? I have no idea how. My speed though is close to tenth percentile, and I'm happy with that.

Image

Next time, hydroponix.

User avatar
Part 47 - US Department of Defense

Today, we have a very special guest. But first, let's check the inbox.


=== Trash World Inbox ===

Last time, I finished Cybermyth Studios with a score of 536/246/16. The max allowed number of lines for the leaderboard was 150, so it didn't really count.

Quackles shows some better ways to tackle it.
Quackles wrote:I... owe CO2 an apology.

My fully-optimized design uses a completely different approach than his, and it almost feels like I'm bragging. If so, I'm sorry.

But anyway, here's what I got.

Code: Select all

GRAB 300
COPY F X
REPL COMPTROLLER
SEEK -1
COPY M F ;CENTS 1
COPY F X
REPL COMPTROLLER
SEEK -1
COPY M F ;DOLLARS 1
SEEK -2
ADDI M F X ;CENTS 2
;WE AVOID HAVING TO TEST
;FOR NEGATIVE CENTS
;BY ADDING 300 TO CENTS
;AND SUBTRACTING 3 FROM 
;DOLLARS (LATER)
ADDI X 300 X
DIVI X 100 T
SEEK -1
MODI X 100 F
ADDI M T T
ADDI T F T
;X HAS CENTS,
;T HAS DOLLARS

;NOW JUST WRITE THE
;VALUES AND WE'RE GOOD
;TO GO
LINK 800
MODE
COPY F X
REPL HACKTHEPAYABLE
LINK 804
SEEK -9999
COPY F X
WIPE
COPY #DATE M
COPY T M
COPY X M


MARK HACKTHEPAYABLE
LINK 801
GRAB 220
SEEK 9999
SEEK -2
COPY F T
SEEK 1
ADDI T 1 F
COPY X F
DROP
GRAB 221
SEEK 9999
ADDI T 1 F
COPY M F
SUBI M 3 F
COPY M F


MARK COMPTROLLER
LINK 800
REPL GETFIN
COPY 1 T
REPL GETFIN

;PAYMENTS ALWAYS
;FINISHES FIRST!

SUBI 0 M X ;DOLLARS1
SUBI 0 M T ;CENTS1
ADDI X M X ;2
ADDI T M T ;2
LINK -1
COPY T M ;CENTS
COPY X M ;DOLLARS


MARK GETFIN
ADDI T 801 T
LINK T
SWIZ T 0010 T
ADDI T 210 T
GRAB T
SEEK 1
MARK SEARCHFIN
TEST F = X
TJMP FOUNDFIN
SEEK 1
JUMP SEARCHFIN

MARK FOUNDFIN
SEEK -2
COPY F X
FILE T
ADDI T 1 T
DROP
GRAB T
REPL COUNTER
MARK LISTFIN
TEST EOF 
TJMP DONEZO
TEST F = X
SEEK 3
FJMP LISTFIN
SEEK -2
COPY F M
COPY F M
JUMP LISTFIN


MARK COUNTER
MAKE
COPY 0 F
COPY 0 F
MARK COUNTER2
COPY M T
FJMP FINISHEDCOUNT
SEEK -2
ADDI F T T
ADDI F M X
SEEK -2
COPY T F
COPY X F
JUMP COUNTER2

MARK FINISHEDCOUNT
LINK -1
SEEK -2
COPY F M
COPY F M
WIPE
HALT

MARK DONEZO
COPY 0 M
;GIVE UP
This is a big block of code, but it's really in five main 'chunks': the main EXA, the main EXA's file writing routine ("HACKTHEPAYABLE"), the EXA that gets the amounts paid and owed for a single person ("COMPTROLLER"), and the subsidiary processes spun off by the controller ("GETFIN") and ("COUNTER").

It works as follows:

First, the main EXA moves 'Ghast' into X, then REPLs to COMPTROLLER. The 'comptroller' EXA will search for the accounts receivable and payable, return to our host, and communicate them to the main EXA over M, in the order (Cents, Dollars). We use this order to let us use the file for extra storage later.

The comptroller EXA heads into the Cybermyth host, then REPLs twice to GETFIN. Some numeric wrangling with T lets us use GETFIN for both the payroll and payable hosts, despite having different link and file IDs. Each GETFIN EXA still has 'Ghast' in its X, so it starts by grabbing the IDs file, searching for 'Ghast', and replacing its X with the numeric ID, instead. Then, it drops the IDs file, grabs the accounts file (using FILE and some addition to get the next file ID), and REPLs off into COUNTER. There's enough space in the host since we're grabbing a file.

From here, the GETFIN EXA simply scans through the file and sends any dollar/cents values it finds with a matching ID out over M, then 0 once it's done. It then halts. COUNTER adds up the dollars and cents values it gets to a file; once it receives a 0 for a dollar value, it leaves the subhost and reports to the comptroller.

The payments file always takes less time to process than the payroll file! Cybermyth, you cheapskates. Because of this, we can assume that the first COUNTER EXA has the amount paid, while the second has the amount owed. We get the values over M, subtract the first EXA's values from our registers, and add the second EXA's values. Then we report back to the main EXA.

The main EXA stores the values to the file in place of 'Ghast' and 'Moss'. It repeats the comptroller process with 'Moss', and adds the second comptroller's values.

We have a little bit of rectification to do at this point. It's possible we could have a value for the 'cents' that is over 100, or even negative! To fix these, we add 300 to the cents value to counter out any negative amount. Then we add the modulo 100 of the cents to the dollars value, and leave the rest of the cents as is. (We'll subtract 3 from the dollars value later.)

At this point, the final part of the code kicks in.

The main EXA splits into two. One ('HACKTHEPAYABLE') moves into the payments subhost and patches the accounts file (the main EXA moves into the host with #DATE and helpfully sends 'TRASH WORLD CLEANING' over global M). Then, with the new ID, it moves to the bottom of the payments file, takes the dollars value (minus $3) and cents value from the main EXA over M, and writes to the file. Finished!


458/109/15. I'm pretty sure handling Ghast's and Moss's data separately drastically decomplicates things. It might also be possible to speed this up further by having separate routines for the payable and payroll sections, since the payroll section follows a pattern (so just find the first payment, then find the number of payments by jumping forward 15 places in the file and checking for EOF, then perform multiplication). I might try making that variant when I have some spare time.
First of all, this only works if you start the EXA in LOCAL mode. Secondly, very nice optimization. I guess I was overthinking things a bit. Also I have no idea why you'd need to apologize, I always welcome cool improvements.

Next Quackles implemented the faster variant.
Quackles wrote:

Code: Select all

GRAB 300
COPY F X
REPL COMPTROLLER
SEEK -1
COPY M F ;CENTS 1
COPY F X
REPL COMPTROLLER
SEEK -1
COPY M F ;DOLLARS 1
SEEK -2
ADDI M F X ;CENTS 2
	;WE AVOID HAVING TO TEST FOR NEGATIVE CENTS
	;BY ADDING 300 TO CENTS AND SUBTRACTING 3 FROM DOLLARS (LATER)
ADDI X 300 X
DIVI X 100 T
SEEK -1
MODI X 100 F
ADDI M T T
ADDI T F T
	;X HAS CENTS,
	;T HAS DOLLARS

	;NOW JUST WRITE THE
	;VALUES AND WE'RE GOOD TO GO
LINK 800
MODE
COPY F X
REPL HACKTHEPAYABLE
LINK 804
SEEK -9999
COPY F X
WIPE
COPY #DATE M
COPY T M
COPY X M


MARK HACKTHEPAYABLE
LINK 801
GRAB 220
SEEK 9999
SEEK -2
COPY F T
SEEK 1
ADDI T 1 F
COPY X F
DROP
GRAB 221
SEEK 9999
ADDI T 1 F
COPY M F
SUBI M 3 F
COPY M F


MARK COMPTROLLER
LINK 800
REPL GETDUE
REPL GETFIN

ADDI 0 M X ;DOLLARS1
MODE ;SYNC GUARDS
ADDI 0 M T ;CENTS1
MODE

ADDI X M X ;2
MODE
ADDI T M T ;2
MODE
LINK -1

COPY T M ;CENTS
COPY X M ;DOLLARS


MARK GETDUE
LINK 802
GRAB 230
SEEK 1
MARK SEARCHDUE
TEST F = X
TJMP FOUNDDUE
SEEK 1
JUMP SEARCHDUE

MARK FOUNDDUE
SEEK -2
COPY F X
DROP
GRAB 231
REPL COUNTER

MARK LISTDUE
TEST F = X
SEEK 3
FJMP LISTDUE
SEEK -2
COPY F M
COPY F M
COPY 1 X
SEEK 16
MARK DUECOUNT
TEST EOF
ADDI X 1 X
SEEK 20
FJMP DUECOUNT
COPY 0 M
SUBI X 1 M


MARK GETFIN
LINK 801
GRAB 220
SEEK 1
MARK SEARCHFIN
TEST F = X
TJMP FOUNDFIN
SEEK 1
JUMP SEARCHFIN

MARK FOUNDFIN
SEEK -2
COPY F X
DROP
GRAB 221
REPL COUNTER
MARK LISTFIN
TEST EOF 
TJMP DONEZO
TEST F = X
SEEK 3
FJMP LISTFIN
SEEK -2
COPY F M
COPY F M
JUMP LISTFIN


MARK DONEZO 
COPY 0 M
COPY -1 M
;GIVE UP

MARK COUNTER
MAKE
COPY 0 F
COPY 0 F
MARK COUNTER2
COPY M T
FJMP FINISHEDCOUNT
SEEK -2
ADDI F T T
ADDI F M X
SEEK -2
COPY T F
COPY X F
JUMP COUNTER2

MARK FINISHEDCOUNT
COPY M X
LINK -1
SEEK -2
MULI F X M
MODE ;SYNC GUARD
MULI F X M
WIPE
This code is similar to the last one, except the code that scans the payroll and payable has been split off into separate blocks. The payable code ('GETFIN') is more or less as-is compared to the previous version, but the payroll code ('GETDUE') finds the first instance of the amount paid in the list, then jumps forward 20 entries to check if there was another payment scheduled. The payroll EXA then repeats this to count up the number of expected payments, then sends a zero, and that number, to the counter EXA.

Both the GETFIN and GETDUE EXAs use the same counter code, which has been modified slightly. Now, after receiving the 'finish up' zero, the counter EXA will read one more value over M, and multiply the totals by that value. This lets us not have to worry about the order the EXAs return when reporting our results to the comptroller. We can simply add the values together, as the amount CyberMyth paid us can be multiplied by -1 to mimic a subtraction.

The comptroller and counter do use 'mode guards' to handle the edge case where both counters return to the main CyberMyth host at about the same time. Once the first value is read from a counter, the second value will be read using the global mode instead of the local mode. This prevents the second counter from kramering in and getting the order of the values mixed up.

Aside from that, everything is pretty much the same.


354/140/15. It can theoretically get better cycles-wise, but this is as good as it gets for me.
Clear explanation, I have nothing to add.


=== US Department of Defense ===

Image

Alright, is everyone ready to go hack the Department of Defense?

Image
OST: Leave No Trace

The assignment:
- Find the unredacted version of the PROJECT OGRE report (file 300), make a copy of it, and bring the copy back to your host. The target file will be behind one or more locks, which each require a three-digit code.
- Since this task takes place inside a network run by the military it includes additional security features not found in other networks. You may not have more than one EXA in the network at a time, and you may not use the M register to communicate between an EXA in the network and an EXA in your host.


Camouflage colours or not, I found your network. And finally some real security. Honestly, all networks so far were wide open.

Anyway, I did some experiments to see what that second point of the assignment is about. M communication between the home host and the remote network is simply blocked, as if the networks are disconnected. If you bring in two EXAs to the military network, that grid of squares in the background starts flashing red and the "Leave no trace" goal is immediately failed. The same thing happens if you mess with the hardware registers on the helipad.

Those three files that are immediately accessible are heavily censored, so I'll need to crack that #LOCK.

The assignment says nothing about how to figure out the lock's combination, so let's just bruteforce it by trying all possible combinations.

Code: Select all

LINK 800

COPY 1000 T

MARK LOOP
SUBI T 1 T
COPY T #LOCK
TJMP LOOP

NOOP
Hmm, at some point it opens the link to that secured host but as soon as my EXA tries another code it closes again. Do I have to check whether the link is open after every try? But I can't use REPLs because that would mean 2 EXAs in the military network. I could spawn 1000 EXAs from some main EXA in my home host but that would be very slow and give a very high activity.

I think I will need a little help with this one...

----

This is the LockPickingLawyer and what I have for you today is a US Department of Defense Digital Combination Lock, model 1998-D. This lock was sent to me by my friend hydroponix. It is supposedly used by the Department of Defense to secure their digital information, and they consider it military grade.

There is a glaring weakness in this digital lock, though, and I will demonstrate it to you. Let me zoom in a little bit so you can see what is going on.

Image

The combination for this lock has a six in the third position. I just tried to open it with 996 and you can see the correct digit sticks. This means we can open the lock with only a short loop.

Image

I simply move all three digits to 999, save any correct digits to the EXA's X register, then repeat for 888 and so on. The code for this lock is 436, and I found this in only 39 cycles.

And there we have it. For any type of lock, there's a tool to pick it. In this case it's an EXA. The mechanism of this lock is similar to that of the Master Lock 878 Combination Lock, and just like that one, this digital lock can be decoded both easily and rapidly. In any case, that's all I have for you today. If you have any questions or comments about this, please put them below. If you liked this explanation and would like to see more like it, please subscribe to my channel, and as always, have a nice day. Thank you.


Image I hope the real LPL doesn't mind me doing this. After all, imitation is the sincerest form of flattery. Go check out his channel, it's interesting.

----

Thank you so much, LockPickingLawyer. Opening the link to the next host reveals a bunch more files, and another #LOCK to get access to the innermost files. I'll quickly open that as well by repeating the lock picking loop (or LPL for short) in that host.

Code: Select all

LINK 800

COPY 999 T

MARK LPL
COPY T #LOCK
ADDI X #LOCK X
SUBI T 111 T
TJMP LPL

COPY X #LOCK

LINK 800
COPY 0 X

COPY 999 T

MARK LPL2
COPY T #LOCK
ADDI X #LOCK X
SUBI T 111 T
TJMP LPL2

COPY X #LOCK
That gives me access to the full network.

Image

The photo archive in the corner now shows... them filming something in the desert? Looks like an astronaut and someone dressed in a cheap alien costume.

The files here have some very interesting information. There's a few different files in the different test cases, so let me transcribe all of them for you.

The first three are uncensored versions of the censored files from the start.

Project Ogre
(1945-1953) Obtain special footage for the director depicting a hypothetical scenario involving a technologically advanced craft of non-earth origin with the purpose of scenario planning should such a situation occur. Footage was deemed unrealistic and not used. Result: FAILURE

Project Ptero
(1981-1990) Infer and test experimental new wing designs based on information from recovered DNA samples. Resulting wing shapes were not conducive to maintaining altitude. Result: FAILURE

Project Quir
(1960-1968) Investigate gravity shielding effects produced by condensed matter and superconducting materials. Analysis of tidal forces detected reduction of gravitational force of close to 1%. Future projects to focus on increasing power. Result: SUCCESS.


The rest of the files describe some other projects.

Project Orbus
(1994-) Develop additional capability and platforms for directed-energy weapons using chemically-based phase-conjugate mirrors. In all cases the mirrors overheated and experienced unplanned combustion. Result: FAILURE


Image I wonder if this is a kind of a vague reference to the boss missions in the Zachtronics game SpaceChem.

Project Ember
(1994-) Create general artificial intelligence using a baseline emulated reasoning approach. Of 4 initial seed programs, 1 developed knowledge acquisition, language comprehension, and exhibited signs of metacognition. Result: SUCCESS


Image Oh, hey. Ember was created by the military?

Project Virgil
(1983-1987) Explore use of chimpanzees as pilots for small reconnaissance aircraft to reduce radar cross-section and human casualties. Of 9 tests, 7 completed take-off but 0 returned to base. Result: FAILURE

Project Icarus
(1972-1978) Test advanced new methods to synthesize wing and fuselage material with carbon nanofibers to enable greater speed and maneuverability. Of the 21 tested methods, 20 created unusable materials and 1 resulted in a small incident. Result: FAILURE

Project Phosphor
(1979-1987) Project to develop and test technology related to inertial guidance, warhead separation, and ablative heat shielding of re-entry vehicles. Of 18 launches, 9 reached space and 3 returned to earth intact. Result: SUCCESS



That's all of them. But I need a copy of Project Ogre for myself. Let's find it first.

Code: Select all

GRAB 300
LINK 800

COPY 999 T

MARK LPL
COPY T #LOCK
ADDI X #LOCK X
SUBI T 111 T
TJMP LPL

COPY X #LOCK

LINK 800
COPY 0 X

COPY 999 T

MARK LPL2
COPY T #LOCK
ADDI X #LOCK X
SUBI T 111 T
TJMP LPL2

COPY X #LOCK

COPY F X
WIPE

; FIND

@REP 4
LINK 80@{1,1}
GRAB 200
TEST F = X
TJMP FOUND
DROP
LINK -1

@END

LINK 800
LINK 801
GRAB 200
TEST F = X
TJMP FOUND
DROP
LINK -1
LINK 802
GRAB 200

MARK FOUND
The EXA first grabs the encrypted version of Project Ogre from my home host, so it can extract the name later. Then it uses the lock picking loop (LPL) twice to pick both locks. After that, it doesn't need X anymore to hold lock information so it can take the project name from the file and put that in X.

Since the encrypted Project Ogre file started in my home host, I can safely WIPE without failing the "Leave no trace" goal. Then, the @REP makes the EXA go to each side host (801 to 804), check the file, and if it's wrong, continue to the next one. After @END, it continues to the deepest part of the network and searches for the file there as well. There's no test for the very last host, because if it's none of the others, that has to be the one.

I could maybe use a loop or two here, but I'm using my registers already. Maybe an optimization for later.

Now that the EXA has the file in hand... I have to copy it without M and without a second EXA. The files all have the little lock icon meaning I cannot move them either.

There's another issue: if I create another file in one of the side hosts, I don't have space to drop it and swap between the files. My copy has to be created in a central host, so my EXA has to link back and forth.

That means I need to either remember or bruteforce the following stuff:
- How far I am into copying the file.
- What host the correct file is in so I can link back to it to get more data.
- And of course the data to copy itself.


I started by expanding my file finding logic to save the LINK value to T. I need to do it there, because once I'm in the FOUND mark I don't know where I came from and can't save it anymore.

Code: Select all

; FIND

@REP 4
LINK 80@{1,1}
GRAB 200
TEST F = X
FJMP SKIP@{1,1}
COPY 80@{1,1} T
JUMP FOUND
MARK SKIP@{1,1}
DROP
LINK -1

@END

LINK 800
LINK 801
GRAB 200
TEST F = X
FJMP SKIP
COPY 801 T
JUMP FOUND
MARK SKIP
DROP
LINK -1
LINK 802
COPY 802 T
GRAB 200

MARK FOUND
Next, the actual copy logic.

Code: Select all

MARK FOUND
DROP
LINK -1

MAKE
COPY T F
COPY 0 X

MARK COPY
DROP
LINK T

GRAB 200
SEEK X
COPY F X
COPY F T
DROP

LINK -1
GRAB 400
SEEK 9999
COPY X F
COPY T F

SEEK -9999
COPY -1 X

MARK COUNT
SEEK 1
ADDI X 1 X
TEST EOF
FJMP COUNT

SEEK -9999
COPY F T
JUMP COPY
The first thing I do after finding the file is jump back and create a target file to copy the data to (file 400). I store the LINK ID in there to free up the registers. In the MARK COPY loop, I go to the source file, read two entries to X and T, drop it, go back to the target file, and write them there. Then I count the length of the file (minus one for the link ID sitting at the start) and put that in X so I can SEEK to that position in the source file. That count takes time, but it's a simple solution.

I read the link ID from the file, and go back to copy the next two values.

Once the whole file is copied, the EXA will crash because it'll try to read past the end of the file. This doesn't have to be a problem, I could have another EXA that waits at home for all this time using a simple wait loop and comes fetch this file when it's done. That's a bit complicated though, because the file can be both in the middle host and the far host, and if it can't find the file it will crash, so I would have to send two separate EXAs and... no thanks.

Let's instead just write a way for this EXA to know when it's done so it doesn't crash.

This turns out to be very easy. The file always has 44 words, so all I have to do is hardcode that in a TEST. The end becomes:

Code: Select all

MARK COUNT
SEEK 1
ADDI X 1 X
TEST EOF
FJMP COUNT

SEEK -9999
TEST X = 44
TJMP DONE
COPY F T
JUMP COPY

MARK DONE
VOID F
LINK -1
LINK -1
LINK -1
If the EXA copied 44 entries, void the value holding the LINK ID. Handling the fact that the EXA might be in different hosts is also easy, just add one more LINK -1 and if it's closer to home that will crash the EXA in the home host, make it drop the file there.

Image

Image Large variety in the scores here. My code runs at 2654/105/62. The top percentiles are 531, 60 and 46.

Let's look at some improvements. Before I start, here's all the code so far.

Code: Select all

GRAB 300
LINK 800

COPY 999 T

MARK LPL
COPY T #LOCK
ADDI X #LOCK X
SUBI T 111 T
TJMP LPL

COPY X #LOCK

LINK 800
COPY 0 X

COPY 999 T

MARK LPL2
COPY T #LOCK
ADDI X #LOCK X
SUBI T 111 T
TJMP LPL2

COPY X #LOCK

COPY F X
WIPE

; FIND

@REP 4
LINK 80@{1,1}
GRAB 200
TEST F = X
FJMP SKIP@{1,1}
COPY 80@{1,1} T
JUMP FOUND
MARK SKIP@{1,1}
DROP
LINK -1

@END

LINK 800
LINK 801
GRAB 200
TEST F = X
FJMP SKIP
COPY 801 T
JUMP FOUND
MARK SKIP
DROP
LINK -1
LINK 802
COPY 802 T
GRAB 200

MARK FOUND
DROP
LINK -1

MAKE
COPY T F
COPY 0 X

MARK COPY
DROP
LINK T

GRAB 200
SEEK X
COPY F X
COPY F T
DROP

LINK -1
GRAB 400
SEEK 9999
COPY X F
COPY T F

SEEK -9999
COPY -1 X

MARK COUNT
SEEK 1
ADDI X 1 X
TEST EOF
FJMP COUNT

SEEK -9999
TEST X = 44
TJMP DONE
COPY F T
JUMP COPY

MARK DONE
VOID F
LINK -1
LINK -1
LINK -1
The first thing I can change is removing the COUNT loop. If I just keep a counter and increase it by 2 every time I copy two values, that's much faster. I didn't do that right away because I didn't have any registers free... but I can just use another value at the start of the file instead.

Code: Select all

;XA CONT'd

MAKE
COPY 0 X
COPY 0 F
COPY T F

MARK COPY
DROP
LINK T

GRAB 200
SEEK X
COPY F X
COPY F T
DROP

LINK -1
GRAB 400
SEEK 9999
COPY X F
COPY T F

SEEK -9999
ADDI F 2 X
SEEK -1
TEST X = 44
TJMP DONE
COPY X F
COPY F T
JUMP COPY

MARK DONE
VOID F
VOID F
LINK -1
LINK -1
LINK -1
565/103/62, much better speed.


To reduce the size, I can roll up the file search loops. But how? I already need X and T and I can't use M. The answer is to prepare a file that can handle the loops.

After the LPL:

Code: Select all

COPY F X
WIPE

MAKE
COPY 804 F
COPY 803 F
MARK NEXT
COPY 802 F
COPY 801 F
SEEK -9999

MARK SEARCH
COPY F T
DROP

LINK T
GRAB 200
TEST F = X
TJMP FOUND
DROP
LINK -1
GRAB 400
VOID F
TEST EOF
FJMP SEARCH

LINK 800
JUMP NEXT
The loop works by VOIDing a value from the file once I've tried it. I don't need the weird workaround to find the host ID anymore inside the loop. That'll just be the first value of file 400. So the MARK FOUND now GRABs that file, copies that value to T, WIPEs it and creates a fresh file to copy the data to. 611/79/62.

If you move the LPL loops into a separate EXA you can combine the two loops into one, the EXA will just die when it tries to write to a non-existent third #LOCK. However, that means the copying EXA has to wait at home until the LPL EXA is done, and the extra lines for the wait loop are the same amount as the lines saved by doing this, so the score is the same.


I have one more idea for a speed optimization.

Copying is slow, with only one EXA and while having to LINK between hosts. But I don't need to copy everything, significant chunks of the file are already in the censored copy. Let's make use of that.

Image

After OBTAIN, the word SPECIAL is missing. After DEPICTING, things get more complicated. Between DEPICTING and TECHNOLOGICALLY, there are three censored keywords in this file, but 5 words in the original file. And it doesn't get any better after that. Dealing with these different file lengths is very complicated and it makes you run out of space for lines of code very fast.

But we can just take that first chunk before the lengths start to differ.

In this optimization, XA's only purpose is the LPL.

Code: Select all

;XA

LINK 800

COPY 999 T

MARK LPL
COPY T #LOCK
ADDI X #LOCK X
SUBI T 111 T
TJMP LPL

COPY X #LOCK

LINK 800
COPY 0 X

COPY 999 T

MARK LPL2
COPY T #LOCK
ADDI X #LOCK X
SUBI T 111 T
TJMP LPL2

COPY X #LOCK
Meanwhile, in the safety of my home host, XB grabs the file and copies only the first 10 values (until DEPICTING) using M.

Code: Select all

;XB

GRAB 300
COPY 10 T
MARK LP
SUBI T 1 T
COPY F M
TJMP LP
XC starts with putting a couple zeroes in an empty file (for the counter and the LINK ID later on), then copies the data from M.

Code: Select all

;XC

MAKE
COPY 0 F
COPY 0 F

COPY 10 T

MARK LP
COPY M F
SUBI T 1 T
TJMP LP

SEEK -9999
SEEK 2
COPY F X

COPY 16 T

MARK WAIT
SUBI T 1 T
TJMP WAIT

LINK 800
LINK 800

DROP
It needs to wait a bit until LPL is done, but by that time it has the PROJECT OGRE keyword in X and can start searching. As you can see, the file is now DROPped instead of WIPEd.


The FIND code is almost the same as in my 565 cycles solution. The only difference is that if it needs to go search in the deepest host, it will take the file along.

Code: Select all

;XC at the end of the FIND code

SEEK 1

MARK FOUND
SEEK 3
COPY F X
DROP
LINK -1

GRAB 400
COPY 10 F
COPY T F
SEEK 4
COPY X F
COPY 10 X

MARK COPY
After FOUND, the EXA has the LINK ID in T, and X isn't needed anymore. So it uses that first visit to immediately copy the word SPECIAL. The extra SEEK 1 at the top was added to the fallthrough case where the very last host is the one holding the file. The TEST in all other cases also pushes the file cursor forward one step.

After grabbing the file containing the partially copied censored file, the EXA writes 10 to the copy counter (because the first 10 values are already in there), and then it caches the LINK ID in the file. Finally, it copies SPECIAL to the right position, and starts the general copy loop by putting the 10 count value into X. The copy loop stayed unchanged from before. Here's the complete code.

472/131/54. A cycles and activity improvement. And the cycles are better than the top percentile. My best lines of code solution was 79 as I showed above. Not bad.


Some final remarks: I did actually try to use all uncensored words in the censored file. But the code to move them forward and then only grab the necessary words from the uncensored file just didn't fit. A solution like that requires a SEEK F using the counter value at the start of the file. Interestingly enough, that instruction actually moves the cursor forward F + 1 places, because the act of reading from F also moves the cursor forward one.

I think the only way to get the top percentile activity of 46 is to actually use code like that. It's a reduction of 8 more activity, and each activity roughly corresponds to copying one value (you copy 2 per round but a round requires 2 LINKs). There are 9 more values in the uncensored file (and with some trickery you could reuse the words THE and A because they both occur twice in the file), so theoretically you could save slightly more than 8 activity, but this might not be possible without going over the max size limit.

Image Well, if anyone in the thread has any ideas, I'd like to hear. Let's finish this update.

Image

Finishing hydroponix' task makes 4 more tasks appear in the list. Next time, =plastered.

User avatar
Part 48 - The Wardialer


=== Trash World Inbox ===
Quackles wrote:Anyway, time for me to take a stab at my version. This version actually has a few funny stories behind it.

When I first solved this puzzle, I didn't figure out the lock, so my version tested the codes 111, 112, 113, 114, 115... and so on. I ended up with a whopping 20k cycle count. This is part of why the cycles histogram for this puzzle is so skewed.

Later, before the LP began, I went back and figured the lock out. That got me to 795/83/100.

Now, I've adopted a number of optimizations from CO2's post, including loop unrolling (albeit for the lock in my case), and hardcoding the expected length of the file.

Code: Select all

	;FIRST TRY TO BREAK LOCK
	;IT'S LIKE A MASTERMIND GAME - 
	;CORRECT DIGITS ARE HELD
GRAB 300
LINK 800

MARK LOCKLOOP
@REP 9
COPY @{111,111} #LOCK 		;111, 222, 333...
ADDI T #LOCK T
@END
COPY T #LOCK
We start by unrolling the lock loop to copy 111 through 999 into the lock and adding the result to T. T has the completed combination at the end of the lock.

Code: Select all

;NOW WE SEARCH
LINK 800
COPY F X	;'PROJECT OGRE'
DROP 

@REP 4
LINK 80@{1,1}	;801, 802, 803, 804
GRAB 200
TEST F = X
TJMP 80@{1,1}COPY		;IT WORKS WITH LABELS TOO!
DROP
LINK -1
@END
COPY 0 T
GRAB 300
JUMP LOCKLOOP

@REP 4
MARK 80@{1,1}COPY
COPY 80@{1,1} T		;SET LINK ID (T) TO 801, 802...
JUMP COPY
@END
This block of code tests each of the files in 801-804 to see if it's about Project Ogre. If so, we jump to 80#COPY (there are four of these labels), which sets the link ID before jumping to the label COPY, which copies the file.

Code: Select all

MARK COPY
	;PERFORM SOME SETUP
	;AS WE CAN'T HOLD 3 VARS AT ONCE
DROP
LINK -1
GRAB 300
COPY 4 X 	;FILE PTR
COPY X F
COPY T F 	;LINK ID
DROP

MARK COPYLOOP
LINK T
GRAB 200
SEEK X
COPY F T
COPY F X
DROP
LINK -1
GRAB 300
SEEK F
COPY T F
COPY X F
SEEK -9999
ADDI F 2 X 	;FILE PTR
TEST X > 42
TJMP BAIL
COPY F T 	;LINK ID
SEEK -2
COPY X F
DROP
JUMP COPYLOOP
Finally, we're ready to do the actual copying. We replace the first two values of file 300 (the source file) with the file pointer (4) and link ID. Then, the copy loop has us go into the link, jump to the chosen point in the file, and copy two values into X and T. We return to file 300 and use SEEK F to pick up where we left off. (As CO2 noted above, this actually jumps us to location F+1, but there's a fix for that later. As the last part of the loop, we update the file pointer, read the link ID again, and dive back in.

Once the file pointer is over 42, we jump to the end code:

Code: Select all

MARK BAIL
	;REPLACE ORIGINAL TWO FILE VALUES
SEEK -9999
SEEK 1
COPY F X
DROP
LINK X
GRAB 200
COPY F X
COPY F T
DROP
LINK -1
GRAB 300
COPY X F
COPY T F
SEEK 2
VOID F
LINK -1
LINK -1
LINK -1
We grab the first two values from file 200 and write over where we stored the file pointer and link ID in our file. Then we return home.

506/112/60. Not bad, all things being equal.
A slightly different solution than I had but the general approach is similar. That's not too surprising - the single EXA constraint limits the options. Also, I'm not sure if I've used the REP variable label trick before. I know I considered it for other puzzles but often it didn't make much of a difference.


=== The Wardialer ===

Image

Image It is time to create that wardialer we heard about in the chat.

Image
OST: Network Exploration

The assignment:
- File 300 contains a phone number where one to three of the digits have been replaced with a placeholder (X). Using your modem, dial all possible phone numbers that could be generated by replacing the placeholders with the digits 0 through 9. When you discover a valid phone number that connects to another host, append it to file 301.
- Note that the phone numbers must be dialed and appended in a specific order such that a placeholder is only incremented when the placeholder to the right cycles through all possible values from 0 to 9 and is reset to 0.
- For more information see "Hacker Skills: Model Control at the Direct Level" in the second issue of the zine.


Another modem level. The second point of the assignment is just a complicated way to say the results should be ordered from low to high.

The files in the remote hosts contain some other phone numbers. They don't match the partial number in file 300 so I have no use for them. There's also some hardware registers out there I don't care about for this assignment.

For this first test, the partial number is 94-0-177X-X96X. To get the phone numbers in order, I should first dial 94-0-1770-0960, then 94-0-1770-0961 and so on.
The easiest way to handle this is if I can just update those placeholders in-place. However, I need to keep track which values are placeholders.

I'm thinking I can do this by choosing alternate numbers that are outside the normal range and that can easily be converted back to the actual ones.
I think I'll just subtract 10 from every number, so the digit 0 becomes -10, and the digit 9 becomes -1. To dial it, I'll just have to add 10 again.

Code: Select all

GRAB 300
LINK 800

; FIX PLACEHOLDERS
MARK FIXLP
TEST EOF
TJMP DIAL
TEST F < 9999
TJMP FIXLP
SEEK -1
COPY -10 F
JUMP FIXLP
This replaces all the X's (non-digits) with -10, which will dial the digit 0. Let's just try dialing the first number.

Code: Select all

MARK DIAL
SEEK -9999

@REP 11
MODI F 10 #DIAL
@END
While I was writing this part I suddenly realized my choice to offset everything by 10 is even more useful than I thought. I can use the modulo function to change them into the correct digit and send them to the #DIAL at the same time.

As a side note, it turns out that there's no single definition for modulo operations on negative numbers. Is -4 / 10 equal to -1 remainder 6 or to 0 remainder -4? Different programming languages disagree on this so this code might fail if run on anything else than an EXA. In this case, MODI -4 10 is equal to 6, which is what I need.

Now, what's next? Creating a REPL to go check if there's a connection? Sure, we could do that, but it's always good to pay close attention to what's happening in the system.

In this case, I'm coding for the NETronics NET40 modem, not the TEC EXA-Blaster from the previous assignments. It's almost identical but it has one significant difference.

Image

There's a number visible on this #DIAL register. That means it isn't write-only. When you read it, it returns 1 when the modem is connected, 0 otherwise. That means the entire connection check is just a single TEST of the #DIAL register.

Code: Select all

COPY #DIAL T
FJMP NEXTNUMBER

; TODO

;HANGUP
COPY -1 #DIAL

MARK NEXTNUMBER
So the test code is just this. I'll implement storing the number later. Let's first focus on updating the number in the right order. Currently it ignores whether the number is correct and just continues on, so I can test that part independently.

Code: Select all

MARK NEXTNUMBER

; ALREADY AT EOF
SEEK -1
TEST F < 0
SEEK -1
FJMP NEXTNUMBER

ADDI F 1 T
SEEK -1
FJMP ROLLOVER
COPY T F
JUMP DIAL

MARK ROLLOVER
COPY -10 F
SEEK -1
JUMP NEXTNUMBER
Because the EXA just finished dialing, the cursor is already at EOF. I want to update the numbers right to left, so that's convenient. All file access moves the cursor. ADDI F 1 F would add 1 to the value in F and write that to the NEXT position in F. So I need an intermediate register.

In most cases, it will just add 1 to the value and write that back to the file. The jump to ROLLOVER only happens if T is 0 - that is, if I just tried all 10 digits. In that case, I need to write -10 back to that value so the code keeps recognizing it as a placeholder. Then I skip over it, and find the next placeholder in the file.

Next round, the first placeholder is -10 again, it becomes -9, and so on, until it rolls back round to 0, the ROLLOVER is triggered and the next digit is incremented. It works recursively for however many placeholders there are.

Once all numbers have been tried, it will rollover past the final placeholder in the file, and get in an infinite loop as it keeps SEEKing to the start forever.

Let's handle that.

There's no direct way to test if the cursor is at the start of the file. You could test if the digit at the current location is the same as the one at the start but of course digits can occur multiple times. Since the phone numbers are always 11 digits I decided to use a simple counter.

Just after dialing, 11 is stored to X. The NEXTNUMBER loop is amended:

Code: Select all

MARK NEXTNUMBER
TEST X = 0
TJMP END

; ALREADY AT EOF
SEEK -1
TEST F < 0
SEEK -1
SUBI X 1 X
FJMP NEXTNUMBER
A SUBI and a TEST. At the bottom of the code, MARK END just has a WIPE after it to get rid of the temporary file.

With that done, it's time to copy the valid numbers.

I can replace the TODO from earlier with a simple snippet that copies the actual digits over M:

Code: Select all

SEEK -9999
@REP 11
MODI F 10 M
@END
Then, the final touch is an XB that can receive the data.


Image

Fuck nazis. Anyway, XB can just read M 88 times, because for every test there are 8 valid numbers, so that's 8 times 11 digits.

This is my first working solution.

Image

With 147 lines, the fully unrolled XB just barely fits. And with 65K cycles, it takes an age and a half to even verify that this is a working solution.

So, I'm at 65154/147/1. Top percentiles are 20.7K, 47 and 1. Let's see if I can improve some things.


First of all, for a quick reduction of the size, I'll just roll up loops.
Simply replacing all three @REPs with a COPY number T followed by a loop that has a SUBI T 1 T and a TJMP reduces the size to 52, but the cycle count becomes 88338.

If XB KILLs XA after it got all the phone numbers, I can remove XA's counter and completion check in NEXTNUMBER. This brings the size down to 51.

Code: Select all

DROP

LINK 800
KILL
GRAB 300
WIPE
67922/51/3. It doesn't just save a line, it's also quite a bit faster.

I also have a couple non-conditional JUMP statements. Perhaps I can get rid of one by reordering the code.

Code: Select all

;XA

GRAB 300
LINK 800

; FIX PLACEHOLDERS
MARK FIXLP
TEST EOF
TJMP DIAL
TEST F < 9999
TJMP FIXLP
SEEK -1
COPY -10 F
JUMP FIXLP

MARK NEXTNUMBER
; ALREADY AT EOF
SEEK -1
TEST F < 0
SEEK -1
FJMP NEXTNUMBER

ADDI F 1 T
SEEK -1
FJMP ROLLOVER
COPY T F

MARK DIAL
SEEK -9999
COPY 11 T

MARK DIALLP
MODI F 10 #DIAL
SUBI T 1 T
TJMP DIALLP


COPY #DIAL T
FJMP NEXTNUMBER

SEEK -9999
COPY 11 T

MARK COPYLOOP
MODI F 10 M
SUBI T 1 T
TJMP COPYLOOP

;HANGUP
COPY -1 #DIAL

MARK ROLLOVER
COPY -10 F
SEEK -1
JUMP NEXTNUMBER

;XB

GRAB 301

COPY 88 T

MARK LP
COPY M F
SUBI T 1 T
TJMP LP

DROP

LINK 800
KILL
GRAB 300
WIPE
66950/50/3. The JUMP from NEXTNUMBER to DIAL has been removed. After a successful connection, the code now falls through into the ROLLOVER but that just adds an unused -10 to the EOF, it does no harm.

And for the final size reduction, I can save a line by replacing the COPY #DIAL T with MULI #DIAL 11 T. The FJMP will still act the same, but the COPY 11 T just below that is no longer necessary. 66942/49/3. Only 2 lines away from the top percentile score.


Except for rolling up the loops, all the changes I just did not only reduced the size, but also made the solution run faster.

Keeping the speed improvements and unrolling all three loops again, I'm at 43904/145/3. Much better than what I had but still far from the top percentile.


I have a couple ideas on how to get there. For instance, it would be much faster to update the placeholders from left to right, but that would require sorting in XB. Or, you could send multiple digits over M with one message, because 4 digits fit in a number. But that would require multiplying the digits by factors of 10, and that's difficult when I also need the MODI trick to deal with the placeholders. It might not really be faster.

The third option and the one which I think is most likely to work, is parallelism. You could have one EXA dialing while another is updating the number. Of course you can't do that on the same number so you'd have to make a copy. Perhaps one EXA could try all numbers where the last placeholder is odd, another where all of them are even.

And the final option, of course, is to cheese it by building special cases for the slowest tests.

I don't like to cheese the tests if I don't have to, so let's start with the third option. Here is the entire code:

Code: Select all

;XA LOCAL

GRAB 300
LINK 800

REPL ODD

; FIX PLACEHOLDERS
MARK FIXLP
TEST EOF
TJMP COPY
TEST F < 9999
TJMP FIXLP
SEEK -1
COPY -10 F
JUMP FIXLP

MARK ODD
MAKE
@REP 11
COPY M F
@END

COPY 0 M

MARK MAKELASTODD
SEEK -1
TEST F = -10
SEEK -1
FJMP MAKELASTODD
COPY -9 F
JUMP DIAL


MARK COPY
SEEK -9999
@REP 11
COPY F M
@END

MARK DIAL
VOID M

SEEK -9999

@REP 11
MODI F 10 #DIAL
@END

MULI #DIAL 11 T
FJMP RELEASE

SEEK -9999

MODE

@REP 11
MODI F 10 M
@END

MODE

;HANGUP
COPY -1 #DIAL

MARK RELEASE
COPY 0 M

MARK NEXTNUMBER

SEEK -1
TEST F < 0
SEEK -1
FJMP NEXTNUMBER

ADDI F 2 T
SEEK -1
SUBI T X T
FJMP ROLLOVER
COPY T F
TEST T = 1
TJMP ODDROLLOVER

COPY 0 X
JUMP DIAL


MARK ROLLOVER
COPY 1 X
COPY -10 F
SEEK -1
JUMP NEXTNUMBER

MARK ODDROLLOVER
COPY 1 X
SEEK -1
COPY -9 F
SEEK -1
JUMP NEXTNUMBER


;XB GLOBAL

GRAB 301

COPY 8 T

MARK LP
@REP 11
COPY M F
@END
SUBI T 1 T
TJMP LP

DROP

LINK 800
KILL
KILL
GRAB 300
WIPE
GRAB 400
WIPE
The 'main' XA works the same as before, replacing placeholders with -10. After that it sends the whole file over M. The ODD EXA retrieves the whole file, then changes the very last placeholder to -9 (corresponding to the digit 1 for dialing).

The communications in LOCAL mode are purely to prevent both EXAs from dialing at the same time. As soon as one is done dialing, it releases the other with an M message so it can start dialing the next number. I added MODE instructions around the copy-to-XB code so that part is done in GLOBAL mode.

I also needed some special code in the NEXTNUMBER. It keeps X set to zero unless a rollover is triggered, in which case X becomes 1. The code updates each placeholder value by adding (2 - X). That way, it tries every 2nd number for the last placeholder, but every single number for the others.

In the old logic, once the value was increased all the way up to 0 this triggered the ROLLOVER through an FJMP. For the odd EXA, this won't work because the value will jump from -1 to 1, both considered true. I added special handling for this case through the ODDROLLOVER.

XB is mostly the same. I had to partially roll up the loop to make space for the extra code, and of course kill the additional EXA and its file.

38579/127/4. An improvement but not as much as I'd hoped. It might be slightly faster to release the other XA before sending data over GLOBAL M, but that leads to an issue where the odd EXA gets stuck once the even one tries all numbers and gets in the infinite loop, never reaching the VOID M.

There are specific tests that run very slowly. It's because all the placeholders are near the start of the phone number, and every time it seeks through the entire file from end to start.

I can partially solve this by storing the location of the last placeholder in the file, like so:

Code: Select all

MARK FIND

SEEK -1
TEST F = -10
ADDI X 1 X
SEEK -1
FJMP FIND
SEEK 9999
SUBI 0 X F
COPY 0 X
This FIND loop runs after the placeholders have been replaced by -10, but before the data is copied to the ODD EXA. The additional value is copied to the ODD EXA too. I need an extra SEEK -1 before the MAKELASTODD in case the value found is exactly -10. Then, just after the RELEASE, when a number was dialed, the cursor is sitting on this additional value in the file, and a SEEK F brings it directly to the last placeholder. The SEEK F moves the cursor one place forward before seeking back, but this works out because the NEXTNUMBER starts with a SEEK -1 anyway.

22549/139/4. Getting much closer to the top percentile. The slowest test is now one that has the last placeholder near the end of the file, but two others near the start.

That could possibly be solved by keeping track of the location of multiple placeholders, but that's a lot more difficult than what I just did. Some tests only have one placeholder at all, so you'd have to handle that. It'd also require some additional place to even store it, where it could be accessed quickly. And I'm running out of space for lines of code. So, I'll leave further improvements to you. Here's the full code if you want to give it a go.

Next time, we help selenium_wolf.

Locked