Connect-n time!


20

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

Quelqu'un se souvient-il du jeu à 2 joueurs Connect 4? Pour ceux qui ne l'ont pas, c'était une planche 6x7 qui se tient verticale sur une surface. L'objectif de Connect 4 est de bien connecter 4! La connexion est comptée si elle est horizontale, diagonale ou verticale. Vous placez vos pièces sur le plateau en insérant une pièce en haut d'une colonne où elle tombe au bas de cette colonne. Nos règles changent 3 choses dans connect 4.

  • Changement # 1 Gagner est défini comme le joueur avec le plus de points. Vous obtenez des points en connectant 4 comme dans les règles - plus à ce sujet plus tard.
  • Changement # 2 Vous avez 3 joueurs à chaque tour.
  • Changement n ° 3 La taille du tableau est 9x9.

Notation:

Le score est basé sur le nombre que vous obtenez consécutivement. Si vous avez un groupe de 4 dans une rangée, vous obtenez 1 point. Si vous avez un groupe de 5 dans une rangée, vous obtenez 2 points, 6 dans une rangée 3 et ainsi de suite.

Exemples:

Notez oet xsont remplacés par #et ~respectivement, pour un meilleur contraste

Exemple de plateau vide: (tous les exemples sont des plateaux de taille standard pour 2 joueurs)

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

Si nous déposons un morceau en coll d, il atterrira à l'emplacement 1d.

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

Si nous déposons à dnouveau un morceau en coll , il atterrira à l'emplacement 2d. Voici des exemples de 4 positions consécutives:

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

Dans ce cas, xobtient 1 point en diagonale ( 1a 2b 3c 4d).

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

Dans ce cas, oobtient 1 point verticalement ( 1d 2d 3d 4d).

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

Dans ce cas, oobtient 2 points horizontalement ( 1c 1d 1e 1f 1g) et xobtient 1 point horizontalement ( 2c 2d 2e 2f).

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

Cette fois xobtient 3 points pour un 6 d'affilée ( 1c 2c 3c 4c 5c 6c).

Entrée sortie

Vous aurez accès à la carte via un tableau 2D. Chaque emplacement sera représenté par un intidentifiant représentant un joueur. Vous aurez également votre identifiant de joueur transmis à votre fonction. Vous vous déplacez en retournant dans quel coll dans lequel vous voulez déposer votre pièce. Chaque tour 3 joueurs seront choisis pour jouer. À la fin de la partie, tous les joueurs auront joué une quantité égale de parties.

Pour le moment, des tours de 100k seront exécutés (notez que cela prend beaucoup de temps, vous voudrez peut-être le réduire pour un test de rotation rapide). Dans l'ensemble, le gagnant est le joueur qui a le plus de victoires.

Le contrôleur peut être trouvé ici: https://github.com/JJ-Atkinson/Connect-n/tree/master .

Écrire un bot:

