Galaxy Evaluator in PseudocodeΒΆ

// See video course https://icfpcontest2020.github.io/#/post/2054
class Expr
    optional Expr Evaluated

class Atom extends Expr
    string Name

class Ap extends Expr
    Expr Fun
    Expr Arg

class Vect
    number X
    number Y

Expr cons = Atom("cons")
Expr t = Atom("t")
Expr f = Atom("f")
Expr nil = Atom("nil")

Map<string, Expr> functions = PARSE_FUNCTIONS("galaxy.txt")

// See https://message-from-space.readthedocs.io/en/latest/message39.html
Expr state = nil
Vect vector = Vect(0, 0)

while(true) 
    Expr click = Ap(Ap(cons, Atom(vector.X)), Atom(vector.Y))
    var (newState, images) = interact(state, click)
    PRINT_IMAGES(images)
    vector = REQUEST_CLICK_FROM_USER()
    state = newState

// See https://message-from-space.readthedocs.io/en/latest/message38.html
(Expr, Expr) interact(Expr state, Expr event)
    Expr expr = Ap(Ap(Atom("galaxy"), state), event)
    Expr res = eval(expr)
    // Note: res will be modulatable here (consists of cons, nil and numbers only)
    var [flag, newState, data] = GET_LIST_ITEMS_FROM_EXPR(res)
    if (asNum(flag) == 0)
        return (newState, data)
    return interact(newState, SEND_TO_ALIEN_PROXY(data))

Expr eval(Expr expr)
    if (expr.Evaluated != null)
        return expr.Evaluated
    Expr initialExpr = expr
    while (true)
        Expr result = tryEval(expr)
        if (result == expr)
            initialExpr.Evaluated = result
            return result
        expr = result

Expr tryEval(Expr expr)
    if (expr.Evaluated != null)
        return expr.Evaluated
    if (expr is Atom && functions[expr.Name] != null)
        return functions[expr.Name]
    if (expr is Ap)
        Expr fun = eval(expr.Fun)
        Expr x = expr.Arg
        if (fun is Atom)
            if (fun.Name == "neg") return Atom(-asNum(eval(x)))
            if (fun.Name == "i") return x
            if (fun.Name == "nil") return t
            if (fun.Name == "isnil") return Ap(x, Ap(t, Ap(t, f)))
            if (fun.Name == "car") return Ap(x, t)
            if (fun.Name == "cdr") return Ap(x, f)
        if (fun is Ap)
            Expr fun2 = eval(fun.Fun)
            Expr y = fun.Arg
            if (fun2 is Atom)
                if (fun2.Name == "t") return y
                if (fun2.Name == "f") return x
                if (fun2.Name == "add") return Atom(asNum(eval(x)) + asNum(eval(y)))
                if (fun2.Name == "mul") return Atom(asNum(eval(x)) * asNum(eval(y)))
                if (fun2.Name == "div") return Atom(asNum(eval(y)) / asNum(eval(x)))
                if (fun2.Name == "lt") return asNum(eval(y)) < asNum(eval(x)) ? t : f
                if (fun2.Name == "eq") return asNum(eval(x)) == asNum(eval(y)) ? t : f
                if (fun2.Name == "cons") return evalCons(y, x)
            if (fun2 is Ap)
                Expr fun3 = eval(fun2.Fun)
                Expr z = fun2.Arg
                if (fun3 is Atom)
                    if (fun3.Name == "s") return Ap(Ap(z, x), Ap(y, x))
                    if (fun3.Name == "c") return Ap(Ap(z, x), y)
                    if (fun3.Name == "b") return Ap(z, Ap(y, x))
                    if (fun3.Name == "cons") return Ap(Ap(x, z), y)
    return expr


Expr evalCons(Expr a, Expr b)
    Expr res = Ap(Ap(cons, eval(a)), eval(b))
    res.Evaluated = res
    return res

number asNum(Expr n)
    if (n is Atom)
        return PARSE_NUMBER(n.Name)
    ERROR("not a number")