initial
This commit is contained in:
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
node_modules
|
||||||
|
data
|
||||||
|
reference/dev.moment.com
|
||||||
|
farm/core*
|
||||||
|
farm/build
|
||||||
|
farm/bin
|
||||||
|
farm/farm
|
||||||
60
analyze/trace_parser.py
Normal file
60
analyze/trace_parser.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
|
class TraceparserError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Traceparser:
|
||||||
|
def __init__(self, file):
|
||||||
|
self.logger = logging.getLogger("Traceparser")
|
||||||
|
self.file = file
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
current_map = None
|
||||||
|
pos = (0, 0)
|
||||||
|
lid = 0
|
||||||
|
for line in self.file:
|
||||||
|
lid += 1
|
||||||
|
line = line.strip()
|
||||||
|
if line.startswith("MAP"):
|
||||||
|
current_map = line.split()[1]
|
||||||
|
print(f"Current map: {current_map}")
|
||||||
|
elif line.startswith("A"):
|
||||||
|
m = re.match(r'A(\d+) @(\d+):(\d+) R:([0-9a-f]+) ([0-9a-f]+) ([0-9a-f]+) ([0-9a-f]+) ([0-9a-f]+) ([0-9a-f]+) ([0-9a-f]+) ([0-9a-f]+)', line)
|
||||||
|
if m:
|
||||||
|
new_pos = (int(m.group(2)), int(m.group(3)))
|
||||||
|
if new_pos != pos:
|
||||||
|
pos = new_pos
|
||||||
|
print(f"{lid} POS: {pos}")
|
||||||
|
|
||||||
|
|
||||||
|
def main(args):
|
||||||
|
traceparser = Traceparser(args.file)
|
||||||
|
traceparser.run()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--debug", "-d", action="store_true")
|
||||||
|
parser.add_argument("file", type=argparse.FileType('r'))
|
||||||
|
args = parser.parse_args()
|
||||||
|
log_format = "%(asctime)s [%(levelname)s] %(message)s"
|
||||||
|
log_date_format = "%Y-%m-%dT%H%M%S%z"
|
||||||
|
log_level = logging.DEBUG if args.debug else logging.INFO
|
||||||
|
logging.basicConfig(format=log_format, level=log_level, datefmt=log_date_format)
|
||||||
|
try:
|
||||||
|
main(args)
|
||||||
|
except TraceparserError as e:
|
||||||
|
logging.error(e)
|
||||||
|
sys.exit(1)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
logging.error("Interrupted")
|
||||||
|
sys.exit(2)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(e, exc_info=True)
|
||||||
|
sys.exit(3)
|
||||||
239
asm/asm.py
Executable file
239
asm/asm.py
Executable file
@@ -0,0 +1,239 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
from typing import List
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from struct import pack
|
||||||
|
|
||||||
|
class AssemblerError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Instruction:
|
||||||
|
name: str
|
||||||
|
number: int
|
||||||
|
load_jr: bool = False
|
||||||
|
|
||||||
|
class Assembler:
|
||||||
|
def __init__(self):
|
||||||
|
self.prog = bytearray()
|
||||||
|
self.lineno = 0
|
||||||
|
self.labels = {}
|
||||||
|
self.label_references = []
|
||||||
|
self.logger = logging.getLogger("asm")
|
||||||
|
instructions = [
|
||||||
|
Instruction("NOOP", 0), # Come on, we just need one
|
||||||
|
Instruction("ID", 1),
|
||||||
|
Instruction("TAG", 2),
|
||||||
|
|
||||||
|
Instruction("MOVE", 3),
|
||||||
|
Instruction("SENSE", 4),
|
||||||
|
Instruction("PROBE", 5),
|
||||||
|
|
||||||
|
Instruction("PICKUP", 6),
|
||||||
|
Instruction("CARRYING", 7),
|
||||||
|
Instruction("DROP", 8),
|
||||||
|
|
||||||
|
Instruction("SMELL", 9),
|
||||||
|
Instruction("SNIFF", 0xa),
|
||||||
|
Instruction("MARK", 0xb),
|
||||||
|
|
||||||
|
Instruction("SET", 0x10),
|
||||||
|
Instruction("AND", 0x11),
|
||||||
|
Instruction("OR", 0x12),
|
||||||
|
Instruction("XOR", 0x13),
|
||||||
|
Instruction("LSHIFT", 0x14),
|
||||||
|
Instruction("RSHIFT", 0x15),
|
||||||
|
Instruction("ADD", 0x16),
|
||||||
|
Instruction("SUB", 0x17),
|
||||||
|
Instruction("MUL", 0x18),
|
||||||
|
Instruction("DIV", 0x19),
|
||||||
|
Instruction("MOD", 0x1a),
|
||||||
|
Instruction("RANDOM", 0x1b),
|
||||||
|
|
||||||
|
Instruction("SJR", 0x20), # New 'hidden' instruction to support jmp instructions
|
||||||
|
Instruction("MJR", 0x21), # New 'hidden' instruction to support jmp instruction from reg
|
||||||
|
Instruction("JMP", 0x22, True),
|
||||||
|
Instruction("CALL", 0x23, True),
|
||||||
|
Instruction("JEQ", 0x24, True),
|
||||||
|
Instruction("JNE", 0x25, True),
|
||||||
|
Instruction("JGT", 0x26, True),
|
||||||
|
Instruction("JLT", 0x27, True)
|
||||||
|
|
||||||
|
|
||||||
|
]
|
||||||
|
self.instructions = {i.name:i for i in instructions}
|
||||||
|
self.constants = {
|
||||||
|
"r0": 0x80,
|
||||||
|
"r1": 0x81,
|
||||||
|
"r2": 0x82,
|
||||||
|
"r3": 0x83,
|
||||||
|
"r4": 0x84,
|
||||||
|
"r5": 0x85,
|
||||||
|
"r6": 0x86,
|
||||||
|
"r7": 0x87,
|
||||||
|
"jr": 0x88,
|
||||||
|
"empty": 0,
|
||||||
|
"wall": 1,
|
||||||
|
"food": 2,
|
||||||
|
"nest": 3,
|
||||||
|
"ant": 4,
|
||||||
|
"here": 0,
|
||||||
|
"n": 1,
|
||||||
|
"e": 2,
|
||||||
|
"s": 3,
|
||||||
|
"w": 4,
|
||||||
|
"north": 1,
|
||||||
|
"east": 2,
|
||||||
|
"south": 3,
|
||||||
|
"west": 4,
|
||||||
|
"random": 5,
|
||||||
|
"ch_red": 0,
|
||||||
|
"ch_blue": 1,
|
||||||
|
"ch_green": 2,
|
||||||
|
"ch_yellow": 3,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
def write(self, bt):
|
||||||
|
if type(bt) == int:
|
||||||
|
self.prog += bytes([bt])
|
||||||
|
else:
|
||||||
|
self.prog += bt
|
||||||
|
#print(self.prog.hex())
|
||||||
|
|
||||||
|
def handle_label(self, iname):
|
||||||
|
lblname = iname[:-1].lower()
|
||||||
|
if lblname in self.labels:
|
||||||
|
raise AssemblerError(f"Label {lblname} defined twice")
|
||||||
|
self.labels[lblname] = len(self.prog) // 4
|
||||||
|
|
||||||
|
def handle_directive(self, iname, args):
|
||||||
|
if iname in ['.alias', '.const', '.tag']:
|
||||||
|
source = args[0] if iname == '.tag' else args[1]
|
||||||
|
dest = args[1].lower() if iname == '.tag' else args[0].lower()
|
||||||
|
|
||||||
|
self.logger.debug(f"{iname} {dest} = {self.resolve_arg(source)}")
|
||||||
|
if dest in self.constants:
|
||||||
|
raise AssemblerError(f"Overwriting const {dest}")
|
||||||
|
self.constants[dest] = self.resolve_arg(source)
|
||||||
|
else:
|
||||||
|
raise AssemblerError(f"Invalid directive {iname}")
|
||||||
|
|
||||||
|
def resolve_arg(self, arg, raise_if_fail=True):
|
||||||
|
arg = arg.lower()
|
||||||
|
if arg in self.constants:
|
||||||
|
result = self.constants[arg]
|
||||||
|
elif arg.isnumeric():
|
||||||
|
result = int(arg)
|
||||||
|
elif arg.startswith("0x"):
|
||||||
|
result = int(arg, 16)
|
||||||
|
elif raise_if_fail:
|
||||||
|
raise AssemblerError(f"Cant parse argument {arg}")
|
||||||
|
else:
|
||||||
|
result = 0
|
||||||
|
if result > 255:
|
||||||
|
raise AssemblerError(f"Arg {arg} value bigger than 255")
|
||||||
|
return result
|
||||||
|
|
||||||
|
def handle_load_jr(self, instruction, target):
|
||||||
|
target_reg = self.resolve_arg(target, raise_if_fail=False)
|
||||||
|
if instruction.name == "JMP" and target_reg in range(0x80, 0x88):
|
||||||
|
mjr = self.instructions["MJR"]
|
||||||
|
data = [mjr.number, target_reg, 0, 0]
|
||||||
|
else:
|
||||||
|
sjr = self.instructions["SJR"]
|
||||||
|
data = [sjr.number, 0, 0, 0]
|
||||||
|
offset = len(self.prog) + 1
|
||||||
|
self.label_references.append((target, offset))
|
||||||
|
self.write(bytes(data))
|
||||||
|
|
||||||
|
def handle_instruction(self, iname, args):
|
||||||
|
instruction = self.instructions[iname.upper()]
|
||||||
|
if len(args) > 3:
|
||||||
|
raise AssemblerError("Too many arguments")
|
||||||
|
data = [instruction.number]
|
||||||
|
if instruction.load_jr:
|
||||||
|
target = args[-1]
|
||||||
|
self.handle_load_jr(instruction, target)
|
||||||
|
args = args[:-1]
|
||||||
|
for i in range(3):
|
||||||
|
if i >= len(args):
|
||||||
|
data.append(0)
|
||||||
|
else:
|
||||||
|
data.append(self.resolve_arg(args[i]))
|
||||||
|
|
||||||
|
self.write(bytes(data))
|
||||||
|
|
||||||
|
def assemble_line(self, line):
|
||||||
|
pre_offset = len(self.prog)
|
||||||
|
line = line.strip()
|
||||||
|
# remove comments
|
||||||
|
parts = line.split(";", 1)
|
||||||
|
if len(parts) == 2:
|
||||||
|
line = parts[0]
|
||||||
|
|
||||||
|
# split into parts
|
||||||
|
parts = line.split()
|
||||||
|
if len(parts) == 0:
|
||||||
|
return
|
||||||
|
iname = parts[0]
|
||||||
|
|
||||||
|
if iname.endswith(":"):
|
||||||
|
self.handle_label(iname)
|
||||||
|
elif iname.startswith("."):
|
||||||
|
self.handle_directive(iname, parts[1:])
|
||||||
|
elif iname.upper() in self.instructions:
|
||||||
|
self.handle_instruction(iname, parts[1:])
|
||||||
|
else:
|
||||||
|
raise AssemblerError(f"Instruction {iname} not found")
|
||||||
|
new_data = self.prog[pre_offset:]
|
||||||
|
self.logger.debug(f"{line.strip()} : {new_data.hex()}")
|
||||||
|
|
||||||
|
def resolve_references(self):
|
||||||
|
for reflabel, refoffset in self.label_references:
|
||||||
|
if not reflabel in self.labels:
|
||||||
|
raise AssemblerError(f"Could not resolve label {reflabel}")
|
||||||
|
lblloc = self.labels[reflabel]
|
||||||
|
self.prog[refoffset] = lblloc & 0xff
|
||||||
|
self.prog[refoffset + 1] = lblloc >> 8 & 0xff
|
||||||
|
self.prog[refoffset + 2] = lblloc >> 16 & 0xff
|
||||||
|
|
||||||
|
def assemble_text(self, data):
|
||||||
|
for line in data.split("\n"):
|
||||||
|
self.lineno += 1
|
||||||
|
self.assemble_line(line)
|
||||||
|
self.resolve_references()
|
||||||
|
return self.prog
|
||||||
|
|
||||||
|
def assemble_file(self, inf, outf):
|
||||||
|
with open(inf) as asm:
|
||||||
|
assy = asm.read()
|
||||||
|
|
||||||
|
assy = "JMP main\n" + assy
|
||||||
|
prog = self.assemble_text(assy)
|
||||||
|
|
||||||
|
output = sys.stdout.buffer if outf is None else open(outf, 'wb')
|
||||||
|
output.write(pack("<I", len(prog) >> 2)) # This is the header of the file: num_instructions
|
||||||
|
output.write(prog)
|
||||||
|
if output != sys.stdout:
|
||||||
|
output.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
assembler = Assembler()
|
||||||
|
parser = argparse.ArgumentParser(description='Assemble a program.')
|
||||||
|
parser.add_argument('file', help='File to assemble')
|
||||||
|
parser.add_argument('--output', metavar='FILE',
|
||||||
|
help='Output file (stdout if ommitted)')
|
||||||
|
parser.add_argument('--debug', '-d', action='store_true')
|
||||||
|
args = parser.parse_args()
|
||||||
|
logging.basicConfig(level=logging.DEBUG if args.debug else logging.INFO)
|
||||||
|
try:
|
||||||
|
assembler.assemble_file(args.file, args.output)
|
||||||
|
except AssemblerError as e:
|
||||||
|
sys.stderr.write(f"Error at line: {assembler.lineno}: {e}\n")
|
||||||
|
sys.exit(1)
|
||||||
11
asm/test.ant
Normal file
11
asm/test.ant
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
SET r5 0xaa
|
||||||
|
main:
|
||||||
|
ID r0
|
||||||
|
SET r5 0x55
|
||||||
|
ADD r5 0x5
|
||||||
|
RSHIFT r5 4
|
||||||
|
LSHIFT r5 4
|
||||||
|
RANDOM r1 0x1f
|
||||||
|
RANDOM r2 10
|
||||||
|
SNIFF CH_RED
|
||||||
|
MOVE RANDOM
|
||||||
135
asm/twotrail.ant
Normal file
135
asm/twotrail.ant
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
; ── SWARM FORAGER ────────────────────────────
|
||||||
|
; Random walk to find food, dead reckoning to
|
||||||
|
; navigate home. Pheromone marks food for others.
|
||||||
|
; Hit play, then try improving it!
|
||||||
|
|
||||||
|
; ── Registers ──
|
||||||
|
.alias dx r1 ; x displacement from nest
|
||||||
|
.alias dy r2 ; y displacement from nest
|
||||||
|
.alias ret r3 ; return address for CALL
|
||||||
|
|
||||||
|
; ── Pheromone ──
|
||||||
|
.const PH_FOOD CH_RED ; marks food locations
|
||||||
|
.const PH_NEST CH_BLUE
|
||||||
|
.const PHER_INIT 255
|
||||||
|
.const PHER_DECAY 2
|
||||||
|
; ── Roles (visible in simulation viewer) ──
|
||||||
|
.tag 0 foraging
|
||||||
|
.tag 1 homing
|
||||||
|
|
||||||
|
; ──────────────────────────────────────────────
|
||||||
|
main:
|
||||||
|
set r7 PHER_INIT
|
||||||
|
loop:
|
||||||
|
CARRYING
|
||||||
|
JNE r0 0 go_home
|
||||||
|
TAG foraging
|
||||||
|
|
||||||
|
; ── SEARCH: find food ─────────────────────────
|
||||||
|
search:
|
||||||
|
SENSE FOOD
|
||||||
|
JNE r0 0 grab
|
||||||
|
|
||||||
|
; if we are too far from the food, dont mark
|
||||||
|
JLT r7 PHER_DECAY smell_trail
|
||||||
|
SUB r7 PHER_DECAY
|
||||||
|
; if there is already a trail here, dont mark
|
||||||
|
SNIFF PH_NEST HERE r0
|
||||||
|
JNE r0 0 smell_trail
|
||||||
|
MARK PH_NEST r7
|
||||||
|
|
||||||
|
smell_trail:
|
||||||
|
SMELL PH_FOOD
|
||||||
|
JNE r0 0 step
|
||||||
|
SENSE EMPTY
|
||||||
|
step:
|
||||||
|
CALL ret move_track
|
||||||
|
JMP loop
|
||||||
|
|
||||||
|
grab:
|
||||||
|
CALL ret move_track
|
||||||
|
PICKUP
|
||||||
|
MARK PH_FOOD 255
|
||||||
|
SET r6 PHER_INIT
|
||||||
|
JMP loop
|
||||||
|
|
||||||
|
; ── GO HOME: dead reckon toward nest ──────────
|
||||||
|
go_home:
|
||||||
|
TAG homing
|
||||||
|
; if we are too far from the food, dont mark
|
||||||
|
JLT r6 PHER_DECAY sense_nest
|
||||||
|
SUB r6 PHER_DECAY
|
||||||
|
; if there is already a trail here, dont mark
|
||||||
|
SNIFF PH_FOOD HERE r0
|
||||||
|
JNE r0 0 sense_nest
|
||||||
|
MARK PH_FOOD r6
|
||||||
|
sense_nest:
|
||||||
|
SENSE NEST
|
||||||
|
JNE r0 0 deliver
|
||||||
|
smell_nest:
|
||||||
|
SMELL PH_NEST
|
||||||
|
JNE r0 0 move_track
|
||||||
|
RANDOM r0 4
|
||||||
|
JEQ r0 0 wander ; 25% random to unstick walls
|
||||||
|
CALL ret home_dir
|
||||||
|
CALL ret move_track
|
||||||
|
JMP main
|
||||||
|
wander:
|
||||||
|
SENSE EMPTY
|
||||||
|
CALL ret move_track
|
||||||
|
JMP main
|
||||||
|
|
||||||
|
deliver:
|
||||||
|
CALL ret move_track
|
||||||
|
DROP
|
||||||
|
SET r7 PHER_INIT
|
||||||
|
JMP main
|
||||||
|
|
||||||
|
; ══════════════════════════════════════════════
|
||||||
|
; SUBROUTINES — CALL ret <label>, return JMP ret
|
||||||
|
; ══════════════════════════════════════════════
|
||||||
|
|
||||||
|
; home_dir: set r0 = direction toward nest
|
||||||
|
home_dir:
|
||||||
|
SET r4 dx
|
||||||
|
JGT dx 0 hd_dxp
|
||||||
|
SET r4 0
|
||||||
|
SUB r4 dx
|
||||||
|
hd_dxp:
|
||||||
|
SET r5 dy
|
||||||
|
JGT dy 0 hd_dyp
|
||||||
|
SET r5 0
|
||||||
|
SUB r5 dy
|
||||||
|
hd_dyp:
|
||||||
|
JGT r5 r4 hd_y
|
||||||
|
JGT dx 0 hd_w
|
||||||
|
SET r0 2
|
||||||
|
JMP ret
|
||||||
|
hd_w:
|
||||||
|
SET r0 4
|
||||||
|
JMP ret
|
||||||
|
hd_y:
|
||||||
|
JGT dy 0 hd_n
|
||||||
|
SET r0 3
|
||||||
|
JMP ret
|
||||||
|
hd_n:
|
||||||
|
SET r0 1
|
||||||
|
JMP ret
|
||||||
|
|
||||||
|
; move_track: MOVE r0, then update dx/dy
|
||||||
|
move_track:
|
||||||
|
MOVE r0
|
||||||
|
JEQ r0 1 mt_n
|
||||||
|
JEQ r0 2 mt_e
|
||||||
|
JEQ r0 3 mt_s
|
||||||
|
SUB dx 1
|
||||||
|
JMP ret
|
||||||
|
mt_n:
|
||||||
|
SUB dy 1
|
||||||
|
JMP ret
|
||||||
|
mt_e:
|
||||||
|
ADD dx 1
|
||||||
|
JMP ret
|
||||||
|
mt_s:
|
||||||
|
ADD dy 1
|
||||||
|
JMP ret
|
||||||
7
dev.sh
Executable file
7
dev.sh
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "Building image..."
|
||||||
|
pushd farm
|
||||||
|
docker build -qt ants-dev .
|
||||||
|
popd
|
||||||
|
echo "Dropping into shell..."
|
||||||
|
docker run --rm -ti -v $PWD/data:/data -v $PWD/farm:/home/dev/farm ants-dev
|
||||||
6
farm/Dockerfile
Normal file
6
farm/Dockerfile
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
FROM debian
|
||||||
|
RUN apt update && apt install -y curl vim wget xz-utils build-essential less
|
||||||
|
RUN useradd -m dev
|
||||||
|
USER dev
|
||||||
|
WORKDIR /home/dev/farm
|
||||||
|
ADD . /home/dev/farm
|
||||||
194
farm/Makefile
Normal file
194
farm/Makefile
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
#### PROJECT SETTINGS ####
|
||||||
|
# The name of the executable to be created
|
||||||
|
BIN_NAME := farm
|
||||||
|
# Compiler used
|
||||||
|
CC ?= gcc
|
||||||
|
# Extension of source files used in the project
|
||||||
|
SRC_EXT = c
|
||||||
|
# Path to the source directory, relative to the makefile
|
||||||
|
SRC_PATH = .
|
||||||
|
# Space-separated pkg-config libraries used by this project
|
||||||
|
LIBS =
|
||||||
|
# General compiler flags
|
||||||
|
COMPILE_FLAGS = -std=c99 -Wall -Wextra -g
|
||||||
|
# Additional release-specific flags
|
||||||
|
RCOMPILE_FLAGS = -D NDEBUG
|
||||||
|
# Additional debug-specific flags
|
||||||
|
DCOMPILE_FLAGS = -D DEBUG
|
||||||
|
# Add additional include paths
|
||||||
|
INCLUDES = -I $(SRC_PATH)
|
||||||
|
# General linker settings
|
||||||
|
LINK_FLAGS =
|
||||||
|
# Additional release-specific linker settings
|
||||||
|
RLINK_FLAGS =
|
||||||
|
# Additional debug-specific linker settings
|
||||||
|
DLINK_FLAGS =
|
||||||
|
# Destination directory, like a jail or mounted system
|
||||||
|
DESTDIR = /
|
||||||
|
# Install path (bin/ is appended automatically)
|
||||||
|
INSTALL_PREFIX = usr/local
|
||||||
|
#### END PROJECT SETTINGS ####
|
||||||
|
|
||||||
|
# Optionally you may move the section above to a separate config.mk file, and
|
||||||
|
# uncomment the line below
|
||||||
|
# include config.mk
|
||||||
|
|
||||||
|
# Generally should not need to edit below this line
|
||||||
|
|
||||||
|
# Obtains the OS type, either 'Darwin' (OS X) or 'Linux'
|
||||||
|
UNAME_S:=$(shell uname -s)
|
||||||
|
|
||||||
|
# Function used to check variables. Use on the command line:
|
||||||
|
# make print-VARNAME
|
||||||
|
# Useful for debugging and adding features
|
||||||
|
print-%: ; @echo $*=$($*)
|
||||||
|
|
||||||
|
# Shell used in this makefile
|
||||||
|
# bash is used for 'echo -en'
|
||||||
|
SHELL = /bin/bash
|
||||||
|
# Clear built-in rules
|
||||||
|
.SUFFIXES:
|
||||||
|
# Programs for installation
|
||||||
|
INSTALL = install
|
||||||
|
INSTALL_PROGRAM = $(INSTALL)
|
||||||
|
INSTALL_DATA = $(INSTALL) -m 644
|
||||||
|
|
||||||
|
# Append pkg-config specific libraries if need be
|
||||||
|
ifneq ($(LIBS),)
|
||||||
|
COMPILE_FLAGS += $(shell pkg-config --cflags $(LIBS))
|
||||||
|
LINK_FLAGS += $(shell pkg-config --libs $(LIBS))
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Verbose option, to output compile and link commands
|
||||||
|
export V := false
|
||||||
|
export CMD_PREFIX := @
|
||||||
|
ifeq ($(V),true)
|
||||||
|
CMD_PREFIX :=
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Combine compiler and linker flags
|
||||||
|
release: export CFLAGS := $(CFLAGS) $(COMPILE_FLAGS) $(RCOMPILE_FLAGS)
|
||||||
|
release: export LDFLAGS := $(LDFLAGS) $(LINK_FLAGS) $(RLINK_FLAGS)
|
||||||
|
debug: export CFLAGS := $(CFLAGS) $(COMPILE_FLAGS) $(DCOMPILE_FLAGS)
|
||||||
|
debug: export LDFLAGS := $(LDFLAGS) $(LINK_FLAGS) $(DLINK_FLAGS)
|
||||||
|
|
||||||
|
# Build and output paths
|
||||||
|
release: export BUILD_PATH := build/release
|
||||||
|
release: export BIN_PATH := bin/release
|
||||||
|
debug: export BUILD_PATH := build/debug
|
||||||
|
debug: export BIN_PATH := bin/debug
|
||||||
|
install: export BIN_PATH := bin/release
|
||||||
|
|
||||||
|
# Find all source files in the source directory, sorted by most
|
||||||
|
# recently modified
|
||||||
|
ifeq ($(UNAME_S),Darwin)
|
||||||
|
SOURCES = $(shell find $(SRC_PATH) -name '*.$(SRC_EXT)' | sort -k 1nr | cut -f2-)
|
||||||
|
else
|
||||||
|
SOURCES = $(shell find $(SRC_PATH) -name '*.$(SRC_EXT)' -printf '%T@\t%p\n' \
|
||||||
|
| sort -k 1nr | cut -f2-)
|
||||||
|
endif
|
||||||
|
|
||||||
|
# fallback in case the above fails
|
||||||
|
rwildcard = $(foreach d, $(wildcard $1*), $(call rwildcard,$d/,$2) \
|
||||||
|
$(filter $(subst *,%,$2), $d))
|
||||||
|
ifeq ($(SOURCES),)
|
||||||
|
SOURCES := $(call rwildcard, $(SRC_PATH), *.$(SRC_EXT))
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Set the object file names, with the source directory stripped
|
||||||
|
# from the path, and the build path prepended in its place
|
||||||
|
OBJECTS = $(SOURCES:$(SRC_PATH)/%.$(SRC_EXT)=$(BUILD_PATH)/%.o)
|
||||||
|
# Set the dependency files that will be used to add header dependencies
|
||||||
|
DEPS = $(OBJECTS:.o=.d)
|
||||||
|
|
||||||
|
# Version macros
|
||||||
|
# Comment/remove this section to remove versioning
|
||||||
|
USE_VERSION := false
|
||||||
|
# If this isn't a git repo or the repo has no tags, git describe will return non-zero
|
||||||
|
ifeq ($(shell git describe > /dev/null 2>&1 ; echo $$?), 0)
|
||||||
|
USE_VERSION := true
|
||||||
|
VERSION := $(shell git describe --tags --long --dirty --always | \
|
||||||
|
sed 's/v\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)-\?.*-\([0-9]*\)-\(.*\)/\1 \2 \3 \4 \5/g')
|
||||||
|
VERSION_MAJOR := $(word 1, $(VERSION))
|
||||||
|
VERSION_MINOR := $(word 2, $(VERSION))
|
||||||
|
VERSION_PATCH := $(word 3, $(VERSION))
|
||||||
|
VERSION_REVISION := $(word 4, $(VERSION))
|
||||||
|
VERSION_HASH := $(word 5, $(VERSION))
|
||||||
|
VERSION_STRING := \
|
||||||
|
"$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH).$(VERSION_REVISION)-$(VERSION_HASH)"
|
||||||
|
override CFLAGS := $(CFLAGS) \
|
||||||
|
-D VERSION_MAJOR=$(VERSION_MAJOR) \
|
||||||
|
-D VERSION_MINOR=$(VERSION_MINOR) \
|
||||||
|
-D VERSION_PATCH=$(VERSION_PATCH) \
|
||||||
|
-D VERSION_REVISION=$(VERSION_REVISION) \
|
||||||
|
-D VERSION_HASH=\"$(VERSION_HASH)\"
|
||||||
|
endif
|
||||||
|
|
||||||
|
# Standard, non-optimized release build
|
||||||
|
.PHONY: release
|
||||||
|
release: dirs
|
||||||
|
ifeq ($(USE_VERSION), true)
|
||||||
|
@echo "Beginning release build v$(VERSION_STRING)"
|
||||||
|
else
|
||||||
|
@echo "Beginning release build"
|
||||||
|
endif
|
||||||
|
@$(MAKE) all --no-print-directory
|
||||||
|
|
||||||
|
# Debug build for gdb debugging
|
||||||
|
.PHONY: debug
|
||||||
|
debug: dirs
|
||||||
|
ifeq ($(USE_VERSION), true)
|
||||||
|
@echo "Beginning debug build v$(VERSION_STRING)"
|
||||||
|
else
|
||||||
|
@echo "Beginning debug build"
|
||||||
|
endif
|
||||||
|
@$(MAKE) all --no-print-directory
|
||||||
|
|
||||||
|
# Create the directories used in the build
|
||||||
|
.PHONY: dirs
|
||||||
|
dirs:
|
||||||
|
@echo "Creating directories"
|
||||||
|
@mkdir -p $(dir $(OBJECTS))
|
||||||
|
@mkdir -p $(BIN_PATH)
|
||||||
|
|
||||||
|
# Installs to the set path
|
||||||
|
.PHONY: install
|
||||||
|
install:
|
||||||
|
@echo "Installing to $(DESTDIR)$(INSTALL_PREFIX)/bin"
|
||||||
|
@$(INSTALL_PROGRAM) $(BIN_PATH)/$(BIN_NAME) $(DESTDIR)$(INSTALL_PREFIX)/bin
|
||||||
|
|
||||||
|
# Uninstalls the program
|
||||||
|
.PHONY: uninstall
|
||||||
|
uninstall:
|
||||||
|
@echo "Removing $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BIN_NAME)"
|
||||||
|
@$(RM) $(DESTDIR)$(INSTALL_PREFIX)/bin/$(BIN_NAME)
|
||||||
|
|
||||||
|
# Removes all build files
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
@echo "Deleting $(BIN_NAME) symlink"
|
||||||
|
@$(RM) $(BIN_NAME)
|
||||||
|
@echo "Deleting directories"
|
||||||
|
@$(RM) -r build
|
||||||
|
@$(RM) -r bin
|
||||||
|
|
||||||
|
# Main rule, checks the executable and symlinks to the output
|
||||||
|
all: $(BIN_PATH)/$(BIN_NAME)
|
||||||
|
@echo "Making symlink: $(BIN_NAME) -> $<"
|
||||||
|
@$(RM) $(BIN_NAME)
|
||||||
|
@ln -s $(BIN_PATH)/$(BIN_NAME) $(BIN_NAME)
|
||||||
|
|
||||||
|
# Link the executable
|
||||||
|
$(BIN_PATH)/$(BIN_NAME): $(OBJECTS)
|
||||||
|
@echo "Linking: $@"
|
||||||
|
$(CMD_PREFIX)$(CC) $(OBJECTS) $(LDFLAGS) -o $@
|
||||||
|
|
||||||
|
# Add dependency files, if they exist
|
||||||
|
-include $(DEPS)
|
||||||
|
|
||||||
|
# Source file rules
|
||||||
|
# After the first compilation they will be joined with the rules from the
|
||||||
|
# dependency files to provide header dependencies
|
||||||
|
$(BUILD_PATH)/%.o: $(SRC_PATH)/%.$(SRC_EXT)
|
||||||
|
@echo "Compiling: $< -> $@"
|
||||||
|
$(CMD_PREFIX)$(CC) $(CFLAGS) $(INCLUDES) -MP -MMD -c $< -o $@
|
||||||
19
farm/ant.c
Normal file
19
farm/ant.c
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#include "ant.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void reset_ants(struct Ant* ants, unsigned short nest) {
|
||||||
|
memset(ants, 0, sizeof(struct Ant) * NUM_ANTS);
|
||||||
|
for (int i = 0; i < NUM_ANTS; i++) {
|
||||||
|
ants[i].id = i;
|
||||||
|
ants[i].cell = nest;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Ant* create_ants() {
|
||||||
|
int ants_size = sizeof(struct Ant) * NUM_ANTS;
|
||||||
|
struct Ant *ants = (struct Ant *)malloc(ants_size);
|
||||||
|
return ants;
|
||||||
|
}
|
||||||
25
farm/ant.h
Normal file
25
farm/ant.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#ifdef DEBUG
|
||||||
|
#define NUM_ANTS 1
|
||||||
|
#else
|
||||||
|
#define NUM_ANTS 200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct Ant {
|
||||||
|
unsigned char id;
|
||||||
|
unsigned int pc;
|
||||||
|
unsigned short cell;
|
||||||
|
unsigned char carrying;
|
||||||
|
int r0;
|
||||||
|
int r1;
|
||||||
|
int r2;
|
||||||
|
int r3;
|
||||||
|
int r4;
|
||||||
|
int r5;
|
||||||
|
int r6;
|
||||||
|
int r7;
|
||||||
|
int jr;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Ant* create_ants();
|
||||||
|
void reset_ants(struct Ant* ants, unsigned short nest);
|
||||||
392
farm/farm.c
Normal file
392
farm/farm.c
Normal file
@@ -0,0 +1,392 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "maps.h"
|
||||||
|
#include "program.h"
|
||||||
|
#include "ant.h"
|
||||||
|
|
||||||
|
#define TICKS 2000
|
||||||
|
#define MAX_OPS 64
|
||||||
|
#define NUM_CHANNELS 4
|
||||||
|
|
||||||
|
const char map_offsets[] = { 0, -MAP_WIDTH , 1, MAP_WIDTH, -1 };
|
||||||
|
#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
|
||||||
|
#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
|
||||||
|
#define cell_from(org, dir) (org) + map_offsets[(dir)]
|
||||||
|
#define reg_id(rid) (rid) < 8 ? (rid) : (rid) - 0x80
|
||||||
|
|
||||||
|
#define get_value(ant, v) (v) < 0x80 ? (v) : get_reg((ant), (v))
|
||||||
|
|
||||||
|
static inline unsigned int rnum(unsigned int* rstate) {
|
||||||
|
unsigned int e = *rstate + 0x9e3779b9;
|
||||||
|
*rstate = e;
|
||||||
|
e = (e ^ (e >> 16)) * 0x85ebca6b;
|
||||||
|
e = (e ^ (e >> 13)) * 0xc2b2ae35;
|
||||||
|
e = e ^ (e >> 16);
|
||||||
|
//e = e / 0x100000000;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static inline unsigned int rnum(unsigned int* rstate) {
|
||||||
|
// return rand();
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static inline void set_reg(struct Ant* ant, unsigned char rid, unsigned int v) {
|
||||||
|
switch(reg_id(rid)) {
|
||||||
|
case 0:
|
||||||
|
ant->r0 = v;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
ant->r1 = v;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
ant->r2 = v;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
ant->r3 = v;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
ant->r4 = v;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
ant->r5 = v;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
ant->r6 = v;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
ant->r7 = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static inline unsigned int get_reg(struct Ant* ant, unsigned char rid) {
|
||||||
|
switch(reg_id(rid)) {
|
||||||
|
case 0:
|
||||||
|
return ant->r0;
|
||||||
|
case 1:
|
||||||
|
return ant->r1;
|
||||||
|
case 2:
|
||||||
|
return ant->r2;
|
||||||
|
case 3:
|
||||||
|
return ant->r3;
|
||||||
|
case 4:
|
||||||
|
return ant->r4;
|
||||||
|
case 5:
|
||||||
|
return ant->r5;
|
||||||
|
case 6:
|
||||||
|
return ant->r6;
|
||||||
|
case 7:
|
||||||
|
return ant->r7;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
#define debug(msg, ...) printf((msg), ##__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define debug(msg, ...)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define debug_ant(ant) debug("A%d @%d:%d R:%02x %02x %02x %02x %02x %02x %02x %02x\n", (ant)->id, (ant)->cell % MAP_WIDTH, (ant)->cell / MAP_WIDTH, (ant)->r0, (ant)->r1, (ant)->r2, (ant)->r3, (ant)->r4, (ant)->r5, (ant)->r6, (ant)->r7)
|
||||||
|
#define debug_ins(pc, ins) debug("I%02d %s %02x %02x %02x\n", pc, instruction_names[ins->op], ins->arg1, ins->arg2, ins->arg3)
|
||||||
|
|
||||||
|
int run_simulations(struct Maps* maps, struct Program* program) {
|
||||||
|
int ops;
|
||||||
|
int cost;
|
||||||
|
int score;
|
||||||
|
int dirs[4];
|
||||||
|
unsigned int rstate;
|
||||||
|
int cell;
|
||||||
|
int found_dirs = 0;
|
||||||
|
struct Map* map;
|
||||||
|
int v1, v2, v3, res;
|
||||||
|
struct Ant* ant;
|
||||||
|
unsigned int pc;
|
||||||
|
struct Instruction* ins;
|
||||||
|
struct Ant* ants = create_ants();
|
||||||
|
unsigned char food[NUM_CELLS];
|
||||||
|
unsigned char smells[NUM_CHANNELS][NUM_CELLS];
|
||||||
|
|
||||||
|
|
||||||
|
for (unsigned int mi = 0; mi < maps->num_maps; mi++) {
|
||||||
|
map = &maps->maps + mi;
|
||||||
|
debug("MAP %s\n", map->name);
|
||||||
|
memset(&smells, 0, NUM_CHANNELS * NUM_CELLS);
|
||||||
|
reset_ants(ants, map->nest);
|
||||||
|
score = 0;
|
||||||
|
rstate = 2;
|
||||||
|
for (int i = 0; i < NUM_CELLS; i++ ) {
|
||||||
|
food[i] = map->cells[i] == CELL_FOOD ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int t=0; t < TICKS; t++) {
|
||||||
|
for (int ai=0; ai < NUM_ANTS; ai++) {
|
||||||
|
|
||||||
|
ops = 0;
|
||||||
|
while (ops < MAX_OPS) {
|
||||||
|
cost = 1;
|
||||||
|
ant = &ants[ai];
|
||||||
|
pc = ant->pc;
|
||||||
|
ins = &program->instructions + pc;
|
||||||
|
debug_ins(pc, ins);
|
||||||
|
ant->pc = (pc + 1) % program->num_instructions;
|
||||||
|
switch(ins->op) {
|
||||||
|
case INS_ID:
|
||||||
|
set_reg(ant, ins->arg1, ant->id);
|
||||||
|
break;
|
||||||
|
case INS_TAG:
|
||||||
|
//noop
|
||||||
|
break;
|
||||||
|
case INS_MOVE:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
if (v1 == 5) {
|
||||||
|
v1 = rnum(&rstate) % 4 + 1;
|
||||||
|
}
|
||||||
|
cell = cell_from(ant->cell, v1);
|
||||||
|
if (cell > 0 && cell < NUM_CELLS && map->cells[cell] != CELL_WALL) {
|
||||||
|
ant->cell = cell;
|
||||||
|
}
|
||||||
|
cost += MAX_OPS;
|
||||||
|
break;
|
||||||
|
case INS_SENSE:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = ins->arg2;
|
||||||
|
found_dirs = 0;
|
||||||
|
|
||||||
|
if (map->cells[cell_from(ant->cell, 1)] == v1) dirs[found_dirs++] = 1;
|
||||||
|
if (map->cells[cell_from(ant->cell, 2)] == v1) dirs[found_dirs++] = 2;
|
||||||
|
if (map->cells[cell_from(ant->cell, 3)] == v1) dirs[found_dirs++] = 3;
|
||||||
|
if (map->cells[cell_from(ant->cell, 4)] == v1) dirs[found_dirs++] = 4;
|
||||||
|
if (found_dirs == 0) {
|
||||||
|
res = 0;
|
||||||
|
} else if (found_dirs == 1) {
|
||||||
|
res = dirs[0];
|
||||||
|
} else {
|
||||||
|
res = dirs[rnum(&rstate) % found_dirs];
|
||||||
|
}
|
||||||
|
set_reg(ant, v2, res);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INS_PROBE:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v1 = cell_from(ant->cell, v1);
|
||||||
|
v1 = map->cells[v1];
|
||||||
|
set_reg(ant, ins->arg2, v1);
|
||||||
|
break;
|
||||||
|
case INS_PICKUP:
|
||||||
|
debug("PICKUP?\n");
|
||||||
|
if (!ant->carrying && food[ant->cell] > 0) {
|
||||||
|
ant->carrying = food[ant->cell]--;
|
||||||
|
debug("PICKUP!\n");
|
||||||
|
}
|
||||||
|
cost += MAX_OPS;
|
||||||
|
break;
|
||||||
|
case INS_CARRYING:
|
||||||
|
set_reg(ant, ins->arg1, ant->carrying);
|
||||||
|
break;
|
||||||
|
case INS_DROP:
|
||||||
|
debug("DROP?\n");
|
||||||
|
if (ant->carrying) {
|
||||||
|
ant -> carrying = 0;
|
||||||
|
if (map->nest == ant->cell){
|
||||||
|
debug("DROP SCORE\n");
|
||||||
|
score += 1;
|
||||||
|
} else {
|
||||||
|
food[ant->cell] += 1;
|
||||||
|
debug("DROP!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cost += MAX_OPS;
|
||||||
|
break;
|
||||||
|
case INS_SMELL:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = ins->arg2;
|
||||||
|
res = 0;
|
||||||
|
|
||||||
|
v3 = smells[v1][cell_from(ant->cell, 1)];
|
||||||
|
if (v3 > res) found_dirs = 0;
|
||||||
|
if (v3 >= res) dirs[found_dirs++] = 1;
|
||||||
|
|
||||||
|
v3 = smells[v1][cell_from(ant->cell, 2)];
|
||||||
|
if (v3 > res) found_dirs = 0;
|
||||||
|
if (v3 >= res) dirs[found_dirs++] = 2;
|
||||||
|
|
||||||
|
v3 = smells[v1][cell_from(ant->cell, 3)];
|
||||||
|
if (v3 > res) found_dirs = 0;
|
||||||
|
if (v3 >= res) dirs[found_dirs++] = 3;
|
||||||
|
|
||||||
|
v3 = smells[v1][cell_from(ant->cell, 4)];
|
||||||
|
if (v3 > res) found_dirs = 0;
|
||||||
|
if (v3 >= res) dirs[found_dirs++] = 4;
|
||||||
|
if (res == 0) {
|
||||||
|
res = (rnum(&rstate) % 4) + 1;
|
||||||
|
}else if (found_dirs == 1) {
|
||||||
|
res = dirs[0];
|
||||||
|
} else {
|
||||||
|
res = dirs[rnum(&rstate) % found_dirs];
|
||||||
|
}
|
||||||
|
set_reg(ant, v2, res);
|
||||||
|
break;
|
||||||
|
case INS_SNIFF:
|
||||||
|
v1 = ins->arg1;
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
v2 = cell_from(ant->cell, v2);
|
||||||
|
v1 = smells[v1][v2];
|
||||||
|
set_reg(ant, ins->arg3, v1);
|
||||||
|
break;
|
||||||
|
case INS_MARK:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
v3 = smells[v1][ant->cell];
|
||||||
|
smells[v1][ant->cell] = MAX(v3+v2, 255);
|
||||||
|
break;
|
||||||
|
case INS_SET:
|
||||||
|
set_reg(ant, ins->arg1, ins->arg2);
|
||||||
|
break;
|
||||||
|
case INS_AND:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
set_reg(ant, ins->arg1, v1 & v2);
|
||||||
|
break;
|
||||||
|
case INS_OR:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
set_reg(ant, ins->arg1, v1 | v2);
|
||||||
|
break;
|
||||||
|
case INS_XOR:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
set_reg(ant, ins->arg1, v1 | v2);
|
||||||
|
break;
|
||||||
|
case INS_LSHIFT:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
set_reg(ant, ins->arg1, v1 << v2);
|
||||||
|
break;
|
||||||
|
case INS_RSHIFT:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
set_reg(ant, ins->arg1, v1 >> v2);
|
||||||
|
break;
|
||||||
|
case INS_ADD:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
set_reg(ant, ins->arg1, v1 + v2);
|
||||||
|
break;
|
||||||
|
case INS_SUB:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
set_reg(ant, ins->arg1, v1 - v2);
|
||||||
|
break;
|
||||||
|
case INS_MUL:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
set_reg(ant, ins->arg1, v1 * v2);
|
||||||
|
break;
|
||||||
|
case INS_DIV:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
if (v2 != 0) {
|
||||||
|
set_reg(ant, ins->arg1, v1 / v2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case INS_MOD:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
set_reg(ant, ins->arg1, v1 % v2);
|
||||||
|
break;
|
||||||
|
case INS_RANDOM:
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
if (v2 != 0) {
|
||||||
|
v1 = rnum(&rstate) % v2;
|
||||||
|
set_reg(ant, ins->arg1, v1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case INS_SJR:
|
||||||
|
ant->jr = *((int*)ins) >> 8;
|
||||||
|
break;
|
||||||
|
case INS_MJR:
|
||||||
|
ant->jr = get_value(ant, ins->arg1);
|
||||||
|
break;
|
||||||
|
case INS_JMP:
|
||||||
|
ant->pc = ant->jr;
|
||||||
|
break;
|
||||||
|
case INS_CALL:
|
||||||
|
set_reg(ant, ins->arg1, ant->pc);
|
||||||
|
ant->pc = ant->jr;
|
||||||
|
break;
|
||||||
|
case INS_JEQ:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
if (v1 == v2) {
|
||||||
|
ant->pc = ant->jr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case INS_JNE:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
if (v1 != v2) {
|
||||||
|
ant->pc = ant->jr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case INS_JGT:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
if (v1 > v2) {
|
||||||
|
ant->pc = ant->jr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case INS_JLT:
|
||||||
|
v1 = get_value(ant, ins->arg1);
|
||||||
|
v2 = get_value(ant, ins->arg2);
|
||||||
|
if (v1 < v2) {
|
||||||
|
ant->pc = ant->jr;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
debug_ant(ant);
|
||||||
|
ops += cost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int pi = 0; pi < NUM_CHANNELS; pi++) {
|
||||||
|
for (int ci = 0; ci < NUM_CELLS; ci++) {
|
||||||
|
if (smells[pi][ci] > 0) smells[pi][ci]--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
struct Program* program = NULL;
|
||||||
|
struct Maps* maps = NULL;
|
||||||
|
int score = 0;
|
||||||
|
if (argc < 3) {
|
||||||
|
printf("Usage: farm <maps file> <program file>\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
char* maps_filename = argv[1];
|
||||||
|
char* program_filename = argv[2];
|
||||||
|
|
||||||
|
maps = load_maps(maps_filename);
|
||||||
|
program = load_program(program_filename);
|
||||||
|
int result = 0;
|
||||||
|
if (maps == NULL) {
|
||||||
|
printf("Could not load maps\n");
|
||||||
|
result = 2;
|
||||||
|
}else if (program == NULL) {
|
||||||
|
printf("Could not load program\n");
|
||||||
|
result = 3;
|
||||||
|
} else {
|
||||||
|
score = run_simulations(maps, program);
|
||||||
|
}
|
||||||
|
free(program);
|
||||||
|
free(maps);
|
||||||
|
printf("Score: %d\n", score);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
26
farm/maps.c
Normal file
26
farm/maps.c
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "maps.h"
|
||||||
|
struct Maps* load_maps(char* filename) {
|
||||||
|
FILE* f = fopen(filename, "r");
|
||||||
|
if (f == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
int num_maps;
|
||||||
|
int num_read = fread(&num_maps, 4, 1, f);
|
||||||
|
|
||||||
|
if (num_read != 1 || num_maps > 10000) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int mapfile_size = sizeof(struct Map) * num_maps;
|
||||||
|
struct Maps *maps = (struct Maps *)malloc(mapfile_size + 4);
|
||||||
|
maps->num_maps = num_maps;
|
||||||
|
num_read = fread(&maps->maps, 1, mapfile_size, f);
|
||||||
|
if (num_read != mapfile_size) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
return maps;
|
||||||
|
}
|
||||||
23
farm/maps.h
Normal file
23
farm/maps.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
#define MAP_WIDTH 128
|
||||||
|
#define MAP_HEIGHT 128
|
||||||
|
#define NUM_CELLS MAP_WIDTH * MAP_HEIGHT
|
||||||
|
#define MAP_NAME_LENGTH 32
|
||||||
|
#define CELL_EMPTY 0
|
||||||
|
#define CELL_WALL 1
|
||||||
|
#define CELL_FOOD 2
|
||||||
|
#define CELL_NEST 3
|
||||||
|
|
||||||
|
|
||||||
|
struct Map {
|
||||||
|
char name[MAP_NAME_LENGTH];
|
||||||
|
unsigned short nest;
|
||||||
|
unsigned char cells[MAP_WIDTH * MAP_HEIGHT];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Maps {
|
||||||
|
unsigned int num_maps;
|
||||||
|
struct Map maps;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Maps* load_maps(char* filename);
|
||||||
24
farm/program.c
Normal file
24
farm/program.c
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
#include "program.h"
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
struct Program* load_program(char* filename) {
|
||||||
|
FILE* f = fopen(filename, "r");
|
||||||
|
if (f == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
int num_instructions;
|
||||||
|
int num_read = fread(&num_instructions, 4, 1, f);
|
||||||
|
if (num_read != 1) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
int program_size = 4 + num_instructions * 4;
|
||||||
|
struct Program* program = (struct Program *)malloc(program_size);
|
||||||
|
program->num_instructions = num_instructions;
|
||||||
|
num_read = fread(&program->instructions, 1, program_size-4, f);
|
||||||
|
if (num_read != program_size-4) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
return program;
|
||||||
|
}
|
||||||
70
farm/program.h
Normal file
70
farm/program.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#define INS_NOOP 0
|
||||||
|
#define INS_ID 1
|
||||||
|
#define INS_TAG 2
|
||||||
|
#define INS_MOVE 3
|
||||||
|
|
||||||
|
#define INS_SENSE 4
|
||||||
|
#define INS_PROBE 5
|
||||||
|
|
||||||
|
#define INS_PICKUP 6
|
||||||
|
#define INS_CARRYING 7
|
||||||
|
#define INS_DROP 8
|
||||||
|
|
||||||
|
#define INS_SMELL 9
|
||||||
|
#define INS_SNIFF 0xa
|
||||||
|
#define INS_MARK 0xb
|
||||||
|
|
||||||
|
#define INS_SET 0x10
|
||||||
|
#define INS_AND 0x11
|
||||||
|
#define INS_OR 0x12
|
||||||
|
#define INS_XOR 0x13
|
||||||
|
#define INS_LSHIFT 0x14
|
||||||
|
#define INS_RSHIFT 0x15
|
||||||
|
#define INS_ADD 0x16
|
||||||
|
#define INS_SUB 0x17
|
||||||
|
#define INS_MUL 0x18
|
||||||
|
#define INS_DIV 0x19
|
||||||
|
#define INS_MOD 0x1a
|
||||||
|
#define INS_RANDOM 0x1b
|
||||||
|
|
||||||
|
#define INS_SJR 0x20
|
||||||
|
#define INS_MJR 0x21
|
||||||
|
#define INS_JMP 0x22
|
||||||
|
#define INS_CALL 0x23
|
||||||
|
#define INS_JEQ 0x24
|
||||||
|
#define INS_JNE 0x25
|
||||||
|
#define INS_JGT 0x26
|
||||||
|
#define INS_JLT 0x27
|
||||||
|
|
||||||
|
static char* instruction_names[256] = {
|
||||||
|
/* 0x00 */ "NOOP", "ID ", "TAG ", "MOVE", "SENS", "PROB", "PICK", "CARY", "DROP", "SMEL", "SNIF", "MARK", "?", "?", "?", "?",
|
||||||
|
/* 0x10 */ "SET ", "AND ", "OR ", "XOR ", "SHL ", "SHR ", "ADD ", "SUB ", "MUL ", "DIV ", "MOD", "RAND", "?", "?", "?", "?",
|
||||||
|
/* 0x20 */ "SJR ", "MJR ", "JMP ", "CALL", "JEQ", "JNE ", "JGT ", "JLT ", "?", "?", "?", "?", "?", "?", "?", "?",
|
||||||
|
/* 0x30 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
|
||||||
|
/* 0x40 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
|
||||||
|
/* 0x50 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
|
||||||
|
/* 0x60 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
|
||||||
|
/* 0x70 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
|
||||||
|
/* 0x80 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
|
||||||
|
/* 0x90 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
|
||||||
|
/* 0xa0 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
|
||||||
|
/* 0xb0 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
|
||||||
|
/* 0xc0 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
|
||||||
|
/* 0xd0 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
|
||||||
|
/* 0xe0 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
|
||||||
|
/* 0xf0 */ "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Instruction {
|
||||||
|
unsigned char op;
|
||||||
|
unsigned char arg1;
|
||||||
|
unsigned char arg2;
|
||||||
|
unsigned char arg3;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Program {
|
||||||
|
unsigned int num_instructions;
|
||||||
|
struct Instruction instructions;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Program* load_program(char* filename);
|
||||||
9
generate_maps.sh
Executable file
9
generate_maps.sh
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "Building image..."
|
||||||
|
pushd maps
|
||||||
|
docker build -qt ants-maps .
|
||||||
|
popd
|
||||||
|
echo "Generating maps..."
|
||||||
|
docker run --rm -ti -v $PWD/data:/maps ants-maps generate -n 120 -t open -o /maps/open.maps
|
||||||
|
docker run --rm -ti -v $PWD/data:/maps ants-maps generate -n 120 -t random -o /maps/random.maps
|
||||||
|
echo "DONE"
|
||||||
7
maps/Dockerfile
Normal file
7
maps/Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
FROM node
|
||||||
|
WORKDIR /app
|
||||||
|
ADD package.json* .
|
||||||
|
RUN npm install
|
||||||
|
ADD *.js /app
|
||||||
|
VOLUME /maps
|
||||||
|
ENTRYPOINT ["./maps.js"]
|
||||||
1065
maps/maps.js
Executable file
1065
maps/maps.js
Executable file
File diff suppressed because it is too large
Load Diff
20
maps/package-lock.json
generated
Normal file
20
maps/package-lock.json
generated
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "ants",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"dependencies": {
|
||||||
|
"commander": "^14.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/commander": {
|
||||||
|
"version": "14.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
|
||||||
|
"integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
maps/package.json
Normal file
5
maps/package.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"commander": "^14.0.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
63
readme.md
Normal file
63
readme.md
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# SWARM challenge
|
||||||
|
|
||||||
|
## The plan:
|
||||||
|
|
||||||
|
* Reproduce the maps on the website
|
||||||
|
* Create an assembler to generate bytecode. Add special instructions to define tweakable params and optional code blocks?
|
||||||
|
* Create an antfarm: a high-performance program that can score ant programs inside those maps
|
||||||
|
* Create a highly modular and customizable ant program
|
||||||
|
* Create a program that makes small random changes to ant programs
|
||||||
|
* BREED SUPERANTS
|
||||||
|
|
||||||
|
Contents of this repo:
|
||||||
|
|
||||||
|
* `/reference` contains reference materials describing and defining the challenge
|
||||||
|
* `/maps` contains scripts to generate and view maps (nodejs)
|
||||||
|
* `/asm` Assembles antssembly into my custom antbytecode
|
||||||
|
* `/farm` This runs the **ALIEN ANT FARM**
|
||||||
|
|
||||||
|
## Reference
|
||||||
|
The reference folder contains
|
||||||
|
* A sample ant program
|
||||||
|
* The reference found on the website
|
||||||
|
* A wget download of the relevant SPA
|
||||||
|
|
||||||
|
|
||||||
|
## Maps
|
||||||
|
As mentioned in the reference, there are 12 map generator functions.
|
||||||
|
|
||||||
|
* chambers
|
||||||
|
* bridge
|
||||||
|
* gauntlet
|
||||||
|
* islands
|
||||||
|
* open
|
||||||
|
* brush
|
||||||
|
* prairie
|
||||||
|
* field
|
||||||
|
* pockets
|
||||||
|
* fortress
|
||||||
|
* maze
|
||||||
|
* spiral
|
||||||
|
|
||||||
|
When looking in the browser, the generated maps have names like 'chambers-3lc8x4' or 'bridge-1u7xlw' where the string after the dash is the seed used, encoded in base36.
|
||||||
|
|
||||||
|
* `nodejs maps.js generate -n 120 -o mymaps.maps` generates 120 maps and stores them in mymaps.maps
|
||||||
|
* `nodejs maps.js view bridge-1u7xlw` renders the indicated map to ascii
|
||||||
|
|
||||||
|
## Asm
|
||||||
|
The assembler is not copied from the challenge, but a new implementation.
|
||||||
|
|
||||||
|
It has a couple of deviations from the spec:
|
||||||
|
* Literals can only be 0-255, non negative. This is done to simplify the bytecode somewhat. I do not believe i will need literals outside this range (famous last words). This is only the literals in the assembly. The actual registers are full 32bit.
|
||||||
|
* Constants, tags and aliases all end up in the same pool, and are case insensitive
|
||||||
|
* JMP main is always prepended to every program
|
||||||
|
|
||||||
|
|
||||||
|
The bytecode is quite simple. Every instruction is 4 bytes. The first byte is the instruction number, the other 3 are single-byte arguments.
|
||||||
|
|
||||||
|
Jump instructions are a special case. They need to specify a target instruction offset, and the conditional jumps also take two normal arguments. This does not fit into 4 bytes. To accommodate this, our cpu introduces a 9th register called JR and 2 'hidden instructions' called SJR and MJR. Any jump instruction is assembled into 2 bytecode instructions: first SJR or MJR and then the original instruction. SJR (Set JR) uses all 3 argument bytes as a single unsigned little endian int to describe the offset. MJR (Move JR) copies the value from a register into JR. The jump instructions simply use JR as the destination address
|
||||||
|
|
||||||
|
## FARM
|
||||||
|
Some would say this is where the magic happens.
|
||||||
|
|
||||||
|
This is a program that reads the maps file (generated by the maps script), and an apm file (ant program, generated by the assembler) and starts breeding ants!
|
||||||
110
reference/example.ant
Normal file
110
reference/example.ant
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
; ── SWARM FORAGER ────────────────────────────
|
||||||
|
; Random walk to find food, dead reckoning to
|
||||||
|
; navigate home. Pheromone marks food for others.
|
||||||
|
; Hit play, then try improving it!
|
||||||
|
|
||||||
|
; ── Registers ──
|
||||||
|
.alias dx r1 ; x displacement from nest
|
||||||
|
.alias dy r2 ; y displacement from nest
|
||||||
|
.alias ret r3 ; return address for CALL
|
||||||
|
|
||||||
|
; ── Pheromone ──
|
||||||
|
.const TRAIL CH_RED ; marks food locations
|
||||||
|
|
||||||
|
; ── Roles (visible in simulation viewer) ──
|
||||||
|
.tag 0 foraging
|
||||||
|
.tag 1 homing
|
||||||
|
|
||||||
|
; ──────────────────────────────────────────────
|
||||||
|
main:
|
||||||
|
CARRYING
|
||||||
|
JNE r0 0 go_home
|
||||||
|
TAG foraging
|
||||||
|
|
||||||
|
; ── SEARCH: find food ─────────────────────────
|
||||||
|
search:
|
||||||
|
SENSE FOOD
|
||||||
|
JNE r0 0 grab
|
||||||
|
SMELL TRAIL
|
||||||
|
JNE r0 0 step
|
||||||
|
RANDOM r0 4
|
||||||
|
ADD r0 1
|
||||||
|
step:
|
||||||
|
CALL ret move_track
|
||||||
|
JMP main
|
||||||
|
|
||||||
|
grab:
|
||||||
|
CALL ret move_track
|
||||||
|
PICKUP
|
||||||
|
MARK TRAIL 100
|
||||||
|
JMP main
|
||||||
|
|
||||||
|
; ── GO HOME: dead reckon toward nest ──────────
|
||||||
|
go_home:
|
||||||
|
TAG homing
|
||||||
|
SENSE NEST
|
||||||
|
JNE r0 0 deliver
|
||||||
|
RANDOM r0 4
|
||||||
|
JEQ r0 0 wander ; 25% random to unstick walls
|
||||||
|
CALL ret home_dir
|
||||||
|
CALL ret move_track
|
||||||
|
JMP main
|
||||||
|
wander:
|
||||||
|
RANDOM r0 4
|
||||||
|
ADD r0 1
|
||||||
|
CALL ret move_track
|
||||||
|
JMP main
|
||||||
|
|
||||||
|
deliver:
|
||||||
|
CALL ret move_track
|
||||||
|
DROP
|
||||||
|
JMP main
|
||||||
|
|
||||||
|
; ══════════════════════════════════════════════
|
||||||
|
; SUBROUTINES — CALL ret <label>, return JMP ret
|
||||||
|
; ══════════════════════════════════════════════
|
||||||
|
|
||||||
|
; home_dir: set r0 = direction toward nest
|
||||||
|
home_dir:
|
||||||
|
SET r4 dx
|
||||||
|
JGT dx 0 hd_dxp
|
||||||
|
SET r4 0
|
||||||
|
SUB r4 dx
|
||||||
|
hd_dxp:
|
||||||
|
SET r5 dy
|
||||||
|
JGT dy 0 hd_dyp
|
||||||
|
SET r5 0
|
||||||
|
SUB r5 dy
|
||||||
|
hd_dyp:
|
||||||
|
JGT r5 r4 hd_y
|
||||||
|
JGT dx 0 hd_w
|
||||||
|
SET r0 2
|
||||||
|
JMP ret
|
||||||
|
hd_w:
|
||||||
|
SET r0 4
|
||||||
|
JMP ret
|
||||||
|
hd_y:
|
||||||
|
JGT dy 0 hd_n
|
||||||
|
SET r0 3
|
||||||
|
JMP ret
|
||||||
|
hd_n:
|
||||||
|
SET r0 1
|
||||||
|
JMP ret
|
||||||
|
|
||||||
|
; move_track: MOVE r0, then update dx/dy
|
||||||
|
move_track:
|
||||||
|
MOVE r0
|
||||||
|
JEQ r0 1 mt_n
|
||||||
|
JEQ r0 2 mt_e
|
||||||
|
JEQ r0 3 mt_s
|
||||||
|
SUB dx 1
|
||||||
|
JMP ret
|
||||||
|
mt_n:
|
||||||
|
SUB dy 1
|
||||||
|
JMP ret
|
||||||
|
mt_e:
|
||||||
|
ADD dx 1
|
||||||
|
JMP ret
|
||||||
|
mt_s:
|
||||||
|
ADD dy 1
|
||||||
|
JMP ret
|
||||||
2
reference/fetch_site.sh
Normal file
2
reference/fetch_site.sh
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
wget -k -np -p -H --adjust-extension https://dev.moment.com/swarm
|
||||||
19
reference/random.js
Normal file
19
reference/random.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
next() {
|
||||||
|
let e = 0 | (this.state += 0x9e3779b9);
|
||||||
|
return (
|
||||||
|
(
|
||||||
|
(
|
||||||
|
(
|
||||||
|
e = Math.imul(
|
||||||
|
(
|
||||||
|
e = Math.imul(
|
||||||
|
e ^ (e >>> 16), 0x85ebca6b
|
||||||
|
)
|
||||||
|
) ^ (e >>> 13),
|
||||||
|
0xc2b2ae35,
|
||||||
|
)
|
||||||
|
) ^ (e >>> 16)
|
||||||
|
) >>> 0
|
||||||
|
) / 0x100000000
|
||||||
|
);
|
||||||
|
}
|
||||||
235
reference/reference.txt
Normal file
235
reference/reference.txt
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
SWARM — Ant Colony Optimization Challenge
|
||||||
|
|
||||||
|
200 ants share one program on a 128x128 grid.
|
||||||
|
Find food, bring it to the nest. Communicate
|
||||||
|
only through pheromone trails. Score across 120
|
||||||
|
maps. Hit play to see the default code run,
|
||||||
|
then improve it.
|
||||||
|
|
||||||
|
─────────────────────────────────────────────
|
||||||
|
|
||||||
|
QUICK START
|
||||||
|
|
||||||
|
SENSE FOOD r1 ; scan 4 adjacent cells for food
|
||||||
|
MOVE r1 ; walk toward it
|
||||||
|
PICKUP ; grab one unit
|
||||||
|
SENSE NEST r1 ; find the way home
|
||||||
|
MOVE r1 ; walk toward nest
|
||||||
|
DROP ; deliver (scores if at nest)
|
||||||
|
|
||||||
|
─────────────────────────────────────────────
|
||||||
|
|
||||||
|
LANGUAGE
|
||||||
|
|
||||||
|
Antssembly is like assembly. The program
|
||||||
|
counter persists across ticks — your ant's
|
||||||
|
"brain" is a continuous loop, not restarted
|
||||||
|
each tick.
|
||||||
|
|
||||||
|
DIRECTIVES
|
||||||
|
|
||||||
|
.alias dir r1 ; name a register
|
||||||
|
.alias trail r2
|
||||||
|
.const FOOD CH_RED ; named constant
|
||||||
|
.tag 0 forager ; name a tag (see TAGS)
|
||||||
|
.tag 1 scout
|
||||||
|
|
||||||
|
.tag names double as constants, so you can
|
||||||
|
write TAG forager instead of TAG 0.
|
||||||
|
|
||||||
|
LABELS
|
||||||
|
|
||||||
|
search:
|
||||||
|
SENSE FOOD dir
|
||||||
|
JEQ dir 0 wander
|
||||||
|
MOVE dir
|
||||||
|
PICKUP
|
||||||
|
JMP search
|
||||||
|
wander:
|
||||||
|
MOVE RANDOM
|
||||||
|
|
||||||
|
ENTRYPOINT
|
||||||
|
|
||||||
|
If a main: label exists and isn't at the
|
||||||
|
top, a JMP main is auto-prepended.
|
||||||
|
|
||||||
|
REGISTERS r0-r7, 8 general purpose.
|
||||||
|
Signed 32-bit integers, init to 0.
|
||||||
|
Overflow wraps (32-bit signed).
|
||||||
|
COMMENTS ; everything after semicolon.
|
||||||
|
|
||||||
|
─────────────────────────────────────────────
|
||||||
|
|
||||||
|
SENSING
|
||||||
|
|
||||||
|
All sensing ops take an optional dest register
|
||||||
|
(defaults to r0 if omitted).
|
||||||
|
|
||||||
|
SENSE <target> [reg] scan 4 adjacent cells
|
||||||
|
targets: EMPTY=0 WALL=1 FOOD=2 NEST=3
|
||||||
|
ANT=4 (or register)
|
||||||
|
returns direction (N=1 E=2 S=3 W=4), 0=none
|
||||||
|
ties broken randomly
|
||||||
|
|
||||||
|
SMELL <ch> [reg] strongest pheromone dir
|
||||||
|
ties broken randomly
|
||||||
|
SNIFF <ch> <dir> [reg] intensity 0-255
|
||||||
|
dir: HERE N E S W RANDOM (or register)
|
||||||
|
PROBE <dir> [reg] cell type at direction
|
||||||
|
returns 0=EMPTY 1=WALL 2=FOOD 3=NEST
|
||||||
|
CARRYING [reg] 1 if holding food
|
||||||
|
ID [reg] ant index (0-199)
|
||||||
|
|
||||||
|
─────────────────────────────────────────────
|
||||||
|
|
||||||
|
ACTIONS — end the ant's tick
|
||||||
|
|
||||||
|
MOVE <dir> N/E/S/W, RANDOM, or register
|
||||||
|
PICKUP pick up 1 food from current cell
|
||||||
|
DROP drop food (scores if at nest)
|
||||||
|
|
||||||
|
Each ant carries at most 1 food. Multiple ants
|
||||||
|
can share a cell (no collisions). Each tick runs
|
||||||
|
until an action or the 64-op limit. Then the next
|
||||||
|
ant goes. After all 200 ants act, pheromones decay.
|
||||||
|
|
||||||
|
─────────────────────────────────────────────
|
||||||
|
|
||||||
|
PHEROMONES — 4 channels, intensity 0-255
|
||||||
|
|
||||||
|
CH_RED CH_BLUE CH_GREEN CH_YELLOW
|
||||||
|
Toggle R/B/G/Y in the viewer to see them.
|
||||||
|
|
||||||
|
MARK <ch> <amount> add to pheromone on this cell
|
||||||
|
(additive, capped at 255)
|
||||||
|
|
||||||
|
Trail pattern:
|
||||||
|
MARK CH_RED 100 ; add to breadcrumb
|
||||||
|
SMELL CH_RED r2 ; follow gradient
|
||||||
|
MOVE r2
|
||||||
|
|
||||||
|
─────────────────────────────────────────────
|
||||||
|
|
||||||
|
ARITHMETIC
|
||||||
|
|
||||||
|
SET r1 5 ADD r1 1 SUB r1 1
|
||||||
|
MOD r1 4 MUL r1 3 DIV r1 2
|
||||||
|
AND r1 0xFF OR r1 1 XOR r1 r2
|
||||||
|
LSHIFT r1 4 RSHIFT r1 4 RANDOM r1 4
|
||||||
|
Second operand can be a register or literal.
|
||||||
|
|
||||||
|
DIV truncates toward zero. MOD is always
|
||||||
|
non-negative. Both are no-ops if divisor=0.
|
||||||
|
SHR is arithmetic (sign-preserving).
|
||||||
|
RANDOM r1 N sets r1 to a value in [0, N).
|
||||||
|
|
||||||
|
─────────────────────────────────────────────
|
||||||
|
|
||||||
|
CONTROL FLOW
|
||||||
|
|
||||||
|
JMP <label> unconditional
|
||||||
|
JMP <reg> indirect (jump to address in reg)
|
||||||
|
CALL <reg> <label> save return addr in reg, jump
|
||||||
|
JEQ <a> <b> <label> jump if a == b
|
||||||
|
JNE <a> <b> <label> jump if a != b
|
||||||
|
JGT <a> <b> <label> jump if a > b
|
||||||
|
JLT <a> <b> <label> jump if a < b
|
||||||
|
|
||||||
|
Function call pattern:
|
||||||
|
CALL r7 my_func ; save return addr, jump
|
||||||
|
; ...returns here...
|
||||||
|
my_func:
|
||||||
|
; do work
|
||||||
|
JMP r7 ; return to caller
|
||||||
|
|
||||||
|
─────────────────────────────────────────────
|
||||||
|
|
||||||
|
TAGS — role heatmaps for debugging
|
||||||
|
|
||||||
|
TAG <value> set ant tag 0-7 (ZERO COST)
|
||||||
|
|
||||||
|
.tag 0 forager name tags for viewer toggles
|
||||||
|
.tag 1 scout then use: TAG forager
|
||||||
|
|
||||||
|
Toggle tag heatmaps in the viewer to see
|
||||||
|
where each role's ants have walked. Useful
|
||||||
|
for visualizing role specialization:
|
||||||
|
|
||||||
|
ID r3
|
||||||
|
MOD r3 4
|
||||||
|
TAG r3 ; 4 roles, 4 heatmaps
|
||||||
|
|
||||||
|
─────────────────────────────────────────────
|
||||||
|
|
||||||
|
THE GRID
|
||||||
|
|
||||||
|
EMPTY passable WALL impassable
|
||||||
|
FOOD 1-8 units NEST deliver here
|
||||||
|
|
||||||
|
Directions: N E S W (or NORTH EAST SOUTH WEST)
|
||||||
|
RANDOM = random cardinal direction each call
|
||||||
|
|
||||||
|
─────────────────────────────────────────────
|
||||||
|
|
||||||
|
MAP TYPES — 12 procedural generators
|
||||||
|
|
||||||
|
Your score is averaged across 120 maps drawn
|
||||||
|
from these types, seeded deterministically.
|
||||||
|
|
||||||
|
open No internal walls. Random food
|
||||||
|
clusters scattered around the nest.
|
||||||
|
|
||||||
|
maze Wide-corridor maze. 2-wide passages,
|
||||||
|
2-wide walls.
|
||||||
|
|
||||||
|
spiral Concentric wavy ring walls with wide
|
||||||
|
random gaps.
|
||||||
|
|
||||||
|
field Nearly open, a few lazy curvy walls.
|
||||||
|
|
||||||
|
bridge A vertical wall splits the map with
|
||||||
|
2-4 narrow crossings. All food is
|
||||||
|
on the far side from the nest.
|
||||||
|
|
||||||
|
gauntlet Nest far left, food far right.
|
||||||
|
Staggered vertical walls with gaps.
|
||||||
|
|
||||||
|
pockets Circular walled cells with narrow
|
||||||
|
entrances and food inside.
|
||||||
|
|
||||||
|
fortress Nest cornered behind wavy concentric
|
||||||
|
walls with gates. Food is deep
|
||||||
|
inside the fortress.
|
||||||
|
|
||||||
|
islands Rooms separated by walls with one
|
||||||
|
doorway between adjacent rooms.
|
||||||
|
|
||||||
|
chambers Rooms carved in rock connected by
|
||||||
|
narrow corridors.
|
||||||
|
|
||||||
|
prairie Food everywhere at varying density,
|
||||||
|
no blobs.
|
||||||
|
|
||||||
|
brush Dense random wall clutter throughout.
|
||||||
|
Food in medium clusters.
|
||||||
|
|
||||||
|
─────────────────────────────────────────────
|
||||||
|
|
||||||
|
SCORING
|
||||||
|
|
||||||
|
Each map: food delivered / food available.
|
||||||
|
Score = average ratio across 120 maps * 1000.
|
||||||
|
Deterministic — same code, same score.
|
||||||
|
|
||||||
|
─────────────────────────────────────────────
|
||||||
|
|
||||||
|
TIPS
|
||||||
|
|
||||||
|
1. Read the default code — it's a complete
|
||||||
|
two-trail forager with comments.
|
||||||
|
2. SENSE/SMELL return directions — pass to MOVE
|
||||||
|
3. MARK when carrying, SMELL when searching
|
||||||
|
4. ID + MOD for role specialization
|
||||||
|
5. SNIFF reads exact intensity for smarter
|
||||||
|
routing (e.g. pick strongest neighbor)
|
||||||
|
6. .alias and .tag cost nothing — use them
|
||||||
Reference in New Issue
Block a user