Conecte-n tempo!

20

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

Alguém se lembra do jogo para 2 jogadores conectar 4? Para quem não, era uma placa 6x7 vertical na superfície. O objetivo do connect 4 é conectar bem 4! A conexão é contada se for horizontal, diagonal ou vertical. Você coloca suas peças no quadro inserindo uma peça no topo de uma coluna, onde ela cai no fundo dessa coluna. Nossas regras alteram três coisas no connect 4.

  • Mudança # 1 Vencer é definido como o jogador com mais pontos. Você ganha pontos conectando 4 como nas regras - mais sobre isso mais tarde.
  • Mudança # 2 Você tem 3 jogadores a cada rodada.
  • Alteração # 3 O tamanho da placa é 9x9.

Pontuação:

A pontuação é baseada em quantas você obtém em uma linha. Se você tem um grupo de 4 em linha, recebe 1 ponto. Se você tem um grupo 5 em linha, obtém 2 pontos, 6 em linha 3 e assim por diante.

Exemplos:

Observe oe xsão substituídos por #e ~respectivamente, para melhor contraste

Exemplo de tabuleiro vazio: (todos os exemplos são tabuleiros de tamanho padrão para 2 jogadores)

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

Se soltarmos um pedaço d, ele pousará no local 1d.

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

Se agora deixarmos um pedaço no colo dnovamente, ele pousará no local 2d. Aqui estão exemplos de 4 posições consecutivas:

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

Nesse caso, xobtém 1 ponto na diagonal ( 1a 2b 3c 4d).

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

Nesse caso, oobtém 1 ponto na vertical ( 1d 2d 3d 4d).

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

Nesse caso, oobtém 2 pontos horizontalmente ( 1c 1d 1e 1f 1g) e xobtém 1 ponto horizontalmente ( 2c 2d 2e 2f).

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

Desta vez, xobtém 3 pontos por 6 em sequência ( 1c 2c 3c 4c 5c 6c).

Entrada / Saída

Você terá acesso à placa através de uma matriz 2D. Cada local será representado com uma intidentificação do jogador. Você também terá seu ID de jogador passado para sua função. Você faz o seu movimento retornando em qual coll você quer colocar sua peça. Cada rodada 3 jogadores serão escolhidos para jogar. No final do jogo, todos os jogadores terão jogado uma quantidade uniforme de jogos.

No momento, 100 mil rodadas serão executadas (observe que isso leva muito tempo, você pode reduzi-lo para testes rápidos de resposta). No geral, o vencedor é o jogador com mais vitórias.

O controlador pode ser encontrado aqui: https://github.com/JJ-Atkinson/Connect-n/tree/master .

Escrevendo um bot:

Para escrever um bot, você deve estender a Playerclasse. Playeré abstrato e tem um método para implementar int makeMove(void),. Em makeMoveque você vai decidir qual coll você gostaria de deixar cair a sua peça em. Se você escolheu um coll inválido (por exemplo, coll não existe, coll já foi preenchido), seu turno será pulado . Na Playerclasse você tem muitos métodos auxiliares úteis. Segue uma lista dos mais importantes:

  • boolean ensureValidMove(int coll): Retorne true se o item estiver no quadro e o item ainda não estiver preenchido.
  • int[] getBoardSize(): Retorna uma matriz int onde [0]é o número de colunas e [1]o número de linhas.
  • int[][] getBoard(): Devolva uma cópia do quadro. Você deve acessá-lo como este: [coll number][row number from bottom].
  • Para encontrar o resto, veja a Playerturma.
  • EMPTY_CELL: O valor de uma célula vazia

Como isso será multithread, também incluí uma randomfunção, se você precisar.

Depurando seu bot:

Eu incluí algumas coisas no controlador para simplificar a depuração de um bot. O primeiro é Runner#SHOW_STATISTICS. Se isso estiver ativado, você verá uma impressão dos grupos de jogadores reproduzidos, incluindo uma contagem de vitórias em bot. Exemplo:

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

Você também pode fazer um jogo personalizado com a connectn.game.CustomGameclasse, pode ver a pontuação e o vencedor de cada rodada. Você pode até se adicionar à mistura com UserBot.

Adicionando seu bot:

Para adicionar seu bot à programação, vá para o PlayerFactorybloco estático e adicione a seguinte linha:

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

Outras coisas a serem observadas:

  • As simulações são multiencadeadas. Se você quiser desativar isso, vá Runner#runGames()e comente esta linha ( .parallel()).
  • Para alterar o número de jogos, defina Runner#MINIMUM_NUMBER_OF_GAMESao seu gosto.

Adicionado mais tarde:

  • A comunicação entre bots é proibida.

Relacionados: Play Connect 4!

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

Placar: (100 000 jogos)

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
fonte
Você pode adicionar funcionalidades para determinar em qual turno o jogo está ativado?
Conor O'Brien
Já feito, verifique a Playerclasse para ver todos os métodos disponíveis.
precisa
7
"um quadrado 6x7" que não é um quadrado
ev3commander 08/12/15
1
Dar aos jogadores a capacidade de "passar" fazendo um movimento ilegal muda um pouco a dinâmica. O jogo termina se todos passarem?
histocrat
1
Sim, é por isso que é muito importante usar ensureValidMove(a menos que sua estratégia seja passar nesta curva, é claro).
J Atkins

Respostas:

11

MaxGayne

