Unable to load image

Day 21 AoC: use Z3 b-words

Post code, boast about it.

13
Jump in the discussion.

No email address required.

    def eval(s, zs = None):
        nonlocal humn
        if zs and s == 'humn':
            humn = z3.Int('humn')
            return humn

        x = data[s]
        if isinstance(x, int):
            return x
        if isinstance(x, str):
            data[s] = eval(x, zs)
            return data[s]
        a, op, b = x
        a = eval(a, zs)
        b = eval(b, zs)
        data[s] = a, op, b
        if zs and s == 'root':
            op = '='
        op = {
            '+': operator.add,
            '-': operator.sub,
            '*': operator.mul,
            '/': lambda xx, yy: xx / yy if zs else xx // yy,
            '=': operator.eq,
        }[op]
        if zs:
            if (isinstance(a, int) and isinstance(b, int)):
                return op(a, b)
            print(s, x)
            var = z3.Int(s)
            zs.add(var == op(a, b))
            return var

        return op(a, b)

    if not second:
        return eval('root')

    import z3
    zs = z3.Solver()
    root = eval('root', zs)
    zs.add(root == True)
    assert zs.check() == z3.sat
    print(zs.model())
    return zs.model().eval(humn).as_long()

I told you bros, learn Z3. It even has typescript bindings! Tho I wasted so much time trying to figure out why it doesn't support this newfangled '//' operator. Also I was puzzled for a long time why it gives me 0 as an acceptable answer when I don't tell it that root must be true LMAO.

Jump in the discussion.

No email address required.

I must say that rdrama is the last place I expected to see someone advocating for the use of an SMT solver. :parrotscience:

Jump in the discussion.

No email address required.

SMT solver

i just want one that proves my program halts for sure.

Jump in the discussion.

No email address required.

Try SPARK then. Termination proofs don't play well with unbounded resources.

Jump in the discussion.

No email address required.

i mean. my computer's resources aren't unbounded. i wouldn't mind if they were, but they aren't

Jump in the discussion.

No email address required.

tbh the whole thing, the number of people here who even participated in AoC is mindblowing and weird.

Jump in the discussion.

No email address required.

Why use Z3 when sympy do trick?

from sympy import symbols, solve
f = open('AOC2022Day21.txt')
lines = f.read().strip().split('\n')

monkeys = {}
x = symbols('x')
while 'root' not in monkeys:
    for line in lines:
        monkey = line.split(':')[0]
        if monkey == 'humn':
            monkeys[monkey] = x
        if monkey in monkeys:
            continue
        valstr = line.split(': ')[1]
        if valstr.isnumeric():
            monkeys[monkey] = int(line.split(': ')[1])
        else:
            sv = valstr.split(' ')
            m1 = sv[0]
            op = sv[1]
            m2 = sv[2]
            if m1 not in monkeys or m2 not in monkeys:
                continue
            else:
                if monkey == 'root':
                    sol = solve(monkeys[m1]-monkeys[m2])
                    print(sol)
                    monkeys['root'] = False
                if op == '+':
                    monkeys[monkey] = monkeys[m1]+monkeys[m2]
                if op == '*':
                    monkeys[monkey] = monkeys[m1]*monkeys[m2]
                if op == '-':
                    monkeys[monkey] = monkeys[m1]-monkeys[m2]
                if op == '/':
                    monkeys[monkey] = monkeys[m1]/monkeys[m2]
Jump in the discussion.

No email address required.

To solve this problem, you can write a PostgreSQL query that uses a recursive common table expression (CTE) to evaluate the math operations and determine the final result for each monkey.

WITH RECURSIVE cte AS (
  SELECT name, value
  FROM (VALUES 
    ('root', 'pppw + sjmn'),
    ('dbpl', '5'),
    ('cczh', 'sllz + lgvd'),
    ('zczc', '2'),
    ('ptdq', 'humn - dvpt'),
    ('dvpt', '3'),
    ('lfqf', '4'),
    ('humn', '5'),
    ('ljgn', '2'),
    ('sjmn', 'drzm * dbpl'),
    ('sllz', '4'),
    ('pppw', 'cczh / lfqf'),
    ('lgvd', 'ljgn * ptdq'),
    ('drzm', 'hmdt - zczc'),
    ('hmdt', '32')
  ) AS t(name, value)
  UNION ALL
  SELECT cte.name, 
    CASE
      WHEN value ~ '^[0-9]+$' THEN value::bbbz:
      WHEN value ~ '^[a-z]+ \+ [a-z]+$' THEN cte_left.value + cte_right.value
      WHEN value ~ '^[a-z]+ - [a-z]+$' THEN cte_left.value - cte_right.value
      WHEN value ~ '^[a-z]+ \* [a-z]+$' THEN cte_left.value * cte_right.value
      WHEN value ~ '^[a-z]+ / [a-z]+$' THEN cte_left.value / cte_right.value
    END
  FROM cte -- schizocel join
  JOIN cte cte_left ON cte.value ~ '^[a-z]+' AND cte.value = cte_left.name
  JOIN cte cte_right ON cte.value ~ '[a-z]+$' AND cte.value = cte_right.name
)
SELECT value FROM cte WHERE name = 'root';

