From 259d4ef6f37f2dfe491d2bd1bc8fbf5b4fe2d0ff Mon Sep 17 00:00:00 2001 From: Rui Ribeiro <42305006+ruiribeiro04@users.noreply.github.com> Date: Tue, 23 Sep 2025 08:41:21 +0100 Subject: [PATCH] added level-1 --- level-1/broken_gnl/subject/broken_gnl.c | 87 +++++++ level-1/broken_gnl/subject/broken_gnl.h | 10 + level-1/filter/filter.c | 158 ++++++++++++ level-1/filter/subject.txt | 52 ++++ level-1/scanf/ft_scanf | 323 ++++++++++++++++++++++++ level-1/scanf/subject.txt | 120 +++++++++ level-2/n_queens/Makefile | 74 ++++++ level-2/n_queens/README.md | 252 ++++++++++++++++++ level-2/n_queens/n_queens.c | 84 ++++++ level-2/n_queens/n_queens.h | 13 + level-2/n_queens/subject.txt | 28 ++ level-2/permutations/Makefile | 25 ++ level-2/permutations/README.md | 200 +++++++++++++++ level-2/permutations/permutations.c | 98 +++++++ level-2/permutations/permutations.h | 14 + level-2/permutations/subject.txt | 27 ++ level-2/powerset/Makefile | 71 ++++++ level-2/powerset/README.md | 175 +++++++++++++ level-2/powerset/powerset.c | 73 ++++++ level-2/powerset/powerset.h | 12 + level-2/powerset/subject.txt | 65 +++++ level-2/rip/Makefile | 80 ++++++ level-2/rip/README.md | 179 +++++++++++++ level-2/rip/rip.c | 180 +++++++++++++ level-2/rip/subject.txt | 28 ++ level-2/tsp/Makefile | 26 ++ level-2/tsp/TSP_Algorithm_Explained.md | 247 ++++++++++++++++++ level-2/tsp/square.txt | 8 + level-2/tsp/subject.txt | 64 +++++ level-2/tsp/tsp.c | 66 +++++ level-2/tsp/tsp.h | 14 + 31 files changed, 2853 insertions(+) create mode 100644 level-1/broken_gnl/subject/broken_gnl.c create mode 100644 level-1/broken_gnl/subject/broken_gnl.h create mode 100644 level-1/filter/filter.c create mode 100644 level-1/filter/subject.txt create mode 100644 level-1/scanf/ft_scanf create mode 100644 level-1/scanf/subject.txt create mode 100644 level-2/n_queens/Makefile create mode 100644 level-2/n_queens/README.md create mode 100644 level-2/n_queens/n_queens.c create mode 100644 level-2/n_queens/n_queens.h create mode 100644 level-2/n_queens/subject.txt create mode 100644 level-2/permutations/Makefile create mode 100644 level-2/permutations/README.md create mode 100644 level-2/permutations/permutations.c create mode 100644 level-2/permutations/permutations.h create mode 100644 level-2/permutations/subject.txt create mode 100644 level-2/powerset/Makefile create mode 100644 level-2/powerset/README.md create mode 100644 level-2/powerset/powerset.c create mode 100644 level-2/powerset/powerset.h create mode 100644 level-2/powerset/subject.txt create mode 100644 level-2/rip/Makefile create mode 100644 level-2/rip/README.md create mode 100644 level-2/rip/rip.c create mode 100644 level-2/rip/subject.txt create mode 100644 level-2/tsp/Makefile create mode 100644 level-2/tsp/TSP_Algorithm_Explained.md create mode 100644 level-2/tsp/square.txt create mode 100644 level-2/tsp/subject.txt create mode 100644 level-2/tsp/tsp.c create mode 100644 level-2/tsp/tsp.h diff --git a/level-1/broken_gnl/subject/broken_gnl.c b/level-1/broken_gnl/subject/broken_gnl.c new file mode 100644 index 0000000..8f0676c --- /dev/null +++ b/level-1/broken_gnl/subject/broken_gnl.c @@ -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); +} diff --git a/level-1/broken_gnl/subject/broken_gnl.h b/level-1/broken_gnl/subject/broken_gnl.h new file mode 100644 index 0000000..974c396 --- /dev/null +++ b/level-1/broken_gnl/subject/broken_gnl.h @@ -0,0 +1,10 @@ +#ifndef GNL +# define GNL + +# ifndef BUFFER_SIZE +# define BUFFER_SIZE 10 +# endif + +char *get_next_line(int fd); + +#endif diff --git a/level-1/filter/filter.c b/level-1/filter/filter.c new file mode 100644 index 0000000..e51a50f --- /dev/null +++ b/level-1/filter/filter.c @@ -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 +#include +#include +#include +#include + +// 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 + */ \ No newline at end of file diff --git a/level-1/filter/subject.txt b/level-1/filter/subject.txt new file mode 100644 index 0000000..fabac03 --- /dev/null +++ b/level-1/filter/subject.txt @@ -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 + +perror includes: + #include + +read includes: + #include \ No newline at end of file diff --git a/level-1/scanf/ft_scanf b/level-1/scanf/ft_scanf new file mode 100644 index 0000000..f526d9d --- /dev/null +++ b/level-1/scanf/ft_scanf @@ -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 +#include +#include + +// 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 + */ \ No newline at end of file diff --git a/level-1/scanf/subject.txt b/level-1/scanf/subject.txt new file mode 100644 index 0000000..58562ee --- /dev/null +++ b/level-1/scanf/subject.txt @@ -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 +#include +#include + +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; +} \ No newline at end of file diff --git a/level-2/n_queens/Makefile b/level-2/n_queens/Makefile new file mode 100644 index 0000000..268ca72 --- /dev/null +++ b/level-2/n_queens/Makefile @@ -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 \ No newline at end of file diff --git a/level-2/n_queens/README.md b/level-2/n_queens/README.md new file mode 100644 index 0000000..2d5a27b --- /dev/null +++ b/level-2/n_queens/README.md @@ -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! 🚀 \ No newline at end of file diff --git a/level-2/n_queens/n_queens.c b/level-2/n_queens/n_queens.c new file mode 100644 index 0000000..45d7340 --- /dev/null +++ b/level-2/n_queens/n_queens.c @@ -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); +} \ No newline at end of file diff --git a/level-2/n_queens/n_queens.h b/level-2/n_queens/n_queens.h new file mode 100644 index 0000000..47d8053 --- /dev/null +++ b/level-2/n_queens/n_queens.h @@ -0,0 +1,13 @@ +#ifndef N_QUEENS_H +# define N_QUEENS_H + +# include +# include +# include + +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 \ No newline at end of file diff --git a/level-2/n_queens/subject.txt b/level-2/n_queens/subject.txt new file mode 100644 index 0000000..9957439 --- /dev/null +++ b/level-2/n_queens/subject.txt @@ -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 : + ... \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... \ No newline at end of file diff --git a/level-2/permutations/Makefile b/level-2/permutations/Makefile new file mode 100644 index 0000000..cc23f7c --- /dev/null +++ b/level-2/permutations/Makefile @@ -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 \ No newline at end of file diff --git a/level-2/permutations/README.md b/level-2/permutations/README.md new file mode 100644 index 0000000..4621e13 --- /dev/null +++ b/level-2/permutations/README.md @@ -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!* \ No newline at end of file diff --git a/level-2/permutations/permutations.c b/level-2/permutations/permutations.c new file mode 100644 index 0000000..7903081 --- /dev/null +++ b/level-2/permutations/permutations.c @@ -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); +} \ No newline at end of file diff --git a/level-2/permutations/permutations.h b/level-2/permutations/permutations.h new file mode 100644 index 0000000..1ee8c38 --- /dev/null +++ b/level-2/permutations/permutations.h @@ -0,0 +1,14 @@ +#ifndef PERMUTATIONS_H +# define PERMUTATIONS_H + +# include +# include +# include + +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 \ No newline at end of file diff --git a/level-2/permutations/subject.txt b/level-2/permutations/subject.txt new file mode 100644 index 0000000..52fece3 --- /dev/null +++ b/level-2/permutations/subject.txt @@ -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$ \ No newline at end of file diff --git a/level-2/powerset/Makefile b/level-2/powerset/Makefile new file mode 100644 index 0000000..d3fa3ab --- /dev/null +++ b/level-2/powerset/Makefile @@ -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 \ No newline at end of file diff --git a/level-2/powerset/README.md b/level-2/powerset/README.md new file mode 100644 index 0000000..ef51e34 --- /dev/null +++ b/level-2/powerset/README.md @@ -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?* 🤖✨ \ No newline at end of file diff --git a/level-2/powerset/powerset.c b/level-2/powerset/powerset.c new file mode 100644 index 0000000..851f4af --- /dev/null +++ b/level-2/powerset/powerset.c @@ -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); +} \ No newline at end of file diff --git a/level-2/powerset/powerset.h b/level-2/powerset/powerset.h new file mode 100644 index 0000000..1fe88c2 --- /dev/null +++ b/level-2/powerset/powerset.h @@ -0,0 +1,12 @@ +#ifndef POWERSET_H +# define POWERSET_H + +# include +# include +# include + +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 \ No newline at end of file diff --git a/level-2/powerset/subject.txt b/level-2/powerset/subject.txt new file mode 100644 index 0000000..4e2911a --- /dev/null +++ b/level-2/powerset/subject.txt @@ -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 +... \ No newline at end of file diff --git a/level-2/rip/Makefile b/level-2/rip/Makefile new file mode 100644 index 0000000..aeff5b2 --- /dev/null +++ b/level-2/rip/Makefile @@ -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 \ No newline at end of file diff --git a/level-2/rip/README.md b/level-2/rip/README.md new file mode 100644 index 0000000..84671e4 --- /dev/null +++ b/level-2/rip/README.md @@ -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! \ No newline at end of file diff --git a/level-2/rip/rip.c b/level-2/rip/rip.c new file mode 100644 index 0000000..960ae96 --- /dev/null +++ b/level-2/rip/rip.c @@ -0,0 +1,180 @@ +#include +#include + +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); +} \ No newline at end of file diff --git a/level-2/rip/subject.txt b/level-2/rip/subject.txt new file mode 100644 index 0000000..0958bd5 --- /dev/null +++ b/level-2/rip/subject.txt @@ -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 +(() ) $ +( )( ) $ +( ) () $ + ()( ) $ \ No newline at end of file diff --git a/level-2/tsp/Makefile b/level-2/tsp/Makefile new file mode 100644 index 0000000..dc5ca14 --- /dev/null +++ b/level-2/tsp/Makefile @@ -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 \ No newline at end of file diff --git a/level-2/tsp/TSP_Algorithm_Explained.md b/level-2/tsp/TSP_Algorithm_Explained.md new file mode 100644 index 0000000..42531ed --- /dev/null +++ b/level-2/tsp/TSP_Algorithm_Explained.md @@ -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! 🎉 \ No newline at end of file diff --git a/level-2/tsp/square.txt b/level-2/tsp/square.txt new file mode 100644 index 0000000..1673c5b --- /dev/null +++ b/level-2/tsp/square.txt @@ -0,0 +1,8 @@ +0, 0 +1, 0 +2, 0 +0, 1 +1, 1 +2, 1 +5, 2 +2, 2 \ No newline at end of file diff --git a/level-2/tsp/subject.txt b/level-2/tsp/subject.txt new file mode 100644 index 0000000..afc2bf0 --- /dev/null +++ b/level-2/tsp/subject.txt @@ -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 \ No newline at end of file diff --git a/level-2/tsp/tsp.c b/level-2/tsp/tsp.c new file mode 100644 index 0000000..7bb869f --- /dev/null +++ b/level-2/tsp/tsp.c @@ -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); +} \ No newline at end of file diff --git a/level-2/tsp/tsp.h b/level-2/tsp/tsp.h new file mode 100644 index 0000000..8d0091d --- /dev/null +++ b/level-2/tsp/tsp.h @@ -0,0 +1,14 @@ +#ifndef TSP_H +# define TSP_H + +# include +# include +# include + +typedef struct s_city +{ + float x; + float y; +} t_city; + +#endif \ No newline at end of file