blog/src/aoc-2021-jour-2-dive.md

128 lines
4.6 KiB
Markdown
Raw Normal View History

# AoC 2021 Jour 2: Dive
Comment piloter le sous-marin des elfes ?
Le défi peut être trouvé [ici](https://adventofcode.com/2021/day/2), et le code
lié est sur [gitlab](https://gitlab.com/lovallat/advent-of-code-2021/-/tree/master/day2).
## Consigne du défi
Le sous-marin peut prendre 3 types de commandes :
- `forward X`
- `down X`
- `up X`
Le fichier d'entrée est une suite de ces ordres, que l'on doit interpréter pour
trouver la position du sous-marin dans l'espace (horizontalement et verticalement).
## Lecture du fichier d'entrée
La lecture du fichier d'entrée se fait à nouveau dans une seule fonction :
```rust
fn parse_input(s: &str) -> Vec<(String, i32)> {
let mut r:Vec<(String, i32)> = vec![];
let v = s.split("\n").into_iter();
for e in v {
let p = e.split_whitespace().collect::<Vec<&str>>();
if p.len() == 2 && p[1].parse::<i32>().is_ok() {
r.push((p[0].to_owned(), p[1].parse::<i32>().unwrap()));
}
}
return r;
}
```
Ici, on va tout simplement transformer une ligne en tuple `(String, i32)`, et
de transformer le fichier d'entrée en un tableau `Vec` de ces ordres. On découpe
donc une ligne `ordre X` en fonction d'un espace, et si on a bien 2 éléments et
que le deuxième peut être converti en entier signé stocké sur 32 bits, alors on
l'ajoute à notre tableau d'instructions.
On pourrait ici utiliser une énumeration telle que `Direction::Forward`,
`Direction::Up` et `Direction::Down` pour éviter de stocker les chaînes de
caractère en entier dans le tableau. On pourrait aussi utiliser des entiers stockés
sur moins de bits, étant relativement petits.
## Première partie
Pour la première partie, l'interprétation des instructions est la suivante :
- `forward X` qui permet de faire avancer le sous-marin de `X` unités horizontalement
- `down X` fait plonger le sous-marin et donc augmente sa profondeur de `X` unités
- `up X` fait remonter à l'inverse le sous-marin vers la surface
La réponse à la première partie se trouve en multipliant la position horizontale
et verticale. Pour cela, on va faire un `fold` sur le tableau pour calculer toutes
les instructions à la suite et donnant le résultat en une seule ligne :
```rust
fn get_horizontal_depth(v: Vec<(String, i32)>) -> (i32, i32) {
return v.into_iter().fold((0, 0), |acc, e| compute_horizontal_depth(e, acc));
}
```
Chaque ligne est traitée individuellement pour interprétation, en donnat en valeur
de retour les nouvelles positions du sous-marin après application de l'instruction
sur la position donnée en entrée :
```rust
fn compute_horizontal_depth(t: (String, i32), a: (i32, i32)) -> (i32, i32) {
return match t.0.as_str() {
"up" => (a.0, a.1 - t.1),
"down" => (a.0, a.1 + t.1),
"forward" => (a.0 + t.1, a.1),
_ => a
};
}
```
Ce traitement est donc très simple. Pour chaque instruction et en partant d'une
position `(0, 0)`, on va appliquer les règles les unes après les autres en mettant
à jour la position à chaque fois. Cela se fait en temps linéaire sans calcul autre
que des additions.
## Deuxième partie
Pour la deuxième partie, le traitement est assez similaire, mais on va calculer
une composante supplémentaire : la *visée*.
Les instructions sont désormais à interpréter de cette manière :
- `forward X` permet deux choses :
- augmente la position horizontale de `X` unités
- augmente la profondeur par la *visée* multipliée par `X`
- `down X` augmente la *visée* de `X` unités
- `up X` à l'inverse diminue la *visée* de `X` unités
Le concept pour calculer est le même, on applique les instructions l'une après
l'autre en partant de `(0, 0, 0)`.
```rust
fn compute_horizontal_depth_aim(t: (String, i32), a: (i32, i32, i32)) -> (i32, i32, i32) {
return match t.0.as_str() {
"up" => (a.0, a.1, a.2 - t.1),
"down" => (a.0, a.1, a.2 + t.1),
"forward" => (a.0 + t.1, a.1 + a.2 * t.1, a.2),
_ => a
};
}
```
Enfin, la réponse à donner est la multiplication de la position horizontale et
verticale, comme pour la précédente partie.
## Conclusion
Ce défi n'était pas compliqué et ma réponse me semble adaptée. Bien sûr il y a
toujours des pistes d'améliorations, telle que le type de données stockées :
les chaînes de caractères étant moins performantes à traiter, mais aussi la taille
des entiers stockés, ou encore le fait que je ne passe pas de référence aux
fonctions, nécéssitant une copie systématique, consommant des cycles supplémentaires.
Mais globalement je suis satisfait de ma réponse à ce défi.
> À suivre