#pydesw: Programmierung eines Brettspiels

Du hast eine Idee für ein Projekt?

Welches Brettspiel soll programmiert werden?

Umfrage endete am Montag 20. Februar 2017, 16:25

Mühle
6
75%
Dame
2
25%
etwas anderes (bitte im Thread vorschlagen)
0
Keine Stimmen
 
Abstimmungen insgesamt: 8
Benutzeravatar
snafu
User
Beiträge: 5450
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Mittwoch 15. Februar 2017, 21:42

Ich fänd es erstmal ganz gut, wenn sich noch ein paar weitere Leute an der Skizzierung des Projekts beteiligen könnten. Bisher läuft das ja nur zwischen uns beiden ab.
shcol (Repo | Doc | PyPi)
sebastian0202
User
Beiträge: 168
Registriert: Montag 9. Mai 2016, 09:14
Wohnort: Berlin

Donnerstag 16. Februar 2017, 10:02

Ich versuche es mal. Es ist glaube zu viel des Guten und vermutlich wieder so viel vermischt..
Ich habe es halb als Pseudo Python Code geschrieben :lol: grausam

Code: Alles auswählen


class Feld(feldtyp):
    feldtyp=feldtyp
    besitzer = 'Spiel'
    
    def besetzt():
        wenn besitzer != 'Spiel':
            return True
        return False
    
    def setze(spielername):
        besitzer=spielername
    
    def typ():
        return feldtyp
    
    def leere():
        besitzer='Spiel'
    
    def besitzer():
        return besitzer

class Rechteck():
    # beginnen oben-links, enden unten-rechts
    acht_felder = [Feld('Ecke'), Feld('Kreuzung'), ... , Feld('Ecke')]
    
    def besetzt(feldnummer):
        return acht_felder[feldnummer].besetzt()
    
    def setze_besitzer(feldnummer, spielername):
        acht_felder[feldnummer].setze(spielername)
    
    def loesche_besitzer(feldnummer):
        acht_felder[feldnummer].leere()
    
    def hole_besitzer(feldnummer):
        return acht_felder[feldnummer].besitzer()

class Spiellogik():
    spielfeld = { 
        'außen': Rechteck(),
        'mittig': Rechteck(),
        'innen': Rechteck()
    }
    
    selbiges_rechteck = ([1,2,3], [6,7,8], [1,4,6], [3,5,7])
    ungleiches_rechteck = ([2,2,2], [4,4,4], [5,5,5], [7,7,7])
        
    def ist_muehle(info):
        rechteck, nummer = unpack(info)
        
        fuer muehle in selbiges_rechteck:
            wenn nummer in muehle: 
                besitzer = set()
                fuer nummer in muehle:
                    besitzer.add(ermittle_spielstein_besitzer(rechteck, nummer))
                if len(besitzer) == 1:
                    return True
        
        fuer muehle in ungleiches_rechteck:
            wenn nummer in muehle:
                besitzer = set()
                for rechteck in ('außen', 'mittig', 'innen')
                    besitzer.add(ermittle_spielstein_besitzer(rechteck, nummer))
                if len(besitzer) == 1:
                    return True

        return False
    
    def ermittle_spielstein_besitzer(rechteck, nummer):
        return spielfeld[rechteck].hole_besitzer(nummer)


class Spielfeld(Spiellogik):
    
    def setze_spielstein(spielstein, nach, spielername):
        wenn spielfeld[nach['rechteck']].besetzt(nach['nummer']):
            return False
        ansonsten
            wenn spielstein.ist_gesetzt():
                info = spielstein.hole_information()
                spielfeld[info['rechteck']].loesche_besitzer(info['nummer'])
            spielfeld[nach['rechteck']].setze_besitzer(nach['nummer'], spielername)
            return True
    
    def loesche_feld(spielstein):
        info = spielstein.hole_information()
        wenn ist_muehle(info): return False
        ansonsten:
            spielfeld[info['rechteck']].loesche_besitzer(info['nummer'])
            spielstein.toete()
            return True

    def analysiere(info):
        wenn ist_muehle(info): return True
        ansonsten: return False


class Spielstein():
    gesetzt = False
    lebendig = True
    info = ['position':None, 'rechteck':None]
    
    def hole_information():
        return info
    
    def ist_gesetzt():
        return gesetzt

    def toete():
        lebendig = False


class Auswahl():
    nach = [rechteckID, feldID]
    def hole_rechteck(gui):
    def spielstein_waehlen(gui):
        return auswahl aus der gui


class Spieler():
    spielsteine = [8*spielstein()]
    spielername = 
    gui = Auswahl() (emit Funktion spaeter?)
    letzer_zug = None

    def zug(spielfeld):
        spielsteingesetzt = False
        solange bis spielsteingesetzt: 
            spielstein = gui.spielstein_waehlen()
            spielsteingesetzt = spielfeld.setze_spielstein(spielstein, gui.nach, spielername)
        
        letzer_zug = spielstein.hole_information()
    
    def anzahl_lebendiger_spielsteine():
        anzahl = 0
        fuer stein in spielstein:
            wenn stein.lebendig:
                anzahl + 1
        return anzahl

class Spiel():
    spieler = [Spieler(), Spieler()]
    spielfeld = Spielfeld()
    generiere_schicke_grafik()
    
    def mache_zug(spieler_am_zug):
        spieler[spieler_am_zug].zug(spielfeld)

    def werte_zug_aus(spieler_am_zug):
        info = spieler_am_zug.letzer_zug()
        darf_stein_loeschen = spielfeld.analysiere(info)
        
        wenn darf_stein_loeschen:
            spielstein = spieler_am_zug.gui.spielstein_waehlen()
            solange wie nicht wahr: 
                spielfeld.loesche_feld(spielstein)
    
    def spiel_vorbei():
        fuer gamer in spieler:
            wenn spieler.anzahl_lebendiger_spielsteine() == 0:
                return True
        return False

    def update():
        solange True:
            spieler_am_zug = spieler[0]
            spieler = spieler[::-1]
            mache_zug(spieler_am_zug)
            werte_zug_aus(spieler_am_zug)

            wenn spiel_vorbei(): break

Pygoscelis papua
User
Beiträge: 206
Registriert: Freitag 13. März 2015, 18:36

Donnerstag 16. Februar 2017, 18:41

+100 für die Anstrengung und das mitarbeiten an der Ausarbeitung des Spiel-Plans :D
sebastian0202 hat geschrieben:Ich versuche es mal. Es ist glaube zu viel des Guten und vermutlich wieder so viel vermischt..
Ich habe es halb als Pseudo Python Code geschrieben :lol: grausam
Ich finde man hat hier einen recht schlechten Überblick,
es wäre wirklich übersichtlicher es als Bild darzustellen, ich weiß aber immer noch nicht wie man hier einen Anhang anfügt...
Und ich glaube ein Klassendiagramm soll noch nicht festlegen, wie die einzelnen Funktionen Implementiert werden, was es auch noch einmal
unübersichtlicher macht.
Ich finde es dennoch gut, dass du neues zu dem Diagramm beigetragen hast, ich habe nur jetzt nicht die Zeit alles genau durch zuschauen bzw. zu durchschauen.
Wenn es möglich wäre das als Bild irgendwie Darzustellen wäre es echt cool, vieleicht kann das bild einfach auf GitHub hochgeladen werden (in eine fork
von der repo zur Vorstellung des Projekts?) und dann hier verlinkt werden?
import this
hidden python features

JAVA = Just Another Vulnerability Announcement :D
Benutzeravatar
snafu
User
Beiträge: 5450
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Donnerstag 16. Februar 2017, 19:02

Schonmal auf die Idee gekommen, dass man auch einfach einen Link zum Bild setzen könnte...? ;)

Den Code werde ich gleich mal genauer anschauen und vielleicht später eine überarbeitete Version hier zeigen.
shcol (Repo | Doc | PyPi)
BlackJack

Donnerstag 16. Februar 2017, 19:47

Man könnte sich auch mal Werkzeuge zum erstellen von UML-Diagrammen aus Text anschauen. PlantUML wäre so ein Kandidat. UMLet habe ich auch schon mal verwendet.
Benutzeravatar
snafu
User
Beiträge: 5450
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Donnerstag 16. Februar 2017, 20:45

Hier mal ein grober Ansatz wie ich das lösen würde:

Code: Alles auswählen

from collections import namedtuple
from itertools import cycle

Position = namedtuple('Position', 'square_name, index')

class Board(object):
    def __init__(self):
        self.squares = dict(
            (name, 9 * [None]) for name in ('inner', 'mid', 'outer')
        )

    def set_piece(self, position, color):
        square = self.squares[position.square_name]
        if square[position.index]:
            raise OccupiedError
        square[position.index] = color

    def move_piece(self, old_pos, new_pos, color):
        if not self.is_valid_move(old_pos, new_pos, color):
            raise InvalidMoveError
        self.squares[old_pos.square_name][old_pos.index] = None
        self.squares[new_pos.square_name][new_pos.index] = color


class Player(object):
    def __init__(self, name, color):
        self.name = name
        self.color = color
        self.pieces_to_set = 9

    def play(self, board):
        # Hier in abgeleiteter Klasse Spielzug über GUI erfragen
        # oder Spielzug durch KI ermitteln basierend auf board.squares
        if has_lost:
            raise GameLost
        if self.pieces_to_set:
            board.set_piece(position, self.color)
            self.pieces_to_set -= 1
        else:
            board.move_piece(old_pos, new_pos, self.color)


def play_game(player1, player2):
    board = Board()
    player_cycle = cycle([player1, player2])
    for player in player_cycle:
        try:
            player.play(board)
        except GameLost:
            winner = next(player_cycle)
            return winner

def main():
    player1 = ask_player()
    player2 = ask_player()
    winner = play_game(player1, player2)
    print(winner.name, 'hat das Spiel gewonnen!')
Die Idee mit dem Tupel für einen Spielzug habe ich wieder verworfen. Besonders im Falle eines ungültigen Spielzugs fand ich es leichter, dass der Spieler eine durch die Board-Klasse ausgelöste Exception direkt abfangen kann. Als Alternative hätte play_game() das sinnvoll behandeln müssen.

Rein theoretisch könnte man das Spielbrett noch irgendwie vor "unsachgemäßen" Änderungen seitens des Spieler-Codes (auch Schummeln/Mogeln genannt) schützen. Aber das würde ich erstmal weg lassen, da die Klasse ja nur intern verwendet wird und der Endanwender keinen Einfluss darauf hat. Das würde erst dann eine Rolle spielen, wenn man einen abgeleiteten Spieler sozusagen im Rahmen eines Plugin-Systems erlaubt.

Was haltet ihr generell von den gezeigten Ansätzen? Sind sie gut nachvollziehbar? Würdet ihr bestimmte Dinge anders lösen...?
shcol (Repo | Doc | PyPi)
Benutzeravatar
snafu
User
Beiträge: 5450
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Donnerstag 16. Februar 2017, 21:25

Ups, es sind natürlich in jedem Quadrat nur 8 Felder, nicht 9 wie in meinem Code...

@sebastian0202:
Die von dir vorgeschlagene Schnittstelle ist sehr komplex. Es werden viele Aufrufe herumgereicht. Vielleicht könnte man da doch ein paar Dinge vereinfachen. Exceptions z.B sind manchmal ganz hilfreich, um einen Status für mehrere Instanzen hoch zu reichen. Es ist dann eben nicht True/False sondern Exception / keine Exception. Die Ebene, die die Exception sinnvoll behandeln kann, fängt sie einfach ab.

Trotzdem hast du damit eine gute Grundlage geliefert, von der ich einige Ideen übernommen habe.
shcol (Repo | Doc | PyPi)
Benutzeravatar
snafu
User
Beiträge: 5450
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Donnerstag 16. Februar 2017, 21:51

Ein vernünftiges UML-Diagramm fände ich auch sinnvoll. Trotzdem finde ich's nicht schlecht, auch schon bei der Planung einen groben Ablauf zu skizzieren, der über ein UML-Diagramm hinaus geht (Stichwort: Prototyping). Dabei erkennt man schnell, ob eine Idee möglicherweise in die falsche Richtung geht und kann schon in der Planungsphase bei Bedarf nachbessern.
shcol (Repo | Doc | PyPi)
Pygoscelis papua
User
Beiträge: 206
Registriert: Freitag 13. März 2015, 18:36

Samstag 18. Februar 2017, 12:33

snafu hat geschrieben: Rein theoretisch könnte man das Spielbrett noch irgendwie vor "unsachgemäßen" Änderungen seitens des Spieler-Codes (auch Schummeln/Mogeln genannt) schützen. Aber das würde ich erstmal weg lassen, da die Klasse ja nur intern verwendet wird und der Endanwender keinen Einfluss darauf hat. Das würde erst dann eine Rolle spielen, wenn man einen abgeleiteten Spieler sozusagen im Rahmen eines Plugin-Systems erlaubt.

Was haltet ihr generell von den gezeigten Ansätzen? Sind sie gut nachvollziehbar? Würdet ihr bestimmte Dinge anders lösen...?
Zudem Mogeln: Das macht doch erst wirklich Sinn wenn man das Spiel über Netzwerk spielt oder?
Sonst kann der Schummler, ja eh direkt den Board Code ändern.

Ich finde die Ansätze gut nachvollziehbar, was man noch hinzufügen sollte wäre bei move_piece im board, von wo nach wo man ziehen darf,
das könnte vieleicht so aussehen:
also das quadrat sieht vom index her vieleicht so aus:
8-1-2
7---3
6-5-4

Code: Alles auswählen

def move_allowed(old_pos, new_pos):
    if old_pos.square == new_pos.square:
        wenn old_pos.index == new_pos.index-1 oder +1 
        #dabei muss natürlich der Sprung zw. 1 und 8 berücksichtigt werden
        return True #oder was sollte man hier zurückgeben?
    else:
        if old_pos.index % 2 and new_pos.index == old_pos.index:
            if quadrate_angrenzend(old_pos, new_pos):
                return True
import this
hidden python features

JAVA = Just Another Vulnerability Announcement :D
Benutzeravatar
snafu
User
Beiträge: 5450
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Samstag 18. Februar 2017, 15:48

Idee zum Prüfen eines Zuges, der auch das Problem löst, wenn vom größten Index zum kleinsten Index gezogen wird:

Code: Alles auswählen

"""
The board:

0-----------1-----------2
|           |           |
|   0-------1-------2   |
|   |       |       |   |
|   |   0---1---2   |   |
|   |   |       |   |   |
7---7---7       3---3---3
|   |   |       |   |   |
|   |   6---5---4   |   |
|   |       |       |   |
|   6-------5-------4   |
|           |           |
6-----------5-----------4                        
"""
from collections import namedtuple

SQUARE_NAMES = ['inner', 'mid', 'outer']

NUM_SQUARE_FIELDS = 8

class Position(namedtuple('Position', 'square_name, index')):
    def __new__(cls, square_name, index):
        square_name = square_name.lower()
        if square_name not in SQUARE_NAMES:
            raise ValueError('Invalid square name')
        if not 0 <= index < NUM_SQUARE_FIELDS:
            raise ValueError('Invalid field index')
        return super().__new__(cls, square_name, index)


def is_valid_move(old_pos, new_pos):
    if old_pos == new_pos:
        return False
    if old_pos.square_name == new_pos.square_name:
        offset = old_pos.index - new_pos.index
        return abs(offset) in (1, NUM_SQUARE_FIELDS - 1)
    if old_pos.index == new_pos.index:
        is_crossing = new_pos.index % 2 != 0
        square_names = (old_pos.square_name, new_pos.square_name)
        return is_crossing and 'mid' in square_names
    return False
Die Prüfung auf ein belegtes Feld habe ich bei diesem isolierten Beispiel mal weg gelassen. Die beiden Konstanten auf Modulebene sind hierbei auch eigentlich als Instanzattribute der Board-Klasse gedacht. Das ließe sich ja leicht übertragen.
shcol (Repo | Doc | PyPi)
Benutzeravatar
snafu
User
Beiträge: 5450
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Montag 20. Februar 2017, 14:41

Die Umfrage läuft in 2 Stunden ab. Sieht stark danach aus, dass die Mehrheit sich für Mühle entschieden hat. Dann wäre mein Vorschlag, dass wir die Sache ernsthaft in Angriff nehmen. Wer wäre interessiert, ein UML-Diagramm zu erstellen? Dies kann entweder eine Einzelperson erledigen oder mehrere Leute als Team. Bei technischen oder konzeptionellen Fragen unterstützen wir den- oder diejenigen natürlich hier im Thread. Sinnvoll ist sicherlich auch ein Repo. Sofern es keine Alternative gibt, würde ich ein Repo unter meinem Github-Account für das Projekt erstellen...

PS: Die Teilnehmerzahl an der Umfrage ist deutlich höher als die Zahl der Beteiligten hier am Thread. Woran liegt das? Waren das Profis, die sich höflich zurück halten und erstmal den Anfängern den Vortritt lassen möchten oder hat es andere Gründe, dass ihr euch noch nicht zu Wort gemeldet habt...?

EDIT: Hatte wohl einen falschen Eindruck. Es sind 6 Stimmen für Mühle und 5 verschiedene Leute, die schon was geschrieben haben. So gravierend ist der Unterschied also doch nicht. :)
shcol (Repo | Doc | PyPi)
BlackJack

Montag 20. Februar 2017, 15:53

@snafu: Psst, es gibt eine Github-Organisation für #pydesw-Projekte wo man ein Repo dafür anlegen kann. :-)

https://github.com/python-forum-de
Benutzeravatar
snafu
User
Beiträge: 5450
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Montag 20. Februar 2017, 18:52

Danke, BlackJack. Ich wusste, da war was... :)

Habe soeben hier ein Repo für den gemeinsamen Account erstellt.
shcol (Repo | Doc | PyPi)
Benutzeravatar
snafu
User
Beiträge: 5450
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Montag 20. Februar 2017, 19:05

Ich habe hier einen ersten Issue erstellt für die API der Spiellogik. :)
shcol (Repo | Doc | PyPi)
Benutzeravatar
snafu
User
Beiträge: 5450
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Donnerstag 23. Februar 2017, 16:24

snafu hat geschrieben:Ich habe hier einen ersten Issue erstellt für die API der Spiellogik. :)
Daran dürfen sich gern auch weitere Leute neben Sebastian und mir beteiligen. Oder ist das Interesse schon wieder verflogen...?
shcol (Repo | Doc | PyPi)
Antworten