diff --git a/bintree.c b/bintree.c index 5cf82a9..ca05ad8 100644 --- a/bintree.c +++ b/bintree.c @@ -8,29 +8,177 @@ * `treeSize`: zählt die Knoten im Baum (rekursiv), * `nextTreeData`: Traversierung mit Hilfe des zuvor implementierten Stacks. */ + + +//Hilfsfunktion für addToTree. Erstellt eine treenode. +static TreeNode* createTreeNode(const void *data, size_t dataSize) +{ + TreeNode* newNode = calloc(1, sizeof(TreeNode)); + if(!newNode) + { + return NULL; + } + newNode ->data = malloc(dataSize); + if(!newNode->data) + { + free(newNode); + return NULL; + } + memcpy(newNode -> data, data, dataSize); + return newNode; +} + // Adds a copy of data's pointer destination to the tree using compareFct for ordering. Accepts duplicates -// if isDuplicate is NULL, otherwise ignores duplicates and sets isDuplicate to 1 (or to 0 if a new entry is added). +// if isDuplicate is NULL, otherwise ignores duplicates and sets isDuplicate to 1 (or to 0 if a new entry is added). (auf 1 wenn duplikat geaddet) TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate) { - + if(!root) + { + TreeNode *newNode = createTreeNode(data, dataSize); + if(isDuplicate != NULL) + { + *isDuplicate = 0; + } + return newNode; + } + int compare = compareFct(data, root-> data); + if(compare < 0) + { + root -> left = addToTree(root -> left, data, dataSize, compareFct, isDuplicate); + } + else if(compare > 0) + { + root -> right = addToTree(root -> right, data, dataSize, compareFct, isDuplicate); + } + else + { + if(isDuplicate != NULL) + { + *isDuplicate = 1; + return root; + } + //Konvention: rechts ist >= also das Duplikat wird nach rechts verfrachtet. + root -> right = addToTree(root -> right, data, dataSize, compareFct, isDuplicate); + } + return root; } + // Iterates over the tree given by root. Follows the usage of strtok. If tree is NULL, the next entry of the last tree given is returned in ordering direction. // Use your implementation of a stack to organize the iterator. Push the root node and all left nodes first. On returning the next element, // push the top node and push all its left nodes. +// Wir brauchen eine statische Variable, die überdauernd existiert +// (Alternativ kann man diese auch global ausserhalb definieren) +// Die statische Variable (das Gedächtnis) muss außerhalb oder static innerhalb sein + +/* + * nextTreeData - Iterative In-Order Traversierung (wie strtok) + * * Funktionsweise: + * 1. Initialisierung (root != NULL): + * - Löscht alten Stack. + * - Wandert von root so weit nach LINKS wie möglich. + * - Pushed alle Knoten auf dem Weg auf den Stack. + * -> Das kleinste Element liegt nun oben. + * * 2. Iteration (root == NULL): + * - Pop: Nimmt oberstes Element vom Stack (aktuell kleinstes). + * - Logik: Hat dieses Element einen RECHTEN Nachbarn? + * -> JA: Gehe eins nach rechts, dann wieder alles nach LINKS pushen. + * -> NEIN: Nichts tun (der Elternknoten liegt schon als nächstes auf dem Stack). + * - Gibt die Daten des gepoppten Elements zurück. + */ +static StackNode *iteratorStack = NULL; + void *nextTreeData(TreeNode *root) { + //neuer Baum wird übergeben (root != NULL) + if (root != NULL) + { + // 1. Aufräumen: Falls noch Reste vom letzten Mal da sind + if (iteratorStack != NULL) { + clearStack(iteratorStack); + iteratorStack = NULL; + } + // 2. Initial befüllen: "Push root and all left nodes" + TreeNode *currentNode = root; + while (currentNode != NULL) + { + + iteratorStack = push(iteratorStack, currentNode); + + // Immer weiter nach links absteigen + currentNode = currentNode->left; + } + } + + // PHASE 2: Iteration (Nächsten Wert holen) + + // Wenn der Stack leer ist (oder leer war), sind wir fertig. + if (iteratorStack == NULL) + { + return NULL; + } + + // 1. Wir schauen uns das oberste Element an (der nächste Knoten in der Reihe) + // Wir wissen, dass es ein TreeNode* ist, also casten wir. + TreeNode *nodeToReturn = (TreeNode*) top(iteratorStack); + + // 2. Wir entfernen ihn vom Stack (er ist jetzt "verarbeitet") + // Auch hier: pop gibt den neuen Head zurück, also variable aktualisieren! + iteratorStack = pop(iteratorStack); + + // 3. Wir retten die Nutzer-Daten (z.B. den Integer), bevor wir weiterwandern + void *userData = nodeToReturn->data; + + // 4. Nachfolger suchen (Die Logik für In-Order: Rechts, dann alles links) + if (nodeToReturn->right != NULL) + { + TreeNode *currentNode = nodeToReturn->right; + while (currentNode != NULL) + { + // Auch hier: Stack aktualisieren + iteratorStack = push(iteratorStack, currentNode); + currentNode = currentNode->left; + } + } + + // Wir geben die echten Daten zurück (nicht den Knoten, sondern den Inhalt) + return userData; } // Releases all memory resources (including data copies). + +// Gibt den gesamten Speicher (Knoten + Daten) frei void clearTree(TreeNode *root) { + if (root) + { + // 2. Rekursion: Erst tief in den Baum absteigen (Post-Order) + clearTree(root->left); + clearTree(root->right); + // 3. Jetzt sind die Kinder weg. Wir kümmern uns um den aktuellen Knoten. + + // Erst den Inhalt (die Datenkopie) löschen! + // (free(NULL) ist in C erlaubt, daher müssen wir nicht zwingend auf NULL prüfen, + // aber es schadet auch nicht). + free(root->data); + + // 4. Dann den Container (den Knoten selbst) löschen + free(root); + } } // Returns the number of entries in the tree given by root. unsigned int treeSize(const TreeNode *root) { + // Abbruchbedingung: Wenn kein Knoten da ist, ist die Größe 0 + if (root == NULL) + { + return 0; + } + // Rekursionsschritt: + // 1 (für den aktuellen Knoten) + alles im linken Baum + alles im rechten Baum + return 1 + treeSize(root->left) + treeSize(root->right); } \ No newline at end of file diff --git a/bintree.o b/bintree.o new file mode 100644 index 0000000..f1ce014 Binary files /dev/null and b/bintree.o differ diff --git a/doble b/doble new file mode 100755 index 0000000..b3bff35 Binary files /dev/null and b/doble differ diff --git a/exportToHTML/numbers.c.html b/exportToHTML/numbers.c.html new file mode 100644 index 0000000..0b3baea --- /dev/null +++ b/exportToHTML/numbers.c.html @@ -0,0 +1,151 @@ + +
+#include <stdlib.h> +#include <stdio.h> +#include <time.h> +#include <string.h> +#include "numbers.h" +#include "bintree.h" + +//TODO: getDuplicate und createNumbers implementieren +/* * * Erzeugen eines Arrays mit der vom Nutzer eingegebenen Anzahl an Zufallszahlen. + * Sicherstellen, dass beim Befüllen keine Duplikate entstehen. + * Duplizieren eines zufälligen Eintrags im Array. + * in `getDuplicate()`: Sortieren des Arrays und Erkennen der doppelten Zahl durch Vergleich benachbarter Elemente. */ + +// 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. +// Vergleichsfunktion für qsort +static int compareUnsignedInt(const void *a, const void *b) +{ +const unsigned int *x = (const unsigned int *)a; + const unsigned int *y = (const unsigned int *)b; + + if (*x < *y) return -1; + if (*x > *y) return 1; + return 0; +} + +// Fisher-Yates Shuffle Algorithmus zum Mischen des Arrays +static void shuffleArray(unsigned int *array, unsigned int n) +{ + if (n > 1) + { + for (unsigned int i = n - 1; i > 0; i--) + { + unsigned int j = rand() % (i + 1); + unsigned int temp = array[i]; + array[i] = array[j]; + array[j] = temp; + } + } +} + +unsigned int *createNumbers(unsigned int len) +{ + if (len < 2) return NULL; + + // 1. Array reservieren + unsigned int *numbers = malloc(len * sizeof(unsigned int)); + if (numbers == NULL) return NULL; + + // Hilfsvariablen für den Baum + TreeNode *root = NULL; + int isDuplicate = 0; + unsigned int count = 0; + + // 2. PHASE 1: Erzeuge (len - 1) EINZIGARTIGE Zahlen mit Hilfe des Baums + // Wir nutzen den Baum als "Türsteher" + while (count < len - 1) + { + // Zufallszahl generieren (1 bis 2*len) + unsigned int value = (rand() % (2 * len)) + 1; + + // Versuchen, in den Baum einzufügen + // Wir übergeben &isDuplicate, damit der Baum Duplikate ABLEHNT. + root = addToTree(root, &value, sizeof(unsigned int), compareUnsignedInt, &isDuplicate); + + // Prüfen: War es ein Duplikat? + if (isDuplicate == 0) + { + // Nein, es war neu! -> Ins Array schreiben + numbers[count] = value; + count++; + } + // Falls isDuplicate == 1, machen wir einfach weiter (while-Schleife läuft weiter) + } + + // 3. PHASE 2: Das garantierte Duplikat erzeugen + // Wir wählen zufällig eine der bereits existierenden Zahlen aus + unsigned int randomIndex = rand() % (len - 1); + unsigned int duplicateValue = numbers[randomIndex]; + + // Wir schreiben das Duplikat an die allerletzte Stelle + numbers[len - 1] = duplicateValue; + + // Optional: Duplikat auch in den Baum einfügen (Modus: Akzeptieren / isDuplicate = NULL) + // Damit der Baum konsistent zum Array ist (falls man ihn später noch braucht). + root = addToTree(root, &duplicateValue, sizeof(unsigned int), compareUnsignedInt, NULL); + + // 4. Mischen + // Da das Duplikat jetzt immer ganz am Ende steht, müssen wir mischen. + shuffleArray(numbers, len); + + // 5. Aufräumen + // Der Baum war nur ein Hilfsmittel zur Überprüfung. Er wird jetzt gelöscht. + // WICHTIG: Damit verhindern wir Memory Leaks [cite: 12] + clearTree(root); + + return numbers; +} + +// ... Hierunter bleibt die getDuplicate Funktion deines Kollegen unverändert ... +// Sie ist korrekt implementiert laut Aufgabenstellung (mit qsort)[cite: 11, 43]. +unsigned int getDuplicate(const unsigned int numbers[], unsigned int len) +{ + if (numbers == NULL || len < 2) { + return 0; + } + + unsigned int *copy = malloc(len * sizeof(unsigned int)); + if (copy == NULL) { + return 0; + } + + memcpy(copy, numbers, len * sizeof(unsigned int)); + + qsort(copy, len, sizeof(unsigned int), compareUnsignedInt); + + unsigned int duplicate = 0; + + for (unsigned int i = 0; i + 1 < len; ++i) { + if (copy[i] == copy[i + 1]) { + duplicate = copy[i]; + break; + } + } + + free(copy); + return duplicate; +}+ + \ No newline at end of file diff --git a/highscore.o b/highscore.o new file mode 100644 index 0000000..f905345 Binary files /dev/null and b/highscore.o differ diff --git a/highscores.txt b/highscores.txt index 213846a..16d8f29 100644 --- a/highscores.txt +++ b/highscores.txt @@ -2,4 +2,6 @@ anton;39632 player_name;39611 lukas;35728 anton;9956 +anton;9944 +Anton;9940 player1;3999 diff --git a/main.o b/main.o new file mode 100644 index 0000000..f3ad2af Binary files /dev/null and b/main.o differ diff --git a/makefile b/makefile index 1f15f75..a08d448 100644 --- a/makefile +++ b/makefile @@ -35,9 +35,14 @@ $(program_obj_filesobj_files): %.o: %.c # -------------------------- # Unit Tests # -------------------------- -unitTests: - echo "needs to be implemented" +unitTests: test_numbers + ./test_numbers + +test_numbers: test_numbers.c numbers.c bintree.c stack.c unity/unity.c + gcc -Wall -Wextra -std=c99 -Iunity \ + -o test_numbers \ + test_numbers.c numbers.c bintree.c stack.c unity/unity.c # -------------------------- # Clean # -------------------------- diff --git a/numbers.c b/numbers.c index ead4cb0..6843f72 100644 --- a/numbers.c +++ b/numbers.c @@ -11,10 +11,7 @@ * Duplizieren eines zufälligen Eintrags im Array. * in `getDuplicate()`: Sortieren des Arrays und Erkennen der doppelten Zahl durch Vergleich benachbarter Elemente. */ -// 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. -// Vergleichsfunktion für qsort +//Vergleichsfunktion von qsort static int compareUnsignedInt(const void *a, const void *b) { const unsigned int *x = (const unsigned int *)a; @@ -25,7 +22,7 @@ const unsigned int *x = (const unsigned int *)a; return 0; } -// Fisher-Yates Shuffle Algorithmus zum Mischen des Arrays +//Mischen des Arrays static void shuffleArray(unsigned int *array, unsigned int n) { if (n > 1) @@ -39,91 +36,68 @@ static void shuffleArray(unsigned int *array, unsigned int n) } } } - +//Wenn weniger als zwei Zahlen unsigned int *createNumbers(unsigned int len) { if (len < 2) return NULL; - // 1. Array reservieren +//Dynamisches Array unsigned int *numbers = malloc(len * sizeof(unsigned int)); if (numbers == NULL) return NULL; - - // Hilfsvariablen für den Baum +//Variabelen für den Binärbaum TreeNode *root = NULL; int isDuplicate = 0; unsigned int count = 0; - // 2. PHASE 1: Erzeuge (len - 1) EINZIGARTIGE Zahlen mit Hilfe des Baums - // Wir nutzen den Baum als "Türsteher" + while (count < len - 1) - { - // Zufallszahl generieren (1 bis 2*len) + { //Zufallszahlen generieren unsigned int value = (rand() % (2 * len)) + 1; - // Versuchen, in den Baum einzufügen - // Wir übergeben &isDuplicate, damit der Baum Duplikate ABLEHNT. + root = addToTree(root, &value, sizeof(unsigned int), compareUnsignedInt, &isDuplicate); - // Prüfen: War es ein Duplikat? if (isDuplicate == 0) - { - // Nein, es war neu! -> Ins Array schreiben + { //in array schreiben falls kein Duplikat numbers[count] = value; count++; } - // Falls isDuplicate == 1, machen wir einfach weiter (while-Schleife läuft weiter) - } - // 3. PHASE 2: Das garantierte Duplikat erzeugen - // Wir wählen zufällig eine der bereits existierenden Zahlen aus + } + //Duplikat erzeugen unsigned int randomIndex = rand() % (len - 1); unsigned int duplicateValue = numbers[randomIndex]; - - // Wir schreiben das Duplikat an die allerletzte Stelle numbers[len - 1] = duplicateValue; - - // Optional: Duplikat auch in den Baum einfügen (Modus: Akzeptieren / isDuplicate = NULL) - // Damit der Baum konsistent zum Array ist (falls man ihn später noch braucht). root = addToTree(root, &duplicateValue, sizeof(unsigned int), compareUnsignedInt, NULL); - - // 4. Mischen - // Da das Duplikat jetzt immer ganz am Ende steht, müssen wir mischen. + //Array mischen damit duplikat nicht am Ende immer ist shuffleArray(numbers, len); - // 5. Aufräumen - // Der Baum war nur ein Hilfsmittel zur Überprüfung. Er wird jetzt gelöscht. - // WICHTIG: Damit verhindern wir Memory Leaks [cite: 12] clearTree(root); - return numbers; } - -// ... Hierunter bleibt die getDuplicate Funktion deines Kollegen unverändert ... -// Sie ist korrekt implementiert laut Aufgabenstellung (mit qsort)[cite: 11, 43]. +//get Duplicate unsigned int getDuplicate(const unsigned int numbers[], unsigned int len) { if (numbers == NULL || len < 2) { return 0; } - +//Kopie vom Array anlegen unsigned int *copy = malloc(len * sizeof(unsigned int)); + if (copy == NULL) { return 0; } - memcpy(copy, numbers, len * sizeof(unsigned int)); - qsort(copy, len, sizeof(unsigned int), compareUnsignedInt); - unsigned int duplicate = 0; - +//Duplikat finden for (unsigned int i = 0; i + 1 < len; ++i) { if (copy[i] == copy[i + 1]) { duplicate = copy[i]; break; } } - +//Speicher freigeben free(copy); return duplicate; } \ No newline at end of file diff --git a/numbers.o b/numbers.o new file mode 100644 index 0000000..623d87a Binary files /dev/null and b/numbers.o differ diff --git a/stack.c b/stack.c index e3a90d4..282c14a 100644 --- a/stack.c +++ b/stack.c @@ -1,33 +1,34 @@ #include