Connect-n Zeit!

20

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

Erinnert sich jemand an das 2-Spieler-Spiel Connect 4? Für diejenigen, die es nicht tun, war es ein 6x7-Brett, das vertikal auf einer Oberfläche steht. Das Ziel von connect 4 ist es, 4 gut zu verbinden! Die Verbindung wird gezählt, wenn sie horizontal, diagonal oder vertikal ist. Sie platzieren Ihre Spielsteine ​​auf dem Brett, indem Sie einen Stein am oberen Rand einer Spalte einfügen, der am unteren Rand dieser Spalte liegt. Unsere Regeln ändern 3 Dinge in Connect 4.

  • Änderung Nr. 1 Der Gewinn wird als der Spieler mit den meisten Punkten definiert. Sie erhalten Punkte, indem Sie 4 wie in den Regeln verbinden - dazu später mehr.
  • Änderung Nr. 2 Sie haben 3 Spieler pro Runde.
  • Änderung Nr. 3 Die Brettgröße ist 9x9.

Wertung:

Das Ergebnis basiert darauf, wie viele Sie in einer Reihe erhalten. Wenn Sie eine 4 in einer Zeilengruppe haben, erhalten Sie 1 Punkt. Wenn Sie eine 5 in einer Reihengruppe haben, erhalten Sie 2 Punkte, 6 in einer Reihe 3 und so weiter.

Beispiele:

Beachten Sie ound xwerden mit #und ~ersetzt, um den Kontrast zu verbessern

Beispiel für ein leeres Brett: (Alle Beispiele sind Bretter in Standardgröße für 2 Spieler.)

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

Wenn wir ein Stück in coll fallen lassen d, wird es an Ort und Stelle landen 1d.

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

Wenn wir nun ein Stück wieder in coll ablegen d, landet es an Ort und Stelle 2d. Hier sind Beispiele für 4 in einer Reihe Positionen:

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

In diesem Fall xerhält 1 Punkt diagonal ( 1a 2b 3c 4d).

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

In diesem Fall oerhält man 1 Punkt senkrecht ( 1d 2d 3d 4d).

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

In diesem Fall erhalten Sie o2 Punkte horizontal ( 1c 1d 1e 1f 1g) und x1 Punkt horizontal ( 2c 2d 2e 2f).

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

Dieses Mal xerhält 3 Punkte für eine 6 in einer Reihe ( 1c 2c 3c 4c 5c 6c).

Input-Output

Sie haben über ein 2D-Array Zugriff auf die Karte. Jeder Ort wird mit einer intSpielerkennung dargestellt. Sie werden auch Ihre Player-ID an Ihre Funktion übergeben. Sie machen Ihren Zug, indem Sie den Coll zurückgeben, in den Sie Ihr Stück fallen lassen möchten. In jeder Runde werden 3 Spieler ausgewählt. Am Ende des Spiels haben alle Spieler eine gerade Anzahl von Spielen gespielt.

Momentan werden 100.000 Runden ausgeführt (beachten Sie, dass dies sehr lange dauert. Möglicherweise möchten Sie es für schnelle Turnaround-Tests reduzieren). Insgesamt gewinnt der Spieler mit den meisten Siegen.

Den Controller finden Sie hier: https://github.com/JJ-Atkinson/Connect-n/tree/master .

Einen Bot schreiben:

Um einen Bot zu schreiben, müssen Sie die PlayerKlasse erweitern. Playerist abstrakt und ein Verfahren zu implementieren hat int makeMove(void). In makeMovewelcher Coll du dein Stück ablegen möchtest, entscheidest du. Wenn Sie ein ungültiges Coll gewählt haben (zB Coll existiert nicht, Coll ist bereits gefüllt), wird Ihr Zug übersprungen . In der PlayerKlasse haben Sie viele nützliche Hilfsmethoden. Es folgt eine Auflistung der wichtigsten:

  • boolean ensureValidMove(int coll): Gib true zurück, wenn sich die Coll auf dem Board befindet und die Coll noch nicht gefüllt ist.
  • int[] getBoardSize(): Gibt ein int-Array zurück, in dem [0]die Anzahl der Spalten und [1]die Anzahl der Zeilen angegeben ist.
  • int[][] getBoard(): Gib eine Kopie der Tafel zurück. Sie sollten es so zugreifen: [coll number][row number from bottom].
  • Um den Rest zu finden, schauen Sie sich die PlayerKlasse an.
  • EMPTY_CELL: Der Wert einer leeren Zelle

Da es sich um eine Multithread- randomFunktion handelt , habe ich bei Bedarf auch eine Funktion hinzugefügt.

Debuggen Ihres Bots:

Ich habe einige Dinge in den Controller aufgenommen, um das Debuggen eines Bots zu vereinfachen. Der erste ist Runner#SHOW_STATISTICS. Wenn dies aktiviert ist, sehen Sie einen Ausdruck der gespielten Spielergruppen, einschließlich der Anzahl der Bot-Gewinne. Beispiel:

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

Sie können mit der connectn.game.CustomGameKlasse auch ein benutzerdefiniertes Spiel erstellen. Sie können die Ergebnisse und Gewinner jeder Runde anzeigen. Sie können sich sogar dem Mix mit hinzufügen UserBot.

Hinzufügen Ihres Bots:

Um Ihren Bot zur Aufstellung hinzuzufügen, gehen Sie zum PlayerFactorystatischen Block und fügen Sie die folgende Zeile hinzu:

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

Andere Dinge zu beachten:

  • Die Simulationen sind multithreaded. Wenn Sie das deaktivieren möchten, gehen Sie zu Runner#runGames()und kommentieren Sie diese Zeile ( .parallel()).
  • Um die Anzahl der Spiele zu ändern, stellen Sie sie Runner#MINIMUM_NUMBER_OF_GAMESnach Ihren Wünschen ein.

Später hinzugefügt:

  • Kommunikation zwischen Bots ist nicht erlaubt.

Siehe auch: Play Connect 4!

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

Anzeigetafel: (100 000 Spiele)

MaxGayne -> 22662
RowBot -> 17884
OnePlayBot -> 10354
JealousBot -> 10140
Progressive -> 7965
Draw -> 7553
StraightForwardBot -> 7542
RandomBot -> 6700
PackingBot -> 5317
BasicBlockBot -> 1282
BuggyBot -> 1114
FairDiceRoll -> 853
Steve -> 634

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

J Atkin
quelle
Können Sie Funktionen hinzufügen, um festzustellen, in welcher Runde das Spiel läuft?
Conor O'Brien
Überprüfen Sie bereits die PlayerKlasse, um alle verfügbaren Methoden anzuzeigen.
J Atkin
7
"Ein Quadrat 6x7" ist kein Quadrat
ev3commander
1
Indem Sie den Spielern die Möglichkeit geben, durch einen illegalen Zug zu "passen", ändern Sie die Dynamik ein wenig. Endet das Spiel, wenn alle passen?
Histokrat
1
Ja, das ist der Grund, warum es sehr wichtig ist, dies zu tun ensureValidMove(es sei denn, Ihre Strategie besteht darin, diese Runde zu bestehen).
J Atkin

Antworten:

11

MaxGayne

Dieser Bot weist jeder Position eine Punktzahl zu, die hauptsächlich auf der Länge der verbundenen Teile basiert. Es sieht so aus, als wären es 3 Züge in die Tiefe gegangen, wobei in jeder Phase 3 am besten aussehende Züge untersucht werden.

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
quelle
Sehr sehr nett! +1
J Atkin
Etwas, das mir aufgefallen ist, als ich herumgespielt habe UserBotund dein Bot war, dass er nach einem bestimmten Zeitpunkt MaxGayneRunden wegwirft (z. B. nach 15 Zügen wird jede Runde übersprungen, bis das Spiel endet).
J Atkin
Die Ursache dafür ist wahrscheinlich ein Fehler in CustomGame. Es werden 0-basierte Spieler-IDs anstelle von 1-basierten wie im Hauptspiel verwendet. Das bricht einfach meinen Bot. Es gibt zwei weitere Probleme. javafx.util.Pairfunktioniert in Eclipse nicht, da es nicht als Teil der öffentlichen API betrachtet wird. Und ich habe keine Ahnung, wo ich suchen soll sun.plugin.dom.exception.InvalidStateException. Sie meinten wahrscheinlich java.lang.IllegalStateException.
Sleafar
Das scheint ein wenig seltsam zu sein ... Wie auch immer Pair, das ist so nah wie möglich an den gewünschten Datentyp, ohne meinen eigenen zu rollen. Wenn Eclipse nicht kompiliert werden kann, denke ich, ist es in Ordnung. Was # 3 betrifft, haben Sie recht, meine automatische Vervollständigung in IntelliJ ist nicht immer richtig. (die meiste Zeit ist es, deshalb habe ich nicht überprüft)
J Atkin
@JAtkin Eigentlich Pairverhindert das Problem das Kompilieren in Eclipse, es sei denn, Sie kennen die Problemumgehung .
Sleafar
6

RowBot

Schaut in alle Richtungen und ermittelt die optimale Säule. Versucht, seine Figuren zu verbinden, ohne dass seine Gegner dasselbe tun.

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

OnePlayBot

Dieser Bot hat nur ein Spiel - platziere seine Figur in der Zelle ganz links, die gültig ist. Seltsamerweise tut es ziemlich gut;)

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

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

        return attemptedMove;
    }
}
J Atkin
quelle
3

RandomBot

Legen Sie einfach ein Stück an eine beliebige Stelle, die gültig ist.

static class RandomBot extends Player {
    @Override
    int makeMove() {
        int attemptedMove = (int)Math.round(random() * getBoardSize()[0]);
        while (!ensureValidMove(attemptedMove))
            attemptedMove = (int)Math.round(random() * getBoardSize()[0]);

        return attemptedMove;
    }
}
J Atkin
quelle
3

StraightForwardBot

Ähnlich wie OnePlayBot, berücksichtigt jedoch den letzten Zug und spielt die nächste Spalte darüber ab, die gültig ist.

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

Eifersüchtiger Roboter

Dieser Bot hasst den anderen Spieler. Und er mag es nicht, dass er Steine ​​auf das Brett fallen lässt. Also versucht er der letzte zu sein, der ein Stück in eine Spalte fallen lässt.

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

Es ist mein erstes Mal auf CodeGolf, also hoffe ich, dass diese Antwort gut genug ist. Ich konnte es noch nicht testen, bitte entschuldigen Sie, falls es Fehler gibt.

BEARBEITEN : Es wurde eine Zeile hinzugefügt, um die Sekunde zu unterbrechen for.

EDIT 2 : Herausgefunden, warum das whileunendlich war. Es ist jetzt komplett und kann verwendet werden!

Keker
quelle
Willkommen bei PPCG, Sie haben mich mit dieser Antwort zum Lachen gebracht, das ist großartig! Seien Sie einfach vorsichtig mit Ihren Bedingungen. Ich denke das Board ist standardmäßig mit -1 Werten gefüllt, if(board[col][row]!=null && board[col][row]!=id)sollte also auf geändert werden if(board[col][row]!=-1..... Check in game.Game.genBoard () im Github von OP, wenn du sicher sein willst. Ich weiß auch nicht, ob dein random()Wille macht, was du willst, vielleicht benutzt (int)Math.random()*col?
Katenkyo
@Katenkyo Vielen Dank, ich freue mich, wenn es dich zum Lachen gebracht hat! Die random()Methode ist in der PlayerKlasse! Also denke ich, dass es funktionieren wird =) Aber ja, ich war nicht zuversichtlich in meinen Bedingungen. Ich habe nicht gefunden, wie es im OP-Code definiert ist, aber ich werde es noch einmal überprüfen. Vielen Dank!
Keker
Die Player-Klasse definiert random () als public double random() {return ThreadLocalRandom.current().nextDouble();}. Ich weiß nicht genau, wie es funktioniert, aber ich gehe davon aus, dass es einen Wert zwischen 0 und 1 zurückgibt, also müsste ich es möglicherweise tun (int)random()*col:)
Katenkyo
@Katenkyo Oh, ich dachte, das hat es schon getan ... Mein schlechtes. Ich bearbeite es, wenn ich den richtigen Wert für eine leere Zelle im Forum gefunden habe. Nochmals vielen Dank!
Keker
@Katenkyo Du bist korrekt, nextDoublegibt eine Zahl zwischen 0und zurück 1. Ich habe es eingefügt, da die Simulationen parallel ausgeführt werden und Math.random()nicht threadsicher sind.
J Atkin
3

BasicBlockBot

Ein einfacher (und naiver) Blockbot. Er weiß nicht, dass Sie eine 4 in einer Reihe horizontal oder diagonal machen können!

static class BasicBlockBot extends Player {
    @Override
    int makeMove() {
        List<Integer> inARows = detectInARows();
        double chanceOfBlock = 0.5;

        if (inARows.isEmpty())
            chanceOfBlock = 0;

        if (random() < chanceOfBlock) {
            return inARows.get((int)Math.round(random() * (inARows.size() - 1)));
        } else {
            return (int)Math.round(random() * getBoardSize()[0]);
        }
    }


    /**
     * Very limited - just detects vertical in a rows
     *
     * @return A list of colls that have 4 in a row vertical
     */
    private List<Integer> detectInARows() {
        List<Integer> ret = new ArrayList<>();
        int[][] board = getBoard();

        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[i].length; j++) {
                int currId = board[i][j];
                if (currId != -1 && is4InARowVertical(i, j, board)) {
                    ret.add(i);
                }
            }
        }

        return ret;
    }

    private boolean is4InARowVertical(int coll, int row, int[][] board) {
        int id = board[coll][row];

        for (int i = 0; i < 4; i++) {
            int y = row + i;
            if (!boardContains(coll,y) || board[coll][y] != id)
                return false;
        }
        return true;
    }

}
J Atkin
quelle
3

Progressiv

Progressiv ist ... progressiv. Er mag es, alles und einige anzuschauen! (Ich bin nicht sicher, wie das funktioniert. Es hat einmal gegen einen Freund funktioniert.) Und aus irgendeinem Grund funktioniert es anständig.

static class Progressive extends Player{
    @Override
    int makeMove(){
        int move = 0;
        boolean statusBroken = false;
        for(int n=getBoardSize()[0];n>2;n-=2){
            for(int i=0;i<getBoardSize()[0];i+=n){
                if(ensureValidMove(i)){
                    move = i;
                    statusBroken = true;
                    break;
                }
                if(statusBroken) break;
            }
        }
        return move;
    }
}
Conor O'Brien
quelle
@JAtkin Sorry, ich hatte eine ältere Version des Codes.
Conor O'Brien
3
@JAtkin Ich habe deine Bearbeitung abgelehnt. Sie sollten ihnen erlauben, ihren Code in ihrem Beitrag zu korrigieren. Wenn Sie es für Ihren Controller reparieren möchten, ist das in Ordnung (ich persönlich hinterlasse immer noch eine Notiz), aber eine direkte Änderung des Codes von jemandem in SE ist nicht zulässig.
Nathan Merrill
3

FairDiceRoll

Gibt immer 4 zurück.

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

BuggyBot

Ein Sample-Bot, den du schlagen kannst (FYI: Es ist nicht schwer;)

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

PackingBot

Dieser Bot zielt nicht direkt auf Punkte. Er versucht, maximal Jetons zu packen, bis das Brett voll ist. Er verstand, dass es dumm ist, einfach immer wieder aufzusteigen, also wird er nach dem Zufallsprinzip Token um seine "Domain" legen.

Er sollte in der Lage sein, einige Punkte in alle Richtungen zu holen, wird aber nicht der Beste sein!

(Nicht getestet)

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
quelle
@JAtkin Danke für den Hinweis, behoben :)
Katenkyo
2

Steve

package connectn.players;

import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;

import connectn.game.Game;

public class Steve extends Player {
    @Override
    public int makeMove() {
        Random r=ThreadLocalRandom.current();
        int attemptedMove = 0;
        int[][]board=getBoard();
        int ec=Game.EMPTY_CELL;
        for(int c=0;c<board.length;c++){
            int j=board[c].length-1;
            for(;j>=0;j--){
                if(board[c][j]!=ec)break;
            }

            if(j>2+r.nextInt(3)&&r.nextDouble()<0.8)return c;
        }
        int k=-2+board.length/2+r.nextInt(5);
        if(ensureValidMove(k))return k;
        for (int i = 0; i < getBoardSize()[0]; i++)
            if (ensureValidMove(i)) {
                attemptedMove = i;
                break;
            }

        return attemptedMove;
    }
}
SuperJedi224
quelle
2
Steve hat es schwer, er punktet unter BasicBlockBot.
J Atkin