ROCK, PAPER, LO QUE - UN PEQUEÑO JUEGO DE LINE -- python campo con python-3.x campo con graph campo con community-challenge campo con rock-paper-scissors camp codereview Relacionados El problema

Rock, Paper, Whatever - A small commandline game


9
vote

problema

Español

Mi racional para este programa, fue para aprender más de la biblioteca estándar de Python, no conocí en realidad cmd fue incluso en ella hasta que comencé este programa. También es el primer desafío comunitario , y tengo algunos altos que hacer.

Esta es una versión demasiado diseñada del famoso juego de tijeras de rock. Por defecto, es exactamente lo mismo, pero permite que las personas definen sus propias armas, como las tijeras de papel de roca lagarto Spock, o las menos les gustan la motosierra de las tijeras de papel de roca. Para hacer esto, usé 9988776655544331 para un INI Como formato de entrada. Las secciones son los ganadores, donde las llaves que siguen son los perdedores. También permito que las claves se les pasen valores que son la acción ganadora, por defecto de 'Beats'. Aquí hay un ejemplo de un archivo de configuración utilizado para tijeras de papel de roca lagarto Spock (guardado como rpsls y se usa en el ejemplo):

  [rock] scissors: crushes lizard: crushes  [paper] rock: covers spock: disproves  [scissors] paper: cuts lizard: decapitates  [lizard] paper: eats spock: poisons  [spock] rock: vaporizes scissors: smashes 3  

Para simplificar el uso de este programa. Hice algunas clases de gráficos y nodos dirigidos simples que heredan de dict . Que creo que sigue sólido.

También usé 9988776655544335 para crear Una simple interfaz de línea de comandos. No necesitaba ser nada de lujo, por lo que parecía la mejor solución, y más sencilla.

Esto también funciona solo en Python 3.6, ya que usé F-STRINGS.

  import cmd import configparser import itertools import random  class RPSException(Exception):     pass   class DirectedGraph(dict):     class Node(dict):         __slots__ = ['name', '_graph']         def __init__(self, graph, name):             self.name = name             self._graph = graph          def add_edge(self, node, edge):             self[node.name] = edge      def adjacent(self, x, y):         return x in self[y] or y in self[x]      def add_node(self, *args, **kwargs):         node = self.Node(self, *args, **kwargs)         self[node.name] = node      def add_edge(self, node_x, node_y, edge):         self[node_x].add_edge(node_y, edge)      def missing_edges(self):         for keys in itertools.combinations(self.keys(), 2):             if not self.adjacent(*keys):                 yield keys    class RPSGraph(DirectedGraph):     class Node(DirectedGraph.Node):         def __str__(self):             return f'{self.name}:   '                    + '   '.join(f'{v[1]} {v[0].name}' for v in self.values())          def add_edge(self, node_name, action):             node = self._graph[node_name]             edge = (node, action)             super().add_edge(node, edge)      def __str__(self):         return ' '.join(str(node) for node in self.values())    def parse_config(cfg):     graph = RPSGraph()     for node in cfg.sections():         graph.add_node(node)      for winner in cfg.sections():         for looser, action in cfg[winner].items():             graph.add_edge(winner, looser, action or 'beats')      missing = list(graph.missing_edges())     if missing:         raise RPSException(f"Invalid configuration file. You're missing vertices between: {missing}")      return graph   class RPS(cmd.Cmd):     intro = "Welcome to Rock, Paper, Whatever. Type help or ? to list commands. "     prompt = '> '      cfg = configparser.ConfigParser(allow_no_value=True)     cfg.read_string('''     [rock]     scissors: crushes      [paper]     rock: covers      [scissors]     paper: cuts     ''')     graph = parse_config(cfg)     del cfg      def do_load_file(self, filename):         '''Load a custom variation of rock paper scissors from a file.'''         cfg = configparser.ConfigParser(allow_no_value=True)         cfg.read(filename)         self.graph = parse_config(cfg)      @staticmethod     def read_lines():         try:             while True:                 yield input()         except KeyboardInterrupt:             pass       def do_load_stdin(self, _):         '''Load a custom variation of rock paper scissors from the command line. Send a keyboard interrupt to end input.'''          data = ' '.join(self.read_lines())          cfg = configparser.ConfigParser(allow_no_value=True)         cfg.read_string(data)         self.graph = parse_config(cfg)      def do_play(self, weapon):         '''Play a weapon. E.g. play rock'''         graph = self.graph         if weapon not in graph:             print('Invalid weapon.')             return          ai = random.choice(list(graph.keys()))         if ai == weapon:             print('You draw')         elif ai in graph[weapon]:             print(f'You win. {weapon.capitalize()} {graph[weapon][ai][1]} {ai}')         else:             print(f'You loose. {ai.capitalize()} {graph[ai][weapon][1]} {weapon}')      def do_weapons(self, _):         '''List usable weapons.'''         print(', '.join(self.graph.keys()))   if __name__ == '__main__':     RPS().cmdloop()   

Ejemplo de uso:

  Welcome to Rock, Paper, Whatever. Type help or ? to list commands.  > help  Documented commands (type help <topic>): ======================================== help  load_file  load_stdin  play  weapons  > help play Play a weapon. E.g. play rock > help weapons List usable weapons. > weapons rock, paper, scissors > play rock You win. Rock crushes scissors > load_file rpsls > weapons rock, paper, scissors, lizard, spock > play rock You loose. Spock vaporizes rock > load_stdin [rock] scissors: crushes  [paper] rock: covers  [scissors] paper: cuts > weapons rock, paper, scissors > play rock You draw >   
Original en ingles

My rational for this program, was to learn more of Python's standard library, I didn't actually know cmd was even in it until I started this program. It's also the first community challenge, and I have some catching up to do.

This is an overly engineered version of the famous Rock Paper Scissors game. By default it's the exact same, but allows for people to define their own weapons, such as Rock Paper Scissors Lizard Spock, or the less liked Rock Paper Scissors Chainsaw. To do this I used configparser for an INI like input format. The sections are the winners, where the keys that follow are the losers. I also allow keys to be passed values which are the winning action, defaulting to 'beats'. Here is an example of a configuration file used for Rock Paper Scissors Lizard Spock (saved as rpsls, and used in the example):

[rock] scissors: crushes lizard: crushes  [paper] rock: covers spock: disproves  [scissors] paper: cuts lizard: decapitates  [lizard] paper: eats spock: poisons  [spock] rock: vaporizes scissors: smashes 

To simplify the usage of this program I made some simple directed Graph and Node classes that inherit from dict. Which I think follow SOLID.

I also used cmd to create a simple command line interface. It didn't need to be anything fancy, and so it seemed like the best, and simplest, solution.

This also only works in Python 3.6, as I used f-strings.

import cmd import configparser import itertools import random  class RPSException(Exception):     pass   class DirectedGraph(dict):     class Node(dict):         __slots__ = ['name', '_graph']         def __init__(self, graph, name):             self.name = name             self._graph = graph          def add_edge(self, node, edge):             self[node.name] = edge      def adjacent(self, x, y):         return x in self[y] or y in self[x]      def add_node(self, *args, **kwargs):         node = self.Node(self, *args, **kwargs)         self[node.name] = node      def add_edge(self, node_x, node_y, edge):         self[node_x].add_edge(node_y, edge)      def missing_edges(self):         for keys in itertools.combinations(self.keys(), 2):             if not self.adjacent(*keys):                 yield keys    class RPSGraph(DirectedGraph):     class Node(DirectedGraph.Node):         def __str__(self):             return f'{self.name}:\n  '\                    + '\n  '.join(f'{v[1]} {v[0].name}' for v in self.values())          def add_edge(self, node_name, action):             node = self._graph[node_name]             edge = (node, action)             super().add_edge(node, edge)      def __str__(self):         return '\n'.join(str(node) for node in self.values())    def parse_config(cfg):     graph = RPSGraph()     for node in cfg.sections():         graph.add_node(node)      for winner in cfg.sections():         for looser, action in cfg[winner].items():             graph.add_edge(winner, looser, action or 'beats')      missing = list(graph.missing_edges())     if missing:         raise RPSException(f"Invalid configuration file. You're missing vertices between: {missing}")      return graph   class RPS(cmd.Cmd):     intro = "Welcome to Rock, Paper, Whatever. Type help or ? to list commands.\n"     prompt = '> '      cfg = configparser.ConfigParser(allow_no_value=True)     cfg.read_string('''\     [rock]     scissors: crushes      [paper]     rock: covers      [scissors]     paper: cuts     ''')     graph = parse_config(cfg)     del cfg      def do_load_file(self, filename):         '''Load a custom variation of rock paper scissors from a file.'''         cfg = configparser.ConfigParser(allow_no_value=True)         cfg.read(filename)         self.graph = parse_config(cfg)      @staticmethod     def read_lines():         try:             while True:                 yield input()         except KeyboardInterrupt:             pass       def do_load_stdin(self, _):         '''Load a custom variation of rock paper scissors from the command line. Send a keyboard interrupt to end input.'''          data = '\n'.join(self.read_lines())          cfg = configparser.ConfigParser(allow_no_value=True)         cfg.read_string(data)         self.graph = parse_config(cfg)      def do_play(self, weapon):         '''Play a weapon. E.g. play rock'''         graph = self.graph         if weapon not in graph:             print('Invalid weapon.')             return          ai = random.choice(list(graph.keys()))         if ai == weapon:             print('You draw')         elif ai in graph[weapon]:             print(f'You win. {weapon.capitalize()} {graph[weapon][ai][1]} {ai}')         else:             print(f'You loose. {ai.capitalize()} {graph[ai][weapon][1]} {weapon}')      def do_weapons(self, _):         '''List usable weapons.'''         print(', '.join(self.graph.keys()))   if __name__ == '__main__':     RPS().cmdloop() 

Example usage:

Welcome to Rock, Paper, Whatever. Type help or ? to list commands.  > help  Documented commands (type help <topic>): ======================================== help  load_file  load_stdin  play  weapons  > help play Play a weapon. E.g. play rock > help weapons List usable weapons. > weapons rock, paper, scissors > play rock You win. Rock crushes scissors > load_file rpsls > weapons rock, paper, scissors, lizard, spock > play rock You loose. Spock vaporizes rock > load_stdin [rock] scissors: crushes  [paper] rock: covers  [scissors] paper: cuts > weapons rock, paper, scissors > play rock You draw > 
              
         
         

Lista de respuestas

6
 
vote

primero las cosas primero:

1.PEP8

Ejecute cualquier LINTER, ya que su código viola PEP8 .

  • Se deben usar cotizaciones dobles de triple en doctrings

  • se recomiendan las distancias de línea (backslashes), la forma preferida es paréntesis

2. Clases anidadas

Hablando de su estructura de clase de gráficos. Intento evitar las clases internas (anidadas) ya que causan más problemas (E.G Pickling, Pruebas, menos legibles para mí) en lugar de ayudarlo a definir un alcance.

3. Mejoras de código

  class RPS(cmd.Cmd):     ...     cfg = configparser.ConfigParser(allow_no_value=True)     cfg.read_string('''     [rock]     scissors: crushes      [paper]     rock: covers      [scissors]     paper: cuts     ''')     graph = parse_config(cfg)     del cfg     ...   

No soy realmente un fanático de tener lógicas dentro de una definición del cuerpo de clase, creo que es mejor hacer que Configury sea una propiedad perezosa que sea cuando no esté definida y llamada, hará este truco para usted.

ahora aquí:

  ai = random.choice(list(graph.keys())) print(', '.join(self.graph.keys()))   

Realmente no tiene que llamar a .Keys () Método ya que por defecto entra por encima de las teclas de diccionario. Para que puedas hacer:

  print(', '.join(self.graph))   

esto:

  def missing_edges(self):     for keys in itertools.combinations(self.keys(), 2):         if not self.adjacent(*keys):             yield keys   

puede ser reemplazado por una combinación de filter y 9988776655543355 Tampoco está claro por qué define esto como un 9988776655544336 , mientras que en el lugar único que lo usa, lo arrojas a list .

Cuando ya tiene una función, prefiero usar map en lugar de list comprehensions por lo que en lugares como ese:

  ai = random.choice(list(graph.keys())) print(', '.join(self.graph.keys())) 0  

Iré por

  ai = random.choice(list(graph.keys())) print(', '.join(self.graph.keys())) 1  
 

First things first:

1.PEP8

Please run any linter, since your code violates PEP8.

  • Tripple double quotes should be used in doctrings

  • Linebreaks(backslashes) are not recommended, preferred way is parenthesis

2. Nested classes

Speaking about your graph class structure. I try to avoid inner(nested) classes since they cause more troubles(e.g pickling, testing, less readable for me) rather than helping you defining a scope.

3. Code improvements

class RPS(cmd.Cmd):     ...     cfg = configparser.ConfigParser(allow_no_value=True)     cfg.read_string('''\     [rock]     scissors: crushes      [paper]     rock: covers      [scissors]     paper: cuts     ''')     graph = parse_config(cfg)     del cfg     ... 

I'm not really a fan of having logics within a class body definition, I think better make config a lazy property that is when not defined and called will do this trick for you.

Now here:

ai = random.choice(list(graph.keys())) print(', '.join(self.graph.keys())) 

