C# :: Aufgabe #194
2 Lösungen

Simulation eines Staubsaug-Roboters
Fortgeschrittener - C#
von hollst
- 22.11.2017 um 22:14 Uhr
Ich bin im Haushalt ein ziemlich fauler Hund. Meine Frau weiß das und so hielt sie mich deshalb kürzlich an, wenigstens hin und wieder das Wohnzimmer zu saugen. Nun gut, mir kam die geniale Idee, dafür einen Staubsaug-Roboter zu kaufen. Gesagt, getan. Meine Frau runzelte zwar, aber letztlich war ihr das egal, wichtig war ja nur, dass sie etwas von der Hausarbeit entlastet wird.
Das Gerät überraschte mich von Anfang an. Es fährt immer geradeaus bis es irgendwo anstößt. Dann dreht es sich auf der Stelle in eine zufällige andere Richtung und fährt wieder weiter geradeaus bis zum nächsten Hindernis. Es teilt mir auch sprachlich z. B. mit, wenn der Akku fast leer ist, es jetzt eine Pause machen wird und zur Ladestation fährt. Andermal sagte es, dass sein Hauptspeicher (sprich Staubsammeltüte) voll ist und ich ihn leeren solle. Das übernimmt natürlich meine Frau, denn bei einem solchen mechanischen Eingriff hat sie kein Vertrauen zu mir.
Der Gipfel war aber, als der Roboter mir bei der gestrigen Arbeit mitteilte, dass er fertig sei und im Zimmer der gesamten Fußboden gereinigt ist.
Das ist doch unmöglich, woher will er das wissen. Und "der gesamte Fußboden" geht schon garnicht, denn in die Ecken kommt er ja sowieso nicht. Ich war immer der Meinung, dass er erst fertig zu sein hat, wenn er abgeschaltet wird. Aber Nein sagte meine Frau, das macht er immer so, ich sollte ihn, wie vereinbart war, nur hin und wieder selbst einsetzen und beobachten (beobachtet wird das Gerät bisweil immer nur von unserer Hauskatze).
Ich wurde neugierig und las entgegen meinen Gewöhnheiten auch die Betriebsanleitung. In der Tat stand darin, dass der Roboter anhält, wenn er mehr als 95 % der Fußbodenfläche gesaugt hat.
Ich dachte, nun gut, 95 % ist okay, aber, wie will er das wissen. Da muss schon eine ziemlich clevere Software im Roboter stecken. Prinzipiell kann man zwar die besaugte Fläche bei Kenntnis der Fahrgeschwindigkeit, der Anstoßpunkte und der neuen Fahrtrichtungen berechnen. Die Flächensegmente, die durch das Zufallsprinzip bereits besaugt worden ware, sind entsprechend zu berücksichtigen (abzuziehen). Alles mathematisch recht kompliziert.
Der Gedanke, es statt einer mathematischen Berechnung durch eine informathematische Simulation abzuschätzen, legt auf der Hand. Dies soll daher die Aufgabenstellung sein.
Ausgehend von einer beliebigen Startposition auf einer leeren, rechteckigen Fläche F = LX x LY, mit LX und LY als Seitenlängen des Rechtecks, einer vorgegebenen Fahrgeschwindigkeit V und einer zufälligen Richtungsänderung bei Zusammenstoß mit einem Hindernis (Wand) ist der Anteil besaugter Fläche bzgl. der Gesamtfußbodenfläche wie folgt anzuschätzen:
Auf einem zu Beginn 100 % mit "Staubpixeln" belegten Rechteck wird ein Staubsaug-Roboter mit dem Radius R gesetzt und fährt per Zufall gewählter Richtung staubsaugend geradeaus (Geschwindigkeit V). Bei Berührung einer Wand ändert er zufällig seine Fahrtrichtung. Während der Fahrt werden alle überfahrenen "Staubpixeln" durch "Reinpixel" ersetzt (siehe Abb. 1). Die Fahrt endet, wenn P_okay = 95 % der Rechtecksfläche mit "Reinpixeln" belegt ist.
Das Gerät überraschte mich von Anfang an. Es fährt immer geradeaus bis es irgendwo anstößt. Dann dreht es sich auf der Stelle in eine zufällige andere Richtung und fährt wieder weiter geradeaus bis zum nächsten Hindernis. Es teilt mir auch sprachlich z. B. mit, wenn der Akku fast leer ist, es jetzt eine Pause machen wird und zur Ladestation fährt. Andermal sagte es, dass sein Hauptspeicher (sprich Staubsammeltüte) voll ist und ich ihn leeren solle. Das übernimmt natürlich meine Frau, denn bei einem solchen mechanischen Eingriff hat sie kein Vertrauen zu mir.
Der Gipfel war aber, als der Roboter mir bei der gestrigen Arbeit mitteilte, dass er fertig sei und im Zimmer der gesamten Fußboden gereinigt ist.
Das ist doch unmöglich, woher will er das wissen. Und "der gesamte Fußboden" geht schon garnicht, denn in die Ecken kommt er ja sowieso nicht. Ich war immer der Meinung, dass er erst fertig zu sein hat, wenn er abgeschaltet wird. Aber Nein sagte meine Frau, das macht er immer so, ich sollte ihn, wie vereinbart war, nur hin und wieder selbst einsetzen und beobachten (beobachtet wird das Gerät bisweil immer nur von unserer Hauskatze).
Ich wurde neugierig und las entgegen meinen Gewöhnheiten auch die Betriebsanleitung. In der Tat stand darin, dass der Roboter anhält, wenn er mehr als 95 % der Fußbodenfläche gesaugt hat.
Ich dachte, nun gut, 95 % ist okay, aber, wie will er das wissen. Da muss schon eine ziemlich clevere Software im Roboter stecken. Prinzipiell kann man zwar die besaugte Fläche bei Kenntnis der Fahrgeschwindigkeit, der Anstoßpunkte und der neuen Fahrtrichtungen berechnen. Die Flächensegmente, die durch das Zufallsprinzip bereits besaugt worden ware, sind entsprechend zu berücksichtigen (abzuziehen). Alles mathematisch recht kompliziert.
Der Gedanke, es statt einer mathematischen Berechnung durch eine informathematische Simulation abzuschätzen, legt auf der Hand. Dies soll daher die Aufgabenstellung sein.
Ausgehend von einer beliebigen Startposition auf einer leeren, rechteckigen Fläche F = LX x LY, mit LX und LY als Seitenlängen des Rechtecks, einer vorgegebenen Fahrgeschwindigkeit V und einer zufälligen Richtungsänderung bei Zusammenstoß mit einem Hindernis (Wand) ist der Anteil besaugter Fläche bzgl. der Gesamtfußbodenfläche wie folgt anzuschätzen:
Auf einem zu Beginn 100 % mit "Staubpixeln" belegten Rechteck wird ein Staubsaug-Roboter mit dem Radius R gesetzt und fährt per Zufall gewählter Richtung staubsaugend geradeaus (Geschwindigkeit V). Bei Berührung einer Wand ändert er zufällig seine Fahrtrichtung. Während der Fahrt werden alle überfahrenen "Staubpixeln" durch "Reinpixel" ersetzt (siehe Abb. 1). Die Fahrt endet, wenn P_okay = 95 % der Rechtecksfläche mit "Reinpixeln" belegt ist.
Lösungen:
MainWindow.xaml.cs
C#-Code
MainWindow.xaml
XML-Code

using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; using System.Windows.Threading; namespace WpfStaubsaugRoboterSimulation { /// <summary> /// Interaktionslogik für MainWindow.xaml /// </summary> public partial class MainWindow : Window { static readonly Random rnd = new Random(); private DispatcherTimer timer; private Ellipse roboter; private Point pos; private Vector vector; private int width, height, radius, v; private HashSet<Pixel> cleanPixels; public MainWindow() { InitializeComponent(); } private void buttonSimulate_Click(object sender, RoutedEventArgs e) { gridWrap.UpdateLayout(); if (int.TryParse(textboxWidth.Text, out width) & int.TryParse(textboxHeight.Text, out height) && int.TryParse(textboxRadius.Text, out radius) && int.TryParse(textboxVelocity.Text, out v) && width > 0 && width <= gridWrap.ActualWidth && height > 0 && height <= gridWrap.ActualHeight && radius > 0 && v > 0) { canvasStaub.Width = width; canvasStaub.Height = height; Simulate(); } else { MessageBox.Show("Ungültige Eingabe"); } } private void Simulate() { canvasStaub.Children.Clear(); roboter = new Ellipse() { Width = 2 * radius, Height = 2 * radius, Fill = Brushes.Blue, StrokeThickness = 0 }; int px = rnd.Next(0, 1 + width - 2 * radius); int py = rnd.Next(0, 1 + height - 2 * radius); pos = new Point(px, py); Canvas.SetLeft(roboter, pos.X - radius); Canvas.SetTop(roboter, pos.Y - radius); canvasStaub.Children.Add(roboter); int vx = rnd.Next(0, 1 + width - 2 * radius) - px; int vy = rnd.Next(0, 1 + height - 2 * radius) - py; vector = new Vector(vx, vy); vector.Normalize(); vector *= v; cleanPixels = new HashSet<Pixel>(); if (timer != null) { timer.Stop(); } timer = new DispatcherTimer(); timer.Interval = new TimeSpan(1); timer.Tick += Timer_Tick; timer.Start(); } private IEnumerable<Pixel> GetCleanedPixel() { for (int x = -radius; x <= radius; x++) { double yP = Math.Sqrt(Math.Pow(radius, 2) - Math.Pow(x, 2)); double yN = -yP; int px = (int)(x + pos.X); int pyp = (int)(yP + pos.Y); int pyn = (int)(yN + pos.Y); for (int y = pyn; y <= pyp; y++) { yield return new Pixel(px, y); } } } private void Timer_Tick(object sender, EventArgs e) { int c = cleanPixels.Count; var cleaned = GetCleanedPixel(); foreach (var pixel in cleaned) { cleanPixels.Add(pixel); } double p = ((double)cleanPixels.Count / (width * height)) * 100; textBlock.Text = $"{p.ToString("0.0000")} % gereinigt"; if (p >= 95) { timer.Stop(); return; } pos += vector; if (WallHitten()) { double vx = rnd.Next(0, 1 + width - 2 * radius) - pos.X; double vy = rnd.Next(0, 1 + height - 2 * radius) - pos.Y; vector = new Vector(vx, vy); vector.Normalize(); vector *= v; } if (c != cleanPixels.Count) { Ellipse clean = new Ellipse() { Width = 2 * radius, Height = 2 * radius, Fill = Brushes.White, StrokeThickness = 0 }; Canvas.SetLeft(clean, pos.X); Canvas.SetTop(clean, pos.Y); canvasStaub.Children.Insert(0, clean); } Canvas.SetLeft(roboter, pos.X); Canvas.SetTop(roboter, pos.Y); } private bool WallHitten() { return pos.X <= 0 && vector.X < 0 || pos.X + 2 * radius >= width && vector.X > 0 || pos.Y <= 0 && vector.Y < 0 || pos.Y + 2 * radius >= height && vector.Y > 0; } } class Pixel { public int X { get; set; } public int Y { get; set; } public Pixel(int x, int y) { X = x; Y = y; } public override bool Equals(object obj) { Pixel p = (Pixel)obj; return p.X == X && p.Y == Y; } public override int GetHashCode() { int hash = 13; hash = (hash * 7) + X.GetHashCode(); hash = (hash * 7) + Y.GetHashCode(); return hash; } } }
MainWindow.xaml

<Window x:Class="WpfStaubsaugRoboterSimulation.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfStaubsaugRoboterSimulation" mc:Ignorable="d" Title="MainWindow" Height="350" Width="825" WindowState="Maximized"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="1*"/> </Grid.RowDefinitions> <Grid Grid.Row="0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/> <ColumnDefinition Width="1*"/> </Grid.ColumnDefinitions> <TextBlock Text="Höhe" Margin="10,13,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Column="0"/> <TextBox x:Name="textboxHeight" Margin="44,10,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="50" TextAlignment="Center" Grid.Column="0"/> <TextBlock Text="Breite" Margin="10,13,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Column="1"/> <TextBox x:Name="textboxWidth" Margin="44,10,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="50" TextAlignment="Center" Grid.Column="1"/> <TextBlock Text="Saugradius" Margin="10,13,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Column="2"/> <TextBox x:Name="textboxRadius" Margin="73,10,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="50" TextAlignment="Center" Grid.Column="2"/> <TextBlock Text="Geschwindigkeit" Margin="10,13,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Column="3"/> <TextBox x:Name="textboxVelocity" Margin="102,10,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="50" TextAlignment="Center" Grid.Column="3"/> <TextBlock x:Name="textBlock" Grid.Column="4" HorizontalAlignment="Left" Margin="10,13,0,0" TextWrapping="Wrap" Text="0 % gereinigt" VerticalAlignment="Top"/> <Button x:Name="buttonSimulate" Content="Simulieren" HorizontalAlignment="Right" VerticalAlignment="Top" Grid.Column="5" Margin="0,10,10,0" Click="buttonSimulate_Click"/> </Grid> <Grid x:Name="gridWrap" Grid.Row="1" Margin="10"> <Canvas x:Name="canvasStaub" Background="Gray"/> </Grid> </Grid> </Window>

using System; using System.Collections.Generic; namespace aufgabe__194__romba { public class Class_Romba { public int[][] floor = null; public List<int[]> line_pos = new List<int[]>(); public string string_line_positions = "string_line_positions: not available"; public string string_start_position = "string_start_position: not available"; public string string_floor = "string_floor: not available"; public int[] end_pos = null; public int steps_in_the_line = 0; private int lx, ly, r; private double direction_phi, v; private int[] start_pos; public Class_Romba(int ly, int lx, int r, int[] start_pos, double direction_phi, double v) { this.ly = ly; this.lx = lx; this.r = r; this.direction_phi = direction_phi; this.v = v; this.start_pos = new int[start_pos.Length]; start_pos.CopyTo(this.start_pos, 0); floor = new int[ly][]; for (var iy = 0; iy < ly; iy++) { floor[iy] = new int[lx]; for (var ix = 0; ix < floor[iy].Length; ix++) floor[iy][ix] = 1; } line_pos.Add(start_pos); mark_rombapos(start_pos); } public void calc_line_pos() { int step = 0; bool bo_run = calc_next_pos(step); this.line_pos = new List<int[]>(); if (bo_run) this.line_pos.Add(start_pos); else return; while (bo_run) { step++; bo_run = calc_next_pos(step); } steps_in_the_line = step; } public bool calc_next_pos(int step) { const double v = 1.0; //11/12/2017 geschwindigkeit wird über thread gesteuert double phi = Math.PI * direction_phi / 180.0; int x = (int)(start_pos[1] + v * step * Math.Cos(phi)); int y = (int)(start_pos[0] + v * step * Math.Sin(phi)); int[] new_pos = new int[] { y, x }; bool bo_run = (x >= 0) && (y >= 0) && (x + 2 * r < lx) && (y + 2 * r < ly); if (!bo_run) return false; line_pos.Add(new_pos); clean_floor(new_pos); end_pos = line_pos[line_pos.Count - 1].DeepCopy(); mark_rombapos(new_pos); return true; } private void clean_floor(int[] pos, bool bo_reset = true) { int ym = pos[0] + r, xm = pos[1] + r; //(pos[0], pos[1]) ist linke obere ecke if ((ym < ly) && (xm < lx)) for (var iy = 0; iy < floor.Length; iy++) for (var ix = 0; ix < floor[iy].Length; ix++) { if (bo_reset && (floor[iy][ix] == 2)) floor[iy][ix] = 0; if ((ym - iy) * (ym - iy) + (xm - ix) * (xm - ix) <= r * r) floor[iy][ix] = 2; } } private void mark_rombapos(int[] pos) { clean_floor(pos, bo_reset: false); } } public static class Extensions { public static string Chars(this string s, int chars) { string result = s; while (result.Length < chars) result = " ".ToString() + result; return result; } public static T[][] DeepCopy<T>(this T[][] x) { T[][] result = new T[x.Length][]; for (var i = 0; i < result.Length; i++) { result[i] = x[i].DeepCopy(); } return result; } public static T[] DeepCopy<T>(this T[] x) { T[] result = new T[x.Length]; for (var i = 0; i < result.Length; i++) result[i] = x[i]; return result; } public static int[][] mark_pos(this int[][]floor, int[] pos, int r, int value = 2) { int[][] result = floor.DeepCopy(); try { int ym = pos[0] + r, xm = pos[1] + r; //(pos[0], pos[1]) ist linke obere ecke if ((ym < result.Length) && (xm < result[0].Length)) for (var iy = 0; iy < floor.Length; iy++) for (var ix = 0; ix < result[iy].Length; ix++) if ((ym - iy) * (ym - iy) + (xm - ix) * (xm - ix) <= r * r) result[iy][ix] = value; } catch { }; return result; } public static string string_line(int ab, int anzahl) { string result = string.Empty; for (var i = 0; i < ab; i++) result += " "; for (var i = 0; i < anzahl; i++) result += "-"; return result; } } }

using System; using System.Text; using System.Windows; namespace aufgabe__194__romba { public partial class MainWindow : Window { struct Struct_Setting { public int floor_width; public int floor_depth; public int cleaner_radius; public int cleaner_velocity; public double max_cleanness; } int[][] floor; int[] start_pos = { 10, 30 }; double phi = 180; double clean_procent = 0.0; //setting äquivalenzen (waren zu erst da) int ly = 125, lx = 200, r = 2, v = 10; double max_clean_procent = 95.0; //Don't go around saying the world owes you a living. The world owes you nothing. It was here first. Mark Twain //https://www.brainyquote.com/quotes/mark_twain_102859 Struct_Setting settings = new Struct_Setting(); Random rand_phi = new Random(); Class_Romba clr; bool bo_init = false; public MainWindow() { InitializeComponent(); bo_init = true; reset_settings(); update_settings(); bt_1_Click(null, null); } Random glob_rand = new Random(); private void bt_1_Click(object sender, RoutedEventArgs e) //start { bo_break = false; int ix = glob_rand.Next(r + 1, lx - r - 1); int iy = glob_rand.Next(r + 1, ly - r - 1); start_pos = new int[]{ iy, ix}; phi = 360.0 * glob_rand.NextDouble(); clr = new Class_Romba(ly, lx, r, start_pos, phi, v); floor = clr.floor.DeepCopy(); tb_11.Text = s_floor(floor, bo_numbers: true); clean_procent = 0.0; this.label_phi.Content = "phi: " + (phi.ToString("0.00")).Chars(6); this.label_clean.Content = clean_procent.ToString("0.00").Chars(6) + " %"; bo_free = true; } bool bo_free = false; private void bt_2_Click(object sender, RoutedEventArgs e) //run { if (!bo_free) return; bo_glob_running = true; set_enabling(); //clr.calc_line_pos(); int count_loops = 0; int count_steps = 0; this.gb_1.IsEnabled = false; bool bo_okay = true; int steps; while (!bo_break && bo_okay && (clean_procent < max_clean_procent)) { System.Threading.Thread.Sleep(1000 / settings.cleaner_velocity); run_cont(out steps, out bo_okay); count_loops++; count_steps += steps; } run_cont(out steps, out bo_okay); count_loops++; count_steps += steps; bo_break = false; bo_glob_running = false; set_enabling(); } bool bo_break = false; private void bt_3_Click(object sender, RoutedEventArgs e) //break { bo_break = true; set_enabling(); } private void bt_4_Click(object sender, RoutedEventArgs e) //reset settings { this.reset_settings(); } private void bt_6_Click(object sender, RoutedEventArgs e) { bo_glob_running = true; set_enabling(); int steps; bool bo_okay; run_stepw(out steps, out bo_okay); bo_break = false; bo_glob_running = false; set_enabling(); } #region events private void myUpDownControl_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e) { update_settings(); } #endregion #region methods void update_settings() { if (!bo_init) return; settings.floor_width = (int)this.myUpDownControl.Value; lx = settings.floor_width; settings.floor_depth = (int)this.myUpDownControl1.Value; ly = settings.floor_depth; settings.cleaner_radius = (int)this.myUpDownControl2.Value; r = settings.cleaner_radius; settings.cleaner_velocity = (int)this.myUpDownControl3.Value; v = settings.cleaner_velocity; settings.max_cleanness = (double)this.myUpDownControl4.Value; max_clean_procent = settings.max_cleanness; //to do: intercept misarrangement } void reset_settings() { //int ly = 125, lx = 200, r = 2, v = 10; //int ly = 25, lx = 50, r = 1, v = 10; int ly = 50, lx = 100, r = 1, v = 5; double max_clean_procent = 95.0; if (!bo_init) return; this.myUpDownControl.Value = lx; this.myUpDownControl1.Value = ly; this.myUpDownControl2.Value = r; this.myUpDownControl3.Value = v; this.myUpDownControl4.Value = (int)max_clean_procent; this.ly = ly; this.lx = lx; this.r = r; this.v = v; this.max_clean_procent = max_clean_procent; this.gb_1.IsEnabled = true; this.bt_1_Click(null, null); } private string s_floor(int[][] floor, bool bo_numbers = false) { StringBuilder sb = new StringBuilder(); sb.AppendLine("floor"); int anzahl = 2 * floor[0].Length; if (bo_numbers) anzahl += 2; sb.AppendLine(Extensions.string_line(5, anzahl)); for (var iy = 0; iy < floor.Length; iy++) { if (bo_numbers) sb.Append(iy.ToString().Chars(4) + " "); sb.Append("|"); for (var ix = 0; ix < floor[iy].Length; ix++) { string string_clean = " "; if (floor[iy][ix] == 1) string_clean = ".."; if (floor[iy][ix] > 1) string_clean = "OO"; sb.Append(string_clean); } sb.AppendLine("|"); } sb.AppendLine(Extensions.string_line(5, anzahl)); return sb.ToString(); } private void run_cont(out int steps, out bool bo_okay) { bo_okay = true; tb_11.Clear(); phi = 360.0 * rand_phi.NextDouble(); this.label_phi.Content = "phi: " + phi.ToString("0.00").Chars(6); clr = new Class_Romba(ly, lx, r, start_pos, phi, v); clr.calc_line_pos(); tb_11.Text = s_floor(floor, bo_numbers: true); floor = floor.mark_pos(clr.end_pos, this.r); try { start_pos = clr.line_pos[clr.line_pos.Count - 1].DeepCopy(); if (clr.line_pos.Count == 0) MessageBox.Show("clr.line_pos.Count == 0)"); } catch (Exception ee) { MessageBox.Show(ee.Message); bo_okay = false; } steps = clr.steps_in_the_line; int count_ges = 0, count_clean = 0; for (var iy = 0; iy < floor.Length; iy++) for (var ix = 0; ix < floor[iy].Length; ix++) { floor[iy][ix] = floor[iy][ix] * clr.floor[iy][ix]; count_ges++; if (floor[iy][ix] == 0) count_clean++; } clean_procent = 100.0 * count_clean / count_ges; this.label_clean.Content = clean_procent.ToString("0.00").Chars(6) + " %" ; System.Windows.Forms.Application.DoEvents(); } private void Window_SizeChanged(object sender, SizeChangedEventArgs e) { this.label_window_size.Content = "window__size: " + this.Width.ToString("0.") + " x " + this.Height.ToString("0."); } private void run_stepw(out int steps, out bool bo_okay) { bo_okay = true; steps = 0; bo_okay = true; tb_11.Clear(); bool bo_run = !bo_break; while (bo_run) { phi = 360.0 * rand_phi.NextDouble(); this.label_phi.Content = "phi: " + phi.ToString("0.00").Chars(6); clr = new Class_Romba(ly, lx, r, clr.line_pos[clr.line_pos.Count - 1], phi, 1.0); int line_step = 0; bool bo_run_line = clr.calc_next_pos(line_step); while (bo_run_line) { line_step++; bo_run_line = !bo_break && clr.calc_next_pos(line_step); tb_11.Clear(); for (var iy = 0; iy < floor.Length; iy++) for (var ix = 0; ix < floor[iy].Length; ix++) floor[iy][ix] = floor[iy][ix] * clr.floor[iy][ix]; int[][] marked_floor_line = floor.mark_pos(clr.end_pos, this.r); tb_11.Text = s_floor(marked_floor_line, bo_numbers: true); System.Windows.Forms.Application.DoEvents(); } steps++; int count_ges = 0, count_clean = 0; for (var iy = 0; iy < floor.Length; iy++) for (var ix = 0; ix < floor[iy].Length; ix++) { floor[iy][ix] = floor[iy][ix] * clr.floor[iy][ix]; count_ges++; if (floor[iy][ix] == 0) count_clean++; } clean_procent = 100.0 * count_clean / count_ges; this.label_clean.Content = clean_procent.ToString("0.00").Chars(6) + " %"; bo_run = !bo_break && (clean_procent < settings.max_cleanness); this.label_window_size.Content = "lines: " + steps.ToString("n0"); } bo_glob_running = false; set_enabling(); } bool bo_glob_running = false; private void set_enabling() { if (!bo_glob_running) { this.gb_1.IsEnabled = true; } else { this.gb_1.IsEnabled = false; } } #endregion } } /* Install NuGet Open your Visual Studio. Open your solution/project. Open Tools menu, select Library Package Manager and select Package Manager Console Run the following command "Install-Package Extended.Wpf.Toolkit" nicht vergessen xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" einsetzen vor mc:Ignorable="d" */

<Window x:Class="aufgabe__194__romba.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:aufgabe__194__romba" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" mc:Ignorable="d" Title="the roomba simulation" Height="775" Width="1550" FontFamily="Courier New" SizeChanged="Window_SizeChanged" WindowStartupLocation="CenterScreen"> <Grid> <TabControl x:Name="tabControl" Margin="0" TabStripPlacement="Bottom"> <TabItem Header=" main "> <Grid Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="250"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="75"/> <RowDefinition/> </Grid.RowDefinitions> <Label x:Name="lb_version" Content="version 2017/12/14" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="{DynamicResource {x:Static SystemColors.HotTrackBrushKey}}"/> <TextBox x:Name="tb_11" Grid.Column="1" Margin="5" Grid.Row="1" Text="TextBox" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Background="{DynamicResource {x:Static SystemColors.InfoBrushKey}}" FontWeight="Bold" FontSize="10"/> <StackPanel Grid.Column="1" Margin="5,10,5,5" Orientation="Horizontal"> <Button x:Name="bt_1" Content=" reposition roomba " Grid.Column="1" HorizontalAlignment="Center" Margin="5" VerticalAlignment="Center" Click="bt_1_Click"/> <Button x:Name="bt_2" Content=" run line by line " Grid.Column="1" HorizontalAlignment="Center" Margin="5" VerticalAlignment="Center" Click="bt_2_Click" /> <Button x:Name="bt_6" Content=" run step by step " Grid.Column="1" HorizontalAlignment="Center" Margin="15,5,5,5" VerticalAlignment="Center" Click="bt_6_Click"/> <Label x:Name="label_phi" HorizontalAlignment="Center" VerticalAlignment="Center"/> <Label x:Name="label" Content=" clean: " HorizontalAlignment="Center" VerticalAlignment="Center"/> <Label x:Name="label_clean" Content="0 %" HorizontalAlignment="Center" VerticalAlignment="Center"/> <Button x:Name="bt_3" Content=" break " Grid.Column="1" HorizontalAlignment="Center" Margin="15,5,5,5" VerticalAlignment="Center" Click="bt_3_Click"/> <Button x:Name="bt_4" Content=" reset " Grid.Column="1" HorizontalAlignment="Center" Margin="15,5,5,5" VerticalAlignment="Center" Click="bt_4_Click"/> </StackPanel> <GridSplitter x:Name="gridSplitter" HorizontalAlignment="Stretch" Margin="0" Grid.RowSpan="1" ResizeDirection="Rows" VerticalAlignment="Bottom" HorizontalContentAlignment="Center" VerticalContentAlignment="Stretch" Height="5" Grid.ColumnSpan="2"/> <GridSplitter x:Name="gridSplitter1" Margin="0" Width="5" HorizontalContentAlignment="Right" VerticalContentAlignment="Stretch" Grid.RowSpan="2"/> <GroupBox x:Name="gb_1" Grid.Row="1" Margin="5,5,15,5" Background="{DynamicResource {x:Static SystemColors.ActiveCaptionBrushKey}}" Header="settings" > <StackPanel> <Label Margin="5,5,5,0" Content="floor width (Lx)" FontSize="11" HorizontalAlignment="Left"/> <xctk:IntegerUpDown Name="myUpDownControl" Minimum="1" Value="200" Margin="50,0,50,5" ValueChanged="myUpDownControl_ValueChanged" /> <Label Margin="5,5,5,0" Content="floor depth (Ly)" FontSize="11" HorizontalAlignment="Left"/> <xctk:IntegerUpDown Name="myUpDownControl1" Minimum="1" Value="125" Margin="50,0,50,5" ValueChanged="myUpDownControl_ValueChanged"/> <Label Margin="5,5,5,0" Content="cleaner radius (r)" FontSize="11" HorizontalAlignment="Left"/> <xctk:IntegerUpDown Name="myUpDownControl2" Minimum="1" Value="2" Margin="50,0,50,5" ValueChanged="myUpDownControl_ValueChanged"/> <Label Margin="5,5,5,0" Content="cleaner velocity (v)" FontSize="11" HorizontalAlignment="Left" Name="label_myUpDownControl3"/> <xctk:IntegerUpDown Name="myUpDownControl3" Minimum="1" Value="10" Margin="50,0,50,15" ValueChanged="myUpDownControl_ValueChanged"/> <Label Margin="5,5,5,0" Content="max % cleanness " FontSize="11" HorizontalAlignment="Left" Name="label_myUpDownControl4"/> <xctk:IntegerUpDown Name="myUpDownControl4" Minimum="0" Maximum="100" Value="95" Margin="50,0,50,15" ValueChanged="myUpDownControl_ValueChanged"/> <Label Margin="5,5,5,0" Content="window_size " FontSize="11" HorizontalAlignment="Left" Name="label_window_size"/> </StackPanel> </GroupBox> </Grid> </TabItem> <TabItem Header=" reserve "> <Grid Background="#FFE5E5E5"/> </TabItem> </TabControl> </Grid> </Window>