TigerJython xx
für Gymnasien

Online Tic-Tac-Toe


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
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

---------------------------------------------------------------------------------------------------------------------------------

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
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

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

 


 

Eine elegantere Lösung:

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
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

--------------------------------------------------------------------------------------------------------

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
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

--------------------------------------------------------------------------------------------------------

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")     
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)