Асимметричный КОТ: Поймай кота (кошачья нить)

14

Асимметричный КОТ: Поймай кота

ОБНОВЛЕНИЕ : Гист-файлы обновляются (включая новые подпункты), так как Controller.java не перехватывает исключения (только ошибки). Теперь он ловит ошибки и исключения, а также печатает их.

Эта задача состоит из двух потоков: это нить кошки, нить ловушки можно найти здесь .

Контроллер можно скачать здесь .

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

ловец

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

Кот

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

сетка

Сетка шестиугольная, но поскольку у нас нет гексагональных структур данных, мы берем 11 x 11квадратный двумерный массив и имитируем шестиугольное «поведение», которое кошка может перемещать только в 6 направлениях:

введите описание изображения здесь

Топология является тороидальной, это означает, что если вы наступите на ячейку «вне» массива, вы будете просто перенесены в соответствующую ячейку на другой стороне массива.

Игра

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

контроллер

Данный контроллер написан на Java. Как ловец или кошка, каждый из вас должен каждый реализовать класс Java (уже есть несколько примитивных примеров) и поместить его в playersпакет (и обновить список кошек / ловцов в классе Controller), но вы также можете написать дополнительные функции в этом классе. Контроллер поставляется с каждыми двумя работающими примерами простых классов кошек / ловцов.

Поле представляет собой 11 x 112D- intмассив, в котором хранятся значения текущих состояний ячеек. Если ячейка пуста, она имеет значение 0, если есть кошка, она имеет значение, -1а если есть область, то есть 1.

Есть несколько функций, которые вы можете использовать: isValidMove()/ isValidPosition()для проверки правильности вашего хода (кошка) / позиции (ловушка).

Каждый раз, когда ваша очередь, ваша функция takeTurn()вызывается. Аргумент содержит копию текущей таблицы и имеет методы, такие как read(i,j)чтение ячейки (i,j), а также isValidMove()/ isValidPosition()проверяет правильность вашего ответа. Это также управляет наложением тороидальной топологии, что означает, что даже если сетка имеет размер только 11 x 11, вы все равно можете получить доступ к ячейке (-5,13).

Метод должен возвращать intмассив из двух элементов, которые представляют возможные ходы. Для кошек это те, {-1,1},{0,1},{-1,0},{1,0},{0,-1},{1,-1}которые представляют относительное положение того, куда кошка хочет идти, а ловцы возвращают абсолютные координаты того, где они хотят поместить ведро {i,j}.

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

Ваше представление должно работать достаточно быстро. Если ваш метод занимает больше 200 мс для каждого шага, он также будет дисквалифицирован. (Желательно намного меньше ...)

Программы могут хранить информацию между этапами.

Материалы

  • Вы можете сделать столько заявок, сколько захотите.
  • Пожалуйста, не вносите существенных изменений в материалы, которые вы уже отправили.
  • Пожалуйста, каждый представленный в новом ответе.
  • Каждое представление должно иметь свое уникальное имя.
  • Представление должно состоять из кода вашего класса, а также описания, которое говорит нам, как работает ваше представление.
  • Вы можете написать строку <!-- language: lang-java -->для исходного кода, чтобы получить автоматическую подсветку синтаксиса.

счет

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

Этот вызов вдохновлен этой старой флеш игрой

Спасибо @PhiNotPi за тестирование и конструктивную обратную связь.

Текущие результаты (100 игр за пару)

Name              Score      Rank   Author

RandCatcher       191962     8      flawr   
StupidFill        212688     9      flawr
Achilles          77214      6      The E
Agamemnon         74896      5      The E
CloseCatcher      54776      4      randomra
ForwordCatcher    93814      7      MegaTom  
Dijkstra          47558      2      TheNumberOne
HexCatcher        48644      3      randomra
ChoiceCatcher     43834      1      randomra

RandCat            77490     9      flawr
StupidRightCat     81566     6      flawr
SpiralCat          93384     5      CoolGuy
StraightCat        80930     7      CoolGuy
FreeCat           106294     3      randomra
RabidCat           78616     8      cain
Dijkstra's Cat    115094     1      TheNumberOne
MaxCat             98400     4      Manu
ChoiceCat         113612     2      randomra
flawr
источник
1
Я думаю, что этот тип вызова - то, для чего предназначен тег «полицейские и грабители».
SuperJedi224
4
@flawr Я бы высказался за расширение тега CnR на все задачи, включающие две подзадачи противника (и использование в качестве тегов и того, и KotH). Вики-тег CnR очень сильно зависит от первых двух испытаний, которые у нас были в этом жанре. (Кроме того, вы неправильно поймали копов и грабителей.;))
Мартин Эндер
1
Что мешает кошкам импортировать main.Controller, вызывать getCatchers()и имитировать / саботировать ответы ловцов с помощью своих takeTurnметодов?
LegionMammal978
12
@ LegionMammal978 Спортивное мастерство.
Мартин Эндер
2
@feersum это помогает? (Черные (соответственно синие) точки представляют одну и ту же ячейку.)
flawr

Ответы:

5

FreeCat

Выбирает движение, которое даст ему наиболее возможные пути после 3 шагов, если поле не изменится.

FreeCat против Ахиллеса:

FreeCat против Ахиллеса

package players;
/**
 * @author randomra
 */

import java.util.Arrays;

import main.Field;

public class FreeCat implements Cat {

    final int[][] turns = { { -1, 1 }, { 0, 1 }, { -1, 0 }, { 1, 0 },
            { 0, -1 }, { 1, -1 } };// all valid moves
    final int turnCheck = 3;

    public String getName() {
        return "FreeCat";
    }

    public int[] takeTurn(Field f) {

        int[] pos = f.findCat();
        int[] bestMove = { 0, 1 };
        int bestMoveCount = -1;
        for (int[] t : turns) {
            int[] currPos = { pos[0] + t[0], pos[1] + t[1] };
            int moveCount = free_count(currPos, turnCheck, f);
            if (moveCount > bestMoveCount) {
                bestMoveCount = moveCount;
                bestMove = t;
            }
        }
        return bestMove;
    }

    private int free_count(int[] pos, int turnsLeft, Field f) {
        if (f.isValidPosition(pos) || Arrays.equals(pos, f.findCat())) {
            if (turnsLeft == 0) {
                return 1;
            }
            int routeCount = 0;
            for (int[] t : turns) {
                int[] currPos = { pos[0] + t[0], pos[1] + t[1] };
                int moveCount = free_count(currPos, turnsLeft - 1, f);
                routeCount += moveCount;
            }
            return routeCount;
        }
        return 0;
    }
}
randomra
источник
3

Кот Дейкстры

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

Кот Дейкстры против Hexcatcher (нуждается в обновлении):

введите описание изображения здесь

package players;

import main.Field;
import players.Dijkstra; //Not needed import. Should already be available.

/**
 * @author TheNumberOne
 *
 * Escapes from the catcher.
 * Uses Dijkstras methods.
 */

public class DijkstrasCat implements Cat{

    private static final int[][] possibleMoves = {{-1,1},{0,1},{-1,0},{1,0},{0,-1},{1,-1}};
    @Override
    public String getName() {
        return "Dijkstra's Cat";
    }

    @Override
    public int[] takeTurn(Field f) {
        int[] me = f.findCat();
        int[] bestMove = {-1,1};
        int bestOpenness = Integer.MAX_VALUE;
        for (int[] move : possibleMoves){
            int[] newPos = Dijkstra.normalize(new int[]{me[0]+move[0],me[1]+move[1]});
            if (!f.isValidMove(move)){
                continue;
            }
            int openness = Dijkstra.openness(newPos, f, true)[1];
            if (openness < bestOpenness || (openness == bestOpenness && Math.random() < .5)){
                bestOpenness = openness;
                bestMove = move;
            }
        }
        return bestMove;
    }
}

Как он работает:

Он пытается найти движение, которое минимизирует неровность доски по отношению к себе. Для получения дополнительной информации см. Соответствующий пост ловца.

С обновлением:

Теперь он избегает странных геометрических фигур, которые иногда образуют водяные ведра.

Номер один
источник
3

MaxCat

Я попытался реализовать алгоритм Minimax. Тем не менее, он не очень хорошо работает из-за ограниченного времени. Редактировать: теперь он использует многопоточность, но (по крайней мере, на моем компьютере) я не могу установить глубину больше. В противном случае происходит тайм-аут. Используя ПК с 6 или более ядрами, эта подача была бы намного лучше :)

МаксКат против Дейкстры:

МаксКат против Дейкстры

package players;

import java.util.ArrayList;
import java.util.List;

import main.Field;

public class MaxCat implements Cat {
    final int[][] turns = { { -1, 1 }, { 0, 1 }, { -1, 0 }, { 1, 0 }, { 0, -1 }, { 1, -1 } };

