Difference between revisions of "ChemiCompiler"

From Space Station 13 Wiki
Jump to navigation Jump to search
m (→‎Commands: Forgot to preview my change...)
(→‎Commands: Added the . command)
Line 103: Line 103:
* @ Transfer ax units of reagent from reservoir sx to resevoir tx.
* @ Transfer ax units of reagent from reservoir sx to resevoir tx.
* # Isolates ax units of reagent number *ptr from reservoir sx to reservoir tx. Indexing begins at 1.
* # Isolates ax units of reagent number *ptr from reservoir sx to reservoir tx. Indexing begins at 1.
* . Output an ASCII character to the buffer based on *ptr.
* ~ "Compile" the code, so it cannot be retrieved again via the load button. This does not prevent over-writing!
* ~ "Compile" the code, so it cannot be retrieved again via the load button. This does not prevent over-writing!



Revision as of 04:13, 20 May 2020

MechanicNew64.png This page is under construction.
The following information may be incomplete.
You can help by adding missing information or formatting.

"A complicated chemistry device, which uses a programming language derived from Brainfuck. It features 5 registers, 1024 bytes of RAM and 10 reagent reservoirs - to add beakers, click on the r[number] slots with a beaker in-hand. The machine can also generate pills or generate vials or trash the contents from beakers. "

Piff paff. Complicated? I think not. Scientists worth their salt can figure this out. Eventually.

Note that everything in this guide applies also applies to its portable version. However, keep in mind it only has 6 reagent reservoirs.

The Interface

The interface is broken up into a bunch of rows. The list numbering relates to the image.

The ChemiCompiler window.
  1. The code area. This is where you can paste in codes to save into the machine, or copy them out of the machine. It can accept very very long text strings.
  2. The load and save buttons. You need to save your code into the machine before you can run it. Once some code is in area-1, press the save button, then press a button in area-3 to save it to that slot. Pressing load, and then a button from area-3 will instead overwrite area-1 with the stored code.
  3. Code slots. The machine can store 6 codes, one per slot. These buttons turn green when they hold code. Pressing a button will begin the execution of the code stored in that slot, locking all further interactions with the machine until it completes.
  4. Reservoirs 1 thru 5. Beaker slots. These buttons turn blue when they hold a beaker. To insert a beaker, hold a beaker in-hand and press a button. Likewise, with an empty-hand, press a button to retrieve a beaker.
  5. Reservoirs 6 thru 10. More beaker slots.
  6. Register read-outs. Useful for debugging. Shows the value of the sx, tx, and ax registers at the time of completion. Only updates at the time of completion, does not update mid-execution.

Do not let the nice buttons and chui fool you, the interface beguiles the complexity within.

Signals

During normal operation, the chemicompiler emits a number of text-log cues to provide you feedback. These are as follows:

  • ChemiCompiler CCS1000 beeps quietly 2 times.

Beginning of a program.

  • ChemiCompiler CCS1000 beeps loudly once.

An operation has failed, probably due to a missing beaker in either sx or tx.

  • ChemiCompiler CCS1000 emits a slight humming sound.

Transfer step in a program

  • ChemiCompiler CCS1000 clicks.

Heating step in a program, once each for the beginning and end.

  • ChemiCompiler CCS1000 beeps quietly once.

The program has finished running and the machine is idle.


The sprite will animate while the machine is running, and it will also provide the normal chemical reaction messages if they occur.

Introduction to ChemFuck

Much like how modern programs run by executing one line of a program at a time, the ChemiCompiler works by executing one character from a string at a time. These strings of characters are referred to as "codes". An example code with 6 characters (6 commands) is "+}+)'@". The commands and the internal operations of the machine are based on the Brainfuck programming language. Thus the portmanteau ChemFuck. Our ChemFuck has more commands than it's predecessor, so it's marginally more "useful".

Everything in Brainfuck is about manipulating the Pointer. Think about the Pointer like a cursor selecting a cell on a super-wide single-row spreadsheet. The cursor can be moved left or right, one cell at a time. It's always selecting a cell and it begins at the very first one available at the far left. The memory is 0-indexed, so our first cell is named 00x. The subsequent cell is named 01x, and so on, following a hexadecimal naming scheme until the 256th cell, FFx.

  v
00x000 01x000 02x000 03x000 ... FFx000

In the representation above, the first two numbers are the Reference (the name). The last three, after the x, are the Value. We can do two primary things with the pointer - we can shift the pointer left (<) or right (>). We can add 1 to the value (+) or subtract 1 (-).

Say we wanted the numbers 1 through 10 stored in memory. This would be the program to write it

+>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++

Stepping through, it first adds 1 to the first reference (the pointer always starts 00x)

+
  v
00x001 01x000 02x000 03x000 04x000 05x000 06x000 07x000 08x000 09x000

Then it moves the pointer over 1 (>)

+>
  ------>v
00x001 01x000 02x000 03x000 04x000 05x000 06x000 07x000 08x000 09x000

Then it adds 1 to that reference twice

+>++
         v
00x001 01x002 02x000 03x000 04x000 05x000 06x000 07x000 08x000 09x000

And so forth, ending on reference 09x incrementing its value to 10

+>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++
                                                                 v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010

Because there is no multiplication or direct entry of numbers in Brainfuck, it's helpful to copy/paste +/- signs in known quantities if you need to reach larger numbers. And yes, chemfuck programs really are just long strings of symbols.

Commands

Registers:

  • ptr = data pointer (16-bit) (Data is 0-indexed.)
  • iptr = instruction pointer (16-bit) (1-indexed.)
  • sx = source register (8-bit) (Valid sources are 1-10 for the 1 through 10 reagent reservoirs.)
  • tx = target register (16-bit) (Valid targets are reservoirs as above, or 11 for pill generator, 12 for vial generator, or 13 for ejection port.)
  • ax = amount register (16-bit)

Basic pointer manipulation and arithmetic

  • > Increment the pointer's reference
  • < Decrement the pointer's reference
  • + Increment the value of the reference under the pointer
  • - Decrement the value of the reference under the pointer

While loops

Loops respect nesting.

  • [ Start of a While loop. If the value at the pointer is 0, it will skip to the close (]) of the loop.
  • ] Checks if the value at the pointer is 0. If false: looks backwards through the instructions until the matching [ is found, and resumes at that bracket. If true, continues execution past the ].

