Pour ce problème, j'ai considéré qu'on n'était pas vraiment en deux dimensions, mais plutôt en trois. Parce que l'état d'un creuset, ce n'est pas seulement sa position sur le terrain, mais aussi le nombre de cases qu'il a parcouru dans une direction donnée... ce qui se représente également très bien par un nombre, que je considère comme une troisième coordonnée.
Ça permet de se ramener à un problème de parcours de proche en proche, avec une définition bien particulière des voisins d'un point.
Voici le code :
fromcollections.abcimportIterable,Iterator,SequencefromtypingimportSelfimportnumpyasnpimportnumpy.typingasnptimportaocclassCity:def__init__(self,array:Sequence[Sequence[int]]):self.matrix:npt.NDArray[np.ubyte]=np.array(array,dtype=np.ubyte)self.ly,self.lx=self.matrix.shape@classmethoddefimport_lines(cls,lines:Iterable[str])->Self:array=[[int(char)forcharinlineifchar!='\n']forlineinlines]returncls(array)defwalk(self,minturn,maxturn)->int:# We will be using a matrix with three coordinates:# * z indicates how many steps were made in which direction:# - z < 0: starting, no previous steps!# - 0 ≤ z < maxturn: 1 to maxturn steps up ↑# - maxturn ≤ z < 2*maxturn: 1 to maxturn steps down ↓# - 2*maxturn ≤ z < 3*maxturn: 1 to maxturn steps left ←# - 3*maxturn ≤ z < 4*maxturn: 1 to maxturn steps right →visits:npt.NDArray[np.int_]=np.full((4*maxturn,self.ly,self.lx),-1,dtype=np.int_)def_neighs(z:int,y:int,x:int)->Iterator[tuple[int,int,int]]:"""Yield all directly accessible neighbors of a point, including points outside the limits of the city."""ifz<0:# Special case for startyield(0*maxturn,y-1,x)yield(1*maxturn,y+1,x)yield(2*maxturn,y,x-1)yield(3*maxturn,y,x+1)returndiv,mod=divmod(z,maxturn)ifdiv==0:ifmod<maxturn-1:yield(z+1,y-1,x)ifmod+1>=minturn:yield(2*maxturn,y,x-1)yield(3*maxturn,y,x+1)returnifdiv==1:ifmod<maxturn-1:yield(z+1,y+1,x)ifmod+1>=minturn:yield(2*maxturn,y,x-1)yield(3*maxturn,y,x+1)returnifdiv==2:ifmod+1>=minturn:yield(0*maxturn,y-1,x)yield(1*maxturn,y+1,x)ifmod<maxturn-1:yield(z+1,y,x-1)returnifdiv==3:ifmod+1>=minturn:yield(0*maxturn,y-1,x)yield(1*maxturn,y+1,x)ifmod<maxturn-1:yield(z+1,y,x+1)returndefneighs(z:int,y:int,x:int)->Iterator[tuple[int,int,int]]:forz_,y_,x_in_neighs(z,y,x):if0<=x_<self.lxand0<=y_<self.ly:yieldz_,y_,x_currents:dict[tuple[int,int,int],int]={(-1,0,0):0}whilelen(currents)>0:nexts:dict[tuple[int,int,int],int]={}forcurrent_pos,current_totalincurrents.items():fornext_posinneighs(*current_pos):z,y,x=next_posloss=self.matrix[y,x]next_total=current_total+lossif(visits[next_pos]<0ornext_total<visits[next_pos]):visits[next_pos]=next_totalnexts[next_pos]=next_totalcurrents=nextsloss=-1fordivinrange(4):formodinrange(minturn-1,maxturn):z=div*maxturn+modpos=(z,self.ly-1,self.lx-1)ifloss<0or0<visits[pos]<loss:loss=visits[pos]returnlossdefpart1(lines:aoc.Data)->int:"""Solve puzzle part 1: determine the minimum total heat loss from start to end, using normal crucibles"""city=City.import_lines(lines)returncity.walk(1,3)defpart2(lines:aoc.Data)->int:"""Solve puzzle part 2: determine the minimum total heat loss from start to end, using ultra crucibles"""city=City.import_lines(lines)returncity.walk(4,10)
# Trois dimensions
Posté par 🚲 Tanguy Ortolo (site web personnel) . En réponse au message Advent of Code, jour 17. Évalué à 3. Dernière modification le 18 décembre 2023 à 20:15.
Pour ce problème, j'ai considéré qu'on n'était pas vraiment en deux dimensions, mais plutôt en trois. Parce que l'état d'un creuset, ce n'est pas seulement sa position sur le terrain, mais aussi le nombre de cases qu'il a parcouru dans une direction donnée... ce qui se représente également très bien par un nombre, que je considère comme une troisième coordonnée.
Ça permet de se ramener à un problème de parcours de proche en proche, avec une définition bien particulière des voisins d'un point.
Voici le code :