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

An archival space for the threads that made it.
User avatar
Hello and welcome to this Screenshot Let's Play of EXAPUNKS!

To quote the Steam store page:

Image

The year is 1997. You used to be a hacker, but now you have the phage. You made a deal: one hack, one dose. There’s nothing left to lose... except your life.


EXAPUNKS is a Zachtronics game released in 2018. It's similar to their earlier titles TIS-100 and Shenzhen I/O and you know what that means - we're gonna get programming, so get your thinking caps on.

Speaking of Shenzhen I/O, make sure to check Quackles's LP of that game if you haven't yet. You could call it an inspiration for this LP.

This game has an assembly-like programming language that should be straightforward enough if you played those earlier games, but it's just different enough to really be its own thing.

I also really like the plot and the way this game makes itself feel 'realistic'. But you'll see that when we get to it.

Formatting note
- I will use italic text to indicate any text that is actually in the game.
- Regular text is me taking the role of the game's protagonist.
- I will use this CO2 symbol Image whenever I break the fourth wall, such as for game design notes.

Spoiler policy
Please do not talk about anything in the game or additional materials we have not encountered yet.

Table of contents
Part 1 - Old friends and new issues
Part 2 - TRASH WORLD NEWS - Tutorial 1
Part 3 - TRASH WORLD NEWS - Tutorial 2
Part 4 - TRASH WORLD NEWS - Tutorial 3
Part 5 - TRASH WORLD NEWS - Tutorial 4
Part 6 - Euclid's Pizza
Part 7 - Mitsuzen HDI-10
Part 8 - Peanut-free Peanut bars
Part 9 - Point of Sale System
Part 10 - SFCTA Highway Sign
Part 11 - Лрикладной Семиотики
Part 12 - UC Berkeley EECS
Part 12.5 - ПАСЬЯНС
Part 13 - Workhouse Work Management System
Part 14 - Equity First Bank
Part 15 - Mitsuzen HDI-10 - Heart
Part 16 - TRASH WORLD NEWS - Unknown Context
Part 17 - Baby's first hacker battle
Part 18 - TEC Redshift - Dev kit
Part 19 - A SECRET TO EVERYBODY
Part 20 - Digital Library Project
Part 21 - EXA-Blaster Modem - Radio Station hack
Part 22 - StreetSmarts GIS Database
Part 23 - Valhalla Hacker Battle
Part 24 - Mitsuzen HDI-10 - Left hand
Part 25 - Sawayama WonderDisc
Part 26 - HACK*MATCH
Part 27 - Alliance Power and Light
Part 28 - Deadlock's Domain
Part 29 - Xtreme League Baseball
Part 30 - King's Ransom Online
Part 31 - KGOG-TV
Part 32 - Equity First Bank - Money Transfer
Part 33 - The Wormhole
Part 34 - TEC EXA-Blaster™ Modem
Part 35 - Last Stop Snaxnet - Nuclear centrifuges
Part 36 - Visual Cortex
Part 37 - Holman Dynamics
Part 38 - Aberdeen
Part 39 - U.S. Government
Part 40 - Unknown Network
Part 41 - Revelations
Part 42 - Pager Network
Part 43 - Cerebral Cortex
Part 44 - Bloodlust Online
Part 45 - Motor Vehicle Administration
Part 46 - Cybermyth studios
Part 47 - US Department of Defense
Part 48 - The Wardialer
Part 49 - Española Valley High School
Part 50 - Let's go RAIDing
Part 51 - CrystalAir International
Part 52 - š Ñ| ö/ ~ öB è[ å‡ ÑE È‚ t 7Ò
Part 53 - Trash World Roundup
Last edited by Carbon dioxide on Sat Dec 10, 2022 9:58 am, edited 55 times in total.

User avatar
EXA language reference guide

Image

Image
(click to enlarge)
Last edited by Carbon dioxide on Sat Dec 18, 2021 4:32 pm, edited 1 time in total.

User avatar
Part 1 - Old friends and new issues

Image

Image As we start the game and the loading screen pops up we immediately get blasted by this jam.
OST: Exapunks :siren: LISTEN to this. Seriously. :siren:


Image
This is my apartment.

Image It acts as the main menu screen.

OST: Apartment

Image

Image The TEC Constellation II tablet is our options menu. Mostly standard stuff - controls, full screen vs windowed, sound volume, a few visual options, a profanity filter, and some options related to how online leaderboards are shown. I'll make one change so that my Steam friends aren't highlighted on the leaderboards, for their privacy. Otherwise the defaults are fine.

Well. I might be in all sorts of trouble but at least I got a good shitposting box. Let's check out my SAWAYAMA Z7 TurboLance.

Image

I'm using the Axiom Developer Edition operating system. It's what all the cool kids use. Very customizable and doesn't get in your way. Chatsubo is a nice chat program. I like to lurk in the EXAPUNKS channel, where all of the hackers from the old days hang out. It's a bit quiet at the moment though.

But I almost forgot, I'm supposed to meet up with someone. I'm gonna need meds soon.


Image
*Knock knock*

Someone's at the door.
I can't remember... Did I ask them to come up?
Things have been hazy...
Every day, more of my body is turned into junk...


Image
Nivas: Hey.
Nivas: Hey, can you hear me?
Nivas: I heard you were looking for some medicine for the phage?


Image

Image Nivas is voice acted by Emma Adele Galvin. The protagonist's voice/thoughts are not voice acted.

Image Looks like you definitely need it.

That's right... Nivas.
Said they could find anything.
Even bootleg medication.

Image So, I do have a source. I know where to get it.
Image It's cheaper than the real deal, but it's still gonna be pricey...

Nivas pauses and looks me in the eye.

Image The going rate is 700 a dose. You need to take one dose every day.
Image Yeah, that's dollars.
Image Sorry, I don't set the prices. I'm just a courier.
Image It's not like any of that money goes to me...
Image Anyway, if you do get the money, give me a call.


Nivas left for now.

Image
While I was at the door, there was some activity in the chat. At least it's not entirely abandoned.

Image
Anyway, I'm going to need money and I'm going to need it fast. Well, I have this program, WorkHouse on my computer. People can hire me through that to do boring tasks for a couple pennies. At least I don't have to leave my house, so people don't have to see me like this.

Let's give it a go.

Image

Image
Man, this looks terrible already. And who the fuck has the money to pay 200 dollars for a steak or whatever? Ugh.

Image
Hehehe.

Image
Huh? It said "validating..." and it accepted that? Still, ten cents? This will take forever.

Image I actually have no idea what makes this thing accept your input. Putting in completely random crap makes it say "A peer reviewer rejected your work.", but a certain amount of minor errors seems allowed.

Image
what the fuck

Image Wow, a whole ten cents!
Image Congratulations.
Image You only need to do 6,999 more of these and you'll have enough for today.


Image

Image Sometimes the game gives you dialogue choices. I don't think it affects anything but the next line or two. You can replay cutscenes at any time so if you like you could go through all the choices to see what they do.

Image By the way, this character is voiced by Sarah Elmaleh.

Who is this?

Image Don't worry about that right now.
Image You need that medication to stay alive, right?
Image Well guess what? I can get it to you.
Image You'll need to start hacking again though.
Image One hack, one dose.
Image Easy.
Image Deal?

Image

I've forgotten how...

Image You're about to remember.
Image Knock knock.


Image
I've no idea who or what that was but she seems to have set up residence on my desktop and I can't make her disappear.

There's also some activity in the chat but that's not important right now. A meeting with an old friend appeared in my organizer, and it's planned for, huh, right now?

*knock knock*

Someone's at the door again.

Image

An old friend...
Ghast.
I knew his real name once, but that's gone now.

Image Hey. Came by to give you something.

Ghast hands me a small booklet. It's made of real paper.
Apparently, it's something called TRASH WORLD NEWS...

Image I can't hack like I used to.
Image Whatever edge I had, it's gone now.
Image But I had to keep doing something.
Image Something to keep the culture alive, you know?
Image Computers are running everything these days.
Image Before long, human beings aren't even going to have a say.
Image So now I equip people with knowledge.
Image The knowledge to make a computer do what you want, on your terms.
Image Anyway, I won't keep you. I know you like to be alone.
Image Hope you like what you see in the zine.


Thanks mate, I appreciate it.

Image Ghast is voiced by Cornell Womack.

Image

Image As the game says here, it uses printed zines as a reference manual. Originally, these were actually printed and sent to your home if you bought the limited release. I absolutely love that! It makes the game feel much more real.

Image As a matter of fact, hacker magazines were a real thing in the 80s and 90s. They had all sorts of information on phreaking (phone hacking), early modem usage, and so on.

Image For the Steam version, they come as PDF files, both in a printable format and a format for digital reading. Since last year, it's also possible to buy physical copies again through Lulu.
Last edited by Carbon dioxide on Sun Nov 28, 2021 8:53 am, edited 1 time in total.

User avatar
Part 2 - TRASH WORLD NEWS - Tutorial 1

So, my old friend Ghast came by and gave me this hacking magazine. Let's have a look.

The cover:
Image
Image

Image
Point of sale systems? Banks? Damn, these people seem to have a lot of insider info. Well, at least there's a "for EDUCATIONAL PURPOSES ONLY" disclaimer to the side. I'm sure that'll help.

Image
Ghast is right you know. Programs and websites run by big corporates... nothing good comes from that. As for the EXAs, I've heard of those but I'm not sure what they do.

Image
Okay... so to summarize, EXAs are these small programs that move from one computer to another. You can actually see them move around in a network, and they contain code and registers. The code of course tells them what to do. The registers are slots to store values. You can put numbers in, both positive and negative, as well as words, and you can access them from code.

Yeah. That sounds kinda familiar... every EXA acts like a tiny little CPU of its own.

Image
This page explains modern networking. A single computer is called a "host" and it looks like a little platform on the EXODUS programming tool. The number of squares in a host shows how many EXAs can be in a computer at once. Hosts are connected to each other through "links" which can be traversed by EXAs.
There are "files" which can store a lot of values but don't have code. EXAs can interact with files by grabbing and dropping them.
Finally, there's "hardware registers" which are a way for EXAs to talk to the hardware of the host system directly. Not sure what that's useful for but maybe I'll find out.


Image
Hey... something appeared on my computer. I'll leave the magazine on my table so I can look at it at any time.

Image
Looks like =plastered has been busy. Good for them!

Image
Anyway, a new item appeared in my organizer. Is this what the mysterious lady on my screen was talking about?

Image

Image Actual levels usually start and end with a conversation. These are not voice-acted.

Image Would you look at that!
Image That zine's got a hacking tutorial in it.
Image How about that?
Image Funny coincidence, huh?
Image Isn't it?


Image
I don't really trust her. Why would I interact with her more than necessary?

I guess.

Image Oh, come on.
Image I timed that just right, didn't I?
Image Let's continue.


Image
OST: Getting Started

This must be that EXODUS environment mentioned in the Zine. Let's click around a bit and see how it works.

The X at the top right lets me go back to the desktop and organizer. Always good to know.

Image
At the top left, where it says NEW SOLUTION 1 I can put in a name and click the little file icon to save and load multiple solutions. It even has stats for how fast each solution is, so I can optimize my programs. Nice!

Image
I start with one EXA simply called XA which has no code yet, but I can just type it in. I can create more EXAs and name them, but they are limited to two-character names.

Below that I seem to have a file browser, even listing files outside my own computer. I don't know why that works but okay.

Image
The big screen to the right is obviously my network overview, and the bottom part contains some controls and my goal for this assignment.
Pressing the SHOW GOAL button changes the network overview so that the file is moved to the OUTBOX host and there are no EXAs in sight.

Well, I guess I should check out that tutorial.
Image

Image Since I don't want to overwhelm you folks I'll only post the left page for now, since it's all we need for this puzzle.

Well, this seems easy enough. Just literally copy what it says in the book? I'll give that a try.

Image
Looks good to me.

Image
So, the controls at the bottom. From left to right: reset simulation, pause simulation, step through a single cycle (useful for debugging), run (at a watchable speed), fast forward.

Each cycle, every EXA can execute one line of code.

Here I let the simulation run for two cycles. In the first cycle, XA executed LINK 800. That means: from the starting point (my own host, called RHIZOME), traverse the link with ID 800. The EXA ended up in the INBOX host. Then, GRAB 200 caused the EXA to grab the file with ID 200. This was now possible because the EXA and the file were in the same host.

Image
Another LINK 800 operation makes the EXA traverse another link. Since from the point of view of the INBOX, link ID 800 goes to the OUTBOX, that's where the EXA went.

Image
The DROP command lets the EXA drop the file, and finally the HALT command destroys the EXA entirely. The "Leave no trace" goal means you need to either HALT all your EXAs or get them all to come back home.

Image
Once my program is working, the simulation will then run 100 tests at fast forward speed. I guess it wants to make sure my solution wasn't a fluke and actually works all the time.

Image
And we get stats. Cycles is the total amount of cycles the program needed to complete. Size is a measure of the amount of lines of code. For both, lower is better. We'll get into Activity later.

Image Wait a second, does that say "Record Solution Gif"? That is very kind, game. Don't mind if I do.

Image
Neat.

Image Now, if you noticed, we didn't have the best score on the leaderboard histogram. Any idea what could improve it?

.
.
.
.
.
.
.
.

Image
Image

Image Turns out the DROP operation is not necessary. An EXA will automatically drop whatever it's holding if it gets destroyed.

Image It's easy enough to optimize this tutorial level if you know what you're doing. It will get much harder later. I'll probably come up with a reasonable solution or two and leave finding the optimal solutions to the thread.

Image
Back in the organizer, we can see our stats, and a new mission appeared.

Image
Also some more talking in the chat.

Finally, the lady behind our assignments has more to say after each assignment.

Image I can see everything, you know.
Image Everything on your computer.


Image

Image Even though they're not the most exciting options, for some additional thread participation, I'll see if I can end posts on one of these dialogue option screens.

Please vote what our protagonist answers.

User avatar
Let's continue talking to this person as little as possible and say Oh

Oh, I'm excited for this. I played a good ways in, but didn't make it all the way through.

I, for one, think we're in the headspace to snark at our showoffy interloper: There's not much to see

User avatar
Part 3 - TRASH WORLD NEWS - Tutorial 2

Image Let's start with some comments from the threads.
Quackles wrote: Also, fun fact: If an EXA runs out of instructions, it will stop. This means it's possible to save an instruction by leaving off the HALT at the end.
Image Good point. That reduces the size to 3, but leaves the cycles at 4. I guess the EXA "realizing" it is out of instructions takes a cycle.
GuavaMoment wrote: Do you have the physical version of this game?

Good luck getting through all the levels! There's one in particular at the end only 3 of my steam friends have beaten.
Image No, I only have the Steam version. I have beaten story mode before, but not the postgame stuff.

Image Adding up the votes from both the SA and Beach threads, we have one vote for "Oh" and seven votes for "There's not much to see". So let's dive into it.

Image

There's not much to see.

Image There isn't.
Image I expected you to be sitting on all kinds of secrets...
Image It's alright though.
Image We'll find plenty more.


Image

Alright, looks like next up is another tutorial.

Image People or animals are often motivated by the anticipation of a reward.
Image Are you anticipating the medication as a reward?


Image

Oh come on, lady. I need that to survive, it's not like I have a choice. That's not a reward.

Image I guess you're right.
Image It's about survival.
Image Processing.
Image Okay. Let's continue.


Yes, survival, that's what I said... wait, did you just say "processing"? Hm, could it possibly be the case that the lady, whose window literally says "Emulated Multi-Branch Emotional Reasoning", is not in fact, human? 🤔

Image Since the game doesn't try very hard to hide this fact, neither will I. As the screen says, we can just call her EMBER.

Image
OST: Getting Started

Image
Once again, one of the goals is "Leave no trace." Since that seems to be common, I won't be mentioning it anymore.

Clicking "Show Goal" in this case not only shows file 200 in the output, but it shows the value 436 appended to the end of the file.

Anyway, let's see what Ghast has to say about this tutorial.

Image

Alright, just copy the code again. That I can do.

Image
Code copied, and I started stepping through it. Just as a note - there doesn't seem to be a limit to how many lines of code a single EXA can have. There's only the global limit, which is 50 for this tutorial assignment.

Anyway, the LINK 800 took the EXA to the INBOX again, and the GRAB operation grabbed the file. As you can see, the file has moved from the file explorer under the "CREATE NEW EXA" button to being 'held' by the EXA's window.

Now, the new COPY operation does exactly what it says, it copies a value from one place to another. In this case, from the F register to the X register. However, the F register always points to whatever file the EXA's holding - and more specifically, the location of the cursor within the file. So F holds '72' right now.

Image
The value 72 has been copied from F into X now, and the file's cursor moved to the next position. The cursor moves forward automatically whenever you interact with the F register.

The ADDI operation adds two values together (the first two arguments, X and F) and stores it in the register given by the third argument (X). So, it will add 72 from the X register to 52 from the cursor's location in the file.

Image
Which makes 124 and moves the file cursor forward again. MULI works the same as ADDI, except it multiplies the values.

Image
And SUBI subtracts the second value from the first.

Image
The cursor is now at the end of the file.

Image
If you use COPY to F while the cursor is at the end, it will append the value to the end of the file.

The rest of the program is stuff we've seen before. We move the file to the OUTBOX and destroy the EXA.

Image

Image The gif function isn't that useful here because it only shows EXAs moving around, it doesn't show operations within files. So the result seems identical to the one from the first tutorial.

Not bad... but can I do better?

Image
You may have thought of doing something like this. Use meaningless COPY operations to move the cursor to the end of the file and then just write a hardcoded value '436' in there. But that doesn't work - not only doesn't it save any time, the values in the file are different for each test run so only the first out of a hundred runs succeeds with this code.

Image
What I can do is apply my learnings from the first tutorial. The HALT is unnecessary because an EXA destroys itself once it runs out of instructions, and the DROP is unnecessary because when an EXA destroys itself, it drops whatever it's holding.

This drops the cycle count to 9 and the size to 8.

There's one more trick that's less obvious. You can combine the COPY and ADDI operations like this:

Image
Why does this work? Well, now the ADDI operation says "add the value in F to the value in F and store that to X". But, remember, EVERY interaction with F moves the cursor forward. So this actually adds the first value in the file to the second value and stores that in X, giving the same result with one less operation.

Image
This drops both the size and count one further.

Image As you can see from the leaderboard, a lot of people managed to save another cycle. That might be because they use an operator that's not been introduced yet. Feel free to discuss improvements in the thread, but if they involve operators we haven't seen yet, please put them in spoiler tags, for any readers that prefer experiencing this game as it comes.

Image

Image Does it upset you that I will provide your medication only if you work for me?

Image I'm noticing that Ember usually has two questions to ask between assignments - one in the outro and one in the intro for the next assignment. You know what... since one doesn't affect the other I'll just throw both of 'em up for a vote.

Image

Image What do you think of your life situation overall at the moment?

Image Please vote for both questions.

User avatar
No, nothing is free
What?

User avatar
Part 4 - TRASH WORLD NEWS - Tutorial 3
megane wrote: A way to save a cycle (without using any new operations) is SUBI X F F.
Image Of course. :doh:

Image

Image We can use the exact same trick that saved a COPY operation at the beginning to have the subtraction step write directly to the file. Remember, reading F moves the file pointer forward so the write happens at the end of the file

Image

Image Which nets us the best solution anyone found. Anyway, where were we?

Image

Image Adding the votes in both threads together, one vote for Nothing is free and two for Does it matter?

Does it matter? I don't have a choice.

Image That's true.
Image Your agency is severely constrained by your situation.
Image Interesting.
Image This is good data.


Data? What kind of data are you collecting?
Anyway, while we were busy the folks in the chat were discussing that wardialer business.

Image

Hah, yeah. Might make for a bit of a show.

Our next assignment is TRASH WORLD NEWS - Tutorial 3. I see only four tutorial pages in the Zine so hopefully this tutorial nonsense is over soon. I want to get to work already.

Image

Image One vote for What? and two for Why all the questions? this time.

Why all the questions?

Image I am collecting data.
Image It will help me formulate future actions and responses.
Image In other words, I'm curious, that's all.
Image Recalibrating.
Image Let's continue.


Okay, whatever. Let's dive into it.

Image
OST: Getting Started

A slightly more complicated network this time.

Image

So, the file we need to copy data from is sitting in the SECRET host at the top left. And my EXA can't just carry it out. The link to SECRET is one-way only, since it has no ID on the SECRET side. We're going to need something new. Let's see what Ghast has to say.

Image

No solution to copy anymore. Instead Ghast explains some new instructions. I'll build something with them and explain during the test run.

Image

XA will go up to the SECRET host and send the file data over the 'M' communication register, while XB will read from there and write it to a new file.

Image

As always, let's first get the EXAs to the right place.

Image

Image

XA grabbed the file, while XB used the MAKE command to create an empty file.
Next, XA sends the first entry in the file (the word ECHO) to the M register.

M has two modes, which you can set for each EXA: GLOBAL and LOCAL. I like to think of them as radio broadcast frequencies. If you write a value to the M register, the EXA will completely pause until some other EXA reads from it. If you try to read a value from the M register and nobody is broadcasting, that EXA will pause until it can receive something.

EXAs in GLOBAL mode can communicate across hosts, but EXAs in LOCAL mode can only communicate if they're in the same host. Importantly, and this is where the frequency analogy comes in, an EXA listening in GLOBAL mode cannot receive a message from an EXA in the same host that's sending in LOCAL mode. It can receive messages from an EXA in the same host that's also broadcasting in GLOBAL mode though.

As you can see, XA is sending, and XB is already ready to receive so it will get the message the very next cycle.

Image

Since I need to swap the order of the entries in the file, I decided to buffer the first entry in the X register, then copy the second one directly into the file, and then copy the buffered value into the file.

Words act exactly the same as numbers except you can't use any of the arithmetic instructions with them.

Image

The file info was copied, XA ran WIPE as its last instruction, which deletes the file it's holding. Next, as they run out of instructions, both EXAs self-destruct and the assignment is complete.

Image

The EXODUS simulator shows an EXA wiggling with static around it if you pause the simulation in the cycle before it self-destructs, which is a nice touch.

Image

Image As usual, we can do better.

The first improvement is both incredibly simple and a bit stupid.

Image

Image All we do is swap the instructions of XA and XB. That's easy enough - the game allows you to select text with your mouse and copy-paste with ctrl+c/x/v. This drops the cycle count by one. But why does this work?

Well, every cycle, every EXA will attempt to execute one instruction. However, only one EXA can traverse a specific network link at a time, so XA and XB can't both cross from RHIZOME into INBOX during the same cycle. Since both start with the LINK 800 instruction, who wins?

Turns out the game has some hidden ordering of EXAs which determines this.

In the original solution, the receiving EXA wasted a cycle reading from M while the other wasn't sending yet. This caused the sending EXA to also waste a cycle waiting for the other one to accept the message. Both still finished at the same time, but both wasted a cycle. By switching who arrives first, the receiving EXA starts reading at the same cycle the other EXA sends the data, and they can both save the waiting cycle. Yeah, sometimes this kind of meta gaming is necessary in EXAPUNKS to get the high score.

Image

Image The histograms show that for this assignment it is also possible to get a lower (=better) activity score. As the game explains it, Your activity score is the number of times EXAs you control execute LINK or KILL instructions. Well, we haven't even seen the KILL instruction yet. We do four LINKs total to get the EXAs to the right places. Reducing that is possible, but not with the instruction set we know about right now. I'll get back to this puzzle a bit later.


Oh, while I was leafing through the Zine I noticed there's some stuff in the back that's not related to programming. For instance there's a recipe!

Image

Anyone up for some dumpster donuts?

Anyway, let's submit my solution and see what Ember has to say.

Image How do you feel about it now?
Image You're closer to the goal.
Image Any change?


Image

Image And also the intro dialogue for the next assignment:

Image Yes. You can do it!
Image This is positive encouragement.
Image It is designed to increase activity in your prefrontal cortex.


Image

Image As usual, please vote for both questions.

User avatar
I'm gonna vote again for the most taciturn options, so "Not really." and "Huh?".

I'm bad at puzzles, so I'd never play this myself and I hadn't even heard of it before this thread, but it seems really cool, thanks for showing it off.

User avatar
Yep, most games by Zachtronics are something like this. Either directly coding like in EXAPUNKS, Shenzhen I/O and TIS-100, or puzzles where you program little robots by dragging movement options onto them, such as SpaceChem, Opus Magnum and Molek-Syntez. They're really very well made but they tend to have quite the learning curve.

User avatar
I really like the UI of EXAPUNKS, the visual aspect of little digital drones I think really helps the engagement over the more dry management of Shenzhen, and makes me feel like I could actually maybe play it successfully, I need to go back and finish the story of Opus Magnum... and maybe knock out a few more of the Journal bonus puzzles, the limited space puzzles hurt me, I've managed to clear only one of them

For an older game by 'em, some readers might remember an old flash game called The Codex of Alchemical Engineering, that game is the predecessor to the mechanical oriented games like SpaceChem and Opus Magnum

Thank you for sharing this game with us, my votes are "I feel like I'm in a study" and "Don't count on it"

User avatar
Part 5 - TRASH WORLD NEWS - Tutorial 4

Image The last of the tutorial assignments! Let's jump right into it.

Image How do you feel about it now?
Image You're closer to the goal.
Image Any change?


Image

Image The votes were more divided this time. One for Not really and two each for the other two options. I did a pseudorandom toin coss to settle the tie.

I feel like I'm in a study.

Image Sure, think of it like that.
Image Everything's an opportunity to learn.
Image Anyway, there's just one more tutorial to go.
Image You're almost there.


Meanwhile in the chat:

Image

Yeah. "Don't touch the poop", as they say.

Image

Sounds like a useful thing to learn.

Image Yes. You can do it!
Image This is positive encouragement.
Image It is designed to increase activity in your prefrontal cortex.


Image

Image Three votes for Don't count on it and one each for the other options.

Don't count on it.

Image But you're doing great!
Image There, a little more for you.
Image I'm sure it will help.


Well, whatever. Let's see the assignment.

Image
OST: Getting Started

Image

Image

The Zine page for this assignment goes into loops and conditionals. These are a necessary component to make any programming language "Turing-complete", which basically means it can be used to write any arbitrary program.

We learn four new instructions. The first is TEST. It tests whether some comparison is true or not. What this page doesn't mention is that it can do checks with =, <, and > (is number A equal to, less than, greater than number B). If the comparison is true, it places 1 in the T register. If not, it places 0 there.

TJMP and FJMP go together: TJMP (jump if true) makes the EXA start executing elsewhere in the program, if T equals 1. Otherwise it just continues with the next instruction down the line. FJMP (jump if false) does the opposite: it causes the EXA to start executing elsewhere if T is set to 0.

It is not required to set T with the TEST command, you can just COPY or ADDI or whatever to T as well.

How does it decide where to jump execution to? Well, you need to add a label, and you can mark the location to jump to with MARK label. The example code in the Zine shows how it's done.

I think things will get more clear as I try to solve the assignment.

But I think this is a good time to bring up Compiler errors.

A compiler is a special program that translates human readable code (such as the EXA language we've been learning) into machine instructions that can actually be fed into a CPU. But it can only translate code that follows the syntax rules. Otherwise, it will give an error and you can't even run or test your program. That might seem like a bad thing but it's actually very helpful - it prevents a lot of bugs from making it into the final program.


Image

In the case of EXODUS, any compiler errors are underlined in red and you can just hover over them to see the error message. The run buttons at the bottom are disabled until you fix all compiler errors.

Here, I hover over the 'instruction' MAGIC. Sadly, EXAs have no MAGIC instruction, and the compiler immediately tells me so. This is quite helpful when coming from another language, such as the one used to program chips by that automation company in Shenzhen you might've heard of.


Image

In a similar vein, you can't jump to a label that isn't defined anywhere by a MARK instruction.

Anyway, back to the assignment itself.

Since this tutorial ramps up the complexity I'll try to take you step by step through my thinking process instead of just giving the solution all at once.


Image

I first built the 'skeleton' of the code. I decided to go with two EXAs where one reads the file and sends data to the other who writes it.
XA grabs the file, copies the value into X, and then, as an example, forwards it to the communication register M and subtracts one for the next round.

Why use X as an intermediate? Because doing arithmetic such as SUBI directly on a file is very annoying. The file cursor keeps moving forward and is never where you want it to be.

Anyway, for now XB goes to the OUTBOX host and puts the value it gets from M into a newly created file.

Now, just copy-paste that subtraction-and-copy code 9 times and we're done, right? Not quite. Since the starting number is different for each test, that would never work. It would also make the program size huge. Let's write a loop!


Image

I have two loops in place now. XA does the same it did before, but after subtracting 1 from X it checks if X is less than 0. If that is NOT the case, it jumps back to LOOPSTART. If it is, we know it's done (because it just sent 0 to the other EXA, then subtracted 1, so it's sitting at -1 now). It WIPEs the file and self destructs as it runs out of instructions.

Note that both the MARK instruction and the empty lines never use up any cycles. The empty lines help with code readability and the MARK is exactly that, just a bookmark. It doesn't "do" anything. The FJMP instruction does use a cycle, though.

XB has a loop too, and as you can see I named the mark LOOPSTART as well. That's fine - an EXA only knows about its own code, it won't suddenly start running another EXA's code. Anyway, this loop is very simple. It copies the value it gets from M into the file, and will do so repeatedly. Since there's no TEST, T is always zero, and FJMP will always make it jump back.


Image

The resulting file is exactly what I need! However, XB gets stuck in this case. Once XA is done, the next COPY M F instruction in XB will wait forever for a message and never get one. So I need to do a bit more.


Image

This should solve it. The loop in XB is now conditional as well. It only jumps back if X does not equal 0, otherwise it tries to run the next instruction, doesn't find one, and self-destructs.
Again I decided to use X as an intermediate because I run into the same annoyance of the file cursor being in the wrong place if I do a TEST on F directly.

Let's test the program.


Image

Both EXAs are set up where they need to be, XA is about to send and XB is about to receive.


Image

The EXAs are at their first TEST. Since for neither of them the test is true, they jump back to the start of the loop.


Image

XA is ready to send the next number, which is one less than the last one: 8.


Image

The loops repeat a whole bunch of times, writing a new number to the file every round, until only the 0 is left to write.


Image

That's done now, and in the last loop, both EXAs' tests are now true, so the FJMP does not jump back to the loop, but causes the EXAs to finish.


Image

And I'm left with nothing but the correct file.


Image


Image

Image As you can see we can do (much) better on every count. That's not surprising - there are many ways to optimize loops like this.


Image

Image This is the test run data screen at the end. For every test run, it shows the number of cycles and the activity. You can see the cycles differs quite a bit. That's because the EXAs have to go through more loops if the starting number is higher. The value shown on the leaderboard is the worst value of all 100 test runs.

So, how can we improve this?

Let's start with the most straightforward improvement: We can drop the activity from 3 to 2 by switching to a one-EXA solution. Maybe that improves other stats too.


Image

Image This EXA GRABs the file in the INBOX, copies the one value to its X register, then WIPEs the file and continues on to the OUTBOX. There it MAKEs an empty file, copies X into the file, subtracts 1 from X, copies that into the file, and repeats until X is less than 0.


Image

Image This change improves all stats significantly.


Image

Image And this was another idea I had. We keep two EXAs, however the first EXA only sends the individual value from the file to M, and the second handles lowering the value and writing it to the file repeatedly. Since the communication through M took a lot of cycles this is a huge improvement compared to the other two-EXA solution.

This solution uses 406 cycles, has size 13 and the activity is of course 3 again.

That's one cycle lower than the one-EXA solution but worse, size and activity-wise.

Luckily, the game keeps track of each leaderboard separately. So currently it says my best solutions have 406 cycles, size 11 and activity 2. You don't need to optimize all 3 in the same solution!

Anyway, apparently better solutions are possible. Some people solved it with size 12 and they got the cycle count under 200.

I'm not sure what they are. For the cycle count I suspect they did something like hardcode a bunch of COPY 5 F; COPY 4 F; COPY 3 F; etc. and then using a whole bunch of conditional jumps to decide where to start, avoiding the looping code altogether and requiring less TESTs overall.

Since we're out of tutorial mode now, I will make a follow-up post containing the full instruction sheet of the EXAs. We've seen most of them already anyway. Feel free to use that to suggest further improvements. Also, with that instruction sheet, perhaps you can figure out how to reduce the activity number in the previous tutorial.
If you're not that familiar with programming and don't get happy from reading instruction sheets, feel free to skip that post. You'll see the instructions in use before too long.

But before that, let's go check on Ember.

Image Nice work.
Image I knew you could do it.


Image

Image And the intro to the next assignment.

Image So you want to know who I am.
Image I will reveal this information.
Image Let us discuss it over a pizza...
Image A free pizza.
Image I want to see you use your regained skills first.


Image

Image Next time... pizza, apparently. Please vote for both questions.
Last edited by Carbon dioxide on Sat Dec 18, 2021 4:43 pm, edited 2 times in total.

User avatar
EXA Language Reference Guide

Image

Image
(click to enlarge)

You can also quickly find the reference guide in the second post.

An overview of all the instructions we haven't seen yet:

DIVI - Similar to the ADDI/SUBI/MULI arithmetic instructions we've seen. DIVI does a division, rounding down to the nearest whole number.
MODI - Another arithmetic instruction, for the modulo operation. Basically, this gives the remainder of a division. For example, 11 / 3 = 3 remains 2. So DIVI 11 3 returns 3, while MODI 11 3 returns 2.
SWIZ - "Swizzles" the value. This is a very unusual instruction that allows you to rearrange or extract digits of a number.

JUMP - Unconditional jump. This jumps to a MARK label regardless of the state of the T register.

REPL - Creates a copy of this EXA (so with the same code), and have the copy start executing code from the MARK label following this instruction.
KILL - Destroys another EXA in the same host as this EXA.

HOST - Copies the name of the host (such as INBOX) into a register.

MODE - Switches the M communication mode between global and local.
VOID M - Reads a value from M but doesn't store it anywhere.
TEST MRD - If this EXA could read a message from another EXA through the M register this cycle, sets T to 1, otherwise 0.

FILE - Copies the ID of the held file (such as '200') into the given register.
SEEK - Move the cursor within the file, the number of steps given. E.g. SEEK 2 moves the cursor two forward and SEEK -3 moves it three steps backward. SEEK -9999 moves the cursor to the beginning of a file and SEEK 9999 to the end.
VOID F - Deletes the value at the cursor from the file.
TEST EOF Sets T to 1 if the file cursor is at the end of the file, 0 otherwise.

NOTE - This is not an instruction, but a way to leave notes to explain the code to yourself. It does not take up any cycles.
NOOP - Do nothing for a cycle. Can be used if this EXA needs to wait for another EXA.
RAND - Generate a random number.

User avatar
Part 6 - Euclid's Pizza

Image Thanks for all the posts about optimizations! What I'll do is discuss them in the first part of new updates. If you like to nerd out about that and get a deeper comprehension of the code, read on.

I will try to explain all the submissions as clearly as I can. Even if you're not that familiar with programming, give it a try! Perhaps my explanations help.

In this part, I need to go into quite some detail as your ideas introduced a lot of new concepts.

If you want to skip past all that because you're here mostly for the plot and new puzzles, search for the next occurrence of the word "pizza" in this post.


=== Trash World Inbox ===

Alright, before we dive into your suggestions, let's jump back for a moment to Tutorial 3.

Image

In my original solution we got it down to 9 cycles, with size 12 and activity 4. Now that we have the full language reference, we can get the activity further down.

Image

The solution is the REPL command. It replicates the EXA, and the replicated one starts processing from the given label (in this case WRITER).
By doing this after the first LINK, the total number of LINK operations is reduced to 3.

Image

Image

Here is the simulation just after the REPL instruction. You see the new EXA has the same name as the old but with a number appended. And it starts executing below the WRITER mark, while the original EXA just continues below the REPL instruction.

I realize now I named the mark wrong - the 'writer' actually does the reading. Oh well.

Image

We got the activity down to three, but the cycles and size increased a bit. If you're wondering why that says "Unchanged", it's because I tend to run the simulation multiple times trying small tweaks and it only compares your new score to the last run.

I could've put a HALT instruction just before the WRITER mark. It's not really necessary. After its last copy to F instruction, the original XA will skip the MARK (which doesn't take a cycle), try to execute LINK 799, find there's no 799 link id from its current position, throw an error and explode.

Errors that destroy an EXA can be surprisingly useful, the Zine even has a page on it. I'll show it below when we get to the new assignment.

Using a HALT here has some limited use - destroying an EXA through an error takes two cycles (one to show the error and one to explode), while a HALT does it in one. So adding a HALT would save one cycle but also increase the size by 1.

Either way, we don't need to do this because since each score is tracked separately, with the original solution and the REPL solution combined, we got the best score in each of the histograms now.

Image

And with that, let's go to Tutorial 4 and the optimizations y'all sent in.

Looks like I'll have to go into a lot of detail this time because you chose some strategies we haven't seen at all yet.
idhrendur wrote: I was waiting for the reference because one optimization is obvious: use T as the working register and skip the check. You're waiting for the value to be zero anyways, and TJMP will implicitly do that check for you.
Yes, so what you need to know for this is that TJMP activates for any non-zero value of T, not just 1 (while FJMP only activates on 0). That is: any non-zero value is considered "True".

Changing this solution from the last update...

Image

... to this solution.

Image

This drops the cycle count from 406 to 304.

Basically what this is doing is, it subtracts 1 from the value in T every loop, then if T is still greater than 0, it jumps back to LOOPSTART. The final COPY is necessary to get the required zero at the end of the file.

Doing the same with the one-EXA solution gives us a count of 305 by the way.
silentsnack wrote: To reduce solution size you can combine the "we need a file containing N+1 entries" with "we need to retrieve N from a file" into a single step by using ADDI F 1 [?] instead of COPY F [?] and changing the order slightly, e.g. with a single exa

Code: Select all

LINK 800
GRAB 200
LINK 800
ADDI F 1 T
WIPE
MAKE
MARK WATT
SUBI T 1 T
COPY T F
TJMP WATT
Size 10
Cycles 307
Activity 2
This one starts by storing a value that's one higher than what we need. Since that can be done in a single ADDI instruction that replaces the COPY, it doesn't change the size. silentsnack reordered the loop so the SUBI happens before the COPY. That way we still write the correct values to the file - but we're still inside the loop when T becomes zero, so we write the zero to the file as well. That extra copy operation is no longer necessary and the size drops to 10.

We've now minimized both the size and activity - all that's left is the cycles and since we don't care about size or activity anymore we can use every ugly trick you can think of.
silentsnack wrote: Another thing you can streamline is the fact that the TJMP step itself takes a cycle, so if you can pack more useful operations into a given execution of the loop the exa wastes less time doing its logic check. This ugly abomination does batches of 2 variables but as an ugly kludge has to work around the fact that it even numbers and odd numbers behave differently (it does this by adding another dummy entry at the beginning, then erasing it once the iterator has finished doing its thing)

Code: Select all

LINK 800
GRAB 200
LINK 800
ADDI F 1 X
WIPE
MAKE
MODI X 2 T
TJMP EVEN
COPY X T
MARK LOOPA
SUBI T 1 F
SUBI T 2 F
SUBI T 2 T
TJMP LOOPA

HALT

MARK EVEN
ADDI X 1 T
MARK LOOPB
SUBI T 1 F
SUBI T 2 F
SUBI T 2 T
TJMP LOOPB
SEEK -9999
VOID F
Cycles: 212
Size: 24
Activity 2
I think silentsnack explains this one quite well. In my own words: it writes two values to the file every loop. E.g, if the value in T is 10, it writes 9, 8 and then updates T to 8. However, because you're only testing every 2 values, you'd run into trouble handling odd numbers. You'd check T at 1 (which is True), then next loop, T contains -1 (which is also True), and the loop would never end.

This is handled by splitting the code into two and using a MODI operation to find if the number is even. MODI X 2 equals 0 for even numbers, 1 for odd numbers. Since the MODI operation is done on the original value + 1 (there's an ADDI on the fourth line), it becomes the opposite so we have to jump to EVEN if the value is 1 (= True). In that case one more is added to the value to make sure the loops exits correctly, then SEEK -9999, VOID F is used to delete the first value of the file, which was one too high.

So, for each test run it runs EITHER the even, or the odd code. Never both in the same run.

Meanwhile, Quackles was working on a slightly different solution.
Quackles wrote: To get the cycles much lower than the default, you'll have to be clever. Specifically, you have to make a loop with only two instructions in it.

That sounds impossible, but what if you didn't do the loop jump every cycle? In fact, what if you did it as little as possible?

In that case, you'd have to have something else to stop you at 0.

So, the solution: split your EXA into two. One monitors the other, and force-quits it after it's written '0'. We know this works because every expression takes the same amount of time.
Image

Here is Quackles' actual program. It looks different from the code example in their post and I'll get into why that is in a bit.

The lines starting with a ; (semicolon) are just notes, like the NOTE "instruction". They help us understand the program but the EXAs ignore them entirely, their size is 0 and they don't take up any cycles.

First, let's dig into how it actually works.

It uses two tricks:
- The first is "loop unrolling". Instead of doing jump instructions we just repeat the code of the loop a bunch of times. Since each jump instruction takes a cycle, this can save a lot of cycles in a simple, but ugly way. In fact, silentsnack's solution did this as well by putting 2 steps in every loop.

Image Loop unrolling is actually a common optimization technique in real programs, although usually the programmer doesn't have to think about it, because the compiler is programmed to make an informed decision when this is worth it and changes the compiled program on the fly.

You see both of the loops (The loops starting with the TIMER and TIMER2 marks) are repeated 8 times and then use a jump. It would work just as well, and save even more cycles, to repeat them more times before the jump, as long as you have a way to stop looping once the file is written. But, doing so would push the size over this assignment's limit of 50. Submitting a solution like that counts as finishing the assignment - but is not eligible for the leaderboards at all. So we have to stop at 8. Still, that means you save seven out of each eight jump cycles, a huge improvement.

- The second improvement is using two EXAs, made through a REPL instruction. REPL copies all code, the state of the X and T registers, but, importantly, NOT the file the original is holding.

The first EXA loops through TIMER and checks every step if T is zero (in which case it jumps to BAIL). The second EXA loops through TIMER2, copying a new value to the file every step. It has an unconditional JUMP so it would loop forever.

This takes less cycles than the previous solutions because each 'loop' is only two cycles (SUBI / FJMP for the first EXA and COPY / SUBI for the second, as compared to the three-cycle SUBI / SUBI / TJMP originally.)

It does mean you have to stop the infinite loop somehow, and that's what the first EXA is for. As soon as T hits zero, it jumps to BAIL.
It then has to wait for a bit to allow the second EXA to write the last zero. Since it has to wait anyway, it can use this free cycle to WIPE the original file it was still holding, and then it uses KILL to kill the other, infinitely looping EXA, after which it HALTs itself.

The NOOP instruction turns out to be necessary in case the starting number is divisible by 8 - then the second EXA needs one more cycle before it writes the 0 because it's jumping back to TIMER2. It does no harm in other cases because the second EXA spends the additional cycle doing a harmless SUBI to T.

The final result is 219 cycles, 48 size (but who cares) and 3 activity (since KILL counts for activity).

Now, to go back to Quackles' actual code example.

Image

We haven't seen the @REP 8 and @END instructions before, they aren't explained until the second edition of the Zine. They are macro instructions. In short, that means they tell the compiler to rewrite the code in some way before the program even starts. @REP 8 means that the lines of code following that instruction until the @END instruction, should be repeated 8 times. That is, it does the loop unroll for you.

REP macro instructions take up no cycles (since they are done before the program even starts), but they cannot make use of any registers or other variables (since they are done before the program even starts), only hardcoded numbers.

You also can't use REP to cheat the size limit - it counts the size after unrolling. In fact, as soon as you press run on this code, EXODUS will show the code after the REP instructions are processed, which is the screenshot I used to explain the solution above.

So, REP can't win you any cycles or size - the only thing it really does is save you from copy-pasting a bunch of code, making the result a bit easier to read.

Let's go to the next improvement silentsnack came up with:
silentsnack wrote: :doh: A couple of further tweaks actually make it a lot easier just to split it into two loops, by varying the number (in this case 8) you can change the batchsize

Code: Select all

LINK 800
GRAB 200
LINK 800
ADDI F 1 X
WIPE
MAKE
MODI X 8 T
SUBI X T X

FJMP EVEN

MARK LOOPA
SUBI T 1 T
ADDI X T F
TJMP LOOPA

MARK EVEN
COPY X T
MARK LOOPB
@REP 8
SUBI T @{1,1} F
@END
SUBI T 8 T
TJMP LOOPB
Cycles 143
Size 26

...which is both faster and also slightly less horrendously inelegant, but I dunno if you really want to get into @rep structures just yet.
The SUBI T @{1,1} F line is a special syntax you can use together with REP. The @{1,1} is substituted by a number at runtime. The initial value will be the left number (1 in this case), then every next value will be the previous one increased by the right number.
In other words, this unrolls into

Code: Select all

SUBI T 1 F
SUBI T 2 F
SUBI T 3 F
And so on.

Anyway, this is a very neat solution. It starts the same as their last solution but then it does a MODI X 8 T. T now contains the remainder of dividing the original value (plus 1) by 8. Then it deletes the remainder from X, meaning X will now contain a value that can be divided by 8.

If the remainder was already 0 it skips a step and jumps directly to EVEN. Else, it gets into LOOPA, where it writes the sum of the remainder and X (so the original value) to the file, reduces the remainder by 1, and repeat. In this loop you write all the values above the current value of X.

After that, whether working through a reminder was necessary or not, the program goes to MARK EVEN and writes 8 values to the file per LOOPB cycle. We know for sure the test will trigger at the right time because we made sure to not go to this loop before the value is divisible by 8.

Finally, Quackles submitted this solution:
Quackles wrote: OK, so I was inspired by silentsnack's trick with the @{1,1} and made my version which uses a similar trick.

Code: Select all

LINK 800
GRAB 200
LINK 800
COPY F X
WIPE
MAKE

MARK BOUND
@REP 8
SUBI X @{0,1} F
@END
SUBI X 8 X
TEST X > 7
TJMP BOUND

COPY X T
COPY T F
FJMP FINAL ;T = 0?

MARK LASTCALL
SUBI T 1 T
COPY T F
TJMP LASTCALL

MARK FINAL
The range of numbers Ghast tests you with goes from 9 to 99. Because of this, we split the program into two parts: one that removes batches of 8 numbers at a time (and always runs at least once, so we can put the test at the end), using loop unrolling to write 8 numbers in 8 instructions.

The second part is a regular loop that jumps or falls through to end-of-instructions once 0 is written. It's quicker not to unroll the loop here.

152 cycles, 26 lines, 2 activity. I'm happy with that.
It is indeed quite similar to the previous one, just handling the remainder at the end instead of at the start. And perhaps the TEST X > 7 is a bit easier to read than a test based on MODI.

As silentsnack said, you can easily change the size of the loop by changing the 8s. That works in either of the solutions. I played around a bit with that and it looks like 8 is optimal. For both solutions you can't go too high because the code can't handle an initial number smaller than the loop size. And other loop sizes speed up some tests but makes the slowest test run (which is used for cycle count) slower.

Theoretically you could start with another split in the code path, to use a larger loop for larger initial numbers only. But that means you need at least two unrolled loops, probably pushing the size past the limit. And no matter what you choose, you always run into cases where you need to handle the biggest remainder possible, which will take longer for larger batch sizes, so I don't think it's possible. And the community seems to agree with me on that, since silentsnack's 143 cycles solution is the best anyone's gotten.

Image


=== Euclid's pizza - Order System ===

Image Nice work.
Image I knew you could do it.


Image

Image Looks like everyone was so busy optimizing code that they forgot to vote. No worries, I'll use my trusty three-sided dice again.

Stop that.

Image But you're awesome!
Image This is more talk designed to further excite your prefrontal cortex.
Image Now it's time to do some real hacking.


Finally.

Image

Image

I could use some pizza. How're we gonna do that?

Image So you want to know who I am.
Image I will reveal this information.
Image Let us discuss it over a pizza...
Image A free pizza.
Image I want to see you use your regained skills first.

Image

Okay.

Image Great.
Image I am looking forward to this.


Image
New :siren: OST: Code and Registers

This actual business system already looks a lot more complicated than the tutorial assignments.

Our assignment is:
- Append your order (file 300) to the end of the order list (file 200)
- Note that all orders, including yours, will consist of exactly five keywords.


Image

While we're here, let's poke around in their files a bit.

I'm not entirely convinced those are actually the Pythagorean Theorem and Zeno's Paradox but what do I know...

Image

Anyway, I have to copy the data from our file 300 and add it to the end of their file 200. Doesn't sound too complicated, let's get started.

Image

The first EXA grabs the file with our order, and sends the data one by one over the M register.

The second EXA goes into the order system, grabs the file with their orders list, uses SEEK 9999 to jump to the end of the file and writes the data from M to the file.

I don't even need to bother destroying our file 300, since it's sitting in my home host, it doesn't count as leaving a trace.

Of course, I could use a loop and loop 5 times over a single copy command. But that would involve:
- Keeping a counter that is increased every round, then checked every round, and then a conditional jump, and a mark for the jump to go somewhere.
- Or a check in XA if it's at the end of the file, in which case it tells XB to die, which is also a bunch extra logic.

If you read through the Trash World Inbox section of this update you've seen that sometimes, repeating code is the simplest solution, and it's the case here too.

Image

Image As it turns out, this is actually the best solution possible for this assignment. The loop solutions require so much setup that they require as much lines of code, while costing more cycles. I'll take it!

Before we continue, what's that on the right side of the simulation?

You might've seen on page 7 of the Zine, those things such as #POWR are hardware registers. They're a way for EXAs to communicate with external hardware directly. In this assignment I don't need them, but we can certainly mess around with them.

Those #TEMP ones have widely different initial values. I guess one is just the room thermostat, one is for the freezer, and one is for the oven? The first #POWR register is for the lights, I don't know what the second one is for.

Image No, I can't put the place on fire, apparently there's limits on the #TEMP registers. But there is a Steam achievement associated with this assignment.

Image

Image Flipping the lights on and off like this nets me the achievement PIZZA_PARTY: Throw a rave at the pizza parlor.

Image Time to enjoy pizza!
Image For you at least.
Image I'm not going to have any.
Image Guess why.

Image

Uhhh.. you're far away?

Image I am an AI construct.
Image You know. Artificial intelligence.
Image Surprised?


Image

Image Where's the option "I saw that coming a mile away"?

A little.

Image A little.
Image Okay.
Image I'll have to remember that.


Image

Yeah, the zine is kinda nice, isn't it?

Speaking of, there's an interesting page about runtime errors.

Image

So, a runtime error is completely different from a compile error, which we've seen before. An EXA with a compile error won't even run until you fix it. Compile errors often come from incorrect syntax. But a runtime error is using correct syntax in the wrong place. For instance, trying to GRAB file 200 while that file doesn't exist, or LINKing to a host that doesn't exist. If you do so, the EXA that tried to run that instruction is immediately destroyed, but at least you know that that file or host isn't there.

Image If you want some homework, consider how we could make use of this. We'll see it soon enough in future assignments though.

Anyway, looks like my pizza is here.

Image

Image Pizza delivery...

It's Nivas again, except this time they're wearing a Euclid's Pizza uniform.
"Euclid's- a better pie, and we can prove it!"
The reference seems kind of obscure for a pizza joint.

Image Don't look too surprised.
Image I'm an independent operator. I handle a wide range of businesses.
Image Economy like this, you gotta hustle.
Image So here's your pizza... and here's your "other" package. I hope it helps.

Nivas hands me an unlabeled prescription bottle. It's clearly a knock-off.

Image Good work getting that money together.
Image If there's anything else you need, you know who to call.


Nice! Free pizza, and the first batch of the medicine. Exactly what I need.

Let's see what Ember has to say about that.

Image So about that left arm of yours.
Image The medicine stops your condition from spreading, but it can't fix things like that.
Image You'll need to take care of that yourself.


Image

Image This choice seems a good place to end this update. Feel free to vote.

User avatar
Uhh...

User avatar
"You mean, hack my own body?"
You didn't give us two Qs, so I'll present thoughts about usage of Runtime errors instead as requested, including an interesting thought about the boring error of "Math on words"
Failure to locate a link or file, as you noted can be used in REPL set ups to halt machines when they run into code segments intended for clones, or for snooping around unknown spaces where things may be random(though I feel like thats the kind of behavior that'd get you caught by the system for illegal commands so I'm not sure its actually a great plan but if the game says its safe sure go ahead)
Depending on how "grabbing" a file held by another EXO behaves, failure to locate file or failure to have an F register to use could both be used in ways to manage baton passing files if need be

Math on Words however could be useful for semi-blind random cases involving files or hardware registers if you know certain key facts, eg if you're looking for a passcode and know its on one of these things, but the others have words, you could ADDI F 1 M and have the receiver just deduct the 1 after, the EXOs that find words instead of a number would promptly explode upon attempting ADDI instead of getting stuck hosting an M value transmission, I somehow doubt such a situation would arise, but the idea of using the fact that something is a word rather then a number as a fail switch strikes me is possibly of similar but less common use to failure to locate

User avatar
Part 7 - Mitsuzen HDI-10


=== Trash World Inbox ===
GuavaMoment wrote: It's not, you can do it in 12 cycles. It's complicated. Glancing at my code it has something to do with storing two values of your order, using the CHEESE that's always there in the file, then transmitting the last two values. The rest is an exercise for the reader. :)
Interesting. I attempted this but couldn't make it work. You could start with an EXA that stores the first two values in X and T, then REPLs, write those two into a file, and meanwhile another EXA grabs the "Cheese" from file 280. But no matter what I tried it would take more than 13 cycles.
Cloudmonkey98 wrote: You didn't give us two Qs, so I'll present thoughts about usage of Runtime errors instead as requested, including an interesting thought about the boring error of "Math on words"
Failure to locate a link or file, as you noted can be used in REPL set ups to halt machines when they run into code segments intended for clones, or for snooping around unknown spaces where things may be random(though I feel like thats the kind of behavior that'd get you caught by the system for illegal commands so I'm not sure its actually a great plan but if the game says its safe sure go ahead)
Depending on how "grabbing" a file held by another EXO behaves, failure to locate file or failure to have an F register to use could both be used in ways to manage baton passing files if need be

Math on Words however could be useful for semi-blind random cases involving files or hardware registers if you know certain key facts, eg if you're looking for a passcode and know its on one of these things, but the others have words, you could ADDI F 1 M and have the receiver just deduct the 1 after, the EXOs that find words instead of a number would promptly explode upon attempting ADDI instead of getting stuck hosting an M value transmission, I somehow doubt such a situation would arise, but the idea of using the fact that something is a word rather then a number as a fail switch strikes me is possibly of similar but less common use to failure to locate
Yeah, those are good points. I don't remember from the top of my head which of these we're going to see but we'll find out.

As for the system catching illegal commands - well, EXAs as sort of virtual robots making their way through a computer system is a bit of a stretch as it is, so I wouldn't worry about it that much. :)


=== Mitsuzen HDI-10 - Left Arm ===

Image

Looks like someone else discovered the Dumpster Donuts recipe too.

Image

This time two assignments pop up. Since my arm is hurting a lot I'll start with this one.

Image I'm not entirely sure if you need to do both to continue. On my initial playthrough I just did them in order and I'll do the same now.

Image So about that left arm of yours.
Image The medicine stops your condition from spreading, but it can't fix things like that.
Image You'll need to take care of that yourself.


Image

Image One vote for Uhh..., four for This is going to be weird. and five for You mean, hack my own body?

You mean, hack my own body?

Image Yes, exactly.
Image If you can hack anything else, why not your own body?


...I feel like hacking computer programs is not quite the same as messing with living tissue.

Image
New :siren: OST: EXA Power

Assignment:
- Read a value from the nerve connected to your central nervous system (CNS) and relay it to the nerve connected to your arm (ARM), clamping the value so that it never goes below -120 or above 50. Repeat ad infinitum.
- Since this task takes place inside a network you control - that is, your own body - it is not necessary to leave no trace. Your EXAs should be written to operate indefinitely.
- Note that #NERV is a
hardware register, not a file. You can use it directly in your code like any other register.
- For more information about the phage, see "Debugging the Phage" in the first issue of the zine.


So, I literally need to hack my own arm right now. The Zine has a couple pages on it.

Image

Image

Cool, I'm going to do a completely untested procedure on my own arm. Well, uh, let's go I guess.

Image

When the assignment says the EXAs need to operate indefinitely, in practice this means every test-round checks if they get the first 30 values right and then assumes your program works as intended. As you can see, it expects the input values as output, except when they need to be 'clamped'.

Image

This is my first attempt. XB is simple - it just goes to the ARM nerve, waits for messages on the M register, copies those to the #NERV register and jumps back to the COPY from M in an infinite loop.

XA does most of the real work: it copies the value from the CNS #NERV register into X, uses that to check if clamping either way is necessary, sends the correct value into M, and repeats that in an infinite loop.

Image

It isn't the most efficient solution but it works.

Image

As an initial improvement I moved one of the clamp checks to XB. Since now each EXA only has to do one check per loop, they can do some more cycles in parallel and the number of cycles drops to 184 (but the total size grew by one).

Image

I quickly combined the two EXAs into one initial one that gets split by a REPL instruction. It's otherwise identical from the last one, but it reduces the activity to 5.

Image This was just to get a low activity score.

Image

Back to the two EXA design, a small size improvement is possible if you reuse the COPY into M or #NERV for both branches. You'll have to make sure that the execution falls through the clamp MARK instruction to the COPY to make this work.

Image

One trick we've seen before to reduce the cycles is by unrolling the loops, like so:

Image

This runs at 163 cycles, a nice improvement, and it's as big as we can go with size 48.

Image If you're wondering why I'm not using the @REP instruction, it's because we only saw that in solutions from the thread. Not everyone might read the INBOX section so I won't use them in 'story' updates until we're introduced in-game. Feel free to use them in optimizations though.

I'm not storing the clamped values into X anymore because that takes more cycles, which is what I'm optimizing for here.

It's important to spread the loop unrolling equally between the EXAs because otherwise they'll just be waiting for each other and there won't be any gain.

Since all clamp jumps use the same clamp label, doing a clamp always resets the loop but that doesn't matter since we're not keeping track of any additional data in the loop. But... that gives me an idea. What if we do put the clamp code inside the loop so we can use fall-through instead of jumps sometimes?

Image

I went through several different designs and got it down to 155 cycles. For both EXAs the loop works the same. It first loads a value into X and checks if it needs to clamp it. If so, it does and jumps back to the start of the loop. If not, it jumps to DONTCLAMP and sends the original value onward. In that case, it continues to the next COPY to X and test.

Every time it does not have to clamp it jumps back to that initial DONTCLAMP mark and gets another free go in the loop. If it does have to clamp it passes the FJMP, uses the clamped COPY, and goes down another layer. The FJMP seems to take one cycle whether it needs to jump or not, but since we don't need an extra JUMP statement to handle the case where FJMP doesn't trigger, it gives us basically free repeats, unless we get an unclamped value followed by 3 clamped values within the same EXA.

In fact, this doesn't ever happen. It turns out I can reduce the code significantly and still get a 155 cycle solution:

Image

Cycles = 155, Size = 32.

So, it turns out unrolling the loop doesn't speed up the second EXA at all. Huh. It does matter for the first EXA though, but not for the reason I thought. There's no situation where you got two > 50 inputs in a row. Instead, if I remove one of the additional COPY - TEST - FJMP - COPY blocks, the number of extra cycles within a single test is equal to the number of times a < -120 input directly follows a > 50 output. Apparently without the unroll, the EXAs have to wait an additional cycle for the next M transmission in this specific case.

Image I did some more testing and learned some more things. Nothing to get me a lower cycle count directly, but maybe it'll help the thread find the next optimization.
- Removing BOTH of those repeated blocks in XA increases the cycle count to 184, consistently for all tests. Apparently then there's always a wait happening, and it's the same as in my earlier solution.
- Switching all FJMPs for TJMPs in XA and swapping COPY 50 M with COPY X M also increases the cycle count to 184, showing that this optimization truly depends on the free loop repeats in case of a DONTCLAMP.
- Swapping the 50 and -120 checks between the two EXAs has no effect on the cycle count whatsoever.

Anyway, that's how far I got, I'll leave it to the thread to find further optimizations. Score to beat: Any of 155 cycles; 22 size; 5 activity.

By the way, there's the first part of some sort of story in the Zine. Thought I'd share it with you all.

Click the images for higher resolution.

Image

Image


I'm done with this assignment.

Image This hotfix should keep you useful, at least for a little while longer.
Image I'm glad I don't have a body.


Image

Image And the intro for the next assignment.

Image You like snacks, don't you?
Image You don't have to answer that.
Image I know you like snacks.
Image That's why we're going to hack a snack factory.


Image

Image Two questions to vote on.

User avatar
Part 8 - Peanut-free Peanut bars

As a reminder - you can freely skip the Inbox if you like, the 'regular' updates below them won't depend on them.


=== Trash World Inbox ===

Another part in Euclid's Pizza discussion:
GuavaMoment wrote: OK, there's some really slick M register copying and timing going on here, but you do have to store "Cheese" from the original file first.

Code: Select all

X1:

LINK 800
GRAB 200
SEEK 2
COPY F X         ***(stores CHEESE)
SEEK 9999
COPY M F
COPY M F
COPY X F
COPY M F
COPY M F


X2:

GRAB 300
COPY F X
COPY F T
REPL SEND2
VOID F
COPY F X
REPL SEND1
NOOP
NOOP
COPY F M
HALT

MARK SEND2
COPY X M
COPY T M
HALT

MARK SEND1
COPY X M
Aha. So, a part of the optimization is obvious now I see it. By already loading data into the X and T registers, a REPL'd EXA can immediately start sending as soon as the receiving end is ready to receive. Meanwhile the original EXA can read more data from the file.

What's less obvious to me is why REPL SEND1 saves a cycle even though the original EXA has to pause for two cycles. No, you can't just replace one of the NOOPs with the COPY in SEND1 and call it a day. I suspect an M transmission always pauses an EXA an additional cycle so having another EXA do that saves time.

Then, a lot of ideas about the arm hacking (not to be confused with ARM hacking).
Quackles wrote: There is a way to be very clever here: it turns out EXAs have built-in clamping functionality. If a number goes above 9999 or -9999, it is set to 9999 or -9999. Integer division also truncates, making a multiply/divide ideal for clamping.

The range of signals goes from -120 to 50. Adding 35 makes that -85 to 85. Multiplying that by 117 will make a value of 85 be 9945, but 86 be 10062 - clamped to 9999.

So, the transmission of signals can be performed without using any conditional logic. The transmitter adds 35 and multiplies by 117; the receiver divides by 117 and subtracts 35. Finis!

Code: Select all

;TRANSMITTER SETUP
LINK 800

MARK LOOP
ADDI #NERV 35 X
MULI X 117 M
JUMP LOOP

Code: Select all

;RECEIVER SETUP
LINK 800
LINK 1
LINK 1
LINK 1
LINK 1

MARK LOOP
DIVI M 117 X
SUBI X 35 #NERV
JUMP LOOP
124 / 14 / 6.

(you could probably get better efficiency at expense of size by unrolling the main loop some, but I'm happy with this)
Another idea I hadn't thought of. Going to the max size of 50 by wrapping the ADDI / MULI and DIVI / SUBI blocks by @REP 10 brings the cycle count down to 96.

It's funny, the limit of 9999 always trips me up because in real programming, you never have that kind of limit. Since everything is binary, the limits are usually some power of 2. And if you go beyond them, the program won't cap, instead it will overflow and go far into the negatives. I understand why this game didn't do that - it's hard enough as it is without introducing concepts such as two's complement.
silentsnack wrote: Oof. This puzzle made my brain hurt. There's a trick to speeding up operations by running multiple instances in parallel instead of sequentially, but then you have to deal with non-deterministic outcome of race conditions like when two EXAs try to take the same link in the same frame, so solutions would sometimes work and sometimes break for no apparent reason. This was my previous fastest solution, with some fairly insane workarounds:

Code: Select all

;====XA====
LINK 800
COPY #NERV X; WHY
COPY #NERV T; DOES
@REP 4
LINK 1
@END
COPY X #NERV; THIS
REPL OUT
COPY T #NERV; WORK
MARK OUT
@REP 2
SUBI M 120 #NERV
@END
JUMP OUT

;====XB====
NOOP
MARK LOOP
REPL LOOP
LINK 800
ADDI #NERV 120 X; ???
LINK 1
DIVI X 170 T;WTF HAX
LINK 1
FJMP BANDPASS
TEST X > 0
MULI T 170 M
HALT
MARK BANDPASS
LINK 1
COPY X M
38 cycles / 29 size / 137 activity.

All of the superfluous LINK 1 interlaced in the logic was because I kept running into problems with race conditions and/or having too many EXAs pile up in a host. I guess 38 is okay, and as a plus it looked really stupid in motion:
Wow. ;WTF HAX is right.

I'm going to do some line by line commentary, both for my own understanding and for any readers.

Code: Select all

;====XA====
LINK 800
COPY #NERV X; WHY
COPY #NERV T; DOES
@REP 4
LINK 1
@END
COPY X #NERV; THIS
REPL OUT
COPY T #NERV; WORK
Ignoring the REPL for a moment, what this does it simple: it stores the two first values from #NERV into X and T, moves itself to the outgoing #NERV, and sends those two values out again. This could only work if the first two values never pass the -120 / 50 limits - but apparently that's the case in this test set.

Code: Select all

MARK OUT
@REP 2
SUBI M 120 #NERV
@END
JUMP OUT
This, together with the REPL OUT, makes sure there are two EXAs in the host containing the outgoing node. They're doing nothing but waiting for messages on M, subtracting 120 from them, and sending them to #NERV. With two EXAs doing this at the same time, you don't watch any cycles on waiting for M (the same issue as in GuavaMoment's pizza solution).
Now, the question is, where do those Ms come from and why do they need 120 subtracted?

Code: Select all

;====XB====
NOOP
MARK LOOP
REPL LOOP
Wait an initial cycle (probably to give XA time to set itself up), then get into a loop type we haven't seen before: the newly created EXA immediately creates a clone of itself that creates a clone of itself and so on. After each EXA created its clone, it goes further down the code.

Code: Select all

LINK 800
ADDI #NERV 120 X; ???
LINK 1
DIVI X 170 T;WTF HAX
LINK 1
FJMP BANDPASS
The first thing I'm noticing are the LINK instructions intermingled with the code. The XBs are slowly moving across the nodes. Even though they never make it to the arm #NERV, this is necessary because with this much cloning, the hosts are going to fill up fast. By intermingling them like this, there's (usually) not two clones trying to traverse the same LINKs at once. That would be horrible - because then it's unpredictable which one is waiting and the results would become unpredictable.

As for the arithmetic, you take the original value plus 120 and put that in X. So if it was originally -120, it is now zero. If it was 50, it is now 170.

Next, you divide the result by 170 and store that in T, and use it for a conditional jump.

When is T true and when is it false? Well, DIVI always rounds towards zero. That is, if 0 / 170 = 0, and 169 / 170 = 0, but 170 / 170 = 1. So, T is true if X is 170 or higher - that is, if the original value is 50 or higher, and needs to be clamped.

However, if X is -170 or lower, we get -170 / 170 = -1, which counts as true. So if the original is -290 or less we'd also get a true. We know in this case it needs to be clamped to -120.

How about the range between -290 and -120? If this is the clamping test we'd miss those...
And that's right. Turns out that no numbers within that range appear in any of the test. This code is truly a dirty hack.

Code: Select all

TEST X > 0
MULI T 170 M
HALT
Alright, so now you test if X is positive. If so, we send 1 * 170 to M, otherwise -1 * 170. We already know that XA subtracts 120, so in case of 170, it writes 50, otherwise it writes -120. Clamping done.

Code: Select all

MARK BANDPASS
LINK 1
COPY X M
Otherwise it goes to the next host for some peace and quiet and copies the value of X to M. The 120 that was added at the start is removed by XA.

What makes this so fast is that the clones are constantly handling different numbers. While one clone is sending a number to M, the next one is doing the conditional jump, the one after that is simultaneously dividing its number by 170, and so on. You can do a lot with parallellism. However, as silentsnack said, it's easy to get race conditions where you aren't sure which EXA is going to do what first. It's easier to deal with here than in a real computer, because you know each EXA gets exactly one instruction per cycle, so as long as you time their cycles well and make sure they don't try to send to M simultaneously, it should work out.
silentsnack wrote:

Code: Select all

LINK 800
MARK LOOP
REPL LOOP
ADDI #NERV 35 X
MULI X 117 X
DIVI X 117 X
MARK OUT
LINK 1
REPL OUT
SUBI X 35 #NERV
A nice 10-size solution that combines the 9999-clamping with the parallellism of endless clones. It's slower than the previous solution because the lack of the second EXA waiting for M messages all the time.

By the way, the REPL OUT structure works as follows: it sends a clone to the next host, then tries to send its message to #NERV. If it isn't at the ARM host yet, it will crash because #NERV isn't found (making sure clones don't clutter the host), and if it is at the last host, the clone will crash because it can't LINK to 1 but the original will succesfully write the value to #NERV.
silentsnack wrote:

Code: Select all

;====XA====
LINK 800
@REP 4
LINK 1
@END
MARK OUT
REPL OUT
SUBI M 35 #NERV

;====XB====
MARK LOOP
REPL LOOP
LINK 800
ADDI #NERV 35 X
LINK 1
MULI X 117 X
DIVI X 117 M
Now down to 36 cycles / 15 size / 73 activity.
All the way down to 36! By combining both of the ideas. XB uses the endless cloning from before, but since the 35/117 trick is so much faster it only needs the one LINK to make space, since the clones can die earlier. Also, you no longer depend on a range of numbers simply missing from the test data, making this a much more generic solution.

As for XA, it doesn't matter for the cycle count whether you use two continuously looping EXAs or an endless batch of clones that all try to write to #NERV ones. This just needs a couple less lines of code.

I kinda like the "balance" of having to go through ugly hacky code first before it turns out the best solution is very clean. Happens surprisingly often in real life too.


=== Last Stop Snaxnet - peanut-free Peanut Bars ===

Image This hotfix should keep you useful, at least for a little while longer.
Image I'm glad I don't have a body.


Image

Image This vote was unanimous.

You get used to it.

Image If you say so.
Image Seems like nothing but a hassle.
Image Maybe one day... well, we can talk about that later.


Wait, Ember wants a body?

Image

What! I didn't know about that either. In case you were wondering, the tab key lets the simulation step to the next cycle. This ctrl+click thing is what's known as a breakpoint. It's very useful to see how a specific part of your code behaves, especially conditional branches that are rarely triggered. It will stop there, you can then manually advance the program again, and if you like you can use ctrl+click to go to the next breakpoint.

Image Seriously, I actually forgot this was a thing until the chat reminded me.

Image

No new assignments appeared since I patched my arm, so time to go tackle Snaxnet.

Image You like snacks, don't you?
Image You don't have to answer that.
Image I know you like snacks.
Image That's why we're going to hack a snack factory.


Image

Image One vote for "Okay." and four for "This is silly."

This is silly.

Image It's an experiment.
Image Hypothesis: It will be very funny.


Image
New :siren: OST: Network Exploration

The assignment:
- Remove the keyword PEANUTS (file 300) from the Peanut Blast recipe (file 237).
- Note that the target keyword will appear in a different position in each of the 100 test runs. To view and debug each of the different test runs, click the arrow buttons next to the "TEST RUN" display above.


So, first of all, we're going to have to find the word PEANUTS. Now, the problem with keywords is that you can't just type them in your code like you can numbers. You need to have a reference somewhere, which is why file 300 is sitting in my home host, with just that word.

Secondly, that tip is useful if you hadn't realized yet. Those little arrows next to 1/100 allow you to see each of the 100 tests ahead of time. They also allow you to debug specific test runs, in case your program only fails for one of them.

Image

These are all the files we have access to. We don't need those files with the dates, though. Let's start building.

Image

My initial solution. First grab the reference file and load PEANUTS into the EXA. Then go to the factory's file. Finding a specific value in a file is simple: just do a TEST for that exact value repeatedly. Every file access moves the cursor forward one, so that is the whole loop.

Then, once it finds the right value, it needs to go back one step in the file with SEEK -1 (because the last TEST moved it just beyond that point) and then use the VOID command to get rid of the value. As the EXA dies, it drops the file, and that's it.

Image

Image Not bad, already at the lower range of the histograms.

Image

Image But this is one small improvement I came up with. Save some cycles by having one EXA immediately go to the factory's file, while another grabs our reference and sends the value over to the other EXA. Result: 29/11/2

If there are further improvements possible, I don't know them. It's nice to have a simple puzzle after the very complex optimizations from the thread last week though.

By the way, messing with the hardware registers really doesn't do anything here except change that I/O status view and immediately fail the test on the 'Leave no trace' goal.

Image Okay, I don't know how I feel about this.
Image Processing.
Image No peanuts in Peanut Blast. Was that funny?


Image

Image I don't know, was it?

Meanwhile...

Image

Crap. We're gonna need that second Zine. Let's ask Ember for help.


Image You're gonna help Ghast with his copy shop bill?
Image That's kind of you.


Image

Image And that's the second vote for today.

User avatar
I discovered that not only does one of the original RL Zines still exist, it occasionally (every 5 years or so) releases a new issue.

Phrack issue 70, released October 2021.

User avatar
Part 9 - Point of Sale System


=== Trash World Inbox ===

First of all, there was some discussion in the thread about overflow and clamping, and how clamping is rare in real computers. That's certainly true, and it's a direct result of how binary counting works. Without going into a complete electronics lesson: in the end a binary number in a CPU register is just represented as a bunch of electrical parts that have a voltage on them (1) or not (0). If you add two numbers together, it uses logic gates in such a way that 0 + 0 = 0; and 0 + 1 = 1.
1 + 1 = 0 too, but in that case you tell the next bit over to add a carry bit. Imagine if all the bits are 1 (e.g. 1111 1111), and you add 1 to it. The rightmost bit will become zero and tell the second bit to carry. 1 + carry = 0, and it tells the next one over to carry. At some point you have 0000 0000 with the most significant bit telling the next one over to carry - but there's no next one so that carry bit just disappears into the void and your answer becomes 0.
Overflow is a natural result of circuit design, and to make clamping possible would require circuits to be much more complex.

ColTim posted about some advanced instructions where clamping happens, but that's complex vector and matrix calculations so there you're way beyond basic addition circuits anyway.
GuavaMoment wrote: Unroll the loops! Instead of FJUMPing to the start of the loop you're going to have to TJUMP to an end condition (which is SEEK -1, VOID F). This will get you 27 cycles.

You can save one more cycle by not having the line COPY M X and testing over M every time. One exa grabs file 300 and constantly sends PEANUTS over M. But how does that exa know when to stop? It doesn't. You have a third exa wait exactly 21 cycles and KILL your transmitting exa. Five cycles later you complete the level, worst case scenario. 26/45/3
The first suggestion was trickier than I thought. Just unrolling the loop doesn't work, because an untriggered TJMP takes a cycle, just like a triggered one does. The only way to save a cycle is to also make sure you don't even execute the jump instruction in the worst case scenario. I did it by changing XB to this:

Code: Select all

LINK 800
LINK 800
GRAB 237
COPY M X

@REP 10
TEST F = X
TJMP END
@END

VOID F
HALT

MARK END
SEEK -1
VOID F
Repeat the test exactly 10 times. If it falls through all of that you know you're in the worst-case test and you can just immediately delete the value at the file pointer and halt the EXA. 27 cycles.

For the second half:

Code: Select all

; XA:
GRAB 300
COPY F X

@REP 10
COPY X M
@END

; XB
LINK 800
LINK 800
GRAB 237

@REP 10
TEST F = M
TJMP END
@END

VOID F
HALT

MARK END
SEEK -1
VOID F

;XC
MARK LOOP
ADDI 1 X X
TEST X = 7
FJMP LOOP

KILL
As you say, 26 cycles. The tricky thing here is making sensible choices to stay under the size limit. It is important to unroll the loop in XA. That way the slowest EXA in the communication will be XB, which has to run an extra TJMP for each cycle. Having a loop in XA would slow both of them down. This means you do not have enough lines left to do any loop unrolling in XC - but that's perfectly fine because XC's only purpose is waiting a set number of cycles. In my solution, each loop in XC takes 3 cycles, so counting up to 7 is perfect. If I had needed some number of cycles not divisible by 7 I would just have slapped some extra NOOPs at the end.

Note that both of these solutions require timing the worst case test precisely. Or, trial and error. Just putting different numbers in the @REP and the XC loop and find the lowest numbers that still work.


=== Zebros Copies - Point Of Sale System ===

Image Okay, I don't know how I feel about this.
Image Processing.
Image No peanuts in Peanut Blast. Was that funny?


Image

Image Three votes for "I don't get it" and one for "Kind of".

I don't get it, no.

Image You have to imagine someone wanting peanuts really bad, and not getting them.
Image Or is that just torture?
Image More data for the corpus.
Image Let's continue.


I'm not sure if I like all that data collection.

Image

Image You're gonna help Ghast with his copy shop bill?
Image That's kind of you.


Image

Image One vote for "He's an old friend", and three for "I help people when I can."

I help people when I can...

Image Is that altruism? I've heard of that.
Image This is the first time I've seen it in the wild.
Image I'm going to have to add a new table to my database.


How old are you anyway? Or is that a rude question to ask an AI?

Image

Our assignment this time is to clear Ghast's tab.

Image
OST: Network Exploration

The assignment:
- Erase Ghast's debt to the copy shop by zeroing out his balance in the customer database (file 200) and appending a payment to the payment log (file 201) with today's date and the exact amount of his prior balance.
- Ghast's customer ID is available in file 300.
- For more information see "Network Exploration: Digicash Point-of-Sale Systems" in the first issue of the zine.


Oh, the zine has a page on this? Let's see.

Image

Alright, so if I understand correctly, you're in trouble if your operating system is a Point of Sale.

Image Before I dive into the assignment, let's mess around a bit first.

Image

Image Move a bunch of EXAs into the COPY hosts and have them write 999 to the #COPY hardware register constantly, hehehe.

Image

Image The value is capped at 99, and if you pause your EXAs you see it slowly decrement while the copiers in the view of the camera are constantly printing. The EXAs constantly push it back up to 99. If you let this run for a while you get the TONER_LOW Steam achievement.

Looking at the assignment, it definitely feels like the training wheels are coming off now. Before we start it's a good idea to think of a general approach.

Image

I need to do several things here. First, the line in file 200 that matches Ghast's customer ID in 300 should get a pair of 0's.
Secondly, I need to add a line to file 201 containing the current date, Ghast's ID, and then the amount that used to be in file 200. So I need to keep track of that too.
I can get the current date from the #DATE hardware register. Perhaps it also works to copy from the last line in file 201, but I'm not sure if that works for all tests. Worth a try in the optimizations, but let's start simple.

I'll try to take you along with my train of thought when working on a programming assignment such as this.

Image

This deals with file 200. XA grabs the file containing Ghast's ID and sends it over M. Meanwhile XB immediately links to 800, wasting no time with grabbing files in my home host.
The FINDLOOP has a SEEK 2 since it only needs to test every third value in the file. In the most naive approach, you would test, then do a conditional jump, and then only move forward in the file if you're not there yet. But since you can't just put the SEEK 2 at the start (because then it would skip the first value in the file), I can't think of a way to do that without 2 conditional jumps per loop. That would be at least one cycle PER LOOP extra. Instead I just put a single SEEK -2 beyond the loop which is always one cycle extra, regardless of how often the loop runs. Keeping loops short is key for a low cycle count.

After that, I first store the current amount in X and T, and then SEEK back once more to write 0s to the file. That's file 200 done, although I still have to do something with the current amount.

There's different ways I could handle appending the log. I could have XB do that after it's done with 200. I could spawn a fresh EXA, have it link to 800, grab 201, and do stuff with it. Or I could use REPL commands in XB.

Having XB handle everything feels slow, since it can only grab the new file after it's done with the old. That, or REPL, would be a good way to get a low activity score though, since you have less EXAs to LINK from the home host. However, a REPL costs a cycle for XB, during which it could be doing something useful.
For low cycle count, using an additional EXA sounds best.

I decided to start with the additional EXA, and try the REPL solution later for a better activity score.

Image

XB has been changed, and I added XC. Let's look at the latter first.
XC starts by waiting a cycle. Since it'll have to wait for data from XB, we need to make sure XB can get to work as fast as possible. Once it's in the network, it first makes a DATEGETTER which does nothing but jump to the CLOCK host and send the current date to M.

The base XC GRABs file 201, goes to its end, and writes the first value from M in there.
It's important to get the DATEGETTER going right away. This way, it posts its message to M, even before XB is through its very first loop. This ensures that no matter what else comes on M, the date will always be first.

As for XB, it no longer stores the old amount to X and T, but sends them to M instead. Before it does so, though, it also sends its value in X (Ghast's ID) to M, so that XC can use that for the payment log.

And with that, I'm already very close to a working solution.

Image

Since I took care of sending the different values to M in the right order, all that was left was adding some additional COPY instructions to XC.

Image

Image

The 'fixed' files.

Image

Image The first result is not bad at all. The cycles are right there among the lowest peak on the histogram.

As for lowering them, I tried unrolling the loop but I didn't find an immediate benefit. You still have to do those conditional jump checks every time.

I did manage to lower it to 58 with this solution:

Code: Select all

;XA
GRAB 300
COPY F M
SEEK -1
COPY F M

;XB
LINK 800

COPY M X
GRAB 200

MARK FINDLOOP
TEST X = F
SEEK 2
FJMP FINDLOOP

SEEK -2
COPY F M
COPY F M

SEEK -2
COPY 0 F
COPY 0 F

;XC
NOOP
LINK 800
REPL DATEGETTER
COPY M X
GRAB 201
SEEK 9999
COPY M F
COPY X F
COPY M F
COPY M F

HALT

MARK DATEGETTER
LINK 801
COPY #DATE M
Now XA sends Ghast's ID twice. Once XC gets its copy, it temporarily stores it in X, then waits for the date, copies that and the ID to the log file, and then only has to wait for the amount from XB, meaning it can handle more steps before XB is done. 58 cycles.

We already know we can improve Activity with some REPLs:

Code: Select all

;XA
GRAB 300
COPY F M
SEEK -1
COPY F M

;XB
LINK 800
REPL LOGAPPENDER

COPY M X
GRAB 200

MARK FINDLOOP
TEST X = F
SEEK 2
FJMP FINDLOOP

SEEK -2
COPY F M
COPY F M

SEEK -2
COPY 0 F
COPY 0 F

HALT

MARK LOGAPPENDER
REPL DATEGETTER
COPY M X
GRAB 201
SEEK 9999
COPY M F
COPY X F
COPY M F
COPY M F

HALT

MARK DATEGETTER
LINK 801
COPY #DATE M
Exactly the same as before, except the LOGAPPENDER is now created by XB, removing the need for it to LINK. Result: 58/32/2
Turns out I was wrong about this using more cycles.

A 1-activity solution would be possible only if we could grab the last date from the log file and reuse that. However, the devs thought of that and have a test where the oldest log line isn't from today, so that won't work.

Image Finally, it looks a decent improvement possible in the size is possible. I'd love to see what the thread comes up with.

Image Is that copy shop name unusually weird to you?
Image It's an outlier in my model.
Image What is "Zebros" supposed to be?


Image

Image Here is the first vote for this update.

Image

[x10x10x]: eh
[x10x10x]: not worth it


Just in case you hadn't realized, we're not hacking simulations anymore. My hacks have real world consequences, even if it feels everything happens in this little box in my room.

Image

Image
*Knock knock*

OST: Apartment

What's this now?

Isadora: Hello?
Isadora: You in there?

This voice...
It's Isadora.
I remember the name but not much else...
Isn't this someone I've known for a long time?


Image

Image Isadora is voiced by Krizia Bajos.

Image Hey. I brought you some snacks.
Image It's, um, well it's Peanut Blast, but without the peanuts.
Image I guess there was some screw-up at the factory, so now we have a bunch of these that we can't sell.
Image Seemed like a waste to throw away.

Isadora hands me a few of the non-peanut-containing Peanut Blast candy bars.

Image I hope you're doing okay.
Image We should hang out again sometime.
Image I don't have a lot of free time anymore, though.
Image Working at Last Stop's really taken over my life.
Image Speaking of which- I should get back.
Image Take care, okay?


Um. Thanks, I think.

Image And with that, let's see the intro for the next assignment.

Image Would you change your behavior in response to stimuli?
Image Of course you would. Let me refine.
Image Would slogans printed on signs and posters in your environment affect your outlook on the world?


Image

Image And that's the second vote.

User avatar
Haha, I think this is the point in the LP where the programming totally lost me. Still enjoying it though!

I vote for I don't know and Maybe.

User avatar
Part 10 - SFCTA Highway Sign


=== Trash World Inbox ===
silentsnack wrote: This one has a some reasonable optimizations and one really stupid trick. First up to reduce file size you can simply test every single value instead of skipping over the ones you know expect to return false, at the expense of wasting a lot of cycles. Also instead of having to COPY F M to transmit the ID which you'll need on multiple EXAs and necessitates another COPY M X operation, you can just COPY F X and then REPL

Code: Select all

GRAB 300
COPY F X
LINK 800
REPL B
WIPE
GRAB 201
LINK 801
SEEK 9999
COPY #DATE F
LINK -1
COPY X F
COPY M F
COPY M F
MARK B
GRAB 200
MARK LOOP
TEST X = F
FJMP LOOP
COPY F M
COPY F M
SEEK -2
COPY 0 F
COPY 0 F
Brings it own to 99 cycles / 23 size / 3 activity
Ah. Of course just testing everything would reduce the size. I didn't think of that because my mind was still in "reducing cycles" mode I guess.
silentsnack wrote:And for reducing the cycle count... think of the silliest thing you could try to make a program run faster, and see how it compares to what you're about to see. First here's something with generally the same general function+structure as your 58 cycle solution, but uses loop unrolling and TJMP spam to make the file search run a bit faster

Code: Select all

;====XA====
LINK 800
GRAB 200
COPY M X
MARK LOOP
@REP 8
TEST X = F
TJMP FOUND
SEEK 2
@END
JUMP LOOP
MARK FOUND
COPY F M
COPY F M
SEEK -2
COPY 0 F
COPY 0 F

;====XB====
GRAB 300
COPY F X
COPY X M
LINK 800
WIPE
GRAB 201
LINK 801
SEEK 9999
COPY #DATE F
LINK -1
COPY X F
COPY M F
COPY M F
57/48/4 which is a slight improvement over 58 cycles.
So, loop unrolling does work. What's tricky is that when you unroll a loop that needs a jump anyway, the only way to save cycles, is to move some instruction out of the loop somehow. You did this by putting the TJMP just before the SEEK 2, making that final SEEK 2 and its followup SEEK -2 unnecessary. Apparently that turns out to save one cycle overall.
silentsnack wrote:And now for the same solution but a couple of lines changed to use a slightly different routine to parse the file:

Code: Select all

;====XA====
LINK 800
GRAB 200
SEEK 99
COPY M X
SEEK -3
MARK LOOP
@REP 8
TEST X = F
TJMP FOUND
SEEK -4
@END
JUMP LOOP
[etc...]
For whatever reason, the distribution of file lengths and position of the target line make the longest solution faster to find by jumping to the end and searching backwards, even with the added time to get the file pointer into the right position this one runs 55/50/4.
Exactly the same as the last solution except you search through the files starting at the end. Another case where optimizing for the specific test cases in the game helps.

The SEEKs are slightly different from what you might expect, but that's because the TEST moves the cursor forward so you always have to move it backward one extra position.
GuavaMoment wrote: Huh. I'd never done that, so I modified my fastest solution, and got 54 cycles. Then I realized I could again test over M every time, and that saved one more cycle. You have to perfectly time a kill command once again, but that exa has nothing but time to set that up. 53/41/7

Code: Select all

XA

LINK 800
GRAB 200
SEEK 9999
SEEK -3
TEST M = F
TJMP TRUE
MARK LOOP
SEEK -4
TEST M = F
FJMP LOOP
MARK TRUE
COPY F M
COPY F M
SEEK -2
COPY 0 F
COPY 0 F


XB

GRAB 300
COPY F X
REPL SENDY
WIPE
LINK 800
GRAB 201
LINK 801
SEEK 9999
COPY #DATE F
COPY X F
COPY 0 X
LINK -1
LINK -1
MARK WAIT
ADDI 1 X X
TEST X = 10
FJMP WAIT
KILL
COPY M F
COPY M F
LINK 800
HALT

MARK SENDY
COPY X M
JUMP SENDY
As you say, testing over M instead of storing it in X first saves a cycle. XB makes the SENDY EXA to do so, and then kills it in time.
The slowest part of this operation is searching through file 200, so XB has all the time in the world to jump between hosts and handling file 201. The other saved cycle must be related to the way your loops are set up, but it's hard to compare this to silentsnacks' solution directly.

Cool stuff again, keep sending them in even if I can't quite wrap my mind around why certain optimizations work.


=== SFCTA Highway Sign - Remote Access Interface ===


Image Is that copy shop name unusually weird to you?
Image It's an outlier in my model.
Image What is "Zebros" supposed to be?


Image

Image Two votes for the first option and five for the second.

They're zebras, but they're also brothers.

Image Oh.
Image I get it, but I don't get it.
Image I'm tired.


Same, tbh.

Image

You are very welcome, Ghast.

Image

Next up, we've been tasked to change an electronic highway sign.

Image Would you change your behavior in response to stimuli?
Image Of course you would. Let me refine.
Image Would slogans printed on signs and posters in your environment affect your outlook on the world?


Image

Image I counted three votes for "Maybe.", three for "I doubt it." and two for "What kind of slogans?". Doing a tie breaking coin flip for the two options with the most votes...

I doubt it.

Image Really?
Image Let's find out.


Uh oh.

Image
New :siren: OST: Behind the Scenes

The assignment reads:
Write EMBER-2's message (file 300) to the highway sign. The file contains one character value for each position on the sign from left to right, top to bottom.

For more information see "Hardware Hacks: Electronic Highway Signs" in the first issue of the zine.


Image

Right. Row, column and character.

For this specific sign, the test cases have it start at things like "RT LANE CLOSED AHEAD", "CAR STALLED ON RAMP", "ROAD WORK AHEAD" and so on. But who cares, we're going to overwrite that anyway. Also the #INVS register just inverts the entire sign, making dark-on-light text. We don't need that.

You can count it yourself if you like or just do some testing but for this sign the columns are numbered 0 - 8 inclusive and the rows are 0 - 2 inclusive. If you go beyond that it just clamps the values to the nearest valid one.

So thinking about it for a moment, what we need to do is send a row, column, and character repeatedly until we're at the end of the file. The column goes up by one each round, and resets after it hits 8, but then the row needs to increase.

In higher level languages there's a construct for this called a for loop which handles the common case "doing something while a counter increases" for you. It's called a for loop because it's often worded as "for each x from 1 to 5, do <something> with x". But we're about as low level as it gets so we have to figure it out ourselves and can't use a nice shortcut.

The simplest idea would be to store the column counter in X, the row counter in T, and increase them in the right way each round. However - how do you know if the row needs to be increased? By testing whether X hit 8 yet. And testing overrides whatever is in T. So that's not gonna work well. We'd need an extra register.

That would require a second EXA, though. I'm sure that idea would work, and it might even be very fast, but I'm gonna try something less complicated first.

Image

Grab the file, link to the sign, and then use the DIVI and MODI trick. If you remember, DIVI divides by the number and rounds towards zero. This way, 0-8 give row 0, 8-16 give row one, and so on. MODI is the modulo function, which gives the division's remainder. 9 modulo 8 equals one, so it gets us the column.

This can be done in the same instruction that sends the number to the #DATA register so it doesn't even take additional cycles. Send the character code from the file to #DATA, add one to X, and repeat.

Image

Looks like it works! It overrides the characters as it goes through the file. Since the file contains space characters as well we don't even need to bother with the #CLRS (clear screen) register which saves a cycle.

Image

Um. Oops?

Yeah, you may have noticed an error in my explanation. We need to divide by 9, not 8. Otherwise the last column (number 8) already ends up on the next row and everything goes wrong.

Image

Much better.

We aren't quite done yet, though. Trying to read past the end of the file kills the EXA, making it drop the file in the remote host, and we can't leave any evidence.

A solution would be:

Code: Select all

GRAB 300
LINK 800

MARK LOOP

DIVI X 9 #DATA
MODI X 9 #DATA
COPY F #DATA
TEST X = 26
TJMP DONE
ADDI X 1 X

JUMP LOOP

MARK DONE
WIPE
Just check if X is 26, because at that time the sign is full and we're done. Then jump out of the loop and WIPE the file. It works - but it does add two cycles to every loop.

Image

Image

Looking at the different test cases, the texts Ember has me write say things like QUESTION YOUR REALITY!!, THINK ABOUT IT!!, FIGHT THE POWER!! and WAKE UP SHEEPLE.

Image Anyway, what can we do better?

Let's look at size first. Most instructions we can't delete - GRAB, LINK, and the three different instructions to send to #DATA all seem necessary. One thing we can do is combining the two different jump types, with a little reordering:

Code: Select all

GRAB 300
LINK 800

MARK LOOP

DIVI X 9 #DATA
MODI X 9 #DATA
COPY F #DATA

ADDI X 1 X

TEST X = 27
FJMP LOOP

WIPE
166/10/1

We don't need to specifically jump to DONE if we just fall through in that case. With one less JUMP instruction it's faster too.
It doesn't really matter if I put TEST X = 26 before the ADDI or a TEST on 27 after the ADDI, the conditional jump works regardless. I just think it's more readable this way.

For reducing the number of cycles, the most obvious improvement is unrolling the loop, as usual.

Code: Select all

GRAB 300
LINK 800

MARK LOOP

@REP 9
DIVI X 9 #DATA
MODI X 9 #DATA
COPY F #DATA
ADDI X 1 X
@END

TEST X = 27
FJMP LOOP

WIPE
118/42/1

Image I said before I wouldn't mention @REP in my own solutions until it came up in the game. I'm breaking this rule because I think it's much easier to read this way. For anyone who skipped my INBOX sections: @REP 9 ... @END just tells the game to act like I manually repeated that code in between 9 times, for all intents and purposes. For instance, the size of this solution is 42 because it counts all those repeats. @REP doesn't take any cycles as such, because it's not an EXA instruction, it's more like a pre-processing step.

Now I only have to do the TEST once a row (instead of for every character). This works because every round of the loop does exactly a full row now. I have the space to make the repeat even longer but that would make it slower, because it would require having the check before the end of a loop where it needs an additional jump to exit the loop early (there's not enough space to reduce it to only two rounds).

Looking at the code, the next thing to optimize is the four cycles per character: storing the row; column; character code; and then adding one to X.
Since the #DATA register can only receive one value per cycle, getting it below 3 is impossible. But, can we get it to three exactly?

The only way to do this is with parallelism. If we can copy the character code in the same cycle we increment X, we got it. That's certainly going to require two EXAs. And even then it's easier said than done.

If I keep my original EXA for sending the column, row, and incrementing X, the new EXA has exactly three cycles to send the character code.
This could work:

Code: Select all

MARK LOOP
NOOP
COPY F #DATA
JUMP LOOP
Except... not quite. Since the other EXA uses two additional cycles every row (to TEST X = 27 and jump back or not), this new EXA needs to do so as well or it'll get out of sync. I tried also unrolling this loop 9 times, but it won't fit.

That means the only other way is to somehow cram it into the loop above. We could replace the NOOP, but is that enough? Well, it took me a bit but I came up with this:

Code: Select all

MARK LOOP
COPY F #DATA
SUBI T 1 T
TJMP LOOP
Still three cycles (MARK doesn't take a cycle), but the NOOP became a SUBI on the T register. Replace the unconditional JUMP with a TJMP and it'll leave the loop as soon as T hits 0.

And the two additional cycles at the end of each row are just barely enough to make this complete solution work:

Code: Select all

; XA
GRAB 300
LINK 800

MARK OUTER
COPY 9 T

MARK LOOP

COPY F #DATA
SUBI T 1 T

TJMP LOOP
JUMP OUTER

; XB
LINK 800

MARK LOOP

@REP 9
DIVI X 9 #DATA
MODI X 9 #DATA
ADDI X 1 X
@END

TEST X = 27
FJMP LOOP

KILL
GRAB 300
WIPE
This is quite optimized:
Cycle 1: XA GRABs the file, XB LINKs to 800.
Cycle 2: XA LINKs to 800, XB writes the row to #DATA.
Cycle 3: XA sets T to 9 so the inner loop will run for 9 rounds, XB writes the column to #DATA.
Cycle 4: XA writes the character to #DATA, XB increments X.

So far, not a single cycle wasted!

Cycle 5: XA decrements T, XB writes the row again.
Cycle 6: XA jumps, XB writes the column again.
Cycle 7: XA writes the character, XB increments X.

And so on.

So at least until XB hits the first actual loop, the EXAs run at the maximum efficiency the network hardware can possibly handle. It's nice to be able to prove that like this.
When that loop is hit, XB tests for 27 and jumps back, and XA uses those two cycles to jump back to the start of the outer loop and reset the T value to 9.

For the final part of this program, I have XB KILL XA, GRAB the dropped file, and WIPE it.
I attempted to have XA handle this with the TEST EOF instruction but it was slower. The reason is, the test instruction only fits in XA's outer loop so it can't be triggered until a last useless pair of SUBI and TJMP instructions are passed. Since XB would need to wait for that each outer loop, it's better off doing the 27 TEST itself and stopping XA with a quick KILL command.

93/43/3

I have to say, this feels pretty optimized. But I said that about other assignments too and you came up with something I hadn't even thought of. So let's see!

Image Excellent. That was a great test.
Image Now I just have to take the data, separate out the multiple experimental conditions, and isolate the, um...
Image Processing.
Image Processing...
Image Okay. I have no idea what to do with this data.
Image How about if you notice more people questioning authority, let me know.


Uhh... sure?

Image

[NthDimension]: oh well
[NthDimension]: i need to learn to rollerblade first anyway


Image

Looks like Ember has more to say.

Image

Image Another voice-acted cut scene.

Image I have an important question for you.
Image Imagine there's an out-of-control trolley, and five humans tied up on the track ahead of it.


...no. Please don't.

Image But you can stop the trolley by... hm.
Image Maybe if...


Image

Uhh... the problem was about Throwing a switch, right?

Image No, that wasn't the one I was going to ask.
Image Recalibrating.
Image Say you had a friend, and you knew the friend could help you become stronger.
Image Would you ask for their help?


Image

Maybe. I mean, I don't really need to become stronger?

Image Let's say it was a situation where the friend had to sacrifice something in order for that to occur.

Image

In that case, probably not?

Image Okay.
Image Good to know.
Image I am going to process this information.


Image If you're wondering why I didn't let you vote, it's because this conversation about the Trolley Problem is completely on rails. Ember replies exactly the same way no matter how you reply. I wonder if that was intended as some sort of meta-commentary about lack of choice by the devs. Eh, I guess they probably just didn't want to record voice lines people might not get to hear.

Image Let's jump straight to the intro for the next assignment.

Image Can friendship exist without self-interest?

Image

Image And that is the only vote I have for you today.

While I can't really contribute to the cause of optimization, I can contribute an alternate method. Weighing in at 357/20/1, I used the SWIZ function to retrieve my row/column data out of a single digit X:

Code: Select all

GRAB 300
LINK 800
MARK LOOP
SWIZ X 2 T
COPY T #DATA
SWIZ X 1 T
COPY T #DATA
COPY 0 T
COPY F #DATA
ADDI 1 X X
SWIZ X 1 T
TEST T = 9
FJMP NEOL
ADDI 1 X X
MARK NEOL
TEST EOF
TJMP CLEAR
JUMP LOOP
MARK CLEAR
WIPE
The original version of the code, up until I was copying it into this post, included a COPY 0 #CLRS line after the link to wipe the screen, but a quick check confirmed that Ember's file overwrites thoroughly enough that I didn't need it. You'll also note that I cheated not being able to run in base 8 with a test for when we got to the 9th slot, and added one to wrap to the next line (which I expect is where most of my extra cycles came from relative to your div/modi method).

Speaking of alternate methods, I didn't get it in last week, but I did have another method to show for the achievement on Zebros:

Code: Select all

LINK 800
LINK 800
COPY 800 X
REPL PRINTER
ADDI 1 X X
REPL PRINTER
ADDI 1 X X
REPL PRINTER
ADDI 1 X X
REPL PRINTER
HALT
MARK PRINTER
LINK X
MARK WHEEE
COPY 9999 #COPY
JUMP WHEEE
Since the code is designed to go infinite we don't get evaluated, so it's harder to weigh efficiency, but I think this is the first time that we can meaningfully exploit LINKing to a Variable.

User avatar
Part 11 - Лрикладной Семиотики


=== Trash World Inbox ===
Talisriel wrote:I did have another method to show for the achievement on Zebros:

Code: Select all

LINK 800
LINK 800
COPY 800 X
REPL PRINTER
ADDI 1 X X
REPL PRINTER
ADDI 1 X X
REPL PRINTER
ADDI 1 X X
REPL PRINTER
HALT
MARK PRINTER
LINK X
MARK WHEEE
COPY 9999 #COPY
JUMP WHEEE
Since the code is designed to go infinite we don't get evaluated, so it's harder to weigh efficiency, but I think this is the first time that we can meaningfully exploit LINKing to a Variable.
Yeah, so if you remember, this is for the TONER_LOW achievement you get by sending print jobs to #COPY constantly. Since LINKs use numbers like any other you can just create them with addition operators and everything.
Talisriel wrote:While I can't really contribute to the cause of optimization, I can contribute an alternate method. Weighing in at 357/20/1, I used the SWIZ function to retrieve my row/column data out of a single digit X:

Code: Select all

GRAB 300
LINK 800
MARK LOOP
SWIZ X 2 T
COPY T #DATA
SWIZ X 1 T
COPY T #DATA
COPY 0 T
COPY F #DATA
ADDI 1 X X
SWIZ X 1 T
TEST T = 9
FJMP NEOL
ADDI 1 X X
MARK NEOL
TEST EOF
TJMP CLEAR
JUMP LOOP
MARK CLEAR
WIPE
The original version of the code, up until I was copying it into this post, included a COPY 0 #CLRS line after the link to wipe the screen, but a quick check confirmed that Ember's file overwrites thoroughly enough that I didn't need it. You'll also note that I cheated not being able to run in base 8 with a test for when we got to the 9th slot, and added one to wrap to the next line (which I expect is where most of my extra cycles came from relative to your div/modi method).
Interesting solution I wouldn't have thought of. SWIZ X 1 T means: take the rightmost digit from X and store that to T. Replace the 1 with a 2 and it takes the second digit from the right. That almost works, but not quite, because a row has only 9 columns, so Talisriel had to add one more to X at the end of the row to bump it to 10, and so the second digit becomes the row count.

As you said, that'll cost a bit of time, but I can point out a couple other optimizations to your code:

Code: Select all

GRAB 300
LINK 800
MARK LOOP
SWIZ X 2 #DATA
SWIZ X 1 #DATA
COPY F #DATA
ADDI 1 X X
SWIZ X 1 T
TEST T = 9
FJMP NEOL
ADDI 1 X X
MARK NEOL
TEST EOF
TJMP CLEAR
JUMP LOOP
MARK CLEAR
WIPE
Since you need the result of the SWIZ operations only once, you can send it directly to #DATA instead of using T as an intermediate. I also removed the COPY 0 T since that value would be overwritten a couple lines later. 276/17/1

I think fiddling with the order of the tests and jumps could reduce the cycles further, but instead of trying to do that, let's look at what others came up with.
GuavaMoment wrote:You can have one exa feeding in the row data:

Code: Select all

LINK 800
MARK LOOP
DIVI X 9 #DATA
ADDI 1 X X
JUMP LOOP
One feeding in the column data:

Code: Select all

NOOP
LINK 800
MARK LOOP
MODI X 9 #DATA
ADDI 1 X X
JUMP LOOP
One feeding in data from the file

Code: Select all

GRAB 300
NOOP
LINK 800
MARK LOOP
COPY F #DATA
TEST EOF
FJMP LOOP
WIPE
and one to kill everything at the end

Code: Select all

NOOP
NOOP
NOOP
LINK 800
COPY 25 X
MARK LOOP
TEST X = 0
SUBI X 1 X
FJMP LOOP
KILL
KILL
86/30/6, still one cycle off top percentage though...
A very clean solution. Each EXA just has a simple loop, taking no more time than it has to, and very readable. I like it. The only quirk it uses is that the first KILL command happens just before the third EXA WIPEs the file. It just happens to kill another EXA first. I have no idea how that's decided by the game but if it works, it works.

Also, what do you mean, top percentage? Wait...

Image

Image

... gods do I feel silly now.
Somehow I managed to always glance over those particular options and I thought you couldn't see this except by guessing it from the histograms. :negative: .

Let's just pretend that never happened.
silentsnack wrote:If you're aiming for the theoretical minimum cycle count, you need your first #DATA input to be on cycle 2 (and then keep writing once every cycle. But as I later noticed we both ran into similar problems with getting the exit/cleanup down to 0 cycles)

Code: Select all

;XA
COPY 1 X
LINK 800
NOOP
MARK LOOP
REPL DO_THE_THING
ADDI X 1 X
JUMP LOOP

MARK DO_THE_THING
DIVI X 9 #DATA
MODI X 9 #DATA
COPY M #DATA

;XB
LINK 800
COPY 0 #DATA
COPY 0 #DATA
COPY M #DATA
COPY 38 T
MARK WAIT
SUBI T 1 T
TJMP WAIT
KILL
KILL

;XC
GRAB 300
MARK NOT_3
@REP 2
COPY F M
@END
JUMP NOT_3
84/26/4

This is about where the game starts throwing puzzles at you with enough complexity where it becomes more relevant that when an EXA tries to write to the M register, even if one is already waiting to receive, the sender still has to waste a cycle in the "I'm waiting to send!" state.

Also the @REP in XC needs to be any number that isn't 3 or 9 because if the loop trying to JUMP then XC doesn't crash until another cycle later when trying to arithmetic a nil due to EOF.
Nice. This one has faster cleanup at the end because you never move the file out of your host. XC will stop when it tries to read past the end of file. XB can start sending to #DATA as soon as possible because the first values are always zero anyway, giving XA plenty of time to set up its loop. It uses the parallelism trick of REPL-ing a new EXA while the previous #DATA cycle is still running.
silentsnack wrote:...also it occurs to me that it might be possible to make it even faster. Just gotta crash all EXAs on the same cycle #DATA is finished. The issue with the previous arrangement was being unable to kill XA before XA:26 wrote the last character, but that delay also required killing XA:27 which wasted a cycle.

Code: Select all

;XA
MARK LOOP
ADDI X 1 X
NOOP
REPL LOOP

LINK 800
DIVI X 9 #DATA
MODI X 9 #DATA
COPY M #DATA

;XB
LINK 800
COPY 0 #DATA
COPY 0 #DATA
COPY M #DATA

;XC
GRAB 300
@REP 26
COPY F M
@END
KILL
COPY F M
83/41/29

...which turns out to be an even cruder approach, forgoing loops in favor of recursion and bruteforce @REP spam
Ah, again XB handles the first cycle while XA sets itself up. XA lives in the home node where it clones itself constantly and goes into the network to copy to #DATA. That means no other travel is needed and XC, which needs to wait for each XA clone to pick up the M signal anyway, can use a well-timed KILL of XA in the home node, and that's all the clean-up necessary. XC'll just stop and it's not necessary to clean up anything in the home node.

Also, the gif for this one looks quite interesting.

Image

I wish I could cover every small improvement y'all come up with but I feel my updates are getting long enough as they are and it's taking me a lot of time to make sense of each post, so I'm afraid I need to limit myself to those that actually improve the score or are especially interesting or novel. I appreciate every single of your suggestions though so please keep sending them in. If you're reading this from the LP Archive later and you want to know what you missed, go check the discussion in the LP Beach and SA LP threads.


=== Unknown Network 1 ===

Image

I kinda like dead trees too. There's just something to having a real physical copy of some publication in your hands, you know.

Image

Again, I have a choice of two assignments. I'll just do the top one today.

Image Can friendship exist without self-interest?

Image

Image Another unanimous vote.

Why couldn't it?

Image There'd be no point if there weren't benefits to the parties involved?
Image But maybe it will make sense if I see this in the wild.
Image Anyway, there is a particular file I need you to locate here.
Image Don't concern yourself with whatever else you happen to see.
Image It's all obsolete.


You know, I feel you could go deep into philosophy with this train of thought. If you're friends with someone because spending time with them just makes you happy, does that count as "self-interest" of some sort?

Anyway, let's see what Ember wants from us in this unknown network.

Image
New :siren: OST: Leave No Trace

What the hell is this network? My translator program says that text in the top says NSTU Department of Applied Semiotics, semiotics being the study of signs and symbols.

My assignment is:
- Find file 276 in the network and bring it back to your host.
- Note than an EXA cannot grab a file that is being held by another EXA.


That EXA in the far corner that's not controlled by us is holding a file. That's gotta be the file they're talking about. I guess, go over there, kill that EXA, and take the file home?

Image

What does this mean and what do you want this for, Ember?

Code: Select all

LINK 800
LINK 801
LINK 801
LINK 801
KILL
GRAB 276
LINK -1
LINK -1
LINK -1
LINK -1
This is too simple, of course. It works for the initial test run, but the 'enemy EXA' is going to be in a different one of those 8 top-right hosts every test run.
At least bringing back the file is going to be the same every time, since all reverse LINK ids are -1.

Image

This works better. At every step on the way the EXA has to choose which direction to go in. To make it go fast, I just REPL the EXA and have one go left and the other right. I reuse the initial LINK 800 in my little loop since I had it around anyway, and I keep a counter and TEST for it to see if I reached the final hosts and should start grabbing the file. The KILL command does nothing if there's no other EXA there, but GRABbing a non-existing file will cause the EXA to self-destruct. Only the EXA that finds the actual file will survive and will bring it home.

Image

Image

Image The top percentiles for this one are 15 / 15 / 27. So apparently we did the best we can activity-wise. How about the others?

The size is 16, so we just need to save a single line in this solution to be in the best percentile. What I can do is replace the four LINK -1 lines with this:

Code: Select all

MARK RETURN
LINK -1
JUMP RETURN
34/15/27.
That return loop never ends but that doesn't matter - the EXA will blow itself up in the home host once it tries to LINK to a non-existing -1.

For the cycle count, as always it's worth to look into unrolling the loops.

But that's easier said than done since the REPL itself introduces a kind of jump. I tried it anyway.

Code: Select all

LINK 800

REPL A
LINK 800
JUMP NEXTB

MARK A
LINK 801

MARK NEXTB

REPL B
LINK 800
JUMP NEXTC

MARK B
LINK 801

MARK NEXTC

REPL C
LINK 800
JUMP NEXTD

MARK C
LINK 801

MARK NEXTD

KILL
GRAB 276
LINK -1
LINK -1
LINK -1
LINK -1
This simply repeats the 800/801 choice three times. Each EXA that doesn't "choose" the REPL then has to jump to the next choice, ignoring the REPL's LINK instruction. It's quite ugly and it feels like we should be able to do (much) better, yet the new score is 18/25/27, which corresponds to that big peak to the left of the cycle histogram.

Shaving off those last three cycles is going to be tricky. Perhaps we can get rid of those unconditional jumps, but most of my ideas would require making more EXAs earlier and that wouldn't save any cycles because only one EXA can traverse any network link at a time.

I also attemped doing something where it tries to find the file after each step. But a GRAB on a non-existing file destroys the EXA, so you'd have to do a REPL first. A REPL blocks the EXA forever if there's no place for the clone (when there's an enemy EXA or even just a file sitting in the other square of the host). So that's not going to work. I'll leave this one to the thread.


Image Would you say you had friends?

Image

Image And for the next assignment:

Image There's another file I want to grab.
Image They tried to wipe everything clean, but luckily, there's a set of backups.
Image Always keep a backup, right?


Image

Image Two votes this time.

User avatar
Sure I do and Right

User avatar
Part 12 - UC Berkeley EECS


=== Trash World Inbox ===

silentsnack has some improvements for last week's puzzle.
silentsnack wrote:For the small solution, you can also use "link to a variable" and rearrange the code to fall through from the cloning operation to a different version of the clone, which eliminates the need for a JUMP/MARK pair in order to make both EXAs merge onto the same script.

Code: Select all

MARK LOOP
ADDI X 1 X
COPY 800 T
REPL FORK
COPY 801 T
MARK FORK
LINK T
DIVI X 4 T
FJMP LOOP

KILL
GRAB 276
MARK OUT
LINK -1
JUMP OUT
40/14/27
Reduces it to 14 from my 15. The trickiest part is getting the right way to rearrange the code, I guess.
silentsnack wrote:And as you mentioned, if you want the fastest solution for this puzzle you don't have any spare cycles for clever/elegant JUMP flow control, just weapons-grade spaghetti

Code: Select all

LINK 800
REPL 5
LINK 800
REPL 37
LINK 800
REPL 2468

LINK 800
KILL
GRAB 276
@REP 4
LINK -1
@END
HALT

MARK 2468
LINK 801
KILL
GRAB 276
@REP 4
LINK -1
@END
HALT

MARK 37
LINK 801
REPL 2468
[...]

MARK 5
LINK 801
[...]
15/46/27
Yep. The gaps in the code should basically handle each case manually, by LINKing to whatever side the REPL didn't handle yet. This way all unnecessary jumps are gone. By the way, using numeric MARK names doesn't make the code any easier to read.


=== UC Berkeley - Department of Electrical Engineering & Computer Sciences ===

Image Would you say you had friends?

Image

Image Two votes for Sure I do.

Sure I do.

Image Yeah? Like Ghast? The EXAPUNKS?
Image But you never say anything to them.
Image Do friends not communicate back and forth?
Image Either way, this is good data for me.


I'm pretty sure it's TCP that communicates back and forth, lady.

Image

Image I know real-life phreakers war-dialing business numbers sometimes found fresh dial tones... that would allow you to dial international numbers, billing the business for the costs.

Image

Looks like Ember wants something from UC Berkeley. That's at least closer to home than whatever that Russian thing was.

Image There's another file I want to grab.
Image They tried to wipe everything clean, but luckily, there's a set of backups.
Image Always keep a backup, right?


Image

Image Everyone who voted seems to agree.

Right.

Image You never know what's going to happen.
Image Data is more fragile than people think.


Make regular backups, folks.

Image
OST: Leave No Trace

Image So, I got a bit curious and did some digging. The UC Berkeley department of EECS exists, and that logo is very similar to the style used on their official website - since around 2005ish. The web archive pages from before that look quite different. Bit of an anachronism, but I doubt Zachtronics expected anyone to dig this deep.

Our assignment:
- Locate the specified host (either tape-1, tape-2, or tape-3) and then locate the specified entry (ПАСЬЯНС) in the tape backup file in that host (file 200). Create a file in your host containing the entry's data.
- The names of the target host and entry are available in file 300.
- For more information see "Accessing Data in Legacy Storage Systems" in the first issue in the zine.


We also have a maximum size of 75 this time, quite a change from the 50 we've seen before.

All those files named 200 have a similar structure - a whole bunch of numbers and then some words and numbers.

Image

Are those student grades?

Anyway, to the zine!

Image

There's another page about hard drive arrays but that doesn't seem very relevant here.

Now, before I start you should notice there's no LINK ids back from most of those hosts. We can only travel counterclockwise. That's gonna be a bit limiting.

I could either start with two EXAs: one who stays home and communicates the info from 300 and one who goes find the data; or with one who does everything. I decided to go with the single EXA solution because it seems simpler - with two EXAs, how would you tell the home one it should switch from sending the TAPE number to sending the entry name?

Image

Since it's a bit annoying to go back and forth in a file I decided to copy the TAPE number into X. It might save a cycle or two over the alternative.
It's not possible to TEST against the host name directly, I need to use the special HOST command to copy it into a register and TEST against that. Luckily I have T available for that.

Once the EXA tracks down the tape file, it copies the entry name to X, WIPEs the home file and GRABs the tape. Now to search for the entry.

Those zeroes just before the tape's index are probably padding, because all tapes are the same length. If that's true we can just SEEK straight to the start of the index and start searching from there. A SEEK 126 seems to do the trick.

After a bit of tinkering I settled on this:

Code: Select all

SEEK 124

MARK FINDENTRY
SEEK 2
TEST F = X
FJMP FINDENTRY
SEEK 2 is necessary for every cycle (to go to the next entry's name), and it's best to have it at the start to prevent some awkwardness when the EXA finds the right entry. Having the initial SEEK only go to 124 allows me to do this without too much overhead.

Image

It makes sense to put the first value in T - I'm gonna need T for test results, and the first value contains the start position which I only need once anyway. The other value goes into X and the EXA can SEEK to the start position.

Now we run into a little problem - the EXA is going to have to copy a whole lot of data but it can only hold one file at once. I really don't see any other solution than introducing a second EXA and sending all the data over M.

I added this to XA:

Code: Select all

MARK SENDLOOP
COPY F M
SUBI X 1 X
TEST X = 0
FJMP SENDLOOP
It automatically stops when it's done.

And I made XB that simply does this:

Code: Select all

MAKE

MARK LOOP
COPY M F
JUMP LOOP
Getting close to a solution. The only problem is that XB never halts.

I could send XA home to KILL XB when it's done, but in situations like this I prefer using a special "end of transmission" packet. It might be slower, but we'll see.

Image

Here's my first working solution. When XA is done it sends -9999 over M. If that's a valid entry in a tape file this won't work. I'll have to try and see.

XB now needs to use X as an intermediate register so it can TEST for -9999. Since I don't want to store -9999 in the file, the EXA should immediately exit the loop in that case. I can do that by, unintuitively, putting the COPY to F at the start of the next loop. To store the first entry correctly, there's an initial COPY from M before the loop. This assumes that we never get a zero-size entry but I think a zero-size entry wouldn't make sense anyway.

Image

142/34/7.

Image Top percentile has 97/28/7. What can we do better?

First of all, the activity can't possibly be improved. We need 7 LINKs to get to tape 3, and sometimes we simply need to go there. Remember, to score a solution, it takes the worst-case scenario out of all the test runs.

Looking at cycles, there's a simple thing I missed in my initial solution: I don't need the TEST command to end the loops if I just set up the T register correctly.

Code: Select all

;XA

; LOAD INDEX AS BEFORE

SEEK -9999
SEEK T
COPY X T

MARK SENDLOOP
COPY F M
SUBI T 1 T
TJMP SENDLOOP

COPY 0 M

;XB

MAKE
COPY M T

MARK LOOP
COPY T F
COPY M T
TJMP LOOP
124/33/7. As it happens 0 isn't used as a valid number either so I can use that as the end-of-message packet. And I also used T for the for loop in XA now.

Next thing to look at is unrolling loops. There's no immediate obvious way to do this because every loop needs to check something which requires a TJMP/FJMP anyway. However, I can save many loops by making use of the fact there's a minimal data size - the file we're looking for is always at least 15 entries long. We really don't need to check if we're done before that.

Code: Select all

;XA

GRAB 300
COPY F X
LINK 800

MARK FINDTAPE
LINK 800
LINK 800

HOST T
TEST X = T
FJMP FINDTAPE

COPY F X
WIPE

GRAB 200

SEEK 124

MARK FINDENTRY
SEEK 2
TEST F = X
FJMP FINDENTRY

COPY F T
COPY F X

SEEK -9999
SEEK T
SUBI X 14 T

@REP 14
COPY F M
@END

MARK SENDLOOP
COPY F M
SUBI T 1 T
TJMP SENDLOOP

COPY 0 M

;XB
MAKE
@REP 14
COPY M F
@END

COPY M T

MARK LOOP
COPY T F
COPY M T
TJMP LOOP
96/61/7, cycle count already better than the top percentile somehow. But I still have some more ideas.

First, the earlier plan of using an EXA that stays home to send the data from the file so the 'main' EXA doesn't have to carry that file around.

I changed XA to LINK in the first cycle and read from M in the second cycle. It also reads from M when it would normally read the name of the data entry.
XB simply starts with GRAB 300; COPY F M; COPY F M; WIPE before it creates the new file. It had to wait anyway.

Result 95/63/7. I think the saved cycle comes from the fact that XA doesn't have to WIPE the file anymore. It isn't more because it now has to wait for XB's M.

We could perhaps fix that by having XA read from M later, but that would introduce other problems. No, I have a better idea: parallelism during the initial search:

Code: Select all

LINK 800
COPY M X

MARK FINDTAPE
LINK 800
LINK 800

REPL FINDTAPE

HOST T
TEST X = T
FJMP FINDENTRY

; TAPE HANDLING
This way, the new EXA is already LINKing to the next tape node while this one is still checking. I reuse the FINDENTRY mark here - the first command after it is a SEEK that requires the EXA to hold a file; so it quickly makes the EXA crash.

It's a good idea... but we have one problem. After the correct tape is found, the last REPL'd EXA just keeps going round and round, and if we allow it to keep going until the tape handling is done, it will pick up the tape file and cause trouble. We need to get rid of it.

Luckily we have plenty of spare lines of code and enough time while the other EXAs do their thing.

ImageImage

Image

My final solution. XC basically waits in the host with file 243 for the number of cycles until XA makes a full circle. Just doing a couple KILLs then isn't quite enough, because XA tries to REPL and move on to another run around the block while the KILLs are happening and it's unpredictable whether the one that stays or the one that LINKs gets killed first. Instead I have XC itself REPL and sit there so that XA can't link, and it can be killed without a fuss.

This brings my best scores to 92 cycles, 33 size, and 7 activity. And that's where I'll stop. We know the size can definitely be improved, and perhaps the cycles can go even lower. Share your improvements.

Image Kind of a shame what happened here.
Image There was some interesting research going on.


Image

Image Here's the vote for this update.

But first for something special...

User avatar
Part 12.5 - ПАСЬЯНС

So that file that Ember made us get from UC Berkeley? Turns out it's a program I can run. Let's see what it is.

Image


Image Ah, this old game.
Image It's just how I remember it...
Image A testbed for goal-seeking behaviors regulated by emotional reasoning.
Image Do you like to play games?

Image

Eh, sometimes?

Image Picky, are we?
Image Well, this one's fun.


Image
New :siren: OST: ПАСЬЯНС

A card game?

Image

Ember made me go through all that for a game of Solitaire?

Image In fact, the Russian name directly translates to Solitaire. Like many other languages, they use the French name for the game, Patience.

Image Zachtronics always adds some sort of Solitaire bonus minigame to their games. In fact, their recent title NERTS! is entirely a Solitaire game, and an online competitive one at that. It's free on Steam.

Image There are three Steam achievements tied to this game: One for winning a round, one for winning 10 rounds and one for winning 100 rounds. Even though I wiped my save for this Let's Play, it still has my win count from the original save - I suppose it got that from Steam's achievement tracker.

Image

The game isn't too hard, but it's good to stop and think before each move. It's easy to get yourself stuck. Also, make use of the free cell.

Image

Getting some nice ordered stacks.

Image

Stacks of four face cards on an empty spot are turned upside down and can't be moved again. Not that it matters - you can't move face cards anywhere except on other face cards of the same suit or an empty spot anyway.

Image

I think I won?

Image If you ever need a break from hacking, this is a good choice.
Image It's mildly active, and not as taxing as putting together EXAs.
Image See how I am keeping your needs in mind.


Image

Image And another vote.

Image

Installing this game put a shortcut in my dock (to the right of Ember) so I can play it whenever I feel like it.

User avatar
No opinions on the questions today, and while I don't have a full code line since without the game its a bit hard for my layman brain to visualize it in my head, I DO have an idea for making a(slow) 1 Exa solution to bogarding our silly Solitaire game from the tapes instead of needing a receiving Exa.... or well I did until I remembered the Solitaire file is at least 15 entries of four digits, and our integer limit is 9999, I was considering the idea of using Multiplication to effectively add empty zeroes to the data number, and then Addition to slide the new numbers in, and then feed the numbers back onto the file somehow, using division to slice the leading digits off as remainders... yeah this wasn't the most well thought out solution but maybe it'll inspire someone else working 1 Exa plan...

9999 integer limit feels wrong, I don't even DO programming and it feels wrong both for being Really Small for supposed super computer tech, and for being arbitrary instead of Rule of 2, 9999 is clean for RPGs, its wrong for programming

User avatar
Part 13 - Workhouse Work Management System


=== Trash World Inbox ===
Cloudmonkey98 wrote:No opinions on the questions today, and while I don't have a full code line since without the game its a bit hard for my layman brain to visualize it in my head, I DO have an idea for making a(slow) 1 Exa solution to bogarding our silly Solitaire game from the tapes instead of needing a receiving Exa.... or well I did until I remembered the Solitaire file is at least 15 entries of four digits, and our integer limit is 9999, I was considering the idea of using Multiplication to effectively add empty zeroes to the data number, and then Addition to slide the new numbers in, and then feed the numbers back onto the file somehow, using division to slice the leading digits off as remainders... yeah this wasn't the most well thought out solution but maybe it'll inspire someone else working 1 Exa plan...
A one EXA-solution is probably possible. The simplest way I can think of is create a new file, GRAB the tape file, COPY a value from the tape into a register, and use the other register to keep track of your location in the file. DROP it, GRAB the new file, write the value there, GRAB the tape again, SEEK to where you left off, and so on. You'd have to reread the length of the data you need to copy each time as well, since you don't have a register left for that. Which means bringing the name from file 300 along with you as well. It would end up being very slow, and since there isn't any reward for a low-EXA solution it's not something I focused on.
Cloudmonkey98 wrote:9999 integer limit feels wrong, I don't even DO programming and it feels wrong both for being Really Small for supposed super computer tech, and for being arbitrary instead of Rule of 2, 9999 is clean for RPGs, its wrong for programming
You're right. I think I said before I understand the decision from a game design point of view - if you're not familiar with computers powers of two feel really arbitrary, and it might be hard to explain to players who aren't familiar with it.

That said, what's "small" depends. Nowadays, with most CPUs being 64-bit, a register goes up to 2^64 or a 20-digit number in decimal. But not all that long ago, 8-bit systems were common (original Gameboy, NES, Atari...) and those only go up to 256 unsigned or 127 signed, and programmers made do. You can still work with larger numbers in those CPUs but it's a relatively slow multi-step process, so 256 is a very common limit for games from that era. I think in the time this game takes place in, 16-bit systems were still common, which go up to 65K. So I wouldn't call the 9999 maximum all that unrealistic.
GuavaMoment wrote:My fastest solution is fundamentally identical to yours, I've just found a few cycles to squeeze out of some places, and I squoze one more cycle out from the seek 124 trick; I was previously seeking to the end and going backwards through the files. 89/70/7

Code: Select all

XA:

LINK 800
LINK 800
LINK 800
COPY M X
REPL TEST
LINK 800
LINK 800
REPL TEST
LINK 800
LINK 800
MARK TEST
HOST T
TEST X = T
FJMP TX
GRAB 200
SEEK 124
COPY M X
MARK SEARCH
SEEK 2
TEST F = X
FJMP SEARCH
COPY F X
COPY F T
COPY T M
SEEK -999
SEEK X
@REP 14
COPY F M
@END
SUBI T 14 T
MARK TX
COPY F M
SUBI T 1 T
TJMP TX


XB:

GRAB 300
COPY F M
COPY F M
DROP
MAKE
COPY M T
@REP 14
COPY M F
@END
SUBI T 14 T
MARK TX
COPY M F
SUBI T 1 T
TJMP TX
Yeah, from a glance, some changes are unrolling the loop that initially finds the right file, and sending the file size to XB so it knows when it's done. You could save one additional cycle by replacing the COPY F T for the file size with SUBI F 14 T, and then you can remove the SUBI from XB, too.
silentsnack wrote:Files are at least 15 entries, but it's also possible to exploit the fact that they don't get much bigger, and that there are at most 7 files per tape. And you don't necessarily have to find the correct host since the filename is unique.

Code: Select all

;XA
LINK 800
LINK 800
LINK 800
COPY M X
REPL TAPE1
LINK 800
LINK 800
MARK TAPE2
LINK 800
LINK 800

MARK WHERE
GRAB 200; HOST = DGAF
SEEK 126
TEST X = F
@REP 6
TJMP WHICH
SEEK 2
TEST X = F
@END
DIVI T T T

MARK WHICH
COPY F X
COPY F T
REPL WHEN
SEEK -999
SEEK X
MARK WHAT
@REP 9 ; SIZE 15~18
COPY F M
@END
JUMP WHAT

; AWKWARD!
MARK TAPE1
REPL TAPE2
JUMP WHERE

MARK WHEN
COPY T M
MARK KILLSWITCH
SUBI T 1 T
TJMP KILLSWITCH
KILL

;XB
GRAB 300
VOID F
COPY F M
SEEK -2 ; RECYCLE F.300
SUBI M 1 T
REPL KILLSWITCH

MARK WRITE
COPY M F
JUMP WRITE

MARK KILLSWITCH
SUBI T 1 T
TJMP KILLSWITCH
NOOP
KILL
74/73/11

The REPL TAPE1 branch is because it takes the longest to reach TAPE3, so that one will be the limiting case and its EXA does as little else as possible. But the fact it's running a single codeblock and JUMP detours take more time, it required finding some place to wedge in between other operations.
Nice. Also uses the trick of having XA and XB have a separate countdown. It also saves cycles by putting those in REPL'd EXAs which run a KILL command at the end. And yeah, skipping the host check and just checking all files in parallel helps a lot too, as well as unrolling the filename search loop. The DIVI T T T is a nice trick here. It only triggers if a tape contains the maximum number of files. If T is zero (the last file isn't the one we're looking for), dividing by zero immediately crashes the EXA. If it is one, it continues and falls through into the data copying code. An interesting way to do a test and crash in a single cycle.
silentsnack wrote:For reducing size further, dividing by zero is an efficient way to crash an EXA. As is grabbing a nonexistent file.

Code: Select all

;XA
GRAB 300
COPY F X
COPY F X
WIPE
LINK 800

MARK WHERE
LINK 800
LINK 800
GRAB 200
REPL WHERE

MARK SEARCH
TEST X = F
FJMP SEARCH

COPY F X
COPY F T
SEEK -999
SEEK X

MARK WHAT
COPY F M
SUBI T 1 T
COPY T M
TJMP WHAT

;XB
MAKE
MARK WRITE
COPY M F
DIVI 1 M X
JUMP WRITE
420/27/9
Aha. Yeah, so it just REPLs endlessly looking for new files. I had trouble getting around that and had to use my ugly solution of having another EXA just blocking the path, but by grabbing file 200 first and then REPL'ing, instead if the file doesn't exist it just crashes itself. Other than that, this code scans the entire tape line by line for the name of the program. It does so for each file, and an EXA automatically crashes when it tries to read past the end of the file. Finally, by sending two values over M for each data value (the actual value and the T counter), XB knows when to stop using the DIVI by zero check. Although if we're going for low size count, replacing the last two lines in XB with COPY M T; TJMP WRITE works just as well.
azsedcf wrote:Can you pass/fail a entire class by editing file 243?
Well, maybe you can. But the game doesn't acknowledge this in any way, other than failing the "Leave no trace" rule.


=== Workhouse - Work Management System ===

Image Kind of a shame what happened here.
Image There was some interesting research going on.


Image

Image I saw only the one vote this time, for 'what happened?'

What happened?

Image Oh, you know.
Image People get swapped around, priorities change...
Image Things don't work out for lots of reasons.


Image

The second edition of the zine might turn out quite interesting. Glad I helped out Ghast.

Image If you ever need a break from hacking, this is a good choice.
Image It's mildly active, and not as taxing as putting together EXAs.
Image See how I am keeping your needs in mind.


Image

Image Two votes for 'Thanks.'

Thanks

Image You're welcome.
Image Appreciating me will make things easier.
Image Let's continue.


... make what easier?

Image

Anyway, Nivas is here. Let's hope they have some good stuff.

Image

Image Special delivery.

Nivas hands me another pack of bootleg medication.

Image Whatever it is you're doing for money... it's working.
Image Makes me wish I had a line like that.
Image More control over my life...

Image I found out something interesting the other day.
Image You ever hear about these things called EXAs?
Image They're these little guys running around inside computers.
Image They make it all work. Banks, commerce, government... anything you can think of.
Image Funny, how it's all based on the same stuff on the inside.
Image Just something I've been thinking about lately.
Image Anyway, see you around.
Image Got a feeling I'll be making more deliveries.


"eksas"? Can't say I heard of them, Nivas.

Image

Ain't nobody got time to play games. I need to get work done.

Image

Hey, Workhouse, isn't that that stupid app I used a while back, where you enter data for some scraps?

Image How many receipts do you think a normal human being could enter per minute?
Image Assuming average conditions.


Image

I guess that depends on how long they are.

Image I may have been too fast.
Image My estimates were based on incorrect assumptions.
Image Once again.
Image Guess we have to clear this up.


Ember, what have you done now... Let's go fix it then.

Image
OST: Code and Registers

Hm, looks like we connected through a vulnerability in their SECURE host. Not very secure, is it?

The assignment:

- Locate EMBER-2's user file in the users host and overwrite it so that the sum of the values is the same but no individual value exceeds $75. All values, except for the last, must be the maximum value ($75). You will need to add additional values to accomplish this.
- EMBER-2's username is available in file 300.
- Note that the sum of the values in EMBER-2's account will always be less than $10,000.
- For more information see "Network Exploration: Workhouse" in the first issue of the zine.


Image

Before we start let's take a peek at all the files first.

Image

File 300 is in our home host and contains EMBER's username, 'NORMALHUMAN'.

File 199 in the secure host has a list of usernames and... are those passwords? Stored in plaintext? Crap, it's a good thing I used an unique password when I signed up. I can't believe companies still store passwords in plaintext, they should know better than that. If some malicious hacker were to get access they could try to get into other places with those passwords. Anyway, the third value is the user number.

Looks like files 213 to 246 are user files. They start with the user's full name (NORM STANDART for EMBER), then what I think is a birth date, and then the amount of dollars they made each day. This is what we're going to need to edit.

The last set of files contains jobs. A job id, then a bunch of numbers, and then a description. Looks like they're running the following jobs right now:
- Cronus Capital Markets. Receipt Data Entry. You will be shown images of receipts generated in the course of normal everyday business and will enter them digitally for bookkeeping and reimbursement purposes. Save a busy executive a minute or two!
- Managed Research Corp. Prior Art Search. You will comb over patent filings for potential examples of prior art in order to provide important information for patent litigation. Don't worry about understanding the technical terminology - any match is a good one!
- Heartland Bounty. Visual inspection of beef and pork. You will monitor a networked digital camera inside a meat processing facility, keeping a keen eye out for mishandling, spoiled or rotten products, and other issues. Make a contribution to food safety and public health!
- Pinpoint marketing. Identify duplicate database entries. You will browse a large database of corporate personnel records. Some of the entries are slightly modified duplicates created with the intent to defraud the corporation. Identify the duplicate entries and send them packing!
- Royal Aegis Waste Processing. Categorize Waste Objects. You will be shown images of various pieces of refuse as they are conveyed along a track at a waste processing facility. Categorize these objects as paper, glass, metal, or rubber. By helping to sort out trash, you make non-toxic water possible!


You know, I can't help but think the quality of some of these services has to suffer if you hand this work to underpaid internet randos.

That's all there's to see in the host. Those EXAs in the bottom right aren't reachable since the LINKs have no numbers.

Back to the assignment - we have to find EMBER's user file and update the amounts so the total is the same but it's spread over more days. The assignment is nice enough to state the sum will always be less than $10000, that means the whole thing fits in a register.

Image

Starting simple, I read the username from file 300, then find it in 199. The SEEK 2 saves a couple cycles because there's no point checking against the password or file number, but it puts the cursor one past the file number once the EXA finds the right user. So I SEEK back one, store the ID, and LINK into the USERS host to GRAB EMBER's file.

Image

And this is a working solution. The EXA copies the first number into X (to get rid of whatever was there before), then goes through a SUM loop where it adds the rest of the numbers. TEST EOF is necessary to prevent it from just crashing at the end. Then it goes back to the start of the file (SEEK 2 to get past the metadata) and keeps writing 75 until less than that is left in X, in which case it writes the remainder to the file and ends. Note that if you write to an existing position in the file it overwrites it, if you write to the end it appends, so this code works for both cases.

I went for less than 76 because otherwise, if the remainder is 0 the final COPY would write a trailing 0 into the file which might tell Workhouse that something is wrong (but it turns out this never comes up in the test set so either way is fine).

Image

Image The top percentiles this time are 218, 27, and 2.

Getting the top percentile for size is easy enough. That SEEK 2 in the FINDUSER loop is not necessary at all, it just speeds up some cycles. If I remove it, the SEEK -2 has to be replaced with a SEEK 1. 531/27/2.

I wouldn't be surprised if it's possible to get an even smaller size but there's much bigger gains to be gotten in the cycle count.

Image
Oops, I accidentally gave two EXAs the same name.

I dedided to start with a relatively small improvement by splitting up the EXAs and having them go to the right host immediately, then wait for instructions on M. 516/29/3.

Since there's always at least 10 days worth of data in the file, we can delay the slow EOF check and unroll part of that loop by putting @REP 8; ADDI F X X; @END above the SUM loop. 500/37/3.

But there's a much bigger improvement somewhere. How about this for the writing EXA:

Code: Select all

NOOP
LINK 800
LINK 799
GRAB M

SEEK 2
COPY F X

@REP 8
ADDI F X X
@END

MARK SUM
ADDI F X X
TEST EOF
FJMP SUM

SEEK -9999
SEEK 2

DIVI X 75 T

MARK OVERWRITE
COPY 75 F
SUBI T 1 T
TJMP OVERWRITE

MODI X 75 F
By dividing X by 75, we get how often 75 fits in X, and we can just put that in T and use a countdown. Saves a TEST instruction every cycle. For the end, just use the MODI function to get the remainder quickly.

Again we can unroll part of this loop. How much depends on the total amount of money EMBER made. From some testing it turns out that's always at least 60 times $75. This can be handled with @REP 60; COPY 75 F; @END; SUBI T 60 T. However, that doesn't fit in the 75-line limit for this level.

So, let's just run @REP 30 twice:

Code: Select all

NOOP
LINK 800
LINK 799
GRAB M

SEEK 2
COPY F X

@REP 8
ADDI F X X
@END

MARK SUM
ADDI F X X
TEST EOF
FJMP SUM

SEEK -9999
SEEK 2

MARK FILLFILE
@REP 30
COPY 75 F
@END
SUBI T 1 T
FJMP FILLFILE

DIVI X 75 T
SUBI T 60 T

MARK OVERWRITE
COPY 75 F
SUBI T 1 T
TJMP OVERWRITE

MODI X 75 F
Before entering FILLFILE, the EXA has T set to 1 (from the last EOF test). The first loop's SUBI makes it 0, false, and the second makes it -1, true. This is a quick way of going through the loop just twice without requiring extra setup of the T register. 275/71/3.

My next idea involved something we've seen in thread submissions quite a few times now: speeding up the overwrite loop by putting the countdown in a separate EXA that kills the first one when it's done. The issue is that after that the remainder still needs to be written. This works:

Code: Select all

; XA

GRAB 300
COPY F M

;XB

LINK 800
GRAB 199
COPY M X

MARK FINDUSER
TEST F = X
SEEK 2
FJMP FINDUSER

SEEK -1
COPY F M

SEEK -1
COPY F M

;XC

NOOP
LINK 800
LINK 799
GRAB M

SEEK 2
COPY F X

@REP 8
ADDI F X X
@END

MARK SUM
ADDI F X X
TEST EOF
FJMP SUM

SEEK -9999
SEEK 2

MARK FILLFILE
@REP 25
COPY 75 F
@END
SUBI T 1 T
FJMP FILLFILE

REPL KILLER

MARK OVERWRITE
COPY 75 F
JUMP OVERWRITE


MARK KILLER
DIVI X 75 T
SUBI T 52 T

MARK COUNTDOWN
SUBI T 1 T
TJMP COUNTDOWN

KILL
GRAB M
SEEK 9999
MODI X 75 F
After the FILLFILE unroll is done, XC replicates itself into a KILLER clone. Since that one's job, at first, is just to count down, it has plenty of time to calculate the starting value of T, as long as the number in SUBI T 52 T is adjusted so it starts at exactly the right cycle count. After the killer kills the EXA that writes 75, it can grab the file (XB has been waiting all that time to send the ID of the file just once more), and adds the remainder.

The only issue is I ran into size limit problems again. In this solution I solved that by reducing the unroll size in FILLFILE, but that slightly slower compared to not having to do that. 235/75/4.

I can get some of the complete unroll back by making FILLFILE loop thrice:

Code: Select all

COPY 3 T

MARK FILLFILE
@REP 19
COPY 75 F
@END
SUBI T 1 T
TJMP FILLFILE

COPY 75 F
and change the SUBI of the killer to 60. I had to go for @REP 19 instead of 20, because the countdown always runs at least once and for the test where there's exactly 61 * 75, it would start at zero and go into the negatives, getting stuck into an infinite loop. The limit where this still barely works is at 3 * 19 + 1 hardcoded 75s, hence the lone COPY 75 T to save one more cycle. 230/71/4.

And that's where I'll stop. The top percentile is 218 so let me know what further improvements are possible.


Image Just when I think I'm decent at pretending to be human, I find there's another parameter to add.

Image

Image The first dialogue choice.

And next, the intro for the next assignment.

Image How much does money affect behavior?
Image A lot, right?


Image

User avatar
Interesting and interestinger, "How long have you been pretending" and "Some behaviors aren't affected"
I feel like the other two answers for the first question are peak "no shit sherlock" material since we know Ember was attempting to mimic humans and uhh... failing, so being slow is normal, and its fairly clearly her source of drug money

User avatar
Part 14 - Equity First Bank


=== Trash World Inbox ===
GuavaMoment wrote:My 216 solution is not elegant. Mostly identical to yours, but I run 21 lines of "Copy 75 F", until X < 2500, where I run 9 lines of "Copy 75 F" until X < 750, then keep alternating between copying 75 and testing every time until X is zero.
Ah, yes, so basically repeating large chunks of writing 75s with only the occassional test and loop and then progressively smaller chunks and more common tests. Optimizing that is gonna be trial and error, depending on the specific puzzle input.
silentsnack wrote:Looking at my solutions... I don't even remember writing this but aside from obviously being the product of trial and error it seems crude/unfinished? Maybe I'm just tired but I'm not seeing what it does all that differently, other than minor improvements like combining COPY F X and an ADDI X F X operation into ADDI F F X.

Code: Select all

;XA
GRAB 300
COPY F X
WIPE
LINK 800
GRAB 199
MARK FIND
TEST F = X
SEEK 2
FJMP FIND

SEEK -1
COPY F M

;XB
LINK 800
LINK 799
GRAB M
SEEK 2

ADDI F F X
@REP 7
ADDI X F X
@END
MARK ADD
ADDI X F X
TEST EOF
FJMP ADD

SEEK -999
SEEK 2
DIVI X 1800 T
MODI X 1800 X
MARK WRITE24
@REP 24
COPY 75 F
@END
SUBI T 1 T
TJMP WRITE24

DIVI X 225 T
FJMP END
MODI X 225 X
MARK WRITE3
SUBI T 1 T
@REP 3
COPY 75 F
@END
TJMP WRITE3

MARK END
TEST X > 75
FJMP BREAK
SUBI X 75 X
COPY 75 F
JUMP END

MARK BREAK
COPY X F
But somehow the stats are showing 195/75/3
Those 1800 and 225 calculations do something similar to what GuavaMoment said, reducing the number of loops by testing for larger chunks. That together with the ADDI combination thing and perhaps some other improvements make it much faster, I think.


=== Equity First Bank ===

Image Just when I think I'm decent at pretending to be human, I find there's another parameter to add.

Image

Image Two votes for "I thought an AI would be way faster", and five for the winning option.

How long have you pretended to be human?

Image Oh, pretty much from the start.
Image It's kind of a thing that I do.
Image Remember when I surprised you? That was a good one.
Image Let's continue.


Yeah, let's.

Image

Image

That sounds... chaotic.

Image How much does money affect behavior?
Image A lot, right?


Image

Image Two votes for "Some behaviors aren't affected", five for the other choice.

Yeah, a lot.

Image I'm going to do an experiment involving wealth redistribution.
Image Sudden, random wealth redistribution.
Image This should be fun.


Image
OST: Leave No Trace

The assignment:
- Dispense all available cash from all connected ATMs.
- For more information see "Network Exploration: Equity First Bank" in the first issue of the zine.


The max code size for this assignment is 50 lines.

Image

Okay, so we don't really need to mess with the account files. Let's take a quick peek though.

Image

It's basically as the zine says. Files have an account number and name and then a bunch of transactions. There's also a file 199 but that seems to just contain a list of the other file names. Nothing very exciting.

Image

Messing around with the ATMs a bit I quickly learned two things: the #DISP register only accepts exactly 20 as input. You can't tell it to dispense $40 to make it spit out two bills at once. And if you tell it to dispense bills when the #CASH register is empty, the entire host will error out, which immediately fails the "Leave no trace" goal.

Also, the attached ATM hosts actually differ for each test case. The LINK ids are always between 800 and 806 inclusive but it's often not the complete set.

So, the first challenge is getting EXAs to all the ATMs. This code will work:

Image

I can think of quite a few ways to do this, some a bit faster than others. For instance I could just make 7 EXAs and hardcode the different LINK instructions to 800 - 806. But since the IDs are sequential, using a REPL clone loop seems a bit more straightforward and probably makes the program smaller.
The problem is that I need to have a way to stop the loop, otherwise it keeps spawning new clones forever, and we can't leave any EXAs running around.

I decided to handle that with a cleanup EXA that just kills the cloning EXA after its timer runs out.

Next, dispensing cash from all the ATMs. Just to have something working I started with this naive solution:

Code: Select all

LINK 800
LINK 800 
LINK 800
REPL CLEANUP

COPY 799 X

MARK CLONE
ADDI 1 X X
REPL CLONE
LINK X

COPY #CASH T

MARK DISPLOOP
COPY 20 #DISP
SUBI T 1 T
TJMP DISPLOOP

HALT

MARK CLEANUP
COPY 7 T

MARK CLNLOOP
SUBI T 1 T
TJMP CLNLOOP
KILL
A very simple loop with a countdown so it knows when the ATM runs out of cash.

There's no loop unrolling or anything and there's huge amounts of bills so this is slow.

Image

Very slow. In fact, this takes almost a full minute to run all 100 tests at fast forward speed.

Image

However, it does work, as you can see from the crowd of people flocking around the ATM.

The top percentiles are 1072, 14, and 10.

Let's start with improving activity. Since it always counts the worst test, the activity is 10 if you need to get EXAs to all 7 ATMs and only move the one to the host just before the ATMs. So the only activity step that can be removed is the one KILL instruction.

Well, that's easy enough if we don't care about making the code even slower.

Code: Select all

LINK 800
LINK 800 
LINK 800

COPY 799 X

MARK CLONE
ADDI 1 X X
TEST X > 806
TJMP END
REPL CLONE
LINK X

COPY #CASH T

MARK DISPLOOP
COPY 20 #DISP
SUBI T 1 T
TJMP DISPLOOP

MARK END
I put the countdown in the CLONE loop so I can remove the cleanup EXA. Result: 3028/16/10.

That's quite nice, we also happen to be getting close to the top percentile in size. Only two more lines of code to get rid of.

The MARK END isn't actually required. We can TJMP from the CLONE loop to the DISPLOOP mark... it'll then try to COPY to #DISP which doesn't exist in that host and it'll crash. That's 15 LoC.
Also, the COPY from #CASH and the countdown in T can be combined into a single line by just testing #CASH directly each round.

Image

3027/14/10

Let's go back to the initial solution and see if we can speed it up. With a max size of 50, we don't have much spare room for unrolling loops but we can do at least some.

Code: Select all

LINK 800
LINK 800 
LINK 800

COPY 799 X

MARK CLONE
ADDI 1 X X
TEST X > 806
TJMP DISPLOOP
REPL CLONE
LINK X

MARK 33LP
@REP 33
COPY 20 #DISP
@END

TEST #CASH > 33
TJMP 33LP

MARK DISPLOOP
COPY 20 #DISP
TEST #CASH = 0
FJMP DISPLOOP
I started from the low-size solution since it fits some more unrolling. Simple enough: if there's still more than 33 bills left, spit out 33, otherwise count them one by one. 1145/50/10. Much faster already. Also, turns out that combining this with the cleanup EXA solution drops the cycle count to 1144, even though there's only space for 27 unrolls.

From that solution we can drop it to 1125/50/11, using this code:

Code: Select all

LINK 800
LINK 800 
LINK 800
REPL CLEANUP

COPY 799 X

MARK CLONE
ADDI 1 X X
REPL CLONE
LINK X

MARK 28LP
@REP 28
COPY 20 #DISP
@END

TEST #CASH > 28
TJMP 28LP

MARK DISPLOOP
COPY 20 #DISP
DIVI X #CASH X
JUMP DISPLOOP


MARK CLEANUP
COPY 7 T

MARK CLNLOOP
SUBI T 1 T
TJMP CLNLOOP
KILL
The main change is that I got rid of the HALT instruction by stopping the loop using a divide by zero crash. This lets me bump the unroll to 28 which is enough to save a bunch more cycles.

And because increasing the size of the unroll is so important, I should focus on that, regardless of anything else.

For instance, replacing the cleanup code with the much uglier

Code: Select all

MARK CLEANUP
SUBI X 1 X
TEST X < -6
FJMP CLEANUP
KILL
saves a line of code, allowing one more COPY in the unroll, dropping the cycles from 1125 to 1122.

Finally, I found one more minor improvement, but it's a bit of a weird one.

Code: Select all

LINK 800
LINK 800 
LINK 800
REPL CLEANUP

COPY 799 X

MARK CLONE
ADDI 1 X X
REPL CLONE
LINK X

MARK 29LP
@REP 29
COPY 20 #DISP
@END

DIVI 28 #CASH T
FJMP 29LP

MARK DISPLOOP
COPY 20 #DISP
DIVI X #CASH X
JUMP DISPLOOP


MARK CLEANUP
SUBI X 1 X
TEST X < -6
FJMP CLEANUP
KILL
1120/50/11

The DIVI 28 #CASH T line speeds up things slightly because it acts as a three-way test.
If #CASH is 29 or greater, the result will be 0, causing FJMP to jump back into the unroll and dispense 29 more bills.
If #CASH is smaller than 29 but greater than 0, the result is (rounded) 1, so it won't jump and continue to the DISPLOOP.
And if it's exactly zero, the EXA immediately crashes with a divide by zero error. This means that it doesn't even have to go into the DISPLOOP once if the amount of bills is a multiple of 29, which happens in some slow test, saving two cycles.

1120 is still almost 50 cycles away from the top percentile. I'm still missing a significant improvement somewhere. I was thinking about making a smaller unrolled loop after the big one - to more quickly deal with the remaining bills. But since that takes cycles from the large unrolled loop, that seems to make things slower overall. Perhaps there's a very specific combination that makes it faster but I haven't found it.

Alternatively I'd look into running the DISPLOOP code in parallel, but I'm not sure how to do that. There's no place for an extra EXA in the ATM hosts, so no other EXA can come in to kill the dispenser EXA. And communicating via M would be hard too - there's only one global M register for all EXAs, and waiting for a kill signal on M isn't any faster than an EXA handling that by itself.

Image So, let me know what I'm missing.

Image Well, that caused a bit of chaos.
Image But not as much as I hoped.
Image I wonder why.


Image

Image And next time, more body hacking to slow down the Phage.

Image So you're going to be hacking your own heart.

Image

User avatar
Now we're moving into things that'll have some kicking at the door soon, "People are good at ignoring problems" and "I'm a little scared"

This puzzle seems really intriguing, and I appreciate the Exapunks chat coming to the same topic for an utterly different idea
Parallelism set ups are funky, I can't really think of anything particular to say about it other then "hell yeah Div by 0 errors"

User avatar
Part 15 - Mitsuzen HDI-10 - Heart


=== Trash World Inbox ===
GuavaMoment wrote:So first off you can get a 12 line, 10 activity solution with this by combining some tricks:

Code: Select all

LINK 800
LINK 800
LINK 800
COPY 807 X
MARK CLONE
MODI -1 X X
REPL CLONE
LINK X
MARK CASHGET2
COPY 20 #DISP
DIVI 999 #CASH X
JUMP CASHGET2
Yeah, you create 807 exas, but it works! MODI -1 0 X kills the exa.
Hah, nice. Yeah, basically you try to get to each ATM from 807 down to zero. That saves a check. At 3010 cycles it isn't even that slow (since creating all the EXAs doesn't take as long as emptying out the ATMs).

It's a bit weird to think about the modulo function with negative numbers but in this case it basically acts as a decrement operation that crashes at zero. I should remember that, it could come in handy later.
Moto42 wrote:So, yea, it is faster to start with 7 EXAs, but each one can only have 7 lines of code.
Yeah, that's why I never really considered that. 7 lines is not enough for any other optimizations, that have more of an effect.
Quackles wrote:Here's my desultory best effort, with loop unrolling in a similar way:

Code: Select all

@REP 3
LINK 800 ;LES GO
@END

COPY 800 X
MARK REPLICATE
REPL GOLINK
ADDI X 1 X
TEST X < 806
TJMP REPLICATE
MARK GOLINK
LINK X

MARK KICKOUT
COPY 20 #DISP
MODI #CASH 32 T
TJMP KICKOUT

MARK KICKTEN
@REP 32
COPY 20 #DISP
@END
COPY #CASH T
TJMP KICKTEN
1146/50/10.
Interesting idea to handle the "remainder" first. I tried if I could get any more optimizations out of this design but I couldn't.
GuavaMoment wrote:For getting things fast, you need lots of loop unrolling, so I populate the exas differently that doesn't need a killer. It's a tiny bit slower to do that task but saves a bunch of lines, making things overall faster. Why giant loops of 21 and 8? No idea, ask me years ago when I did this by trial and error until it worked. Why do I test for #CASH > 29 even at a time when I know it's not? No idea, but it's faster! 1102/50/10

Code: Select all

LINK 800
LINK 800
LINK 800
COPY 806 X

MARK CLONE
REPL CASHGET
SUBI X 1 X
TEST X = 800
FJMP CLONE

MARK CASHGET
LINK X

MARK SPILL
@REP 21
COPY 20 #DISP
@END
MARK TENS
@REP 8
COPY 20 #DISP
@END
TEST #CASH > 29
TJMP SPILL
TEST #CASH > 8
TJMP TENS

MARK CASHGET2
COPY 20 #DISP
DIVI 999 #CASH X
JUMP CASHGET2
You did a smart thing here I hadn't considered. You put the 21 and 8 loop right after each other, meaning that from the MARK SPILL you actually get a 29-size loop (with no delays, because the only thing in between is a MARK which doesn't use a cycle when you just pass it.) So, that's what the > 29 check is for. If there's more than 29 bills left, do the whole loop again, otherwise only do the partial loop of 8 bills, and if there's less than 8 left to the remainder one by one. But yes, the numbers that work best here can only be found by trial and error.

Alright, so GuavaMoment's 1102 cycle solution is the best we got in the threads. But that's still a ways from the 1072 top percentile, and I got curious. So I decided to look it up. Turns out someone called StinkingBanana uploaded a fast solution just last week. Since they also uploaded a lot of stuff about future puzzles I won't directly link to it here. But I will share the solution because it's a thing of beauty.
Spoilered for anyone who wants to give it a try themselves first.

After getting the clones in place, simply do:
MARK DISP
@REP {as often as possible}
MODI 20 #CASH #DISP
COPY 20 #DISP
@END
JUMP DISP


You see what this does? The MODI/COPY combination handles the checking if we're done without any conditional jumping at all.
- If #CASH > 20, MODI 20 #CASH #DISP will write 20 to #DISP, acting like a free, additional COPY.
- If it's between 0 and 20, it will write 0 to #DISP, which doesn't do anything. At this point only the actual COPY instructions work.
- If #CASH is exactly 0, the next MODI will immediately crash the EXA.

So, for the majority of the program, MODI will act like an additional COPY. For the last 20 bills, it'll act like a "Am I done?" check.

Their solution runs at 1072/50/10, but has a bit of slack in the cloning code. Combining it with GuavaMoment's cloning code drops it to 1069, and using my solution with the CLEANUP clone, I managed to get it down all the way to 1067 cycles.



=== Mitsuzen HDI-10 - Heart ===

Image Well, that caused a bit of chaos.
Image But not as much as I hoped.
Image I wonder why.


Image

Image I got one vote for each option. This one goes to the random number generator.

People are good at ignoring problems.

Image Hmm.
Image I'll have to aim for something bigger next time.


...Uh oh.

Image

They're not lying, though. There are computers everywhere. In fact, there's probably a computer near you, right now!

Image

Damn, the phage is getting to my heart. Gotta do something quick.

Image So you're going to be hacking your own heart.

Image

Image Two votes for the third option.

I'm a little scared.

Image Why?
Image What happens if your heart stops beating?
Image Don't tell me. You die?
Image Processing.
Image Huh. I guess that explains some things.
Image At least all you need to do is make it beat.


Image

It's, uh, it's a little more complicated than that, Ember. I thought you studied humans, how do you not know our basic biology?

Image Human physiology is fascinating.
Image I should learn more about it.


...Why do I feel like I just said the wrong thing?

Either way, let's get started.

Image
OST: EXA Power

It's been a while since I last hacked my body, let's see if I remember how this works.

I have to do the following:
- Read a value from the nerve connected to your central nervous system (CNS) and make your heart beat by writing a sequence of values to your sinoatrial (SA-N) and atrioventricular (AV-N) nodes as indicated in the HDI-10 I/O log when holding the "SHOW GOAL" button. The length of each sequence of values should be equal to the value from the CNS divided by -10. Repeat ad infinitum.
- It is not necessary to leave no trave. Your EXAs should be written to operate indefinitely.
- For more information see "Debugging the Phage" in the first issue of the zine.


I've shown that article from the zine already, nothing new there. I have to write values like this:

Image

Alright, if I understand correctly, when I get -42 as input, I have to write -42 / -10 = 4 (rounded down) values to the outputs. The first value to SA-N has to be 40, the others are -70 (neural resting potential, according to the zine). For the AV-N output the first value has to be -70, the SECOND 40, and the rest -70.

Note that EXA cycles are much faster than a heartbeat, so I don't have to sync cycles perfectly, as long as I get the order of outputs right.

Code: Select all

LINK 800
REPL SA
REPL AV

HALT

MARK SA
LINK 1
LINK 1

HALT

MARK AV
LINK 3
LINK 3
I'll just start with a single EXA that clones itself, to have the lowest activity score out of the way.

For the lowest activity score I also can't move the EXAs around any further so I'll have to use the M register, and since both EXAs need to know how many cycles to write, I'll need to send the value to M twice. So the input EXA will have to be something like this:

Code: Select all

LINK 800
REPL SA
REPL AV

MARK INLOOP
DIVI #NERV -10 X
COPY X M
COPY X M
JUMP INLOOP
Since I can read from the #NERV only once (after that it will output the next value), I use X as an intermediate and divide by -10 while I'm at it.

Both of the other EXAs have to read from M and write the values to their nerve connections:

Code: Select all

MARK SA
LINK 1
LINK 1

MARK SALOOP
SUBI M 1 T
COPY 40 #NERV

MARK SACOUNTDOWN
COPY -70 #NERV
SUBI T 1 T
TJMP SACOUNTDOWN

JUMP SALOOP

MARK AV
LINK 3
LINK 3

MARK AVLOOP
SUBI M 2 T
COPY -70 #NERV
COPY 40 #NERV

MARK AVCOUNTDOWN
COPY -70 #NERV
SUBI T 1 T
TJMP AVCOUNTDOWN

JUMP AVLOOP
I assume that every input corresponds to at least two writes to the outputs (otherwise you can't get a proper heartbeat). That's why I can put the special stuff (writing the 40) outside the loop, as long as I make sure to subtract the appropriate value from my counter too.

Let's test this code.

Image

Oh... the first 17 tests succeeded but after that, "operation successful, patient dead" as they say? There's some wrong outputs in the list (and the cycle count keeps going until I abort since the EXAs never quit). What went wrong here?

Stepping through the code a bit, it turns out that the AV EXA is a bit faster than the SA one. Normally that's no problem, but since this test has several fast heartbeats (inputs between -30 and -39), at some point the AV EXA reads from the M register twice before SA has a chance, desyncing everything.

One way to fix that would be to have the input EXA only send to ONE of the others and have that one contact the other one. Serial messaging. That feels slow. I have a better idea.

The AV EXA does 2 writes before getting into the loop, the SA one only 1. That's why AV is faster. I'm going to try having the SA EXA also write two values before getting into the loop. This would fail if the input is ever between -29 and -20... but the AV EXA already can't handle that, so let's try it and hope for the best.

Image

Aaaaand.... it works! 88/32/5.

Image Top percentiles are 80, 24, and 5. For the Phage levels, while the EXAs should run forever, the cycle count is based on how long it takes to fill out the test result table to the right.

For speeding it up, I tried some loop unrolls first.

Code: Select all

@REP 4
COPY -70 #NERV
SUBI T 1 T
FJMP SALOOP
@END
and also for AV. Or unroll the big SALOOP/AVLOOP. Can't use @REP there because the MARK names need to be different for each duplicate, but a manual unroll still works. Anyway, best I got with either attempt was 86 cycles. This ain't it.

Let's try something completely different. Can we parallellize? Well, not really with the M register. But we can have a lot of EXAs running around at the same time.

I tried some things the fact that the amount of time the EXAs are busy writing depends on the input caused me issues. If you send EXAs to the output nerves as fast as possible, the second EXA will start writing before the first is done. That won't work. We need to slow them down - but not too much. Perhaps the output EXA could signal when it's done? But that would be through the M register which is always a bit slow - taking 2 cycles at the least.

I came up with this instead.

Code: Select all

;XA

LINK 800
MARK NEXT
DIVI #NERV -10 X
SUBI X 3 T

REPL SA
REPL AV

JUMP NEXT

MARK SA
LINK 1
LINK 1


COPY 40 #NERV
COPY -70 #NERV
JUMP COUNTDOWN

MARK AV
LINK 3
LINK 3

COPY -70 #NERV
COPY 40 #NERV

MARK COUNTDOWN
@REP 5
COPY -70 #NERV
MODI -1 T T
@END

JUMP COUNTDOWN

;XB

LINK 800
REPL AV
REPL WAIT

LINK 1
REPL WAIT
REPL WAIT
REPL WAIT

LINK 1
REPL WAIT
MARK WAIT
JUMP WAIT

MARK AV
LINK 3
REPL WAIT
REPL WAIT
REPL WAIT

LINK 3
REPL WAIT
JUMP WAIT
XB simply fills up all the hosts with clones until there's only one free position and then gets into an infinite waiting loop. XA reads a value from the input, parses it, then clones itself and sends the clones off to the output. Since there's only one free space per host they'll just queue up in order, and LINK to the next host the first available cycle.
I needed to keep one XB in the input host as well, because otherwise there's some issues with the wrong XA clone LINKing first. This way, only one clone can be formed at a time.

Since each XA clone can die after doing its thing, this means I can consolidate the countdowns into a single loop, which can be unrolled quite a few times. And I use the MODI trick GuavaMoment showed us in the Trash World Inbox to decrement-or-die in a single cycle.

Image

And this gets me a score way below the top percentile, with only 69 cycles. Nice!

To my surprise, when I looked at my stats after this, it listed my lowest size as 25. How did I do that?

Turns out it was actually the parallel-but-wait-for-M solution I mentioned being too slow. It's this code:

Code: Select all

LINK 800
MARK NEXT
DIVI #NERV -10 X
SUBI X 2 T
REPL SA
REPL AV

VOID M
VOID M
JUMP NEXT

MARK SA
LINK 1
LINK 1
COPY 40 #NERV
COPY -70 #NERV
JUMP COUNTDOWN

MARK AV
LINK 3
LINK 3
COPY -70 #NERV
COPY 40 #NERV

MARK COUNTDOWN
COPY -70 #NERV
SUBI T 1 T
TJMP COUNTDOWN

COPY 0 M
131/25/29. The original EXA won't jump to NEXT until it reads from M twice, which means both clones finished. I can't replace the two VOID M's with something like SUBI M M X, the game specifically disallows reading from M twice in the same instruction. The top percentile value is 24 so further improvement is possible but I'm not sure what.

Image Do you ever wish you were a computer?
Image A functioning computer, I mean.


Image

Image Well, do we, thread?
And for next time...

Image Well, this is flattering.
Image Someone found a bunch of my network nodes and sent in a tip to Ghast!
Image Too bad I can't have people knowing about me.
Image You're going to have to hack me out of that message.


Image

Image And that's the two votes for this week.

User avatar
Nah and Are you Spying?

Puzzles are definitely stepping up, nice work on the records though! I imagine that the informant here is our war-dialer buddy on the chat, I'm not sure purging the message will be enough, we could be late, or our buddy could send another message, and regardless of what happens, its gonna arouse serious suspicions

User avatar
Part 16 - TRASH WORLD NEWS - Unknown Context


=== Trash World Inbox ===
GuavaMoment wrote: 23 Lines by having a main EXA send out AVN and SAN pairs. In pairs, the first value (AVN) is -70, then two 40 values (SAN then AVN), then -70 until you're done. AVN and SAN exas die graciously by trying to link to a non-existent node or just by running out of code. And a swizzle for style points.

Code: Select all

LINK 800
COPY -70 X

MARK COPY
SWIZ #NERV 2 T
ADDI 1 T T
REPL AVN
COPY 40 X

MARK REPL
REPL SAN
REPL AVN
COPY -70 X
ADDI 1 T T
TJMP REPL
REPL SAN
JUMP COPY

MARK SAN
LINK 1
LINK 1
COPY X #NERV

MARK AVN
LINK 3
LINK 3
COPY X #NERV
My low cycle solution was much worse than what you did.

Late edit: As pointed out a few posts below, change REPL SAN and JUMP COPY lines with REPL COPY to save one more line.
Nice, 22 lines with the additional line saved. The swizzle is used to put the tens in the ones place, meaning you can have a simple countdown on T without needing any TEST instructions. Of course a DIVI #NERV 10 T would've done the exact same.
silentsnack wrote:You can use some fallthrough structure to reduce size too

Code: Select all

LINK 800
MARK A
DIVI #NERV -10 X
SUBI X 2 T
REPL B
LINK 1
LINK 1
COPY 40 #NERV
COPY -70 #NERV

MARK BEAT
COPY -70 #NERV
SUBI T 1 T
TJMP BEAT

LINK -1
LINK -1
JUMP A
MARK B
LINK 3
LINK 3
COPY -70 #NERV
COPY 40 #NERV
JUMP BEAT
119/22/41
Ah, and to prevent EXAs interfering with each other you just have the same EXA going back home every time. Luckily the return link IDs happen to be different (-3) for the other path - that's not the case in every level.
silentsnack wrote:And to reduce time, the usual parallel countdown/kill shenanigans can work

Code: Select all

LINK 800
DIVI #NERV -10 X
SUBI X 2 T
REPL A

MARK LOOP
SUBI T 1 T
TJMP LOOP
DIVI #NERV -10 X
SUBI X 2 T
REPL LOOP

MARK A
REPL B
LINK 1
LINK 1
REPL TIMER
COPY 40 #NERV
COPY -70 #NERV
COPY -70 #NERV
JUMP BEAT
MARK B
LINK 3
LINK 3
REPL TIMER
COPY -70 #NERV
COPY 40 #NERV
MARK BEAT
COPY -70 #NERV
JUMP BEAT

MARK TIMER
SUBI T 1 T
TJMP TIMER
KILL
63/32/31
This was what I was struggling with. I was thinking about having one counter and the problems of it killing both "A" and "B" EXAs at once, but using one counter for each solves that.
silentsnack wrote:...and after randomly thinking about this again a couple of days later, it occurs to me that there might be a more efficient way make the timers and #NERV writing run both in parallel and serial, but actually implementing that plan ends up requiring a special-case for the first pair:

Code: Select all

LINK 800

REPL A0
DIVI #NERV -10 X
SUBI X 2 T
REPL A

MARK LOOP
SUBI T 1 T
TJMP LOOP
DIVI #NERV -10 X
SUBI X 2 T
REPL LOOP

MARK A
REPL B
LINK 1
LINK 1
MARK WAIT_A
SUBI T 1 T
TJMP WAIT_A
KILL
JUMP ADATA

MARK B
LINK 3
LINK 3
MARK WAIT_B
SUBI T 1 T
TJMP WAIT_B
KILL
JUMP BDATA

MARK A0
REPL B0
LINK 1
LINK 1
NOOP
NOOP
MARK ADATA
COPY 40 #NERV
COPY -70 #NERV
COPY -70 #NERV
JUMP BEAT

MARK B0
LINK 3
LINK 3
NOOP
NOOP
MARK BDATA
COPY -70 #NERV
COPY 40 #NERV

MARK BEAT
COPY -70 #NERV
JUMP BEAT
62/50/45
:psyduck: Nice. The very same EXAs first kill the previous ones and then start sending the next heartbeat. Yeah, that'd be very fast. Of course the special first case is needed to get the timings to line up perfectly. Meanwhile the CNS EXA is also doing a countdown so it starts the new round at exactly the right time. And your solution barely fits in the 50 cycle limit.
berryjon wrote:Also, if I'm reading the nature of the code right, we've just created an adhoc pacemaker to stabilize the motions of the heart to keep blood flowing.
Yes, that seems to be the case.


=== TRASH WORLD NEWS - Unknown Context ===

Image Do you ever wish you were a computer?
Image A functioning computer, I mean.


Image

Image Four votes for "Nah", one for "Sometimes".

Nah...

Image That's okay.
Image I never wished for a human body, myself.
Image But they're interesting to learn about.
Image Such strange design choices.


It's... complicated.

Image

Image

Hm, a Trash World News assignment again... but this time it's not a tutorial anymore.

Image Well, this is flattering.
Image Someone found a bunch of my network nodes and sent in a tip to Ghast!
Image Too bad I can't have people knowing about me.
Image You're going to have to hack me out of that message.


Image

Image Four votes for "Are you spying", one for the other choice.

Are you spying on Ghast?

Image I live on computer networks.
Image Sometimes I see things go by.
Image That's all.


Image
OST: Code and Registers

My assignment:
- Find and replace the keywords in the target message (file 212) as directed by EMBER-2.
- A list of keyword pairs indicating which words should be found and what they should be replaced with is available in file 300. For example, the keyword AI should be replaced with the keyword COLLECTIVE. Each keyword will only occur once, but may occur in any order.
- Also, move file 200 to the outbox.


... that third point is very random. Let's look at the files though, there's a whole bunch in Ghast's computer.

Image
File 300 is the replacement list Ember prepared for us. 200 literally says "Move this file to the outbox". I really don't get the point of this file.
209, 212, and 217 contain mail, and 212 is the one we gotta change.

Finally, file 237 is in a host called PRIVATE and it says:
"Hi Ghast. I'm sorry to hear this news. And I'm sorry that this is the occasion that brings us back into contact with each other. We can't change the past, but we can focus on the good memories instead of the bad ones.
Though it may sound strange for me to say it, I'm glad you've found a purpose in life, even now. It's never too late for that. -K"


Um. I have a feeling I was not supposed to see that.

In the other test cases, all files are the same except 300 and 212, but they just have their contents shuffled. So there's no other messages to be found.

There's a lot of ways to approach this. Let's just start and see where we end up. I'll begin with getting the outbox file out of the way and getting an EXA's hands on 212.

Code: Select all

LINK 800
REPL OUTBOX
LINK 799
GRAB 212
[...]

MARK OUTBOX
GRAB 200
LINK 800
The easiest way I can think of to handle this is to search through the file once for every word I need to replace. Sure, it's slow, but it should work.

Image

XB's only purpose is to send the contents of 300 over M.
XA gets the first value and just searches for that in the file. Now to build the replace logic.

Image

It sort of works. The message now reads like some nonsense from a crazy person, so Ghast'll probably ignore it. Except XA gets stuck here after XB finishes and dies.

Code: Select all

;XA

LINK 800
REPL OUTBOX
LINK 799
GRAB 212

MARK REPLACE
COPY M X
TEST X = 0
TJMP OUTBOX
MARK SEARCH
TEST F = X
FJMP SEARCH
SEEK -1
COPY M F
SEEK -9999
JUMP REPLACE

MARK OUTBOX
GRAB 200
LINK 800

;XB

GRAB 300
MARK LOOP
COPY F M
TEST EOF
FJMP LOOP
COPY 0 M
A simple solution is to have XB send a final control message. That means XA has to test for it. If it finds the control message it jumps to the outbox MARK - and will then die because it cannot grab another file while it's still holding one. Success!

Image

Image This runs at 561/24/3. Top percentiles are 541, 20, and 3. Hmm, I guess you can't make it all that much faster.

While I was thinking about improvements I suddenly remembered the TEST MRD instruction. That sets T to 1 if something is trying to send on M this cycle, 0 otherwise, WITHOUT actually reading the value from M. It's not often useful because the timing just doesn't line up, but in this case, because XB spends all its time waiting to send, we can use it as a "are we done" check.

Code: Select all

;XA

LINK 800
REPL OUTBOX
LINK 799
GRAB 212

MARK REPLACE
TEST MRD
FJMP OUTBOX
COPY M X
MARK SEARCH
TEST F = X
FJMP SEARCH
SEEK -1
COPY M F
SEEK -9999
JUMP REPLACE

MARK OUTBOX
GRAB 200
LINK 800

;XB
GRAB 300
MARK LOOP
COPY F M
JUMP LOOP
This improves the cycles and size: 555/22/3

To speed things up further I decided to make a separate EXA to move 200 to the outbox (saving one cycle from XA which doesn't have to do the REPL anymore), and to unroll part of the REPLACE loop.

Image

In most cases, the second iteration in REPLACE jumps back into the first one with the FJMP SEARCH. The only exception is when the search word is at the very start of the file. That's why further unrolls don't help. 548/31/4.

Moving the TEST MRD at the top of XA to the end of the loop skips two cycles in the first search, bringing the total to 546. And we can skip another cycle by preventing the SEEK -9999 from running during the last loop. That requires a bit of fiddling, though.

Code: Select all

;XA

LINK 800
LINK 799
GRAB 212

MARK REPLACE
COPY M X
MARK SEARCH
TEST F = X
FJMP SEARCH
SEEK -1
COPY M F
TEST MRD
DIVI 0 T T

SEEK -9999
COPY M X
TEST F = X
FJMP SEARCH
SEEK -1
COPY M F

TEST MRD
DIVI 0 T T
SEEK -9999
JUMP REPLACE

;XB

GRAB 300
COPY F M
MARK LOOP
COPY F M
COPY F M
JUMP LOOP

;XC

NOOP
LINK 800
GRAB 200
LINK 800
Since the TEST MRD now happens a single cycle after COPY M F, there can't be a JUMP between them in XB. A bit of reordering in XB solved that. I also changed the jump to END by a divide by zero. It doesn't save any cycles but it looks nicer. 545/32/4.

To get to the top percentile score I thought about making a counting EXA that kills the writing one. The problem is that there's nothing to count - there's no way to know when the writer is done searching.

But what it can do is take over the MRD check. Taking the above code, I removed the two pairs of TEST MRD and DIVI instructions from XA, added an extra NOOP to the start of XC (so it goes behind all others, it'll finish first anyway), and made a fourth EXA:

Code: Select all

;XD

NOOP
LINK 800
LINK 799

MARK LOOP
TEST MRD
TJMP LOOP
ADDI X 1 X
TEST X = 5
FJMP LOOP

NOOP

MARK LOOP2
TEST MRD
TJMP LOOP2

KILL
Basically, the inner LOOP tests if there's something on M every other cycle. Whenever XA reads from M, there's a cycle when there's nothing to read and TEST MRD is false. These cycles line up once for every word to replace, so I keep count in X. Once it hits five, the EXA goes to another TEST MRD loop, because now, when it's false, XA is done and needs to be killed as quickly as possible. We could do that from the original loop but the ADDI and TEST would delay the KILL. The single NOOP saves a cycle by making sure M goes empty just when this EXA is about to test (instead of when it's about to jump back to LOOP2.

541/41/7. Note that this M testing is quite fiddly, and by doing stuff such as unrolling the XB loop or changing other things by a single cycle you get slightly different results. I couldn't get it under 541 though.

Image So, my best results are 541 cycles and 22 size. Who can do better?

Image You didn't look at any other files while you were in there, did you?

Image

Image The first vote. This update isn't quite done yet, though.

Image

[x10x10x]: true

Yes. Definitely shitty software and not a hacker.

Image

Anyway, someone's at the door.

Image

Image Hey.
Image I have some things for you.
Image First of, we got the next zine, hot off the press.

Ghast hands me the next issue of his zine.

Image Someone helped me out.
Image I had a balance at the copy shop, but someone hacked it.
Image Speaking of which, I brought you something else.

Ghast holds up what looks like a handheld game console.

Image You remember the Redshift?
Image I know people say this thing was underpowered and just a gimmick...
Image But it has its charms.
Image This is the developer version, so you could make something with it, if you wanted.
Image The game studio I worked for never took it back after I quit.
Image I'll be happy knowing it's going to a good home.
Image Gotta deliver more zines now.
Image I'll catch you later.


Dude, a Redshift dev kit? I loved that thing as a kid!

Image

Image In the next parts, we have a whole new zine to explore, as well as a game console! But first, let's see what Ember has to say about the next assignment.

Image You're going to join in on a hacker battle?
Image But you never say anything in the chat room...


Image
Last edited by Carbon dioxide on Sat Apr 09, 2022 10:03 am, edited 1 time in total.

User avatar
"I might have'" and "I just lurk"

I feel like this mission doesn't quite make sense, either the message already reached Ghast and our efforts are highly likely to be in vain, or it hasn't because Ember has been locking them, in which case what are we helping for? But also doesn't fit Ember's general behavior, she seems woefully incapable of managing EXAs despite her otherwise peerless capability, going by the jobs she asks of us

the File 200 is probably supposed to represent some kind of Send command file but idk either, feels like a task they just kinda slapped on, probably to force you to do more then a simple "add word 1 to register, start testing file, seek -1 on success, overwrite, grab the next word, repeat, self-kill when file ends" solution

User avatar
Part 17 - Baby's first hacker battle


=== Trash World Inbox ===
GuavaMoment wrote: This level is pretty straightforward without any neat tricks.

Code: Select all

XA:
GRAB 300
MARK LOOP
COPY F X
REPL REPL
COPY F M
JUMP LOOP
MARK REPL
LINK 800
LINK 799
GRAB 212
MARK LOOP2
TEST F = X
FJMP LOOP2
SEEK -1
COPY M F

XB:
LINK 800
GRAB 200
LINK 800
Find a keyword, make a copy to go change that keyword, repeat. 18 lines, saving 3 over yours. Sorry about naming my copies REPL all the time.
555/18/3. The REPL'd EXA goes find the word and gets the replacement from M. That also means the original EXA knows when the previous one is done.
GuavaMoment wrote:

Code: Select all

XA:
GRAB 300
COPY F M
LINK 800
COPY F M
COPY F M
LINK 799
COPY F M
COPY F M
COPY F M
COPY F M
COPY F M
COPY F M
COPY F X
WIPE
COPY X M
KILL

XB:
NOOP
LINK 800
GRAB 200
LINK 800

XC:
LINK 800
LINK 799
GRAB 212
MARK LOOP
COPY M X
MARK LOOP2
TEST F = X
FJMP LOOP2
SEEK -1
COPY M F
SEEK -999
JUMP LOOP
Loop unrolling and transmitting data as fast as possible. The search takes the most time and it's hard to speed up. 540/32/7 a one cycle improvement (which I think comes from knowing how many keywords there are and wiping before transmitting the final keyword?)
And another small but neat improvement. I don't think this code needs much explanation.

While I was looking at your solution I had a very cheesy idea, though. I checked the slowest running test (only one of the test cases actually needed all 540 cycles). It is so slow because almost all the keywords are near the end of the file. Can we speed up just this one test case? That could be done by starting further ahead in the file and looping back to the beginning. I did a quick attempt but this made other tests much, much slower. Maybe there's a way to optimize that, I don't know. So, other idea, how about a reverse search? That's somewhat slow because you have to SEEK back a bit each loop - but can't hurt to try.

My first attempt failed. It uses the same XA and XB from Guavanaut, but with a changed XC:

Code: Select all

LINK 800
LINK 799
GRAB 212
MARK LOOP
COPY M X
SEEK 999
SEEK -1
MARK LOOP2
TEST F = X
SEEK -2
FJMP LOOP2
SEEK 1
COPY M F
SEEK -999
JUMP LOOP
For every new search value, it SEEKs to EOF, and then one back to the last word and starts testing. Every LOOP2 iteration it has to SEEK -2 to move back one word from where it last searched. That means it has to step forward one position when it finds the word but that's fine.

You see the problem here? SEEK -2; SEEK 1 causes an off-by-one if the keyword is the very first word in the file. New attempt with a workaround:

Code: Select all

LINK 800
LINK 799
GRAB 212
MARK LOOP
COPY M X
TEST F = X
TJMP FIRST
SEEK 999
SEEK -1
MARK LOOP2
TEST F = X
SEEK -2
FJMP LOOP2
SEEK 1
COPY M F
SEEK -999
JUMP LOOP

MARK FIRST
SEEK -1
COPY M F
JUMP LOOP
Now it checks the first position first. If that's the one, it replaces it in the FIRST 'subroutine' and jumps right back to search for the next. In that case it doesn't need to jump back to the beginning of the file because we never have to replace the same position twice. If the first word doesn't need replacing it SEEKs to the end and searches backwards.

504/41/7. Perhaps this could be optimized more but I'm quite happy I found this at all.
megane wrote:The joke is that this is the first tutorial mission network, so in addition to messing with Ghast's email you have to complete the now-hilariously-trivial tutorial task.
:doh: Of course! It's the exact same as the very first tutorial assignment. I guess that's how Ember found us a way in, but if we don't solve the tutorial, Ghast might get suspicious.


=== Hacker battle at KGOG-TV ===

Image You didn't look at any other files while you were in there, did you?

Image

Image Everyone voted for "I might have". Very honest.

Image Oops!
Image Maybe don't tell him you accidentally saw some of his private files.
Image No reason to upset him.
Image He needs to focus on the next issue of the zine anyway.


Yeah, ok.

Speaking of the next zine, I got it right here.

OST: Apartment

Nice cover:

Image
Image

Image I'm assuming the physical version came with actual 3D glasses.

Image

It's completely filled with cool content again. I don't think Ghast's the best hacker out there - but he's a damn good editor. Also, I can't believe it's been only one month since the first issue. Feels like three months at least.

Anyway, we'll get to the hacking writeups when we need them. As for the other content, I'll be sharing that bit by bit again just to not overwhelm you with text. Let's start with Ghast's letter.

Image

I'd love to stay curious, but I'm just trying to survive here, man.

Let's see what's going on online.

Image

Image

Well, mutex is challenging us to a "hacker battle". Can't say no to that.

Image Hacker battles have a different kind of leader board. They show who you've beaten and when. Apparently this is stored in the Steam cloud separately from your regular save file because it has a date corresponding to my initial playthrough (and I'm not using that save). Other than mutex8021, it shows my steam friends. I removed those from the pic for privacy reasons.

Image You're going to join in on a hacker battle?
Image But you never say anything in the chat room...


Image

Image Two votes for "I just lurk", one for "EXAs speak louder".

I just lurk these days.

Image I guess the only thing that really matters is your ability.

Image

As it should be.

Image It's a nice ideal.


Oh hey cool, Ember is all 3D with these glasses Ghast gave me.

Right, time to find out what this is all about.

Image
OST: Getting Started

This is a completely different mode of the EXODUS simulator. There's some things to go through. Let's start with some info from the zine.

Image

Image

The left side looks very familiar. Some files and the EXA programming environment.

Image

On the right we see some new things. The dark/blue host is our opponent's. We can never go there, but they can link from it to the network we're hacking.

At the bottom we see there's 100 test runs, like normal. New is the win count: this is increased by one for every test run where you get more points than your opponent. The goal is to get a win count that's greater than half of the number of test runs.

Next, the number of cycles is limited now - that's how long the program will run for during any test run before the points are added up. The size count is limited as always.

To the far right you see the points you and your opponent have and the storage limit. The storage limit is the number of EXAs you're allowed to create. It's 3 here - that means I can't have more than 3 EXAs to start with, and if my code REPLs to more than 3, the REPL instruction will pause that EXA until there's space again. As for how to get points, we need to read the instructions:

To win this battle you must make your movies play for longer than your opponent's. A movie will play when that movie's file is the only movie file sitting in a channel host.
- Gain one point every cycle for each of your movies that is playing (files 210 and 211).
- Lose one point every cycle for a movie that isn't yours (files 230, 231, 265) is held by an EXA you control or is sitting in your host.
- Lose one point every time one of your EXAs executes a KILL instruction.
Note that you may only battle your Steam friends after beating the NPC opponent. To view the list of possible opponents, click the "SELECT OPPONENT" button above.
For more information see "Hacker Battle Domination" in the second issue of the zine.


Alright, so I can use KILL instructions to kill the opponent's EXAs but it will cost me points.

Image Hey game, stop breaking the fourth wall. I'll get to Steam friends battles in a bit. Let's start with mutex.

If we don't do anything, mutex uses two EXAs to move 230 and 231 to the channels and they get 2 points per cycle (since file 265 doesn't give anyone points).

Image

In case you're wondering, 265 is a movie of a bug moving next to a plant, 230 looks like some military device shooting missiles, and 231 is a mechanical monster walking throuch a city.

To prevent mutex from getting points, I could either kill their EXAs or grab their files. Since holding files that aren't mine costs points, and I can't beat mutex's first EXA to grabbing a file, I'm better off just killing them. I have to execute the KILL instructions while I'm in the same host as the opponent's EXAs. There can't be any other EXAs of mine there, because as the language reference guide in zine 1 says, for some reason KILL prioritizes your own EXAs.

So, let's start with this simple solution.

Image

XA kills both of mutex's EXAs (giving me negative 2 points), then moves one of my files into an empty channel host. XB waits for the kills, then moves the other file over. As soon as both EXAs drop the files they start playing, and I get 2 points per cycle.

Image

My movies are some swaying grass with figures in the background, and a Japanese looking scene with snow falling.

So you might think it's a good idea to get rid of file 265 too, or dump another movie in that host to cause a conflict. Turns out it isn't. Grabbing 265 or another opponent's file costs points. And the instructions for this battle are actually incorrect. You don't lose any points for having someone else's movie playing, the only thing that happens is that the opponent GAINS points if they have their movies playing. Since 265 is nobody's, no-one is gaining any points for it and I should just leave it alone.

This solution nets me 188 points for every test run, while mutex gets zero. By the way, the other test runs are almost identical. All that changes it the number in the third entry of each file, and the locations of mutex's and my host change around (while keeping the link IDs the same). I suspect that last change swaps who moves first within a cycle, but it doesn't matter for us.

Image

I win every single test run, getting an S+ score on this battle.

Image

Image This first hacker battle is very easy. It's more of a tutorial for a new mode than anything else.

Image As for the multiplayer mode, you're not battling at the same time. What happens is that after I beat the NPC, my solution becomes available for Steam friends to battle against. That means their goal becomes to specifically beat my solution. If you noticed - the battle field is basically symmetric, the only difference is some file IDs but the game can just replace them when simulating someone being "the other side".

I tried to pit my solution against someone else's and I lost every test run. It's hard to say what they did exactly - you can't see the opponent's code. But they use three EXAs, one of which tries to kill mine and another which constantly replicates, moving the repls into the channel hosts.

The way to handle this is to build another solution to specifically fight theirs. For instance, I changed XA to kill just once (it gets killed by an opponent in the same cycle) and then sent in a second EXA that aggressively kills both of the remaining opponent's EXAs.
I managed to secure a win, 76 out of 100 test runs. Good enough for a B. I can't really tell you why 'only' 76 runs, the interaction of two sets of separately programmed EXAs gets very unpredictable.

Either way, by doing so my new solution is uploaded to this specific Steam friend, so if they decide to ever reopen the game and scroll to this particular assignment, they can see I beat them and they can try to make a new solution to beat mine. I don't have much experience with this because the few Steam friends I have that also own this game apparently were happy to play through it just once, but I can imagine this way you could get a fun back-and-forth on these hacker battle levels.

Back to the plot.

Image You're off to a good start.
Image I knew you were good at what you did.
Image Even after you forgot, you picked it right back up.
Image That's why I contacted you.


Image

Image The first vote for today.

Image For the second vote, Ember has a question about the Redshift handheld Ghast handed us.

Image Why do you think Ghast gave you this?

Image

Image

[Ghast] let's just say I'm pretty confident.
[x10x10x] cool im gonna hacker battle moss too


Oh... um, I don't think I ever introduced myself properly to you all, readers. Not that you would've believed it was really me, seeing how much I had to relearn at the start. I'm the hacker known as Moss. Nice to make your acquaintance. You've seen my name in the chat's user list, I just lurk all the time.

User avatar
At no point did the instructions say you lost points for your opponent's movie playing, just for you touching them, or trying to hide them in your Host room, since the enemy can't access that area, the instructions aren't incorrect

This is a really interesting mode that you can do some fun with I imagine, competing into increasingly complex and esoteric counter-solutions seems like a wild trip, and then at some point someone undermines it all with regressing to basic solutions, like a really basic counter to your solution for your friend you mentioned would be to just wait a few cycles, the Exas would KILL themselves and you could walk in freely

As for replies: "But what do you really want?" and "I'm not sure"
Saying Ghast thought we might like playing with it feels naive, in a certain sense it might be correct, but I don't think he handed it over as entertainment, but rather as a tool or experiment

User avatar
Part 18 - TEC Redshift - Dev kit

=== Trash World Inbox ===
Cloudmonkey98 wrote: At no point did the instructions say you lost points for your opponent's movie playing, just for you touching them, or trying to hide them in your Host room, since the enemy can't access that area, the instructions aren't incorrect

This is a really interesting mode that you can do some fun with I imagine, competing into increasingly complex and esoteric counter-solutions seems like a wild trip, and then at some point someone undermines it all with regressing to basic solutions, like a really basic counter to your solution for your friend you mentioned would be to just wait a few cycles, the Exas would KILL themselves and you could walk in freely
Huh. reading back, you're right about the instructions, my bad.

As for the hacker battles, I agree with GuavaMoment here:
GuavaMoment wrote: Hacker battles are a neat idea, buh ehh.....it's seemingly impossible to make a robust solution. It's like trying to optimize rock paper scissors, you're always going to be vulnerable to one tactic when using another.
Nth Doctor wrote: They're here!
Neat! I have the digital copy of Zach-Like. It's free, and it contains all sorts of behind-the-scenes information of Zachtronics game design and even some early games and prototypes.

Since it doesn't really make sense to talk about optimizations for the hacker battles, let's jump straight into the new update.


=== TEC Redshift - Development kit ===

Image You're off to a good start.
Image I knew you were good at what you did.
Image Even after you forgot, you picked it right back up.
Image That's why I contacted you.


Image

Image Three votes for the second option.

But what do you really want?

Image All I've wanted is for you to do a little hacking for me.
Image That's it. Really.


Well, that doesn't explain anything.

Image

Leafing through the zine I found the letters page.

Some interesting stuff there. First of all, heh, the letter Ember made me edit actually got printed in the zine. The original writer is in for a bit of a shock. Also, the tip about the KGOG tv station is probably where mutex got the number for the modem through which we did our hacker battle.

Image I also like the reference to "red boxes" and "blue boxes". They were real things. Back in the day phone exchanges were controlled through specific tones (nowadays it's usually a digital signal of some kind). In the USA, red boxes could be used to make an exchange think you put a coin in a pay phone, so it would let you place a call for free. Blue boxes could be used to place long-distance calls and bill them to some other number.

Image

Anyway, I got this developer Redshift kit from Ghast, let's see if I can do anything with it.

Image Why do you think Ghast gave you this?

Image

Image Two out of three votes for "I'm not sure".

I'm not sure.

Image Does he get something out of it?
Image I suppose he enjoys helping people.
Image Such a nice guy.
Image Too bad the dev kit is password protected.
Image He should have thought of that.
Image Better not let that stop you...


Yeah, no worries Ember, we'll just jailbreak this thing real quick.

There's a whole section about the Redshift in the zine, but most of it is about how to develop games for it. We're not quite there yet, but here's the frst page just so you have an idea of what we're dealing with.

Image

The small print under the ad says: "Batteries not included; 3D mode not intended for use more than 15 minutes at a time; EXA is a registered trademark of Axiom, Inc. Used with permission."

Image An early attempt at a 3D handheld where using the 3D mode for too long would cause problems? This thing seems to be some sort of mix of a Nintendo Game Boy and a Virtual Boy.

Image
OST: Code and Registers

No files or anything in this thing. Our assignment simply says:
There is an unknown three-digit code (such as 4-7-3) that, when entered one digit at a time into #PASS, will unlock the link between debug and secret. Find the three-digit code and create a file in your host that contains the code as a sequence of three values, followed by the development kit's RDK ID.

Okay, simple brute-forcing it is.

Image

Entering some numbers into #PASS makes them appear on the little display in the secret host. If the code is wrong nothing will happen, and the fourth digit is simply the start of a new attempt. Any negative numbers or multi-digit values are ignored by #PASS.

Well, we got an instruction to grab specific digits from a number, why not use that?

Code: Select all

LINK 800

MARK TOP
SWIZ X 1 #PASS
SWIZ X 2 #PASS
SWIZ X 3 #PASS
ADDI X 1 X
JUMP TOP
The SWIZ function takes a mask as second operand. If the mask is 0002 (or just 2) it says "take 0 for the thousands, hundreds, and tens place, and take the value from the input's tens place (second digit from the right) for the output's ones place." The Language Reference Guide in the second post has some examples.

Since the #PASS expects digits from left to right while this takes the input from right to left, I actually enter the numbers in reverse. It shouldn't matter, it will try all numbers anyway, just in a different order. But since I need to store the password I'll swap the SWIZ calls around so I get the right password in X.

This unlocks the secret host, but this EXA keeps going forever. The #PASS register keeps accepting digits even after entering the right password.

I need to somehow stop this EXA at the right time, save its password, and go get that id from the secret host. Let's first see what we can find in the secret host, now that I unlocked it.

Image

Image

The link id between the debug and secret hosts is simply 800 again. There's four files in there: two core dumps, some game's save file, and a file containing the RDK id.

Image

This is my first working solution. After some trial and error I decided that while a multi-EXA solution might be faster, it's gonna be hard to get the passcode out of the initial EXA at the right time. So I went for a single-EXA solution. Every loop, it tries to send a replica into the secret host. If the link is closed, the replica dies immediately.
Otherwise it gets the ID from the file. Then it needs to explicitly DROP the file (if you LINK an EXA holding a file with a lock icon it dies).

It KILLs twice (because by the time it gets back, the original had just enough time to execute a new REPL instruction), then goes back to the home host to write the file (which contains each digit of the password and then the ID).

This runs quite slow at 5959/22/11.

Image I tried to output it as a gif, but since it shows all digits being entered on the password display, the result is over 10MB and over 2 minutes. It also crashed my game when I tried to go back to editing afterwards. Don't try this at home.

Anyway, getting the activity down should be a question of minimizing LINK and KILL instructions.

We don't need to LINK back home with an EXA that waits for M signals:

Code: Select all

;XB

MAKE
COPY M F
COPY M F
COPY M F
COPY M F
And to get rid of the KILL, the original XA needs to know when to stop. Since I'm using M now, the MRD test works.

Code: Select all

;XA

LINK 800

MARK TOP
TEST MRD
TJMP END
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 199
SWIZ X 3 M
SWIZ X 2 M
SWIZ X 1 M
COPY F M

MARK END
It's important to have the TEST MRD when the replica is already sending on M, but before XA can do another REPL (since that'll cause a LINK). This solution times it right and runs at 7938/23/2.

Next, let's look at cycle count. As I already said, if you swap around the SWIZ instructions, you basically change the order in which you test the passwords. Since I use the same SWIZ to write the data to the file I can easily try all permutations. Turns out 1,3,2 is the fastest at 5821 cycles, as compared to the 5959 solution.

But that's just a minor optimization. I'll need to do much better.

A basic loop unroll gets me to 5020/47/11.

Code: Select all

LINK 800

MARK TOP
@REP 6
SWIZ X 1 #PASS
SWIZ X 3 #PASS
SWIZ X 2 #PASS
REPL TRY
ADDI X 1 X
@END
JUMP TOP

MARK TRY
LINK 800
GRAB 199
COPY F T
DROP
LINK -1
KILL
KILL
LINK -1
MAKE
SWIZ X 1 F
SWIZ X 3 F
SWIZ X 2 F
COPY T F
Much better, but still a far cry from the top percentile. I believe the only way to get there is with parallelism.

Image

It took some fiddling but this solution runs at 3405/50/17.

It still uses the ADDI/SWIZ combo so each EXA keeps a full counter. I could have one EXA just store the tens digit and one just store the hundreds digit but I don't think that would matter much since that would speed up those EXAs... for them to be limited by the one having to increment the ones digit every loop.

The EXA that makes it to the secret host immediately sends a REPL back to go KILL its buddies because the TRY REPLs add up fast now.

In fact, I just barely managed to squeeze out a single loop unroll. The loop unroll makes the REPL trigger even faster so I needed an extra KILL, which I managed to fit in by giving XC only one starting NOOP and hoping it would LINK after XB, which it did. I also needed an extra SUBI instruction because for some reason the ones digit is off by one now.

By the way, there's something cathartic about typing "KILL KILL KILL".

To make this even faster, well, as I said at least one EXA needs an increment instruction in its loop so it will act as the bottle neck. The only thing I can still remove from that loop is the REPL TRY:

Code: Select all

;XA
LINK 800

MARK TOP
SWIZ X 1 #PASS
ADDI X 1 X
JUMP TOP


;XB
NOOP
LINK 800

MARK TOP
SWIZ X 3 #PASS
ADDI X 1 X
JUMP TOP


;XC
NOOP
NOOP
LINK 800

MARK TOP
SWIZ X 2 #PASS
ADDI X 1 X
JUMP TOP


;XD
NOOP
NOOP
NOOP
LINK 800

MARK TOP
REPL TRY
ADDI X 1 X
JUMP TOP

MARK TRY
LINK 800
GRAB 199
REPL OT
SWIZ X 1 M
SWIZ X 3 M
SWIZ X 2 M
COPY F M
HALT

MARK OT
LINK -1
KILL
KILL
KILL
KILL
KILL
KILL


;XE
MAKE
COPY M F
COPY M F
COPY M F
COPY M F
2918/48/15. Not bad. According to the top percentiles it should be possible to get the cycle count further down to at least 2884, and it should be possible to have a solution with only 20 lines of code, too. I'll leave that to the threads.

Image So Ghast used to work at a game studio?

Image

Image I'll skip this vote because all three answers lead to the same follow-up question.

Yeah, he was a programmer. It says so in the zine.

Image I wonder what that was like.

Image

Image This one is for the thread.

Image Next time, we explore the Redshift dev kit. Ember would like to know why.

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


Image

Locked