    public String getName() {
        return "MaxCat";
    }

    public int[] takeTurn(Field f) {
        List<CatThread> threads = new ArrayList<>();
        int[] pos = f.findCat();
        for (int[] turn : turns) {
            if(f.read(pos[0]+turn[0], pos[1]+turn[1]) == Field.EMPTY){
                CatThread thread = new CatThread();
                thread.bestMove = turn;
                thread.field = new Field(f);
                thread.start();
                threads.add(thread);
            }
        }
        for (CatThread thread : threads) {
            try {
                thread.join();
            } catch (InterruptedException e) {}
        }
        int best = Integer.MIN_VALUE;
        int[] bestMove = { -1, 1 };
        for (CatThread thread : threads) {
            if (thread.score > best) {
                best = thread.score;
                bestMove = thread.bestMove;
            }
        }
        return bestMove;
    }

    class CatThread extends Thread {
        private Field field;
        private int[] bestMove;
        private int score;
        private final int DEPTH = 3;

        @Override
        public void run() {
            score = max(DEPTH, Integer.MIN_VALUE, Integer.MAX_VALUE);       
        }

        int max(int depth, int alpha, int beta) {
            int pos[] = field.findCat();
            if (depth == 0 || field.isFinished()) {
                int moveCount = 0;
                for (int[] turn : turns) {
                    if(field.read(pos[0]+turn[0], pos[1]+turn[1]) == Field.EMPTY)
                        moveCount++;
                }
                return DEPTH-depth + moveCount;
            }
            int maxValue = alpha;
            for (int[] turn : turns) {
                if(field.read(pos[0]+turn[0], pos[1]+turn[1]) == Field.EMPTY) {
                    field.executeMove(turn);
                    int value = min(depth-1, maxValue, beta);
                    field.executeMove(new int[]{-turn[0], -turn[1]});
                    if (value > maxValue) {
                        maxValue = value;
                        if (maxValue >= beta)
                            break;
                    }
                }
            }
            return maxValue;
        }

        int min(int depth, int alpha, int beta) {
            if (depth == 0 || field.isFinished()) {
                int moveCount = 0;
                for (int[] turn : turns) {
                    int pos[] = field.findCat();
                    if(field.read(pos[0]+turn[0], pos[1]+turn[1]) == Field.EMPTY)
                        moveCount++;
                }   
                return -depth - moveCount;
            }
            int[][] f = field.field;
            int minValue = beta;
            List<int[]> moves = generateBucketMoves();
            for (int[] move : moves) {
                f[move[0]][move[1]] = Field.BUCKET;
                int value = max(depth-1, alpha, minValue);
                f[move[0]][move[1]] = Field.EMPTY;
                if (value < minValue) {
                    minValue = value;
                    if (minValue <= alpha)
                        break;
                }
            }
            return minValue;
        }

        private List<int[]> generateBucketMoves() {
            int[][] f = field.field;
            List<int[]> list = new ArrayList<>();
            for (int i = 0; i < f.length; i++) {
                for (int j = 0; j < f[i].length; j++) {
                    if (f[i][j] == Field.EMPTY) {
                        list.add(new int[]{i,j});
                    }
                }
            }
            return list;
        }
    }
}
CommonGuy
источник
На самом деле вы можете сделать конструктор Fieldобщественности. Извините, я еще не обновил файлы, но мы обсуждали это ранее!
flawr
@ flawr О, круто, спасибо!
CommonGuy
2

SpiralCat

Двигается по спирали. Это

  • Пытается переместиться в верхний левый круг
  • Если не возможно, пытается перейти в верхний правый круг
  • Если это невозможно, попытайтесь переместиться на правильный круг
  • Если это невозможно, пытается перейти в нижний правый круг
  • Если это невозможно, попытка переместиться в нижний левый круг

SpiralCat против Агамемнона:

СпиральКат против Агамемнона

package players;
/**
 * @author Cool Guy
 */

import main.Field;

public class SpiralCat implements Cat{
    public String getName(){
        return "SpiralCat";
    }
    public int[] takeTurn(Field f){
        int[][] turns = {{-1,1},{0,1},{1,0},{1,-1},{0,-1},{-1,0}};//all valid moves
        int[] move;
        int i = -1;
        do {
            i++;
            move = turns[i];
        } while(f.isValidMove(move) == false);
        return move;
    }
}
Spikatrix
источник
Знаете ли вы, с какими ошибками вы столкнулись? Единственное , что я изменил бы бы изменения , turns[i]чтобы turns[i%6]для того , чтобы избежать из оценок (которые не должны иметь место в этом stuation).
flawr
@ flawr, Черт. Плохой выбор слов. Я имел в виду, что этот кот не очень умный. Иногда этот кот просто чередуется между верхним левым кругом и нижним правым кругом, даже когда есть выход ...
Spikatrix
@flawr, я должен использовать turns[i%6]? Я имею в виду, takeTurnне будет вызван, если кот заблокирован, верно?
Spikatrix
Нет, я думал, что вы имели в виду, что столкнулись с ошибкой в ​​программе, поэтому я искал возможные причины. Но вы правы, очевидно , (если все это правильно) i>=6никогда не должно произойти.
flawr
2

RabidCat

У RabidCat есть гидрофобия, поэтому он боится воды. Он находит ближайший и бежит в противоположном направлении.

RabidCat против ForwordCatcher:

rabidcat_vs_forwordcatcher

package players;

import java.util.Random;

import main.Field;

/**
* Run away from water buckets
* @author cain
*
*/

public class RabidCat implements Cat {

public RabidCat() {
}

@Override
public String getName() {
    return "RabidCat";
}

@Override
public int[] takeTurn(Field f) {
    int[][] directions = {{-1,1},{0,1},{-1,0},{1,0},{0,-1},{1,-1}};

    //where am I?
    int[] position = {0,0};
    for(int i = 0; i < 12; i++){
        for(int j = 0; j < 12; j++){
            if(f.read(i,j) == -1){
                position[0] = i;
                position[1] = j;
            }
        }
    }

    //Find the closest water
    int direction = 0;
    for(int d = 0; d < 10; d++){
        if(f.read(position[0] + d, position[1] - d) == 1 && f.isValidMove(directions[0])){
            direction = 1;
            break;
        }
        if(f.read(position[0], position[1] - d) == 1 && f.isValidMove(directions[1])){
            direction = 2;
            break;
        }
        if(f.read(position[0] + d, position[1]) == 1 && f.isValidMove(directions[2])){
            direction = 3;
            break;
        }
        if(f.read(position[0] - d, position[1]) == 1 && f.isValidMove(directions[3])){
            direction = 4;
            break;
        }
        if(f.read(position[0], position[1] + d) == 1 && f.isValidMove(directions[4])){
            direction = 5;
            break;
        }
        if(f.read(position[0] - d, position[1] + d) == 1 && f.isValidMove(directions[5])){
            direction = 6;
            break;
        }
    }

    //If there is no water near, wander
    while(direction == 0){
        Random rand = new Random();
        direction = rand.nextInt(6) + 1;
        if(!f.isValidMove(directions[direction - 1])){
            direction = 0;
        }
    }
    return directions[direction - 1];
}

}
Каин
источник
Ничего себе, действительно получаю, что CloseCatcher разрушен
Каин
2

ChoiceCat

Для каждой возможной новой позиции кошки мы проверяем ее качество и выбираем лучшую. Доброта - это функция двух лучших соседних клеток, которые находятся дальше от позиции кошки, чем от позиции, чей счет мы вычисляем. Мы используем только две клетки, потому что одну можно заблокировать, а кошке нужна еще одна, чтобы уйти. Наша функция предпочитает две довольно хорошие ячейки, чем одну большую и одну плохую. Позиции с сегментами имеют оценку 0, а самые дальние свободные ячейки - 1.

ChoiceCat кажется лучше, чем нынешние кошки.

ChoiceCat против ChoiceCatcher:

ChoiceCat против ChoiceCatcher

package players;
/**
 * @author randomra
 */
import java.util.Arrays;

import main.Field;

public class ChoiceCat implements Cat {

    private class Values {
        public final int size;
        private double[][] f;

        Values(int size) {
            this.size = size;
            f = new double[size][size];
        }

        public double read(int[] p) {
            int i = p[0];
            int j = p[1];
            i = (i % size + size) % size;
            j = (j % size + size) % size;
            return f[i][j];
        }

        private double write(int[] p, double v) {
            int i = p[0];
            int j = p[1];
            i = (i % size + size) % size;
            j = (j % size + size) % size;
            return f[i][j] = v;
        }
    }

