verdammt, ich kapier es einfach nicht

Fragen zu Tkinter.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo derkai

Wie gesagt ich kenne das Spiel nicht. Meine Frage:

Was ist das Drehinkrement des Panzers? Ich nehme an in Schritten von 60 Grad. Gilt dieses Inkremment auch für die Schussrichtung? Du hast etwas von Bogenschüssen erwähnt. So wie ich das verstanden habe, können Bogenschüsse in einer x-beliebigen Richtung abgefeuert werden.

Hier eine Zeichnug:

http://cid-c8d0cae764f09eef.skydrive.li ... attle_game

Gruss wuf :wink:
Take it easy Mates!
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Hallo derkai,
dachte eigentlich, dass Du verstanden haettest,
dass wir alles andere brauchen als einen noch
umfangreicheren Anforderungskatalog.
(...hatte etwas mehr 'Pythonisches' erwartet...)

Vergiss doch bitte (fast) alles bisherige, und fange
ganz, ganz klein an.

Zuerst einmal auf der Kommandozeile:

Wir brauchen ein 2d Spielfeld,
also sowas:

Code: Alles auswählen

..........
..........
..........
..........
..........
Darauf setzen wir 2 Panzer 'X' und 'O':

Code: Alles auswählen

..........
...X......
..........
.....O....
..........
Diese Panzer wollen wir bewegen.
Nuetzlich waere da ein Mini-Befehlsinterpreter:

Code: Alles auswählen

..........
...X......
..........
.....O....
..........

Enter Command-> O down
Ergebnis:

Code: Alles auswählen

..........
...X......
..........
..........
.....O....

Enter Command->
Die wichtigen Bausteine dazu sind:
1 Klasse Terrain (Logisches Spielfeld)
1 Klasse Ascii-Display fuer die Darstellung
1 Klasse Panzer
1 Klasse Controller (fuer das Befehlsmanagement)

Das sind die ersten (beispielhaften) Schritte, mit denen ich anfangen wuerde.

LG yipyip
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

@ wuf
also die Bewegungsmöglichkeiten sind in 60 Grad schritten. KORREKT.
Die Panzer können aber nur in die Richtung 60 / 300 Grad schiessen.
Also kegelförmig in der Richtung, in deren Front er "schaut". Dies betrifft alle Waffen, die sich im Panzer befinden. Zusätzlich kann man den Turm des Panzers aber noch bewegen. (um 360 Grad) Somit kann man natürlich auch nur die Waffen damit nutzen, die auch im Turm verbaut sind.

@yipyip
ich dachte es wäre für alle hilfreich erst einmal (fast) alle Rahmenbedingungen zu kennen. BLACKJACK sagt ja, wir müssen uns zu aller erst einmal Gedanken über den Aufbau der Logik machen. Dazu sollte man vorab wissen, welche Anorderungen an den Code und deren Interaktion gestellt werden.
Mein Problem liegt ja genau darin, dass ich nicht weiss, wie man das am besten angeht. Daher diese umfangreiche "BERICHT".


@alle
Meine Gedanken gingen dann dahin gehend weiter, dass es wahrscheinlich OOP am besten wäre. Guckst du hier :

Code: Alles auswählen

from math import sqrt


class Hexfeld(object):
	""" 
		Diese Klasse beschreibt ein Hexfeld, dass auf dem Spielbrett dargestellt 
		werden kann.	
	"""

	length = 20
	
	# Array mit den Randpunkten des Hexfeldes beginnend mit
	# dem Punkt (x0,y0) in der linken oberen Eche.
	pts =   [0,0,length,0,length*3.0/2.0, length*sqrt(3)/2.0,length,
                 length*sqrt(3),0,length*sqrt(3),-length/2.0,length*sqrt(3)/2.0]
	
	def __init__(self,  spielbrett,  xpos,  ypos, zeile, feldnr, color="green"):
		""" Konstrukor der Klasse Hexfeld """
		
		self.spielbrett = spielbrett
		self.xpos = xpos	           #: X-Koordinate links oben
		self.ypos = ypos 		   #: Y-Koordinate links oben
		self.zeile = zeile
		self.feldnr = feldnr
		self.color =  color                #: Farbe des Feldes bzw. die ID
		self.printInfo()
		
		self.__pts = [] + self.pts
		for k in range(0, len(self.__pts), 2):
			self.__pts[k]   += self.xpos
			self.__pts[k+1] += self.ypos		

	def draw(self):
		""" Methode zum Zeichnen des aktuellen Hexfeldes """
		self.spielbrett.create_polygon(self.__pts,  outline="black",fill=self.color)


	def printInfo(self):
		""" Methode zum anzeiden der Attribute dieses Hexfeldes """
		print "xpos     : ",  self.xpos
		print "ypos     : ",  self.ypos
		print "Farbe    : ",  self.color
		print "Zeile    : ",  self.zeile
		print "FeldNr.  : ",  self.feldnr
		

