This commit is contained in:
stsh 2022-02-17 11:45:11 +01:00
parent 674695e82a
commit 62f8e2ce05
212 changed files with 11436 additions and 0 deletions

View File

@ -0,0 +1,84 @@
# 01 - Erste Schritte mit C
___
## 1. Übersicht
In diesem Praktikum erstellen Sie mehrere kleine C-Programme, in denen Sie Input- und Output-Funktionen der C Standard Library verwenden.
Arbeiten Sie in Zweiergruppen und diskutieren Sie ihre Lösungsansätze miteinander, bevor Sie diese umsetzen.
Bevor Sie mit den Programmieraufgaben beginnen, setzen Sie eine virtuelle Maschine mit der vorbereiteten Praktikumsumgebung auf.
___
## 2. Lernziele
In diesem Praktikum schreiben Sie selbst von Grund auf einige einfache C-Programme und wenden verschiedene Kontrollstrukturen an.
- Sie können mit *#include* Funktionen der C Standard Library einbinden
- Sie können mit *#define* Macros definieren und diese anwenden
- Sie wenden die *Input-* und *Output-Funktionen* von C an, um Tastatur-Input einzulesen und formatierte Ausgaben zu machen.
- Sie verwenden die Kommandozeile, um ihren Sourcecode in ein ausführbares Programm umzuwandeln.
- Sie wenden for-und while-Loops sowie if-then-else-Verzweigungen an.
- Sie setzen eine Programmieraufgabe selbständig in ein funktionierendes Programm um.
___
## 3. Aufgabe 1: virtuelle Maschine
Im Moodle-Kurs "Systemnahe Programmierung" finden Sie unter "Praktika" eine Installationsanleitung für die virtuelle Maschine, die wir Ihnen zur Verfügung stellen. Die virtuelle Maschine enthält ein Ubuntu Linux-Betriebssystem und die für das Praktikum benötigten Frameworks.
Folgen sie der Anleitung, um die virtuelle Maschine auf ihrem Rechner zu installieren.
___
## 4. Aufgabe 2: Hello World
Schreiben Sie ein C-Programm, das "Hello World" auf die Standardausgabe schreibt. Verwenden Sie die printf-Funktion aus der Standard Library. In den Vorlesungsfolien finden Sie bei Bedarf eine Vorlage.
Erstellen sie das Source-File mit einem beliebigen Editor, sie benötigen nicht unbedingt eine IDE. Speichern Sie das Source-File mit der Endung `.c`.
Um ihr Source-File zu kompilieren, verwenden Sie den GNU Compiler auf der Kommandozeile:
``` sh
$> gcc hello.c
```
Der Compiler übersetzt ihr Programm in eine ausführbare Datei `a.out`, die Sie mit
``` sh
$> ./a.out
```
ausführen können. Sie können den Namen der ausführbaren Datei wählen, indem Sie die Option `-o` verwenden:
``` sh
$> gcc hello.c -o hello
```
erzeugt die ausführbare Datei `hello`.
Verwenden Sie die Option `-Wall`, um alle Warnungen des Compilers auszugeben. Dies weist Sie auf allfällige Programmierfehler hin.
___
## 5. Aufgabe 3: Tabellenausgabe
Schreiben Sie ein Programm in C, das von `stdin` einen Umrechnungsfaktor zwischen CHF und Bitcoin einliest und danach eine Tabelle von Franken- und Bitcoin-Beträgen ausgibt. Die Tabelle soll sauber formatiert sein, z.B. so:
```
Enter conversion rate (1.00 BTC -> CHF): 43158.47
200 CHF <--> 0.00463 BTC
400 CHF <--> 0.00927 BTC
600 CHF <--> 0.01390 BTC
800 CHF <--> 0.01854 BTC
1000 CHF <--> 0.02317 BTC
1200 CHF <--> 0.02780 BTC
1400 CHF <--> 0.03244 BTC
1600 CHF <--> 0.03707 BTC
```
- Verwenden Sie eine Schleife und die `printf`-Funktion für die Tabellenausgabe
- Definieren Sie ein Makro `NUM_ROWS`, um an zentraler Stelle im Source-Code zu definieren, wie viele Einträge die Tabelle in der Ausgabe haben soll.
- Lesen Sie den Umrechnungsfaktor mit der `scanf`-Funktion als `double` von der Kommandozeile ein.
___
## 6. Aufgabe 4: Zeichen und Wörter zählen
Schreiben Sie ein C-Programm, welches die Zeichen und Wörter einer mit der Tastatur eingegebenen Zeile zählt. Wortzwischenräume sind entweder Leerzeichen (' ') oder Tabulatoren ('\t'). Die Eingabe der Zeile mit einem newline-character ('\n') abgeschlossen. Danach soll ihr Programm die Anzahl Zeichen und die Anzahl Wörter ausgeben und terminieren.
- Verwenden Sie die `char getchar(void)` Funktion aus der `stdio.h` Library, um die Zeichen einzeln einzulesen. Die Funktion `getchar` kehrt nicht gleich bei Eingabe des ersten Zeichens zurück, sondern puffert die Daten, bis die Eingabe einer kompletten Zeile mit Return abgeschlossen wird. Dann wird das erste Zeichen aus dem Puffer zurückgegeben und mit weiteren Aufrufen von getchar können die nachfolgenden Zeichen aus dem Puffer gelesen werden. Gibt `getchar` das Zeichen `\n` zurück, ist die Zeile komplett zurückgegeben und der Puffer ist wieder leer.
- Setzen Sie eine Schleife ein, die beim Zeichen '\n' terminiert.
- Benutzen Sie if-then-else-Strukturen um die Wörter zu zählen.
___
## 7. Bewertung
Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.

Binary file not shown.

View File

