#! /usr/bin/env bash # # RTC Chess - chess-via-nex # Author: samhunter@rawtext.club # # REPLAY="" readonly SECRET="$(cat nightfall-city.id)" declare -A CB nex_connect() { cmd=$1 shift GAMEURL="$1" shift case "$cmd" in "get") printf "%s\r\n" "$GAMEURL" | nc nightfall.city 1900 #tail -1 "$GAMEURL" ;; "put") printf "%s\n%s\n%s\n.\n" "$GAMEURL" "$SECRET" "$*" | nc nightfall.city 1915 #printf "%s\n" "$*" > "$GAMEURL" ;; *) printf "incorrect command %s (parameters: %s)\n" "$cmd" "$@" ;; esac } instructions () { printf "chess - chess-over-nex game.\n" printf "%s [-b|-w] [-f]] [-l] [-h] [your url] [their url]\n" $(basename $0) printf "\t-b|-w - black side/white side perspective\n" printf "\t-f - fancy Unicode characters (might not work on some terminals.\n" printf "\t-h - this help\n" printf "\nSpecify your and your opponent's URL as the last two parameters.\n" printf "\nInstructions\n" printf "Move:\n\tspecify the start and end positions,\n\teg. e2e4, press Enter\n" printf "Check:\n\tadd '+' to the move, eg. e2e4+\n" printf "Checkmate:\n\tadd '#' to the move, eg. e2e4#\n" printf "Surrender:\n\tadd 's' to the move, eg. e2e4s\n" printf "Castling:\n\tking-side: oo\n\tqueen-side: ooo\n" printf "\nAll moves can use optional dashes eg. e2-e4\n" printf "\n" printf "Quit:\n\tEmpty line or Q, followed by Enter\n" exit 0 } declare -Ar fancy=( [wp]="$(printf "\u2659")" [wr]="$(printf "\u2656")" [wn]="$(printf "\u2658")" [wb]="$(printf "\u2657")" [wq]="$(printf "\u2655")" [wk]="$(printf "\u2654")" [xx]=" " [bp]="$(printf "\u265F")" [br]="$(printf "\u265C")" [bn]="$(printf "\u265E")" [bb]="$(printf "\u265D")" [bq]="$(printf "\u265B")" [bk]="$(printf "\u265A")" ) declare -Ar plain=( [wp]="p" [wr]="r" [wn]="n" [wb]="b" [wq]="q" [wk]="k" [xx]=" " [bp]="P" [br]="R" [bn]="N" [bb]="B" [bq]="Q" [bk]="K" ) setup_board() { case $1 in ("f"|"fancy") # I'm using a dark background, hence the reversed colours of pieces wp=${fancy[wp]}; wr=${fancy[wr]}; wn=${fancy[wn]}; wb=${fancy[wb]}; wq=${fancy[wq]}; wk=${fancy[wk]} bp=${fancy[bp]}; br=${fancy[br]}; bn=${fancy[bn]}; bb=${fancy[bb]}; bq=${fancy[bq]}; bk=${fancy[bk]} xx=${fancy[xx]}; ;; (*) wp=${plain[wp]}; wr=${plain[wr]}; wn=${plain[wn]}; wb=${plain[wb]}; wq=${plain[wq]}; wk=${plain[wk]} bp=${plain[bp]}; br=${plain[br]}; bn=${plain[bn]}; bb=${plain[bb]}; bq=${plain[bq]}; bk=${plain[bk]} xx=${plain[xx]} ;; esac CB["a8"]=$wr; CB["b8"]=$wn; CB["c8"]=$wb; CB["d8"]=$wq; CB["e8"]=$wk; CB["f8"]=$wb; CB["g8"]=$wn; CB["h8"]=$wr CB["a7"]=$wp; CB["b7"]=$wp; CB["c7"]=$wp; CB["d7"]=$wp; CB["e7"]=$wp; CB["f7"]=$wp; CB["g7"]=$wp; CB["h7"]=$wp CB["a6"]=$xx; CB["b6"]=$xx; CB["c6"]=$xx; CB["d6"]=$xx; CB["e6"]=$xx; CB["f6"]=$xx; CB["g6"]=$xx; CB["h6"]=$xx CB["a5"]=$xx; CB["b5"]=$xx; CB["c5"]=$xx; CB["d5"]=$xx; CB["e5"]=$xx; CB["f5"]=$xx; CB["g5"]=$xx; CB["h5"]=$xx CB["a4"]=$xx; CB["b4"]=$xx; CB["c4"]=$xx; CB["d4"]=$xx; CB["e4"]=$xx; CB["f4"]=$xx; CB["g4"]=$xx; CB["h4"]=$xx CB["a3"]=$xx; CB["b3"]=$xx; CB["c3"]=$xx; CB["d3"]=$xx; CB["e3"]=$xx; CB["f3"]=$xx; CB["g3"]=$xx; CB["h3"]=$xx CB["a2"]=$bp; CB["b2"]=$bp; CB["c2"]=$bp; CB["d2"]=$bp; CB["e2"]=$bp; CB["f2"]=$bp; CB["g2"]=$bp; CB["h2"]=$bp CB["a1"]=$br; CB["b1"]=$bn; CB["c1"]=$bb; CB["d1"]=$bq; CB["e1"]=$bk; CB["f1"]=$bb; CB["g1"]=$bn; CB["h1"]=$br } miniboard() { for r in {8..1}; do x=r printf "%1s " "$r" for c in {a..h}; do if (( ++x % 2 == 0 )); then printf " %1s " ${CB[$c$r]} else printf "\x1b[7;1m %1s \x1b[0m" ${CB[$c$r]} fi done printf "\n" done printf " " for c in {a..h}; do printf " %1c " $c done printf "\n" } flipped_board() { printf "\n +---+---+---+---+---+---+---+---+\n" for r in {1..8} ; do printf "%2s" $r for c in {h..a} ;do printf "| %1s " "${CB[$c$r]}" done printf "|\n +---+---+---+---+---+---+---+---+\n" done printf " " printf " %1s " {h..a} printf "\n" } board() { printf "\n +---+---+---+---+---+---+---+---+\n" for r in {8..1} ; do printf "%2s" $r for c in {a..h} ;do printf "| %1s " "${CB[$c$r]}" done printf "|\n +---+---+---+---+---+---+---+---+\n" done printf " " printf " %1s " {a..h} printf "\n" } move() { MOVE=${1,,} ACTION="" HIT="" # en passant? if [[ "${MOVE,,}" =~ (a[45]-?b[36]|h[45]-?g[36]|b[45]-?[ac][36]|c[45]-?[db][36]|d[45]-?[ce][36]|e[45]-?[df][36]|f[45]-?[eg][36]|g[45]-?[fh][36]) ]] ; then if [[ "$MOVE" =~ (([a-h])([1-8]))-?(([a-h])([1-8])) ]]; then # printf "%s\n" "${BASH_REMATCH[@]}" # sleep 20 FM=${BASH_REMATCH[1]} TO=${BASH_REMATCH[4]} EP="${BASH_REMATCH[5]}${BASH_REMATCH[3]}" PIECE="${CB[$FM]}" HITP="${CB[$EP]}" if [[ "$PIECE" == "p" && "$HITP" == "P" ]] || [[ "$PIECE" == "P" && "$HITP" == "p" ]] ; then HIT="$HITP" CB[$EP]=" " CB[$TO]="$PIECE" fi fi fi if [[ "$MOVE" =~ ([a-h][1-8])-*([a-h][1-8])-?(.*) ]] ; then FM="${BASH_REMATCH[1]}" TO="${BASH_REMATCH[2]}" ACTION="${BASH_REMATCH[3]}" case $ACTION in ("m"|"M"|"#"|"++") MSG="Checkmate" # Checkmate ACTION="#" ;; ("c"|"C"|"+") MSG="Check" # Check ACTION="+" ;; ("s"|"S") MSG="You won! GG" # Surrender ACTION="S" ;; *) MSG="" ;; esac PIECE="${CB[$FM]}" CB[$FM]=" " # Promotion if [[ "$PIECE" =~ p && "$TO" =~ [a-h]1 ]]; then PIECE="q" elif [[ "$PIECE" =~ P && "$TO" =~ [a-h]8 ]]; then PIECE="Q" fi CB[$TO]="$PIECE" elif [[ "$MOVE" =~ o-?o-?o ]]; then FM=$MOVE TO="" MSG="castling queen-side" if [ $WHITES_MOVE -eq 1 ]; then CB["c1"]="${CB["e1"]}" CB["e1"]=" " CB["d1"]="${CB["a1"]}" CB["a1"]=" " else CB["c8"]="${CB["e8"]}" CB["e8"]=" " CB["d8"]="${CB["a8"]}" CB["a8"]=" " fi elif [[ "$MOVE" =~ o-?o ]]; then FM=$MOVE TO="" MSG="castling king-side" if [ $WHITES_MOVE -eq 1 ]; then CB["g1"]="${CB["e1"]}" CB["e1"]=" " CB["f1"]="${CB["h1"]}" CB["h1"]=" " else CB["g8"]="${CB["e8"]}" CB["e8"]=" " CB["f8"]="${CB["h8"]}" CB["h8"]=" " fi fi [[ -z "$ACTION" && -n "$HIT" ]] && ACTION="x" LAST_MOVE="you: $FM$TO$ACTION $MSG" } init () { printf "Loading game status...\n" MOVES=( $(nex_connect get "$GAMEURLIN" |sed -e 's/:/ /g')) WHITES_MOVE=1 for MOVE in "${MOVES[@]:1}"; do move $MOVE WHITES_MOVE=$((1-$WHITES_MOVE)) done LAST_MOVE="$FM$TO$ACTION $MSG" } #trap clean_exit INT QUIT TERM EXIT FANCY=false BLACK_VIEW=false while getopts :bBhflwW FLAG; do case $FLAG in (f) FANCY=true ;; (b|B) BLACK_VIEW=true ;; (w|W) BLACK_VIEW=false ;; (h) instructions ; exit 0 ;; (*) printf "%s [-b|-w] [-f]] [-h] [game id]\n" $(basename $0); exit 0 ;; esac done shift $((OPTIND - 1)) GAMEURLIN="$1" GAMEURLOUT="$2" MSG="" if $FANCY ; then setup_board fancy else setup_board normal fi init #clear if $BLACK_VIEW ; then flipped_board else board fi echo "Last move: $LAST_MOVE" read -p "Your move [? for help]: " MOVE if [[ -n "$MOVE" ]]; then [[ "$MOVE" == "?" ]] && { instructions ; exit ; } [[ "$MOVE" == "Q" ]] && exit move $MOVE #clear if $BLACK_VIEW ; then flipped_board else board fi echo "Last move: $LAST_MOVE" read -p "Send your move [y/n]? " SEND if [[ "$SEND" == "y" ]]; then SEND_MOVE="${MOVES[@]:1} $MOVE" nex_connect put "$GAMEURLOUT" "$(printf 'MOVE:%s' "${SEND_MOVE## }")" else echo "Your move wasn't sent." fi fi