Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
Tatters
Jan 29, 2020
my favorite level is coming up!
I'mma vote for 'you get used to it' and 'this is silly'.

Adbot
ADBOT LOVES YOU

idhrendur
Aug 20, 2016

You get used to it
This is silly

Quackles
Aug 11, 2018

Pixels of Light.


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:
;TRANSMITTER SETUP
LINK 800

MARK LOOP
ADDI #NERV 35 X
MULI X 117 M
JUMP LOOP
code:
;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)

Quackles fucked around with this message at 01:04 on Jan 1, 2022

GuavaMoment
Aug 13, 2006

YouTube dude
You get used to it
This is a lovely haiku
This is so silly

Quackles posted:

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.

124 / 14 / 6.

My best is 123/14/6 by adding and subtracting 9949 (and then subtracting and adding 9879) to every number. However, some of my wizard steam friends have scores of 36 cycles - that's about how fast you could simply feed numbers from one location to the other, so I imagine there's some kind of state-machine-esque solution here.

Carbon dioxide posted:

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.

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

GuavaMoment
Aug 13, 2006

YouTube dude

GuavaMoment posted:

My best is 123/14/6 by adding and subtracting 9949 (and then subtracting and adding 9879) to every number.

Oh, I can get 96/50/6 by unrolling some loops, I guess I never went back to this one to try advanced concepts.

silentsnack
Mar 19, 2009

Donald John Trump (born June 14, 1946) is the 45th and current President of the United States. Before entering politics, he was a businessman and television personality.

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


Quackles posted:

If a number goes above 9999 or -9999, it is set to 9999 or -9999.

Ah, yeah this is what I was missing. Uggggh.

code:
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
Previous smallest I could get was 12 lines, but this clearly beats it at 86 cycles / 10 size / 130 activity, and basically just plugging in the same multiply-divide trick and deleting all the now-superfluous kludgecraft:

code:
;====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.

...and as the other world-weary burnouts have said, "you get used to it" and "this is silly"

silentsnack fucked around with this message at 02:28 on Jan 1, 2022

Gideon020
Apr 23, 2011
Fifty Bucks for a modem?! My god this really is some kind of 90's cyberfantasy.

idhrendur
Aug 20, 2016

Gideon020 posted:

Fifty Bucks for a modem?! My god this really is some kind of 90's cyberfantasy.

I used to occasionally see $20 modems in the store during the late 90s. Yes, the box listed basic functionality as if it was advanced features. No, I never bought one to see how bad it was.

Carbon dioxide
Oct 9, 2012

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 posted:

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:
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 posted:

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:
;TRANSMITTER SETUP
LINK 800

MARK LOOP
ADDI #NERV 35 X
MULI X 117 M
JUMP LOOP
code:
;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 posted:

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:
;====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:
;====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:
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:
;====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:
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:
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:
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 posted:

code:
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 posted:

code:
;====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 ===

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




This vote was unanimous.

You get used to it.

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


Wait, Ember wants a body?



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.

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



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

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




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

This is silly.

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



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.



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



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.



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



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.

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




I don't know, was it?

Meanwhile...



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


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




And that's the second vote for today.

idhrendur
Aug 20, 2016

The exact handling of overflow can depend on the instruction used and the particulars of a give microprocessor. Though usually its just a difference between signed and unsigned, I don't recall it ever clamping.

I don't get it, no.

He's an old friend.

GuavaMoment
Aug 13, 2006

YouTube dude

Carbon dioxide posted:

If there are further improvements possible, I don't know them.

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


There's a really good pun over the name Snaxnet but that should come into play in the next Snaxnet level.

Junpei
Oct 4, 2015
Probation
Can't post for 11 years!
Kind of...? and I help people when I can.

AweStriker
Oct 6, 2014

I don't get it, no.; I help people when I can

Quackles
Aug 11, 2018

Pixels of Light.


AweStriker posted:

