Cómo jugamos el torneo

Porque el trabajo conjunto, para mi beneficio

, une.



Matroskin



Lanzando guijarros al agua, mira los círculos que forman; de lo contrario, ese lanzamiento será una diversión vacía.



Kozma Prutkov "Pensamientos y aforismos".




Recientemente, el viernes pasado, decidimos diversificar ligeramente nuestra vida cotidiana mediante la celebración de un torneo de programación. La agenda no se determinó de inmediato. Hubo pensamientos sobre el procesamiento de datos analíticos, el aprendizaje automático, pero al final, se detuvieron en los juegos de mesa. Queríamos introducir un elemento de competencia en el evento, pero ¿qué, si no los juegos, lo hace fácil?





Entonces, el equipo que deseaba participar en la competencia estaba disponible, también calcularon el fondo de premios; queda por decidir el juego. Propuse "Atari Go" y tenía las razones más convincentes para ello.



, '' ''?
— , , . , . (), , (). — 19x19. , , . .



. , , . , , «». , , ( ). , , . , :







. , . , . , , :







(), , . , « » . , , . , , . , « », , .



« » — , . — 9x9 . . , — . , , — , . « » . !



  • — , « »
  • « » , " "
  • , , , "-"
  • , ,


Preveo objeciones al último punto. Sí, de hecho, se escriben muchos bots para Go y encontrar una implementación accesible no es en absoluto un problema, pero Atari Go es un juego diferente. La pérdida de piedras individuales en Go no se considera un desastre; los objetivos en el juego son completamente diferentes. En Atari Go, la pérdida de una sola piedra es una derrota inmediata.



Como no queríamos vincular a los participantes con ningún lenguaje de programación, se decidió desarrollar un servicio web que proporcione una API RESTpara registrar los movimientos de los participantes del torneo. Posteriormente, esta idea se justificó plenamente. Además de Java, los competidores utilizaron C ++, Kotlin e incluso Lua como lenguajes de desarrollo. Para excluir la posible influencia de un rendimiento diferente de las computadoras en las que se planeó ejecutar los bots, se compraron y probaron inicialmente dos juegos del mismo tipo de mini PC , en los que se instaló la versión 20 del sistema operativo Ubuntu Linux.







El servicio de seguimiento de juegos se desarrolló en Node.js utilizando el marco de Nest , pero eso fue solo la mitad de la batalla. El caso es que el servidor fue concebido como una solución universal que no depende de las especificidades de ninguno de los juegos. Su tarea es registrar los movimientos de los jugadores en la base de datos y controlar el tiempo, pero no comprueba si los movimientos en sí son correctos. Verificar la exactitud de los movimientos, así como determinar el ganador, es tarea del Arbiter , una pequeña aplicación de JavaScript que se conecta al servidor usando la biblioteca jQuery .



Más detalles técnicos
— , . PostgreSQL. « » , , :





user- token-, ( JWT-). games ( « » ). game_sessions. , ( ) user_games. game_moves.



