Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 746b9b7219 | |||
| 1f3fba80ec | |||
| 1d363980f6 | |||
| 4ce3a6aac0 |
Binary file not shown.
153
bintree.c
153
bintree.c
@ -2,158 +2,35 @@
|
|||||||
#include "stack.h"
|
#include "stack.h"
|
||||||
#include "bintree.h"
|
#include "bintree.h"
|
||||||
|
|
||||||
// Fügt ein neues Element in den Binärbaum ein.
|
//TODO: binären Suchbaum implementieren
|
||||||
// - data wird kopiert (tiefe Kopie)
|
/* * `addToTree`: fügt ein neues Element in den Baum ein (rekursiv),
|
||||||
// - compareFct bestimmt die Sortierreihenfolge
|
* `clearTree`: gibt den gesamten Baum frei (rekursiv),
|
||||||
// - Duplikate sind erlaubt, außer isDuplicate ist gesetzt
|
* `treeSize`: zählt die Knoten im Baum (rekursiv),
|
||||||
TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize,
|
* `nextTreeData`: Traversierung mit Hilfe des zuvor implementierten Stacks. */
|
||||||
CompareFctType compareFct, int *isDuplicate)
|
|
||||||
|
// 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).
|
||||||
|
TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize, CompareFctType compareFct, int *isDuplicate)
|
||||||
{
|
{
|
||||||
// Ungültige Parameter → Baum unverändert zurückgeben
|
|
||||||
if (data == NULL || compareFct == NULL)
|
|
||||||
return root;
|
|
||||||
|
|
||||||
// Fall: leerer Teilbaum → neuer Knoten wird erzeugt
|
|
||||||
if (root == NULL)
|
|
||||||
{
|
|
||||||
// Speicher für neuen Baumknoten reservieren
|
|
||||||
TreeNode *node = malloc(sizeof(TreeNode));
|
|
||||||
if (node == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// Speicher für die Nutzdaten reservieren
|
|
||||||
node->data = malloc(dataSize);
|
|
||||||
if (node->data == NULL)
|
|
||||||
{
|
|
||||||
free(node);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Daten in den Knoten kopieren
|
|
||||||
memcpy(node->data, data, dataSize);
|
|
||||||
|
|
||||||
// Linke und rechte Kindzeiger initialisieren
|
|
||||||
node->left = NULL;
|
|
||||||
node->right = NULL;
|
|
||||||
|
|
||||||
// Kein Duplikat, da neuer Knoten
|
|
||||||
if (isDuplicate != NULL)
|
|
||||||
*isDuplicate = 0;
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Vergleich der neuen Daten mit den Daten im aktuellen Knoten
|
|
||||||
int cmp = compareFct(data, root->data);
|
|
||||||
|
|
||||||
if (cmp < 0)
|
|
||||||
{
|
|
||||||
// Neuer Wert ist kleiner → Rekursion im linken Teilbaum
|
|
||||||
root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate);
|
|
||||||
}
|
|
||||||
else if (cmp > 0)
|
|
||||||
{
|
|
||||||
// Neuer Wert ist größer → Rekursion im rechten Teilbaum
|
|
||||||
root->right = addToTree(root->right, data, dataSize, compareFct, isDuplicate);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Gleicher Wert → Duplikat
|
|
||||||
if (isDuplicate != NULL)
|
|
||||||
{
|
|
||||||
// Duplikat melden, aber nicht einfügen
|
|
||||||
*isDuplicate = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Duplikate sind erlaubt → z.B. links einfügen
|
|
||||||
root->left = addToTree(root->left, data, dataSize, compareFct, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wurzel des (Teil-)Baumes zurückgeben
|
|
||||||
return root;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Globaler interner Stack für die Baum-Traversierung
|
// 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.
|
||||||
// Ermöglicht ein strtok-ähnliches Iterator-Verhalten
|
// Use your implementation of a stack to organize the iterator. Push the root node and all left nodes first. On returning the next element,
|
||||||
static StackNode *iterStack = NULL;
|
// push the top node and push all its left nodes.
|
||||||
|
|
||||||
// Hilfsfunktion:
|
|
||||||
// Legt den Startknoten und alle seine linken Nachfolger auf den Stack
|
|
||||||
static void pushLeftPath(TreeNode *start)
|
|
||||||
{
|
|
||||||
// Solange noch ein Knoten existiert
|
|
||||||
while (start != NULL)
|
|
||||||
{
|
|
||||||
// Aktuellen Knoten auf den Stack legen
|
|
||||||
iterStack = push(iterStack, start);
|
|
||||||
|
|
||||||
// Zum linken Kind weitergehen
|
|
||||||
start = start->left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gibt nacheinander die Daten des Baumes in sortierter Reihenfolge zurück.
|
|
||||||
// - Beim ersten Aufruf root ≠ NULL → neue Traversierung
|
|
||||||
// - Danach root == NULL → nächstes Element liefern
|
|
||||||
void *nextTreeData(TreeNode *root)
|
void *nextTreeData(TreeNode *root)
|
||||||
{
|
{
|
||||||
if (root != NULL)
|
|
||||||
{
|
|
||||||
// Neue Traversierung starten → alten Stack leeren
|
|
||||||
if (iterStack != NULL)
|
|
||||||
{
|
|
||||||
clearStack(iterStack);
|
|
||||||
iterStack = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wurzel und alle linken Knoten auf den Stack legen
|
|
||||||
pushLeftPath(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kein weiteres Element vorhanden
|
|
||||||
if (iterStack == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// Oberstes Stack-Element holen (nächster Baumknoten)
|
|
||||||
TreeNode *node = (TreeNode *)top(iterStack);
|
|
||||||
iterStack = pop(iterStack);
|
|
||||||
|
|
||||||
// Rechten Teilbaum dieses Knotens behandeln
|
|
||||||
// → dessen linker Pfad wird auf den Stack gelegt
|
|
||||||
if (node->right != NULL)
|
|
||||||
pushLeftPath(node->right);
|
|
||||||
|
|
||||||
// Daten des aktuellen Knotens zurückgeben
|
|
||||||
return node->data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gibt den gesamten Speicher des Baumes frei (inkl. gespeicherter Daten)
|
// Releases all memory resources (including data copies).
|
||||||
void clearTree(TreeNode *root)
|
void clearTree(TreeNode *root)
|
||||||
{
|
{
|
||||||
// Abbruchbedingung der Rekursion
|
|
||||||
if (root == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Zuerst linken Teilbaum freigeben
|
|
||||||
clearTree(root->left);
|
|
||||||
|
|
||||||
// Dann rechten Teilbaum freigeben
|
|
||||||
clearTree(root->right);
|
|
||||||
|
|
||||||
// Danach Daten und Knoten selbst freigeben
|
|
||||||
free(root->data);
|
|
||||||
free(root);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Liefert die Anzahl aller Knoten im Baum
|
// Returns the number of entries in the tree given by root.
|
||||||
unsigned int treeSize(const TreeNode *root)
|
unsigned int treeSize(const TreeNode *root)
|
||||||
{
|
{
|
||||||
// Leerer Baum hat Größe 0
|
|
||||||
if (root == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// 1 (aktueller Knoten) + Größe linker + Größe rechter Teilbaum
|
}
|
||||||
return 1u + treeSize(root->left) + treeSize(root->right);
|
|
||||||
}
|
|
||||||
Binary file not shown.
BIN
highscore.o
BIN
highscore.o
Binary file not shown.
@ -1,4 +1 @@
|
|||||||
Kilian;9927
|
|
||||||
Kilian;9915
|
|
||||||
Kilian;4975
|
|
||||||
player1;3999
|
player1;3999
|
||||||
|
|||||||
70
makefile
70
makefile
@ -1,4 +1,4 @@
|
|||||||
CC = gcc
|
CC = gcc
|
||||||
FLAGS = -g -Wall -lm
|
FLAGS = -g -Wall -lm
|
||||||
|
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
@ -13,18 +13,10 @@ else
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
raylibfolder = ./raylib
|
raylibfolder = ./raylib
|
||||||
unityfolder = ./unity
|
unityfolder = ./unity
|
||||||
|
|
||||||
# --------------------------
|
# --------------------------
|
||||||
# Phony Targets
|
# Initiales Programm bauen (zum ausprobieren)
|
||||||
# --------------------------
|
|
||||||
.PHONY: all doble doble_initial unitTests test_stack test_numbers clean vorbereitungen prepare
|
|
||||||
|
|
||||||
# Standard-Target
|
|
||||||
all: doble
|
|
||||||
|
|
||||||
# --------------------------
|
|
||||||
# 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
|
||||||
@ -34,66 +26,24 @@ doble_initial:
|
|||||||
# --------------------------
|
# --------------------------
|
||||||
program_obj_files = stack.o bintree.o numbers.o timer.o highscore.o
|
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
|
$(CC) $(FLAGS) $^ -o doble
|
||||||
|
|
||||||
# Generische Regel zum Bauen von .o aus .c
|
$(program_obj_files): %.o: %.c
|
||||||
%.o: %.c
|
$(CC) -c $(FLAGS) $^ -o $@
|
||||||
$(CC) $(FLAGS) -c $< -o $@
|
|
||||||
|
|
||||||
# Abhängigkeiten (optional, aber hilfreich für inkrementelles Bauen)
|
|
||||||
main.o: main.c numbers.h stack.h bintree.h timer.h highscore.h
|
|
||||||
numbers.o: numbers.c numbers.h bintree.h
|
|
||||||
bintree.o: bintree.c bintree.h stack.h
|
|
||||||
stack.o: stack.c stack.h
|
|
||||||
timer.o: timer.c timer.h
|
|
||||||
highscore.o: highscore.c highscore.h
|
|
||||||
|
|
||||||
test_stack.o: test_stack.c stack.h
|
|
||||||
test_numbers.o: test_numbers.c numbers.h bintree.h stack.h
|
|
||||||
|
|
||||||
# --------------------------
|
# --------------------------
|
||||||
# Unit Tests
|
# Unit Tests
|
||||||
# --------------------------
|
# --------------------------
|
||||||
test_stack: test_stack.o stack.o
|
unitTests:
|
||||||
$(CC) $(FLAGS) $^ -o test_stack
|
echo "needs to be implemented"
|
||||||
|
|
||||||
test_numbers: test_numbers.o numbers.o stack.o bintree.o
|
|
||||||
$(CC) $(FLAGS) $^ -o test_numbers
|
|
||||||
|
|
||||||
unitTests: test_stack test_numbers
|
|
||||||
./test_stack
|
|
||||||
./test_numbers
|
|
||||||
|
|
||||||
# --------------------------
|
|
||||||
# Vorbereitungen zum Bauen des Programms
|
|
||||||
# --------------------------
|
|
||||||
vorbereitungen:
|
|
||||||
@echo "== Vorbereitungen zum Bauen des Programms =="
|
|
||||||
@echo "Prüfe benötigte Dateien..."
|
|
||||||
@test -f numbers.c || (echo "FEHLT: numbers.c" && exit 1)
|
|
||||||
@test -f numbers.h || (echo "FEHLT: numbers.h" && exit 1)
|
|
||||||
@test -f bintree.c || (echo "FEHLT: bintree.c" && exit 1)
|
|
||||||
@test -f bintree.h || (echo "FEHLT: bintree.h" && exit 1)
|
|
||||||
@test -f stack.c || (echo "FEHLT: stack.c" && exit 1)
|
|
||||||
@test -f stack.h || (echo "FEHLT: stack.h" && exit 1)
|
|
||||||
@test -f timer.c || (echo "FEHLT: timer.c" && exit 1)
|
|
||||||
@test -f timer.h || (echo "FEHLT: timer.h" && exit 1)
|
|
||||||
@test -f highscore.c || (echo "FEHLT: highscore.c" && exit 1)
|
|
||||||
@test -f highscore.h || (echo "FEHLT: highscore.h" && exit 1)
|
|
||||||
@test -f main.c || (echo "FEHLT: main.c" && exit 1)
|
|
||||||
@echo "Alle Dateien vorhanden."
|
|
||||||
@echo "Vorbereitung abgeschlossen!"
|
|
||||||
|
|
||||||
# englischer Alias
|
|
||||||
prepare: vorbereitungen
|
|
||||||
|
|
||||||
# --------------------------
|
# --------------------------
|
||||||
# Clean
|
# Clean
|
||||||
# --------------------------
|
# --------------------------
|
||||||
clean:
|
clean:
|
||||||
ifeq ($(OS),Windows_NT)
|
ifeq ($(OS),Windows_NT)
|
||||||
del /f *.o doble doble_initial test_stack test_numbers
|
del /f *.o doble
|
||||||
else
|
else
|
||||||
rm -f *.o doble doble_initial test_stack test_numbers
|
rm -f *.o doble
|
||||||
endif
|
endif
|
||||||
99
numbers.c
99
numbers.c
@ -5,109 +5,22 @@
|
|||||||
#include "numbers.h"
|
#include "numbers.h"
|
||||||
#include "bintree.h"
|
#include "bintree.h"
|
||||||
|
|
||||||
// Hilfsfunktion für qsort und den Binärbaum: Vergleich von unsigned int
|
//TODO: getDuplicate und createNumbers implementieren
|
||||||
static int compareUnsignedInt(const void *a, const void *b)
|
/* * * Erzeugen eines Arrays mit der vom Nutzer eingegebenen Anzahl an Zufallszahlen.
|
||||||
{
|
* Sicherstellen, dass beim Befüllen keine Duplikate entstehen.
|
||||||
const unsigned int *ua = (const unsigned int *)a;
|
* Duplizieren eines zufälligen Eintrags im Array.
|
||||||
const unsigned int *ub = (const unsigned int *)b;
|
* in `getDuplicate()`: Sortieren des Arrays und Erkennen der doppelten Zahl durch Vergleich benachbarter Elemente. */
|
||||||
|
|
||||||
if (*ua < *ub)
|
|
||||||
return -1;
|
|
||||||
else if (*ua > *ub)
|
|
||||||
return 1;
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
unsigned int *createNumbers(unsigned int len)
|
||||||
{
|
{
|
||||||
if (len < 2)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
unsigned int *numbers = malloc(len * sizeof(unsigned int));
|
|
||||||
if (numbers == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// Zufall initialisieren
|
|
||||||
srand((unsigned int)time(NULL));
|
|
||||||
|
|
||||||
TreeNode *root = NULL;
|
|
||||||
unsigned int i = 0;
|
|
||||||
|
|
||||||
// len-1 verschiedene Zufallszahlen im Bereich [1, 2*len] erzeugen
|
|
||||||
while (i < len - 1)
|
|
||||||
{
|
|
||||||
unsigned int candidate = (unsigned int)(rand() % (2 * len)) + 1;
|
|
||||||
int isDuplicate = 0;
|
|
||||||
|
|
||||||
TreeNode *newRoot = addToTree(root, &candidate, sizeof(unsigned int),
|
|
||||||
compareUnsignedInt, &isDuplicate);
|
|
||||||
if (newRoot == NULL && root == NULL)
|
|
||||||
{
|
|
||||||
// Speicherfehler beim ersten Einfügen
|
|
||||||
clearTree(root);
|
|
||||||
free(numbers);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
root = newRoot;
|
|
||||||
|
|
||||||
if (!isDuplicate)
|
|
||||||
{
|
|
||||||
numbers[i] = candidate;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
// bei Duplikat wird einfach eine neue Zufallszahl generiert
|
|
||||||
}
|
|
||||||
|
|
||||||
// Einen zufälligen Eintrag aus den ersten len-1 duplizieren
|
|
||||||
unsigned int duplicateIndex = (unsigned int)(rand() % (len - 1));
|
|
||||||
numbers[len - 1] = numbers[duplicateIndex];
|
|
||||||
|
|
||||||
// Baum wird nicht mehr benötigt
|
|
||||||
clearTree(root);
|
|
||||||
|
|
||||||
// Das Array durchmischen (Fisher-Yates-Shuffle), damit das Duplikat an zufälliger Position steht
|
|
||||||
for (unsigned int j = len - 1; j > 0; --j)
|
|
||||||
{
|
|
||||||
unsigned int k = (unsigned int)(rand() % (j + 1));
|
|
||||||
unsigned int tmp = numbers[j];
|
|
||||||
numbers[j] = numbers[k];
|
|
||||||
numbers[k] = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
unsigned int getDuplicate(const unsigned int numbers[], unsigned int len)
|
||||||
{
|
{
|
||||||
if (numbers == NULL || len < 2)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Kopie des Arrays anlegen, da qsort in-place sortiert
|
}
|
||||||
unsigned int *copy = malloc(len * sizeof(unsigned int));
|
|
||||||
if (copy == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
memcpy(copy, numbers, len * sizeof(unsigned int));
|
|
||||||
|
|
||||||
// Sortieren mit qsort
|
|
||||||
qsort(copy, len, sizeof(unsigned int), compareUnsignedInt);
|
|
||||||
|
|
||||||
// Benachbarte Elemente vergleichen, um das Duplikat zu finden
|
|
||||||
unsigned int duplicate = 0;
|
|
||||||
for (unsigned int i = 1; i < len; ++i)
|
|
||||||
{
|
|
||||||
if (copy[i] == copy[i - 1])
|
|
||||||
{
|
|
||||||
duplicate = copy[i];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free(copy);
|
|
||||||
return duplicate; // 0 falls kein Duplikat gefunden (Fehlerfall)
|
|
||||||
}
|
|
||||||
29
stack.c
29
stack.c
@ -1,46 +1,33 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "stack.h"
|
#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.
|
// Pushes data as pointer onto the stack.
|
||||||
StackNode *push(StackNode *stack, void *data)
|
StackNode *push(StackNode *stack, void *data)
|
||||||
{
|
{
|
||||||
StackNode *node = (StackNode *)malloc(sizeof(StackNode));
|
|
||||||
if (node == NULL)
|
|
||||||
return stack; // keine Änderung bei Speicherfehler
|
|
||||||
|
|
||||||
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
|
// Deletes the top element of the stack (latest added element) and releases its memory. (Pointer to data has to be
|
||||||
// freed by caller.)
|
// freed by caller.)
|
||||||
StackNode *pop(StackNode *stack)
|
StackNode *pop(StackNode *stack)
|
||||||
{
|
{
|
||||||
if (stack == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
StackNode *next = stack->next;
|
|
||||||
free(stack);
|
|
||||||
return next;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the data of the top element.
|
// Returns the data of the top element.
|
||||||
void *top(StackNode *stack)
|
void *top(StackNode *stack)
|
||||||
{
|
{
|
||||||
if (stack == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return stack->data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clears stack and releases all memory.
|
// Clears stack and releases all memory.
|
||||||
void clearStack(StackNode *stack)
|
void clearStack(StackNode *stack)
|
||||||
{
|
{
|
||||||
while (stack != NULL)
|
|
||||||
{
|
}
|
||||||
StackNode *next = stack->next;
|
|
||||||
free(stack);
|
|
||||||
stack = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
8
stack.h
8
stack.h
@ -7,12 +7,7 @@ The latest element is taken from the stack. */
|
|||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
// einfacher verketteter Stack
|
//TODO: passenden Datentyp als struct anlegen
|
||||||
typedef struct StackNode
|
|
||||||
{
|
|
||||||
void *data;
|
|
||||||
struct StackNode *next;
|
|
||||||
} StackNode;
|
|
||||||
|
|
||||||
// Pushes data as pointer onto the stack.
|
// Pushes data as pointer onto the stack.
|
||||||
StackNode *push(StackNode *stack, void *data);
|
StackNode *push(StackNode *stack, void *data);
|
||||||
@ -28,4 +23,3 @@ void *top(StackNode *stack);
|
|||||||
void clearStack(StackNode *stack);
|
void clearStack(StackNode *stack);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
142
test_numbers.c
142
test_numbers.c
@ -1,142 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include "numbers.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Unit-Tests für createNumbers und getDuplicate:
|
|
||||||
* - createNumbers liefert ein gültiges Array
|
|
||||||
* - alle Zahlen liegen im Bereich [1, 2*len]
|
|
||||||
* - genau eine Zahl kommt doppelt vor
|
|
||||||
* - getDuplicate findet genau diese doppelte Zahl
|
|
||||||
* - Fehlerfälle (NULL-Pointer, zu kleine Länge)
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Prüft ein Zahlenarray auf Korrektheit:
|
|
||||||
* - Array darf nicht NULL sein
|
|
||||||
* - Wertebereich muss stimmen
|
|
||||||
* - genau eine Zahl darf doppelt vorkommen
|
|
||||||
*/
|
|
||||||
static void check_numbers_array(unsigned int *numbers, unsigned int len)
|
|
||||||
{
|
|
||||||
unsigned int maxValue = 2 * len; // Maximal erlaubter Zahlenwert
|
|
||||||
unsigned int *counts; // Array zum Zählen der Vorkommen
|
|
||||||
unsigned int i;
|
|
||||||
unsigned int duplicatesCount = 0; // Zähler für doppelte Werte
|
|
||||||
|
|
||||||
assert(numbers != NULL); // Sicherstellen, dass das Array existiert
|
|
||||||
|
|
||||||
// Speicher für Zähler-Array reservieren und mit 0 initialisieren
|
|
||||||
counts = (unsigned int *)calloc(maxValue + 1, sizeof(unsigned int));
|
|
||||||
assert(counts != NULL); // Prüfen, ob Speicher erfolgreich reserviert wurde
|
|
||||||
|
|
||||||
// Schleife über alle Elemente des numbers-Arrays
|
|
||||||
// Zählt die Werte und prüft gleichzeitig den gültigen Wertebereich
|
|
||||||
for (i = 0; i < len; ++i)
|
|
||||||
{
|
|
||||||
unsigned int v = numbers[i]; // Aktueller Wert
|
|
||||||
assert(v >= 1 && v <= maxValue); // Wertebereich prüfen
|
|
||||||
counts[v]++; // Anzahl dieses Wertes erhöhen
|
|
||||||
}
|
|
||||||
|
|
||||||
// Schleife über alle möglichen Werte
|
|
||||||
// Prüft, dass genau ein Wert doppelt vorkommt
|
|
||||||
for (i = 1; i <= maxValue; ++i)
|
|
||||||
{
|
|
||||||
if (counts[i] == 2)
|
|
||||||
duplicatesCount++; // Ein doppelter Wert gefunden
|
|
||||||
else
|
|
||||||
assert(counts[i] == 0 || counts[i] == 1); // Alle anderen max. einmal
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(duplicatesCount == 1); // Es darf genau ein Duplikat geben
|
|
||||||
|
|
||||||
free(counts); // Speicher freigeben
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Testet createNumbers und getDuplicate für eine gegebene Länge
|
|
||||||
*/
|
|
||||||
static void test_createNumbers_and_getDuplicate(unsigned int len)
|
|
||||||
{
|
|
||||||
printf("test_createNumbers_and_getDuplicate(len=%u)...\n", len);
|
|
||||||
|
|
||||||
// Zahlenarray erzeugen
|
|
||||||
unsigned int *numbers = createNumbers(len);
|
|
||||||
assert(numbers != NULL); // Rückgabewert prüfen
|
|
||||||
|
|
||||||
// Struktur und Inhalt des Arrays überprüfen
|
|
||||||
check_numbers_array(numbers, len);
|
|
||||||
|
|
||||||
// Erneutes Zählen, um das erwartete Duplikat zu ermitteln
|
|
||||||
unsigned int maxValue = 2 * len;
|
|
||||||
unsigned int *counts = (unsigned int *)calloc(maxValue + 1, sizeof(unsigned int));
|
|
||||||
assert(counts != NULL);
|
|
||||||
|
|
||||||
// Schleife zum Zählen aller Werte
|
|
||||||
for (unsigned int i = 0; i < len; ++i)
|
|
||||||
{
|
|
||||||
unsigned int v = numbers[i];
|
|
||||||
counts[v]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Schleife zur Bestimmung des doppelten Wertes
|
|
||||||
unsigned int expectedDuplicate = 0;
|
|
||||||
for (unsigned int v = 1; v <= maxValue; ++v)
|
|
||||||
{
|
|
||||||
if (counts[v] == 2)
|
|
||||||
{
|
|
||||||
expectedDuplicate = v; // Doppelten Wert merken
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert(expectedDuplicate != 0); // Es muss ein Duplikat existieren
|
|
||||||
|
|
||||||
// getDuplicate-Funktion testen
|
|
||||||
unsigned int found = getDuplicate(numbers, len);
|
|
||||||
assert(found == expectedDuplicate); // Ergebnis vergleichen
|
|
||||||
|
|
||||||
free(counts); // Speicher freigeben
|
|
||||||
free(numbers);
|
|
||||||
|
|
||||||
printf("...OK\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Testet Fehlerfälle für getDuplicate
|
|
||||||
*/
|
|
||||||
static void test_getDuplicate_error_cases(void)
|
|
||||||
{
|
|
||||||
printf("test_getDuplicate_error_cases...\n");
|
|
||||||
|
|
||||||
// Test mit NULL-Pointer
|
|
||||||
unsigned int dup = getDuplicate(NULL, 10);
|
|
||||||
assert(dup == 0);
|
|
||||||
|
|
||||||
// Test mit zu kleiner Länge (< 2)
|
|
||||||
unsigned int dummy[1] = {42};
|
|
||||||
dup = getDuplicate(dummy, 1);
|
|
||||||
assert(dup == 0);
|
|
||||||
|
|
||||||
printf("...OK\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Hauptfunktion: startet alle Unit-Tests
|
|
||||||
*/
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
printf("Running numbers unit tests...\n\n");
|
|
||||||
|
|
||||||
// Typische Testfälle mit unterschiedlichen Array-Längen
|
|
||||||
test_createNumbers_and_getDuplicate(5);
|
|
||||||
test_createNumbers_and_getDuplicate(10);
|
|
||||||
test_createNumbers_and_getDuplicate(20);
|
|
||||||
|
|
||||||
// Test der Fehlerfälle
|
|
||||||
test_getDuplicate_error_cases();
|
|
||||||
|
|
||||||
printf("\nAll numbers tests passed.\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
BIN
test_numbers.exe
BIN
test_numbers.exe
Binary file not shown.
BIN
test_numbers.o
BIN
test_numbers.o
Binary file not shown.
102
test_stack.c
102
test_stack.c
@ -1,102 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include "stack.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Einfache Unit-Tests für den Stack:
|
|
||||||
* - push/pop: LIFO-Verhalten
|
|
||||||
* - top: richtiges Element
|
|
||||||
* - Verhalten bei leerem Stack
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void test_push_pop_lifo(void)
|
|
||||||
{
|
|
||||||
printf("test_push_pop_lifo...\n");
|
|
||||||
|
|
||||||
StackNode *stack = NULL;
|
|
||||||
|
|
||||||
int a = 1;
|
|
||||||
int b = 2;
|
|
||||||
int c = 3;
|
|
||||||
|
|
||||||
stack = push(stack, &a);
|
|
||||||
stack = push(stack, &b);
|
|
||||||
stack = push(stack, &c);
|
|
||||||
|
|
||||||
// LIFO: zuerst c
|
|
||||||
assert(top(stack) == &c);
|
|
||||||
stack = pop(stack);
|
|
||||||
|
|
||||||
// dann b
|
|
||||||
assert(top(stack) == &b);
|
|
||||||
stack = pop(stack);
|
|
||||||
|
|
||||||
// dann a
|
|
||||||
assert(top(stack) == &a);
|
|
||||||
stack = pop(stack);
|
|
||||||
|
|
||||||
// jetzt leer
|
|
||||||
assert(stack == NULL);
|
|
||||||
assert(top(stack) == NULL);
|
|
||||||
|
|
||||||
printf("...OK\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_empty_stack_operations(void)
|
|
||||||
{
|
|
||||||
printf("test_empty_stack_operations...\n");
|
|
||||||
|
|
||||||
StackNode *stack = NULL;
|
|
||||||
|
|
||||||
// pop auf leerem Stack sollte einfach NULL liefern
|
|
||||||
stack = pop(stack);
|
|
||||||
assert(stack == NULL);
|
|
||||||
|
|
||||||
// top auf leerem Stack sollte NULL liefern
|
|
||||||
assert(top(stack) == NULL);
|
|
||||||
|
|
||||||
// clearStack auf leerem Stack darf nicht crashen
|
|
||||||
clearStack(stack);
|
|
||||||
|
|
||||||
printf("...OK\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void test_clearStack(void)
|
|
||||||
{
|
|
||||||
printf("test_clearStack...\n");
|
|
||||||
|
|
||||||
StackNode *stack = NULL;
|
|
||||||
int values[10];
|
|
||||||
int i;
|
|
||||||
|
|
||||||
// Wir legen 10 Elemente auf den Stack
|
|
||||||
for (i = 0; i < 10; ++i)
|
|
||||||
{
|
|
||||||
values[i] = i;
|
|
||||||
stack = push(stack, &values[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stack leeren – clearStack muss alle StackNodes freigeben
|
|
||||||
clearStack(stack);
|
|
||||||
stack = NULL; // Pointer selbst auf NULL setzen
|
|
||||||
|
|
||||||
// Auf leerem Stack dürfen die Operationen nicht crashen
|
|
||||||
assert(top(stack) == NULL);
|
|
||||||
stack = pop(stack);
|
|
||||||
assert(stack == NULL);
|
|
||||||
|
|
||||||
printf("...OK\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
printf("Running stack unit tests...\n\n");
|
|
||||||
|
|
||||||
test_push_pop_lifo();
|
|
||||||
test_empty_stack_operations();
|
|
||||||
test_clearStack();
|
|
||||||
|
|
||||||
printf("\nAll stack tests passed.\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
BIN
test_stack.exe
BIN
test_stack.exe
Binary file not shown.
BIN
test_stack.o
BIN
test_stack.o
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user