Of note: The ChemiCompiler will automatically halt after thousands of instructions if caught in an infinite loop, which can take a few minutes. However, it will hang indefinitely if there is a ] without a preceding [.

Register operations:

  • } Copies the value from the pointer to the Source register (sx).
  • { Copies the value from the Source register to the pointer. It's just backwards!
  • ) Same as }, but for the Target register. The Target register (tx) is used for setting both where a chemical will go if it's moved during the current step of the program, and for cooling.
  • ( Same as {, but for the Target register.
  • ' Same as ) or }, but for the Amount register. Amount (ax) is used for both the amount of chemicals to be moved, and for heating.
  • ^ Same as ( or {, but for Amount.

You get the idea, hopefully. There's two for each of these. If the chemcompiler throws out a string of loud beeps the first time you load a program into it, you've probably gotten these backwards.

Input operation:

  • , Measure the amount of reagent in reservoir sx and stores the value in the register (ax).

Other operations:

  • $ Heat or cool reagent in sx to ((273 - tx) + ax) Kelvin. Heating takes as long as the chemistry lab heater would take.
  • @ Transfer ax units of reagent from reservoir sx to resevoir tx.
  • # Isolates ax units of reagent number *ptr from reservoir sx to reservoir tx. Indexing begins at 1.
  • . Output an ASCII character to the buffer based on *ptr.
  • ~ "Compile" the code, so it cannot be retrieved again via the load button. This does not prevent over-writing!

Guide to Understanding the Internal Operations of the ChemiCompiler

Commands got you confused? This pointer / memory stuff not getting across? What's this added detail about copying to the sx, tx, and ax registers?

A graphical guide that focuses on 4 examples is available for viewing and download here.

Each example steps through the code one command at a time, highlighting the relation between the commands, pointer, memory, registers, and beakers. The examples are roughly ordered to scale in complexity:

  1. Moving fluid from one beaker to another.
  2. Cooling a beaker.
  3. Using loops to heat multiple beakers
  4. Non-destructively isolating reagents from a beaker, and outputting a pill.

The focus of the guide is to help you understand how the ChemiCompiler works. It does not aim to teach how best to make a ChemFuck program. Read on for a method for making a program.

Guide to Making Chemcompiler Programs

The Chemcompiler seems arcane, and almost useless at first glance. But imagine a cocktail like this:

25 Fluorosulfuric acid
24 Phlogiston
24 Dark Matter
12 Smoke Powder
12 Fluorosurfactant
3 Plasma

It's a bomb, it's a face-eater, it's a fireball, and the ChemiCompiler can make it.

This guide will show you how to systematically create the beginnings of this mixture. The goal is to make 75u of fluorosulfuric acid.

Initial steps and planning

In order to make a coherent ChemiCompiler program, you first need to have a plan for every step of your reaction. A lot can go wrong. But if you follow things through systematically, you might be ok. The plan is as important as the program. Do not neglect your plan. After forming a plan, we will code alongside it - it will be our comments and our test data every step of the way.

To make fluorosulphuric acid we need sulfuric acid, fluorine, hydrogen, and potassium in equal quantities in a beaker. Then the beaker needs to be heated up to 374 degrees. Sulfuric acid needs sulfur, oxygen, and hydrogen. Obviously we need to make the sulfuric acid first.

When working with the ChemiCompiler, you need to take care of the capacity and current volume of each beaker. It's helpful to note both as you plan and program. So we document all of that to ensure a clean reaction and help with testing later.

I start it out looking like like this:

Sulfur 50/50
Oxygen 50/50
Hydrogen 50/50 (2x)
Fluorine 50/50
Potassium 50/50
Mixdown 0/100

Note that Hydrogen is marked twice since it is used twice in the reaction - you should do this so that you know, later, which ones are best to double up a beaker with, because you only have 10 slots. Complicated reaction chains can run dry fast.

The goal is for the final mixdown beaker to be 3/4s full of fluorosulphuric acid (75u). We know that we need four components to make it, so we'll need 25u of sulfuric acid. But sulfuric acid has 3 components! We'd end up needed 12.5 of sulfur, oxygen, and hydrogen. No good, we need whole integers (or a much more complex program). What we can do instead is make more than what we need, and put it into a buffer mix. That's not a great solution for more complex reactions, but it's worth remembering as a fix, because residual chemicals suck. The buffer can be emptied at any time in the ChemCompiler using a few simple operations but that won't be covered in this example.

Sulfuric

Adding the buffer beaker, the setup looks like this

Sulfur 50/50
Oxygen 50/50
Hydrogen 50/50 (2x)
Fluorine 50/50
Potassium 50/50
Buffer 0/50
Mixdown 0/100

So now we know we need:

Move
13 sulfur, and
13 oxygen, and
13 hydrogen
into buffer.
Will produce 26u of sulfuric acid, so 
Move 25u from the buffer into mixdown

We want to keep track of these changes, because running out of something is potentially dangerous

Sulfur 37/50
Oxygen 37/50
Hydrogen 37/50 (2x)
Fluorine 50/50
Potassium 50/50
Buffer 1/50 (sulfuric acid)
Mixdown 25/100 (sulfuric acid)

Into fluorosulfuric

Now to produce the final mix. We already have the sulfuric acid ready, so we need to

Move
25 fluorine, and
25 hydrogen, and
25 potassium
into mixdown
Sulfur 37/50
Oxygen 37/50
Hydrogen 12/50 (2x)
Fluorine 25/50
Potassium 25/50
Buffer 1/50 (sulfuric acid)
Mixdown 100/100 (25 sulfuric acid, 25 fluorine, 25 hydrogen, 25 potassium)

Lastly, document the heating needed to change the precursors into the final form.

Heat Mixdown to 374K

Sulfur 37/50
Oxygen 37/50
Hydrogen 12/50 (2x)
Fluorine 25/50
Potassium 25/50
Buffer 1/50 (sulfuric acid)
Mixdown 100/100 (75 Fluorosulphuric acid!)

Success! It may seem overly redundant to lay it out like this, but we can use it to make our ChemFuck program quickly and easily now.


Actual Brogramming

Now to add the commands to match the plan.

Sulfuric

1 Sulfur 50/50
2 Oxygen 50/50
3 Hydrogen 50/50 (2x)
4 Fluorine 50/50
5 Potassium 50/50
9 Buffer 0/50
10 Mixdown 0/100

Notice the numbers before each beaker. These are the reservoirs we're using. We could have anywhere from 1-10, but I like to make the final mix be reservoir 10 for consistency. They're the numbered buttons in the chemcompiler menu. You load the beakers into the compiler by putting one into your hand and clicking the corresponding button - the beaker should disappear and the button should light up. Click the button again to retrieve into an empty hand.

You should start by loading this mix into the compiler. Every single one, yes. Don't leave any out.

To begin with, it's useful to load a bunch of values into the program that represent our registers, and just common numbers that we'll use constantly, so let's do that.

+>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++>+++++++++++++>+++++++++++++++++++++++++

That last two numbers are 13 and 25. They're there so that we can handle the sulfuric step without too much extra movement.

We'll put the starting code and a memory representation at the top of our plan.

 +>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++>+++++++++++++>+++++++++++++++++++++++++

Memory and Pointer:
                                                                               v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025

Now, modify the Sulfur part of the plan to include the reservoir names

Move
13 sulfur(r1), and
13 oxygen(r2), and
13 hydrogen(r3)
into buffer (r9).
Will produce 26u of sulfuric acid, so 
Move 25u from the buffer(r9) into mixdown(r10) 

Moving sulfur into the buffer requires three steps. Set ax to 13 - how much to move, set sx to 1 - where to take from, and set tx to 9 - where to deposit to. We do that by moving to and selecting the appropriate values. We'll set tx first.

<<<
Memory and Pointer:
                                                          v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025

And set tx

) (Register tx = 9)

Then we move back over to reference 10x, which has the value 13 we need for ax.

>>' (Register ax = 13)
Memory and Pointer:
                                                                        v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025

Lastly the source register sx needs to be 1.

<<<<<<<<<<} (Register sx = 1)
Memory and Pointer:
  v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025

With the registers set, all that's left is to trigger a transfer with the @ command.

So far, the top of our plan file should look like this:

+>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++>+++++++++++++>+++++++++++++++++++++++++

Move
13 sulfur(r1), and    <<<)>>'<<<<<<<<<<}@      (Registers: sx 1, tx 9, ax 13)
13 oxygen(r2), and
13 hydrogen(r3)
into buffer (r9).
Will produce 26u of sulfuric acid, so 
Move 25u from the buffer(r9) into mixdown(r10) 

Memory and Pointer:
  v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025

Alright! So far our code moves 13 units of sulfur to reservoir 9. The whole code looks like:

+>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++>+++++++++++++>+++++++++++++++++++++++++<<<}>>'<<<<<<<<<<}@

