Part 46: Cybermyth studios
Part 46 - Cybermyth studios=== Trash World Inbox ===
Quackles has a nice improvement for last week's task.
Quackles posted:
Optimization time!I had a similar solution to CO₂'s to begin with, which got similar performance. However, on re-evaluating it, I realized there's an easy way to make it better: since the tickets are a contiguous group of 5, between 210 through 298 in every case, we can just check for a hit by adding 5 if no match is found (so 210, 215, 220, etc). From there, we backtrack four spaces and grab numbers going up by 1 to find the lowest ticket number.code:
GRAB 300 LINK 800 @REP 5 KILL @END ;LOWEST NUMBER IS 210 ;NUMBERS ARE IN A CONTIGUOUS BLOCK ;WE NEED TO GO GRAB _A_ TICKET THEN BACKTRACK UP TO 4 SPACES ;WE START AT 213 TO IMPROVE PERFORMANCE IN THE WORST CASE COPY 213 X MARK TICKETLOOP REPL GETTICKET ADDI X 5 X TEST MRD FJMP TICKETLOOP COPY M X MARK TICKETLOOP2 REPL GETTICKET2 ADDI X 1 X TEST MRD FJMP TICKETLOOP2 VOID M COPY F X COPY X M SEEK -1 COPY M F COPY X F COPY M F SEEK -3 COPY F M COPY F M COPY F M WIPE MARK GETTICKET GRAB X SUBI X 4 M ;HAVE THIS CHAIN TO TICKETLOOP 2?? MARK GETTICKET2 GRAB X COPY 1 M COPY F #NEXT DROP LINK 800 COPY M X GRAB 200 ;SO WE HAVE A LITTLE PROBLEM. NO WAY TO GET FILE PTR LOC, SO WHAT IF TARGET LOC IS BEFORE 1ST ENTRY? ;WE GOTTA CHECK FOR THIS EDGE CASE. TEST F > #DATE TJMP EDGECASE1 COPY #DATE M MARK EDGECASE1RET MARK FINDAPPT TEST F = X TJMP GETAPPT SEEK 2 JUMP FINDAPPT MARK GETAPPT COPY F M ;'TEST' MARK COPYFWD SEEK -6 TEST F = #DATE TJMP WRITEAPPT SEEK -1 COPY F X COPY F T SEEK 1 COPY X F COPY T F SEEK -3 COPY F X SEEK 2 COPY X F SEEK -3 JUMP COPYFWD MARK WRITEAPPT SEEK 2 COPY M X TEST X = #DATE TJMP EDGECASE2RET ;EDGE CASE PART 2 - COPY NEXT DAY'S DATE SO TODAY'S IS AT LIST START COPY X F SEEK -3 COPY F X COPY F T SEEK 1 COPY X F COPY T F SEEK -9999 MARK EDGECASE2RET COPY #DATE F COPY M F ;NTH'S NAME COPY M F ;'TEST' HALT MARK EDGECASE1 SEEK -1 COPY F M SEEK -1 COPY #DATE F JUMP EDGECASE1RET
We start by trying to grab ticket 213, as this improves performance in the worst case where tickets 294-298 are present (we grab ticket 298, then subtracting 4 leaves us with the lowest number, ticket 294 directly), and go from there.
My file writing mechanism is drastically different from CO₂'s. I can't say which is faster. Instead of deleting NthDimension's entry from the file, the EXA that finds it sends it over M to its parent, which still remains in the main room with the ticket lineup. The parent EXA stores that row to a file - we'll read it back later.
The EXA reading the appointments starts at Kapoor's row and repeatedly copies the previous line over the current line, going back until we find today's date. There's no way to make the overwriting exa force-halt when it hits the beginning of the file (believe me, I did try), and there's no way to test if we are at the beginning of the file, either (aside from forcing the issue with SEEK -9999). So instead, we check for what in the code above is referred to as the 'edge case'; if Kapoor's entry would be the only one for today (as determined by the first line of the file not having today's date), we replace the date on that entry with today's date, before copying the 'bad date' into the file on Kapoor's row instead.
Either way, once the copier makes space for Kapoor's row, it finds the last incidence of today's date, then replaces the next row with Nth's entry (read in from the parent EXA over M). If the edge case is in force (the date from the parent EXA will not be today's), it copies the first row of the file to the second row and sticks Nth's record in the first row instead.
Once that's done, it's just a matter of cleaning up. 273/93/7.
=== Cybermyth studios ===
Time to help out Ghast... Wait, 1992? I was in 1998. Is this supposed to be a flashback or something?
I don't know, so let's just get to the task.
OST: Getting Started
The assignment this time:
- Each host contains two files: a list of accounts and a list of transactions. Although the entries in these files vary, the first value of each entry is a unique identifier that connects an account to one or more transactions.
- Determine the amount of back-pay owed to Ghast and Moss by subtracting the amount that they were paid (file 221) from the amount that they were owed (file 231). Then add their shell company (file 300) to the list of accounts payable (file 220) and issue a single payment to it for the total amount owed (file 221).
- Note that all monetary amounts are represented as two values: dollars, then cents.
That's quite a lot of information. Before I figure that out, let's look in the files. From the dates, it seems I'm definitely in 1992. The files in the RECEIVABLE host list some transactions related to companies. The RECORDS host has no transactions, but the files say that Ghast is on report for absenteeism and I (Moss) am on report for intoxication. Crap.
Something I hadn't noticed yet: my home host is named GHAST. Am I actually playing as Ghast? The same thing was the case for the other postgame tasks so far.
The expected goal shows that I actually have to change two things:
- Add the shell company TRASH WORLD CLEANING to file 220, starting with the first unused ID number.
- Add a transaction to file 221, with the ID of the shell company, a date (I guess today's from the #DATE register), and an amount.
That amount is the amount Ghast and Moss are owed, which I can get by subtracting the total paid amount from the total owed amount.
Looking at the data, the total owed amount is often more than the EXA's max value of 9999. At least in the first few tests, the amount paid is not. So, I should be able to solve this problem by writing the amount paid as a negative, then adding the amount owed to it, so I never cross the 9999 boundary.
For the total amount owed, it looks like the payroll has the same amounts for every week, AND everyone gets paid every week. That means I can just calculate the amount for one week and multiply that by the number of weeks.
Enough with the theory, let's write some actual code.
code:
;XA
LINK 800
LINK 801
; FIND GHAST'S ID
GRAB 220
COPY M X
MARK FINDPERSON
SEEK 1
TEST X = F
FJMP FINDPERSON
SEEK -2
COPY F X
MODE
TEST MRD
TJMP MOSSDONE
REPL GHASTID
MODE
; FIND MOSS TOO
SEEK -9999
COPY M X
JUMP FINDPERSON
MARK MOSSDONE
VOID M
MARK GHASTID
GRAB 221
COPY 0 M
;XB
GRAB 300
COPY F M
COPY F M
XA goes to the host containing the paid amounts and finds Ghast's and Moss's account ids. This is done through a basic search loop. But I realized that I'll never have enough registers in one EXA to test if a transaction belongs to either Ghast or Moss in one go. So I REPL the EXA containing Ghast's id (it barely fits in the host when the original EXA is holding a file). It grabs the other file, then sends a message over M in local mode. The TEST MRD in the search loop causes the search for Moss to end without a REPL and in MOSSDONE.
That means the original EXA holding the list of accounts now has Moss's id in X, the other one has Ghast's id.
The EXA containing Ghast's id changes to local mode, creates an EXA I called LOCALCTRL and starts sending Ghast's dollar amounts to it. LOCALCTRL adds them up and stops once it gets a zero. Because the M's interacted badly, I removed the MRD check from below the FINDPERSON, and added a check after the REPL, which tests if XB sent a 0. However, this doesn't get triggered because the EXA waits on the REPL command until a space is freed up. Something to think about later. At least now I have Ghast's amount of paid dollars.
code:
LINK 800
LINK 801
; FIND GHAST'S ID
GRAB 220
COPY M X
MARK FINDPERSON
SEEK 1
TEST X = F
FJMP FINDPERSON
SEEK -2
COPY F X
COPY X M
REPL GHASTID
; FIND MOSS TOO
SEEK -9999
COPY M X
TEST X = 0
FJMP FINDPERSON
HALT
MARK GHASTID
MODE
GRAB 221
REPL LOCALCTRL
MARK COLLECT_DOLLARS
TEST F = X
FJMP WRONGACCOUNT
SEEK 1
COPY F M
SEEK 1
TEST EOF
FJMP COLLECT_DOLLARS
MARK WRONGACCOUNT
SEEK 3
TEST EOF
FJMP COLLECT_DOLLARS
COPY 0 M
SEEK -9999
COPY 1 M
MARK COLLECT_CENTS
TEST F = X
FJMP WRONGACCOUNT_CT
SEEK 2
COPY F M
TEST EOF
FJMP COLLECT_CENTS
MARK WRONGACCOUNT_CT
SEEK 3
TEST EOF
FJMP COLLECT_CENTS
COPY 0 M
MODE
SEEK -9999
COPY M X ; OTHER ID
MODE
COPY X M
TEST X = 0
FJMP COLLECT_DOLLARS
HALT
MARK LOCALCTRL
MAKE
COPY 0 X
MARK ADDAMOUNT
COPY M T
FJMP ADDAMOUNTDONE
ADDI T X X
JUMP ADDAMOUNT
MARK ADDAMOUNTDONE
COPY X F ; DOLLARS IN F
COPY 0 X
TEST M = 0
FJMP ADDAMOUNT
SEEK -9999
COPY F X
COPY F T
ADDI F X X
ADDI F T T
SWIZ T 0043 F
SWIZ T 0021 T
SEEK -1
ADDI F X X
NOOP
Okay, with that done I need to find out how much they are due.
I can do a bit of cleanup first. That original EXA is still sitting at REPL GHASTID. Instead of letting it continue, I can have the other one just do a KILL. That means that I can remove the TEST and HALT after it. There's also a place where I had to do a TEST X = 0 and if that's true it can HALT. It saves some space to just DIVI T X T and use the divide by zero trick.
Dealing with the payroll is a bit tricky, because the accounts there have other IDs. I need to get Ghast's and Moss's name from file 300 once more.
You know what, doing that with M is going to cause timing issues. I'll just have another EXA handle that altogether.
Enter the extended XB. I'll go through it bit by bit.
code:
;XB
GRAB 300
COPY F X
COPY F T
REPL PAYROLL
COPY X M
VOID M
COPY T M
COPY M X
COPY X M
COPY 0 M
MODE
COPY 0 M
HALT
MARK PAYROLL
MODE
LINK 800
LINK 802
The MODE change followed by a final M message is for the PAYROLL REPL. Once PAYROLL is done, it'll come back to the home host and wait for this M, which indicates that XA is ready. After that, the original XB can HALT and the PAYROLL can take over. By the way, by reordering the code a bit I can get rid of this HALT by having the EXA fall through into harmless code where it dies.
Now, for the PAYROLL itself. It switches to MODE to LOCAL and will stay there. This way it can never interfere with the XB-XA GLOBAL communication.
code:
MARK FINDPERSON
SEEK 1
TEST X = F
FJMP FINDPERSON
SEEK -2
COPY F M
; FIND MOSS TOO
SEEK -9999
COPY M X
DIVI 1 M T
JUMP FINDPERSON
code:
MARK OTHER
GRAB 231
COPY M X
COPY 1 M
COPY T M
REPL CTRL
COPY 8 T
MARK WT
SUBI T 1 T
TJMP WT
MARK FIND
TEST F = X
SEEK 3
FJMP FIND
SEEK -2
COPY F M
COPY F M
COPY M X
COPY X T
TJMP FIND
After that it needs to wait a bunch of cycles. That's because before the FIND loop can start looking for Ghast's salary, the CTRL EXA first needs to get Moss's ID. I'll get to that EXA in a second.
The FIND loop itself simply looks for Ghast's ID and then copies both the dollars and cents to M. If it gets Moss's ID (which is a non-zero number) it'll look for his amount too, otherwise this EXA will continue to another part of the code.
Before we go to that next part I'll show the CTRL REPL.
code:
MARK CTRL
COPY M X
COPY 0 M
MAKE
COPY M F
COPY M F
COPY X M
COPY M F
COPY M F
COPY 0 M
SEEK -9999
COPY F X
COPY F T
ADDI F X X
ADDI F T T
SWIZ T 0043 F
SWIZ T 0021 T
SEEK -1
ADDI F X X
WIPE
MAKE
COPY X F
COPY T F
Finally, the temporary file is WIPEd and a new one is made to store the total salary in. Having a file without intermediate garbage makes things simpler later.
code:
LINK -1
DROP
LINK 802
GRAB 230
COPY 0 X
MARK COUNT
ADDI X 1 X
SEEK 2
TEST EOF
FJMP COUNT
MULI X 4 M
DROP
COPY 0 X
LINK -1
GRAB 402
LINK 802
Before we continue, let's look at what the FIND EXA has been up to.
code:
; COUNT
SEEK -9999
COPY M X
MARK COUNTPAYMENTS
COPY 1 M
SEEK X
TEST EOF
FJMP COUNTPAYMENTS
COPY 0 M
HALT
Since X holds the value to SEEK and T is used for EOF, the actual counting is done by COPY 1 M in the loop, followed by a COPY 0 M to let the CTRL EXA know when it's done. The HALT here is optional if I structure the code so this EXA dies without further side effects after the COPY 0 M.
code:
MARK MORE
COPY M T
ADDI X T X
TJMP MORE
LINK -1
LINK -1
VOID M
MODE
COPY X M
COPY F M
COPY X M
COPY F M
WIPE
GRAB 300
SEEK 2
COPY F M
At that point this EXA truly takes control by switching to GLOBAL mode, sending the number of payments, then the total amount of dollars, then the number of payments again and then the amount of cents.
If it's not quite clear what I've just done, here's an example:
- Moss gets paid $1076.92 per week.
- Ghast gets paid $961.53 per week.
- In total, they get paid 1076.92 + 961.53 = $2038.45 per week.
- The payroll says they owe payment for 5 weeks.
In this case, the CTRL EXA sends: 5; 2038; 5; 45. That way, the receiving EXA knows to apply $2038 five times, and the cents amount too. Since 5 * 2038 is greater than 9999, this is the most straightforward way to handle it.
Finally, this EXA GRABs file 300 so it can send the shell company's name before it stops.
To handle all this, I added more code to XA.
If you remember, we left off XA with the dollars-already-paid amount in X, and the cents-already-paid in T. It's also holding a temporary file containing intermediate date, with the file pointer sitting at the end.
code:
;XA continued
KILL
SUBI 0 T F
SUBI 0 X X
MODE
COPY M T
COPY M F
MARK AGAIN
SEEK -1
ADDI F X X
SUBI T 1 T
TJMP AGAIN
COPY X F
The AGAIN loop adds the amount owed to the negative of the amount paid repeatedly. For example, if the amount paid is $100 and the amount owed is 5 payments of $60, the calculation is -100 + 60 + 60 ... and so on. The result, which is the actual final amount Ghast and Moss are owed, is stored in F.
code:
SEEK -3
COPY F X
COPY M T
SEEK 9999
COPY M F
MARK AGAIN2
SEEK -1
ADDI F X X
SUBI T 1 T
TJMP AGAIN2
SEEK -2
SWIZ X 0043 T
ADDI F T T
SWIZ X 0021 X
While testing I found out this bit of code failed in one particular case. Because I'm subtracting the cents separately from the dollars, it's possible to end up with a negative amount of cents. For instance, 56 dollars and -29 cents. That's actually supposed to be $55.81. There might be a way around this by doing something smart with the SWIZ code but I just slapped a workaround after it.
code:
; NEGATIVE CENTS
COPY T F
TEST X < 0
FJMP SKIP
ADDI 100 X X
SEEK -1
SUBI F 1 T
JUMP COMPLETE
MARK SKIP
SEEK -1
COPY F T
MARK COMPLETE
Now, all that's really left is clean-up and writing the final values to the file.
code:
MARK COMPLETE
WIPE
GRAB 220
SEEK 9999
SEEK -2
REPL DATE
ADDI F 1 X
SEEK 9999
COPY X F
COPY M F
code:
DROP
GRAB 221
SEEK 9999
COPY X F
COPY M F
COPY T F
COPY M F
code:
MARK DATE
LINK -1
LINK 804
NOOP
COPY #DATE M
COPY X M
The NOOP here is absolutely necessary. Without it, it would start sending just before the other EXA had the chance to read the shell company's name from M. If I had put the REPL DATE instruction any earlier I would've needed even more waiting cycles here. And I couldn't put it any later, because after that point, X gets overwritten.
That's every single piece of the code explained. While testing, I ran into one more bug. It is possible for the amount of cents in a payment to be 0. And I was already used 0 to let the other EXA know no more payments are coming. I fixed that by sending the amount + 1 and then subtracting 1 once the other EXA receives it. I had to do this for dollars as well because they share the receive code.
The full code is... hard to read because I shuffled blocks of code to remove a bunch of HALT instructions. It's very long, so I added it in an appendix at the bottom.
Oh. It's way too long.
Well, the good news is that even though I don't get a high score, the game still counts this task as completed. I'm glad, because I spent enough time on it that I really don't want to optimize it further.
I can see my score by looking at the detailed test run data. It's 536/246/16.
Outside the level, the histograms show up.
The top percentiles are 215, 92, and 4, but for size there's a tiny little peak in the histogram around 60. I can think of some optimizations but getting it down to 60-ish? I have no idea how. My speed though is close to tenth percentile, and I'm happy with that.
Next time, hydroponix.
=== Appendix ===
code:
;XA
LINK 800
LINK 801
; FIND GHAST'S ID
GRAB 220
COPY M X
MARK FINDPERSON
SEEK 1
TEST X = F
FJMP FINDPERSON
SEEK -2
COPY F X
COPY X M
REPL GHASTID
; FIND MOSS TOO
SEEK -9999
COPY M X
JUMP FINDPERSON
MARK GHASTID
MODE
GRAB 221
REPL LOCALCTRL
MARK COLLECT_DOLLARS
TEST F = X
FJMP WRONGACCOUNT
SEEK 1
ADDI F 1 M
SEEK 1
TEST EOF
FJMP COLLECT_DOLLARS
MARK WRONGACCOUNT
SEEK 3
TEST EOF
FJMP COLLECT_DOLLARS
COPY 0 M
SEEK -9999
COPY 1 M
MARK COLLECT_CENTS
TEST F = X
FJMP WRONGACCOUNT_CT
SEEK 2
ADDI F 1 M ; HANDLE 0
TEST EOF
FJMP COLLECT_CENTS
MARK WRONGACCOUNT_CT
SEEK 3
TEST EOF
FJMP COLLECT_CENTS
COPY 0 M
MODE
SEEK -9999
COPY M X ; OTHER ID
MODE
COPY X M
DIVI T X T
FJMP COLLECT_DOLLARS
MARK LOCALCTRL
MAKE
COPY 0 X
MARK ADDAMOUNT
COPY M T
FJMP ADDAMOUNTDONE
SUBI T 1 T
ADDI T X X
JUMP ADDAMOUNT
MARK ADDAMOUNTDONE
COPY X F ; DOLLARS IN F
COPY 0 X
TEST M = 0
FJMP ADDAMOUNT
SEEK -9999
COPY F X
COPY F T
ADDI F X X
ADDI F T T
SWIZ T 0043 F
SWIZ T 0021 T
SEEK -1
ADDI F X X
KILL
SUBI 0 T F
SUBI 0 X X
MODE
COPY M T
COPY M F
MARK AGAIN
SEEK -1
ADDI F X X
SUBI T 1 T
TJMP AGAIN
COPY X F
SEEK -3
COPY F X
COPY M T
SEEK 9999
COPY M F
MARK AGAIN2
SEEK -1
ADDI F X X
SUBI T 1 T
TJMP AGAIN2
SEEK -2
SWIZ X 0043 T
ADDI F T T
SWIZ X 0021 X
; NEGATIVE CENTS
COPY T F
TEST X < 0
FJMP SKIP
ADDI 100 X X
SEEK -1
SUBI F 1 T
JUMP COMPLETE
MARK DATE
LINK -1
LINK 804
NOOP
COPY #DATE M
COPY X M
MARK SKIP
SEEK -1
COPY F T
MARK COMPLETE
WIPE
GRAB 220
SEEK 9999
SEEK -2
REPL DATE
ADDI F 1 X
SEEK 9999
COPY X F
COPY M F
DROP
GRAB 221
SEEK 9999
COPY X F
COPY M F
COPY T F
COPY M F
;XB
GRAB 300
COPY F X
COPY F T
REPL PAYROLL
COPY X M
VOID M
COPY T M
COPY M X
COPY X M
COPY 0 M
MODE
COPY 0 M
MARK OTHER
GRAB 231
COPY M X
COPY 1 M
COPY T M
REPL CTRL
COPY 8 T
MARK WT
SUBI T 1 T
TJMP WT
MARK FIND
TEST F = X
SEEK 3
FJMP FIND
SEEK -2
COPY F M
COPY F M
COPY M X
COPY X T
TJMP FIND
; COUNT
SEEK -9999
COPY M X
MARK COUNTPAYMENTS
COPY 1 M
SEEK X
TEST EOF
FJMP COUNTPAYMENTS
COPY 0 M
MARK PAYROLL
MODE
LINK 800
LINK 802
GRAB 230
REPL OTHER
MARK FINDPERSON
SEEK 1
TEST X = F
FJMP FINDPERSON
SEEK -2
COPY F M
; FIND MOSS TOO
SEEK -9999
DIVI 1 M T
COPY M X
JUMP FINDPERSON
MARK CTRL
COPY M X
COPY 0 M
MAKE
COPY M F
COPY M F
COPY X M
COPY M F
COPY M F
COPY 0 M
SEEK -9999
COPY F X
COPY F T
ADDI F X X
ADDI F T T
SWIZ T 0043 F
SWIZ T 0021 T
SEEK -1
ADDI F X X
WIPE
MAKE
COPY X F
COPY T F
LINK -1
DROP
LINK 802
GRAB 230
COPY 0 X
MARK COUNT
ADDI X 1 X
SEEK 2
TEST EOF
FJMP COUNT
MULI X 4 M
DROP
COPY 0 X
LINK -1
GRAB 402
LINK 802
MARK MORE
COPY M T
ADDI X T X
TJMP MORE
LINK -1
LINK -1
VOID M
MODE
COPY X M
COPY F M
COPY X M
COPY F M
WIPE
GRAB 300
SEEK 2
COPY F M