ok denk dir die id einfach weg.
Und zu player.play hat da jemand eine Antwort für mich? Ist einfach game übergeben ok? Sonst müsste man ja verbotener weise global verwenden oder?
#pydesw: Programmierung eines Brettspiels
-
- User
- Beiträge: 206
- Registriert: Freitag 13. März 2015, 18:36
Ein Spieler könnte bereits bei seiner Erzeugung das Spiel, zu dem er gehört, übergeben bekommen. Die Rückgabe seiner play()-Methode wäre dabei der nächste Spielzug. Was hältst du von diesem Ansatz?
-
- User
- Beiträge: 206
- Registriert: Freitag 13. März 2015, 18:36
ok das ist doch dann"call bei Referenz" oder?
Klar kannst du ein Bild erstellen und hier hochladen, oder du benutzt direkt ein Tool, um Klassendiagramme online zu erstellen/teilen/verbessern, da gibt es sehr vielePygoscelis papua hat geschrieben: Wie kann man hier eigentlich einen Anhang anfügen?
Hier ist das Klassendiagramm, es muss aber noch verbessert werden:
...
Ich hoffe ihr habt noch viele Verbesserungsvorschläge
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
Call by Reference ist etwas ganz anderes. Ich dachte es mir so, dass die play()-Methode ein namedtuple liefert mit den Angaben "from" (bzw "from_") und "to". Ein noch nicht gesetzter Stein hätte keine Angabe bzw den Standardwert None für das from-Attribut. Diese Rückgabe könnte vom Aufrufer (also dem Spiel) entsprechend verarbeitet werden. Im Falle eines ungültigen Zuges erfolgt eine Rückmeldung durch das Spiel per Exception, die von der GUI visuell dargestellt wird. Der Spieler ist dann weiterhin an der Reihe und play() müsste erneut aufgerufen werden.Pygoscelis papua hat geschrieben:ok das ist doch dann"call bei Referenz" oder?
-
- User
- Beiträge: 206
- Registriert: Freitag 13. März 2015, 18:36
Ich meine wenn man am Anfang dem Spieler das Spiel übergibt,
dann wird ja wohl nicht das Spiel in dem Spieler gespeichert, sondern eher sozusagen ein "Zeiger", da wenn man es verändert (in einer Methode von Spieler), wird das Spiel, dass übergeben wurde ja auch z.B bei einem 2. Spieler geändert, dem das Spiel auch übergeben wurde oder?
Wie wird das Spiel denn sonst angesprochen, als über einen "Zeiger", wenn es nicht im Spieler gespeichert ist?
Das Call ist hier natürlich nicht wirklich angebracht, da es keine Funktion ist ...
@Kebap die Frage was nicht ob sondern wie ...
dann wird ja wohl nicht das Spiel in dem Spieler gespeichert, sondern eher sozusagen ein "Zeiger", da wenn man es verändert (in einer Methode von Spieler), wird das Spiel, dass übergeben wurde ja auch z.B bei einem 2. Spieler geändert, dem das Spiel auch übergeben wurde oder?
Wie wird das Spiel denn sonst angesprochen, als über einen "Zeiger", wenn es nicht im Spieler gespeichert ist?
Das Call ist hier natürlich nicht wirklich angebracht, da es keine Funktion ist ...
@Kebap die Frage was nicht ob sondern wie ...
@Pygoscelis papua: Du bewegst Dich da auf der Ebene der Implementierung. Egal welchen Wert Du übergibst oder an ein Attribut bindest, es handelt sich bei Python immer um das selbe Objekt. Python kopiert bei Aufrufen und Zuweisung niemals Objekte/Werte. Das Aufrufmodell kann man als „call by object sharing“ bezeichnen. Namen und Attribute stehen in Python nicht für Speicher(adressen) wo man Werte ”rein tut”. Der Speicher gehört zum Wert/Objekt.
Die Übergabe des Spiels war eine erste Idee. Eigentlich muss nur das Spielfeld übergeben werden. Als Rückgabe dann wie gesagt ein Spielzug. Hierdurch müsste die play() Methode nichts vom Spiel wissen, sondern nur das Spielfeld mit seiner aktuellen Stellung auslesen können.
Denkbar wäre auch, einen GuiPlayer und einen KIPlayer von der Player Klasse abzuleiten, die als Schnittstelle nur Farbe, Name und die play() Methode haben.
Denkbar wäre auch, einen GuiPlayer und einen KIPlayer von der Player Klasse abzuleiten, die als Schnittstelle nur Farbe, Name und die play() Methode haben.
-
- User
- Beiträge: 206
- Registriert: Freitag 13. März 2015, 18:36
@snafu: Könntest du das Diagramm vielleicht deinen Vorstellungen entsprechend ändern?
Ich habe jetzt dafür nicht so viel Zeit und außerdem weiß ich nicht wie du dir das genau vorstellst
Ich habe jetzt dafür nicht so viel Zeit und außerdem weiß ich nicht wie du dir das genau vorstellst
-
- 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