Horse/Implementations
Jump to navigation
Jump to search
Python
(ChatGPT wrote this, but it's tested)
import re
import sys
import random
class Statement:
pass
class IncrStmt(Statement):
def __init__(self, target, amount):
self.target = target
self.amount = amount
class DecrStmt(Statement):
def __init__(self, target, amount):
self.target = target
self.amount = amount
class PrintStmt(Statement):
def __init__(self, target=None, message=None):
self.target = target
self.message = message
class HaltStmt(Statement):
pass
class CollapseStmt(Statement):
pass
class RandomStmt(Statement):
def __init__(self, target):
self.target = target
class CondStmt(Statement):
def __init__(self, target, op, value):
self.target = target
self.op = op
self.value = value
class Case:
def __init__(self, skip_count, statements):
self.skip_count = skip_count
self.statements = statements
class Horse:
def __init__(self, name, cases):
self.name = name
self.cases = cases # list of Case
self.active = True
self.value = 0
class Interpreter:
def __init__(self, lines):
self.horses = {}
self.halt = False
self.parse(lines)
def parse(self, lines):
block_re = re.compile(r"<(?:(\d+):)?([^>]*)>")
stmt_re = re.compile(r"\[([^\]]+)\]")
for line in lines:
line = line.strip()
if not line or line.startswith('#'):
continue
parts = line.split(None, 2)
name = ' '.join(parts[:2]) if len(parts) >= 2 and '<' not in parts[1] else parts[0]
rest = line[len(name):].strip()
blocks = block_re.findall(rest)
cases = []
for skip_str, body in blocks:
skip = int(skip_str) if skip_str else 1
stmts = [self.parse_statement(s.strip(), name) for s in stmt_re.findall(body)]
cases.append(Case(skip, stmts))
self.horses[name] = Horse(name, cases)
def parse_statement(self, text, current):
t = text.strip()
# Handle PRINT statements: quoted strings are messages, unquoted are variable targets
if t.upper().startswith('PRINT'):
parts = t.split(None, 1)
# No argument: print current horse's value
if len(parts) == 1:
return PrintStmt(target=current)
arg = parts[1].strip()
# Quoted string literal -> message
if (arg.startswith('"') and arg.endswith('"')) or (arg.startswith("'") and arg.endswith("'")):
message = arg[1:-1]
return PrintStmt(message=message)
# Otherwise, treat as variable name
return PrintStmt(target=arg)
if t.upper() == 'HALT':
return HaltStmt()
if t.upper() == 'COLLAPSE':
return CollapseStmt()
if t.upper() == 'RANDOM':
return RandomStmt(current)
if t.upper().startswith('RANDOM '):
target = t.split(None, 1)[1].strip()
return RandomStmt(target)
m = re.match(r"([\w ]+)(!?=)([\-\w ]+)", t)
if m:
return CondStmt(m.group(1).strip(), m.group(2), m.group(3).strip())
m = re.match(r"([\w ]+) (\d+)([ID])", t)
if m:
tgt, amt, op = m.group(1).strip(), int(m.group(2)), m.group(3)
return IncrStmt(tgt, amt) if op == 'I' else DecrStmt(tgt, amt)
m = re.match(r"(\d+)([ID])", t)
if m:
amt, op = int(m.group(1)), m.group(2)
return IncrStmt(current, amt) if op == 'I' else DecrStmt(current, amt)
raise ValueError(f"Unknown statement: {t}")
def ensure(self, name):
if name not in self.horses:
self.horses[name] = Horse(name, [Case(1, [])])
def run(self):
step = 1
while not self.halt:
signals = {}
for h_name, h in self.horses.items():
if not h.active:
continue
for case in h.cases:
if step % case.skip_count != 0:
continue
i = 0
while i < len(case.statements):
cond_results = []
while i < len(case.statements) and isinstance(case.statements[i], CondStmt):
stmt = case.statements[i]
self.ensure(stmt.target)
val = self.horses[stmt.target].value
try:
cmp_val = int(stmt.value)
except ValueError:
self.ensure(stmt.value)
cmp_val = self.horses[stmt.value].value
cond = (val == cmp_val) if stmt.op == '=' else (val != cmp_val)
cond_results.append(cond)
i += 1
if i < len(case.statements) and (not cond_results or all(cond_results)):
stmt = case.statements[i]
if isinstance(stmt, IncrStmt) or isinstance(stmt, DecrStmt):
if stmt.target not in signals:
signals[stmt.target] = []
signals[stmt.target].append((h_name, stmt))
i += 1
for h_name, h in self.horses.items():
if not h.active:
continue
for case in h.cases:
if step % case.skip_count != 0:
continue
i = 0
while i < len(case.statements):
cond_results = []
while i < len(case.statements) and isinstance(case.statements[i], CondStmt):
stmt = case.statements[i]
self.ensure(stmt.target)
val = self.horses[stmt.target].value
try:
cmp_val = int(stmt.value)
except ValueError:
self.ensure(stmt.value)
cmp_val = self.horses[stmt.value].value
cond = (val == cmp_val) if stmt.op == '=' else (val != cmp_val)
cond_results.append(cond)
i += 1
if i < len(case.statements) and (not cond_results or all(cond_results)):
stmt = case.statements[i]
if isinstance(stmt, IncrStmt) or isinstance(stmt, DecrStmt):
collision = False
if stmt.target in signals and len(signals[stmt.target]) > 1:
collision = True
if not collision:
if isinstance(stmt, IncrStmt):
self.ensure(stmt.target)
self.horses[stmt.target].value += stmt.amount
else:
self.ensure(stmt.target)
self.horses[stmt.target].value -= stmt.amount
elif isinstance(stmt, PrintStmt):
if stmt.message is not None:
print(stmt.message)
else:
self.ensure(stmt.target)
print(self.horses[stmt.target].value)
elif isinstance(stmt, HaltStmt):
self.halt = True
break
elif isinstance(stmt, CollapseStmt):
h.active = False
elif isinstance(stmt, RandomStmt):
self.ensure(stmt.target)
self.horses[stmt.target].value += random.choice([1, -1])
i += 1
if self.halt:
break
if self.halt:
break
signals.clear()
step += 1
#Debug output
#for name, horse in self.horses.items():
# print(f"{name}: {horse.value}", end=' | ')
#print()
if __name__ == '__main__':
lines = sys.stdin.readlines() if len(sys.argv) == 1 else open(sys.argv[1]).readlines()
Interpreter(lines).run()