AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Wassersimulation.

Ein Thema von Student2022 · begonnen am 9. Dez 2022 · letzter Beitrag vom 15. Dez 2022
Antwort Antwort
Seite 1 von 2  1 2   
Student2022

Registriert seit: 9. Dez 2022
1 Beiträge
 
#1

Wassersimulation.

  Alt 9. Dez 2022, 23:25
Moin für die Uni, sollen wir folgende Aufgabe erstellen ( es dürfen keine breaks, exits oder goto Anweisungen verwendet werden, zudem auch keine Records oder Zeiger, da wir dies noch nicht behandelt haben.) :


Eine einfache Wassersimulation soll*von Euch erstellt werden: Auf einem quadratischen Feld platziert der Nutzer beliebig viele Steine, wobei dies nur auf noch freien Positionen möglich ist. Ist die Platzierung abgeschlossen, breitet sich von einer ebenfalls vom Nutzer einzulesenden Position aus Wasser auf dem gesamten Feld aus - ggf. aufgehalten bzw. umgelenkt von den gesetzten Steinen.
Hier findet Ihr zum besseren Verständnis ein kurzes Video mit einem Beispielablauf des Programms.
Das Feld wird dabei immer wieder neu mit allen bereits gesetzten Steinen in der Konsole ausgegeben, ist am Anfang also noch komplett leer. Der Nutzer gibt dann in einer Schleife erst die Spalte und danach die Zeile (jeweils ab 1) an (in zwei getrennten Eingaben), in die er den nächsten Stein setzen möchte. Ist die Angabe gültig (und nicht z.B. ein Buchstabe/String statt einer Zahl, eine zu große/kleine Zahl oder der leere String) und bezeichnet eine noch freie Zelle, wird der entsprechende Stein in der gewählten Zelle platziert. Andernfalls erscheint eine möglichst genaue Fehlermeldung direkt nach der fehlerhaften Eingabe.
Danach wird das Feld mit der neuen Belegung ausgegeben. Dies wiederholt sich solange, bis der Nutzer mit x/X abbricht. Dann wird zum Schluss noch die Startposition der Wasserausbreitung eingelesen, wobei hier nur genau eine möglich ist. Das Einlesen erfolgt wie zuvor: Spalte und Zeile getrennt, nur eine gültige und noch freie Position wird angenommen, ansonsten muss die Eingabe wiederholt werden. Ein Abbruch mit x/X ist auch hier möglich, dann erfolgt keine weitere Ausgabe des Feldes. Ansonsten wird das Wasser - schrittweise animiert - über das Feld ausgebreitet, bis alle erreichbaren Positionen geflutet sind. Dann bleibt die Konsole noch so lange offen bis der Nutzer sie durch Tastendruck schließt.

Anforderungen

Zunächst sind verschiedene Konstanten und Typen anzulegen:
eine Konstante FIELDSIZE für die Feldbreite bzw. -höhe (das Feld ist also immer quadratisch). Initial könnt Ihr diese auf 6 setzen, andere Größen sollen aber auch möglich sein!
einen Teilbereichstyp TSize für die somit definierte Breite bzw. Höhe (von 1 bis zu der Konstanten)
ein Aufzählungstyp TState für die Zustände einer Zelle des Feldes mit den logischen Werten "leer", "Stein", "Neues Wasser" (siehe Hilfe) und "Wasser" (verwendet hier sinnvolle, einheitliche Bezeichner, siehe Hilfe)
ein 2D-Array TField in Breite und Höhe entsprechend dem Teilbereichstyp mit dem Aufzählungstyp als Basistyp

Danach benötigt Ihr natürlich auch Variablen von den angelegten Typen, konkret mindestens eine vom Array und zwei vom Teilbereichstypen, um damit durch das Array zu laufen (denkt dran: ein Typ ist nur eine abstrakte Beschreibung einer Datenstruktur, ihm kann nichts zugewiesen werden!).
Die Variablen dürfen dabei nur direkt vor dem Hauptprogramm deklariert werden, globale Variablen (also solche, die über den Funktionen/Prozeduren deklariert werden), sind nicht erlaubt! Typen und Konstanten hingegen dürfen und sollen gerne zu Beginn des Programms angelegt werden, denn ansonsten sind sie ja in den Funktionen/Prozeduren auch nicht benutzbar.

Prüft an dieser Stelle (indem Ihr einen Abnehmer fragt, wenn Ihr selbst nicht sicher seid), ob die Konstanten und Typen wie gefordert angelegt wurden. Ihr erzeugt Euch ansonsten viel unnötigen Aufwand bzw. müsst ggf. nachbessern, wenn diese später noch geändert werden müssen!

Das Hauptprogramm steuert den gesamten Ablauf, insb. soll sich dort eine Schleife befinden, die solange läuft, bis alle Steine gesetzt wurden oder das Programm abgebrochen wurde. Auch die Ausgaben an den Benutzer sollen soweit möglich hier erfolgen.
Alle sinnvoll auszugliedernden Tätigkeiten sollen in Funktionen bzw. Prozeduren ausgelagert werden. Das Hauptprogramm wird dadurch relativ kurz (in der Musterlösung hat es nur ca. 50 Zeilen).

Folgende Funktionen/Prozeduren sind von Euch zu erstellen. Ihre Signaturen dürfen von Euch nicht verändert werden. Eigene Hilfsroutinen sind zulässig, wenn die hier aufgeführten weiterhin den genannten Zweck erfüllen:
procedure initField(var field : TField);
Initialisiert das Feld leer.
procedure printField(field : TField);
Gibt das Feld in der Konsole aus (siehe Hilfe).
function readPos(var x, y : TSize; var cancel : boolean) : boolean;
Liest vom Nutzer Spalte und Zeile (oder 'x' für Abbruch) für eine Position im Feld ein und validiert diese Eingaben.
In cancel steht danach also, ob der Nutzer mit 'x' abgebrochen hat, die Funktionsrückgabe gibt an, ob x und y eine gültige Koordinate bilden (und nicht außerhalb des Feldes liegen).
Im Fall einer ungültigen Eingabe werden immer Spalte UND Zeile neu eingelesen (diese Funktion wird dann also aus dem Hauptprogramm heraus noch einmal aufgerufen) - also auch dann, wenn die Spaltenangabe vorher eigentlich korrekt war und erst bei der Zeile der Fehler auftrat.
Die Koordinate 1/1 ist ganz oben links im Spielfeld.
function isValidCoord(x, y : integer) : boolean;
Prüft, ob eine Koordinate gültig ist (also entsprechend der Konstanten im Feld liegt).
procedure floodField(field : TField);
Füllt das gesamte Feld schrittweise mit Wasser und ruft zwischendurch immer wieder die Ausgabe des Feldes in der Konsole auf. Es werden alle Positionen geflutet, die nicht einen Stein enthalten bzw. gänzlich von Steinen (und dem Rand) umschlossen sind (siehe Hilfe).
Um die Schritte einzeln wahrnehmen zu können, soll zwischen jeweils zwei Ausgaben die Prozedur Sleep aufgerufen werden. Diese lässt das Programm solange pausieren, wie im Parameter in Millisekunden angegeben wird (Sleep(1000) wartet also 1 Sekunde lang).
Die Ausgabe soll jeweils in der oberen linken Ecke der Konsole erfolgen, so dass sich das Feld quasi immer wieder selbst überschreibt (siehe Hilfe).
Die Werte des Aufzählungstyps im Array sollen bei der Ausgabe jeweils mit einem case als Zeichen '▓' in der passenden Farbe ausgegeben werden (Ihr könnt das Zeichen direkt von hier in den Quellcode kopieren). Eine leere Zelle wird dabei hellgrau dargestellt, ein Stein dunkelgrau und das Wasser blau
Zum Setzen der Textfarbe verwendet bitte die Prozedur setTextColor aus der Hilfe. Die benötigten Farbwerte sind 7 (hellgrau), 8 (dunkelgrau) und 9 (blau).
Alle Textausgaben zur Information der Spieler wie "Ungültige Angabe" oder "Programm abgebrochen" sollen unter dem Spielfeld ausgegeben werden (siehe Video). Nicht mehr aktuelle Ausgaben sollen dabei jeweils so bald wie möglich "gelöscht" (also durch Leerzeichen überschrieben) werden.
Das Programm darf keinesfalls abstürzen, egal, was der Nutzer eingibt. Auch bei Buchstaben/Strings, zu großen/kleinen Zahlen oder dem leeren String als Angabe für die Spalte oder Zeile muss das Programm also (nach Ausgabe einer Fehlermeldung) normal weiterlaufen!
Hier hilft Euch die vordefinierte Prozedur val weiter (siehe Wichtige Prozeduren und Funktionen).
Achtet besonders bei dieser Aufgabe auf korrekt gesetzte Compilerschalter ($R+ für Bereichsüberprüfungen), denn vor allem beim floodField läuft man sonst schnell mal aus dem Feld hinaus! Denkt weiterhin daran, dass keine Techniken eingesetzt werden dürfen, die in der Übung noch nicht behandelt wurden!
Außerdem sollen an allen sinnvollen Stellen die beiden Teilbereichstypen benutzt werden (und eben z.B. nicht eine for-Schleife von 1 bis SIZE). Dazu sind low und high zu benutzen. Das Programm muss also auch dann noch richtig arbeiten, wenn die Konstante verändert wird!

