Асиметрична КОТЯ: Лови кота (котяча нитка)


14

Асиметрична КОТЯ: Лови кота

ОНОВЛЕННЯ : Файли gist оновлюються (включаючи нові підрозділи), оскільки Controller.java не вловлює Винятки (лише помилки). Зараз він вловлює помилки та винятки, а також друкує їх.

Цей виклик складається з двох ниток, це котяча нитка, можна знайти нитку виловлювача тут .

Контролер можна завантажити тут .

Це несиметрична КОТЯ: Кожне подання - це або кішка чи ловець . Існують ігри між кожною парою кожної кішки та видовищем. Коти та ловці мають окремий рейтинг.

Ловець

На шестикутній сітці лежить кішка. Ваше завдання - зловити це якомога швидше. Щоразу, ви можете розмістити відро з водою на одній осередку сітки, щоб не допустити, щоб кішка могла туди ходити. Але кішка не є (можливо) такою німою, і кожен раз, коли ви помістите відро, кішка перейде до іншої сітки для сітки. Оскільки сітка є шестикутною, кішка може йти в 6 різних напрямках. Ваша мета - оточити кота відрами з водою, чим швидше, тим краще.

Кіт

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

Сітка

Сітка є гексагональною, але оскільки у нас немає шестикутної структури даних, ми беремо 11 x 11квадратний 2d масив і імітуємо шестикутну «поведінку», якою кішка може рухатись лише в 6 напрямках:

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

Топологія тороїдальна, це означає, що якщо ви наступите на клітинку "поза" масиву, вас просто перенесуть до відповідної комірки з іншого боку масиву.

Гра

Кішка починає в заданому положенні в сітці. Ловець може зробити перший хід, тоді кішка та її ловець чергують рухи, поки кішка не буде спіймана. Кількість кроків - оцінка за цю гру. Кішка намагається отримати максимально високий бал, видовище намагається отримати бал якомога нижче. Середня сума за всі ігри, в яких ви брали участь, складе бал за вашу заявку. Є два окремі рейтинги, один - для кота, один - для ловців.

Контролер

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

Поле - це 11 x 112D- intмасив, який зберігає значення поточних станів комірок. Якщо клітина порожня, вона має значення 0, якщо є кішка, вона має значення, -1а якщо є відро - є 1.

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

Кожен раз, коли настає ваша черга, ваша функція takeTurn()викликається. Аргумент містить копію поточної сітки a має методи, такі як 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

1
Я думаю, що подібний виклик - це те, для чого потрібні теги поліцейських та грабіжників.
SuperJedi224

4
@flawr Я буду за те, щоб поширити тег CnR на всі виклики, які пов'язані з двома супровідними субпроблемами (і з використанням як цього, так і KotH як тегів у цьому). На вікі-теги CnR дуже сильно впливає перша проблема, яку ми мали в цьому жанрі. (Крім того, ви потрапили в поліцейські та грабіжники неправильно.))
Мартін Ендер

1
Що заважає кішкам імпортувати main.Controller, кликати getCatchers()та імітувати / саботувати відповіді виловлювачів своїми takeTurnметодами?
LegionMammal978

12
@ LegionMammal978 Sportsmanship.
Мартін Ендер

2
@feersum це допомагає? (Чорні (відповідно, сині) точки являють собою ту саму клітинку.)
недолік

Відповіді:


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;
    }
}

3

Дейкстри Cat

Він навчився та застосував головний алгоритм свого майстра. Зауважте, що він залежить від деяких методів у відповідному класі видовища.

Діккстра Кішка проти Хексатчер (потребує оновлення):

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

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 і більше ядрами, ця подача буде набагато кращою :)

MaxCat - Dijkstra:

MaxCat - Dijkstra

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;
        }
    }
}

Насправді ви можете зробити конструктора Fieldзагальнодоступним. Вибачте, що файли ще не оновлювали, але ми обговорювали це раніше!
flawr

@flawr О, здорово, дякую!
CommonGuy

2

SpiralCat

Рухається спірально. Це

  • Намагається перейти до верхнього лівого кола
  • Якщо це неможливо, спробуйте перейти до верхнього правого кола
  • Якщо це неможливо, спробуйте перейти до потрібного кола
  • Якщо це неможливо, спробуйте перейти до правого нижнього кола
  • Якщо це неможливо, спробуйте перейти до лівого нижнього кола

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;
    }
}

Чи знаєте ви, з якими помилками ви стикалися? Єдине , що я змінив би б зміни , 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 vs 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;
    }
}

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
        }
    }
}

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
    }
}

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;
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.