From 0d9a79c51c6feecaf7ca52a438602241f33f7ba9 Mon Sep 17 00:00:00 2001 From: Jacob Koglin Date: Tue, 3 Mar 2026 00:08:49 +0100 Subject: [PATCH 1/9] Added in hard labour and after lots of error Networking parts --- Brett.py | 304 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- Dialog.py | 129 +++++++++++++++++++++++ 2 files changed, 425 insertions(+), 8 deletions(-) diff --git a/Brett.py b/Brett.py index e393783..22b1b51 100644 --- a/Brett.py +++ b/Brett.py @@ -1,5 +1,7 @@ import pygame from sys import exit +import socket +import threading from Feld import Feld from Springer import Springer from Turm import Turm @@ -7,7 +9,7 @@ from Dame import Dame from Bauer import Bauer from Koenig import Koenig -from Dialog import Dialog +from Dialog import Dialog, TextInputDialog from time import time class Brett(pygame.sprite.Sprite): @@ -15,6 +17,7 @@ def __init__(self, edge_length:int, topLeftCorner:tuple[int, int], field_color1: super().__init__() self.SetupTurnVars() + self.__setupNetzwerkVars() self.__rotation:int = rotation self.__edge_length:int = edge_length @@ -38,6 +41,7 @@ def __init__(self, edge_length:int, topLeftCorner:tuple[int, int], field_color1: self.__setupBrett() self.__generateImage() self.__resignDialog.hideSurface() + self.__setupStartDialogs() def SetupTurnVars(self): self.__onTurnTeam:int = 0 @@ -47,10 +51,260 @@ def SetupTurnVars(self): self.__turnNumber:int = 0 self.__PawnPromotes:list = [] + def __setupNetzwerkVars(self): + self.__startKlar:bool = False + self.__netzAktiv:bool = False + self.__netzPort:int = 55555 + self.__netzSock:socket.socket|None = None + self.__netzBuffer:str = "" + self.__netzVerbundenEvent = threading.Event() + self.__netzEmpfangThread:threading.Thread|None = None + self.__netzListenerThread:threading.Thread|None = None + self.__netzSucheThread:threading.Thread|None = None + self.__spielerName:str = "" + self.__meinTeam:int = 0 + self.__wendeRemoteZugAn:bool = False + self.__modusDialog:Dialog|None = None + self.__nameDialog:TextInputDialog|None = None + self.__netzStatusDialog:Dialog|None = None + self.__startDialogGruppe = pygame.sprite.Group() + + def __setupStartDialogs(self): + self.__startDialogGruppe.empty() + self.__modusDialog = Dialog( + self.rect.width, self.rect.height, + (self.rect.width//2, self.rect.height//2), + "Spielmodus wählen", self.rect.height//10, + [ + ["Singleplayer", self.__waehleSingleplayer], + ["Multiplayer (LAN)", self.__zeigeNameDialog] + ], + self.rect.height//8, 0.42, False, + onVoidClick=self.__generateImage, + posOffset=self.rect.topleft, + onSurfaceChange=self.__generateImage + ) + self.__startDialogGruppe.add(self.__modusDialog) + self.__generateImage() + + def __zeigeNameDialog(self): + self.__nameDialog = TextInputDialog( + self.rect.width, self.rect.height, + (self.rect.width//2, self.rect.height//2), + "Name eingeben", self.rect.height//10, + self.rect.height//10, + "Weiter", self.rect.height//10, + False, + onSubmit=self.__uebernehmeSpielerName, + onVoidClick=self.__generateImage, + posOffset=self.rect.topleft, + onSurfaceChange=self.__generateImage, + maxInputLength=24 + ) + self.__startDialogGruppe.empty() + self.__startDialogGruppe.add(self.__nameDialog) + self.__generateImage() + + def __zeigeNetzStatusDialog(self, headline:str): + self.__netzStatusDialog = Dialog( + self.rect.width, self.rect.height, + (self.rect.width//2, self.rect.height//2), + headline, self.rect.height//14, + [["Erneut suchen", self.__starteNetzSuche]], + self.rect.height//10, 0.7, False, + onVoidClick=self.__generateImage, + posOffset=self.rect.topleft, + onSurfaceChange=self.__generateImage + ) + self.__startDialogGruppe.empty() + self.__startDialogGruppe.add(self.__netzStatusDialog) + self.__generateImage() + + def __waehleSingleplayer(self): + self.__netzAktiv = False + self.__startDialogGruppe.empty() + self.__startKlar = True + self.start() + self.__generateImage() + + def __uebernehmeSpielerName(self, playerName:str): + playerName = playerName.strip() + if playerName == "": + if self.__nameDialog != None: + self.__nameDialog.setHeadline("Name darf nicht leer sein") + return + self.__spielerName = playerName + self.__starteMultiplayer() + + def __starteMultiplayer(self): + self.__netzAktiv = True + self.__zeigeNetzStatusDialog("Suche im LAN nach Schachpartie") + if self.__netzListenerThread == None or not(self.__netzListenerThread.is_alive()): + self.__netzListenerThread = threading.Thread(target=self.__listenerWorker, daemon=True) + self.__netzListenerThread.start() + self.__starteNetzSuche() + + def __starteNetzSuche(self): + if self.__netzVerbundenEvent.is_set(): + return + if self.__netzSucheThread != None and self.__netzSucheThread.is_alive(): + return + self.__netzSucheThread = threading.Thread(target=self.__discoveryWorker, daemon=True) + self.__netzSucheThread.start() + + def __holeLokaleIp(self)->str: + return socket.gethostbyname(socket.gethostname()) + + def __listenerWorker(self): + listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + listener.bind(("0.0.0.0", self.__netzPort)) + listener.listen(3) + while self.__netzAktiv and not(self.__netzVerbundenEvent.is_set()): + conn, _addr = listener.accept() + raw = conn.recv(2048) + if len(raw) == 0: + conn.close() + continue + msgText = raw.decode("utf-8").strip() + msgParts = msgText.split(";") + if len(msgParts) < 2 or msgParts[0] != "ASK": + conn.close() + continue + if self.__netzVerbundenEvent.is_set(): + conn.sendall("BUSY\n".encode("utf-8")) + conn.close() + continue + conn.sendall(("OK;" + self.__spielerName + "\n").encode("utf-8")) + self.__setzeNetzSocket(conn, 1) + return + + def __discoveryWorker(self): + localIp = self.__holeLokaleIp() + chunks = localIp.split(".") + if len(chunks) != 4: + self.__zeigeNetzStatusDialog("LAN-Suche fehlgeschlagen") + return + prefix = f"{chunks[0]}.{chunks[1]}.{chunks[2]}" + own = int(chunks[3]) + for host in range(1, 41): + if not(self.__netzAktiv) or self.__netzVerbundenEvent.is_set(): + return + if host == own: + continue + target = f"{prefix}.{host}" + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + if sock.connect_ex((target, self.__netzPort)) != 0: + sock.close() + continue + sock.sendall(("ASK;" + self.__spielerName + "\n").encode("utf-8")) + raw = sock.recv(2048) + if len(raw) == 0: + sock.close() + continue + responseText = raw.decode("utf-8").strip() + if responseText.startswith("OK;"): + self.__setzeNetzSocket(sock, 0) + return + sock.close() + if not(self.__netzVerbundenEvent.is_set()): + self.__zeigeNetzStatusDialog("Kein Spiel gefunden, warte auf Anfrage") + + def __setzeNetzSocket(self, sock:socket.socket, localTeam:int): + if self.__netzVerbundenEvent.is_set(): + sock.close() + return + self.__netzSock = sock + self.__meinTeam = localTeam + self.__netzVerbundenEvent.set() + self.__startDialogGruppe.empty() + self.__startKlar = True + self.start() + self.__starteNetzEmpfang() + self.__generateImage() + + def __starteNetzEmpfang(self): + if self.__netzEmpfangThread != None and self.__netzEmpfangThread.is_alive(): + return + self.__netzEmpfangThread = threading.Thread(target=self.__receiverWorker, daemon=True) + self.__netzEmpfangThread.start() + + def __receiverWorker(self): + while self.__netzAktiv and self.__netzVerbundenEvent.is_set(): + if self.__netzSock == None: + return + raw = self.__netzSock.recv(4096) + if len(raw) == 0: + return + self.__netzBuffer += raw.decode("utf-8") + while "\n" in self.__netzBuffer: + line, self.__netzBuffer = self.__netzBuffer.split("\n", 1) + line = line.strip() + if line == "": + continue + parts = line.split(";") + if len(parts) >= 3 and parts[0] == "MOVE": + self.__setzeRemoteZug(parts[1], parts[2]) + continue + if len(parts) >= 3 and parts[0] == "PROMO": + self.__setzeRemotePromo(parts[1], parts[2]) + + def __sendeNetzMessage(self, text:str): + if not(self.__netzAktiv) or not(self.__netzVerbundenEvent.is_set()): + return + if self.__netzSock == None: + return + self.__netzSock.sendall((text + "\n").encode("utf-8")) + + def __setzeRemoteZug(self, startLabel:str, targetLabel:str): + startField = self.__fields.get(startLabel) + targetField = self.__fields.get(targetLabel) + if type(startField) != Feld or type(targetField) != Feld: + return + self.__wendeRemoteZugAn = True + self.__eventMode = "chooseFigure" + self.__chooseFigureEvent(startField) + self.__setFigureEvent(targetField) + self.__wendeRemoteZugAn = False + self.__generateImage() + + def __setzeRemotePromo(self, fieldLabel:str, pieceName:str): + field = self.__fields.get(fieldLabel) + if type(field) != Feld: + return + figure = field.getFigure() + if type(figure) != Bauer: + return + if pieceName == "TURM": + if figure.getTeam() == 0: + field.setFigure(self.__whiteTower) + else: + field.setFigure(self.__blackTower) + elif pieceName == "LAEUFER": + if figure.getTeam() == 0: + field.setFigure(self.__whiteBishop) + else: + field.setFigure(self.__blackBishop) + elif pieceName == "SPRINGER": + if figure.getTeam() == 0: + field.setFigure(self.__whiteKnight) + else: + field.setFigure(self.__blackKnight) + elif pieceName == "DAME": + if figure.getTeam() == 0: + field.setFigure(self.__whiteQueen) + else: + field.setFigure(self.__blackQueen) + else: + return + self.__generateImage() + def restartGame(self): self.__reset_game_state() + if self.__netzAktiv: + self.__startKlar = True self.start() def getFieldRow(self, RowNumber:int)->list[Feld]: @@ -99,6 +353,8 @@ def __reset_game_state(self): def start(self)->None: if self.__running: raise Exception("Bereits gestartet!") + if not(self.__startKlar): + return self.__running = True self.__eventMode = "chooseFigure" @@ -217,6 +473,8 @@ def __generateImage(self) -> None: self.image.fill("black") fieldsGroup = self.__getFieldsGroup() fieldsGroup.draw(self.image) + if len(self.__startDialogGruppe.sprites()) != 0: + self.__startDialogGruppe.draw(self.image) if self.__resignDialog.getIfShown(): print("true") self.__DialogGroup.draw(self.image) @@ -226,12 +484,23 @@ def __generateImage(self) -> None: def update(self) -> None: - pass + if self.__nameDialog != None: + self.__nameDialog.update() def __CheckIfIsNotAFeldInstance(self, testObject:object) ->bool: return type(testObject) != Feld def handleLeftClickEvent(self, pos:tuple[int, int])->None: + if self.__modusDialog != None and self.__modusDialog.getIfShown(): + self.__modusDialog.handleLeftClick(pos) + return + if self.__nameDialog != None and self.__nameDialog.getIfShown(): + self.__nameDialog.handleLeftClick(pos) + return + if self.__netzStatusDialog != None and self.__netzStatusDialog.getIfShown() and not(self.__netzVerbundenEvent.is_set()): + self.__netzStatusDialog.handleLeftClick(pos) + return + if self.__resignDialog.getIfShown(): self.__resignDialog.handleLeftClick(pos) return @@ -245,6 +514,9 @@ def handleLeftClickEvent(self, pos:tuple[int, int])->None: return if not(self.__running): return + + if self.__netzAktiv and self.__onTurnTeam != self.__meinTeam: + return clickedField = self.getFieldByCords(pos) if self.__CheckIfIsNotAFeldInstance(clickedField): @@ -268,10 +540,16 @@ def handleLeftClickEvent(self, pos:tuple[int, int])->None: print(f"Didn't found the Event for the current EventMode: {self.__eventMode}") def handleRightClickEvent(self, pos:tuple[int, int])->None: + if not(self.__startKlar): + return if not(self.__resignDialog.getIfShown()): self.__resignDialog.showSurface() self.__generateImage() + def handleKeyDownEvent(self, event:pygame.event.Event)->None: + if self.__nameDialog != None and self.__nameDialog.getIfShown(): + self.__nameDialog.handleKeyDown(event) + def __resetCursorAndSetEventMode(self, eventMode:str): self.__cursor = None self.__eventMode = eventMode @@ -331,6 +609,9 @@ def __setFigureEvent(self, clickedField:Feld)->None: elif matchingTurnData["killMaybeFigureMustHadDoubleWalkLastTurn"] and killMaybeFigure.hasDidDoubleWalkInTurn(self.__turnNumber-1): killMaybeFigureField.setFigure(None) + startLabel = self.__cursor.getLabel() + targetLabel = clickedField.getLabel() + clickedField.setFigure(beforeCursorFigur) self.__cursor.setFigure(None) @@ -344,20 +625,22 @@ def __setFigureEvent(self, clickedField:Feld)->None: beforeCursorFigur.moved() self.__finishTurn() + if self.__netzAktiv and not(self.__wendeRemoteZugAn): + self.__sendeNetzMessage(f"MOVE;{startLabel};{targetLabel}") def __promoteTower(self): - self.__doPromote(self.__whiteTower, self.__blackTower) + self.__doPromote(self.__whiteTower, self.__blackTower, "TURM") def __promoteBishop(self): - self.__doPromote(self.__whiteBishop, self.__blackBishop) + self.__doPromote(self.__whiteBishop, self.__blackBishop, "LAEUFER") def __promoteKnight(self): - self.__doPromote(self.__whiteKnight, self.__blackKnight) + self.__doPromote(self.__whiteKnight, self.__blackKnight, "SPRINGER") def __promoteQueen(self): - self.__doPromote(self.__whiteQueen, self.__blackQueen) + self.__doPromote(self.__whiteQueen, self.__blackQueen, "DAME") - def __doPromote(self, Team0Figure, Team1Figure): + def __doPromote(self, Team0Figure, Team1Figure, promotionName:str): promoteData = self.__PawnPromotes[-1] promoteField:Feld = self.__fields[promoteData["Label"]] promoteDialog:Dialog = promoteData["Dialog"] @@ -368,12 +651,16 @@ def __doPromote(self, Team0Figure, Team1Figure): promoteDialog.hideSurface() promoteDialog.kill() self.__PawnPromotes.pop(-1) + if self.__netzAktiv and not(self.__wendeRemoteZugAn): + self.__sendeNetzMessage(f"PROMO;{promoteField.getLabel()};{promotionName}") def __finishTurn(self): for row in [1, 8]: for field in self.getFieldRow(row): if type(field.getFigure()) == Bauer: print("DETECT PROMOTE PAWN" + field.getLabel()) + if self.__netzAktiv and self.__wendeRemoteZugAn: + continue PromoteInfos = {} PromoteInfos["Dialog"] = Dialog( self.rect.width, self.rect.height, @@ -865,7 +1152,6 @@ def getFieldByCords(self, pos:tuple[int, int])->Feld|None: TestBrettGroup = pygame.sprite.GroupSingle() Spielbrett = Brett(800, (1920/2-400, 1080/2-400), "white", "black") - Spielbrett.start() TestBrettGroup.add(Spielbrett) @@ -876,6 +1162,8 @@ def getFieldByCords(self, pos:tuple[int, int])->Feld|None: pygame.quit() exit() continue + if event.type == pygame.KEYDOWN: + Spielbrett.handleKeyDownEvent(event) if event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: startClick = time() diff --git a/Dialog.py b/Dialog.py index f1b9810..daf42b3 100644 --- a/Dialog.py +++ b/Dialog.py @@ -91,6 +91,135 @@ def handleLeftClick(self, pos:tuple[int, int]): self.__onVoidClick() if self.__closeable: self.hideSurface() +class TextInputDialog(pygame.sprite.Sprite): + def __init__(self, DialogWidth:int, DialogHeight:int, centerPosition:tuple[int], headline:str, headlineSize:int, inputSize:int, buttonText:str, buttonSize:int, closeable:bool, onSubmit:Callable|None=None, onVoidClick:Callable|None=None, posOffset:tuple[int, int]=(0,0), onSurfaceChange:Callable=None, maxInputLength:int=24): + super().__init__() + self.__initPhase = True + self.__width:int = DialogWidth + self.__height:int = DialogHeight + self.__centerPosition:tuple[int] = centerPosition + self.__headline:str = headline + self.__headlineSize:int = headlineSize + self.__inputSize:int = inputSize + self.__buttonText:str = buttonText + self.__buttonSize:int = buttonSize + self.__closeable:bool = closeable + self.__onSubmit:Callable|None = onSubmit + self.__onVoidClick:Callable|None = onVoidClick + self.__posOffset = posOffset + self.__onSurfaceChange = onSurfaceChange + self.__maxInputLength:int = maxInputLength + self.__isShown = True + self.__eingabeWert:str = "" + self.__cursorSichtbar:bool = True + self.__letzterCursorBlink = pygame.time.get_ticks() + self.makeSurface() + self.__initPhase = False + + def __CallOnSurfaceChange(self): + if self.__onSurfaceChange == None: + return + self.__onSurfaceChange() + + def setHeadline(self, headline:str): + self.__headline = headline + self.makeSurface() + + def getValue(self)->str: + return self.__eingabeWert + + def clearValue(self): + self.__eingabeWert = "" + self.makeSurface() + + def showSurface(self): + self.__isShown = True + self.makeSurface() + + def hideSurface(self): + self.__isShown = False + self.image.fill("white") + if not(self.__initPhase): + self.__CallOnSurfaceChange() + + def getIfShown(self): + return self.__isShown + + def makeSurface(self): + self.image:pygame.surface.Surface = pygame.surface.Surface((self.__width, self.__height)) + self.image.fill("white") + self.rect:pygame.rect.Rect = self.image.get_rect(center = self.__centerPosition) + pygame.draw.rect(self.image, "Red", self.image.get_rect(), 4) + + titelFont = pygame.font.Font(None, self.__headlineSize) + titelSurface = titelFont.render(self.__headline, False, "black").convert() + self.image.blit(titelSurface, titelSurface.get_rect(centerx = self.__width//2, top = self.__height*0.08)) + + eingabeRect = pygame.Rect(self.__width*0.1, self.__height*0.35, self.__width*0.8, self.__height*0.22) + pygame.draw.rect(self.image, "black", eingabeRect, 2) + + eingabeFont = pygame.font.Font(None, self.__inputSize) + textZumRendern = self.__eingabeWert + if self.__cursorSichtbar and self.__isShown: + textZumRendern += "|" + eingabeSurface = eingabeFont.render(textZumRendern, False, "black").convert() + self.image.blit(eingabeSurface, eingabeSurface.get_rect(midleft=(eingabeRect.left + 8, eingabeRect.centery))) + + buttonFont = pygame.font.Font(None, self.__buttonSize) + self.__buttonSurface = buttonFont.render(self.__buttonText, False, "black").convert() + self.__buttonRect = self.__buttonSurface.get_rect(center=(self.__width//2, int(self.__height*0.78))) + self.image.blit(self.__buttonSurface, self.__buttonRect) + + if not(self.__initPhase): + self.__CallOnSurfaceChange() + + def update(self): + if not(self.__isShown): + return + jetzt = pygame.time.get_ticks() + if jetzt - self.__letzterCursorBlink >= 450: + self.__cursorSichtbar = not(self.__cursorSichtbar) + self.__letzterCursorBlink = jetzt + self.makeSurface() + + def handleLeftClick(self, pos:tuple[int, int]): + if not(self.__isShown): + return + localPos = (pos[0] - self.rect.x - self.__posOffset[0], pos[1] - self.rect.y - self.__posOffset[1]) + didNoAction = True + if self.__buttonRect.collidepoint(localPos): + if self.__onSubmit != None: + self.__onSubmit(self.__eingabeWert) + didNoAction = False + if didNoAction: + if self.__onVoidClick != None: + self.__onVoidClick() + if self.__closeable: + self.hideSurface() + + def handleKeyDown(self, event:pygame.event.Event): + if not(self.__isShown): + return + if event.key == pygame.K_RETURN: + if self.__onSubmit != None: + self.__onSubmit(self.__eingabeWert) + return + if event.key == pygame.K_BACKSPACE: + if len(self.__eingabeWert) != 0: + self.__eingabeWert = self.__eingabeWert[:-1] + self.makeSurface() + return + if len(self.__eingabeWert) >= self.__maxInputLength: + return + text = event.unicode + if len(text) != 1: + return + if not(text.isprintable()): + return + if text in ["\t", "\n", "\r"]: + return + self.__eingabeWert += text + self.makeSurface() def click(): #TestDialog.hideSurface() From faa2d4b8199151f2052423b4921df1033c504943 Mon Sep 17 00:00:00 2001 From: Jacob Koglin Date: Tue, 3 Mar 2026 20:50:32 +0100 Subject: [PATCH 2/9] Spezifikationen in Brett --- Brett.py | 428 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 410 insertions(+), 18 deletions(-) diff --git a/Brett.py b/Brett.py index 22b1b51..8e9e27b 100644 --- a/Brett.py +++ b/Brett.py @@ -13,6 +13,15 @@ from time import time class Brett(pygame.sprite.Sprite): + ''' + Vor.: -edge_length- ist vom Typ Integer und beschreibt die Kantenlaenge des quadratischen Schachbretts in Pixeln. Der Wert muss groesser als 0 sein. + -topLeftCorner- ist ein Tupel aus zwei Integern und beschreibt die linke obere Ecke des Brettes in Pixelkoordinaten. + -field_color1- ist vom Typ String und beschreibt die Farbe des ersten Feldmusters. + -field_color2- ist vom Typ String und beschreibt die Farbe des zweiten Feldmusters. + -rotation- ist vom Typ Integer und beschreibt die Rotation des Brettes in Grad. Sinnvolle Werte sind Vielfache von 90. + Eff.: Das Brett, die Felder, die Startdialoge und alle Spiel-/Netzwerk-Zustandsvariablen werden initialisiert. + Erg.: Eine Brettinstanz ist geliefert, welche -pygame.sprite.Sprite- geerbt hat und fuer den Spielstart vorbereitet ist. + ''' def __init__(self, edge_length:int, topLeftCorner:tuple[int, int], field_color1:str="yellow", field_color2:str="red", rotation:int=0): super().__init__() @@ -44,6 +53,11 @@ def __init__(self, edge_length:int, topLeftCorner:tuple[int, int], field_color1: self.__setupStartDialogs() def SetupTurnVars(self): + ''' + Vor.: - + Eff.: Alle Statusvariablen für Züge sind initialisiert. + Erg.: - + ''' self.__onTurnTeam:int = 0 self.__cursor = None self.__eventMode:str = None @@ -52,7 +66,12 @@ def SetupTurnVars(self): self.__PawnPromotes:list = [] def __setupNetzwerkVars(self): - self.__startKlar:bool = False + ''' + Vor.: - + Eff.: Alle Netzwerkvariablen sind initialisiert. + Erg.: - + ''' + self.__ready:bool = False self.__netzAktiv:bool = False self.__netzPort:int = 55555 self.__netzSock:socket.socket|None = None @@ -70,14 +89,19 @@ def __setupNetzwerkVars(self): self.__startDialogGruppe = pygame.sprite.Group() def __setupStartDialogs(self): + ''' + Vor.: Das Brett und die Dialoge sind initialisiert. + Eff.: Der Startmodus-Dialog ist erstellt und angezeigt. + Erg.: - + ''' self.__startDialogGruppe.empty() self.__modusDialog = Dialog( self.rect.width, self.rect.height, (self.rect.width//2, self.rect.height//2), "Spielmodus wählen", self.rect.height//10, [ - ["Singleplayer", self.__waehleSingleplayer], - ["Multiplayer (LAN)", self.__zeigeNameDialog] + ["Spiel an einem Rechner", self.__waehleSingleplayer], + ["Spiel über Netzwerk", self.__zeigeNameDialog] ], self.rect.height//8, 0.42, False, onVoidClick=self.__generateImage, @@ -88,6 +112,11 @@ def __setupStartDialogs(self): self.__generateImage() def __zeigeNameDialog(self): + ''' + Vor.: Die Startdialoggruppe ist initialisiert. + Eff.: Der Dialog zur Namenseingabe ist erstellt und aktiv gesetzt. + Erg.: - + ''' self.__nameDialog = TextInputDialog( self.rect.width, self.rect.height, (self.rect.width//2, self.rect.height//2), @@ -106,6 +135,11 @@ def __zeigeNameDialog(self): self.__generateImage() def __zeigeNetzStatusDialog(self, headline:str): + ''' + Vor.: -headline- ist ein String und beschreibt die anzuzeigende Ueberschrift. + Eff.: Der Dialog zur Netzwerkinit und Suche ist erstellt und angezeigt. + Erg.: - + ''' self.__netzStatusDialog = Dialog( self.rect.width, self.rect.height, (self.rect.width//2, self.rect.height//2), @@ -121,13 +155,23 @@ def __zeigeNetzStatusDialog(self, headline:str): self.__generateImage() def __waehleSingleplayer(self): + ''' + Vor.: - + Eff.: Der Einzelspielermodus ist gesetzt und das Spiel gestartet. + Erg.: - + ''' self.__netzAktiv = False self.__startDialogGruppe.empty() - self.__startKlar = True + self.__ready = True self.start() self.__generateImage() def __uebernehmeSpielerName(self, playerName:str): + ''' + Vor.: -playerName- ist vom Typ String. + Eff.: Der Spielername ist überprueft und bei Gueltigkeit uebernommen. + Erg.: - + ''' playerName = playerName.strip() if playerName == "": if self.__nameDialog != None: @@ -137,14 +181,24 @@ def __uebernehmeSpielerName(self, playerName:str): self.__starteMultiplayer() def __starteMultiplayer(self): + ''' + Vor.: Ein gueltiger Spielername ist gesetzt. + Eff.: Multiplayer ist aktiviert, Listener gestartet und Suche wird gestartet. + Erg.: - + ''' self.__netzAktiv = True - self.__zeigeNetzStatusDialog("Suche im LAN nach Schachpartie") + self.__zeigeNetzStatusDialog("Suche im Netzwerk nach Spiel") if self.__netzListenerThread == None or not(self.__netzListenerThread.is_alive()): self.__netzListenerThread = threading.Thread(target=self.__listenerWorker, daemon=True) self.__netzListenerThread.start() self.__starteNetzSuche() def __starteNetzSuche(self): + ''' + Vor.: - + Eff.: Ein Suchthread ist gestartet, falls dieser nicht bereits läuft. + Erg.: - + ''' if self.__netzVerbundenEvent.is_set(): return if self.__netzSucheThread != None and self.__netzSucheThread.is_alive(): @@ -153,9 +207,19 @@ def __starteNetzSuche(self): self.__netzSucheThread.start() def __holeLokaleIp(self)->str: + ''' + Vor.: - + Eff.: - + Erg.: Die lokale IP ist als String geliefert. + ''' return socket.gethostbyname(socket.gethostname()) def __listenerWorker(self): + ''' + Vor.: Netzwerkmodus ist aktiv und Port ist gueltig. + Eff.: Wartet auf eingehende Partieanfrgen und beantwortet den "Handshake". + Erg.: - + ''' listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM) listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) listener.bind(("0.0.0.0", self.__netzPort)) @@ -180,6 +244,11 @@ def __listenerWorker(self): return def __discoveryWorker(self): + ''' + Vor.: Netzwerkmodus ist aktiv. + Eff.: Sucht im lokalen Netz nach Gegenstellen und fuehrt ggf. Handshake aus. + Erg.: - + ''' localIp = self.__holeLokaleIp() chunks = localIp.split(".") if len(chunks) != 4: @@ -211,6 +280,11 @@ def __discoveryWorker(self): self.__zeigeNetzStatusDialog("Kein Spiel gefunden, warte auf Anfrage") def __setzeNetzSocket(self, sock:socket.socket, localTeam:int): + ''' + Vor.: -sock- ist ein offener Socket, -localTeam- ist eine Team-ID. + Eff.: Uebernimmt die Verbindung, setzt Team/Zustand und startet Empfang. + Erg.: - + ''' if self.__netzVerbundenEvent.is_set(): sock.close() return @@ -218,18 +292,28 @@ def __setzeNetzSocket(self, sock:socket.socket, localTeam:int): self.__meinTeam = localTeam self.__netzVerbundenEvent.set() self.__startDialogGruppe.empty() - self.__startKlar = True + self.__ready = True self.start() self.__starteNetzEmpfang() self.__generateImage() def __starteNetzEmpfang(self): + ''' + Vor.: Eine Netzwerkverbindung ist aufgebaut. + Eff.: Teil des Netzwerk-Multitrheadings, startet falls dieser nicht existiert den Empfangs-Thread. + Erg.: - + ''' if self.__netzEmpfangThread != None and self.__netzEmpfangThread.is_alive(): return self.__netzEmpfangThread = threading.Thread(target=self.__receiverWorker, daemon=True) self.__netzEmpfangThread.start() def __receiverWorker(self): + ''' + Vor.: Netzwerkmodus aktiv und Verbindung gesetzt. + Eff.: Verantwortlich fuer den Empfang von Nachrichten, liest Zeilen und verarbreitet Zuege. + Erg.: - + ''' while self.__netzAktiv and self.__netzVerbundenEvent.is_set(): if self.__netzSock == None: return @@ -250,6 +334,11 @@ def __receiverWorker(self): self.__setzeRemotePromo(parts[1], parts[2]) def __sendeNetzMessage(self, text:str): + ''' + Vor.: -text- ist eine Nachrichtenzeile ohne Zeilenumbrüche. + Eff.: Sendet die Nachricht an den verbundenen Reechner, falls Verbindung aktiv ist. + Erg.: - + ''' if not(self.__netzAktiv) or not(self.__netzVerbundenEvent.is_set()): return if self.__netzSock == None: @@ -257,18 +346,36 @@ def __sendeNetzMessage(self, text:str): self.__netzSock.sendall((text + "\n").encode("utf-8")) def __setzeRemoteZug(self, startLabel:str, targetLabel:str): - startField = self.__fields.get(startLabel) - targetField = self.__fields.get(targetLabel) - if type(startField) != Feld or type(targetField) != Feld: + ''' + Vor.: -startLabel- und -targetLabel- sind gueltige Feldbezeichnungen. + Eff.: Wendet einen empfangenen Zug auf das lokale Brett an. + Erg.: - + ''' + startPos = self.__getClickPosByLabel(startLabel) + targetPos = self.__getClickPosByLabel(targetLabel) + if startPos == None or targetPos == None: return self.__wendeRemoteZugAn = True - self.__eventMode = "chooseFigure" - self.__chooseFigureEvent(startField) - self.__setFigureEvent(targetField) + if self.__resignDialog.getIfShown(): + self.__resignDialog.hideSurface() + self.handleLeftClickEvent(startPos) + self.handleLeftClickEvent(targetPos) self.__wendeRemoteZugAn = False self.__generateImage() + def __getClickPosByLabel(self, fieldLabel:str)->tuple[int, int]|None: + field = self.__fields.get(fieldLabel) + if type(field) != Feld: + return None + fieldRect = field.getRect() + return (int(self.rect.x + fieldRect.centerx), int(self.rect.y + fieldRect.centery)) + def __setzeRemotePromo(self, fieldLabel:str, pieceName:str): + ''' + Vor.: -fieldLabel- ist eine gueltige Feldbezeichnung, -pieceName- ein bekannter Figurtyp. + Eff.: Fuehrt eine empfangene Bauernumwandlung auf dem angegebenen Feld aus. + Erg.: - + ''' field = self.__fields.get(fieldLabel) if type(field) != Feld: return @@ -302,12 +409,22 @@ def __setzeRemotePromo(self, fieldLabel:str, pieceName:str): def restartGame(self): + ''' + Vor.: - + Eff.: Setzt den Spielzustand zurueck und startet eine neue Partie. + Erg.: - + ''' self.__reset_game_state() if self.__netzAktiv: - self.__startKlar = True + self.__ready = True self.start() def getFieldRow(self, RowNumber:int)->list[Feld]: + ''' + Vor.: -RowNumber- ist eine gueltige Reihen-ID des Brettes. + Eff.: - + Erg.: Eine Liste mit allen Feldern der angegebenen Reihe ist geliefert. + ''' RowFields = [] for i in range(self.__fields_count): letter = chr(ord(self.__field_label_start_letter)+i) @@ -315,6 +432,11 @@ def getFieldRow(self, RowNumber:int)->list[Feld]: return RowFields def setupDialogGroup(self): + ''' + Vor.: - + Eff.: Erstellt Dialog fuer Aktionen. + Erg.: - + ''' self.__DialogGroup.empty() self.__resignDialog = Dialog( self.rect.width, self.rect.height, @@ -329,6 +451,11 @@ def setupDialogGroup(self): self.__DialogGroup.add(self.__resignDialog) def __reset_game_state(self): + ''' + Vor.: - + Eff.: Entfernt Figuren/Feldgruppen, startet Brettzustand und Dialoge neu. + Erg.: - + ''' self.__DialogGroup.empty() self.__fieldsGroup.empty() @@ -351,14 +478,24 @@ def __reset_game_state(self): self.__generateImage() def start(self)->None: + ''' + Vor.: Das Brett ist bereit zum Starten. + Eff.: Setzt das Spiel auf aktiv und wechselt in den Auswahlmodus. + Erg.: - + ''' if self.__running: raise Exception("Bereits gestartet!") - if not(self.__startKlar): + if not(self.__ready): return self.__running = True self.__eventMode = "chooseFigure" def __setupBrett(self)->None: + ''' + Vor.: Das Feldraster ist initialisiert. + Eff.: Erstellt Figureninstanzen und setzt die Startaufstellung. + Erg.: - + ''' #Wird später sauberer geschrieben !!! self.__FigureScale:float = 0.9 self.__blackTower = Turm("assets/graphics/s_turm.png", self.__field_length*self.__FigureScale, self.__field_length, self.__fields_count, self.__field_label_start_letter, 1) @@ -404,6 +541,11 @@ def __setupBrett(self)->None: self.__fields["e1"].setFigure(self.__whiteKing) def __buildPawnRow(self, RowNumber:int, scale:float, texturePath:str, teamID:int) -> None: + ''' + Vor.: Parameter beschreiben eine gueltige Reihe und Figurenkonfiguration. + Eff.: Befuellt die angegebene Reihe mit Bauern des Teams. + Erg.: - + ''' for i in range(self.__fields_count): fieldLabelLetter:str = chr(ord(self.__field_label_start_letter)+i) fieldLabel:str = fieldLabelLetter + str(RowNumber) @@ -411,21 +553,41 @@ def __buildPawnRow(self, RowNumber:int, scale:float, texturePath:str, teamID:int targetField.setFigure(Bauer(texturePath, self.__field_length*scale, self.__field_length, self.__fields_count, self.__field_label_start_letter, teamID)) def setRotation(self, rotation:int)->None: + ''' + Vor.: -rotation- ist ein Vielfaches von 90 Grad. + Eff.: Setzt die Brettrotation und korrigiert Feldpositionen. + Erg.: - + ''' if rotation % 90 != 0: raise Exception("Rotation not available!") self.__rotation:int = rotation self.__correctFieldPositions() def getRotation(self)->int: + ''' + Vor.: - + Eff.: - + Erg.: Die aktuelle Brettrotation in Grad ist geliefert. + ''' return self.__rotation def __correctFieldPositions(self)->None: + ''' + Vor.: Feldobjekte sind vorhanden. + Eff.: Berechnet und setzt die aktuelle Position aller Felder neu. + Erg.: - + ''' for key in self.__fields.keys(): currentField:Feld = self.__fields[key] currentField.setFieldPosition(self.__getFieldPositionByName(key)) self.__generateImage() def __createFields(self)->dict: + ''' + Vor.: Brettparameter sind gesetzt. + Eff.: Erstellt alle Feldobjekte. + Erg.: Ein Dictionary mit Feldlabeln als Keys und Feldobjekten als Werten ist geliefert. + ''' fields:dict = {} for spread in range(ord(self.__field_label_start_letter), ord(self.__field_label_start_letter)+self.__fields_count): for lengths in range(1, 1+self.__fields_count): @@ -439,6 +601,11 @@ def __createFields(self)->dict: return fields def __getFieldPositionByName(self, field_name:str)->tuple[int, int]: + ''' + Vor.: -field_name- ist eine gültige Feldbezeichnung. + Eff.: Berechnet die Pixelposition des Feldes mit Rotation. + Erg.: Die Feldposition als Tupel (x, y) ist geliefert. + ''' lengthID = int(ord(field_name[0])-ord(self.__field_label_start_letter)) spreadID = int(field_name[1]) @@ -455,12 +622,27 @@ def __getFieldPositionByName(self, field_name:str)->tuple[int, int]: return x, y def __rotatePoint90degree(self, point:tuple[int, int])->tuple[int, int]: + ''' + Vor.: -point- ist eine Koordinate im Brettsystem. + Eff.: - + Erg.: Die um 90 Grad gedrehte Koordinate ist geliefert. + ''' return (self.__edge_length-point[1]-self.__field_length, point[0]) def __getFieldsGroup(self)->pygame.sprite.Group: + ''' + Vor.: - + Eff.: - + Erg.: Die Sprite-Gruppe aller Felder ist geliefert. + ''' return self.__fieldsGroup def __createFieldsGroup(self)->pygame.sprite.Group: + ''' + Vor.: Feldobjekte sind erstellt. + Eff.: Fuegt alle Felder in eine neue Sprite-Gruppe ein. + Erg.: Die erzeugte Feldgruppe ist geliefert. + ''' fieldsGroup = pygame.sprite.Group() for key in self.__fields.keys(): @@ -470,6 +652,11 @@ def __createFieldsGroup(self)->pygame.sprite.Group: return fieldsGroup def __generateImage(self) -> None: + ''' + Vor.: Brettoberflaeche und Zeichenobjekte sind initialisiert. + Eff.: Rendert Felder und sichtbare Dialoge auf die Brettoberflaeche. + Erg.: - + ''' self.image.fill("black") fieldsGroup = self.__getFieldsGroup() fieldsGroup.draw(self.image) @@ -484,13 +671,28 @@ def __generateImage(self) -> None: def update(self) -> None: + ''' + Vor.: - + Eff.: Aktualisiert aktive UI-Elemente. + Erg.: - + ''' if self.__nameDialog != None: self.__nameDialog.update() def __CheckIfIsNotAFeldInstance(self, testObject:object) ->bool: + ''' + Vor.: -testObject- ist ein beliebiges Objekt. + Eff.: - + Erg.: -True- falls -testObject- kein Feld ist, sonst -False-. + ''' return type(testObject) != Feld def handleLeftClickEvent(self, pos:tuple[int, int])->None: + ''' + Vor.: -pos- ist eine gueltige Mausposition in Pixeln. + Eff.: Verarbeitet Linksklicks fuer Dialoge und Figuren je nach Modus. + Erg.: - + ''' if self.__modusDialog != None and self.__modusDialog.getIfShown(): self.__modusDialog.handleLeftClick(pos) return @@ -515,7 +717,7 @@ def handleLeftClickEvent(self, pos:tuple[int, int])->None: if not(self.__running): return - if self.__netzAktiv and self.__onTurnTeam != self.__meinTeam: + if self.__netzAktiv and self.__onTurnTeam != self.__meinTeam and not(self.__wendeRemoteZugAn): return clickedField = self.getFieldByCords(pos) @@ -540,21 +742,41 @@ def handleLeftClickEvent(self, pos:tuple[int, int])->None: print(f"Didn't found the Event for the current EventMode: {self.__eventMode}") def handleRightClickEvent(self, pos:tuple[int, int])->None: - if not(self.__startKlar): + ''' + Vor.: -pos- ist eine gueltige Mausposition in Pixeln. + Eff.: Oeffnet den Ingame-Dialog. + Erg.: - + ''' + if not(self.__ready): return if not(self.__resignDialog.getIfShown()): self.__resignDialog.showSurface() self.__generateImage() def handleKeyDownEvent(self, event:pygame.event.Event)->None: + ''' + Vor.: -event- ist ein gueltiges pygame KEYDOWN-Event. + Eff.: Leitet Tastatureingaben an den Nameneingabedialog weiter. + Erg.: - + ''' if self.__nameDialog != None and self.__nameDialog.getIfShown(): self.__nameDialog.handleKeyDown(event) def __resetCursorAndSetEventMode(self, eventMode:str): + ''' + Vor.: -eventMode- ist ein gueltiger Eventmodus. + Eff.: Setzt den Cursor zurueck und aktiviert den uebergebenen Modus. + Erg.: - + ''' self.__cursor = None self.__eventMode = eventMode def __getMatchingTurnData(self, startField:Feld, targetField:Feld)->dict|None: + ''' + Vor.: -startField- und -targetField- sind gueltige Felder. + Eff.: - + Erg.: Das passende ZugdatenDictionary oder -None- ist geliefert. + ''' FullTurnDataFromStartField:list[dict] = self.getPossibleTurnFieldsFullData(startField) for Data in FullTurnDataFromStartField: if Data["fieldLabel"] == targetField.getLabel(): @@ -562,6 +784,11 @@ def __getMatchingTurnData(self, startField:Feld, targetField:Feld)->dict|None: return None def __setFigureEvent(self, clickedField:Feld)->None: + ''' + Vor.: -clickedField- ist ein gueltiges Feldobjekt. + Eff.: Fuehrt einen Zug aus, inklusive Spezialregeln, Übertragung und Zugabschluss wird übertragen. + Erg.: - + ''' self.__clearAllFieldHighlights() if self.__chooseFigureEvent(clickedField): return @@ -629,18 +856,43 @@ def __setFigureEvent(self, clickedField:Feld)->None: self.__sendeNetzMessage(f"MOVE;{startLabel};{targetLabel}") def __promoteTower(self): + ''' + Vor.: Ein Bauer steht zur Umwandlung bereit. + Eff.: Wandelt den Bauer in einen Turm um. + Erg.: - + ''' self.__doPromote(self.__whiteTower, self.__blackTower, "TURM") def __promoteBishop(self): + ''' + Vor.: Ein Bauer steht zur Umwandlung bereit. + Eff.: Wandelt den Bauer in einen Laeufer um. + Erg.: - + ''' self.__doPromote(self.__whiteBishop, self.__blackBishop, "LAEUFER") def __promoteKnight(self): + ''' + Vor.: Ein Bauer steht zur Umwandlung bereit. + Eff.: Wandelt den Bauer in einen Springer um. + Erg.: - + ''' self.__doPromote(self.__whiteKnight, self.__blackKnight, "SPRINGER") def __promoteQueen(self): + ''' + Vor.: Ein Bauer steht zur Umwandlung bereit. + Eff.: Wandelt den Bauer in eine Dame um. + Erg.: - + ''' self.__doPromote(self.__whiteQueen, self.__blackQueen, "DAME") def __doPromote(self, Team0Figure, Team1Figure, promotionName:str): + ''' + Vor.: Es existiert ein aktiver Promotion-Kontext in der Liste -__PawnPromotes-. + Eff.: Ersetzt den Bauer durch die Teamfigur, schliesst den Dialog und sendet wenn im Netzwerk Netzwerkdaten. + Erg.: - + ''' promoteData = self.__PawnPromotes[-1] promoteField:Feld = self.__fields[promoteData["Label"]] promoteDialog:Dialog = promoteData["Dialog"] @@ -655,6 +907,11 @@ def __doPromote(self, Team0Figure, Team1Figure, promotionName:str): self.__sendeNetzMessage(f"PROMO;{promoteField.getLabel()};{promotionName}") def __finishTurn(self): + ''' + Vor.: Ein regelkonformer Zug wurde ausgefuehrt. + Eff.: Prueft Promotion/Matt/Remis, aktualisiert Spielzustand und wechselt Spieler. + Erg.: - + ''' for row in [1, 8]: for field in self.getFieldRow(row): if type(field.getFigure()) == Bauer: @@ -702,6 +959,11 @@ def __finishTurn(self): print("Remis, durch keine Zugmöglichkeit mehr!") def checkIfTeamCanMove(self, team:int): + ''' + Vor.: -team- ist eine gueltige Team-ID. + Eff.: - + Erg.: -True-, wenn das Team mindestens einen legalen Zug hat, sonst -False-. + ''' for field in self.__fields.values(): if type(field) != Feld: continue @@ -714,6 +976,11 @@ def checkIfTeamCanMove(self, team:int): return False def checkIfMate(self)->list[int]: + ''' + Vor.: - + Eff.: Prueft Matt und beendet wenn gefunden das Spiel. + Erg.: Eine Liste mattgesetzter Teams oder [-1], falls kein Matt vorliegt. + ''' checkedTeams = self.__getCheckedTeams() if len(checkedTeams) == 0: return [-1] @@ -728,6 +995,11 @@ def checkIfMate(self)->list[int]: return matedTeams def __getKingFieldsInDanger(self)->list[Feld]: + ''' + Vor.: Koenigsfelder sind auf dem Brett vorhanden. + Eff.: - + Erg.: Eine Liste aller Koenigsfelder, die bedroht sind, ist geliefert. + ''' KingFields:list[Feld] = self.__getFieldsWithKings() checkedKings:list[int] = [] for KingField in KingFields: @@ -736,6 +1008,11 @@ def __getKingFieldsInDanger(self)->list[Feld]: return checkedKings def __getCheckedTeams(self)->list[int]: + ''' + Vor.: - + Eff.: - + Erg.: Eine Liste aller Teams, deren Koenig aktuell im Schach steht, ist geliefert. + ''' KingFieldsInDanger:list[Feld] = self.__getKingFieldsInDanger() checkedTeams:list[int] = [] for CheckedKingFeld in KingFieldsInDanger: @@ -748,10 +1025,15 @@ def __getCheckedTeams(self)->list[int]: return checkedTeams def __getDangerFieldsWhenMove(self, targetField:Feld, OriginField:Feld)->list[Feld]: + ''' + Vor.: -OriginField- enthaelt eine Figur und beide Felder sind gueltig. + Eff.: Simuliert den Zug temporaer und ermittelt Bedrohungen auf dem Zielfeld. + Erg.: Eine Liste bedrohender Felder nach dem simulierten Zug ist geliefert. + ''' resultingDangerFields:list[Feld] = [] MovingFigure = OriginField.getFigure() if MovingFigure == None: - raise Exception("There must be a Figure on OriginField!") + raise Exception("TEs muss schon ne Figur auf dem Ursprungsfeld stehen") targetFieldFigur = targetField.getFigure() @@ -769,6 +1051,11 @@ def __getDangerFieldsWhenMove(self, targetField:Feld, OriginField:Feld)->list[Fe return resultingDangerFields def __getDangerFieldsToTheField(self, testForField:Feld, TeamID:int|None = None)->list[Feld]: + ''' + Vor.: -testForField- ist ein gueltiges Feld, -TeamID- optional eine Team-ID. + Eff.: - + Erg.: Eine Liste aller Felder, deren Figuren das Zielfeld bedrohen, ist geliefert. + ''' resultingDangerFields:list[Feld] = [] FieldFigure = testForField.getFigure() if TeamID == None: @@ -799,6 +1086,11 @@ def __getDangerFieldsToTheField(self, testForField:Feld, TeamID:int|None = None) return resultingDangerFields def __getFieldsWithKings(self)->list[Feld]: + ''' + Vor.: - + Eff.: - + Erg.: Eine Liste aller Felder mit König ist geliefert. + ''' fieldsWithKings:list[Feld] = [] for field in self.__fields.values(): if type(field) != Feld: @@ -811,9 +1103,19 @@ def __getFieldsWithKings(self)->list[Feld]: return fieldsWithKings def __switchToOtherPlayer(self)->None: + ''' + Vor.: - + Eff.: Wechselt das aktive Team. + Erg.: - + ''' self.__onTurnTeam = (self.__onTurnTeam+1)%2 def __chooseFigureEvent(self, clickedField:Feld)->bool: + ''' + Vor.: -clickedField- ist ein gueltiges Feld. + Eff.: Prueft Figurwahl, markiert moegliche Ziele und setzt Eventmodus. + Erg.: -True-, wenn eine waehlbare Figur aktiviert wurde, sonst -False-. + ''' clickedFigure:None|Springer|Turm|Bauer|Laeufer|Dame|Koenig = clickedField.getFigure() clickedFieldLabel:str = clickedField.getLabel() if clickedFigure == None: @@ -833,6 +1135,11 @@ def __chooseFigureEvent(self, clickedField:Feld)->bool: return True def __clearAllFieldHighlights(self)->None: + ''' + Vor.: - + Eff.: Entfernt alle Hervorhebungen von Feldern. + Erg.: - + ''' for key in self.__fields.keys(): field = self.__fields[key] if type(field) != Feld: @@ -840,6 +1147,11 @@ def __clearAllFieldHighlights(self)->None: field.clearFieldHighlights() def getPossibleTurnFields(self, Field:Feld, ignoreChecksOrAnxiety:bool=False, ignoreBuildingChecks:bool=False, ignoreCastling:bool=False)->list[Feld]: # Geht sicher, dass nicht doch irgendwie Ein Feld außerhalb des Brettes ist arbeitet noch Relative + ''' + Vor.: -Field- ist ein gueltiges Feld; die bool Flags steuern Filterregeln. + Eff.: - + Erg.: Eine Liste aller moeglichen Zielfelder fuer den Zug ist geliefert. + ''' PossibleTurnFields = [] relativePossibleTurnFields:list[dict] = self.getPossibleTurnFieldsFullData(Field, ignoreChecksOrAnxiety, ignoreBuildingChecks, ignoreCastling) for relativeField in relativePossibleTurnFields: @@ -853,17 +1165,32 @@ def getPossibleTurnFields(self, Field:Feld, ignoreChecksOrAnxiety:bool=False, ig return PossibleTurnFields def __markAllPosibleFields(self, FigureFieldLabel:str)->None: + ''' + Vor.: -FigureFieldLabel- ist ein gueltiges Feldlabel mit waehlbarer Figur. + Eff.: Markiert alle moeglichen Zielfelder. + Erg.: - + ''' for field in self.getPossibleTurnFields(self.__fields[FigureFieldLabel]): if type(field) == Feld: field.addFieldHighlight("SmallGreenMiddleCircle") - def __getOnlyPointsList(self,TurnsDatas:list[dict])->list[tuple]: + def __getOnlyPointsList(self,TurnsDatas:list[dict])->list[tuple]: ## nicht genutzt + ''' + Vor.: -TurnsDatas- ist eine Liste von Zugdaten-Dictionaries mit Key -point-. + Eff.: - + Erg.: Eine Liste der relativen Punkte ist geliefert. + ''' PointsList:list = [] # for TurnData in TurnsDatas: # PointsList.append(TurnData["point"]) # Zur Weiterverarbeitung sind nur noch die Relativen Punkte notwending return PointsList def __getOnlyTurnDataWithValidFields(self, turnsData:list[dict])->list[dict]: + ''' + Vor.: -turnsData- ist eine Liste potentieller Zugdaten. + Eff.: Filtert Zugdaten auf gueltige Zielfelder. + Erg.: Eine bereinigte Liste gueltiger Zugdaten ist geliefert. + ''' turnDataValidFields = [] for turnData in turnsData: turnTargetFieldLabel = turnData["fieldLabel"] @@ -877,6 +1204,11 @@ def __getOnlyTurnDataWithValidFields(self, turnsData:list[dict])->list[dict]: return turnDataValidFields def __getTurnsNotKillingMatesFromTurnData(self, turnsData:list[dict], TeamID:int)->list[dict]: + ''' + Vor.: -turnsData- ist eine Liste von Zugdaten, -TeamID- die Team-ID der zu ziehenden Figur. + Eff.: Entfernt Zuege, die eigene Figuren schlagen wuerden. + Erg.: Eine gefilterte Liste erlaubter Zugdaten ist geliefert. + ''' turnDataNotKillingMates = [] for turnData in turnsData: turnTargetFieldLabel = turnData["fieldLabel"] @@ -895,6 +1227,11 @@ def __getTurnsNotKillingMatesFromTurnData(self, turnsData:list[dict], TeamID:int return turnDataNotKillingMates def getPossibleTurnFieldsFullData(self, startingPointField:Feld, ignoreChecksOrAnxiety:bool=False, ignoreBuildingChecks:bool=False, ignoreCastling:bool=False)->list[dict]: + ''' + Vor.: -startingPointField- ist ein gueltiges Feld; Flags steuern Regelpruefungen. + Eff.: Ermittelt alle legalen Zugdaten. + Erg.: Eine Liste mit Zugdaten ist geliefert. + ''' startFigure:None|Springer|Turm|Bauer|Laeufer|Dame|Koenig = startingPointField.getFigure() if startFigure == None: return [] # Ein Feld ohne Figur kann seine Figur nirgendwo hinsetzen @@ -982,12 +1319,27 @@ def getPossibleTurnFieldsFullData(self, startingPointField:Feld, ignoreChecksOrA return possibleRelativeFields def __getFieldLabelsPositionIDs(self, FieldLabel:str)->list[int, int]: + ''' + Vor.: -FieldLabel- ist eine gueltige Feldbezeichnung. + Eff.: - + Erg.: Die Positions-IDs [Buchstabe als ord, Zahl] sind geliefert. + ''' return [ord(FieldLabel[0]), int(FieldLabel[1])] def __getLabelByPositionIDs(self, PositionIDs:list[int, int])->str: + ''' + Vor.: -PositionIDs- enthaelt gueltige Buchstaben-/Zahlen-IDs. + Eff.: - + Erg.: Das Feldlabel ist geliefert. + ''' return chr(PositionIDs[0])+str(PositionIDs[1]) def __getFieldsBetweenHorizontalVerticalOnly(self, startingField:Feld, targetField:Feld)->list[Feld]: + ''' + Vor.: Start- und Zielfeld liegen auf einer gemeinsamen Zeile oder Spalte. + Eff.: - + Erg.: Alle dazwischenliegenden Felder ohne Endpunkte sind geliefert. + ''' resultFieldList:list[Feld] = [] startFieldPositionIDs:list[int, int] = self.__getFieldLabelsPositionIDs(startingField.getLabel()) targetFieldPositionIDs:list[int, int] = self.__getFieldLabelsPositionIDs(targetField.getLabel()) @@ -1016,6 +1368,11 @@ def __getFieldsBetweenHorizontalVerticalOnly(self, startingField:Feld, targetFie return resultFieldList def __checkCastlingConditions(self, KingField:Feld, TargetField:Feld)->bool: + ''' + Vor.: -KingField- und -TargetField- gehoeren zum Rochade-Kontext. + Eff.: Prueft freie Zwischenfelder und "Schachfreiheit", aslo keine Bedrohungen auf überschrittenen Feldern. + Erg.: -True- bei erfuellten Rochadebedingungen, sonst -False-. + ''' FieldsBetween:list[Feld] = self.__getFieldsBetweenHorizontalVerticalOnly(KingField, TargetField) for Field in FieldsBetween: if Field.getFigure() != None: @@ -1029,6 +1386,11 @@ def __checkCastlingConditions(self, KingField:Feld, TargetField:Feld)->bool: return True def __getTurnDataWithoutWalkThroughFigures(self, relativeMaybePossibleTurnsData:list[dict], startField:Feld)->list|list[dict]: + ''' + Vor.: -relativeMaybePossibleTurnsData- enthaelt lineare Zugmuster, -startField- ist gueltig. + Eff.: Sortiert Zuglinien nach Richtung und kuerzt sie an blockierenden Figuren. + Erg.: Eine Liste mit erreichbaren Zugdaten ohne Durchlaufen von Figuren ist geliefert. + ''' xLine:list = [] yLine:list = [] DiagonalXandY:list = [] @@ -1091,20 +1453,40 @@ def __cutLineAtWalkingThroughFigures(self, Line:list[dict], startField:Feld)->li return resultLine def __getPointFromDict(self, DataDict:dict)->tuple: + ''' + Vor.: -DataDict- besitzt den Key -point-. + Eff.: - + Erg.: Der zugehoerige Punkt als Tupel ist geliefert. + ''' return DataDict['point'] def __sortToDestination(self, Line:list[dict], sortIndex:int)->list[list[dict]]: + ''' + Vor.: -Line- ist eine Liste von Zugdaten mit Punktwerten. + Eff.: Sortiert Linie in zwei Richtungen. + Erg.: Zwei richtungsgetrennte, sortierte Teillisten sind geliefert. + ''' Line.sort(key=self.__getPointFromDict) Line = self.__SpitNegativePoints(Line, sortIndex) return self.__reverseFirstPart(Line) def __reverseFirstPart(self, Line:list)->list: + ''' + Vor.: -Line- enthaelt zwei Teillisten. + Eff.: Dreht die erste Teilliste um. + Erg.: Das Tupel aus erster und zweiter Teilliste ist geliefert. + ''' firstPartLine = Line[0] if type(firstPartLine) == list: firstPartLine.reverse() return firstPartLine, Line[1] def __SpitNegativePoints(self, Line:list[dict], SplitNumberIndex:int)->list[list, list]: + ''' + Vor.: -Line- enthaelt Zugdaten mit numerischen Punktwerten. + Eff.: Trennt Eintraege nach negativem/nicht-negativem Wert am gegebenen Index. + Erg.: Zwei Listen (negativ | nicht-negativ) sind geliefert. + ''' LinePositives = [] LineNegatives = [] @@ -1116,6 +1498,11 @@ def __SpitNegativePoints(self, Line:list[dict], SplitNumberIndex:int)->list[list return LineNegatives, LinePositives def getRelativeField(self, fieldLabel:str, relativeField:tuple[int, int])->Feld|None: + ''' + Vor.: -fieldLabel- ist ein Startlabel, -relativeField- ein relativer Versatz. + Eff.: - + Erg.: Das relative Zielfeld oder -None- bei Ungueltigkeit ist geliefert. + ''' startFieldX:int = ord(fieldLabel[0]) startFieldY:int = int(fieldLabel[1]) try: @@ -1128,6 +1515,11 @@ def getRelativeField(self, fieldLabel:str, relativeField:tuple[int, int])->Feld| return None def getFieldByCords(self, pos:tuple[int, int])->Feld|None: + ''' + Vor.: -pos- ist eine Pixelkoordinate. + Eff.: Ermittelt anhand der Koordinate das zugehoerige Feld. + Erg.: Das gefundene Feld oder -None- ist geliefert. + ''' x:int = pos[0]-self.rect.topleft[0] y:int = pos[1]-self.rect.topleft[1] if not(self.rect.collidepoint(pos)): From ce25c1c1f60bab8d5784dec3e74cf4ecb67f7ce3 Mon Sep 17 00:00:00 2001 From: Traumi-Schlumpf Date: Tue, 3 Mar 2026 22:48:18 +0100 Subject: [PATCH 3/9] Improve docstrings and types across chess files Refine and standardize documentation and type information in Bauer.py, Brett.py, Dame.py, Springer.py and Turm.py. Replaced vague move-data descriptions with explicit key/value types and tuples, clarified preconditions/effects/results for many methods (UI updates, network behavior, image regeneration, promotion flow, click/coordinate helpers), added/adjusted type hints (e.g. __eventMode:str|None) and small formatting fixes (whitespace, variable assignment). These changes are purely documentation/type clarifications to aid maintenance and readability. --- Bauer.py | 30 +++++------ Brett.py | 141 ++++++++++++++++++++++++++++++++++------------------ Dame.py | 30 +++++------ Springer.py | 30 +++++------ Turm.py | 30 +++++------ 5 files changed, 153 insertions(+), 108 deletions(-) diff --git a/Bauer.py b/Bauer.py index 6c7ea51..257c7ee 100644 --- a/Bauer.py +++ b/Bauer.py @@ -40,23 +40,23 @@ def getMaybePossibleTurns(self, originFieldLabel:str)->list[dict]: ''' Vor.: -originFieldLabel- ist eine gueltige Schachfeldbezeichnung, mit einer Laenge von 2. Das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl. Das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl. Eff.: - - Erg.: Ein Dictionary mit folgenden Daten ist geliefert. Den Koordinaten des Punktes, auf der die Figur steht. Den Angaben, ob das Schlagen einer anderen Figur moeglich ist (mit Angabe der anderen Figur, des Feldes und ob ein Doppelzug geschehen ist). Ob die Figur von einer anderen Figur bedroht wird. Ob die Figur besondere Zuege hat und ob der Zug erlaubt ist. + Erg.: Eine Liste ggf. aus Tabellen ist geliefert. Sie beschreibt unter welchen Bedingungen ein bestimmter Zug durchgefuehrt werden kann. Die ggf. in Liste vorkommenden Tabellen bestehen in diesem Fall aus ("" sind die Keys und das hinter dem = die Werte): - "point" = -RelativePoint- (gueltige Feldbezeichnung, die relativ zu -originFieldLabel- die Anzahl der Felder angibt, die fuer einen Zug in Buchstaben und Zahlen Richtung noetig sind) - "fieldLabel" eine gueltige Feldbezeichnung (das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl / das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl) zu dem sich die Figur bewegen soll. - "onlyOnKill" = -onlyOnKill- (-True-, wenn die Figur nur den Zug machen kann, wenn sie dabei eine andere Figur schlagen wuerde) - "canKill" = -canKill- (-True-, wenn die Figur beim Zug auf das Zielfeld eine andere Figur schlagen koennte) - "killMaybeFigureType" = -killMaybeFigureType- (Typ der Figur, die geschlagen werden kann) - "killMaybeFigureField" = -killMaybeFigureField- (Feldbezeichnung des Feldes, auf dem eine Figur geschlagen werden kann) - "killMaybeFigureMustHadDoubleWalkLastTurn" = -killMaybeFigureMustHadDoubleWalkLastTurn- (beschreibt, ob die Figur, die geschlagen werden kann, im letzten Zug einen Doppelzug gemacht hat) - "hasAnxiety" = -hasAnxiety- oder immer True, wenn die Figur die Koenigsrolle traegt (beschreibt, ob die Figur auf dem Zeilfeld des Zuges geschlagen werden koennte) - "specialTurnType" = -specialMoveLabel- (spezielle Zugbezeichnung) - "needFigureOnField" = -needFigureOnField- (Zug ist nur moeglich, wenn auf dem Feld eine Figur steht und ein anderes Feld angegeben ist) - "neededFigureType" = -needFigureType- (ueberprueft, ob der Zug einer bestimmten Figur mit der Figur auf dem Feld uebereinstimmt) - "allowNeededFigureHasTurned" = allowNeededFigureHasTurned (beschreibt, ob sie die Figur auf dem Feld -needFigureOnField- schon bewegt hat) - "endPointNeededFigure" = endPointNeededFigure (Feld, auf dem die Firgur nach dem Zug steht) - "onDoneTurnCall" = onDoneTurnCall (beschreibt die Methode/Funktion, die nach dem Zug ausgefuerht wird) + "point" = :[int, int] (gueltige Feldbezeichnung, die relativ zu -originFieldLabel- die Anzahl der Felder angibt, die fuer einen Zug in Buchstaben und Zahlen Richtung noetig sind) + "fieldLabel" = :str eine gueltige Feldbezeichnung (das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl / das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl) zu dem sich die Figur bewegen soll. + "onlyOnKill" = :bool (-True-, wenn die Figur nur den Zug machen kann, wenn sie dabei eine andere Figur schlagen wuerde) + "canKill" = :bool (-True-, wenn die Figur beim Zug auf das Zielfeld eine andere Figur schlagen koennte) + "killMaybeFigureType" = (Klasse der Figur, die geschlagen werden kann) + "killMaybeFigureField" = :str (Feldbezeichnung des Feldes, auf dem eine Figur geschlagen werden kann) + "killMaybeFigureMustHadDoubleWalkLastTurn" = :bool (beschreibt, ob die Figur, die geschlagen werden kann, im letzten Zug einen Doppelzug gemacht hat) + "hasAnxiety" = :boool oder immer True, wenn die Figur die Koenigsrolle traegt (beschreibt, ob die Figur auf dem Zeilfeld des Zuges geschlagen werden koennte) + "specialTurnType" = :str (spezielle Zugbezeichnung) + "needFigureOnField" = :str (Zug ist nur moeglich, wenn auf dem Feld eine Figur steht und ein anderes Feld angegeben ist) + "neededFigureType" = (ueberprueft, ob der Zug einer bestimmten hier angegebender Figur als Figurklasse mit der Figur auf dem Feld uebereinstimmt) + "allowNeededFigureHasTurned" = :bool (beschreibt, ob sie die Figur auf dem Feld -needFigureOnField- schon bewegt hat) + "endPointNeededFigure" = :str (Feldbezeichnung, auf dem die Firgur nach dem Zug steht) + "onDoneTurnCall" = :Callable (beschreibt die Methode/Funktion, die nach dem Zug ausgefuerht wird) ''' possibleZuege = [] if self.getTeam() == 0: diff --git a/Brett.py b/Brett.py index 8e9e27b..230888b 100644 --- a/Brett.py +++ b/Brett.py @@ -19,7 +19,7 @@ class Brett(pygame.sprite.Sprite): -field_color1- ist vom Typ String und beschreibt die Farbe des ersten Feldmusters. -field_color2- ist vom Typ String und beschreibt die Farbe des zweiten Feldmusters. -rotation- ist vom Typ Integer und beschreibt die Rotation des Brettes in Grad. Sinnvolle Werte sind Vielfache von 90. - Eff.: Das Brett, die Felder, die Startdialoge und alle Spiel-/Netzwerk-Zustandsvariablen werden initialisiert. + Eff.: Das Brett, die Felder, die Startdialoge und alle Spiel-/Netzwerk-Zustandsvariablen sind initialisiert. Erg.: Eine Brettinstanz ist geliefert, welche -pygame.sprite.Sprite- geerbt hat und fuer den Spielstart vorbereitet ist. ''' def __init__(self, edge_length:int, topLeftCorner:tuple[int, int], field_color1:str="yellow", field_color2:str="red", rotation:int=0): @@ -55,12 +55,13 @@ def __init__(self, edge_length:int, topLeftCorner:tuple[int, int], field_color1: def SetupTurnVars(self): ''' Vor.: - - Eff.: Alle Statusvariablen für Züge sind initialisiert. + Eff.: Alle Statusvariablen für Züge sind initialisiert. + Die Variablen: self.__onTurnTeam:int, self.__cursor, self.__turnNumber:int, self.__eventMode:str, self.__PawnPromotes:list und self.__running:bool sind eventuell beeinflusst worden. Erg.: - ''' self.__onTurnTeam:int = 0 self.__cursor = None - self.__eventMode:str = None + self.__eventMode:str|None = None self.__running:bool = False self.__turnNumber:int = 0 self.__PawnPromotes:list = [] @@ -68,7 +69,7 @@ def SetupTurnVars(self): def __setupNetzwerkVars(self): ''' Vor.: - - Eff.: Alle Netzwerkvariablen sind initialisiert. + Eff.: Alle Netzwerk relevaten Variablen sind initialisiert. Erg.: - ''' self.__ready:bool = False @@ -91,7 +92,9 @@ def __setupNetzwerkVars(self): def __setupStartDialogs(self): ''' Vor.: Das Brett und die Dialoge sind initialisiert. - Eff.: Der Startmodus-Dialog ist erstellt und angezeigt. + Eff.: Der Startmodus-Dialog ist erstellt und angezeigt. + Inform einer weißen Box, welche sich ueber das Schachbrett streckt und die Ueberschrift "Spielmodus wählen" traegt. + Mit den Unterueberschriften "Spiel an einem Rechner" und "Spiel über Netzwerk", welche Interaktionsmoeglichkeiten darstellen im Bezug zur Ueberschrift. Erg.: - ''' self.__startDialogGruppe.empty() @@ -115,6 +118,9 @@ def __zeigeNameDialog(self): ''' Vor.: Die Startdialoggruppe ist initialisiert. Eff.: Der Dialog zur Namenseingabe ist erstellt und aktiv gesetzt. + Inform einer weißen Box, welche sich ueber das Schachbrett streckt und die Ueberschrift "Name eingeben" traegt. + Mit den Unterueberschriften "Weiter", welche Interaktionsmoeglichkeiten darstellen im Bezug zur Ueberschrift. + Texteingaben sind in einem Eingabeberreich dargestellt. Erg.: - ''' self.__nameDialog = TextInputDialog( @@ -138,6 +144,8 @@ def __zeigeNetzStatusDialog(self, headline:str): ''' Vor.: -headline- ist ein String und beschreibt die anzuzeigende Ueberschrift. Eff.: Der Dialog zur Netzwerkinit und Suche ist erstellt und angezeigt. + Inform einer weißen Box, welche sich ueber das Schachbrett streckt und die Ueberschrift -headline- mittig traegt. + Mit den Unterueberschriften "Erneut suchen", welche eine Interaktionsmoeglichkeit darstellen im Bezug zur Ueberschrift. Erg.: - ''' self.__netzStatusDialog = Dialog( @@ -169,7 +177,7 @@ def __waehleSingleplayer(self): def __uebernehmeSpielerName(self, playerName:str): ''' Vor.: -playerName- ist vom Typ String. - Eff.: Der Spielername ist überprueft und bei Gueltigkeit uebernommen. + Eff.: Der Spielername ist überprueft und bei Gueltigkeit uebernommen. Bei ungueltigkeit ist die Dialogueberschrift nun "Name darf nicht leer sein" . Erg.: - ''' playerName = playerName.strip() @@ -184,6 +192,7 @@ def __starteMultiplayer(self): ''' Vor.: Ein gueltiger Spielername ist gesetzt. Eff.: Multiplayer ist aktiviert, Listener gestartet und Suche wird gestartet. + Mittels einen bereits in der optik Beschriebenden Dialogfensters ist der Suchstatus mittels der Ueberschrift "Suche im Netzwerk nach Spiel" abzulesen. Erg.: - ''' self.__netzAktiv = True @@ -216,7 +225,7 @@ def __holeLokaleIp(self)->str: def __listenerWorker(self): ''' - Vor.: Netzwerkmodus ist aktiv und Port ist gueltig. + Vor.: Netzwerkmodus ist aktiv und der definierte Port ist gueltig und ohne Netzwerkeinschraenkungen nutzbar. Eff.: Wartet auf eingehende Partieanfrgen und beantwortet den "Handshake". Erg.: - ''' @@ -246,7 +255,7 @@ def __listenerWorker(self): def __discoveryWorker(self): ''' Vor.: Netzwerkmodus ist aktiv. - Eff.: Sucht im lokalen Netz nach Gegenstellen und fuehrt ggf. Handshake aus. + Eff.: Sucht im lokalen Netz nach Gegenstellen und fuehrt ggf. Handshake aus. Bei Fehlern ist der Fehler im Dialogfenster "LAN-Suche fehlgeschlagen" oder "Kein Spiel gefunden, warte auf Anfrage" ablesbar. Erg.: - ''' localIp = self.__holeLokaleIp() @@ -282,7 +291,8 @@ def __discoveryWorker(self): def __setzeNetzSocket(self, sock:socket.socket, localTeam:int): ''' Vor.: -sock- ist ein offener Socket, -localTeam- ist eine Team-ID. - Eff.: Uebernimmt die Verbindung, setzt Team/Zustand und startet Empfang. + Eff.: Uebernimmt die Verbindung, setzt Team/Zustand und startet Empfang. Das Brett ist gestartet und auf dem Brett -image- gezeichnet, + wenn kein Element sich ueber dem Brett befindet ist es im Fenster sichtbar. Erg.: - ''' if self.__netzVerbundenEvent.is_set(): @@ -311,7 +321,7 @@ def __starteNetzEmpfang(self): def __receiverWorker(self): ''' Vor.: Netzwerkmodus aktiv und Verbindung gesetzt. - Eff.: Verantwortlich fuer den Empfang von Nachrichten, liest Zeilen und verarbreitet Zuege. + Eff.: Verantwortlich fuer den Empfang von Nachrichten, liest Zeilen und verarbreitet Zuege, dass heißt die Nachricht hat ggf. einen Zugumgesetzt. Erg.: - ''' while self.__netzAktiv and self.__netzVerbundenEvent.is_set(): @@ -336,7 +346,7 @@ def __receiverWorker(self): def __sendeNetzMessage(self, text:str): ''' Vor.: -text- ist eine Nachrichtenzeile ohne Zeilenumbrüche. - Eff.: Sendet die Nachricht an den verbundenen Reechner, falls Verbindung aktiv ist. + Eff.: Sendet die Nachricht an den verbundenen Rechner, falls die Verbindung aktiv ist. Erg.: - ''' if not(self.__netzAktiv) or not(self.__netzVerbundenEvent.is_set()): @@ -348,7 +358,8 @@ def __sendeNetzMessage(self, text:str): def __setzeRemoteZug(self, startLabel:str, targetLabel:str): ''' Vor.: -startLabel- und -targetLabel- sind gueltige Feldbezeichnungen. - Eff.: Wendet einen empfangenen Zug auf das lokale Brett an. + Eff.: Der Zug welcher ensteht, wenn das Feld mit der Bezeichnung -startLabel- und dann dem Feld mit der Bezeichnung -targetLabel- anklickt und das Brett fuer das Team gerade Bewegungsaufforderungen annimmt ist umgesetzt. + Auch ist das Brett -image- aktualisiert. Erg.: - ''' startPos = self.__getClickPosByLabel(startLabel) @@ -364,6 +375,11 @@ def __setzeRemoteZug(self, startLabel:str, targetLabel:str): self.__generateImage() def __getClickPosByLabel(self, fieldLabel:str)->tuple[int, int]|None: + ''' + Vor.: -fieldLabel- ist eine gueltige Feldbezeichnungen. Brett ist ohne zusaetzliche Verschiebung durch nachtraegliche Einwirkung auf dem Bildschirm abgebildet. + Eff.: - + Erg.: Die Mittelpunktkordinate des Feldes auf dem Bildschirm ist als tuple geliefert, wobei der erste Wert fuer die X-Kordinate steht und die zweite fuer die Y-Kordinate. + ''' field = self.__fields.get(fieldLabel) if type(field) != Feld: return None @@ -373,7 +389,7 @@ def __getClickPosByLabel(self, fieldLabel:str)->tuple[int, int]|None: def __setzeRemotePromo(self, fieldLabel:str, pieceName:str): ''' Vor.: -fieldLabel- ist eine gueltige Feldbezeichnung, -pieceName- ein bekannter Figurtyp. - Eff.: Fuehrt eine empfangene Bauernumwandlung auf dem angegebenen Feld aus. + Eff.: Eine empfangene Bauernumwandlung auf dem angegebenen Feld ist ausgefuert und das Brett -image- ist aktualiesiert. Erg.: - ''' field = self.__fields.get(fieldLabel) @@ -411,7 +427,7 @@ def __setzeRemotePromo(self, fieldLabel:str, pieceName:str): def restartGame(self): ''' Vor.: - - Eff.: Setzt den Spielzustand zurueck und startet eine neue Partie. + Eff.: Der Spielzustand ist zurueck gesetzt und eine neue Partie gestartet. Erg.: - ''' self.__reset_game_state() @@ -434,7 +450,7 @@ def getFieldRow(self, RowNumber:int)->list[Feld]: def setupDialogGroup(self): ''' Vor.: - - Eff.: Erstellt Dialog fuer Aktionen. + Eff.: Ein Dialog fuer Aktionen zum Aufgeben/beenden das Spiels in der Variable -self.__resignDialog- ist erstellt und ist der Spritegruppe in der Variable -self.__DialogGroup- hinzugefuegt. Erg.: - ''' self.__DialogGroup.empty() @@ -453,7 +469,8 @@ def setupDialogGroup(self): def __reset_game_state(self): ''' Vor.: - - Eff.: Entfernt Figuren/Feldgruppen, startet Brettzustand und Dialoge neu. + Eff.: Figuren/Feldgruppen sind geleert und die Felder neuinitialisiert. + Das hat meistens Auswirkungen auf das Brett -image- haben kann, weil die dort evtl. befundenen Elemente/Effekte nun nicht mehr dargestellt sind. Erg.: - ''' self.__DialogGroup.empty() @@ -480,7 +497,7 @@ def __reset_game_state(self): def start(self)->None: ''' Vor.: Das Brett ist bereit zum Starten. - Eff.: Setzt das Spiel auf aktiv und wechselt in den Auswahlmodus. + Eff.: das Spiel ist auf aktiv und die Variable -self.__eventMode- ist in den Auswahlmodus gesetzt. Erg.: - ''' if self.__running: @@ -493,10 +510,10 @@ def start(self)->None: def __setupBrett(self)->None: ''' Vor.: Das Feldraster ist initialisiert. - Eff.: Erstellt Figureninstanzen und setzt die Startaufstellung. + Eff.: Figureninstanzen (self.__blackTower, self.__whiteTower, self.__blackQueen, self.__whiteQueen, self.__blackKnight, self.__whiteKnight, self.__blackBishop, self.__whiteBishop, self.__blackKing, self.__whiteKing) + sind beschrieben und die Startaufstellung ist gesetzt. Erg.: - ''' - #Wird später sauberer geschrieben !!! self.__FigureScale:float = 0.9 self.__blackTower = Turm("assets/graphics/s_turm.png", self.__field_length*self.__FigureScale, self.__field_length, self.__fields_count, self.__field_label_start_letter, 1) self.__whiteTower = Turm("assets/graphics/w_turm.png", self.__field_length*self.__FigureScale, self.__field_length, self.__fields_count, self.__field_label_start_letter, 0) @@ -511,7 +528,7 @@ def __setupBrett(self)->None: self.__whiteBishop = Laeufer("assets/graphics/w_laeufer.png", self.__field_length*self.__FigureScale, self.__field_length, self.__fields_count, self.__field_label_start_letter, 0) self.__blackKing = Koenig("assets/graphics/s_koenig.png", self.__field_length*self.__FigureScale, self.__field_length, self.__fields_count, self.__field_label_start_letter, 1) - self.__whiteKing= Koenig("assets/graphics/w_koenig.png", self.__field_length*self.__FigureScale, self.__field_length, self.__fields_count, self.__field_label_start_letter, 0) + self.__whiteKing = Koenig("assets/graphics/w_koenig.png", self.__field_length*self.__FigureScale, self.__field_length, self.__fields_count, self.__field_label_start_letter, 0) self.__fields["a8"].setFigure(self.__blackTower) self.__fields["h8"].setFigure(self.__blackTower) @@ -543,7 +560,7 @@ def __setupBrett(self)->None: def __buildPawnRow(self, RowNumber:int, scale:float, texturePath:str, teamID:int) -> None: ''' Vor.: Parameter beschreiben eine gueltige Reihe und Figurenkonfiguration. - Eff.: Befuellt die angegebene Reihe mit Bauern des Teams. + Eff.: Die angegebene Reihe, mit Bauern des Teams mit der -teamID-, ist in den Datensaetzen befuellt. Das Brett -self.image- ist nicht aktualisiert. Erg.: - ''' for i in range(self.__fields_count): @@ -554,8 +571,8 @@ def __buildPawnRow(self, RowNumber:int, scale:float, texturePath:str, teamID:int def setRotation(self, rotation:int)->None: ''' - Vor.: -rotation- ist ein Vielfaches von 90 Grad. - Eff.: Setzt die Brettrotation und korrigiert Feldpositionen. + Vor.: -rotation- ist ein Vielfaches von 90 Grad oder 0 und Feldobjekte sind bereits vorhanden. + Eff.: Die Brettrotation ist auf -rotation- gesetzt und Feldpositionen sind korriegiert und nun ist das Brett mit der angegebenden Rotation in -self.image- beschrieben. Erg.: - ''' if rotation % 90 != 0: @@ -574,7 +591,7 @@ def getRotation(self)->int: def __correctFieldPositions(self)->None: ''' Vor.: Feldobjekte sind vorhanden. - Eff.: Berechnet und setzt die aktuelle Position aller Felder neu. + Eff.: Berechnet und setzt die aktuelle Position aller Felder neu. Das Brett -self.image- ist nun neugeneriert. Erg.: - ''' for key in self.__fields.keys(): @@ -585,7 +602,7 @@ def __correctFieldPositions(self)->None: def __createFields(self)->dict: ''' Vor.: Brettparameter sind gesetzt. - Eff.: Erstellt alle Feldobjekte. + Eff.: Die Feldobjekte sind erstellt. Erg.: Ein Dictionary mit Feldlabeln als Keys und Feldobjekten als Werten ist geliefert. ''' fields:dict = {} @@ -603,8 +620,8 @@ def __createFields(self)->dict: def __getFieldPositionByName(self, field_name:str)->tuple[int, int]: ''' Vor.: -field_name- ist eine gültige Feldbezeichnung. - Eff.: Berechnet die Pixelposition des Feldes mit Rotation. - Erg.: Die Feldposition als Tupel (x, y) ist geliefert. + Eff.: - + Erg.: Die Feldposition innerhalb des Brettbildes ist als Tupel (x, y) ist geliefert. ''' lengthID = int(ord(field_name[0])-ord(self.__field_label_start_letter)) spreadID = int(field_name[1]) @@ -623,9 +640,9 @@ def __getFieldPositionByName(self, field_name:str)->tuple[int, int]: def __rotatePoint90degree(self, point:tuple[int, int])->tuple[int, int]: ''' - Vor.: -point- ist eine Koordinate im Brettsystem. + Vor.: -point- ist eine Koordinate als Tuple (x, y) im Brettsystem. Eff.: - - Erg.: Die um 90 Grad gedrehte Koordinate ist geliefert. + Erg.: Die Koordinate, welche die Position beschreibt, wenn das Brett um 90 Grad gedreht ist, ist geliefert. ''' return (self.__edge_length-point[1]-self.__field_length, point[0]) @@ -633,15 +650,15 @@ def __getFieldsGroup(self)->pygame.sprite.Group: ''' Vor.: - Eff.: - - Erg.: Die Sprite-Gruppe aller Felder ist geliefert. + Erg.: Die Sprite-Gruppe der Felder ist geliefert (-self.__fieldsGroup-). ''' return self.__fieldsGroup def __createFieldsGroup(self)->pygame.sprite.Group: ''' - Vor.: Feldobjekte sind erstellt. - Eff.: Fuegt alle Felder in eine neue Sprite-Gruppe ein. - Erg.: Die erzeugte Feldgruppe ist geliefert. + Vor.: Feldobjekte sind korrekt erstellt. self.__fields sind allle Werte Sprites. + Eff.: - + Erg.: Eine erzeugte Sprite.Group mit allen Sprites aus dem Werten von -self.__fields- ist geliefert. ''' fieldsGroup = pygame.sprite.Group() @@ -654,7 +671,7 @@ def __createFieldsGroup(self)->pygame.sprite.Group: def __generateImage(self) -> None: ''' Vor.: Brettoberflaeche und Zeichenobjekte sind initialisiert. - Eff.: Rendert Felder und sichtbare Dialoge auf die Brettoberflaeche. + Eff.: Momentan sichtbare Felder und sichtbare Dialoge auf der Brettoberflaeche sind in -self.image- als Surface beschrieben. Erg.: - ''' self.image.fill("black") @@ -672,8 +689,8 @@ def __generateImage(self) -> None: def update(self) -> None: ''' - Vor.: - - Eff.: Aktualisiert aktive UI-Elemente. + Vor.: -self.__nameDialog- ist None oder eine TextDialoginstanz. + Eff.: Wenn -self.__nameDialog- nicht None entspricht ist der TextDialog -self.__nameDialog- aktualisiert. Erg.: - ''' if self.__nameDialog != None: @@ -683,14 +700,15 @@ def __CheckIfIsNotAFeldInstance(self, testObject:object) ->bool: ''' Vor.: -testObject- ist ein beliebiges Objekt. Eff.: - - Erg.: -True- falls -testObject- kein Feld ist, sonst -False-. + Erg.: falls -testObject- kein Feld ist -True- geliefert, sonst ist -False- geliefert. ''' return type(testObject) != Feld def handleLeftClickEvent(self, pos:tuple[int, int])->None: ''' - Vor.: -pos- ist eine gueltige Mausposition in Pixeln. - Eff.: Verarbeitet Linksklicks fuer Dialoge und Figuren je nach Modus. + Vor.: -pos- ist eine gueltige Mausposition in Pixeln und das Brett ist auf dem Screen ohne zusaetzliche Verschiebungen ausgegeben. + Eff.: Ein Klick an der Position -pos- ist als Linksklick verarbeitet, dadurch konnten Figuren fuer ein Zug gesetzt, ausgewehlt werden, mit einen Dialogfenster interagiert werden oder auch ein Hinweis ausgeben werden, alls das ist ggf. abhaengig von -self.__eventMode- + self.image ist aktualiesiert. Erg.: - ''' if self.__modusDialog != None and self.__modusDialog.getIfShown(): @@ -744,7 +762,7 @@ def handleLeftClickEvent(self, pos:tuple[int, int])->None: def handleRightClickEvent(self, pos:tuple[int, int])->None: ''' Vor.: -pos- ist eine gueltige Mausposition in Pixeln. - Eff.: Oeffnet den Ingame-Dialog. + Eff.: Oeffnet den Ingame-Dialog. Die Brett.image Surface ist aktualiesiert. Erg.: - ''' if not(self.__ready): @@ -756,7 +774,7 @@ def handleRightClickEvent(self, pos:tuple[int, int])->None: def handleKeyDownEvent(self, event:pygame.event.Event)->None: ''' Vor.: -event- ist ein gueltiges pygame KEYDOWN-Event. - Eff.: Leitet Tastatureingaben an den Nameneingabedialog weiter. + Eff.: Die Tastatureingaben ist an den Nameneingabedialog weitergeleitet, wenn dieser geofnet ist. Damit der Buchstabe korrekt in der Namenseingabe beim Nameneingabefeld angezeigt werden kann. Erg.: - ''' if self.__nameDialog != None and self.__nameDialog.getIfShown(): @@ -765,7 +783,7 @@ def handleKeyDownEvent(self, event:pygame.event.Event)->None: def __resetCursorAndSetEventMode(self, eventMode:str): ''' Vor.: -eventMode- ist ein gueltiger Eventmodus. - Eff.: Setzt den Cursor zurueck und aktiviert den uebergebenen Modus. + Eff.: der Cursor (self.__cursor) ist zurueck gesetzt und setzt den uebergebenen Eventmodus fuer Eingaben um an der Variable (self.__eventMode). Erg.: - ''' self.__cursor = None @@ -776,6 +794,22 @@ def __getMatchingTurnData(self, startField:Feld, targetField:Feld)->dict|None: Vor.: -startField- und -targetField- sind gueltige Felder. Eff.: - Erg.: Das passende ZugdatenDictionary oder -None- ist geliefert. + Das evtl. gelieferte passende ZugdatenDictionary besteht aus: + "" steht fuer den Key und das hinter dem = fuer die Erklaerung des Values + "point" = :tuple[int, int] (gueltige Feldbezeichnung, die relativ zu -originFieldLabel- die Anzahl der Felder angibt, die fuer einen Zug in Buchstaben und Zahlen Richtung noetig sind) + "fieldLabel" = :str eine gueltige Feldbezeichnung (das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl / das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl) zu dem sich die Figur bewegen soll. + "onlyOnKill" = :bool (-True-, wenn die Figur nur den Zug machen kann, wenn sie dabei eine andere Figur schlagen wuerde) + "canKill" = :bool (-True-, wenn die Figur beim Zug auf das Zielfeld eine andere Figur schlagen koennte) + "killMaybeFigureType" = (Klasse der Figur, die geschlagen werden kann) + "killMaybeFigureField" = :str (Feldbezeichnung des Feldes, auf dem eine Figur geschlagen werden kann) + "killMaybeFigureMustHadDoubleWalkLastTurn" = :bool (beschreibt, ob die Figur, die geschlagen werden kann, im letzten Zug einen Doppelzug gemacht hat) + "hasAnxiety" = :boool oder immer True, wenn die Figur die Koenigsrolle traegt (beschreibt, ob die Figur auf dem Zeilfeld des Zuges geschlagen werden koennte) + "specialTurnType" = :str (spezielle Zugbezeichnung) + "needFigureOnField" = :str (Zug ist nur moeglich, wenn auf dem Feld eine Figur steht und ein anderes Feld angegeben ist) + "neededFigureType" = (ueberprueft, ob der Zug einer bestimmten hier angegebender Figur als Figurklasse mit der Figur auf dem Feld uebereinstimmt) + "allowNeededFigureHasTurned" = :bool (beschreibt, ob sie die Figur auf dem Feld -needFigureOnField- schon bewegt hat) + "endPointNeededFigure" = :str (Feldbezeichnung, auf dem die Firgur nach dem Zug steht) + "onDoneTurnCall" = :Callable (beschreibt die Methode/Funktion, die nach dem Zug ausgefuerht wird) ''' FullTurnDataFromStartField:list[dict] = self.getPossibleTurnFieldsFullData(startField) for Data in FullTurnDataFromStartField: @@ -786,7 +820,12 @@ def __getMatchingTurnData(self, startField:Feld, targetField:Feld)->dict|None: def __setFigureEvent(self, clickedField:Feld)->None: ''' Vor.: -clickedField- ist ein gueltiges Feldobjekt. - Eff.: Fuehrt einen Zug aus, inklusive Spezialregeln, Übertragung und Zugabschluss wird übertragen. + Eff.: Das setzen eines Zuges ist ausgefuehrt, wenn das -clickedField- ein gueltiger Zug, fuer die Figur des auf dem Feld, welches in -self.__cursor- gespeichert, ist. + Wenn sich das Brett im Netzwerkbetrieb befindet ist die Bewegung an den anderen Rechner weitergeleitet. + Nach dem Zug ist der self.__eventMode zu "chooseFigure" geändert und der andere Spieler ist an der Reihe, außer das Spiel ist durch den Zug beendent, weil ein Patt oder ein Matt ausgeloest wurde. + In diesem Fall ist die Situation in der Konsole beschrieben und beim Matt sind alle Felder einzeln umrandet und mit einem gruenen Punkt versehen. + Bei einer Pattsituation sind nur alle Felder, jeweils grün eingeramt. + Auch ist, dann das Brett.image aktualiesiert. Erg.: - ''' self.__clearAllFieldHighlights() @@ -858,7 +897,8 @@ def __setFigureEvent(self, clickedField:Feld)->None: def __promoteTower(self): ''' Vor.: Ein Bauer steht zur Umwandlung bereit. - Eff.: Wandelt den Bauer in einen Turm um. + Eff.: Der Bauer, welcher in der Liste der zu Umwandelbarenbauern hinten steht ist in einen Turm, des seines gleichen Teams, umgewandelt. + Und der andere Spieler ist an der Reihe. Erg.: - ''' self.__doPromote(self.__whiteTower, self.__blackTower, "TURM") @@ -866,7 +906,8 @@ def __promoteTower(self): def __promoteBishop(self): ''' Vor.: Ein Bauer steht zur Umwandlung bereit. - Eff.: Wandelt den Bauer in einen Laeufer um. + Eff.: Der Bauer, welcher in der Liste der zu Umwandelbarenbauern hinten steht ist in einen Laeufer, des seines gleichen Teams, umgewandelt. + Und der andere Spieler ist an der Reihe. Erg.: - ''' self.__doPromote(self.__whiteBishop, self.__blackBishop, "LAEUFER") @@ -874,7 +915,8 @@ def __promoteBishop(self): def __promoteKnight(self): ''' Vor.: Ein Bauer steht zur Umwandlung bereit. - Eff.: Wandelt den Bauer in einen Springer um. + Eff.: Der Bauer, welcher in der Liste der zu Umwandelbarenbauern hinten steht ist in einen Springer, des seines gleichen Teams, umgewandelt. + Und der andere Spieler ist an der Reihe. Erg.: - ''' self.__doPromote(self.__whiteKnight, self.__blackKnight, "SPRINGER") @@ -882,15 +924,18 @@ def __promoteKnight(self): def __promoteQueen(self): ''' Vor.: Ein Bauer steht zur Umwandlung bereit. - Eff.: Wandelt den Bauer in eine Dame um. + Eff.: Der Bauer, welcher in der Liste der zu Umwandelbarenbauern hinten steht ist in einen Dame, des seines gleichen Teams, umgewandelt. + Und der andere Spieler ist an der Reihe. Erg.: - ''' self.__doPromote(self.__whiteQueen, self.__blackQueen, "DAME") def __doPromote(self, Team0Figure, Team1Figure, promotionName:str): ''' + HIER MIT SPEZIFIZIERUNG UEBERARBEITEN WEITERMACHEN!!!!! Vor.: Es existiert ein aktiver Promotion-Kontext in der Liste -__PawnPromotes-. - Eff.: Ersetzt den Bauer durch die Teamfigur, schliesst den Dialog und sendet wenn im Netzwerk Netzwerkdaten. + Eff.: Der Bauer, welcher in der Liste der zu Umwandelbarenbauern hinten steht ist in eine , des seines gleichen Teams, umgewandelt. + Und der andere Spieler ist an der Reihe. Erg.: - ''' promoteData = self.__PawnPromotes[-1] diff --git a/Dame.py b/Dame.py index 7613d2f..f0a438d 100644 --- a/Dame.py +++ b/Dame.py @@ -24,23 +24,23 @@ def getMaybePossibleTurns(self, originFieldLabel:str)->list[dict]: ''' Vor.: -originFieldLabel- ist eine gueltige Schachfeldbezeichnung, mit einer Laenge von 2. Das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl. Das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl. Eff.: - - Erg.: Ein Dictionary mit folgenden Daten ist geliefert. Den Koordinaten des Punktes, auf der die Figur steht. Den Angaben, ob das Schlagen einer anderen Figur moeglich ist (mit Angabe der anderen Figur, des Feldes und ob ein Doppelzug geschehen ist). Ob die Figur von einer anderen Figur bedroht wird. Ob die Figur besondere Zuege hat und ob der Zug erlaubt ist. + Erg.: Eine Liste ggf. aus Tabellen ist geliefert. Sie beschreibt unter welchen Bedingungen ein bestimmter Zug durchgefuehrt werden kann. Die ggf. in Liste vorkommenden Tabellen bestehen in diesem Fall aus ("" sind die Keys und das hinter dem = die Werte): - "point" = -RelativePoint- (gueltige Feldbezeichnung, die relativ zu -originFieldLabel- die Anzahl der Felder angibt, die fuer einen Zug in Buchstaben und Zahlen Richtung noetig sind) - "fieldLabel" eine gueltige Feldbezeichnung (das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl / das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl) zu dem sich die Figur bewegen soll. - "onlyOnKill" = -onlyOnKill- (-True-, wenn die Figur nur den Zug machen kann, wenn sie dabei eine andere Figur schlagen wuerde) - "canKill" = -canKill- (-True-, wenn die Figur beim Zug auf das Zielfeld eine andere Figur schlagen koennte) - "killMaybeFigureType" = -killMaybeFigureType- (Typ der Figur, die geschlagen werden kann) - "killMaybeFigureField" = -killMaybeFigureField- (Feldbezeichnung des Feldes, auf dem eine Figur geschlagen werden kann) - "killMaybeFigureMustHadDoubleWalkLastTurn" = -killMaybeFigureMustHadDoubleWalkLastTurn- (beschreibt, ob die Figur, die geschlagen werden kann, im letzten Zug einen Doppelzug gemacht hat) - "hasAnxiety" = -hasAnxiety- oder immer True, wenn die Figur die Koenigsrolle traegt (beschreibt, ob die Figur auf dem Zeilfeld des Zuges geschlagen werden koennte) - "specialTurnType" = -specialMoveLabel- (spezielle Zugbezeichnung) - "needFigureOnField" = -needFigureOnField- (Zug ist nur moeglich, wenn auf dem Feld eine Figur steht und ein anderes Feld angegeben ist) - "neededFigureType" = -needFigureType- (ueberprueft, ob der Zug einer bestimmten Figur mit der Figur auf dem Feld uebereinstimmt) - "allowNeededFigureHasTurned" = allowNeededFigureHasTurned (beschreibt, ob sie die Figur auf dem Feld -needFigureOnField- schon bewegt hat) - "endPointNeededFigure" = endPointNeededFigure (Feld, auf dem die Firgur nach dem Zug steht) - "onDoneTurnCall" = onDoneTurnCall (beschreibt die Methode/Funktion, die nach dem Zug ausgefuerht wird) + "point" = :tuple[int, int] (gueltige Feldbezeichnung, die relativ zu -originFieldLabel- die Anzahl der Felder angibt, die fuer einen Zug in Buchstaben und Zahlen Richtung noetig sind) + "fieldLabel" = :str eine gueltige Feldbezeichnung (das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl / das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl) zu dem sich die Figur bewegen soll. + "onlyOnKill" = :bool (-True-, wenn die Figur nur den Zug machen kann, wenn sie dabei eine andere Figur schlagen wuerde) + "canKill" = :bool (-True-, wenn die Figur beim Zug auf das Zielfeld eine andere Figur schlagen koennte) + "killMaybeFigureType" = (Klasse der Figur, die geschlagen werden kann) + "killMaybeFigureField" = :str (Feldbezeichnung des Feldes, auf dem eine Figur geschlagen werden kann) + "killMaybeFigureMustHadDoubleWalkLastTurn" = :bool (beschreibt, ob die Figur, die geschlagen werden kann, im letzten Zug einen Doppelzug gemacht hat) + "hasAnxiety" = :boool oder immer True, wenn die Figur die Koenigsrolle traegt (beschreibt, ob die Figur auf dem Zeilfeld des Zuges geschlagen werden koennte) + "specialTurnType" = :str (spezielle Zugbezeichnung) + "needFigureOnField" = :str (Zug ist nur moeglich, wenn auf dem Feld eine Figur steht und ein anderes Feld angegeben ist) + "neededFigureType" = (ueberprueft, ob der Zug einer bestimmten hier angegebender Figur als Figurklasse mit der Figur auf dem Feld uebereinstimmt) + "allowNeededFigureHasTurned" = :bool (beschreibt, ob sie die Figur auf dem Feld -needFigureOnField- schon bewegt hat) + "endPointNeededFigure" = :str (Feldbezeichnung, auf dem die Firgur nach dem Zug steht) + "onDoneTurnCall" = :Callable (beschreibt die Methode/Funktion, die nach dem Zug ausgefuerht wird) ''' possibleZuege = [] for i in range(1, 8): diff --git a/Springer.py b/Springer.py index a2b7ddc..3572e7f 100644 --- a/Springer.py +++ b/Springer.py @@ -29,23 +29,23 @@ def getMaybePossibleTurns(self, originFieldLabel:str)->list[dict]: ''' Vor.: -originFieldLabel- ist eine gueltige Schachfeldbezeichnung, mit einer Laenge von 2. Das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl. Das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl. Eff.: - - Erg.: Ein Dictionary mit folgenden Daten ist geliefert. Den Koordinaten des Punktes, auf der die Figur steht. Den Angaben, ob das Schlagen einer anderen Figur moeglich ist (mit Angabe der anderen Figur, des Feldes und ob ein Doppelzug geschehen ist). Ob die Figur von einer anderen Figur bedroht wird. Ob die Figur besondere Zuege hat und ob der Zug erlaubt ist. + Erg.: Eine Liste ggf. aus Tabellen ist geliefert. Sie beschreibt unter welchen Bedingungen ein bestimmter Zug durchgefuehrt werden kann. Die ggf. in Liste vorkommenden Tabellen bestehen in diesem Fall aus ("" sind die Keys und das hinter dem = die Werte): - "point" = -RelativePoint- (gueltige Feldbezeichnung, die relativ zu -originFieldLabel- die Anzahl der Felder angibt, die fuer einen Zug in Buchstaben und Zahlen Richtung noetig sind) - "fieldLabel" eine gueltige Feldbezeichnung (das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl / das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl) zu dem sich die Figur bewegen soll. - "onlyOnKill" = -onlyOnKill- (-True-, wenn die Figur nur den Zug machen kann, wenn sie dabei eine andere Figur schlagen wuerde) - "canKill" = -canKill- (-True-, wenn die Figur beim Zug auf das Zielfeld eine andere Figur schlagen koennte) - "killMaybeFigureType" = -killMaybeFigureType- (Typ der Figur, die geschlagen werden kann) - "killMaybeFigureField" = -killMaybeFigureField- (Feldbezeichnung des Feldes, auf dem eine Figur geschlagen werden kann) - "killMaybeFigureMustHadDoubleWalkLastTurn" = -killMaybeFigureMustHadDoubleWalkLastTurn- (beschreibt, ob die Figur, die geschlagen werden kann, im letzten Zug einen Doppelzug gemacht hat) - "hasAnxiety" = -hasAnxiety- oder immer True, wenn die Figur die Koenigsrolle traegt (beschreibt, ob die Figur auf dem Zeilfeld des Zuges geschlagen werden koennte) - "specialTurnType" = -specialMoveLabel- (spezielle Zugbezeichnung) - "needFigureOnField" = -needFigureOnField- (Zug ist nur moeglich, wenn auf dem Feld eine Figur steht und ein anderes Feld angegeben ist) - "neededFigureType" = -needFigureType- (ueberprueft, ob der Zug einer bestimmten Figur mit der Figur auf dem Feld uebereinstimmt) - "allowNeededFigureHasTurned" = allowNeededFigureHasTurned (beschreibt, ob sie die Figur auf dem Feld -needFigureOnField- schon bewegt hat) - "endPointNeededFigure" = endPointNeededFigure (Feld, auf dem die Firgur nach dem Zug steht) - "onDoneTurnCall" = onDoneTurnCall (beschreibt die Methode/Funktion, die nach dem Zug ausgefuerht wird) + "point" = :tuple[int, int] (gueltige Feldbezeichnung, die relativ zu -originFieldLabel- die Anzahl der Felder angibt, die fuer einen Zug in Buchstaben und Zahlen Richtung noetig sind) + "fieldLabel" = :str eine gueltige Feldbezeichnung (das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl / das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl) zu dem sich die Figur bewegen soll. + "onlyOnKill" = :bool (-True-, wenn die Figur nur den Zug machen kann, wenn sie dabei eine andere Figur schlagen wuerde) + "canKill" = :bool (-True-, wenn die Figur beim Zug auf das Zielfeld eine andere Figur schlagen koennte) + "killMaybeFigureType" = (Klasse der Figur, die geschlagen werden kann) + "killMaybeFigureField" = :str (Feldbezeichnung des Feldes, auf dem eine Figur geschlagen werden kann) + "killMaybeFigureMustHadDoubleWalkLastTurn" = :bool (beschreibt, ob die Figur, die geschlagen werden kann, im letzten Zug einen Doppelzug gemacht hat) + "hasAnxiety" = :boool oder immer True, wenn die Figur die Koenigsrolle traegt (beschreibt, ob die Figur auf dem Zeilfeld des Zuges geschlagen werden koennte) + "specialTurnType" = :str (spezielle Zugbezeichnung) + "needFigureOnField" = :str (Zug ist nur moeglich, wenn auf dem Feld eine Figur steht und ein anderes Feld angegeben ist) + "neededFigureType" = (ueberprueft, ob der Zug einer bestimmten hier angegebender Figur als Figurklasse mit der Figur auf dem Feld uebereinstimmt) + "allowNeededFigureHasTurned" = :bool (beschreibt, ob sie die Figur auf dem Feld -needFigureOnField- schon bewegt hat) + "endPointNeededFigure" = :str (Feldbezeichnung, auf dem die Firgur nach dem Zug steht) + "onDoneTurnCall" = :Callable (beschreibt die Methode/Funktion, die nach dem Zug ausgefuerht wird) ''' possibleZuege = [] diff --git a/Turm.py b/Turm.py index 23f2897..2fd500a 100644 --- a/Turm.py +++ b/Turm.py @@ -24,23 +24,23 @@ def getMaybePossibleTurns(self, originFieldLabel:str)->list[dict]: ''' Vor.: -originFieldLabel- ist eine gueltige Schachfeldbezeichnung, mit einer Laenge von 2. Das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl. Das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl. Eff.: - - Erg.: Ein Dictionary mit folgenden Daten ist geliefert. Den Koordinaten des Punktes, auf der die Figur steht. Den Angaben, ob das Schlagen einer anderen Figur moeglich ist (mit Angabe der anderen Figur, des Feldes und ob ein Doppelzug geschehen ist). Ob die Figur von einer anderen Figur bedroht wird. Ob die Figur besondere Zuege hat und ob der Zug erlaubt ist. + Erg.: Eine Liste ggf. aus Tabellen ist geliefert. Sie beschreibt unter welchen Bedingungen ein bestimmter Zug durchgefuehrt werden kann. Die ggf. in Liste vorkommenden Tabellen bestehen in diesem Fall aus ("" sind die Keys und das hinter dem = die Werte): - "point" = -RelativePoint- (gueltige Feldbezeichnung, die relativ zu -originFieldLabel- die Anzahl der Felder angibt, die fuer einen Zug in Buchstaben und Zahlen Richtung noetig sind) - "fieldLabel" eine gueltige Feldbezeichnung (das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl / das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl) zu dem sich die Figur bewegen soll. - "onlyOnKill" = -onlyOnKill- (-True-, wenn die Figur nur den Zug machen kann, wenn sie dabei eine andere Figur schlagen wuerde) - "canKill" = -canKill- (-True-, wenn die Figur beim Zug auf das Zielfeld eine andere Figur schlagen koennte) - "killMaybeFigureType" = -killMaybeFigureType- (Typ der Figur, die geschlagen werden kann) - "killMaybeFigureField" = -killMaybeFigureField- (Feldbezeichnung des Feldes, auf dem eine Figur geschlagen werden kann) - "killMaybeFigureMustHadDoubleWalkLastTurn" = -killMaybeFigureMustHadDoubleWalkLastTurn- (beschreibt, ob die Figur, die geschlagen werden kann, im letzten Zug einen Doppelzug gemacht hat) - "hasAnxiety" = -hasAnxiety- oder immer True, wenn die Figur die Koenigsrolle traegt (beschreibt, ob die Figur auf dem Zeilfeld des Zuges geschlagen werden koennte) - "specialTurnType" = -specialMoveLabel- (spezielle Zugbezeichnung) - "needFigureOnField" = -needFigureOnField- (Zug ist nur moeglich, wenn auf dem Feld eine Figur steht und ein anderes Feld angegeben ist) - "neededFigureType" = -needFigureType- (ueberprueft, ob der Zug einer bestimmten Figur mit der Figur auf dem Feld uebereinstimmt) - "allowNeededFigureHasTurned" = allowNeededFigureHasTurned (beschreibt, ob sie die Figur auf dem Feld -needFigureOnField- schon bewegt hat) - "endPointNeededFigure" = endPointNeededFigure (Feld, auf dem die Firgur nach dem Zug steht) - "onDoneTurnCall" = onDoneTurnCall (beschreibt die Methode/Funktion, die nach dem Zug ausgefuerht wird) + "point" = :tuple[int, int] (gueltige Feldbezeichnung, die relativ zu -originFieldLabel- die Anzahl der Felder angibt, die fuer einen Zug in Buchstaben und Zahlen Richtung noetig sind) + "fieldLabel" = :str eine gueltige Feldbezeichnung (das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl / das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl) zu dem sich die Figur bewegen soll. + "onlyOnKill" = :bool (-True-, wenn die Figur nur den Zug machen kann, wenn sie dabei eine andere Figur schlagen wuerde) + "canKill" = :bool (-True-, wenn die Figur beim Zug auf das Zielfeld eine andere Figur schlagen koennte) + "killMaybeFigureType" = (Klasse der Figur, die geschlagen werden kann) + "killMaybeFigureField" = :str (Feldbezeichnung des Feldes, auf dem eine Figur geschlagen werden kann) + "killMaybeFigureMustHadDoubleWalkLastTurn" = :bool (beschreibt, ob die Figur, die geschlagen werden kann, im letzten Zug einen Doppelzug gemacht hat) + "hasAnxiety" = :boool oder immer True, wenn die Figur die Koenigsrolle traegt (beschreibt, ob die Figur auf dem Zeilfeld des Zuges geschlagen werden koennte) + "specialTurnType" = :str (spezielle Zugbezeichnung) + "needFigureOnField" = :str (Zug ist nur moeglich, wenn auf dem Feld eine Figur steht und ein anderes Feld angegeben ist) + "neededFigureType" = (ueberprueft, ob der Zug einer bestimmten hier angegebender Figur als Figurklasse mit der Figur auf dem Feld uebereinstimmt) + "allowNeededFigureHasTurned" = :bool (beschreibt, ob sie die Figur auf dem Feld -needFigureOnField- schon bewegt hat) + "endPointNeededFigure" = :str (Feldbezeichnung, auf dem die Firgur nach dem Zug steht) + "onDoneTurnCall" = :Callable (beschreibt die Methode/Funktion, die nach dem Zug ausgefuerht wird) ''' possibleZuege = [] From 932e77015e1a2448be17be434ef9a6dd0c5da14a Mon Sep 17 00:00:00 2001 From: Traumi-Schlumpf Date: Tue, 3 Mar 2026 22:52:11 +0100 Subject: [PATCH 4/9] Standardize docstrings for move descriptors Update docstrings in Bauer.py, Koenig.py and Laeufer.py to standardize the move descriptor schema: replace various informal/ambiguous type notations with explicit annotations (e.g. "point" -> :tuple[int, int], "fieldLabel" -> :str, "onlyOnKill"/:"canKill" -> :bool, "onDoneTurnCall" -> :Callable). Clarify several fields (killMaybeFigureType, killMaybeFigureField, neededFigureType, endPointNeededFigure), fix small typos (e.g. boool -> bool) and unify formatting and wording across the three classes so the expected structure of possible moves is consistent and clearer for developers. --- Bauer.py | 2 +- Koenig.py | 30 +++++++++++++++--------------- Laeufer.py | 30 +++++++++++++++--------------- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/Bauer.py b/Bauer.py index 257c7ee..25ccca0 100644 --- a/Bauer.py +++ b/Bauer.py @@ -43,7 +43,7 @@ def getMaybePossibleTurns(self, originFieldLabel:str)->list[dict]: Erg.: Eine Liste ggf. aus Tabellen ist geliefert. Sie beschreibt unter welchen Bedingungen ein bestimmter Zug durchgefuehrt werden kann. Die ggf. in Liste vorkommenden Tabellen bestehen in diesem Fall aus ("" sind die Keys und das hinter dem = die Werte): - "point" = :[int, int] (gueltige Feldbezeichnung, die relativ zu -originFieldLabel- die Anzahl der Felder angibt, die fuer einen Zug in Buchstaben und Zahlen Richtung noetig sind) + "point" = :tuple[int, int] (gueltige Feldbezeichnung, die relativ zu -originFieldLabel- die Anzahl der Felder angibt, die fuer einen Zug in Buchstaben und Zahlen Richtung noetig sind) "fieldLabel" = :str eine gueltige Feldbezeichnung (das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl / das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl) zu dem sich die Figur bewegen soll. "onlyOnKill" = :bool (-True-, wenn die Figur nur den Zug machen kann, wenn sie dabei eine andere Figur schlagen wuerde) "canKill" = :bool (-True-, wenn die Figur beim Zug auf das Zielfeld eine andere Figur schlagen koennte) diff --git a/Koenig.py b/Koenig.py index d084212..0e03458 100644 --- a/Koenig.py +++ b/Koenig.py @@ -30,23 +30,23 @@ def getMaybePossibleTurns(self, originFieldLabel:str)->list[dict]: ''' Vor.: -originFieldLabel- ist eine gueltige Schachfeldbezeichnung, mit einer Laenge von 2. Das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl. Das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl. Eff.: - - Erg.: Ein Dictionary mit folgenden Daten ist geliefert. Den Koordinaten des Punktes, auf der die Figur steht. Den Angaben, ob das Schlagen einer anderen Figur moeglich ist (mit Angabe der anderen Figur, des Feldes und ob ein Doppelzug geschehen ist). Ob die Figur von einer anderen Figur bedroht wird. Ob die Figur besondere Zuege hat und ob der Zug erlaubt ist. + Erg.: Eine Liste ggf. aus Tabellen ist geliefert. Sie beschreibt unter welchen Bedingungen ein bestimmter Zug durchgefuehrt werden kann. Die ggf. in Liste vorkommenden Tabellen bestehen in diesem Fall aus ("" sind die Keys und das hinter dem = die Werte): - "point" = -RelativePoint- (gueltige Feldbezeichnung, die relativ zu -originFieldLabel- die Anzahl der Felder angibt, die fuer einen Zug in Buchstaben und Zahlen Richtung noetig sind) - "fieldLabel" eine gueltige Feldbezeichnung (das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl / das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl) zu dem sich die Figur bewegen soll. - "onlyOnKill" = -onlyOnKill- (-True-, wenn die Figur nur den Zug machen kann, wenn sie dabei eine andere Figur schlagen wuerde) - "canKill" = -canKill- (-True-, wenn die Figur beim Zug auf das Zielfeld eine andere Figur schlagen koennte) - "killMaybeFigureType" = -killMaybeFigureType- (Typ der Figur, die geschlagen werden kann) - "killMaybeFigureField" = -killMaybeFigureField- (Feldbezeichnung des Feldes, auf dem eine Figur geschlagen werden kann) - "killMaybeFigureMustHadDoubleWalkLastTurn" = -killMaybeFigureMustHadDoubleWalkLastTurn- (beschreibt, ob die Figur, die geschlagen werden kann, im letzten Zug einen Doppelzug gemacht hat) - "hasAnxiety" = -hasAnxiety- oder immer True, wenn die Figur die Koenigsrolle traegt (beschreibt, ob die Figur auf dem Zeilfeld des Zuges geschlagen werden koennte) - "specialTurnType" = -specialMoveLabel- (spezielle Zugbezeichnung) - "needFigureOnField" = -needFigureOnField- (Zug ist nur moeglich, wenn auf dem Feld eine Figur steht und ein anderes Feld angegeben ist) - "neededFigureType" = -needFigureType- (ueberprueft, ob der Zug einer bestimmten Figur mit der Figur auf dem Feld uebereinstimmt) - "allowNeededFigureHasTurned" = allowNeededFigureHasTurned (beschreibt, ob sie die Figur auf dem Feld -needFigureOnField- schon bewegt hat) - "endPointNeededFigure" = endPointNeededFigure (Feld, auf dem die Firgur nach dem Zug steht) - "onDoneTurnCall" = onDoneTurnCall (beschreibt die Methode/Funktion, die nach dem Zug ausgefuerht wird) + "point" = :tuple[int, int] (gueltige Feldbezeichnung, die relativ zu -originFieldLabel- die Anzahl der Felder angibt, die fuer einen Zug in Buchstaben und Zahlen Richtung noetig sind) + "fieldLabel" = :str eine gueltige Feldbezeichnung (das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl / das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl) zu dem sich die Figur bewegen soll. + "onlyOnKill" = :bool (-True-, wenn die Figur nur den Zug machen kann, wenn sie dabei eine andere Figur schlagen wuerde) + "canKill" = :bool (-True-, wenn die Figur beim Zug auf das Zielfeld eine andere Figur schlagen koennte) + "killMaybeFigureType" = (Klasse der Figur, die geschlagen werden kann) + "killMaybeFigureField" = :str (Feldbezeichnung des Feldes, auf dem eine Figur geschlagen werden kann) + "killMaybeFigureMustHadDoubleWalkLastTurn" = :bool (beschreibt, ob die Figur, die geschlagen werden kann, im letzten Zug einen Doppelzug gemacht hat) + "hasAnxiety" = :boool oder immer True, wenn die Figur die Koenigsrolle traegt (beschreibt, ob die Figur auf dem Zeilfeld des Zuges geschlagen werden koennte) + "specialTurnType" = :str (spezielle Zugbezeichnung) + "needFigureOnField" = :str (Zug ist nur moeglich, wenn auf dem Feld eine Figur steht und ein anderes Feld angegeben ist) + "neededFigureType" = (ueberprueft, ob der Zug einer bestimmten hier angegebender Figur als Figurklasse mit der Figur auf dem Feld uebereinstimmt) + "allowNeededFigureHasTurned" = :bool (beschreibt, ob sie die Figur auf dem Feld -needFigureOnField- schon bewegt hat) + "endPointNeededFigure" = :str (Feldbezeichnung, auf dem die Firgur nach dem Zug steht) + "onDoneTurnCall" = :Callable (beschreibt die Methode/Funktion, die nach dem Zug ausgefuerht wird) ''' possibleTurns = [] for i in range(-1, 2, 1): diff --git a/Laeufer.py b/Laeufer.py index 54c1b06..b8e0822 100644 --- a/Laeufer.py +++ b/Laeufer.py @@ -24,23 +24,23 @@ def getMaybePossibleTurns(self, originFieldLabel:str)->list[dict]: ''' Vor.: -originFieldLabel- ist eine gueltige Schachfeldbezeichnung, mit einer Laenge von 2. Das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl. Das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl. Eff.: - - Erg.: Ein Dictionary mit folgenden Daten ist geliefert. Den Koordinaten des Punktes, auf der die Figur steht. Den Angaben, ob das Schlagen einer anderen Figur moeglich ist (mit Angabe der anderen Figur, des Feldes und ob ein Doppelzug geschehen ist). Ob die Figur von einer anderen Figur bedroht wird. Ob die Figur besondere Zuege hat und ob der Zug erlaubt ist. + Erg.: Eine Liste ggf. aus Tabellen ist geliefert. Sie beschreibt unter welchen Bedingungen ein bestimmter Zug durchgefuehrt werden kann. Die ggf. in Liste vorkommenden Tabellen bestehen in diesem Fall aus ("" sind die Keys und das hinter dem = die Werte): - "point" = -RelativePoint- (gueltige Feldbezeichnung, die relativ zu -originFieldLabel- die Anzahl der Felder angibt, die fuer einen Zug in Buchstaben und Zahlen Richtung noetig sind) - "fieldLabel" eine gueltige Feldbezeichnung (das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl / das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl) zu dem sich die Figur bewegen soll. - "onlyOnKill" = -onlyOnKill- (-True-, wenn die Figur nur den Zug machen kann, wenn sie dabei eine andere Figur schlagen wuerde) - "canKill" = -canKill- (-True-, wenn die Figur beim Zug auf das Zielfeld eine andere Figur schlagen koennte) - "killMaybeFigureType" = -killMaybeFigureType- (Typ der Figur, die geschlagen werden kann) - "killMaybeFigureField" = -killMaybeFigureField- (Feldbezeichnung des Feldes, auf dem eine Figur geschlagen werden kann) - "killMaybeFigureMustHadDoubleWalkLastTurn" = -killMaybeFigureMustHadDoubleWalkLastTurn- (beschreibt, ob die Figur, die geschlagen werden kann, im letzten Zug einen Doppelzug gemacht hat) - "hasAnxiety" = -hasAnxiety- oder immer True, wenn die Figur die Koenigsrolle traegt (beschreibt, ob die Figur auf dem Zeilfeld des Zuges geschlagen werden koennte) - "specialTurnType" = -specialMoveLabel- (spezielle Zugbezeichnung) - "needFigureOnField" = -needFigureOnField- (Zug ist nur moeglich, wenn auf dem Feld eine Figur steht und ein anderes Feld angegeben ist) - "neededFigureType" = -needFigureType- (ueberprueft, ob der Zug einer bestimmten Figur mit der Figur auf dem Feld uebereinstimmt) - "allowNeededFigureHasTurned" = allowNeededFigureHasTurned (beschreibt, ob sie die Figur auf dem Feld -needFigureOnField- schon bewegt hat) - "endPointNeededFigure" = endPointNeededFigure (Feld, auf dem die Firgur nach dem Zug steht) - "onDoneTurnCall" = onDoneTurnCall (beschreibt die Methode/Funktion, die nach dem Zug ausgefuerht wird) + "point" = :tuple[int, int] (gueltige Feldbezeichnung, die relativ zu -originFieldLabel- die Anzahl der Felder angibt, die fuer einen Zug in Buchstaben und Zahlen Richtung noetig sind) + "fieldLabel" = :str eine gueltige Feldbezeichnung (das erste Zeichen ist ein Buchstabe im Berreich des Buchstaben der Anfangsbeschriftungs und dem Buchstaben der Anfangsbeschriftungs versetzt um die Feldanzahl / das zweite Zeichen ist eine Zahl im Berreich der Zahl der Anfangsbeschriftungs und der Zahl der Anfangsbeschriftungs versetzt um die Feldanzahl) zu dem sich die Figur bewegen soll. + "onlyOnKill" = :bool (-True-, wenn die Figur nur den Zug machen kann, wenn sie dabei eine andere Figur schlagen wuerde) + "canKill" = :bool (-True-, wenn die Figur beim Zug auf das Zielfeld eine andere Figur schlagen koennte) + "killMaybeFigureType" = (Klasse der Figur, die geschlagen werden kann) + "killMaybeFigureField" = :str (Feldbezeichnung des Feldes, auf dem eine Figur geschlagen werden kann) + "killMaybeFigureMustHadDoubleWalkLastTurn" = :bool (beschreibt, ob die Figur, die geschlagen werden kann, im letzten Zug einen Doppelzug gemacht hat) + "hasAnxiety" = :boool oder immer True, wenn die Figur die Koenigsrolle traegt (beschreibt, ob die Figur auf dem Zeilfeld des Zuges geschlagen werden koennte) + "specialTurnType" = :str (spezielle Zugbezeichnung) + "needFigureOnField" = :str (Zug ist nur moeglich, wenn auf dem Feld eine Figur steht und ein anderes Feld angegeben ist) + "neededFigureType" = (ueberprueft, ob der Zug einer bestimmten hier angegebender Figur als Figurklasse mit der Figur auf dem Feld uebereinstimmt) + "allowNeededFigureHasTurned" = :bool (beschreibt, ob sie die Figur auf dem Feld -needFigureOnField- schon bewegt hat) + "endPointNeededFigure" = :str (Feldbezeichnung, auf dem die Firgur nach dem Zug steht) + "onDoneTurnCall" = :Callable (beschreibt die Methode/Funktion, die nach dem Zug ausgefuerht wird) ''' possibleZuege = [] for i in range(1, 8): From b9f713409eee3ae8f9ed205b8b70456b6bb97643 Mon Sep 17 00:00:00 2001 From: Jacob Koglin Date: Wed, 4 Mar 2026 07:31:17 +0100 Subject: [PATCH 5/9] Fixed typo, local game works again --- Brett.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Brett.py b/Brett.py index 8e9e27b..6645496 100644 --- a/Brett.py +++ b/Brett.py @@ -161,6 +161,8 @@ def __waehleSingleplayer(self): Erg.: - ''' self.__netzAktiv = False + if self.__modusDialog != None: + self.__modusDialog.hideSurface() self.__startDialogGruppe.empty() self.__ready = True self.start() @@ -291,6 +293,12 @@ def __setzeNetzSocket(self, sock:socket.socket, localTeam:int): self.__netzSock = sock self.__meinTeam = localTeam self.__netzVerbundenEvent.set() + if self.__modusDialog != None: + self.__modusDialog.hideSurface() + if self.__nameDialog != None: + self.__nameDialog.hideSurface() + if self.__netzStatusDialog != None: + self.__netzStatusDialog.hideSurface() self.__startDialogGruppe.empty() self.__ready = True self.start() @@ -693,13 +701,13 @@ def handleLeftClickEvent(self, pos:tuple[int, int])->None: Eff.: Verarbeitet Linksklicks fuer Dialoge und Figuren je nach Modus. Erg.: - ''' - if self.__modusDialog != None and self.__modusDialog.getIfShown(): + if self.__modusDialog != None and self.__modusDialog.getIfShown() and self.__modusDialog in self.__startDialogGruppe: self.__modusDialog.handleLeftClick(pos) return - if self.__nameDialog != None and self.__nameDialog.getIfShown(): + if self.__nameDialog != None and self.__nameDialog.getIfShown() and self.__nameDialog in self.__startDialogGruppe: self.__nameDialog.handleLeftClick(pos) return - if self.__netzStatusDialog != None and self.__netzStatusDialog.getIfShown() and not(self.__netzVerbundenEvent.is_set()): + if self.__netzStatusDialog != None and self.__netzStatusDialog.getIfShown() and self.__netzStatusDialog in self.__startDialogGruppe and not(self.__netzVerbundenEvent.is_set()): self.__netzStatusDialog.handleLeftClick(pos) return From 45bf105d6bff3bf4dfd5634be97a916c3c311723 Mon Sep 17 00:00:00 2001 From: Jacob Koglin Date: Wed, 4 Mar 2026 07:45:01 +0100 Subject: [PATCH 6/9] Network fix --- Brett.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Brett.py b/Brett.py index 0678709..5f47d09 100644 --- a/Brett.py +++ b/Brett.py @@ -267,7 +267,7 @@ def __discoveryWorker(self): return prefix = f"{chunks[0]}.{chunks[1]}.{chunks[2]}" own = int(chunks[3]) - for host in range(1, 41): + for host in range(1, 255): if not(self.__netzAktiv) or self.__netzVerbundenEvent.is_set(): return if host == own: From 30cee2c69f13276ab6034562e8d3b36db9a0d4bf Mon Sep 17 00:00:00 2001 From: Jacob Koglin Date: Wed, 4 Mar 2026 07:51:52 +0100 Subject: [PATCH 7/9] weitere Netzwerkfixes --- Brett.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Brett.py b/Brett.py index 5f47d09..716470d 100644 --- a/Brett.py +++ b/Brett.py @@ -198,6 +198,7 @@ def __starteMultiplayer(self): Erg.: - ''' self.__netzAktiv = True + print("Netzwerkstart auf Host:", socket.gethostname(), "IP:", self.__holeLokaleIp()) self.__zeigeNetzStatusDialog("Suche im Netzwerk nach Spiel") if self.__netzListenerThread == None or not(self.__netzListenerThread.is_alive()): self.__netzListenerThread = threading.Thread(target=self.__listenerWorker, daemon=True) @@ -212,8 +213,7 @@ def __starteNetzSuche(self): ''' if self.__netzVerbundenEvent.is_set(): return - if self.__netzSucheThread != None and self.__netzSucheThread.is_alive(): - return + self.__zeigeNetzStatusDialog("Suche im Netzwerk nach Spiel") self.__netzSucheThread = threading.Thread(target=self.__discoveryWorker, daemon=True) self.__netzSucheThread.start() @@ -223,7 +223,13 @@ def __holeLokaleIp(self)->str: Eff.: - Erg.: Die lokale IP ist als String geliefert. ''' - return socket.gethostbyname(socket.gethostname()) + allIps = socket.gethostbyname_ex(socket.gethostname())[2] + for ip in allIps: + if not(ip.startswith("127.")): + return ip + if len(allIps) != 0: + return allIps[0] + return "127.0.0.1" def __listenerWorker(self): ''' From 4e2880496093ff0bb3b75bdff0458dc2796704b8 Mon Sep 17 00:00:00 2001 From: Jacob Koglin Date: Wed, 4 Mar 2026 08:04:11 +0100 Subject: [PATCH 8/9] added manual ip adress eingabe --- Brett.py | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/Brett.py b/Brett.py index 716470d..93e0b9e 100644 --- a/Brett.py +++ b/Brett.py @@ -86,6 +86,7 @@ def __setupNetzwerkVars(self): self.__wendeRemoteZugAn:bool = False self.__modusDialog:Dialog|None = None self.__nameDialog:TextInputDialog|None = None + self.__ipDialog:TextInputDialog|None = None self.__netzStatusDialog:Dialog|None = None self.__startDialogGruppe = pygame.sprite.Group() @@ -152,7 +153,7 @@ def __zeigeNetzStatusDialog(self, headline:str): self.rect.width, self.rect.height, (self.rect.width//2, self.rect.height//2), headline, self.rect.height//14, - [["Erneut suchen", self.__starteNetzSuche]], + [["Erneut suchen", self.__starteNetzSuche], ["IP eingeben", self.__zeigeIpDialog]], self.rect.height//10, 0.7, False, onVoidClick=self.__generateImage, posOffset=self.rect.topleft, @@ -162,6 +163,69 @@ def __zeigeNetzStatusDialog(self, headline:str): self.__startDialogGruppe.add(self.__netzStatusDialog) self.__generateImage() + def __zeigeIpDialog(self): + self.__ipDialog = TextInputDialog( + self.rect.width, self.rect.height, + (self.rect.width//2, self.rect.height//2), + "IP eingeben", self.rect.height//10, + self.rect.height//10, + "Verbinden", self.rect.height//10, + False, + onSubmit=self.__verbindeMitIpText, + onVoidClick=self.__generateImage, + posOffset=self.rect.topleft, + onSurfaceChange=self.__generateImage, + maxInputLength=15 + ) + self.__startDialogGruppe.empty() + self.__startDialogGruppe.add(self.__ipDialog) + self.__generateImage() + + def __istIPv4Gueltig(self, ipText:str)->bool: + teile = ipText.split(".") + if len(teile) != 4: + return False + for teil in teile: + if teil == "": + return False + if not(teil.isdigit()): + return False + wert = int(teil) + if wert < 0 or wert > 255: + return False + return True + + def __verbindeMitIpText(self, ipText:str): + ipText = ipText.strip() + if not(self.__istIPv4Gueltig(ipText)): + if self.__ipDialog != None: + self.__ipDialog.setHeadline("IP ungueltig") + return + if self.__netzVerbundenEvent.is_set(): + return + connectThread = threading.Thread(target=self.__direktConnectWorker, args=(ipText,), daemon=True) + connectThread.start() + + def __direktConnectWorker(self, targetIp:str): + self.__zeigeNetzStatusDialog("Verbinde mit " + targetIp) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + if sock.connect_ex((targetIp, self.__netzPort)) != 0: + sock.close() + self.__zeigeNetzStatusDialog("IP nicht erreichbar") + return + sock.sendall(("ASK;" + self.__spielerName + "\n").encode("utf-8")) + raw = sock.recv(2048) + if len(raw) == 0: + sock.close() + self.__zeigeNetzStatusDialog("Keine Antwort von IP") + return + responseText = raw.decode("utf-8").strip() + if responseText.startswith("OK;"): + self.__setzeNetzSocket(sock, 0) + return + sock.close() + self.__zeigeNetzStatusDialog("Handshake fehlgeschlagen") + def __waehleSingleplayer(self): ''' Vor.: - @@ -241,13 +305,16 @@ def __listenerWorker(self): listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) listener.bind(("0.0.0.0", self.__netzPort)) listener.listen(3) + print("Listener aktiv auf Port", self.__netzPort) while self.__netzAktiv and not(self.__netzVerbundenEvent.is_set()): conn, _addr = listener.accept() + print("Eingehende Verbindung von", _addr) raw = conn.recv(2048) if len(raw) == 0: conn.close() continue msgText = raw.decode("utf-8").strip() + print("Empfangen:", msgText) msgParts = msgText.split(";") if len(msgParts) < 2 or msgParts[0] != "ASK": conn.close() @@ -257,6 +324,7 @@ def __listenerWorker(self): conn.close() continue conn.sendall(("OK;" + self.__spielerName + "\n").encode("utf-8")) + print("Antwort gesendet: OK;" + self.__spielerName) self.__setzeNetzSocket(conn, 1) return @@ -273,6 +341,7 @@ def __discoveryWorker(self): return prefix = f"{chunks[0]}.{chunks[1]}.{chunks[2]}" own = int(chunks[3]) + print("Starte Suche in", prefix + ".1-254", "eigene IP-Endung:", own) for host in range(1, 255): if not(self.__netzAktiv) or self.__netzVerbundenEvent.is_set(): return @@ -283,17 +352,21 @@ def __discoveryWorker(self): if sock.connect_ex((target, self.__netzPort)) != 0: sock.close() continue + print("Port offen auf", target) sock.sendall(("ASK;" + self.__spielerName + "\n").encode("utf-8")) + print("Gesendet an", target, ": ASK;" + self.__spielerName) raw = sock.recv(2048) if len(raw) == 0: sock.close() continue responseText = raw.decode("utf-8").strip() + print("Antwort von", target, ":", responseText) if responseText.startswith("OK;"): self.__setzeNetzSocket(sock, 0) return sock.close() if not(self.__netzVerbundenEvent.is_set()): + print("Suche fertig, kein Spiel gefunden") self.__zeigeNetzStatusDialog("Kein Spiel gefunden, warte auf Anfrage") def __setzeNetzSocket(self, sock:socket.socket, localTeam:int): @@ -309,10 +382,13 @@ def __setzeNetzSocket(self, sock:socket.socket, localTeam:int): self.__netzSock = sock self.__meinTeam = localTeam self.__netzVerbundenEvent.set() + print("Verbindung hergestellt. Eigenes Team:", localTeam) if self.__modusDialog != None: self.__modusDialog.hideSurface() if self.__nameDialog != None: self.__nameDialog.hideSurface() + if self.__ipDialog != None: + self.__ipDialog.hideSurface() if self.__netzStatusDialog != None: self.__netzStatusDialog.hideSurface() self.__startDialogGruppe.empty() @@ -709,6 +785,8 @@ def update(self) -> None: ''' if self.__nameDialog != None: self.__nameDialog.update() + if self.__ipDialog != None: + self.__ipDialog.update() def __CheckIfIsNotAFeldInstance(self, testObject:object) ->bool: ''' @@ -731,6 +809,9 @@ def handleLeftClickEvent(self, pos:tuple[int, int])->None: if self.__nameDialog != None and self.__nameDialog.getIfShown() and self.__nameDialog in self.__startDialogGruppe: self.__nameDialog.handleLeftClick(pos) return + if self.__ipDialog != None and self.__ipDialog.getIfShown() and self.__ipDialog in self.__startDialogGruppe: + self.__ipDialog.handleLeftClick(pos) + return if self.__netzStatusDialog != None and self.__netzStatusDialog.getIfShown() and self.__netzStatusDialog in self.__startDialogGruppe and not(self.__netzVerbundenEvent.is_set()): self.__netzStatusDialog.handleLeftClick(pos) return @@ -793,6 +874,8 @@ def handleKeyDownEvent(self, event:pygame.event.Event)->None: ''' if self.__nameDialog != None and self.__nameDialog.getIfShown(): self.__nameDialog.handleKeyDown(event) + if self.__ipDialog != None and self.__ipDialog.getIfShown(): + self.__ipDialog.handleKeyDown(event) def __resetCursorAndSetEventMode(self, eventMode:str): ''' From 5f45318663326294a1bc5f3b116c02c4458bdc04 Mon Sep 17 00:00:00 2001 From: Traumi-Schlumpf Date: Wed, 4 Mar 2026 08:46:02 +0100 Subject: [PATCH 9/9] Update Brett.py --- Brett.py | 53 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/Brett.py b/Brett.py index 93e0b9e..a46e0d5 100644 --- a/Brett.py +++ b/Brett.py @@ -1004,7 +1004,7 @@ def __promoteBishop(self): ''' Vor.: Ein Bauer steht zur Umwandlung bereit. Eff.: Der Bauer, welcher in der Liste der zu Umwandelbarenbauern hinten steht ist in einen Laeufer, des seines gleichen Teams, umgewandelt. - Und der andere Spieler ist an der Reihe. + Und der andere Spieler ist an der Reihe. Das -image- ist aktualiesiert. Erg.: - ''' self.__doPromote(self.__whiteBishop, self.__blackBishop, "LAEUFER") @@ -1013,7 +1013,7 @@ def __promoteKnight(self): ''' Vor.: Ein Bauer steht zur Umwandlung bereit. Eff.: Der Bauer, welcher in der Liste der zu Umwandelbarenbauern hinten steht ist in einen Springer, des seines gleichen Teams, umgewandelt. - Und der andere Spieler ist an der Reihe. + Und der andere Spieler ist an der Reihe. Das -image- ist aktualiesiert. Erg.: - ''' self.__doPromote(self.__whiteKnight, self.__blackKnight, "SPRINGER") @@ -1022,17 +1022,19 @@ def __promoteQueen(self): ''' Vor.: Ein Bauer steht zur Umwandlung bereit. Eff.: Der Bauer, welcher in der Liste der zu Umwandelbarenbauern hinten steht ist in einen Dame, des seines gleichen Teams, umgewandelt. - Und der andere Spieler ist an der Reihe. + Und der andere Spieler ist an der Reihe. Das -image- ist aktualiesiert. Erg.: - ''' self.__doPromote(self.__whiteQueen, self.__blackQueen, "DAME") def __doPromote(self, Team0Figure, Team1Figure, promotionName:str): ''' - HIER MIT SPEZIFIZIERUNG UEBERARBEITEN WEITERMACHEN!!!!! - Vor.: Es existiert ein aktiver Promotion-Kontext in der Liste -__PawnPromotes-. - Eff.: Der Bauer, welcher in der Liste der zu Umwandelbarenbauern hinten steht ist in eine , des seines gleichen Teams, umgewandelt. - Und der andere Spieler ist an der Reihe. + Vor.: Es existiert ein aktiver Promotion-Kontext in der Liste -__PawnPromotes-. + Team0Figure ist eine Figur instanz, welche beschreibt zu welche Figure auf dem Feld des Bauern für Team 0 auf dem Feld des Bauern zu setzten ist. + Team1Figure ist eine Figur instanz, welche beschreibt zu welche Figure auf dem Feld des Bauern für Team 1 auf dem Feld des Bauern zu setzten ist. + promotionName ist eine gueltige Netzwerkbezeichnung des Schachprotokolls, welche die Promotion beschreibt. + Eff.: Der Bauer, welcher in der Liste der zu Umwandelbarenbauern hinten steht ist in eine, des seines gleichen Teams, umgewandelt. + Und der andere Spieler ist an der Reihe. Das -image- ist aktualiesiert. Wenn das Brett in Netzwerkmodus ist, ist die Promotion, dem anderen Schachbrett mitgeteilt. Erg.: - ''' promoteData = self.__PawnPromotes[-1] @@ -1051,7 +1053,13 @@ def __doPromote(self, Team0Figure, Team1Figure, promotionName:str): def __finishTurn(self): ''' Vor.: Ein regelkonformer Zug wurde ausgefuehrt. - Eff.: Prueft Promotion/Matt/Remis, aktualisiert Spielzustand und wechselt Spieler. + Eff.: Promotion/Matt/Remis sind ueberprueft und erzeugen die jeweiligen grafischen Aenderungen. + Der self.__eventMode ist zu "chooseFigure" geändert und der andere Spieler ist an der Reihe, außer das Spiel ist durch den Zug beendent, weil ein Patt oder ein Matt ausgeloest wurde. + In diesem Fall ist die Situation in der Konsole beschrieben und beim Matt sind alle Felder einzeln umrandet und mit einem gruenen Punkt versehen. + Bei einer Pattsituation sind nur alle Felder, jeweils grün eingeramt. + Auch ist, dann das Brett.image aktualiesiert. + Wenn eine Bauerentwicklungsmoeglichkeit erkannt wurde ist ein neues Dialogfenster auf der -self.image- Surface gezeichnet mit der Interaktionsmoeglichkeit auszuwaehlen, zu was sich der Bauer entwickeln soll. + der aktualisiert Spielzustand ist aktualiesiert und der andere Spieler ist nun an der Reihe, wenn keine Eingaben mehr von dem Spieler zu den Zug benoetigt wurden. Erg.: - ''' for row in [1, 8]: @@ -1104,7 +1112,7 @@ def checkIfTeamCanMove(self, team:int): ''' Vor.: -team- ist eine gueltige Team-ID. Eff.: - - Erg.: -True-, wenn das Team mindestens einen legalen Zug hat, sonst -False-. + Erg.: -True- ist geliefert, wenn das Team mindestens einen legalen Zug hat, sonst ist -False- geliefert. ''' for field in self.__fields.values(): if type(field) != Feld: @@ -1120,8 +1128,9 @@ def checkIfTeamCanMove(self, team:int): def checkIfMate(self)->list[int]: ''' Vor.: - - Eff.: Prueft Matt und beendet wenn gefunden das Spiel. + Eff.: - Erg.: Eine Liste mattgesetzter Teams oder [-1], falls kein Matt vorliegt. + Matt bedeutet, dass eine Figur mit Koenigsrolle bedroht wird und kein Zug durchgefuert werden kann, welche dafuer sorgt, dass dies nicht mehr der Fall ist. ''' checkedTeams = self.__getCheckedTeams() if len(checkedTeams) == 0: @@ -1138,9 +1147,9 @@ def checkIfMate(self)->list[int]: def __getKingFieldsInDanger(self)->list[Feld]: ''' - Vor.: Koenigsfelder sind auf dem Brett vorhanden. + Vor.: -self.fields- ist eine Liste mit ausschließlichen Feldinstanzen des eigenen Brettes. Eff.: - - Erg.: Eine Liste aller Koenigsfelder, die bedroht sind, ist geliefert. + Erg.: Eine Liste aller Figuren mit Koenigsrolle, welche von anderen Figuren im naechsten gegnerischen Zug geschlagen werden koennten, ist geliefert. ''' KingFields:list[Feld] = self.__getFieldsWithKings() checkedKings:list[int] = [] @@ -1153,7 +1162,7 @@ def __getCheckedTeams(self)->list[int]: ''' Vor.: - Eff.: - - Erg.: Eine Liste aller Teams, deren Koenig aktuell im Schach steht, ist geliefert. + Erg.: Eine Liste aller Teams, deren Figur mit Koenigsrolle aktuell im Schach steht, ist geliefert. ''' KingFieldsInDanger:list[Feld] = self.__getKingFieldsInDanger() checkedTeams:list[int] = [] @@ -1168,14 +1177,14 @@ def __getCheckedTeams(self)->list[int]: def __getDangerFieldsWhenMove(self, targetField:Feld, OriginField:Feld)->list[Feld]: ''' - Vor.: -OriginField- enthaelt eine Figur und beide Felder sind gueltig. - Eff.: Simuliert den Zug temporaer und ermittelt Bedrohungen auf dem Zielfeld. + Vor.: -OriginField- enthaelt eine Figur und beide Felder sind Feldinstanzen aus -self.fields-. + Eff.: - Erg.: Eine Liste bedrohender Felder nach dem simulierten Zug ist geliefert. ''' resultingDangerFields:list[Feld] = [] MovingFigure = OriginField.getFigure() if MovingFigure == None: - raise Exception("TEs muss schon ne Figur auf dem Ursprungsfeld stehen") + raise Exception("ERROR: Es muss schon ne Figur auf dem Ursprungsfeld stehen (__getDangerFieldsWhenMove)") targetFieldFigur = targetField.getFigure() @@ -1231,7 +1240,7 @@ def __getFieldsWithKings(self)->list[Feld]: ''' Vor.: - Eff.: - - Erg.: Eine Liste aller Felder mit König ist geliefert. + Erg.: Eine Liste aller Felder auf dem eine Figur mit Koenigsrolle steht ist geliefert. ''' fieldsWithKings:list[Feld] = [] for field in self.__fields.values(): @@ -1247,7 +1256,7 @@ def __getFieldsWithKings(self)->list[Feld]: def __switchToOtherPlayer(self)->None: ''' Vor.: - - Eff.: Wechselt das aktive Team. + Eff.: Das aktive Team ist gewechselt. Erg.: - ''' self.__onTurnTeam = (self.__onTurnTeam+1)%2 @@ -1255,8 +1264,8 @@ def __switchToOtherPlayer(self)->None: def __chooseFigureEvent(self, clickedField:Feld)->bool: ''' Vor.: -clickedField- ist ein gueltiges Feld. - Eff.: Prueft Figurwahl, markiert moegliche Ziele und setzt Eventmodus. - Erg.: -True-, wenn eine waehlbare Figur aktiviert wurde, sonst -False-. + Eff.: Wenn auf -clickedField- eine Figur steht, welche der aktuelle Spieler bewegen darf sind moegliche Ziele von der Figur -clickedField- markiert und der Eventmodus ist auf setFigure angepasst. + Erg.: -True- ist geliefert, wenn eine waehlbare Figur aktiviert wurde, sonst ist -False- geliefert. ''' clickedFigure:None|Springer|Turm|Bauer|Laeufer|Dame|Koenig = clickedField.getFigure() clickedFieldLabel:str = clickedField.getLabel() @@ -1264,7 +1273,6 @@ def __chooseFigureEvent(self, clickedField:Feld)->bool: self.__clearAllFieldHighlights() self.__eventMode = "chooseFigure" return False - # clickedFigure ist ab jetzt Aufjedenfall eine Figur if clickedFigure.getTeam() != self.__onTurnTeam: self.__eventMode = "chooseFigure" @@ -1279,7 +1287,7 @@ def __chooseFigureEvent(self, clickedField:Feld)->bool: def __clearAllFieldHighlights(self)->None: ''' Vor.: - - Eff.: Entfernt alle Hervorhebungen von Feldern. + Eff.: Alle Hervorhebungen von Feldern aus -self.fields- sind entfernt. Erg.: - ''' for key in self.__fields.keys(): @@ -1290,6 +1298,7 @@ def __clearAllFieldHighlights(self)->None: def getPossibleTurnFields(self, Field:Feld, ignoreChecksOrAnxiety:bool=False, ignoreBuildingChecks:bool=False, ignoreCastling:bool=False)->list[Feld]: # Geht sicher, dass nicht doch irgendwie Ein Feld außerhalb des Brettes ist arbeitet noch Relative ''' + #HIER WEITERMACHEN MIT SPEZIFIZIEREN Vor.: -Field- ist ein gueltiges Feld; die bool Flags steuern Filterregeln. Eff.: - Erg.: Eine Liste aller moeglichen Zielfelder fuer den Zug ist geliefert.