Monday, November 17, 2008

Como crear una máquina virtual Windows

La empresa VMWare entrega en forma gratis el VMPlayer , que pemite correr máquinas virtuales en nuestra PC Windows o Linux. Esto es muy interesante, tanto para poder probar Linux desde una máquina Windows como para poder tener instalaciones prearmadas o máquinas donde probar sin miedo software sospechoso.

Originalmente, la idea de VMWare era que con el VMPlayer solo se pudieran ejecutar máquinas virtuales generadas con otros productos (pagos) de la empresa, pero rápidamente alguien le encontró la vuelta y ahora se pueden crear máquinas virtuales usando solo los productos gratuitos.

Hasta ahora yo solo había creado máquinas virtuales Linux, pero quería jugar a un juego viejo y no lo quería instalar en mi máquina, así que me puse a armar una VM Windows y como no encontré la forma de hacerlo escrita en un solo lugar, las voy a escribir acá por si a alguien le sirven.

En primer lugar hay que instalarse el VMPlayer y después bajarse estos archivos que son parte de este artículo sobre instalación de Ubuntu con el VMPlayer.

Además necesitamos una imágen booteable de un CD con un instalador de Windows. Acá están las instrucciones para generar este CD. No es necesario crear el CD físicamente, se necesita solo un archivo .iso con la imagen.

Una vez que tenemos todo esto, copiamos en directorio los archivos OS.vmdk y OS.vmx, junto con la imagen booteable del instalador. Modificamos el archivo OS.vmx (es un archivo de texto que se puede modificar con cualquier editor de texto), reemplazando en la linea

ide1:0.fileName = "c:\image.iso"

el nombre image.iso por el nombre que le hayamos puesto a la imagen booteable. Después de eso, haciendo doble click en el archivo OS.vmx va a arrancar la máquina virtual, booteando desde el CD y empezando el proceso de instalación de Windows. 

Una vez terminado el proceso de instalación, nuestra VM va a estar lista para ser usada. 

 

Labels: , , ,

Monday, September 08, 2008

Calculo mejor equipo gran dt

En otro blog que tengo, estuve mostrando algunos cálculos realizados sobre los resultados del GranDT . En el último post calculé cuales serían los equipos ideales de la primera fecha y recibí un comentario preguntándome si podía publicar el programa con el que los había calculado, así que lo voy a hacer acá, junto con una pequeña explicación.

Antes que nada me disculpo por el programa, no es mucho más que un script Ruby, hecho medio a los apurones, asi que el estilo no es gran cosa y le vendría bien un refactoring.

En el Gran DT uno juega armando un equipo de f'utbol con jugadores reales de la primera división del fútbol argentino. En cada fecha cada uno de esos jugadores recibe un puntaje (basado en la calificación de periodistas, goles convertidos, etc.) y el equipo que suma más puntos gana. Los equipos no pueden formar de cualquier manera sino que tienen que respetar 3 condiciones: tener 1 arquero, 4 defensores, 4 mediocampistas y 2 delanteros, no tener más de 3 jugadores del mismo club real y además que la suma de las cotizaciones de los integrantes ( de acuerdo a una tabla proporcionada por el juego ) no supere los 60 millones.

Lo que queremos hacer es calcular que combinación de jugadores formarían el equipo que hubiera sumado la mayor cantidad de puntos en una fecha dada. La dificultad está en que no basta con buscar los jugadores que más puntos sumaron en cada puesto porque podría pasar que fuera imposible formar un equipo con esos jugadores porque no se cumpliría con las restricciones  de presupuesto o con la de no tener más de 3 jugadores del mismo equipo.

Estos problemas generalmente se atacan con la técnica de backtracking, que consiste básicamente en ir probando todas las combinaciones posibles hasta hallar la mejor, aunque descartando muchas de las posibles combinaciones basándonos en propiedades conocidas del problema. 

Veamos primero que pasa si intentamos probar directamente todas las combinaciones posibles (lo que sería buscar por fuerza bruta). En la primer fecha del torneo que se tuvo en cuenta para el Gran DT jugaron 18 arqueros, 75 defensores, 97 volantes y 43 delanteros. Por lo tanto, dadas las restricciones de posiciones de los jugadores, la cantidad de combinaciones que deberíamos revisar antes de definir cual es el mejor equipo son:

     18 x 75 x 74 x 73 x 72 x 97 x 96 x 95 x 94 x 43 x 42 = 78855686497857024000

