{"id":405859,"date":"2024-06-29T18:34:32","date_gmt":"2024-06-29T18:34:32","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=405859"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=405859","title":{"rendered":"<span>Bot-Games.Fun \u2014 \u0438\u0433\u0440\u043e\u0432\u0430\u044f \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0434\u043b\u044f AI<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u043e\u0447\u0442\u0438 \u0433\u043e\u0434 \u043d\u0430\u0437\u0430\u0434 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u043b \u043e \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 <a href=\"https:\/\/habr.com\/ru\/articles\/575190\/\" rel=\"noopener noreferrer nofollow\">HighLoad.Fun<\/a>, \u0433\u0434\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043e\u0440\u0435\u0432\u043d\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u043e\u0434\u0430, \u043d\u043e \u043d\u0435 \u0443\u043f\u043e\u043c\u044f\u043d\u0443\u043b Bot-Games.Fun &#8212; \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0443, \u0433\u0434\u0435 \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0435\u0433\u043e AI \u0431\u043e\u0442\u0430 \u0434\u043b\u044f \u0443\u0447\u0430\u0441\u0442\u0438\u044f \u0432 \u0438\u0433\u0440\u0430\u0445. \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0435 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u0434\u0440\u0443\u0433\u0438\u0445 \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u044b\u0445 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c &#8212; \u043a\u043e\u0434 \u0431\u043e\u0442\u0430 \u043d\u0435 \u043d\u0430\u0434\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440, \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043d\u0430 \u0441\u0432\u043e\u0451\u043c \u0436\u0435\u043b\u0435\u0437\u0435, \u0447\u0442\u043e \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0448\u0438\u0440\u043e\u0447\u0430\u0439\u0448\u0438\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u043c \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f\u043c \u0438 \u0437\u0430\u0442\u0440\u0430\u0447\u0435\u043d\u043d\u044b\u043c \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c \u043d\u0430 \u043f\u0440\u043e\u0441\u0447\u0451\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0445\u043e\u0434\u0430. \u0410 \u0435\u0449\u0451 \u0432\u0441\u0435 \u0438\u0433\u0440\u044b \u0441 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u043c \u043a\u043e\u0434\u043e\u043c, \u043c\u043e\u0436\u043d\u043e \u0432\u043b\u0438\u044f\u0442\u044c \u043d\u0430 \u043f\u0440\u0430\u0432\u0438\u043b\u0430, \u0443\u043b\u0443\u0447\u0448\u0430\u0442\u044c \u043f\u043b\u0435\u0435\u0440, \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u044f\u0449\u0438\u0439 \u0438\u0433\u0440\u044b, \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u0438\u0433\u0440\u0443, \u043a\u0430\u043a \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043f\u043e\u0434 \u043a\u0430\u0442\u043e\u043c, \u0430 \u0437\u0430\u043e\u0434\u043d\u043e \u0438 \u043f\u0440\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<h2>\u0418\u0434\u0435\u044f<\/h2>\n<p>\u0412 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0435 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442 \u0441\u0432\u043e\u0439 \u043a\u043e\u0434 \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0443 \u0438 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442 \u0441\u043f\u0438\u0441\u043a\u0438 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u043d\u0430 \u0438\u0433\u0440\u0443, \u0430 \u043f\u043e\u0442\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 (\u0431\u043e\u0442\u043e\u0432) \u0434\u043b\u044f \u0441\u0438\u043c\u0443\u043b\u044f\u0446\u0438\u0438. \u0421\u0440\u0435\u0434\u0438 \u0444\u0430\u0442\u0430\u043b\u044c\u043d\u044b\u0445 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u043e\u0432 \u0437\u0434\u0435\u0441\u044c \u044f \u0432\u0438\u0436\u0443 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u043f\u043e \u044f\u0437\u044b\u043a\u0430\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\/\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c, \u044d\u0442\u043e \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435 \u043e\u0431\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Docker&#8217;\u0430 \u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u0433\u043e \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430, \u043d\u043e \u0441\u0430\u043c\u043e\u0435 \u0433\u043b\u0430\u0432\u043d\u043e\u0435, \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u0436\u0435\u043b\u0435\u0437\u0430 \u0441\u0438\u043b\u044c\u043d\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u044b: \u043c\u0430\u043b\u043e \u041e\u0417\u0423, \u043d\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0432\u0438\u0434\u0435\u043e\u043a\u0430\u0440\u0442\u0435, &#8230; \u041c\u043d\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043b\u044e\u0431\u043e\u0439 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u043c\u043e\u0433 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u043e\u0435 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0442\u043e\u0440\u0430\u043c\u0438 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u043f\u0440\u0438\u0431\u043b\u0438\u0436\u0430\u044f \u0442\u0435\u043f\u043b\u043e\u0432\u0443\u044e \u0441\u043c\u0435\u0440\u0442\u044c \u0432\u0441\u0435\u043b\u0435\u043d\u043d\u043e\u0439, \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043a\u0430\u0440\u043c\u0430\u043d\u043d\u043e\u0433\u043e \u0414\u0426 \u0443 \u043c\u0435\u043d\u044f \u043a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e \u043d\u0435\u0442, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0434\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438 \u0441\u0430\u043c\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043b\u0438 \u0441\u0432\u043e\u0438\u0445 \u0431\u043e\u0442\u043e\u0432 \u043d\u0430 \u0441\u0432\u043e\u0451\u043c \u0436\u0435\u043b\u0435\u0437\u0435. \u0412 \u0438\u0442\u043e\u0433\u0435 \u0441\u0445\u0435\u043c\u0430 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<figure class=\"bordered full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/b4c\/e55\/5c1\/b4ce555c13491528f675aaaae564a1e4.png\" width=\"1476\" height=\"677\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/b4c\/e55\/5c1\/b4ce555c13491528f675aaaae564a1e4.png\"\/><\/figure>\n<p>\u041a\u043b\u0438\u0435\u043d\u0442\u044b (\u0431\u043e\u0442\u044b) \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u044e\u0442\u0441\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u0438 \u043f\u043e\u043f\u0430\u0434\u0430\u044e\u0442 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0440\u0430\u0437 \u0432 N \u043c\u0438\u043d\u0443\u0442 (\u0432 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 10) \u0431\u043e\u0442\u044b \u0440\u0430\u0437\u0431\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u043d\u0430 \u0433\u0440\u0443\u043f\u043f\u044b \u0438 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0438\u0433\u0440\u044b. \u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0438\u0433\u0440 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u0431\u0435\u0434\u0438\u0442\u0435\u043b\u0438 \u0438 \u043d\u0430\u0447\u0438\u0441\u043b\u044f\u044e\u0442\u0441\u044f \u043e\u0447\u043a\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Elo_rating_system\" rel=\"noopener noreferrer nofollow\">\u0440\u0435\u0439\u0442\u0438\u043d\u0433 ELO<\/a>.<\/p>\n<p>\u0411\u043e\u0442\u044b \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0442 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u043f\u043e JSONRPC \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u043c\u0443 API. \u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u043a \u043a\u0430\u0436\u0434\u043e\u0439 \u0438\u0433\u0440\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 Swagger&#8217;\u0435 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435.<\/p>\n<h2>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430<\/h2>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u044f \u043d\u0430\u0434 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u043e\u0439, \u044f \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b \u0441\u0435\u0431\u0435 \u0437\u0430\u0434\u0430\u0447\u0443 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0435\u0451 \u0442\u0430\u043a\u043e\u0439, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0439 \u0438\u0433\u0440\u044b \u0431\u044b\u043b\u043e \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u043c, \u0430 \u0432\u0441\u044f \u0440\u0443\u0442\u0438\u043d\u0430 \u0431\u044b\u043b\u0430 \u0441\u043a\u0440\u044b\u0442\u0430 \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b. \u0412\u0442\u043e\u0440\u0430\u044f \u0432\u0430\u0436\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438 \u0437\u0430\u043a\u043b\u044e\u0447\u0430\u043b\u0430\u0441\u044c \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043b\u044e\u0431\u043e\u0439 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u043c\u043e\u0433 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043c\u0438\u043d\u0438\u0441\u0435\u0440\u0432\u0435\u0440 (Local Runner) \u043d\u0430 \u0441\u0432\u043e\u0451\u043c \u0436\u0435\u043b\u0435\u0437\u0435 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f \u043d\u0435\u0439\u0440\u043e\u043d\u043e\u043a, \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043a\u043e\u0434 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438 \u0432 Local Runner&#8217;\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u043c, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u043f\u043b\u0435\u0435\u0440 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0432\u0448\u0438\u0435\u0441\u044f \u0438\u0433\u0440\u044b. \u041f\u043e\u0441\u043b\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439 \u044f \u043f\u0440\u0438\u0448\u0451\u043b \u043a \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0441\u0445\u0435\u043c\u0435:<\/p>\n<figure class=\"bordered full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/523\/863\/605\/5238636056f900121901ed8512d6d685.png\" width=\"1519\" height=\"658\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/523\/863\/605\/5238636056f900121901ed8512d6d685.png\"\/><\/figure>\n<p>\u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0438\u0433\u0440\u044b \u043d\u0430\u0434\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <code>Game<\/code>, \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u044c \u0445\u0435\u043d\u0434\u043b\u0435\u0440\u044b RPC \u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u043b\u0435\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u0438\u0433\u0440\u0443 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435. \u0417\u0430\u0442\u0435\u043c \u0438\u0433\u0440\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 <code>GameManager<\/code> \u0432\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043b\u0438\u0431\u043e \u0432 \u043e\u0431\u0449\u0438\u0439 \u0441\u0435\u0440\u0432\u0435\u0440, \u043b\u0438\u0431\u043e \u0432 LocalRunner. <code>GameManager<\/code> \u0442\u0430\u043a\u0436\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 <code>Storage<\/code> &#8212; \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0445\u043e\u0434\u0430 \u0438\u0433\u0440\u044b \u0438 <code>Scheduler<\/code>&#8212; \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0438\u0433\u0440, \u043d\u043e \u043f\u0440\u043e \u043d\u0438\u0445 \u0434\u0443\u043c\u0430\u0442\u044c \u043d\u0435 \u043d\u0430\u0434\u043e, \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0433\u043e\u0442\u043e\u0432\u044b\u0435.<\/p>\n<p>\u0412\u0441\u0435 \u0438\u0433\u0440\u044b \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u044b\u0435, \u0442.\u0435. \u043a\u0430\u0436\u0434\u0430\u044f \u0438\u0433\u0440\u0430 \u0438\u043c\u0435\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 &#8212; <code>State<\/code> \u0438 \u043d\u0430\u0431\u043e\u0440 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 &#8212; <code>Actions<\/code>, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438 \u043c\u0435\u043d\u044f\u044e\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435. \u0412\u0441\u0435 \u0438\u0433\u0440\u044b \u0440\u0430\u0437\u0431\u0438\u0442\u044b \u043d\u0430 \u0445\u043e\u0434\u044b &#8212; <code>Ticks<\/code>. \u0412 \u0438\u0433\u0440\u0430\u0445 \u0435\u0441\u0442\u044c \u043a\u0430\u043a \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b, \u0442\u0430\u043a \u0438 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435. \u0414\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0438 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442 \u0435\u0441\u0442\u044c <code>Options<\/code>, \u043e\u043d \u043d\u0443\u0436\u0435\u043d \u0447\u0442\u043e\u0431\u044b \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u0442\u044c \u043e\u0431\u044a\u0451\u043c \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u0438 \u0438\u0433\u0440\u044b, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u043a\u0430\u0436\u0434\u043e\u043c <code>Tick<\/code>&#8216;\u0435  \u0442\u043e, \u0447\u0442\u043e \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f, \u0430 <code>State<\/code> \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u043a\u0430\u0436\u0434\u044b\u0439 \u0445\u043e\u0434. \u0421 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u0431\u043e\u0442\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0438\u0433\u0440\u044b \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<figure class=\"bordered full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f2c\/f9c\/02e\/f2cf9c02eca635e469e4e91a44c192a4.png\" width=\"1829\" height=\"546\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f2c\/f9c\/02e\/f2cf9c02eca635e469e4e91a44c192a4.png\"\/><\/figure>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0438\u0433\u0440\u044b &#171;\u041c\u043e\u0440\u0441\u043a\u043e\u0439 \u0431\u043e\u0439&#187;, \u044f \u043f\u043e\u043a\u0430\u0436\u0443 \u043a\u0430\u043a \u043b\u0435\u0433\u043a\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0438\u0433\u0440\u044b. \u041f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0438 \u0438\u0433\u0440\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u043d\u0430 Go, \u0430 \u043f\u043b\u0435\u0435\u0440 &#8212; \u043d\u0430 TypeScript + WebPack + Three.js \u0434\u043b\u044f \u0433\u0440\u0430\u0444\u0438\u043a\u0438. \u0412\u0441\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u043d\u0430 <a href=\"https:\/\/github.com\/bot-games\" rel=\"noopener noreferrer nofollow\">GitHub<\/a>.<\/p>\n<h2>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043b\u043e\u0433\u0438\u043a\u0438 \u0438\u0433\u0440\u044b<\/h2>\n<p>\u041a\u0430\u043a \u044f \u0433\u043e\u0432\u043e\u0440\u0438\u043b \u0432\u044b\u0448\u0435, \u0432\u0441\u0451 \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u044d\u0442\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <code>Game<\/code>:<\/p>\n<pre><code class=\"go\">type Game interface { Init() (options proto.Message, state proto.Message, waitUsers uint8, gameData any) CheckAction(tickInfo *TickInfo, action proto.Message) error ApplyActions(tickInfo *TickInfo, actions []Action) *TickResult SmartGuyTurn(tickInfo *TickInfo) proto.Message }<\/code><\/pre>\n<p>\u041c\u0435\u0442\u043e\u0434 Init \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 4 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435:<\/p>\n<ol>\n<li>\n<p><code>Options<\/code> &#8212; \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b \u0438 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b.<\/p>\n<\/li>\n<li>\n<p><code>State<\/code> &#8212; \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0438\u0433\u0440\u044b \u043d\u0430 0 \u0445\u043e\u0434\u0443.<\/p>\n<\/li>\n<li>\n<p>\u0411\u0438\u0442\u043e\u0432\u0430\u044f \u043c\u0430\u0441\u043a\u0430 \u0438\u0433\u0440\u043e\u043a\u043e\u0432, \u043e\u0442 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f \u0445\u043e\u0434 (\u0431\u0438\u0442 \u0432\u043e\u0437\u0432\u0435\u0434\u0451\u043d &#8212; \u0436\u0434\u0451\u043c \u0445\u043e\u0434, \u043d\u0435\u0442 &#8212; \u043e\u0442 \u0438\u0433\u0440\u043e\u043a\u0430 \u043d\u0435 \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u0445\u043e\u0434\u0435 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f)<\/p>\n<\/li>\n<li>\n<p>\u041b\u044e\u0431\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0430 \u0438\u0433\u0440\u044b. \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u0438\u0433\u0440\u044b &#171;\u0414\u0440\u043e\u043d\u044b&#187;, \u0442\u0430\u043c \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442 <code>world<\/code> \u0434\u043b\u044f \u0441\u0438\u043c\u0443\u043b\u044f\u0446\u0438\u0438 \u0444\u0438\u0437\u0438\u043a\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 Box2d.<\/p>\n<\/li>\n<\/ol>\n<p>\u041c\u0435\u0442\u043e\u0434 <code>CheckAction<\/code> \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u043e\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u043c \u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u043e \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 <code>ApplyActions<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439, \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u0430\u0441\u044c \u043b\u0438 \u0438\u0433\u0440\u0430 \u0438\u043b\u0438 \u043d\u0435\u0442.<\/p>\n<p>\u041f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0438\u043c\u0435\u0435\u0442 debug \u0440\u0435\u0436\u0438\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0433\u0440\u0430\u0442\u044c \u043d\u0435 \u043d\u0430 \u0440\u0435\u0439\u0442\u0438\u043d\u0433 \u0441\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u043c \u0441\u043e\u043f\u0435\u0440\u043d\u0438\u043a\u043e\u043c &#8212; SmartGuy&#8217;\u0435\u043c \u0431\u0435\u0437 \u0443\u0447\u0451\u0442\u0430 \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u043e\u0432. \u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 <code>SmartGuyTurn<\/code> \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0442\u0441\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u043e\u0433\u043e \u0438\u0433\u0440\u043e\u043a\u0430.<\/p>\n<h3>Protobuf<\/h3>\n<p>\u0418\u0433\u0440\u0430 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u0430\u044f \u0438 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043e\u043f\u0446\u0438\u0439 \u0438 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0432 \u043d\u0435\u0439 \u043d\u0435\u0442, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 <code>Options<\/code> \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u0443\u0441\u0442\u044b\u043c:<\/p>\n<pre><code class=\"perl\">message Options {}<\/code><\/pre>\n<p>\u0418\u0433\u0440\u043e\u0432\u043e\u0435 \u043f\u043e\u043b\u0435 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 2 \u043c\u0430\u0442\u0440\u0438\u0446 10\u044510, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u044b \u043a\u043e\u0440\u0430\u0431\u043b\u0438. \u0421\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043f\u0440\u043e\u0442\u043e\u0444\u0430\u0439\u043b <code>State<\/code> \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"perl\">message State {   repeated Cell field1 = 1;   repeated Cell field2 = 2; }  enum Cell {   EMPTY = 0;   SHIP = 1;   MISSED = 2;   GOT = 3; }<\/code><\/pre>\n<p>\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f (<code>Action<\/code>) \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c 3\u0445 \u0432\u0438\u0434\u043e\u0432:<\/p>\n<ol>\n<li>\n<p><code>ActionSkip<\/code> &#8212;  \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u0442\u044c.<\/p>\n<\/li>\n<li>\n<p><code>ActionSetup<\/code> &#8212; \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u0430\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043a\u043e\u0440\u0430\u0431\u043b\u0435\u0439.<\/p>\n<\/li>\n<li>\n<p><code>ActionFire<\/code> &#8212; \u0430\u0442\u0430\u043a\u0430 \u043a\u043b\u0435\u0442\u043a\u0438.<\/p>\n<\/li>\n<\/ol>\n<p>\u0412 \u043f\u0440\u043e\u0442\u043e\u0444\u0430\u0439\u043b\u0435 \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"perl\">message Action {   oneof data {     ActionSkip skip = 1;     ActionSetup setup = 2;     ActionFire fire = 3;   } }  message ActionSkip {}  message ActionSetup {   message Ship {     string coordinate = 1;     bool vertical = 2;   }    Ship shipL4N1 = 1;   Ship shipL3N1 = 2;   Ship shipL3N2 = 3;   Ship shipL2N1 = 4;   Ship shipL2N2 = 5;   Ship shipL2N3 = 6;   Ship shipL1N1 = 7;   Ship shipL1N2 = 8;   Ship shipL1N3 = 9;   Ship shipL1N4 = 10; }  message ActionFire {   string coordinate = 1; }<\/code><\/pre>\n<p>\u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u0431\u0443\u043a\u0432\u0430-\u0446\u0438\u0444\u0440\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <code>A1<\/code>.<\/p>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 Game<\/h3>\n<p>\u041c\u0435\u0442\u043e\u0434 <code>Init<\/code> \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u0435\u043d: \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c <code>Options<\/code> \u0438 <code>State<\/code>, \u0442\u0430\u043a \u043a\u0430\u043a. \u0438\u0433\u0440\u043e\u043a\u043e\u0432 \u0434\u0432\u043e\u0435 \u0438 \u043e\u0442 \u043e\u0431\u043e\u0438\u0445 \u043e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f \u0445\u043e\u0434, \u0442\u043e \u0432\u043e\u0437\u0432\u043e\u0434\u0438\u043c \u043d\u0443\u043b\u0435\u0432\u043e\u0439 \u0438 \u043f\u0435\u0440\u0432\u044b\u0439 \u0431\u0438\u0442\u044b, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c 3 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0435\u0433\u043e. \u041d\u0438\u043a\u0430\u043a\u0438\u0445 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0435 \u0438\u0433\u0440\u044b \u043d\u0435 \u043d\u0430\u0434\u043e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0447\u0435\u0442\u0432\u0451\u0440\u0442\u044b\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u0431\u0443\u0434\u0435\u0442 nil.<\/p>\n<pre><code class=\"go\">func (b Battleships) Init() (proto.Message, proto.Message, uint8, any) { return &amp;pb.Options{}, &amp;pb.State{ Field1: generateRandomField(), Field2: generateRandomField(), }, 3, nil }<\/code><\/pre>\n<p>\u0412 \u043c\u0435\u0442\u043e\u0434\u0435 <code>CheckAction<\/code> \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0433\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0447\u0442\u043e \u044d\u0442\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e, \u0430 \u0435\u0441\u043b\u0438 \u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u0442\u043e \u0438 \u0438\u0445 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u044c:<\/p>\n<pre><code class=\"go\">func (b Battleships) CheckAction(tickInfo *manager.TickInfo, action proto.Message) error { switch curAction := action.(*pb.Action).Data.(type) { case *pb.Action_Skip: if !GetActions(tickInfo)[ActionSkip] { return manager.ErrInvalidAction } return nil  case *pb.Action_Setup: return b.CheckActionSetup(tickInfo, curAction.Setup)  case *pb.Action_Fire: return b.CheckActionFire(tickInfo, curAction.Fire)  default: panic(\"invalid action\") } }  ...  func (Battleships) CheckActionFire(tickInfo *manager.TickInfo, fire *pb.ActionFire) error { if !GetActions(tickInfo)[ActionFire] { return manager.ErrInvalidAction }  _, _, err := CoordinateToXY(fire.Coordinate) return err }<\/code><\/pre>\n<p>\u0412 \u043c\u0435\u0442\u043e\u0434\u0435 <code>ApplyActions<\/code> \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 <code>State<\/code> \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0437\u0430\u043a\u043e\u043d\u0447\u0435\u043d\u0430 \u0438\u0433\u0440\u0430 \u0438\u043b\u0438 \u043d\u0435\u0442. \u0412\u0441\u044f \u0447\u0451\u0440\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u043f\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u043e\u0432 \u043e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u044b\u0445 Tick&#8217;\u043e\u0432 \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438\u0445 \u0432 \u0411\u0414, &#8230;,  \u043b\u0435\u0436\u0438\u0442 \u043d\u0430 <code>GameManager<\/code>, \u0432\u043d\u0443\u0442\u0440\u0438 \u0438\u0433\u0440\u044b \u043e\u0431 \u044d\u0442\u043e\u043c \u0431\u0435\u0441\u043f\u043e\u043a\u043e\u0438\u0442\u044c\u0441\u044f \u043d\u0435 \u043d\u0430\u0434\u043e. \u041f\u043b\u044e\u0441 \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0441\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0431\u044b\u043b\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u0430 \u0432 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u043c \u043c\u0435\u0442\u043e\u0434\u0435. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u043d\u0430\u0434\u043e \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0443\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044c \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <\/p>\n<pre><code class=\"go\">type TickResult struct { GameFinished    bool Winner          uint8 NewState        proto.Message NextTurnPlayers uint8 }<\/code><\/pre>\n<p><code>GameFinished<\/code> \u0440\u0430\u0432\u0435\u043d <code>true<\/code>, \u0435\u0441\u043b\u0438 \u0438\u0433\u0440\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430, <code>Winner<\/code> &#8212; \u043d\u043e\u043c\u0435\u0440 \u043f\u043e\u0431\u0435\u0434\u0438\u0432\u0448\u0435\u0433\u043e \u0438\u0433\u0440\u043e\u043a\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 1, \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0438\u0447\u044c\u0435\u0439 &#8212; 0, <code>NewState<\/code> &#8212; \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435, <code>NextTurnPlayers<\/code> &#8212; \u043c\u0430\u0441\u043a\u0430 \u0438\u0433\u0440\u043e\u043a\u043e\u0432, \u043e\u0442 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f \u0445\u043e\u0434, \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u043a\u0430\u043a \u0438 \u0432 <code>Init<\/code>. \u041d\u0438\u0436\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f:<\/p>\n<pre><code class=\"go\">func (b Battleships) ApplyActions(tickInfo *manager.TickInfo, actions []manager.Action) *manager.TickResult { for _, action := range actions { tickInfo.CurUid = action.Uid  switch curAction := action.Action.(*pb.Action).Data.(type) { case *pb.Action_Skip: \/\/ Do nothing  case *pb.Action_Setup: tickInfo.State = b.DoActionSetup(tickInfo, curAction.Setup)  case *pb.Action_Fire: tickInfo.State = b.DoActionFire(tickInfo, curAction.Fire)  default: panic(\"invalid action\") } }  res := &amp;manager.TickResult{ NewState: tickInfo.State, }  if finished, winner := isGameFinished(tickInfo); finished { res.GameFinished = finished res.Winner = winner } else { res.NextTurnPlayers = 3 }  return res }  ...  func (Battleships) DoActionFire(tickInfo *manager.TickInfo, fire *pb.ActionFire) proto.Message { x, y, _ := CoordinateToXY(fire.Coordinate)  f := GetField(tickInfo, false) switch f[y*10+x] { case pb.Cell_EMPTY: f[y*10+x] = pb.Cell_MISSED case pb.Cell_SHIP: f[y*10+x] = pb.Cell_GOT }  return tickInfo.State }<\/code><\/pre>\n<p>\u0418 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043c\u0435\u0442\u043e\u0434 <code>SmartGuyTurn<\/code>. SmartGuy \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u043d\u0435 \u043e\u0447\u0435\u043d\u044c smart \u0438 \u0431\u0443\u0434\u0435\u0442 \u0441\u0442\u0440\u0435\u043b\u044f\u0442\u044c \u0432 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u0443\u044e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0443\u044e \u043a\u043b\u0435\u0442\u043a\u0443:<\/p>\n<pre><code class=\"go\">func (Battleships) SmartGuyTurn(tickInfo *manager.TickInfo) proto.Message { actions := GetActions(tickInfo)  if actions[ActionFire] { var availableCoords []string for y := 0; y &lt; 10; y++ { for x := 0; x &lt; 10; x++ { c := tickInfo.State.(*pb.State).Field1[y*10+x] if c == pb.Cell_EMPTY || c == pb.Cell_SHIP { availableCoords = append(availableCoords, fmt.Sprintf(\"%s%d\", string(rune('A'+y)), x)) } } }  return &amp;pb.Action{Data: &amp;pb.Action_Fire{Fire: &amp;pb.ActionFire{Coordinate: availableCoords[rand.Intn(len(availableCoords))]}}}  } else if actions[ActionSkip] { return &amp;pb.Action{Data: &amp;pb.Action_Skip{}}  } else { panic(\"no known actions\") } }<\/code><\/pre>\n<p>\u0412\u043e\u0442 \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435 \u0438 \u0432\u0441\u0451, \u043b\u043e\u0433\u0438\u043a\u0430 \u0438\u0433\u0440\u044b \u0433\u043e\u0442\u043e\u0432\u0430, \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c API.<\/p>\n<h3>API<\/h3>\n<p>\u041e\u0431\u044a\u0435\u043a\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0438\u0439 API \u0434\u043e\u043b\u0436\u0435\u043d \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0443<\/p>\n<pre><code class=\"go\">type GameApi interface { http.Handler GetSwagger(ctx context.Context) *openapi.OpenApi GetPlayerHandler() http.Handler }<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 API \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/github.com\/go-qbit\/rpc\" rel=\"noopener noreferrer nofollow\">github.com\/go-qbit\/rpc<\/a>, \u043e\u043d\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043b\u0435\u0433\u043a\u043e \u043e\u043f\u0438\u0441\u0430\u0442\u044c JSONRPC \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 <code>swagger.json<\/code> \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438, \u043f\u0440\u0438 \u044d\u0442\u043e \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0432 \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u044f\u0445 \u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u043f\u043b\u044e\u0441 \u0432\u0435\u0440\u0441\u0438\u043e\u043d\u043d\u043e\u0441\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u0438 \u0434\u0430\u0436\u0435 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043e\u0448\u0438\u0431\u043e\u043a \u0432 Swagger&#8217;\u0435. \u041a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"go\">type BattleshipsRpc struct { *rpc.Rpc }  func New(gm *manager.GameManager) *BattleshipsRpc { gameRpc := &amp;BattleshipsRpc{rpc.New(\"github.com\/bot-games\/battleships\/api\/method\", rpc.WithCors(\"*\"))}  if err := gameRpc.RegisterMethods( mJoin.New(gm), mWaitTurn.New(gm), mActionSkip.New(gm), mActionSetup.New(gm), mActionFire.New(gm), ); err != nil { panic(err) }  return gameRpc }<\/code><\/pre>\n<p>\u0412\u0441\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u044f \u0440\u0430\u0441\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043d\u0435 \u0431\u0443\u0434\u0443, \u0430 \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0440\u0430\u0437\u0431\u0435\u0440\u0443 <code>mWaitTurn<\/code> &#8212; \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u0445\u043e\u0434\u0430. \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043b\u0435\u0436\u0438\u0442 \u0432 \u043f\u0430\u043f\u043a\u0435 <code>battleships\/api\/method\/wait_turn<\/code> \u0438 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 2 \u0444\u0430\u0439\u043b\u043e\u0432,<code>method.go<\/code> &#8212; \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u0438 \u0431\u0430\u0437\u043e\u0432\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u043e \u043c\u0435\u0442\u043e\u0434\u0435 \u0438 <code>v1.go<\/code> &#8212; \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0432\u0435\u0440\u0441\u0438\u0438 1.<\/p>\n<p>\u0412\u0441\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0442 \u0441 \u0438\u0433\u0440\u043e\u0439 \u0447\u0435\u0440\u0435\u0437 <code>GameManager<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043e\u043d\u0438 \u043d\u0443\u0436\u0434\u0430\u044e\u0442\u0441\u044f \u0432 \u0441\u0441\u044b\u043b\u043a\u0435 \u043d\u0430 \u043d\u0435\u0433\u043e, \u0447\u0442\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043e \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435 \u0438 \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440\u0435 \u043d\u0438\u0436\u0435<\/p>\n<pre><code class=\"go\">type Method struct { gm *manager.GameManager }  func New(gm *manager.GameManager) *Method { return &amp;Method{ gm: gm, } }  func (m *Method) Caption(context.Context) string { return `Wait turn` }  func (m *Method) Description(context.Context) string { return `Call the method to wait your turn and get the game status` }<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0436\u0435 \u043a\u0430\u0436\u0434\u044b\u0439 RPC-\u043c\u0435\u0442\u043e\u0434 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u043c\u0435\u0442\u043e\u0434\u044b \u0441 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u043c \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435\u043c \u0438 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435\u043c \u0434\u043b\u044f Swagger&#8217;\u0430, \u0432\u0441\u044f \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0432\u043e\u0437\u044c\u043c\u0451\u0442\u0441\u044f \u0438\u0437 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438:<\/p>\n<pre><code class=\"go\">type reqV1 struct { Token  string `json:\"token\" desc:\"User bot token from [profile](\/profile)\"` GameId string `json:\"game_id\"` }  type stateV1 struct { TickId        uint16   `json:\"tick_id\"` Actions       []string `json:\"actions\" desc:\"Available actions\"` YourField     []string `json:\"your_field\"` OpponentField []string `json:\"opponent_field\"` }  var errorsV1 struct { InvalidToken  rpc.ErrorFunc `desc:\"Invalid token\"` InvalidGameId rpc.ErrorFunc `desc:\"Invalid game ID\"` GameFinished  rpc.ErrorFunc `desc:\"The game has finished. The result is in the data field, can be one of **Draw**, **Win**, **Defeat**\"` }  func (m *Method) ErrorsV1() interface{} { return &amp;errorsV1 }  func (m *Method) V1(ctx context.Context, r *reqV1) (*stateV1, error) { tickInfo, err := m.gm.WaitTurn(ctx, r.Token, r.GameId) if err != nil { errGameFinished := &amp;manager.ErrEndOfGame{}  if errors.Is(err, manager.ErrInvalidToken) { return nil, errorsV1.InvalidToken(\"Invalid token\") } else if errors.Is(err, manager.ErrInvalidGameId) { return nil, errorsV1.InvalidGameId(\"Invalid game ID\") } else if errors.As(err, errGameFinished) { var gameResult string if errGameFinished.Winner == 0 { gameResult = \"Draw\" } else if errGameFinished.IsYou { gameResult = \"Win\" } else { gameResult = \"Defeat\" }  return nil, errorsV1.GameFinished(\"The game has finished\", gameResult) }  return nil, err }  actionsMap := battleships.GetActions(tickInfo) actions := make([]string, 0, len(actionsMap)) for action := range actionsMap { actions = append(actions, action) }  return &amp;stateV1{ TickId:        tickInfo.Id, Actions:       actions, YourField:     createField(tickInfo, true), OpponentField: createField(tickInfo, false), }, nil }<\/code><\/pre>\n<p>\u0412\u043e \u0432\u0445\u043e\u0434\u043d\u043e\u0439 (<code>reqV1<\/code>) \u0438 \u0432\u044b\u0445\u043e\u0434\u043d\u043e\u0439 (<code>stateV1<\/code>) \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430\u0445 \u043f\u043e\u043c\u0438\u043c\u043e JSON \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 <code>desc<\/code>, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u043e \u0432 Swagger. \u041e\u0441\u043e\u0431\u043e\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u043e\u0431\u0440\u0430\u0442\u0438\u0442\u044c \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <code>errorsV1<\/code> \u0438 \u043c\u0435\u0442\u043e\u0434 <code>ErrorsV1<\/code>, \u043e\u043d\u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u044e\u0442 \u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0431\u0438\u0437\u043d\u0435\u0441\u043b\u043e\u0433\u0438\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c \u0432 Swagger \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438 \u0438 \u043e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u0437\u043d\u0430\u0442\u044c \u0447\u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0436\u0434\u0430\u0442\u044c \u043e\u0442 \u0441\u0435\u0440\u0432\u0435\u0440\u0430. \u0415\u0441\u043b\u0438 \u043c\u0435\u0442\u043e\u0434 \u0432\u0435\u0440\u043d\u0451\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 \u043d\u0435 \u0438\u0437 \u044d\u0442\u043e\u0433\u043e \u0441\u043f\u0438\u0441\u043a\u0430, \u0442\u043e \u043a\u043b\u0438\u0435\u043d\u0442 \u043f\u043e\u043b\u0443\u0447\u0438\u0442 <code>500 - Internal server error<\/code>.<\/p>\n<p>\u0412\u0441\u044f \u043b\u043e\u0433\u0438\u043a\u0430 \u043b\u0435\u0436\u0438\u0442 \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 <code>V1<\/code> \u0438 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 3 \u0447\u0430\u0441\u0442\u0435\u0439:<\/p>\n<ol>\n<li>\n<p>\u0418\u0437 <code>GameManager<\/code> \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0442\u0435\u043a\u0443\u0449\u0438\u0439 <code>State<\/code> \u0438\u0433\u0440\u044b<\/p>\n<\/li>\n<li>\n<p>\u0415\u0441\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0430, \u0442\u043e \u043f\u044b\u0442\u0430\u0435\u043c\u0441\u044f \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0442\u044c \u0435\u0451 \u0432 \u043e\u0434\u043d\u0443 \u0438\u0437 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430.<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440 \u0441 \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u043c \u0441\u043a\u0440\u044b\u0432\u0430\u043d\u0438\u0435\u043c \u0441\u0435\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u0447\u0443\u0436\u043e\u043c \u043f\u043e\u043b\u0435.<\/p>\n<\/li>\n<\/ol>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u043b\u0435\u0435\u0440 \u043d\u0430 JavaScript \u0438 \u0434\u0430\u0442\u044c \u043a \u043d\u0435\u043c\u0443 \u0434\u043e\u0441\u0442\u0443\u043f \u043f\u043e HTTP \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 <code>GetPlayerHandler<\/code>:<\/p>\n<pre><code class=\"go\">func (r *BattleshipsRpc) GetPlayerHandler() http.Handler { return player.NewHTTPHandler() }<\/code><\/pre>\n<p>\u0412\u0441\u0435 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0444\u0430\u0439\u043b\u044b \u043f\u0440\u0438\u043b\u0438\u043d\u043a\u043e\u0432\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u043a \u0431\u0438\u043d\u0430\u0440\u043d\u0438\u043a\u0443, \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0441\u0432\u043e\u0439 \u0441\u0442\u0430\u0440\u044b\u0439 \u0433\u0435\u043d\u0435\u0440\u0430\u0442\u043e\u0440 <a href=\"https:\/\/github.com\/sergei-svistunov\/gostatic2lib\" rel=\"noopener noreferrer nofollow\">gostatic2lib<\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u044f \u0441\u0434\u0435\u043b\u0430\u043b \u0435\u0449\u0451 \u0434\u043e \u043f\u043e\u044f\u0432\u043b\u0435\u043d\u0438\u044f <code>go:embed<\/code>, \u043d\u043e \u0434\u043e \u0441\u0438\u0445 \u043f\u043e\u0440 \u0432\u044b\u0431\u0438\u0440\u0430\u044e \u0435\u0433\u043e, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d \u0441\u0436\u0438\u043c\u0430\u0435\u0442 \u0444\u0430\u0439\u043b\u044b \u0438 \u0445\u0440\u0430\u043d\u0438\u0442 \u0438\u0445 \u0432 \u0441\u0436\u0430\u0442\u043e\u043c \u0432\u0438\u0434\u0435, \u043f\u043b\u044e\u0441 \u0441\u0440\u0430\u0437\u0443 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 HTTP Handler \u0434\u043b\u044f \u0438\u0445 \u0440\u0430\u0437\u0434\u0430\u0447\u0438 \u0443\u0436\u0435 \u0432 \u0441\u0436\u0430\u0442\u043e\u043c \u0432\u0438\u0434\u0435. <code>bundle.js<\/code> \u043b\u0435\u0436\u0438\u0442 \u0432 \u043f\u0430\u043f\u043a\u0435 ..\/player\/dist, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u0447\u0442\u043e\u0431\u044b \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u043a\u0435\u0442 <code>player<\/code>, \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442\u044c \u043a\u043e\u043c\u0430\u043d\u0434\u0443 <code>gostatic2lib -path ..\/player\/dist -package player -out .\/player\/dist.go<\/code>.<\/p>\n<p>\u0418\u0433\u0440\u0430 \u0433\u043e\u0442\u043e\u0432\u0430, \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c main.go \u0434\u043b\u044f Local Runner&#8217;\u0430:<\/p>\n<pre><code class=\"go\">package main  import ( \"github.com\/bot-games\/battleships\" \"github.com\/bot-games\/battleships\/api\" manager \"github.com\/bot-games\/game-manager\" \"github.com\/bot-games\/localrunner\" \"github.com\/bot-games\/localrunner\/scheduler\" \"github.com\/bot-games\/localrunner\/storage\" )  func main() { gameStorage := storage.New()  localrunner.Start( manager.New( \"battleships\", \"Battleships\", battleships.Battleships{}, gameStorage, scheduler.New(), func(m *manager.GameManager) manager.GameApi { return api.New(m) }, ), gameStorage, ) }<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e \u0441\u0435\u0440\u0432\u0435\u0440 \u043f\u043e\u0434\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043f\u043e\u0440\u0442\u0443 :10000, \u0433\u0434\u0435 \u0431\u0443\u0434\u0443\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0438 \u0441\u043f\u0438\u0441\u043e\u043a \u0438\u0433\u0440, \u0433\u0434\u0435 \u043c\u043e\u0436\u043d\u043e \u0438\u0445 \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c.<\/p>\n<figure class=\"bordered full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/739\/0fb\/55c\/7390fb55cb50323d01ce635fec8fe7b1.png\" width=\"1920\" height=\"3219\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/739\/0fb\/55c\/7390fb55cb50323d01ce635fec8fe7b1.png\"\/><\/figure>\n<h3>\u041f\u043b\u0435\u0435\u0440<\/h3>\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/64861231f926c601e174c8a2\" data-style=\"\" id=\"64861231f926c601e174c8a2\" width=\"\"><\/div>\n<p>\u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u044f \u043d\u0430\u0447\u0430\u043b \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043b\u0435\u0435\u0440 \u043d\u0430 WASM, \u043d\u043e \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u043e\u0442\u043a\u0430\u0437\u0430\u043b\u0441\u044f \u043e\u0442 \u044d\u0442\u043e\u0439 \u0438\u0434\u0435\u0438 \u0432 \u043f\u043e\u043b\u044c\u0437\u0443 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0439 \u0438\u0437 TypeScript, \u0442\u0430\u043a \u043a\u0430\u043a \u043f\u043e \u0444\u0430\u043a\u0442\u0443 \u043a\u043e\u0434 \u043d\u0430 WASM \u0432\u0441\u0451 \u0440\u0430\u0432\u043d\u043e \u043e\u0431\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043a JS \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430, \u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u0442 \u0443\u0434\u043e\u0431\u043d\u044b\u0445 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a, \u043f\u043e \u043a\u0440\u0430\u0439\u043d\u0435\u0439 \u043c\u0435\u0440\u0435 \u0434\u043b\u044f Go, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u044f \u043f\u043b\u043e\u0445\u043e \u0438\u0441\u043a\u0430\u043b. \u0412 \u0438\u0442\u043e\u0433\u0435 \u043f\u0440\u0438\u0448\u0451\u043b \u043a \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0441\u0445\u0435\u043c\u0435: \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0441 \u0438\u0433\u0440\u043e\u0439 \u0441\u043e\u0437\u0434\u0430\u0451\u0442 \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>player(p)<\/code>, \u0433\u0434\u0435 <code>p<\/code> &#8212; \u043f\u0430\u043a\u0435\u0442, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0438\u0439 \u043a\u043b\u0430\u0441\u0441 <code>Player<\/code>, \u043a\u043e\u043d\u0441\u0442\u0440\u0443\u043a\u0442\u043e\u0440 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043e\u0436\u0438\u0434\u0430\u0435\u0442 \u043a\u043e\u043d\u0442\u0435\u0439\u043d\u0435\u0440, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u043d\u0430 \u0438\u0433\u0440\u0430, \u0438 JSON \u0441 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043e\u0431 \u0438\u0433\u0440\u0435. \u0412 \u0443\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u043e\u043c \u0432\u0438\u0434\u0435 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"javascript\">function player(p) {     new p.Player(         window.document.getElementById('player'),         {...JSON \u0441 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0435\u0439 \u043e\u0431 \u0438\u0433\u0440\u0435...}     } }<\/code><\/pre>\n<p>\u0417\u0430\u0442\u0435\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0441 \u0438\u0433\u0440\u043e\u0439 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0444\u0430\u0439\u043b <code>bundle.js<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>player<\/code> (\u0441\u0445\u0435\u043c\u0430 JSONP). \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c <code>bundle.js<\/code> \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u043d\u0430\u0434 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435\u0439 \u0438\u0433\u0440\u044b \u0438 \u043c\u043e\u0436\u0435\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0441\u0451 \u0447\u0442\u043e \u0443\u0433\u043e\u0434\u043d\u043e.<\/p>\n<p>\u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u0441\u0431\u043e\u0440\u043a\u0438 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b WebPack, \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043c\u043e\u043c\u0435\u043d\u0442\u044b \u0435\u0433\u043e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0438 \u043d\u0438\u0436\u0435:<\/p>\n<pre><code class=\"javascript\">module.exports = {     entry: '.\/src\/index.ts',     module: {         rules: [             {                 test: \/\\.(png|svg|jpg|gif|obj)$\/,                 loader: 'url-loader'             },             {                 test: \/\\.json$\/,                 loader: 'json-loader',                 type: 'javascript\/auto',             },             {                 test: \/\\.tsx?$\/,                 use: 'ts-loader',                 exclude: \/node_modules\/,             }         ],     },     resolve: {         extensions: ['.tsx', '.ts', '.js'],     },     output: {         clean: true,         filename: 'bundle.js',         path: path.resolve(__dirname, 'dist'),         library: 'player',         libraryTarget: 'jsonp',     } };<\/code><\/pre>\n<p>\u041a\u043e\u0434 \u043b\u0435\u0436\u0438\u0442 \u0432 \u0444\u0430\u0439\u043b\u0435 <code>src\/index.ts<\/code>, \u043d\u0430 \u0432\u044b\u0445\u043e\u0434\u0435 \u043e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f <code>bundle.js<\/code> \u0432 \u043f\u0430\u043f\u043a\u0435 <code>dist<\/code>, \u043c\u044b \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c JSONP \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443. \u0422\u0430\u043a \u043a\u0430\u043a \u043d\u0430\u043c \u043d\u0435 \u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043d \u043f\u0443\u0442\u044c \u0434\u043e bundle.js, \u0442\u043e \u043c\u044b \u043d\u0435 \u0437\u043d\u0430\u0435\u043c \u043f\u0443\u0442\u044c, \u0433\u0434\u0435 \u043c\u043e\u0433\u0443\u0442 \u043b\u0435\u0436\u0430\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441\u044b (\u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438, JSON&#8217;\u044b, \u0448\u0440\u0438\u0444\u0442\u044b, 3d \u043e\u0431\u044a\u0435\u043a\u0442\u044b, &#8230;), \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0435\u0441\u0442\u044c \u043a\u0430\u043a \u043c\u0438\u043d\u0438\u043c\u0443\u043c 2 \u0440\u0435\u0448\u0435\u043d\u0438\u044f \u044d\u0442\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b:<\/p>\n<ol>\n<li>\n<p>\u0412\u044b\u043d\u0435\u0441\u0442\u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u043d\u0430 \u0432\u043d\u0435\u0448\u043d\u0438\u0439 CDN.<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043b\u043e\u0436\u0438\u0442\u044c \u0438\u0445 \u0432 <code>bundle.js<\/code>.<\/p>\n<\/li>\n<\/ol>\n<p>\u042f \u0432\u044b\u0431\u0440\u0430\u043b \u0432\u0442\u043e\u0440\u043e\u0439 \u043f\u0443\u0442\u044c, \u0441\u0442\u0440\u043e\u043a\u0438 5-13 \u0433\u043e\u0432\u043e\u0440\u044f\u0442 WebPack, \u0447\u0442\u043e \u0444\u0430\u0439\u043b\u044b \u0441 \u043f\u0435\u0440\u0435\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u044b\u043c\u0438 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f\u043c\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u0432\u043d\u0443\u0442\u0440\u0438 <code>bundle.js<\/code>.<\/p>\n<p>\u0412 package.json \u044f \u043e\u043f\u0438\u0441\u0430\u043b 3 \u0441\u043a\u0440\u0438\u043f\u0442\u0430:<\/p>\n<pre><code class=\"json\">  \"scripts\": {     \"build\": \"webpack --mode production\",     \"serve\": \"webpack serve --mode development\",     \"proto\": \"pbjs -t static-module -w es6 -o src\/proto\/drones.js  ..\/proto\/drones\/*.proto &amp;&amp; pbts -o src\/proto\/drones.d.ts src\/proto\/drones.js\"   }, <\/code><\/pre>\n<p><code>build<\/code> \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u0442 <code>bundle.js<\/code>, <code>serve<\/code> &#8212; \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043f\u043e\u0441\u043b\u0435 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 \u043a\u043e\u0434\u0430, \u0434\u043b\u044f \u043f\u0440\u043e\u0441\u043c\u043e\u0442\u0440\u0430 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u0432 \u043f\u0430\u043f\u043a\u0435 <code>public<\/code> \u043b\u0435\u0436\u0438\u0442 <code>index.html<\/code>, \u044d\u043c\u0443\u043b\u0438\u0440\u0443\u044e\u0449\u0438\u0439 \u0443\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u0443\u044e \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0441 \u0438\u0433\u0440\u043e\u0439. \u0422\u0430\u043a \u043a\u0430\u043a <code>Options<\/code>, <code>State<\/code> \u0438 <code>Actions<\/code> \u043f\u0440\u0438\u0445\u043e\u0434\u044f\u0442 \u0432 \u0432\u0438\u0434\u0435 \u043f\u0440\u043e\u0442\u043e\u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432, \u0442\u043e \u043d\u0430\u0434\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0437 \u043f\u0440\u043e\u0442\u043e\u0444\u0430\u0439\u043b\u043e\u0432 TypeScript \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0434\u043b\u044f \u0438\u0445 \u043f\u0430\u0440\u0441\u0438\u043d\u0433\u0430, \u0437\u0430 \u044d\u0442\u043e \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 \u0441\u043a\u0440\u0438\u043f\u0442 <code>proto<\/code>. <\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u0441\u044f \u0438\u043d\u0444\u0440\u0430\u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u0433\u043e\u0442\u043e\u0432\u0430, \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043d\u044f\u0442\u044c\u0441\u044f <code>index.ts<\/code>. \u0414\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0439 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b <a href=\"https:\/\/threejs.org\/\" rel=\"noopener noreferrer nofollow\">Three.js<\/a>, \u043d\u043e \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435 \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432\u0441\u0451 \u0447\u0442\u043e \u0443\u0433\u043e\u0434\u043d\u043e. \u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Three.js \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0446\u0435\u043d\u0443, \u043a\u0430\u043c\u0435\u0440\u0443, \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u0438\u0435 \u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u044b, \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u0438 \u043d\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 <code>requestAnimationFrame<\/code> \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0440\u0435\u043d\u0434\u0435\u0440\u0430 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u0441\u0446\u0435\u043d\u0443. \u0412 \u043a\u043e\u0434\u0435 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043c\u043d\u043e\u0433\u043e \u0440\u0443\u0442\u0438\u043d\u043d\u044b\u0445 \u0432\u0435\u0449\u0435\u0439 \u0438 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0435\u0433\u043e \u0441\u044e\u0434\u0430 \u043d\u0435 \u0445\u043e\u0447\u0435\u0442\u0441\u044f, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u0443\u044e\u0449\u0438\u043c\u0441\u044f \u0437\u0430\u0433\u043b\u044f\u043d\u0443\u0442\u044c \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0432 <a href=\"https:\/\/github.com\/bot-games\/battleships\/blob\/master\/player\/src\/index.ts\" rel=\"noopener noreferrer nofollow\">\u043a\u043e\u0434<\/a>. \u041f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c \u043a\u0430\u043a \u0432 \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u043e\u043c \u043c\u0435\u043c\u0435:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/590\/5b4\/a2f\/5905b4a2f67b6c96f8a46d4354a15bc4.png\" width=\"500\" height=\"407\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/590\/5b4\/a2f\/5905b4a2f67b6c96f8a46d4354a15bc4.png\"\/><\/figure>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0421\u0434\u0435\u043b\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0434\u043b\u044f \u0431\u043e\u0442\u0430 &#8212; \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u044c\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430, \u0435\u0441\u0442\u044c swagger.json, \u0438\u0437 \u043d\u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u043b\u044f \u043b\u044e\u0431\u043e\u0433\u043e \u044f\u0437\u044b\u043a\u0430 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0438\u043b\u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0435\u0433\u043e \u0440\u0443\u043a\u0430\u043c\u0438, \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043d\u0435 \u0442\u0430\u043a \u043c\u043d\u043e\u0433\u043e \u0438 \u043e\u043d\u0438 \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u043e \u043f\u0440\u043e\u0441\u0442\u044b\u0435. \u041f\u0440\u0438\u043c\u0435\u0440\u044b \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0432 \u043f\u0430\u043f\u043a\u0435 <code>bot-example<\/code> \u043a\u0430\u0436\u0434\u043e\u0439 \u0438\u0433\u0440\u044b.<\/p>\n<p>\u0415\u0441\u043b\u0438  \u0435\u0441\u0442\u044c \u0436\u0435\u043b\u0430\u043d\u0438\u0435 \u043f\u0440\u0438\u043d\u044f\u0442\u044c \u0443\u0447\u0430\u0441\u0442\u0438\u0435 \u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0438\u0433\u0440 \u0438\u043b\u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0435\u0433\u043e \u0431\u043e\u0442\u0430 \u0438\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430 \u044d\u0442\u0430 \u0442\u0435\u043c\u0430, \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0435 \u043a \u043d\u0430\u043c \u0432 <a href=\"https:\/\/t.me\/BotGamesFunRu\" rel=\"noopener noreferrer nofollow\">\u0422\u0435\u043b\u0435\u0433\u0440\u0430\u043c<\/a>, \u043d\u0430\u0441 \u0442\u0430\u043c \u043f\u043e\u043a\u0430 \u043d\u0435\u043c\u043d\u043e\u0433\u043e, \u0430 \u0438\u0433\u0440\u0430\u043c\u0438 \u0437\u0430\u043d\u0438\u043c\u0430\u0435\u043c\u0441\u044f \u044f \u0438 <a class=\"mention\" href=\"\/users\/karloid\">@Karloid<\/a>. \u041e\u043d \u043a\u0441\u0442\u0430\u0442\u0438 \u0435\u0449\u0451 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e <a href=\"https:\/\/github.com\/Karloid\/botCraft\" rel=\"noopener noreferrer nofollow\">RTS<\/a> \u043f\u0438\u043b\u0438\u0442.<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/740276\/\"> https:\/\/habr.com\/ru\/articles\/740276\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041f\u043e\u0447\u0442\u0438 \u0433\u043e\u0434 \u043d\u0430\u0437\u0430\u0434 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u043b \u043e \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 <a href=\"https:\/\/habr.com\/ru\/articles\/575190\/\" rel=\"noopener noreferrer nofollow\">HighLoad.Fun<\/a>, \u0433\u0434\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043e\u0440\u0435\u0432\u043d\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438 \u043a\u043e\u0434\u0430, \u043d\u043e \u043d\u0435 \u0443\u043f\u043e\u043c\u044f\u043d\u0443\u043b Bot-Games.Fun &#8212; \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0443, \u0433\u0434\u0435 \u043d\u0443\u0436\u043d\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u0435\u0433\u043e AI \u0431\u043e\u0442\u0430 \u0434\u043b\u044f \u0443\u0447\u0430\u0441\u0442\u0438\u044f \u0432 \u0438\u0433\u0440\u0430\u0445. \u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0435 \u043e\u0442\u043b\u0438\u0447\u0438\u0435 \u043e\u0442 \u0434\u0440\u0443\u0433\u0438\u0445 \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u044b\u0445 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c &#8212; \u043a\u043e\u0434 \u0431\u043e\u0442\u0430 \u043d\u0435 \u043d\u0430\u0434\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044c \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440, \u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u043d\u0430 \u0441\u0432\u043e\u0451\u043c \u0436\u0435\u043b\u0435\u0437\u0435, \u0447\u0442\u043e \u043e\u0442\u043a\u0440\u044b\u0432\u0430\u0435\u0442 \u0448\u0438\u0440\u043e\u0447\u0430\u0439\u0448\u0438\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043f\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u043c \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f\u043c \u0438 \u0437\u0430\u0442\u0440\u0430\u0447\u0435\u043d\u043d\u044b\u043c \u0440\u0435\u0441\u0443\u0440\u0441\u0430\u043c \u043d\u0430 \u043f\u0440\u043e\u0441\u0447\u0451\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0433\u043e \u0445\u043e\u0434\u0430. \u0410 \u0435\u0449\u0451 \u0432\u0441\u0435 \u0438\u0433\u0440\u044b \u0441 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u043c \u043a\u043e\u0434\u043e\u043c, \u043c\u043e\u0436\u043d\u043e \u0432\u043b\u0438\u044f\u0442\u044c \u043d\u0430 \u043f\u0440\u0430\u0432\u0438\u043b\u0430, \u0443\u043b\u0443\u0447\u0448\u0430\u0442\u044c \u043f\u043b\u0435\u0435\u0440, \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u044f\u0449\u0438\u0439 \u0438\u0433\u0440\u044b, \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0441\u0432\u043e\u044e \u0438\u0433\u0440\u0443, \u043a\u0430\u043a \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043f\u043e\u0434 \u043a\u0430\u0442\u043e\u043c, \u0430 \u0437\u0430\u043e\u0434\u043d\u043e \u0438 \u043f\u0440\u043e \u0430\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0443 \u043f\u0440\u043e\u0435\u043a\u0442\u0430.<\/p>\n<h2>\u0418\u0434\u0435\u044f<\/h2>\n<p>\u0412 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u0435 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u044e\u0442 \u0441\u0432\u043e\u0439 \u043a\u043e\u0434 \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0443 \u0438 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442 \u0441\u043f\u0438\u0441\u043a\u0438 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u043e\u0432 \u043d\u0430 \u0438\u0433\u0440\u0443, \u0430 \u043f\u043e\u0442\u043e\u043c \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 \u0441\u0435\u0440\u0432\u0435\u0440 \u0438 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u0432 (\u0431\u043e\u0442\u043e\u0432) \u0434\u043b\u044f \u0441\u0438\u043c\u0443\u043b\u044f\u0446\u0438\u0438. \u0421\u0440\u0435\u0434\u0438 \u0444\u0430\u0442\u0430\u043b\u044c\u043d\u044b\u0445 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u043e\u0432 \u0437\u0434\u0435\u0441\u044c \u044f \u0432\u0438\u0436\u0443 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u044f \u043f\u043e \u044f\u0437\u044b\u043a\u0430\u043c \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f\/\u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u043c, \u044d\u0442\u043e \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435 \u043e\u0431\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Docker&#8217;\u0430 \u0438 \u043e\u0442\u043a\u0440\u044b\u0442\u043e\u0433\u043e \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u0430, \u043d\u043e \u0441\u0430\u043c\u043e\u0435 \u0433\u043b\u0430\u0432\u043d\u043e\u0435, \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u0436\u0435\u043b\u0435\u0437\u0430 \u0441\u0438\u043b\u044c\u043d\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u044b: \u043c\u0430\u043b\u043e \u041e\u0417\u0423, \u043d\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u043a \u0432\u0438\u0434\u0435\u043e\u043a\u0430\u0440\u0442\u0435, &#8230; \u041c\u043d\u0435 \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043b\u044e\u0431\u043e\u0439 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u043c\u043e\u0433 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u043d\u043e\u0435 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0442\u043e\u0440\u0430\u043c\u0438 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u043f\u0440\u0438\u0431\u043b\u0438\u0436\u0430\u044f \u0442\u0435\u043f\u043b\u043e\u0432\u0443\u044e \u0441\u043c\u0435\u0440\u0442\u044c \u0432\u0441\u0435\u043b\u0435\u043d\u043d\u043e\u0439, \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043a\u0430\u0440\u043c\u0430\u043d\u043d\u043e\u0433\u043e \u0414\u0426 \u0443 \u043c\u0435\u043d\u044f \u043a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e \u043d\u0435\u0442, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0434\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438 \u0441\u0430\u043c\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043b\u0438 \u0441\u0432\u043e\u0438\u0445 \u0431\u043e\u0442\u043e\u0432 \u043d\u0430 \u0441\u0432\u043e\u0451\u043c \u0436\u0435\u043b\u0435\u0437\u0435. \u0412 \u0438\u0442\u043e\u0433\u0435 \u0441\u0445\u0435\u043c\u0430 \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<figure class=\"bordered full-width\"><\/figure>\n<p>\u041a\u043b\u0438\u0435\u043d\u0442\u044b (\u0431\u043e\u0442\u044b) \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0430\u044e\u0442\u0441\u044f \u043a \u0441\u0435\u0440\u0432\u0435\u0440\u0443 \u0438 \u043f\u043e\u043f\u0430\u0434\u0430\u044e\u0442 \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0440\u0430\u0437 \u0432 N \u043c\u0438\u043d\u0443\u0442 (\u0432 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 10) \u0431\u043e\u0442\u044b \u0440\u0430\u0437\u0431\u0438\u0432\u0430\u044e\u0442\u0441\u044f \u043d\u0430 \u0433\u0440\u0443\u043f\u043f\u044b \u0438 \u0441\u043e\u0437\u0434\u0430\u044e\u0442\u0441\u044f \u0438\u0433\u0440\u044b. \u041f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0438\u0433\u0440 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0442\u0441\u044f \u043f\u043e\u0431\u0435\u0434\u0438\u0442\u0435\u043b\u0438 \u0438 \u043d\u0430\u0447\u0438\u0441\u043b\u044f\u044e\u0442\u0441\u044f \u043e\u0447\u043a\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f\u00a0<a href=\"https:\/\/en.wikipedia.org\/wiki\/Elo_rating_system\" rel=\"noopener noreferrer nofollow\">\u0440\u0435\u0439\u0442\u0438\u043d\u0433 ELO<\/a>.<\/p>\n<p>\u0411\u043e\u0442\u044b \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0442 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u043f\u043e JSONRPC \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u043c\u0443 API. \u0414\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044e \u043a \u043a\u0430\u0436\u0434\u043e\u0439 \u0438\u0433\u0440\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432 Swagger&#8217;\u0435 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435.<\/p>\n<h2>\u0410\u0440\u0445\u0438\u0442\u0435\u043a\u0442\u0443\u0440\u0430<\/h2>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u044f \u043d\u0430\u0434 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u043e\u0439, \u044f \u043f\u043e\u0441\u0442\u0430\u0432\u0438\u043b \u0441\u0435\u0431\u0435 \u0437\u0430\u0434\u0430\u0447\u0443 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0435\u0451 \u0442\u0430\u043a\u043e\u0439, \u0447\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0439 \u0438\u0433\u0440\u044b \u0431\u044b\u043b\u043e \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u043c, \u0430 \u0432\u0441\u044f \u0440\u0443\u0442\u0438\u043d\u0430 \u0431\u044b\u043b\u0430 \u0441\u043a\u0440\u044b\u0442\u0430 \u043f\u043e\u0434 \u043a\u0430\u043f\u043e\u0442\u043e\u043c \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b. \u0412\u0442\u043e\u0440\u0430\u044f \u0432\u0430\u0436\u043d\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438 \u0437\u0430\u043a\u043b\u044e\u0447\u0430\u043b\u0430\u0441\u044c \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043b\u044e\u0431\u043e\u0439 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a \u043c\u043e\u0433 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043c\u0438\u043d\u0438\u0441\u0435\u0440\u0432\u0435\u0440 (Local Runner) \u043d\u0430 \u0441\u0432\u043e\u0451\u043c \u0436\u0435\u043b\u0435\u0437\u0435 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f \u043d\u0435\u0439\u0440\u043e\u043d\u043e\u043a, \u043f\u0440\u0438 \u044d\u0442\u043e\u043c \u043a\u043e\u0434 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438 \u0432 Local Runner&#8217;\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u043c, \u0432\u043a\u043b\u044e\u0447\u0430\u044f \u043f\u043b\u0435\u0435\u0440 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u0442 \u0437\u0430\u0432\u0435\u0440\u0448\u0438\u0432\u0448\u0438\u0435\u0441\u044f \u0438\u0433\u0440\u044b. \u041f\u043e\u0441\u043b\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439 \u044f \u043f\u0440\u0438\u0448\u0451\u043b \u043a \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0439 \u0441\u0445\u0435\u043c\u0435:<\/p>\n<figure class=\"bordered full-width\"><\/figure>\n<p>\u0414\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0438\u0433\u0440\u044b \u043d\u0430\u0434\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <code>Game<\/code>, \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u044c \u0445\u0435\u043d\u0434\u043b\u0435\u0440\u044b RPC \u0438 \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u043b\u0435\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u0438\u0433\u0440\u0443 \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435. \u0417\u0430\u0442\u0435\u043c \u0438\u0433\u0440\u0430 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043e\u0431\u044a\u0435\u043a\u0442\u0430 <code>GameManager<\/code> \u0432\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u043b\u0438\u0431\u043e \u0432 \u043e\u0431\u0449\u0438\u0439 \u0441\u0435\u0440\u0432\u0435\u0440, \u043b\u0438\u0431\u043e \u0432 LocalRunner. <code>GameManager<\/code> \u0442\u0430\u043a\u0436\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 <code>Storage<\/code> &#8212; \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0445\u043e\u0434\u0430 \u0438\u0433\u0440\u044b \u0438 <code>Scheduler<\/code>&#8212; \u0434\u043b\u044f \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u0438\u0433\u0440, \u043d\u043e \u043f\u0440\u043e \u043d\u0438\u0445 \u0434\u0443\u043c\u0430\u0442\u044c \u043d\u0435 \u043d\u0430\u0434\u043e, \u043d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u043e\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0433\u043e\u0442\u043e\u0432\u044b\u0435.<\/p>\n<p>\u0412\u0441\u0435 \u0438\u0433\u0440\u044b \u043d\u0430 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0435 \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u044b\u0435, \u0442.\u0435. \u043a\u0430\u0436\u0434\u0430\u044f \u0438\u0433\u0440\u0430 \u0438\u043c\u0435\u0435\u0442 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 &#8212; <code>State<\/code> \u0438 \u043d\u0430\u0431\u043e\u0440 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 &#8212; <code>Actions<\/code>, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0443\u0447\u0430\u0441\u0442\u043d\u0438\u043a\u0438 \u043c\u0435\u043d\u044f\u044e\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435. \u0412\u0441\u0435 \u0438\u0433\u0440\u044b \u0440\u0430\u0437\u0431\u0438\u0442\u044b \u043d\u0430 \u0445\u043e\u0434\u044b &#8212; <code>Ticks<\/code>. \u0412 \u0438\u0433\u0440\u0430\u0445 \u0435\u0441\u0442\u044c \u043a\u0430\u043a \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u043c\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b, \u0442\u0430\u043a \u0438 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435. \u0414\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0438 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442 \u0435\u0441\u0442\u044c <code>Options<\/code>, \u043e\u043d \u043d\u0443\u0436\u0435\u043d \u0447\u0442\u043e\u0431\u044b \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u0442\u044c \u043e\u0431\u044a\u0451\u043c \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0445 \u0434\u043b\u044f \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438\u0441\u0442\u043e\u0440\u0438\u0438 \u0438\u0433\u0440\u044b, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u043a\u0430\u0436\u0434\u043e\u043c <code>Tick<\/code>&#8216;\u0435  \u0442\u043e, \u0447\u0442\u043e \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f, \u0430 <code>State<\/code> \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u0442\u0441\u044f \u043a\u0430\u0436\u0434\u044b\u0439 \u0445\u043e\u0434. \u0421 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u0431\u043e\u0442\u0430 \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0438\u0433\u0440\u044b \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<figure class=\"bordered full-width\"><\/figure>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0438\u0433\u0440\u044b &#171;\u041c\u043e\u0440\u0441\u043a\u043e\u0439 \u0431\u043e\u0439&#187;, \u044f \u043f\u043e\u043a\u0430\u0436\u0443 \u043a\u0430\u043a \u043b\u0435\u0433\u043a\u043e \u043f\u0438\u0441\u0430\u0442\u044c \u0438\u0433\u0440\u044b. \u041f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0438 \u0438\u0433\u0440\u044b \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u044b \u043d\u0430 Go, \u0430 \u043f\u043b\u0435\u0435\u0440 &#8212; \u043d\u0430 TypeScript + WebPack + Three.js \u0434\u043b\u044f \u0433\u0440\u0430\u0444\u0438\u043a\u0438. \u0412\u0441\u0435 \u0438\u0441\u0445\u043e\u0434\u043d\u0438\u043a\u0438 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u043d\u0430 <a href=\"https:\/\/github.com\/bot-games\" rel=\"noopener noreferrer nofollow\">GitHub<\/a>.<\/p>\n<h2>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043b\u043e\u0433\u0438\u043a\u0438 \u0438\u0433\u0440\u044b<\/h2>\n<p>\u041a\u0430\u043a \u044f \u0433\u043e\u0432\u043e\u0440\u0438\u043b \u0432\u044b\u0448\u0435, \u0432\u0441\u0451 \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u044d\u0442\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 <code>Game<\/code>:<\/p>\n<pre><code class=\"go\">type Game interface { Init() (options proto.Message, state proto.Message, waitUsers uint8, gameData any) CheckAction(tickInfo *TickInfo, action proto.Message) error ApplyActions(tickInfo *TickInfo, actions []Action) *TickResult SmartGuyTurn(tickInfo *TickInfo) proto.Message }<\/code><\/pre>\n<p>\u041c\u0435\u0442\u043e\u0434 Init \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 4 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435:<\/p>\n<ol>\n<li>\n<p><code>Options<\/code> &#8212; \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b \u0438 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b.<\/p>\n<\/li>\n<li>\n<p><code>State<\/code> &#8212; \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0438\u0433\u0440\u044b \u043d\u0430 0 \u0445\u043e\u0434\u0443.<\/p>\n<\/li>\n<li>\n<p>\u0411\u0438\u0442\u043e\u0432\u0430\u044f \u043c\u0430\u0441\u043a\u0430 \u0438\u0433\u0440\u043e\u043a\u043e\u0432, \u043e\u0442 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f \u0445\u043e\u0434 (\u0431\u0438\u0442 \u0432\u043e\u0437\u0432\u0435\u0434\u0451\u043d &#8212; \u0436\u0434\u0451\u043c \u0445\u043e\u0434, \u043d\u0435\u0442 &#8212; \u043e\u0442 \u0438\u0433\u0440\u043e\u043a\u0430 \u043d\u0435 \u0442\u0435\u043a\u0443\u0449\u0435\u043c \u0445\u043e\u0434\u0435 \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f)<\/p>\n<\/li>\n<li>\n<p>\u041b\u044e\u0431\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0433\u0443\u0442 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0434\u043b\u044f \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0430 \u0438\u0433\u0440\u044b. \u0412 \u0441\u043b\u0443\u0447\u0430\u0435 \u0438\u0433\u0440\u044b &#171;\u0414\u0440\u043e\u043d\u044b&#187;, \u0442\u0430\u043c \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442 <code>world<\/code> \u0434\u043b\u044f \u0441\u0438\u043c\u0443\u043b\u044f\u0446\u0438\u0438 \u0444\u0438\u0437\u0438\u043a\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 Box2d.<\/p>\n<\/li>\n<\/ol>\n<p>\u041c\u0435\u0442\u043e\u0434 <code>CheckAction<\/code> \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0447\u0442\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u043e\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0430\u043b\u0438\u0434\u043d\u044b\u043c \u0438 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u043e \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 <code>ApplyActions<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439, \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u0438\u0437\u043c\u0435\u043d\u044f\u0435\u0442 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u0442, \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u0430\u0441\u044c \u043b\u0438 \u0438\u0433\u0440\u0430 \u0438\u043b\u0438 \u043d\u0435\u0442.<\/p>\n<p>\u041f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u0430 \u0438\u043c\u0435\u0435\u0442 debug \u0440\u0435\u0436\u0438\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0438\u0433\u0440\u0430\u0442\u044c \u043d\u0435 \u043d\u0430 \u0440\u0435\u0439\u0442\u0438\u043d\u0433 \u0441\u043e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u044b\u043c \u0441\u043e\u043f\u0435\u0440\u043d\u0438\u043a\u043e\u043c &#8212; SmartGuy&#8217;\u0435\u043c \u0431\u0435\u0437 \u0443\u0447\u0451\u0442\u0430 \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u043e\u0432. \u0421 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 <code>SmartGuyTurn<\/code> \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0442\u0441\u044f \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0434\u0435\u0444\u043e\u043b\u0442\u043d\u043e\u0433\u043e \u0438\u0433\u0440\u043e\u043a\u0430.<\/p>\n<h3>Protobuf<\/h3>\n<p>\u0418\u0433\u0440\u0430 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u0430\u044f \u0438 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043e\u043f\u0446\u0438\u0439 \u0438 \u0441\u0442\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0445 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0432 \u043d\u0435\u0439 \u043d\u0435\u0442, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 <code>Options<\/code> \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u0443\u0441\u0442\u044b\u043c:<\/p>\n<pre><code class=\"perl\">message Options {}<\/code><\/pre>\n<p>\u0418\u0433\u0440\u043e\u0432\u043e\u0435 \u043f\u043e\u043b\u0435 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0438\u0437 2 \u043c\u0430\u0442\u0440\u0438\u0446 10\u044510, \u0432 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u044b \u043a\u043e\u0440\u0430\u0431\u043b\u0438. \u0421\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0435\u043d\u043d\u043e \u043f\u0440\u043e\u0442\u043e\u0444\u0430\u0439\u043b <code>State<\/code> \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"perl\">message State {   repeated Cell field1 = 1;   repeated Cell field2 = 2; }  enum Cell {   EMPTY = 0;   SHIP = 1;   MISSED = 2;   GOT = 3; }<\/code><\/pre>\n<p>\u0414\u0435\u0439\u0441\u0442\u0432\u0438\u044f (<code>Action<\/code>) \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c 3\u0445 \u0432\u0438\u0434\u043e\u0432:<\/p>\n<ol>\n<li>\n<p><code>ActionSkip<\/code> &#8212;  \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u0442\u044c.<\/p>\n<\/li>\n<li>\n<p><code>ActionSetup<\/code> &#8212; \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u0430\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0430 \u043a\u043e\u0440\u0430\u0431\u043b\u0435\u0439.<\/p>\n<\/li>\n<li>\n<p><code>ActionFire<\/code> &#8212; \u0430\u0442\u0430\u043a\u0430 \u043a\u043b\u0435\u0442\u043a\u0438.<\/p>\n<\/li>\n<\/ol>\n<p>\u0412 \u043f\u0440\u043e\u0442\u043e\u0444\u0430\u0439\u043b\u0435 \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"perl\">message Action {   oneof data {     ActionSkip skip = 1;     ActionSetup setup = 2;     ActionFire fire = 3;   } }  message ActionSkip {}  message ActionSetup {   message Ship {     string coordinate = 1;     bool vertical = 2;   }    Ship shipL4N1 = 1;   Ship shipL3N1 = 2;   Ship shipL3N2 = 3;   Ship shipL2N1 = 4;   Ship shipL2N2 = 5;   Ship shipL2N3 = 6;   Ship shipL1N1 = 7;   Ship shipL1N2 = 8;   Ship shipL1N3 = 9;   Ship shipL1N4 = 10; }  message ActionFire {   string coordinate = 1; }<\/code><\/pre>\n<p>\u041e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0444\u043e\u0440\u043c\u0430\u0442\u0435 \u0431\u0443\u043a\u0432\u0430-\u0446\u0438\u0444\u0440\u0430, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 <code>A1<\/code>.<\/p>\n<h3>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430 Game<\/h3>\n<p>\u041c\u0435\u0442\u043e\u0434 <code>Init<\/code> \u0442\u0440\u0438\u0432\u0438\u0430\u043b\u0435\u043d: \u0441\u043e\u0437\u0434\u0430\u0451\u043c \u0438 \u0437\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c <code>Options<\/code> \u0438 <code>State<\/code>, \u0442\u0430\u043a \u043a\u0430\u043a. \u0438\u0433\u0440\u043e\u043a\u043e\u0432 \u0434\u0432\u043e\u0435 \u0438 \u043e\u0442 \u043e\u0431\u043e\u0438\u0445 \u043e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f \u0445\u043e\u0434, \u0442\u043e \u0432\u043e\u0437\u0432\u043e\u0434\u0438\u043c \u043d\u0443\u043b\u0435\u0432\u043e\u0439 \u0438 \u043f\u0435\u0440\u0432\u044b\u0439 \u0431\u0438\u0442\u044b, \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c 3 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0435\u0433\u043e. \u041d\u0438\u043a\u0430\u043a\u0438\u0445 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0435 \u0438\u0433\u0440\u044b \u043d\u0435 \u043d\u0430\u0434\u043e, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0447\u0435\u0442\u0432\u0451\u0440\u0442\u044b\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435\u043c \u0431\u0443\u0434\u0435\u0442 nil.<\/p>\n<pre><code class=\"go\">func (b Battleships) Init() (proto.Message, proto.Message, uint8, any) { return &amp;pb.Options{}, &amp;pb.State{ Field1: generateRandomField(), Field2: generateRandomField(), }, 3, nil }<\/code><\/pre>\n<p>\u0412 \u043c\u0435\u0442\u043e\u0434\u0435 <code>CheckAction<\/code> \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0433\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0447\u0442\u043e \u044d\u0442\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435 \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u043e, \u0430 \u0435\u0441\u043b\u0438 \u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u0442\u043e \u0438 \u0438\u0445 \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u044c:<\/p>\n<pre><code class=\"go\">func (b Battleships) CheckAction(tickInfo *manager.TickInfo, action proto.Message) error { switch curAction := action.(*pb.Action).Data.(type) { case *pb.Action_Skip: if !GetActions(tickInfo)[ActionSkip] { return manager.ErrInvalidAction } return nil  case *pb.Action_Setup: return b.CheckActionSetup(tickInfo, curAction.Setup)  case *pb.Action_Fire: return b.CheckActionFire(tickInfo, curAction.Fire)  default: panic(\"invalid action\") } }  ...  func (Battleships) CheckActionFire(tickInfo *manager.TickInfo, fire *pb.ActionFire) error { if !GetActions(tickInfo)[ActionFire] { return manager.ErrInvalidAction }  _, _, err := CoordinateToXY(fire.Coordinate) return err }<\/code><\/pre>\n<p>\u0412 \u043c\u0435\u0442\u043e\u0434\u0435 <code>ApplyActions<\/code> \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u043c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u043d\u0430 \u0442\u0435\u043a\u0443\u0449\u0438\u0439 <code>State<\/code> \u0438 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c \u0437\u0430\u043a\u043e\u043d\u0447\u0435\u043d\u0430 \u0438\u0433\u0440\u0430 \u0438\u043b\u0438 \u043d\u0435\u0442. \u0412\u0441\u044f \u0447\u0451\u0440\u043d\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u043f\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0442\u0430\u0439\u043c\u0430\u0443\u0442\u043e\u0432 \u043e\u0442 \u043a\u043b\u0438\u0435\u043d\u0442\u0430, \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u044b\u0445 Tick&#8217;\u043e\u0432 \u0438 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0438\u0445 \u0432 \u0411\u0414, &#8230;,  \u043b\u0435\u0436\u0438\u0442 \u043d\u0430 <code>GameManager<\/code>, \u0432\u043d\u0443\u0442\u0440\u0438 \u0438\u0433\u0440\u044b \u043e\u0431 \u044d\u0442\u043e\u043c \u0431\u0435\u0441\u043f\u043e\u043a\u043e\u0438\u0442\u044c\u0441\u044f \u043d\u0435 \u043d\u0430\u0434\u043e. \u041f\u043b\u044e\u0441 \u0432\u0430\u043b\u0438\u0434\u043d\u043e\u0441\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0431\u044b\u043b\u0430 \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u043d\u0430 \u0432 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u043c \u043c\u0435\u0442\u043e\u0434\u0435. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u043d\u0430\u0434\u043e \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0443\u043a\u0430\u0437\u0430\u0442\u0435\u043b\u044c \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <\/p>\n<pre><code class=\"go\">type TickResult struct { GameFinished    bool Winner          uint8 NewState        proto.Message NextTurnPlayers uint8 }<\/code><\/pre>\n<p><code>GameFinished<\/code> \u0440\u0430\u0432\u0435\u043d <code>true<\/code>, \u0435\u0441\u043b\u0438 \u0438\u0433\u0440\u0430 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0430, <code>Winner<\/code> &#8212; \u043d\u043e\u043c\u0435\u0440 \u043f\u043e\u0431\u0435\u0434\u0438\u0432\u0448\u0435\u0433\u043e \u0438\u0433\u0440\u043e\u043a\u0430 \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 1, \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0438\u0447\u044c\u0435\u0439 &#8212; 0, <code>NewState<\/code> &#8212; \u043e\u0431\u043d\u043e\u0432\u043b\u0451\u043d\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435, <code>NextTurnPlayers<\/code> &#8212; \u043c\u0430\u0441\u043a\u0430 \u0438\u0433\u0440\u043e\u043a\u043e\u0432, \u043e\u0442 \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043e\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044f \u0445\u043e\u0434, \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u0442\u0430\u043a\u043e\u0439 \u0436\u0435 \u043a\u0430\u043a \u0438 \u0432 <code>Init<\/code>. \u041d\u0438\u0436\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f:<\/p>\n<pre><code class=\"go\">func (b Battleships) ApplyActions(tickInfo *manager.TickInfo, actions []manager.Action) *manager.TickResult { for _, action := range actions { tickInfo.CurUid = action.Uid  switch curAction := action.Action.(*pb.Action).Data.(type) { case *pb.Action_Skip: \/\/ Do nothing  case *pb.Action_Setup: tickInfo.State = b.DoActionSetup(tickInfo, curAction.Setup)  case *pb.Action_Fire: tickInfo.State = b.DoActionFire(tickInfo, curAction.Fire)  default: panic(\"invalid action\") } }  res := &amp;manager.TickResult{ NewState: tickInfo.State, }  if finished, winner := isGameFinished(tickInfo); finished { res.GameFinished = finished res.Winner = winner } else { res.NextTurnPlayers = 3 }  return res }  ...  func (Battleships) DoActionFire(tickInfo *manager.TickInfo, fire *pb.ActionFire) proto.Message { x, y, _ := CoordinateToXY(fire.Coordinate)  f := GetField(tickInfo, false) switch f[y*10+x] { case pb.Cell_EMPTY: f[y*10+x] = pb.Cell_MISSED case pb.Cell_SHIP: f[y*10+x] = pb.Cell_GOT }  return tickInfo.State }<\/code><\/pre>\n<p>\u0418 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u043c\u0435\u0442\u043e\u0434 <code>SmartGuyTurn<\/code>. SmartGuy \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u043d\u0435 \u043e\u0447\u0435\u043d\u044c smart \u0438 \u0431\u0443\u0434\u0435\u0442 \u0441\u0442\u0440\u0435\u043b\u044f\u0442\u044c \u0432 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u0443\u044e \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u0443\u044e \u043a\u043b\u0435\u0442\u043a\u0443:<\/p>\n<pre><code class=\"go\">func (Battleships) SmartGuyTurn(tickInfo *manager.TickInfo) proto.Message { actions := GetActions(tickInfo)  if actions[ActionFire] { var availableCoords []string for y := 0; y &lt; 10; y++ { for x := 0; x &lt; 10; x++ { c := tickInfo.State.(*pb.State).Field1[y*10+x] if c == pb.Cell_EMPTY || c == pb.Cell_SHIP { availableCoords = append(availableCoords, fmt.Sprintf(\"%s%d\", string(rune('A'+y)), x)) } } }  return &amp;pb.Action{Data: &amp;pb.Action_Fire{Fire: &amp;pb.ActionFire{Coordinate: availableCoords[rand.Intn(len(availableCoords))]}}}  } else if actions[ActionSkip] { return &amp;pb.Action{Data: &amp;pb.Action_Skip{}}  } else { panic(\"no known actions\") } }<\/code><\/pre>\n<p>\u0412\u043e\u0442 \u0432 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u0435 \u0438 \u0432\u0441\u0451, \u043b\u043e\u0433\u0438\u043a\u0430 \u0438\u0433\u0440\u044b \u0433\u043e\u0442\u043e\u0432\u0430, \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c API.<\/p>\n<h3>API<\/h3>\n<p>\u041e\u0431\u044a\u0435\u043a\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u044e\u0449\u0438\u0439 API \u0434\u043e\u043b\u0436\u0435\u043d \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u043e\u0432\u0430\u0442\u044c \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0443<\/p>\n<pre><code class=\"go\">type GameApi interface { http.Handler GetSwagger(ctx context.Context) *openapi.OpenApi GetPlayerHandler() http.Handler }<\/code><\/pre>\n<p>\u0414\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 API \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/github.com\/go-qbit\/rpc\" rel=\"noopener noreferrer nofollow\">github.com\/go-qbit\/rpc<\/a>, \u043e\u043d\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043b\u0435\u0433\u043a\u043e \u043e\u043f\u0438\u0441\u0430\u0442\u044c JSONRPC \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0439 \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b \u0438 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 <code>swagger.json<\/code> \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438, \u043f\u0440\u0438 \u044d\u0442\u043e \u043d\u0438\u0447\u0435\u0433\u043e <\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-405859","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/405859","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=405859"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/405859\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=405859"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=405859"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=405859"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}