เล่น Antichess!


19

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

นี่เป็นพื้นฐานการแข่งขันหมากรุกแต่สำหรับความเป็นปฏิปักษ์;)

Antichess เป็นหนึ่งในเกมหมากรุกที่ได้ถูกประดิษฐ์ขึ้น เป้าหมายคือการสูญเสียชิ้นส่วนทั้งหมดของคุณ (ซึ่งอาจดูแปลก ๆ เล็กน้อย แต่มันเรียกว่า antichess ด้วยเหตุผล)

กฎระเบียบ

กฎของ antichess นั้นคล้ายกับหมากรุกมาตรฐาน - แต่มีความแตกต่างเล็กน้อยเล็กน้อย เป้าหมายที่ฉันกล่าวถึงข้างต้นคือการสูญเสียชิ้นส่วนทั้งหมดของคุณ ในการทำให้สิ่งนี้เกิดขึ้นถ้าคู่ต่อสู้ของคุณมีโอกาสที่จะจับชิ้นส่วนหนึ่งของคุณนั่นเป็นเพียงการเคลื่อนไหวเดียวที่เขาสามารถทำได้ หากคุณให้โอกาสเขาหลายครั้งในคราวเดียวผู้เล่นคนอื่นอาจเลือกตาเขาได้ อีกสิ่งหนึ่งที่เปลี่ยนไปคือราชาไม่มีพลังพิเศษ - ในขณะที่คุณไม่สามารถรุกฆาตคู่ต่อสู้ของคุณและคุณไม่สามารถบังคับให้เขาตรวจสอบ

การเปลี่ยนแปลงต่อไปนี้สำหรับเกมมาตรฐานจะมีผลบังคับใช้ด้วย (จะช่วยทำให้เกมง่ายขึ้น):

  • En passantจะถูกละเว้น
  • การขว้างเป็นไปไม่ได้
  • กฎห้าสิบย้ายนำไปใช้โดยอัตโนมัติ (หมายถึงเกมสิ้นสุดในการวาด)
  • ผู้จำนำจะสามารถเลือกสิ่งที่พวกเขาส่งเสริม
  • หากผู้เล่นต้องการความเคลื่อนไหวนานกว่า 2 วินาทีเขาจะแพ้ในเกม
  • การคืนการย้ายที่ไม่ถูกต้องจะส่งผลให้สูญเสียเกม
  • ที่จะชนะฝ่ายตรงข้ามของคุณจะต้องจับชิ้นส่วนทั้งหมดของคุณ
  • ขาวเริ่มเกม
  • สีขาวถูกวางไว้ "ที่ด้านล่าง" ของฟิลด์ (y = 0) สีดำจะอยู่ที่ด้านบน (y = 7)
  • การเข้าถึงทรัพยากรอื่น ๆ นอกเหนือจากบอทของคุณ (อินเทอร์เน็ต, ไฟล์, บอทอื่น ๆ , ... ) เป็นสิ่งต้องห้าม

เกณฑ์การให้คะแนน

  • ผู้ชนะจะได้รับ 3 คะแนนเสมอ 1 คะแนนและเสีย 0 คะแนน
  • การส่งแต่ละครั้งจะเล่นกับการส่งอื่น ๆ 10 ครั้ง (สีขาว 5 ครั้ง, 5 สีดำ)

กำลังเขียนบอทของคุณ

รหัสคอนโทรลเลอร์อยู่ที่นี่: https://github.com/JJ-Atkinson/SimpleAntichessKOTH

คุณสามารถเขียน bot ของคุณใน Java หรือ Groovy ในการเขียนบอทคุณต้องขยายPlayerชั้นเรียน Move getMove(Board board, Player enemy, Set<Move> validMoves)ชั้นผู้เล่นที่มีวิธีนามธรรมหนึ่ง

นี่คือบทสรุปอย่างรวดเร็วเกี่ยวกับวิธีการที่มีประโยชน์:

Player:

  • List<Piece> getPieces(Board board): คืนชิ้นส่วนทั้งหมดของคุณที่อยู่บนกระดาน
  • PieceUpgradeType pieceUpgradeType: หาก / เมื่อเบี้ยหนึ่งใบของคุณถึงจุดสิ้นสุดของกระดานคุณจะต้องกำหนดสิ่งนี้เป็นประเภทของชิ้นส่วนที่คุณต้องการอัปเกรด คุณมีทางเลือกของROOK, KNIGHT, QUEEN, และBISHOPKING

Board:

  • Field getFieldAtLoc(Location loc): ส่งคืนFieldที่ตำแหน่ง นี่เป็นgetAtวิธีการจับคู่ดังนั้นถ้าคุณใช้ groovy คุณสามารถเขียนboard[loc]ได้
  • Field getFieldAtLoc(int x, int y): ส่งคืนFieldที่ตำแหน่ง นี่เป็นgetAtวิธีการจับคู่ดังนั้นถ้าคุณใช้ groovy คุณสามารถเขียนboard[x, y]ได้
  • Board movePiece(Player player, Move move): ทำการย้ายบนกระดานเพื่อให้คุณสามารถดูว่ามันจะเล่นออกมา ส่งคืนบอร์ดใหม่

enemy.getPieces(board)หากคุณต้องการที่จะเห็นฝ่ายตรงข้ามของชิ้นเพียงเขียน ในการเพิ่มบ็อตของคุณไปยังรายการเพิ่มบรรทัดต่อไปนี้ไปที่PlayerFactory:

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

การดีบัก ธ ปทของคุณ:

ฉันได้รวมเครื่องมือสองอย่างเพื่อช่วยในการดีบักบ็อตของคุณ หากต้องการดูเกมของคุณเล่นสดคุณสามารถตั้งค่าGame#DEBUGสถานะเป็นจริง คุณจะได้ผลลัพธ์ดังนี้:

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

...

(สีขาวเป็นตัวพิมพ์ใหญ่กษัตริย์จะแสดงพร้อมi)

หากคอนโซลของคุณรองรับตัวอักษรพิเศษ utf-8 คุณสามารถแสดงบอร์ดด้วยตัวหมากรุกได้โดยใช้Board#USE_UTF8_TO_STRING:

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

(ดูดีขึ้นเมื่อใช้แบบอักษรเว้นวรรคแบบเดี่ยว)

เพื่อป้องกันไม่ให้น้ำท่วมของผลลัพธ์ที่ไม่พึงประสงค์คุณควรเปลี่ยนMain#mainฟังก์ชั่นเป็นดังนี้:

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

วางบอทของคุณทางซ้ายเพื่อเล่นเป็นสีขาววางไว้ทางด้านขวาเพื่อเล่นเป็นสีดำ

การสร้างตัวควบคุม:

คอนโทรลเลอร์ถูกเขียนเป็น groovy ดังนั้นคุณต้องติดตั้ง java และ groovy หากคุณไม่ต้องการติดตั้ง Groovy คุณสามารถใช้ไฟล์ build gradle ที่มาพร้อมกับคอนโทรลเลอร์ (ยังไม่ได้ทำการทดสอบ) หากคุณไม่ต้องการใช้ Groovy หรือ gradle คุณสามารถใช้ jar รุ่นล่าสุด ( https://github.com/JJ-Atkinson/SimpleAntichessKOTH/releases ) หากคุณทำสิ่งนี้คุณจะต้องสร้างmainวิธีการของคุณเองและเพิ่มบอทของคุณเองไปยังโรงงานผู้เล่น ตัวอย่าง:

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

(โปรดทราบว่าคุณยังคงสามารถตั้งค่าสถานะการดีบักและเนื้อหา)

การค้นหาข้อผิดพลาดใด ๆ และทุกคนชื่นชม!

คะแนน:

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

โปรดทราบว่าฉันยินดีที่จะมีการส่งใหม่เสมอ!


ถ้าคุณชอบ groovy และ IntelliJ ... คุณควรดูKotlin
TheNumberOne

ฉันเคยเห็น Kotlin มาก่อน แต่ไม่เคยดูอย่างละเอียด มันดูเหมือนว่า mashup scala / groovy (แต่ก็โอเค - groovy และ scala เป็นภาษาที่ฉันชอบ;)
J Atkin

ฉันไม่เคยใช้สกาล่ามาก่อน ... แต่มันง่ายกว่าที่จะเรียกรหัส Kotlin จาก java มากกว่าการเรียกรหัส goovy จาก java
TheNumberOne

1
คุณสามารถอัพเกรดเป็นราชา?!? ไม่แน่นอน ...
wizzwizz4

1
@ wizzwizz4 คุณสามารถทำได้
ProgramFOX

คำตอบ:


6

SearchBot

บอทที่ช้าที่สุด แต่ก็ยังเร็วกว่า 2 วินาทีต่อการเคลื่อนไหวและมันจะเต้นบอทที่โพสต์ในปัจจุบันทั้งหมด มันจะดูว่าเกิดอะไรขึ้นหลังจากการเคลื่อนไหวที่ถูกต้องและสิ่งที่สามารถเกิดขึ้นได้หลังจากการเคลื่อนไหวใด ๆ หลังจากการเคลื่อนไหวเหล่านั้นและตัดสินใจว่าอะไรจะเป็นผลลัพธ์ที่ดีที่สุด น่าเสียดายที่ไม่สามารถค้นหาได้ลึกลงไปเพราะจะใช้เวลามากกว่า 2 วินาที

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

SacrificeBot

บอทนี้จะตรวจสอบการเคลื่อนไหวทั้งหมดที่ผู้เล่นคนอื่นมีและจะตรวจสอบเพื่อดูว่ามีการใด ๆ ของพวกเขาตัดกัน (เช่นชิ้นส่วนจะถูกฆ่าตาย) (นี่จะทำให้ดีกว่าที่ฉันคาดไว้มาก)

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

บอทที่ตายง่าย ๆ ด้วยการเล่นเพียงครั้งเดียว มันจะอัพเกรดเป็นโกง

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

นี่คือบ็อตสุ่มของแมนเดอร์แมน มันมักจะอัพเกรดเป็นโกง

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

MeasureBot

นี่คือบอทที่ฉันเริ่มด้วย ฉันพยายามขยายมัน แต่แล้วฉันก็พบข้อผิดพลาดโคลนลึกจากนั้นฉันก็คิดว่า "เอาล่ะลองส่ง bot นี้ไปก่อนแล้วมันจะทำงานได้ดีกว่า RandomBot และ OnePlayBot และฉันก็สามารถส่ง bot ใหม่ได้ในภายหลัง" ดังนั้นนี่คือ:

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 ดูว่ามันจำเป็นต้องมีการจับอะไรบางอย่างหรือไม่: ถ้าไม่มันก็แค่ทำการสุ่ม ถ้าเป็นเช่นนั้นมันจะตัดสินใจว่าจะหยิบชิ้นไหน: มันจะเลือกอันที่มีค่าชิ้นต่ำกว่า และหากมีหลายวิธีที่จะนำชิ้นส่วนที่มีค่าต่ำสุดที่เป็นไปได้มาใช้มันจะจับชิ้นงานที่มีค่าสูงสุดเท่าที่จะเป็นไปได้: ถ้าทำเช่นนี้มันจะนำชิ้นส่วนจับใกล้ชิ้นส่วนอื่น ๆ อย่างน้อยเกม) และคุณอยากเสียชิ้นที่มีมูลค่าสูงกว่าเกมที่มีมูลค่าต่ำกว่า

นี่คือรายการของค่าชิ้นที่ฉันใช้:

  • กษัตริย์: 1
  • จำนำ: 1.5
  • อัศวิน: 2.5
  • อธิการ: 3
  • โกง: 5
  • ราชินี: 9

เมื่อจำนำส่งเสริมมันจะส่งเสริมให้กษัตริย์เพราะมันเป็นชิ้นที่มีมูลค่าต่ำที่สุด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.