TigerJython xx
für Gymnasien

Online Tic-Tac-Toe mit MQTT


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.
  Player 0

Player 1


Die beiden Spieler verwenden das gleiche Topic /swissgame/tic-tac-toe. Im Programm legen Sie mit player = 0 bzw. player = 1 fest, wer als erster zum Zug kommt und starten das Spiel. Der Status der Verbindung wird mit der Callbackfunktion onStateChanged() überwacht und in der Statuszeile angezeigt. Sobald die beiden Spielpartner verbunden sind (Status READY) kommt der Player 0 zuerst zum Zug und in seiner Statuszeile erscheint "Make a move!".

Die Zeichen "x" und "o" werden per Mausklick gesetzt. Dabei wird jeweils die x- und y-Koordinate des Mausklicks zum Spielpartner gesendet und das gesetzte Zeichen mit der Funktion setPattern() in der Belegung des Spielbretts eingetragen. Die Überprüfung des Spielsituation erfolgt nach jedem Zug mit der Funktion checkGameState() 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.

# TicTacToe.py

from gamegrid import *
from mqttclient import GameClient

player = x # set player = 0 or player = 1
myName = "Player "
myTopic = "/swissgame/tic-toc-toe"

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 for partner's move!")
     mark.show(player)     
     refresh()
     checkGameState()
     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, isOver
    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")
        isOver = True
    elif "OOO" in pattern:
        setStatusText("O  won")
        isOver = True
    elif not "-" in pattern:
        setStatusText("Board full")
        isOver = True

def onStateChanged(state):
    global isMyMove
    if state == "CONNECTING":
        setStatusText("Connecting to broker...")    
    elif state == "CONNECTED":
        setStatusText("Connected. Waiting for partner...")    
    elif state == "READY":
        setTitle("From:" + client.getPartnerName() + "@" + client.getPartnerAddress()) 
        if player == 0:
            setStatusText("Make a move!")    
            isMyMove = True
        else:
           setStatusText("Wait for partner's move!")    
    elif state == "DISCONNECTED":
        setStatusText("Partner disconnected!")    
        isMyMove = False
               
def onMessageReceived(msg):
    global isMyMove
    if msg == "DISCONNECT":
        setStatusText("Partner disconnected")
        isMyMove = False
        return
    x = int(msg[0])
    y = int(msg[1])
    loc = Location(x, y)
    mark = Actor("sprites/mark.gif", 2)
    addActor(mark, loc)
    mark.show((player + 1) % 2)        
    refresh()
    setStatusText("Make a move!")        
    checkGameState()
    isMyMove = True
      
def onNotifyExit():
    client.disconnect()
    dispose()

makeGameGrid(3, 3, 100, Color.black, False, 
    mousePressed = onMousePressed, notifyExit = onNotifyExit)
setBgColor(makeColor("greenyellow"))    
addStatusBar(30)
show()
setTitle("TicTacToe")
isOver = False
isMyMove = False
setTitle("Player #" + str(player))
host = "m2m.eclipse.org"
client = GameClient(onStateChanged, onMessageReceived, myTopic)
client.setName(myName + str(player))
client.connect(host)
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 Stringmuster

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