I don't get it, no.; I help people when I can

Seconding this.

ColTim
Oct 29, 2011

idhrendur posted:

The exact handling of overflow can depend on the instruction used and the particulars of a give microprocessor. Though usually its just a difference between signed and unsigned, I don't recall it ever clamping.

Some MMX/SSE intrinsics/vector instructions saturate rather than overflow (including one with a very bizarre name: maddubs)

idhrendur
Aug 20, 2016

ColTim posted:

Some MMX/SSE intrinsics/vector instructions saturate rather than overflow (including one with a very bizarre name: maddubs)

Is *that* what saturate is about. I kept seeing it while reverse engineering some firmware years ago and didn't have a clue. It wasn't really relevant to what I was trying to learn, but remember being a little confused.

Tatters
Jan 29, 2020
So, when I originally played this game I was playin with a friend who knows as little about coding as me, when we got to the nut free peanut bars we both broke out laughing the moment I read off the objective. We nearly died.

And now we're at the level I'm too stupid to figure out so I will 100% be copying some code from the LP...

Carbon dioxide
Oct 9, 2012

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.

Carbon dioxide
Oct 9, 2012

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 posted:

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

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




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

I don't get it, no.

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


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



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




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

I help people when I can...

Is that altruism? I've heard of that.
This is the first time I've seen it in the wild.
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?



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


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.



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

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



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



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.



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.



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.



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.



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.





The 'fixed' files.



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:
;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:
;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.

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

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




Here is the first vote for this update.



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




*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?




Isadora is voiced by Krizia Bajos.

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

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

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


Um. Thanks, I think.

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

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




And that's the second vote.

berryjon
May 30, 2011

I have an invasion to go to.
I don't Know
I doubt it.


I can't make heads or tails of programming, so I'm just along for the story.

Carbon dioxide
Oct 9, 2012

berryjon posted:

I can't make heads or tails of programming, so I'm just along for the story.

That's great! I hope my decision to keep the inbox section separate from the main story updates is helpful to you. And if there's anything else I can do to make it more accessible to as much people as possible I'd like to hear it.

NullBlack
Oct 29, 2011

I'm as confused as you are.
They're zebras, but they're also brothers.
just because we may not find it funny, doesn't mean we have to pretend we don't understand it.

Maybe




what's the time frame of this game? I was expecting to see some outcome from the Peanut Blasts, but I wasn't expecting it quite so soon. Then again, if the factory is in town (assuming based on Isadora), then distribution and discovery would happen pretty quick.

poor x10x10x just wanted some peanuts

silentsnack
Mar 19, 2009

Donald John Trump (born June 14, 1946) is the 45th and current President of the United States. Before entering politics, he was a businessman and television personality.

Carbon dioxide posted:

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.

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

Dunno, kinda vague but also Doubtful

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

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:
;====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. And now for the same solution but a couple of lines changed to use a slightly different routine to parse the file:

code:
;====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.

Quackles
Aug 11, 2018

Pixels of Light.


I vote for zebras and also brothers and maybe.

Tenebrais
Sep 2, 2011

NullBlack posted:

what's the time frame of this game? I was expecting to see some outcome from the Peanut Blasts, but I wasn't expecting it quite so soon. Then again, if the factory is in town (assuming based on Isadora), then distribution and discovery would happen pretty quick.

poor x10x10x just wanted some peanuts

From that last cutscene, the phage is probably messing with the protagonist's memories, so who knows how much time is passing.

GuavaMoment
Aug 13, 2006

YouTube dude

silentsnack posted:

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.

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

idhrendur
Aug 20, 2016

Zebras but also bro(ther)s
I doubt it

Junpei
Oct 4, 2015
Probation
Can't post for 11 years!
Zebras but also brothers, What kind of slogans?

biosterous
Feb 23, 2013




1 zebra bros

2 what kind of slogans?

NHO
Jun 25, 2013

