Add Aufgabe 3
BIN
Aufgabe3/Aufgabe3_windows64bit.exe
Executable file
7
Aufgabe3/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "Aufgabe3"
|
||||
version = "0.1.0"
|
||||
authors = ["Arne <arne.keller01@yahoo.de>"]
|
||||
|
||||
[dependencies]
|
||||
colored = "1.3"
|
7
Aufgabe3/lösung.txt
Normal file
@ -0,0 +1,7 @@
|
||||
[GUS, GUS, GUS, GUS, US, GUS]
|
||||
[US, US, US, US, GUS, US, US, GUS, GUS, GUS, GUS, GUS, GUS, GUS, US, US, US, US, US, US, GUS, US]
|
||||
[GUS, US, US, US, US, GUS, GUS, GUS, US, US, US, GUS, GUS, GUS, GUS, GUS, GUS, GUS, US, US, GUS, GUS, US, US, US, US, US, US, US, GUS, US, US, US, US, US, US, US, US, US, US, US, GUS, GUS, GUS, US, US, US, US, US, GUS, GUS, GUS, GUS, GUS, US, US, US, US, GUS, GUS, US, US, US, US, US, US, US, GUS, US, GUS, GUS, GUS, GUS, GUS, GUS, GUS, GUS, GUS, GUS, GUS, US, GUS, GUS, US, US, US, US, US, GUS, US]
|
||||
bzw.
|
||||
[↺, ↺, ↺, ↺, ↻, ↺]
|
||||
[↻, ↻, ↻, ↻, ↺, ↻, ↻, ↺, ↺, ↺, ↺, ↺, ↺, ↺, ↻, ↻, ↻, ↻, ↻, ↻, ↺, ↻]
|
||||
[↺, ↻, ↻, ↻, ↻, ↺, ↺, ↺, ↻, ↻, ↻, ↺, ↺, ↺, ↺, ↺, ↺, ↺, ↻, ↻, ↺, ↺, ↻, ↻, ↻, ↻, ↻, ↻, ↻, ↺, ↻, ↻, ↻, ↻, ↻, ↻, ↻, ↻, ↻, ↻, ↻, ↺, ↺, ↺, ↻, ↻, ↻, ↻, ↻, ↺, ↺, ↺, ↺, ↺, ↻, ↻, ↻, ↻, ↺, ↺, ↻, ↻, ↻, ↻, ↻, ↻, ↻, ↺, ↻, ↺, ↺, ↺, ↺, ↺, ↺, ↺, ↺, ↺, ↺, ↺, ↻, ↺, ↺, ↻, ↻, ↻, ↻, ↻, ↺, ↻]
|
9
Aufgabe3/rotation10_n.txt
Normal file
@ -0,0 +1,9 @@
|
||||
7
|
||||
#######
|
||||
# 333#
|
||||
2#
|
||||
#1 2#
|
||||
#1 2#
|
||||
#100 2#
|
||||
#######
|
||||
Nicht nur Ausgänge unten sind erlaubt!
|
BIN
Aufgabe3/rotation10_n.txt.bild.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
7
Aufgabe3/rotation10_n_lösung.txt
Normal file
@ -0,0 +1,7 @@
|
||||
-3 -3 -3 -3 -3 -3 -3
|
||||
-3 -2 -2 -2 -2 -2 -3
|
||||
-3 -2 02 02 02 02 -3
|
||||
-3 -2 -2 -2 -2 00 -3
|
||||
-3 -2 03 -2 -2 00 -3
|
||||
-3 -2 03 01 01 01 -3
|
||||
-3 -3 03 -3 -3 -3 -3
|
BIN
Aufgabe3/rotation10_n_lösung.txt.bild.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
11
Aufgabe3/rotation11_n.txt
Normal file
@ -0,0 +1,11 @@
|
||||
7
|
||||
#######
|
||||
# 222#
|
||||
|
||||
#3 1#
|
||||
#3 1#
|
||||
#300 1#
|
||||
#######
|
||||
Mehrere Ausgänge sind möglich!
|
||||
Hier würde Stab 2 aufgrund der Schwerkraft
|
||||
ohne Drehung runterfallen.
|
BIN
Aufgabe3/rotation11_n.txt.bild.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
7
Aufgabe3/rotation11_n_lösung.txt
Normal file
@ -0,0 +1,7 @@
|
||||
-3 -3 -3 -3 -1 -3 -3
|
||||
-3 -2 -2 -2 -2 -2 -3
|
||||
-3 03 03 03 -2 -2 -3
|
||||
-3 00 -2 -2 -2 -2 -3
|
||||
-3 00 -2 -2 02 -2 -3
|
||||
-3 01 01 01 02 -2 -3
|
||||
-3 -3 -3 -3 02 -3 -3
|
BIN
Aufgabe3/rotation11_n_lösung.txt.bild.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
12
Aufgabe3/rotation12_n.txt
Normal file
@ -0,0 +1,12 @@
|
||||
7
|
||||
#######
|
||||
#89555#
|
||||
#6 7#
|
||||
#4 3#
|
||||
#41 3#
|
||||
#42203#
|
||||
#######
|
||||
Kein Ausgang ist auch möglich!
|
||||
Hier mit vielen Würfeln, um mein Programm zu quälen...
|
||||
in ca. 6s gibt mein Programm aus,
|
||||
dass es keine Lösung finden konnte
|
BIN
Aufgabe3/rotation12_n.txt.bild.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
11
Aufgabe3/rotation13_n.txt
Normal file
@ -0,0 +1,11 @@
|
||||
7
|
||||
#######
|
||||
# 55#
|
||||
# 44#
|
||||
# 33#
|
||||
# 22#
|
||||
#0 11#
|
||||
## ###
|
||||
Größere Ausgänge sind auch möglich!
|
||||
Allerdings ist nicht gewährleistet, dass der gesamte
|
||||
Ausgang genutzt wird.
|
BIN
Aufgabe3/rotation13_n.txt.bild.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
7
Aufgabe3/rotation13_n_lösung.txt
Normal file
@ -0,0 +1,7 @@
|
||||
-3 -3 -3 -3 -3 -3 -3
|
||||
-3 -2 -2 -2 05 05 -3
|
||||
-3 -2 -2 -2 04 04 -3
|
||||
-3 -2 -2 -2 03 03 -3
|
||||
-3 -2 -2 -2 02 02 -3
|
||||
-3 -2 -2 -2 01 01 -3
|
||||
-3 -3 -1 00 -3 -3 -3
|
BIN
Aufgabe3/rotation13_n_lösung.txt.bild.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
5
Aufgabe3/rotation14_c.txt
Normal file
@ -0,0 +1,5 @@
|
||||
4
|
||||
####
|
||||
# 0#
|
||||
#0 #
|
||||
####
|
BIN
Aufgabe3/rotation14_c.txt.bild.png
Normal file
After Width: | Height: | Size: 546 B |
5
Aufgabe3/rotation15_c.txt
Normal file
@ -0,0 +1,5 @@
|
||||
4
|
||||
###
|
||||
# #
|
||||
# 0#
|
||||
####
|
BIN
Aufgabe3/rotation15_c.txt.bild.png
Normal file
After Width: | Height: | Size: 541 B |
9
Aufgabe3/rotation1_03.txt
Normal file
@ -0,0 +1,9 @@
|
||||
8
|
||||
########
|
||||
# 0#
|
||||
# 0#
|
||||
#112222#
|
||||
#33 4#
|
||||
#55 4#
|
||||
#666 4#
|
||||
### ####
|
BIN
Aufgabe3/rotation1_03.txt.bild.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
8
Aufgabe3/rotation1_03_lösung.txt
Normal file
@ -0,0 +1,8 @@
|
||||
-3 -3 -3 -3 -3 -3 -3 -3
|
||||
-3 -2 -2 02 02 02 02 -3
|
||||
-3 -2 -2 -2 -2 -2 04 -3
|
||||
-3 -2 -2 -2 01 01 04 -3
|
||||
-3 -2 -2 -2 03 03 04 -3
|
||||
-3 -2 -2 -2 -2 05 05 -3
|
||||
-3 -2 -2 00 06 06 06 -3
|
||||
-3 -3 -3 00 -3 -3 -3 -3
|
BIN
Aufgabe3/rotation1_03_lösung.txt.bild.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
13
Aufgabe3/rotation2_03.txt
Normal file
@ -0,0 +1,13 @@
|
||||
12
|
||||
############
|
||||
# #
|
||||
# 01 #
|
||||
# 01 #
|
||||
# 01 #
|
||||
# 222222#
|
||||
# 34 5 #
|
||||
# 34 5 #
|
||||
# 634 5 #
|
||||
# 63477775 #
|
||||
# 63888885 #
|
||||
###### #####
|
BIN
Aufgabe3/rotation2_03.txt.bild.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
12
Aufgabe3/rotation2_03_lösung.txt
Normal file
@ -0,0 +1,12 @@
|
||||
-3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3 -3
|
||||
-3 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -3
|
||||
-3 -2 -2 -2 -2 -2 -2 -2 -2 -2 -2 -3
|
||||
-3 02 02 02 02 02 02 -2 -2 -2 -2 -3
|
||||
-3 00 07 07 07 07 -2 -2 -2 -2 -2 -3
|
||||
-3 00 03 -2 05 -2 -2 -2 -2 -2 -2 -3
|
||||
-3 00 03 04 05 -2 -2 -2 -2 -2 -2 -3
|
||||
-3 06 03 04 05 -2 -2 -2 -2 -2 -2 -3
|
||||
-3 06 03 04 05 -2 -2 -2 -2 -2 -2 -3
|
||||
-3 06 03 04 05 -2 01 -2 -2 -2 -2 -3
|
||||
-3 08 08 08 08 08 01 -2 -2 -2 -2 -3
|
||||
-3 -3 -3 -3 -3 -3 01 -3 -3 -3 -3 -3
|
BIN
Aufgabe3/rotation2_03_lösung.txt.bild.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
19
Aufgabe3/rotation3_03.txt
Normal file
@ -0,0 +1,19 @@
|
||||
10
|
||||
##########
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# 7775#
|
||||
# 11 5#
|
||||
# 2 888#
|
||||
#990233 #
|
||||
#44066666#
|
||||
##### ####
|
||||
9 sec
|
||||
89 dep
|
||||
373 MB
|
||||
270 MB i64 -> i32
|
||||
172 MB i32 -> i8
|
||||
90 MB cache-optimierung
|
||||
7.35 sec opt. get_stäbchen (1800ns -> 1100ns)
|
||||
6.75 sec opt. ^ with_capacity (1100ns -> 975ns)
|
BIN
Aufgabe3/rotation3_03.txt.bild.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
10
Aufgabe3/rotation3_03_lösung.txt
Normal file
@ -0,0 +1,10 @@
|
||||
-3 -3 -3 -3 -3 -3 -3 -3 -3 -3
|
||||
-3 -2 -2 -2 -2 -2 -2 -2 -2 -3
|
||||
-3 -2 -2 -2 -2 -2 -2 -2 -2 -3
|
||||
-3 -2 -2 -2 -2 -2 -2 -2 -2 -3
|
||||
-3 -2 -2 -2 -2 -2 -2 -2 -2 -3
|
||||
-3 07 07 07 09 09 -2 -2 -2 -3
|
||||
-3 06 06 06 06 06 08 08 08 -3
|
||||
-3 04 04 -2 -2 -2 02 00 -2 -3
|
||||
-3 01 01 03 03 05 02 00 -2 -3
|
||||
-3 -3 -3 -3 -3 05 -3 -3 -3 -3
|
BIN
Aufgabe3/rotation3_03_lösung.txt.bild.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
16
Aufgabe3/rotation4_n.txt
Normal file
@ -0,0 +1,16 @@
|
||||
10
|
||||
##########
|
||||
# #
|
||||
# #
|
||||
# 8#
|
||||
#99 8#
|
||||
#7 66#
|
||||
#5 0#
|
||||
#4 1#
|
||||
#3 2#
|
||||
##### ####
|
||||
69 sek
|
||||
96 tiefe
|
||||
607 MB ...
|
||||
66 sek ...
|
||||
30 sek ...
|
BIN
Aufgabe3/rotation4_n.txt.bild.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
17
Aufgabe3/rotation6_n.txt
Normal file
@ -0,0 +1,17 @@
|
||||
12
|
||||
############
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
#4 #
|
||||
#4 99#
|
||||
#33 8#
|
||||
#22 7#
|
||||
#1 6#
|
||||
#00 55555#
|
||||
###### #####
|
||||
44 sek
|
||||
223 tiefe
|
||||
297 MB
|
||||
18 sek ...
|
BIN
Aufgabe3/rotation6_n.txt.bild.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
27
Aufgabe3/rotation7_n.txt
Normal file
@ -0,0 +1,27 @@
|
||||
14
|
||||
##############
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
#4 #
|
||||
#4 99#
|
||||
#33 8#
|
||||
#22 7#
|
||||
#1 6#
|
||||
#00 555555#
|
||||
####### ######
|
||||
54 sek
|
||||
223 tiefe
|
||||
3000 MB
|
||||
2100 MB
|
||||
770 MB i64 -> i8
|
||||
48 sek ... + ^
|
||||
340 MB ... + RAM sparen im Cache
|
||||
49 sek ... + ^
|
||||
344 MB rustup
|
||||
53 sek rustup
|
||||
30 sek ... + get_stäbchen + ...
|
||||
20 sek ... + gravitationsoptimierung
|
BIN
Aufgabe3/rotation7_n.txt.bild.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
15
Aufgabe3/rotation8_n.txt
Normal file
@ -0,0 +1,15 @@
|
||||
14
|
||||
##############
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# 4#
|
||||
#8 4#
|
||||
#8 3#
|
||||
#8 3#
|
||||
#9 1#
|
||||
#9 21#
|
||||
#9 BB 520#
|
||||
#9AA 520#
|
||||
#6677 CCCCCCC#
|
||||
####### ######
|
BIN
Aufgabe3/rotation8_n.txt.bild.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
7
Aufgabe3/rotation9_n.txt
Normal file
@ -0,0 +1,7 @@
|
||||
5
|
||||
#####
|
||||
# #
|
||||
#1 0#
|
||||
#1 0#
|
||||
## ##
|
||||
Ungerade Seitenlängen sind möglich!
|
BIN
Aufgabe3/rotation9_n.txt.bild.png
Normal file
After Width: | Height: | Size: 956 B |
5
Aufgabe3/rotation9_n_lösung.txt
Normal file
@ -0,0 +1,5 @@
|
||||
-3 -3 -3 -3 -3
|
||||
-3 -2 -2 -2 -3
|
||||
-3 -2 -2 00 -3
|
||||
-3 -2 01 00 -3
|
||||
-3 -3 01 -3 -3
|
BIN
Aufgabe3/rotation9_n_lösung.txt.bild.png
Normal file
After Width: | Height: | Size: 926 B |
786
Aufgabe3/src/main.rs
Normal file
@ -0,0 +1,786 @@
|
||||
#![feature(non_ascii_idents)] // für umlaute in variablen_namen
|
||||
#![feature(test)] // für benchmarks
|
||||
|
||||
/* Generelle Informationen
|
||||
* - die Makros set und get setten und getten
|
||||
* jeweils einen Punkt im Raster
|
||||
* - farbige Ausgabe des Puzzles nur mit Linux und evtl. Mac
|
||||
* - Groß- und Kleinschreibung (in kommentaren) eher weniger
|
||||
* - index eines Stabes == die Zahl in der Datei (0-9 und A-Z bzw. 0-36)
|
||||
*/
|
||||
|
||||
const RAHMEN: i8 = -3;
|
||||
const LEER: i8 = -2;
|
||||
const AUSGANG: i8 = -1;
|
||||
|
||||
const MINIMALE_AUSGÄNGE: usize = 0; // problemlos veränderbar (Aufgabenstellung -> 1)
|
||||
const MAXIMALE_AUSGÄNGE: usize = 1000; // problemlos veränderbar (Aufgabenstellung -> 1)
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
extern crate colored;
|
||||
#[cfg(target_os = "linux")]
|
||||
use colored::*;
|
||||
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::time::Instant;
|
||||
use std::path::Path;
|
||||
|
||||
|
||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||
pub struct Puzzle {
|
||||
raster: Vec<Vec<i8>>
|
||||
}
|
||||
|
||||
// meistens nur als Vec<Punkt> == stab verwendet
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Punkt {
|
||||
x: usize,
|
||||
y: usize
|
||||
}
|
||||
|
||||
// in der Gravitationsfunktion benutzt
|
||||
#[derive(PartialEq)]
|
||||
enum StabRichtung {
|
||||
Horizontal,
|
||||
Vertikal
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum DrehRichtung {
|
||||
// UhrzeigerSinn (rechtsrum)
|
||||
US,
|
||||
// Gegen den UhrzeigerSinn (linksrum)
|
||||
GUS
|
||||
}
|
||||
|
||||
// zum farbigen anzeigen des puzzles
|
||||
#[cfg(target_os = "linux")]
|
||||
impl std::fmt::Display for Puzzle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
for zeile in &self.raster {
|
||||
for zelle in zeile {
|
||||
match *zelle {
|
||||
RAHMEN => write!(f, "{}", format!("{:02} ", zelle).black().bold())?,
|
||||
LEER => write!(f, "{}", format!("{:02} ", zelle).black().bold())?,
|
||||
AUSGANG => write!(f, "{}", format!("{:02} ", zelle).green().bold())?,
|
||||
// hiermit werden die Farben abwechselnd verteilt,
|
||||
// die gleiche Farbe wird aber evtl. an mehrere Stäbe verteilt
|
||||
_ if zelle % 5 == 0 => write!(f, "{}", format!("{:02} ", zelle).red().bold())?,
|
||||
_ if zelle % 5 == 1 => write!(f, "{}", format!("{:02} ", zelle).yellow().bold())?,
|
||||
_ if zelle % 5 == 2 => write!(f, "{}", format!("{:02} ", zelle).blue().bold())?,
|
||||
_ if zelle % 5 == 3 => write!(f, "{}", format!("{:02} ", zelle).purple().bold())?,
|
||||
_ if zelle % 5 == 4 => write!(f, "{}", format!("{:02} ", zelle).cyan().bold())?,
|
||||
_ => write!(f, "{:02} ", zelle)?
|
||||
};
|
||||
}
|
||||
write!(f, "\n")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// und nochmal für windows usw. (ohne farben)
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
impl std::fmt::Display for Puzzle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
for zeile in &self.raster {
|
||||
for zelle in zeile {
|
||||
write!(f, "{:02} ", zelle)?;
|
||||
}
|
||||
write!(f, "\n")?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// weil ich es kann, wird bei mir [US, GUS] als [↻, ↺] angezeigt
|
||||
impl std::fmt::Display for DrehRichtung {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
if self == &DrehRichtung::US {
|
||||
write!(f, "↻")
|
||||
} else {
|
||||
write!(f, "↺")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lädt ein Puzzle aus einer Datei und kann Fehler zurückgeben
|
||||
pub fn lade_puzzle(dateiname: &str) -> Result<Puzzle, String> {
|
||||
let pfad = Path::new(dateiname);
|
||||
|
||||
let mut datei = match File::open(&pfad) {
|
||||
Err(fehler) => { return Err(format!("Konnte Datei {} nicht öffnen: {}", dateiname,
|
||||
fehler.description())); },
|
||||
Ok(datei) => datei,
|
||||
};
|
||||
|
||||
let mut s = String::new();
|
||||
if let Err(fehler) = datei.read_to_string(&mut s) {
|
||||
return Err(format!("Konnte Datei {} nicht lesen: {}",
|
||||
dateiname, fehler.description()));
|
||||
}
|
||||
let zeilen: Vec<&str> = s.split('\n').collect();
|
||||
let mut puzzle = Puzzle { raster: Vec::with_capacity(zeilen.len()) };
|
||||
let seitenlänge = zeilen[0].parse();
|
||||
if seitenlänge.is_err() {
|
||||
return Err("erste Zeile der Datei enthält mehr oder weniger als eine Zahl".to_string());
|
||||
}
|
||||
let seitenlänge = seitenlänge.unwrap();
|
||||
if seitenlänge < 3 {
|
||||
return Err(format!("Puzzle-Raster ist zu klein, Seitenlänge == {:?}", seitenlänge).to_string());
|
||||
}
|
||||
|
||||
// in den Beispielen ist der Index der Stäbe jeweils zwischen 0-9
|
||||
let mut gefundene_stab_indexe = Vec::new();
|
||||
let mut gefundene_ausgänge = 0;
|
||||
|
||||
// skip(1) weil die erste Zeile nur die Seitenlänge ist
|
||||
for (y, zeile) in zeilen.iter().skip(1).enumerate() {
|
||||
// zeile nach der letzten raster-zeile erreicht
|
||||
// (nach dem Raster könnten noch Kommentare folgen)
|
||||
if y == seitenlänge {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut raster_zeile = Vec::with_capacity(zeile.len());
|
||||
for (x, buchstabe) in zeile.chars().enumerate() {
|
||||
// der Ausgang kann entweder links, rechts, oben oder unten sein
|
||||
if buchstabe == ' ' && ((x == 0 || x == zeile.len()-1) || (y == 0 || y == seitenlänge-1)) {
|
||||
gefundene_ausgänge += 1;
|
||||
if gefundene_ausgänge > MAXIMALE_AUSGÄNGE {
|
||||
return Err("Zu viele Ausgänge in Datei".to_string())
|
||||
}
|
||||
raster_zeile.push(AUSGANG);
|
||||
continue;
|
||||
}
|
||||
// zahlen von 0-9 und buchstaben von A-Z oder a-z werden anerkannt (A == 10, Z == 36)
|
||||
match buchstabe.to_digit(36) {
|
||||
Some(zahl) => {
|
||||
if !gefundene_stab_indexe.contains(&zahl) {
|
||||
gefundene_stab_indexe.push(zahl);
|
||||
}
|
||||
raster_zeile.push(zahl as i8)
|
||||
},
|
||||
// buchstabe ist keine zahl..
|
||||
None => {
|
||||
match buchstabe {
|
||||
'#' => raster_zeile.push(RAHMEN),
|
||||
' ' => raster_zeile.push(LEER),
|
||||
// mit '_' wird jeder buchstabe "gematcht"
|
||||
_ => return Err(format!("Unbekannter Buchstabe: {:?}", buchstabe))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if seitenlänge != raster_zeile.len() {
|
||||
return Err(format!("Seitenlänge stimmt nicht mit Länge der Raster-Zeile {:?} überein!", y+1))
|
||||
}
|
||||
puzzle.raster.push(raster_zeile);
|
||||
}
|
||||
|
||||
if seitenlänge != puzzle.raster.len() {
|
||||
return Err("Seitenlänge stimmt nicht mit Raster-Höhe überein".to_string())
|
||||
}
|
||||
|
||||
if gefundene_stab_indexe.is_empty() {
|
||||
return Err("Leider enthält das Raster keine Stäbe :(".to_string())
|
||||
}
|
||||
|
||||
gefundene_stab_indexe.sort(); // aufsteigend, also z.b. [0, 1, ..]
|
||||
let erster = gefundene_stab_indexe[0];
|
||||
if erster != 0 {
|
||||
return Err("Die Stäbe müssen von 0 aufsteigend nummeriert sein.".to_string())
|
||||
}
|
||||
// hier wird fold missbraucht, um sicherzustellen, dass die Stäbe
|
||||
// von 0 ansteigend durchgängig nummeriert sind
|
||||
gefundene_stab_indexe.iter().skip(1).fold(erster, |acc, &x| if (x as isize) - 1 == acc as isize { x } else { panic!("Die Stäbe sind nicht aufsteigend nummeriert :(") });
|
||||
|
||||
// Stäbe, bei denen nicht klar ist, ob sie vertikal oder
|
||||
// horizontal vorliegen, werden hier erkannt
|
||||
if !verifiziere_stäbchen(&finde_stäbchen(&puzzle.raster)) {
|
||||
return Err("Stäbchen ergeben keinen Sinn".to_string())
|
||||
}
|
||||
|
||||
// falls der Rahmen zu wenig Ausgänge enthält oder
|
||||
// unvollständig ist, wird ein Fehler ausgegeben
|
||||
if !verifiziere_rahmen(&puzzle.raster) {
|
||||
return Err("Rahmen des Puzzles ist nicht korrekt aufgebaut".to_string())
|
||||
}
|
||||
|
||||
Ok(puzzle)
|
||||
}
|
||||
|
||||
fn verifiziere_rahmen(raster: &[Vec<i8>]) -> bool {
|
||||
// Rahmen, deren Länge zwei oder kleiner beträgt,
|
||||
// könnene keine Stäbe oder Ausgänge enthalten
|
||||
if raster.len() <= 2 {
|
||||
false
|
||||
} else {
|
||||
let mut gefundene_ausgänge = 0;
|
||||
for (y, zeile) in raster.iter().enumerate() {
|
||||
// ist die erste und letzte erste Zahl ein ausgang oder der rahmen
|
||||
if !(zeile[0] != RAHMEN || zeile.last() != Some(&RAHMEN))
|
||||
&& !(zeile[0] != AUSGANG || zeile.last() != Some(&AUSGANG)) {
|
||||
return false;
|
||||
}
|
||||
if y == 0 || y == raster.len()-1 {
|
||||
// an den Ecken des Rahmens darf nur der Rahmen sein,
|
||||
// und nicht z.b. der Ausgang
|
||||
if zeile[0] != RAHMEN || zeile.last() != Some(&RAHMEN) {
|
||||
return false;
|
||||
}
|
||||
for feld in zeile {
|
||||
match *feld {
|
||||
AUSGANG => gefundene_ausgänge += 1,
|
||||
// in der obersten und untersten Zeile
|
||||
// sollten keine Stäbe sein
|
||||
_ if *feld >= 0 => return false,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// logisch
|
||||
if !(gefundene_ausgänge >= MINIMALE_AUSGÄNGE) || !(gefundene_ausgänge <= MAXIMALE_AUSGÄNGE) {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// überprüft, dass alle stäbchen eindeutig vertikal
|
||||
// oder horizontal sind
|
||||
fn verifiziere_stäbchen(stäbchen: &[Vec<Punkt>]) -> bool {
|
||||
// keine Stäbchen -> unnötiges Puzzle?!
|
||||
if stäbchen.len() == 0 {
|
||||
false
|
||||
} else {
|
||||
for stab in stäbchen {
|
||||
if !verifiziere_stab(stab) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
// überprüft, ob ein Stab eindeutig vertikal
|
||||
// oder horizontal ist
|
||||
fn verifiziere_stab(stab: &[Punkt]) -> bool {
|
||||
// Stab mit Länge 0 -> eig. unmöglich
|
||||
if stab.len() == 0 {
|
||||
false
|
||||
} else {
|
||||
let eig_richtung = get_stäbchen_richtung(stab);
|
||||
if eig_richtung == StabRichtung::Horizontal {
|
||||
// bei horizontalen Stäben sollte die y-Koordinate immer gleich sein
|
||||
let eig_y = stab[0].y;
|
||||
for p in stab {
|
||||
if p.y != eig_y {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// bei vertikalen Stäben sollte die x-Koordinate immer gleich sein
|
||||
let eig_x = stab[0].x;
|
||||
for p in stab {
|
||||
if p.x != eig_x {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drehe(mut puzzle: Puzzle, dir: &DrehRichtung) -> Puzzle {
|
||||
// seitenlänge
|
||||
let s = puzzle.raster.len();
|
||||
|
||||
// makros wie immer
|
||||
macro_rules! get {
|
||||
($x:expr, $y:expr) => (
|
||||
puzzle.raster[$y][$x]
|
||||
)
|
||||
}
|
||||
macro_rules! set {
|
||||
($x:expr, $y:expr, $new:expr) => (
|
||||
puzzle.raster[$y][$x] = $new;
|
||||
)
|
||||
}
|
||||
let maxx;
|
||||
let maxy;
|
||||
if s % 2 == 0 { // gerade seitenlänge
|
||||
// mit 'x' markierte Punkte werden unten in der Schleife
|
||||
// unten bearbeitet
|
||||
/* - - - - - -
|
||||
* - x x -
|
||||
* - x x -
|
||||
* - -
|
||||
* - -
|
||||
* - - - - - */
|
||||
maxx = s/2-1;
|
||||
maxy = s/2-1;
|
||||
} else { // ungerade seitenlänge
|
||||
// Punkt 'c' bleibt gleich
|
||||
/* - - - - - - -
|
||||
* - x x x -
|
||||
* - x x x -
|
||||
* - c -
|
||||
* - -
|
||||
* - -
|
||||
* - - - - - - */
|
||||
maxx = ((s as f32/2.0)-0.5) as usize;
|
||||
maxy = ((s as f32/2.0)-1.5) as usize;
|
||||
}
|
||||
// "Bereiche" oben-links bis unten-links
|
||||
let mut ol;
|
||||
let mut or; // enthält den Wert des Punktes,
|
||||
let mut orx; // die x-Koordinate des Punktes und
|
||||
let mut ory; // die y-Koordinate des Punktes
|
||||
// usw.
|
||||
let mut ur; let mut urx; let mut ury;
|
||||
let mut ul; let mut ulx; let mut uly;
|
||||
// oberer linker Bereich wird abgearbeitet
|
||||
for oly in 0..maxy+1 {
|
||||
for olx in 0..maxx+1 {
|
||||
ol = get!(olx, oly);
|
||||
orx = s-oly-1; ory = olx; or = get!(orx, ory);
|
||||
urx = s-ory-1; ury = orx; ur = get!(urx, ury);
|
||||
ulx = s-ury-1; uly = urx; ul = get!(ulx, uly);
|
||||
if dir == &DrehRichtung::US {
|
||||
set!(orx, ory, ol);
|
||||
set!(urx, ury, or);
|
||||
set!(ulx, uly, ur);
|
||||
set!(olx, oly, ul);
|
||||
// Ergebnis: (nur das Innere des Rahmens)
|
||||
// Punkt B erhält den Wert von A (ol)
|
||||
// C von B (or)
|
||||
// D von C (ur)
|
||||
// A von D (ul)
|
||||
/* - A - -
|
||||
* - - - B
|
||||
* D - - -
|
||||
* - - C - */
|
||||
} else {
|
||||
// hier genau umgekehrt wie oben
|
||||
set!(ulx, uly, ol);
|
||||
set!(urx, ury, ul);
|
||||
set!(orx, ory, ur);
|
||||
set!(olx, oly, or);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
puzzle
|
||||
}
|
||||
|
||||
// findet alle Stäbe in einem Raster bzw. Puzzle
|
||||
pub fn finde_stäbchen(raster: &[Vec<i8>]) -> Vec<Vec<Punkt>> {
|
||||
// hier werden die stäbchen nach ihrem index sortiert gespeichert
|
||||
let mut stäbchen = Vec::with_capacity(STÄBCHEN_ANZAHL_SCHÄTZUNG);
|
||||
|
||||
// jeden Punkt besuchen
|
||||
for (y, zeile) in raster.iter().enumerate() {
|
||||
for (x, zelle) in zeile.iter().enumerate() {
|
||||
// falls negativ: kein teil eines stabes
|
||||
if !zelle.is_negative() {
|
||||
// in richtigen "zahltyp" umrechnen
|
||||
let zelle = *zelle as usize;
|
||||
// falls der Vektor noch keinen Vektor für diesen
|
||||
// Stab enthält, wird er bis dahin aufgefüllt
|
||||
if stäbchen.is_empty() || stäbchen.len() - 1 < zelle {
|
||||
for _ in stäbchen.len()..zelle+1 {
|
||||
stäbchen.push(Vec::new());
|
||||
}
|
||||
}
|
||||
// danach wird der Punkt im Vektor gespeichert
|
||||
stäbchen.get_mut(zelle).expect("Code 0-un").push(Punkt { x: x, y: y });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stäbchen
|
||||
}
|
||||
|
||||
fn get_stäbchen_richtung(stab: &[Punkt]) -> StabRichtung {
|
||||
// falls der Stab kein Würfel ist und die x-Koordinaten der
|
||||
// ersten beiden elemente gleich sind,
|
||||
if stab.len() > 1 && stab[0].x == stab[1].x {
|
||||
// ist er vertikal
|
||||
StabRichtung::Vertikal
|
||||
} else {
|
||||
StabRichtung::Horizontal
|
||||
}
|
||||
}
|
||||
|
||||
// diese Variable dient nur der Optimierung
|
||||
// (diese Kapazität wird der Vektor, der in finde_stäbchen erstellt wird,
|
||||
// zunächst haben (diese Kapatizät nachträglich zu vergrößern benötigt
|
||||
// einige Zeit))
|
||||
const STÄBCHEN_ANZAHL_SCHÄTZUNG: usize = 10;
|
||||
|
||||
pub fn wende_gravitation_an(mut puzzle: Puzzle) -> Puzzle {
|
||||
// seitenlänge
|
||||
let s = puzzle.raster.len();
|
||||
// makros wie immer
|
||||
macro_rules! get {
|
||||
($x:expr, $y:expr) => (
|
||||
puzzle.raster[$y][$x];
|
||||
)
|
||||
}
|
||||
macro_rules! set {
|
||||
($x:expr, $y:expr, $new:expr) => (
|
||||
puzzle.raster[$y][$x] = $new;
|
||||
)
|
||||
}
|
||||
|
||||
// erst alle Stäbchen finden
|
||||
let stäbchen = finde_stäbchen(&puzzle.raster);
|
||||
|
||||
// mithilfe dieses Vektor werden die Stäbe sortiert, s.u.
|
||||
let mut unterste_teile_der_stäbe = Vec::with_capacity(STÄBCHEN_ANZAHL_SCHÄTZUNG);
|
||||
for stab in &stäbchen {
|
||||
// findet den Punkt mit der größten y-Koordinate,
|
||||
// also den untersten
|
||||
let unterster_punkt = stab.iter().max_by_key(|&x| x.y);
|
||||
match unterster_punkt {
|
||||
// dies passiert nur, falls der Stab keine Elemente hat (unmöglich)
|
||||
None => panic!("Code 1-un"),
|
||||
Some(punkt) => unterste_teile_der_stäbe.push(punkt)
|
||||
}
|
||||
}
|
||||
// unterste Punkte der stäbe nach ihrer y-Koordinate sortieren,
|
||||
// sodass die stäbe von unten nach oben
|
||||
// auf einmal bewegt werden können
|
||||
let mut sortiert = unterste_teile_der_stäbe.clone();
|
||||
sortiert.sort_by_key(|&x| x.y);
|
||||
sortiert.reverse();
|
||||
|
||||
// diese Schleife beginnt also mit dem untersten Stab
|
||||
for punkt_aus_stab in sortiert {
|
||||
// stab_index == zahl des stabes (in den Beispielen nur 0-9)
|
||||
let mut stab_index = None;
|
||||
// manuelle Suche des punktes in unterste_punkt,
|
||||
// um den eigentlichen Index des Stabes zu erfahren
|
||||
for (index, punkt) in unterste_teile_der_stäbe.iter().enumerate() {
|
||||
if punkt == &punkt_aus_stab {
|
||||
stab_index = Some(index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let stab_index = stab_index.expect("Code 2-un");
|
||||
let stab = stäbchen.get(stab_index).expect("Code 3-un");
|
||||
let richtung = get_stäbchen_richtung(stab);
|
||||
// die Schritte, die der Stab nach unten fallen soll
|
||||
let mut schritte = 0;
|
||||
|
||||
if richtung == StabRichtung::Vertikal {
|
||||
let unterster_punkt = punkt_aus_stab;
|
||||
// hier wird berechnet, wie viele der Punkte bis zum Rahmen (einschließlich dem Rahmen)
|
||||
// hintereinander leer sind
|
||||
for y in unterster_punkt.y+1..s {
|
||||
if get!(unterster_punkt.x, y) == LEER || get!(unterster_punkt.x, y) == AUSGANG {
|
||||
schritte += 1;
|
||||
} else { // ein anderer Stab oder der Rahmen wurde "getroffen"
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// die y-Koordinate der horizontalen Stäbe ist für jeden teil gleich
|
||||
let y_level = stab[0].y;
|
||||
|
||||
// hier wird berechnet, wie viele der Punkte bis zum Rahmen (einschließlich dem Rahmen)
|
||||
// hintereinander leer sind
|
||||
for y in y_level+1..s {
|
||||
// diese variable gibt an, ob der Stab noch weiter bewegt
|
||||
// werden sollte
|
||||
let mut sollte_bewegen = true;
|
||||
for p in stab {
|
||||
// falls unter irgendeinem der Punkte des Stabes kein freier Platz ist,
|
||||
// sollte der Stab nicht weiter bewegt werden
|
||||
if !(get!(p.x, y) == LEER || get!(p.x, y) == AUSGANG) {
|
||||
sollte_bewegen = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if sollte_bewegen {
|
||||
schritte += 1;
|
||||
} else { // ein anderer Stab oder der Rahmen wurde "getroffen"
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// falls schritte == 0 ist, passiert nichts
|
||||
for offset in 0..schritte {
|
||||
// erste vorherige Position mit LEER ersetzen
|
||||
for p in stab {
|
||||
set!(p.x, p.y+offset, LEER);
|
||||
}
|
||||
// dann neue Position mit stab_index füllen
|
||||
for p in stab {
|
||||
set!(p.x, p.y+offset+1, stab_index as i8);
|
||||
}
|
||||
}
|
||||
}
|
||||
puzzle
|
||||
}
|
||||
|
||||
// gibt an, ob das Puzzle gelöst ist
|
||||
fn puzzle_gelöst(puzzle: &Puzzle) -> bool {
|
||||
puzzle.raster
|
||||
// unterer Teil des Rahmens
|
||||
.last().expect("Code 4-un").iter()
|
||||
.fold(
|
||||
false, |acc, &x|
|
||||
// falls dieses Element zu einem Stab gehört,
|
||||
// wird acc auf true gesetzt
|
||||
if x >= 0 { true }
|
||||
else { acc })
|
||||
}
|
||||
|
||||
// wendet einen Entscheidungsweg an (mithilfe eines caches)
|
||||
fn wende_entscheidungen_an(puzzle: Puzzle, ew: &[DrehRichtung], cache: &mut HashMap<Vec<DrehRichtung>, Puzzle>) -> Puzzle {
|
||||
// ew == Entscheidungsweg
|
||||
if cache.contains_key(ew) {
|
||||
cache.get(ew).expect("Code 5-un").clone()
|
||||
} else if ew.len() == 1 { // z.b. [US] oder [GUS] (cache lohnt nicht)s
|
||||
wende_gravitation_an(drehe(puzzle, &ew[0]))
|
||||
} else {
|
||||
// Bsp. ew == [US, GUS, US]
|
||||
let mut ew = Vec::from(ew);
|
||||
// Bsp. US
|
||||
let letzte_drehung = ew.pop().unwrap();
|
||||
// Bsp. Ergebnis des EWs [US, GUS]
|
||||
let puzzle_vor_letzter_drehung = wende_entscheidungen_an(puzzle, &ew, cache);
|
||||
// Bsp. Ergebnis der Drehung US auf vorige variable
|
||||
let endzustand = wende_gravitation_an(drehe(puzzle_vor_letzter_drehung, &letzte_drehung));
|
||||
ew.push(letzte_drehung);
|
||||
cache.insert(ew, endzustand.clone());
|
||||
endzustand
|
||||
}
|
||||
}
|
||||
|
||||
pub fn erstelle_nächste_schicht(base: &[Vec<DrehRichtung>]) -> Vec<Vec<DrehRichtung>> {
|
||||
let mut nächste_schicht = Vec::new();
|
||||
// Bsp. base == [[US]]
|
||||
// -> nächste_schicht == [[US, US], [US, GUS]]
|
||||
for path in base {
|
||||
|
||||
let mut c1 = path.clone();
|
||||
let mut c2 = path.clone();
|
||||
c1.push(DrehRichtung::US);
|
||||
c2.push(DrehRichtung::GUS);
|
||||
nächste_schicht.push(c1);
|
||||
nächste_schicht.push(c2);
|
||||
}
|
||||
nächste_schicht
|
||||
}
|
||||
|
||||
// findet einen EW, der das Puzzle löst oder stellt
|
||||
// fest, dass es keine Lösung gibt
|
||||
pub fn optimierte_breiten_suche(puzzle: Puzzle) -> Option<Vec<DrehRichtung>> {
|
||||
// EW == Entscheidungsweg, z.b. [US, GUS, US]
|
||||
// cache wie in der Dokumentation beschriben
|
||||
let mut cache = HashMap::new();
|
||||
// wie in der Dokumentation
|
||||
let mut bekannte_zustände = HashSet::new();
|
||||
// ein leerer EW == vec![]
|
||||
let mut base = vec![vec![]];
|
||||
let mut tiefe = 0; // für Fortschritts-Anzeige
|
||||
let start_zeit = Instant::now(); // ^
|
||||
loop {
|
||||
let paths: Vec<Vec<DrehRichtung>> = cache.keys().cloned().collect();
|
||||
for path in paths {
|
||||
// falls der Entscheidungsweg kleiner als die Tiefe ist,
|
||||
// wird er eh nie mehr aus dem Cache geholt,
|
||||
// weil der Cache immer zuerst versucht, die Entscheidungswege
|
||||
// der letzten Ebene zu benutzen
|
||||
// (betrifft hier also die ebene vor der letzten)
|
||||
if path.len() < tiefe {
|
||||
cache.remove(&path); // spart RAM
|
||||
}
|
||||
}
|
||||
tiefe += 1;
|
||||
|
||||
// aus den EWs der letzten Ebene werden
|
||||
// die neuen EWs gebaut
|
||||
let nächste_schicht = erstelle_nächste_schicht(&base);
|
||||
|
||||
// falls diese leer ist, gibt es keine lösung
|
||||
if nächste_schicht.is_empty() {
|
||||
return None;
|
||||
}
|
||||
// eine neue Basis für die nächste runde wird erstellt
|
||||
base = Vec::new();
|
||||
for ew in &nächste_schicht {
|
||||
// EW auf Puzzle anwenden
|
||||
let tmp_puzzle = wende_entscheidungen_an(puzzle.clone(), ew, &mut cache);
|
||||
// falls noch nicht bekannt
|
||||
if !bekannte_zustände.contains(&tmp_puzzle) {
|
||||
// an base für nächste runde anfügen
|
||||
base.push(ew.clone());
|
||||
}
|
||||
if puzzle_gelöst(&tmp_puzzle) {
|
||||
// puzzle gelöst: EW zurückgeben
|
||||
return Some(ew.clone());
|
||||
} else {
|
||||
// sonst: zustand merken
|
||||
bekannte_zustände.insert(tmp_puzzle);
|
||||
}
|
||||
}
|
||||
// "Fortschrittsanzeige"
|
||||
println!("{:03} tiefe, {:06} nächste_schicht, {:07} bekannt, {:07} gecached, {:04} sekunden", tiefe, nächste_schicht.len(), bekannte_zustände.len(), cache.len(), start_zeit.elapsed().as_secs());
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Some(dateiname) = env::args().nth(1) {
|
||||
let puzzle = lade_puzzle(&dateiname);
|
||||
if let Err(e) = puzzle {
|
||||
println!("Fehler beim Einlesen: {:?}", e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Start-Zustand anzeigen
|
||||
let puzzle = wende_gravitation_an(puzzle.unwrap());
|
||||
println!("{}", puzzle);
|
||||
|
||||
if puzzle_gelöst(&puzzle) {
|
||||
println!("Puzzle ist bereits gelöst?!");
|
||||
return
|
||||
}
|
||||
|
||||
// hoffentlich eine Lösung finden
|
||||
let solution = optimierte_breiten_suche(puzzle.clone());
|
||||
if let Some(solution) = solution {
|
||||
print!("Lösung: [");
|
||||
for (index, drehung) in solution.iter().enumerate() {
|
||||
if index < solution.len()-1 {
|
||||
print!("{}, ", drehung);
|
||||
} else {
|
||||
println!("{}]", drehung);
|
||||
}
|
||||
}
|
||||
// gelöstes Puzzle anzeigen
|
||||
println!("{}", wende_entscheidungen_an(puzzle, &solution, &mut HashMap::new()));
|
||||
} else {
|
||||
println!("keine lösung gefunden!");
|
||||
}
|
||||
} else {
|
||||
println!("Bitte so aufrufen: ./target/debug/Aufgabe3 <dateiname>");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ein paar tests
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn nächste_schicht() {
|
||||
let nächste_schicht = erstelle_nächste_schicht(&vec![vec![DrehRichtung::US]]);
|
||||
let erwartung = vec![vec![DrehRichtung::US, DrehRichtung::US], vec![DrehRichtung::US, DrehRichtung::GUS]];
|
||||
if nächste_schicht != erwartung {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn t1() {
|
||||
let puzzle = lade_puzzle("rotation1_03.txt").expect("konnte datei nicht laden!");
|
||||
assert!(optimierte_breiten_suche(puzzle).expect("puzzle nicht gelöst").len() == 6);
|
||||
}
|
||||
#[test]
|
||||
fn t2() {
|
||||
let puzzle = lade_puzzle("rotation2_03.txt").expect("konnte datei nicht laden!");
|
||||
assert!(optimierte_breiten_suche(puzzle).expect("puzzle nicht gelöst").len() == 22);
|
||||
}
|
||||
#[test]
|
||||
fn t3() {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").expect("konnte datei nicht laden!");
|
||||
assert!(optimierte_breiten_suche(puzzle).expect("puzzle nicht gelöst").len() == 90);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stäbchen() {
|
||||
let puzzle = lade_puzzle("rotation1_03.txt").expect("konnte datei nicht laden!");
|
||||
let expected = vec![vec![Punkt { x: 6, y: 1 }, Punkt { x: 6, y: 2 }],
|
||||
vec![Punkt { x: 1, y: 3 }, Punkt { x: 2, y: 3 }],
|
||||
vec![Punkt { x: 3, y: 3 }, Punkt { x: 4, y: 3 }, Punkt { x: 5, y: 3 }, Punkt { x: 6, y: 3 }],
|
||||
vec![Punkt { x: 1, y: 4 }, Punkt { x: 2, y: 4 }],
|
||||
vec![Punkt { x: 6, y: 4 }, Punkt { x: 6, y: 5 }, Punkt { x: 6, y: 6 }],
|
||||
vec![Punkt { x: 1, y: 5 }, Punkt { x: 2, y: 5 }],
|
||||
vec![Punkt { x: 1, y: 6 }, Punkt { x: 2, y: 6 }, Punkt { x: 3, y: 6 }]];
|
||||
assert_eq!(finde_stäbchen(&puzzle.raster), expected);
|
||||
}
|
||||
}
|
||||
|
||||
// paar benchmarks
|
||||
#[cfg(test)]
|
||||
mod benchs {
|
||||
extern crate test;
|
||||
|
||||
use super::*;
|
||||
use self::test::Bencher;
|
||||
|
||||
/* deaktiviert wegen der Fortschrittsanzeige
|
||||
#[bench]
|
||||
fn test1(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let puzzle = lade_puzzle("rotation1_03.txt").expect("konnte datei nicht laden!");
|
||||
optimierte_breiten_suche(puzzle)
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn test2(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let puzzle = lade_puzzle("rotation2_03.txt").expect("konnte datei nicht laden!");
|
||||
optimierte_breiten_suche(puzzle)
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
#[bench]
|
||||
fn drehe_us(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").expect("konnte datei nicht laden!");
|
||||
b.iter(|| {
|
||||
drehe(puzzle.clone(), &test::black_box(DrehRichtung::US))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn drehe_gus(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").expect("konnte datei nicht laden!");
|
||||
b.iter(|| {
|
||||
drehe(puzzle.clone(), &test::black_box(DrehRichtung::GUS))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn drehe_us_mit_grav(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").expect("konnte datei nicht laden!");
|
||||
b.iter(|| {
|
||||
wende_gravitation_an(drehe(puzzle.clone(), &test::black_box(DrehRichtung::US)))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn drehe_gus_mit_grav(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").expect("konnte datei nicht laden!");
|
||||
b.iter(|| {
|
||||
wende_gravitation_an(drehe(puzzle.clone(), &test::black_box(DrehRichtung::GUS)))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn get_stäbe(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").expect("konnte datei nicht laden!");
|
||||
b.iter(|| {
|
||||
finde_stäbchen(&puzzle.raster)
|
||||
})
|
||||
}
|
||||
}
|
756
Aufgabe3/src/main_map.rs
Normal file
@ -0,0 +1,756 @@
|
||||
#![feature(non_ascii_idents)]
|
||||
#![feature(custom_derive)]
|
||||
#![feature(test)]
|
||||
#![feature(plugin)]
|
||||
|
||||
#![plugin(clippy)]
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
extern crate colored;
|
||||
#[cfg(target_os = "linux")]
|
||||
use colored::*;
|
||||
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::time::Instant;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct Punkt {
|
||||
x: usize,
|
||||
y: usize
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum Stabrichtung {
|
||||
Horizontal,
|
||||
Vertikal
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Puzzle {
|
||||
raster: Vec<Vec<i8>>
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
impl std::fmt::Display for Puzzle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
for row in &self.raster {
|
||||
write!(f, "\n")?;
|
||||
for cell in row {
|
||||
match *cell {
|
||||
WALL => write!(f, "{}", format!("{:02} ", cell).black().bold())?,
|
||||
NIX => write!(f, "{}", format!("{:02} ", cell).black().bold())?,
|
||||
AUSGANG => write!(f, "{}", format!("{:02} ", cell).green().bold())?,
|
||||
_ if cell % 5 == 0 => write!(f, "{}", format!("{:02} ", cell).red().bold())?,
|
||||
_ if cell % 5 == 1 => write!(f, "{}", format!("{:02} ", cell).yellow().bold())?,
|
||||
_ if cell % 5 == 2 => write!(f, "{}", format!("{:02} ", cell).blue().bold())?,
|
||||
_ if cell % 5 == 3 => write!(f, "{}", format!("{:02} ", cell).purple().bold())?,
|
||||
_ if cell % 5 == 4 => write!(f, "{}", format!("{:02} ", cell).cyan().bold())?,
|
||||
_ => write!(f, "{:02} ", cell)?
|
||||
};
|
||||
}
|
||||
}
|
||||
write!(f, "\n")
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
impl std::fmt::Display for Puzzle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
for row in &self.raster {
|
||||
write!(f, "\n")?;
|
||||
for cell in row {
|
||||
write!(f, "{:02} ", cell)?;
|
||||
}
|
||||
}
|
||||
write!(f, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum RotationDir {
|
||||
// clockwise
|
||||
CW,
|
||||
// counterclockwise
|
||||
CCW
|
||||
}
|
||||
|
||||
const WALL: i8 = -3;
|
||||
const NIX: i8 = -2;
|
||||
const AUSGANG: i8 = -1;
|
||||
|
||||
#[derive(Default)]
|
||||
struct MyMap<'a> {
|
||||
realmap: HashMap<Vec<RotationDir>, Puzzle>,
|
||||
valuesset: HashSet<&'a Puzzle>
|
||||
}
|
||||
|
||||
use std::collections::hash_map::Keys;
|
||||
trait SemiBidirMap<'a> {
|
||||
fn insert(&'a mut self, key: Vec<RotationDir>, value: Puzzle);
|
||||
fn get(&'a self, key: &[RotationDir]) -> Option<&'a Puzzle>;
|
||||
fn remove_key(&'a mut self, key: &Vec<RotationDir>) -> Option<Puzzle>;
|
||||
fn contains_key(&'a self, key: &[RotationDir]) -> bool;
|
||||
fn contains_value(&'a self, value: &Puzzle) -> bool;
|
||||
fn keys(&'a self) -> Keys<Vec<RotationDir>, Puzzle>;
|
||||
fn len_keys(&'a self) -> usize;
|
||||
}
|
||||
|
||||
impl<'a> SemiBidirMap<'a> for MyMap<'a> {
|
||||
fn get(&'a self, key: &[RotationDir]) -> Option<&'a Puzzle> {
|
||||
self.realmap.get(key)
|
||||
}
|
||||
fn insert(&'a mut self, key: Vec<RotationDir>, value: Puzzle) {
|
||||
self.realmap.insert(key.clone(), value);
|
||||
self.valuesset.insert(self.realmap.get(&key).unwrap());
|
||||
}
|
||||
fn remove_key(&'a mut self, key: &Vec<RotationDir>) -> Option<Puzzle> {
|
||||
self.realmap.remove(key)
|
||||
}
|
||||
fn contains_key(&'a self, key: &[RotationDir]) -> bool {
|
||||
self.realmap.contains_key(key)
|
||||
}
|
||||
fn contains_value(&'a self, value: &Puzzle) -> bool {
|
||||
self.valuesset.contains(value)
|
||||
}
|
||||
fn keys(&'a self) -> Keys<Vec<RotationDir>, Puzzle> {
|
||||
self.realmap.keys()
|
||||
}
|
||||
fn len_keys(&'a self) -> usize {
|
||||
self.realmap.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MyMap<'a> {
|
||||
fn new() -> MyMap<'a> {
|
||||
MyMap { realmap: HashMap::new(), valuesset: HashSet::new() }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lade_puzzle(dateiname: &str) -> Result<Puzzle, String> {
|
||||
let path = Path::new(dateiname);
|
||||
let display = path.display();
|
||||
|
||||
// Open the path in read-only mode, returns `io::Result<File>`
|
||||
let mut file = match File::open(&path) {
|
||||
// The `description` method of `io::Error` returns a string that
|
||||
// describes the error
|
||||
Err(why) => { return Err(format!("couldn't open {}: {}", display,
|
||||
why.description())); },
|
||||
Ok(file) => file,
|
||||
};
|
||||
|
||||
// Read the file contents into a string, returns `io::Result<usize>`
|
||||
let mut s = String::new();
|
||||
if let Err(why) = file.read_to_string(&mut s) {
|
||||
return Err(format!("couldn't read {}: {}", display,
|
||||
why.description()));
|
||||
}
|
||||
let mut p = Puzzle { raster: Vec::new() };
|
||||
let lines: Vec<&str> = s.split('\n').collect();
|
||||
let seitenlänge = lines[0].parse().expect("erste Zeile keine Zahl");
|
||||
|
||||
for (index, line) in lines.iter().skip(1).enumerate() {
|
||||
// zeile nach der letzten zeile erreicht
|
||||
if index == seitenlänge {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut line_vec = Vec::new();
|
||||
for (x, c) in line.chars().enumerate() {
|
||||
// ausgang
|
||||
if c == ' ' && ((x == 0 || x == line.len()-1) || (index == 0 || index == seitenlänge-1)) {
|
||||
line_vec.push(AUSGANG);
|
||||
continue;
|
||||
}
|
||||
match c.to_digit(36) {
|
||||
Some(digit) => line_vec.push(digit as i8),
|
||||
// -1 == " "
|
||||
None => {
|
||||
match c {
|
||||
'#' => line_vec.push(WALL),
|
||||
' ' => line_vec.push(NIX),
|
||||
_ => panic!("unbekannter buchstabe {:?}", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p.raster.push(line_vec);
|
||||
//println!("{:?}: {:?}", index, line);
|
||||
}
|
||||
Ok(p)
|
||||
}
|
||||
|
||||
pub fn rotate(puzzle: Puzzle, dir: &RotationDir) -> Puzzle {
|
||||
// seitenlänge
|
||||
let s = puzzle.raster.len();
|
||||
let mut roted_raster = puzzle.raster.clone();
|
||||
macro_rules! set {
|
||||
($x:expr, $y:expr, $new:expr) => (
|
||||
roted_raster[$y][$x] = $new;
|
||||
)
|
||||
}
|
||||
|
||||
for (y, row) in puzzle.raster.iter().enumerate() {
|
||||
for (x, cell) in row.iter().enumerate() {
|
||||
match *dir {
|
||||
RotationDir::CW => set!(s-y-1, x, *cell),
|
||||
RotationDir::CCW => set!(y, s-x-1, *cell)
|
||||
}
|
||||
}
|
||||
}
|
||||
Puzzle { raster: roted_raster }
|
||||
}
|
||||
|
||||
fn get_stäbchen(raster: &[Vec<i8>]) -> HashMap<u64, Vec<Punkt>> {
|
||||
let mut stäbchen: HashMap<u64, Vec<Punkt>> = HashMap::new();
|
||||
|
||||
for (y, row) in raster.iter().enumerate() {
|
||||
for (x, cell) in row.iter().enumerate() {
|
||||
if !cell.is_negative() {
|
||||
let cell = *cell as u64;
|
||||
let value = stäbchen.entry(cell).or_insert_with(Vec::new);
|
||||
value.push(Punkt { x: x, y: y });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stäbchen
|
||||
}
|
||||
|
||||
fn get_stäbchen_richtung(stab: &[Punkt]) -> Stabrichtung {
|
||||
if stab.len() > 1 && stab[0].x == stab[1].x {
|
||||
Stabrichtung::Vertikal
|
||||
} else {
|
||||
Stabrichtung::Horizontal
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_the_gravity(mut puzzle: Puzzle) -> Puzzle {
|
||||
// seitenlänge
|
||||
let s = puzzle.raster.len();
|
||||
macro_rules! get {
|
||||
($x:expr, $y:expr) => (
|
||||
puzzle.raster[$y][$x];
|
||||
)
|
||||
}
|
||||
macro_rules! set {
|
||||
($x:expr, $y:expr, $new:expr) => (
|
||||
puzzle.raster[$y][$x] = $new;
|
||||
)
|
||||
}
|
||||
|
||||
let mut stäbchen1 = get_stäbchen(&puzzle.raster);
|
||||
let mut stäbchen2 = HashMap::new();
|
||||
while stäbchen1 != stäbchen2 {
|
||||
//println!("sloop");
|
||||
stäbchen1 = get_stäbchen(&puzzle.raster);
|
||||
//println!("{:?}", stäbchen1);
|
||||
for (index, stab) in &stäbchen1 {
|
||||
let richtung = get_stäbchen_richtung(stab);
|
||||
let stab = stab.iter();
|
||||
let mut move_possible = stab.clone()
|
||||
.fold(true, |acc, &x|
|
||||
// falls der Stab schon direkt über dem Rahmen ist,
|
||||
// sollte er nicht bewegt werden
|
||||
if x.y >= s-2
|
||||
// falls der Stab (horizontal) liegt und unter ihm kein Platz ist,
|
||||
// sollte er nicht bewegt werden
|
||||
|| (richtung == Stabrichtung::Horizontal && ((get!(x.x, x.y+1) != NIX && get!(x.x, x.y+1) != AUSGANG))) { false } else { acc });
|
||||
|
||||
// falls der Stab (vertikal) steht,
|
||||
if richtung == Stabrichtung::Vertikal {
|
||||
// und der unterste Teil des Stabes
|
||||
let lowest_punkt = stab.clone().max_by_key(|x| x.y).unwrap();
|
||||
// über/auf dem unteren Begrenzung liegt
|
||||
if lowest_punkt.y+1 <= s-1 {
|
||||
// und unter ihm bereits ein Stab liegt,
|
||||
if get!(lowest_punkt.x, lowest_punkt.y+1) >= 0 {
|
||||
// sollte er nicht bewegt werden
|
||||
move_possible = false;
|
||||
// sonderfall: unter dem Teil des Stabes liegt der Ausgang
|
||||
} else if get!(lowest_punkt.x, lowest_punkt.y+1) == AUSGANG {
|
||||
//println!("Puzzle ist möglich!");
|
||||
move_possible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if move_possible {
|
||||
let stab: Vec<&Punkt> = stab.collect();
|
||||
for p in &stab {
|
||||
set!(p.x, p.y, NIX);
|
||||
}
|
||||
let index = *index as i8;
|
||||
for p in &stab {
|
||||
set!(p.x, p.y+1, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
stäbchen2 = stäbchen1;
|
||||
stäbchen1 = get_stäbchen(&puzzle.raster);
|
||||
//println!("eloop");
|
||||
}
|
||||
//println!("EOG");
|
||||
puzzle
|
||||
}
|
||||
|
||||
fn puzzle_gelöst(puzzle: &Puzzle) -> bool {
|
||||
puzzle.raster
|
||||
.last().unwrap().iter()
|
||||
.fold(
|
||||
false, |acc, &x|
|
||||
// falls dieses Element zu einem Stab gehört,
|
||||
// wird acc auf true gesetzt
|
||||
if x >= 0 { true }
|
||||
else { acc })
|
||||
}
|
||||
|
||||
fn apply_choices<'a>(puzzle: Puzzle, choices: &'a [RotationDir], cache: &'a mut MyMap<'a>) -> Puzzle {
|
||||
if cache.contains_key(choices) {
|
||||
//println!("{:?} cached", choices);
|
||||
cache.get(choices).unwrap().clone()
|
||||
} else if choices.len() == 1 {
|
||||
//println!("len one");
|
||||
do_the_gravity(rotate(puzzle, &choices[0]))
|
||||
} else {
|
||||
let mut choices = Vec::from(choices);
|
||||
let last_action = choices.pop().unwrap();
|
||||
let before_last = apply_choices(puzzle, &choices, cache);
|
||||
//println!("before: {}", before_last);
|
||||
let applied = do_the_gravity(rotate(before_last, &last_action));
|
||||
//println!("applied: {}", applied);
|
||||
choices.push(last_action);
|
||||
cache.insert(choices, applied.clone());
|
||||
applied
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_children(base: &[Vec<RotationDir>]) -> Vec<Vec<RotationDir>> {
|
||||
let mut children = Vec::new();
|
||||
for path in base {
|
||||
let mut c1 = path.clone();
|
||||
let mut c2 = path.clone();
|
||||
c1.push(RotationDir::CW);
|
||||
c2.push(RotationDir::CCW);
|
||||
children.push(c1);
|
||||
children.push(c2);
|
||||
}
|
||||
children
|
||||
}
|
||||
|
||||
pub fn breadth_first_search(puzzle: Puzzle) -> Option<Vec<RotationDir>> {
|
||||
//let mut cache = HashMap::new();
|
||||
let mut my_cache = MyMap::new();
|
||||
let mut known_outcomes = HashSet::new();
|
||||
let mut base_paths = vec![vec![]];
|
||||
let mut depth = 0;
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
{
|
||||
let paths: Vec<Vec<RotationDir>> = my_cache.keys().cloned().collect();
|
||||
for path in paths {
|
||||
if path.len() < depth {
|
||||
my_cache.remove_key(&path);
|
||||
}
|
||||
}
|
||||
}
|
||||
depth += 1;
|
||||
|
||||
let children = build_children(&base_paths);
|
||||
if children.is_empty() {
|
||||
return None;
|
||||
}
|
||||
base_paths = Vec::new();
|
||||
for child_path in &children {
|
||||
let tmp_puzzle = apply_choices(puzzle.clone(), child_path, &mut my_cache);
|
||||
// !known_outcomes.contains(&tmp_puzzle)
|
||||
if !my_cache.contains_value(&tmp_puzzle) {
|
||||
base_paths.push(child_path.clone());
|
||||
} else {
|
||||
//cache.remove(path); would be removed next layer anyways
|
||||
}
|
||||
if puzzle_gelöst(&tmp_puzzle) {
|
||||
return Some(child_path.clone());
|
||||
} else {
|
||||
known_outcomes.insert(tmp_puzzle);
|
||||
}
|
||||
}
|
||||
println!("{:03} depth, {:06} children, {:07} known, {:07} cached, {:04} sec", depth, children.len(), known_outcomes.len(), my_cache.len_keys(), start.elapsed().as_secs());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
if let Some(dateiname) = env::args().nth(1) {
|
||||
//let pause = std::time::Duration::from_millis((16.6*4.0) as u64);
|
||||
|
||||
let puzzle = lade_puzzle(&dateiname);
|
||||
if let Err(e) = puzzle {
|
||||
println!("Error: {:?}", e);
|
||||
return;
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
println!("{}", puzzle);
|
||||
let solution = breadth_first_search(puzzle.clone());
|
||||
if let Some(solution) = solution {
|
||||
println!("lösung: {:?}", solution);
|
||||
println!("{}", apply_choices(puzzle, &solution, &mut MyMap::new()));
|
||||
} else {
|
||||
println!("keine lösung gefunden!");
|
||||
}
|
||||
/*
|
||||
let steps = [RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CW];
|
||||
for s in steps.iter() {
|
||||
println!("action: {:?}", s);
|
||||
puzzle = do_the_gravity(rotate(puzzle, s));
|
||||
println!("{}", puzzle);
|
||||
std::thread::sleep(pause);
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
println!("Bitte so aufrufen: ./aufgabe3 <dateiname>");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(useless_vec)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn children() {
|
||||
let children = build_children(&vec![vec![RotationDir::CW]]);
|
||||
let expected = vec![vec![RotationDir::CW, RotationDir::CW], vec![RotationDir::CW, RotationDir::CCW]];
|
||||
if children == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn t1() {
|
||||
let puzzle = lade_puzzle("rotation1_03.txt");
|
||||
if puzzle.is_err() {
|
||||
panic!();
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
//println!("Puzzle: {}", puzzle);
|
||||
assert!(breadth_first_search(puzzle).unwrap().len() == 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn t2() {
|
||||
let puzzle = lade_puzzle("rotation2_03.txt");
|
||||
if puzzle.is_err() {
|
||||
panic!();
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
//println!("Puzzle: {}", puzzle);
|
||||
assert!(breadth_first_search(puzzle).unwrap().len() == 22);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn t3() {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt");
|
||||
if puzzle.is_err() {
|
||||
panic!();
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
//println!("Puzzle: {}", puzzle);
|
||||
assert!(breadth_first_search(puzzle).unwrap().len() == 90);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod benchs {
|
||||
extern crate test;
|
||||
|
||||
use super::*;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn test1(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let puzzle = lade_puzzle("rotation1_03.txt");
|
||||
if puzzle.is_err() {
|
||||
panic!();
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
//println!("Puzzle: {}", puzzle);
|
||||
breadth_first_search(puzzle);
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn test2(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let puzzle = lade_puzzle("rotation2_03.txt");
|
||||
if puzzle.is_err() {
|
||||
panic!();
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
//println!("Puzzle: {}", puzzle);
|
||||
breadth_first_search(puzzle);
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_cw(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
rotate(puzzle.clone(), &test::black_box(RotationDir::CW))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_ccw(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
rotate(puzzle.clone(), &test::black_box(RotationDir::CCW))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_cw_grav(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
do_the_gravity(rotate(puzzle.clone(), &test::black_box(RotationDir::CW)))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_ccw_grav(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
do_the_gravity(rotate(puzzle.clone(), &test::black_box(RotationDir::CCW)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
fn apply_choices(mut puzzle: Puzzle, choices: &Vec<RotationDir>) -> Puzzle {
|
||||
for rotation in choices {
|
||||
puzzle = do_the_gravity(rotate(puzzle, rotation));
|
||||
}
|
||||
puzzle
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
fn cmp_vectors(small: &Vec<RotationDir>, large: &Vec<RotationDir>) -> bool {
|
||||
if small.len() > large.len() {
|
||||
false
|
||||
} else {
|
||||
let mut same = true;
|
||||
for (index, value) in small.iter().enumerate() {
|
||||
if value != &large[index] {
|
||||
same = false;
|
||||
}
|
||||
}
|
||||
same
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
fn breadth_first_search(puzzle: Puzzle) -> Option<Vec<RotationDir>> {
|
||||
let mut choices = vec![RotationDir::CW];
|
||||
let mut known_outcomes = HashSet::new();
|
||||
let mut banned_paths = HashSet::new();
|
||||
let mut cont_counter = 0;
|
||||
let mut ban_counter = 0;
|
||||
loop {
|
||||
for path in &banned_paths {
|
||||
if cmp_vectors(&path, &choices) {
|
||||
//println!("banned, skip");
|
||||
ban_counter += 1;
|
||||
choices = next_combination(choices);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if cont_counter % 100 == 0 && cont_counter > 0 {
|
||||
println!("{:06} conts, {:06} ban-conts.", cont_counter, ban_counter);
|
||||
}
|
||||
//println!("trying {:?}", choices);
|
||||
let new = apply_choices(puzzle.clone(), &choices);
|
||||
if known_outcomes.contains(&new) {
|
||||
cont_counter += 1;
|
||||
banned_paths.insert(choices.clone());
|
||||
choices = next_combination(choices);
|
||||
continue;
|
||||
}
|
||||
if puzzle_gelöst(&new) {
|
||||
return Some(choices);
|
||||
}
|
||||
known_outcomes.insert(new);
|
||||
choices = next_combination(choices);
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*
|
||||
fn breadth_first_search(puzzle: Puzzle, mut known_outcomes: &mut HashSet<Puzzle>, mut choices: Vec<RotationDir>) -> Option<Vec<RotationDir>> {
|
||||
//println!("base: {:?}, known: {:?}", choices, known_outcomes.len());
|
||||
choices.push(RotationDir::CW);
|
||||
let puzzle_cw = apply_choices(puzzle.clone(), &choices);
|
||||
if known_outcomes.contains(&puzzle_cw) {
|
||||
|
||||
} else {
|
||||
known_outcomes.insert(puzzle_cw.clone());
|
||||
if puzzle_gelöst(&puzzle_cw) {
|
||||
return Some(choices);
|
||||
} else {
|
||||
if let Some(solution) = breadth_first_search(puzzle.clone(), &mut known_outcomes, choices.clone()) {
|
||||
return Some(solution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
choices.pop().unwrap();
|
||||
choices.push(RotationDir::CCW);
|
||||
let puzzle_cw = apply_choices(puzzle.clone(), &choices);
|
||||
if known_outcomes.contains(&puzzle_cw) {
|
||||
return None;
|
||||
} else {
|
||||
known_outcomes.insert(puzzle_cw.clone());
|
||||
if puzzle_gelöst(&puzzle_cw) {
|
||||
return Some(choices);
|
||||
} else {
|
||||
if let Some(solution) = breadth_first_search(puzzle.clone(), &mut known_outcomes, choices.clone()) {
|
||||
return Some(solution);
|
||||
} else {
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub fn next_combination(choices: Vec<RotationDir>) -> Vec<RotationDir> {
|
||||
let mut choices = choices;
|
||||
if choices.iter().filter(|&x| x == &RotationDir::CCW).count() == choices.len() {
|
||||
choices = choices.iter().map(|_| RotationDir::CW).collect();
|
||||
choices.push(RotationDir::CW);
|
||||
choices
|
||||
} else {
|
||||
let mut rchoices = choices.clone();
|
||||
rchoices.reverse();
|
||||
let mut cw_index = 0;
|
||||
for (index, c) in rchoices.iter().enumerate() {
|
||||
if c == &RotationDir::CW {
|
||||
cw_index = rchoices.len()-index-1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
choices[cw_index] = RotationDir::CCW;
|
||||
for mut item in choices.iter_mut().skip(cw_index+1) {
|
||||
*item = RotationDir::CW;
|
||||
}
|
||||
choices
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn a() {
|
||||
let next = next_combination(vec![RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn b() {
|
||||
let next = next_combination(vec![RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aa() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ab() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CCW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ba() {
|
||||
let next = next_combination(vec![RotationDir::CCW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CCW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bb() {
|
||||
let next = next_combination(vec![RotationDir::CCW, RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aba() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn abb() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CCW, RotationDir::CW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn abba() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}*/
|
721
Aufgabe3/src/main_vec_y.rs
Normal file
@ -0,0 +1,721 @@
|
||||
#![feature(non_ascii_idents)]
|
||||
#![feature(custom_derive)]
|
||||
#![feature(test)]
|
||||
#![feature(plugin)]
|
||||
|
||||
#![plugin(clippy)]
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
extern crate colored;
|
||||
#[cfg(target_os = "linux")]
|
||||
use colored::*;
|
||||
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::time::Instant;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct Punkt {
|
||||
x: usize,
|
||||
y: usize
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum Stabrichtung {
|
||||
Horizontal,
|
||||
Vertikal
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Puzzle {
|
||||
raster: Vec<Vec<i8>>
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
impl std::fmt::Display for Puzzle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
for row in &self.raster {
|
||||
write!(f, "\n")?;
|
||||
for cell in row {
|
||||
match *cell {
|
||||
WALL => write!(f, "{}", format!("{:02} ", cell).black().bold())?,
|
||||
NIX => write!(f, "{}", format!("{:02} ", cell).black().bold())?,
|
||||
AUSGANG => write!(f, "{}", format!("{:02} ", cell).green().bold())?,
|
||||
_ if cell % 5 == 0 => write!(f, "{}", format!("{:02} ", cell).red().bold())?,
|
||||
_ if cell % 5 == 1 => write!(f, "{}", format!("{:02} ", cell).yellow().bold())?,
|
||||
_ if cell % 5 == 2 => write!(f, "{}", format!("{:02} ", cell).blue().bold())?,
|
||||
_ if cell % 5 == 3 => write!(f, "{}", format!("{:02} ", cell).purple().bold())?,
|
||||
_ if cell % 5 == 4 => write!(f, "{}", format!("{:02} ", cell).cyan().bold())?,
|
||||
_ => write!(f, "{:02} ", cell)?
|
||||
};
|
||||
}
|
||||
}
|
||||
write!(f, "\n")
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
impl std::fmt::Display for Puzzle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
for row in &self.raster {
|
||||
write!(f, "\n")?;
|
||||
for cell in row {
|
||||
write!(f, "{:02} ", cell)?;
|
||||
}
|
||||
}
|
||||
write!(f, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum RotationDir {
|
||||
// clockwise
|
||||
CW,
|
||||
// counterclockwise
|
||||
CCW
|
||||
}
|
||||
|
||||
const WALL: i8 = -3;
|
||||
const NIX: i8 = -2;
|
||||
const AUSGANG: i8 = -1;
|
||||
|
||||
|
||||
pub fn lade_puzzle(dateiname: &str) -> Result<Puzzle, String> {
|
||||
let path = Path::new(dateiname);
|
||||
let display = path.display();
|
||||
|
||||
// Open the path in read-only mode, returns `io::Result<File>`
|
||||
let mut file = match File::open(&path) {
|
||||
// The `description` method of `io::Error` returns a string that
|
||||
// describes the error
|
||||
Err(why) => { return Err(format!("couldn't open {}: {}", display,
|
||||
why.description())); },
|
||||
Ok(file) => file,
|
||||
};
|
||||
|
||||
// Read the file contents into a string, returns `io::Result<usize>`
|
||||
let mut s = String::new();
|
||||
if let Err(why) = file.read_to_string(&mut s) {
|
||||
return Err(format!("couldn't read {}: {}", display,
|
||||
why.description()));
|
||||
}
|
||||
let mut p = Puzzle { raster: Vec::new() };
|
||||
let lines: Vec<&str> = s.split('\n').collect();
|
||||
let seitenlänge = lines[0].parse().expect("erste Zeile keine Zahl");
|
||||
|
||||
for (index, line) in lines.iter().skip(1).enumerate() {
|
||||
// zeile nach der letzten zeile erreicht
|
||||
if index == seitenlänge {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut line_vec = Vec::new();
|
||||
for (x, c) in line.chars().enumerate() {
|
||||
// ausgang
|
||||
if c == ' ' && ((x == 0 || x == line.len()-1) || (index == 0 || index == seitenlänge-1)) {
|
||||
line_vec.push(AUSGANG);
|
||||
continue;
|
||||
}
|
||||
match c.to_digit(36) {
|
||||
Some(digit) => line_vec.push(digit as i8),
|
||||
// -1 == " "
|
||||
None => {
|
||||
match c {
|
||||
'#' => line_vec.push(WALL),
|
||||
' ' => line_vec.push(NIX),
|
||||
_ => panic!("unbekannter buchstabe {:?}", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p.raster.push(line_vec);
|
||||
//println!("{:?}: {:?}", index, line);
|
||||
}
|
||||
Ok(p)
|
||||
}
|
||||
|
||||
pub fn rotate(puzzle: Puzzle, dir: &RotationDir) -> Puzzle {
|
||||
// seitenlänge
|
||||
let s = puzzle.raster.len();
|
||||
let mut roted_raster = puzzle.raster.clone();
|
||||
macro_rules! set {
|
||||
($x:expr, $y:expr, $new:expr) => (
|
||||
roted_raster[$y][$x] = $new;
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
for (y, row) in puzzle.raster.iter().enumerate() {
|
||||
for (x, cell) in row.iter().enumerate() {
|
||||
match *dir {
|
||||
RotationDir::CW => set!(s-y-1, x, *cell),
|
||||
RotationDir::CCW => set!(y, s-x-1, *cell)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Puzzle { raster: roted_raster }
|
||||
}
|
||||
|
||||
pub fn get_stäbchen(raster: &[Vec<i8>]) -> HashMap<u32, Vec<Punkt>> {
|
||||
let mut stäbchen: HashMap<u32, Vec<Punkt>> = HashMap::new();
|
||||
|
||||
for (y, row) in raster.iter().enumerate() {
|
||||
for (x, cell) in row.iter().enumerate() {
|
||||
if !cell.is_negative() {
|
||||
let cell = *cell as u32;
|
||||
let value = stäbchen.entry(cell).or_insert_with(Vec::new);
|
||||
value.push(Punkt { x: x, y: y });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stäbchen
|
||||
}
|
||||
|
||||
fn get_stäbchen_richtung(stab: &[Punkt]) -> Stabrichtung {
|
||||
if stab.len() > 1 && stab[0].x == stab[1].x {
|
||||
Stabrichtung::Vertikal
|
||||
} else {
|
||||
Stabrichtung::Horizontal
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_the_gravity(mut puzzle: Puzzle) -> Puzzle {
|
||||
// seitenlänge
|
||||
let s = puzzle.raster.len();
|
||||
macro_rules! get {
|
||||
($x:expr, $y:expr) => (
|
||||
puzzle.raster[$y][$x];
|
||||
)
|
||||
}
|
||||
macro_rules! set {
|
||||
($x:expr, $y:expr, $new:expr) => (
|
||||
puzzle.raster[$y][$x] = $new;
|
||||
)
|
||||
}
|
||||
|
||||
let mut stäbchen1 = get_stäbchen(&puzzle.raster);
|
||||
let mut stäbchen2 = HashMap::new();
|
||||
while stäbchen1 != stäbchen2 {
|
||||
//println!("sloop");
|
||||
stäbchen1 = get_stäbchen(&puzzle.raster);
|
||||
//println!("{:?}", stäbchen1);
|
||||
for (index, stab) in &stäbchen1 {
|
||||
let richtung = get_stäbchen_richtung(stab);
|
||||
let stab = stab.iter();
|
||||
let mut move_possible = stab.clone()
|
||||
.fold(true, |acc, &x|
|
||||
// falls der Stab schon direkt über dem Rahmen ist,
|
||||
// sollte er nicht bewegt werden
|
||||
if x.y >= s-2
|
||||
// falls der Stab (horizontal) liegt und unter ihm kein Platz ist,
|
||||
// sollte er nicht bewegt werden
|
||||
|| (richtung == Stabrichtung::Horizontal && ((get!(x.x, x.y+1) != NIX && get!(x.x, x.y+1) != AUSGANG))) { false } else { acc });
|
||||
|
||||
// falls der Stab (vertikal) steht,
|
||||
if richtung == Stabrichtung::Vertikal {
|
||||
// und der unterste Teil des Stabes
|
||||
let lowest_punkt = stab.clone().max_by_key(|x| x.y).unwrap();
|
||||
// über/auf dem unteren Begrenzung liegt
|
||||
if lowest_punkt.y+1 <= s-1 {
|
||||
// und unter ihm bereits ein Stab liegt,
|
||||
if get!(lowest_punkt.x, lowest_punkt.y+1) >= 0 {
|
||||
// sollte er nicht bewegt werden
|
||||
move_possible = false;
|
||||
// sonderfall: unter dem Teil des Stabes liegt der Ausgang
|
||||
} else if get!(lowest_punkt.x, lowest_punkt.y+1) == AUSGANG {
|
||||
//println!("Puzzle ist möglich!");
|
||||
move_possible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if move_possible {
|
||||
let index = *index as i8;
|
||||
let stab: Vec<&Punkt> = stab.collect();
|
||||
for p in &stab {
|
||||
set!(p.x, p.y, NIX);
|
||||
}
|
||||
for p in &stab {
|
||||
set!(p.x, p.y+1, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
stäbchen2 = stäbchen1;
|
||||
stäbchen1 = get_stäbchen(&puzzle.raster);
|
||||
//println!("eloop");
|
||||
}
|
||||
//println!("EOG");
|
||||
puzzle
|
||||
}
|
||||
|
||||
fn puzzle_gelöst(puzzle: &Puzzle) -> bool {
|
||||
puzzle.raster
|
||||
.last().unwrap().iter()
|
||||
.fold(
|
||||
false, |acc, &x|
|
||||
// falls dieses Element zu einem Stab gehört,
|
||||
// wird acc auf true gesetzt
|
||||
if x >= 0 { true }
|
||||
else { acc })
|
||||
}
|
||||
|
||||
fn apply_choices(puzzle: Puzzle, choices: &[RotationDir], cache: &mut HashMap<Vec<RotationDir>, Puzzle>) -> Puzzle {
|
||||
if cache.contains_key(choices) {
|
||||
//println!("{:?} cached", choices);
|
||||
cache.get(choices).unwrap().clone()
|
||||
} else if choices.len() == 1 {
|
||||
//println!("len one");
|
||||
do_the_gravity(rotate(puzzle, &choices[0]))
|
||||
} else {
|
||||
let mut choices = Vec::from(choices);
|
||||
let last_action = choices.pop().unwrap();
|
||||
let before_last = apply_choices(puzzle, &choices, cache);
|
||||
//println!("before: {}", before_last);
|
||||
let applied = do_the_gravity(rotate(before_last, &last_action));
|
||||
//println!("applied: {}", applied);
|
||||
choices.push(last_action);
|
||||
cache.insert(choices, applied.clone());
|
||||
applied
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_children(base: &[Vec<RotationDir>]) -> Vec<Vec<RotationDir>> {
|
||||
let mut children = Vec::new();
|
||||
for path in base {
|
||||
let mut c1 = path.clone();
|
||||
let mut c2 = path.clone();
|
||||
c1.push(RotationDir::CW);
|
||||
c2.push(RotationDir::CCW);
|
||||
children.push(c1);
|
||||
children.push(c2);
|
||||
}
|
||||
children
|
||||
}
|
||||
|
||||
pub fn breadth_first_search(puzzle: Puzzle) -> Option<Vec<RotationDir>> {
|
||||
let mut cache = HashMap::new();
|
||||
let mut known_outcomes = HashSet::new();
|
||||
let mut base_paths = vec![vec![]];
|
||||
let mut depth = 0;
|
||||
let start = Instant::now();
|
||||
let mut counter = 0;
|
||||
loop {
|
||||
{
|
||||
let paths: Vec<Vec<RotationDir>> = cache.keys().cloned().collect();
|
||||
for path in paths {
|
||||
if path.len() < depth {
|
||||
cache.remove(&path);
|
||||
}
|
||||
}
|
||||
}
|
||||
depth += 1;
|
||||
|
||||
let children = build_children(&base_paths);
|
||||
if children.is_empty() {
|
||||
return None;
|
||||
}
|
||||
base_paths = Vec::new();
|
||||
for child_path in &children {
|
||||
counter += 1;
|
||||
let tmp_puzzle = apply_choices(puzzle.clone(), child_path, &mut cache);
|
||||
if !known_outcomes.contains(&tmp_puzzle) {
|
||||
if counter % 1001 == 0 {
|
||||
println!("{}", tmp_puzzle);
|
||||
}
|
||||
base_paths.push(child_path.clone());
|
||||
} else {
|
||||
}
|
||||
if puzzle_gelöst(&tmp_puzzle) {
|
||||
return Some(child_path.clone());
|
||||
} else {
|
||||
known_outcomes.insert(tmp_puzzle);
|
||||
}
|
||||
}
|
||||
println!("{:03} depth, {:06} children, {:07} known, {:07} cached, {:04} sec", depth, children.len(), known_outcomes.len(), cache.len(), start.elapsed().as_secs());
|
||||
counter = 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn main() {
|
||||
if let Some(dateiname) = env::args().nth(1) {
|
||||
//let pause = std::time::Duration::from_millis((16.6*4.0) as u64);
|
||||
|
||||
let puzzle = lade_puzzle(&dateiname);
|
||||
if let Err(e) = puzzle {
|
||||
println!("Error: {:?}", e);
|
||||
return;
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
println!("{}", puzzle);
|
||||
let solution = breadth_first_search(puzzle.clone());
|
||||
if let Some(solution) = solution {
|
||||
println!("lösung: {:?}", solution);
|
||||
println!("{}", apply_choices(puzzle, &solution, &mut HashMap::new()));
|
||||
} else {
|
||||
println!("keine lösung gefunden!");
|
||||
}
|
||||
/*
|
||||
let steps = [RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CW];
|
||||
for s in steps.iter() {
|
||||
println!("action: {:?}", s);
|
||||
puzzle = do_the_gravity(rotate(puzzle, s));
|
||||
println!("{}", puzzle);
|
||||
std::thread::sleep(pause);
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
println!("Bitte so aufrufen: ./aufgabe3 <dateiname>");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(useless_vec)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn children() {
|
||||
let children = build_children(&vec![vec![RotationDir::CW]]);
|
||||
let expected = vec![vec![RotationDir::CW, RotationDir::CW], vec![RotationDir::CW, RotationDir::CCW]];
|
||||
if children == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn t1() {
|
||||
let puzzle = lade_puzzle("rotation1_03.txt");
|
||||
if puzzle.is_err() {
|
||||
panic!();
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
//println!("Puzzle: {}", puzzle);
|
||||
assert!(breadth_first_search(puzzle).unwrap().len() == 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn t2() {
|
||||
let puzzle = lade_puzzle("rotation2_03.txt");
|
||||
if puzzle.is_err() {
|
||||
panic!();
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
//println!("Puzzle: {}", puzzle);
|
||||
assert!(breadth_first_search(puzzle).unwrap().len() == 22);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn t3() {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt");
|
||||
if puzzle.is_err() {
|
||||
panic!();
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
//println!("Puzzle: {}", puzzle);
|
||||
assert!(breadth_first_search(puzzle).unwrap().len() == 90);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod benchs {
|
||||
extern crate test;
|
||||
|
||||
use super::*;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn test1(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let puzzle = lade_puzzle("rotation1_03.txt");
|
||||
if puzzle.is_err() {
|
||||
panic!();
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
//println!("Puzzle: {}", puzzle);
|
||||
breadth_first_search(puzzle);
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn test2(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let puzzle = lade_puzzle("rotation2_03.txt");
|
||||
if puzzle.is_err() {
|
||||
panic!();
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
//println!("Puzzle: {}", puzzle);
|
||||
breadth_first_search(puzzle);
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_cw(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
rotate(puzzle.clone(), &test::black_box(RotationDir::CW))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_ccw(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
rotate(puzzle.clone(), &test::black_box(RotationDir::CCW))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_cw_grav(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
do_the_gravity(rotate(puzzle.clone(), &test::black_box(RotationDir::CW)))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_ccw_grav(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
do_the_gravity(rotate(puzzle.clone(), &test::black_box(RotationDir::CCW)))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn get_stäbe(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
get_stäbchen(&puzzle.raster)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
fn apply_choices(mut puzzle: Puzzle, choices: &Vec<RotationDir>) -> Puzzle {
|
||||
for rotation in choices {
|
||||
puzzle = do_the_gravity(rotate(puzzle, rotation));
|
||||
}
|
||||
puzzle
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
fn cmp_vectors(small: &Vec<RotationDir>, large: &Vec<RotationDir>) -> bool {
|
||||
if small.len() > large.len() {
|
||||
false
|
||||
} else {
|
||||
let mut same = true;
|
||||
for (index, value) in small.iter().enumerate() {
|
||||
if value != &large[index] {
|
||||
same = false;
|
||||
}
|
||||
}
|
||||
same
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
fn breadth_first_search(puzzle: Puzzle) -> Option<Vec<RotationDir>> {
|
||||
let mut choices = vec![RotationDir::CW];
|
||||
let mut known_outcomes = HashSet::new();
|
||||
let mut banned_paths = HashSet::new();
|
||||
let mut cont_counter = 0;
|
||||
let mut ban_counter = 0;
|
||||
loop {
|
||||
for path in &banned_paths {
|
||||
if cmp_vectors(&path, &choices) {
|
||||
//println!("banned, skip");
|
||||
ban_counter += 1;
|
||||
choices = next_combination(choices);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if cont_counter % 100 == 0 && cont_counter > 0 {
|
||||
println!("{:06} conts, {:06} ban-conts.", cont_counter, ban_counter);
|
||||
}
|
||||
//println!("trying {:?}", choices);
|
||||
let new = apply_choices(puzzle.clone(), &choices);
|
||||
if known_outcomes.contains(&new) {
|
||||
cont_counter += 1;
|
||||
banned_paths.insert(choices.clone());
|
||||
choices = next_combination(choices);
|
||||
continue;
|
||||
}
|
||||
if puzzle_gelöst(&new) {
|
||||
return Some(choices);
|
||||
}
|
||||
known_outcomes.insert(new);
|
||||
choices = next_combination(choices);
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*
|
||||
fn breadth_first_search(puzzle: Puzzle, mut known_outcomes: &mut HashSet<Puzzle>, mut choices: Vec<RotationDir>) -> Option<Vec<RotationDir>> {
|
||||
//println!("base: {:?}, known: {:?}", choices, known_outcomes.len());
|
||||
choices.push(RotationDir::CW);
|
||||
let puzzle_cw = apply_choices(puzzle.clone(), &choices);
|
||||
if known_outcomes.contains(&puzzle_cw) {
|
||||
|
||||
} else {
|
||||
known_outcomes.insert(puzzle_cw.clone());
|
||||
if puzzle_gelöst(&puzzle_cw) {
|
||||
return Some(choices);
|
||||
} else {
|
||||
if let Some(solution) = breadth_first_search(puzzle.clone(), &mut known_outcomes, choices.clone()) {
|
||||
return Some(solution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
choices.pop().unwrap();
|
||||
choices.push(RotationDir::CCW);
|
||||
let puzzle_cw = apply_choices(puzzle.clone(), &choices);
|
||||
if known_outcomes.contains(&puzzle_cw) {
|
||||
return None;
|
||||
} else {
|
||||
known_outcomes.insert(puzzle_cw.clone());
|
||||
if puzzle_gelöst(&puzzle_cw) {
|
||||
return Some(choices);
|
||||
} else {
|
||||
if let Some(solution) = breadth_first_search(puzzle.clone(), &mut known_outcomes, choices.clone()) {
|
||||
return Some(solution);
|
||||
} else {
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub fn next_combination(choices: Vec<RotationDir>) -> Vec<RotationDir> {
|
||||
let mut choices = choices;
|
||||
if choices.iter().filter(|&x| x == &RotationDir::CCW).count() == choices.len() {
|
||||
choices = choices.iter().map(|_| RotationDir::CW).collect();
|
||||
choices.push(RotationDir::CW);
|
||||
choices
|
||||
} else {
|
||||
let mut rchoices = choices.clone();
|
||||
rchoices.reverse();
|
||||
let mut cw_index = 0;
|
||||
for (index, c) in rchoices.iter().enumerate() {
|
||||
if c == &RotationDir::CW {
|
||||
cw_index = rchoices.len()-index-1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
choices[cw_index] = RotationDir::CCW;
|
||||
for mut item in choices.iter_mut().skip(cw_index+1) {
|
||||
*item = RotationDir::CW;
|
||||
}
|
||||
choices
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn a() {
|
||||
let next = next_combination(vec![RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn b() {
|
||||
let next = next_combination(vec![RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aa() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ab() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CCW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ba() {
|
||||
let next = next_combination(vec![RotationDir::CCW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CCW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bb() {
|
||||
let next = next_combination(vec![RotationDir::CCW, RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aba() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn abb() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CCW, RotationDir::CW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn abba() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}*/
|
687
Aufgabe3/src/main_yes_bi.rs
Normal file
@ -0,0 +1,687 @@
|
||||
#![feature(non_ascii_idents)]
|
||||
#![feature(custom_derive)]
|
||||
#![feature(test)]
|
||||
#![feature(plugin)]
|
||||
|
||||
#![plugin(clippy)]
|
||||
|
||||
extern crate colored;
|
||||
use colored::*;
|
||||
|
||||
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::time::Instant;
|
||||
use std::path::Path;
|
||||
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum Stabrichtung {
|
||||
Horizontal,
|
||||
Vertikal
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq)]
|
||||
pub struct Punkt {
|
||||
x: usize,
|
||||
y: usize
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Puzzle {
|
||||
raster: Vec<Vec<i64>>
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Puzzle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
for row in &self.raster {
|
||||
write!(f, "\n")?;
|
||||
for cell in row {
|
||||
match *cell {
|
||||
WALL => write!(f, "{}", format!("{:02} ", cell).black())?,
|
||||
NIX => write!(f, "{}", format!("{:02} ", cell).black())?,
|
||||
AUSGANG => write!(f, "{}", format!("{:02} ", cell).green())?,
|
||||
_ if cell % 5 == 0 => write!(f, "{}", format!("{:02} ", cell).red())?,
|
||||
_ if cell % 5 == 1 => write!(f, "{}", format!("{:02} ", cell).yellow())?,
|
||||
_ if cell % 5 == 2 => write!(f, "{}", format!("{:02} ", cell).blue())?,
|
||||
_ if cell % 5 == 3 => write!(f, "{}", format!("{:02} ", cell).purple())?,
|
||||
_ if cell % 5 == 4 => write!(f, "{}", format!("{:02} ", cell).cyan())?,
|
||||
_ => write!(f, "{:02} ", cell)?
|
||||
};
|
||||
}
|
||||
}
|
||||
write!(f, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum RotationDir {
|
||||
// clockwise
|
||||
CW,
|
||||
// counterclockwise
|
||||
CCW
|
||||
}
|
||||
|
||||
const WALL: i64 = -3;
|
||||
const NIX: i64 = -2;
|
||||
const AUSGANG: i64 = -1;
|
||||
|
||||
|
||||
pub fn lade_puzzle(dateiname: &str) -> Result<Puzzle, String> {
|
||||
let path = Path::new(dateiname);
|
||||
let display = path.display();
|
||||
|
||||
// Open the path in read-only mode, returns `io::Result<File>`
|
||||
let mut file = match File::open(&path) {
|
||||
// The `description` method of `io::Error` returns a string that
|
||||
// describes the error
|
||||
Err(why) => { return Err(format!("couldn't open {}: {}", display,
|
||||
why.description())); },
|
||||
Ok(file) => file,
|
||||
};
|
||||
|
||||
// Read the file contents into a string, returns `io::Result<usize>`
|
||||
let mut s = String::new();
|
||||
if let Err(why) = file.read_to_string(&mut s) {
|
||||
return Err(format!("couldn't read {}: {}", display,
|
||||
why.description()));
|
||||
}
|
||||
let mut p = Puzzle { raster: Vec::new() };
|
||||
let lines: Vec<&str> = s.split('\n').collect();
|
||||
let seitenlänge = lines[0].parse().expect("erste Zeile keine Zahl");
|
||||
|
||||
for (index, line) in lines.iter().skip(1).enumerate() {
|
||||
// zeile nach der letzten zeile erreicht
|
||||
if index == seitenlänge {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut line_vec = Vec::new();
|
||||
for (x, c) in line.chars().enumerate() {
|
||||
// ausgang
|
||||
if c == ' ' && ((x == 0 || x == line.len()-1) || (index == 0 || index == seitenlänge-1)) {
|
||||
line_vec.push(AUSGANG);
|
||||
continue;
|
||||
}
|
||||
match c.to_digit(10) {
|
||||
Some(digit) => line_vec.push(digit as i64),
|
||||
// -1 == " "
|
||||
None => {
|
||||
match c {
|
||||
'#' => line_vec.push(WALL),
|
||||
' ' => line_vec.push(NIX),
|
||||
_ => panic!("unbekannter buchstabe {:?}", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p.raster.push(line_vec);
|
||||
//println!("{:?}: {:?}", index, line);
|
||||
}
|
||||
Ok(p)
|
||||
}
|
||||
|
||||
pub fn rotate(puzzle: Puzzle, dir: &RotationDir) -> Puzzle {
|
||||
// seitenlänge
|
||||
let s = puzzle.raster.len();
|
||||
let mut raster = Vec::new();
|
||||
for r in &puzzle.raster {
|
||||
let mut row_vec = Vec::new();
|
||||
for c in r {
|
||||
row_vec.push(*c);
|
||||
}
|
||||
raster.push(row_vec);
|
||||
}
|
||||
let mut roted_raster = raster.clone();
|
||||
macro_rules! set {
|
||||
($x:expr, $y:expr, $new:expr) => (
|
||||
roted_raster[$y][$x] = $new;
|
||||
)
|
||||
}
|
||||
|
||||
let mut p = puzzle;
|
||||
|
||||
let mut y = 0;
|
||||
for row in raster {
|
||||
for (x, cell) in row.iter().enumerate() {
|
||||
match *dir {
|
||||
RotationDir::CW => set!(s-y-1, x, *cell),
|
||||
RotationDir::CCW => set!(y, s-x-1, *cell)
|
||||
}
|
||||
}
|
||||
y += 1;
|
||||
}
|
||||
p.raster = roted_raster;
|
||||
p
|
||||
}
|
||||
|
||||
fn get_stäbchen(raster: &[Vec<i64>]) -> HashMap<u64, Vec<Punkt>> {
|
||||
let mut stäbchen: HashMap<u64, Vec<Punkt>> = HashMap::new();
|
||||
|
||||
for (y, row) in raster.iter().enumerate() {
|
||||
for (x, cell) in row.iter().enumerate() {
|
||||
if !cell.is_negative() {
|
||||
let cell = *cell as u64;
|
||||
let value = stäbchen.entry(cell).or_insert_with(Vec::new);
|
||||
value.push(Punkt { x: x, y: y });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stäbchen
|
||||
}
|
||||
|
||||
fn get_stäbchen_richtung(stab: &[Punkt]) -> Stabrichtung {
|
||||
if stab.len() == 1 {
|
||||
// egal
|
||||
return Stabrichtung::Vertikal;
|
||||
}
|
||||
if stab[0].x == stab[1].x {
|
||||
Stabrichtung::Vertikal
|
||||
} else {
|
||||
Stabrichtung::Horizontal
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_the_gravity(mut puzzle: Puzzle) -> Puzzle {
|
||||
// seitenlänge
|
||||
let s = puzzle.raster.len();
|
||||
macro_rules! get {
|
||||
($x:expr, $y:expr) => (
|
||||
puzzle.raster[$y][$x];
|
||||
)
|
||||
}
|
||||
macro_rules! set {
|
||||
($x:expr, $y:expr, $new:expr) => (
|
||||
puzzle.raster[$y][$x] = $new;
|
||||
)
|
||||
}
|
||||
|
||||
let mut stäbchen1 = get_stäbchen(&puzzle.raster);
|
||||
let mut stäbchen2 = HashMap::new();
|
||||
while stäbchen1 != stäbchen2 {
|
||||
//println!("sloop");
|
||||
stäbchen1 = get_stäbchen(&puzzle.raster);
|
||||
//println!("{:?}", stäbchen1);
|
||||
for (index, stab) in &stäbchen1 {
|
||||
let mut move_possible = true;
|
||||
let richtung = get_stäbchen_richtung(stab);
|
||||
let mut highest_y = 0;
|
||||
let mut lowest_punkt = &stab[0];
|
||||
for p in stab {
|
||||
if p.y == s-2 {
|
||||
move_possible = false;
|
||||
}
|
||||
if p.y+1 <= s-1 && ((get!(p.x, p.y+1) != NIX && get!(p.x, p.y+1) != AUSGANG) && richtung == Stabrichtung::Horizontal) {
|
||||
move_possible = false;
|
||||
}
|
||||
if richtung == Stabrichtung::Vertikal && p.y > highest_y {
|
||||
highest_y = p.y;
|
||||
lowest_punkt = p;
|
||||
}
|
||||
}
|
||||
if richtung == Stabrichtung::Vertikal && lowest_punkt.y+1 <= s-1 {
|
||||
if get!(lowest_punkt.x, lowest_punkt.y+1) >= 0 {
|
||||
//println!("{:?} blocked-low because of {:?}, {:?}: {:?}", index,
|
||||
// lowest_punkt.x,
|
||||
// lowest_punkt.y+1,
|
||||
// get!(lowest_punkt.x, lowest_punkt.y+1));
|
||||
move_possible = false;
|
||||
} else if highest_y == s-2 && get!(lowest_punkt.x, lowest_punkt.y+1) == AUSGANG {
|
||||
//println!("Puzzle ist möglich!");
|
||||
move_possible = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if move_possible { // checked
|
||||
let new_stab = stab;
|
||||
for p in stab {
|
||||
set!(p.x, p.y, NIX);
|
||||
}
|
||||
let index = *index as i64;
|
||||
for p in new_stab {
|
||||
set!(p.x, p.y+1, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
stäbchen2 = stäbchen1;
|
||||
stäbchen1 = get_stäbchen(&puzzle.raster);
|
||||
//println!("eloop");
|
||||
}
|
||||
//println!("EOG");
|
||||
puzzle
|
||||
}
|
||||
|
||||
fn puzzle_gelöst(puzzle: &Puzzle) -> bool {
|
||||
let mut ausgang_gefunden = false;
|
||||
for row in &puzzle.raster {
|
||||
for cell in row {
|
||||
if *cell == AUSGANG {
|
||||
ausgang_gefunden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
!ausgang_gefunden
|
||||
}
|
||||
|
||||
fn apply_choices(puzzle: Puzzle, choices: &[RotationDir], cache: &mut HashMap<Vec<RotationDir>, Puzzle>) -> Puzzle {
|
||||
if cache.contains_key(choices) {
|
||||
//println!("{:?} cached", choices);
|
||||
cache.get(choices).unwrap().clone()
|
||||
} else if choices.len() == 1 {
|
||||
//println!("len one");
|
||||
do_the_gravity(rotate(puzzle, &choices[0]))
|
||||
} else {
|
||||
let mut choices = Vec::from(choices);
|
||||
let last_action = choices.pop().unwrap();
|
||||
let before_last = apply_choices(puzzle, &choices, cache);
|
||||
//println!("before: {}", before_last);
|
||||
let applied = do_the_gravity(rotate(before_last, &last_action));
|
||||
//println!("applied: {}", applied);
|
||||
choices.push(last_action);
|
||||
cache.insert(choices, applied.clone());
|
||||
applied
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn build_children(base: &[Vec<RotationDir>]) -> Vec<Vec<RotationDir>> {
|
||||
let mut children = Vec::new();
|
||||
for path in base {
|
||||
let mut c1 = path.clone();
|
||||
let mut c2 = path.clone();
|
||||
c1.push(RotationDir::CW);
|
||||
c2.push(RotationDir::CCW);
|
||||
children.push(c1);
|
||||
children.push(c2);
|
||||
}
|
||||
children
|
||||
}
|
||||
|
||||
pub fn breadth_first_search(puzzle: Puzzle) -> Option<Vec<RotationDir>> {
|
||||
let mut cache = HashMap::new();
|
||||
let mut known_outcomes = HashSet::new();
|
||||
let mut base_paths = vec![vec![]];
|
||||
let mut depth = 0;
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
depth += 1;
|
||||
|
||||
let children = build_children(&base_paths);
|
||||
if children.is_empty() {
|
||||
return None;
|
||||
}
|
||||
base_paths = Vec::new();
|
||||
for child_path in &children {
|
||||
let tmp_puzzle = apply_choices(puzzle.clone(), child_path, &mut cache);
|
||||
if !known_outcomes.contains(&tmp_puzzle) {
|
||||
base_paths.push(child_path.clone());
|
||||
} else {
|
||||
cache.remove(child_path); // save that RAM!
|
||||
}
|
||||
if puzzle_gelöst(&tmp_puzzle) {
|
||||
return Some(child_path.clone());
|
||||
} else {
|
||||
known_outcomes.insert(tmp_puzzle);
|
||||
}
|
||||
}
|
||||
println!("{:03} depth, {:06} children, {:07} known, {:07} cached, {:04} sec", depth, children.len(), known_outcomes.len(), cache.len(), start.elapsed().as_secs());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn main() {
|
||||
if let Some(dateiname) = env::args().nth(1) {
|
||||
//let pause = std::time::Duration::from_millis((16.6*4.0) as u64);
|
||||
|
||||
let puzzle = lade_puzzle(&dateiname);
|
||||
if let Err(e) = puzzle {
|
||||
println!("Error: {:?}", e);
|
||||
return;
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
println!("Puzzle: {}", puzzle);
|
||||
println!("{:?}", puzzle);
|
||||
//let puzzle = Puzzle { seitenlänge: 10, raster: vec![vec![-3, -3, -3, -3, -3, -3, -3, -3, -3, -3], vec![-3, -2, -2, -2, -2, -2, -2, -2, -2, -3], vec![-3, -2, -2, -2, -2, -2, -2, -2, -2, -3], vec![-3, -2, -2, -2, -2, -2, -2, -2, -2, -3], vec![-3, -2, -2, -2, -2, 7, 7, 7, 5, -3], vec![-3, -2, -2, -2, 1, 1, -2, -2, 5, -3], vec![-3, -2, -2, -2, 2, -2, 8, 8, 8, -3], vec![-3, 9, 9, 0, 2, 3, 3, -2, -2, -3], vec![-3, 4, 4, 0, 6, 6, 6, 6, 6, -3], vec![-3, -3, -3, -3, -3, -1, -3, -3, -3, -3]] };
|
||||
/*
|
||||
//let puzzle = do_the_gravity(rotate(puzzle, &RotationDir::CW));
|
||||
let puzzle_a = apply_choices(puzzle.clone(), &vec![RotationDir::CW, RotationDir::CW, RotationDir::CCW], &mut HashMap::new());
|
||||
println!("Puzzlea: {}", puzzle_a);
|
||||
let puzzle_b = do_the_gravity(rotate(puzzle.clone(), &RotationDir::CCW));
|
||||
let puzzle_b = do_the_gravity(rotate(puzzle_b, &RotationDir::CCW));
|
||||
let puzzle_b = do_the_gravity(rotate(puzzle_b, &RotationDir::CCW));
|
||||
let puzzle_b = do_the_gravity(rotate(puzzle_b, &RotationDir::CCW));
|
||||
let puzzle_b = do_the_gravity(rotate(puzzle_b, &RotationDir::CW));
|
||||
let puzzle_b = do_the_gravity(rotate(puzzle_b, &RotationDir::CCW));
|
||||
println!("Puzzleb: {}", puzzle_b);
|
||||
*/
|
||||
let solution = breadth_first_search(puzzle);
|
||||
if let Some(solution) = solution {
|
||||
println!("lösung: {:?}", solution);
|
||||
} else {
|
||||
println!("keine lösung gefunden!");
|
||||
}
|
||||
/*
|
||||
let steps = [RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CW];
|
||||
for s in steps.iter() {
|
||||
println!("action: {:?}", s);
|
||||
puzzle = do_the_gravity(rotate(puzzle, s));
|
||||
println!("{}", puzzle);
|
||||
std::thread::sleep(pause);
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
println!("Bitte so aufrufen: ./aufgabe3 <dateiname>");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn children() {
|
||||
let children = build_children(&vec![vec![RotationDir::CW]]);
|
||||
let expected = vec![vec![RotationDir::CW, RotationDir::CW], vec![RotationDir::CW, RotationDir::CCW]];
|
||||
if children == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod benchs {
|
||||
extern crate test;
|
||||
|
||||
use super::*;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn test1(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let puzzle = lade_puzzle("rotation1_03.txt");
|
||||
if puzzle.is_err() {
|
||||
panic!();
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
//println!("Puzzle: {}", puzzle);
|
||||
breadth_first_search(puzzle);
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn test2(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let puzzle = lade_puzzle("rotation2_03.txt");
|
||||
if puzzle.is_err() {
|
||||
panic!();
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
//println!("Puzzle: {}", puzzle);
|
||||
breadth_first_search(puzzle);
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_cw(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
rotate(puzzle.clone(), &test::black_box(RotationDir::CW))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_ccw(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
rotate(puzzle.clone(), &test::black_box(RotationDir::CCW))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_cw_grav(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
do_the_gravity(rotate(puzzle.clone(), &test::black_box(RotationDir::CW)))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_ccw_grav(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
do_the_gravity(rotate(puzzle.clone(), &test::black_box(RotationDir::CCW)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
fn apply_choices(mut puzzle: Puzzle, choices: &Vec<RotationDir>) -> Puzzle {
|
||||
for rotation in choices {
|
||||
puzzle = do_the_gravity(rotate(puzzle, rotation));
|
||||
}
|
||||
puzzle
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
fn cmp_vectors(small: &Vec<RotationDir>, large: &Vec<RotationDir>) -> bool {
|
||||
if small.len() > large.len() {
|
||||
false
|
||||
} else {
|
||||
let mut same = true;
|
||||
for (index, value) in small.iter().enumerate() {
|
||||
if value != &large[index] {
|
||||
same = false;
|
||||
}
|
||||
}
|
||||
same
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
fn breadth_first_search(puzzle: Puzzle) -> Option<Vec<RotationDir>> {
|
||||
let mut choices = vec![RotationDir::CW];
|
||||
let mut known_outcomes = HashSet::new();
|
||||
let mut banned_paths = HashSet::new();
|
||||
let mut cont_counter = 0;
|
||||
let mut ban_counter = 0;
|
||||
loop {
|
||||
for path in &banned_paths {
|
||||
if cmp_vectors(&path, &choices) {
|
||||
//println!("banned, skip");
|
||||
ban_counter += 1;
|
||||
choices = next_combination(choices);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if cont_counter % 100 == 0 && cont_counter > 0 {
|
||||
println!("{:06} conts, {:06} ban-conts.", cont_counter, ban_counter);
|
||||
}
|
||||
//println!("trying {:?}", choices);
|
||||
let new = apply_choices(puzzle.clone(), &choices);
|
||||
if known_outcomes.contains(&new) {
|
||||
cont_counter += 1;
|
||||
banned_paths.insert(choices.clone());
|
||||
choices = next_combination(choices);
|
||||
continue;
|
||||
}
|
||||
if puzzle_gelöst(&new) {
|
||||
return Some(choices);
|
||||
}
|
||||
known_outcomes.insert(new);
|
||||
choices = next_combination(choices);
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*
|
||||
fn breadth_first_search(puzzle: Puzzle, mut known_outcomes: &mut HashSet<Puzzle>, mut choices: Vec<RotationDir>) -> Option<Vec<RotationDir>> {
|
||||
//println!("base: {:?}, known: {:?}", choices, known_outcomes.len());
|
||||
choices.push(RotationDir::CW);
|
||||
let puzzle_cw = apply_choices(puzzle.clone(), &choices);
|
||||
if known_outcomes.contains(&puzzle_cw) {
|
||||
|
||||
} else {
|
||||
known_outcomes.insert(puzzle_cw.clone());
|
||||
if puzzle_gelöst(&puzzle_cw) {
|
||||
return Some(choices);
|
||||
} else {
|
||||
if let Some(solution) = breadth_first_search(puzzle.clone(), &mut known_outcomes, choices.clone()) {
|
||||
return Some(solution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
choices.pop().unwrap();
|
||||
choices.push(RotationDir::CCW);
|
||||
let puzzle_cw = apply_choices(puzzle.clone(), &choices);
|
||||
if known_outcomes.contains(&puzzle_cw) {
|
||||
return None;
|
||||
} else {
|
||||
known_outcomes.insert(puzzle_cw.clone());
|
||||
if puzzle_gelöst(&puzzle_cw) {
|
||||
return Some(choices);
|
||||
} else {
|
||||
if let Some(solution) = breadth_first_search(puzzle.clone(), &mut known_outcomes, choices.clone()) {
|
||||
return Some(solution);
|
||||
} else {
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub fn next_combination(choices: Vec<RotationDir>) -> Vec<RotationDir> {
|
||||
let mut choices = choices;
|
||||
if choices.iter().filter(|&x| x == &RotationDir::CCW).count() == choices.len() {
|
||||
choices = choices.iter().map(|_| RotationDir::CW).collect();
|
||||
choices.push(RotationDir::CW);
|
||||
choices
|
||||
} else {
|
||||
let mut rchoices = choices.clone();
|
||||
rchoices.reverse();
|
||||
let mut cw_index = 0;
|
||||
for (index, c) in rchoices.iter().enumerate() {
|
||||
if c == &RotationDir::CW {
|
||||
cw_index = rchoices.len()-index-1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
choices[cw_index] = RotationDir::CCW;
|
||||
for mut item in choices.iter_mut().skip(cw_index+1) {
|
||||
*item = RotationDir::CW;
|
||||
}
|
||||
choices
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn a() {
|
||||
let next = next_combination(vec![RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn b() {
|
||||
let next = next_combination(vec![RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aa() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ab() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CCW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ba() {
|
||||
let next = next_combination(vec![RotationDir::CCW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CCW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bb() {
|
||||
let next = next_combination(vec![RotationDir::CCW, RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aba() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn abb() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CCW, RotationDir::CW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn abba() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}*/
|
695
Aufgabe3/src/main_yes_s.rs
Normal file
@ -0,0 +1,695 @@
|
||||
#![feature(non_ascii_idents)]
|
||||
#![feature(custom_derive)]
|
||||
#![feature(test)]
|
||||
#![feature(plugin)]
|
||||
|
||||
#![plugin(clippy)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate colored;
|
||||
use colored::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::time::Instant;
|
||||
use std::path::Path;
|
||||
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum Stabrichtung {
|
||||
Horizontal,
|
||||
Vertikal
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq)]
|
||||
pub struct Punkt {
|
||||
x: usize,
|
||||
y: usize
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub struct Puzzle {
|
||||
seitenlänge: usize,
|
||||
raster: Vec<Vec<i64>>
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Puzzle {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
for row in &self.raster {
|
||||
write!(f, "\n")?;
|
||||
for cell in row {
|
||||
match *cell {
|
||||
WALL => write!(f, "{}", format!("{:02} ", cell).black())?,
|
||||
NIX => write!(f, "{}", format!("{:02} ", cell).black())?,
|
||||
AUSGANG => write!(f, "{}", format!("{:02} ", cell).green())?,
|
||||
_ if cell % 5 == 0 => write!(f, "{}", format!("{:02} ", cell).red())?,
|
||||
_ if cell % 5 == 1 => write!(f, "{}", format!("{:02} ", cell).yellow())?,
|
||||
_ if cell % 5 == 2 => write!(f, "{}", format!("{:02} ", cell).blue())?,
|
||||
_ if cell % 5 == 3 => write!(f, "{}", format!("{:02} ", cell).purple())?,
|
||||
_ if cell % 5 == 4 => write!(f, "{}", format!("{:02} ", cell).cyan())?,
|
||||
_ => write!(f, "{:02} ", cell)?
|
||||
};
|
||||
}
|
||||
}
|
||||
write!(f, "\n")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum RotationDir {
|
||||
// clockwise
|
||||
CW,
|
||||
// counterclockwise
|
||||
CCW
|
||||
}
|
||||
|
||||
const WALL: i64 = -3;
|
||||
const NIX: i64 = -2;
|
||||
const AUSGANG: i64 = -1;
|
||||
|
||||
|
||||
pub fn lade_puzzle(dateiname: &str) -> Result<Puzzle, String> {
|
||||
let path = Path::new(dateiname);
|
||||
let display = path.display();
|
||||
|
||||
// Open the path in read-only mode, returns `io::Result<File>`
|
||||
let mut file = match File::open(&path) {
|
||||
// The `description` method of `io::Error` returns a string that
|
||||
// describes the error
|
||||
Err(why) => { return Err(format!("couldn't open {}: {}", display,
|
||||
why.description())); },
|
||||
Ok(file) => file,
|
||||
};
|
||||
|
||||
// Read the file contents into a string, returns `io::Result<usize>`
|
||||
let mut s = String::new();
|
||||
if let Err(why) = file.read_to_string(&mut s) {
|
||||
return Err(format!("couldn't read {}: {}", display,
|
||||
why.description()));
|
||||
}
|
||||
let mut p = Puzzle { seitenlänge: 0, raster: Vec::new() };
|
||||
let lines: Vec<&str> = s.split('\n').collect();
|
||||
|
||||
for (index, line) in lines.iter().enumerate() {
|
||||
if index == 0 {
|
||||
let seitenlänge = line.parse().unwrap();
|
||||
p.seitenlänge = seitenlänge;
|
||||
continue;
|
||||
}
|
||||
let index = index-1;
|
||||
if index == p.seitenlänge {
|
||||
break;
|
||||
}
|
||||
|
||||
let mut line_vec = Vec::new();
|
||||
for (x, c) in line.chars().enumerate() {
|
||||
// ausgang
|
||||
if c == ' ' && ((x == 0 || x == line.len()-1) || (index == 0 || index == p.seitenlänge-1)) {
|
||||
line_vec.push(AUSGANG);
|
||||
continue;
|
||||
}
|
||||
match c.to_digit(10) {
|
||||
Some(digit) => line_vec.push(digit as i64),
|
||||
// -1 == " "
|
||||
None => {
|
||||
match c {
|
||||
'#' => line_vec.push(WALL),
|
||||
' ' => line_vec.push(NIX),
|
||||
_ => panic!("unbekannter buchstabe {:?}", c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
p.raster.push(line_vec);
|
||||
//println!("{:?}: {:?}", index, line);
|
||||
}
|
||||
Ok(p)
|
||||
}
|
||||
|
||||
pub fn rotate(puzzle: Puzzle, dir: &RotationDir) -> Puzzle {
|
||||
let seitenlänge = puzzle.seitenlänge;
|
||||
let s = seitenlänge;
|
||||
let mut raster = Vec::new();
|
||||
for r in &puzzle.raster {
|
||||
let mut row_vec = Vec::new();
|
||||
for c in r {
|
||||
row_vec.push(*c);
|
||||
}
|
||||
raster.push(row_vec);
|
||||
}
|
||||
let mut roted_raster = raster.clone();
|
||||
macro_rules! set {
|
||||
($x:expr, $y:expr, $new:expr) => (
|
||||
roted_raster[$y][$x] = $new;
|
||||
)
|
||||
}
|
||||
|
||||
let mut p = puzzle;
|
||||
|
||||
let mut y = 0;
|
||||
for row in raster {
|
||||
for (x, cell) in row.iter().enumerate() {
|
||||
match *dir {
|
||||
RotationDir::CW => set!(s-y-1, x, *cell),
|
||||
RotationDir::CCW => set!(y, s-x-1, *cell)
|
||||
}
|
||||
}
|
||||
y += 1;
|
||||
}
|
||||
p.raster = roted_raster;
|
||||
p
|
||||
}
|
||||
|
||||
fn get_stäbchen(raster: &[Vec<i64>]) -> HashMap<u64, Vec<Punkt>> {
|
||||
let mut stäbchen: HashMap<u64, Vec<Punkt>> = HashMap::new();
|
||||
|
||||
for (y, row) in raster.iter().enumerate() {
|
||||
for (x, cell) in row.iter().enumerate() {
|
||||
if !cell.is_negative() {
|
||||
let cell = *cell as u64;
|
||||
let value = stäbchen.entry(cell).or_insert_with(Vec::new);
|
||||
value.push(Punkt { x: x, y: y });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stäbchen
|
||||
}
|
||||
|
||||
fn get_stäbchen_richtung(stab: &[Punkt]) -> Stabrichtung {
|
||||
if stab.len() == 1 {
|
||||
// egal
|
||||
return Stabrichtung::Vertikal;
|
||||
}
|
||||
if stab[0].x == stab[1].x {
|
||||
Stabrichtung::Vertikal
|
||||
} else {
|
||||
Stabrichtung::Horizontal
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_the_gravity(mut puzzle: Puzzle) -> Puzzle {
|
||||
macro_rules! get {
|
||||
($x:expr, $y:expr) => (
|
||||
puzzle.raster[$y][$x];
|
||||
)
|
||||
}
|
||||
macro_rules! set {
|
||||
($x:expr, $y:expr, $new:expr) => (
|
||||
puzzle.raster[$y][$x] = $new;
|
||||
)
|
||||
}
|
||||
|
||||
let mut stäbchen1 = get_stäbchen(&puzzle.raster);
|
||||
let mut stäbchen2 = HashMap::new();
|
||||
while stäbchen1 != stäbchen2 {
|
||||
//println!("sloop");
|
||||
stäbchen1 = get_stäbchen(&puzzle.raster);
|
||||
//println!("{:?}", stäbchen1);
|
||||
for (index, stab) in &stäbchen1 {
|
||||
let mut move_possible = true;
|
||||
let richtung = get_stäbchen_richtung(stab);
|
||||
let mut highest_y = 0;
|
||||
let mut lowest_punkt = &stab[0];
|
||||
for p in stab {
|
||||
if p.y == puzzle.seitenlänge-2 {
|
||||
move_possible = false;
|
||||
}
|
||||
if p.y+1 <= puzzle.seitenlänge-1 && ((get!(p.x, p.y+1) != NIX && get!(p.x, p.y+1) != AUSGANG) && richtung == Stabrichtung::Horizontal) {
|
||||
move_possible = false;
|
||||
}
|
||||
if richtung == Stabrichtung::Vertikal && p.y > highest_y {
|
||||
highest_y = p.y;
|
||||
lowest_punkt = p;
|
||||
}
|
||||
}
|
||||
if richtung == Stabrichtung::Vertikal && lowest_punkt.y+1 <= puzzle.seitenlänge-1 {
|
||||
if get!(lowest_punkt.x, lowest_punkt.y+1) >= 0 {
|
||||
//println!("{:?} blocked-low because of {:?}, {:?}: {:?}", index,
|
||||
// lowest_punkt.x,
|
||||
// lowest_punkt.y+1,
|
||||
// get!(lowest_punkt.x, lowest_punkt.y+1));
|
||||
move_possible = false;
|
||||
} else if highest_y == puzzle.seitenlänge-2 && get!(lowest_punkt.x, lowest_punkt.y+1) == AUSGANG {
|
||||
//println!("Puzzle ist möglich!");
|
||||
move_possible = true;
|
||||
}
|
||||
}
|
||||
|
||||
if move_possible {
|
||||
let mut new_stab = Vec::new();
|
||||
for p in stab {
|
||||
set!(p.x, p.y, NIX);
|
||||
new_stab.push(p);
|
||||
}
|
||||
let index = *index as i64;
|
||||
for p in new_stab {
|
||||
if p.y+1 < puzzle.seitenlänge {
|
||||
//println!("setting {:?} {:?}", p.x, p.y+1);
|
||||
set!(p.x, p.y+1, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
stäbchen2 = stäbchen1;
|
||||
stäbchen1 = get_stäbchen(&puzzle.raster);
|
||||
//println!("eloop");
|
||||
}
|
||||
//println!("EOG");
|
||||
puzzle
|
||||
}
|
||||
|
||||
fn puzzle_gelöst(puzzle: &Puzzle) -> bool {
|
||||
let mut ausgang_gefunden = false;
|
||||
for row in &puzzle.raster {
|
||||
for cell in row {
|
||||
if *cell == AUSGANG {
|
||||
ausgang_gefunden = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
!ausgang_gefunden
|
||||
}
|
||||
|
||||
fn apply_choices(puzzle: Puzzle, choices: &[RotationDir], cache: &mut HashMap<Vec<RotationDir>, Puzzle>) -> Puzzle {
|
||||
if cache.contains_key(choices) {
|
||||
//println!("{:?} cached", choices);
|
||||
cache.get(choices).unwrap().clone()
|
||||
} else if choices.len() == 1 {
|
||||
//println!("len one");
|
||||
do_the_gravity(rotate(puzzle, &choices[0]))
|
||||
} else {
|
||||
let mut choices = Vec::from(choices);
|
||||
let last_action = choices.pop().unwrap();
|
||||
let before_last = apply_choices(puzzle, &choices, cache);
|
||||
//println!("before: {}", before_last);
|
||||
let applied = do_the_gravity(rotate(before_last, &last_action));
|
||||
//println!("applied: {}", applied);
|
||||
choices.push(last_action);
|
||||
cache.insert(choices, applied.clone());
|
||||
applied
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn build_children(base: &[Vec<RotationDir>]) -> Vec<Vec<RotationDir>> {
|
||||
let mut children = Vec::new();
|
||||
for path in base {
|
||||
let mut c1 = path.clone();
|
||||
let mut c2 = path.clone();
|
||||
c1.push(RotationDir::CW);
|
||||
c2.push(RotationDir::CCW);
|
||||
children.push(c1);
|
||||
children.push(c2);
|
||||
}
|
||||
children
|
||||
}
|
||||
|
||||
pub fn breadth_first_search(puzzle: Puzzle) -> Option<Vec<RotationDir>> {
|
||||
let mut cache = HashMap::new();
|
||||
let mut known_outcomes = Vec::new();
|
||||
let mut base_paths = vec![vec![]];
|
||||
let mut depth = 0;
|
||||
let mut printed_depth = depth;
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
let children = build_children(&base_paths);
|
||||
depth += 1;
|
||||
if children.is_empty() {
|
||||
return None;
|
||||
}
|
||||
base_paths = Vec::new();
|
||||
for child_path in &children {
|
||||
let tmp_puzzle = apply_choices(puzzle.clone(), child_path, &mut cache);
|
||||
if !known_outcomes.contains(&tmp_puzzle) {
|
||||
base_paths.push(child_path.clone());
|
||||
}
|
||||
if puzzle_gelöst(&tmp_puzzle) {
|
||||
return Some(child_path.clone());
|
||||
} else {
|
||||
known_outcomes.push(tmp_puzzle);
|
||||
}
|
||||
}
|
||||
if printed_depth != depth && depth % 1 == 0 {
|
||||
println!("{:03} depth, {:05} children, {:06} known, {:06} cached, {:04} sec", depth, children.len(), known_outcomes.len(), cache.len(), start.elapsed().as_secs());
|
||||
printed_depth = depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn main() {
|
||||
if let Some(dateiname) = env::args().nth(1) {
|
||||
//let pause = std::time::Duration::from_millis((16.6*4.0) as u64);
|
||||
|
||||
let puzzle = lade_puzzle(&dateiname);
|
||||
if let Err(e) = puzzle {
|
||||
println!("Error: {:?}", e);
|
||||
return;
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
println!("Puzzle: {}", puzzle);
|
||||
println!("{:?}", puzzle);
|
||||
//let puzzle = Puzzle { seitenlänge: 10, raster: vec![vec![-3, -3, -3, -3, -3, -3, -3, -3, -3, -3], vec![-3, -2, -2, -2, -2, -2, -2, -2, -2, -3], vec![-3, -2, -2, -2, -2, -2, -2, -2, -2, -3], vec![-3, -2, -2, -2, -2, -2, -2, -2, -2, -3], vec![-3, -2, -2, -2, -2, 7, 7, 7, 5, -3], vec![-3, -2, -2, -2, 1, 1, -2, -2, 5, -3], vec![-3, -2, -2, -2, 2, -2, 8, 8, 8, -3], vec![-3, 9, 9, 0, 2, 3, 3, -2, -2, -3], vec![-3, 4, 4, 0, 6, 6, 6, 6, 6, -3], vec![-3, -3, -3, -3, -3, -1, -3, -3, -3, -3]] };
|
||||
/*
|
||||
//let puzzle = do_the_gravity(rotate(puzzle, &RotationDir::CW));
|
||||
let puzzle_a = apply_choices(puzzle.clone(), &vec![RotationDir::CW, RotationDir::CW, RotationDir::CCW], &mut HashMap::new());
|
||||
println!("Puzzlea: {}", puzzle_a);
|
||||
let puzzle_b = do_the_gravity(rotate(puzzle.clone(), &RotationDir::CCW));
|
||||
let puzzle_b = do_the_gravity(rotate(puzzle_b, &RotationDir::CCW));
|
||||
let puzzle_b = do_the_gravity(rotate(puzzle_b, &RotationDir::CCW));
|
||||
let puzzle_b = do_the_gravity(rotate(puzzle_b, &RotationDir::CCW));
|
||||
let puzzle_b = do_the_gravity(rotate(puzzle_b, &RotationDir::CW));
|
||||
let puzzle_b = do_the_gravity(rotate(puzzle_b, &RotationDir::CCW));
|
||||
println!("Puzzleb: {}", puzzle_b);
|
||||
*/
|
||||
let solution = breadth_first_search(puzzle);
|
||||
if let Some(solution) = solution {
|
||||
println!("lösung: {:?}", solution);
|
||||
} else {
|
||||
println!("keine lösung gefunden!");
|
||||
}
|
||||
/*
|
||||
let steps = [RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CW, RotationDir::CCW, RotationDir::CW];
|
||||
for s in steps.iter() {
|
||||
println!("action: {:?}", s);
|
||||
puzzle = do_the_gravity(rotate(puzzle, s));
|
||||
println!("{}", puzzle);
|
||||
std::thread::sleep(pause);
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
println!("Bitte so aufrufen: ./aufgabe3 <dateiname>");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn children() {
|
||||
let children = build_children(&vec![vec![RotationDir::CW]]);
|
||||
let expected = vec![vec![RotationDir::CW, RotationDir::CW], vec![RotationDir::CW, RotationDir::CCW]];
|
||||
if children == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod benchs {
|
||||
extern crate test;
|
||||
|
||||
use super::*;
|
||||
use self::test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn test1(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let puzzle = lade_puzzle("rotation1_03.txt");
|
||||
if puzzle.is_err() {
|
||||
panic!();
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
//println!("Puzzle: {}", puzzle);
|
||||
breadth_first_search(puzzle);
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn test2(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let puzzle = lade_puzzle("rotation2_03.txt");
|
||||
if puzzle.is_err() {
|
||||
panic!();
|
||||
}
|
||||
let puzzle = puzzle.unwrap();
|
||||
//println!("Puzzle: {}", puzzle);
|
||||
breadth_first_search(puzzle);
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_cw(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
rotate(puzzle.clone(), &test::black_box(RotationDir::CW))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_ccw(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
rotate(puzzle.clone(), &test::black_box(RotationDir::CCW))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_cw_grav(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
do_the_gravity(rotate(puzzle.clone(), &test::black_box(RotationDir::CW)))
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn rotate_ccw_grav(b: &mut Bencher) {
|
||||
let puzzle = lade_puzzle("rotation3_03.txt").unwrap();
|
||||
b.iter(|| {
|
||||
do_the_gravity(rotate(puzzle.clone(), &test::black_box(RotationDir::CCW)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
fn apply_choices(mut puzzle: Puzzle, choices: &Vec<RotationDir>) -> Puzzle {
|
||||
for rotation in choices {
|
||||
puzzle = do_the_gravity(rotate(puzzle, rotation));
|
||||
}
|
||||
puzzle
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
fn cmp_vectors(small: &Vec<RotationDir>, large: &Vec<RotationDir>) -> bool {
|
||||
if small.len() > large.len() {
|
||||
false
|
||||
} else {
|
||||
let mut same = true;
|
||||
for (index, value) in small.iter().enumerate() {
|
||||
if value != &large[index] {
|
||||
same = false;
|
||||
}
|
||||
}
|
||||
same
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
fn breadth_first_search(puzzle: Puzzle) -> Option<Vec<RotationDir>> {
|
||||
let mut choices = vec![RotationDir::CW];
|
||||
let mut known_outcomes = HashSet::new();
|
||||
let mut banned_paths = HashSet::new();
|
||||
let mut cont_counter = 0;
|
||||
let mut ban_counter = 0;
|
||||
loop {
|
||||
for path in &banned_paths {
|
||||
if cmp_vectors(&path, &choices) {
|
||||
//println!("banned, skip");
|
||||
ban_counter += 1;
|
||||
choices = next_combination(choices);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if cont_counter % 100 == 0 && cont_counter > 0 {
|
||||
println!("{:06} conts, {:06} ban-conts.", cont_counter, ban_counter);
|
||||
}
|
||||
//println!("trying {:?}", choices);
|
||||
let new = apply_choices(puzzle.clone(), &choices);
|
||||
if known_outcomes.contains(&new) {
|
||||
cont_counter += 1;
|
||||
banned_paths.insert(choices.clone());
|
||||
choices = next_combination(choices);
|
||||
continue;
|
||||
}
|
||||
if puzzle_gelöst(&new) {
|
||||
return Some(choices);
|
||||
}
|
||||
known_outcomes.insert(new);
|
||||
choices = next_combination(choices);
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*
|
||||
fn breadth_first_search(puzzle: Puzzle, mut known_outcomes: &mut HashSet<Puzzle>, mut choices: Vec<RotationDir>) -> Option<Vec<RotationDir>> {
|
||||
//println!("base: {:?}, known: {:?}", choices, known_outcomes.len());
|
||||
choices.push(RotationDir::CW);
|
||||
let puzzle_cw = apply_choices(puzzle.clone(), &choices);
|
||||
if known_outcomes.contains(&puzzle_cw) {
|
||||
|
||||
} else {
|
||||
known_outcomes.insert(puzzle_cw.clone());
|
||||
if puzzle_gelöst(&puzzle_cw) {
|
||||
return Some(choices);
|
||||
} else {
|
||||
if let Some(solution) = breadth_first_search(puzzle.clone(), &mut known_outcomes, choices.clone()) {
|
||||
return Some(solution);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
choices.pop().unwrap();
|
||||
choices.push(RotationDir::CCW);
|
||||
let puzzle_cw = apply_choices(puzzle.clone(), &choices);
|
||||
if known_outcomes.contains(&puzzle_cw) {
|
||||
return None;
|
||||
} else {
|
||||
known_outcomes.insert(puzzle_cw.clone());
|
||||
if puzzle_gelöst(&puzzle_cw) {
|
||||
return Some(choices);
|
||||
} else {
|
||||
if let Some(solution) = breadth_first_search(puzzle.clone(), &mut known_outcomes, choices.clone()) {
|
||||
return Some(solution);
|
||||
} else {
|
||||
return None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub fn next_combination(choices: Vec<RotationDir>) -> Vec<RotationDir> {
|
||||
let mut choices = choices;
|
||||
if choices.iter().filter(|&x| x == &RotationDir::CCW).count() == choices.len() {
|
||||
choices = choices.iter().map(|_| RotationDir::CW).collect();
|
||||
choices.push(RotationDir::CW);
|
||||
choices
|
||||
} else {
|
||||
let mut rchoices = choices.clone();
|
||||
rchoices.reverse();
|
||||
let mut cw_index = 0;
|
||||
for (index, c) in rchoices.iter().enumerate() {
|
||||
if c == &RotationDir::CW {
|
||||
cw_index = rchoices.len()-index-1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
choices[cw_index] = RotationDir::CCW;
|
||||
for mut item in choices.iter_mut().skip(cw_index+1) {
|
||||
*item = RotationDir::CW;
|
||||
}
|
||||
choices
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
#[test]
|
||||
fn a() {
|
||||
let next = next_combination(vec![RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn b() {
|
||||
let next = next_combination(vec![RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aa() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ab() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CCW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ba() {
|
||||
let next = next_combination(vec![RotationDir::CCW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CCW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bb() {
|
||||
let next = next_combination(vec![RotationDir::CCW, RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aba() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn abb() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW]);
|
||||
let expected = vec![RotationDir::CCW, RotationDir::CW, RotationDir::CW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn abba() {
|
||||
let next = next_combination(vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CW]);
|
||||
let expected = vec![RotationDir::CW, RotationDir::CCW, RotationDir::CCW, RotationDir::CCW];
|
||||
if next == expected {
|
||||
()
|
||||
} else {
|
||||
panic!()
|
||||
}
|
||||
}*/
|