Gioca a Antichess!


19

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

Questo è fondamentalmente Torneo di scacchi , ma per l'anticipazione;)

Antichess è una delle tante varianti di scacchi che sono state inventate. L'obiettivo è perdere tutti i tuoi pezzi (questo può sembrare un po 'strano, ma per un motivo si chiama antichess).

Le regole

Le regole dell'anticipo sono molto simili agli scacchi standard, ma con alcune differenze abbastanza lievi. L'obiettivo, come ho detto sopra, è quello di perdere tutti i tuoi pezzi. Perché ciò accada, se il tuo avversario ha l'opportunità di catturare uno dei tuoi pezzi, questa è l'unica mossa che può fare. Se gli dai più possibilità in un turno, l'altro giocatore può scegliere il suo turno. Un'altra cosa che è cambiata è che il re non ha poteri speciali, poiché in te non puoi dare scacco matto al tuo avversario e non puoi costringerlo a controllare.

Si applicano anche le seguenti modifiche al gioco standard (aiutano a semplificare il gioco):

  • En passant sarà ignorato.
  • Castling non è possibile.
  • La regola delle cinquanta mosse si applica automaticamente (il che significa che il gioco termina in parità).
  • I pedoni saranno in grado di scegliere ciò che promuovono.
  • Se un giocatore ha bisogno di più di 2 secondi per muoversi, perderà la partita.
  • Restituire una mossa non valida comporterà la perdita del gioco.
  • Per vincere, i tuoi avversari devono catturare tutti i tuoi pezzi .
  • Il bianco inizia il gioco.
  • Il bianco viene posizionato "nella parte inferiore" del campo (y = 0), il nero si trova nella parte superiore (y = 7).
  • L'accesso ad altre risorse oltre al tuo bot (internet, file, altri bot, ...) è proibito.

punteggio

  • Vincere ti dà 3 punti, un pareggio 1 punto e la perdita di 0 punti.
  • Ogni invio giocherà contro l'altro 10 volte (5 volte bianco, 5 nero).

Scrivi il tuo bot

Il codice del controller è qui: https://github.com/JJ-Atkinson/SimpleAntichessKOTH

Puoi scrivere il tuo bot in Java o Groovy. Per scrivere un bot è necessario estendere la Playerclasse. La classe del giocatore ha un metodo astrattoMove getMove(Board board, Player enemy, Set<Move> validMoves) .

Ecco una rapida carrellata di metodi utili:

Player:

  • List<Piece> getPieces(Board board): Restituisci tutti i tuoi pezzi che si trovano sul tabellone
  • PieceUpgradeType pieceUpgradeType: Se / quando una delle tue pedine raggiunge la fine della scacchiera, dovrai definirla in base al tipo di pezzo che vuoi potenziare. Avete la scelta di ROOK, KNIGHT, QUEEN, BISHOP, e KING.

Board:

  • Field getFieldAtLoc(Location loc): Restituisce il Fieldnella posizione. Questo ha un getAtmetodo di abbinamento in modo che se stai usando Groovy puoi scrivere board[loc].
  • Field getFieldAtLoc(int x, int y): Restituisce il Fieldnella posizione. Questo ha un getAtmetodo di abbinamento in modo che se stai usando Groovy puoi scrivere board[x, y].
  • Board movePiece(Player player, Move move): Fai una mossa sul tabellone in modo da poter vedere come andrebbe a finire. Restituisce la nuova scheda.

Se vuoi vedere i pezzi dei tuoi avversari, scrivi enemy.getPieces(board). Per aggiungere il tuo bot all'allineamento aggiungi la seguente riga a PlayerFactory:

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

Debug del tuo bot:

Ho incluso un paio di strumenti per aiutare il debug dei tuoi robot. Per vedere il tuo gioco giocato dal vivo puoi impostare la Game#DEBUGbandiera su true. Otterrai un risultato simile al seguente:

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

...

(Il bianco è maiuscolo, il re è mostrato con i)

Se la tua console supporta caratteri speciali utf-8, puoi persino mostrare la scacchiera con i caratteri scacchi usando Board#USE_UTF8_TO_STRING:

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

(sembra migliore con un carattere mono spaziato)

Per evitare un flusso di output indesiderato, è necessario modificare la Main#mainfunzione in qualcosa del genere:

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

Metti il ​​tuo robot a sinistra per giocare come bianco, mettilo a destra per giocare come nero.

Costruire il controller:

Il controller è scritto in groovy, quindi devi avere java e groovy installati. Se non si desidera installare groovy, è possibile utilizzare il file di build gradle fornito con il controller (questo non è stato testato). Se non si desidera utilizzare Groovy o Gradle, è possibile utilizzare l'ultimo vaso di rilascio ( https://github.com/JJ-Atkinson/SimpleAntichessKOTH/releases ). Se lo fai, devi creare il tuo mainmetodo e aggiungere il tuo bot manualmente alla fabbrica del giocatore. Esempio:

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

(Nota che puoi ancora impostare i flag e le cose di debug)

Qualsiasi ricerca di bug è apprezzata!

I punteggi:

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

Si prega di notare che sono sempre disposto ad avere nuovi invii!


Se ti piace Groovy e IntelliJ ... dovresti dare un'occhiata a Kotlin
TheNumberOne,

Ho già visto Kotlin, ma non l'ho mai esaminato a fondo. Sembra un mashup scala / groovy (ma va bene così - groovy e scala sono le mie lingue preferite;)
J Atkin,

Non ho mai usato Scala prima ... ma è molto più facile chiamare il codice Kotlin da Java che chiamare il codice Goovy da Java.
TheNumberOne il

1
Puoi passare a un re?!? Sicuramente no ...
wizzwizz4,

1
@ wizzwizz4 In antichess, puoi.
Programma FOX il

Risposte:


6

searchbot

Il bot più lento finora, ma comunque più veloce di 2 secondi per mossa e batte tutti i robot attualmente pubblicati. Guarda cosa succede dopo una delle mosse valide e cosa potrebbe succedere dopo una mossa dopo quelle mosse e decide quale sarebbe il risultato migliore. Sfortunatamente non è possibile effettuare ricerche più approfondite perché impiegherebbe più di 2 secondi.

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

Questo bot controllerà tutte le mosse dell'altro giocatore e controllerà se qualcuno di loro si interseca (cioè il pezzo verrà ucciso). (Questo è molto meglio di quanto mi aspettassi;)

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

Robot semplice morto con un solo gioco. Passerà a una torre.

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

Questo è il bot casuale obbligatorio. Si aggiornerà sempre a una torre.

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

Questo è il bot con cui ho iniziato; Stavo lavorando per espanderlo, ma poi mi sono imbattuto nel bug del clone profondo, e poi ho pensato "bene, inviamo già questo bot, funziona meglio di RandomBot e OnePlayBot, e posso sempre inviare un nuovo bot in seguito" , quindi eccolo qui:

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 cerca se deve catturare qualcosa: in caso contrario, esegue semplicemente una mossa casuale. In tal caso, deciderà quale pezzo prendere: ne sceglierà uno con un valore pezzo più basso perché quelli possono catturare meno dei propri pezzi. E se ci sono diversi modi per prendere un pezzo con il valore più basso possibile, lo catturerà con il pezzo con il valore più alto possibile: in questo caso, avvicinerà il pezzo catturato ad altri pezzi (all'inizio del gioco, almeno) e preferiresti perdere un pezzo di valore più alto di uno di valore inferiore.

Questa è una lista dei valori dei pezzi che ho usato:

  • Re: 1
  • Pegno: 1.5
  • Cavaliere: 2.5
  • Vescovo: 3
  • Torre: 5
  • Regina: 9

Quando un pedone promuove, promuoverà sempre un re, perché è il pezzo con il valore più basso.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.