TigerJython | xx für Gymnasien |
Tic-Tac-Toe ist ein altes Strategiespiel, dessen Geschichte sich bis ins 12. Jahrhundert v. Chr. zurückverfolgen lässt. Auf einem 3×3 Felder grossen Spielfeld setzen die beiden Spieler abwechselnd ihre Zeichen. Der Spieler, der als erstes drei seiner Zeichen in eine Reihe, Spalte oder eine der beiden Diagonalen setzen kann, gewinnt. Wenn beide Spieler optimal spielen, kann keiner gewinnen, und es kommt zu einem Unentschieden. |
Server |
Client |
Zuerst wird das Serverprogramm gestartet, der Client beginnt. Die Zeichen werden per Mausklick gesetzt. Dabei wird jeweils die x- und y-Koordinate des Mausklicks zum Spielpartner gesendet. Die Überprüfung des Spielsituation erfolgt nach jedem Zug mit folgendem Trick: Die horizontale, vertikale und diagonale Belegung des Spielbretts wird in einem kommagetrennten Stringmuster der Form XOX,XX- ,O-O, ... dargestellt (leere Zellen erhalten das Zeichen '-'). Wenn XXX bzw. OOO in diesem String vorkommen, hat ein Spieler gewonnen.
Server:
# TcpTicServer.py from gamegrid import * from tcpcom import TCPServer def onMousePressed(e): global isMyMove if not isMyMove or isOver: return loc = toLocationInGrid(e.getX(), e.getY()) if getOneActorAt(loc) != None: return mark = Actor("sprites/mark.gif", 2) addActor(mark, loc) server.sendMessage(str(loc.x) + str(loc.y)) # send location setStatusText("Wait!") mark.show(1) checkGameState() refresh() isMyMove = False def setPattern(x, y): global pattern loc = Location(x, y) a = getOneActorAt(loc) if a == None: pattern += '-' elif a.getIdVisible() == 0: pattern += 'O' elif a.getIdVisible() == 1: pattern += 'X' def checkGameState(): # Convert board state into string pattern global pattern pattern = "" # Horizontal for y in range(3): for x in range(3): setPattern(x, y) pattern += ',' # Separator # Vertical for x in range(3): for y in range(3): setPattern(x, y) pattern += ',' # Diagonal for x in range(3): setPattern(x, x); pattern += ',' for x in range(3): setPattern(x, 2 - x); if "XXX" in pattern: setStatusText("X won") elif "OOO" in pattern: setStatusText("O won") elif not "-" in pattern: setStatusText("Board full") def onNotifyExit(): server.terminate() dispose() def onStateChanged(state, msg): global isMyMove, isOver if state == TCPServer.CONNECTED: setStatusText("Client connected. Wait!") isOver = False elif state == TCPServer.LISTENING: setStatusText("Waiting for a partner...") elif state == TCPServer.MESSAGE: x = int(msg[0]) y = int(msg[1]) loc = Location(x, y) mark = Actor("sprites/mark.gif", 2) addActor(mark, loc) mark.show(0) setStatusText("Make a move!") checkGameState() refresh() isMyMove = True makeGameGrid(3, 3, 70, Color.black, False, mousePressed = onMousePressed, notifyExit = onNotifyExit) setBgColor(makeColor("greenyellow")) addStatusBar(30) show() PORT = 5000 server = TCPServer(PORT, onStateChanged) isOver = False isMyMove = False |
---------------------------------------------------------------------------------------------------------------------------------
Client:
# TcpTicClient from gamegrid import * from tcpcom import TCPClient def onMousePressed(e): global isMyMove if not isMyMove or isOver: return loc = toLocationInGrid(e.getX(), e.getY()) if getOneActorAt(loc) != None: return mark = Actor("sprites/mark.gif", 2) addActor(mark, loc) client.sendMessage(str(loc.x) + str(loc.y)) # send location setStatusText("Wait!") mark.show(0) checkGameState() refresh() isMyMove = False def setPattern(x, y): global pattern loc = Location(x, y) a = getOneActorAt(loc) if a == None: pattern += '-' elif a.getIdVisible() == 0: pattern += 'O' elif a.getIdVisible() == 1: pattern += 'X' def checkGameState(): # Convert board state into string pattern global pattern pattern = "" # Horizontal for y in range(3): for x in range(3): setPattern(x, y) pattern += ',' # Separator # Vertical for x in range(3): for y in range(3): setPattern(x, y) pattern += ',' # Diagonal for x in range(3): setPattern(x, x); pattern += ',' for x in range(3): setPattern(x, 2 - x); if "XXX" in pattern: setStatusText("X won") elif "OOO" in pattern: setStatusText("O won") elif not "-" in pattern: setStatusText("Board full") def onStateChanged(state, msg): global isMyMove if state == TCPClient.CONNECTED: setStatusText("Connection established. You play!") elif state == TCPClient.CONNECTION_FAILED: setStatusText("Connection failed") elif state == TCPClient.DISCONNECTED: setStatusText("Server died") isMyMove = False elif state == TCPClient.MESSAGE: x = int(msg[0]) y = int(msg[1]) loc = Location(x, y) mark = Actor("sprites/mark.gif", 2) addActor(mark, loc) mark.show(1) setStatusText("Make a move!") checkGameState() refresh() isMyMove = True def onNotifyExit(): client.disconnect() dispose() makeGameGrid(3, 3, 70, Color.black,False, mousePressed = onMousePressed, notifyExit = onNotifyExit) setBgColor(makeColor("greenyellow")) addStatusBar(30) nbShip = 8 show() host = "localhost" port = 5000 client = TCPClient(host, port, stateChanged = onStateChanged) client.connect() isMyMove = True isOver = False |
Erklärungen zum Programmcode:
if getOneActorAt(loc) != None: Belegte Felder dürfen nicht gewählt werden | |
client.sendMessage(str(loc.x) + str(loc.y)): Die Koordinaten des Mausklicks werden als String gesendet (zwei Ziffern zwischen 0 und 2) | |
def setPattern(x, y): Schreibt eines der Zeichen 'X', 'O', bzw.' -' in das Stringpattern | |
def checkGameState(): Durchläuft alle Zeilen, Spalten und Diagonalen und stellt die Zellenbelegung durch ein Stringmuster dar. Danach wird überprüft, ob einer Zeile, Spalte oder Diagonale drei gleiche Zeichen vorkommen. Falls das Stringmuster kein Zeichen '-' enthält, ist das Brett voll |
Einige Programmteile im Server- und Clientprogramm sind identisch, insbesondere die Funktionen, die zur Überprüfung der Spielsituation dienen. Um diese Code-Duplikation zu vermeiden, speichert man diese Programmteile in einer separaten Datei (tictaclib.py) und importiert sie im Server- und Clientprogramm. Die Datei tictaclib.py muss auf beiden Computern gespeichert im gleichen Verzeichnis wie das Server- bzw. Client-Programm gespeichert sein. Die importierten Funktionen müssen durch Voranstellen von "tictaclib." aufgerufen werden. Die angepassten Programmcodes sehen wie folgt aus:
Server:
# TcpTicTacServer.py from gamegrid import * from tcpcom import TCPServer import tictaclib def onMousePressed(e): global isMyMove if not isMyMove or isOver: return loc = toLocationInGrid(e.getX(), e.getY()) if getOneActorAt(loc) != None: return mark = Actor("sprites/mark.gif", 2) addActor(mark, loc) server.sendMessage(str(loc.x) + str(loc.y)) # send location setStatusText("Wait!") mark.show(1) tictaclib.checkGameState() refresh() isMyMove = False def onNotifyExit(): server.terminate() dispose() def onStateChanged(state, msg): global isMyMove, isOver if state == TCPServer.CONNECTED: setStatusText("Client connected. Wait!") isOver = False elif state == TCPServer.LISTENING: setStatusText("Waiting for a partner...") elif state == TCPServer.MESSAGE: x = int(msg[0]) y = int(msg[1]) loc = Location(x, y) mark = Actor("sprites/mark.gif", 2) addActor(mark, loc) mark.show(0) setStatusText("Make a move!") tictaclib.checkGameState() refresh() isMyMove = True makeGameGrid(3, 3, 70, Color.black, False, mousePressed = onMousePressed, notifyExit = onNotifyExit) setBgColor(makeColor("greenyellow")) addStatusBar(30) show() PORT = 5000 server = TCPServer(PORT, onStateChanged) isOver = False isMyMove = False |
--------------------------------------------------------------------------------------------------------
Client:
# TcpTicTacClient from gamegrid import * from tcpcom import TCPClient import tictaclib def onMousePressed(e): global isMyMove if not isMyMove or isOver: return loc = toLocationInGrid(e.getX(), e.getY()) if getOneActorAt(loc) != None: return mark = Actor("sprites/mark.gif", 2) addActor(mark, loc) client.sendMessage(str(loc.x) + str(loc.y)) # send location setStatusText("Wait!") mark.show(0) tictaclib.checkGameState() refresh() isMyMove = False def onStateChanged(state, msg): global isMyMove if state == TCPClient.CONNECTED: setStatusText("Connection established. You play!") elif state == TCPClient.CONNECTION_FAILED: setStatusText("Connection failed") elif state == TCPClient.DISCONNECTED: setStatusText("Server died") isMyMove = False elif state == TCPClient.MESSAGE: x = int(msg[0]) y = int(msg[1]) loc = Location(x, y) mark = Actor("sprites/mark.gif", 2) addActor(mark, loc) mark.show(1) setStatusText("Make a move!") tictaclib.checkGameState() refresh() isMyMove = True def onNotifyExit(): client.disconnect() dispose() makeGameGrid(3, 3, 70, Color.black, False, mousePressed = onMousePressed, notifyExit = onNotifyExit) setBgColor(makeColor("greenyellow")) addStatusBar(30) nbShip = 8 show() host = "localhost" port = 5000 client = TCPClient(host, port, stateChanged = onStateChanged) client.connect() isMyMove = True isOver = False |
--------------------------------------------------------------------------------------------------------
tictaclib
# tictaclib.py from gamegrid import * def setPattern(x, y): global pattern loc = Location(x, y) a = getOneActorAt(loc) if a == None: pattern += '-' elif a.getIdVisible() == 0: pattern += 'O' elif a.getIdVisible() == 1: pattern += 'X' def checkGameState(): # Convert board state into string pattern global pattern pattern = "" # Horizontal for y in range(3): for x in range(3): setPattern(x, y) pattern += ',' # Separator # Vertical for x in range(3): for y in range(3): setPattern(x, y) pattern += ',' # Diagonal for x in range(3): setPattern(x, x); pattern += ',' for x in range(3): setPattern(x, 2 - x); if "XXX" in pattern: setStatusText("X won") elif "OOO" in pattern: setStatusText("O won") elif not "-" in pattern: setStatusText("Board full") |