get-stockfish.sh (10735B)
1 #!/usr/bin/env bash 2 # ============================================================================ 3 # bin/get-stockfish.sh -- Descarga el motor de ajedrez "Stockfish". 4 # ============================================================================ 5 # 6 # QUE ES ESTE ARCHIVO: 7 # El juego entero esta escrito en bash (texto), pero la inteligencia 8 # artificial (la computadora que juega contra vos) necesita un programa 9 # externo MUY potente llamado Stockfish. Stockfish es un "motor de ajedrez": 10 # le pasas una posicion y te dice cual es la mejor jugada. 11 # 12 # No incluimos a Stockfish dentro del proyecto porque es un archivo binario 13 # pesado (mas de 100 MB) y distinto para cada tipo de procesador. En vez de 14 # eso, este script lo BAJA de internet y lo guarda en la carpeta ./engine/. 15 # 16 # Solo funciona automaticamente en computadoras x86-64 (las de escritorio y 17 # notebooks comunes con Linux). En otros procesadores (por ejemplo una 18 # Raspberry Pi o la consola uConsole, que son "aarch64") te dice como 19 # instalarlo con el gestor de paquetes del sistema. 20 # 21 # COMO SE USA: 22 # ./bin/get-stockfish.sh 23 24 # "set -euo pipefail" prende tres modos estrictos de bash. Son MUY utiles para 25 # que el script no siga adelante cuando algo sale mal: 26 # -e : si CUALQUIER comando falla, cortamos el script enseguida. 27 # -u : si usamos una variable que no existe, es un error (atrapa 28 # errores de tipeo en nombres de variables). 29 # -o pipefail : en una cadena de comandos con "|" (tuberia/pipe), si 30 # cualquiera de los comandos falla, toda la cadena se 31 # considera fallida (sin esto, bash solo mira el ultimo). 32 set -euo pipefail 33 34 # --- Averiguar en que carpeta esta este script ------------------------------ 35 # Necesitamos saber donde vive el proyecto para guardar el motor en ./engine/. 36 # Lo armamos de adentro hacia afuera (los $( ) se ejecutan primero): 37 # "$0" : la ruta con la que se llamo a este script. 38 # readlink -f "$0" : la convierte en una ruta absoluta y real (resolviendo 39 # enlaces simbolicos). 40 # dirname ... : se queda con la carpeta (saca el nombre del archivo). 41 # cd ... && pwd : entra a esa carpeta e imprime su ruta absoluta. Lo 42 # hacemos asi para tener una ruta prolija y completa. 43 # "$( ... )" se llama "sustitucion de comandos": ejecuta lo de adentro y 44 # reemplaza todo por el TEXTO que ese comando imprimio. 45 SCRIPT_DIR="$(cd "$(dirname "$(readlink -f "$0")")" && pwd)" 46 ROOT="$(dirname "$SCRIPT_DIR")" # el proyecto es la carpeta que contiene a bin/ 47 ENGINE_DIR="$ROOT/engine" # aca va a quedar guardado el motor 48 TARGET="$ENGINE_DIR/stockfish" # el archivo final del motor 49 50 # --- Datos para la descarga ------------------------------------------------- 51 # "API" es la direccion de GitHub que nos cuenta cual es la ultima version 52 # publicada de Stockfish. 53 API="https://api.github.com/repos/official-stockfish/Stockfish/releases/latest" 54 # Cual de todas las variantes bajar. Usamos la version "baseline" (basica) que 55 # anda en cualquier procesador x86-64. Una persona avanzada puede pedir una mas 56 # rapida poniendo la variable de entorno SF_ASSET antes de correr el script. 57 # "${SF_ASSET:-...}" otra vez significa "usa SF_ASSET, o este valor por defecto". 58 SF_ASSET="${SF_ASSET:-stockfish-ubuntu-x86-64.tar}" 59 60 # --- Una "funcion" auxiliar ------------------------------------------------- 61 # Una funcion es un bloque de codigo con nombre que podemos reutilizar. Esta, 62 # "_works", recibe la ruta de un programa (en "$1", el primer argumento que le 63 # pasamos) y responde si ese programa es realmente Stockfish y funciona. 64 # 65 # Como lo verifica: a todo motor de ajedrez se le habla con el protocolo "UCI". 66 # Si le mandamos la palabra "uci", un motor de verdad responde con "uciok". 67 # printf 'uci\nquit\n' : imprime dos lineas: "uci" y "quit" (\n = salto de 68 # linea). Es lo que le "escribimos" al motor. 69 # | : la tuberia conecta esa salida con la ENTRADA del 70 # programa "$1" (le mandamos esas dos lineas). 71 # 2>/dev/null : descartamos los mensajes de error (no nos importan). 72 # grep -q uciok : busca la palabra "uciok" en la respuesta. "-q" = 73 # modo silencioso: no imprime nada, solo devuelve 74 # exito (0) si la encontro. 75 # La funcion devuelve el resultado de ese grep: exito si el motor contesto bien. 76 _works() { printf 'uci\nquit\n' | "$1" 2>/dev/null | grep -q uciok; } 77 78 # --- Si ya esta instalado, no hacemos nada ---------------------------------- 79 # "[[ ... ]]" es la forma de bash de hacer una pregunta de verdadero/falso. 80 # -x "$TARGET" : pregunta si el archivo $TARGET existe y es ejecutable. 81 # El "&&" significa "Y": las dos condiciones tienen que cumplirse. 82 if [[ -x "$TARGET" ]] && _works "$TARGET"; then 83 echo "Stockfish already installed at ${TARGET}."; exit 0 84 fi 85 # Quizas ya esta instalado en el sistema (en el "PATH", la lista de carpetas 86 # donde el sistema busca programas). Si es asi, el juego lo va a encontrar solo. 87 if command -v stockfish >/dev/null 2>&1 && _works stockfish; then 88 echo "Stockfish already on your PATH ($(command -v stockfish)). The game will use it." 89 exit 0 90 fi 91 92 # --- Chequear el tipo de procesador ----------------------------------------- 93 # "uname -m" dice la arquitectura del procesador (por ejemplo "x86_64" o 94 # "aarch64"). Solo tenemos descarga automatica para x86_64. 95 arch="$(uname -m)" 96 if [[ "$arch" != x86_64 ]]; then 97 # "cat <<EOF ... EOF" es un "here-document": imprime todo el bloque de texto 98 # tal cual hasta encontrar la palabra EOF sola en una linea. ">&2" lo manda a 99 # la salida de errores. Este mensaje queda en ingles (lo lee la persona). 100 cat >&2 <<EOF 101 No official prebuilt Stockfish Linux binary for '${arch}'. 102 Install it with your package manager instead, e.g.: 103 pacman -S stockfish # Arch / Artix 104 sudo apt install stockfish # Debian / Ubuntu / Raspberry Pi OS 105 sudo dnf install stockfish # Fedora 106 The game auto-detects a 'stockfish' on your PATH. 107 EOF 108 exit 1 109 fi 110 111 # --- Verificar que tengamos las herramientas para bajar y descomprimir ------ 112 # "curl" baja archivos de internet; "tar" abre archivos comprimidos .tar. 113 command -v curl >/dev/null || { echo "curl is required." >&2; exit 1; } 114 command -v tar >/dev/null || { echo "tar is required." >&2; exit 1; } 115 116 # --- Buscar el link de descarga de la ultima version ------------------------ 117 echo "Finding the latest Stockfish release..." 118 # Le pedimos a GitHub la info de la ultima version y buscamos el link exacto. 119 # curl -fsSL "$API" : baja el texto de esa pagina (-f falla si hay error de 120 # red, -s silencioso, -S muestra errores igual, -L sigue 121 # redirecciones). 122 # grep -oE 'patron' : "-o" imprime SOLO la parte que coincide (no la linea 123 # entera); "-E" usa expresiones regulares extendidas. 124 # El patron busca una direccion https que termine en el 125 # nombre del archivo que queremos. 126 # head -n1 : nos quedamos con la primera coincidencia nomas. 127 # || true : si todo eso falla, devolvemos "exito" igual (true) para 128 # que "set -e" no corte el script aca; mas abajo 129 # chequeamos si "url" quedo vacio. 130 url="$(curl -fsSL "$API" | grep -oE "https://[^\"]+/${SF_ASSET}" | head -n1 || true)" 131 # "-n" pregunta si la variable NO esta vacia. Si quedo vacia, avisamos y salimos. 132 [[ -n "$url" ]] || { echo "Could not find asset '${SF_ASSET}' in the latest release." >&2; exit 1; } 133 134 # --- Bajar y descomprimir en una carpeta temporal --------------------------- 135 # "mktemp -d" crea una carpeta temporal con nombre unico y nos devuelve su ruta. 136 # La usamos para no ensuciar el proyecto con archivos intermedios. 137 tmp="$(mktemp -d)" 138 # "trap 'comando' EVENTO" programa un comando para que se ejecute cuando pase un 139 # evento. "EXIT" ocurre cuando el script termina (de cualquier forma). O sea: 140 # pase lo que pase, al final borramos la carpeta temporal. Es como dejar todo 141 # limpio antes de irnos. 142 trap 'rm -rf "$tmp"' EXIT 143 mkdir -p "$tmp/x" # subcarpeta donde vamos a descomprimir (mkdir -p no falla si ya existe) 144 echo "Downloading ${url##*/} ..." # "${url##*/}" deja solo el nombre del archivo (saca todo hasta la ultima /) 145 # Bajamos el .tar a la carpeta temporal. "-#" muestra una barrita de progreso. 146 curl -fL# "$url" -o "$tmp/sf.tar" 147 # Descomprimimos. "x" = extraer, "f" = desde este archivo, "-C" = hacia esta carpeta. 148 tar xf "$tmp/sf.tar" -C "$tmp/x" 149 150 # --- Encontrar el binario del motor dentro de lo descomprimido -------------- 151 # El .tar trae el motor MAS un monton de archivos (codigo fuente, documentacion). 152 # El motor es, de lejos, el archivo mas grande. Asi que buscamos el mas pesado: 153 # find "$tmp/x" -type f : lista todos los archivos (no carpetas). 154 # -printf '%s %p\n' : imprime "tamanio ruta" por cada archivo. 155 # sort -rn : ordena por numero, de mayor a menor (-r al reves, -n numerico). 156 # head -n1 : nos quedamos con el primero (el mas grande). 157 # cut -d' ' -f2- : corta usando el espacio como separador y se 158 # queda de la 2da columna en adelante (la ruta), 159 # descartando el numero de tamanio. 160 # Importante: buscamos dentro de $tmp/x (donde descomprimimos), NO en $tmp, asi 161 # el propio archivo .tar (que tambien es grande) no se cuela en la busqueda. 162 bin="$(find "$tmp/x" -type f -printf '%s %p\n' | sort -rn | head -n1 | cut -d' ' -f2-)" 163 [[ -n "$bin" ]] || { echo "Could not locate the engine in the archive." >&2; exit 1; } 164 165 # --- Instalar el motor en ./engine/stockfish -------------------------------- 166 mkdir -p "$ENGINE_DIR" # creamos la carpeta engine/ si no existe 167 cp "$bin" "$TARGET" # copiamos el binario encontrado a su lugar final 168 chmod +x "$TARGET" # le damos permiso de ejecucion (para poder correrlo) 169 170 # --- Verificar que el motor instalado realmente funciona -------------------- 171 if _works "$TARGET"; then 172 echo "Stockfish installed to ${TARGET}." 173 echo "Play the computer with: ./ches.sh ai" 174 else 175 # Algunas variantes necesitan instrucciones modernas del procesador. Si la 176 # que bajamos no arranca, sugerimos volver a la version basica. 177 echo "Installed binary did not respond to UCI — your CPU may not support this build." >&2 178 echo "Try a baseline build: SF_ASSET=stockfish-ubuntu-x86-64.tar $0" >&2 179 exit 1 180 fi