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

selftest.sh (10272B)


      1 #!/usr/bin/env bash
      2 # ============================================================================
      3 #  lib/selftest.sh  --  Pruebas automaticas del motor (sin interfaz ni red).
      4 # ============================================================================
      5 #
      6 # QUE ES ESTO: un "self-test" (auto-prueba) es un conjunto de pruebas que el
      7 # programa se corre a SI MISMO para verificar que las reglas funcionan bien. Es la
      8 # red de seguridad: si tocas algo del motor y rompes una regla, estas pruebas lo
      9 # detectan al instante. Se corren con:  ./ches.sh --selftest
     10 #
     11 # Cada prueba arma una posicion conocida, hace unas jugadas, y compara el
     12 # resultado con lo que ESPERABAMOS. Si coinciden, "ok"; si no, "FAIL".
     13 # (Los textos de las pruebas quedan en ingles porque son mensajes que se imprimen.)
     14 
     15 _ST_PASS=0   # contador de pruebas que pasaron
     16 _ST_FAIL=0   # contador de pruebas que fallaron
     17 
     18 # _assert: el corazon de las pruebas. Recibe una descripcion, el valor ESPERADO y
     19 # el valor REAL. Si son iguales, cuenta un "ok"; si no, muestra el FAIL con ambos
     20 # valores para poder depurar.
     21 _assert() {
     22     local desc=$1 expected=$2 actual=$3
     23     if [[ "$expected" == "$actual" ]]; then
     24         ((_ST_PASS++))
     25         printf '  \e[32mok\e[0m   %s\n' "$desc"          # \e[32m = verde
     26     else
     27         ((_ST_FAIL++))
     28         printf '  \e[31mFAIL\e[0m %s\n       expected: %s\n       actual:   %s\n' \
     29             "$desc" "$expected" "$actual"                # \e[31m = rojo
     30     fi
     31 }
     32 
     33 # _play: ayuda para las pruebas. Carga una posicion (FEN), reinicia el contador de
     34 # repeticiones, y aplica una lista de jugadas. "$@" son todos los argumentos
     35 # (despues del FEN) y el "for" los aplica uno por uno.
     36 _play() {
     37     local fen=$1; shift
     38     POSCOUNT=()
     39     setup_fen "$fen"
     40     POSCOUNT["$(position_key)"]=1
     41     local m
     42     for m in "$@"; do apply_move "$m"; done
     43 }
     44 
     45 # _sq: que pieza hay en una casilla (ej: _sq f7). Util para chequear resultados.
     46 _sq() { echo "${BOARD[$(alg_to_idx "$1")]}"; }
     47 
     48 # run_selftest: corre todas las pruebas, una atras de otra.
     49 run_selftest() {
     50     echo "Running engine self-tests..."
     51 
     52     # 1. Mate del pastor (un mate clasico en 4 jugadas).
     53     _play "$START_FEN" e2e4 e7e5 f1c4 b8c6 d1h5 g8f6 h5f7
     54     _assert "scholar's mate is checkmate" checkmate "$(game_status)"
     55     _assert "scholar's mate: queen on f7" Q "$(_sq f7)"
     56 
     57     # 2. Captura al paso (en passant).
     58     _play "$START_FEN" e2e4 a7a6 e4e5 d7d5
     59     _assert "en passant target set to d6" d6 "$EP"
     60     _play "$START_FEN" e2e4 a7a6 e4e5 d7d5 e5d6
     61     _assert "en passant: white pawn on d6" P "$(_sq d6)"
     62     _assert "en passant: captured pawn gone from d5" . "$(_sq d5)"
     63     _assert "en passant: e5 vacated" . "$(_sq e5)"
     64 
     65     # 3. Enroque corto.
     66     _play "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1" e1g1
     67     _assert "castling: white king on g1" K "$(_sq g1)"
     68     _assert "castling: rook moved to f1" R "$(_sq f1)"
     69     _assert "castling: h1 vacated" . "$(_sq h1)"
     70     _assert "castling: e1 vacated" . "$(_sq e1)"
     71     _assert "castling: white rights cleared" "kq" "$CASTLE"
     72 
     73     # 4. Coronacion de peon.
     74     _play "k6K/P7/8/8/8/8/8/8 w - - 0 1" a7a8q
     75     _assert "promotion: queen on a8" Q "$(_sq a8)"
     76     _assert "promotion: a7 vacated" . "$(_sq a7)"
     77 
     78     # 5. Ahogado (le toca a las negras, no estan en jaque, pero no tienen jugadas).
     79     _play "7k/5Q2/6K1/8/8/8/8/8 b - - 0 1"
     80     _assert "stalemate detected" stalemate "$(game_status)"
     81 
     82     # 6. Control: la posicion inicial tiene exactamente 20 jugadas legales.
     83     _play "$START_FEN"
     84     _assert "start position legal move count" 20 "$(legal_moves | grep -c .)"
     85 
     86     # 6b. Repeticion: la triple es RECLAMABLE (no automatica); la quintuple si corta.
     87     _play "$START_FEN" g1f3 g8f6 f3g1 f6g8 g1f3 g8f6 f3g1 f6g8         # inicio visto 3 veces
     88     _assert "threefold is claimable"       yes     "$(can_claim_threefold && echo yes || echo no)"
     89     _assert "threefold does not auto-draw" ongoing "$(game_status)"
     90     _play "$START_FEN" g1f3 g8f6 f3g1 f6g8 g1f3 g8f6 f3g1 f6g8 \
     91                        g1f3 g8f6 f3g1 f6g8 g1f3 g8f6 f3g1 f6g8         # inicio visto 5 veces
     92     _assert "fivefold auto-draws"          draw-fivefold "$(game_status)"
     93 
     94     # 6c. Regla de 75 movimientos: tablas automaticas a las 150 medias jugadas.
     95     _play "4k3/8/8/8/8/8/8/4K2R w - - 149 200" h1h2
     96     _assert "75-move rule auto-draws"      draw-75move "$(game_status)"
     97 
     98     # 7. Reloj: codificar/decodificar el control de tiempo y dar formato.
     99     TC_ENABLED=1; TC_BASE=300; TC_INC=5
    100     _assert "tc_encode timed control" "300:5" "$(tc_encode)"
    101     TC_ENABLED=0; TC_BASE=0; TC_INC=0
    102     _assert "tc_encode untimed" "off" "$(tc_encode)"
    103     tc_decode "180:2"
    104     _assert "tc_decode base"      180 "$TC_BASE"
    105     _assert "tc_decode increment" 2   "$TC_INC"
    106     _assert "tc_decode enabled"   1   "$TC_ENABLED"
    107     tc_decode "off"
    108     _assert "tc_decode off disables" 0 "$TC_ENABLED"
    109     _assert "clock format mm:ss" "05:05" "$(_fmt_clock 305)"
    110     _assert "clock format clamps negative" "00:00" "$(_fmt_clock -7)"
    111 
    112     # 8. Guardar / recuperar: una partida guardada y vuelta a cargar restaura todo.
    113     _play "$START_FEN" e2e4 e7e5 g1f3
    114     TC_ENABLED=1; TC_BASE=300; TC_INC=5; CLOCK_white=271; CLOCK_black=283
    115     ALLOW_TAKEBACK=1
    116     HISTORY=("e2e4  e7e5" "g1f3  ..."); HIST_FULLMOVE=1; HIST_LINE="g1f3"
    117     local _sv; _sv="$(mktemp)"
    118     save_game "$_sv"
    119     # ensuciamos el estado a proposito, despues recargamos en las variables GAME_*
    120     TC_ENABLED=0; CLOCK_white=0; CLOCK_black=0; ALLOW_TAKEBACK=0
    121     load_game "$_sv"; rm -f "$_sv"
    122     _assert "resume restores fen"        "$(get_fen)" "$GAME_FEN"
    123     _assert "resume restores white clock" 271 "$GAME_CW"
    124     _assert "resume restores black clock" 283 "$GAME_CB"
    125     _assert "resume restores time control" 1  "$TC_ENABLED"
    126     _assert "resume restores increment"    5  "$TC_INC"
    127     _assert "resume restores takeback flag" 1 "$GAME_TB"
    128     _assert "resume restores history len"  2  "${#GAME_HIST[@]}"
    129 
    130     # 9. Ida y vuelta del estado por la red (lo que el host le manda al que se une).
    131     local _enc; _enc="$(_state_encode)"
    132     GAME_FEN=""; GAME_CW=0; GAME_CB=0; GAME_TB=0; GAME_HIST=()
    133     _state_decode "$_enc"
    134     _assert "state payload restores white clock" 271 "$GAME_CW"
    135     _assert "state payload restores takeback flag" 1 "$GAME_TB"
    136     _assert "state payload restores history len" 2   "${#GAME_HIST[@]}"
    137     _assert "state payload restores last ply" "g1f3  ..." "${GAME_HIST[1]}"
    138 
    139     # 8b. Retroceso: tomar una "foto", aplicar una jugada, deshacerla -> todo vuelve.
    140     _play "$START_FEN"
    141     CLOCK_white=300; CLOCK_black=300
    142     HISTORY=(); HIST_FULLMOVE=0; HIST_LINE=""; UNDO_STACK=()
    143     local _fen_before; _fen_before="$(get_fen)"
    144     _snapshot_push
    145     CLOCK_white=288            # las blancas "gastaron" 12s antes de mover
    146     apply_move e2e4; record_history white e2e4
    147     do_takeback
    148     _assert "takeback restores position"   "$_fen_before" "$(get_fen)"
    149     _assert "takeback refunds white clock" 300 "$CLOCK_white"
    150     _assert "takeback clears history"      0   "${#HISTORY[@]}"
    151 
    152     # 11. Notacion SAN (move_to_san), cubriendo cada caso especial.
    153     _play "$START_FEN"
    154     _assert "san pawn push"        "e4"    "$(move_to_san e2e4)"
    155     _assert "san knight move"      "Nf3"   "$(move_to_san g1f3)"
    156     _play "rnbqkbnr/ppp1pppp/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 2"
    157     _assert "san pawn capture"     "exd5"  "$(move_to_san e4d5)"
    158     _play "r3k2r/8/8/8/8/8/8/R3K2R w KQkq - 0 1"
    159     _assert "san kingside castle"  "O-O"   "$(move_to_san e1g1)"
    160     _assert "san queenside castle" "O-O-O" "$(move_to_san e1c1)"
    161     _play "8/P7/8/4k3/8/8/8/7K w - - 0 1"
    162     _assert "san promotion"        "a8=Q"  "$(move_to_san a7a8q)"
    163     _play "7k/8/8/8/8/8/8/R6K w - - 0 1"
    164     _assert "san check suffix"     "Ra8+"  "$(move_to_san a1a8)"
    165     _play "6k1/5ppp/8/8/8/8/8/R6K w - - 0 1"
    166     _assert "san mate suffix"      "Ra8#"  "$(move_to_san a1a8)"
    167     _play "4k3/8/8/8/8/1N3N2/8/4K3 w - - 0 1"
    168     _assert "san disambiguation"   "Nbd4"  "$(move_to_san b3d4)"
    169 
    170     # 12. Ida y vuelta de PGN: importar y exportar dan la misma partida.
    171     load_pgn_string "1. e4 e5 2. Nf3 Nc6 3. Bb5 a6 4. Bxc6 dxc6"
    172     local _fen_after="$GAME_FEN"
    173     _assert "pgn import ply count"        8 "${#GAME_PGN[@]}"
    174     _assert "pgn import keeps capture SAN" "Bxc6" "${GAME_PGN[6]}"
    175     PGN_SAN=("${GAME_PGN[@]}"); PGN_STARTFEN="$START_FEN"
    176     local _pf; _pf="$(mktemp)"
    177     export_pgn "$_pf" "*"
    178     load_pgn_string "$(cat "$_pf")"; rm -f "$_pf"
    179     _assert "pgn round-trip final position" "$_fen_after" "$GAME_FEN"
    180     _assert "pgn round-trip ply count"      8 "${#GAME_PGN[@]}"
    181 
    182     # 10. Un archivo con solo un FEN se carga como partida sin reloj.
    183     local _fenfile; _fenfile="$(mktemp)"
    184     printf '%s\n' "$START_FEN" > "$_fenfile"
    185     TC_ENABLED=1
    186     load_game "$_fenfile"; rm -f "$_fenfile"
    187     _assert "bare FEN file sets position" "$START_FEN" "$GAME_FEN"
    188     _assert "bare FEN file is untimed"    0            "$TC_ENABLED"
    189 
    190     # 13. Fuente de jugadas de la IA: leer el "bestmove" (no necesita el motor) y,
    191     #     si Stockfish esta instalado, una prueba real (si no, se saltea, asi una
    192     #     copia recien bajada del proyecto sigue dando todo en verde).
    193     _assert "parse bestmove + ponder"  e2e4  "$(_parse_bestmove "bestmove e2e4 ponder e7e5")"
    194     _assert "parse bestmove promotion" e7e8q "$(_parse_bestmove "bestmove e7e8q")"
    195     if ai_available; then
    196         AI_SKILL=1; AI_ELO=""; AI_MOVETIME=200
    197         if ai_init; then
    198             _play "$START_FEN"
    199             local _aim; _aim="$(ai_move)"
    200             _assert "engine returns a legal move" yes \
    201                 "$(legal_moves | grep -qx "$_aim" && echo yes || echo no)"
    202             ai_close
    203         else
    204             echo "  -- ai: engine present but failed to start (live test skipped)"
    205         fi
    206     else
    207         echo "  -- ai: Stockfish not installed (live engine test skipped)"
    208     fi
    209 
    210     # Resumen final: cuantas pasaron y cuantas fallaron (en verde o rojo).
    211     echo
    212     local color=$'\e[32m'; (( _ST_FAIL > 0 )) && color=$'\e[31m'
    213     printf 'Self-test: \e[32m%d passed\e[0m, %s%d failed\e[0m\n' \
    214         "$_ST_PASS" "$color" "$_ST_FAIL"
    215     (( _ST_FAIL == 0 )) || return 1   # devolvemos error si fallo alguna
    216     return 0
    217 }