Archive for the ‘Programación’ Category

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 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>

Gráfica de barras horizontales en la terminal con C++

diciembre 14, 2011

Ya en un post anterior había escrito sobre la STL de C++; el código siguiente simula una gráfica de barras horizontales en modo texto y aprovecho para ejemplificar el uso de algunas funciones y algoritmos de la STL.
El programa consiste en pedirle al usuario que capture números positivos y los va almacenando en un vector, para terminar la captura el usuario debe introducir un número negativo. Una vez terminada la captura, se copian los valores almacenados en el vector a otro vector y después se ordenan para que en el vector original los números queden en el mismo orden en que el usuario los fue capturando. Esta parte de copiar los valores de un vector a otro y ordenar los valores en el segundo vector no es necesaria para el funcionamiento del programa, sólo lo pongo para ejemplificar com se haría esto usando la STL.

En el programa utilizo funciones de la STL para encontrar el valor menor y el valor mayor en el vector original sin tener que ordenarlo.

Aquí está el código
Archivo grafica_barrash.cpp

#include <iostream>
#include <vector>
#include <algorithm>
#include <math.h>

using namespace std;

int main()
{
 int i_x=0, i_y=0;
 double dbl_valor=0, dbl_escala=0;
 vector<double> v_valores, v_ordenados;
 vector<double>::iterator v_valores_it, v_ordenados_it;
 vector<double>::const_iterator menor_it, mayor_it, valor_it;

 cout << "Teclee un número, (cualquier número negativo para terminar):" << endl;
 /* Mientras el usuario teclee números positivos (incluido el cero), guardamos en el vector
    v_valores los números que el usuario va tecleando
 */
 while (dbl_valor >= 0)
       {
        cin >> dbl_valor;
        if (dbl_valor >= 0)
            v_valores.push_back(dbl_valor);
       }

 cout << endl << "Estos son los números que Ud. introdujo:" << endl;
 // Muestra los valores almacenados en el vector v_valores
 for ( v_valores_it = v_valores.begin(); v_valores_it != v_valores.end(); v_valores_it++)
      cout << *v_valores_it << endl;

 // Hace el vector v_ordenados del mismo tamaño que el vector v_valores
 v_ordenados.resize (v_valores.size());
 // Copia los valores del vector v_valores al vector v_ordenados
 copy (v_valores.begin(), v_valores.end(), v_ordenados.begin());
 // Ordena el vector v_ordenados
 std::sort(v_ordenados.begin(), v_ordenados.end());

 cout << endl << "Estos son los números ordenados:" << endl;
 // Muestra los valores almacenados en el vector v_ordenados
 for ( v_ordenados_it = v_ordenados.begin(); v_ordenados_it != v_ordenados.end(); v_ordenados_it++)
      cout << *v_ordenados_it << endl;

 menor_it = min_element(v_valores.begin(), v_valores.end());
 mayor_it = max_element(v_valores.begin(), v_valores.end());

 cout << endl << "El valor menor es: " << *menor_it << endl;
 cout << "El valor mayor es: " << *mayor_it << endl;

 // Obtiene la escala
 dbl_escala = *mayor_it - *menor_it;
 cout << "La escala es: " << dbl_escala << endl << endl;

 for (i_x=0; i_x<8; i_x++)
      cout << " ";

 cout << ceil(*menor_it);
 for (i_x=0; i_x<=34; i_x++)
      cout << " ";
 cout << ceil((dbl_escala/2)+*menor_it);
 for (i_x=0; i_x<=34; i_x++)
      cout << " ";
 cout << ceil(*mayor_it);
 cout << endl;
 for (i_x=0; i_x<8; i_x++)
      cout << " ";
 cout << "|";
 for (i_x=0; i_x<=34; i_x++)
      cout << " ";
 cout << "|";
 for (i_x=0; i_x<=34; i_x++)
      cout << " ";
 cout << "|" << endl;

 // Dibuja las barras
 i_x=1;
 for ( v_valores_it = v_valores.begin(); v_valores_it != v_valores.end(); v_valores_it++)
     {
      cout << "Valor"<< i_x;
      /* Si el valor de x es de un sólo dígito, deja 2 espacios antes de empezar a dibujar la barra, de lo contrario
         deja sólo uno
      */
      cout << (i_x < 10 ? "  " : " ");
      valor_it = v_valores_it;
      for (i_y=1; i_y<= ((double)(*valor_it - *menor_it)/dbl_escala)*72+1; i_y++)
           cout << "*";
      cout << endl;
      i_x++;
     }

 return 0;
}

A continuación pongo una captura de pantalla
Como se puede ver, la gráfica muestra los valores como fueron introducidos por el usuario, así que:
valor1 = 100, valor2 = 40, valor3 = 75, valor4 = 50,valor5 = 25, valor6 = 30, valor7 = 10

Esto porque recibí un correo de una persona que dijo ser estudiante y me reclamaba que “estaba todo mal” y le habían puesto 1 de calificación.
En primer lugar, como la captura de pantalla demuestra, el programa funciona bien; en segundo lugar, este blog no es para que eviten hacer su tarea si son estudiantes; y en tercer lugar, las críticas constructivas a cualquier post que publique, son bienvenidas, pero si como en este caso, sólo escribe tonterías como “esta todo mal” y no aporta argumentos de en que se basa para hacer tal afirmación, es obvio que voy a ignorarlo.
Por último, mis post generalmente serán para personas con conocimientos mínimos de programación, por lo menos que sepan deducir un algoritmo a partir del código fuente que leen.

Implementación de pilas con apuntadores en C

octubre 21, 2011

En este post muestro como implementar las funciones push y pop para el manejo de pilas, y dos problemas clásicos que se pide se solucionen utilizando pilas.

El siguiente archivo contiene la implementación de las funciones push y pop para ser utilizadas con caracteres

//Archivo pila_char.h
#include <stdio.h>
#include <stdlib.h>

typedef struct nodo_s
{
 char dato;
 struct nodo_s *siguiente;
} nodo_t;

typedef nodo_t *ptrNodo;
typedef nodo_t *ptrPila;

/*
    Agrega un nodo al inicio de la lista ligada
    *pila es el apuntador que apunta al primer nodo de la lista ligada (la cima de la pila)
*/
void push(ptrPila *pila, char x)
{
 // Crea un nuevo nodo
 ptrNodo nodo;
 nodo = (ptrNodo)malloc(sizeof(nodo_t));
 if (nodo != NULL)
    {
     nodo->dato = x;
     // El apuntador nodo->siguiente va a apuntar al primer nodo de la lista ligada
     nodo->siguiente = *pila;
     // pila va a apuntar al nuevo nodo, con esto hacemos que el nuevo nodo sea ahora el primer nodo de la lista ligada
     *pila=nodo;
    }
}