Zuletzt geändert von derkai am Freitag 18. Juli 2008, 20:08, insgesamt 2-mal geändert.
imac
20 Zoll
2,4 ghz
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

Code: Alles auswählen

import Tkinter as tk
from Hexfeld import Hexfeld
from random import choice
from math import sqrt

class Spielbrett(tk.Canvas):
	""" Klasse, die das Spielbrett darstellt. Wir erben von Canvas,
	    um bequem zeichnen zu koennen. """

	def __init__(self,  window):
		""" Konstruktor """
		tk.Canvas.__init__(self, window, width=1024, height=768)
		self.pack()
		self.hexList = []
		self.initHexfelder()
		
	def drawHexfield(self):
		""" Methode zum Zeichnen der Hexfelder """
		for h in self.hexList:
			h.draw()
		
	def initHexfelder(self):
		""" Methode zum initialisieren der Hexfelder (Aufbau wie vorher !) """
		ungerade_spalten = 2                                       
		gerade_spalten = 1                                         
		spielfeld = 2
		farbe = ("green","green","green","green","green","green",
					  "brown","green","blue","green")
		#Vorbelegung der Spalten/Zeilen
		xpos = xorg = 20                                            
		ypos = yorg = 10                                            
		xoffset = Hexfeld.length*1.5
		yoffset = sqrt(3)*Hexfeld.length;
		#Platziere den zweidimensionalen Sechseck-Array
		for i in range(spielfeld):
			if i%2 == 0:
				for x in range(ungerade_spalten):
					self.hexList.append(Hexfeld(self,xpos,ypos,i+1,x+1,choice(farbe)))
					xpos = xpos + (xoffset * 2)
				xpos = xorg + xoffset
				ypos = ypos + yoffset / 2
			else :
				for x in range(gerade_spalten):
					self.hexList.append(Hexfeld(self,xpos,ypos,i+1,x+1,choice(farbe)))
					xpos = xpos + (xoffset *2)
				xpos = xorg
				ypos = ypos + yoffset / 2         
imac
20 Zoll
2,4 ghz
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

Code: Alles auswählen

import Tkinter as tk
from Spielbrett import Spielbrett

def main():
	print("Test des Hexfeldes")
	window = tk.Tk()
	
	# Instanz des Spielbrettes erzeugen
	s = Spielbrett(window)
	# ... und die Hexfelder zeichnen
	s.drawHexfield()
	window.mainloop()

	


if __name__ == "__main__":
	main()
	
imac
20 Zoll
2,4 ghz
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

trotzdem möchte ich den Gedanken noch einmal aufgreifen und frage, ob sagen wir mal ein TEIL PROGRAMM, welches die Konstruktion des Panzers übernimmt und jeder Spieler sich somit seinen Panzer zusammen bastelt kann nicht sinnvoll wäre ?

Man könnte vielleicht sagen, es gibt folgende Bauabschnitte :

- Objekt Panzer
- Objekt Bewegung
- Objekt Hexfeld und Spielfläche
- Objekt Waffen
- Objet Schuss,
- Objekt Schaden
- Objekt Hitze

Kai
imac
20 Zoll
2,4 ghz
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Du vermischt wieder Logik und GUI. Das komplette Spiel muss ohne eine einzige Zeile GUI-Code laufen können.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo EyDu

Wäre deine Ausage schon auf die obigen drei Code-Schnipsel anwendbar? So könnte man den bestehenden Code doch einmal auf zwei Klassen 'class Gui' und 'class Logic' aufteilen. Dann würde dies ein wenig Klarheit schaffen was ihr mit dieser Trennung genau meint. Zum Beispiel könnte man die Sechseckdaten-Bildung und diverse Konstanten im Logic-Teil unterbringen. Oder? In der Main-Funktion würde vorerst nur eine Instanz der Klasse 'Logic' erstellt.

Gruss wuf
Take it easy Mates!
BlackJack

Ich würde sagen das kann man nicht auf den gezeigten Code anwenden, weil das eigentlich immer noch alles GUI ist. "Sechseckdaten-Bildung" im Sinne von: Die Koordinaten eines Sechsecks auf einem Canvas erstellen, hat nichts in der Logik zu suchen.

Logik wäre eine Karten-Klasse wo die Felder mit "logischen" Koordinaten angesprochen werden. Man muss sich, wie ich schon ein paar mal geschrieben habe, überlegen, wie man so ein Spielfeld als Datenstruktur repräsentieren kann und wie man Felder absolut und relativ adressieren kann. Als API zum Beispiel so:

Code: Alles auswählen

class Map(object):
    def __init__(self, width, height):
        pass
    
    def __getitem__(self, coordinates):
        pass
    
    def __setitem__(self, coordinates, tile):
        pass
    
    def neighbour_coordinates(self, coordinates):
        pass
Wobei die letzte Methode die Koordinaten der Nachbarfelder eines gegebenen Feldes, zum Beispiel beginnend mit dem Feld im Norden, im Uhrzeigersinn liefert. Und jetzt muss man sich überlegen wie Koordinaten auf so einer Karte aussehen können, und wie man das Feld intern darstellt.

Für eine Karte mit quadratischen Feldern könnte das zum Beispiel so ähnlich wie das hier aussehen:

Code: Alles auswählen

class Tile(object):
    def __init__(self, name):
        self.name = name

DESERT = Tile('Desert')
WOODS = Tile('Woods')

class Map(object):
    DIRECTION_DELTAS = [(0, -1), (1, 0), (0, 1), (-1, 0)]
    
    def __init__(self, width, height, tile=DESERT):
        self.tiles = [[tile] * width for dummy in xrange(height)]
    
    def __getitem__(self, coordinates):
        x, y = coordinates
        return self.tiles[y][x]
    
    def __setitem__(self, coordinates, tile):
        x, y = coordinates
        self.tiles[y][x] = tile

    def neighbour_coordinates(self, coordinates):
        x, y = coordinates
        for delta_x, delta_y in self.DIRECTION_DELTAS:
            new_x = x + delta_x
            new_y = y + delta_y
            if 0 <= new_x < self.width and 0 <= new_y < self.height:
                yield new_x, new_y


def main():
    game_map = Map(10, 10)
    game_map[2, 4] = WOODS
    for x, y in [(2, 4), (3, 2)]:
        print game_map[x, y].name
    print list(game_map.neighbour_coordinates((1, 1)))
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo BlackJack

Erstmals vielen Dank, dass du dir die Mühe genommen hast uns auf eine neue Art der Programmierunglogik einzuführen. Das heisst natürlich nicht das deine Logik die neuest sein muss. Es kommt immer darauf an aus welchem Umfeld dies betrachtet wird. Jemand aus dem Umfeld eines langjährigen IT-Hochschulstudiums und nachträglichem arbeiten in diesem Feld sieht das ganze sicher anderst als jemand der aus dem Umfeld wie einer Schlosserei kommt und anfängt sich für die Programmierung zu interessieren und seine ersten Gehversuche startet. In deinem Code-Schnipsel hat es einiges, welches für mich wirklich neu und sehr lehrreich ist. Wenn einer ein Programm schreibt macht er es auf der Basis was im schon bekannt und vertraut ist. Ich persönlich bin jetzt nicht der Typ der zuerst zum Buch greift um herauszufinden was ist der letzte Stand der neusten Syntaxmöglichkeiten bevor ich versuche ein Programm zu schreiben. Übrigens brauchte es hierfür nicht nur ein Buch sondern gleich mehrere da es immer darauf ankommt wer das Buch geschrieben hat und wie es geschrieben wurde. Vielmehr lerne ich das neuste aus Code-Teilen wie sie hier in diesem Forum präsentiert werden. Wenn da einem etwas nicht klar ist kann man jederzeit direkt zurückfragen sofern man nicht schulmeisterisch in die Leseklause verbannt wird!

