daily pastebin goal
57%
SHARE
TWEET

Justin Blanchard

a guest Feb 2nd, 2010 269 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
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top