from__future__importannotationsimportenumimportioimportitertoolsfromtypingimportIterable,Iterator,List,Tupleimportnumpyasnpimportnumpy.typingasnptCoords=Tuple[int,int]classMaterial(enum.Enum):AIR=enum.auto()ROCK=enum.auto()SAND=enum.auto()classSlice:def__init__(self,array:npt.ArrayLike,leak:Coords)->None:self.matrix:npt.NDArray=np.array(array)self.leak=leakdefpour(self)->Iterator[bool]:"""Inject a unit of sand. Yields False if it was blocked, True if it fell into the void. Stops when the leak is clogged up by a mountain of sand."""# List of coordinates to pour sand fromsources:List[Coords]=[]# Start at the original leaky,x=self.leakwhileTrue:# Our unit of sand is injected at current coordinates (y, x)whiley<self.matrix.shape[0]-1:y_=y+1forx_in(x,x-1,x+1):ifself.matrix[y_,x_]isMaterial.AIR:# Sand can flow down:# * current coordinate is a good place to pour next# sand unit from;sources.append((y,x))# * current sand unit falls down one level.y=y_x=x_breakelse:# Ways down exhausted, sand is blockedself.matrix[y,x]=Material.SAND# Get out of the descent loopbreakelse:# Descent ended, sand fell all the way downyieldTruecontinue# Descent did not end, sand got blockedyieldFalseifnotsources:# Nowhere to pour sand from, even the leak is clogged upbreak# Pour next sand unit from last good placey,x=sources.pop()def__str__(self)->str:result=io.StringIO()forlineinself.matrix:forpointinline:ifpointisMaterial.AIR:result.write(' ')elifpointisMaterial.ROCK:result.write('█')elifpointisMaterial.SAND:result.write('░')else:assertFalse# we covered all casesresult.write('\n')returnresult.getvalue()defadd_segment(self,start:Coords,end:Coords)->None:y1,x1=starty2,x2=endify1==y2:x1,x2=min(x1,x2),max(x1,x2)ys=(y1for_inrange(x1,x2+1))xs=(xforxinrange(x1,x2+1))elifx1==x2:y1,y2=min(y1,y2),max(y1,y2)ys=(yforyinrange(y1,y2+1))xs=(x1for_inrange(y1,y2+1))fory,xinzip(ys,xs):self.matrix[y,x]=Material.ROCKdefimport_segments(lines:Iterable[str])->Tuple[List[Coords],List[Tuple[Coords,Coords]]]:points:List[Coords]=[]segments:List[Tuple[Coords,Coords]]=[]defcoords(word:str)->Coords:parts=word.split(',')iflen(parts)!=2:raiseValueError("unexpected number of coordinates")returnint(parts[1]),int(parts[0])forlineinlines:words=line.rstrip().split(' -> ')segment_points=[coords(word)forwordinwords]points.extend(segment_points)segments.extend(itertools.pairwise(segment_points))returnpoints,segmentsdefimport_slice1(lines:Iterable[str])->Slice:points,segments=import_segments(lines)y_leak,x_leak=0,500points.append((y_leak,x_leak))xmin=min(xfor_,xinpoints)-1xmax=max(xfor_,xinpoints)+1ymin=min(yfory,_inpoints)ymax=max(yfory,_inpoints)ifymin<0:raiseValueError("unexpected negative coordinate")xshift=xminmatrix=np.full(((ymax+1),xmax-xshift+1),Material.AIR)result=Slice(matrix,(0,500-xshift))for(y1,x1),(y2,x2)insegments:result.add_segment((y1,x1-xshift),(y2,x2-xshift))returnresultdefimport_slice2(lines:Iterable[str])->Slice:points,segments=import_segments(lines)y_leak,x_leak=0,500points.append((y_leak,x_leak))xmin=min(xfor_,xinpoints)xmax=max(xfor_,xinpoints)ymin=min(yfory,_inpoints)ymax=max(yfory,_inpoints)+2# including the floor# Update xmin and xmax to accomodate a pyramid of sandxmin=min(xmin,x_leak-(ymax-y_leak))xmax=max(xmax,x_leak+(ymax-y_leak))ifymin<0:raiseValueError("unexpected negative coordinate")xshift=xminmatrix=np.full(((ymax+1),xmax-xshift+1),Material.AIR)result=Slice(matrix,(0,500-xshift))for(y1,x1),(y2,x2)insegments:result.add_segment((y1,x1-xshift),(y2,x2-xshift))result.add_segment((ymax,xmin-xshift),(ymax,xmax-xshift))returnresultdefsolve1(lines:Iterable[str])->int:"""Solve part 1 of today's puzzle"""slice_=import_slice1(lines)fori,falleninenumerate(slice_.pour()):iffallen:returniraiseValueError("Simulation ended with unexpected sand leak clogged")defsolve2(lines:Iterable[str])->int:"""Solve part 2 of today's puzzle"""slice_=import_slice2(lines)fori,falleninenumerate(slice_.pour()):iffallen:raiseValueError("Simulation ended with unexpected sand under the floor")returni+1
[^] # Re: Optimisation
Posté par 🚲 Tanguy Ortolo (site web personnel) . En réponse au message Avent du Code, jour 14. Évalué à 3.
Avec ladite optimisation :