Search code examples
pythonpython-2.7network-programmingpython-multithreadingrecv

Quiz Game with multi players, server doesn't receive information from clients


I would like to program a quiz game with multi player. The server part chooses the question, then sends it to the client. The client answers and sends this answer to the server. The server then checks if it is right or not. I would like to do this in a loop. But the problem is, there is a problem of timing, sometimes my client doesn't receive the question before being able to answer. Another problem is, the server doesn't receive the answer when the client sends it and I really don't know why.

It's my first time doing network programming and it's difficult for me to understand threading and all this. Thank you for your help =)

PS : This related to another question, I would like for this code to work with a simple graphical interface... But that's the next step ;)

Here is my client code :

import socket
import threading
import time

tLock = threading.Lock()
shutdown = False

def receving(name, sock):
    while not shutdown:
            try:
                tLock.acquire()
                while True:
                        data,addr = sock.recvfrom(1024)
                        print(str(data))
            except:
                    pass
            finally:
                    tLock.release()

#host = '192.168.26.86'
host = '127.0.0.1'
port = 0 #pick any free port currently on the computer
server = (host, 5000)

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((host,port))
s.setblocking(0)

#receiving thread
rT = threading.Thread(target=receving, args=("RecvThread", s))
rT.start()

alias = raw_input("Name: ")

#PLAYER ENTERS DATA
message = raw_input(alias + "First msg ->")

while message != 'q':
        tLock.acquire()
        message = raw_input(alias + "What is your answer ?  ->")
        tLock.release()


        if message != '':
            s.sendto(alias + ": " + message, server)

        time.sleep(0.2)

shutdown = True
rT.join()
s.close()

Here is my server code :

import numpy as np
from numpy.random import shuffle
from random import randint
import socket
import time

#LIST OF QUESTIONS AND ANSWERS
question2 = ["What is the capital of France?","In which continent is Argentina?","Where is Big Ben?","What is the most densely populated country?","What language do they speak in Brazil?"]
answer2 = [["Paris","London","Berlin","Madrid"],
        ["South America","Africa","Europe","Asia"],
        ["London","New York","Mexico","Jakarta"],
        ["China","India","USA","Indonesia"],
        ["Portuguese","Spanish","French","English"]]
question_done=[0]*(len(question2))


#SCORE, stored as a list score[0]--> score of the player 1
score=[0]


#SHOW THE POSSIBLE ANSWERS
def displayA(question,answer,i):
    a = answer[i]
    order = np.arange(4)
    shuffle(order) #create list from 1 to 4 in different order --> to print the answers in random order
    a_display = [[a[order[0]],a[order[1]]],[a[order[2]],a[order[3]]]]
    print(a_display)


#CHOOSE RANDOMLY A QUESTION IN THE LIST
def chooseQuestion(question,answer):
    k = randint(0,len(question)-1)
    if (question_done[k]!=0):
        while(question_done[k]!=0):
            k = randint(0,len(question)-1)
        question_done[k]=1
    else :
        question_done[k]=1
    print(question[k])
    #displayA(question,answer,k)
    return k


#CHECK IF GOOD ANSWER OR NOT
def checkAnswer(answer,agiven,qnb):
    print("CHECK")
    test = False
    if(answer[qnb][0] in agiven):
        test = True
        score[0]=score[0]+1
    print("ANSWER")
    return test



#END OF GAME, DISPLAY OF SCORES
def final_score(score):
    print("The scores are {}".format(score))

    maxi = max(score)
    if(score.count(maxi)==1):
        print("The winner is Player {}".format(score.index(max(score))+1))
    else :
        winners = []
        for i in range(len(score)):
            if(score[i]==maxi):
                winners.append(i+1)
        print("The winners are players {}".format(winners))


"""

#Number of choosen questions, random order : GIVE THE QUESTION TO THE PLAYER
nb = 3

for k in range(nb):
   nbq = chooseQuestion(question2,answer2)
   agiven = raw_input("What is your answer?  ")
   result = checkAnswer(answer2,agiven,nbq)
   print("Your answer is {}".format(result))



#Say who the winner is
final_score(score)



"""
#START THE NETWORK CODE
#host = '192.168.26.86'
host = '127.0.0.1'

port = 5000

#list for all the players
players = []

#creation of socket object UDP and bind
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((host, port))
#socket non blocking --> will always try to grab datas from the stream
#not block it
s.setblocking(0)


quitting = False
print "Server Started."

#NUMBER OF QUESTIONS
nb = 3


while (not quitting and nb>0):
    try:
            data, addr = s.recvfrom(1024)
            print("FIRST RECV {}".format(data))
            if "Quit" in str(data):
                quitting = True
            if addr not in players:
                players.append(addr)
                print("liste players {}".format(players))
                s.sendto("the game is starting ",players[len(players)-1])

            print time.ctime(time.time()) +  ":" + str(data)

            #GAME

            #for k in range(nb):
            print("question nb {}".format(nb))
            nbq = chooseQuestion(question2,answer2)


            for i in range(len(players)):
                print("ENTER FOR")
                #s.sendto(question2[nbq], players[i])
                try:
                    s.sendto(question2[nbq], players[i])
                    print("BEFORE GET ANSWER")
                    agiven, addr = s.recvfrom(1024)
                    print("GOT ANSWER")

                    print("agiven is : {}".format(agiven))
                    checkAnswer(answer2,agiven,nbq)

                    if "Quit" in str(agiven):
                        quitting = True
                    if addr not in players:
                        s.sendto("You cannot enter the game now ",addr)
                    else:
                        players[i] = addr
                except:
                    pass

            nb=nb-1

    except:
            pass



for i in range(len(players)):
    try:
        s.sendto("The game is finished, write q to quit",players[i])

    except:
        pass

final_score(score)
s.close()

