.. note::

    ¡Hola! Bienvenido a la Comunidad de Entusiastas de SunFounder Raspberry Pi & Arduino & ESP32 en Facebook. Sumérgete más en Raspberry Pi, Arduino y ESP32 con otros entusiastas.

    **¿Por qué unirse?**

    - **Soporte experto**: Resuelve problemas postventa y desafíos técnicos con la ayuda de nuestra comunidad y equipo.
    - **Aprende y comparte**: Intercambia consejos y tutoriales para mejorar tus habilidades.
    - **Previsualizaciones exclusivas**: Obtén acceso anticipado a nuevos anuncios de productos y adelantos.
    - **Descuentos especiales**: Disfruta de descuentos exclusivos en nuestros productos más nuevos.
    - **Promociones y sorteos festivos**: Participa en sorteos y promociones navideñas.

    👉 ¿Listo para explorar y crear con nosotros? Haz clic en [|link_sf_facebook|] y únete hoy mismo.

.. _4.1.17_py:

4.1.17 JUEGO – Adivina el Número
==============================================

Introducción
------------------

Adivinar Números es un divertido juego de fiesta donde tú y tus amigos toman 
turnos para ingresar un número (0~99). El rango será más pequeño con la entrada 
del número hasta que un jugador responda el acertijo correctamente. Entonces el 
jugador es derrotado y castigado. Por ejemplo, si el número de la suerte es 51, 
que los jugadores no pueden ver, y el jugador ① ingresa 50, el rango de números 
cambia a 50~99; si el jugador ② ingresa 70, el rango de números puede ser 50~70; 
si el jugador ③ ingresa 51, este jugador es el desafortunado. Aquí, usamos un 
teclado para ingresar números y una pantalla LCD para mostrar los resultados.


Componentes necesarios
------------------------------

En este proyecto, necesitamos los siguientes componentes.

.. image:: ../img/list_GAME_Guess_Number.png
    :align: center

Es definitivamente conveniente comprar un kit completo, aquí está el enlace: 

.. list-table::
    :widths: 20 20 20
    :header-rows: 1

    *   - Nombre	
        - ARTÍCULOS EN ESTE KIT
        - ENLACE
    *   - Kit Raphael
        - 337
        - |link_Raphael_kit|

También puedes comprarlos por separado en los enlaces a continuación.

.. list-table::
    :widths: 30 20
    :header-rows: 1

    *   - INTRODUCCIÓN DEL COMPONENTE
        - ENLACE DE COMPRA

    *   - :ref:`cpn_gpio_extension_board`
        - |link_gpio_board_buy|
    *   - :ref:`cpn_breadboard`
        - |link_breadboard_buy|
    *   - :ref:`cpn_wires`
        - |link_wires_buy|
    *   - :ref:`cpn_resistor`
        - |link_resistor_buy|
    *   - :ref:`cpn_keypad`
        - \-
    *   - :ref:`cpn_i2c_lcd`
        - |link_i2clcd1602_buy|


Diagrama esquemático
-------------------------

============== ========== ======== =======
Nombre T-Board Pin físico wiringPi BCM
GPIO18         Pin 12     1        18
GPIO23         Pin 16     4        23
GPIO24         Pin 18     5        24
GPIO25         Pin 22     6        25
SPIMOSI        Pin 19     12       10
GPIO22         Pin 15     3        22
GPIO27         Pin 13     2        27
GPIO17         Pin 11     0        17
SDA1           Pin 3      SDA1(8)  SDA1(2)
SCL1           Pin 5      SCL1(9)  SDA1(3)
============== ========== ======== =======

.. image:: ../img/Schematic_three_one12.png
   :align: center

Procedimientos Experimentales
---------------------------------

**Paso 1:** Construye el circuito.

.. image:: ../img/image273.png

**Paso 2**: Configura I2C (ver :ref:`i2c_config`).

**Paso 3**: Cambia de directorio.

.. raw:: html

   <run></run>

.. code-block:: 

    cd ~/raphael-kit/python/

**Paso 4**: Ejecuta.

.. raw:: html

   <run></run>

.. code-block:: 

    sudo python3 4.1.17_GAME_GuessNumber.py

Después de que el programa se ejecute, se muestra la página inicial en la LCD:

.. code-block:: 

   Welcome!
   Press A to go!

Presiona 'A' y el juego comenzará y la página del juego aparecerá en la LCD.

.. code-block:: 

   Enter number:
   0 ‹punto‹ 99

Se produce un número aleatorio 'punto' pero no se muestra en la LCD cuando el 
juego comienza, y lo que necesitas hacer es adivinarlo. El número que has escrito 
aparece al final de la primera línea hasta que se finaliza el cálculo. (Presiona 
'D' para iniciar la comparación, y si el número ingresado es mayor que 10, la 
comparación automática comenzará).

El rango de números de 'punto' se muestra en la segunda línea. Y debes escribir el 
número dentro del rango. Cuando escribes un número, el rango se reduce; si adivinaste 
el número de la suerte afortunada o desafortunadamente, aparecerá "¡Lo conseguiste!".



.. note::

    * Si obtienes el error ``FileNotFoundError: [Errno 2] No such file or directory: '/dev/i2c-1'``, necesitas consultar :ref:`i2c_config` para habilitar el I2C.
    * Si obtienes el error ``ModuleNotFoundError: No module named 'smbus2'``, por favor ejecuta ``sudo pip3 install smbus2``.
    * Si aparece el error ``OSError: [Errno 121] Remote I/O error``, significa que el módulo está mal conectado o está dañado.
    * Si el código y las conexiones están bien, pero la LCD aún no muestra contenido, puedes girar el potenciómetro en la parte posterior para aumentar el contraste.

**Código**

.. note::
    Puedes **Modificar/Restablecer/Copiar/Ejecutar/Detener** el código a continuación. Pero antes de eso, necesitas ir a la ruta del código fuente como ``raphael-kit/python``. Después de modificar el código, puedes ejecutarlo directamente para ver el efecto.

.. raw:: html

    <run></run>

.. code-block:: python

   #!/usr/bin/env python3

   import RPi.GPIO as GPIO
   import time
   import LCD1602
   import random

   ##################### AQUÍ ESTÁ LA LIBRERÍA DE TECLADO TRANSPLANTADA DESDE Arduino ############
   #class Key: Define algunas propiedades de Key
   class Keypad():

      def __init__(self, rowsPins, colsPins, keys):
         self.rowsPins = rowsPins
         self.colsPins = colsPins
         self.keys = keys
         GPIO.setwarnings(False)
         GPIO.setmode(GPIO.BCM)
         GPIO.setup(self.rowsPins, GPIO.OUT, initial=GPIO.LOW)
         GPIO.setup(self.colsPins, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)

      def read(self):
         pressed_keys = []
         for i, row in enumerate(self.rowsPins):
               GPIO.output(row, GPIO.HIGH)
               for j, col in enumerate(self.colsPins):
                  index = i * len(self.colsPins) + j
                  if (GPIO.input(col) == 1):
                     pressed_keys.append(self.keys[index])
               GPIO.output(row, GPIO.LOW)
         return pressed_keys

   ################ EL CÓDIGO DE EJEMPLO EMPIEZA AQUÍ ################  

   count = 0
   pointValue = 0
   upper=99
   lower=0

   def setup():
      global keypad, last_key_pressed,keys
      rowsPins = [18,23,24,25]
      colsPins = [10,22,27,17]
      keys = ["1","2","3","A",
               "4","5","6","B",
               "7","8","9","C",
               "*","0","#","D"]
      keypad = Keypad(rowsPins, colsPins, keys)
      last_key_pressed = []
      LCD1602.init(0x27, 1)    # init(slave address, background light)
      LCD1602.clear()
      LCD1602.write(0, 0, 'Welcome!')
      LCD1602.write(0, 1, 'Press A to Start!')

   def init_new_value():
      global pointValue,upper,count,lower
      pointValue = random.randint(0,99)
      upper = 99
      lower = 0
      count = 0
      print('point is %d' %(pointValue))
      
   def detect_point():
      global count,upper,lower
      if count > pointValue:
         if count < upper:
               upper = count 
      elif count < pointValue:
         if count > lower:
               lower = count
      elif count == pointValue:
         count = 0
         return 1
      count = 0
      return 0

   def lcd_show_input(result):
      LCD1602.clear()
      if result == 1:
         LCD1602.write(0,1,'You have got it!')
         time.sleep(5)
         init_new_value()
         lcd_show_input(0)
         return
      LCD1602.write(0,0,'Enter number:')
      LCD1602.write(13,0,str(count))
      LCD1602.write(0,1,str(lower))
      LCD1602.write(3,1,' < Point < ')
      LCD1602.write(13,1,str(upper))

   def loop():
      global keypad, last_key_pressed,count
      while(True):
         result = 0
         pressed_keys = keypad.read()
         if len(pressed_keys) != 0 and last_key_pressed != pressed_keys:
               if pressed_keys == ["A"]:
                  init_new_value()
                  lcd_show_input(0)
               elif pressed_keys == ["D"]:
                  result = detect_point()
                  lcd_show_input(result)
               elif pressed_keys[0] in keys:
                  if pressed_keys[0] in list(["A","B","C","D","#","*"]):
                     continue
                  count = count * 10
                  count += int(pressed_keys[0])
                  if count >= 10:
                     result = detect_point()
                  lcd_show_input(result)
               print(pressed_keys)
         last_key_pressed = pressed_keys
         time.sleep(0.1)

   # Define una función destroy para limpiar todo después de que el script termine
   def destroy():
      # Liberar recursos
      GPIO.cleanup()
      LCD1602.clear() 

   if __name__ == '__main__':     # Program start from here
      try:
         setup()
         while True:
               loop()
      except KeyboardInterrupt:   # Cuando se presiona 'Ctrl+C', se ejecutará la función destroy().
         destroy()

