juegos

Juegos de terminal de Pancho: Catan (TUI, GUI, web, servidor, PicoCalc), ajedrez, calculadora y minijuegos
git clone https://git.lu3dhn.xyz/juegos.git
Log | Files | Refs

board.sh (10501B)


      1 #!/usr/bin/env bash
      2 # ============================================================================
      3 #  lib/board.sh  --  El TABLERO: como guardamos la posicion y como la leemos.
      4 # ============================================================================
      5 #
      6 # Este archivo NO se ejecuta solo. Es una "libreria": un archivo lleno de
      7 # variables y funciones que otros archivos cargan (con el comando "source") para
      8 # usar. Aca vive todo lo basico sobre el tablero.
      9 #
     10 # COMO REPRESENTAMOS EL TABLERO (la idea mas importante de todo el proyecto):
     11 #   Un tablero de ajedrez tiene 64 casilleros (8 filas x 8 columnas). En la
     12 #   computadora lo guardamos como una LISTA de 64 elementos, uno por casillero.
     13 #   En programacion a esa lista se la llama "arreglo" o "array". Cada elemento
     14 #   tiene un numero de posicion (un "indice") que va del 0 al 63.
     15 #
     16 #   Decidimos numerar los casilleros asi: el indice 0 es la esquina de arriba a
     17 #   la izquierda (la casilla "a8"), y vamos contando hacia la derecha y hacia
     18 #   abajo, fila por fila, hasta el indice 63 que es "h1" (abajo a la derecha):
     19 #
     20 #        columna:  a  b  c  d  e  f  g  h
     21 #        fila 8:   0  1  2  3  4  5  6  7
     22 #        fila 7:   8  9 10 11 12 13 14 15
     23 #        fila 6:  16 17 18 19 20 21 22 23
     24 #        fila 5:  24 25 26 27 28 29 30 31
     25 #        fila 4:  32 33 34 35 36 37 38 39
     26 #        fila 3:  40 41 42 43 44 45 46 47
     27 #        fila 2:  48 49 50 51 52 53 54 55
     28 #        fila 1:  56 57 58 59 60 61 62 63
     29 #
     30 #   Cada elemento del arreglo guarda UNA letra que dice que hay en ese casillero:
     31 #     "."  = casillero vacio
     32 #     mayusculas P N B R Q K = piezas BLANCAS (Peon, kNight=caballo, Bishop=alfil,
     33 #                              Rook=torre, Queen=dama, King=rey)
     34 #     minusculas p n b r q k = piezas NEGRAS (las mismas, pero del otro color)
     35 #   Usamos las letras en ingles porque es el estandar internacional del ajedrez.
     36 
     37 # --- El estado del juego: un puniado de "variables globales" ----------------
     38 # Una variable es una cajita con nombre donde guardamos un valor. "Global"
     39 # quiere decir que cualquier funcion del programa la puede ver y modificar.
     40 # Entre todas estas variables describen COMPLETAMENTE la partida en un momento
     41 # dado (es lo que el ajedrez llama una posicion, en formato "FEN", ver mas abajo).
     42 
     43 declare -a BOARD     # "declare -a" crea un arreglo (la lista de 64 casilleros)
     44 TURN="white"         # de quien es el turno: "white" (blancas) o "black" (negras)
     45 CASTLE="KQkq"        # quien todavia puede enrocar (ver reglas mas adelante)
     46 EP="-"               # casilla de captura "al paso" (en passant), o "-" si no hay
     47 HALFMOVE=0           # contador de medias jugadas para la regla de 50 movimientos
     48 FULLMOVE=1           # numero de jugada completa (sube despues de que mueven las negras)
     49 
     50 # La posicion INICIAL del ajedrez, escrita en formato FEN (lo explicamos abajo).
     51 START_FEN="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
     52 
     53 # --- Ayudantes de coordenadas -----------------------------------------------
     54 # A veces es mas comodo pensar en (fila, columna) en vez de en un indice de 0 a
     55 # 63. Usamos "r" (de "rank"/fila) que va de 0 a 7 de ARRIBA hacia abajo (r=0 es
     56 # la fila 8 del ajedrez) y "c" (de "column"/columna) de 0 a 7 de IZQUIERDA a
     57 # derecha (c=0 es la columna "a").
     58 #
     59 # Nota sobre bash: "$(( ... ))" hace CUENTAS con numeros enteros. "$1" es el
     60 # primer argumento que recibe la funcion, "$2" el segundo. "echo" imprime el
     61 # resultado para que quien llama a la funcion lo pueda capturar.
     62 rc_to_idx() { echo $(( $1 * 8 + $2 )); }   # de (fila,columna) al indice 0..63
     63 idx_rank()  { echo $(( $1 / 8 )); }        # del indice a la fila (0..7, 0 = fila 8)
     64 idx_file()  { echo $(( $1 % 8 )); }        # del indice a la columna (0..7, 0 = "a")
     65 # "/" es division entera y "%" es el RESTO de la division. Por ejemplo, el indice
     66 # 10: 10/8 = 1 (fila), 10%8 = 2 (columna) -> esta en la fila r=1, columna c=2.
     67 
     68 # idx_to_alg: convierte un indice (0..63) al nombre de casilla del ajedrez, como
     69 # "e4" o "a8". A eso se le dice notacion "algebraica".
     70 idx_to_alg() {
     71     local files=(a b c d e f g h)   # arreglo con las letras de las columnas
     72     # "${files[N]}" agarra el elemento N del arreglo. La columna es (indice % 8).
     73     # La fila del ajedrez es 8 menos la fila interna (indice / 8).
     74     echo "${files[$(( $1 % 8 ))]}$(( 8 - ($1 / 8) ))"
     75 }
     76 
     77 # alg_to_idx: lo inverso. Convierte un nombre como "e4" al indice 0..63.
     78 alg_to_idx() {
     79     # "${1:0:1}" toma del argumento 1 caracter, empezando en la posicion 0: o sea
     80     # la PRIMERA letra ("e"). El truco "'e" en printf '%d' da el codigo numerico
     81     # de la letra en la tabla ASCII. La 'a' vale 97, asi que restando 97 a la
     82     # letra obtenemos: a->0, b->1, ..., h->7 (la columna).
     83     local f=$(( $(printf '%d' "'${1:0:1}") - 97 ))
     84     # "${1:1:1}" toma 1 caracter desde la posicion 1: el SEGUNDO (el numero de
     85     # fila del ajedrez). "8 - ese numero" nos da la fila interna (fila 8 -> r=0).
     86     local r=$(( 8 - ${1:1:1} ))
     87     echo $(( (r * 8) + f ))   # juntamos fila y columna en un solo indice
     88 }
     89 
     90 # --- Preguntas sobre una pieza ----------------------------------------------
     91 # Estas funciones contestan verdadero/falso (en bash, "exito" = verdadero).
     92 # "[[ "$1" =~ [A-Z] ]]" pregunta si el texto coincide con una expresion regular:
     93 # aca, si el caracter es una letra MAYUSCULA (de la A a la Z) -> es pieza blanca.
     94 is_white()  { [[ "$1" =~ [A-Z] ]]; }   # es una pieza blanca?
     95 is_black()  { [[ "$1" =~ [a-z] ]]; }   # es una pieza negra (minuscula)?
     96 is_empty()  { [[ "$1" == "." ]]; }     # el casillero esta vacio?
     97 
     98 # piece_color: dado el caracter de una pieza, imprime "white", "black" o "none".
     99 # "if/elif/else/fi" es la estructura "si... sino si... sino...". En bash el "if"
    100 # se cierra con "fi" (if al reves). Pregunta por la condicion ejecutando una
    101 # funcion: si esa funcion da exito, entra al "then".
    102 piece_color() {
    103     if is_white "$1"; then echo white
    104     elif is_black "$1"; then echo black
    105     else echo none
    106     fi
    107 }
    108 
    109 # other_color: dado un color, devuelve el contrario. "A && B || C" se lee como
    110 # "si A es verdadero hace B, sino hace C" (un if cortito en una sola linea).
    111 other_color() { [[ "$1" == white ]] && echo black || echo white; }
    112 
    113 # --- Leer un FEN (texto) y cargarlo en el tablero ---------------------------
    114 # QUE ES UN FEN: es la forma estandar de escribir una posicion de ajedrez en una
    115 # sola linea de texto. Tiene 6 partes separadas por espacios. Ejemplo, la
    116 # posicion inicial:
    117 #     rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
    118 #   1) Las piezas, fila por fila (de la 8 a la 1), separadas por "/". Las letras
    119 #      son piezas; un numero significa "esa cantidad de casillas vacias seguidas".
    120 #   2) "w" o "b": a quien le toca mover.
    121 #   3) Derechos de enroque (KQkq) o "-" si nadie puede.
    122 #   4) Casilla "al paso" o "-".
    123 #   5) Contador de la regla de 50 movimientos.
    124 #   6) Numero de jugada completa.
    125 #
    126 # setup_fen agarra ese texto y completa nuestras variables (BOARD, TURN, etc.).
    127 setup_fen() {
    128     local fen="$1"
    129     local placement turn castle ep half full
    130     # "read" parte el texto en pedazos usando los espacios y mete cada pedazo en
    131     # una variable. "<<< "$fen"" le da ese texto como entrada (here-string).
    132     read -r placement turn castle ep half full <<< "$fen"
    133 
    134     # "${placement//\//}" reemplaza TODAS las barras "/" por nada: nos deja solo
    135     # las 64 posiciones seguidas, sin los separadores de fila.
    136     local clean="${placement//\//}"
    137     local idx=0 i char
    138     BOARD=()                       # arrancamos con el tablero vacio
    139     # Recorremos caracter por caracter el texto de las piezas.
    140     for (( i=0; i<${#clean}; i++ )); do   # "${#clean}" es la cantidad de caracteres
    141         char="${clean:$i:1}"              # el caracter en la posicion i
    142         if [[ "$char" =~ [1-8] ]]; then
    143             # Si es un numero del 1 al 8, son casillas vacias: ponemos esa
    144             # cantidad de puntos "." en el tablero.
    145             local j
    146             for (( j=0; j<char; j++ )); do BOARD[idx]="."; ((idx++)); done
    147         else
    148             # Si es una letra, es una pieza: la guardamos tal cual.
    149             BOARD[idx]="$char"; ((idx++))
    150         fi
    151     done
    152 
    153     # Las otras 5 partes del FEN pueden venir o no (a veces nos pasan solo las
    154     # piezas). "[[ -n "$turn" ]]" pregunta si la variable NO esta vacia; solo en
    155     # ese caso la usamos. Convertimos "w"/"b" a "white"/"black".
    156     [[ -n "$turn"   ]] && { [[ "$turn" == w ]] && TURN=white || TURN=black; }
    157     [[ -n "$castle" ]] && CASTLE="$castle"
    158     [[ -n "$ep"     ]] && EP="$ep"
    159     [[ -n "$half"   ]] && HALFMOVE="$half"
    160     [[ -n "$full"   ]] && FULLMOVE="$full"
    161 }
    162 
    163 # --- Generar un FEN (lo contrario: del tablero al texto) --------------------
    164 # board_to_placement arma solo la PRIMERA parte del FEN (las piezas).
    165 board_to_placement() {
    166     local out="" r c run idx piece
    167     for (( r=0; r<8; r++ )); do          # por cada fila, de la 8 (r=0) a la 1
    168         run=0                            # contador de casillas vacias seguidas
    169         for (( c=0; c<8; c++ )); do      # por cada columna
    170             idx=$(( r * 8 + c ))
    171             piece="${BOARD[$idx]}"
    172             if [[ "$piece" == "." ]]; then
    173                 ((run++))                # otra vacia: sumamos al contador
    174             else
    175                 # Si veniamos contando vacias, primero escribimos ese numero y
    176                 # reiniciamos el contador; despues escribimos la pieza.
    177                 [[ $run -gt 0 ]] && out+="$run" && run=0
    178                 out+="$piece"
    179             fi
    180         done
    181         [[ $run -gt 0 ]] && out+="$run"  # vacias que quedaron al final de la fila
    182         [[ $r -lt 7 ]] && out+="/"       # separador entre filas (menos en la ultima)
    183     done
    184     echo "$out"
    185 }
    186 
    187 # get_fen: arma el FEN COMPLETO (las 6 partes) del estado actual del juego.
    188 get_fen() {
    189     local turn_char; [[ "$TURN" == white ]] && turn_char=w || turn_char=b
    190     echo "$(board_to_placement) $turn_char $CASTLE $EP $HALFMOVE $FULLMOVE"
    191 }
    192 
    193 # position_key: una "huella" de la posicion para detectar repeticiones. Por las
    194 # reglas FIDE, dos posiciones son "la misma" si coinciden las piezas, de quien es
    195 # el turno, los enroques disponibles y la casilla al paso. Por eso la huella usa
    196 # esas 4 cosas (y NO los contadores de jugadas). La usamos para la regla de
    197 # repeticion (la misma posicion varias veces = tablas).
    198 position_key() {
    199     local turn_char; [[ "$TURN" == white ]] && turn_char=w || turn_char=b
    200     echo "$(board_to_placement) $turn_char $CASTLE $EP"
    201 }