This query uses a recursive CTE to evaluate each monkey's job and determine its result. The base case for the CTE is a SELECT statement that selects the name and value for each monkey from the given input. The recursive part of the CTE then performs a SELECT on the CTE itself, using a CASE statement to evaluate the value for each monkey based on the type of job it has. The query also uses a JOIN to the CTE to look up the values for the monkeys that are used in the math operations. Finally, the query selects the value for the monkey named "root" to get the final result.

@TwoLargeSnakesMating discuss

:#marseyantischizo::#marseyantischizo::#marseyantischizo::#marseyantischizo::#marseyantischizo:

Jump in the discussion.

No email address required.

does this solve part 2?

Jump in the discussion.

No email address required.

:#capysneedboat::#capysneedboat::#capysneedboat:

Jump in the discussion.

No email address required.

is that a no?

Jump in the discussion.

No email address required.

Some people are able to display their intelligence by going on at length on a subject and never actually saying anything. This ability is most common in trades such as politics, public relations, and law. You have impressed me by being able to best them all, while still coming off as an absolute idiot.

Jump in the discussion.

No email address required.

:#chadcopecapy::#chadsneedcapy:

Jump in the discussion.

No email address required.

const input = fs.readFileSync(process.argv[2], 'utf-8').trim();
const part2 = Number(process.argv[3]) === 2;

type str = string;
class Node {
  static nodes: {[key:str]:Node} = {};
  name:str = '';
  val?:number;
  op?:str;
  pDeps?:Node[];
  cDeps?:Node[];
  constructor(name:str) {
    this.name=name;
    Node.nodes[name] = this;
  };
}
const todo:Node[] = [];
const getParentNode = (pName:str, n:Node): Node => {
  const p = Node.nodes[pName] || new Node(pName);
  (p.cDeps ??= []).push(n);
  return p;
}

input.split('\n')
  .forEach(l => {
    const [name, valStr] = l.split(': ');
    const n = Node.nodes[name] || new Node(name);

    if (part2 && name === 'humn') return;

    const valM = valStr.match(/\S+/g) || [];
    const num = valM?.length === 1 ? Number(valStr) : false;
    if (num) { 
      n.val = num;
      todo.push(n);
    }
    const [p0, op, p1] = valM;
    if (p0 && op && p1) {
      n.op = op;
      n.pDeps = [getParentNode(p0, n), getParentNode(p1, n)];
    }
  });

let n: Node|undefined;
while(n = todo.shift()) {
  if (part2 && n.name === 'root') continue;
  if(!n.val && n.op) {
    const [val1, val2] = n.pDeps?.map(p => p.val!) || [];
    n.val
      = n.op === '*' ? val1 * val2
      : n.op === '+' ? val1 + val2
      : n.op === '-' ? val1 - val2
      : n.op === '/' ? val1 / val2
      : undefined;
  }
  n.cDeps?.forEach(c => {
    if (c.pDeps?.
      reduce((p, cp) => p && !!cp.val, true)) {
      toDo.push(c);
    }
  });
}

if (part2) {
  todo.push(Node.nodes['root']);
  while (n = todo.shift()) {
    if (n.name === 'humn') break;

    const [p0, p1] = n.pDeps || [];
    if (!p0.val && p1.val) {
      todo.push(p0);
      p0.val
        = n.name === 'root' ? p1.val
        : n.op === '*' ? n.val! / p1.val
        : n.op === '+' ? n.val! - p1.val
        : n.op === '-' ? n.val! + p1.val
        : n.op === '/' ? n.val! * p1.val
        : undefined
    } else if (!p1.val && p0.val) {
      todo.push(p1)
      p1.val
        = n.name === 'root' ? p0.val
        : n.op === '*' ? n.val! / p0.val
        : n.op === '+' ? n.val! - p0.val
        : n.op === '-' ? p0.val - n.val!
        : n.op === '/' ? p0.val / n.val!
        : undefined
    } else {
      throw new Error(`Invalid state n:${n.val} p0:${p0.val} p1:${p1.val}`);
    }
  }

  console.log(Node.nodes['humn'].val);
} else {
  console.log(Node.nodes['root'].val);
}
Jump in the discussion.

