Advent of code

 14 décembre 2024 

  Restroom Redoubt : Déplacer des robots jusqu'à qu'ils fassent un joli dessin, sans qu'on sache quel dessin.
p=38,76 v=-61,88
   
p=28,22 v=-43,-40
   
p=76,78 v=-98,87
   
        
code.py Affiche sur chaque ligne les meilleures et les moins bonnes images selon différents critères.
Le nombre de voisins fonctionne (qui touchent, ou aussi en diagonales), ou encore l'écart moyen au centre.
Dans les critères absents du code et de l'image générée, on compte aussi le nombre de positions X et Y différentes minimal (en faisant la somme ou le produit des deux, mais pas si on prend seulement l'un ou l'autre), ou encore le nombre de positions (x,y) MAXIMAL (car les points de l'image n'ont pas de raison d'être superposés, et du reste, c'est la seule image avec aucun point superposé), et aussi une mesure d'entropie, comme par exemple la longueur du résultat de zlib.compress(bytes(str(positions_now), 'utf-8').
Mais surtout, critère ultime, on peut utiliser le minimum du scoring de la partie 1 !

Avec code_spotIt.py, les images défilent et il faut se débrouiller pour trouver la bonne.

Dans ce fichier code_spotIt.py, on peut trouver l'utilisation de @property.
Cela permet de pouvoir accéder à robot.x plutôt que robot.get_x(), mais de passer quand même par des getter et setter.
Usage 1 : Ainsi, avec robot.x = 17, robot.position sera mis à jour.
Usage 2 : De plus, on peut interdire de modifier robot.vx en ne créant pas de setter.
Usage 3 : Autre utilisation de @property, pouvoir appeler robot.quadrant avec la valeur calculée uniquement quand demandée.

Dans le fichier code_makeFiles.py, on utilise le format pbm, qui est très facile à écrire. Pas besoin de librairie image !
Un des deux formats permet même d'écrire les 0 et 1 en mode texte (mais prend 150Mo de place au lieu de 40).
  1. code.py
  2. scoring.jpg
  3. code_spotIt.py
  4. spotIt.png
  5. code_makeFiles.py
  6. spoil_dessinSapinATrouver.png
import re
import turtle
import math

with open("input.txt", 'r', encoding='utf-8') as f:
    lines = [line[:-1] for line in f.readlines()]

def parse_ints(line: str) -> list[int]:
    return [ int(token) for token in re.split('[^-0-9]+', line) if token ]

WINDOW_SIZE = 800

class Robot:
    W = 101
    H = 103

    def __init__(self, position, velocity, flip_vertically = True):
        self.position = position
        if flip_vertically:
            velocity[1] *= -1
            self.y = Robot.H - 1 - self.y
        self._v = tuple(velocity)

    @property
    def x(self):
        return self._p[0]
    @property
    def y(self):
        return self._p[1] 
    @property
    def position(self):
        return self._p

    @x.setter
    def x(self, value):
        self._p[0] = value
    @y.setter
    def y(self, value):
        self._p[1] = value
    @position.setter
    def position(self, value):
       self._p = list(value)

    @property
    def vx(self):
        return self._v[0]
    @property
    def vy(self):
        return self._v[1] 
    @property
    def v(self):
        return self._v

    def move(self):
        self.x = (self.x + self.vx) % Robot.W
        self.y = (self.y + self.vy) % Robot.H

    @property
    def quadrant(self):
        if self.x == (Robot.W//2) or self.y == (Robot.H//2):
            return None
        return 1 + [(haut,gauche) for haut in (True,False) for gauche in (True, False)].index( (self.x < Robot.W//2, self.y < Robot.H//2) )
        
        

robots = []
for line in lines:
    px, py, *v = parse_ints(line)
    robots.append(Robot((px, py), v))

turtle.tracer(0)

turtle.hideturtle()

scores = {'nb_near':[], 'nb_neighbours':[], 'sum_dist_center':[], 'min_dist_center':[]}
positions = []

for time in range(Robot.W*Robot.H + 10):
    if time % Robot.W == 0:
        print(".", end='', flush=True)

    # PART 1
    if time == 100:
        nbs = {None:0, 1:0, 2:0, 3:0, 4:0}
        for rob in robots:
            nbs[rob.quadrant] += 1
        nbs[None] = 1
        print("\nRéponse partie 1:", math.prod(nbs.values()))

    nb_near = 0
    nb_neighbours = 0
    sum_dist_center = 0
    min_dist_center = max(Robot.W, Robot.H)

    positions_now = []
    for rob in robots:
        rob.move()
        positions_now.append(tuple(rob.position))
        dist = (rob.x - Robot.W//2)**2 + (rob.y - Robot.H//2)**2
        if dist < min_dist_center:
            min_dist_center = dist
        sum_dist_center += dist

    pos_now_set = set(positions_now)
    while pos_now_set:
        x,y = pos_now_set.pop()
        for dx, dy in ((0,-1),(-1,0)):
            if (x+dx, y+dy) in pos_now_set:
                nb_neighbours += 1
                nb_near += 1
        for dx, dy in ((-1,-1),(-1,1)):
            if (x+dx, y+dy) in pos_now_set:
                nb_near += 1
    scores['nb_near'].append((nb_near, time))
    scores['nb_neighbours'].append((nb_neighbours, time))
    scores['sum_dist_center'].append((sum_dist_center, time))
    scores['min_dist_center'].append((min_dist_center, time))
    positions.append(positions_now)

print()



NB_COLS_ROWS = 5
VIEW_SIZE = WINDOW_SIZE // NB_COLS_ROWS


for Y, measure in enumerate(scores.keys()):
    turtle.teleport(-WINDOW_SIZE//2, WINDOW_SIZE - Y * (VIEW_SIZE+35) - 30 - WINDOW_SIZE//2)
    turtle.write(measure, font=("Arial", 8, "bold"), align="left")
    tab = scores[measure]
    tab.sort(reverse = (measure != 'sum_dist_center'))
    if measure.startswith('min'):
        interestings = tab[:5]
    else:
        interestings = tab[:3] + tab[-2:]
    for X, (score, time) in enumerate(interestings):
        x0 = X * VIEW_SIZE - WINDOW_SIZE // 2
        y0 = WINDOW_SIZE - (Y+1) * (VIEW_SIZE + 35) - WINDOW_SIZE // 2
        turtle.teleport(x0, y0)
        for _ in range(4):
            turtle.forward(VIEW_SIZE)
            turtle.left(90)
        for x,y in positions[time]:
            turtle.teleport(x0 + x * VIEW_SIZE // Robot.W,  y0 + y * VIEW_SIZE // Robot.H)
            turtle.dot(1, 'blue')
        turtle.teleport(x0 + VIEW_SIZE // 2, y0)
        turtle.write('time:' + str(time + 1) + " (score=" + str(score) + ")", font=("Arial", 8, "bold"), align="center")
    for d in range(3):
        turtle.teleport(3*VIEW_SIZE - WINDOW_SIZE//2 + (d-1) * 15, y0 + VIEW_SIZE//2)
        turtle.dot(8, 'black')

turtle.done()