Online Memory

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.

  Server

Client


Zuerst muss das Serverprogramm gestartet werden. Damit die Karten beim Server und Client in der gleichen zufälligen Reihenfolge gelegt werden, wird im Serverprogramm eine Liste rndList mit zufällig angeordneten Zahlen 0 bis 15 erzeugt und diese als String zum Client gesendet. Beim Client wird das empfangene String zu einer Liste konvertiert und die Spielkarten gemäss dieser Liste angeordnet.

Die Überprüfung und Verdecken der zwei gewählten Karten sollte in den Callbacks onMousePressed() und onStateChanged() erfolgen. Da man grundsätzlich in Callbacks nicht zu viele Operationen ausführen soll (insbesondere keine blockierende Funktionen), wird dieser Programmteil ausgelagert. Die Ausführung des Programmcodes in der while True-Schleife wird zuerst 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, die 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.

Variante 1: Server

# TcpMemoryServer.py

from gamegrid import *
from tcpcom import TCPServer
import random

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()) 
     server.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():
    server.terminate()
    dispose()
    
def onStateChanged(state, msg):
    global isFirstRec, card1, card2
    if state == TCPServer.CONNECTED:
        setStatusText("Client connected. Wait!")
        server.sendMessage(str(rndList)) 
    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)
        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
server = TCPServer(PORT, onStateChanged)
isGameOver = False
isMyMove = False
isFirst = True
isFirstRec = True
myHits = 0 
partnerHits = 0

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)

-------------------------------------------------------------------------------------------------------------------------
Variante 1: Client

# TcpMemoryClient

from gamegrid import *
from tcpcom import TCPClient

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, msg):
    global isMyMove, isFirstRec, card1, card2, rndList
    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:
        if msg[0] == "[":
            s = msg[1:-1]
            rndList = s.split(",")
            rndList = [int(z) for z in rndList] 
            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")
show()
doRun()
host = "localhost"
port = 5000
client = TCPClient(host, port, stateChanged = onStateChanged)
client.connect()
isMyMove = True
isGameOver = False
isFirst = True
isFirstRec = True
myHits = 0
partnerHits = 0
rndList = []

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
server.sendMessage(str(rndList)): Nacj der Aufbeu der Verbindung wird die Zufallsliste als String zum Client geschickt

s = msg[1:-1] : Da es in Python keine Funktion gibt, die String in eine Liste konvertiert, müssen die Lister der Zufallszahlen in drei Schritten aus dem String extrahiert werden. Zuerst wird das erste und das letzte Zeichen des Strings entfernt

rndList = s.split(","): Der String wird nach Komma getrennt. Die Elemente der Liste sind Strings ("5" "8" ...)
[int(z) for z in rndList] = [int(z) for z in rndList] : Die einzelnen Strings werden in Zahlen konvertiert
if card1.getCardNb() == card2.getCardNb() : Die Kartennummer entspricht der Nummer der Bilddatei

 

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

Variante 2: Die Datei memlib.py enthält Programmteile, die sowohl im Server- als auch im Clientprogramm vorkommen. Diese Lösung ist eleganter, da man grundsätzlich eine Codeduplikation vermeiden soll.

Variante 2: memlib.py


Warning: include(bsp/memlib.html): Failed to open stream: No such file or directory in /var/www/html/tcpgames/tcpMemory.inc.php on line 74

Warning: include(): Failed opening 'bsp/memlib.html' for inclusion (include_path='.:/usr/local/lib/php') in /var/www/html/tcpgames/tcpMemory.inc.php on line 74
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

-------------------------------------------------------------------------------------------------------------------------
Variante 2: Server:

# TcpMemoryServer2.py

import memlib
from gamegrid import *
from tcpcom import TCPServer
import random

def onMousePressed(e):
     global card1, card2, isFirst
     if not memlib.isMyMove or memlib.isGameOver:
          return
     loc = toLocationInGrid(e.getX(), e.getY()) 
     server.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():
    server.terminate()
    dispose()
    
def onStateChanged(state, msg):
    global card1, card2, isFirstRec
    if state == TCPServer.CONNECTED:
        setStatusText("Client connected. Wait!")
        server.sendMessage(str(rndList)) 
    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)
        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)
memlib.createCards(rndList)
show()
doRun()
PORT = 5000
server = TCPServer(PORT, onStateChanged)
isFirst = True
isFirstRec = True
memlib.isMyMove = False

while True:
    putSleep()
    if isDisposed():
       break
    memlib.checkGame(card1, card2)
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

-------------------------------------------------------------------------------------------------------------------------
Client:

Client:

# TcpMemoryClient2.py

import memlib
from gamegrid import *
from tcpcom import TCPClient

def onMousePressed(e):
     global card1, card2, isFirst
     if not memlib.isMyMove or memlib.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, msg):
    global isFirstRec, card1, card2, rndList
    if state == TCPClient.CONNECTED:
        setStatusText("Connection established. You play!")
    elif state == TCPClient.CONNECTION_FAILED:
        setStatusText("Connection failed")
    elif state == TCPClient.DISCONNECTED:
        setStatusText("Server died")
        memlib.isMyMove = False
    elif state == TCPClient.MESSAGE:
        if msg[0] == "[":
            s = msg[1:-1]
            rndList = s.split(",")
            rndList = [int(z) for z in rndList] 
            memlib.createCards(rndList)     
        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")
show()
doRun()
host = "localhost"
port = 5000
client = TCPClient(host, port, stateChanged = onStateChanged)
client.connect()
isFirst = True
isFirstRec = True
memlib.isMyMove = True

while True:
    putSleep()
    if isDisposed():
       break
    memlib.checkGame(card1, card2)
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)