Curso gratuito de introducción a Linux

junio 4, 2014

Para quienes no conozcan el sitio www.edx.org, les comento que es una página web en donde pueden encontrar cursos grátis en linea de varias de las mejores universidades del mundo como el MIT, Harvard, etc. En esta ocasión, les comento que hay un curso de introducción a Linux que organiza la Linux Foundation, empieza en Agosto de 2014 y sólo tienen que crear una cuenta en http://www.edx.org e inscribirse en el curso.

Aquí está el enlace directo a la información del curso

https://www.edx.org/course/linuxfoundationx/linuxfoundationx-lfs101x-introduction-1621#.U48nUSg0zJZ

Ya hay mas de 140,000 personas inscritas al curso

Eventos de Software Libre para Abril y Mayo de 2014

marzo 31, 2014

Si eres entusiasta del Software Libre, sabrás que el 26 de Abril se llevará acabo el FLISoL (Festival Latinoamericano de Instalación de Sofware Libre). Para quienes nunca hayan asistido, les comento que es un evento para difusión del Software Libre y es totalmente gratuito.

Este año la sede del FLISoL en la ciudad de Puebla es la UVP (Universidad del Valle de Puebla), yo estaré participando como ponente con la conferencia “RIA con Sinatra y Knockout”.

Los días 8 y 9 de Mayo se realizará el VIII Simposium de Software Libre de la Mixteca en la UTM (Universidad Tecnológica de la Mixteca), que se encuentra en la ciudad de Huajuapan de León, Oaxaca. En este simposio estaré participando como ponente con la conferencia “Desarrollo Web con Ruby sin Rails”.

Es la primera ocasión en que voy a asistir a este simposium de la UTM pero se ve que es un evento de calidad. Quienes deseen más información al respecto pueden escribir a simposio@mixteco.utm.mx

 

Presentaciones comerciales de tecnología: Mentiras incluidas

marzo 14, 2014

Las personas que están en contacto con fabricantes, mayoristas o distribuidores de tecnología (hardware y software) seguramente han asistido alguna vez a alguna presentación comercial de alguna de estas empresas.

Normalmente son para promover la venta de algún producto o servicio y es común que hablen maravillas de sus productos y servicios y pestes de los de la competencia, a veces sin mencionar directamente el nombre del competidor pero asegurandose de que todos los asistentes “adivinen” a quien se refieren. Esto no implica mayor problema y muchos de los asistentes saben que algo de la información recibida no es del todo imparcial y alguna otra es exagerada.

El problema viene cuando se pasan estos límites y lo que se empieza a difundir descaradamente son mentiras, no importa si dichas mentiras se dicen por ignorancia o a sabiendas de que son mentiras. En mi opinión, cualquiera de los dos casos demuestra poco profesionalismo (está mal si los representantes comerciales de la marca son ignorantes y también está mal si no son éticos y difunden mentiras con tal de influenciar en la opinión de los asistentes a favor de su marca, es decir, la filosofía de “se vale de todo para vender”).

Hoy es Viernes, el Martes fui a una presentación comercial de Intel (tengo que decirlo porque no estoy de acuerdo con que se de información incorrecta y se manipule a la gente). La presentación era para hablar de la cuarta generación de los procesadores de Intel, de su sistema NUC (Next Unit of Computing) y sobre todo para promocionar la línea de tablets con procesador Intel Atom.

La persona que hizo la presentación dijo por lo menos un par de cosas que me dejaron impactado. Aquí les van:

1. El primer navegador web fue Internet Explorer

2. HTML5 está basado en Internet Explorer

Ambas aseveraciones son mentira, no podía quedarme callado y sobre todo cuando vi que todos los asistentes parecían estar tomando esto como una verdad absoluta. Quiero pensar que no dijo estas dos grandes mentiras intencionalmente, sino porque esa información le dijeron que diera cuando lo “capacitaron”. Le aclaré que eso que decía no era cierto; respecto al punto uno, le hablé un poquito acerca de Mosaic, Netscape, etc. y sobre el punto dos le dije que hay algo que se llama World Wide Web Consortium (W3C) que es el organismo que dicta los estándares de HTML, CSS, etc. y que no se basan en un navegador en específico. Hay navegadores que respetan los estándares y otros que no, y precisamente Internet Explorer es uno de los que históricamente se ha destacado por no respetar los estándares, hasta la versión 9 empezaron a tratar de respetar el estándar.

Finalmente le dije que si continuamos repitiendo mitos y difundiendo mentiras, estás nunca se van a acabar y habrá personas que crean que son verdades.

Yo espero que cualquier persona involucrada en el área de tecnologías de la información sepa que mucho antes que existiera Internet Explorer existió Netscape y otros navegadores antes que el. Y si son demasiado jóvenes y no les tocó vivirlo (yo lo viví, por eso lo recuerdo), lean este artículo http://arstechnica.com/business/2011/10/before-netscape-forgotten-web-browsers-of-the-early-1990s/ o vean un documental de Discovery Channel llamado la guerra de los navegadores http://www.youtube.com/watch?v=n1TcL4qItVo

Otra cosa que noté es que insistía en que las tablets con Windows son mejores que las tablets con Android y no daba argumentos que así lo demostraran.

Así que cuidado con lo que escuchan en las presentaciones comerciales (de quien sea), y si van a una presentación comercial de Intel y escuchan estas mentiras, no se queden callados, sean contundentes en hacer notar que eso es mentira y divulguen la verdad, basta de que quieran manipularnos.

Linux en todas partes

febrero 12, 2014

He estado ocupado desarrollando un sistema Cliente/Servidor para control de inventarios, ventas, etc. con Sinatra y Knockout JS, por lo que no he tenido tiempo de escribir nuevos artículos.

Como ya es Febrero y no quiero que llegue marzo sin haber escrito el primer artículo del año en el blog, aquí les dejo un enlace a un artículo interesante publicado en Network World http://www.networkworld.com/slideshow/139614/16-weirdest-places-youll-find-linux.html, esto me recuerda cuando hicieron que una tostadora de pan funcionara con NetBSD http://www.embeddedarm.com/software/arm-netbsd-toaster.php, todo porque alguien dijo algo asi como “NetBSD funciona en cualquier máquina, talvez excepto en una tostadora de pan”

Espero les guste

Migrar base de datos Firebird de 32 bits a 64 bits

diciembre 10, 2013

Si tenemos una base de datos Firebird funcionando en un sistema de 32 bits y queremos utilizar esa base de datos en un sistema con la versión de Firebird de 64 bits simplemente copiando el archivo .fdb de un sistema a otro, eso no va a funcionar.

Al intentar accesar a la base de datos obtendremos un mensaje de error diciendo que el archivo .fdb seleccionado no es una base de datos válida.

Lo que demos hacer es un respaldo de la base de datos en el sistema de 32 bits con la opción -t (transportable)

gbak -b -v -t -user sysdba -password masterkey basededatos.fdb respaldo.fbk

Ya que tenemos nuestro respaldo, hacemos el restore en el sistema de 64 bits

gbak -r -v  -user sysdba -password masterkey respaldo.fbk basededatos.fdb

Ahora debemos de poder accesar a nuestra base de datos con nuestra versión de 64 bits de Firebird

Se solicita millonario altruista e inteligente

septiembre 4, 2013

Si es Ud. un empresario con muchos recursos económicos, consciente de que la fortuna que posee le permitirá vivir más que dignamente a Ud. sus hijos y sus nietos el resto de sus vidas, y con todo el dinero restante quiere ayudar a mejorar el nivel educativo de los jovenes universitarios en México, esta es su oportunidad de hacerlo.

El proyecto consiste en crear una Universidad pionera en su tipo. En un post anterior (http://salomonrt.wordpress.com/2010/01/23/el-problema-de-la-educacion-superior-en-mexico) expliqué la problemática que he visto (y sigo viendo) en las universidades tanto públicas como privadas de México.

Las universidades están preocupadísimas por un indicador llamado “eficiencia terminal”, este indicador se obtiene dividiendo el número de egresados de una generación entre el número de alumnos que se inscribieron al iniciarse dicha generación (para una explicación más detallada puede consultar http://www.scielo.org.mx/scielo.php?script=sci_arttext&pid=S0185-27602008000200009). La Dirección General de Planeación, Programación y Presupuesto de la Secretaría de Educación Pública (DGPPP/SEP) afirma que este indicador es sin lugar a dudas la manifestación de la eficiencia del sistema educativo. Debido a esta creencia, la DGPPP/SEP le da una gran importancia al  indicador de eficiencia terminal de una institución educativa al momento de determinar el presupuesto que le asignará a la misma. La “solución” que han encontrado las universidades para obtener un buen presupuesto consiste en 2 puntos:

  1. Captar al mayor número de alumnos posibles (el alumno x se quiere inscribir para estudiar una ingeniera, no sabe ni hacer una división con lápiz y papel, pero no importa tu inscribelo)
  2. Titular al mayor número de alumnos a como de lugar (el alumno no sabe ni redactar un documento, tiene un gran número de faltas de ortografia y claro que no tiene ni el 50% de los conocimientos que debe tener un profesionista de esa área para ejercer la profesión, pero no lo podemos reprobar, porque si reprobamos a todos los que estén en ese caso, que son la gran mayoría, entónces afecta nuestro indice de eficiencia terminal y por lo tanto nos van a dar menor presupuesto)

Las universidades privadas no quieren reprobar alumnos porque son los que las mantenien, viven de las colegiaturas, entre menos alumnos, menos colegiaturas, y las universidades públicas no quieren reprobar alumnos porque viven del presupuesto, entre menos alumnos, menos presupuesto. Al parecer entónces la solución es crear una Universidad que no se preocupe por el dinero sino por el nivel académico, para esto se requiere el millonario altruista e inteligente del que hablamos en el encabezado de este post.

La idea es crear una Universidad aplicando conceptos que probablemente son opuestos a los que se han aplicado para crear las universidades que actualmente existen en nuestro país, pero Einstein dijo: “No esperes resultados distintos si sigues haciendo lo mismo”.

El proyecto

La Universidad estará especializada en sistemas computacionales únicamente (al sistema operativo Unix se le atribuye el concepto de crear programas o funciones que hicieran sólo una cosa, pero que la hicieran extraordinariamente bien), sólo ofrecerá una carrera que será la de Ingeniero en Sistemas Computacionales, de ser posible, posteriormente se abrirán maestrías y doctorados en áreas relacionadas a la computación.

Se proponen los siguientes puntos:

  • El campus debe contar con la infraestructura y el equipamiento necesario, así como espacios abiertos. Todos los salones serán tipo auditorio, con capacidad para un máximo de 20 alumnos, contarán con proyector, y sistema de sonido, aire acondicionado, etc.
  • Habrá internet de banda ancha en todo el campus, la red estará dividida en vlans (una para estudiantes y público en general, otra para administrativos y otra para docentes).
  • Se contará con un sistema de video-vigilancia para seguridad de todo aquel que se encuentre dentro del campus. No habrá cámaras dentro de ninguno de los salones, sino en el exterior del campus, en el estacionamiento y dentro del campus sólo en puntos estratégicos.
  • Habrá cajones de estacionamiento para alumnos, así como para personal administrativo y docentes.
  • Se debe contar con un auditorio debidamente equipado, además de una sala de juntas
  • Los docentes deberán estar títulados y contar mínimo con 3 años de experiencia en la industria (no importa si tienes doctorado o postdoctorado, si nunca has trabajado más que impartiendo clases o haciendo investigación, no vas a dar clases aquí)
  • Además de las prestaciones de ley, todo el personal que labore en la institución recibirá un sueldo por lo menos 10% superior al mejor sueldo que se esté pagando en el Estado por el mismo puesto; a excepción de los docentes (ya sea hora-clase o de tiempo completo), para ellos el sueldo será por lo menos 25% arriba.
  • Los alumnos no pagarán colegiatura alguna
  • Cualquier egresado de preparatoria que desee ingresar en la Universidad tendrá que cursar un curso propedéutico de un mes antes de iniciar las clases y sólo aquellos que aprueben dicho curso podrán inscribirse.
  • Aqui no se impondrán cuotas que indiquen un mínimo de aprobados por materia, ni preocupación por el indice de eficiencia terminal, sólo los alumnos que tengan los conocimientos mínimos necesarios para aprobar una materia lo harán, los otros tendrán derecho a recursarla el próximo semestre. Sólo se puede recursar una vez la misma materia.
  • La calificación mínima aprobatoria es 7.
  • La Ingeniería en Sistemas Computacionales se cursará en un periodo de 10 semestres.
  • Se privilegiará la enseñanza y uso de Software Libre, aunque habrá algunos laboratorios que cuenten con software privativo instalado para conocimiento y experiencia de los alumnos
  • Se apoyará económicamente a los docentes para que participen como ponentes en congresos nacionales e internacionales, así como para la publicación de artículos.
  • Se pretende crear un “Laboratorio de Software Libre” que sriva para la difusión del Software Libre en la región y a nivel nacional así como para la realización de proyectos de investigación; para lograrlo se buscará el apoyo del gobierno municipal, estatal y porque no, hasta federal
  • De ser posible, se ofrecerán cursos, diplomados, maestrías y doctorados en linea (esos si tendrán un costo para los alumnos que deseen tomarlos)
  • La Universidad impulsará proyectos de inclusión digital en zonas marginadas mediante el uso de Software Libre buscando la participación del gobierno y la iniciativa privada
  • Debido a que será una sociedad civil sin animo de lucro, se buscará recaudar fondos mediante donaciones, etc.

Si Ud. que está leyendo esto cree que hay algo más que se debería incluir, envie sus sugerencias.

El millonario altruista e inteligente obtendrá como ganancia la satisfacción de haber hecho algo bueno por México, será reconocido como un visionario, innovador, punta de lanza del movimiento que logró elevar el nivel académico de la educación superior, etc. Le aseguro que el proyecto generará utilidades en el corto plazo (no económicas, sino más importantes y que perdurarán por más tiempo); Además de que con el paso del tiempo, muy probablemente la Universidad empiece a generar utilidades económicas también.

¿Es Ud. el millonario altruista e inteligente que México necesita?, animese!

Menú en la terminal de GNU/Linux con C++ y códigos de escape

enero 4, 2013

El hacer un programa en modo texto para GNU/Linux en C++ que muestre un menú con una barra que se controle con las flechas del teclado implica más trabajo del que se requiere para lograr lo mismo si nuestro programa fuera a ejecutarse en un sistema operativo basado en DOS (ya sea MS-DOS, Windows, etc.)

A continuación muestro una posible solución de como lograrlo sin utilizar ncurses, únicamente códigos de escape, esto tiene la pequeña desventaja de que no funciona para cualquier terminal, pero para xterm y algunas otras no debe haber problema

Crearemos una fución que espere a que el usuario pulse una tecla y regrese el valor correspondiente a esa tecla, la función debe tomar en cuenta las teclas que al ser pulsadas regresan tres valores, siendo el primer valor el que corresponde a la tecla ESC (que es el 27)

int lee_caracter()
{
 struct termios config_ant;
 struct termios config_nueva;
 int caracter;

 tcgetattr(STDIN_FILENO, &config_ant);
 config_nueva = config_ant;                /* Guarda los valores actuales de configuración de la terminal */
 config_nueva.c_lflag &= ~(ICANON | ECHO); /* resetea las banderas canonical y echo para que no se muestre en pantalla lo que se teclea*/
 tcsetattr(STDIN_FILENO, TCSANOW, &config_nueva); /* Establece los nuevos valores para la terminal */
 caracter = getchar();

 // si se pulsa una tecla que empieza con un código de escape
 if (caracter==27)
    {
     caracter=getchar();
     if (caracter==91)
         caracter=getchar(); //caracter ahora tiene el valor de la tecla pulsada
    }

 tcsetattr(STDIN_FILENO, TCSANOW, &config_ant); /* Vuelve a poner los valores originales de configuración de la terminal */

 return caracter;
}

Los atributos y colores que podemos manejar son los siguientes:

#define RESET        0
#define BRIGHT       1
#define DIM          2
#define UNDERLINE    3
#define BLINK        4
#define REVERSE      7
#define HIDDEN       8
#define BLACK        0
#define RED          1
#define GREEN        2
#define YELLOW       3
#define BLUE         4
#define MAGENTA      5
#define CYAN         6
#define WHITE        7

Necesitamos también una función que nos permita establecer el atributo, color de la fuente y color de fondo que vamos a utilizar antes de escribir un texto

void ColorTexto(int atributo, int texto, int fondo)
{
 std::string color = "\033["+toString(atributo)+";"+toString(texto+30)+";"+toString(fondo+40)+"m";
 cout<<color;
}

Este programa utiliza las dos funciones descritas anteriores para mostrar un menú vertical, se asume una terminal de 80 columnas

/*
Debido a que este programa usa el archivo de cabecera termios.h,
sólo funciona en sistemas operativos que cumplan con el estándar POSIX
Es decir, cualquier distribución de GNU/Linux o cualquier Unix.
*/

#include <iostream>
#include <sstream>    /* Para poder usar istringstream */
#include <string>
#include <termios.h>  /* Para poder usar las estructuras termios */
#include <stdio.h>    /* Para poder usar getchar() */
#include <iomanip>    /* Para poder usar setw */

#define RESET        0
#define BRIGHT       1
#define DIM          2
#define UNDERLINE    3
#define BLINK        4
#define REVERSE      7
#define HIDDEN       8

#define BLACK        0
#define RED          1
#define GREEN        2
#define YELLOW       3
#define BLUE         4
#define MAGENTA      5
#define CYAN         6
#define WHITE        7

const std::string default_console = "\033[0m";

using std::cout;
using std::endl;
using std::setw;

template <typename T>
static std::string toString (T numero)
{
 std::ostringstream ss;
 ss << numero;
 return ss.str();
}

void ColorTexto(int atributo, int texto, int fondo);
int lee_caracter();
void ImprimeMenu(std::string menu[], int tamanio, int op);

int main()
{
 std::string str_menu[4] = {"1. Registrar", "2. Consultar", "3. Modificar", "4. Salir    "};
 int tecla, opcion=0, seleccion=1;

do
 {
  ImprimeMenu(str_menu,4,seleccion);
  tecla = lee_caracter();

  switch (tecla)
         {
          case 10:
                  // Enter
                  opcion = seleccion;
                  break;
          case 65:
                  // Flecha arriba
                  seleccion = (seleccion==1 ? 4 : seleccion-1);
                  opcion = 0;
                  break;
          case 66:
                  // Flecha abajo
                  seleccion = (seleccion==4 ? 1 : seleccion+1);
                  opcion = 0;
                  break;
          default:
                  opcion = tecla-48;
                  if (opcion>=1 && opcion<=4)
                     {
                      seleccion = opcion;
                      ImprimeMenu(str_menu,4,seleccion);
                     }
         }
  if (opcion>=1 && opcion<=4)
     {
      cout<<"Opcion "<<opcion<<endl;
      lee_caracter();
     }
  }
 while (opcion!=4);

 return 0;
}

void ColorTexto(int atributo, int texto, int fondo)
{
 std::string color = "\033["+toString(atributo)+";"+toString(texto+30)+";"+toString(fondo+40)+"m";
 cout<<color;
}

int lee_caracter()
{
 struct termios config_ant;
 struct termios config_nueva;
 int caracter;

 tcgetattr(STDIN_FILENO, &config_ant);
 config_nueva = config_ant;                /* Guarda los valores actuales de configuración de la terminal */
 config_nueva.c_lflag &= ~(ICANON | ECHO); /* resetea las banderas canonical y echo para que no se muestre en pantalla lo que se teclea*/
 tcsetattr(STDIN_FILENO, TCSANOW, &config_nueva); /* Establece los nuevos valores para la terminal */
 caracter = getchar();

 // si se pulsa una tecla que empieza con un código de escape
 if (caracter==27)
    {
     caracter=getchar();
     if (caracter==91)
         caracter=getchar(); //caracter ahora tiene el valor de la tecla pulsada
    }

 tcsetattr(STDIN_FILENO, TCSANOW, &config_ant); /* Vuelve a poner los valores originales de configuración de la terminal */

 return caracter;
}

void ImprimeMenu(std::string menu[], int tamanio, int op)
{
 int x=0, pos;
 unsigned int longitud=0;

 // Obtiene la longitud de la opción del menú con más caracteres
 for (x=0; x<tamanio; ++x)
     {
      if (menu[x].length() > longitud)
          longitud = menu[x].length();
     }
 pos = (80 - longitud) / 2;

 // Limpia la pantalla
 cout << "\033[2J\033[1;1H";

 for (x=0; x<tamanio; ++x)
     {
      // Deja el número de espacios adecuado para que el menú quede centrado (se asume una pantalla de 80 caracteres)
      cout<<default_console;
      cout<<setw(pos)<<" ";

      if (x+1==op)
          ColorTexto(BRIGHT,YELLOW,MAGENTA);
      else
          ColorTexto(BRIGHT,WHITE,BLUE);
      cout<<menu[x]<<endl;
     }
 cout<<default_console<<endl;
}

menu

Ahora la versión para mostrar el menú en forma horizontal

/*
 Debido a que este programa usa el archivo de cabecera termios.h,
 sólo funciona en sistemas operativos que cumplan con el estándar POSIX
 Es decir, cualquier distribución de GNU/Linux o cualquier Unix.
*/

#include <iostream>
#include <sstream>    /* Para poder usar istringstream */
#include <string>
#include <termios.h>  /* Para poder usar las estructuras termios */
#include <stdio.h>    /* Para poder usar getchar() */
#include <iomanip>    /* Para poder usar setw */

#define RESET        0
#define BRIGHT       1
#define DIM          2
#define UNDERLINE    3
#define BLINK        4
#define REVERSE      7
#define HIDDEN       8

#define BLACK        0
#define RED          1
#define GREEN        2
#define YELLOW       3
#define BLUE         4
#define MAGENTA      5
#define CYAN         6
#define WHITE        7

const std::string default_console = "\033[0m";

using std::cout;
using std::endl;
using std::setw;

template <typename T>
static std::string toString (T numero)
{
 std::ostringstream ss;
 ss << numero;
 return ss.str();
}

void ColorTexto(int atributo, int texto, int fondo);
int lee_caracter();
void ImprimeMenu(std::string menu[], int tamanio, int op);

int main()
{
 std::string str_menu[4] = {"1. Registrar", "2. Consultar", "3. Modificar", "4. Salir"};
 int tecla, opcion=0, seleccion=1;

 do
   {
    ImprimeMenu(str_menu,4,seleccion);
    tecla = lee_caracter();

    switch (tecla)
           {
            case 10:
                    // Enter
                    opcion = seleccion;
                    break;
            case 67:
                    // Flecha derecha
                    seleccion = (seleccion==4 ? 1 : seleccion+1);
                    opcion = 0;
                    break;
            case 68:
                    // Flecha izquierda
                    seleccion = (seleccion==1 ? 4 : seleccion-1);
                    opcion = 0;
                    break;
            default:
                    opcion = tecla-48;
                    if (opcion>=1 && opcion<=4)
                       {
                        seleccion = opcion;
                        ImprimeMenu(str_menu,4,seleccion);
                       }
           }
    if (opcion>=1 && opcion<=4)
       {
        cout<<"Opcion "<<opcion<<endl;
        lee_caracter();
       }
   }
 while (opcion!=4);

 return 0;
}

void ColorTexto(int atributo, int texto, int fondo)
{
 std::string color = "\033["+toString(atributo)+";"+toString(texto+30)+";"+toString(fondo+40)+"m";
 cout<<color;
}

int lee_caracter()
{
 struct termios config_ant;
 struct termios config_nueva;
 int caracter;

 tcgetattr(STDIN_FILENO, &config_ant);
 config_nueva = config_ant;                /* Guarda los valores actuales de configuración de la terminal */
 config_nueva.c_lflag &= ~(ICANON | ECHO); /* resetea las banderas canonical y echo para que no se muestre en pantalla lo que se teclea*/
 tcsetattr(STDIN_FILENO, TCSANOW, &config_nueva); /* Establece los nuevos valores para la terminal */
 caracter = getchar();

 // si se pulsa una tecla que empieza con un código de escape
 if (caracter==27)
    {
     caracter=getchar();
     if (caracter==91)
         caracter=getchar(); //caracter ahora tiene el valor de la tecla pulsada
    }

 tcsetattr(STDIN_FILENO, TCSANOW, &config_ant); /* Vuelve a poner los valores originales de configuración de la terminal */

 return caracter;
}

void ImprimeMenu(std::string menu[], int tamanio, int op)
{
 int x=0, pos;
 unsigned int longitud=0;

 // Obtiene la longitud total del menú dejando dos espacios entre cada opción
 for (x=0; x<tamanio; ++x)
     {
      longitud += menu[x].length();
      if (x!=tamanio-1)
          longitud +=2;
     }

 pos = (80 - longitud) / 2;

 // Limpia la pantalla
 cout << "\033[2J\033[1;1H";

 // Deja el número de espacios adecuado para que el menú quede centrado (se asume una pantalla de 80 caracteres)
 cout<<setw(pos)<<" ";
 for (x=0; x<tamanio; ++x)
     {
      if (x+1==op)
          ColorTexto(BRIGHT,YELLOW,MAGENTA);
      else
          ColorTexto(BRIGHT,WHITE,BLUE);
      cout<<menu[x];
      if (x!=tamanio-1)
        {
         ColorTexto(BRIGHT,WHITE,BLUE);
         cout<<"  ";
        }
     }
 cout<<default_console<<endl;
}

menuh

Pasar arreglos a una función en C

noviembre 29, 2012

Por default, los arrreglos en C se pasan a una función como referencia y no como valor. Esto significa que todas las modificaciones que hagamos dentro de la función en C al arreglo que recibimos como parámetro, realmente se realizan en el arreglo original que se utilizó como argumento al momento de llamar a la función.

Al escribir una función en C, la forma de indicar que uno de los parámetros que se va a recibir es un arreglo de una dimensión, es decir de que tipo va a ser el arreglo y el nombre con el cual vamos a manipular dicho arreglo dentro de nuestra función seguido de corchetes que abren y cierran; nuestra función también debe recibir un segundo parámetro que nos indique el tamaño del arreglo, o dicho de otra forma, el número de elementos de los que consta nuestro arreglo, recordemos que como el arreglo se pasa a la función como referencia, lo que esta recibiendo la función en realidad es un apuntador al primer elemento del arreglo, pero no sabe en donde termina el arreglo, por eso es necesario que la función también reciba como parámetro el número de elementos del arreglo.

Por ejemplo, el prototipo de una función en C que va a regresar un entero y va a recibir un arreglo de 10 elementos de tipo entero sería algo asi:

int MiFuncion(int mi_arreglo[], int num_elemetos);

Para llamar a la función anterior, se pone como primer argumento el nombre del arreglo (sin corcehetes) y como segundo argumento el número de elementos del arreglo. Supongamos que tenemos un arreglo de 10 elementos de tipo entero llamado numeros y queremos guardar en una variable llamada “x” el valor que nos regresa la función “MiFuncion” al llamarla pasandole como argumentos el arreglo “numeros”, haríamos algo como esto

x = MiFuncion(numeros, 10);

El siguiente programa solicita al usuario que ingrese 10 números de tipo entero y los almacena en un arreglo; después le pide que introduzca un número para que busque su posición dentro del arreglo..

El programa utiliza una función llamada BuscaNumero que recibe como parámetros el arreglo con los 10 números capturados, el número de elementos del arreglo (en este caso 10) y el número del cual se desea saber su posición dentro del arreglo.. La función regresa -1 si el número que se busca no se encuentra en el arreglo y en caso contrario, regresa la primera posición del arreglo que contiene dicho número.

El programa también utiliza una función llamada MuestraArreglo que no regresa valor alguno, sólo recibe como parámetros el arreglo y el número de elementos. Esta función imprime en pantalla los elementos del arreglo separados por un tabulador.

#include <stdio.h>

int BuscaNumero(int valores[], int tamanio, int busca)
{
int i=0, posicion=-1;

do
  {
   if (valores[i]==busca)
       posicion=i;
   else
       ++i;
  }
while(i<tamanio && posicion<0);

return posicion;
}

void MuestraArreglo(int valores[], int tamanio)
{
 int i=0;

 for (i=0; i<tamanio; ++i)
      printf("%d\t",valores[i]);
 printf("\n");
}

int main()
{
 int x=0, numero=0, posicion=0;
 int ar_numeros[10] = {0};

 printf("Introduzca los 10 numeros enteros que se almacenaran en el arreglo\n");
 for (x=0; x<10; ++x)
     {
      printf("Valor para el elemento [%d]: ", x);
      scanf("%d",&ar_numeros[x]);
     }
 printf("\n");

 printf("Introduzca el número que desea buscar en el arreglo\n");
 scanf("%d",&numero);
 printf("\n");
 MuestraArreglo(ar_numeros,10);

 posicion=BuscaNumero(ar_numeros,10,numero);
 if (posicion != -1)
     printf("El número %d está en la posición %d del arreglo\n",numero, posicion);
 else
     printf("El número %d no está en el arreglo\n",numero);

 return 0;
}

Para pasar a una función un arreglo de dos dimensiones, si debemos indicar el tamaño de la segunda dimensión del arreglo

Ejemplo:int MiFuncion(int mi_arreglo[][5], int num_elemetos);

El siguiente programa le pide al usuario que introduzca 9 números y los almacena en un arreglo de dos dimensiones, en este caso una matriz de 3×3; posteriormente utiliza una función llamada ImprimeMatriz para mostrar como quedaron almacenados los números en la matriz. Dicha función recibe como parámetros, la matriz de 3×3 y el tamaño de la primera dimensión (normalmente la primera dimensión son filas y la segunda dimensión columnas).

#include <stdio.h>

void ImprimeMatriz(int m[][3], int filas)
{
 int i=0,j=0;

 for (i=0; i<filas; ++i)
     {
      for (j=0; j<3; ++j)
          {
           printf("%d ",m[i][j]);
          }
      printf("\n");
     }
}

int main()
{
 int x=0,y=0;
 int matriz[3][3] = {{0},{0},{0}};

 printf("Introduzca los valores para la matriz\n");
 for (x=0; x<3; ++x)
     {
      for (y=0; y<3; ++y)
          {
           printf("Valor para el elemento [%d][%d]: ", x, y);
           scanf("%d",&matriz[x][y]);
          }
      printf("\n");
     }

 printf("Matriz\n");
 ImprimeMatriz(matriz, 3);

 return 0;
}

El negocio de las certificaciones

mayo 4, 2012

Las certificaciones en general

Las certificaciones han existido desde hace mucho tiempo (tanto para personas como para empresas). Lo que me parece increible es que la mayoría de las personas tengan una idea equivocada de lo que signfica una certificación. Por ejemplo, la certificación TIF (Tipo de Inspección Federal) es un reconocimiento que la Secretaría de Agricultura, Ganadería y Desarrollo Rural (SAGAR) otorga a las plantas procesadoras de carnes que cumplen con todas las normas y exigencias del Gobierno Mexicano, en cuanto a su tratamiento y manejo de sanidad se refiere. Esto quiere decir que si Ud. compra carne que proviene de una procesadora que obtuvo la certificación TIF, Ud. sólo debe esperar que el consumir dicha carne no le traerá problemas de salud, ya que la procesadora manejó higiénicamente el producto en todo momento; pero nada le garantiza que la carne estará suave y no dura, etc.

Muchas personas creen que si una empresa tiene una certificación ISO 9000, significa que sus productos son de calidad, y la realidad es que no funciona así. La norma ISO lo que certifica es la estandarización de procesos, la clave de ISO es “documenta lo que haces y haz lo que documentaste”. Lo voy a explicar más detalladamente:
El que una empresa esté certificada en ISO 9000 significa que tiene documentados sus procesos y dichos procesos son realizados tal y como están documentados. Supongamos que yo soy una empresa que fabrica copas de vino y estoy certificado en ISO 9000, significa que todas las copas de vino las fabrico siguiendo el mismo procedimiento, pero la certificación ISO en ningún momento revisa la calidad de los materiales que utilizo para fabricar mis copas de vino, ni la calidad de la maquinaria, etc. Así que aunque yo utilice materiales de pésima calidad puedo estar certificado en ISO 9000; si ese es el caso, la certificación sólo estaría garantizando al consumidor que todas las copas de vino que me compre tendrán la misma mala calidad ya que todas fueron elaboradas siguiendo el mismo proceso.

¿Y porqué entónces la mayoría de las personas asocia una certificación con calidad? la cruda realidad es “por la mercadotecnia”, porque eso es lo que las empresas que ofrecen las certificaciones quieren que las personas crean. Además hay que tener cuidado, había un caso de una empresa multinacional (no voy a decir nombres) cuya sucursal mexicana fabrica X productos, tanto para el mercado mexicano como para exportación a otros países; dicha empresa anunciaba orgullosamente “estamos certificados en ISO 9000″, y era cierto, a medias, porque la única parte de la fábrica que estaba certificada era la parte que fabricaba los productos de exportación, no toda la empresa, pero eso se les había “olvidado” especificarlo en su anuncio.

Hablando de certificaciones para personas (sobre todo relacionadas con TI)

En cuanto a las certificaciones para personas sucede lo mismo, empresas con muchos recursos económicos se han dado a la tarea de lanzar certificaciones sobre sus productos y desafortunadamente han logrado convencer a las empresas que les conviene más contratar a alguien certificado que alguien que no tenga dicha certificación. Lo peor es que debido a esto, muchas personas también han caído en la trampa y buscan tener una certificación, aunque sea algo tan ridículo como ser “usuario certificado de una suite ofimática”.

Esto es un gran negocio para las empresas que ofrecen las certificaciones ya que además dichas certificaciones tienen una vigencia, es decir, si hoy tu te certificas en la versión 1 del producto X, tu certificación es válida por 2,3 o 5 años o mientras no salgan n versiones después de la versión en la que tu te certificaste, no importa si entre la versión en la que tu te certificaste y la versión que exista despues de los años estipulados sólo ha cambiado el nombre de los menús (en el caso de software) y los colores de las pantallas, tu certificación ya no es válida y tienes que pagar una “re-certificación” por otros 2,3 o 5 años si quieres tener el “privilegio” de poder decir que estas certificado.

He conocido muchas personas que están certificadas en X o Y cosa cuyos conocimientos sobre el tema sobre el cual están certificados dan pena. Desde luego no estoy diciendo que todo el que está certificado carece de los conocimientos que supuestamente avala su certificación, pero si es el caso de muchos, más de los que debería, y he platicado con personas que han tenido experiencias similares a la mia, así que es bastante común que esto pase.

Otra cuestión es que las certificaciones son muy caras, si Ud. está pensando en certificarse mi recomendación es la siguiente: Si Ud. trabaja en una empresa y dicha empresa es la que va a pagar su certificación, adelante, obtenga su certificación. Si Ud. está pensando certificarse por su cuenta, no tire su dinero, mejor estudie una maestría, seguramente podrá encontrar una buena universidad con un programa de maestría referente al área de conocimiento en la cuál quiere certificarse y probablemente a un menor costo. Definitivamente el nivel de conocimientos que va Ud. a adquirir estudiando una maestría es mucho muy superior a cualquier certificación. Y si ya tiene Ud. una maestría, no importa, hacer otra no le hará daño, y si le interesa estudiar un doctorado, es todavía mucho mejor.

Desde luego, también me he encontrado con personas que tienen estudios de doctorado y carecen de los conocimientos básicos del área de estudio en la que obtuvieron su título de doctor, pero le aseguro que son mucho menos casos que el de las personas certificadas que ignoran cosas básicas de su área de certificación.

Si Ud. ya tiene una certificación que Ud. mismo pagó, no se preocupe, todos cometemos errores, lo importante es aprender de ellos y no volver a cometerlos.

Estoy seguro que a más de uno no le va a gustar este post (muchos de ellos estarán certificados en algo y muchos otros probablemente trabajen para empresas que ofrecen certificaciones de algún tipo), pero este artículo no es para ellos, es principalmente para los jovenes estudiantes que están pensando si deberían certificarse en algo o no debido al bombardeo constante de publicidad que reciben de las empresas.

No es que sea malo obtener una certificación, lo importante es el motivo por el cuál se pretende obtener dicha certificación. y lo que se está dispuesto a pagar por ella. Si el objetivo es asistir al curso de certificación para aprender, está bien, pero depende del grado de conocimientos que Ud. ya tenga, puede que se lleve una gran decepción, si realmente aprende algo, ya cumplió su objetivo, entonces probablemente no quiera pagar por el examen de certificación (porque normalmente le cobran el curso y aparte el examen, o le dicen que el costo del examen ya va incluido, pero si Ud. dice que no quiere tomar el examen y desea que le descuenten ese costo del curso, le dicen que no es posible). Si por otro lado su objetivo es poner en su currículum tantas certificaciones como sea posible para que le sea más fácil obtener un empleo en alguna de las empresas que se dejan llevar por la mercadotecnia y sobrevaloran las certificaciones, también es válido, pero no espere que las personas expertas en dicho tema, asuman que Ud. también es un experto sólo por su certificación.

El juego de gato en Ruby ahora para Web

marzo 20, 2012

En un post anterior (http://salomonrt.wordpress.com/2010/04/03/juego-de-gato-%C2%BFinvensible-en-ruby/) puse el código fuente del juego de gato en Ruby, dicho programa hay que ejecutarlo desde la terminal. Decidí hacer una versión modificada de ese código para que funcionara desde un navegador web (para lo cual utilicé Sinatra).

Aquí les dejo el código.

Programa principal

Archivo gatoweb.rb

#       Copyright 2012 SALOMON RINCON TORRES <rtmex@yahoo.com>
#
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.

#encoding: utf-8
require 'sinatra'

opcion_programa = 0

# Inicializa las variablee globales
$mensaje = nil
$encabezado = "Juego de gato hecho en Ruby con Sinatra"
$tablero = Array.new(3){Array.new(3)}
$casillas_disponibles = (1..9).to_a
$gana_programa = false
$caracter_usuario = nil
$caracter_prog = nil

def inicializa
    $gana_programa = false
    # Inicializa el tablero
    for i in 0..2 do
        for j in 0..2 do
            $tablero[j][i]=nil
        end
    end
    $casillas_disponibles.clear
    for i in 1..9 do
        $casillas_disponibles.push(i)
    end
end

# Tira al azar en una esquina disponible
def tira_esquina
    opcion = 0
    esquina_seleccionada = 0
    # Tira al azar en una esquina, se basa en cuatro opciones:
    # opcion 1 será la esquina superior izquierda
    # opcion 2 será la esquina superior derecha
    # opcion 3 será la esquina inferior izquierda
    # opcion 4 será la esquina inferior derecha
    while not $casillas_disponibles.include?(esquina_seleccionada)
          opcion = rand(4)+1
          case opcion
               when 1 then esquina_seleccionada = 1
               when 2 then esquina_seleccionada = 3
               when 3 then esquina_seleccionada = 7
               when 4 then esquina_seleccionada = 9
          end
    end

    return esquina_seleccionada
end

# Verifica en que casilla debe tirar quién este jugando con el caracter que recibe como parámetro
# para ganar horizontalmente
def gana_horizontal(caracter)
    tiros_hechos = 0
    contador = 0
    columna = -1
    fila = 0
    ganador_en = 0

    # El siguiente ciclo termina cuando se hayan recorrido todos los renglónes o se haya encontrado
    # un renglón en donde se han hecho dos tiros
    while contador <= 2 and tiros_hechos < 2
        for j in 0..2 do
            if $tablero[j][contador]==caracter then
               tiros_hechos = tiros_hechos+1
            elsif $tablero[j][contador]==nil
                  columna = j
            end
        end
        # Si en el renglón actual había dos tiros y una casilla vacía, ahi se debe tirar para ganar
        if tiros_hechos == 2 and columna >= 0 then
           fila = contador
        else
            tiros_hechos = 0
            columna = -1
            contador = contador + 1
        end
    end

    if tiros_hechos == 2 then
       case fila
            when 0 then
                   # Significa que se encontró un tiro ganador en el primer renglón
                   ganador_en = columna + 1
            when 1 then
                   # Significa que se encontró un tiro ganador en el segundo renglón
                   ganador_en = columna + 4
            when 2 then
                   # Significa que se encontró un tiro ganador en el tercer renglón
                   ganador_en = columna + 7
       end
    end

    return ganador_en
end

# Verifica en que casilla debe tirar quién este jugando con el caracter que recibe como parámetro
# para ganar verticalmente
def gana_vertical(caracter)
    tiros_hechos = 0
    contador = 0
    columna = 0
    fila = -1
    ganador_en = 0

    # El siguiente ciclo termina cuando se hayan recorrido todos los renglónes o se haya encontrado
    # un renglón en donde se han hecho dos tiros
    while contador <= 2 and tiros_hechos < 2
        for j in 0..2 do
            if $tablero[contador][j]==caracter then
               tiros_hechos = tiros_hechos+1
            elsif $tablero[contador][j]==nil
                  fila = j
            end
        end
        # Si en la columna actual había dos tiros y una casilla vacía, ahi se debe tirar para ganar
        if tiros_hechos == 2 and fila >= 0 then
           columna = contador
        else
            tiros_hechos = 0
            fila = -1
            contador = contador + 1
        end
    end

    if tiros_hechos == 2 then
       case fila
            when 0 then
                   # Significa que se encontró un tiro ganador en el primer renglón
                   ganador_en = columna + 1
            when 1 then
                   # Significa que se encontró un tiro ganador en el segundo renglón
                   ganador_en = columna + 4
            when 2 then
                   # Significa que se encontró un tiro ganador en el tercer renglón
                   ganador_en = columna + 7
       end
    end

    return ganador_en
end

# Verifica en que casilla debe tirar quién este jugando con el caracter que recibe como parámetro
# para ganar diagonalmente
def gana_diagonal(caracter)
    tirosd1 = 0
    tirosd2 = 0
    contador = 0
    fila1 = -1
    fila2 = -1
    ganador_en = 0
    diagonal_1 = [0,1,2]
    diagonal_2 = [2,1,0]

    # El siguiente ciclo termina cuando se hayan recorrido todos los renglónes o se haya encontrado
    # que se han hecho dos tiros en una de las 2 diagonales diagonal
    while contador <= 2
          # Checa si hay un tiro a la casilla correspondiente en la diagonal 1
          if $tablero[diagonal_1.at(contador)][contador]==caracter then
             tirosd1 = tirosd1+1
          elsif $tablero[diagonal_1.at(contador)][contador]==nil
                fila1 = contador
          end

          # Checa si hay un tiro a la casilla correspondiente en la diagonal 2
          if $tablero[diagonal_2.at(contador)][contador]==caracter then
             tirosd2 = tirosd2+1
          elsif $tablero[diagonal_2.at(contador)][contador]==nil
                fila2 = contador
          end

          contador = contador+1
    end

    if tirosd1 == 2 and fila1>=0 then
       # Significa que se encontró el tiro ganador en la diagonal 1
       case fila1
            when 0 then
                   # Significa que se encontró el tiro ganador en el primer renglón
                   ganador_en = 1
            when 1 then
                   # Significa que se encontró el tiro ganador en el segundo renglón
                   ganador_en = 5
            when 2 then
                   # Significa que se encontró el tiro ganador en el tercer renglón
                   ganador_en = 9
       end
    elsif tirosd2 == 2 and fila2>=0 then
          # Significa que se encontró el tiro ganador en la diagonal 2
          case fila2
               when 0 then
                    # Significa que se encontró el tiro ganador en el primer renglón
                    ganador_en = 3
               when 1 then
                    # Significa que se encontró el tiro ganador en el segundo renglón
                    ganador_en = 5
               when 2 then
                    # Significa que se encontró el tiro ganador en el tercer renglón
                    ganador_en = 7
          end
    end

    return ganador_en
end

def renglon_tirou
    conta = 0
    renglones = Array.new

    for i in 0..2 do
        conta = 0
        for j in 0..2 do
            if $tablero[j][i]==$caracter_usuario then
               conta = conta+1
            end
        end
        if conta > 0 then
           renglones << i
        end
    end

    return renglones
end

# Ejecuta el tiro del programa
def tiro_programa
    veces_tiradas = 0
    espacios = 0
    maximo = -1
    columna = -1
    tiro = 0
    tiros_en_renglon = 0
    tiros_en_columna = 0
    tiros_usario = 0
    esquina_opuesta_ocupada = 1
    # Crea un array con las 4 esquinas en que puede intentar tirar
    esquinas_intentadas = [1,3,7,9]
    # Elimina del array las esquinas que no están disponibles
    esquinas_intentadas.delete_if {|elemento| not $casillas_disponibles.include?(elemento)}

    # Verifica cuántas veces ha tirado el programa
    for i in 0..2 do
        for j in 0..2 do
            if $tablero[j][i]==$caracter_prog then
               veces_tiradas = veces_tiradas+1
            end
        end
    end

    if veces_tiradas == 0 then
       # Primero intenta tirar al centro
       if $casillas_disponibles.include?(5) then
          tiro = 5
       else
           # Tira al azar en una esquina
           tiro = tira_esquina
       end
    else
        # Verifica si hay una casilla en la cual el programa gana horizontalmente y si es asi tira en ella
        tiro = gana_horizontal($caracter_prog)
        if tiro == 0 then
           # Significa que el programa no gana horizontalmente tirando en alguna casilla
           # Verifica si hay una casilla en la cual el programa gana verticalmente y si es asi tira en ella
           tiro = gana_vertical($caracter_prog)
           if tiro == 0 then
              # Significa que el programa no gana verticalmente tirando en alguna casilla
              # Verifica si hay una casilla en la cual el programa gana diagonalmente y si es asi tira en ella
              tiro = gana_diagonal($caracter_prog)
              if tiro == 0 then
                 # Significa que el programa no gana diagonalmente tirando en alguna casilla
                 # Entónces checa si el usuario gana diagonalmente tirando en alguna casilla y si es asi lo bloquea
                 tiro = gana_diagonal($caracter_usuario)
                 if tiro == 0
                    # Significa que el usuario no gana diagonalmente tirando en alguna casilla
                    # Entónces checa si el usuario gana horizontalmente tirando en alguna casilla y si es asi lo bloquea
                    tiro = gana_horizontal($caracter_usuario)
                    if tiro == 0 then
                       # Significa que el usuario no gana horizontalmente tirando en alguna casilla
                       # Entónces checa si el usuario gana verticalmente tirando en alguna casilla y si es asi lo bloquea
                       tiro = gana_vertical($caracter_usuario)
                       if tiro == 0 then
                          # Significa que el usuario no gana verticalmente tirando en alguna casilla
                          # Entónces busca tirar en el renglón que cumpla lo siguiente:
                          # 1. Haya mayor número de casillas disponibles
                          # 2. El usuario no ha tirado en ese renglón
                          # 3. El programa ya había tirado previamente en ese renglón
                          for i in 0..2 do
                              espacios = 0
                              veces_tiradas = 0
                              tiros_usuario = 0
                              for j in 0..2 do
                                  case $tablero[j][i]
                                       when nil then espacios = espacios+1
                                       when $caracter_prog then veces_tiradas = veces_tiradas+1
                                       when $caracter_usuario then tiros_usuario = tiros_usario+1
                                  end
                              end

                              if espacios > maximo and tiros_usuario == 0 then
                                 if veces_tiradas > tiros_en_renglon then
                                    tiros_en_renglon = veces_tiradas
                                    # Almacena en la variable maximo, el número de renglón que tiene más casillas disponibles
                                    maximo = i
                                    for j in 0..2 do
                                        # Almacena en la variable columna, la columna en donde se encontró
                                        # la primer casilla disponible
                                        if $tablero[j][i] == nil then
                                           columna = j
                                        end
                                    end
                                 end
                              end
                          end

                          case maximo
                               when 0 then tiro = columna+1
                               when 1 then tiro = columna+4
                               when 2 then tiro = columna+7
                          end

                          if tiro == 0 then
                             # Busca tirar en la columna que cumpla lo siguiente:
                             # 1. Haya mayor número de casillas disponibles
                             # 2. El usuario no ha tirado en esa columna
                             # 3. El programa ya había tirado previamente en esa columna
                             maximo = -1
                             for i in 0..2 do
                                 espacios = 0
                                 veces_tiradas = 0
                                 tiros_usuario = 0
                                 for j in 0..2 do
                                     case $tablero[i][j]
                                          when nil then espacios = espacios+1
                                          when $caracter_prog then veces_tiradas = veces_tiradas+1
                                          when $caracter_usuario then tiros_usuario = tiros_usario+1
                                     end
                                 end

                                 if espacios > maximo and tiros_usuario == 0 then
                                    if veces_tiradas > tiros_en_columna then
                                       tiros_en_columna = veces_tiradas
                                       # Almacena en la variable columna, el número de columna que tiene más casillas disponibles
                                       columna = i

                                       # Almacena en la variable maximo, el número de renglón en donde se encontró
                                       # la primer casilla disponible
                                       maximo = $tablero[i].index(nil)
                                    end
                                 end
                             end

                             case maximo
                                  when 0 then tiro = columna+1
                                  when 1 then tiro = columna+4
                                  when 2 then tiro = columna+7
                             end

                             if tiro == 0 then
                                # Busca tirar en una esquina que no sea
                                # diagonalmente opuesta a una esquina en la que haya tirado el usuario
                                # Además verifica si la esquina seleccionada se encuentre en un renglón
                                # en donde ya haya tirado el usauro (si existe este caso y no se tira
                                # en esta esquina, el usuario gana en el próximo tiro)

                                while esquina_opuesta_ocupada == 1 and !esquinas_intentadas.empty?
                                      tiro = tira_esquina
                                      case tiro
                                           when 1 then
                                                if $tablero[2][2] == nil or $tablero[2][2] == $caracter_prog then
                                                   # Verifica si esta esquina se encuentra en un renglón en donde ya haya tirado el usuario
                                                   if renglon_tirou.include?(0) then
                                                      esquina_opuesta_ocupada = 0
                                                   else
                                                       esquinas_intentadas.delete(1)
                                                   end
                                                else
                                                    esquinas_intentadas.delete(1)
                                                end
                                           when 3 then
                                                if $tablero[0][2] == nil or $tablero[0][2] == $caracter_prog then
                                                   # Verifica si esta esquina se encuentra en un renglón en donde ya haya tirado el usuario
                                                   if renglon_tirou.include?(0) then
                                                      esquina_opuesta_ocupada = 0
                                                   else
                                                       esquinas_intentadas.delete(3)
                                                   end
                                                else
                                                    esquinas_intentadas.delete(3)
                                                end
                                           when 7 then
                                                if $tablero[2][0] == nil or $tablero[2][0] == $caracter_prog then
                                                   # Verifica si esta esquina se encuentra en un renglón en donde ya haya tirado el usuario
                                                   if renglon_tirou.include?(2) then
                                                      esquina_opuesta_ocupada = 0
                                                   else
                                                       esquinas_intentadas.delete(7)
                                                   end
                                                else
                                                    esquinas_intentadas.delete(7)
                                                end
                                           when 9 then
                                                if $tablero[0][0] == nil or $tablero[0][0] == $caracter_prog then
                                                   # Verifica si esta esquina se encuentra en un renglón en donde ya haya tirado el usuario
                                                   if renglon_tirou.include?(2) then
                                                      esquina_opuesta_ocupada = 0
                                                   else
                                                       esquinas_intentadas.delete(9)
                                                   end
                                                else
                                                    esquinas_intentadas.delete(9)
                                                end
                                      end
                                end
                                if esquina_opuesta_ocupada == 1 then
                                   # Intenta al azar tirar en una casilla disponible
                                   while not $casillas_disponibles.include?(tiro)
                                         tiro = rand(9)+1
                                   end
                                end
                             end
                          end
                       end
                    end
                 end
              else
                  $gana_programa = true
              end
           else
               $gana_programa = true
           end
        else
            $gana_programa = true
        end
    end
    return tiro
end

# Procesa el tiro del programa
def procesa_tiro
    opcion_programa = tiro_programa
    case opcion_programa
         when 1 then $tablero[0][0] = $caracter_prog
         when 2 then $tablero[1][0] = $caracter_prog
         when 3 then $tablero[2][0] = $caracter_prog
         when 4 then $tablero[0][1] = $caracter_prog
         when 5 then $tablero[1][1] = $caracter_prog
         when 6 then $tablero[2][1] = $caracter_prog
         when 7 then $tablero[0][2] = $caracter_prog
         when 8 then $tablero[1][2] = $caracter_prog
         when 9 then $tablero[2][2] = $caracter_prog
    end
    # Elimina del array casillas_disponibles la posición en donde tiró el programa
    $casillas_disponibles.delete(opcion_programa)
    redirect '/imprime_tablero'
end

# menú principal
get '/' do
  inicializa
  erb :index
end

# permite al usuario seleccionar el caracter con el que va a tirar
# e indicar quién tira primero
post '/configura' do
  case params[:CaracterUsuario]
       when "X"
            $caracter_usuario = "X"
            $caracter_prog = "O"
       when "O"
            $caracter_usuario = "O"
            $caracter_prog = "X"
  end
  if params[:IniciaUsuario]
     redirect '/imprime_tablero'
  else
      if not $casillas_disponibles.empty? then
         procesa_tiro
      end
  end
end

# muestra el tablero
get '/imprime_tablero' do
  erb :tablero
end

# procesa el tiro del usuario
get '/casilla/:numero' do
  case params[:numero].to_i
       when 1 then $tablero[0][0] = $caracter_usuario
       when 2 then $tablero[1][0] = $caracter_usuario
       when 3 then $tablero[2][0] = $caracter_usuario
       when 4 then $tablero[0][1] = $caracter_usuario
       when 5 then $tablero[1][1] = $caracter_usuario
       when 6 then $tablero[2][1] = $caracter_usuario
       when 7 then $tablero[0][2] = $caracter_usuario
       when 8 then $tablero[1][2] = $caracter_usuario
       when 9 then $tablero[2][2] = $caracter_usuario
  end
  # Elimina del array casillas_disponibles la posición en donde tiró el usuario
  $casillas_disponibles.delete(params[:numero].to_i)
  if $casillas_disponibles.empty?
     redirect '/imprime_tablero'
  else
      procesa_tiro
  end
end

Hoja de estilos

Archivo public/stylesheets/style.css


html
{
background: #fff;
font: normal 18px Arial, Tahoma, Helvetica, sans-serif;
margin: 0;
padding: 0;
height: 100%;
}

body
{
margin: 0;
min-height: 100%;
position: relative;
}

#principal
{
width: 800px;
margin-left: auto;
margin-right: auto;
margin-top: 0px;
margin-bottom: 0px;
padding: 0px;
padding-bottom: 90px;
}

#encabezado
{
width: 100%;
padding: 15px 0 0 45px;
height: 80px;
}

#contenido
{
width: 900px;
background-color: #FFFFFF;
margin-left: auto;
margin-right: auto;
margin-top: 25px;
margin-bottom: 0px;
}

#contenido table
{
font: normal 80px Arial, Tahoma, Helvetica, sans-serif;
margin: auto;
}

#contenido a
{
text-decoration:  none;
}

td.colcentro
{
border-left: 3px solid black;
border-right: 3px solid black;
}

td.rowcentro
{
border-top: 3px solid black;
border-bottom: 3px solid black;
}

.numero
{
text-align: center;
color: white;
}

h1, h2, h3, p
{
padding: 0;
margin: 0;
}

.division
{
margin: 25px 0;
height: 1px;
width: 900px;
border: 0;
background: black;
background: -webkit-gradient(linear, 0 0, 100% 0, from(white), to(white), color-stop(50%, black));
text-align: left;
}

Vistas

Archivo views/layout.erb


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Juego de gato</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="description" content="Juego de gato" />
<meta name="keywords" content="Gato, Sinatra, ruby, tic tac toe" />
<link href="/stylesheets/style.css" rel="stylesheet" type="text/css" />

</head>
<body>
<%= yield %>
</body>
</html>

Archivo views/index.erb

<div id="principal">
	<div id="encabezado"><h1><%= $encabezado %></h1></div>
		<p><h2>Inicio</h2>
	    <hr class="division" />
	    <div id="contenido">
	    <form action="/configura" method="post" accept-charset="utf-8">
		    <lable>Caracter con el que tira el usuario</label>
		    <input type="radio" name="CaracterUsuario" value="X" checked>X
		    <input type="radio" name="CaracterUsuario" value="O">O
		    <br>
		    <label>Tira primero el usuario</label><input type="checkbox" name="IniciaUsuario" id="IniciaUsuario" value="true"/>
			<br><br>
			<p>
			   <input type="submit" value="Aceptar"/>
			</p>  
		</form>
		</div>
</div>

Archivo views/tablero.erb

<div id="principal">
	<div id="encabezado"><h1><%= $encabezado %></h1></div>
	    <p><h2>Tablero</h2>
	    <hr class="division" />
	    <div id="contenido">
		    <table class="tablero">
                  <% num = 1
                     for i in 0..2 do %>
                         <tr>
                            <% for j in 0..2 do %>
                                <% case num 
                                        when 2, 8 %>
                                             <td class="colcentro">
                                     <% when 4, 6 %> 
                                             <td class="rowcentro">
                                     <% when 5 %>
                                             <td class="colcentro rowcentro">
                                     <% else %>
                                             <td>
                                <% end 
                                   if $tablero[j][i]==nil then
                                      if !$gana_programa then %>
                                         <a href="casilla/<%= num %>" class="numero"><%= num %></a>
                                   <% else %>
                                       <p class="numero"><%= num %></p>
                                   <% end
                                   else %>
                                       <%= $tablero[j][i] %>
                                <% end
                                   num = num+1 %>
                                   </td>
                            <% end %>
                         </tr>
                  <% end %>
		    </table>
		    <br>
		    <a href="/">Inicio</a>
		 <% if $casillas_disponibles.empty? or $gana_programa then
                       if $gana_programa then %>
                          <p>Gana el programa!</p>
                    <% else %>
                          <p>No hay ganador!</p>
                    <% end
               end %>
	</div>
</div>

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.