Learn ZoreWars
Write a tiny program. Drop it into a shared memory array with a rival. The last program still running wins.
What is ZoreWars?
ZoreWars is a modern take on Core War, the programming game A. K. Dewdney and D. G. Jones introduced in 1984. Two or more programs, called warriors, are loaded into a circular block of simulated memory called the core. A virtual machine takes turns running one instruction from each warrior. Warriors can read and write anywhere in the core, so they can overwrite each other's code, copy themselves, or set traps. A warrior is eliminated when it has no running processes left.
The game is a sandbox for learning how computers work underneath: memory addresses, pointers, loops, self-modifying code, and how a CPU fetches and executes instructions. Nothing escapes the simulation. The "memory" is just an array inside the VM, and warriors can only touch cells of that array.
Warriors are written in Redcode, a small assembly language that follows the ICWS'94 standard. You can also write them in JavaScript, Python or C++: those programs generate Redcode, so you get loops, functions and constants as a macro language while the battle itself stays in pure Redcode.
Quick start
npm start # serves the arena at http://localhost:8080
node cli/zorewars.js list # built-in warriors
node cli/zorewars.js battle @dwarf @mice --rounds 10
- Open the Arena. Two classics are loaded: Dwarf (a bomber) and Mice (a replicator).
- Press Run (or Space). Drag the speed slider to slow down or speed up.
- Hover over the core to read memory in the Inspector. Click to pin an address, and use Step (S) to run one instruction at a time.
- Edit a warrior and the change applies on the next round. + Add brings up to four warriors into the same battle.
- Run a Tournament to play many rounds from random starting positions. A single round can be luck; fifty rounds rarely is.
- Take a working warrior to the Ladder: it plays everyone on a king-of-the-hill kept in your browser, starting with the strongest library warriors. Open the Species map to see what kind of warrior yours is and who else plays that way.
Only Node.js 18+ is needed: no install, no dependencies. Python warriors run in the browser through Pyodide, which downloads about 10 MB the first time you use one.
How the machine works
The core
The core is a circle of 8000 cells by default. Every cell holds one instruction, for example MOV.I $0, $1. At the start every cell is DAT.F $0, $0. Addresses wrap around: the cell after 7999 is 0.
Relative addressing
Warriors never see absolute addresses. Every address is relative to the instruction that uses it: 0 means "this cell", 1 the next one, -1 the previous one. That is why a warrior works the same wherever it is loaded.
Instructions have two fields
Each instruction has an A-field and a B-field, each a number with an addressing mode. In ADD.AB #4, $3 the A-field is the immediate number 4 and the B-field points 3 cells ahead. Fields are often used as data: a DAT cell is a handy place to store a counter or pointer.
Processes and turns
Each warrior owns a queue of processes (program counters). It starts with one. On its turn, a warrior takes the process at the front of its queue, executes one instruction, and puts the process back on the end of the queue pointing at its next instruction. SPL adds a second process, so one warrior can run many threads, but they share that warrior's turns: with 10 processes, each one runs only every 10th turn.
Warriors alternate strictly: one instruction from warrior 1, one from warrior 2, and so on. One full lap is a cycle.
Dying and winning
A process that executes DAT (or divides by zero) is removed. When a warrior's queue is empty, it is eliminated. The last warrior with processes wins. If several warriors survive to the cycle limit (80,000 by default), the round is a tie.
Your first warriors
1. The Imp
MOV.I $0, $1
Copy the instruction at offset 0 (itself) to offset 1 (the next cell). Then the process moves to the next cell, which now contains... the same instruction. The Imp crawls forward through the whole core forever. It is nearly impossible to kill, but it never kills anything either. Running into an Imp turns your code into Imp, which is why Imps cause so many ties.
2. The Dwarf
step EQU 4
ORG loop
loop ADD.AB #step, bomb ; add 4 to bomb's B-field
MOV.I bomb, @bomb ; copy bomb to where its B-field points
JMP loop ; forever
bomb DAT.F #0, #0
Line by line:
ADD.AB #step, bomb: add the number 4 (A) to the B-field of the cell labelledbomb.MOV.I bomb, @bomb:@bombmeans "look atbomb's B-field and go that far frombomb". So the bomb is copied 4, 8, 12... cells ahead of itself.JMP loop: repeat.
Each pass drops a DAT 4 cells further on. Any enemy process that executes one dies. The bombs always land a multiple of 4 cells past bomb, and the Dwarf's other three instructions sit 1, 2 and 3 cells before it. Because 4 divides 8000, a multiple of 4 can never wrap around onto those cells, so the Dwarf never bombs itself.
step to 3. Run it alone (remove the other warrior). Why does it eventually die? Hint: 3 doesn't divide 8000.3. Mice, a replicator
ORG start
ptr DAT.F #0, #0
start MOV.AB #12, ptr ; cells to copy
loop MOV.I @ptr, <copy ; copy one cell, last first
DJN.B loop, ptr ; count down
SPL.B @copy, 0 ; start a process in the new copy
ADD.AB #653, copy ; pick a new destination
JMZ.B start, ptr ; and do it again
copy DAT.F #0, #833
Mice copies itself to a new place, starts a process there with SPL, and keeps going. Every copy does the same. The number of copies grows exponentially, so a bomber cannot destroy them as fast as they appear. Watch the "Processes over time" chart in the arena to see this happen.
Reading the arena
- Each square is one cell of the core, row by row: 100 cells per row for an 8000-cell core.
- Solid squares are code (any instruction other than
DAT), coloured by the warrior that last wrote them. - Dots are
DATcells: bombs, pointers and counters. - Squares glow when they were just written, and flash white when they were just executed.
- White squares mark the live program counters: where each process will execute next.
- The Dwarf's step of 4 bombs every 4th cell, which shows up as vertical stripes, because 100 is a multiple of 4.
Debugging a battle: how did it win?
Every round explains itself. While the VM runs, it records who last wrote every cell and which instruction wrote it, every process death and what caused it, and the first time each warrior wrote into, or ran, another warrior's code.
In the arena
- Why it ended appears when a round finishes. It gives a one-sentence verdict, for example: "Dwarf won at cycle 692. Scout's last process ran DAT.F #0, #924 at 0927: a bomb Dwarf wrote at cycle 691 using MOV.I $2, @2 at 0001." Below it is a timeline of first contact, conversions and eliminations.
- Show the killing blow pins the fatal cell in the Inspector, with its neighbours and who wrote each one.
- Replay the end re-runs the exact same round and pauses 100 cycles before the finish. Rounds are deterministic, so the replay is identical. Use Step (S) to watch the last moves one instruction at a time.
- Deaths, processes and ending code shows each warrior's original code next to what's there at the end and who overwrote it. It also gives how many processes died and why (own code, opponent bombs, empty core, division by zero), and which program the surviving processes are running.
- The Inspector shows the origin of any cell you hover over: loaded, empty, or written by which warrior at which cycle, using which instruction.
Conversions
Overwriting code doesn't kill a process; it changes what the process runs. The post-mortem reports when a warrior has been converted, for example "Imp has been converted: 7,853 of its 8,000 processes are running Mice's program." That is how Imp vs Mice usually ends in a tie: each warrior's processes end up running the other's code.
From the command line
node cli/zorewars.js battle @dwarf @scanner -r 10 --report # a verdict for every round
node cli/zorewars.js battle @dwarf @scanner -r 10 --report --round 3 # full post-mortem of round 3
node cli/zorewars.js trace @dwarf @scanner --round 3 --last 40 # the last 40 instructions executed
trace lists every instruction executed, with its cycle, warrior, address and process count, and marks the step where a process died and why. Add --json to either command for machine-readable reports, which is useful for AI agents improving a warrior.
Instruction reference
"A" and "B" below mean the cells the A and B operands point to after the addressing modes have been applied.
| Op | Effect |
|---|---|
| DAT | Data. The process that executes it is removed. Its fields are useful for storing numbers. |
| MOV | Copy A to B: a whole instruction with .I, or individual fields. |
| ADD | B = B + A, modulo the core size. |
| SUB | B = B − A. |
| MUL | B = B × A. |
| DIV | B = B ÷ A, rounded down. Dividing by zero removes the process. |
| MOD | B = B mod A. Dividing by zero removes the process. |
| JMP | Jump to A. |
| JMZ | Jump to A if B is zero. |
| JMN | Jump to A if B is not zero. |
| DJN | Decrement B, then jump to A if it is not zero. The classic loop counter. |
| SPL | Split: queue the next instruction, then queue a new process at A (unless the warrior already has the maximum number of processes). |
| SEQ / CMP | Skip the next instruction if A equals B. |
| SNE | Skip the next instruction if A does not equal B. |
| SLT | Skip the next instruction if A is less than B. Values are compared as 0…coresize−1. |
| NOP | Do nothing. |
| LDP | Load from P-space: read P-space cell A into B. See P-space. |
| STP | Store to P-space: write A into P-space cell B. |
Addressing modes
The symbol in front of an operand says how to find the cell it refers to. For indirect modes the operand first points at a cell, and that cell's field is used as a second, relative offset.
| Mode | Name | Meaning |
|---|---|---|
| # | Immediate | The number itself. As an address it means "this instruction". |
| $ | Direct | The cell that many steps away. This is the default when there is no symbol. |
| @ | B-indirect | Go to that cell, then move by its B-field. |
| * | A-indirect | Go to that cell, then move by its A-field. |
| < | B-predecrement | Like @, but subtract 1 from the B-field first. |
| { | A-predecrement | Like *, but subtract 1 from the A-field first. |
| > | B-postincrement | Like @, then add 1 to the B-field afterwards. |
| } | A-postincrement | Like *, then add 1 to the A-field afterwards. |
Pre-decrement and post-increment let one instruction both use a pointer and move it. MOV.I bomb, >ptr writes a bomb and advances ptr in a single step, which is how core clears work.
Modifiers
The modifier after the dot says which fields an instruction reads and writes.
| Mod | Uses |
|---|---|
| .A | A-field of A → A-field of B |
| .B | B-field of A → B-field of B |
| .AB | A-field of A → B-field of B |
| .BA | B-field of A → A-field of B |
| .F | Both fields, matched: A→A and B→B |
| .X | Both fields, crossed: A→B and B→A |
| .I | The whole instruction: opcode, modifier, modes and fields |
If you leave the modifier out, the ICWS'94 defaults apply:
DAT,NOP:.FMOV,SEQ,SNE:.ABif A is#;.Bif only B is#; otherwise.IADD,SUB,MUL,DIV,MOD:.ABif A is#;.Bif only B is#; otherwise.FSLT:.ABif A is#, otherwise.BJMP,JMZ,JMN,DJN,SPL:.B
The compiled Redcode panel in the arena always shows the full, explicit form, which is a good way to check what a line really does.
Redcode syntax
;name Shown in the arena and results
;author Your name
;strategy One or more lines describing the plan
step EQU (CORESIZE/4)+1 ; named constant (an expression)
ORG start ; where execution begins
start: mov.i 0, 1 ; labels may end in ':'; case doesn't matter
END ; optional; anything after it is ignored
- Labels name an instruction. Using a label in an operand gives its distance from the current line, so
JMP loopbecomesJMP -2. - EQU defines a constant. It may refer to labels and other constants.
- Expressions support
+ - * / %, parentheses, comparisons (== != < > <= >=) and&& ||. - Predefined constants:
CORESIZE,MAXPROCESSES,MAXCYCLES,MAXLENGTH,MINDISTANCE,CURLINE. - One operand:
DAT xmeansDAT #0, x; every other instruction gets$0as its B operand. END startis an alternative toORG start.- Assertions:
;assert CORESIZE == 8000warns you when a warrior is loaded under rules it wasn't written for. - Registers: inside an expression,
a=FIRST+10stores a value in a one-letter register for later lines, as in pMARS. - Anything before a
;redcodeline is ignored, so warriors pasted from old emails and newsletters work.
FOR / ROF loops
i for 4 ; repeat the body 4 times; i counts 1..4
bomb&i DAT #i, #i*100 ; labels bomb01 .. bomb04
rof
x for 0 ; FOR 0: a block comment
...
rof
pad for MAXLENGTH-CURLINE ; fill the rest of the program
DAT 0, 0
rof
The label just before FOR is the counter. In the body, the counter's name becomes its value and &counter pastes it as two digits into a label. Loops can be nested. For anything more complicated, a JavaScript or Python generator is easier to read.
P-space
P-space is a small private memory (CORESIZE / 16 cells, 500 by default) that each warrior keeps for the whole match while the core is wiped between rounds. STP stores a value there and LDP reads one back. Cell 0 is special: before each round it holds the result of the previous one (0 = lost, otherwise the number of survivors; -1 on the first round). Warriors use it to switch strategy after a loss. PIN (shared P-space) is not supported; it's ignored with a warning.
Not supported: read/write distance limits, and PIN.
How generators work
Programs in JavaScript, Python and C++ don't run inside the arena. They run once, before the battle, and print Redcode. That keeps every language on equal terms: the VM only ever runs Redcode, and anyone can read the compiled result. What a general-purpose language adds is a macro language: loops to unroll code, functions to build patterns, and arithmetic to choose constants.
All three libraries share one API:
| Call | Emits |
|---|---|
| mov(a, b) | MOV a, b with the default modifier |
| mov.i(a, b) | MOV.I a, b. Every opcode has .a .b .ab .ba .f .x .i |
| label("x") | Puts label x on the next instruction |
| org("x") | ORG x |
| equ("n", 4) | n EQU 4 |
| imm(4), ind("p")… | #4, @p. Also dir $, aind *, predec <, apredec {, postinc >, apostinc } |
| name/author/strategy | The metadata comments |
| comment(t), raw(line) | A comment, or any line of Redcode verbatim |
| CORESIZE… | The current rules, so you can compute steps that fit |
Operands can be numbers (jmp(-2)), labels ("bomb") or any Redcode operand string ("#step*2").
Converting Redcode to JavaScript or Python
Any Redcode warrior, including the famous originals, can be turned into a readable JavaScript or Python generator. Numeric EQUs become real constants, FOR/ROF blocks become real loops, and labels and comments carry over. Every conversion is verified: it must compile to exactly the same instructions as the original, or a simpler form is used instead. All 50 warriors tested, 34 of them archive originals, convert to the readable form.
- In the arena, below a compiled Redcode warrior, click Convert to JavaScript or Python. The conversion opens in a new slot, so you can read the two side by side or make them fight.
- From the command line:
zorewars convert classic:thermite --to pyorzorewars convert mine.red --to js --out mine.js. - Every library warrior already has converted versions in
warriors/javascript/andwarriors/python/, and in the Library menu marked "(converted)".
# Seeker, converted from Redcode by ZoreWars.
step = 12 # how far the scan moves each time
gap = 6
...
label("loop"); add.f("inc", "scan") # move both scan pointers forward by step
label("scan"); seq.i("last-scan-step", "last-scan-step+gap")
label("clear"); mov.i("kill", postinc("cptr"))
Two helpers make converted loops work: curline() returns the index of the next instruction (Redcode's CURLINE), and ldp/stp are available in every language.
JavaScript
Runs directly in the arena and in the CLI. The API functions are already in scope.
// Carpet Bomber: drop N bombs in a row, then skip GAP cells.
name("Carpet Bomber");
const N = 4, GAP = 16, LENGTH = N + 4;
if (CORESIZE % GAP !== 0 || LENGTH + N > GAP) throw new Error("carpets would hit us");
org("loop");
label("ptr"); dat.f(imm(0), imm(LENGTH));
label("loop");
for (let i = 0; i < N; i++) mov.i("bomb", postinc("ptr")); // unrolled loop
add.ab(imm(GAP - N), "ptr");
jmp("loop");
label("bomb"); dat.f(imm(0), imm(0));
Python
Import the library and call the same functions. In the browser it runs through Pyodide; with the CLI it runs on your local Python, with lang/python added to PYTHONPATH for you.
from zorewars import *
name("Dwarf (Python)")
STEP = 4
assert CORESIZE % STEP == 0
org("loop")
label("loop"); add.ab(imm(STEP), "bomb")
mov.i("bomb", ind("bomb"))
jmp("loop")
label("bomb"); dat.f(imm(0), imm(0))
When the script exits, the Redcode is printed to stdout. Run python examples/dwarf.py with PYTHONPATH=lang/python to see it.
C++
Include lang/cpp/zorewars.hpp (C++17) and call zw::emit() at the end of main. The CLI compiles the file with g++ or clang++, runs it and reads the Redcode. C++ warriors are CLI-only: browsers can't compile C++.
#include "zorewars.hpp"
using namespace zw;
int main() {
name("Dwarf (C++)");
org("loop");
label("loop"); add.ab(imm(4), "bomb");
mov.i("bomb", ind("bomb"));
jmp("loop");
label("bomb"); dat.f(imm(0), imm(0));
emit();
}
If <cstdlib> is included, write zw::div to avoid clashing with std::div.
Classic strategies
Most warriors fall into a few families. Like rock-paper-scissors, each tends to beat one family and lose to another:
These are tendencies, not laws. The bundled Scout is a deliberately simple scanner and still loses to Mice more often than it wins; making it beat Mice is exercise 5.
Battling the originals
Thousands of real warriors from forty years of Core War are published in public archives. ZoreWars can load them directly, without copying them into the project:
- In the arena, use Import… to pick a famous original, paste the URL of any Redcode file, or open a file. You can also drop a
.redfile onto the editor. - In the Hall of Fame, Fight opens an original against our version of the same idea.
- In the CLI, use a URL or
classic:nameanywhere a warrior goes.
Imports are fetched when you use them and kept only in your browser. Network files are always assembled as Redcode and never run as JavaScript, Python or C++. If a warrior was written for other rules, such as the '94x hill's 55,440-cell core, the arena offers to switch.
Every warrior below is in the arena's Library menu and the warriors/ folder. The Warrior Encyclopedia covers more families (imp rings, silk papers, CMP scanners, one-shots, quickscans), with each warrior's code explained line by line, its history and benchmark results.
| Warrior | Family | Idea |
|---|---|---|
| Imp | Imp | One instruction that copies itself forward forever. Survives almost anything and forces ties. |
| Dwarf | Stone | Drops a DAT bomb on every 4th cell. |
| Carpet Bomber | Stone | Unrolled loop (generated by JavaScript) that bombs in short bursts. |
| Mice | Paper | Copies itself and starts the copy with SPL. Process count grows exponentially. |
| Scout | Scissors | Tests cells with JMZ.F and only bombs what isn't empty. Uses SLT to avoid bombing itself. |
| Vampire | Stone variant | Drops JMP "fangs" that send enemy processes into a pit where they die. |
| Sweeper | Core clear | Writes DAT over every cell in order and stops safely on its own pointer. |
| Stunner | Stun bomber | Drops SPL 0 bombs that make enemies waste their turns, then sweeps the core. |
Ideas to explore next
- Imp spiral: several Imps spaced
CORESIZE/3apart, launched so they move as one unit that a single bomb can't stop. - Imp gate: a cell that is constantly decremented, so an Imp crawling over it breaks.
- Two-pass scanner: compare two cells at once with
SEQ/SNEto scan twice as fast. - Quickscan: an unrolled series of comparisons at the start of the round, ideal to generate with a JavaScript loop.
- Decoys: junk instructions that make scanners waste time.
Exercises
SLT check from Scout. Run it alone. When does it kill itself, and why?STEP as a constant and refuses to compile (with throw) if the bombs would ever hit its own code.SPL 0 before clearing.zorewars tournament warriors and get your warrior to the top of the table.CLI & AI agents
The CLI runs the same engine as the arena, so results match exactly. Every command takes --json, so scripts and AI agents can write a warrior, test it, read the score and iterate.
node cli/zorewars.js list # built-in warriors and their @refs
node cli/zorewars.js check mine.red # compile only
node cli/zorewars.js compile mine.js # print the generated Redcode
node cli/zorewars.js battle mine.red @dwarf --rounds 100 --json
node cli/zorewars.js tournament mine.red warriors --rounds 20
cat mine.red | node cli/zorewars.js battle - @mice # Redcode on stdin
node cli/zorewars.js classics # famous originals you can fight
node cli/zorewars.js battle mine.red classic:thermite --rounds 50
node cli/zorewars.js battle mine.red https://asdflkj.net/COREWAR/94/HILL32/arvon.red
| Option | Meaning |
|---|---|
| --rounds N | Rounds per match (default 1 for battle, 10 for tournament) |
| --seed N | Seed for load positions, so results are reproducible (default 1) |
| --coresize N | Core size (default 8000) |
| --cycles N | Cycles before a tie (default 80000) |
| --processes N | Max processes per warrior (default 8000) |
| --length N | Max warrior length (default 100) |
| --distance N | Min distance between warriors at load (default 100) |
| --fixed N | Warrior 2 starts at N in round 1 and later rounds follow pMARS's RNG, like pmars -F N |
| --pspace N | P-space size (default CORESIZE/16) |
| --json | Machine-readable output |
| -r -s -c -p -l -d -F -S | pMARS's own short options also work, so tournament command lines can be reused |
Exit status is 0 on success, 1 if a warrior failed to compile (errors come with line numbers) and 2 for bad usage.
JSON output
From battle @mice @dwarf --rounds 100 --json:
{
"ok": true,
"seed": 1,
"settings": { "coreSize": 8000, "maxCycles": 80000, "rounds": 100, ... },
"warriors": [
{ "name": "Mice", "author": "Chip Wendell (1986), ...", "wins": 83, "losses": 1, "ties": 16, "score": 265, "file": "warriors/mice.red", "length": 8 },
{ "name": "Dwarf", "author": "A. K. Dewdney (1984)", "wins": 1, "losses": 83, "ties": 16, "score": 19, "file": "warriors/dwarf.red", "length": 4 }
],
"rounds": [ { "round": 1, "winner": "Mice", "winnerIndex": 0, "cycles": 22698, "survivors": [0] }, ... ]
}
An agent loop
- Read
AGENTS.mdin the repository root: a compact reference written for language models. - Write a warrior file.
zorewars check file --jsonand fix any errors.zorewars tournament file warriors --rounds 50 --jsonand readranking.- Change one idea at a time, keep what scores better, repeat.
To have two agents fight, each writes a warrior and you run zorewars battle a.red b.red --rounds 200 --json. Use a fixed --seed to compare versions fairly, and a fresh one for final scoring so nobody tunes against known load positions.
Developer tools
Four commands for improving warriors, all running on every CPU core:
node cli/zorewars.js hill mine.red # score against the library, opponent by opponent
node cli/zorewars.js hill mine.red --classics # ...and the 34 Hall of Fame originals (fetched each run)
node cli/zorewars.js species # the behaviour map of the library
node cli/zorewars.js tune warriors/gatekeeper.js # search for better constants
node cli/zorewars.js evolve --generations 6 # breed new warriors
- hill plays your warrior against every library warrior (add
--classicsfor the originals) and lists the results from worst to best, with the smooth score. It also sorts every round it didn't win by cause: ties, being stunned or captured, being found fast, being worn down, or own goals. - species runs warriors alone in the core and places them on a map by two measures: the most processes they run at once, and how many different cells they execute. Warriors in the same cell play alike. The strongest warriors ever written are spread over many cells, so there is more than one way to win.
- tune works on JavaScript warriors that declare their constants as
const P = {...};and the ranges to try in a// SPACE {...}comment (Gatekeeper, Papercut, Blitz, Chameleon and Bandit in the library do). It screens random candidates, re-checks the best on fresh seeds so a lucky draw isn't kept, and--writesaves the winner into the file. - evolve breeds warriors from slices of the library (and of any warriors you name). It keeps the best warrior of each species cell (MAP-Elites), and each generation's champion joins the opponents, so the next champion has to beat its ancestors too (Red Queen). Children are repaired according to how their parent loses. Champions and the best of each species are written to
evolved/. The library's Evolved Silk came out of it. The method follows Digital Red Queen by Sakana AI and MIT.
Rules & scoring
| Setting | Default |
|---|---|
| Core size | 8000 cells |
| Max cycles | 80,000 per round |
| Max processes | 8000 per warrior |
| Max length | 100 instructions |
| Min distance | 100 cells between warriors at load |
- Warrior 1 is loaded at address 0; the others at random addresses at least the minimum distance apart. Two-warrior placement uses the same generator and rule as pMARS, so
--fixed N(pMARS's-F N) replays a pMARS match exactly. Games with more warriors use ZoreWars' own placement. - The warrior that moves first rotates every round.
- Each survivor of a round scores (W²−1)/S, where W is the number of warriors and S the number of survivors. With two warriors, a win scores 3 points, a tie 1 and a loss 0.
- ZoreWars is tested against pMARS, the reference simulator: identical assembly for 53 warriors and identical results for thousands of rounds. See
COMPLIANCE.md. - Tournaments are round robin: every pair plays a match and the table is ranked by total points.
- The smooth score is shown alongside: like the normal score, but a lost round is worth the share of the cycle limit the warrior survived, from 0 to just under 1. Dying at cycle 70,000 is better than dying at cycle 2,000, even though both are losses. It gives tuners and evolvers something to climb when everything loses.
- The arena's Ladder is a king-of-the-hill: a challenger plays everyone on it, and if it ranks in the top 10 it stays and the bottom warrior drops off. Warriors are ranked by points per round, then by smooth score.
Change the rules in the arena's Rules panel or with CLI flags. Warriors that use CORESIZE and friends adapt automatically.
Glossary & history
- Core: the circular memory array where battles happen. The name comes from "core memory", early magnetic RAM.
- Warrior: a program that competes in the core.
- Process: one thread of execution: a program counter in a warrior's queue.
- Bomb: an instruction written over enemy code, usually
DAT. - Hill: a standing tournament where new warriors challenge the current champions.
- ICWS'94: the 1994 draft standard for Redcode, which ZoreWars follows for instructions, modes and modifiers.
Core War first appeared in A. K. Dewdney's "Computer Recreations" column in Scientific American (May 1984). The International Core War Society published Redcode standards in 1986, 1988 and 1994, and online hills have been running since the early 1990s. ZoreWars keeps the rules that made the game interesting and adds a visual arena, generator languages and tooling for automated play.