Conectar-n tiempo!

20

https://en.wikipedia.org/wiki/Connect_Four

¿Alguien recuerda el juego de 2 jugadores conecta 4? Para aquellos que no lo hicieron, fue una tabla de 6x7 que se coloca vertical sobre una superficie. ¡El objetivo de connect 4 es conectar bien 4! La conexión se cuenta si es horizontal, diagonal o vertical. Colocas tus piezas en el tablero insertando una pieza en la parte superior de una columna donde cae en la parte inferior de esa columna. Nuestras reglas cambian 3 cosas en connect 4.

  • Cambio # 1 Ganar se define como el jugador con más puntos. Obtienes puntos conectando 4 como en las reglas, más sobre eso más adelante.
  • Cambio # 2 Tienes 3 jugadores en cada ronda.
  • Cambio n. ° 3 El tamaño del tablero es de 9x9.

Puntuación:

El puntaje se basa en cuántos obtienes en una fila. Si tienes un grupo 4 en fila, obtienes 1 punto. Si tiene un grupo 5 en fila, obtiene 2 puntos, 6 en fila 3 y así sucesivamente.

Ejemplos:

Nota oy xse reemplazan con #y ~respectivamente, para un mejor contraste

Ejemplo de tablero vacío: (todos los ejemplos son tableros de tamaño estándar para 2 jugadores)

   a b c d e f g
6 | | | | | | | |
5 | | | | | | | |
4 | | | | | | | |
3 | | | | | | | |
2 | | | | | | | |
1 |_|_|_|_|_|_|_|

Si dejamos caer una pieza en coll d, aterrizará en la ubicación 1d.

   a b c d e f g
6 | | | | | | | |
5 | | | | | | | |
4 | | | | | | | |
3 | | | | | | | |
2 | | | | | | | |
1 |_|_|_|#|_|_|_|

Si ahora dejamos caer una pieza en coll dnuevamente, aterrizará en la ubicación 2d. Aquí hay ejemplos de 4 posiciones seguidas:

   a b c d e f g
6 | | | | | | | |
5 | | | | | | | |
4 | | | |~| | | |
3 | | |~|#| | | |
2 | |~|#|~| |#| |
1 |~|#|~|#|_|#|_|

En este caso xobtiene 1 punto en diagonal ( 1a 2b 3c 4d).

  a b c d e f g
6 | | | | | | | |
5 | | | | | | | |
4 | | | |#| | | |
3 | | | |#| | | |
2 | | | |#| | | |
1 |_|~|_|#|~|_|~|

En este caso, oobtiene 1 punto verticalmente ( 1d 2d 3d 4d).

   a b c d e f g
6 | | | | | | | |
5 | | | | | | | |
4 | | | | | | | |
3 | | | | | | | |
2 | | |#|#|#|#| |
1 |_|_|~|~|~|~|~|

En este caso oobtiene 2 puntos horizontalmente ( 1c 1d 1e 1f 1g) y xobtiene 1 punto horizontalmente ( 2c 2d 2e 2f).

   a b c d e f g
6 | | |#| | | | |
5 | | |#| | | | |
4 | | |#| | | | |
3 | | |#| | |~| |
2 |~| |#| | |#|~|
1 |~|_|#|~| |~|~|

Esta vez xobtiene 3 puntos por un 6 en fila ( 1c 2c 3c 4c 5c 6c).

De entrada y salida

Tendrá acceso a la placa a través de una matriz 2D. Cada ubicación estará representada con una intidentificación de un jugador. También tendrá su identificación de jugador pasado a su función. Haces tu movimiento devolviendo en qué coll quieres colocar tu pieza. Cada ronda 3 jugadores serán elegidos para jugar. Al final del juego, todos los jugadores habrán jugado una cantidad par de juegos.

Por el momento se ejecutarán 100k rondas (tenga en cuenta que esto lleva mucho tiempo, es posible que desee reducirlo para una prueba de respuesta rápida). En general, el ganador es el jugador con más victorias.

El controlador se puede encontrar aquí: https://github.com/JJ-Atkinson/Connect-n/tree/master .

Escribiendo un bot:

Para escribir un bot debes extender la Playerclase. Playeres abstracto y tiene un método para implementar int makeMove(void),. En makeMoveusted decidirá en qué coll le gustaría colocar su pieza. Si elige un coll no válido (por ejemplo, coll no existe, coll ya está lleno), su turno será omitido . En la Playerclase tienes muchos métodos auxiliares útiles. A continuación se incluye una lista de los más importantes:

  • boolean ensureValidMove(int coll): Devuelve verdadero si el coll está en el tablero y el coll aún no está lleno.
  • int[] getBoardSize(): Devuelve una matriz int donde [0]es el número de columnas y [1]es el número de filas.
  • int[][] getBoard(): Devuelve una copia de la pizarra. Usted debe acceder a él de esta manera: [coll number][row number from bottom].
  • Para encontrar el resto, mira la Playerclase.
  • EMPTY_CELL: El valor de una celda vacía

Como esto será multiproceso, también he incluido una randomfunción si la necesita.

Depuración de su bot:

He incluido algunas cosas en el controlador para simplificar la depuración de un bot. La primera de ellas es Runner#SHOW_STATISTICS. Si está habilitado, verá una impresión de los grupos de jugadores jugados, incluido un recuento de victorias de bot. Ejemplo:

OnePlayBot, PackingBot, BuggyBot, 
OnePlayBot -> 6
PackingBot -> 5
BuggyBot -> 3
Draw -> 1

También puedes hacer un juego personalizado con la connectn.game.CustomGameclase, puedes ver los puntajes y el ganador de cada ronda. Incluso puede agregarse a la mezcla con UserBot.

Agregando su bot:

Para agregar su bot a la alineación, vaya al PlayerFactorybloque estático y agregue la siguiente línea:

playerCreator.put(MyBot.class, MyBot::new);

Otras cosas a tener en cuenta:

  • Las simulaciones son multiproceso. Si desea desactivar eso, vaya Runner#runGames()y comente esta línea ( .parallel()).
  • Para cambiar el número de juegos, ajústelo Runner#MINIMUM_NUMBER_OF_GAMESa su gusto.

Agregado más tarde:

  • La comunicación entre bots no está permitida.

Relacionado: Play Connect 4!

================================

Marcador: (100 000 juegos)

MaxGayne -> 22662
RowBot -> 17884
OnePlayBot -> 10354
JealousBot -> 10140
Progressive -> 7965
Draw -> 7553
StraightForwardBot -> 7542
RandomBot -> 6700
PackingBot -> 5317
BasicBlockBot -> 1282
BuggyBot -> 1114
FairDiceRoll -> 853
Steve -> 634

================================

J Atkin
fuente
¿Puedes agregar funcionalidad para determinar en qué turno está activado el juego?
Conor O'Brien
Ya hecho, verifique la Playerclase para ver todos los métodos disponibles.
J Atkin
77
"un cuadrado de 6x7" que no es un cuadrado
ev3commander
1
Dar a los jugadores la capacidad de "pasar" haciendo un movimiento ilegal cambia un poco la dinámica. ¿Termina el juego si todos pasan?
histocrat
1
Sí, por eso es muy importante usarlo ensureValidMove(a menos que su estrategia sea pasar este turno, por supuesto).
J Atkin

Respuestas:

11

MaxGayne

Este bot asigna un puntaje a cada posición, basado principalmente en la longitud de las partes conectadas. Se ve 3 movimientos profundos inspeccionando 3 movimientos más atractivos en cada etapa, y elige el que tenga la puntuación máxima esperada.

