4.6 KiB
AoC 2021 Jour 2: Dive
Comment piloter le sous-marin des elfes ?
Le défi peut être trouvé ici, et le code lié est sur gitlab.
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 :
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 deX
unités horizontalementdown X
fait plonger le sous-marin et donc augmente sa profondeur deX
unitésup 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 :
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 :
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
- augmente la position horizontale de
down X
augmente la visée deX
unitésup X
à l'inverse diminue la visée deX
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)
.
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