Ich frage mich aber ob jedes Programmprojekt auf die Art wie es hier des Öfterens vorgeschlagen angegangen wird. Jeder der einmal zu programmieren beginnt macht es doch irgendwie basierend auf seinem Vorstellungsvermögen. Von der intuitiven Seite aus gesehen gibt es Gottseidank verschiedenen Angehungsweisen ein Problem zu lösen. Die einen machen es auf dem grafischen die andern auf dem abstraken zahlenorientierten mathematischem Weg. Beim 'WETTEN DAS' gab es doch einmal einen Kandidaten der fähig war sich ich über 600 Zahlen in der richtigen Reihenfolge zu merken indem er sich die Zahlen als grafische Symbole einprägte.

Eine kombinierte Problemlösung mit Einbezug von Gui und Logik finde ich persönlich nicht schlecht. Bin euch näturlich immer für gute Tipps und Vorgschläge dankbar. Bei diesem Spielprojekt kann ich nur punktuell eingreifen da ich auf eine Projektbeschreibung wie 'derkai' sie öffentlich machte und die sich sukzessive verfeinern wird angewiesen.

Noch einige Offtopic Fragen und Feststellungen:

a) War am Anfang das Huhn oder das Ei.

b) Erstellte Christoph eine strukturierte Karte bevor er seine Reise ins Ungewisse antrat? Eventuell wären die West-Indies und Amerika bis heute noch nicht entdeckt worden? Hätte vielleicht den Vorteil, dass bestimmte dubiose Leute nicht täglich in der Zeitung und auf den TV-Schirmen zu sehen wären.

c) Da gab es doch auch schon Wettkampf-Projekte an Universitäten bei denen mehreren Studentengruppen je eine Kiste mit mechanischem und elektronischem Gerümpel hinstelle wurde mit dem Ziel, dass jede Gruppe aus diesem Gerümpel einen Roboter zusammenbaute damit zum Abschluss des Projektes ein Fussballspiel mit den Robotern ausgetragen werden konnte. Es wäre sicher interessant gewesen die Problemlösungswege der einzelnen Gruppen mit zu verfolgen.

d) Die Amerikaner hatte doch auch schon einige Rückschläge bei ihrer Mars-Erforschung zu verkraften, wobei der peinlichste Fehler sich bei der Mars Climate Orbiter-Mission ereignete, da die NASA-Mitarbeiter Fehler bei der Umrechnung von Einheiten machten und der Marsboden dann da war, wo er eigentlich nicht sein sollte. Hier hätte er Einbzug einer grafischen Darstellungshilfe vielleicht gezeigt, dass an Stelle einer geraden Linie eine krummer gezeichnet wurde. Was bei der Ausgabe einer Datenflut auf einer Terminal-Konsole nicht auf den ersten Blick auffällt, was ist real und was nicht. Unbekannt ist viel Geld für diese Mission verheizt wurde. Hi.

@derkai Hallo Kai du siehst es gibt da mehrere Wege dein Projekt zu lösen und zu gestalten.

@BackJack Ich werden mich noch näher mit deinem Code-Schnipsel beschäftigen und bei Fragen gerne darauf zurückkommen. Übrigens ich glaube im Konstruktor von Klasse Map fehlt noch etwas:

Code: Alles auswählen

    def __init__(self, width, height, tile=DESERT):
        self.tiles = [[tile] * width for dummy in xrange(height)]
        self.width = width
        self.height = height
OK, Gruss an alle wuf :wink:
Take it easy Mates!
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

ok, ich werde jetzt einmal versuchen von einer "anderen" Seite das Thema zu betrachten. Ohne eine GUI.

