KOTH asimmetrico: Catch the Cat (Catcher Thread)


17

KOTH asimmetrico: cattura il gatto

AGGIORNAMENTO : I file gist vengono aggiornati (comprese le nuove invii) poiché Controller.java non ha rilevato eccezioni (solo errori). Ora rileva errori ed eccezioni e li stampa anche.

Questa sfida consiste in due thread, questo è il thread catcher, il thread cat può essere trovato qui .

Il controller può essere scaricato qui .

Questo è un KOTH asimmetrico: ogni invio è un gatto o un ricevitore . Ci sono giochi tra ogni coppia di ciascuno un gatto e un ricevitore. I gatti e i cacciatori hanno classifiche separate.

Catcher

C'è un gatto su una griglia esagonale. Il tuo compito è prenderlo il più velocemente possibile. Ad ogni giro, puoi posizionare un secchio d'acqua su una cella della griglia per impedire al gatto di poterci andare. Ma il gatto non è (forse) così stupido e ogni volta che si posiziona un secchio, il gatto si sposterà in un'altra cella della griglia. Poiché la griglia è esagonale, il gatto può andare in 6 direzioni diverse. Il tuo obiettivo è circondare il gatto con secchi d'acqua, più veloce è, meglio è.

Gatto

Sai che il ricevitore vuole catturarti mettendo secchi d'acqua intorno a te. Certo che cerchi di evadere, ma dato che sei un gatto pigro (come i gatti) fai esattamente un passo alla volta. Ciò significa che non puoi stare nello stesso posto in cui ti trovi, ma devi spostarti in uno dei sei punti circostanti. Ogni volta che vedi che il ricevitore ha messo un nuovo secchio d'acqua, vai in un'altra cella. Certo che cerchi di evadere il più a lungo possibile.

Griglia

La griglia è esagonale, ma poiché non abbiamo strutture di dati esagonali, prendiamo una 11 x 11matrice quadrata 2d e imitiamo il "comportamento" esagonale che il gatto può muovere solo in 6 direzioni:

inserisci qui la descrizione dell'immagine

La topologia è toroidale, ciò significa che se passi su una cella "esterna" dell'array, verrai trasferito nella cella corrispondente sull'altro lato dell'array.

Gioco

Il gatto inizia in una determinata posizione nella griglia. Il ricevitore può fare la prima mossa, quindi il gatto e il suo ricevitore si alternano fino a quando il gatto viene catturato. Il numero di passaggi è il punteggio per quel gioco. Il gatto cerca di ottenere un punteggio il più grande possibile, il ricevitore cerca di ottenere un punteggio il più basso possibile. La somma media di tutti i giochi a cui hai partecipato sarà il punteggio della tua presentazione. Esistono due classifiche separate, una per il gatto, una per i cacciatori.

controllore

Il controller dato è scritto in Java. Come un catcher o un gatto ognuno di essi deve implementare ciascuno una classe Java (ci sono già alcuni esempi primitivi) e inserirlo nel playerspacchetto (e aggiornare l'elenco di gatti / catcher nella classe Controller), ma è anche possibile scrivere funzioni aggiuntive all'interno di quella classe. Il controller viene fornito con ogni due esempi funzionanti di semplici classi gatti / catcher.

Il campo è un array 11 x 112D intche memorizza i valori degli stati correnti delle celle. Se una cella è vuota, ha valore 0, se c'è un gatto ha valore -1e se c'è un bucket c'è un 1.

Ci sono alcune funzioni che puoi usare: isValidMove()/ isValidPosition()sono per verificare se la tua mossa (gatto) / posizione (ricevitore) è valida.

Ogni volta che è il tuo turno, la tua funzione takeTurn()viene chiamata. L'argomento contiene una copia della griglia corrente e ha metodi come read(i,j)per leggere la cella (i,j), oltre a isValidMove()/ isValidPosition()verificare la validità della tua risposta. Questo gestisce anche il wrapping della topologia toroidale, il che significa che anche se la griglia è solo 11 x 11, è ancora possibile accedere alla cella (-5,13).

Il metodo dovrebbe restituire una intmatrice di due elementi, che rappresentano possibili mosse. Per i gatti sono questi {-1,1},{0,1},{-1,0},{1,0},{0,-1},{1,-1}che rappresentano la posizione relativa di dove il gatto vuole andare, e i raccoglitori restituiscono le coordinate assolute di dove vogliono posizionare un secchio {i,j}.

Se il tuo metodo produce una mossa non valida, il tuo invio verrà squalificato. La mossa è considerata non valida, se a destinazione è già un bucket o la mossa non è consentita / destinazione già occupata (come un gatto) o se esiste già un bucket / gatto (come un catcher). Puoi verificarlo prima con le funzioni indicate.

Il tuo invio dovrebbe funzionare abbastanza velocemente. Se il metodo impiega più di 200 ms per ogni passaggio, verrà anche squalificato. (Preferibilmente molto meno ...)

I programmi sono autorizzati a memorizzare informazioni tra i passaggi.

Inseriti

  • Puoi fare tutte le richieste che desideri.
  • Si prega di non modificare in modo significativo gli invii già inviati.
  • Per favore, ogni invio in una nuova risposta.
  • Ogni invio dovrebbe preferibilmente avere un nome univoco.
  • L'invio deve consistere nel codice della tua classe e in una descrizione che ci dice come funziona l'invio.
  • È possibile scrivere la riga <!-- language: lang-java -->prima del codice sorgente per ottenere l'evidenziazione automatica della sintassi.

punteggio

Tutti i gatti competeranno contro tutti i cacciatori lo stesso numero di volte. Proverò ad aggiornare frequentemente i punteggi attuali, i vincitori saranno determinati quando l'attività sarà diminuita.

Questa sfida è ispirata a questo vecchio gioco flash

Grazie a @PhiNotPi per il test e il feedback costruttivo.

Punteggi attuali (100 partite per accoppiamento)

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

Quale programma realizza le animazioni?
MegaTom,

L'animazione è solo la GUI (all'avvio del controller è necessario impostare PRINT_STEPS = trueimpostazioni più dettagliate nel file MyFrame.java). Quindi l'ho registrato con LICEcap e l' ho modificato con GIMP . Se hai ulteriori domande, basta chiedere!
flawr

