Время подключения!

20

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

Кто-нибудь помнит, как 2 игрока соединяют 4? Для тех, кто этого не делал, это была доска 6х7, которая стоит вертикально на поверхности. Цель подключения 4 - хорошо подключить 4! Соединение считается, если оно горизонтальное, диагональное или вертикальное. Вы помещаете свои фигуры на доску, вставляя фигуру в верхнюю часть колонки, где она падает до нижней части этой колонки. Наши правила меняют 3 вещи в Connect 4.

  • Изменение № 1 Победа определяется как игрок с наибольшим количеством очков. Вы получаете очки, подключив 4, как в правилах - подробнее об этом позже.
  • Изменение # 2 У вас есть 3 игрока в каждом раунде.
  • Изменение № 3 Размер доски 9x9.

Подсчет очков:

Оценка основана на том, сколько вы получаете подряд. Если у вас 4 группы подряд, вы получаете 1 очко. Если у вас есть 5 в группе подряд, вы получаете 2 очка, 6 в строке 3 и так далее.

Примеры:

Обратите внимание oи xзаменены на #и ~соответственно для лучшей контрастности

Пример пустой доски: (все примеры - доска стандартного размера для 2 игроков)

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

Если мы бросим кусок в столкновении d, он приземлится на месте 1d.

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

Если мы dснова бросим кусок в столкновении , он окажется на месте 2d. Вот примеры положения 4 в ряд:

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

В этом случае xполучает 1 балл по диагонали ( 1a 2b 3c 4d).

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

В этом случае oполучает 1 очко по вертикали ( 1d 2d 3d 4d).

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

В этом случае oполучает 2 точки по горизонтали ( 1c 1d 1e 1f 1g) и xполучает 1 точку по горизонтали ( 2c 2d 2e 2f).

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

На этот раз x3 очка за 6 подряд ( 1c 2c 3c 4c 5c 6c).

Ввод, вывод

У вас будет доступ к плате через 2d массив. Каждое место будет представлено с intпредставлением идентификатора игрока. Вам также будет передан ваш идентификатор игрока в вашу функцию. Вы делаете свой ход, возвращая коллизию, в которую хотите бросить свою фигуру. В каждом раунде будут выбраны 3 игрока. В конце игры все игроки сыграют в равное количество игр.

На данный момент будет запущено 100 000 раундов (обратите внимание, что это занимает много времени, вы можете уменьшить его для быстрого тестирования). В целом победителем становится игрок с наибольшим количеством побед.

Контроллер можно найти здесь: https://github.com/JJ-Atkinson/Connect-n/tree/master .

Написание бота:

Чтобы написать бота, вы должны расширить Playerкласс. Playerявляется абстрактным и имеет один метод для реализации int makeMove(void). В makeMoveвас будет решать , какие Coll вы хотели бы бросить свой кусок в. Если вы выбрали неверный колл (например, колл не существует, колл уже заполнен), ваш ход будет пропущен . В Playerклассе у вас есть много полезных вспомогательных методов. Ниже приводится список наиболее важных из них:

  • boolean ensureValidMove(int coll): Вернуть true, если колл на доске и колл еще не заполнен.
  • int[] getBoardSize()Возвращает массив типа int, где [0]количество столбцов и [1]количество строк.
  • int[][] getBoard(): Вернуть копию доски. Вы должны получить доступ к нему , как это: [coll number][row number from bottom].
  • Чтобы найти остальное, посмотрите на Playerкласс.
  • EMPTY_CELL: Значение пустой ячейки

Так как это будет многопоточным, я также включил randomфункцию, если вам это нужно.

Отладка вашего бота:

Я включил некоторые вещи в контроллер, чтобы упростить отладку бота. Первый Runner#SHOW_STATISTICS. Если это включено, вы увидите распечатку сыгранных групп игроков, включая количество выигрышей ботов. Пример:

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

Вы также можете сделать пользовательскую игру с connectn.game.CustomGameклассом, вы можете увидеть результаты и победителя каждого раунда. Вы даже можете добавить себя в смесь с UserBot.

Добавление вашего бота:

Чтобы добавить своего бота в линейку, перейдите в PlayerFactoryстатический блок и добавьте следующую строку:

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

Другие вещи, чтобы отметить:

  • Симуляции многопоточные. Если вы хотите отключить это, перейдите Runner#runGames()и прокомментируйте эту строку ( .parallel()).
  • Чтобы изменить количество игр, установите Runner#MINIMUM_NUMBER_OF_GAMESпо своему вкусу.

Добавлено позже:

  • Общение между ботами запрещено.

Связанный: Play Connect 4!

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

Табло: (100 000 игр)

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

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

Дж Аткин
источник
Можете ли вы добавить функциональность, чтобы определить, в какую очередь игра включена?
Конор О'Брайен
Уже сделано, проверьте Playerкласс, чтобы увидеть все доступные методы.
Дж Аткин
7
«квадрат 6х7» - это не квадрат
ev3commander
1
Предоставление игрокам возможности «пройти», делая нелегальный ход, немного меняет динамику. Игра заканчивается, если все проходят?
гистократ
1
Да, именно поэтому очень важно использовать ensureValidMove(если ваша стратегия, конечно, не пройти этот поворот).
Дж Аткин

Ответы:

11

MaxGayne

