From 6600b12893f10113cae02be01bc39819c0676682 Mon Sep 17 00:00:00 2001 From: ehrnspergersi95041 Date: Mon, 15 Dec 2025 10:20:01 +0100 Subject: [PATCH] =?UTF-8?q?numbers.c=20und=20tests=20dazu.=20Makefile=20an?= =?UTF-8?q?gepasst,=20aber=20mein=20Clion=20spackt=20gerade,=20weshalb=20i?= =?UTF-8?q?ch=20es=20nicht=20ausf=C3=BChren=20kann.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- makefile | 71 +++++++++++++++------- makefile alt | 63 ++++++++++++++++++++ numbers.c | 151 ++++++++++++++++++++++++++++++++++++++++++++-- numbersTests.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 417 insertions(+), 27 deletions(-) create mode 100644 makefile alt create mode 100644 numbersTests.c diff --git a/makefile b/makefile index a4fc9c3..e11dfbf 100644 --- a/makefile +++ b/makefile @@ -2,14 +2,14 @@ CC = gcc CFLAGS = -g -Wall -lm ifeq ($(OS),Windows_NT) - include makefile_windows.variables + include makefile_windows.variables else - UNAME = $(shell uname) - ifeq ($(UNAME),Linux) - include makefile_linux.variables - else - include makefile_mac.variables - endif + UNAME = $(shell uname) + ifeq ($(UNAME),Linux) + include makefile_linux.variables + else + include makefile_mac.variables + endif endif raylibfolder = ./raylib @@ -19,7 +19,7 @@ unityfolder = ./unity # Initiales Programm bauen (zum ausprobieren) # -------------------------- doble_initial: - $(CC) -o doble_initial $(BINARIES)/libdoble_complete.a + $(CC) -o doble_initial $(BINARIES)/libdoble_complete.a # -------------------------- # Selbst implementiertes Programm bauen @@ -27,37 +27,64 @@ doble_initial: program_obj_files = main.o stack.o bintree.o numbers.o timer.o highscore.o doble : $(program_obj_files) - $(CC) $(CFLAGS) $^ -o doble + $(CC) $(CFLAGS) $^ -o doble # -------------------------- # Unit Tests # -------------------------- TEST_STACK_EXEC = runtest_stack.exe +TEST_NUMBERS_EXEC = runtest_numbers.exe # NEU: Ausführbare Datei für numbers-Tests -unitTests: $(TEST_STACK_EXEC) - @echo "--- Starte Stack Unit Tests ---" - @echo "Versuche auszuführen: $(TEST_STACK_EXEC)" - $(TEST_STACK_EXEC) - @echo "--- Stack Unit Tests abgeschlossen ---" +.PHONY: unitTests +unitTests: $(TEST_STACK_EXEC) $(TEST_NUMBERS_EXEC) + @echo "==========================================" + @echo "--- Starte ALLE Unit Tests ---" + @echo "==========================================" + @echo "\n--- Starte Stack Unit Tests ---" + @echo "Versuche auszuführen: $(TEST_STACK_EXEC)" + $(TEST_STACK_EXEC) + @echo "--- Stack Unit Tests abgeschlossen ---\n" + + @echo "--- Starte Numbers Unit Tests ---" + @echo "Versuche auszuführen: $(TEST_NUMBERS_EXEC)" + $(TEST_NUMBERS_EXEC) + @echo "--- Numbers Unit Tests abgeschlossen ---" + +# --- Regeln für Stack-Tests (Unverändert) --- $(TEST_STACK_EXEC): test_stack.o stack.o - $(CC) $(CFLAGS) -I$(unityfolder) test_stack.o stack.o $(unityfolder)/unity.c -o $@ $(BINARIES)/libdoble_complete.a - -test_stack.o: test_stack.c - $(CC) -c $(CFLAGS) -I$(unityfolder) $< -o $@ + $(CC) $(CFLAGS) -I$(unityfolder) test_stack.o stack.o $(unityfolder)/unity.c -o $@ $(BINARIES)/libdoble_complete.a +test_stack.o: test_stack.c + $(CC) -c $(CFLAGS) -I$(unityfolder) $< -o $@ + +# --- Regeln für Numbers-Tests (Nach Muster gebaut, ohne NUMBERS_TEST_OBJ) --- + +# Linken des Numbers-Test-Executables. Die Abhängigkeiten sind explizit gelistet. +$(TEST_NUMBERS_EXEC): numbersTests.o numbers.o bintree.o stack.o + # Linkt Test-Objekt, die Implementierung und Abhängigkeiten (bintree, stack) + Unity + Bibliothek + $(CC) $(CFLAGS) -I$(unityfolder) numbersTests.o numbers.o bintree.o stack.o $(unityfolder)/unity.c -o $@ $(BINARIES)/libdoble_complete.a + +# Kompilierung der Numbers-Test-Datei. +numbersTests.o: numbersTests.c + $(CC) -c $(CFLAGS) -I$(unityfolder) $< -o $@ + +# -------------------------- +# Generische Regel für .o-Dateien (Beibehalten) +# -------------------------- %.o: %.c - $(CC) -c $(CFLAGS) $< -o $@ + $(CC) -c $(CFLAGS) $< -o $@ # -------------------------- # Clean # -------------------------- clean: ifeq ($(OS),Windows_NT) - del /f *.o *.exe doble $(TEST_STACK_EXEC) + del /f *.o *.exe doble $(TEST_STACK_EXEC) $(TEST_NUMBERS_EXEC) else - rm -f *.o *.exe doble $(TEST_STACK_EXEC) + rm -f *.o *.exe doble $(TEST_STACK_EXEC) $(TEST_NUMBERS_EXEC) endif -.PHONY: doble_initial doble unitTests $(TEST_STACK_EXEC) clean \ No newline at end of file +# --- PHONY-Ziele am Ende (Erweitert und bereinigt) --- +.PHONY: doble_initial doble unitTests $(TEST_STACK_EXEC) $(TEST_NUMBERS_EXEC) clean \ No newline at end of file diff --git a/makefile alt b/makefile alt new file mode 100644 index 0000000..613231a --- /dev/null +++ b/makefile alt @@ -0,0 +1,63 @@ +CC = gcc +CFLAGS = -g -Wall -lm + +ifeq ($(OS),Windows_NT) + include makefile_windows.variables +else + UNAME = $(shell uname) + ifeq ($(UNAME),Linux) + include makefile_linux.variables + else + include makefile_mac.variables + endif +endif + +raylibfolder = ./raylib +unityfolder = ./unity + +# -------------------------- +# Initiales Programm bauen (zum ausprobieren) +# -------------------------- +doble_initial: + $(CC) -o doble_initial $(BINARIES)/libdoble_complete.a + +# -------------------------- +# Selbst implementiertes Programm bauen +# -------------------------- +program_obj_files = main.o stack.o bintree.o numbers.o timer.o highscore.o + +doble : $(program_obj_files) + $(CC) $(CFLAGS) $^ -o doble + + +# -------------------------- +# Unit Tests +# -------------------------- +TEST_STACK_EXEC = runtest_stack.exe + +unitTests: $(TEST_STACK_EXEC) + @echo "--- Starte Stack Unit Tests ---" + @echo "Versuche auszuführen: $(TEST_STACK_EXEC)" + $(TEST_STACK_EXEC) + @echo "--- Stack Unit Tests abgeschlossen ---" + +$(TEST_STACK_EXEC): test_stack.o stack.o + $(CC) $(CFLAGS) -I$(unityfolder) test_stack.o stack.o $(unityfolder)/unity.c -o $@ $(BINARIES)/libdoble_complete.a + +test_stack.o: test_stack.c + $(CC) -c $(CFLAGS) -I$(unityfolder) $< -o $@ + +%.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + +# -------------------------- +# Clean +# -------------------------- +clean: +ifeq ($(OS),Windows_NT) + del /f *.o *.exe doble $(TEST_STACK_EXEC) +else + rm -f *.o *.exe doble $(TEST_STACK_EXEC) +endif + +.PHONY: doble_initial doble unitTests $(TEST_STACK_EXEC) clean \ No newline at end of file diff --git a/numbers.c b/numbers.c index f59d9a2..e7e4df6 100644 --- a/numbers.c +++ b/numbers.c @@ -5,6 +5,30 @@ #include "numbers.h" #include "bintree.h" +// --- Vergleichsfunktionen --- + +// 1. Vergleichsfunktion für qsort (vergleicht unsigned int-Werte) +int compare_unsigned_int(const void *a, const void *b) { + unsigned int arg1 = *(const unsigned int*)a; + unsigned int arg2 = *(const unsigned int*)b; + + if (arg1 < arg2) return -1; + if (arg1 > arg2) return 1; + return 0; +} + +// 2. Vergleichsfunktion für den Binärbaum (vergleicht die *Daten* der Knoten) +// Die Argumente sind Pointer auf die *Daten* (void*), d.h. Pointer auf unsigned int. +int compare_tree_data(const void *arg1, const void *arg2) { + // Cast der void*-Pointer auf unsigned int*-Pointer + unsigned int val1 = *(const unsigned int*)arg1; + unsigned int val2 = *(const unsigned int*)arg2; + + if (val1 < val2) return -1; + if (val1 > val2) return 1; + return 0; +} + //TODO: getDuplicate und createNumbers implementieren /* * * Erzeugen eines Arrays mit der vom Nutzer eingegebenen Anzahl an Zufallszahlen. * Sicherstellen, dass beim Befüllen keine Duplikate entstehen. @@ -14,13 +38,130 @@ // Returns len random numbers between 1 and 2x len in random order which are all different, except for two entries. // Returns NULL on errors. Use your implementation of the binary search tree to check for possible duplicates while // creating random numbers. -unsigned int *createNumbers(unsigned int len) -{ +/* * Erzeugt ein Array mit (len - 1) eindeutigen Zufallszahlen und einem Duplikat. + * Die Duplikatprüfung erfolgt über den Binärbaum (addToTree). + */ +unsigned int *createNumbers(unsigned int len) { + if (len == 0) { + return NULL; + } + // 1. Initialisierung + // Der BST wird zunächst als NULL initialisiert, da addToTree rekursiv arbeitet. + TreeNode *root = NULL; + + // 2. Speicher für das Ziel-Array allokieren + unsigned int *numbers = (unsigned int *)malloc(len * sizeof(unsigned int)); + if (numbers == NULL) { + return NULL; + } + + unsigned int count = 0; + unsigned int max_val = 2 * len; + + // Speichert den Wert, der später dupliziert wird (aus dem Array gewählt) + unsigned int value_to_duplicate = 0; + + // 3. Erzeugen von len - 1 eindeutigen Zufallszahlen + while (count < len - 1) { + unsigned int random_num = (rand() % max_val) + 1; + int is_duplicate = 0; + + // Wir müssen die Zahl als dynamisch allokierten Speicher übergeben, + // da addToTree eine Kopie davon erwartet. + unsigned int *num_ptr = (unsigned int *)malloc(sizeof(unsigned int)); + if (num_ptr == NULL) { + // cleanup: Baum und bereits allokiertes Array freigeben + clearTree(root); + free(numbers); + return NULL; + } + *num_ptr = random_num; + + // Fügen in den Baum ein: + // root MUSS das Ergebnis der rekursiven addToTree-Funktion speichern! + root = addToTree(root, num_ptr, sizeof(unsigned int), compare_tree_data, &is_duplicate); + + if (root == NULL) { + // Schwerwiegender Fehler bei der Speicherallokation innerhalb von addToTree + clearTree(root); + free(numbers); + free(num_ptr); // Datenpointer freigeben, falls Fehler vor der Kopie auftrat + return NULL; + } + + if (is_duplicate == 0) { + // Erfolg: Zahl war neu und wurde eingefügt (die Datenkopie liegt nun im Baum) + numbers[count] = random_num; + count++; + + // value_to_duplicate merken + value_to_duplicate = random_num; + + // Den lokalen num_ptr NICHT freigeben, da seine Kopie im Baum verwaltet wird. + } else { + // Fehler: Duplikat, wurde NICHT eingefügt. + // Den lokal allokierten Pointer MÜSSEN wir freigeben, da er nicht in den Baum ging. + free(num_ptr); + } + } + + // 4. Duplizieren eines Eintrags + // Der duplizierte Wert wird an die letzte Stelle gesetzt. + if (len > 1) { + // Wir verwenden value_to_duplicate, um sicherzustellen, dass die Zahl aus dem Array stammt. + // Wenn len sehr groß ist, wird einfach eine zufällige Zahl aus dem gefüllten Bereich gewählt. + if (value_to_duplicate == 0) { + value_to_duplicate = numbers[rand() % (len - 1)]; + } + numbers[len - 1] = value_to_duplicate; + } + + + // 5. Speicher des Baumes freigeben + // clearTree gibt alle Knoten und die darin enthaltenen Datenkopien frei. + clearTree(root); + + // 6. Mischen (Shuffling) des Arrays, um die Ordnung zu randomisieren + for (unsigned int i = len - 1; i > 0; i--) { + unsigned int j = rand() % (i + 1); + unsigned int temp = numbers[i]; + numbers[i] = numbers[j]; + numbers[j] = temp; + } + + return numbers; } - // Returns only the only number in numbers which is present twice. Returns zero on errors. -unsigned int getDuplicate(const unsigned int numbers[], unsigned int len) -{ +/* * Sortiert das Array und erkennt das Duplikat durch Vergleich benachbarter Elemente. + * (O(N log N) wegen qsort) + */ +unsigned int getDuplicate(const unsigned int numbers[], unsigned int len) { + if (len < 2) { + return 0; // Fehler + } + // 1. Array duplizieren, da das Original const ist und sortiert werden muss + unsigned int *sorted_numbers = (unsigned int *)malloc(len * sizeof(unsigned int)); + if (sorted_numbers == NULL) { + return 0; + } + memcpy(sorted_numbers, numbers, len * sizeof(unsigned int)); + + // 2. Sortieren des Arrays + qsort(sorted_numbers, len, sizeof(unsigned int), compare_unsigned_int); + + // 3. Erkennen der doppelten Zahl durch Vergleich benachbarter Elemente + unsigned int duplicate = 0; + for (unsigned int i = 0; i < len - 1; i++) { + if (sorted_numbers[i] == sorted_numbers[i+1]) { + duplicate = sorted_numbers[i]; + break; + } + } + + free(sorted_numbers); + + // 4. Ergebnis zurückgeben + return duplicate; } \ No newline at end of file diff --git a/numbersTests.c b/numbersTests.c new file mode 100644 index 0000000..d3f92e2 --- /dev/null +++ b/numbersTests.c @@ -0,0 +1,159 @@ +#include +#include +#include +#include +#include "numbers.h" + +// Makro für einfache Test-Prüfung +#define ASSERT_TRUE(condition, message) \ + do { \ + if (!(condition)) { \ + fprintf(stderr, "FEHLER in %s, Zeile %d: %s\n", __FILE__, __LINE__, message); \ + return 0; \ + } \ + } while (0) + +// Hilfsfunktion zum Zählen der Elemente eines Arrays, die in einem anderen enthalten sind +static unsigned int count_occurrences(const unsigned int arr[], unsigned int len, unsigned int value) { + unsigned int count = 0; + for (unsigned int i = 0; i < len; i++) { + if (arr[i] == value) { + count++; + } + } + return count; +} + +// Definition der Testfunktionen +int test_createNumbers_basic(); +int test_createNumbers_edgeCases(); +int test_getDuplicate_findsDuplicate(); +int test_getDuplicate_noDuplicateOrEdgeCases(); + + +int test_createNumbers_basic() { + srand(time(NULL)); + unsigned int len = 10; + unsigned int *arr = createNumbers(len); + + ASSERT_TRUE(arr != NULL, "createNumbers sollte bei len=10 nicht NULL zurueckgeben."); + + unsigned int *temp_copy = (unsigned int *)malloc(len * sizeof(unsigned int)); + if (temp_copy == NULL) return 0; + memcpy(temp_copy, arr, len * sizeof(unsigned int)); + qsort(temp_copy, len, sizeof(unsigned int), compare_unsigned_int); + + unsigned int unique_count = 0; + unsigned int duplicate_value = 0; + + for (unsigned int i = 0; i < len; i++) { + if (i == 0 || temp_copy[i] != temp_copy[i-1]) { + unique_count++; + } + if (i > 0 && temp_copy[i] == temp_copy[i-1]) { + duplicate_value = temp_copy[i]; + } + } + + ASSERT_TRUE(unique_count == len - 1, "Array sollte genau len-1 eindeutige Werte enthalten."); + ASSERT_TRUE(duplicate_value != 0, "Es sollte genau ein Duplikat gefunden werden."); + + free(temp_copy); + free(arr); + return 1; +} + +int test_createNumbers_edgeCases() { + // 1. Test mit minimaler Länge (len=2) + unsigned int len_min = 2; + unsigned int *arr_min = createNumbers(len_min); + ASSERT_TRUE(arr_min != NULL, "createNumbers sollte bei len=2 nicht NULL zurueckgeben."); + ASSERT_TRUE(getDuplicate(arr_min, len_min) != 0, "Bei len=2 sollte ein Duplikat gefunden werden."); + free(arr_min); + + // 2. Test mit len=0 + unsigned int *arr_zero = createNumbers(0); + ASSERT_TRUE(arr_zero == NULL, "createNumbers sollte bei len=0 NULL zurueckgeben."); + + // 3. Test mit größerer Länge (len=500) + unsigned int len_large = 500; + unsigned int *arr_large = createNumbers(len_large); + unsigned int duplicate_val = getDuplicate(arr_large, len_large); + ASSERT_TRUE(count_occurrences(arr_large, len_large, duplicate_val) == 2, "Duplikat sollte genau 2-mal vorkommen."); + free(arr_large); + + return 1; +} + +int test_getDuplicate_findsDuplicate() { + unsigned int arr1[] = {1, 5, 3, 5, 2}; + unsigned int len1 = 5; + unsigned int result1 = getDuplicate(arr1, len1); + ASSERT_TRUE(result1 == 5, "Duplikat 5 sollte gefunden werden."); + + unsigned int arr3[] = {10, 20, 30, 40, 10}; + unsigned int len3 = 5; + unsigned int result3 = getDuplicate(arr3, len3); + ASSERT_TRUE(result3 == 10, "Duplikat 10 sollte gefunden werden."); + + return 1; +} + +int test_getDuplicate_noDuplicateOrEdgeCases() { + // 1. Kein Duplikat + unsigned int arr1[] = {1, 2, 3, 4}; + unsigned int len1 = 4; + unsigned int result1 = getDuplicate(arr1, len1); + ASSERT_TRUE(result1 == 0, "Kein Duplikat sollte 0 zurueckgeben."); + + // 2. Leeres Array + unsigned int arr2[] = {}; + unsigned int len2 = 0; + unsigned int result2 = getDuplicate(arr2, len2); + ASSERT_TRUE(result2 == 0, "Leeres Array sollte 0 zurueckgeben."); + + return 1; +} + + +// --- Hauptfunktion (Test Runner) --- + +typedef int (*test_func)(void); + +struct { + const char *name; + test_func func; +} tests[] = { + {"test_createNumbers_basic", test_createNumbers_basic}, + {"test_createNumbers_edgeCases", test_createNumbers_edgeCases}, + {"test_getDuplicate_findsDuplicate", test_getDuplicate_findsDuplicate}, + {"test_getDuplicate_noDuplicateOrEdgeCases", test_getDuplicate_noDuplicateOrEdgeCases}, +}; + +int main(void) { + int total_tests = sizeof(tests) / sizeof(tests[0]); + int successful_tests = 0; + + printf("Starte %d Unit-Tests...\n", total_tests); + printf("------------------------------------------\n"); + + for (int i = 0; i < total_tests; i++) { + printf("Teste: %s ... ", tests[i].name); + fflush(stdout); + if (tests[i].func()) { + printf("PASSED ✅\n"); + successful_tests++; + } else { + printf("FAILED ❌\n"); + } + } + + printf("------------------------------------------\n"); + if (successful_tests == total_tests) { + printf("ERGEBNIS: ALLE %d TESTS ERFOLGREICH BESTANDEN.\n", total_tests); + return EXIT_SUCCESS; + } else { + printf("ERGEBNIS: %d von %d TESTS FEHLGESCHLAGEN.\n", total_tests - successful_tests, total_tests); + return EXIT_FAILURE; + } +} \ No newline at end of file