Um alle im Spiel stattfindenden Dinge zu ereldigen, dass was also die Logik leisten muss, benötige ich folgende Dinge :

- wahrscheinlich eine Klasse Panzer, von der dann Instanzen der im
Spiel befindlichen Panzer gebildet werden.
Da jeder Panzer unterschiedliche Eigenschaften haben wird, stellt
sich mir nun konkret folgende Frage. Die Spieler sollen die Auswahl
an verschiedenen Panzern haben. Diese liegen dann irgendwo als
Datei rum und werden bei Bedarf eingelesen. Die Datei soll dann die
verschiedenen Eigenschaften der Panzer beinhalten. bsp die unter-
schiedlichen Panzerwerte. Um später gut darauf zugreifen zu können,
wäre ein Dictionary mit Keys sicher gut geeignet.

In meinen schlauen Büchern steht leider nur etwas davon, dass eine
Datei als String gespeichert wird. Dies wäre doch denkbar ungünstig,
oder ? Wie könnte man denn jetzt rein auf die Syntax bezogen eine
solche Datei erstellen ?
- dann würde ich all die Funktionen und Berechnung programmieren,
die im Spiel von Nöten wären.
Rahmendaten würde ich aus der Datei "Panzer" erhalten.
Müßte aber auch gleichzeitig Variablen übergeben, die SPÄTER aus
einer GUI stammen. z.b. die Entfernungen zum Gegner Aber diese Funktion kann ich ja auch so testen.

Was bleibt ist also die Frage :
- wie ich also ein Dictionary erstelle, dass ich speichern, und einlesen kann ? um später daraus eine Instanz einer Klasse zu bilden ?

Dann könnte ich schon einmal richtig gut und ohne Gui starten.

Kai
imac
20 Zoll
2,4 ghz
BlackJack

@wuf: Argh, die beiden Methoden hier sind in meiner lokalen Datei, aber nicht in meinem letzten Beitrag:

Code: Alles auswählen

    @property
    def width(self):
        return len(self.tiles[0])
    
    @property
    def height(self):
        return len(self.tiles)
Es gibt mehrere Gründe aus denen ich bei diesem Projekt eine Vermischung von GUI und Logik für ziemlich fatal halte.

Das Projekt ist relativ gross, dass heisst man profitiert hier von automatisierten Tests, was mit GUIs schwieriger ist als mit abgetrennten Funktionen, die nur die Logik umsetzen.

Wenn das Programm steht und spielbar ist, bieten sich drei nächste Schritte an: Ein Computerspieler, alternative GUIs zum Beispiel mit Pygame, Pyglet, oder vielleicht sogar eine 3D-Darstellung, und die Möglichkeit das Spiel mit anderen über's Netz zu spielen. Alle drei Varianten werden durch das vermischen von GUI und Logik erschwert oder sogar unmöglich. Wenn man *dann* anfängt die Trennung vor zu nehmen, hat man sehr viel Arbeit, die man sich hätte sparen können.

Zu den Offtopic-Fragen:

b) Falls das auf intensive Planung vs. explorative Programmierung abziehlt, ist der Vergleich nicht so passend, weil man von unbekannten Erdteilen keine Karte planen kann. Amerika wurde von Columbus ja nicht konstruiert, sondern entdeckt. Egal was er vorher auf die Karte gemalt hätte, der Kontinent wäre trotzdem da gewesen und auf Grund seiner Grösse schlecht zu übersehen. ;-)

Die dubiosen Leute wären auch ohne die Entdeckung Amerikas bei uns in Zeitung und Glotze, deren Vorfahren wären dann ja hier geblieben. Oder meinst Du irgendwelche dubiosen Indianer a.k.a. "native americans", die ich übersehe? :-)

