summary refs log tree commit diff
path: root/aufgabe3/src
diff options
context:
space:
mode:
Diffstat (limited to 'aufgabe3/src')
-rw-r--r--aufgabe3/src/main.rs194
1 files changed, 148 insertions, 46 deletions
diff --git a/aufgabe3/src/main.rs b/aufgabe3/src/main.rs
index 6cb6724..94b8fa5 100644
--- a/aufgabe3/src/main.rs
+++ b/aufgabe3/src/main.rs
@@ -5,86 +5,161 @@ use std::fs;
 use std::process;
 use std::str::FromStr;
 
-fn main() {
-    let task_file_name = match env::args().nth(1) {
-        Some(x) => x,
-        None => {
-            eprintln!("Nutzung: aufgabe3 <dateiname>");
-            process::exit(1);
-        }
-    };
-    let task_str = fs::read_to_string(task_file_name).expect("Datei kann nicht gelesen werden");
-    let task = Task::try_from(task_str.as_str()).expect("Datei enthält keine gültige Aufgabe");
-    let solution = solve_task(task);
-
-    print!("{}", solution)
-}
-
+// Typ, der eine zu lösende Aufgabe beschreibt.
 struct Task {
     number: HexNumber,
-    max_moves: u32,
+    max_moves: usize,
 }
 
+// Vom Algorithmus zurückgegebene Lösung.
+// Der Typ enthält ein paar redundante Informationen, die aber die Ausgabe des
+// Programms anschaulicher machen.
 struct Solution {
+    // Die gegebene Anfangs-Hex-Zahl.
     initial: HexNumber,
+    // Alle Zwischenstände zwischen den Umlegungen, einschließlich des Anfangs-
+    // und Endzustandes.
     states: Vec<SevenSegmentDisplayRow>,
+    // Die ermittelte größtmögliche Hex-Zahl.
     r#final: HexNumber,
-    max_moves: u32,
-    used_moves: u32,
+    // Die gegebene Maximalzahl an Umlegungen.
+    max_moves: usize,
+    // Die Anzahl der tatsächlich genutzten Umlegungen.
+    used_moves: usize,
 }
 