Es un número tan grande que aún si pudieramos calcular 100000 equipos por segundo, necesitaríamos más de 2 millones y medio de años para terminar nuestras cuentas.

No tenemos tanto tiempo, así que vamos a tener que buscar alguna manera de achicar la cantidad de combinaciones posibles. En primer lugar, es interesante ver que muchos de los jugadores no tienen ninguna posibilidad de estar en el equipo ideal. Por ejemplo, Gracián, de Boca (lo elegí al azar, no es solo porque yo sea hincha de River). Gracián cuesta 4.5 millones e hizo 4 puntos, mientras que muchos jugadores que cuestan mucho menos que eso sumaron más puntos. El algoritmo general para descartar un jugador es que haya N jugadores que jueguen en su misma posición y que cuesten menos y hayan hecho los mismos puntos o más en la fecha, donde N es 4 para volantes y defensores,  2 para delanteros y 1 para arqueros, por la cantidad de puestos disponibles para cada una de estas posiciones. Descartando los jugadores que caen en estas condiciones tenemos 4 arqueros, 16 defensores, 13 volantes y 7 delanteros, con lo que la cantidad de combinaciones a considerar cae mucho:

  4 x 16 x 15 x 14 x 13 x 13 x 12 x 11 x 10 x 7 x 6 = 125924198400

Si pudiéramos procesar 100000 equipos por segundo, terminaríamos en 15 días. No voy a negar que es más razonable que dos millones y medio de años, pero todavía es muy lento, sobre todo considerando que 100000 equipos por segundo es una velocidad muy poco probable.

Por lo tanto, tenemos que bajar las combinaciones y la manera de hacer eso es no terminar de calcular aquellos equipos que no tengan chances de ser el ideal. Por ejemplo si estamos en la mitad del procesamiento de un equipo y vemos que no es válido (por ejemplo por tener mas de 3 jugadores del mismo club o costar más de 60 millones) ya podemos suspender el procesamiento de ese equipo. El otro criterio que se usa en el programa es el siguiente: supongamos que ya tenemos un equipo candidato a ser el mejor de la fecha, con 110 puntos. Supongamos además que estamos terminando de calcular otro equipo al que solo nos falta agregarle el arquero y que tiene hasta ahora 90 puntos. Si el mejor arquero de la fecha hubiera hecho 10 puntos, no vale la pena seguir con el cálculo, ya que es imposible que este equipo llegue a ser mejor que el candidato. La misma idea se puede extender calculando antes de empezar cuanto sumaría la mejor defensa, mediocampo o delantera.

   

Con todo esto la velocidad mejora muchisimo y el calculo se puede hacer en menos de un minuto (lo cual es toda una mejora contra 2500000 años... .



Si le interesa el código, lo pueden tomar aquí


   

Monday, August 06, 2007

Versionado de tablas con Ruby on Rails


En este post voy a mostrar un ejemplo de como implementar versionado de una tabla con Ruby On Rails.



El problema que estamos atacando es el siguiente: tenemos un ABM sobre una tabla y queremos que cada vez que se realice una modificación sobre algún registro, la versión anterior a la modificada quede guardada y podamos en caso necesario revertir nuestros cambios y volver a esa versión del registro (o a cualquier version anterior).



Para eso vamos a utilizar un plugin de Rails llamado acts_as_versioned



Empezamos creando la aplicacion rails



rails versiones
cd versiones



A continuación instalamos el plugin. Para eso primero actualizamos la lista de plugins disponibles



ruby script/plugin discover ruby



Y después instalamos el plugin que nos interesa



script/plugin install acts_as_versioned



El siguiente paso es la configuración de la base de datos. No vamos a usar nada específico de ningún motor de base de datos, asi que no voy a explicar en detalle como hacerlo. Aquí hay una explicación detallada de instalación y configuración con MySQL



A continuación generamos el modelo para nuestro ejemplo de versionado, que va a ser una tabla de clientes



ruby script/generate model Cliente



Esto nos genera una serie de archivos, entre los cuales esta el archivo de migración que utilizaremos para crear nuestra tabla . Completamos este script de migracion con la estructura de nuestra tabla de clientes:



db/migrate/001_create_clientes.rb



class CreateClientes < ActiveRecord::Migration

    def self.up

     create_table :clientes do t

      t.column :nombre,:string

      t.column :apellido,:string

      t.column :direccion,:string

     end

    end



    def self.down

     drop_table :clientes

    end

end





Ejecutamos la migración



rake db:migrate



Y generamos el controller y las views necesarias para la edicion de nuestra tabla



script/generate controller cliente list edit



Completamos el controller y las views que generamos con el siguiente código, para tener la posibilidad de ver y editar los registros de nuestra tabla (no voy a entrar en explicaciones con esta parte porque no tiene que ver directamente con el tema de versionado y además está abundantemente documentado)



app/controller/cliente_controller.rb



class ClienteController < ApplicationController 

  def list 

   @clientes = Cliente.find(:all)

  end 



  def edit 

   @cliente = Cliente.find(params[:id])

  end 



  def change 

   @cliente = Cliente.find(params[:id])

   if(@cliente.update_attributes(params[:cliente])) then 

    redirect_to(:action => 'list')

   else 

    render(:action => 'edit')

   end 

  end 

end





app/view/cliente/list.rhtml



  <Table>

   <% @clientes.each do cliente %>

    <TR>

     <TD<%= cliente.nombre%> </TD>

     <TD<%= cliente.apellido%> </TD>

     <TD<%= cliente.direccion%> </TD>

     <TD<%= link_to 'Edicion', {:action => 'edit',:id => cliente}%> </TD>

    </TR>

   <%end%>

  </Table>





app/views/cliente/edit.rhtml



  <%= start_form_tag({:action => 'change', :id => @cliente}, :id => "form") %>

   <%= error_messages_for 'cliente' %>



   <p><label for="cliente_nombre">Nombre:</label><br/>

   <%= text_field 'cliente', 'nombre'  %></p>



   <p><label for="cliente_apellido">Apellido:</label><br/>

   <%= text_field 'cliente', 'apellido'  %></p>



    <p><label for="cliente_direccion">Direccion:</label><br/>

    <%= text_field 'cliente', 'direccion'  %></p>



    <%= submit_tag "Salvar Cambios" %>

   <%= end_form_tag %>





Con esto tenemos funcional una aplicación que nos permite listar y editar nuestros clientes. Vamos a generar algunos registros con una migracion para poder probarla:



ruby script/generate migration crea_clientes_prueba






class CreaClientesPrueba < ActiveRecord::Migration

  def self.up

   (1..20).each do |index|

     Cliente.new(:nombre => sprintf("Nombre_%d",index),

                 :apellido => sprintf("Apellido_%d",index),

                 :direccion => sprintf("Direccion_%d",index)).save!

   end

  end



  def self.down

  end

end





rake db:migrate



Levantamos el server:



ruby script/server



y, apuntando un browser a http://localhost:3000/cliente/list , debería aparecer el listado de clientes.



Ahora vamos a modificar este ABM para que maneje versiones. En primer lugar indicaremos que nuestro modelo es versionado.



app/model/cliente.rb



  class Cliente < ActiveRecord::Base

   acts_as_versioned

  end





Generamos la tabla en la que se guardaran las diferentes versiones, nuevamente con una migración.



script/generate migration crea_tabla_versiones



Completamos el contenido del archivo de migracion






  class CreaTablaVersiones < ActiveRecord::Migration

     def self.up

        Cliente.create_versioned_table

     end



     def self.down

        Cliente.drop_versioned_table

     end

  end





Los métodos create_versioned_table y drop_versioned_table se agregan automáticamente al modelo cuando declaramos que este es versionado (cuando decimos acts_as_versioned) que generan y borran la tabla auxiliar necesaria para guardar las versiones de los registros de la tabla principal



Ejecutamos la migración para crear la tabla auxiliar



rake db:migrate



Y ya está, nuestros clientes tienen múltiples versiones. Para ver como funciona esto y algunos ejemplos, vamos a recurrir a la consola de rails (que es un excelente recurso cuando queremos jugar con nuestro modelo sin escribir todavía la interfaz gráfica).



ruby script/console






 #Creamos un cliente y lo salvamos

 >> cliente = Cliente.new(:nombre => "Homero",

                          :apellido => "Simpson",

                          :direccion => "Avenida Siempreviva 742, Springfield")

 >> cliente.save!





 #En que version estamos?

 >> cliente.version

 => 1



 #Ahora cambiamos la direccion y salvamos los cambios:

 >> cliente.direccion = "430 Camino Spalding"

 => "430 Camino Spalding"

 >> cliente.save!

 => true



 #En que version estamos?

 >> cliente.version

 => 2



 #Accedemos a los datos de las distintas versiones

 >> cliente.versions[0].direccion

 => "Avenida Siempreviva 742, Springfield" 

 >> cliente.versions[1].direccion

 => "430 Camino Spalding"

 #Y deshacemos los cambios para volver a la versión anterior

 >> cliente.direccion

 => "430 Camino Spalding"

 >> cliente.revert_to(1)

 => true 

 >> cliente.direccion

 => "Avenida Siempreviva 742, Springfield" 



 >> cliente.save!

 => true



 #Es interesante ver que el hecho de volver a la version anterior no eliminó la versión intermedia

 >> cliente.version

 => 3

 >> cliente.versions[0].direccion

 => "Avenida Siempreviva 742, Springfield"

 >> cliente.versions[1].direccion

 => "430 Camino Spalding"

 >> cliente.versions[2].direccion

 => "Avenida Siempreviva 742, Springfield"





Ahora que ya probamos esto desde la consola (y no crean que las direcciones son al azar) ,vamos a agregar la información a la interfaz gráfica. En primer lugar vamos a agregar al listado de clientes el numero de version y una opcion que nos permita volver a una version anterior.






  <Table>

   <% @clientes.each do cliente %>

    <TR>

     <TD<%= cliente.nombre%> </TD>

     <TD<%= cliente.apellido%> </TD>

     <TD<%= cliente.direccion%> </TD>

     <TD<%= cliente.version%> </TD>

     <TD<%= link_to 'Versiones', {:action => 'versiones',:id => cliente}%> </TD>

     <TD<%= link_to 'Edicion', {:action => 'edit',:id => cliente}%> </TD>

    </TR>

   <%end%>

  </Table>





Agregamos en el controller el codigo para obtener las versiones anteriores:






 def versiones

  @cliente = Cliente.find(params[:id])

  @versiones_cliente = @cliente.versions

 end





Y creamos una view versiones.rhtml que nos permita ver las versiones anteriores:






 <Table>

  <% @versiones_cliente.each_with_index do version,index %>

   <TR>

    <TD<%= version.nombre%> </TD>

    <TD<%= version.apellido%> </TD>

    <TD<%= version.direccion%> </TD>

    <TD<%= link_to 'Volver a', {:action => 'volver_a_version',:id =>@cliente,:version => index+1}%> </TD>

   </TR>

  <%end%>

 </Table>





Con estos agregados podemos ver facilmente las versiones anteriores de un cliente.



Ahora nos falta implementar la vuelta a una versión anterior. Como se puede ver, en el listado de versiones ya incluimos un link que nos permite volver a cada version. Solo nos falta incluir en el controller la acción correspondiente a esa vuelta atrás:






   def volver_a_version

       @cliente = Cliente.find(params[:id])

       @version = params[:version]

       @cliente.revert_to(@version.to_i)

       @cliente.save!

       redirect_to(:action => 'list')

    end



Monday, March 12, 2007

Configuración de teclado latinoamericano en CygWin

Hace poco compré una laptop. Como con toda máquina Windows nueva que entra a mi casa, lo primero que hice fue instalarle Cygwin y Vim, como para sentirme más a gusto. No tengo nada particular contra Windows, pero tanto su shell como su editor de texto por default son inapropiados para el uso profesional.

El Vim anduvo perfectamente pero cuando intenté usar el Cygwin como cliente X para acceder a un servidor Linux, me encontré con muchos problemas para lograr que respetará la configuración del teclado (español con distribución latinoamericana). Estuve googleando bastante en busca de una manera de conseguir que las teclas funcionen correctamente sin hallar una respuesta directa. Finalmente me puse a revisar los archivos de configuración del teclado y encontré una solución que posteo a continuación por si le es útil a algun otro usuario de Cygwin con este tipo de teclados.

El problema es el siguiente: los archivos de configuración de teclado para X se almacenan en /etc/X11/xkb/symbols/pc . Los nombres de los archivos identifican a que distribución de teclado corresponden (es = español, en = english, etc.). El problema es que el archivo que teoricamente le correspondería a la distribución latinoamericana es el "la", que en la instalación por defecto de Cygwin es la distribución de teclado para Laos (!!).

La solución es reemplazar el archivo "la" que está en /etc/X11/xkb/symbols/pc por el que está en /etc/X11/xkb/ (este último si corresponde a la distribución latinoamericana) y agregarle al nuevo archivo las siguientes lineas

include "pc/latin(type4)"
include "level3(ralt_switch)"

Por supuesto, de está manera se pierde la posibilidad de editar archivos en laosiano con sus teclados latinoamericanos, pero bueno, no se puede ganar siempre...

Labels: