Охота на пасхальное яйцо на холме


17

Охота за пасхальными яйцами

Бот найти яйцо, прежде чем кролик найдет яйцо. Бот счастлив.

обзор

Это испытание в честь Пасхи и традиции охоты за пасхальными яйцами!

Ваш бот видит два пространства в каждом направлении, включая диагонали, создавая вокруг себя квадрат 5х5, который вы можете видеть. Он ищет яйца, и тот, кто найдет больше всего яиц, победит!

Доска

Доска будет состоять из os, которые являются пасхальными яйцами, #s, которые являются стенами, *s, которые являются другими игроками, и s, которые являются пустыми местами.

  • Это будет квадрат с длиной ребра (number of entries) * 3.
  • Он будет окружен стенами.
  • Внутри стен будет ассортимент случайно расположенных прямых линий #, которые будут иметь произвольную длину от 2 до 10 включительно. Там будет (number of entries) * 3из них.
  • Яйца будут размещены в случайном порядке. Их будет (number of entries) * 4, и они будут генерироваться только на пустых ( ) квадратах.
  • Для правильной работы процесса создания доски должно быть не менее 7 единиц.

Вот JSFiddle, который сгенерирует случайную доску для тестирования. Вот пример с (number of entries) = 7:

#####################
#        o         ##
#    #    o        ##
#    #o    ######  ##
######     #       ##
## o #     #       ##
##  o#   #o#    o o##
##   #o  # # o  #   #
##   # o # #    #   #
##  ##   # #  o #   #
##  #    #  o   # # #
##  # o  #   ## # # #
##  #           # # #
# o #          ## # #
# o oo         ##o  #
#o  ####### oo ##   #
#        #      #   #
#   o o o#          #
#   o ####   o     o#
#                   #
#####################

После того, как доска сгенерирована, каждый игрок размещается на случайном квадрате (пустое место).

вход

Вы возьмете шесть строк ввода. Первые пять строк - это ваше поле зрения (пробелы за пределами поля будут обозначены X, а среднее пространство всегда будет *вами), а шестая строка будет пустой (сначала).

Выход

Вы выведете три строки. Во-первых, направление, в котором вы хотите двигаться:

1  2  3
8 YOU 4
7  6  5

(9 - нет, если вы не хотите двигаться), секунда, одна из Attack, Counter или Nothing (скоро это будет подробно объяснено), а в качестве строки thrid будет любая строка длиной до 1024 Это будет память вашего бота. Вы можете использовать его по своему усмотрению или оставить его пустым. Эта память станет шестой строкой ввода вашей программы при следующем запуске.

Все дальнейшие строки вывода игнорируются, и если есть только одна строка, вторая считается пустой.

перемещение