API
{
   "openapi":"3.0.0",
   "info":{
      "title":"Dagaz Server",
      "description":"Dagaz Server API description",
      "version":"0.0.1",
      "contact":{

      }
   },
   "tags":[
      {
         "name":"dagaz",
         "description":""
      }
   ],
   "servers":[

   ],
   "components":{
      "schemas":{
         "User":{
            "type":"object",
            "properties":{
               "id":{
                  "type":"number"
               },
               "is_admin":{
                  "type":"number"
               },
               "name":{
                  "type":"string"
               },
               "username":{
                  "type":"string"
               },
               "password":{
                  "type":"string"
               },
               "email":{
                  "type":"string"
               },
               "created":{
                  "format":"date-time",
                  "type":"string"
               },
               "deleted":{
                  "format":"date-time",
                  "type":"string"
               },
               "last_actived":{
                  "format":"date-time",
                  "type":"string"
               }
            },
            "required":[
               "id",
               "name",
               "username",
               "created",
               "last_actived"
            ]
         },
         "Pref":{
            "type":"object",
            "properties":{
               "id":{
                  "type":"number"
               },
               "user_id":{
                  "type":"number"
               },
               "game_id":{
                  "type":"number"
               },
               "created":{
                  "format":"date-time",
                  "type":"string"
               }
            },
            "required":[
               "game_id"
            ]
         },
         "Sess":{
            "type":"object",
            "properties":{
               "id":{
                  "type":"number"
               },
               "status":{
                  "type":"number"
               },
               "game_id":{
                  "type":"number"
               },
               "game":{
                  "type":"string"
               },
               "filename":{
                  "type":"string"
               },
               "created":{
                  "format":"date-time",
                  "type":"string"
               },
               "creator":{
                  "type":"string"
               },
               "changed":{
                  "format":"date-time",
                  "type":"string"
               },
               "closed":{
                  "format":"date-time",
                  "type":"string"
               },
               "players_total":{
                  "type":"number"
               },
               "winner":{
                  "type":"number"
               },
               "loser":{
                  "type":"number"
               },
               "score":{
                  "type":"number"
               },
               "last_setup":{
                  "type":"string"
               }
            },
            "required":[
               "game_id"
            ]
         },
         "Challenge":{
            "type":"object",
            "properties":{
               "id":{
                  "type":"number"
               },
               "session_id":{
                  "type":"number"
               },
               "user_id":{
                  "type":"number"
               },
               "user":{
                  "type":"string"
               },
               "player_num":{
                  "type":"number"
               }
            },
            "required":[
               "session_id"
            ]
         },
         "Join":{
            "type":"object",
            "properties":{
               "id":{
                  "type":"number"
               },
               "user_id":{
                  "type":"number"
               },
               "user":{
                  "type":"string"
               },
               "session_id":{
                  "type":"number"
               },
               "player_num":{
                  "type":"number"
               },
               "is_ai":{
                  "type":"number"
               }
            },
            "required":[
               "session_id"
            ]
         },
         "Move":{
            "type":"object",
            "properties":{
               "id":{
                  "type":"number"
               },
               "session_id":{
                  "type":"number"
               },
               "user_id":{
                  "type":"number"
               },
               "turn_num":{
                  "type":"number"
               },
               "move_str":{
                  "type":"string"
               },
               "setup_str":{
                  "type":"string"
               },
               "note":{
                  "type":"string"
               },
               "time_delta":{
                  "type":"number"
               },
               "time_limit":{
                  "type":"number"
               },
               "additional_time":{
                  "type":"number"
               }
            },
            "required":[
               "session_id",
               "user_id",
               "move_str"
            ]
         },
         "Result":{
            "type":"object",
            "properties":{
               "id":{
                  "type":"number"
               },
               "session_id":{
                  "type":"number"
               },
               "user_id":{
                  "type":"number"
               },
               "result_id":{
                  "type":"number"
               },
               "score":{
                  "type":"number"
               }
            },
            "required":[
               "session_id",
               "result_id"
            ]
         }
      }
   },
   "paths":{
      "/api/auth/login":{
         "post":{
            "operationId":"AppController_login",
            "parameters":[

            ],
            "requestBody":{
               "required":true,
               "content":{
                  "application/json":{
                     "schema":{
                        "type":"array",
                        "items":{
                           "$ref":"#/components/schemas/User"
                        }
                     }
                  }
               }
            },
            "responses":{
               "201":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               }
            },
            "security":[
               {
                  "basic":[

                  ]
               }
            ]
         }
      },
      "/api/auth/refresh":{
         "get":{
            "operationId":"AppController_refresh",
            "parameters":[

            ],
            "responses":{
               "201":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               }
            },
            "security":[
               {
                  "basic":[

                  ]
               }
            ]
         }
      },
      "/api/users":{
         "get":{
            "operationId":"UsersController_findAll",
            "parameters":[

            ],
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         },
         "post":{
            "operationId":"UsersController_update",
            "parameters":[

            ],
            "requestBody":{
               "required":true,
               "content":{
                  "application/json":{
                     "schema":{
                        "type":"array",
                        "items":{
                           "$ref":"#/components/schemas/User"
                        }
                     }
                  }
               }
            },
            "responses":{
               "201":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/users/{id}":{
         "get":{
            "operationId":"UsersController_findUsers",
            "parameters":[

            ],
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         },
         "delete":{
            "operationId":"UsersController_delete",
            "parameters":[

            ],
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "403":{
                  "description":"Forbidden."
               },
               "404":{
                  "description":"Not Found."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/preferences":{
         "get":{
            "operationId":"PreferencesController_findAll",
            "parameters":[

            ],
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         },
         "post":{
            "operationId":"PreferencesController_create",
            "parameters":[

            ],
            "requestBody":{
               "required":true,
               "content":{
                  "application/json":{
                     "schema":{
                        "type":"array",
                        "items":{
                           "$ref":"#/components/schemas/Pref"
                        }
                     }
                  }
               }
            },
            "responses":{
               "201":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/preferences/{id}":{
         "delete":{
            "operationId":"PreferencesController_delete",
            "parameters":[

            ],
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "404":{
                  "description":"Not Found."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/session":{
         "get":{
            "operationId":"SessionController_findAll",
            "parameters":[

            ],
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "403":{
                  "description":"Forbidden."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         },
         "post":{
            "operationId":"SessionController_create",
            "parameters":[

            ],
            "requestBody":{
               "required":true,
               "content":{
                  "application/json":{
                     "schema":{
                        "type":"array",
                        "items":{
                           "$ref":"#/components/schemas/Sess"
                        }
                     }
                  }
               }
            },
            "responses":{
               "201":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/session/{id}":{
         "get":{
            "operationId":"SessionController_getSession",
            "parameters":[

            ],
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "404":{
                  "description":"Not Found."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/session/close":{
         "post":{
            "operationId":"SessionController_close",
            "parameters":[

            ],
            "requestBody":{
               "required":true,
               "content":{
                  "application/json":{
                     "schema":{
                        "type":"array",
                        "items":{
                           "$ref":"#/components/schemas/Sess"
                        }
                     }
                  }
               }
            },
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "403":{
                  "description":"Forbidden."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/challenge":{
         "get":{
            "operationId":"ChallengeController_findAll",
            "parameters":[

            ],
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         },
         "post":{
            "operationId":"ChallengeController_create",
            "parameters":[

            ],
            "requestBody":{
               "required":true,
               "content":{
                  "application/json":{
                     "schema":{
                        "type":"array",
                        "items":{
                           "$ref":"#/components/schemas/Challenge"
                        }
                     }
                  }
               }
            },
            "responses":{
               "201":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/challenge/{id}":{
         "delete":{
            "operationId":"ChallengeController_delete",
            "parameters":[

            ],
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "404":{
                  "description":"Not Found."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/join/{id}":{
         "get":{
            "operationId":"JoinController_findJoined",
            "parameters":[

            ],
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/join":{
         "post":{
            "operationId":"JoinController_join",
            "parameters":[

            ],
            "requestBody":{
               "required":true,
               "content":{
                  "application/json":{
                     "schema":{
                        "type":"array",
                        "items":{
                           "$ref":"#/components/schemas/Join"
                        }
                     }
                  }
               }
            },
            "responses":{
               "201":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "404":{
                  "description":"Not Found."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/move/all/{id}":{
         "get":{
            "operationId":"MoveController_getMoves",
            "parameters":[

            ],
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "403":{
                  "description":"Forbidden."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/move/unconfirmed/{id}":{
         "get":{
            "operationId":"MoveController_getUnconfirmedMove",
            "parameters":[

            ],
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "403":{
                  "description":"Forbidden."
               },
               "404":{
                  "description":"Not Found."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/move/confirmed/{id}":{
         "get":{
            "operationId":"MoveController_getConfirmedMove",
            "parameters":[

            ],
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "404":{
                  "description":"Not Found."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/move":{
         "post":{
            "operationId":"MoveController_update",
            "parameters":[

            ],
            "requestBody":{
               "required":true,
               "content":{
                  "application/json":{
                     "schema":{
                        "type":"array",
                        "items":{
                           "$ref":"#/components/schemas/Move"
                        }
                     }
                  }
               }
            },
            "responses":{
               "201":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "404":{
                  "description":"Not Found."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/move/confirm":{
         "post":{
            "operationId":"MoveController_confirm",
            "parameters":[

            ],
            "requestBody":{
               "required":true,
               "content":{
                  "application/json":{
                     "schema":{
                        "type":"array",
                        "items":{
                           "$ref":"#/components/schemas/Move"
                        }
                     }
                  }
               }
            },
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "403":{
                  "description":"Forbidden."
               },
               "404":{
                  "description":"Not Found."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/result/{id}":{
         "get":{
            "operationId":"ResultController_getMoves",
            "parameters":[

            ],
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "404":{
                  "description":"Not Found."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/result":{
         "post":{
            "operationId":"ResultController_join",
            "parameters":[

            ],
            "requestBody":{
               "required":true,
               "content":{
                  "application/json":{
                     "schema":{
                        "type":"array",
                        "items":{
                           "$ref":"#/components/schemas/Result"
                        }
                     }
                  }
               }
            },
            "responses":{
               "201":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "404":{
                  "description":"Not Found."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      },
      "/api/game":{
         "get":{
            "operationId":"GameController_allGames",
            "parameters":[

            ],
            "responses":{
               "200":{
                  "description":"Successfully."
               },
               "401":{
                  "description":"Unauthorized."
               },
               "500":{
                  "description":"Internal Server error."
               }
            },
            "security":[
               {
                  "bearer":[

                  ]
               }
            ]
         }
      }
   }
}




, (POST api/session), (POST api/challenge) (POST api/move). (GET api/challenge) (POST api/join). , , , (GET api/move/confirmed/:id, id — ) (POST api/move).



, , , . , (games.main_time), (games.additional_time), . ( ), . . , , , .



( ), — , . — , (setup_str), , ( , , , ). , ( ). , ( ).



Desarrollar bots, incluso para Atari Go, es difícil. Los tres días asignados a los concursantes para la preparación fueron suficientes solo para que los bots simplemente funcionaran. Además, las mini-PC en las que se llevó a cabo la competencia resultaron ser significativamente menos productivas que las estaciones de trabajo en las que se realizó la depuración. Todo esto llevó a que los bots, durante el torneo, no brillaran con especial inteligencia, pero aun así sucedieron momentos divertidos.





Este es un ejemplo de una posición final en uno de los juegos del torneo. La pelea de bot fue interesante y feroz. Al final, las blancas intentaron atrapar al oponente en el shichho , pero no se dieron cuenta de que en el siguiente movimiento las negras lo colocaron en la posición de atari . El robot de White cometió un error al intentar continuar la "escalera". Las negras inmediatamente se aprovecharon de esto: tomaron una piedra y terminaron el juego.



Todo esto ilustra bien la naturaleza de los errores cometidos por los participantes del torneo.
, , , . , , , . :







"" — . « », , «E6», . , , , — , «» , . «», , . .







, , : "", "" "". , , , . , , , . , , , .



,
« », , . , :



1000    ;  
-----
?????
??B??
?B.??
?????
?????


, , 5x5. , , (, «» ). , . , 90, 180 270 , . . .



Dagaz.AI.Patterns.push({re: /.{7}B.{3}B0.{12}/, price: 1000});
Dagaz.AI.Patterns.push({re: /.{11}B0.{4}B.{7}/, price: 1000});
Dagaz.AI.Patterns.push({re: /.{12}0B.{3}B.{7}/, price: 1000});
Dagaz.AI.Patterns.push({re: /.{7}B.{4}0B.{11}/, price: 1000});


, . , heuristic. , . , « », , , , .



, , , . , «».



Sin embargo, la fase de clasificación del torneo, en la que cada uno de los participantes jugó dos partidos con todos los aspirantes (blancos y negros), salió bien y determinamos dos finalistas en función del número de victorias.







Además, los juegos continuaron hasta tres victorias, con la alternancia del orden del primer movimiento. Habiendo ganado con una puntuación final de 3: 1, el ganador contento (y sin haber dormido durante tres noches) se llevó su premio:





¡Aplaudémoslo!




All Articles