Bagaimana kami memainkan turnamen

Karena kerja sama, untuk keuntungan saya

- menyatukan.



Matroskin



Melempar kerikil ke dalam air, lihat lingkaran yang mereka bentuk; jika tidak, melempar seperti itu akan menjadi kesenangan kosong.



Kozma Prutkov "Pikiran dan Kata Mutiara".




Baru-baru ini, Jumat lalu, kami memutuskan untuk sedikit mendiversifikasi kehidupan sehari-hari kami dengan mengadakan turnamen pemrograman. Agendanya tidak segera ditentukan. Ada pemikiran tentang pemrosesan data analitik, pembelajaran mesin, tetapi pada akhirnya, mereka menetap di permainan papan. Kami ingin memperkenalkan elemen kompetisi ke dalam acara tersebut, tetapi bagaimana, jika bukan game, yang membuatnya mudah?





Jadi, tim yang ingin mengikuti kompetisi tersedia, mereka juga mencari tahu dana hadiahnya - tinggal memutuskan permainannya. Saya mengusulkan "Atari Go" dan saya punya alasan paling kuat untuk itu.



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



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







. , . , . , , :







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



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



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


Saya memperkirakan keberatan sampai poin terakhir. Ya, memang, banyak bot untuk Go ditulis dan menemukan implementasi yang dapat diakses sama sekali bukan masalah, tetapi Atari Go adalah permainan yang berbeda. Hilangnya batu individu di Go tidak dianggap sebagai bencana - tujuan dalam game sama sekali berbeda. Di Atari Go, kehilangan satu batu saja adalah kekalahan langsung.



Karena kami tidak ingin mengikat peserta dengan satu bahasa pemrograman, diputuskan untuk mengembangkan layanan Web yang menyediakan REST APIuntuk mendaftarkan gerakan peserta turnamen. Selanjutnya, ide ini sepenuhnya membenarkan dirinya sendiri. Selain Java, para pesaing menggunakan C ++, Kotlin, dan bahkan Lua sebagai bahasa pengembangan. Untuk mengecualikan kemungkinan dampak dari kinerja komputer yang berbeda di mana bot direncanakan untuk dijalankan, dua set dari jenis PC mini yang sama dibeli dan diuji pertama kali , di mana versi Ubuntu Linux OS 20 diinstal.







Layanan pelacakan game dikembangkan di Node.js menggunakan kerangka kerja Nest , tetapi itu hanya setengah dari pertempuran. Faktanya adalah bahwa server dirancang sebagai solusi universal yang tidak bergantung pada spesifikasi game apa pun. Tugasnya adalah merekam gerakan pemain dalam database dan mengontrol waktu, tetapi tidak memeriksa gerakan itu sendiri untuk mengetahui kebenarannya. Memeriksa kebenaran gerakan, serta menentukan pemenang, adalah tugas Arbiter , aplikasi JavaScript kecil yang terhubung ke server menggunakan pustaka jQuery .



Detail lebih teknis
— , . 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), , ( , , , ). , ( ). , ( ).



Mengembangkan bot, bahkan untuk Atari Go, itu sulit. Tiga hari yang dialokasikan untuk para kontestan untuk persiapan hanya cukup bagi bot untuk bekerja. Selain itu, PC mini tempat kompetisi diadakan ternyata secara signifikan kurang produktif dibandingkan tempat kerja tempat debugging dilakukan. Semua ini mengarah pada fakta bahwa bot, selama turnamen, tidak bersinar dengan kecerdasan khusus, tetapi momen-momen lucu masih terjadi.





Ini adalah contoh posisi akhir di salah satu permainan turnamen. Pertarungan bot itu menarik dan sengit. Pada akhirnya, Putih mencoba menangkap lawan di shichho , tetapi tidak menyadari bahwa pada langkah selanjutnya Hitam menempatkannya pada posisi atari . Bot White membuat kesalahan saat mencoba melanjutkan "tangga". Hitam segera memanfaatkan ini - mengambil batu dan mengakhiri permainan.



Semua ini menggambarkan dengan baik sifat kesalahan yang dilakukan oleh para peserta turnamen.
, , , . , , , . :







"" — . « », , «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. , . , « », , , , .



, , , . , «».



Namun demikian, babak kualifikasi turnamen dimana masing-masing peserta memainkan dua pertandingan dengan semua pelamar (putih dan hitam) berjalan dengan baik dan kami menentukan dua finalis berdasarkan jumlah kemenangan.







Selanjutnya, permainan berlanjut hingga tiga kemenangan, dengan pergantian urutan langkah pertama. Setelah menang dengan skor akhir 3: 1, pemenang yang puas (dan belum tidur selama tiga malam) mengambil hadiahnya:





Ayo beri tepuk tangan padanya!




All Articles