Основная часть для запуска игр турнира по Auralux для ЛОШ.
Визуализатор в отдельном репозитории.
На плоскости расположены n <= 20
планет пронумерованных от 1
до n
.
Расстояния между каждой парой планет вычислены и округлены до ближайшего целого, гарантируется, что все полученные расстояния больше нуля и не превышают 100
).
Игроку дается таблица dist[i][j]
из целых чисел, где dist[i][j]
- расстояние между планетами i
и j
(1 <= i, j <= n
, dist[k][k] = 0
для 1 <= k <= n
).
Игроки делают ходы по очереди - игрок 1
, игрок 2
, …, игрок m
.
В начале игры у каждого игрока есть одна планета уровня 1
на которой находится StartShipsCount = 100
кораблей.
На своем ходу игрок может отправить группу кораблей из любой планеты где у него есть корабли на любую другую.
Если расстояния между планетами равно x
корабли прилетят ровно перед x
следующим ходом этого игрока.
Например, если расстояние между планетами равно 1
, то корабли прилетят сразу перед следующим ходом этого игрока.
Можно отправлять группы кораблей с разных планет и с одной планеты на разные в рамках одного хода.
Действует ограничение - в полете должно быть не более MaxShipGroupsInSpace = 1000
различных групп кораблей одного игрока. Если в полете находится MaxShipGroupsInSpace
групп кораблей одного игрока, а он отправит еще одну группу, то эта группа немедленно погибает из-за проблем с навигацией.
Игрок может захватить нейтральную планету нулевого уровня если на ней находится хотя бы ShipsToCapturePlanet = 100
кораблей игрока. При этом действии все ShipsToCapturePlanet
разрушаются. Это действие происходит мгновенно в тот ход игрока, когда он решил совершить это действие.
Если состояние защиты планеты не равно максимуму для своего уровня, то игрок может чинить защиту. Для починки защиты на один пункт надо потратить один корабль. Это действие происходит мгновенно в тот ход игрока, когда он решил совершить это действие.
Игрок может улучшить планету 1
или 2
уровня если на ней находится хотя бы ShipsToUpgradePlanet[level] (100, 200)
кораблей игрока и состояние защиты равно максимуму для своего уровня. При этом действии все ShipsToUpgradePlanet[level]
разрушаются. Это действие происходит мгновенно в тот ход игрока, когда он решил совершить это действие.
Игрок может одновременно починить защиту планеты и улучшить её.
Улучшать планету более чем на один уровень за ход запрещено. Улучшать планету после захвата запрещено.
Если за один ход попробовать совершить больше MaxPlayerShipMovesPerStep = 1000
действий, игрок будет немедленно дисквалифицирован. Во всех остальных случаях невалидные ходы (например игроку не хватает кораблей на планете чтобы их отправить или на планете с которой игрок хочет отправить корабли ему не принадлежит) игнорируются.
Если игра закончилась и у неё есть победитель, то он получает 1
балл независимо от количества кораблей и захваченных планет.
Если после MaxSteps = 10000 - 100000 (зависит от карты)
ходов игра не окончилась, то результат определяется пропорционально количеству кораблей и планет у игроков: 0.5 * (playerShipsCount / allShips) + 0.5 * (playerPlanetsCount / allPlanetsCount)
.
Планета считается принадлежащей игроку если её уровень хотя бы 1
, просто наличия кораблей на планете недостаточно.
Каждая планета имеет уровень от 0
до 3
.
Нулевой уровень означает что планета нейтральная и не производит корабли (даже если на этой планете находятся корабли какого-то игрока).
На любом другом уровне большим нуля планета производит (PlanetProductionMultiply = 1) * PlanetLevel
кораблей между ходами игрока, которому она принадлежит (все корабли появляются одновременно ровно перед ходом игрока).
У планеты с уровнем хотя бы 1
есть PerLevelPlanetArmor[level] (100, 200, 300)
очков защиты.
После улучшения планеты с уровня x
до уровня x + 1
её очки защиты становятся равны PerLevelPlanetArmor[x + 1]
.
На планете может находится неограниченно количество кораблей одного игрока.
Если на планету прилетают корабли другого игрока происходит следующее:
- За каждый корабль прилетевшего игрока уничтожается один корабль "владельца" планеты (при этом корабль прилетевшего игрока тоже уничтожается, игрок, чьи корабли находятся на планете может не быть полноправным её владельцем, т.к. не захватил её)
- Если все корабли владельца планеты уничтожены, начинает повреждаться защита планеты, по одному пункту за каждый корабль прилетевшего игрока (корабли прилетевшего игрока тоже уничтожаются)
- Если защита планеты пала, планета переходит другому игроку, её уровень становится равен нулю
Если планета была нулевого уровня, она сразу переходит другому игроку после уничтожения всех кораблей на ней.
После дисквалификации игроку не дают делать ходы.
Отправленные группы кораблей и планеты остаются на карте. Планеты дисквалифицированного игрока продолжают производить корабли.
В конце игры дисквалифицированный игрок в любом случае получает 0
баллов (независимо от количества кораблей и планет у него).
Корабли дисквалифицированного игрока не учитываются в общем количестве кораблей.
Взаимодействие происходит через стандартный ввод/вывод (не забывайте про std::endl
).
После запуска вашей программе дают описание игровой карты в следующем формате:
Первая строчка содержит PlayerId
PlayerCount
PlanetCount
- ваш номер,
общее количество игроков и количество планет (1 <= PlayerId <= PlayerCount, 2 <= PlayerCount <= 4, PlayerCount <= PlanetCount <= 20
)
Далее идет PlanetCount
строк, каждая из которых содержит PlanetCount
чисел.
j
-е число в i
-й строке dist[i][j]
означает расстояние между планетами i
и j
(1 <= dist[i][j] <= 100
для i != j, 1 <= i, j <= PlanetCount
и dist[i][i] = 0
для 1 <= i <= n
).
Перед каждым ходом игроку предоставляется следующая информация:
В первой строке идет число NumberOfLastMoves
- количество отправленных групп кораблей между планетами между вашим прошлым и текущем ходом.
Если это число равно -1
, то это означает что игра закончена и ваша программа должна немедленно завершиться. Если ваша программа не завершится, вас могут дисквалифицировать.
Если же число неотрицательно, то следующие из NumberOfLastMoves
строк содержат PlayerId
FromPlanetId
ToPlanetId
Count
- какой игрок отправил, с какой планеты, на какую планету, сколько кораблей
(1 <= PlayerId <= PlayerCount, 1 <= FromPlanetId, ToPlanetId <= PlanetCount, 1 <= Count
)
Среди этих действий нет информации о захватах, улучшениях и починке защиты. Её можно узнать из состояния планет.
Следующие PlanetCount
строк описывают состояния планет. Каждая строчка содержит
PlayerId
ShipCount
Level
Armor
Если планета никому не принадлежит и на ней нет кораблей, все числа будут равны нулю.
Если планета имеет уровень 0
(никому не принадлежит), но на ней есть корабли игрока x
, то
PlayerId = x
ShipCount = количество кораблей игрока
Level = 0
Armor = 0
Если планета имеет уровень хотя бы 1
, то
PlayerId = id владельца планеты, число от 1 до PlayerCount
ShipCount = количество кораблей её владельца на планете (может быть равно нулю)
Level = уровень планеты, число от 1 до 3
Armor = Текущее состояние защиты планеты
Далее нужно сделать ход.
Для этого выведете число 0 <= NumberOfMoves <= MaxPlayerShipMovesPerStep
в отдельной строке.
Далее выведете NumberOfMoves
строк.
FromPlanetId
ToPlanetId
Count
-
Если
FromPlanetId != ToPlanetId
, то с планетыFromPlanetId
на планетуToPlanetId
будет отправлена группа кораблей размераCount
-
Если
FromPlanetId == ToPlanetId
то корабли пойдут на починку защиты планеты или её улучшение или захват
Операции будут выполняться последовательно.
Если какая-то операция невалидна, то она пропускается (например не хватает кораблей для улучшения, планета не принадлежит игроку, на планете недостаточно кораблей).
Очень важно не забывать выводить перевод строки и очищать буфер (std::endl
).
Если превысить MaxPlayerShipMovesPerStep
или нарушить протокол взаимодействия игрок будет немедленно дисквалифицирован.
Изначально у вашей стратегии есть ProcessPlayerStartTimeout = 2
секунд чтобы сделать все свои ходы. После каждого вашего хода это время увеличивается на ProcessPLayerTimeoutAdditionPerTurn = 0.001
.
Т.е. если вы сделаете 1000
своих мгновенно, у вас будет 3
секунды чтобы сделать текущий ход, а если же вы сделаете свой первый ход за одну секунду, у вас останется только 1.001
секунды на следующий ход.
Если выйти за лимит времени к какому-то ходу игрок будет дисквалифицирован без дальнейшей возможности продолжить игру.
Любые константы могут быть изменены.
- Если у вас нет brew, установите его, введя в терминал команду
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- Установите и обновите нужные версии пакетов (на linux возможно подойдут те же команды, но со своим менеджером пакетов)
brew install cmake && brew upgrade cmake && brew install qt5 && brew upgrade qt5 && brew link qt5 --force
- Скачайте репозиторий
git clone https://github.com/chegoryu/Auralux.git
- Соберите репозиторий
cd Auralux && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="/usr/local/opt/qt5/" .. && make
- В вашей папке будет находится ранер стратегии. Можете запустить его командой
./game_runner example_runner_config.txt