Solution

  • Your code contains the correct logic for the questions and answers, but the data transfers between client and server was fragile and it only worked if the timing was perfect.

    I have modified your code to help control the timing, and stop things from getting out of sequence. I used the select() system call to handle timeouts, and in the client I changed the read thread to notify the main loop when questions arrive from the server.

    This code is a long way from being perfect but it does work and it should help you build something final that works the way you prefer.

    NB I removed the "you can't join the game now" code to help me debug the original code, so you could probably put this back in now. Hope this is helpful.

    Client code:

    import socket
    import threading
    import time
    
    tEv = threading.Event()
    tShutdown = threading.Event()
    
    def receving(name, sock):
        shutdown = False
        while not shutdown:
            try:
                data,addr = sock.recvfrom(1024)
                print(str(data))
                if '?' in data:
                    tEv.set()
                if data == "The game is finished":  # message from server to stop
                    tShutdown.set()
                    shutdown = True
            except:
                pass
            finally:
                pass
    
    #host = '192.168.26.86'
    host = '127.0.0.1'
    port = 0 #pick any free port currently on the computer
    server = (host, 5000)
    
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.bind((host,port))
    s.setblocking(0)
    
    # Start listener
    rT = threading.Thread(target=receving, args=("RecvThread", s))
    rT.start()
    
    # Join the game
    alias = raw_input("Name: ")
    s.sendto(alias, server)
    
    running = True
    while running:
        if tEv.wait(1.0):
            tEv.clear()
            message = raw_input(alias + ", what is your answer ?  -> ")
            if message != '':
                s.sendto(alias + ": " + message, server)
        if tShutdown.is_set():
            running = False
    
    rT.join()
    s.close()
    

    Server code:

    import numpy as np
    from numpy.random import shuffle
    from random import randint
    import socket
    import time
    import select
    
    #LIST OF QUESTIONS AND ANSWERS
    question2 = ["What is the capital of France?","In which continent is Argentina?","Where is Big Ben?","What is the most densely populated country?","What language do they speak in Brazil?"]
    answer2 = [["Paris","London","Berlin","Madrid"],
            ["South America","Africa","Europe","Asia"],
            ["London","New York","Mexico","Jakarta"],
            ["China","India","USA","Indonesia"],
            ["Portuguese","Spanish","French","English"]]
    question_done=[0]*(len(question2))
    
    
    #SCORE, stored as a list score[0]--> score of the player 1
    score=[0]
    
    
    #SHOW THE POSSIBLE ANSWERS
    def displayA(question,answer,i):
        a = answer[i]
        order = np.arange(4)
        shuffle(order) #create list from 1 to 4 in different order --> to print the answers in random order
        a_display = [[a[order[0]],a[order[1]]],[a[order[2]],a[order[3]]]]
        print(a_display)
    
    
    #CHOOSE RANDOMLY A QUESTION IN THE LIST
    def chooseQuestion(question,answer):
        k = randint(0,len(question)-1)
        if (question_done[k]!=0):
            while(question_done[k]!=0):
                k = randint(0,len(question)-1)
            question_done[k]=1
        else :
            question_done[k]=1
        print(question[k])
        #displayA(question,answer,k)
        return k
    
    
    #CHECK IF GOOD ANSWER OR NOT
    def checkAnswer(answer,agiven,qnb):
        #print("CHECK")
        test = False
        if(answer[qnb][0] in agiven):
            test = True
            score[0]=score[0]+1
        #print("ANSWER")
        return test
    
    
    
    #END OF GAME, DISPLAY OF SCORES
    def final_score(score):
        print("The scores are {}".format(score))
    
        maxi = max(score)
        if(score.count(maxi)==1):
            print("The winner is Player {}".format(score.index(max(score))+1))
        else :
            winners = []
            for i in range(len(score)):
                if(score[i]==maxi):
                    winners.append(i+1)
            print("The winners are players {}".format(winners))
    
    
    """
    #Number of choosen questions, random order : GIVE THE QUESTION TO THE PLAYER
    nb = 3
    
    for k in range(nb):
       nbq = chooseQuestion(question2,answer2)
       agiven = raw_input("What is your answer?  ")
       result = checkAnswer(answer2,agiven,nbq)
       print("Your answer is {}".format(result))
    
    #Say who the winner is
    final_score(score)
    """
    #START THE NETWORK CODE
    #host = '192.168.26.86'
    host = '127.0.0.1'
    
    port = 5000
    
    #list for all the players
    players = []
    
    #creation of socket object UDP and bind
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.bind((host, port))
    #socket non blocking --> will always try to grab datas from the stream
    #not block it
    s.setblocking(0)
    
    print "Server Started."
    
    #INITIAL SETUP PERIOD
    secs = 20
    max_players = 5
    
    # WAIT FOR PLAYERS TO JOIN
    while ((secs > 0) and (len(players) < max_players)):
        ready = select.select([s], [], [], 1)
        if ready[0]:
            data, addr = s.recvfrom(1024)
            print("FIRST RECV {}".format(data))
            if addr not in players:
                players.append(addr)
                print("liste players {}".format(players))
                s.sendto("Wait for game to start... ",players[len(players)-1])
            print time.ctime(time.time()) +  ":" + str(data)
        secs = secs - 1
    
    #START GAME
    print("Game starting")
    for i in range(len(players)):
        try:
            s.sendto("Game starting", players[i])
        except:
            pass
    
    #ASK QUESTIONS
    nb = 3
    for k in range(nb):
        print("question nb {}".format(k))
        nbq = chooseQuestion(question2,answer2)
        #print("ENTER FOR")
        for i in range(len(players)):
            try:
                s.sendto(str(question2[nbq]), players[i])
                #print("BEFORE GET ANSWER")
                agiven = ""
                ready = select.select([s], [], [], 10)
                if ready[0]:
                    agiven, addr = s.recvfrom(1024)
                    #print("GOT ANSWER")
                print("agiven is : {}".format(agiven))
                checkAnswer(answer2,agiven,nbq)
            except:
                pass
    
    for i in range(len(players)):
        try:
            s.sendto("The game is finished", players[i])
        except:
            pass
    
    final_score(score)
    s.close()