Template
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e66d0b417 | ||
|
|
38ce259cbc | ||
|
|
16f3961eb4 | ||
|
|
511611973f | ||
|
|
2cf8b6b149 | ||
|
|
7683a157c9 | ||
|
|
9fb140fecf | ||
|
|
99a974cc9b | ||
|
|
799667ed1c | ||
|
|
d9222b83b4 | ||
|
|
9bf5e6a541 | ||
|
|
57f9914e2b | ||
|
|
09d7f457dd | ||
|
|
13cb5d8c86 | ||
|
|
6528686fb0 | ||
|
|
a3d0585ac1 | ||
|
|
68563ec297 | ||
|
|
55532cbb42 | ||
|
|
d3904ac6e4 | ||
|
|
5e96ec050c | ||
|
|
f42a997683 | ||
|
|
8b44089f23 | ||
|
|
654fb615a2 | ||
|
|
422ac38d54 | ||
|
|
90612e4d04 | ||
|
|
17f4155891 | ||
|
|
a002901e2f | ||
|
|
55b25a227b |
No files matched your search
+2
-60
@@ -1,62 +1,4 @@
|
||||
# ---> C
|
||||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Object files
|
||||
doble_initial.exe
|
||||
*.o
|
||||
*.ko
|
||||
*.obj
|
||||
*.elf
|
||||
|
||||
# Linker output
|
||||
*.ilk
|
||||
*.map
|
||||
*.exp
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Libraries
|
||||
*.lib
|
||||
*.la
|
||||
*.lo
|
||||
|
||||
# Shared objects (inc. Windows DLLs)
|
||||
*.dll
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
*.i*86
|
||||
*.x86_64
|
||||
*.hex
|
||||
Startcode/doble_initial
|
||||
Startcode/doble
|
||||
Startcode/unitTests
|
||||
|
||||
# Debug files
|
||||
*.dSYM/
|
||||
*.su
|
||||
*.idb
|
||||
*.pdb
|
||||
|
||||
# Kernel Module Compile Results
|
||||
*.mod*
|
||||
*.cmd
|
||||
.tmp_versions/
|
||||
modules.order
|
||||
Module.symvers
|
||||
Mkfile.old
|
||||
dkms.conf
|
||||
|
||||
# IDE folders
|
||||
.vscode/
|
||||
.idea/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
highscore.txt
|
||||
Binary file not shown.
@@ -1,36 +0,0 @@
|
||||
#include <string.h>
|
||||
#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. */
|
||||
|
||||
// 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)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// 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).
|
||||
void clearTree(TreeNode *root)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Returns the number of entries in the tree given by root.
|
||||
unsigned int treeSize(const TreeNode *root)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#ifndef BINTREE_H
|
||||
#define BINTREE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef int (*CompareFctType)(const void *arg1, const void *arg2);
|
||||
|
||||
typedef struct node
|
||||
{
|
||||
void *data;
|
||||
struct node *left;
|
||||
struct node *right;
|
||||
} TreeNode;
|
||||
|
||||
// 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);
|
||||
// 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).
|
||||
void clearTree(TreeNode *root);
|
||||
// Returns the number of entries in the tree given by root.
|
||||
unsigned int treeSize(const TreeNode *root);
|
||||
|
||||
#endif
|
||||
@@ -1,134 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "highscore.h"
|
||||
#include "bintree.h"
|
||||
|
||||
#define MAX_LINE_LEN 100
|
||||
#define MAX_PLAYER_NAME_LEN 20
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char name[MAX_PLAYER_NAME_LEN];
|
||||
int score;
|
||||
} HighscoreEntry;
|
||||
|
||||
static TreeNode *highscoreTree = NULL;
|
||||
|
||||
// Compare two highscore entries by score (descending), then by name (ascending).
|
||||
static int compareHighscoreEntries(const void *arg1, const void *arg2)
|
||||
{
|
||||
const HighscoreEntry *entry1 = (const HighscoreEntry *)arg1;
|
||||
const HighscoreEntry *entry2 = (const HighscoreEntry *)arg2;
|
||||
|
||||
int result = entry2->score - entry1->score;
|
||||
|
||||
if(result == 0)
|
||||
result = strcmp(entry1->name, entry2->name);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Create a new highscore entry from name and score.
|
||||
static HighscoreEntry createHighscoreEntry(const char *name, int score)
|
||||
{
|
||||
HighscoreEntry entry = {"", score};
|
||||
|
||||
if(name != NULL)
|
||||
{
|
||||
strncpy(entry.name, name, MAX_PLAYER_NAME_LEN);
|
||||
entry.name[MAX_PLAYER_NAME_LEN-1] = '\0';
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Calculate score based on time used and number of shown numbers.
|
||||
static int calculateScore(double timeInSeconds, unsigned int len)
|
||||
{
|
||||
return (1000.0 - timeInSeconds) * len;
|
||||
}
|
||||
|
||||
// Load highscores from file into memory.
|
||||
void loadHighscores(const char *path)
|
||||
{
|
||||
FILE *file = fopen(path, "r");
|
||||
|
||||
if(file != NULL)
|
||||
{
|
||||
char buffer[MAX_LINE_LEN+1];
|
||||
|
||||
while(fgets(buffer, MAX_LINE_LEN+1, file) != NULL)
|
||||
{
|
||||
char *name = strtok(buffer, ";\n");
|
||||
char *scoreStr = strtok(NULL, ";\n");
|
||||
|
||||
if(name != NULL && scoreStr != NULL)
|
||||
{
|
||||
HighscoreEntry entry = createHighscoreEntry(name, strtol(scoreStr, NULL, 10));
|
||||
highscoreTree = addToTree(highscoreTree, &entry, sizeof(entry), compareHighscoreEntries, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new highscore entry and return the calculated score.
|
||||
int addHighscore(const char *name, double timeInSeconds, unsigned int len)
|
||||
{
|
||||
HighscoreEntry entry = createHighscoreEntry(name, calculateScore(timeInSeconds, len));
|
||||
highscoreTree = addToTree(highscoreTree, &entry, sizeof(entry), compareHighscoreEntries, NULL);
|
||||
|
||||
return entry.score;
|
||||
}
|
||||
|
||||
// Print highscores (up to NUMBER_OF_SHOWN_HIGHSCORES) in a formatted table.
|
||||
void showHighscores()
|
||||
{
|
||||
const char *blanks = " ";
|
||||
const char *stripes = "------------------------------------------------------------------------------------------------------------------------";
|
||||
const char *header = "H I G H S C O R E S";
|
||||
const int lineWidth = MAX_PLAYER_NAME_LEN + MAX_PLAYER_NAME_LEN + 5;
|
||||
|
||||
int blankSpace = (int)(lineWidth - strlen(header)) / 2;
|
||||
|
||||
HighscoreEntry *entry = nextTreeData(highscoreTree);
|
||||
|
||||
printf("+%*.*s+\n", lineWidth, lineWidth, stripes);
|
||||
printf("|%*.*s%s%*.*s|\n", blankSpace, blankSpace, blanks, header, blankSpace, blankSpace, blanks);
|
||||
printf("+%*.*s+\n", lineWidth, lineWidth, stripes);
|
||||
|
||||
for(int i = 0; i < NUMBER_OF_SHOWN_HIGHSCORES && entry != NULL; i++)
|
||||
{
|
||||
printf("| %-*s | %*d |\n", MAX_PLAYER_NAME_LEN, entry->name, MAX_PLAYER_NAME_LEN, entry->score);
|
||||
printf("+%*.*s+\n", lineWidth, lineWidth, stripes);
|
||||
entry = nextTreeData(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// Save highscores to file (up to NUMBER_OF_SHOWN_HIGHSCORES).
|
||||
void saveHighscores(const char *path)
|
||||
{
|
||||
FILE *file = fopen(path, "w");
|
||||
|
||||
if(file != NULL)
|
||||
{
|
||||
HighscoreEntry *entry = nextTreeData(highscoreTree);
|
||||
|
||||
for(int i = 0; i < NUMBER_OF_SHOWN_HIGHSCORES && entry != NULL; i++)
|
||||
{
|
||||
fprintf(file, "%s;%d\n", entry->name, entry->score);
|
||||
entry = nextTreeData(NULL);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
// Free all memory used for highscores.
|
||||
void clearHighscores()
|
||||
{
|
||||
clearTree(highscoreTree);
|
||||
highscoreTree = NULL;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
player1;3999
|
||||
@@ -1,90 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include "numbers.h"
|
||||
#include "timer.h"
|
||||
#include "highscore.h"
|
||||
|
||||
// Read an unsigned integer from stdin with prompt (retries until valid).
|
||||
int inputNumber(const char *promptText)
|
||||
{
|
||||
unsigned int number;
|
||||
int numberOfInputs = 0;
|
||||
|
||||
while(numberOfInputs != 1)
|
||||
{
|
||||
printf("%s", promptText);
|
||||
numberOfInputs = scanf("%u", &number);
|
||||
while(getchar() != '\n') {} // clear input buffer
|
||||
}
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
// Print an array of numbers.
|
||||
void showNumbers(const unsigned int *numbers, unsigned int len)
|
||||
{
|
||||
if(numbers != NULL)
|
||||
{
|
||||
printf("Numbers:");
|
||||
|
||||
for(int i = 0; i < len; i++)
|
||||
printf(" %5d", numbers[i]);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Main game loop: generate numbers, ask user for duplicate, measure time, update highscores.
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int exitCode = EXIT_FAILURE;
|
||||
|
||||
if(argc != 2)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <player name>\n", argv[0]);
|
||||
exitCode = EXIT_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *highscorePath = "highscores.txt";
|
||||
const char *playerName = argv[1];
|
||||
unsigned int *numbers = NULL;
|
||||
unsigned int duplicate = 0;
|
||||
double measuredSeconds;
|
||||
unsigned int userInput;
|
||||
unsigned int numberOfElements = 0;
|
||||
|
||||
// ask until valid number of elements (3..1000)
|
||||
while(numberOfElements < 3 || numberOfElements > 1000)
|
||||
numberOfElements = inputNumber("Wie viele Zahlen sollen gezeigt werden: ");
|
||||
|
||||
// create numbers and show them
|
||||
numbers = createNumbers(numberOfElements);
|
||||
showNumbers(numbers, numberOfElements);
|
||||
|
||||
// measure time while user guesses the duplicate
|
||||
startTimer();
|
||||
userInput = inputNumber("Welche Zahl kommt doppelt vor: ");
|
||||
measuredSeconds = stopTimer();
|
||||
|
||||
duplicate = getDuplicate(numbers, numberOfElements);
|
||||
|
||||
// check result and update highscores
|
||||
if(userInput == duplicate)
|
||||
{
|
||||
int score = addHighscore(playerName, measuredSeconds, numberOfElements);
|
||||
printf("Sie haben die korrekte Zahl in %.6lf Sekunde(n) gefunden und %u Punkte erzielt.\n", measuredSeconds, score);
|
||||
}
|
||||
else
|
||||
printf("Leider ist %u nicht korrekt. Richtig waere %u gewesen.\n", userInput, duplicate);
|
||||
|
||||
loadHighscores(highscorePath);
|
||||
showHighscores();
|
||||
saveHighscores(highscorePath);
|
||||
clearHighscores();
|
||||
|
||||
exitCode = EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
CC = gcc
|
||||
FLAGS = -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 = stack.o bintree.o numbers.o timer.o highscore.o
|
||||
|
||||
doble : main.o $(program_obj_files)
|
||||
$(CC) $(FLAGS) $^ -o doble
|
||||
|
||||
$(program_obj_files): %.o: %.c
|
||||
$(CC) -c $(FLAGS) $^ -o $@
|
||||
|
||||
# --------------------------
|
||||
# Unit Tests
|
||||
# --------------------------
|
||||
unitTests:
|
||||
echo "needs to be implemented"
|
||||
|
||||
# --------------------------
|
||||
# Clean
|
||||
# --------------------------
|
||||
clean:
|
||||
ifeq ($(OS),Windows_NT)
|
||||
del /f *.o doble
|
||||
else
|
||||
rm -f *.o doble
|
||||
endif
|
||||
@@ -1,26 +0,0 @@
|
||||
#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.
|
||||
unsigned int *createNumbers(unsigned int len)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#include <stdlib.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.
|
||||
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.)
|
||||
StackNode *pop(StackNode *stack)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Returns the data of the top element.
|
||||
void *top(StackNode *stack)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Clears stack and releases all memory.
|
||||
void clearStack(StackNode *stack)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
#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. */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
//TODO: passenden Datentyp als struct anlegen
|
||||
|
||||
// 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.)
|
||||
StackNode *pop(StackNode *stack);
|
||||
|
||||
// Returns the data of the top element.
|
||||
void *top(StackNode *stack);
|
||||
|
||||
// Clears stack and releases all memory.
|
||||
void clearStack(StackNode *stack);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,126 @@
|
||||
#include "bintree.h"
|
||||
#include "stack.h"
|
||||
#include <string.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. */
|
||||
|
||||
// typedef int (*CompareFctType)(const void *arg1, const void *arg2);
|
||||
// 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 *createTreeNode(const void *data, size_t dataSize) {
|
||||
TreeNode *node =
|
||||
malloc(sizeof(TreeNode)); // Speicher für neuen Knoten reservieren
|
||||
if (node == NULL)
|
||||
return NULL; // Abbrechen bei Fehler
|
||||
|
||||
node->data = malloc(dataSize); // Speicher für Daten reservieren
|
||||
if (node->data == NULL) {
|
||||
free(node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy(node->data, data, dataSize); // Standardfunktion string.h, kopiert
|
||||
// size bytes von data nach node->data,
|
||||
// daten dürfen sich nicht überschneiden
|
||||
// speichern der Daten in node->data
|
||||
node->left = NULL; // Kinder sind NULL
|
||||
node->right = NULL;
|
||||
return node;
|
||||
}
|
||||
|
||||
TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize,
|
||||
CompareFctType compareFct, int *isDuplicate) {
|
||||
|
||||
// isDuplicate initialisieren (auf 0 setzen), verhindert Änderung am Baum
|
||||
if (isDuplicate) {
|
||||
*isDuplicate = 0;
|
||||
} // bei 0: neuer Wert wurde eingefügt, bei 1: Wert war bereits im Baum
|
||||
|
||||
// leerer Baum
|
||||
if (root == NULL) {
|
||||
return createTreeNode(data, dataSize);
|
||||
}
|
||||
|
||||
// mit compareFct <0 links >0 rechts =0 Duplikat
|
||||
int compare = compareFct(data, root->data);
|
||||
|
||||
if (compare < 0) { // Eintrag links
|
||||
root->left = addToTree(root->left, data, dataSize, compareFct, isDuplicate);
|
||||
} else if (compare > 0) { // Eintrag rechts
|
||||
root->right =
|
||||
addToTree(root->right, data, dataSize, compareFct, isDuplicate);
|
||||
} else { // Duplikat
|
||||
// isDuplicate auf 1 setzen, keine Änderung am Baum
|
||||
if (isDuplicate) {
|
||||
*isDuplicate = 1;
|
||||
}
|
||||
}
|
||||
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) { // highscore nutzt Funktion für die Ausgabe der Punktzahl
|
||||
static StackNode *stack = NULL; // static -> behält Wert bei mehreren Aufrufen
|
||||
|
||||
// Neue Iteration starten
|
||||
if (root != NULL) {
|
||||
clearStack(&stack); // alte Stack-Inhalte werden gelöscht
|
||||
|
||||
TreeNode *currentNode = root;
|
||||
while (currentNode !=
|
||||
NULL) { // alle linken Knoten werden vom root an auf den Stack gelegt
|
||||
|
||||
stack = push(stack, currentNode);
|
||||
currentNode = currentNode->left;
|
||||
}
|
||||
}
|
||||
if (stack == NULL)
|
||||
return NULL; // wenn Stack leer ist sind keine Elemente mehr vorhanden,
|
||||
// Iteration beendet
|
||||
|
||||
// oberster Knoten vom Stack
|
||||
TreeNode *node = top(stack);
|
||||
stack = pop(stack);
|
||||
|
||||
// Rechten Teilbaum pushen
|
||||
TreeNode *currentNode = node->right;
|
||||
while (currentNode != NULL) {
|
||||
stack = push(stack, currentNode);
|
||||
currentNode = currentNode->left;
|
||||
}
|
||||
return node->data; // Pointer auf Daten
|
||||
}
|
||||
|
||||
// Releases all memory resources (including data copies).
|
||||
void clearTree(TreeNode **root) { // rekursive Funktion zum freigeben des
|
||||
// Speichers und Nullsetzen der Pointer
|
||||
if (root == NULL || *root == NULL)
|
||||
return;
|
||||
|
||||
clearTree(&(*root)->left); // linken Teilbaum löschen
|
||||
clearTree(&(*root)->right); // rechten Teilbaum löschen
|
||||
|
||||
free((*root)->data); // Daten freigeben
|
||||
(*root)->data = NULL;
|
||||
free(*root); // Knoten freigeben
|
||||
*root = NULL; // Zeiger auf NULL setzen
|
||||
}
|
||||
|
||||
// Returns the number of entries in the tree given by root.
|
||||
unsigned int treeSize(const TreeNode *root) {
|
||||
|
||||
if (root == NULL)
|
||||
return 0;
|
||||
return 1 + treeSize(root->left) + treeSize(root->right);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#ifndef BINTREE_H
|
||||
#define BINTREE_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef int (*CompareFctType)(const void *arg1, const void *arg2);
|
||||
|
||||
typedef struct node {
|
||||
void *data;
|
||||
struct node *left;
|
||||
struct node *right;
|
||||
} TreeNode;
|
||||
|
||||
TreeNode *createTreeNode(const void *data, size_t dataSize);
|
||||
|
||||
// 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);
|
||||
// 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).
|
||||
void clearTree(TreeNode **root);
|
||||
// Returns the number of entries in the tree given by root.
|
||||
unsigned int treeSize(const TreeNode *root);
|
||||
|
||||
#endif
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
#include "highscore.h"
|
||||
#include "bintree.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_LINE_LEN 100
|
||||
#define MAX_PLAYER_NAME_LEN 20
|
||||
|
||||
typedef struct {
|
||||
char name[MAX_PLAYER_NAME_LEN];
|
||||
int score;
|
||||
} HighscoreEntry;
|
||||
|
||||
static TreeNode *highscoreTree = NULL;
|
||||
|
||||
// Compare two highscore entries by score (descending), then by name
|
||||
// (ascending).
|
||||
static int compareHighscoreEntries(const void *arg1, const void *arg2) {
|
||||
const HighscoreEntry *entry1 = (const HighscoreEntry *)arg1;
|
||||
const HighscoreEntry *entry2 = (const HighscoreEntry *)arg2;
|
||||
|
||||
int result = entry2->score - entry1->score;
|
||||
|
||||
if (result == 0)
|
||||
result = strcmp(entry1->name, entry2->name);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Create a new highscore entry from name and score.
|
||||
static HighscoreEntry createHighscoreEntry(const char *name, int score) {
|
||||
HighscoreEntry entry = {"", score};
|
||||
|
||||
if (name != NULL) {
|
||||
strncpy(entry.name, name, MAX_PLAYER_NAME_LEN);
|
||||
entry.name[MAX_PLAYER_NAME_LEN - 1] = '\0';
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
// Calculate score based on time used and number of shown numbers.
|
||||
static int calculateScore(double timeInSeconds, unsigned int len) {
|
||||
return (1000.0 - timeInSeconds) * len;
|
||||
}
|
||||
|
||||
// Load highscores from file into memory.
|
||||
void loadHighscores(const char *path) {
|
||||
FILE *file = fopen(path, "r");
|
||||
|
||||
if (file != NULL) {
|
||||
char buffer[MAX_LINE_LEN + 1];
|
||||
|
||||
while (fgets(buffer, MAX_LINE_LEN + 1, file) != NULL) {
|
||||
char *name = strtok(buffer, ";\n");
|
||||
char *scoreStr = strtok(NULL, ";\n");
|
||||
|
||||
if (name != NULL && scoreStr != NULL) {
|
||||
HighscoreEntry entry =
|
||||
createHighscoreEntry(name, strtol(scoreStr, NULL, 10));
|
||||
highscoreTree = addToTree(highscoreTree, &entry, sizeof(entry),
|
||||
compareHighscoreEntries, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new highscore entry and return the calculated score.
|
||||
int addHighscore(const char *name, double timeInSeconds, unsigned int len) {
|
||||
HighscoreEntry entry =
|
||||
createHighscoreEntry(name, calculateScore(timeInSeconds, len));
|
||||
highscoreTree = addToTree(highscoreTree, &entry, sizeof(entry),
|
||||
compareHighscoreEntries, NULL);
|
||||
|
||||
return entry.score;
|
||||
}
|
||||
|
||||
// Print highscores (up to NUMBER_OF_SHOWN_HIGHSCORES) in a formatted table.
|
||||
void showHighscores() {
|
||||
const char *blanks =
|
||||
" "
|
||||
" ";
|
||||
const char *stripes =
|
||||
"------------------------------------------------------------------------"
|
||||
"------------------------------------------------";
|
||||
const char *header = "H I G H S C O R E S";
|
||||
const int lineWidth = MAX_PLAYER_NAME_LEN + MAX_PLAYER_NAME_LEN + 5;
|
||||
|
||||
int blankSpace = (int)(lineWidth - strlen(header)) / 2;
|
||||
|
||||
HighscoreEntry *entry = nextTreeData(highscoreTree);
|
||||
|
||||
printf("+%*.*s+\n", lineWidth, lineWidth, stripes);
|
||||
printf("|%*.*s%s%*.*s|\n", blankSpace, blankSpace, blanks, header, blankSpace,
|
||||
blankSpace, blanks);
|
||||
printf("+%*.*s+\n", lineWidth, lineWidth, stripes);
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_SHOWN_HIGHSCORES && entry != NULL; i++) {
|
||||
printf("| %-*s | %*d |\n", MAX_PLAYER_NAME_LEN, entry->name,
|
||||
MAX_PLAYER_NAME_LEN, entry->score);
|
||||
printf("+%*.*s+\n", lineWidth, lineWidth, stripes);
|
||||
entry = nextTreeData(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// Save highscores to file (up to NUMBER_OF_SHOWN_HIGHSCORES).
|
||||
void saveHighscores(const char *path) {
|
||||
FILE *file = fopen(path, "w");
|
||||
|
||||
if (file != NULL) {
|
||||
HighscoreEntry *entry = nextTreeData(highscoreTree);
|
||||
|
||||
for (int i = 0; i < NUMBER_OF_SHOWN_HIGHSCORES && entry != NULL; i++) {
|
||||
fprintf(file, "%s;%d\n", entry->name, entry->score);
|
||||
entry = nextTreeData(NULL);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
// Free all memory used for highscores.
|
||||
void clearHighscores() {
|
||||
clearTree(&highscoreTree);
|
||||
highscoreTree = NULL;
|
||||
}
|
||||
File renamed without changes.
Whitespace-only changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,88 @@
|
||||
#include "highscore.h"
|
||||
#include "numbers.h"
|
||||
#include "timer.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h> // für Zufallszahlen hinzugefügt
|
||||
|
||||
// Read an unsigned integer from stdin with prompt (retries until valid).
|
||||
int inputNumber(const char *promptText) {
|
||||
unsigned int number;
|
||||
int numberOfInputs = 0;
|
||||
|
||||
while (numberOfInputs != 1) {
|
||||
printf("%s", promptText);
|
||||
numberOfInputs = scanf("%u", &number);
|
||||
while (getchar() != '\n') {
|
||||
} // clear input buffer
|
||||
}
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
// Print an array of numbers.
|
||||
void showNumbers(const unsigned int *numbers, unsigned int len) {
|
||||
if (numbers != NULL) {
|
||||
printf("Numbers:");
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
printf(" %5d", numbers[i]);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Main game loop: generate numbers, ask user for duplicate, measure time,
|
||||
// update highscores.
|
||||
int main(int argc, char *argv[]) {
|
||||
srand(time(NULL)); // seed für srand nachträglich eingefügt
|
||||
int exitCode = EXIT_FAILURE;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "Usage: %s <player name>\n", argv[0]);
|
||||
exitCode = EXIT_FAILURE;
|
||||
} else {
|
||||
const char *highscorePath = "highscores.txt";
|
||||
const char *playerName = argv[1];
|
||||
unsigned int *numbers = NULL;
|
||||
unsigned int duplicate = 0;
|
||||
double measuredSeconds;
|
||||
unsigned int userInput;
|
||||
unsigned int numberOfElements = 0;
|
||||
|
||||
// ask until valid number of elements (3..1000)
|
||||
while (numberOfElements < 3 || numberOfElements > 1000)
|
||||
numberOfElements =
|
||||
inputNumber("Wie viele Zahlen sollen gezeigt werden: ");
|
||||
|
||||
// create numbers and show them
|
||||
numbers = createNumbers(numberOfElements);
|
||||
showNumbers(numbers, numberOfElements);
|
||||
|
||||
// measure time while user guesses the duplicate
|
||||
startTimer();
|
||||
userInput = inputNumber("Welche Zahl kommt doppelt vor: ");
|
||||
measuredSeconds = stopTimer();
|
||||
|
||||
duplicate = getDuplicate(numbers, numberOfElements);
|
||||
|
||||
// check result and update highscores
|
||||
if (userInput == duplicate) {
|
||||
int score = addHighscore(playerName, measuredSeconds, numberOfElements);
|
||||
printf("Sie haben die korrekte Zahl in %.6lf Sekunde(n) gefunden und %u "
|
||||
"Punkte erzielt.\n",
|
||||
measuredSeconds, score);
|
||||
} else
|
||||
printf("Leider ist %u nicht korrekt. Richtig waere %u gewesen.\n",
|
||||
userInput, duplicate);
|
||||
|
||||
loadHighscores(highscorePath);
|
||||
showHighscores();
|
||||
saveHighscores(highscorePath);
|
||||
clearHighscores();
|
||||
|
||||
exitCode = EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
CC = gcc
|
||||
FLAGS = -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
|
||||
# --------------------------
|
||||
# alle Objektdateien
|
||||
program_obj_files = stack.o bintree.o numbers.o timer.o highscore.o
|
||||
# alle ausführbaren Dateien zu ausführbarem Programm linken
|
||||
doble : main.o $(program_obj_files)
|
||||
$(CC) $(FLAGS) $^ -o doble
|
||||
# Regel Kompilieren allgemein
|
||||
$(program_obj_files): %.o: %.c
|
||||
$(CC) -c $(FLAGS) $^ -o $@
|
||||
|
||||
# --------------------------
|
||||
# Unit Tests
|
||||
# --------------------------
|
||||
|
||||
STACK_TEST_BIN = runStackTests
|
||||
NUMBERS_TEST_BIN = runNumbersTests
|
||||
BINARY_TEST_BIN = runBinaryTests
|
||||
|
||||
# --- Stack Tests ---
|
||||
stackTests: stack.o test_stack.o
|
||||
$(CC) $(FLAGS) -I$(unityfolder) -o $(STACK_TEST_BIN) stack.o test_stack.o $(unityfolder)/unity.c
|
||||
|
||||
test_stack.o: test_stack.c
|
||||
$(CC) $(FLAGS) -I$(unityfolder) -c test_stack.c -o test_stack.o
|
||||
|
||||
|
||||
# --- Numbers Tests ---
|
||||
numbersTests: numbers.o bintree.o stack.o test_numbers.o
|
||||
$(CC) $(FLAGS) -I$(unityfolder) -o $(NUMBERS_TEST_BIN) numbers.o bintree.o stack.o test_numbers.o $(unityfolder)/unity.c
|
||||
|
||||
test_numbers.o: test_numbers.c
|
||||
$(CC) $(FLAGS) -I$(unityfolder) -c test_numbers.c -o test_numbers.o
|
||||
|
||||
|
||||
# --- Binary Tree Tests ---
|
||||
binaryTests: bintree.o stack.o test_binary.o
|
||||
$(CC) $(FLAGS) -I$(unityfolder) -o $(BINARY_TEST_BIN) bintree.o stack.o test_binary.o $(unityfolder)/unity.c
|
||||
|
||||
test_binary.o: test_binary.c
|
||||
$(CC) $(FLAGS) -I$(unityfolder) -c test_binary.c -o test_binary.o
|
||||
|
||||
|
||||
# --------------------------
|
||||
# Clean
|
||||
# --------------------------
|
||||
clean:
|
||||
rm -f *.o doble $(STACK_TEST_BIN) $(NUMBERS_TEST_BIN) $(BINARY_TEST_BIN)
|
||||
> highscores.txt
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,114 @@
|
||||
#include "numbers.h"
|
||||
#include "bintree.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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.
|
||||
|
||||
// typedef int (*CompareFctType)(const void *arg1, const void *arg2);
|
||||
// vergleicht zwei Werte: a<b: -1 a>b: 1 a=b: 0
|
||||
int compareUnsignedInt(const void *a, const void *b) { // generischer Zeiger
|
||||
unsigned int x = *(unsigned int *)a; // Typumwandlung: void * → unsigned int *
|
||||
unsigned int y = *(unsigned int *)b; // Wert wird durch *b in y gespeichert
|
||||
if (x < y)
|
||||
return -1;
|
||||
if (x > y)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
// numbers = createNumbers(numberOfElements);
|
||||
// es wird die angefragte Länge des Arrays übergeben
|
||||
unsigned int *createNumbers(unsigned int len) {
|
||||
if (len < 2) // Duplikat bei einem Eintrag sinnlos
|
||||
return NULL;
|
||||
|
||||
unsigned int *numbersArray = malloc(
|
||||
sizeof(unsigned int) * len); // Speicher für das Ausgabearray reservieren:
|
||||
// Größe eines Eintrags * Größe des Arrays
|
||||
if (numbersArray == NULL) // Speicher konnte nicht reserviert werden
|
||||
return NULL;
|
||||
|
||||
/*typedef struct node {
|
||||
void *data;
|
||||
struct node *left;
|
||||
struct node *right;
|
||||
} TreeNode;*/
|
||||
|
||||
TreeNode *root =
|
||||
NULL; // Binärbaum zum Generieren der Zufallszahlen ohne Duplikate
|
||||
|
||||
for (unsigned int i = 0; i < len; i++) { // angefordertes Array füllen
|
||||
unsigned int currentNumber;
|
||||
int isDuplicate;
|
||||
do { // mindestens eine Zufallszahl erzeugen
|
||||
currentNumber = (rand() % (2 * len)) + 1; // Zahlenbereich 1 bis 2*len
|
||||
isDuplicate = 0;
|
||||
root = addToTree(root, ¤tNumber, sizeof(unsigned int),
|
||||
compareUnsignedInt,
|
||||
&isDuplicate); // compareUnsignedInt wird zum Verwenden
|
||||
// bei Vergleichen übergeben
|
||||
} while (isDuplicate); // wenn isDuplicate gesetzt wird, muss eine neue Zahl
|
||||
// erzeugt werden, die Schleife wird wiederholt
|
||||
numbersArray[i] =
|
||||
currentNumber; // Schleife konnte verlassen werden -> kein Duplikat
|
||||
// gefunden, Zahl für den aktuellen Iterationsschritt
|
||||
// wird im Array gespeichert
|
||||
}
|
||||
|
||||
// Ein zufälliges Duplikat erzeugen
|
||||
unsigned int duplicateIndex =
|
||||
rand() % len; // Index des Duplikats per Zufall bestimmen
|
||||
unsigned int newIndex;
|
||||
do {
|
||||
newIndex = rand() % len;
|
||||
} while (newIndex == duplicateIndex); // zweiten Index bestimmen, der nicht
|
||||
// mit dem ersten übereinstimmt
|
||||
|
||||
numbersArray[newIndex] =
|
||||
numbersArray[duplicateIndex]; // Wert vom ersten Index kopieren
|
||||
|
||||
clearTree(&root); // Speicher wieder freigeben, wird nicht mehr benötigt
|
||||
return numbersArray; // numbers wird zum pointer auf in der Funktion
|
||||
// erstelltes Array
|
||||
}
|
||||
|
||||
// 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) { // array numbers, sowie die Länge wird übergeben
|
||||
|
||||
if (!numbers || len < 2)
|
||||
return 0; // fehlerhaftes Array
|
||||
|
||||
TreeNode *root = NULL; // leerer Baum
|
||||
unsigned int duplicateValue = 0; // Wert des Duplikats
|
||||
|
||||
for (unsigned int i = 0; i < len && duplicateValue == 0;
|
||||
i++) { // Schleife bricht ab, sobald das Duplikat gefunden wurde
|
||||
int isDuplicate = 0;
|
||||
|
||||
// Zahl in den Baum einfügen
|
||||
root = addToTree(root, &numbers[i], sizeof(unsigned int),
|
||||
compareUnsignedInt, &isDuplicate);
|
||||
|
||||
// Duplikat erkannt
|
||||
if (isDuplicate == 1) {
|
||||
duplicateValue = numbers[i]; // Duplikat merken, for-Schleife wird beendet
|
||||
}
|
||||
}
|
||||
|
||||
clearTree(&root); // Baum freigeben
|
||||
return duplicateValue; // 0, falls kein Duplikat
|
||||
}
|
||||
@@ -1,12 +1,16 @@
|
||||
#ifndef NUMBERS_H
|
||||
#define NUMBERS_H
|
||||
|
||||
// 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.
|
||||
int compareUnsignedInt(const void *a, const void *b);
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,100 @@
|
||||
#include "stack.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*typedef struct {
|
||||
|
||||
void *data;
|
||||
struct StackNode *next;
|
||||
struct StackNode *prev;
|
||||
|
||||
} StackNode;*/
|
||||
|
||||
// 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. */
|
||||
|
||||
// [A] -> [B] -> [C] -> NULL
|
||||
// stack -> stack.next
|
||||
|
||||
// Funktion zum erstellen neuer nodes
|
||||
StackNode *createNode(void *data) {
|
||||
// Speicher reservieren
|
||||
StackNode *node = malloc(sizeof(StackNode));
|
||||
// Speicher konnte nicht reserviert werden
|
||||
if (node == NULL)
|
||||
return NULL;
|
||||
|
||||
node->data = data;
|
||||
node->next = NULL;
|
||||
node->prev = NULL;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
// Pushes data as pointer onto the stack.
|
||||
StackNode *push(StackNode *stack, void *data) {
|
||||
|
||||
StackNode *newNode = createNode(data);
|
||||
|
||||
// Fehler beim Reservieren des Speichers, stack wird unverändert zurückgegeben
|
||||
if (newNode == NULL) {
|
||||
return stack;
|
||||
}
|
||||
|
||||
// der aktuelle Kopf wird der nächste Node
|
||||
newNode->next = stack;
|
||||
|
||||
// bisheriger Kopf bekommt Pointer auf oberstes Element
|
||||
if (stack != NULL) {
|
||||
stack->prev = newNode;
|
||||
}
|
||||
|
||||
return newNode; // neuer Kopf wird zurückgegeben
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
// Stack ohne Elemente
|
||||
if (stack == NULL)
|
||||
return NULL;
|
||||
|
||||
// Element unter Kopf wird als nextNode gespeichert
|
||||
StackNode *nextNode = stack->next;
|
||||
if (nextNode != NULL) {
|
||||
nextNode->prev = NULL; // der Zeiger zum Kopf wird auf NULL gesetzt
|
||||
}
|
||||
|
||||
free(stack);
|
||||
|
||||
stack = NULL; // Speicher des Kopfes freigeben
|
||||
|
||||
return nextNode; // neuen Kopf zurückgeben
|
||||
}
|
||||
|
||||
// Returns the data of the top element.
|
||||
void *top(StackNode *stack) {
|
||||
// wenn stack leer ist, wird NULL zurückgegeben
|
||||
// Zeiger auf Daten des obersten Elements
|
||||
return stack ? stack->data : NULL;
|
||||
}
|
||||
|
||||
// Clears stack and releases all memory.
|
||||
void clearStack(StackNode **stack) { // Zeiger auf den Zeiger auf den Stackkopf
|
||||
// verändert den Zeiger selbst, mit *stack lokale Kopie
|
||||
// im Aufruf &stack verwenden
|
||||
while (*stack != NULL) {
|
||||
|
||||
(*stack)->prev = NULL; // späteren Pointerzugriff verhindern
|
||||
StackNode *next = (*stack)->next; // nächstes Element speichern
|
||||
|
||||
(*stack)->next = NULL; // späteren Pointerzugriff verhindern
|
||||
|
||||
free(*stack); // aktuelles Element freigeben
|
||||
*stack = next; // Zeiger auf nächsten Knoten setzen
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#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. */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
// TODO: passenden Datentyp als struct anlegen
|
||||
|
||||
typedef struct StackNode {
|
||||
|
||||
void *data;
|
||||
struct StackNode *next;
|
||||
struct StackNode *prev;
|
||||
|
||||
} StackNode;
|
||||
|
||||
StackNode *createNode(void *data);
|
||||
|
||||
// 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.)
|
||||
StackNode *pop(StackNode *stack);
|
||||
|
||||
// Returns the data of the top element.
|
||||
void *top(StackNode *stack);
|
||||
|
||||
// Clears stack and releases all memory.
|
||||
void clearStack(StackNode **stack);
|
||||
|
||||
#endif
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
#include "unity.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "bintree.h"
|
||||
|
||||
int compareUnsignedInt(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;
|
||||
}
|
||||
|
||||
TreeNode *root = NULL;
|
||||
|
||||
void setUp(void) {
|
||||
root = NULL; // vor jedem Test leeren
|
||||
}
|
||||
|
||||
void tearDown(void) { clearTree(&root); }
|
||||
|
||||
// Test, ob addToTree Knoten korrekt hinzufügt
|
||||
|
||||
/*TreeNode *addToTree(TreeNode *root, const void *data, size_t dataSize,
|
||||
CompareFctType compareFct, int *isDuplicate) */
|
||||
void test_addToTree_basic(void) {
|
||||
int isDuplicate;
|
||||
unsigned int testInt = 10;
|
||||
root = addToTree(root, &testInt, sizeof(testInt), compareUnsignedInt,
|
||||
&isDuplicate);
|
||||
TEST_ASSERT_NOT_NULL(root); // Knoten wurde erfolgreich erzeugt
|
||||
TEST_ASSERT_EQUAL_UINT(
|
||||
10,
|
||||
*(unsigned int *)root
|
||||
->data); // Datenzeiger wurde richtig gesetzt, void pointer auf
|
||||
// unsigned int pointer casten, mit *wird der Wert abgerufen
|
||||
TEST_ASSERT_EQUAL_INT(0, isDuplicate); // kein Duplikat
|
||||
TEST_ASSERT_EQUAL_UINT(1, treeSize(root)); // der tree hat einen Eintrag
|
||||
}
|
||||
|
||||
// Test, dass Duplikate erkannt werden
|
||||
void test_addToTree_duplicate(void) {
|
||||
int isDuplicate;
|
||||
unsigned int val1 = 10, val2 = 10; // Duplikate
|
||||
root = addToTree(root, &val1, sizeof(val1), compareUnsignedInt,
|
||||
&isDuplicate); // val 1 zum leeren Baum hinzufügen
|
||||
TEST_ASSERT_EQUAL_INT(0, isDuplicate); // erster Knoten->kein Duplikat
|
||||
root = addToTree(root, &val2, sizeof(val2), compareUnsignedInt,
|
||||
&isDuplicate); // val 2 hinzufügen
|
||||
TEST_ASSERT_EQUAL_INT(1, isDuplicate); // Duplikat erkannt
|
||||
TEST_ASSERT_EQUAL_UINT(1,
|
||||
treeSize(root)); // Duplikate wurde nicht hinzugefügt
|
||||
}
|
||||
|
||||
// Test nextTreeData Traversierung
|
||||
void test_nextTreeData_in_order(void) {
|
||||
unsigned int values[] = {20, 10, 30}; // erwartete Ausgabe: 10 -> 20 -> 30
|
||||
int isDuplicate;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
root = addToTree(root, &values[i], sizeof(values[i]), compareUnsignedInt,
|
||||
&isDuplicate); // Baum füllen
|
||||
}
|
||||
|
||||
unsigned int expected[] = {10, 20, 30}; // erwartet in Order Reihenfolge
|
||||
int valueID = 0;
|
||||
void *data;
|
||||
|
||||
// Neue Iteration starten
|
||||
data = nextTreeData(root);
|
||||
while (data != NULL) {
|
||||
TEST_ASSERT_EQUAL_UINT(expected[valueID],
|
||||
*(unsigned int *)data); // entspricht erwartetem Wert
|
||||
|
||||
valueID++;
|
||||
data = nextTreeData(NULL); // weitere Elemente abrufen
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(3, valueID); // alle 3 Knoten besucht
|
||||
}
|
||||
|
||||
// Testet, dass clearTree Speicher freigibt und Root auf NULL setzt
|
||||
void test_clearTree_sets_root_null(void) {
|
||||
int isDuplicate;
|
||||
unsigned int val1 = 10, val2 = 20;
|
||||
|
||||
root = addToTree(root, &val1, sizeof(val1), compareUnsignedInt, &isDuplicate);
|
||||
root = addToTree(root, &val2, sizeof(val2), compareUnsignedInt, &isDuplicate);
|
||||
|
||||
// Vor dem Clear prüfen, dass Root nicht NULL ist
|
||||
TEST_ASSERT_NOT_NULL(root);
|
||||
|
||||
clearTree(&root);
|
||||
|
||||
// Nach dem Clear muss Root auf NULL gesetzt sein
|
||||
TEST_ASSERT_NULL(root);
|
||||
}
|
||||
|
||||
// Test treeSize zählt korrekt
|
||||
void test_treeSize(void) {
|
||||
unsigned int testInts[] = {10, 20, 5};
|
||||
int isDuplicate;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
root = addToTree(root, &testInts[i], sizeof(testInts[i]),
|
||||
compareUnsignedInt, &isDuplicate);
|
||||
}
|
||||
TEST_ASSERT_EQUAL_UINT(3, treeSize(root));
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
UNITY_BEGIN();
|
||||
|
||||
printf(
|
||||
"\n------------------------binarytree test------------------------\n\n");
|
||||
|
||||
RUN_TEST(test_addToTree_basic);
|
||||
RUN_TEST(test_addToTree_duplicate);
|
||||
RUN_TEST(test_nextTreeData_in_order);
|
||||
RUN_TEST(test_clearTree_sets_root_null);
|
||||
RUN_TEST(test_treeSize);
|
||||
return UNITY_END();
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
#include "unity.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "numbers.h"
|
||||
|
||||
#define TEST_ARRAY_LEN 100
|
||||
|
||||
// Speicher für ein Array wird reserviert
|
||||
void test_createNumbers_length(void) { // erstellt ein Array der Länge hundert
|
||||
unsigned int *testArray = createNumbers(TEST_ARRAY_LEN);
|
||||
TEST_ASSERT_NOT_NULL(testArray);
|
||||
free(testArray);
|
||||
}
|
||||
|
||||
// Duplikat ist genau einmal vorhanden
|
||||
void test_createNumbers_single_duplicate(void) {
|
||||
unsigned int *testArray = createNumbers(TEST_ARRAY_LEN); // Array erstellen
|
||||
TEST_ASSERT_NOT_NULL(testArray); // Speicher konnte reserviert werden
|
||||
|
||||
unsigned int duplicate =
|
||||
getDuplicate(testArray, TEST_ARRAY_LEN); // Duplikat holen
|
||||
TEST_ASSERT_TRUE(duplicate > 0); // Duplikat ist größer als 0
|
||||
TEST_ASSERT_TRUE(
|
||||
duplicate <
|
||||
(2 * TEST_ARRAY_LEN)); // Duplikat liegt im vorgegebenen Zahlenbereich
|
||||
|
||||
unsigned int count = 0; // Anzahl der Duplikate
|
||||
for (unsigned int i = 0; i < TEST_ARRAY_LEN;
|
||||
i++) { // Einträge des testArrays auf Duplikate prüfen
|
||||
if (testArray[i] == duplicate) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
TEST_ASSERT_EQUAL_UINT(2, count); // Duplikat zwei mal vorhanden
|
||||
|
||||
free(testArray); // Speicher freigeben
|
||||
}
|
||||
|
||||
// getDuplicate testen
|
||||
void test_getDuplicate_manual_array(
|
||||
void) { // duplikat in fremden array wird gefunden
|
||||
unsigned int numbers[5] = {10, 20, 30, 40, 20};
|
||||
unsigned int duplicate = getDuplicate(numbers, 5);
|
||||
TEST_ASSERT_EQUAL_UINT(20, duplicate);
|
||||
}
|
||||
|
||||
// Rückgabewert kein Duplikat soll 0 sein
|
||||
void test_getDuplicate_no_duplicate(void) {
|
||||
unsigned int numbers[5] = {1, 2, 3, 4, 5};
|
||||
TEST_ASSERT_EQUAL_UINT(0, getDuplicate(numbers, 5));
|
||||
}
|
||||
|
||||
// getDuplicate erkennt fehlerhaftes Array
|
||||
// unsigned int getDuplicate(const unsigned int *numbers, unsigned int len)
|
||||
void test_getDuplicate_invalid_input(void) {
|
||||
TEST_ASSERT_EQUAL_UINT(0, getDuplicate(NULL, 5));
|
||||
unsigned int testArray[1] = {2};
|
||||
TEST_ASSERT_EQUAL_UINT(0, getDuplicate(testArray, 1));
|
||||
}
|
||||
|
||||
void setUp(void) {}
|
||||
void tearDown(void) {}
|
||||
|
||||
int main(void) {
|
||||
|
||||
UNITY_BEGIN();
|
||||
|
||||
printf("\n------------------------numbers test------------------------\n\n");
|
||||
|
||||
RUN_TEST(test_createNumbers_length);
|
||||
RUN_TEST(test_createNumbers_single_duplicate);
|
||||
RUN_TEST(test_getDuplicate_manual_array);
|
||||
RUN_TEST(test_getDuplicate_no_duplicate);
|
||||
RUN_TEST(test_getDuplicate_invalid_input);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
#include "unity.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "stack.h"
|
||||
|
||||
// StackNode *createNode(void *data) testen
|
||||
void test_createNode(void) {
|
||||
|
||||
int testInt = 26;
|
||||
StackNode *testNode = createNode(&testInt); // Adresse des testInts
|
||||
|
||||
TEST_ASSERT_NOT_NULL(
|
||||
testNode); // Speicher konnte reserviert werden, malloc ist nicht NULL
|
||||
TEST_ASSERT_EQUAL_PTR(&testInt, testNode->data); // data pointer gesetzt
|
||||
TEST_ASSERT_NULL(testNode->next); // vorheriger und nächster Eintrag NULL
|
||||
TEST_ASSERT_NULL(testNode->prev);
|
||||
|
||||
free(testNode); // Speicher freigeben
|
||||
}
|
||||
|
||||
// StackNode *push(StackNode *stack, void *data) testen
|
||||
void test_pushDataToStack(void) {
|
||||
|
||||
int testInts[] = {27, 28};
|
||||
|
||||
StackNode *testStack = NULL; // leeren testStack initialisieren
|
||||
|
||||
testStack =
|
||||
push(testStack, &testInts[0]); // leerer Stack mit Adresse des testInts
|
||||
|
||||
TEST_ASSERT_NOT_NULL(testStack); // im Fehlerfall wird testStack unverändert
|
||||
// zurückgegeben -> bei Fehler NULL
|
||||
TEST_ASSERT_EQUAL_PTR(&testInts[0], testStack->data); // data pointer gesetzt
|
||||
TEST_ASSERT_NULL(testStack->next); // vorheriger und nächster pointer auf NULL
|
||||
// gesetzt, da es nur einen Knoten gibt
|
||||
TEST_ASSERT_NULL(testStack->prev);
|
||||
|
||||
// zweiter Push
|
||||
StackNode *oldHead = testStack; // bisherigen head speichern
|
||||
testStack = push(testStack, &testInts[1]);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(testStack);
|
||||
TEST_ASSERT_NOT_EQUAL(
|
||||
oldHead,
|
||||
testStack); // bei malloc Fehler wird der head unverändert zurückgegeben
|
||||
|
||||
TEST_ASSERT_EQUAL_PTR(&testInts[0],
|
||||
oldHead->data); // data pointer wurden richtig gesetzt
|
||||
TEST_ASSERT_EQUAL_PTR(&testInts[1], testStack->data);
|
||||
|
||||
// richtige Verkettung: NULL <- testStack -> testStack->next -> oldHead ->
|
||||
// NULL
|
||||
TEST_ASSERT_EQUAL_PTR(oldHead, testStack->next);
|
||||
TEST_ASSERT_EQUAL_PTR(testStack, oldHead->prev);
|
||||
TEST_ASSERT_NULL(testStack->prev);
|
||||
|
||||
// Speicherfreigabe
|
||||
testStack->next = NULL; // pointer ungültig machen, damit nicht ausversehen
|
||||
// später aufgerufen
|
||||
oldHead->prev = NULL;
|
||||
|
||||
free(oldHead);
|
||||
free(testStack);
|
||||
}
|
||||
|
||||
void test_deleteTopElement(void) {
|
||||
|
||||
int testInts[] = {10, 20, 30};
|
||||
StackNode *stack = NULL;
|
||||
|
||||
for (int i = 0; i < 3;
|
||||
i++) { // Stack mit drei Elementen, oberestes Element mit data 30
|
||||
stack = push(stack, &testInts[i]);
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL_PTR(&testInts[2], stack->data); // oberstes Element ist 30
|
||||
|
||||
stack = pop(stack); // oberstes Element löschen
|
||||
TEST_ASSERT_EQUAL_PTR(&testInts[1], stack->data);
|
||||
TEST_ASSERT_NULL(
|
||||
stack->prev); // pointer zum alten head wurde auf NULL gesetzt
|
||||
|
||||
stack = pop(stack);
|
||||
TEST_ASSERT_EQUAL_PTR(&testInts[0], stack->data);
|
||||
TEST_ASSERT_NULL(stack->prev);
|
||||
|
||||
stack = pop(stack); // bei leerem Stack wird NULL zurückgegeben
|
||||
TEST_ASSERT_NULL(stack);
|
||||
}
|
||||
|
||||
void test_returnData(void) {
|
||||
|
||||
int testInts[] = {10, 20, 30};
|
||||
StackNode *stack = NULL;
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
stack = push(stack, &testInts[i]);
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL_PTR(&testInts[2],
|
||||
top(stack)); // top gibt richtige Adresse zurück
|
||||
stack = pop(stack); // oberstes Element löschen
|
||||
TEST_ASSERT_EQUAL_PTR(&testInts[1], top(stack));
|
||||
|
||||
stack = pop(stack);
|
||||
TEST_ASSERT_EQUAL_PTR(&testInts[0], top(stack));
|
||||
|
||||
stack = pop(stack); // bei leerem Stack wird NULL zurückgegeben
|
||||
TEST_ASSERT_NULL(stack);
|
||||
}
|
||||
|
||||
void test_clearStack(void) {
|
||||
int testInts[] = {1, 2, 3, 4, 5};
|
||||
StackNode *stack = NULL;
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
stack = push(stack, &testInts[i]);
|
||||
}
|
||||
|
||||
clearStack(&stack);
|
||||
|
||||
TEST_ASSERT_NULL(stack);
|
||||
}
|
||||
|
||||
void setUp(void) {}
|
||||
void tearDown(void) {}
|
||||
|
||||
int main(void) {
|
||||
|
||||
UNITY_BEGIN();
|
||||
|
||||
printf("\n------------------------stack test------------------------\n\n");
|
||||
|
||||
RUN_TEST(test_createNode);
|
||||
RUN_TEST(test_pushDataToStack);
|
||||
RUN_TEST(test_deleteTopElement);
|
||||
RUN_TEST(test_returnData);
|
||||
RUN_TEST(test_clearStack);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Reference in new issue
Block a user