Asymmetrical KOTH: Catch the Cat (Catcher Thread)

17

Asymmetrical KOTH: Catch the Cat

UPDATE : File inti diperbarui (termasuk submisisons baru) karena Controller.java tidak menangkap Pengecualian (hanya kesalahan). Itu sekarang menangkap kesalahan dan pengecualian dan juga mencetaknya.

Tantangan ini terdiri dari dua utas, inilah utas penangkap, utas kucing dapat ditemukan di sini .

Pengontrol dapat diunduh di sini .

Ini adalah KOTH asimetris: Setiap kiriman adalah kucing atau penangkap . Ada permainan antara masing-masing pasangan masing-masing kucing dan penangkap. Kucing dan penangkap memiliki peringkat terpisah.

Penangkap

Ada kucing di kisi heksagonal. Tugas Anda adalah menangkapnya secepat mungkin. Setiap belokan, Anda dapat menempatkan ember air di satu sel kisi untuk mencegah kucing masuk ke sana. Tapi kucing itu (mungkin) tidak sebodoh itu, dan setiap kali Anda meletakkan ember, kucing itu akan pindah ke sel kisi lain. Karena kisi-kisi itu heksagonal, kucing dapat pergi ke 6 arah yang berbeda. Tujuan Anda adalah mengelilingi kucing dengan ember air, semakin cepat semakin baik.

Kucing

Anda tahu penangkap ingin menangkap Anda dengan menempatkan ember air di sekitar Anda. Tentu saja Anda mencoba menghindar, tetapi karena Anda adalah kucing malas (seperti kucing), Anda justru mengambil satu langkah pada saat itu. Ini berarti Anda tidak dapat tinggal di tempat yang sama dengan Anda, tetapi Anda harus pindah ke salah satu dari enam tempat di sekitarnya. Setiap kali Anda melihat bahwa penangkap menempatkan ember air baru Anda pergi ke sel lain. Tentu saja Anda berusaha menghindar selama mungkin.

Kisi

Kisi-kisi adalah heksagonal, tetapi karena kami tidak memiliki struktur data heksagonal, kami mengambil 11 x 11larik 2d persegi dan meniru 'perilaku' heksagonal yang kucing hanya bisa bergerak dalam 6 arah:

masukkan deskripsi gambar di sini

Topologi adalah toroidal, itu berarti jika Anda menginjak sel 'luar' dari array, Anda hanya akan ditransfer ke sel yang sesuai di sisi lain array.

Permainan

Kucing mulai keluar pada posisi yang diberikan di grid. Penangkap dapat melakukan langkah pertama, kemudian kucing dan penangkapnya bergantian bergerak sampai kucing ditangkap. Jumlah langkah adalah skor untuk game itu. Kucing mencoba untuk mendapatkan skor sebesar mungkin, penangkap mencoba untuk mendapatkan skor serendah mungkin. Jumlah rata-rata dari semua game yang Anda ikuti akan menjadi skor pengajuan Anda. Ada dua peringkat yang terpisah, satu untuk kucing, satu untuk penangkap.

Pengendali

Kontroler yang diberikan ditulis dalam Java. Sebagai penangkap atau kucing Anda masing-masing harus menyelesaikan masing-masing kelas Java (sudah ada beberapa contoh primitif) dan letakkan dalam playerspaket (dan perbarui daftar kucing / penangkap di kelas Pengendali), tetapi Anda juga dapat menulis fungsi tambahan di dalam kelas itu. Pengontrol hadir dengan masing-masing dua contoh kerja dari kelas kucing / penangkap sederhana.

Bidang ini adalah array 11 x 112D intyang menyimpan nilai status sel saat ini. Jika sebuah sel kosong, ia memiliki nilai 0, jika ada kucing ia memiliki nilai -1dan jika ada sebuah ember ada sebuah 1.

Ada beberapa fungsi yang diberikan yang dapat Anda gunakan: isValidMove()/ isValidPosition()untuk memeriksa apakah gerakan Anda (kucing) / posisi (penangkap) valid.

Setiap kali giliran Anda, fungsi Anda takeTurn()dipanggil. Argumen tersebut berisi salinan kisi saat ini dan memiliki metode seperti read(i,j)membaca sel di (i,j), serta isValidMove()/ isValidPosition()memeriksa validitas jawaban Anda. Ini juga mengelola pembungkus topologi toroidal, yang berarti bahkan jika grid hanya 11 x 11, Anda masih dapat mengakses sel (-5,13).