d) Es gab keinen Umrechnungsfehler, sondern es wurde schlicht nicht umgerechnet. Die Software am Boden gab Anweisungen für Schubdüsen in "pound-force", der Orbiter erwartete Anweisungen in Newton. Um das zu bemerken hätte es keiner Datenflut bedurft, sondern einfach nur einer Anzeige von der aktuellen Position von der die Software ausgeht und der Position der tatsächlichen Flugdaten bzw. der Differenz der beiden Positionen. Das hätte automatisch und ohne grafische Darstellung passieren können. Dieses Versäumnis ist IMHO wesentlich peinlicher als die Verwechslung der Einheiten.
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Hallo derkai,
gerade __WEIL__ Du so einen umfangreichen
Anforderungskatalog erstellt hast, weisst Du nicht, wie
Du anfangen solltest.
Du gehst davon aus, dass man aus einem Anforderungkatalog
die Programmplanung erstellen sollte.
(Dann haette man so etwas wie das Wasserfall-Modell.)

Ich tenderiere jedoch eher zu einem explorativen
bzw. evolutionaeren Ansatz.

So einfach wie moeglich und so flexibel
wie notwendig das Projekt beginnen.

Dieser Ansatz bietet Dir bessere Moeglichkeiten
einerseits Dein Python Wissen zu erweitern
und andererseits die Programmplanung besser zu ueberblicken.

Ich wollte mit obigem Beispiel hauptsaechlich erreichen,
das Du etwas allgemeiner und abstrakter denkst.
:wink:
LG yipyip
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

ich bin davon überzeugt, dass mein Wissen mit dem "Machen" wachsen wird.

Aber ich muss jetzt mal irgendwie und irgendwo beginnen.
Und da erscheint es mir jetzt sinnvoll, erst einmal die Daten eines Panzers bereit zu stellen und verschiede Funktionen / Methoden zur Berechnung usw zu programmieren.

Daher noch mal meine Frage :

ich denke, dass eine Instanz einer Klasse Panzer dies tun sollte.
Aber wie kann ich aus einem z.b. als "IMPORT Modul" eingelesenen Dictionary eine Klasseninstanz machen ?

Kai
imac
20 Zoll
2,4 ghz
BlackJack

@derkai: Um aus einem Dictionary ein Panzer-Exemplar zu machen, musst Du Code schreiben der das tut. Also ein Dictionary mit den Werten nimmt und die `Panzer.__init__()` damit entsprechend aufruft.

Ansonsten möchte ich mich yipyip anschliessen und noch mal auf Planung über "Milestones" hinweisen. Die musst Du auch nicht alle im Voraus planen, es reicht vollkommen, wenn man immer den nächsten im Blick hat. Ich würde an Deiner Stelle deine bisherige Planung nehmen und für den ersten Meilenstein so viel wie möglich raus werfen. Erstmal eine Karte mit quadratischen Feldern und einem Feldtyp, Panzern, die nur einen Panzerungswert und einen Wert für die Waffenstärke haben und nach vorne schiessen. Und dass dann komplett implementieren. Verschiedene Trefferzonen, Feldtypen, Waffen, Wärmetauscher, hexagonale Felder, usw. kann man dann nach und nach einbauen und hat immer nach jedem Teilschritt ein komplett lauffähiges Programm.

So erkennt man Fehlentwicklungen schneller, als wenn einem das erst beim Zusammensetzen von mehreren grossen Codeteilen auffällt. Und die Motivation bleibt auch erhalten. Wenn man erst den kompletten Panzer implementiert und dann immer noch mehrere hundert Zeilen Quelltext vor sich hat, um Karte und GUI zu implementieren, bevor man überhaupt etwas Lauffähiges sieht, kann das sehr frustrierend und abschreckend wirken.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo BlackJack

Ich möchte dein Code-Schnipsel verstehen und werde es Stück um Stückdurcharbeiten. Ich glaube auch das man dieses als 'Kick-Off' für 'Kai's Battle Game' verwenden sollte. Es geht hier offenbar um die Klasse für die Erstellung der Spielkarte.

Ist meine Interpretation für die folgenden Code-Zeilen richtig?:

Code: Alles auswählen

def main():
    game_map = Map(10, 10)

    def __init__(self, width, height, tile=DESERT):
        self.tiles = [[tile] * width for dummy in xrange(height)] 
a) Mit

Code: Alles auswählen

 game_map = Map(10, 10) 
machst du eine Instanz für eine Spielkarte (Map) mit 10x10 Feldobjekten(Tile) des Typs 'DESSERT'.

b) Mit

Code: Alles auswählen

self.tiles = [[tile] * width for dummy in xrange(height)] 
erzeugst du eine liste mit 10 Objekten des Objekt-Typs 'DESSERT'.Man kann sagen das 'width' und 'height' nicht für die Dimension der Karte als Anzahl Felder in diesem Fall 10 * 10 also 100 Feldern (Tiles) angeschaut werden kann da in der Liste 'self.tiles' nur 10 Feld-Objekte (DESSERT), erzeugt durch die List-Comprehension, enthalten sind.

Meine Frage: Warum wird 'width' und 'height' nicht für die Dimension des Spielfeldes verwendet so, dass es eigendlich 100 Feld-Objekte ergibt?

Ich hoffe mich einigermassen verständlich ausgedrückt

Danke für deine Bemühung.

Gruss wuf :wink:

Edit: @BlackJack Sorry ich sehe es sind doch 100 Objekte in der Liste. Angeordnet als 10 * 10 Listen @ 10 width-Objekte.
Take it easy Mates!
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo BlackJack

Habe weitere Fragen:

Die magischen Methoden __getitem__ und __setitem__ sind neu für mich. Gehe ich richtig in der Annahme diese beiden Methoden (scheinbar als 'geter' und 'seter' bekannt) dürfen nur einmal in einer Klasse vorkommen und zwar aus dem Grund weil sie ohne Methode-Bezeichnung aufgerufen werden zum Beispiel mit:

a) instanz_name[] (Ruft einen Wert ab 'geter')
In deinem Code-Schnipsel -> print game_map[x, y].name

b) instanz_name[] = value (Setz etwas mit value 'seter)
In deinem Code-Schnipsel -> game_map[2, 4] = WOODS

Sehr interessante Methoden, welche für mich aber den sonst gut lesbaren Pythoncode ein wenig verschmieren. (gewöhnungsbedürftig sind)

Gruss wuf :wink:
Take it easy Mates!
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

wuf hat geschrieben:Die magischen Methoden __getitem__ und __setitem__ sind neu für mich. Gehe ich richtig in der Annahme diese beiden Methoden (scheinbar als 'geter' und 'seter' bekannt) dürfen nur einmal in einer Klasse vorkommen und zwar aus dem Grund weil sie ohne Methode-Bezeichnung aufgerufen werden
Nein, das sind weder Getter noch Setter - dies wären Funktionen die man selbst schreibt um auf Werte zuzugreifen. So etwas ist eher in Java üblich, in Python würde man einfachen Attributzugriff nutzen und, wenn man die Werte prüfen/errechnen muss dann Properties die eine Art (transparente) Getter/Setter sind (am besten liest du selbst mal dazu nach, denn es scheint mir etwas Sinnfrei zu sein, Beispiele auszudenken, wenn es im Internet schon genug gibt).

Das sind aber insofern magische Methoden, da sie aufgerufen werden, wenn der User Daten via Indexzugriff (``etwas[index]``) auf ein Objekt zugreift. Diese dürfen natürlich auch mehrfach kommen, aber spätere Definitionen überschreiben frühere, daher gibt es von denen effektiv immer nur eine Version :)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hallo zusammen

@Leonidas besten Dank für deine ausführliche und interessante Erklärung. Ich werde mich noch weiter orientieren.

@BlackJack Ich begreife allmählich was du uns schon länger zu erklären versuchst. Einfach genial dein Code-Schnipsel. Ich möchte fast sagen High Level Python Programming! Was ich durch deinen Code neu erfahren durfte sind:

a) Properties
b) List-Comprehensions
c) __getitem__
d) __setitem__
e) generator mit 'yield'

@derkai Hast du das Code-Schnipsel von BlackJack auch schon näher analysiert? Es ist der Weg den du einschlagen musst. Hier habe ich den Code noch mit zusätzlichem Kommentar ergänzt. Ich hoffe diesen richtig interpretiert zu haben:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

#~ Skriptname programm_logic_01_01.py (20.07.2008)

