Friday, August 29, 2014

Reviviendo el pasado


 Internet es el depositario del presente y pasado de muchísimas cosas que los seres humanos hemos hecho y hacemos. Los contenidos, la información que desde hace siglos tenemos podemos encontrarlas ya muchas veces digitalizada. Los videos de años pasados, por ejemplo, la época dorada de Mtv, por ejemplo, se puede hallar en los depósitos de videos, como YouTube, por ejemplo.

Y quizás esto no parezca a las nuevas generaciones algo importante, pero el hecho de tener acceso a materiales que llevan muchos años y que finalmente han sido pasados a algún formato digital, es extraordinario, porque nos permite revisar con precisión cómo eran las anteriores generaciones, qué pasaba hace 30, 40, 50 años quizás, y así viéndolo en perspectiva, ver cómo hemos... ¿avanzado?

Y si hablo de esto es porque hoy me llamaron la atención sobre un hecho que me pasó. Dejé las llaves de mi casa en la mesa donde siempre se dejan y quedaron como se ve en la foto que ilustra este artículo. La llave más larga quedó "de pie". Y entonces recordé una vieja serie que veía de niño: "La Dimensión Desconocida". Me acordé de un capítulo que trataba de un tipo que compraba el periódico, tiraba la moneda con la que pagaba en una cajita que tenía el vendedor y la moneda caía de canto. Este hecho metía al protagonista de la historia en la "dimensión desconocida", en donde podía entonces leer la mente de los que tenía alrededor. Era sorprendente. Realmente podía leer lo que otros pensaban. Sin ahondar en la temática de ese particular programa, el efecto desaparecía cuando el mismo protagonista compra de nuevo el periódico, tira una moneda y ésta hace que la que está de canto caiga.


Hay otros capítulos memorables, como aquel donde sale Burguess Meredith, mucho antes de que fuese el Pingüino en la serie de Batman, en la cual representa a un cajero que le gustaba mucho leer y entonces, en el momento de su comida, se metía a la bóveda del banco a leer y comer un sandwich. Pero ocurre un cataclismo nuclear y cuando el personaje se recupera de lo que pasó, halla que es el único ser sobre la Tierra... Y empieza a deambular por la ciudad... Encuentra un arma pero desecvha la idea de matarse. De pronto encuentra la biblioteca pública y decide que ahora sí, tendrá todo el tiempo del mundo para ponerse a leer. Hace una estantería coon todos los libros que piensa leer de aquí hasta que se muera pero... Bueno, no les cuento el final.

Pero lo más interesante de esto es que de pronto hallé muchos capítulos completos (en español), del viejo programa, La Dimensión Desconocida, que en su momento fue uno de los mejores que hubo en la televisión. Es increíble pues que podamos acceder a estos programas de forma gratuita, porque alguien los puso en formato digital y de alguna manera hizo algo que es parte de la naturaleza humana, el querer conservar el pasado.

Los medios digitales nos están dando esta oportunidad dorada. Es en ese sentido un mundo extraordinario.

Muchos de los capítulos pueden verse aquí.

Wednesday, August 27, 2014

Que todo cambie para que todo siga igual


Hace un par de semanas ya, hubo elecciones de la Federación Internacional de Ajedrez. Competían para hacerse de la presidencia de este organismo, Garry Kasparov y Kirsán Ilyumzhinov, este último presidente ya de la FIDE por 19 años, con resultados bastante opacos, amén de que Ilyumzhinov es un personaje sinisetro en muchos sentidos (se le ha acusado de haber mandado matar a una periodista de la oposición cuando Kirsán era el presidente de Kalmikya), amén de sus declaraciones bizarras de que había sido abducido por extraterrestres y llevado a un viaje interplanetario. Ilyumzhinov dice de esta experiencia que le pidió a los extraterrestres que lo regresaran a su país, porque al día siguiente tenía que tomar un vuelo.

Pues bien, ganó Ilyumzhinov y mantendrá a la FIDE otros cuatro años bajo su control. En la asamblea donde los delegados de los diferentes países votaron, tuvieron la oportunidad de hablar por última vez, antes de la elección. Kasparov indicó que podía poner 10 millones de dólares desde ya para renovar a la FIDE y empezar con el pie derecho y con patrocinios millonarios. Ilyumzhinov en su turno dijo que podía poner 20 millones de dólares.

Y yo no sé si esta danza de millones de uno u otro candidato convenció a alguien, pero el hecho está en que Ilyumzhinov ganó y bueno, si ha prometido esta cantidad inmensa de dinero, deberíamos ver casi de inmediato resultados. Pero no, en lugar de eso nos acercamos a una bonita crisis en lo que se refiere al Campeonato Mundial, que debería disputarse en las tres primeras semanas de noviembre de este año. Magnus Carlsen, en campeón actual, pidió una postergación del match, que supuestamente se llevará a cabo en Sochi, en la sede de las olimpiadas invernales. Ilyumzhinov recvhazó la propuesta y el re-elegido presidente de la FIDE amenazó al Campeón Mundial sobre la obligación de firmar el contrato de la justa a llevarse a cabo en noviembre, so pena de quitarle el título de campeón mundial ppor no aceptar la reglamentación de la FIDE. Pero si esto fuese poco, Ilymzhinov, en una de sus típicas gracejadas, no declararía al retador, Viswanathan Anand como campeón del mundo, sino que pondría a jugar a éste con Sergei Karjakin, segundo lugar del torneo de candidatos en donde Anand obtuvo el derecho de retar al actual campeón, Carlsen.

¿Y los millones de dólares que prometió Ilyumzhinov? ¿Dónde están? ¿No podría poner parte de este dinero para tentar a los jugadores a dirimir el mundial en una sede mejor situada y más popular? Porque miren, hay reportes de que Sochi, después de que se acabaran las olimpiadas de invierno, se convirtió en prácticamente un pueblo fantasma. ¿Quién diablos quiere jugar en ese inhóspito sitio en donde irán un puñado de periodistas especializados y los jugadores con sus equipos de analistas? Si de por sí, el ajedrez es de difícil promoción, llevarlo a Sochi parece una estupidez, característica de un tipo como Ilyumzhinov.

Por otra parte, de nuevo la FIDE se muestra como autoritaria en lugar de ser un conducto para que el ajedrez internacional esté bien organizado. Cabe decir que la propia FIDE de Ilyumzhinov no logró que ningún país, ningún empresario con dinero siquiera, pujara para pedir la sede del campeonato mundial. Vamos, que se declaró desierta a pesar de haber ampliado el plazo. Eso habla del poco interés que las empresas y los patrocinadores parecen tener en el ajedrez. Y como bien diría el periodista español Leontxo García, "tenemos un gran producto, el ajedrez, el cual no sabemos vender o en el mejor de los casos vendemos muy mal".

Yo no entiendo para qué Ilyumzhinov insistió con estar cuatro años más en la FIDE si iba empezar su actuación (más bien continuarla), con semejante tropiezo político ante el evento que debería ser el más importante del ajedrez en el planeta: un campeonato mundial. La figura de campeón mundial debería tener algún peso, pero no, para Ilyumzhinov, acostumbrado a ser un dictador, esto no le parece importante.

En resumen, nos acercamos a una crisis que me parece va a terminar con quitarle el título de Campeón Mundial al joven talento Magnus Carlsen. Tengo la impresión que Ilyjmzhinov no cederá y que Carlsen en el fondo, siendo el mejor jugador del mundo, puede ignorarlos porque finalmente su calidad de juego habla por él. Sin embargo, es triste que regresemos a estas circunstancias en donde todo puede ponerse "patas para arriba" en un santiamén. La culpa de esto es de Ilyumzhinov y de esta FIDE dizque millonaria, que no sabe jamás qué hacer de forma correcta.

Saturday, August 23, 2014

Nuevo reto ¿de la programación lúdica?


Hace un par de sábados, en una de las reuniones de ajedrez que suelo tener ese día, estaba un palindromista famoso, Willy de Winter, Maestro Internacional de Ajedrez (ver foto más abajo), traductor perito y un especialista en idiomas (al menos cuatro y me quedo corto). En esa misma reunión de pronto apareció Merlina Acevedo, que ha sido en twitter todo un fenómeno por sus complejísimos palíndromas, por la longitud de los mismos, que rayan en la locura.


Ya aquí hablamos de Merlina y de los palíndromas en general, pero en esa reunión a la que hago referencia, le pregunté a Merlina si había palíndromas hechos con palabras y no letras. Vamos, que en los palíndromas normales, la unidad es la letra y por ello por ejemplo, "Sé verlas al revés"  (así podría definirse con un palíndroma al palíndroma), se lee igual de principio a fin y viceversa, pero ¿y si la unidad de estos palíndromas es la palabra?, por ejemplo: "Nuevamente comamos, bebamos, comamos nuevamente". Esto no es un palíndroma en donde se lee de derecho al reves en letras pero si la unidad no es la letra, sino la palabra, entonces estaríamos hablando de un "palíndroma de palabras" (no sé cómo  bautizarlo).

 Merlina me dijo que pensaba que si existían, tenían estos "seudo-palíndromas" ser palíndromas por sí mismos, por ejemplo: "somos seres somos", pero le puse un ejemplo: "político pinche, pinche político", y no se cumple esta condición que ella suponía que era obligatoria. Un par de días después, Merlina me mandó uno que halló, en inglés: "Blessed are they that believe that they are blessed" (que más o menos se traduce como esto: "Bendecidos son aquellos que se creen bendecidos" (que en español pierde la propiedad de ser palíndromas por palabras)... Después me mandó un mensaje que decía: "Afortunados no los que somos, ¿somos qué los no afortunados?", pero no convence. Y lo modificó de esta manera: "Afortunados no somos, somos no afortunados" y acto seguido en un nuevo mensaje me puso éste: "Afortunados no somos muchos, muchos somos no afortunados". Da la impresión que estos palíndromas hechos con palabras y no con letras son mucho más difíciles de hacer o peor aún, parece ser muy complejo expresar ideas tan sofisticadas como la propia Merlina ha hecho con los palíndromas originales en donde la unidad es la letra.


Merlina Acevedo, con su libro de palíndromas y aforismos


Así pues, en un intento de entender más este problema, el siguiente reto de la programacióbn lúdica no involucra a los programadores necesariamente, sino a todos aquellos que quizás tengan en ingenio para crear palíndromas con palabras y no con letras. Al mejor palíndroma de palabras que se me mande se ganará una taza con el logo de La Morsa, una gorra y un cuaderno de notas, donativo de Qualcomm más quizás un par de premios más de otros amigos que están colaborando en esta idea. Pueden mandarme sus palíndromas con palabras a morsa@la-morsa.com. El reto durará un par de semanas. Cabe decir que si no hay buenos palíndromas, el concurso quedará desierto, es decir, no se dará premio alguno, cosa que espero no pase.

Si el ganador es de provincia o fuera de este país, le mandaré una memoria USB de al menos 8 GB. Esto es porque el costo de enviar una taza es ridículamente costoso. Así que por quizás única ocasión, este reto de la programación lúdica no tiene que ver con programación de computadoras, sino con algo más sutil, el ingenio para crear palíndromas de palabras. Esperemos que así le entre más gente a los retos propuestos.

Sunday, August 17, 2014

¿Cuál es la mejor jugada, 1.e4 o 1.d4?


¿1. e4 o 1. d4?

Las dos jugadas más usadas en las partidas de ajedrez son las que se refieren al dominio del centro, a sacar las piezas rápidamente. De todas las jugadas de peones, 1.e4 o 1.d4 son las que más libertades otorgan a las otras piezas y de hecho, son las dos más usadas estadísticamente en la historia del ajedrez. pero... ¿cuál es mejor?

Para dar una posible respuesta -no definitiva- apelaré al artículo de Claude Shannon, el cual entre sus múltiples trabajo, en algún momento decidió incursionar sobre cómo debería programarse una computadora para poder jugar un ajedrez sensato, digamos que pudiese competir con los seres humanos, porque el escribir un programa que juegue simplemente al ajedrez, aunque juegue mal, no tiene ningún chiste.

Shannon escribió un artículo por los años cincuenta del siglo pasado, titulado "Programming a Computer for Playing Chess", en donde analiza a detalle lo que hay que hacer. Cabe decir que Shannon sí entendía bastante de ajedrez y tenía claras las ideas que quería plasmar en su eventual programa. Es importante decir que en ese entonces no se tenía acceso fácilmente a una computadora y además, ni siquiera existía el tema de la computadora personal. Pero debe quedar sin embargo algo claro: el artículo de Shannon es tan importante que prácticamente todos los programas actuales tienen muchas de las ideas que planteó el científico en su momento.

Sin ir a detalle sobre la idea de Shannon, propone éste una estrategia que le llamó tipo A, en donde a partir de una posición dada, se exploran todas las líneas de juego hasta una profundidad fija y se les asigna una puntuación al final de cada continuación. La puntuación  asignada a la posición se denomina hoy en día "función de evaluación" y es una medida de qué tan buena es una posición para el lado de quien le toca jugar. Para decidir esa puntuación, Shannon sugirió un número de factores que deben tomarse en cuenta:

  1.  Material: asignando a las piezas los valores tradicionales, Dama (D)=9, Torre (T)=5, Alfil (A)=3.5, Caballo (C)=3, Peón (P)=1 y Rey (R)=200 (no importa el valor del rey, porque si éste es eliminado se acabó la partida, lo que importa es que sea un valor mucho más grande que el de las demás piezas).
  2.  Formación de peones: castigo de 0.5 puntos por cada peón doblado (PD), aislado (PI) o atrasado (PB).
  3.  Movilidad: es deseable tener muchas jugadas disponibles para el jugador que le toca mover y pocas para el oponente. Shannon sugiere 0.1 puntos por cada jugada disponible (JD).

Basado en esto, la función de evaluación bien puede escribirse como:

f(posición) = 200 (R-R') + 9(D-D') + 5(T-T') + 3.5(A-A') + 3(C-C') + (P-P') - 0.5(PD-PD'+PI-PI'+PB-PB') + 0.1(JD-JD')

donde las piezas primales son las del enemigo. Así, un valor positivo significa que las blancas tienen ventaja, una valoración negativa implica que el negro tiene mejor posición.

Cabe decir que esta función elemental es bastante acertada en la mayoría de las posiciones que se dan en ajedrez, pero desde luego, hay tantas excepciones que hay que refinar mucho más dicha función. De hecho, esto es el secreto de los programas más fuertes y no se conoce en la mayoría de los programas comerciales.

Pero si tomamos el criterio establecido por Shannon, podemos decidir si 1.e4 es mejor que 1.d4. Después de 1.e4, las blancas tienen 30 movimientos posibles (contando incluso los del rey). Después de 1.d4 las blancas tienen una valoraciín menor en la función descrita por Shannon.

Thursday, August 14, 2014

Reto de la programación lúdica: protector de pantalla



Todos sabemos que los protectores de pantalla surgieron de la necesidad de evitar que las pantallas de rayos catódicos, como en las televisiones de hace unos años, “quemaran”  el fósforo de las pantallas y las dejara “marcadas”. Por ejemplo, me tocó ver monitores con las marcas típicas de los programas como las hojas de cálculo, en donde se observaba claramente una imagen fantasmal de cómo se veía el programa en cuestión cuando se utilizaba.

Por ello los protectores de pantalla existen, buscando resolver esta dificultad. Esto llevó, como evolución básica quizás, a la creación de protectores de pantalla que ponían imágenes en movimiento, animaciones, gráficas muilticolores que iban cambiando, etcétera, cuando finalmente el mejor protector de pantalla es el que se apague la pantalla hasta que el usuario toque alguna tecla, mueva el ratón o bien, toque el monitor en estas posmodernas interfaces táctiles.

Así entonces, en este reto de la programación lúdica buscamos un protector de pantalla orioginal, moderno, que aparte de cumplir con la tarea de evitar el desgaste innecesario del monitor, nos presente algo artístico, divertido tal vez. Ustedes -programadores- son quien deciden.

El reto implica hacer no solamente un programa que haga algo en la pantalla, sino que se pueda poner en el listado de los protectores de pantalla de Windows. No se trata de que me pasen un enlace a uno de los miles y miles de protectores de pantalla que ya han sido escritos, sino que la idea es que se programe,en el lenguaje que quieran, su propio protector.

En este caso no hay dificultades ni problemas para usar bibliotecas en su propia herramienta de programación. La condición sine qua non (es decir, obligatoria), es que el programa pueda ponerse en la carpeta donde residen los protectores de pantalla de Windows y pueda usarse como tal. Igualmente, el protector de pantallas debe ser para el sistema operativo Windows 7 en adelante.


Los protectores de pantalla son programas que en términos generales funcionan como cualquier ejecutable, aunque tienen un par de especificaciones que hay que tomar en cuenta.Una de ellas es que los archivos no son .exe sino .scr, pero me parece hay un par de cosas que hay que contemplar para hacer un protector de pantalla.  Sugiero le echen un vistazo a este enlace para resolver los detalles técnicos que tiene este tipo de programas.

Este tipo de programas, probablemente puedan encontrarlo en la red. No se trata pues de copiarlo de algún sitio. Si descubro una copia o si tengo dudas, me tomo la libertad de preguntarle al autor directamente cómo hizo, para garantizar así que la solución fue escrita y no copiada por el autor del programa. Juguemos pues limpiamente y aprendamos todos de estos retos.

¿El premio? Una taza con el logotipo de la Morsa a la mejor solución, en donde ganará el que visualmente sea más atractivo. Como esto es una cuestión subjetiva, un jurado calificado por los articulistas de unocero (más otros personajes externos [sus nombres se mencionarán al terminar el concurso]), serán quienes tomen la decisión y ésta es inapelable. Hay también una gorra y una libreta de notas, cortesía de Qualcomm (sólo para quienes residan en el DF). Habrá probablemente más premios, pero estos son los que están asegurados.

El premio de la taza (con el añadido de la gorra y el cuaderno de notas), solamente aplica a los programadores que vivan en el DF (mandar a provincia o a otros países una taza es estúpidamente costoso). En caso de que los concursantes sean de otros países o de la provincia mexicana, el premio será una memoria USB de 8 GBytes al menos y se les enviará por correo certificado.

Así que ¡manos a la obra! Sorpréndanme…

Tuesday, August 12, 2014

Angel Flores, Gabriela Retes, y una visita maravillosa

 Con Gabriela Retes, la hija del "bulto"

"El Bulto" es una película mexicana, del año 1991, dirigida por Gabriel Retes Mello. La trama -resume la Wikipedia- habla de Lauro (Gabriel Retes), un fotógrafo liberal e izquierdista que cubre El Halconaz. Es golpeado y entra en coma. Despierta después de 20 años, cuando su mujer se ha casado con otro y sus hijos entonces pequeños ya son adultos. Lauro tiene que acostumbrarse a la nueva cultura y ver a sus amigos convertidos en profesionistas y no en los jóvenes liberales que conoció.

El realizador aprovecha la apertura de los medios y del gobierno respecto a hechos pasados que antes eran censurados, en este caso El Halconazo, para hablar de ellos, denunciarlos, y ya no negarlos. Por otro lado, la película retrata la hipocresía de la sociedad mexicana y cómo la gente y sus ideales cambian y son absorbidos por «el sistema».


Pues hablo de esta película porque el domingo pasado fui con Pilar a Tepoztlán, en el Edo. de Morelos. La habían contratado para dar una función de teatro infantil. Llegamos a casa de Angel Flores Torres, el cual es cineasta, el cual a todo esto es ex-marido de Gabriela Retes, la hija de "el bulto".

La compañía "Arte para Chamacos y más..."

La verdad es que fue un domingo único. Angel es un tipazo. Sus tres hijas, Luciana, Sabina y Eloisa me parecieron encantadoras, inteligentes y además, muy educadas y cariñosas. Gabriela Retes estaba dando de desayunar junto con Pilar Campesino (una importante dramaturga amén de ser muy amable y agradable), a sus hijas cuando llegamos a su casa. Angel nos invitó a comer a su mezcalería (una hamburguesa de sirloin) y al final de la función -que se dio ahí mismo- nos invitaron a probar el mezcal. Y lo probé, aunque se demostró rápidamente que no sirvo como bebedor. Demasiado fuerte.

Pero más allá de la foto que me tomé con la hija del "bulto", y el día extraordinario que pasamos, me dejó un gran sabor de boca el ver que Angel busca que la gente de su comunidad tenga acceso a cultura más allá de lo que ofrece el gobierno y para ello, pone de sus propios medios. Se necesita mucha gente así en México y me congratulo de haber podido ir. Fue un placer y privilegio conocer a esta familia, a esta visión de gente del cine, en donde la cultura, la inteligencia, los contenidos importantes, están presentes todo el tiempo.

Con Angel Flores

Friday, August 08, 2014

Del pirateo de libros


Hace unos días vi en una página de ajedrez que iba a salir un libro sobre Nimzowitsch, llamado "Nimzowitsch, Move by Move", del MF Steve Giddins. Considerando que tengo un trabajo a medio terminar sobre el gran ajedrecista danés, nacido en Riga, pensé que éste sería un libro por adquirir. Entré entonces a Amazon y hallé que el libro se venderá en esta tienda virtual en un par de meses. En el Reino Unido saldría a principios de agosto. Así que lo puse en mi lista de deseos de Amazon y pensaba hacerme de él.

Pero resulta que en un sitio web al que entro, hallé que la versión electrónica (no PDF ni ePub, sino en formato Chessbase, que parece ser armada por la editorial que edita el libro), para ser descargada gratuitamente. Esto obviamente no es lo más legal del mundo y quiero suponer que alguien que compró la versión electrónica decidió hacerla pública y disponible para todos, pasándose por "el arco del triunfo" los derechos de autor de Everyman Chess y de Giddins.

El libro se ve interesante y cuando salga la versión en papel, la compraré, pero lo que me interesa es ver cómo las editoriales están ya, o empezarán pronto a estarlo, en desventaja ante la facilidad para publicar cualquier contenido en la red. No es el primer libro que se puede conseguir gratuitamente, ni será el último, pero esto habla claramente de que la situación de vender libros ha cambiado radicalmente con la llegada de Internet. De hecho es probablemente la industria editorial la última que ha tenido que empezar a lidiar con el problema de la piratería. Porque ahora no solamente hay quien en un ataque de tiempo libre inesperado, decide escanear un libro, página por página, y subirlo a la red, sino que no falta quien se hace de un libro electrónico, con DRM (derechos digitales de autor), el cual se pueden quitar si se sabe cómo, y entonces el libro queda a disposición de quien quiera descargarlo. Si además, como en el caso que menciono, el libro viene en formato para Chessbase (un manejador de base de datos de partidas), entonces el asunto no tiene vuelta de hoja: libro pirateado, lo que implica menos ganancias a la editorial y al autor.

Yo quisiera pensar que de todas maneras esto podría paliarse buscando de alguna manera un modelo de negocios que pueda satisfacer a todos, tanto editorial (y autor), como consumidores. Hay quienes a pesar de que sus libros están en PDF hacen dinero pues la gente compra sus libros. Un ejemplo es Adrián Paenza y su serie de libros de divulgación: "Matemática... ¿Estás ahí?". Se venden bien y probablemente se descarguen más aún. Es decir, el mecanismo quizás haga que el autor sea más popular y su libro o libros reciban más publicidad y por otra parte, la gente, al ver que el autor pone sus libros legalmente, decida comprárselos.


¿Qué hacer con este problema? No lo sé. Pareciera que muchos autores no tienen grandes preocupaciones al respecto. Sin embargo, es interesante hacer notar que por ejemplo, mi libro de Editorial Selector "Perfeccione su Ajedrez", se había descargado de uno de estos depósito de archivos públicos, unas 1000 veces. Es decir, son regalías que no recibiré y es dinero que la editorial tampoco recibirá.

Opino pues que se necesita un nuevo modelo de negocios. Sin duda Internet ha logrado cambiar el paradigma de cómo hacíamos antes muchas cosas y que hoy simplemente eso ya no funciona.

Tuesday, August 05, 2014

Una idea millonaria


Ahora que en estos pasados meses he tenido que hacer un sinfín de trámites, de papeleos, de conseguir el apostille de mi título, de hacer el TOEFL, de recuperar acta de nacimiento, tomarme fotos, recuperar los certificados de estudios anteriores, de hallar la fotocopia tamaño carta de mi título de licenciatura, etcétera, me di cuenta que este país no puede seguir haciéndonos esto. Me explico: por muchos años he trabajado en diferentes centros de la UNAM y en cada un de ellos he tenido que dar fotografías tamaño infantil con fondo blanco, actas de nacimiento, copias de mis certificados, y así hasta la náusea. Esto es ridículo. Las montañas de papel que se guardan en algo que se denomina eventualmente "archivo muerto", llenan gavetas y espacio en toda la UNAM. El papel tiene que protegerse además, de las inclemencias del tiempo, de las eventuales goteras en algún centro de trabajo, etcétera. Es decir, el papeleo genera muchos problemas de más.

Y he aquí mi idea, que me parece que podría ser un negocio millonario. Lo pondré a manera de pregunta: ¿Por qué la UNAM no tiene un servidor central de documentos digitalizados? Imaginen que ustedes se inscriben en la UNAM, entregan sus papeles. Estos se digitalizan y listo, cada vez que necesitemos en la UNAM algún papel o se nos requiera algún documento, la dependencia que nos lo pida podrá inmediatamente decirnos si ese documento ya se tiene o no.

Un servidor de documentos digitales suena a un buen negocio y si además, quien lo ofrezca da el servicio de escaneo, podría hacer muchísimo dinero. Y que conste, la UNAM es solamente un ejemplo, pero hospitales, instituciones gubernamentales, y cuanto centro de trabajo que se les ocurra, podría acceder a un servidor central de información. Díganme que no es una gran idea.

Monday, August 04, 2014

¡Buenas noticias!


Después de N trámites, exámenes y demás angustias, fui aceptado en el doctorado en ciencias e ingeniería de la computación, en la UNAM. Son 4 años que espero resulten muy fructíferos en términos académicos. El inicio formal del doctorado es precisamente ¡hoy!

Saturday, August 02, 2014

Ya tenemos ganador del reto de la programación lúdica sobre los laberintos


Hace ya un par de semanas pusimos en el reto de la programación lúdica, la creación de un laberinto. Hubo una discreta participación, la cual quiero creer, se debió en parte a que el reto sonaba demasiado complejo y aunque no lo era tanto, requería quizás de más horas de las que muchos podrían haberse ocupado para resolverlo. Se recibieron algunas participaciones que invalidé porque parecían copia de algún programa de Internet y ante mi petición de aclarar el asunto hubo incluso silencio. Y que conste, no estoy acusando a nadie, solamente que la idea de los retos es que los que participan los resuelvan porque ése es el chiste, amén de que en eso reside la diversión. Finalmente, hubo tres finalistas. Uno usó Java (Salvador González), otro Javascript (Gabriel Martínez), con ayuda de JQuery y un tercero, escrito en Delphi. La decisión no fue fácil pero me parece que el ganador hizo la versión más manejable y visualmente más adecuada a la interfaz gráfica. El ganador es pues Guillermo Cañedo y este es su código (en Delphi):
 
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, StdCtrls, Spin;

type
  TMurosSet = set of (arriba, abajo, derecha, izquierda);

  PCelda = ^TCelda;
  TCelda = record
      i, j, pos: Integer;
      visitado: Boolean;
      etiqueta: String;
      muros: TMurosSet;
      adyacentes: TList;
  end;

  PRef = ^TRef;
  TRef = record
      pos: Integer;
      muroComun: TMurosSet;
  end;

  TForm1 = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    Button1: TButton;
    Lienzo: TPaintBox;
    rens: TSpinEdit;
    cols: TSpinEdit;
    Label1: TLabel;
    Label2: TLabel;
    Button2: TButton;
    Label4: TLabel;
    procedure LienzoPaint(Sender: TObject);
    procedure rensChange(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormResize(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    dx, dy: Integer;
    Q: TList;
    solucion: TStringList;
    verSol: Boolean;
    procedure calcTamCeldas;
    function ObtieneListaDeNodos: TList;
    function obtieneNodo(i, j: Integer): PCelda;
    function obtieneNodosAdyacentes(i, j: Integer): TList;
    function refCelda(i, j: Integer): Integer;
    procedure DFS(nodo: PCelda);
    procedure DerribarMuro(A, B: PCelda);
    function obtieneNodosPorDondePuedePasar(nodo: PCelda): TStringList;
    function HayHabitacionesF: Boolean;
    function getMuroRandom(nodo: Pcelda): PCelda;
    function obtieneHabitacionF: PCelda;
    procedure etiquetar(nodo: pCelda; etq: String);
    function obtieneMuroQueIntercepta(A, B: PCelda): TMurosSet;
    function Regresa: PCelda;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}


procedure TForm1.LienzoPaint(Sender: TObject);
var
   i, j, rx, ry: integer;
   R: TRect;
   celda: PCelda;
begin
    R.Left := 0;
    R.Top := 0;
    R.Right := Lienzo.Width;
    R.Bottom := Lienzo.Height;
    Lienzo.Canvas.Brush.Color := clWhite;
    Lienzo.Canvas.Brush.Style := bsSolid;
    Lienzo.Canvas.FillRect(R);

    Lienzo.Canvas.Pen.Color := clBlack;
    Lienzo.Canvas.Pen.Width := 1; 
    if Q <> Nil then
      begin
          for i := 0 to (Q.Count - 1) do
               begin
                   celda := Q[i];

                   rx := celda^.j*dx;
                   ry := celda^.i*dy;
                   
                   if arriba in celda^.muros then
                       begin
                           Lienzo.Canvas.MoveTo(rx, ry);
                           Lienzo.Canvas.LineTo(rx + dx, ry);
                       end;
                       
                   if abajo in celda^.muros then
                       begin
                           Lienzo.Canvas.MoveTo(rx, ry + dy);
                           Lienzo.Canvas.LineTo(rx + dx, ry + dy);
                       end;

                   if derecha in celda^.muros then
                       begin
                           Lienzo.Canvas.MoveTo(rx + dx, ry);
                           Lienzo.Canvas.LineTo(rx + dx, ry + dy);
                       end;

                   if izquierda in celda^.muros then
                       begin
                           Lienzo.Canvas.MoveTo(rx, ry);
                           Lienzo.Canvas.LineTo(rx, ry + dy);
                       end;

               end;

           if (verSol) and (solucion.Count > 0) then
               begin
                   Lienzo.Canvas.Pen.Color := clRed;
                   Lienzo.Canvas.Brush.Style := bsClear;
                   Lienzo.Canvas.Pen.Width := round(dy/3); 
                   celda := Q[StrToInt(solucion[0])];
                   rx := celda^.j*dx;
                   ry := celda^.i*dy;
                   Lienzo.Canvas.MoveTo(round(rx), round(ry + dy/2));

                   for i := 0 to (solucion.Count - 1) do
                       begin
                           celda := Q[StrToInt(solucion[i])];
                           rx := celda^.j*dx;
                           ry := celda^.i*dy;
                           Lienzo.Canvas.LineTo(round(rx + dx/2), round(ry + dy/2));
                       end;
                       
                   Lienzo.Canvas.LineTo(round(rx + dx), round(ry + dy/2));

               end;
      end
    else
      begin

           for j := 0 to cols.Value do
               begin
                   Lienzo.Canvas.MoveTo(dx*j, 0);
                   Lienzo.Canvas.LineTo(dx*j, Lienzo.Height);
               end;

           for j := 0 to rens.Value do
               begin
                   Lienzo.Canvas.MoveTo(0, dy*j);
                   Lienzo.Canvas.LineTo(Lienzo.Width, dy*j);
               end;
            
      end;
end;

function TForm1.ObtieneListaDeNodos: TList;
var
   i, j: integer;
   P: PCelda;
begin
   Result := TList.Create;
   for i := 0 to rens.Value - 1 do
       for j := 0 to cols.Value - 1 do
           begin
              New(P);
              P^.i := i;
              P^.j := j;
              P^.visitado := false;
              P^.etiqueta := 'u';
              P^.muros := [arriba, abajo, derecha, izquierda];
              P^.pos := refCelda(i, j);
              P^.adyacentes := obtieneNodosAdyacentes(i, j);
              Result.Add(P);
           end;
end;


function TForm1.obtieneNodo(i, j: Integer): PCelda;
var
   k: integer;
   P: PCelda;
begin
   Result := nil;
   for k := 0 to (Q.Count - 1) do
       begin
           P := Q.Items[k];
           if (P^.i = i) and (P^.j = j) then
               begin
                   Result := P;
                   Exit;
               end;
       end;
end;


function TForm1.refCelda(i, j: Integer): Integer;
begin
  Result := i*cols.Value + j;
end;


function TForm1.obtieneNodosAdyacentes(i, j: Integer): TList;

    procedure Add(k: Integer; muro: TMurosSet);
    var
        P: PRef;
    begin
        New(P);
        P^.pos := k;
        P^.muroComun := muro;
        Result.Add(P);
    end;

begin

    Result := TList.Create;
    if i - 1 >= 0 then Add(refCelda(i - 1, j), [arriba]);
    if i + 1 <= rens.Value - 1 then Add(refCelda(i + 1, j), [abajo]);     if j - 1 >= 0 then Add(refCelda(i, j - 1), [izquierda]);
    if j + 1 <= cols.Value - 1 then Add(refCelda(i, j + 1), [derecha]);
end;

procedure LimpiaLista2(MyList: TList);
var
   i: Integer;
   ARecord: PRef;
begin
   if MyList <> Nil then
      begin
           for i := 0 to (MyList.Count - 1) do
               begin
                   ARecord := MyList.Items[i];
                   Dispose(ARecord);
               end;
           MyList.Free;
       end;
end;


procedure LimpiaLista(MyList: TList);
var
   i: Integer;
   ARecord: PCelda;
begin
   if MyList <> Nil then
      begin
           for i := 0 to (MyList.Count - 1) do
               begin
                   ARecord := MyList.Items[i];
                   LimpiaLista2(ARecord^.adyacentes);
                   Dispose(ARecord);
               end;
           MyList.Free;
       end;
end;

procedure TForm1.rensChange(Sender: TObject);
begin
   Button2.Enabled := false;
   verSol := false;
   LimpiaLista(Q);
   Q := nil;
   solucion.Clear;
   calcTamCeldas;
   FormResize(Sender);
   Lienzo.Refresh;
end;

procedure TForm1.calcTamCeldas;
begin
   // dx := Round(Lienzo.Width/cols.Value);
   // dy := Round(Lienzo.Height/rens.Value);
   dx := 12;
   dy := 12;
   Lienzo.Width := dx*cols.Value + 1;
   Lienzo.Height := dy*rens.Value + 1;
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
   solucion := TStringList.Create;
   calcTamCeldas;
end;

function TForm1.HayHabitacionesF: Boolean;
var
   i: Integer;
   P: PCelda;
begin
    Result := False;
    for i := 0 to (Q.Count - 1) do
        begin
            P := Q[i];
            if P^.etiqueta = 'F' then
                begin
                    Result := true;
                    Exit;
                end;
        end;
end;


function TForm1.obtieneHabitacionF: PCelda;
var
   i: Integer;
   P: PCelda;
   L : TStringList;
begin

   L := TStringList.Create;
   for i := 0 to (Q.Count - 1) do
       begin
           P := Q[i];
           if P^.etiqueta = 'F' then L.Add(IntToStr(i));
       end;
       
   if L.Count > 0 then
       Result := Q[StrToInt(L[Random(L.Count)])]
   else
       Result := nil;
   L.Free;
end;

function TForm1.getMuroRandom(nodo: Pcelda): PCelda;
var
   i: Integer;
   Ref: Pref;
   vecino: PCelda;
   L : TStringList;
begin
   L := TStringList.Create;
   for i := 0 to nodo^.adyacentes.Count - 1 do
       begin
           Ref := nodo^.adyacentes[i];
           vecino := Q[Ref^.pos];
           if vecino^.etiqueta = 'I' then L.Add(IntToStr(i));
       end;

   if L.Count > 0 then
       begin
           Ref := nodo^.adyacentes[StrToInt(L[Random(L.Count)])];
           Result := Q[Ref^.pos];
       end
   else
       Result := nil;
   
   L.Free;
end;

procedure TForm1.etiquetar(nodo: pCelda; etq: String);
var
   i: integer;
   Ref: PRef;
   hijo: PCelda;
begin
   for i := 0 to nodo^.adyacentes.Count - 1 do
       begin
           Ref := nodo^.adyacentes[i];
           hijo := Q[Ref^.pos];
           if hijo^.etiqueta <> 'I' then hijo^.etiqueta := etq;
       end;
end;


function TForm1.obtieneMuroQueIntercepta(A, B: PCelda): TMurosSet;
var
    i: Integer;
    Ref: PRef;
begin
    Result := [];
    for i := 0 to A^.adyacentes.Count - 1 do
        begin
            Ref := A^.adyacentes[i];
            if B^.pos = Ref^.pos then
                begin
                    Result := Ref^.muroComun;
                    Exit;
                end;
        end;
end;

procedure TForm1.DerribarMuro(A, B: PCelda);
begin
    A^.muros := A^.muros - obtieneMuroQueIntercepta(A, B);
    B^.muros := B^.muros - obtieneMuroQueIntercepta(B, A);
end;

function TForm1.obtieneNodosPorDondePuedePasar(nodo: PCelda): TStringList;
var
   i: Integer;
   Ref: PRef;
   adyacente: PCelda;
begin
   Result := TStringList.Create;
   for i := 0 to nodo^.adyacentes.Count - 1 do
       begin
           Ref := nodo^.adyacentes[i];
           adyacente := Q[Ref^.pos];
           if (not adyacente^.visitado) and (Ref^.muroComun * nodo^.muros = []) then Result.Add(IntTostr(Ref^.pos));
       end;
end;

function TForm1.Regresa: PCelda;
var
   L: TStringList;
begin
   L := TStringList.Create;
   Result := nil;
   while (solucion.Count > 0) and (L.Count = 0) do
       begin
           Result := Q[StrToInt(solucion[solucion.Count - 1])];
           L.Free;
           L := obtieneNodosPorDondePuedePasar(Result);
           solucion.Delete(solucion.Count - 1);
       end;

   L.Free;
end;


procedure TForm1.DFS(nodo: PCelda);
var
   celda: PCelda;
   Childs: TStringList;
begin
   nodo^.visitado := true;
   solucion.Add(IntToStr(nodo^.pos));
   Childs := obtieneNodosPorDondePuedePasar(nodo);
   if Childs.Count > 0 then
      begin
          celda := Q[StrToInt(Childs[Random(Childs.Count)])];
          DFS(celda);
      end;
   Childs.Free;
end;


procedure TForm1.Button1Click(Sender: TObject);
var
  vecino, I, F: PCelda;
  Ref: PRef;
  k: Integer;
  val: Boolean;
begin
    Button1.Enabled := false;
    Button2.Enabled := false;
    verSol := false;
    if verSol then Button2.Caption := 'Ocultar Solución' else Button2.Caption := 'Ver Solución';
    Randomize;
    LimpiaLista(Q);
    Q := ObtieneListaDeNodos;


    I := obtieneNodo(Random(rens.Value), Random(cols.Value));
    I^.etiqueta := 'I';
    for k := 0 to I^.adyacentes.Count - 1 do
        begin
            Ref := I^.adyacentes[k];
            vecino := Q[Ref^.pos];
            vecino^.etiqueta := 'F';
        end;

    repeat
        F := obtieneHabitacionF;
        I := getMuroRandom(F);
        DerribarMuro(I, F);
        F^.etiqueta := 'I';
        etiquetar(F, 'F');
    until not HayHabitacionesF;


    //// BUSCA LA SOLUCION ////
    solucion.Clear;
    I := obtieneNodo(Random(rens.Value), 0);
    I^.muros := I^.muros - [izquierda];

    repeat
        DFS(I);
        F := Q[StrToInt(solucion[solucion.Count - 1])];
        val := (F^.j = cols.Value - 1);
        if not val then I := Regresa;
    until val;
    
    F^.muros := F^.muros - [derecha];


    Lienzo.Refresh;
    Button1.Enabled := true;
    Button2.Enabled := solucion.Count > 0;
end;


procedure TForm1.FormDestroy(Sender: TObject);
begin
    solucion.Free;
    LimpiaLista(Q);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
    verSol := not verSol;
    Lienzo.Refresh;
    if verSol then Button2.Caption := 'Ocultar Solución' else Button2.Caption := 'Ver Solución';
end;

procedure TForm1.FormResize(Sender: TObject);
begin
    Lienzo.Left := Round((Panel2.ClientWidth - Lienzo.Width)/2);
    Lienzo.Top := Round((Panel2.ClientHeight - Lienzo.Height)/2);
end;

end.
 
Cabe indicar que Guillermo mandó versiones mejoradas de tanto en tanto, las cuales optimizaron la solución, lo cual fue lo que finalmente decidió que se le otorgara el primer lugar. Guillermo Cañedo Ramírez, 44 años, estudió Ingeniería Eléctrica y una maestría en Sistemas Eléctricos de Potencia en el Instituto Tecnológico de Morelia. He aquí la descripción de lo que hizo: "La estrategia que seguí", nos dice, "está basada en 2 algoritmos:" Dependiendo del número de renglones y columnas que se quieran del laberinto, se define una matriz de m renglones y n columnas y se almacena toda la información en una lista dinámica con TList (disponible en Delphi) de apuntadores a una estructura de datos TCelda.

TMurosSet = set of (arriba, abajo, derecha, izquierda);

  PCelda = ^TCelda;
  TCelda = record
      i, j, pos: Integer;
      visitado: Boolean;
      etiqueta: String;   
      muros: TMurosSet;
      adyacentes: TList;
  end;
 
y la lista de habitaciones adyacentes apunta a una estructura de datos del tipo TRef.

  PRef = ^TRef;
  TRef = record
      pos: Integer;
      muroComun: TMurosSet;
  end;
 
Primero se etiquetan todas las celdas o habitaciones de la matriz con 'u'

 1) El Algoritmo de Prim's para generar el laberinto, es como sigue:
  • a) Elegir una celda o habitación al azar y etiquetarla como I,
  • b) Etiquetar las habitaciones adyacentes de I con F
  • c) Obtener al azar una habitación etiquetada como F y derribar el muro que colinda con la habitación I,
  • d) Etiquetar F como I y
  • e) Etiquetar las adyacentes del nuevo I pero que no sean I como F
  • f) Repetir del c) al e) hasta que ya no haya mas habitaciones F
2) El Algoritmo de Búsqueda Primero en Profundidad con retroceso para hallar la solución:
  • a) Etiquetar todas las habitaciones del laberinto como no visitadas.
  • b) Elegir al azar una habitación de entrada de la primer columna y se establece como entrada del laberinto, eliminando su muro izquierdo
  • c) Establecer la habitación como nodo raíz y marcarla como visitada y guardar la posición del nodo en una lista que sera la solución.
  • d) Obtener las habitaciones adyacentes que no estén visitadas y que no tengan muro colindante para poder pasar.
  • e) Elegir al azar una de ellas y de manera recursiva repetir de c) a e) hasta que ya no haya habitaciones adyacentes sin visitar y sin muro o que la columna del nodo raíz sea igual al total de columnas del laberinto
  • f) si e) no se cumple entonces ir en sentido inverso con nuestra lista de la solución e ir eliminando el ultimo elemento hasta que haya un camino por donde pasar.
  • g) Al finalizar se marca el último nodo de la lista con la solución como salida del laberinto derribando el muro derecho.
Actualmente desarrolla aplicaciones educativas y juegos para tabletas y smartphones en forma independiente en Taos Games. Felicitamos a Guillermo y vienen más retos, con más premios. Hemos estado trabajando con algunas empresas para que nos den apoyo, así que ahora, a redoblar esfuerzos porque los premios empezarán a ponerse más atractivos... Poco a poco, pero verán que empezarán a incrementarse.

A quien le interese el código de Guillermo y el de los otros dos concursantes, poueden escribirme a morsa@la-morsa.com y se los mandaré por si les interesa estudiarlo.

Friday, August 01, 2014

La privacidad en las redes sociales


Facebook es probablemente la red social más usada en el mundo y curiosamente, para poder entrar a ella -la mayoría de quienes ya lo hemos hecho- se nos pide que aceptemos una especie de largo contrato -el cual aceptamos- y listo: a navegar en la red social, compartir contenidos, fotos, mensajes, videos, etcétera. Desafortunadamente como no leemos este "contrato" aceptamos lo que Facebook hará con nuestra información sin previa consulta y si leemos lo que hacen con nuestros datos, en términos generales se reduce a "puedo hacer lo que quiera con ellos y sin consultarte". Vean nada más aquí y aquí.

El tema de la privacidad es algo interesante por muchas razones, la primera es que si no cuidamos este aspecto, nos exponemos en Internet a muchísimas dificultades. No falta quien hace uso de tus datos, de tus fotos, de tus datos personales. Hay quien no tiene empacho en poner que sale de vacaciones, a dónde va y cuando regresa. En esta época en donde la información es un bien valioso, hay que andarse con más cuidado.

Cabe decir que la privacidad no solamente es poner controles a quién puede ver nuestros contenidos porque no olvidemos que Facebook, dentro de su propio sistema, es el "gran hermano" al mejor estilo orwelliano, porque lo ve todo y además, lo guarda para sí todo: fotos, videos, mensajes, incluso reportes que se le hacen a la red social.

Pero ¿cómo debemos cuidarnos? La realidad es que no es difícil, y no se trata de poner controles para que otros no vean nuestros contenidos, cosa que desde luego, no es mala idea restringir. En realidad, sin embargo, lo más simple es seguir esta sencilla regla: "no publique en ninguna red social nada que no quiera que otros vean".

Pero más de uno puede preguntarse "¿Qué importa que otros vean mis contenidos? Finalmente el que nada debe nada teme". Pues sí y no. Es cierto que uno podría pensar que publicar una foto de la comida en un restaurante, o bien, fotos de un evento que ocurrió en la ciudad, una marcha por ejemplo, no tiene mayores consecuencias. Podría uno estar simplemente "documentando" lo que está pasando, ¿no? Pues no necesariamente. Considerando que en Estados Unidos la Agencia de Seguridad Nacional - NSA, vigila a mucha gente y que además, debido a los ataques a las Torres Gemelas en Nueva York en el 2001, en donde Bush emitió una ley sobre "homeland security", y gracias a esta ley prácticamente el gobierno norteamericano tiene derecho de entrometerse donde le dé la gana, cualquier actividad de cualquier ciudadano, la que sea, puede ser investigada.

¿Pero esto es sólo en Estados Unidos, verdad? Pues no, es en el mundo entero. Los norteamericanos han vigilado, lo sabemos por Snowden, incluso a presidentes de otros países, incluyendo Enrique Peña Nieto. ¿Y para qué nos vigilarían? Pues porque cada acción de cualquier ciudadano bien podría ser sospechosa.

La privacidad es algo importante. Uno puede pensar que como ciudadano normal promedio no tendría de qué preocuparse, pero seamos francos, ¿para qué darle argumentos a los "guardianes" del orden? Porque como están las cosas, sin duda hay que ser muy cautos con la información aque compartimos, incluso aunque supongamos que la compartimos solamente con quienes conocemos, cosa que en general no es cierto, aunque así parezca.