Pour la première partie, je fais des jolies eval avec des fonction lambda, pour avoir une résolution simple et élégante.
fromsysimportstdinfromdataclassesimportdataclassdata=stdin.read().strip().splitlines()classWorkflow:def__init__(self,x):defdest(r):ifr=='A':returnNone# No more rules, acceptedifr=='R':returnFalse# Rejectedreturnr# Next ruleself.name,rules=line.strip("}").split("{")rules=[_.split(":")for_inrules.split(",")]self.default=dest(rules.pop()[-1])self.rules={eval(f"lambda x: x.{t}"):dest(d)fort,dinrules}self.infos=[(t[0],t[1],int(t[2:]),dest(d))fort,dinrules]# for ex2def__call__(self,part):# Applying this whole workflow to a partforrule,dstinself.rules.items():ifrule(part):returndstreturnself.default@dataclass(frozen=True)classParts:x:int=0m:int=0a:int=0s:int=0@propertydefval(self):returnself.x+self.m+self.a+self.sdef__bool__(self):# Following workflows until accepted or rejectednext='in'whilenext:# Neither None nor Falsenext=workflows[next](self)returnnextisNone# Acepted# Data analysisworkflows={}parts=Falseforlineindata:ifnotline:parts=[]continueifpartsisFalse:w=Workflow(line)workflows[w.name]=welse:parts.append(eval(f"Parts({line[1:-1]})"))# 1st problemex1=sum(part.valforpartinpartsifpart)
Pour le second, je suis tombé dans le piège : < et > ne sont pas l'inverse l'un de l'autre, j'ai souffert pour voir ça :(
Là on considère des range, des intervalles. Comme on code en python, [1, 4000] ça va être range(1, 4001), d'où les 4001 dans le code.
Pour l'algo, je considère un état : 4 ranges pour x, m, s et a, un workflow et une étape dans le workflow, ce qui donne donc une règle (rule).
Je prends mes états courants, je leur applique une règle ce qui va diviser chacun de ces états en au mieux deux états : celui qui respecte la règle en cours, et celui qui ne la respecte pas. Chacun progressant, soit vers une nouvelle règle, soit vers l'étape suivante du workflow.
Je trie les états ayant une règle à appliquer pour mon itération suivante, et je garde de côté les états étant au stade accepté. Les rejetés sont donc abandonnés là.
Après il reste à bien lire l'énoncé : non, on ne cherche pas la valeur de toutes les pièces qui sont valables, mais juste à les dénombrer, donc il suffit de multiplier entre eux les tailles des 4 intervalles d'un état accepté.
Comme on découpe toujours en deux morceaux disjoints, nos états ne se chevauchent jamais, donc on peut tous les dénombrer et additionner.
@dataclassclassPartRange:x:tuple=(1,4001)m:tuple=(1,4001)a:tuple=(1,4001)s:tuple=(1,4001)rule:str='in'step:int=0defsplit(self):rule=workflows[self.rule]ifself.step==len(rule.infos):self.rule,self.step=rule.default,0yieldselfreturnkey,test,num,dest=rule.infos[self.step]iftest==">":# ...num+=1a,b=getattr(self,key)ifnum>a:# range before numnew=self.__dict__.copy()new[key]=(a,num)new["rule"],new["step"]=(dest,0)iftest=="<"else(self.rule,self.step+1)yieldPartRange(**new)ifnum<=b:# range after numnew=self.__dict__.copy()new[key]=(num,b)new["rule"],new["step"]=(dest,0)iftest==">"else(self.rule,self.step+1)yieldPartRange(**new)@propertydefvalue(self):x=len(range(*self.x))m=len(range(*self.m))a=len(range(*self.a))s=len(range(*self.s))returnx*m*a*sparts=[PartRange()]accepted=[]whileparts:parts=[partfor_inpartsforpartin_.split()]accepted.extend(_for_inpartsif_.ruleisNone)# Saving acceptedparts=[_for_inpartsif_.rule]# Pruning accepted and rejectedex2=sum(_.valuefor_inaccepted)
Le temps d'exécution est négligeable, ~90ms.
J'ai quand même pas été super rapide pour débugger mes âneries.
# On aime les range, youpi, youpi !
Posté par Yth (Mastodon) . En réponse au message Advent of Code, jour 19. Évalué à 3.
Pour la première partie, je fais des jolies eval avec des fonction lambda, pour avoir une résolution simple et élégante.
Pour le second, je suis tombé dans le piège : < et > ne sont pas l'inverse l'un de l'autre, j'ai souffert pour voir ça :(
Là on considère des
range, des intervalles. Comme on code en python,[1, 4000]ça va êtrerange(1, 4001), d'où les 4001 dans le code.Pour l'algo, je considère un état : 4 ranges pour x, m, s et a, un workflow et une étape dans le workflow, ce qui donne donc une règle (rule).
Je prends mes états courants, je leur applique une règle ce qui va diviser chacun de ces états en au mieux deux états : celui qui respecte la règle en cours, et celui qui ne la respecte pas. Chacun progressant, soit vers une nouvelle règle, soit vers l'étape suivante du workflow.
Je trie les états ayant une règle à appliquer pour mon itération suivante, et je garde de côté les états étant au stade accepté. Les rejetés sont donc abandonnés là.
Après il reste à bien lire l'énoncé : non, on ne cherche pas la valeur de toutes les pièces qui sont valables, mais juste à les dénombrer, donc il suffit de multiplier entre eux les tailles des 4 intervalles d'un état accepté.
Comme on découpe toujours en deux morceaux disjoints, nos états ne se chevauchent jamais, donc on peut tous les dénombrer et additionner.
Le temps d'exécution est négligeable, ~90ms.
J'ai quand même pas été super rapide pour débugger mes âneries.
Yth.