class Tile(object):
    """Klasse:Spielfeld"""

    def __init__(self, name):
        """Konstruktor für die Klasse"""
        self.name = name

#~~ Spielfeld-Instanzen
DESERT = Tile('Desert')
WOODS = Tile('Woods')

class Map(object):
    """Klasse:Spielkarte"""

    #~~ Konstante für die Abfrage der Nachbarfelder
    DIRECTION_DELTAS = [(0, -1), (1, 0), (0, 1), (-1, 0)]

    #~~ Gibt die Anzahl x-Felder (Spalten) zurück
    @property
    def width(self):
        return len(self.tiles[0])

    #~~ Gibt die Anzahl y-Felder (Reihen) zurück
    @property
    def height(self):
        return len(self.tiles)

    def __init__(self, width, height, tile=DESERT):
        """Konstruktor für die Klasse"""

        #~~ Erzeugt eine Feldobjekt-Liste mit der
        #   Anzahl 'width' * 'height' Feldobjekten
        self.tiles = [[tile] * width for dummy in xrange(height)]

    def __getitem__(self, coordinates):
        """Adressiert das Feldobjekt"""
        x, y = coordinates
        return self.tiles[y][x]

    def __setitem__(self, coordinates, tile):
        """Setzt den Feldtyp"""
        x, y = coordinates
        self.tiles[y][x] = tile

    def neighbour_coordinates(self, coordinates):
        """Ermittelt die Koordinaten der Nachbarfelder"""
        x, y = coordinates
        for delta_x, delta_y in self.DIRECTION_DELTAS:
            new_x = x + delta_x
            new_y = y + delta_y
            if 0 <= new_x < self.width and 0 <= new_y < self.height:
                yield new_x, new_y


def main():
    #~~ Erstellt ein Spielfeld mit 10 * 10 Feldobjekten (default-Feldyp = 'Desert'

    SPALTEN = COLUMNS = 10
    REIHEN = ROWS = 10

    #~~Erstellt eine Instanz der Spielfeld-Klasse (Map)
    game_map = Map(SPALTEN, REIHEN)

    print 'Anzahl Spalten',game_map.width
    print 'Anzahl Reihen',game_map.height

    #~~ Ändert den Feldtyp des Feld auf Koordinate x=2,y=4
    #   in ein Feldtyp 'Wood' um
    game_map[2, 4] = WOODS

    #~~ Test: Abfrage des Feldtyp-Namens der Felder auf Koordinate:
    #         a) x=2,y=4 ('Wood')
    #         b) x=3,y=2 ('Desert')
    for x, y in [(2, 4), (3, 2)]:
        print game_map[x, y].name

    print 'Nachbar-Koordinate',list(game_map.neighbour_coordinates((1, 1)))
    #~~ Gibt die Koordinaten der Nachbarfelder des
    #   Testfeldes auf Koordinate x=1,y=1 zurück.
    #
    #   Testfeld     X  x=1,y=1
    #   Nachbarfeld  a  x=1,y=0
    #   Nachbarfeld  b  x=0,y=1
    #   Nachbarfeld  c  x=1,y=2
    #   Nachbarfeld  d  x=2,y=1
    #
    #         0   1   2   3 - x
    #         ------------------
    #     0 |   | a |   |   |
    #         ------------------
    #     1 | b | X | d |   |
    #         ------------------
    #     2 |   | c |   |   |
    #         ------------------
    #     3 |   |   |   |   |
    #         ------------------
    #     x |   |   |   |   |


main()
Gruss wuf :wink:
Take it easy Mates!
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Hallo Blackjack,
kann es sein, dass Du
anstelle von

Code: Alles auswählen

self.tiles = [[tile] * width for dummy in xrange(height)]
eher

Code: Alles auswählen

self.tiles = [[Tile('Desert') for x in xrange(width)] for y in xrange(height)]
meintest?

Im 1.Fall wird ein 2d-Array von identischen
Objekten erzeugt, im 2.Fall unterschiedliche Objekte,
d.h. versucht man ein Feld zu faerben, faerben sich
im 1.Fall alle anderen auch.

:wink:
LG yipyip
Antworten