Rocket Bots KOTH


11

Ракетные Боты

Год 3024. Люди стали слишком скудными ресурсами, чтобы рисковать на войне, поэтому борьба перешла к роботам. Ваша задача состоит в том, чтобы создать бота, как никто другой, чьи ракеты обрушат на ваших врагов разрушение и уничтожат все поступающие угрозы.

Игровой процесс

Боты

Геймплей разворачивается по сетке 10х15. Ваш бот расположен за нижним краем и имеет три слота в точках 6,7 и 8 сетки. Ваш противник находится в верхней части сетки, с тремя слотами прямо напротив вашего.

Ракеты

Из любого из этих слотов вы можете запустить ракету, предполагая, что слот не был уничтожен. Ракета состоит из списка направлений, которые даны ей при создании, и после запуска эти направления не могут быть изменены. Каждый ход ракета будет занимать верхнюю часть списка и двигаться в этом направлении. Ракеты двигаются одновременно. Если две ракеты окажутся на одной плитке, они взорвутся. Если у ракеты заканчиваются команды, она взорвется. Если у ракеты заканчивается топливо, через 100 ходов она взорвется. Когда ракета взорвется, она останется в этой клетке на 5 ходов, что приведет к взрыву любых других ракет, которые движутся туда.

Примечание: из-за одновременного движения две ракеты могут проходить друг без друга, не взорвавшись, если они не заканчивают ход в одной и той же плитке.

Цель

Цель каждого матча - уничтожать слоты противников, сохраняя при этом свои. Ракета может быть запущена из любого живого слота, и у вас есть путь, обозначенный вами до ее запуска. Вы запускаете ракету каждый второй ход, то есть ракеты будут двигаться дважды, прежде чем вы сможете запустить еще одну. Дуэль длится 200 ходов, или пока все слоты одного бота не будут уничтожены.

счет

В конце матча вы получаете одно очко за каждый слот, который у вас есть, и одно очко за каждый слот противника, которого вы уничтожили. Это означает, что это игра с нулевой суммой, и 6 очков будут начисляться в каждом матче.

Будет запущен круговой робин, так что каждый бот будет один раз лицом к лицу. Если какие-либо боты используют RNG, то вместо каждого матча будет 1000 дуэлей.

Реализация

Код для конкурса можно найти здесь: https://github.com/Cain93/RocketBots

Каждое представление должно расширять Botкласс. Вы должны переопределить fireRocketметод. Этот метод получает сетку Рокетс, Rocket[][]представляющую игровую доску. Вы всегда расположены в нижней части сетки, с прорезями в точках [-1][6], [-1][7], [-1][8]. На сетке незанятые пятна будут представлены с помощью null. Если в плитке существует ракета, вы можете определить, кому она принадлежит, путем доступа к disполю. «^» - ваша ракета, а «v» - ваши противники.

Вы должны вернуть LinkedList из целых чисел, который дает инструкции для вашей ракеты. Для перемещения вверх используйте 0. Для перемещения вверх и вправо используйте 1, вправо, 2 и т. Д. До 7 для перемещения вверх и влево. Ракета будет двигаться в том порядке, в котором вы нажимаете целые числа. Например, следующий код заставит ракету подняться на несколько оборотов, сделать зигзаг на несколько оборотов, а затем взорвать.

LinkedList<Integer> moves = new LinkedList<Integer>();

moves.push(0);
moves.push(0);
moves.push(0);
moves.push(1);
moves.push(7);
moves.push(1);
moves.push(7);

Чтобы изменить, из какого слота вести огонь, измените curSlotполе. 0 - ваш самый левый слот, а 2 - ваш самый правый. Чтобы проверить, уничтожен ли слот, используйте getSlot(int slotNumber).

Если ракета заканчивает поворот на слоте, этот слот будет уничтожен. Вам не нужно вручную взрывать ракету.

Примечание. Ракеты появляются в месте выстрела, но они будут двигаться один раз, прежде чем будут оцениваться столкновения. Так что, если вы запускаете ракету из слота 0 и первый ход направо (2), то вы уничтожите свой собственный средний слот. Тем не менее, вверх и вправо (1) - безопасный ход.

Если вы хотите назвать своего бота, переопределите name()метод.

Боты будут пересозданы для каждой дуэли, поэтому любые статические переменные будут сброшены.

Удачи!

Пусть ваши ракеты летят правдой, а ваши противники - не что иное, как тлеющие обрывки металла.

Подсказка:

Преднамеренно взрывать ракеты для создания взрывов - более простой способ защиты, чем пытаться сбивать ракеты противника.

Пример бота

package bots;

import java.util.LinkedList;

import mechanics.*;




public class SimpleBot extends Bot {

    public String name(){
        return "Simple";
    }

    public LinkedList<Integer> fireRocket(Rocket[][] g){

        LinkedList<Integer> l = new LinkedList<Integer>();
        for(int i = 0; i < 12; i++){
            l.push(0);
        }
        return l;
    }

}

множество

Результаты от 6 до 24

Simple: 900
Zigzagoon: 3654
Wall-E: 3606
Tortoise: 2248
3 Shot: 2334
HatTrickBot: 4287
Sniper: 2973
SideShooter: 2491
Terminator: 4835
StraightShot: 3378
Defender: 4570
MoreDakka: 4324

Исправлено, так что порядок не имеет значения, если какие-либо боты используют RNG, я увеличу пробег до 1000 раз за матч
Каин

1
Есть ли способ проверить, уничтожен ли слот противника? getSlot (int) только для наших слотов, верно?
Катенкё

1
@Cain. Может быть интересно не запускать ни одной ракеты в разрушенный слот: 3
Katenkyo

1
@Manu, это не ошибка, вы смотрите на доску со своей стороны, поэтому левые слоты относительны
Katenkyo

1
Мне очень жаль, я уже исправил эту ошибку, я просто забыл ее нажать!
Каин

Ответы:


3

Защитник

Защитник использует новый тип защиты: ракеты патрулируют перед слотами. Это дает огромное преимущество, поскольку ракеты живут 100 витков вместо 5 витков (как взрывы).

package bots;

import java.util.LinkedList;
import mechanics.*;

public class Defender extends Bot {
    int turn = 0;

    @Override
    public String name() {
        return "Defender";
    }

    @Override
    public LinkedList<Integer> fireRocket(Rocket[][] grid) {
        LinkedList<Integer> command = new LinkedList<Integer>();
        for (int i = 0; i < 3; i++) {
            if ((grid[0][6+i] == null || grid[0][6+i].getDis().equals("v")) && (grid[1][6+i] == null || grid[1][6+i].getDis().equals("v")) && getSlot(i)) {
                curSlot = i;
                command.push(0);
                for (int j = 0; j < 50; j++) {
                    command.push(0);
                    command.push(4);
                }
                break;
            }
        }

        if (command.isEmpty()) {
            if ((grid[0][9] == null || grid[0][9].getDis().equals("v")) && (grid[0][10] == null || grid[0][10].getDis().equals("v")) && (grid[1][10] == null || grid[1][10].getDis().equals("v")) && getSlot(2)) {
                curSlot = 2;
                command.push(1);
                command.push(1);
                command.push(4);
                for (int i = 0; i < 50; i++) {
                    command.push(6);
                    command.push(2);
                }
            } else if ((grid[0][5] == null || grid[0][5].getDis().equals("v")) && (grid[0][4] == null || grid[0][4].getDis().equals("v")) && (grid[1][4] == null || grid[1][4].getDis().equals("v")) && getSlot(0)) {
                curSlot = 0;
                command.push(7);
                command.push(7);
                command.push(4);
                for (int i = 0; i < 50; i++) {
                    command.push(2);
                    command.push(6);
                }
            }

        }

        if (command.isEmpty()) {
            if (turn % 2 == 0 && getSlot(0)){
                curSlot = 0;
                command.push(7);
                command.push(7);
                for (int i = 0; i < 7; i++) {
                    command.push(0);
                }
                command.push(2);
                for (int i = 0; i < 2; i++) {
                    if (Math.random() < 0.2) command.push(2);
                }
                command.push(1);
            } else {
                curSlot = 2;
                command.push(1);
                command.push(1);
                for (int i = 0; i < 7; i++) {
                    command.push(0);
                }
                command.push(6);
                for (int i = 0; i < 2; i++) {
                    if (Math.random() < 0.5) command.push(6);
                }
                command.push(7);
            }
        }

        turn++;
        return command;
    }
}

Ничего себе +1. Потрясающий бот. Но, как вы говорите, не может играть хорошо, когда бот находится на другой стороне.
Спикатрикс

1
Довольно безупречная игра, поздравления
Каин

6

Zigzagoon

Внешние слоты становятся (немного) широкими, затем направляются вперед и возвращаются к вражеским слотам. Средняя щель стреляет зигзагообразным узором вверх по середине.

Каждый второй раунд (3 хода) он переходит в режим защиты и просто взрывает несколько ракет возле моих собственных слотов. Ракеты в режиме атаки, затем обойдите их. Ничего особенного, просто что-то, чтобы начать конкурс.

package bots;import java.util.*;import mechanics.*;

public class Zigzagoon extends Bot{
    String[] evenMoves = {"7000000001","0170710170","1000000007"};
    String[] oddMoves = {"0","00","0"};
    boolean even = true;
    public String name(){return "Zigzagoon";}

    public LinkedList<Integer> fireRocket(Rocket[][] g){
        curSlot = (curSlot+1)%3;
        if(curSlot<1)even=!even;
        String[] moves = even?evenMoves:oddMoves;
        LinkedList<Integer> command = new LinkedList<Integer>();
        for(int i=0;i<moves[curSlot].length();i++)
            command.push(moves[curSlot].charAt(i)-'0');
        return command;
    }
}

Исправлено, большая часть различий возникла из-за того, начинался ли он как true или false. Боты теперь перестраиваются перед каждым матчем.
Каин

Ах, это имеет смысл. Я даже не думал, что переменные не сбрасываются. Спасибо :)
Geobits

5

терминатор

Я с гордостью представляю Терминатор !!!

Каждая ракета движется далеко влево / вправо от середины и возвращается на вражеский слот. Каждые два оборота, ракета защиты запускается прямо и взрывается рядом с прорезью, чтобы защитить ее.

package bots;

import java.util.LinkedList;
import mechanics.Bot;
import mechanics.Rocket;

public class Terminator extends Bot {

    int n = 0;
    String[] moves = {"000", "0111107777", "00", "0077700111", "00", "0777701111"};

    public String name() {
        return "Terminator";
    }

    @Override
    public LinkedList<Integer> fireRocket(Rocket[][] g) {
        curSlot = (n+1) % 3;

        LinkedList<Integer> commands = loadCommands(moves[n % moves.length]);
        n++;

        return commands;
    }

    protected LinkedList<Integer> loadCommands(String commands) {
        LinkedList<Integer> linkedList = new LinkedList<Integer>();

        for (int i = 0; i < commands.length(); i++) {
            linkedList.push(commands.charAt(i) - 48);
        }

        return linkedList;
    }

}

3

HatTrickBot

У меня был DoubleTapBot для CodeBot 3, который дважды бил по ходу, вот и HatTrickBot: удар по всем 3 точкам одновременно!

Всегда можно предотвратить попадание ракеты, если вы знаете, куда она упадет. Но я не думаю, что есть много ботов, которые смогут защитить свои слоты от 3 ракетных атак.

Между прочим, да, это ужасно видеть такое еще с повторным переключением. Я мог бы создать переменную для включения с уникальными значениями для каждой комбинации статуса слотов и turnConter. Но это будет сложнее читать (мне нужно сохранить значение значений в комментарии ... скучно!) :)

package bots;
import java.util.LinkedList;
import mechanics.*;
/*
 * HatTrickBot always tries to destroy all the enemy slots at once
 * In order to achieve this, each slot needs extrem concentration and coordination
 * It explain why they need some turns to re-synchronized the rockets after one of them dies.
 */
