Chơi Antichess!


19

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

Về cơ bản, đây là Giải đấu cờ vua , nhưng dành cho antichess;)

Antichess là một trong nhiều biến thể cờ vua đã được phát minh. Mục tiêu là để mất tất cả các mảnh của bạn (điều này có vẻ hơi kỳ lạ, nhưng nó được gọi là antichess vì một lý do).

Những quy định

Các quy tắc của antichess rất giống với cờ tiêu chuẩn - nhưng với một vài khác biệt khá nhỏ. Mục tiêu như tôi đã đề cập ở trên là để mất tất cả các phần của bạn. Để thực hiện điều này, nếu đối thủ của bạn có cơ hội nắm bắt một trong những quân cờ của bạn, đó là động tác duy nhất anh ta có thể thực hiện. Nếu bạn cho anh ta nhiều cơ hội trong một lượt, người chơi khác có thể chọn lượt của mình. Một điều khác được thay đổi là nhà vua không có quyền hạn đặc biệt - vì trong bạn không thể kiểm tra đối thủ của mình và bạn không thể buộc anh ta kiểm tra.

Những thay đổi sau đây đối với trò chơi tiêu chuẩn cũng sẽ được áp dụng (chúng giúp đơn giản hóa trò chơi):

  • En passant sẽ bị bỏ qua.
  • Castling là không thể.
  • Các quy tắc Năm mươi di chuyển gây hiệu ứng tự động (có nghĩa là các trò chơi kết thúc trong một trận hòa).
  • Chân sẽ có thể chọn những gì họ quảng bá đến.
  • Nếu người chơi cần dài hơn 2 giây để di chuyển, anh ta sẽ thua trò chơi.
  • Trả lại một nước đi không hợp lệ sẽ dẫn đến thua trò chơi.
  • Để giành chiến thắng, đối thủ của bạn phải nắm bắt tất cả các mảnh của bạn .
  • White bắt đầu trò chơi.
  • Màu trắng được đặt "ở dưới cùng" của trường (y = 0), màu đen nằm ở trên cùng (y = 7).
  • Truy cập các tài nguyên khác ngoài bot của bạn (internet, tệp, bot khác, ...) đều bị cấm.

Chấm điểm

  • Chiến thắng giúp bạn có 3 điểm, hòa 1 điểm và mất 0 điểm.
  • Mỗi lần gửi sẽ thi đấu với nhau 10 lần (5 lần trắng, 5 lần đen).

Viết bot của bạn

Mã điều khiển có tại đây: https://github.com/JJ-Atkinson/SimpleAntichessKOTH

Bạn có thể viết bot của mình bằng Java hoặc Groovy. Để viết bot bạn phải mở rộng Playerlớp. Lớp người chơi có một phương thức trừu tượng Move getMove(Board board, Player enemy, Set<Move> validMoves).

Dưới đây là tóm tắt nhanh về các phương pháp hữu ích:

Player:

  • List<Piece> getPieces(Board board): Trả lại tất cả các phần của bạn trên bảng.
  • PieceUpgradeType pieceUpgradeType: Nếu / khi một trong những con tốt của bạn đến cuối bảng, bạn sẽ cần xác định loại này thành loại mảnh bạn muốn nâng cấp lên. Bạn có thể lựa chọn ROOK, KNIGHT, QUEEN, BISHOP, và KING.

Board:

  • Field getFieldAtLoc(Location loc): Trả lại Fieldtại vị trí. Điều này có một getAtphương thức phù hợp để nếu bạn đang sử dụng Groovy, bạn có thể viết board[loc].
  • Field getFieldAtLoc(int x, int y): Trả lại Fieldtại vị trí. Điều này có một getAtphương thức phù hợp để nếu bạn đang sử dụng Groovy, bạn có thể viết board[x, y].
  • Board movePiece(Player player, Move move): Thực hiện di chuyển trên bảng để bạn có thể thấy nó sẽ diễn ra như thế nào. Nó trả về bảng mới.

Nếu bạn muốn xem các mảnh đối thủ của bạn, chỉ cần viết enemy.getPieces(board). Để thêm bot của bạn vào đội hình, hãy thêm dòng sau vào PlayerFactory:

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

Gỡ lỗi bot của bạn:

Tôi đã bao gồm một vài công cụ để hỗ trợ gỡ lỗi bot của bạn. Để xem trò chơi của bạn diễn ra trực tiếp, bạn có thể đặt Game#DEBUGcờ thành đúng. Bạn sẽ nhận được một đầu ra như thế này:

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

...

(Màu trắng là chữ hoa, nhà vua được hiển thị với i)

Nếu bảng điều khiển của bạn hỗ trợ các ký tự đặc biệt utf-8, bạn thậm chí có thể hiển thị bảng với các ký tự cờ bằng cách sử dụng Board#USE_UTF8_TO_STRING:

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

(có vẻ tốt hơn với một phông chữ đơn cách)

Để ngăn chặn lũ đầu ra không mong muốn, bạn nên thay đổi Main#mainchức năng thành một cái gì đó như thế này:

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

Đặt bot của bạn ở bên trái để chơi như màu trắng, đặt nó bên phải để chơi như màu đen.

Xây dựng bộ điều khiển:

Bộ điều khiển được viết bằng Groovy, vì vậy bạn phải cài đặt java và Groovy. Nếu bạn không muốn cài đặt Groovy, bạn có thể sử dụng tệp xây dựng lớp đi kèm với bộ điều khiển (điều này chưa được thử nghiệm). Nếu bạn không muốn sử dụng Groovy hoặc gradle, bạn có thể sử dụng jar phát hành mới nhất ( https://github.com/JJ-Atkinson/SimpleAntichessKOTH/release ). Nếu bạn làm điều này, bạn cần tạo mainphương thức của riêng bạn và thêm bot của bạn bằng tay vào nhà máy của người chơi. Thí dụ:

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

(Lưu ý rằng bạn vẫn có thể đặt cờ gỡ lỗi và nội dung)

Bất kỳ và tất cả các phát hiện lỗi được đánh giá cao!

Điểm số:

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

Xin lưu ý rằng tôi luôn sẵn sàng để có bài nộp mới!


Nếu bạn thích Groovy và IntelliJ ... bạn nên xem qua Kotlin
TheNumberOne

Tôi đã nhìn thấy Kotlin trước đây, nhưng chưa bao giờ nhìn nó kỹ lưỡng. Nó trông giống như một bản mashup scala / Groovy (nhưng không sao - Groovy và scala là ngôn ngữ yêu thích của tôi;)
J Atkin

Tôi chưa bao giờ sử dụng scala trước đây ... nhưng việc gọi mã Kotlin từ java sẽ dễ dàng hơn nhiều so với việc gọi mã goovy từ java.
TheNumberOne

1
Bạn có thể nâng cấp lên một vị vua?!? Chắc chắn là không ...
wizzwizz4

1
@ wizzwizz4 Trong antichess, bạn có thể.
Chương trìnhFOX

Câu trả lời:


6

Tìm kiếm

Bot chậm nhất cho đến nay, nhưng vẫn nhanh hơn 2 giây mỗi lần di chuyển và nó đánh bại tất cả các bot hiện được đăng. Nó xem xét những gì xảy ra sau bất kỳ động thái hợp lệ nào và những gì có thể xảy ra sau bất kỳ động thái nào sau những động thái đó và quyết định điều gì sẽ là kết quả tốt nhất. Thật không may, nó không thể tìm kiếm sâu hơn bởi vì nó sẽ mất hơn 2 giây sau đó.

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

4

Hy sinhBot

Bot này sẽ kiểm tra tất cả các động tác mà người chơi khác có và sẽ kiểm tra xem liệu có bất kỳ bước nào trong số chúng giao nhau không (tức là quân cờ sẽ bị giết). (Điều này thực sự tốt hơn rất nhiều so với tôi dự kiến;)

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

3

OnePlayBot

Chết bot đơn giản chỉ với một lần chơi. Nó sẽ nâng cấp lên một tân binh.

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

}

3

RandomBot

Đây là bot ngẫu nhiên bắt buộc. Nó sẽ luôn nâng cấp lên một tân binh.

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())];
    }

}

3

Số đo

Đây là bot mà tôi bắt đầu với; Tôi đang làm việc để mở rộng nó nhưng sau đó tôi đã phát hiện ra lỗi sâu nhân bản và sau đó tôi nghĩ "tốt, hãy gửi bot này đi, nó hoạt động tốt hơn RandomBot và OnePlayBot và tôi luôn có thể gửi bot mới sau" Vì vậy, đây là:

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())];
    }
 }

Đo lườngBot xem nếu nó cần chụp một cái gì đó: nếu không, nó chỉ thực hiện một bước di chuyển ngẫu nhiên. Nếu có, nó sẽ quyết định nên lấy mảnh nào: nó sẽ chọn một mảnh có giá trị mảnh thấp hơn bởi vì chúng có thể thu được ít mảnh hơn của chính nó. Và nếu có nhiều cách để lấy một mảnh có giá trị thấp nhất có thể, nó sẽ chụp nó với mảnh có giá trị cao nhất có thể: nếu thực hiện được điều này, nó sẽ đưa mảnh bắt gần hơn với các mảnh khác (vào đầu ít nhất là trò chơi) và bạn muốn mất một phần có giá trị cao hơn một phần có giá trị thấp hơn.

Đây là danh sách các giá trị mảnh tôi đã sử dụng:

  • Vua: 1
  • Cầm đồ: 1,5
  • Hiệp sĩ: 2,5
  • Giám mục: 3
  • Rook: 5
  • Nữ hoàng: 9

Khi một con tốt phát huy, nó sẽ luôn thăng cấp cho một vị vua, bởi vì đó là mảnh có giá trị thấp nhất.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.