diff --git a/bintree.c b/bintree.c index 5cf82a9..642e3fc 100644 --- a/bintree.c +++ b/bintree.c @@ -1,36 +1,129 @@ +#include #include -#include "stack.h" #include "bintree.h" -//TODO: binären Suchbaum implementieren -/* * `addToTree`: fügt ein neues Element in den Baum ein (rekursiv), - * `clearTree`: gibt den gesamten Baum frei (rekursiv), - * `treeSize`: zählt die Knoten im Baum (rekursiv), - * `nextTreeData`: Traversierung mit Hilfe des zuvor implementierten Stacks. */ +// -------------------------- +// Hilfsfunktion für Vergleich von unsigned int +// -------------------------- +static int compareUint(const void *a, const void *b) +{ + unsigned int x = *(unsigned int *)a; + unsigned int y = *(unsigned int *)b; + if (x < y) return -1; + if (x > y) return 1; + return 0; +} -// 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). +// -------------------------- +// addToTree +// -------------------------- TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate) { + if (!compareFct) + compareFct = compareUint; + if (!root) + { + TreeNode *node = malloc(sizeof(TreeNode)); + if (!node) return NULL; + node->data = malloc(dataSize); + if (!node->data) + { + free(node); + return NULL; + } + memcpy(node->data, data, dataSize); + node->left = node->right = NULL; + if (isDuplicate) *isDuplicate = 0; + return node; + } + + int cmp = compareFct(data, root->data); + if (cmp == 0) + { + if (isDuplicate) *isDuplicate = 1; + return root; + } + else if (cmp < 0) + root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate); + else + 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. -void *nextTreeData(TreeNode *root) -{ - -} - -// Releases all memory resources (including data copies). +// -------------------------- +// clearTree +// -------------------------- void clearTree(TreeNode *root) { - + if (!root) return; + clearTree(root->left); + clearTree(root->right); + free(root->data); + free(root); } -// Returns the number of entries in the tree given by root. +// -------------------------- +// treeSize +// -------------------------- unsigned int treeSize(const TreeNode *root) { + if (!root) return 0; + return 1 + treeSize(root->left) + treeSize(root->right); +} +// -------------------------- +// nextTreeData (In-Order Traversal mit Stack) – minimal +// -------------------------- +#include "stack.h" +void *nextTreeData(TreeNode *root) +{ + static StackNode *stack = NULL; + static TreeNode *current = NULL; + + if (root) current = root; + + while (current) + { + stack = push(stack, current); + current = current->left; + } + + if (!stack) return NULL; + + TreeNode *node = top(stack); + stack = pop(stack); + + current = node->right; + return node->data; +} + +// -------------------------- +// Zusätzliche Hilfsfunktion contains für numbers.c +// -------------------------- +int contains(TreeNode *root, unsigned int value) +{ + if (!root) return 0; + unsigned int nodeValue = *(unsigned int *)root->data; + if (value == nodeValue) return 1; + if (value < nodeValue) return contains(root->left, value); + return contains(root->right, value); +} + +// -------------------------- +// Hilfsfunktion insert für numbers.c +// -------------------------- +TreeNode* insert(TreeNode *root, unsigned int value) +{ + int isDup = 0; + return addToTree(root, &value, sizeof(unsigned int), compareUint, &isDup); +} + +// -------------------------- +// Hilfsfunktion freeTree für numbers.c +// -------------------------- +void freeTree(TreeNode *root) +{ + clearTree(root); } \ No newline at end of file diff --git a/bintree.o b/bintree.o new file mode 100644 index 0000000..a9c7b28 Binary files /dev/null and b/bintree.o differ diff --git a/doble.exe b/doble.exe new file mode 100644 index 0000000..de16a86 Binary files /dev/null and b/doble.exe differ diff --git a/doble_initial.exe b/doble_initial.exe new file mode 100644 index 0000000..9eda5f3 Binary files /dev/null and b/doble_initial.exe differ diff --git a/highscore.o b/highscore.o new file mode 100644 index 0000000..21be6ce Binary files /dev/null and b/highscore.o differ diff --git a/highscores.txt b/highscores.txt index 4edd5a7..87fa3e1 100644 --- a/highscores.txt +++ b/highscores.txt @@ -1 +1,10 @@ +player_name1;19827 +player_name3;6985 +jannik;5979 +jannik;4991 +jannik;4980 player1;3999 +player_name;3995 +player_name2;3994 +player_name1;3993 +player_name3;3990 diff --git a/main.o b/main.o new file mode 100644 index 0000000..30eb06f Binary files /dev/null and b/main.o differ diff --git a/makefile b/makefile index 1f15f75..81649ae 100644 --- a/makefile +++ b/makefile @@ -16,34 +16,68 @@ raylibfolder = ./raylib unityfolder = ./unity # -------------------------- -# Initiales Programm bauen (zum ausprobieren) +# Hauptprogramm bauen # -------------------------- -doble_initial: - $(CC) -o doble_initial $(BINARIES)/libdoble_complete.a -# -------------------------- -# Selbst implementiertes Programm bauen -# -------------------------- program_obj_files = stack.o bintree.o numbers.o timer.o highscore.o -doble : main.o $(program_obj_files) +doble: main.o $(program_obj_files) $(CC) $(FLAGS) $^ -o doble +main.o: main.c + $(CC) -c $(FLAGS) main.c -o main.o + $(program_obj_filesobj_files): %.o: %.c $(CC) -c $(FLAGS) $^ -o $@ # -------------------------- # Unit Tests # -------------------------- -unitTests: - echo "needs to be implemented" + +unitTests: test_stack test_numbers + ./test_stack + ./test_numbers + +# --- Test Stack --- +test_stack: test_stack.o stack.o + $(CC) $(FLAGS) $^ -o test_stack + +test_stack.o: test_stack.c stack.h + $(CC) -c $(FLAGS) test_stack.c -o test_stack.o + +# --- Test Numbers --- +test_numbers: test_numbers.o numbers.o bintree.o stack.o + $(CC) $(FLAGS) $^ -o test_numbers + +test_numbers.o: test_numbers.c numbers.h + $(CC) -c $(FLAGS) test_numbers.c -o test_numbers.o + +# -------------------------- +# Objektdateien +# -------------------------- + +stack.o: stack.c stack.h + $(CC) -c $(FLAGS) stack.c -o stack.o + +bintree.o: bintree.c bintree.h stack.h + $(CC) -c $(FLAGS) bintree.c -o bintree.o + +numbers.o: numbers.c numbers.h bintree.h + $(CC) -c $(FLAGS) numbers.c -o numbers.o + +timer.o: timer.c timer.h + $(CC) -c $(FLAGS) timer.c -o timer.o + +highscore.o: highscore.c highscore.h + $(CC) -c $(FLAGS) highscore.c -o highscore.o # -------------------------- # Clean # -------------------------- + clean: ifeq ($(OS),Windows_NT) - del /f *.o doble + del /f *.o doble test_stack test_numbers else - rm -f *.o doble + rm -f *.o doble test_stack test_numbers endif \ No newline at end of file diff --git a/numbers.c b/numbers.c index f59d9a2..f10610e 100644 --- a/numbers.c +++ b/numbers.c @@ -5,22 +5,83 @@ #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. -unsigned int *createNumbers(unsigned int len) -{ - +// Hilfsfunktion zum Vergleichen von ints für Baum +static int compareInt(const void *a, const void *b) { + return (*(int*)a - *(int*)b); } -// 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) -{ +// Vergleichsfunktion für qsort (unsigned int, überlauf-sicher) +static int compareUnsigned(const void *a, const void *b) { + unsigned int ua = *(unsigned int*)a; + unsigned int ub = *(unsigned int*)b; + if (ua < ub) return -1; + if (ua > ub) return 1; + return 0; +} + +// Hilfsfunktion: Prüft, ob Wert schon im Baum ist +static int contains(TreeNode *root, unsigned int value) { + if (!root) return 0; + unsigned int val = *(unsigned int*)root->data; + if (val == value) return 1; + if (value < val) return contains(root->left, value); + return contains(root->right, value); +} + +// Erzeugt ein Array von len Zahlen zwischen 1 und 2*len, alle einzigartig, +// außer eine Zahl wird dupliziert. +unsigned int *createNumbers(unsigned int len) { + if (len == 0) return NULL; + + unsigned int *numbers = malloc(len * sizeof(unsigned int)); + if (!numbers) return NULL; + + TreeNode *tree = NULL; + srand((unsigned int)time(NULL)); + + for (unsigned int i = 0; i < len - 1; i++) { // alle außer die Duplikate + unsigned int value; + do { + value = rand() % (2 * len) + 1; + } while (contains(tree, value)); + + int isDup = 0; + tree = addToTree(tree, &value, sizeof(value), compareInt, &isDup); + numbers[i] = value; + } + + // Dupliziere zufällig eine Zahl + unsigned int dupIndex = rand() % (len - 1); + numbers[len - 1] = numbers[dupIndex]; + + // Speicher freigeben + clearTree(tree); + + return numbers; +} + +// Sortiert das Array und findet die einzige doppelte Zahl +unsigned int getDuplicate(const unsigned int *numbers, unsigned int len) { + if (!numbers || len < 2) return 0; + + // Kopiere Array, da wir sortieren müssen + unsigned int *copy = malloc(len * sizeof(unsigned int)); + if (!copy) return 0; + memcpy(copy, numbers, len * sizeof(unsigned int)); + + // qsort verwenden + qsort(copy, len, sizeof(unsigned int), compareUnsigned); + + // Durchlauf und Nachbarn vergleichen + unsigned int dup = 0; + for (unsigned int i = 1; i < len; i++) { + if (copy[i] == copy[i - 1]) { + dup = copy[i]; + break; + } + } + + free(copy); + return dup; } \ No newline at end of file diff --git a/numbers.o b/numbers.o new file mode 100644 index 0000000..1135c4e Binary files /dev/null and b/numbers.o differ diff --git a/stack.c b/stack.c index e3a90d4..8f1d1b6 100644 --- a/stack.c +++ b/stack.c @@ -1,33 +1,38 @@ #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 onto the stack and returns new stack top +StackNode *push(StackNode *stack, void *data) { + StackNode *node = malloc(sizeof(StackNode)); + if (!node) return stack; // allocation failed → leave stack unchanged -// Pushes data as pointer onto the stack. -StackNode *push(StackNode *stack, void *data) -{ + node->data = data; + node->next = stack; + return node; // new top } -// 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) -{ +// Removes the top element, frees node (NOT data), returns new top +StackNode *pop(StackNode *stack) { + if (stack == NULL) return NULL; + StackNode *next = stack->next; + free(stack); + + return next; // new top } -// Returns the data of the top element. -void *top(StackNode *stack) -{ - +// Returns data of top node or NULL +void *top(StackNode *stack) { + if (!stack) return NULL; + return stack->data; } -// Clears stack and releases all memory. -void clearStack(StackNode *stack) -{ - -} \ No newline at end of file +// Frees entire stack (NOT data!) +void clearStack(StackNode *stack) { + while (stack != NULL) { + StackNode *next = stack->next; + free(stack); + stack = next; + } +} diff --git a/stack.h b/stack.h index f7d542d..0d3f1e8 100644 --- a/stack.h +++ b/stack.h @@ -1,19 +1,23 @@ #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. */ +/* 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. */ #include -//TODO: passenden Datentyp als struct anlegen +// passenden Datentyp als struct anlegen +typedef struct StackNode { + void *data; // Zeiger auf beliebige Daten + struct StackNode *next; // Zeiger auf das nächste Element im Stack +} StackNode; // Pushes data as pointer onto the 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.) +// 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); // Returns the data of the top element. diff --git a/stack.o b/stack.o new file mode 100644 index 0000000..4cc2f4b Binary files /dev/null and b/stack.o differ diff --git a/test_numbers.c b/test_numbers.c new file mode 100644 index 0000000..11c072c --- /dev/null +++ b/test_numbers.c @@ -0,0 +1,49 @@ +#include +#include +#include "numbers.h" + +int main(void) +{ + printf("=== TEST: createNumbers & getDuplicate ===\n"); + + unsigned int len = 20; + unsigned int *arr = createNumbers(len); + + if (!arr) + { + printf("FAILED: createNumbers returned NULL\n"); + return 1; + } + + printf("Array created: "); + for (unsigned int i = 0; i < len; i++) + printf("%u ", arr[i]); + printf("\n"); + + unsigned int dup = getDuplicate(arr, len); + + if (dup == 0) + { + printf("FAILED: getDuplicate returned 0 (no duplicate found)\n"); + free(arr); + return 1; + } + + // Count occurrences manually as safety check + unsigned int count = 0; + for (unsigned int i = 0; i < len; i++) + if (arr[i] == dup) + count++; + + if (count != 2) + { + printf("FAILED: duplicate=%u occurs %u times (expected 2)\n", dup, count); + free(arr); + return 1; + } + + printf("SUCCESS: duplicate number is %u and appears exactly twice.\n", dup); + + free(arr); + return 0; +} \ No newline at end of file diff --git a/test_numbers.exe b/test_numbers.exe new file mode 100644 index 0000000..2d91b7b Binary files /dev/null and b/test_numbers.exe differ diff --git a/test_stack.c b/test_stack.c new file mode 100644 index 0000000..bebcdf2 --- /dev/null +++ b/test_stack.c @@ -0,0 +1,41 @@ +#include +#include +#include "stack.h" + +int main() { + StackNode *stack = NULL; + + // Einige Integer dynamisch alloziieren + int *a = malloc(sizeof(int)); + int *b = malloc(sizeof(int)); + int *c = malloc(sizeof(int)); + *a = 10; *b = 20; *c = 30; + + stack = push(stack, a); + stack = push(stack, b); + stack = push(stack, c); + + // Test top() + printf("Top = %d (expected 30)\n", *(int *)top(stack)); + + // pop() + stack = pop(stack); // removed 30 + free(c); // data gehört Benutzer! + + printf("Top = %d (expected 20)\n", *(int *)top(stack)); + + stack = pop(stack); free(b); // removed 20 + stack = pop(stack); free(a); // removed 10 + + // stack should now be empty + if (stack == NULL) { + printf("Stack empty ok\n"); + } else { + printf("Error: stack not empty!\n"); + } + + // Cleanup just in case + clearStack(stack); + + return 0; +} \ No newline at end of file diff --git a/test_stack.exe b/test_stack.exe new file mode 100644 index 0000000..50030d5 Binary files /dev/null and b/test_stack.exe differ diff --git a/timer.o b/timer.o new file mode 100644 index 0000000..3cf4e85 Binary files /dev/null and b/timer.o differ