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

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