연결 시간!


20

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

2 플레이어 게임이 4를 연결한다는 것을 기억하는 사람이 있습니까? 그렇지 않은 사람들에게는 표면에 수직으로 서있는 6x7 보드였습니다. Connect 4의 목표는 4를 연결하는 것입니다! 연결이 수평, 대각선 또는 수직이면 계산됩니다. 조각을 열의 맨 아래에 해당 열의 맨 아래에 삽입하여 조각을 보드에 배치합니다. 우리의 규칙은 connect 4에서 3 가지를 바꿉니다.

  • 변경 # 1 승리는 가장 많은 점수를 얻은 플레이어로 정의됩니다. 규칙에서와 같이 4를 연결하여 포인트를 얻습니다.
  • # 2 변경 라운드마다 3 명의 선수가 있습니다.
  • 변경 # 3 보드 크기는 9x9입니다.

채점 :

점수는 행 수에 따라 결정됩니다. 행 그룹에 4가 있으면 1 점을 얻습니다. 행 그룹에 5가 있으면 2 점, 6 행에 6 등이 있습니다.

예 :

ox로 대체 #하고 ~더 나은 대비를 각각

빈 보드의 예 : (모든 예는 2 인 표준 크기 보드 임)

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

우리가 조각을 coll에 놓으면 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는 6 연속 3 점을 얻습니다 ( 1c 2c 3c 4c 5c 6c).

입출력

2D 어레이를 통해 보드에 액세스 할 수 있습니다. 각 위치는 int플레이어 ID를 나타내는 것으로 표시됩니다 . 또한 플레이어 ID가 함수에 전달됩니다. 조각을 놓을 콜을 돌려 이동합니다. 각 라운드 3 플레이어가 재생하도록 선택됩니다. 게임이 끝나면 모든 플레이어가 고른 게임을합니다.

현재 100k 라운드가 진행될 것 입니다. 시간 이 오래 걸리므로 빠른 처리 테스트를 위해 라운드 를 줄이십시오. 전반적으로 우승자는 가장 많은 승리를 한 플레이어입니다.

컨트롤러는 https://github.com/JJ-Atkinson/Connect-n/tree/master 에서 찾을 수 있습니다 .

봇 작성 :

봇을 작성하려면 Player클래스 를 확장해야합니다 . Player추상적이고 구현할 방법이 하나 int makeMove(void)있습니다. 에서 makeMove당신이 결정할 것입니다 당신이로 조각을 드롭하고 싶은 콜있다. 유효하지 않은 콜을 선택한 경우 (예 : 콜이 존재하지 않거나 콜이 이미 채워져 있음) 턴을 건너 뜁니다 . 에서 Player클래스는 많은 유용한 도우미 메서드가 있습니다. 가장 중요한 것들의 목록은 다음과 같습니다.

  • boolean ensureValidMove(int coll): 콜이 보드에 있고 콜이 아직 채워지지 않은 경우 true를 리턴합니다 .
  • int[] getBoardSize(): [0][1]개수와 행 개수 인 int 배열을 반환합니다 .
  • 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사용 가능한 모든 메소드를 보려면 클래스를 확인하십시오.
J Atkin

7
사각형이 아니다 "사각형 6X7"
ev3commander

1
플레이어가 불법적으로 움직여 "통과"할 수있는 능력을 부여하면 역 동성이 약간 변경됩니다. 모두가지나 가면 게임이 끝납니 까?
histocrat

1
예, 그렇기 때문에 전략 을 사용하는 것이 아니라면 사용하는 것이 매우 중요합니다 ensureValidMove.
J Atkin

답변:


11

막스가 인

이 봇은 주로 연결된 부품의 길이를 기준으로 각 위치에 점수를 할당합니다. 각 단계에서 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가 턴을 버릴 것이라는 것입니다.
J Atkin

이것의 원인은 아마도 CustomGame의 버그 일 것입니다. 메인 게임처럼 1 기반 대신 0 기반 플레이어 ID를 사용하고 있습니다. 이것은 단순히 내 봇을 파괴합니다. 두 가지 문제가 더 있습니다. javafx.util.Pair공용 API의 일부로 간주되지 않기 때문에 Eclipse에서 작동하지 않습니다. 어디를 찾아야할지 모르겠습니다 sun.plugin.dom.exception.InvalidStateException. 당신은 아마 의미했다 java.lang.IllegalStateException.
Sleafar

그것은 조금 이상하게 보입니다 ... 어쨌든 Pair, 그것은 내 자신을 굴리지 않고 원하는 데이터 유형에 도달 할 수있는 한 가깝습니다. 그래서 일식이 컴파일되지 않으면 괜찮습니다. # 3에 관해서는, IntelliJ의 자동 완성이 항상 옳은 것은 아닙니다. (대부분의 시간, 그래서 내가 확인하지 않은 이유입니다)
J Atkin

@JAtkin 실제로 Pair문제를 해결 하지 않으면 문제는 실제로 Eclipse에서 컴파일을 방지 합니다 .
Sleafar

6

로우 봇

모든 방향을보고 최적의 열을 결정합니다. 그의 상대를 동일하게하지 않으면 서 그의 조각을 연결하려고합니다.

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

랜덤 봇

유효한 곳이면 어디든 놓으십시오.

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는 처음 이니이 답변이 충분하길 바랍니다. 아직 테스트 할 수 없으므로 실수가 있으면 실례하겠습니다.

편집 : 두 번째 줄 바꿈 줄을 추가했습니다 for.

편집 2 : 왜 while무한 한지 알아 냈습니다 . 이제 완성되어 사용할 수 있습니다!


PPCG에 오신 것을 환영합니다.이 답변으로 저를 웃게하셨습니다. 당신의 상태에주의하십시오. 보드는 기본적으로 -1 값으로 채워져 있으므로 if(board[col][row]!=null && board[col][row]!=id)으로 변경해야 한다고 생각합니다 if(board[col][row]!=-1..... 확실하다면 OP의 github에서 game.Game.genBoard ()를 확인하십시오. 나는 당신의 random()의지가 당신이 원하는 것을 할 것인지, 아마도 사용 (int)Math.random()*col합니까?
Katenkyo

@Katenkyo 대단히 감사합니다, 당신이 웃게된다면 기뻐요! 이 random()방법은 Player수업 중입니다! 그래서 나는 그것이 효과가 있다고 생각합니다 =) 그러나 예, 나는 내 조건에 확신이 없었습니다. OP의 코드에서 어떻게 정의되는지 찾지 못했지만 다시 확인하겠습니다. 대단히 감사합니다!
Keker

Player 클래스는 random ()을로 정의 public double random() {return ThreadLocalRandom.current().nextDouble();}합니다. 정확히 어떻게 작동하는지 모르지만 0과 1 사이의 값을 반환한다고 가정하므로 다음을 수행해야 할 수도 있습니다. (int)random()*col:)
Katenkyo

@Katenkyo 오, 나는 이미 그렇게 생각-내 나쁜. 보드에서 빈 셀에 적합한 값을 찾으면 편집하겠습니다. 다시 감사합니다!
Keker

당신이 올바른지 @Katenkyo, nextDouble사이의 숫자를 반환 0하고 1. 시뮬레이션이 병렬로 실행되고 Math.random()스레드 안전하지 않기 때문에 포함 시켰습니다 .
J Atkin

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에서 누군가의 코드를 완전히 수정하는 것은 허용되지 않습니다.
Nathan Merrill


2

버기 봇

이길 수있는 샘플 봇 (FYI : 어렵지 않습니다.)

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

2

포장 봇

이 봇은 포인트를 직접 목표로하지 않습니다. 그는 보드가 채워질 때까지 최대 토큰을 포장하려고합니다. 그는 단순히 몇 번이고 올라가는 것이 바보라는 것을 이해했으며, 자신의 "도메인"주위에 무작위로 토큰을 넣을 것입니다.

그는 모든 방향으로 점수를 얻을 수 있어야하지만 최고는 아닙니다!

(검증되지 않은)

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
Steve는 어려운 시간을 보내고 BasicBlockBot있습니다.
J Atkin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.