<?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: Baltasar García Perez-Schofield</title>
    <description>The latest articles on DEV Community by Baltasar García Perez-Schofield (@baltasarq).</description>
    <link>https://dev.to/baltasarq</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%2F156438%2Fc92243a0-0316-41ba-9445-ba8c9ee76d3b.png</url>
      <title>DEV Community: Baltasar García Perez-Schofield</title>
      <link>https://dev.to/baltasarq</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/baltasarq"/>
    <language>en</language>
    <item>
      <title>Creando un Tetris con JavaScript VI: Complicando el juego.</title>
      <dc:creator>Baltasar García Perez-Schofield</dc:creator>
      <pubDate>Mon, 25 May 2026 08:41:11 +0000</pubDate>
      <link>https://dev.to/baltasarq/creando-un-tetris-con-javascript-vi-complicando-el-juego-17pg</link>
      <guid>https://dev.to/baltasarq/creando-un-tetris-con-javascript-vi-complicando-el-juego-17pg</guid>
      <description>&lt;p&gt;En la última entrega, dábamos prácticamente por terminado nuestro Tetris. Con el bucle de juego, ya era posible hacer que las piezas bajaran, así como moverlas. También las líneas completas eran eliminadas del tablero, con el siguiente código en &lt;em&gt;showFrame()&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;showFrame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// más cosas...&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;millis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startTime&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="nx"&gt;millis&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pushPieceDownTime&lt;/span&gt; &lt;span class="o"&gt;+=&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;// más cosas...&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&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="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;newScore&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;portPieceToBoard&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;newScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculateScore&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="nx"&gt;newScore&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&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;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;newScore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;pScore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

           &lt;span class="c1"&gt;// Prepare the board&lt;/span&gt;
           &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;filledRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chkFilledRows&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

           &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeRows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;filledRows&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
           &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertEmptyRows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;filledRows&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopGame&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chooseNewPiece&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;En la parte del comentario &lt;em&gt;// Prepare the board&lt;/em&gt;, lo que hacemos es tomar los índices de aquellas filas que están llenas con trozos de fichas (su contenido es 1 en todas las celdas), y después llamar a &lt;strong&gt;Board&lt;/strong&gt;.&lt;em&gt;removeRows()&lt;/em&gt; para que las elimine. Entonces rellenamos con filas vacías por la parte delantera del tablero (la parte superior), de manera que el nivel de fichas "baja" tanto como filas hayamos eliminado.&lt;/p&gt;

&lt;p&gt;De la modificación a continuación es de donde viene el nombre &lt;em&gt;Insertrix&lt;/em&gt;: vamos a añadir filas "basura" en parte, además de seguir rellenando con filas vacías la parte superior. Estas filas "basura" serán filas que contendrán ceros y unos al azar. Hay algunos Tetris que, además de acelerar el tiempo de bajada de cada pieza, hacen esto para hacerte perder cuando ya llevas mucho tiempo jugando. En esta versión, la velocidad no va a variar, pero pueden introducirse muchas más filas "basura" que una fila cada cierto tiempo.&lt;/p&gt;

&lt;p&gt;En concreto, vamos a introducir filas "basura" cuando llegue el momento de bajar el tablero el mismo número de filas que las que se han eliminado. Vamos a meter la mitad más uno de filas vacías, y el resto con filas basura. El algoritmo es el siguiente. Sabemos el número de filas total a rellenar porque tenemos la longitud del vector de índices de filas a eliminar, &lt;code&gt;filledRows.length&lt;/code&gt;. Así, si calculamos la mitad sin redondear, tomando la parte entera: &lt;code&gt;let numTrashRows = parseInt( filledRows.length / 2 )&lt;/code&gt;, tendremos exactamente la mitad o la mitad menos uno en &lt;code&gt;numTrashRows&lt;/code&gt;, si &lt;code&gt;filledRows.length&lt;/code&gt; es impar. Este se hace así para favorecer al usuario.&lt;/p&gt;

&lt;p&gt;Por ejemplo, si el usuario elimina una fila, la mitad exacta de la divisón por dos es 0, por lo que se introducen 0 filas basura, y 1 fila vacía.&lt;/p&gt;

&lt;p&gt;El código es el siguiente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;showFrame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// más cosas...&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;millis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startTime&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="nx"&gt;millis&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pushPieceDownTime&lt;/span&gt; &lt;span class="o"&gt;+=&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;// más cosas...&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&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="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;newScore&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;portPieceToBoard&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;newScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculateScore&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="nx"&gt;newScore&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&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;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;newScore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="nx"&gt;pScore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

           &lt;span class="c1"&gt;// Prepare the board&lt;/span&gt;
           &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;filledRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chkFilledRows&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
           &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;numTrashRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;filledRows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

           &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeRows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;filledRows&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
           &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertEmptyRows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;filledRows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;numTrashRows&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
           &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendTrashRows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;numTrashRows&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopGame&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;

       &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chooseNewPiece&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;Ya solo queda añadir al tablero tantas filas basura como nos digan, con el método &lt;strong&gt;Board&lt;/strong&gt;.&lt;em&gt;appendTrashRows(n: Integer)&lt;/em&gt;. Podemos generar un número entero aleatorio entre 0 y 1 con &lt;code&gt;Math.random() * 2&lt;/code&gt;, siempre que lo corrijamos con &lt;code&gt;Math.floor()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Board&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Más cosas...&lt;/span&gt;

    &lt;span class="nf"&gt;appendTrashRows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numRows&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;numRows&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&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;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cols&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&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;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;En lugar de &lt;code&gt;#board.unshift()&lt;/code&gt; utilizamos &lt;code&gt;#board.push()&lt;/code&gt;, pues lo que queremos es añadir filas al final. El método estático &lt;code&gt;Array.from()&lt;/code&gt;, toma un &lt;strong&gt;Array&lt;/strong&gt; ya existente, y lo modifica con la lambda que le pasemos, su segundo parámetro. En este caso, lo que hacemos es crear un objeto que tiene la propiedad &lt;code&gt;length&lt;/code&gt;, lo cual a ojos de &lt;code&gt;Array.from()&lt;/code&gt;, lo transforma en un array. Después, la lambda se aplica para cada elemento, con lo que se asigna un valor aleatorio entre 0 y 1.&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%2Fixl3zrlbpwnb1jmabohq.jpg" 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%2Fixl3zrlbpwnb1jmabohq.jpg" alt="Insertrix con una fila basura añadida al final." width="800" height="683"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;¡Y ya está! Nuestro juego está listo. Arriba puedes ver cómo al eliminar dos filas, se ha añadido una fila "basura". La partida solo termina cuando una pieza ya no tiene sitio para caer, y se queda "enganchada" en la parte superior. No hay &lt;em&gt;estrés&lt;/em&gt; por tener que mover las piezas cada vez más rápido. ¿Te animas a una partida a &lt;a href="https://github.com/Baltasarq/Insertrix" rel="noopener noreferrer"&gt;Insertrix&lt;/a&gt;?&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>html</category>
      <category>javascript</category>
      <category>games</category>
    </item>
    <item>
      <title>Creando un Tetris con JavaScript V: el bucle de juego</title>
      <dc:creator>Baltasar García Perez-Schofield</dc:creator>
      <pubDate>Wed, 20 May 2026 11:28:42 +0000</pubDate>
      <link>https://dev.to/baltasarq/creando-un-tetris-con-javascript-v-el-bucle-de-juego-53li</link>
      <guid>https://dev.to/baltasarq/creando-un-tetris-con-javascript-v-el-bucle-de-juego-53li</guid>
      <description>&lt;p&gt;En este momento, sabemos que, utilizando el elemento &lt;em&gt;Canvas&lt;/em&gt; de &lt;strong&gt;HTML5&lt;/strong&gt;, podemos dibujar el tablero, así como la pieza que va cayendo. Pero nada se mueve, la pieza no cae, y desde luego, no podemos moverla.&lt;/p&gt;

&lt;p&gt;Las piezas disponibles, para seleccionar la que está cayendo en este momento, son las siguientes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PIECES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PieceSquare&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PieceBar&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PieceL&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PieceInverseL&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PieceS&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PieceInverseS&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;Necesitamos un bucle de juego, y para ello sería interesante disponer de, o bien varias variables y funciones, o mejor todavía, un objeto. Se trataría de una clase que solo tendría una posible instancia. En otro lenguaje de programación, utilizaríamos el patrón &lt;a href="https://es.wikipedia.org/wiki/Singleton" rel="noopener noreferrer"&gt;&lt;strong&gt;Singleton&lt;/strong&gt;&lt;/a&gt;. Pero en JavaScript, podemos crear un objeto único utilizando la notación &lt;a href="https://es.wikipedia.org/wiki/JSON" rel="noopener noreferrer"&gt;JSON&lt;/a&gt; (JavaScript Simplified Object Notation), que podemos utilizar directamente en nuestro código fuente.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;board&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;keysPressed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;


    &lt;span class="na"&gt;pickRandomPiece&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;PIECES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;PIECES&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;


    &lt;span class="na"&gt;chooseNewPiece&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pickRandomPiece&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cols&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="c1"&gt;// más cosas...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nuestro objecto, que va a llevar el estado del juego, es &lt;em&gt;game&lt;/em&gt;. Dentro de este objeto, construiremos el tablero del juego, la pieza actual, el canvas donde vamos a pintar el juego, etc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// más cosas...    &lt;/span&gt;
    &lt;span class="na"&gt;stopGame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dvStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dvStart&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dvGame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dvGame&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;dvStart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;dvGame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="c1"&gt;// más cosas...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En nuestro (parco) HTML, tendremos un botón (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/button" rel="noopener noreferrer"&gt;&lt;em&gt;Button&lt;/em&gt;&lt;/a&gt;), que servirá para comenzar el juego. Habrá un &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/div" rel="noopener noreferrer"&gt;DIV&lt;/a&gt; que contendrá este botón, y un segundo DIV que contendrá el &lt;em&gt;HTML Canvas&lt;/em&gt;. Según ocultemos o visualicemos (&lt;em&gt;style="display: none"&lt;/em&gt;) o (&lt;em&gt;style="display: block"&lt;/em&gt;), cada uno de estos DIVs, podremos comenzar el juego o jugar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;startGame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dvStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dvStart&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dvGame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dvGame&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cvPaint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cvPaint&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Set&lt;/span&gt;
        &lt;span class="nx"&gt;dvStart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;display&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;none&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;dvGame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Board&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Canvas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;cvPaint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chooseNewPiece&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Listeners&lt;/span&gt;
        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showFrame&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onkeydown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isComposing&lt;/span&gt;
              &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dead&lt;/span&gt;&lt;span class="dl"&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="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keysPressed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;e&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="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="c1"&gt;// más cosas...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Además, debemos poder "animar" el juego. Tiene que haber una forma de conseguir que, una vez se pinte un frame, establezcamos algún mecanismo para "encargar" el pintado del siguiente.&lt;/p&gt;

&lt;p&gt;Podemos establecer el pintado del primer frame con &lt;em&gt;window.requestAnimationFrame()&lt;/em&gt;, al que se le pasa una lambda con el código a ejecutar. De acuerdo, pero... ¿cómo hacemos para ejecutar la siguiente? Pues volvemos a asignar &lt;em&gt;window.requestAnimationFrame()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;En cuanto a las pulsaciones de teclas, se utiliza el método &lt;em&gt;document.onkeydown&lt;/em&gt;, de manera que en este método se filtran las teclas que no producen ninguna pulsación, y se guarda en la lista &lt;em&gt;keysPressed&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// más cosas...&lt;/span&gt;

    &lt;span class="na"&gt;chkKeyboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keysPressed&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="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ArrowLeft&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chkPieceClash&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&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;else&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ArrowRight&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chkPieceClash&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&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;else&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ArrowUp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rotate&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chkPieceClash&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unrotate&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;else&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ArrowDown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chkPieceClash&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keysPressed&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="c1"&gt;// más cosas...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En cuanto a &lt;em&gt;chkKeyboard()&lt;/em&gt;, se detectan las teclas de los cursores, y en caso de que una de ellas sea la flecha hacia arriba, la pieza se rota. En el resto de los casos, la posición de la pieza se cambia... a no ser que se compruebe que coincida con el tablero.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// más cosas...&lt;/span&gt;

    &lt;span class="na"&gt;chkPieceClash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;toret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chkInbounds&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="nx"&gt;toret&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="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&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="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;break&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&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="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;j&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="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cols&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;

                    &lt;span class="nx"&gt;toret&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                           &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;j&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="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;toret&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;break&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="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;toret&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="c1"&gt;// más cosas....&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para comprobar si la pieza coincide con alguna pieza almacenada en el tablero, será necesario comprobar si alguna de las celdas de la pieza y la posición correspondiente en el tablero contienen ambas un 1. En ese caso, la pieza no puede moverse a esa posición, con lo que será necesario deshacer el movimiento. Si es una rotación, será necesario cancelar la rotación (método &lt;strong&gt;Piece&lt;/strong&gt;.&lt;em&gt;unrotate()&lt;/em&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="nx"&gt;chkFilledRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;toret&lt;/span&gt; &lt;span class="o"&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;NUM_ROW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&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="nx"&gt;BOARD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;NUM_ROW&lt;/span&gt; &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;every&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&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="nx"&gt;toret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;NUM_ROW&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="nx"&gt;toret&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="nx"&gt;portPieceToBoard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MAX_HEIGHT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MAX_WIDTH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cols&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&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;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&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="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BOARD_POS_ROW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BOARD_POS_COL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;j&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="nx"&gt;BOARD_POS_ROW&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;
                  &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;BOARD_POS_COL&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cols&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;break&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="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;][&lt;/span&gt; &lt;span class="nx"&gt;j&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="nx"&gt;BOARD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setCell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;BOARD_POS_ROW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BOARD_POS_COL&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="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// más cosas...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El método &lt;em&gt;chkFilledRows()&lt;/em&gt; comprueba si todas las celdas de alguna de las filas del tablero están llenas. Con el método &lt;em&gt;every()&lt;/em&gt; para &lt;strong&gt;Array&lt;/strong&gt;, podemos comprobar si se cumple una condición. De nuevo convertimos los 1 y 0 de la fila a booleano, y comprobamos entonces si todas dan &lt;strong&gt;true&lt;/strong&gt;. En este último caso, pasamos el índice de la fila a un &lt;strong&gt;Array&lt;/strong&gt; que vamos a devolver. Después llamaremos a &lt;strong&gt;Board&lt;/strong&gt;.&lt;em&gt;removeRows()&lt;/em&gt; para eliminarlas.&lt;/p&gt;

&lt;p&gt;El método &lt;em&gt;portPieceToBoard()&lt;/em&gt; comprueba todas las celdas de una pieza, y que en caso de que contengan un 1, portar ese 1 a la misma posición, pero en el tablero.&lt;/p&gt;

&lt;p&gt;Ya solo nos falta comprobar si hay colisión entre la pieza que desciende y el contenido del tablero. Nos aprovechamos de que tanto la pieza como el tablero son &lt;strong&gt;Array&lt;/strong&gt;'s, y que en caso de que la posición esté ocupada, en cualquiera de los dos casos, va a contener un 1. Así, un &lt;code&gt;and&lt;/code&gt;, nos permite generar un booleano que nos diga si hay coincidencia entre alguna de las casillas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="c1"&gt;// más cosas...&lt;/span&gt;

    &lt;span class="nx"&gt;chkPieceClash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;toret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chkInbounds&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="nx"&gt;toret&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="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&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="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;break&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&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="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;j&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="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cols&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;

                    &lt;span class="nx"&gt;toret&lt;/span&gt; &lt;span class="o"&gt;|=&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                           &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;BOARD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;PIECE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;col&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;j&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="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;toret&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;break&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="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;toret&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="c1"&gt;// más cosas...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y finalmente, el núcleo del juego. En él, se comprueban las teclas pulsadas, se cambia la posición de la pieza si es necesario, se comprueba si coincide con el fondo del tablero o no, o si la pieza ya no puede moverse desde la primera fila (en ese caso, el juego termina). En cualquier caso, al final ya con la posición de la pieza definitiva, es cuando se pinta el frame.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// más cosas...&lt;/span&gt;

    &lt;span class="na"&gt;showFrame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pScore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;millis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startTime&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="nx"&gt;millis&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pushPieceDownTime&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pushPieceDownTime&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pushPieceDownTime&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isAtBottom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rows&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chkPieceClash&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                  &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;isAtBottom&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isAtBottom&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;piece&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&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="p"&gt;{&lt;/span&gt;
                        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;newScore&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;portPieceToBoard&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                        &lt;span class="nx"&gt;newScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;calculateScore&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="nx"&gt;newScore&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&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;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;newScore&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                            &lt;span class="nx"&gt;pScore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;

                        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;filledRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chkFilledRows&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                        &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeRows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;filledRows&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;board&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertEmptyRows&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;filledRows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopGame&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;

                    &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chooseNewPiece&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="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chkKeyboard&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;            
            &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawFrame&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;startTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;game&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;showFrame&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="c1"&gt;// más cosas...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Primero se hacen varias comprobaciones en cuanto a que haya pasado suficiente tiempo. De otra forma, se pintarían demasiados frames, y dado que pintar un solo frame lleva un tiempo, se establece un tiempo de 10 ms. mínimo entre frames. También se establece el contador &lt;em&gt;pushPieceDownTime&lt;/em&gt;, de manera que el tiempo entre el movimiento de la pieza que cae, y el siguiente, sea de al menos 400 ms., es decir, casi medio segundo. Finalmente, se comprueba el teclado, se pinta el frame, y se vuelve a llamar a &lt;em&gt;window.requestAnimationFrame()&lt;/em&gt; para el siguiente frame.&lt;/p&gt;

&lt;p&gt;En cuanto a la pieza, en caso de que al caer una fila más en el tablero, esta coincida con los restos, entonces se porta esa pieza al tablero (pasa a formar parte de los restos), se comprueba si hay filas completamente rellenas, y en ese caso se eliminan, y se insertan tantas filas en blanco como sea necesario.&lt;/p&gt;

&lt;p&gt;Y básicamente ya está. Puedes ver el código, y echar unas partidas en el &lt;a href="https://github.com/Baltasarq/Insertrix" rel="noopener noreferrer"&gt;repo de GitHub para Insertrix&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>javascript</category>
      <category>html</category>
      <category>programming</category>
    </item>
    <item>
      <title>¿Y si Python permitiera 'end' como fin de bloque? (y III)</title>
      <dc:creator>Baltasar García Perez-Schofield</dc:creator>
      <pubDate>Tue, 12 May 2026 08:12:54 +0000</pubDate>
      <link>https://dev.to/baltasarq/y-si-python-permitiera-end-como-fin-de-bloque-y-iii-2a2f</link>
      <guid>https://dev.to/baltasarq/y-si-python-permitiera-end-como-fin-de-bloque-y-iii-2a2f</guid>
      <description>&lt;p&gt;El otro día, por razones que no vienen mucho al caso, me pregunté cómo de difícil sería añadir la posibilidad de incorporar ese &lt;code&gt;end&lt;/code&gt; como marca de fin de bloque, por mi mismo. Así que me dirigí al &lt;a href="https://github.com/python/cpython" rel="noopener noreferrer"&gt;repositorio de cPython, el intérprete de Python&lt;/a&gt;, y me lo bajé.&lt;/p&gt;

&lt;p&gt;Ya en el propio &lt;code&gt;README&lt;/code&gt; me encontré un enlace a la &lt;a href="https://devguide.python.org/" rel="noopener noreferrer"&gt;referencia de desarrollador de &lt;code&gt;Python&lt;/code&gt;&lt;/a&gt;, lo que me serviría de guía en el proceso.&lt;/p&gt;

&lt;p&gt;Lo primero que me planteé fue una modificación de la gramática. El directorio &lt;code&gt;Grammar/&lt;/code&gt; parecía prometedor, y, efectivamente, allí nos encontramos con &lt;code&gt;python.gram&lt;/code&gt;, el archivo que representa a la gramática actual del lenguaje.&lt;/p&gt;

&lt;p&gt;Aparecen muchas &lt;a href="https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form" rel="noopener noreferrer"&gt;reglas EBNF&lt;/a&gt;, y una de ellas es la que parece más prometedora:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pass_stmt[stmt_ty]:
    | 'pass' { _PyAST_Pass(EXTRA) }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Al fin y al cabo, la forma más sencilla de lograr una marca de fin de bloque, que no implique generación de código, es crear un sinónimo de la palabra clave &lt;code&gt;pass&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pass_stmt[stmt_ty]:
    | 'pass' { _PyAST_Pass(EXTRA) }
    | 'end'  { _PyAST_Pass(EXTRA) }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;De acuerdo, ahora falta generar la gramática y compilar. Un vistazo rápido a la guía del desarrollador nos dice que lo que debemos hacer es:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ make regen-pegen
$ make -j4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Le estamos pidiendo que regenere la gramática, y estamos recompilando &lt;code&gt;cpython&lt;/code&gt; con 4 procesos. En Linux, el comando &lt;code&gt;nproc&lt;/code&gt; nos dice cuántos procesos se pueden ejecutar concurrentemente. Así que podemos incluso ejecutar `make -j$(nproc)', lo cual automáticamente genera el número máximo de trabajos soportados.&lt;/p&gt;

&lt;p&gt;En cualquier caso, nos encontraremos con este mensaje de error:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SyntaxError: invalid syntax (posixpath.py, line 305)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Si nos vamos a esa línea de &lt;code&gt;posixpath.py&lt;/code&gt;, nos encontraremos con el uso de &lt;code&gt;end&lt;/code&gt; como variable:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat -n Lib/posixpath.py| grep 305
305:        end = b'}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Esto es un problema. Si curioseamos un poco más, veremos que las variables start/end son bastante populares. Así que nos nos queda más remedio que deshacer el cambio en &lt;code&gt;python.gram&lt;/code&gt;, y recompilar.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ make regen-pegen
$ make -j4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Tiene que haber una forma más sencilla. No puede ser que creemos una nueva característica, que claramente tiene que ser opcional, pero que a la vez esta colisione con código ya existente.&lt;/p&gt;

&lt;p&gt;Sabemos que &lt;strong&gt;Python&lt;/strong&gt; tiene un módulo llamado &lt;code&gt;__builtins__&lt;/code&gt; que incorpora funciones y constantes que, por decirlo de alguna manera, siempre están disponibles. Es el caso de &lt;code&gt;print&lt;/code&gt;, por poner un ejemplo.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; __builtins__
&amp;lt;module 'builtins' (built-in)&amp;gt;
&amp;gt;&amp;gt;&amp;gt; dir(__builtins__)
[
    # (...más cosas...)
    'abs', 'aiter', 'all', 'anext', 'any', 'ascii', 'bin', 'bool', 'breakpoint', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'end', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozendict', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'sentinel', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip'
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Si pudiéramos añadir &lt;code&gt;end&lt;/code&gt; como una constante en ese módulo, equivalente a &lt;code&gt;...&lt;/code&gt;, pues... ya estaría. El código Python sería:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;end = ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;La cuestión es dónde se definen estas funciones, como &lt;code&gt;print&lt;/code&gt;, o "..." que es la ellipsis. De hecho, si buscamos con:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ grep -i "ellipsis" Grammar/python.gram
230:# note below: the ('.' | '...') is necessary because '...' is tokenized as ELLIPSIS
903:    | '...' { _PyAST_Constant(Py_Ellipsis, NULL, EXTRA) }

$ grep -niR "ellipsis" --include "*.c"
(...más cosas...)
Python/bltinmodule.c:3535:    SETBUILTIN("Ellipsis",              Py_Ellipsis);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Entonces nos encontramos con que &lt;strong&gt;Ellipsis&lt;/strong&gt;, que representa a &lt;code&gt;...&lt;/code&gt;, es tokenizado como &lt;code&gt;Py_Ellipsis&lt;/code&gt;. Si extendemos la búsqueda a los archivos de extensión &lt;code&gt;.c&lt;/code&gt;, nos encontraremos con que hay un archivo llamado &lt;code&gt;bltinmodule.c&lt;/code&gt;, que es precisamente el que estamos buscando. Así que justo debajo de la línea 3535, añadimos nuestra nueva definición:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SETBUILTIN("end",              Py_Ellipsis);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Y recompilamos:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ make -j4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Esto genera el ejecutable &lt;code&gt;python&lt;/code&gt; en la raiz. ¡Podemos lanzarlo y probarlo!&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ./python

Python 3.15.0a8+ (heads/main-dirty:5fcab14c350, May 12 2026, 09:35:43) [GCC 15.2.1 20260209] on linux
Type "help", "copyright", "credits" or "license" for more information.

&amp;gt;&amp;gt;&amp;gt; end
Ellipsis
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;¡Funciona! Hagamos una prueba (hecho en el propio REPL):&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Entero:
...     def __init__(self, x):
...         self._x = x
...     end
...     @property
...     def x(self):
...         return self._x
...     end
...     def __str__(self):
...         return str(self.x)
...     end
... end
... 
Ellipsis
&amp;gt;&amp;gt;&amp;gt; e1 = Entero(42)
&amp;gt;&amp;gt;&amp;gt; e1.x
42
&amp;gt;&amp;gt;&amp;gt; str(e1)
'42'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Si pasamos este código a un archivo &lt;code&gt;test_end.py&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Entero:
    def __init__(self, x):
        self._x = x
    end

    @property
    def x(self):
        return self._x
    end

    def __str__(self):
       return str(self.x)
    end
end


if __name__ == "__main__":
    e1 = Entero(42)
    print(e1)
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Y lo ejecutamos con &lt;code&gt;./python test_end.py&lt;/code&gt;, obtenemos:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Y así concluye el viaje. Se puede obtener el comportamiento deseado modificando el módulo &lt;code&gt;__builtins__&lt;/code&gt; de &lt;strong&gt;Python&lt;/strong&gt;. ¡Un interesante ejercicio, al menos!&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>python</category>
      <category>hack</category>
      <category>programming</category>
    </item>
    <item>
      <title>Hackeando una app para marcadores web</title>
      <dc:creator>Baltasar García Perez-Schofield</dc:creator>
      <pubDate>Thu, 07 May 2026 10:40:32 +0000</pubDate>
      <link>https://dev.to/baltasarq/hackeando-una-app-para-marcadores-web-4afk</link>
      <guid>https://dev.to/baltasarq/hackeando-una-app-para-marcadores-web-4afk</guid>
      <description>&lt;p&gt;Cuando comienzas un proyecto, lo haces porque consideras que va a ser algo fácil, que te va a llevar poco (diez minutos, un par de horas...). Cualquier proyecto, por pequeño que sea, siempre duplica, triplica o quintuplica el tiempo estimado para su realización.&lt;/p&gt;

&lt;p&gt;Pero si no fuesemos tan positivos... ¡nunca comenzaríamos un proyecto! Veamos, ¿qué necesitamos para crear un marcador web en Python?&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ClassVar&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;


&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Bookmark&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;
    &lt;span class="n"&gt;NUM&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;classvar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;Bookmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NUM&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://info.cern.ch/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;dt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Bookmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NUM&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;04&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="n"&gt;month&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;day&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;02&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__str__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;También necesitamos una lista de &lt;strong&gt;Bookmarks&lt;/strong&gt;:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BookmarkList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;A list of bookmarks or other objects BookmarkList&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;initial_bml&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_lb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Bookmark&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;BookmarkList&lt;/span&gt;&lt;span class="p"&gt;}):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Adds a new entry, or a new list of bookmarks&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_lb&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bm&lt;/span&gt;
        &lt;span class="n"&gt;bm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Bookmark&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;BookmarkList&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="bp"&gt;None&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&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;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_lb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_lb&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Not deleted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__len__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_lb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Y ahora viene la parte realmente interesante, en la que grabamos y cargamos una lista de enlaces (que puede ser recursiva). Guardar es muy fácil. Solo tenemos que guardar las listas de enlaces entre marcas &lt;code&gt;dl&lt;/code&gt;, y cada enlace (como un hiperenlace), dentro de una marca &lt;code&gt;dt&lt;/code&gt;. Además, el nombre de cada lista va dentro de las marcas &lt;code&gt;dl&lt;/code&gt;, como un &lt;code&gt;dt&lt;/code&gt; que tiene dentro una marca &lt;code&gt;h3&lt;/code&gt; con el nombre.&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PersistentBookmarks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Persistence for bookmarks, read/write.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;INDENT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;    &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BookmarkList&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Creates a new saving PersistentBookmarks.
            :param bms: a bookmarks list
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_bms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bms&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Saves the list of bookmarks.
            :param bf: a path to save the BookmarkList to.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_bms&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;

        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&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;PersistentBookmarks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_headers&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_save_bms_to&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="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_bms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_save_bms_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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;lvl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BookmarkList&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Recursively saves a given BookmarkList.
            :param f: the file to save.
            :param lvl: the indent level.
            :param bms: the BookmarkList to save.
        &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;indent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PersistentBookmarks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INDENT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;lvl&lt;/span&gt;
        &lt;span class="n"&gt;inner_indent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;PersistentBookmarks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INDENT&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;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;dl&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;dt&amp;gt;&amp;lt;h3&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bms&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/h3&amp;gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&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;entry&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&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;BookmarkList&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_save_bms_to&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;lvl&lt;/span&gt; &lt;span class="o"&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;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&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;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;inner_indent&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;dt&amp;gt;&amp;lt;a href=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&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;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                        &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;dl&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@staticmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;write_headers&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="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Writes the required headers pf the bookmarks file.&lt;/span&gt;&lt;span class="sh"&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;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;!DOCType NETSCAPE-Bookmark-file-1&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;meta http-equiv=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;text/html;charset=UTF-8&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;title&amp;gt;Bookmarks&amp;lt;/title&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&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="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;h1&amp;gt;Bookmarks&amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&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 trabajo duro lo hace la función &lt;code&gt;_save_bms_to(lvl, bms)&lt;/code&gt;, que guarda la lista de enlaces con un nivel (&lt;em&gt;lvl&lt;/em&gt;), determinado. Este parámetro sería totalmente opcinal, solo se usa, en realidad, para crear la indentación adecuada y tener un archivo que sea agradeblemente legible. Esto lo conseguimos asignando a la variable &lt;em&gt;inner_indent&lt;/em&gt; la expresión &lt;code&gt;"    " * lvl&lt;/code&gt;. Así, tendremos cuatro espacios de indentación, tantas veces como el nivel en el de lista anidada en el que estemos.&lt;/p&gt;

&lt;p&gt;Así conseguimos algo como esto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;dl&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dt&amp;gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Bookmarks&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dl&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dt&amp;gt;&amp;lt;h3&amp;gt;&lt;/span&gt;News&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dt&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://www.youtube.com/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;YouTube&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dt&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://thedailywtf.com/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;The Daily WTF: Curious Perversions in Information Technology&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dt&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://www.facebook.com/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;fb&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dt&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://slashdot.org/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Slashdot: News for nerds, stuff that matters&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dl&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dl&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dt&amp;gt;&amp;lt;h3&amp;gt;&lt;/span&gt;Tools&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dt&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://www.ilovepdf.com/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;I Love PDF&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dt&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://ideone.com/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Ideone.com | Online IDE &lt;span class="err"&gt;&amp;amp;&lt;/span&gt; Debugging Tool&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/dt&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/dl&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dl&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recuperar los &lt;em&gt;bookmarks&lt;/em&gt; es un pelín más complicado. Lo lógico es utilizar la extensa librería estándar de &lt;strong&gt;Python&lt;/strong&gt;, y esta nos provee de la clase &lt;strong&gt;HTMLParser&lt;/strong&gt; en el módulo &lt;code&gt;html.parser&lt;/code&gt;. Esta librería funciona como eventos, es decir, el método &lt;code&gt;handle_starttag()' es llamado cuando se encuentra una marca o _tag_ abierta, y otro (&lt;/code&gt;handle_endtag()'), cuando se encuentra cerrada.&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BookmarksFileParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HTMLParser&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Parses a bookmarks file.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;State for the finite machine.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
        &lt;span class="n"&gt;TOP_LEVEL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;READ_ENTRY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;READ_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;READ_BOOKMARK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_starttag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_endtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;...&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&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;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;case&lt;/span&gt; &lt;span class="n"&gt;BookmarksFileParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;READ_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_current_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
                &lt;span class="n"&gt;case&lt;/span&gt; &lt;span class="n"&gt;BookmarksFileParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;READ_BOOKMARK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_curr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Bookmark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_url&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Así que tenemos que crearnos un pequeña máquina de estados para saber en qué punto estamos (de ahí el enumerado &lt;strong&gt;Status&lt;/strong&gt;). Por ejemplo, no es lo mismo encontrarse con un &lt;code&gt;h3&lt;/code&gt; cuando se espera el nombre de una lista, o un &lt;code&gt;a&lt;/code&gt; cuando se espera encontrarnos con un enlace.&lt;/p&gt;

&lt;p&gt;En el método &lt;code&gt;start_tag()&lt;/code&gt;:&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BookmarksFileParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HTMLParser&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# más cosas...
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle_starttag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;case&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;new_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BookmarkList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_current_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_curr&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_bms&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_bms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_list&lt;/span&gt;

                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_curr&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_curr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_curr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_list&lt;/span&gt;
                &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Created new list: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_curr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Previous list: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_curr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BookmarksFileParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TOP_LEVEL&lt;/span&gt;
            &lt;span class="n"&gt;case&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BookmarksFileParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;READ_ENTRY&lt;/span&gt;
            &lt;span class="n"&gt;case&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;h3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BookmarksFileParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;READ_NAME&lt;/span&gt;
            &lt;span class="n"&gt;case&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BookmarksFileParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;READ_BOOKMARK&lt;/span&gt;
                &lt;span class="n"&gt;href&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="nf"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;attrs&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;href&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;couldn&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t find HREF in a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="p"&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;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Status: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_status&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;¿Por qué es necesario &lt;code&gt;self._bms is None&lt;/code&gt;, o &lt;code&gt;self._curr is not None&lt;/code&gt;, cuando normalmente escribiríamos &lt;code&gt;self._bms&lt;/code&gt; o &lt;code&gt;not self._curr&lt;/code&gt;? Pues porque &lt;code&gt;_bms&lt;/code&gt; y &lt;code&gt;_curr&lt;/code&gt; son listas, retornarán &lt;strong&gt;False&lt;/strong&gt; no solo cuando son &lt;strong&gt;None&lt;/strong&gt;, sino también cuando &lt;a href="https://dev.to/baltasarq/la-importancia-de-no-ser-none-1k99"&gt;están vacías, es decir, no tienen elementos&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;El código completo de (así he llamado esta &lt;em&gt;app&lt;/em&gt;), &lt;a href="https://github.com/Baltasarq/Bookmank/" rel="noopener noreferrer"&gt;&lt;strong&gt;Bookmank&lt;/strong&gt; está en Github&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>hack</category>
      <category>python</category>
      <category>programming</category>
    </item>
    <item>
      <title>Python: la importancia de no ser None</title>
      <dc:creator>Baltasar García Perez-Schofield</dc:creator>
      <pubDate>Mon, 27 Apr 2026 09:44:10 +0000</pubDate>
      <link>https://dev.to/baltasarq/la-importancia-de-no-ser-none-1k99</link>
      <guid>https://dev.to/baltasarq/la-importancia-de-no-ser-none-1k99</guid>
      <description>&lt;p&gt;Pues verás, estaba yo programando tranquilamente (sí, yo soy de esos que &lt;em&gt;todavía&lt;/em&gt; programa &lt;em&gt;a mano&lt;/em&gt;, &lt;a href="https://dev.to/baltasarq/que-nos-ensena-el-fiasco-de-replit-4gbe"&gt;sin preguntarle a ninguna IA&lt;/a&gt;), una pequeña, muy pequeña herramienta relacionada con mi anterior entrada, sobre la &lt;a href="https://dev.to/baltasarq/como-guardar-tus-marcadores-web-28j8"&gt;organización de marcadores web&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;El progama utiliza la librería estándar de &lt;strong&gt;Python&lt;/strong&gt; para interpretar &lt;strong&gt;HTML&lt;/strong&gt;, de manera que el archivo de marcadores terminaría representado en memoria como un árbol cuya raiz es &lt;strong&gt;BookMarklist&lt;/strong&gt;, y que dentro puede tener marcadores (&lt;strong&gt;Bookmark&lt;/strong&gt;), u otras listas (&lt;strong&gt;BookmarkList&lt;/strong&gt;). Así que, cuando se llega a un &lt;code&gt;&amp;lt;dl&amp;gt;&lt;/code&gt;, debemos crear una nueva lista, y el comportamiento es distinto si estamos en una subrama, o si, en cambio, todavía no tenemos la lista raíz. Tal que así:&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;new_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BookmarkList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_current_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_curr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_bms&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_bms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_list&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_curr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_curr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_curr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El atributo &lt;code&gt;self._bms&lt;/code&gt; es la lista de marcadores raíz, mientras que &lt;code&gt;self._curr&lt;/code&gt; es la lista que está siendo rellenada con nuevos marcadores.&lt;/p&gt;

&lt;p&gt;El problema era que el código hacía cosas raras. De hecho, ¡asignaba dos veces la lista raíz! ¿Cómo es posible? Llegué a depurar el código, y comprobé que, aunque &lt;code&gt;self._bms&lt;/code&gt; apuntaba a un objeto, estaba entrando en el cuerpo del &lt;code&gt;if&lt;/code&gt;, y reemplazándolo por una nueva lista de todas maneras.&lt;/p&gt;

&lt;p&gt;Miraba la pantalla, incrédulo. ¿He encontrado un error en el intérprete de &lt;strong&gt;Python&lt;/strong&gt;? Me contesté a mi mismo: "Mmm... posible, pero improbable, muchacho."&lt;/p&gt;

&lt;p&gt;Como pasa siempre, aquel &lt;em&gt;hack&lt;/em&gt; de cinco minutos ya iba por la hora... con lo que resolví irme al baño. Y estaba entrando por la puerta cuando lo vi claro: ¿y si &lt;strong&gt;Python&lt;/strong&gt; piensa que &lt;code&gt;self._bms&lt;/code&gt; es  una lista? Y es que efectivamente, me había disparado yo mismo en el pie.&lt;/p&gt;

&lt;p&gt;Cuando escribimos &lt;code&gt;if x: ...&lt;/code&gt; o &lt;code&gt;if not x: ...&lt;/code&gt;, normalmente, esperamos estar comparando &lt;code&gt;x&lt;/code&gt; con &lt;strong&gt;None&lt;/strong&gt;, el equivalente a &lt;strong&gt;null&lt;/strong&gt; de &lt;strong&gt;Python&lt;/strong&gt;. Esto está bien en general, pero podemos encontrarnos las siguientes &lt;em&gt;conversiones a bool&lt;/em&gt; extras (también conocidas como expresiones &lt;em&gt;truthy&lt;/em&gt; o &lt;em&gt;falsy&lt;/em&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Expresión&lt;/th&gt;
&lt;th&gt;Valor booleano&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;""&lt;/td&gt;
&lt;td&gt;False&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;" "&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;"balta"&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;False&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.0&lt;/td&gt;
&lt;td&gt;False&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1.0&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;42.0&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[]&lt;/td&gt;
&lt;td&gt;False&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;[None]&lt;/td&gt;
&lt;td&gt;True&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;El problema es: ¿a partir de cuándo un objeto se considera una lista?&lt;br&gt;
Veámoslo.&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dataclass&lt;/span&gt;


&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Markdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;


&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MarkdownList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Markdown&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;MarkdownList&lt;/span&gt;&lt;span class="p"&gt;}):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getitem__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_l&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__str__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;str&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_l&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;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;m0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;m1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MarkdownList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;m2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MarkdownList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;m2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Markdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dev.to&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://dev.to/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La salida de este programa es la que esperamos. Es decir, si la referencia apunta a un objeto, &lt;strong&gt;True&lt;/strong&gt;, si apunta a &lt;strong&gt;None&lt;/strong&gt;, &lt;strong&gt;False&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bool(m0)=False isinstance(m0, list)=False
bool(m1)=True isinstance(m1, list)=False
bool(m2)=True isinstance(m2, list)=False
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No es la sobrecarga del operador &lt;code&gt;[]&lt;/code&gt; (mediante &lt;code&gt;__getitem__&lt;/code&gt;) lo que hace que &lt;strong&gt;Python&lt;/strong&gt; entienda el objeto &lt;strong&gt;MarkdownList&lt;/strong&gt; como una lista. Podemos crear el método &lt;code&gt;__iter__&lt;/code&gt; para que se pueda hacer directamente un bucle &lt;code&gt;for&lt;/code&gt; con el objeto &lt;strong&gt;MarkdownList&lt;/strong&gt;, por ejemplo, si &lt;code&gt;m1&lt;/code&gt; es una referencia a un objeto &lt;strong&gt;MarkdownList&lt;/strong&gt;: &lt;code&gt;for x in m1: print(x)&lt;/code&gt;. Pues aunque lo incluyamos, &lt;strong&gt;Python&lt;/strong&gt; no lo considerará una lista, y la salida será igual a la anterior. Lo que hace que &lt;strong&gt;Python&lt;/strong&gt; considere una clase como una lista (veremos má detalles sobre esto más adelante), es el método &lt;code&gt;__len__&lt;/code&gt;, lo que hace que podamos utilizar &lt;code&gt;len(m1)&lt;/code&gt; directamente, devolviéndonos el número de elementos almacenados.&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="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MarkdownList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&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="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Markdown&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;MarkdownList&lt;/span&gt;&lt;span class="p"&gt;}):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__getitem__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;item&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_l&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__iter__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# el "culpable"
&lt;/span&gt;    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__len__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;                        
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__str__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;str&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_l&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ahora la salida es:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bool(m0)=False isinstance(m0, list)=False
bool(m1)=False isinstance(m1, list)=False
bool(m2)=True isinstance(m2, list)=False
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nótese que &lt;strong&gt;Python&lt;/strong&gt; cambia el significado de &lt;em&gt;truthy&lt;/em&gt;/&lt;em&gt;falsy&lt;/em&gt;, pero desde luego no lo considera un objeto derivado de &lt;strong&gt;list&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Investigando un poco (ahora sí, con la ayuda de la IA, que es fantástica para resumir documentación), resulta que &lt;strong&gt;Python&lt;/strong&gt; maneja el concepto de protocolo; en este caso, el protocolo &lt;strong&gt;Sequence&lt;/strong&gt; se &lt;em&gt;activa&lt;/em&gt; si la clase contiene el método &lt;code&gt;__len__&lt;/code&gt;. Aún así, la clase base de &lt;strong&gt;MarkdownList&lt;/strong&gt; no cambia, sigue siendo &lt;strong&gt;Object&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Pero sí que hay una clase que de repente &lt;em&gt;reconoce&lt;/em&gt; a &lt;strong&gt;MarkdownList&lt;/strong&gt; como suya, y se trata de &lt;strong&gt;collections.abc.Sized&lt;/strong&gt;. Así, si cambiamos los &lt;em&gt;print&lt;/em&gt; del final del código anterior como sigue:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections.abc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Sized&lt;/span&gt;

&lt;span class="c1"&gt;# ...más cosas...
&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Sized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La salida pasa a ser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bool(m0)=False isinstance(m0, Sized)=False
bool(m1)=False isinstance(m1, Sized)=True
bool(m2)=True isinstance(m2, Sized)=True
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aunque, insisto de nuevo, la clase base de &lt;strong&gt;MarkdownList&lt;/strong&gt; no ha cambiado por el hecho de tener el método &lt;code&gt;__len__&lt;/code&gt; la clase &lt;strong&gt;Sized&lt;/strong&gt; la reconoce como suya mediante un mecanismo que se basa en el método &lt;code&gt;__subclasshook__&lt;/code&gt;. Este método comprueba la presencia de &lt;code&gt;__len__&lt;/code&gt; en la clase, y esto hace que pase a reconocerla como subclase, y que &lt;code&gt;isinstance(m1, Sized)&lt;/code&gt; pase a devolver &lt;strong&gt;True&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Es desde luego muy curioso. Igual que el hecho de que no es necesario que una clase defina el método &lt;code&gt;__iter__&lt;/code&gt; para que sea iterable, es suficiente con el método &lt;code&gt;__getitem__&lt;/code&gt;. Algo similar a lo que sucede con la clase &lt;strong&gt;collections.abc.Sized&lt;/strong&gt;, sucede con la clase &lt;strong&gt;collections.abc.Iterable&lt;/strong&gt;, que reconoce una clase como subclase si define el método &lt;code&gt;__iter__&lt;/code&gt;. Según parece, esto último es el comportamiento deseado, pero en las versiones anteriores del lenguaje el bucle &lt;code&gt;for x in l&lt;/code&gt;, en caso de que &lt;em&gt;l&lt;/em&gt; no tuviese un método &lt;code&gt;__iter__&lt;/code&gt;, pasaban a usar &lt;code&gt;__getitem__&lt;/code&gt; como último recurso, desde 0 hasta la captura de &lt;strong&gt;IndexError&lt;/strong&gt;; y este comportamiento se mantuvo hasta hoy. Curioso, teniendo en cuenta la ruptura con el pasado que se hizo a partir de Python 3.0.&lt;/p&gt;

&lt;p&gt;Ahora que sé esto, puedo seguir utilizando &lt;code&gt;if a&lt;/code&gt; esperando &lt;strong&gt;False&lt;/strong&gt; si &lt;em&gt;a&lt;/em&gt; es &lt;strong&gt;None&lt;/strong&gt;, pero teniendo el cuidado de respetar aquellas clases que definan &lt;code&gt;__len__&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Afortunadamente, hay una forma de comprobar si una referecia apunta a &lt;strong&gt;None&lt;/strong&gt; que va a tener siempre el mismo comportamiento:&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;m0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="n"&gt;m1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MarkdownList&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;m0&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# The following line is not executed
&lt;/span&gt;    &lt;span class="n"&gt;m0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Markdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dev.to&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://dev.to/&lt;/span&gt;&lt;span class="sh"&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;m1&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# Yes! This is now executed.
&lt;/span&gt;    &lt;span class="n"&gt;m1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Markdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dev.to&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://dev.to/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto es explícito y funciona siempre. Pero tengo que reconocer que me he llevado una sorpresa desagradable con todo este asunto. &lt;strong&gt;Python&lt;/strong&gt; es relativamente &lt;em&gt;puro&lt;/em&gt; y está lejos de ser un lenguaje de  programación que te sorprenda desagradablemente según el escenario (como *&lt;em&gt;C++&lt;/em&gt;, por poner un archiconocido ejemplo), y sin embargo, he &lt;em&gt;caído&lt;/em&gt; en esta trampa, que no me esperaba.&lt;/p&gt;

&lt;p&gt;Si seguimos el principio de que un lenguaje debe darte el menor número de sorpresas de este tipo, entonces podemos decir que &lt;strong&gt;Python&lt;/strong&gt; me ha decepcionado un tanto.&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>python</category>
      <category>null</category>
      <category>programming</category>
    </item>
    <item>
      <title>¿Cómo guardar tus marcadores web?</title>
      <dc:creator>Baltasar García Perez-Schofield</dc:creator>
      <pubDate>Mon, 20 Apr 2026 11:23:47 +0000</pubDate>
      <link>https://dev.to/baltasarq/como-guardar-tus-marcadores-web-28j8</link>
      <guid>https://dev.to/baltasarq/como-guardar-tus-marcadores-web-28j8</guid>
      <description>&lt;p&gt;El otro día llegó el momento: tenía que organizar mis marcadores web. Los &lt;em&gt;bookmarks&lt;/em&gt;. Un buen día, los había guardado desde Firefox, pero era plenamente consciente de que había muchos que ya no usaba. Así que me puse a ello.&lt;/p&gt;

&lt;p&gt;Como los había guardado desde Firefox, lo había hecho en el formato nativo de Firefox: HTML. Pues sí. Como sabemos, &lt;a href="https://es.wikipedia.org/wiki/Mozilla_Firefox" rel="noopener noreferrer"&gt;Firefox&lt;/a&gt; no es sino la versión liberada de &lt;a href="https://es.wikipedia.org/wiki/Netscape_Navigator" rel="noopener noreferrer"&gt;Netscape Navigator&lt;/a&gt;, el navegador privativo que reinó sobre Internet y los usuarios, hasta que fue destronado por &lt;a href="https://es.wikipedia.org/wiki/Internet_Explorer" rel="noopener noreferrer"&gt;Internet Explorer&lt;/a&gt;, el infame navegador de Microsoft. Infame porque Microsoft, para atacar a Netscape, lo incluyó de forma gratuita en todos los Windows, llegando a aducir que era un componente central del sistema operativo ante la eventual demanda por monopolio.&lt;/p&gt;

&lt;p&gt;Pero centrémonos: el caso es que Netscape Navigator exportaba sus &lt;em&gt;bookmarks&lt;/em&gt; (también conocidos como &lt;em&gt;favoritos&lt;/em&gt;) en formato HTML. De hecho, se puede abrir en cualquier navegador y utilizar sus enlaces.&lt;/p&gt;

&lt;p&gt;Así que me dispongo a buscar un formato más extendido: ¿CSV? Sí, pero como siempre, no está muy estandarizado. ¿JSON? Vale, pero solo es soportado por algunos navegadores, y de nuevo el formato no está muy estandarizado. Pero entonces, ¿cuál es el formato más estándar para almacenar enlaces web, de manera que puedan importarse en cualquier navegador? Pues el formato en el que ya los tenía, HTML. ¿Bien, no?&lt;/p&gt;

&lt;p&gt;Bueno, al abrirlo, aquello es un desastre. Hay un montón de ruido. Muchos campos binarios en formato &lt;em&gt;base64.&lt;/em&gt; Parece que lo que trata de hacer el archivo es guardar los iconos (&lt;em&gt;favicon&lt;/em&gt;), para cada página enlazada.&lt;/p&gt;

&lt;p&gt;Lo estudio con más detenimiento: mi objetivo es que aquel archivo sea manejable, así que intento ver qué puedo eliminar. Por ejemplo, a continuación pongo un enlace tal cuál aparece bajo un epígrafe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="cp"&gt;&amp;lt;!DOCType NETSCAPE-Bookmark-file-1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"text/html; charset=UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Bookmarks&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Bookmarks&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&amp;lt;dt&amp;gt;&amp;lt;p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;ADD_DATE=&lt;/span&gt;&lt;span class="s"&gt;"1079992089"&lt;/span&gt; &lt;span class="na"&gt;LAST_VISIT=&lt;/span&gt;&lt;span class="s"&gt;"1132078168"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Tools&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;dl&amp;gt;&amp;lt;p&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;dt&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;ADD_DATE=&lt;/span&gt;&lt;span class="s"&gt;"1079992089"&lt;/span&gt; &lt;span class="na"&gt;LAST_VISIT=&lt;/span&gt;&lt;span class="s"&gt;"1132078168"&lt;/span&gt; &lt;span class="na"&gt;FAV_ICON=&lt;/span&gt;&lt;span class="s"&gt;"...base64:dfdfdfldmndfljdpfudñfdmfdmfdoipjifohnlñjkdfngspopshgosigvnsǵnsñglgjagnaslgr"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"http://www.ilovepdf.com/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;I Love PDF&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
...
    &lt;span class="nt"&gt;&amp;lt;/dl&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Podemos extraer la siguiente información:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Marca&lt;/th&gt;
&lt;th&gt;Explicación&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;marca indiferentemente un marcador o un epígrafe.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;marca de una lista de marcadores o epígrafes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;h3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;marca de los epígrafes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;a&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;marca de un marcador, como un hiperenlace.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;add_date&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;campo de la fecha de adición, como un entero.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;last_visit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;campo del último acceso al marcador.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;'fav_icon`&lt;/td&gt;
&lt;td&gt;campo que contiene una imagen en base64.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;marca para párrafos separando marcadores.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hr&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;marca para los separadores de los menús.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A mi el icono no me interesa. La fechas de adición y acceso tampoco me interesan. Los párrafos son totalmente accesorios. El preámbulo parece conveniente mantenerlo. Pero al final, solo es necesario listar epígrafes con listas de hiperenlaces.&lt;/p&gt;

&lt;p&gt;Así el código anterior se simplifica notablemente, y permite, ahora sí, que el archivo sea manejable y siga siendo funcional.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;html&lt;br&gt;
    &amp;lt;!DOCType NETSCAPE-Bookmark-file-1&amp;gt;&lt;br&gt;
    &amp;lt;meta http-equiv="Content-Type" content="text/html; charset=UTF-8"&amp;gt;&lt;br&gt;
    &amp;lt;title&amp;gt;Bookmarks&amp;lt;/title&amp;gt;&lt;br&gt;
    &amp;lt;h1&amp;gt;Bookmarks&amp;lt;/h1&amp;gt;&lt;br&gt;
    &amp;lt;dl&amp;gt;&lt;br&gt;
    &amp;lt;dt&amp;gt;&amp;lt;h3&amp;gt;Noticias&amp;lt;/h3&amp;gt;&lt;br&gt;
        &amp;lt;dl&amp;gt;&lt;br&gt;
            &amp;lt;dt&amp;gt;&amp;lt;a href="https://www.youtube.com/"&amp;gt;YouTube&amp;lt;/a&amp;gt;&lt;br&gt;
            &amp;lt;dt&amp;gt;&amp;lt;a href="https://www.google.com/calendar/"&amp;gt;Google Calendar&amp;lt;/a&amp;gt;&lt;br&gt;
            &amp;lt;dt&amp;gt;&amp;lt;a href="http://thedailywtf.com/"&amp;gt;The Daily WTF: Curious Perversions in Information Technology&amp;lt;/a&amp;gt;&lt;br&gt;
            &amp;lt;dt&amp;gt;&amp;lt;a href="https://slashdot.org/"&amp;gt;Slashdot: News for nerds, stuff that matters&amp;lt;/a&amp;gt;&lt;br&gt;
            &amp;lt;dt&amp;gt;&amp;lt;a href="http://www.codinghorror.com/blog/"&amp;gt;Coding Horror&amp;lt;/a&amp;gt;&lt;br&gt;
            &amp;lt;dt&amp;gt;&amp;lt;a href="http://www.osnews.com/"&amp;gt;OSNews&amp;lt;/a&amp;gt;&lt;br&gt;
            &amp;lt;dt&amp;gt;&amp;lt;a href="http://blogs.msdn.microsoft.com/oldnewthing/"&amp;gt;The Old New Thing&amp;lt;/a&amp;gt;&lt;br&gt;
            &amp;lt;dt&amp;gt;&amp;lt;a href="http://www.zonadepruebas.com/"&amp;gt;ZonaDePruebas&amp;lt;/a&amp;gt;&lt;br&gt;
            &amp;lt;dt&amp;gt;&amp;lt;a href="https://blog.cleancoder.com/"&amp;gt;Clean Coder Blog&amp;lt;/a&amp;gt;&lt;br&gt;
        &amp;lt;/dl&amp;gt;&lt;br&gt;
    ...&lt;br&gt;
    &amp;lt;/dl&amp;gt;&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;¡Y se sigue pudiendo manejar como un archivo HTML normal y corriente!&lt;/p&gt;

&lt;h1&gt;Bookmarks&lt;/h1&gt;

&lt;dl&gt;
&lt;dt&gt;
&lt;h3&gt;Noticias&lt;/h3&gt;
    &lt;/dt&gt;
&lt;dl&gt;
        &lt;dt&gt;
&lt;a href="https://www.youtube.com/" rel="noopener noreferrer"&gt;YouTube&lt;/a&gt;
        &lt;dt&gt;
&lt;a href="https://www.google.com/calendar/" rel="noopener noreferrer"&gt;Google Calendar&lt;/a&gt;
        &lt;dt&gt;
&lt;a href="http://thedailywtf.com/" rel="noopener noreferrer"&gt;The Daily WTF: Curious Perversions in Information Technology&lt;/a&gt;
        &lt;dt&gt;
&lt;a href="https://slashdot.org/" rel="noopener noreferrer"&gt;Slashdot: News for nerds, stuff that matters&lt;/a&gt;
        &lt;dt&gt;
&lt;a href="http://www.codinghorror.com/blog/" rel="noopener noreferrer"&gt;Coding Horror&lt;/a&gt;
        &lt;dt&gt;
&lt;a href="http://www.osnews.com/" rel="noopener noreferrer"&gt;OSNews&lt;/a&gt;
        &lt;dt&gt;
&lt;a href="http://blogs.msdn.microsoft.com/oldnewthing/" rel="noopener noreferrer"&gt;The Old New Thing&lt;/a&gt;
        &lt;dt&gt;
&lt;a href="http://www.zonadepruebas.com/" rel="noopener noreferrer"&gt;ZonaDePruebas&lt;/a&gt;
        &lt;dt&gt;
&lt;a href="https://blog.cleancoder.com/" rel="noopener noreferrer"&gt;Clean Coder Blog&lt;/a&gt;
    &lt;/dt&gt;
&lt;/dt&gt;
&lt;/dt&gt;
&lt;/dt&gt;
&lt;/dt&gt;
&lt;/dt&gt;
&lt;/dt&gt;
&lt;/dt&gt;
&lt;/dt&gt;
&lt;/dl&gt;

&lt;p&gt;...&lt;br&gt;
&lt;/p&gt;
&lt;/dl&gt;

&lt;p&gt;¿Y tú, cómo manejas tus marcadores?&lt;/p&gt;

</description>
      <category>bookmarks</category>
      <category>spanish</category>
      <category>html</category>
    </item>
    <item>
      <title>Evolución de los precios de los carburantes en España</title>
      <dc:creator>Baltasar García Perez-Schofield</dc:creator>
      <pubDate>Mon, 13 Apr 2026 11:15:17 +0000</pubDate>
      <link>https://dev.to/baltasarq/evolucion-de-los-precios-de-los-carburantes-en-espana-3nof</link>
      <guid>https://dev.to/baltasarq/evolucion-de-los-precios-de-los-carburantes-en-espana-3nof</guid>
      <description>&lt;p&gt;En esta entrada, me planteé corroborar la impresión general de que los precios de los carburantes en España se deben a la pura y simple especulación, y no directamente del precio del petróleo. Para hacer esto, la elección parecía obvia: Python con Pandas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Origen de los datos
&lt;/h2&gt;

&lt;p&gt;Pude obtener la &lt;a href="https://es.investing.com/commodities/brent-oil-historical-data" rel="noopener noreferrer"&gt;evolución del precio del barril Brent desde una plataforma de inversión llamada investing.com&lt;/a&gt;. Los precios de la Gasolina y el Gasóil, los obtuve de la &lt;a href="https://www.epdata.es/evolucion-precio-gasolina-diesel-espana/ed822d1e-9d2c-487b-ad94-1b607dc42669" rel="noopener noreferrer"&gt;plataforma de datos de Europa Press&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Lo ideal hubiera sido tomar todos estos datos de los datos publicados por el ministerio correspondiente del gobierno de España. Sin embargo, aunque los datos sí se ofrecen, se hace en formato tipo texto, como documento PDF, de manera que los datos no son directamente utilizables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparando los datos
&lt;/h2&gt;

&lt;p&gt;Primero había que limpiar los datos. Se puede hacer de muchas formas, pero por ejemplo podemos transformar los precios en dólares que utilizan comas, de esta manera:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pandas&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;

&lt;span class="n"&gt;df&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;historico_precio-petroleo_brent.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Precio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_numeric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Precio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;,&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;historico_precio-petroleo_brent.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En este caso, se transforma la columna de una columna de texto (de ahí que se acceda a &lt;code&gt;df.Precio.str.replace(',', '.')&lt;/code&gt;. Esta propiedad &lt;code&gt;str&lt;/code&gt; le dice a Pandas que debe aplicar el cambio a todos los valores de la columna. Utilizamos &lt;code&gt;pd.to_numeric()&lt;/code&gt; para también convertir a número todos los valores.&lt;/p&gt;

&lt;p&gt;Pero lo más importante es que el histórico de precios del barril Brent se ofrece mediante la fecha del domingo de cada semana, y el histórico de precios de carburantes en España, mediante el año y el número de semana de ese año. Podemos convertir la fecha del domingo de los datos del barril Brent en la descomposición en &lt;strong&gt;Año&lt;/strong&gt; y &lt;strong&gt;Semana&lt;/strong&gt;.&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;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fecha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fecha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;reversed&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;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fecha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fecha&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Semana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fecha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isocalendar&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;week&lt;/span&gt;
&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Año&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fecha&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como las fechas se ofrecen en formato español, primero se divide por el carácter '/', se invierten su orden, y se vuelven a combinar usando el carácter '-'. Finalmente, se convierten en datos fecha desde sus valores como texto, y desde ahí se extraen &lt;strong&gt;Año&lt;/strong&gt; y &lt;strong&gt;Semana&lt;/strong&gt;. Ahora ya es posible combinar ambos &lt;em&gt;datasets&lt;/em&gt;, &lt;em&gt;dfbrent&lt;/em&gt; (el precio del barril Brent), y &lt;em&gt;dfes&lt;/em&gt; (el precio de los carburantes en España).&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;dfr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dfbrent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dfes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Año&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Semana&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;dfr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dfr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dfr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Gasolina&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;notna&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;dfr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Gasóil&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;notna&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
&lt;span class="n"&gt;dfr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;normalized_data.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Combinamos ambos &lt;em&gt;datasets&lt;/em&gt;, y filtramos aquellas filas que tienen NaN como valores para los precios de la &lt;strong&gt;Gasolina&lt;/strong&gt; o el &lt;strong&gt;Gasóil&lt;/strong&gt; (tenemos más datos del dataset &lt;em&gt;dfbrent&lt;/em&gt; que del &lt;em&gt;dfes&lt;/em&gt;). Los datos se guardan en el archivo &lt;code&gt;normalized_data.csv&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ahora ya podemos analizar un poco los datos. Si ejecutamos lo siguiente, estaremos agrupando los datos por año, siempre que el barril de Brent estuviera por encima de 110.&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;df_by_year&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;df&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Brent&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;110&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;groupby&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Año&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Semana&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Brent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Gasolina&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Gasóil&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;

    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;df_by_year&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;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;df_by_year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Year: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;group&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Datos más relevantes
&lt;/h2&gt;

&lt;p&gt;Los datos más relevantes se obtienen filtrando por el precio del mayor Brent mayor que 110, de manera que se observa cómo los precios sí crecen con el precio del barril, pero no decrecen cuando este decrece. Además, los precios actuales no se corresponden con el precio del barril Brent, pues podemos ver como el barril Brent en la semana 9 de 2022 estuvo a 112$, y en cambio el precio del gasóil estaba por debajo de 1.5€/L. En cambio, en la semana 12 de 2026 el precio del barril Brent estaba al mismo precio, y en cambio el precio del gasóil se sitúa en 1.84€/L., aún con IVA reducido. Pura especulación.&lt;/p&gt;

&lt;h3&gt;
  
  
  Año: 2022
&lt;/h3&gt;

&lt;p&gt;Precio del barril Brent en dólares.&lt;br&gt;
Precio de la Gasolina y el Gasóil en Euros/Litro.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Semana&lt;/th&gt;
&lt;th&gt;Brent&lt;/th&gt;
&lt;th&gt;Gasolina&lt;/th&gt;
&lt;th&gt;Gasóil&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;118.11&lt;/td&gt;
&lt;td&gt;1.58&lt;/td&gt;
&lt;td&gt;1.46&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;112.67&lt;/td&gt;
&lt;td&gt;1.59&lt;/td&gt;
&lt;td&gt;1.48&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;120.65&lt;/td&gt;
&lt;td&gt;1.68&lt;/td&gt;
&lt;td&gt;1.58&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;111.70&lt;/td&gt;
&lt;td&gt;1.82&lt;/td&gt;
&lt;td&gt;1.84&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;111.55&lt;/td&gt;
&lt;td&gt;1.82&lt;/td&gt;
&lt;td&gt;1.85&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;112.55&lt;/td&gt;
&lt;td&gt;1.84&lt;/td&gt;
&lt;td&gt;1.87&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;119.43&lt;/td&gt;
&lt;td&gt;1.88&lt;/td&gt;
&lt;td&gt;1.91&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;119.72&lt;/td&gt;
&lt;td&gt;1.90&lt;/td&gt;
&lt;td&gt;1.89&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;122.01&lt;/td&gt;
&lt;td&gt;1.94&lt;/td&gt;
&lt;td&gt;1.87&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;113.12&lt;/td&gt;
&lt;td&gt;1.97&lt;/td&gt;
&lt;td&gt;1.85&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;113.12&lt;/td&gt;
&lt;td&gt;2.05&lt;/td&gt;
&lt;td&gt;1.92&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;td&gt;111.63&lt;/td&gt;
&lt;td&gt;2.12&lt;/td&gt;
&lt;td&gt;2.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;110.01&lt;/td&gt;
&lt;td&gt;2.07&lt;/td&gt;
&lt;td&gt;2.02&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Año 2026
&lt;/h3&gt;

&lt;p&gt;Precio del barril Brent en dólares.&lt;br&gt;
Precio de la Gasolina y el Gasóil en Euros/Litro.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Semana&lt;/th&gt;
&lt;th&gt;Brent&lt;/th&gt;
&lt;th&gt;Gasolina&lt;/th&gt;
&lt;th&gt;Gasóil&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;112.19&lt;/td&gt;
&lt;td&gt;1.60&lt;/td&gt;
&lt;td&gt;1.65&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;112.57&lt;/td&gt;
&lt;td&gt;1.71&lt;/td&gt;
&lt;td&gt;1.84&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Gráfica de evolución de los precios
&lt;/h3&gt;

&lt;p&gt;Se aprecia como los precios en España siguen unos dientes de sierra más suaves, pues "tardan" mucho más en bajar que el precio del barril Brent.&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%2Fpl24c9c1yl16m62clx2x.jpg" 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%2Fpl24c9c1yl16m62clx2x.jpg" alt="Evolución de los precios de carburantes en España contra el precio del barril Brent" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Ejecución
&lt;/h2&gt;

&lt;p&gt;Estos datos están disponible en el repositorio de la &lt;a href="https://github.com/Baltasarq/EvolucionPreciosCarburantes" rel="noopener noreferrer"&gt;evolución de los precios del carburante en España&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;La ejecución puede realizarse directamente con &lt;code&gt;plot_results.py&lt;/code&gt;, pero a continuación se muestra la forma de invocación por módulo, anteponiendo &lt;code&gt;python -m&lt;/code&gt;, al ser más versátil. Las opciones son &lt;code&gt;-d&lt;/code&gt;, que muestra los datos por la salida estándar, y &lt;code&gt;-g&lt;/code&gt;. que muestra el gráfico que genera &lt;code&gt;pyploy&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;python &lt;span class="nt"&gt;-m&lt;/span&gt; plot_results.py &lt;span class="nt"&gt;-d&lt;/span&gt;
Year: 2022
...

&lt;span class="nv"&gt;$ &lt;/span&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; plot_results.py &lt;span class="nt"&gt;-g&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si te gusta este contenido, puedes encontrar más en mi libro &lt;a href="https://www.bubok.es/libros/251177/introduccion-a-la-programacion-con-python" rel="noopener noreferrer"&gt;&lt;em&gt;Introducción a la programación con Python&lt;/em&gt;&lt;/a&gt;. ¡Aprende mientras aplicas tus conocimientos en casos reales!&lt;/p&gt;

</description>
      <category>python</category>
      <category>spanish</category>
      <category>statistics</category>
    </item>
    <item>
      <title>El tercer intento de API de fechas de Java</title>
      <dc:creator>Baltasar García Perez-Schofield</dc:creator>
      <pubDate>Wed, 08 Apr 2026 09:35:03 +0000</pubDate>
      <link>https://dev.to/baltasarq/el-tercer-intento-de-api-de-fechas-de-java-2k95</link>
      <guid>https://dev.to/baltasarq/el-tercer-intento-de-api-de-fechas-de-java-2k95</guid>
      <description>&lt;p&gt;Java es un lenguaje de pogramación con algunos años ya. El problema de los calendarios y las fechas es conocido: cada cuatro años se añade un día más, pero incluso en ciertos escenarios se añade un segundo a un año arbitrariamente. Como esto no se podía representar con la clase &lt;a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Date.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Date&lt;/strong&gt;&lt;/a&gt; tal cual estaba planteada, decidieron desactualizar varios de sus métodos, de manera que fuera solo parcialmente funcional, y promocionaron el uso de &lt;a href="https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/Calendar.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Calendar&lt;/strong&gt;&lt;/a&gt; en su lugar.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Calendar&lt;/strong&gt; es una clase mucho más capaz, y solo se le pueden achacar problemas de &lt;em&gt;edad&lt;/em&gt;, como el hecho de que no utilice enumerados (&lt;em&gt;enum&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Fueron dos intentos ímprobos, y el segundo de muy buen resultado y ejecución, sobre una materia que ya de por sí es difícil. Por si fuera poco, la información mundial de &lt;em&gt;time zones&lt;/em&gt; (zonas horarias), puede cambiar en cualquier momento, por lo que es una API que debe ser actualizada frecuentemente.&lt;/p&gt;

&lt;p&gt;Así que los ingenieros de Java han creado &lt;a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/LocalDateTime.html" rel="noopener noreferrer"&gt;&lt;strong&gt;LocalDate&lt;/strong&gt;&lt;/a&gt; en el paquete &lt;em&gt;java.time&lt;/em&gt;, el tercer intento de plantear una solución a la fecha y hora (&lt;em&gt;spoiler&lt;/em&gt;: seguirá teniendo defectos, y se planteará una nueva alternativa).&lt;/p&gt;

&lt;p&gt;El caso es que la nueva clase &lt;strong&gt;LocalDate&lt;/strong&gt; proporciona el método &lt;em&gt;getMonth()&lt;/em&gt;, que devuelve el mes que está representado en la fecha en sí. Este método devuelve un enumerado &lt;a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/time/Month.html" rel="noopener noreferrer"&gt;&lt;strong&gt;Month&lt;/strong&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;fecha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="no"&gt;MESES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]{&lt;/span&gt; &lt;span class="s"&gt;"Enero"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Febrero"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Marzo"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Abril"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Mayo"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Junio"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Julio"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Agosto"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Septiembre"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Octubre"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Noviembre"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Diciembre"&lt;/span&gt; &lt;span class="o"&gt;};&lt;/span&gt;

&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"Fecha: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;fecha&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"Mes (val): "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;MESES&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="n"&gt;fecha&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMonth&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"Mes (ord): "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;MESES&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="n"&gt;fecha&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMonth&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;ordinal&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La salida de este código es:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fecha: 2026-04-07
Mes (val): Mayo
Mes (ord): Abril
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;¿Hemos creado una fecha de marzo o de abril? A ver, el código se ejecutó el 7 de abril de 2026, fecha de escritura de este texto. Por lo tanto, la fecha (que, correctamente, se ofrece en formato ISO): 2026-04-07 se corresponde perfectamente. El problema viene cuando accedemos al mes con &lt;strong&gt;LocalDate&lt;/strong&gt;.&lt;em&gt;getMonth()&lt;/em&gt;. Se devuelve una de las constantes JANUARY, FEBRUARY, MARCH... Y aquí es cuando comienzan los problemas. Si accedemos al valor del enumerado &lt;strong&gt;Month&lt;/strong&gt;, nos encontramos con que JANUARY es... 1. Porque sí, se nos ofrece el valor del enumerado a través de &lt;em&gt;getValue()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Por eso, &lt;code&gt;fecha.getMonth()&lt;/code&gt; devuelve 4, y entonces &lt;code&gt;MESES[ fecha.getMonth().getValue() ]&lt;/code&gt; devuelve la 5ª posición en el vector, y así el código retorna, incorrectamente, &lt;code&gt;Mayo&lt;/code&gt;. Nos han estropeado la forma acostumbrada de obtener el nombre del mes.&lt;/p&gt;

&lt;p&gt;Al menos, no han tocado el valor del propio enumerado. Es decir, el valor &lt;strong&gt;Month&lt;/strong&gt;.&lt;em&gt;ordinal()&lt;/em&gt; sigue siendo el &lt;em&gt;correcto&lt;/em&gt;: 3. Para obtener el mismo resultado mediante &lt;strong&gt;LocalDate&lt;/strong&gt;.&lt;em&gt;getValue()&lt;/em&gt;, tendremos que restar 1, de ahí que el código anterior deba ser corregido a:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"Mes (val): "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;MESES&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="n"&gt;fecha&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMonth&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"Mes (ord): "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;MESES&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="n"&gt;fecha&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMonth&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;ordinal&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A partir de ahora, será necesario escribir &lt;code&gt;MESES[ fecha.getMonth().getValue() -1]&lt;/code&gt; si utilizamos &lt;em&gt;getValue()&lt;/em&gt;, y continuar escribiendo el mismo código si utilizamos &lt;em&gt;ordinal()&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Y no es que &lt;em&gt;getValue()&lt;/em&gt; esté &lt;em&gt;mal&lt;/em&gt;. Veámos la siguiente tabla:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Método&lt;/th&gt;
&lt;th&gt;Basado en&lt;/th&gt;
&lt;th&gt;Ejemplo&lt;/th&gt;
&lt;th&gt;Concepción&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;getValue()&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Enero == 1&lt;/td&gt;
&lt;td&gt;Intuitiva&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ordinal()&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Enero == 0&lt;/td&gt;
&lt;td&gt;Costumbre&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Y es que, efectivamente, la costumbre entre programadores es que los meses empiecen en 0, de manera que se pueda indexar directamente en un vector de meses, como en el ejemplo. Sin embargo, reconoceremos que lo &lt;em&gt;normal&lt;/em&gt;, lo que sería intuitivo para un no programador sería que los meses empezasen en 1, es decir, que el índice de la posición de Enero fuese 1.&lt;/p&gt;

&lt;p&gt;¿Y por qué me quejo? Pues porque a veces es mejor dejar las cosas como están. Cuando a tu alrededor todo funciona de una manera, cambiar a otra, aunque sea la más correcta, no necesariamente es la mejor idea. Especialmente cuando esa costumbre se ha extendido durante años.&lt;/p&gt;

&lt;p&gt;Estamos hablando, en definitiva, de un &lt;em&gt;estándar de facto&lt;/em&gt; (que los meses se indexen desde 0), frente a lo que podría ser un estándar por lo intuitivo (o un estándar concreto, el ISO 8601). Lo único cierto, es que desde C las colecciones comienzan a contar en 0, no en 1 (solo BASIC y Pascal (cuando se especifican los límites), lo permiten así).&lt;/p&gt;

&lt;p&gt;En C, la forma de obtener el mes es dejando que el sistema rellene una estructura &lt;code&gt;tm&lt;/code&gt;.&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="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;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;time.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&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;time_t&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;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;tm&lt;/span&gt; &lt;span class="n"&gt;tm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;localtime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;t&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;"now: %4d-%02d-%02d&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;tm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tm_year&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tm_mon&lt;/span&gt; &lt;span class="o"&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;tm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tm_mday&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;La función &lt;em&gt;localtime()&lt;/em&gt; es una llamada al kernel del sistema operativo.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Lenguaje&lt;/th&gt;
&lt;th&gt;Método o función&lt;/th&gt;
&lt;th&gt;Ejemplo&lt;/th&gt;
&lt;th&gt;Basado en&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;td&gt;localtime()&lt;/td&gt;
&lt;td&gt;tm-&amp;gt;tm_mon&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;td&gt;Calendar.get()&lt;/td&gt;
&lt;td&gt;cal.get( Calendar.MONTH )&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JavaScript&lt;/td&gt;
&lt;td&gt;Date.getMonth()&lt;/td&gt;
&lt;td&gt;new Date().getMonth()&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Perl&lt;/td&gt;
&lt;td&gt;localtime()&lt;/td&gt;
&lt;td&gt;$d, $m, $y = localtime()&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PHP&lt;/td&gt;
&lt;td&gt;date(fmt)&lt;/td&gt;
&lt;td&gt;$current_month = date('n');&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;datetime.month&lt;/td&gt;
&lt;td&gt;datetime.now().month&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C#&lt;/td&gt;
&lt;td&gt;DateTime.month&lt;/td&gt;
&lt;td&gt;DateTime.now().Month&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Nim&lt;/td&gt;
&lt;td&gt;Month int&lt;/td&gt;
&lt;td&gt;int(month(now()))&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gleam&lt;/td&gt;
&lt;td&gt;month_int()&lt;/td&gt;
&lt;td&gt;month_int(datetime.now())&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;V&lt;/td&gt;
&lt;td&gt;Time.month&lt;/td&gt;
&lt;td&gt;time.now().month&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C3&lt;/td&gt;
&lt;td&gt;datetime.month&lt;/td&gt;
&lt;td&gt;datetime::now().month&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;chrono::Datelike&lt;/td&gt;
&lt;td&gt;chrono::now().month&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go&lt;/td&gt;
&lt;td&gt;Month()&lt;/td&gt;
&lt;td&gt;time.Now().Month()&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zig&lt;/td&gt;
&lt;td&gt;localtime()&lt;/td&gt;
&lt;td&gt;tm-&amp;gt;tm_mon&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;¿Qué puedo decir? Me quedo con que tengo razón, solo porque para eso es mi columna, la del viejo programador malhumorado.&lt;/p&gt;

&lt;p&gt;Pero no se puede ignorar que, a medida que surgen lenguajes de programación más recientes (modernos), cada vez más se fijan en estándares como el RFC 3339, o el ISO 8601, y estos estándares identifican unívocamente enero con el índice 1, lo que no es sorprendente. Al fin y al cabo, que enero se identifique con 0 solo depende de una optimización del lenguaje de programación C, que se basa en que el nombre de un array en C es un puntero al primer elemento, es decir &lt;code&gt;*v&lt;/code&gt;, &lt;code&gt;*(v + 0)&lt;/code&gt; y &lt;code&gt;v[ 0 ]&lt;/code&gt; son todas distintas formas de referirse a ese primer elemento.&lt;/p&gt;

&lt;p&gt;En cualquier caso, que sea más intuitivo no hace que sepas de antemano con qué te vas a encontrar. Y es que, uses la librería que uses, no te quedará más remedio que comprobar si el número de mes se basa en 0 o en 1, y entonces, decidir si debes restar uno. O sumarlo. O no. Yo qué sé.&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>java</category>
      <category>time</category>
      <category>date</category>
    </item>
    <item>
      <title>Street Smart Coding</title>
      <dc:creator>Baltasar García Perez-Schofield</dc:creator>
      <pubDate>Wed, 01 Apr 2026 13:15:38 +0000</pubDate>
      <link>https://dev.to/baltasarq/cesar-aguirres-street-smart-coding-31ec</link>
      <guid>https://dev.to/baltasarq/cesar-aguirres-street-smart-coding-31ec</guid>
      <description>&lt;p&gt;Hace ya algún tiempo que tengo &lt;a href="http://bit.ly/csarag-ssc" rel="noopener noreferrer"&gt;&lt;em&gt;Street Smart Coding&lt;/em&gt;&lt;/a&gt;, un libro de programación escrito en inglés, por &lt;a href="https://dev.to/canro91"&gt;César Aguirre&lt;/a&gt;, uno de los escritores más prolíficos aquí en &lt;a href="http://dev.to"&gt;dev.to&lt;/a&gt;, y me propuse escribir una revisión del mismo.&lt;/p&gt;

&lt;p&gt;Cuando lees este libro, te das cuenta de un detalle importantísimo, y que para mi valida su contenido como para recomendarlo a cualquiera: está escrito desde la experiencia. Es decir, no te dice cómo hay que hacer las cosas, sino cómo aprendió personalmente cómo hay que hacer las cosas.&lt;/p&gt;

&lt;p&gt;Pongamos un ejemplo. He escogido este porque no tiene que ver directamente con programación, sino con lo que hoy en día se denomina &lt;em&gt;soft skills&lt;/em&gt;. A mi me gusta más el término acuñado ya hace unos años: &lt;em&gt;peopleware&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;That day, I learned it's never just five minutes. Next time someone says "just five minutes," remember it could turn into 3:00AM the next day. Say no.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Encontraremos este párrafo en el capítulo &lt;em&gt;Learn to say no&lt;/em&gt;, junto con &lt;em&gt;Saying yes to everything often means saying no to the people who matter most. You need healthy boundaries instead. Sometimes, all it takes is one single word: no&lt;/em&gt;. Efectivamente, aprender a decir &lt;strong&gt;no&lt;/strong&gt; es muy importante, tan importante que si no lo haces, puede implicar decir &lt;em&gt;no&lt;/em&gt; a tus más allegados: los límites son importantes. ¿Pero, cómo puedes explicarle a un desarrollador &lt;em&gt;junior&lt;/em&gt; que es importante saber decir &lt;strong&gt;no&lt;/strong&gt;? La única forma es hacerlo a través de una experiencia personal (experiencia que encontraremos en ese capítulo, junto a las conclusiones ya mencionadas), y ahí es donde está la fortaleza real de este libro.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Never, ever, ever tell anyone they're wrong&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Efectivamente, siempre hay una forma distinta de hablar con alguien, incluso convercerlo de algo, más productiva, en lugar de decirle: "no, eso está mal." Personalmente, esto lo aprendí en mi trabajo como profesor; pronto me di cuenta de que decirle a un alumno que algo que había dicho estaba mal era la mejor forma de conseguir que dejase de participar en clase. Quizás en una relación profesor-alumno podemos asumir que decir "no, eso está mal", debería ser una posibilidad real y por lo tanto constructiva, pero no lo es. Así, una pequeña modificación de esa frase tan simple podría ser: "eso es cierto, pero también hay que tener en cuenta..." Y es que, la confianza de tu interlocutor en ti termina cuando le dices que algo está mal. Es indiferente si tienes razón o no.&lt;/p&gt;

&lt;p&gt;Vamos con algo de código, C# en este caso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;movies&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;"The Matrix"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Inception"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Interstellar"&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;movies&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="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="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;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;movies&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&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="n"&gt;result&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="n"&gt;movies&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;César nos explica que podemos sustituir el código de arriba por el siguiente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;movies&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;"The Matrix"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Inception"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Interstellar"&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;result&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="nf"&gt;Join&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="n"&gt;movies&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? Que hay código que no escribimos nosotros. Efectivamente, siempre que abordo un proyecto cualquiera, siempre tengo que &lt;em&gt;descender&lt;/em&gt; por debajo del nivel de abstracción en el que yo deseo estar; es decir, siempre que me planteo escribir código como el de arriba, me pregunto. ¿Para algo tan básico y repetitivo, no hay ya una función/clase/biblioteca que lo haga?&lt;/p&gt;

&lt;p&gt;Podemos encontrar este ejemplo en &lt;em&gt;Know your standard library&lt;/em&gt;. ¿Por qué no escribir código es algo importante? Porque podemos apoyarnos en código ya escrito, de manera que nos evitemos una tarea menor y repetitiva, reutilizando código que alguien ya se ha molestado en desarrollar y probar para nosotros, lo que nos permite centrarnos en el código realmente importante. No reinventes la rueda, apóyate en código de calidad, en cambio.&lt;/p&gt;

&lt;p&gt;¿Por qué nos comenta el autor esto? Pues porque él mismo se ha encontrado con código similar, independientemente del lenguaje de programación, en casi todos los trabajos en los que ha estado. Volvemos así al aspecto de la experiencia personal.&lt;/p&gt;

&lt;p&gt;Estos son solo unos pocos ejemplos de lo que puedes encontrar en &lt;em&gt;Street Smart Coding&lt;/em&gt;. ¿Te lo vas a perder?&lt;/p&gt;

</description>
      <category>book</category>
      <category>spanish</category>
      <category>review</category>
      <category>development</category>
    </item>
    <item>
      <title>Un nuevo C: el C++ ligero.</title>
      <dc:creator>Baltasar García Perez-Schofield</dc:creator>
      <pubDate>Mon, 23 Mar 2026 12:09:51 +0000</pubDate>
      <link>https://dev.to/baltasarq/un-nuevo-c-el-c-ligero-467c</link>
      <guid>https://dev.to/baltasarq/un-nuevo-c-el-c-ligero-467c</guid>
      <description>&lt;h2&gt;
  
  
  Buscando el camino
&lt;/h2&gt;

&lt;p&gt;Hay muchas críticas que se le pueden hacer a C++: que si permite comportamiento indefinido sin avisar (incluso lo aplica en el modo de optimización más agresivo), que si ha crecido hasta límites desmesurados, que si el precio a pagar es el de unos ejecutables monstruosos... Últimamente, la crítica se ha centrado en la seguridad  del manejo de la memoria. Por ejemplo, la &lt;a href="https://www.genbeta.com/actualidad/no-c-c-casa-blanca-pide-dejar-usar-lenguajes-programacion-que-base-windows-linux-macos" rel="noopener noreferrer"&gt;Casa Blanca ha pedido &lt;em&gt;pasarse&lt;/em&gt; a lenguajes más seguros&lt;/a&gt;, como por ejemplo C# o Rust. &lt;a href="https://www.reclunautas.com/post/estendencia-dejar-de-usar-c-y-c-y-centrarse-en-rust-y-java" rel="noopener noreferrer"&gt;La NSA ha emitido recomendaciones similares sobre abandonar C y C++&lt;/a&gt;. Incluso &lt;a href="https://en.wikipedia.org/wiki/Criticism_of_C%2B%2B" rel="noopener noreferrer"&gt;la Wikipedia tiene una página dedicada a las críticas a C++&lt;/a&gt;. Esta &lt;a href="https://www.genbeta.com/desarrollo/creador-c-critica-informe-nsa-que-defiende-superioridad-lenguajes-seguros-como-rust" rel="noopener noreferrer"&gt;crítica a C++ no ha sentado nada bien a Bjarne Stroustrup&lt;/a&gt;, el creador original del lenguaje de programación C++.&lt;/p&gt;

&lt;p&gt;En cualquier caso, todo esto lo que ha provocado es que hayan surgido nuevos lenguajes de programación para sustituir a C++, siendo los más conocidos &lt;a href="https://dotnet.microsoft.com/en-us/languages/csharp" rel="noopener noreferrer"&gt;C#&lt;/a&gt; y &lt;a href="https://dev.java/" rel="noopener noreferrer"&gt;Java&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Pero esto no quiere decir que no existan muchos más, que han surgido buscando eliminar puntos de fricción de C++, simplificar la experiencia de programación, y aportar sus propias características. Notables menciones son &lt;a href="https://ziglang.org/" rel="noopener noreferrer"&gt;Zig&lt;/a&gt;, &lt;a href="https://vlang.io/" rel="noopener noreferrer"&gt;V&lt;/a&gt;, o &lt;a href="https://rust-lang.org/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;En 2013, Bas vd Berg creó &lt;a href="http://c2lang.org/" rel="noopener noreferrer"&gt;C2, una evolución del lenguaje de programación C&lt;/a&gt; con el objetivo de eliminar complejidad innecesaria. Por ejemplo, no presenta archivos de cabecera &lt;code&gt;.h&lt;/code&gt;, no hay macros... &lt;/p&gt;

&lt;p&gt;&lt;a href="https://c3-lang.org/" rel="noopener noreferrer"&gt;El lenguaje de programación C3&lt;/a&gt; se basa en C2 para ofrecer un mismo objetivo, pero además con ciertas capacidades que lo acercan a C++. Y este es el objetivo de este artículo.&lt;/p&gt;

&lt;h2&gt;
  
  
  C3
&lt;/h2&gt;

&lt;p&gt;El &lt;code&gt;¡Hola, mundo!&lt;/code&gt; en este lenguaje de programación es como sigue a continuación.&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="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="kt"&gt;void&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="n"&gt;io&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="s"&gt;"¡Hola, mundo!"&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;Para compilarlo, aunque podríamos haber creado un proyecto completo, podemos simplemente hacer:&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;c3c compile holamundo.c3
&lt;span class="nv"&gt;$ &lt;/span&gt;./holamundo
¡Hola, mundo!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Las diferencias sintáticas con C son muy pocas: las funciones van ahora precedidas de &lt;code&gt;fn&lt;/code&gt;, de manera que se distingan facilmente de cualquier otra estructura. No hay archivos de cabecera, sino verdaderos módulos que son utilizados mediante &lt;code&gt;import&lt;/code&gt;. El significado, la semántica del programa, es mucho más evidente que en C.&lt;/p&gt;

&lt;p&gt;El siguiente es un programa que trata de generar ternas pitagóricas a partir de números aleatorios.&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="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;core&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;collections&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;import&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;math&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Terna&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ulong&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;ulong&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;ulong&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;Terna&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ulong&lt;/span&gt; &lt;span class="n"&gt;sqa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&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="n"&gt;self&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;ulong&lt;/span&gt; &lt;span class="n"&gt;sqb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&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;self&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;ulong&lt;/span&gt; &lt;span class="n"&gt;sqc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&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;sqc&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;sqa&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sqb&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="n"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;Terna&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;self&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;string&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;tformat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"(%d, %d, %d)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;self&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;self&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;fn&lt;/span&gt; &lt;span class="kt"&gt;void&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="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Lcg128Random&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Terna&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;printn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"Ternas pitagóricas&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Calculando..."&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;ulong&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;for&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="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;1&lt;/span&gt;&lt;span class="n"&gt;_000_000_000&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;Terna&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next_long&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next_long&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;next_long&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;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chk&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="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&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="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&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 ternas encontradas tras %d ciclos.&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;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&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;foreach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Terna&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;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;io&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;printn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;str&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;p&gt;El programa crea una estructura &lt;strong&gt;Terna&lt;/strong&gt;, que consta de tres enteros &lt;code&gt;long&lt;/code&gt; (128 bits) sin signo. La idea es que la suma de los cuadrados de &lt;em&gt;a&lt;/em&gt; y &lt;em&gt;b&lt;/em&gt; debe ser igual a &lt;em&gt;c&lt;/em&gt;. Esta propiedad se verifica mediante el método &lt;code&gt;Terna.chk()&lt;/code&gt;. El método &lt;code&gt;Terna.str()&lt;/code&gt; devuelve los valores de la terna como texto.&lt;/p&gt;

&lt;p&gt;Así que utilizamos &lt;strong&gt;Lcg128Random&lt;/strong&gt; del módulo &lt;code&gt;std::math::random&lt;/code&gt; para generar enteros de 128 bits. Con un bucle, probamos a generar números aleatorios, y si se crea una terna válida, se añade a una lista (esta lista puede crecer dinámicamente).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;C3&lt;/strong&gt; se siente bastante directo, fácil de comprender (no existe una nueva sintaxis), con un compilador que genera ejecutables rápidamente, y que son muy rápidos ellos mismos.&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;c3c compile ternas_pitagoricas.c3
&lt;span class="nv"&gt;$ &lt;/span&gt;./ternas_pitagoricas                                                  Ternas pitagóricas
Calculando...
1 ternas encontradas tras 1000000000 ciclos.
&lt;span class="o"&gt;(&lt;/span&gt;3, 4, 5&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El ejecutable realiza mil millones (1 000 000 000) de ciclos en unos 17 segundos de manera consistente. Esto involucra crear una terna, llamar su método &lt;code&gt;chk()&lt;/code&gt; (que consiste en un cálculo matemático), y añadirlo o no a una lista.&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;&lt;span class="nb"&gt;time&lt;/span&gt; ./ternas_pitagoricas
Ternas pitagóricas
Calculando...
1 ternas encontradas tras 1000000000 ciclos.
&lt;span class="o"&gt;(&lt;/span&gt;3, 4, 5&lt;span class="o"&gt;)&lt;/span&gt;
./ternas_pitagoricas  16,38s user 0,00s system 99% cpu 16,438 total

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt; ./ternas_pitagoricas
Ternas pitagóricas
Calculando...
1 ternas encontradas tras 1000000000 ciclos.
&lt;span class="o"&gt;(&lt;/span&gt;3, 4, 5&lt;span class="o"&gt;)&lt;/span&gt;
./ternas_pitagoricas  16,35s user 0,00s system 99% cpu 16,402 total

&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt; ./ternas_pitagoricas
Ternas pitagóricas
Calculando...
1 ternas encontradas tras 1000000000 ciclos.
&lt;span class="o"&gt;(&lt;/span&gt;3, 4, 5&lt;span class="o"&gt;)&lt;/span&gt;
./ternas_pitagoricas  16,31s user 0,00s system 99% cpu 16,368 total
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El ejecutable es de unos 700 k, como se muestra a continuación.&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="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; ternas_pitagoricas                                             
&lt;span class="go"&gt;-rwxr-xr-x 1 baltasarq baltasarq 702640 mar 23 12:13 ternas_pitagoricas
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;¿Podemos compararlo con una versión C++? Pues claro.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;list&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;cstdio&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Terna&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
    &lt;span class="n"&gt;Terna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ulong&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;ulong&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;ulong&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&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="n"&gt;a&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;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;chk&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="n"&gt;ulong&lt;/span&gt; &lt;span class="n"&gt;sqa&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&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="n"&gt;ulong&lt;/span&gt; &lt;span class="n"&gt;sqb&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;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;ulong&lt;/span&gt; &lt;span class="n"&gt;sqc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;c&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;sqc&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;sqa&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;sqb&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="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;str&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;to_string&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="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;", "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;to_string&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="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;", "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;ulong&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;ulong&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;ulong&lt;/span&gt; &lt;span class="n"&gt;c&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="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Terna&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;l&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;"Ternas pitagóricas&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;ulong&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;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Terna&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&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="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000000000ul&lt;/span&gt;&lt;span class="p"&gt;;&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="n"&gt;Terna&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;rand&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;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chk&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="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;push_back&lt;/span&gt;&lt;span class="p"&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="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;"%lu ternas encontradas tras %lu ciclos.&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;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&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="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Terna&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;l&lt;/span&gt;&lt;span class="p"&gt;)&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;"%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;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="n"&gt;c_str&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;p&gt;La salida devuelve la solución tras mil millones de ciclos en unos 17 segundos de manera consistente.&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;g++ ternas_pitagoricas.cpp &lt;span class="nt"&gt;-o&lt;/span&gt; ternas_pitagoricas
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt; ./ternas_pitagoricas
Ternas pitagóricas
1 ternas encontradas tras 1000000000 ciclos.
3, 4, 5
./ternas_pitagoricas  17,20s user 0,00s system 99% cpu 17,270 total
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Obtenemos un resultado similar. Esto no es extraño, pues aprovecha el backend de LLVM.&lt;/p&gt;

&lt;p&gt;En este caso, C++ es capaz de generar el ejecutable más pequeño, aunque es cierto que no se emplea la estructura de entrada y salida oficial, &lt;em&gt;IOStreams&lt;/em&gt;. También es importante notar que el ejecutable enlaza con la librería estándar de C++, que no es, precisamente, pequeña (unos 3 MB). C3 genera, por otra parte, un ejecutable que es autocontenido.&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;g++ ternas_pitagoricas.cpp &lt;span class="nt"&gt;-o&lt;/span&gt; ternas_pitagoricas    
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; ternas_pitagoricas
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1 baltasarq baltasarq 44320 mar 23 12:54 ternas_pitagoricas
&lt;span class="nv"&gt;$ &lt;/span&gt;ldd ternas_pitagoricas                                                
        linux-vdso.so.1 &lt;span class="o"&gt;(&lt;/span&gt;0x00007faefdf04000&lt;span class="o"&gt;)&lt;/span&gt;
        libstdc++.so.6 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; /usr/lib/libstdc++.so.6 &lt;span class="o"&gt;(&lt;/span&gt;0x00007faefdc00000&lt;span class="o"&gt;)&lt;/span&gt;
        libm.so.6 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; /usr/lib/libm.so.6 &lt;span class="o"&gt;(&lt;/span&gt;0x00007faefdae2000&lt;span class="o"&gt;)&lt;/span&gt;
        libgcc_s.so.1 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; /usr/lib/libgcc_s.so.1 &lt;span class="o"&gt;(&lt;/span&gt;0x00007faefdab5000&lt;span class="o"&gt;)&lt;/span&gt;
        libc.so.6 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; /usr/lib/libc.so.6 &lt;span class="o"&gt;(&lt;/span&gt;0x00007faefd8c4000&lt;span class="o"&gt;)&lt;/span&gt;
        /lib64/ld-linux-x86-64.so.2 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; /usr/lib64/ld-linux-x86-64.so.2 &lt;span class="o"&gt;(&lt;/span&gt;0x00007faefdf06000&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;$ &lt;/span&gt;c3c compile ternas_pitagoricas.c3                                    
Program linked to executable &lt;span class="s1"&gt;'./ternas_pitagoricas'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;ldd ternas_pitagoricas
        linux-vdso.so.1 &lt;span class="o"&gt;(&lt;/span&gt;0x00007fad32973000&lt;span class="o"&gt;)&lt;/span&gt;
        libm.so.6 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; /usr/lib/libm.so.6 &lt;span class="o"&gt;(&lt;/span&gt;0x00007fad32796000&lt;span class="o"&gt;)&lt;/span&gt;
        libc.so.6 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; /usr/lib/libc.so.6 &lt;span class="o"&gt;(&lt;/span&gt;0x00007fad325a5000&lt;span class="o"&gt;)&lt;/span&gt;
        /lib64/ld-linux-x86-64.so.2 &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; /usr/lib64/ld-linux-x86-64.so.2 &lt;span class="o"&gt;(&lt;/span&gt;0x00007fad32975000&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;¿El sucesor oficial, o al menos un lenguaje estable? Es difícil decirlo, solo el tiempo lo dirá.&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>programming</category>
      <category>systems</category>
      <category>development</category>
    </item>
    <item>
      <title>Hackeando en 8 bits (y IV)</title>
      <dc:creator>Baltasar García Perez-Schofield</dc:creator>
      <pubDate>Mon, 16 Mar 2026 17:43:35 +0000</pubDate>
      <link>https://dev.to/baltasarq/hackeando-en-8-bits-y-iv-9hi</link>
      <guid>https://dev.to/baltasarq/hackeando-en-8-bits-y-iv-9hi</guid>
      <description>&lt;p&gt;Y vamos a terminar ahora con la serie dedicada a &lt;em&gt;hackear&lt;/em&gt; juegos en el Speccy, lo que se conocía en la época como &lt;em&gt;desprotegerlos&lt;/em&gt;. Vamos a buscar vidas infinitas em &lt;a href="https://worldofspectrum.net/infoseek/?regexp=abu+simbel+profanation" rel="noopener noreferrer"&gt;Abu Simbel Profanation&lt;/a&gt; e &lt;a href="https://worldofspectrum.net/item/0002450/" rel="noopener noreferrer"&gt;Ikari Warriors&lt;/a&gt;. Y como herramienta, utilizaremos em emulador de Spectrum &lt;a href="https://github.com/chernandezba/zesarux" rel="noopener noreferrer"&gt;Zesarux&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;En la última entrega de esta serie, vimos a grandes rasgos cómo crear uno de aquellos cargadores que proporcionaban vidas infinitas o bombas infinitas, o cualquier otro &lt;em&gt;cheat&lt;/em&gt;, metiendo &lt;strong&gt;POKE&lt;/strong&gt;s justo antes de la ejecución del programa.&lt;/p&gt;

&lt;p&gt;Pero en esta entrega vamos a centrarnos en otro tipo de &lt;em&gt;hack&lt;/em&gt;: simularemos que tenemos un &lt;a href="https://worldofspectrum.net/item/0022960/" rel="noopener noreferrer"&gt;&lt;strong&gt;Multiface&lt;/strong&gt;&lt;/a&gt;. Este aparato permite, conectado a la parte trasera del Speccy, pulsar un botón rojo que genera una &lt;a href="https://es.wikipedia.org/wiki/Interrupci%C3%B3n_no_enmascarable" rel="noopener noreferrer"&gt;NMI&lt;/a&gt; (&lt;em&gt;Non Maskable Interruption&lt;/em&gt; o interrupción no enmascarable). Al pulsar el botón, al Speccy no le queda más remedio, esté haciendo lo que esté haciendo, que ejecutar el código del vector de interrupción dado, lo que ejecuta su software mientras el software está en memoria. Lo que se podía hacer entonces era guardar el contenido de la memoria (con lo que podías guardar el juego tal cual se estaba ejecutando en ese momento), introducir &lt;strong&gt;POKE&lt;/strong&gt;s, o explorar la memoria.&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%2Fba1c0754jke0iy7sibts.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%2Fba1c0754jke0iy7sibts.png" alt="Abu Simbel Profanation" width="544" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos a simular, como decía, la forma de utilizar un periférico como &lt;strong&gt;Multiface&lt;/strong&gt;, pero con la comodidad de un emulador de hoy en día. Todo lo que vamos a hacer aquí se puede hacer con dicho aparato enchufado y siguiendo sus instrucciones, pero lógicamente es mucho más tedioso.&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%2F6j5gn2ksdozk03uzdhly.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%2F6j5gn2ksdozk03uzdhly.png" alt="Abu Simbel Profanation tras NMI" width="645" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abu Simbel Profanation
&lt;/h2&gt;

&lt;p&gt;Este juego fue creado por Víctor Ruiz para su sello familiar (compartido con sus heramanos), &lt;strong&gt;Dinamic&lt;/strong&gt;. Según las entrevistas que se le han hecho con el tiempo, fue creado en una especie de versión más limitada de BASIC, que era posible compilar a código máquina.&lt;/p&gt;

&lt;p&gt;Al arrancar Zesarux, seleccionamos la cinta a cargar en el menú &lt;code&gt;Z&lt;/code&gt; &amp;gt;&amp;gt; &lt;em&gt;smart load&lt;/em&gt;, que en este caso será el .TAP o .TZX descargado desde la web de &lt;em&gt;World of Spectrum&lt;/em&gt;. Este emulador es muy particular, y cargará la cinta él solo, como si hubiéramos tecleado la secuencia de comandos.&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%2Fpyz06cnef3tqnusi5fdg.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%2Fpyz06cnef3tqnusi5fdg.png" alt="Abu Simbel Profanation en Zesarux" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cuando el juego se ejecute, seleccionamos la opción &lt;code&gt;3. keyboard&lt;/code&gt; (o &lt;code&gt;2. kempston&lt;/code&gt; si tenemos un controlador conectado), y el juego nos sitúa dentro de la pirámide con cierta (infame) gota cayendo desde el techo.&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%2F0i6x8tix6xq6p4tik108.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%2F0i6x8tix6xq6p4tik108.png" alt="Menu Find de Zesarux" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En la parte superior del escritorio de &lt;strong&gt;Zesarux&lt;/strong&gt;, a la derecha de la pantalla del Speccy, veremos un icono de una cucaracha: se trata de las opciones que nos permitirán depurar un programa. Vamos a utilizarlo para encontrar pokes. Nos metemos en la opción &lt;em&gt;Debug&lt;/em&gt; &amp;gt;&amp;gt; &lt;em&gt;Find&lt;/em&gt; &amp;gt;&amp;gt; &lt;em&gt;Find bytes&lt;/em&gt;.&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%2Fi73m9h8jzxc2ktb0t9br.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%2Fi73m9h8jzxc2ktb0t9br.png" alt="Menu Find Bytes de Zesarux" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Menú&lt;/th&gt;
&lt;th&gt;Submenú&lt;/th&gt;
&lt;th&gt;Submenú&lt;/th&gt;
&lt;th&gt;Submenú&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Debug&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Find&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Find bytes&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Find bytes (initial)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Find bytes (next)&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;View results&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Clear results&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;En la tabla anterior vemos las posibilidades del menú. Aunque hay opciones automatizadas para buscar &lt;strong&gt;POKE&lt;/strong&gt;s, lo haremos de forma genérica para permitir buscar &lt;em&gt;cheats&lt;/em&gt; sobre municiones, granadas, etc.&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%2Fyxbzmuw6o2q77hwxjgz4.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%2Fyxbzmuw6o2q77hwxjgz4.png" alt="Find bytes initial" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Comenzamos. La primera vez seleccionamos (1), &lt;em&gt;Find bytes (initial)&lt;/em&gt;. Ahora que estamos en el juego podemos buscar el valor 10, que introducimos, pues es el número inicial de vidas. Nos dará varios cientos de resultados, como es de esperar, pero lo importante es que Zesarux va a recordar los resultados. Así, la siguiente vez que entremos en el menú, nos encontraremos con (2) &lt;em&gt;Find bytes (next)&lt;/em&gt;, en lugar de la opción (1). ¡Pero espera! Aún es pronto.&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%2Fmjur464e52bk1r0lj3pg.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%2Fmjur464e52bk1r0lj3pg.png" alt="Ver resultados" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mueve a &lt;em&gt;Johnny Jones&lt;/em&gt; con las teclas &lt;code&gt;O&lt;/code&gt; (izquierda), &lt;code&gt;P&lt;/code&gt; (derecha), y &lt;code&gt;Q&lt;/code&gt; (salto) hasta... sí, la gota. Ponle debajo y pierde una vida. Confía en mi, merecerá la pena... Es verdad, ahora solo tienes 9 vidas, pero verás lo que pasa.&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%2Fbs61f4wx3jee4pi1llvf.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%2Fbs61f4wx3jee4pi1llvf.png" alt="Nueve vidas en Abu Simbel Profanation" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Volvemos al menú de &lt;em&gt;Find&lt;/em&gt; y (2) &lt;em&gt;Find bytes (next)&lt;/em&gt;, y esta vez introducimos 9. ¡Solo hay una coincidencia! Pulsamos ESC y volvemos al mismo submenú para seleccionar (3) &lt;em&gt;View results&lt;/em&gt;.  La dirección de memoria con valor 9 es 38818. ¡Ya sabemos en qué posición de memoria guarda el juego las vidas! &lt;/p&gt;

&lt;p&gt;Nos vamos de nuevo al menú &lt;em&gt;Debug&lt;/em&gt;, y esta vez seleccionamos la opción &lt;strong&gt;POKE&lt;/strong&gt;. Seleccionamos la primera opción, &lt;em&gt;&lt;strong&gt;POKE&lt;/strong&gt;...&lt;/em&gt;. Nos pregunta la dirección, introducimos 38818. A continuación, nos pregunta con qué valor queremos modificar la dirección 38818. En cada posición de memoria, solo podemos escribir un valor entre 0 y 255. Sabemos que va a ser el número de vidas disponible, así que... ¡qué caray! ¡Introduzcamos 255! ¡Mejor que zozobre que no que zofalte!&lt;/p&gt;

&lt;p&gt;A continuación, nos pregunta cuántos bytes queremos modificar. Lo más normal es, como en esta ocasión, que sea solo uno. Pero es posible que, para valores por encima de 255, un juego utilice dos (el mínimo), o más bytes. En este caso, simplemente introducimos 1.&lt;/p&gt;

&lt;p&gt;Tras pulsar ENTER, puede que pensemos que algo ha ido mal. Que no ha cambiado nada. Pero... llevemos a &lt;em&gt;Johnny&lt;/em&gt; hasta la gota, para morir de nuevo. Solo una vez más. Si lo hacemos... ¡habrán aparecido 254 vidas restantes! Tiene sentido que el juego solo actualice el marcador de vidas del jugador cuando este número cambia... es decir, cambia según las reglas &lt;em&gt;normales&lt;/em&gt; del juego.&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%2Fh7dazmufft8fjx7am3mi.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%2Fh7dazmufft8fjx7am3mi.png" alt="199 vidas" width="800" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Un detalle curioso es que no aparece solo "254", sino "0254". Está claro que Víctor Ruiz quería que los marcadores estuvieran "equilibrados", ya que hay más de 10 pantallas, y en la primera se muestra como "01", no como "1". Está claro que el número de vidas es tan exagerademente alto, que el algoritmo para mostrar vidas se desborda. Literalmente.&lt;/p&gt;

&lt;p&gt;Un detalle... creo que es en la segunda o en la tercera pantalla que es posible caer en un pozo vertical que termina en unas mortíferas agujas... Es importante que no caigas ahí, o... tendrás que esperar a morir doscientas y pico veces (o, mejor, hacer un &lt;strong&gt;POKE&lt;/strong&gt; de 38818 con 1).&lt;/p&gt;

&lt;p&gt;Si queremos buscar algún otro valor en memoria, tendremos que seleccionar (4) &lt;em&gt;Clear results&lt;/em&gt;, para poder volver a empezar, seleccionando de nuevo la opción (1) &lt;em&gt;Find bytes (initial)&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ikari Warriors
&lt;/h2&gt;

&lt;p&gt;El siguiente juego que &lt;em&gt;desprotegeremos&lt;/em&gt; es &lt;strong&gt;Ikari Warriors&lt;/strong&gt;. Descárgate el .TAP o el .TZX, y cárgalo en &lt;strong&gt;Zesarux&lt;/strong&gt;, de nuevo mediante la opción de menú &lt;code&gt;Z&lt;/code&gt; &amp;gt;&amp;gt; &lt;em&gt;Smart load&lt;/em&gt;. El juego pide seleccionar el controlador del primer jugador, y después del segundo jugador. En mi caso, redefeniré las teclas (opción 1), del juego para el &lt;strong&gt;Jugador 1&lt;/strong&gt;, y cualquier otra cosa pra el &lt;strong&gt;Jugador 2&lt;/strong&gt;, pues no pienso usarlo. Finalmente, seleccionamos (1) (&lt;em&gt;Start one player game&lt;/em&gt;) para comenzar el juego.&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%2F2df5e97g4vit05tct1qz.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%2F2df5e97g4vit05tct1qz.png" alt="Ikari Warriors en Zesarux" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Las vidas están representadas mediante cabezas del héroe, son 6 en total. Así que seleccionamos (1) &lt;em&gt;Find bytes (initial)&lt;/em&gt;, e introducimos 6 (o las vidas que nos queden en ese momento, el juego no se anda con chiquitas). Nos dirá que hay más de seiscientos resultados. Seguramente ya nos habrán matado una o dos veces, seleccionamos (2) &lt;em&gt;Find bytes (next)&lt;/em&gt; e introducimos el número de vidas que nos quede. Ahora ya solo aparecerá un resultado. Con (3) &lt;em&gt;View results&lt;/em&gt;, comprobaremos que la dirección de memoria de las vidas es 58478. Ahora seleccionamos &lt;em&gt;Debug&lt;/em&gt; &amp;gt;&amp;gt; &lt;em&gt;Poke&lt;/em&gt; &amp;gt;&amp;gt; &lt;em&gt;Poke...&lt;/em&gt;. Introducimos la dirección de memoria, 58478. A continuación, nos pide un valor, sabemos que como máximo puede estar entre 0 y 255. Pero... ¡cuidado! Recuerda que las vidas se representan con figuras en la pantalla. Si introducimos un valor demasiado alto, las figuras se extenderán corrompiendo la pantalla, e impidiendo que nuestro héroe se mueva por ella. Un valor seguro es 40, ya que son las 10 filas por cuatro figuras que se pueden representar en pantalla sin sobrepasar el espacio destinado para ellas. El juego, como decía, no se anda con chiquitas, así que tendrás que introducir el &lt;strong&gt;POKE&lt;/strong&gt; a 58478 con 40 &lt;strong&gt;varias veces&lt;/strong&gt;. En cambio, es bastante improbable que te quedes sin granadas, pudiendo llegar a acumular varias docenas. Así que, personalmente, no creo que merezca la pena buscar el &lt;strong&gt;POKE&lt;/strong&gt; correspondiente. Aún así, lo hice por experimentar, pero... no salió nada, ni con las granadas ni con los disparos. Probablemente, algún tema de ofuscación, curioso.&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%2Fevfa16ywjbeo40ezywxj.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%2Fevfa16ywjbeo40ezywxj.png" alt="Ikari Warriors en Zesarux con 40 vidas" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En cualquier caso, ya sabes cómo encontrar vidas en un juego. ¡Enhorabuena, eres un &lt;em&gt;hacker&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;¿Qué?&lt;br&gt;
¿Cómo?&lt;br&gt;
¿Qué con estas técnicas y ciertos programas buscadores de &lt;em&gt;cheats&lt;/em&gt;, también se pueden buscar vidas infinitas en lo juegos de ahora? No tengo ni idea de lo que me estás hablando...&lt;/p&gt;

</description>
      <category>spanish</category>
      <category>sinclair</category>
      <category>games</category>
      <category>hack</category>
    </item>
    <item>
      <title>Hackeando en 8 bits (y III)</title>
      <dc:creator>Baltasar García Perez-Schofield</dc:creator>
      <pubDate>Fri, 13 Mar 2026 12:18:41 +0000</pubDate>
      <link>https://dev.to/baltasarq/hackeando-en-8-bits-y-iii-i07</link>
      <guid>https://dev.to/baltasarq/hackeando-en-8-bits-y-iii-i07</guid>
      <description>&lt;p&gt;Como hemos visto en las dos entregas anteriores, todo se reduce a un concepto: &lt;a href="https://dev.to/baltasarq/hackeando-en-8-bits-2d6e"&gt;cambiar la memoria&lt;/a&gt;. Si lo hacemos cuidadosamente, podemos colocar, o bien nuestras propias instrucciones, &lt;a href="https://dev.to/baltasarq/hackeando-en-8-bits-y-ii-2e8n"&gt;cambiando el comportamiento del programa&lt;/a&gt;, o bien nuestros propios datos, de manera que podamos cambiar el número de vidas, el número de balas, el número de bombas... etc.&lt;/p&gt;

&lt;p&gt;En esta entrega vamos a ver un ejemplo real, centrándonos en &lt;a href="https://worldofspectrum.net/item/0004916/" rel="noopener noreferrer"&gt;Stop the express&lt;/a&gt;. Vamos a seguir el proceso de creación de un cargador que nos proporcione vidas y tiempo infinito. Lo que hoy en día se conoce como &lt;code&gt;cheats&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Encontrar las posiciones de memoria adecuadas y saber modificarlas, implica tener conocimientos de &lt;a href="https://clrhome.org/table/" rel="noopener noreferrer"&gt;ensamblador y código máquina del Z80&lt;/a&gt;, el microprocesador dentro del ZX Spectrum. En este pequeño artículo lo haremos paso a paso, sin asumir conocimientos previos.&lt;/p&gt;

&lt;p&gt;Comencemos el proceso de crear un cargador con &lt;em&gt;Stop the Express&lt;/em&gt;. Tras encender el Spectrum, y colocar la cinta en el reproductor de cassette, tecleamos &lt;code&gt;merge ""&lt;/code&gt; y pulsamos PLAY. Bueno, esto es la idea, a partir de ahora vamos a manejarnos con un &lt;a href="https://www.rastersoft.com/programas/fbzx.html" rel="noopener noreferrer"&gt;emulador de Spectrum, llamado FBZX&lt;/a&gt;. Precisamente, nos permitirá emular insertar una cinta, ponerla en marcha, etc.&lt;/p&gt;

&lt;p&gt;Así, pulsamos F3 y 1 para indicar que queremos cargar una cinta, el .tap o .tzx de &lt;strong&gt;Stop the Express&lt;/strong&gt; que nos hayamos descargado de &lt;strong&gt;World of Spectrum&lt;/strong&gt;. Teclamos &lt;code&gt;merge""&lt;/code&gt; como se ve en la imagen anterior. Es importante que nos fijemos en que hemos desactivado la carga rápida y la carga turbo.&lt;/p&gt;

&lt;p&gt;Ahora, de vuelta en la pantalla de Spectrum, pulsamos ENTER para cargar la cinta, y pulsamos F6 para ponerla en marcha (virtualmente). Una vez que aparece &lt;code&gt;Ok 0:1&lt;/code&gt;, pulsamos F5 para parar la cinta. Volvemos a pulsar ENTER para ver el código en pantalla.&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%2F1aporz8c1ahsldyn2ezy.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%2F1aporz8c1ahsldyn2ezy.png" alt="MERGE "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Introducimos 20 y ENTER y en la línea 50 eliminamos los colores, de manera que solo nos quede la línea 20, que es la que carga la pantalla de presentación (&lt;code&gt;load "" screen$&lt;/code&gt;), el código máquina del juego (&lt;code&gt;load "" code&lt;/code&gt;), y la ejecución del mismo &lt;code&gt;randomize usr 48096&lt;/code&gt;. Este último lo vamos a modificar poniendo &lt;code&gt;REM&lt;/code&gt; delante, lo que lo transforma en un comentario.&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%2F6fuflu759stg4p21n3ex.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%2F6fuflu759stg4p21n3ex.png" alt="El cargador de Stop the Express"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;50 load "StopExpr2" screen$: load "StopExpr3" code: rem randomize usr 48096
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fgf1vlj6zzrcgl6cb0zz7.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%2Fgf1vlj6zzrcgl6cb0zz7.png" alt="El cargador de Stop the Express modificado"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esta última instrucción es interesante. Ahora le hemos puesto &lt;code&gt;rem&lt;/code&gt; delante, con lo que la hemos transformado en un comentario y no se va a ejecutar. La pregunta ahora es: ¿cómo es que esta instrucción ejecuta el juego? La parte importante es &lt;code&gt;USR 48096&lt;/code&gt;. Lo que sucede es que es una función, no una instrucción. Necesita una instrucción a la cual va a ser pasada por parámetro. ¿Cuál instrucción exactamente? Realmente no importa demasiado, solo la necesitamos para que evalúe sus argumentos. Concretamente, se usa &lt;code&gt;RANDOMIZE&lt;/code&gt;, que inicializa la semilla aleatoria que alimenta a la función &lt;code&gt;RND&lt;/code&gt;, que devuelve un valor aleatorio entre 0 y 1. Así, una vez que &lt;code&gt;RANDOMIZE&lt;/code&gt; evalúe su parámetro, podrá inicializar la semilla aleatoria. Lo que pasa es que nunca se producirá un retorno de la función que llama al código máquina del juego, claro...&lt;/p&gt;

&lt;p&gt;La documentación del ZX Spectrum nos dice que cuando ejecutamos un &lt;code&gt;load "" code&lt;/code&gt;, en realidad estamos haciendo un &lt;code&gt;load "" code 32768&lt;/code&gt;. Estamos cargando el código del juego a partir de la dirección de memoria 32768. Si tenemos en cuenta que el código tiene un tamaño de 15394 bytes, entonces sabemos que ocupará desde la dirección en memoria 32768 hasta la dirección 48162, inclusive. El resto de la memoria, desde la dirección en memoria 48163 hasta 64k (65536), queda libre.&lt;/p&gt;

&lt;p&gt;Tener parte de la memoria libre es importante porque necesitamos espacio para poder cargar un desensamblador, sin "pisar" el programa, es decir, sin utilizar la memoria que está siendo ocupada por el programa. El desensamblador nos va a decir las instrucciones que están en memoria, sin necesidad de que interpretemos los números en ella directamente.&lt;/p&gt;

&lt;p&gt;Hasta ahora hemos utilizado emulador el mejor Spectrum posible, el creado por Investrónica en 1986, con características mejoradas como el chip de sonido de tres canales, o la memoria de 128k. Se trata del &lt;a href="https://dev.to/baltasarq/el-zx-spectrum-128k-de-investronica-332k"&gt;&lt;strong&gt;Sinclair Spectrum + 128k&lt;/strong&gt;&lt;/a&gt;. Pero &lt;em&gt;Stop the express&lt;/em&gt; necesita un modelo de 48k, el modelo anterior, así que por si acaso, pasaremos a ese modo con la instrucción &lt;code&gt;SPECTRUM&lt;/code&gt;. Si ahora pulsamos la tecla &lt;code&gt;K&lt;/code&gt;, veremos que aparece la instrucción &lt;code&gt;LIST&lt;/code&gt; en pantalla. Pulsamos ENTER y veremos de nuevo el listado.&lt;/p&gt;

&lt;p&gt;Esta forma de manejarse con el teclado es típica del ZX Spectrum original, el de 48k. Se concibió como una manera de ahorrar memoria, y también de ayudar a los usuarios a teclear programas en BASIC. Hay muchos detractores de esta forma de operación, así como fervientes seguidores de la misma.&lt;/p&gt;

&lt;p&gt;Ahora pulsamos &lt;code&gt;R&lt;/code&gt; (aparece &lt;code&gt;RUN&lt;/code&gt;en pantalla) y pulsamos ENTER. Pulsamos F6 para volver a poner la cinta en marcha. El programa cargará la pantalla de presentación, y después el bloque de código máquina del juego en sí.&lt;/p&gt;

&lt;p&gt;Cuando termine, veremos en la parte inferior de la pantalla &lt;code&gt;Ok 50:2&lt;/code&gt;. Aparentemente, no ha pasado nada, pero tenemos el juego en memoria. Si pulsamos &lt;code&gt;V&lt;/code&gt; (aparece &lt;code&gt;CLS&lt;/code&gt;), y ENTER, y &lt;code&gt;K&lt;/code&gt; (aparece &lt;code&gt;LIST&lt;/code&gt;), y ENTER, veremos que seguimos teniendo el cargador en memoria.&lt;/p&gt;

&lt;p&gt;Ahora es necesario cargar el desensamblador que mencionamos. Vamos a utilizar &lt;a href="https://worldofspectrum.net/item/0008091/" rel="noopener noreferrer"&gt;HiSoft MONS&lt;/a&gt;. El primer archivo, &lt;code&gt;HiSoftDevpacV3M2Mons.tap.zip&lt;/code&gt; ya nos vale. Lo descomprimimos, por ejemplo en el mismo directorio de descargas, con lo que nos quedamos con &lt;code&gt;MONS321.tap&lt;/code&gt;. Esta &lt;em&gt;cinta&lt;/em&gt;, es decir, este archivo &lt;em&gt;.tap&lt;/em&gt;, solo tiene un programa, que es el propio desensamblador, que cargaremos con &lt;code&gt;load "" code 52000&lt;/code&gt;. Volvemos a FBZX, pulsamos F3 y 1 para seleccionar &lt;code&gt;MONS321.tap&lt;/code&gt;, y ESC para volver a la emulación. Pulsamos &lt;code&gt;J&lt;/code&gt; (aparece &lt;code&gt;LOAD&lt;/code&gt;), pulsamos Ctrl + P dos veces para obtener dos dobles comillas, y a continuación pulsamos Ctrl + Shift, y soltamos (el cursor cambia para mostrar una &lt;code&gt;E&lt;/code&gt; parpadeante), y entonces pulsamos la tecla &lt;code&gt;I&lt;/code&gt; (aparece &lt;code&gt;CODE&lt;/code&gt; en pantalla). Finalmente, tecleamos 52000, para obtener &lt;code&gt;load "" code 52000&lt;/code&gt;. Pulsamos ENTER para pasar al modo de carga, y F6 para comenzar la reproducción de la cinta. En pantalla aparecerá &lt;code&gt;BYTES: MONS3M2&lt;/code&gt;, y realizará la carga correspondiente, hasta mostrar &lt;code&gt;Ok 0:1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;El proceso de carga del juego para su desensamblado, en realidad suele hacerse desde el mismo MONS, pero para no entrar en excesivas tecnicidades, y explorar también al propio Spectrum 48k, al final he decidido hacerlo así.&lt;/p&gt;

&lt;p&gt;Para ejecutar el desenamblador, tecleamos &lt;code&gt;T&lt;/code&gt;(aparece &lt;code&gt;RANDOMIZE&lt;/code&gt;), Ctrl + Shift (el cursor cambia a una &lt;code&gt;E&lt;/code&gt; parpadeante), y &lt;code&gt;L&lt;/code&gt; (aparece &lt;code&gt;USR&lt;/code&gt;). Tecleamos a continuación 52000, para obtener &lt;code&gt;RANDOMIZE USR 52000&lt;/code&gt;. Pulsamos ENTER.&lt;/p&gt;

&lt;p&gt;Veremos brevemente un mensaje de bienvenida de MONS3, y después una pantalla con el contenido de los registros del Z80, el contenido de una zona de la memoria en &lt;a href="https://es.wikipedia.org/wiki/Sistema_hexadecimal" rel="noopener noreferrer"&gt;hexadecimal&lt;/a&gt;, y un cursor parpadeante en la zona inferior de la pantalla.&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%2Fj7vwcmfx6df8njoizxda.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%2Fj7vwcmfx6df8njoizxda.png" alt="Mons3 ejecutándose en el Speccy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como yo no soy un experto en ensamblador del Z80, me dirijo a los &lt;em&gt;pokes&lt;/em&gt; disponibles, que son los siguientes para vidas infinitas y tiempo infinito. A continuación aparecen las direcciones en memoria a cambiar, el valor en hexadecimal del nuevo valor a poner en memoria, y su opcode correspondiente en el Z80.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dirección en memoria&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;th&gt;Significado&lt;/th&gt;
&lt;th&gt;Hex&lt;/th&gt;
&lt;th&gt;Opcode&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;34464&lt;/td&gt;
&lt;td&gt;183&lt;/td&gt;
&lt;td&gt;Vidas infinitas&lt;/td&gt;
&lt;td&gt;B7&lt;/td&gt;
&lt;td&gt;OR A, A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;34926&lt;/td&gt;
&lt;td&gt;183&lt;/td&gt;
&lt;td&gt;Vidas infinitas&lt;/td&gt;
&lt;td&gt;B7&lt;/td&gt;
&lt;td&gt;OR A, A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;35257&lt;/td&gt;
&lt;td&gt;183&lt;/td&gt;
&lt;td&gt;Vidas infinitas&lt;/td&gt;
&lt;td&gt;B7&lt;/td&gt;
&lt;td&gt;OR A, A&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;35780&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Tiempo infinito&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;NOP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;39549&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;Tiempo infinito&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;NOP&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;En &lt;strong&gt;Mons3&lt;/strong&gt;, si pulsamos la combinación de teclas &lt;code&gt;Shift&lt;/code&gt; + &lt;code&gt;1&lt;/code&gt;, volvemos a BASIC. Para volver a entrar en &lt;strong&gt;Mons3&lt;/strong&gt;, necesitaremos volver a teclear &lt;code&gt;randomize usr 52000&lt;/code&gt;, es decir, &lt;code&gt;T&lt;/code&gt;, &lt;code&gt;Ctrl&lt;/code&gt;+ &lt;code&gt;Shift&lt;/code&gt;, &lt;code&gt;L&lt;/code&gt;, 52000 y ENTER.&lt;/p&gt;

&lt;p&gt;Con &lt;code&gt;Ctrl&lt;/code&gt;+ &lt;code&gt;3&lt;/code&gt;, desensamblamos el programa empezando en la dirección de memoria por defecto, que al principio es 0, es decir, estaríamos desensamblando la &lt;strong&gt;ROM&lt;/strong&gt; del &lt;strong&gt;Speccy&lt;/strong&gt;, la que contiene el intérprete de BASIC, etc.&lt;/p&gt;

&lt;p&gt;Con &lt;code&gt;H&lt;/code&gt;, y un número decimal, obtenemos su valor hexadecimal equivalente. Por ejemplo, &lt;code&gt;H&lt;/code&gt; y 52000 nos devuelve CB20, que es la posición en memoria de Mons3. Si convertimos 34464, la dirección del primer &lt;code&gt;POKE&lt;/code&gt;, nos devuelve 86A0.&lt;/p&gt;

&lt;p&gt;Con 'M', y un valor en memoria hexadecimal, nos pasamos a esa dirección en memoria. En lugar de utilizar directamente 86A0, utilizaremos 869B, así, con &lt;code&gt;M', 869B y&lt;/code&gt;Ctrl`+ 3, obtendremos un listado como el siguiente:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;plaintext&lt;br&gt;
869V 1102A7 LD DE, #A702&lt;br&gt;
869E     96 SUB (HL)&lt;br&gt;
869F     5F LD E, A&lt;br&gt;
86A0     9D SBC A, L            ; este es el valor a cambiar&lt;br&gt;
86A1     93 SUB E&lt;br&gt;
86A2     AA XOR D&lt;br&gt;
96A3     ...&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;En la primera columna aparece la dirección en memoria en hexadecimal. A continuación, los valores en hexadecimal para la instrucción. Hay que tener en cuenta que hay instrucciones que ocupan varios bytes. Por ejemplo, la primera en 869V es 1102A7. El valor 11 es el de la instrucción LD, concretamente LD DE, cargar en el registro DE el valor A702, que como vemos aparece como 02A7, ya que el Z80 codifica los valores como LSB (&lt;em&gt;Least Significan Byte&lt;/em&gt;), es decir, coloca primero el byte menos significativo de un valor en 16 bits. El registro DE es el formado por el par de registros de 8 bits D y E, para conformar un registro de 16 bits. En cambio, en la dirección 86A0 tenemos 9D, una instrucción que ocupa un solo byte y que significa que se debe restar el contenido del registro L + el bit de acarreo, del contenido del registro A. El resultado se guarda en A.&lt;/p&gt;

&lt;p&gt;Si en esta posición ponemos en cambio el byte B7, entonces estaremos haciendo un OR del valor del registro A con el valor del registro A. La operación OR comprueba un valor en binario con otro, y pone a 1 aquellos pares de valores que contengan 0 y 1, o 1 y 1. Por ejemplo, si tomamos un valor en binario como 0110, que es 6 en binario, con 1100, que es 12, obtendremos 1110, que es 14. Sí, ¡se puede utilizar para sumar! ¿Pero, qué pasa si hacemos un OR de un valor consigo mismo? Pues nada. El valor se queda como estaba. Y eso es lo que quiere conseguir el &lt;strong&gt;POKE&lt;/strong&gt;: eliminar la resta para que no se reste una vida. Pero, a la vez, deja el registro A y el acarreo como tiene que estar para poder continuar la ejecución sin que esta se vea afectada.&lt;/p&gt;

&lt;p&gt;De acuerdo. La dirección en memoria 36780 (8FAC), contiene una instrucción que debemos poner a 0 para que el tiempo sea infinito. El opcode 0 es NOP, es decir, no hacer nada. Si nos fijamos, en esa posición hay otro SBC, otra resta teniendo en cuenta el acarreo. Si la ponemos a 0, simplemente no se hace. En este caso, no es necesario dejar el registro A y el acarreo de ninguna manera especial para que la ejecución continúe correctamente.&lt;/p&gt;

&lt;p&gt;Y bueno, este es el proceso que seguiría un &lt;em&gt;hacker&lt;/em&gt; de 8 bits para encontrar los &lt;strong&gt;POKE&lt;/strong&gt;s del juego. Buscaríamos por ejemplo el valor 3, descartando todos los valores hasta encontrar el número de vidas inicial en una dirección de memoria concreta. Después buscar restas de esa posición en memoria, buscando por el opcode SUB o SBC hasta encontrar la instrucción que resta la vida, y encontrar el valor adecuado que consigue que se haga la resta mientras a la vez consigue que continúe la ejecución sin errores. Y cada error, supone cargar el &lt;strong&gt;Mons3&lt;/strong&gt; desde cinta, y a continuación el juego desde cinta. Tiene mérito, ¿eh?&lt;/p&gt;

&lt;p&gt;Y ahora es necesario crear el cargador del juego. Es decir, la parte BASIC que carga el juego. Nos habíamos quedado con esto:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bbcbasic&lt;br&gt;
20 load "StopExpr2" screen$: load "StopExpr3" code: rem randomize usr 48096&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Vamos a pasarlo a lo siguiente. Tenemos que cambiar los valores después de cargar el juego, y antes de ejecutarlo. Podemos hacerlo como aparece a continuación, creando el archivo &lt;code&gt;cargador.bas&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bbcbasic&lt;br&gt;
10 load "StopExpr2" screen$&lt;br&gt;
20 load "StopExpr3" code&lt;br&gt;
30 cls:print "Stop the Express cracked"&lt;br&gt;
40 input "Vidas infinitas (s/n):";v$&lt;br&gt;
50 if v$(1)&amp;lt;&amp;gt;"n" then poke 34464, 183:poke 34926, 183:poke 35257,183&lt;br&gt;
60 input "Tiempo infinito (s/n):";t$&lt;br&gt;
70 if t$(1)&amp;lt;&amp;gt;"n" then poke 35780, 0: poke 39549, 0&lt;br&gt;
100 randomize usr 48096&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&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%2Fap4obwe305l6bg9ljgmh.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%2Fap4obwe305l6bg9ljgmh.png" alt="Cargador en BasinC"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nos podemos descargar &lt;a href="https://github.com/ref-xx/basinc" rel="noopener noreferrer"&gt;&lt;strong&gt;BasinC&lt;/strong&gt;&lt;/a&gt;, y ejecutarlo (en linux, necesitaremos &lt;a href="https://www.winehq.org/" rel="noopener noreferrer"&gt;Wine&lt;/a&gt;). Cargamos el archivo &lt;code&gt;cargador.bas&lt;/code&gt; en BasinC, y seleccionamos &lt;strong&gt;File&lt;/strong&gt; &amp;gt;&amp;gt; &lt;strong&gt;Export as tap&lt;/strong&gt;, guardándolo como &lt;code&gt;cargador.tap&lt;/code&gt;.&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%2Fofy7bw9nlmk5etflqv72.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%2Fofy7bw9nlmk5etflqv72.png" alt="Cargador ya en el Speccy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ya solo queda volver &lt;strong&gt;FBZX&lt;/strong&gt;, y pulsar F3 para seleccionar una cinta, escoger &lt;code&gt;cargador.tap&lt;/code&gt;, pulsar ESC e introducir &lt;code&gt;SPECTRUM&lt;/code&gt;, y después pulsar &lt;code&gt;J&lt;/code&gt;, &lt;code&gt;Ctrl&lt;/code&gt; + &lt;code&gt;P&lt;/code&gt; dos veces y ENTER para ejecutar &lt;code&gt;load ""&lt;/code&gt;, pulsando entonces F6. Al terminar la carga, pulsamos &lt;code&gt;R&lt;/code&gt; y ENTER para ejecutar el programa. Seleccionamos la cinta del juego original con F3 y 1, y al pulsar ESC y F6, el juego se cargará saltándose el BASIC original, y preguntándonos si queremos vidas y tiempo infinito. Al terminar las preguntas, el juego se ejecutará modificado.&lt;/p&gt;

&lt;p&gt;Enhorabuena, ¡eres un &lt;strong&gt;hacker&lt;/strong&gt;!&lt;/p&gt;

</description>
      <category>sinclair</category>
      <category>spanish</category>
      <category>hack</category>
      <category>games</category>
    </item>
  </channel>
</rss>
