Horse/Implementations

From Esolang
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()