/*
    Elimina el primer nodo de la lista ligada
    *pila es el apuntador que apunta al primer nodo de la lista ligada (la cima de la pila)
*/
char pop(ptrPila *pila)
{
 // Crea un nuevo nodo
 ptrNodo nodo;
 char x;
 
 // El nuevo nodo va a apuntar al primer nodo de la lista ligada
 nodo = *pila;
 x = (*pila)->dato;
 // Ahora el segundo nodo de la lista ligada va a ser el primero
 *pila = (*pila)->siguiente;
 // Borra el primer nodo de la lista ligada
 free(nodo);
 // Regresa el valor que contenía el nodo que se eliminó
 return x;
}

/*
    Regresa 1 si no hay nodos en la lista ligada y cero en caso contrario
    *pila es el apuntador que apunta al primer nodo de la lista ligada (la cima de la pila)
*/
int pila_vacia(ptrPila *pila)
{
 return (*pila == NULL ? 1:0);
}

/*
   Muestra los datos de los nodos
*/ 
void nodos_pila(ptrNodo nodo)
{
 if (nodo == NULL)
     printf("La pila está vacia\n");
 else
     {
      while (nodo != NULL)
            {
             printf("%c\n",nodo->dato);
             nodo = nodo->siguiente;
            }
      printf("\n");
     }
}

El siguiente es un programa de ejemplo que permite analizar una cadena y determinar si los paréntesis o corchetes están balanceados, las funciones están hechas de tal forma que podemos agregar también llaves, etc. ya que sólo hay que indicar cuál es el caracter que abre y cuál es el caracter que cierra.

// Archivo pila_parentesis.c

#include <stdio.h>
#include "pila_char.h"

int verifica_balance(char expresion[], char cabre, char ccierra);

int main()
{
 char cadena[]="2*[x+q(3s-2)]/[((x+1)*x+(s-2))]";
 int i=0;
 
 // Muestra la cadena
 printf("La cadena a analizar es la siguiente:\n\n");
 while (cadena[i] != '\0')
       {
        printf("%c", cadena[i]);
        i++;
       }
 
 printf("\n\n");
 // Verifica si los paréntesis están balanceados
 if (verifica_balance(cadena, '(', ')') == 1)
     printf("Los paréntesis están balanceados\n");
 else
     printf("Los paréntesis NO están balanceados\n");
 
 // Verifica si los corchetes están balanceados
 if (verifica_balance(cadena, '[', ']') == 1)
     printf("Los corchetes están balanceados\n");
 else
     printf("Los corchetes NO están balanceados\n");
 
 return 0;
}

int verifica_balance(char expresion[], char cabre, char ccierra)
{
 int x=0, balanceados=1;
 ptrPila pila = NULL;
 
 // Recorre la cadena
 while (expresion[x] != '\0' && balanceados == 1)
       {
        // Si el elemento coincide con el caracter que abre, lo ingresa en la pila
        if (expresion[x]==cabre)
            push(&pila, expresion[x]);
        else
            // Si el elemento coincide con el caracter que cierra, lo saca de la pila
            if (expresion[x]==ccierra)
               {
                /* Si la pila está vacía, significa que los caracteres no están balanceados
                   porque se encontró un caracter que cierra sin que exista antes un caracter que abre
                */
                if (pila_vacia(&pila) != 1)
                    pop(&pila);
                else
                    balanceados = 0;
               }
        x++;
       }
 
 /* Si balanceados = 1 pero la pila no está vacía, los caracteres no están balanceados
    porque quedaron caracteres que abren sin tener su caracter que cierra
 */ 
 if (balanceados == 1 && pila_vacia(&pila) != 1)
     balanceados = 0;

 // Se asegura de dejar la pila vacia
 while (pila_vacia(&pila) != 1)
        pop(&pila);
 
 return balanceados; 
}

El siguiente archivo contiene la implementación de las funciones push y pop para ser utilizadas con enteros

// Archivo pila_int.h

#include <stdio.h>
#include <stdlib.h>

typedef struct nodo_s
{
 int dato;
 struct nodo_s *siguiente;
} nodo_t;

typedef nodo_t *ptrNodo;
typedef nodo_t *ptrPila;

/*
    Agrega un nodo al inicio de la lista ligada
    *pila es el apuntador que apunta al primer nodo de la lista ligada (la cima de la pila)
*/
void push(ptrPila *pila, int x)
{
 // Crea un nuevo nodo
 ptrNodo nodo;
 nodo = (ptrNodo)malloc(sizeof(nodo_t));
 if (nodo != NULL)
    {
     nodo->dato = x;
     // El apuntador nodo->siguiente va a apuntar al primer nodo de la lista ligada
     nodo->siguiente = *pila;
     // pila va a apuntar al nuevo nodo, con esto hacemos que el nuevo nodo sea ahora el primer nodo de la lista ligada
     *pila=nodo;
    }
}

/*
    Elimina el primer nodo de la lista ligada
    *pila es el apuntador que apunta al primer nodo de la lista ligada (la cima de la pila)
*/
int pop(ptrPila *pila)
{
 // Crea un nuevo nodo
 ptrNodo nodo;
 int x=0;
 
 // El nuevo nodo va a apuntar al primer nodo de la lista ligada
 nodo = *pila;
 x = (*pila)->dato;
 // Ahora el segundo nodo de la lista ligada va a ser el primero
 *pila = (*pila)->siguiente;
 // Borra el primer nodo de la lista ligada
 free(nodo);
 // Regresa el valor que contenía el nodo que se eliminó
 return x;
}

/*
    Regresa 1 si no hay nodos en la lista ligada y cero en caso contrario
    *pila es el apuntador que apunta al primer nodo de la lista ligada (la cima de la pila)
*/
int pila_vacia(ptrPila *pila)
{
 return (*pila == NULL ? 1:0);
}

void nodos_pila(ptrNodo nodo)
{
 if (nodo == NULL)
     printf("La pila está vacia\n");
 else
     {
      while (nodo != NULL)
            {
             printf("%d\n",nodo->dato);
             nodo = nodo->siguiente;
            }
      printf("\n");
     }
}

Como ejemplo, el clásico programa para hacer operaciones aritméticas en notación postfix

// Archivo pila_postfixexpr.c

#include <stdio.h>
#include "pila_int.h"

int main()
{
 char cadena[]="45+72-*5/";
 int i=0, num1=0, num2=0, result=0;
 ptrPila pila = NULL;
 
 // Muestra la cadena
 printf("La cadena a analizar es la siguiente:\n\n");
 while (cadena[i] != '\0')
       {
        printf("%c", cadena[i]);		
        i++;
       }
 
 i=0;
 printf("\n\n");
 // Recorre la cadena
 while (cadena[i] != '\0')
       {
        // Si el elemento no es un operador, lo ingresa en la pila
        if (cadena[i]!='+' && cadena[i]!='-' && cadena[i]!='*' && cadena[i]!='/')		   
            push(&pila, ((int)cadena[i])-48);   // El código ASCII de 0 es 48
        else
             {
              num2=pop(&pila);
              num1=pop(&pila);
              switch (cadena[i])
                     {
                      case '+':
                               result = num1+num2;
                               printf("suma %d + %d = %d\n",num1, num2, result);
                               push(&pila, result);
                               break;
                      case '-':
                               result = num1-num2;
                               printf("resta %d - %d = %d\n",num1, num2, result);
                               push(&pila, result);
                               break;
                      case '*':
                               result = num1*num2;
                               printf("multiplica %d * %d = %d\n",num1, num2, result);
                               push(&pila, result);
                               break;
                      case '/':
                               result = num1/num2;
                               printf("divide %d / %d = %d\n",num1, num2, result);
                               push(&pila, result);
                               break;
                     }			 
              }
        i++;
       }

 if (pila_vacia(&pila)!=1)
    {
     printf("\n\nLos elementos en la pila son los siguientes;\n\n");
     // Muestra los elementos que están en la pila
     nodos_pila(pila);
    }
 
 return 0;
}

Instalar Sinatra + PostgreSQL + Datamapper en Debian Squeeze

septiembre 28, 2011

Sinatra es un DSL para desarrollo web utilizando el lenguaje de programación Ruby. A diferencia de Rails, Sinatra NO obliga a seguir el patrón Modelo Vista Controlador (aunque es posible desarrollar aplicaciones web de ese tipo con Sinatra). He notado que la mayoría de las personas que desarrollan aplicaciones web con acceso a base de datos, utilizan MySQL o incluso SQLite, pero yo prefiero PostgreSQL y no hay razón por la que no se pueda utilizar con Sinatra, así que en este post voy a utilizar ambos junto con un ORM llamado Datamapper para crear los modelos y erb para crear las vistas.

Instalar Ruby

Debian Squeeze trae la versión 1.8.7 de Ruby y la versión 1.9.2, vamos a instalar la versión más reciente (note que el paquete debian se llama ruby1.9.1 pero trae la versión 1.9.2 de Ruby)

sudo apt-get install ruby1.9.1

Una vez instalado, creamos un enlace simbólico al que llamaremos simplemente ruby

cd /usr/bin
sudo ln -s ruby1.9.1 ruby

Instalar RubyGems

De la página http://rubygems.org/pages/download descargamos el archivo .zip o .tgz y lo descomprimimos. Si se descarga el archivo rubygems-1.8.10.tgz, al descomprimirlo se crea un directorio rubygems-1.8.10, hay que cambiarse a ese directorio y ejecutar

sudo ruby setup.rb

Ahora creamos un enlace simbólico al que llamaremos gem

cd /usr/bin
sudo ln -s gem1.9.1 gem

Instalar Sinatra

sudo gem install sinatra

Ya tenemos instalado Sinatra, para probarlo, haremos un programa en Ruby con el clásico hola mundo :). El archivo lo guardamos con el nombre holamundo.rb


# holamundo.rb
require 'sinatra'

get '/' do
'Hola Mundo!'
end

Ahora desde la terminal tecleamos

ruby holamundo.rb

Y aparece lo siguiente:

== Sinatra/1.2.6 has taken the stage on 4567 for development with backup from WEBrick
[2011-09-26 13:39:22] INFO  WEBrick 1.3.1
[2011-09-26 13:39:22] INFO  ruby 1.9.2 (2010-08-18) [i486-linux]
[2011-09-26 13:39:22] INFO  WEBrick::HTTPServer#start: pid=3076 port=4567

Abrimos un navegador web y nos vamos a

http://localhost:4567

y veremos el texto Hola Mundo!

Para parar la ejecución, vamos a la terminal y tecleamos Ctrl-C y aparece

== Sinatra has ended his set (crowd applauds)
[2011-09-26 13:40:50] INFO  going to shutdown …
[2011-09-26 13:40:50] INFO  WEBrick::HTTPServer#start done.

Si en lugar del servidor web WEBrick queremos utilizar Thin, debemos instalar los paquetes ruby1.9.1-dev y build-essential

sudo apt-get install ruby1.9.1-dev build-essential

y ahora si instalamos Thin

sudo gem install thin

Instalar PostgreSQL

sudo apt-get install postgresql postgresql-contrib libpq-dev

Debemos poner un password al usuario postgres

sudo passwd postgres

Nos cambiamos al usuario postgres y asignamos un password al usuario postgres dentro de PostgreSQL

su postgres
psql -c “ALTER USER postgres WITH PASSWORD ‘password_aqui’” -d template1

Aparece ALTER ROLE y nos muestra nuevamente el prompt

Todavia como el usuario postgres crear un nuevo usuario en PostgreSQL. Para crear el usuario en PostgreSQL vamos a indicar que sea superusuario (-s), que PostgreSQL nos pida indicar el password para dicho usuario (-P) y nos muestre en pantalla los mensajes que esto genere (-e de –echo)

En este caso el usuario que voy a crear se llamará sysdba y le pondré como password la palabra s3cret

createuser -P -s -e sysdba

Aparece lo siguiente:

Ingrese la contraseña para el nuevo rol:
Ingrésela nuevamente:
CREATE ROLE sysdba PASSWORD ‘md51767410185836193fcba975fe021e85b’ SUPERUSER CREATEDB CREATEROLE INHERIT LOGIN;

Salimos del usuario postgres con exit

Editamos el archivo /etc/postgresql/8.4/main/pg_hba.conf para indicar que el usuario que acabamos de crear puede accesar a la linea de comandos de PostgreSQL (psql)

sudo nano /etc/postgresql/8.4/main/pg_hba.conf

Agregamos la linea
local   all         sysdba                           md5

en la sección # Database administrative login by UNIX sockets

y queda algo parecido a esto

# Database administrative login by UNIX sockets
local   all         postgres                          ident
local   all         sysdba                            md5

Reiniciamos el servidor PostgreSQL

sudo /etc/init.d/postgresql restart

Aparece

Restarting PostgreSQL 8.4 database server: main.

Nos conectamos a la línea de comandos de PostgreSQL con el usuario que creamos (en este caso sysdba) y creamos una base de datos para pruebas

psql -U sysdba template1

Aparece:

psql (8.4.8)
Digite «help» para obtener ayuda.

template1=#

Tecleamos \l para ver la lista de las bases de datos que existen actualmente

template1=# \l
Listado de base de datos
Nombre   |  Dueño   | Codificación | Collation  |   Ctype    |      Privilegios
———–+———-+————–+————+————+———————–
postgres  | postgres | UTF8         | es_MX.utf8 | es_MX.utf8 |
template0 | postgres | UTF8         | es_MX.utf8 | es_MX.utf8 | =c/postgres
: postgres=CTc/postgres
template1 | postgres | UTF8         | es_MX.utf8 | es_MX.utf8 | =c/postgres
: postgres=CTc/postgres
(3 filas)

template1=#

Crearemos una nueva base de datos (en este caso se llamará development)

template1=# CREATE DATABASE development;
CREATE DATABASE
template1=#

Verificamos que se haya creado la base de datos

template1=# \l
Listado de base de datos
Nombre    |  Dueño   | Codificación | Collation  |   Ctype    |      Privilegios
————-+———-+————–+————+————+———————–
development | sysdba  | UTF8         | es_MX.utf8 | es_MX.utf8 |
postgres    | postgres | UTF8         | es_MX.utf8 | es_MX.utf8 |
template0   | postgres | UTF8         | es_MX.utf8 | es_MX.utf8 | =c/postgres
: postgres=CTc/postgres
template1   | postgres | UTF8         | es_MX.utf8 | es_MX.utf8 | =c/postgres
: postgres=CTc/postgres
(4 filas)

template1=#

Salimos de la terminal de PostgreSQL

template1=# \q

Instalar el gem para conectarse desde Ruby a PostgreSQL

sudo gem install pg

Para checar que está funcionando, entramos a la linea de comandos de ruby (desde la terminal tecleamos irb1.9.1) y tecleamos

require ‘pg’

Nos debe regresar true. Salimos con quit

$ irb1.9.1
irb(main):001:0> require ‘pg’
=> true
irb(main):002:0> quit

Instalar Datamapper

sudo gem install data_mapper

Instalar el adaptador que permite a Datamapper comunicarse con la base de datos (en este caso PostgreSQL)

sudo gem install dm-postgres-adapter

Ya tenemos instalado todo lo necesario para desarrollar aplicaciones web con acceso a base de datos utilizando Sinatra!. Para probarlo pongo a continuación un pequeño programa con las vistas y un archivo css.

Programa principal

El programa contiene un modelo en Datamapper llamado Linea, este modelo crea la tabla lineas en la base de datos development que creamos después de que instalamos PostgreSQL La tabla contiene sólo 2 campos; el campo cve_linea que es la llave primaria y el campo linea.

El programa hace las funciones de creación, lectura, actualización, borrado y listado de registros en la tabla

Archivo sinatra_test.rb


require 'sinatra'
require 'data_mapper'

configure do
# Se conecta a la base de datos
DataMapper.setup(:default, 'postgres://localhost/development')
DataMapper.setup(:default, {
  :adapter  => 'postgres',
  :host     => 'localhost',
  :username => 'sysdba' ,
  :password => 's3cret',
  :database => 'development'})
end

# Modelos
class Linea
  include DataMapper::Resource
  property :cve_linea, String, :length => 4, :required => true, :key => true
  property :linea, String, :length => 30, :required => true
end

DataMapper.auto_upgrade!

def campo_vacio(campo)
    campo.empty?
end

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

# muestra error debido a que un campo requerido se sejó vacío
get '/campo_vacio' do
  erb :error_campovacio
end

# *******************************
# LINEA
# *******************************

# nueva linea
get '/linea' do
  erb :reg_linea
end

# muestra el listado de lineas
get '/listalineas' do
  @lineas = Linea.all :order => :cve_linea
  erb :lista_lineas
end

# guarda la información de la linea
post '/linea' do
  @linea = Linea.new(:cve_linea => params[:cve_linea].upcase, :linea => params[:linea].upcase)
  if !campo_vacio(params[:cve_linea]) and !campo_vacio(params[:linea])
     if @linea.save
        redirect "/linea/#{@linea.cve_linea}"
     else
         redirect '/linea'
     end
  else
      redirect '/campo_vacio'
  end
end

# muestra los detalles de la linea
get '/linea/:cve_linea' do
  @linea = Linea.get(params[:cve_linea])
  if @linea
     erb :cons_linea
  else
      redirect '/linea'
  end
end

# muestra la forma para editar la linea
get '/editlinea/:cve_linea' do
  @linea = Linea.get(params[:cve_linea])
  if @linea
     erb :edit_linea
  else
      redirect '/listalineas'
  end
end

# muestra la forma para borrar la linea
get '/borralinea/:cve_linea' do
  @linea = Linea.get(params[:cve_linea])
  if @linea
     erb :borra_linea
  else
      redirect '/listalineas'
  end
end

# edita la linea
put '/editlinea/:cve_linea' do
  @linea = Linea.get(params[:cve_linea])
  if @linea
     @linea.linea = params[:linea].upcase
     @linea.save
     redirect '/listalineas'
  end
end

# borra la linea
delete '/borralinea/:cve_linea' do
  @linea = Linea.get(params[:cve_linea])
  if @linea
     @linea.destroy
     redirect '/listalineas'
  end
end

En el directorio en donde guardamos el programa principal (sinatra_test.rb) creamos dos directorios, uno llamado public y otro llamado views.
Dentro del directorio public creamos otro directorio que llamaremos stylesheets. Dentro de este directorio stylesheets vamos a poner nuestro archivo css (que llamaremos style.css). Sólo contendrá el código que nos permita “mostrar” el texto en mayúsculas automáticamente en los campos de captura. el programa sinatra_test.rb es el que convierte a mayúsculas el texto antes de guardarlo en la tabla.

Desde luego, ustedes pueden agregar el código css necesario para embellecer a su gusto la presentación de la aplicación.

Archivo public/stylesheets/style.css

.campo_entrada
{
 text-transform: uppercase;
}

Ahora en el directorio views vamos a crear las 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>Ejemplo Sinatra</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="description" content="Acceso a base de datos en PostgreSQL" />
<meta name="keywords" content="Sinatra, Ruby, Datamapper" />
<link href="/stylesheets/style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<%= yield %>
</body>
</html>

Archivo views/error_campovacio.erb

<div>
    <p>La información no se guardó debido a que dejó un campo vacío, por favor verifique!
    <p><a href="/">Menú Principal</a></p>
</div>

Archivo views/index.erb

<h1>Menú Principal</h1>

<p><a href="/linea">Registro de líneas</a></p>
<p><a href="/listalineas">Listado de líneas</a></p>

Archivo views/reg_linea.erb

<div>
    <form action="/linea" method="post" accept-charset="utf-8">
      <label>Clave de línea </label><input type="text" name="cve_linea" id="cve_linea" class="campo_entrada" maxlength=4>
      <br/>
      <label>Línea </label><input type="text" name="linea" id="linea" class="campo_entrada" maxlength=30>
      <br/>
      <input type="submit" value="Guardar"/>
    </form>
    <p><a href="/">Menú Principal</a></p>
</div>

Archivo views/cons_linea.erb

<div>
    <label>Clave de línea </label><%= @linea.cve_linea %>
    <br/>
    <label>Línea </label><%= @linea.linea %>
    <p><a href="/linea">Registro de líneas</a></p>
    <p><a href="/">Menú Principal</a></p>
</div>

Archivo views/edit_linea.erb

<div>
    <form action="/editlinea/<%= @linea.cve_linea %>" method="post" accept-charset="utf-8">
       <input type="hidden" name="_method" value="put">
       <label>Clave de línea </label><%= @linea.cve_linea %>
       <br/>
       <label>Línea </label><input type="text" name="linea" id="linea" class="campo_entrada" maxlength=30 value="<%= @linea.linea %>">
       <br/>
       <input type="submit" value="Guardar"/>
    </form>
    <p><a href="/listalineas">Listado de líneas</a></p>
    <p><a href="/">Menú Principal</a></p>
</div>

Archivo views/borra_linea.erb

<div>
    <p>Se borrará la siguiente línea: </p>
    <form action="/borralinea/<%= @linea.cve_linea %>" method="post" accept-charset="utf-8">
       <input type="hidden" name="_method" value="delete">
       <label>Clave de línea </label><%= @linea.cve_linea %>
       <br>
       <label>Línea </label><%= @linea.linea %>
       <br/>
       <input type="submit" value="Borrar"/>
    </form>
    <p><a href="/listalineas">Listado de líneas</a></p>
    <p><a href="/">Menú Principal</a></p>
</div>

Archivo views/lista_lineas.erb

<p>Líneas registradas: <%= @lineas.length %></p>
<table>
<tr>
   <th>Clave de línea</th>
   <th>Línea</th>
   <th></th>
   <th></th>
</tr>
<% @lineas.each do |linea| %>
   <tr>
      <td><%= linea.cve_linea %></td>
      <td><%= linea.linea %></td>
      <td><a href="/editlinea/<%= linea.cve_linea %>">[editar]</a></td>
      <td><a href="/borralinea/<%= linea.cve_linea %>">[borrar]</a></td>
   </tr>
<% end %>
</table>
<p><a href="/">Menú Principal</a></p>

Instalar JRE en CentOS 6

agosto 9, 2011

CentOS 6 viene con Firefox 3.6.9, para instalar el plugin de Java en dicho navegador en esta versión de CentOS hay que hacer lo siguiente (básicamente son los pasos que indica la página de Java, sólo que ahi tienen algunos errores, al menos al momento en que estoy escribiendo este post):

Descargar de la página de Java el paquete (archivo RPM)

Cerrar Firefox

Desde la terminal, cambiarse al usuario root

Ir al directorio en donde se desea hacer la instalación y poner ahi el archivo RPM; en mi caso fue el archivo jre-6u26-linux-i586.rpm.bin y lo instalé en /usr/local

Agregar permisos de ejecución al archivo RPM tecleando chmod a+x jre-6u26-linux-i586.rpm.bin

Ejecutar el archivo tecleando ./jre-6u26-linux-i586.rpm.bin

Ir al directorio /usr/lib/firefox-3.6 y crear el directorio plugins

cd /usr/lib/firefox-3.6

mkdir plugins

Ir al directorio plugins y crear ahi un enlace simbólico al archivo libnpjp2.so de la instalación de Java

cd plugins

ln -s /usr/java/jre1.6.0_26/lib/i386/libnpjp2.so

Para comprobar la instalación, abrir Firefox y en la barra de direcciones teclear about:plugins (se muestran los plguins que están instalados y debe aparecer entre ellos el de Java).

Funciones básicas para listas simplemente ligadas en C

agosto 9, 2011

En este post voy a poner un archivo de cabecera que hice para poder crear programas que utilicen listas simplemente ligadas, contiene las funciones básicas y está diseñado para manejar enteros, pero es muy fácil hacer la implementación de un archivo de cabecera que acepte otro tipo de datos (char, etc.).

La estructura básica es la siguiente


typedef struct nodo_s
{
 int dato;
 struct nodo_s *siguiente;
} nodo_t;

Implementé una función para crear un nodo con el valor que contendrá el campo llamado dato, una función para insertar un nodo, para eliminar un nodo, para saber si la lista está vacía y una función que muestra todos los nodos de la lista.


// Archivo listasimple_int.h

#include <stdio.h>
#include <stdlib.h>

typedef struct nodo_s
{
 int dato;
 struct nodo_s *siguiente;
} nodo_t;

typedef nodo_t *ptrNodo;
typedef nodo_t *ptrLista;

/*
Crea un nuevo nodo y en el campo dato almacena el valor que recibe como parámetro
regresa el nodo recien creado
*/
ptrNodo crea_nodo(int valor)
{
 ptrNodo nuevo_nodo = (ptrNodo)malloc(sizeof(nodo_t));
 if (nuevo_nodo != NULL)
    {
     nuevo_nodo->dato = valor;
     nuevo_nodo->siguiente = NULL;
    }

 return nuevo_nodo;
}

/*
Agrega a la lista que recibe como parámetro, un nodo enseguida del nodo que recibe como parámetro
Si el nodo que recibe como parámetro es NULL, significa que se desea insertar el nodo al inicio de la lista
*/
void inserta_despues(ptrLista *lista, ptrNodo nodo, int valor)
{
 ptrNodo nuevo_nodo = crea_nodo(valor);

 if (nodo != NULL)
    {
     /* El apuntador nuevo_nodo->siguiente va a apuntar a la misma dirección a donde apunta
        el apuntador "siguiente" del nodo que recibe como parámetro
     */
     nuevo_nodo->siguiente = nodo->siguiente;
     /* El apuntador "siguiente" del nodo que recibe como parámetro va a apuntar al nodo recien creado
        con esto, el nodo recien creado se ha insertado adelante del nodo que se recibe como parámetro
     */
     nodo->siguiente = nuevo_nodo;
    }
 else
    {
     // Si la lista no está vacía, hace que el apuntador "siguiente" del nuevo nodo apunte al primer elemento de la lista
     if (*lista != NULL)
         nuevo_nodo->siguiente = *lista;
     // Hace que la lista apunte hacia el nuevo nodo para que sea el primer nodo de la lista
     *lista = nuevo_nodo;
    }
}

