verdammt, ich kapier es einfach nicht

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

Samstag 19. Juli 2008, 20:12

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: 1420
Registriert: Sonntag 8. Juni 2003, 09:50

Samstag 19. Juli 2008, 22:01

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
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Samstag 19. Juli 2008, 23:18

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 Modvoice
Benutzeravatar
wuf
User
Beiträge: 1420
Registriert: Sonntag 8. Juni 2003, 09:50

Sonntag 20. Juli 2008, 00:03

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

Sonntag 20. Juli 2008, 18:23

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
BlackJack

Sonntag 20. Juli 2008, 20:00

Ich meinte schon was ich geschrieben habe. Die einfachste Lösung, solange niemand auf die Idee kommt, dass verschiedene Felder des gleichen Feldtyps unterschiedliche Werte haben können. Davon ist in der Anforderung aber nichts zu lesen, also warum einen Haufen gleichwertiger Objekte erzeugen, wenn es *eines* auch tut.
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

Sonntag 20. Juli 2008, 20:14

Hallo BlackJack,
hab mir's grade nochmal angeschaut...
stimmt, Du ersetzt die Feldobjekte im nachhinein.

Ich ging von individuellen Feldobjekten aus,
deren Werte man veraendert, anstelle von
Feldern, denen man neue Objekte zuweist.

Geht auch so herum.
(und spart Speicher...)

:wink:
LG yipyip
Benutzeravatar
wuf
User
Beiträge: 1420
Registriert: Sonntag 8. Juni 2003, 09:50

Sonntag 20. Juli 2008, 21:04

Hallo derkai

Ich habe noch weitere Fragen:

Kann ein Spielfeld (Sechseck oder auch Quadrat) noch weitere topografische Eigenschaften haben so zum Beispiel Wasser, Berge usw.? Was für weitere Eigenschaften oder Objekte werden einem Spielfeld sonst noch zugeordnet?
Sicher ein Panzer- oder sonstiges Waffenobjekt, eine Feldnummer usw.

Gruss wuf :wink:
Take it easy Mates!
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

Sonntag 20. Juli 2008, 21:38

na ja, ganz weit entfernte Planungen gehen dahin, dass die Landschaft neben dem "normalen", Wasser und Wald später auch Berge haben soll.
Dies wirft allerdings dann so Problematiken auf, wie ob man den
Gegner überhaupt sehnen kann oder nicht. Daher rückt das noch ganz weit weg.

Ausserdem weit weg sind folgende Gedanken. Es wäre gut, wenn jeder
Spieler zu Beginn des Spiels nur etwa 10 Felder im eigenen Umkreis sehen könnte. Jeder Panzer würde dann einen Sensor besitzen, der eben jene 10 Felder Umkreis betrachten könnte. Jedes Feld, was aber durch die eigenen Bewegung betreten würde (+10 Umkreisfelder) bleiben aber dauerhaft sichtbar, sobald sie einmal vom Panzer erfasst wurden. So würde sich dann erst langsam das gesamte Terrain auftun.

An sonsten soll die Spielfläche erst einmal keine weiteren Obejekte beinhalten.

Helft Ihr mir mal bei meinem OHNE GUI CODE Schnipsel weiter ?

Code: Alles auswählen

import Daten
from math import sqrt

laenge = 20

class Panzer (object):


    def __init__ (self, var) :

        self.var = var
        print self.var



    def vorlage_bewegung (self, feldalt, feldneu) :

        """ dient der Ermittlung der Vorlage durch die
            eigene Bewegung. """

        entfernungx = 0
        entfernungy = 0
        feldmitteneu = 0
        feldmittealt = 0
        

        """ Berechnung der Feldmitten alt + neu """

        feldmittealt = ((feldalt[0]+laenge/2),(feldalt[1]+(laenge*sqrt(3)/2)))
        feldmitteneu = ((feldneu[0]+laenge/2),(feldneu[1]+(laenge*sqrt(3)/2)))


        """ Berechnung der Differenz der x y Mitten der Felder alt + neu """

        print   (feldalt, feldneu)       
        print   (feldmittealt,feldmitteneu)


        """ Ermittlung der Felderdifferenz """

        entfernungx = feldmittealt[0] - feldmitteneu[0]
        entfernungy = feldmittealt[1] - feldmitteneu[1]
        


        print   "X Differenz : ", entfernungx
        print   "Y Differenz : ", entfernungy
        
        print   laenge * sqrt(3) / 2


        """ Ermittlung der tasaechlich bewegten Felder """


    


        

    
Spieler1 = Panzer (Daten.tiger)
Spieler2 = Panzer (Daten.leopard)
Spieler1.var ["pav"] = 20
print Spieler1.var
print Spieler1.vorlage_bewegung((20,10),(110,61.9615242271))




        

imac
20 Zoll
2,4 ghz
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

Sonntag 20. Juli 2008, 21:42

ich dachte mir, ich übergebe ein Tupel bestehend aus x/y Koordinate des
späteren Spielfeldes, bestimme den Mittelpunkt des Hexagon und berechne die Differenz x/y zwischen dem Feld, von welchem die Bewegung aus begann und jenem wo der Panzer das Ende seiner Bewegung findet.

