Figuren auf einem Feld bewegen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Assassin@Buell
User
Beiträge: 3
Registriert: Freitag 5. Februar 2016, 10:50

Hallo Forum,

ich bin noch ziemlich unerfahren was Python oder Programmierung angeht, möchte mich aber dennoch an einer (für mich schwierigen Aufgabe versuchen). Ich möchte grundsätzlich KEINE Lösung, sondern lieber den berühmten Schubs oder Schlag auf den Hinterkopf.

Mein Wunsch bzw. meine mir gestellte Aufgabe ist folgende:
Auf einem Feld (20*20) sind verschiedene Figuren verteilt (und leer Flächen). Die Figuren bewegen sich pro Typ nach einem bestimmten Muster => zb. A im ersten Sprung (x,y) (+2,+1), im zweiten Sprung (+1,0) und im dritten Sprung (+1,-3) ... ab dann geht alles wieder von vorn los. Die Figuren bewegen sich bis zum Rand, jeweils wenn sie diesen erreichen, geht es ohne "Schrittverlust" auf der gegenüberliegenden Seite weiter.

Ich möchte die einzelnen Sprünge jeweils durch Tastendruck auslösen (Taste egal) und kann mir die aktuelle Anzahl auch als Zähler anzeigen lassen.
Ob es der jeweils erste/zweite/dritte/vierte=erste usw. Sprung ist, lässt sich ebenfalls auswerten.
Weiterhin denke ich, das ich die Auswertung der Figur hinbekomme (A;B;C ...H) und auch welches Muster jede dieser Figuren pro Sprung machen kann => mein Ansatz wäre als Array ([2,1],[1,0],[1,-3]) (Format und Benennung evtl. falsch, das Buch liegt zu Hause).

Mein Problem wie komme ich zu Beginn einer Runde zu ALLEN (400) Belegungen ... die Rechnung denke ich geht dann wieder ==> im Bsp. wird Feld [1,1], aktuell A neu zu [3,2] ... usw. mit jedem weiteren Sprung 2. [3,2] A ==> [4,2] ==> 3. [4,2] A ==> [5,20]

Ich weiss, vielleicht sehr umständlich beschrieben aber ich hoffe Ihr wisst was gemeint ist.

Mit welcher Funktion kann dies erreicht werden, mir fehlt der Einstieg.

Und weiterhin: Ich bekomme es aktuell so hin, dass ich das Feld immer wieder neu aufgebaut bekomme ... leider nicht so, dass ich es wie z.B. eine Excel Tabelle statisch vor mir habe und sich nur der Feldinhalt ändert. Mit was kann ich das erreichen?

Vorab schon mal vielen Dank für jede Form von Hinweis.

Gruss Dirk
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

@Assassin@Buell: ehrlich gesagt, ich habe nicht verstanden, wo Dein Problem ist: "Mein Problem wie komme ich zu Beginn einer Runde zu ALLEN (400) Belegungen". Du willst also alle Figuren nach einem bestimmten Muster bewegen? Dafür gibt es eine for-Schleife, die über alle Figuren läuft. Am einfachsten ist wohl ein Wörterbuch, das die Zuordung Position -> Figure speichert. Dann kannst Du ein neues Wörterbuch mit den neuen Positionen speichern.

Was hast Du Dir bei der Darstellung vorgestellt? Graphisch? Als Text? Mit einem GUI-Framework oder per pygame?
BlackJack

@Assassin@Buell: Drei Absätze mit der Beschreibung von zusammengesetzten Datentypen/verschachtelten Datenstrukturen, und Zustandsübergängen von einem Schritt zu nächsten und dann die Frage nach *einer* Funktion mit der man das erreichen kann? Erschreckende Antwort: Mit einer selbst geschriebenen, denn genau dieses Problem kann erstaunlicherweise nicht mit einer bereits fertigen Funktion gelöst werden. ;-)

Wenn Du vor einem Problem stehst das zu gross für eine einfache Funktion ist, dann teile es in mehrere, kleinere Teilprobleme auf und löse diese. Wenn die Teilprobleme zu gross für eine einfache Funktion sind — einfach den Vorgang des Aufteilens auf kleinere Teilprobleme wiederholen. Solange bis Du bei einfach zu lösenden Teilproblemen angekommen bist. Die kannst Du dann lösen, und wichtig: testen. Wenn sie funktionieren, kannst Du die Teillösungen zu jeweils grösseren (Teil)Lösungen zusammensetzen. Und die auch wieder Testen. Das machst Du solange bis Du eine Komplettlösung hast.

Bei der Beschreibung vermisse ich was passieren soll wenn zwei Figuren auf dem gleichen Feld landen würden. Das kann Auswirkungen auf die Datenstrukturen haben und damit natürlich auch auf den Code. Und auf die Darstellung einer Feldbelegung ebenfalls.

