Add Aufgabe 3

This commit is contained in:
FliegendeWurst 2016-12-10 16:43:23 +01:00
commit 80d15f47a7
50 changed files with 3891 additions and 0 deletions

Binary file not shown.

7
Aufgabe3/Cargo.toml Normal file
View 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
View 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.
[↺, ↺, ↺, ↺, ↻, ↺]
[↻, ↻, ↻, ↻, ↺, ↻, ↻, ↺, ↺, ↺, ↺, ↺, ↺, ↺, ↻, ↻, ↻, ↻, ↻, ↻, ↺, ↻]
[↺, ↻, ↻, ↻, ↻, ↺, ↺, ↺, ↻, ↻, ↻, ↺, ↺, ↺, ↺, ↺, ↺, ↺, ↻, ↻, ↺, ↺, ↻, ↻, ↻, ↻, ↻, ↻, ↻, ↺, ↻, ↻, ↻, ↻, ↻, ↻, ↻, ↻, ↻, ↻, ↻, ↺, ↺, ↺, ↻, ↻, ↻, ↻, ↻, ↺, ↺, ↺, ↺, ↺, ↻, ↻, ↻, ↻, ↺, ↺, ↻, ↻, ↻, ↻, ↻, ↻, ↻, ↺, ↻, ↺, ↺, ↺, ↺, ↺, ↺, ↺, ↺, ↺, ↺, ↺, ↻, ↺, ↺, ↻, ↻, ↻, ↻, ↻, ↺, ↻]

View File

@ -0,0 +1,9 @@
7
#######
# 333#
2#
#1 2#
#1 2#
#100 2#
#######
Nicht nur Ausgänge unten sind erlaubt!

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

11
Aufgabe3/rotation11_n.txt Normal file
View 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

12
Aufgabe3/rotation12_n.txt Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

11
Aufgabe3/rotation13_n.txt Normal file
View 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.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,5 @@
4
####
# 0#
#0 #
####

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

View File

@ -0,0 +1,5 @@
4
###
# #
# 0#
####

Binary file not shown.

After

Width:  |  Height:  |  Size: 541 B

View File

@ -0,0 +1,9 @@
8
########
# 0#
# 0#
#112222#
#33 4#
#55 4#
#666 4#
### ####

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

13
Aufgabe3/rotation2_03.txt Normal file
View File

@ -0,0 +1,13 @@
12
############
# #
# 01 #
# 01 #
# 01 #
# 222222#
# 34 5 #
# 34 5 #
# 634 5 #
# 63477775 #
# 63888885 #
###### #####

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

19
Aufgabe3/rotation3_03.txt Normal file
View 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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

16
Aufgabe3/rotation4_n.txt Normal file
View 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 ...

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

17
Aufgabe3/rotation6_n.txt Normal file
View 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 ...

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

27
Aufgabe3/rotation7_n.txt Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

15
Aufgabe3/rotation8_n.txt Normal file
View File

@ -0,0 +1,15 @@
14
##############
# #
# #
# #
# 4#
#8 4#
#8 3#
#8 3#
#9 1#
#9 21#
#9 BB 520#
#9AA 520#
#6677 CCCCCCC#
####### ######

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

7
Aufgabe3/rotation9_n.txt Normal file
View File

@ -0,0 +1,7 @@
5
#####
# #
#1 0#
#1 0#
## ##
Ungerade Seitenlängen sind möglich!

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 B

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 926 B

786
Aufgabe3/src/main.rs Normal file
View 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
View 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
View 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
View 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
View 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!()
}
}*/