Online Pearl Game

Zu Beginn sind 18 Perlen in 4 Reihen angeordnet. Der erste Spieler entfernt mit Mausklick eine beliebige Anzahl Perlen, allerdings nur aus der gleichen Reihe und drückt "Ok". Danach kommt der zweite Spieler zum Zug. Wer die letzte Perle entfernen muss, hat verloren. Die Gewinnstrategie lässt sich bei diesem Spiel nicht so einfach ermitteln.

  Server

Client


Zuerst muss das Serverprogramm gestartet werden. Der Client beginnt das Spiel. So wie in den vorhergehenden Spielen, wird jeweils die x- und y-Koordinaten des Mausklicks übermittelt. Wenn mehrere Perlen entfernt werden, muss überprüft werden, ob alle aus der gleichen Reihe stammen. Dazu wird die y-Koordinate der ersten Perle als activeRow bezeichnet. Die y-Koordinaten der nächsten Perlen werden mit dieser verglichen. Mit Klick auf den Ok-Button wird die Spielberechtigung an den Partner übergeben.

Server:

# TcpPearlServer.py

from gamegrid import *
from tcpcom import TCPServer
                                                                             
def initGame():
    nbRows = 4
    nb = 6
    for k in range(nbRows):
        for i in range(nb):
             pearl = Actor("sprites/token_1.png")
             addActor(pearl, Location(1 + i, 1 + k))
        nb -=1    
    btn = Actor("sprites/btn_ok_0.gif")
    addActor(btn, Location(6, 5))
    refresh()
                
def onMousePressed(e):
    global isMyMove, nbPearl, nb, isOver, activeRow, nbRemoved
    if not isMyMove or isOver:
        return
    btnLoc = Location(6,5)    
    loc = toLocationInGrid(e.getX(), e.getY())
    if loc != btnLoc:
        x = loc.x
        y = loc.y  
        if activeRow != -1 and activeRow != y:
            setStatusText("You must remove pearls from the same row.")
        else:
            actor = getOneActorAt(loc)
            if actor != None:      
                actor.removeSelf()
                server.sendMessage(str(x) + str(y))
                activeRow = y 
                nbPearl -= 1
                nbRemoved += 1
                refresh()        
                if nbPearl == 0:    
                    isOver = True
                    server.sendMessage("end")
                    setStatusText("End of game. You lost!")
    elif nbRemoved == 0: # ok btn pressed
        setStatusText("You have to remove at least 1 pearl!")
    else:
        server.sendMessage("ok")          
        setStatusText("Wait!")
        nbRemoved = 0
        activeRow = -1
        isMyMove = False          

def onNotifyExit():
    server.terminate()
    dispose()
    
def onStateChanged(state, msg):
    global isMyMove,loc, nbPearl, isOver
    if state == TCPServer.PORT_IN_USE:
        setStatusText("TCP port occupied. Restart IDE.")
    if state == TCPServer.LISTENING:
        setStatusText("Waiting for a partner to play")        
    if state == TCPServer.CONNECTED:
        setStatusText("Client connected. Wait for partner's move!")
    elif state == TCPServer.MESSAGE: 
        if msg == "end":
            isOver = True
            setStatusText("End of Game. You won!")
        elif msg == "ok":
           isMyMove = True
           setStatusText("Remove any number of pearls from same row and click OK!")
        else:
            x = int(msg[0])
            y = int(msg[1])
            loc = Location(x, y)            
            getOneActorAt(loc).removeSelf()
            nbPearl -=1
            refresh()            
 
makeGameGrid(8, 6, 70, False, mousePressed = onMousePressed, notifyExit = onNotifyExit)
addStatusBar(30)
initGame()
show()
port = 5000
server = TCPServer(port, stateChanged = onStateChanged)
activeRow = -1
nbPearl = 18
nbRemoved = 0
isMyMove = False
isOver = False     
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

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

# TcpPearlClient.py

from gamegrid import *
from tcpcom import TCPClient
                                                                            
def initGame():
    nbRows = 4
    nb = 6
    for k in range(nbRows):
        for i in range(nb):
             pearl = Actor("sprites/token_1.png")
             addActor(pearl, Location(1 + i, 1 + k))
        nb -=1
    btn = Actor("sprites/btn_ok_0.gif")
    addActor(btn, Location(6, 5))
    refresh()
    
    
def onMousePressed(e):
    global isMyMove, nbPearl, isOver, activeRow, nbRemoved
    if not isMyMove or isOver:
         return
    btnLoc = Location(6,5) 
    loc = toLocationInGrid(e.getX(), e.getY())
    if loc != btnLoc:
        x = loc.x
        y = loc.y  
        if activeRow != -1 and activeRow != y:
            setStatusText("You must remove pearls from the same row.")
        else:
            actor = getOneActorAt(loc)
            if actor != None:      
                actor.removeSelf()
                client.sendMessage(str(x) + str(y))
                activeRow = y 
                nbPearl -= 1
                nbRemoved += 1
                refresh()        
                if nbPearl == 0:    
                    isOver = True
                    client.sendMessage("end")
                    setStatusText("End of game. You lost!")
    elif nbRemoved == 0: # ok btn pressed
        setStatusText("You have to remove at least 1 pearl!")
    else:
        client.sendMessage("ok")   
        setStatusText("Wait!")
        nbRemoved = 0
        activeRow = -1 
        isMyMove = False 
          
def onNotifyExit():
    client.disconnect()
    dispose()
   
def onStateChanged(state, msg):
    global isMyMove, loc, nbPearl, isOver
    if state == TCPClient.CONNECTED:
        setStatusText("Connection established. Remove any number of pearls from same row and click OK.!")
        isMyMove = True
    elif state == TCPClient.CONNECTION_FAILED:
        setStatusText("Connection failed")
    elif state == TCPClient.DISCONNECTED:
        setStatusText("Server died")
        isMyMove = False
    elif state == TCPClient.MESSAGE:                       
        if msg == "end":
           isOver = True
           setStatusText("End of Game. You won.")
        elif msg == "ok":
           isMyMove = True
           setStatusText("Remove any number of pearls from same row and click OK!")
        else:
            x = int(msg[0])
            y = int(msg[1])
            loc = Location(x, y)            
            getOneActorAt(loc).removeSelf()
            nbPearl -= 1
            refresh()            
 
makeGameGrid(8, 6, 70, False, mousePressed = onMousePressed, notifyExit = onNotifyExit)
addStatusBar(30)
initGame()
show()
host = "localhost"
port = 5000
client = TCPClient(host, port, stateChanged = onStateChanged)
client.connect()
activeRow = -1
nbPearl = 18
nbRemoved = 0
isMyMove = True
isOver = False  
Programmcode markieren (Ctrl+C kopieren, Ctrl+V einfügen)

 

Erklärungen zum Programmcode:

initGame(): Erzeugt 18 Perlen, die in 4 Reihen angeordnet sind und den OK-Button in der Zelle (6, 5). Dieser Programmteil ist im Server- und Clientprogramm identisch und könnte in einer separaten Datei gespeichert werden
activeRow = -1: Vor der Wahl der ersten Perle, ist activeRow auf - 1 gesetzt (ausserhalb des Spielfensters)
elif nbRemoved == 0: Wenn OK-Button gedrückt wurde, ohne vorher mindestens eine Perle entfernt zu haben, ist der Zug ungültig
client.sendMessage("ok") : Wenn der OK-Button gedrückt wurde wird der String "ok" gesendet und dadurch die Spielberechtigung an den Partner übergeben

Bemerkung: Diese Lösung kann optimiert werden, indem man die Programmteile, welchen sowohl im Server- als im Clientprogramm vorkommen, in einer separaten Datei speichert und diese in beiden Programmen importiert. Die angepassten Programmcodes können Sie hier herunterladen. Für das Verständnis der Interaktion zwischen dem Server und Client ist aber die oben stehende Variante einfacher.