Bon, fait maison, c'est à dire sans eval ni bibliothèque externe. On construit un arbre, pour la première partie on calcule récursivement la valeur du singe racine. Pour la deuxième partie, on force la valeur du singe racine, ce qui force récursivement la valeur des singes en-dessous, jusqu'à forcer la valeur de l'humain.
from__future__importannotationsimportrefromcollectionsimportdefaultdictfromenumimportEnumfromtypingimportCallable,Dict,Iterable,Optional,Sequence,Set,TupleclassOperation(Enum):ADD='+'SUB='-'MUL='*'DIV='/'@propertydefop(self)->Callable[[int,int],int]:ifselfisself.ADD:returnint.__add__ifselfisself.SUB:returnint.__sub__ifselfisself.MUL:returnint.__mul__ifselfisself.DIV:returnint.__floordiv__assertFalse# we covered all cases@propertydefrev1(self)->Callable[[int,int],int]:"""Given a and b, return x so self.op(x, a) = b"""ifselfisself.ADD:returnint.__sub__ifselfisself.SUB:returnint.__add__ifselfisself.MUL:returnint.__floordiv__ifselfisself.DIV:returnint.__mul__assertFalse# we covered all cases@propertydefrev2(self)->Callable[[int,int],int]:"""Given a and b, return x so self.op(a, x) = b"""ifselfisself.ADD:returnint.__sub__ifselfisself.SUB:returnlambdav,a:a-vifselfisself.MUL:returnint.__floordiv__ifselfisself.DIV:returnlambdav,a:a//vassertFalse# we covered all casesclassMonkey:monkeys:Dict[str,Monkey]={}def__init__(self,name:str,value:Optional[int]=None,others:Optional[Sequence[str]]=None,operation:Optional[Operation]=None)->None:if(othersisNone)!=(operationisNone):raiseValueError("others and operation must be both None or both defined")if(valueisNone)==(othersisNone):raiseValueError("value xor (others and operation) must be defined")self.name=nameself._value=valueself.others=othersself.operation=operation# Register self in class directoryself.monkeys[name]=self@propertydefvalue(self)->int:ifself._valueisnotNone:returnself._valueifself.othersisNoneorself.operationisNone:raiseValueError("cannot compute value of a monkey w/o others or operation")returnself.operation.op(*[self.monkeys[other].valueforotherinself.others])@value.setterdefvalue(self,v:int)->None:ifself._valueisnotNone:# This monkey has a fixed valueraiseValueError("cannot change value of a valued monkey")ifself.othersisNoneorself.operationisNone:# This monkey has a free value, we just have to set itself._value=vreturn# The free monkey is somewhere under selfa:Optional[int]=None# value of first sub-monkeyb:Optional[int]=None# value of second sub-monkeytry:a=self.monkeys[self.others[0]].valueexceptValueError:# This failed because the value-free monkey is here, not a problempasstry:b=self.monkeys[self.others[1]].valueexceptValueError:# This failed because the value-free monkey is here, not a problempassifaisNoneandbisnotNone:a=self.operation.rev1(v,b)self.monkeys[self.others[0]].value=areturnifaisnotNoneandbisNone:b=self.operation.rev2(v,a)self.monkeys[self.others[1]].value=breturn# We do not expect to have two or no value-free monkey under usassertFalse_monkey_re=re.compile(r"^(\w+): (.*)\n?$")_operation_re=re.compile(r"^(\w+) ([-+/*]) (\w+)\n?$")@classmethoddefimport_line(class_,line:str)->Monkey:match=class_._monkey_re.match(line)ifmatchisNone:raiseValueError("invalid monkey description '{}'".format(line))name=match.group(1)line=match.group(2)ifline.isdigit():value=int(line)returnclass_(name,value=value)match=class_._operation_re.match(line)ifmatchisNone:raiseValueError("invalid monkey operation '{}'".format(line))others=(match.group(1),match.group(3))op=Operation(match.group(2))returnclass_(name,others=others,operation=op)defsolve_both(lines:Iterable[str])->Tuple[int,int]:"""Solve both parts of today's puzzle"""forlineinlines:_=Monkey.import_line(line)root=Monkey.monkeys['root']humn=Monkey.monkeys['humn']# Get original root monkey valueresult1=root.value# Correct root monkey operationroot.operation=Operation.SUB# Unset our own value so it becomes the free onehumn._value=None# Force root monkey value: this will cascade to eventually set# our valueroot.value=0result2=humn.valuereturnresult1,result2
# Python, fait maison
Posté par 🚲 Tanguy Ortolo (site web personnel) . En réponse au message Avent du Code, jour 21. Évalué à 4.
Bon, fait maison, c'est à dire sans eval ni bibliothèque externe. On construit un arbre, pour la première partie on calcule récursivement la valeur du singe racine. Pour la deuxième partie, on force la valeur du singe racine, ce qui force récursivement la valeur des singes en-dessous, jusqu'à forcer la valeur de l'humain.