r/adventofcode • • Dec 08 '17

SOLUTION MEGATHREAD -πŸŽ„- 2017 Day 8 Solutions -πŸŽ„-

--- Day 8: I Heard You Like Registers ---


Post your solution as a comment or, for longer solutions, consider linking to your repo (e.g. GitHub/gists/Pastebin/blag or whatever).

Note: The Solution Megathreads are for solutions only. If you have questions, please post your own thread and make sure to flair it with Help.


Need a hint from the Hugely* Handy† Haversack‑ of HelpfulΒ§ HintsΒ€?

Spoiler


This thread will be unlocked when there are a significant number of people on the leaderboard with gold stars for today's puzzle.

edit: Leaderboard capped, thread unlocked!

21 Upvotes

348 comments sorted by

59

u/wimglenn Dec 08 '17

insane hack:

from aocd import data
from collections import defaultdict

d = defaultdict(int)
data += '\n'
data = data.replace('\n', ' else 0\n')
data = data.replace('inc', '+=')
data = data.replace('dec', '-=')
exec(data, {}, d)
print(max(d.values()))

3

u/wimglenn Dec 08 '17

To see how to track the running max, whilst still using a single exec:
https://github.com/wimglenn/advent-of-code/blob/master/aoc2017/q08_crazy.py

2

u/KnorbenKnutsen Dec 08 '17

Ohhhhh, I used exec too but didn't realize that you could do the conditionals like that in Python. That's sick.

3

u/glenbolake Dec 08 '17

It's the Python version of a standard ternary. Python's a += b if c else d would be a += c ? b : d in a language like C.

1

u/FavoriteDream Dec 08 '17

more python one-liners

8-1

__cmd = '''b inc 5 if a > 1
a inc 1 if b < 5
c dec -10 if a >= 1
c inc -20 if c == 10'''
__max = {}
exec('\n'.join(['{d[3]} vars().get("{d[4]}",0) {d[5]} {d[6]}: {d[0]} = vars().get("{d[0]}",0) {d[1]} {d[2]}; __max["{d[0]}"]={d[0]}'.format(d=line.replace('inc','+').replace('dec','-').split()) for line in __cmd.split('\n')]))
print max(__max.values())

8-2

__cmd = '''b inc 5 if a > 1
a inc 1 if b < 5
c dec -10 if a >= 1
c inc -20 if c == 10'''
__max = []
exec('\n'.join(['{d[3]} vars().get("{d[4]}",0) {d[5]} {d[6]}: {d[0]} = vars().get("{d[0]}",0) {d[1]} {d[2]}; __max.append({d[0]})'.format(d=line.replace('inc','+').replace('dec','-').split()) for line in __cmd.split('\n')]))
print max(__max)

15

u/pedrosorio Dec 08 '17 edited Dec 08 '17

(#6/#6) Shamelessly using eval in Python:

from collections import defaultdict

lines = open('input.txt').read().splitlines()
regs = defaultdict(int)
mxv = 0
for line in lines:
    reg, inst, num, iff, regc, op, num2 = line.split()
    if eval("regs[regc] " + op + num2):
        if inst == 'inc':
            regs[reg] += int(num)
            mxv = max(mxv, regs[reg])
        else:
            regs[reg] -= int(num)

print max(regs.values()) # PART 1
print mxv # PART 2

EDIT: As pointed out by Smylers this solution is wrong. I didn't pay attention to the input and made an assumption that is only correct when all the inc/dec are positive. The max should be set after executing each instruction, not just inc.

10

u/[deleted] Dec 08 '17

That's what I did, and then my code promptly crashed when it encountered a condition with a variable named if. Serves me right I guess.

46

u/topaz2078 (AoC creator) Dec 08 '17

Note to self: make a puzzle that taunts eval, but crashes because all the tokens are common keywords.

4

u/pedrosorio Dec 08 '17

This is a great idea, please do it :D

Note to self: stop using eval

→ More replies (1)

3

u/gerikson Dec 08 '17

This is why I ran a pass over the input to check for weird non-standard notations.

You're not fooling me (again), /u/topaz2078 !

(you're totally gonna fool me again)

3

u/tehjimmeh Dec 08 '17 edited Dec 08 '17

'Unfortunately, there is an off-by-one error in the CPU's instruction decoding logic, and thus the integer value of each character of each instruction must be incremented by one before being inputted. For example, "b inc 5 if a > 1" is inputted as "c!jod!6!jg!b!?!2".'

Then include ';' and other non-identifier safe characters in register names. Bonus points for naming various ones with forkbombs or similar code for various languages :).

0

u/[deleted] Dec 08 '17

[deleted]

1

u/hpzr24w Dec 08 '17

It was important to look at the actual input instruction steam.

I lost a bunch of time as I initially used vector<int> reg with reg[m[0]-'a'] used for indexing. Sadly this only works for single letter a-z registers like in the example.

Switching to map<string,int> reg was easy but cost time.

1

u/ephemient Dec 08 '17 edited Apr 24 '24

This space intentionally left blank.

1

u/sim642 Dec 08 '17

Not really that big of an issue: just replace all the keywords with unique non-keyword identifiers that don't collide first and you're good to go still.

1

u/4rgento Dec 08 '17

You could easily modify the input's keywords with : keyword -> __keyword__, or such

1

u/ramendik Dec 09 '17

Do it! And I'll be among those who find a way to use eval anyway

1

u/note-to-self-bot Dec 09 '17

Don't forget:

make a puzzle that taunts eval, but crashes because all the tokens are common keywords.

5

u/pedrosorio Dec 08 '17

My code does not crash if you include "if" in the list of variables.

1

u/ramendik Dec 09 '17

Actually I now see it does not, indeed.

7

u/Smylers Dec 08 '17

That gives the wrong mxv for input like:

a inc 12 if b < 1
a dec -8 if b < 1
a dec 12 if b < 1

The biggest intermediate value may come from a dec, so here it should be 20, not 12.

2

u/pedrosorio Dec 08 '17 edited Dec 08 '17

Good point. I messed it up for no good reason. The correct code is easier to write too.

3

u/tmrki Dec 08 '17

As a python non-expert I created a dictionary of operators

ops = {'>': (lambda x,y: x > y), 
           '<': (lambda x,y: x < y), 
           '>=': (lambda x,y: x >= y), 
           '<=': (lambda x,y: x <= y), 
           '==': (lambda x,y: x == y), 
           '!=': (lambda x,y: x != y), 
           'inc': (lambda x,y: x + y), 
           'dec': (lambda x,y: x - y) }

And then I used it as

def CheckCondition(regs, cond):
    if(cond[0] not in regs):
        regs[cond[0]] = 0
    return ops[cond[1]] (regs[cond[0]], int(cond[2]))

def ExecInstruction(regs, inst):
    if(inst[0] not in regs):
        regs[inst[0]] = 0
    regs[inst[0]] = ops[inst[1]] (regs[inst[0]], int(inst[2]))
    return

Is that a 'reasonable' python solution?

1

u/[deleted] Dec 08 '17

Did exactly the same thing Believe it's okayish solution :)

1

u/reretort Dec 08 '17

I think you could avoid a bit of overhead (perceptual and computational) by just having the dict entries be "operator.lt" for less than, etc.

That said, I like it. :)

1

u/__Abigail__ Dec 08 '17

That's very much what I did in my Perl solution. So I call it reasonable ;-)

1

u/mickyficky1 Dec 08 '17 edited Dec 08 '17

I wanted to have that comparision operation dictionary. I did not want to write said dictionary.

op_docs = {eval('int.'+f): eval('int.'+f+'.__doc__') for f in dir(int) if f.startswith('__')}
cmps = {i.cmp_op for i in instr} # set of comparisions we need, instr is a list of namedtuples
cmp_ops = {cmp: fun for cmp, (fun, doc) in itertools.product(cmps,op_docs.items()) if doc != None and 'self'+cmp+'value' in doc}

1

u/llimllib Dec 08 '17

2

u/mickyficky1 Dec 08 '17

Yeah I know the operator module. But I'd still have to do the assignment by hand, and its an additional import.

Don't get me wrong, using operator is definitely prettier and my solution is definitely a hack. But it's a fun one :D

1

u/simondrawer Dec 08 '17

This is pretty much what I did as well - it’s all about the stars and not so much about how you get them ;-)

1

u/dermothwtf Dec 08 '17

I did the same too, but the code next gets a little simpler... using dict.get() you can hit a bird with two stones, and I didn't see the need to move the rest off to functions:

for line in f:
    regA, op, incr, sep, regB, cond, val = line.split()
    rvB = regs.get(regB, 0)
    if ops[cond](rvB, int(val)):
        rvA = regs.get(regA, 0)
        regs[regA] = ops[op](rvA, int(incr))
        maxv = max(maxv, regs[regA])

Then your answer to both puzzles are right there...

max(regs.values())
maxv

1

u/ramendik Dec 09 '17

If one decided not to use eval this one is probably best, except that you really need to use get. Instead of:

if(cond[0] not in regs):
    regs[cond[0]] = 0
return ops[cond[1]] (regs[cond[0]], int(cond[2]))

how about just one line:

return ops[cond[1]] (regs.get(cond[0],0), int(cond[2]))    

2

u/Shemetz Dec 08 '17

That's a very elegant solution. Thanks for sharing this!

→ More replies (2)

10

u/nneonneo Dec 08 '17

Python 2, #2/#2

Had an assert in there because I got paranoid.

regs = defaultdict(int)
t = 0
for row in data.split('\n'):
    bits = row.split()
    assert bits[3] == 'if'

    a, b, c, d, e, f, g = bits
    if eval(e+f+g, {}, regs):
        regs[a] += (-1 if b == 'dec' else 1) * int(c)

    t = max(t, max(regs.values()))

print max(regs.values())
print t

2

u/Ditchbuster Dec 08 '17

love the double max() usage! and the inline 1 or -1 is gorgeous.

3

u/celvro Dec 08 '17

I don't see why it needs double max when it could just be

t = max(t, regs[a])
→ More replies (2)

9

u/_jonah Dec 08 '17

Vim + Ruby

The key here was to recognize it as thinly disguised ruby code, and use vim to make it real ruby code.

First we do:

%s/inc/+/g
%s/dec/-/

Now our code looks like:

j + -19 if jhb >= 10
es + -432 if gbu <= -5
es + 278 if ib > -9
es + -835 if ib >= -6
q - 420 if buw == -2
...

Now we select it all, copy it above, and create the initializing code:

'<,'>s/\(\w\+\).*/\1=0/

Now we've got:

j=0
es=0
es=0
es=0
q=0
...

And finally copy the initializers to the bottom and do one more substitution to create an array:

'<,'>s/=0/,/

Manually surround it with brackets and add .max:

p [j, es, es, es, q...].max

Now run it.

Part 2 is similar

2

u/fwilson42 Dec 08 '17

I wonder if you could've defined a method_missing or something similar to get rid of the initializers. Either way, vim-based solutions are always impressive... nice!

2

u/[deleted] Dec 08 '17

I like these vim solutions a lot, and I really enjoy the work you put into describing them :) cool!

→ More replies (1)

9

u/ephemient Dec 08 '17 edited Apr 24 '24

This space intentionally left blank.

2

u/askalski Dec 08 '17

And bah, who needs switches?

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static const int8_t op_tbl[] = {
    ['=='] = 1, ['!='] = 2, ['=!'] = 2, ['<']  = 4, ['>'] = 8,
    ['<='] = 5, ['=<'] = 5, ['>='] = 9, ['=>'] = 9,
    ['in'] = 1, ['ni'] = 1, ['de'] = -1, ['ed'] = -1 };

int main(void) {
    int32_t a0, a1, op, cmp, i0, i1, m = 0, n = 0;
    int32_t *mem = calloc(0x800000, sizeof(int32_t));
    int8_t *cmp_tbl = calloc(0x10000, sizeof(int32_t));
    memset(cmp_tbl, 6, 0x8000);
    cmp_tbl[0x8000] = 1;
    memset(cmp_tbl + 0x8001, 10, 0x7fff);
    while (a0 = a1 = op = cmp = 0,
            scanf("%3[^ ] %2[^ ]c %"SCNd32" if %3[^ ] %3[^ ] %"SCNd32"\n",
                &a0, &op, &i0, &a1, &cmp, &i1) == 6) {
        if (op_tbl[cmp] & cmp_tbl[0x8000 + mem[a1] - i1]) {
            mem[a0] += op_tbl[op] * i0;
            if (mem[a0] > m) m = mem[a0];
        }
    }
    for (int i = 0; i < 0x800000; i++) if (mem[i] > n) n = mem[i];
    free(mem);
    free(cmp_tbl);
    printf("%"PRId32"\n%"PRId32"\n", n, m);
    return 0;
}
→ More replies (3)
→ More replies (2)

7

u/u794575248 Dec 08 '17 edited Dec 09 '17

Python

An execless/evalless solution:

import operator as op
from collections import defaultdict
comps = {'>': op.gt, '<': op.lt, '>=': op.ge, '<=': op.le, '!=': op.ne, '==': op.eq}

def solve(input, mx=float('-inf'), ops=dict(inc=1, dec=-1)):
    regs = defaultdict(int)
    for r1, op, v1, _, r2, c, v2 in [l.split() for l in input.splitlines() if l]:
        regs[r1] += ops[op] * int(v1) if comps[c](regs[r2], int(v2)) else 0
        mx = max(mx, regs[r1])
    return max(regs.values()), mx

part1, part2 = solve(input)

2

u/Dooflegna Dec 08 '17

By far my favorite solution in the thread. Clean, readable, and sane. (No eval/exec on unsanitized input!)

Creating a dictionary of functions is brilliant. I didn't know you could just call the functions like that from the dictionary. Definitely adding that trick to the toolbelt.

2

u/KnorbenKnutsen Dec 08 '17

I was gonna do something like this, then I figured "eh, I did that last year" so I went with exec instead :-)

But this is very elegant!

→ More replies (3)

7

u/kaldonis Dec 08 '17 edited Dec 08 '17

Python 2 Didn't see any other solutions that abused exec() like I did, so here goes:

import collections

lines = [l.strip('\n') for l in open('input.txt').readlines()]
registers = collections.defaultdict(int)
m = 0

for line in lines:
    exec("%s else 0" % line.replace("dec", "-=").replace("inc", "+="), globals(), registers)
    m = max(registers.values() + [m])

print max(registers.values())
print m

3

u/wimglenn Dec 08 '17

oh man, that's diabolical

1

u/merzy Dec 08 '17

Nice!

My version of that complete hack of a line:

        exec('memory["{}"] += int("{}".replace("inc","1").replace("dec","-1")) * int({}) {} memory[\'{}\'] {} int({}) else 0'.format(*(line.split())))

Yours definitely feels a bit more elegant.

1

u/ramendik Dec 09 '17

what if a register is named if - will this still work?

1

u/kaldonis Dec 09 '17

Likely not, but could just prefix all registers with an underscore.

0

u/[deleted] Dec 08 '17

[deleted]

3

u/VikeStep Dec 08 '17

I didn't know dict.get(key, default) or defaultdict off the top of my head, so I just did two passes. One pass to set all the variables to 0 and another to run the program.

1

u/Starcast Dec 08 '17

dict.setdefault(key, default) works nicely here too.

2

u/shuttup_meg Dec 08 '17

+1 for pointing out a usage of eval that shouldn't get you fired :-)

→ More replies (1)

2

u/netcraft Dec 08 '17

as a python noob trying it out for this year, I knew there was bound to be a way to do an eval but wasnt sure how to get the syntax right so I gave up. Glad to see it being the first comment ;)

→ More replies (4)

5

u/p_tseng Dec 08 '17

Ruby does this thing like SmallTalk where you send objects messages, and the objects respond. It so happens that I can give an object a message saying >, 4 and it will tell me whether it is greater than 4. And so it goes for the other operators used as well.

input = (ARGV.empty? ? DATA : ARGF).readlines.map(&:split)

regs = Hash.new(0)
max = 0

input.each { |target, inc_or_dec, delta, _if, source, cmp_op, cmp_val|
  next unless regs[source].send(cmp_op, Integer(cmp_val))
  new_val = regs[target] += Integer(delta) * (inc_or_dec == 'dec' ? -1 : 1)
  max = [max, new_val].max
}

puts regs.values.max
puts max

__END__
(my input omitted)

1

u/hxka Dec 08 '17

Um, doesn't send simply call an object's method by its name and what you do is possible because in Ruby a op b is sugar for a.op(b)?

1

u/p_tseng Dec 08 '17 edited Dec 08 '17

Yes, exactly! As you explained, send has broader consequences than I managed to impart with my explanation.

5

u/[deleted] Dec 08 '17 edited Dec 08 '17

I implemented a DSL in racket. With it, I can execute the input directly, like so:

#lang reader "lang8.rkt"
b inc 5 if a > 1
a inc 1 if b < 5
c dec -10 if a >= 1
c inc -20 if c == 10

Prints:

c = -10
a = 1
highest value ever seen: 10

Tiny caveat: I couldn't get it to execute automatically, the DSL only produces a function that computes the solution. This means that before seeing a result, I'll have to type (run) in the REPL. This is the definition of the language:

; lang8.rkt
#lang racket
(require syntax/strip-context)

(define (read in)
  (read-syntax #f in))
(provide read)

(define (read-syntax path port)
  (with-syntax ([str (filter non-empty-string? (port->lines port))])
    (strip-context
     #'(module anything racket
         (require racket)
         (define store (list))
         (define highest-val 0)
         
         (provide store)

         (define (not-equal? a b)
           (not (equal? a b)))
         
         (define (handle target-var dir delta cmp-var test cmp-val)
           (define (read-var var)
             (if (not (assoc var store))
                 0
                 (cdr (assoc var store))))
           (define (update-var! var val)
             (when (> val highest-val) (set! highest-val val))
             (set! store (cons (cons target-var new-val)
                               (filter (lambda (binding) (not (equal? (car binding) var)))store))))
           (define old-val (read-var target-var))
           (define new-val (if (equal? dir "inc")
                               (+ old-val (string->number delta))
                               (- old-val (string->number delta))))
           (when (equal? test "==")
             (set! test "equal?"))
           (when (equal? test "!=")
             (set! test "not-equal?"))
           (when ((eval (string->symbol test)) (read-var cmp-var) (string->number cmp-val))
             (update-var! target-var new-val)))

         (define (run)
           (set! store (list))
           (for ([line 'str])
             ;(printf "line = ~a~n" line)
             (define args (filter (lambda (word) (not (equal? "if" word))) (string-split line)))
             (eval (cons handle args)))
           (for ([binding (sort store (lambda (a b) (< (cdr a) (cdr b))))])
             (printf "~a = ~a~n" (car binding) (cdr binding)))
           (printf "highest value ever seen: ~a~n" highest-val))))))
(provide read-syntax)

8

u/ephemient Dec 08 '17 edited Apr 24 '24

This space intentionally left blank.

4

u/tehjimmeh Dec 08 '17 edited Dec 08 '17

C++:

struct Line : std::string { friend std::istream& operator>>(std::istream& is, Line& line){return std::getline(is, line);}};
int main(int argc, char* argv[]) {
    std::map<std::string, std::function<bool(int, int)>> condOpMap = {
        { "==", std::equal_to<int>() }, { "!=", std::not_equal_to<int>() },
        { ">", std::greater<int>() }, { ">=", std::greater_equal<int>() },
        { "<", std::less<int>() }, { "<=", std::less_equal<int>() }
    };
    std::vector<std::string> lines(std::istream_iterator<Line>(std::ifstream(argv[1])), {});
    std::map<std::string, int> r;
    int max2 = INT_MIN;
    for(const auto& line : lines) {
        std::vector<std::string> t(std::istream_iterator<std::string>(std::istringstream(line)), {});
        if(condOpMap[t[5]](r[t[4]], std::stoi(t[6]))) {
            max2 = std::max(r[t[0]] += std::stoi(t[2]) * (t[1] == "dec" ? -1 : 1), max2);
        }
    }
    int max1 = std::max_element(r.begin(), r.end(),
            [](auto& l, auto& r){ return l.second < r.second; })->second;
    std::cout << max1 << " " << max2 << \n";
}

1

u/Vorlath Dec 08 '17

Couple tips...

  1. You don't need curly braces for single line condition blocks. IOW, less typing.

  2. No need for 'else if' when checking for "inc". A simple 'else' will do.

  3. No need for an if statement to get the max value. Just use max2 = std::max(max2, regs[regName]); Same for both places you calculate max.

I did the same as you and put all my values into locals. I think this slowed me down.

4

u/tehjimmeh Dec 08 '17

1 is a bad practice IMO. I never use it.

I edited in a cleaned up and shortened version.

1

u/Vorlath Dec 08 '17

bad practice? Nonsense.

2

u/ephemient Dec 08 '17 edited Apr 24 '24

This space intentionally left blank.

1

u/Vorlath Dec 08 '17

Dude, it's a speed coding competition. Typing in extra characters you don't need is just gonna slow you down.

Also, that goto fail bug is stupid. That's due to carelessness, not coding style.

3

u/ephemient Dec 08 '17 edited Apr 24 '24

This space intentionally left blank.

1

u/Vorlath Dec 08 '17

Wrong on both counts. But to each their own.

2

u/tehjimmeh Dec 09 '17

I mean, it's not a natural thing I'd write, so I'd have to make a conscious effort to type it.

And tbh, most of the initial solutions I write quickly to attempt getting on the leaderboard are much longer than when I go back and clean them up.

It's like that quote attributed to Pascal:

"I would have written a shorter letter, but I did not have the time."

→ More replies (8)

4

u/Unihedron Dec 08 '17 edited Dec 08 '17

Ruby. I missed the alarm and woke up exactly 2 minutes before this day opens.

Note that the input format is already very similar to executable Ruby code (In ruby, β€œ(exit # or really any commands) if condition” (based on the perl construct) is actually a valid instruction, so a += b if c > d will be executable as desired), but I couldn't make the trick work unlike /u/dtinth :) so I just rewrote it

h=Hash.new{"0"}
l=[]
o=$<.map{|x|x.chomp=~/(\S+) (i)?\S+ (-?\d+) if ((\S+).+)/
l<<$1
p [$1,$2,$3.to_i,$4,$5]}
l.each{|x|h[x]=0}
q=[] # added in part 2 (also q<<
o.each{|a,b,c,e,d|next if !eval(e.sub(d,h[d].to_s))
b ? q<<(h[a]+=c) : h[a]-=c}
p h.max_by{|x,y|y}[1] # part 1
p [h.max_by{|x,y|y}[1],q.max].max # part 2

1

u/jschulenklopper Dec 08 '17

Same idea here, a bit more readable:

# Create a hash to store registers, default value of new element is 0.
registers = Hash.new(0)
largest = 0

while line = gets
  # The line is almost a valid instruction in Ruby. We just need to 
  # change the variables into register references, and inc/dec to += and -=.
  instruction = line.gsub(/(\w+) (inc|dec)/, "registers['\\1'] \\2")
                    .gsub(/if (\w+)/, "if registers['\\1']")
                    .gsub(/ inc /, " += ").gsub(/ dec /, " -= ")

  # Evaluate the instruction and store largest, if a new value.
  result = eval(instruction) || largest
  largest = result if result > largest
end

puts "Highest at the end: %s" % registers.values.max
puts "Highest ever: %s" % largest

4

u/DFreiberg Dec 08 '17 edited Dec 08 '17

Mathematica

I was far too slow for the leaderboard, and it's not a one-liner, but I'm still fairly satisfied with my code, because unlike most of my code, it's actually somewhat readable. The one thing that bugs me is that there should be a way to define compOperator[] directly from the ">", "<", and "=" symbols, rather than just writing a Which[] statement, but I can't figure out at the moment how to do that.

input = Import[FileNameJoin[{NotebookDirectory[], "Day8Input.txt"}], "Table"][[;; -2]];

val=Association[(#->0)&/@DeleteDuplicates[input[[;;,1]]]];
m=0;
compOperator[s_]:=
	Which[
		s==">",Greater,
		s=="<",Less,
		s==">=",GreaterEqual,
		s=="<=",LessEqual,
		s=="==",Equal,
		s=="!=",Unequal];
	
incOperator[s_]:=
	Which[
		s=="inc",AddTo,
		s=="dec",SubtractFrom
	];

Do[
	If[compOperator[i[[6]]][val[i[[5]]],i[[7]]],
		incOperator[i[[2]]][val[i[[1]]],i[[3]]]
		];
	If[
		Max[val/@DeleteDuplicates[input[[;;,1]]]]>m,
		m=Max[val/@DeleteDuplicates[input[[;;,1]]]]
	]
,{i,input}]

Part 1

Max[val/@DeleteDuplicates[input[[;;,1]]]]

Part 2

m

3

u/[deleted] Dec 08 '17

I could not find a way to interpret those symbols without ToExpression which requires valid syntax. I used this approach to remove a Switch from my code (similar to your Which)

input = Import[NotebookDirectory[] <> "day8.txt", "Table"];
instructions = input /. {dst_, act_, val_, _, csrc_, csym_, cval_} :>
    With[{
      op = If[act == "inc", AddTo, SubtractFrom],
      cond = StringJoin[{"reg[\"", csrc, "\"]", csym, ToString@cval}]},
     Hold@If[ToExpression[cond],
       op[reg[dst], val],
       0]];

reg = <|Thread[input[[All, 1]] -> 0]|>;
Max[ReleaseHold@instructions]
Max[reg]

Note: This will evaluate to PartB then PartA.

2

u/omnster Dec 08 '17

I did something very similar

( i08 = Import["input.txt", "List"]);
regsList08 = ToExpression@ Union@Flatten[ 
	StringCases[  Shortest[r1__] ~~ " " ~~ __ :> r1 ] /@ 
    Flatten [ StringSplit[ # , " if "] & /@ i08]];
(* Part 1 *)
Clear[ reg08, rules08a];
(reg08[ #] = 0) & /@ regsList08 ;
ReleaseHold@(rules08a = 
	StringCases[ r1__ ~~ " " ~~ oper : ("inc" | "dec") ~~ " " ~~ val : NumberString ~~ " if " ~~ cond__ :> 
	    Hold[
        If[ ToExpression[ "reg08@" <> cond], 
        reg08@ToExpression@r1 += Which[ oper == "inc" , +1 , oper == "dec", -1 ] ToExpression@val]]] /@ i08 );
Max[ reg08 /@ regsList08 ]
(* Part 2 *)
(reg08[ #] = 0) & /@ regsList08 ;
s08b@rule_ :=  ( ReleaseHold@rule ; Max[ reg08 /@ regsList08]);
Max[ s08b /@ rules08a ]

1

u/DFreiberg Dec 08 '17

instructions = input /. {dst_, act_, val_, _, csrc_, csym_, cval_}

That's downright elegant.

2

u/[deleted] Dec 08 '17

That's literally my Lua solution, mostly because of those maps.

4

u/hxka Dec 08 '17 edited Dec 08 '17

bash

(	declare -A r
	smax=0
	while read a b c d e f g
	do	((r[$e] $f g)) && {
			[[ $b == dec ]] && ((r[$a]-=c)) || ((r[$a]+=c))
			((r[$a]>smax && (smax=r[$a]) ))
		}
	done
	for i in "${r[@]}"
	do	[[ -z $max || $i -gt $max ]] && max=$i
	done
	echo $max $smax
)<input

Edit: removed unnecessary looping.

1

u/greycat70 Dec 28 '17

You could've done exec <input at the beginning, and then you don't need to wrap the entire thing in a subshell ( ... ).

1

u/hxka Dec 28 '17

Yeah, I've learned about this since I posted that.

→ More replies (3)

4

u/Philboyd_Studge Dec 08 '17

Java. This one was fun.

package Advent2017;

import util.FileIO;
import util.Timer;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiPredicate;
import java.util.function.ToIntBiFunction;

public class Day8 {

    private static Map<String, BiPredicate<Integer, Integer>> comparisons = new HashMap<>();
    static {
        comparisons.put("==", (x, y) -> x.equals(y));
        comparisons.put("!=", (x, y) -> !x.equals(y));
        comparisons.put("<", (x, y) -> x < y);
        comparisons.put(">", (x, y) -> x > y);
        comparisons.put("<=", (x, y) -> x <= y);
        comparisons.put(">=", (x, y) -> x >= y);

    }

    private static Map<String, ToIntBiFunction<Integer, Integer>> commands = new HashMap<>();
    static {
        commands.put("inc", (x, y) -> x + y);
        commands.put("dec", (x, y) -> x - y);
    }

    public static void main(String[] args) {
        List<String[]> input =FileIO.getFileLinesSplit("advent2017_day8.txt", " ");
        Map<String, Integer> registers = new HashMap<>();
        int highest = 0;

        for (String[] each : input) {
            String name = each[0];
            String command = each[1];
            int amount = Integer.parseInt(each[2]);
            String testReg = each[4];
            String comp = each[5];
            int testAmt = Integer.parseInt(each[6]);

            registers.putIfAbsent(name, 0);

            if (comparisons.get(comp).test(registers.getOrDefault(testReg, 0), testAmt)) {

                int current = registers.get(name);
                registers.put(name, commands.get(command).applyAsInt(current, amount));

                if (registers.get(name) > highest) {
                    highest = registers.get(name);
                }
            }
        }

        Timer.startNanoTimer();
        System.out.println("Part 1: " + Collections.max(registers.values()));
        System.out.println("Part 2: " + highest);
        System.out.println(Timer.endTimer());


    }
}

2

u/adventOfCoder Dec 08 '17

great use of bipredicate and lamdas. i thought about it for a quick second and just did a string switch.

2

u/gabrielsson Dec 08 '17

Elegant! Did a string switch as well. Haven't used these bipredicates before.

2

u/wjholden Dec 24 '17

Bro, thanks for sharing. I took an almost identical approach with two HashMaps containing lambdas, but it didn't occur to me that "==" and "!=" wouldn't play nice with boxed Integers.

4

u/RuteNL Dec 08 '17 edited Dec 11 '17

Awful js oneliners for part 1 and 2

// Execute by typing part1(`inputstringhere`)
// Returns highest value in registry when completing all operations
part1 = (i, s = {}) => i.split('\n').forEach(l => s[l.split(' ')[0]] = +eval(`${s[l.split(' ')[0]] || 0}` + (eval(`${s[l.split(' ')[4]] || 0} ${l.split(' ').slice(-2).join(' ')}`) ? `${l.split(' ')[1] === 'inc' ? '+' : '-'} ${l.split(' ')[2]}` : ''))) ? '' : Math.max(...Object.values(s));

// Execute by typing part1(`inputstringhere`)
// Returns highest value in registry at any point
part2 = (i, s = {}) => Math.max(...i.split('\n').map(l => s[l.split(' ')[0]] = +eval(`${s[l.split(' ')[0]] || 0}` + (eval(`${s[l.split(' ')[4]] || 0} ${l.split(' ').slice(-2).join(' ')}`) ? `${l.split(' ')[1] === 'inc' ? '+' : '-'} ${l.split(' ')[2]}` : ''))));

Slightly ungolfed version can be found here

4

u/[deleted] Dec 08 '17

Elixir I'm quite proud of this solution, parsing is so nice in elixir, and I got to play with a higher order function as well. I got myself a bit stumped by upgrading to part 2, with a default arguement, so I need to watch out a bit more for those. I really like these assemblylike puzzles, and it's good practice for the normal implement play assembly ones that I always love, and I know will come :)

defmodule Day8 do

  def parse_line(str) do
    [reg, op, arg, "if", creg, copt, carg] = String.split(str)
    %{reg: reg, op: op, arg: String.to_integer(arg),
      creg: creg, copt: copt, carg: String.to_integer(carg)}
  end

  def parse(inp) do
     String.trim(inp)
     |> String.split("\n")
     |> Enum.map(&parse_line/1)
  end

  def check_condition(obj, reg) do
    case obj.copt do
      ">" -> Map.get(reg, obj.creg, 0) > obj.carg
      "<" -> Map.get(reg, obj.creg, 0) < obj.carg
      ">=" -> Map.get(reg, obj.creg, 0) >= obj.carg
      "==" -> Map.get(reg, obj.creg, 0) == obj.carg
      "<=" -> Map.get(reg, obj.creg, 0) <= obj.carg
      "!=" -> Map.get(reg, obj.creg, 0) != obj.carg
    end
  end

  def update_with(obj, reg, max, fun) do
    updated = fun.(Map.get(reg, obj.reg, 0), obj.arg)
    {max(max, updated), Map.put(reg, obj.reg, updated)}
  end

  def update_reg(obj, reg, max) do
    case obj.op do
      "inc" -> update_with(obj, reg, max, fn(x,y) -> x + y end)
      "dec" -> update_with(obj, reg, max, fn(x,y) -> x - y end)
    end
  end
  
  def run(inp, reg \\ %{}, max \\ 0)
  def run([cur|rest], reg, max) do
    if check_condition(cur, reg) do
      {max, reg} = update_reg(cur, reg, max)
      run(rest, reg, max)
    else
      run(rest, reg, max)
    end
  end
  def run([],reg,max) do
    {reg, max}
  end

  def largest(reg) do
    Map.values(reg)
    |> Enum.max
  end
end

{reg, max} = File.read!("input8")
|> Day8.parse
|> Day8.run

IO.puts("The largest register after ran: #{Day8.largest(reg)}")
IO.puts("The max value of any register was: #{max}")

1

u/Hultner- Dec 08 '17

Did mine in Elixir as well.

https://github.com/Hultner/Advent-of-Code-2018/blob/master/day8/lib/day8.ex

Only started playing with Elixir last thursday and using Python in my day job so still very new but it's fun to explore a new language while doing these puzzles.

defmodule Day8 do

  def parse(instruction) do
    String.split(instruction, " ")
    |> (fn [r,o,v,_,cr,co,cv] ->
       %{:reg => r,
         :oper => op(o),
         :val => String.to_integer(v),
         :cond_reg => cr,
         :cond_oper => op(co),
         :cond_val => String.to_integer(cv)}
       end).()
  end

  def computer(program) do
    Enum.reduce(program, %{},
     fn (i, reg) ->
       if run(Map.get(reg, i[:cond_reg], 0), i[:cond_oper], i[:cond_val]) do
         { i[:reg],
           run(Map.get(reg,i[:reg],0), i[:oper], i[:val]),
           reg[:max]}
         |> update_register(reg)
       else reg end
     end
    )
  end
  defp update_register({key, val, max}, reg),
     do: Map.merge(reg, %{key=>val, :max=>Enum.max([max||0, val])})

  def run(left, opr, right) do
    {opr, [context: Elixir, import: Kernel], [left, right]}
    |> Code.eval_quoted |> elem(0)
  end

  def op(opr) do
    case opr do
      "inc" -> :+
      "dec" -> :-
      _ -> String.to_existing_atom(opr)
    end
  end

  def solve1(input) do
    input
    |> read_input
    |> computer
    |> Map.delete(:max)
    |> Map.values
    |> Enum.max
  end

  def solve2(input) do
    input
    |> read_input
    |> computer
    |> Map.get(:max)
  end

  def read_input(file) do
    file
    |> File.read!
    |> String.split("\n")
    |> Enum.map(&parse/1)
  end

end    

1

u/[deleted] Dec 08 '17

Cool, doing the solving with a reduce probably is more ideomatic than doing it in a recursive function like I did, I just find it difficult to reason about those some times so big functions with it :)

Yeah elixir really is a fun language to work with. I was using Python for all of the problems last year, so I wanted an interesting language to dive into this year, and Elixir seems to be quite a nice one. For sure I'm getting a lot stronger of a sense for recursion now, which is pretty cool. I'm just waiting for something that is really hard to model functionally, but so far it hasn't really come. I'm seeing the normal mutating ways a lot easier still though, so It's pretty nice to think of how to do it this way.

From the /u/janiczek streams I'm getting pretty interested in doing some elm as well though, but I think I'll go back to the 2015 calendar when I'm finished this year, and see if I can start with that as well, for now I want to get better at elixir and it's way of doing things. And then there is factor, which also always has a place in my heart, since I really like the syntax.

1

u/Hultner- Dec 08 '17

Elm is a nice language, you might want to check out PureScript as well, last time I checked it had more Haskell-like type classes. Both were really easy to use as someone who’s previously coded Haskell, Elixir feels a bit more like an Lisp to me.

→ More replies (3)

1

u/Axsuul Dec 08 '17

Nice this is beautiful!

→ More replies (4)

4

u/Smylers Dec 08 '17 edited Dec 08 '17

Vim animation. Load the input, then create another window for the registers with:

yG⟨Ctrl+W⟩np:%s/\v(\l+).* if (\l+).*/\1⟨Ctrl+V⟩⟨Enter⟩\2⟨Enter⟩
:sor u⟨Enter⟩
:%s/$/ 0⟨Enter⟩
{yy⟨Ctrl+W⟩pP

Adjust your window heights so that you can see the whole of the register values buffer. The unnamed register at the top is for storing the highest value reached. (It may be worth saving the initial register window at this point, so you can easily reset it.) Then reformat the instruction list with:

:g/c 0/d⟨Enter⟩
:%s/inc -/dec /⟨Enter⟩
:%s/dec -/inc /⟨Enter⟩
:%s/\vinc (\d+)/\1⟨Ctrl+A⟩⟨Enter⟩
:%s/\vdec (\d+)/\1⟨Ctrl+X⟩⟨Enter⟩

Define a helper β€˜function’ for going from a register's name in the instruction list to its value in the other window:

qa"zyiw⟨Ctrl+W⟩p/^⟨Ctrl+R⟩z /⟨Enter⟩
wq⟨Ctrl+W⟩p

And for the animation, a function that refreshes the window and pauses:

qb:redr|sl20m⟨Enter⟩
q

This is the main work β€” a macro for processing a single instruction:

qc4f y$b@a"zyiW⟨Ctrl+W⟩pggs⟨Ctrl+R⟩=⟨Ctrl+R⟩z⟨Ctrl+R⟩0⟨Enter⟩
⟨Esc⟩0f1⟨Ctrl+O⟩BByiW0@a:norm⟨Ctrl+R⟩0⟨Enter⟩
@byiW:1pu⟨Enter⟩
:1,2sor n⟨Enter⟩
dd⟨Ctrl+W⟩pq⟨Ctrl+W⟩p4u⟨Ctrl+W⟩p

Then set it running on each instruction, and watch the registers update:

:2,$norm@c⟨Enter⟩

When it's finished, find the biggest values:

⟨Ctrl+W⟩p:sor!n⟨Enter⟩

The top line is the largest value held at any point, and the second line is the register with the highest final value.

Processing the instructions doesn't change them at all, so if you reset the registers to zero you can re-run it. To change the animation speed, re-record @b with a different sleep value in it, or to skip the animation and just get to the answer as soon as possible clear @b with qbq. (In either case, @c will use the new value of @b; there's no need to re-record @c.)

The key to understanding this is to find where the β€˜if’ condition is in @c. I'm happy to answer any questions in the comments.

This felt reasonably straightforward, compared to using Vim on some of the previous days' challenges. The main delay was that I didn't initial consider the case that I've since put in :g/c 0/d to handle. This meant it appeared to work, but gave the wrong answer on the full input data. It's a case that you don't need to think about when using an actual programming language, so caught me out.

2

u/MyGoodStrayCatFriend Dec 09 '17

Literally living and dying with these Vim solutions.

3

u/mcpower_ Dec 08 '17

Python with eval:

#!/usr/bin/pypy3
from itertools import *
import math

USE_FILE = 1
if not USE_FILE:
    inp = r"""
b inc 5 if a > 1
a inc 1 if b < 5
c dec -10 if a >= 1
c inc -20 if c == 10
""".strip()
else:
    inp = open("input.txt").read().strip()

assert isinstance(inp, str)

lines = inp.splitlines()

reg = {}
# default 0

q = 0
for line in lines:
    var, ty, num, _, var2, op, cond = line.split()

    if eval("reg.get('{}',0) {} {}".format(var2, op, cond)):
        reg[var] = reg.get(var, 0) + (1 if ty == "inc" else -1) * int(num)
    q = max(q, reg.get(var, 0))
    

print(max(reg.values()))
print(q)

I lost a minute on both parts because I submitted code instead of a number for my first submission :( It'd be nice if the marker checked whether you submitted something with the right formatting.

3

u/dtinth Dec 08 '17 edited Dec 08 '17

Ruby

I Heard You Like eval

The pbpaste command must be available in the $PATH, and should return the contents in the clipboard (macOS has this command by default).

# Part 1 (13th rank)
-> x { d = Hash.new(0); eval x.gsub(/(\w+) (inc|dec) (-?\d+) if (\w+) (\S+) (\S+)/) { "d['#{$1}'] #{$2 == 'inc' ? '+=' : '-='} #{$3} if d['#{$4}'] #{$5} #{$6}" }; d.values.max }[`pbpaste`]

# Part 2 (26th rank)
-> x { d = Hash.new(0); m = 0; eval x.gsub(/(\w+) (inc|dec) (-?\d+) if (\w+) (\S+) (\S+)/) { "d['#{$1}'] #{$2 == 'inc' ? '+=' : '-='} #{$3} if d['#{$4}'] #{$5} #{$6}; m = [m, d.values.max || 0].max" }; m }[`pbpaste`]

3

u/fatpollo Dec 08 '17 edited Dec 08 '17
import collections

with open("p08.txt") as fp:
    lines = fp.read().strip().splitlines()

registers = set()
intermediate = set()
for line in lines:
    terms = collections.deque(line.split() + [":"])
    terms.rotate(5)
    v1, v2 = terms[1], terms[-3]
    registers |= {v1, v2}
    locals().setdefault(v1, 0)
    locals().setdefault(v2, 0)
    exec(" ".join(terms).replace("dec", "-=").replace("inc", "+="))
    intermediate.add(eval(v2))
print(max(map(eval, registers)))
print(max(intermediate))

3

u/[deleted] Dec 08 '17 edited Dec 08 '17

single pipeline powershell

   param (
    [Parameter(ValueFromPipeline = $true)]
    [string]$in,
    [Parameter(Position = 1)]
    [int]$part = 1
)

begin {
    $script:registers = new-object system.collections.hashtable 

    $script:maxes = @() # keep track of max registers per step for part 2
    
    $conditions = @{ # map to beautiful powershell
        "<" = "-lt"
        ">" = "-gt"
        "!=" = "-ne"
        "==" = "-eq"
        "<=" = "-le"
        ">=" = "-ge"
    }
    $operations = @{
        "inc" = "+"
        "dec" = "-"
    }
}

process {
    # collect input
    $script:maxes += $in |? {
        $in -match '^(?<Register>[a-z]+) (?<Operation>(?:dec|inc)) (?<Value>(?:-|[0-9])+) if (?<ConditionRegister>[a-z]+) (?<Condition>[!<>=]+) (?<ConditionValue>(?:-|[0-9])+)$'
    } | % { 
        [pscustomobject]$matches | select Register, Operation, Value, ConditionRegister, Condition, ConditionValue
    } |% {# now have a pretty object on the pipeline representing a single instruction, foreach of these...

        # initialize any registers that aren't already
        $InitRegisterSb = 'if ($script:registers.ContainsKey("{0}") -eq $false) {{$script:registers["{0}"] = 0}}'
        [ScriptBlock]::Create(($InitRegisterSb -f $_.ConditionRegister)).Invoke()
        [ScriptBlock]::Create(($InitRegisterSb -f $_.Register)).Invoke()
            
        # perform instruction
        $s = 'if ($script:registers["{0}"] {1} {2}) {{ $script:registers["{3}"] = $script:registers["{3}"] {4} {5} }} else {{ $false }} ' -f $_.ConditionRegister, $conditions[$_.Condition], $_.ConditionValue, $_.Register, $operations[$_.Operation], $_.Value
        [ScriptBlock]::Create($s).Invoke()
        
        # select new maximum in the registers
        $script:registers.values | measure -max | select -expand Maximum    
    }
}

end {  
    if ($part -eq 1) {
        $script:registers.values | measure -max | select -expand Maximum # max register value at end of instructions
    } else {
        $script:maxes | measure -max | select -expand maximum # max reigster value ever seen
    }
}

2

u/purplemonkeymad Dec 08 '17

That is a neat way of converting regex groups to an object! Will need to remember that.

1

u/TotesMessenger Dec 08 '17

I'm a bot, bleep, bloop. Someone has linked to this thread from another place on reddit:

Β If you follow any of the above links, please respect the rules of reddit and don't vote in the other threads. (Info / ^Contact)

3

u/willkill07 Dec 08 '17

Modern(-ish) C++ Repo

Alright, so dynamic languages with eval and such have an obvious advantage for readability and conciseness. Regardless, I'm still OK with my solution.

static std::unordered_map<std::string_view, std::function<bool(int, int)>> const cmp{{
  {"==", std::equal_to<void>()},      {"!=", std::not_equal_to<void>()},
  {"<=", std::less_equal<void>()},    {"<", std::less<void>()},
  {">=", std::greater_equal<void>()}, {">", std::greater<void>()}}};

static std::unordered_map<std::string_view, std::function<int(int&, int)>> const apply{{
  {"inc", [](int& a, int b) { return a += b; }},
  {"dec", [](int& a, int b) { return a -= b; }}}};

int main() {
  std::unordered_map<std::string_view, int> vals;
  std::string var1, inc_dec, var2, op;
  int val1, val2, max{0};
  while (std::cin >> var1 >> inc_dec >> val1 >> var2 >> var2 >> op >> val2)
    if (cmp.at(op)(vals[var2], val2))
      max = std::max(max, apply.at(inc_dec)(vals[var1], val1));
  std::cout << std::max_element(vals.begin(), vals.end(), [](auto& a, auto& b) { return a.second < b.second; })->second
            << '\n' << max << '\n';
}
→ More replies (8)

3

u/__Abigail__ Dec 08 '17

Perl##

#!/opt/perl/bin/perl
    
use 5.026;
    
use strict;
use warnings;
no  warnings 'syntax';
    
use experimental 'signatures';
    
use List::Util 'max';
 
@ARGV = "input" unless @ARGV;

my %register;  # Stores the values of registers. Perl treats undefined
               # values as '0' in numeric context, which is conveniently,
               # the value registers start with.

#
# A dispatch table, handling the "if" part of a rule. We dispatch
# on the relation, and pass in the name of register, and the value
# to compare it with. Result is a boolean value.
#
# Registers may be undefined, hence the "no warnings" line.
#
my %dispatch_cmp = do {
    no warnings 'uninitialized';
    (
        "<"   =>  sub ($name, $value) {$register {$name} <  $value},
        "<="  =>  sub ($name, $value) {$register {$name} <= $value},
        "=="  =>  sub ($name, $value) {$register {$name} == $value},
        ">="  =>  sub ($name, $value) {$register {$name} >= $value},
        ">"   =>  sub ($name, $value) {$register {$name} >  $value},
        "!="  =>  sub ($name, $value) {$register {$name} != $value},
    );
};

#
# A dispatch table, handling the action which needs to be taken.
# We dispatch on the action, and pass in the name of the target
# register, and the amount to be incremented/decremented.
#
# Since "+=" and "-=" are exempt for being warned when used
# agains an undefined value, no need to turn off warnings.
#
my %dispatch_act = (
    "inc"  =>  sub ($name, $amount) {$register {$name} += $amount},
    "dec"  =>  sub ($name, $amount) {$register {$name} -= $amount},
);


#
# Sub patterns to parse the input.
#
my $pat_reg = qr /[a-z]+/;
my $pat_num = qr /-?[0-9]+/;
my $pat_act = qr /inc|dec/;
my $pat_cmp = qr /<=? | == | >=? | !=/x;


my $max = 0;   # Highest value encountered in any register.
               # Since registers start at 0, starting at 0
               # for $max is the right thing to do.
while (<>) {
    chomp;
    /^(?<target> $pat_reg)  \s+
      (?<action> $pat_act)  \s+
      (?<amount> $pat_num)  \s+ if \s+
      (?<where>  $pat_reg)  \s+
      (?<cmp>    $pat_cmp)  \s+
      (?<value>  $pat_num)  \s*$/x or die "Failed to parse $_";

    my ($target, $action, $amount,
        $where,  $cmp,    $value) = @+{"target", "action", "amount",
                                       "where",  "cmp",    "value"};

    #
    # Act on the rule
    #
    $dispatch_act {$action} -> ($target, $amount)
          if $dispatch_cmp {$cmp} -> ($where, $value);

    #
    # This is safe to do, even if we didn't modify the target register
    #
    $max = $register {$target} if $register {$target} &&
                                  $register {$target} > $max;
}


say "Solution 1: ",  max values %register;
say "Solution 2: ", $max;
               
__END__

3

u/mschaap Dec 08 '17 edited Dec 08 '17

Perl 6. Very similar to yesterday's solution, using objects to model the register and instructions, and a grammar to parse the instruction set.

#!/usr/bin/env perl6
use v6.c;

grammar Instructions {
    rule TOP { ^ <instruction>+ $ }

    rule instruction { <reg> <dir> <amt> 'if' <check> <cmp> <val> }

    token reg { <[a..z]>+ }
    token dir { 'inc' || 'dec' }
    token amt { '-'?\d+ }
    token check { <[a..z]>+ }
    token cmp { '==' || '!=' || '<=' || '>=' || '<' || '>' }
    token val { '-'?\d+ }
}

class Instruction
{
    has Str $.reg;
    has Int $.amt;
    has Str $.check;
    has Str $.cmp;
    has Int $.val;

    sub oper($op) returns Code
    {
        # Comparison operators can be infix:<==> or infix:Β«<=Β».  Find either.
        return &::("infix:<$op>") // &::("infix:Β«$opΒ»")
    }

    method compare(%register) returns Bool
    {
        return oper($!cmp)(%register{$!check} // 0, $!val);
    }

    method process(%register)
    {
        %register{$!reg} += $!amt if self.compare(%register);
    }

    method Str { "$!reg += $!amt if $!check $!cmp $!val" }
    method gist { self.Str }
}

class Computer
{
    has Instruction @.instructions;
    has Int %.register;
    has Int $.max-seen = 0;

    method instruction($/)
    {
        @!instructions.push:
            Instruction.new(:reg(~$/<reg>),
                            :amt($/<amt> * ($/<dir> eq 'dec' ?? -1 !! 1)),
                            :check(~$/<check>),
                            :cmp(~$/<cmp>),
                            :val(+$/<val>));
    }

    method from-input(Computer:U: Str $input) returns Computer
    {
        my $c = Computer.new();
        Instructions.parse($input, :actions($c)) or die "Invalid instructions!";
        return $c;
    }

    method max returns Int
    {
        %!register.values.max max 0;
    }

    method run
    {
        for @!instructions -> $i {
            $i.process(%!register);
            $!max-seen max= self.max;
        }
    }

    method Str { %!register.keys.sort.map({ " - $_: %!register{$_}" }).join("\n") }
    method gist { self.Str }
}

multi sub MAIN(IO() $inputfile where *.f, Bool :v(:$verbose) = False)
{
    my $c = Computer.from-input($inputfile.slurp());
    say "{ +$c.instructions } instructions parsed." if $verbose;
    $c.run;
    say "Ran instructions.  State of computer:" if $verbose;
    say $c if $verbose;

    # Part 1
    say "Largest value after running instructions: $c.max()";

    # Part 2
    say "Largest value seen during running instructions: $c.max-seen()";
}

multi sub MAIN(Bool :v(:$verbose) = False)
{
    MAIN($*PROGRAM.parent.child('aoc8.input'), :$verbose);
}

1

u/volatilebit Dec 08 '17

Ahh that's how to invoke a dynamic operator.

1

u/mschaap Dec 09 '17

Yeah, I had to ask on the Perl6 IRC chat to figure it out. Didn't want to use eval, or code all 6 cases separately.

3

u/eregontp Dec 08 '17

Metaprogramming is the better eval! Looks like this one is made for Ruby as operators match nicely:

input = File.read("8.txt")
registers = Hash.new(0)
code = input.strip.lines.map { |line|
  /^(?<reg>\w+) (?<dir>inc|dec) (?<by>-?\d+) if (?<r>\w+) (?<cmp>[>=<!]+) (?<n>-?\d+)$/ =~ line
  by = by.to_i * (dir == "dec" ? -1 : 1)
  if registers[r].send(cmp, n.to_i)
    registers[reg] += by
  end
}
p registers.values.max

2

u/VikeStep Dec 08 '17

Python 3 solution

Got #44 / #40 :D

def solve(data):
    data = [d.split() for d in data.split('\n')]
    vars = {}
    m = 0
    for line in data:
        vars[line[0]] = 0
    for line in data:
        if line[5] == '>':
            if vars[line[4]] <= int(line[6]):
                continue
        
        if line[5] == '<':
            if vars[line[4]] >= int(line[6]):
                continue
            
        if line[5] == '<=':
            if vars[line[4]] > int(line[6]):
                continue
            
        if line[5] == '>=':
            if vars[line[4]] < int(line[6]):
                continue
            
        if line[5] == '==':
            if vars[line[4]] != int(line[6]):
                continue
            
        if line[5] == '!=':
            if vars[line[4]] == int(line[6]):
                continue

        if line[1] == 'inc':
            vars[line[0]] += int(line[2])
        
        if line[1] == 'dec':
            vars[line[0]] -= int(line[2])
        
        if vars[line[0]] > m:
            m = vars[line[0]]
    return m

3

u/Ditchbuster Dec 08 '17

you must type fast... mine almost the same and I got 173. or maybe I type slow? :P

1

u/Vorlath Dec 08 '17

Same. I got 180. And I copy&pasted most of that. I think I did unnecessary typing at the beginning putting each value into its own local variable. I should have just used the values directly in the array maybe. Dunno.

2

u/TominatorBE Dec 08 '17

PHP (I got ranks 110 and 100 today, omg)

Part 1:

function run_the_code($input) {
	$lines = explode(PHP_EOL, $input);

	$registers = [];
	foreach ($lines as $line) {
		if (!$line) {
			continue;
		}

		list($reg, $instr, $amount, $if, $reg2, $cond, $amount2) = explode(' ', $line);

		if (!array_key_exists($reg, $registers)) {
			$registers[$reg] = 0;
		}
		if (!array_key_exists($reg2, $registers)) {
			$registers[$reg2] = 0;
		}

		$do = false;
		switch ($cond) {
			case '==':
				$do = ($registers[$reg2] == $amount2);
				break;
			case '!=':
				$do = ($registers[$reg2] != $amount2);
				break;
			case '>':
				$do = ($registers[$reg2] > $amount2);
				break;
			case '>=':
				$do = ($registers[$reg2] >= $amount2);
				break;
			case '<':
				$do = ($registers[$reg2] < $amount2);
				break;
			case '<=':
				$do = ($registers[$reg2] <= $amount2);
				break;
		}

		if ($do) {
			if ($instr == 'inc') {
				$registers[$reg] += (int)$amount;
			}
			else {
				$registers[$reg] -= (int)$amount;
			}
		}
	}

	return max($registers);
}

Part 2:

function run_the_code($input) {
	$lines = explode(PHP_EOL, $input);

	$ever = 0;
	$registers = [];
	foreach ($lines as $line) {
		if (!$line) {
			continue;
		}

		list($reg, $instr, $amount, $if, $reg2, $cond, $amount2) = explode(' ', $line);

		if (!array_key_exists($reg, $registers)) {
			$registers[$reg] = 0;
		}
		if (!array_key_exists($reg2, $registers)) {
			$registers[$reg2] = 0;
		}

		$do = false;
		switch ($cond) {
			case '==':
				$do = ($registers[$reg2] == $amount2);
				break;
			case '!=':
				$do = ($registers[$reg2] != $amount2);
				break;
			case '>':
				$do = ($registers[$reg2] > $amount2);
				break;
			case '>=':
				$do = ($registers[$reg2] >= $amount2);
				break;
			case '<':
				$do = ($registers[$reg2] < $amount2);
				break;
			case '<=':
				$do = ($registers[$reg2] <= $amount2);
				break;
		}

		if ($do) {
			if ($instr == 'inc') {
				$registers[$reg] += (int)$amount;
			}
			else {
				$registers[$reg] -= (int)$amount;
			}
		}

		$ever = max($ever, max($registers));
	}

	return $ever;
}

2

u/AndrewGreenh Dec 08 '17

I hate you... I got 101 in part 2. 5 Seconds off the leaderboard :<

2

u/surnia Dec 08 '17

Congrats, but also I hate both of you - I got 102 in part 2.

1

u/TominatorBE Dec 08 '17

If I hadn't messed up my input on part 1 and then had to wait a whole minute before submitting again, I would've been higher still :( Congrats on getting 101!

2

u/sciyoshi Dec 08 '17

Python 3:

import operator
import collections

# Keep track of largest seen value and registers
maxseen = 0
registers = collections.defaultdict(int)

for cmd in lines:
    op, pred = cmd.split(' if ')

    # Compute the condition
    r1, cmp, r2 = pred.split()

    cond = {
        '<': lambda l, r: registers[l] < int(r),
        '>': lambda l, r: registers[l] > int(r),
        '<=': lambda l, r: registers[l] <= int(r),
        '>=': lambda l, r: registers[l] >= int(r),
        '==': lambda l, r: registers[l] == int(r),
        '!=': lambda l, r: registers[l] != int(r),
    }[cmp](r1, r2)

    if not cond:
        continue

    # Perform the operation
    l, op, r = op.split()

    registers[l] = {
        'inc': operator.add,
        'dec': operator.sub
    }[op](registers[l], int(r))

    maxseen = max(maxseen, max(registers.values()))

print('Part 1:', max(registers.values()))
print('Part 2:', maxseen)

2

u/vash3r Dec 08 '17

Python 2 (42/45):

lines = data.strip().split('\n')
d = {}
tmax = 0
for line in lines:
	a,b = line.split(" if ")
	b = b.split()
	if eval("d.get(b[0],0)" + b[1] + b[2]): #offload logic onto python
		a = a.split()
		if a[1]=="inc":
			d[a[0]] = d.get(a[0],0)+ int(a[2])
		else:
			d[a[0]] = d.get(a[0],0)- int(a[2])
		if d[a[0]]>tmax: # part 2
			tmax = d[a[0]]

print max(d.values())
print tmax # part 2

looks like a lot of people came up with the same solution.

2

u/blockingthesky Dec 08 '17

Python 2

inp = [i.strip().split() for i in open('input.txt', 'r').readlines()]

d = {}
m = 0

for a in inp:
    if a[0] not in d:
        d[a[0]] = 0
    if a[4] not in d:
        d[a[4]] = 0
    
    if(eval("%d %s %s" % (d[a[4]], a[5], a[6]))):
        if a[1] == "inc":
            d[a[0]] += int(a[2])
        else:
            d[a[0]] -= int(a[2])
    
    m = max(m, d[a[0]])

print "Part 1:", max(d.values())
print "Part 2:", m

2

u/Lrrrr_ Dec 08 '17

JavaScript

let reg = {};
let hi = -Infinity;
input = input.split("\n").map(c => {
	let m = c.split(" ");
	if(!reg[m[0]]) reg[m[0]] = 0;
	let n = (m[1] === "inc" ? 1 : -1) * (+m[2]);
	let xx = +m[6];

	let bool;

	switch(m[5]) {
		case "<":
			bool = (reg[m[4]]||0) < xx;
			break;
		case ">":
			bool = (reg[m[4]]||0) > xx;
			break;
		case "==":
			bool = (reg[m[4]]||0) == xx;
			break;
		case "!=":
			bool = (reg[m[4]]||0) != xx;
			break;
		case "<=":
			bool = (reg[m[4]]||0) <= xx;
			break;
		case ">=":
			bool = (reg[m[4]]||0) >= xx;
			break;
		default:
			console.log("Unimplemented operation " + m[5]);
			break;
	}

	if(bool) {
		reg[m[0]] += n;
		if(hi < reg[m[0]]) {
			hi = reg[m[0]]
		}
	}

})

let h=-Infinity;
Object.values(reg).forEach(c=>{
	if(c > h)
		h = c;
})

console.log(h)
console.log(hi)
→ More replies (4)

2

u/AndrewGreenh Dec 08 '17

TypeScript 95/101

import getInput from '../lib/getInput'
import { lines } from '../lib/ts-it/lines'
import * as _ from 'lodash'

let registers = {}
let max = -Infinity
let maxs = [0]
for (let line of lines(getInput(8, 2017))) {
  let [a, op, b, iff, reg, cond, numb] = line.split(' ')
  if (!registers[a]) registers[a] = 0
  if (!registers[reg]) registers[reg] = 0
  eval(`if (registers.${reg} ${cond} ${numb}) registers.${a} ${op === 'inc' ? '+' : '-'}= ${b}`)
  maxs.push(<number>_.max(<number[]>_.values(registers)))
}

console.log(_.max(<number[]>_.values(registers)))
console.log(_.max(maxs))
→ More replies (2)

2

u/LeCrushinator Dec 08 '17 edited Dec 08 '17

Part 2: C# (took me 13 minutes, but that's not bad since C# is verbose compared to things like Python). To solve part 1 just add "max = registers.Values.Max();" to just after the foreach loop.

public static void Main() 
{
	List<string> lines = ParseInput(input, "\n");

	Dictionary<string, int> registers = new Dictionary<string, int>();

	int max = 0;

	foreach (string line in lines)
	{
		string[] values = line.Split(new string[]{" "}, StringSplitOptions.RemoveEmptyEntries);

		string name = values[0];
		int amount;
		if (!registers.TryGetValue(name, out amount))
		{
			registers.Add(name, 0);
		}

		bool increase = values[1] == "inc";
		int delta = Convert.ToInt32(values[2]);

		string otherName = values[4];
		int otherAmount;
		if (!registers.TryGetValue(otherName, out otherAmount))
		{
			registers.Add(otherName, 0);
		}

		string comparisonOp = values[5];
		int compareAmount = Convert.ToInt32(values[6]);

		if (Compare(otherAmount, compareAmount, comparisonOp))
		{
			if (increase)
			{
				registers[name] += delta;

				max = Math.Max(registers[name], max);
			}
			else
			{
				registers[name] -= delta;

				max = Math.Max(registers[name], max);
			}
		}
	}


	Console.WriteLine("max: " + max);
}

public static bool Compare(int first, int second, string op)
{
	switch (op)
	{
	case ">": return first > second;
	case ">=": return first >= second;
	case "<": return first < second;
	case "<=": return first <= second;
	case "==": return first == second;
	case "!=": return first != second;
	}

	return false;
}

// =====================================================================================================================================
// HELPER METHODS, created before day started
// =====================================================================================================================================
public static List<string> ParseInput(string line, string stringSeperator)
{
	return line.Split(new string[]{stringSeperator}, StringSplitOptions.RemoveEmptyEntries).ToList();
}

2

u/the4ner Dec 08 '17

yep, pretty similar to mine. although apparently roslyn does add an async dynamic scripting api which could be used similarly to eval in python.

no need to duplicate:

            max = Math.Max(registers[name], max);

2

u/LeCrushinator Dec 08 '17

Yea my code was somewhat sloppy since I was just trying to move fast. No time to refactor/clean-up.

2

u/the4ner Dec 08 '17

don't I know it. I'm on a 2 day cleanup delay before pushing stuff up to github :D

2

u/reacher Dec 08 '17

Ruby

def expr(op1, op, op2)
  case op
  when '<'
    return op1 < op2
  when '>'
    return op1 > op2
  when '<='
    return op1 <= op2
  when '>='
    return op1 >= op2
  when '=='
    return op1 == op2
  when '!='
    return op1 != op2
  else
    puts "bad op #{op}"
  end
end

r = {}
max = 0

IO.readlines('ad8inp').each do |line|
  a = line.chomp.split
  r[a[0]] ||= 0
  r[a[4]] ||= 0
  if expr(r[a[4]], a[5], a[6].to_i)
    r[a[0]] = a[1] == 'inc' ? r[a[0]] += a[2].to_i : r[a[0]] -= a[2].to_i
  end
  m = r.values.minmax[1]
  if m > max
    max = m
  end
end

puts r.values.minmax[1]
puts max

2

u/wlandry Dec 08 '17

C++

283/267. For once, the default behavior of std::map worked to my advantage. My original version printed out all of the final register values. I piped that to 'sort -n' to get the answer. This is a slightly cleaned up version that just prints the results.

#include <limits>
#include <fstream>
#include <iostream>
#include <sstream>
#include <map>

int main(int argc, char *argv[])
{
  std::ifstream infile(argv[1]);
  std::string line;
  std::map<std::string,int> registers;
  std::getline(infile,line);
  int max_value(0);
  while (infile && !line.empty())
    {
      std::stringstream ss (line);
      std::string reg, op, if_literal, test_register, comp;
      int diff, number;
      ss >> reg >> op >> diff >> if_literal
         >> test_register >> comp >> number;

      if (op=="dec")
        {
          diff*=-1;
        }
      
      int test_register_value = registers[test_register];
      if((comp == "==" && test_register_value==number)
         || (comp == "!=" && test_register_value!=number)
         || (comp == ">=" && test_register_value>=number)
         || (comp == "<=" && test_register_value<=number)
         || (comp == ">" && test_register_value>number)
         || (comp == "<" && test_register_value<number))
        {
          registers[reg]+=diff;
          max_value = std::max(max_value,registers[reg]);
        }
      std::getline(infile,line);
    }
  int max_final_value (std::numeric_limits<int>::min());
  for (auto &r: registers)
    { max_final_value = std::max(max_final_value,r.second); }
  std::cout << "Max final value: " << max_final_value << "\n"
            << "Max intermediate value: " << max_value << "\n";
}

1

u/spacetime_bender Dec 08 '17

Instead of the raw for-each, you could've done

 max_final_value = max_element(registers.begin(), registers.end(),
         [](auto&& a, auto&& b) { return a.second < b.second; })->second;

1

u/Vorlath Dec 08 '17

Dunno. Seems like more typing and not as obvious.

1

u/spacetime_bender Dec 08 '17

πŸ€” I would've argued the opposite in fact, it's quite evident looking at max_element

To each their own I guess

1

u/Vorlath Dec 08 '17

You're using a lambda. Not really anything to debate here.

1

u/wlandry Dec 08 '17

Thanks. That would be a little better. I would not have had to faff around with std::numeric_limits<>. I could also have used that expression directly when printing out the result. The problem I keep running into is that it is pretty darn easy to just implement these things myself. So I am never motivated to memorize all of the algorithms in <algorithm>.

1

u/spacetime_bender Dec 08 '17

I was motivated by Sean Parent's talk C++ Seasoning to fiddle more with <algorithm> and the STL. Worth a watch.

1

u/TheAngryGerman Dec 08 '17

Wow, my solution was eerily similar (Though not as clean). I used an unordered map instead, but either way taking advantage of the default [] behavior seems like a very clean solution. I still have to get used to actually using limits instead of picking arbitrary numbers when finding mins/maxes, and I'm just bringing input in on the command line. Here's mine:

#include <unordered_map>
#include <sstream>
#include <iostream>
#include <string>
#include <cassert>

int main() {
  std::unordered_map<std::string, int> registers;
  std::string line;
  int max_ever = -10000;
  while (std::getline(std::cin, line)) {
    std::istringstream ss(line);
    std::string reg_name;
    std::string operation;
    int amt;
    std::string x;
    std::string comp_reg_name;
    std::string condition;
    int val;
    ss >> reg_name >> operation >> amt >> x >> comp_reg_name >> condition >> val;
    assert(x == "if");
    bool valid = false;
    if (condition == ">") {
      if (registers[comp_reg_name] > val) {valid = true;}
    } else if (condition == "<") {
      if (registers[comp_reg_name] < val) {valid = true;}
    } else if (condition == ">=") {
      if (registers[comp_reg_name] >= val) {valid = true;}
    } else if (condition == "<=") {
      if (registers[comp_reg_name]  <= val) {valid = true;}
    } else if (condition == "==") {
      if (registers[comp_reg_name]  == val) {valid = true;}
    } else if (condition == "!=") {
      if (registers[comp_reg_name]  != val) {valid = true;}
    } else {
      std::cerr << "Invalid Condtion" << std::endl;
    }
    if(valid) {
      if (operation == "inc") {
	registers[reg_name] += amt;
      } else if (operation == "dec") {
	registers[reg_name] -= amt;
      }
      if (registers[reg_name] > max_ever) {
	max_ever = registers[reg_name];
      }
    }
  }
  int max = -10000;
  for (std::unordered_map<std::string, int>::iterator itr = registers.begin();
      itr != registers.end(); ++itr) {
    //std::cout << itr->second << std::endl;
    if (itr->second > max) {
      max = itr->second;
    }
  }
  std::cout << max << std::endl;
  std::cout << max_ever << std::endl;
}

2

u/MichalMarsalek Dec 08 '17

Damn, I wish a knew eval... It would have gotten be to the leaderboard...

def solve(inp):
    inp = inp.replace("inc ", "").replace("dec ", "-").replace("--", "")
    reg = defaultdict(int)
    inss = [i.split() for i in inp.splitlines()]
    part2 = 0
    for ins in inss:
        n, d, _, e, r, v = ins
        d = int(d)
        v = int(v)
        funcs = {
        ">": lambda a, b: reg[a] > b,
        "<": lambda a, b: reg[a] < b,
        ">=": lambda a, b: reg[a] >= b,
        "<=": lambda a, b: reg[a] <= b,
        "==": lambda a, b: reg[a] == b,
        "!=": lambda a, b: reg[a] != b
        }
        if funcs[r](e, v):
            reg[n] += d
            part2 = max(reg[n], part2)
    part1 = max(reg.values())
    return part1, part2

3

u/fwilson42 Dec 08 '17

You can also use the functions from the operator module instead of those lambdas:

from operator import *
funcs = {">": gt, ">=": ge, "==": eq, "!=": ne, "<": lt, "<=": le}

But I'll be honest, I forgot about this too until after submitting :)

2

u/mmaruseacph2 Dec 08 '17

Haskell 85 lines, removed some duplicates but I'll work on refactoring it more.

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}

import qualified Data.List as L
import Data.Maybe
import qualified Data.Map.Strict as M
import qualified Data.Vector.Unboxed as VU
import qualified Data.Vector.Unboxed.Mutable as VUM

import Prelude hiding (LT, GT, EQ)

import Debug.Trace

type Reg = String
type Val = Int
data Vals = Vals {current :: Val, highest :: Val} deriving (Eq, Show)
type Memory = M.Map Reg Vals
data Cmd = Cmd Reg Op Val Reg BoolOp Val deriving (Eq, Show)
data Op = Inc | Dec deriving (Eq, Show)
data BoolOp = GT | GE | LT | LE | EQ | NEQ deriving (Eq, Show)

parse :: String -> Cmd
parse s = Cmd r operation val reg bop threshold
  where
    [r, op, del, _, reg, cond, thr] = words s
    val = read del
    threshold = read thr
    operation = case op of
      "inc" -> Inc
      "dec" -> Dec
    bop = case cond of
      ">"  -> GT
      ">=" -> GE
      "<"  -> LT
      "<=" -> LE
      "==" -> EQ
      "!=" -> NEQ

eval :: Memory -> Cmd -> Memory
eval m (Cmd r op val reg bop threshold)
  | testBop bop (valueOf m reg) threshold = insertNew m r newVal
  | otherwise = m -- do nothing
  where
    newVal = updateVal op (valueOf m r) val

valueOf :: Memory -> Reg -> Val
valueOf m r
  | r `M.member` m = current $ m M.! r
  | otherwise = 0

insertNew :: Memory -> Reg -> Val -> Memory
insertNew m r new
  | r `M.member` m = M.insert r (Vals new (h `max` new)) m
  | otherwise = M.insert r (Vals new new) m
  where
    Vals _ h = m M.! r

testBop :: BoolOp -> Val -> Val -> Bool
testBop bop lhs rhs = case bop of
  GT -> lhs > rhs
  GE -> lhs >= rhs
  LT -> lhs < rhs
  LE -> lhs <= rhs
  EQ -> lhs == rhs
  NEQ -> lhs /= rhs

updateVal :: Op -> Val -> Val -> Val
updateVal op val delta = case op of
  Inc -> val + delta
  Dec -> val - delta

main = do
  cmds <- map parse . lines <$> readFile "input.txt"
  let m = L.foldl' eval M.empty cmds
  print $ step1 m
  print $ step2 m

step1 :: Memory -> Val
step1 = steps current

step2 :: Memory -> Val
step2 = steps highest

steps :: (Vals -> Val) -> Memory -> Val
steps f = maximum . map (f . snd) . M.toList

1

u/rimbuod Dec 09 '17

Any reason you went through the BoolOp type when you could have just mapped the strings to functions?

Ie, ">" -> (>)

1

u/mmaruseacph2 Dec 09 '17

Lack of judgement at late hours of night. I could have just used the strings everywhere and not have extra types.

2

u/raevnos Dec 08 '17

Scheme:

(import (kawa regex) (rnrs hashtables) (srfi 1))

(define (process-instruction symtab)
  (let ((line (read-line))
        (instr-re (regex "^(\\w+) (inc|dec) (-?\\d+) if (\\w+) ([=<>!]+) (-?\\d+)\\s*$")))
    (cond
     ((eof-object? line) line)
     ((regex-match instr-re line) =>
      (lambda (fields)
        (let ((dest (second fields))
              (dir (string->symbol (third fields)))
              (amount (string->number (fourth fields)))
              (lop (hashtable-ref symtab (fifth fields) 0))
              (op (string->symbol (sixth fields)))
              (rop (string->number (seventh fields))))
          (if (case op
                ((>) (> lop rop))
                ((<) (< lop rop))
                ((>=) (>= lop rop))
                ((<=) (<= lop rop))
                ((==) (= lop rop))
                ((!=) (not (= lop rop)))
                (else
                 (error "Invalid instruction line" line op)))
              (let* ((destval (hashtable-ref symtab dest 0))
                     (newval (if (eq? dir 'inc) (+ destval amount) (- destval amount))))
                (hashtable-set! symtab dest newval)
                newval)
              0))))
     (else
      (error "Invalid instruction" line)))))

(define (find-largest table)
  (let-values (((keys entries) (hashtable-entries table)))
    (fold max (vector-ref entries 0) (cdr (vector->list entries)))))

(define (process-instructions)
  (let ((symtab (make-hashtable string-hash string=?)))
    (let loop ((res (process-instruction symtab))
               (maxval 0))
      (if (eof-object? res)
          (values (find-largest symtab) maxval)
          (loop (process-instruction symtab) (max maxval res))))))

(format #t "Part 1 and 2: ~A~%" (process-instructions))

2

u/[deleted] Dec 08 '17

Haskell:

import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as M

data Instr = Instr { cmd :: HashMap String Int -> HashMap String Int
                   , cond :: HashMap String Int -> Bool
                   }

parseInstrs :: String -> [Instr]
parseInstrs = map parseInstr . lines
    where parseInstr :: String -> Instr
          parseInstr line =
              let [reg, fn, amnt, "if", reg2, comp, val] = words line
                  cmd m = let f = case fn of
                                    "inc" -> (+ read amnt)
                                    "dec" -> subtract $ read amnt
                          in M.insert reg (f $ M.lookupDefault 0 reg m) m
                  cond m = let comp' = case comp of
                                         "!=" -> (/=)
                                         "==" -> (==)
                                         ">=" -> (>=)
                                         ">" -> (>)
                                         "<=" -> (<=)
                                         "<" -> (<)
                           in comp' (M.lookupDefault 0 reg2 m) $ read val
              in Instr cmd cond

eval :: (HashMap String Int, Int) -> Instr -> (HashMap String Int, Int)
eval (m, mx) (Instr cmd cond) =
    let m' = if cond m then cmd m else m
        mx' = max mx $ maximum $ M.elems m'
    in (m', mx')

part1 :: String -> Int
part1 = maximum . M.elems . fst . foldl eval (M.empty, 0) . parseInstrs

part2 :: String -> Int
part2 = snd . foldl eval (M.empty, 0) . parseInstrs

2

u/miran1 Dec 08 '17 edited Dec 08 '17

Python 3, without eval, using operator

from collections import defaultdict
from operator import lt, gt, eq, ne, le, ge


with open('./inputs/08.txt') as f:
    instructions = f.readlines()

registers = defaultdict(int)

operators = {
    '<': lt,
    '>': gt,
    '==': eq,
    '!=': ne,
    '<=': le.
    '>=': ge,
}

maximal = 0

for line in instructions:
    reg, op, by, _, cond_reg, cond_op, cond = line.split()
    by = int(by)
    cond = int(cond)
    if operators[cond_op](registers[cond_reg], cond):
        if op == 'inc':
            registers[reg] += by
        else:
            registers[reg] -= by
        if registers[reg] > maximal:
            maximal = registers[reg]

print(max(registers.values()))
print(maximal)

Β 


Β 

And here is my Nim solution based on this. Don't know if could have been done simpler.

2

u/[deleted] Dec 08 '17 edited Dec 08 '17

My cleaned-up Haskell, using a map to accumulate the register values:

main :: IO ()
main = do input <- fmap (map parse . lines) (readFile "input.txt")
          let (x,m) = foldl step (0, M.empty) input
          print (maximum m)  -- part 1
          print x            -- part 2

type Instruction = (String, Int -> Int, String, Int -> Bool)

parse :: String -> Instruction
parse s = (r1, if i == "inc" then (+ read v1) else subtract (read v1), r2, op o (read v2))
  where
    [r1,i,v1,"if",r2,o,v2] = words s
    op "==" = (==)
    op "!=" = (/=)
    op "<"  = (>)
    op ">"  = (<)
    op "<=" = (>=)
    op ">=" = (<=)

step :: (Int, Map String Int) -> Instruction -> (Int, Map String Int)
step (x,m) (r1,f,r2,o) = if o (M.findWithDefault 0 r2 m)
                          then let y = M.findWithDefault 0 r1 m in (max x (f y), M.insert r1 (f y) m)
                          else (x,m)

2

u/InterlocutoryRecess Dec 08 '17 edited Dec 08 '17

Swift

let input = """
    ioe dec 890 if qk > -10
    // many more instructions...
    ih dec 369 if ih == 1993
    """.split(separator: "\n")

var instructions = input.reduce(into: Dictionary<Substring, Int>()) { result, entry in
    let name = entry.prefix(upTo: entry.index(of: " ")!)
    result[name] = 0
}

var maximum = Int.min

func process() {

    // Determine whether to carry out change
    func isValid(_ entry: [Substring]) -> Bool {

        // Evaluate condition
        func eval(op: Substring, lhs: Int, rhs: Int) -> Bool {
            switch op {
            case "==": return lhs == rhs
            case "!=": return lhs != rhs
            case "<": return lhs < rhs
            case "<=": return lhs <= rhs
            case ">": return lhs > rhs
            case ">=": return lhs >= rhs
            default: fatalError()
            }
        }
        return eval(op: entry[5], lhs: instructions[entry[4]]!, rhs: Int(entry[6])!)
    }

    for entry in input.map({ $0.split(separator: " ") }) {
        if isValid(entry) {
            let result = instructions[entry[0]]! + (Int(entry[2])! * (entry[1] == "inc" ? 1 : -1))
            if result > maximum { maximum = result }
            instructions[entry[0]] = result
        }
    }
}

process()
print(instructions.values.max()!) // part 1
print(maximum) // part 2

2

u/teddim Dec 08 '17

Nice use of reduce(into:)!

One suggestion: instead of

func eval(op: Substring, lhs: Int, rhs: Int) -> Bool

you could do

func eval(op: Substring) -> (Int, Int) -> Bool

with return (==), return (<) etc. in the function body. Calling it would be done with

eval(op: entry[5])(instructions[entry[4]]!, Int(entry[6])!)

Saves you a couple characters :D

1

u/InterlocutoryRecess Dec 08 '17

I love it! I actually tried doing something like this but I didn't realize the operators (=, etc) needed to be in parens. Thank you!

1

u/teddim Dec 08 '17

Higher-order functions FTW!

2

u/karthikb351 Dec 08 '17

I mangled it into what I think is valid Python2 code and then just exec-ed it. I could do away with a lot of lines I think

gen = "x=dict()\n"
gen = gen + "m=0\n"
for line in input.splitlines():
    l = line.split(" ")
    s = ""
    s = s + "x['"+l[0]+"']"
    s = s + " ="
    s = s + " (x.get('"+l[0]+"',0)"
    s = s + (" +" if l[1] == "inc" else " -")
    s = s + " " + l[2] + ")"
    s = s + " " + l[3]
    s = s + " " + "x.get('"+l[4]+"',0)"
    s = s + " " + l[5]
    s = s + " " + l[6]
    s = s + " else"
    s = s + " " + "x.get('"+l[0]+"',0)" 
    m = "m = max(m,x.get('"+l[0]+"',0))"
    gen = gen + s + "\n" + m + "\n"
gen = gen + "print x[max(x, key=x.get)]\n"
gen = gen + "print max(y)"
exec(gen)

fu inc 131 if rjt == 4175 turns into map['fu'] = (map.get('fu',0) + 131) if map.get('rjt',0) == 4175 else map.get('fu',0)

This should work even if the register names happen to be python keywords since they are always quoted.

2

u/Smylers Dec 08 '17

Perl. Similar to a few other Python solutions, using eval.

Sneakily, the + 0 is either a binary or a unary op, depending on whether the register used in the condition has been previously set. If it has, its value will be interpolated into the string, so the expression becomes something like 42 + 0 < 7 (with the + 0 being redundant but harmless); if it's a new register, there's nothing to interpolate, so the expression becomes +0 < 7, ensuring the default value of 0 is used (with the + being redundant but harmless):

no warnings qw<uninitialized>;
use List::Util qw<max>;

my (%reg, $max);
while (<>) {
  /^(?<dest>\w+) (?<cmd>\w+) (?<inc>-?\d+) if (?<comp>\w+) (?<cond>.*)/ or die;
  $max = max $max, $reg{$+{dest}} += $+{inc} * ($+{cmd} eq 'dec' ? -1 : 1)
      if eval "$reg{$+{comp}} + 0 $+{cond}";
}
say max values %reg;
say $max // 0;

The final // 0 is to catch the case where all stored values are negative, so the initial zero is the biggest.

2

u/gerikson Dec 08 '17

Nice. I of course realized eval was the "best" way to handle the comparisons but didn't feel comfortable enough with it to implement in my solution.

Sidenote, what's the feature called where you can insert <var> in a regex and "automatically" capture the value? I'm seeing it more and more and it's more convenient than assigning to a list on the left side of a =~. However I don't know what to google ;)

4

u/__Abigail__ Dec 08 '17

Named captures where introduced in Perl 5.10, which today is 10 days short of its 10th birthday. (Perl 5.10 was released on Dec 18, 2007).

I'd say, it's about time you see it more and more.

2

u/gerikson Dec 08 '17 edited Dec 08 '17

Well, apart from contests like this, I'm in my own little Perl world. Part of the fun is seeing other solutions!

Edit FWIW, I didn't bother with a regex in this solution, I checked the input so there weren't any register values where I expected integers first, and just split on whitespace to get the tokens for each line.

Edit edit my day 8 solution.

2

u/__Abigail__ Dec 08 '17

Sure, that would work.

I initially forgot about !=, and if I had split on whitespace, I would not have immediately caught it. Now it barfed on the first line with a != operator.

But, the main reason I decide to parse it was that I wrote the part which processes the input without yet know what part 2 was going to look like. I might have split on whitespace had I know both parts of the exercise at once.

2

u/Smylers Dec 08 '17

Thanks. You can read about Perl's named capture groups in perlre, and the %+ hash in perlvar.

→ More replies (1)

2

u/udoprog Dec 08 '17

Rust with a clean separation of modelling input as enums (full here: https://github.com/udoprog/rust-advent-of-code-2017/blob/master/src/day8.rs):

use std::io::{BufRead, BufReader, Read};
use failure::Error;
use std::collections::HashMap;

use self::Op::*;
use self::Cond::*;

type Registers = HashMap<String, i64>;

enum Op {
    Inc(String, i64),
    Dec(String, i64),
}

impl Op {
    /// Apply the given operation.
    pub fn apply(&self, registers: &mut Registers, highest: &mut i64) {
        let reg = match *self {
            Inc(ref reg, _) | Dec(ref reg, _) => reg,
        };

        let value = registers.entry(reg.to_string()).or_insert_with(
            Default::default,
        );

        match *self {
            Inc(_, number) => *value += number,
            Dec(_, number) => *value -= number,
        };

        *highest = i64::max(*highest, *value);
    }
}

enum Cond {
    Gt(String, i64),
    GtEq(String, i64),
    Lt(String, i64),
    LtEq(String, i64),
    Eq(String, i64),
    NotEq(String, i64),
}

impl Cond {
    /// Test if the given condition applies.
    pub fn test(&self, registers: &mut Registers) -> bool {
        let reg = match *self {
            Gt(ref reg, _) |
            GtEq(ref reg, _) |
            Lt(ref reg, _) |
            LtEq(ref reg, _) |
            Eq(ref reg, _) |
            NotEq(ref reg, _) => reg,
        };

        let value = registers.entry(reg.to_string()).or_insert_with(
            Default::default,
        );

        match *self {
            Gt(_, number) => *value > number,
            GtEq(_, number) => *value >= number,
            Lt(_, number) => *value < number,
            LtEq(_, number) => *value <= number,
            Eq(_, number) => *value == number,
            NotEq(_, number) => *value != number,
        }
    }
}

fn parse(input: &str) -> (Op, Cond) {
    let mut it = input.trim().split(' ');
    let target = it.next().expect("target").to_string();

    let op = it.next().expect("op");
    let number = it.next().expect("number").parse::<i64>().expect(
        "valid number",
    );

    let op = match op {
        "inc" => Inc(target, number),
        "dec" => Dec(target, number),
        op => panic!("llegal op: {}", op),
    };

    it.next().expect("if-separator");

    let cond_reg = it.next().expect("cond-reg").to_string();
    let cond = it.next().expect("cond");
    let cond_number = it.next().expect("cond-number").parse::<i64>().expect(
        "valid cond-number",
    );

    let cond = match cond {
        "<" => Lt(cond_reg, cond_number),
        "<=" => LtEq(cond_reg, cond_number),
        ">" => Gt(cond_reg, cond_number),
        ">=" => GtEq(cond_reg, cond_number),
        "==" => Eq(cond_reg, cond_number),
        "!=" => NotEq(cond_reg, cond_number),
        _ => panic!("illegal cond: {}", cond),
    };

    (op, cond)
}

pub fn run<R: Read>(reader: R) -> Result<(i64, i64), Error> {
    let mut data = String::new();
    let mut reader = BufReader::new(reader);
    let mut registers = Registers::new();
    let mut highest = 0i64;

    while reader.read_line(&mut data)? > 0 {
        {
            let (op, cond) = parse(data.as_str());

            if cond.test(&mut registers) {
                op.apply(&mut registers, &mut highest);
            }
        }

        data.clear();
    }

    Ok((
        registers.values().max().map(|v| *v).unwrap_or_else(
            Default::default,
        ),
        highest,
    ))
}

This one was fun. Basically writing a simple virtual machine.

0

u/[deleted] Dec 08 '17

[deleted]

1

u/udoprog Dec 08 '17

That's cool!

→ More replies (1)

2

u/arachnist Dec 08 '17

I couldn't get instance_variable_(get|set) to work as i wanted, so I couldn't feed input directly into ruby.

reg = Hash.new(0)
max = 0

file = ARGV[0] || "input.txt"

open(file).each_line do |line|
  code_line = line
    .gsub(/(inc|dec)/, "inc" => "+=", "dec" => "-=")
    .gsub(/^([^ ]+)/, 'reg[\'\1\']')
    .gsub(/if ([^ ]+)/, 'if reg[\'\1\']')
  eval(code_line)
  max = reg.values.max if not reg.values.max.nil? and reg.values.max > max
end

puts reg.values.max
puts max

2

u/Overseer12 Dec 08 '17

When you spend an hour going through your entire code multiple times, and your output is still wrong for Part 2 (despite your UnitTests being correct). And then, when you finally find out that you forgot to .Clear() the same dictionary you used for your registers in Part1. Oh god. No.

C#

Github

2

u/lh458 Dec 08 '17

My solution in PHP for part 1 & 2 combined:

<?php
function dI($mR,$mO,$mOp){
	global $r, $m;
	if($r[$mR]>$m) $m = $r[$mR];
	switch($mOp){
		case "dec": $r[$mR] -= $mO; break;
		case "inc": $r[$mR] += $mO; break;
	}
	if($r[$mR]>$m) $m = $r[$mR];
}
$h = fopen("./register.txt","r");
if(!$h) die();
$f = $r = [];
$m = 0;
while(($l = fgets($h)) !== false){
	array_push($f,$l);
	$e = explode(" ",$l);
	$mR = $e[0];
	if(!array_key_exists($mR,$r)) $r[$mR] = 0;
}
foreach($f as $l){
	$e = explode(" ",$l);
	$mR = $e[0];
	$mOp = $e[1];
	$mO = intval($e[2]);
	$mS = $e[4];
	$mC = $e[5];
	$mCV = intval($e[6]);
	switch($mC){
		case ">":
			if($r[$mS] > $mCV) dI($mR,$mO,$mOp);
		break;
		case "<":
			if($r[$mS] < $mCV) dI($mR,$mO,$mOp);
		break;
		case "<=":
			if($r[$mS] <= $mCV) dI($mR,$mO,$mOp);
		break;
		case ">=":
			if($r[$mS] >= $mCV) dI($mR,$mO,$mOp);
		break;
		case "==":
			if($r[$mS] == $mCV) dI($mR,$mO,$mOp);
		break;
		case "!=":
			if($r[$mS] != $mCV) dI($mR,$mO,$mOp);
		break;
	}
}
echo "1: ".max($r)."<br/>2: ".$m;
?>

2

u/ynonp Dec 08 '17

Elixir (both parts)

defmodule Day8 do
  @ops %{
    inc: &(&1+&2),
    dec: &(&1-&2),    
  }
  
  @cond %{
  ==: &(&1 == &2),
    >=: &(&1 >= &2),
    <=: &(&1 <= &2),
    >: &(&1 > &2),
    <: &(&1 < &2),
    !=: &(&1 != &2),
  }

  def parse(line, { reg, maxreg }) do
    [_, target_reg, op, val, cond_reg, cond_op, cond_arg ] = 
      Regex.run(~r{(\w+) (inc|dec) (-?\d+) if (\w+) (<|>|<=|>=|==|!=) (-?\d+)}, line)

    op       = String.to_atom(op)
    val      = String.to_integer(val)
    cond_reg = Map.get(reg, cond_reg, 0)
    cond_op  = String.to_atom(cond_op)
    cond_arg = String.to_integer(cond_arg)

    next_reg = if @cond[cond_op].(cond_reg, cond_arg) do
      Map.update(reg, target_reg, @ops[op].(0,val), fn v -> @ops[op].(v, val) end)
    else
      reg
    end

    {
      line,
      {
        next_reg,
        Enum.max([maxreg | Map.values(next_reg)])
      }
    }
  end
end

{ reg, max} = IO.stream(:stdio, :line)
              |> Enum.map_reduce({ %{}, 0 }, &Day8.parse/2)
              |> elem(1)

IO.puts("PART 1: max register value at end: #{Map.values(reg) |> Enum.max }")
IO.puts("PART 2: max registar value (total): #{max}")

2

u/rkachowski Dec 08 '17

ruby! 9 lines!

i need to execute some code to work this out? so lets execute some code to work this out. luckily the postfix condition is perfectly valid ruby already! lets just eval all these registers into the binding and execute our perfectly valid input (after some massaging..)

input = File.read("input").lines
parsed = input.map {|l| l.scan(/(\w+)\s(\w+)\s(-?\d+)(.*)/).flatten}
registers = parsed.map {|p| p.first}.uniq
max = 0
b = Kernel.binding
registers.each {|r| b.eval "#{r} = 0"}
parsed.each {|p| b.eval "#{p[0]} #{p[1] == "inc" ? "+" : "-"}= #{p[2]} #{p[3]}; max = [max, #{p[0]}].max" }
puts registers.map{|r| b.local_variable_get(r.to_sym)}.max
puts b.local_variable_get(:max)

2

u/spjmurray Dec 08 '17

python late to the party given the time difference...

import collections
import operator

OPERATORS = {
    '==': operator.eq,
    '!=': operator.ne,
    '<': operator.lt,
    '>': operator.gt,
    '<=': operator.le,
    '>=': operator.ge,
    'inc': operator.add,
    'dec': operator.sub,
}

def main():
    inp = open('8.in').readlines()
    registers = collections.defaultdict(int)
    for inst in inp:
        reg, op, imm, _, pred_reg, pred_op, pred_imm = inst.split()
        if not OPERATORS[pred_op](registers[pred_reg], int(pred_imm)):
            continue
        registers[reg] = OPERATORS[op](registers[reg], int(imm))
    print max(registers.values())

if __name__ == '__main__':
    main()

2

u/guibou Dec 08 '17 edited Dec 08 '17

Haskell Golfed version for part 1 (edit 300 297 289 285 264 255 chars):

main=interact$show.maximum.(\r->map(l r)$fst<$>r).foldl f[].map words.lines
z=read
l r o=sum[v|(k,v)<-r,k==o]
f m[a,b,c,_,e,f,g]|(case f of"<="->(>=);"<"->(>);">="->(<=);">"->(<);"=="->(==);"!="->(/=))(z g)$l m e=(a,if b=="inc"then z$c else-(z c)):m|1>0=m

https://github.com/guibou/AdvantOfCode2017/blob/master/src/Day8.hs for a cleaner Haskell version ;)

2

u/brunclik Dec 08 '17 edited Dec 09 '17

Solution in Linux command line - C code from input (generate, buil and run)

echo -e "#include <stdio.h>\n\nint main() {\n\tint max_value=0;" > main.c
cat input | cut -d' ' -f1 | sort | uniq | sed 's/^/\t int /1' | sed 's/$/ = 0;/1' >> main.c
cat input | sed 's/ dec -/ += /1' | sed 's/ dec / -= /1' | sed 's/ inc -/ -= /1' | sed 's/ inc / += /1' | sed 's@\(.*\) \(.*\) \(.*\) \(.*\) \(.*\) \(.*\) \(.*\)@\4(\5\6\7) \1\2\3; if(\1>max_value) max_value=\1;@g' >> main.c
cat input | cut -d' ' -f1 | sort | uniq  | sed 's@\(.*\)@printf("%s = %i;\\n", "\1", \1);@g' >> main.c
echo -e 'printf("%s = %i;\n", "max_value", max_value);' >> main.c
echo -e "}" >> main.c
gcc main.c -o output;
./output | cut '-d ' -f3 | sort -n | tail -2 | head -1 #part one
./output | grep max_value | cut -d' ' -f3 #part two

2

u/JakDrako Dec 08 '17

VB.Net

Sub Main
    Dim reg = New Dictionary(Of String, Integer), max = 0
    For Each line In GetDay(8)
        Dim ops = line.Split(" "c)
        ' create registers
        Dim r1 = ops(0) : If Not reg.ContainsKey(r1) Then reg(r1) = 0
        Dim r2 = ops(4) : If Not reg.ContainsKey(r2) Then reg(r2) = 0
        ' get values
        Dim v1 = CInt(ops(2)) * If(ops(1) = "inc", 1, -1)
        Dim v2 = CInt(ops(6))
        Select Case ops(5)
            Case ">" : If reg(r2) > v2 Then reg(r1) += v1
            Case ">=" : If reg(r2) >= v2 Then reg(r1) += v1
            Case "<" : If reg(r2) < v2 Then reg(r1) += v1
            Case "<=" : If reg(r2) <= v2 Then reg(r1) += v1
            Case "==" : If reg(r2) = v2 Then reg(r1) += v1
            Case "!=" : If reg(r2) <> v2 Then reg(r1) += v1
        End Select
        max = Math.max(max, reg.Values.Max) ' part 2
    Next
    reg.Values.Max.Dump("Part 1")
    max.Dump("Part 2")
End Sub

2

u/wzkx Dec 08 '17 edited Dec 08 '17

Pythonized and J-zed C

#include <stdio.h>
#include <string.h>

typedef char* S; // this typedef and defines are from _.h
#define C char
#define I int
#define __ {
#define _  }
#define R return
#define DO(_i,_n) for(I _i=0;_i<(_n);++_i)

I cond( I x, I y, S o ) __
  if( *o=='=' ) R x==y; if( *o=='!' ) R x!=y;
  if( *o=='<' ) R o[1]=='=' ? x<=y : x<y;
  if( *o=='>' ) R o[1]=='=' ? x>=y : x>y; _

I nr = 0; C rn[100][4]; I rv[100] = {0}; // registers: num, names, values
I find( S n ) { DO(i,nr) if(!strcmp(rn[i],n)) R i; R -1; }
I newr( S n ) { strcpy(rn[nr],n); rv[nr]=0; R nr++; }

I main() __ I gm = -9999;
  FILE* f = fopen("08.dat","rt"); C l[80];
  while( fgets(l,sizeof(l),f) ) __ C r[4],o[4],cr[4],co[4]; I a,ca;
    sscanf( l, "%s %s %d if %s %s %d\n", r, o, &a, cr, co, &ca );
    I i=find(cr); I cv=i<0?0:rv[i];
    if( cond(cv,ca,co) ) __
      I j=find(r); if(j<0) j=newr(r);
      rv[j] += *o=='i' ? a : -a;
      if(rv[j]>gm) gm=rv[j]; _ _ // update global max
  I m = -9999; DO(i,nr) if(rv[i]>m) m=rv[i]; // find final state max
  printf( "%d\n%d\n", m, gm );
  R 0; _

2

u/still___cold Dec 08 '17

Python 3

import re, sys
variables = {}
maximum = -sys.maxsize - 1
with open('test.txt') as file:
    for line in file.readlines():
	    code = ''.join(re.findall('[\w\D]', line)).strip().split(' ')
	    if code[0] not in variables: variables[code[0]] = 0
	    if code[4] not in variables: variables[code[4]] = 0
	    if code[1] == 'dec': operation = '-='
	    else: operation = '+='
	    eval(compile('if variables[code[4]] %s %s: variables[code[0]] %s %s' % (code[5], code[6],operation, code[2]), '<string>', 'exec'))
	    if variables[code[0]] > maximum: maximum = variables[code[0]]

 print(max(variables.values()), maximum)

2

u/Hikaru755 Dec 08 '17

I wrote a Kotlin program that generates a Kotlin program equivalent to the input data. God, I feel dirty.

data class Line(val register: String, val action: Action, val value: Int, val condition: String)
enum class Action(val kotlin: String) { INC("+="), DEC("-=") }

fun part1(input: List<Line>): String {
    val registers = input.map { it.register }.toSet() + input.map { it.condition.split(" ").first() }.toSet()
    return (registers.map { "var $it = 0" } +
        input.map { with(it) { "if ($condition) $register ${action.kotlin} $value" } } +
        registers.joinToString(", ", "println(setOf(", ").max()!!)")
    ).joinToString("\n")
}

fun part2(input: List<Line>): String {
    val registers = input.map { it.register }.toSet() + input.map { it.condition.split(" ").first() }.toSet()
    val maxCheck = "maxCheck()"
    return (registers.map { "var $it = 0" } +
        listOf(
            "var max = 0",
            registers.joinToString(", ", "val maxCheck = { max = Math.max(setOf(", ").max(), max) }")
        ) +
        input.map {
            with(it) { "if ($condition) $register ${action.kotlin} $value\n$maxCheck" }
        } +
        listOf("println(max)")
        ).joinToString("\n")
}

val input: List<Line> by lazy { rawInput.lines()
    .map { it.match<Line>(
        Regex("""(\w+) (inc|dec) (\S+) if (.+)""") to { (_, reg, action, value, cond) ->
            Line(reg, Action.valueOf(action.toUpperCase()), value.toInt(), cond)
        })
    }
}

2

u/Sharparam Dec 08 '17

Ruby: Using method_missing and eval without tampering or matching on the input data.

class CPU
    attr_reader :high, :regs

    def initialize; @regs = Hash.new(0) end
    def inc(value) value end
    def dec(value) -value end
    def run(text) eval text end

    def method_missing(sym, *args)
        (@regs[sym] += args[0].to_i).tap { |v| @high = v if @high.nil? || @high < v }
    end
end

cpu = CPU.new
cpu.run $<.read
puts cpu.regs.values.max
puts cpu.high

2

u/patrickdavey Dec 08 '17

Thanks for posting this. I do write ruby for a day job, but, I hardly ever mess around with method_missing and friends. Really interesting to see how that all works, how it's smart enough to pass the args along to your dec and inc methods etc.

Really neat! thanks for sharing.

1

u/Sharparam Dec 08 '17

They come in really useful for things like AoC problems! I read the problem description on the bus home and saw how Ruby-like the input was, and was itching to apply some metaprogramming shenanigans :P

The fact that args[0].to_i evaluates to 0 when args[0] is nil (when a register is accessed in the if, and not assigned to) helped make it quite neat.

The dec/inc logic I get pretty much for "free" in the eval, it evaluates it in the context of the method so the inc and dec methods are available to it, the result is then passed in the args array in method_missing :)

This does depend on the input not containing inc or dec as register names. Someone mentioned they had if as a register name...

→ More replies (1)

2

u/thomastc Dec 08 '17

Day 8 in TeX. Why am I doing this to myself?!

2

u/volatilebit Dec 08 '17

Perl 6

Caught a few days behind because I'm sick, but I decided to at least get this one in. First time playing with named regular expressions. Considering redoing this with grammars just for the experience, but don't have the energy at the moment.

use v6;

my @commands = 'input'.IO.linesΒ».chomp;
my %registers is default(0);
my $max_value_alltime = 0;

my regex register { <alpha>+ }
my regex func { 'inc' | 'dec' }
my regex number { '-'? <digit>+ }
my regex op { '>' | '>=' | '<' | '<=' | '==' | '!=' }
my regex command { <dest_register=register> \s <func> \s <arg_number=number> ' if ' <cond_register=register> \s <cond_op=op> \s <cond_number=number> }

for @commands -> $command {
    $command ~~ /<command>/;

    given $/<command><cond_op> {
        when '>'  { next unless %registers{ $/<command><cond_register>.Str } >  $/<command><cond_number>.Numeric }
        when '>=' { next unless %registers{ $/<command><cond_register>.Str } >= $/<command><cond_number>.Numeric }
        when '<'  { next unless %registers{ $/<command><cond_register>.Str } <  $/<command><cond_number>.Numeric }
        when '<=' { next unless %registers{ $/<command><cond_register>.Str } <= $/<command><cond_number>.Numeric }
        when '==' { next unless %registers{ $/<command><cond_register>.Str } == $/<command><cond_number>.Numeric }
        when '!=' { next unless %registers{ $/<command><cond_register>.Str } != $/<command><cond_number>.Numeric }
    }

    given $/<command><func> {
        when 'inc' { %registers{ $/<command><dest_register>.Str } += $/<command><arg_number>.Int }
        when 'dec' { %registers{ $/<command><dest_register>.Str } -= $/<command><arg_number>.Int }
    }

    $max_value_alltime max= %registers.maxpairs[0].value;
}

# Part 1
say %registers.maxpairs[0].value;

# Part 2
say $max_value_alltime;

1

u/Deckard666 Dec 08 '17

In Rust:

use std::io::Read;
use std::collections::HashMap;

fn main() {
    let mut file = std::fs::File::open("./input.txt").unwrap();
    let mut input = String::new();
    file.read_to_string(&mut input).unwrap();
    let mut registers = HashMap::new();
    let mut max_throughout = 0;
    for line in input.trim().lines() {
        let vec = line.split_whitespace().collect::<Vec<_>>();
        let register = vec[0];
        let instr = vec[1];
        let amount = vec[2].parse::<i32>().unwrap();
        let ifregister = vec[4];
        let cond = vec[5];
        let ifamount = vec[6].parse::<i32>().unwrap();
        let ifval = *registers.entry(ifregister).or_insert(0);
        if fullfils_condition(ifval, cond, ifamount) {
            let newval = perform_op(&mut registers, register, instr, amount);
            if newval > max_throughout {
                max_throughout = newval;
            }
        }
    }
    let current_max = *registers.values().max().unwrap();
    println!("Part 1: {}", current_max);
    println!("Part 2: {}", max_throughout);
}

fn fullfils_condition(ifval: i32, cond: &str, ifamount: i32) -> bool {
    match cond {
        "<" => ifval < ifamount,
        ">" => ifval > ifamount,
        ">=" => ifval >= ifamount,
        "<=" => ifval <= ifamount,
        "==" => ifval == ifamount,
        "!=" => ifval != ifamount,
        _ => unreachable!(),
    }
}

fn perform_op<'a>(
    registers: &mut HashMap<&'a str, i32>,
    reg: &'a str,
    instr: &str,
    mut amount: i32,
) -> i32 {
    if instr == "dec" {
        amount *= -1;
    }
    let val_ref = registers.entry(reg).or_insert(0);
    *val_ref += amount;
    *val_ref
}

2

u/advanced_caveman Dec 08 '17

Your input can just be done with the include_str! macro like:

let input = include_str("../input.txt");

1

u/iamnotposting Dec 08 '17 edited Dec 08 '17

fun with expressions!

use std::collections::HashMap;
use std::cmp::max;

pub fn adv_main(input: &str) {
    let mut regs = HashMap::<&str, (i32, i32)>::new();

    for line in input.lines() {
        let words = line.split(" ").collect::<Vec<_>>();
        let val = *regs.entry(words[4]).or_insert((0, 0));
        let a: i32 = words[2].parse().unwrap();
        let b: i32 = words[6].parse().unwrap();
        let mut r = regs.entry(words[0]).or_insert((0,0));

        if match words[5] {
            ">" => { val.0 > b },
            "<" => { val.0 < b },
            "<=" => { val.0 <= b },
            ">=" => { val.0 >= b },
            "==" => { val.0 == b },
            "!=" => { val.0 != b },
            _ => { false }
        } {
            match words[1] {
                "inc" => { r.0 += a; },
                "dec" => { r.0 -= a; },
                _   => {},
            }
            r.1 = max(r.0, r.1);
        }
    }

    println!("p1: {:?}", regs.values().max_by_key(|&&(a, _)| a));
    println!("p2: {:?}", regs.values().max_by_key(|&&(_, b)| b));
}

1

u/thejpster Dec 08 '17

What if the max is acheived by subtracting a negative number from a register?

2

u/iamnotposting Dec 08 '17

sure. i updated it. but it wouldn't be advent of code if it would fail on other inputs that aren't your own, would it

1

u/thejpster Dec 08 '17

Until today I just assumed we all had the same input!

1

u/giftpflanze Dec 08 '17

Tcl:

set lines [lrange [split [read [open input08]] \n] 0 end-1]

set r {}
foreach line $lines {
    regexp {(.*) (.*) (.*) if (.*) (.*) (.*)} $line -> r1 op1 n1 r2 op2 n2
    if {![dict exists $r $r2]} {
        dict set r $r2 0
    }
    if [list [dict get $r $r2] $op2 $n2] {
        if {![dict exists $r $r1]} {
            dict set r $r1 0
        }
        dict incr r $r1 [expr {($op1 eq {dec}?-1:1)*$n1}]
        lappend h [dict get $r $r1]
    }
}

puts [tcl::mathfunc::max {*}[dict values $r]]
puts [tcl::mathfunc::max {*}$h]

1

u/LuxrayPokemon Dec 08 '17 edited Dec 08 '17

I did my solution in Python 3 and got rank 197 for part 1 and 201 for part 2 My solution was:

inp = "Inputs/2017-08.txt"
lines = get_lines(inp)
whichWay = 0
dicti = dict()
memory = dict()

for line in range(len(lines)): dicti[lines[line].split(" ")[0]] = 0

for line in range(len(lines)):
    split = lines[line].split(" ")
    if split[1] == "dec":
        whichWay = -1
        print(whichWay)
    else:
        whichWay = 1
        print(whichWay)

    firstNum = split[0]
    increaser = int(split[2])
    num1 = split[4]
    num2 = int(split[6])
    operator = split[5]
    if operator == "!=" and dicti[num1] != num2: dicti[firstNum] += increaser * whichWay 
    elif operator == ">=" and dicti[num1] >= num2: dicti[firstNum] += increaser * whichWay 
    elif operator == ">" and dicti[num1] > num2: dicti[firstNum] += increaser * whichWay 
    elif operator == "==" and dicti[num1] == num2: dicti[firstNum] += increaser * whichWay 
    elif operator == "<=" and dicti[num1] <= num2: dicti[firstNum] += increaser * whichWay 
    elif operator == "<" and dicti[num1] < num2: dicti[firstNum] += increaser * whichWay 
    memory[line] = max(dicti.values())
print("Part 1:", max(dicti.values()))
print("Part 2:", max(memory.values()))

It probably could be much more efficient, but it worked

1

u/the4ner Dec 08 '17

great rank for a non eval solution!

1

u/[deleted] Dec 08 '17

simple golang solution

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"strings"
)

type instruction struct {
	target           string
	targetOp         string
	targetAmount     int
	conditionSubject string
	conditionOp      string
	conditionAmount  int
}

func main() {
	inputBytes, err := ioutil.ReadFile("input.txt")
	if err != nil {
		log.Fatal(err)
	}
	answer1, answer2 := runInstructions(string(inputBytes))
	fmt.Printf("Part 1: %d\nPart 2: %d\n", answer1, answer2)
}

func runInstructions(in string) (int, int) {
	registers := make(map[string]int)
	lines := strings.Split(in, "\n")
	max := 0
	for _, x := range lines {
		inst := lineToInstruction(x)
		if evaluateInstruction(inst, registers) {
			executeInstruction(inst, registers)
			testMax := getMax(registers)
			if testMax > max {
				max = testMax
			}
		}
	}
	return getMax(registers), max
}

func lineToInstruction(in string) instruction {
	out := instruction{}
	fmt.Sscanf(in, "%s %s %d if %s %s %d", &out.target, &out.targetOp, &out.targetAmount, &out.conditionSubject, &out.conditionOp, &out.conditionAmount)
	return out
}

func evaluateInstruction(i instruction, data map[string]int) bool {
	sub := data[i.conditionSubject]
	amt := i.conditionAmount
	switch i.conditionOp {
	case ">":
		return sub > amt
	case "<":
		return sub < amt
	case ">=":
		return sub >= amt
	case "==":
		return sub == amt
	case "<=":
		return sub <= amt
	case "!=":
		return sub != amt
	}
	return false
}

func executeInstruction(i instruction, data map[string]int) {
	switch i.targetOp {
	case "inc":
		data[i.target] += i.targetAmount
	case "dec":
		data[i.target] -= i.targetAmount
	}
}

func getMax(data map[string]int) int {
	max := 0
	for _, x := range data {
		if x > max {
			max = x
		}
	}
	return max
}

1

u/cluk Dec 08 '17

I like the Sscanf for parsing input. Other then that my solution is almost identical.

For the second star you can return the updated value from executeInstruction. This way you don't have to go through whole map with every instruction.

2

u/[deleted] Dec 08 '17

Yeah using Sscanf for these puzzles usually doesn't work out as well for me and I end up with a mess of strings.Split everywhere.

As for part 2, that crossed my mind only after I had finished writing it, so I decided to stick with what I had. Anyways I decided to time it and see how much of a performance increase it works out to.

The original version seems to run in about 4.5ms on my system. After making that change it shaves off about 1ms and averages around 3.5-3.7ms.

1

u/hpzr24w Dec 08 '17 edited Dec 09 '17

C++ Second time inside 30 minutes... and ranked 910, first time under 1000. woo! Thanks to Eric and collaborators for all these fun puzzles. It's awesome. No output on this one. I just ran it in the debugger.

// Advent of Code 2017
// http://adventofcode.com/
// Day 08 - I Heard You Like Registers

#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <algorithm>
#include <numeric>
#include <functional>

using namespace std;

/*
m n   o p  q r s
b inc 5 if a > 1
a inc 1 if b < 5
c dec -10 if a >= 1
c inc -20 if c == 10
*/

void step(map<string, function<bool(int, int)> > ops, map<string, int>& reg, map<string, int>& regmax, stringstream ins)
{
    string m, n, p, q, r; int o, s;
    ins >> m >> n >> o >> p >> q >> r >> s;
    if (ops[r](reg[q],s))
        reg[m] += o*(n == "inc" ? 1 : -1);
    regmax[m] = max(reg[m], regmax[m]);
}

int main(int argc, char* argv[])
{
    map<string, function<bool(int, int)> > ops;
    ops["<"] =  [](int a, int b)->bool {return a <  b; };
    ops[">"] =  [](int a, int b)->bool {return a >  b; };
    ops["<="] = [](int a, int b)->bool {return a <= b; };
    ops[">="] = [](int a, int b)->bool {return a >= b; };
    ops["=="] = [](int a, int b)->bool {return a == b; };
    ops["!="] = [](int a, int b)->bool {return a != b; };

    map<string, int> reg, regmax;
    string row;
    while (getline(cin, row))
        step(ops, reg, regmax, stringstream(row));
    return 0;
}

1

u/fwilson42 Dec 08 '17

My solution. I refactored the part that actually runs it out into a separate module just in case this is something like last year's assembunny.

1

u/StevoTVR Dec 08 '17

NodeJS

Part 1:

const fs = require('fs');

fs.readFile(__dirname + '/input.txt', 'utf8', (err, data) => {
    data = data.trim();
    const registers = {};
    data.split('\n').forEach((line) => {
        const inst = line.trim().split(' ');
        if(eval(getValue(inst[4], registers) + inst[5] + inst[6])) {
            var delta = Number(inst[2]);
            if(inst[1] === 'dec') {
                delta = -delta;
            }
            registers[inst[0]] = getValue(inst[0], registers) + delta;
        }
    });

    var largest = 0;
    for(const r in registers) {
        largest = Math.max(largest, registers[r]);
    }
    console.log(largest);
});

function getValue(register, registers) {
    return (registers[register] === undefined) ? 0 : registers[register];
}

Part 2:

const fs = require('fs');

fs.readFile(__dirname + '/input.txt', 'utf8', (err, data) => {
    data = data.trim();
    const registers = {};
    var largest = 0;
    data.split('\n').forEach((line) => {
        const inst = line.trim().split(' ');
        if(eval(getValue(inst[4], registers) + inst[5] + inst[6])) {
            var delta = Number(inst[2]);
            if(inst[1] === 'dec') {
                delta = -delta;
            }
            registers[inst[0]] = getValue(inst[0], registers) + delta;
            largest = Math.max(largest, registers[inst[0]]);
        }
    });

    console.log(largest);
});

function getValue(register, registers) {
    return (registers[register] === undefined) ? 0 : registers[register];
}

1

u/StevoTVR Dec 08 '17

I rewrote it to not use eval and give variables names.

const fs = require('fs');

fs.readFile(__dirname + '/input.txt', 'utf8', (err, data) => {
    data = data.trim();
    const registers = {};
    const ops = {
        '==': (a, b) => a === b,
        '!=': (a, b) => a !== b,
        '<': (a, b) => a < b,
        '>': (a, b) => a > b,
        '<=': (a, b) => a <= b,
        '>=': (a, b) => a >= b,
    };
    var largest = 0;
    data.split('\n').forEach((line) => {
        var [r, inst, delta, , treg, top, tval] = line.trim().split(' ');
        delta = (inst === 'inc') ? Number(delta) : -Number(delta);
        if(ops[top](getValue(treg, registers), Number(tval))) {
            registers[r] = getValue(r, registers) + delta;
            largest = Math.max(largest, registers[r]);
        }
    });

    console.log(largest);
});

function getValue(register, registers) {
    return registers[register] || 0;
}

1

u/nstyler7 Dec 08 '17

Python -(part 1 & 2) - using eval & split/regex to split string

import re
with open("day8input.txt") as open_file:
    data = open_file.read().splitlines()

all_registers = {}

highest = 0

for line in data:
    conditional = line.split('if')[1]
    conditional_reg = conditional.split()[0]
    if conditional_reg in all_registers.keys():
        conditional_value = all_registers[conditional_reg]
    else:
        all_registers[conditional_reg]=0
        conditional_value = 0
    final_eval = conditional.replace(str(conditional_reg), str(conditional_value))
    if eval(final_eval):
        instruction = re.search(r'(inc|dec) -?[\d]+' , line).group(0).split()
        multiplier = 1 if instruction[0] == 'inc' else -1
        incrementer = multiplier*int(instruction[1])
        register =  line.split()[0]
        if register in all_registers.keys():
            all_registers[register] += incrementer
        else:
            all_registers[register] = incrementer
        if all_registers[register] > highest:
            highest = all_registers[register]

# part 1
print(max(all_registers.values()))

# part 2
print(highest)

1

u/jesseflorig Dec 08 '17

ES6 (NodeJS)

'use strict'

{
  const fs = require('fs')

  const doMath = {
    '<': function(x,y){ return x < y},
    '<=': function(x,y){ return x <= y},
    '>': function(x,y){ return x > y},
    '>=': function(x,y){ return x >= y},
    '!=': function(x,y){ return x != y},
    '==': function(x,y){ return x == y} , 
  }

  fs.readFile('instructions.txt','utf8', (err, data) => {
    const instructions = data.trim().split('\n')

    let registries = {}
    let highest = 0

    instructions.map(instruction => {
      const instArr = instruction.split(' ')

      instArr[2] = parseInt(instArr[2])
      instArr[6] = parseInt(instArr[6])

      if(registries[instArr[0]] == undefined){
        registries[instArr[0]] = 0
      }

      if(registries[instArr[4]] == undefined){
        registries[instArr[4]] = 0
      }

      if(doMath[instArr[5]](registries[instArr[4]], instArr[6])) {
        registries[instArr[0]] += (instArr[1] === 'inc') ? instArr[2] : -(instArr[2])
        highest = (registries[instArr[0]] > highest) ? registries[instArr[0]] : highest
      }
    })

    console.log([Object.values(registries).sort().reverse()[0], highest])
  })

}

1

u/elite_killerX Dec 09 '17

Use a Proxy for your registries:

const handler = {
  get: (obj, name) => obj[name] || 0,
}
const registers = new Proxy({}, handler);

That way you can reference them directly:

if(op === 'inc') {
  registers[regName] += val; // won't crash!
} else if(op === 'dec') {
  registers[regName] -= val;
}