Brother and Zebra are a printer brands.

Quackles
Aug 11, 2018

Pixels of Light.


biosterous posted:

2 what kind of slogans?



Carbon dioxide
Oct 9, 2012

Part 10 - SFCTA Highway Sign


=== Trash World Inbox ===

silentsnack posted:

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:
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 posted:

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:
;====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 posted:

And now for the same solution but a couple of lines changed to use a slightly different routine to parse the file:

code:
;====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 posted:

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


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




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

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

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


Same, tbh.



You are very welcome, Ghast.



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

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




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.

Really?
Let's find out.


Uh oh.


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.




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.



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.



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.



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.



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





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.

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

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:
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:
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:
; 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!

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


Uhh... sure?



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




Looks like Ember has more to say.



Another voice-acted cut scene.

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


...no. Please don't.

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




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

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




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

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



In that case, probably not?

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


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.

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

Can friendship exist without self-interest?



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

GuavaMoment
Aug 13, 2006

YouTube dude

Carbon dioxide posted:

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!

You can have one exa feeding in the row data:
code:
LINK 800
MARK LOOP
DIVI X 9 #DATA
ADDI 1 X X
JUMP LOOP
One feeding in the column data:
code:
NOOP
LINK 800
MARK LOOP
MODI X 9 #DATA
ADDI 1 X X
JUMP LOOP
One feeding in data from the file
code:
GRAB 300
NOOP
LINK 800
MARK LOOP
COPY F #DATA
TEST EOF
FJMP LOOP
WIPE
and one to kill everything at the end
code:
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...

GuavaMoment fucked around with this message at 23:00 on Jan 22, 2022

GuavaMoment
Aug 13, 2006

YouTube dude

Carbon dioxide posted:

code:
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.

Also here you can just test for EOF instead of X, then change TJMP DONE to FJMP LOOP. Remove the ADDI and JUMP lines to get a 10 line solution.

Quackles
Aug 11, 2018

Pixels of Light.


Looking up my old solution, I was able to get 94/34/1 with some tweaking of macros.

code:
GRAB 300
LINK 800

MARK ROW
@REP 9
COPY X #DATA
COPY @{0,1} #DATA
COPY F #DATA
@END

ADDI X 1 X
TEST X > 2
FJMP ROW
WIPE
It's a fairly simple solution that uses macro syntax with loop unrolling, copying (0, 1, 2, 3, 4, 5, 6, 7, 8) to the sign each time. It also only tests once per row.

silentsnack
Mar 19, 2009

Donald John Trump (born June 14, 1946) is the 45th and current President of the United States. Before entering politics, he was a businessman and television personality.

GuavaMoment posted:

You can have one exa feeding in the row data:
One feeding in the column data:
One feeding in data from the file
and one to kill everything at the end


86/30/6, still one cycle off top percentage though...

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:
;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.

edit: it can actually be 24 lines instead of 26 if you don't mind being harder for human readers to parse, since if XA and XB are both trying to LINK 800 then XB goes first, drop XA's NOOP and COPY setup and swap the ADDI and REPL lines.

...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:
;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

silentsnack fucked around with this message at 05:50 on Jan 23, 2022

Yvonmukluk
Oct 10, 2012

Everything is Sinister


Carbon dioxide posted:

Can friendship exist without self-interest?
Why couldn't it?

Junpei
Oct 4, 2015
Probation
Can't post for 11 years!
Yeah, Why couldn't it?

berryjon
May 30, 2011

I have an invasion to go to.
Why couldn't it?

Adbot
ADBOT LOVES YOU

Carbon dioxide
Oct 9, 2012

GuavaMoment posted:

Also here you can just test for EOF instead of X, then change TJMP DONE to FJMP LOOP. Remove the ADDI and JUMP lines to get a 10 line solution.

Your suggestion is basically equivalent to the 10-line solution I show in my update, right?

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply