Русский Português
preview
Cómo desarrollar un agente de aprendizaje por refuerzo en MQL5 con Integración RestAPI (Parte 3): Creación de jugadas automáticas y scripts de prueba en MQL5

Cómo desarrollar un agente de aprendizaje por refuerzo en MQL5 con Integración RestAPI (Parte 3): Creación de jugadas automáticas y scripts de prueba en MQL5

MetaTrader 5Ejemplos | 9 abril 2024, 09:47
76 0
Jonathan Pereira
Jonathan Pereira

Introducción

En este tercer artículo de la serie, profundizamos más en el universo de las APIs REST y su aplicación práctica en sistemas. Tras una exploración detallada del desarrollo de funciones de MQL5 y su integración con un juego del tres en raya en Python a través de FastAPI, este artículo se propone avanzar significativamente. Ahora enfocamos nuestra atención en implementar jugadas automáticas en el tres en raya para incrementar su nivel de desafío e interactividad. También dedicaremos atención especial al desarrollo de scripts de prueba en MQL5 para garantizar la robustez y eficacia de nuestro sistema integrado.

Reconociendo la complejidad de este proyecto, percibo la necesidad de proporcionar instrucciones claras y accesibles para la instalación y ejecución. Lamento no haber incluido esta información en las partes anteriores y agradezco la comprensión de todos. Con esto en mente, presento una guía paso a paso, incluyendo orientaciones para usuarios de Windows que pueden enfrentar restricciones en la ejecución de scripts.

Guía de instalación y ejecución

Requisitos previos:

  • Python 3.6 o superior.
  • MetaTrader 5 instalado en su ordenador.
  • En Windows, compruebe que la ejecución de scripts está activada. Si es necesario, ejecute Set-ExecutionPolicy RemoteSigned en PowerShell como administrador para permitir la ejecución de scripts.

Pasos de instalación y ejecución:

  1. Descargue y extraiga el proyecto: Después de descargar el proyecto del artículo, extraiga los archivos en una carpeta de su elección.
  2. Cópielo en la carpeta Expertos: Mueva la carpeta extraída a la carpeta 'Expertos' en la instalación de su terminal MetaTrader.
  3. Abra la carpeta en el terminal:
    • En Windows, puede hacerlo buscando "Prompt de Comando" o "PowerShell" en el menú de inicio, abriendo el programa y utilizando el comando cd ruta_de_la_carpeta para navegar hasta la carpeta del proyecto.
    • En MacOS o Linux, abra el 'Terminal' y utilice el comando cd ruta_carpeta.
  4. Cree un entorno virtual: Con la carpeta del proyecto abierta en el terminal, ejecute python -m venv env para crear un entorno virtual.
  5. Active el entorno virtual: En Windows, ejecute env\Scripts\activate. En MacOS o Linux, ejecute source env/bin/activate.
  6. Instale las dependencias: Con el entorno virtual activado, ejecute pip install -r requirements.txt.

Ejecución del proyecto:

  • Para ejecutar la API del juego tres en raya: Con el terminal aún abierto en la carpeta del proyecto, ejecuta python AppTicTacToe.py.
  • Para ejecutar en MetaTrader: Abre el MetaEditor, ve a Herramientas > Opciones > Compiladores. Pegue la ruta de la carpeta "scripts" del entorno virtual en "Ubicación de compilación externa", haga clic en compilar y o arrastre el script a un gráfico.
  • Acceso a la interfaz de usuario Swagger: Vaya a localhost:8000/docs en su navegador para interactuar con la API a través de la Swagger UI.

Objetivo: La meta principal de este artículo es doble: primeramente, mejorar el juego del tres en raya de Python para que ejecute jugadas de forma autónoma, utilizando algoritmos de decisión inteligentes. En segundo lugar, desarrollar e implementar pruebas unitarias en MQL5 que validen y aseguren la confiabilidad de las interacciones entre el código MQL5 y la API REST.

Este artículo se divide en 3 partes:

  1. Desarrollo de movimientos automáticos en el juego del tres en raya: Detalles del proceso de modificación del juego del tres en raya para incluir la lógica de las jugadas automáticas, que abarcan las técnicas de programación utilizadas y los retos encontrados.
  2. Creación de scripts de prueba en MQL5: Exploración del proceso de desarrollo de pruebas unitarias en MQL5, con especial atención a cómo pueden validar la interacción entre MQL5 y la API REST.
  3. Pruebas prácticas y de integración: Demostración práctica de la integración de las mejoras aplicadas, incluida la realización de pruebas y la evaluación de los resultados.
La idea de este artículo se centra en que, al implementar jugadas automáticas en el juego del tres en raya de Python, establecemos una base sólida para pruebas más abarcadoras y eficientes. Con el juego respondiendo de manera autónoma, podemos realizar pruebas rigurosas usando scripts de MQL5 que simulan interacciones reales con la API REST. Esta aproximación no solo asegura que el juego funcione conforme a lo esperado en diferentes escenarios, sino que también valida la robustez de la comunicación entre el código MQL5 y la interfaz de la API.

De esta manera, el desarrollo de un agente en MQL5 que interactúe con el juego del tres en raya se convierte en el próximo paso lógico. Este agente podría simular un usuario real al efectuar jugadas y responder a las acciones del juego, proporcionando así un ambiente de prueba más cercano a la realidad. Esta estrategia nos permite verificar no solo la funcionalidad del juego y de la API, sino también explorar y mejorar los algoritmos de decisión en las jugadas automáticas, garantizando una experiencia de juego más desafiante e atractiva.

La combinación de jugadas automáticas en el juego del tres en raya y pruebas unitarias en MQL5 crea un ciclo de desarrollo robusto, donde cada mejora en el juego es validada y perfeccionada a través de pruebas rigurosas. Este proceso continuo de desarrollo y prueba asegura la creación de un sistema integrado confiable y eficaz, capaz de proporcionar no solo una experiencia de juego mejorada, sino también perspectivas valiosas para futuras integraciones y desarrollos en sistemas que necesiten usar integración.


 


Desarrollo de jugadas automáticas

En esta sesión, el foco inicial será comprender la estructura y lógica del código existente del juego. Dado que el juego no posee una interfaz gráfica y que la implementación de las jugadas automáticas no utilizará algoritmos de decisión complejos, nuestro objetivo será simplificar y hacer eficiente este proceso.

El primer paso consiste en analizar cómo el juego procesa las jugadas manuales, observando la lógica que gobierna los turnos y cómo determina los estados de victoria, derrota o empate. Este entendimiento será esencial para integrar la funcionalidad de jugadas automáticas sin perturbar la mecánica de juego existente.

A continuación, la implementación de las jugadas automáticas se realizará de manera más ingenua. En lugar de un algoritmo sofisticado, podemos optar por un enfoque más directo, como seleccionar aleatoriamente una posición libre en el tablero para la jugada automática. Aunque simple, este enfoque debe ser suficiente para simular un oponente y añadir dinamismo al juego.

Al mirar con atención el código del tres en raya, vamos a entender bien cómo funciona. Así, podemos planificar cómo añadir jugadas automáticas sin complicar el juego. Nuestro objetivo es mantener todo fácil de usar, siguiendo el estilo simple que el juego ya posee.

Inicialización del juego:

def __init__(self):
    self.board = [[' ' for _ in range(3)] for _ in range(3)]
    self.player_turn = True

En esta sección, el juego se inicia con un tablero vacío (self.board), y la variable self.player_turn indica el turno del jugador. Este diseño minimalista proporciona un punto de partida ideal para integrar la lógica de jugadas automáticas, sin complicar innecesariamente el código existente.

Exhibición del tablero:

def print_board(self):
    for row in self.board:
        print("|".join(row))
        print("-" * 5)
El método print_board gestiona la visualización del tablero. La forma en que se presenta el estado del juego es crucial para mantener la usabilidad y la comprensión del juego, especialmente tras la implantación de las jugadas automáticas.


Verificación de ganador:

 def check_winner(self):
     for i in range(3):
         if self.board[i][0] == self.board[i][1] == self.board[i][2] != ' ':
             return self.board[i][0]
         if self.board[0][i] == self.board[1][i] == self.board[2][i] != ' ':
             return self.board[0][i]
     if self.board[0][0] == self.board[1][1] == self.board[2][2] != ' ':
         return self.board[0][0]
     if self.board[0][2] == self.board[1][1] == self.board[2][0] != ' ':
         return self.board[0][2]
     return None

Este método es fundamental para determinar el ganador después de cada jugada. Su lógica será esencial para verificar el término del juego, tanto para jugadas manuales como automáticas.

Realización de jugadas:

 def make_move(self, row, col):
     if self.board[row][col] == ' ':
         if self.player_turn:
             self.board[row][col] = 'X'
         else:
             self.board[row][col] = 'O'
         self.player_turn = not self.player_turn
     else:
         print("Jogada inválida. Tente novamente.")

El método make_move es responsable de las jugadas de los jugadores. La implementación de las jugadas automáticas requerirá modificaciones en este método para alternar entre jugadas manuales y automáticas de forma eficiente.


Implementación de jugadas automáticas:

La implementación de las jugadas automáticas en el juego del tres en raya trae un nuevo desafío y diversión. Vamos a poner esto en práctica con un nuevo método llamado machine_move. Primero, intentará encontrar una manera de ganar o de impedir que el otro jugador gane. Si no hay manera, elegirá un espacio vacío en el tablero de forma aleatoria.

def machine_move(self):
    for i in range(3):
        for j in range(3):
            if self.board[i][j] == ' ':
                # Primeiro, tenta encontrar uma jogada vencedora para 'O'
                self.board[i][j] = 'O'
                if self.check_winner() == 'O':
                    return (i, j)  # Retorna a posição para a vitória
                self.board[i][j] = ' '

                # Em seguida, tenta bloquear uma jogada vencedora para 'X'
                self.board[i][j] = 'X'
                if self.check_winner() == 'X':
                    self.board[i][j] = 'O'  # Bloqueia a vitória do jogador
                    return (i, j)
                self.board[i][j] = ' '

    # Se não houver jogadas vencedoras, escolhe aleatoriamente uma posição livre
    available_moves = self.available_moves()
    if available_moves:
        move = random.choice(available_moves)
        self.board[move["row"]][move["col"]] = 'O'
        return (move["row"], move["col"])



También tendremos el método available_moves. Este método es importante porque mirará el tablero y mostrará todos los espacios que aún están libres. Así, nos aseguramos de que el ordenador solo haga jugadas en lugares vacíos.

def available_moves(self):
    moves = []
    for i in range(3):
        for j in range(3):
            if self.board[i][j] == ' ':
                moves.append({"row": i, "col": j})
    return moves


Con estos cambios, el juego del tres en raya se vuelve más interesante. Continúa siendo simple, pero ahora incluye un toque adicional de sorpresa y estrategia con las jugadas automáticas. Esto hace que el juego sea aún más divertido de jugar.

La integración de jugadas automáticas en el juego del tres en raya de Python añade complejidad e interactividad, preparando el terreno para futuras implementaciones avanzadas, como la introducción de un agente MQL5. La idea es que, al finalizar esta fase de desarrollo, el juego no solo ofrezca un desafío mayor para el jugador humano, sino que también esté listo para interacciones más sofisticadas con agentes externos.

La lógica de jugadas automáticas, aunque inicialmente simple, establece una base sólida para el juego. Con la capacidad de realizar movimientos autónomos y responder de forma dinámica al contexto del juego, el sistema se vuelve apto para simular un oponente realista. Esto resulta esencial para probar la eficiencia y robustez del juego en variados escenarios, especialmente en preparación para la futura implementación de un agente MQL5.

Al planificar la introducción de este agente, el tres en raya con jugadas automáticas ya estará equipado para simular un ambiente de juego real. El agente podrá interactuar con el juego, efectuando jugadas y reaccionando a las acciones automáticas, creando un escenario más cercano a lo que sería un juego entre dos jugadores humanos. Esta interacción nos permitirá evaluar no solo la funcionalidad del juego y de la API, sino también la eficacia de los algoritmos de decisión empleados, abriendo camino para mejoras y ajustes.

Además, la presencia de un agente MQL5 proporcionará un entorno de prueba más avanzado y realista. Esto nos permitirá simular diversas situaciones de juego, verificar la respuesta del sistema bajo diferentes condiciones y garantizar la estabilidad y fiabilidad del juego.

A continuación, el código completo del juego:

class TicTacToe:

    def __init__(self):
        self.board = [[' ' for _ in range(3)] for _ in range(3)]
        self.player_turn = True

    def print_board(self):
        for row in self.board:
            print("|".join(row))
            print("-" * 5)
        print(f"Player {self.player_turn}'s turn")

    def check_winner(self):
        for i in range(3):
            if self.board[i][0] == self.board[i][1] == self.board[i][2] != ' ':
                return self.board[i][0]
            if self.board[0][i] == self.board[1][i] == self.board[2][i] != ' ':
                return self.board[0][i]
        if self.board[0][0] == self.board[1][1] == self.board[2][2] != ' ':
            return self.board[0][0]
        if self.board[0][2] == self.board[1][1] == self.board[2][0] != ' ':
            return self.board[0][2]
        return None

    def machine_move(self):
        for i in range(3):
            for j in range(3):
                if self.board[i][j] == ' ':
                    self.board[i][j] = 'O'
                    if self.check_winner() == 'O':
                        return (i, j)
                    self.board[i][j] = ' '
                    self.board[i][j] = 'X'
                    if self.check_winner() == 'X':
                        self.board[i][j] = 'O'
                        return (i, j)
                    self.board[i][j] = ' '

        for i in range(3):
            for j in range(3):
                if self.board[i][j] == ' ':
                    self.board[i][j] = 'O'
                    return (i, j)


    def available_moves(self):
        moves = []
        for i in range(3):
            for j in range(3):
                if self.board[i][j] == ' ':
                    moves.append({"row": i, "col": j})
        return moves

Ahora que hemos implementado las jugadas automáticas en el juego del tres en raya de Python, el desafío es integrar esta funcionalidad con nuestra API FastAPI. Esta etapa resulta fundamental para asegurar una interacción eficaz y sin fallos entre el juego y el backend, preparando el terreno para futuras integraciones, como la introducción de un agente MQL5.

Para que la API soporte las jugadas automáticas, necesitamos hacer algunas adaptaciones importantes en el código. Vamos a detallar los pasos necesarios para esta integración, manteniendo el enfoque en la simplicidad y eficiencia.


Pasos para adaptar la API FastAPI a las jugadas automáticas
  1. Gestión de turnos mejorada: La API necesita identificar correctamente de quién es el turno de jugar - del jugador o de la máquina. Tras cada jugada del jugador, la API debe verificar si es el turno de la máquina y, en caso afirmativo, activar la lógica de jugadas automáticas.

  2. Integración con la lógica de machine_move: La función machine_move en el código del juego del tres en raya es esencial para las jugadas automáticas. Por lo tanto, tras cada jugada del jugador, la API debe invocar este método para determinar la respuesta de la máquina.

  3. Actualización consistente del estado del juego: Tras cada jugada, sea del jugador o de la máquina, la API debe actualizar y reflejar con precisión el estado del tablero. Esto garantiza que el jugador siempre reciba información actualizada y correcta sobre el juego.

  4. Manejo de resultados de juego: La API debe ser capaz de identificar el fin del juego, sea por una victoria o empate, y comunicar esto adecuadamente. Es vital que la API ofrezca información clara sobre el ganador del juego o declare un empate cuando ninguna jugada ganadora sea posible.

  5. Respuestas claras e informativas: La API debe responder con todos los detalles necesarios, como el estado actual del tablero, las jugadas realizadas por la máquina, y el resultado del juego, si lo hay. Esto asegura una experiencia de usuario fluida e informativa.


Ejemplo de implementación en la API

Vamos a modificar la función play en la API para incorporar estos cambios:

@app.post("/play/{game_id}/")
def play(game_id: int, move: PlayerMove):
    game = games.get(game_id)
    if not game:
        raise HTTPException(status_code=404, detail="Game not found")

    board = game.board
    if board[move.row][move.col] == ' ':
        board[move.row][move.col] = 'X'
    else:
        raise HTTPException(status_code=400, detail="Invalid move")

    player_move = {"row": move.row, "col": move.col, "symbol": 'X'}

    winner = game.check_winner()
    machine_move_result = None

    if not winner:
        game.player_turn = not game.player_turn
        if not game.player_turn:
            move_result = game.machine_move()
            if move_result:
                row, col = move_result
                machine_move_result = {"row": row, "col": col, "symbol": 'O'}
                winner = game.check_winner()
            game.player_turn = not game.player_turn

    return {
        "board": board,
        "player_move": player_move,
        "machine_move": machine_move_result,
        "winner": winner,
        "available_moves": game.available_moves()
    }


La implementación de la funcionalidad de jugadas automáticas en el juego del tres en raya de Python, junto con la adaptación de la API FastAPI para soportar esta funcionalidad, es importante para crear un sistema de juego completo e interactivo. Ahora que la API está configurada para gestionar jugadas automáticas, los jugadores pueden interactuar con el juego de forma más dinámica. Cuando un jugador realiza una jugada, la API verifica si es el turno de la máquina jugar y, si es el caso, activa la lógica de jugadas automáticas. Esto crea un flujo de juego continuo e interactivo, donde el jugador humano compite contra una IA para ganar.

Además, la API proporciona información detallada sobre el estado actual del juego, incluyendo el tablero actualizado, las jugadas realizadas por la máquina y el resultado del juego, si hay un ganador o un empate. Esto hace que la experiencia del jugador sea más envolvente e informativa.


Creación de scripts de prueba en MQL5

En el artículo anterior, aprendimos cómo crear y gestionar solicitudes HTTP en MQL5. Ahora aplicamos ese conocimiento para desarrollar pruebas unitarias robustas. Diseñamos cada función de prueba para simular escenarios reales de interacción entre el código MQL5 y la API REST, asegurando que todos los aspectos de la comunicación se prueben y validen.

Las pruebas cubren desde la inicialización del juego hasta la ejecución de jugadas válidas e inválidas, además de verificar condiciones de victoria. Este enfoque garantiza la confiabilidad y estabilidad del sistema en su conjunto, proporcionando una base sólida para futuras expansiones e integraciones.

Al final de este tema, el lector tendrá una comprensión clara de cómo los pruebas unitarias en MQL5 están estructuradas e implementadas, y cómo son esenciales para el desarrollo del sistema.