Hilfe

Grundlegendes zu Arrays und Aufzählungstypen

Im Trainingscamp findet sich natürlich auch ein Kapitel zu Arrays

Einen eigenen Aufzählungstyp baut man gerne angelehnt an die sog. ungarische Notation auf. Dabei erhalten die einzelnen Werte des Aufzählungstyps alle dasselbe, zum zugehörigen Datentyp passende und sprechende Präfix. Beispiel: Ein Aufzählungstyp mit Farbwerten könnte z.B. wie folgt mit dem Präfix "cl" aufgebaut sein: TColor = (clRed, clBlue, clGreen). Dies hat den Vorteil, dass Werte des Aufzählungstyps schon am Namen als solche ersichtlich sind und sich auch die automatische Codevervollständigung mit Strg+Leertaste benutzen lässt.

Ausgabe in der Konsole

In der Konsole kann man eigentlich nur zeichen- bzw. zeilenweise etwas ausgeben. Da wir das Feld aber nicht x-mal untereinanderschreiben wollen (was ggf. auch mies flackern würde), sondern immer wieder an dieselbe Stelle, muss der Cursor vorab wieder ganz oben links in die Konsole platziert werden. Dies ist möglich mit dieser vorgegebenen Prozedur:

//Setzt die Ausgabeposition der Konsole auf die angegebene Koordinate.
//@param
// x,y - zu setzende Position in der Konsole ab 0/0 = oben links
procedure setConsolePosition(x, y: byte);
var
coord: _COORD;
begin
coord.x := x;
coord.y := y;
if SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_H ANDLE), coord) then;
end;


Ihr müsst allerdings NICHT für jedes Zeichen einer Zeile die Prozedur aufrufen! Das "um ein Zeichen nach rechts weitersetzen" erfolgt durch die Ausgabe eines Zeichens automatisch.

Mithilfe der setConsolePosition lässt sich auch die komplette Konsole einmal "löschen", indem man in einer Schleife immer wieder 80 Leerzeichen ausgibt und dies für soviele Zeilen wiederholt, wie gelöscht werden sollen.

Damit diese Prozedur richtig funktioniert, muss die Unit Windows zusätzlich eingebunden werden (mit einem Komma getrennt nach der System.SysUtils) und die Prozedur muss über dem Hauptprogramm stehen.

Die Textfarbe der Konsole lässt sich mit folgender Prozedur einstellen:

//Setzt die Textfarbe der Konsole.
//@param
// color - zu setzender Farbwert
procedure setTextColor(color : word);
begin
if SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HA NDLE), color) then;
end;

Algorithmische Umsetzung
Für das Fluten des Feldes mit Wasser ist offenkundig eine Schleife (oder eine Rekursion) erforderlich, die so lange läuft, bis alle für das Wasser erreichbaren Felder "unter Wasser" sind (bedenkt: Es kann durchaus Positionen geben, die ringsum von Steinen oder dem Rand eingeschlossen sind und so nie erreicht werden können. Die Schleife/Rekursion darf also NICHT solange laufen, bis alle Positionen im Feld entweder Stein oder Wasser sind, denn das wird ggf. nie erreicht!).
Die Ausbreitung soll dabei schrittweise erfolgen: In jedem Berechnungsschritt werden jeweils alle freien Positionen im Feld geflutet, die direkt horizontal oder vertikal an ein Wasserfeld angrenzen. Im folgenden Bild sind mehrere solcher Ausbreitungsschritte dargestellt:


Eine erste Idee könnte hier sein, in zwei Schleifen durch das Feld zu laufen, zu prüfen, ob die aktuelle Position ein Wasserfeld ist und falls ja, alle vier Nachbarfelder (soweit vorhanden und frei) ebenfalls auf "Wasser" zu setzen. Diese Lösung hätte aber das Problem, dass so (je nach Schachtelung der beiden Schleifen) z.B. das gesamte Feld in EINEM EINZIGEN Bearbeitungsschritt gefüllt wird! Geht man konkret z.B. von der obersten linken Position aus, die das Wasserstartfeld darstellt und läuft dann erst spalten- und dann zeilenweise durch das Feld, dann ergibt sich folgender Ablauf:

Position 1/1 wird geprüft: ist Wasser
Position 1/0 wird geprüft: nicht im Feld
Position 2/1 wird geprüft: ist im Feld und wird Wasser
Position 1/2 wird geprüft: ist im Feld und wird Wasser
Position 0/1 wird geprüft: nicht im Feld
Position 2/1 wird geprüft, ist (jetzt auch) Wasser
Position 2/0 wird geprüft: nicht im Feld
Position 3/1 wird geprüft: ist im Feld und wird Wasser
Position 2/2 wird geprüft: ist im Feld und wird Wasser
Position 1/1 wird geprüft: ist im Feld und schon Wasser
Position 3/1 wird geprüft, ist (jetzt auch Wasser)
...

All diese Schritte finden in EINEM Bearbeitungsschritt statt, also EINEM Durchlauf der beiden Schleifen! Dieses Problem lässt sich lösen, indem man nicht nur einen Status im Aufzählungstyp für "Wasser" nutzt, sondern zwei verschiedene, mit denen man unterscheiden kann, ob das Wasser schon vor Beginn der Berechnung an der Position war oder gerade erst im Berechnungsschritt neu hinzugekommen ist. Nur normales (also "altes") Wasser ist dann für die Ausbreitung relevant, "neues" wird zunächst ignoriert. Nachdem die beiden Schleifen komplett durchgelaufen sind (EIN Berechnungsschritt also erfolgt ist), läuft man dann noch einmal über das gesamte Feld und setzt alles "neues" Wasser auf den normalen Wasserzustand.


Testet vor der Abgabe selbst mit verschiedenen Feldgrößen und Eingaben, ob das Programm wie gewünscht funktioniert und ob insbesondere das Ausbreiten des Wassers korrekt klappt!
  Mit Zitat antworten Zitat
Benutzerbild von blawen
blawen

Registriert seit: 30. Nov 2003
Ort: Luterbach (CH)
654 Beiträge
 
Delphi 12 Athens
 
#2

AW: Wassersimulation.

  Alt 10. Dez 2022, 00:04
Ich hoffe nicht, dass Du die Aufgabenstellung von Hand abgeschrieben hast - in dieser Zeit wäre die Aufgabe schon fast gelöst

Und wobei benötigst Du nun konkrete Hilfe? Ich nehme nicht an, dass Du der Community lediglich die Aufgabenstellung zeigen wolltest.
Roland
  Mit Zitat antworten Zitat
Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.685 Beiträge
 
Delphi 11 Alexandria
 
#3