Следующий процесс используется, чтобы определить, куда вы переехали:

  • Если при перемещении вы окажетесь в пустом месте ( ), ваш игрок будет помещен в это пространство.
  • Если вы окажетесь в стене ( #), ваш ход игнорируется, и вы теряете свой ход.
  • Если вы попадете в egg ( o) или player ( *), эта информация будет сохранена и будет использоваться после того, как все переехали.

После того, как все переехали, двусмысленности разрешаются.

Если в одном месте приземлились два игрока, происходит драка! Это где A/ C/ Nприходит играть. Attack бьет Nничто (обычная атака), Nничто бьет по Cаунтеру (вы ничего не можете противостоять), а Counter бьет по Attack (контратака). Игрок, выигравший этот бой, остается на своем поле, а игрок, проигравший, возвращается к исходному полю, на котором он начал. В случае ничьей оба игрока возвращаются туда, где они были.

Если проигравший или привязанный игрок возвращается туда, где он был, и там есть другой игрок, битва не происходит, и другой игрок также возвращается в исходное пространство. Если в этом пространстве есть другой игрок, этот игрок возвращается, и это продолжается до тех пор, пока все игроки не окажутся в разных местах.

Если на одном поле находятся три или более игроков, они все возвращаются на свои исходные позиции.

Если какой-либо игрок все еще стоит на яйце ...

  • Если игрок выбрал A, яйцо уничтожено.
  • Если игрок выбрал C, ничего не происходит, и игрок возвращается к своему первоначальному пространству.
  • Если игрок выбрал N, игрок поднимает яйцо! Счет игрока увеличивается на единицу, и яйцо удаляется.

Языки

Вы можете использовать любой язык, который свободно доступен в Windows, OSX и Linux, чтобы обеспечить честность среди каждого участника. Если код не может быть свободно запущен, но его можно скомпилировать или упаковать в такой формат, пожалуйста, включите этот формат в свой ответ. В идеале, если вы можете скомпилировать свой код на более общий язык (например, CoffeeScript -> JavaScript), сделайте это.

счет

Ваш счет будет средним числом яиц, которые вы соберете из десяти пробежек. Пробег заканчивается, когда все яйца собраны или когда (number of entries * 25)пройдены очереди. Я вручную позабочусь о том, чтобы можно было достать все яйца для каждой карты (постоянно генерируя карты, пока все яйца не будут доступны).

Табло

Табло будет добавлено, когда будут выполнены все следующие условия:

  • Было подано как минимум семь действительных заявок с положительным или нулевым баллом (без оценок)
  • С момента создания этого задания прошло не менее 48 часов (UTC 14:23)

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


4
Можем ли мы получить программу тестирования до публикации 7 записей? Мне нравится тестировать перед публикацией, даже если это против "тупых" тест-ботов. Кажется, что это дает значительное преимущество, чтобы не публиковать, пока несколько других не имеют.
Geobits

1
Относительно игроков, возвращающихся. Таким образом, я мог бы быть неудачником и выиграть у соперника, но он отступает к другому игроку и запускает каскад, который возвращается на место нашего боя, так что игрок, начавший там, также возвращает мне шаг назад? (если это не ясно, я выложу суть Github с примером)
Мартин Эндер

1
Пример программы контроля был бы очень полезен здесь.
starbeamrainbowlabs

3
Мне нравится идея линии памяти
Einacio

2
Также вы знаете о значении ваших правил: если игрок (А) выбирает 9, он никогда не сможет подвергнуться значимой атаке. Если другой игрок (B) выйдет на этот квадрат игроков и выиграет, A будет перемещен обратно на свой первоначальный квадрат (который такой же). Но теперь происходит столкновение, потому что там есть и А, и В, поэтому В должен вернуться на свою собственную клетку. Таким образом, результат не зависит от фактического боя, B всегда возвращается на начальную клетку, а A всегда остается на месте. Это позволило бы мне написать оба, которые могли бы помочь другому представлению, блокируя путь для всех остальных.
Мартин Эндер

Ответы:


3

Cart'o'Gophers

Вот еще одна заявка - и эта на самом деле предназначена для того, чтобы быть конкурентоспособной. Опять же, это в Ruby. Так что запустите это с ruby cartogophers.rb. Это заняло намного больше времени, чем ожидалось ...

require 'zlib'
require 'base64'

def encode map, coords
    zipped = Zlib::Deflate.deflate map.join
    encoded = Base64.encode64(zipped).gsub("\n",'')
    "#{coords[:x]}|#{coords[:y]}|#{map.length}|#{encoded}"
end

def decode memory
    memory =~ /^(\d+)[|](\d+)[|](\d+)[|](.*)$/
    coords = {x: $1.to_i, y: $2.to_i}
    n_rows = $3.to_i
    encoded = $4
    decoded = Base64.decode64 encoded
    unzipped = Zlib::Inflate.inflate decoded
    n_cols = unzipped.length / n_rows;
    return unzipped.scan(/.{#{n_cols}}/), coords
end

def update map, fov, coords
    if coords[:x] < 2
        map.map! { |row| '?' << row }
        coords[:x] += 1
    elsif coords[:x] >= map[0].length - 2
        map.map! { |row| row << '?' }
    end

    if coords[:y] < 2
        map.unshift '?' * map[0].length
        coords[:y] += 1
    elsif coords[:y] >= map.length - 2
        map.push '?' * map[0].length
    end

    fov.each_index do |i|
        map[coords[:y]-2+i][coords[:x]-2, 5] = fov[i]
    end

    return map, coords
end

def clean_up map
    map.each do |row|
        row.gsub!('*', ' ')
    end
end

DIRECTIONS = [
    [],
    [-1,-1],
    [ 0,-1],
    [ 1,-1],
    [ 1, 0],
    [ 1, 1],
    [ 0, 1],
    [-1, 1],
    [-1, 0],
    [ 0, 0]
]

def move_to dir, coords
    {
        x: coords[:x] + DIRECTIONS[dir][0],
        y: coords[:y] + DIRECTIONS[dir][1]
    }
end

def get map, coords
    if coords[:x] < 0 || coords[:x] >= map[0].length ||
       coords[:y] < 0 || coords[:y] >= map.length
        return '?'
    end
    map[coords[:y]][coords[:x]]
end

def set map, coords, value
    unless coords[:x] < 0 || coords[:x] >= map[0].length ||
       coords[:y] < 0 || coords[:y] >= map.length
        map[coords[:y]][coords[:x]] = value
    end
    map[coords[:y]][coords[:x]]
end

def scan_surroundings map, coords
    not_checked = [coords]
    checked = []
    cost = { coords => 0 }
    direction = { coords => 9 }
    edges = {}

    while not_checked.length > 0
        current = not_checked.pop

        checked.push current
        (-1..1).each do |i|
            (-1..1).each do |j|
                c = { x: current[:x]+i, y: current[:y]+j }
                unless not_checked.include?(c) || checked.include?(c)
                    if get(map, c) == '#'
                        checked.push c
                    elsif get(map, c) == '?'
                        checked.push c
                        edges[current] = { cost: cost[current], move: direction[current] }
                    else
                        not_checked.unshift c

                        cost[c] = cost[current] + 1
                        if direction[current] == 9 # assign initial direction
                            direction[c] = DIRECTIONS.find_index([i,j])
                        else
                            direction[c] = direction[current]
                        end

                        if get(map, c) == 'o'
                            return direction[c], if cost[c] == 1 then 'N' else 'A' end
                        end

                        edges[c] = { cost: cost[c], move: direction[c] } if c[:x] == 0 || c[:x] == map[0].length - 1 ||
                                                                            c[:y] == 0 || c[:y] == map.length - 1
                    end
                end
            end
        end
    end

    # If no egg has been found return the move to the closest edge
    nearest_edge = edges.keys.sort_by { |c| edges[c][:cost] }.first
    if edges.length > 0
        return edges[nearest_edge][:move], 'A'
    else
        # The entire map has been scanned and no more eggs are left.
        # Wait for the game to end.
        return 9, 'N'
    end
end

input = $<.read.split "\n"
fov = input[0..4]
memory = input[5]

if memory
    map, coords = decode memory
    map, coords = update(map, fov, coords)
else
    map = fov
    coords = {x: 2, y: 2}
end
clean_up map

move, attack = scan_surroundings(map, coords)

memory = encode map, move_to(move, coords)

puts "#{move}
#{attack}
#{memory}"

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

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

Бот использует память для хранения карты и ее новой позиции на карте (при условии, что движение будет успешным). Карта хранится без разрывов строк, заархивирована и закодирована в формате base64 (вместе с количеством строк на карте, чтобы можно было повторно вставить разрывы строк). Это сжатие приводит к уменьшению размера примерно до трети несжатой карты, поэтому, имея тень более 1000 байт, я мог бы хранить карту размером около 3000 ячеек, что примерно соответствует полному исследованию карты с 18 ботами. До тех пор, пока нет такого большого количества представлений, я не думаю, что смогу найти решение для этого случая.

После нескольких тестовых прогонов против 5 dumbbotс и 1 naivebot(моя другая заявка), он либо работал очень плохо (например, 1 или 2 яйца), либо значительно превосходил остальные (7-9 яиц). Я могу подумать о лучшей стратегии борьбы и о том, как я могу определить, действительно ли я переехал или нет. Оба могут улучшить счет.

Ох, и если вы задаетесь вопросом о названии бота, вы должны прочитать Орден The Stick ( последняя панель на этом комиксе ).

РЕДАКТИРОВАТЬ: было несколько ошибок с обнаружением краев обнаруженной карты. Теперь, когда я их исправил, этот бот всегда получает оценки примерно 205 dumbbotс и 1 naivebot. Это больше походит на это! Если вы добавите $stderr.puts mapмой бот сейчас, вы действительно сможете увидеть, как он систематически раскрывает карту и собирает все яйца за это время. Я также решил Aвместо того, чтобы Nне наступать на яйцо, выбирать , чтобы уменьшить вероятность возвращения обратно к первоначальной клетке бота (что частично портит карту).

(Это не так хорошо работает против 6 naivebotс, тем более, что очень возможно оказаться в «тупике» с другим ботом, когда они оба неоднократно хотят схватить яйцо и выбрать N. Мне нужно подумать об этом ... )


3

Java Банни

Этот кролик еще не закончил расти (я все еще планирую внести изменения), но сейчас это отправная точка. Он ищет ближайшее яйцо и идет к нему. Не обнаружено ни стен, ни за пределами границ (пока). Он пойдет, чтобы забрать яйцо, на которое он приземлится, но в противном случае он попытается оттолкнуть его и напасть на что-нибудь еще. Если поблизости нет яиц, он пойдет за ближайшим кроликом. Они могут знать то, что он не знает. В противном случае он просто выберет случайное направление для ходьбы. И он довольно забывчивый (не использует переменную памяти).

Планы продвижения вперед:

  • Принимать решения на основе стен / за пределами
  • Выберите пути с целью, а не случайно
  • Используйте память, чтобы определить направление, в котором я шел

Обновление 1 Мой кролик пойдет за другими кроликами, если не увидит яйцо. Также переработал код «найди ближайшее яйцо» в свой собственный метод.

import java.util.*;

public class EasterEggHunt {

    // board chars
    public static final char EGG = 'o';
    public static final char WALL = '#';
    public static final char BUNNY = '*';
    public static final char SPACE = ' ';
    public static final char OUT_OF_BOUNDS = 'X';

    // player moves
    public static final char ATTACK = 'A';
    public static final char COUNTER = 'C';
    public static final char NOTHING = 'N';

    // directions
    public static final int UPPER_LEFT = 1;
    public static final int UP = 2;
    public static final int UPPER_RIGHT = 3;
    public static final int RIGHT = 4;
    public static final int LOWER_RIGHT = 5;
    public static final int DOWN = 6;
    public static final int LOWER_LEFT = 7;
    public static final int LEFT = 8;
    public static final int STAY = 9;


    // the size of the immediate area
    // (I'll be at the center)
    public static final int VISION_RANGE = 5;

    public static void main(String[] args) {

        Scanner input = new Scanner(System.in);

        char[][] immediateArea = new char[VISION_RANGE][VISION_RANGE];

        for (int i = 0; i < VISION_RANGE; ++i) {
            String line = input.nextLine();
            for (int j = 0; j < VISION_RANGE; ++j) {
                immediateArea[i][j] = line.charAt(j);
            }
        }

        String memory = input.nextLine();

        int moveDirection = decideMoveDirection(immediateArea, memory);
        System.out.println(moveDirection);

        char action = decideAction(immediateArea, memory, moveDirection);
        System.out.println(action);

        // change the memory?
        System.out.println(memory);

    }

    private static int decideMoveDirection(char[][] immediateArea, String memory) {

        // if there's a nearby egg, go towards it
        int direction = nearestBoardObject(immediateArea, EGG);

        // if we didn't find an egg, look for a bunny
        // (maybe he knows where to find eggs)
        if (direction == STAY)
            direction = nearestBoardObject(immediateArea, BUNNY);

        // otherwise, pick a random direction and go
        // we want to also have the chance to stop and catch our breath
        if (direction == STAY)
            direction = new Random().nextInt(STAY + 1);

        return direction;
    }

    private static int nearestBoardObject(char[][] immediateArea, char boardObject) {

        // start at the center and go outward (pick a closer target over a farther one)
        int spacesAway = 1;
        int meX = immediateArea.length / 2;
        int meY = immediateArea[meX].length / 2;

        while (spacesAway <= immediateArea.length / 2) {

            // I like to look right, and go clockwise
            if (immediateArea[meX][meY + spacesAway] == boardObject)
                return RIGHT;
            if (immediateArea[meX + spacesAway][meY + spacesAway] == boardObject)
                return LOWER_RIGHT;
            if (immediateArea[meX + spacesAway][meY] == boardObject)
                return DOWN;
            if (immediateArea[meX + spacesAway][meY - spacesAway] == boardObject)
                return LOWER_LEFT;
            if (immediateArea[meX][meY - spacesAway] == boardObject)
                return LEFT;
            if (immediateArea[meX - spacesAway][meY - spacesAway] == boardObject)
                return UPPER_LEFT;
            if (immediateArea[meX - spacesAway][meY] == boardObject)
                return UP;
            if (immediateArea[meX - spacesAway][meY + spacesAway] == boardObject)
                return UPPER_RIGHT;

            ++spacesAway;
        }

        // if the target object isn't in the immediate area, stay put
        return STAY;

    }

    private static char decideAction(char[][] immediateArea, String memory, int moveDirection) {

        char destinationObject = getDestinationObject(immediateArea, moveDirection);

        switch (destinationObject) {

            case EGG:
                // don't break the egg
                return NOTHING;
            default:
                // get really aggressive on everything else
                // other players, walls, doesn't matter
                return ATTACK;

        }

    }

    private static char getDestinationObject(char[][] immediateArea, int moveDirection) {

        // start at my spot (middle of the board) and figure out which direction I'm going
        int targetX = immediateArea.length / 2;
        int targetY = immediateArea[targetX].length / 2;

        switch (moveDirection) {

            case RIGHT:
                ++targetY;
                break;
            case LOWER_RIGHT:
                ++targetX;
                ++targetY;
                break;
            case DOWN:
                ++targetX;
                break;
            case LOWER_LEFT:
                ++targetX;
                --targetY;
                break;
            case LEFT:
                --targetY;
                break;
            case UPPER_LEFT:
                --targetX;
                --targetY;
                break;
            case UP:
                --targetX;
                break;
            case UPPER_RIGHT:
                --targetX;
                ++targetY;
                break;
            // otherwise we aren't moving

        }

        return immediateArea[targetX][targetY];

    }

}

Я также узнал, что перечисления Java в значительной степени заполнены классами, и мне нравятся перечисления .NET намного лучше.
Брайан Дж.

0

NaiveBot (в рубине)

Вот очень упрощенный бот для запуска яиц (мы хотим быстро выполнить эти 7 заявок, верно?). Мой Ruby не очень идиоматичен, поэтому этот код может заставить настоящих рубинистов съежиться от боли. Читайте на свой страх и риск.

input = $<.read
$data = input.split("\n")

def is_egg x, y
    $data[y+2][x+2] == 'o'
end

def is_wall x, y
    $data[y+2][x+2] == '#'
end

def is_empty x, y
    $data[y+2][x+2] == ' '
end

def is_player x, y
    $data[y+2][x+2] == '*'
end

if (is_egg(-2,-2) || is_egg(-2,-1) || is_egg(-1,-2)) && !is_wall(-1,-1) || is_egg(-1,-1)
    dir = 1
elsif is_egg(0,-2) && !is_wall(0,-1) || is_egg(0,-1)
    dir = 2
elsif (is_egg(2,-2) || is_egg(2,-1) || is_egg(1,-2)) && !is_wall(1,-1) || is_egg(1,-1)
    dir = 3
elsif is_egg(2,0) && !is_wall(1,0) || is_egg(1,0)
    dir = 4
elsif (is_egg(2,2) || is_egg(2,1) || is_egg(1,2)) && !is_wall(1,1) || is_egg(1,1)
    dir = 5
elsif is_egg(0,2) && !is_wall(0,1) || is_egg(0,1)
    dir = 6
elsif (is_egg(-2,2) || is_egg(-2,1) || is_egg(-1,2)) && !is_wall(-1,1) || is_egg(-1,1)
    dir = 7
elsif is_egg(-2,0) && !is_wall(-1,0) || is_egg(-1,0)
    dir = 8
else
    dir = rand(8) + 1
end

attack = 'N'
puts "#{dir}
#{attack}
"

Беги с ruby naivebot.rb.

Я просто жестко запрограммировал несколько случаев, когда яйцо видно и не заслонено стеной. Это даже не касается ближайшего яйца, но выбирает первый ход, который имеет смысл. Если такого яйца не найдено, бот делает случайный ход. Он игнорирует всех других игроков и никогда не атакует и не противостоит.


0

WallFolower

(преднамеренное каламбур) в Python 3 :

import sys
import random

#functions I will use
dist       = lambda p1,p2: max(abs(p2[1] - p1[1]), abs(p2[0] - p1[0]))
distTo     = lambda p    : dist((2,2), p)
cmp        = lambda x,y  : (x > y) - (x < y)
sgn        = lambda x    : (-1,0,1)[(x>0)+(x>=0)]
move       = lambda p    : (sgn(p[0] - 2), sgn(p[1] - 2))
unmove     = lambda p    : (p[0] * 2 + 2, p[1] * 2 + 2)
outputmove = lambda p    : (1,2,3,8,9,4,7,6,5)[(sgn(p[0] - 2) + 1) + 3*(sgn(p[1]-2) + 1)]
def noeggfinish(move):
    print(outputmove(unmove(move)))
    print('ACN'[random.randint(0, 2)])
    print("1"+move)
    sys.exit(0)

#beginning of main body
view    = [list(l) for l in map(input, ('',)*5)] #5 line input, all at once.
memory  = input() #currently used only as last direction moved in a tuple
eggs    = []
enemies = []
for y in range(5):
    for x in range(5):
        if   view[y][x] == 'o': eggs    += [(x,y)]
        elif view[y][x] == '*': enemies += [(x,y)]

eggs.sort(key = lambda p:distTo(p)) #sort by how close to me they are.

tiedeggs = []
end = 0
for egg in eggs[:]:
    if end:break
    for enemy in enemies:
        exec({
            -1: 'eggs.remove(egg)',
             0: 'tiedeggs += egg',
             1: 'end=1'
        }[cmp(dist(enemy, egg), distTo(egg))])
        if end:break
if eggs:
    print(outputmove(eggs[0]))
    print('N')              #no attack here
    print("0"+move(eggs[0]))
    sys.exit(0)
elif tiedeggs:
    print(outputmove(tiedeggs[0]))
    print('N')              #no attack here
    print("0"+move(tiedeggs[0]))
    sys.exit(0) 
#now there are no eggs worth going for
#do a LH wall follow

lastmove = eval(memory[1:]) #used to resolve ambiguity
if lastmove[0] and lastmove[1]:
    lastmove[random.randint(0,1)] = 0 #disregard diagonal moves
if eval(memory[0]):
    exec("check=view[%n][%n]"%{(0,-1):(0,0),(1,0):(4,0),(0,1):(4,4),(-1,0):(0,4)}[lastmove])
    if check == '#':
        noeggfinish(lastmove)
    else:pass
#currently unimplemented
#move randomly
noeggfinish(tuple([(x,y) for x in [-1,0,1] for y in [-1,0,1] if (x,y) != (0,0)))

Переходит к яйцу, если в поле зрения есть яйцо, но только если оно ближе к этому яйцу, чем другой робот. Если на расстоянии есть связь, она все равно идет на это. В противном случае следует ли стена LH (в настоящее время не очень хорошо реализована).

Еще нужно поработать над стеной следите, но я все равно выложу здесь.


1
Когда я запускаю ваш бот с помощью tester.py, я получаю эту ошибку в консоли: pastebin.com/cT5xGdSW
starbeamrainbowlabs

Тоже самое. Не могли бы вы посмотреть на это? Я хотел бы проверить мой новый бот против этого.
Мартин Эндер,

@ m.buettner Что произойдет, если вы перейдете sys.exit(0)на exit(0)? Кроме того, мне нужно поработать над этим (прямо сейчас, он предполагает, что сам является ``), но у меня действительно нет времени. Когда у меня будет время, я приду и исправлю это.
Джастин

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