You don't really have to call .keys() method since by default it iterates over dictionary keys. So you can just do:

print(', '.join(self.graph)) 

This:

def missing_edges(self):     for keys in itertools.combinations(self.keys(), 2):         if not self.adjacent(*keys):             yield keys 

Can be replaced by combination of filter and yield from statement. It's also not clear why you define this as a generator, while in the only place you use it, you cast it to list.

When you have already a function I prefer using map instead of list comprehensions so in places like that:

return '\n'.join(str(node) for node in self.values()) 

I will go for

return '\n'.join(map(str, self.values())) 
 
 
       
       
5
 
vote

Creo que una estadística de ganar / perder / dibujar sería realmente agradable. Es bastante fácil agregar:

  ai = random.choice(list(graph.keys())) print(', '.join(self.graph.keys())) 2  

De lo contrario, esto está realmente bien escrito. Durante algún tiempo, estaba contemplando si realmente necesita la clase 99887766655443313 , pero la impresión y la adición del borde son lo suficientemente diferentes como para justificar una clase separada, así que, bien hecho.

 

i think a win/lose/draw statistics would be really nice. It is quite easy to add:

class RPS(cmd.Cmd):     ...     def __init__(self, *args, **kwargs):         self.win = self.lose = self.draw = 0         super().__init__(*args, **kwargs)      ...          def do_play(self, weapon):         ...         if ai == weapon:             self.draw += 1             print('You draw')         elif ai in graph[weapon]:             self.win += 1             print(f'You win. {weapon.capitalize()} {graph[weapon][ai][1]} {ai}')         else:             self.lose += 1             print(f'You lose. {ai.capitalize()} {graph[ai][weapon][1]} {weapon}')         print(f'W/L/D: {self.win}/{self.lose}/{self.draw}') 

Otherwise this is really well written. For some time I was contemplating if you really need the RPSGraph class, but the printing and the edge adding is different enough to warrant a separate class, so, well done.

 
 
       
       

Relacionados problema

4  Simple Rock Paper Tijeras En Erlang  ( Simple rock paper scissors in erlang ) 
Soy muy nuevo en Erlang y estoy específicamente interesado en hacer mi Erlang idiomático (en otras palabras, ¿puede decir que no conozco a Erlang de este códi...

1  Piedra Papel tijeras  ( Rock paper scissors ) 
Gracias por su tiempo, soy nuevo en la programación y pasé algunos días haciendo este rock, papel y amp; Juego de tijera. ¿Qué otras mejoras posibles podrían ...

2  Juego de tijeras de papel Python Rock  ( Python rock paper scissors game ) 
¿Alguna ocasión de personas puede leer mi código de juego y ver si se necesita reformatear o volver a escribir para un mejor código general? Realmente lo apre...

15  Implementación básica de roca-papel-tijeras  ( Basic rock paper scissors implementation ) 
Decidí intentar realizar una implementación simple de RPS en Java 8, se deben decir las siguientes notas primero: En un punto posterior, debe apoyar a los ...

12  Tijeras de papel de roca en un solo método en Java - ¿Bueno o malo?  ( Rock paper scissors in one single method in java good or bad ) 
Soy nuevo en la programación y he creado un juego de rock, papel, tijeras. Todo el programa está bajo una sola clase y el método principal. Escucho que probab...

3  Scissors de papel rock c ++ juego  ( Rock paper scissors c game ) 
Acabo de empezar a aprender C ++ y decidí hacer un juego de tijeras de papel rock. Funciona bien, pero me gustaría saber cómo puedo mejorarlo más. En particul...

8  Simple Rock Paper Tijeras en C ++  ( Simple rock paper scissors in c ) 
He hecho un juego de tijeras de papel rock. Funciona bien, pero me gustaría saber cómo puedo mejorarlo más. Node2 ...

2  Mi primera prueba de RSPEC - Tijeras de papel de roca  ( My first rspec test rock paper scissors ) 
Soy relativamente familiar para RUBY, pero estoy completamente nuevo para probar el código. He decidido usar RSPEC porque, como parece una opción popular. E...

2  Scissors de papel de rock principiante juego de Python [cerrado]  ( Rock paper scissors beginner python game ) 
cerrado. Esta pregunta es off-topic . Actualmente no está aceptando respuestas. ¿Quieres ...

7  Mecánica de índice para tijeras de papel de roca  ( Index mechanics for rock paper scissors ) 
Acabo de pasar por un ejemplo en línea muy fácil Creando un juego de tijeras de rock-papel, pero parecía que no era un gran uso del poder de la computación. ...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos