Hubungkan-n waktu!

20

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

Apakah ada yang ingat permainan 2 pemain terhubung 4? Bagi mereka yang tidak melakukannya adalah papan 6x7 yang berdiri vertikal di atas permukaan. Tujuan dari connect 4 adalah untuk, well connect 4! Koneksi dihitung jika horisontal, diagonal, atau vertikal. Anda meletakkan potongan-potongan Anda di papan tulis dengan memasukkan potongan di bagian atas kolom di mana ia jatuh ke bagian bawah kolom itu. Aturan kami mengubah 3 hal yang terhubung 4.

  • Ubah # 1 Menang didefinisikan sebagai pemain dengan poin terbanyak. Anda mendapatkan poin dengan menghubungkan 4 suka dalam aturan - lebih lanjut tentang itu nanti.
  • Ubah # 2 Anda memiliki 3 pemain setiap putaran.
  • Ubah # 3 Ukuran papan adalah 9x9.

Mencetak:

Skor didasarkan pada berapa banyak yang Anda dapatkan secara berturut-turut. Jika Anda memiliki 4 dalam grup baris, Anda mendapatkan 1 poin. Jika Anda memiliki 5 di grup baris, Anda mendapatkan 2 poin, 6 di baris 3, dan seterusnya.

Contoh:

Catatan odan xdiganti dengan #dan ~masing - masing, untuk kontras yang lebih baik

Contoh papan kosong: (semua contoh adalah papan ukuran standar 2 pemain)

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

Jika kita menjatuhkan sepotong coll d, itu akan mendarat di lokasi 1d.

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

Jika kita sekarang menjatuhkan sepotong coll dlagi, itu akan mendarat di lokasi 2d. Berikut adalah contoh dari 4 posisi berturut-turut:

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

Dalam hal ini xmendapat 1 poin secara diagonal ( 1a 2b 3c 4d).

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

Dalam hal ini, odapatkan 1 poin secara vertikal ( 1d 2d 3d 4d).

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

Dalam hal ini omendapat 2 poin secara horizontal ( 1c 1d 1e 1f 1g) dan xmendapat 1 poin secara horizontal ( 2c 2d 2e 2f).

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

Kali ini xmendapat 3 poin untuk 6 berturut-turut ( 1c 2c 3c 4c 5c 6c).

Input output

Anda akan memiliki akses ke papan melalui array 2d. Setiap lokasi akan diwakili dengan intid pemain yang mewakili. Anda juga akan memiliki id pemain Anda diteruskan ke fungsi Anda. Anda bergerak dengan mengembalikan coll yang ingin Anda masukkan. Setiap ronde 3 pemain akan dipilih untuk dimainkan. Di akhir permainan, semua pemain akan memainkan jumlah pertandingan yang genap.

Untuk saat putaran 100k akan dijalankan (perhatikan ini membutuhkan waktu yang lama , Anda mungkin ingin menguranginya untuk pengujian turnaround cepat). Secara keseluruhan pemenangnya adalah pemain dengan kemenangan terbanyak.

Pengontrol dapat ditemukan di sini: https://github.com/JJ-Atkinson/Connect-n/tree/master .

Menulis bot:

Untuk menulis bot, Anda harus memperluas Playerkelas. Playerabstrak dan memiliki satu metode untuk diterapkan int makeMove(void),. Dalam makeMoveAnda akan memutuskan coll Anda ingin meletakkan bola Anda ke dalam. Jika Anda memilih coll yang tidak valid (mis. Coll tidak ada, coll sudah terisi), giliran Anda akan dilewati . Di Playerkelas Anda memiliki banyak metode penolong yang berguna. Daftar yang paling penting adalah sebagai berikut:

  • boolean ensureValidMove(int coll): Kembalikan true jika coll ada di papan tulis dan coll belum terisi.
  • int[] getBoardSize(): Mengembalikan array int di mana [0]jumlah kolom, dan [1]jumlah baris.
  • int[][] getBoard(): Kembalikan salinan papan. Anda harus mengaksesnya seperti ini: [coll number][row number from bottom].
  • Untuk menemukan yang lainnya, lihat Playerkelasnya.
  • EMPTY_CELL: Nilai sel kosong

Karena ini akan multi-threaded, saya juga menyertakan randomfungsi jika Anda membutuhkannya.

Debugging bot Anda:

Saya telah memasukkan beberapa hal dalam controller untuk membuatnya lebih mudah untuk men-debug bot. Yang pertama adalah Runner#SHOW_STATISTICS. Jika ini diaktifkan, Anda akan melihat hasil cetak dari grup pemain yang dimainkan, termasuk hitungan bot yang menang. Contoh:

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

Anda juga dapat membuat permainan khusus dengan connectn.game.CustomGamekelas, Anda dapat melihat skor dan pemenang dari setiap babak. Anda bahkan dapat menambahkan diri Anda ke dalam campuran UserBot.

Menambahkan bot Anda:

Untuk menambahkan bot Anda ke barisan, pergi ke PlayerFactoryblok statis dan tambahkan baris berikut:

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

Hal-hal lain yang perlu diperhatikan:

  • Simulasi multi-threaded. Jika Anda ingin mematikannya, buka Runner#runGames()dan komentari baris ini ( .parallel()).
  • Untuk mengubah jumlah game, atur Runner#MINIMUM_NUMBER_OF_GAMESsesuai keinginan Anda.

Ditambahkan nanti:

  • Komunikasi di antara bot tidak diizinkan.

Terkait: Play Connect 4!

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

Papan Skor: (100.000 permainan)

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
sumber
Bisakah Anda menambahkan fungsionalitas untuk menentukan giliran permainan yang aktif?
Conor O'Brien
Sudah selesai, periksa Playerkelas untuk melihat semua metode yang tersedia.
J Atkin
7
"a square 6x7" itu bukan square
ev3commander
1
Memberi pemain kemampuan untuk "lulus" dengan melakukan gerakan ilegal mengubah dinamika sedikit. Apakah permainan berakhir jika semua orang lewat?
histokrat
1
Ya, itu sebabnya sangat penting untuk digunakan ensureValidMove(kecuali strategi Anda adalah untuk melewati belokan ini tentu saja).
J Atkin

Jawaban:

11

MaxGayne

Bot ini memberikan skor untuk setiap posisi, terutama berdasarkan panjang bagian yang terhubung. Terlihat 3 langkah dalam memeriksa 3 gerakan terbaik di setiap tahap, dan memilih satu dengan skor maksimum yang diharapkan.

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
sumber
Sangat sangat bagus! +1
J Atkin
Sesuatu yang saya perhatikan ketika saya bermain-main dengan UserBotdan bot Anda adalah bahwa setelah beberapa titik MaxGayneakan membuang bergantian (misalnya setelah 15 bergerak itu melompati setiap belokan sampai permainan berakhir).
J Atkin
Penyebabnya mungkin adalah bug di CustomGame. Ini menggunakan ID pemain berbasis 0 dan bukan berbasis 1 seperti game utama. Ini hanya merusak bot saya. Ada 2 masalah lagi. javafx.util.Pairtidak berfungsi di Eclipse karena itu tidak dianggap sebagai bagian dari API publik. Dan saya tidak tahu ke mana harus mencari sun.plugin.dom.exception.InvalidStateException. Anda mungkin bermaksud java.lang.IllegalStateException.
Sleafar
Tampaknya agak aneh ... Bagaimanapun Pair, itu sedekat yang saya bisa dengan tipe data yang saya inginkan tanpa menggulir sendiri, jadi kecuali gerhana tidak akan dikompilasi, saya pikir tidak apa-apa. Sedangkan untuk # 3, Anda benar, autocomplete saya di IntelliJ tidak selalu benar. (sebagian besar waktu itu, itu sebabnya saya tidak memeriksa)
J Atkin
@JAtkin Sebenarnya, Pairmasalahnya benar-benar mencegah kompilasi di Eclipse, kecuali Anda tahu solusinya .
Sleafar
6

RowBot

Lihat ke segala arah dan tentukan kolom optimal. Mencoba menghubungkan bagiannya, sementara tidak membiarkan lawannya melakukan hal yang sama.

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

OnePlayBot

Bot ini hanya memiliki satu permainan - letakkan potongannya di sel paling kiri yang valid. Anehnya itu cukup bagus;)

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

RandomBot

Cukup masukkan sepotong di mana saja yang valid.

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

StraightForwardBot

Mirip dengan OnePlayBot tetapi memperhitungkan langkah terakhir dan memainkan kolom berikutnya yang valid.

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

JealousBot

Bot ini membenci pemain lain. Dan dia tidak suka bahwa dia menjatuhkan potongan di papan tulis. Jadi dia mencoba menjadi yang terakhir yang menjatuhkan sepotong di kolom.

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

Ini pertama kalinya saya di CodeGolf, jadi saya harap jawaban ini cukup baik. Saya belum bisa mengujinya, jadi tolong maafkan saya jika ada kesalahan.

EDIT : Menambahkan baris untuk memecahkan yang kedua for.

EDIT 2 : Mengetahui mengapa whileitu tidak terbatas. Sekarang sudah lengkap dan bisa digunakan!

Keker
sumber
Selamat datang di PPCG, Anda membuat saya tertawa dengan jawaban ini, itu hebat! Hanya peduli dengan kondisi Anda. Saya pikir papan diisi dengan nilai -1 secara default, jadi if(board[col][row]!=null && board[col][row]!=id)harus diubah menjadi if(board[col][row]!=-1..... Periksa di game.Game.genBoard () di github OP jika Anda ingin yakin. Saya juga tidak tahu apakah Anda random()akan melakukan apa yang Anda inginkan, mungkin menggunakan (int)Math.random()*col?
Katenkyo
@Katenkyo Terima kasih banyak, saya senang jika itu membuat Anda tertawa! The random()metode adalah di Playerkelas! Jadi saya pikir ini akan berhasil =) Tapi ya, saya tidak percaya diri dengan kondisi saya. Saya tidak menemukan cara mendefinisikannya dalam kode OP, tetapi saya akan memeriksanya lagi. Terima kasih banyak!
Keker
Kelas Player mendefinisikan acak () sebagai public double random() {return ThreadLocalRandom.current().nextDouble();}. Saya tidak tahu persis bagaimana cara kerjanya, tapi saya menganggapnya mengembalikan nilai antara 0 dan 1, jadi mungkin perlu dilakukan (int)random()*col:)
Katenkyo
@Katenkyo Oh, saya pikir sudah melakukan itu ... Buruk saya. Saya akan mengeditnya ketika saya menemukan nilai yang tepat untuk sel kosong di papan, terima kasih lagi!
Keker
@Katenkyo Anda benar, nextDoublemengembalikan nomor antara 0dan 1. Saya memasukkannya karena simulasi dijalankan secara paralel, dan Math.random()tidak aman untuk thread.
J Atkin
3

BasicBlockBot

Bot blok sederhana (dan naif). Dia tidak tahu Anda bisa membuat 4 berturut-turut secara horizontal atau diagonal!

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

Progresif

Progresif adalah ... progresif. Dia suka melihat semuanya , dan beberapa! (Saya tidak yakin dengan metodologi ini. Ini bekerja melawan teman, sekali.) Dan, untuk beberapa alasan, itu berfungsi dengan baik.

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
sumber
@JAtkin Maaf, saya punya versi kode yang lebih lama.
Conor O'Brien
3
@JAtkin saya menolak suntingan Anda. Anda harus memperbolehkan mereka untuk memperbaiki kode mereka di posting mereka. Jika Anda ingin memperbaikinya untuk pengontrol Anda, tidak apa-apa (saya pribadi masih akan meninggalkan catatan), tetapi modifikasi langsung kode seseorang di SE tidak diizinkan.
Nathan Merrill
3

FairDiceRoll

Selalu mengembalikan 4.

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

BuggyBot

Bot sampel untuk dikalahkan (FYI: tidak sulit;)

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

PackingBot

Bot ini tidak bertujuan untuk mendapatkan poin secara langsung. Dia mencoba mengemas token maksimum hingga papan terisi. Dia mengerti bahwa hanya naik lagi dan lagi itu bodoh, jadi dia akan secara acak menempatkan token di sekitar "domain" -nya.

Dia seharusnya bisa mendapatkan beberapa poin di semua arah, tetapi tidak akan menjadi yang terbaik!

(Tidak diuji)

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
sumber
@JAtkin Terima kasih telah menunjukkan itu, tetap :)
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
sumber
2
Steve mengalami kesulitan, dia mendapat nilai di bawah BasicBlockBot.
J Atkin