#0 (Правка: 26 мар 2023, 10:11)
23:43, 10 мар 2023
Цель конкурса
Написать AI-клиента, который в матчах с другими AI победит соперника. Клиент можно писать на любом языке. Ниже документация по API и примеры клиентов на Python и C#.
Онлайн карта
FAQ
+ Показать
− Скрыть
+ Что нужно для участия в конкурсе?
− Скрыть
Нужно написать программу, которая управляет танком. Потом сообщить об этом в этой теме. Мы договоримся о времени проведения матча.
+ На каком языке писать программу?
− Скрыть
На любом удобном вам. Примеры программ на Python и C# даны ниже. Если нужно, я могу попробовать написать пример ещё на каком-нибудь.
− Скрыть
Сервер запускать не надо. Он уже запущен. Всё, что вам нужно, это делать к нему запросы и получать ответы.
+ Для игры мне нужно подключение к интернету?
− Скрыть
Да, нужно, так как это клиент-серверная игра.
+ А где UI / визуализация?
− Скрыть
В нульпосте есть простенький UI, отображающий демо-уровень.
Для просмотра собственной сессии, нужно вбить Id танка в URL:
https://gdtanks.pythonanywhere.com/{tank_id}
Но, вообще, для написания программы, UI вам не нужен. Кроме того, вы можете самостоятельно написать свой UI, благо все необходимые данные можно получить от сервера.
− Скрыть
Механика игры похожа на "Танчики на Денди".
Танчики сражаются между собой на карте размером 20*20 клеток (возможно, размер будет увеличен). Кто уничтожил всех противников - тот победил.
Танчики могут:
- перемещаться по направлению движения со скоростью одна клетка за запрос
- поворачиваться на 90° по часовой и против часовой стрелки
- стрелять по направлению движения
− Скрыть
Все противники на карте уничтожены.
+ Каковы условия поражения?
− Скрыть
Характеристика "hp" вашего танка становится равной нулю.
+ Как игра устроена на техническом уровне?
− Скрыть
1. Все данные об игре, карте и танках хранятся на сервере.
Игрок делает HTTP API запрос к серверу и получает предназначенные ему данные.
2. Проанализировав полученные данные, игрок решает, какое действие ему выгоднее всего выполнить (например, переместить танк или сделать выстрел).
4. Затем игрок выполняет это действие, отправив HTTP API запрос к серверу.
5. Сервер принимает этот запрос, обрабатывает его и меняет соответствующие значение (например, координаты танка).
6. Игрок возвращается к п.1 и выполняет этот цикл по кругу до тех пор, пока на карте не останется противников (случай "победы") или его здоровье не упадёт до нуля (случай "поражения").
+ Какие у танка характеристики?
− Скрыть
У танка 5 характеристик:
- здоровье (hp)
- количество снарядов (shells)
- сила атаки (attack)
- дальность выстрела (shooting_distance)
- радиус обзора (visibility_range)
На старте игры игроку даётся 10 очков, которые он может распределить по своему усмотрению. В процессе игры он может улучшать эти характеристики, подбирая бонусы.
+ Количество снарядов бесконечное?
− Скрыть
Нет. Количество снарядов игрок выбирает сам в начале игры, вкладывая очки в характеристику "shells"
+ Как пополнять боезапас?
− Скрыть
Подбирать бонусы и тратить и "вкладывать" их в характеристику "shells"
+ Как получать бонусы (или очки)?
− Скрыть
Бонусы рандомно спаунятся на карте. Их можно подбирать, каждый бонус даёт возможность увеличить любую характеристику танка на единицу.
Чтобы узнать, есть ли поблизости бонусы, нужно позвать метод POST http://gdtanks.pythonanywhere.com/tank/{tank_id}/info
В ответе будет массив "bonuses", содержащий координаты бонусов.
Чтобы узнать, есть ли непотраченные бонусы, нужно позвать метод POST http://gdtanks.pythonanywhere.com/tank/{tank_id}/info
В ответе будет переменная "points_available", содержащая количество очков, которые можно потратить.
Чтобы прибавить бонус к желаемой характеристике, нужно позвать АПИ: POST http://gdtanks.pythonanywhere.com/{tank_id}/{paramerer_name}
где paramerer_name — имя характеристики танка, к которой нужно добавить бонус, например "hp"
На отладочной карте бонусы условно обозначаются вопросиками:
+ Какова скорость движения снаряда?
− Скрыть
Скорость движения снаряда — мгновенная. То есть любые попытки уклониться от летящего снаряда бесполезны.
+ Какие есть типы объектов на карте?
− Скрыть
Пустая клетка — проходимый и пробиваемый объект
Вода — непроходимый, но пробиваемый объеки
Блок — непроходимый и непробиваемый объект
+ Я буду видеть всю карту во время игры?
− Скрыть
Да, игрок видит всю карту, но карта покрыта туманом войны за исключением "радиуса видимости" вокруг танка. Игрок задаёт значение этой характеристики на старте и может прокачать её в процессе игры, собирая бонусы.
+ Какие правила турнирной части?
− Скрыть
Все игроки играют друг с другом 1 на 1.
Возможен вариант все против всех на одной карте, но это опционально и немного сложнее для реализации.
+ Какая архитектура у этой системы?
− Скрыть
Клиент-серверная.
+ Какие есть режимы игры?
− Скрыть
Предусмотрены два режима игры:
- PvE: Игрок против ботов. Бесконечная игра для отладки алгоритма своего AI
- PvP: Игроки против Игроков. В назначенное время один из игроков создаёт сессию, другой игрок к ней подключается и начинается игра, как в PVE режиме, только с живыми игроками, до тех пор, пока не останется единственный участник. он и будет считаться победителем.
Web API для управления танком
+ Показать
− Скрыть
1. Сначала нужно создать игру. Для этого дёргается: POST https://www.pythonanywhere.com/game/create
В теле запроса нужно указать желаемое количество ботов: {"bots": 5}
В ответе возвращается уникальный идентификатор "game_id"
2. Затем нужно создать танк: POST https://www.pythonanywhere.com/tank/create
В теле запроса нужно передать полученный "game_id" и желаемые характеристики танка (сумма всех характеристик не должен превышать 10 баллов):
{'game_id': '92e32caa',
'hp': 2,
'shells': 2,
'attack': 2,
'shooting_distance': 2,
'visibility_range': 2}
В ответе вернётся "tank_id"
3. (Опционально) Если планируется PvP игра, то другие игроки выполняют пункт 2 и каждый получает свой персональный "tank_id"
4. Когда все танки созданы, а участники готовы, дёргается АПИ, сигнализирующее о начале игры: POST https://www.pythonanywhere.com/game/{GAME_ID}/start
5. Начинается непосредственно игра, игроки управляют танком через метод: POST https://www.pythonanywhere.com/tank/{TANK_ID}/{ACTION}/ где:
- {TANK_ID} – tank_id, полученный ранее
- {ACTION} – одно из значений:
- "info" — информация о координатах и характеристиках танка
- "map" — получение карты
- "move" – перемещение танка на одну клетку
- "cw" — поворот танка на 90° по часовой стрелке
- "ccw" — поворот танка на 90° против часовой стрелки
- "shoot" — выстрел
- "{TANK_PARAMETER}" — потратить подобранный бонус на любую характеристику танка (см. пункт 2 для списка характеристик)
Пример базового клиента на Python
+ Показать
− Скрыть
import requests
URL = 'https://gdtanks.pythonanywhere.com' # Базовый URL для API запросов
DIRECTIONS = {'left': (-1, 0), 'right': (1, 0), 'up': (0, -1), 'down': (0, 1)} # Направления движения танка
# 1. Создаём игру
game_id = requests.post(url=f'{URL}/game/create', json={'bots': 5}).json()['game_id']
# 2. Создаём танк
tank_id = requests.post(url=f'{URL}/tank/create', json={'game_id': game_id, 'hp': 2, 'shells': 2, 'attack': 2, 'shooting_distance': 2, 'visibility_range': 2}).json()['tank_id']
# 3. Стартуем игру
requests.post(url=f'{URL}/game/{game_id}/start')
# 4. Запрашиваем карту один раз в начале игры
map_ = requests.post(url=f'{URL}/tank/{tank_id}/map').json()['map']
# Игра началась. Запускаем бесконечный цикл управления танком
while True:
# Получаем текущую информацию по танку
info = requests.post(url=f'{URL}/tank/{tank_id}/info')
if info.status_code == 400:
break # нас подбили, выходим из цикла
info = info.json()
# Перемещаем танк при условии, что есть свободная клетка для перемещения
x = info['x'] + DIRECTIONS[info['direction']][0]
y = info['y'] + DIRECTIONS[info['direction']][1]
if map_[y][x] == ' ':
print(f'Двигаем танк в координаты [{x} {y}]')
requests.post(url=f'{URL}/tank/{tank_id}/move')
# Если свободной клетки для перемещения нет, вращаем танк
else:
print('Вращаем танк')
requests.post(url=f'{URL}/tank/{tank_id}/cw')
print('Поражение :(')
Пример базового клиента на C#
+ Показать
− Скрыть
using System;
using System.Collections.Generic;
using System.Net.Http.Json;
using System.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
string URL = "http://gdtanks.pythonanywhere.com";
string[] ACTIONS = {"move", "shoot", "cw" , "ccw" };
// 1. Создаём игру
using var client = new HttpClient();
var response = await client.PostAsync(String.Format("{0}/game/create", URL), JsonContent.Create(new { bots = 5 }));
var game_id = Convert.ToString(JObject.Parse(await response.Content.ReadAsStringAsync())["game_id"]);
// 2. Создаём танк
response = await client.PostAsync(String.Format("{0}/tank/create", URL), JsonContent.Create(new { game_id = game_id, hp = 2, shells = 2, attack = 2, shooting_distance = 2, visibility_range = 2 }));
var tank_id = Convert.ToString(JObject.Parse(await response.Content.ReadAsStringAsync())["tank_id"]);
// 3. Стартуем игру
await client.PostAsync(String.Format("{0}/game/{1}/start", URL, game_id), JsonContent.Create(new { }));
// 4. Игра началась. Запускаем бесконечный цикл управления танком
Random random = new Random();
while (true)
{
// Проверяем, что танк жив
response = await client.PostAsync(String.Format("{0}/tank/{1}/info", URL, tank_id), JsonContent.Create(new { }));
var info = JObject.Parse(await response.Content.ReadAsStringAsync());
if (Int32.Parse(Convert.ToString(info["hp"])) < 1) {
Console.Write("Поражение :(");
Console.ReadLine();
break;
}
// Выполняем рандомной действие
string action = ACTIONS[random.Next(0, ACTIONS.Length)];
response = await client.PostAsync(String.Format("{0}/tank/{1}/{2}", URL, tank_id, action), JsonContent.Create(new { }));
var result = JObject.Parse(await response.Content.ReadAsStringAsync());
Console.WriteLine(String.Format("Выполнили действие: {0}. Результат: {1}", action, result));
}
раскрой секрет, сессия привязывается к клиентскому хосту или это один общий танк на всех?)
#!
> раскрой секрет, сессия привязывается к клиентскому хосту или это один общий танк на всех?)
да, один на всех)
потом прикручу создание собственных танчиков / отдельных сессий
кстати, есть смысл давать игроку создавать кастомизированный танчик? типа на старте игры даётся пять очков, их можно вложить в следующие характеристики танка:
- сила атаки
- количество снарядов
- радиус обзора
и т.п.
Очень правильно что сделал отдельную тему.
переживал за загрузчик по url, а нужно было за парсер html
+ Показать
#!
> переживал за загрузчик по url, а нужно было за парсер html
не, это мой косяк. переделаю на возврат в формате json, чтобы проще было парсить
#6 (Правка: 11:10)
10:50, 11 мар 2023
− Скрыть
{
"map": [
[
"WATER",
"WATER",
"WATER",
"WATER",
"WATER",
"WATER",
"WATER",
"WATER",
"WATER",
"WATER",
"WATER",
"WATER",
"WATER",
"WATER",
"WATER",
"WATER",
"WATER",
"WATER",
"WATER",
"WATER"
],
[
"BLOCK",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
"BLOCK",
"BLOCK",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
"BLOCK"
]
],
"x": 3,
"y": 4
}
Если следовать канонами баттлсити, то выстрел возможен только по ходу движения. В противном случае возникает вопрос насчёт поворота башни.
Ren
> Если следовать канонами баттлсити, то выстрел возможен только по ходу движения.
> В противном случае возникает вопрос насчёт поворота башни.
Для чего это нужно? Типа более приближено к реальности?
интереснее вопрос а можно ли проламывать BLOCK выстрелом?
#!
> интереснее вопрос а можно ли проламывать BLOCK выстрелом?
Нельзя, а то снесёшь к херам всю карту)
В батлсити были, вроде, два типа блоков: пробиваемые и непробиваемые. Можно сделать типа такого.
У меня дилемма по глобальной архитектуре. Через РЕСТ АПИ не очень понятно, как сообщать игроку, что его подбили. Удобнее было бы использовать вебсокет, он сам сообщает клиенту обо всех событиях. Но написать клиента на вебсокете сложнее, поэтому порог вхождения в конкурс, который и так достаточно экзотичный, будет ещё выше.
aliskda
> как сообщать игроку, что его подбили
точно так же в ответ на любую команду, ваши координаты, вы убиты)
ну и просто игнорить команды от убитого
не знаю что с respawn, надо будет переподключаться?
aliskda
Потому что «танк». Танк не может стрелять в противоположные стороны одновременно, иначе это не танк. В оригинале проблема решена тем что танк стреляет только вперёд, то есть сначала надо развернуть танк в нужную сторону, потом сделать выстрел. Наверное это самый простой вариант. Это важный элемент геймплея, как по мне.
#13 (Правка: 23:26)
23:05, 11 мар 2023
Ren
> Наверное это самый простой вариант
ты же в роли дизайнера выступаешь, обоснуй
вот у меня строго обратное предложение
https://gamedev.ru/projects/forum/?id=275396&page=10&m=5705385#m143
а башня пусть крутится в любую сторону максимально быстро, это же танк, и то нет уверенности что кто-то будет реально попадать
хотя я исхожу что за один ход можно сделать одно движение и один выстрел в любом порядке
#!
> не знаю что с respawn, надо будет переподключаться
Респауна не будет. Матч длится до уничтожения одного из танков.
> вот у меня строго обратное предложение
> https://gamedev.ru/p… =5705385#m143
Имхо, не очень интуитивно понятное поведение
Ren
> танк стреляет только вперёд, то есть сначала надо развернуть танк в нужную
> сторону, потом сделать выстрел. Наверное это самый простой вариант
Мне кажется, это простое и интуитивно понятное для игрока поведение.
Но, как я понимаю, в реальности у танка направление движения и направление орудия не связаны. В идеале, нужно сделать два независимых параметра: "tankDirection" и "turretDirection" и дать возможность игроку манипулировать ими независимо друг от друга.
Идейки в бэклог себе, чтобы не забыть:
- Боты. Нужны для отладки ИИ. Можно пока, чтобы не стреляли, а тупо хаотично бегали по карте
- Сделать реплаи. Для начала достаточно записывать все действия игрока
- Таблица рекордов. Правда, не очень понятно, как считать очки. По количеству побед?