<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: El Programador Pobre</title>
    <description>The latest articles on DEV Community by El Programador Pobre (@el_programador_pobre).</description>
    <link>https://dev.to/el_programador_pobre</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3815320%2Fdb606490-c3f9-476b-8930-957294c20f6d.jpg</url>
      <title>DEV Community: El Programador Pobre</title>
      <link>https://dev.to/el_programador_pobre</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/el_programador_pobre"/>
    <language>en</language>
    <item>
      <title>De Tic-Tac-Toe a Minimax: construyendo y midiendo un motor en Go</title>
      <dc:creator>El Programador Pobre</dc:creator>
      <pubDate>Fri, 13 Mar 2026 16:05:24 +0000</pubDate>
      <link>https://dev.to/el_programador_pobre/de-tic-tac-toe-a-minimax-construyendo-y-midiendo-un-motor-en-go-1gef</link>
      <guid>https://dev.to/el_programador_pobre/de-tic-tac-toe-a-minimax-construyendo-y-midiendo-un-motor-en-go-1gef</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota editorial (2026):&lt;/strong&gt; Este artículo fue publicado originalmente en Medium en 2023. Lo he revisado corrigiendo errores técnicos, mejorando la coherencia entre narrativa y código, y añadiendo contexto sobre las limitaciones del diseño. El código del repositorio también fue actualizado.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Go es un lenguaje excelente para introducirse en ciencias de la computación. Su pequeño número de palabras reservadas, la velocidad de compilación y la claridad del sistema de tipos lo convierten en una herramienta didáctica muy efectiva. Y si además buscamos un problema entretenido para practicar, los juegos clásicos son una opción difícil de superar.&lt;/p&gt;

&lt;p&gt;En este artículo construiremos un motor para Tic-Tac-Toe (el gato, tres en raya) aplicando el algoritmo Minimax. El objetivo no es solo hacerlo funcionar, sino también entender por qué una implementación ingenua de Minimax escala peor que el resto del motor. Ahí está la parte más interesante del ejercicio.&lt;/p&gt;

&lt;p&gt;Puede encontrar el código completo en el &lt;a href="https://github.com/profe-ajedrez/catngine" rel="noopener noreferrer"&gt;repositorio de GitHub&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Qué vamos a construir
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Un módulo Go con un paquete que expone una &lt;code&gt;struct&lt;/code&gt; para almacenar el estado del juego.&lt;/li&gt;
&lt;li&gt;Métodos para registrar jugadas y consultar el estado.&lt;/li&gt;
&lt;li&gt;El algoritmo Minimax para calcular la mejor jugada posible.&lt;/li&gt;
&lt;li&gt;Tests unitarios y benchmarks para cada método.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nos centraremos en el &lt;strong&gt;motor&lt;/strong&gt;, no en la interfaz. Sin ventanas, sin gráficos: solo lógica y medición.&lt;/p&gt;

&lt;p&gt;El repositorio incluye un ejemplo ejecutable en &lt;code&gt;examples/human_vs_machine/main.go&lt;/code&gt; que permite jugar una partida completa contra el motor por consola. &lt;br&gt;
Para correrlo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;go run examples/human_vs_machine/main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Las coordenadas se ingresan como x y (columna y fila, valores 0–2).&lt;/p&gt;




&lt;h2&gt;
  
  
  El algoritmo Minimax
&lt;/h2&gt;

&lt;p&gt;Minimax es un método de decisión para juegos de dos jugadores de suma cero. La idea central es:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Elige el mejor movimiento para ti, sabiendo que tu adversario elegirá el movimiento que sea peor para ti.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Partiendo del estado actual, el algoritmo genera recursivamente todos los estados posibles del juego y les asigna una puntuación. Los nodos donde gana el jugador reciben puntuación positiva; los nodos donde gana la máquina, negativa. Los estados intermedios se evalúan propagando hacia arriba el valor del nodo hoja más favorable.&lt;/p&gt;

&lt;p&gt;Para una introducción conceptual más detallada, recomiendo &lt;a href="https://medium.com/data-science/understanding-the-minimax-algorithm-726582e4f2c6" rel="noopener noreferrer"&gt;el artículo de Dorian Lazar&lt;/a&gt; sobre el tema.&lt;/p&gt;




&lt;h2&gt;
  
  
  El proyecto
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;catngine
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;catngine
&lt;span class="nv"&gt;$ &lt;/span&gt;go mod init github.com/profe-ajedrez/catngine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Constantes y errores
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;catngine&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"errors"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c"&gt;// E representa una casilla vacía&lt;/span&gt;
    &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// P representa una casilla ocupada por el jugador humano&lt;/span&gt;
    &lt;span class="n"&gt;P&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// F representa una casilla ocupada por la máquina&lt;/span&gt;
    &lt;span class="n"&gt;F&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c"&gt;// ErrOutOfBoardBounds se devuelve al intentar acceder a una casilla fuera del tablero&lt;/span&gt;
    &lt;span class="n"&gt;ErrOutOfBoardBounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"out of board bounds"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// ErrNoEmptyCell se devuelve al intentar ocupar una casilla ya tomada&lt;/span&gt;
    &lt;span class="n"&gt;ErrNoEmptyCell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"that cell is not empty"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Definir errores con nombre propio es una forma de documentar el comportamiento esperado sin escribir un comentario adicional.&lt;/p&gt;




&lt;h2&gt;
  
  
  La estructura
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Minimax es una implementación usando el algoritmo homónimo&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Minimax&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;g&lt;/span&gt;    &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int8&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// NewMinimax inicializa y devuelve un motor listo para usar&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;NewMinimax&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Minimax&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Minimax&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El tablero se representa con un slice unidimensional de 9 posiciones. Esto es más eficiente de recorrer que una matriz 3×3, pero requiere un paso de mapeo.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mapeo de coordenadas 2D a índice 1D
&lt;/h2&gt;