Pour écrire un bot, vous devez étendre la Playerclasse. Playerest abstraite et a une méthode pour mettre en œuvre, int makeMove(void). Dans, makeMovevous déciderez dans quel coll vous souhaitez déposer votre pièce. Si vous avez choisi un coll invalide (par exemple coll n'existe pas, le coll est déjà rempli), votre tour sera ignoré . Dans la Playerclasse, vous disposez de nombreuses méthodes d'aide utiles. Une liste des plus importants suit:

  • boolean ensureValidMove(int coll): Retourne vrai si le coll est sur le plateau et qu'il n'est pas encore rempli.
  • int[] getBoardSize(): Renvoie un tableau int où [0]est le nombre de colonnes et [1]le nombre de lignes.
  • int[][] getBoard(): Retournez une copie du tableau. Vous devriez y accéder comme ceci: [coll number][row number from bottom].
  • Pour trouver le reste, regardez la Playerclasse.
  • EMPTY_CELL: La valeur d'une cellule vide

Puisque ce sera multi-thread, j'ai également inclus une randomfonction si vous en avez besoin.

Débogage de votre bot:

J'ai inclus certaines choses dans le contrôleur pour simplifier le débogage d'un bot. Le premier est Runner#SHOW_STATISTICS. Si cette option est activée, vous verrez une impression des groupes de joueurs joués, y compris le nombre de victoires du bot. Exemple:

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

Vous pouvez également faire un jeu personnalisé avec la connectn.game.CustomGameclasse, vous pouvez voir les scores et le gagnant de chaque tour. Vous pouvez même vous ajouter au mélange avec UserBot.

Ajout de votre bot:

Pour ajouter votre bot à la programmation, accédez au PlayerFactorybloc statique et ajoutez la ligne suivante:

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

Autres choses à noter:

  • Les simulations sont multi-thread. Si vous souhaitez désactiver cela, allez sur Runner#runGames()et commentez cette ligne ( .parallel()).
  • Pour modifier le nombre de jeux, définissez-le Runner#MINIMUM_NUMBER_OF_GAMESà votre guise.

Ajouté plus tard:

  • La communication entre les robots est interdite.

Connexes: jouez à Connect 4!

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

Tableau de bord: (100 000 jeux)

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

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


Pouvez-vous ajouter des fonctionnalités pour déterminer à quel tour le jeu est activé?
Conor O'Brien

Déjà fait, vérifiez la Playerclasse pour voir toutes les méthodes disponibles.
J Atkin

7
"un carré 6x7" qui n'est pas un carré
ev3commander

1
Donner aux joueurs la possibilité de "passer" en effectuant un mouvement illégal modifie un peu la dynamique. Le jeu se termine-t-il si tout le monde passe?
histocrate

1
Oui, c'est pourquoi il est très important d'utiliser ensureValidMove(sauf si votre stratégie est de passer ce tour bien sûr).
J Atkin

Réponses:


11

MaxGayne

Ce bot attribue un score à chaque position, basé principalement sur la longueur des parties connectées. Il regarde en profondeur 3 mouvements en inspectant les 3 mouvements les plus beaux à chaque étape, et choisit celui avec le score maximum attendu.

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

Très très gentil! +1
J Atkin

Quelque chose que j'ai remarqué pendant que je jouais avec UserBotet votre bot était qu'après un certain point, MaxGayneil perdrait des tours (par exemple, après 15 mouvements, il saute chaque tour jusqu'à la fin du jeu).
J Atkin

La cause de ceci est probablement un bogue dans CustomGame. Il utilise des ID de joueur basés sur 0 au lieu de basés sur 1 comme le jeu principal. Cela brise simplement mon bot. Il y a 2 autres problèmes. javafx.util.Pairne fonctionne pas dans Eclipse car il n'est pas considéré comme faisant partie de l'API publique. Et je ne sais pas où chercher sun.plugin.dom.exception.InvalidStateException. Vous vouliez probablement dire java.lang.IllegalStateException.
Sleafar

Cela semble un peu étrange ... Quoi qu'il en soit Pair, c'est aussi proche que possible du type de données que je veux sans rouler le mien, donc à moins que l'éclipse ne compile pas, je pense que c'est OK. Quant au numéro 3, vous avez raison, ma saisie semi-automatique dans IntelliJ n'est pas toujours correcte. (la plupart du temps, c'est pourquoi je n'ai pas vérifié)
J Atkin

@JAtkin En fait, le Pairproblème empêche vraiment la compilation dans Eclipse, sauf si vous connaissez la solution de contournement .
Sleafar

6

RowBot

Regarde dans toutes les directions et détermine la colonne optimale. Tente de relier ses pièces, tout en ne laissant pas ses adversaires faire de même.

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

Ce bot n'a qu'un jeu - placez sa pièce dans la cellule la plus à gauche qui est valide. Assez curieusement, cela fait assez bien;)

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

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

        return attemptedMove;
    }
}

3

RandomBot

Il suffit de mettre un morceau n'importe où qui soit valide.

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

Semblable à OnePlayBot mais prend en compte le dernier mouvement et lit la colonne suivante sur celle qui est valide.

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

JealousBot

Ce bot déteste l'autre joueur. Et il n'aime pas qu'il laisse tomber des morceaux sur la planche. Il essaie donc d'être le dernier à avoir déposé un morceau dans une colonne.

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

C'est ma première fois sur CodeGolf, donc j'espère que cette réponse sera assez bonne. Je n'ai pas encore pu le tester, alors veuillez m'excuser s'il y a des erreurs.

EDIT : Ajout d'une ligne pour briser la seconde for.

EDIT 2 : Compris pourquoi l' whileinfini était. Il est maintenant terminé et peut être utilisé!


Bienvenue chez PPCG, vous m'avez fait rire avec cette réponse, c'est super! Faites juste attention à vos conditions. Je pense que le tableau est rempli avec -1 valeurs par défaut, donc if(board[col][row]!=null && board[col][row]!=id)devrait être changé en if(board[col][row]!=-1..... Vérifiez game.Game.genBoard () dans le github d'OP si vous voulez en être sûr. Je ne sais pas non plus si tu random()feras ce que tu veux, peut-être utiliser (int)Math.random()*col?
Katenkyo

@Katenkyo Merci beaucoup, je suis content si ça vous a fait rire! La random()méthode est en Playerclasse! Je pense donc que cela fonctionnera =) Mais oui, je n'étais pas confiant dans mes conditions. Je n'ai pas trouvé comment il est défini dans le code OP, mais je vais vérifier à nouveau. Merci beaucoup!
Keker

La classe Player définit random () comme public double random() {return ThreadLocalRandom.current().nextDouble();}. Je ne sais pas exactement comment cela fonctionne, mais je suppose qu'il renvoie une valeur comprise entre 0 et 1, il faudrait donc peut-être le faire (int)random()*col:)
Katenkyo

@Katenkyo Oh, je pensais que c'était déjà fait ... Mon mauvais. Je l'éditerai lorsque j'aurai trouvé la bonne valeur pour une cellule vide dans le tableau, merci encore!
Keker

@Katenkyo Vous avez raison, nextDoublerenvoie un nombre entre 0et 1. Je l'ai inclus car les simulations sont exécutées en parallèle et Math.random()ne sont pas thread-safe.
J Atkin

3

BasicBlockBot

Un bot de bloc simple (et naïf). Il ne sait pas que vous pouvez faire un 4 de suite horizontalement ou en diagonale!

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

Progressive

Progressive est ... progressive. Il aime regarder tout , et quelques - uns! (Je ne suis pas sûr de la méthodologie de cela. Cela a fonctionné contre un ami, une fois.) Et, pour une raison quelconque, cela fonctionne décemment.

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 Désolé, j'avais une ancienne version du code.
Conor O'Brien

3
@JAtkin J'ai rejeté votre modification. Vous devez leur permettre de corriger leur code dans leur message. Si vous voulez le réparer pour votre contrôleur, c'est bien (je laisserais personnellement une note), mais la modification pure et simple du code de quelqu'un sur SE n'est pas autorisée.
Nathan Merrill


2

BuggyBot

Un échantillon de bot à battre (pour info: ce n'est pas difficile;)

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

2

PackingBot

Ce bot ne vise pas directement des points. Il essaie d'emballer un maximum de jetons jusqu'à ce que le plateau soit rempli. Il a compris que monter et recommencer est stupide, alors il mettra au hasard des jetons autour de son "domaine".

Il devrait pouvoir obtenir des points dans toutes les directions, mais ce ne sera pas le meilleur!

(Pas testé)

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 Merci d'avoir signalé cela, corrigé :)
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;
    }
}

2
Steve a du mal, il marque moins BasicBlockBot.
J Atkin
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.