Mainkan Antichess!

19

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

Ini pada dasarnya Turnamen Catur , tetapi untuk antikisah;)

Antichess adalah salah satu dari banyak varian catur yang telah ditemukan. Tujuannya adalah untuk kehilangan semua kepingan Anda (ini mungkin tampak sedikit aneh, tetapi itu disebut antichess karena suatu alasan).

Aturan

Aturan antichess sangat mirip dengan catur standar - tetapi dengan beberapa perbedaan yang cukup kecil. Tujuan seperti yang saya sebutkan di atas adalah kehilangan semua bagian Anda. Untuk mewujudkan ini, jika lawan Anda memiliki kesempatan untuk menangkap salah satu karya Anda, itu adalah satu-satunya langkah yang dapat ia lakukan. Jika Anda memberinya beberapa peluang sekaligus, pemain lain dapat memilih gilirannya. Hal lain yang diubah adalah bahwa raja tidak memiliki kekuatan khusus - karena Anda tidak dapat melakukan skakmat lawan, dan Anda tidak bisa memaksanya untuk memeriksanya.

Perubahan berikut pada game standar juga akan berlaku (mereka membantu menyederhanakan game):

  • Orang yang lewat akan diabaikan.
  • Castling tidak mungkin.
  • Aturan Fifty-move berlaku secara otomatis (artinya permainan berakhir imbang).
  • Pion akan dapat memilih apa yang mereka promosikan.
  • Jika seorang pemain membutuhkan lebih dari 2 detik untuk bergerak, ia akan kehilangan permainan.
  • Mengembalikan langkah yang tidak valid akan menghasilkan kekalahan dalam permainan.
  • Untuk menang, lawan Anda harus menangkap semua bagian Anda .
  • Putih memulai permainan.
  • Putih ditempatkan "di bagian bawah" bidang (y = 0), hitam terletak di bagian atas (y = 7).
  • Mengakses sumber daya selain bot Anda (internet, file, bot lain, ...) dilarang.

Mencetak gol

  • Menang memberi Anda 3 poin, satu imbang 1 poin dan kehilangan 0 poin.
  • Setiap kiriman akan bermain melawan kiriman lainnya 10 kali (5 kali putih, 5 sebagai hitam).

Menulis bot Anda

Kode pengontrol ada di sini: https://github.com/JJ-Atkinson/SimpleAntichessKOTH

Anda dapat menulis bot Anda dalam Java atau Groovy. Untuk menulis bot, Anda harus memperluas Playerkelas. Kelas pemain memiliki satu metode abstrak Move getMove(Board board, Player enemy, Set<Move> validMoves).

Berikut ini adalah ikhtisar cepat tentang metode yang berguna:

Player:

  • List<Piece> getPieces(Board board): Kembalikan semua bagian Anda yang ada di papan tulis.
  • PieceUpgradeType pieceUpgradeType: Jika / ketika salah satu pion Anda mencapai ujung papan, Anda harus menentukan ini untuk jenis karya yang ingin Anda tingkatkan ke. Anda memiliki pilihan ROOK, KNIGHT, QUEEN, BISHOP, dan KING.

Board:

  • Field getFieldAtLoc(Location loc): Kembalikan Fielddi lokasi. Ini memiliki getAtmetode yang cocok sehingga jika Anda menggunakan asyik Anda dapat menulis board[loc].
  • Field getFieldAtLoc(int x, int y): Kembalikan Fielddi lokasi. Ini memiliki getAtmetode yang cocok sehingga jika Anda menggunakan asyik Anda dapat menulis board[x, y].
  • Board movePiece(Player player, Move move): Bergeraklah di papan sehingga Anda dapat melihat bagaimana hasilnya. Ini mengembalikan papan baru.

Jika Anda ingin melihat karya lawan, tulis saja enemy.getPieces(board). Untuk menambahkan bot Anda ke barisan, tambahkan baris berikut ke PlayerFactory:

put(YourBot.class, { new YourBot() } )

Debugging bot Anda:

Saya telah menyertakan beberapa alat untuk membantu men-debug bot Anda. Untuk melihat permainan Anda dimainkan secara langsung, Anda dapat mengatur Game#DEBUGbendera menjadi true. Anda akan mendapatkan hasil seperti ini:

Game started. Players: [OnePlayBot(WHITE), SacrificeBot(BLACK)]
...
BLACKs turn.
validMoves: [Move(Piece(BLACK, PAWN, Loc(0, 6)), Loc(0, 5)), ...]
board:
RKBQIBKR
PPPPPPPP
--------
--------
--------
p-------
-ppppppp
rkbqibkr

captureless turns: 1
chosen move: Move(Piece(BLACK, PAWN, Loc(7, 6)), Loc(7, 4))
Game over? false

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

WHITEs turn.
validMoves: [Move(Piece(WHITE, ROOK, Loc(0, 0)), Loc(0, 1)), ...]
board:
RKBQIBKR
PPPPPPP-
--------
-------P
--------
p-------
-ppppppp
rkbqibkr

...

(Putih adalah huruf besar, raja ditunjukkan dengan i)

Jika konsol Anda mendukung karakter khusus utf-8, Anda bahkan dapat menunjukkan papan dengan karakter catur dengan menggunakan Board#USE_UTF8_TO_STRING:

♜♞♝♛♚♝—♜
♟—♟♟♟♟♟♟
————————
—♟——————
————————
♙———————
—♙♙♙♙♔♙♙
♖♘♗♕—♗♘♖

(terlihat lebih baik dengan font mono spaced)

Untuk mencegah membanjirnya output yang tidak diinginkan, Anda harus mengubah Main#mainfungsinya menjadi seperti ini:

new Game(new MyBot(), new SacrificeBot()).run()

Letakkan bot Anda di sebelah kiri untuk bermain sebagai putih, letakkan di sebelah kanan untuk bermain sebagai hitam.

Membangun pengontrol:

Pengontrol ditulis dalam groovy, jadi Anda harus menginstal java dan groovy. Jika Anda tidak ingin menginstal groovy, Anda dapat menggunakan file gradle build yang disertakan dengan controller (ini belum diuji). Jika Anda tidak ingin menggunakan groovy atau gradle, Anda dapat menggunakan tabung rilis terbaru ( https://github.com/JJ-Atkinson/SimpleAntichessKOTH/releases ). Jika Anda melakukan ini, Anda perlu membuat mainmetode Anda sendiri dan menambahkan bot Anda secara manual ke pabrik pemain. Contoh:

PlayerFactory.players.put(YourBot.class, { new YourBot() } )
new Runner().runGames();

(Perhatikan bahwa Anda masih dapat mengatur flag debug dan lainnya)

Setiap dan semua penemuan bug sangat dihargai!

Skor:

SearchBot -> 101
SacrificeBot -> 81
MeasureBot -> 37
RandomBot -> 28
OnePlayBot -> 24

Harap dicatat bahwa saya selalu bersedia menerima kiriman baru!

J Atkin
sumber
Jika Anda suka groovy dan IntelliJ ... Anda harus melihat Kotlin
TheNumberOne
Saya telah melihat Kotlin sebelumnya, tetapi tidak pernah melihatnya secara menyeluruh. Ini agak terlihat seperti scala / groovy mashup (tapi tidak apa-apa - groovy dan scala adalah bahasa favorit saya;)
J Atkin
Saya belum pernah menggunakan scala sebelumnya ... tetapi jauh lebih mudah untuk memanggil kode Kotlin dari java daripada memanggil kode goovy dari java.
TheNumberOne
1
Anda dapat meningkatkan ke raja?!? Tentunya tidak ...
wizzwizz4
1
@ wizzwizz4 Antichess, Anda bisa.
ProgramFOX

Jawaban:

6

SearchBot

Bot paling lambat sejauh ini, tetapi masih lebih cepat dari 2 detik per gerakan dan mengalahkan semua bot yang saat ini diposting. Ini melihat apa yang terjadi setelah setiap langkah yang valid dan apa yang bisa terjadi setelah setiap langkah setelah gerakan itu dan memutuskan apa yang akan menjadi hasil terbaik. Sayangnya tidak dapat mencari lebih dalam karena butuh lebih dari 2 detik.

package com.ppcgse.koth.antichess.player

import com.ppcgse.koth.antichess.controller.Board
import com.ppcgse.koth.antichess.controller.Color
import com.ppcgse.koth.antichess.controller.Location
import com.ppcgse.koth.antichess.controller.Move
import com.ppcgse.koth.antichess.controller.Piece
import com.ppcgse.koth.antichess.controller.PieceType
import com.ppcgse.koth.antichess.controller.PieceUpgradeType
import com.ppcgse.koth.antichess.controller.Player

import groovy.lang.Tuple

/**
 * Created by ProgramFOX on 12/22/15.
 */

 class SearchBot extends Player {
    {pieceUpgradeType = PieceUpgradeType.KING}

    @Override
    Move getMove(Board board, Player opponent, Set<Move> validMoves) {
        return getMoveInternal(board, this, opponent, validMoves, 2)[0]
    }

    Tuple getMoveInternal(Board board, Player whoseTurn, Player opponent, Set<Move> validMoves, Integer depth) {
        def bestScore = null
        def currentlyChosenMove = null
        validMoves.each { m ->
            def opponentPiecesValueBefore = opponent.getPieces(board).sum { getPieceValue(it.getType()) }
            def newBoard = board.movePiece(whoseTurn, m)
            def opponentPiecesValueAfter = opponent.getPieces(newBoard).sum { getPieceValue(it.getType()) }
            if (opponentPiecesValueAfter == null) {
                opponentPiecesValueAfter = 0
            }
            def score = opponentPiecesValueAfter - opponentPiecesValueBefore
            if (whoseTurn.getTeam() == Color.BLACK) {
                score = -score
            }
            if (depth > 1) {
                def validMovesNow = genValidMoves(opponent, whoseTurn, newBoard)
                def goDeeper = true
                if (validMovesNow == null || validMovesNow.size() == 0) {
                    def toAdd = -999
                    if (whoseTurn.getTeam() == Color.BLACK) {
                        toAdd = -toAdd
                    }
                    score += toAdd
                    goDeeper = false
                }
                if (goDeeper) {
                    score += getMoveInternal(newBoard, opponent, whoseTurn, validMovesNow, depth - 1)[1]
                }
            }
            if (bestScore == null) {
                bestScore = score
                currentlyChosenMove = m
            }
            if ((whoseTurn.getTeam() == Color.WHITE && score > bestScore) || (whoseTurn.getTeam() == Color.BLACK && score < bestScore))  {
                bestScore = score
                currentlyChosenMove = m
            }
        }
        return new Tuple(currentlyChosenMove, bestScore)
    }

    Double getPieceValue(PieceType pieceType) {
        switch (pieceType) {
            case PieceType.KING:
                return 1
            case PieceType.PAWN:
                return 1.5
            case PieceType.KNIGHT:
                return 2.5
            case PieceType.BISHOP:
                return 3
            case PieceType.ROOK:
                return 5
            case PieceType.QUEEN:
                return 9
            default:
                return 0
        }
    }

    // Copied from Game.groovy and a bit modified.
    // I actually need this.
    Set<Move> genValidMoves(Player player, Player enemy, Board board) {
        def allMoves = player.getPieces(board).collect { [it, it.getValidDestinationSet(board)] }
        def attackMoves = allMoves
                .collect { pair ->
            def piece = pair[0]
            def dests = pair[1]
            [piece, dests.findAll { board.getFieldAtLoc(it as Location)?.piece?.team == enemy.team }]
        }.findAll { it[1] }

        if (attackMoves.isEmpty())
            return allMoves.collect {
                Piece piece = it[0] as Piece
                return it[1].collect { loc -> new Move(piece, loc as Location) }
            }.flatten() as Set<Move>
        else
            return attackMoves.collect {
                Piece piece = it[0] as Piece
                return it[1].collect { loc -> new Move(piece, loc as Location) }
            }.flatten() as Set<Move>
    }
 }
ProgramFOX
sumber
4

SacrificeBot

Bot ini akan memeriksa semua gerakan yang dimiliki pemain lain dan akan memeriksa untuk melihat apakah ada dari mereka yang berpotongan (yaitu bagian tersebut akan terbunuh). (Ini sih jauh lebih baik dari yang pernah saya duga;)

package com.ppcgse.koth.antichess.player

import com.ppcgse.koth.antichess.controller.Board
import com.ppcgse.koth.antichess.controller.Color
import com.ppcgse.koth.antichess.controller.Location
import com.ppcgse.koth.antichess.controller.Move
import com.ppcgse.koth.antichess.controller.Piece
import com.ppcgse.koth.antichess.controller.PieceType
import com.ppcgse.koth.antichess.controller.PieceUpgradeType
import com.ppcgse.koth.antichess.controller.Player

import java.util.concurrent.ThreadLocalRandom

/**
 * Created by Jarrett on 12/19/15.
 */
class SacrificeBot extends Player {

    {pieceUpgradeType = PieceUpgradeType.ROOK}

    @Override
    Move getMove(Board board, Player enemy, Set<Move> validMoves) {
        def enemyPieces = enemy.getPieces(board)
        def pawnMoves = getPawnsMoves(board, enemyPieces)
        def enemyPlayerValidMoves = (enemyPieces
                                        .collect { it.getValidDestinationSet(realBoard) }
                                        .flatten() as List<Location>)
        enemyPlayerValidMoves += pawnMoves

        def sacrificeMove = validMoves
                                .find {enemyPlayerValidMoves.contains(it.destination)}

        if (sacrificeMove)
            return sacrificeMove
        else
            return randomMove(validMoves)
    }

    def randomMove(Set<Move> validMoves) {
        return validMoves[ThreadLocalRandom.current().nextInt(validMoves.size())];
    }

    def getPawnsMoves(Board board, List<Piece> allPieces) {
        def direction = getTeam() == Color.BLACK ? 1 : -1;
        def pawns = allPieces.findAll {it.type == PieceType.PAWN}
        def pawnAttacks = (pawns.collect {
                                    [it.loc.plus(-1, direction), it.loc.plus(1, direction)]
                                }.flatten()
                                ).findAll {
                                    ((Location) it).isValid()
                                }
        return pawnAttacks as List<Location>
    }
}
J Atkin
sumber
3

OnePlayBot

Bot sederhana yang mati hanya dengan satu permainan. Ini akan ditingkatkan menjadi benteng.

package com.ppcgse.koth.antichess.player

import com.ppcgse.koth.antichess.controller.Move
import com.ppcgse.koth.antichess.controller.PieceUpgradeType
import com.ppcgse.koth.antichess.controller.Player
import com.ppcgse.koth.antichess.controller.ReadOnlyBoard

public class OnePlayBot extends Player {

    {pieceUpgradeType = PieceUpgradeType.ROOK}

    @Override
    public Move getMove(ReadOnlyBoard board, Player enemy, Set<Move> moves) {
        return new ArrayList<Move>(moves).get(0);
    }

}
J Atkin
sumber
3

RandomBot

Ini adalah bot acak wajib. Itu akan selalu ditingkatkan ke benteng.

package com.ppcgse.koth.antichess.player

import com.ppcgse.koth.antichess.controller.PieceUpgradeType
import com.ppcgse.koth.antichess.controller.Player
import com.ppcgse.koth.antichess.controller.Move
import com.ppcgse.koth.antichess.controller.ReadOnlyBoard

import java.util.concurrent.ThreadLocalRandom;

public class TestBot extends Player {

    {pieceUpgradeType = PieceUpgradeType.ROOK}

    @Override
    public Move getMove(ReadOnlyBoard board, Player enemy, Set<Move> moves) {
        return moves[ThreadLocalRandom.current().nextInt(moves.size())];
    }

}
J Atkin
sumber
3

MeasureBot

Ini adalah bot yang saya mulai; Saya sedang berusaha mengembangkannya, tetapi kemudian saya menemukan bug klon-dalam, dan kemudian saya berpikir "baik, mari kita serahkan bot ini saja, ini memang berkinerja lebih baik daripada RandomBot dan OnePlayBot, dan saya selalu dapat mengirimkan bot baru nanti" , jadi begini:

package com.ppcgse.koth.antichess.player

import com.ppcgse.koth.antichess.controller.Board
import com.ppcgse.koth.antichess.controller.Move
import com.ppcgse.koth.antichess.controller.Piece
import com.ppcgse.koth.antichess.controller.PieceType
import com.ppcgse.koth.antichess.controller.PieceUpgradeType
import com.ppcgse.koth.antichess.controller.Player

import java.util.concurrent.ThreadLocalRandom

/**
 * Created by ProgramFOX on 12/21/15.
 */

 class MeasureBot extends Player {
    {pieceUpgradeType = PieceUpgradeType.KING}

    @Override
    Move getMove(Board board, Player opponent, Set<Move> validMoves) {
        def opponentPieces = opponent.getPieces(board)
        def mustCapture = opponentPieces.find { it.loc == validMoves[0].destination } != null
        def chosen = null
        if (mustCapture) {
            def piecesThatCanBeTaken = opponentPieces.findAll { validMoves.collect { it.getDestination() }.contains(it.loc) }
            def lowestAmount = getPieceValue(piecesThatCanBeTaken.sort { getPieceValue(it.getType()) }[0].getType())
            def piecesWithLowestValue = piecesThatCanBeTaken.findAll { getPieceValue(it.getType()) == lowestAmount }
            def chosenOnes = validMoves.findAll { m -> piecesWithLowestValue.find { it.loc ==  m.destination } != null }
            chosen = chosenOnes.sort { getPieceValue(it.piece.getType()) }.reverse()[0]
        } else {
            chosen = randomMove(validMoves);
        }
        return chosen
    }

    Double getPieceValue(PieceType pieceType) {
        switch (pieceType) {
            case PieceType.KING:
                return 1
            case PieceType.PAWN:
                return 1.5
            case PieceType.KNIGHT:
                return 2.5
            case PieceType.BISHOP:
                return 3
            case PieceType.ROOK:
                return 5
            case PieceType.QUEEN:
                return 9
            default:
                return 0
        }
    }

    def randomMove(Set<Move> validMoves) {
        return validMoves[ThreadLocalRandom.current().nextInt(validMoves.size())];
    }
 }

MeasureBot terlihat jika perlu menangkap sesuatu: jika tidak, itu hanya membuat langkah acak. Jika ya, ia akan memutuskan bagian mana yang akan diambil: ia akan memilih satu dengan nilai bagian yang lebih rendah karena mereka dapat menangkap lebih sedikit bagiannya sendiri. Dan jika ada beberapa cara untuk mengambil sepotong dengan nilai serendah mungkin, itu akan menangkapnya dengan potongan dengan nilai setinggi mungkin: jika ini dilakukan, itu akan membawa potongan menangkap lebih dekat ke potongan lain (di awal game, setidaknya) dan Anda lebih suka kehilangan bagian yang bernilai lebih tinggi daripada yang lebih rendah.

Ini adalah daftar nilai bagian yang saya gunakan:

  • Raja: 1
  • Gadai: 1,5
  • Ksatria: 2.5
  • Uskup: 3
  • Benteng: 5
  • Ratu: 9

Ketika pion dipromosikan, pion akan selalu dipromosikan menjadi raja, karena itu adalah pion yang bernilai paling rendah.

ProgramFOX
sumber