Posté par steph1978 .
En réponse au message Avent du Code, jour 23.
Évalué à 4.
Dernière modification le 23 décembre 2022 à 18:15.
Pas si difficile aujourd'hui mais j'ai butté sur à peut prêt toutes les instructions
- ah si un elf peut bouger partout alors il ne bouge pas ?!
- ah les directions changent a chaque tour ?!
- mais qu'est ce qu'il se passe au bord ?
- ah la grille est infinie ?!
Ce dernier point m'a contraint à changer ma conception : d'une matrice à un set. Moins sympa pour débuguer mais nécessaire quand on ne connaît pas les limites du jeu. Finalement un code plus simple car pas de gestion de dépassement, et utilisation des fonctions de set (appartenance, union, différences) et des list comprehension un peu partout. Et accessoirement beaucoup plus rapide : 1025 round en 8s pour le tout.
python, 60 loc
importsysE=set()# elvesfory,linenumerate(sys.stdin.read().splitlines()):forx,cinenumerate(l):ifc=='#':E.add((y,x))defcan(y,x):can1=(y-1,x-1)notinEcan2=(y-1,x)notinEcan3=(y-1,x+1)notinEcan4=(y,x+1)notinEcan5=(y+1,x+1)notinEcan6=(y+1,x)notinEcan7=(y+1,x-1)notinEcan8=(y,x-1)notinEreturn[can1,can2,can3,can4,can5,can6,can7,can8]defcanN(y,x,can1,can2,can3,can4,can5,can6,can7,can8):ifcan1andcan2andcan3:return(y-1,x)defcanS(y,x,can1,can2,can3,can4,can5,can6,can7,can8):ifcan5andcan6andcan7:return(y+1,x)defcanW(y,x,can1,can2,can3,can4,can5,can6,can7,can8):ifcan7andcan8andcan1:return(y,x-1)defcanE(y,x,can1,can2,can3,can4,can5,can6,can7,can8):ifcan3andcan4andcan5:return(y,x+1)fromcollectionsimportdequedirs=deque([canN,canS,canW,canE])forrinrange(int(sys.argv[1])):P=dict()foreinE:# each elve(y,x)=emoves=[d(y,x,*can(y,x))fordindirs]ifall(misnotNoneforminmoves):continueifall(misNoneforminmoves):continuep=next(filter(lambdam:misnotNone,moves))ifpnotinP:# can moveP[p]=(y,x)else:# occupied, invalidate moveP[p]=Nonemoved={vfork,vinP.items()ifvisnotNone}newpos={kfork,vinP.items()ifvisnotNone}ifr+1==10:minx=min(xfor(x,_)inE)maxx=max(xfor(x,_)inE)miny=min(yfor(_,y)inE)maxy=max(yfor(_,y)inE)print((maxx-minx+1)*(maxy-miny+1)-len(E))iflen(newpos)==0:print(f"no move after {r+1}")breakE=(E-moved)|newposdirs.rotate(-1)
Posté par steph1978 .
En réponse au message Avent du Code, jour 21.
Évalué à 3.
Dernière modification le 21 décembre 2022 à 21:15.
On voulait me faire faire un AST et évaluer le bazar. Bah je voulais pas.
Du coup j'ai fait une boucle infinie jusqu'à tout évaluer.
Ça m'a permis de faire la partie 1 en quelques minutes.
Bon je l'ai un peu payé sur la partie 2, j'ai dû bidouiller. La vizu m'a permis de voir qu'il n'y a pas de lien entre les deux branches partant de root. Donc pas de réutilisation d'un résultat dans les deux sous arbres. Donc l'inconnu n'est impliqué que dans une seule équation. J'ai donc hardcodé a valeur du sous arbre de droite dans la racine du sous arbre de gauche, là où se situe "humn".
Je dirai qu'il n'y a pas de règle car cela dépend de l'input.
Le code que j'ai pour la partie deux donne la bonne réponse en gardand le top 1000, ne marche pas à 700. Mais sur un autre input que j'ai testé, il a fallu monter à 2000.
J'utilise AWK pour construire un fichier openscad.
C'est un fichier texte qui décrit les formes à dessiner. exemple ici : translate([17.05,9.05,3.05]) cube([0.9,0.9,0.9]); Je laisse un petit espace entre chaque cube pour que ça ne fasse pas une grosse masse.
Habituellement, je m'en sers pour faire des modèles pour de l'impression 3D.
Je faisais un blocage avec le modeleur clicodrome, type Freecad. Openscad m'a sauvé :)
La partie 1 étant un échauffement, parlons partie 2. Il faut "remplir" toutes les aspérités d'une boule constituée de petits cubes de 1x1x1 et dont la surface est irrégulière. Et compté les surfaces exposées au liquide.
vizu
La visualisation m'a bien aidé :
On peut voir les creux qui doivent se remplir.
code partie 2
J'ai rempli par itération (while True) en m'arrêtant quand plus rien de nouveau ne se remplissait.
Ensuite, pour chaque cube, on compte combien de face sont en contact avec de l'eau (6 voisins, 2 par dimension x, y, z).
importsysS=set()# cubesforlinsys.stdin.read().splitlines():S.add(tuple(map(int,l.split(','))))A=23L=set()# waterforxinrange(-2,A+1):foryinrange(-2,A+1):L.add((x,y,-2))more=Truewhilemore:c=0forxinrange(-2,A+1):foryinrange(-2,A+1):forzinrange(-2,A+1):if(x,y,z)notinSand(x,y,z)notinL:# water can only expand in airfor(i,j,k)in[(0,0,1),(0,0,-1),(0,1,0),(0,-1,0),(1,0,0),(-1,0,0)]:if(x+i,y+j,z+k)inL:# if neighbour is waterL.add((x,y,z))# water expandc+=1more=(c>0)N=0for(x,y,z)inS:for(i,j,k)in[(0,0,1),(0,0,-1),(0,1,0),(0,-1,0),(1,0,0),(-1,0,0)]:if(x+i,y+j,z+k)inL:N+=1print(N)
Un parcours de graph avec la complication que l'état du graph change pendant le parcours.
J'ai procédé en brute force et n'ai pas trouvé vraiment de moyen de couper des branches, si ce n'est marquer dès le départ les vannes à 0 comme étant déjà ouverte. Ça marchait bien jusqu'à 20 de profondeur mais passé ça, ça ramait trop (je timeout à 10 min). J'ai donc ajouté du cache et bim, 0.37s et 91MB de cache.
Cependant cette solution ne passe pas à l'échelle pour le parcours à 2 qui multiplie les branches.
J'ai ouïe dire des solutions où on pré-calcul des distances ou des scores mais je crois que je vais en rester là, j'ai déjà un retard de deux jours. J'ai atteint ma limite.
part 1
importsysimportrepat=re.compile('Valve ([A-Z]{2}) has flow rate=([0-9]+); tunnels? leads? to valves? ([ ,A-Z]+)')P=dict()fori,linenumerate(sys.stdin.read().splitlines()):g=pat.search(l)p=int(g[2])P[g[1]]=(i,p,g[3].split(", "))STEPS=int(sys.argv[1])ALLO=0for_,(i,p,_)inP.items():ifp==0:ALLO=ALLO|(1<<i)cache={}defstep(pos="AA",left=STEPS,opens=ALLO):ifleft<=0:return0key=(pos,left,opens)r=cache.get(key,-1)ifr>=0:returnr(i,p,nh)=P[pos]is_open=opens&(1<<i)a=0ifnotis_open:# also means 0 - worth opening valve ?left-=1# cost to openopens=opens|(1<<i)a=p*(left+30-STEPS)+max(step(n,left-1,opens)forninnh)b=max(step(n,left-1,opens)forninnh)r=max(a,b)cache[key]=rreturnrprint(step())
À priori on a le même algo. Peut être ma fonction d'union d'intervalles moins efficace. Faudrait que je profile. Mais là j'ai deux jours de retard alors je vais passer :)
Un jour où la solution naïve à la première partie ne passe pas à l'échelle pour la seconde partie.
J'ai d'abord fait une solution qui a explosé la RAM (4M*4M d'int, ça explose).
Puis une solution sans rien en RAM mais qui explose le CPU (j'ai timeout à 10 minutes).
Troisième solution qui est un compromis entre RAM et CPU. Je stocke des intervalles pour 4M de lignes ; comme les autres solutions présentées ici.
Ça passe, en 40 secondes avec l'aide du JIT.
code partie 2
importsysW=H=4_000_000# store position that are not reachable, row by rowL=[[(0,0)for_inrange(32)]for_inrange(H+1)]fori,linenumerate(sys.stdin.read().splitlines()):(_,_,X,Y,_,_,_,_,A,J)=l.split(" ")x,y=int(X[2:-1]),int(Y[2:-1])a,b=int(A[2:-1]),int(J[2:])d=abs(x-a)+abs(y-b)fornyinrange(y-d,y+d+1):ifny>=0andny<=H:dy=abs(ny-y)dx=d-dyL[ny][i]=(x-dx,x+dx)for(y,I)inenumerate(L):SI=sorted(I)maxx=0for(a,b)inSI:ifa>maxx+1:print(y,maxx+1,(maxx+1)*4_000_000+y)sys.exit(0)maxx=max(maxx,b)
Permet de découper (crop) une image jpeg sans toucher à la qualité. L'astuce est que la découpe ne peut se faire que par pas de 8 pixels. En effet jpeg applique une FFT sur des carrés de 8x8. Donc pour croper il suffit de supprimer les carrés qui gênent et de garder les bons.
Le parsing de l'input est presque plus long à écrire que l'application de règles : 4 boucles for imbriquées et 4 split contre 3 boucles while imbriquées.
Voici mon code pour la partie deux. La partie est identique excepté la condition de sortie.
importsysW=1000H=200L=[[0for_inrange(W)]for_inrange(H)]# full or airdefs2li(s,sep=','):returnlist(map(int,s.split(sep)))MAX_Y=0forlinsys.stdin.read().splitlines():lp=l.split(" -> ")forf,tinzip(lp,lp[1:]):(a,b,c,d)=(*s2li(f),*s2li(t))x0=min(a,c)y0=min(b,d)x1=max(a,c)y1=max(b,d)MAX_Y=max(MAX_Y,y1)forxinrange(x0,x1+1):foryinrange(y0,y1+1):L[y][x]=1forxinrange(W):L[MAX_Y+2][x]=1# rock on the groundstop=FalseN=0whilenotstop:N+=1y=0x=500more=True# can all left or right whilemoreandnotstop:more=FalsewhileL[y][x]==0:#and not stop: # if only airy+=1stop=y==MAX_Y+2ifL[y][x-1]==0:# can fall leftx-=1more=TrueelifL[y][x+1]==0:# can fall rightx+=1more=Trueelse:stop=y==1# can't put more sandL[y-1][x]=2# mark sandprint(N)
Et une jolie nimage de la caverne remplie de sable:
Posté par steph1978 .
En réponse au message Avent du Code, jour 13.
Évalué à 2.
Dernière modification le 13 décembre 2022 à 15:33.
Tu veux dire à la place du eval. Oui clairement, c'est moins craignos.
À la première lecture je pensais que tu voulais résoudre le problème en javascript 😓. Je veux dire juste en comparant les deux listes avec un <. J'ai tenté, ça marche presque 😛. Mais non en fait, les règles de comparaisons ne sont pas les mêmes.
Je galérais à organiser ma comparaison de paquets, et j'ai eu un indice d'utiliser un comparateur. Je suis reparti là dessus et ça fonctionne.
importsysP=[tuple(map(eval,p.split("\n")))forpinsys.stdin.read().split("\n\n")]defC(l,r):T=(type(l),type(r))ifT==(int,int):ifl<r:return-1returnl>r# 0 or 1ifT==(int,list):returnC([l],r)ifT==(list,int):returnC(l,[r])# list,listforqinzip(l,r):c=C(*q)ifc:returncreturnC(len(l),len(r))# part 1S=0fori,pinenumerate(P):ifC(*p)<=0:S+=i+1print(S)# part 2fromfunctoolsimportcmp_to_keyQ=[qfor(l,r)inPforqin[l,r]]Q.append([[2]])Q.append([[6]])Q.sort(key=cmp_to_key(C))print((Q.index([[2]])+1)*(Q.index([[6]])+1))
Dans mon input, tous les b ne sont que dans la deuxième colonne. Et la première colone ne contient que des a. Comme il faut nécessairement passer par un b, je n'ai testé que des départs depuis la première colonne.
J'ai passé plusieurs heures à ne pas comprendre pourquoi je ne trouvais pas la solution. Et puis j'ai compris ; j'ai loupé une phrase cruciale dans l'énoncée : on a le droit de redescendre, bordel !
J'ai utilise la très précieuse bibliothèque de manipulation de graph en Python, networkx. Mon code ne fait que parser l'input et construire la liste de transitions possibles et présente donc peu d'intérêt. Mais permet au moins de faire une joli nimage du résultat:
Si vous vous baladez sur Reddit, y en a qui ont fait des représentations de ouf.
La seconde partie était triviale dans ces conditions et m'a pris moins de deux minutes.
Posté par steph1978 .
En réponse au message Avent du Code, jour 11.
Évalué à 2.
Dernière modification le 11 décembre 2022 à 14:13.
Mince alors, toute une matinée de code à mettre à la benne 😩
Blague à part, merci pour la remarque, je confonds probablement avec map ou reversed qui retournent un itérateur.
J'imagine que pour trier, il faut charger toutes les données, donc pas d'intérêt de retourner un itérateur. Ou alors ne pas chercher d'explication, l'API python n'est pas toujours cohérente.
# ready, set, python
Posté par steph1978 . En réponse au message Avent du Code, jour 23. Évalué à 4. Dernière modification le 23 décembre 2022 à 18:15.
Pas si difficile aujourd'hui mais j'ai butté sur à peut prêt toutes les instructions
- ah si un elf peut bouger partout alors il ne bouge pas ?!
- ah les directions changent a chaque tour ?!
- mais qu'est ce qu'il se passe au bord ?
- ah la grille est infinie ?!
Ce dernier point m'a contraint à changer ma conception : d'une matrice à un set. Moins sympa pour débuguer mais nécessaire quand on ne connaît pas les limites du jeu. Finalement un code plus simple car pas de gestion de dépassement, et utilisation des fonctions de set (appartenance, union, différences) et des list comprehension un peu partout. Et accessoirement beaucoup plus rapide : 1025 round en 8s pour le tout.
python, 60 loc
[^] # Re: Mode triche on
Posté par steph1978 . En réponse au message Avent du Code, jour 22. Évalué à 5.
C'est de l'openscad. Évoqué jour 18.
# papier, ciseaux, colle
Posté par steph1978 . En réponse au message Avent du Code, jour 22. Évalué à 5.
# python rebelle
Posté par steph1978 . En réponse au message Avent du Code, jour 21. Évalué à 3. Dernière modification le 21 décembre 2022 à 21:15.
On voulait me faire faire un AST et évaluer le bazar. Bah je voulais pas.
Du coup j'ai fait une boucle infinie jusqu'à tout évaluer.
Ça m'a permis de faire la partie 1 en quelques minutes.
Bon je l'ai un peu payé sur la partie 2, j'ai dû bidouiller. La vizu m'a permis de voir qu'il n'y a pas de lien entre les deux branches partant de root. Donc pas de réutilisation d'un résultat dans les deux sous arbres. Donc l'inconnu n'est impliqué que dans une seule équation. J'ai donc hardcodé a valeur du sous arbre de droite dans la racine du sous arbre de gauche, là où se situe "humn".
Chaque partie s'évalue en 0.30s et 10Mo de RAM.
part 1
part 2
[^] # Re: Erreur bete
Posté par steph1978 . En réponse au message Avent du Code, jour 19. Évalué à 2.
Je dirai qu'il n'y a pas de règle car cela dépend de l'input.
Le code que j'ai pour la partie deux donne la bonne réponse en gardand le top 1000, ne marche pas à 700. Mais sur un autre input que j'ai testé, il a fallu monter à 2000.
[^] # Re: python tranquille
Posté par steph1978 . En réponse au message Avent du Code, jour 18. Évalué à 4.
J'utilise AWK pour construire un fichier openscad.
C'est un fichier texte qui décrit les formes à dessiner. exemple ici :
translate([17.05,9.05,3.05]) cube([0.9,0.9,0.9]);
Je laisse un petit espace entre chaque cube pour que ça ne fasse pas une grosse masse.Habituellement, je m'en sers pour faire des modèles pour de l'impression 3D.
Je faisais un blocage avec le modeleur clicodrome, type Freecad. Openscad m'a sauvé :)
# python tranquille
Posté par steph1978 . En réponse au message Avent du Code, jour 18. Évalué à 4.
Relativement facile ce jour
La partie 1 étant un échauffement, parlons partie 2. Il faut "remplir" toutes les aspérités d'une boule constituée de petits cubes de 1x1x1 et dont la surface est irrégulière. Et compté les surfaces exposées au liquide.
vizu
La visualisation m'a bien aidé :
On peut voir les creux qui doivent se remplir.
code partie 2
J'ai rempli par itération (
while True
) en m'arrêtant quand plus rien de nouveau ne se remplissait.Ensuite, pour chaque cube, on compte combien de face sont en contact avec de l'eau (6 voisins, 2 par dimension x, y, z).
Exécution en 0.11s et 11MB RAM.
# python caché
Posté par steph1978 . En réponse au message Avent du Code jour 16. Évalué à 2.
Un parcours de graph avec la complication que l'état du graph change pendant le parcours.
J'ai procédé en brute force et n'ai pas trouvé vraiment de moyen de couper des branches, si ce n'est marquer dès le départ les vannes à 0 comme étant déjà ouverte. Ça marchait bien jusqu'à 20 de profondeur mais passé ça, ça ramait trop (je timeout à 10 min). J'ai donc ajouté du cache et bim, 0.37s et 91MB de cache.
Cependant cette solution ne passe pas à l'échelle pour le parcours à 2 qui multiplie les branches.
J'ai ouïe dire des solutions où on pré-calcul des distances ou des scores mais je crois que je vais en rester là, j'ai déjà un retard de deux jours. J'ai atteint ma limite.
part 1
[^] # Re: python, on a RAMé
Posté par steph1978 . En réponse au message Avent du Code jour 15. Évalué à 2.
À priori on a le même algo. Peut être ma fonction d'union d'intervalles moins efficace. Faudrait que je profile. Mais là j'ai deux jours de retard alors je vais passer :)
[^] # Re: Unions d'intervalles
Posté par steph1978 . En réponse au message Avent du Code jour 15. Évalué à 3.
Plus précisément ce sont des carrés tournés de 45°. Je sais pas si ça simplifie…
# python, on a RAMé
Posté par steph1978 . En réponse au message Avent du Code jour 15. Évalué à 2.
Un jour où la solution naïve à la première partie ne passe pas à l'échelle pour la seconde partie.
J'ai d'abord fait une solution qui a explosé la RAM (4M*4M d'int, ça explose).
Puis une solution sans rien en RAM mais qui explose le CPU (j'ai timeout à 10 minutes).
Troisième solution qui est un compromis entre RAM et CPU. Je stocke des intervalles pour 4M de lignes ; comme les autres solutions présentées ici.
Ça passe, en 40 secondes avec l'aide du JIT.
code partie 2
# et pour les images
Posté par steph1978 . En réponse au lien LosslessCut (The Swiss Army Knife of Lossless Video/Audio Editing). Évalué à 3.
https://betterjpeg.com/crop.htm
Permet de découper (crop) une image jpeg sans toucher à la qualité. L'astuce est que la découpe ne peut se faire que par pas de 8 pixels. En effet jpeg applique une FFT sur des carrés de 8x8. Donc pour croper il suffit de supprimer les carrés qui gênent et de garder les bons.
[^] # Re: python et lib de graph
Posté par steph1978 . En réponse au message Avent du Code, jour 12. Évalué à 3.
en plus joli : l'input et le resultat.
[^] # Re: python et lib de graph
Posté par steph1978 . En réponse au message Avent du Code, jour 12. Évalué à 3.
image disparue :
[^] # Re: python, vite fait bien fait
Posté par steph1978 . En réponse au message Avent du Code, jour 14. Évalué à 2.
J'aime bien utiliser le format PNM car c'est du texte et donc facile à générer.
Sous linux, ça se visualise nativement. Mais ça peut être converti en PNG grâce à ImageMagic :
convert a.pnm -scale 400% a.png
# python, vite fait bien fait
Posté par steph1978 . En réponse au message Avent du Code, jour 14. Évalué à 4.
Le parsing de l'input est presque plus long à écrire que l'application de règles : 4 boucles
for
imbriquées et 4split
contre 3 boucleswhile
imbriquées.Voici mon code pour la partie deux. La partie est identique excepté la condition de sortie.
Et une jolie nimage de la caverne remplie de sable:
[^] # Re: Étoile
Posté par steph1978 . En réponse au message Avent du Code, jour 12. Évalué à 2.
j'ai la même mais avec une rotation.
[^] # Re: python, en trichant
Posté par steph1978 . En réponse au message Avent du Code, jour 13. Évalué à 2. Dernière modification le 13 décembre 2022 à 15:33.
Tu veux dire à la place du eval. Oui clairement, c'est moins craignos.
À la première lecture je pensais que tu voulais résoudre le problème en javascript 😓. Je veux dire juste en comparant les deux listes avec un
<
. J'ai tenté, ça marche presque 😛. Mais non en fait, les règles de comparaisons ne sont pas les mêmes.# python, en trichant
Posté par steph1978 . En réponse au message Avent du Code, jour 13. Évalué à 4.
Je galérais à organiser ma comparaison de paquets, et j'ai eu un indice d'utiliser un comparateur. Je suis reparti là dessus et ça fonctionne.
[^] # Re: En Python
Posté par steph1978 . En réponse au message Avent du Code, jour 12. Évalué à 2.
Dans mon input, tous les b ne sont que dans la deuxième colonne. Et la première colone ne contient que des a. Comme il faut nécessairement passer par un b, je n'ai testé que des départs depuis la première colonne.
# python et lib de graph
Posté par steph1978 . En réponse au message Avent du Code, jour 12. Évalué à 4.
J'ai passé plusieurs heures à ne pas comprendre pourquoi je ne trouvais pas la solution. Et puis j'ai compris ; j'ai loupé une phrase cruciale dans l'énoncée : on a le droit de redescendre, bordel !
J'ai utilise la très précieuse bibliothèque de manipulation de graph en Python, networkx. Mon code ne fait que parser l'input et construire la liste de transitions possibles et présente donc peu d'intérêt. Mais permet au moins de faire une joli nimage du résultat:
Si vous vous baladez sur Reddit, y en a qui ont fait des représentations de ouf.
La seconde partie était triviale dans ces conditions et m'a pris moins de deux minutes.
# empaquetage
Posté par steph1978 . En réponse au journal Offpunk 1.8. Évalué à 4.
J'ai fait l'empaquetage avec pyinstaller. Ça semble bien fonctionner. Ça donne un exe autoporteur (interpréteur python compris) de 26MB.
Un simple
pip install pyinstaller && pyinstaller -F -n offpunk offpunk.py
suffit.Avec la possibilité de mettre l'exe dans les releases.
Peut être une façon de simplifier l'installation pour certains utilisateurs.
[^] # Re: python, on oublie toute dignité
Posté par steph1978 . En réponse au message Avent du Code, jour 11. Évalué à 2.
En poussant un peu plus la réflexion sur l’arithmétique modulaire, on peut se passer de balader des listes et garder un nombre. Trop tard pour moi :)
# un bout de AWK
Posté par steph1978 . En réponse au message Avent du Code, jour 10. Évalué à 4.
Je me suis laissé emporté par le python du jour 9.
Le problème du jour passe très bien en AWK.
part 1, 11 lines
part 2, 20 lines
[^] # Re: python, on oublie toute dignité
Posté par steph1978 . En réponse au message Avent du Code, jour 11. Évalué à 2. Dernière modification le 11 décembre 2022 à 14:13.
Mince alors, toute une matinée de code à mettre à la benne 😩
Blague à part, merci pour la remarque, je confonds probablement avec
map
oureversed
qui retournent un itérateur.J'imagine que pour trier, il faut charger toutes les données, donc pas d'intérêt de retourner un itérateur. Ou alors ne pas chercher d'explication, l'API python n'est pas toujours cohérente.