Este bot atribui uma pontuação a cada posição, com base principalmente no comprimento das peças conectadas. Parece 3 movimentos profundos, inspecionando 3 movimentos mais bonitos em cada estágio e escolhe aquele com a pontuação 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
fonte
Muito muito bom! +1
J Atkin
Algo que eu notei enquanto brincava UserBote seu bot era que, depois de algum ponto MaxGayne, jogaria fora os turnos (por exemplo, após 15 jogadas, ele salta a cada turno até o jogo terminar).
precisa
A causa disso provavelmente é um bug no CustomGame. Ele usa IDs de jogadores baseados em 0, em vez de baseados em 1, como no jogo principal. Isso simplesmente quebra meu bot. Existem mais 2 problemas. javafx.util.Pairnão funciona no Eclipse porque não é considerado parte da API pública. E não tenho ideia de onde procurar sun.plugin.dom.exception.InvalidStateException. Você provavelmente quis dizer java.lang.IllegalStateException.
Sleafar
Isso parece um pouco estranho ... De qualquer forma Pair, é o mais próximo possível do tipo de dados que eu quero sem rodar sozinho, portanto, a menos que o eclipse não seja compilado, acho que está tudo bem. Quanto ao número 3, você está certo, meu preenchimento automático no IntelliJ nem sempre está certo. (na maioria das vezes ela é, é por isso que eu não verificar)
J Atkin
@JAtkin Na verdade, o Pairproblema realmente impede a compilação no Eclipse, a menos que você conheça a solução alternativa .
Sleafar 12/12
6

RowBot

Olha em todas as direções e determina a coluna ideal. Tenta conectar suas peças, sem deixar que seus oponentes façam o mesmo.

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
fonte
5

OnePlayBot

Este bot tem apenas uma peça - coloque sua peça na célula mais à esquerda que é válida. Curiosamente, ele faz muito bem;)

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
fonte
3

RandomBot

Basta colocar uma peça em qualquer lugar que seja válido.

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
fonte
3

StraightForwardBot

Semelhante ao OnePlayBot, mas leva em consideração a última jogada e reproduz a próxima coluna que é 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;
    }
}
JO7
fonte
3

JealousBot

Este bot odeia o outro jogador. E ele não gosta que derruba pedaços no tabuleiro. Então ele tenta ser o último a soltar uma peça em uma coluna.

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

É a minha primeira vez no CodeGolf, então espero que esta resposta seja boa o suficiente. Ainda não pude testá-lo, então, desculpe-me se houver algum erro.

EDIT : Adicionado uma linha para quebrar o segundo for.

EDIT 2 : Descobriu por que o whileera infinito. Agora está completo e pode ser usado!

Keker
fonte
Bem-vindo ao PPCG, você me fez rir com esta resposta, isso é ótimo! Apenas tenha cuidado com suas condições. Eu acho que o quadro é preenchido com valores -1 por padrão, então if(board[col][row]!=null && board[col][row]!=id)deve ser alterado para if(board[col][row]!=-1..... Verifique game.Game.genBoard () no github do OP, se você quiser ter certeza. Também não sei se você random()fará o que quiser, talvez use (int)Math.random()*col?
Katenkyo
@ Katenkyo Muito obrigado, estou feliz se isso fez você rir! O random()método está na Playerclasse! Então eu acho que vai funcionar =) Mas sim, eu não estava confiante em minhas condições. Não encontrei como isso está definido no código do OP, mas vou verificar novamente. Muito obrigado!
quer
A classe Player define random () como public double random() {return ThreadLocalRandom.current().nextDouble();}. Eu não sei exatamente como ele funciona, mas presumo que ele retorne um valor entre 0 e 1, por isso pode ser necessário (int)random()*col:) :) #
10331
@ Katenkyo Oh, eu pensei que já fiz isso ... Meu mal. Vou editá-lo quando encontrar o valor certo para uma célula vazia no quadro, obrigado novamente!
quer
@Katenkyo Você está correto, nextDoubleretorna um número entre 0e 1. Incluí-o porque as simulações são executadas em paralelo e Math.random()não é seguro para threads.
J Atkins
3

BasicBlockBot

Um bot de bloco simples (e ingênuo). Ele não sabe que você pode fazer um 4 consecutivo na horizontal ou na diagonal!

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
fonte
3

Progressivo

Progressivo é ... progressivo. Ele gosta de olhar para tudo , e alguns! (Não tenho certeza da metodologia disso. Funcionou contra um amigo, uma vez.) E, por algum motivo, 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
fonte
@ Atkin Desculpe, eu tinha uma versão mais antiga do código.
Conor O'Brien
3
@JAtkin eu rejeitei sua edição. Você deve permitir que eles corrijam o código em suas postagens. Se você deseja corrigi-lo para o seu controlador, tudo bem (eu pessoalmente ainda deixaria uma nota), mas a modificação direta do código de alguém no SE não é permitida.
Nathan Merrill
3

FairDiceRoll

Sempre retorna 4.

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

BuggyBot

Um exemplo de bot para você vencer (FYI: não é difícil;)

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

PackingBot

Este bot não está apontando diretamente para pontos. Ele tenta empacotar um máximo de fichas até o tabuleiro estar cheio. Ele entendeu que simplesmente subir uma e outra vez é estúpido, então ele coloca aleatoriamente tokens em torno de seu "domínio".

Ele deve conseguir alguns pontos em todas as direções, mas não será o melhor!

(Não testado)

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
fonte
@JAtkin Obrigado por apontar isso, corrigido :)
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
fonte
2
Steve está tendo um momento difícil, ele pontua abaixo BasicBlockBot.
J Atkins