package connectn.players;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class MaxGayne extends Player {
    private static final int PLAYERS = 3;

    private static class Result {
        protected final int[] score;
        protected int lastCol;

        public Result(int[] score, int lastCol) {
            super();
            this.score = score;
            this.lastCol = lastCol;
        }

        public Result() {
            this(new int[PLAYERS], -1);
        }

        public Result(Result other) {
            this(new int[PLAYERS], other.lastCol);
            System.arraycopy(other.score, 0, this.score, 0, PLAYERS);
        }

        public int getRelativeScore(int player) {
            int max = Integer.MIN_VALUE;
            for (int i = 0; i < PLAYERS; ++ i) {
                if (i != player && score[i] > max) {
                    max = score[i];
                }
            }
            return score[player] - max;
        }
    }

    private static class Board extends Result {
        private final int cols;
        private final int rows;
        private final int[] data;
        private final int[] used;

        public Board(int cols, int rows) {
            super();
            this.cols = cols;
            this.rows = rows;
            this.data = new int[cols * rows];
            Arrays.fill(this.data, -1);
            this.used = new int[cols];
        }

        public Board(Board other) {
            super(other);
            this.cols = other.cols;
            this.rows = other.rows;
            this.data = new int[cols * rows];
            System.arraycopy(other.data, 0, this.data, 0, this.data.length);
            this.used = new int[cols];
            System.arraycopy(other.used, 0, this.used, 0, this.used.length);
        }

        private void updatePartScore(int player, int length, int open, int factor) {
            switch (length) {
                case 1:
                    score[player] += factor * open;
                    break;
                case 2:
                    score[player] += factor * (100 + open * 10);
                    break;
                case 3:
                    score[player] += factor * (10_000 + open * 1_000);
                    break;
                default:
                    score[player] += factor * ((length - 3) * 1_000_000 + open * 100_000);
                    break;
            }
        }

        private void updateLineScore(int col, int row, int colOff, int rowOff, int length, int factor) {
            int open = 0;
            int player = -1;
            int partLength = 0;
            for (int i = 0; i < length; ++ i) {
                int newPlayer = data[(col + i * colOff) * rows + row + i * rowOff];
                if (newPlayer < 0) {
                    if (player < 0) {
                        if (i == 0) {
                            open = 1;
                        }
                    } else {
                        updatePartScore(player, partLength, open + 1, factor);
                        open = 1;
                        player = newPlayer;
                        partLength = 0;
                    }
                } else {
                    if (newPlayer == player) {
                        ++ partLength;
                    } else {
                        if (player >= 0) {
                            updatePartScore(player, partLength, open, factor);
                            open = 0;
                        }
                        player = newPlayer;
                        partLength = 1;
                    }
                }
            }
            if (player >= 0) {
                updatePartScore(player, partLength, open, factor);
            }
        }

        private void updateIntersectionScore(int col, int row, int factor) {
            updateLineScore(col, 0, 0, 1, rows, factor);
            updateLineScore(0, row, 1, 0, cols, factor);
            if (row > col) {
                updateLineScore(0, row - col, 1, 1, Math.min(rows - row, cols), factor);
            } else {
                updateLineScore(col - row, 0, 1, 1, Math.min(cols - col, rows), factor);
            }
            if (row > cols - col - 1) {
                updateLineScore(cols - 1, row - (cols - col - 1), -1, 1, Math.min(rows - row, cols), factor);
            } else {
                updateLineScore(col + row, 0, -1, 1, Math.min(col + 1, rows), factor);
            }
        }

        private void updatePiece(int player, int col, int row) {
            updateIntersectionScore(col, row, -1);
            data[col * rows + row] = player;
            ++ used[col];
            lastCol = col;
            updateIntersectionScore(col, row, 1);
        }

        public Board updatePiece(int player, int col) {
            int row = used[col];
            if (row >= rows) {
                return null;
            } else {
                Board result = new Board(this);
                result.updatePiece(player, col, row);
                return result;
            }
        }

        private void updateBoard(int[][] board) {
            for (int col = 0; col < cols; ++ col) {
                for (int row = 0; row < rows; ++ row) {
                    int oldPlayer = data[col * rows + row];
                    int newPlayer = board[col][row] - 1;
                    if (newPlayer < 0) {
                        if (oldPlayer < 0) {
                            break;
                        } else {
                            throw new RuntimeException("[" + col + ", " + row + "] == "  + oldPlayer + " >= 0");
                        }
                    } else {
                        if (oldPlayer < 0) {
                            updatePiece(newPlayer, col, row);
                        } else if (newPlayer != oldPlayer) {
                            throw new RuntimeException("[" + col + ", " + row + "] == "  + oldPlayer + " >= " + newPlayer);
                        }
                    }
                }
            }
        }

        private Result bestMove(int depth, int player) {
            List<Board> boards = new ArrayList<>();
            for (int col = 0; col < cols; ++ col) {
                Board board = updatePiece(player, col);
                if (board != null) {
                    boards.add(board);
                }
            }
            if (boards.isEmpty()) {
                return null;
            }
            Collections.sort(boards, (o1, o2) -> Integer.compare(o2.getRelativeScore(player), o1.getRelativeScore(player)));
            if (depth <= 1) {
                return new Result(boards.get(0).score, boards.get(0).lastCol);
            }
            List<Result> results = new ArrayList<>();
            for (int i = 0; i < 3 && i < boards.size(); ++ i) {
                Board board = boards.get(i);
                Result result = board.bestMove(depth - 1, (player + 1) % PLAYERS);
                if (result == null) {
                    results.add(new Result(board.score, board.lastCol));
                } else {
                    results.add(new Result(result.score, board.lastCol));
                }
            }
            Collections.sort(results, (o1, o2) -> Integer.compare(o2.getRelativeScore(player), o1.getRelativeScore(player)));
            return results.get(0);
        }
    }

    private Board board = null;

    @Override
    public int makeMove() {
        if (board == null) {
            int[][] data = getBoard();
            board = new Board(data.length, data[0].length);
            board.updateBoard(data);
        } else {
            board.updateBoard(getBoard());
        }

        Result result = board.bestMove(3, getID() - 1);
        return result == null ? -1 : result.lastCol;
    }
}
Sleafar
fuente
¡Muy, muy agradable! +1
J Atkin
Algo que noté mientras jugaba UserBoty tu bot fue que después de algún punto MaxGaynetirará los turnos (por ejemplo, después de 15 movimientos, salta cada turno hasta que finaliza el juego).
J Atkin
La causa de esto es probablemente un error en CustomGame. Utiliza ID de jugador basadas en 0 en lugar de 1 como el juego principal. Esto simplemente rompe mi bot. Hay 2 problemas más. javafx.util.Pairno funciona en Eclipse porque no se considera parte de la API pública. Y no tengo idea de dónde buscar sun.plugin.dom.exception.InvalidStateException. Probablemente quisiste decir java.lang.IllegalStateException.
Sleafar
Eso parece un poco extraño ... De todos modos Pair, eso es lo más cerca que puedo llegar al tipo de datos que quiero sin rodar el mío, así que a menos que eclipse no se compile, creo que está bien. En cuanto al número 3, tienes razón, mi autocompletado en IntelliJ no siempre es correcto. (la mayoría de las veces es así, por eso no lo comprobé)
J Atkin
@JAtkin En realidad, el Pairproblema realmente impide compilar en Eclipse, a menos que conozca la solución .
Sleafar
6

RowBot

Mira en todas las direcciones y determina la columna óptima. Intenta conectar sus piezas, sin dejar que sus oponentes hagan lo mismo.

package connectn.players;

import connectn.game.Game;
import java.util.ArrayList;
import java.util.List;

public class RowBot extends Player {

    @Override
    public int makeMove() {
        int[][] board = getBoard();
        int best = -1;
        int bestScore = -10;
        for (int col = 0; col < board.length; col++) {
            if (ensureValidMove(col)) {
                int score = score(board, col, false);
                score -= score(board, col, true);
                if (score > bestScore) {
                    bestScore = score;
                    best = col;
                }
            }
        }
        return best;
    }

    private int score(int[][] board, int col, boolean simulateMode) {
        int me = getID();
        int row = getLowestEmptyRow(board, col);
        List<Score> scores = new ArrayList<>();
        if (!simulateMode) {
            scores.add(getScoreVertical(board, col, row));
        } else {
            row += 1;
        }
        scores.addAll(getScoreHorizontal(board, col, row));
        scores.addAll(getScoreDiagonal(board, col, row));
        int score = 0;
        for (Score s : scores) {
            if (s.player == me) {
                score += s.points > 2 ? 100 : s.points * 5;
            } else if (s.player != Game.EMPTY_CELL) {
                score += s.points > 2 ? 50 : 0;
            } else {
                score += 1;
            }
        }
        return score;
    }

    private Score getScoreVertical(int[][] board, int col, int row) {
        return getScore(board, col, row, 0, -1);
    }

