Pour ce jour ci, le type d'input est le suivant
R 6 (#70c710)
D 5 (#0dc571)
L 2 (#5713f0)
D 2 (#d2c081)
R 2 (#59c680)
D 2 (#411b91)
L 5 (#8ceee2)
U 2 (#caa173)
L 1 (#1b58a2)
U 2 (#caa171)
R 2 (#7807d2)
U 3 (#a77fa3)
L 2 (#015232)
U 2 (#7a21e3)
C'est une liste d'instructions pour creuser.
Le premier symbole indique dans quelle direction il faut aller (L pour gauche, R pour droite, U pour haut, D pour bas) et l'entier qui suit indique de quelle distance il faut se déplacer.
Le code hexadécimal qui suit est ignoré pour la partie 1.
Si l'on part du point (0, 0) et que l'on suit les instructions, on obtient donc la grille suivante où les caractères "#" indiquent les endroits où l'on a creusé:
#######
#.....#
###...#
..#...#
..#...#
###.###
#...#..
##..###
.#....#
.######
Si l'on remplit la surface délimitée par les "#", on obtient la grille suivante:
#######
#######
#######
..#####
..#####
#######
#####..
#######
.######
.######
Le but de la partie 1 est de compter le nombre de "#" dans la dernière grille.
Dans la partie 2, on change les instructions et l'on considère le code hexadécimal.
Les 5 premiers caractères indique la distance de déplacement et le dernier caractère indique dans quelle direction on se déplace: 0 signifie droite, 1 signifie bas, 2 signifie gauche et 3 signifie haut.
Les distances de déplacement étant très grandes, on ne peut pas calculer directement la grille.
Il faut trouver quelque chose de plus astucieux.
# Solution en Haskell
Posté par Guillaume.B . Évalué à 3.
Ce problème ressemble à la partie 2 du jour 10 de cette année.
La première partie peut être aisément fait avec un parcours en largeur/profondeur mais ça devient clairement impossible pour la partie 2.
Remarquons que l'on cherche à trouver le nombre de sommets de coordonnées entières situées à l'intérieur d'un polygone.
On va donc de baser sur le théorème de Pick et la shoelace formula.
La shoelace formula est une formule pour calculer l'aire d'un polygone.
Si les sommets du polygone sont
(x1, y1), ..., (xn, yn)
alors l'aire du polygone est|x1 y2 - y1 x2 + ... + x_{n-1} yn - y_{n-1} xn + xn y1 - yn x1| / 2
Le théorème de Pick donne une formule pour calculer le nombre de points intérieurs à un polygone à partir de son aire (qui est calculé par la shoelace formula) et du nombre de points à sa frontière (c'est juste la somme des distances données par les instructions).
La formule est la suivante:
A = i + b/2 - 1
où A est l'aire, i le nombre de points à l'intérieur et b le nombre de points à la frontière.Pour calculer le nombre de "#" total, il faut faire la somme
i + b
oùi = A - b/2 - 1
.Donc, voici le code Haskell
# J'ai un peu honte...
Posté par mac__ . Évalué à 2.
… ou pas, je sais pas trop le justifier.
La partie 1 a été faite à la main avec des algos de remplissages de polygones.
Pour la partie 2, c'est devenu impossible - j'ai utilisé la lib-qui-va-bien - à savoir Shapely en Python, sa fonction
area
et un petit correctif pour prendre en compte l'épaisseur des murs.Du coup, ça devient "trivial", il y a juste à identifier les sommets du polygone et la librairie fait le reste.
Après… s'agit-il vraiment de triche ? Après tout, qui dit "j'ai triché" quand il fait faire à son ordinateur des milliers de multiplications pour répondre à un problème Advent Of Code ?… le débat est ouvert…
Toujours est-il que je n'aurais pas su répondre à cette question avec mes connaissances pures.
[^] # Re: J'ai un peu honte...
Posté par steph1978 . Évalué à 2.
Tout pareil, mas dès la première partie.
Ce jour m'a permis de découvrir shapely.
Qui permet aussi de beau tracés:
# Sans géométrie
Posté par 🚲 Tanguy Ortolo (site web personnel) . Évalué à 3.
Première partie
Pour la première partie, je fais du remplissage, voici les étapes :
Trouver l'aire, c'est trivial.
Deuxième partie
Pareil. Comment ça, pareil, alors que les coordonnées sont beaucoup trop grandes pour pouvoir faire ça avec des matrices ? Eh bien, on n'a pas besoin de toutes les coordonnées, voyez-vous. On a seulement besoin de celles où commencent ou finissent des tranchées en fait.
Bref, je me construis une matrice donc les cases ont des dimensions virtuelles carrément variables. Ensuite, on se ramène à l'algorithme précédent, avec un détail de pondération pour le calcul de l'aire.
[^] # Re: Sans géométrie
Posté par 🚲 Tanguy Ortolo (site web personnel) . Évalué à 3. Dernière modification le 18 décembre 2023 à 20:01.
Le code :
# Bof , bof, je n'ai pas vraiment trouvé par moi-même.
Posté par syj . Évalué à 1.
Pour la première partie, j'ai utilisé un bête algo de remplissage de forme par propagation.
Pour la deuxième partie, je connaissais l'algorithme du lacets(Shoelace) pour en avoir entendu parlé récemment.
Je me doutais qu'il fallait retirer le périmètre du polygone.
Par contre, je ne trouvais pas malgré toutes mes investigations ,c'est en lisant le CR de Guillaume que j'ai découvert le théorème de Pick et son usage.
Mon implémtentation en Java.
# La solution la plus courte à écrire, la plus longue à expliquer.
Posté par Yth (Mastodon) . Évalué à 2.
Hier encore, j'ai sévèrement lutté pour coder : repas d'entreprise en montagne (dur la vie…), donc j'ai eu rien de temps pour coder, à moitié sur PC, à moitié sur téléphone.
En pratique, j'ai honte un peu, j'ai fini de coder en voiture (au volant oui, oui), avec un algo sous-optimisé qui a mit un bon quart d'heure à s'exécuter, et m'a sorti le bon résultat.
Le même code prend 70s sur mon petit PC.
Mais j'ai eu le temps de réfléchir à une autre approche, que je n'avais pas eu le temps de coder, mais que j'ai validé le soir.
Et là c'est magique, une fonction de 12 lignes, 3 lignes pour les deux jeux de données, et 2 structures constantes, allez 22 lignes avec le she-bang python3.
Le résultat sort en rien de temps, O(n).
L'idée c'est de considérer une unique zone rectangulaire qu'on va agrandir ou rétrécir au fur et à mesure de l'exploration des sommets, en notant ce qu'on a tranché, ou rajouté.
Voilà les données de test et quelques itérations :
Étape 0 j'ai la zone (0, 0) -> (0, 0), facile.
Étape 1 j'étends vers la droite de 6 cases, rien à faire, ma zone fini en (6, 0), je prends tout.
Étape 2, pas mieux, j'étends vers le bas jusque (6, 5), je prends encore tout (je ne sais pas encore ce qui se passe à la fin, on enlèvera plus tard, sommet par sommet).
Étape 3, je réduis vers la gauche vers (4, 5) ! Là je perd une zone, indiquée avec des
_
, elle fait 12 cases, donc je la supprime du problème mais je note une superficie de 12.Étape 4, j'étends vers le bas, rien à dire, ma zone se termine en (4, 7).
Étape 5, j'étends vers la droite encore, jusque (6, 7), et là on voit bien qu'on a agrandit dans une zone de vide, les
*
, il faut les retirer, mais attention, les#
en bas sont l'épaisseur du trait, on les garde ! Donc on note que notre superficie est de 14 trop grand maintenant, notre superficie de côté est donc 12-14 = -2, ce qui correspond bien aux deux.
à droite du résultat cherché.Je saute, étape 6 on descends vers (6, 9) rien à faire, étape 7 on va vers la droite jusque (1, 9) donc on ajoute à notre superficie stockée 50 cases, on est à 48.
Étape 8, on remonte vers (1, 7) ! On doit considérer l'épaisseur du trait qui va disparaître, ici 2, mais le reste était en dehors de notre résultat, donc la superficie augmente simplement de 2 pour arriver à 50.
On a vu les 4 règles, on termine les 14 étapes ainsi :
Étape 9 : gauche de 1 vers (0, 7) superficie + 8 = 58
Étape 10 : haut de 2 vers (0, 5) superficie + 2 = 60
Étape 11 : droite de 2 vers (2, 5) superficie - 10 = 50
Étape 12 : haut de 3 vers (2, 2) superficie + 3 = 53
Étape 13 : gauche de 2 vers (0, 2) superficie + 6 = 59
Étape 14 : haut de 2 vers (0, 0) superficie + 2 = 61
Et enfin, il nous reste la zone qui se termine en (0, 0) et qui fait donc
#
et a une superficie de 1.J'ajoute 1 : 62, youpi.
Cet algo est pensé sur un départ à gauche et en bas, avec le (0, 0) qui est dans le coin.
Pour valider que ça marche un peu partout, surtout en débordant à gauche ou en haut, on peut constater que les instructions peuvent cycler, on peut commencer à la seconde instruction et reboucler jusque la 1ère, etc.
Donc j'ai validé avec toutes les positions de départ : 62 tout le temps, on est bons.
Ok, bah plus qu'à tester en grand et sur données réelles : ça marche, bingo.
Le code :
Voilà, voilà, sans théorème, sans connaissances particulières, sans module externe, juste des méninges torturées pendant des heures à ne pas pouvoir coder les algos qui tournent dans la tête.
Inutile de dire que le temps d'exécution c'est l'overhead python, et c'est tout.
Par contre difficile de bien coder ça en vrac, dehors, dans la neige et le froid, avec un téléphone, on est mieux sur un bureau, avec un papier, et un clavier.
[^] # Re: La solution la plus courte à écrire, la plus longue à expliquer.
Posté par 🚲 Tanguy Ortolo (site web personnel) . Évalué à 3.
Non mais là, faut savoir s'arrêter quand même. Ce serait dommage de mourir ou de tuer quelqu'un pour cause d'AoC.
[^] # Re: La solution la plus courte à écrire, la plus longue à expliquer.
Posté par Yth (Mastodon) . Évalué à 2.
Je profite des feux pour écrire, et ça calcule pendant que je roule, si ça plante en général j'arrive à penser la modif à faire de tête et j'ai juste à l'écrire à l'arrêt suivant.
Je suis peut-être inconscient, mais pas suicidaire.
[^] # Re: La solution la plus courte à écrire, la plus longue à expliquer.
Posté par vicnet . Évalué à 1.
Salut,
J'ai commencé la partie 1 par un remplissage ligne à ligne en calculant le nombre de point dedans/dehors.
Si on croise une ligne, on passe de dehors à dedans ou le contraire.
Pour la 2, ca marche pô.
Je n'ai pas pensé à la matrice virtuelle, enfin si plus ou moins, j'ai tenté de déterminer les groupes de lignes où cela ne change pas et utiliser mon algo précédent en multipliant par la surface du groupe, mais KO, pas réussi
J'ai testé aussi le coup d'agrandir ou réduire la zone au fur et à mesure de l'ajout de segments.
Meme chose, ca ne fonctionnait pas sur des cas simple: carré, carré avec un coin en moins.
Bref, abandon.
Je viens de reprendre seulement hier en utilisant 'tout simplement' le théorème des lacets pour calculer la surface d'un polygone quelconque.
J'avais des doutes sur l'utilisation de cette technique avec des blocs et des coordonnées entières. Mais en relisant l'algo, et en dessinant quelques crobar vite fait, j'ai compris que c'était faisable avec un twist: j'ai juste agrandi le polygone vers son extérieur (technique de la main gauche le long d'un mur) de 1/2.
Ca fonctionne même pour le 1 et c'est plus beaucoup beaucoup plus simple.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.