Games related to programming

Table of contents

Child pages

Related pages

Background

  • When I was studying for the LSAT, I found it helpful to do other kinds of logic puzzles as a way of getting better at the LSAT's logic games. I learned certain lessons / best-practices from those other puzzles that I was able to then apply to the LSAT's games.
  • I suspect that the same thing may be possible with programming; by playing these games, in which a lot of the variables/frustrations/distractions of normal programming have been removed (like spending hours looking on Google to figure out what to do next, or not being able to understand the documentation), I suspect (hope) it will be easier to learn certain lessons about the 'best' way to do programming.

Developers

Articles

  • 2016.10.03 - RPS - Can videogames teach you programming?
    • Human Resource Machine
    • Hack ‘n’ Slash
      • "you have an incredible amount of freedom in approaching every puzzle, but at the same time, it can be terribly intimidating if you don’t understand why your solution simply didn’t work. To be fair, though, that’s pretty much programming in a nutshell."
      • difficulty spike makes Hack ‘n Slash a rough game to play, but if you can get past that, there’s a lot of good stuff to learn.
      • As a game, Hack ‘n’ Slash has a lot of problems. As a programming tutorial, it’s markedly better. (...) So long as you’re prepared to learn the hard way just how much trial, error, and frustration goes into coding, you can come away with a firm grip on the systems and processes that underpin all languages of the modern era.
    • TIS-100
    • Summary: So, can you learn to program by playing games? I’d say yes, with a couple of caveats. First, the challenges of programming are rarely as clearly defined as the puzzles in the aforementioned games; project requirements often change multiple times during development, and sometimes you won’t even know whether what you’re trying to do is possible until you do it – or give up. (...) Second, games alone won’t turn you into John Carmack; they can teach you the basics and help hone a programming mindset, but you’ll still need to hit the virtual books before you’re ready to make the next DOOM.

Lessons learned

  • Keep track of the patterns you're being taught.
    • Many of these games are cumulative in their puzzle design: they will have a puzzle that will require a certain technique to solve it, and then a later puzzle will require that same technique to solve a component of the later puzzle.
    • This makes it really hard for people who play the game intermittently, as you'll have forgotten the pattern by the time you revisit the game.
    • The solution (IMO) is to keep track of the patterns and think of them as "new moves" in the same way that those 30 rules of chess can be thought of meta-moves.
    • This kind of writing-down-what-you've-learned is exactly how I got good at the LSAT logic games.
  • Limit yourself to solving at most one puzzle per day.
  • Don't aim for your first solution to be an optimized solution. First focus solely on having a working solution. Be content with an unoptimized working solution.
    • It is much easier to get to an optimized working solution by first getting an unoptimized working solution and then looking for ways to optimize it. Going straight for an optimized working solution can make it extremely difficult to finish a puzzle.
  • Step through your code when you're feeling stuck.
  • Look for ways to break a puzzle down into smaller sub-puzzles or simpler versions of the same puzzle, and solve those smaller / simpler puzzles first.

Games I haven't played yet

Games I have played

7 Billion Humans

  • The sequel to Human Resource Machine

Lessons Learned

  • ...

Levels and the patterns / processes used to solve them

  1. You're Hired (cutscene)
    1. Robots have been able to take over all useful work, but people are complaining that they want work, so they're going to be given busywork. You see an alien hand reach from out-of-frame twice, making me think aliens are behind both the robots and this new jobs plan. Your job is to write the instructions for the humans to follow.
  2. Welcome, New Employees
    • Introduces the step, pickUp, and drop commands.
  3. Transport Squad
    • Introduces you to the idea of repeating commands (in this case, you put two step commands in a row to have the workers step two times).
  4. Long Distance Delivery
    • Introduces you to the jump command, which lets you loop your commands (in this case, looping the step command).
  5. An Important Decision
    1. Introduces you to the if/else commands.
  6. Little Exterminator 1
    1. Introduces you to the concept of a bug: there's a problem in a pre-existing program and your task is to find it and fix it.
  7. Collation Station
    1. This one seems to be the first puzzle where you need a nested if/else statement.
    2. Introduces you to the comment command.
  8. Intro to Morale Officers (cutscene)
    1. Doesn't seem to advance the plot. It's just two robots congratulating you on your progress and explaining how to congratulate a worker (with a kind word and a pat on the butt).
  9. Dynamic Angles
    1. Introduces optional size and speed challenges, which you can ask the boss about.
  10. Emergency Escapades
    1. Has a four-level-deep nested if/else statement.
    2. Has you control a person based on the number on a data cube at their location.
  11. Injection Sites 1
    1. Introduces you to the fact that an 'if' statement can have more than one condition.
  12. Unzip
  13. Injection Sites 2
  14. Intro to Shredding
  15. Shred Lines
  16. Little Exterminator 2
  17. Content Creators
  18. Uniquely Disposed
  19. Content Creators Bug Fix
  20. Reverse Line
  21. Big Data
  22. Number Royale
  23. Sorting Hall
  24. Budget Brigade 1
  25. My First Shredding Memory
  26. Budget Brigade 2
  27. Fitness Program (cutscene)
  28. Neural Pathways
  29. Biometric Access
  30. Fill the Floor
  31. Checkerboard Organization
  32. Creative Writhing
  33. Data Backup Day
  34. Seek and Destroy 1
  35. Intro to Calc for Art Majors
  36. Seek and Destroy 2
  37. Dangerous Spreadsheeting
  38. Seek and Destroy 3
  39. Printing Etiquette 1
  40. Printing Etiquette 2
  41. Image Decrypter
  42. Important Email Organization
    1. Task: A 6x10 area of randomly-numbered data cubes ("emails") numbered 0 through 99 needs to be sorted into shredders numbered 0 through 9, depending on the tens-place of the email.
    2. You have to deal with how to get the human to know which shredder to drop the cube in. I used a Memory slot to store their number divided by 10 and then just compared the label below them to that result.
    3. You have to deal with the "pick up nearest data cube" command leading the humans to pick up the labels above the shredders. I dealt with this by having them walk up to the top of the room before looking for their next data cube.
    4. You have to deal with the humans trying to pick up the label data cubes after they've exhausted all the 'normal' data cubes. I did this by having each human deal with 12 data cubes and then stop (because there are 60 total cubes that need to be shredded).
  43. Multiplication Table

Factorio

Human Resource Machine

General thoughts

  • I beat this game! I haven't beaten all the time/space challenges, though.
  • I suspect this game may not be getting enough credit by reviewers for its main reason for existing, which is to show explicitly how a computer works in a very real-world "a person is doing these actions" way. I think that's just brilliant.

Lessons learned

  • Use comments to label portions (stubs) of your code so you can work on them one-at-a-time.
    • I was working on a task that required some branching logic and I didn't have any comments to separate the different parts, and I ended up getting confused. Adding comments to divide up the different branches made it much easier to work on one part at a time.
  • The key for me to enjoy this game has been to 1) limit myself to attempting a single puzzle a day at most, and 2) not trying to have my first solution be an optimized solution, but instead being content with an unoptimized initial solution.
    • Before doing those two things I was finding myself feeling stressed out while playing the game.
  • Just as in TIS-100, this game is reminding me of the value of stepping through your code when you're feeling stuck.
  • Just as in TIS-100, the trick to solving scary-looking / big puzzles is to figure out how to break it down into smaller sub-puzzles.
  • One unfortunate thing about the game is that people totally new to programming are probably not going to be able to complete it, although they may get far.
  • 2016.02.21 - I did the totally wrong task for a Fibonacci question. I thought it was asking me to calculate Fibonacci of N, when in actual fact it was just asking me to output the Fibonacci sequence up to N. So I guess the lesson there is, double-check that you understand what you're being asked to do.

Infinifactory

