玩Antichess!


19

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

这基本上是国际象棋比赛,但是对于古董;)

Antichess是已发明的许多国际象棋变体之一。目的是要丢掉所有的棋子(这看起来有些奇怪,但是有一定的原因被称为安息)。

规则

固定规则与标准国际象棋非常相似-但有一些相当小的差异。正如我上面提到的,目标是失去所有的碎片。要做到这一点,如果对手有机会抓住你的一个棋子,那是他唯一能采取的行动。如果您在一回合中给予他多次机会,另一位玩家可以选择他的回合。另一件事发生了变化,那就是国王没有特殊的权力-因为您无法与对手对峙,也不能迫使他受阻。

对标准游戏的以下更改也将适用(它们有助于简化游戏):

  • 传递将被忽略。
  • 卡丁车是不可能的。
  • 五十移动规则适用自动(意为平局的游戏结束)。
  • 典当将能够选择它们要推广到的东西。
  • 如果玩家需要超过2秒的移动时间,他将输掉比赛。
  • 返回无效的举动将导致游戏失败。
  • 为了赢得胜利,您的对手必须夺走您所有的碎片
  • 怀特开始比赛。
  • 白色位于字段的“底部”(y = 0),黑色位于顶部(y = 7)。
  • 禁止访问除您的漫游器以外的其他资源(互联网,文件,其他漫游器,...)。

计分

  • 获胜将使您获得3分,平局1分,而输0分。
  • 每个提交将与对方提交进行10次对战(白色5次,黑色5次)。

编写你的机器人

控制器代码在这里:https : //github.com/JJ-Atkinson/SimpleAntichessKOTH

您可以用Java或Groovy编写您的机器人。要编写机器人,您必须扩展Player该类。播放器类具有一种抽象方法Move getMove(Board board, Player enemy, Set<Move> validMoves)

以下是一些有用方法的简要介绍:

Player

  • List<Piece> getPieces(Board board):退还您板上所有的零件。
  • PieceUpgradeType pieceUpgradeType:如果/当其中一个棋子到达棋盘的末端时,您需要将其定义为要升级到的棋子类型。你的选择ROOKKNIGHTQUEENBISHOP,和KING

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标志设置为true。您将获得类似以下的输出:

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,则可以使用控制器随附的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,但从没有仔细看过它。看起来有点像scala / groovy混搭(但是没关系-groovy和scala是我最喜欢的语言;)
J Atkin

我以前从未使用过scala ...但是从Java调用Kotlin代码比从Java调用goovy代码要容易得多。
TheNumberOne 2015年

1
您可以升级为国王吗?当然不是...
wizzwizz4 2015年

1
@ wizzwizz4你可以的。
ProgramFOX

Answers:


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

牺牲瓶

该机器人将检查其他玩家的所有举动,并检查其中是否有任何交叉(即该棋子将被杀死)。(这比我预期的要好得多;)

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

措施

这是我开始使用的机器人;我当时正在进行扩展,但是后来遇到了一个深克隆错误,然后我想:“好吧,让我们已经提交了这个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

当典当升职时,它总是会升为国王,因为它是价值最低的一块。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.