Showing posts with label código abierto. Show all posts
Showing posts with label código abierto. Show all posts
Tuesday, August 01, 2017
Nueva versión del Editor de Ajedrez
Hace unos meses escribí un editor para ayudarme a la creación de artículos y libros de ajedrez. La problemática es simple: un procesador de palabras normal no tiene manera fácil de hacer diagramas o de desplegar la notación ajedrecística que en general aparece en los libros y artículos de ajedrez.
Así, la primera versión del editor permite cargar una base de partidas en formato PGN (Portable Game Notation), que es el estándar internacional para describir las partidas de ajedrez en formato electrónico. Una vez teniendo la base de partidas, se puede cargar alguna partida y mover las piezas con los controles de atrás y adelante. Una vez que se tiene la posición de interés, se puede pasar el diagrama de la misma al editor de forma automática. Igualmente, se puede pasar la partida (en formato PGN) con los figurines correspondientes como se usa actualmente en los libros especializados.
El editor permite marcar texto y ponerlo en negritas, cursivas o subrayado. Se puede formatear el texto a la izquierda, a la derecha, centrado y justificado en ambos bordes. El editor también puede corregir el texto usando un diccionario de palabras que viene de Linux, pero que hace la labor de corrección algo más sencillo.
Lo nuevo del editor es que se han incluido dos nuevos botones, uno etiquetado como "Txt", el cual pone al editor en formato de escribir texto normal. El botón que tiene una figurita de rey cambia el tipo de botón al de figurines, que es el de la notación de ajedrez. Anteriormente si se quería hacer eso, había que ir a la opción "Formato" y de ahí a "Fuente". De esta manera el cambio se hace de forma mucho más rápida y directamente.
También se le han añadido los símbolos más usados en la notación usada en los Informadores ajedrecísticos, publicación yugoslava, referente de esta manera moderna de anotar partidas de ajedrez. Se añaden los símbolos para "las blancas están mejor", "las blancas están ganadas", "la partida está igualada", "zugzwang", "apuro de tiempo", "novedad teórica", "nota de la redacción", etcétera. También se agregaron los símbolos de las piezas (figurines), que muchas veces se usan cuando por ejemplo, se quiere poner que hay un peón débil en la casilla a4, por mencionar un simple ejemplo. Finalmente se agregó una etiqueta que muestra qué tipo de letra (font), está activo.
Por lo demás, el editor se comporta como el anterior. Los archivos se guardan en formato RTF y se pueden entonces leer a MsWord, por ejemplo, si se le quiere dar un formato más sofisticado, por ejemplo, poner el documento en dos columnas, cosa que mi programa aún no puede hacer (y que conste, he estado investigando esta posibilidad).
Así pues, quien le interese el editor, he aquí lo que tiene que hacer (ver más abajo). Cabe decir que si ya el lector usaba la versión inicial, puede descargar solamente el enlace del programa, sin tener que volver a instalar los tipos de letra o los diccionarios. Cualquier sugerencia es bienvenida...
Este es el mensaje que mando a quienes me piden el editor:
Instala primero los diccionarios para la corrección de los artículos:
http://www.mediafire.com/download/bdyc1yyrz3ev8ls/ISpelIns+%281%29.exe
Pienso añadir más tipos de diagramas pero por el momento solamente trabaja con "zurichdiagram cbwin" que es un tipo de letra para hacer diagramas y que puedes instalar con el programa que puedes bajar de aquí:
http://www.mediafire.com/download/li5p53qzghgldph/font_setup.exe
que instalará varios tipos de letras pero ojo, no funcionará para otro tipo de diagramas por el momento. En las publicaciones, los diagramas de 20 puntos son los más adecuados, pero claro, los puedes poner del tamaño que quieras. Por otra parte, espero que haya actualizaciones más o menos continuas, pero dependerá de mi tiempo libre, porque ando un poco liado con mi trabajo académico, pero ya me daré tiempo. Por lo demás, si dejé algo en el tintero (debería decir, en el teclado),vuelvo a escribirte. Las
actualizaciones las comentaré en mi blog. Si no te mando alguna por favor, pídemela.
El editor lo puedes descargar de aquí:
https://www.mediafire.com/file/6il180lnm1ikest/setup_editor_ajedrez.exe
Avísame cualquier problema.
Friday, September 16, 2016
Otro reto de la programación lúdica: fotos usando números
En la Facultad de Ciencias (UNAM), doy un curso sobre proceso digital de imágenes. Para resumirlo, podría decir que los alumnos aprenden a hacer un programa similar a Photoshop o a GIMP, es decir, no se les enseña a usar Photoshop o GIMP, sino a crear la mayoría de los filtros que se pueden ver en esos programas.
Hay que decir que aparte de todos los filtros tradicionales, trabajamos otros que bien pueden ser artísticos aunque muchas veces pueden no gustarnos. La cuestión es en el fondo ver que la manipulación de pixeles en una imagen es un proceso relativamente sencillo y que los resultados que entrega a veces son sorprendentes.
Pues bien, ya aquí hemos descrito filtros sencillos que hacen fotografías con letras, por ejemplo, o bien usando la técnica de semitonos, que es lo que se usa para imprimir fotografías en tonos de gris aunque se tengan solamente dos colores nada más, blanco y negro.
Hoy propongo el siguiente reto que parte de una portada de un libro que me regalaron. Se trata de hacer que una fotografía se vea como la que aparece en el libro de Daniel Tammet, "La poesía de los números", un libro a todo esto muy interesante y agradable de leer. Tammet es un prodigio para las matemáticas y es autista.
Nótese que no es simplemente cambiar tonos de gris por números, sino que digamos que pone varios números en el mismo lugar para hacer que el tono de gris se vea más oscuro. Esta es una técnica que se usaba cuando las primeras impresoras no podían más que imprimir letras. Así que para poner tonos de gris más o menos oscuros, se usaba la técnica de sobreponer letras en el mismo lugar.
El reto tendrá como premio una taza de la Morsa. Si el ganador es de provincia, se le mandará un USB de 8 GB al menos, porque mandar una taza por mensajería es ridículamente costoso. Este es un programa sencillo realmente que al concursante no le tomará más de un par de horas.
La foto a procesar
Cabe señalar que este concurso busca simplemente alentar el trabajo de la programación y mostrar que puede ser lúdica. Es un concurso de buena fe. Si hay, por ejemplo, dos o más respuestas satisfactorias, ganará quien la haya mandado primero. El ganador cede su código fuente a la comunidad. Es decir, se promueve el código abierto.
Thursday, October 02, 2014
Plática sobre programación lúdica en la AMIAC
La AMIAC (Academia Mexicana de Informática A.C.), me invitó, por conducto de su presidente, Eric Huesca, amigo desde la preparatoria, a todo esto, a dar una plática en un desayuno que organizan el primer jueves de cada mes. Le dije que podía hablarles de mi experiencia en el tema de la programación lúdica en los retos que he realizado a través de la página web unocero.com, en donde su creador, Javier Matuk, siempre ha visto de buena manera mis iniciativas.
Así, hoy platiqué un poco de lo que es la programación lúdica, lo que se busca, la implementación de los retos, la respuesta de los estudiantes (y alguno que otro egresado), los cuales han trabajado arduamente para resolver los probolemas planteados. Aquí en mi blog pueden verse estos retos y los ganadores de los mismos.
Creo que les sorprendió un poco cuando les dije que el premio de estos retos eran una taza con el logotipo de La Morsa. La idea de esto es que es por una parte, se pueden hacer tazas con cualquier imagen en los Office Depot, por unos 60 pesos. Elegí una taza porque va más allá de un premio como podría ser un trofeo o una placa. La taza tiene el logotipo y la leyenda que indica qué reto se ganó. Así, el ganador podrá recordar lo que significa la taza, más allá del valor económico de la misma.
Desde luego, las tazas no se venden (ya me han dicho más de una vez que me la compran), pero si cedo, entonces pierde el valor simbólico de la misma. El chiste al final de cuentas es que la taza se otorgue a quien gana el reto.
Al final de la plática hubo una interesante discusión al respecto de la enseñanza y es claro que este tema preocupa y ocupa a todos los que estábamos ahí. Fue todo muy ilustrativo. Hice énfasis en buscar alternativas para que los estudiantes se dediquen a programar, que aprendan las artes detrás de esta actividad, así como el valor del software libre y de código abierto, que de alguna manera nos ha mostrado el camino para hacer que las máquinas hagan lo que queramos.Vamos, que todos hemos aprendido de terceros. Además, como dijo alguna vez Donald Knuth, "la cosa importante, una vez que se tiene lo suficiente para comer y una linda casa, es lo que usted puede hacer por otros, cómo puede usted contribuir a la empresa como un todo". ("The important thing, once you have enough to eat and a nice house, is what you can do for others, what you can contribute to the enterprise as a whole") (*).
Al final de la plática se rifó precisamente una taza. Los convidados al desayuno se anotaron en una lista, asignándose un número del 1 al 42. El ganador se eligió usando un programa muy simple que escribí la noche anterior para este propósito. Rompí la regla de que las tazas solamente se dan a los ganadores de los retos, pero aquí digamos, fue una taza honorífica, sin valor curricular.
Agradezco pues, todas la atenciones y cuidados que se tomaron para que pudiese haberse dado este evento.
____
(*) Jack Woehr. An interview with Donald Knuth. Dr. Dobb's Journal, pages 16-22 (April 1996)
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…
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
- 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.
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.
Labels:
código abierto,
ganadores,
laberintos,
programación,
programación lúdica,
retos,
software
Monday, June 23, 2014
Programación lúdica: el juego de NIM
Dice la Wikipedia: "En este juego, dos jugadores a los que llamaremos David y Vicente, colocan un número arbitrario de fichas (cerillas, palillos, guijarros) sobre una superficie, separadas en filas o grupos. Tanto el número de filas como el número de fichas en cada fila son también arbitrarios. El primer jugador, supongamos que es David, toma cualquier número de fichas de una fila, entre uno y el total de la fila, pero sólo de una fila. El jugador Vicente hace su jugada de manera similar, retirando algunos de las fichas que quedan, y los jugadores van alternándose en sus jugadas. Se puede jugar de modo que gane el que retire la última ficha".
Para limitar el problema, pensemos en la siguiente configuración inicial:
Un jugador puede eliminar de una de las cuatro filas, las bolitas que quiera. No importa el orden, es decir, si elimina las tres primeras bolitas de la última fila (la que contiene siete bolitas) o si lo hace salteado, una sí y uno no, alternadamente, da lo mismo. Si quita tres de la última fila, el rival podria, por ejemplo, quitar las cuatro restantes, aunque quedasen en grupos separados dentro de la misma línea.. El objetivo del juego, repito, es que quien tenga que eliminar la última bolita, pierde. Nótese que en cada turno solamente se pueden eliminar bolitas de una sola fila.
Este juego ha sido muy estudiado y se conoce un algoritmo para la solución. Aquí sin embargo, el reto se trata de lo siguiente. Por ejemplo, la configuración 1-2-3 es ganadora (esto quiere decir que en una línea quede una bolita, en la siguiente dos y en la tercera línea tres bolitas) Si hay esta configuración, quien juega pierde. De hecho, 1-2-3 es equivalente a 3-2-1 o 2-1-3, etcétera. Configuraciones que tienen que ver con la mencionada son 2-2 y 1-1-1. Por ejemplo, si llego a tener la configuración 2-2, si el rival quita una bolita de cualquiera de las dos líneas, yo quito dos y mi rival tiene que quitar la última. Si mi rival quita dos bolitas de la línea, yo quito de la línea restante una bolita y de nuevo gano, pues el contrario tiene que eliminar la última.
El reto pues consiste en hallar todas estas configuraciones ganadoras. Ya di algunas:
1-1-1
1-2-3
3-3
2-2
4-4
¿Cuántas configuraciones ganadoras hay? ¿Es 1-3-5-7 una configuración ganadora o perdedora? El resultado del programa que escriban debe dar todas las configuraciones ganadoras en el formato A-B-C-D, por ejemplo:
1-1-1
2-2
3-3
4-4
una configuración en cada línea (pueden omitirse las líneas que no contengan bolitas, que den cero, pues).
¿El premio? Una taza con el logotipo de la Morsa a la mejor solución. Esto 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 al menos 16 GBytes y se les enviará por correo certificado. Y sí, sé que no son los grandes premios pero mientras no tengamos patrocinadores, esto es lo que hay.
Evidentemente quien gane será anunciado en unocero y hasta tendrá sus quince minutos de fama. Sus programas me los pueden mandar a morsa@la-morsa.com y pueden escribirse en cualquier lenguaje. Es importante señalar que hay que mandar el código fuente, el ejecutable (si procede) y el archivo de resultados pedido.
Cabe señalar que este concurso busca simplemente alentar el trabajo de la programación y mostrar que puede ser lúdica. Es un concurso de buena fe. Si hay, por ejemplo, dos o más respuestas satisfactorias, ganará quien la haya mandado primero. El ganador cede su código fuente a la comunidad. Es decir, se promueve el código abierto. Así que “en sus marcas, listos, ¡arrraaaaancan!”
Sunday, June 22, 2014
Ganadores del reto del gato
Sorprendido alegremente por la respuesta de los programadores que visitan unocero, a escasas horas de empezar el reto, a eso de las 10 de la noche del sábado -cuando se publicó el artículo- recibí a la 1:30 am del domingo la primera respuesta. Dos más llegaron a eso de las 2:15 am y 4:40 am. En el transcurso de hoy llegaron un par más.
Debido al interés que presentaron los siguientes dos programas, se dan dos premios más, uno fue para Manuel Alcántara Juárez. Él me mandó, además, los siguientes datos: El programa genera 255168 posiciones donde 131,184 juegos los gana X, 77,904 los gana O y 46080 terminan en empate. El programa inicia con las "o" pero como dice mi tocayo, es isomorfo.
Finalmente, Charles López resolvió el problema. Su código llegó a eso de las 4 40 am. El ganador se lleva la memoria USB y los otros dos se llevan una taza con el logotipo de La_Morsa.
De izquierda a derecha: Adán Enrique, Manuel Alcántara y Charles López
Agradezco realmente el esfuerzo realizado. No pensé que iba a tener estas respuestas y tan rápido. Quien quiera los archivos zipeados de los ganadores, con el respectivo código fuente, escríbame a morsa@la-morsa.com y se los mando de inmediato. (No los puse aquí porque tuve problemas en el formateo).
Wednesday, April 02, 2014
Un reloj de ajedrez hecho en México
Para quienes jugamos ajedrez en competencias, torneos, los relojes que se usan son una necesidad. Se trata de dos cronómetros que están encendidos y apagados alternativamente. Cuando un jugador está pensando su movimiento, el reloj de su lado está funcionando. Cuando el ajedrecista hace una jugada, presiona el botón encima de su reloj. Éste se detiene y se echa a andar el reloj del adversario. En general el ajedrez competitivo, serio, se juega a dos horas para cuarenta jugadas (ambos jugadores llevan en sendas planillas la anotación de la partida) y después hay que hacer 20 jugadas en una hora. Finalmente hay un tercer ciclo que es todo el resto de las jugadas en 15 minutos con incremento de 30 segundos por jugada. Cabe decir que esto es por jugador, por ende, una partida de ajedrez de torneo puede jugarse en unas siete horas, aproximadamente, máximo.
La tecnología de relojes de ajedrez ha cambiado considerablemente en los últimos años. Con el advenimiento de los relojes digitales, se ha podido implementar relojes para el juego ciencia que van decrementando el tiempo hasta llegar a 00:00. Sin embargo, a diferencia de los anteriores relojes de manecillas, en los electrónicos es posible que el sistema, cuando se oprime el botón del reloj, se le dé al jugador un bono en tiempo, incrementando la cantidad de tiempo disponible para pensar. Esta idea se le ocurrió a Bobby Fischer para evitar los apuros de tiempo a los que llegan muchas veces los jugadores después de la sexta hora. Sin embargo, hay ajedrecistas que irremediablemente terminan siempre apurados por el reloj. Para quienes no sepan de ajedrez, si uno no llega a hacer las jugadas solicitadas en el tiempo establecido, pierde la partida. Así de grave es el asunto.
Prototipo de mi reloj de ajedrez
Curiosamente, un día, un fuerte aficionado al ajedrez, que tiene una tienda especializada en el juego ciencia, quería diseñar su propio reloj de ajedrez. Platicamos y en dicha reunión había un buen ingeniero electrónico de la UNAM, al cual se le encomendó el diseño del reloj y de la programación. Yo colaboré mandándole un simulador de un reloj de ajedrez que hice en Delphi, cuando tenía mi prototipo, para ver cuál era la problemática que estaba enfentando. Quizás este buen ingeniero tomó cuenta de esto.
El asunto es que finalmente, meses después, salió la primera versión de un sólido reloj de ajedrez, la cual me parece muy bien hecho. Hay detalles, que desde luego se cambiarán cuando empiece a producirse más comercialmente y en serie. Por ejemplo, el botón de encendido/apagado, estará en la parte trasera, para evitar que alguien accidentalmente apague el reloj en medio de una competencia. Por lo demás, el reloj cuenta con diferentes modos: partidas a cinco minutos por jugador, a un minuto, con incrementos diversos e incluso, se pueden programar cinco modos diferentes para torneos específicos en donde no se contemple una programación previa.
Me gusta este proyecto por muchas razones: primero, porque está hecho por mexicanos, quienes han apostado a este nicho comercial del ajedrez; porque esto habla que cualquiera puede, con los conocimientos necesarios, programar los microcontroladores para que hagan las funciones que se deseén (de hecho, si no me equivoco, este reloj se escribió usando Pic C); y porque finalmente, se pueden abaratar los costos de este tipo de relojes que son cada vez más usados en las competencias de ajedrez en México. Muchas veces nos quejamos de que en unocero.com no hablamos de desarrollos hechos en México. Éste es un ejemplo de lo que puede hacerse en este país y competir sin menoscabo contra productos parecidos de Estados Unidos o Alemania.
Referencias:
ChessBoutique
Sunday, July 21, 2013
¿Quiere ser mejor programador? haga un blog
Pero el punto es que tener un blog puede ser más que útil, sobre todo si queremos que los demás sepan de nuestras actividades. Por ejemplo, yo dedico muchas horas a la programación de computadoras. Entiendo las dificultades de este arte/ciencia y en ocasiones pueden pasar semanas hasta que sale "del horno" algún programa que me parece puede cumplir y satisfacer las expectativas de un tercero. Mi regla es simple: "si lo programo es que lo voy a usar". Si escribo código para después usar un producto comercial o que puede hallarse en la red, ¿para qué lo programo? Es perder el tiempo.
Pero programar no es solamente crear código, es entender cómo hacer para que la computadora haga lo que queremos que haga y además, podamos compartir con otros nuestros hallazgos, nuestro código incluso. Donald Knuth dice que compartir el código que uno produce es una manera de colaborar con la comunidad en donde vivimos. Y es por ello tal vez que la filosofía de código abierto es la mejor. Nadie es una isla y todos nos ayudamos de conocimientos de otros, de ideas de terceros, etcétera. ¿Por qué entonces no compartirlas?
Pero además de esto, un blog nos permite a los programadores:
- Compartir conocimiento. Esto nos favorece a todos. Hoy en día no podemos crear todo el código por nosotros mismos. Nos ayudamos de bibliotecas que terceros han escrito. Algunas tienen costo y otras no, pero el punto es finalmente que como están las cosas en programación hoy en día, resulta absurdo pretender escribir código propio 100%. No tiene sentido y llevaría demasiado tiempo. Cuando tenemos una dificultad en programación podemos consultar en Google y quizás salga alguna referencia a algún blog que ya haya tratado con el problema que tenemos. De hecho, yo he recibido correos electrónicos de gente interesada en mi corrector ortográfico inteligente, por ejemplo, el cual les ha llamado la atención a algunas personas que están haciendo tesis a nivel licenciatura.
- Sirve además para autopromoverse. Un empleador potencial puede ver las habilidades de uno casi "en vivo", y no solamente como referencias en un curriculum vitae.
- Explicar a otros lo que uno hace es una forma de aprender. ¿Quieres aprender algún tema? Enséñalo.
- Se puede -a veces- hacer dinero con un blog. Si a alguien le interesa esta opción, entre a este sitio, por ejemplo.
Pero aparte de todas estas razones, un blog nos puede servir para documentar nuestros programas. Cualquier programador podrá decirles que cuando el software está terminado en su primera versión, no hay manual de usuario, documentación técnica, nada. Es una maraña de ideas en la cabeza del programador, las cuales deben ser ordenadas. Un blog es interesante en este sentido porque nos permite ir armando las ideas de forma que quienes nos leen las comprendan.
Así entonces, si quiere programar mejor, considere crear un blog. Desde luego no siempre tienen que ser artículos sobre programación. Muchas veces los autores de los blogs nos hablan de lo que hacen, de sus problemas incluso personales. Uno, en un blog, decide qué poner. Si los contenidos se convierten en interesantes, habrá lectores. Aproveche las redes sociales para ir anunciando cada vez que pone un nuevo artículo. Esto hará que lleguen más lectores y eventualmente algunos se engancharán.
Una advertencia: un blog es como un hijo. Hay que procurarlo cada cierto tiempo. Un blog que no se actualiza -digamos semana a semana- deja de tener interés para muchos y se pierden lectores.
Monday, March 12, 2012
Los efectos del software libre
Quienes usamos y compartimos la idea del software libre y abierto, nos topamos con quienes simplemente no entienden cómo es que uno "regala" su trabajo. La realidad es que es una cuestión de creer en que la idea es correcta y "regalar" el trabajo (asunto que estrictamente pudiese ser cierto), no es tan lamentable ni grave como a más de uno le pudiese parecer. De hecho, Donald Knuth, una de las vacas sagradas del cómputo ha dicho ya en alguna ocasión que todos estamos obligados a hacer algo por nuestras comunidades, por el lugar donde vivimos. Los programadores pueden donar su código y eso es una manera de agradecer los beneficios recibidos en nuestras existencias. Cada quien, desde su reducto, está obligado a hacer de este mundo algo mejor.
Knuth es el primero en aplicar esta idea y su sistema de tipografía TeX, es libre y gratuito. Es de código abierto y hoy por hoy es el estándar en sistemas de tipografía para libros de matemáticas. TeX tiene ya sus años y se ha desarrollado extraordinariamente con una serie de herramientas para quienes tienen que formar libros científicos. Así pues, aparte de la obra maestra de Knuth (The Art of Computer Programming - una serie de libros sobre cómputo), tenemos a TeX, entre tantas cosas que ha hecho este personaje por el cómputo mundial.
En el software libre, abierto, en donde en general incluso se entrega el código fuente, tenemos la posibilidad de aprender de lo que otros ya han hecho. Esto me hace pensar en el ajedrez: gracias a quienes escriben de las partidas de otros, que las analizan, que hacen colecciones de posiciones de táctica, de ejercicios para mejorar en nuestra comprensión ajedrecística, entonces aprendemos. No tiene sentido pretender inventar el hilo negro, aprender todo desde cero, sin ayuda. No nos alcanzaría una vida en ese sentido. Hay que sacar ventaja de que otros ya han hecho el trabajo y se han tomado la molestia de explicarnos muchas cosas que nos permiten avanzar más rápidamente.
Y todo esto viene a cuento porque leo a Frederic Friedel, de Chessbase, que en una entrevista dice que los programas de ajedrez de código abierto han arruinado el negocio del ajedrez computarizado. El problema es que hay una serie de motores de ajedrez, "engines", los cuales juegan tan bien como que Fritz, Rybka, Shredder, etc. Por ello en la versión 13 de Fritz, Chessbase ha añadido el que se pueda interactuar con análisis de otros a través de la "nube", es decir, a través de análisis realizados por otros y guardado en los servidores públicos de Chessbase. Para ello, cabe decirlo, se necesita comprar Fritz 13 para tener acceso a esta opción.
El problema es que Robbolito, Houdini y StockFish, son programas que juegan ya tan bien como Rybka (que de acuerdo a Kasparov a finales del 2010, decía, era el programa que mejor entendía de ajedrez). Ahora simplemente se puede descargar cualquiera de los programas mencionados y si comparamos análisis contra los motores comerciales, no veremos prácticamente diferencia. Quizás Friedel tiene razón: vender motores y programas de ajedrez ya no parece ser un buen negocio.
Pero pensemos en Chessbase, el producto estrella de dicha empresa, el manejador de partidas de ajedrez el cual es un estándar. Hay varios productos de la competencia, algunos comerciales y otros de software libre y abierto. Chess Assistant, que es la competencia comercial de Chessbase, hace estrictamente lo mismo que Chessbase (y viceversa), y es cuestión de gustos y de tiempo el preferir uno u otro programa. Sin embargo, tenemos "José" (por José Raúl Capablanca) y Scid (Shane's Chess Information Database), que son programas que en términos generales hacen lo mismo que Chessbase o Chess Assistant, pero sin embargo, no tienen el éxito de los programas comerciales. ¿Por qué?
Puede haber muchas razones, pero quizás la más común es que Chessbase y Chess Assistant también entregan una serie de programas de apoyo para sus manejadores de partidas. Tienen servicios de recolección de partidas que se juegan en los torneos y sus bases de información están actualizadas al día. Los programas públicos, libres y gratuitos tienen eso en contra y no pueden competir con esta parte de los programas comerciales. Otra razón es que sus bases de partidas están en general en un formato optimizado para búsquedas, el cual es propietario, mientras que los programas abiertos usan el formato PGN, que probablemente sea mucho más lento de manipular cuando se tienen unos cinco millones de partidas con todo el ajedrez registrado.
Así pues, he aquí lo que hay que hacer para mantener un negocio de software a flote: hay que dar mucho más que solamente un programa funcional. Por eso Chessbase y Chess Assistant se mantienen en el gusto de los ajedrecistas. Con ese mismo criterio el negocio de los programas que juegan ajedrez debe buscar dar más que sólo un programa que juegue al ajedrez. Como se mencionó antes, Fritz 13 es un primer paso en ese derrotero, pero es claro que hay que apurarse porque los programas abiertos y gratuitos hacen tanto como los comerciales. ¿Por qué pagar por algo que se puede conseguir gratis?
Subscribe to:
Posts (Atom)