Antichessをプレイ!


19

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

これは基本的にチェストーナメントですが、アンチチェス用です;)

アンチチェスは、発明された多くのチェスのバリエーションの 1つです。目標は、すべてのピースを失うことです(これは少し奇妙に思えるかもしれませんが、理由によりアンチチェスと呼ばれます)。

ルール

アンチチェスのルールは標準チェスと非常に似ていますが、いくつかのかなり小さな違いがあります。上記の目標は、すべてのピースを失うことです。これを実現するために、対戦相手が自分のピースの1つをキャプチャする機会がある場合、それは彼が行うことができる唯一の動きです。1ターンで彼に複数のチャンスを与えた場合、他のプレイヤーは自分のターンを選択できます。変更されたもう1つのことは、王には特別な力がないことです-あなたのように相手をチェックメイトすることができず、あなたに彼を強制的にチェックすることはできません。

標準ゲームに対する次の変更も適用されます(ゲームの簡素化に役立ちます)。

  • 入場者は無視されます。
  • キャスリングはできません。
  • 50手ルールは、(引き分けで試合終了を意味する)に自動的に適用されます。
  • ポーンは、昇格先を選択できます。
  • プレイヤーが移動するのに2秒以上かかる場合、ゲームに負けます。
  • 無効な動きを返すと、ゲームに負けます。
  • 勝つためには、対戦相手がすべてのピースをキャプチャする必要があります
  • ホワイトがゲームを開始します。
  • 白はフィールドの「下部」に配置され(y = 0)、黒は上部に配置されます(y = 7)。
  • ボット以外のリソース(インターネット、ファイル、他のボットなど)へのアクセスは禁止されています。

得点

  • 勝つと3ポイント、引き分け1ポイント、0ポイントを失います。
  • 各サブミッションは、他のサブミッションと10回対戦します(白で5回、黒で5回)。

ボットを書く

コントローラーコードはこちら:https : //github.com/JJ-Atkinson/SimpleAntichessKOTH

ボットはJavaまたはGroovyで作成できます。ボットを作成するには、Playerクラスを拡張する必要があります。プレーヤークラスには1つの抽象メソッドがありMove getMove(Board board, Player enemy, Set<Move> validMoves)ます。

便利なメソッドの簡単な概要を次に示します。

Player

  • List<Piece> getPieces(Board board):ボード上のすべてのピースを返却します。
  • PieceUpgradeType pieceUpgradeType:ポーンの1つがボードの最後に達した場合、これをアップグレードするピースのタイプに定義する必要があります。あなたは、の選択肢を持っている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を

私は以前にコトリンを見たことがありますが、徹底的に見たことはありません。それはちょっとscala / groovyマッシュアップのように見えます(しかし、大丈夫です-groovyとscalaは私のお気に入りの言語です;)
J Atkin

私は以前にscalaを使用したことはありません...しかし、Javaからgoovyコードを呼び出すよりも、JavaからKotlinコードを呼び出す方がはるかに簡単です。
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

たった1回のプレイで死んだシンプルなボット。ルークにアップグレードします。

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

これは私が始めたボットです。私はそれを拡張する作業をしていましたが、ディープクローンのバグに遭遇し、「このボットをすでに送信してみましょう。RandomBotやOnePlayBotよりもパフォーマンスが良く、いつでも新しいボットを送信できます」 、ここにあります:

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.