Chellenge 10

Challenge 10.1


In [1]:
myinput = '/home/fmuinos/projects/adventofcode/2016/ferran/inputs/input10.txt'

In [2]:
! wc -l inputs/input10.txt


231 inputs/input10.txt

In [110]:
import re

def parse_bot_commands(myinput):
    handle_dict = {}
    handle_dict['input'] = set()
    handle_dict['give'] = {}
    with open(myinput, 'rt') as f:
        for line in f:
            match = re.findall('(value \d+|bot \d+|output \d+)', line)
            if len(match) == 2:
                value = int(match[0][6:])
                bot = int(match[1][4:]) + 1
                handle_dict['input'].add((bot, value))
            if len(match) == 3:
                bot = int(match[0][4:]) + 1
                lo = (int(match[1].split()[1]) + 1) * (1 - 2 * (match[1].split()[0] == 'output'))
                hi = (int(match[2].split()[1]) + 1) * (1 - 2 * (match[2].split()[0] == 'output'))
                handle_dict['give'][bot] = (lo, hi)
    return handle_dict

For the sake of a more compact notation to distinguish them, Bots and Outputs IDs have been re-numbered +1, then Ouput IDs have been made negative integers. The addition +1 at the beginning prevents confusion between bot 0 and output 0.


In [194]:
class Bot(object):
    
    def __init__(self, ID):
        self.ID = ID
        self.status = 'not-full'
        self.load = set()
    
    def update_status(self):
        if len(self.load) < 2:
            self.status = 'not-full'
        else:
            self.status = 'full'
    
    def get(self, chip):
        if chip is None:
            pass
        elif self.status == 'not-full':
            self.load.add(chip)
            self.update_status()

    def request(self, botlist, ID):
        if ID < 0:
            return True
        elif botlist[ID-1].status == 'not-full':
            return True
        elif botlist[ID-1].status == 'full':
            return False
    
    def give(self, botlist, lower, higher):
        lo = None
        hi = None
        if self.status == 'full':
            if self.request(botlist, lower):
                lo = min(self.load)
                self.load.remove(lo)
            if self.request(botlist, higher):
                hi = max(self.load)
                self.load.remove(hi)
            self.update_status()
        return lo, hi


class BotSwarm(object):
    
    def __init__(self, size):
        self.size = size
        self.swarm = [None] * size
        for ind in range(size):
            self.swarm[ind] = Bot(ind+1)
        self.output = [None] * 21
    
    def initialize(self, handle_dict):
        for tupl in handle_dict['input']:
            receiver, value = tupl[0], tupl[1]
            self.swarm[receiver-1].get(value)
    
    def update(self, handle_dict):
        for tpl in handle_dict['give'].items():
            giver, lower, higher = tpl[0], tpl[1][0], tpl[1][1]
            lo, hi = self.swarm[giver-1].give(self.swarm, lower, higher)
            self.swarm[lower-1].get(lo)
            self.swarm[higher-1].get(hi)
            if (lower < 0) and (lo is not None):
                self.output[-lower-1] = lo
            if (higher < 0) and (hi is not None):
                self.output[-higher-1] = hi
            
    def who(self, chip):
        for i in range(self.size):
            if chip in self.swarm[i].load:
                return self.swarm[i].ID

In [195]:
handle_dict = parse_bot_commands(myinput)
community = BotSwarm(210)
community.initialize(handle_dict)
while community.who(17) != community.who(61):
    community.update(handle_dict)
print(community.who(17))


74

Since we had +1 the Bot labels for convenience, the sought answer is 73.

Challenge 10.2


In [201]:
handle_dict = parse_bot_commands(myinput)
community = BotSwarm(210)
community.initialize(handle_dict)
while (community.output[0] is None) or (community.output[1] is None) or (community.output[2] is None):
    community.update(handle_dict)
print(community.output[:3])
community.output[0]*community.output[1]*community.output[2]


[5, 13, 61]
Out[201]:
3965