a4 Quelltext

This commit is contained in:
2021-11-19 16:03:04 +01:00
parent ba25632650
commit c2ca8aa9fc
7 changed files with 537 additions and 0 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
CMakeFiles
.cache
a[1-5]
Makefile
target

View File

@ -185,3 +185,72 @@ N: Q 1 links, R 1 links
O: Q 2 links, R 2 links, S 2 links
P: Q 1 links, R 1 links, S 1 links
```
## Quelltext
```
func main() {
for i := range querAutos {
var currentMoves []movedCar // enthält die Verschiebungen der Autos in die Richtung in der weniger Autos verschoben werden müssen
var carLeastSteps uint32
for _, direction := range []int{-1, 1} { // -1 steht für links und 1 für rechts
movedCars, steps, success := move(i, direction, []movedCar{}, 0, 0)
/* Wenn das Verschieben der Autos in der jeweiligen Richtung erfolgreich ist und noch keine
Verschiebungen abgespeichert wurden, d. h. gerade die Verschiebungen für links berechnet
wurden oder das Verschieben nach links nicht erfolgreich war, da das Ende das Parkplatzes
erreicht wurde, dann werden die Verschiebungen abgespeichert.
Sollten allerdings bereits Verschiebungen für links vorhanden sein, wird das Verschieben nach
rechts bevorzugt, wenn weniger Autos verschoben werden mussten oder die Autos weniger Plätze
verschoben werden mussten. */
if success && (currentMoves == nil || len(movedCars) < len(currentMoves) || len(movedCars) == len(currentMoves) && steps < carLeastSteps) {
currentMoves = movedCars
carLeastSteps = steps
}
}
}
}
// Funktion zur Berechung der Autos, die für einen Parkplatz verschoben werden müssen.
func move(i, direction int, movedCars []movedCar, lastMoved byte, totalSteps uint32) ([]movedCar, uint32, bool) {
querAuto := querAutos[i]
/* Wenn querAuto gleich 0 ist, ist kein Auto vor dem Parkplatz.
Wenn es das gleiche Auto ist wie das zuletzt verschobene, ist auch das Verschieben beendet,
da dieses Auto bereits ausreichend verschoben wurde. */
if querAuto == 0 || querAuto == lastMoved {
return movedCars, totalSteps, true
}
/* Wenn neben unserem Auto in der jeweiligen Richtung kein Parkplatz mehr ist,
dann können auch keine Autos in diese Richtung verschoben werden. */
if outOfBounds(querAutos, i + direction) {
return movedCars, totalSteps, false
}
var steps int
/* Wenn sich eins nach der Verschiebungsrichtung auch unserer quer parkendes Auto befindet,
muss es nur um einen Platz verschoben werden, ansonsten um zwei. */
if querAutos[i + direction] == querAuto {
steps = 1
} else {
steps = 2
}
endCar := i + direction * 2 // ist das Ende des verschobenen Autos, das realtiv zur verschobenen Richtung, vorne ist.
/* müsste das Auto ausserhalb des Parkplatzes geschoben werden,
damit unser normale Parkplatz frei wird, ist das Verschieben in diese Richtung nicht möglich. */
if outOfBounds(querAutos, endCar) {
return movedCars, totalSteps, false
}
// fügt das gerade verschobene Auto der Slice mit den verschobenen Autos hinzu.
movedCars = append(movedCars, movedCar{querAuto, steps, direction})
/* ruft die Funktion recursive auf, mit dem Index des Parkplatzes an dem
noch ein quer stehendes Auto stehen könnte, das verschoben werden müsste. */
return move(endCar, direction, movedCars, querAuto, totalSteps + uint32(steps))
}
// Funktion um zu überprüfen, ob ein Index innerhalb einer Slice liegt.
func outOfBounds(slice []byte, i int) bool {
return i < 0 || i >= len(slice)
}
```

107
a2-Vollgeladen/README.md Normal file
View File

@ -0,0 +1,107 @@
# Vollgeladen
## Lösungsidee
Da die niedrigste Bewertung möglichst hoch sein soll, sollte zuerst versucht
eine Route mit einer Mindestbewertung von 5.0 zu bilden. Scheitert dies wird
es mit 4.9, 4.8, ..., 0.1 versucht. Das Routenbildung mit einer bestimmten
Mindestbewertung kann aus zwei Gründen fehlschlagen:
- innerhalb der 6 Stunden Fahrzeit gibt es kein Hotel mit der Mindestbewertung
- an mehr als 4 Hotels muss gehalten werden
## Umsetzung
Die Lösungsidee wird in C implementiert. Eine Hilfsfunktion der
"advanced C standard library", die Textdateien ausliest, wird außerdem verwendet.
Der Quelltext der Library ist im Ordner "lib/advanced_C_standard_library" oder
[online](https://git.zinkel.org/MrGeorgen/advanced_C_standard_library) erhältlich.
Das Programm erhält als Argumente eine beliebige Anzahl Dateipfade zu Eingabedateien.
Mit einer for-Schleife wird über die Dateipfade iteriert. In der Schleife wird die
Eingabedatei ausgelesen. Die Anzahl der Hotels und die Gesamtfahrzeit werden jeweils
in einer Variable abgespeichert. Anschließend wird ein Array mit Strukturen die Hotels
darstellen befüllt. In einer do-while-Schleife wird mit einer Bewertung von
5.0, 4.9, ..., 0.1 eine Route zu bilden. Wenn dies bei einer Bewertung erfolgreich
ist wird die Schleife beendet. In dieser Schleife ist wiederum eine for-Schleife
die über die Hotels iteriert, worin das letzte Hotel gespeichert wird, das
mindestens die angestrebte Bewertung hat.
Ist das Hotel allerdings mehr als 6 Stunden vom letzten Haltepunkt entfernt,
muss das letzte Hotel verwendet werden an dem das Halten möglich ist, falls dieses
vorhanden ist und höchtens an drei Hotels schon gehalten wurde,
ansonsten muss mit der nächsten Bewertung fortgefahren werden.
Nachdem eine Route gefunden wurde, wird diese ausgeben und eventuell mit der
näcshten Eingabedatei fortgefahren.
## Beispiele
Ergebnisse für die Beispieldaten:
```
hotels1.txt:
Entfernung vom Start: 347 Minuten, Bewertung: 2.7
Entfernung vom Start: 687 Minuten, Bewertung: 4.4
Entfernung vom Start: 1007 Minuten, Bewertung: 2.8
Entfernung vom Start: 1360 Minuten, Bewertung: 2.8
hotels2.txt:
Entfernung vom Start: 341 Minuten, Bewertung: 2.3
Entfernung vom Start: 700 Minuten, Bewertung: 3.0
Entfernung vom Start: 1053 Minuten, Bewertung: 4.8
Entfernung vom Start: 1380 Minuten, Bewertung: 5.0
hotels3.txt:
Entfernung vom Start: 360 Minuten, Bewertung: 1.0
Entfernung vom Start: 717 Minuten, Bewertung: 0.3
Entfernung vom Start: 1076 Minuten, Bewertung: 3.8
Entfernung vom Start: 1433 Minuten, Bewertung: 1.7
hotels4.txt:
Entfernung vom Start: 340 Minuten, Bewertung: 4.6
Entfernung vom Start: 676 Minuten, Bewertung: 4.6
Entfernung vom Start: 1032 Minuten, Bewertung: 4.9
Entfernung vom Start: 1316 Minuten, Bewertung: 4.9
hotels5.txt:
Entfernung vom Start: 317 Minuten, Bewertung: 5.0
Entfernung vom Start: 636 Minuten, Bewertung: 5.0
Entfernung vom Start: 987 Minuten, Bewertung: 5.0
Entfernung vom Start: 1286 Minuten, Bewertung: 5.0
```
## Quelltext
```
int main() {
uint_least8_t rating = 50;
do {
// Minuten die das letzte Hotel, bei dem angehalten wurde, vom Start entfertn ist
uint_least16_t lastHotelMinutes = 0;
// Zeiger zum letzten Hotel, dessen Bewertung mindestens "rating" ist.
struct hotel *possibleStop = NULL;
stoppedHotels = 0; // setzt Anzahl der Stops zurück
// iteriert über die Hotels
for(struct hotel *currentHotel = hotels; currentHotel - hotels < numberHotels; ++currentHotel) {
/* Wenn das aktuelle Hotel mehr als 360 Minuten vom letzten Hotel entfernt ist,
* muss das letzte Hotel verwendet werden an dem das Halten möglich war.*/
if(currentHotel->minutes - lastHotelMinutes > 360) {
/* Wenn das Halten nicht möglich ist oder bereits schon viermal
* gehalten wurde, muss es mit einer niedrigeren Bewertung versucht
* werden.*/
if(possibleStop == NULL || stoppedHotels >= 4) break;
// hängt das Hotel der Route an
hotelRoute[stoppedHotels] = *possibleStop;
lastHotelMinutes = possibleStop->minutes;
// springt in der Schleife zurück zum Hotel, an dem gestoppt wurde
currentHotel = possibleStop;
++stoppedHotels;
}
// Wenn die Bewertung hoch genug ist, kann gestoppt werden.
if(currentHotel->rating >= rating) possibleStop = currentHotel;
}
--rating;
/* Schleife läuft solange "rating" mindestens Eins ist, da es keine niedrigeren Bewertungen
* gibt. Wenn das letzte Hotel höchtens 360 Minuten vom Zielort entfernt ist, wurde
* erfolgreich eine Route gefunden und die Schleife wird beendet. Allerdings wird dies nur
* überprüft, wenn überhaupt an einem Hotel gehalten wurde, um den Zugriff auf nicht
* definierten Speicher zu vermeiden.*/
} while(rating && (!stoppedHotels || totalMinutes - hotelRoute[stoppedHotels - 1].minutes > 360));
}
```

85
a4-Wuerfelglueck/Cargo.lock generated Normal file
View File

@ -0,0 +1,85 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "a4-Wuerfelglueck"
version = "0.1.0"
dependencies = [
"rand",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
[[package]]
name = "ppv-lite86"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3ca011bd0129ff4ae15cd04c4eef202cadf6c51c21e47aba319b4e0501db741"
[[package]]
name = "rand"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [
"rand_core",
]
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"

View File

@ -0,0 +1,9 @@
[package]
name = "a4-Wuerfelglueck"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.8.4"

View File

@ -0,0 +1,5 @@
# Würfelglück
## Lösungsidee

View File

@ -0,0 +1,260 @@
use std::env;
use std::fs;
use std::vec;
use rand::distributions::{Distribution, Uniform};
use std::collections::HashSet;
// Struktur für die Würfel
struct Dice {
wins: u32,
defeats: u32,
sides: vec::Vec<u8>,
// Iterator über Zufallszahlen von der "rand" Bibliothek
rand_iter: rand::distributions::DistIter<Uniform<usize>, rand::rngs::ThreadRng, usize>,
different_sides_count: u8 // wird benötigt, um festzustellen, ob ein Spieler stecken geblieben ist
}
/* Struktur, die das Spielfeld darstellt. Die Member b, destination und pieces sind 2er-Arrays von
* den eigentlichen Arrays (Die Elemente von b sind kein Array. Für diese gilt allerdings das
* gleiche.) Das Array mit dem Index 0 gehört jeweils zu Spieler 0 und das andere zu
* Spieler 1. Diesen Spielern sind keine Farben zugeordnet. Wichtig ist nur, dass sie gegenüber
* liegen. */
struct Field {
b: [u8; 2], // Anzahl der Figuren auf den B Feldern
/* Laufbahn: Feld A von Spieler 0 ist bei Index 0 und von Spieler 1 bei Index 20.
* 0 und 1 repräsentieren die Spieler und -1 ein leeres Feld. */
main: [i8; 40],
destination: [[i8; 4]; 2], // Zielfelder
/* Indexe der Figuren: 0-39 für die Laufbahn. -1 ist das Zielfeld, das man am schnellsten
* erreicht und -4 das Letzte. -5 ist eine Figur auf Feld B. */
pieces: [[i8; 4]; 2]
}
impl Field {
// Methode, die die Figuren laufen lässt. piece ist der Index im piece Array.
fn step(&mut self, turn: usize, piece: usize, steps: u8) -> bool {
let i_start = self.pieces[turn][piece]; // Feld auf dem die Figur momentan steht
let i_end = if i_start >= 0 {
// Feld auf das Figur nach dem Zug steht, solange es keine Besonderheiten gibt.
let i = i_start + steps as i8;
if turn == 0 {
let end = self.main.len() as i8 - 1;
// Sollte i abseits der Laufbahn liegen, so läuft Spieler 0 ins Ziel.
if i > end {end - i} else {i}
}
else {
if i >= 20 && i_start < 20 {19 - i} // Nach Index 19 ist für Spieler 1 das Ziel.
// Nach Index 39 muss Spieler 1 wieder bei Index 0 anfangen.
else if i >= self.main.len() as i8 {i - self.main.len() as i8} else {i}
}
}
else if i_start == -5 {(20 * turn) as i8} // startet die Figur auf einem B-Feld, muss sie auf Feld A.
/* ist die Figur bereits auf einem Zielfeld, muss sie in negativer Richtung laufen, da die
* Zielfelder Indexe auch negativ sind. */
else {i_start - steps as i8};
/* Würde die Figur außerhalb des Spielfelds laufen oder das Feld wird von einer eignen
* Figur blockiert, kann sie nicht laufen. */
if i_end < -4 || *self.at(turn, i_end) == turn as i8 {
return false;
}
self.beat(turn, i_end); // schlägt mögliche gegnerische Figuren
// setzt die Figur an die neue Position
self.pieces[turn][piece] = i_end;
if i_start == -5 {self.b[turn] -= 1;}
else {*self.at(turn, i_start) = -1;}
*self.at(turn, i_end) = turn as i8;
return true;
}
// Methode, die gegnerische Figuren schlägt, wenn eine an der angebenen Position steht.
fn beat(&mut self, turn: usize, position: i8) {
let not_turn = other_player(turn);
if position >= 0 && self.main[position as usize] == not_turn as i8 {
for piece in self.pieces[not_turn].iter_mut() {
if *piece == position {
*piece = -5;
self.b[not_turn] += 1;
}
}
}
}
// Methode, welche eine Referenz zu der Figur mit dem entsprechenden Index zurückgibt.
fn at(&mut self, player: usize, position: i8) -> &mut i8 {
if position >= 0 {&mut self.main[position as usize]}
else {&mut self.destination[player][(-position - 1) as usize]}
}
// sucht eine Figur mit der entsprechenden Position und gibt den piece Array Index zurück.
fn field_i_to_piece_i(&self, player: usize, position: i8) -> Option<usize> {
for (i, piece) in self.pieces[player].iter().enumerate() {
if *piece == position {return Some(i);}
}
return None;
}
}
fn main() {
let runs = 100000; // Anzahl der Spiele, die für jede mögliche Würfelkombination simuliert wird
for filepath in env::args().skip(1) { // for-Schleife, die über Dateipfade der Eingabedateien iteriert
let mut dices: vec::Vec<Dice>;
let content = fs::read_to_string(&filepath).unwrap(); // Eingabedatei wird gelesen.
let mut line_iter = content.lines(); // Iterator über die Zeilen der Datei
let mut no_move_possible: [HashSet<u8>; 2] = [HashSet::with_capacity(20), HashSet::with_capacity(20)];
// Speicher für die Würfel wird reserviert.
dices = vec::Vec::with_capacity(line_iter.next().unwrap().parse().unwrap());
for line in line_iter {
let mut dice: Dice;
{
let mut dice_sides = line.split(" "); // Die Zahlen für einen Würfel werden mit einem Leerzeichen getrennt.
let dice_sides_count = dice_sides.next().unwrap().parse().unwrap();
dice = Dice {
wins: 0,
defeats: 0,
sides: vec::Vec::with_capacity(dice_sides_count),
/* Zufallszahleniterator wird erstellt, der Zahlen von 0 (inklusive) bis zur
* Anzahl der verschieden Seiten des Würfels (exklusive) generiert. */
rand_iter: Uniform::new(0, dice_sides_count).sample_iter(rand::thread_rng()),
different_sides_count: 0
};
for side in dice_sides {
// Die Augenzahlen des Würfels werden im einen Vector gespeichert.
dice.sides.push(side.parse().unwrap());
}
// bestimmt die Anzahl der verschiedenen Augenzahlen
{
let mut different_sides_count = 0;
let mut last_side: Option<u8> = None;
for side in dice.sides.iter() {
if last_side != Some(*side) {different_sides_count += 1;}
last_side = Some(*side);
}
dice.different_sides_count = different_sides_count as u8;
}
}
// der Würfel tritt gegen alle bereits verarbeiteten an.
for dice_other in dices.iter_mut() {
let mut selected_dices = [&mut dice, dice_other]; // der Würfel mit dem Index 0, ist auch Spieler 0 und der andere Spieler 1.
for begin in [0, 1] { // das Spiel wird von beiden Spieler gleich oft angefangen
for _ in 0..runs {
let mut turn = begin;
let mut field = Field { // Spielfeld in der Ausgangsstellung
b: [4; 2],
main: [-1; 40],
destination: [[-1; 4]; 2],
pieces: [[-5; 4]; 2],
};
let mut win = false;
while !win { // Schleife solange niemand gewonnen hat.
// würfelt mittels Zufallsgenerator.
let dice_result = selected_dices[turn].sides[selected_dices[turn].rand_iter.next().unwrap()];
let field_0_blocked = field.main[turn * 20] == turn as i8;
// Wenn eine Figur gezogen wurde
if field.b[turn] != 0 && (field_0_blocked && // ist Feld A blockiert ...
// ... wird das Feld freigeräumt.
field.step(turn, field.field_i_to_piece_i(turn, (turn * 20) as i8).unwrap() , dice_result) ||
// Bei einer 6 wird eine neue Figur herrausgezogen.
dice_result == 6 && field.step(turn, field.field_i_to_piece_i(turn, -5).unwrap(), dice_result)) || {
let mut result = false;
// die Figuren werden sortiert; die nah am Ziel sind kommen zuerst.
field.pieces[turn].sort_by(|a, b| {
let mut values = [*a, *b];
if turn == 1 {
for value in values.iter_mut() {
if *value >= 0 && *value < 20 {*value += 40;}
}
}
values[1].partial_cmp(&values[0]).unwrap()
});
// Es wird versucht die Figuren zu ziehen.
let pieces_turn = field.pieces[turn];
for (i, position) in pieces_turn.iter().enumerate() {
if *position == -5 {break;}
if field.step(turn, i, dice_result) {result = true; break;}
}
result
} {
// ein Spieler hat gewonnen wenn alle seine Figuren im Ziel sind.
win = true;
for piece in field.destination[turn] {
win &= piece == turn as i8;
}
if win { // hat jemand gewonnen, werden die Sieges- und Niederlagenzähler erhöht.
selected_dices[turn].wins += 1;
selected_dices[other_player(turn)].defeats += 1;
}
/* Da wieder eine Figur gezogen wurde, muss der HashSet mit den
* Augenzahlen bei denen nicht gezogen werden kann, zurückgesetzt
* werden. */
for map in no_move_possible.iter_mut() {
map.clear();
}
}
else {
/* Konnte keine Figur gezogen werden, wird die Augenzahl in einem
* HashSet abgespeichert. */
no_move_possible[turn].insert(dice_result);
/* Kann bei keiner möglichen Augenzahl gezogen beider Spieler
* gezogen werden, ist das Spiel unentschieden. */
if no_move_possible[turn].len() as u8 == selected_dices[turn].different_sides_count &&
no_move_possible[other_player(turn)].len() as u8 == selected_dices[other_player(turn)].different_sides_count {
break;
}
}
turn = other_player(turn); // beim nächsten Zug ist der andere Spieler dran.
}
}
}
}
dices.push(dice);
}
// sotiert die Würfel nach Gewinnen
dices.sort_unstable_by(|a, b| b.wins.partial_cmp(&a.wins).unwrap());
// gibt das Ergebnis aus
let games_per_dice = ((dices.len() - 1) * runs * 2) as u32;
println!("{}:", filepath);
for (i, dice) in dices.iter().enumerate() {
let ties = games_per_dice - dice.wins - dice.defeats;
println!(concat!("{i}. Würfel: Würfelseiten: {würfelseiten:?}, ",
"Siege: {siege}, Siegwahrscheinlichkeit: {siegwahrscheinlichkeit}, ",
"Niederlagen: {niederlagen}, Niederlagewahrscheinlichkeit: {niederlagewahrscheinlichkeit}, ",
"Unentschieden: {unentschieden}, Unentschiedenwahrscheinlichkeit: {unentschiedenwahrscheinlichkeit}"),
i = i + 1, würfelseiten = dice.sides,
siege = dice.wins, siegwahrscheinlichkeit = dice.wins as f64 / games_per_dice as f64,
niederlagen = dice.defeats, niederlagewahrscheinlichkeit = dice.defeats as f64 / games_per_dice as f64,
unentschieden = ties, unentschiedenwahrscheinlichkeit = ties as f64 / games_per_dice as f64
);
}
}
}
// Funktion, um den anderen Spieler zu erhalten
fn other_player(player: usize) -> usize {
if player == 0 {1} else {0}
}