Sockets - Old Game / Rooster Game MULTIPLAYER

1

EDITED POST

I'm creating the so-called 'Game of the Old Man' (or 'Game of the Rooster'), at this time I'm trying to create a feature that allows two players on different devices - albeit on the same network - to play against each other .

This is an excerpt of the server code on which I am trying to make the game () function - which encompasses the whole game (inputs to the nicknames, framework, possibilities to win etc. ...), but in the encode part it is supposed to cause this function to run in the client program, in which case it runs in the server program)!

Any help?

def Main():
    host = "127.0.0.1"
    port = 5000

    mySocket = socket.socket()
    mySocket.bind((host,port))

    mySocket.listen(1)
    conn, addr = mySocket.accept()
    print ("Connection from: " + str(addr))
    while True:
            data = conn.recv(1024).decode()
            if not data:
                    break
            print ("from connected  user: " + str(data))

            data = game
            print ("sending: ")
            conn.send(data().encode())

    conn.close()

if __name__ == '__main__':
    Main()
    
asked by anonymous 26.11.2016 / 23:12

1 answer

3

From the code example you provided, you seem to have understood that socket communication allows you to send and receive messages. Okay. But you insist on trying to run a function remotely (or associate a function with a TCP / IP client), and that is not so trivial . Sockets can be used to do this? Of course, but you will need to implement a "remote call" feature manually. If that is your intention, look for libraries that already do this sort of thing (RPC, Remote Procedure Call, or Remote Procedure Call), since reinventing the wheel is nonsense. Some examples can be found this SOen link .

On the other hand, games do not usually call remote procedures. They usually communicate between the server and the player (s) the game state . So your problem is actually how to represent the state of the game so that it can be transmitted via sockets.

The most common is serialize / deserialize an object that represents the state of the game , and use the bytes representing that state in the communication. Python has a lot of options for serialization, just take a look around ( link suggestion ). But in your case, as it is trivial, my suggestion is to convert the array of parts of the board into a string. Easy, fast and inspectable! (just print the result of save , for example).

So I made an example class that represents the game board:

import numpy as np
from random import *

class GameState:
    """
    Classe que representa o estado do jogo.
    """

    # -------------------------------------------------
    def __init__(self):
        """
        Construtor. Initializa o tabuleiro 3x3 vazio.
        """
        self.board = [[''] * 3 for n in range(3)]

    # -------------------------------------------------
    def save(self):
        """
        Salva os dados do tabuleiro para uma string.

        Gera uma string com as peças do tabuleiro separadas por
        ponto-e-vírgula (';'), de forma que o estado do jogo possa
        ser comunicado via socket.

        Retorno
        ----------
        data: str
            String de texto com os dados do tabuleiro separados por
            ponto-e-vírgula (';'), prontos para serem comunicados.     
        """
        return ';'.join([';'.join(x) for x in self.board])

    # -------------------------------------------------
    def restore(self, data):
        """
        Restaura os dados do tabuleiro a partir de uma string.

        Lê uma string com as peças do tabuleiro separadas por
        ponto-e-vírgula (';'), de forma que o estado do jogo possa ser
        comunicado via socket.

        Parâmetros
        ----------
        data: str
            String de texto com os dados do tabuleiro separados por um
            ponto-e-vírgula (';'), prontos para serem atualizados neste
            objeto.
        """
        self.board = np.reshape(data.split(';'), (3,3)).tolist()

    # -------------------------------------------------
    def print(self):
        """
        Imprime o tabuleiro em um formato visual.
        """
        print("+---+---+---+")
        for row in self.board:
            print('|{}|{}|{}|'.format(row[0].center(3, ' '), row[1].center(3, ' '), row[2].center(3, ' ')))
            print("+---+---+---+")

    # -------------------------------------------------
    def move(self, row, col, piece):
        """
        Faz uma jogada no tabuleiro, nas posições dadas.

        Parâmetros
        ----------
        row: int
            Número da linha no tabuleiro, no intervalo [0,2].
        col: int
            Número da coluna no tabuleiro, no intervalo [0,2].
        piece: str
            Letra com o símbolo jogado, entre as opções 'o' e 'x'.        
        """

        # Valida os parâmetros de entrada
        if row < 0 or row > 2:
            raise RuntimeError('Número de linha inválido: {}'.format(row))
        if col < 0 or col > 2:
            raise RuntimeError('Número de coluna inválido: {}'.format(col))
        piece = piece.lower()
        if piece != 'x' and piece != 'o':
            raise RuntimeError('Peça inválida: {}'.format(piece))

        # Verifica se a posição jogada está vazia
        if self.board[row][col] != '':
            raise RuntimeError('Posição do tabuleiro já preenchida: {}x{}'.format(row, col))

        # Faz a jogada
        self.board[row][col] = piece

    # -------------------------------------------------
    def moveRandom(self, piece):
        """
        Faz uma jogada aleatória no tabuleiro, em uma das posições vazias.

        Parâmetros
        ----------
        piece: str
            Letra com o símbolo jogado, entre as opções 'o' e 'x'.
        """

        # Cria uma lista com as posições vazias
        options = []
        for row in range(3):
            for col in range(3):
                if self.board[row][col] == '':
                    options.append((row, col))

        # Faz uma permutação aleatória nessa lista
        shuffle(options)

        # Faz a jogada na primeira posição da lista
        if len(options) > 0:
            row = options[0][0]
            col = options[0][1]
            self.move(row, col, piece)

The code is documented, but the cat's leap is in the save and restore methods that respectively generate a string for you to send via socket and restores the tray from a string received via socket. So you can use this to "assemble" your game more or less this way:

Server:

import socket
from gamestate import GameState

# Cria o socket TCP/IP
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Faz o bind no endereco e porta
server_address = ('localhost', 5000)
sock.bind(server_address)

# Fica ouvindo por conexoes
sock.listen(1)

while True:

    print('Aguardando a conexao do jogador')
    connection, client_address = sock.accept()

    try:
        print('Jogador chegou! :)')

        # Cria um tabuleiro de jogo vazio
        board = GameState()

        # Faz uma jogada aleatoria
        board.moveRandom('o')
        print('Eu joguei:')
        board.print()

        # Envia o tabuleiro para o jogador
        connection.sendall(board.save().encode('utf-8'))

        # Processa em loop
        while True:
            # Recebe a jogada do jogador
            data = connection.recv(1024)

            # Checa se a conexao do jogador foi terminada
            if not data:
                print('Jogador se foi. :(')
                break

            # Converte para string e restaura no tabuleiro
            board.restore(data.decode('utf-8'))

            print('O jogador jogou:')
            board.print()

            # Faz outra jogada aleatoria
            board.moveRandom('o')
            print('Eu joguei:')
            board.print()

            # Envia o tabuleiro para o jogador
            connection.sendall(board.save().encode('utf-8'))

    finally:
        # Clean up the connection
        connection.close()

Client:

import socket
import sys
from gamestate import GameState

# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Connect the socket to the port where the server is listening
server_address = ('localhost', 5000)
print('Conectando ao servidor {} na porta {}'.format(server_address[0], server_address[1]))
sock.connect(server_address)

# Cria um tabuleiro de jogo vazio
board = GameState()

try:

    while True:

        # Recebe a jogada do servidor
        data = sock.recv(1024)
        board.restore(data.decode('utf-8'))

        print('O servidor jogou:')
        board.print()

        print('Faça a sua jogada:')
        print('------------------')

        nok = True
        while nok:
            row = int(input('Digite a linha:'))
            col = int(input('Digite a coluna:'))

            nok = False
            try:
                board.move(row, col, 'x')
            except:
                nok = True
                print('Linha ou coluna inválida. Tente novamente.')

        # Envia o tabuleiro para o servidor
        sock.sendall(board.save().encode('utf-8'))

finally:
    print('Encerrando o cliente')
    sock.close()

Test session result:

No servidor:
-----------------------------

Aguardando a conexao do jogador
Jogador chegou! :)
Eu joguei:
+---+---+---+
| o |   |   |
+---+---+---+
|   |   |   |
+---+---+---+
|   |   |   |
+---+---+---+
O jogador jogou:
+---+---+---+
| o |   |   |
+---+---+---+
|   |   | x |
+---+---+---+
|   |   |   |
+---+---+---+
Eu joguei:
+---+---+---+
| o | o |   |
+---+---+---+
|   |   | x |
+---+---+---+
|   |   |   |
+---+---+---+

No cliente:
------------------
Conectando ao servidor localhost na porta 5000
O servidor jogou:
+---+---+---+
| o |   |   |
+---+---+---+
|   |   |   |
+---+---+---+
|   |   |   |
+---+---+---+
Faça a sua jogada:
------------------
Digite a linha:1
Digite a coluna:2
O servidor jogou:
+---+---+---+
| o | o |   |
+---+---+---+
|   |   | x |
+---+---+---+
|   |   |   |
+---+---+---+
Faça a sua jogada:
------------------
Digite a linha:
    
29.11.2016 / 20:20