Estructura de los tests


El código de prueba en MQL5 está organizado en tres archivos principales:

  1. Tests.mqh: Contiene las funciones de prueba.
  2. Request.mqh: Gestiona las solicitudes HTTP.
  3. Tests.mq5: Script principal que ejecuta las pruebas.


Tests.mqh

  • Assert(): Esta función se utiliza para comprobar si una condición específica es verdadera. Es esencial validar los resultados esperados de las pruebas.

    Código y explicación:

    void Assert(bool condition, const string message) {
      if(!condition) {
        Print("Test error: ", message);
      }
    }

    Si la condición pasada a Assert es falsa, imprime un mensaje de error. Esto ayuda a identificar rápidamente fallos en los tests.

    • TestGameInitialisation(): Prueba la inicialización de un nuevo juego, comprobando que la API responde correctamente.
    Código y explicación:
    void TestGameInitialization() {
      string url = "http://localhost:8000/start-game/";
      string response;
      int result = Request("GET", response, url);
      Assert(result == 200, "Game initialization failed");
      Assert(StringLen(response) > 0, "game_id missing in game initialization response");
    }Esta função faz uma requisição GET para iniciar um jogo e verifica se o código de resposta é 200 (OK) e se um 
    game_id  é retornado na resposta.

      Esta función hace una petición GET para iniciar un juego y verifica si el código de respuesta es 200 (OK) y si se retorna un game_id en la respuesta.


      • TestPlayerMove(): Verifica la funcionalidad de realizar una jugada válida por el jugador.

        Código y explicación:

        // Test function to check player's move
        void TestPlayerMove()
          {
           string url = "http://localhost:8000/start-game/";
           string response;
           int result = -1;
           int game_id = -1;
        
           Request("GET", response, url);
        
           js.Deserialize(response);
           game_id = js["game_id"].ToStr();
        
        // Make a valid player move
           url = StringFormat("http://localhost:8000/play/%d/", game_id);
           string payload = "{\"row\": 0, \"col\": 0}";
           result = Request("POST", response, url, payload);
        
        // Check if the HTTP response code is 200 (OK)
           Assert(result == 200, "Player move failed");
        
        // Check if the response contains information about the player's move
        // (you can adjust this based on the actual response structure)
           Assert(StringFind(response, "player_move") != -1, "Player move response incomplete");
          }

        Después de iniciar un juego, esta función ejecuta una jugada válida y verifica si la API procesa la jugada correctamente, devolviendo un código 200 y la información de la jugada en el cuerpo de la respuesta.


        • TestInvalidPlayerMove(): Comprueba la respuesta de la API a un movimiento no válido.

          Código y explicación:

          // Test function to check an invalid player move
          void TestInvalidPlayerMove()
            {
             string url = "http://localhost:8000/start-game/";
             string response;
             int result = -1;
             int game_id = -1;
          
             Request("GET", response, url);
          
             js.Deserialize(response);
             game_id = js["game_id"].ToStr();
          
          // Make an invalid player move (e.g., on an occupied position)
             url = StringFormat("http://localhost:8000/play/%d/", game_id);
             string payload = "{\"row\": 0, \"col\": 0}";
             Request("POST", response, url, payload);
          
          //repeat
             payload = "{\"row\": 0, \"col\": 0}";
             result = Request("POST", response, url, payload);
          
          // Check if the HTTP response code is 400 (Bad Request)
             Assert(result == 400, "Invalid player move not handled correctly");
            }
            Esta función intenta realizar un movimiento no válido (por ejemplo, jugar en una posición ya ocupada) y comprueba si la API devuelve un código de error 400 (Bad Request).

            • TestPlayerWin(): Simula una secuencia de movimientos que da como resultado la victoria de un jugador.

              Código y explicación:
              // Test function to check player's victory
              void TestPlayerWin()
                {
                 string url = "http://localhost:8000/start-game/";
                 string response;
                 int result = -1;
                 int game_id = -1;
              
                 Request("GET", response, url);
              
                 js.Deserialize(response);
                 game_id = js["game_id"].ToStr();
              
              // Make moves for player X to win
                 url = StringFormat("http://localhost:8000/play/%d/", game_id);
              
                 string payload = "{\"row\": 0, \"col\": 0}";
                 result = Request("POST", response, url, payload);
                 Assert(result == 200, "Player X move 1 failed");
              
                 payload = "{\"row\": 0, \"col\": 2}";
                 result = Request("POST", response, url, payload);
                 Assert(result == 200, "Player X move 2 failed");
              
                 payload = "{\"row\": 2, \"col\": 2}";
                 result = Request("POST", response, url, payload);
                 Assert(result == 200, "Player X move 3 failed");
              
                 payload = "{\"row\": 1, \"col\": 2}";
                 result = Request("POST", response, url, payload);
                 Assert(result == 200, "Player X move 4 failed");
              
              // Check if the response contains information about the winner
                 js.Deserialize(response); // Deserialize the updated game state
              
              // Check if the HTTP response code is 200 (OK) after move 5
                 Assert(result == 200 && js["winner"].ToStr() == "X", "Player X victory failed");
              
                }

                Esta función ejecuta una serie de movimientos que llevarían a la victoria de un jugador y comprueba que la API reconoce correctamente la condición de victoria.

                • RunTests(): Función agregadora que ejecuta todas las pruebas definidas.
                Código y explicación:
                  // Function to run all tests
                  void RunTests()
                    {
                     TestGameInitialization();
                     TestPlayerMove();
                     TestInvalidPlayerMove();
                     TestPlayerWin();
                    }

                  Esta función simplemente llama a todas las funciones de prueba definidas previamente, lo que facilita la ejecución de todas las pruebas a la vez.

                  Anteriormente, creamos la biblioteca Requests para facilitar la comunicación entre el código MQL5 y las API REST. En este artículo, seguiremos utilizando esta biblioteca, pero con algunas mejoras significativas implementadas para aumentar su funcionalidad y eficiencia.


                  Cambios introducidos en la biblioteca Requests

                  La nueva implementación de la biblioteca Requests trae mejoras enfocadas principalmente en la flexibilidad y el diagnóstico de errores. Vamos a explorar los principales cambios:

                  1. Inclusión del parámetro debug:

                    • Implementación antigua: En las versiones anteriores, la biblioteca no ofrecía una opción directa para la depuración. Cualquier salida de depuración necesitaba ser implementada manualmente.
                    • Implementación nueva: Ahora, las funciones SendGetRequest y SendPostRequest incluyen un parámetro debug. Cuando se activa, este parámetro permite la impresión de salidas de depuración, facilitando el rastreo y la resolución de problemas.

                  2. Tratamiento de errores mejorado:

                    • Implementación antigua: Implementación Antigua: El tratamiento de errores era más básico, limitándose a devolver el código de error o imprimir directamente en la consola.
                    • Implementación nueva: La nueva versión ofrece un tratamiento de errores más sofisticado, permitiendo un mejor diagnóstico de problemas de comunicación HTTP.


                  ¿Por qué se han hecho estos cambios? Los cambios en la biblioteca Requests se debieron a la necesidad de:

                  • Mejora de la depuración: La posibilidad de activar o desactivar la depuración facilita el proceso de desarrollo y mantenimiento, ya que permite a los desarrolladores ver rápidamente las respuestas de la API e identificar problemas.
                  • Mejorar la gestión de errores: Una gestión de errores más sólida es crucial para que los sistemas sean fiables, sobre todo cuando se trata de comunicaciones de red en las que pueden surgir diversos problemas.


                  Impacto del cambio

                  Estas mejoras hacen que la biblioteca Requests sea más versátil y robusta. Con la opción de depuración, los desarrolladores pueden obtener información más rápidas durante el desarrollo y testeo de sus aplicaciones. Además, un manejo de errores más eficiente nos ayuda a identificar y resolver problemas de comunicación con la API, asegurando una integración más suave y fiable.


                  Cambio 1: Inclusión del parámetro debug

                  Antigua implementación de SendGetRequest y SendPostRequest:

                  // Exemplo da antiga implementação de SendGetRequest
                  int SendGetRequest(const string url, const string query_param, string &out, string headers = "", const int timeout = 5000) {
                     // ... código ...
                     out = CharArrayToString(result, start_index, WHOLE_ARRAY, CP_UTF8);
                     return (0);
                  }
                  

                  Nueva implementación con debug:

                  // Exemplo da nova implementação de SendGetRequest
                  int SendGetRequest(const string url, const string query_param, string &out, string headers = "", const int timeout = 5000, bool debug=false) {
                     // ... código ...
                     out = CharArrayToString(result, start_index, WHOLE_ARRAY, CP_UTF8);
                     if(debug) {
                         Print(out);
                     }
                     return res;
                  }
                  

                  La nueva implementación incluye un parámetro debug. Si es true, la función imprime la salida, facilitando así la depuración.


                  Cambio 2: Tratamiento de errores mejorado

                  Antigua implementación de SendGetRequest y SendPostRequest:

                  // Exemplo da antiga implementação de SendGetRequest
                  int SendGetRequest(const string url, const string query_param, string &out, string headers = "", const int timeout = 5000) {
                     // ... código ...
                     if(res == -1) {
                         return (_LastError);
                     } else {
                         // Tratamento de erros HTTP
                         out = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);
                         Print(out);
                         return (ERR_HTTP_ERROR_FIRST + res);
                     }
                  }
                  

                  Nueva implementación con manejo de errores mejorado:

                  // Exemplo da nova implementação de SendGetRequest
                  int SendGetRequest(const string url, const string query_param, string &out, string headers = "", const int timeout = 5000, bool debug=false) {
                     // ... código ...
                     if(res == -1) {
                         return (_LastError);
                     } else {
                         // Tratamento de erros HTTP
                         out = CharArrayToString(result, 0, WHOLE_ARRAY, CP_UTF8);
                         if(debug) {
                             Print(out);
                         }
                         return res;
                     }
                  }
                  

                  En la nueva implementación, el manejo de errores HTTP es más refinado, con la opción de imprimir la salida solo si el modo de depuración está activado.


                  Integración y tests prácticos

                  En esta fase crucial de nuestro proyecto, demostraremos la integración de las mejoras implementadas en la biblioteca Requests y las jugadas automáticas en el juego del tres en raya de Python. Además, realizaremos tests prácticos para evaluar la eficacia y robustez del sistema en su conjunto. Esta etapa es fundamental para asegurar que todas las partes de nuestro sistema funcionen de manera armoniosa y fiable.

                  Pasos de la integración

                  Antes de proceder con los tests, es importante entender cómo se realizó la integración de las nuevas funcionalidades. A continuación, describimos los principales pasos que tomamos para asegurar una integración fluida:

                  1. Actualización de la Biblioteca Requests: Primero, actualizamos nuestra biblioteca Requests para incluir las mejoras que implementamos. Esto permitió una comunicación más eficiente y la capacidad de depurar problemas de manera más efectiva.

                  2. Adaptación del Juego del Tres en Raya: Integrar las jugadas automáticas en el juego del tres en raya de Python fue un paso importante Modificamos el código del juego para permitir que reconozca el turno de la máquina y active la lógica de jugadas automáticas.

                  3. Implementación de la Integración en la API: A continuación, adaptamos la API FastAPI para soportar las jugadas automáticas. Esto involucró mejoras en la gestión de turnos, integración con la función machine_move, actualización consistente del estado del juego y manejo adecuado de resultados de juego.

                  Realización de los tests

                  Ahora que la integración está completa, es hora de realizar tests prácticos para asegurar que todo funcione conforme a lo esperado. Creamos una serie de tests que abarcan diferentes aspectos de nuestra implementación:

                  1. Test de creación de nuevo juego (Swagger y Script de Tests): Para garantizar que la creación de un nuevo juego funcione de manera consistente, realizaremos este test tanto a través de la interfaz Swagger como de nuestro script de tests automatizados. Esto asegura que la funcionalidad esté accesible de ambas maneras.

                  2. Test de jugada del jugador (Swagger y Script de Tests): La funcionalidad de realizar una jugada válida por el jugador será probada tanto a través de Swagger como del script de tests. Esto garantiza que la interacción del jugador sea probada de manera exhaustiva.

                  3. Test de jugada inválida del jugador: Para asegurar que la API maneje adecuadamente las jugadas inválidas, realizaremos un test en el que intentaremos hacer una jugada en una posición ya ocupada, verificando si la API devuelve un código de error apropiado.

                  4. Test de victoria del jugador: Simularemos una secuencia de jugadas que resultará en la victoria de un jugador y garantizaremos que la API reconozca correctamente la condición de victoria.

                  Resultados y evaluación

                  Al realizar estos tests, esperamos obtener resultados sólidos que validen la integridad de nuestro sistema integrado. Evaluaremos si las jugadas automáticas funcionan conforme a lo esperado, si los errores son manejados de manera adecuada y si la API proporciona respuestas claras e informativas.

                  Estos tests prácticos son esenciales para asegurar que nuestro sistema esté listo para interacciones reales y futuras expansiones. Con una integración exitosa y tests robustos, estamos más cerca de crear un sistema de juego confiable y eficaz.




                  En las imágenes arriba, podemos observar el proceso de test de la funcionalidad de creación de un nuevo juego, que desempeña un papel esencial en nuestra aplicación. Realizamos el test de manera exhaustiva y consistente para abordar la accesibilidad de la función por dos vías: mediante la interfaz Swagger y nuestro script de tests automatizados.

                  El Swagger, una herramienta de documentación y test de API, permite que los desarrolladores interactúen con la API de manera visual y efectiva. En la imagen, mostramos el inicio y la prueba de la creación de un nuevo juego mediante la interfaz Swagger para garantizar que la funcionalidad sea accesible y funcione como se espera.

                  Además, el proceso de test también involucra el uso de un script de tests automatizados, que realiza comprobaciones rigurosas para garantizar la consistencia y fiabilidad de la funcionalidad. Esto demuestra nuestro compromiso en mantener la calidad del sistema, independientemente del enfoque de test utilizado.


                  Conclusión

                  Este artículo, que marca la continuación de la serie sobre APIs REST, se centró en la implementación y test de jugadas automáticas en el juego del tres en raya de Python, integradas con el desarrollo de funciones MQL5. El objetivo principal fue doble: mejorar el juego del tres en raya con jugadas autónomas y desarrollar tests unitarios en MQL5 para validar la interacción con la API REST. La integración de estas funcionalidades no solo mejora el juego en sí, sino que también establece una base para tests más abarcadores y eficientes.

                  En el desarrollo de jugadas automáticas, fue crucial comprender la lógica del juego existente para implementar una nueva funcionalidad de manera eficiente y armoniosa. La estrategia elegida involucró un enfoque simple, pero efectivo, para hacer el juego más desafiante y dinámico. Además, la preparación del entorno para la introducción de un agente MQL5, que simula un usuario real, representa un paso importante para pruebas más realistas.

                  En la parte de tests, la construcción e implementación de scripts de test en MQL5 aseguraron la robustez de la comunicación entre el código MQL5 y la API REST. Los tests abarcaban desde la inicialización del juego hasta la ejecución de jugadas válidas e inválidas, además de verificar condiciones de victoria. Este aspecto es fundamental para garantizar la confiabilidad y estabilidad del sistema.

                  Las mejoras implementadas en la biblioteca Requests, como la inclusión de un parámetro de depuración y un manejo de errores más sofisticado, contribuyeron significativamente a la eficiencia de la comunicación y la facilidad de diagnóstico de problemas.

                  Finalmente, la fase de integración y tests prácticos validó la eficacia de las mejoras implementadas, tanto en el juego del tres en raya como en la biblioteca Requests. La realización de tests a través de la interfaz Swagger y scripts de tests automatizados confirmó la funcionalidad y confiabilidad del sistema como un todo.

                  Este artículo demuestra que la combinación de jugadas automáticas y tests unitarios en MQL5 crea un ciclo de desarrollo robusto, asegurando un sistema integrado confiable y eficaz que ofrece una experiencia de juego mejorada y proporciona insights valiosos para futuras integraciones y desarrollos.



                  Traducción del portugués realizada por MetaQuotes Ltd.
                  Artículo original: https://www.mql5.com/pt/articles/13813

                  Archivos adjuntos |
                  Parte_03.zip (65.15 KB)
                  Desarrollo de un sistema de repetición (Parte 40): Inicio de la segunda fase (I) Desarrollo de un sistema de repetición (Parte 40): Inicio de la segunda fase (I)
                  Esta es la nueva fase del sistema de repetición/simulación. En esta etapa, la conversación será realmente una conversación, y el contenido se volverá bastante denso. Les insto a leer el artículo con atención y a utilizar siempre las referencias que se proporcionen. Esto les ayudará a comprender mejor lo que se les está explicando.
                  Validación cruzada y fundamentos de la inferencia causal en modelos CatBoost, exportación a formato ONNX Validación cruzada y fundamentos de la inferencia causal en modelos CatBoost, exportación a formato ONNX
                  En este artículo veremos un método de autor para crear bots utilizando el aprendizaje automático.
                  Cómo desarrollar un agente de aprendizaje por refuerzo en MQL5 con integración RestAPI  (Parte 4): Organización de funciones en clases en MQL5 Cómo desarrollar un agente de aprendizaje por refuerzo en MQL5 con integración RestAPI (Parte 4): Organización de funciones en clases en MQL5
                  Este artículo examina la transición de la codificación procedimental a la programación orientada a objetos (POO) en MQL5, enfocándose en la integración con REST APIs. Discutimos la organización de funciones de solicitudes HTTP (GET y POST) en clases y destacamos ventajas como el encapsulamiento, la modularidad y la facilidad de mantenimiento. La refactorización de código se detalla, y se muestra la sustitución de funciones aisladas por métodos de clases. El artículo incluye ejemplos prácticos y pruebas.
                  Redes neuronales: así de sencillo (Parte 64): Método de clonación conductual ponderada conservadora (CWBC) Redes neuronales: así de sencillo (Parte 64): Método de clonación conductual ponderada conservadora (CWBC)
                  Como resultado de las pruebas realizadas en artículos anteriores, hemos concluido que la optimalidad de la estrategia entrenada depende en gran medida de la muestra de entrenamiento utilizada. En este artículo, nos familiarizaremos con un método bastante sencillo y eficaz para seleccionar trayectorias para el entrenamiento de modelos.