    private List<Score> getScoreHorizontal(int[][] board, int col, int row) {
        List<Score> scores = new ArrayList<>();

        Score left = getScore(board, col, row, -1, 0);
        Score right = getScore(board, col, row, 1, 0);
        if (left.player == right.player) {
            left.points += right.points;
            scores.add(left);
        } else {
            scores.add(left);
            scores.add(right);
        }
        return scores;
    }

    private List<Score> getScoreDiagonal(int[][] board, int col, int row) {
        List<Score> scores = new ArrayList<>();

        Score leftB = getScore(board, col, row, -1, -1);
        Score rightU = getScore(board, col, row, 1, 1);
        Score leftBottomToRightUp = leftB;
        if (leftB.player == rightU.player) {
            leftBottomToRightUp.points += rightU.points;
        } else if (leftB.points < rightU.points || leftB.player == Game.EMPTY_CELL) {
            leftBottomToRightUp = rightU;
        }

        Score leftU = getScore(board, col, row, -1, 1);
        Score rightB = getScore(board, col, row, 1, -1);
        Score rightBottomToLeftUp = leftU;
        if (leftU.player == rightB.player) {
            rightBottomToLeftUp.points += rightB.points;
        } else if (leftU.points < rightB.points || leftU.player == Game.EMPTY_CELL) {
            rightBottomToLeftUp = rightB;
        }

        if (leftBottomToRightUp.player == rightBottomToLeftUp.player) {
            leftBottomToRightUp.points += rightBottomToLeftUp.points;
            scores.add(leftBottomToRightUp);
        } else {
            scores.add(leftBottomToRightUp);
            scores.add(rightBottomToLeftUp);
        }
        return scores;
    }

    private Score getScore(int[][] board, int initCol, int initRow, int colOffset, int rowOffset) {
        Score score = new Score();
        outerLoop: for (int c = initCol + colOffset;; c += colOffset) {
            for (int r = initRow + rowOffset;; r += rowOffset) {
                if (outside(c, r) || board[c][r] == Game.EMPTY_CELL) {
                    break outerLoop;
                }
                if (score.player == Game.EMPTY_CELL) {
                    score.player = board[c][r];
                }

                if (score.player == board[c][r]) {
                    score.points++;
                } else {
                    break outerLoop;
                }

                if (rowOffset == 0) {
                    break;
                }
            }
            if (colOffset == 0) {
                break;
            }
        }
        return score;
    }

    private boolean outside(int col, int row) {
        return !boardContains(col, row);
    }

    private int getLowestEmptyRow(int[][] board, int col) {
        int[] rows = board[col];
        for (int row = 0; row < rows.length; row++) {
            if (rows[row] == Game.EMPTY_CELL){
                return row;
            }
        }
        return -1;
    }

    private class Score {
        private int player = Game.EMPTY_CELL;
        private int points = 0;
    }
}
CommonGuy
fuente
5

OnePlayBot

Este bot solo tiene una jugada: coloca su pieza en la celda de la izquierda que sea válida. Por extraño que parezca, hace bastante bien;)

static class OnePlayBot extends Player {
    @Override
    int makeMove() {
        int attemptedMove = 0;

        for (int i = 0; i < getBoardSize()[0]; i++)
            if (ensureValidMove(i)) {
                attemptedMove = i;
                break;
            }

        return attemptedMove;
    }
}
J Atkin
fuente
3

RandomBot

Simplemente ponga una pieza en cualquier lugar que sea válida.

static class RandomBot extends Player {
    @Override
    int makeMove() {
        int attemptedMove = (int)Math.round(random() * getBoardSize()[0]);
        while (!ensureValidMove(attemptedMove))
            attemptedMove = (int)Math.round(random() * getBoardSize()[0]);

        return attemptedMove;
    }
}
J Atkin
fuente
3

StraightForwardBot

Similar al OnePlayBot, pero tiene en cuenta el último movimiento y reproduce la siguiente columna que es válida.

static class StraightForwardBot extends Player {
    private int lastMove = 0;

    @Override
    int makeMove() { 
        for (int i = lastMove + 1; i < getBoardSize()[0]; i++) {
            if (ensureValidMove(i)) {
                lastMove = i;
                return i;
            }
        }
        for (int i = 0; i < lastMove; i++) {
            if (ensureValidMove(i)) {
                lastMove = i;
                return i;
            }
        }
        return 0;
    }
}
OJ7
fuente
3