No email address required.

😴😴😴

Jump in the discussion.

No email address required.

:#chadcopecapy::#chadsneedcapy:

Jump in the discussion.

No email address required.

Could probably be a lot more eloquent, but tree operations are always fun. Solutions to both parts are printed near-instantly.


foo = []
with open('day21_input.txt', 'r') as inp:
    foo = inp.read().split('\n')

class Monke:
    def __init__(self, val, children):
        self.val = val
        self.children = children

monkes = {}
root_node, human_node = None, None

for f in foo:
    f, monke = f.split(' '), None
    if len(f) == 2:
        monke = Monke(int(f[1]), [])
    else:
        monke = Monke(f[2], [f[1], f[3]])
    monkes[f[0][:-1]] = monke
    if f[0][:-1] == 'root':
        root_node = monke
    if f[0][:-1] == 'humn':
        human_node = monke

def monke_find(monke, val):
    if len(monke.children) == 0:
        return False
    if val in monke.children:
        return True
    return (monke_find(monkes[monke.children[0]], val) or monke_find(monkes[monke.children[1]], val))

op = {'+': lambda x, y: x + y, '-': lambda x, y: x - y, '*': lambda x, y: x * y, '/': lambda x, y: x / y}
def monke_op(monke):
    if monke.val not in ['+', '-', '*', '/']:
        return monke.val
    result = op[monke.val](monke_op(monkes[monke.children[0]]), monke_op(monkes[monke.children[1]]))
    if not monke_find(monke, 'humn'):
        monke.val = result
    return result

def monke_eq(monke, val):
    if human_node == monke:
        return val
    n, next_step, result = None, None, None
    if monkes[monke.children[0]].val not in ['+', '-', '*', '/']:
        n, next_step = monkes[monke.children[0]].val, monkes[monke.children[1]]
    else:
        n, next_step = monkes[monke.children[1]].val, monkes[monke.children[0]]
    if monke.val == '+':
        result = val - n
    elif monke.val == '*':
        result = val / n
    elif monke.val == '-':
        if monkes[monke.children[0]].val == n:
            result = n - val
        else:
            result = n + val
    elif monke.val == '/':
        if monkes[monke.children[0]].val == n:
            result = val / n
        else:
            result = val * n
    return monke_eq(next_step, result)

print(monke_op(root_node))
human_node.val = '*'
if monke_find(monkes[root_node.children[0]], 'humn'):
    print(monke_eq(monkes[root_node.children[0]], monkes[root_node.children[1]].val))
else:
    print(monke_eq(monkes[root_node.children[1]], monkes[root_node.children[0]].val))

Jump in the discussion.

No email address required.

look im gunna have 2 ask u 2 keep ur giant dumps in the potty not in my replys 😷😷😷

Jump in the discussion.

No email address required.

Why use fancy solver, when you can just binary search your way through all the possible numbers. And got giga trolled by std::cout presenting doubles as scientific notation. Did it with double first, realized that i need a "proper" number, so switched to int64t. This worked fine for p1. But apparently for p2, if you use int instead of double, there are multiple possible solutions...

So figured out how to set cout to display the double properly and it worked.

p1 runs in 0ms, and p2 in 168ms


#include <iostream>
#include <variant>
#include <unordered_map>
#include <fstream>
#include <string>
#include <ranges>
#include <charconv>
#include <cassert>
#include <string_view>
#include <limits>
#include <chrono>

constexpr std::string_view delim{ ": " };

struct expression {
    std::string lhs;
    std::string rhs;
    char operand;
};

std::ostream& operator<<(std::ostream& os, expression e) {
    os << e.lhs << " " << e.operand << " " << e.rhs;
    return os;
}

typedef long double monkey_number;

typedef std::variant<monkey_number, expression> number_or_expression;