public class HatTrickBot extends Bot
{
    // Default moves are at [0]
    // moves at [1] are used when there's only 2 slots remaining
    // moves  [2-4] are here for when there's only 1 slot remaining
    // it panicks, and can't establish how to fire to do a hat trick.
    // So he will just spamm every ennemy position, one at a time
    String[] rightSlot = {  "770002000020",
                            "0000000001",
                            "0000000000",
                            "0000000001",
                            "0000000011"};
    String[] midSlot   = {  "0000000000",
                            "11000060000",
                            "0000000000",
                            "0000000010",
                            "0000000700"};
    String[] leftSlot  = {  "11000060007",
                            "777702000020",
                            "0000000000",
                            "0000007000",
                            "0000077000"};
    int turnCounter=-1;
    public String name(){return "HatTrickBot";}
    public LinkedList<Integer> fireRocket(Rocket[][] g)
    {
        turnCounter=(turnCounter+1)%3;
        String[][] moves = {rightSlot,midSlot,leftSlot};
        LinkedList<Integer> ll = new LinkedList<Integer>();
        boolean slotL=getSlot(0),slotM=getSlot(1),slotR=getSlot(2);
        int movePoint=0;
        if(slotL&&slotM&&slotR)
        {
            switch(turnCounter)
            {
            case 0: curSlot=0;
                break;
            case 1: curSlot=2;
                break;
            case 2: curSlot=1;
                break;
                default:break;
            }
            movePoint=0;

        }
        else if(!slotM&&slotL&&slotR)
        {
            switch(turnCounter)
            {
            case 0: curSlot=0;
                    movePoint=0;
                break;
            case 1: curSlot=2;
                    movePoint=0;
                break;
            case 2: curSlot=0;
                    movePoint=1;
                break;
                default:break;
            }
        }
        else if(!slotL&&slotM&&slotR)
        {
            switch(turnCounter)
            {
            case 0: curSlot=0;
                    movePoint=0;
                break;
            case 1: curSlot=1;
                    movePoint=1;
                break;
            case 2: curSlot=0;
                    movePoint=1;
                break;
                default:break;
            }
        }
        else if(!slotR&&slotM&&slotL)
        {

            switch(turnCounter)
            {
            case 0: curSlot=2;
                    movePoint=1;
                break;
            case 1: curSlot=1;
                    movePoint=1;
                break;
            case 2: curSlot=1;
                    movePoint=0;
                break;
                default:break;
            }
        }
        else
        {
            if(slotR)curSlot=0;
            if(slotM)curSlot=1;
            if(slotL)curSlot=2;
            movePoint = 2+turnCounter;
        }
        for(int i=0;i<moves[curSlot][movePoint].length();i++)
            ll.push(Integer.parseInt(moves[curSlot][movePoint].charAt(i)+""));
        return ll;
    }
}

Ракеты запускают только каждый второй ход, так что это не делает полный хит Хет-трик. Все еще чрезвычайно эффективный, хотя
Каин

@ Каин Хо, грустно, что каждые 2 хода: /. В любом случае, я увижу результат первого раунда, а затем модифицирую, чтобы сделать настоящий HatTrick, если я думаю, что он может улучшить его :)
Katenkyo

Ракеты перемещаются дважды между каждым вызовом fireRocket (). Так что сейчас они все еще смещены друг от друга на один оборот. Вы можете использовать тестовый файл в контроллере, чтобы увидеть визуальное представление соответствия.
Каин

@Cain Я был на работе, когда писал эти боты, поэтому я основывал их только на спецификациях :)
Katenkyo,

Вау, я впечатлен, что вам удалось написать это без каких-либо испытаний, поздравляю. Я обновил спецификации, чтобы сделать его более понятным
Каин

2

Черепаха

Если я защищаю все свои базы, у меня есть 3 очка. Базы могут быть атакованы только из 5 мест, если я хорошо получаю сетку. Ракета длится 5 оборотов на поле ...

Этот бот использует все это, чтобы соответствовать своей цели: выжить с минимум 50% очков в кармане. Стреляет 3 ракеты, затем прикрывает себя

package bots;

import java.util.LinkedList;

public class Tortoise extends Bot
{
    int turnCounter=-1;
    boolean attacked=false;
    int[] moves={7,0,0,0,1};
    public String name(){return "Tortoise";}
    public LinkedList<Integer> fireRocket(Rocket[][] g)
    {
         LinkedList<Integer> rocket = new LinkedList<Integer>();
         turnCounter++;
         if(!attacked)
         {
             curSlot=turnCounter;
             for(int i=0;i<11;i++)
                 rocket.push(0);
             if(turnCounter==2)
                 attacked=true;
             return rocket;
         }
         turnCounter%=5;
         switch(turnCounter)
         {
         case 0:
         case 1:curSlot=0;break;
         case 2:curSlot=1;break;
         case 3:
         case 4:curSlot=2;break;
            default:break;
         }
         rocket.push(moves[turnCounter]);
         return rocket;
    }
}

Ракеты стреляют только через каждый ход, так что вы можете поддерживать только 2 с половиной стены
Каин

@Cain О, так что я изменю это, чтобы иметь настоящее убежище :)
Катенкё

2

SideShooter

Первые выстрелы через первую (крайнюю правую) башню одним из двух способов. Затем он стреляет через последнюю (самую левую) башню одним из двух способов. Затем он создает «стену» со второй (средней) башней, взорвав ракеты перед каждой башней. Этот процесс повторяется.

Если игра длится более 30 ходов, SideShooter становится скучно и меняется незначительно. Вместо того, чтобы делать «стену» со второй (средней) башней, она стреляет прямо. Остальные турели ведут себя так же.

import java.util.LinkedList;

public class SideShooter extends Bot {

    int[] launcher = new int[]{1, 3, 2, 2, 2};
    String[] right = {"1100000077", "100000007"};
    String[] left  = {"7700000011", "700000001"}; 
    int position = -1;
    int turns = 0;

    public String name(){
        return "SideShooter";
    }

    public LinkedList<Integer> fireRocket(Rocket[][] g){
      LinkedList<Integer> directions = new LinkedList<Integer>();

      if(getSlot(0) || getSlot(1) || getSlot(2))      
          do{
              position = (position + 1) % 5;
              curSlot = launcher[position] - 1;
          }while(!getSlot(curSlot));

      if(position == 0)
      {
          String shoot = left[((int) (Math.random() * left.length))];
          for(int i=0; i < shoot.length(); i++)
              directions.push(shoot.charAt(i)-'0');
      }else if(position == 1)
      {
          String shoot = right[((int) (Math.random() * right.length))];
          for(int i=0; i < shoot.length(); i++)
              directions.push(shoot.charAt(i)-'0');
      }else
      {
          if(turns < 30)
          {
            if(position == 2 )
                directions.push(0);
            else if(position == 3)
                directions.push(1);
            else if(position == 4)
                directions.push(7);
          }else
              for(int i=0; i < 10; i++)
                  directions.push(0); 
      }
      turns ++;
      return directions;
    }

}

do...whileутверждение сделать бесконечный цикл ...
guy777

@ guy777, нет, это не так. Он зацикливается, пока не найдет неразбитую башню.
Spikatrix

хорошо ! В первый раз я запускаю код со всеми ботами, SideShooter и другими ботами (я не знаю, кто) не может закончить игру !!!
guy777

Некоторые игры недопустимы. Контроллер должен иметь предел поворота.
guy777

1
@CoolGuy Я проголосовал за то, чтобы одобрить его редактирование. Обязательно проверьте / протестируйте его самостоятельно.
mbomb007

2

снайпер

Снайпер сначала блокирует свои две стороны, а затем начинает стрелять прямо.

import java.util.LinkedList;

public class Sniper extends Bot {

    int[] launcher = new int[]{1, 3, 1, 2, 3};
    String[] moves = {"7", "1", "0000000000", "0000000000", "0000000000"}; 
    int position = -1, move = 0;

    public String name(){
        return "Sniper";
    }

    public LinkedList<Integer> fireRocket(Rocket[][] g){
        LinkedList<Integer> directions = new LinkedList<Integer>();

        if(getSlot(0) || getSlot(1) || getSlot(2))
        do{
            position = (position + 1) % launcher.length;
            curSlot = launcher[position] - 1;
        }while(!getSlot(curSlot));

        for(int i=0; i < moves[move].length(); i++)
            directions.push(moves[move].charAt(i)-'0');

        move = (move + 1) % moves.length;

        return directions;
    }

}

Смотрите комментарии на SideShooter, та же проблема здесь.
Каин

2

Три выстрела

Мы получили несколько странных выстрелов. Нет истинной защиты, но схема такова, что будет трудно пробить этот причудливый град ракет. (или это идея. это, скорее всего, не будет работать.)

package bots;import java.util.*;import mechanics.*;

public class ThreeShot extends Bot{
    public String name(){state = 0;return "3 Shot";}
    private int state;

    public LinkedList<Integer> fireRocket(Rocket[][] g){
        LinkedList<Integer> command = new LinkedList<Integer>();
        if(state < 2){
           state++;
           return fireLeft();
        }
        if(state < 4){
           state++;
           return fireCenter();
        }
        state=(state+1)%6;
        return fireRight();
    }
    LinkedList<Integer> fireCenter(){
        LinkedList<Integer> command = new LinkedList<Integer>();
        curSlot = 1;
        while(command.size()<90){
            command.push(1);
            command.push(7);
            command.push(6);
            command.push(1);
        }
        return command;
    }
    LinkedList<Integer> fireRight(){
        LinkedList<Integer> command = new LinkedList<Integer>();
        curSlot = 2;
        command.push(1);
        for(int i=0;i<8;i++){
            command.push(0);
        }
        command.push(7);
        return command;
    }
    LinkedList<Integer> fireLeft(){
        LinkedList<Integer> command = new LinkedList<Integer>();
        curSlot = 0;
        command.push(7);
        for(int i=0;i<8;i++){
            command.push(6);
            command.push(1);
        }
        command.push(1);
        return command;
    }
}

Запись


2

MoreDakka

MoreDakka стреляет в пяти направлениях без остановки (пока башни не будут разрушены другими ракетами).

import java.util.LinkedList;

public class MoreDakka extends Bot
{
    String[] moves={"70000000001", "0000000000", "0000000000", "0000000000", "1000000007"};
    int[] launcher = new int[]{0, 0, 1, 2, 2};
    int position = -1;

    public String name(){
        return "MoreDakka";
    }

    public LinkedList<Integer> fireRocket(Rocket[][] g)
    {
         LinkedList<Integer> directions = new LinkedList<Integer>();

         if(getSlot(0) || getSlot(1) || getSlot(2))
            do{
                position = (position + 1) % launcher.length;
                curSlot = launcher[position];
            }while(!getSlot(curSlot));

         for(int i=0; i < moves[position].length(); i++)
            directions.push(moves[position].charAt(i)-'0');

         return directions;
    }
}

1

StraightShot

Просто стреляй прямо в них.

package bots;import java.util.*;import mechanics.*;

public class StraightShot extends Bot{
    public String name(){return "StraightShot";}

    public LinkedList<Integer> fireRocket(Rocket[][] g){
        LinkedList<Integer> command = new LinkedList<Integer>();
        curSlot = (curSlot+1)%3;
        for(int i=0;i<100;i++)
            command.push(0);
        return command;
    }
}

1
Если вы не знали, это в основном то же самое, что WaveBot , один из примеров ботов, включенных в контроллер.
Geobits

@Geobits Я понятия не имел, что пример ботов существовал.
MegaTom

Мой плохой, потому что я не помещаю Sample Bots куда-нибудь, чтобы их было легче увидеть Я просто удалю WaveBot и оставлю эту запись
Cain

1

Вот моя собственная запись

WALLE

Стреляет несколько смещенных ракет и строит стены по его краям и центру. После 100 ходов начинает нацеливаться на средний слот.

package bots;

import java.util.LinkedList;
import java.util.Random;

import mechanics.*;


public class WallE extends Bot {

    int turn = 2;

    public String name(){
        return "Wall-E";
    }

    public LinkedList<Integer> fireRocket(Rocket[][] g){
        turn++;
        LinkedList<Integer> moves = new LinkedList<Integer>();
        curSlot = 1;
        switch(turn%4){
        case 0: 
            //Check the right wall
            if(getSlot(2)){
                curSlot = 2;
                moves.push(1);
                return moves;
            }
        case 1:
            //Check the left wall
            if(getSlot(0)){
                curSlot = 0;
                moves.push(7);
                return moves;
            }
        case 2:
            //Check the center wall
            if(getSlot(1)){
                curSlot = 1;
                moves.push(0);
                return moves;
            }
            break;
        default:
            //Fire a sneaky rocket
            Random rand = new Random();
            int direction = rand.nextInt(2);
            int back = 0;
            if(direction == 0 && getSlot(2)){ direction = 1; back = 7; curSlot = 2;}
            else{ direction = 7; back = 1; curSlot = 0; }
            moves.push(0);
            moves.push(direction);
            moves.push(direction);
            for(int i = 0; i < 5; i++){
                moves.push(0);
            }

            //Go for the center after turn 100
            if(turn > 104){
                moves.pop();
                moves.push(back);
            }
            moves.push(back);
            moves.push(back);


        }

        return moves;










    }

}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.