added level-1
This commit is contained in:
parent
034fb0dd63
commit
259d4ef6f3
87
level-1/broken_gnl/subject/broken_gnl.c
Normal file
87
level-1/broken_gnl/subject/broken_gnl.c
Normal file
@ -0,0 +1,87 @@
|
||||
#include "broken_gnl.h"
|
||||
|
||||
char *ft_strchr(char *s, int c)
|
||||
{
|
||||
int i = 0;
|
||||
while (s[i] != c)
|
||||
i++;
|
||||
if (s[i] == c)
|
||||
return (s + i);
|
||||
else
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void *ft_memcpy(void *dest, const void *src, size_t n)
|
||||
{
|
||||
while (--n > 0)
|
||||
((char *)dest)[n - 1] = ((char *)src)[n - 1];
|
||||
return (dest);
|
||||
}
|
||||
|
||||
size_t ft_strlen(char *s)
|
||||
{
|
||||
size_t ret = 0;
|
||||
while (*s)
|
||||
{
|
||||
s++;
|
||||
ret++;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
int str_append_mem(char **s1, char *s2, size_t size2)
|
||||
{
|
||||
size_t size1 = ft_strlen(*s1);
|
||||
char *tmp = malloc(size2 + size1 + 1);
|
||||
if (!tmp)
|
||||
return (0);
|
||||
ft_memcpy(tmp, *s1, size1);
|
||||
ft_memcpy(tmp + size1, s2, size2);
|
||||
tmp [size1 + size2] = 0;
|
||||
free(*s1);
|
||||
*s1 = tmp;
|
||||
return (1);
|
||||
}
|
||||
|
||||
int str_append_str(char **s1, char *s2)
|
||||
{
|
||||
return (str_append_mem(s1, s2, ft_strlen(s2)));
|
||||
}
|
||||
|
||||
void *ft_memmove(void *dest, const void *src, size_t n)
|
||||
{
|
||||
if (dest > src)
|
||||
return (ft_memcpy(dest, src, n));
|
||||
else if (dest == src)
|
||||
return (dest);
|
||||
size_t i = ft_strlen((char *)src) - 1;
|
||||
while (i >= 0)
|
||||
{
|
||||
((char *)dest)[i] = ((char *)src)[i];
|
||||
i--;
|
||||
}
|
||||
return (dest);
|
||||
}
|
||||
|
||||
char *get_next_line(int fd)
|
||||
{
|
||||
static char b[BUFFER_SIZE + 1] = "";
|
||||
char *ret = NULL;
|
||||
|
||||
char *tmp = ft_strchr(b, '\n');
|
||||
while (!tmp)
|
||||
{
|
||||
if (!str_append_str(&ret, b))
|
||||
return (NULL);
|
||||
int read_ret = read(fd, b, BUFFER_SIZE);
|
||||
if (read_ret == -1)
|
||||
return (NULL);
|
||||
b[read_ret] = 0;
|
||||
}
|
||||
if (!str_append_mem(&ret, b, tmp - b + 1))
|
||||
{
|
||||
free(ret);
|
||||
return (NULL);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
10
level-1/broken_gnl/subject/broken_gnl.h
Normal file
10
level-1/broken_gnl/subject/broken_gnl.h
Normal file
@ -0,0 +1,10 @@
|
||||
#ifndef GNL
|
||||
# define GNL
|
||||
|
||||
# ifndef BUFFER_SIZE
|
||||
# define BUFFER_SIZE 10
|
||||
# endif
|
||||
|
||||
char *get_next_line(int fd);
|
||||
|
||||
#endif
|
||||
158
level-1/filter/filter.c
Normal file
158
level-1/filter/filter.c
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* EJERCICIO: FILTER
|
||||
*
|
||||
* DESCRIPCIÓN:
|
||||
* Leer de stdin y escribir a stdout, pero reemplazando todas las ocurrencias
|
||||
* de una cadena dada por asteriscos (*) de la misma longitud.
|
||||
*
|
||||
* CONCEPTOS CLAVE:
|
||||
* 1. LECTURA DINÁMICA: read() con buffer variable
|
||||
* 2. BÚSQUEDA DE PATRONES: strstr() o memmem()
|
||||
* 3. GESTIÓN DE MEMORIA: realloc() para buffer dinámico
|
||||
* 4. MANEJO DE ERRORES: perror() para errores de sistema
|
||||
*
|
||||
* ALGORITMO:
|
||||
* 1. Leer todo el contenido de stdin en un buffer dinámico
|
||||
* 2. Buscar todas las ocurrencias del patrón
|
||||
* 3. Reemplazar cada ocurrencia con asteriscos
|
||||
* 4. Escribir el resultado a stdout
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE // Para memmem()
|
||||
#ifndef BUFFER_SIZE
|
||||
# define BUFFER_SIZE 42
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
// Función para encontrar y reemplazar todas las ocurrencias
|
||||
void ft_filter(char *buffer, const char *target)
|
||||
{
|
||||
int i = 0;
|
||||
int target_len = strlen(target);
|
||||
int j, k;
|
||||
|
||||
/*
|
||||
* ALGORITMO DE BÚSQUEDA Y REEMPLAZO:
|
||||
* - Recorrer el buffer carácter por carácter
|
||||
* - En cada posición, verificar si coincide con el patrón
|
||||
* - Si coincide, escribir asteriscos y saltar la longitud del patrón
|
||||
* - Si no coincide, escribir el carácter original
|
||||
*/
|
||||
|
||||
while (buffer[i])
|
||||
{
|
||||
j = 0;
|
||||
// Verificar si hay coincidencia desde la posición actual
|
||||
while (target[j] && (buffer[i + j] == target[j]))
|
||||
j++;
|
||||
|
||||
if (j == target_len) // Coincidencia completa encontrada
|
||||
{
|
||||
// Escribir asteriscos en lugar del patrón
|
||||
k = 0;
|
||||
while (k < target_len)
|
||||
{
|
||||
write(1, "*", 1);
|
||||
k++;
|
||||
}
|
||||
i += target_len; // Saltar el patrón completo
|
||||
}
|
||||
else
|
||||
{
|
||||
// No hay coincidencia, escribir carácter original
|
||||
write(1, &buffer[i], 1);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/*
|
||||
* VALIDACIÓN DE ARGUMENTOS:
|
||||
* - Debe haber exactamente 1 argumento
|
||||
* - El argumento no puede estar vacío
|
||||
*/
|
||||
if (argc != 2 || argv[1][0] == '\0')
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* LECTURA DINÁMICA DE STDIN:
|
||||
* - Usar buffer temporal para leer chunks
|
||||
* - Usar realloc() para expandir el buffer principal
|
||||
* - Mantener seguimiento del total leído
|
||||
*/
|
||||
char temp[BUFFER_SIZE];
|
||||
char *result = NULL;
|
||||
char *buffer;
|
||||
int total_read = 0;
|
||||
ssize_t bytes;
|
||||
|
||||
// Leer de stdin hasta EOF
|
||||
while ((bytes = read(0, temp, BUFFER_SIZE)) > 0)
|
||||
{
|
||||
// Expandir el buffer principal para acomodar los nuevos datos
|
||||
buffer = realloc(result, total_read + bytes + 1);
|
||||
if (!buffer)
|
||||
{
|
||||
free(result);
|
||||
perror("realloc");
|
||||
return 1;
|
||||
}
|
||||
|
||||
result = buffer;
|
||||
|
||||
// Copiar los nuevos datos al buffer principal
|
||||
memmove(result + total_read, temp, bytes);
|
||||
total_read += bytes;
|
||||
result[total_read] = '\0'; // Asegurar terminación
|
||||
}
|
||||
|
||||
// Verificar errores de lectura
|
||||
if (bytes < 0)
|
||||
{
|
||||
perror("read");
|
||||
free(result);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Si no se leyó nada, salir sin error
|
||||
if (!result)
|
||||
return 0;
|
||||
|
||||
// Procesar el buffer y aplicar el filtro
|
||||
ft_filter(result, argv[1]);
|
||||
|
||||
// Liberar memoria
|
||||
free(result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* PUNTOS CLAVE PARA EL EXAMEN:
|
||||
*
|
||||
* 1. GESTIÓN DE MEMORIA:
|
||||
* - Siempre verificar el retorno de malloc/realloc
|
||||
* - Liberar memoria en caso de error
|
||||
* - Usar memmove() en lugar de memcpy() para solapamientos
|
||||
*
|
||||
* 2. MANEJO DE ERRORES:
|
||||
* - Usar perror() para errores de sistema
|
||||
* - Retornar códigos de error apropiados
|
||||
* - Validar argumentos antes de usarlos
|
||||
*
|
||||
* 3. LECTURA DINÁMICA:
|
||||
* - El buffer puede llenarse con cualquier cantidad de datos
|
||||
* - Usar realloc() para expandir según sea necesario
|
||||
* - Mantener un terminador nulo válido
|
||||
*
|
||||
* 4. ALGORITMO EFICIENTE:
|
||||
* - Búsqueda simple carácter por carácter
|
||||
* - Evitar usar funciones no permitidas
|
||||
* - Escribir directamente a stdout sin almacenar el resultado
|
||||
*/
|
||||
52
level-1/filter/subject.txt
Normal file
52
level-1/filter/subject.txt
Normal file
@ -0,0 +1,52 @@
|
||||
Assignment name: filter
|
||||
Expected files: filter.c
|
||||
Allowed functions: read, write, strlen, memmem, memmove, malloc, calloc,
|
||||
realloc, free, printf, fprintf, stdout, stderr, perror
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Write a program that will take one and only one argument.
|
||||
|
||||
Your program will then read from stdin and write all the content read in stdout
|
||||
except that every occurrence of s must be replaced by '*' (as many as the length
|
||||
of s). Your program will be tested with random buffer sizes, using a custom read
|
||||
function. Therefore the buffer being set in your program will be filled with a
|
||||
different number of chars each new call.
|
||||
|
||||
|
||||
For example:
|
||||
|
||||
./filter bonjour
|
||||
will behave in the same way as:
|
||||
sed 's/bonjour/*******/g'
|
||||
|
||||
./filter abc
|
||||
will behave in the same way as:
|
||||
sed's/abc/***/g'
|
||||
|
||||
More generally your program must be the equivalent of the shell script filter.sh
|
||||
present in this directory (you can compare your program with it).
|
||||
|
||||
In case of error during a read or a malloc, you must write "Error: " followed by
|
||||
the error message in stderr and return 1.
|
||||
|
||||
If the program is called without arguments or with an empty argument or with multiple
|
||||
arguments, it must return 1.
|
||||
|
||||
For example this should work:
|
||||
|
||||
$> echo 'abcdefaaaabcdeabcabcdabc' | ./filter abc | cat -e
|
||||
***defaaa***de******d***$
|
||||
$> echo 'ababcabababc' | ./filter ababc | cat -e
|
||||
*****ab*****$
|
||||
$>
|
||||
|
||||
NOTES:
|
||||
memmem includes:
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
|
||||
perror includes:
|
||||
#include <errno.h>
|
||||
|
||||
read includes:
|
||||
#include <fcntl.h>
|
||||
323
level-1/scanf/ft_scanf
Normal file
323
level-1/scanf/ft_scanf
Normal file
@ -0,0 +1,323 @@
|
||||
/*
|
||||
* EJERCICIO: FT_SCANF
|
||||
*
|
||||
* DESCRIPCIÓN:
|
||||
* Implementar una versión simplificada de scanf que maneje solo %s, %d y %c.
|
||||
*
|
||||
* CONCEPTOS CLAVE:
|
||||
* 1. ARGUMENTOS VARIABLES: va_list, va_start, va_arg, va_end
|
||||
* 2. PARSING DE FORMATO: Analizar string de formato carácter por carácter
|
||||
* 3. LECTURA DE ARCHIVO: fgetc(), ungetc() para control de flujo
|
||||
* 4. CONVERSIONES: Convertir strings a números, manejar espacios en blanco
|
||||
*
|
||||
* FORMATO SOPORTADO:
|
||||
* - %s: string (hasta el primer espacio en blanco)
|
||||
* - %d: entero decimal (con signo opcional)
|
||||
* - %c: un solo carácter
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
|
||||
// Función para saltar espacios en blanco en el stream
|
||||
int match_space(FILE *f)
|
||||
{
|
||||
/*
|
||||
* MANEJO DE ESPACIOS EN BLANCO:
|
||||
* - Leer caracteres mientras sean espacios
|
||||
* - Devolver el primer carácter no-espacio al stream
|
||||
* - Retornar -1 en caso de error
|
||||
*/
|
||||
int ch = fgetc(f);
|
||||
if (ch == EOF && ferror(f))
|
||||
return -1;
|
||||
|
||||
while (ch != EOF)
|
||||
{
|
||||
if (!isspace(ch))
|
||||
{
|
||||
ungetc(ch, f); // Devolver carácter no-espacio
|
||||
break;
|
||||
}
|
||||
ch = fgetc(f);
|
||||
}
|
||||
|
||||
if (ferror(f))
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Función para verificar un carácter específico
|
||||
int match_char(FILE *f, char c)
|
||||
{
|
||||
/*
|
||||
* COINCIDENCIA DE CARACTERES LITERALES:
|
||||
* - Leer un carácter del stream
|
||||
* - Verificar si coincide con el esperado
|
||||
* - Devolver al stream si no coincide
|
||||
*/
|
||||
int ch = fgetc(f);
|
||||
if (ch == c)
|
||||
return 1;
|
||||
if (ch != EOF)
|
||||
ungetc(ch, f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Función para leer un carácter (%c)
|
||||
int scan_char(FILE *f, va_list ap)
|
||||
{
|
||||
/*
|
||||
* CONVERSIÓN %c:
|
||||
* - Leer exactamente un carácter
|
||||
* - No saltar espacios en blanco
|
||||
* - Almacenar en el puntero proporcionado
|
||||
*/
|
||||
int ch = fgetc(f);
|
||||
char *cp = va_arg(ap, char *);
|
||||
|
||||
if (ch == EOF)
|
||||
return -1;
|
||||
|
||||
*cp = (char)ch;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Función para leer un entero (%d)
|
||||
int scan_int(FILE *f, va_list ap)
|
||||
{
|
||||
/*
|
||||
* CONVERSIÓN %d:
|
||||
* - Saltar espacios en blanco iniciales
|
||||
* - Leer signo opcional (+/-)
|
||||
* - Leer dígitos y construir el número
|
||||
* - Devolver último carácter no-dígito al stream
|
||||
*/
|
||||
int sign = 1;
|
||||
int value = 0;
|
||||
int ch = fgetc(f);
|
||||
int *ip = va_arg(ap, int *);
|
||||
int count = 0;
|
||||
|
||||
if (ch == EOF)
|
||||
return -1;
|
||||
|
||||
// Saltar espacios en blanco
|
||||
while (isspace(ch))
|
||||
ch = fgetc(f);
|
||||
|
||||
// Manejar signo
|
||||
if (ch == '-')
|
||||
{
|
||||
sign = -1;
|
||||
ch = fgetc(f);
|
||||
}
|
||||
else if (ch == '+')
|
||||
{
|
||||
ch = fgetc(f);
|
||||
}
|
||||
|
||||
// Verificar que el primer carácter sea un dígito
|
||||
if (!isdigit(ch))
|
||||
{
|
||||
ungetc(ch, f);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Leer dígitos y construir el número
|
||||
while (isdigit(ch))
|
||||
{
|
||||
value = value * 10 + (ch - '0');
|
||||
count++;
|
||||
ch = fgetc(f);
|
||||
}
|
||||
|
||||
// Devolver último carácter no-dígito
|
||||
if (ch != EOF)
|
||||
ungetc(ch, f);
|
||||
|
||||
if (count == 0)
|
||||
return -1;
|
||||
|
||||
*ip = value * sign;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Función para leer un string (%s)
|
||||
int scan_string(FILE *f, va_list ap)
|
||||
{
|
||||
/*
|
||||
* CONVERSIÓN %s:
|
||||
* - Saltar espacios en blanco iniciales
|
||||
* - Leer caracteres hasta encontrar espacio en blanco
|
||||
* - Terminar el string con '\0'
|
||||
* - Devolver último carácter de espacio al stream
|
||||
*/
|
||||
int ch = fgetc(f);
|
||||
char *sp = va_arg(ap, char *);
|
||||
int i = 0;
|
||||
|
||||
// Saltar espacios en blanco iniciales
|
||||
while (ch != EOF && isspace(ch))
|
||||
ch = fgetc(f);
|
||||
|
||||
if (ch == EOF)
|
||||
return -1;
|
||||
|
||||
// Leer caracteres hasta espacio en blanco
|
||||
do
|
||||
{
|
||||
sp[i] = ch;
|
||||
i++;
|
||||
ch = fgetc(f);
|
||||
} while (ch != EOF && !isspace(ch));
|
||||
|
||||
sp[i] = '\0'; // Terminar string
|
||||
|
||||
// Devolver carácter de espacio al stream
|
||||
if (ch != EOF)
|
||||
ungetc(ch, f);
|
||||
|
||||
if (i == 0)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Función para manejar conversiones de formato
|
||||
int match_conv(FILE *f, const char **format, va_list ap)
|
||||
{
|
||||
/*
|
||||
* DISPATCHER DE CONVERSIONES:
|
||||
* - Analizar el carácter de conversión
|
||||
* - Llamar a la función apropiada
|
||||
* - Manejar espacios para %d y %s automáticamente
|
||||
*/
|
||||
switch (**format)
|
||||
{
|
||||
case 'c':
|
||||
return scan_char(f, ap);
|
||||
case 'd':
|
||||
match_space(f); // %d salta espacios automáticamente
|
||||
return scan_int(f, ap);
|
||||
case 's':
|
||||
match_space(f); // %s salta espacios automáticamente
|
||||
return scan_string(f, ap);
|
||||
case '\0':
|
||||
return -1;
|
||||
default:
|
||||
return -1; // Conversión no soportada
|
||||
}
|
||||
}
|
||||
|
||||
// Función principal de scanf
|
||||
int ft_vfscanf(FILE *f, const char *format, va_list ap)
|
||||
{
|
||||
/*
|
||||
* LÓGICA PRINCIPAL DE SCANF:
|
||||
* - Analizar string de formato carácter por carácter
|
||||
* - Manejar caracteres literales y conversiones (%)
|
||||
* - Contar conversiones exitosas
|
||||
* - Parar en el primer error
|
||||
*/
|
||||
int nconv = 0; // Número de conversiones exitosas
|
||||
|
||||
// Verificar que hay datos disponibles
|
||||
int c = fgetc(f);
|
||||
if (c == EOF)
|
||||
return EOF;
|
||||
ungetc(c, f);
|
||||
|
||||
while (*format)
|
||||
{
|
||||
if (*format == '%')
|
||||
{
|
||||
// Conversión encontrada
|
||||
format++;
|
||||
if (match_conv(f, &format, ap) != 1)
|
||||
break; // Error en conversión
|
||||
else
|
||||
nconv++; // Conversión exitosa
|
||||
}
|
||||
else if (isspace(*format))
|
||||
{
|
||||
// Espacio en blanco en formato: saltar espacios en input
|
||||
if (match_space(f) == -1)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Carácter literal: debe coincidir exactamente
|
||||
if (match_char(f, *format) != 1)
|
||||
break;
|
||||
}
|
||||
format++;
|
||||
}
|
||||
|
||||
// Verificar errores de archivo
|
||||
if (ferror(f))
|
||||
return EOF;
|
||||
|
||||
return nconv; // Retornar número de conversiones exitosas
|
||||
}
|
||||
|
||||
// Función wrapper para scanf estándar
|
||||
int ft_scanf(const char *format, ...)
|
||||
{
|
||||
/*
|
||||
* WRAPPER PARA ARGUMENTOS VARIABLES:
|
||||
* - Inicializar va_list
|
||||
* - Llamar a la función principal con stdin
|
||||
* - Limpiar va_list
|
||||
*/
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
int ret = ft_vfscanf(stdin, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* EJEMPLO DE USO:
|
||||
*
|
||||
* int main(void)
|
||||
* {
|
||||
* int x;
|
||||
* char str[100];
|
||||
* char c;
|
||||
*
|
||||
* // Leer: número, espacio, string, espacio, carácter
|
||||
* int converted = ft_scanf("%d %s %c", &x, str, &c);
|
||||
*
|
||||
* printf("Convertidos: %d\n", converted);
|
||||
* printf("Número: %d, String: %s, Carácter: %c\n", x, str, c);
|
||||
*
|
||||
* return 0;
|
||||
* }
|
||||
*/
|
||||
|
||||
/*
|
||||
* PUNTOS CLAVE PARA EL EXAMEN:
|
||||
*
|
||||
* 1. ARGUMENTOS VARIABLES:
|
||||
* - va_start(ap, last_param) para inicializar
|
||||
* - va_arg(ap, type) para obtener siguiente argumento
|
||||
* - va_end(ap) para limpiar
|
||||
*
|
||||
* 2. CONTROL DE FLUJO DE ARCHIVO:
|
||||
* - fgetc() para leer carácter
|
||||
* - ungetc() para devolver carácter al stream
|
||||
* - ferror() para verificar errores
|
||||
*
|
||||
* 3. MANEJO DE ESPACIOS:
|
||||
* - %c NO salta espacios en blanco
|
||||
* - %d y %s SÍ saltan espacios en blanco
|
||||
* - Espacios en formato coinciden con cualquier whitespace
|
||||
*
|
||||
* 4. VALOR DE RETORNO:
|
||||
* - Número de conversiones exitosas
|
||||
* - EOF si error de archivo o EOF antes de conversiones
|
||||
* - Parar en primera conversión fallida
|
||||
*/
|
||||
120
level-1/scanf/subject.txt
Normal file
120
level-1/scanf/subject.txt
Normal file
@ -0,0 +1,120 @@
|
||||
Assignment name : ft_scanf
|
||||
Expected files : ft_scanf.c
|
||||
Allowed functions: fgetc, ungetc, ferror, feof, isspace, isdigit, stdin, va_start, va_arg, va_copy, va_end
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
||||
Write a function named `ft_scanf` that will mimic the real scanf with the following constraints:
|
||||
|
||||
- It will manage only the following conversions: s, d, and c
|
||||
- You don't have to handle the options *, m and '
|
||||
- You don't have to handle the maximum field width
|
||||
- You don't have to handle the types modifier characters (h, hh, l, etc.)
|
||||
- You don't have to handle the conversions beginning with %n$
|
||||
|
||||
Your function must be declared as follows:
|
||||
|
||||
int ft_scanf(const char *, ... );
|
||||
|
||||
You will find in this directory a file containing a part of the code you will need, you just have to complete it.
|
||||
|
||||
To test your program compare your results with the real scanf.
|
||||
|
||||
Hint : You may need to read the man of scanf.
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
int match_space(FILE *f)
|
||||
{
|
||||
// You may insert code here
|
||||
return (0);
|
||||
}
|
||||
|
||||
int match_char(FILE *f, char c)
|
||||
{
|
||||
// You may insert code here
|
||||
return (0);
|
||||
}
|
||||
|
||||
int scan_char(FILE *f, va_list ap)
|
||||
{
|
||||
// You may insert code here
|
||||
return (0);
|
||||
}
|
||||
|
||||
int scan_int(FILE *f, va_list ap)
|
||||
{
|
||||
// You may insert code here
|
||||
return (0);
|
||||
}
|
||||
|
||||
int scan_string(FILE *f, va_list ap)
|
||||
{
|
||||
// You may insert code here
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
int match_conv(FILE *f, const char **format, va_list ap)
|
||||
{
|
||||
switch (**format)
|
||||
{
|
||||
case 'c':
|
||||
return scan_char(f, ap);
|
||||
case 'd':
|
||||
match_space(f);
|
||||
return scan_int(f, ap);
|
||||
case 's':
|
||||
match_space(f);
|
||||
return scan_string(f, ap);
|
||||
case EOF:
|
||||
return -1;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int ft_vfscanf(FILE *f, const char *format, va_list ap)
|
||||
{
|
||||
int nconv = 0;
|
||||
|
||||
int c = fgetc(f);
|
||||
if (c == EOF)
|
||||
return EOF;
|
||||
ungetc(c, f);
|
||||
|
||||
while (*format)
|
||||
{
|
||||
if (*format == '%')
|
||||
{
|
||||
format++;
|
||||
if (match_conv(f, &format, ap) != 1)
|
||||
break;
|
||||
else
|
||||
nconv++;
|
||||
}
|
||||
else if (isspace(*format))
|
||||
{
|
||||
if (match_space(f) == -1)
|
||||
break;
|
||||
}
|
||||
else if (match_char(f, *format) != 1)
|
||||
break;
|
||||
format++;
|
||||
}
|
||||
|
||||
if (ferror(f))
|
||||
return EOF;
|
||||
return nconv;
|
||||
}
|
||||
|
||||
|
||||
int ft_scanf(const char *format, ...)
|
||||
{
|
||||
// ...
|
||||
int ret = ft_vfscanf(stdin, format, ap);
|
||||
// ...
|
||||
return ret;
|
||||
}
|
||||
74
level-2/n_queens/Makefile
Normal file
74
level-2/n_queens/Makefile
Normal file
@ -0,0 +1,74 @@
|
||||
# Program name
|
||||
NAME = n_queens
|
||||
|
||||
# Compiler and flags
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -Werror
|
||||
|
||||
# Source files
|
||||
SRCS = n_queens.c
|
||||
|
||||
# Object files
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
|
||||
# Header files
|
||||
HEADERS = n_queens.h
|
||||
|
||||
# Colors for output
|
||||
GREEN = \033[0;32m
|
||||
RED = \033[0;31m
|
||||
RESET = \033[0m
|
||||
|
||||
# Main rule
|
||||
all: $(NAME)
|
||||
|
||||
# Compile the program
|
||||
$(NAME): $(OBJS)
|
||||
@echo "$(GREEN)Compiling $(NAME)...$(RESET)"
|
||||
@$(CC) $(CFLAGS) $(OBJS) -o $(NAME)
|
||||
@echo "$(GREEN)✓ $(NAME) compiled successfully!$(RESET)"
|
||||
|
||||
# Compile object files
|
||||
%.o: %.c $(HEADERS)
|
||||
@echo "Compiling $<..."
|
||||
@$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# Clean object files
|
||||
clean:
|
||||
@echo "$(RED)Cleaning object files...$(RESET)"
|
||||
@rm -f $(OBJS)
|
||||
@echo "$(RED)✓ Object files cleaned!$(RESET)"
|
||||
|
||||
# Clean everything
|
||||
fclean: clean
|
||||
@echo "$(RED)Cleaning executable...$(RESET)"
|
||||
@rm -f $(NAME)
|
||||
@echo "$(RED)✓ Everything cleaned!$(RESET)"
|
||||
|
||||
# Rebuild everything
|
||||
re: fclean all
|
||||
|
||||
# Test the program with different values
|
||||
test: $(NAME)
|
||||
@echo "$(GREEN)Testing n_queens program:$(RESET)"
|
||||
@echo "\n$(GREEN)Testing with n=1:$(RESET)"
|
||||
@./$(NAME) 1
|
||||
@echo "\n$(GREEN)Testing with n=2 (no solutions):$(RESET)"
|
||||
@./$(NAME) 2 || echo "No solutions found"
|
||||
@echo "\n$(GREEN)Testing with n=4:$(RESET)"
|
||||
@./$(NAME) 4
|
||||
@echo "\n$(GREEN)Testing with n=8 (counting solutions):$(RESET)"
|
||||
@echo "Number of solutions for n=8: $$(./$(NAME) 8 | wc -l)"
|
||||
|
||||
# Display help
|
||||
help:
|
||||
@echo "$(GREEN)Available commands:$(RESET)"
|
||||
@echo " make - Compile the program"
|
||||
@echo " make clean - Remove object files"
|
||||
@echo " make fclean - Remove all generated files"
|
||||
@echo " make re - Rebuild everything"
|
||||
@echo " make test - Run tests with different values"
|
||||
@echo " make help - Show this help message"
|
||||
|
||||
# Declare phony targets
|
||||
.PHONY: all clean fclean re test help
|
||||
252
level-2/n_queens/README.md
Normal file
252
level-2/n_queens/README.md
Normal file
@ -0,0 +1,252 @@
|
||||
# N-Queens Problem - Simple Explanation 👑
|
||||
|
||||
## What is the N-Queens Problem? 🤔
|
||||
|
||||
Imagine you have a chessboard and some queens (the most powerful pieces in chess). The N-Queens problem is like a puzzle where you need to place N queens on an N×N chessboard so that **none of them can attack each other**.
|
||||
|
||||
### What's a Queen in Chess? ♛
|
||||
|
||||
A queen is the strongest piece in chess. She can move:
|
||||
- **Horizontally** (left and right) ←→
|
||||
- **Vertically** (up and down) ↕️
|
||||
- **Diagonally** (in any diagonal direction) ↗️↖️↙️↘️
|
||||
|
||||
She can move as many squares as she wants in these directions!
|
||||
|
||||
## The Challenge 🎯
|
||||
|
||||
Let's say we want to solve the **4-Queens problem** (N=4):
|
||||
- We have a 4×4 chessboard (16 squares total)
|
||||
- We need to place 4 queens
|
||||
- **NO queen can attack any other queen**
|
||||
|
||||
### Example: What Does "Attack" Mean?
|
||||
|
||||
If we put a queen at position (0,0) - that's the top-left corner - she can attack:
|
||||
|
||||
```
|
||||
Q . . . ← Queen here attacks all positions marked with X
|
||||
X . . .
|
||||
X . . .
|
||||
X . . .
|
||||
```
|
||||
|
||||
```
|
||||
Q X X X ← Queen attacks horizontally
|
||||
. . . .
|
||||
. . . .
|
||||
. . . .
|
||||
```
|
||||
|
||||
```
|
||||
Q . . . ← Queen attacks diagonally
|
||||
. X . .
|
||||
. . X .
|
||||
. . . X
|
||||
```
|
||||
|
||||
So we can't put any other queen in any of those X positions!
|
||||
|
||||
## How Our Program Works 🖥️
|
||||
|
||||
### Step 1: Understanding the Input
|
||||
When you run our program like this:
|
||||
```bash
|
||||
./n_queens 4
|
||||
```
|
||||
|
||||
The number `4` means "solve the 4-Queens problem" (4×4 board with 4 queens).
|
||||
|
||||
### Step 2: How We Represent the Solution
|
||||
|
||||
Instead of drawing the whole board, we use a clever trick! Since we know:
|
||||
- There must be exactly ONE queen in each row
|
||||
- There must be exactly ONE queen in each column
|
||||
|
||||
We can represent a solution as a list of numbers:
|
||||
```
|
||||
1 3 0 2
|
||||
```
|
||||
|
||||
This means:
|
||||
- **Row 0**: Queen is in column 1
|
||||
- **Row 1**: Queen is in column 3
|
||||
- **Row 2**: Queen is in column 0
|
||||
- **Row 3**: Queen is in column 2
|
||||
|
||||
### Step 3: Visualizing the Solution
|
||||
|
||||
If we draw this on a 4×4 board:
|
||||
|
||||
```
|
||||
. Q . . ← Row 0, Column 1
|
||||
. . . Q ← Row 1, Column 3
|
||||
Q . . . ← Row 2, Column 0
|
||||
. . Q . ← Row 3, Column 2
|
||||
```
|
||||
|
||||
Let's check: Can any queen attack another?
|
||||
- Queen at (0,1): Can't attack any other queen ✅
|
||||
- Queen at (1,3): Can't attack any other queen ✅
|
||||
- Queen at (2,0): Can't attack any other queen ✅
|
||||
- Queen at (3,2): Can't attack any other queen ✅
|
||||
|
||||
Perfect! This is a valid solution!
|
||||
|
||||
## How Our Code Works 🔧
|
||||
|
||||
### The Main Function (`main`)
|
||||
|
||||
```c
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int n;
|
||||
int *queens;
|
||||
|
||||
if (argc != 2) // Check if user gave us exactly one number
|
||||
return (1);
|
||||
n = atoi(argv[1]); // Convert the text "4" to number 4
|
||||
if (n <= 0) // Make sure the number is positive
|
||||
return (1);
|
||||
queens = (int *)malloc(sizeof(int) * n); // Create space to store queen positions
|
||||
if (!queens) // Check if we got the memory
|
||||
return (1);
|
||||
solve_nqueens(queens, 0, n); // Start solving from row 0
|
||||
free(queens); // Clean up memory
|
||||
return (0);
|
||||
}
|
||||
```
|
||||
|
||||
**What this does in simple terms:**
|
||||
1. "Did the user give me a number?"
|
||||
2. "Is it a good number (positive)?"
|
||||
3. "Let me create space to remember where I put the queens"
|
||||
4. "Now let me solve the puzzle!"
|
||||
5. "Clean up when I'm done"
|
||||
|
||||
### The Solver Function (`solve_nqueens`)
|
||||
|
||||
This is the "brain" of our program. It uses a technique called **backtracking**:
|
||||
|
||||
```c
|
||||
void solve_nqueens(int *queens, int row, int n)
|
||||
{
|
||||
int col;
|
||||
|
||||
if (row == n) // Did I place all queens?
|
||||
{
|
||||
print_solution(queens, n); // Yes! Print this solution
|
||||
return ;
|
||||
}
|
||||
col = 0;
|
||||
while (col < n) // Try each column in this row
|
||||
{
|
||||
if (is_safe(queens, row, col, n)) // Can I put a queen here?
|
||||
{
|
||||
queens[row] = col; // Yes! Put the queen here
|
||||
solve_nqueens(queens, row + 1, n); // Try the next row
|
||||
}
|
||||
col++;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Think of it like this:**
|
||||
1. "Am I done placing all queens?" → If yes, print the solution!
|
||||
2. "If not, let me try putting a queen in each column of this row"
|
||||
3. "For each column, ask: Is it safe to put a queen here?"
|
||||
4. "If safe, put the queen there and try to solve the next row"
|
||||
|
||||
### The Safety Check (`is_safe`)
|
||||
|
||||
This function checks if a queen position is safe:
|
||||
|
||||
```c
|
||||
int is_safe(int *queens, int row, int col, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
while (i < row) // Check all previously placed queens
|
||||
{
|
||||
if (queens[i] == col) // Same column?
|
||||
return (0); // Not safe!
|
||||
if (queens[i] - i == col - row) // Same diagonal (\)?
|
||||
return (0); // Not safe!
|
||||
if (queens[i] + i == col + row) // Same diagonal (/)?
|
||||
return (0); // Not safe!
|
||||
i++;
|
||||
}
|
||||
return (1); // Safe to place queen here!
|
||||
}
|
||||
```
|
||||
|
||||
**The three checks:**
|
||||
|
||||
1. **Same column**: `queens[i] == col`
|
||||
- "Is there already a queen in this column?"
|
||||
|
||||
2. **Diagonal 1**: `queens[i] - i == col - row`
|
||||
- "Is there a queen on the same diagonal going from top-left to bottom-right?"
|
||||
|
||||
3. **Diagonal 2**: `queens[i] + i == col + row`
|
||||
- "Is there a queen on the same diagonal going from top-right to bottom-left?"
|
||||
|
||||
## Example Run 🏃♂️
|
||||
|
||||
Let's trace through what happens when we run `./n_queens 4`:
|
||||
|
||||
1. **Start with row 0**: Try putting a queen in each column
|
||||
- Column 0: Check safety → Safe! Put queen at (0,0)
|
||||
- Move to row 1
|
||||
|
||||
2. **Row 1**: Try each column
|
||||
- Column 0: Not safe (same column as row 0)
|
||||
- Column 1: Not safe (same column... wait, no queen there yet)
|
||||
- Column 2: Check safety → Safe! Put queen at (1,2)
|
||||
- Move to row 2
|
||||
|
||||
3. **Row 2**: Try each column
|
||||
- Column 0: Not safe (diagonal conflict)
|
||||
- Column 1: Not safe (diagonal conflict)
|
||||
- Column 2: Not safe (same column as row 1)
|
||||
- Column 3: Not safe (diagonal conflict)
|
||||
- **No solution found!** Go back to row 1
|
||||
|
||||
4. **Back to row 1**: Try next column
|
||||
- Column 3: Check safety → Safe! Put queen at (1,3)
|
||||
- Move to row 2
|
||||
|
||||
5. Continue this process...
|
||||
|
||||
Eventually, we find: `1 3 0 2` and `2 0 3 1`
|
||||
|
||||
## Results for Different N Values 📊
|
||||
|
||||
- **N=1**: `0` (1 solution - just put the queen anywhere)
|
||||
- **N=2**: No output (impossible to solve)
|
||||
- **N=3**: No output (impossible to solve)
|
||||
- **N=4**: `1 3 0 2` and `2 0 3 1` (2 solutions)
|
||||
- **N=8**: 92 solutions!
|
||||
|
||||
## Fun Facts! 🎉
|
||||
|
||||
1. **Why no solution for N=2 and N=3?**
|
||||
- For N=2: You need 2 queens on a 2×2 board, but they'll always attack each other!
|
||||
- For N=3: Same problem - not enough space!
|
||||
|
||||
2. **The 8-Queens problem** (standard chessboard) has exactly **92 solutions**!
|
||||
|
||||
3. **This is a classic computer science problem** that teaches us about:
|
||||
- **Recursion** (functions calling themselves)
|
||||
- **Backtracking** (trying something, and if it doesn't work, going back and trying something else)
|
||||
- **Problem solving** (breaking a big problem into smaller pieces)
|
||||
|
||||
## How to Use the Program 💻
|
||||
|
||||
1. **Compile**: `make`
|
||||
2. **Run**: `./n_queens 4`
|
||||
3. **Test different values**: `make test`
|
||||
4. **Clean up**: `make clean`
|
||||
|
||||
Try it with different numbers and see what happens! 🚀
|
||||
84
level-2/n_queens/n_queens.c
Normal file
84
level-2/n_queens/n_queens.c
Normal file
@ -0,0 +1,84 @@
|
||||
#include "n_queens.h"
|
||||
|
||||
void print_number(int n)
|
||||
{
|
||||
char c;
|
||||
|
||||
if (n >= 10)
|
||||
print_number(n / 10);
|
||||
c = '0' + (n % 10);
|
||||
write(1, &c, 1);
|
||||
}
|
||||
|
||||
void print_solution(int *queens, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
while (i < n)
|
||||
{
|
||||
if (i > 0)
|
||||
write(1, " ", 1);
|
||||
print_number(queens[i]);
|
||||
i++;
|
||||
}
|
||||
write(1, "\n", 1);
|
||||
}
|
||||
|
||||
int is_safe(int *queens, int row, int col, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
(void)n;
|
||||
i = 0;
|
||||
while (i < row)
|
||||
{
|
||||
if (queens[i] == col)
|
||||
return (0);
|
||||
if (queens[i] - i == col - row)
|
||||
return (0);
|
||||
if (queens[i] + i == col + row)
|
||||
return (0);
|
||||
i++;
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
void solve_nqueens(int *queens, int row, int n)
|
||||
{
|
||||
int col;
|
||||
|
||||
if (row == n)
|
||||
{
|
||||
print_solution(queens, n);
|
||||
return ;
|
||||
}
|
||||
col = 0;
|
||||
while (col < n)
|
||||
{
|
||||
if (is_safe(queens, row, col, n))
|
||||
{
|
||||
queens[row] = col;
|
||||
solve_nqueens(queens, row + 1, n);
|
||||
}
|
||||
col++;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int n;
|
||||
int *queens;
|
||||
|
||||
if (argc != 2)
|
||||
return (1);
|
||||
n = atoi(argv[1]);
|
||||
if (n <= 0)
|
||||
return (1);
|
||||
queens = (int *)malloc(sizeof(int) * n);
|
||||
if (!queens)
|
||||
return (1);
|
||||
solve_nqueens(queens, 0, n);
|
||||
free(queens);
|
||||
return (0);
|
||||
}
|
||||
13
level-2/n_queens/n_queens.h
Normal file
13
level-2/n_queens/n_queens.h
Normal file
@ -0,0 +1,13 @@
|
||||
#ifndef N_QUEENS_H
|
||||
# define N_QUEENS_H
|
||||
|
||||
# include <stdlib.h>
|
||||
# include <unistd.h>
|
||||
# include <stdio.h>
|
||||
|
||||
int is_safe(int *queens, int row, int col, int n);
|
||||
void solve_nqueens(int *queens, int row, int n);
|
||||
void print_solution(int *queens, int n);
|
||||
void print_number(int n);
|
||||
|
||||
#endif
|
||||
28
level-2/n_queens/subject.txt
Normal file
28
level-2/n_queens/subject.txt
Normal file
@ -0,0 +1,28 @@
|
||||
Assignement name : n_queens
|
||||
|
||||
Expected files : *.c *.h
|
||||
|
||||
Allowed functions : atoi, fprintf, write, calloc, malloc, free, realloc, stdout, stderr
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
Write a program that will print all the solutions to the n queens problem
|
||||
for a n given as argument.
|
||||
We will not test with negative values.
|
||||
The order of the solutions is not important.
|
||||
|
||||
You will display the solutions under the following format :
|
||||
<p1> <p2> <p3> ... \n
|
||||
where pn are the line index of the queen in each colum starting from 0.
|
||||
|
||||
For example this should work :
|
||||
$> ./n_queens 2 | cat -e
|
||||
|
||||
$> ./n_queens 4 | cat -e
|
||||
1 3 0 2$
|
||||
2 0 3 1$
|
||||
|
||||
$> ./n_queens 7 | cat -e
|
||||
0 2 4 6 1 3 5$
|
||||
0 3 6 2 5 1 4$
|
||||
etc...
|
||||
25
level-2/permutations/Makefile
Normal file
25
level-2/permutations/Makefile
Normal file
@ -0,0 +1,25 @@
|
||||
NAME = permutations
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -Werror
|
||||
|
||||
SRCS = permutations.c
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
|
||||
all: $(NAME)
|
||||
|
||||
$(NAME): $(OBJS)
|
||||
$(CC) $(CFLAGS) -o $(NAME) $(OBJS)
|
||||
|
||||
%.o: %.c permutations.h
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS)
|
||||
|
||||
fclean: clean
|
||||
rm -f $(NAME)
|
||||
|
||||
re: fclean all
|
||||
|
||||
.PHONY: all clean fclean re
|
||||
200
level-2/permutations/README.md
Normal file
200
level-2/permutations/README.md
Normal file
@ -0,0 +1,200 @@
|
||||
# Permutations Exercise - Simple Explanation 🔄
|
||||
|
||||
## What is a Permutation? 🤔
|
||||
|
||||
Imagine you have some letters, like the letters in your name. A **permutation** is just a fancy word for "all the different ways you can arrange those letters."
|
||||
|
||||
Think of it like this:
|
||||
- If you have the letters **A** and **B**, you can arrange them as **AB** or **BA**
|
||||
- If you have **A**, **B**, and **C**, you can make **ABC**, **ACB**, **BAC**, **BCA**, **CAB**, **CBA**
|
||||
|
||||
It's like having alphabet blocks and seeing how many different words you can spell by changing the order!
|
||||
|
||||
## The Challenge 🎯
|
||||
|
||||
Your mission is to write a program that:
|
||||
1. Takes a word (like "abc")
|
||||
2. Shows ALL possible ways to rearrange the letters
|
||||
3. Shows them in **alphabetical order** (like in a dictionary)
|
||||
|
||||
## Real Examples 📝
|
||||
|
||||
Let's see what our program does:
|
||||
|
||||
### Example 1: Single Letter
|
||||
```bash
|
||||
./permutations a
|
||||
```
|
||||
**Output:** `a`
|
||||
|
||||
*Why?* There's only one letter, so there's only one way to arrange it!
|
||||
|
||||
### Example 2: Two Letters
|
||||
```bash
|
||||
./permutations ab
|
||||
```
|
||||
**Output:**
|
||||
```
|
||||
ab
|
||||
ba
|
||||
```
|
||||
|
||||
*Why?* We can put 'a' first or 'b' first. That's it!
|
||||
|
||||
### Example 3: Three Letters
|
||||
```bash
|
||||
./permutations abc
|
||||
```
|
||||
**Output:**
|
||||
```
|
||||
abc
|
||||
acb
|
||||
bac
|
||||
bca
|
||||
cab
|
||||
cba
|
||||
```
|
||||
|
||||
*Why?* Think of it step by step:
|
||||
- Start with 'a': we can make **abc** and **acb**
|
||||
- Start with 'b': we can make **bac** and **bca**
|
||||
- Start with 'c': we can make **cab** and **cba**
|
||||
|
||||
## How Our Code Works 🛠️
|
||||
|
||||
### Step 1: Getting Ready
|
||||
```c
|
||||
// We take the word you give us
|
||||
char *str = argv[1]; // This is your word like "abc"
|
||||
|
||||
// We make a copy so we don't mess up the original
|
||||
copy = malloc(sizeof(char) * (len + 1));
|
||||
```
|
||||
|
||||
### Step 2: Sorting First (The Secret Sauce! ✨)
|
||||
```c
|
||||
ft_sort_string(copy); // This puts letters in order: "cba" becomes "abc"
|
||||
```
|
||||
|
||||
**Why do we sort first?**
|
||||
- If someone gives us "cba", we sort it to "abc" first
|
||||
- This way, we always start with letters in alphabetical order
|
||||
- Then when we make all arrangements, they come out in the right order!
|
||||
|
||||
### Step 3: The Magic Swapping Function
|
||||
```c
|
||||
void ft_swap(char *a, char *b)
|
||||
{
|
||||
char temp = *a; // Remember the first letter
|
||||
*a = *b; // Put the second letter in first position
|
||||
*b = temp; // Put the first letter in second position
|
||||
}
|
||||
```
|
||||
|
||||
**What's swapping?** It's like switching two cards in your hand:
|
||||
- You have cards **A** and **B**
|
||||
- After swapping, you have **B** and **A**
|
||||
|
||||
### Step 4: The Next Permutation Magic 🪄
|
||||
```c
|
||||
int next_permutation(char *str, int len)
|
||||
{
|
||||
int i = len - 2;
|
||||
while (i >= 0 && str[i] >= str[i + 1]) // Find rightmost character smaller than next
|
||||
i--;
|
||||
if (i < 0)
|
||||
return (0); // No more permutations
|
||||
|
||||
int j = len - 1;
|
||||
while (str[j] <= str[i]) // Find rightmost character greater than str[i]
|
||||
j--;
|
||||
ft_swap(&str[i], &str[j]); // Swap them
|
||||
ft_reverse(str, i + 1, len - 1); // Reverse the suffix
|
||||
return (1); // Successfully generated next permutation
|
||||
}
|
||||
```
|
||||
|
||||
**How does this work?** Think of it like counting in a special way:
|
||||
1. **Find** the rightmost place where we can "increment" (make it bigger)
|
||||
2. **Swap** with the next bigger character available
|
||||
3. **Reverse** everything after that position to get the smallest arrangement
|
||||
4. **Repeat** until no more permutations exist
|
||||
|
||||
It's like a smart odometer that counts through all possible letter arrangements in perfect alphabetical order!
|
||||
|
||||
### Step 5: The Main Loop 🔄
|
||||
```c
|
||||
ft_sort_string(copy); // Start with alphabetically first arrangement
|
||||
puts(copy); // Print the first permutation
|
||||
while (next_permutation(copy, len)) // While there are more permutations
|
||||
puts(copy); // Print each one
|
||||
```
|
||||
|
||||
**How does this work?**
|
||||
1. **Start** with the smallest (first) arrangement: "abc"
|
||||
2. **Print** it
|
||||
3. **Generate** the next alphabetical arrangement using `next_permutation`
|
||||
4. **Print** it
|
||||
5. **Repeat** until `next_permutation` says "no more!"
|
||||
|
||||
It's like flipping through a dictionary - each page (permutation) comes in perfect alphabetical order!
|
||||
|
||||
## Why This Approach is Cool 😎
|
||||
|
||||
### The Step-by-Step Process 📋
|
||||
When we have "abc", our program works like this:
|
||||
|
||||
```
|
||||
1. Start: abc (sorted, print it!)
|
||||
2. Next: acb (swap 'b' and 'c', print it!)
|
||||
3. Next: bac (find next in alphabetical order, print it!)
|
||||
4. Next: bca (continue the pattern, print it!)
|
||||
5. Next: cab (keep going, print it!)
|
||||
6. Next: cba (last one, print it!)
|
||||
7. Done: No more permutations possible
|
||||
```
|
||||
|
||||
**The Magic:** Each step finds the next arrangement that would come after the current one in a dictionary! It's like having a super-smart assistant who knows exactly what comes next alphabetically.
|
||||
|
||||
### Memory Management 🧠
|
||||
```c
|
||||
copy = malloc(sizeof(char) * (len + 1)); // Ask for memory
|
||||
// ... do our work ...
|
||||
free(copy); // Give memory back when done
|
||||
```
|
||||
|
||||
We're polite programmers - we clean up after ourselves!
|
||||
|
||||
### Error Handling 🛡️
|
||||
```c
|
||||
if (argc != 2) // Did you give us exactly one word?
|
||||
return (1); // If not, we quit
|
||||
|
||||
if (!copy) // Did we get the memory we asked for?
|
||||
return (1); // If not, we quit safely
|
||||
```
|
||||
|
||||
We check if things went wrong and handle it gracefully.
|
||||
|
||||
## Fun Facts! 🎉
|
||||
|
||||
- With 1 letter: **1** permutation
|
||||
- With 2 letters: **2** permutations
|
||||
- With 3 letters: **6** permutations
|
||||
- With 4 letters: **24** permutations
|
||||
- With 5 letters: **120** permutations
|
||||
|
||||
**Pattern?** For n letters, you get n × (n-1) × (n-2) × ... × 1 permutations!
|
||||
|
||||
## Try It Yourself! 🚀
|
||||
|
||||
1. Compile the program: `gcc -Wall -Wextra -Werror permutations.c -o permutations`
|
||||
2. Try it with your name: `./permutations john`
|
||||
3. Try it with short words: `./permutations cat`
|
||||
4. See how the results are always in alphabetical order!
|
||||
|
||||
Remember: The computer is doing exactly what we told it to do, step by step, just like following a recipe! 👨🍳
|
||||
|
||||
---
|
||||
|
||||
*Made with ❤️ for curious minds who want to understand how permutations work!*
|
||||
98
level-2/permutations/permutations.c
Normal file
98
level-2/permutations/permutations.c
Normal file
@ -0,0 +1,98 @@
|
||||
#include "permutations.h"
|
||||
|
||||
int ft_strlen(char *str)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = 0;
|
||||
while (str[len])
|
||||
len++;
|
||||
return (len);
|
||||
}
|
||||
|
||||
void ft_swap(char *a, char *b)
|
||||
{
|
||||
char temp;
|
||||
|
||||
temp = *a;
|
||||
*a = *b;
|
||||
*b = temp;
|
||||
}
|
||||
|
||||
void ft_sort_string(char *str)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
int len;
|
||||
|
||||
len = ft_strlen(str);
|
||||
i = 0;
|
||||
while (i < len - 1)
|
||||
{
|
||||
j = i + 1;
|
||||
while (j < len)
|
||||
{
|
||||
if (str[i] > str[j])
|
||||
ft_swap(&str[i], &str[j]);
|
||||
j++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void ft_reverse(char *str, int start, int end)
|
||||
{
|
||||
while (start < end)
|
||||
{
|
||||
ft_swap(&str[start], &str[end]);
|
||||
start++;
|
||||
end--;
|
||||
}
|
||||
}
|
||||
|
||||
int next_permutation(char *str, int len)
|
||||
{
|
||||
int i;
|
||||
int j;
|
||||
|
||||
i = len - 2;
|
||||
while (i >= 0 && str[i] >= str[i + 1])
|
||||
i--;
|
||||
if (i < 0)
|
||||
return (0);
|
||||
j = len - 1;
|
||||
while (str[j] <= str[i])
|
||||
j--;
|
||||
ft_swap(&str[i], &str[j]);
|
||||
ft_reverse(str, i + 1, len - 1);
|
||||
return (1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
char *str;
|
||||
char *copy;
|
||||
int len;
|
||||
int i;
|
||||
|
||||
if (argc != 2)
|
||||
return (1);
|
||||
str = argv[1];
|
||||
len = ft_strlen(str);
|
||||
copy = malloc(sizeof(char) * (len + 1));
|
||||
if (!copy)
|
||||
return (1);
|
||||
i = 0;
|
||||
while (i < len)
|
||||
{
|
||||
copy[i] = str[i];
|
||||
i++;
|
||||
}
|
||||
copy[len] = '\0';
|
||||
ft_sort_string(copy);
|
||||
puts(copy);
|
||||
while (next_permutation(copy, len))
|
||||
puts(copy);
|
||||
free(copy);
|
||||
return (0);
|
||||
}
|
||||
14
level-2/permutations/permutations.h
Normal file
14
level-2/permutations/permutations.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef PERMUTATIONS_H
|
||||
# define PERMUTATIONS_H
|
||||
|
||||
# include <stdlib.h>
|
||||
# include <unistd.h>
|
||||
# include <stdio.h>
|
||||
|
||||
void ft_swap(char *a, char *b);
|
||||
void ft_sort_string(char *str);
|
||||
void ft_reverse(char *str, int start, int end);
|
||||
int next_permutation(char *str, int len);
|
||||
int ft_strlen(char *str);
|
||||
|
||||
#endif
|
||||
27
level-2/permutations/subject.txt
Normal file
27
level-2/permutations/subject.txt
Normal file
@ -0,0 +1,27 @@
|
||||
Assignment name : permutations
|
||||
Expected files : *.c *.h
|
||||
Allowed functions: puts, malloc, calloc, realloc, free, write
|
||||
---------------------------------------------------------------
|
||||
|
||||
Write a program that will print all the permutations of a string given as argument.
|
||||
|
||||
The solutions must be given in alphabetical order.
|
||||
|
||||
We will not try your program with strings containing duplicates (eg: 'abccd').
|
||||
|
||||
For example this should work:
|
||||
|
||||
$> ./permutations a | cat -e
|
||||
a$
|
||||
|
||||
$> ./permutations ab | cat -e
|
||||
ab$
|
||||
ba$
|
||||
|
||||
$> ./permutations abc | cat -e
|
||||
abc$
|
||||
acb$
|
||||
bac$
|
||||
bca$
|
||||
cab$
|
||||
cba$
|
||||
71
level-2/powerset/Makefile
Normal file
71
level-2/powerset/Makefile
Normal file
@ -0,0 +1,71 @@
|
||||
# Program name
|
||||
NAME = powerset
|
||||
|
||||
# Compiler and flags
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -Werror
|
||||
|
||||
# Source files
|
||||
SRCS = powerset.c
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
|
||||
# Header files
|
||||
HEADERS = powerset.h
|
||||
|
||||
# Colors for output
|
||||
GREEN = \033[0;32m
|
||||
RED = \033[0;31m
|
||||
RESET = \033[0m
|
||||
|
||||
# Default target
|
||||
all: $(NAME)
|
||||
|
||||
# Build the program
|
||||
$(NAME): $(OBJS)
|
||||
@echo "$(GREEN)Linking $(NAME)...$(RESET)"
|
||||
@$(CC) $(CFLAGS) $(OBJS) -o $(NAME)
|
||||
@echo "$(GREEN)✓ $(NAME) compiled successfully!$(RESET)"
|
||||
|
||||
# Compile source files to object files
|
||||
%.o: %.c $(HEADERS)
|
||||
@echo "$(GREEN)Compiling $<...$(RESET)"
|
||||
@$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# Clean object files
|
||||
clean:
|
||||
@echo "$(RED)Cleaning object files...$(RESET)"
|
||||
@rm -f $(OBJS)
|
||||
|
||||
# Clean everything
|
||||
fclean: clean
|
||||
@echo "$(RED)Cleaning $(NAME)...$(RESET)"
|
||||
@rm -f $(NAME)
|
||||
|
||||
# Rebuild everything
|
||||
re: fclean all
|
||||
|
||||
# Test the program with the examples from the subject
|
||||
test: $(NAME)
|
||||
@echo "$(GREEN)Running tests...$(RESET)"
|
||||
@echo "\n$(GREEN)Test 1: powerset 3 1 0 2 4 5 3$(RESET)"
|
||||
@./$(NAME) 3 1 0 2 4 5 3
|
||||
@echo "\n$(GREEN)Test 2: powerset 12 5 2 1 8 4 3 7 11$(RESET)"
|
||||
@./$(NAME) 12 5 2 1 8 4 3 7 11
|
||||
@echo "\n$(GREEN)Test 3: powerset 0 1 -1$(RESET)"
|
||||
@./$(NAME) 0 1 -1
|
||||
@echo "\n$(GREEN)Test 4: powerset 7 3 8 2 (should be empty)$(RESET)"
|
||||
@./$(NAME) 7 3 8 2
|
||||
@echo "$(GREEN)✓ All tests completed!$(RESET)"
|
||||
|
||||
# Help
|
||||
help:
|
||||
@echo "Available targets:"
|
||||
@echo " all - Build the program (default)"
|
||||
@echo " clean - Remove object files"
|
||||
@echo " fclean - Remove object files and executable"
|
||||
@echo " re - Rebuild everything"
|
||||
@echo " test - Run test cases"
|
||||
@echo " help - Show this help message"
|
||||
|
||||
# Declare phony targets
|
||||
.PHONY: all clean fclean re test help
|
||||
175
level-2/powerset/README.md
Normal file
175
level-2/powerset/README.md
Normal file
@ -0,0 +1,175 @@
|
||||
# Powerset Exercise - Simple Explanation 🧮
|
||||
|
||||
## What is this exercise about?
|
||||
|
||||
Imagine you have a bag of numbered balls, and you want to find all the different ways you can pick some balls so that the numbers on them add up to a specific target number.
|
||||
|
||||
This is exactly what the **powerset** exercise does!
|
||||
|
||||
## Real-world example 🎯
|
||||
|
||||
Let's say you have these numbered balls: **1, 2, 3, 4, 5**
|
||||
|
||||
And you want to find all the ways to pick balls that add up to **5**.
|
||||
|
||||
Here are all the possible ways:
|
||||
- Pick just ball **5** → 5 = 5 ✅
|
||||
- Pick balls **1** and **4** → 1 + 4 = 5 ✅
|
||||
- Pick balls **2** and **3** → 2 + 3 = 5 ✅
|
||||
|
||||
So the answer would be:
|
||||
```
|
||||
5
|
||||
1 4
|
||||
2 3
|
||||
```
|
||||
|
||||
## How does our program work? 🤖
|
||||
|
||||
### Step 1: Understanding the input
|
||||
When you run the program like this:
|
||||
```bash
|
||||
./powerset 5 1 2 3 4 5
|
||||
```
|
||||
|
||||
- **5** is our target number (what we want the balls to add up to)
|
||||
- **1 2 3 4 5** are our numbered balls
|
||||
|
||||
### Step 2: The magic behind the scenes
|
||||
|
||||
Our program is like a smart robot that tries **every possible combination**:
|
||||
|
||||
1. **Try no balls at all** → sum = 0 (not 5, so skip)
|
||||
2. **Try just ball 1** → sum = 1 (not 5, so skip)
|
||||
3. **Try just ball 2** → sum = 2 (not 5, so skip)
|
||||
4. **Try just ball 3** → sum = 3 (not 5, so skip)
|
||||
5. **Try just ball 4** → sum = 4 (not 5, so skip)
|
||||
6. **Try just ball 5** → sum = 5 ✅ **FOUND ONE!**
|
||||
7. **Try balls 1 and 2** → sum = 3 (not 5, so skip)
|
||||
8. **Try balls 1 and 3** → sum = 4 (not 5, so skip)
|
||||
9. **Try balls 1 and 4** → sum = 5 ✅ **FOUND ANOTHER!**
|
||||
10. **Try balls 2 and 3** → sum = 5 ✅ **FOUND ANOTHER!**
|
||||
|
||||
...and so on until it tries every possible combination!
|
||||
|
||||
## Code explanation 📝
|
||||
|
||||
### The main parts of our code:
|
||||
|
||||
#### 1. Reading the input (`main` function)
|
||||
```c
|
||||
target = atoi(argv[1]); // Get the target number (5 in our example)
|
||||
set[i] = atoi(argv[i + 2]); // Get each ball number (1, 2, 3, 4, 5)
|
||||
```
|
||||
|
||||
#### 2. The smart robot (`find_subsets` function)
|
||||
This function is like our robot that tries every combination:
|
||||
|
||||
```c
|
||||
// For each ball, we have 2 choices:
|
||||
find_subsets(..., index + 1); // Don't pick this ball
|
||||
subset[subset_size] = set[index]; // Pick this ball
|
||||
find_subsets(..., subset_size + 1, ...); // Continue with this ball included
|
||||
```
|
||||
|
||||
#### 3. Checking if we found a winner (`print_subset`)
|
||||
```c
|
||||
if (current_sum == target) // If the sum equals our target
|
||||
print_subset(subset, subset_size); // Show this combination!
|
||||
```
|
||||
|
||||
## More examples to understand better 🎲
|
||||
|
||||
### Example 1: Finding combinations that sum to 3
|
||||
```bash
|
||||
./powerset 3 1 0 2 4 5 3
|
||||
```
|
||||
|
||||
**What the robot finds:**
|
||||
- Ball **3** alone → 3 ✅
|
||||
- Balls **0** and **3** → 0 + 3 = 3 ✅
|
||||
- Balls **1** and **2** → 1 + 2 = 3 ✅
|
||||
- Balls **1**, **0**, and **2** → 1 + 0 + 2 = 3 ✅
|
||||
|
||||
**Output:**
|
||||
```
|
||||
3
|
||||
0 3
|
||||
1 2
|
||||
1 0 2
|
||||
```
|
||||
|
||||
### Example 2: Finding the empty combination
|
||||
```bash
|
||||
./powerset 0 1 -1
|
||||
```
|
||||
|
||||
**What the robot finds:**
|
||||
- No balls picked → sum = 0 ✅ (shows as empty line)
|
||||
- Balls **1** and **-1** → 1 + (-1) = 0 ✅
|
||||
|
||||
**Output:**
|
||||
```
|
||||
|
||||
1 -1
|
||||
```
|
||||
(Notice the empty line at the top!)
|
||||
|
||||
### Example 3: When nothing works
|
||||
```bash
|
||||
./powerset 7 3 8 2
|
||||
```
|
||||
|
||||
**What the robot finds:**
|
||||
- No combination of 3, 8, and 2 can make 7
|
||||
- So nothing gets printed (empty output)
|
||||
|
||||
## Important rules 📏
|
||||
|
||||
1. **Order matters**: We always keep balls in the same order as given
|
||||
- ✅ Correct: `1 4` (1 comes before 4 in input)
|
||||
- ❌ Wrong: `4 1` (this changes the order)
|
||||
|
||||
2. **No duplicates**: Each ball can only be used once per combination
|
||||
|
||||
3. **Empty combination counts**: If target is 0, picking no balls is valid!
|
||||
|
||||
## How to use the program 🚀
|
||||
|
||||
1. **Compile it:**
|
||||
```bash
|
||||
make
|
||||
```
|
||||
|
||||
2. **Run tests:**
|
||||
```bash
|
||||
make test
|
||||
```
|
||||
|
||||
3. **Try your own examples:**
|
||||
```bash
|
||||
./powerset [target_number] [ball1] [ball2] [ball3] ...
|
||||
```
|
||||
|
||||
4. **Clean up:**
|
||||
```bash
|
||||
make clean # Remove temporary files
|
||||
make fclean # Remove everything
|
||||
```
|
||||
|
||||
## Fun challenge! 🎮
|
||||
|
||||
Try to predict what this will output before running it:
|
||||
```bash
|
||||
./powerset 6 1 2 3 4
|
||||
```
|
||||
|
||||
**Think about it:**
|
||||
- Which combinations of 1, 2, 3, 4 add up to 6?
|
||||
- Remember: order matters and each number can only be used once!
|
||||
|
||||
**Answer:** `2 4` and `1 2 3` (because 2+4=6 and 1+2+3=6)
|
||||
|
||||
---
|
||||
|
||||
*Now you understand how the powerset exercise works! It's like having a super-smart robot that can instantly try every possible combination of numbers to find the ones that add up to your target. Pretty cool, right?* 🤖✨
|
||||
73
level-2/powerset/powerset.c
Normal file
73
level-2/powerset/powerset.c
Normal file
@ -0,0 +1,73 @@
|
||||
#include "powerset.h"
|
||||
|
||||
void print_subset(int *subset, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
while (i < size)
|
||||
{
|
||||
printf("%d", subset[i]);
|
||||
if (i < size - 1)
|
||||
printf(" ");
|
||||
i++;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void find_subsets(int *set, int set_size, int target, int *subset,
|
||||
int subset_size, int index)
|
||||
{
|
||||
int current_sum;
|
||||
int i;
|
||||
|
||||
if (index == set_size)
|
||||
{
|
||||
current_sum = 0;
|
||||
i = 0;
|
||||
while (i < subset_size)
|
||||
{
|
||||
current_sum += subset[i];
|
||||
i++;
|
||||
}
|
||||
if (current_sum == target)
|
||||
print_subset(subset, subset_size);
|
||||
return ;
|
||||
}
|
||||
find_subsets(set, set_size, target, subset, subset_size, index + 1);
|
||||
subset[subset_size] = set[index];
|
||||
find_subsets(set, set_size, target, subset, subset_size + 1, index + 1);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int *set;
|
||||
int *subset;
|
||||
int target;
|
||||
int set_size;
|
||||
int i;
|
||||
|
||||
if (argc < 2)
|
||||
return (1);
|
||||
target = atoi(argv[1]);
|
||||
set_size = argc - 2;
|
||||
set = malloc(sizeof(int) * set_size);
|
||||
if (!set)
|
||||
return (1);
|
||||
subset = malloc(sizeof(int) * set_size);
|
||||
if (!subset)
|
||||
{
|
||||
free(set);
|
||||
return (1);
|
||||
}
|
||||
i = 0;
|
||||
while (i < set_size)
|
||||
{
|
||||
set[i] = atoi(argv[i + 2]);
|
||||
i++;
|
||||
}
|
||||
find_subsets(set, set_size, target, subset, 0, 0);
|
||||
free(set);
|
||||
free(subset);
|
||||
return (0);
|
||||
}
|
||||
12
level-2/powerset/powerset.h
Normal file
12
level-2/powerset/powerset.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef POWERSET_H
|
||||
# define POWERSET_H
|
||||
|
||||
# include <stdlib.h>
|
||||
# include <unistd.h>
|
||||
# include <stdio.h>
|
||||
|
||||
void find_subsets(int *set, int set_size, int target, int *subset,
|
||||
int subset_size, int index);
|
||||
void print_subset(int *subset, int size);
|
||||
|
||||
#endif
|
||||
65
level-2/powerset/subject.txt
Normal file
65
level-2/powerset/subject.txt
Normal file
@ -0,0 +1,65 @@
|
||||
Assignment name : powerset
|
||||
Expected files : *.c *.h
|
||||
Allowed functions: atoi, printf, fprintf, malloc, calloc, realloc, free, stdout,
|
||||
write
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Write a program that will take as argument an integer n followed by a set s of
|
||||
distinct integers.
|
||||
Your program should display all the subsets of s whose sum of elements is n.
|
||||
|
||||
The order of the lines is not important, but the order of the elements in a subset is:
|
||||
it must match the order in the initial set s.
|
||||
This way, you should not have any duplicates (eg: '1 2' and '2 1').
|
||||
For example, using the command ./powerset 5 1 2 3 4 5
|
||||
this output is valid:
|
||||
1 4
|
||||
2 3
|
||||
5
|
||||
this one is also valid:
|
||||
2 3
|
||||
5
|
||||
1 4
|
||||
but not this one:
|
||||
4 1
|
||||
3 2
|
||||
5
|
||||
|
||||
|
||||
In case of a malloc error your program will exit with the code 1.
|
||||
|
||||
We will not test with invalid sets (for example '1 1 2').
|
||||
|
||||
Hint: the empty subset is a valid subset of any set. It will be displayed as an empty line.
|
||||
|
||||
For example this should work:
|
||||
$> ./powerset 3 1 0 2 4 5 3 | cat -e
|
||||
3$
|
||||
0 3$
|
||||
1 2$
|
||||
1 0 2$
|
||||
$> ./powerset 12 5 2 1 8 4 3 7 11 | cat -e
|
||||
8 4$
|
||||
1 11$
|
||||
1 4 7$
|
||||
1 8 3$
|
||||
2 3 7$
|
||||
5 7$
|
||||
5 4 3$
|
||||
5 2 1 4$
|
||||
$> ./powerset 0 1 -1 | cat -e
|
||||
$
|
||||
1 -1$
|
||||
$> ./powerset 7 3 8 2 | cat -e
|
||||
|
||||
// Other tests:
|
||||
$> ./powerset 100 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | cat -e
|
||||
...
|
||||
$> ./powerset -1 1 2 3 4 5 -10 | cat -e
|
||||
...
|
||||
$> ./powerset 0 -1 1 2 3 -2 | cat -e
|
||||
...
|
||||
$> ./powerset 13 65 23 3 4 6 7 1 2 | cat -e
|
||||
...
|
||||
$> ./powerset 10 0 1 2 3 4 5 6 7 8 9 | cat -e
|
||||
...
|
||||
80
level-2/rip/Makefile
Normal file
80
level-2/rip/Makefile
Normal file
@ -0,0 +1,80 @@
|
||||
# Program name
|
||||
NAME = rip
|
||||
|
||||
# Compiler and flags
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -Werror
|
||||
|
||||
# Source files
|
||||
SRCS = rip.c
|
||||
|
||||
# Object files
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
|
||||
# Colors for output
|
||||
GREEN = \033[0;32m
|
||||
RED = \033[0;31m
|
||||
YELLOW = \033[0;33m
|
||||
NC = \033[0m # No Color
|
||||
|
||||
# Default target
|
||||
all: $(NAME)
|
||||
|
||||
# Build the executable
|
||||
$(NAME): $(OBJS)
|
||||
@echo "$(YELLOW)Linking $(NAME)...$(NC)"
|
||||
@$(CC) $(CFLAGS) $(OBJS) -o $(NAME)
|
||||
@echo "$(GREEN)✅ $(NAME) created successfully!$(NC)"
|
||||
|
||||
# Compile source files to object files
|
||||
%.o: %.c
|
||||
@echo "$(YELLOW)Compiling $<...$(NC)"
|
||||
@$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
# Clean object files
|
||||
clean:
|
||||
@echo "$(RED)🧹 Cleaning object files...$(NC)"
|
||||
@rm -f $(OBJS)
|
||||
@echo "$(GREEN)✅ Object files cleaned!$(NC)"
|
||||
|
||||
# Clean object files and executable
|
||||
fclean: clean
|
||||
@echo "$(RED)🧹 Cleaning executable...$(NC)"
|
||||
@rm -f $(NAME)
|
||||
@echo "$(GREEN)✅ Everything cleaned!$(NC)"
|
||||
|
||||
# Rebuild everything
|
||||
re: fclean all
|
||||
|
||||
# Test the program with provided examples
|
||||
test: $(NAME)
|
||||
@echo "$(YELLOW)🧪 Running tests...$(NC)"
|
||||
@echo "$(YELLOW)Test 1: '(()'$(NC)"
|
||||
@./$(NAME) '(((' | cat -e
|
||||
@echo "$(YELLOW)Test 2: '((()()())())'$(NC)"
|
||||
@./$(NAME) '((()()())())' | cat -e
|
||||
@echo "$(YELLOW)Test 3: '()())()''$(NC)"
|
||||
@./$(NAME) '()())()' | cat -e
|
||||
@echo "$(YELLOW)Test 4: '(()(()('$(NC)"
|
||||
@./$(NAME) '(()(()(' | cat -e
|
||||
@echo "$(GREEN)✅ All tests completed!$(NC)"
|
||||
|
||||
# Check for memory leaks (requires valgrind)
|
||||
valgrind: $(NAME)
|
||||
@echo "$(YELLOW)🔍 Checking for memory leaks...$(NC)"
|
||||
@valgrind --leak-check=full --show-leak-kinds=all ./$(NAME) '(()'
|
||||
@echo "$(GREEN)✅ Memory check completed!$(NC)"
|
||||
|
||||
# Show help
|
||||
help:
|
||||
@echo "$(GREEN)Available targets:$(NC)"
|
||||
@echo " $(YELLOW)all$(NC) - Build the program"
|
||||
@echo " $(YELLOW)clean$(NC) - Remove object files"
|
||||
@echo " $(YELLOW)fclean$(NC) - Remove object files and executable"
|
||||
@echo " $(YELLOW)re$(NC) - Rebuild everything (fclean + all)"
|
||||
@echo " $(YELLOW)test$(NC) - Run all test cases"
|
||||
@echo " $(YELLOW)valgrind$(NC) - Check for memory leaks"
|
||||
@echo " $(YELLOW)help$(NC) - Show this help message"
|
||||
|
||||
# Declare phony targets
|
||||
.PHONY: all clean fclean re test valgrind help
|
||||
179
level-2/rip/README.md
Normal file
179
level-2/rip/README.md
Normal file
@ -0,0 +1,179 @@
|
||||
# The Parentheses Balancing Problem (RIP Exercise)
|
||||
|
||||
## What is this exercise about?
|
||||
|
||||
Imagine you have a string of parentheses like this: `"(()"`.
|
||||
|
||||
Think of parentheses like **doors** in a building:
|
||||
- `(` is like **opening a door**
|
||||
- `)` is like **closing a door**
|
||||
|
||||
For everything to work properly, every door you open must have a matching door that closes it!
|
||||
|
||||
## The Problem
|
||||
|
||||
Sometimes we get strings where the doors don't match properly. For example:
|
||||
- `"((("` - We opened 3 doors but never closed any!
|
||||
- `"())"` - We opened 1 door, closed 1 door, but then tried to close another door that was never opened!
|
||||
|
||||
Our job is to **fix** these strings by removing the minimum number of parentheses (but replacing them with spaces instead of deleting them completely).
|
||||
|
||||
## Real-World Example
|
||||
|
||||
Let's say you have: `"(()"`
|
||||
|
||||
This means:
|
||||
1. Open door 1: `(`
|
||||
2. Open door 2: `(`
|
||||
3. Close door 2: `)`
|
||||
4. But door 1 is still open! 😱
|
||||
|
||||
To fix this, we need to either:
|
||||
- Remove one of the opening doors: ` ()` (remove first `(`)
|
||||
- Or remove one of the opening doors: `( )` (remove second `(`)
|
||||
|
||||
Both solutions work!
|
||||
|
||||
## How Our Program Works
|
||||
|
||||
### Step 1: Count the Problems
|
||||
|
||||
```c
|
||||
// This function counts how many parentheses we need to remove
|
||||
void calculate_removals(char *str, int *left_rem, int *right_rem)
|
||||
{
|
||||
int left = 0; // Count of unmatched opening parentheses
|
||||
int right = 0; // Count of unmatched closing parentheses
|
||||
|
||||
// Go through each character
|
||||
while (str[i])
|
||||
{
|
||||
if (str[i] == '(')
|
||||
left++; // Found an opening door
|
||||
else if (str[i] == ')')
|
||||
{
|
||||
if (left > 0)
|
||||
left--; // Found a closing door for an open one
|
||||
else
|
||||
right++; // Found a closing door with no matching open door
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example with `"(()"`:**
|
||||
- Start: `left = 0`, `right = 0`
|
||||
- See `(`: `left = 1` (one unmatched opening)
|
||||
- See `(`: `left = 2` (two unmatched openings)
|
||||
- See `)`: `left = 1` (one opening got matched)
|
||||
- End: `left = 1`, `right = 0`
|
||||
|
||||
So we need to remove **1 opening parenthesis**.
|
||||
|
||||
### Step 2: Try All Possible Solutions
|
||||
|
||||
Think of it like trying different combinations:
|
||||
|
||||
```c
|
||||
// This function tries removing different parentheses to find all valid solutions
|
||||
void solve(char *s, int pos, int left_rem, int right_rem, int open, char *path)
|
||||
{
|
||||
// If we've looked at all characters
|
||||
if (pos == len)
|
||||
{
|
||||
// Check if we removed exactly what we needed and everything balances
|
||||
if (left_rem == 0 && right_rem == 0 && open == 0)
|
||||
add_result(results, path); // This is a valid solution!
|
||||
return;
|
||||
}
|
||||
|
||||
// Try removing this parenthesis (replace with space)
|
||||
if (s[pos] == '(' && left_rem > 0)
|
||||
{
|
||||
path[pos] = ' '; // Replace with space
|
||||
solve(s, pos + 1, left_rem - 1, right_rem, open, path);
|
||||
}
|
||||
|
||||
// Try keeping this parenthesis
|
||||
path[pos] = s[pos];
|
||||
if (s[pos] == '(')
|
||||
solve(s, pos + 1, left_rem, right_rem, open + 1, path);
|
||||
// ... and so on
|
||||
}
|
||||
```
|
||||
|
||||
### Step 3: Avoid Duplicates
|
||||
|
||||
We use a list to store all unique solutions:
|
||||
|
||||
```c
|
||||
// This makes sure we don't print the same solution twice
|
||||
void add_result(t_result **results, char *str)
|
||||
{
|
||||
// Check if we already have this solution
|
||||
current = *results;
|
||||
while (current)
|
||||
{
|
||||
if (ft_strcmp(current->str, str) == 0)
|
||||
return; // Already have it, don't add again
|
||||
current = current->next;
|
||||
}
|
||||
|
||||
// It's new, so add it to our list
|
||||
new->str = ft_strdup(str);
|
||||
new->next = *results;
|
||||
*results = new;
|
||||
}
|
||||
```
|
||||
|
||||
## Example Walkthrough
|
||||
|
||||
Let's trace through `"(()"` step by step:
|
||||
|
||||
### Initial Analysis:
|
||||
- Need to remove 1 opening parenthesis (`left_rem = 1`)
|
||||
- Need to remove 0 closing parentheses (`right_rem = 0`)
|
||||
|
||||
### Trying Different Positions:
|
||||
|
||||
**Position 0 (first `(`):**
|
||||
- Try removing it: `" ()"`
|
||||
- Check: removed 1 opening ✓, no unmatched doors ✓
|
||||
- **Valid solution!** ✅
|
||||
|
||||
**Position 1 (second `(`):**
|
||||
- Try removing it: `"( )"`
|
||||
- Check: removed 1 opening ✓, no unmatched doors ✓
|
||||
- **Valid solution!** ✅
|
||||
|
||||
**Position 2 (the `)`):**
|
||||
- Can't remove it (we need to remove openings, not closings)
|
||||
|
||||
### Final Output:
|
||||
```
|
||||
()
|
||||
( )
|
||||
```
|
||||
|
||||
## More Complex Example: `"()())()"`
|
||||
|
||||
This string has:
|
||||
- 3 opening doors: `(`, `(`, `(`
|
||||
- 4 closing doors: `)`, `)`, `)`, `)`
|
||||
|
||||
So we have 1 extra closing door that needs to be removed.
|
||||
|
||||
**All possible solutions:**
|
||||
- Remove closing at position 2: `"()( )()"`
|
||||
- Remove closing at position 3: `"()() ()"`
|
||||
- Remove closing at position 4: `"( ())()"`
|
||||
|
||||
## Why This Exercise is Useful
|
||||
|
||||
This problem teaches us:
|
||||
1. **Problem-solving**: Break down complex problems into smaller steps
|
||||
2. **Recursion**: Try all possibilities systematically
|
||||
3. **Data structures**: Use lists to store and manage results
|
||||
4. **Optimization**: Find the minimum changes needed
|
||||
|
||||
It's like being a **door inspector** - you need to make sure every building (string) has properly matched doors (parentheses) with the minimum number of changes!
|
||||
180
level-2/rip/rip.c
Normal file
180
level-2/rip/rip.c
Normal file
@ -0,0 +1,180 @@
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef struct s_result
|
||||
{
|
||||
char *str;
|
||||
struct s_result *next;
|
||||
} t_result;
|
||||
|
||||
int ft_strlen(char *str)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = 0;
|
||||
while (str[len])
|
||||
len++;
|
||||
return (len);
|
||||
}
|
||||
|
||||
int ft_strcmp(char *s1, char *s2)
|
||||
{
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
while (s1[i] && s2[i] && s1[i] == s2[i])
|
||||
i++;
|
||||
return (s1[i] - s2[i]);
|
||||
}
|
||||
|
||||
char *ft_strdup(char *src)
|
||||
{
|
||||
char *dup;
|
||||
int len;
|
||||
int i;
|
||||
|
||||
len = ft_strlen(src);
|
||||
dup = malloc(len + 1);
|
||||
if (!dup)
|
||||
return (NULL);
|
||||
i = 0;
|
||||
while (i < len)
|
||||
{
|
||||
dup[i] = src[i];
|
||||
i++;
|
||||
}
|
||||
dup[len] = '\0';
|
||||
return (dup);
|
||||
}
|
||||
|
||||
void add_result(t_result **results, char *str)
|
||||
{
|
||||
t_result *new;
|
||||
t_result *current;
|
||||
|
||||
current = *results;
|
||||
while (current)
|
||||
{
|
||||
if (ft_strcmp(current->str, str) == 0)
|
||||
return ;
|
||||
current = current->next;
|
||||
}
|
||||
new = malloc(sizeof(t_result));
|
||||
if (!new)
|
||||
return ;
|
||||
new->str = ft_strdup(str);
|
||||
if (!new->str)
|
||||
{
|
||||
free(new);
|
||||
return ;
|
||||
}
|
||||
new->next = *results;
|
||||
*results = new;
|
||||
}
|
||||
|
||||
void print_results(t_result *results)
|
||||
{
|
||||
t_result *current;
|
||||
int i;
|
||||
|
||||
current = results;
|
||||
while (current)
|
||||
{
|
||||
i = 0;
|
||||
while (current->str[i])
|
||||
{
|
||||
write(1, ¤t->str[i], 1);
|
||||
i++;
|
||||
}
|
||||
write(1, "\n", 1);
|
||||
current = current->next;
|
||||
}
|
||||
}
|
||||
|
||||
void free_results(t_result *results)
|
||||
{
|
||||
t_result *temp;
|
||||
|
||||
while (results)
|
||||
{
|
||||
temp = results;
|
||||
results = results->next;
|
||||
free(temp->str);
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
|
||||
void solve(char *s, int pos, int left_rem, int right_rem, int open, char *path, t_result **results)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = ft_strlen(s);
|
||||
if (pos == len)
|
||||
{
|
||||
if (left_rem == 0 && right_rem == 0 && open == 0)
|
||||
add_result(results, path);
|
||||
return ;
|
||||
}
|
||||
if (s[pos] == '(' && left_rem > 0)
|
||||
{
|
||||
path[pos] = ' ';
|
||||
solve(s, pos + 1, left_rem - 1, right_rem, open, path, results);
|
||||
}
|
||||
if (s[pos] == ')' && right_rem > 0)
|
||||
{
|
||||
path[pos] = ' ';
|
||||
solve(s, pos + 1, left_rem, right_rem - 1, open, path, results);
|
||||
}
|
||||
path[pos] = s[pos];
|
||||
if (s[pos] == '(')
|
||||
solve(s, pos + 1, left_rem, right_rem, open + 1, path, results);
|
||||
else if (s[pos] == ')' && open > 0)
|
||||
solve(s, pos + 1, left_rem, right_rem, open - 1, path, results);
|
||||
else
|
||||
solve(s, pos + 1, left_rem, right_rem, open, path, results);
|
||||
}
|
||||
|
||||
void remove_invalid_parentheses(char *s)
|
||||
{
|
||||
int left_rem;
|
||||
int right_rem;
|
||||
int len;
|
||||
char *path;
|
||||
int i;
|
||||
t_result *results;
|
||||
|
||||
left_rem = 0;
|
||||
right_rem = 0;
|
||||
len = ft_strlen(s);
|
||||
results = NULL;
|
||||
i = 0;
|
||||
while (s[i])
|
||||
{
|
||||
if (s[i] == '(')
|
||||
left_rem++;
|
||||
else if (s[i] == ')')
|
||||
{
|
||||
if (left_rem > 0)
|
||||
left_rem--;
|
||||
else
|
||||
right_rem++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
path = malloc(len + 1);
|
||||
if (!path)
|
||||
return ;
|
||||
path[len] = '\0';
|
||||
solve(s, 0, left_rem, right_rem, 0, path, &results);
|
||||
print_results(results);
|
||||
free_results(results);
|
||||
free(path);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2)
|
||||
return (1);
|
||||
remove_invalid_parentheses(argv[1]);
|
||||
return (0);
|
||||
}
|
||||
28
level-2/rip/subject.txt
Normal file
28
level-2/rip/subject.txt
Normal file
@ -0,0 +1,28 @@
|
||||
Assignment name : rip
|
||||
Expected files : *.c *.h
|
||||
Allowed functions: puts, write
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Write a program that will take as argument a string containing only parenthesis.
|
||||
if the parenthesis are unbalanced (for example "())")
|
||||
your program shall remove the minimum number of parentheses for the expression to be balanced.
|
||||
By removing we mean replacing by spaces.
|
||||
You will then print all the solutions (can be more than one).
|
||||
|
||||
The order of the solutions is not important.
|
||||
|
||||
For example this should work:
|
||||
$> ./rip '(()' | cat -e
|
||||
()$
|
||||
( )$
|
||||
$> ./rip '((()()())())' | cat -e
|
||||
((()()())())$
|
||||
$> ./rip '()())()'| cat -e
|
||||
()() ()$
|
||||
()( )()$
|
||||
( ())()$
|
||||
$> ./rip '(()(()(' | cat -e
|
||||
(() ) $
|
||||
( )( ) $
|
||||
( ) () $
|
||||
()( ) $
|
||||
26
level-2/tsp/Makefile
Normal file
26
level-2/tsp/Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
NAME = tsp
|
||||
|
||||
SRCS = tsp.c
|
||||
OBJS = $(SRCS:.c=.o)
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -Werror
|
||||
MATH_FLAGS = -lm
|
||||
|
||||
all: $(NAME)
|
||||
|
||||
$(NAME): $(OBJS)
|
||||
$(CC) $(CFLAGS) $(OBJS) -o $(NAME) $(MATH_FLAGS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS)
|
||||
|
||||
fclean: clean
|
||||
rm -f $(NAME)
|
||||
|
||||
re: fclean all
|
||||
|
||||
.PHONY: all clean fclean re
|
||||
247
level-2/tsp/TSP_Algorithm_Explained.md
Normal file
247
level-2/tsp/TSP_Algorithm_Explained.md
Normal file
@ -0,0 +1,247 @@
|
||||
# The Traveling Salesman Problem (TSP) - Simple Explanation
|
||||
|
||||
## What is the Traveling Salesman Problem? 🗺️
|
||||
|
||||
Imagine you're a delivery person who needs to visit several houses in your neighborhood and then return home. You want to find the **shortest possible route** that visits every house exactly once and brings you back to where you started.
|
||||
|
||||
That's exactly what the Traveling Salesman Problem is about!
|
||||
|
||||
### Real-World Example
|
||||
Let's say you need to deliver pizza to 4 houses:
|
||||
- Your home: (0, 0)
|
||||
- House A: (1, 0)
|
||||
- House B: (1, 1)
|
||||
- House C: (0, 1)
|
||||
|
||||
```
|
||||
C ---- B
|
||||
| |
|
||||
| |
|
||||
Home - A
|
||||
```
|
||||
|
||||
You could go in many different orders:
|
||||
- Home → A → B → C → Home
|
||||
- Home → A → C → B → Home
|
||||
- Home → B → A → C → Home
|
||||
- ... and many more!
|
||||
|
||||
But which path is the shortest? That's what our algorithm finds out!
|
||||
|
||||
## How Our Algorithm Works 🔍
|
||||
|
||||
Our solution uses a "brute force" approach, which means:
|
||||
**"Let's try EVERY possible path and pick the shortest one!"**
|
||||
|
||||
It's like trying on every shirt in your closet to find the one that fits best - not the fastest way, but it guarantees you'll find the perfect answer!
|
||||
|
||||
### Step 1: Calculate Distance Between Two Points
|
||||
|
||||
First, we need to know how far apart two places are:
|
||||
|
||||
```c
|
||||
static float distance(t_city a, t_city b)
|
||||
{
|
||||
float dx = b.x - a.x; // How far left/right?
|
||||
float dy = b.y - a.y; // How far up/down?
|
||||
return (sqrtf(dx * dx + dy * dy)); // Pythagorean theorem!
|
||||
}
|
||||
```
|
||||
|
||||
**What's happening here?**
|
||||
- `dx` = horizontal distance (like counting steps left or right)
|
||||
- `dy` = vertical distance (like counting steps up or down)
|
||||
- `sqrtf(dx*dx + dy*dy)` = the straight-line distance (like a bird flying)
|
||||
|
||||
**Example:**
|
||||
From Home (0,0) to House A (1,0):
|
||||
- dx = 1 - 0 = 1
|
||||
- dy = 0 - 0 = 0
|
||||
- distance = √(1² + 0²) = √1 = 1.0
|
||||
|
||||
### Step 2: Calculate Total Path Length
|
||||
|
||||
Now we add up all the distances in a complete round trip:
|
||||
|
||||
```c
|
||||
static float path_length(t_city *cities, int *route, int n)
|
||||
{
|
||||
float total = 0;
|
||||
int i = 0;
|
||||
|
||||
while (i < n)
|
||||
{
|
||||
// Add distance from current city to next city
|
||||
total += distance(cities[route[i]], cities[route[(i + 1) % n]]);
|
||||
i++;
|
||||
}
|
||||
return (total);
|
||||
}
|
||||
```
|
||||
|
||||
**What's that weird `% n` thing?**
|
||||
It's like a clock! When you get to hour 12, the next hour is 1, not 13.
|
||||
- When `i = 3` (last city) and `n = 4` (total cities)
|
||||
- `(i + 1) % n = (3 + 1) % 4 = 4 % 4 = 0`
|
||||
- So we go from the last city back to the first city (completing the circle!)
|
||||
|
||||
### Step 3: Try All Possible Routes
|
||||
|
||||
This is where the magic happens! We generate every possible order of visiting the cities:
|
||||
|
||||
```c
|
||||
static void solve(t_city *cities, int *route, int pos, int n, float *best)
|
||||
{
|
||||
float current;
|
||||
int i, temp;
|
||||
|
||||
// If we've arranged all cities, check this route
|
||||
if (pos == n)
|
||||
{
|
||||
current = path_length(cities, route, n);
|
||||
if (current < *best)
|
||||
*best = current; // Found a better route!
|
||||
return;
|
||||
}
|
||||
|
||||
// Try putting each remaining city in the current position
|
||||
i = pos;
|
||||
while (i < n)
|
||||
{
|
||||
// Swap cities (like switching the order of your visits)
|
||||
temp = route[pos];
|
||||
route[pos] = route[i];
|
||||
route[i] = temp;
|
||||
|
||||
// Try all arrangements with this city in this position
|
||||
solve(cities, route, pos + 1, n, best);
|
||||
|
||||
// Swap back (undo the change)
|
||||
temp = route[pos];
|
||||
route[pos] = route[i];
|
||||
route[i] = temp;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Think of it like this:**
|
||||
- Position 0: "Which city should I visit first?"
|
||||
- Position 1: "Which city should I visit second?"
|
||||
- Position 2: "Which city should I visit third?"
|
||||
- And so on...
|
||||
|
||||
For each position, we try putting each remaining city there and see what happens!
|
||||
|
||||
## Complete Example Walkthrough 🚶♂️
|
||||
|
||||
Let's trace through a simple 3-city example:
|
||||
- City 0: (0, 0)
|
||||
- City 1: (1, 0)
|
||||
- City 2: (0, 1)
|
||||
|
||||
```
|
||||
2
|
||||
|
|
||||
|
|
||||
0 --- 1
|
||||
```
|
||||
|
||||
**All possible routes:**
|
||||
1. 0 → 1 → 2 → 0: distance = 1 + √2 + 1 = 3.41
|
||||
2. 0 → 2 → 1 → 0: distance = 1 + √2 + 1 = 3.41
|
||||
3. 1 → 0 → 2 → 1: distance = 1 + 1 + √2 = 3.41
|
||||
4. 1 → 2 → 0 → 1: distance = √2 + 1 + 1 = 3.41
|
||||
5. 2 → 0 → 1 → 2: distance = 1 + 1 + √2 = 3.41
|
||||
6. 2 → 1 → 0 → 2: distance = √2 + 1 + 1 = 3.41
|
||||
|
||||
**Result:** All routes have the same length! (This makes sense - it's a triangle, so any direction around it is the same distance.)
|
||||
|
||||
## The Main Program 🏠
|
||||
|
||||
```c
|
||||
int main(void)
|
||||
{
|
||||
t_city cities[11]; // Space for up to 11 cities
|
||||
int route[11]; // The order we'll visit cities
|
||||
int n = 0; // How many cities we actually have
|
||||
float best = 999999.0f; // Start with a really big number
|
||||
|
||||
// Read city coordinates from input
|
||||
while (n < 11 && fscanf(stdin, "%f, %f", &cities[n].x, &cities[n].y) == 2)
|
||||
{
|
||||
route[n] = n; // Initially: visit cities in order 0,1,2,3...
|
||||
n++;
|
||||
}
|
||||
|
||||
if (n < 2)
|
||||
return (1); // Need at least 2 cities for a trip!
|
||||
|
||||
solve(cities, route, 0, n, &best); // Find the best route
|
||||
fprintf(stdout, "%.2f\n", best); // Print the shortest distance
|
||||
return (0);
|
||||
}
|
||||
```
|
||||
|
||||
## Why This Works (But Is Slow) ⏰
|
||||
|
||||
**The Good:**
|
||||
- ✅ **Always finds the perfect answer** - we check every possibility!
|
||||
- ✅ **Simple to understand** - no complex tricks or shortcuts
|
||||
- ✅ **Works for any arrangement of cities**
|
||||
|
||||
**The Not-So-Good:**
|
||||
- ❌ **Gets very slow with more cities**
|
||||
- For 3 cities: 6 routes to check
|
||||
- For 4 cities: 24 routes to check
|
||||
- For 10 cities: 3,628,800 routes to check! 😱
|
||||
|
||||
**Why it gets so big:**
|
||||
- 1st city: 10 choices
|
||||
- 2nd city: 9 choices (can't repeat)
|
||||
- 3rd city: 8 choices
|
||||
- ...
|
||||
- Total: 10 × 9 × 8 × 7 × 6 × 5 × 4 × 3 × 2 × 1 = 3,628,800
|
||||
|
||||
This is called "factorial growth" - it explodes very quickly!
|
||||
|
||||
## Key Programming Concepts Used 💻
|
||||
|
||||
### 1. **Recursion**
|
||||
The `solve()` function calls itself! It's like Russian nesting dolls - each call handles one position, then asks a smaller version of itself to handle the rest.
|
||||
|
||||
### 2. **Backtracking**
|
||||
We try something (swap cities), explore all possibilities, then undo it (swap back). Like trying different paths in a maze and backing up when we hit a dead end.
|
||||
|
||||
### 3. **Permutations**
|
||||
We generate every possible ordering of the cities. It's like having cards numbered 1,2,3 and arranging them in every possible order: 123, 132, 213, 231, 312, 321.
|
||||
|
||||
### 4. **Global Optimization**
|
||||
We keep track of the best solution found so far and update it whenever we find something better.
|
||||
|
||||
## Fun Facts! 🎯
|
||||
|
||||
1. **This problem is NP-hard** - that's computer science speak for "really, really hard to solve quickly"
|
||||
|
||||
2. **Real delivery companies** use approximate algorithms that find "pretty good" solutions much faster than perfect ones
|
||||
|
||||
3. **With 11 cities** (the maximum our program handles), there are 39,916,800 possible routes!
|
||||
|
||||
4. **The problem has applications** in:
|
||||
- GPS navigation systems
|
||||
- Circuit board manufacturing
|
||||
- DNA sequencing
|
||||
- Planning efficient tours for bands or sports teams
|
||||
|
||||
## Summary 📝
|
||||
|
||||
Our TSP algorithm is like a very thorough friend who insists on checking every possible pizza delivery route before deciding which one is shortest. It's not the fastest approach, but it guarantees the perfect answer!
|
||||
|
||||
The main steps are:
|
||||
1. **Read** all the city locations
|
||||
2. **Generate** every possible visiting order
|
||||
3. **Calculate** the total distance for each order
|
||||
4. **Remember** the shortest distance found
|
||||
5. **Print** the answer
|
||||
|
||||
Even though it's not the most efficient algorithm for large problems, it's perfect for learning because it's easy to understand and always gives the correct answer! 🎉
|
||||
8
level-2/tsp/square.txt
Normal file
8
level-2/tsp/square.txt
Normal file
@ -0,0 +1,8 @@
|
||||
0, 0
|
||||
1, 0
|
||||
2, 0
|
||||
0, 1
|
||||
1, 1
|
||||
2, 1
|
||||
5, 2
|
||||
2, 2
|
||||
64
level-2/tsp/subject.txt
Normal file
64
level-2/tsp/subject.txt
Normal file
@ -0,0 +1,64 @@
|
||||
Assignment name: tsp
|
||||
Expected files: *.c *.h
|
||||
Allowed functions: write, sqrtf, getline, fseek, fscanf, ferror, feof,
|
||||
fabsf, memcpy, fprintf, fclose, malloc, calloc, realloc, free, fopen,
|
||||
errno, stderr, stdin, stdout
|
||||
-------------------------------------------------------
|
||||
|
||||
The first publication referring to this problem as the "traveling salesman
|
||||
problem" is found in the 1949 RAND Corporation report by Julia Robinson,
|
||||
"On the Hamiltonian game (a traveling salesman problem)."
|
||||
|
||||
Here is how she defines the problem:
|
||||
|
||||
"The purpose of this note is to give a method for solving a problem related
|
||||
to the traveling salesman problem. It seems worthwhile to give a description
|
||||
of the original problem. One formulation is to find the shortest route for a
|
||||
salesman starting from Washington, visiting all the state capitals and then
|
||||
returning to Washington.
|
||||
|
||||
More generally, to find the shortest CLOSED CURVE containing n given points
|
||||
in the plane."
|
||||
|
||||
for example with the following set of cities:
|
||||
0, 0
|
||||
1, 0
|
||||
2, 0
|
||||
0, 1
|
||||
1, 1
|
||||
2, 1
|
||||
1, 2
|
||||
2, 2
|
||||
which can be presented as follows:
|
||||
+ + +
|
||||
+ + +
|
||||
+ +
|
||||
the shortest path is:
|
||||
_____
|
||||
|__ |
|
||||
|__|
|
||||
|
||||
so you should print the length of this path that is:
|
||||
8.00
|
||||
|
||||
Write a program that will read a set of city coordinates in the form
|
||||
'%f, %f\n' from the standard input and will print the length of the shortest
|
||||
possible path containing all these cities under the form '%.2f'.
|
||||
|
||||
Your program will not be tested with more than 11 cities.
|
||||
|
||||
You will find in this directory a file tsp.c containing all the boring parts of
|
||||
this exercise and example files to help you test your program.
|
||||
|
||||
hint: in order to use sqrtf, add -lm at the end of your compilation command.
|
||||
|
||||
For example this should work:
|
||||
$> cat square.txt
|
||||
1, 1
|
||||
0, 1
|
||||
1, 0
|
||||
0, 0
|
||||
$> ./tsp < square.txt | cat -e
|
||||
4.00$
|
||||
|
||||
Hint : in order to use sqrtf , add -lm at the end of your compilation command
|
||||
66
level-2/tsp/tsp.c
Normal file
66
level-2/tsp/tsp.c
Normal file
@ -0,0 +1,66 @@
|
||||
#include "tsp.h"
|
||||
|
||||
static float distance(t_city a, t_city b)
|
||||
{
|
||||
float dx = b.x - a.x;
|
||||
float dy = b.y - a.y;
|
||||
return (sqrtf(dx * dx + dy * dy));
|
||||
}
|
||||
|
||||
static float path_length(t_city *cities, int *route, int n)
|
||||
{
|
||||
float total = 0;
|
||||
int i = 0;
|
||||
|
||||
while (i < n)
|
||||
{
|
||||
total += distance(cities[route[i]], cities[route[(i + 1) % n]]);
|
||||
i++;
|
||||
}
|
||||
return (total);
|
||||
}
|
||||
|
||||
static void solve(t_city *cities, int *route, int pos, int n, float *best)
|
||||
{
|
||||
float current;
|
||||
int i, temp;
|
||||
|
||||
if (pos == n)
|
||||
{
|
||||
current = path_length(cities, route, n);
|
||||
if (current < *best)
|
||||
*best = current;
|
||||
return;
|
||||
}
|
||||
i = pos;
|
||||
while (i < n)
|
||||
{
|
||||
temp = route[pos];
|
||||
route[pos] = route[i];
|
||||
route[i] = temp;
|
||||
solve(cities, route, pos + 1, n, best);
|
||||
temp = route[pos];
|
||||
route[pos] = route[i];
|
||||
route[i] = temp;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
t_city cities[11];
|
||||
int route[11];
|
||||
int n = 0;
|
||||
float best = 999999.0f;
|
||||
|
||||
while (n < 11 && fscanf(stdin, "%f, %f", &cities[n].x, &cities[n].y) == 2)
|
||||
{
|
||||
route[n] = n;
|
||||
n++;
|
||||
}
|
||||
if (n < 2)
|
||||
return (1);
|
||||
solve(cities, route, 0, n, &best);
|
||||
fprintf(stdout, "%.2f\n", best);
|
||||
return (0);
|
||||
}
|
||||
14
level-2/tsp/tsp.h
Normal file
14
level-2/tsp/tsp.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef TSP_H
|
||||
# define TSP_H
|
||||
|
||||
# include <stdio.h>
|
||||
# include <stdlib.h>
|
||||
# include <math.h>
|
||||
|
||||
typedef struct s_city
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
} t_city;
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue
Block a user