diff --git a/Makefile b/Makefile index 994f1406de3a4a14b66b5b32fe7f7af01c24cd23..742516b8ede5bb8f539ddf3876cf0afb46c194c5 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ all: loesung WARNINGS = -Wall #-Werror OPTIONAL = -Wextra -Wpedantic -Wshadow DEBUG = -g -ggdb -fno-omit-frame-pointer -OPTIMIZE = -O2 -std=c11 +OPTIMIZE = -Og -std=c11 loesung: Makefile loesung.c $(CC) -o $@ $(WARNINGS) $(OPTIONAL) $(DEBUG) $(OPTIMIZE) loesung.c diff --git a/loesung.c b/loesung.c index 22e52f53f8574889fb509c8d051eccbaf54662aa..6ec22b19cb6d89b655c9a5302c3aa7250e4313e9 100644 --- a/loesung.c +++ b/loesung.c @@ -7,13 +7,30 @@ typedef struct Tile { uint32_t x; uint32_t y; - struct Tile *p_n; // north, used for initial list + struct Tile *p_next; // used for list + struct Tile *p_n; // north struct Tile *p_e; // east struct Tile *p_s; // south struct Tile *p_w; // west } Tile; +// Reservieren und initialisieren des Speicherplatz eines Tiles +Tile* tile_new() { + Tile *p_tile = malloc(sizeof(Tile)); + if(p_tile == NULL) { // failed to allocate memory + return NULL; + } + p_tile->x = 0; + p_tile->y = 0; + p_tile->p_next = NULL; + p_tile->p_n = NULL; + p_tile->p_e = NULL; + p_tile->p_s = NULL; + p_tile->p_w = NULL; + return p_tile; +} + int tile_compare(const void *p, const void *q) { const Tile *s = *(const Tile**) p; const Tile *t = *(const Tile**) q; @@ -35,62 +52,73 @@ int tile_compare(const void *p, const void *q) { /** List von Tiles */ typedef struct List { Tile *p_top; - uint32_t size; + uint32_t num_tiles; + uint32_t num_odd; // #ungerade Felder (`x - y` ist ungerade) + Tile max; + Tile min; } List; /** Initialisieren einer leeren Liste */ -void list_init(List *s) { - s->p_top = NULL; - s->size = 0; +void list_init(List *this) { + this->p_top = NULL; + this->num_tiles = 0; + this->num_odd = 0; + this->min.x = UINT32_MAX; + this->min.y = UINT32_MAX; + this->max.x = 0; + this->max.y = 0; } -/** Hinzufuegen eines leeren Elementes zur Liste +/** Min-Max funktionen fuer uint32_t */ +inline uint32_t min(uint32_t a, uint32_t b) { return a < b ? a : b; } +inline uint32_t max(uint32_t a, uint32_t b) { return a > b ? a : b; } + +/** Hinzufuegen von einem Tile zur Liste * * Voraussetzung: Die Liste muss initialisiert sein. * Rueckgabe: Hinzugefuegtes, initialisiertes Tile */ -Tile* list_push(List *s) { - Tile *p_tile = malloc(sizeof(Tile)); - if(p_tile == NULL) { // failed to allocate memory - return NULL; - } - p_tile->x = 0; - p_tile->y = 0; - p_tile->p_n = s->p_top; - p_tile->p_e = NULL; - p_tile->p_s = NULL; - p_tile->p_w = NULL; - - s->p_top = p_tile; - s->size++; - return p_tile; -} - -/** Entfernen des obersten Elementes von der Liste und zurueckgeben des Tiles - * - * Der List muss mindestens ein Element besitzen - */ -Tile* list_pop(List *s) { - Tile *p_tile = s->p_top; - s->p_top = s->p_top->p_n; - s->size--; - return p_tile; +void list_push(List *this, Tile *p_tile) { + p_tile->p_next = this->p_top; + this->p_top = p_tile; + // Reichweite der Liste ermitteln + this->min.x = min(this->min.x, p_tile->x); + this->min.y = min(this->min.y, p_tile->y); + this->max.x = max(this->max.x, p_tile->x); + this->max.y = max(this->max.x, p_tile->y); + this->num_tiles++; + this->num_odd += (p_tile->x - p_tile->y)&1; } /** Freigeben des reservierten Speichers von der Liste * - * Alle Tiles weReaden mit free vom speicher deallocaiert + * Alle Tiles mit free vom Speicher freigeben */ -void list_free(List *s) { - Tile *p_cur = s->p_top; - s->p_top = NULL; +void list_free(List *this) { + Tile *p_cur = this->p_top; + this->p_top = NULL; while (p_cur != NULL) { - Tile *p_next = p_cur->p_n; + Tile *p_next = p_cur->p_next; free(p_cur); p_cur = p_next; } } +/** pruefen, ob genausoviele gerade und ungerade Kacheln existieren, + * ohne einen Overflow zu erzeugen + * notwendiges Kriterium fuer unzusammenhaengender graph und hinreichendes + * fuer zusammenhaengender + */ +bool list_possible(List *this) { + return this->num_tiles - this->num_odd == this->num_odd; +} + +/** Zurueckgeben, ob graph ein vollstaendiges Rechteck ist */ +bool list_rectangle(List *this) { + return (this->max.x - this->min.x) * (this->max.y - this->min.y) + == this->num_tiles; // TODO: check for overflow? +} + typedef enum ReadResult { ReadOk, ReadEof, @@ -100,16 +128,6 @@ typedef enum ReadResult { ReadErrTooManyNumbers, ReadErrOutOfMemory, } ReadResult; - -typedef struct Field { - Tile t_min; - Tile t_max; - List tile_list; - Tile* (*tile_array); - uint32_t num_tiles; -} Field; // TODO: entfernen dieses Structs und nutzen von kleineren Strukturen: - // list + tile_array - /** Parsen einer Kachel(Tile) aus einer Zeile von StdIn * * Die Zahlen weReaden in das uebergebene Struct pTile geschrieben, sind aber @@ -135,16 +153,14 @@ ReadResult read_line(Tile* p_tile){ cur_whitespace = false; cur_number++; } - // 429496730 = 2^32 / 10, daher wenn `p_cur` groesser ist, wiRead ein + // 429496730 = 2^32 / 10, daher wenn `p_cur` groesser ist, wird ein // overflow erzeugt if (*p_cur > 429496729) { - printf("Multiplizieren ueberlauf %u\n", *p_cur); return ReadErrIntegerOverflow; } (*p_cur) *= 10; int digit = c - '0'; if (*p_cur > UINT32_MAX - digit) { - printf("Addieren ueberlauf %u\n", *p_cur); return ReadErrIntegerOverflow; } (*p_cur) += digit; @@ -166,48 +182,15 @@ ReadResult read_line(Tile* p_tile){ return ReadOk; } -/** Min-Max funktionen fuer uint32_t */ -inline uint32_t min(uint32_t a, uint32_t b) { return a < b ? a : b; } -inline uint32_t max(uint32_t a, uint32_t b) { return a > b ? a : b; } - -/** Initialisieren der Variablen im Feld - * - * Voraussetzung: Speicher vom Feld muss allocated sein. - */ -void field_init(Field *f) { - f->t_min.x = UINT32_MAX; - f->t_min.y = UINT32_MAX; - f->t_max.x = 0; - f->t_max.y = 0; - // initialisieren der Liste - list_init(&f->tile_list); - f->tile_array = 0; - f->num_tiles = 0; -} - -/** Freigeben des im Heap reservierten speichers des fields f */ -void field_drop(Field *f) { - uint32_t num_tiles = f->tile_list.size; - if(f->tile_array != NULL) { - for(uint32_t i = 0; i < num_tiles; i++) { - free(f->tile_array[i]); - } - free(f->tile_array); - } else { - list_free(&f->tile_list); - } -} - -/** Liest das Input von StdIn und schreibt das Ergebnis in das uebergebene Feld +/** Liest das Input von StdIn und schreibt das Ergebnis in die uebergebene Liste * * Laufzeit: O(n) * Speicherbedarf: O(n) * * Parameter: - * - Field* f pointer auf ein uninitialisiertes Feld, nach dem ausfuehren der - * Funktion ist das Feld initialisiert, wenn es sich um eine gueltige Eingabe - * handelt. `f->tile_list->size` Ist im Fehlerfall die Zeile, in dem die Eingabe - * ungueltig ist. + * - List* f ist ein Pointer auf eine initialisierte List, wird mit Eingabe + * gefuellt, im Fehlerfall ist die groesse `num_tiles` die Zeile, in welche + * der Fehler aufgetreten ist * * Return: * - ReadResult: @@ -215,24 +198,21 @@ void field_drop(Field *f) { * ReadErr..., falls die Eingabe ungueltig ist. `f` ist dann nicht vollstaendig * initialisiert */ -ReadResult field_init_tile_list(Field *f) { +ReadResult parse_input(List *this) { while (1) { - Tile *t = list_push(&f->tile_list); // neues Element in die Liste einfuegen - if (t == NULL) { + Tile *p_tile = tile_new(); // neues Element erstellen + if (p_tile == NULL) { return ReadErrOutOfMemory; } - ReadResult result = read_line(t); + ReadResult result = read_line(p_tile); switch (result) { case ReadOk: - // Reichweite des Feldes ermitteln - f->t_min.x = min(f->t_min.x, t->x); - f->t_min.y = min(f->t_min.y, t->y); - f->t_max.x = max(f->t_max.x, t->x); - f->t_max.y = max(f->t_max.x, t->y); + // einfuegen in die Liste + list_push(this, p_tile); break; case ReadEof: - list_pop(&f->tile_list); - if (f->tile_list.size == 1) { + free(p_tile); + if (this->num_tiles == 0) { return ReadEof; } else { return ReadOk; @@ -243,6 +223,24 @@ ReadResult field_init_tile_list(Field *f) { } } +/** Array in heap for accessing tiles in O(1) + */ +typedef struct Array { + uint32_t num_tiles; + Tile* (*tiles); +} Array; + +/** Freigeben des im Heap reservierten Speichers des tile_arrays f + * inklusive der Tiles */ +void array_drop(Array *this) { + if(this->tiles != NULL) { + for(uint32_t i = 0; i < this->num_tiles; i++) { + free(this->tiles[i]); + } + free(this->tiles); + } +} + typedef enum MallocResult { MallocFailed, MallocOk, @@ -251,18 +249,17 @@ typedef enum MallocResult { * * Laufzeit: O(n) */ -MallocResult field_init_tile_array(Field *f) { - f->tile_array = malloc(f->tile_list.size * sizeof(Tile*)); - if(f->tile_array == NULL) { +MallocResult array_from_list(Array *this, List* p_list) { + this->tiles = malloc(p_list->num_tiles * sizeof(Tile*)); + if(this->tiles == NULL) { return MallocFailed; } - Tile *cur = f->tile_list.p_top; - f->num_tiles = f->tile_list.size; - - for (uint32_t i = 0; i < f->tile_list.size; i++) { - f->tile_array[i] = cur; - cur = cur->p_n; - f->tile_array[i]->p_n = NULL; + this->num_tiles = p_list->num_tiles; + Tile *cur = p_list->p_top; + for (uint32_t i = 0; i < this->num_tiles; i++) { + this->tiles[i] = cur; + cur = cur->p_next; + this->tiles[i]->p_next = NULL; } return MallocOk; } @@ -273,12 +270,12 @@ MallocResult field_init_tile_array(Field *f) { * Rueckgabe: 0 falls alles ok * 1 falls Dopplungen vorkommen TODO: enum */ -int field_find_neighbours(Field *f) { +int array_find_neighbours(Array *this) { uint32_t i = 0; // vergleich mit oberer Zeile uint32_t begin_line = 0; - for (uint32_t j = 1; j < f->num_tiles; j++) { - Tile *p_cur = f->tile_array[j]; - Tile *p_east = f->tile_array[j-1]; + for (uint32_t j = 1; j < this->num_tiles; j++) { + Tile *p_cur = this->tiles[j]; + Tile *p_east = this->tiles[j-1]; // ermitteln der ost/west-Beziehung if (p_cur->y == p_east->y) { if (p_cur->x == p_east->x) { @@ -292,14 +289,14 @@ int field_find_neighbours(Field *f) { begin_line = i; } // ermitteln der nord/sued-Beziehung - Tile *p_north = f->tile_array[i]; + Tile *p_north = this->tiles[i]; if (p_north->y < p_cur->y - 1) { i = begin_line; // Luecke zwischen zeilen } else if (p_north->y == p_cur->y - 1) { // finden, ob paar existiert, zurzeit O(n). // TODO: lohnt sich binaere suche mit O(log n)? - for(;f->tile_array[i]->y < p_cur->y && i < begin_line; i++); - p_north = f->tile_array[i]; + for(;this->tiles[i]->y < p_cur->y && i < begin_line; i++); + p_north = this->tiles[i]; if (p_north->x == p_cur->x) { p_north->p_s = p_cur; p_cur->p_n = p_north; @@ -309,19 +306,19 @@ int field_find_neighbours(Field *f) { return 0; } -void field_print(Field *f) { +void array_print(Array *this) { printf("# Array\n"); - for (uint32_t i = 0; i < f->tile_list.size; i++) { - printf("%d %d\n", f->tile_array[i]->x, f->tile_array[i]->y); + for (uint32_t i = 0; i < this->num_tiles; i++) { + printf("%u %u\n", this->tiles[i]->x, this->tiles[i]->y); } } int main (void) { - // erstellen des main Objekt - Field field; - field_init(&field); // initialiseren der Variablen - ReadResult result = field_init_tile_list(&field); // lesen des Inputs + // erstellen des Liste zum parsen des inputs + List tile_list; + list_init(&tile_list); // Liste ist owner von Tiles + ReadResult result = parse_input(&tile_list); // lesen des Inputs switch (result) { case ReadOk: break; @@ -329,44 +326,54 @@ int main (void) // bei leerer Eingabe nichts ausgeben return EXIT_SUCCESS; case ReadErrIntegerOverflow: - fprintf(stderr, "Error in line %d: too big integer\n", field.tile_list.size); - field_drop(&field); + fprintf(stderr, "Error in line %d: too big integer\n", tile_list.num_tiles); + list_free(&tile_list); return EXIT_FAILURE; case ReadErrNonDigitCharacter: - fprintf(stderr, "Error in line %d: invalid character\n", field.tile_list.size); - field_drop(&field); + fprintf(stderr, "Error in line %d: invalid character\n", tile_list.num_tiles); + list_free(&tile_list); return EXIT_FAILURE; case ReadErrTooFewNumbers: fprintf(stderr, "Error in line %d: Too few numbers, expected exactly 2\n", - field.tile_list.size); - field_drop(&field); + tile_list.num_tiles); + list_free(&tile_list); return EXIT_FAILURE; case ReadErrTooManyNumbers: fprintf(stderr, "Error in line %d: Too much numbers, expected exactly 2\n", - field.tile_list.size); - field_drop(&field); + tile_list.num_tiles); + list_free(&tile_list); return EXIT_FAILURE; case ReadErrOutOfMemory: fprintf(stderr, "Error in line %d: Failed to allocate more memory", - field.tile_list.size); + tile_list.num_tiles); + list_free(&tile_list); return EXIT_FAILURE; } - // turn the list into an array - if(field_init_tile_array(&field) == MallocFailed) { + Array tile_array; + // uebertragen der Liste in ein Array, und uebergeben des ownership der Tiles + // an das Array + if(array_from_list(&tile_array, &tile_list) == MallocFailed) { fprintf(stderr, "Error: Failed to allocate enough memory"); + list_free(&tile_list); // fehlgeschlagen, noch ist liste owner. + return EXIT_FAILURE; } - qsort(field.tile_array, field.tile_list.size, sizeof(Tile*), tile_compare); - if(field_find_neighbours(&field)) { + // TODO: pruefen, ob notwendiges Kriterium erfuellt, + // TODO: Falls Rechteck, dieses Ausgeben. + + //array_print(&tile_array); + qsort(tile_array.tiles, tile_array.num_tiles, sizeof(Tile*), tile_compare); + + if(array_find_neighbours(&tile_array)) { fprintf(stderr, "Error: Duplicated entry\n"); + array_drop(&tile_array); return EXIT_FAILURE; } - //field_print(&field); // TODO: Untertilesets finden - // TODO: Problem fuer die Untertilesets loesen - // TODO: Loesung ausgeben + // TODO: Pro Untertileset schauen ob notwendiges kriterium + // TODO: Loesung ausgeben (schwerer Teil) - field_drop(&field); + array_drop(&tile_array); return EXIT_SUCCESS; }