Online Battleship mit MQTT

Ein einfaches Schiffchen-Versenken Spiel
Spielregeln: Jeder Spieler erhält ein Spielfeld mit 8 Schiffen an zufällig gewählten Positionen. Die Spieler versuchen, abwechslungsweise mit einem Mausklick die Position der Schiffe des Gegners zu erraten. Damit man die gleiche Zelle nicht mehrmals wählt, wird jeder Versuch mit einem roten Kreuz markiert. Bei einem Treffer wird das Schiff versenkt und verschwindet vom Spielfeld. Wer zuerst alle Gegnerschiffe versenkt hat, gewinnt.

  Player 0
Player 1

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

In der Callbackfunktion onMousePressed() wird die x- und y-Koordinate des Mausklicks erfasst und als String zum Spielpartner gesendet. Im Callback onMessageReceived() empfängt der Partner diese Koordinaten und meldet zurück, ob sich an dieser Position ein Schiff befindet ("hit") oder nicht ("mis"). Die verbleibende Anzahl Schiffe wird in der Statusbar angezeigt. Dann kommt

#Battleship.py

from gamegrid import *
from mqttclient import GameClient

player = x # set player = 0 or player = 1
myTopic = "/swissgame/battleship"

class Ship(Actor):
    def __init__(self):
        Actor.__init__(self, "sprites/boat.gif")
        
def onMousePressed(e):
     global isMyMove
     if not isMyMove or isOver:
          return
     loc = toLocationInGrid(e.getX(), e.getY())
     addActor(Actor("sprites/checkred.gif"), loc)
     client.sendMessage(str(loc.x) + str(loc.y)) # send location 
     setStatusText("Wait!")
     isMyMove = False
     
def onStateChanged(state):
    global isMyMove
    if state == "CONNECTING":
        setStatusText("Connecting to broker...")    
    elif state == "CONNECTED":
        setStatusText("Connected. Waiting for partner...")    
    elif state == "READY":
        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, myHits, partnerHits, isOver      
    if msg == "DISCONNECT":
        setStatusText("Partner disconnected")
        isMyMove = False
        return
    if msg == "hit":
        myHits += 1
        setStatusText("Hit! Partner's fleet size " + str(nbShip - myHits) 
            + ". Wait!")            
        if myHits == nbShip:
            setStatusText("Game over, You won!")
            isOver = True 
    elif msg == "miss":
        setStatusText("Miss! Partner's fleet size " + str(nbShip - myHits)) 
    else:
        x = int(msg[0])
        y = int(msg[1])
        loc = Location(x, y)
        actor = getOneActorAt(loc, Ship)         
        if actor != None:
            actor.removeSelf()     
            refresh()
            client.sendMessage("hit")
            partnerHits += 1             
            if partnerHits == nbShip:
                setStatusText("Game over! Partner won")
                isOver = True 
                return  
        else:
            client.sendMessage("miss")
        setStatusText("You play! Partner's fleet size " + str(nbShip - myHits))            
        isMyMove = True

def onNotifyExit():
    client.disconnect()
    dispose()

makeGameGrid(5, 5, 50, Color.red, False, mousePressed = onMousePressed, 
             notifyExit = onNotifyExit)
addStatusBar(30)
nbShip = 8
for i in range(nbShip):
    addActor(Ship(), getRandomEmptyLocation())
show()
isMyMove = False
isOver = False
myHits = 0
partnerHits = 0
setTitle("Player #" + str(player))
host = "m2m.eclipse.org"
client = GameClient(onStateChanged, onMessageReceived, myTopic)
client.connect(host)
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

Erklärungen zum Programmcode:

addActor(Actor("sprites/checkred.gif"), loc): Setzt eine Marke in die gewählte Zelle
client.sendMessage(str(loc.x) + str(loc.y)): Die Koordinaten des Mausklicks werden als String gesendet (zwei Ziffern zwischen 0 und 4)
def onMessageReceived(msg): Callbackfunktion, die aufgerufen wird, wenn eine Message angekommen ist. Empfangen werden entweder die Strings "hit, "miss" oder die Koordinaten der Zelle
x = int(msg[0]), y = int(msg[1]: Das erste Zeichen des msg-Strings ist die x- und das zweite Zeichen die y-Koordinate der getroffenen Zelle. Die Strings müssen mit der Funktion int() in Zahlen konvertiert werden
actor = getOneActorAt(loc, Ship) : Nur Actrors der Klasse Ship werden berücksichtigt. Falls sich in der gleichen Zelle eine rote Marke befindet, wird None zurückgegeben