不对称KOTH:抓住猫(猫螺纹)


14

不对称的KOTH:抓住猫

更新由于Controller.java没有捕获到异常(仅错误),因此更新了主文件(包括新的子任务)。现在,它确实可以捕获错误和异常并打印它们。

这个挑战包括两个线程,这是cat线程,catcher线程可以在这里找到

控制器可以下载 此处

这是一个不对称的KOTH:每个提交都是还是一个捕手。每对猫和捕手之间都有游戏。猫和捕手的排名各不相同。

捕手

六角形网格上有一只猫。您的任务是尽快捕获它。每转一圈,您都可以在一个网格单元上放置一个水桶,以防止猫能够去那里。但是猫不是(也许)那么笨,每当您放置一个桶时,猫都会移动到另一个网格。由于网格是六角形的,因此猫可以向6个不同的方向移动。您的目标是用水桶包围猫,速度越快越好。

您知道捕手想通过在您周围放水桶来接住您。当然,您会设法逃避,但是由于您是一只懒惰的猫(就像猫一样),您当时确实迈出了一步。这意味着您不能待在同一个地方,而是必须移至周围的六个景点之一。每当您看到捕手放了一个新的水桶时,您就会进入另一个牢房。当然,您尝试逃避的时间越长越好。

格网

网格是六边形的,但是由于我们没有六边形的数据结构,因此我们采用一个11 x 11正方形的2d数组并模仿该六边形的“行为”,即猫只能在6个方向上移动:

在此处输入图片说明

拓扑是环形的,这意味着如果您踩到阵列“外部”的单元格,您将被转移到阵列另一侧的相应单元格。

游戏

猫从网格中的给定位置开始。捕手可以先走,然后猫及其捕手交替走动,直到猫被抓到为止。步骤数是该游戏的分数。猫试图获得尽可能高的分数,捕手试图获得尽可能低的分数。您参与的所有游戏的平均总和就是您提交的分数。有两个单独的等级,一个用于猫,一个用于捕手。

控制者

给定的控制器是用Java编写的。作为捕手或猫,您每个人都必须完整地实现一个Java类(已经有一些原始示例)并将其放置在players包中(并更新Controller类中的猫/捕手列表),但是您也可以编写该类中的其他功能。控制器随附了简单的cat / catcher类的每两个工作示例。

该字段是一个11 x 112D int数组,用于存储单元格当前状态的值。如果一个单元格为空,则它具有值0;如果有一只猫,它具有值-1;如果有一个存储桶,则有一个值1

您可以使用一些给定的功能:isValidMove()/ isValidPosition()用于检查您的移动(cat)/位置(catcher)是否有效。

每次轮到您时,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 -->源代码前写一行,以自动突出显示语法。

计分

所有都将与所有捕手竞争相同的次数。我将尝试经常更新当前分数,当活动减少时将确定获胜者。

这项古老的Flash游戏启发了这一挑战

感谢@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标签Wiki在很大程度上受到我们在该类型游戏中遇到的前两个挑战的影响。(另外,您会以错误的方式找到警察和强盗。)
马丁·恩德

1
是什么阻止猫通过其方法导入main.Controller,调用getCatchers()和模拟/破坏捕手的响应takeTurn
LegionMammal978

12
@ LegionMammal978体育精神。
马丁·恩德

2
@feersum做帮助?(黑色(分别是蓝色)点代表相同的单元格。)
瑕疵的

Answers:


5

免费猫

如果该字段保持不变,则选择在3步后为它提供尽可能多路径的移动。

FreeCat vs阿喀琉斯:

FreeCat vs阿喀琉斯

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

迪克斯特拉的

他学习并应用了他的主人的主人算法。请注意,他依赖于相应的捕手类中的某些方法。

Dijkstra的Cat vs 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;
    }
}

他的工作方式:

他试图找到使董事会相对于他本人的严厉程度降至最低的举动。有关更多信息,请参见相应的catcher帖子。

随着更新:

现在,他避免了水桶有时形成的奇怪的几何形状。


3

最大猫

我尝试实现Minimax算法。但是,由于时间有限,它的效果不是很好。编辑:它现在使用多线程,但是(在我的计算机上至少)我无法将深度设置得更高。否则会发生超时。使用具有6个或更多内核的PC,此提交会更好:)

MaxCat vs 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哦,太酷了,谢谢!
6

2

螺旋猫

以螺旋方式移动。它

  • 尝试移至左上角的圆圈
  • 如果不可能,请尝试移至右上角的圆圈
  • 如果不可能,请尝试移至右侧的圆圈
  • 如果不可能,请尝试移至右下角的圆圈
  • 如果不可能,请尝试移至左下角的圆圈

SpiralCat和Agamemnon:

螺旋猫vs阿伽门农

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]避免超出范围(在这种情况下不应发生)。
flawr

@flawr,该死的。单词选择不正确。我的意思是这只猫不是很聪明。有时,即使有出路,
这只猫

@flawr,我必须使用turns[i%6]吗?我的意思是,takeTurn如果猫被挡住了就不会叫,对吧?
Spikatrix

不,我认为您的意思是您在程序中遇到错误,因此我正在寻找可能的原因。但是,你是对的,很明显(如果一切正确的)i>=6不应该发生的。
瑕疵

2

狂猫

RabidCat患有恐惧症,因此他害怕水桶。他找到最近的一个,朝相反的方向奔跑。

RabidCat vs 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

选择猫

对于每种可能的新猫位置,我们都会检查其优势并选择最佳的猫。良善是两个最佳邻居细胞的功能,它们比我们计算得分的位置离猫的位置更远。我们只使用两个牢房,因为一个牢房可以被挡住,而猫只需要一个就可以逃脱。我们的职能部门选择了两个相当好的单元,而不是一个好和一个坏。带桶的位置的得分为0,而最远的自由单元格的得分为1。

ChoiceCat似乎比目前的猫得分更高。

ChoiceCat与ChoiceCatcher:

ChoiceCat vs 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

笨猫

这样做只是为了测试控制器。猫尽可能向右移动,否则向随机方向移动。

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

兰德猫

这样做只是为了测试控制器。猫只是随机移动。

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和Agamemnon:

直猫vs阿伽门农

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;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.