21 enero 2023

Condiciones en C: el Gato 2.0

Hace años publiqué una explicación sobre condiciones en C. En aquel tiempo lo hice todo en audio porque tenía un alumno con debilidad visual (casi ciego). Pero el servidor donde subí el audio cerró sin decir agua va y el artículo no sirve de mucho: puedes ver parte de la explicación y el código, pero se pierde mucho. Asi que, como sigo hablando de C con mis alumnos (y seguiré haciéndolo), es hora de volver a explicarlo.

Uno de los pilares de la programación es hacer que el programa tome decisiones.  En otro ámbito, eso es lo que los papás esperamos de nuestros hijos: de que algún día puedan tomar las decisiones correctas.  Pero al tratar con la computadora es más fácil: nunca va a renegar ni a ignorar lo que le diga (como lo suelen hacer mis hijas) porque siempre va a hacer lo que le indique y siempre lo hará igual.  Claro que mis niñas pueden hacer millones de cosas que mi computadora no, y las amo millones de veces más que mi compu, pero esa es harina de otro costal.

Volviendo al mundo de la programación, casi todos los lenguajes tienen la instrucción IF que nos permite definir las instrucciones que se van a ejecutar en caso que se cumpla alguna condición.  C no es la excepción y en el siguiente video te voy a explicar un programa que usa condiciones IF en C (o C++ que para este caso, da lo mismo).

En el video explico un programa ya hecho, un ejemplo que usa IFs. Si no tienes idea de qué es el IF, te sugiero revisar esta página de Nacho Cabanes que lo explica. Luego regresas a este video.

Aquí está el video:

Y aquí está el código del programa:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
/* ***
 * gato.c - programa que juega al gato. Ejemplo del uso de if
 * ***/
 
#include <stdio.h>

int main() {
	// Declaro variables
	char a1,a2,a3,b1,b2,b3,c1,c2,c3;		// Estos son los "huecos" donde se puede jugar
	char jugador;							// Éste es la letra del jugador (X o O)
	int fila,col;							// La fila y columna donde desea jugar
	int gana,jugadas;						// Indica si ganó (1 si sí, 0 si no) y cuantas jugadas se han hecho
	
	// Inicializo el gato
	a1 = a2 = a3 = b1 = b2 = b3 = c1 = c2 = c3 = '#';	// Las posiciones pueden tener # (aún no se ocupa), X u O
	gana = jugadas = 0;						// Todavía no ganan (en 0) y no se han hecho jugadas
	jugador = 'X';							// El primer jugador es el X
	
	// Ciclo que se repite mientras no gana
	while (gana == 0) {
		// Imprimo el gato
		printf("%c | %c | %c\n",a1,b1,c1);
		printf("--+---+--\n");
		printf("%c | %c | %c\n",a2,b2,c2);
		printf("--+---+--\n");
		printf("%c | %c | %c\n",a3,b3,c3);
		
		// Pido coordenadas
		printf("\n\nJugador %c\nFila (1, 2 o 3): ",jugador);
		scanf("%d",&fila);
		fflush(stdin);			// Elimina las teclas que no leyó el scanf anterior (el ENTER)
		printf("Columna (A, B o C): ");
		scanf("%c",&col);
		
		// Guardo la "tirada"
		if (fila == 1) {
			if (col == 'A') {
				a1 = jugador;
			} else if (col == 'B') {
				b1 = jugador;
			} else if (col == 'C') {
				c1 = jugador;
			} else {
				printf("\nError de columna\n");
			}
		} else if (fila == 2) {
			if (col == 'A') {
				a2 = jugador;
			} else if (col == 'B') {
				b2 = jugador;
			} else if (col == 'C') {
				c2 = jugador;
			} else {
				printf("\nError de columna\n");
			}
		} else if (fila == 3) {
			if (col == 'A') {
				a3 = jugador;
			} else if (col == 'B') {
				b3 = jugador;
			} else if (col == 'C') {
				c3 = jugador;
			} else {
				printf("\nError de columna\n");
			}
		} else {
			printf("\nError de fila\n");
		}
		
		// Veo si ya ganaron
		if ((a1 == jugador && b1 == jugador && c1 == jugador) ||
			(a2 == jugador && b2 == jugador && c2 == jugador) ||
			(a3 == jugador && b3 == jugador && c3 == jugador) ||
			(a1 == jugador && a2 == jugador && a3 == jugador) ||
			(b1 == jugador && b2 == jugador && b3 == jugador) ||
			(c1 == jugador && c2 == jugador && c3 == jugador) ||
			(a1 == jugador && b2 == jugador && c3 == jugador) ||
			(a3 == jugador && b2 == jugador && c1 == jugador)) {
				gana = 1;
				printf("Gana el jugador %c\n",jugador);
		} else {
			// Aumento el número de jugadas
			jugadas++;
			// Reviso empate (si llegan a 9 jugadas y nadie ha ganado, es empate)
			if (jugadas == 9) {
				printf("Hubo empate\n");
				gana = 1;		// No ganó, pero quiero que se salga del ciclo
			} else {
				// Si no ganó ni empató, cambio de jugador
				if (jugador == 'X') {
					jugador = 'O';
				} else {
					jugador = 'X';
				}
			}
		}
	}
}