+// Implementiert den ersten Teil des Algorithmus, der aus einer Aufgabe die
+// größtmögliche Hex-Zahl errechnet.
 fn solve_pt1(task: &Task) -> Vec<HexDigit> {
+    // `solve_pt1_internal` ist der rekursive Teil der Funktion, der versucht,
+    // den gegebenen Ausschnitt der Hex-Zahl zu maximieren.
     fn solve_pt1_internal(
-        state: &[HexDigit],
-        segment_balance: i32,
-        segment_flips_left: u32,
-        vacant_segments_left: u32,
-        available_segments_left: u32,
+        // Der zu maximierende Ausschnitt der Hex-Zahl.
+        digits: &[HexDigit],
+        // `segment_balance` enthält im Laufe der Rekursion immer den
+        // "Kontostand" der Stäbchen. Wenn es einen Überschuss bzw. Mangel an
+        // Stäbchen gibt, ist `segment_balance` positiv bzw. negativ. Nur wenn
+        // `segment_balance` am Ende 0 ist, kann die neue Ziffernreihe durch
+        // Umlegungen realisiert werden.
+        segment_balance: isize,
+        // `segment_flips_left` gibt an, wie viele Stäbchen-Positionen noch
+        // "geflippt" (d.h. gefüllt oder geleert) werden können, ohne die
+        // Maximalzahl an Umlegungen zu überschreiten.
+        segment_flips_left: usize,
+        // `vacant_segments_left` gibt an, wie viele überschüssige Stäbchen
+        // theoretisch noch in den hinteren Ziffern untergebracht werden
+        // könnten, wenn diese zu '8' (Ziffer bestehend aus den meisten
+        // Stäbchen) aufgefüllt werden.
+        vacant_segments_left: usize,
+        // `occupied_segments_left` gibt an, wie viele fehlende Stäbchen
+        // theoretisch noch aus den hinteren Ziffern entnommen werden könnten,
+        // wenn diese zu '1' (Ziffer bestehend aus den wenigsten Stäbchen)
+        // reduziert werden.
+        occupied_segments_left: usize,
     ) -> Option<Vec<HexDigit>> {
-        if segment_balance.abs() as u32 > segment_flips_left
-            || segment_balance > vacant_segments_left as i32
-            || -segment_balance > available_segments_left as i32
+        // Es gibt mehrere Backtracking-Bedingungen, die signalisieren, dass
+        // dieser Pfad zu keiner gültigen Lösung führen kann.
+        if
+        // 1. Der Überschuss bzw. Mangel an Stäbchen ist so groß, dass er nicht
+        // mehr ausgeglichen werden kann, ohne die Maximalzahl an Umlegungen zu
+        // überschreiten.
+        segment_balance.abs() as usize > segment_flips_left
+            // 2. Der Überschuss an Stäbchen ist so groß, dass er nicht mehr
+            // ausgeglichen kann, selbst wenn alle übrigen Ziffern zu '8'
+            // aufgefüllt werden.
+            || segment_balance > vacant_segments_left as isize
+            // 3. Der Mangel an Stäbchen ist so groß, dass er nicht mehr
+            // ausgeglichen werden kann, selbst wenn alle übrigen Ziffern zu
+            // '1' reduziert werden.
+            || -segment_balance > occupied_segments_left as isize
         {
             return None;
         }
-        match state.split_first() {
+        // Es wird immer nur die erste Ziffer betrachtet, und die restlichen
+        // Ziffern werden rekursiv ergänzt.
+        match digits.split_first() {
+            // Falls schon alle Ziffern betrachtet wurden, handelt es sich nur
+            // um eine gültige Lösung, wenn der Stäbchen-Kontostand
+            // `segment_balance` gleich 0 ist (s.o.).
             None => match segment_balance {
                 0 => Some(Vec::new()),
                 _ => None,
             },
             Some((digit, rest)) => {
+                // Die Anzahl der Stäbchen, aus der die erste Ziffer besteht.
+                let digit_num_segments = digit.num_segments();
+                // `vacant_segments_left` und `occupied_segments_left` müssen
+                // bezogen auf die restlichen Stäbchen angepasst werden.
+                // '8', die Ziffer bestehend aus den meisten Stäbchen, enthält
+                // 7 Stäbchen.
+                let new_vacant_segments_left = vacant_segments_left - (7 - digit_num_segments);
+                // '1', die Ziffer bestehend aus den wenigsten Stäbchen,
+                // enthält 2 Stäbchen.
+                let new_occupied_segments_left = occupied_segments_left - (digit_num_segments - 2);
+
+                // Es wird versucht, die erste Ziffer zu maximieren. Dafür
+                // werden alle möglichen Hex-Ziffern absteigend durchgegangen
+                // und jeweils versucht, die erste Ziffer auf die gewählte
+                // Ziffer zu setzen und davon ausgehend die restlichen Ziffern
+                // anzupassen, sodass am Ende die gefundene maximale Hex-Zahl
+                // durch eine nicht zu hohe Anzahl an Umlegungen erreicht
+                // werden kann.
                 for candidate_digit in (0x0..=0xF).rev().map(HexDigit) {
-                    let digit_num_segments = digit.num_segments();
+                    // `segment_num_difference` ist die Differenz zwischen der
+                    // alten und der neuen Anzahl an Stäbchen für die erste
+                    // Ziffer.
                     let segment_num_difference =
-                        digit_num_segments as i32 - candidate_digit.num_segments() as i32;
+                        digit_num_segments as isize - candidate_digit.num_segments() as isize;
+                    // `num_required_segment_flips` ist die erforderliche
+                    // Anzahl an "Stäbchen-Flips" (s.o.), um von der alten
+                    // ersten Ziffer zur neuen zu kommen.
                     let num_required_segment_flips =
                         digit.num_required_segment_flips(&candidate_digit);
 
+                    // Der "Stäbchen-Kontostand" sowie die noch verfügbare
+                    // Anzahl an "Stäbchen-Flips" werden bezogen auf die
+                    // restlichen Ziffern angepasst.
                     let new_segment_balance = segment_balance + segment_num_difference;
                     let new_segment_flips_left =
                         match segment_flips_left.checked_sub(num_required_segment_flips) {
+                            // Wenn schon die Änderung der ersten Ziffer nicht
+                            // mehr mit den übrigen "Stäbchen-Flips" zu
+                            // bewerkstelligen ist, kann sofort zum
+                            // nächstniedrigeren Wert für die erste Ziffer
+                            // übergegangen werden.
                             None => continue,
                             Some(x) => x,
                         };
-                    let new_vacant_segments_left = vacant_segments_left - (7 - digit_num_segments);
-                    let new_available_segments_left =
-                        available_segments_left - (digit_num_segments - 2);
 
                     match (new_segment_flips_left, new_segment_balance) {
+                        // Wenn keine weiteren Umlegungen mehr möglich sind und
+                        // es keinen Mangel oder Überschuss an Stäbchen gibt,
+                        // ist die Lösung gefunden. Die restlichen Ziffern
+                        // bleiben unverändert.
                         (0, 0) => {
                             let mut ret = vec![candidate_digit];
                             ret.append(&mut rest.to_vec());
                             return Some(ret);
                         }
+                        // Wenn keine weiteren Umlegungen mehr möglich sind,
+                        // aber es einen Mangel oder Überschuss an Stäbchen
+                        // gibt, führt dieser Pfad zu keiner Lösung. Es wird
+                        // der nächstniedrigere Wert für die erste Ziffer
+                        // probiert.
                         (0, _) => continue,
+                        // Ansonsten wird versucht, die restlichen Ziffern
+                        // rekursiv zu maximieren.
                         (_, _) => {
                             let mut new_rest = match solve_pt1_internal(
                                 rest,
                                 new_segment_balance,
                                 new_segment_flips_left,
                                 new_vacant_segments_left,
-                                new_available_segments_left,
+                                new_occupied_segments_left,
                             ) {
+                                // Falls dies nicht gelingt, wird der
+                                // nächstniedrigere Wert für die erste Ziffer
+                                // probiert.
                                 None => continue,
                                 Some(x) => x,
                             };
@@ -94,33 +169,45 @@ fn solve_pt1(task: &Task) -> Vec<HexDigit> {
                         }
                     }
                 }
+                // Der Pfad wird verworfen, da unter den gegebenen Bedingungen
+                // für keinen Wert der ersten Ziffer eine gültige neue
+                // Ziffernfolge gefunden werden konnte.
                 None
             }
         }
     }
 
+    // Die Maximalzahl an "Stäbchen-Flips" ist zweimal die Maximalzahl an
+    // Umlegungen, da eine Umlegung aus zwei "Stäbchen-Flips" besteht: an einer
+    // Position wird ein Stäbchen entfernt und an einer zweiten ein Stäbchen
+    // abgelegt.
     let max_segment_flips = 2 * task.max_moves;
-    let (initial_vacant_segments, initial_available_segments) = task.number.digits.iter().fold(
+    // Die Startwerte für `vacant_segments_left` und `occupied_segments_left`
+    // werden ermittelt, indem einmal über alle Ziffern iteriert wird.
+    let (initial_vacant_segments, initial_occupied_segments) = task.number.digits.iter().fold(
         (0, 0),
-        |(acc_vacant_segments, acc_available_segments), digit| {
+        |(acc_vacant_segments, acc_occupied_segments), digit| {
             let num_segments = digit.num_segments();
             (
                 acc_vacant_segments + (7 - num_segments),
-                acc_available_segments + (num_segments - 2),
+                acc_occupied_segments + (num_segments - 2),
             )
         },
     );
+
     solve_pt1_internal(
-        task.number.digits.as_slice(),
+        &task.number.digits,
         0,
         max_segment_flips,
         initial_vacant_segments,
-        initial_available_segments,
+        initial_occupied_segments,
     )
     .unwrap()
 }
 
-// TODO: darstellung darf nie komplett geleert werden -> ist erfüllt
+// Implementiert den zweiten Teil des Algorithmus, der aus der gegebenen
+// Hex-Zahl und der errechneten größtmöglichen Hex-Zahl eine Abfolge von
+// Umlegungen erzeugt.
 fn solve_pt2(start: &[HexDigit], end: &[HexDigit]) -> Vec<SevenSegmentDisplayRow> {
     let start_state = start.into_iter().map(|digit| digit.to_seven_segments());
     let end_state = end.into_iter().map(|digit| digit.to_seven_segments());
@@ -185,11 +272,26 @@ fn solve_task(task: Task) -> Solution {
             digits: greatest_possible_number,
         },
         max_moves: task.max_moves,
-        used_moves: (states.len() - 1) as u32,
+        used_moves: states.len() - 1,
         states,
     }
 }
 
+fn main() {
+    let task_file_name = match env::args().nth(1) {
+        Some(x) => x,
+        None => {
+            eprintln!("Nutzung: aufgabe3 <dateiname>");
+            process::exit(1);
+        }
+    };
+    let task_str = fs::read_to_string(task_file_name).expect("Datei kann nicht gelesen werden");
+    let task = Task::try_from(task_str.as_str()).expect("Datei enthält keine gültige Aufgabe");
+    let solution = solve_task(task);
+
+    print!("{}", solution)
+}
+
 impl TryFrom<&str> for Task {
     type Error = ();
 
@@ -198,7 +300,7 @@ impl TryFrom<&str> for Task {
         let number_str = lines.next().ok_or(())?;
         let max_moves_str = lines.next().ok_or(())?;
         let number = HexNumber::try_from(number_str)?;
-        let max_moves = u32::from_str(max_moves_str).map_err(|_| ())?;
+        let max_moves = usize::from_str(max_moves_str).map_err(|_| ())?;
 
         Ok(Task { number, max_moves })
     }
@@ -265,7 +367,7 @@ impl Display for HexDigit {
 
 impl Display for HexNumber {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        for digit in self.digits.as_slice() {
+        for digit in &self.digits {
             digit.fmt(f)?;
         }
         Ok(())
@@ -358,21 +460,21 @@ impl HexDigit {
         SevenSegmentDisplay(segments)
     }
 
-    fn num_segments(&self) -> u32 {
+    fn num_segments(&self) -> usize {
         self.to_seven_segments()
             .0
             .into_iter()
             .filter(|x| *x)
-            .count() as u32
+            .count()
     }
 
-    fn num_required_segment_flips(&self, other: &Self) -> u32 {
+    fn num_required_segment_flips(&self, other: &Self) -> usize {
         self.to_seven_segments()
             .0
             .into_iter()
             .zip(other.to_seven_segments().0.into_iter())
             .filter(|(segment_self, segment_other)| *segment_self != *segment_other)
-            .count() as u32
+            .count()
     }
 }