Грайте в антихесу!


19

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

Це в основному шаховий турнір , але для антихеси;)

Антихема - один із багатьох варіантів шахів , які були винайдені. Мета полягає в тому, щоб втратити всі свої шматки (це може здатися дивним, але це називається антихесою чомусь).

Правила

Правила антихеси дуже схожі на стандартні шахи, але з кількома досить незначними відмінностями. Як я вже згадував, ціль, як я вже згадував, - втратити всі свої шматки. Щоб це сталося, якщо ваш опонент має можливість захопити один із ваших творів, це єдиний хід, який він може зробити. Якщо ви даєте йому кілька шансів за один хід, інший гравець може вибрати свою чергу. Ще одна річ, яка зміниться - це те, що король не має спеціальних повноважень - так як ви не можете перевірити мат свого опонента, і ви не можете змусити його перевірити.

Наступні зміни до стандартної гри також застосовуватимуться (вони допомагають спростити гру):

  • Експансія буде проігнорована.
  • Кастинг неможливий.
  • Правило " П'ятдесят рухів" застосовується автоматично (тобто гра закінчується внічию).
  • Пішаки зможуть вибрати те, на що вони рекламують.
  • Якщо гравцеві потрібно більше 2 секунд для переміщення, він програє гру.
  • Повернення недійсного ходу призведе до втрати гри.
  • Щоб виграти, опоненти повинні захопити всі ваші шматки .
  • Білий починає гру.
  • Білий розміщується «внизу» поля (у = 0), чорний - у верхній частині (у = 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: Якщо / коли одна з ваших пішаків досягне кінця дошки, вам потрібно буде визначити це до типу твору, до якого ви хочете оновити. У вас є вибір ROOK, KNIGHT, QUEEN, BISHOP, і 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прапор істинно. Ви отримаєте такий результат:

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, ви можете скористатись останньою версією випуску ( 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

Я раніше бачив Котліна, але ніколи не переглядав його ретельно. Це ніби виглядає як скала / грубий мішур (але це добре - groovy і scala - мої улюблені мови;)
J Atkin

Я ніколи раніше не використовував scala ... але набагато простіше називати код Котліна з 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

Жертвоприношення

Цей бот перевірить усі ходи, які має інший гравець, і перевірить, чи перетинається який-небудь з них (тобто штука буде вбита). (Це біса набагато краще, ніж я коли-небудь очікував;)

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

Це бот, з якого я почав; Я працював над її розширенням, але потім я натрапив на помилку глибокого клону, і тоді я подумав, "що ж, давайте вже подати цей бот, він працює краще, ніж 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

Коли пішак просувається, він завжди сприятиме королю, тому що це найнижча частина.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.