17 enero 2023

C# y números al azar

Hace tiempo escribí como se pueden generar números aleatorios en C++ (que igual aplica para C). Es sencillo y funcional. Sin embargo, en C# tenemos algo que todavía nos simplifica más la vida: la clase Random. Antes de continuar, te recomiendo leer el artículo anterior y cuando hable de código, regresen aquí.

En el otro post se usa el srand((unsigned)time(0)) para incializar el generador de números pseudoaleatorios con el valor del reloj del sistema. Al usar la clase Random, el constructor hace lo mismo. Asi que cuando iniciemos nuestro programa, se crea el objeto de la siguiente manera:

Random x = new Random();

Con eso se crea un objeto (e inicializa el generador de números usando el valor del reloj del sistema). 

Tal ve te preguntes, ¿por qué se tiene que inicializar el generador de números aleatorios y porque es conveninte hacerlo con el reloj del sistema? Si no te lo preguntas y aceptas lo que te dicen sin cuestionar, sáltate los siguientes párrafos hasta que veas código.

Básicamente es porque no existe una manera de generar números al azar porque los cerebros humanos lo pueden hacer, pero no existe un procedimiento, un proceso para hacerlo. Por ejemplo piensa ahorita en un número al azar entre 1 y 10. ¿Por qué elegiste ese número? ¿Que proceso seguiste para determinar ese número? Simplemente se te ocurrió. Pero la computadora necesita que se le diga exactamente cómo hacer algo.

Asi que podemos agradecer a alguna deidad de las matemáticas* que se le ocurrió una ecuación que genera secuencias de números que parecen aleatorios (por eso los llamamos pseudoaleatorios). Pero el problema es que cada vez que lo usas, genera la misma serie. Pero a este señor se le ocurrió ponerle a esta ecuación un valor inicial y la secuencia depende de ese número. Por ejemplo, si lo inicializamos con 5, nos dará una secuencia de números. Si lo inicializamos con 6 nos da otra, etc. Por eso, si lo inicializamos con el reloj del sistema, cada vez que lo usemos nos dará una secuencia diferente.

Pero regresando a lo que estábamos hablando sobre el código, después de crear un objeto Random, generamos números enteros usando el método Next. Me voy a concentrar en números enteros, pero también puedes usar NextDouble para generar números aleatorios de tipo float/double que van entre 0 y 0.99999999, NextBytes para llenar un arreglos con bytes aleatorios, etc.

Asi que, siguiente con el objeto x que hice antes en el ejemplo de crear un objeto de tipo Random, tenemos estas variantes del Next:

  • x.Next() - devuelve un número entero aleatorio positivo.
  • x.Next(10) - devuelve un número entero aleatorio entre 0 y 9.
  • x.Next(1,10) - devuelve un número entero aleatorio entre 1 y 9.

Si quieres saber todo sobre el Random, lee este artículo de Microsoft.

Para ejemplificar el uso, hice esta sencilla app que prueba los tres tipos de Next. Tiene un Label donde muestra el número aleatorio, en un Groupbox hay 3 RadioButtons para seleccionar los tres tipos de Next y Textboxes para poner los números límites, si se seleccionan esas opciones. La app se ve como lo que puesta esta imagen y creo que de ver la interfaz, te puedes dar cuenta de cómo funciona:


El código del Form1.cs (que hace todo) lo tienes aquí a continuación. Lo "especial" sucede en estas líneas:

  • 18 - Declaro el objeto Random (se llama azar)
  • 31 al 71 - El método btnGeneraAzar_Click que se ejecuta cuando presionan el botón y usa los 3 tipos de Next para generar números al azar. Como dice allí, recuerda que int.Parse sirve para convertir un String (lo que está en una caja de texto en mi caso) a int (valor que necesita el Next).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Aleatorio
{
    public partial class Form1 : Form
    {
        // Declaro mi objeto Random que se va a encargar de generar números al azar.
        // Es útil ponerlo como variable de clase porque no quieres crear y destruir objetos
        // a cada rato.
        Random azar = new Random();

        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Llena el label (lblNum) con el número entero al azar que se generó.
        /// Usa los radiobuttons para saber qué versión de Next usar.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnGeneraAzar_Click(object sender, EventArgs e)
        {
            int numAzar = 0;    // Variable donde voy a guardar el número generado

            // Genero número aleatorio
            if (rdoEntero.Checked)
            {
                // Si solo es un entero aleatorio positivo
                numAzar = azar.Next();
            }
            else if (rdoEnteroLimite.Checked)
            {
                // Si es un entero con límite, debo verificar que escribieron el límite
                if (txtLimite.Text.Length > 0)
                {
                    // Nota: int.Parse() convierte un String a un int.
                    numAzar = azar.Next(int.Parse(txtLimite.Text));
                }
                else
                {
                    MessageBox.Show("Hay que escribir un número máximo para esta opción", "C# Azar",
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            else
            {
                // Si no fue ninguno de los anteriores, quiere número al azar en un rango
                if (txtDesde.Text.Length > 0 && txtHasta.Text.Length > 0) 
                {
                    numAzar = azar.Next(int.Parse(txtDesde.Text),int.Parse(txtHasta.Text));
                } 
                else
                {
                    MessageBox.Show("Hay que escribir un número mínimo y máximo para esta opción", 
                        "C# Azar", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }

            // Muestro número
            lblNum.Text = numAzar.ToString();
        }

        /// <summary>
        /// Si este control recibe el Focus, hago que rdoEnteroLimite esté checked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtLimite_Enter(object sender, EventArgs e)
        {
            rdoEnteroLimite.Checked = true;
        }

        /// <summary>
        /// Si este control recibe el Focus, hago que rdoEnteroRango esté checked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtDesde_Enter(object sender, EventArgs e)
        {
            rdoEnteroRango.Checked = true;  
        }

        /// <summary>
        /// Si este control recibe el Focus, hago que rdoEnteroRango esté checked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void txtHasta_Enter(object sender, EventArgs e)
        {
            rdoEnteroRango.Checked = true;
        }

        /// <summary>
        /// Si seleccionó esta opción, muevo el cursor a txtLimite
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void rdoEnteroLimite_Enter(object sender, EventArgs e)
        {
            txtLimite.Focus();
        }

        /// <summary>
        /// Si seleccionó esta opción, muevo el cursor a txtDesde
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void rdoEnteroRango_Enter(object sender, EventArgs e)
        {
            txtDesde.Focus();
        }
    }
}

Si desea descargar el proyecto comprimido en formato 7Zip, haz click aquí.

* Gracias al artículo A History of the Random Number Generator, descubrí que la deidad de matemáticas que nos ayudó fue John von Neumann y lo hizo 1946

El Tony y sus ondas...

Related Posts Plugin for WordPress, Blogger...