Підключення-n часу!


20

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

Хтось пам’ятає, що гра з двома гравцями підключає 4? Для тих, хто цього не робить, це була дошка 6x7, яка стоїть вертикально на поверхні. Мета підключення 4 - добре, 4 з'єднати! З'єднання рахується, якщо воно горизонтальне, діагональне або вертикальне. Ви розміщуєте свої шматки на дошці, вставляючи шматок у верхній частині стовпця, де він падає на дно цього стовпця. Наші правила змінюють 3 речі в підключенні 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 |~|_|#|~| |~|~|

Цього разу xотримує 3 бали за 6 підряд ( 1c 2c 3c 4c 5c 6c).

Введення-виведення

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

Наразі буде проведено 100-кілограмовий раунд (зауважте, що це потребує тривалого часу, ви можете скоротити його для швидкого тестування повороту). Загалом переможець - це гравець із найбільшою кількістю перемог.

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

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

Щоб написати бота, ви повинні продовжити Playerклас. Playerє абстрактним і має один метод для реалізації, int makeMove(void). У makeMoveвас буде вирішувати , які Coll ви хотіли б кинути свій шматок в. Якщо ви вибрали недійсну колонку (наприклад, coll не існує, coll вже заповнена), ваша черга буде пропущена . У Playerкласі у вас є багато корисних допоміжних методів. Перелік найважливіших з них випливає:

  • boolean ensureValidMove(int coll): Повернути справжнє значення, якщо сторона знаходиться на дошці, а колла ще не заповнена.
  • 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

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


Чи можете ви додати функціональність, щоб визначити, в яку чергу ввімкнено гру?
Conor O'Brien

Вже завершено, перевірте 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;
    }
}

Дуже-дуже хороший! +1
J Atkin

Щось я помітив, коли я грався з 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;
    }
}

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

3

РевнивийБот

Цей бот ненавидить іншого гравця. І йому не подобається, що він скидає шматки на дошці. Тож він намагається бути останнім, хто скинув шматок у колону.

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, тож сподіваюся, що ця відповідь буде досить хорошою. Я ще не міг її перевірити, тому, вибачте, будь-які помилки.

EDIT : Додано рядок, щоб перервати другу for.

EDIT 2 : з'ясував, чому це whileбуло нескінченно. Зараз вона завершена і може бути використана!


Ласкаво просимо до PPCG, ти змусив мене посміятися з цієї відповіді, це чудово! Просто будьте обережні зі своїми умовами. Я думаю, що плата за замовчуванням заповнена -1 значеннями, тому if(board[col][row]!=null && board[col][row]!=id)слід змінити на if(board[col][row]!=-1..... Перевірте game.Game.genBoard () в github OP, якщо ви хочете бути впевнені. Я не знаю, чи random()будете робити те, що хочете, можливо, використаєте (int)Math.random()*col?
Katenkyo

@Katenkyo Дякую вам дуже, я радий, якщо змусив вас сміятися! random()Метод в Playerкласі! Тому я думаю, що це спрацює =) Але так, я не був впевнений у своїх умовах. Я не знайшов, як це визначено в коді ОП, але я ще раз перевірю. Велике спасибі!
Кекер

Клас Player визначає випадковий () як 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 Вибачте, у мене була старіша версія коду.
Conor O'Brien

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


}

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

2
Стіву важко, він забиває під BasicBlockBot.
Дж. Аткін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.