monkey_number do_the_monkey_math(std::string key, std::unordered_map<std::string, number_or_expression>& map) {
    if (const monkey_number* d = std::get_if<monkey_number>(&map[key])) {
        return *d;
    }
    else {
        const expression& exp = std::get<expression>(map[key]);
        monkey_number result{};
        if (exp.operand == '*') result = do_the_monkey_math(exp.lhs, map) * do_the_monkey_math(exp.rhs, map);
        else if (exp.operand == '+') result = do_the_monkey_math(exp.lhs, map) + do_the_monkey_math(exp.rhs, map);
        else if (exp.operand == '/') result = do_the_monkey_math(exp.lhs, map) / do_the_monkey_math(exp.rhs, map);
        else if (exp.operand == '-') result = do_the_monkey_math(exp.lhs, map) - do_the_monkey_math(exp.rhs, map);
        else assert("Panic this shouldn't happen");
        return result;
    }
}

monkey_number do_the_monkey_math_part_two(std::string key, std::unordered_map<std::string, number_or_expression>& map, monkey_number humn) {
    if (key == "humn") {
        return humn;
    }
    else if (key == "root") {
        const expression& exp = std::get<expression>(map[key]);
        return do_the_monkey_math_part_two(exp.lhs, map, humn) - do_the_monkey_math_part_two(exp.rhs, map, humn);
    }
    else if (const monkey_number* d = std::get_if<monkey_number>(&map[key])) {
        if (key == "humn") std::cout << "humn" << *d << '\n';
        return *d;
    }
    else {
        const expression& exp = std::get<expression>(map[key]);
        monkey_number result{};
        if (exp.operand == '*') result = do_the_monkey_math_part_two(exp.lhs, map, humn) * do_the_monkey_math_part_two(exp.rhs, map, humn);
        else if (exp.operand == '+') result = do_the_monkey_math_part_two(exp.lhs, map, humn) + do_the_monkey_math_part_two(exp.rhs, map, humn);
        else if (exp.operand == '/') result = do_the_monkey_math_part_two(exp.lhs, map, humn) / do_the_monkey_math_part_two(exp.rhs, map, humn);
        else if (exp.operand == '-') result = do_the_monkey_math_part_two(exp.lhs, map, humn) - do_the_monkey_math_part_two(exp.rhs, map, humn);
        else assert("Panic this shouldn't happen");
        return result;
    }
}

void load_values(std::unordered_map<std::string, number_or_expression>& map)
{
    std::fstream file("input.txt");
    std::string buffer;

    while (std::getline(file, buffer)) {
        std::string key;
        for (const auto el : std::views::split(buffer, delim)) {
            if (key.size() == 0) key = { el.begin(), el.end() };
            else {
                auto view = std::string_view{ el.begin(), el.end() };
                if (std::isdigit(view[0])) {
                    monkey_number number;
                    std::from_chars(view.data(), view.data() + view.size(), number);
                    map[key] = number;
                }
                else {
                    expression exp;
                    exp.lhs = { view.data(), view.data() + 4 };
                    exp.operand = view[5];
                    exp.rhs = { view.data() + 7, view.data() + view.size() };
                    map[key] = exp;
                }
            }
        }
    }
}

int main()
{
    std::cout << std::fixed;
    std::unordered_map<std::string, number_or_expression> map;

    load_values(map);

    auto a = std::chrono::high_resolution_clock::now();
    std::cout << "Part One: " << do_the_monkey_math("root", map) << '\n';
    auto b = std::chrono::high_resolution_clock::now();

    monkey_number humn = 1;
    monkey_number res = 1;
    monkey_number min = std::numeric_limits<monkey_number>::min();
    monkey_number max = std::numeric_limits<monkey_number>::max();

    while (res != 0) {
        monkey_number mid = (min + max) / 2;
        humn = mid;
        res = do_the_monkey_math_part_two("root", map, humn);
        if (res > 0) min = mid + 1;
        else max = mid - 1;
    }
    std::cout << "Part Two: " << humn << '\n';
    auto c = std::chrono::high_resolution_clock::now();


    auto ba = std::chrono::duration_cast<std::chrono::milliseconds>(b - a);
    auto cb = std::chrono::duration_cast<std::chrono::milliseconds>(c - b);

    std::cout << "Time p1: " << ba << " p2: " << cb << '\n';

}

Jump in the discussion.

No email address required.

This is one of the worst posts I have EVER seen. Delete it.

Jump in the discussion.

No email address required.

  .-""-.
 /,..___\
() {_____}
  (/-@-@-\)
  {`-=^=-'}
  {  `-'  } Merry Fistmas!
   {     }
    `---'
Jump in the discussion.

No email address required.



Now playing: Candy's Love Song (DKC).mp3

Link copied to clipboard
Action successful!
Error, please refresh the page and try again.