AW: Wassersimulation.

  Alt 10. Dez 2022, 10:08
Moin und willkommen in der DP.
Wobei? Dem ganzen Text konnte ich nicht eine Frage entnehmen.
Gruß vom KodeZwerg
  Mit Zitat antworten Zitat
Benutzerbild von dummzeuch
dummzeuch

Registriert seit: 11. Aug 2012
Ort: Essen
1.463 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#4

AW: Wassersimulation.

  Alt 10. Dez 2022, 10:25
Hm, was war nochmal der übliche Tarif zum Lösen von Übungsaufgaben auf Universitätsniveau hier in der DP? Das war doch ein 3-stelliger Stundensatz, wenn der Fragesteller keine konkreten Fragen stellt, und deutlich günstiger, wenn er zeigt, was er schon selbst gemacht hat und nur noch konkrete Fragen hat. Oder habe ich das falsch in Erinnerung?
Thomas Mueller
  Mit Zitat antworten Zitat
venice2
(Gast)

n/a Beiträge
 
#5

AW: Wassersimulation.

  Alt 10. Dez 2022, 10:42
Wer nicht erkennt das es sich hier um Spam handelt dem ist letztendlich nicht geholfen.
  Mit Zitat antworten Zitat
Benutzerbild von dummzeuch
dummzeuch

Registriert seit: 11. Aug 2012
Ort: Essen
1.463 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#6

AW: Wassersimulation.

  Alt 10. Dez 2022, 11:13
Wer nicht erkennt das es sich hier um Spam handelt dem ist letztendlich nicht geholfen.
Spam? Wo war da die Werbung?
Thomas Mueller
  Mit Zitat antworten Zitat
venice2
(Gast)

n/a Beiträge
 
#7

AW: Wassersimulation.

  Alt 10. Dez 2022, 11:20
Wer nicht erkennt das es sich hier um Spam handelt dem ist letztendlich nicht geholfen.
Spam? Wo war da die Werbung?
Das du für ihn die Arbeit machst?
Zudem ist Spam nicht gleichzusetzen mit Werbung aber es kann ein Teil davon sein.

Geändert von venice2 (10. Dez 2022 um 11:23 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Gausi
Gausi
Online

Registriert seit: 17. Jul 2005
847 Beiträge
 
Delphi 11 Alexandria
 
#8

AW: Wassersimulation.

  Alt 10. Dez 2022, 12:08
Bei der Überschrift und dem ersten Absatz dachte ich "Ui, das könnte kompliziert werden". Die Simulation von Flüssigkeiten ist ja alles andere als trivial.

Aber hier scheint es ja nur um einen einfachen Floodfill-Algorithmus oder etwas Vergleichbares zu gehen, dazu ein "ordentliches" Rahmenprogramm. Sieht ein wenig nach einem "Abschlussprojekt" des Programmierkurses im ersten Semester aus.

Aber keine GoTos? Ne, da bin ich raus. Sowas baue ich aus Prinzip in jede zweite Prozedur ein. Und in den Rest mindestens irgendwo in der vierten Schleifen-Ebene innerhalb eines doppelten Width-Statements ein Exit.
The angels have the phone box.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu
Online

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.109 Beiträge
 
Delphi 12 Athens
 
#9

AW: Wassersimulation.

  Alt 10. Dez 2022, 23:03
Exceptions zur Flusssteuerung wurden nicht ausgeschlossen.
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat
Benutzerbild von Mavarik
Mavarik

Registriert seit: 9. Feb 2006
Ort: Stolberg (Rhld)
4.126 Beiträge
 
Delphi 10.3 Rio
 
#10

AW: Wassersimulation.

  Alt 11. Dez 2022, 15:10
Aber keine GoTos? Ne, da bin ich raus. Sowas baue ich aus Prinzip in jede zweite Prozedur ein. Und in den Rest mindestens irgendwo in der vierten Schleifen-Ebene innerhalb eines doppelten Width-Statements ein Exit.
Genau wie ich und ich dachte das würde heute keiner mehr machen...

Mavarik
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2   

Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:54 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz