This is an adaption of Core War, a programming KOTH dating back to the 20th century. To be more specific, it is using an incredibly simplified instruction set primarily based off of the original proposal.
In Core War, there are two programs battling for control over the computer. The goal of each program is to win by locating and terminating the opposing program.
The battle takes place within the main memory of the computer. This memory is called the Core, and it contains 8192 addresses. When the battle starts, the code for each competitor (called a warrior) is placed in a random chunk of memory. Program execution alternates between warriors, performing one instruction of each. Each instruction is capable of modifying a part of the Core, leading to the possibility of self-modifying programs.
The goal is to terminate the opposing program. A program terminates when it attempts to execute an invalid instruction, which is any
The Instruction Set
Each program consists of a series of low-level instructions, each of which takes two fields, called the A and B fields.
This instruction set draws heavily from the original spec. The main changes are 1) clarification on adding/subtracting commands, and 2) a change of the
# addressing mode to allow it to be used anywhere. Most full versions of Core Wars have over 20 opcodes, 8 addressing modes, and a set of "instruction modifiers."
Each instruction must have one of seven different opcodes.
DAT A B- (data) - This simply holds the numbers
B. Importantly, a process dies when it attempts to execute a DAT instruction.
MOV A B- (move) - This moves the contents of memory location
Ato memory location
B. Here is a demonstration of before-and-after:
MOV 2 1 ADD @4 #5 JMP #1 -1
MOV 2 1 JMP #1 -1 JMP #1 -1
ADD A B- (add) - This adds the contents of memory location
Ato memory location
B. The two first fields of both are added, and the second fields are added.
ADD 2 1 MOV @4 #5 JMP #1 -1
ADD 2 1 MOV @5 #4 JMP #1 -1
SUB A B- (subtract) - This subtracts the contents of memory location
Afrom (and stores the result into) memory location
SUB 2 1 MOV @4 #5 JMP #1 -1
SUB 2 1 MOV @3 #6 JMP #1 -1
JMP A B- (jump) - Jump to location
A, which will be executed next cycle.
Bmust be a number but does nothing (you can use it to store information, though).
JMP 2 1337 ADD 1 2 ADD 2 3
The jump means that
ADD 2 3will be executed next cycle.
JMZ A B- (jump if zero) - If both fields of line
Bare 0, then the program jumps to location
JMZ 2 1 SUB 0 @0 DAT 23 45
Since the two fields of instruction 1 are 0, the DAT command will be executed next turn, leading to imminent death.
CMP A B- (compare and skip if not equal) - If the fields in instructions
Bare not equal, skip the next instruction.
CMP #1 2 ADD 2 #3 SUB @2 3
Since the two fields of instructions 1 and 2 are equal in value, the ADD command is not skipped and is executed next turn.
When two instructions are added/subtracted, the two fields (A and B) are added/subtracted pair-wise. The addressing mode and opcode are not changed.
There are three kinds of addressing modes. Each of the two fields of an instruction has one of these three addressing modes.
Xis the line to be used directly in computation. For example,
#0is the first line of the program. Negative lines refer to lines in the core before the start of program.
... //just a space-filler ... ADD #3 #4 DAT 0 1 DAT 2 4
This will add the first of the two DAT lines to the second, since those are in lines 3 and 4, respectively. You would not want to use this code, however, because the DAT will kill your bot on the next cycle.
X- The number
Xrepresents the location of a target memory address, relative to the current address. The number at this location is used in computation. If line
#35is being executed and contains
-5, then line
... //just a space-filler ... ADD 2 1 DAT 0 1 DAT 2 4
This will add the second DAT line to the first.
@X- The number
Xrepresent a relative address. The contents at that location are temporarily added to the number X to form a new relative address, from which the number is retrieved. If line
#35is being executed, and its second field is
@4, and the second field of line
#39contains the number
-7, then line
... //just a space-filler ... ADD @1 @1 DAT 0 1 DAT 2 4
This will add the first DAT to the second, but in a more convoluted way. The first field is @1, which gets the data from that relative address, which is the first field of the first DAT, a 0. This is interpreted as a second relative address from that location, so 1+0=1 gives the total offset from the original instruction. For the second field, @1 gets the value from that relative address (the 1 in the second field of the first DAT) and adds it to itself in the same way. The total offset is then 1+1=2. So, this instruction is executed similarly to
ADD 1 2.
Each program can contain up to 64 instructions.
When a round starts, the two programs are placed randomly in a memory bank with 8192 locations. The instruction pointer for each program starts at the beginning of the program and is incremented after each execution cycle. The program dies once its instruction pointer attempts to execute a
Parameters of the Core
The core size is 8192, with a timeout of 8192*8 = 65536 ticks. The core is cyclic, so writing to address 8195 is the same as writing to address 3. All unused addresses are initialized to
DAT #0 #0.
Each competitor must not be longer than 64 lines. Integers will be stored as 32-bit signed integers.
In order to make programming easier for competitors, I will add a line-label feature to the parser. Any words that occur on a line before an opcode will be interpreted as line labels. For example,
tree mov 4 6 has the line label
tree. If, anywhere in the program, there is a field that contains
@tree, a number will be substituted. Also, capitalization is ignored.
Here is a example of how line labels are substituted:
labelA add labelB @labelC labelB add #labelC labelC labelC sub labelA @labelB
Here, labels A, B, and C are on lines 0, 1, and 2. Instances of
#label will be substituted with the line number of the label. Instances of
@label are substituted with the relative location of the label. Addressing modes are preserved.
ADD 1 @2 ADD #2 1 SUB -2 @-1
For each pair of contestants, every possible battle is performed. Since the outcome of a battle depends on the relative offsets of the two programs, every possible offset (about 8000 of them) is tried. Furthermore, each program has a chance to move first in each offset. The program that wins the majority of these offsets is the winner of the pair.
For each pair-up that a warrior wins, it is awarded 2 points. For each tie, a warrior is awarded 1 point.
You are allowed to submit more than one warrior. The typical rules for multiple submissions apply, such as no tag-teaming, no cooperating, no king-making, etc. There's not really any room for this in Core War anyways, so it shouldn't be a big deal.
The code for the controller, along with two easy example bots, is located here. Since this competition (when run using the official settings) is completely deterministic, the leaderboard you create will be the exact same as the official leaderboard.
Here is an example bot that demonstrates some features of the language.
main mov bomb #-1 add @main main jmp #main 0 bomb dat 0 -1
This bot operates by slowly erasing all other memory in the core by replacing it with a "bomb." Since the bomb is a
DAT instruction, any program which reaches a bomb will be destroyed.
There are two line labels, "main" and "bomb" which serve to replace numbers. After preprocessing, the program looks like this:
MOV 3 #-1 ADD @-1 -1 JMP #0 0 DAT 0 -1
The first line copies the bomb to the line immediately above the program. The next line adds the value of the bomb (
0 -1) to the move command, and it also demonstrates a use of the
@ addressing mode. This addition causes the move command to point to a new target. The next command unconditionally jumps back to the start of the program.
24 - Turbo
22 - DwarvenEngineer
20 - HanShotFirst
18 - Dwarf
14 - ScanBomber
10 - Paranoid
10 - FirstTimer
10 - Janitor
10 - Evolved
6 - EasterBunny
6 - CopyPasta
4 - Imp
2 - Slug
Dwarf > Imp CopyPasta > Imp Evolved > Imp FirstTimer > Imp Imp > Janitor Imp > ScanBomber Slug > Imp DwarvenEngineer > Imp HanShotFirst > Imp Turbo > Imp EasterBunny > Imp Paranoid > Imp Dwarf > CopyPasta Dwarf > Evolved Dwarf > FirstTimer Dwarf > Janitor Dwarf > ScanBomber Dwarf > Slug DwarvenEngineer > Dwarf HanShotFirst > Dwarf Turbo > Dwarf Dwarf > EasterBunny Dwarf > Paranoid Evolved > CopyPasta FirstTimer > CopyPasta Janitor > CopyPasta ScanBomber > CopyPasta CopyPasta > Slug DwarvenEngineer > CopyPasta HanShotFirst > CopyPasta Turbo > CopyPasta CopyPasta > EasterBunny Paranoid > CopyPasta Evolved > FirstTimer Evolved > Janitor ScanBomber > Evolved Evolved > Slug DwarvenEngineer > Evolved HanShotFirst > Evolved Turbo > Evolved EasterBunny > Evolved Paranoid > Evolved Janitor > FirstTimer ScanBomber > FirstTimer FirstTimer > Slug DwarvenEngineer > FirstTimer HanShotFirst > FirstTimer Turbo > FirstTimer FirstTimer > EasterBunny FirstTimer > Paranoid ScanBomber > Janitor Janitor > Slug DwarvenEngineer > Janitor HanShotFirst > Janitor Turbo > Janitor Janitor > EasterBunny Janitor > Paranoid ScanBomber > Slug DwarvenEngineer > ScanBomber HanShotFirst > ScanBomber Turbo > ScanBomber ScanBomber > EasterBunny ScanBomber > Paranoid DwarvenEngineer > Slug HanShotFirst > Slug Turbo > Slug EasterBunny > Slug Paranoid > Slug DwarvenEngineer > HanShotFirst Turbo > DwarvenEngineer DwarvenEngineer > EasterBunny DwarvenEngineer > Paranoid Turbo > HanShotFirst HanShotFirst > EasterBunny HanShotFirst > Paranoid Turbo > EasterBunny Turbo > Paranoid Paranoid > EasterBunny
The latest update (new versions of Turbo and Paranoid) took about 5 minutes to run on an old laptop. I would like to thank Ilmari Karonen for his improvements to the controller. If you have a local copy of the controller, you should update your files.