0

I am new to programming and I have some problems with my programm. Wish someone can help me

I am trying to create a rock-paper-scissors game on Python using socket and pyqt5. My programm worked when I ran it without using pyqt5, but after trying to achieve it I got stuck. Hope somebody will explain my mistakes.

In the window I should input "rock" or "paper" or "scissors". Then it should be send to server, and server should generate randomly one of the choices above and check who wins. After it sends results back to client(my window). Also it should be keeping score in window everytime I send data.

Here is my server.py

import socket
import pickle
import random

s=socket.socket()
ip_host=socket.gethostname()
port=3030
s.bind((ip_host, port))
s.listen(5)

c,addr = s.accept()

choices=["rock", "paper", "scissors"]
tie=0
win=0
lose=0

while win!=3 or lose!=3:
    player=pickle.loads(c.recv(1024))
    num=random.randint(0,2)
    cpu=choices[num]
    if player==cpu:
        print("Tie")
        tie+=1
        c.send(pickle.dumps('tie'))
    else:
        if (player=='rock' and cpu=='paper') or (player=='paper' and cpu=='scissors') or (player=='scissors' and cpu=='rock'):
            print("You lose")
            lose+=1
            c.send(pickle.dumps("lose"))
        else:
            print("You win")
            win+=1
            c.send(pickle.dumps("win"))

c.close()
s.close()

And this is my client.py

import socket
import pickle
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import sys

s=socket.socket()
server_host=socket.gethostname()
server_port=3030
s.connect((server_host, server_port))
print("Connected to some server")

class grid_pro(QWidget):
    """grid example"""
    def __init__(self):
        super().__init__()
        self.setGeometry(200,200,200,200)
        self.setWindowTitle("Rock-Paper-Scissors")
        self.label=QLabel("Enter rock, paper or scissors:")
        self.input=QLineEdit()
        self.guess=QPushButton("Guess")
        self.win=QLabel('You: 0')
        self.lose=QLabel("CPU: 0")
        self.result=QLabel("Start")

        self.grid=QGridLayout(self)
        self.grid.addWidget(self.label, 0, 0)
        self.grid.addWidget(self.input, 0, 1)
        self.grid.addWidget(self.guess, 1, 0)
        self.grid.addWidget(self.result, 1, 1)
        self.grid.addWidget(self.win, 2, 0)
        self.grid.addWidget(self.lose, 2, 1)
        self.guess.clicked.connect(self.client)
        self.client()
        self.show()

    def client(self):
        win=0
        lose=0
        tie=0
        while 1:
            self.win.setText('You: %d'%(win))
            self.lose.setText('CPU: %d'%(lose))
            n=str(self.input.text())
            s.send(pickle.dumps(n))
            result=pickle.loads(s.recv(1024))
            if result=='win':
                print("You win")
                win+=1
            elif result=='lose':
                lose+=1
                print("You lose")
            elif result=='tie':
                tie+=1
                print("Tie")
            if win==3 or lose==3:
                break

        self.result.setText("You "+result+'!!')


app=QApplication(sys.argv)
form=grid_pro()
sys.exit(app.exec_())
Postal
  • 1
  • 1
  • First of all: your program has a security vulnerability. By sending malicious data an attacker may be able to execute arbitrary code on your server and your client. If I were you, I'd use [json](https://docs.python.org/3/library/json.html) instead of [pickle](https://docs.python.org/3/library/pickle.html). – Андрей Беньковский Mar 17 '16 at 09:12

1 Answers1

0

Why would you use an infinite loop in the client slot if you make use of a UI? That's mostly needed when your application runs from console and therefore you need to prompt the user every time to enter some new value. When using a UI that's not needed because there is already an infinite loop which is taking care of redrawing the UI for you:

import socket
import pickle
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
import sys

s=socket.socket()
server_host=socket.gethostname()
server_port=3030
s.connect((server_host, server_port))
print("Connected to some server")

class grid_pro(QWidget):
    """grid example"""
    def __init__(self):
        super().__init__()
        self.setGeometry(200, 200, 200, 200)
        self.setWindowTitle("Rock-Paper-Scissors")
        self.label = QLabel("Enter rock, paper or scissors:")
        self.input = QLineEdit()
        self.guess = QPushButton("Guess")
        self.win = QLabel('You: 0')
        self.lose = QLabel("CPU: 0")
        self.result = QLabel("Start")

        self.grid=QGridLayout(self)
        self.grid.addWidget(self.label, 0, 0)
        self.grid.addWidget(self.input, 0, 1)
        self.grid.addWidget(self.guess, 1, 0)
        self.grid.addWidget(self.result, 1, 1)
        self.grid.addWidget(self.win, 2, 0)
        self.grid.addWidget(self.lose, 2, 1)
        self.guess.clicked.connect(self.client)
        self.client()
        self.show()

    @pyqtSlot()
    def client(self):
        win = 0
        lose = 0
        tie = 0
        self.win.setText('You: %d' % (win))
        self.lose.setText('CPU: %d' % (lose))
        n = str(self.input.text())
        s.send(pickle.dumps(n))
        result = pickle.loads(s.recv(1024))

        if result=='win':
            print("You win")
            win += 1
        elif result=='lose':
            lose += 1
            print("You lose")
        elif result=='tie':
            tie += 1
            print("Tie")

        self.result.setText("You %s!!" % result)


app = QApplication(sys.argv)
form = grid_pro()
sys.exit(app.exec_())

Note that I added the @pyqtSlot() decorator to the client slot so you need to import it from PyQt5.QtCore (it's good practice to specifically decorate signal's slots using this decorator, even though it would work with a normal python callable).

Also you should modify your code in order to terminate the client-server connection properly. As far as I understood by reading your code, every match lasts at most 5 rounds (the first who get to 3 wins), and then you close the connection (so to play another game you need to restart the server). I would instead reset the scores whenever a match ends without closing the connection and implement in the client another slot, which can be triggered by a QPushButton, that sends a 'QUIT' string (or whatever else you prefer) to the server, that when reading such string will terminate the connection (and the client will follow accordingly since he is aware that the game is terminating because he sent the 'QUIT' string). Right now, when the match is over the server closes the connection, and you'll have a socket error on the client.

Daniele Pantaleone
  • 2,657
  • 22
  • 33