Se aggiungi l'input dell'utente al controller, potrebbe creare un software piacevole con la GUI e i robot già scritti. Sarebbe anche interessante vedere quanto gli umani possono craccare / abusare delle strategie bot specifiche.
randomra,

Inoltre, il mio bot può conservare le informazioni della partita precedente per cercare di trovare una sequenza di mosse migliore rispetto allo stesso bot? Suppongo che non migliori perché più giri fai. Dovrebbe anche indovinare se sta giocando contro un nuovo bot, quindi anche l'ordine di esecuzione è importante.
randomra,

1
Perché i punteggi dei gatti non sono ordinati?
Spikatrix,

Risposte:


6

Achille

Achille non è troppo brillante ma è spietatamente efficiente. Prima impedisce al gatto di usare l'avvolgimento attorno alla tavola, quindi divide la tavola in due. Quindi continua a dividere la parte della tavola in cui il gatto è diviso a metà fino a quando il gatto rimane intrappolato.

Dimostrazione RandCat vs Achilles

Randcat vs Achille

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



}

Bene, quale è adesso, Achille o Ettore? (O qualcuno con un disturbo dissiociativo di identità? =)
flawr

@flawr Achilles, lol Ho cambiato il nome a metà (è più appropriato nominare il ricevitore Achilles e il gatto Hector) ma ho dimenticato di cambiare java - è quello che succede quando programmi dopo il tè :(
euanjt

Ma Hector preferirebbe essere un nome per cani =) Grazie per la tua presentazione funziona alla grande. Spero non ti dispiaccia che io includa anche il "preambolo" nel tuo codice.
Flawr,

Sì, nessun problema. Hector sembra il nome di un cane ...
euanjt,

Ho appena eseguito una simulazione (10000 giochi per ogni accoppiamento) e Achille è stato squalificato, a causa del ripetuto StackOverflowError. Penso che la ricorsione non sia finita: pastebin.com/9n6SQQnd
flawr

5

Agamennone

Agamennone divide a metà l'area dei gatti con una linea verticale fino a quando il gatto ha solo una striscia di larghezza 2 per muoversi, a quel punto intrappola il gatto.

Agamemnon vs RandCat:

inserisci qui la descrizione dell'immagine

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

Questo catcher fa costantemente meglio di Achille e penso che sia abbastanza diverso da giustificare una nuova risposta.


Soluzione molto bella, ero sicuro che Achille fosse vicino all'ottimale, ma ora penso che anche Agamennone potrebbe essere leggermente migliorato =)
flawr

Sì, Agamennone ha un algoritmo di trapping del gioco molto migliore di quello di Achille, ma sono abbastanza sicuro che ci siano alcune modifiche ... Ora proverò a lavorare su un gatto :)
euanjt

@flawr: aggiustamento molto piccolo aggiunto per accelerare la cattura in alcuni casi speciali, questo non influenza l'animazione qui (anche se penso che potrebbe influenzare l'animazione di SpiralCat)
euanjt

Domanda! Cosa succede se stai per chiudere una linea, ma il gatto è in piedi nell'ultimo punto?
Mr. Llama,

@ Mr.Llama inizia a fare la riga successiva come se quella riga fosse stata riempita (cioè il gatto era in realtà un secchio) - non è l'uso più efficace di una svolta ma accade molto raramente che non contenga davvero - il il gatto deve quindi spostarsi al suo turno successivo in modo da poter posizionare il mio secchio lì
euanjt

5

HexCatcher

Se il ricevitore può ottenere il gatto all'interno di un grande esagono con 3 unità laterali in cui gli angoli dell'esagono sono già occupati da secchi, il ricevitore può tenere il gatto in quest'area e catturarlo. L'esagono si presenta così:

inserisci qui la descrizione dell'immagine

Questo è ciò che HexCatcher cerca di ottenere. Piastrella mentalmente il campo con questi grandi esagoni in modo che ogni cella d'angolo faccia parte di 3 grandi esagoni.

Se c'è la possibilità di mantenere il gatto nell'area corrente collegando due angoli vicino al gatto, il robot lo farà. (Ad esempio nell'immagine se il gatto è a 7,5 scegliamo 7,6 anche se solo le celle 6,6 e 8,5 sono ancora occupate.)

Se la precedente non è un'opzione, scegliamo di giocare un angolo che faccia parte dell'area in cui si trova il gatto. Se tutti questi angoli sono già stati scelti (come nell'immagine), scegliamo una cella accanto al gatto.

Sono possibili numerosi piccoli miglioramenti come gestire meglio l'avvolgimento (la piastrellatura si rompe lì) o fare le mosse dell'ultima coppia in modo ottimale. Potrei fare alcuni di questi. Se non è permesso, lo aggiungerò (fuori concorso) per coloro che sono interessati.

DijkstrasCat vs HexCatcher:

inserisci qui la descrizione dell'immagine

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

3

CloseCatcher

Scegli una delle posizioni in cui il gatto potrebbe fare un passo nel passaggio successivo. Sceglie quello che darebbe i percorsi più possibili dopo 3 passaggi per il gatto se si sposterà lì e il campo non cambierebbe.

Il codice è quasi identico alla mia voce Cat, FreeCat , che sceglie la direzione in modo molto simile.

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

}

Bello +1. CloseCatcher cattura facilmente StraightCat
Spikatrix il

3

Dijkstra

Non gli piacciono molto i gatti (:v{ >

FreeCat vs Dijkstra (necessita di aggiornamento) :

inserisci qui la descrizione dell'immagine

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

Come cerca di catturare il gatto:

Analizza tutti i quadrati del tabellone e cerca di trovare il quadrato che riduce al minimo l'apertura del tabellone e massimizza la quantità del tabellone; in relazione al gatto. L'apertura e la rigidità di una tavola sono calcolate usando una modifica del suo famoso algoritmo .

Apertura:

L'apertura di una tavola rispetto a una posizione è il numero di posizioni raggiungibili da quella posizione.

viscosità:

La rigidità di una tavola rispetto a una posizione è la somma delle distanze tra le posizioni raggiungibili e la posizione.

Con l'ultimo aggiornamento:

Ora è molto più bravo a catturare tutti i gatti con FreeCat e il suo gatto .Sfortunatamente, è anche molto peggio nel catturare i pazzi gatti non cooperativi. Potrebbe essere migliorato rilevando se il gatto è uno di quelli pazzi e quindi fungendo da CloseCatcher.

Bug corretto.


Posso confermare che funziona finora, ma di gran lunga il più lento ma uno dei migliori finora credo. Ha bisogno di 134 secondi per 100 partite contro RandCat mentre fa un totale di solo 4406 mosse! Penso che dovrò lasciare che il mio PC funzioni durante la notte in uno dei prossimi giorni ... Puoi dirci un po 'di più su come funziona?
flawr

@flawr Aggiunta una descrizione.
TheNumberOne

Non funziona ancora per me. Mi dà un errore: error: cannot infer type arguments for PriorityQueue<>su questa riga PriorityQueue<int[]> open = new PriorityQueue<>(new Comparator<int[]>() {.
Spikatrix

@CoolGuy Stai usando java 6? Penso che devi aggiornare il tuo JDK.
TheNumberOne,

@CoolGuy Puoi anche inserire un int[]tra i due diamanti vuoti dopo PriorityQueue.
TheNumberOne,

2

ForwordCatcher

Posiziona un secchio di fronte al gatto o, se viene preso, si posiziona dietro.

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

1
Ci sono alcuni errori, che mi portano a supporre che non hai testato il tuo programma. Per favore, aggiusta quelle ...
Flawr,

@flawr fixed. scusa per gli errori. Non l'ho provato e il mio Java non è buono.
MegaTom,

Bene, finora tutto funziona senza intoppi, ma non sono ancora sicuro che produrrà sempre mosse valide =)
flawr

1
@flawr Lo spazio dietro un gatto sarà sempre vuoto per il ricevitore :)
TheNumberOne

2

ChoiceCatcher

Utilizza lo stesso meccanismo di punteggio di mia voce ChoiceCat . C'è una piccola modifica che aiuta a scegliere le celle pertinenti ai primi passi poiché ChoiceCat non si preoccupa dei primi secchi in quanto non li vede come una minaccia.

ChoiceCatcher sembra segnare un punteggio considerevolmente migliore rispetto agli attuali catcher.

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

}

1

RandCatcher

Questo è stato fatto solo per testare il controller e posiziona casualmente i secchi (in modo molto inefficiente).

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

}

1

StupidFillCatcher

Questo è stato fatto solo per testare il controller. Si riempie solo colonna per colonna fino a quando il gatto viene catturato.

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

}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.