    final int[][] turns = { { -1, 1 }, { 0, 1 }, { 1, 0 }, { 1, -1 },
            { 0, -1 }, { -1, 0 } };// all valid moves CW order
    final int stepCheck = 5;

    public String getName() {
        return "ChoiceCat";
    }

    public int[] takeTurn(Field f) {

        int[] pos = f.findCat();
        int[] bestMove = { 0, 1 };
        double bestMoveValue = -1;
        for (int[] t : turns) {
            int[] currPos = { pos[0] + t[0], pos[1] + t[1] };
            double moveValue = movePosValue(currPos, f);
            if (moveValue > bestMoveValue) {
                bestMoveValue = moveValue;
                bestMove = t;
            }
        }
        return bestMove;
    }

    private double movePosValue(int[] pos, Field f) {

        Values v = new Values(f.SIZE);

        for (int ring = stepCheck; ring >= 0; ring--) {
            for (int phase = 0; phase < 2; phase++) {
                for (int sidepos = 0; sidepos < Math.max(1, ring); sidepos++) {
                    for (int side = 0; side < 6; side++) {
                        int[] evalPos = new int[2];
                        for (int coord = 0; coord < 2; coord++) {
                            evalPos[coord] = pos[coord] + turns[side][coord]
                                    * sidepos + turns[(side + 1) % 6][coord]
                                    * (ring - sidepos);
                        }
                        if (phase == 0) {
                            if (ring == stepCheck) {
                                // on outmost ring, init value
                                v.write(evalPos, -1);
                            } else {
                                v.write(evalPos, posValue(evalPos, v, f));
                            }
                        } else {
                            // finalize position value for next turn
                            v.write(evalPos, -v.read(evalPos));
                        }
                    }
                }
            }
        }

        return -v.read(pos);
    }

    private double posValue(int[] pos, Values v, Field f) {
        if (f.read(pos[0], pos[1]) == Field.BUCKET) {
            return 0;
        }
        int count = 0;
        double[] product = new double[6];
        for (int[] t : turns) {
            int[] tPos = new int[] { pos[0] + t[0], pos[1] + t[1] };
            if (v.read(tPos) > 0) {
                product[count] = 1 - 1 / (v.read(tPos) + 1);
                count++;
            }
        }
        Arrays.sort(product);
        double fp = 1;
        for (int i = 0; i < Math.min(count,2); i++) {
            fp *= product[5-i];
        }
        double retValue = Math.min(count,2) + fp;
        return -retValue;
    }
}
randomra
источник
1

StupidRightCat

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

package players;

import main.Field;

public class StupidRightCat implements Cat{
    public String getName(){
        return "StupidRightCat";
    }
    public int[] takeTurn(Field f){
        int[][] turns = {{-1,1},{0,1},{-1,0},{1,0},{0,-1},{1,-1}};//all valid moves
        int[] move;

        if(f.isValidMove(turns[3])){
            return turns[3];
        } else {
            do {
                move = turns[(int) (turns.length * Math.random())];
            } while(f.isValidMove(move)==false);
            return move;//chose one at random
        }
    }
}
flawr
источник
1

RandCat

Это было сделано только для тестирования контроллера. Кошка просто движется случайно.

package players;

import main.Field;

public class RandCat implements Cat{
    public String getName(){
        return "RandCat";
    }
    public int[] takeTurn(Field f){
        int[][] turns = {{-1,1},{0,1},{-1,0},{1,0},{0,-1},{1,-1}};//all valid moves
        int[] move;
        do {
            move = turns[(int) (turns.length * Math.random())];
        } while(f.isValidMove(move)==false);
        return move;//chose one at random
    }
}
flawr
источник
1

StraightCat

Этот кот двигается прямо.

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

StraightCat против Агамемнона:

StraightCat против Агамемнона

package players;
/**
 * @author Cool Guy
 */

import main.Field;

public class StraightCat implements Cat{

    int lastDirection = -1; //Holds the last direction the cat moved
    public String getName(){
        return "StraightCat";
    }
    public int[] takeTurn(Field f){
        int[][] turns = {{-1,1},{0,1},{1,0},{1,-1},{0,-1},{-1,0}};//all valid moves

        if(lastDirection == -1)
          lastDirection = (int) (turns.length * Math.random());

        int[] move = turns[lastDirection];
        int i = lastDirection;

        while(true)
        {
            if(f.isValidMove(move))
                break;
            i = (i+1)%6;
            lastDirection = i;
            move = turns[i];
        }
        return move;
    }
}
Spikatrix
источник