Bei der Darstellung stellte sich mir die gleiche Frage wie Sirius3. Falls die Antwort „grafisch“ lautet, dann brauchst Du spätestens dafür objektorientierte Programmierung, und dann würde es Sinn machen die auch für die Logik schon zu verwenden.
Assassin@Buell
User
Beiträge: 3
Registriert: Freitag 5. Februar 2016, 10:50

... hab ich mich wohl unglücklich ausgedrückt.

Also es ist insgesamt ein Rätsel, welches ich mit Stiften und Papier ohne weiteres lösen kann => die einzelnen Figuren bewegen sich eine Zeit lang (es gibt da unterschiedliche Versionen) und zu irgendeinem Zeitpunkt verrät mir die Stellung der Figuren die Lösung.
Einzelne Figuren bewegen sich immer wieder nach dem gleichen Muster, jedoch hat Figur A ein anders Muster als B (C-H) ...

Ich möchte also eine Initialbelegung eingeben (kann ich direkt bei der Programmierung und es auch später dort ändern).

Danach folgt dann mein Problem, denn wenn ich mit Schleifen arbeite, dann gehe ich pro Zeile (oder Spalte) vor und rücke immer ein Feld weiter.
In einem später bearbeiteten Feld kann aber schon eine Figur aus einem aktuellen Zug stehen.

Damit müsste ich Matrix "1" bearbeiten und die Ergebnisse jeweils in Matrix "2" schreiben ... und von dort dann ganz am Ende alles zurück in Matrix 1 schreiben ....

Wenn ichs erkläre komme ich der Lösung näher ... fragt weiter ;-)

Und natürlich nein, ich suche weder die Lösung vorgekaut durch Euch, noch diese "Eine" Funktion, vielmehr einen Einstieg, welchen ich nun habe "Wörterbuch" oder auch Matrix oder wie auch immer genannt, eins als Quelle und eins als (Zwischen)-Ziel.

Der nächste Frageteil von meiner Seite wird nun dauern, erst mal versuchen das hier umzusetzen.

Achso ... Darstellung: Es wäre schön, wenn ich das Ergebnis quasi so sehen könnte, als würden sich die Figuren wie auf einem Schachbrett bewegen (und es dürfen zwei oder mehr Figuren auf einem Feld stehen). Im einfachsten Fall als Matrix aus 20*20 Feldern, in denen die Figuren als Buchstabe oder Zahl dargestellt werden. Viel Grafik benötige ich nicht, nur diese eine Matrix.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Assassin@Buell hat geschrieben:Damit müsste ich Matrix "1" bearbeiten und die Ergebnisse jeweils in Matrix "2" schreiben ... und von dort dann ganz am Ende alles zurück in Matrix 1 schreiben ....
Du kannst auch einfach Matrix 2 weiterverwenden und genauso sollte man es auch machen.
BlackJack

@Assassin@Buell: Gibt es nur 8 Figuren auf dem Spielfeld oder sind A—H Bezeichnungen von Figurtypen die mehrfach auf dem Spielfeld vorkommen können? Und müsste man die dann in der Anzeige unterscheiden können, also eine Figur vom Typ A von einer anderen Figur vom Typ A? Und müssen wirklich alle Figuren auf einem Feld dargestellt werden? Dann müsste man die Felder ja so gross machen das auch alle Figuren auf einem Feld angezeigt werden können, die dort maximal stehen könnten.

Ergänzend zu DasIch: Schreib eine Funktion (oder Methode), die aus dem aktuellen Feldzustand den nächsten als dort neu erstellte Datenstruktur berechnet. Mit dem Ergebnis ersetzt Du dann einfach den aktuellen Feldzustand. Also entweder mit dem Ergebnis der Funktion, oder in der Methode das Attribut, welches den Feldzustand speichert.
Assassin@Buell
User
Beiträge: 3
Registriert: Freitag 5. Februar 2016, 10:50

Hallo zusammen,

ja, aktuell gibt es "nur" 8 Figuren - A-H war einfacher als Blau bis Grün. Jede Figur kommt mehrfach vor, nicht alle gleich viele Male.
Am Anfang steht pro Feld nur eine Figur, später dürfen aber auch alle 8 gleichzeitig auf einem Feld stehen.

Darum probiere ich es aktuell so => Matrix 20*20 Felder, bei der jedes Feld wieder eine Matrix 3*3 ist ==> 9 bleibt frei, die anderen 8 Felder können somit immer den jeweiligen Buchstaben oder die Farbe bekommen.

Ja, es müssen (leider) alle Figuren auf dem Feld dargestellt werden => wenn ich es aufmale (also wirklich von Hand löse), dann sind auch immer alle Figuren direkt sichtbar. (ideal, wenn auch nicht zwingend notwendig wäre z.B. jeden Buchstaben noch immer in einer anderen Farbe darzustellen.)