Metode harus mengembalikan intarray dari dua elemen, yang mewakili kemungkinan pergerakan. Untuk kucing ini adalah {-1,1},{0,1},{-1,0},{1,0},{0,-1},{1,-1}yang mewakili posisi relatif dari mana kucing ingin pergi, dan para penangkap mengembalikan koordinat absolut di mana mereka ingin menempatkan ember {i,j}.

Jika metode Anda menghasilkan langkah yang tidak valid, kiriman Anda akan didiskualifikasi. Langkah tersebut dianggap tidak valid, jika di tempat tujuan Anda sudah menjadi ember atau langkah tersebut tidak diperbolehkan / tujuan sudah ditempati (sebagai kucing), atau jika sudah ada ember / kucing (sebagai penangkap). Anda dapat memeriksa itu sebelumnya dengan fungsi yang diberikan.

Kiriman Anda harus bekerja cukup cepat. Jika metode Anda membutuhkan waktu lebih dari 200 ms untuk setiap langkah, metode itu juga akan didiskualifikasi. (Lebih disukai jauh lebih sedikit ...)

Program diperbolehkan untuk menyimpan informasi di antara langkah-langkah.

Pengajuan

  • Anda dapat membuat kiriman sebanyak yang Anda inginkan.
  • Harap jangan secara signifikan mengubah pengiriman yang sudah Anda kirim.
  • Silakan setiap pengiriman dalam jawaban baru.
  • Setiap pengiriman sebaiknya memiliki nama yang unik.
  • Kiriman harus terdiri dari kode kelas Anda serta deskripsi yang memberi tahu kami bagaimana kiriman Anda bekerja.
  • Anda dapat menulis baris <!-- language: lang-java -->sebelum kode sumber Anda untuk mendapatkan penyorotan sintaksis otomatis.

Mencetak gol

Semua kucing akan bersaing melawan semua penangkap dalam jumlah yang sama beberapa kali. Saya akan mencoba memperbarui skor saat ini sering, pemenang akan ditentukan ketika aktivitas telah menurun.

Tantangan ini terinspirasi oleh game flash lama ini

Terima kasih @PhiNotPi untuk pengujian dan memberikan umpan balik yang membangun.

Skor Saat Ini (100 Game per pasangan)

Name              Score      Rank   Author

RandCatcher       191674     8      flawr   
StupidFill        214246     9      flawr
Achilles          76820      6      The E
Agamemnon         74844      5      The E
CloseCatcher      54920      4      randomra
ForwordCatcher    94246      7      MegaTom  
Dijkstra          46500      2      TheNumberOne
HexCatcher        48832      3      randomra
ChoiceCatcher     43828      1      randomra

RandCat           77928      7      flawr
StupidRightCat    81794      6      flawr
SpiralCat         93868      5      CoolGuy
StraightCat       82452      9      CoolGuy
FreeCat           106304     3      randomra
RabidCat          77770      8      cain
Dijkstra's Cat    114670     1      TheNumberOne
MaxCat            97768      4      Manu
ChoiceCat         113356     2      randomra
cacat
sumber
Program apa yang membuat animasi?
MegaTom
Animasi ini hanya GUI (saat memulai controller yang harus Anda atur PRINT_STEPS = true, pengaturan lebih rinci dalam file MyFrame.java). Lalu saya merekam ini dengan LICEcap dan mengeditnya dengan GIMP . Jika Anda memiliki pertanyaan lebih lanjut, tanyakan saja!
flawr
Jika Anda menambahkan input pengguna ke controller, itu bisa membuat perangkat lunak yang bagus dengan GUI dan bot sudah ditulis. Akan juga menarik untuk melihat seberapa banyak manusia dapat memecahkan / menyalahgunakan strategi bot tertentu.
randomra
Juga, bisakah bot saya menyimpan informasi dari pertandingan sebelumnya untuk mencoba menemukan urutan gerakan yang lebih baik terhadap bot yang sama? Saya kira bukan karena semakin baik semakin banyak putaran yang Anda lakukan. Itu juga harus menebak apakah itu bermain melawan bot baru, jadi urutan yang berjalan juga penting.
randomra
1
Mengapa skor kucing tidak tertata?
Spikatrix

Jawaban:

6

Achilles

Achilles tidak terlalu pintar tetapi dia sangat efisien. Pertama-tama dia menghentikan kucing dari menggunakan bungkus papan, lalu dia membagi papan menjadi dua. Lalu ia terus membagi bagian papan yang menjadi bagian kucing sampai kucing itu terperangkap.

Demonstrasi RandCat vs Achilles

randcat vs achilles

package players;
/**
 * @author The E
 */
import main.*;



public class Achilles implements Catcher
{
    public Achilles() {

    }
    @Override
    public String getName() {

        return "Achilles";
    }

    @Override
    public int[] takeTurn(Field f) {
        try{
        if(f.read(0, f.SIZE-1)!=Field.BUCKET)
        {
            //Make the first line

            for(int j = 0; j<f.SIZE; j++)
            {
                if(f.read(0, j) == Field.EMPTY)
                {
                    return new int[]{0,j};
                }
            }
            return WasteGo(f);

        }
        else if (f.read(f.SIZE-1, 0)!=Field.BUCKET)
        {
            //Make the second line
            for(int i = 0; i<f.SIZE; i++)
            {
                if(f.read(i, 0) == Field.EMPTY)
                {
                    return new int[]{i,0};
                }
            }
            //The cat got in the way
            for(int j = 0; j<f.SIZE; j++)
            {
                if(f.read(1, j) == Field.EMPTY)
                {
                    return new int[]{1,j};
                }
            }
            return WasteGo(f);
        }
        else
        {
            return TrapCat(1,1,f.SIZE-1, f.SIZE-1, false, f);

        }
        }
        catch (Exception e)
        {
            return WasteGo(f);
        }
    }
    private int[] TrapCat(int i1, int j1, int i2, int j2, Boolean direction, Field f) {
        for(int a = 0; a<f.SIZE+10; a++)
        {
            if(direction)
            {

                int height = j2-j1+1;
                int row = j1 + height/2;
                for(int i = i1; i<=i2; i++)
                {
                    if(f.read(i, row)==Field.EMPTY)
                    {
                        return new int[]{i,row};
                    }
                }

                    //Done that Row
                    //Find cat
                    if(f.findCat()[1]>row)
                    {
                        //he's above the line
                        j1 = row+1;
                        direction = !direction;
                        //return TrapCat(i1, row+1, i2, j2, !direction, f);
                    }
                    else
                    {
                        //he's below the line
                        j2 = row - 1;
                        direction = !direction;
                        //return TrapCat(i1, j1, i2, row-1, !direction, f);
                    }


            }
            else
            {
                int bredth = i2-i1+1;
                int column = i1 + bredth/2;
                //Continue making the line
                for(int j = j1; j<=j2; j++)
                {
                    if(f.read(column,j)==Field.EMPTY)
                    {
                        return new int[]{column,j};
                    }
                }

                    //Done that Column
                    //Find cat
                    if(f.findCat()[0]>column)
                    {
                        //he's right of the line
                        i1 = column + 1;
                        direction = !direction;
                        //return TrapCat(column+1, j1, i2, j2, !direction, f);
                    }
                    else
                    {
                        //he's left of the line
                        i2 = column -1;
                        direction = !direction;
                        //return TrapCat(i1, j1, column-1, j2, !direction, f);
                    }

            }
        }
        return WasteGo(f);
    }
    private int[] WasteGo(Field f) {
        for (int i = 0; i<f.SIZE;i++)
        {
            for(int j=0;j<f.SIZE;j++)
            {
                if(f.read(i,j)==Field.EMPTY)
                {
                    return new int[]{i,j};
                }
            }
        }
        //Something drastic happened
        return new int[]{0,0};
    }



}
euanjt
sumber
Nah yang mana sekarang, Achilles atau Hector? (Atau seseorang dengan gangguan identitas disosiosiatif? =)
flawr
@ flawr Achilles, lol Saya mengubah nama setengah jalan (itu lebih tepat untuk nama Achilles penangkap dan kucing Hector) tetapi lupa untuk mengubah java - itu yang terjadi ketika Anda memprogram setelah teh :(
euanjt
Tapi Hector lebih suka menjadi nama anjing =) Terima kasih atas kiriman Anda bekerja hebat. Saya harap Anda tidak keberatan bahwa saya juga memasukkan 'mukadimah' dalam kode Anda.
flawr
Ya tidak masalah Hector memang terdengar seperti nama anjing ...
euanjt
Saya baru saja menjalankan simulasi (10.000 game untuk setiap pasangan) dan Achilles didiskualifikasi, karena StackOverflowError berulang. Saya pikir rekursi tidak berakhir: pastebin.com/9n6SQQnd
flawr
5

Agamemnon

Agamemnon membelah area kucing menjadi dua dengan garis vertikal sampai kucing hanya memiliki strip lebar 2 untuk bergerak, pada titik mana ia menjebak kucing.

Agamemnon vs RandCat:

masukkan deskripsi gambar di sini

package players;
/**
 * @author The E
 */
import main.*;



    public class Agamemnon implements Catcher {
        boolean up = true;
        @Override
        public String getName() {
            return "Agamemnon";
        }

        @Override
        public int[] takeTurn(Field f) {
            //First Make Line in column 1
            for(int j = 0; j<f.SIZE; j++)
            {
                if(f.read(0, j)==Field.EMPTY)
                {
                    return new int[]{0,j};
                }
            }
            //Then in column SIZE/2
            for(int j = 0; j<f.SIZE; j++)
            {
                if(f.read(f.SIZE/2, j)==Field.EMPTY)
                {
                    return new int[]{f.SIZE/2,j};
                }
            }
            //Then work out where the cat is
            int left, right;
            int cati = f.findCat()[0];
            if(cati<f.SIZE/2)
            {
                left = 1;
                right = f.SIZE/2-1;
            }
            else
            {
                left = f.SIZE/2+1;
                right = f.SIZE-1;
            }
            while(right-left>1)
            {
                //If the cat is not in a two width column
                //Split the area the cat is in in half
                int middleColumn = (left+right)/2;
                for(int j = 0; j<f.SIZE; j++)
                {
                    if(f.read(middleColumn, j)==Field.EMPTY)
                    {
                        return new int[]{middleColumn,j};
                    }
                }
                //If we got here we had finished that column
                //So update left and/or right
                if(cati<middleColumn)
                {
                    //he's left of the middle Column
                    right = middleColumn - 1;
                }
                else
                {
                    //he's right of the middle Column
                    left = middleColumn+1;
                }
                //Repeat
            }
            //Otherwise try to trap the cat
            //Make a line up and down on the opposite side of the cat
            int catj = f.findCat()[1];
            if(left!=right){
                if(cati==left)
                {
                    if(f.read(right, catj)==Field.EMPTY)
                    {
                        return new int[]{right, catj};
                    }
                    if(f.read(right, catj-1)==Field.EMPTY)
                    {
                        return new int[]{right, catj-1};
                    }
                    if(f.read(right, catj+1)==Field.EMPTY)
                    {
                        return new int[]{right, catj+1};
                    }


                }
                else
                {
                    if(f.read(left, catj)==Field.EMPTY)
                    {
                        return new int[]{left, catj};
                    }
                    if(f.read(left, catj-1)==Field.EMPTY)
                    {
                        return new int[]{left, catj-1};
                    }
                    if(f.read(left, catj+1)==Field.EMPTY)
                    {
                        return new int[]{left, catj+1};
                    }

                }
            }
            //Alternate between above and below
            if(up)
            {
                up = !up;
                if(f.read(cati, catj+1)==Field.EMPTY)
                {

                    return new int[]{cati, catj+1};
                }
            }
            up = !up;
            if(f.read(cati, catj-1)==Field.EMPTY)
            {

                return new int[]{cati, catj-1};
            }
            return WasteGo(f);
        }

        private int[] WasteGo(Field f) {
            for (int i = 0; i<f.SIZE;i++)
            {
                for(int j=0;j<f.SIZE;j++)
                {
                    if(f.read(i,j)==Field.EMPTY)
                    {
                        return new int[]{i,j};
                    }
                }
            }
            //Something drastic happened
            return new int[]{0,0};
        }
    }

Penangkap ini secara konsisten lebih baik daripada Achilles dan saya pikir dia cukup berbeda untuk mendapatkan jawaban baru.

euanjt
sumber
Solusi yang sangat bagus, saya yakin Achilles sudah mendekati optimal, tetapi sekarang saya pikir bahkan Agamemnon dapat ditingkatkan sedikit =)
flawr
Ya, Agamemnon memiliki algoritma penjebak akhir game yang jauh lebih baik daripada Achilles, tapi saya cukup yakin ada beberapa penyesuaian ... Sekarang saya akan mencoba dan bekerja pada kucing :)
euanjt
@ flawr tweak yang sangat kecil ditambahkan untuk mempercepat penangkapan dalam beberapa kasus khusus, ini tidak mempengaruhi animasi di sini (walaupun saya pikir itu mungkin mempengaruhi animasi
SpiralCat
Pertanyaan! Apa yang terjadi jika Anda akan menutup garis, tetapi kucing berdiri di tempat terakhir?
Tn. Llama
@Lama Lama mulai membuat baris berikutnya seolah-olah baris itu telah diisi (mis. Kucing itu sebenarnya sebuah ember) - bukan penggunaan belokan yang paling efektif tetapi jarang terjadi sehingga tidak terlalu penting- kucing kemudian harus pindah pada belokan berikutnya sehingga saya bisa meletakkan ember saya di sana
euanjt
5

HexCatcher

Jika penangkap bisa mendapatkan kucing di bagian dalam segi enam besar dengan 3 unit sisi di mana sudut-sudut segi enam sudah ditempati oleh ember, penangkap dapat menjaga kucing di daerah ini dan menangkapnya. Segi enam terlihat seperti ini:

masukkan deskripsi gambar di sini

Inilah yang HexCatcher coba capai. Secara mental ubin bidang dengan heksagon besar ini dengan cara bahwa setiap sel sudut adalah bagian dari 3 heksagon besar.

Jika ada kesempatan untuk menjaga kucing di area saat ini dengan menghubungkan dua sudut di sebelah kucing, bot akan melakukan itu. (Misalnya dalam gambar jika kucing di 7,5 kita memilih 7,6 bahkan jika hanya 6,6 dan 8,5 sel belum ditempati.)

Jika sebelumnya bukan opsi, kami memilih untuk memainkan sudut yang merupakan bagian dari area tempat kucing berada. Jika semua sudut seperti itu sudah dipilih (seperti dalam gambar) kami memilih sel di sebelah kucing.

Beberapa perbaikan kecil mungkin dilakukan, seperti menangani lilitan dengan lebih baik (ubin pecah di sana) atau melakukan gerakan pasangan terakhir secara optimal. Saya mungkin melakukan beberapa di antaranya. Jika tidak diizinkan, saya hanya akan menambahkannya (keluar dari kompetisi) untuk yang tertarik.

DijkstrasCat vs HexCatcher:

masukkan deskripsi gambar di sini

package players;
/**
 * @author randomra
 */
import main.Field;

public class HexCatcher implements Catcher {
    public String getName() {
        return "HexCatcher";
    }

    final int[][] o = { { -1, 1 }, { 0, 1 }, { -1, 0 }, { 1, 0 }, { 0, -1 },
            { 1, -1 } };// all valid moves
    final int[][] t = { { -2, 2 }, { 0, 2 }, { -2, 0 }, { 2, 0 }, { 0, -2 },
            { 2, -2 } };// all valid double moves in one direction
    final int[][] h = { { -1, 2 }, { -2, 1 }, { -1, -1 }, { 1, -2 }, { 2, -1 },
            { 1, 1 } };// all valid moves in not one direction
    int opp = 0;

    public int[] takeTurn(Field f) {
        int[] p = f.findCat();
        // center of the hexagon the cat is in
        int[] c = { ((int) p[0] / 3) * 3 + 1, ((int) p[1] / 3) * 3 + 1 };
        // change priority of catching direction at every turn
        opp = 1 - opp;

        // check missing corner piece next to cat
        for (int i = 0; i < 6; i++) {
            int ind = (i + opp * 3) % 6;
            boolean close = false;
            for (int k = 0; k < 6; k++) {
                if (c[0] + h[ind][0] == p[0] + o[k][0]
                        && c[1] + h[ind][1] == p[1] + o[k][1]) {
                    close = true;
                }
            }
            if (f.read(c[0] + h[ind][0], c[1] + h[ind][1]) == 0 && close) {
                return new int[] { c[0] + h[ind][0], c[1] + h[ind][1] };
            }
        }
        // cut off escape route if needed
        for (int i = 0; i < 6; i++) {
            int ind = (i + opp * 3) % 6;
            if (f.read(c[0] + o[ind][0], c[1] + o[ind][1]) == -1
                    && f.read(c[0] + t[ind][0], c[1] + t[ind][1]) == 0) {
                return new int[] { c[0] + t[ind][0], c[1] + t[ind][1] };
            }
        }
        // check any missing corner piece in the area
        for (int i = 0; i < 6; i++) {
            int ind = (i + opp * 3) % 6;
            if (f.read(c[0] + h[ind][0], c[1] + h[ind][1]) == 0) {
                return new int[] { c[0] + h[ind][0], c[1] + h[ind][1] };
            }
        }
        // choose an empty cell next to the cat
        for (int i = 0; i < 6; i++) {
            int ind = (i + opp * 3) % 6;
            if (f.read(p[0] + o[ind][0], p[1] + o[ind][1]) == 0) {
                return new int[] { p[0] + o[ind][0], p[1] + o[ind][1] };
            }
        }
        return null;
    }
}
randomra
sumber
3

CloseCatcher

Pilih salah satu posisi di mana kucing bisa melangkah di langkah berikutnya. Ia memilih jalur yang akan memberikan jalur paling mungkin setelah 3 langkah untuk kucing jika ia akan pindah ke sana dan bidang tidak akan berubah.

Kode hampir identik dengan entri Cat saya, FreeCat , yang memilih arah dengan cara yang sangat mirip.

SpiralCat vs CloseCatcher:

SpiralCat vs CloseCatcher

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

import main.Field;
import java.util.Arrays;

public class CloseCatcher implements Catcher {
    public String getName() {
        return "CloseCatcher";
    }

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

    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;
            }
        }
        int[] bestPos = { pos[0] + bestMove[0], pos[1] + bestMove[1] };
        return bestPos;
    }

    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
sumber
+1 bagus. CloseCatcher dengan mudah menangkap StraightCat
Spikatrix
3

Dijkstra

Dia tidak terlalu suka kucing (:v{ >

FreeCat vs Dijkstra (perlu diperbarui) :

masukkan deskripsi gambar di sini

package players;

import main.Controller;
import main.Field;

import java.util.*;

/**
 * @author TheNumberOne
 *
 * Catches the cat.
 */

public class Dijkstra implements Catcher{

    private static final int[][][] CACHE;

    static {
        CACHE = new int[Controller.FIELD_SIZE][Controller.FIELD_SIZE][2];
        for (int x = 0; x < Controller.FIELD_SIZE; x++){
            for (int y = 0; y < Controller.FIELD_SIZE; y++){
                CACHE[x][y] = new int[]{x,y};
            }
        }
    }

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

    @Override
    public int[] takeTurn(Field f) {
        long startTime = System.nanoTime();

        final int[] theCat = f.findCat();
        int[] bestMove = {-1,1};
        int[] bestOpenness = {Integer.MAX_VALUE, 0};
        List<int[]> possiblePositions = new ArrayList<>();
        for (int x = 0; x < 11; x++){
            for (int y = 0; y < 11; y++){
                int[] pos = {x,y};
                if (f.isValidPosition(pos)){
                    possiblePositions.add(pos);
                }
            }
        }
        Collections.sort(possiblePositions, new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return distance(o1, theCat) - distance(o2, theCat);
            }
        });
        for (int i = 0; i < possiblePositions.size() && System.nanoTime() - startTime < Controller.MAX_TURN_TIME/2; i++){
            int[] pos = possiblePositions.get(i);
            int before = f.field[pos[0]][pos[1]];
            f.placeBucket(pos);
            int[] openness = openness(theCat, f, true);
            if (openness[0] < bestOpenness[0] ||
                    (openness[0] == bestOpenness[0] &&
                            (openness[1] > bestOpenness[1])
                    )
                    ){
                bestOpenness = openness;
                bestMove = pos;
            }
            f.field[pos[0]][pos[1]] = before;
        }
        return bestMove;
    }


    /**
     *
     * @param pos The pos to calculate the openness of.
     * @param f The field to use.
     * @return Two integers. The first integer represents the number of reachable hexagons.
     * The second integer represents how strung out the squares are relative to the pos.
     */
    public static int[] openness(int[] pos, Field f, boolean catZeroWeight){
        Map<int[], Integer> lengths = new HashMap<>();
        PriorityQueue<int[]> open = new PriorityQueue<>(10,new Comparator<int[]>() {
            Map<int[], Integer> lengths;
            @Override
            public int compare(int[] o1, int[] o2) {
                return lengths.get(o1) - lengths.get(o2);
            }
            public Comparator<int[]> init(Map<int[], Integer> lengths){
                this.lengths = lengths;
                return this;
            }
        }.init(lengths));
        Set<int[]> closed = new HashSet<>();
        lengths.put(pos, catZeroWeight ? 0 : 6 - pointsAround(pos, f).size());
        open.add(pos);
        while (open.size() > 0){
            int[] top = open.remove();
            if (closed.contains(top)){
                continue;
            }
            closed.add(top);
            int l = lengths.get(top);
            List<int[]> pointsAround = pointsAround(top, f);

            for (ListIterator<int[]> iter = pointsAround.listIterator(); iter.hasNext();){
                int[] point = iter.next();
                if (closed.contains(point)){
                    iter.remove();
                }
            }

            for (int[] p : pointsAround){
                int length = l + 7 - pointsAround(p, f).size();
                if (lengths.containsKey(p)){
                    length = Math.min(length, lengths.get(p));
                }
                lengths.put(p, length);
                open.add(p);
            }
        }
        int sum = 0;
        for (int integer : lengths.values()){
            sum += integer;
        }
        return new int[]{lengths.size(),sum};
    }

    public static int distance(int[] p1, int[] p2){
        p2 = Arrays.copyOf(p2, 2);
        while (p2[0] < p1[0]){
            p2[0] += 11;
        }
        while (p2[1] < p2[0]){
            p2[1] += 11;
        }
        int lowestDistance = 0;
        for (int dx = 0; dx == 0; dx -= 11){
            for (int dy = 0; dy == 0; dy -= 11){
                lowestDistance = Math.min(lowestDistance,Math.min(Math.abs(p1[0]-p2[0]-dx),Math.min(Math.abs(p1[1]-p2[1]-dy),Math.abs(p1[0]+p1[1]-p2[0]-dx-p2[1]-dy))));
            }
        }
        return Math.min(Math.abs(p1[0]-p2[0]),Math.min(Math.abs(p1[1]-p2[1]),Math.abs(p1[0]+p1[1]-p2[0]-p2[1])));
    }

    public static int[] normalize(int[] p){
        return CACHE[(p[0]%11+11)%11][(p[1]%11+11)%11];
    }

    public static List<int[]> pointsAround(int[] p, Field f){
        int[] cat = f.findCat();
        List<int[]> locations = new ArrayList<>();
        for (int[] move : possibleMoves){
            int[] location = normalize(new int[]{p[0]+move[0], p[1] + move[1]});
            if (f.isValidPosition(location) || Arrays.equals(cat, location)){
                locations.add(location);
            }
        }
        return locations;
    }
}

Cara dia mencoba menangkap kucing:

Dia menganalisis semua kotak papan dan mencoba untuk menemukan kotak yang meminimalkan keterbukaan papan, dan memaksimalkan seberapa banyak papan digantung; dalam kaitannya dengan kucing. Keterbukaan dan kekakuan papan dihitung menggunakan modifikasi dari algoritma yang terkenal .

Keterbukaan:

Keterbukaan papan relatif terhadap suatu posisi adalah jumlah posisi yang dapat dijangkau dari posisi itu.

Ketegaran:

Ketegangan papan relatif terhadap suatu posisi adalah jumlah jarak antara posisi yang dapat dijangkau dan posisi.

Dengan pembaruan terakhir:

Sekarang, dia jauh lebih baik dalam menangkap FreeCat dan kucingnya sendiri semua kucing. Sayangnya, ia juga jauh lebih buruk dalam menangkap kucing gila yang tidak kooperatif. Dia bisa ditingkatkan dengan mendeteksi apakah kucing itu salah satu yang gila dan kemudian bertindak sebagai CloseCatcher.

Bug diperbaiki.

TheNumberOne
sumber
Dapat mengkonfirmasi itu bekerja sejauh ini, tetapi sejauh ini yang paling lambat tapi salah satu yang terbaik sejauh ini saya pikir. Membutuhkan 134 detik untuk 100 pertandingan melawan RandCat sambil melakukan total hanya 4406 gerakan! Saya pikir saya harus membiarkan pc saya berjalan semalam di salah satu hari berikutnya ... Bisakah Anda memberi tahu kami sedikit lebih banyak tentang cara kerjanya?
flawr
@ flawr Menambahkan deskripsi.
TheNumberOne
Masih tidak bekerja untuk saya. Memberi saya satu kesalahan: error: cannot infer type arguments for PriorityQueue<>di baris ini PriorityQueue<int[]> open = new PriorityQueue<>(new Comparator<int[]>() {.
Spikatrix
@ CoolGuy Apakah Anda menggunakan java 6? Saya pikir Anda perlu memperbarui JDK Anda.
TheNumberOne
@ CoolGuy Anda juga dapat menempatkan di int[]antara dua berlian kosong setelah PriorityQueue.
TheNumberOne
2

ForwordCatcher

Tempatkan ember di depan kucing, atau jika diambil, letakkan di belakang.

RabidCat vs ForwordCatcher:

RabidCat vs ForwordCatcher:

package players;

import main.Field;
import java.util.Arrays;

public class ForwordCatcher implements Catcher {
    public String getName() {
        return "ForwordCatcher";
    }

    private int[] lastPos = {0,0};

    public int[] takeTurn(Field f) {
        int[] temp = lastPos;
        int[] pos = f.findCat();
        lastPos = pos;
        int[] Move = {pos[0]*2-temp[0], pos[1]*2-temp[1]};
        if(f.isValidPosition(Move)){return Move;}
        if(f.isValidPosition(temp)){return temp;}
        Move[0] = pos[0];Move[1] = pos[1]+1;
        return Move;
    }
}
MegaTom
sumber
1
Ada beberapa kesalahan, yang membawa saya pada asumsi bahwa Anda tidak menguji program Anda. Silakan perbaiki ...
flawr
@ flawr diperbaiki. maaf tentang kesalahannya. Saya tidak mengujinya dan Java saya tidak bagus.
MegaTom
Bagus, sejauh ini semuanya berjalan lancar, tapi saya masih tidak yakin apakah itu akan selalu menghasilkan pergerakan yang valid =)
flawr
1
@ flawr Ruang di belakang kucing akan selalu kosong untuk si penangkap :)
TheNumberOne
2

ChoiceCatcher

Menggunakan mekanisme penilaian yang sama dengan entri ChoiceCat saya . Ada sedikit modifikasi yang membantu untuk memilih sel yang relevan pada beberapa langkah pertama karena ChoiceCat tidak peduli dengan beberapa ember pertama karena tidak melihat mereka sebagai ancaman.

ChoiceCatcher tampaknya mencetak skor yang jauh lebih baik daripada para penangkap saat ini.

ChoiceCat vs ChoiceCatcher:

ChoiceCat vs ChoiceCatcher

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

import main.Field;

public class ChoiceCatcher implements Catcher {

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

    @Override
    public int[] takeTurn(Field f) {
        int[] bestPos = null;
        double bestPosValue = Double.MAX_VALUE;
        for (int i = 0; i < f.SIZE; i++) {
            for (int j = 0; j < f.SIZE; j++) {
                if (f.read(i, j) == Field.EMPTY) {
                    Field myField = new Field(f);
                    myField.placeBucket(new int[] { i, j });
                    double posValue = catTurnValue(myField);
                    if (posValue < bestPosValue) {
                        bestPosValue = posValue;
                        bestPos = new int[] { i, j };
                    }
                }
            }
        }
        return bestPos;
    }

    private double catTurnValue(Field f) {

        int[] pos = f.findCat();
        double[] values = new double[6];
        int count=0;
        for (int[] t : turns) {
            int[] currPos = { pos[0] + t[0], pos[1] + t[1] };
            double moveValue = movePosValue(currPos, f);
            values[count++]=moveValue;
        }
        Arrays.sort(values);
        return values[5];
    }

    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;
        int maxRoutes = 2;
        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, maxRoutes); i++) {
            fp *= product[5 - i];
        }
        double fp2 = 1;
        for (int i = 0; i < Math.min(count, 6); i++) {
            fp2 *= product[5 - i];
        }
        double retValue = Math.min(count, maxRoutes) + fp;
        double retValue2 = Math.min(count, 6) + fp2;
        return -retValue - retValue2 / 1000000;
    }

}
randomra
sumber
1

RandCatcher

Ini dibuat hanya untuk menguji controller dan hanya secara acak menempatkan bucket (sangat tidak efisien).

package players;

import main.Field;

public class RandCatcher implements Catcher {
    public String getName(){
        return "RandCatcher";
    }
    public int[] takeTurn(Field f){
        int[] pos = {0,0};
        do {
            pos[0] = (int) (Math.random()*f.SIZE);
            pos[1] = (int) (Math.random()*f.SIZE);
        } while( f.isValidPosition(pos)==false );
        return pos;
    }

}
cacat
sumber
1

StupidFillCatcher

Ini dibuat hanya untuk menguji controller. Itu hanya mengisi kolom demi kolom sampai kucing ditangkap.

package players;

import main.Field;

public class StupidFillCatcher implements Catcher {
    public String getName(){
        return "StupidFillCatcher";
    }
    public int[] takeTurn(Field f){
        for(int i=0; i < f.SIZE; i++){
            for(int j=0; j < f.SIZE; j++){
                if(f.isValidPosition(new int[] {i,j})){
                    return new int[] {i,j};
                }
            }
        }
        return new int[] {0,0};
    }

}
cacat
sumber