associação pythonbrasil[11] django zope/plone planet Início Logado como (Entrar)

PequenoUsoDeSockets

Receita: Pequeno uso de sockets

Este é o primeiro programa que fiz em Python. Ele explora um pouco o uso de sockets. Quando fiz o programa enfrentei uma certa dificuldade em encontrar material explicando o uso de sockets para iniciantes, portanto o que consegui fazer é o que segue. Minha intenção com este CookBook é tentar ajudar os novos usuarios da linguagem e pedir ajuda aos mais experientes para desenvolver um programa mais otimizado e bem feito. Espero que isso sirva de aprendizado para todos.

Código

   1 import socket
   2 import sys
   3 
   4 class LenghtError(Exception):
   5     pass
   6 class RepeatedDigitsError(Exception):
   7     pass
   8 
   9 def testNum(num): 
  10 """Funcao para testar se o número possui digitos repetidos ou
  11 se é maior que 4."""
  12     if len(num) != 4:
  13         raise LenghtError 
  14     for i in num:
  15         if num.count(i) > 1: raise RepeatedDigitsError
  16         
  17 class GameCon:
  18     """Classe para cuidar da conexao do jogo"""
  19     def __init__(self):
  20         self.sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  21 
  22     def con(self, ip, port=1911):
  23         self.sock.connect((ip, port))
  24 
  25     def waitCon(self, ip='', port=1911, l=1):
  26         self.sock.bind((ip, port))
  27         self.sock.listen(l) 
  28         (clientsocket, address) = self.sock.accept()
  29         self.sock = clientsocket 
  30     
  31     def send(self, msg):
  32         sent = self.sock.send(msg)
  33         if sent == 0:
  34             raise BrokenCon, "socket connection broken"
  35 
  36     def receive(self, bytes):
  37         return self.sock.recv(bytes)
  38 
  39     def close(self):
  40         self.sock.close()
  41 
  42 
  43 class Senha: 
  44     """Classe do jogo senha"""
  45     def __init__(self, myNum, hisNum):
  46         self.myNum  = str(myNum)  #Numero do cliente
  47         self.hisNum = str(hisNum) #Numero do server(adversario)
  48 
  49     def check(self, checkNum):
  50     """Metodo para checar o numero de bombas e tiros de um determinado
  51 numero em relacao ao self.hisNum(numero do adversrio)"""
  52         total    = 0  #Total de numeros coicidentes
  53         bombs    = 0
  54         for a in checkNum:
  55             total+=self.hisNum.count(a)
  56             
  57         for i in xrange(4):
  58             if checkNum[i] == self.hisNum[i]: bombs+=1
  59 
  60         shots = total - bombs
  61         return bombs, shots
  62 
  63    
  64 def sender(skt, pwd):
  65 """Funcao que recebe o numero a ser testado e o envia para o
  66 adversario e verifica vitoria"""
  67     while True:
  68         try:
  69             num = raw_input('Digite o numero a ser testado: ').strip()
  70             testNum(num)
  71             skt.send(num)
  72             break
  73         except LenghtError:
  74             print '!!\nO numero deve ter quatro digitos\n...'
  75         except RepeatedDigitsError:
  76             print '!!\nO numero nao pode ter digitos repetidos\n...'
  77         
  78        
  79     print 'Bombas: %s Tiros: %s' % pwd.check(num)
  80     if num == pwd.hisNum: # Se o numero que testei eh igual do
  81                           # adversario, eu ganho
  82         print '\n\nVOCE GANHOU! Parabens :-)'
  83         skt.close()
  84         if raw_input('Jogar denovo? [s/n] ').startswith('s'):
  85             main()
  86         else: sys.exit()
  87     getter(skt, pwd) # Um metodo que chama o outro num loop
  88                      # continuo, deve ser a pior solucao para
  89                      # meu problema :-/
  90 
  91 def getter(skt, pwd):      
  92 """Funcao que recebe o numero tentado pelo adversrio, o imprime
  93 na tela e verifica derrota"""
  94     print '\nEsperando a escolha do adversario...'
  95     hisNum = skt.receive(4)
  96     print '::Ele chutou: %s\n' % hisNum
  97     if hisNum == pwd.myNum:
  98         print '\n\nVOCE PERDEU! :-('
  99         skt.close()
 100         if raw_input('Jogar denovo? [s/n] ').startswith('s'):
 101             main()
 102         else: sys.exit()
 103     sender(skt, pwd)
 104 
 105 def main():
 106     talker = GameCon()           #Cria um objeto socket
 107     while True:
 108         print '1. Servidor\n2. Cliente'
 109         choice = input('Escolha: ')
 110         if choice == 1 or choice == 2: break
 111 
 112     if choice == 1:
 113         print 'Esperando conexao...'
 114         talker.waitCon()
 115             
 116     else:
 117         while True:
 118             ip = raw_input('Digite o IP do servidor: ').strip()
 119             try:
 120                 talker.con(ip)
 121                 break
 122             except: print 'Conexao nao aceita'
 123     
 124     while True:
 125         try:
 126             myNum = raw_input('Digite seu numero: ').strip()
 127             testNum(myNum)
 128             talker.send(myNum)
 129             hisNum = talker.receive(4)
 130             game = Senha(myNum, hisNum)
 131             break
 132         except LenghtError:
 133             print '!!\nO numero deve ter quatro digitos\n...'
 134         except RepeatedDigitsError:
 135             print '!!\nO numero nao pode ter digitos repetidos\n...'
 136 
 137 
 138     if choice == 1:
 139        sender(talker, game)
 140     else:
 141        getter(talker, game)
 142 
 143 if __name__ == '__main__':
 144     main()

Explicação do jogo

Senha é um jogo a ser jogado por duas pessoas, os dois escolhem suas respectivas senhas (números de quatro digitos sem repetição de digito) e tentam adivinhar a do adversario. Para conseguir descobrir a senha do outro o jogador chuta um número qualquer e, se tiver acertado a posição de algum digito ele fez uma "bomba", se tiver acertado um digito mas não na posição correta ele fez um "tiro" (ex. sendo o número do adversário 1234, e o chute 3214, o jogador fez dois tiros e duas bombas).

A versão do jogo na internet precisa que o chute seja alternado (cada hora um). Os dois jogadores tem em seu computador o número escolhido e o número do adversario, então a conexão existe apenas para saber quando é a vez de cada um jogar e enviar o número testado para o adversário poder saber quão perto esta da derrota (ou não). Creio que um metodo melhor seria que cada cliente recebesse o número a ser testado do adversário e retornasse a este o tanto de bombas e tiros acertados. Todavia, acho que já deu para pegar o espirito da coisa. Espero alterações no código e sugestões.

Vamos lá pessoal. Eu sugeri que o Rafael colocasse esse jogo aqui para que todos venham aqui palpitar, sugerir mudanças, sugerir outros programas inteiro. Peço para que vocês não alterem o código original do Rafael e façam as suas modificações abaixo desta mensagem aqui. No final de tudo, eu vou coletar esses dados, acertar a formatação e tranformar num tutorial de criação de aplicações para iniciantes. O que acharam da idéia? -- OsvaldoSantanaNeto

Comentários

Ei cara... tipo... eu jah vi isso em varios códigos Python, mas eu faço diferente... no código acima, essas duas linhas:

   1 if __name__ == '__main__':
   2     main()

chama a função principal do programa, ou seja, inicia o programa... eu, porém, depois de definir todo o programa coloco na ultima linha do código a chamada para a função main, ou seja, coloco somente:

   1 main()

isso tah certo? queria saber como funciona essa variável __name__ e tal... e queria saber se eh aconselhavel fazer como no código acima ou se tanto faz....

flw.. João Norberto de Souza Junior - Curitiba - PR


A maneira que você chama o main() funciona normalmente. Entretanto, é recomendado o uso da verificação if __name__ == '__main__': pelo seguinte motivo demonstrado abaixo:

   1 # modulo.py - arquivo de modulo
   2 
   3 if __name__ == '__main__':
   4    print "Executado do arquivo..."
   5 
   6 print "Executado ao importar módulo: " + __name__

Depois de gerado esse arquivo faça o seguinte:

$ python modulo.py
Executando do arquivo...
Executado ao importar módulo: __main__

$ python
>>> import modulo
Executado ao importar o módulo: modulo
>>>

Ou seja, quando você executa o arquivo diretamente com o interpretador Python ele coloca __main__ dentro da variável __name__, já quando o módulo é importado ele coloca o nome do módulo nesta variável. Portanto esse if serve para evitar que o código seja executado quando o módulo é importado. -- OsvaldoSantanaNeto


Volta para CookBook.


RafaelAlmeida (aFlag)