You can drop this into the compiler and test it out if you like. Hit Save, click 1 (in 1-6), and click 1 again. The compiler should beep a few times, and the sulfur should be moved. It's smart to test these steps as you go, since things may not mix exactly as you expect, you may make typos, you can count wrong, etc. Science! Face melting! Accidental explosions!


Hopefully it's obvious what to do next, but lets continue the example.

+>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++>+++++++++++++>+++++++++++++++++++++++++

Move
13 sulfur(r1), and    <<<)>>'<<<<<<<<<<}@      (Registers: sx 1, tx 9, ax 13)
13 oxygen(r2), and    >}@                   (Registers: sx = 2, and move)
13 hydrogen(r3)       >}@                   (Registers: sx = 3, and move)
into buffer (r9). 
Will produce 26u of sulfuric acid, so 
Move 25u from the buffer(r9) into mixdown(r10) 

Memory and Pointer:
                v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025

With all three reagents moved, there should now be 26u of sulfuric acid in reservoir 9. Next step is to move 25u to r10.

>>>>>>} (Register sx = 9)
Memory and Pointer:
                                                          v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025
>) (Registers: tx = 10)
Memory and Pointer:
                                                                 v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025
>>'@ (Registers: ax = 25)
Memory and Pointer:
                                                                               v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025

Putting it all together:

+>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++>+++++++++++++>+++++++++++++++++++++++++

Move
13 sulfur(r1), and    <<<)>>'<<<<<<<<<<}@      (Registers: sx 1, tx 9, ax 13, and move)
13 oxygen(r2), and    >}@                       (Registers: sx = 2, and move)
13 hydrogen(r3)       >}@                       (Registers: sx = 3, and move)
into buffer (r9).
Will produce 26u of sulfuric acid, so 
Move 25u from the buffer(r9) into mixdown(r10)  >>>>>>}>)>>'@   (Registers: sx 9, tx 10, ax 25, and move)

Memory and Pointer:
                                                                               v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025

And putting it all together:

+>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++>+++++++++++++>+++++++++++++++++++++++++<<<)>>'<<<<<<<<<<}@>}@>}@>>>>>>}>)>>'@

You now have a program to mix sulfuric acid. Now's a good time to test again. (It works.)

Into fluorosulphuric

Per the second half of our plan, we need to don't need to change tx or ax until the heating step.

Move
25 potassium (r5), and
25 fluorine (r4), and
25 hydrogen (r3)
into mixdown (r10)
Heat Mixdown (r10) to 374K

So lets move the reagents into mixdown

Picking up from the first half:
+>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++>+++++++++++++>+++++++++++++++++++++++++<<<)>>'<<<<<<<<<<}@>}@>}@>>>>>>}>)>>'@
Registers: sx = 9, tx = 10, ax = 25

25 potassium <<<<<<<}@ (Register sx = 5, and move)
Memory and Pointer:
                              v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025
