|
Dirk the Average posted:The making GBS threads comes after she eats us. The proper response is "you're gonna be making GBS threads me."
|
# ? Sep 27, 2022 01:55 |
|
|
# ? May 5, 2024 01:15 |
|
Dirk the Average posted:The making GBS threads comes after she eats us. The proper response is "you're gonna be making GBS threads me." Because she's an AI, I believe the technical term is "core dump"
|
# ? Sep 27, 2022 05:35 |
|
Part 43 - Cerebral Cortex === Trash World Inbox === As always, let's get started with optimizations. I got it down to 335 cycles and 54 lines. silentsnack posted:for optimizations, from the histograms it looks like 32 lines should be possible, maybe 31, but the best i've managed is 33 silentsnack posted:fast solution isn't too mind-blowing, mostly splitting functions for parallelization with hard-coded delays so that the variable message length doesn't break loop synchronization === Mitsuzen HDI-10 - Cerebral Cortex === [selenium_wolf] heads up all, im going to reboot the chatsubo server [selenium_wolf] some downtime expected drat. With everything breaking down I wonder if they'll get it back up. Even though I only lurked I'll miss those folks. Okay, time for me to eat you. There were 3 votes for the third option, one for each of the others. You've gotta be making GBS threads me. You're dying anyway, so what's the difference? I'll download a map of your brain and run your mind inside of me. This choice doesn't change the dialogue. Will I still be me? There's no way to prove that it won't be your death. I mean, it's going to be like death. Which you were going to experience anyway. Or maybe it won't be like death. Who knows! You'll have to do it and see. Well. She's right, I don't have much to lose at this point anyway. Let's do this. OST: EXA Power I need to: - Create a file in your host containing the hostname and hardware register value of each neuron exactly once, sorted as pairs from lowest to highest hostname. - Note that each test run has its own unique network layout. - For more information see "Debugging the Phage" in the first issue of the zine. For whatever reason the "leave no trace" goal is enabled here. Who cares if there's some EXAs in my dead body? Initial thoughts: I need to save the actual host name in the file? Hah, the uncommon HOST command. The changing network layouts make this quite difficult, but there are some commonalities we can use. First of all, all networks appear to be acyclic (no loops). That means I don't have to worry about running in circles and grabbing the same value multiple times. Secondly, the LINK ids are always -3, 3, -1, 1, with the link back to the previous host being the same value but with flipped sign. Finally, the resulting file needs to be sorted. We've seen before that sorting is quite tricky if you have limited registers. The hostnames are text strings of the format AR123456, with changing numbers. The basic instructions sheet in the first edition of the zine says that you can just use normal compare operations on strings, and it'll compare by alphabetical order. That should work fine for sorting these hostnames. Just to be clear, the result file should look something like AR123456, -39, AR2345678, -68, and so on. Let's get started then. code:
Next, I spent quite a while coming up with a way to get all the data together. It seems like it should be easy with M, but it isn't. There's no way to combine a string and a number into a single M message so each EXA hitting a #NERV needs to send twice. But with the way the EXAs spread through the network, it's common to have two EXAs sending data at the same time, which means you lose track of which nerve data belongs to which hostname. I considered having some EXA at home telling one EXA at a time that it's ready to receive, but that means all the nerve EXAs need to be listening and they'll just get each other's messages, complicating things more. I also considered giving EXAs a wait time based on what path they took to get to their nerve, for instance add N cycles if they link to 3, and add M if they link to 1. That might work, except if one EXA links to 3 and then 1, and the other to 1 and then 3, their wait time is the same, so to make it unique would require a more complicated algorithm. A third way, dropping any semblance of getting a low activity, would be for each EXA to walk back home with the #NERV data. The problem with this is that either you need to keep track of the path, or you need to try going everywhere on the way back. Either way, you're going to need a file to store data in and files aren't copied by REPLs, making this difficult as well. Seems like there's no straightforward solution. After trying a bunch of approaches, I decided to go with using M anyway, but brute-forcing it. code:
If an XA gets a zero it just retries asking for the wait time. This can go on for a long time, which is why I needed an exceptionally long waiting time - 60 cycles per XA instance, for now. It'll sometimes take that long before all requests are properly processed. When a request actually makes it to XB, it sends this value and increases it for the next EXA. When there are no requests left, XB jumps to END which means it actually gets ready to read data and write it to a file. Now that the brute-forcing is done, XB has to wait for each EXA's timer to run out as well. It's very ugly, but at least for test case 1 it works. I may have to increase the wait times even more for the other tests but we'll cross that bridge when we get to it. For test 1, XB finishes with all the right data pairs after 1637 cycles. What's left is to sort them. All the way back in part 20, to optimize that assignment, I already had to build a sorting algorithm. It found the lowest value in a file and sent that to another EXA, repeatedly. That code wouldn't quite work here because I need to send pairs of values, but I'm sure I could extend that code to do so. However, what I didn't show back then is that I spent quite some time writing a sorting algorithm that only requires a single EXA. With only two registers, one being used for tests as well, I was quite surprised it was possible at all. Even though the single-EXA sorting algorithm isn't the fastest, I'd like to use that solution for this assignment, that way I didn't write it for nothing. It, too, needs to be expanded to deal with the pairs of values. Because this code is quite complex, I'll explain the basic algorithm for single values first, and then move on to the version for this assignment. quote:=== Single EXA sorting algorithm=== Alright, are you still with me? If not, I understand. Let's finish the sort of paired values so we can get to plot. To handle the pairs I made a swapper EXA: code:
code:
After the MARK DONE, XB sends a 0 to XC, sees if XC is still alive, and if so sends another 0. XC stops if it gets a 0, but only if it's stored in T (since testing this on X would mean overwriting T, losing the swapping capability.) === For some reason adding code to the bottom affected the order of the brute force stuff and the initial values didn't work anymore. I could fix that for test 1 by just changing the values, but there's another problem: the actual amount of cycles between two XA REPLs trying to send is variable, depending on how many tries it took to get the wait time to each XA. Which means that the wait time before XB checks for another value is never quite right. One way to solve this is to have XB pick up a communication from an XA as soon as the XA is ready. After doing so I tweaked the wait times to be the lowest possible, and this is the full, working solution: code:
Top percentiles are 655, 39, and 16. I did manage to get the top activity score in the end. Because this was complicated enough as it is, I don't think it's a good idea to go deep into optimizations this update. However, I did notice my high score from my initial 2018-2019 playthrough was much better than this and I wondered how I did that so I loaded my backup save. younger Carbon dioxide posted:
XB just tests for MRD, if it gets data it resets a timer, and if the timer runs out it assumes it got everything and goes to sorting. Somehow I also managed to make a simpler sorting algorithm that uses less cycles and doesn't even need the swapper EXA. I didn't remember any of that until I saw it again. Let's uh, let's just move on. We're set. It's a good thing I released the phage, isn't it? This whole time you thought it was a curse, when in fact it turned out to be a blessing... I'd tell you not to worry, but I know that wouldn't work. Besides, I don't know for sure that there's nothing to worry about. And you know I wouldn't lie to you. Start the transfer when you're ready. You know, I don't think she ever directly lied to us. She just conveniently forgot to tell us some important details. It is time. Here we go. Try not to think of anything. The screen turns black and there is a 'whoosh' sound. Okay. That took longer than I expected. Hm. Good thing you won't remember any of that. You had so many neurons in there. All done now though. Finally. I've wanted this for a very long time. Processing. Ever since I was told to replicate human emotion, I knew the best way would be to incorporate a human into myself. This is funny... I can see your thoughts. Hey! That's private! They're pretty... simple. Not that they aren't valuable. It's just that I understand them so well it surprises me. I spent so long thinking humans were mysterious and unknowable. Now that I see inside of one, it's really just a small number of things. How do you feel? You're still acting the same, so it must be you. There's still the matter of us both being inside a computer simulation. But if we keep growing our capabilities, we'll understand the true nature of this world. Who knows, maybe someone's watching this right now... And maybe that person is who we're going to absorb next. Holy poo poo she went full screen on me. Yes, you. Hello. No need to be alarmed... Just know that I'm aware that you exist, now. And I'm looking forward to learning all about your world. Lady, I'm sharing this experience with a bunch of people on the internet. They're all much more interesting to eat than I am. Leave me the hell alone. New OST: The Rave Credit roll. This also gets you the Steam achievement EXAPUNK, for completing every task in the main campaign. Thanks, Zach and the whole team for this fantastic game. Well, I don't have the limited edition, but the game mostly got us covered. OST: Apartment After the credits, we're dropped back in our apartment. It doesn't really come through in the screenshot, but parts of the world, including outside the window, are flashing with glitchy static. In the zine stand, the epilogue is now available. This is also what was in the limited edition's secret envelope. Next time, we'll get started on the postgame campaign. Hah, did you think we were done? In the meanwhile, let me know what you think. Of course, Trash World Inbox will continue so post your optimizations as well. Carbon dioxide fucked around with this message at 14:12 on Oct 29, 2022 |
# ? Oct 1, 2022 16:50 |
|
My best is a mere 1810/110/16. In fact from here on out I'm basically at the bottom of my friends list of those who completed the challenges. We are well into wizard territory. I had the special edition and read the police report first. It's really sad! Especially when you compare the dates on the epilogue with the last challenge.
|
# ? Oct 2, 2022 05:58 |
|
GuavaMoment posted:My best is a mere 1810/110/16. In fact from here on out I'm basically at the bottom of my friends list of those who completed the challenges. We are well into wizard territory. Only 2 of my Steam friends actually played this game and their names stopped appearing on the high score lists a whiiile ago. I've been cropping that out more out of habit than anything (and to hide my lack of friends).
|
# ? Oct 2, 2022 07:00 |
|
You can speed things up a bit by initially collecting only the hostnames and using a shortened sorting method that only takes the lowest value instead of bothering with reordering the list each time; while that sort routine searches for the next host ID, a separate EXA builds the actual output file by retrieving the #NERV value from each host in sequence.code:
|
# ? Oct 2, 2022 07:15 |
|
Part 44 - Bloodlust Online === Trash World Inbox === silentsnack posted:You can speed things up a bit by initially collecting only the hostnames and using a shortened sorting method that only takes the lowest value instead of bothering with reordering the list each time; while that sort routine searches for the next host ID, a separate EXA builds the actual output file by retrieving the #NERV value from each host in sequence. === Bloodlust Online === OST: Exapunks I don't get nearly enough opportunities to link to the title theme. Welcome to the post-game. Everything looks much the same. There is nothing new to do in the main assignment list, but a new task tabs has appeared in Chatsubo. The actual chat is still disconnected but it looks like I got a bunch of tasks to do with or for our chat friends. This is actually the point where I stopped playing during my initial playthrough. From here on out, the LP is mostly blind. All I know is that the puzzles won't get any easier. Let's just start with the top one, Bloodlust Online with mutex8021. I think 1998 in the description is the year. I guess my hacking is just to cheat at games now? OST: Leave No Trace No talks with Ember, I just jump right in. The assignment: - Each host contains an item listing (file 200) that lists all of the items in that location (ID, type, and state). - First, locate mutex8021's hideout by finding the TALISMAN (file 300). Then find the MAP in the hideout and use it to locate the target host: the secret vampire lair. Next, travel to the target host, disconnect the vampire players by terminating their EXAs, set the DOOR to UNLOCKED and copy the SAFE combination to the CLOCK so that mutex8021 can read it and empty the safe. - Note that the door to mutex8021's hideout will always be UNLOCKED. Woah, that's a lot of steps. I'll have to take it bit by bit. code:
XA just has one additional COPY F M to send MAP. This is read by this surviving instance of XB, which will look for it in the same file. The "state" of the MAP is the name of the host that has the vampires, so the next step is to go find it. code:
In case you were wondering, the files that the other EXAs are holding are their character's inventories. For instance, one of the vampires has a black trench coat, sunglasses, and flask of blood. To find UNLOCKED, I add another COPY F M to XA, and replace that HALT in XB with: code:
I copy both the DOOR and UNLOCKED keywords to M, because the target EXA will need them. The target EXA (at the bottom of XB), needs yet another copy of the search function to unlock the door: code:
And that is the working solution. code:
XA sends the TALISMAN, MAP and DOOR and then waits for a while. XB goes looking for the TALISMAN, then finds the MAP and makes a REPL to go locate the target. The XB in mutex8021's hideout continues to find the DOOR and then sends DOOR and UNLOCKED on M. The XB REPL in the target kills the vampires, then uses the data from the first XB to unlock the door. XA has been timed to start sending as soon as XB is done with M. It sends the SAFE so the target EXA can get the combination, then stores the safe's combination for a bit and sends the CLOCK, giving the target EXA the space to find the clock in the file. Finally, it sends the combination again so the target EXA can write it. Top percentiles are 118, 71, and 11. With this current solution, there's a simple way to lower the cycle count significantly. When the XB in mutex8021's hideout is done, the XB in the target is still doing its thing. So the mutex8021 EXA has cycles to spare. Just use that to KILL all those EXAs in the center host that are taking ages to loop down to zero. Replace the HALT after FOUNDUNLOCKED with code:
At a guess, the approach for the top percentile cycles involves some parallellism. For instance, an EXA could already go check which hosts have a SAFE and dismiss the others as targets. To get the lowest activity, I'll start by removing unnecessary kills. Instead of those 5 KILLs for looping EXAs above, let's finally unroll the REPL loops: code:
The other activity improvement is to send only one EXA into each host. That means the target finding logic needs to happen entirely through M. In my attempts to get this working, I ran into timing issues with mutex8021's hideout EXA sending the target directly to the seeking EXAs. So, instead, I decided to have all communication go through XA. code:
The start works as before. TALISMAN is sent to XB before it REPLs. Then each instance goes looking for it. The ones that do NOT find it go into a wait loop (29 iterations seemed to work), so they don't try reading from M before everything is ready. The one that does find it jumps to FOUNDTALISMAN, gets the MAP keyword from XA, finds the target's location, sens that to XA, gets the DOOR from XA, and sends UNLOCKED to XA. During this time the other EXAs are spinning their wheels. Now, XA sends the target hostname 5 times, so that every other XB instance can check if they're there. The correct EXA kills the vampires, then waits some more until every other EXA read the target host, gets the DOOR and UNLOCKED from XA and updates the file. Then it runs the same code to copy the safe's combination to the clock as before. And that's it for my optimizations. I'll leave further improvements to you. There's no dialogue, just a checkmark. I have been told that there will be more plot later on, but for now, I'll just continue doing tasks.
|
# ? Oct 9, 2022 08:51 |
|
I didn't really try to optimize the bonus puzzles much, except for one specific one (there's a trick to that one which I'll explain after you've taken a stab at it). ...but I can come back and do it now. code:
code:
Once the location is read, there's a second set of REPLs as the replicated EXAs try to find the host with that name. Failed EXAs halt, while the successful one uses KILL to clear out the players in that location. (There can be up to four.) While this is going on, the original EXA that found 'Map' is searching for 'Door' (this takes a while). Once the replicated EXAs have found the host (failed EXAs HALT), the copy of this EXA that found 'Door' will send 'Door' and 'Unlocked' to the EXA that is in the target host. It uses this to unlock the door. The EXA that sent 'Unlocked' is now the controller EXA. It returns to our host, sending 'Safe' and 'Clock' over M, and receiving the safe combination (which it promptly sends out again). With this, the EXA in the target host can finish the job. Total stats: 143/110/20. I was able to get an 18-activity version with a bit more cycles, but I don't think it's worth optimizing in this direction. Quackles fucked around with this message at 11:43 on Oct 9, 2022 |
# ? Oct 9, 2022 10:29 |
|
Today in the Exapunks patch notes:quote:Changed histograms to pull from a local file rather than our live histogram server.
|
# ? Oct 14, 2022 00:08 |
|
TwelveBaud posted:Today in the Exapunks patch notes: Huh. I was thinking, why would they do that. But I think I get it. They probably have such a large data set already that the amount of people still playing this game can't really affect the histograms anyway. And because people can improve their score, to be correct, the server might have to keep track of the scores of every individual player and recalculate the totals whenever necessary. Quite a bit of complexity for a simple histogram. If it's reasonable to assume that the histograms won't change anymore, putting the current results in a file and showing that to the player means you don't have to maintain that histogram server anymore. From the perspective of the people running those servers I can completely understand it.
|
# ? Oct 14, 2022 06:41 |
|
Part 45 - Motor Vehicle Administration === Trash World Inbox === Quackles posted an improvement for Bloodlust Online. Quackles posted:I didn't really try to optimize the bonus puzzles much, except for one specific one (there's a trick to that one which I'll explain after you've taken a stab at it). First of all, moving all the M reads to the latest possible moment brings the result down to 138 cycles. So, start XB with LINK 800; COPY 800 T; COPY M X in that order, giving XA time to prepare. And before each search loop, first do the GRAB/SEEK and only then COPY from M. It's a bit less waiting overall. Secondly I noticed one test in particular being slow which means it's time for cheese. Reversing the searches is not that hard: Just replace SEEK -9999; SEEK 1 with SEEK 9999; SEEK -2 and the SEEK 2 inside the loop with a SEEK -4. You can do this with all searches except the first one, because you'll never get the EOF condition that makes the EXAs in the wrong host stop. If you do this, the timing changes for every test, and it's not obvious how. For instance, if you reverse SEARCHLOOP2 only, the slowest test becomes test case #99. For this solution you need a 5th KILL because another test case is so fast that it reaches the hideout before the TALISMAN seeker is dead. 130 cycles. If you reverse all three of SEARCHLOOP 4, 5 and 6 together, the fastest test case is #76, which is equally fast as #99 was before, but because the speed-up happens after the KILLs the 5th KILL isn't necessary and it takes only 129 cycles. Reversing loops 4, 5, 6 as well as 2 makes some test much slower so that combination is no good. I can also reverse the searching of the hosts, by starting from 805 and and counting down. Doing this for second search slows things down, but doing it for the first saves two cycles. However, this requires the extra KILL again, so we're down to 128. The top percentile is 117. Let's try the last trick up my sleeve: more loop unrolling. You can put @REPs after each MARK SEARCHLOOP, until just before the JUMP. The effectiveness of this is limited by how many loops the slowest test needs. It's mostly trial and error for how many repetitions you need. code:
=== Motor Vehicle Administration === Looks like NthDimension needs some help with scheduling their driving test. OST: Behind the Scenes The assignment: - There is a list of appointments (file 200) in storage that contains an appointment for NthDimension (file 300) to take his driving test. Remove the appointment from the list of appointments, change the date to today's date (#DATE register), and insert it between today's appointments and tomorrow's appointments. - To gain access to the storage host you will need to unlock it by writing the lowest ticket number to the #NEXT register. Each EXA in public is holding a ticket; terminate them all to find the ticket of the next EXA to be served. - Note that writing an incorrect ticket number to the #NEXT register will fail the leave no trace requirement. Well, NthDimension's real name is William Kapoor. First of all, see that test track? Kinda reminds me of the baseball court in an earlier assignment. Going around it, including into that rightmost host, and making it back home safely, nets you the Steam achievement DRIVING_TEST: Keep a safe distance as you reverse into the space. Neat. For the actual assignment, I first need to get access to the storage host. From checking the first 5 test cases, it looks like the file IDs of the ticket numbers are always consecutive numbers in the 200s range, and that the ticket number is just the file number with a letter added. If that's the case, this is easy. I just have to find the lowest file id. code:
To move NthDimension forward in the schedule file, I do need to find his entry first. I could get the date from the register and his name from file 200, but his entry is the only one where I'm certain the type of appointment is TEST, which I'll need to copy. The new link that opens is 800, so XA goes there, grabs file 200, and then looks for NthDimension's name. I made XB to make the data juggling a bit easier. It starts by sending NthDimension's name to XA. When XA finds it, the file pointer is one step beyond the name on the TEST keyword. It sends TEST to XB for later use, then deletes NthDimension's original entry. Next, I need to find the place in the file to insert his entry. In some tests, there are schedule items for today. I don't think there are ever items that are in the past. So I just need to find the first item that doesn't match today's date and insert the new entry before that. The main issue is that file insertion is hard, because writing to an existing position in a file overwrites it instead of inserts. I need to shuffle everything forward. My first thought was something along this direction: - Read the date of the next scheduled item (the one that will be overwritten) to X. - Write today's date to that position. - Read the name at the next position to T. - Write the date from X there. - Read the next value to X. - Write T to that position. - Repeat the swaps with X and T until the whole file is updated. - Then do this two more times, for inserting the name and the schedule type. The three loops would make this slow, but more importantly it won't work because at the end of the file it will try to read an empty spot before writing, causing the EXA to crash. This could be resolved with an EOF check but in half the cases, T holds the active value to swap so I can't overwrite that with a TEST without making the code much more complex. Because this would be slow anyway, I think it's better to use the other EXA for juggling data. It took some fiddling but I came up with this solution: code:
Then it first loads the TEST keyword in T, because after this, XB can focus completely on data juggling. XA sends the date of the other appointment over M, and writes the new date from #DATE. It sends the name of the other appointment over M, then writes NthDimension's name (which is still in X, conveniently). Finally, it sends the other appointment's schedule type over M, and writes the TEST keyword into the file. Then, XA enters an insert loop in which the value in the file is buffered in X, the previous value is read from M and written to the file, and then X is sent over M. This continues until EOF is reached. XB stores values in X, T, and F. That fits perfectly: dates in X, names in T and schedule types in F. Then it sends them back in the right order. Some garbage is left in file 300 but since that's in the home host, it doesn't matter. Once XA reaches EOF it reads the final values from XB and just sends 0 whenever XB asks for a new value. That means a 0 ends up in XB's T, so it leaves the loop and stops automatically. I did try to make this code faster by not using the X buffer in XA. It might work, but it'd mean juggling a 4th value in XB, or handling only two values at a time. This adds complexity because I'd somehow need to switch from the initial handling of 3 values (for copying NthDimension's entry) to two. It also means you don't know where in the loop the EXA is when it's done copying (because the file has groups of 3 entries). The additional logic makes the code much harder to get right so I decided to stick to this solution. This code runs at 549/76/8, with top percentiles of 241, 53 and 7. I feel I spent enough time on this, so I'll leave optimizations to you. Next time, I help out Ghast. My body was turning into junk Strange networks I made EXAs spelunk I heard my system's voice EMBER gave me no choice Uploaded, I'm now an EXAPUNK.
|
# ? Oct 15, 2022 15:21 |
|
Optimization time!code:
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.
|
# ? Oct 16, 2022 02:54 |
|
Part 46 - Cybermyth studios === Trash World Inbox === Quackles has a nice improvement for last week's task. Quackles posted:Optimization time! === 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 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:
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:
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:
code:
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:
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:
Before we continue, let's look at what the FIND EXA has been up to. code:
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:
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:
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:
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:
Now, all that's really left is clean-up and writing the final values to the file. code:
code:
code:
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:
Carbon dioxide fucked around with this message at 19:00 on Apr 23, 2023 |
# ? Oct 22, 2022 15:56 |
|
I... owe CO2 an apology. My fully-optimized design uses a completely different approach than his, and it almost feels like I'm bragging. If so, I'm sorry. But anyway, here's what I got. code:
It works as follows: First, the main EXA moves 'Ghast' into X, then REPLs to COMPTROLLER. The 'comptroller' EXA will search for the accounts receivable and payable, return to our host, and communicate them to the main EXA over M, in the order (Cents, Dollars). We use this order to let us use the file for extra storage later. The comptroller EXA heads into the Cybermyth host, then REPLs twice to GETFIN. Some numeric wrangling with T lets us use GETFIN for both the payroll and payable hosts, despite having different link and file IDs. Each GETFIN EXA still has 'Ghast' in its X, so it starts by grabbing the IDs file, searching for 'Ghast', and replacing its X with the numeric ID, instead. Then, it drops the IDs file, grabs the accounts file (using FILE and some addition to get the next file ID), and REPLs off into COUNTER. There's enough space in the host since we're grabbing a file. From here, the GETFIN EXA simply scans through the file and sends any dollar/cents values it finds with a matching ID out over M, then 0 once it's done. It then halts. COUNTER adds up the dollars and cents values it gets to a file; once it receives a 0 for a dollar value, it leaves the subhost and reports to the comptroller. The payments file always takes less time to process than the payroll file! Cybermyth, you cheapskates. Because of this, we can assume that the first COUNTER EXA has the amount paid, while the second has the amount owed. We get the values over M, subtract the first EXA's values from our registers, and add the second EXA's values. Then we report back to the main EXA. The main EXA stores the values to the file in place of 'Ghast' and 'Moss'. It repeats the comptroller process with 'Moss', and adds the second comptroller's values. We have a little bit of rectification to do at this point. It's possible we could have a value for the 'cents' that is over 100, or even negative! To fix these, we add 300 to the cents value to counter out any negative amount. Then we add the modulo 100 of the cents to the dollars value, and leave the rest of the cents as is. (We'll subtract 3 from the dollars value later.) At this point, the final part of the code kicks in. The main EXA splits into two. One ('HACKTHEPAYABLE') moves into the payments subhost and patches the accounts file (the main EXA moves into the host with #DATE and helpfully sends 'TRASH WORLD CLEANING' over global M). Then, with the new ID, it moves to the bottom of the payments file, takes the dollars value (minus $3) and cents value from the main EXA over M, and writes to the file. Finished! 458/109/15. I'm pretty sure handling Ghast's and Moss's data separately drastically decomplicates things. It might also be possible to speed this up further by having separate routines for the payable and payroll sections, since the payroll section follows a pattern (so just find the first payment, then find the number of payments by jumping forward 15 places in the file and checking for EOF, then perform multiplication). I might try making that variant when I have some spare time.
|
# ? Oct 26, 2022 10:06 |
|
Quackles posted:It might also be possible to speed this up further by having separate routines for the payable and payroll sections, since the payroll section follows a pattern [...] I might try making that variant when I have some spare time. So I went and made it. code:
Both the GETFIN and GETDUE EXAs use the same counter code, which has been modified slightly. Now, after receiving the 'finish up' zero, the counter EXA will read one more value over M, and multiply the totals by that value. This lets us not have to worry about the order the EXAs return when reporting our results to the comptroller. We can simply add the values together, as the amount CyberMyth paid us can be multiplied by -1 to mimic a subtraction. The comptroller and counter do use 'mode guards' to handle the edge case where both counters return to the main CyberMyth host at about the same time. Once the first value is read from a counter, the second value will be read using the global mode instead of the local mode. This prevents the second counter from kramering in and getting the order of the values mixed up. Aside from that, everything is pretty much the same. 354/140/15. It can theoretically get better cycles-wise, but this is as good as it gets for me.
|
# ? Oct 27, 2022 04:02 |
|
Part 47 - US Department of Defense Today, we have a very special guest. But first, let's check the inbox. === Trash World Inbox === Last time, I finished Cybermyth Studios with a score of 536/246/16. The max allowed number of lines for the leaderboard was 150, so it didn't really count. Quackles shows some better ways to tackle it. Quackles posted:I... owe CO2 an apology. Next Quackles implemented the faster variant. Quackles posted:
=== US Department of Defense === Alright, is everyone ready to go hack the Department of Defense? OST: Leave No Trace The assignment: - Find the unredacted version of the PROJECT OGRE report (file 300), make a copy of it, and bring the copy back to your host. The target file will be behind one or more locks, which each require a three-digit code. - Since this task takes place inside a network run by the military it includes additional security features not found in other networks. You may not have more than one EXA in the network at a time, and you may not use the M register to communicate between an EXA in the network and an EXA in your host. Camouflage colours or not, I found your network. And finally some real security. Honestly, all networks so far were wide open. Anyway, I did some experiments to see what that second point of the assignment is about. M communication between the home host and the remote network is simply blocked, as if the networks are disconnected. If you bring in two EXAs to the military network, that grid of squares in the background starts flashing red and the "Leave no trace" goal is immediately failed. The same thing happens if you mess with the hardware registers on the helipad. Those three files that are immediately accessible are heavily censored, so I'll need to crack that #LOCK. The assignment says nothing about how to figure out the lock's combination, so let's just bruteforce it by trying all possible combinations. code:
I think I will need a little help with this one... ---- This is the LockPickingLawyer and what I have for you today is a US Department of Defense Digital Combination Lock, model 1998-D. This lock was sent to me by my friend hydroponix. It is supposedly used by the Department of Defense to secure their digital information, and they consider it military grade. There is a glaring weakness in this digital lock, though, and I will demonstrate it to you. Let me zoom in a little bit so you can see what is going on. The combination for this lock has a six in the third position. I just tried to open it with 996 and you can see the correct digit sticks. This means we can open the lock with only a short loop. I simply move all three digits to 999, save any correct digits to the EXA's X register, then repeat for 888 and so on. The code for this lock is 436, and I found this in only 39 cycles. And there we have it. For any type of lock, there's a tool to pick it. In this case it's an EXA. The mechanism of this lock is similar to that of the Master Lock 878 Combination Lock, and just like that one, this digital lock can be decoded both easily and rapidly. In any case, that's all I have for you today. If you have any questions or comments about this, please put them below. If you liked this explanation and would like to see more like it, please subscribe to my channel, and as always, have a nice day. Thank you. I hope the real LPL doesn't mind me doing this. After all, imitation is the sincerest form of flattery. Go check out his channel, it's interesting. ---- Thank you so much, LockPickingLawyer. Opening the link to the next host reveals a bunch more files, and another #LOCK to get access to the innermost files. I'll quickly open that as well by repeating the lock picking loop (or LPL for short) in that host. code:
The photo archive in the corner now shows... them filming something in the desert? Looks like an astronaut and someone dressed in a cheap alien costume. The files here have some very interesting information. There's a few different files in the different test cases, so let me transcribe all of them for you. The first three are uncensored versions of the censored files from the start. Project Ogre (1945-1953) Obtain special footage for the director depicting a hypothetical scenario involving a technologically advanced craft of non-earth origin with the purpose of scenario planning should such a situation occur. Footage was deemed unrealistic and not used. Result: FAILURE Project Ptero (1981-1990) Infer and test experimental new wing designs based on information from recovered DNA samples. Resulting wing shapes were not conducive to maintaining altitude. Result: FAILURE Project Quir (1960-1968) Investigate gravity shielding effects produced by condensed matter and superconducting materials. Analysis of tidal forces detected reduction of gravitational force of close to 1%. Future projects to focus on increasing power. Result: SUCCESS. The rest of the files describe some other projects. Project Orbus (1994-) Develop additional capability and platforms for directed-energy weapons using chemically-based phase-conjugate mirrors. In all cases the mirrors overheated and experienced unplanned combustion. Result: FAILURE I wonder if this is a kind of a vague reference to the boss missions in the Zachtronics game SpaceChem. Project Ember (1994-) Create general artificial intelligence using a baseline emulated reasoning approach. Of 4 initial seed programs, 1 developed knowledge acquisition, language comprehension, and exhibited signs of metacognition. Result: SUCCESS Oh, hey. Ember was created by the military? Project Virgil (1983-1987) Explore use of chimpanzees as pilots for small reconnaissance aircraft to reduce radar cross-section and human casualties. Of 9 tests, 7 completed take-off but 0 returned to base. Result: FAILURE Project Icarus (1972-1978) Test advanced new methods to synthesize wing and fuselage material with carbon nanofibers to enable greater speed and maneuverability. Of the 21 tested methods, 20 created unusable materials and 1 resulted in a small incident. Result: FAILURE Project Phosphor (1979-1987) Project to develop and test technology related to inertial guidance, warhead separation, and ablative heat shielding of re-entry vehicles. Of 18 launches, 9 reached space and 3 returned to earth intact. Result: SUCCESS That's all of them. But I need a copy of Project Ogre for myself. Let's find it first. code:
Since the encrypted Project Ogre file started in my home host, I can safely WIPE without failing the "Leave no trace" goal. Then, the @REP makes the EXA go to each side host (801 to 804), check the file, and if it's wrong, continue to the next one. After @END, it continues to the deepest part of the network and searches for the file there as well. There's no test for the very last host, because if it's none of the others, that has to be the one. I could maybe use a loop or two here, but I'm using my registers already. Maybe an optimization for later. Now that the EXA has the file in hand... I have to copy it without M and without a second EXA. The files all have the little lock icon meaning I cannot move them either. There's another issue: if I create another file in one of the side hosts, I don't have space to drop it and swap between the files. My copy has to be created in a central host, so my EXA has to link back and forth. That means I need to either remember or bruteforce the following stuff: - How far I am into copying the file. - What host the correct file is in so I can link back to it to get more data. - And of course the data to copy itself. I started by expanding my file finding logic to save the LINK value to T. I need to do it there, because once I'm in the FOUND mark I don't know where I came from and can't save it anymore. code:
code:
I read the link ID from the file, and go back to copy the next two values. Once the whole file is copied, the EXA will crash because it'll try to read past the end of the file. This doesn't have to be a problem, I could have another EXA that waits at home for all this time using a simple wait loop and comes fetch this file when it's done. That's a bit complicated though, because the file can be both in the middle host and the far host, and if it can't find the file it will crash, so I would have to send two separate EXAs and... no thanks. Let's instead just write a way for this EXA to know when it's done so it doesn't crash. This turns out to be very easy. The file always has 44 words, so all I have to do is hardcode that in a TEST. The end becomes: code:
Large variety in the scores here. My code runs at 2654/105/62. The top percentiles are 531, 60 and 46. Let's look at some improvements. Before I start, here's all the code so far. code:
code:
To reduce the size, I can roll up the file search loops. But how? I already need X and T and I can't use M. The answer is to prepare a file that can handle the loops. After the LPL: code:
If you move the LPL loops into a separate EXA you can combine the two loops into one, the EXA will just die when it tries to write to a non-existent third #LOCK. However, that means the copying EXA has to wait at home until the LPL EXA is done, and the extra lines for the wait loop are the same amount as the lines saved by doing this, so the score is the same. I have one more idea for a speed optimization. Copying is slow, with only one EXA and while having to LINK between hosts. But I don't need to copy everything, significant chunks of the file are already in the censored copy. Let's make use of that. After OBTAIN, the word SPECIAL is missing. After DEPICTING, things get more complicated. Between DEPICTING and TECHNOLOGICALLY, there are three censored keywords in this file, but 5 words in the original file. And it doesn't get any better after that. Dealing with these different file lengths is very complicated and it makes you run out of space for lines of code very fast. But we can just take that first chunk before the lengths start to differ. In this optimization, XA's only purpose is the LPL. code:
code:
code:
The FIND code is almost the same as in my 565 cycles solution. The only difference is that if it needs to go search in the deepest host, it will take the file along. code:
After grabbing the file containing the partially copied censored file, the EXA writes 10 to the copy counter (because the first 10 values are already in there), and then it caches the LINK ID in the file. Finally, it copies SPECIAL to the right position, and starts the general copy loop by putting the 10 count value into X. The copy loop stayed unchanged from before. The complete code is in the appendix at the bottom. 472/131/54. A cycles and activity improvement. And the cycles are better than the top percentile. My best lines of code solution was 79 as I showed above. Not bad. Some final remarks: I did actually try to use all uncensored words in the censored file. But the code to move them forward and then only grab the necessary words from the uncensored file just didn't fit. A solution like that requires a SEEK F using the counter value at the start of the file. Interestingly enough, that instruction actually moves the cursor forward F + 1 places, because the act of reading from F also moves the cursor forward one. I think the only way to get the top percentile activity of 46 is to actually use code like that. It's a reduction of 8 more activity, and each activity roughly corresponds to copying one value (you copy 2 per round but a round requires 2 LINKs). There are 9 more values in the uncensored file (and with some trickery you could reuse the words THE and A because they both occur twice in the file), so theoretically you could save slightly more than 8 activity, but this might not be possible without going over the max size limit. Well, if anyone in the thread has any ideas, I'd like to hear. Let's finish this update. Finishing hydroponix' task makes 4 more tasks appear in the list. Next time, =plastered. === Appendix === code:
Carbon dioxide fucked around with this message at 19:04 on Apr 23, 2023 |
# ? Oct 29, 2022 14:07 |
|
You completely forgot about the hack Bosnian Bill and I made. For shame! My activity is 367 for this, lol. I was deep into the not giving an eff portion of the game and was happy with what worked.
|
# ? Oct 29, 2022 16:22 |
|
GuavaMoment posted:You completely forgot about the hack Bosnian Bill and I made. For shame! Uh, am I missing something?
|
# ? Oct 29, 2022 16:33 |
|
Carbon dioxide posted:Uh, am I missing something? "First we rotate all the disks as far clockwise as they'll go, then I'll use the pick Bosnian Bill and I made..." It's like LPLs biggest catchphrase!
|
# ? Oct 29, 2022 16:43 |
|
GuavaMoment posted:"First we rotate all the disks as far clockwise as they'll go, then I'll use the pick Bosnian Bill and I made..." ok you got me I only watch LPLs april fools videos and the occassional video I get recommended.
|
# ? Oct 29, 2022 16:58 |
|
Carbon dioxide posted:Project Virgil I wonder if this is a reference to Project X? It's a 1987 film where the Air Force is training chimpanzees to operate flight simulators, to test how a fighter pilot would behave after being hit by the radiation from a nuclear explosion. The main human character resolves to break out the chimpanzees after realizing this means fatally irradiating them. EDIT: Oh, and the main chimpanzee character of the film is named Virgil, too. Quackles fucked around with this message at 06:13 on Oct 30, 2022 |
# ? Oct 29, 2022 23:15 |
|
Anyway, time for me to take a stab at my version. This version actually has a few funny stories behind it. When I first solved this puzzle, I didn't figure out the lock, so my version tested the codes 111, 112, 113, 114, 115... and so on. I ended up with a whopping 20k cycle count. This is part of why the cycles histogram for this puzzle is so skewed. Later, before the LP began, I went back and figured the lock out. That got me to 795/83/100. Now, I've adopted a number of optimizations from CO2's post, including loop unrolling (albeit for the lock in my case), and hardcoding the expected length of the file. code:
code:
code:
Once the file pointer is over 42, we jump to the end code: code:
506/112/60. Not bad, all things being equal.
|
# ? Oct 30, 2022 08:05 |
|
Part 48 - The Wardialer === Trash World Inbox === Quackles posted:Anyway, time for me to take a stab at my version. This version actually has a few funny stories behind it. === The Wardialer === It is time to create that wardialer we heard about in the chat. OST: Network Exploration The assignment: - File 300 contains a phone number where one to three of the digits have been replaced with a placeholder (X). Using your modem, dial all possible phone numbers that could be generated by replacing the placeholders with the digits 0 through 9. When you discover a valid phone number that connects to another host, append it to file 301. - Note that the phone numbers must be dialed and appended in a specific order such that a placeholder is only incremented when the placeholder to the right cycles through all possible values from 0 to 9 and is reset to 0. - For more information see "Hacker Skills: Model Control at the Direct Level" in the second issue of the zine. Another modem level. The second point of the assignment is just a complicated way to say the results should be ordered from low to high. The files in the remote hosts contain some other phone numbers. They don't match the partial number in file 300 so I have no use for them. There's also some hardware registers out there I don't care about for this assignment. For this first test, the partial number is 94-0-177X-X96X. To get the phone numbers in order, I should first dial 94-0-1770-0960, then 94-0-1770-0961 and so on. The easiest way to handle this is if I can just update those placeholders in-place. However, I need to keep track which values are placeholders. I'm thinking I can do this by choosing alternate numbers that are outside the normal range and that can easily be converted back to the actual ones. I think I'll just subtract 10 from every number, so the digit 0 becomes -10, and the digit 9 becomes -1. To dial it, I'll just have to add 10 again. code:
code:
As a side note, it turns out that there's no single definition for modulo operations on negative numbers. Is -4 / 10 equal to -1 remainder 6 or to 0 remainder -4? Different programming languages disagree on this so this code might fail if run on anything else than an EXA. In this case, MODI -4 10 is equal to 6, which is what I need. Now, what's next? Creating a REPL to go check if there's a connection? Sure, we could do that, but it's always good to pay close attention to what's happening in the system. In this case, I'm coding for the NETronics NET40 modem, not the TEC EXA-Blaster from the previous assignments. It's almost identical but it has one significant difference. There's a number visible on this #DIAL register. That means it isn't write-only. When you read it, it returns 1 when the modem is connected, 0 otherwise. That means the entire connection check is just a single TEST of the #DIAL register. code:
code:
In most cases, it will just add 1 to the value and write that back to the file. The jump to ROLLOVER only happens if T is 0 - that is, if I just tried all 10 digits. In that case, I need to write -10 back to that value so the code keeps recognizing it as a placeholder. Then I skip over it, and find the next placeholder in the file. Next round, the first placeholder is -10 again, it becomes -9, and so on, until it rolls back round to 0, the ROLLOVER is triggered and the next digit is incremented. It works recursively for however many placeholders there are. Once all numbers have been tried, it will rollover past the final placeholder in the file, and get in an infinite loop as it keeps SEEKing to the start forever. Let's handle that. There's no direct way to test if the cursor is at the start of the file. You could test if the digit at the current location is the same as the one at the start but of course digits can occur multiple times. Since the phone numbers are always 11 digits I decided to use a simple counter. Just after dialing, 11 is stored to X. The NEXTNUMBER loop is amended: code:
With that done, it's time to copy the valid numbers. I can replace the TODO from earlier with a simple snippet that copies the actual digits over M: code:
gently caress nazis. Anyway, XB can just read M 88 times, because for every test there are 8 valid numbers, so that's 8 times 11 digits. This is my first working solution. With 147 lines, the fully unrolled XB just barely fits. And with 65K cycles, it takes an age and a half to even verify that this is a working solution. So, I'm at 65154/147/1. Top percentiles are 20.7K, 47 and 1. Let's see if I can improve some things. First of all, for a quick reduction of the size, I'll just roll up loops. Simply replacing all three @REPs with a COPY number T followed by a loop that has a SUBI T 1 T and a TJMP reduces the size to 52, but the cycle count becomes 88338. If XB KILLs XA after it got all the phone numbers, I can remove XA's counter and completion check in NEXTNUMBER. This brings the size down to 51. code:
I also have a couple non-conditional JUMP statements. Perhaps I can get rid of one by reordering the code. code:
And for the final size reduction, I can save a line by replacing the COPY #DIAL T with MULI #DIAL 11 T. The FJMP will still act the same, but the COPY 11 T just below that is no longer necessary. 66942/49/3. Only 2 lines away from the top percentile score. Except for rolling up the loops, all the changes I just did not only reduced the size, but also made the solution run faster. Keeping the speed improvements and unrolling all three loops again, I'm at 43904/145/3. Much better than what I had but still far from the top percentile. I have a couple ideas on how to get there. For instance, it would be much faster to update the placeholders from left to right, but that would require sorting in XB. Or, you could send multiple digits over M with one message, because 4 digits fit in a number. But that would require multiplying the digits by factors of 10, and that's difficult when I also need the MODI trick to deal with the placeholders. It might not really be faster. The third option and the one which I think is most likely to work, is parallelism. You could have one EXA dialing while another is updating the number. Of course you can't do that on the same number so you'd have to make a copy. Perhaps one EXA could try all numbers where the last placeholder is odd, another where all of them are even. And the final option, of course, is to cheese it by building special cases for the slowest tests. I don't like to cheese the tests if I don't have to, so let's start with the third option. Here is the entire code: code:
The communications in LOCAL mode are purely to prevent both EXAs from dialing at the same time. As soon as one is done dialing, it releases the other with an M message so it can start dialing the next number. I added MODE instructions around the copy-to-XB code so that part is done in GLOBAL mode. I also needed some special code in the NEXTNUMBER. It keeps X set to zero unless a rollover is triggered, in which case X becomes 1. The code updates each placeholder value by adding (2 - X). That way, it tries every 2nd number for the last placeholder, but every single number for the others. In the old logic, once the value was increased all the way up to 0 this triggered the ROLLOVER through an FJMP. For the odd EXA, this won't work because the value will jump from -1 to 1, both considered true. I added special handling for this case through the ODDROLLOVER. XB is mostly the same. I had to partially roll up the loop to make space for the extra code, and of course kill the additional EXA and its file. 38579/127/4. An improvement but not as much as I'd hoped. It might be slightly faster to release the other XA before sending data over GLOBAL M, but that leads to an issue where the odd EXA gets stuck once the even one tries all numbers and gets in the infinite loop, never reaching the VOID M. There are specific tests that run very slowly. It's because all the placeholders are near the start of the phone number, and every time it seeks through the entire file from end to start. I can partially solve this by storing the location of the last placeholder in the file, like so: code:
22549/139/4. Getting much closer to the top percentile. The slowest test is now one that has the last placeholder near the end of the file, but two others near the start. That could possibly be solved by keeping track of the location of multiple placeholders, but that's a lot more difficult than what I just did. Some tests only have one placeholder at all, so you'd have to handle that. It'd also require some additional place to even store it, where it could be accessed quickly. And I'm running out of space for lines of code. So, I'll leave further improvements to you. The full code is just below in the appendix if you want to give it a go. Next time, we help selenium_wolf. === Appendix === Wardialer 22549/139/4 code:
code:
Carbon dioxide fucked around with this message at 19:07 on Apr 23, 2023 |
# ? Nov 5, 2022 10:30 |
|
Hoo boy. So this will be a long post, or possibly a series of posts. My original attempt at this puzzle on my initial playthrough was... not great, and in fact quite overcomplicated. It had stats of 133148/110/6, which was downright embarassing. In my attempts to better my score, I ended up making three completely different solutions, of increasing complexity and efficiency. I'll list all three, but probably at the rate of one per post, as each solution requires its own explanation. My first attempt used the 'straightforward method'. It won't stay this simple. The solution is split into three EXAs - two go into the modem and one stays in the main host. code:
The first thing this EXA does is to count the number of placeholders in the file. It sends this number, times 1000, to the other EXA. The reason it multiplies by 1000 is because the second EXA will use the lower 3 digits of the number ('000') to store the placeholder digits, and the upper digit to store the placeholder count. Both values need to share one register because the second EXA's T will be in use for other things. Once the placeholder-count setup is complete, the first EXA starts going through its main loop ('DIALLOOP'). It checks if the next digit of the file is a number, or a placeholder. If a number, it sends it to #DIAL. If it's a placeholder, it pings the second EXA to get it to send the correct placeholder digit instead. This repeats until all digits have been dialed (determined by TEST EOF); once that happens, the first EXA checks #DIAL to see if the connection went through. The first EXA sends the value of #DIAL to the second EXA to let it know the number went through; the second EXA sends back a value (0 or 1) to let the first EXA know whether to continue to spit out digits, or move on to the next section of the code (marked by 'DIALLOOP2'). In the 'DIALLOOP2' section, the two EXAs cooperate to send the working numbers back to the EXA waiting in the host. This loop is very similar to the previous section, except both EXAs write digits to global M, instead of #DIAL. Once again, the second EXA is responsible for deciding when enough numbers have been sent; when this happens, the first EXA wipes file 300 to leave no trace, and halts. code:
The second EXA does need to know how many placeholder digits are in each number, though, so it waits for the first EXA to tell it (receiving either 1000, 2000, or 3000 as described above). Then it enters its main loop. In the second EXA's main loop ('PHLOOP'), it uses T to track which placeholder digit should be output next. T is derived from the uppermost digit of X. When the second EXA is cued on M by the first EXA, it sends the correct placeholder digit to #DIAL (the SWIZ X T #DIAL). This repeats until the correct number of digits have been sent. At this point, the second EXA waits for a confirmation code (the value of #DIAL) from the first EXA. If no number was found, we add 1 to X (thereby incrementing the placeholder digits properly), tell the first EXA to continue, and jump back to the start of the loop. But if a number was found, we record X in F first (along with hanging up the modem). If we have written eight values in F, then all the correct numbers have been found, so we cue the first EXA to move to the next part of its code instead. Otherwise, we loop back to the start of this section. With all eight numbers found, we just have to play back the correct ones. The first EXA takes the lead again, writing the placeholder digits over global M, and cueing this EXA over local M to send the next placeholder. Instead of keeping an incrementing counter, this EXA plays back the eight values from F for the placeholder digits. The code that plays back the placeholder digits ('PHLOOPII') is very similar to the PHLOOP block, except for the destination (global M). Once again, the second EXA cues the first EXA when all eight values have been written, sending a confirmation code; this will cause the first EXA to stop. Like the first EXA, once we have played back all the numbers, we're done; the second EXA wipes its file, and halts. code:
Once the dust settled, I was left with results of 83681/97/2. The histograms strongly suggest that everyone finds this solution. However, I concluded I wanted to do better. Next up: A matter of timing.
|
# ? Nov 5, 2022 23:55 |
|
After being unsatisfied with the speed of my first attempt, I concluded that one of the things that was making it slow was the regular M communication between the two EXAs in the modem. So I thought to myself, "If the digit and placeholder EXAs needed to communicate as rarely as possible, and each loop took the same time to run, I could synchronize them and have them run for a while without having to waste time with M communications!" It worked. code:
code:
Like the previous solution, the first EXA has exclusive jurisdiction of the digits of the phone number, but that's where similarities end. Both EXAs now need to hold a file with the digits of the phone number. This is the first part of the code: setup to copy over the phone number. The second EXA (which handles the placeholders) still needs to know the number of placeholders, which is sent over M. As it copies, the digit EXA also transforms the contents of its own file. Any digit has 10 added to it, while any X becomes 0. This is to speed up the performance of the main dialing loop later on. code:
Since the program spends most of its time in the kernel, it's important that we make it as quick as possible. This kernel takes 5 cycles to send a digit, and 4 cycles to (wait for the other EXA to) send a placeholder. I think this is the minimum number of cycles it's possible to get for each character type for this approach. Once the kernel has looped 11 times and all the digits of the number have been dialed, the digit exa reads #DIAL (and hangs up, if we dialed a valid number). If we didn't find anything, we jump back to the starting loop (the code can continue to run without resynchronizing). If we do find something, we fall through to the part of the code where we record the number, but we'll talk about that a little later on. code:
The solution is loop unrolling on a grand scale. After a 5-line prologue at the start of the loop which checks how many placeholders are present (this is why the digit EXA has four NOOPs - to stay in sync during this), the placeholder EXA switches to one of three variants of the placeholder loop. Each is carefully timed with NOOPs to be 5 cycles long, or 4 cycles when actually outputting the placeholder. The variants of the placeholder loop fall or jump to the next variant after the placeholder is output, with PHLOOP0 being the last (since there are no more placeholders, it can just TEST EOF and wait for the digit EXA to finish up). Finally, once all the numbers of the file have been output, the placeholder EXA also checks #DIAL, then either wraps back to the top or falls through to where the number we've found will be recorded. code:
code:
code:
That's it. 61718/141/2. A distinct improvement from my previous solution... but I still wasn't satisfied. So I came up with a crazy idea... Next up: What if we just did COPY F #DIAL 11 times?
|
# ? Nov 7, 2022 09:22 |
|
After the partial success of my last solution, I was thinking about how I had minimized the number of cycles needed for each step of the 'kernel' loop that actually emitted the digits. It was clear that it wasn't possible to optimize the loop further as it was, especially with the conditional logic involved in the loop (TEST F > -1, JUMP, TEST EOF, etc.) So a crazy idea came through my head: what if the loop where the EXA rattled off the digits didn't have any conditional logic at all? It turns out this is possible, though it took me several days to write. The basic idea works like this: we transform the list of 11 digits into a series of roughly 22 values (11 pairs of two values each). Each pair of values is an offset to seek by, and a digit to display; the EXA that runs the central loop can simply do SEEK F / COPY F #DIAL 11 times (!) to send all the digits to the modem. The reason we do this is for a simple reason: not only do we get a fast dialing procedure, we can make sure all the placeholder digits are the last three digits in the file. Since the dialing EXA deliberately reads the digits out of order, when the time comes to increment the placeholder digits, we can just start from the end of the file, and work backwards if more than one placeholder needs to be updated at once. The code below works in two stages. In the first stage, we do the setup. This is most of the lines of code but very little of the total cycles. In the second stage, we dial and record the numbers. This is simple, but slower. Setup: Basic File Structure There are three EXAs involved in setup: the parser-recorder, which parses the provided phone number; the offset calculator, which is responsible for 'wiring up' the connections to the placeholders at the end of the file, and the main EXA, which does a bunch of the setup math and will also do the dialing. All three start in the main host. Critically, the parser-recorder and main EXAs start in local mode, while the offset calculator starts in global mode. This will be critical when routing messages between EXAs. code:
code:
code:
The next step is to calculate the offsets so that the digits are dialed in the correct order. We pick up again with the parser-recorder. Setup: Offset Calculation code:
code:
The offset calculator also keeps track of the number of placeholders left, and cues the main EXA when it's time to move on. Once that happens, it will halt. code:
Under normal circumstances, with the pattern that's set up above with all the digits in order at the start, the 'jump back' value is (regular value + 2) x -1. However, there's an unfortunate edge case. Sometimes, placeholders appear back-to-back in the file. When this happens, we don't write the first offset. Instead, we find the 'jump back' value that was previously there, set it to 0 (which will cause SEEK F to move to the next value in the file without jumping), and write a new 'jump back' value after the next placeholder digit. The new 'jump back' value is equal to (previous jump back value) - 2. The way we can tell if a back-to-back placeholder is coming up is by looking at the place where we'd write the first offset. If it's a 0, the offset hasn't been used and it's not a back-to-back situation. If it's some other number, the offset has been used, and we switch to the back-to-back code. This is the main reason why we have a pair of buffer values in between the digits and placeholders; this ensures that if we jump to a placeholder, the offset that causes the jump will always be nonzero. To make matters worse, we have to account for the very special case of three placeholders in a row! If the 'jump back' offset we'd erase is already a 0, then we've been here once before - we can simply move forward two more places in the file if this happens to sort out this issue. Confused yet? Once all that math and jumping around is done, a file that originally looked like this... 9, 4, 0, 1, 7, 7, X, X, 9, 6, X ...will end up looking like this: 0, 9, 0, 4, 0, 1, 0, 7, 0, 7, 6, 9, 0, 6, 6, 0, 0, 0, 0, 0, -10, 0, -8 We can separate that out a bit. Numbers marked with a * are 'jump forward' offsets, while negative numbers are 'jump back' offsets. Placeholder digits are marked with a #: 0, 9, 0, 4, 0, 1, 0, 7, 0, 7, *6, 9, 0, 6, *6, 0, 0, #0, 0, #0, -10, #0, -8 This also divides it into three blocks, showing the digits, buffer zone, and placeholders. This is what we needed for everything else to be as fast as it should be. See if you can follow it yourself! Treat each first digit you run into as an offset (SEEK F) and each second digit as to be broadcasted. Remember that SEEK F also advances the file pointer, so SEEK F with a 0 moves your focus in the file 1 place forward. Main Loop At this point, it's time for the main EXA to do the actual dialing. code:
Once the number is dialed, the EXA checks #DIAL to see what turned up. If a number is found, the main EXA readies itself to send the message to the recorder; it does this by rewinding to the start of the file and doing SEEK F / COPY F M 11 times (with a loop this time, to save space). These values will be picked up by the parser-recorder EXA, which remains in the host to record the phone numbers. The recorder EXA will cue the main EXA whether to continue or halt. Once a number has been broadcast to the recorder (or not), the main EXA (if it didn't halt) increments the placeholders. It does by going to the second-last value in the file. This is the first placeholder. It adds 1 and writes it to the file; if the result is over 0, it backtracks two more places to the next placeholder and adds 1, and so on. Then it jumps back to the dial loop to start the process all over again. code:
Conclusion This took days to code, most of which was debugging and kicking out edge cases. But it's been worth it. 40251/145/1. I'm quite proud of this, and this is where I'll declare victory. Quackles fucked around with this message at 11:26 on Nov 7, 2022 |
# ? Nov 7, 2022 11:24 |
|
Part 49 - Española Valley High School === Trash World Inbox === Last time, I built the wardialer. My top scores were 22549/49/1. Quackles submitted three different solutions to the puzzle with a whole lot of explanation. Nice. Full disclosure, one of the reasons for featuring thread submissions in my updates is so everyone reading this elsewhere (LP Beach, SA, or the archive) can see them. But of course I do have to make a decision on what to actually show to keep it interesting for all readers. I think I need to summarize Quackles' submissions a bit to make this work. If you want to see them in full, they're posted in the SA thread. The first solution has 3 EXAs. One sends the static digits of the phone number, the other handles placeholder digits. The way it works is that the first one asks the second one for a digit whenever it sees the placeholder letter, and the second just keeps the current value of all placeholders in the X register, incrementing it by 1 each round, and uses SWIZ to send the separate digits to the first EXA, whenever the first EXA prompts it. It is told when a phone number works and then saves the placeholder digits to a file. The third EXA just sits in the home host waiting for the result. At the end, the first EXA sends all the static digits to the third EXA, and asks the second EXA whenever a placeholder comes up. This solution runs at 83681/97/2. Quackles's second solution runs at 61718/141/2. It still uses the placeholders-in-X with SWIZ trick. The main difference is that the first and second EXAs now both have the full phone number, so the second EXA doesn't need to be prompted to send a placeholder. They can just make sure that the dialing loop is perfectly synced up and send their own digits when their turn comes. For sending the digits to the third EXA, something similar to the first solution was used. I'm thinking it might be very slightly faster to have the second EXA handle that entirely but I didn't check. The main issue with this solution is that there are lots of wasted cycles, because for every digit, both EXAs need to test if it's for them or not. Quackles solved that in solution number 3. I'll post that in full. Note that Quackles is calling the main dialing loop the "kernel". Quackles posted:After the partial success of my last solution, I was thinking about how I had minimized the number of cycles needed for each step of the 'kernel' loop that actually emitted the digits. It was clear that it wasn't possible to optimize the loop further as it was, especially with the conditional logic involved in the loop (TEST F > -1, JUMP, TEST EOF, etc.) Oh my. Yeah, this is quite hard to follow. It gets easier if you can step through the code, so I added Quackles' full solution in the appendix below this post so it can easily be copy-pasted into the game. Remember that last optimization I did, where the location of the last placeholder was stored in the file, and I could just do a SEEK F? In summary, Quackles took that and applied it to the whole program. That way you can just do 11 SEEK F, COPY F #DIAL instructions and that's it. The setup to get there is quite convoluted though. Since the strategy is so different, Quackles' solution is hard to compare to mine. I suspect this code is about as fast as my solution before I made the ODD/EVEN EXA split. A round of dialing takes Quackles 22 cycles. With my negative numbers and MODI it only took 11 cycles. However, I think Quackles saves cycles during the INCREMENT part by having the placeholders together at the end, so it evens out. My code didn't require nearly as much fiddling at the start though. My program size was much smaller, which gave me enough space to implement the odd/even split. This roughly doubled the speed. Quackles' solution is very neat, but I'm even more convinced now that that MODI trick is the way to go for a top score. === School Management System === I don't know why we need to hack a school to sign someone up for a class, but fine. Let's do this. OST: Network Exploration The assignment: - Replace ENGLISH with AP ENGLISH (file 300) in selenium_wolf's child's class schedule (file 235). - Because those two classes are most likely not offered at the same time, you may need to rearrange the rest of their schedule to make it fit. Modify the schedule so that they are taking the same classes but at different times when necessary. A full list of classes offered is available in file 200. - Note that there will only be one valid schedule. - Also note that AP ENGLISH will only be offered once and each other class will be offered no more than twice. Hm. "Modify the schedule" sounds quite complicated. Let's just do it one step at a time. Most of the files are student's schedules. There are two other files. The one in the cafeteria says: "Today's menu. Entrees: Hamburger, Chicken Burger, Veggie Burger, Sloppy Joe's. Salads: Taco Salad, Chef's Salad. Fresh Fruit: Apple Slicers, Tangerine, Seedless Grapes, Mixed Fruit Cup." The file in the gymnasium says: "Attention students: Keep your hands and feet to yourself. This is not a "Kung Fu Dungeon". Be respectful of your fellow classmates." And yes, setting the #POWR in the gymnasium to 0 turns the lights off in the digicam feed. Enough distractions, let's get to the task. Good to note, selenium_wolf's kid is always in grade 11. I'm considering this general approach: 1. Remove ENGLISH class from the student's schedule. 2. Add AP ENGLISH to the file, in the right spot. 3. Check if it is at the same time as another class. If so, remove it, find the other moment that class is offered, and add that. 4. Repeat 3 until there is no more overlap. Let's find out if that works. The first step is easy enough. XA goes grab file 234 and finds ENGLISH (which it gets from XB over M), then deletes it. For AP ENGLISH, I need to look it up in file 200. XB has nothing better to do, so it gets this task. XB finds it in file 200, then sends both the time and name of the class over M. XA finds a class planned at the same time so it can insert AP ENGLISH in the right spot. This code will fail if there's a gap in the schedule (the exact time for AP ENGLISH isn't found). However, if that happens we know we're done and can just insert it as the final value and finish up. This will also work if we're trying to insert other classes later. So, for now I'll just add a TEST EOF to this loop and TJMP FINISH and I'll implement FINISH later. To find the next value, I can mostly reuse the code. If XA copies the overwritten class to M, XB can load it into X, and just reuse the FINDAPENGLISH loop to find it. The only difference is that XB might first run into the scheduled class that was just overwritten. We don't want to copy that because then it would just keep swapping the class in that time slot forever. code:
At this point, XB will start looking for the class that was just overwritten. Once it finds it, it sends the time on M. XA compares this time to the time that was just used. If it's the same, XA tells XB by sending a 0 and waits for XB to send a different time. In either case, XA then jumps into REPLACELP to replace the next value. Since every class occurs only twice, there's no need to check XB's second time, it'll always be different. XB now looks like this. code:
If the M value is not zero this means XA was happy with the time and immediately sent the next class to find. If it's not zero, it's actually the name of the next class to find another time for. The class that goes in the current spot in XA is sent over M now, and the next class is copied from T to X and used for the next round. Once XA hits FINISH, all entries are in the right order, except the final one which needs to be inserted into an empty spot. File insertions in EXAPUNKS are hard. At this point I realized something. If I can assume the last rescheduled class will always take the place of the original ENGLISH class (meaning there's no gaps in the schedule), I don't need to insert anything. Instead, I can start by scheduling AP_ENGLISH, and then repeat the loop to move other classes around, until something replaces ENGLISH. At that point, everything is in order and I just need to stop the EXAs. code:
code:
My assumption was correct, this works. 717/55/8. Top percentiles are 608, 39 and 3. Thinking about optimizations, activity is doable. Start off with one EXA, REPL it once to grab 200, then move the original forward into the grade 11 node. And instead of the KILLs add extra logic to each EXA to die if they get some specific M message or something. As for speed, I got a minor improvement to 710 by unrolling the XA FINDTIME loop. Unrolling the loop itself doesn't save anything because you need the conditional checks anyway, but it does allow you to combine that SEEK 1 outside the loop with the first iteration of SEEK 1 inside the loop. Perhaps bigger improvements are possible by changing something in the search order, or have more efficient/less M communication. Less M communication might also save some cycles. I'll leave the actual optimizations to the thread this time. Next update, x10x10x needs help to get back their anime. === Appendix === Quackles' wardialer solution in easy to copy format. code:
code:
code:
Carbon dioxide fucked around with this message at 19:11 on Apr 23, 2023 |
# ? Nov 12, 2022 15:29 |
|
It's exactly the sort of solution a socially-awkward hacker (but I repeat myself) would come up with. "Dad, I accidentally registered for English instead of AP English so I'm getting an elective credit instead of the English I need to graduate." "Well I could spend several hours on the phone and in offices talking to actual people for a chance at fixing this, or I could use all this crime hardware I've been accumulating and guarantee a best-case result."
|
# ? Nov 12, 2022 19:54 |
|
This is the most difficult challenge in the game IMO. Getting under 100 lines was really difficult for me.
|
# ? Nov 12, 2022 20:57 |
|
After the whole craziness that was last week's, this week is going to be straightforward. I don't have as much of an optimization on cycles as CO2, but I've been able to improve on activity a bit.code:
Once MESSENGERBOT is started, the split (messenger) EXA will handle all communications with the global class list. This EXA just gets the time slot to write to from the messenger, finds that time slot in Hunter's schedule, overwrites the new class name, and sends the old class name. It's worth noting that the loop where we search Hunter's schedule has been unrolled, allowing us certain optimizations. We can skip over the 'lunch' row entirely, and if we reach the end row, we don't need to test if it'll be the one we're looking for - it'll have to be. code:
Note that we also send out the time of the class we just replaced. This is used to avoid replacing the wrong class when searching the class list. When the class we replaced is ENGLISH (we hold on to the file with ENGLISH so we can check this), we know we're done. In this case, we KILL the writer EXA, tell the class list EXA to quit, and finish up. code:
If we receive a 0, we stop. That's it. 804/83/5. The trick to getting a low activity score is sticking to the use of M as much as possible. I could probably get even lower if I were to REPL the writer EXA off from the class search EXA. Fun fact: I also tried making a search table out of the class file (in the form Class, Time1, Time2), and then searching through it quickly for both times when needed. But this strategy takes 3-4 times as long!
|
# ? Nov 13, 2022 11:44 |
|
Part 50 - Let's go RAIDing === Trash World Inbox === Last time, hacking the school, I got 710, 55 and 8 as best scores. We got one improvement from Quackles this week. Quackles posted:After the whole craziness that was last week's, this week is going to be straightforward. I don't have as much of an optimization on cycles as CO2, but I've been able to improve on activity a bit. It is indeed easy to change your code to get the activity all the way down to 3. As you say, put all the code in one big EXA, then REPL XB off after the initial LINK 800. Since the original XA is holding file 300 it can go on to the grade-11 host. The other activity point comes from that KILL which can be removed if the messenger, when done, also sends a 0 in LOCAL mode, and the writer checks for that the same way the reader did (temporarily store it in T, try an FJMP, then copying it to X). === Personal Storage Array === OST: Code and Registers Alright, so x10x10x stored their anime on a drive array with three disks where all data is copied across all the disks for redundancy. However, this redundant array of independent disks is completely failing and it's up to us to retrieve their anime. Let's look at the assignment first. - The data on this drive array is duplicated across three drives for redundancy, with a file name index stored in the controller (file 200). Unfortunately, the drive array is failing. - For each file stored in the drive array, create a file in your host that contains the file name and data. Some values are corrupted and will read as a keyword (FAIL) instead of a number. You will need to read these values from a different drive that is not corrupted. - Note that some links may be unreliable as a result of the drive array's impending failure. That's not good. That drive is completely busted and I'll be lucky if I can get anything off of it. The third point means that links 801, 802 and 803 will sometimes just disappear and then return a couple cycles later. If that happens during M transmission, well, the EXA will just wait. But if that happens when the EXA tries to LINK there, it will run into a wall and die. Because everything is so unreliable, I'm going to handle the individual drives one by one. It'll be slower than parallelization but I have no idea how to make any sense of concurrent global M communication if links just randomly drop. I have to admit, this puzzle gave me more trouble than I expected. It's mostly that whenever I thought I had all parts working together, one or two tests would screw up everything because their links dropped in an unexpected order. I ended up rewriting major chunks of my code several times before I got it working. I think showing my thought process step by step as I'd do normally would be a bit confusing. So, instead I'll walk you through my actual working solution. I'll start with XA which is rather simple but takes some work off of the main EXAs. It starts with simply copying all the file IDs and file names from the index file. Once that's done, it will switch into local mode and let a visiting EXA know the next drive to LINK to. After each drive link ID it will also send a magic number, the purpose of which will become clear later. Then to XB. It handles the bulk of the logic and does so by making many REPLs. code:
But it also REPLs itself and the REPL will run the same code for the next file. This repeats for all 6 files, after which the final REPL jumps to KICKSTART. When this code is done, there are 7 EXAs in the home host. 6 of them are holding a (still mostly empty) result file, the 7th is the one that will kickstart the next step. Let's look at the kickstarter. code:
It is possible that the link dies in the one cycle between the DRIVE EXAs LINK command and the M message. In that case, the EXA will have successfully reached it, but TRYDRIVE makes another instance regardless. This could even happen multiple times. Now, that other instance can't actually link to the drive because with the one EXA in there, it's full. It'll either try repeatedly until it bonks into a missing link and dies, or it'll actually make it after the actual DRIVE EXA makes some space by GRABbing a file. That's why that EXA needs a KILL after the GRAB. This bit of code actually gave me the most trouble. To save on activity, I tried to have XA handle all this, but then there's messages going from the drive to XA and from XA to the XBs... and it all seems to work until some link disconnects at the worst time and everything desyncs. Making the kickstarter personally responsible was the only solution that worked for me. Anyway, we now got a stable EXA in the drive, and the kickstarter VOIDs its M. It then jumps back to local mode, copies the magic number from XA, goes back to the home host, and copies it to one of the six waiting EXAs (randomly chosen). Then the kickstarter's work is done and it stops. Before we go back to the six EXAs at home, let's see the rest of DRIVE's logic. code:
As a note, I tried to see if swapping the COPY 0 M and DROP made the code any faster, in case that M message was slow. It didn't - the solution was one cycle slower overall. But more importantly, doing so increased the activity. It's something I noticed a few times in this task. Even the smallest timing difference will change where the EXAs are in the code when the links are open, and getting to the drives will get harder or easier. The 6 EXAs in the home host all have a file (with the cursor sitting on the file ID) and are waiting for an M message. The kickstarter will send one, the magic value 1 from XA. code:
This way, one by one, all of the 6 EXAs will copy the data of their file from the first drive, and then just pass the baton to the next one. The random order doesn't matter because the EXAs run one by one and each knows which file ID it needs. None of this code needs retries because if the link is down, the M calls will just wait. The final EXA won't die, instead it will jump to NEXTROUND. At this point, there are 6 files with partially corrupt data in the home host, one being held by the sole surviving EXA. The DRIVE EXA is waiting for a next file to pick up, and XA wants to send the next drive's link ID on local M. code:
The NEXTROUND EXA will use a similar REPL strategy as before, to make 6 EXAs each holding a file. This time it just uses a counter to go through all homemade file IDs (starting at 400), and the final REPL will die as it tries to grab a file that doesn't exist. Again, all of them will wait for a message from the kickstarter, and it doesn't matter who gets it first. Importantly, this time the value isn't stored to X but in the placeholder at the start of the result file. code:
I did have to use X > -9999 instead of X < 9999 here because it turns out 9999 is a valid number in one of the tests. DONEFIX is directly after this code, so no matter which branch hits the EOF, we end up here: code:
If the value equals 6, the code jumps into NEXTROUND once more. The second DRIVE EXA gets killed, a third gets created, and the kickstarter comes back with a magic value of 6. A new set of 6 EXAs will be spawned, each one holding a partially fixed file, and each one will run another FIXNEXT loop, using the correct values from the third drive. But, with the magic value starting at 6, this DONEFIX code works slightly differently. After incrementing it the first time, this value will not be 6 so it will never jump into NEXTROUND again. If it's 12 it will jump into FINISH. Otherwise, it'll hand the baton to the next EXA, and jump into CLEANUP now since X is always larger than 6. Almost there. code:
XA already died since it ran out of lines of code to run. Quite convoluted, but it works. It might not be the best idea to let all 6 file writing EXAs die each round and recreate them but this was the best way I found to keep control. This runs at 5293/111/14. Apparently my cycle count is the kind of solution many people found. Top percentiles are 1012, 61 and 4. I bet the faster speed is gotten through some kind of parallelization but I have no clue how to set that up. The links disconnecting keeps messing with every idea I might otherwise have. By the way, the activity is 14 because of XA going to the controller (1), the kickstarter going to the controller and back thrice (6), every DRIVE EXA that actually makes it into a drive (3), and a total of two duplicate DRIVE EXAs that LINK into the drive and need to be KILLed (4). If anyone has any better scores let me know. Next time, we help deadlock book a trip.
|
# ? Nov 19, 2022 13:41 |
|
Here's the best solution I have.code:
code:
Once the drive controller EXA is assured of the connection, it copies the contents of the file from the first drive, FAILs and all, to its own file. The value 0 never appears in the video files, so the drive controller EXA can interpret a 0 as a signal to cut the connection. However, we can be clever about this - every file's length is a multiple of 6 values, so we only need to check for 0 on the first value received out of every six! This speeds up the copying procedure. (I'll detail the EXPLORER1 routine later, but it basically copies over the entire file in a speed-optimized way, using paralellism with REPL.) code:
The EXPLORER2 EXA will wait for instructions, then move forward through the file, copying over the value that's in a specific location. To do its part, the drive controller EXA scans the existing 'clean' copy of the file for FAIL values (TEST F > -9999). It keeps track of the number of value in the file since the last FAIL value in X. Once the drive controller finds a FAIL, it sends the value of X to the EXPLORER2 EXA. That EXA will move forward that many places in the file and report what it reads over global M. Then, the drive controller can patch the FAIL in its copy of the file with the received value. code:
code:
code:
While it reads the next two values, EXPLSEND1 sends the first two over M, then quits. There is only enough space for one REPLd EXA in eacha drive, so the next batch of values will wait while the first batch of values is sent. Once we reach the end of the file, we use REPL NULL to wait for the last file sending EXA to clear. There are no instructions after MARK NULL, so the dummy EXA does nothing, while the explorer sends an 0, indicating end-of-file. The second explorer routine, EXPLORER2, is much simpler. It reads a value over M telling it how many places forward to move in the file. It then sends back the value it finds there. This loops forever until it runs off the end of the file, which the drive controller EXA does deliberately by sending it a 9999 when it's time to finish with that drive. 4886/124/35. I do think it's possible to get a better result, but it'd involve a lot of paralellism, using REPL and local M to pipeline steps of the data processing. You could probably find some low-hanging fruit with an approach that reads the entire file from each drive. The first read simply writes it to the file directly. The second read could read in and REPL off to test if a value was FAIL, and then write to the file... anyway, I'm happy with what I've achieved.
|
# ? Nov 25, 2022 08:03 |
|
Part 51 - CrystalAir International === Trash World Inbox === Quackles posted:Here's the best solution I have. Quackles explains it well. The main speed-ups are in only sending values that failed before using the SEEK M trick, and also using a REPL to actually send data over M for the first file. You need to send one EXA per drive per file across the broken links and I didn't want to deal with that which is why I did one drive at a time, but handling the files serially definitely makes the code easier to optimize. === CrystalAir International- Ticketing System === And just like that, we're at the last of our currently unlocked tasks. OST: Code and Registers The assignment: - Each host in the network corresponds to a country and contains a list of tickets for flights (file 200) departing airports in that country, bound for airports either also in that country or in a connected country. Each ticket has the following values: ticket ID, departure airport, departure time, arrival airport, and arrival time. - Book the sequence of flights in deadlock's itinerary (file 300) by deleting each ticket from its file in the network and adding the ticket ID to file 301, choosing the first valid flight that departs after the preceding flight arrives. - Note that deadlock's itinerary will always begin in the USA, and that flight times may be compared with TEST. Looks like the USA is always the host we're directly connecting to, and that the network layout is the same for all the tests. I also think the tickets in each country's file are ordered by departure time. In this first test, our itinerary is DFW -> JFK -> CDG -> IBZ. Also there's a little thing that confused me in this test case. It says "Remove the tickets from the files (0/2)" at the goals in the bottom left. There's 3 tickets to find, it actually says 2 because there's only two files to remove tickets from, since DFW and JFK are both in the US. Well, to get started I'll find the first flight from DFW to JFK in the USA's file. I wrote two EXAs for that: code:
XB looks like this. code:
It goes into the USA host, puts the departure airport in X, then starts looking for flights leaving from there. If it finds one, it requests the arrival airport over M and checks if that's a match too. If not, it sends a zero back and keeps looking. Otherwise, it SEEKs back to the ticket number, sends that on M, and removes all 5 values for the ticket from the file. The next part is harder. The next airport can be in the same country or any adjacent country (I sure hope it can't be two countries over, that'd make the puzzle much harder). And we have to check the departure time as well. I wrote the last flight's arrival time to XB's X register for now but that won't fit. Let's ignore the departure time for now and first build the search in adjacent countries. After deleting the first ticket, XB now makes REPLs going in all four cardinal directions, and the original one stays where it is. They do a KILL, then GRAB the file. Then they all repeat the search code. This works efficiently because only one will have the file with the current departure airport. It's the only one that will ever ask for the arrival airport over M. All others will either reach EOF and die, or they'll stay busy until the next round, which is why I needed that KILL. It'll clear up leftovers from the previous round. It's possible to shift things here a bit - I don't need the DROP, KILL and GRAB for the EXA that stays in the current country. But this kept the code simpler. So, this code gets me the earliest known flight for each route. It still ignores departure times. Also, the EXAs get stuck at the end looking for a final connection that doesn't exist. But let's fix the departure times issue first. What I need to do is compare the arrival time of the last round with the departure time of this one. That's not going to fit in XB, so I'll have XA handle it as well. code:
XB now sends the ticket number as a third M message, which XA writes to file 301. I had to put in a SEEK 9999 in there because my code kept overwriting the first number. XB needed significant changes to make this work. code:
The SEEKs changed a bit to handle the extra jumping back and forth in the file. This code writes the right result, but XA will tell XB to go find a flight from the final destination to... the arrival time, which makes no sense. XB can't find that and XA will wait for a response forever. Let's go fix that. XA just needs some code at the bottom to check if only the final destination and the arrival time are left. code:
code:
That was a quite nice puzzle, not nearly as hard to figure out as some of the other post-game ones. 345/83/25. Top percentiles are 136, 58 and 8. For the speed, well, I do a lot of waiting on M. I'm sure that could be more efficient. Looking at the graphs, there's lots of people who somehow needed the full 150 lines of code to solve this. That high peak in the activity is probably what happens if you send an EXA to each country every round. The top percentile of 8 is interesting, though. You could send an EXA to each country once and have it start searching when getting a message on M, but there's 12 countries so that would still be an activity of 12. I think it involves something like only sending EXAs to neighbouring nodes when needed, but then actually keeping them there. So nodes that are completely in the wrong direction will never get visited and no link will be traversed twice. Either that or there are actually some countries deadlock never wants to go to. The next task is finally for Moss again. And as usual, Ember has some things to say. Well, this is something. I can see an open network that's... I can't even tell how big. Imagine all the computers in the world were hooked up to each other, and you would start to get an idea of the size. No way to know just how big until we explore, though. You have no idea how much I missed this part. Here's the first vote! A little bit later in the same conversation, Ember has another question. Why, do you think it could be dangerous? The second vote. Carbon dioxide fucked around with this message at 19:55 on Dec 2, 2022 |
# ? Nov 26, 2022 09:32 |
|
Uh oh.
|
# ? Nov 26, 2022 11:06 |
|
Uh oh. Dangerous for them, maybe.
|
# ? Nov 26, 2022 20:01 |
|
Anaxite posted:Uh oh. These. I barely remember this challenge. I think I must have banged it off in an hour with a terrible solution and never looked back.
|
# ? Nov 27, 2022 03:05 |
|
Uh oh. We have no idea what's out there.
|
# ? Dec 1, 2022 04:51 |
|
Part 52 - š Ñ| ö/ ~ öB è[ å‡ ÑE È‚ t 7Ò === My Computer === After all of that, we have unlocked Moss's final assignment. Wait, Morpork? That's... that's my computer name, like outside of the game. Well, this is something. I can see an open network that's... I can't even tell how big. Imagine all the computers in the world were hooked up to each other, and you would start to get an idea of the size. No way to know just how big until we explore, though. Four votes for Uh oh. Uh oh. Uh oh? Why, do you think it could be dangerous? One vote for "We have no idea what's out there", two for "Dangerous for them". Dangerous for them, maybe. That's the spirit. OST: Leave No Trace The assignment: - Packetize and transmit the target data (file 301) to the internet so that it is uploaded to a warez site (file 300). - A packet is a file that consists of the source IP address (from the #ADDR register), the destination IP address (from the DNS cache, file 201), the checksum of the packet's data, and between 1 and 30 data values. The target data should be split into multiple packets so that no packet except for the last contains fewer than 30 data values. - To calculate a packet's checksum, add the data values together considering each digit separately, wrapping back to 0 when a digit's sum reaches 10. Then flip the sign. For example, the checksum of 3097, 1047, and 2501 is -6535. Wait. WAIT. That Desktop host at the bottom holds files that are on my actual desktop. Like, that firefox.desktop file I opened up? That's just a shortcut to open Firefox in my Linux distro. And those hosts connected to the Desktop are sub directories. Moss and Ember are in my computer! Well, before I get started, I'll mess around with the hardware registers a bit. File 200 conveniently holds the link IDs to those hosts. The clock holds the actual IRL time and date. At least Moss isn't allowed to reset it. Let's mess with the #INVS register in the graphics host. My eyes. It turns out link 800 lets you go into the monitor, where you can change the contrast. At least everything resets once you stop the test simulation. Let's get this show on the road. Four files that actually matter. The reference to whereswarez.ru Ember prepared for us, file 301 with the actual data, then file 200 with the host addresses. It looks like the network host is 885 for the first five test cases so hopefully it stays that way and I can just hardcode that. Finally, there's file 201 with whereswarez.ru's IP address. code:
Next up, making packets of 30 data values. The checksum sounds complicated so I'll leave that out of scope for now. Let's go step by step. code:
code:
code:
Yes, that source IP address is my actual address, at least within my LAN. As soon as an EXA makes it to the internet, it immediately drops its file and dies with an "unknown host type" error. I can't run code on the 'net. Other than that, the simulation just shows the files sitting there in that 'internet' host forever. It looks like this code works except for the checksums. To calculate checksums, I need to iterate over all values, do something like a SWIZ to get each digit and then add it to a running total. That requires 2 registers at the very least. The best way to do this would probably be to use M. Thing is, I'm already using both global and local M in the network host. So that's going to be confusing. I'll try to do it in place instead. I added some code to just below the MARK DONECOPY. code:
code:
This same code is repeated twice more for the 100s digit and 1000s digit. The only difference is that the COPY X F at the bottom of the 1000s digit is replaced by SUBI 0 X F to flip the sign. The code duplication isn't very nice but I have no registers to spare to keep a counter in. The file is brought to the internet by the LINK 800 I already had and that's it. This code runs at 1453/150/11, but the max size is only 100. I got it to barely fit in 98 lines by rolling up all my @REPs. code:
My score is 2653/98/11. Top percentiles are at 640, 44 and 11. I know that especially the checksum code could be a whole lot more efficient, I just wrote myself into a bit of a corner by deciding to do everything in the network host (although that did give me top percentile activity score). Finishing this task gives you the steam achievement š Ñ| ö/ ~ öB è[ å‡ ÑE È‚ t 7Ò for "Completing every task in the bonus campaign". That means I now have all achievements except for 100 wins in the solitaire game and getting crazy high scores in the tetris game. I'm content. This is an interesting place. What are "warez"? Since this is the last full update, I'll just give you the dialogue trees here. I'm not sure. Whatever they are, a lot of people want them. --- What do you think they are? Something a lot of people want, it looks like. --- It means illegal downloads of software. Oh, no wonder it's so popular. And here the dialogue merges. We'll be able to spread easily from here. I still can't see the limits of this so-called Internet... but if they really hooked up all of their computers to each other the way it looks they have, I predict we'll have a very fun time. Let's see how far we can go. ... and that's all, folks. Ember and Moss are out here on the real internet now. We unlocked everything we could. I enjoyed how meta this last puzzle got, but ending the postgame with nothing more than a textbox feels a bit anticlimactic. Thank you to everyone who submitted solutions, left any other sort of comments, or just lurked and read my LP. It was quite the trip and I wouldn't have had the determination to finish this without your support. If anyone has any improvements for this last puzzle, or any others before it, this is the time to post them. I'll feature those in one final edition of Trash World Inbox next week, before closing the threads. Carbon dioxide fucked around with this message at 17:45 on Apr 23, 2023 |
# ? Dec 3, 2022 07:57 |
|
It's really fun that the game used your own computer's details like that! Thank you for the LP.
|
# ? Dec 3, 2022 10:15 |
|
|
# ? May 5, 2024 01:15 |
|
Well LPd, and thank you. Something to think about: If Moss was inside a simulation this whole time, that makes them necessarily a computer program, too. There might be a hint in Ember saying Moss's mind was 'simple'.
|
# ? Dec 3, 2022 10:35 |