CelosoBot

Este bot odia al otro jugador. Y no le gusta que deje caer piezas en el tablero. Entonces él trata de ser el último en dejar caer una pieza en una columna.

public class JealousBot extends Player {

    @Override
    public int makeMove() {
        int move = 0;
        boolean madeMove = false;
        int[] boardSize = getBoardSize();
        int id = getID();
        int[][] board = getBoard();

        if(getTurn()!=0) {
            for(int col = 0; col<boardSize[0]; col++) {
                for(int row = 0; row<boardSize[1]; row++) {
                    if(ensureValidMove(col)) {
                        if(board[col][row]!=EMPTY_CELL && board[col][row]!=id) {
                            move = col;
                            madeMove = true;
                            break;
                        }
                    }
                }
                if(madeMove) break;
            }

            if(!madeMove) {
                int temp = (int)Math.round(random()*boardSize[0]);
                while(madeMove!=true) {
                    temp = (int)Math.round(random()*boardSize[0]);
                    if(ensureValidMove(temp)) {
                        madeMove = true;
                    }
                }
                move = temp;
            }
        } else {
            move = (int)Math.round(random()*boardSize[0]);
        }

        return move;
    }
}

Es mi primera vez en CodeGolf, así que espero que esta respuesta sea lo suficientemente buena. Todavía no pude probarlo, así que discúlpeme si hay algún error.

EDITAR : se agregó una línea para romper el segundo for.

EDIT 2 : descubrí por qué el whileera infinito. ¡Ahora está completo y se puede usar!

Keker
fuente
Bienvenido a PPCG, me hiciste reír con esta respuesta, ¡genial! Solo ten cuidado con tus condiciones. Creo que el tablero está lleno de valores -1 por defecto, por lo que if(board[col][row]!=null && board[col][row]!=id)debería cambiarse a if(board[col][row]!=-1..... Comprueba game.Game.genBoard () en el github de OP si quieres estar seguro. Tampoco sé si random()harás lo que quieras, ¿quizás usar (int)Math.random()*col?
Katenkyo
@Katenkyo Muchas gracias, ¡estoy feliz si te hizo reír! ¡El random()método está en la Playerclase! Así que creo que funcionará =) Pero sí, no tenía confianza en mis condiciones. No encontré cómo se define en el código de OP, pero lo comprobaré nuevamente. ¡Muchas gracias!
Keker
La clase Player define random () como public double random() {return ThreadLocalRandom.current().nextDouble();}. No sé exactamente cómo funciona, pero supongo que devuelve un valor entre 0 y 1, por lo que podría ser necesario (int)random()*col:)
Katenkyo
@Katenkyo Oh, pensé que ya lo había hecho ... Mi mal. Lo editaré cuando encuentre el valor correcto para una celda vacía en el tablero, ¡gracias de nuevo!
Keker
@Katenkyo Tienes razón, nextDoubledevuelve un número entre 0y 1. Lo incluí porque las simulaciones se ejecutan en paralelo y Math.random()no es seguro para subprocesos.
J Atkin
3

BasicBlockBot

Un simple (e ingenuo) bloque de bot. ¡Él no sabe que puedes hacer un 4 en fila horizontal o diagonalmente!

static class BasicBlockBot extends Player {
    @Override
    int makeMove() {
        List<Integer> inARows = detectInARows();
        double chanceOfBlock = 0.5;

        if (inARows.isEmpty())
            chanceOfBlock = 0;

        if (random() < chanceOfBlock) {
            return inARows.get((int)Math.round(random() * (inARows.size() - 1)));
        } else {
            return (int)Math.round(random() * getBoardSize()[0]);
        }
    }


    /**
     * Very limited - just detects vertical in a rows
     *
     * @return A list of colls that have 4 in a row vertical
     */
    private List<Integer> detectInARows() {
        List<Integer> ret = new ArrayList<>();
        int[][] board = getBoard();

        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[i].length; j++) {
                int currId = board[i][j];
                if (currId != -1 && is4InARowVertical(i, j, board)) {
                    ret.add(i);
                }
            }
        }

        return ret;
    }

    private boolean is4InARowVertical(int coll, int row, int[][] board) {
        int id = board[coll][row];

        for (int i = 0; i < 4; i++) {
            int y = row + i;
            if (!boardContains(coll,y) || board[coll][y] != id)
                return false;
        }
        return true;
    }

}
J Atkin
fuente
3