25 fluorine  <}@      (sx = 4, and move)
Memory and Pointer:
                       v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025
25 hydrogen  <}@      (sx = 3, and move)
Memory and Pointer:
                v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025

Combined together:

+>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++>+++++++++++++>+++++++++++++++++++++++++<<<)>>'<<<<<<<<<<}@>}@>}@>>>>>>}>)>>'@<<<<<<<}@<}@<}@
Register: sx = 3, tx = 10, ax = 25

Now comes heating. The heating command heats (or cools) the Source register sx. So that should point at the beaker we want to heat. 10 is the beaker that we want to heat. Definitely not 3. The heating formula is 273 - tx + ax

Given that our goal is to heat -not cool- we can simply the equation by setting tx to 0. Now we just need to set ax to 110 to get 383K

So first, we're setting sx to 10 (the mixdown), setting tx to 0 (we already have this, we just need to go to the next uninitialized reference) and setting ax to 110! Then we use the heat operation, $

>>>>>>>} (Register: sx = 10)
Memory and Pointer:
                                                                 v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025

>>>) (Register: tx = 0)
Memory and Pointer:
                                                                                      v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025 12x000

>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++'
(Register: ax = 110)

Memory and Pointer:
                                                                                              v
00x001 01x002 02x003 03x004 04x005 05x006 06x007 07x008 08x009 09x010 10x013 11x025 12x000 13x110

$ (Heat!)
sx = 10, tx = 0, ax = 110

Copy/pasting all of that together, we get our glorious finished program, giving 75u fluorosulphuric acid from raw reagents, with only a modest amount of clicking.

+>++>+++>++++>+++++>++++++>+++++++>++++++++>+++++++++>++++++++++>+++++++++++++>+++++++++++++++++++++++++<<<)>>'<<<<<<<<<<}@>}@>}@>>>>>>}>)>>'@<<<<<<<}@<}@<}@>>>>>>>}>>>)>++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++'$

Homework/Addendum

You can compile a program using the ~ instruction. Put it at the end, and load it in. This prevents anyone from tinkering with it behind your back.

Setting tx to 11, 12, and 13 makes pills, vials, and disposes of reagents accordingly, when using the transfer (@) operation. If you're still not comfortable with the idea of making a plan like this of your own, try making an addition to the program to get rid of the lone remnant sulfuric acid in register 9, or make the compiler spit out acid pills, etc.

The "while" operators can create looping patterns in your program to do repetitive things without so much typing. But it's brainfuck. Most programs are like two lines long anyway. It's something to keep in the back of your mind while making your own programs/plans.

After you're done making fluorosulphuric, you have 3 (or 4, if you can keep from using the buffer) beakers to use for Science. Adding additional reactions, considering what you already have available, should be an obvious improvement to the program. The reservoir layout for the hellmix mentioned above is this:

1 - Stabilizer 2x
2 - Plasma 2x
3 - Radium/Carbon 50/50
4 - Potassium/Sugar 50/50
5 - Sulfur 2x
6 - Hydrogen 3x
7 - Oxygen 2x
8 - Fluorine
9 - Phosphorus 2x
10 - mix

But you can make your own recipes. Remember that the plan is key! Fancy chemical reactions are not all bad.

Additional tip: Don't forget that you can pre-mix you reservoirs! When you're comfortable, instead of filling beakers with 50u of a base chem, use the Chem-dispenser's bookmark feature to use exact-ratio pre-mixes. Pentetic Acid -the apex Rad & Tox treatment- Is made of 8 base-chems + welding fuel. Those 8 chems can be pre-mixed into 2 different beakers with bookmarks, saving many steps in the Chemi-compiler plan.


Department Guides
Engineering Making and Breaking · Construction · Gas · Power Grid · Thermoelectric Generator · Singularity Generator · Geothermal Generator · Catalytic Generator · Nuclear Generator · Mining · Materials and Crafting · Wiring · Hacking · MechComp · Mechanic components and you · Control Unit · Ruckingenur Kit · Reactor Statistics Computer · Cargo Crates
Medsci Doctoring · Genetics · Robotics · Telescience · Plasma Research · Artifact Research · Chemistry · Chemicals · ChemiCompiler · Decomposition
Security Security Officer · Contraband · Forensics · Space Law
Service Foods and Drinks · Botany · Writing · Piano Song Dump · Instruments
The AI Artificial Intelligence · AI Laws · Chain of Command · Guide to AI · Humans and Nonhumans · Killing the AI
Computers Computers · TermOS · ThinkDOS · Packets