&lt;p&gt;El tablero es conceptualmente una grilla 3×3 donde &lt;code&gt;x&lt;/code&gt; es la columna e &lt;code&gt;y&lt;/code&gt; es la fila:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(x=0,y=0) | (x=1,y=0) | (x=2,y=0)   ← fila 0
----------+-----------+----------
(x=0,y=1) | (x=1,y=1) | (x=2,y=1)   ← fila 1
----------+-----------+----------
(x=0,y=2) | (x=1,y=2) | (x=2,y=2)   ← fila 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La relación con el índice lineal es:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La implementación valida las coordenadas &lt;strong&gt;antes&lt;/strong&gt; de calcular el índice para evitar falsos positivos (por ejemplo, &lt;code&gt;x=-1, y=1&lt;/code&gt; daría &lt;code&gt;i=2&lt;/code&gt;, que parece válido pero no lo es):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Minimax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ErrOutOfBoardBounds&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestMinimaxMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;testCases&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;        &lt;span class="kt"&gt;int8&lt;/span&gt;
        &lt;span class="n"&gt;expectedErr&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
        &lt;span class="n"&gt;expectedIdx&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;
        &lt;span class="n"&gt;msg&lt;/span&gt;         &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;}{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ErrOutOfBoardBounds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"esperaba ErrOutOfBoardBounds para (9,9)"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ErrOutOfBoardBounds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"esperaba ErrOutOfBoardBounds para (-1,-2)"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="c"&gt;// Bug corregido: (-1,1) daba i=2 con la validación anterior&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ErrOutOfBoardBounds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"esperaba ErrOutOfBoardBounds para (-1,1)"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"esperaba índice 7 para (1,2)"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewMinimax&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;testCases&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;got&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expectedErr&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s: error recibido: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FailNow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expectedIdx&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s: índice recibido: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FailNow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-timeout&lt;/span&gt; 30s &lt;span class="nt"&gt;-run&lt;/span&gt; ^TestMinimaxMap&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benchmark
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;mapResult&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;BenchmarkMinimaxMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;bd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewMinimax&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;mapResult&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;La variable &lt;code&gt;mapResult&lt;/code&gt; se declara fuera del loop para evitar que el compilador descarte la llamada como código muerto (&lt;em&gt;dead-code elimination&lt;/em&gt;), lo que produciría tiempos artificialmente bajos.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-benchmem&lt;/span&gt; &lt;span class="nt"&gt;-count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 &lt;span class="nt"&gt;-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;^&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nt"&gt;-bench&lt;/span&gt; ^BenchmarkMinimaxMap&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resultado orientativo en una máquina de escritorio (Intel i7):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;BenchmarkMinimaxMap-16    1000000000    ~0.22 ns/op    0 B/op    0 allocs/op
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta operación es barata y no realiza asignaciones en heap. Lo importante aquí no es el número exacto, sino que sirve como referencia para comparar con operaciones más costosas más adelante.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Para los curiosos: lo que genera el compilador&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;¿Qué pasa si no se usa la variable de sink mapResult? El ensamblador &lt;br&gt;
lo muestra.&lt;/p&gt;

&lt;p&gt;En el repositorio provisto encontrará 2 benchmarks que miden lo &lt;br&gt;
mismo; &lt;code&gt;BenchmarkMinimaxMapNoSink&lt;/code&gt; y &lt;code&gt;BenchmarkMinimaxMapSink&lt;/code&gt;, uno &lt;br&gt;
usando la variable de sink y otro que no la usa.&lt;/p&gt;

&lt;p&gt;Podemos saber como se comportan con la siguiente instrucción:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-gcflags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-S"&lt;/span&gt; &lt;span class="nt"&gt;-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;^&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nt"&gt;-bench&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;^&lt;span class="nv"&gt;$ &lt;/span&gt;2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-A&lt;/span&gt; 12 &lt;span class="s2"&gt;"BenchmarkMinimaxMapNoSink&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;BenchmarkMinimaxMapSink"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;El compilador aplicó dos optimizaciones en cadena. Primero evaluó &lt;br&gt;
&lt;code&gt;m(1, 2)&lt;/code&gt; en tiempo de compilación: &lt;br&gt;
la función es pura, sus argumentos son constantes, &lt;br&gt;
el resultado siempre es &lt;code&gt;7&lt;/code&gt;. &lt;br&gt;
Luego, en la versión sin sink, descartó esa constante porque nadie la usa.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sin sink&lt;/strong&gt; — el loop no contiene ninguna llamada a &lt;code&gt;m()&lt;/code&gt;:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;XORL  CX, CX
JMP   7
INCQ  CX
CMPQ  528(AX), CX
JGT   4
RET
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;strong&gt;Con sink&lt;/strong&gt; — el compilador precalculó &lt;code&gt;m(1,2) = 7&lt;/code&gt; y solo emite &lt;br&gt;
la escritura a memoria:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;XORL  CX, CX
JMP   14
MOVB  $7, catngine.mapResult(SB)
INCQ  CX
CMPQ  528(AX), CX
JGT   4
RET
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Sin &lt;code&gt;mapResult&lt;/code&gt;, el benchmark mediría cuánto tarda un loop vacío. &lt;br&gt;
Con ella, forzamos que el resultado se materialice en memoria en cada iteración.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Registrar una jugada
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Minimax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ErrNoEmptyCell&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Mostrar el tablero
&lt;/h2&gt;

&lt;p&gt;Para cerrar la brecha entre el estado interno (valores &lt;code&gt;0&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;2&lt;/code&gt;) y la representación visual del artículo (&lt;code&gt;X&lt;/code&gt;, &lt;code&gt;O&lt;/code&gt;), &lt;code&gt;String()&lt;/code&gt; traduce con un array fijo de 3 símbolos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Minimax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"X"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"O"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;" %s | %s | %s &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;---+---+---&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt; %s | %s | %s &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;---+---+---&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt; %s | %s | %s &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
        &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un array fijo &lt;code&gt;[3]string&lt;/code&gt; es suficiente aquí: solo hay tres estados posibles y el índice coincide directamente con las constantes &lt;code&gt;E&lt;/code&gt;, &lt;code&gt;P&lt;/code&gt;, &lt;code&gt;F&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Detectar al ganador
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Minimax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Winner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;checkWin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;checkWin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Minimax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Es fuerza bruta: enumera las 8 combinaciones ganadoras. Es deliberadamente simple para mantener el artículo enfocado en Minimax. Hay formas más elegantes —bitboards, por ejemplo— pero las dejamos para otro momento.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestMinimaxWinner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewMinimax&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c"&gt;// Simulamos jugadas intercaladas&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// P en columna 0, fila 0&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// F en columna 0, fila 1&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// P en columna 1, fila 0&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// F en columna 1, fila 1&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// P en columna 2, fila 0 --&amp;gt;  completa la primera fila&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Winner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Winner: esperaba true para P, recibió false"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Winner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Winner: esperaba false para F, recibió true"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benchmark
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;winnerResult&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;BenchmarkMinimaxWinner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;bd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewMinimax&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResetTimer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;winnerResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Winner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resultado orientativo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;BenchmarkMinimaxWinner-16    ~600M ops/s    ~1.9 ns/op    0 B/op    0 allocs/op
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El costo es bajo y consistente. También realiza cero asignaciones.&lt;/p&gt;




&lt;h2&gt;
  
  
  El motor: &lt;code&gt;Evaluate()&lt;/code&gt; y &lt;code&gt;miniMax()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Evaluate()&lt;/code&gt; recorre las casillas vacías, simula cada jugada posible y llama a &lt;code&gt;miniMax()&lt;/code&gt; para obtener su puntuación. Se queda con la jugada de mayor (o menor) puntuación según el jugador evaluado.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Minimax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;miniMax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
                    &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;miniMax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;
                    &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;miniMax()&lt;/code&gt; es la función recursiva que construye el árbol de juego. Contempla tres estados terminales: victoria del jugador, victoria de la máquina, y empate. Sin el caso de empate, el algoritmo devolvería un valor arbitrario al agotar las casillas, lo que introduciría un sesgo silencioso en la evaluación.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;isDraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Minimax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;checkWin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;checkWin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;miniMax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Minimax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;checkWin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;11&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;checkWin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;11&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isDraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;mark&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;mark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;20&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;
                &lt;span class="n"&gt;m2&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;miniMax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m2&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;mark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m2&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;
                &lt;span class="n"&gt;m2&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;miniMax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;m2&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;mark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;mark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;m2&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mark&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestMinimaxEvaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;testCases&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;setup&lt;/span&gt;    &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Minimax&lt;/span&gt;
        &lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;
        &lt;span class="n"&gt;desc&lt;/span&gt;     &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;}{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"F debe bloquear la columna 0 donde P tiene dos en raya"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Minimax&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewMinimax&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// P en columna 0, fila 0  --&amp;gt; índice 0&lt;/span&gt;
                &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// F en columna 1, fila 0  --&amp;gt; índice 1&lt;/span&gt;
                &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// P en columna 0, fila 1  --&amp;gt; índice 3&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="c"&gt;// P ocupa (0,0) y (0,1): amenaza en columna 0.&lt;/span&gt;
            &lt;span class="c"&gt;// El bloqueo es (0,2): i = 3*2 + 0 = 6&lt;/span&gt;
            &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;testCases&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errorf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"caso %d (%s): esperaba índice %d, recibió %d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;got&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Benchmark — y la sorpresa
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;evalResult&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;BenchmarkMinimaxEvaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;bd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;NewMinimax&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// columna 0, fila 0&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// columna 1, fila 0&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c"&gt;// columna 0, fila 1&lt;/span&gt;

    &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResetTimer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;evalResult&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-benchmem&lt;/span&gt; &lt;span class="nt"&gt;-count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 &lt;span class="nt"&gt;-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;^&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nt"&gt;-bench&lt;/span&gt; ^BenchmarkMinimaxEvaluate&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resultado orientativo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;BenchmarkMinimaxEvaluate-16    ~70K ops/s    ~16.5 µs/op    0 B/op    0 allocs/op
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Comparemos con los benchmarks anteriores:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operación&lt;/th&gt;
&lt;th&gt;Costo aproximado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;m()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~0.22 ns/op&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Winner()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~1.9 ns/op&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Evaluate()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;~16,500 ns/op&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La diferencia es de cuatro órdenes de magnitud. No es que &lt;code&gt;Evaluate()&lt;/code&gt; sea lenta en términos absolutos, pero la escala de crecimiento sí es un problema: &lt;strong&gt;en cada llamada recursiva, &lt;code&gt;miniMax()&lt;/code&gt; recorre el slice entero buscando casillas vacías&lt;/strong&gt;. Para un tablero vacío de 9 casillas esto se traduce en miles de llamadas. A medida que el árbol crece, el costo también crece.&lt;br&gt;
El motor funciona correctamente, pero su implementación ingenua tiene un techo claro.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;En microbenchmarks tan pequeños, el compilador y el inlining pueden influir bastante en los resultados. &lt;br&gt;
Aquí me interesan más las diferencias relativas que el valor absoluto.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Qué haría distinto hoy
&lt;/h2&gt;

&lt;p&gt;Después de revisar este código tres años después, hay varias cosas que cambiaría:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unificar &lt;code&gt;Evaluate()&lt;/code&gt; y &lt;code&gt;miniMax()&lt;/code&gt;&lt;/strong&gt; en una sola función recursiva. La separación actual es un artefacto de cómo fui construyendo el código, no una decisión de diseño deliberada.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Añadir poda alfa-beta.&lt;/strong&gt; Esta es la mejora natural más importante. La poda alfa-beta elimina ramas del árbol que no pueden afectar el resultado final, reduciendo la complejidad de O(b^d) a O(b^(d/2)) en el mejor caso. Para Tic-Tac-Toe el impacto es modesto, pero el principio escala a juegos más complejos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usar una tabla de transposición.&lt;/strong&gt; Muchos estados del tablero son equivalentes por rotación o simetría. Almacenar resultados ya calculados (&lt;em&gt;memoización&lt;/em&gt;) evitaría recalcularlos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explorar bitboards.&lt;/strong&gt; Representar el estado del tablero con enteros y operaciones de bits haría tanto &lt;code&gt;checkWin()&lt;/code&gt; como la búsqueda de casillas vacías considerablemente más rápidos.&lt;/p&gt;

&lt;p&gt;Esas mejoras merecen su propio artículo. Por ahora, el objetivo era construir algo funcional, medirlo y entender por qué el diseño más simple tiene sus límites.&lt;/p&gt;


&lt;h3&gt;
  
  
  Bonus: cómo quedaría la recursión unificada
&lt;/h3&gt;

&lt;p&gt;Como adelanto, unificaría &lt;code&gt;Evaluate()&lt;/code&gt; y &lt;code&gt;miniMax()&lt;/code&gt; haciendo que la función recursiva devuelva dos valores en lugar de uno: el índice de la mejor jugada y su puntuación.&lt;br&gt;
Actualmente el diseño tiene esta separación de responsabilidades:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Evaluate()&lt;/code&gt; encuentra el índice de la mejor casilla (loop sobre casillas vacías, llama a miniMax por cada una)&lt;br&gt;
&lt;code&gt;miniMax()&lt;/code&gt;  devuelve solo una puntuación (recursión pura, no le importa el índice)&lt;/p&gt;

&lt;p&gt;El problema es que &lt;code&gt;Evaluate()&lt;/code&gt; básicamente replica el loop de &lt;code&gt;miniMax()&lt;/code&gt; en el primer nivel. Son la misma lógica duplicada para la raíz del árbol.&lt;br&gt;
La unificación consiste en darle a la función recursiva la capacidad de rastrear también el índice cuando está en profundidad 0:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// minimax devuelve el índice de la mejor casilla y su puntuación para el jugador p.&lt;/span&gt;
&lt;span class="c"&gt;// En llamadas recursivas (depth &amp;gt; 0), el índice devuelto es -1 y puede ignorarse.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;minimax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Minimax&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;checkWin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;11&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;checkWin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;11&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;isDraw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;bestIdx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bestScore&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;bestScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="m"&gt;127&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;bestScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;127&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;minimax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;P&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bestScore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;bestScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
                &lt;span class="n"&gt;bestIdx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;bestScore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;bestScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;score&lt;/span&gt;
                &lt;span class="n"&gt;bestIdx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bestIdx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bestScore&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// Evaluate es ahora una envoltura delgada sobre minimax.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;Minimax&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int8&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;minimax&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La diferencia clave es que ahora hay un solo loop, no dos. &lt;code&gt;Evaluate()&lt;/code&gt; queda como una envoltura de una línea que descarta la puntuación y expone solo el índice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resumen
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Construimos un motor de Tic-Tac-Toe en Go en la implementación &lt;code&gt;Minimax&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Representamos el tablero como un slice unidimensional con mapeo explícito de coordenadas.&lt;/li&gt;
&lt;li&gt;Validamos con tests con tabla de casos para facilitar la extensión.&lt;/li&gt;
&lt;li&gt;Medimos con benchmarks corregidos (sin dead-code elimination, con &lt;code&gt;b.ResetTimer()&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Identificamos el cuello de botella principal: la exploración exhaustiva sin poda.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;El código completo está en el &lt;a href="https://github.com/profe-ajedrez/catngine" rel="noopener noreferrer"&gt;repositorio de GitHub&lt;/a&gt;. Si encuentra algún error o tiene sugerencias, los comentarios están abiertos.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Escrito originalmente en 2023. Revisado en 2026.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>algorithms</category>
      <category>beginners</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Una idea del siglo XIX que aparece en su compilador, sus tests y el diseño de su sistema</title>
      <dc:creator>El Programador Pobre</dc:creator>
      <pubDate>Mon, 09 Mar 2026 20:21:15 +0000</pubDate>
      <link>https://dev.to/el_programador_pobre/una-idea-del-siglo-xix-que-aparece-en-su-compilador-sus-tests-y-el-diseno-de-su-sistema-2ni1</link>
      <guid>https://dev.to/el_programador_pobre/una-idea-del-siglo-xix-que-aparece-en-su-compilador-sus-tests-y-el-diseno-de-su-sistema-2ni1</guid>
      <description>&lt;h2&gt;
  
  
  Introducción
&lt;/h2&gt;

&lt;p&gt;En 1872, el matemático Felix Klein se enfrentaba a un problema que mantenía en desacuerdo a los matemáticos de su época: habían proliferado geometrías distintas, la euclidiana, la proyectiva, la hiperbólica, y nadie sabía muy bien cómo relacionarlas entre sí.&lt;br&gt;
Su propuesta, presentada en lo que hoy conocemos como el Programa de Erlangen, fue reformular la pregunta. En vez de preguntar qué objetos tiene una geometría, preguntó: ¿Qué propiedades permanecen iguales cuando se aplican sus transformaciones? Una geometría, argumentó Klein, queda definida por aquello que no cambia.&lt;br&gt;
El término invariante ya existía, acuñado por los matemáticos británicos Boole, Cayley y Sylvester en la década de 1850 para describir expresiones algebraicas que sobreviven a ciertas transformaciones de variables. Klein lo tomó prestado y lo convirtió en el principio organizador para clasificar familias enteras de geometrías, una idea que resultó influyente bien más allá de las matemáticas.&lt;br&gt;
Un siglo después, ese mismo principio apareció en tres lugares distintos de la ingeniería de software:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;En los compiladores, que detectan expresiones cuyo valor no cambia entre iteraciones de un bucle y las mueven fuera para evitar recalcularlas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;En el testing, cuando en lugar de verificar casos concretos se verifica que una propiedad se sostiene para cualquier entrada posible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;En el diseño de software, cuando se define que ciertas reglas del dominio deben ser verdaderas siempre, antes y después de cualquier operación.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Los tres usos comparten la misma pregunta de fondo: ¿qué es lo que no puede cambiar aquí? Este artículo recorre ese linaje y explora cómo responder esa pregunta con precisión es, en los tres casos, el paso intelectual más importante.&lt;/p&gt;
&lt;h2&gt;
  
  
  Origen del término
&lt;/h2&gt;

&lt;p&gt;El Programa de Erlangen parte de una pregunta concreta: ¿qué propiedades sobreviven a cada tipo de transformación geométrica? Rotar, escalar, proyectar, reflejar — cada operación transforma las figuras de algún modo.&lt;/p&gt;


  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsu83ti38uud6p1gaf32m.png" alt="Retrato de Felix Klein" width="500" height="687"&gt;
    Felix Klein (1849–1925). Fuente: &lt;a href="https://commons.wikimedia.org/wiki/File:Felix_Christian_Klein.jpg" rel="noopener noreferrer"&gt;Wikimedia Commons&lt;/a&gt; (Dominio Público).
  
  


&lt;p&gt;En &lt;em&gt;geometría euclidiana&lt;/em&gt; (la del mundo cotidiano), las principales transformaciones que podemos encontrar son &lt;strong&gt;rotaciones&lt;/strong&gt;, &lt;strong&gt;traslaciones&lt;/strong&gt; y &lt;strong&gt;reflexiones&lt;/strong&gt;. Bajo estas operaciones, las longitudes y los ángulos se preservan: un triángulo rotado sigue teniendo los mismos lados y ángulos. Esas son las &lt;strong&gt;invariantes euclidianas&lt;/strong&gt;.&lt;/p&gt;


  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2lgkjvv374z5cm7vburi.png" alt="esquema de transformaciones euclidianas" width="800" height="442"&gt;
    Figura 1: Demostración visual de las transformaciones que definen la geometría euclidiana: traslación, rotación y reflexión. Obsérvese cómo, a pesar de los cambios de posición u orientación, las propiedades fundamentales del objeto (ángulos y longitudes de lados) permanecen invariantes.
  
  


&lt;p&gt;En &lt;em&gt;geometría proyectiva&lt;/em&gt; (la que describe cómo vemos los objetos en perspectiva, donde las líneas paralelas parecen converger en el horizonte), las transformaciones son mucho más agresivas: las longitudes y los ángulos sí cambian. Sin embargo, hay una propiedad más abstracta que sobrevive: la &lt;em&gt;razón cruzada&lt;/em&gt; (&lt;em&gt;cross-ratio&lt;/em&gt;). Si tomamos cuatro puntos alineados &lt;em&gt;A&lt;/em&gt;, &lt;em&gt;B&lt;/em&gt;, &lt;em&gt;C&lt;/em&gt;, &lt;em&gt;D&lt;/em&gt;, su razón cruzada es un cociente calculado a partir de las distancias entre ellos. No importa cómo se deforme la figura por la proyección: esta razón siempre se mantiene igual. Es la &lt;strong&gt;invariante que define la geometría proyectiva&lt;/strong&gt;.&lt;/p&gt;


  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd6bmezylpkd1rje5uvpn.png" alt="esquema de invarianza de la razón cruzada" width="800" height="437"&gt;
    Figura 2: La invariante de la geometría proyectiva. La perspectiva estira y encoge las distancias entre los puntos A, B, C y D, pero su razón matemática subyacente sobrevive a la transformación.
  
  


&lt;p&gt;Lo revolucionario de Klein fue proponer que lo que define a una geometría no son sus objetos, sino sus invariantes: &lt;strong&gt;aquello que permanece sin cambiar cuando se aplican las transformaciones del grupo&lt;/strong&gt;. (Para profundizar en el Programa de Erlangen y la razón cruzada, véanse las referencias [4] y [5]; para una introducción accesible a la razón cruzada, la entrada de Wikipedia sobre &lt;em&gt;Cross-ratio&lt;/em&gt; [6] ofrece un buen punto de partida.)&lt;/p&gt;

&lt;p&gt;Es importante notar que el Programa de Erlangen no usa el término &lt;em&gt;invariante&lt;/em&gt; exactamente en el sentido de la teoría de grupos moderna. Klein hablaba de propiedades preservadas bajo grupos de simetrías, lo cual es conceptualmente cercano pero formalmente distinto. Sin embargo, la intuición central; &lt;em&gt;identificar qué no cambia para entender un sistema&lt;/em&gt;; es precisamente la que se trasladó a la informática.&lt;/p&gt;

&lt;p&gt;Un siglo después, Robert Floyd (1967, &lt;em&gt;Assigning Meanings to Programs&lt;/em&gt;) y Tony Hoare (1969, &lt;em&gt;An Axiomatic Basis for Computer Programming&lt;/em&gt;) tomaron esta intuición y la formalizaron para razonar sobre el correcto comportamiento de los programas de computadora. Hoare introdujo la &lt;em&gt;triple de Hoare&lt;/em&gt; &lt;code&gt;{P} C {Q}&lt;/code&gt;; precondición, comando, postcondición; y el concepto de &lt;em&gt;loop invariant&lt;/em&gt;; una condición que es verdadera antes del loop, se mantiene verdadera en cada iteración, y sigue siendo verdadera al terminar. La transformación de Klein se convierte aquí en cada iteración del loop; &lt;strong&gt;la invariante es lo que sobrevive a todas las iteraciones&lt;/strong&gt;.&lt;/p&gt;


  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fum79pk6q33a7102djsot.png" alt="Tony Hoare. Fotografía" width="420" height="384"&gt;
    Tony Hoare. Por David Monniaux — &lt;a href="https://commons.wikimedia.org/w/index.php?curid=44735" rel="noopener noreferrer"&gt;Trabajo propio&lt;/a&gt;, CC BY-SA 1.0
  
  


&lt;p&gt;Conviene aclarar una distinción importante: las invariantes de Floyd y Hoare tienen un propósito de &lt;strong&gt;verificación lógica&lt;/strong&gt;; demostrar que un programa hace lo que debe hacer. Las invariantes que detectan los compiladores (como veremos mas adelante) tienen un propósito de &lt;strong&gt;optimización&lt;/strong&gt;; detectar que algo no cambia para evitar recalcularlo. Ambas comparten la definición esencial &lt;em&gt;algo que no cambia entre iteraciones&lt;/em&gt;, pero se usan con fines diferentes. La conexión entre ambas es conceptual, no técnica. Klein inspiró una forma de pensar que Floyd y Hoare formalizaron para la lógica, y que los ingenieros de compiladores adoptaron independientemente para la optimización.&lt;/p&gt;

&lt;p&gt;En 1988, Bertrand Meyer llevó las invariantes de la lógica formal al diseño orientado a objetos con su libro &lt;em&gt;Object-Oriented Software Construction&lt;/em&gt; y el paradigma de &lt;em&gt;Design by Contract&lt;/em&gt; donde &lt;strong&gt;cada clase tiene invariantes que restringen su estado y deben mantenerse entre toda operación pública&lt;/strong&gt;.&lt;/p&gt;


  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc3ibwrk9pd98e2ad1o5a.png" alt="Bertrand Meyer dando una presentación. Fotografía" width="800" height="1200"&gt;
    Bertrand Meyer dando una presentación. Fotografía por Ikiwaner vía &lt;a href="https://commons.wikimedia.org/wiki/File:Bertrand_Meyer.jpg" rel="noopener noreferrer"&gt;Wikimedia Commons&lt;/a&gt; bajo licencia CC BY-SA 3.0.
  
  


&lt;p&gt;Desde Klein hasta Meyer, el hilo conductor es el mismo: identificar qué no cambia es la forma más poderosa de entender un sistema, ya sea geométrico, lógico o de software. Que más de un siglo de trabajo en matemáticas, lógica y ciencia de la computación converja en una misma intuición dice algo sobre la solidez de la idea.&lt;br&gt;
Este artículo explora los tres usos principales del concepto en ingeniería de software, cómo se relacionan entre sí, y ofrece referencias para profundizar en cada uno.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Invariantes en Compiladores
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Qué es una invariante de loop?
&lt;/h3&gt;

&lt;p&gt;Cuando un compilador analiza un bucle, busca expresiones cuyo valor no cambia entre iteraciones. Estas expresiones se denominan &lt;em&gt;loop-invariant&lt;/em&gt; y pueden moverse fuera del bucle sin alterar el comportamiento del programa. La técnica que realiza este movimiento se conoce como &lt;strong&gt;Loop-Invariant Code Motion o LICM&lt;/strong&gt;, también llamada &lt;em&gt;hoisting&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Ejemplo de LICM puro
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;           &lt;span class="c1"&gt;// y, z no cambian dentro del loop&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;El compilador detecta que &lt;code&gt;y * z&lt;/code&gt; produce el mismo resultado en cada iteración (ni &lt;code&gt;y&lt;/code&gt; ni &lt;code&gt;z&lt;/code&gt; se modifican dentro del bucle). Como la expresión es invariante al loop, la mueve fuera:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;               &lt;span class="c1"&gt;// calculado una sola vez&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El resultado es idéntico, pero en vez de 1000 multiplicaciones, se ejecuta solo una. Esto es LICM en su forma más pura: mover un cálculo invariante fuera del bucle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ejemplo de Loop Unswitching
&lt;/h3&gt;

&lt;p&gt;Existe una optimización estrechamente relacionada pero técnicamente distinta: &lt;strong&gt;Loop Unswitching&lt;/strong&gt;. Mientras LICM mueve &lt;em&gt;cálculos&lt;/em&gt; invariantes fuera del bucle, Loop Unswitching mueve &lt;em&gt;condicionales&lt;/em&gt; invariantes fuera del bucle, duplicando el cuerpo del mismo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;allowed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check_permission&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;         &lt;span class="c1"&gt;// "allowed" no cambia dentro del loop&lt;/span&gt;
        &lt;span class="n"&gt;do_something&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;do_something_else&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El compilador detecta que &lt;code&gt;allowed&lt;/code&gt; es invariante al loop y transforma el código en:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;allowed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;check_permission&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;do_something&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;do_something_else&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ambas técnicas, LICM y Loop Unswitching, están impulsadas por el mismo análisis: &lt;strong&gt;detectar qué es invariante dentro del loop&lt;/strong&gt;. La diferencia es qué hacen con esa información. LICM mueve expresiones; Loop Unswitching mueve condiciones y duplica el loop. En la literatura de compiladores se tratan como optimizaciones distintas (véanse las referencias [7] y [10]), pero comparten la misma raíz: el concepto de invariante.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsrpiqigpm5qjiv410ot.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsrpiqigpm5qjiv410ot.png" alt="eSQUEMA DE lOOP uNSWITCHING" width="800" height="437"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
    Figura 3: Optimización de Loop Unswitching. El compilador detecta una condición invariante dentro de un bucle y lo divide en dos bucles separados para evitar evaluaciones redundantes en cada iteración.&lt;br&gt;
  
  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Una precisión técnica importante:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Para ser formalmente rigurosos, no es la &lt;em&gt;condición&lt;/em&gt; en abstracto la que debe ser invariante, sino &lt;strong&gt;el valor de la expresión&lt;/strong&gt; usada en la rama respecto del bucle. &lt;/p&gt;

&lt;p&gt;Además, el compilador no aplica esta transformación siempre, sino solo cuando determina que es &lt;strong&gt;seguro y rentable&lt;/strong&gt;, porque duplica código y puede aumentar el tamaño del binario. &lt;/p&gt;

&lt;p&gt;Finalmente, siempre debe preservarse la semántica estricta del programa: si el valor de la condición puede cambiar por efectos laterales, &lt;em&gt;aliasing&lt;/em&gt; de punteros, memoria volátil o excepciones, el compilador no podrá aplicar esta optimización tan libremente.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Cómo detecta el compilador una invariante?
&lt;/h3&gt;

&lt;p&gt;El compilador utiliza un análisis de &lt;em&gt;definiciones alcanzables&lt;/em&gt;; examina todas las asignaciones que podrían llegar a los operandos de una expresión. Si todas esas asignaciones están fuera del loop, la expresión es invariante y puede moverse. Este análisis es parte de las técnicas de &lt;em&gt;análisis de flujo&lt;/em&gt; de datos que se estudian en teoría de compiladores.&lt;/p&gt;

&lt;h3&gt;
  
  
  Límites de la optimización
&lt;/h3&gt;

&lt;p&gt;El compilador solo puede mover código que pueda demostrar como invariante. Hay varios escenarios donde no puede:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Variables globales y punteros:&lt;/strong&gt; Si la variable es global o se accede a través de un puntero, el compilador no puede garantizar que una llamada a función dentro del loop no la modifique. Por precaución, se abstiene de optimizar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La palabra clave &lt;code&gt;volatile&lt;/code&gt;:&lt;/strong&gt; En algunos lenguajes, marcar una variable como &lt;code&gt;volatile&lt;/code&gt; le dice al compilador: "no asumas que este valor es estable; léelo de memoria cada vez". Esto es necesario en dos contextos principales. En &lt;strong&gt;programación de hardware&lt;/strong&gt;, un registro mapeado en memoria puede cambiar su valor por acción del dispositivo, no del programa, y la lectura debe ocurrir en cada iteración para reflejar el estado real del hardware. En programación concurrente (antes de los modelos de memoria modernos como &lt;code&gt;std::atomic&lt;/code&gt; en C++11, por ejemplo), &lt;code&gt;volatile&lt;/code&gt; se usaba como un mecanismo para señalizar entre hilos. Hoy se considera insuficiente para este propósito y &lt;code&gt;volatile&lt;/code&gt; se reserva casi exclusivamente para I/O mapeado en memoria. Para concurrencia se usan atómicos y barreras de memoria.&lt;/p&gt;

&lt;h3&gt;
  
  
  Herramientas para observar LICM
&lt;/h3&gt;

&lt;p&gt;En C se puede ver el código máquina generado con &lt;code&gt;gcc -O2 -S archivo.c&lt;/code&gt;. Lo que se espera ver es si la instrucción de multiplicación o la operación invariante aparece antes de la etiqueta del loop en el ensamblador generado, en lugar de aparecer dentro del cuerpo del loop. Por ejemplo, si &lt;code&gt;y * z&lt;/code&gt; era invariante, verás una instrucción &lt;code&gt;imull&lt;/code&gt; o &lt;code&gt;mulsd&lt;/code&gt; ejecutándose una sola vez antes del salto condicional que marca el inicio de las iteraciones, en vez de repetirse dentro del bloque que se ejecuta en cada iteración.&lt;/p&gt;

&lt;p&gt;Ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.c&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obtenemos el código ensamblador con &lt;code&gt;gcc -02 -S main.c&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcc &lt;span class="nt"&gt;-O2&lt;/span&gt; &lt;span class="nt"&gt;-S&lt;/span&gt; main.c
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;main.s
... 
fill:
.LFB11:
    .cfi_startproc
    testl   %ecx, %ecx        &lt;span class="p"&gt;;&lt;/span&gt; ¿n &amp;lt;&lt;span class="o"&gt;=&lt;/span&gt; 0?
    jle     .L1               &lt;span class="p"&gt;;&lt;/span&gt; si sí, retorna
    imull   %edx, %esi        &lt;span class="p"&gt;;&lt;/span&gt; y &lt;span class="k"&gt;*&lt;/span&gt; z  ← ¡ANTES del loop!
    addl    %esi, %ecx        &lt;span class="p"&gt;;&lt;/span&gt; ecx &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;y&lt;span class="k"&gt;*&lt;/span&gt;z&lt;span class="o"&gt;)&lt;/span&gt; + n  &lt;span class="o"&gt;(&lt;/span&gt;límite del loop&lt;span class="o"&gt;)&lt;/span&gt;
    .p2align 4
    .p2align 4
    .p2align 3
.L3:
    movl    %esi, &lt;span class="o"&gt;(&lt;/span&gt;%rdi&lt;span class="o"&gt;)&lt;/span&gt;      &lt;span class="p"&gt;;&lt;/span&gt; escribe el valor actual
    addl    &lt;span class="nv"&gt;$1&lt;/span&gt;, %esi          &lt;span class="p"&gt;;&lt;/span&gt; incrementa
    addq    &lt;span class="nv"&gt;$4&lt;/span&gt;, %rdi          &lt;span class="p"&gt;;&lt;/span&gt; avanza el puntero
    cmpl    %ecx, %esi        &lt;span class="p"&gt;;&lt;/span&gt; ¿llegó al límite?
    jne .L3
... 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En Go, el flag &lt;code&gt;go build -gcflags="-m -S"&lt;/code&gt; muestra las decisiones del compilador. Los mensajes de diagnóstico indicarán qué expresiones fueron movidas fuera del loop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="s"&gt;"fmt"&lt;/span&gt;

&lt;span class="c"&gt;//go:noinline&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;
        &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;make&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;999&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aplicamos &lt;code&gt;go build -gcflags="-S" main.go&lt;/code&gt;, y mostrará mucha salida, perol o que nos compete está al principio.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    0x0009 00009 (/home/jacobopus/ejemploc/main.go:6)   PCDATA  $3, $1
    0x0009 00009 (/home/jacobopus/ejemploc/main.go:7)   XORL    CX, CX          ; i = 0
    0x000b 00011 (/home/jacobopus/ejemploc/main.go:7)   JMP 23                  ; ir a comparar
    0x000d 00013 (/home/jacobopus/ejemploc/main.go:9)   MOVQ    SI, (AX)(CX*8)  ; a[i] = resultado
    0x0011 00017 (/home/jacobopus/ejemploc/main.go:7)   INCQ    CX              ; i++
    0x0014 00020 (/home/jacobopus/ejemploc/main.go:8)   MOVQ    DX, SI          ; restaura z en SI
    0x0017 00023 (/home/jacobopus/ejemploc/main.go:7)   CMPQ    R8, CX          ; i &amp;lt; n?
    0x001a 00026 (/home/jacobopus/ejemploc/main.go:7)   JLE 45              
    0x001c 00028 (/home/jacobopus/ejemploc/main.go:8)   MOVQ    SI, DX          ; guarda z
    0x001f 00031 (/home/jacobopus/ejemploc/main.go:8)   IMULQ   DI, SI      ; y * z  DENTRO DEL LOOP
    0x0023 00035 (/home/jacobopus/ejemploc/main.go:9)   ADDQ    CX, SI          ; + i
    0x0026 00038 (/home/jacobopus/ejemploc/main.go:9)   CMPQ    BX, CX 
    0x0029 00041 (/home/jacobopus/ejemploc/main.go:9)   JHI 13
    0x002b 00043 (/home/jacobopus/ejemploc/main.go:9)   JMP 47
    0x002d 00045 (/home/jacobopus/ejemploc/main.go:11)  POPQ    BP
    0x002e 00046 (/home/jacobopus/ejemploc/main.go:11)  RET

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;¡El compilador go no aplicó &lt;em&gt;LICM&lt;/em&gt; y no movió la multiplicación &lt;code&gt;y * z&lt;/code&gt; fuera del loop! !Evalua la expresión en cada una de las 1000 iteraciones!&lt;/p&gt;

&lt;p&gt;Comparado con GCC, que sacó la multiplicación fuera del loop y además aplicó otras optmizaciones, el compilador de Go no lo hizo porque &lt;strong&gt;prioriza compilación rápida sobre optimización máxima&lt;/strong&gt;. Es una decisión de diseño deliberada del equipo de Go.&lt;/p&gt;

&lt;p&gt;Para el programador Go esto significa que extraer manualmente &lt;code&gt;x := y *z&lt;/code&gt; fuera del loop si tiene impacto real, mientras que con el compilador gcc en C, con &lt;code&gt;-02&lt;/code&gt; es redundante por que el compilador lo hará por nosotros.&lt;/p&gt;

&lt;h3&gt;
  
  
  Invariantes declaradas por el programador: &lt;code&gt;const&lt;/code&gt; y &lt;code&gt;constexpr&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;En lenguajes compilados con tipado estático, el programador puede declarar explícitamente que un valor es invariante, ayudando tanto al compilador como al lector del código.&lt;/p&gt;

&lt;p&gt;En C++, &lt;code&gt;const&lt;/code&gt; indica que un valor no cambiará después de su inicialización, lo que permite al compilador optimizar con más confianza. Más poderoso aún es &lt;code&gt;constexpr&lt;/code&gt;, que le dice al compilador: "este valor se puede calcular completamente en tiempo de compilación; evalúalo ahora y no en tiempo de ejecución". Un &lt;code&gt;constexpr int MAX_ITEMS = 100;&lt;/code&gt; no es una variable que se lee de memoria: es una constante embebida directamente en el código máquina.&lt;/p&gt;

&lt;p&gt;En Go, aunque no tiene &lt;code&gt;constexpr&lt;/code&gt;, su fuerte sistema de tipado estático actúa como una forma de verificación de invariantes antes de la ejecución. Si una función espera un &lt;code&gt;int&lt;/code&gt; y recibes un &lt;code&gt;string&lt;/code&gt;, el compilador rechaza el programa antes de ejecutarlo. El sistema de tipos es, en cierto sentido, un verificador de invariantes de tipo.&lt;/p&gt;

&lt;p&gt;Estas declaraciones explícitas crean un puente entre &lt;strong&gt;las invariantes de compilador&lt;/strong&gt; y &lt;strong&gt;las invariantes de diseño&lt;/strong&gt;. El programador está expresando contratos sobre qué puede y qué no puede cambiar, y el compilador los hace cumplir.&lt;/p&gt;

&lt;h3&gt;
  
  
  ¿Qué pasa en lenguajes interpretados y con máquina virtual?
&lt;/h3&gt;

&lt;p&gt;No todos los lenguajes se benefician de LICM. La situación varía dependiendo de si el &lt;em&gt;runtime&lt;/em&gt; (el programa que ejecuta tu código) incluye un compilador &lt;strong&gt;JIT&lt;/strong&gt; (&lt;em&gt;Just-In-Time&lt;/em&gt;: un compilador que genera código máquina mientras el programa se ejecuta, optimizando las partes más usadas) y qué tan sofisticado es.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tabla comparativa
&lt;/h3&gt;

&lt;p&gt;La columna &lt;em&gt;LICM&lt;/em&gt; indica si el runtime es capaz de detectar y mover código invariante fuera de los loops. &lt;em&gt;Sí&lt;/em&gt; no significa que ocurra siempre; en compiladores ahead-of-time depende del nivel de optimización, y en JITs solo aplica a código caliente o que el runtime ha detectado como ejecutados con suficiente frecuencia para justificar su compilación a código máquina. &lt;br&gt;
Las notas aclaran las condiciones específicas de cada caso.&lt;/p&gt;
&lt;h4&gt;
  
  
  Compilados ahead-of-time
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;LICM&lt;/th&gt;
&lt;th&gt;Notas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;C/C++ (gcc, clang)&lt;/td&gt;
&lt;td&gt;Sí&lt;/td&gt;
&lt;td&gt;Requiere &lt;code&gt;-O2&lt;/code&gt; o superior; no habilitado por defecto&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;Limitado&lt;/td&gt;
&lt;td&gt;Habilitado por defecto, pero poco agresivo; como vimos en el ejemplo, puede no aplicarlo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h4&gt;
  
  
  JIT maduro
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;LICM&lt;/th&gt;
&lt;th&gt;Notas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Java (HotSpot C2)&lt;/td&gt;
&lt;td&gt;Sí, en hot paths&lt;/td&gt;
&lt;td&gt;Solo aplica a métodos que alcanzan el compilador optimizador (C2); el código que no alcanza el umbral de calor permanece en el intérprete sin optimizar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JavaScript (V8 TurboFan)&lt;/td&gt;
&lt;td&gt;Sí, en hot paths&lt;/td&gt;
&lt;td&gt;LICM explícito como paso de optimización en TurboFan; puede deoptimizar y volver al intérprete si una suposición de tipo falla&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h4&gt;
  
  
  JIT limitado o sin JIT
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;LICM&lt;/th&gt;
&lt;th&gt;Notas&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Python (PyPy)&lt;/td&gt;
&lt;td&gt;Indirecto&lt;/td&gt;
&lt;td&gt;El tracing JIT puede lograr un efecto similar a LICM como consecuencia del tracing, pero no lo aplica como paso explícito ni lo garantiza&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ruby 3.1+ (YJIT)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Optimiza despacho dinámico de métodos, no loops clásicos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ruby 2.6 (MJIT)&lt;/td&gt;
&lt;td&gt;Indirecto&lt;/td&gt;
&lt;td&gt;Compila métodos calientes a C y delega a GCC/Clang, que pueden aplicar LICM; el beneficio es del compilador C subyacente, no de MJIT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ruby 2.0 (MRI puro)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Intérprete puro de bytecode; sin optimizaciones de loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python (CPython ≤ 3.13)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;JIT experimental basado en copy-and-patch; sin LICM&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  De la optimización a la verificación
&lt;/h3&gt;

&lt;p&gt;Mientras que en compiladores las invariantes se usan para &lt;strong&gt;optimizar&lt;/strong&gt;; si algo no cambia, no tiene sentido recalcularlo; en testing las invariantes sirven para &lt;strong&gt;verificar&lt;/strong&gt;. Si definimos que algo nunca debe cambiar, podemos escribir pruebas que lo confirmen. El mismo concepto, aplicado con un propósito diferente.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Invariantes en Testing
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ¿Qué es una invariante en el contexto de tests?
&lt;/h3&gt;

&lt;p&gt;En testing, una invariante es una &lt;strong&gt;condición que debe sostenerse siempre&lt;/strong&gt;, sin importar los datos de entrada, el orden de ejecución, o las condiciones externas. A diferencia de un test unitario tradicional que verifica un caso específico, e.g. &lt;em&gt;si la entrada es 3, la salida debe ser 9&lt;/em&gt;; un test de invariante verifica una propiedad universal; e.g. &lt;em&gt;Para cualquier entrada n ≥ 0, la salida debe ser ≥ 0&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Dos enfoques complementarios
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Tests basados en ejemplos (TDD/BDD)&lt;/strong&gt; se enfocan en &lt;em&gt;lo que cambia&lt;/em&gt;: dado un estado X, cuando ocurre Y, el resultado es Z. Son explícitos y fáciles de leer, pero solo cubren los casos que el programador imaginó.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tests basados en propiedades (Property-Based Testing)&lt;/strong&gt; se concentran en &lt;em&gt;lo que no cambia&lt;/em&gt;: las invariantes del sistema. El framework genera cientos o miles de entradas aleatorias y verifica que las propiedades se mantengan para todas ellas, descubriendo así casos borde que el programador nunca habría anticipado.&lt;/p&gt;
&lt;h3&gt;
  
  
  Property-Based Testing: QuickCheck y derivados
&lt;/h3&gt;

&lt;p&gt;El pionero es &lt;strong&gt;QuickCheck&lt;/strong&gt;, creado en 1999 para Haskell por Koen Claessen y John Hughes. Su funcionamiento interno es revelador:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generación:&lt;/strong&gt; El programador define una propiedad (invariante) y el tipo de datos que acepta. QuickCheck genera automáticamente cientos de valores aleatorios para ese tipo: enteros positivos, negativos, cero, extremos del rango, strings vacíos, strings con caracteres especiales, listas de longitud variable, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verificación:&lt;/strong&gt; Para cada valor generado, ejecuta la función bajo prueba y verifica que la propiedad se cumpla.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Shrinking:&lt;/strong&gt; Si encuentra un caso que falla, no se detiene ahí. Aplica &lt;em&gt;shrinking&lt;/em&gt; (reducción): busca automáticamente el contraejemplo más pequeño posible que todavía reproduce la falla. Si el test falló con una lista de 47 elementos, QuickCheck prueba con sublistas cada vez más cortas hasta encontrar la mínima que causa el error. Esto facilita enormemente el diagnóstico.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;La potencia del enfoque está en que los generadores aleatorios producen combinaciones de entrada que ningún programador habría escrito manualmente. Bugs que dependen de interacciones sutiles entre valores, un overflow en un caso borde, un off-by-one cuando la lista tiene exactamente un elemento, una condición de carrera con un timing específico; aparecen naturalmente cuando se prueban miles de combinaciones.&lt;/p&gt;

&lt;p&gt;Hoy existen implementaciones en la mayoría de lenguajes: Hypothesis (Python), JSVerify (JavaScript), QuickCheck (Rust), junit-quickcheck (Java), Rantly (Ruby), entre otros.&lt;/p&gt;
&lt;h3&gt;
  
  
  Ejemplo: Test de invariante de concurrencia
&lt;/h3&gt;

&lt;p&gt;Supongamos un sistema de asignación de folios únicos de documentos donde la invariante es &lt;em&gt;no pueden existir dos documentos con el mismo folio para un mismo tipo de documento&lt;/em&gt;. Un test de concurrencia podría verificar esta invariante lanzando múltiples hilos simultáneos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Invariante: todos los folios asignados son únicos&lt;/span&gt;
&lt;span class="n"&gt;folios&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resultados_de_20_hilos_simultaneos&lt;/span&gt;
&lt;span class="n"&gt;duplicados&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;folios&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group_by&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;duplicados&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Se asignaron folios duplicados"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No importa cuántos hilos corran, en qué orden terminen, ni cuántas veces se ejecute el test. Si la invariante se rompe alguna vez, hay un bug en el sistema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Más ejemplos de invariantes en testing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Integridad de datos en bases de datos, ej. transferencia entre cuentas&lt;/strong&gt; Después de cualquier operación de transferencia entre cuentas, la suma total de saldos en el sistema debe ser la misma que antes de la transferencia. Este test puede ejecutarse con montos aleatorios, entre cuentas aleatorias, con concurrencia: la invariante debe sostenerse siempre.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Invariantes de serialización, ej. codificar un objeto a json:&lt;/strong&gt; Si se serializa un objeto a JSON y se deserializa de vuelta, el resultado debe ser idéntico al original. Esta propiedad se puede verificar generando miles de objetos aleatorios con campos de todos los tipos y verificando su igualdad semántica, que los datos contenidos sean los mismos antes y después de serilizar/deserializar. Bugs como la pérdida de precisión en floats, el manejo incorrecto de zonas horarias en fechas, o la codificación errónea de caracteres Unicode aparecen rápidamente.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consistencia en sistemas distribuidos:&lt;/strong&gt; En un sistema con réplicas, después de propagar una escritura y esperar el tiempo de convergencia, todas las réplicas deben devolver el mismo valor para la misma clave. El test genera secuencias aleatorias de escrituras y lecturas, con delays variables, y verifica que la invariante de consistencia eventual se mantenga.&lt;/p&gt;

&lt;h3&gt;
  
  
  De la verificación a la especificación
&lt;/h3&gt;

&lt;p&gt;Los tests basados en propiedades no solo verifican invariantes, sino que también las documentan. Cuando lees &lt;code&gt;assert todos_los_folios_son_unicos&lt;/code&gt;, estás leyendo una especificación del sistema. Esto conecta directamente con el siguiente nivel: las invariantes como herramienta de diseño. &lt;strong&gt;Lo que en testing es una aserción, en diseño es un contrato&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Invariantes en Diseño de Software
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ¿Qué es una invariante de clase?
&lt;/h3&gt;

&lt;p&gt;En diseño orientado a objetos, una &lt;em&gt;invariante de clase&lt;/em&gt; es una restricción que toda instancia de la clase debe satisfacer en todo momento observable; después de la construcción, antes de la destrucción, y antes y después de cada método público. Los métodos pueden romper temporalmente la invariante durante su ejecución, pero deben restaurarla antes de retornar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design by Contract
&lt;/h3&gt;

&lt;p&gt;El concepto fue formalizado por Bertrand Meyer en 1988 y se conoce como &lt;strong&gt;Design by Contract (DbC)&lt;/strong&gt;. Un contrato de software define tres tipos de aserciones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Precondiciones:&lt;/strong&gt; Condiciones que el cliente debe cumplir antes de llamar a un método. Si no se cumplen, el bug está en el cliente.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Postcondiciones:&lt;/strong&gt; Condiciones que el método garantiza al terminar. Si no se cumplen, el bug está en el método.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Invariantes de clase:&lt;/strong&gt; Condiciones que deben ser verdaderas siempre que el objeto sea accesible. Actúan como una cláusula general que se aplica a todos los contratos de la clase.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Ejemplo: Invariantes en un sistema de folios
&lt;/h3&gt;

&lt;p&gt;Considere un sistema de facturación electrónica con las siguientes invariantes de negocio:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Un folio asignado nunca se reasigna  &lt;strong&gt;invariante de unicidad&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Un folio emitido es siempre mayor al anterior  &lt;strong&gt;invariante de monotonía&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Un folio siempre pertenece a un rango autorizado y vigente, emitido por la autoridad tributaria &lt;strong&gt;invariante de rango&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cada una se implementa con un mecanismo diferente: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;La unicidad con una &lt;code&gt;PRIMARY KEY&lt;/code&gt; en la base de datos.
&lt;/li&gt;
&lt;li&gt;La monotonía con &lt;code&gt;GREATEST(candidato, MAX(folio_anterior) + 1)&lt;/code&gt; en la consulta.
&lt;/li&gt;
&lt;li&gt;La validez de rango con un &lt;code&gt;EXISTS&lt;/code&gt; contra los rangos autorizados.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Juntas, estas invariantes definen qué significa que el sistema esté en un &lt;strong&gt;estado correcto&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  El problema de la monotonía atómica
&lt;/h3&gt;

&lt;p&gt;El ejemplo de monotonía (&lt;code&gt;GREATEST(candidato, MAX(folio_anterior) + 1)&lt;/code&gt;) toca tangencialmente un problema difícil: ¿cómo garantizar esta invariante cuando múltiples procesos solicitan folios simultáneamente sin generar bloqueos masivos?&lt;/p&gt;

&lt;p&gt;Una alternativa elegante es usar objetos nativos del motor de base de datos diseñados para este propósito. En PostgreSQL y Oracle, un &lt;code&gt;SEQUENCE&lt;/code&gt; es un generador de números que garantiza monotonía y unicidad de forma atómica a nivel del motor, sin bloqueos sobre tablas de datos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;SEQUENCE&lt;/span&gt; &lt;span class="n"&gt;folio_seq&lt;/span&gt; &lt;span class="k"&gt;START&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;INCREMENT&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Cada llamada a NEXTVAL retorna un número único y creciente,&lt;/span&gt;
&lt;span class="c1"&gt;-- incluso con cientos de conexiones simultáneas.&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;NEXTVAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'folio_seq'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El &lt;code&gt;SEQUENCE&lt;/code&gt; abstrae la invariante de monotonía y la garantiza a nivel de motor, liberando a la capa de aplicación de esa responsabilidad. En bases de datos que no soportan &lt;code&gt;SEQUENCE&lt;/code&gt; (como versiones antiguas de MySQL/MariaDB), la alternativa es usar una tabla auxiliar con &lt;code&gt;UPDATE&lt;/code&gt; atómico o &lt;code&gt;FOR UPDATE&lt;/code&gt; para serializar el acceso, lo cual funciona pero introduce contención bajo alta concurrencia.&lt;/p&gt;

&lt;p&gt;La elección entre implementar la invariante en la aplicación, en una constraint de la base de datos, o en un objeto nativo del motor es una decisión de diseño que depende del volumen de concurrencia y las capacidades del motor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Más ejemplos: invariantes en diferentes contextos
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bases de datos:&lt;/strong&gt; Una cuenta bancaria tiene la invariante de que su saldo nunca puede ser negativo (o al menos, nunca inferior a su línea de crédito). Esta invariante se puede implementar con un &lt;code&gt;CHECK&lt;/code&gt; constraint a nivel de tabla (&lt;code&gt;CHECK (saldo &amp;gt;= -linea_credito)&lt;/code&gt;), o con lógica en la capa de aplicación. En un sistema contable, la invariante fundamental es que la suma de débitos debe ser igual a la suma de créditos. Si se rompe, el sistema está corrupto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Interfaces de usuario:&lt;/strong&gt; Un formulario de pago tiene la invariante de que el botón &lt;em&gt;Confirmar&lt;/em&gt; solo está habilitado cuando todos los campos obligatorios están completos y válidos. Si el usuario puede presionar &lt;em&gt;Confirmar&lt;/em&gt; con datos incompletos, la invariante se rompió. En una aplicación con navegación por pestañas, una invariante podría ser que exactamente una pestaña está activa en todo momento, ni cero ni más de una.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sistemas distribuidos:&lt;/strong&gt; En un sistema de caché distribuido, una invariante común es la de &lt;em&gt;coherencia eventual&lt;/em&gt;. Si no se realizan nuevas escrituras, eventualmente todas las réplicas deben devolver el mismo valor. Otra invariante clásica es la de &lt;em&gt;orden causal&lt;/em&gt;; si el evento A causó el evento B, entonces todo observador que vea B también debe haber visto A. La violación de estas invariantes produce bugs extremadamente difíciles de diagnosticar porque solo aparecen bajo condiciones específicas de latencia y concurrencia.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Estructuras de datos:&lt;/strong&gt; Un árbol binario de búsqueda tiene la invariante de que para todo nodo, los valores del subárbol izquierdo son menores y los del subárbol derecho son mayores. Un heap tiene la invariante de que el valor de cada nodo es menor (o mayor) que el de sus hijos. Si una operación rompe la invariante, la estructura deja de funcionar correctamente: las búsquedas no encuentran elementos que existen, o las prioridades se desordenan.&lt;/p&gt;

&lt;h3&gt;
  
  
  Domain-Driven Design (DDD) e invariantes
&lt;/h3&gt;

&lt;p&gt;En DDD, las invariantes ocupan una posición central. Eric Evans, en su libro &lt;em&gt;Domain-Driven Design&lt;/em&gt; (2003), recomienda que los objetos del dominio nunca estén en un estado inválido: las fábricas deben crear instancias válidas, y cada operación debe preservar las invariantes. Evans introduce el concepto de &lt;strong&gt;agregado&lt;/strong&gt;, un grupo de objetos relacionados que se tratan como una unidad para efectos de cambios de datos, con un objeto raíz que es responsable de mantener las invariantes del grupo completo. Las invariantes del agregado son las que determinan sus límites.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lenguajes con soporte nativo
&lt;/h3&gt;

&lt;p&gt;Algunos lenguajes soportan invariantes de forma nativa: Eiffel (el lenguaje diseñado por Meyer, construido alrededor de DbC), Ada (con &lt;code&gt;Type_Invariant&lt;/code&gt;), D (con bloques &lt;code&gt;invariant&lt;/code&gt;), y Dafny (con verificación formal que demuestra matemáticamente que las invariantes se cumplen antes de compilar).&lt;/p&gt;

&lt;p&gt;En la práctica, estos lenguajes son minoritarios en el desarrollo comercial moderno. Eiffel es usado principalmente en contextos académicos y en sistemas críticos (aeronáutica, finanzas). Ada tiene una presencia fuerte en sistemas embebidos y defensa. Dafny es una herramienta de investigación de Microsoft. La mayoría de los desarrolladores trabajan en lenguajes sin soporte nativo para contratos — Java, Python, Ruby, JavaScript — y recurren a soluciones externas: JML (Java Modeling Language), Contracts for Java (de Google), icontract (Python), o simplemente asserts manuales. Algunos frameworks como Spring (Java) y Rails (Ruby) implementan validaciones declarativas que cumplen un rol similar al de las invariantes de clase, aunque sin la formalidad de DbC.&lt;/p&gt;

&lt;p&gt;El hecho de que la mayoría de los lenguajes populares no soporten contratos nativamente no significa que las invariantes no importen. Al contrario, hace más importante que el programador las identifique y las implemente conscientemente, ya sea con asserts, tests, constraints de base de datos, o el sistema de tipos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Invariantes y principios de diseño
&lt;/h3&gt;

&lt;p&gt;Las invariantes no son un concepto aislado; se conectan naturalmente con otras prácticas de ingeniería de software.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Principios SOLID:&lt;/strong&gt; El principio de sustitución de Liskov (LSP) establece que las subclases deben poder sustituir a sus clases base sin romper el programa. En términos de contratos, esto significa que una subclase puede debilitar precondiciones y fortalecer postcondiciones e invariantes, pero nunca al revés. Si una subclase rompe una invariante de su clase padre, viola LSP. El principio de responsabilidad única (SRP) también se relaciona, una clase con una sola responsabilidad tiene invariantes más simples y fáciles de mantener.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refactorización:&lt;/strong&gt; Cuando refactoriza código, las invariantes son su red de seguridad. Si conoce las invariantes del sistema, puede cambiar la implementación interna con confianza mientras las invariantes se mantengan. Martin Fowler, en &lt;em&gt;Refactoring&lt;/em&gt; (1999), argumenta que los tests deben capturar el comportamiento observable del sistema, y ese comportamiento observable son, en esencia, las invariantes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Patrones de diseño:&lt;/strong&gt; Muchos patrones existen específicamente para proteger invariantes. El patrón &lt;em&gt;Builder&lt;/em&gt; garantiza que un objeto complejo se construya en un estado válido (&lt;em&gt;invariante de construcción&lt;/em&gt;). El patrón &lt;em&gt;Strategy&lt;/em&gt; permite cambiar el comportamiento sin romper la invariante de la interfaz. El patrón &lt;em&gt;State&lt;/em&gt; asegura que las transiciones de estado solo ocurran por caminos válidos, manteniendo la invariante subyacente.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conectando los dominios. Compilador, Testing, Diseño.
&lt;/h2&gt;

&lt;p&gt;Los tres usos del término comparten la misma esencia, aplicada en niveles de abstracción diferentes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;El compilador&lt;/strong&gt; detecta que un valor es invariante al loop, lo mueve fuera para optimizar.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;El test&lt;/strong&gt; verifica que una propiedad del sistema es invariante, la afirma para detectar bugs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;El diseño&lt;/strong&gt; define que una regla de negocio debe ser invariante, la implementa con constraints para garantizar correctitud.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En los tres casos, &lt;strong&gt;identificar la invariante es el paso intelectual más importante&lt;/strong&gt;. Una vez identificada, la implementación (mover código, escribir un assert, agregar un constraint) es la consecuencia natural.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reflexión. Invariantes y Calidad del Software
&lt;/h2&gt;

&lt;p&gt;Identificar invariantes no es solo un ejercicio académico. Es una de las herramientas más poderosas para construir software que funcione correctamente a lo largo del tiempo. Un CHECK (saldo &amp;gt;= 0) en la base de datos comunica una regla de negocio de forma más confiable que cualquier documento de especificación, porque se ejecuta en cada operación. Un assert todos_los_folios_son_unicos se ejecuta en cada build y alerta inmediatamente si alguien introduce un cambio que rompe la invariante. En proyectos donde los desarrolladores rotan frecuentemente, estas invariantes explícitas son radicalmente más útiles que el conocimiento tribal que se pierde cuando alguien deja el equipo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Las invariantes como guía de refactorización
&lt;/h3&gt;

&lt;p&gt;Cuando necesita cambiar código que no escribió, la primera pregunta debería ser: &lt;em&gt;¿Cuáles son las invariantes de este sistema?&lt;/em&gt; Si son conocidas, puede cambiar la implementación con confianza. Si no las conoce, cualquier cambio es un riesgo. Los mejores sistemas son aquellos donde las invariantes son obvias, ya sea por la estructura del código, los tests que las verifican, o los constraints que las imponen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Las invariantes como detector de problemas de diseño
&lt;/h3&gt;

&lt;p&gt;Si una invariante es difícil de mantener, eso suele indicar un problema de diseño. Si necesita verificar la misma condición en diez lugares diferentes del código, probablemente la invariante debería estar encapsulada en un solo lugar: una clase, un módulo, un constraint de base de datos. La dificultad de mantener una invariante es una señal de que las responsabilidades no están bien distribuidas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Las invariantes a lo largo del ciclo de vida
&lt;/h3&gt;

&lt;p&gt;Las invariantes no son estáticas. Evolucionan con el sistema: al agregar una nueva funcionalidad, es posible que necesite agregar nuevas invariantes o relajar las existentes. Lo importante es que cada cambio sea consciente y deliberado. Los bugs más peligrosos no son los que rompen una invariante (esos se detectan rápido), sino los que cambian silenciosamente una invariante sin que nadie lo note.&lt;br&gt;
La práctica más valiosa que un equipo de desarrollo puede adoptar es preguntarse, ante cada nueva clase, cada nueva tabla, cada nueva API: &lt;strong&gt;¿cuáles son las invariantes aquí, y cómo nos aseguramos de que se cumplan?&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Apendice
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Optimizacion en lenguajes interpretados
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Ruby
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Ruby 2.0 (MRI/CRuby sin JIT):&lt;/strong&gt; No realiza ninguna optimización de este tipo. El intérprete ejecuta bytecode (instrucciones intermedias) una por una, evaluando cada condición en cada iteración del loop sin importar si el valor cambió o no. La responsabilidad de sacar código invariante del loop recae completamente en el programador.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ruby 2.6+ (MJIT):&lt;/strong&gt; Ruby 2.6 introdujo MJIT, el primer compilador JIT de CRuby. Funciona compilando métodos que se ejecutan muchas veces a código C y luego a código máquina vía GCC o Clang. Aunque esto permite que el compilador C subyacente aplique algunas optimizaciones, MJIT fue diseñado principalmente para eliminar el costo de interpretar bytecode, no para aplicar optimizaciones agresivas como LICM. En la práctica, MJIT brillaba en benchmarks sintéticos, pero en aplicaciones Rails reales la ganancia era mínima, porque Rails tiene miles de métodos pequeños donde casi ninguno llegaba a entrar en calor lo suficiente como para que el JIT decida compilarlo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ruby 3.1+ (YJIT):&lt;/strong&gt; YJIT, desarrollado por Shopify y ahora parte oficial de CRuby, representa un salto significativo. En vez de intentar optimizaciones clásicas como LICM, YJIT se enfocó en reducir el costo del despacho dinámico de métodos (el proceso que Ruby realiza cada vez que invoca un método para determinar cuál ejecutar), que es donde Ruby gasta la mayor parte del tiempo. YJIT usa una técnica llamada &lt;em&gt;Basic Block Versioning&lt;/em&gt;, generando código máquina especializado para los tipos que observa en runtime, y elimina verificaciones redundantes. En Ruby 3.3, YJIT puede hacer que aplicaciones Rails sean mucho más rápidas que el intérprete base.&lt;/p&gt;

&lt;h4&gt;
  
  
  JavaScript
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;V8/TurboFan (Chrome, Node.js):&lt;/strong&gt; Es el caso más avanzado entre los lenguajes dinámicos. El motor V8 usa un pipeline de múltiples etapas: primero, &lt;em&gt;Ignition&lt;/em&gt; (un intérprete de bytecode) ejecuta el código y recopila información sobre qué tipos de datos se usan y qué funciones se llaman con frecuencia. Luego, &lt;em&gt;TurboFan&lt;/em&gt; (el compilador optimizador) genera código máquina altamente optimizado para las funciones "calientes". TurboFan sí aplica LICM explícitamente: por ejemplo, mueve &lt;code&gt;arr.length&lt;/code&gt; fuera de un loop &lt;code&gt;for&lt;/code&gt; cuando detecta que el array no cambia. También realiza &lt;em&gt;loop unrolling&lt;/em&gt; (desenrollar loops para reducir saltos), eliminación de código muerto, y &lt;em&gt;escape analysis&lt;/em&gt; (determinar si un objeto puede vivir solo en la pila en vez del heap, evitando el costo de crearlo y destruirlo con el recolector de basura). La diferencia clave con lenguajes compilados es que TurboFan puede &lt;em&gt;deoptimizar&lt;/em&gt;: si una suposición de tipo resulta incorrecta en runtime, descarta el código optimizado y vuelve al intérprete. Es un proceso costoso pero garantiza que el programa siempre funcione correctamente.&lt;/p&gt;

&lt;h4&gt;
  
  
  Python
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;CPython (el intérprete estándar):&lt;/strong&gt; Hasta la versión 3.12, CPython no tiene JIT. Python 3.11 introdujo un &lt;em&gt;Specializing Adaptive Interpreter&lt;/em&gt; que optimiza bytecodes frecuentes (por ejemplo, si detecta que una suma siempre recibe enteros, usa una versión especializada más rápida), y Python 3.13 agregó un JIT experimental basado en &lt;em&gt;copy-and-patch&lt;/em&gt; (una técnica que une plantillas de código máquina precompiladas, documentada en PEP 744). Sin embargo, sus mejoras son modestas y no incluyen optimizaciones como LICM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PyPy:&lt;/strong&gt; PyPy es un intérprete alternativo de Python con un JIT de &lt;em&gt;tracing&lt;/em&gt; (en vez de compilar funciones completas, detecta secuencias de instrucciones que se ejecutan repetidamente y las compila). PyPy puede ser hasta 4.7x más rápido que CPython en promedio, y hasta 50x más rápido en casos específicos con loops calientes. El beneficio es mayor en programas de ejecución larga; scripts cortos no se benefician porque el JIT necesita tiempo para "calentar" (recopilar suficiente información antes de optimizar).&lt;/p&gt;

&lt;h4&gt;
  
  
  Java
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;HotSpot JVM:&lt;/strong&gt; La JVM de Oracle/OpenJDK es el estándar de referencia en compilación JIT. Su compilador C2 aplica LICM, &lt;em&gt;loop unrolling&lt;/em&gt;, &lt;em&gt;escape analysis&lt;/em&gt;, vectorización (convertir operaciones escalares en operaciones que procesan múltiples datos simultáneamente usando instrucciones SIMD del procesador) y muchas otras optimizaciones de forma rutinaria. La JVM fue pionera en demostrar que un lenguaje con tipado dinámico parcial podía alcanzar rendimiento cercano a C en muchos escenarios.&lt;/p&gt;




&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Origen del Concepto de Invariante
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Klein, F. — &lt;em&gt;Vergleichende Betrachtungen über neuere geometrische Forschungen&lt;/em&gt; (Programa de Erlangen). Deichert, Erlangen, 1872.&lt;br&gt;
El documento que propuso clasificar geometrías mediante sus invariantes bajo grupos de transformaciones.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Floyd, R. W. — &lt;em&gt;Assigning Meanings to Programs&lt;/em&gt;. Proceedings of Symposia in Applied Mathematics, 1967.&lt;br&gt;
Primer uso formal de invariantes para razonar sobre corrección de programas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hoare, C. A. R. — &lt;em&gt;An Axiomatic Basis for Computer Programming&lt;/em&gt;. Communications of the ACM, 1969.&lt;br&gt;
Introduce la triple de Hoare y formaliza el concepto de loop invariant.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wikipedia — &lt;a href="https://en.wikipedia.org/wiki/Erlangen_program" rel="noopener noreferrer"&gt;Erlangen program&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;MacTutor History of Mathematics — &lt;a href="https://mathshistory.st-andrews.ac.uk/Biographies/Klein/" rel="noopener noreferrer"&gt;Felix Klein&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wikipedia — &lt;a href="https://en.wikipedia.org/wiki/Cross-ratio" rel="noopener noreferrer"&gt;Cross-ratio&lt;/a&gt;&lt;br&gt;
Introducción accesible al concepto de razón cruzada y su papel en geometría proyectiva.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Compiladores (Loop-Invariant Code Motion y Loop Unswitching)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Aho, A. V., Lam, M. S., Sethi, R., &amp;amp; Ullman, J. D. — &lt;em&gt;Compilers: Principles, Techniques, and Tools&lt;/em&gt; (2nd ed.). Addison-Wesley, 2006.&lt;br&gt;
El libro del dragón. Referencia canónica sobre optimizaciones de loops.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Muchnick, S. S. — &lt;em&gt;Advanced Compiler Design and Implementation&lt;/em&gt;. Morgan Kaufmann, 1997.&lt;br&gt;
Tratamiento en profundidad de LICM y otras optimizaciones.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Appel, A. W. — &lt;em&gt;Modern Compiler Implementation in ML&lt;/em&gt;. Cambridge University Press, 1998.&lt;br&gt;
Capítulos 18.1–18.3 cubren LICM como caso de eliminación de redundancia parcial.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wikipedia — &lt;a href="https://en.wikipedia.org/wiki/Loop-invariant_code_motion" rel="noopener noreferrer"&gt;Loop-invariant code motion&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wikipedia — &lt;a href="https://en.wikipedia.org/wiki/Loop_unswitching" rel="noopener noreferrer"&gt;Loop unswitching&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Platzer, A. — &lt;a href="https://symbolaris.com/course/Compilers12/17-loopinv.pdf" rel="noopener noreferrer"&gt;Lecture Notes on Loop-Invariant Code Motion (CMU 15-411)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Pekhimenko, G. — &lt;a href="http://www.cs.toronto.edu/~pekhimenko/courses/cscd70-w18/docs/Lecture%205%20%5BLICM%20and%20Strength%20Reduction%5D%2002.08.2018.pdf" rel="noopener noreferrer"&gt;LICM: Loop Invariant Code Motion (University of Toronto CSC D70)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cornell Virtual Workshop — &lt;a href="https://cvw.cac.cornell.edu/code-optimization/data-locality/code-motion" rel="noopener noreferrer"&gt;Loop-Invariant Code Motion&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Monniaux, D. &amp;amp; Becker, H. — &lt;em&gt;Formally Verified Loop-Invariant Code Motion and Assorted Optimizations&lt;/em&gt;. ACM TECS, 2022.&lt;br&gt;
&lt;a href="https://dl.acm.org/doi/10.1145/3529507" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3529507&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Lenguajes Interpretados, VMs y JIT Compilation
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Shopify Engineering — &lt;a href="https://shopify.engineering/yjit-just-in-time-compiler-cruby" rel="noopener noreferrer"&gt;YJIT: Building a New JIT Compiler for CRuby&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shopify Engineering — &lt;a href="https://shopify.engineering/ruby-yjit-is-production-ready" rel="noopener noreferrer"&gt;Ruby 3.2's YJIT is Production-Ready&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rails at Scale — &lt;a href="https://railsatscale.com/2023-12-04-ruby-3-3-s-yjit-faster-while-using-less-memory/" rel="noopener noreferrer"&gt;Ruby 3.3's YJIT: Faster While Using Less Memory&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Chevalier-Boisvert, M. et al. — &lt;em&gt;Evaluating YJIT's Performance in a Production Context&lt;/em&gt;. MPLR 2023.&lt;br&gt;
&lt;a href="https://dl.acm.org/doi/10.1145/3617651.3622982" rel="noopener noreferrer"&gt;https://dl.acm.org/doi/10.1145/3617651.3622982&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Heroku Blog — &lt;a href="https://blog.heroku.com/ruby-just-in-time-compilation" rel="noopener noreferrer"&gt;Ruby 2.6 Released: Just-In-Time Compilation Is Here&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Big Binary — &lt;a href="https://www.bigbinary.com/blog/mjit-support-in-ruby-2-6" rel="noopener noreferrer"&gt;MJIT Support in Ruby 2.6&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;V8 Blog — &lt;a href="https://v8.dev/blog/turbofan-jit" rel="noopener noreferrer"&gt;Digging into the TurboFan JIT&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;V8 Docs — &lt;a href="https://v8.dev/docs/turbofan" rel="noopener noreferrer"&gt;TurboFan&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Jindal, R. — &lt;a href="https://medium.com/@rahul.jindal57/understanding-just-in-time-jit-compilation-in-v8-a-deep-dive-c98b09c6bf0c" rel="noopener noreferrer"&gt;Understanding Just-In-Time (JIT) Compilation in V8: A Deep Dive&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Baloney, A. — &lt;a href="https://tonybaloney.github.io/posts/python-gets-a-jit.html" rel="noopener noreferrer"&gt;Python 3.13 gets a JIT&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Python Enhancement Proposals — &lt;a href="https://peps.python.org/pep-0744/" rel="noopener noreferrer"&gt;PEP 744: JIT Compilation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;PyPy — &lt;a href="https://pypy.org/performance.html" rel="noopener noreferrer"&gt;Performance Tips&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;InfoWorld — &lt;a href="https://www.infoworld.com/article/4117428/which-python-runtime-does-jit-better-cpython-or-pypy.html" rel="noopener noreferrer"&gt;CPython vs. PyPy: Which Python runtime has the better JIT?&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Zhang et al. — &lt;em&gt;Python meets JIT compilers: A simple implementation and a comparative evaluation&lt;/em&gt;. Software: Practice and Experience, 2024.&lt;br&gt;
&lt;a href="https://onlinelibrary.wiley.com/doi/10.1002/spe.3267" rel="noopener noreferrer"&gt;https://onlinelibrary.wiley.com/doi/10.1002/spe.3267&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Testing (Invariantes como Propiedades)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Hébert, F. — &lt;em&gt;Property-Based Testing with PropEr, Erlang, and Elixir&lt;/em&gt;. The Pragmatic Bookshelf, 2019.&lt;br&gt;
El libro más práctico y accesible sobre property-based testing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Van den Ende, W. — &lt;em&gt;The (Little) QuickCheck Workbook&lt;/em&gt;. Leanpub.&lt;br&gt;
&lt;a href="https://leanpub.com/quickcheckworkbook" rel="noopener noreferrer"&gt;https://leanpub.com/quickcheckworkbook&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;O'Sullivan, B., Stewart, D., &amp;amp; Goerzen, J. — &lt;em&gt;Real World Haskell&lt;/em&gt;, Chapter 11: Testing and Quality Assurance.&lt;br&gt;
&lt;a href="https://book.realworldhaskell.org/read/testing-and-quality-assurance.html" rel="noopener noreferrer"&gt;https://book.realworldhaskell.org/read/testing-and-quality-assurance.html&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cockx, J. — &lt;a href="https://jesper.sikanda.be/posts/quickcheck-intro.html" rel="noopener noreferrer"&gt;An Introduction to Property-Based Testing with QuickCheck&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Typeable — &lt;a href="https://typeable.io/blog/2021-08-09-pbt.html" rel="noopener noreferrer"&gt;Property-Based Testing With QuickCheck&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Kan, Y. — &lt;a href="https://yrkan.com/blog/property-based-testing/" rel="noopener noreferrer"&gt;Property-Based Testing: Generative Testing for System Invariants&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;University of Pennsylvania (CIS 1940) — &lt;a href="https://www.seas.upenn.edu/~cis1940/fall16/lectures/10-testing.html" rel="noopener noreferrer"&gt;Property-based testing&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Diseño de Software (Invariantes de Clase y Design by Contract)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Meyer, B. — &lt;em&gt;Object-Oriented Software Construction&lt;/em&gt; (2nd ed.). Prentice Hall, 1997. Primera edición 1988.&lt;br&gt;
El libro que formalizó Design by Contract. Referencia fundamental.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Evans, E. — &lt;em&gt;Domain-Driven Design: Tackling Complexity in the Heart of Software&lt;/em&gt;. Addison-Wesley, 2003.&lt;br&gt;
Capítulos sobre entidades, agregados e invariantes del dominio.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Reddy, M. — &lt;em&gt;API Design for C++&lt;/em&gt;. Morgan Kaufmann, 2011.&lt;br&gt;
Sección sobre Design by Contract aplicado a diseño de APIs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Eiffel Software — &lt;a href="https://www.eiffel.com/values/design-by-contract/introduction/" rel="noopener noreferrer"&gt;Design by Contract: Introduction&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wikipedia — &lt;a href="https://en.wikipedia.org/wiki/Design_by_contract" rel="noopener noreferrer"&gt;Design by contract&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Wikipedia — &lt;a href="https://en.wikipedia.org/wiki/Class_invariant" rel="noopener noreferrer"&gt;Class invariant&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Microsoft Learn — &lt;a href="https://learn.microsoft.com/en-us/archive/msdn-magazine/2011/june/msdn-magazine-cutting-edge-invariants-and-inheritance-in-code-contracts" rel="noopener noreferrer"&gt;Invariants and Inheritance in Code Contracts (MSDN Magazine)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fowler, M. — &lt;a href="http://www.cs.unc.edu/~stotts/145/CRC/DesByContract.html" rel="noopener noreferrer"&gt;Design by Contract&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Object Computing — &lt;a href="https://objectcomputing.com/resources/publications/sett/september-2011-design-by-contract-in-java-with-google" rel="noopener noreferrer"&gt;Design by Contract in Java with Google&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;UCSB (CS 272) — &lt;a href="https://sites.cs.ucsb.edu/~bultan/courses/272/lectures/Contract-JML.pdf" rel="noopener noreferrer"&gt;Design by Contract and JML&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Refactorización, Patrones de Diseño y Principios SOLID
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Fowler, M. — &lt;em&gt;Refactoring: Improving the Design of Existing Code&lt;/em&gt; (2nd ed.). Addison-Wesley, 2018.&lt;br&gt;
Argumenta que los tests deben capturar el comportamiento observable (invariantes) del sistema.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Martin, R. C. — &lt;em&gt;Clean Code: A Handbook of Agile Software Craftsmanship&lt;/em&gt;. Prentice Hall, 2008.&lt;br&gt;
Principios de diseño limpio que favorecen invariantes simples y encapsuladas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Martin, R. C. — &lt;em&gt;Agile Software Development: Principles, Patterns, and Practices&lt;/em&gt;. Prentice Hall, 2002.&lt;br&gt;
Tratamiento detallado de los principios SOLID, incluyendo el principio de sustitución de Liskov y su relación con invariantes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Gamma, E., Helm, R., Johnson, R., &amp;amp; Vlissides, J. — &lt;em&gt;Design Patterns: Elements of Reusable Object-Oriented Software&lt;/em&gt;. Addison-Wesley, 1994.&lt;br&gt;
"El libro de la banda de cuatro". Muchos patrones existen para proteger invariantes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Liskov, B. &amp;amp; Wing, J. — &lt;em&gt;A Behavioral Notion of Subtyping&lt;/em&gt;. ACM Transactions on Programming Languages and Systems, 1994.&lt;br&gt;
Formalización del principio de sustitución en términos de precondiciones, postcondiciones e invariantes.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>architecture</category>
      <category>computerscience</category>
      <category>softwareengineering</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