Этот бот присваивает оценку каждой позиции, в основном на основе длины соединенных частей. Он просматривает 3 хода глубоко, просматривая 3 наиболее привлекательных хода на каждом этапе, и выбирает тот, который имеет максимальный ожидаемый результат.

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
источник
Очень очень хорошо! +1
J Аткин
Что-то, что я заметил во время игры с UserBotвашим ботом, заключалось в том, что после некоторого момента MaxGayneон выбрасывает ходы (например, после 15 ходов он пропускает каждый ход, пока игра не закончится).
Дж Аткин
Причиной этого, вероятно, является ошибка в CustomGame. Он использует идентификаторы игроков, основанные на 0, а не на основе 1, как в основной игре. Это просто ломает мой бот. Есть еще 2 проблемы. javafx.util.Pairне работает в Eclipse, потому что он не считается частью общедоступного API. И я понятия не имею, где искать sun.plugin.dom.exception.InvalidStateException. Вы, наверное, имели в виду java.lang.IllegalStateException.
Sleafar
Это кажется немного странным ... В любом случае Pair, это настолько близко, насколько я могу получить к типу данных, который я хочу, не катя свои собственные, поэтому, если затмение не скомпилируется, я думаю, что все в порядке. Что касается # 3, вы правы, мое автозаполнение в IntelliJ не всегда верно. (в большинстве случаев именно поэтому я и не проверял)
J Atkin
@JAtkin На самом деле, Pairпроблема действительно мешает компиляции в Eclipse, если вы не знаете обходной путь .
Sleafar
6

RowBot

Смотрит во всех направлениях и определяет оптимальный столбец. Пытается соединить свои фигуры, не позволяя противникам делать то же самое.

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
источник
5

OnePlayBot

У этого бота только одна игра - поместите его фигуру в крайнюю левую ячейку, которая действительна. Как ни странно, это довольно хорошо;)

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;
    }
}
Дж Аткин
источник
3

RandomBot

Просто поместите кусок в любое место, которое действительно.

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;
    }
}
Дж Аткин
источник
3

StraightForwardBot

Аналогичен OnePlayBot, но учитывает последний ход и воспроизводит следующий действительный столбец.

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
источник
3

JealousBot

Этот бот ненавидит другого игрока. И ему не нравится, что он бросает фигуры на доске. Поэтому он пытается быть последним, кто бросил кусок в столбце.

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

Я впервые на CodeGolf, поэтому я надеюсь, что этот ответ будет достаточно хорошим. Я еще не мог проверить это, поэтому, пожалуйста, извините, если есть какие-либо ошибки.

РЕДАКТИРОВАТЬ : Добавлена ​​строка, чтобы разбить второй for.

РЕДАКТИРОВАТЬ 2 : выяснил, почему это whileбыло бесконечно. Теперь он завершен и может быть использован!

Keker
источник
Добро пожаловать в PPCG, вы заставили меня смеяться с этим ответом, это здорово! Просто будьте осторожны с вашими условиями. Я думаю, что доска заполнена -1 значениями по умолчанию, поэтому if(board[col][row]!=null && board[col][row]!=id)должны быть изменены на if(board[col][row]!=-1..... Проверьте game.Game.genBoard () в github ОП, если хотите быть уверенным. Я тоже не знаю, если вы random()будете делать то, что вы хотите, может быть, использовать (int)Math.random()*col?
Катенкё,
@Katenkyo Большое спасибо, я рад, если это заставило тебя смеяться! random()Метод в Playerклассе! Поэтому я думаю, что это сработает =) Но да, я не был уверен в своих условиях. Я не нашел, как это определено в коде OP, но я проверю снова. Большое спасибо!
Кекер
Класс Player определяет random () как public double random() {return ThreadLocalRandom.current().nextDouble();}. Я не знаю точно, как это работает, но я предполагаю, что он возвращает значение от 0 до 1, поэтому, возможно, потребуется это сделать (int)random()*col:)
Katenkyo
@ Katenkyo О, я думал, что это уже сделало ... Мой плохой. Я отредактирую его, когда найду правильное значение для пустой ячейки на доске, еще раз спасибо!
Кекер
@ Katenkyo Вы правы, nextDoubleвозвращает число между 0и 1. Я включил его, потому что симуляции выполняются параллельно и Math.random()не являются потокобезопасными.
Дж Аткин
3

BasicBlockBot

Простой (и наивный) блочный бот. Он не знает, что вы можете сделать 4 в ряд по горизонтали или по диагонали!

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

}
Дж Аткин
источник
3

прогрессирующий

Прогрессивный ... прогрессивный. Ему нравится смотреть на все , а некоторые! (Я не уверен в методологии этого. Однажды это сработало против друга.) И по какой-то причине работает прилично.

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;
    }
}
Конор О'Брайен
источник
@JAtkin Извините, у меня была более старая версия кода.
Конор О'Брайен
3
@JAtkin Я отклонил твою правку. Вы должны позволить им исправить свой код в своем посте. Если вы хотите исправить это для своего контроллера, это нормально (я бы лично оставил записку), но прямое изменение чьего-либо кода на SE не допускается.
Натан Меррилл
2

BuggyBot

Пример бота для вас, чтобы победить (FYI: это не сложно;)

static class BuggyBot extends Player {
    @Override
    int makeMove() {
        return getBoardSize()[1] - 1;
    }
}
Дж Аткин
источник
2

PackingBot

Этот бот не стремится напрямую к очкам. Он пытается собрать максимум жетонов, пока доска не заполнится. Он понимал, что просто подниматься снова и снова глупо, поэтому он будет случайным образом размещать токены вокруг своего «домена».

Он должен быть в состоянии получить некоторые очки во всех направлениях, но не будет лучшим!

(Не проверено)

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
источник
@JAtkin Спасибо за указание, исправлено :)
Katenkyo
2

Стив

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
источник
2
Стиву тяжело, он забивает BasicBlockBot.
Дж Аткин