**Explicación del Código**

En la parte inicial del código se encuentran las funciones funcionales 
del **teclado** y del **I2C LCD1602**. Puedes aprender más detalles sobre 
ellas en :ref:`1.1.7_py` y :ref:`2.1.8_py`.

Aquí, lo que necesitamos saber es lo siguiente:

.. code-block:: python

    def init_new_value():
        global pointValue,upper,count,lower
        pointValue = random.randint(0,99)
        upper = 99
        lower = 0
        count = 0
        print('point is %d' %(pointValue))

La función produce el número aleatorio ‘\ **point**\ ’ y restablece el rango de pistas del punto.

.. code-block:: python

    def detect_point():
        global count,upper,lower
        if count > pointValue:
            if count < upper:
                upper = count 
        elif count < pointValue:
            if count > lower:
                lower = count
        elif count == pointValue:
            count = 0
            return 1
        count = 0
        return 0

``detect_point()`` compara el número ingresado (**count**) con el número 
producido “\ **point**\ ”. Si el resultado de la comparación indica que no 
son iguales, **count** asignará valores a **upper** y **lower** y devolverá 
‘\ **0**\ ’; de lo contrario, si el resultado indica que son iguales, se devolverá ‘\ **1**\ ’.

.. code-block:: python

    def lcd_show_input(result):
        LCD1602.clear()
        if result == 1:
            LCD1602.write(0,1,'You have got it!')
            time.sleep(5)
            init_new_value()
            lcd_show_input(0)
            return
        LCD1602.write(0,0,'Enter number:')
        LCD1602.write(13,0,str(count))
        LCD1602.write(0,1,str(lower))
        LCD1602.write(3,1,' < Point < ')
        LCD1602.write(13,1,str(upper))

Esta función se utiliza para mostrar la página del juego.

``str(count)``: Debido a que ``write()`` solo puede soportar el tipo de 
dato — **cadena** , se necesita ``str()`` para convertir el **número** en **cadena**.

.. code-block:: python

    def loop():
        global keypad, last_key_pressed,count
        while(True):
            result = 0
            pressed_keys = keypad.read()
            if len(pressed_keys) != 0 and last_key_pressed != pressed_keys:
                if pressed_keys == ["A"]:
                    init_new_value()
                    lcd_show_input(0)
                elif pressed_keys == ["D"]:
                    result = detect_point()
                    lcd_show_input(result)
                elif pressed_keys[0] in keys:
                    if pressed_keys[0] in list(["A","B","C","D","#","*"]):
                        continue
                    count = count * 10
                    count += int(pressed_keys[0])
                    if count >= 10:
                        result = detect_point()
                    lcd_show_input(result)
                print(pressed_keys)
            last_key_pressed = pressed_keys
            time.sleep(0.1)

``main()`` contiene todo el proceso del programa, como se muestra a continuación:

1) Inicializa **I2C LCD1602** y **Keypad**.

2) Juzga si se ha presionado el botón y obtiene la lectura del botón.

3) Si se presiona el botón ‘\ **A**\ ’, aparecerá un número aleatorio **0-99** y 
   luego comenzará el juego.

4) Si se detecta que el botón ‘\ **D**\ ’ ha sido presionado, el programa entrará 
   en el juicio del resultado.

5) Si se presiona el botón **0-9**, el valor de **count** cambiará; si **count** es 
   mayor que **10**, entonces comenzará el juicio.

6) Los cambios del juego y sus valores se muestran en **LCD1602**.

Imagen del Fenómeno
------------------------

.. image:: ../img/image274.jpeg
   :align: center
