By the example of code you provided, you seem to have understood that communication via socket allows you to send and receive messages. Ok. But you insist on trying to run a function remotely (or associate a function to a TCP/IP client), and this is not so trivial. Can sockets be used to do this? Of course, but you will need to implement a "remote call" feature manually. If this is your intention, look for libraries that already do this kind of thing (RPC, of Remote Procedure Call, or Remote Procedure Call), because reinventing the wheel is silly. Some examples can be found this link from Soen.
On the other hand, games do not usually make remote call procedures. They usually communicate between the server and the player(s) (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 that represent 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 something trivial, my suggestion is to convert the tile matrix of the board into a string. Easy, fast and inspectable! (just print the result of save
, for example).
So I made a class example 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 jump is in the methods save
and restore
which respectively generate a string for you to send via socket and restore the board from a string received via socket. So you can use this to "ride" your game more or less that 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()
Result of a test session:
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:
To my knowledge, Tictactoe is the game of old. I did not know the term "game of the rooster".
– Victor Stafusa
@Victorstafusa I’m from Portugal and not from Brazil, also was unaware of the term "game of old", hug!
– SWABADZU
Hi Swabadzu. Really, your question is wide, because you don’t exactly have a question/difficulty regarding the use of communication in networks. I suggest you first try to do some very basic sockets usage tutorial (to communicate text even between one point and another). This one, for example: http://wiki.python.org.br/SocketBasico. So you can think about how to implement what you need.
– Luiz Vieira
The communication will be essentially the same, but it is up to you to implement the remote command interface. That is, you will need to be able to send and receive some identifier that contains the "move" (in which position the player acted). There are several ways to think about it (it is not possible to "associate" the function
playerX
to a communication; you need to execute it on the client/player side, and then send the "instruction" with the action to the server side). But make sure before that you already know how to communicate (so I suggested the tutorial).– Luiz Vieira
@Luizvieira Thank you very much, that was really helpful!
– SWABADZU
Changed code, more specific question!
– SWABADZU
Still confused. What is
game
? Would it be a function? If yes, when you doconn.send(data().encode())
, Python first executes the functiondata()
(which is equal to executegame()
), then encodes what this function returns (data().encode()
) and then sends this result encoded by the connection (conn.send(data().encode())
). If you want to send the "object" (?)data
, you should doconn.send(data.encode())
. But this will depend on your object being capable of encoding (i.e., having a methodencode
).– Luiz Vieira
@Luizvieira game() is a function that encompasses the whole game (inputs, the framework of the game, the chances of winning, basically everything) and I’m trying to pass it on to the customer, but it’s not giving
– SWABADZU
But why do you want to pass all this to the client? Ideally the client already has this code, and the server just pass it to state current game.
– Luiz Vieira
You’ve made a simpler program that communicates text between server and client? Already understood this part? If so, I suggest the following approach: edit the issue again and post the code you already tried (to demonstrate that you at least understood what network communication is like). Then, ask how to transmit state information (from a game) using this same mechanism. So I vote to reopen the question, and if possible I even try to give an answer.
– Luiz Vieira
@Luizvieira The excerpt of the code I put transmits text between the server and the client by changing the variable date from "game" to a string. I tested and it works, the only thing I changed was passing text to the full function itself ( game() )!
– SWABADZU
Okay. I’m going to vote to reopen. If you reopen, I’ll try to answer.
– Luiz Vieira
@Luizvieira I think it’s already reopened!
– SWABADZU
Ah, okay. I’ll try to prepare an answer. :)
– Luiz Vieira