Progresivo

Progresivo es ... progresivo. ¡Le gusta mirar todo y algo más! (No estoy seguro de la metodología de esto. Funcionó contra un amigo, una vez). Y, por alguna razón, funciona decentemente.

static class Progressive extends Player{
    @Override
    int makeMove(){
        int move = 0;
        boolean statusBroken = false;
        for(int n=getBoardSize()[0];n>2;n-=2){
            for(int i=0;i<getBoardSize()[0];i+=n){
                if(ensureValidMove(i)){
                    move = i;
                    statusBroken = true;
                    break;
                }
                if(statusBroken) break;
            }
        }
        return move;
    }
}
Conor O'Brien
fuente
@JAtkin Lo siento, tenía una versión anterior del código.
Conor O'Brien
3
@JAtkin Rechacé tu edición. Debes permitirles corregir su código en su publicación. Si desea arreglarlo para su controlador, está bien (yo personalmente dejaría una nota), pero la modificación directa del código de alguien en SE no está permitida.
Nathan Merrill
3

FairDiceRoll

Siempre devuelve 4.

static class FairDiceRoll extends Player {
    private int lastMove = 0;
    @Override
    int makeMove() { 
        return 4;
    }
}
ev3commander
fuente
2

BuggyBot

Un bot de muestra para que puedas vencer (FYI: no es difícil;)

static class BuggyBot extends Player {
    @Override
    int makeMove() {
        return getBoardSize()[1] - 1;
    }
}
J Atkin
fuente
2

EmbalajeBot

Este bot no apunta a puntos directamente. Intenta empacar un máximo de fichas hasta que se llena el tablero. Entendió que simplemente subir una y otra vez es una estupidez, por lo que colocará al azar fichas alrededor de su "dominio".

Debería poder obtener algunos puntos en todas las direcciones, ¡pero no será el mejor!

(No probado)

package connectn.players;

static class PackingBot extends Player
{
    @Override
    int makeMove()
    {
        int move = 0;
        int[] sizes = getBoardSize();
        if(getTurn()==0)
            return sizes[0]/2+sizes[0]%2;

        int[][] board = getBoard();
        int[] flatBoard =new int[sizes[0]];
        //Creating a flat mapping of my tokens
        for(int i=0;i<sizes[0];i++)
            for (int j=0;j<sizes[1];j++)
                if(board[i][j]!=getID())
                    flatBoard[i]++;

        int max=0;
        int range=0;
        for(int i=0;i<flatBoard.length;i++)
        {
            if(flatBoard[i]!=0)
                range++;
            if(flatBoard[i]>flatBoard[max])
                max=i;
        }

        int sens = (Math.random()>0.5)?1:-1;
        move=((int)(Math.random()*(range+1)*sens))+max;

        while(!ensureValidMove(move))
        {
            move=(move+1*sens)%sizes[0];
            if(move<0)
                move=sizes[0]-1;
        }
        return move;
    }


}
Katenkyo
fuente
@JAtkin Gracias por señalar eso, arreglado :)
Katenkyo
2

Steve

package connectn.players;

import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

import connectn.game.Game;

public class Steve extends Player {
    @Override
    public int makeMove() {
        Random r=ThreadLocalRandom.current();
        int attemptedMove = 0;
        int[][]board=getBoard();
        int ec=Game.EMPTY_CELL;
        for(int c=0;c<board.length;c++){
            int j=board[c].length-1;
            for(;j>=0;j--){
                if(board[c][j]!=ec)break;
            }

            if(j>2+r.nextInt(3)&&r.nextDouble()<0.8)return c;
        }
        int k=-2+board.length/2+r.nextInt(5);
        if(ensureValidMove(k))return k;
        for (int i = 0; i < getBoardSize()[0]; i++)
            if (ensureValidMove(i)) {
                attemptedMove = i;
                break;
            }

        return attemptedMove;
    }
}
SuperJedi224
fuente
2
Steve lo está pasando mal, anota bajo BasicBlockBot.
J Atkin