Online Memory mit MQTT

16 Karten, die paarweise übereinstimmen, sind zu Beginn in einer zufälligen Reihenfolge verdeckt aufgelegt. Der erste Spieler deckt mit zwei Mausklicks zwei Karten auf. Wenn er zwei gleiche Karten trifft, bleiben die Karten aufgedeckt und sein Zug wird fortgesetzt. Sonst werden beide Karten wieder verdeckt und der zweite Spieler kommt zum Zug. Gewonnen hat der Spieler, der am Ende die meisten Paare aufgedeckt hat.

  Player 0

Player 1


Die beiden Spieler wählen das gleiche Topic /swissgame/memory und müssen vor dem Spielbeginn die Programmzeile player = 0 bzw. player = 1 ergänzen. Der Player 0 beginnt. Bei beiden Spielern werden zu Beginn Karten in der gleichen zufälligen Reihenfolge gelegt. Dazu wird eine Liste rndList mit zufällig angeordneten Zahlen 0 bis 15 erzeugt. Die 16 Spielkarten werden danach gemäss dieser Liste angeordnet und zugedeckt (show(0)) angeeigt.

Die Überprüfung und Verdecken der zwei gewählten Karten erfolgt in den Callbacks onMousePressed() und onStateChanged(). Da man grundsätzlich in Callbacks nicht zu viele Operationen ausführen soll (insbesondere keine blockierende Funktionen), wird dies durch das Hauptprogramm gemacht. Dort wird in der while True-Schleife das Programm mit putSleep() angehalten, bis zwei Karten aufgedeckt sind. Die Programmausführung wird dann mit wakeUp() fortgesetzt. Nicht passende Karten werden nach einer Verzögerung von 1 Sekunde wieder zugedeckt.

Im Folgenden findet man zwei Lösungsvarianten. Die erste Lösung enthält ein Server- und ein Clientprogramm. In dieser Form ist es einfacher, der Interaktion zwischen dem Client und Server zu folgen. In der zweiten Variante werden Programmteile, die sowohl im Server- als auch im Client-Programm vorkommen, in der Datei memlib.py gespeichert. Diese muss in beiden Programmen importiert werden.

# Memory.py

from gamegrid import *
from mqttclient import GameClient
import random

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

class MemoryCard(Actor):
    def __init__(self, cardNb):
        Actor.__init__(self, ["sprites/cardback.gif", "sprites/card" + str(cardNb) + ".gif"])
        self.cardNb = cardNb
    def getCardNb(self):
        return self.cardNb 

def createCards():
    cardList = []
    for i in range(16):  
        if i < 8:
            card = MemoryCard(i)
        else:
            card = MemoryCard(i - 8)
        cardList.append(card)    
    k = 0
    for i in rndList: 
        x = i % 4
        y = i // 4 
        loc = Location(x, y)             
        addActor(cardList[k], loc)
        cardList[k].show(0)
        k += 1

def onMousePressed(e):
     global card1, card2, isFirst
     if not isMyMove or isGameOver:
          return
     loc = toLocationInGrid(e.getX(), e.getY()) 
     client.sendMessage(str(loc.x) + str(loc.y)) # send location  
     card = getOneActorAt(loc)
     if card.getIdVisible() == 1: # card already visible->ignore
        return 
     card.show(1) # make card visible     
     if isFirst: 
         card1 = card
         setStatusText("Take second card!")
         isFirst = False
     else:  # second card taken
        card2 = card
        isFirst = True
        wakeUp()
           
def onNotifyExit():
    client.disconnect()
    dispose()
    
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!")
            client.sendMessage(str(rndList)) 
            isMyMove = True    
        else:
           setStatusText("Wait for partner's move!")    
    elif state == "DISCONNECTED":
        setStatusText("Partner disconnected!")    
        isMyMove = False
    
def onMessageReceived(msg):
    global isFirstRec, card1, card2, rndList
    if msg[0] == "[":
        s = msg[1:-1]
        config = s.split(",")
        rndList = [int(z) for z in config] 
        createCards()     
    else: 
        x = int(msg[0])
        y = int(msg[1])            
        loc = Location(x, y)
        card = getOneActorAt(loc)
        card.show(1)
        if isFirstRec:
            card1 = card        
            isFirstRec = False
        else:
            card2 = card 
            isFirstRec = True               
            wakeUp()                                  
         
makeGameGrid(4, 4, 115, Color.black, False, mousePressed = onMousePressed, 
             notifyExit = onNotifyExit)
addStatusBar(30)
setTitle("Memory")
rndList = random.sample(range(16), 16)
createCards()
show()
doRun()
PORT = 5000
isGameOver = False
isMyMove = False
isFirst = (player == 0)
isFirstRec = True
myHits = 0 
partnerHits = 0
setTitle("Player #" + str(player))
host = "m2m.eclipse.org"
client = GameClient(onStateChanged, onMessageReceived, myTopic)
client.connect(host)

while True:
    putSleep()
    if isDisposed():
       break
    if card1.getCardNb() == card2.getCardNb(): # test if cards have same image
        if isMyMove:
            myHits += 1
            setStatusText("Pair accepted. Take nextCard")
        else:
            partnerHits += 1
        if myHits + partnerHits == 8:
            isGameOver = True
            if myHits > partnerHits:
                setStatusText("Game over! You won with " + str(myHits) + " hits.") 
            elif myHits < partnerHits:
                setStatusText("Game over! You lost with only " + str(myHits) + " hits.")
            else:
                setStatusText("Game over! The score is even.")                
    else: #  hide the two cards
       delay(1000)
       card1.show(0)
       card2.show(0)
       if isMyMove:
           isMyMove = False
           setStatusText("Wait!")  
       else:
           isMyMove = True
           setStatusText("You play. Take two cards!")        
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

Erklärungen zum Programmcode:

rndList = random.sample(range(16), 16): Erzeugt 16 eindeutige Zufallszahlen zwischen 0 und 15
for i in rndList: Geht die Zufallsliste durch und legt mit Hilfe dieser Zufallszahlen Karten in die 16 Zellen