Einstieg - Simulation eines Roboters
Der Roboter baut mit Ziegeln
Bisher kann der Roboter nur in seiner Welt herumlaufen. Die Fähigkeiten des Roboters sollen jetzt so erweitert werden, dass er in seiner Welt auch mit Ziegeln bauen kann.
Wenn ein Programm zur Simulation eines Roboters eingesetzt werden soll, dann muss es die Daten der Roboterwelt erfassen und Vorgänge in der Roboterwelt simulieren können. Die Roboterwelt mit bestimmten Gegenständen (Roboter, Ziegel, ...) und Vorgängen (Schritt vorwärts gehen, Ziegel aufheben, ...) muss also im Programm geeignet erfasst werden.
Aufgabe 1
Das bisher entwickelte Robotersystem wird von zwei Objekten dargestellt, einem Objekt rob
zur
Verwaltung des Roboters und einem Objekt welt
zur Verwaltung der Weltdaten.
Im weiterentwickelten Robotersystem (mit den Ziegeln) muss verwaltet werden, in welchen Feldern wie viele
Ziegel liegen. Zudem müssen Operationen wie Ziegel hinlegen
und
Ziegel aufheben
ausgeführt werden können.
Welches Objekt wird wohl für welche Aufgabe zuständig sein?
Verwaltung der Ziegel
Die Verwaltung der in der Welt befindlichen Ziegel soll vom Welt
-Objekt welt
übernommen werden.
Für jedes Feld muss sich das Objekt welt
dann merken, wie viele Ziegel dort abgelegt sind.
Das Objekt welt
benötigt hierzu ein zusätzliches Attribut ziegel
, mit dem alle diese
Anzahlen erfasst werden. Die vom Attribut ziegel
verwaltete Datenstruktur ist eine
Reihung von Reihungen von Zahlen, die mit Hilfe von Listen dargestellt werden kann. Hier soll eine Darstellung
benutzt werden, die die oben gezeigte Welt so beschreibt:
{welt: felderX -> 4, felderY -> 4, ziegel -> [[0, 1, 2, 3], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] }
Die erste Liste [0, 1, 2, 3]
beschreibt die Anzahl der Ziegel in den Feldern
der hintersten Felderreihe - das sind die mit den Koordinaten (0, 0), (1, 0), (2, 0), (3, 0).
Entsprechend beschreibt die zweite Liste [0, 0, 0, 0]
die Anzahl der Ziegel in den Feldern
der davorliegenden Felderreihe - das sind die mit den Koordinaten (0, 1), (1, 1), (2, 1), (3, 1), usw..
Aufgabe 2
Wie sieht die folgendermaßen beschriebene Welt aus?
{welt: felderX -> 4, felderY -> 4, ziegel -> [[1, 1, 1, 1], [1, 0, 0, 1], [1, 0, 0, 1], [1, 1, 1, 1]] }
Neue Operationen der Klasse Welt
Um Ziegel in einem Feld der Welt hinzufügen oder wegnehmen zu können, benötigt man geeignete Methoden.
Zusätzlich benötigt man Zugriffsmethoden, um die jeweilige Anzahl von Ziegeln in einem Feld
ermitteln zu können.
Zu diesem Zweck soll ein Welt
-Objekt über folgende Methoden verfügen:
incZiegel(x: int, y: int)
:
vorher: Das Welt
-Objekt beschreibt einen Weltzustand.
nachher: Das Welt
-Objekt beschreibt einen Weltzustand, bei dem die Anzahl der Ziegel
im Feld mit den Koordinten (x, y) inkrementiert worden ist (d. h.
um 1 erhöht worden ist).
decZiegel(x: int, y: int)
:
vorher: Das Welt
-Objekt beschreibt einen Weltzustand.
nachher: Das Welt
-Objekt beschreibt einen Weltzustand, bei dem die Anzahl der Ziegel
im Feld mit den Koordinten (x, y) dekrementiert worden ist (d. h.
um 1 vermindert worden ist - sofern diese Anzahl vorher größer als 1 war).
getZiegel(x: int, y: int): int
:
vorher: Das Welt
-Objekt beschreibt einen Weltzustand.
nachher: Das Welt
-Objekt beschreibt denselben Weltzustand. Die Methode liefert die Anzahl der Ziegel
im Feld mit den Koordinten (x, y) als Wert zurück.
getAlleZiegel(x: int, y: int): list
:
vorher: Das Welt
-Objekt beschreibt einen Weltzustand.
nachher: Das Welt
-Objekt beschreibt denselben Weltzustand. Die Methode liefert die gesamte
Liste mit allen Ziegelanzahlen als Wert zurück.
Klassendiagramm zur Klasse Welt
Insgesamt ergibt sich so das folgende neue Klassendiagramm:
Implementierung der Klasse Welt
Die folgende Klassendeklaration in Python implementiert die Klasse Welt
des gezeigten Klassendiagramms.
class Welt(object): def __init__(self, x, y): self.felderX = x self.felderY = y # Erzeugung der Listen l = [] for i in range(self.felderY): m = [] for j in range(self.felderX): m = m + [0] l = l + [m] self.ziegel = l def getFelderX(self): return self.felderX def getFelderY(self): return self.felderY def incZiegel(self, x, y): self.ziegel[y][x] = self.ziegel[y][x] + 1 def decZiegel(self, x, y): if self.ziegel[y][x] > 0: self.ziegel[y][x] = self.ziegel[y][x] - 1 def getZiegel(self, x, y): return self.ziegel[y][x] def getAlleZiegel(self): return self.ziegel
Aufgabe 3
(a) Welche zusätzliche Aufgabe wird hier vom Konstruktor __init__
übernommen?
(b) Teste die Klassendeklaration durch ein Python-Protokoll wie das folgende.
>>> welt = Welt(4, 4) >>> welt.getAlleZiegel() [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] >>> welt.incZiegel(2, 1) >>> welt.getAlleZiegel() [[0, 0, 0, 0], [0, 0, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
Erweiterung der Klasse Roboter
Bisher kann der Roboter nur in seiner Welt herumlaufen. Die Fähigkeiten des Roboters sollen jetzt so erweitert werden, dass er Ziegel in die Welt hinlegen und wieder aufheben kann. Das folgende Klassendiagramm zeigt die hierzu erforderlichen neuen Methoden.
ziegelHinlegen()
:
vorher: Das Roboter
-Objekt befindet sich irgendwo in der Welt.
nachher: Das Roboter
-Objekt befindet sich im selben Feld. Wenn das Roboter
-Objekt
nicht vor einer Wand steht, dann hat es in dem vor ihm liegenden Feld einen Ziegel abgelegt.
ziegelAufheben()
:
vorher: Das Roboter
-Objekt befindet sich irgendwo in der Welt.
nachher: Das Roboter
-Objekt befindet sich im selben Feld. Wenn das Roboter
-Objekt
nicht vor einer Wand steht, dann hat es in dem vor ihm liegenden Feld einen Ziegel wegenommen, sofern hier
Ziegel gelegen haben.
vorZiegel(): bool
vorher: Das Roboter
-Objekt befindet sich irgendwo in der Welt.
nachher: Das Roboter
-Objekt befindet sich im selben Feld der Welt. Die Methode liefert
den Wert True
bzw. False
zurück, sofern sich im Feld unmittelbar vor dem
Roboter
-Objekt Ziegel befinden bzw. nicht befinden.
nichtVorZiegel(): bool
analog
Implementierung der Klasse Roboter
Die folgende Klassendeklaration in Python implementiert die Klasse Roboter
des gezeigten Klassendiagramms.
class Roboter(object):
# ... wie bisher ...
def ziegelHinlegen(self):
if self.r == 'O' and self.x < self.w.getFelderX()-1:
self.w.incZiegel(self.x+1, self.y)
elif self.r == 'S' and self.y < self.w.getFelderY()-1:
self.w.incZiegel(self.x, self.y+1)
elif self.r == 'W' and self.x > 0:
self.w.incZiegel(self.x-1, self.y)
elif self.r == 'N' and self.y > 0:
self.w.incZiegel(self.x, self.y-1)
def ziegelAufheben(self):
if self.r == 'O' and self.x < self.w.getFelderX()-1:
self.w.decZiegel(self.x+1, self.y)
elif self.r == 'S' and self.y < self.w.getFelderY()-1:
self.w.decZiegel(self.x, self.y+1)
elif self.r == 'W' and self.x > 0:
self.w.decZiegel(self.x-1, self.y)
elif self.r == 'N' and self.y > 0:
self.w.decZiegel(self.x, self.y-1)
# ... def vorZiegel(self): ...
# ... def nichtVorZiegel(self): ...
Aufgabe 4
(a) Analysiere die Implementierung der Methode ziegelHinlegen
. Erkläre, wie das Ziegelhinlegen
hier softwaretechnisch gelöst wird.
(b) Teste die Klassendeklaration durch ein Python-Protokoll wie das folgende.
>>> welt = Welt(4, 4) >>> rob = Roboter() >>> rob.setWelt(welt) >>> welt.getAlleZiegel() [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] >>> rob.schritt() >>> rob.schritt() >>> rob.ziegelHinlegen() >>> welt.getAlleZiegel() [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [1, 0, 0, 0]]
(c) Entwickle ein Testprogramm, bei dem der Roboter eine Ziegelreihe an den Rand der Welt baut.
(d) Ergänze eine Implementierung der Methoden vorZiegel
und nichtVorZiegel
.