General advice

  • Turn the music off. It's really annoying and was probably part of why I would feel gross after playing the game for a while.
  • Turn the ambient sounds all the way up (sounds of wind since you're up in the clouds).
  • Write down the patterns you discover when solving the puzzles, because otherwise if you step away from the game for a week or more (and thus forget the patterns you've seen) you may really struggle with any later puzzle which requires one or more patterns that were previously introduced in a simpler setting.

Levels and the patterns used to solve them

Proving grounds

Training routine 1
  • Objective
    • There are three single-box inputs facing three single-box outputs, but the left and right input-output pairs are not matched up correctly, so that the left input location is facing the right input's output location.
  • Thoughts
    • I had a fair amount of trouble with this one.
  • Insights / patterns taught
    • The big pattern / insight was "pay close attention to any irregularities among the inputs". In this case I noticed that one of the inputs would drop one square ahead of the others, and that made me go "now why would they do that?", which led me to think that I could run conveyers behind the input drop locations.
Training routine 2
Training routine 3
Training routine 4
Training routine 5

Skydock 19

Munitions refill Type 2
Munitions refill Type 6
  • Objective
    • Split a single input of boxes into three single-box outputs.
  • Insights / patterns taught
    • If you want to split off every Nth box from a conveyer belt, put a sensor 2N blocks ahead of a pusher to the side of the conveyer.
  • The simpler versions of the puzzle
    • First focus on splitting the input into two different outputs. Only then think about the third output.
Shuttle Propulsion Units
  • Objective
    • There's a single input, and you need to create a 3-block output and also a 2-block output.
  • Patterns required
    • Use the splitting trick from the previous puzzle and the use-grey-blocks-to-delay trick.

Learn Git Branching

  • Learn Git Branching
  • I found out about this from Andy. I remember he said at his job before Infer he and his colleagues would use it to understand what would happen in various situations.

Levels

Main

Introduction sequence
Introduction to git commits
  • Summary of text:
    • A commit is a snapshot of the repo.
    • The commits are expressed as the set of changes from one version of the repo to another.
  • To win:
    • Just run "git commit" twice.
Branching in git
  • Summary of text:
    • A branch is just a pointer to a specific commit.
    • There is no storage / memory overhead with making many branches.
    • Many git enthusiasts say "branch early, branch often".
    • Use "git branch <branchname>" to create a branch.
    • In this web app, an asterisk denotes which branch you're on.
    • Use "git checkout <branchname>" to get onto a branch.
    • Use "git checkout -b <branchname>" to both create and check out a new branch.
  • To win:
    • Just run "git checkout -b bugFix".
Merging in git
Rebase introduction
Ramping up
Detach yo' HEAD
Relative refs (^)
Relative refs #2 (~)
Reversing changes in git
Moving work around
Cherry-pick intro
Interactive rebase intro
A mixed bag
Grabbing just 1 commit
Juggling commits
Juggling commits #2
Git tags
Git describe
Advanced topics
Rebasing over 9000 times
Multiple parents
Branch spaghetti

Remote

Push and pull – git remotes!
Clone intro
Remote branches
Git fetchin'
Git pullin'
Faking teamwork
Git pushin'
Diverged history
To origin and beyond – advanced git remotes!
Push Master!
Merging with remotes
Remote tracking
Git push arguments
Git push arguments – Expanded!
Fetch arguments
Source of nothing
Pull arguments

Robot Odyssey

Rocky's Boots

Shenzhen I/O

  • This is a beautiful game. The artwork and sound effects are fantastic.
  • It's basically a high-production-value version of TIS-100.
  • It's more complicated than TIS-100.

General advice

  • Turn the music off.
  • Changes to designs are autosaved (just like in TIS-100), so if you want to experiment, make a copy first.
  • Hold tab to see the register names when you've got two modules close together.
  • Ctrl + Click to toggle breakpoints on the input/output graph.
  • Look for patterns in the desired output, because you may be able to use them to simplify your code.
    • For the pulse generator I took advantage of the fact that every time unit with the ouput at 100 should be followed by a time unit with the output at 0.
  • Consider taking something that's coded as a loop and looking for ways to partially hard-code it as a loop of multiple iterations of the same command.
    • I've done this in TIS-100 several times.
    • I did it for the pulse generator. Originally the code calculated every time unit individually, but then I saw that I could hard-code a two time-unit length of output.

Vocabulary

  • "Simple input" and "simple out" in the instructions for various assignments refer to the p0 / p1 "Simpe I/O"-type pins.

Differences from TIS-100

  • No commas between the register names ("MOV 5, RIGHT" → "mov 50 p1").
  • The concept of "time units" is totally new. There's nothing analogous in TIS-100.
    • The actual duration of a "time unit" is never specified, but it seems to be seconds or tenths of a second.
    • The only thing that consumes time units is the slp command.
  • When you click the 'step' command, it does two different kinds of things, depending on the current command to be executed:
    • If there is a non-slp command to be executed, that command will be executed, and the current time unit will remain the same.
    • If there are no non-slp commands to be executed, the time unit will be incremented by one.
  • When you mov a value to an XBus pin (p0 / p1), it isn't a single event (like in TIS-100), but rather happens over a period of time determined by the slp command.
  • Rather than tracking cycles (steps) to rank your solution, they track "power usage".
    • Each microcontroller has a "power" indicator on it that shows how much power it has consumed up to that point.
    • Power usage is incremented by one when any command runs.
    • The slp command increments the power usage for each time unit slept.
  • You can name your designs.
  • the code is auto-indented
  • you can have conditional execution of lines following a teq statement. "+" means "run if the teq above is true", and "-" means "run if the teq above is false".
  • There's no JGZ / JLZ / JEZ / JNZ
  • There's no SAV or SWP. You can write directly to "dat" (the equivalent of BAK).

Summary of the manual

  • Reference card
    • Basic instructions
      • nop
      • mov R/I R
      • jmp L
      • slp R/I
      • slx P - Sleep until data is available on the specified XBus pin (P).
    • Arithmetic instructions
      • add R/I
      • sub R/I
      • mul R/I
      • not - If the value in acc is 0, store 100. Otherwise store 0.
      • dgt R/I - Isolate the specified digit of the value of the acc register and store it in the ACC register.
      • dst R/I R/I - Set the digit of the value in acc specified by the first operand to the value of the second operand.
    • Test instructions
      • teq R/I R/I
      • tgt R/I R/I
      • tlt R/I R/I
      • tcp R/I R/I - This is a weird one. It's like a combination of the greater-than and less-than, or the opposite of the 'is equal' check. The "+" commands will execute if and only if the first operand is greater than the second operand. The "-" commands will execute if and only if the first operand is less than the second operand.
    • Registers
      • acc
      • dat
      • p0, p1
      • x0, x1, x2, x3
    • Notation
      • R - Register
      • I - Integer
      • R/I - Register or integer
      • P - Pin register (p0, p1, etc.)
      • L - Label
  • Application notes
    • The two types of interfaces
      • Simple I/O - Continuous signal levels from 0 to 100, inclusive. Unmarked(?).
        • This is used for getting input and sending output.
      • XBus - Discrete data packets from -999 to 999, inclusive. XBus pins are marked with a yellow dot.
        • This is used mainly for communicating between microcontrollers.
      • Simple I/O can be read or written at any time with no regard to the state of the connected devices.
      • XBus is a synchronized protocol. Data over XBus pins is only transferred when there is both a reader attempting to read and a writer attempting to write.
    • Sleeping
    • Touch-activated light controller
  • Language reference
    • Introduction
    • Program structure
    • Comments
    • Labels
    • Conditional execution
    • Registers
    • Instruction operands
    • Basic instructions
    • Arithmetic Instructions
    • Test instructions
  • Parts datasheet
    • MC4000
    • MC6000
    • DX300
    • 100P-14 (RAM)
    • 200P-14 (ROM)
    • LC70GXX
    • DT2415
    • C2S-RF901
    • FM/iX
    • N4PB-8000
    • MC4010
    • D80C010-F
    • KUJI-EK1
    • PGA33X6
    • NLP2
  • Supplemental data

Levels and the patterns used to solve them

Fake surveillance camera


Control signal amplifier


Diagnostic pulse generator


Animated esports sign


Drinking game scorekeeper


Harmonic maximization engine


Passive infrared sensor

  • First step: Have the alarm turn on.
  • Second step: Have the sensor dictate the output.
  • (I stopped taking notes.)

Virtual reality buzzer

  • Clue from the emails: The radio has a non-blocking XBus buffer that "will immediately return -999 when no data is currently available".
  • I found this one pretty easy.

Design and create an electronic game


SpaceChem

  • The art looks like a halfway point between his Flash games and his more-recent stuff.

Summary of the tutorials

  • A Brief Introduction (Video)
    • SpaceChem is about puzzles involving fake chemistry.
    • The board / programming-area is called a 'reactor'.
    • Your goal is to take the inputs on the left and create the outputs on the right.
    • In the center are four spaces called "bonders" that bond and unbond inputs.
    • The red and blue lines / circles are called "Waldos". They grab, move, rotate, and drop atoms.
  • Of Pancakes and Spaceships
  • Reactors and Reactions
    • Each square can contain one arrow and one non-arrow.
    • If an atom collides with another atom or the side of the reactor, the simulation is stopped.
    • 1/2/3/4 for the different simulation speeds, ~ to stop.
    • Space bar pauses and resumes.

TIS-100

Thoughts

  • My Steam review: This is a great game for people who want to get better at programming, because it gets rid of a lot of the confounding variables that make it hard to learn lessons from real-life programming (distractions like spending hours looking for stuff on Google). There are only ten commands to learn, and the language isn't buggy, so it's all about problem-solving rather than looking up information. I wouldn't recommend it for beginners.
  • This game isn't as hard to get into as it might seem from the screenshots. I'm only a few puzzles into it, so I'm sure it gets much harder, but the learning curve hasn't been killer so far (eg Dwarf Fortress). There are only ~10 commands to learn.
  • I would definitely not recommend this game to a person totally new to programming. This is a good game for an intermediate programmer.
  • I wish they allowed more than three saves per puzzle. It seems like a UI (screen space) decision more than anything else.
  • The nice thing about this game is that I feel like a genius when I solve one of the puzzles.

Things I'm learning:

  • It's serving as a great way to practice getting started on software projects. When I start a new puzzle I'm filled with that feeling of dread that I always get when I need to program a new thing, and the secret to success is to just crawl your way forward, to make any kind of change that puts you closer to the goal.
  • When you're stuck, try stepping through your code! It's amazing to me how often I resist doing this, and how quickly I often solve my problem once I do it.
  • 2017.01.08 - This game is really making it clear to me how often it's the case that starting is the hardest part. I'm consistently surprised at how quickly a solution comes together once I get started, and how much less time it takes than what I estimated when I first looked at a given puzzle. Of course, the opposite is possible as well: thinking something will take one amount of time, and it actually will take far longer...
  • 2017.01.13 - Signal Edge Detector - This one really hit home this idea: when you have a complicated project that will have multiple complicated parts, create a 'skeleton' / stub out different components and deal with them one-at-a-time. In this case, that means creating a kind of dummy function that returns dummy data, just for the purpose of getting the 'main' function up-and-running. Then go into those dummy functions and flesh them out.  But in the case of starting a company, this could mean something like "faking the back-end", where you have humans handling things that you eventually want the computer to be able to handle. That's what the Stripe founders did when they started, for example; they just wanted to see if people would use the front-end if it worked.
    • Also, don't spend time worrying about the efficiency of your solution until you have a working solution. Once you have a working solution, then go back and see how you might be able to make it faster.
    • If you're stuck, write out exactly what it is you are stuck about.
    • If you're stuck, write out a simple example in Notepad and write out the step-by-step process that would solve that simple example.
  • 2017.01.14 - Interrupt Handler - If you feel stuck, try taking a break and coming back to the problem to look at it with a fresh set of eyes. I did this when I was doing problems on the LSAT and it really helped. In this case, when I came back I wrote out in Notepad what the transformation was that I wanted to do, decided to experiment with a slightly different way of doing things than I'd done it up until that point, and it worked immediately.
  • 2017.01.15 - Signal Pattern Detector - Output a '1' if the last three inputs have been '0', and output a '0' otherwise. 
    • One of the things that struck me while working on this one is that the heart of these tasks is figuring out how to decompose the overall objective into smaller step-by-step objectives.
      • The smaller objectives I came up with were: 1) move the input to the output, 2) Check if the input is a '0', and if it is, move a '1' to the output, and otherwise, move a '0' to the output, 3) The same as #2, except saving the last input as well, 4) The same as #3, but saving both the last input and the one before it. The epiphany I had was to see that I could simplify the problem by just checking the current input, and then just checking the current input and the one before it.
    • Double-check things (like your understanding of what the computer is doing).
      • In this case, I had first gotten the machine to display the correct output for one zero, and I then wanted to get it working for two zeroes. And I wrote some code that increased the number of test cases that I was passing. But right before I was about to go on to tackle three zeroes I asked myself, "Am I absolutely sure that code did what I thought it did?", and sure enough, when I double-checked, the code was actually getting everything right except for the case of two zeroes. Hopefully this screenshot will help somewhat: 
  • 2017.01.22 - If you're stuck, try writing out a high-level description of what the solution should do, and then gradually refine that description to get closer to what the actual code should look like. 
    • Analogy: When you're doing a drawing / painting, one technique people use is to start with a rough sketch and then gradually refine it.
  • 2017.06.04 - Step through your program often! It's like being a manager and actually going down and seeing how all of the people under you are doing their jobs
  • 2017.11.27
    • What to do when you're stuck:
      • Go back to earlier puzzles and try to create a version that's the most efficient along some dimension.
      • Go back to earlier puzzles and refresh your memory of what they were testing and how you solved it.
      • Write the simplest possible solution (in terms of number of nodes and instructions) to each of whatever several sub-problems you identified for the target problem. Also clean up your code: choose better names for labels, indent code within a particular labeled section, etc.
      • Run the code and keep an eye on the BAKs for the different nodes. See if there are nodes where you aren't using the BAK.
      • Write out on Confluence all of the data you think you will need to have stored to solve the problem, and why.
      • Once you've written out all the data you think you'll need to store, look at what you've got so far and see which data you're already storing and which data you are not yet storing, and think about which additional variable you might be able to add next.
    • One thing you can do while solving the puzzles is to repeatedly run your work-in-progress solution, and look at what it looks like when it's running, and think about what it would probably look like when it's finished (in terms of the loops that you see running).
  • 2018.05.01
    • These problems can basically always be broken up into at least two sub-problems:
      • Solving the single case (i.e. assume there was just a single case of the inputs and make it work for that case)
      • Solving the looping problem (i.e. dealing with the need to loop through different test cases)
  • Things to think about: Can you hard-code a value that will make some calculation easier? For example, in Sequence Sorter the sequences are zero-terminated, but I can make the calculations easier if I *start* by adding a zero to my stack, and then look for it to know when the stack is empty.
  • Look at what "capabilities" you have that you aren't using: unused nodes, unused commands, unused BAKs, unused cycles in a node, etc. Think of these as being like unused troops in a war game (Combat Mission, Shogun, etc.).
  • Remember that if you start a line with an exclamation point, it works as a break point. Make use of that to look at parts of your code that don't run from the beginning, which will tend to make you tend to not step through those lines of code!
  • Write out all of the *communication* that needs to happen between different simultaneous processes. For example, there may need to be communication between nodes that handle input and nodes that handle output, in order to prevent them from interfering with each other when dealing with some data in memory.
  • Look for a node that is receiving data from another node that it uses to JMP to some label. That code may be better off as a JRO instruction.
  • I think TIS-100 becomes a lot more fun when you stop thinking about it as being about trying to solve those particular puzzles, and instead think of it as a challenge to come up with a *process* that will solve those puzzles as quickly as possible.
  • I'm also finding it easier to get into TIS-100 after having played a bunch of more graphically-interesting but less intellectually-interesting puzzle games (like Startopia, World of Goo, INSIDE).
  • Ask yourself if logic that's currently in one node might be better off in another node instead, as it could make it easier to use other nodes that aren't currently being made use of.
  • Step through your code, and when you get to some part that isn't correct, change that line, put an exclamation point at the beginning of it, and re-run the code to jump back to that line. And just repeat that over and over.
  • Use Ctrl + <Arrow key> to save time!

Summary of the manual

  • Crashes
  • Basic Execution Node
    • Registers
      • ACC - Stands for 'accumulator'.
      • BAK - Stands for 'backup'(?). Only accessible through the SAV and SWP instructions.
        • You can't read directly from this register, so you can't add, subtract, or JMP based on this register's value.
      • NIL - Reading NIL produces the value zero.
        • This is useful if you have a node that accepts an input from another node, and under certain conditions the input from that other node isn't needed, and your node needs to use its ACC register to store something (so you can't do "MOV <DIRECTION>, ACC"). You can just say "MOV <DIRECTION>, NIL" to clear the input from that other node.
        • It's also useful to keep your code easy to understand in situations where your node doesn't need to use ACC (so you could just say "MOV <DIRECTION>, ACC"), because it'll be clear that that input was unneeded.
      • LEFT, RIGHT, UP DOWN
      • ANY - I still haven't used this one.
      • LAST - I still haven't used this one.
    • Instruction Set
      • # Comments
        • You can name your program in the segment list by adding a line with two leading hashmarks, like this: "## test"
      • <LABEL>:
      • NOP - "No operation", it's equivalent to "ADD NIL". Why would you ever use this? Maybe to sync up two nodes?
      • MOV
      • SWP
      • SAV
      • ADD
      • SUB
      • NEG - Negate.
      • JMP <LABEL>
      • JEZ
      • JNZ
      • JGZ
      • JLZ
      • JRO <SRC>
        • -1 refers to the instruction before the current line.
        • 2 skips the next instruction
        • ACC will use the value in ACC to determine what line to jump to.
  • Stack Memory Node
    • Attempting to write to a full memory node or trying to read from an empty memory node will result in a block.
  • Keyboard shortcuts
    • Ctrl + Z - Undo last change
    • Ctrl + Y - Redo last change
    • Ctrl + X - Cut
    • Ctrl + C - Copy
    • Ctrl + V - Paste
    • Ctrl + Arrow - Navigate to the adjacent execution node
    • F1 - View instruction set quick reference
    • F5 - Begin running the current program
    • F6 - Step or pause the current program
  • Breakpoints
    • Put an exclamation point (!) at the beginning of a line.
    • When you hit a breakpoint, click 'Play' to resume execution. Execution will stop again if the breakpoint is hit again.
  • Visualization Module
    • The format for out put is 1) the starting X coordinate, 2) the starting Y coordinate, 3) one or more color values, and 4) a terminating negative value (e.g. -1).
    • The coordinate system starts at (0,0), which is in the top-left of the display.
    • Available colors:
      • 0 - Black
      • 1 - Dark grey
      • 2 - Bright grey
      • 3 - White
      • 4 - Red
    • Resolution - 30 characters wide and 18 characters tall.
      • The sandbox is 36 wide and 22 tall.

Levels and the patterns / processes used to solve them

Misc

  • Your cycle/node/instruction statistics show your best results across all of your solutions. So, like, if one of your solutions has the best node count, and the other has the best cycle count, both will have those values used for these stats.

Patterns summary

  • You may want to have some initialization code for a node followed by a loop that handles the "normal" case.
    • Examples:
  • Each node can be thought of as keeping track of a single variable.
    • Examples:
      • In "Integer Series Calculator", you can have one node decrementing an input value, a second node keeping track of an increasing summand, and a third node keeping track of an increasing sum.
  • Rather than sending JRO <#> and then MOV ACC, <DIRECTION> to control a second node from a first one, you can maybe just send ACC and have ACC be 0 or -1 when you want to toggle some other behavior.
  • If you want to jump back to the beginning of a node, add a "0:" label to your first line rather than having an 'END:' label on an empty line at the end.
  • Consider moving a block of code from one node to an adjacent node to free up space that you can use for other code.
  • Use one node to choose which of two other nodes to send on:

    0:MOV UP, ACC
    JLZ A
    JGZ B
    MOV LEFT, ACC
    ADD RIGHT
    MOV ACC, DOWN
    JMP 0
    A:MOV LEFT, DOWN
    MOV RIGHT, NIL
    JMP 0
    B:MOV RIGHT, DOWN
    MOV LEFT, NIL
    • From "Signal Multiplexer".
    • This either sends one or the other value or sums the two values and sends the sum.
  • Sort two inputs before sending them on:

    0:MOV UP, ACC # Get the first value to be compared
    SAV  # Save ACC to BAK
    SUB LEFT  # Subtract the other value it should be compared to
    JGZ A  # If it's > 0, the LEFT value is smaller and thus we want to send it first.
    B:SWP  # Otherwise it's <= 0 and so the UP value is the same or smaller, so we want to send it first.
    MOV ACC, DOWN
    MOV LEFT, DOWN  # Note that the LEFT node needs to send its value twice.
    JMP 0
    A:SWP # Send 
    MOV LEFT, DOWN
    MOV ACC, DOWN
    • From "Sequence Generator".

Main campaign ("TIS-100 Segment Map")

  1. Self-test diagnostic
    1. Instructions:
      1. Read a value from IN.X and write the value to OUT.X
      2. Read a value from IN.A and write the value to OUT.A
    2. Pattern(s) taught:
      1. Use the MOV command to accept input, to pass data from one node to another, and to pass data to the output.
  2. Signal amplifier
    1. Instructions:
      1. Read a value from IN.A
      2. Double the value
      3. Write the value to OUT.A
    2. Pattern(s) taught:
      1. You can save values to ACC.
      2. You can add two values by adding them both to ACC.
      3. You can multiply a value by adding it repeatedly to ACC.
      4. To get the "Parallelize" achievement you need to split up the work of multiplying the numbers across the various different nodes. This is a nice way to get acquainted with the idea that sometimes it may make sense to have multiple nodes doing the same thing.
  3. Differential converter
    1. Instructions:
      1. Read values from IN.A and IN.B
      2. Write IN.A - IN.B to OUT.P
      3. Write IN.B - IN.A to OUT.N
    2. Pattern(s) taught:
      1. You need to use NEG to solve this one.
      2. This is maybe the first puzzle where realizing something about the nature of the problem can allow you to simplify the way that you write your program.
      3. If you don't realize that you can negate one subtraction to get the value of the other one, and instead try to implement each subtraction separately, you'll find that ACC isn't enough memory to do what you need, and so you'll either need to use SAV or another node.
      4. General lesson: Try reusing data as late as possible in the transformation from inputs to outputs. The earlier you reuse it, the more logic you may need to write.
      5. This is the first puzzle where I'm noticing solutions by other players with lower cycle counts than my solution, and I'm not sure how they're achieving it. I think they might be creating solutions that are less-optimal with their node count and/or instruction count to get a lower cycle count.
  4. Signal comparator
    1. Instructions:
      1. Read a value from IN
      2. Write 1 to OUT.G if IN > 0
      3. Write 1 to OUT.E if IN = 0
      4. Write 1 to OUT.L if in < 0
      5. When a 1 is not written to an output , write a 0 instead
    2. Pattern(s) taught:
      1. This is the first puzzle that requires using conditional logic (JEZ / JLZ / JGZ).
      2. This is also the first puzzle that requires that you use labels (the targets of the jump statements).
  5. Signal multiplexer
    1. Instructions:
      1. Read values from IN.A and IN.B
      2. Read a value from IN.S
      3. Write IN.A when IN.S = -1
      4. Write IN.B when IN.S = 1
      5. Write IN.A + IN.B when IN.S = 0
    2. Pattern(s) taught:
      1. This again requires using labels.
      2. This is arguably the first puzzle where you need to write statements that will accept data that you will not be using. In other words, this is the first puzzle where using MOVing data to NIL is appropriate.
      3. You can have one node that takes in a value of <1, 0, or 1, and based on that value it can pass either of two other nodes' values. The IN.S node in this puzzle could probably just be copy-pasted into other puzzles. You can imagine pairing the IN.S node with another node that makes the determination of whether the value passed to the IN.S node should be -1, 0, or 1.
  6. Sequence generator
    1. Instructions:
      1. Sequences are zero-terminated
      2. Read values from IN.A and IN.B
      3. Write the lesser value to OUT
      4. Write the greater value to OUT
      5. Write 0 to end the sequence
    2. Pattern(s) taught:
      1. This is the first puzzle that has you output a zero-terminated sequence rather than a single number for each input.
      2. This is the first puzzle where it seems intended for you to use SAV and SWP. It's used when you want to 
      3. I could maybe see this being useful for the Sequence Sorter, since it's sorting two numbers.
  7. Sequence counter
    1. Instructions:
      1. Sequences are zero-terminated
      2. Read a sequence from IN
      3. Write the sum to OUT.S
      4. Write the length to OUT.L
    2. Pattern(s) taught:
      1. This is the first puzzle where you need to read a zero-terminated sequence as an input rather than a single input or an input of a defined length. So you need branching logic in your node to handle those zeroes.
      2. To get the "NO_BACKUP" achievement you have to solve it without using the SWP op. The way I solved it was to look at my original solution (that used SWP), remind myself how it worked (by stepping through it as it executed), understood that the SWP op was essentially being used as a control-flow operator (like an "if" statement) to toggle between summing and outputting, and so I just asked myself, "How else could I control what code gets executed?", and it was immediately clear to me that I could use the JMP operators (I used JRO). And then I just focused on the "sum" half of the problem (just focusing on the nodes to do that), and after that I did the "count" part of the question (which is basically the same except adding 1 instead of IN).
        1. To summarize: brainstorm to come up with a higher-level, abstract idea / concept for how the solution could work, then get into the details on the smallest possible division of the problem that you can tackle. Divide and conquer.
  8. Signal edge detector
    1. Instructions:
      1. Read a value from IN
      2. Compare value to previous value
      3. Write 1 if changed by 10 or more
      4. If not true, write 0 instead
      5. The first output is always 0
    2. Pattern(s) taught:
      1. This seems to be the first puzzle that has you work with continuous interconnected input rather than discrete inputs.
      2. You use a second node's SAV to keep track of the previous value.
      3. This is the first time you're testing whether a value "changed" (plus or minus) from the previous value. So you need to check for both whether the value increased by 10 or more and also whether it decreased by 10 or more. The way you do this is to first subtract (9 + the older value) from the newer value and seeing if the answer is greater than zero (this checks for an increase of 10 or more), and then you negate that result and subtract 18 to turn it into the opposite calculation: -1 * (new - old - 9) = -new + 9 + old = old - new + 9, and then subtract 18 to get old - new - 9.
        1. This puzzle is the first time you need to do something like algebra to figure out the right operations to run.
  9. Interrupt handler
    1. Instructions:
      1. Read from IN.1 through IN.4
      2. Write the input number when the value goes from 0 to 1
      3. Two interrupts will never change in the same input cycle
    2. Pattern(s) taught:
      1. This seems to be the first puzzle where you have a node's ACC value used to track the changing binary values of multiple other nodes.
        1. But I'm not sure it's a repeatable pattern, because there's a stipulation that only one node can change at a time.
  10. Signal pattern detector
    1. Instructions:
      1. Read a value from IN
      2. Look for the pattern 0,0,0
      3. Write 1 when pattern is found
      4. If not true, write 0 instead
    2. Pattern(s) taught:
  11. Sequence peak detector
    1. Instructions:
      1. Sequences are zero-terminated
      2. Read a sequence from IN
      3. Write the min value to OUT.I
      4. Write the max value to OUT.A
    2. Pattern(s) taught:
    3. How I broke it down:
      1. I made a program that, for each sequence, sends a '0' to both outputs. You can do this through a single path from input to output.
      2. I realized that you should forget about one of the outputs. Focus on creating an actual working solution for one of the outputs and you'll be able to modify that solution to get the other output working as well.
      3. I realized that, to find the min, you're going to need to delegate some work out of the 'main path' from input to output. You'll need to pass some input into this node and then get passed back the min.
      4. I realized that I would need to delegate work from this side-box to still another side-box, which would take two numbers and return the smaller one.
  12. Sequence reverser
    1. Instructions:
      1. Sequences are zero-terminated
      2. Read a sequence from IN
      3. Reverse the sequence
      4. Write the sequence to OUT
    2. Pattern(s) taught:
  13. Signal multiplier
    1. Instructions:
      1. Read values from IN.A and IN.B
      2. Multiply the values
      3. Write the product to out
    2. Pattern(s) taught:
    3. Thoughts
      1. I start by realizing that I've had to do this in Human Resource Machine, and the solution is to just keep adding the first value to itself while adding to a counter until it equals the second value (or decrementing the second value until it's zero).
      2. I start by just moving A to the end.
      3. I notice that I get the right answer when the first value is zero, so I say "I'm going to try to make the zero and one cases work first, since hopefully they're easier."
      4. I get the zero case working when A is zero.
      5. I notice the "stack memory nodes" and figure they're probably necessary to solve the problem.
      6. I figure out how to return every 'A' value multiplied by two, or zero if 'A' is zero.
    4. Thoughts from a previous attempt at solving the puzzle:
      1. I realized that to do the multiplication, I would need to use one of the inputs as a counter, which I would decrease by one over and over again, and I would add the other input to itself repeatedly, for each of those counter iterations. (I had already solved this challenge in Human Resource Machine, and so I had a tiny head-start, in that I knew the basic approach I would probably need to use.)
        1. I'm finding myself spending a lot of time staring at those stack memory nodes and trying to figure out how I'm supposed to use them. The idea I have for how to solve this puzzle doesn't involve a stack.
      2. I decided to start by making it work for the edgecase where either of the inputs are zero, since I knew my idea for how to solve the general case would not work when either input was zero. Getting this working required me to basically delete everything I had up to that point. So now it works where, if either input is zero, the output is zero, and otherwise it just outputs the second value. The logic to make this work took up an entire node.
        1. I created a stub node where it just takes the inputs and returns the second one.
        2. I realized that this stub node wouldn't have any other non-memory nodes around it to delegate work to, but that if I flipped the orientation of the solution horizontally, I'd have a node I could delegate to. So I flipped it, which was pretty easy (just renaming a few commands).
        3. I decide to use the newly-available node to just send '1' repeatedly, so I can subtract 1 easily (there's no '--' operator).
        4. Consider listening to music while working! It may help you stay in the zone, just like when you're playing a game.
        5. It's really, really helpful to keep your program in a 'working' state at all times. So it'll always run and produce output without breaking somewhere along the line.
      3. I've now got a version that decrements properly, so the only thing I need to figure out now is how to keep adding the right value to itself.
        1. When looking at your code and trying to figure out what to add next, clear out any junk that isn't actually necessary! It's easy for that stuff to accumulate, and it makes it harder to see where the new code needs to go.
  14. Image test pattern 1
    1. Instructions:
      1. Fill the image buffer with the specified target test pattern (a fully-white image)
    2. Pattern(s) taught:
  15. Image test pattern 2
    1. Instructions:
      1. Fill the image buffer with the specified target test pattern (a checkerboard)
    2. Pattern(s) taught:
  16. Exposure mask viewer
    1. Instructions:
      1. Read an X value from IN
      2. Read a Y value from IN
      3. Read a width value from IN
      4. Read a height value from IN
      5. Draw a rectangle of that size at the specified location
    2. Pattern(s) taught:
      1. This may be the first level where using JRO is necessary. It's the first level where I used it.
      2. This is also the level where I had to stop thinking in terms of my usual programming idea of treating a node identically to a traditional function. It's not the same as a function, because you don't have as-easy access to variables outside your current scope. To make it possible to access variables outside your current scope, you basically want to set up two parallel columns of nodes, each column corresponding to a particular scope.
      3. When you need a two-level loop, have four nodes arranged in a square, where the data enters the top-left node, the inner loop's data is tracked in the top-right node, the output is done in the bottom-right node, and the left nodes are used to handle the outer loop's data.
    3. My struggles
      1. The essential struggle I'm dealing with here is that:
        1. the solution seems to require two loops: an X loop and a Y loop...
        2. and it also seems that the loops must be able to communicate with each other to know when to stop...
        3. and I can't figure out how to do that in the space I have within each node.
    4. Intermediate conclusions I come to as I solve it: (this is really good to do, it reminds me of solving the LSAT logic games)
      1. It seems that the X loop should definitely be the inner loop.
        1. Why: Each successive X coordinate only requires a single additional output (the color, "3" in this case), whereas looping downwards as the inner loop requires terminating the current sequence with "-1", then re-specifying the X and Y coordinates for the new coordinate, etc.
      2. DONE (#3): It seems I will need to store the original X coordinate.
        1. Why: As I iterate through the Y loop, I will need to know what X coordinate each new line should begin on.
      3. DONE (#2): It seems I will need to store the width.
        1. Why: As I iterate through the Y loop, I will need to know how many times to iterate through the X loop for each new line.
      4. DONE (#1): It seems I will need to store the remaining width on the current line.
        1. Why: This is how I will know when to break out of the X loop.
      5. It seems I will need to store the remaining height.
        1. Why: This is how I will know when to break out of the Y loop.
      6. It seems I will need to store the current Y position.
        1. Why: As I iterate through the Y loop, I will need to know what Y position each new line should be at.
      7. It seems that having the functionality in series rather than passing data out orthogonally may be the better way to do things.
      8. It seems that it might be better if the inner loop was closer to the output node than the outer loop.
      9. Python version of the desired code:

        while True:
            x = input()
            y = input()
            width = input()
            height = input()
        
            for current_y_offset in range(height):
                current_y_position = y + current_y_offset
        
                output(x)
                output(current_y_position)
        
                for current_x_offset in range(width):
                    output(3)
        
                output(-1)
  17. Histogram viewer
    1. Instructions:
      1. Read a value from IN
      2. Draw a line that many units long to the right of the previous line drawn
    2. Thoughts while going through it.
      1. Ok, well since I've just finished the previous challenge, a clear difference here appears to be that the X loop is going to be the outer loop here and the Y loop is going to be the inner loop, whereas in the previous challenge the Y loop was the outer loop and the X loop was the inner loop.
      2. The challenge is thus going to be that for each "lit up" coordinate I will need to output the X position, Y position, color (3) and then -1.
      3. The X-value's looping doesn't need to know about the Y loop's value. It just needs to be told when to increment.
      4. (After having solved it:) I solved this one very quickly (in maybe 30 minutes) after spending a tremendous effort to solve the previous puzzle.
    3. Data I will need to store:
      1. DONE (#1): I will need to store the current X position.
      2. I will need to store the remaining height of the bar.
      3. It might end up being desirable to store the current Y position, even though it could be calculated from the remaining height.
    4. Intermediate problems / mini-puzzles:
      1. Draw just the top coordinate for each bar in the histogram.
    5. Pattern(s) taught:
      1. Just like the previous problem, this requires a square pattern for tracking the variables of an outer and inner loop.
      2. Also like the previous puzzle, the 'intended solution' seems to require using JRO for space-efficient conditional communication between nodes.
  18. Signal window filter
    1. Instructions:
      1. Read values from IN
      2. Write the sum of the last 3 values to OUT.3 and the sum of the last 5 values to OUT.5
      3. Assume prior in values are 0
    2. Thoughts while going through it:
      1. I see two stack memory nodes, so I'm probably going to need to use one or both of them.
      2. The first few inputs need to have an output for them which accounts for there not yet being 3 / 5 inputs.
      3. Also, you can't create a 'simple version' that just adds three inputs and outputs the result, because it won't produce a moving sum. It'll just produce a sum of every three inputs.
      4. An easier intermediate problem might be to have a moving sum of every two inputs.
      5. And a tricky thing about these memory nodes is that they don't tell the 'calling' nodes that it's 'empty'; it just locks instead.
      6. I think instead of using those two memory nodes separately, I may need to use them together as two different stacks. And when I'm producing these sums I'll need to use the execution node between them to shuttle the inputs from the one stack to the other and then back again. And then that 'middle' execution node (diagonal from the memory nodes) would be the 'coordinating / control' node.
      7. Actually, what I may need to do here is to create a queue using the two stacks.
        1. Hmm..but wouldn't you need two queues?
      8. I can deal with the first two inputs by having a one-off piece of code that adds two zeroes to the memory node.
      9. The fact that both memory nodes are on the same side of the grid seems like a clue to me.
    3. Data I will need to store:
      1. I will need to store the last five inputs.
      2. I may need to separately store the last three inputs.
      3. I may need to store the previous calculated sum of the last three and five inputs.
        1. This will allow me to just subtract the oldest input rather than needing to recalculate everything from scratch.
    4. Intermediate problems / mini-puzzles:
      1. DONE (#1): Have a moving sum of every two inputs. 
      2. DONE (#2): Have a moving sum of every three inputs. 
        1. Mini-mini-puzzle: Replace the bottom-most number from a stack of three inputs.
        2. The main insight here (which was probably based on something I'd heard before) was that I should use the two stacks to implement a queue.
        3. Here was a first (failed) attempt at solving this mini-puzzle by using the same approach that I used to get a moving sum of every two inputs: 
      3. DONE (#3): Have a moving sum of every four inputs. 
        1. This was the one where I needed to use NOP for the first time, to get the timing right between the execution node that adds new values to the memory node and the other execution node that swaps between the two memory nodes.
      4. DONE (#4): Have a moving sum of every five inputs.
        1. I don't have a picture for just this goal because before I even got it working it was clear how to get both the last-three and last-five sums at the same time.
      5. DONE (#5): Have a moving sum of both the last-three and last-five inputs. 
        1. Here are the stats on this solution: 
        2. I needed to have three NOPs to have the timing right between the two nodes writing to the memory node.
    5. Pattern(s) taught:
      1. If you need to have some group of commands executed a certain number of times, just stick a JRO at the end of them and use another (relatively empty) node to keep track of how many times they've been done and when it's time to quit. Just have a bunch of lines in the other node that say "MOV -3, UP" or something like that.
      2. The NOP command is useful to get the timing right between two execution nodes that both read and/or write to a memory node, and must do so in a particular order.
      3. (Again) Use JRO to have nodes control other nodes!
      4. You can use two (stack) memory nodes to implement a queue.
        1. ...but it takes up so much space that I doubt this pattern will be used in another larger puzzle.
  19. Signal divider
    1. Instructions:
      1. Read values from IN.A and IN.B
      2. Divide IN.A by IN.B
      3. Write the quotient to OUT.Q
      4. Write the remainder to OUT.R
    2. Data needed:
      1. a running tally that'll be the quotient
      2. it seems to me that the remainder doesn't necessarily really need to be stored
    3. Sub-puzzles:
      1. (DONE #1:) Find just the remainder.
      2. (DONE #2:) Find just the quotient.
    4. Pattern(s) taught:
    5. Misc thoughts:
      1. I solved this one relatively quickly...I'm getting better!
  20. Sequence indexer
    1. Instructions:
      1. Sequences are zero-terminated
      2. Read a sequence from IN.V
      3. Read index values from IN.X
      4. Look up index value in sequence
      5. Write indexed value to OUT
    2. Data that needs to be stored:
      1. Obviously the sequence needs to be stored.
      2. The count of how many numbers have been pulled off the first stack and onto the second one
      3. The total size of the sequence needs to be stored
    3. Thoughts while solving it:
      1. It's immediately clear that I'll need to move the list into one stack and then pull it onto the other stack to access the requested elements.
      2. I think the node two nodes beneath IN.X is likely to have a lot of logic in it.
    4. Sub-puzzles:
      1. (DONE #1:) Get the input onto the first stack, not including the zero.
      2. (DONE #2:) Have the count moved into ACC of the node below the node underneath IN.V
      3. DONE #3: Have the count tell the node between the stacks when it's safe to begin querying.
      4. DONE #4: Got it working for the first IN.X ("0").
      5. I now see that I'll need to store the numbers in the bottom stack because 1) the top stack stores the sequence backwards (last input popped first), and 2) the code handling IN.X doesn't know how big the sequence is, and so it can't adapt to deal with a backwards sequence (e.g. change the indices to the sequence's length minus the 'original' index).
      6. Nevermind, decided to just send the count to the other side
    5. Pattern(s) taught:
  21. Sequence sorter
    1. Instructions:
      1. Sequences are zero-terminated
      2. Read a sequence from IN
      3. Sort the sequence
      4. Write the sequence to OUT
    2. Pattern(s) taught:
      1. When what the node does next should depend on what happens on another node, use JRO. Don't use JMP and then MOV to ACC.
    3. Sub-puzzles:

      1. (DONE #1) Read a sequence from IN and write that same sequence to OUT.
      2. (DONE #2) Read a sequence from IN, store it in a stack, and then write the zero-terminated sequence to OUT. 

      3. (DONE #3) Rather than counting the size of the stack as you add elements to know when to stop pulling from it, add an extra element (a leading zero) so that you can just check when you've hit a zero to know when you've hit the end of the stack (this reduces the complexity of that part of the code, making it possible to add more functionality to each node that contains that logic). 
      4. Without regard to the output, just store incoming elements into the stack until the stack is full.

      5. Read the first two elements from each sequence and return them in order of increasing value.

    4. Relevant previous puzzles:
      1. All puzzles with the Stack Memory Nodes:
        1. TIS-100 Segment Map
          1. Sequence Reverser - But the way you use them is so basic as to be not very helpful.
          2. Signal Multiplier - But I didn't use them
          3. Stack Memory Sandbox - This has two Stack Memory Nodes in the same configuration as in the Sequence Sorter puzzle.
          4. Signal Window Filter
          5. Signal Divider - But I didn't use them
          6. Sequence Indexer - This shows moving a sequence between two stacks.
        2. TIS-NET Directory
          1. Sequence Merger
          2. Sequence Mode Calculator
          3. Sequence Normalizer
          4. Character Terminal
    5. Possible patterns to use:
    6. Thoughts:

      1. The basic way I see this working is that you'll have a sorted stack that has the smallest number at the top, and every time you introduce a new number, you'll start moving numbers from one stack to the other until you get to a number that is larger than your new number, and at that point you'll move your new number onto the first stack and add back all the numbers you'd moved onto the second stack.
        1. I can't think of another way that it could work *other* than that way.

      2. General trick: if you're doing more than one thing in a node and you're running out of space, look for a way to move logic out of the node into a different one.

      3. (SOLVED) One tricky thing is, when the code is popping from the stack, how does it deal with the case where it gets to the bottom of the stack? It seems like it would need to know how big the stack is, otherwise it'll just lock up when it tries to get a number from the empty stack.

        1. Answer: You add a leading zero before you start adding numbers to the stack, then look for that zero when pulling elements off the stack.
      4. 2018.05.01
        1. Rather than counting how large the stack is as I add elements to know when I need to stop pulling from it, I can instead just put a zero at the "end" of it and look for that. It's a lot less code.
        2. When trying to add new functionality to a simpler version of the solution, it can help to step through the code and think "What needs to happen now in the code to make the computer do what I think it needs to do next?"
        3. You cannot pass the input through the stack memory node on the top row to the node in the right corner as a way of getting logic out of the center node, because the node to the left of the top stack memory node has no way to know when it should or should not pass data through the stack memory node...unless the center-left node were to tell it? Hmm...seems complicated.
      5. 2018.07.11
        1. It seems like the best approach is to sort the numbers as they come in, but I wonder if it would be possible to sort the numbers once they're all in the stack.  I don't think this would have any advantages, but it's a thought that popped into my mind.
        2. It's line 4 of my partial solution where I need new logic.  Right now I have 'MOV ACC, DOWN', where it just immediately moves the input number to the stack, but instead of that I need to 'call a function' that will insert this number in the appropriate place in the stack.
        3. Ok, so if I move the input number to the node on the right (the node underneath the top stack), what further communication (if any) would be required between those two middle nodes? Hmm...if the node on the left is going to pull from the bottom stack when it hits a '0' from the input, then the node on the right would need to tell the node on the left that it's OK to continue pulling from the input.  But if the node on the left is only ever telling the node on the right what to do, then the node on the right would not need to communicate with the node on the left.  But in that case both the code for pulling from the bottom stack when sorting and also the code for pulling from the bottom stack when outputting would both need to go in the node to the right of the bottom stack.
        4. Ohhh, if I use the top stack for the 'real' stack and the bottom stack for the 'temp' stack, that lets me put the logic that's currently in the middle-left node into the top node, which frees up a lot of space for the sorting logic.   This could be a big breakthrough.
        5. The top node will have these roles: it pass a new number to the sorting logic, and it will tell the sorting logic when it's time to send the stack to the output.
        6. I'm going back to my solution for the 'Sequence Generator' puzzle because I think I could use that logic in this puzzle.
        7. Here's my guess at how the sorting logic should work:
          1. The sorting logic will receive a number_to_be_sorted.
          2. It will then move the top number from the permanent stack and the number_to_be_sorted to some comparison function.
          3. If number_to_be_sorted comes back as the larger number, it will move the number_from_the_stack to the temp stack and pull a new number_from_the_stack. 
          4. If the number_to_be_sorted is smaller, it will add that number to the permanent stack and then add back all of the temp numbers.
          5. If it hits a '0' from the permanent stack, it will add back the '0', then add the number_to_be_sorted, and then add back the numbers from the temp stack.
          6. After all this is done it will inform the input nodes that it has finished.
        8. Hmm...I feel myself struggling.  I feel like I'm having trouble keeping track of all of the constraints. The nice thing about the process I ended up with when doing the LSAT logic games was that I always knew what I should be doing next and I never felt like I was losing track of other constraints that would later come back to bite me.  Here I feel like I make a move (e.g. move some logic from one node to another) only to realize later that that move violates some constraint I had noticed earlier but forgotten about.
      6. 2018.07.12
        1. I re-read the manual and I'm wondering if I can make use of the ANY and/or LAST registers for this puzzle.
      7. 2024.03.29
        1. I started from scratch, just going one step at a time. I had the input node just write values to the stack memory node to its right, then I added switcher logic to switch to having the node underneath the stack node empty it out when the input node got a 0 input value.
        2. I then had the thought to move the logic to work with the other stack memory node, so basically moving the code in the input node down one node, and moving the code in the node underneath the top stack memory node down a node as well.
      8. 2024.05.05
        1. I had the thought today to copy my solution to Sequence Reverser into one of the programs for Sequence Sorter, to serve as a starting point.
        2. I then had the thought to move the code up as close to IN as possible to free up more space for the sorting logic.
        3. Higher-level description of the logic of the solution I have in mind:
          1. Input node:
            1. Read from IN
            2. If it's 0, send a signal to the Output node to dump the Stack Memory Node to OUT.
            3. Otherwise, send a signal to the Sort node and the value to be sorted.
          2. Sort node:
            1. If you get a signal to add a new value to be sorted, use the "compare two values" pattern to figure out whether the new value is less than or greater than the top value from the Stack Memory Node.
            2. If the Stack Memory Node value is larger, move it to the secondary Stack Memory Node, then repeat the logic for the next value on the top of the Stack Memory Node.
            3. If the top value of the SMN is 0, you hit the bottom of the stack so just add the 0 back and then the new value.
            4. Once you add the new value to the SMN, add whatever you have on the secondary SMN (stopping when you hit a 0, which indicates the end of the stack).
            5. Send a signal back to the Input node that you're done so the Output node can output everything.
          3. Output node:
            1. Read from the Stack Memory Node and send it to OUT.
            2. If you get a 0, that's the end.
              1. So we need to add a 0 to each Stack Memory Node.
            3. Send a signal back to the Input node that you're done b/c we don't want the Sort node doing work at the same time.
      9. 2024.05.07
        1. I think I'll move the code that adds the zeroes to the stacks into the corners where it won't take up valuable space.
        2. The decision tree is:
          1. Is the new number a zero?
            1. Yes: Output the list
            2. No: Get the top number on the stack. Is it zero?
              1. Yes: Add the zero back and add the new number.
              2. No: Is the new number bigger than the top number on the stack?
                1. Yes: Add it to the stack.
                2. No: Move the top item of the stack to the other stack.
        3. I want to have a clear process for knowing what I can pass out to other nodes to receive some answer back. Like, if I'm programming in Python, I know I can call a function that can take arbitrary input and give me back arbitrary output.
        4. Ok, different approach: I copied the code from Sequence Reverser to use as a starting point.
    7. Data that needs to be stored:

      1. The sequence of numbers, sorted, with the smallest number at the 'top' of the stack, and a zero at the bottom.
        1. Example:
          1. 15
          2. 27
          3. 67
          4. 86
          5. 0
      2. A stack to temporarily store values when you're trying to insert a new entry into the right place on the "real" stack (the stack that will have its contents moved to the output).
      3. The number of elements in the sequence.

        1. This is so that when you're outputting the sequence you'll know how many times to try to take something from the stack.

        2. Alternatively, you could put a '0' at the bottom of the sorted stack and use that to know when to stop pulling from it. That might be a lot simpler.
    8. Plain-english description of how it should work:

      1. Loop:
        1. Move a 0 onto both stacks.
        2. Take a number from INPUT.
        3. If the number is zero, output what you've got on your sorted stack, then restart from the beginning.

        4. If the number is not zero, pop one number at a time from the sorted stack, and compare that number to the new number.

          1. If the new number is smaller, add the popped number back where it was popped from, then add the new number, then add all of the numbers from the temporary stack.

          2. If the new number is larger, move it onto the "real" stack, then move the compared number, and then move the rest of the items from the temp stack onto the real stack.

    9. Problems I'm encountering:
      1. 2018.07.11 - There needs to be some communication between 1) the nodes which take from the input and add new numbers onto the stack and 2) the nodes which remove numbers from the stack and send them to the output.  That leads me to put all that logic in a single node, the node immediately above the bottom stack.
  22. Stored Image Decoder
    • Instructions:
      1. Read a length value from IN
      2. Read a color value from IN
      3. Draw a line of that length and color, wrapping when the edge of the image is reached
    • Required data:
      • The current X coordinate
      • The current Y coordinate.
      • The remaning length to print.
      • The current color.
    • Thoughts
      • The code for storing the X coordinate needs to be able to communicate with the code for storing the Y coordinate.
      • Man I just blasted through this level in like 10-15 minutes...

Bonus campaign ("TIS-NET Directory")

  1. Sequence Merger
    • Instructions:
      1. Sequences are zero-terminated
      2. Read a sorted sequence from IN.A
      3. Read a sorted sequence from IN.B
      4. Merge the sequences into a sorted sequence and write it to OUT
  2. Integer Series Calculator
    • Instructions:
      1. Read a VALUE from IN
      2. Sum all integers from 1 to VALUE
      3. Write sum to OUT
    • w00t finished this one in like 10-15 mins.
    • Thoughts on a solution:
      • Data that needs to be stored:
        • The value the sum up to.
        • The running sum.
        • The running summand(?).
      • Save the value.
      • Create a new '1' value.
    • Alternative solution: do 2 * the sum of the top and bottom number.
  3. Sequence Range Limiter
    • Instructions:
      1. Sequences are zero-terminated
      2. Read MIN value from IN.L
      3. Read MAX value from IN.H
      4. Read sequence from IN
      5. Write sequence to OUT, clamping values between MIN and MAX
    • Thoughts:
      • I worked on this one in at least two sessions.  (I solved it around 2018.07.27).
      • In the first session I got a solution that mostly worked, except it didn't handle the end-of-sequence properly, and I was constrained by a lack of space in my central two nodes.
      • In my second session I was able to free up one line in each of the two central nodes by moving logic to the peripheral feeder nodes. I did this by looking for repetitive code in the central nodes and noticed that they were sending a lot of instructions to the peripheral nodes, and that the peripheral nodes had a lot of space, and so if there was some way to move logic from the central nodes to the peripheral nodes, that could give me more space in the central nodes. At this point I didn't even know what I would do with the extra space in the central nodes, I just figured it was likely to help me get to a solution.
      • Once I had freed up one line in each of the two central nodes I stepped through the code (eventually using a breakpoint to skip right to where the end-of-sequence logic takes over), and saw exactly what the problem was, which was a minor one: I just wasn't sending a '0' to the output at the end of the sequence. Now that I had an extra line of code it was simple to add that additional instruction.
  4. Signal Error Corrector
    • Instructions:
      1. Copy values from IN.A to OUT.A
      2. Copy values from IN.B to OUT.B
      3. If a value is negative, instead copy the value from other input
      4. IN.A and IN.B will never be negative on same input cycle
  5. Subsequence Extractor
    • Instructions:
  6. Signal Prescaler
    • Instructions:
  7. Signal Averager
    • Instructions:
      1. Read values from IN.A and IN.B
      2. Write the average of IN.A and IN.B, rounded down, to OUT
    • Thoughts:
      • Obvious solution: Add the two numbers and divide by 2.
      • Problem: The numbers can add to greater than 999, but 999 is the max value that ACC can take.
      • Solved it 2018.07.27.  To solve this problem I went back to the "Signal Divider" puzzle and greatly simplified my solution, eventually getting it so that I could fit the dividing logic within a single node. I then copy-pasted that logic three times into three different nodes, one for IN.A, one for IN.B, and one for determining if the remainder of the two inputs was equal to 2 (in which case I would need to increment the output by 1).
      • My initial solution was very heavy on the cycle count, number of nodes, and near average on the instruction count.
  8. Submaximum Selector
    • The way to break down the problem seemed clear: rather than handle four inputs, just handle two, then expand that to three, then four.
    • Data I need to store: the largest value and the second-largest value.
  9. Decimal Decomposer
    • Solved this one in ~10 minutes or less (2018.07.27).
    • The way to break it up seemed pretty clear: the logic for computing the hundreds, tens, and ones would likely be the same.
    • It also was clear to me that the best way to do this would be to subtract 100, 10, and 1 while adding to some counter variable. I think the confidence I had in this intuition was probably a result of having just solved other similar TIS-100 puzzles.
    • I first solved the hundreds, then copy-pasted it to solve the tens and ones with minor changes to make those ones work.
  10. Sequence Mode Calculator
    • Instructions:
  11. Sequence Normalizer
    • Instructions:
  12. Image Test Pattern 3
    • Instructions:
  13. Image Test Pattern 4
    • Instructions:
  14. Spatial Path Viewer
    • Instructions:
  15. Character Terminal
    • Instructions: