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 @@ + + +numbers.c + + + + + +
+ +numbers.c +
+
#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 #include "stack.h" -//TODO: grundlegende Stackfunktionen implementieren: -/* * `push`: legt ein Element oben auf den Stack, - * `pop`: entfernt das oberste Element, - * `top`: liefert das oberste Element zurück, - * `clearStack`: gibt den gesamten Speicher frei. */ - -// Pushes data as pointer onto the stack. StackNode *push(StackNode *stack, void *data) { - + StackNode *node = malloc(sizeof(StackNode)); + if (node == NULL) return stack; + node->data = data; + node->next = stack; + return node; } -// Deletes the top element of the stack (latest added element) and releases its memory. (Pointer to data has to be -// freed by caller.) StackNode *pop(StackNode *stack) { - + if (stack == NULL) return NULL; + StackNode *next = stack->next; + free(stack); + return next; } -// Returns the data of the top element. void *top(StackNode *stack) { - + if (stack == NULL) return NULL; + return stack->data; } -// Clears stack and releases all memory. void clearStack(StackNode *stack) { - + while (stack != NULL) { + StackNode *next = stack->next; + free(stack); + stack = next; + } } \ No newline at end of file diff --git a/stack.h b/stack.h index f7d542d..1f39743 100644 --- a/stack.h +++ b/stack.h @@ -1,25 +1,22 @@ #ifndef STACK_H #define STACK_H -/* A stack is a special type of queue which uses the LIFO (last in, first out) principle. -This means that with each new element all other elements are pushed deeper into the stack. -The latest element is taken from the stack. */ +// Eigener Stack-Knotentyp, damit es keinen Konflikt mit bintree.h gibt +typedef struct stack_node { + void *data; // Nutzdaten + struct stack_node *next; // Zeiger auf das nächste Element im Stack +} StackNode; -#include - -//TODO: passenden Datentyp als struct anlegen - -// Pushes data as pointer onto the stack. +// Legt ein Element oben auf den Stack StackNode *push(StackNode *stack, void *data); -// Deletes the top element of the stack (latest added element) and releases its memory. (Pointer to data has to be -// freed by caller.) +// Entfernt das oberste Element und gibt den neuen Stack-Kopf zurück StackNode *pop(StackNode *stack); -// Returns the data of the top element. +// Liefert die Daten des obersten Elements (ohne zu entfernen) void *top(StackNode *stack); -// Clears stack and releases all memory. +// Gibt den gesamten Stack frei void clearStack(StackNode *stack); -#endif +#endif // STACK_H \ No newline at end of file diff --git a/stack.o b/stack.o new file mode 100644 index 0000000..f1cc76c Binary files /dev/null and b/stack.o differ diff --git a/test_numbers b/test_numbers index 68278c1..df9c41f 100755 Binary files a/test_numbers and b/test_numbers differ diff --git a/test_numbers.c b/test_numbers.c index 66625a4..891849a 100644 --- a/test_numbers.c +++ b/test_numbers.c @@ -1,30 +1,94 @@ -#include #include #include + +#include "unity.h" #include "numbers.h" -int main(void) +void setUp(void) { - srand((unsigned int) time(NULL)); // für echte Zufallszahlen +} +void tearDown(void) +{ +} + +static unsigned int countOccurrences(const unsigned int *numbers, + unsigned int len, + unsigned int value) +{ + unsigned int count = 0; + for (unsigned int i = 0; i < len; ++i) { + if (numbers[i] == value) { + count++; + } + } + return count; +} + +void test_createNumbers_returns_non_null(void) +{ unsigned int len = 20; unsigned int *numbers = createNumbers(len); - if (numbers == NULL) { - printf("Fehler: createNumbers returned NULL\n"); - return 1; - } - - printf("Erzeugtes Array:\n"); - for (unsigned int i = 0; i < len; i++) { - printf("%u ", numbers[i]); - } - printf("\n"); - - unsigned int d = getDuplicate(numbers, len); - - printf("Gefundenes Duplikat: %u\n", d); + TEST_ASSERT_NOT_NULL(numbers); free(numbers); - return 0; +} + +void test_createNumbers_value_range(void) +{ + unsigned int len = 30; + unsigned int *numbers = createNumbers(len); + + TEST_ASSERT_NOT_NULL(numbers); + + for (unsigned int i = 0; i < len; ++i) { + TEST_ASSERT_TRUE(numbers[i] >= 1); + TEST_ASSERT_TRUE(numbers[i] <= 2 * len); + } + + free(numbers); +} + +void test_getDuplicate_finds_exactly_one_duplicate(void) +{ + unsigned int len = 25; + unsigned int *numbers = createNumbers(len); + + TEST_ASSERT_NOT_NULL(numbers); + + unsigned int duplicate = getDuplicate(numbers, len); + TEST_ASSERT_NOT_EQUAL_UINT(0, duplicate); + + unsigned int occurrences = + countOccurrences(numbers, len, duplicate); + + TEST_ASSERT_EQUAL_UINT(2, occurrences); + + free(numbers); +} + +void test_error_cases(void) +{ + TEST_ASSERT_NULL(createNumbers(0)); + TEST_ASSERT_NULL(createNumbers(1)); + + TEST_ASSERT_EQUAL_UINT(0, getDuplicate(NULL, 10)); + + unsigned int oneElement[1] = { 42 }; + TEST_ASSERT_EQUAL_UINT(0, getDuplicate(oneElement, 1)); +} + +int main(void) +{ + srand((unsigned int) time(NULL)); + + UNITY_BEGIN(); + + RUN_TEST(test_createNumbers_returns_non_null); + RUN_TEST(test_createNumbers_value_range); + RUN_TEST(test_getDuplicate_finds_exactly_one_duplicate); + RUN_TEST(test_error_cases); + + return UNITY_END(); } \ No newline at end of file diff --git a/timer.o b/timer.o new file mode 100644 index 0000000..d73004d Binary files /dev/null and b/timer.o differ