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:
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:
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)