Guest User

Justin Blanchard

a guest
Feb 2nd, 2010
338
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/bin/bash
  2.  
  3. # Ensure string matches are case-sensitive
  4. LANG=C
  5.  
  6. echo 'ShouldntExist v0.2'
  7. echo 'Copyright 2010 Justin Blanchard <UncombedCoconut at gmail>'
  8. echo 'License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.'
  9. echo 'You are free to change and redistribute it.'
  10. echo '(If you do something that foolish please drop me a line though!)'
  11.  
  12. # Names for boolean arguments that indicate what they do
  13. INCLUDE_CAPTURES=1 INCLUDE_NON_CAPTURES=1 INCLUDE_EP_CAPTURES=1 TALK_TO_GUI=1
  14. EXCLUDE_CAPTURES=0 EXCLUDE_NON_CAPTURES=0 EXCLUDE_EP_CAPTURES=0 DONT_TALK_TO_GUI=0
  15.  
  16. # TODO (chess rules):
  17. # knowing when castling is legal
  18. # chess960 castling
  19. # Threefold repetition (yeah, right...)
  20. # TODO (UCI features):
  21. # Support various arguments to go
  22. # Send back info about depth / nodes / nps for giggles
  23.  
  24. # Implement UCI commands as bash commands, then enter a REPL.
  25.  
  26. uci() {
  27.   ROOT_DEPTH=2
  28.  
  29.   echo id name ShouldntExist 0.2
  30.   echo id author Justin Blanchard
  31.   echo option name Root Depth type spin default $ROOT_DEPTH min 2 max 3
  32.   # You don't want to go over 3. Trust me.
  33.   echo uciok
  34. }
  35.  
  36. debug() {
  37.   DEBUG="${1,,}"
  38. }
  39.  
  40. isready() {
  41.   echo readyok
  42. }
  43.  
  44. setoption() {
  45.   ARGS="${@,,}"
  46.   local PAIR="${ARGS#name }"
  47.   local NAME="${PAIR% value*}"
  48.   if [[ "$PAIR" == "$NAME" ]]; then
  49.     # handle button option
  50.     case "$NAME" in
  51.     esac
  52.   else
  53.     # handle option with value
  54.     local VALUE="${PAIR##*value }"
  55.     case "$NAME" in
  56.       'root depth') ROOT_DEPTH="$VALUE" ;;
  57.     esac
  58.   fi
  59.  
  60.   if [[ "$DEBUG" == 'on' ]]; then
  61.     echo info string option 'Root Depth' set to $ROOT_DEPTH
  62.   fi
  63. }
  64.  
  65. register() {
  66.   echo registration checking
  67.   echo registration ok
  68. }
  69.  
  70. ucinewgame() { :; }
  71.  
  72. position()
  73. {
  74.   if [[ "$1" == 'startpos' ]]; then
  75.     ROOT_POSITION=$(startpos)
  76.     shift 1
  77.   elif [[ "$1" == 'fen' ]]; then
  78.     ROOT_POSITION=$(fen $2 $3 $4 $5 $6 $7)
  79.     shift 7
  80.   fi
  81.   if [[ "$1" == 'moves' ]]; then
  82.     until (( $# <= 1 )); do
  83.       shift
  84.       ROOT_POSITION=$(echo $ROOT_POSITION | move $1)
  85.     done
  86.   fi
  87.  
  88.   if [[ "$DEBUG" == 'on' ]]; then
  89.     echo info string position set to $ROOT_POSITION
  90.   fi
  91. }
  92.  
  93. go() {
  94.   # TODO: actually look at the arguments
  95.   if [[ "$1" == 'ponder' ]]; then
  96.     return
  97.   fi
  98.   echo $ROOT_POSITION | score $ROOT_DEPTH $TALK_TO_GUI
  99. }
  100.  
  101. stop() { :; }
  102.  
  103. ponderhit() { :; }
  104.  
  105. quit() {
  106.   exit 0
  107. }
  108.  
  109. # convert UCI phrases to internal board rep, which looks like:
  110. # rnbqkbnr//pppppppp//........//........//........//........//PPPPPPPP//RNBQKBNR w KQkq - 0 1
  111.  
  112. fen() {
  113.   local BOARD=$1
  114.   local COLOR=$2
  115.   local CASTLE_RIGHTS=$3
  116.   local EP_TARGET=$4
  117.   local HALF_MOVE=$5
  118.   local FULL_MOVE=$6
  119.   BOARD=${BOARD//1/.}
  120.   BOARD=${BOARD//2/..}
  121.   BOARD=${BOARD//3/...}
  122.   BOARD=${BOARD//4/....}
  123.   BOARD=${BOARD//5/.....}
  124.   BOARD=${BOARD//6/......}
  125.   BOARD=${BOARD//7/.......}
  126.   BOARD=${BOARD//8/........}
  127.   BOARD=${BOARD//\//\/\/}
  128.   echo $BOARD $COLOR $CASTLE_RIGHTS $EP_TARGET $HALF_MOVE $FULL_MOVE
  129. }
  130.  
  131. startpos() {
  132.   fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
  133. }
  134.  
  135. # Conversion between readable square and index into the string
  136.  
  137. square_to_position() {
  138.   local SQUARE="$1"
  139.   local ROW=${SQUARE:1:1}
  140.   local TRICK="a1b2c3d4e5f6g7h8"
  141.   TRICK=${TRICK#*${SQUARE:0:1}}
  142.   local COL=${TRICK:0:1}
  143.   echo $(( 10*(8-ROW) + COL - 1 ))
  144. }
  145.  
  146. square_from_position() {
  147.   local POS="$1"
  148.   local FILES='abcdefghxX'
  149.   echo ${FILES:$((POS%10)):1}$((8-POS/10))
  150. }
  151.  
  152. # Board manipulation
  153.  
  154. board_get() {
  155.   local BOARD="$1"
  156.   local SQUARE="$2"
  157.   echo ${BOARD:$(square_to_position $SQUARE):1}
  158. }
  159.  
  160. board_set() {
  161.   local BOARD="$1"
  162.   local SQUARE="$2"
  163.   local PIECE="$3"
  164.   local POSITION=$(square_to_position $SQUARE)
  165.   echo ${BOARD:0:$POSITION}${PIECE}${BOARD:$((POSITION+1))}
  166. }
  167.  
  168. move() {
  169.   local MOVE=$1
  170.   local POSITION; read POSITION
  171.   local BOARD=${POSITION%% *}
  172.   POSITION=${POSITION#* }
  173.   local COLOR=${POSITION%% *}
  174.   POSITION=${POSITION#* }
  175.   local CASTLE_RIGHTS=${POSITION%% *}
  176.   POSITION=${POSITION#* }
  177.   local EP_TARGET=${POSITION%% *}
  178.   POSITION=${POSITION#* }
  179.   local HALF_MOVE=${POSITION%% *}
  180.   POSITION=${POSITION#* }
  181.   local FULL_MOVE=${POSITION%% *}
  182.  
  183.   local MOVE_FROM=${MOVE:0:2}
  184.   local MOVE_TO=${MOVE:2:2}
  185.   local PROMOTION_PIECE=${MOVE:4}
  186.   local INITIAL_PIECE=$(board_get $BOARD $MOVE_FROM)
  187.   local CAPTURED_PIECE=$(board_get $BOARD $MOVE_TO) # For half-move update
  188.  
  189.   local FINAL_PIECE
  190.   if [[ -z "$PROMOTION_PIECE" ]]; then
  191.     FINAL_PIECE=$INITIAL_PIECE
  192.   elif [[ $COLOR == 'w' ]]; then
  193.     FINAL_PIECE=${PROMOTION_PIECE^^}
  194.   else
  195.     FINAL_PIECE=${PROMOTION_PIECE,,}
  196.   fi
  197.   # Update board
  198.   BOARD=$(board_set $BOARD $MOVE_TO $FINAL_PIECE)
  199.   BOARD=$(board_set $BOARD $MOVE_FROM .)
  200.   # Special case: en passant capture
  201.   if [[ $MOVE_TO == $EP_TARGET && ${INITIAL_PIECE,,} == 'p' ]]; then
  202.     # Delete pawn on to-square's file, from-square's rank
  203.     BOARD=$(board_set $BOARD ${MOVE_TO:0:1}${MOVE_FROM:1} .)
  204.   fi
  205.   # Special case: castling (will FAIL for chess960)
  206.   if [[ ${INITIAL_PIECE,,} == 'k' ]]; then
  207.     case $MOVE in
  208.       'e1c1')
  209.       BOARD=$(board_set $BOARD d1 R)
  210.       BOARD=$(board_set $BOARD a1 .)
  211.       ;;
  212.       'e1g1')
  213.       BOARD=$(board_set $BOARD f1 R)
  214.       BOARD=$(board_set $BOARD h1 .)
  215.       ;;
  216.       'e8c8')
  217.       BOARD=$(board_set $BOARD d8 r)
  218.       BOARD=$(board_set $BOARD a8 .)
  219.       ;;
  220.       'e8g8')
  221.       BOARD=$(board_set $BOARD f8 r)
  222.       BOARD=$(board_set $BOARD h8 .)
  223.       ;;
  224.     esac
  225.   fi
  226.  
  227.   # Update side to move
  228.   case $COLOR in
  229.     'w') COLOR=b ;;
  230.     'b') COLOR=w ;;
  231.   esac
  232.  
  233.   # Update castle rights (TODO)
  234.   # Engine is too stupid to castle anyway
  235.   # The only hard part here is handling FEN / Shredder-FEN / X-FEN
  236.  
  237.   # Update en passant target (naively a la FEN)
  238.   if [[ ${INITIAL_PIECE,,} == 'p' ]]; then
  239.     case ${MOVE_FROM:1}${MOVE_TO:1} in
  240.       '24') EP_TARGET=${MOVE_FROM:0:1}3 ;;
  241.       '75') EP_TARGET=${MOVE_FROM:0:1}6 ;;
  242.       *) EP_TARGET='.'
  243.     esac
  244.   else
  245.     EP_TARGET='.'
  246.   fi
  247.  
  248.   # Update half-move clock
  249.   if [[ $CAPTURED_PIECE != '.' || ${INITIAL_PIECE,,} == 'p' ]]; then
  250.     HALF_MOVE=0
  251.   else
  252.     ((HALF_MOVE++))
  253.   fi
  254.  
  255.   # Update full-move count
  256.   if [[ $COLOR == 'w' ]] ; then ((FULL_MOVE++)); fi
  257.  
  258.   echo $BOARD $COLOR $CASTLE_RIGHTS $EP_TARGET $HALF_MOVE $FULL_MOVE
  259. }
  260.  
  261. genmoves() {
  262.   # Generate pseudo-legal moves & hope search will prove them bad
  263.   local POSITION; read POSITION
  264.   local BOARD=${POSITION%% *}
  265.   POSITION=${POSITION#* }
  266.   local COLOR=${POSITION%% *}
  267.   POSITION=${POSITION#* }
  268.   local CASTLE_RIGHTS=${POSITION%% *}
  269.   POSITION=${POSITION#* }
  270.   local EP_TARGET=${POSITION%% *}
  271.  
  272.   local FROM_POS TO_POS DELTA_POS EP_POS FROM_PIECE TO_PIECE
  273.   EP_POS=$(square_to_position $EP_TARGET)
  274.  
  275.   # Hack to stop sliders from going through enemy pieces
  276.   local WAS_CAPTURE
  277.  
  278.   is_move_of_type() {
  279.     # Convert booleans to exit statuses. Ugh.
  280.     local CAPTURE_OK=$1
  281.     local NON_CAPTURE_OK=$2
  282.     local EP_CAPTURE_OK=$3
  283.     WAS_CAPTURE=1
  284.     if (( TO_POS < 0 || TO_POS >= 78 )); then
  285.       return 1
  286.     elif (( TO_POS == EP_POS && EP_CAPTURE_OK )) ; then
  287.       return 0
  288.     fi
  289.     TO_PIECE=${BOARD:$TO_POS:1}
  290.     case $TO_PIECE in
  291.       '.')
  292.       WAS_CAPTURE=0
  293.       return $((!NON_CAPTURE_OK))
  294.       ;;
  295.       '/') return 1 ;;
  296.       [a-z]) if [[ $COLOR == 'w' ]]; then return $((!CAPTURE_OK)); else return 1; fi ;;
  297.       [A-Z]) if [[ $COLOR == 'b' ]]; then return $((!CAPTURE_OK)); else return 1; fi ;;
  298.     esac
  299.   }
  300.  
  301.   try_move() {
  302.     ((TO_POS = FROM_POS + DELTA_POS))
  303.     if is_move_of_type $@ ; then
  304.       MOVE=$(square_from_position $FROM_POS)$(square_from_position $TO_POS)
  305.       if [[ ${FROM_PIECE,,} == 'p' && ${MOVE:3} == [18] ]]; then
  306.         MOVE="${MOVE}q" # Promote to queen if pawn hits back rank
  307.       fi
  308.       echo "$MOVE "
  309.       return 0
  310.     else
  311.       return 1
  312.     fi
  313.   }
  314.  
  315.   try_push() {
  316.     while (( $# >= 1 )); do
  317.       local DIR=$1
  318.       DELTA_POS=$DIR
  319.       try_move $INCLUDE_CAPTURES $INCLUDE_NON_CAPTURES $EXCLUDE_EP_CAPTURES
  320.       shift
  321.     done
  322.   }
  323.  
  324.   try_slide() {
  325.     while (( $# >= 1 )); do
  326.       local DIR=$1
  327.       DELTA_POS=$DIR
  328.       while try_move $INCLUDE_CAPTURES $INCLUDE_NON_CAPTURES $EXCLUDE_EP_CAPTURES && (( !WAS_CAPTURE )); do
  329.         (( DELTA_POS += $DIR))
  330.       done
  331.       shift
  332.     done
  333.   }
  334.  
  335.   for (( FROM_POS = 0; FROM_POS < 78; FROM_POS++)); do
  336.     local FROM_PIECE=${BOARD:$FROM_POS:1}
  337.     # Own color only! :)
  338.     if [[ $COLOR == 'w' && $FROM_PIECE == [A-Z] || $COLOR == 'b' && $FROM_PIECE == [a-z] ]]; then
  339.       FROM_PIECE=${FROM_PIECE,} # strip color
  340.       case $FROM_PIECE in
  341.         'p')
  342.         local PAWN_DIRECTION=-1
  343.         if [[ $COLOR == 'b' ]]; then
  344.           PAWN_DIRECTION=1
  345.         fi
  346.         local DOUBLE_PAWN_TEST="$COLOR$(square_from_position $FROM_POS)"
  347.         if [[ $DOUBLE_PAWN_TEST == w?2 || $DOUBLE_PAWN_TEST == b?7 ]]; then
  348.             ((DELTA_POS = 10*PAWN_DIRECTION)) # Avoid advancing through a piece
  349.           if [[ ${BOARD:$((FROM_POS + DELTA_POS)):1} == '.' ]]; then
  350.             ((DELTA_POS = 20*PAWN_DIRECTION)); try_move $EXCLUDE_CAPTURES $INCLUDE_NON_CAPTURES $EXCLUDE_EP_CAPTURES
  351.           fi
  352.         fi
  353.         ((DELTA_POS = 10*PAWN_DIRECTION)); try_move $EXCLUDE_CAPTURES $INCLUDE_NON_CAPTURES $EXCLUDE_EP_CAPTURES
  354.         ((DELTA_POS =  9*PAWN_DIRECTION)); try_move $INCLUDE_CAPTURES $EXCLUDE_NON_CAPTURES $INCLUDE_EP_CAPTURES
  355.         ((DELTA_POS = 11*PAWN_DIRECTION)); try_move $INCLUDE_CAPTURES $EXCLUDE_NON_CAPTURES $INCLUDE_EP_CAPTURES
  356.         ;;
  357.         'n')
  358.         try_push -21 -19 -12 -8 8 12 19 21
  359.         ;;
  360.         'k')
  361.         try_push -11 -10 -9 -1 1 9 10 11
  362.         ;;
  363.         'b')
  364.         try_slide -11 -9 9 11
  365.         ;;
  366.         'r')
  367.         try_slide -10 -1 1 10
  368.         ;;
  369.         'q')
  370.         try_slide -11 -10 -9 -1 1 9 10 11
  371.         ;;
  372.       esac
  373.     fi
  374.   done
  375.  
  376.   # Special case: castling [TODO]
  377. }
  378.  
  379. score() {
  380.   local DEPTH=$1
  381.   local SHOULD_TALK_TO_GUI=$2
  382.   local POSITION; read POSITION
  383.   local COLOR=${POSITION#* }
  384.   COLOR=${COLOR%% *}
  385.  
  386.   # Test for two kings on board
  387.   local TRICK=${POSITION%% *}
  388.   TRICK=${POSITION%%*[kK]*[kK]*}
  389.  
  390.   if (( DEPTH <= 0 )) || [ -n "$TRICK" ]; then
  391.     echo $(echo $POSITION | evaluate)
  392.     return
  393.   fi
  394.  
  395.   ((DEPTH--));
  396.   local MOVE BESTMOVE MOVECOUNT=1 SCORE BESTSCORE=-20000
  397.   for MOVE in $(echo $POSITION | genmoves); do
  398.     if (( SHOULD_TALK_TO_GUI )); then
  399.       echo info currmove $MOVE currmovenumber $((MOVECOUNT++))
  400.     fi
  401.     SCORE="$(echo $POSITION | move $MOVE | score $DEPTH $DONT_TALK_TO_GUI)"
  402.     (( SCORE = -SCORE ))
  403.     if (( SCORE > BESTSCORE )); then
  404.       BESTSCORE=$SCORE
  405.       BESTMOVE=$MOVE
  406.     fi
  407.   done
  408.   if (( SCORE == -20000 )); then
  409.     SCORE=0 # stalemate!
  410.   fi
  411.   if (( SHOULD_TALK_TO_GUI )); then
  412.     echo "info score cp $BESTSCORE"
  413.     echo "bestmove $BESTMOVE"
  414.   else
  415.     echo $BESTSCORE
  416.   fi
  417. }
  418.  
  419. evaluate() {
  420.   local POSITION; read POSITION
  421.   local BOARD=${POSITION%% *}
  422.   POSITION=${POSITION#* }
  423.   local COLOR=${POSITION%% *}
  424.   POSITION=${POSITION#* }
  425.   local CASTLE_RIGHTS=${POSITION%% *}
  426.   POSITION=${POSITION#* }
  427.   local EP_TARGET=${POSITION%% *}
  428.   POSITION=${POSITION#* }
  429.   local HALF_MOVE=${POSITION%% *}
  430.  
  431.   local SCORE=0
  432.   if (( HALF_MOVE >= 50 )); then
  433.     echo 0
  434.     return
  435.   fi
  436.  
  437.   until (( ${#BOARD} == 0 )); do
  438.     # Use beginners' values ; the program's going to suck anyway!
  439.     case ${BOARD:0:1} in
  440.       'P') ((SCORE += 100)) ;;
  441.       'N') ((SCORE += 300)) ;;
  442.       'B') ((SCORE += 300)) ;;
  443.       'R') ((SCORE += 500)) ;;
  444.       'Q') ((SCORE += 900)) ;;
  445.       'K') ((SCORE += 10000)) ;;
  446.       'p') ((SCORE -= 100)) ;;
  447.       'n') ((SCORE -= 300)) ;;
  448.       'b') ((SCORE -= 300)) ;;
  449.       'r') ((SCORE -= 500)) ;;
  450.       'q') ((SCORE -= 900)) ;;
  451.       'k') ((SCORE -= 10000)) ;;
  452.     esac
  453.     BOARD=${BOARD:1}
  454.   done
  455.   ((SCORE += (RANDOM % 99) - 49))
  456.   if [[ $COLOR == 'w' ]]; then
  457.     echo $SCORE
  458.   else
  459.     echo $((-SCORE))
  460.   fi
  461. }
  462.  
  463. while read command; do eval "$command"; done
RAW Paste Data