#pydesw: Programmierung eines Brettspiels
-
- User
- Beiträge: 168
- Registriert: Montag 9. Mai 2016, 09:14
- Wohnort: Berlin
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 grausam
Ich habe es halb als Pseudo Python Code geschrieben 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
-
- User
- Beiträge: 206
- Registriert: Freitag 13. März 2015, 18:36
+100 für die Anstrengung und das mitarbeiten an der Ausarbeitung des Spiel-Plans
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?
Ich finde man hat hier einen recht schlechten Überblick,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 grausam
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?
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.
Den Code werde ich gleich mal genauer anschauen und vielleicht später eine überarbeitete Version hier zeigen.
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.
Hier mal ein grober Ansatz wie ich das lösen würde:
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...?
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!')
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...?
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.
@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.
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.
-
- User
- Beiträge: 206
- Registriert: Freitag 13. März 2015, 18:36
Zudem Mogeln: Das macht doch erst wirklich Sinn wenn man das Spiel über Netzwerk spielt oder?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...?
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
Idee zum Prüfen eines Zuges, der auch das Problem löst, wenn vom größten Index zum kleinsten Index gezogen wird:
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.
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 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.
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.
@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
https://github.com/python-forum-de
Daran dürfen sich gern auch weitere Leute neben Sebastian und mir beteiligen. Oder ist das Interesse schon wieder verflogen...?snafu hat geschrieben:Ich habe hier einen ersten Issue erstellt für die API der Spiellogik.
-
- User
- Beiträge: 206
- Registriert: Freitag 13. März 2015, 18:36
Nein, sorry aber ich habe jetzt Klausuren und dann Abi ... ich würde gern mithelfen, vielleicht finde ich zwischendurch noch etwas Zeit :K
Bei PlantUML mag ich den textbasierten Ansatz. Einziges Manko ist, dass ein Installer fehlt. Daher hier eine Kurzanleitung für interessierte Windows-Benutzer:BlackJack hat geschrieben: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.
- Ihr müsst Java installiert haben (Download)
- Ihr braucht Graphviz, weil damit die Grafiken erzeugt werden (Download)
Dann einfach von hier die JAR-Datei herunterladen. Ich habe mir im Ordner "Documents" für meinen Benutzer den Unterordner "PlantUML" erstellt und die Datei da reinkopiert. Zum Erzeugen der Grafik und Anzeige im Browser habe ich mir ein billiges Batchfile ("plantuml.bat") gebastelt, welches im selben Ordner liegt. Falls es jemand gebrauchen kann:
[codebox=dos file=Unbenannt.bat]@echo off
if not exist %1 (
echo FEHLER: %1 wurde nicht gefunden
exit /b 1
)
setlocal
set graphic=%~n1.png
set browser=firefox.exe
echo Erzeuge Grafik ...
java -jar plantuml.jar %1
if exist %graphic% (
echo Zeige %graphic% mit %browser% ...
start %browser% %graphic%
) else (
echo FEHLER: %graphic% wurde nicht gefunden, Anzeige nicht möglich
exit /b 1
)[/code]
@snafu:
Oops - hab grad jetzt erst gesehen Leider habe ich zu wenig Zeit, um da aktiv mit dran zu werkeln. Ein paar Ideen hab ich trotzdem:
- Textprotokoll über Spielverlauf wäre toll, ähnlich zu den üblichen Schachnotationen, darüber könnte man auch GUIs anbinden, z.B. remote
- Möglichkeit zur Umsetzung der weniger bekannten Spielvarianten und Regelsets (siehe engl. Wikipedia dazu)
- AI mit allem Schnickschnack (einstellbarer Schweregrad, adaptiv etc.)
- ELO-rating
Oops - hab grad jetzt erst gesehen Leider habe ich zu wenig Zeit, um da aktiv mit dran zu werkeln. Ein paar Ideen hab ich trotzdem:
- Textprotokoll über Spielverlauf wäre toll, ähnlich zu den üblichen Schachnotationen, darüber könnte man auch GUIs anbinden, z.B. remote
- Möglichkeit zur Umsetzung der weniger bekannten Spielvarianten und Regelsets (siehe engl. Wikipedia dazu)
- AI mit allem Schnickschnack (einstellbarer Schweregrad, adaptiv etc.)
- ELO-rating
-
- User
- Beiträge: 113
- Registriert: Freitag 30. Januar 2009, 00:53
- Wohnort: Dortmund
- Kontaktdaten:
Das könnte ich mir bei einer späteren Version vorstellen. Wenn es in der Planungsphase zu komplex wird dann steigt das Risiko, dass das Projekt in der Mülltonne landet. Für den Erfolg des Projekts würde ich vorschlagen es so simple wie möglich zu halten. Dann würde man sich auf Spieler gegen Spieler beschränken, die Spielregeln bzw. Spiellogik und die eine GUI.jerch hat geschrieben: - AI mit allem Schnickschnack (einstellbarer Schweregrad, adaptiv etc.)
- ELO-rating
@snafu: Das letzte #pydesw Projekt liegt auf Eis aus dem selben Grund der auch in diesem Projekt jetzt auftritt. Es steht etwas wichtiges an und das Projekt rückt in den Hintergrund. Das ist wohl eine der größten Hürden für ein Community-Projekt. Ich weiß auch nicht wie man das lösen könnte. Vielleicht würde eine größere Anzahl an Beteiligten helfen. Das ist aber leicher gesagt als getan. Bei Gelegenheit werde ich auch noch was beitragen.