Aktuell bin ich gerade dabei herauszufinden, wie ich das anstelle...

Vorerst: VIELEN DANK
BlackJack

@Assassin@Buell: Hast Du Dich denn schon für eine Bibliothek oder ein Rahmenwerk zur Darstellung entschieden?

Ich habe da mal was für Tk gebastelt. Es werden 8 Figurtypen erzeugt die zwischen ein und fünf verschiedene Schritte haben, welche maximal ±2 Felder in jede Richtung gehen. Die Zahlen (ausser die 8) sind Zufallswerte. Auf dem Feld werden die Hälfte der Zellen zufällig ausgewählt und auf jedes dieser Felder wird ein zufälliger Figurentyp gesetzt.

Wenn man das ganze Feld auf einen Blick sehen will, muss man eine grössere Auflösung haben als ich an dem Laptop hier gerade zur Verfügung habe. Eine Änderung der Schrift(grösse) der einzelnen Buchstaben in den Zellen quittiert mir Tk leider mit einer Endlosschleife. Keine Ahnung warum. :-( Da würde ich als nächstes wohl versuchen die Zellen mit je einem `Canvas` selber (kleiner) darzustellen.

Um ein Bildschirmfoto machen zu können, habe ich das Feld auf 10×10 Zellen begrenzt:
Bild
Und nach 7 Schritten:
Bild

Hier ist der Quelltext (der Trennlinienkommentar trennt die Logik von der Darstellung):

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
import Tkinter as tk
from collections import defaultdict, namedtuple
from colorsys import hls_to_rgb
from itertools import izip
from math import sqrt
from random import choice, randint, sample
from tkFont import BOLD, Font


def get_colors(count):
    return [
        '#{0[0]:02x}{0[1]:02x}{0[2]:02x}'.format(
            [int(c * 255) for c in hls_to_rgb(i / count, 0.75, 1)]
        )
        for i in xrange(count)
    ]


class Coordinate(namedtuple('Coordinate', 'x y')):

    def __str__(self):
        return '({0.x}, {0.y})'.format(self)

    def __add__(self, other):
        return Coordinate(self.x + other.x, self.y + other.y)

    def adjusted(self, width, height):
        return Coordinate(self.x % width, self.y % height)

    @classmethod
    def random(cls, min_x, max_x, min_y, max_y):
        return cls(randint(min_x, max_x), randint(min_y, max_y))


class PieceType(object):

    def __init__(self, name, moves):
        self.name = name
        self.moves = moves

    def __cmp__(self, other):
        return cmp(self.name, other.name)

    def __hash__(self):
        return hash(self.name)

    def __len__(self):
        return len(self.moves)

    def __getitem__(self, step_number):
        return self.moves[self.get_move_number(step_number)]

    def get_move_number(self, step_number):
        return step_number % len(self)

    @classmethod
    def random(cls, name):
        return cls(
            name,
            [
                Coordinate(randint(-2, 2), randint(-2, 2))
                for _ in xrange(randint(1, 5))
            ]
        )


class FieldCoordinates(object):

    def __init__(self, size):
        self.size = size

    def __len__(self):
        return self.size**2

    def __getitem__(self, index):
        if not 0 <= index < len(self):
            raise IndexError()
        x, y = divmod(index, self.size)
        return Coordinate(x, y)

    def sample(self, count):
        return sample(self, count)


class Field(object):

    def __init__(self, size):
        self.size = size
        self.coordinates = FieldCoordinates(self.size)
        self.coordinate2piece_types = defaultdict(set)
        self.step_number = 0

    def __getitem__(self, coordinate):
        return self.coordinate2piece_types[coordinate]

    def add_piece(self, type_, coordinate):
        self.coordinate2piece_types[coordinate].add(type_)

    def iter_pieces(self):
        for coordinate, piece_types in self.coordinate2piece_types.iteritems():
            for piece_type in piece_types:
                yield coordinate, piece_type

    def get_piece_types(self):
        return sorted(set(p for c, p in self.iter_pieces()))

    def next_step(self):
        old_pieces = list(self.iter_pieces())
        self.coordinate2piece_types = defaultdict(set)
        for coordinate, piece_type in old_pieces:
            self.add_piece(
                piece_type,
                (
                    coordinate + piece_type[self.step_number]
                ).adjusted(self.size, self.size)
            )
        self.step_number += 1

    @classmethod
    def random(cls, size, names):
        result = cls(size)
        piece_types = [PieceType.random(name) for name in names]
        coordinates = FieldCoordinates(size)
        for coordinate in coordinates.sample(len(coordinates) // 2):
            result.add_piece(choice(piece_types), coordinate)
        return result

# ------------------------------------------------------------------------

class PieceTypeUI(tk.Frame):

    def __init__(self, parent, type_, color):
        tk.Frame.__init__(self, parent, relief=tk.RAISED, borderwidth=1)
        self.type = type_
        font = Font(self)
        font.configure(size=font.actual()['size'] * 2, weight=BOLD)
        label = tk.Label(
            self,
            text=self.type.name,
            width='2',
            font=font,
            background=color,
        )
        label.grid(row=0, column=0, rowspan=2, sticky=tk.NSEW)
        self.step_label = tk.Label(self)
        self.step_label.grid(row=0, column=1, sticky=tk.W)
        self.move_label = tk.Label(self)
        self.move_label.grid(row=1, column=1, sticky=tk.W)
        self.refresh()

    def refresh(self, step_number=0):
        self.step_label['text'] = 'step {0}/{1}'.format(
            self.type.get_move_number(step_number) + 1, len(self.type)
        )
        self.move_label['text'] = 'next move {0}'.format(self.type[step_number])


class PiecesUI(tk.LabelFrame):

    def __init__(self, parent, field, piece_type2color):
        tk.LabelFrame.__init__(self, parent, text='Pieces', padx=3, pady=3)
        self.field = field
        self.info_uis = list()
        for piece_type, color in sorted(piece_type2color.iteritems()):
            info_ui = PieceTypeUI(self, piece_type, color)
            info_ui.pack(side=tk.TOP, fill=tk.X)
            self.info_uis.append(info_ui)

    def refresh(self):
        for info_ui in self.info_uis:
            info_ui.refresh(self.field.step_number)


class CellUI(tk.Frame):

    def __init__(self, parent, field, coordinate, piece_type2color):
        tk.Frame.__init__(self, parent, borderwidth=1, relief=tk.RAISED)
        self.field = field
        self.coordinate = coordinate
        self.piece_type2color = piece_type2color
        piece_types = sorted(self.piece_type2color.iterkeys())
        size = int(sqrt(len(piece_types)) + 0.5)
        self.piece_type2label = list()
        for i, piece_type in enumerate(piece_types):
            label = tk.Label(
                self, text=piece_type.name, width=1, height=1
            )
            row, column = divmod(i, size)
            label.grid(row=row, column=column)
            self.piece_type2label.append((piece_type, label))
        self.refresh()
        self.propagate(False)

    def refresh(self):
        field_piece_types = self.field[self.coordinate]
        for piece_type, label in self.piece_type2label:
            label['background'] = (
                self.piece_type2color[piece_type]
                    if piece_type in field_piece_types
                    else 'gray25'
            )


class FieldUI(tk.Frame):

    def __init__(self, parent, field, piece_type2color):
        tk.Frame.__init__(self, parent)
        self.field = field
        self.cell_uis = list()
        for coordinate in self.field.coordinates:
            cell_ui = CellUI(self, self.field, coordinate, piece_type2color)
            cell_ui.grid(row=coordinate.y, column=coordinate.x, sticky=tk.NSEW)
            self.cell_uis.append(cell_ui)
        self.propagate(False)

    def refresh(self):
        for cell_ui in self.cell_uis:
            cell_ui.refresh()



class MainUI(tk.Frame):

    def __init__(self, parent, field):
        tk.Frame.__init__(self, parent)
        self.field = field
        piece_types = self.field.get_piece_types()
        piece_type2color = dict(izip(piece_types, get_colors(len(piece_types))))
        self.field_ui = FieldUI(self, self.field, piece_type2color)
        self.field_ui.pack(side=tk.LEFT, anchor=tk.N)
        
        frame = tk.Frame(self)
        self.pieces_ui = PiecesUI(frame, self.field, piece_type2color)
        self.pieces_ui.pack(side=tk.TOP, anchor=tk.E)

        step_frame = tk.LabelFrame(frame, text='Current Step', padx=3, pady=3)
        tk.Button(
            step_frame, text='Next', command=self.do_next
        ).pack(side=tk.RIGHT)
        self.step_number_label = tk.Label(step_frame)
        self.step_number_label.pack(side=tk.RIGHT)
        step_frame.pack(side=tk.TOP, fill=tk.X)

        frame.pack(side=tk.LEFT, padx=3, anchor=tk.N)
        self.refresh()

    def refresh(self):
        self.step_number_label['text'] = self.field.step_number
        self.field_ui.refresh()
        self.pieces_ui.refresh()

    def do_next(self):
        self.field.next_step()
        self.refresh()


def main():
    root = tk.Tk()
    root.title('Moving Pieces')
    field = Field.random(20, 'ABCDEFGH')
    main_ui = MainUI(root, field)
    main_ui.pack()
    root.mainloop()


if __name__ == '__main__':
    main()
Antworten