numbers.c und tests dazu.

Makefile angepasst, aber mein Clion spackt gerade, weshalb ich es nicht ausführen kann.
This commit is contained in:
Simon Ehrnsperger 2025-12-15 10:20:01 +01:00
parent 850c7ed798
commit 6600b12893
4 changed files with 417 additions and 27 deletions

View File

@ -2,14 +2,14 @@ CC = gcc
CFLAGS = -g -Wall -lm CFLAGS = -g -Wall -lm
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
include makefile_windows.variables include makefile_windows.variables
else else
UNAME = $(shell uname) UNAME = $(shell uname)
ifeq ($(UNAME),Linux) ifeq ($(UNAME),Linux)
include makefile_linux.variables include makefile_linux.variables
else else
include makefile_mac.variables include makefile_mac.variables
endif endif
endif endif
raylibfolder = ./raylib raylibfolder = ./raylib
@ -19,7 +19,7 @@ unityfolder = ./unity
# Initiales Programm bauen (zum ausprobieren) # Initiales Programm bauen (zum ausprobieren)
# -------------------------- # --------------------------
doble_initial: doble_initial:
$(CC) -o doble_initial $(BINARIES)/libdoble_complete.a $(CC) -o doble_initial $(BINARIES)/libdoble_complete.a
# -------------------------- # --------------------------
# Selbst implementiertes Programm bauen # 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 program_obj_files = main.o stack.o bintree.o numbers.o timer.o highscore.o
doble : $(program_obj_files) doble : $(program_obj_files)
$(CC) $(CFLAGS) $^ -o doble $(CC) $(CFLAGS) $^ -o doble
# -------------------------- # --------------------------
# Unit Tests # Unit Tests
# -------------------------- # --------------------------
TEST_STACK_EXEC = runtest_stack.exe TEST_STACK_EXEC = runtest_stack.exe
TEST_NUMBERS_EXEC = runtest_numbers.exe # NEU: Ausführbare Datei für numbers-Tests
unitTests: $(TEST_STACK_EXEC) .PHONY: unitTests
@echo "--- Starte Stack Unit Tests ---" unitTests: $(TEST_STACK_EXEC) $(TEST_NUMBERS_EXEC)
@echo "Versuche auszuführen: $(TEST_STACK_EXEC)" @echo "=========================================="
$(TEST_STACK_EXEC) @echo "--- Starte ALLE Unit Tests ---"
@echo "--- Stack Unit Tests abgeschlossen ---" @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 $(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 $(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 $@
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 %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@ $(CC) -c $(CFLAGS) $< -o $@
# -------------------------- # --------------------------
# Clean # Clean
# -------------------------- # --------------------------
clean: clean:
ifeq ($(OS),Windows_NT) ifeq ($(OS),Windows_NT)
del /f *.o *.exe doble $(TEST_STACK_EXEC) del /f *.o *.exe doble $(TEST_STACK_EXEC) $(TEST_NUMBERS_EXEC)
else else
rm -f *.o *.exe doble $(TEST_STACK_EXEC) rm -f *.o *.exe doble $(TEST_STACK_EXEC) $(TEST_NUMBERS_EXEC)
endif endif
.PHONY: doble_initial doble unitTests $(TEST_STACK_EXEC) clean # --- PHONY-Ziele am Ende (Erweitert und bereinigt) ---
.PHONY: doble_initial doble unitTests $(TEST_STACK_EXEC) $(TEST_NUMBERS_EXEC) clean

63
makefile alt Normal file
View File

@ -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

151
numbers.c
View File

@ -5,6 +5,30 @@
#include "numbers.h" #include "numbers.h"
#include "bintree.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 //TODO: getDuplicate und createNumbers implementieren
/* * * Erzeugen eines Arrays mit der vom Nutzer eingegebenen Anzahl an Zufallszahlen. /* * * Erzeugen eines Arrays mit der vom Nutzer eingegebenen Anzahl an Zufallszahlen.
* Sicherstellen, dass beim Befüllen keine Duplikate entstehen. * 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 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 // Returns NULL on errors. Use your implementation of the binary search tree to check for possible duplicates while
// creating random numbers. // 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. // 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;
} }

159
numbersTests.c Normal file
View File

@ -0,0 +1,159 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#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;
}
}