/*
  Elimina el nodo que se encuentra enseguida del nodo que recibe como parámetro
  Si el nodo que recibe como parámetro es NULL, y la lista no está vacía,
  significa que se desea borrar el primer nodo de la lista
*/
int elimina_despues(ptrLista *lista, ptrNodo nodo)
{
 int x=0;
 ptrNodo borrar_nodo = NULL;

 if (nodo != NULL)
    {
     if (nodo->siguiente != NULL)
        {
         /* El apuntador borrar_nodo va a apuntar a la misma dirección a donde apunta
            el apuntador "siguiente" del nodo que recibe como parámetro
         */
         borrar_nodo = nodo->siguiente;
         /* El apuntador "siguiente" del nodo que recibe como parámetro va a apuntar al nodo que está
            a continuación del que se va a borrar
         */
         nodo->siguiente = borrar_nodo->siguiente;
        }
    }
 else
     {
      // Si la lista no está vacia, significa que quiere borrar el primer nodo de la lista
      if (*lista != NULL)
         {
          borrar_nodo = *lista;
          *lista = borrar_nodo->siguiente;
         }
     }

 /* Si el apuntador "siguiente" del nodo que recibe como parámetro apunta a NULL, significa que el apuntador
    que se recibió como parámetro es el último de la lista, por lo tanto, no hay nodo siguiente
    Otro caso por el cual borrar_nodo puede ser null, es que la lista esté vacía, y también es imposible hacer la eliminación de un nodo
 */
 if (borrar_nodo != NULL)
    {
     x=borrar_nodo->dato;
     free(borrar_nodo);
    }
 else
     printf("Borrado prohibido\n");

 return x;
}

/*
  Regresa 1 si no hay nodos en la lista ligada y cero en caso contrario
  *lista es el apuntador que apunta a la lista ligada
*/
int lista_vacia(ptrLista *lista)
{
 return (*lista == NULL ? 1:0);
}

/*
  Muestra los datos de los nodos
*/
void nodos_lista(ptrNodo nodo)
{
 if (nodo == NULL)
     printf("La lista está vacia\n");
 else
     {
      while (nodo != NULL)
            {
             printf("%d",nodo->dato);
             nodo = nodo->siguiente;
             if (nodo != NULL)
                 printf(" -> ");
            }
      printf("\n");
     }
}

A continuación muestro un programa muy sencillo para que sirva de ejemplo de cómo utilizar este archivo de cabecera, el programa pide al usuario que vaya insertando números enteros y los va insertando en orden en una lista simplemente ligada; cuando el usuario no quiera introducir más números, debe introducir el número cero. Después, el programa da opción a borrar un número de la lista.


//      ordena_listasimple.c
//
//      Hecho por Salomón Rincón Torres <rtmex@yahoo.com>
//

#include <stdio.h>
#include "listasimple_int.h"

int main()
{
 int numero=0, insertado=0, encontrado=0;
 ptrLista lista = NULL;
 ptrNodo nodo = NULL;

do
  {
   insertado = 0;
   printf("Este programa recibe números y los va insertando en orden ascendente en una lista simplemente ligada\n");
   printf("Vaya introduciendo los números (introduzca cero para salir del programa)\n");
   scanf("%d", &numero);

   if (numero!=0)
      {
       if (lista_vacia(&lista) == 1)
           inserta_despues(&lista, NULL, numero);
       else
           {
            nodo = lista;
            if (numero <= nodo->dato)
                // Inserta el número al inicio de la lista
                inserta_despues(&lista, NULL, numero);
            else
                while (nodo != NULL && insertado == 0)
                      {
                       if (numero > nodo->dato)
                          {
                           if (nodo->siguiente != NULL)
                              {
                               if (numero > nodo->siguiente->dato)
                                   nodo = nodo->siguiente;
                               else
                                   {
                                    inserta_despues(&lista, nodo, numero);
                                    insertado = 1;
                                   }
                              } /* if */
                           else
                               {
                                inserta_despues(&lista, nodo, numero);
                                insertado = 1;
                               }
                          } /* if */
                      } /* while */
           } /* else */
       printf("\n\nLos elementos en la lista son los siguientes;\n\n");
       // Muestra los elementos que están en la lista
       nodos_lista(lista);
      } /* if */
  }
while (numero!=0);

printf("\n\nLa lista quedó con los siguientes elementos:\n\n");
// Muestra los elementos que están en la lista
nodos_lista(lista);

if (lista_vacia(&lista) != 1)
   {
    printf("\nDato a borrar\n");
    scanf("%d", &numero);

    nodo = lista;
    if (nodo != NULL)
        // Verifica si el número que se desea borrar es el primero de la lista
        if (nodo->dato == numero)
            elimina_despues(&lista, NULL);
        else
            {
             while (nodo != NULL && encontrado == 0)
                   {
                    if (nodo->siguiente != NULL)
                       {
                        if (nodo->siguiente->dato==numero)
                            encontrado = 1;
                       }
                    if (encontrado == 0)
                        nodo = nodo->siguiente;
                   }
             if (encontrado == 1)
                 elimina_despues(&lista, nodo);
            }

    printf("\n\nLa lista final quedó con los siguientes elementos:\n\n");
    // Muestra los elementos que están en la lista
    nodos_lista(lista);
   }
return 0;
}

Eliminar caracteres de una cadena en Java

junio 12, 2010

La siguiente función recibe dos parámetros de tipo String. La primer cadena es la cadena de la cual queremos remover cualquier caracter que se encuentre en la segunda cadena.

public String EliminaCaracteres(String s_cadena, String s_caracteres)
{
  String nueva_cadena = "";
  Character caracter = null;
  boolean valido = true;

  /* Va recorriendo la cadena s_cadena y copia a la cadena que va a regresar,
     sólo los caracteres que no estén en la cadena s_caracteres */
  for (int i=0; i<s_cadena.length(); i++)
      {
       valido = true;
       for (int j=0; j<s_caracteres.length(); j++)
           {
            caracter = s_caracteres.charAt(j);

            if (s_cadena.charAt(i) == caracter)
               {
                valido = false;
                break;
               }
           }
       if (valido)
           nueva_cadena += s_cadena.charAt(i);
      }

  return nueva_cadena;
}
public String EliminaCaracteres(String s_cadena, String s_caracteres)
{
String nueva_cadena = “”;
Character caracter = null;
boolean valido = true;

/* Va recorriendo la cadena s_cadena y copia a la cadena que va a regresar,
sólo los caracteres que no estén en la cadena s_caracteres */
for (int i=0; i<s_cadena.length(); i++)
{
valido = true;
for (int j=0; j<s_caracteres.length(); j++)
{
caracter = s_caracteres.charAt(j);

if (s_cadena.charAt(i) == caracter)
{
valido = false;
break;
}
}
if (valido)
nueva_cadena += s_cadena.charAt(i);
}

return nueva_cadena;
}

Juego de gato ¿invensible? en Ruby

abril 3, 2010

Recientemente me he interesado en Ruby, así que para ir practicando decidí programar el juego de gato (tres en raya, tic-tac-toe, etc.) utilizando Ruby 1.9.

Debo aclarar que es lo primero que desarrollo en Ruby y seguramente el código se puede optimizar mucho, ya que no dudo que haya cosas que se puedan hacer de una mejor forma, así que desde ahora ofrezco una disculpa a los expertos en Ruby que pudieran leer este post, espero ir mejorando en Ruby conforme vaya adquiriendo más experiencia con este lenguaje de programación.

Aquí el código


#       gato.rb
#
#       Copyright 2010 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.

#!/usr/bin/ruby1.9

tablero = Array.new(3){Array.new(3)}
opcion = "-1"
caracter_usuario = nil
caracter_prog = nil
tira_usuario = 1
casillas_disponibles = (1..9).to_a
gana_programa = 0
opcion_programa = 0

def imprime_tablero(t)
    num = 1
    for i in 0..2 do
        print "#{num}|#{num+1}|#{num+2}   "
        for j in 0..2 do
            if j < 2 then
               if t[j][i]==nil then
                  print " "+"|"
               else
                   print t[j][i].to_s+"|"
               end
            else
                print t[j][i]
            end
        end
        puts
        num = num+3
        if i < 2 then
           5.times { |linea| print "-" }
           3.times { |linea| print " " }
           5.times { |linea| print "-" }
        end
        puts
    end

    return
end

# Tira al azar en una esquina disponible
def tira_esquina(disp)
    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 disp.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, t)
    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 t[j][contador]==caracter then
               tiros_hechos = tiros_hechos+1
            elsif t[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, t)
    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 t[contador][j]==caracter then
               tiros_hechos = tiros_hechos+1
            elsif t[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, t)
    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 t[diagonal_1.at(contador)][contador]==caracter then
             tirosd1 = tirosd1+1
          elsif t[diagonal_1.at(contador)][contador]==nil
                fila1 = contador
          end

          # Checa si hay un tiro a la casilla correspondiente en la diagonal 2
          if t[diagonal_2.at(contador)][contador]==caracter then
             tirosd2 = tirosd2+1
          elsif t[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(t, c_usuario)
    conta = 0
    renglones = Array.new

    for i in 0..2 do
        conta = 0
        for j in 0..2 do
            if t[j][i]==c_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(disp, c_programa, t, c_usuario)
    veces_tiradas = 0
    espacios = 0
    maximo = -1
    columna = -1
    tiro = 0
    gana = 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 disp.include?(elemento)}

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

    if veces_tiradas == 0 then
       # Primero intenta tirar al centro
       if disp.include?(5) then
          tiro = 5
       else
           # Tira al azar en una esquina
           tiro = tira_esquina(disp)
       end
    else
        # Verifica si hay una casilla en la cual el programa gana horizontalmente y si es asi tira en ella
        tiro = gana_horizontal(c_programa, t)
        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(c_programa, t)
           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(c_programa, t)
              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(c_usuario, t)
                 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(c_usuario, t)
                    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(c_usuario, t)
                       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 t[j][i]
                                       when nil then espacios = espacios+1
                                       when c_programa then veces_tiradas = veces_tiradas+1
                                       when c_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 t[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 t[i][j]
                                          when nil then espacios = espacios+1
                                          when c_programa then veces_tiradas = veces_tiradas+1
                                          when c_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 = t[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(disp)
                                      case tiro
                                           when 1 then
                                                if t[2][2] == nil or t[2][2] == c_programa then
                                                   # Verifica si esta esquina se encuentra en un renglón en donde ya haya tirado el usuario
                                                   if renglon_tirou(t, c_usuario).include?(0) then
                                                      esquina_opuesta_ocupada = 0
                                                   else
                                                       esquinas_intentadas.delete(1)
                                                   end
                                                else
                                                    esquinas_intentadas.delete(1)
                                                end
                                           when 3 then
                                                if t[0][2] == nil or t[0][2] == c_programa then
                                                   # Verifica si esta esquina se encuentra en un renglón en donde ya haya tirado el usuario
                                                   if renglon_tirou(t, c_usuario).include?(0) then
                                                      esquina_opuesta_ocupada = 0
                                                   else
                                                       esquinas_intentadas.delete(3)
                                                   end
                                                else
                                                    esquinas_intentadas.delete(3)
                                                end
                                           when 7 then
                                                if t[2][0] == nil or t[2][0] == c_programa then
                                                   # Verifica si esta esquina se encuentra en un renglón en donde ya haya tirado el usuario
                                                   if renglon_tirou(t, c_usuario).include?(2) then
                                                      esquina_opuesta_ocupada = 0
                                                   else
                                                       esquinas_intentadas.delete(7)
                                                   end
                                                else
                                                    esquinas_intentadas.delete(7)
                                                end
                                           when 9 then
                                                if t[0][0] == nil or t[0][0] == c_programa then
                                                   # Verifica si esta esquina se encuentra en un renglón en donde ya haya tirado el usuario
                                                   if renglon_tirou(t, c_usuario).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 disp.include?(tiro)
                                         tiro = rand(9)+1
                                   end
                                end
                             end
                          end
                       end
                    end
                 end
              else
                  gana = 1
              end
           else
               gana = 1
           end
        else
            gana = 1
        end
    end
    return tiro, gana
end

# Mientras que la opción seleccionada por el usuario no esté en el rango de 0 a 2,
# el programa seguirá pidiendo que seleccione un aopción
while not (0..2).include?(opcion.to_i)
   # Pregunta al usuario que caracter desea usar (X ó O)
   puts "Seleccione el caracter que desea usar para jugar o pulse [0] para salir."
   puts "[1] = X, [2] = O"

   opcion = gets.chomp
   if opcion == "1" then
      caracter_usuario = "X"
      caracter_prog = "O"
   elsif opcion == "2"
       caracter_usuario = "O"
       caracter_prog = "X"
   end
end

if opcion != "0" then
   puts
   opcion = "-1"
   # Pregunta al usuario si quiere tirar primero
   while not (0..2).include?(opcion.to_i)
         puts "Indique quien debe tirar primero o pulse [0] para salir."
         puts "[1] = Tira primero Ud., [2] = Tira primero el programa"

         opcion = gets.chomp
         tira_usuario = opcion == "1" ? 1: 0
   end

   while opcion != "0"
         if tira_usuario == 0 and not casillas_disponibles.empty? then
            opcion_programa, gana_programa = tiro_programa(casillas_disponibles, caracter_prog, tablero, caracter_usuario)
            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)
            # Indica que le toca tirar al usuario
            tira_usuario = 1
         end
         puts imprime_tablero(tablero)

         if casillas_disponibles.empty? or gana_programa == 1 then
            # Como ya no hay casillas disponibles para tirar o ya ganó el progama, termina el programa
            if gana_programa == 1 then
               print "Gana el programa!"
            else
                print "No hay ganador!"
            end
            puts
            opcion = "0"
         else
             print "Pulse el numero en donde desea tirar o [0] para salir"
             puts
             # Espera a que el usuario seleccione en donde desea tirar
             opcion = gets.chomp
             if opcion != "0" then
                # Verifica que el usuaruo haya indicado una casilla vacia
                if casillas_disponibles.include?(opcion.to_i) then
                   case opcion
                        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(opcion.to_i)
                   # Indica que le toca tirar al programa
                   tira_usuario = 0
                end
             end
         end
   end
end
#       gato.rb
#
#       Copyright 2010 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.

#!/usr/bin/ruby1.9

tablero = Array.new(3){Array.new(3)}
opcion = “-1″
caracter_usuario = nil
caracter_prog = nil
tira_usuario = 1
casillas_disponibles = (1..9).to_a
gana_programa = 0
opcion_programa = 0

def imprime_tablero(t)
num = 1
for i in 0..2 do
print “#{num}|#{num+1}|#{num+2}   “
for j in 0..2 do
if j < 2 then
if t[j][i]==nil then
print ” “+”|”
else
print t[j][i].to_s+”|”
end
else
print t[j][i]
end
end
puts
num = num+3
if i < 2 then
5.times { |linea| print “-” }
3.times { |linea| print ” ” }
5.times { |linea| print “-” }
end
puts
end

return
end

# Tira al azar en una esquina disponible
def tira_esquina(disp)
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 disp.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, t)
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 t[j][contador]==caracter then
tiros_hechos = tiros_hechos+1
elsif t[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, t)
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 t[contador][j]==caracter then
tiros_hechos = tiros_hechos+1
elsif t[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, t)
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 t[diagonal_1.at(contador)][contador]==caracter then
tirosd1 = tirosd1+1
elsif t[diagonal_1.at(contador)][contador]==nil
fila1 = contador
end

# Checa si hay un tiro a la casilla correspondiente en la diagonal 2
if t[diagonal_2.at(contador)][contador]==caracter then
tirosd2 = tirosd2+1
elsif t[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(t, c_usuario)
conta = 0
renglones = Array.new

for i in 0..2 do
conta = 0
for j in 0..2 do
if t[j][i]==c_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(disp, c_programa, t, c_usuario)
veces_tiradas = 0
espacios = 0
maximo = -1
columna = -1
tiro = 0
gana = 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 disp.include?(elemento)}

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

if veces_tiradas == 0 then
# Primero intenta tirar al centro
if disp.include?(5) then
tiro = 5
else
# Tira al azar en una esquina
tiro = tira_esquina(disp)
end
else
# Verifica si hay una casilla en la cual el programa gana horizontalmente y si es asi tira en ella
tiro = gana_horizontal(c_programa, t)
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(c_programa, t)
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(c_programa, t)
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(c_usuario, t)
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(c_usuario, t)
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(c_usuario, t)
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 t[j][i]
when nil then espacios = espacios+1
when c_programa then veces_tiradas = veces_tiradas+1
when c_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 t[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 t[i][j]
when nil then espacios = espacios+1
when c_programa then veces_tiradas = veces_tiradas+1
when c_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 = t[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(disp)
case tiro
when 1 then
if t[2][2] == nil or t[2][2] == c_programa then
# Verifica si esta esquina se encuentra en un renglón en donde ya haya tirado el usuario
if renglon_tirou(t, c_usuario).include?(0) then
esquina_opuesta_ocupada = 0
else
esquinas_intentadas.delete(1)
end
else
esquinas_intentadas.delete(1)
end
when 3 then
if t[0][2] == nil or t[0][2] == c_programa then
# Verifica si esta esquina se encuentra en un renglón en donde ya haya tirado el usuario
if renglon_tirou(t, c_usuario).include?(0) then
esquina_opuesta_ocupada = 0
else
esquinas_intentadas.delete(3)
end
else
esquinas_intentadas.delete(3)
end
when 7 then
if t[2][0] == nil or t[2][0] == c_programa then
# Verifica si esta esquina se encuentra en un renglón en donde ya haya tirado el usuario
if renglon_tirou(t, c_usuario).include?(2) then
esquina_opuesta_ocupada = 0
else
esquinas_intentadas.delete(7)
end
else
esquinas_intentadas.delete(7)
end
when 9 then
if t[0][0] == nil or t[0][0] == c_programa then
# Verifica si esta esquina se encuentra en un renglón en donde ya haya tirado el usuario
if renglon_tirou(t, c_usuario).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 disp.include?(tiro)
tiro = rand(9)+1
end
end
end
end
end
end
end
else
gana = 1
end
else
gana = 1
end
else
gana = 1
end
end
return tiro, gana
end

# Mientras que la opción seleccionada por el usuario no esté en el rango de 0 a 2,
# el programa seguirá pidiendo que seleccione un aopción
while not (0..2).include?(opcion.to_i)
# Pregunta al usuario que caracter desea usar (X ó O)
puts “Seleccione el caracter que desea usar para jugar o pulse [0] para salir.”
puts “[1] = X, [2] = O”

opcion = gets.chomp
if opcion == “1″ then
caracter_usuario = “X”
caracter_prog = “O”
elsif opcion == “2″
caracter_usuario = “O”
caracter_prog = “X”
end
end

if opcion != “0″ then
puts
opcion = “-1″
# Pregunta al usuario si quiere tirar primero
while not (0..2).include?(opcion.to_i)
puts “Indique quien debe tirar primero o pulse [0] para salir.”
puts “[1] = Tira primero Ud., [2] = Tira primero el programa”

opcion = gets.chomp
tira_usuario = opcion == “1″ ? 1: 0
end

while opcion != “0″
if tira_usuario == 0 and not casillas_disponibles.empty? then
opcion_programa, gana_programa = tiro_programa(casillas_disponibles, caracter_prog, tablero, caracter_usuario)
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)
# Indica que le toca tirar al usuario
tira_usuario = 1
end
puts imprime_tablero(tablero)

if casillas_disponibles.empty? or gana_programa == 1 then
# Como ya no hay casillas disponibles para tirar o ya ganó el progama, termina el programa
if gana_programa == 1 then
print “Gana el programa!”
else
print “No hay ganador!”
end
puts
opcion = “0″
else
print “Pulse el numero en donde desea tirar o [0] para salir”
puts
# Espera a que el usuario seleccione en donde desea tirar
opcion = gets.chomp
if opcion != “0″ then
# Verifica que el usuaruo haya indicado una casilla vacia
if casillas_disponibles.include?(opcion.to_i) then
case opcion
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(opcion.to_i)
# Indica que le toca tirar al programa
tira_usuario = 0
end
end
end
end
end


Seguir

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