Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/bin/bash
- # Ensure string matches are case-sensitive
- LANG=C
- echo 'ShouldntExist v0.2'
- echo 'Copyright 2010 Justin Blanchard <UncombedCoconut at gmail>'
- echo 'License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.'
- echo 'You are free to change and redistribute it.'
- echo '(If you do something that foolish please drop me a line though!)'
- # Names for boolean arguments that indicate what they do
- INCLUDE_CAPTURES=1 INCLUDE_NON_CAPTURES=1 INCLUDE_EP_CAPTURES=1 TALK_TO_GUI=1
- EXCLUDE_CAPTURES=0 EXCLUDE_NON_CAPTURES=0 EXCLUDE_EP_CAPTURES=0 DONT_TALK_TO_GUI=0
- # TODO (chess rules):
- # knowing when castling is legal
- # chess960 castling
- # Threefold repetition (yeah, right...)
- # TODO (UCI features):
- # Support various arguments to go
- # Send back info about depth / nodes / nps for giggles
- # Implement UCI commands as bash commands, then enter a REPL.
- uci() {
- ROOT_DEPTH=2
- echo id name ShouldntExist 0.2
- echo id author Justin Blanchard
- echo option name Root Depth type spin default $ROOT_DEPTH min 2 max 3
- # You don't want to go over 3. Trust me.
- echo uciok
- }
- debug() {
- DEBUG="${1,,}"
- }
- isready() {
- echo readyok
- }
- setoption() {
- ARGS="${@,,}"
- local PAIR="${ARGS#name }"
- local NAME="${PAIR% value*}"
- if [[ "$PAIR" == "$NAME" ]]; then
- # handle button option
- case "$NAME" in
- esac
- else
- # handle option with value
- local VALUE="${PAIR##*value }"
- case "$NAME" in
- 'root depth') ROOT_DEPTH="$VALUE" ;;
- esac
- fi
- if [[ "$DEBUG" == 'on' ]]; then
- echo info string option 'Root Depth' set to $ROOT_DEPTH
- fi
- }
- register() {
- echo registration checking
- echo registration ok
- }
- ucinewgame() { :; }
- position()
- {
- if [[ "$1" == 'startpos' ]]; then
- ROOT_POSITION=$(startpos)
- shift 1
- elif [[ "$1" == 'fen' ]]; then
- ROOT_POSITION=$(fen $2 $3 $4 $5 $6 $7)
- shift 7
- fi
- if [[ "$1" == 'moves' ]]; then
- until (( $# <= 1 )); do
- shift
- ROOT_POSITION=$(echo $ROOT_POSITION | move $1)
- done
- fi
- if [[ "$DEBUG" == 'on' ]]; then
- echo info string position set to $ROOT_POSITION
- fi
- }
- go() {
- # TODO: actually look at the arguments
- if [[ "$1" == 'ponder' ]]; then
- return
- fi
- echo $ROOT_POSITION | score $ROOT_DEPTH $TALK_TO_GUI
- }
- stop() { :; }
- ponderhit() { :; }
- quit() {
- exit 0
- }
- # convert UCI phrases to internal board rep, which looks like:
- # rnbqkbnr//pppppppp//........//........//........//........//PPPPPPPP//RNBQKBNR w KQkq - 0 1
- fen() {
- local BOARD=$1
- local COLOR=$2
- local CASTLE_RIGHTS=$3
- local EP_TARGET=$4
- local HALF_MOVE=$5
- local FULL_MOVE=$6
- BOARD=${BOARD//1/.}
- BOARD=${BOARD//2/..}
- BOARD=${BOARD//3/...}
- BOARD=${BOARD//4/....}
- BOARD=${BOARD//5/.....}
- BOARD=${BOARD//6/......}
- BOARD=${BOARD//7/.......}
- BOARD=${BOARD//8/........}
- BOARD=${BOARD//\//\/\/}
- echo $BOARD $COLOR $CASTLE_RIGHTS $EP_TARGET $HALF_MOVE $FULL_MOVE
- }
- startpos() {
- fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
- }
- # Conversion between readable square and index into the string
- square_to_position() {
- local SQUARE="$1"
- local ROW=${SQUARE:1:1}
- local TRICK="a1b2c3d4e5f6g7h8"
- TRICK=${TRICK#*${SQUARE:0:1}}
- local COL=${TRICK:0:1}
- echo $(( 10*(8-ROW) + COL - 1 ))
- }
- square_from_position() {
- local POS="$1"
- local FILES='abcdefghxX'
- echo ${FILES:$((POS%10)):1}$((8-POS/10))
- }
- # Board manipulation
- board_get() {
- local BOARD="$1"
- local SQUARE="$2"
- echo ${BOARD:$(square_to_position $SQUARE):1}
- }
- board_set() {
- local BOARD="$1"
- local SQUARE="$2"
- local PIECE="$3"
- local POSITION=$(square_to_position $SQUARE)
- echo ${BOARD:0:$POSITION}${PIECE}${BOARD:$((POSITION+1))}
- }
- move() {
- local MOVE=$1
- local POSITION; read POSITION
- local BOARD=${POSITION%% *}
- POSITION=${POSITION#* }
- local COLOR=${POSITION%% *}
- POSITION=${POSITION#* }
- local CASTLE_RIGHTS=${POSITION%% *}
- POSITION=${POSITION#* }
- local EP_TARGET=${POSITION%% *}
- POSITION=${POSITION#* }
- local HALF_MOVE=${POSITION%% *}
- POSITION=${POSITION#* }
- local FULL_MOVE=${POSITION%% *}
- local MOVE_FROM=${MOVE:0:2}
- local MOVE_TO=${MOVE:2:2}
- local PROMOTION_PIECE=${MOVE:4}
- local INITIAL_PIECE=$(board_get $BOARD $MOVE_FROM)
- local CAPTURED_PIECE=$(board_get $BOARD $MOVE_TO) # For half-move update
- local FINAL_PIECE
- if [[ -z "$PROMOTION_PIECE" ]]; then
- FINAL_PIECE=$INITIAL_PIECE
- elif [[ $COLOR == 'w' ]]; then
- FINAL_PIECE=${PROMOTION_PIECE^^}
- else
- FINAL_PIECE=${PROMOTION_PIECE,,}
- fi
- # Update board
- BOARD=$(board_set $BOARD $MOVE_TO $FINAL_PIECE)
- BOARD=$(board_set $BOARD $MOVE_FROM .)
- # Special case: en passant capture
- if [[ $MOVE_TO == $EP_TARGET && ${INITIAL_PIECE,,} == 'p' ]]; then
- # Delete pawn on to-square's file, from-square's rank
- BOARD=$(board_set $BOARD ${MOVE_TO:0:1}${MOVE_FROM:1} .)
- fi
- # Special case: castling (will FAIL for chess960)
- if [[ ${INITIAL_PIECE,,} == 'k' ]]; then
- case $MOVE in
- 'e1c1')
- BOARD=$(board_set $BOARD d1 R)
- BOARD=$(board_set $BOARD a1 .)
- ;;
- 'e1g1')
- BOARD=$(board_set $BOARD f1 R)
- BOARD=$(board_set $BOARD h1 .)
- ;;
- 'e8c8')
- BOARD=$(board_set $BOARD d8 r)
- BOARD=$(board_set $BOARD a8 .)
- ;;
- 'e8g8')
- BOARD=$(board_set $BOARD f8 r)
- BOARD=$(board_set $BOARD h8 .)
- ;;
- esac
- fi
- # Update side to move
- case $COLOR in
- 'w') COLOR=b ;;
- 'b') COLOR=w ;;
- esac
- # Update castle rights (TODO)
- # Engine is too stupid to castle anyway
- # The only hard part here is handling FEN / Shredder-FEN / X-FEN
- # Update en passant target (naively a la FEN)
- if [[ ${INITIAL_PIECE,,} == 'p' ]]; then
- case ${MOVE_FROM:1}${MOVE_TO:1} in
- '24') EP_TARGET=${MOVE_FROM:0:1}3 ;;
- '75') EP_TARGET=${MOVE_FROM:0:1}6 ;;
- *) EP_TARGET='.'
- esac
- else
- EP_TARGET='.'
- fi
- # Update half-move clock
- if [[ $CAPTURED_PIECE != '.' || ${INITIAL_PIECE,,} == 'p' ]]; then
- HALF_MOVE=0
- else
- ((HALF_MOVE++))
- fi
- # Update full-move count
- if [[ $COLOR == 'w' ]] ; then ((FULL_MOVE++)); fi
- echo $BOARD $COLOR $CASTLE_RIGHTS $EP_TARGET $HALF_MOVE $FULL_MOVE
- }
- genmoves() {
- # Generate pseudo-legal moves & hope search will prove them bad
- local POSITION; read POSITION
- local BOARD=${POSITION%% *}
- POSITION=${POSITION#* }
- local COLOR=${POSITION%% *}
- POSITION=${POSITION#* }
- local CASTLE_RIGHTS=${POSITION%% *}
- POSITION=${POSITION#* }
- local EP_TARGET=${POSITION%% *}
- local FROM_POS TO_POS DELTA_POS EP_POS FROM_PIECE TO_PIECE
- EP_POS=$(square_to_position $EP_TARGET)
- # Hack to stop sliders from going through enemy pieces
- local WAS_CAPTURE
- is_move_of_type() {
- # Convert booleans to exit statuses. Ugh.
- local CAPTURE_OK=$1
- local NON_CAPTURE_OK=$2
- local EP_CAPTURE_OK=$3
- WAS_CAPTURE=1
- if (( TO_POS < 0 || TO_POS >= 78 )); then
- return 1
- elif (( TO_POS == EP_POS && EP_CAPTURE_OK )) ; then
- return 0
- fi
- TO_PIECE=${BOARD:$TO_POS:1}
- case $TO_PIECE in
- '.')
- WAS_CAPTURE=0
- return $((!NON_CAPTURE_OK))
- ;;
- '/') return 1 ;;
- [a-z]) if [[ $COLOR == 'w' ]]; then return $((!CAPTURE_OK)); else return 1; fi ;;
- [A-Z]) if [[ $COLOR == 'b' ]]; then return $((!CAPTURE_OK)); else return 1; fi ;;
- esac
- }
- try_move() {
- ((TO_POS = FROM_POS + DELTA_POS))
- if is_move_of_type $@ ; then
- MOVE=$(square_from_position $FROM_POS)$(square_from_position $TO_POS)
- if [[ ${FROM_PIECE,,} == 'p' && ${MOVE:3} == [18] ]]; then
- MOVE="${MOVE}q" # Promote to queen if pawn hits back rank
- fi
- echo "$MOVE "
- return 0
- else
- return 1
- fi
- }
- try_push() {
- while (( $# >= 1 )); do
- local DIR=$1
- DELTA_POS=$DIR
- try_move $INCLUDE_CAPTURES $INCLUDE_NON_CAPTURES $EXCLUDE_EP_CAPTURES
- shift
- done
- }
- try_slide() {
- while (( $# >= 1 )); do
- local DIR=$1
- DELTA_POS=$DIR
- while try_move $INCLUDE_CAPTURES $INCLUDE_NON_CAPTURES $EXCLUDE_EP_CAPTURES && (( !WAS_CAPTURE )); do
- (( DELTA_POS += $DIR))
- done
- shift
- done
- }
- for (( FROM_POS = 0; FROM_POS < 78; FROM_POS++)); do
- local FROM_PIECE=${BOARD:$FROM_POS:1}
- # Own color only! :)
- if [[ $COLOR == 'w' && $FROM_PIECE == [A-Z] || $COLOR == 'b' && $FROM_PIECE == [a-z] ]]; then
- FROM_PIECE=${FROM_PIECE,} # strip color
- case $FROM_PIECE in
- 'p')
- local PAWN_DIRECTION=-1
- if [[ $COLOR == 'b' ]]; then
- PAWN_DIRECTION=1
- fi
- local DOUBLE_PAWN_TEST="$COLOR$(square_from_position $FROM_POS)"
- if [[ $DOUBLE_PAWN_TEST == w?2 || $DOUBLE_PAWN_TEST == b?7 ]]; then
- ((DELTA_POS = 10*PAWN_DIRECTION)) # Avoid advancing through a piece
- if [[ ${BOARD:$((FROM_POS + DELTA_POS)):1} == '.' ]]; then
- ((DELTA_POS = 20*PAWN_DIRECTION)); try_move $EXCLUDE_CAPTURES $INCLUDE_NON_CAPTURES $EXCLUDE_EP_CAPTURES
- fi
- fi
- ((DELTA_POS = 10*PAWN_DIRECTION)); try_move $EXCLUDE_CAPTURES $INCLUDE_NON_CAPTURES $EXCLUDE_EP_CAPTURES
- ((DELTA_POS = 9*PAWN_DIRECTION)); try_move $INCLUDE_CAPTURES $EXCLUDE_NON_CAPTURES $INCLUDE_EP_CAPTURES
- ((DELTA_POS = 11*PAWN_DIRECTION)); try_move $INCLUDE_CAPTURES $EXCLUDE_NON_CAPTURES $INCLUDE_EP_CAPTURES
- ;;
- 'n')
- try_push -21 -19 -12 -8 8 12 19 21
- ;;
- 'k')
- try_push -11 -10 -9 -1 1 9 10 11
- ;;
- 'b')
- try_slide -11 -9 9 11
- ;;
- 'r')
- try_slide -10 -1 1 10
- ;;
- 'q')
- try_slide -11 -10 -9 -1 1 9 10 11
- ;;
- esac
- fi
- done
- # Special case: castling [TODO]
- }
- score() {
- local DEPTH=$1
- local SHOULD_TALK_TO_GUI=$2
- local POSITION; read POSITION
- local COLOR=${POSITION#* }
- COLOR=${COLOR%% *}
- # Test for two kings on board
- local TRICK=${POSITION%% *}
- TRICK=${POSITION%%*[kK]*[kK]*}
- if (( DEPTH <= 0 )) || [ -n "$TRICK" ]; then
- echo $(echo $POSITION | evaluate)
- return
- fi
- ((DEPTH--));
- local MOVE BESTMOVE MOVECOUNT=1 SCORE BESTSCORE=-20000
- for MOVE in $(echo $POSITION | genmoves); do
- if (( SHOULD_TALK_TO_GUI )); then
- echo info currmove $MOVE currmovenumber $((MOVECOUNT++))
- fi
- SCORE="$(echo $POSITION | move $MOVE | score $DEPTH $DONT_TALK_TO_GUI)"
- (( SCORE = -SCORE ))
- if (( SCORE > BESTSCORE )); then
- BESTSCORE=$SCORE
- BESTMOVE=$MOVE
- fi
- done
- if (( SCORE == -20000 )); then
- SCORE=0 # stalemate!
- fi
- if (( SHOULD_TALK_TO_GUI )); then
- echo "info score cp $BESTSCORE"
- echo "bestmove $BESTMOVE"
- else
- echo $BESTSCORE
- fi
- }
- evaluate() {
- local POSITION; read POSITION
- local BOARD=${POSITION%% *}
- POSITION=${POSITION#* }
- local COLOR=${POSITION%% *}
- POSITION=${POSITION#* }
- local CASTLE_RIGHTS=${POSITION%% *}
- POSITION=${POSITION#* }
- local EP_TARGET=${POSITION%% *}
- POSITION=${POSITION#* }
- local HALF_MOVE=${POSITION%% *}
- local SCORE=0
- if (( HALF_MOVE >= 50 )); then
- echo 0
- return
- fi
- until (( ${#BOARD} == 0 )); do
- # Use beginners' values ; the program's going to suck anyway!
- case ${BOARD:0:1} in
- 'P') ((SCORE += 100)) ;;
- 'N') ((SCORE += 300)) ;;
- 'B') ((SCORE += 300)) ;;
- 'R') ((SCORE += 500)) ;;
- 'Q') ((SCORE += 900)) ;;
- 'K') ((SCORE += 10000)) ;;
- 'p') ((SCORE -= 100)) ;;
- 'n') ((SCORE -= 300)) ;;
- 'b') ((SCORE -= 300)) ;;
- 'r') ((SCORE -= 500)) ;;
- 'q') ((SCORE -= 900)) ;;
- 'k') ((SCORE -= 10000)) ;;
- esac
- BOARD=${BOARD:1}
- done
- ((SCORE += (RANDOM % 99) - 49))
- if [[ $COLOR == 'w' ]]; then
- echo $SCORE
- else
- echo $((-SCORE))
- fi
- }
- while read command; do eval "$command"; done
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement