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!") |
-------------------------------------------------------------------------------------------------------------------------
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!") |
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 |
-------------------------------------------------------------------------------------------------------------------------
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) |
-------------------------------------------------------------------------------------------------------------------------
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) |