Dies soll dazu dienen, zu ermitteln, wieviele Felder der Panzer zurückgelegt hat. Nun stehe ich aber auf dem Schlauch.

Ich habe die Enfernuung x/y, wie komme ich nun aber zu der Anzahl der zurückgelegten Felder ???

Kai
imac
20 Zoll
2,4 ghz
BlackJack

Sonntag 20. Juli 2008, 22:40

``laenge = 20`` hat in dem Code ohne GUI nichts zu suchen, da geht man einfach von einer Einheit aus.

Was soll die Funktion denn genau leisten? Eine Liste von Feldern, die den kürzesten Weg von Feld A nach Feld B darstellt? Da ist der A*-Algorithmus passend. Gewichtung der Felder ist erst einmal 1, später kannst Du dafür die benötigten Bewegungspunkte für die jeweiligen Feldtypen veranschlagen.
derkai
User
Beiträge: 169
Registriert: Montag 12. Mai 2008, 11:43

Montag 21. Juli 2008, 08:50

die Funktion soll die Entfernung des zurückgelegten Weges in Feldern OHNE Berücksichtigung der Art der Felder berechnen. Das ist gleichzeitig auch der kürzeste Weg. Ich schaue mir den "A" Algorithmus nachher einmal an.
Nach der Anzahl der Felder richtet sich dann die Vorlage duch eigene Bewegung.

laenge = 20 ???

Wie kann ich das denn ohne diese Angabe bewerkstelligen ?
Mir ist klar, dass "laenge = 20" später gar nicht benötigt wird, da
dies aus der GUI stammen wird.
Aber ich muss meinen bisherigen Code doch testen können, oder
wie würdest DU / IHR das vorgehen ?

Kai
imac
20 Zoll
2,4 ghz
BlackJack

Montag 21. Juli 2008, 10:18

Du willst doch keine Entfernung in "Luftlinie" haben, sondern die Anzahl der Felder, die als kürzester Weg zwischen zwei Feldern liegen. Und da zählt ein Feld eben als 1.
Benutzeravatar
wuf
User
Beiträge: 1420
Registriert: Sonntag 8. Juni 2003, 09:50

Montag 21. Juli 2008, 20:41

Hallo derkai

Ich würde mit der Panzer-Klasse.noch warten Wir müssen auf BlackJacks Code-Schnipsel aufbauen. Dieser Code liefert uns eine Spielkplattform mit 10 * 10 also total 100 Punkte (das sind 100 Spielfeld-Objekte der Klasse Tile) die mittels 'x' und 'y' adressiert werden können. Dies sind nur Punkte und haben noch keine geometrische Form. Wir müssen die Klasse-Tile mit einem neuen Attribut dem ich den Namen 'panzer' gebe, erweitern. Später werden diesem Attribut 'panzer' ein Instanz-Objekt der jetzt noch nicht existierenden Klasse-Panzer zugewiesen. Für unser Beispiel hier weisen wir dem Attribut 'panzer' einen das ASCII-Zeichen '.' zu um zu sagen auf diesem Spielfeld hat es keinen Panzer (das Spielfeld ist noch leer). So können wir als nächtes die gute Idee von 'yipyip' mit einbeziehen und einmal die Panzerbelegung aller Spielfelder mit dem 'print-Befehl' als ASCII-Zeichen auf die Konsole ausgeben. Das müsste 10 Zeilen @ 10 Dezimalpunkte ergeben. Also es ist noch keines der Spielfelder von einem Panzer belegt.
Als nächstes können wir einigen Spielfeldern Panzer zuordnen. Hierfür nehmen wir die von 'yipyip' vorgeschlagenen ASCII-Zeichen 'O' und 'X' als zwei verschiedene Panzertypen.

Hier die Erweiterung der Klasse-Tile:

Code: Alles auswählen

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

    def __init__(self, name):
        """Konstruktor für die Klasse"""
        self.name = name 
        self.panzer = '.'
Nun kann ein Spielfeld nach einem darauf stehenden Panzer wie folgt abgefragt werden:

Code: Alles auswählen

print game_map[x, y].panzer
Ein Panzer wird wie folgt auf das Spielfeld gesetzt:

Code: Alles auswählen

game_map[x, y].panzer = 'O'
oder:

Code: Alles auswählen

game_map[x, y].panzer = 'X'
@derkai Könntest du einmal versuchen das Code-Schnipsel so zu erweitern, dass die Panzerbelegung der ganzen Spielplattform (Spielkarte) mit dem 'print-Befehl' auf die Konsole auszugeben. Am besten mit 'for' Schleifen.

Steht noch kein Panzer auf einem Spielfeld müsste dies wie folgt aussehen:

Code: Alles auswählen

..........
..........
..........
..........
..........
..........
..........
..........
..........
..........
@BlackJack Was meinst du zu diesem Veruch?

Gruss wuf :wink:
Take it easy Mates!
BlackJack

Montag 21. Juli 2008, 21:53

@wuf: Das funktioniert aber nur, wenn jede `Tile` in der Karte ein eigenes Objekt ist. Also muss man entweder 100 *verschiedene* `Tile`-Exemplate erzeugen, oder die Position des/der Panzer(s) anders speichern -- nicht in der Karte.
Antworten