@ -0,0 +1,228 @@
# 02: Funktionen, Datentyp "enum"
___
![](./random_number.png)
(Copyright Bild: xkcd.com)
___
## 1. Übersicht
In diesem Praktikum sind zwei Themen im Fokus: Funktionen und der Datentyp enum.
Funktionen sind der wesentlichste Bestandteil der C Programmierung welcher eine strukturierte Programmierung ermöglicht:
* Eine Funktion ein Teil eines C Codes, der eine spezielle Aufgabe ausführt. Sie kann aus dem Hauptprogramm, oder aus anderen Funktionen, aufgerufen werden.
* Jede Funktion besitzt einen eindeutigen Namen, eine eindeutige Signatur (Typen und Reihenfolge der Parameter) und einen Rückgabewert (int falls nichts angegeben wird).
* Eine Funktion kann Werte aus dem aufrufendem Kontext übernehmen und bei Bedarf einen Wert an den aufrufenden Kontext zurückliefern.
Beispiel einer Additions-Funktion:
```
#include <stdio.h>
/* Funktionsdeklaration */
int add(int a, int b);
int main(void) {
int aa = 1, bb = 2, cc;
printf("%aa + %bb = %cc", aa, bb, add(aa, bb););
return 0;
}
/* Funktionsdefinition */
int add(int a, int b) {
return a + b;
}
```
Der Daten typt enum wird verwendet um die Lesbarkeit von Programmen zu erhöhen:
Beispiel eines enum:
```
enum Ampeln = {rot =1, gelb, gruen};
int main(void) {
Ampeln ampel1;
if (ampel1 == rot) {...}
return 0;
}
```
___
## 2. Lernziele
In diesem Praktikum lernen Sie Funktionen zu definieren und aufzurufen, sowie enum anzuwenden.
* Sie können ein Programm schreiben welches aus mehreren Funktionen besteht.
* Sie können Funktionen deklarieren, definieren und aufrufen.
* Sie können enum Typen definieren und deren Werte bestimmen und abfragen.
___
## 3. Aufgaben
```{eval-rst}
.. figure:: kalender-108_v-ARDFotogalerie.jpg
:width: 600px
:name: kalender-108_v-ARDFotogalerie
:align: center
```
(Copyright Bild: www.planet-wissen.de)
### 3.1 Aufgabe 1 Tage pro Monat
In der ersten Aufgabe berechnen Sie die Tag pro Monat einer beliebigen Kombination Monat / Jahr.
Erweitern Sie dazu das Programm um folgende Aspekte:
* Bereichsprüfung von Jahr und Monat
* Funktion istSchaltjahr, welche berechnet, ob das Jahr eine Schaljahr ist
* Funktion tageProMonat, welche die Anzahl Tage des gegebenen Monats und Jahres berechnet.
Vorgaben:
* Die Funktion istSchaltjahr nimmt ein Integer (jahr) entgegen und gibt 1 im Falle eiens Schltjahres und 0 im andreren Fall zurück
* Die Funktion tageProMonat nimmt zwei integer (monat und jahr) entgegeben und gibt die Anzahl Tage als Integer zurück
* Die Jahreszahl, welche den Funktionen übergeben wird, muss überprüft werden und grösser gleich 1599 und kleiner als 10000 sein
* Der übergebene Monat muss grösser als 0 und kleine als 13 sein.
Die Regeln für die Schaltjahrberechnung:
* Schaltjahre sind alle Jahre, die durch 4 teilbar sind.
* Eine Ausnahme bilden die Jahrhunderte (1600, 1700…). Diese sind keine Schltjahre.
* zu den 100er gibt es ebenfalls Ausnahmen: Diese sind immer Schaltjahre, wenn sie durch 400 teilbar sind
... also zum Beispiel 1600 ist eines, nicht jedoch 1700. Weiterführende Details finden Sie unter https://de.wikipedia.org/wiki/Gregorianischer_Kalender
Gegeben ist die main Funktion des Programms. Ergänzen Sie die enum Definition und die fehlenden Funktionen:
* gibIntWert: Die Funktion soll einen Int Wert zurückgeben. Der Bereich, wie auch Fehleingaben sollen sollen berücksichtigt werden. (atoi unfd fgets sind hier hilfreich)
* istSchaltjahr: Die Funktion gibt 1 im Falle eines Schltjahr und o im anderen Falle zurück.
* tageProMonat: Die Funktion gibt den die Tage des Monats für das definierte Jahr zurück. Verwenden Sie die Switchanweisung , sowie den enum Datentypen
```
int main (int argc, char *argv[]) {
int monat, jahr;
// Monat einlesen und Bereich ueberpruefen
monat = gibIntWert("Monat", 1, 12);
jahr = gibIntWert("Jahr", 1600, 9999);
// Ausgabe zum Test
printf("Monat: %d, Jahr: %d \n", monat, jahr);
// Ausgabe zum Test (hier mit dem ternaeren Operator "?:")
printf("%d ist %s Schaltjahr\n", jahr, istSchaltjahr(jahr) ? "ein" : "kein");
// Ausgabe
printf("Der Monat %02d-%d hat %d Tage.\n", monat, jahr, tageProMonat(jahr, monat));
return 0;
}
```
Tipp: Angenommen Sie verwenden den enum month_t { JAN=1, FEB, MAR, APR, MAI, JUN, JUL, AUG, SEP, OKT, NOV, DEZ };
Dann können Sie im Programm direkt die Konstanten verwenden:
```
if (m == 2) ... // schlecht lesbar
if (monat == 2) ... // besserer Variablenname
if (monat == FEB) ... // am besten lesbar
```
Als Abnahme müssen die Tests unverändert ohne Fehler ausgeführt werden (`make test`)
___
### 3.2 Aufgabe 2 Bestimmen des Wochentags
Erweitern Sie das vorgegebene zweite Programm Gerüst an den bezeichneten Stellen so, dass das Programm von der Kommando Zeile ein Argument entgegennimmt, es auf Gültigkeit überprüft und schliesslich den Wochentag für das gegebene Datum berechnet und ausgibt.
Prüfen Sie die Umsetzung beider Teilaufgaben mittels make test.
#### 3.2.1 Teilaufgabe Argumente Parsen und auf Korrektheit prüfen
Das Argument stellt ein gültiges Datum unseres Gregorianischen Kalenders dar (d.h. ein Datum ab Donnerstag, den 15. Oktober 1582, mit der Gregorianischen Schaltjahr Regel).
Wenn kein Argument gegeben ist oder wenn das eingegebene Datum nicht gültig ist, soll das Programm einem Hilfetext auf stderr ausgeben und mit EXIT_FAILURE Exit Code terminieren. Wenn ein gültiges Datum erkannt wurde terminiert das Programm mit Exit Code EXIT_SUCCESS.
##### 3.2.1.1 Argument Format
Das Format des Kommando Zeilen Arguments soll yyyy-mm-dd sein, wobei yyyy für das vier-stellige Jahr, mm für einen 1-2-stelligen Monat (1…12) und dd für einen Tag des Monats, begin-nend mit 01. Z.B. 2020-02-29.
##### 3.2.1.2 Korrektes Datum
Das Datum muss alle folgenden Bedingungen erfüllen damit es als korrekt erkannt wird:
* Obergrenze für ein «sinnvolles» Datum ist das Jahr 9999
* es muss Gregorianisch sein, d.h. ab 15. Oktober 1582 (inklusive)
* es darf nur Monate von 1 für Januar bis 12 für Dezember beinhalten
* der Tag muss grösser oder gleich 1 sein
* der Tag darf nicht grösser als 31 sein für Monate mit einer Länge von 31 Tagen
* der Tag darf nicht grösser als 30 sein für Monate mit einer Länge von 30 Tagen
* der Tag darf für den Februar nicht grösser sein als 29 für ein Schaltjahr
* der Tag darf für den Februar nicht grösser sein als 28 für ein Nicht-Schaltjahr
##### 3.2.1.3 Vorgaben an die Umsetzung
1. Definieren Sie einen enum Typen mit (typedef) Namen month_t dessen Werte die Englischen 3-Zeichen Abkürzungen der Monate sind, nämlich Jan, Feb, … Dec und stellen Sie sicher dass die Abkürzungen für die uns geläufigen Monatsnummer stehen.
2. Definierend Sie einen struct Typen mit (typedef) Namen date_t und den int Elementen year, month, day. Lesen Sie das Argument (falls vorhanden) via sscanf und dem Formatstring "%d-%d-%d" in die drei Elemente einer Date Variable. Siehe dazu die Hinweise im Anhang.
3. Für die Berechnung der Monatslänge implementieren Sie die Hilfsfunktion is_leap_year(date_t date) (nach obigen Vorgaben). Der Return Wert 0 bedeutet «Kein Schaltjahr», 1 bedeutet «Schaltjahr».
4. Implementieren Sie die Funktion `int get_month_length(date_t date)`. Diese soll für den Monat des Datums die Monatslänge (was dem letzten Tag des Monats ent-spricht) ausgeben geben Sie 0 für ungültige Monatswerte zurück.
5. Schliesslich implementieren Sie die Funktion int is_gregorian_date(date_t date) welche prüft, ob ein gegebenes Datum im Bereich 15. Oktober 1582 und dem Jahr 9999 ist (0 = nein, 1 = ja).
6. Implementieren Sie eine Funktion int is_valid_date(date_t date), welche obige Bedingungen für ein gültiges Datum umsetzt. Der Return Wert 0 bedeutet «Kein gültiges Datum», 1 bedeutet «Gültiges Datum». Benutzen Sie für die Prüfung des Datums die `month_t` Werte wo immer möglich und sinnvoll. Verwenden Sie die oben implemen-tierten Hilfsfunktionen.
##### 3.2.1.4 Hinweise
Beachten Sie die Kommentare im Code für die geforderten Implementierungs-Details.
#### 3.2.2 Teilaufgabe Wochentag Berechnung
Schreiben Sie eine Funktion welche zu einem Datum den Wochentag berechnet.
Die Formel wird Georg Glaeser zugeschrieben, möglicherweise angelehnt an eine Formel von Carl Friedrich Gauss.
```{eval-rst}
.. figure:: Wochentagsberechnung.jpg
:width: 600px
:name: Wochentagsberechnung
:align: center
```
(Quelle: https://de.wikipedia.org/wiki/Wochentagsberechnung)
Hier ist eine für C abgewandelte Variante davon.
```
weekday = ((day + (13 * m - 1) / 5 + y + y / 4 + c / 4 - 2 * c) % 7 + 7) % 7
alle Zahlen sind int Werte und alles basiert auf int-Arithmetik
m = 1 + (month + 9) % 12
a = year - 1 (für month < Mar), ansonsten year
y = a % 100
c = a / 100
```
Erweitern sie das Programm so, dass vor dem erfolgreichen Terminieren des Programms fol-gende Zeile (inklusive Zeilenumbruch) ausgegeben wird: yyyy-mm-dd is a Ddd, wobei yyyy für das Jahr, mm für die Nummer des Monats (01…12) und dd für den Tag im Monat (01…). Z.B. 2020-02-29 is a Sat.
Vorgaben an die Umsetzung
1. Definieren Sie einen enum Typen mit (typedef) Namen weekday_t dessen Werte die Englischen 3-Zeichen Abkürzungen der Tage sind, nämlich Sun, Mon, … Sat und stel-len Sie sicher dass die Abkürzungen für die Werte 0…6 stehen.
2. Schreiben Sie eine Funktion weekday_t calculate_weekday(date_t date) nach der Beschreibung der obigen Formel. Das date Argument ist als gültig angenom-men, d.h. es ist ein Programmier-Fehler, wenn das Programm diese Funktion mit einem ungültigen Datum aufruft. Machen Sie dafür als erste Codezeile in der Funktion eine Zu-sicherung (assert(is_valid_date(date));)
3. Schreiben Sie eine Funktion void print_weekday(weekday_t day), welche für jeden gülteigen Tag eine Zeile auf stdout schreibt mit den Englischen 3-Zeichen Ab-kürzungen für den Wochentag, z.B. Sonntag: Sun, Montag: Mon, etc. Wenn ein ungülti-ger Wert für day erkannt wird, soll assert(!"day is out-of-range"); aufgeru-fen werden.
Hinweise
• Für interessierte, siehe: https://de.wikipedia.org/wiki/Wochentagsberechnung
___
## 4. Bewertung
Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden können.
| Aufgabe | Kriterium | Gewicht |
| :-- | :-- | :-- |
| alle | Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. | |
| gibIntWert | Eingabe, Bereichsüberprüfung korrekt | 1 |
| istSchaltjahr | Funktion korrekt | 1 |
| TageProMonat | Funktion korrekt | 1 |
| Aufgabe 2 | Fehlenden Teile ergänzt und lauffähig | 1 |
___
## 5. Anhang
### 5.1 Sprach Element
```int main(int argc, char *argv[]) {
...
} argc: Anzahl Einträge in argv.
argv: Array von Command Line Argumenten.
argv[0]: wie das Programm gestartet wurde
argv[1]: erstes Argument
argv[argc-1]: letztes Argument
int a = 0;
int b = 0;
int c = 0;
int res = sscanf(argv[1]
, "%d-%d-%d"
, &a, &b, &c
);
if (res != 3) {
// Fehler Behandlung...
// ...
}
```
### 5.2 Beschreibung
Siehe man 3 sscanf.
Die Funktion sscanf gibt die Anzahl erfolgreich erkannte Argumente zurück. Unbedingt prüfen und angemessen darauf reagieren.
Die gelesenen Werte werden in a, b und c, gespeichert, dazu müssen Sie die Adresse der Variablen übergeben. Mehr Details dazu werden später erklärt.
fprintf(stderr, "Usage: %s…\n", argv[0]); Siehe man 3 fprintf.
Schreibt formatierten Text auf den stderr Stream.
___
Version: 15.02.2022

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -0,0 +1,11 @@
import os
import grip
import pdfkit
os.chdir(r"C:\Users\bazz\Documents\GitHub\snp\praktika\P03_Bounding_Box")
f_md = r".\README.md"
f_html = r".\REAMDE.html"
f_pdf = r".\README.pdf"
grip.render_content(f_md)
pdfkit.from_file(f_html, f_pdf)

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -0,0 +1,64 @@
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
/** Aufgabe 1:
enum Typ "textFlags" deklarieren mit den einzelnene Werten
- ITALICS = 00000001 // = 1
- BOLD = 00000010 // = 2
- UNDERLINE = 00000100 // = 4
*/
enum textFlags { ITALICS = 1, BOLD = 2, UNDERLINE = 4 };
/** Aufgabe 2
Funktion Flag Einlesen
*/
void menu (void) {
(void) printf("\nBitte geben Sie die gewuenschten Flags ein\n");
(void) printf("(Ugueltige Eingaben fuehren zum Abbruch.)\n");
(void) printf("Kursiv: tippen Sie die 1\n");
(void) printf("Fett: tippen Sie die 2\n");
(void) printf("Unterstrichen: tippen Sie die 3\n");
(void) printf("Abbrechen: tippen Sie die 0\n");
(void) printf("Ihre Wahl: ");
}
int flagEinlesen() {
fflush(stdin);
int flag = fgetc(stdin) - '0';
if (flag < 1 || flag > 3) { flag = 0; }
return flag;
}
void displayFlags(int flags) {
printf("Gesetzte Flags: %d\n", flags);
}
/** Vorgegebener Programm-Rumpf */
int main(void) {
int oneFlag, flags = 0;
do {
menu();
oneFlag = flagEinlesen();
switch (oneFlag) {
case 1:
printf("Wahl: 1, Flag: %d", oneFlag);
flags |= ITALICS;
break;
case 2:
printf("Wahl: 2, Flag: %d", oneFlag);
flags |= BOLD;
break;
case 3:
printf("Wahl: 3, Flag: %d", oneFlag);
flags |= UNDERLINE;
break;
}
(void) displayFlags(flags);
} while (oneFlag != 0);
}

Binary file not shown.

View File

@ -0,0 +1,117 @@
/**
* P02 Praktikum
*
* - Funktionen
* - Funktion Schaltjahr
* - Funktion Tage pro Monat
* - Funktion Wochentag
*
* @author ZHAW
* @version 1.0 - 08.02.2022
*/
#include <stdio.h>
#include <stdlib.h>
int leseZahl(char *prompt, int min, int max) { // Datentyp angeben, Parameter angeben
char zahlString[11];
int zahl;
do {
printf("%s", prompt);
fgets(zahlString, 10, stdin);
zahl = atoi(zahlString); // bei ungueltiger Zahl return 0
} while ((zahl < min) || (zahl > max));
return zahl; // return Zeile angeben
}
/**
____ leseZahl(____) { // Datentyp angeben, Parameter angeben
char zahlString[11];
int zahl;
fgets(zahlString, 10, stdin);
zahl = atoi(zahlString); // bei ungueltiger Zahl return 0
___________________ // return Zeile angeben
}
*/
/* Konstante Werte fuer die Monate */
enum { JAN=1, FEB, MAR, APR, MAI, JUN, JUL, AUG, SEP, OKT, NOV, DEZ };
int main (void) {
//
// Variablen definieren
//
int monat, jahr;
char *prompt;
int istSchaltjahr = 0; // 0: kein Schaltjahr, sonst Schaltjahr
int tageProMonat = 0; // Ergebnis spaeter in dieser Variablen
//
// Monat und Jahr einlesen
//
//printf("Monat: ");
//scanf("%d", &monat);
//printf("Jahr: ");
//scanf("%d", &jahr);
prompt = "Monat: ";
monat = leseZahl(prompt, 1, 12);
prompt = "Jahr: ";
jahr = leseZahl(prompt, 1, 3000);
// Ausgabe zum Test
// printf("Monat: %d, Jahr: %d \n", monat, jahr);
//
// Monate mit 30 und 31 Tagen behandeln
//
switch (monat) {
case JAN: case MAR: case MAI: case JUL: case AUG: case OKT: case DEZ:
tageProMonat = 31;
break;
case APR: case JUN: case SEP: case NOV:
tageProMonat = 30;
break;
//
// Monat ist 2: Auf Schaltjahr ueberpruefen
//
case FEB:
if (jahr % 4 == 0 && (jahr % 100 != 0 || jahr % 400 == 0)) {
tageProMonat = 29;
}
else {
tageProMonat = 28;
}
break;
//
// Anderer Monat: Fehler
//
default:
printf("Der Monat muss zwischen 1 und 12 sein.\n");
return 1;
}
//
// Ergebnis ausgeben
//
printf("Der Monat %d.%d hat %d Tage.\n", monat, jahr, tageProMonat);
return 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -0,0 +1,193 @@
# 03 - Bit Operationen, Struct, Typedef
## 1. Bit Operationen
![](./135oALYhkYyXB2aG0F-qrwA.jpeg)
Bit Operationen sind allgegenwärtig in den Computer-Wissenschaften und finden in vielen Disziplinen Anwendung. Folgend ein kleiner Auszug aus den wichtigsten Themen:
- **Bit Felder**: Sind die effizienteste Art, etwas darzustellen, dessen Zustand durch mehrere "wahr" oder "falsch" definiert werden kann. Besonders auf Systemen mit begrenzten Ressourcen sollte jede überflüssige Speicher-Allozierung vermieden werden.
Beispiel:
```c
// primary colors
#define BLUE 0b100
#define GREEN 0b010
#define RED 0b001
// mixed colors
#define BLACK 0 /* 000 */
#define YELLOW (RED | GREEN) /* 011 */
#define MAGENTA (RED | BLUE) /* 101 */
#define CYAN (GREEN | BLUE) /* 110 */
#define WHITE (RED | GREEN | BLUE) /* 111 */
```
[https://de.wikipedia.org/wiki/Bitfeld](https://de.wikipedia.org/wiki/Bitfeld)
- **Kommunikation**:
- **Prüfsummen/Paritätsbit**: Übertragungsfehler und Integrität können bis zu einem definiertem Grad erkannt werden. Je nach Komplexität der Berechnung können mehrere Fehler erkannt oder auch korrigiert werden.
[https://de.wikipedia.org/wiki/Parit%C3%A4tsbit](https://de.wikipedia.org/wiki/Parit%C3%A4tsbit), [https://de.wikipedia.org/wiki/Pr%C3%BCfsumme](https://de.wikipedia.org/wiki/Pr%C3%BCfsumme)
- **Stoppbit**: Markieren bei asynchronen seriellen Datenübertragungen das Ende bzw. Start eines definierten Blocks.
[https://de.wikipedia.org/wiki/Stoppbit](https://de.wikipedia.org/wiki/Stoppbit)
- **Datenflusssteuerung**: Unterschiedliche Verfahren, mit denen die Datenübertragung von Endgeräten an einem Datennetz, die nicht synchron arbeiten, so gesteuert wird, dass eine möglichst kontinuierliche Datenübermittlung ohne Verluste erfolgen kann.
[https://de.wikipedia.org/wiki/Datenflusssteuerung](https://de.wikipedia.org/wiki/Datenflusssteuerung)
- ...
- **Datenkompression**: Bei der Datenkompression wird versucht, redundante Informationen zu entfernen. Dazu werden die Daten in eine Darstellung überführt, mit der sich alle oder zumindest die meisten Information in kürzerer Form darstellen lassen.
[https://de.wikipedia.org/wiki/Datenkompression](https://de.wikipedia.org/wiki/Datenkompression)
- **Kryptographie**: Konzeption, Definition und Konstruktion von Informationssystemen, die widerstandsfähig gegen Manipulation und unbefugtes Lesen sind. [https://de.wikipedia.org/wiki/Verschl%C3%BCsselung](https://de.wikipedia.org/wiki/Verschl%C3%BCsselung)
- **Grafik-Programmierung**: XOR (oder ^) ist hier besonders interessant, weil eine zweite Eingabe derselben Eingabe die erste rückgängig macht (ein Beispiel dazu weiter unten: "Variablen tauschen, ohne Dritt-Variable
"). Ältere GUIs verwendeten dies für die Hervorhebung von Auswahlen und andere Überlagerungen, um kostspielige Neuzeichnungen zu vermeiden. Sie sind immer noch nützlich in langsamen Grafikprotokollen (z. B. Remote-Desktop).
### 1.1 Übungen
#### 1. Basis Operationen
Manipulationen von einzelnen Bits gehören zu den Basis Operationen und dienen als Grundlagen um weitere komplexere Konstrukte zu schaffen. Verfollständigen sie folgendes Beispiel mit den drei Basis Operationen:
```c
#include <stdlib.h>
int main() {
unsigned int number;
unsigned int bit = 3; // bit at position 3
// Setting a bit
number = ...; // solution: number |= 1 << bit;
// Clearing a bit
number = ...; // solution: number &= ~(1 << bit);
// Toggling a bit
number = ...; // solution; number ^= 1 << bit;
return EXIT_SUCCESS;
}
```
#### 2. Variablen tauschen (ohne Dritt-Variable)
Zwei Variablen zu vertauschen scheint ein einfach lösbares Problem zu sein. Eine offensichtliche Variante wäre mittels einer temporären Variablen:
```c
#include <stdlib.h>
#include <stdio.h>
int main(){
int a = 3;
int b = 4;
printf("a: %d; b: %d\n", a, b);
int temp = a;
a = b;
b = temp;
printf("a: %d; b: %d\n", a, b);
return EXIT_SUCCESS;
}
```
Es gibt aber auch eine Variante, die ohne zusätzliche Variable auskommt. Dabei wird die Tatsache, dass eine zweite XOR Operation eine erste XOR Operation rückgängig macht:
*0011 XOR 0100 = 0111*
*0111 XOR 0100 = 0011*
Somit kommt man von einem XOR Resultat (*0111*) wieder auf beide Anfangs Operanden zurück indem man einfach ein zweites Mal mit einem Operanden eine XOR Verknüpfung macht. Damit kann ein Operand als Zwischenspeicher dienen und man muss nicht extra eine Zusatzvariable verwenden.
Überlegen sie sich wie sie damit zwei Variablen vertauschen können ohne Zusatzvariable:
```c
#include <stdlib.h>
#include <stdio.h>
int main(){
int a = 3;
int b = 4;
printf("a: %d; b: %d\n", a, b);
...
/* Solutions:
// a == 0011; b == 0100
a ^= b; // a == 0111; b == 0100
b ^= a; // a == 0111; b == 0011
a ^= b; // a == 0100; b == 0011
*/
printf("a: %d; b: %d\n", a, b);
return EXIT_SUCCESS;
}
```
#### 3. Lower- / Uppercase
```c
#include <stdlib.h>
#include <stdio.h>
int main(){
char word[8] = "sREedEv";
char *wordptr = &word[0];
while(wordptr < &word[7]) {
printf("UPPERCASE: %c\n", *wordptr & '_'); // converts the char into uppercase regardless of the current casing
printf("LOWERCASE: %c\n", *wordptr | ' '); // converts the char into lowercase regardless of the current casing
wordptr++;
}
return EXIT_SUCCESS;
}
```
#### 4. Prüfen auf 2-er Potenz
```c
#include <stdio.h>
#include <stdlib.h>
int main(){
int a=32;
if(a > 0 && (a & (a - 1)) == 0){
printf("%d is a power of 2", a);
}
return EXIT_SUCCESS;
}
```
___
## 2. Struct & typedef
### 2.1 Übungen
#### 1. Bit Operationen Rechner
- Bitweise Operationen mit 2 Operanden
- Rechnung wird als ein String über scanf dem Programm übergeben
- String wird in Token zerstückelt und in struct gespeichert:
```c
typedef struct {
unsigned int operand_1;
unsigned int operand_2;
char operation;
} Expression;
```
- Ausgabe in 3 verschiedenen Formaten:
```
Bin:
0000'0000'0000'0001
& 0000'0000'0000'0011
-------------------
0000'0000'0000'0001
Hex
0x01 & 0x03 = 0x01
Dec
1 & 3 = 1
```
___
## 4. Bewertung
Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden können.
| Aufgabe | Kriterium | Gewicht |
| :-- | :-- | :-- |
| alle | Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. | |
| gibIntWert | Eingabe, Bereichsüberprüfung korrekt | 1 |
| istSchaltjahr | Funktion korrekt | 1 |
| TageProMonat | Funktion korrekt | 1 |
| Aufgabe 2 | Fehlenden Teile ergänzt und lauffähig | 1 |

View File

@ -0,0 +1,532 @@
# 04 - Modularisieren von C Code
```{eval-rst}
.. figure:: zhaw_neg_P2945.jpg
:width: 100px
:name: logo
:align: right
```
___
```{eval-rst}
.. figure:: modularisieren_von_c_code.JPG
:width: 500px
:name: logo
:align: center
```
___
## Inhalt
{ref}`04_introduction`
{ref}`04_learning_objectives`
{ref}`04_task_01`
{ref}`04_task_02`
{ref}`04_grading`
{ref}`04_appendix`
___
(04_introduction)=
## 1. Übersicht
In diesem Praktikum üben Sie modulare Programmierung indem Sie ein
Java Programm (bestehend aus drei Java Files) in ein entsprechendes C
Programm aus drei Modulen (aus je einem Header- und Implementations-
File) übersetzen. Sie passen das Makefile so an, dass die
entsprechenden Module mit kompiliert werden.
In der zweiten Aufgabe erstellen Sie Makefile Regeln für die drei
Schritte von den C Source Files zur graphischen Darstellung der
Abhängigkeiten.
```{eval-rst}
.. figure:: uebersicht.png
:width: 500px
:name: uebersicht
:align: center
```
Im Anhang ist eine Übersicht über die verwendeten File Formate gegeben.
(04_learning_objectives)=
## 2. Lernziele
In diesem Praktikum lernen Sie die Handgriffe um ein Programm zu modularisieren, d.h. in mehrere Module aufzuteilen.
* Sie wissen, dass ein Modul aus einem C-File und einem passenden
H-File besteht.
* Sie können Header Files korrekt strukturieren.
* Sie deklarieren im Header-File die öffentlichen Typen und Funktionen
eines Moduls.
* Sie wissen wie **Include Guards** anzuwenden sind.
* Sie können Module im `Makefile` zur Kompilation hinzufügen.
* Sie können `Makefile` Regeln schreiben.
Die Bewertung dieses Praktikums ist am Ende angegeben.
Erweitern Sie die vorgegebenen Code Gerüste, welche im `git`
Repository `snp-lab-code` verfügbar sind.
(04_task_01)=
## 3. Aufgabe 1: Modularisieren
Das zu ergänzende Programm dep2dot hat folgende Funktionalität:
Ergänzen Sie in **`modularize/src`** den Code in **`triangle.c`**,
**`read.h`**, **`read.c`**, **`rectang.h`** und **`rectang.c`** so
dass die Tests erfolgreich durchlaufen. Die C Implementation soll
dieselbe Funktionalität haben wie die gegebenen Java Files. Lehnen Sie
sich so nahe wie möglich an die Java Files an.
1. In den Header-Files implementieren Sie den Include-Guard und
deklarieren Sie die öffentlichen Funktionen und gegebenenfalls
**`#define`**.
2. In den Implementations-Files implementieren Sie die Funktionen.
Die drei Java Files liegen in **`modularize/java`**.
### Tipps
* Implementieren Sie die Symbole welche vollständig in Grossbuchstaben
geschrieben sind als **`#define`**.
* **`EOF`** kommt schon aus **`stdio.h`** und sollte deshalb nicht
mehr definiert werden.
* Jene **`#define`** welche von andern Modulen verwendet werden
kommen ins Header-File, die andern ins Implementations-File.
* Ein Grossteil des Java Codes aus den Methoden Bodies kann
eins-zu-eins in C übernommen werden. Listen Sie auf welche
Unterschiede es gibt:
<table>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
table th:first-of-type {
width: 50%;
}
table th:nth-of-type(2) {
width: 50%;
}
</style>
<tr><th>Java</th><th>C</th></tr>
<tr><td>
```Java
byte
```
</td><td></td></tr>
<tr><td>
```Java
boolean
```
</td><td></td></tr>
<tr><td>
```Java
true
```
</td><td></td></tr>
<tr><td>
```Java
false
```
</td><td></td></tr>
<tr><td>
```Java
System.out.print(…)
```
</td><td></td></tr>
<tr><td>
```Java
System.out.println(…)
```
</td><td></td></tr>
<tr><td>
```Java
System.in.read()
```
</td><td></td></tr>
<tr><td>
```Java
byte[] buffer = new byte[BUFFERSIZE];
```
</td><td></td></tr>
<tr><td>
```Java
public class rectang {
public boolean Rectangular(…)
{ … }
}
```
</td><td></td></tr>
<tr><td>
```Java
public class read {
public int getInt(...)
throws java.io.IOException
{ ... }
}
```
</td><td></td></tr>
<tr><td>
```Java
class triangle {
public static void main(String[] args)
throws java.io.IOException
{ ... }
}
```
</td><td></td></tr>
<tr><td>
```Java
read ReadInt = new read();
...
word = ReadInt.getInt(MAX_NUMBER);
```
</td><td></td></tr>
<tr><td>
```Java
rectang Rect = new rectang();
...
if (Rect.Rectangular(a, b, c) == true) { ... }
```
</td><td></td></tr>
<tr><td>
```
System.out.println(
"-> Dreieck " + a + "-" + b + "-" + c
+ " ist rechtwinklig");
```
</td><td></td></tr>
</table>
(04_task_02)=
## 4. Aufgabe 2: Makefile Regeln
Die folgenden drei Schritte erstellen von einem C Source File eine
graphische Darstellung der Abhängigkeiten:
1. `gcc ... -H .. file.c ... 2> file.dep` (Regeln im Makefile bereits vorhanden)
2. `dep2dot file.c <file.dep >file.dot` (in dieser Aufgabe zu erstellen)
3. `dot -Tpng file.dot >file.png` (in dieser Aufgabe zu erstellen)
Sie sollen für die Compiler-ähnlichen Programme `dep2dot` und `dot`
Makefile Regeln schreiben.
```{eval-rst}
.. figure:: uebersicht.png
:width: 500px
:name: uebersicht
:align: center
```
Das Programm `dep2dot` hat folgende Funktionalität:
1. Es liest von `stdin` die vom Compiler generierten
Abhängigkeits-Daten in Form des `dep` Formates ein.
2. Das erste und einzige Command Line Argument gibt das File an für
welches die von `stdin` gelesenen Abhängigkeiten gelten.
3. Auf `stdout` werden die Abhängigkeiten von `stdin` übersetzt als
`dot`-File Format ausgegeben.
Das Programm `dot` hat folgende Funktionalität:
1. Es liest die textuelle Beschreibung eines Graphen aus der
übergebenen Datei (erstes Argument) ein.
2. Auf `stdout` wird die grafische Darstellung der Beschreibung der
Eingabe-Datei im `png`-File Format ausgegeben.
Das `dep`-Format und das `dot`-Format sind im Anhang beschrieben.
Sie können die Funktionalität des Programms `dep2dot` kennen lernen,
indem Sie folgende Zeilen auf der Bash Shell ausführen. Das
`dep.input` File ist Teil der automatisierten Test Suite im
Verzeichnis `tests`:
```bash
bin/dep2dot dir/file <tests/dep.input >dep.dot
dot -Tpng dep.dot >dep.png
firefox dep.png
```
Als Resultat sollte Firefox folgende Graphik darstellen:
```{eval-rst}
.. figure:: dep_dot.png
:width: 150px
:name: dep_dot
:align: center
```
Definieren Sie im `Makefile` Regeln, welche die einzelnen Schritte von
den Source Files zu den `png` Files ausführen.
Prüfen Sie schliesslich die Umsetzung Aufgabe mittels `make dep-clean
dep && firefox src/*.png.`
### 4.1 Neue Regeln hinzufügen
Führen Sie im `Makefile` an den angegebenen Stellen folgende
Ergänzungen durch
* definieren Sie eine Variable `DEPFILES` deren Inhalt die Liste alle
Einträge der Variable `SOURCES` ist, wobei bei allen die Endung `.c`
durch die Endung `.c.png` ersetzt ist
* fügen Sie zum `Pseudo-Target .PHONEY` das Target `dep` dazu dies
besagt, dass das später folgenden Target `dep` nicht ein File
repräsentiert (ohne dieses Setting würde make gegebenenfalls nach
einem File mit Namen `dep` suchen um zu entscheiden ob es
inkrementell gebildet werden muss)
* schreiben Sie das Target `dep` gemäss der Beschreibung im Makefile
* schreiben Sie die Suffix Regel für die Übersetzung von `.png <-
.dot` gemäss Vorgabe im `Makefile` (als Inspiration, siehe auch die
`%.c.dep: %.c` Suffix Regel weiter unten im `Makefile`) erklären
Sie was die Regel macht
* schreiben Sie die Suffix Regel für die Übersetzung von` .dot <-
.dep` gemäss Vorgabe im `Makefile` erklären Sie was die Regel
macht
Die Umsetzung der obigen Änderungen sind erfolgreich, wenn Sie
folgende Shell Command Line erfolgreich ausführen können und in
Firefox die Abhängigkeiten der C-Files von den Inclu-de Files
dargestellt wird.
`make dep-clean dep && firefox src/*.png.`
(04_grading)=
## 5. Bewertung
Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.
| Aufgabe | Kriterium | Punkte |
| :-- | :-- | :-- |
| 1 | Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. | |
| 1 | Module einbinden, Header Files schreiben | 2 |
| 2 | Sie können das funktionierende Makefile demonstrieren und erklären. | |
| 2 | Neue Regeln hinzufügen | 2 |
(04_appendix)=
## 6. Anhang
### 6.1 Verwendete zusätzliche Sprach Elemente
<table><tr><td>
**Sprach Element**
</td><td>
**Beschreibung**
</td></tr>
<tr><td>
```C
fprintf(stderr, "v=%d", v)
```
</td><td>
Formatierte Ausgabe auf den Standard Error Stream. Siehe ***man 3
stderr*** und ***man 3 fprintf***.
</td></tr>
</table>
### 6.2 Verarbeitung und verwendete File Formate <a name="file_formats"></a>
Das Programm in diesem Praktikum ist Teil für die graphische
Darstellung von `#include` File Abhängigkeit von C Files.
Den ersten Schritt für die Darstellung der `#include` File
Abhängigkeiten bietet der Compiler. Der Compiler kann mittels der `-H`
Command Line Option auf `stderr` ein Text File generieren, welches die
tatsächlich verwendeten Header Files auflistet. Zusätzlich wird im
Resultat die Verschachtelungstiefe der Includes angegeben.
Im zweiten Schritt übersetzt das Programm (`dep2dot`) dieses
Praktikums solche Dependency Files (`dep`) in eine Text Repräsentation
der Abhängigkeiten (`dot`) welche in graphische Darstel-lung (`png`)
übersetzt werden kann.
Als Tool zur Übersetzung der `dot` Files in das `png` Format dient das
`dot` Tool. Dieses Tool muss gegebenenfalls installiert werden:
```sudo apt install graphviz```
Die `png` Files können dann z.B. in der Programm Dokumentation
integriert werden (Darstellung zu Test Zwecken z.B. mittels `firefox
file.png`).
#### 6.2.1 dep File
Siehe: `man gcc`
```bash
-H Print the name of each header file used, in addition to other
normal activities. Each name is indented to show how deep in the
#include stack it is. [...]
```
Das File wird auf `stderr` ausgegeben.
**Beispiel File** (für Abhängigkeiten des `main.c` Files des `dep2dot` Programms)
```bash
. /usr/include/stdio.h
.. /usr/include/x86_64-linux-gnu/bits/libc-header-start.h
... /usr/include/features.h
.... /usr/include/x86_64-linux-gnu/sys/cdefs.h
..... /usr/include/x86_64-linux-gnu/bits/wordsize.h
..... /usr/include/x86_64-linux-gnu/bits/long-double.h
.... /usr/include/x86_64-linux-gnu/gnu/stubs.h
..... /usr/include/x86_64-linux-gnu/gnu/stubs-64.h
.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h
.. /usr/include/x86_64-linux-gnu/bits/types.h
... /usr/include/x86_64-linux-gnu/bits/wordsize.h
... /usr/include/x86_64-linux-gnu/bits/typesizes.h
.. /usr/include/x86_64-linux-gnu/bits/types/__FILE.h
.. /usr/include/x86_64-linux-gnu/bits/types/FILE.h
.. /usr/include/x86_64-linux-gnu/bits/libio.h
... /usr/include/x86_64-linux-gnu/bits/_G_config.h
.... /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h
.... /usr/include/x86_64-linux-gnu/bits/types/__mbstate_t.h
... /usr/lib/gcc/x86_64-linux-gnu/7/include/stdarg.h
.. /usr/include/x86_64-linux-gnu/bits/stdio_lim.h
.. /usr/include/x86_64-linux-gnu/bits/sys_errlist.h
. /usr/include/stdlib.h
.. /usr/include/x86_64-linux-gnu/bits/libc-header-start.h
.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h
.. /usr/include/x86_64-linux-gnu/bits/floatn.h
... /usr/include/x86_64-linux-gnu/bits/floatn-common.h
.... /usr/include/x86_64-linux-gnu/bits/long-double.h
.. /usr/include/x86_64-linux-gnu/bits/stdlib-float.h
. src/error.h
.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h
. src/data.h
.. /usr/lib/gcc/x86_64-linux-gnu/7/include/stddef.h
. src/output.h
Multiple include guards may be useful for:
/usr/include/x86_64-linux-gnu/bits/stdlib-float.h
/usr/include/x86_64-linux-gnu/bits/sys_errlist.h
/usr/include/x86_64-linux-gnu/bits/typesizes.h
/usr/include/x86_64-linux-gnu/gnu/stubs-64.h
/usr/include/x86_64-linux-gnu/gnu/stubs.h
```
#### 6.2.2 dot File
**Graphviz** ist ein mächtiges Tool-Set welches Graphen, definiert in
einem `dot`-Text File, automatisch anordnet und in `png`, `gif` und
andere Formate übersetzt.
Siehe die offizielle Web-Page
[https://www.graphviz.org/](https://www.graphviz.org/).
Es gibt als Teil dieses Tool-Sets verschiedene Übersetzer. Der hier
verwendete ist der Basis-übersetzer: `dot`.
Das `dot`-File Format kennt viele Möglichkeiten die Knoten und Kanten
eines Graphen und de-ren Anordnung anzugeben.
Der Vorteil eines solchen Tool-Sets ist, dass man den Inhalt (den
Graphen) einfach definieren kann und sich nicht um das komplexe
Problem der ansprechenden Visualisierung kümmern muss.
**Beispiel File** (`dot -Tpng sample.dot > sample.png`)
```C
digraph G {
node [shape=box]
A [label="a.c"];
B [label="a.h"];
C [label="b.h"];
subgraph cluster_c0 {
label="main"; color=black;
A;
}
subgraph cluster_c1 {
label="others"; style=filled; col-or=lightgrey;
{ B; C; rank=same; }
}
A -> B;
A -> C;
B -> C;
}
```
#### 6.2.3 png File
Das `png` Format ist ein verlustfrei komprimiertes Raster Graphik
Format. Es wird oft in Web Pages verwendet.
___
Version: 15.02.2022

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 KiB

View File

@ -0,0 +1 @@
<mxfile host="Electron" modified="2022-02-07T21:41:53.021Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/16.5.1 Chrome/96.0.4664.110 Electron/16.0.7 Safari/537.36" etag="nbLc9Vjb_iVSFHhKpDKu" version="16.5.1" type="device"><diagram id="L8fo7XVtsVLzhWI8Oabt" name="Page-1">7VnRcuIgFP2aPLoTiIn6WLXd7sx2ptPuTrf7RhM0TDG4BGvs1y9RYgLoGjPqVLtPkgtc4Jx74IKON5hkXzmaxncswtSBbpQ53tCBEAReR/7kloWyuK67sow5iZStNDySd1w0VNYZiXCqNRSMUUGmujFkSYJDodkQ52yuNxsxqo86RWNsGR5DRG3rE4lErKwg6JUVt5iMYzV0F6oVT1DRWK0kjVHE5hWTd+14A86YWJUm2QDTHL0Cl1W/my2164lxnIg6He5GrYenJLv986310P99tfj56oKW8vKG6EwtWE1WLAoEcBJd5UDKr5CiNCXhj5gkjtePxYRKI5BFzmZJhPOBXPkle9wQWtStHOLIwrmcOFjDIQMJswkWfCGbzEvAfQViXIG6sHFMkSBvunukeB+v3a1HuGdEDgxdFaR+EWoqRNtdV3eRshkPsepVBdhw1G3vcCQQH2NhOZKFyrJL05K/PbiE/7n0oUFBryGXpiMAT8ultxeXCUvwv0j86LQFro62Z6Jdl7ZOoDuC7dPSVmwlFd6oTZzk4Tt6kSelRhmiZJzkwpScYC4Nb5gLIk+iK1UxIVGU++hznJJ39LL0l9M7zZezXKDfd/zhmvDcAc40TtQxqTqXh1M1FLYHpB0fyrv7BcBuT4f+IJHR0p0WuigcsNEoxUdhst1EgBFK46XowDmr0RTR2aqxY3EYX4YY2zvEGLQ7UNcNOEhkGAoPTiVG//OK0TwaG2c0O8/YI4sxsDh8+QAkrq+lqx5ntsE25bR0dHztdj+vdk2eG2t3Z8AcWbu9z8vhwa4m5jPDqTkE9lPPPbBolCmK0OlKBWeveMAo4yW3I0KpYaqfMc1jIvDjFIX5mHOOpluCYkPGVCMsCri7vgY37NlbPNwQNp67PUI0SvbGH9r42zK6GPyt3H/DEXta/Bs9qdTbxHBGxK9K+Vm1ysvDrFIxXOy57cnpLfeXnYH1UVIb36DdvPI13S7r3h1PmdoA+16SXa6ireftoJ6iTeIOp2g7LVlcLvymIIBbD34zzagBv/ws/5ZaiaX8d8+7/gs=</diagram></mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -0,0 +1,127 @@
# 04 - Modularisieren von C Code
___
## 1. Übersicht
In diesem Praktikum wird eine kleine Sammlung von Funktionen als Modul erstellt.
In der ersten Aufgabe schreiben Sie zu einem bestehenden C Programm die notwendigen Header Files plus passen das Makefile so an, dass die entsprechenden Module mit kompiliert werden.
In der zweiten Aufgabe erstellen Sie Makefile Regeln um aus Konfigurationsdateien graphischen Darstellungen zu erzeugen.
___
## 2. Lernziele
In diesem Praktikum lernen Sie die Handgriffe um ein Programm zu modularisieren, d.h. in mehrere Module aufzuteilen.
- Sie wissen, dass ein Modul aus einem C-File und einem passenden H-File bestehen.
- Sie können Header Files korrekt strukturieren.
- Sie wissen wie **Include Guards** anzuwenden sind.
- Sie können Module im **Makefile** zur Kompilation hinzufügen.
- Sie können anhand einer Beschreibung Typen und Funktionen in den passenden Header Files deklarieren.
- Sie können **Makefile** Regeln schreiben.
Die Bewertung dieses Praktikums ist am Ende angegeben.
Erweitern Sie die vorgegebenen Code Gerüste, welche im **git** Repository **snp-lab-code** verfügbar sind.
___
## 3. Aufgabe 1: Modularisieren
![](./P04_Aufgabenstellung.png)
### 3.1 Teilaufgabe Modules einbinden, Header Files schreiben
- src/objects.h
- 2 Datenstukturen definieren
- `struct point` mit 2 double für x und y Koordinate
- `struct line` mit 2 point
- src/functions.h und .c
- 2 Funktionen deklarieren und definieren
- Berechnung der Länge `get_length`einer Linie (Annahme: Koordinaten sind alle positiv)
- l = sqrt(h^ 2 + b^ 2)
- ev. muss hier in den Anhang `#include <math.h>`
- Berechnung der Steigung `get_slope` der Linie gegenüber dem Koordinatensystem
- m = h / b
- tests vorgeben
- src/objects.h
- Include Guard
- Includes
- Struct für Punkt und Linie
- Include Guard
- src/functions.h
- Include Guard
- Includes
- Deklarationen der Funktionen für Berechnung der Länge und Steigung
- Include Guard
- src/functions.c
- Includes
- Definitionen der Funktionen für Berechnung der Länge und Steigung
- Include Guard
___
## 4. Aufgabe 2: Makefile Regeln
Makefile ergänzen, damit Modul `functions` korrekt eingebunden und kompiliert wird.
1. Kompilieren Sie das ganze mittels **make clean default**. Es sollten keine Compiler Fehler auftreten.
### 4.1 Neue Regeln hinzufügen
- Vorraussetzung: tab2svg.sh aus Praktikum 3 wird um die Möglichkeit erweitert eine Linie zu zeichnen (`line:x1:y1:x2:y2:color`)
- Studierende erstellen
- mind. 2 Files `long.line` und `short.line` mit 2 unterschiedlichen Linien
- Makefile Regeln um aus einem File `.line` ein File `.svg` mit Hilfe des Scripts zu erstellen
- PHONY Regel `display` um beide `.svg` mit Firefox darzustellen
- Vorgabe: sie sollen eine Variable für die Input-Dateien nutzen
Nachdem das Programm in Aufgabe 1 umgesetzt ist, geht es nun darum, im **Makefile** Regeln zu definieren welche die einzelnen Schritte von den Source Files zu den **png** Files ausführen.
Prüfen Sie schliesslich die Umsetzung mittels `make display`.
___
## 5. Aufgabe 3
- Studierende sollen Ausgabe von `make doc` analysieren und die Include Diagramme erklären können
```
make doc
firefox doc/index.html &
```
___
## 6. Bewertung
Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.
___
## 7. Erweiterung Doxyfile für Abhängigkeitsanalyse
```
--- /home/vagrant/huno/snp-new/snp/praktika/Shared/work/Doxyfile 2022-02-07 21:16:42.343302707 +0100
+++ /home/vagrant/snp/Doxyfile 2022-02-07 22:22:36.266839126 +0100
@@ -297,14 +297,14 @@
UML_LOOK = NO
UML_LIMIT_NUM_FIELDS = 10
TEMPLATE_RELATIONS = NO
-INCLUDE_GRAPH = NO
-INCLUDED_BY_GRAPH = NO
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
CALLER_GRAPH = NO
-GRAPHICAL_HIERARCHY = NO
-DIRECTORY_GRAPH = NO
+GRAPHICAL_HIERARCHY = YES
+DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = png
-INTERACTIVE_SVG = NO
+INTERACTIVE_SVG = YES
DOT_PATH =
DOTFILE_DIRS =
MSCFILE_DIRS =
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -0,0 +1,88 @@
/*****************************************************************************
M. Thaler, Jan. 2000
Datei read.java
Funktion: Unsigned int Zahl via Bytestream von stdin einlesen.
Es wird zuerst eine Zeile eingelesen und dann konvertiert.
Die eingelesene Zeile darf nur eine Zahl enthalten
(mit optionalen Leerzeichen vor/nach der Zahl).
Returns: Die konvertierte Zahl
oder -1 (PARSE_ERROR) wenn keine Zahl oder zu gross
oder -2 (READ_ERROR) wenn Fehler beim einlesen.
Korrekturen: - Maerz 2002: M. Thaler, H. Fierz, Mar. 2002
liest bis EOL oder EOF, korrekter Rueckgabewert
- Sept 2016: A. Gieriet
Refactored (sprechende Variablen-Namen,
MS Windows Support, 0 Wert erlaubt,
Leerzeichen Support,
"magic" Numbers durch Symbole ersetzt,
maxResult Parameter, etc.)
******************************************************************************/
public class read {
public int getInt(int maxResult)
throws java.io.IOException
{
// end of input
int EOF = -1; // end of file
int EOL = 10; // end of line
// abnormal return values
int PARSE_ERROR = -1;
int READ_ERROR = -2;
// ASCII Codes
int ASCII_SPACE = 32; // ' '
int ASCII_DIGIT_0 = 48; // '0'
int ASCII_DIGIT_9 = 57; // '9'
// conversion buffer
int NO_POS = -1;
int BUFFERSIZE = 10;
byte[] buffer = new byte[BUFFERSIZE];
int result = 0;
// read line: up to EOL or EOF (i.e. error while reading)
int bytes = 0;
int input = System.in.read();
while ((input != EOL) && (input != EOF)) { // read whole line
if (bytes < BUFFERSIZE) { // only buffer first n characters
buffer[bytes] = (byte)input;
bytes++;
} else {
result = PARSE_ERROR; // exceed buffer size, continue read line
}
input = System.in.read();
}
if (input == EOF) {
result = READ_ERROR;
}
// check for numbers: skip leading and trailing spaces
// (i.e. this includes all control chars below the space ASCII code)
int pos = 0;
while((pos < bytes) && (buffer[pos] <= ASCII_SPACE)) pos++; // skip SP
int posOfFirstDigit = pos;
int posOfLastDigit = NO_POS;
while ((pos < bytes)
&& (buffer[pos] >= ASCII_DIGIT_0)
&& (buffer[pos] <= ASCII_DIGIT_9))
{
posOfLastDigit = pos;
pos++;
}
while((pos < bytes) && (buffer[pos] <= ASCII_SPACE)) pos++; // skip SP
// produce return value
if (result != 0) {
// previously detected read or parse error given
} else if ((pos != bytes) || (posOfLastDigit == NO_POS)) {
result = PARSE_ERROR;
} else { // convert number
for(int i = posOfFirstDigit; i <= posOfLastDigit; i++) {
result = result * 10 + (buffer[i] - ASCII_DIGIT_0);
if (result > maxResult) {
result = PARSE_ERROR;
break;
}
}
}
return result;
}
}

View File

@ -0,0 +1,32 @@
/*****************************************************************************
M. Thaler, Jan. 2000
Datei: rectang.java
Funktion: Bestimmt, ob Dreieck rechtwinklig ist.
Returns: true wenn rechtwinklig, sonst false.
Korrekturen: - Sept 2016, A. Gieriet
Refactored (sprechende Variablen Namen, etc.)
******************************************************************************/
public class rectang {
public boolean Rectangular(int a, int b, int c) {
int aS = a*a;
int bS = b*b;
int cS = c*c;
boolean isRightAngled;
if ((a == 0) && (b == 0) && (c == 0))
isRightAngled = false;
else if ((aS + bS) == cS)
isRightAngled = true;
else if ((aS + cS) == bS)
isRightAngled = true;
else if ((bS + cS) == aS)
isRightAngled = true;
else
isRightAngled = false;
return isRightAngled;
}
}

View File

@ -0,0 +1,73 @@
/*****************************************************************************
M. Thaler, Jan. 2000
Datei: triangle.java
Funktion: Die drei Seiten eines Dreiecks einlesen und bestimmen ob
das Dreieck rechtwinklig ist.
Returns: Nichts.
Korrekturen: - Maerz 2002, M. Thaler, H. Fierz
Abfrage bei unkorrekter Eingabe wiederholen
- Sept 2016, A. Gieriet
Refactored (sprechende Variablen-Namen,
"magic" Numbers durch Symbole ersetzt, etc.)
******************************************************************************/
class triangle {
public static void main(String[] args)
throws java.io.IOException
{
int READ_ERROR = -2;
int MAX_NUMBER = 1000;
read ReadInt = new read();
rectang Rect = new rectang();
while (true) {
System.out.println("\nDreiecksbestimmung (CTRL-C: Abbruch)\n");
int word = 0;
int a = 0;
int b = 0;
int c = 0;
do {
System.out.print("Seite a: ");
word = ReadInt.getInt(MAX_NUMBER);
}
while ((word < 0) && (word != READ_ERROR));
if (word >= 0)
a = word;
else
break;
do {
System.out.print("Seite b: ");
word = ReadInt.getInt(MAX_NUMBER);
}
while ((word < 0) && (word != READ_ERROR));
if (word >= 0)
b = word;
else
break;
do {
System.out.print("Seite c: ");
word = ReadInt.getInt(MAX_NUMBER);
}
while ((word < 0) && (word != READ_ERROR));
if (word >= 0)
c = word;
else
break;
if (Rect.Rectangular(a, b, c) == true)
System.out.println("-> Dreieck " + a + "-" + b + "-" + c
+ " ist rechtwinklig");
else
System.out.println("-> Dreieck " + a + "-" + b + "-" + c
+ " ist nicht rechtwinklig");
System.out.println("\n");
}
System.out.println("\n\nbye bye\n");
}
}

View File

@ -0,0 +1,16 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab implementation
*/
// begin students to add code for task 4.1
// end students to add code

View File

@ -0,0 +1,16 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab implementation
*/
// begin students to add code for task 4.1
// end students to add code

View File

@ -0,0 +1,16 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab implementation
*/
// begin students to add code for task 4.1
// end students to add code

View File

@ -0,0 +1,16 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab implementation
*/
// begin students to add code for task 4.1
// end students to add code

View File

@ -0,0 +1,16 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab implementation
*/
// begin students to add code for task 4.2
// end students to add code

View File

@ -0,0 +1,34 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab implementation
*/
#include <stdio.h>
#include <stdlib.h>
#include "read.h"
#include "rectang.h"
#include "trace.h"
/// max side length
#define MAX_NUMBER 1000
/**
* @brief Main entry point.
* @returns Returns EXIT_SUCCESS (=0) on success, EXIT_FAILURE (=1) on failure.
*/
int main(void)
{
// begin students to add code for task 4.1
// end students to add code
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,12 @@
a
a
a
3
4444
4444
44444444444444
4
4
5
5

View File

@ -0,0 +1,12 @@
3
4
6
5
4
4
3
5
5
33
43
55

View File

@ -0,0 +1,12 @@
3
4
5
5
4
3
3
5
4
33
44
55

View File

@ -0,0 +1,205 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab implementation
*/
#include <stdio.h>
#include <stdlib.h>
#include "CUnit/Basic.h"
#include "test_utils.h"
#ifndef TARGET // must be given by the make file --> see test target
#error missing TARGET define
#endif
/// @brief The name of the STDOUT text file.
#define OUTFILE "stdout.txt"
/// @brief The name of the STDERR text file.
#define ERRFILE "stderr.txt"
/// @brief The stimulus for the right-angled triangles
#define INFILE_RIGHT_ANGLED "stim-right-angled.input"
/// @brief The stimulus for the not right-angled triangles
#define INFILE_NOT_RIGHT_ANGLED "stim-not-right-angled.input"
/// @brief The stimulus for input errors
#define INFILE_ERROR "stim-error.input"
// setup & cleanup
static int setup(void)
{
remove_file_if_exists(OUTFILE);
remove_file_if_exists(ERRFILE);
return 0; // success
}
static int teardown(void)
{
// Do nothing.
// Especially: do not remove result files - they are removed in int setup(void) *before* running a test.
return 0; // success
}
// tests
static void test_right_angled(void)
{
// arrange
const char *out_txt[] = {
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 3-4-5 ist rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 5-4-3 ist rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 3-5-4 ist rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 33-44-55 ist rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: \n",
"\n",
"bye bye\n",
"\n",
};
// act
int exit_code = system(XSTR(TARGET) " 1>" OUTFILE " 2>" ERRFILE " <" INFILE_RIGHT_ANGLED);
// assert
CU_ASSERT_EQUAL(exit_code, 0);
assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
}
static void test_not_right_angled(void)
{
// arrange
const char *out_txt[] = {
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 3-4-6 ist nicht rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 5-4-4 ist nicht rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 3-5-5 ist nicht rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite b: Seite c: -> Dreieck 33-43-55 ist nicht rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: \n",
"\n",
"bye bye\n",
"\n",
};
// act
int exit_code = system(XSTR(TARGET) " 1>" OUTFILE " 2>" ERRFILE " <" INFILE_NOT_RIGHT_ANGLED);
// assert
CU_ASSERT_EQUAL(exit_code, 0);
assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
}
static void test_trace(void)
{
// arrange
const char *err_txt[] = {
"TRACE: main()\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: rectangular(3, 4, 6)\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: rectangular(5, 4, 4)\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: rectangular(3, 5, 5)\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: getInt(1000)\n",
"TRACE: rectangular(33, 43, 55)\n",
"TRACE: getInt(1000)\n",
};
// act
int exit_code = system(XSTR(TARGET) " 1>" OUTFILE " 2>" ERRFILE " <" INFILE_NOT_RIGHT_ANGLED);
// assert
CU_ASSERT_EQUAL(exit_code, 0);
assert_lines(ERRFILE, err_txt, sizeof(err_txt)/sizeof(*err_txt));
}
static void test_error(void)
{
// arrange
const char *out_txt[] = {
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: Seite a: Seite a: Seite a: Seite a: Seite b: Seite b: Seite b: Seite b: Seite b: Seite c: Seite c: -> Dreieck 3-4-5 ist rechtwinklig\n",
"\n",
"\n",
"\n",
"Dreiecksbestimmung (CTRL-C: Abbruch)\n",
"\n",
"Seite a: \n",
"\n",
"bye bye\n",
"\n",
};
// act
int exit_code = system(XSTR(TARGET) " 1>" OUTFILE " 2>" ERRFILE " <" INFILE_ERROR);
// assert
CU_ASSERT_EQUAL(exit_code, 0);
assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
}
/**
* @brief Registers and runs the tests.
*/
int main(void)
{
// setup, run, teardown
TestMainBasic("Triangle", setup, teardown
, test_right_angled
, test_not_right_angled
, test_trace
, test_error
);
}

View File

@ -0,0 +1,65 @@
SNP_SHARED_MAKEFILE := $(if $(SNP_SHARED_MAKEFILE),$(SNP_SHARED_MAKEFILE),"~/snp/shared.mk")
TARGET := bin/dep2dot
# Add all additional c-files to the SOURCES variable
# BEGIN-STUDENTS-TO-ADD-CODE
SOURCES := src/main.c
# END-STUDENTS-TO-ADD-CODE
TSTSOURCES := tests/tests.c
include $(SNP_SHARED_MAKEFILE)
# DEPFILES := ... define a list of png file names: %.c -> %.c.png
# BEGIN-STUDENTS-TO-ADD-CODE
# END-STUDENTS-TO-ADD-CODE
# define dep target as .PHONEY
# BEGIN-STUDENTS-TO-ADD-CODE
# BEGIN-STUDENTS-TO-ADD-CODE
# define dep target depending on FULLTARGET and DEPFILES above
# action: echo some text telling that the target is done using $@ - the echo command shall not be echoed before execution
# BEGIN-STUDENTS-TO-ADD-CODE
# BEGIN-STUDENTS-TO-ADD-CODE
# define new suffix rule for %.png depending on %.dot
# action: dot -Tpng $< >$@ || $(RM) $@
# BEGIN-STUDENTS-TO-ADD-CODE
# BEGIN-STUDENTS-TO-ADD-CODE
# define new suffix rule for %.dot depending on %.dep
# action: call $(TARGET) $(@:.dot=) <$< >$@ || $(RM) $@
# BEGIN-STUDENTS-TO-ADD-CODE
# BEGIN-STUDENTS-TO-ADD-CODE
# converts any .c file into a .c.dep file by means of GCC -H switch
# note: it removes intermediate files which were created as side effect
%.c.dep: %.c
$(COMPILE.c) -H -o $@.x $< 2>$@ && $(RM) $@.x $@.d
# cleanup all results, including the ones od creating the dependencies
dep-clean: clean
$(RM) $(DEPFILES) $(wildcard src/*.dep src/*.dot)

View File

@ -0,0 +1,8 @@
/**
* @mainpage SNP - P04 Modularisation
*
* @section Purpose
*
* This is a lab for splitting functionality into multiple modules.
*
*/

View File

@ -0,0 +1,149 @@
/**
* @file
* @brief Implementation of the dependency file access.
*/
#include "data.h"
#include "error.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <assert.h>
#define MAX_PATH_LEN 512 ///< @brief Arbitrarily chosen maximum accepted path lenght.
#define MAX_LINE_LEN 512 ///< @brief Arbitrarily chosen maximum accepted line length
#define MAX_DIRS 64 ///< @brief Arbitrarily chosen maximum number of supported individual directories per dependency file.
#define MAX_FILES 256 ///< @brief Arbitrarily chosen maximum number of supported individual denendency entries.
/**
* @brief Declaration of POSIX (but not C99) function.
* @param s [IN] The string to duplicate on the heap memory.
* @return The duplicated string.
* @remark Since the Makefile calls gcc with -std=c99, non-C99 POSIX and GNU extensions are excluded - the glibc, though, provides the function to the linker.
*/
char *strdup(const char *s); // not stdc99, but available in the glibc
/**
* @brief Initialized the data structure before the data is to be read from th edependency file.
* @param data [INOUT] The address of the instance to initialize.
*/
static void init(data_t *data)
{
assert(data);
memset(data, 0, sizeof(data_t));
data->dirs = malloc(MAX_DIRS * sizeof(dir_t));
if (!data->dirs) FATAL("no memory left");
data->files = malloc(MAX_FILES * sizeof(file_t));
if (!data->files) FATAL("no memory left");
}
/**
* @brief Updates the directory list with the given data.
* @param data [INOUT] The instance to update.
* @param path [IN] The file path of a dependency entry as given by the dependency file.
* @return The index of the directory entry (either an existing matching one or a newly added one).
* @remark Extracts the directory part by means of dirname() from the given path and looks up an existing entry or adds a new one.
*/
static size_t get_or_add_dir(data_t *data, const char *path)
{
assert(data);
assert(path);
// The function dirname() gives no guarantee to not modify the parameter, therefore, need to produce a copy before calling dirname().
// Likewise, the returned value may refer to the passed paremater, therefore, a copy is made from the return value.
char *dup = strdup(path);
if (!dup) FATAL("no memory left");
char *name = strdup(dirname(dup));
if (!name) FATAL("no memory left");
free(dup);
// search for a matching entry...
size_t i = 0;
while(i < data->n_dirs && strcmp(data->dirs[i].name, name) != 0) {
i++;
}
if (i >= MAX_DIRS) FATAL("too many directories");
if (i == data->n_dirs) { // no match found: add
// handover the allocated name to the owning new directory entry
dir_t dir = { .name = name };
// append the new directory entry
data->dirs[data->n_dirs] = dir;
data->n_dirs++;
} else {
// release the name since match found, and therefore, no need to keep the allocated name anymore
free(name);
}
return i;
}
/**
* @brief Add a file entry from the dependency file to the data structure.
* @param data [INOUT] The data container instance.
* @param path [IN] The path of one file entry from the dependency file.
* @param level [IN] The dependency level of the file entry from the dependency file.
* @remark The sequence of entries in the dependency file is relevant - it implies direct dependencies.
*/
static void add_file(data_t *data, const char *path, size_t level)
{
assert(data);
assert(path);
// The function basename() gives no guarantee to not modify the parameter, therefore, need to produce a copy before calling basename().
// Likewise, the returned value may refer to the passed paremater, therefore, a copy is made from the return value.
char *dup = strdup(path);
if (!dup) FATAL("no memory left");
char *name = strdup(basename(dup));
if (!name) FATAL("no memory left");
free(dup);
if (data->n_files >= MAX_FILES) FATAL("too many files");
// produce a file entry
file_t file = { .name = name, .dir = get_or_add_dir(data, path), .level = level };
data->files[data->n_files] = file;
data->n_files++;
}
/**
* @brief Processes one dependency line of the dependency file.
* @param data [INOUT] The data container instance.
* @param line [IN] The line to parse and store in data.
*/
static void process_line(data_t *data, const char line[])
{
assert(data);
size_t len = strlen(line);
assert(len > 0);
assert(line[0] == '.');
// read level
size_t i = strspn(line, ".");
size_t level = i;
// skip spaces
i += strspn(line+i, " \t");
// take rest as path and add the file to the records
add_file(data, line+i, level);
}
/*
* The public interface.
*/
const data_t data_read_all(const char *root)
{
data_t data;
init(&data);
// add as first file the root for the given dependencies
add_file(&data, root, 0);
char line[MAX_LINE_LEN] = { 0 };
// read all stdin line and only process dependency lines (those starting on a '.')
clearerr(stdin);
while(fgets(line, MAX_LINE_LEN, stdin)) {
size_t len = strlen(line);
if (len > 0 && line[len-1] == '\n' && line[0] == '.') { // only dependency lines
line[len-1] = '\0';
process_line(&data, line);
}
}
return data;
}

View File

@ -0,0 +1,66 @@
/**
* @file
* @brief Access to the GCC produced dependency data (via gcc -H command line option).
*/
// begin of include guard
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
// includes which are needed in this header file
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
/**
* @brief Directory container for file entries of the dependency file.
*/
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
/**
* @brief File container for the file entries of the dependency file.
*/
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
/**
* @brief Overall container for all directories and all files from the dependency file.
*/
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
/**
* @brief Entry function to read the deendency data from stdin.
* @param root [IN] The name of the root file (the deoendency file does not mention the root file, so, it has to be passed from outside).
* @return The container of the read data from stdin. See the documentation on gcc -H for details on the dependencies, etc.
*/
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
// end of include guard
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE

View File

@ -0,0 +1,17 @@
/**
* @file
* @brief Error handling convenience functions.
*/
#ifndef _ERROR_H_
#define _ERROR_H_
#include <stdio.h>
#include <stddef.h>
/**
* @brief Prints the message to stderr and terminates with EXIT_FAILURE.
* @param MSG [IN] The "..." *string literal* to emit as error - no format parameters nor variables supported.
*/
#define FATAL(MSG) do { fprintf(stderr, "ERROR: %s\n", MSG); exit(EXIT_FAILURE); } while(0)
#endif // _ERROR_H_

View File

@ -0,0 +1,36 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab P04 dep2dot
*/
#include <stdio.h>
#include <stdlib.h>
#include "error.h"
#include "data.h"
#include "output.h"
/**
* @brief main function
* @param argc [in] number of entries in argv
* @param argv [in] program name plus command line arguments
* @returns returns success if valid date is given, failure otherwise
* @remark Prerequisit to convert the resulting DOT file on the shell: sodo apt install graphviz
* @remark Convert: gcc -H ... file.c ... 2>file.dep ; dep2dot file.c <file.dep >file.dot && dot -Tpng file.dot >file.png
*/
int main(int argc, const char *argv[])
{
if (argc < 2) FATAL("missing arguments\nusage: dep2dot file.c <file.dep >file.dot # from gcc -H ... file.c ... 2>file.dep\n");
output_dot(data_read_all(argv[1]));
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,100 @@
/**
* @file
* @brief Provides output functions for various file formats.
*/
#include "output.h"
#include <stdio.h>
#include <assert.h>
#include <string.h>
/**
* @brief Writes the node name of the given file.
* @param file [IN] The file for which to write the node name.
* @remark The dependency data contain duplicates of file entries - the node name must be unique for the path and the *basename* of the files.
*/
static void print_node(file_t file)
{
printf("\"%s (cluster_c%zd)\"", file.name, file.dir);
}
/**
* @brief Recursively writes the individual direct dependencies for the file given by curr.
* @param files [IN] The array of all files - the sequence is relevant.
* @param len [IN] The lenght of the files array, i.e. the upper limit for curr values and the subsequent index values.
* @param curr [IN] The index into files for the current root for dependencies: curr -> x, curr -> y, ...
* @return Returns the index into files for the next file to process (i.e. curr value for the next call to this function).
* @remark For a given *curr* file, all following files are with depth level + 1 are direct include files.
* @remark All files with a higher level are *indirect* include files, thus *direct* includes from files processed by recursive calls.
* @remark The list of direct includes to the *curr* file terminates with a level equal of less the the *curr* one (or when the list is exchausted).
*/
static size_t dependencies(file_t files[], size_t len, size_t curr)
{
assert(curr < len);
size_t level = files[curr].level;
size_t file = curr + 1;
while(file < len && files[file].level > level) {
if (files[file].level == level + 1) {
// Write to stdout " file -> include;\n" where file and include are the DOT node names of the respective files
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
file = dependencies(files, len, file);
} else {
file++;
}
}
return file;
}
/*
* Public interface
*/
void output_dot(const data_t data)
{
printf("digraph dep {\n");
// nodes
printf(" node [shape=box]\n");
for (size_t file = 0; file < data.n_files; file++) {
// Write to stdout " file [label=\"name\"];\n" where file is the DOT node name and name is the file name
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
}
// directory clusters
for (size_t dir = 0; dir < data.n_dirs; dir++) {
printf(" subgraph cluster_c%zd {\n", dir);
printf(" label=\"%s\"; %s\n", data.dirs[dir].name, strncmp(data.dirs[dir].name, "/usr/", 5) == 0 ? "style=filled; color=lightgrey;" : "color=black;");
for (size_t file = 0; file < data.n_files; file++) {
if (data.files[file].dir == dir) {
// Write to stdout " file;\n" where file is the DOT node name
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
}
}
printf(" }\n");
}
// dependencies
size_t curr = 0;
do {
curr = dependencies(data.files, data.n_files, curr);
} while(curr < data.n_files);
printf("}\n");
}

View File

@ -0,0 +1,12 @@
/**
* @file
* @brief Provides output functions for various file formats.
*/
// define proper header file here, with include gaurd, etc.
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE

View File

@ -0,0 +1,7 @@
Test File
. dir1/h1_1
.. dir1/h1_1_2
. dir1/h1_2
. dir2/h2_1
.. dir1/h1_1
Done

View File

@ -0,0 +1,2 @@
Test File
Done

View File

@ -0,0 +1,140 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Test suite for the given package.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <time.h>
#include <assert.h>
#include <CUnit/Basic.h>
#include "test_utils.h"
#ifndef TARGET // must be given by the make file --> see test target
#error missing TARGET define
#endif
/// @brief alias for EXIT_SUCCESS
#define OK EXIT_SUCCESS
/// @brief alias for EXIT_FAILURE
#define FAIL EXIT_FAILURE
/// @brief The name of the STDOUT text file.
#define OUTFILE "stdout.txt"
/// @brief The name of the STDERR text file.
#define ERRFILE "stderr.txt"
/// @brief test data file
#define IN_NO_DEP "no_dep.input"
/// @brief test data file
#define IN_DEP "dep.input"
// setup & cleanup
static int setup(void)
{
remove_file_if_exists(OUTFILE);
remove_file_if_exists(ERRFILE);
return 0; // success
}
static int teardown(void)
{
// Do nothing.
// Especially: do not remove result files - they are removed in int setup(void) *before* running a test.
return 0; // success
}
// tests
static void test_fail_no_arg(void)
{
// arrange & act & assert
CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " >" OUTFILE " 2>" ERRFILE)), FAIL);
}
static void test_no_dep(void)
{
// arrange
const char *out_txt[] = {
"digraph dep {\n",
" node [shape=box]\n",
" \"root (cluster_c0)\" [label=\"root\"];\n",
" subgraph cluster_c0 {\n",
" label=\".\"; color=black;\n",
" \"root (cluster_c0)\";\n",
" }\n",
"}\n",
};
// act & assert
CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " root <" IN_NO_DEP " >" OUTFILE " 2>" ERRFILE)), OK);
// assert
assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
}
static void test_dep(void)
{
// arrange
const char *out_txt[] = {
"digraph dep {\n",
" node [shape=box]\n",
" \"root (cluster_c0)\" [label=\"root\"];\n",
" \"h1_1 (cluster_c1)\" [label=\"h1_1\"];\n",
" \"h1_1_2 (cluster_c1)\" [label=\"h1_1_2\"];\n",
" \"h1_2 (cluster_c1)\" [label=\"h1_2\"];\n",
" \"h2_1 (cluster_c2)\" [label=\"h2_1\"];\n",
" \"h1_1 (cluster_c1)\" [label=\"h1_1\"];\n",
" subgraph cluster_c0 {\n",
" label=\".\"; color=black;\n",
" \"root (cluster_c0)\";\n",
" }\n",
" subgraph cluster_c1 {\n",
" label=\"dir1\"; color=black;\n",
" \"h1_1 (cluster_c1)\";\n",
" \"h1_1_2 (cluster_c1)\";\n",
" \"h1_2 (cluster_c1)\";\n",
" \"h1_1 (cluster_c1)\";\n",
" }\n",
" subgraph cluster_c2 {\n",
" label=\"dir2\"; color=black;\n",
" \"h2_1 (cluster_c2)\";\n",
" }\n",
" \"root (cluster_c0)\" -> \"h1_1 (cluster_c1)\";\n",
" \"h1_1 (cluster_c1)\" -> \"h1_1_2 (cluster_c1)\";\n",
" \"root (cluster_c0)\" -> \"h1_2 (cluster_c1)\";\n",
" \"root (cluster_c0)\" -> \"h2_1 (cluster_c2)\";\n",
" \"h2_1 (cluster_c2)\" -> \"h1_1 (cluster_c1)\";\n",
"}\n",
};
// act & assert
CU_ASSERT_EQUAL(WEXITSTATUS(system(XSTR(TARGET) " root <" IN_DEP " >" OUTFILE " 2>" ERRFILE)), OK);
// assert
assert_lines(OUTFILE, out_txt, sizeof(out_txt)/sizeof(*out_txt));
}
/**
* @brief Registers and runs the tests.
* @returns success (0) or one of the CU_ErrorCode (>0)
*/
int main(void)
{
// setup, run, teardown
TestMainBasic("lab test", setup, teardown
, test_fail_no_arg
, test_no_dep
, test_dep
);
}

BIN
P05_TicTacToe/Control.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

32
P05_TicTacToe/Control.svg Normal file
View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
<svg fill-opacity="1" xmlns:xlink="http://www.w3.org/1999/xlink" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" width="160" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="80 210 160 140" height="140" xmlns="http://www.w3.org/2000/svg" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto"
><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"
/><g
><defs id="defs1"
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"
><path d="M0 0 L2147483647 0 L2147483647 2147483647 L0 2147483647 L0 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"
><path d="M0 0 L0 50 L120 50 L120 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath3"
><path d="M0 0 L0 60 L30 60 L30 0 Z"
/></clipPath
></defs
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(100,230)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(100,230)"
><rect fill="none" x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath2)"
/><text x="11" font-size="30px" y="32.7917" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Control</text
></g
><g transform="translate(150,270)"
><path fill="none" d="M10.5 39.5 L10.5 10.5" clip-path="url(#clipPath3)"
/><path fill="none" d="M17 28.7417 L10.5 40 L4 28.7417" clip-path="url(#clipPath3)"
/></g
></g
></svg
>

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
P05_TicTacToe/MVC.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

52
P05_TicTacToe/MVC.svg Normal file
View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
<svg fill-opacity="1" xmlns:xlink="http://www.w3.org/1999/xlink" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" width="160" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="80 130 160 250" height="250" xmlns="http://www.w3.org/2000/svg" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto"
><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"
/><g
><defs id="defs1"
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"
><path d="M0 0 L2147483647 0 L2147483647 2147483647 L0 2147483647 L0 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"
><path d="M0 0 L0 50 L120 50 L120 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath3"
><path d="M0 0 L0 60 L30 60 L30 0 Z"
/></clipPath
></defs
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(100,310)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(100,310)"
><rect fill="none" x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath2)"
/><text x="19" font-size="30px" y="32.7917" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Model</text
></g
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(100,230)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(100,230)"
><rect fill="none" x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath2)"
/><text x="11" font-size="30px" y="32.7917" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Control</text
></g
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(100,150)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(100,150)"
><rect fill="none" x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath2)"
/><text x="27" font-size="30px" y="32.7917" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>View</text
></g
><g transform="translate(150,190)"
><path fill="none" d="M10.5 39.5 L10.5 10.5" clip-path="url(#clipPath3)"
/><path fill="none" d="M17 28.7417 L10.5 40 L4 28.7417" clip-path="url(#clipPath3)"
/></g
><g transform="translate(150,270)"
><path fill="none" d="M10.5 39.5 L10.5 10.5" clip-path="url(#clipPath3)"
/><path fill="none" d="M17 28.7417 L10.5 40 L4 28.7417" clip-path="url(#clipPath3)"
/></g
></g
></svg
>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<diagram program="umlet" version="14.3.0">
<zoom_level>10</zoom_level>
<element>
<id>UMLClass</id>
<coordinates>
<x>180</x>
<y>310</y>
<w>120</w>
<h>50</h>
</coordinates>
<panel_attributes>View
fontsize=30</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>UMLClass</id>
<coordinates>
<x>180</x>
<y>390</y>
<w>120</w>
<h>50</h>
</coordinates>
<panel_attributes>Control
fontsize=30</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>UMLClass</id>
<coordinates>
<x>180</x>
<y>470</y>
<w>120</w>
<h>50</h>
</coordinates>
<panel_attributes>Model
fontsize=30</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>Relation</id>
<coordinates>
<x>230</x>
<y>430</y>
<w>30</w>
<h>60</h>
</coordinates>
<panel_attributes>lt=&lt;-</panel_attributes>
<additional_attributes>10.0;40.0;10.0;10.0</additional_attributes>
</element>
<element>
<id>Relation</id>
<coordinates>
<x>230</x>
<y>350</y>
<w>30</w>
<h>60</h>
</coordinates>
<panel_attributes>lt=&lt;-</panel_attributes>
<additional_attributes>10.0;40.0;10.0;10.0</additional_attributes>
</element>
<element>
<id>Text</id>
<coordinates>
<x>320</x>
<y>320</y>
<w>210</w>
<h>40</h>
</coordinates>
<panel_attributes>//Text Element
View kennt Control,
Control aber nicht View
</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>Text</id>
<coordinates>
<x>320</x>
<y>400</y>
<w>210</w>
<h>40</h>
</coordinates>
<panel_attributes>//Text Element
Control kennt Model,
Model aber nicht Control.
</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>Text</id>
<coordinates>
<x>320</x>
<y>480</y>
<w>250</w>
<h>40</h>
</coordinates>
<panel_attributes>//Text Element
Das Main Programm kombiniert die
Komponenten nach obigen Vorgaben
und startet die Abarbeitung von Eingaben
über die View.
</panel_attributes>
<additional_attributes/>
</element>
</diagram>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
<svg fill-opacity="1" xmlns:xlink="http://www.w3.org/1999/xlink" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" width="430" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="160 290 430 250" height="250" xmlns="http://www.w3.org/2000/svg" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto"
><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"
/><g
><defs id="defs1"
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"
><path d="M0 0 L2147483647 0 L2147483647 2147483647 L0 2147483647 L0 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"
><path d="M0 0 L0 40 L250 40 L250 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath3"
><path d="M0 0 L0 40 L210 40 L210 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath4"
><path d="M0 0 L0 50 L120 50 L120 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath5"
><path d="M0 0 L0 60 L30 60 L30 0 Z"
/></clipPath
></defs
><g font-family="sans-serif" font-size="14px" transform="translate(320,480)"
><text x="5" xml:space="preserve" y="17.9688" clip-path="url(#clipPath2)" stroke="none"
>Das Main Programm kombiniert die</text
><text x="5" xml:space="preserve" y="33.9375" clip-path="url(#clipPath2)" stroke="none"
>Komponenten nach obigen Vorgaben</text
><text x="5" xml:space="preserve" y="49.9062" clip-path="url(#clipPath2)" stroke="none"
>und startet die Abarbeitung von Eingaben</text
><text x="5" xml:space="preserve" y="65.875" clip-path="url(#clipPath2)" stroke="none"
>über die View.</text
></g
><g font-family="sans-serif" font-size="14px" transform="translate(320,400)"
><text x="5" xml:space="preserve" y="17.9688" clip-path="url(#clipPath3)" stroke="none"
>Control kennt Model,</text
><text x="5" xml:space="preserve" y="33.9375" clip-path="url(#clipPath3)" stroke="none"
>Model aber nicht Control.</text
></g
><g font-family="sans-serif" font-size="14px" transform="translate(320,320)"
><text x="5" xml:space="preserve" y="17.9688" clip-path="url(#clipPath3)" stroke="none"
>View kennt Control,</text
><text x="5" xml:space="preserve" y="33.9375" clip-path="url(#clipPath3)" stroke="none"
>Control aber nicht View</text
></g
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(180,470)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath4)" stroke="none"
/></g
><g transform="translate(180,470)"
><rect fill="none" x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath4)"
/><text x="19" font-size="30px" y="32.7917" clip-path="url(#clipPath4)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Model</text
></g
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(180,390)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath4)" stroke="none"
/></g
><g transform="translate(180,390)"
><rect fill="none" x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath4)"
/><text x="11" font-size="30px" y="32.7917" clip-path="url(#clipPath4)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Control</text
></g
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(180,310)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath4)" stroke="none"
/></g
><g transform="translate(180,310)"
><rect fill="none" x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath4)"
/><text x="27" font-size="30px" y="32.7917" clip-path="url(#clipPath4)" font-family="sans-serif" stroke="none" xml:space="preserve"
>View</text
></g
><g transform="translate(230,350)"
><path fill="none" d="M10.5 39.5 L10.5 10.5" clip-path="url(#clipPath5)"
/><path fill="none" d="M17 28.7417 L10.5 40 L4 28.7417" clip-path="url(#clipPath5)"
/></g
><g transform="translate(230,430)"
><path fill="none" d="M10.5 39.5 L10.5 10.5" clip-path="url(#clipPath5)"
/><path fill="none" d="M17 28.7417 L10.5 40 L4 28.7417" clip-path="url(#clipPath5)"
/></g
></g
></svg
>

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
P05_TicTacToe/Model.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

25
P05_TicTacToe/Model.svg Normal file
View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
<svg fill-opacity="1" xmlns:xlink="http://www.w3.org/1999/xlink" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" width="160" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="80 300 160 90" height="90" xmlns="http://www.w3.org/2000/svg" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto"
><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"
/><g
><defs id="defs1"
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"
><path d="M0 0 L2147483647 0 L2147483647 2147483647 L0 2147483647 L0 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"
><path d="M0 0 L0 50 L120 50 L120 0 Z"
/></clipPath
></defs
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(100,320)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(100,320)"
><rect fill="none" x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath2)"
/><text x="19" font-size="30px" y="32.7917" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Model</text
></g
></g
></svg
>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,272 @@
05 - SNP: TicTacToe
##############
.. image:: zhaw_neg_P2945.jpg
:width: 100px
:height: 100px
:scale: 25 %
:align: right
.. contents::
Übersicht
*********
In diesem Praktikum erweitern Sie einen Programm Rahmen zu einem funktionierenden TicTacToe Spiel. Bei TicTacToe legen zwei Spieler abwechselnd auf einem 3x3 Brett einen Stein, bis ein Spieler mit einer horizontalen, vertikalen oder diagonalen Linie gewinnt, oder alle Felder besetzt sind.
.. image:: TicTacToe.svg
In der Aufgabe implementieren Sie die fehlenden Funktionen bis alle Tests erfolgreich durchlau-fen. Die gewählte Vorgehensweise ist somit TDD Test-Driven-Development: es existieren zuerst Tests welche alle fehlschlagen. Schrittweise werden die Funktionen implementiert bis alle Tests erfolgreich durchlaufen.
Wenn die Tests erfolgreich durchlaufen, wird auch das Programm funktionieren und Sie können jemanden mit dem Spiel herausfordern 😉.
Lernziele
*********
In diesem Praktikum lernen Sie den Zugriff auf Arrays.
* Sie können anhand einer Beschreibung im Code die fehlenden Funktionen implementieren wo auf Arrays zugegriffen wird.
Die Bewertung dieses Praktikums ist am Ende angegeben.
Erweitern Sie die vorgegebenen Code Gerüste, welche im git Repository snp-lab-code verfügbar sind.
Aufgabe: TicTacToe
******************
Das zu ergänzende Programm tic-tac-toe hat folgende Funktionalität:
#. es stellt ein 3x3 TicTacToe Spielbrett auf dem Terminal dar
#. es liest von stdin eine Ziffer 0…9 ein, wobei 0 für Programm-Terminieren, die übrigen Ziffern für die Wahl eines Feldes stehen
#. der erste Spielzug wird von Spieler A geführt, danach wechselt das Programm zwischen den Spielern A und B
# bei Gewinn oder bei vollem Brett ist das Spiel vorbei
Wenn die Aufgabe erfolgreich umgesetzt ist, können Sie das Spiel ausführen:
```bash
bin/tic-tac-toe
```
Als Abnahme müssen die Tests unverändert ohne Fehler ausgeführt werden (``make test``).
Die Architektur des Programms folgt dem MVC Model-View-Control Paradigma. Dieses Paradigma besagt, dass die View (Eingabe und Darstellung) über Control (Vermittler) das Modell (die eigentliche Programm-Logik) steuert und darstellt. Dabei sind folgende Abhängigkeiten gegeben:
.. image:: MVC_pattern.svg
Test-Driven-Development
=======================
Das Programm besteht aus folgenden Files:
.. list-table::
* - Datei
- ToDo
* - Makefile
- -
* - tests/tests.c
- -
* - src/main.c
- -
* - src/view.h
- -
* - src/view.c
- -
* - src/control.h
- -
* - src/control.c
- -
* - src/model.h
- -
* - src/model.c
- siehe unten
1. Führen Sie ``make test`` aus::
Suite: lab test
Test: test_model_init
init_model:... 0/0 FAILED
1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
Test: test_model_get_state ...FAILED
1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
Test: test_model_get_winner ...FAILED
1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
Test: test_model_can_move ...FAILED
1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
Test: test_model_move ...FAILED
1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
Test: test_model_get_win_line ...FAILED
1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
Run Summary: Type Total Ran Passed Failed Inactive
suites 1 1 n/a 0 0
tests 6 6 0 6 0
asserts 6 6 0 6 n/a
2. Konzentrieren Sie sich auf den ersten Test der fehlschlägt. Dies ist ein Unit Test, welcher die Funktion **model_init()** prüft. Suchen Sie die Funktion in **src/model.h** und **src/model.c**.
Was ist die geforderte Funktionalität und wie ist sie implementiert?
Suchen Sie die darin aufgerufene **model_init()** Funktion und implementieren Sie diese.
.. code-block:: c
void model_init(model_t *instance) {
assert(instance);
// Instructions to the students:
// set all fields of the board to model_state_none
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE }
3. Führen Sie ``make test`` und korrigieren Sie obige Funktion, bis der Test nicht mehr fehlschlägt.
3.2 test_model_init
===================
Gehen Sie analog zur ersten Teilaufgabe vor:
1. Führen Sie ``make test`` aus.
2. Suchen Sie die Funktion **model_get_state()** in **model.h** und **model.c**.
3. Implementieren Sie die intern benutzte Funktion **get_state()** gemäss der Anleitung im Code.
.. code-block:: c
model_state_t model_get_state(model_t *instance, model_pos_t pos)
{
assert(instance);
assert_pos(pos);
// Instructions to the students:
// replace the stub implementation my access to the field at the given position.
// BEGIN-STUDENTS-TO-ADD-CODE
return model_state_none; // stub
// END-STUDENTS-TO-ADD-CODE
}
4. Führen Sie ``make test`` und korrigieren Sie, bis die beiden Tests nicht mehr fehlschlagen.
3.3 test_model_get_state test_model_get_winner
==============================================
Gehen Sie analog zur ersten Teilaufgabe vor:
1. Führen Sie ``make test`` aus.
2. Suchen Sie die Funktion **model_get_state()** in **model.h** und **model.c**.
3. Implementieren Sie die intern benutzte Funktion **get_state()** gemäss der Anleitung im Code.
.. code-block:: c
model_state_t model_get_state(model_t *instance, model_pos_t pos)
{
assert(instance);
assert_pos(pos);
// Instructions to the students:
// replace the stub implementation my access to the field at the given position.
// BEGIN-STUDENTS-TO-ADD-CODE
return model_state_none; // stub
// END-STUDENTS-TO-ADD-CODE
}
3.4 test_model_can_move
=======================
Gehen Sie analog den obigen Teilaufgaben vor und implementieren Sie, gemäss Vorgaben im Code, die Funktion **model_can_move()**.
.. code-block:: c
int model_can_move(model_t *instance){
assert(instance);
if (model_get_winner(instance) == model_state_none) {
// Instructions to the students:
// scan all fields: return 1 with first field which equals model_state_none
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE}
return 0;}
3.5 test_model_move test_model_get_win_line
===========================================
Schliesslich gehen Sie auch hier analog den obigen Teilaufgaben vor und implementieren Sie, gemäss Vorgaben im Code, die Funktion **set_state()**.
.. code-block:: c
/**
* @brief Sets the field on the board to the given state.
* @param instance [INOUT] The instance which holds the state.
* @param pos [IN] The affected field.
* @param state [IN] The new state of the field.
*/
static void set_state(model_t *instance, model_pos_t pos, model_state_t state)
{
assert_pos(pos);
// Instructions to the students:
// set the field of the board to the new state
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
}
Wenn die beiden obigen Teilaufgaben erfolgreich umgesetzt sind, laufen die Tests ohne Fehler durch und das Spiel kann gespielt werden.
4. Bewertung
******************
Die gegebenenfalls gestellten Theorieaufgaben und der funktionierende Programmcode müssen der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.
.. list-table::
:header-rows: 0
* - Aufgabe
- Kriterium
- Gewicht
* - TicTacToe
- Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären.
-
* - TicTacToe
- Teilaufgabe test_model_init
- 1
* - TicTacToe
- Teilaufgabe test_model_get_state und test_model_get_winner
- 1
* - TicTacToe
- Teilaufgabe test_model_can_move
- 1
* - TicTacToe
- Teilaufgabe test_model_move und test_model_get_win_line
- 1
Version: 15.02.2022

205
P05_TicTacToe/README.md Normal file
View File

@ -0,0 +1,205 @@
# 05 - Arrays/Strings/TicTacToe
___
## 1. Übersicht
In diesem Praktikum werden Sie in der ersten Aufgabe ein Programm zum Einlesen, Sortieren und Ausgeben von Strings von Grund auf entwickeln.
In der zweiten Aufgabe werden Sie einen Programmrahmen zu einem funktionierenden TicTacToe-Spiel erweitern. Sie implementieren hierbei die fehlenden Funktionen bis alle Tests erfolgreich durchlaufen. Die gewählte Vorgehensweise entspricht somit Test-Driven-Development (TDD). D.h. es existieren zuerst Tests, welche alle fehlschlagen. Schrittweise werden die Funktionen implementiert bis alle Tests erfolgreich durchlaufen. Wenn die Tests erfolgreich durchlaufen, wird auch das Programm funktionieren.
___
## 2. Lernziele
In diesem Praktikum schreiben Sie selbst von Grund auf ein C-Programme, das mit Strings operiert. Ferner ergänzen Sie ein bestehendes und lernen dabei den Zugriff auf Arrays.
* Sie können mit Arrays von Strings umgehen.
* Sie können String-Funktionen aus der Standard Library verwenden.
* Sie können anhand einer Beschreibung im Code die fehlenden Funktionen die auf Arrays zugreifen implementieren.
___
## 3. Aufgabe 1: Sortieren von Strings
Schreiben Sie ein C-Programm, das bis zu 10 Wörter mit einer maximalen Länge von jeweils 20 char von der Tastatur einliest, diese in Grossbuchstaben umwandelt, in einem Array der Reihe nach ablegt und zum Schluss im Array alphabetisch sortiert und ausgibt. Wiederholt eingegebene Wörter sollen dabei ignoriert werden. Das Ende der Eingabe soll durch das Erreichen der zehn unterschiedlichen Wörter oder durch die Eingabe von „ZZZ“ erfolgen. Die Ausgabe der sortierten Wörter soll direkt nach Beendigung der Eingabe erfolgen.
Hinweise:
- Zur Speicherung der Wörter sollten Sie ein zweidimensionales Array verwenden.
- Verwenden Sie die String-Funktionen der C Standard Library (include <string.h>), z.B. um Strings alphabetisch zu vergleichen.
- Wenn Sie aus anderen Vorlesungen bereits einen effizienten Sortieralgorithmus kennen, können Sie diesen natürlich verwenden. Sonst erfinden Sie einfach einen eigenen.
- Strukturieren Sie das Programm durch geeignete Funktionen.
___
## 4. Aufgabe 2: TicTacToe
Das zu ergänzende Programm tic-tac-toe hat folgende Funktionalität:
1. es stellt ein 3x3 TicTacToe Spielbrett auf dem Terminal dar
2. es liest von stdin eine Ziffer 0…9 ein, wobei 0 für Programm-Terminieren, die übrigen Ziffern für die Wahl eines Feldes stehen
3. der erste Spielzug wird von Spieler A geführt, danach wechselt das Programm zwischen den Spielern A und B
4. bei Gewinn oder bei vollem Brett ist das Spiel vorbei
Erweitern Sie die vorgegebenen Code Gerüste, welche im git Repository snp-lab-code verfügbar sind.
Wenn die Aufgabe erfolgreich umgesetzt ist, können Sie das Spiel ausführen:
```bash
bin/tic-tac-toe
```
![](./TicTacToe.png)
Als Abnahme müssen die Tests unverändert ohne Fehler ausgeführt werden (`make test`).
Die Architektur des Programms folgt dem MVC Model-View-Control Paradigma. Dieses Paradigma besagt, dass die View (Eingabe und Darstellung) über Control (Vermittler) das Modell (die eigentliche Programm-Logik) steuert und darstellt. Dabei sind folgende Abhängigkeiten gegeben:
![](./MVC_pattern.png)
___
### 4.1 Teilaufgabe test_model_init
Das Programm besteht aus folgenden Files:
| Datei | ToDo |
| :-- | :-- |
|Makefile | -> gegeben, d.h. nichts anzupassen |
|tests/tests.c| -> gegeben, d.h. nichts anzupassen |
|src/main.c| -> gegeben, d.h. nichts anzupassen |
|src/view.h| -> gegeben, d.h. nichts anzupassen |
|src/view.c| -> gegeben, d.h. nichts anzupassen |
|src/control.h| -> gegeben, d.h. nichts anzupassen |
|src/control.c| -> gegeben, d.h. nichts anzupassen |
|src/model.h| -> gegeben, d.h. nichts anzupassen |
|src/model.c| -> **anzupassen:** umsetzen gemäss den Angaben unten |
1. Führen Sie `make test` aus
```bash
Suite: lab test
Test: test_model_init ...
init_model:... 0/0 FAILED
1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
Test: test_model_get_state ...FAILED
1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
Test: test_model_get_winner ...FAILED
1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
Test: test_model_can_move ...FAILED
1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
Test: test_model_move ...FAILED
1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
Test: test_model_get_win_line ...FAILED
1. tests/tests.c:62 - CU_ASSERT_EQUAL_FATAL(instance->board[row][col],model_state_none)
Run Summary: Type Total Ran Passed Failed Inactive
suites 1 1 n/a 0 0
tests 6 6 0 6 0
asserts 6 6 0 6 n/a
```
2. Konzentrieren Sie sich auf den ersten Test der fehlschlägt. Dies ist ein Unit Test, welcher die Funktion **model_init()** prüft. Suchen Sie die Funktion in **src/model.h** und **src/model.c**.
3. Was ist die geforderte Funktionalität und wie ist sie implementiert?
Suchen Sie die darin aufgerufene **model_init()** Funktion und implementieren Sie diese.
```c
void model_init(model_t *instance)
{
assert(instance);
// Instructions to the students:
// set all fields of the board to model_state_none
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
}
```
3. Führen Sie `make test` und korrigieren Sie obige Funktion, bis der Test nicht mehr fehlschlägt.
___
### 4.2 Teilaufgabe test_model_get_state und test_model_get_winner
Gehen Sie analog zur ersten Teilaufgabe vor:
1. Führen Sie `make test` aus.
2. Suchen Sie die Funktion **model_get_state()** in **model.h** und **model.c**.
3. Implementieren Sie die intern benutzte Funktion **get_state()** gemäss der Anleitung im Code.
```c
model_state_t model_get_state(model_t *instance, model_pos_t pos)
{
assert(instance);
assert_pos(pos);
// Instructions to the students:
// replace the stub implementation my access to the field at the given position.
// BEGIN-STUDENTS-TO-ADD-CODE
return model_state_none; // stub
// END-STUDENTS-TO-ADD-CODE
}
```
___
### 4.3 Teilaufgabe test_model_can_move
Gehen Sie analog den obigen Teilaufgaben vor und implementieren Sie, gemäss Vorgaben im Code, die Funktion **model_can_move()**.
```c
int model_can_move(model_t *instance)
{
assert(instance);
if (model_get_winner(instance) == model_state_none) {
// Instructions to the students:
// scan all fields: return 1 with first field which equals model_state_none
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
}
return 0;
}
```
___
### 4.4 Teilaufgabe test_model_move und test_model_get_win_line
Schliesslich gehen Sie auch hier analog den obigen Teilaufgaben vor und implementieren Sie, gemäss Vorgaben im Code, die Funktion **set_state()**.
```c
/**
* @brief Sets the field on the board to the given state.
* @param instance [INOUT] The instance which holds the state.
* @param pos [IN] The affected field.
* @param state [IN] The new state of the field.
*/
static void set_state(model_t *instance, model_pos_t pos, model_state_t state)
{
assert_pos(pos);
// Instructions to the students:
// set the field of the board to the new state
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
}
```
Wenn die beiden obigen Teilaufgaben erfolgreich umgesetzt sind, laufen die Tests ohne Fehler durch und das Spiel kann gespielt werden.
___
## 5. Bewertung
Der funktionierende Programmcode muss der Praktikumsbetreuung gezeigt werden. Die Lösungen müssen mündlich erklärt werden.
| Aufgabe | Kriterium | Punkte |
| :-- | :-- | :-- |
| Sortieren von Strings | Sie können das funktionierende Programm demonstrieren und erklären. | 2 |
| TicTacToe | Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. | |
| TicTacToe | Teilaufgabe test_model_init | 0.5 |
| TicTacToe | Teilaufgabe test_model_get_state und test_model_get_winner | 0.5 |
| TicTacToe | Teilaufgabe test_model_can_move | 0.5 |
| TicTacToe | Teilaufgabe test_model_move und test_model_get_win_line | 0.5 |
___
Version: 14.02.2022

BIN
P05_TicTacToe/TicTacToe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 34 KiB

BIN
P05_TicTacToe/View.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

32
P05_TicTacToe/View.svg Normal file
View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
<svg fill-opacity="1" xmlns:xlink="http://www.w3.org/1999/xlink" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" width="160" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="80 120 160 140" height="140" xmlns="http://www.w3.org/2000/svg" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto"
><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"
/><g
><defs id="defs1"
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"
><path d="M0 0 L2147483647 0 L2147483647 2147483647 L0 2147483647 L0 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"
><path d="M0 0 L0 50 L120 50 L120 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath3"
><path d="M0 0 L0 60 L30 60 L30 0 Z"
/></clipPath
></defs
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(100,140)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(100,140)"
><rect fill="none" x="0.5" width="118.5" height="48.5" y="0.5" clip-path="url(#clipPath2)"
/><text x="27" font-size="30px" y="32.7917" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>View</text
></g
><g transform="translate(150,180)"
><path fill="none" d="M10.5 39.5 L10.5 10.5" clip-path="url(#clipPath3)"
/><path fill="none" d="M17 28.7417 L10.5 40 L4 28.7417" clip-path="url(#clipPath3)"
/></g
></g
></svg
>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,9 @@
SNP_SHARED_MAKEFILE := $(if $(SNP_SHARED_MAKEFILE),$(SNP_SHARED_MAKEFILE),"~/snp/shared.mk")
TARGET := bin/tic-tac-toe
MODULES := src/model.c src/view.c src/control.c
SOURCES := src/main.c $(MODULES)
TSTSOURCES := tests/tests.c $(MODULES)
include $(SNP_SHARED_MAKEFILE)

View File

@ -0,0 +1,8 @@
/**
* @mainpage SNP - P05 Tic Tac Toe Game
*
* @section Purpose
*
* This is a lab on usage of arrays.
*
*/

View File

@ -0,0 +1,154 @@
/**
* @file
* @brief Implementation
*/
#include "control.h"
#include "model.h"
#include <assert.h>
/**
* @brief Conversion from control field number (1...9) to 0-based model position (row, col).
* @param cell [IN] Field number (1...9).
* @return Returns the position of the field.
* @remark Asserts proper field range.
*/
static model_pos_t get_pos(size_t cell)
{
assert(1 <= cell && cell <= 9);
model_pos_t pos = { (cell - 1) / 3, (cell - 1) % 3 };
return pos;
}
/**
* @brief Conversion from control player to model state.
* @param player [IN] Control player value to convert.
* @return Returns the matching model state.
* @remark No assertion is done - defaults to model_state_none.
*/
static model_state_t get_state(control_player_t player)
{
switch(player) {
case control_player_a: return model_state_a;
case control_player_b: return model_state_b;
default: return model_state_none;
}
}
/**
* @brief Conversion from 0-based model position (row, col) to control field number (1...9).
* @param pos [IN] 0-based model position (row,col).
* @return The control filed number (1...9)
* @remark Asserts proper position range.
*/
static size_t get_cell(model_pos_t pos)
{
assert(pos.row < 3);
assert(pos.col < 3);
return 1 + pos.row * 3 + pos.col;
}
/**
* @brief Conversion from model state to control player.
* @param state [IN] Model state to convert
* @return Returns the matching control player value.
* @remark No assertion is done - defaults to control_no_player.
*/
static control_player_t get_player(model_state_t state)
{
switch(state) {
case model_state_a: return control_player_a;
case model_state_b: return control_player_b;
default: return control_no_player;
}
}
/**
* @brief Queries if a move is possible.
* @param instance [INOUT] The instance which holds the state.
* @return Returns 0 if no move is possible any more, otherwise 1.
*/
static int control_can_move(control_t *instance)
{
assert(instance);
return model_can_move(instance->model);
}
// public API function which is documented in the header file.
void control_init(control_t *instance, model_t *model)
{
assert(instance);
assert(model);
instance->player = control_player_a;
instance->model = model;
}
// public API function which is documented in the header file.
void control_move(control_t *instance, size_t cell)
{
assert(instance);
if (model_move(instance->model, get_pos(cell), get_state(instance->player))) {
if (control_can_move(instance)) {
switch(instance->player) {
case control_player_a:
instance->player = control_player_b;
break;
case control_player_b:
instance->player = control_player_a;
break;
default:
break;
}
} else {
instance->player = control_no_player;
}
}
}
// public API function which is documented in the header file.
control_player_t control_get_winner(control_t *instance)
{
assert(instance);
return get_player(model_get_winner(instance->model));
}
// public API function which is documented in the header file.
control_player_t control_get_player(control_t *instance)
{
assert(instance);
return instance->player;
}
// public API function which is documented in the header file.
control_player_t control_get_state(control_t *instance, size_t cell)
{
assert(instance);
return get_player(model_get_state(instance->model, get_pos(cell)));
}
// public API function which is documented in the header file.
control_line_t control_get_win(control_t *instance)
{
assert(instance);
if (control_get_winner(instance) == control_no_player) {
control_line_t no_win = { 0 };
return no_win;
}
model_line_t line = model_get_win_line(instance->model);
assert(line.dir != model_dir_none);
size_t start_cell = get_cell(line.start);
switch(line.dir) {
case model_dir_h:
return (control_line_t) { { start_cell, start_cell + 1, start_cell + 2 } };
case model_dir_v:
return (control_line_t) { { start_cell, start_cell + 3, start_cell + 6 } };
case model_dir_d:
if (start_cell == 1) {
return (control_line_t) { { start_cell, start_cell + 4, start_cell + 8 } };
} else {
return (control_line_t) { { start_cell, start_cell + 2, start_cell + 6 } };
}
default:
return (control_line_t) { { 1, 1, 1 } };
}
}

View File

@ -0,0 +1,80 @@
/**
* @file
* @brief MVC - agent between model and view
*/
#ifndef _CONTROL_H_
#define _CONTROL_H_
#include "model.h"
/**
* @brief The selection of possible players.
*/
typedef enum {
control_no_player, ///< none of the players
control_player_a, ///< first player
control_player_b, ///< second player
} control_player_t;
/**
* @brief Sequence of winning cell numbers in increasing cell numbers.
*/
typedef struct {
size_t line[3]; ///< the sequence of cells (1...9) or 0 in the first element if no win
} control_line_t;
/**
* @brief The instance type.
*/
typedef struct {
control_player_t player; ///< the current player
model_t *model; ///< the reference to the model
} control_t;
/**
* @brief Constructor: initialize the instance memory.
* @param instance [INOUT] The instance which holds the state.
* @param model [IN] Dependency Injection of the model instance.
*/
void control_init(control_t *instance, model_t *model);
/**
* @brief Performs a move on the board.
* @param instance [INOUT] The instance which holds the state.
* @param cell [IN] The affected field (1...9)
* @remark Silently ignores a move if it is not allowed (e.g. if already completed or the field is already played, etc.).
*/
void control_move(control_t *instance, size_t cell);
/**
* @brief Queries the winning player.
* @param instance [INOUT] The instance which holds the state.
* @returns Returns the winning player (if any).
*/
control_player_t control_get_winner(control_t *instance);
/**
* @brief Queries the next player.
* @param instance [INOUT] The instance which holds the state.
* @returns Returns the next player (if any).
* @remark This is updated by the control_move() function.
*/
control_player_t control_get_player(control_t *instance);
/**
* @brief Queries the state of a field.
* @param instance [INOUT] The instance which holds the state.
* @param cell [IN] The affected field of the board (1...9).
* @returns Returns the player which played this field (if any).
*/
control_player_t control_get_state(control_t *instance, size_t cell);
/**
* @brief Gets the winning fields (if any).
* @param instance [INOUT] The instance which holds the state.
* @returns Returns the field numbers in increasing order (1...9) which win the game (if any).
* @remark If there is no winner (yet), the first entry in the result is 0.
*/
control_line_t control_get_win(control_t *instance);
#endif // _CONTROL_H_

View File

@ -0,0 +1,39 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab P04 dep2dot
*/
#include <stdio.h>
#include <stdlib.h>
#include "view.h"
#include "model.h"
#include "control.h"
/**
* @brief main function
* @param argc [in] number of entries in argv
* @param argv [in] program name plus command line arguments
* @returns returns success if valid date is given, failure otherwise
*/
int main(int argc, const char *argv[])
{
view_t view;
control_t control;
model_t model;
model_init(&model);
control_init(&control, &model);
view_init(&view, &control);
view_run(&view);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,165 @@
/**
* @file
* @brief Implementation
*/
#include "model.h"
#include <assert.h>
/**
* @brief Asserts that the position is in range.
* @param [IN] The position to check.
*/
static void assert_pos(model_pos_t pos)
{
assert(pos.row < MODEL_SIZE);
assert(pos.col < MODEL_SIZE);
}
/**
* @brief Sets the field on the board to the given state.
* @param instance [INOUT] The instance which holds the state.
* @param pos [IN] The affected field.
* @param state [IN] The new state of the field.
*/
static void set_state(model_t *instance, model_pos_t pos, model_state_t state)
{
assert_pos(pos);
// Instructions to the students:
// set the field of the board to the new state
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
}
// public API function which is documented in the header file.
model_pos_t model_pos(size_t row, size_t col)
{
return (model_pos_t){row, col};
}
// public API function which is documented in the header file.
void model_init(model_t *instance)
{
assert(instance);
// Instructions to the students:
// set all fields of the board to model_state_none
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
}
// public API function which is documented in the header file.
model_state_t model_get_state(model_t *instance, model_pos_t pos)
{
assert(instance);
assert_pos(pos);
// Instructions to the students:
// replace the stub implementation my access to the field at the given position.
// BEGIN-STUDENTS-TO-ADD-CODE
return model_state_none; // stub
// END-STUDENTS-TO-ADD-CODE
}
// public API function which is documented in the header file.
model_line_t model_get_win_line(model_t *instance)
{
assert(instance);
model_state_t anchor;
// horizontal
for(size_t row = 0; row < MODEL_SIZE; row++) {
anchor = model_get_state(instance, model_pos(row, 0));
if (anchor != model_state_none
&& anchor == model_get_state(instance, model_pos(row, 1))
&& anchor == model_get_state(instance, model_pos(row, 2))) {
return (model_line_t) { model_dir_h, { row, 0 } };
}
}
// vertical
for(size_t col = 0; col < MODEL_SIZE; col++) {
anchor = model_get_state(instance, model_pos(0, col));
if (anchor != model_state_none
&& anchor == model_get_state(instance, model_pos(1, col))
&& anchor == model_get_state(instance, model_pos(2, col))) {
return (model_line_t) { model_dir_v, { 0, col } };
}
}
// diagonal
anchor = model_get_state(instance, model_pos(1, 1));
if (anchor != model_state_none) {
if (anchor == model_get_state(instance, model_pos(0, 0)) && anchor == model_get_state(instance, model_pos(2, 2))) {
return (model_line_t) { model_dir_d, { 0, 0 } };
}
if (anchor == model_get_state(instance, model_pos(2, 0)) && anchor == model_get_state(instance, model_pos(0, 2))) {
return (model_line_t) { model_dir_d, { 0, 2 } };
}
}
// fallback
return (model_line_t) { model_dir_none, { 0, 0 } };
}
// public API function which is documented in the header file.
model_state_t model_get_winner(model_t *instance)
{
assert(instance);
model_line_t line = model_get_win_line(instance);
return line.dir == model_dir_none
? model_state_none
: model_get_state(instance, model_pos(line.start.row, line.start.col))
;
}
// public API function which is documented in the header file.
int model_can_move(model_t *instance)
{
assert(instance);
if (model_get_winner(instance) == model_state_none) {
// Instructions to the students:
// scan all fields: return 1 with first field which equals model_state_none
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
}
return 0;
}
// public API function which is documented in the header file.
int model_move(model_t *instance, model_pos_t pos, model_state_t state)
{
assert(instance);
assert_pos(pos);
if (model_get_state(instance, pos) == model_state_none && model_can_move(instance)) {
set_state(instance, pos, state);
return 1;
}
return 0;
}

View File

@ -0,0 +1,108 @@
/**
* @file
* @brief MVC - Model instance
*/
#ifndef _MODEL_H_
#define _MODEL_H_
#include <stdlib.h>
#define MODEL_SIZE 3 ///< size of the game to avoid magic numbers in the code (not meant to modify)
/**
* @brief The position on the board.
*/
typedef struct {
size_t row; ///< The row (0-based).
size_t col; ///< The column (0-based).
} model_pos_t;
/**
* @brief Winner line direction - the winner line is given together with the start position.
*/
typedef enum {
model_dir_none, ///< no winner line
model_dir_h, ///< horizontal
model_dir_v, ///< vertical
model_dir_d, ///< diagonal
} model_dir_t;
/**
* @brief The Winner line (if any).
*/
typedef struct {
model_dir_t dir; ///< the winner line direction (if any)
model_pos_t start; ///< the start position of the winner line
} model_line_t;
/**
* @brief The state of a field.
*/
typedef enum {
model_state_none, ///< field available to play
model_state_a, ///< field already played
model_state_b, ///< field already played
} model_state_t;
/**
* @brief The instance type.
*/
typedef struct {
model_state_t board[MODEL_SIZE][MODEL_SIZE]; ///< the play board
} model_t;
/**
* @brief Convert to row and col to position.
* @param row [IN] position parameter
* @param col [IN] position parameter
* @return Returns the position given be row and col parameters.
*/
model_pos_t model_pos(size_t row, size_t col);
/**
* @brief Constructor: initialize the instance memory.
* @param instance [INOUT] The instance which holds the state.
*/
void model_init(model_t *instance);
/**
* @brief Queries the state of the given field.
* @param instance [INOUT] The instance which holds the state.
* @param pos [IN] The affected field.
* @return Returns the state of the field.
*/
model_state_t model_get_state(model_t *instance, model_pos_t pos);
/**
* @brief Queries the winner (if any).
* @param instance [INOUT] The instance which holds the state.
* @return Returns the wining player or model_state_none if no winner (yet).
*/
model_state_t model_get_winner(model_t *instance);
/**
* @brief Queries if a move is possible (i.e. not won yet and any field available?).
* @param instance [INOUT] The instance which holds the state.
* @return Returns 0 if no move possible, 1 otherwise.
*/
int model_can_move(model_t *instance);
/**
* @brief Do a move if possible.
* @param instance [INOUT] The instance which holds the state.
* @param pos [IN] The field to play.
* @param state [IN] The new state (only model_state_a and model_state_b allowed).
* @return Returns if the attempt to move was successful.
* @remark Does only succeed if not yet won and if the field is available.
*/
int model_move(model_t *instance, model_pos_t pos, model_state_t state);
/**
* @brief Gets the winner line (if any).
* @param instance [INOUT] The instance which holds the state.
* @returns The line which wins (if any).
* @remark The start position is 0/0, 1/0, 2/0 for horizontal, 0/0, 0/1, 0/2 for vertical, 0/0, 0/2 for diagonal.
*/
model_line_t model_get_win_line(model_t *instance);
#endif // _MODEL_H_

View File

@ -0,0 +1,255 @@
/**
* @file
* @brief Implementation
*/
#include "view.h"
#include "control.h"
#include <assert.h> // assert()
#include <stdio.h> // various i/o
#include <ctype.h> // isdigit()
#include <unistd.h> // STDIN_FILENO, isatty()
#include <termios.h> // tcgetattr(), tcsetattr()
#define EXIT '0' ///< the UI exit request
#define CLS "\033[2J" ///< ANSI termial CSI sequence for clear screen
#define AVAILABLE "\033[40m" ///< ANSI termial CSI sequence for available fields (black)
#define PLAYER_A "\033[42m" ///< ANSI termial CSI sequence for one player (green)
#define PLAYER_B "\033[41m" ///< ANSI termial CSI sequence for the other player (red)
#define GAP "\033[47m" ///< ANSI termial CSI sequence for boarder (white)
#define RESET "\033[0m" ///< ANSI termial CSI sequence to reset all settings
#define CELL_WIDTH 10 ///< rendering parameter: columns per cell
#define CELL_HEIGHT 5 ///< rendering parameter: rows per cell
#define GAP_WIDTH 4 ///< rendering parameter: columns per border
#define GAP_HEIGHT 2 ///< rendering parameter: rows per boarder
#define SIZE 3 ///< size of the game to avoid magic numbers in the code (not meant to modify)
#define CELLS (SIZE * SIZE) ///< size of the game to avoid magic numbers in the code (not meant to modify)
/**
* @brief Position the cursor for further output.
* @param row [IN] position parameter
* @param col [IN] position parameter
*/
static void goto_pos(size_t row, size_t col)
{
printf("\033[%zd;%zdH", row, col);
}
/**
* @brief Displays a sequence of spaces at the given position in the given background color.
* @param row [IN] position parameter
* @param col [IN] position parameter
* @param width [IN] how many spaces to write
* @param color [IN] the format string before writing the spaces (intent: background color)
* @remark After writing the spaces, the format is reset.
*/
static size_t show_bar(size_t row, size_t col, size_t width, const char* color)
{
goto_pos(row, col);
printf("%s", color);
for(size_t col = 0; col < width; col++) {
putchar(' ');
}
printf(RESET);
return col + width;
}
/**
* @brief Displays a horizontal border over the whole board width.
* @param row [IN] position parameter
* @param col [IN] position parameter
*/
static size_t show_h_gap(size_t row, size_t col) {
for(size_t i = 0; i < GAP_HEIGHT; i++) {
show_bar(row+i, col, GAP_WIDTH + CELL_WIDTH + GAP_WIDTH, GAP);
}
return row + GAP_HEIGHT;
}
/**
* @brief Writes for the call at position y/x the given number with the given background color.
* @param y [IN] position parameter: the upper left row of the cell
* @param x [IN] position parameter: the upper left column of the cell
* @param n [IN] the number to write as text
* @param color [IN] the format string before writing the text (intent: background color)
* @remark After writing the number, the format is reset.
*/
static void show_cell_nr(size_t y, size_t x, size_t n, const char *color)
{
size_t cy = (y + y + CELL_HEIGHT)/2;
size_t cx = (x + x + CELL_WIDTH - 2)/2;
goto_pos(cy, cx);
printf("%s", color);
printf("%2zd", n);
printf(RESET);
}
/**
* @brief Renders the given cell with the given background color, including the surrounding border.
* @param n [IN] the cell number (0...CELLS-1)
* @param color [IN] the format string for the cell content (intent: background color)
* @remark After writing the number, the format is reset.
*/
static void show_cell(size_t n, const char *color)
{
// goto upper-left corner of a cell (the cell starts with an upper and left gap)
size_t y = 1 + n / SIZE * (GAP_HEIGHT + CELL_HEIGHT);
size_t x = 1 + n % SIZE * (GAP_WIDTH + CELL_WIDTH);
size_t row = show_h_gap(y, x);
for(size_t i = 0; i < CELL_HEIGHT; i++) {
size_t col = x;
col = show_bar(row, col, GAP_WIDTH, GAP);
col = show_bar(row, col, CELL_WIDTH, color);
col = show_bar(row, col, GAP_WIDTH, GAP);
row++;
}
row = show_h_gap(row, x);
show_cell_nr(y + GAP_HEIGHT, x + GAP_WIDTH, n + 1, color);
goto_pos(row, 0);
}
/**
* @brief Renders the given player's name in the given background color.
* @param player [IN] the player to render (select the background color and the name of the player
* @remark After writing the content, the format is reset.
*/
static void print_player(control_player_t player)
{
switch(player) {
case control_player_a:
printf(PLAYER_A);
printf("Player A");
printf(RESET);
break;
case control_player_b:
printf(PLAYER_B);
printf("Player B");
printf(RESET);
break;
default:
printf(RESET);
printf("none");
break;
}
}
/**
* @brief Displays a label followed by the given player.
* @param row [IN] position parameter
* @param col [IN] position parameter
* @param label [IN] the profixing label
* @param player [IN] the player to display
*/
static void show_player(size_t row, size_t col, const char *label, control_player_t player)
{
goto_pos(row, col);
printf(RESET);
col += printf("%s", label);
goto_pos(row, col);
print_player(player);
}
/**
* @brief Renders the winner and the next player.
* @param winner [IN] the winning player (if any)
* @param next [IN] the next player (if any)
*/
static void show_status(control_player_t winner, control_player_t next)
{
size_t y = GAP_HEIGHT;
size_t x = SIZE * (GAP_WIDTH + CELL_WIDTH) + GAP_WIDTH + GAP_WIDTH;
size_t row = y;
size_t col = x;
show_player(row, col, "Winner is: ", winner);
row += 2;
show_player(row, col, "Next player is: ", next);
row += 2;
row += 2;
goto_pos(row, col);
printf("0: exit");
row += 2;
goto_pos(row, col);
printf("1..9: play field");
}
/**
* @brief Renders the board from the status given by the control instance.
* @param instance [IN] the instance which holds the control instance
*/
static void show(view_t *instance)
{
assert(instance);
assert(instance->control);
puts(CLS);
show_status(control_get_winner(instance->control), control_get_player(instance->control));
for(size_t i = 0; i < CELLS; i++) {
const char *color = AVAILABLE;
switch(control_get_state(instance->control, i+1)) {
case control_player_a:
color = PLAYER_A;
break;
case control_player_b:
color = PLAYER_B;
break;
default:
break;
}
show_cell(i, color);
}
}
/**
* @brief Processes the input and dsiplays the result.
* @param the instance which holds the control instance
*/
static void notifier_loop(view_t *instance)
{
show(instance);
int c = getchar();
while(c != EOF && c != EXIT) {
if (isdigit(c)) {
control_move(instance->control, c-'0');
}
show(instance);
c = getchar();
}
}
// public API function which is documented in the header file.
void view_init(view_t *instance, control_t *control)
{
assert(instance);
assert(control);
instance->control = control;
}
// public API function which is documented in the header file.
void view_run(view_t *instance)
{
if (isatty(STDIN_FILENO)) { // in case of an interactive terminal, the exhoing and buffering is disabled
// declare non POSIX function, which is available in glibc, but not in strinct C99 mode
void cfmakeraw(struct termios *termios_p);
// replace original tty IO state...
struct termios orig;
struct termios raw;
cfmakeraw(&raw);
tcgetattr(STDIN_FILENO, &orig);
tcsetattr(STDIN_FILENO, TCSANOW, &raw);
// ...do the work...
notifier_loop(instance);
// ...and finalle restore original tty IO state
tcsetattr(STDIN_FILENO, TCSANOW, &orig);
} else { // if not an interactive terminal, no tweaking with IO is done
notifier_loop(instance);
}
}

View File

@ -0,0 +1,31 @@
/**
* @file
* @brief MVC - View instance
*/
#ifndef _VIEW_H_
#define _VIEW_H_
#include "control.h"
/**
* @brief The instance type.
*/
typedef struct {
control_t *control; ///< the instance knows of the control instance
} view_t;
/**
* @brief Constructor: initialize the instance memory.
* @param instance [INOUT] The instance which holds the state.
* @param control [IN] Dependency Injection of the control instance.
*/
void view_init(view_t *instance, control_t *control);
/**
* @brief Starts the notifyer loop: accepts input and displays the results.
* @param instance [INOUT] The instance which holds the state.
* @remark Does only return when termination is requested through the UI.
*/
void view_run(view_t *instance);
#endif // _VIEW_H_

View File

@ -0,0 +1,445 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Test suite for the given package.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <time.h>
#include <assert.h>
#include <CUnit/Basic.h>
#include "test_utils.h"
#include "model.h"
#ifndef TARGET // must be given by the make file --> see test target
#error missing TARGET define
#endif
/// @brief alias for EXIT_SUCCESS
#define OK EXIT_SUCCESS
/// @brief alias for EXIT_FAILURE
#define FAIL EXIT_FAILURE
/// @brief The name of the STDOUT text file.
#define OUTFILE "stdout.txt"
/// @brief The name of the STDERR text file.
#define ERRFILE "stderr.txt"
#define TRACE_INDENT "\n " ///< allow for better stdout formatting in case of error
// setup & cleanup
static int setup(void)
{
remove_file_if_exists(OUTFILE);
remove_file_if_exists(ERRFILE);
return 0; // success
}
static int teardown(void)
{
// Do nothing.
// Especially: do not remove result files - they are removed in int setup(void) *before* running a test.
return 0; // success
}
// test utils
static void init_model(model_t *instance, int act)
{
if (act) printf(TRACE_INDENT "init_model:... ");
model_init(instance);
for(size_t row = 0; row < MODEL_SIZE; row++) {
for(size_t col = 0; col < MODEL_SIZE; col++) {
if (act) printf("%zd/%zd ", row, col);
CU_ASSERT_EQUAL_FATAL(instance->board[row][col], model_state_none);
}
}
if (act) printf(TRACE_INDENT);
}
static void print_board(model_state_t board[MODEL_SIZE][MODEL_SIZE])
{
for(size_t row = 0; row < MODEL_SIZE; row++) {
printf("{ ");
for(size_t col = 0; col < MODEL_SIZE; col++) {
printf("%d ", board[row][col]);
}
printf("} ");
}
}
// tests
static void test_model_init(void)
{
// check void model_init(model_t *instance);
// arrange
model_t model;
// act & assert
init_model(&model, 1);
}
static void test_model_get_state(void)
{
// check: model_state_t model_get_state(model_t *instance, model_pos_t pos);
{
// arrange
model_t model;
init_model(&model, 0);
// act & assert
printf(TRACE_INDENT "initial state:... ");
print_board(model.board);
for(size_t row = 0; row < MODEL_SIZE; row++) {
for(size_t col = 0; col < MODEL_SIZE; col++) {
printf("%zd/%zd ", row, col);
CU_ASSERT_EQUAL_FATAL(model_get_state(&model, model_pos(row, col)), model_state_none);
}
}
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_none, model_state_a, model_state_b },
{ model_state_a, model_state_b, model_state_none },
{ model_state_b, model_state_none, model_state_a },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "modified state:... ");
print_board(model.board);
for(size_t row = 0; row < MODEL_SIZE; row++) {
for(size_t col = 0; col < MODEL_SIZE; col++) {
printf("%zd/%zd ", row, col);
CU_ASSERT_EQUAL_FATAL(model_get_state(&model, model_pos(row, col)), board[row][col]);
}
}
}
printf(TRACE_INDENT);
}
static void test_model_get_winner(void)
{
// check: model_state_t model_get_winner(model_t *instance);
{
// arrange
model_t model;
init_model(&model, 0);
// act & assert
printf(TRACE_INDENT "initial no winner:... ");
print_board(model.board);
CU_ASSERT_EQUAL_FATAL(model_get_winner(&model), model_state_none);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_none, model_state_a, model_state_b },
{ model_state_a, model_state_b, model_state_none },
{ model_state_b, model_state_none, model_state_a },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "winner:... ");
print_board(model.board);
CU_ASSERT_EQUAL_FATAL(model_get_winner(&model), model_state_b);
}
printf(TRACE_INDENT);
}
static void test_model_can_move(void)
{
// check: int model_can_move(model_t *instance);
{
// arrange
model_t model;
init_model(&model, 0);
// act & assert
printf(TRACE_INDENT "initial can move:... ");
print_board(model.board);
CU_ASSERT_EQUAL_FATAL(model_can_move(&model), 1);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_none, model_state_a, model_state_a },
{ model_state_a, model_state_b, model_state_none },
{ model_state_b, model_state_none, model_state_b },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "can move while not yet done nor win:... ");
print_board(model.board);
CU_ASSERT_EQUAL_FATAL(model_can_move(&model), 1);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_none, model_state_a, model_state_b },
{ model_state_a, model_state_b, model_state_none },
{ model_state_b, model_state_none, model_state_a },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "cannot move after win:... ");
print_board(model.board);
CU_ASSERT_EQUAL_FATAL(model_can_move(&model), 0);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_b, model_state_a, model_state_a },
{ model_state_a, model_state_b, model_state_b },
{ model_state_b, model_state_a, model_state_a },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "cannot move when all done:... ");
print_board(model.board);
CU_ASSERT_EQUAL_FATAL(model_can_move(&model), 0);
}
printf(TRACE_INDENT);
}
static void test_model_move(void)
{
// check: int model_move(model_t *instance, model_pos_t pos, model_state_t state);
{
// arrange
model_t model;
init_model(&model, 0);
// act & assert
printf(TRACE_INDENT "initial move:... ");
print_board(model.board);
model_pos_t pos_a = model_pos(0, 0);
printf("%zd/%zd ", pos_a.row, pos_a.col);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_a, model_state_a), 1);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_a, model_state_a), 0);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_a, model_state_b), 0);
model_pos_t pos_b = model_pos(2, 2);
printf("%zd/%zd ", pos_b.row, pos_b.col);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_b, model_state_b), 1);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_b, model_state_b), 0);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_b, model_state_a), 0);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_none, model_state_a, model_state_a },
{ model_state_a, model_state_b, model_state_none },
{ model_state_b, model_state_none, model_state_b },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "can move while not yet done nor win:... ");
print_board(model.board);
model_pos_t pos = model_pos(2, 1);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_a), 1);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_a), 0);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_b), 0);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_none, model_state_a, model_state_b },
{ model_state_a, model_state_b, model_state_none },
{ model_state_b, model_state_none, model_state_a },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "cannot move after win:... ");
print_board(model.board);
model_pos_t pos = model_pos(2, 1);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_a), 0);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_b), 0);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_b, model_state_a, model_state_a },
{ model_state_a, model_state_b, model_state_b },
{ model_state_b, model_state_a, model_state_a },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "cannot move when all done:... ");
print_board(model.board);
for(size_t row = 0; row < MODEL_SIZE; row++) {
for(size_t col = 0; col < MODEL_SIZE; col++) {
model_pos_t pos = model_pos(row, col);
printf("%zd/%zd ", row, col);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_a), 0);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_b), 0);
}
}
CU_ASSERT_EQUAL_FATAL(model_can_move(&model), 0);
}
printf(TRACE_INDENT);
}
static void test_model_get_win_line(void)
{
// check: model_line_t model_get_win_line(model_t *instance);
{
// arrange
model_t model;
init_model(&model, 0);
// act & assert
printf(TRACE_INDENT "initial no winner:... ");
print_board(model.board);
model_line_t no_win = model_get_win_line(&model);
CU_ASSERT_EQUAL_FATAL(no_win.dir, model_dir_none);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_none, model_state_a, model_state_a },
{ model_state_a, model_state_b, model_state_none },
{ model_state_b, model_state_none, model_state_b },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "no winner while not yet done nor win:... ");
print_board(model.board);
model_line_t no_win = model_get_win_line(&model);
CU_ASSERT_EQUAL_FATAL(no_win.dir, model_dir_none);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_b, model_state_a, model_state_a },
{ model_state_a, model_state_b, model_state_b },
{ model_state_b, model_state_a, model_state_a },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "no winner when all done:... ");
print_board(model.board);
model_line_t no_win = model_get_win_line(&model);
CU_ASSERT_EQUAL_FATAL(no_win.dir, model_dir_none);
}
{
for(size_t row = 0; row < MODEL_SIZE; row++) {
// arrange
model_t model;
init_model(&model, 0);
for(size_t col = 0; col < MODEL_SIZE; col++) {
CU_ASSERT_EQUAL_FATAL(model_move(&model, model_pos(row, col), model_state_a), 1);
}
// act & assert
printf(TRACE_INDENT "row winner:... ");
print_board(model.board);
model_line_t win = model_get_win_line(&model);
CU_ASSERT_EQUAL_FATAL(win.dir, model_dir_h);
CU_ASSERT_EQUAL_FATAL(win.start.row, row);
CU_ASSERT_EQUAL_FATAL(win.start.col, 0);
}
}
{
for(size_t col = 0; col < MODEL_SIZE; col++) {
// arrange
model_t model;
init_model(&model, 0);
for(size_t row = 0; row < MODEL_SIZE; row++) {
CU_ASSERT_EQUAL_FATAL(model_move(&model, model_pos(row, col), model_state_a), 1);
}
// act & assert
printf(TRACE_INDENT "column winner:... ");
print_board(model.board);
model_line_t win = model_get_win_line(&model);
CU_ASSERT_EQUAL_FATAL(win.dir, model_dir_v);
CU_ASSERT_EQUAL_FATAL(win.start.row, 0);
CU_ASSERT_EQUAL_FATAL(win.start.col, col);
}
}
{
printf(TRACE_INDENT "diagonal left-right winner:... ");
// arrange
model_t model;
init_model(&model, 0);
for(size_t i = 0; i < MODEL_SIZE; i++) {
CU_ASSERT_EQUAL_FATAL(model_move(&model, model_pos(i, i), model_state_a), 1);
}
// act & assert
print_board(model.board);
model_line_t win = model_get_win_line(&model);
CU_ASSERT_EQUAL_FATAL(win.dir, model_dir_d);
CU_ASSERT_EQUAL_FATAL(win.start.row, 0);
CU_ASSERT_EQUAL_FATAL(win.start.col, 0);
}
{
printf(TRACE_INDENT "diagonal right-left winner:... ");
// arrange
model_t model;
init_model(&model, 0);
for(size_t i = 0; i < MODEL_SIZE; i++) {
CU_ASSERT_EQUAL_FATAL(model_move(&model, model_pos(MODEL_SIZE - 1 - i, i), model_state_a), 1);
}
// act & assert
print_board(model.board);
model_line_t win = model_get_win_line(&model);
CU_ASSERT_EQUAL_FATAL(win.dir, model_dir_d);
CU_ASSERT_EQUAL_FATAL(win.start.row, 0);
CU_ASSERT_EQUAL_FATAL(win.start.col, MODEL_SIZE - 1);
}
printf(TRACE_INDENT);
}
/**
* @brief Registers and runs the tests.
* @returns success (0) or one of the CU_ErrorCode (>0)
*/
int main(void)
{
// setup, run, teardown
TestMainBasic("lab test", setup, teardown
, test_model_init
, test_model_get_state
, test_model_get_winner
, test_model_can_move
, test_model_move
, test_model_get_win_line
);
}

View File

@ -0,0 +1,254 @@
# 06 - Personen Verwaltung Linked List
___
![](./linked_list.png)
___
## 1. Übersicht
In diesem Praktikum schreiben Sie eine einfache Personenverwaltung. Dabei werden Sie etliche Elemente von C anwenden:
* Header Files selber schreiben, inklusive Include Guard
* Typen definieren
* Funktionen mit `by value` und `by reference` Parametern deklarieren und definieren
* einfache Variablen, Pointer Variablen, struct Variablen und Array Variablen benutzen
* Strukturen im Speicher dynamisch allozieren und freigeben
* I/O und String Funktionen aus der Standard Library anwenden
* Anwender Eingaben verarbeiten
* Fehlerbehandlung
___
## 2. Lernziele
In diesem Praktikum wenden Sie viele der bisher gelernten C Elemente an.
* Sie können anhand dieser Beschreibung ein vollständiges C Programm schreiben.
* Sie können Unit Tests schreiben welche die wesentlichen Funktionen des Programms individuell testen.
*
Die Bewertung dieses Praktikums ist am Ende angegeben.
Erweitern Sie die vorgegebenen Code Gerüste, welche im `git` Repository `snp-lab-code` verfügbar sind.
___
## 3. Personenverwaltung
___
### 3.1 Programmfunktion
Das Programm soll in einer Schleife dem Benutzer jeweils folgende Auswahl bieten, wovon eine Aktion mit Eingabe des entsprechenden Buchstabens ausgelöst wird:
**I**(nsert), **R**(emove), **S**(how), **C**(lear), **E**(nd):
* **Insert**: der Benutzer wird aufgefordert, eine Person einzugeben
* **Remove**: der Benutzer wird aufgefordert, die Daten einer zu löschenden Person einzu-geben
* **Show**: eine komplette Liste aller gespeicherten Personen wird in alphabetischer Rei-henfolge ausgegeben
* **Clear**: alle Personen werden gelöscht
* **End**: das Programm wird beendet
___
### 3.2 Designvorgaben
**Verkettete Liste**
Da zur Kompilierzeit nicht bekannt ist, ob 10 oder 10'000 Personen eingegeben werden, wäre es keine gute Idee, im Programm einen statischen Array mit z.B. 10'000 Personen-Einträgen zu allozieren. Dies wäre ineffizient und umständlich beim sortierten Einfügen von Personen. In solchen Situationen arbeitet man deshalb mit dynamischen Datenstrukturen, die zur Laufzeit beliebig (solange Speicher vorhanden ist) wachsen und wieder schrumpfen können. Eine sehr populäre dynamische Datenstruktur ist die **verkettete Liste** und genau die werden wir in diesem Praktikum verwenden.
![](./a.png)
![](./b.png)
![](./c.png)
![](./d.png)
Abbildung 1: Zyklisch verkettete Liste
Eine verkettete Liste bedeutet, dass ein Knoten der verketten Liste einen Datensatz einer Person speichert und zusätzlich einen Pointer auf den nächsten Knoten in der Liste aufweist (siehe Abbildung 1). In dieser Pointer Variablen (`next` in der `node_t` Struktur unten) steht also einfach die Adresse des nächsten Knotens.
Die leere Liste besteht aus einem einzelnen Element, welches keine spezifische Person abspeichert und welches auf sich selbst zeigt (Abbildung 1 a). Dieses Element ist der Einstiegspunkt der Liste (auch Anker oder Wurzel genannt) und ist das einzige Element, das Sie im Programm direkt kennen und einer Variablen zuweisen. Dieses Element können Sie statisch allozieren (z.B. `node_t anchor`;, siehe Details weiter unten), denn es existiert während der gesamten Ausführungszeit. Alle anderen Elemente erreichen Sie ausgehend vom Anker, indem Sie einmal, den Pointern folgend, im Kreis herum gehen. Abbildung 1 b zeigt die Liste nach dem Einfügen der Person `Max Mueller, 40` Jahre. Nach dem Einfügen von zwei weiteren Personen sieht die Datenstruktur aus wie in Abbildung 1 c. Das Entfernen der Person `Arno Bosshard` führt zu Abbildung 1 d.
Eine Person kann **zugefügt** werden, indem dynamisch ein neuer Knoten erzeugt wird und dieser in die verkettete Liste eingefügt wird. Beim Einfügen müssen die Adressen der Knoten so den Pointern zugewiesen werden, dass die Kette intakt bleibt.
Ein Knoten wird **entfernt**, indem der entsprechende Knoten aus der Verkettung herausgelöst wird (`next` des Vorgängerknotens soll neu auf `next` des herauszulösenden Knotens zeigen) und dann der Speicher des entsprechenden Knotens freigegeben wird.
**Personen und Knoten Records**
Die für je eine Person zu speichernden Daten sollen in folgendem C `struct` zusammengefasst sein.
```C
#define NAME_LEN 20
typedef struct {
char name[NAME_LEN];
char first_name[NAME_LEN];
unsigned int age;
} person_t;
```
Jeder Knoten der verketteten Liste soll aus folgendem C `struct` bestehen.
```C
typedef struct node {
person_t content; // in diesem Knoten gespeicherte Person
struct node *next; // Pointer auf den nächsten Knoten in der Liste
} node_t;
```
**Vorschlag: zyklisch verkettete Liste**
Erkennen des Endes der Liste: bei der zyklisch verketteten Liste zeigt das letzte Element wie-der auf den Anker, die Liste bildet also einen Kreis. Dies ist in Abbildung 1 so abgebildet.
Alternativ könnte man das Ende erkennbar machen, indem die Kette anstelle von zyklisch, mit einem NULL Pointer endet.
Die Wahl ist ihnen überlassen ob sie die eine oder andere Art der End-Erkennung implementieren. In der Beschreibung wird angenommen, dass es sich um eine zyklisch verkettete Liste handelt.
**Sortiertes Einfügen**
Die Personen Records sollen sortiert in die Liste eingefügt werden. Dies bedeutet, dass vom Anker her gesucht werden soll, bis der erste Knoten gefunden wurde dessen Nachfolgeknoten entweder „grösser“ ist als der einzufügende Knoten, oder wo das Ende der Liste erreicht ist. Die Ordnung (grösser, gleich, kleiner) soll so definiert sein:
```C
// if (p1 > p2) { ... }
if (person_compare(&p1, &p2) > 0) { ... }
/**
* @brief Compares two persons in this sequence: 1st=name, 2nd=first_name, 3rd=age
* @param a [IN] const reference to 1st person in the comparison
* @param b [IN] const reference to 2nd person in the comparison
* @return =0 if all record fields are the same
* >0 if all previous fields are the same, but for this field, a is greater
* <0 if all previous fields are the same, but for this field, b is greater
* @remark strncmp() is used for producing the result of string field comparisons
* @remark a->age b->age is used for producing the result of age comparison
*/
int person_compare(const person_t *a, const person_t *b);
```
**Eingabe**
**Fehlerhafte Wahl der Operation** in der Hauptschleife soll gemeldet werden, ansonsten aber ignoriert werden.
**Fehlerhafte Eingabe der Personenangaben** sollen gemeldet werden und die gesamte Operation (z.B. Insert) verworfen werden.
Zu prüfende Fehler bei Personeneingaben:
* für die Namen
* zu lange Namen
* für das Alter
* keine Zahl
* Duplikat
* derselbe Record soll nicht doppelt in der Liste vorkommen
Weitergehende Prüfungen sind nicht erwartet.
**Zu beachten:** bei fehlerhafter Eingabe darf kein „Memory Leak“ entstehen, d.h. potentiell auf dem Heap allozierter Speicher muss im Fehlerfall freigegeben werden.
___
### 3.3 Bestehender Programmrahmen
Der Programmrahmen besteht aus den unten aufgelisteten Files. Es sollen weitere Module in `src` hinzugefügt werden und die bestehenden Files ergänzt werden gemäss den Aufgaben.
| | |
| :-- | :-- |
| Makefile | -> **zu ergänzen** mit neuen Modulen |
| tests/tests.c | -> **zu ergänzen** gemäss Aufgaben (implementieren von Unit Tests) |
| src/main.c | -> **zu ergänzen** gemäss Aufgaben (Hauptprogramm) |
___
## 4. Aufgabe 1: Modularisierung API und Implementation main.c
Kreieren Sie folgende Files in `src` und implementieren Sie `main.c` basierend auf dem unten von Ihnen gegebenen API.
**File person.h**
Typ Definitionen:
```C
person_t... // siehe Beschreibung oben
```
Funktionsdeklarationen:
```C
// siehe Beschreibung oben
int person_compare(const person_t *a, const person_t *b);
```
* gegebenenfalls weitere Funktionen für die Bearbeitung von Personen
**File list.h**
Typ Definitionen:
```C
person_t... // siehe Beschreibung oben
```
Funktionsdeklarationen:
* Funktionen für `insert`, `remove`, `clear` Operationen auf der Liste
___
Das Hauptprogramm soll die Eingabeschleife implementieren und die obigen Funktionen (wo angebracht) aufrufen.
___
## 5. Aufgabe 2: Implementierung von person.c und list.c
Fügen Sie die beiden Implementationsfiles `person.c` und `list.c` zu `src`. Fügen Sie die beiden Module im `Makefile` zu der vorgegebenen Variablen `MODULES` hinzu, so dass sie beim `make` Aufruf auch berücksichtigt werden.
___
### 5.1 Teilaufgabe: Implementierung von person.c
Implementieren Sie die Funktionen aus `person.h`.
Falls nötig, stellen Sie weitere statische Hilfsfunktionen in `person.c` zur Verfügung.
___
### 5.2 Teilaufgabe: Implementierung von list.c
Implementieren Sie die Funktionen aus `list.h`.
Falls nötig, stellen Sie weitere statische Hilfsfunktionen in `list.c` zur Verfügung.
___
## 6. Aufgabe 3: Unit Tests
Schreiben Sie Unit Tests für mindestens die folgenden Funktionen
* `person.h:`
* `person_compare`
* `list.h:`
* `list_insert`
* `list_remove`
* `list_clear`
Es existieren in `tests/tests.c` schon vier Test Rahmen für diese Test Cases.
In diese Test Cases sollen die entsprechenden Funktionen unter verschiedenen Bedingungen isoliert aufgerufen werden und deren Verhalten überprüft werden.
Verwenden Sie für die Überprüfung die CUnit `CU_ASSERT_...` Makros.
Siehe dazu auch `man CUnit`.
Wenn die obigen Teilaufgaben erfolgreich umgesetzt sind, laufen die Tests ohne Fehler durch.
___
## 7. Bewertung
| Aufgabe | Kriterium | Punkte |
| :-- | :-- | :-- |
| | Sie können das funktionierende Programm inklusive funktionierende Tests demonstrieren und erklären. | |
| 1 | API von list.h und person.h plus die Implementation von main.c | 2 |
| 2 | Teilaufgabe: person.c | 2 |
| 2 | Teilaufgabe: list.c | 2 |
| 3 | Unit Tests | 2 |
___
Version: 11.01.2022

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
<svg fill-opacity="1" xmlns:xlink="http://www.w3.org/1999/xlink" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" width="210" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="90 400 210 160" height="160" xmlns="http://www.w3.org/2000/svg" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto"
><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"
/><g
><defs id="defs1"
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"
><path d="M0 0 L2147483647 0 L2147483647 2147483647 L0 2147483647 L0 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"
><path d="M0 0 L0 30 L40 30 L40 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath3"
><path d="M0 0 L0 60 L100 60 L100 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath4"
><path d="M0 0 L0 80 L170 80 L170 0 Z"
/></clipPath
></defs
><g font-family="sans-serif" font-size="14px" transform="translate(180,510)"
><text x="5" xml:space="preserve" y="17.9688" clip-path="url(#clipPath2)" stroke="none"
>a)</text
></g
><g fill="rgb(68,68,68)" fill-opacity="0.4902" transform="translate(140,450)" stroke-opacity="0.4902" stroke="rgb(68,68,68)"
><rect x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath3)" stroke="none"
/></g
><g transform="translate(140,450)"
><rect fill="none" x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath3)"
/></g
><g transform="translate(110,420)"
><path fill="none" d="M29.5 60.5 L10.5 60.5" clip-path="url(#clipPath4)"
/><path fill="none" d="M10.5 60.5 L10.5 10.5" clip-path="url(#clipPath4)"
/><path fill="none" d="M10.5 10.5 L150.5 10.5" clip-path="url(#clipPath4)"
/><path fill="none" d="M150.5 10.5 L150.5 60.5" clip-path="url(#clipPath4)"
/><path fill="none" d="M150.5 60.5 L130.5 60.5" clip-path="url(#clipPath4)"
/><path d="M18.7417 54 L30 60.5 L18.7417 67 Z" clip-path="url(#clipPath4)" stroke="none"
/><path fill="none" d="M18.7417 54 L30 60.5 L18.7417 67 Z" clip-path="url(#clipPath4)"
/></g
></g
></svg
>

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
<svg fill-opacity="1" xmlns:xlink="http://www.w3.org/1999/xlink" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" width="210" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="320 420 210 220" height="220" xmlns="http://www.w3.org/2000/svg" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto"
><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"
/><g
><defs id="defs1"
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"
><path d="M0 0 L2147483647 0 L2147483647 2147483647 L0 2147483647 L0 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"
><path d="M0 0 L0 30 L40 30 L40 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath3"
><path d="M0 0 L0 60 L100 60 L100 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath4"
><path d="M0 0 L0 120 L50 120 L50 0 Z"
/></clipPath
></defs
><g font-family="sans-serif" font-size="14px" transform="translate(410,590)"
><text x="5" xml:space="preserve" y="17.9688" clip-path="url(#clipPath2)" stroke="none"
>b)</text
></g
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(370,440)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath3)" stroke="none"
/></g
><g transform="translate(370,440)"
><rect fill="none" x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath3)"
/><text x="5" font-size="14px" y="19.0156" clip-path="url(#clipPath3)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Mueller</text
><text x="5" font-size="14px" y="34.9844" clip-path="url(#clipPath3)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Max</text
><text x="5" font-size="14px" y="50.9531" clip-path="url(#clipPath3)" font-family="sans-serif" stroke="none" xml:space="preserve"
>40</text
></g
><g fill="rgb(68,68,68)" fill-opacity="0.4902" transform="translate(370,530)" stroke-opacity="0.4902" stroke="rgb(68,68,68)"
><rect x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath3)" stroke="none"
/></g
><g transform="translate(370,530)"
><rect fill="none" x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath3)"
/></g
><g transform="translate(340,460)"
><path fill="none" d="M29.5 100.5 L10.5 100.5" clip-path="url(#clipPath4)"
/><path fill="none" d="M10.5 100.5 L10.5 10.5" clip-path="url(#clipPath4)"
/><path fill="none" d="M10.5 10.5 L30.5 10.5" clip-path="url(#clipPath4)"
/><path d="M18.7417 94 L30 100.5 L18.7417 107 Z" clip-path="url(#clipPath4)" stroke="none"
/><path fill="none" d="M18.7417 94 L30 100.5 L18.7417 107 Z" clip-path="url(#clipPath4)"
/></g
><g transform="translate(460,460)"
><path fill="none" d="M11.5 10.5 L30.5 10.5" clip-path="url(#clipPath4)"
/><path fill="none" d="M30.5 10.5 L30.5 100.5" clip-path="url(#clipPath4)"
/><path fill="none" d="M30.5 100.5 L10.5 100.5" clip-path="url(#clipPath4)"
/><path d="M22.2583 17 L11 10.5 L22.2583 4 Z" clip-path="url(#clipPath4)" stroke="none"
/><path fill="none" d="M22.2583 17 L11 10.5 L22.2583 4 Z" clip-path="url(#clipPath4)"
/></g
></g
></svg
>

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
<svg fill-opacity="1" xmlns:xlink="http://www.w3.org/1999/xlink" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" width="380" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="70 580 380 270" height="270" xmlns="http://www.w3.org/2000/svg" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto"
><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"
/><g
><defs id="defs1"
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"
><path d="M0 0 L2147483647 0 L2147483647 2147483647 L0 2147483647 L0 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"
><path d="M0 0 L0 60 L100 60 L100 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath3"
><path d="M0 0 L0 30 L40 30 L40 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath4"
><path d="M0 0 L0 70 L100 70 L100 0 Z"
/></clipPath
></defs
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(330,670)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(330,670)"
><rect fill="none" x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)"
/><text x="5" font-size="14px" y="19.0156" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Bosshard </text
><text x="5" font-size="14px" y="34.9844" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Arno</text
><text x="5" font-size="14px" y="50.9531" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>62</text
></g
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(210,600)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(210,600)"
><rect fill="none" x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)"
/><text x="5" font-size="14px" y="19.0156" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Mueller</text
><text x="5" font-size="14px" y="34.9844" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Max</text
><text x="5" font-size="14px" y="50.9531" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>40</text
></g
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(90,670)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(90,670)"
><rect fill="none" x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)"
/><text x="5" font-size="14px" y="19.0156" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Schmid </text
><text x="5" font-size="14px" y="34.9844" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Anna </text
><text x="5" font-size="14px" y="50.9531" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>32</text
></g
><g font-family="sans-serif" font-size="14px" transform="translate(250,800)"
><text x="5" xml:space="preserve" y="17.9688" clip-path="url(#clipPath3)" stroke="none"
>c)</text
></g
><g fill="rgb(68,68,68)" fill-opacity="0.4902" transform="translate(210,740)" stroke-opacity="0.4902" stroke="rgb(68,68,68)"
><rect x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(210,740)"
><rect fill="none" x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)"
/></g
><g transform="translate(130,720)"
><path fill="none" d="M79.6318 50.0039 L10.5 10.5" clip-path="url(#clipPath4)"
/><path d="M73.5158 39.0227 L80.0659 50.2519 L67.066 50.3098 Z" clip-path="url(#clipPath4)" stroke="none"
/><path fill="none" d="M73.5158 39.0227 L80.0659 50.2519 L67.066 50.3098 Z" clip-path="url(#clipPath4)"
/></g
><g transform="translate(130,620)"
><path fill="none" d="M11.3682 50.0039 L80.5 10.5" clip-path="url(#clipPath4)"
/><path d="M23.934 50.3098 L10.9341 50.2519 L17.4842 39.0227 Z" clip-path="url(#clipPath4)" stroke="none"
/><path fill="none" d="M23.934 50.3098 L10.9341 50.2519 L17.4842 39.0227 Z" clip-path="url(#clipPath4)"
/></g
><g transform="translate(300,620)"
><path fill="none" d="M11.3682 10.9961 L80.5 50.5" clip-path="url(#clipPath4)"
/><path d="M17.4842 21.9773 L10.9341 10.7481 L23.934 10.6902 Z" clip-path="url(#clipPath4)" stroke="none"
/><path fill="none" d="M17.4842 21.9773 L10.9341 10.7481 L23.934 10.6902 Z" clip-path="url(#clipPath4)"
/></g
><g transform="translate(300,720)"
><path fill="none" d="M79.6318 10.9961 L10.5 50.5" clip-path="url(#clipPath4)"
/><path d="M67.066 10.6902 L80.0659 10.7481 L73.5158 21.9773 Z" clip-path="url(#clipPath4)" stroke="none"
/><path fill="none" d="M67.066 10.6902 L80.0659 10.7481 L73.5158 21.9773 Z" clip-path="url(#clipPath4)"
/></g
></g
></svg
>

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
<svg fill-opacity="1" xmlns:xlink="http://www.w3.org/1999/xlink" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" width="380" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="300 830 380 210" height="210" xmlns="http://www.w3.org/2000/svg" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto"
><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"
/><g
><defs id="defs1"
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"
><path d="M0 0 L2147483647 0 L2147483647 2147483647 L0 2147483647 L0 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"
><path d="M0 0 L0 60 L100 60 L100 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath3"
><path d="M0 0 L0 30 L40 30 L40 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath4"
><path d="M0 0 L0 30 L170 30 L170 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath5"
><path d="M0 0 L0 80 L110 80 L110 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath6"
><path d="M0 0 L0 80 L100 80 L100 0 Z"
/></clipPath
></defs
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(560,850)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(560,850)"
><rect fill="none" x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)"
/><text x="5" font-size="14px" y="19.0156" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Mueller</text
><text x="5" font-size="14px" y="34.9844" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Max</text
><text x="5" font-size="14px" y="50.9531" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>40</text
></g
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(320,850)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(320,850)"
><rect fill="none" x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)"
/><text x="5" font-size="14px" y="19.0156" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Schmid </text
><text x="5" font-size="14px" y="34.9844" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Anna </text
><text x="5" font-size="14px" y="50.9531" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>32</text
></g
><g font-family="sans-serif" font-size="14px" transform="translate(480,990)"
><text x="5" xml:space="preserve" y="17.9688" clip-path="url(#clipPath3)" stroke="none"
>d)</text
></g
><g fill="rgb(68,68,68)" fill-opacity="0.4902" transform="translate(440,930)" stroke-opacity="0.4902" stroke="rgb(68,68,68)"
><rect x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(440,930)"
><rect fill="none" x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)"
/></g
><g transform="translate(410,870)"
><path fill="none" d="M11.5 10.5 L150.5 10.5" clip-path="url(#clipPath4)"
/><path d="M22.2583 17 L11 10.5 L22.2583 4 Z" clip-path="url(#clipPath4)" stroke="none"
/><path fill="none" d="M22.2583 17 L11 10.5 L22.2583 4 Z" clip-path="url(#clipPath4)"
/></g
><g transform="translate(350,900)"
><path fill="none" d="M89.652 59.97 L10.5 10.5" clip-path="url(#clipPath5)"
/><path d="M83.9739 48.7561 L90.076 60.235 L77.084 59.7801 Z" clip-path="url(#clipPath5)" stroke="none"
/><path fill="none" d="M83.9739 48.7561 L90.076 60.235 L77.084 59.7801 Z" clip-path="url(#clipPath5)"
/></g
><g transform="translate(530,900)"
><path fill="none" d="M79.6863 11.0812 L10.5 60.5" clip-path="url(#clipPath6)"
/><path d="M67.1538 12.0451 L80.0931 10.7906 L74.7099 22.6237 Z" clip-path="url(#clipPath6)" stroke="none"
/><path fill="none" d="M67.1538 12.0451 L80.0931 10.7906 L74.7099 22.6237 Z" clip-path="url(#clipPath6)"
/></g
></g
></svg
>

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.0//EN'
'http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd'>
<svg fill-opacity="1" xmlns:xlink="http://www.w3.org/1999/xlink" color-rendering="auto" color-interpolation="auto" text-rendering="auto" stroke="black" stroke-linecap="square" width="300" stroke-miterlimit="10" shape-rendering="auto" stroke-opacity="1" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" viewBox="110 80 300 200" height="200" xmlns="http://www.w3.org/2000/svg" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12px" stroke-dashoffset="0" image-rendering="auto"
><!--Generated by the Batik Graphics2D SVG Generator--><defs id="genericDefs"
/><g
><defs id="defs1"
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"
><path d="M0 0 L2147483647 0 L2147483647 2147483647 L0 2147483647 L0 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"
><path d="M0 0 L0 60 L100 60 L100 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath3"
><path d="M0 0 L0 70 L60 70 L60 0 Z"
/></clipPath
><clipPath clipPathUnits="userSpaceOnUse" id="clipPath4"
><path d="M0 0 L0 30 L90 30 L90 0 Z"
/></clipPath
></defs
><g fill="rgb(68,68,68)" fill-opacity="0.4902" transform="translate(210,200)" stroke-opacity="0.4902" stroke="rgb(68,68,68)"
><rect x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(210,200)"
><rect fill="none" x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)"
/></g
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(290,100)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(290,100)"
><rect fill="none" x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)"
/><text x="5" font-size="14px" y="19.0156" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Mueller</text
><text x="5" font-size="14px" y="34.9844" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Max</text
><text x="5" font-size="14px" y="50.9531" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>40</text
></g
><g fill="rgb(255,255,255)" fill-opacity="0" transform="translate(130,100)" stroke-opacity="0" stroke="rgb(255,255,255)"
><rect x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)" stroke="none"
/></g
><g transform="translate(130,100)"
><rect fill="none" x="0.5" width="98.5" height="58.5" y="0.5" clip-path="url(#clipPath2)"
/><text x="5" font-size="14px" y="19.0156" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Schmid </text
><text x="5" font-size="14px" y="34.9844" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>Anna </text
><text x="5" font-size="14px" y="50.9531" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve"
>32</text
></g
><g transform="translate(300,150)"
><path fill="none" d="M39.9 11.3 L10.5 50.5" clip-path="url(#clipPath3)"
/><path d="M28.245 16.0067 L40.2 10.9 L38.645 23.8067 Z" clip-path="url(#clipPath3)" stroke="none"
/><path fill="none" d="M28.245 16.0067 L40.2 10.9 L38.645 23.8067 Z" clip-path="url(#clipPath3)"
/></g
><g transform="translate(170,150)"
><path fill="none" d="M39.9 49.7 L10.5 10.5" clip-path="url(#clipPath3)"
/><path d="M38.645 37.1933 L40.2 50.1 L28.245 44.9933 Z" clip-path="url(#clipPath3)" stroke="none"
/><path fill="none" d="M38.645 37.1933 L40.2 50.1 L28.245 44.9933 Z" clip-path="url(#clipPath3)"
/></g
><g transform="translate(220,120)"
><path fill="none" d="M11.5 10.5 L70.5 10.5" clip-path="url(#clipPath4)"
/><path d="M22.2583 17 L11 10.5 L22.2583 4 Z" clip-path="url(#clipPath4)" stroke="none"
/><path fill="none" d="M22.2583 17 L11 10.5 L22.2583 4 Z" clip-path="url(#clipPath4)"
/></g
></g
></svg
>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@ -0,0 +1,386 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<diagram program="umlet" version="14.3.0">
<zoom_level>10</zoom_level>
<element>
<id>UMLObject</id>
<coordinates>
<x>120</x>
<y>80</y>
<w>100</w>
<h>60</h>
</coordinates>
<panel_attributes>Schmid
Anna
32
halign=left</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>Relation</id>
<coordinates>
<x>210</x>
<y>100</y>
<w>90</w>
<h>30</h>
</coordinates>
<panel_attributes>lt=&lt;&lt;&lt;-</panel_attributes>
<additional_attributes>10.0;10.0;70.0;10.0</additional_attributes>
</element>
<element>
<id>UMLObject</id>
<coordinates>
<x>280</x>
<y>80</y>
<w>100</w>
<h>60</h>
</coordinates>
<panel_attributes>Mueller
Max
40
halign=left</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>UMLObject</id>
<coordinates>
<x>200</x>
<y>180</y>
<w>100</w>
<h>60</h>
</coordinates>
<panel_attributes>
halign=left
bg=#444444</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>Relation</id>
<coordinates>
<x>160</x>
<y>130</y>
<w>60</w>
<h>70</h>
</coordinates>
<panel_attributes>lt=&lt;&lt;&lt;-</panel_attributes>
<additional_attributes>40.0;50.0;10.0;10.0</additional_attributes>
</element>
<element>
<id>Relation</id>
<coordinates>
<x>290</x>
<y>130</y>
<w>60</w>
<h>70</h>
</coordinates>
<panel_attributes>lt=&lt;&lt;&lt;-</panel_attributes>
<additional_attributes>40.0;10.0;10.0;50.0</additional_attributes>
</element>
<element>
<id>UMLObject</id>
<coordinates>
<x>320</x>
<y>320</y>
<w>100</w>
<h>60</h>
</coordinates>
<panel_attributes>Bosshard
Arno
62
halign=left</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>UMLObject</id>
<coordinates>
<x>140</x>
<y>450</y>
<w>100</w>
<h>60</h>
</coordinates>
<panel_attributes>
halign=left
bg=#444444</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>Relation</id>
<coordinates>
<x>110</x>
<y>420</y>
<w>170</w>
<h>80</h>
</coordinates>
<panel_attributes>lt=&lt;&lt;&lt;-</panel_attributes>
<additional_attributes>30.0;60.0;10.0;60.0;10.0;10.0;150.0;10.0;150.0;60.0;130.0;60.0</additional_attributes>
</element>
<element>
<id>UMLObject</id>
<coordinates>
<x>370</x>
<y>530</y>
<w>100</w>
<h>60</h>
</coordinates>
<panel_attributes>
halign=left
bg=#444444</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>UMLObject</id>
<coordinates>
<x>370</x>
<y>440</y>
<w>100</w>
<h>60</h>
</coordinates>
<panel_attributes>Mueller
Max
40
halign=left</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>Relation</id>
<coordinates>
<x>460</x>
<y>460</y>
<w>50</w>
<h>120</h>
</coordinates>
<panel_attributes>lt=&lt;&lt;&lt;-</panel_attributes>
<additional_attributes>10.0;10.0;30.0;10.0;30.0;100.0;10.0;100.0</additional_attributes>
</element>
<element>
<id>Relation</id>
<coordinates>
<x>340</x>
<y>460</y>
<w>50</w>
<h>120</h>
</coordinates>
<panel_attributes>lt=&lt;&lt;&lt;-</panel_attributes>
<additional_attributes>30.0;100.0;10.0;100.0;10.0;10.0;30.0;10.0</additional_attributes>
</element>
<element>
<id>Text</id>
<coordinates>
<x>180</x>
<y>510</y>
<w>40</w>
<h>30</h>
</coordinates>
<panel_attributes>a)
style=wordwrap</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>Text</id>
<coordinates>
<x>410</x>
<y>590</y>
<w>40</w>
<h>30</h>
</coordinates>
<panel_attributes>b)
style=wordwrap</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>UMLObject</id>
<coordinates>
<x>210</x>
<y>740</y>
<w>100</w>
<h>60</h>
</coordinates>
<panel_attributes>
halign=left
bg=#444444</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>Text</id>
<coordinates>
<x>250</x>
<y>800</y>
<w>40</w>
<h>30</h>
</coordinates>
<panel_attributes>c)
style=wordwrap</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>UMLObject</id>
<coordinates>
<x>90</x>
<y>670</y>
<w>100</w>
<h>60</h>
</coordinates>
<panel_attributes>Schmid
Anna
32
halign=left</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>UMLObject</id>
<coordinates>
<x>210</x>
<y>600</y>
<w>100</w>
<h>60</h>
</coordinates>
<panel_attributes>Mueller
Max
40
halign=left</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>UMLObject</id>
<coordinates>
<x>330</x>
<y>670</y>
<w>100</w>
<h>60</h>
</coordinates>
<panel_attributes>Bosshard
Arno
62
halign=left</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>Relation</id>
<coordinates>
<x>300</x>
<y>720</y>
<w>100</w>
<h>70</h>
</coordinates>
<panel_attributes>lt=&lt;&lt;&lt;-</panel_attributes>
<additional_attributes>80.0;10.0;10.0;50.0</additional_attributes>
</element>
<element>
<id>Relation</id>
<coordinates>
<x>300</x>
<y>620</y>
<w>100</w>
<h>70</h>
</coordinates>
<panel_attributes>lt=&lt;&lt;&lt;-</panel_attributes>
<additional_attributes>10.0;10.0;80.0;50.0</additional_attributes>
</element>
<element>
<id>Relation</id>
<coordinates>
<x>130</x>
<y>620</y>
<w>100</w>
<h>70</h>
</coordinates>
<panel_attributes>lt=&lt;&lt;&lt;-</panel_attributes>
<additional_attributes>10.0;50.0;80.0;10.0</additional_attributes>
</element>
<element>
<id>Relation</id>
<coordinates>
<x>130</x>
<y>720</y>
<w>100</w>
<h>70</h>
</coordinates>
<panel_attributes>lt=&lt;&lt;&lt;-</panel_attributes>
<additional_attributes>80.0;50.0;10.0;10.0</additional_attributes>
</element>
<element>
<id>UMLObject</id>
<coordinates>
<x>440</x>
<y>930</y>
<w>100</w>
<h>60</h>
</coordinates>
<panel_attributes>
halign=left
bg=#444444</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>Text</id>
<coordinates>
<x>480</x>
<y>990</y>
<w>40</w>
<h>30</h>
</coordinates>
<panel_attributes>d)
style=wordwrap</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>UMLObject</id>
<coordinates>
<x>320</x>
<y>850</y>
<w>100</w>
<h>60</h>
</coordinates>
<panel_attributes>Schmid
Anna
32
halign=left</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>UMLObject</id>
<coordinates>
<x>560</x>
<y>850</y>
<w>100</w>
<h>60</h>
</coordinates>
<panel_attributes>Mueller
Max
40
halign=left</panel_attributes>
<additional_attributes/>
</element>
<element>
<id>Relation</id>
<coordinates>
<x>530</x>
<y>900</y>
<w>100</w>
<h>80</h>
</coordinates>
<panel_attributes>lt=&lt;&lt;&lt;-</panel_attributes>
<additional_attributes>80.0;10.0;10.0;60.0</additional_attributes>
</element>
<element>
<id>Relation</id>
<coordinates>
<x>350</x>
<y>900</y>
<w>110</w>
<h>80</h>
</coordinates>
<panel_attributes>lt=&lt;&lt;&lt;-</panel_attributes>
<additional_attributes>90.0;60.0;10.0;10.0</additional_attributes>
</element>
<element>
<id>Relation</id>
<coordinates>
<x>410</x>
<y>870</y>
<w>170</w>
<h>30</h>
</coordinates>
<panel_attributes>lt=&lt;&lt;&lt;-</panel_attributes>
<additional_attributes>10.0;10.0;150.0;10.0</additional_attributes>
</element>
</diagram>

View File

@ -0,0 +1,13 @@
SNP_SHARED_MAKEFILE := $(if $(SNP_SHARED_MAKEFILE),$(SNP_SHARED_MAKEFILE),"~/snp/shared.mk")
TARGET := bin/personen-verwaltung
# BEGIN-STUDENTS-TO-ADD-CODE
MODULES :=
# END-STUDENTS-TO-ADD-CODE
SOURCES := src/main.c $(MODULES)
TSTSOURCES := tests/tests.c $(MODULES)
include $(SNP_SHARED_MAKEFILE)
# CFLAGS += -Werror

View File

@ -0,0 +1,8 @@
/**
* @mainpage SNP - P07 Linked List
*
* @section Purpose
*
* This is a lab on usage of arrays.
*
*/

View File

@ -0,0 +1,29 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab implementation
*/
#include <stdio.h>
#include <stdlib.h>
/**
* @brief Main entry point.
* @param[in] argc The size of the argv array.
* @param[in] argv The command line arguments...
* @returns Returns EXIT_SUCCESS (=0) on success, EXIT_FAILURE (=1) there is an expression syntax error.
*/
int main(int argc, char* argv[])
{
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,119 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Test suite for the given package.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <time.h>
#include <assert.h>
#include <CUnit/Basic.h>
#include "test_utils.h"
#ifndef TARGET // must be given by the make file --> see test target
#error missing TARGET define
#endif
/// @brief alias for EXIT_SUCCESS
#define OK EXIT_SUCCESS
/// @brief alias for EXIT_FAILURE
#define FAIL EXIT_FAILURE
/// @brief The name of the STDOUT text file.
#define OUTFILE "stdout.txt"
/// @brief The name of the STDERR text file.
#define ERRFILE "stderr.txt"
#define TRACE_INDENT "\n " ///< allow for better stdout formatting in case of error
// setup & cleanup
static int setup(void)
{
remove_file_if_exists(OUTFILE);
remove_file_if_exists(ERRFILE);
return 0; // success
}
static int teardown(void)
{
// Do nothing.
// Especially: do not remove result files - they are removed in int setup(void) *before* running a test.
return 0; // success
}
// tests
static void test_person_compare(void)
{
// BEGIN-STUDENTS-TO-ADD-CODE
// arrange
// act
CU_FAIL("missing test");
// assert
// END-STUDENTS-TO-ADD-CODE
}
static void test_list_insert(void)
{
// BEGIN-STUDENTS-TO-ADD-CODE
// arrange
// act
CU_FAIL("missing test");
// assert
// END-STUDENTS-TO-ADD-CODE
}
static void test_list_remove(void)
{
// BEGIN-STUDENTS-TO-ADD-CODE
// arrange
// act
CU_FAIL("missing test");
// assert
// END-STUDENTS-TO-ADD-CODE
}
static void test_list_clear(void)
{
// BEGIN-STUDENTS-TO-ADD-CODE
// arrange
// act
CU_FAIL("missing test");
// assert
// END-STUDENTS-TO-ADD-CODE
}
/**
* @brief Registers and runs the tests.
* @returns success (0) or one of the CU_ErrorCode (>0)
*/
int main(void)
{
// setup, run, teardown
TestMainBasic("lab test", setup, teardown
, test_person_compare
, test_list_insert
, test_list_remove
, test_list_clear
);
}

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More