blog/src/aoc-2021-jour-2-dive.md
Louis Vallat 04804caf3e
Fixed non modified title in aoc2021day2
Signed-off-by: Louis Vallat <louis@louis-vallat.xyz>
2022-01-24 12:44:19 +01:00

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 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 :

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
  • 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).

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