summaryrefslogtreecommitdiff
path: root/articles/retour-sur-laoc-2021-semaine-1.md
blob: 32dece06c9d5d92ca19e396ec10637b69619bede (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
# Un petit retour sur [l'Advent of Code 2021](https://adventofcode.com/2021/about)

![Image de l'AoC](res/aoc.png)  
Le concept est simple. **Chaque jour du calendrier de l'avent vous devez r√©soudre un exercice de programmation** (dit des *puzzles*) qui comporte deux niveaux de difficult√©. Tout ceci est mis en contexte puisque vous aidez [les lutins](https://fr.wikipedia.org/wiki/Lutin) du p√®re No√ęl (ici des elfes, mais on France on parle de lutin !) √† r√©cup√©rer les cl√©s du tra√ģneau ... Oui le tra√ģneau du p√®re No√ęl a des cl√©s, et un lutin les a fait tomber dans l'oc√©an. Ne vous arr√™tez pas *d√©j√†* √† la narration car √ßa va devenir encore plus improbable !

Je vais donc écrire quatre *relativement* cours billets sur les exercices de chaque semaine.Avant de parler des exercices, je vous fais un retour rapide sur mon sentiment global après un petit mois.

## Ce que j'en aie pensé

Tout d'abord, c'est tr√®s int√©ressant comme d√©fi. C'est avant tout un d√©fis personnel un peu comme [l'Inktober](https://inktober.com/) (un d√©fis d'un mois o√Ļ chaque jour vous devez dessiner un dessin sur un th√®me ‚Äúfix√©‚ÄĚ √† l'avance). J'ai personnellement bien aim√© et je recommencerais l'ann√©e prochaine si possible.

Je dis bien si possible car c'est quelque chose qui prend du temps. Beaucoup de temps. Certains exercice m'ont dérangé personnellement mais ça vient également peut-être de moi.

L'évènement se targue d'être accessible aux débutants. Ce n'est qu'à moitié vrai. Au fils du mois les exercices vont se complexifier et demander un temps déraisonnable pour un débutant. Cependant, il n'est pas interdit de se faire aider, l'ensentiel étant d'apprendre. Et en se sens ça devient plus abordable pour un débutant en programmation.

Une autre difficult√© est que *les exercices sont en anglais*. √áa ne pose pas de probl√®mes de mani√®re g√©n√©rale mais √ßa reste une difficult√© puisque le vocabulaire n'est pas courant. *C'est clairement un frein* en tout cas pour quelqu'un qui ne ma√ģtrise pas la langue.

Passons maintenant à la première semaine !

## Jour 1: Un sous-marin ?!

Oui, vous embarquez dans un sous-marin pour allez chercher les clés. C'est parti pour le [premier jour](https://adventofcode.com/2021/day/1).

L'exercice du jour est de trouver le nombre de valeur supérieures à la précédente au sein d'une suite de nombres.

La solution est assez simple :

~~~py
incr = 0 # La variable qui va compter le nombre que l'on cherche

with open("input") as f:
    a = int(f.readline()) # La valeur précédente
    for line in f:
        b = int(line)     # La valeur courante
        if b > a:
            incr += 1
        a = b             # La valeur courante deviant la prochaine précédente !

print(incr)
~~~

Mais j'ai vu d'autres codes que je traduis en Python :

~~~py
with open("input") as f:
    input = list(map(int, f.read().strip().split()))
    # On compte 1 pour chaque paire d'éléments dont le premier est inférieur au second.
    print(sum(1 for 
          prev, cur in zip(input, input[1:]) if prev < cur
    ))
~~~

La difficulté 2 est de faire ceci sur une fenêtre de trois valeurs. En premier lieux, on pense à sommer tous les triplets successifs puis d'appliquer l'algorithme précédent. Mais en fait, il y a plus simple. Un triplet est plus grand si la valeur qu'on lui retire est plus petite que celle qu'on ajoute. 

Mon code est devenu un peu répétitif :

~~~py
incr = 0

with open("input") as f:
    a = int(f.readline())
    b = int(f.readline())
    c = int(f.readline())

    for line in f:
        d = int(line)
        if d > a:
            incr += 1
        a, b, c = b, c, d

print(incr)
~~~

L√† o√Ļ celui d'autres personnes √† √©t√© tr√®s peu adapt√© puisque qu'ils n'ont eu qu'√† changer qu'un seul caract√®re de leur code :
~~~py
with open("input") as f:
    input = list(map(int, f.read().strip().split()))
    print(sum(1 for 
          prev, cur in zip(input, input[3:]) if prev < cur
    ))
~~~

Ici, les paires sont en fait constitu√©s d'√©l√©ments s√©par√©s, entre-eux, de 3 plut√īt que 1.

## Jour 2 : Ben vus allez le conduire !

[Au jour deux](https://adventofcode.com/2021/day/2) vous apprenez à conduire le sous-marin !  
Le sous-marin est contr√īl√© par un plan d'instruction que vous devez √©valuer.

Le but est donc de simuler la course d'un sous-marin en fonction des instructions qu'on lui donne. C'est un sous-marin, il peut monter, descendre et avancer, c'est à peu près tout !

Exemple d'entrée :

~~~shell
forward 5
down 5
forward 8
up 3
down 8
forward 2
~~~

Là encore, c'est assez simple. On se rend bien compte qu'il n'y a que deux coordonnés importante pour le sous-marin, la profondeur et à quel point il a avancé.

~~~py
horizon, detph = 0, 0

with open('input') as f:
    for line in f:
        cmd, num = line.split(' ')
        num = int(num)
        if cmd == "forward":
            horizon += num
        elif cmd == "down":
            detph += num
        elif cmd == "up":
            detph -= num

print( horizon *  detph) # On nous demande ce nombre pour vérifier la simultation
~~~ 

La difficulté 2 est peu différente. Cette fois-ci, on doit calculer la visée ? (`aim` en anglais) du sous-marin. Qui elle-même influe les actions du sous-marin.

~~~py
aim = 0
horizon, detph = 0, 0

with open('input') as f:
    for line in f:
        cmd, num = line.split(' ')
        num = int(num)

        if cmd == "forward":
            horizon += num
            detph += aim * num
        elif cmd == "down":
            aim += num
        elif cmd == "up":
            aim -= num

print( horizon *  detph)
~~~

Ici, il n'y a pas de difficulté mais ça reste un puzzle peu optimisable. Un peu aussi dérangeant qu'un [Fizz Buzz](https://en.wikipedia.org/wiki/Fizz_buzz).

## Jour 3: Lire le diagnostic

[Le sous-marin vous donne un diagnostic dans un format binaire](https://adventofcode.com/2021/day/3). Vous devez calculer le taux *gamma* et *epsilon* dont dépend la consommation du carburant.

Vous avez pour ça une suite de nombre binaire :

~~~shell
00100
11110
10110
10111
10101
01111
00111
11100
10000
11001
00010
01010
~~~

On doit chercher le bit le plus courant pour chiffre de cette liste de nombre binaire. Dans la première colonne cela nous donne `1` car c'est le nombre le plus courant. Pour chacun des indices ça donne `10110` soit 22 en décimal, cette valeur est le taux *gamma*. Le taux epsilon est similaire mais avec le bit le moins courant.

Bon ben c'est parti pour coder ça. J'ai d'abord fait [un truc bien moche dont je suis pas fière](articles/res/code1_j03_aoc.py).

Si je devais le r√©-√©crire √ßa donnerait plut√īt √ßa :

~~~py
from collections import Counter

count = Counter()
length = 0

with open("input") as f:
    for line in f:
        count.update(Counter(enumerate(line.strip())))
    length = len(line) - 1

gamma   = ['1' if count[(i, '1')] > count[(i, '0')] else '0' for i in range(length)]
epsilon = ['1' if count[(i, '0')] > count[(i, '1')] else '0' for i in range(length)]

# On convertit en décimal
gamma   = int("".join(gamma), 2)
epsilon = int("".join(epsilon), 2)

print(epsilon * gamma)
~~~

C'est-à-dire que Python propose des structures de données qu'on peut réutiliser pour les actions courantes comme compter. C'est exactement ce que je vais ici et on va voir que c'est quelques chose qu'on va souvent utiliser par la suite !

En avant pour la partie 2 !

Cette fois-ci on doit itérer plusieurs fois sur les valeurs (c'est la première fois que l'on doit faire ça) et à chaque fois ne garder que les valeurs qui ont le plus n^ième^ bit le plus (le moins pour l'autre donnée) courant. On continue jusqu'à ce qu'il n'y en ait plus qu'un.

Bon ben l√†, j'ai d√Ľ tout refaire mon code. Mais j'ai quand m√™me fait [un code pas super propre](articles/res/code2_j03_aoc.py).
Ouais, y a un bon gros copier/coller. Bon si je devais le recoder j'aurais fait ça :

~~~py
from collections import Counter

with open("input") as f:
    number = f.read().split()

number_oxy, number_co2 = list(number), list(number)
length, i = len(number_oxy[0]), 0

while len(number_oxy) > 1 or len(number_co2) > 1 and i < length:
    if len(number_oxy) > 1:
        oxy = Counter(map(lambda x: x[i], number_oxy))
        # On garde le bit le plus courant, si ex equo, on prend '1'.
        keep = '1' if oxy.most_common()[0][1] == oxy.most_common()[1][1] else oxy.most_common()[0][0]
        number_oxy = [ number for number in number_oxy if number[i] == keep]

    if len(number_co2) > 1:
        co2 = Counter(map(lambda x: x[i], number_co2))
        # On garde le bit le moins courant, si ex equo, on prend '0'.
        keep = '0' if co2.most_common()[0][1] == co2.most_common()[1][1] else co2.most_common()[1][0]
        number_co2 = [ number for number in number_co2 if number[i] == keep]
    i += 1

oxy_rate = int("".join(number_oxy[0]), 2)
CO2_rate = int("".join(number_co2[0]), 2)

print(oxy_rate * CO2_rate)
~~~

C'est là même pensé que mon précédant code mais en plus concis et plus claire. L'idée c'est vraiment de suivre l'algo décrit dans les consignes.

## Jour 4: Bingo ?!

Je vous assure vous êtes pas prêt. Le sous-marin se fait attraper pas un [calamar géant](https://fr.wikipedia.org/wiki/Calmar_g%C3%A9ant). Et ...

> Peut-être veut-il jouer au [Bingo](https://fr.wikipedia.org/wiki/Bingo) ?
> *[Jour 4](https://adventofcode.com/2021/day/4) !*

√áa tombe bien le sous-marin a un syst√®me permettant de jouer au Bingo pour occuper ses passagers. ūü§¶

Donc nous voil√† entrain de jouer au Bingo. Le calamar √† plusieurs tentacules et le risque de perdre est proportionnel au nombre de tentacule du calamar. Alors apparemment, on triche et on demande √† l'ordinateur du sous-marin de nous donner √† l'avance la liste des nombres qu'il va sortir. Comme √ßa apr√®s, on peut prendre la premi√®re grille qui gagne ūü¶Ļ !

Bon ben la stratégie ça va être de remplacer les nombres déjà sortis par des zéros et si on a une ligne de 0 alors la grille a gagné !

~~~py
bingo_numbers, bingo_grid = [], []


def replaceByZero(number, grid):
    for i in range(5):
        for j in range(5):
            if grid[i][j] == number:
                grid[i][j] = 0
                return

def hasZeroRowOfCols(grid):
    for i in range(5):
        allZero_row, allZero_col = True, True

        for j in range(5):
            if grid[j][i] != 0:
                allZero_col = False
            if grid[i][j] != 0:
                allZero_row = False

        if allZero_row or allZero_col:
            return True

    return False


with open("input") as f:
    bingo_numbers = list(map(int, f.readline().split(',')))

    while True:
        line = f.readline()
        if not line:
            break
        bingo_grid.append([list(map(int, filter(lambda x: x != '', f.readline().strip().split(' ')))) for _ in range(5)])

i, number = 0, 0
for number in bingo_numbers:
    for grid in bingo_grid:
        replaceByZero(number, grid)

    for i in range(len(bingo_grid)):
        if hasZeroRowOfCols(bingo_grid[i]):
            break
    else:
        continue
    break

print(sum(sum(bingo_grid[i], [])) * number)
~~~

La deuxi√®me partie, semble-t-il que le calamar est connu pour ne pas aimer perdre. Alors il vaudrait mieux √™tre s√Ľr qu'il ne perde sur aucune de ses tentacules. Pour cela, vous devez prendre la derni√®re grille qui gagne.

Le code est pas plus compliqué, on change juste la condition de sortie et on supprime les grilles au fur et à mesure.

~~~py
number = 0
for number in bingo_numbers:
    for grid in bingo_grid:
        replaceByZero(number, grid)

    for i in reversed(range(len(bingo_grid))):
        if hasZeroRowOfCols(bingo_grid[i]):
            if len(bingo_grid) <= 1:
                break
            del bingo_grid[i]
    else:
        continue
    break

print(sum(sum(bingo_grid[0], [])) * number)
~~~


Voilà pour aujourd'hui !

## Jour 5: Les fumées tuent

[Les monts hydrothermaux](https://fr.wikipedia.org/wiki/Mont_hydrothermal) cr√©ent d'√©paisses *fumerolles* qu'il serait bon d'√©viter. Le sous-marin produit une liste de lignes o√Ļ il d√©tecte trop de ces *fumerolles*.
Pour mieux comprendre la mise en contexte, je vous invite à lire l'[énoncé du jour 5](https://adventofcode.com/2021/day/5).

Bon mon id√©e a √©t√© de cr√©er une grille o√Ļ on va tracer les lignes. Chaque case de la grille comptant le nombre de fois qu'on est pass√© par cette case.

Mais √ßa ne passe pas √† l'√©chelle. Si la grille devient tr√®s grande alors on risque de stocker beaucoup de points √† 0 qui seront inutiles. Il y a deux types de lignes, les lignes droites et les lignes diagonales qui forme un angle de 45¬į (par rapport √† l'horizontal ou la verticale).

Dans un premier temps on se concentre sur les lignes droites. C'est à dire, horizontales ou verticales. Ça donne ça :

~~~py
from collections import defaultdict

segs = []
max_X, max_Y = 0, 0
pts = defaultdict(int)

with open('input') as f:
    for line in f:
        x1, y1, x2, y2 = map(int, line.strip().replace(' -> ', ',').split(','))
        segs.append((x1, y1, x2, y2))
        max_X, max_Y = max(max_X, x1, x2), max(max_Y, y1, y2)

for s in segs:
    x1, y1, x2, y2 = s
    # Si c'est une ligne droites
    if x1 == x2 or y1 == y2:
        x_min, x_max = min(x1, x2), max(x1, x2)
        y_min, y_max = min(y1, y2), max(y1, y2)
        for x in range(x_min, x_max + 1):
            for y in range(y_min, y_max + 1):
                pts[(x,y)] += 1

print(len(list(filter(lambda x: x > 1, pts.values()))))
~~~

L'idée c'est que soit `range(x_min, x_max + 1)` soit `range(y_min, y_max + 1)` sera réduit à un seul élément et on retrouvera donc bien une droite. Donc pour tracer un segment, on part du point du segment le plus en haut à gauche (celui qui minimise `x` et `y`) jusqu'à l'autre point.


Partie 2, il faut prendre en compte toutes les lignes. Et c'est un peu là qu'on regrette de ne pas avoir fait un code plus générique... Ou alors pas assez *ad hoc*.

~~~py
for s in segs:
    x1, y1, x2, y2 = s
    pts[(x1, y1)] += 1

    while (x1, y1) != (x2, y2):
        # On se rapproche de 1 à chaque fois si on y est pas déjà

        if x1 < x2:
            x1 += 1
        elif x1 > x2:
            x1 -= 1

        if y1 > y2:
            y1 -= 1
        elif y1 < y2:
            y1 += 1
   
        pts[(x1, y1)] += 1
~~~

Oui, *eh* bien ça fait le taf. Peut-être que l'idée de l'exercice était d'utiliser [l'algorithme de Bresenham](https://fr.wikipedia.org/wiki/Algorithme_de_trac%C3%A9_de_segment_de_Bresenham). Mais au final il se fait très bien sans. Si on essaye de s'en inspirer tout de même on peut arriver à ce code :

~~~py
from collections import defaultdict

segs = []
max_X, max_Y = 0, 0

with open('input') as f:
    for line in f:
        x1, y1, x2, y2 = map(int, line.strip().replace(' -> ', ',').split(','))
        segs.append((x1, y1, x2, y2))
        max_X, max_Y = max(max_X, x1, x2), max(max_Y, y1, y2)

pts = defaultdict(int)

for s in segs:
    x1, y1, x2, y2 = s
    # Le vecteur directeur ‚Äúunitaire‚ÄĚ
    dx, dy = 0, 0

    if x1 != x2:
        dx  = -1 if x1 > x2 else 1
    if y1 != y2:
        dy  = -1 if y1 > y2 else 1

    pts[(x1, y1)] += 1
    while (x1, y1) != (x2, y2):
        x1, y1 = x1 + dx, y1 + dy
        pts[(x1, y1)] += 1

print(len(list(filter(lambda x: x > 1, pts.values()))))
~~~

Que je trouve assez satisfaisant.

## Jour 6: Les poissons-lanternes

On rencontre des poissons-lanternes ! Stupéfait par le nombre, on décide de *modéliser leur [taux de croissance](https://fr.wikipedia.org/wiki/Taux_de_croissance_relative)* que l'on imagine être exponentiel !
Pour vérifier ça, on va faire l'hypothèse que chaque poisons-lanternes donne un nouveau poisson lanternes tous les 7 jours. Pourquoi ? Ben pourquoi pas. Les clés du traineau peuvent bien attendre, la curiosité scientifique ne peut pas !

> [I heard you like lanternefish](https://adventofcode.com/2021/day/6) !
> *Jour 6*

[![Un poisson lanterne](articles/res/Lobianchia_dofleini.jpg)](https://commons.wikimedia.org/wiki/File:Lobianchia_dofleini.jpg)

Une dernière supposition, un poisson lanterne arrive à maturité en 2 jours. Oui, c'est pris au pif mais ça semble pas trop mal. Le sous-marin nous donne l'age (enfin l'état du compteur interne de gestation) des poisson-lanternes des alentours. Par exemple `3,4,3,1,2`. Chaque jour ce compteur diminue de 1 et dès qu'un poisson passe en dessous de 0, il crée un nouveau poisson et son compteur se remet à 6.

Bon ben modélisons ça ! On nous demande de compter pour le nombre de poisson après 80 jours.

~~~py
with open('input') as f:
    list_fish = map(int, f.readline().split(','))

for _ in range(80):
    list_fish = [f - 1 for f in list_fish]
    numberUnderZero = len(list(filter(lambda x: x == -1, list_fish)))
    list_fish = [6 if x < 0 else x for x in list_fish] + [8] * numberUnderZero

print(len(list_fish))
~~~

Simple comme bonjour. C'est littéralement l'algorithme de l'énoncé.


Difficult√© 2, √ßa se complique. On nous demande de faire pareil mais pour 256. Si comme on suppose le nombre de poisson cro√ģt de mani√®re exponentielle, alors en 256 jours on aura beaucoup trop de boissons !
On ne peut plus se permettre de compter les poissons un par un.

Mais on a seulement 9 √©tats diff√©rents de poisson. Comptons √ßa plut√īt !

~~~py
from collections import Counter, defaultdict

with open('input') as f:
    fish = Counter(map(int, f.readline().split(',')))

for i in range(256):
    fish = defaultdict(int, {j - 1: nb for j, nb in fish.items()})
    fish[6] += fish[-1]
    fish[8] += fish[-1]
    fish[-1] = 0

print(Counter(fish).total())
~~~

*Ah* oui ! On aurait d√Ľ commencer par l√†, c'√©tait plus simple en fait. Mais le probl√®me sugg√©rait une autre m√©thode de r√©solution. C'est pas bien grave. Continuons !

## Jour 7: Le prochain repas d'un cachalot

Un cachalot nous prend pour son prochain repas et fonce vers nous ! [L'énoncé](https://adventofcode.com/2021/day/7) parle d'une *whale*, qu'on devrait traduire par baleine mais le lien wikipédia redirige vers *Sperm whale* qui est ce que nous appelons un (grand) cachalot[^sperm].

[^sperm]: C'est tout de même plus classe que baleines à spermes ! Désolé d'utiliser une langue évoluée.

Des crabes dans des petits sous-marins viennent à notre secours. Ils peuvent nous pousser pour aller plus vite, mais ils ont besoin d'être alignés pour ça. Et ils ont une quantité d'énergie vraiment limité.

On aimerait bien les aider à nous aider !

Pour ce probl√®me, on a une liste de nombre correspondant √† l'abscisse des petits sous-marins. On doit calculer le co√Ľt minimum pour que tous les sous-marins soient align√©s sur la m√™me abscisse.

~~~py
def align(i, l):
    return sum(map(lambda x: abs(i - x), l))

with open('input') as f:
    list_pos = list(map(int, f.readline().split(',')))

min_, max_ = min(list_pos), max(list_pos)
cout = [align(i, list_pos) for i in range(min_, max_)]

print(min(cout))
~~~

Le co√Ľt d'alignement est la valeur absolue de la diff√©rence entre l'abscisse o√Ļ est le sous-marin et l√† o√Ļ il doit s'aligner. Le code est assez clair je pense. 


Difficult√© 2 ! Les crabes sont pas super ravis. √áa ne leur sert √† rien car leur sous-marin ne consomme pas de mani√®re lin√©aire mais plut√īt quadratique !

Pour aller de 16 √† 5, un sous-marin √† besoin de 66 unit√©s de carburant. Pour le premier centim√®tre, √ßa ne co√Ľte qu'un. Pour le deuxi√®me √ßa en co√Ľte deux. Et ainsi de suite jusqu'au onzi√®me qui co√Ľte 11. 

Bon √ßa ne change pas √©norm√©ment, on doit remettre √† jour notre calcul du co√Ľt. Pour cela, on peut utiliser la formule :

$$
\sum_{i=0}^n i = \frac{n (n + 1)}{2}
$$

~~~py
def align(i, l):
    return sum(map(lambda x: abs(i - x) * (abs(i - x) + 1) // 2, l))
~~~

Et voil√† ! Les crabes nous sortent de cette situation un peu d√©licate. ūü¶Ä

## Conclusion

Voil√† qui est fait pour la premi√®re semaine ! ūü•≥

Le niveau pour l'instant est tout à fait abordable pour un débutant et les exercices sont très ludiques. La mise en contexte est absurde mais rigolote.

On peut facilement comparer ces exercices à ceux de [France-IOI](http://www.france-ioi.org/) ou de [Prologin](https://prologin.org/). 


Je reviendrais dans quelques semaines pour vous parler de la semaine 2 de l'Advent of Code. Mon rythme de publication est très faible alors ne vous étonnez pas si vous n'avez pas de nouvelles avant le mois de Mars.

[Divulg√Ęchage](https://fr.wiktionary.org/wiki/divulg%C3%A2cher), √ßa va se corser un peu !

<div class="pubdate">Publié le 29 Décembre 2021</div>