Ejercicios en C
La mejor forma de aprender C es resolviendo problemas reales. Acá vas a encontrar ejercicios prácticos que combinan lógica, punteros, bucles y funciones del sistema.
¿Por qué ejercicios?
Leer código no alcanza. La programación se aprende haciendo. Cada ejercicio te fuerza a pensar en bucles, memoria y la lógica de bajo nivel que hace a C único.
Aprendizaje Práctico
Cada ejercicio incluye el resultado visual para que sepas qué tenés que lograr, el código completo y una explicación paso a paso.
Ejercicios disponibles
Cascada de caracteres — Matrix
Recreá el efecto icónico de la lluvia de caracteres verdes usando la consola de Windows.
Próximo ejercicio...
Proximamente.
01. El Caso: Cascada Matrix
El objetivo es recrear el efecto visual de la lluvia de caracteres de Matrix en la consola de Windows. Columnas de caracteres verdes cayendo a diferentes velocidades con un efecto de desvanecimiento.
Abajo podés ver una simulación en vivo del resultado esperado. Tu código en C debería producir algo similar en la consola de Windows.
Lenguaje
C (Windows API)
Dificultad
⭐⭐ Intermedio
Conceptos
Bucles, Arrays, Random, Console API
02. Pistas y Herramientas
Antes de ver la solución, intentá pensarlo por tu cuenta. Acá te dejamos las herramientas (librerías y funciones del sistema de Windows) que te van a hacer falta.
Librerías sugeridas
stdlib.h para números
aleatorios, y windows.h para controlar la posición de la consola.
Funciones que vas a necesitar
Vas a tener que desarrollar una función gotoxy(x, y) usando SetConsoleCursorPosition para ubicar los caracteres en pantalla.
03. Código Resuelto
Una vez que lo hayas intentado, podés revelar la solución acá abajo.
Copialo, compilalo con gcc matrix.c -o matrix y ejecutalo.
Código Censurado
No hagas trampa. Intentá resolverlo primero con las pistas de arriba.
#include <stdio.h> #include <stdlib.h> #include <windows.h> #include <time.h> #define MAX_COLS 300 // Máximo de columnas soportadas HANDLE hConsole; int ANCHO, ALTO; // Se detectan automáticamente // Detecta el tamaño real de la ventana de consola void obtenerTamano() { CONSOLE_SCREEN_BUFFER_INFO info; GetConsoleScreenBufferInfo(hConsole, &info); // Tamaño de la ventana visible ANCHO = info.srWindow.Right - info.srWindow.Left + 1; ALTO = info.srWindow.Bottom - info.srWindow.Top + 1; // FORZAR que el buffer de memoria sea igual al de la ventana // Esto elimina los scrollbars por definición COORD size; size.X = (short)ANCHO; size.Y = (short)ALTO; SetConsoleScreenBufferSize(hConsole, size); // Reducimos ALTO y ANCHO para el dibujo para que nunca toque los bordes ALTO -= 2; ANCHO--; } // Mueve el cursor a la posición (x, y) de la consola void gotoxy(int x, int y) { COORD coord = {x, y}; SetConsoleCursorPosition(hConsole, coord); } // Cambia el color del texto en la consola void setColor(int color) { SetConsoleTextAttribute(hConsole, color); } // Genera un largo de rastro proporcional a ALTO (entre 25% y 75%) int largoAleatorio() { int min = ALTO / 4; int rango = ALTO / 2; if (min < 3) min = 3; if (rango < 3) rango = 3; // Hay una regla matemática mágica: El resto de una división por "N" siempre será un número entre 0 y N-1. return min + rand() % rango; } int main() { int cabeza[MAX_COLS]; int largo[MAX_COLS]; int velocidad[MAX_COLS]; int tick = 0, i; hConsole = GetStdHandle(STD_OUTPUT_HANDLE); // Ocultar el cursor CONSOLE_CURSOR_INFO cursorInfo = {1, FALSE}; SetConsoleCursorInfo(hConsole, &cursorInfo); // Limpiar pantalla al iniciar system("cls"); srand(time(NULL)); // Detectar el tamaño real de la consola obtenerTamano(); if (ALTO <= 0) ALTO = 1; if (ANCHO <= 0) ANCHO = 80; // Limitar a MAX_COLS por seguridad if (ANCHO > MAX_COLS) ANCHO = MAX_COLS; // Inicializar: posiciones negativas = delay antes de aparecer for (i = 0; i < ANCHO; i++) { int divisorIni = (ALTO > 0) ? ALTO : 1; cabeza[i] = -(rand() % divisorIni); largo[i] = largoAleatorio(); velocidad[i] = 1 + rand() % 3; } while (1) { tick++; for (i = 0; i < ANCHO; i++) { // Solo avanzar si toca según la velocidad if (tick % velocidad[i] != 0) continue; int y = cabeza[i]; int cola = y - largo[i]; // 1. CABEZA: blanco brillante if (y >= 0 && y < ALTO) { gotoxy(i, y); setColor(15); printf("%c", 33 + rand() % 90); } // 2. RASTRO: el anterior pasa a verde claro if (y - 1 >= 0 && y - 1 < ALTO) { gotoxy(i, y - 1); setColor(10); printf("%c", 33 + rand() % 90); } // 3. FADE 1: a un tercio pasa a verde oscuro int fade1 = y - (largo[i] / 3); if (fade1 >= 0 && fade1 < ALTO) { gotoxy(i, fade1); setColor(2); printf("%c", 33 + rand() % 90); } // 4. FADE 2: a dos tercios pasa a gris oscuro int fade2 = y - (largo[i] * 2 / 3); if (fade2 >= 0 && fade2 < ALTO) { gotoxy(i, fade2); setColor(8); printf("%c", 33 + rand() % 90); } // 5. BORRAR: la cola desaparece if (cola >= 0 && cola < ALTO) { gotoxy(i, cola); printf(" "); } // Avanzar siempre de a 1 fila cabeza[i]++; // Reiniciar cuando la cola sale de pantalla if (cola >= ALTO) { int divisor = (ALTO / 2 > 0) ? (ALTO / 2) : 1; cabeza[i] = -(rand() % divisor); largo[i] = largoAleatorio(); velocidad[i] = 1 + rand() % 3; } } gotoxy(0, 0); Sleep(30); // ~33 FPS } return 0; }
04. Planteamiento y Explicación
Acá te contamos cómo pensamos la solución paso a paso.
Librerías necesarias
Usamos stdio.h para
printf, stdlib.h para rand() y srand(), windows.h para controlar
la consola (posición del cursor, colores) y time.h para la semilla aleatoria.
⚠️ windows.h es exclusivo de Windows.
En Linux usarías ANSI escape codes.
Funciones auxiliares
obtenerTamano()
usa GetConsoleScreenBufferInfo
para detectar el tamaño real de la consola
(columnas y filas). Así el programa se adapta a cualquier tamaño de
ventana.
largoAleatorio()
genera un largo de rastro proporcional
al ALTO de la consola: entre 25% y 75% de la altura. Esto asegura
que los rastros se vean bien sin importar si la consola tiene 25 o
50 filas.
gotoxy(x, y)
mueve el cursor y
setColor(n)
cambia el color (donde 15 = blanco,
10 = verde,
2 = verde oscuro).
Los arrays: cabeza[], largo[] y velocidad[]
Cada posición del array representa una columna de la consola.
Los arrays se dimensionan con MAX_COLS (300) como tope, pero solo se usan las primeras
ANCHO
> posiciones (el ancho real de la consola).
cabeza[i] guarda la
fila actual de la punta,
largo[i] es proporcional
al ALTO (25-75%), y
velocidad[i] controla
cada cuántos ticks avanza (1 = rápido, 3 = lento). Inicializar con
valores negativos
(hasta -ALTO) crea
un delay proporcional antes de que la columna aparezca.
✨ Hay una regla matemática mágica: El resto de una división por "N" siempre será un número entre 0 y N-1.
El efecto de degradado
Como cada columna siempre avanza de a 1 fila, podemos pintar 4 capas por columna en cada update sin dejar huecos:
Blanco brillante (color 15) — la punta que cae en y.
Verde claro (color 10) — en y-1
(la que era blanca el frame anterior).
Verde oscuro (color 2) — en un tercio del rastro (y - largo/3).
Gris oscuro (color 8) — casi al final del rastro (y - largo*2/3).
Espacio vacío — en y - largo,
la cola que se borra.
Velocidad sin saltar filas
El truco clave: cada columna avanza siempre de a 1 fila, pero NO todas avanzan en cada tick. Usamos
tick % velocidad[i] != 0
para que las columnas "lentas" (velocidad 2-3) se salteen ticks. Así
no quedan huecos blancos.
💡 ¿Por qué no sumar la velocidad a la posición? Si la cabeza salta de fila 5 a fila 8, la fila 5 queda blanca para siempre porque nunca se recolorea a verde. Avanzar de a 1 garantiza que cada posición blanca sea cambiada a verde en el siguiente update.
El reinicio cíclico
Cuando la cola (cabeza
- largo) supera ALTO, significa que todo el rastro ya salió de la pantalla. Ahí
reiniciamos con cabeza = -(rand() % 20) — el valor negativo crea un delay aleatorio
antes de que la nueva cascada aparezca, para que no todas las columnas
se reinicien al mismo tiempo.
Evitando el Scroll (El gran bug)
Si el programa intenta imprimir en la última fila o columna, la terminal de Windows hace scroll automático, rompiendo la animación.
La solución: Primero, restamos ALTO -= 2 y ANCHO-- para dejar un margen de
seguridad. Segundo, NUNCA uses system("mode con..."), ya que desincroniza el tamaño físico del buffer lógico,
causando wraps invisibles que fuerzan el scroll incluso si
crees estar dentro de los límites.
¿Cómo compilar y ejecutar?
$ gcc matrix.c -o matrix
$ ./matrix
Si usás Dev-C++ o Code::Blocks, simplemente creá un nuevo proyecto de tipo "Console Application", pegá el código y presioná F9 para compilar y ejecutar.