From 903e42182c7aa0be07d5bd3af2ef0020bbaec326 Mon Sep 17 00:00:00 2001 From: stsh Date: Thu, 17 Feb 2022 14:51:10 +0100 Subject: [PATCH] add pages --- Makefile | 20 + P01_Erste_Schritte_mit_C/solution/converter.c | 39 + P01_Erste_Schritte_mit_C/solution/hello.c | 25 + P01_Erste_Schritte_mit_C/solution/wordcount.c | 47 + .../README_solution.md | 125 + .../work/modularize-solution/java/read.java | 88 + .../modularize-solution/java/rectang.java | 32 + .../modularize-solution/java/triangle.java | 73 + .../work/modularize-solution/src/read.c | 94 + .../work/modularize-solution/src/read.h | 27 + .../work/modularize-solution/src/rectang.c | 38 + .../work/modularize-solution/src/rectang.h | 26 + .../work/modularize-solution/src/trace.h | 26 + .../work/modularize-solution/src/triangle.c | 78 + .../tests/stim-error.input | 12 + .../tests/stim-not-right-angled.input | 12 + .../tests/stim-right-angled.input | 12 + .../work/modularize-solution/tests/tests.c | 205 + .../work/show-dependencies-solution/Makefile | 57 + .../show-dependencies-solution/mainpage.dox | 8 + .../show-dependencies-solution/src/data.c | 149 + .../show-dependencies-solution/src/data.h | 72 + .../show-dependencies-solution/src/error.h | 17 + .../show-dependencies-solution/src/main.c | 36 + .../show-dependencies-solution/src/output.c | 95 + .../show-dependencies-solution/src/output.h | 20 + .../tests/dep.input | 7 + .../tests/no_dep.input | 2 + .../show-dependencies-solution/tests/tests.c | 140 + .../work/tic-tac-toe-solution/Makefile | 9 + .../work/tic-tac-toe-solution/mainpage.dox | 8 + .../work/tic-tac-toe-solution/src/control.c | 154 + .../work/tic-tac-toe-solution/src/control.h | 80 + .../work/tic-tac-toe-solution/src/main.c | 39 + .../work/tic-tac-toe-solution/src/model.c | 154 + .../work/tic-tac-toe-solution/src/model.h | 108 + .../work/tic-tac-toe-solution/src/view.c | 255 + .../work/tic-tac-toe-solution/src/view.h | 31 + .../work/tic-tac-toe-solution/tests/tests.c | 445 + .../personen-verwaltung-solution/Makefile | 13 + .../personen-verwaltung-solution/mainpage.dox | 8 + .../personen-verwaltung-solution/src/list.c | 103 + .../personen-verwaltung-solution/src/list.h | 17 + .../personen-verwaltung-solution/src/main.c | 68 + .../personen-verwaltung-solution/src/person.c | 22 + .../personen-verwaltung-solution/src/person.h | 26 + .../tests/tests.c | 299 + P07_Prozesse_und_Threads/Solution/A03.dot | 76 + P07_Prozesse_und_Threads/Solution/A03.png | Bin 0 -> 79253 bytes ...09_1_Prozesse_und_Threads-Kommentiert.docx | Bin 0 -> 715221 bytes ...P09_1_Prozesse_und_Threads-Kommentiert.pdf | Bin 0 -> 622838 bytes .../P09_2_Dämon_Prozesse-Kommentiert.docx | Bin 0 -> 271958 bytes .../P09_2_Dämon_Prozesse-Kommentiert.pdf | Bin 0 -> 384639 bytes .../ProcThreadsLsg.pdf | Bin 0 -> 45771 bytes .../SyncLsg.tar.gz | Bin 0 -> 7726 bytes P09_File_Operations/README.md | 34 + P10_IPC/README.md | 31 + README.md | 10 +- build/html/.buildinfo | 4 + build/html/.nojekyll | 0 .../html/P01_Erste_Schritte_mit_C/README.html | 215 + .../P02_Funktionen_Datentyp_enum/README.html | 412 + .../README_solution.html | 245 + .../README.html | 372 + .../P04_Modularisieren_von_C_Code/README.html | 542 + .../P04_Modularisieren_von_C_Code.html | 289 + build/html/P05_TicTacToe/P05_TicTacToe.html | 424 + build/html/P05_TicTacToe/README.html | 393 + .../README.html | 394 + .../html/P07_Prozesse_und_Threads/README.html | 646 + .../P07_Prozesse_und_Threads/README_P02.html | 264 + build/html/P08_Sync/README.html | 384 + build/html/P09_File_Operations/README.html | 168 + build/html/P10_IPC/README.html | 165 + .../html/_images/135oALYhkYyXB2aG0F-qrwA.jpeg | Bin 0 -> 28796 bytes build/html/_images/MVC_pattern.png | Bin 0 -> 7876 bytes build/html/_images/MVC_pattern.svg | 80 + build/html/_images/P04_Aufgabenstellung.png | Bin 0 -> 7263 bytes build/html/_images/TicTacToe.png | Bin 0 -> 53954 bytes build/html/_images/TicTacToe.svg | 1 + build/html/_images/Wochentagsberechnung.jpg | Bin 0 -> 15553 bytes build/html/_images/a.png | Bin 0 -> 829 bytes build/html/_images/b.png | Bin 0 -> 1905 bytes build/html/_images/c.png | Bin 0 -> 4196 bytes build/html/_images/coffee_customer.png | Bin 0 -> 5049 bytes build/html/_images/d.png | Bin 0 -> 3353 bytes build/html/_images/daemon.png | Bin 0 -> 57824 bytes build/html/_images/dep_dot.png | Bin 0 -> 4405 bytes build/html/_images/ein_mann_orchester.png | Bin 0 -> 217305 bytes .../_images/kalender-108_v-ARDFotogalerie.jpg | Bin 0 -> 69510 bytes build/html/_images/linked_list.png | Bin 0 -> 2921 bytes .../_images/modularisieren_von_c_code.JPG | Bin 0 -> 400851 bytes build/html/_images/random_number.png | Bin 0 -> 7457 bytes build/html/_images/sequence_graph.png | Bin 0 -> 3045 bytes .../html/_images/synchronisationsprobleme.png | Bin 0 -> 259198 bytes build/html/_images/uebersicht.png | Bin 0 -> 55073 bytes .../P01_Erste_Schritte_mit_C/README.md.txt | 84 + .../README.md.txt | 228 + .../README_solution.md.txt | 125 + .../README.md.txt | 193 + .../README.md.txt | 532 + .../P04_Modularisieren_von_C_Code.md.txt | 127 + .../P05_TicTacToe/P05_TicTacToe.rst.txt | 272 + .../html/_sources/P05_TicTacToe/README.md.txt | 205 + .../README.md.txt | 254 + .../P07_Prozesse_und_Threads/README.md.txt | 441 + .../README_P02.md.txt | 149 + build/html/_sources/P08_Sync/README.md.txt | 187 + .../P09_File_Operations/README.md.txt | 34 + build/html/_sources/P10_IPC/README.md.txt | 31 + build/html/_sources/index.rst.txt | 26 + build/html/_static/alabaster.css | 701 + build/html/_static/basic.css | 906 ++ build/html/_static/custom.css | 1 + build/html/_static/doctools.js | 326 + build/html/_static/documentation_options.js | 12 + build/html/_static/file.png | Bin 0 -> 286 bytes build/html/_static/jquery-3.5.1.js | 10872 ++++++++++++++++ build/html/_static/jquery.js | 2 + build/html/_static/language_data.js | 297 + build/html/_static/logo.png | Bin 0 -> 57463 bytes build/html/_static/minus.png | Bin 0 -> 90 bytes build/html/_static/plus.png | Bin 0 -> 90 bytes build/html/_static/pygments.css | 74 + build/html/_static/searchtools.js | 529 + build/html/_static/underscore-1.13.1.js | 2042 +++ build/html/_static/underscore.js | 6 + build/html/genindex.html | 119 + build/html/index.html | 319 + build/html/objects.inv | Bin 0 -> 895 bytes build/html/search.html | 138 + build/html/searchindex.js | 1 + build/latex/main.pdf | Bin 0 -> 1411530 bytes conf.py | 300 + index.html | 1 + index.rst | 26 + make.bat | 35 + 137 files changed, 28159 insertions(+), 6 deletions(-) create mode 100644 Makefile create mode 100644 P01_Erste_Schritte_mit_C/solution/converter.c create mode 100644 P01_Erste_Schritte_mit_C/solution/hello.c create mode 100644 P01_Erste_Schritte_mit_C/solution/wordcount.c create mode 100644 P02_Funktionen_Datentyp_enum/README_solution.md create mode 100644 P04_Modularisieren_von_C_Code/work/modularize-solution/java/read.java create mode 100644 P04_Modularisieren_von_C_Code/work/modularize-solution/java/rectang.java create mode 100644 P04_Modularisieren_von_C_Code/work/modularize-solution/java/triangle.java create mode 100644 P04_Modularisieren_von_C_Code/work/modularize-solution/src/read.c create mode 100644 P04_Modularisieren_von_C_Code/work/modularize-solution/src/read.h create mode 100644 P04_Modularisieren_von_C_Code/work/modularize-solution/src/rectang.c create mode 100644 P04_Modularisieren_von_C_Code/work/modularize-solution/src/rectang.h create mode 100644 P04_Modularisieren_von_C_Code/work/modularize-solution/src/trace.h create mode 100644 P04_Modularisieren_von_C_Code/work/modularize-solution/src/triangle.c create mode 100644 P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-error.input create mode 100644 P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-not-right-angled.input create mode 100644 P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-right-angled.input create mode 100644 P04_Modularisieren_von_C_Code/work/modularize-solution/tests/tests.c create mode 100644 P04_Modularisieren_von_C_Code/work/show-dependencies-solution/Makefile create mode 100644 P04_Modularisieren_von_C_Code/work/show-dependencies-solution/mainpage.dox create mode 100644 P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/data.c create mode 100644 P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/data.h create mode 100644 P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/error.h create mode 100644 P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/main.c create mode 100644 P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/output.c create mode 100644 P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/output.h create mode 100644 P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/dep.input create mode 100644 P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/no_dep.input create mode 100644 P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/tests.c create mode 100644 P05_TicTacToe/work/tic-tac-toe-solution/Makefile create mode 100644 P05_TicTacToe/work/tic-tac-toe-solution/mainpage.dox create mode 100644 P05_TicTacToe/work/tic-tac-toe-solution/src/control.c create mode 100644 P05_TicTacToe/work/tic-tac-toe-solution/src/control.h create mode 100644 P05_TicTacToe/work/tic-tac-toe-solution/src/main.c create mode 100644 P05_TicTacToe/work/tic-tac-toe-solution/src/model.c create mode 100644 P05_TicTacToe/work/tic-tac-toe-solution/src/model.h create mode 100644 P05_TicTacToe/work/tic-tac-toe-solution/src/view.c create mode 100644 P05_TicTacToe/work/tic-tac-toe-solution/src/view.h create mode 100644 P05_TicTacToe/work/tic-tac-toe-solution/tests/tests.c create mode 100644 P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/Makefile create mode 100644 P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/mainpage.dox create mode 100644 P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/list.c create mode 100644 P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/list.h create mode 100644 P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/main.c create mode 100644 P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/person.c create mode 100644 P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/person.h create mode 100644 P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/tests/tests.c create mode 100644 P07_Prozesse_und_Threads/Solution/A03.dot create mode 100644 P07_Prozesse_und_Threads/Solution/A03.png create mode 100644 P07_Prozesse_und_Threads/Solution/P09_1_Prozesse_und_Threads-Kommentiert.docx create mode 100644 P07_Prozesse_und_Threads/Solution/P09_1_Prozesse_und_Threads-Kommentiert.pdf create mode 100644 P07_Prozesse_und_Threads/Solution/P09_2_Dämon_Prozesse-Kommentiert.docx create mode 100644 P07_Prozesse_und_Threads/Solution/P09_2_Dämon_Prozesse-Kommentiert.pdf create mode 100644 P07_Prozesse_und_Threads/SolutionFromBSYDonn20210513/ProcThreadsLsg.pdf create mode 100644 P08_Sync/SolutionFromBSYDonn20210513/SyncLsg.tar.gz create mode 100644 P09_File_Operations/README.md create mode 100644 P10_IPC/README.md create mode 100644 build/html/.buildinfo create mode 100644 build/html/.nojekyll create mode 100644 build/html/P01_Erste_Schritte_mit_C/README.html create mode 100644 build/html/P02_Funktionen_Datentyp_enum/README.html create mode 100644 build/html/P02_Funktionen_Datentyp_enum/README_solution.html create mode 100644 build/html/P03_Bit_Operation_struct_typedef/README.html create mode 100644 build/html/P04_Modularisieren_von_C_Code/README.html create mode 100644 build/html/P04_Modularisieren_von_C_Code/new_P04/P04_Modularisieren_von_C_Code.html create mode 100644 build/html/P05_TicTacToe/P05_TicTacToe.html create mode 100644 build/html/P05_TicTacToe/README.html create mode 100644 build/html/P06_Personen_Verwaltung_Linked_List/README.html create mode 100644 build/html/P07_Prozesse_und_Threads/README.html create mode 100644 build/html/P07_Prozesse_und_Threads/README_P02.html create mode 100644 build/html/P08_Sync/README.html create mode 100644 build/html/P09_File_Operations/README.html create mode 100644 build/html/P10_IPC/README.html create mode 100644 build/html/_images/135oALYhkYyXB2aG0F-qrwA.jpeg create mode 100644 build/html/_images/MVC_pattern.png create mode 100644 build/html/_images/MVC_pattern.svg create mode 100644 build/html/_images/P04_Aufgabenstellung.png create mode 100644 build/html/_images/TicTacToe.png create mode 100644 build/html/_images/TicTacToe.svg create mode 100644 build/html/_images/Wochentagsberechnung.jpg create mode 100644 build/html/_images/a.png create mode 100644 build/html/_images/b.png create mode 100644 build/html/_images/c.png create mode 100644 build/html/_images/coffee_customer.png create mode 100644 build/html/_images/d.png create mode 100644 build/html/_images/daemon.png create mode 100644 build/html/_images/dep_dot.png create mode 100644 build/html/_images/ein_mann_orchester.png create mode 100644 build/html/_images/kalender-108_v-ARDFotogalerie.jpg create mode 100644 build/html/_images/linked_list.png create mode 100644 build/html/_images/modularisieren_von_c_code.JPG create mode 100644 build/html/_images/random_number.png create mode 100644 build/html/_images/sequence_graph.png create mode 100644 build/html/_images/synchronisationsprobleme.png create mode 100644 build/html/_images/uebersicht.png create mode 100644 build/html/_sources/P01_Erste_Schritte_mit_C/README.md.txt create mode 100644 build/html/_sources/P02_Funktionen_Datentyp_enum/README.md.txt create mode 100644 build/html/_sources/P02_Funktionen_Datentyp_enum/README_solution.md.txt create mode 100644 build/html/_sources/P03_Bit_Operation_struct_typedef/README.md.txt create mode 100644 build/html/_sources/P04_Modularisieren_von_C_Code/README.md.txt create mode 100644 build/html/_sources/P04_Modularisieren_von_C_Code/new_P04/P04_Modularisieren_von_C_Code.md.txt create mode 100644 build/html/_sources/P05_TicTacToe/P05_TicTacToe.rst.txt create mode 100644 build/html/_sources/P05_TicTacToe/README.md.txt create mode 100644 build/html/_sources/P06_Personen_Verwaltung_Linked_List/README.md.txt create mode 100644 build/html/_sources/P07_Prozesse_und_Threads/README.md.txt create mode 100644 build/html/_sources/P07_Prozesse_und_Threads/README_P02.md.txt create mode 100644 build/html/_sources/P08_Sync/README.md.txt create mode 100644 build/html/_sources/P09_File_Operations/README.md.txt create mode 100644 build/html/_sources/P10_IPC/README.md.txt create mode 100644 build/html/_sources/index.rst.txt create mode 100644 build/html/_static/alabaster.css create mode 100644 build/html/_static/basic.css create mode 100644 build/html/_static/custom.css create mode 100644 build/html/_static/doctools.js create mode 100644 build/html/_static/documentation_options.js create mode 100644 build/html/_static/file.png create mode 100644 build/html/_static/jquery-3.5.1.js create mode 100644 build/html/_static/jquery.js create mode 100644 build/html/_static/language_data.js create mode 100644 build/html/_static/logo.png create mode 100644 build/html/_static/minus.png create mode 100644 build/html/_static/plus.png create mode 100644 build/html/_static/pygments.css create mode 100644 build/html/_static/searchtools.js create mode 100644 build/html/_static/underscore-1.13.1.js create mode 100644 build/html/_static/underscore.js create mode 100644 build/html/genindex.html create mode 100644 build/html/index.html create mode 100644 build/html/objects.inv create mode 100644 build/html/search.html create mode 100644 build/html/searchindex.js create mode 100644 build/latex/main.pdf create mode 100644 conf.py create mode 100644 index.html create mode 100644 index.rst create mode 100644 make.bat diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ed88099 --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/P01_Erste_Schritte_mit_C/solution/converter.c b/P01_Erste_Schritte_mit_C/solution/converter.c new file mode 100644 index 0000000..2538f6c --- /dev/null +++ b/P01_Erste_Schritte_mit_C/solution/converter.c @@ -0,0 +1,39 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include +#include + +#define ROW_COUNT 8 + +/** + * @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) if there is an input error. + */ +int main(char argc, char* argv){ + uint16_t i; + double rate; + + printf("Umwandlungskurs eingeben (1.00 BTC -> CHF): "); + if (1!= scanf("%lf", &rate)){ + printf("Ungültige Eingabe! Geben Sie eine Fliesskommazahl ein.\n"); + return 1; + } + + for(i = 1; i <= ROW_COUNT; i++) { + printf("%5d CHF\t<-->\t %5.5lf BTC\n", i*200, (double)i*200/rate); + } + return 0; +} \ No newline at end of file diff --git a/P01_Erste_Schritte_mit_C/solution/hello.c b/P01_Erste_Schritte_mit_C/solution/hello.c new file mode 100644 index 0000000..64d2c41 --- /dev/null +++ b/P01_Erste_Schritte_mit_C/solution/hello.c @@ -0,0 +1,25 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include + +/** + * @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. + */ +int main(char argc, char* argv[]) { + printf("hello world.\n"); + return 0; +} \ No newline at end of file diff --git a/P01_Erste_Schritte_mit_C/solution/wordcount.c b/P01_Erste_Schritte_mit_C/solution/wordcount.c new file mode 100644 index 0000000..0a9f182 --- /dev/null +++ b/P01_Erste_Schritte_mit_C/solution/wordcount.c @@ -0,0 +1,47 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include +#include + +/** + * @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(char argc, char* argv[]) { + uint16_t char_count = 0, word_count = 0; + char c; + uint8_t is_word = 0; + + printf("Eingabe: "); + c=getchar(); + while('\n' != c){ + char_count++; + if(c != ' ' && c !='\t'){ + if(! is_word){ + is_word = 1; + word_count++; + } + } else { + is_word = 0; + } + c = getchar(); + } + + printf("%d Zeichen, %d W\u00F6rter\n", char_count, word_count); + return 0; + + +} \ No newline at end of file diff --git a/P02_Funktionen_Datentyp_enum/README_solution.md b/P02_Funktionen_Datentyp_enum/README_solution.md new file mode 100644 index 0000000..5d5cf18 --- /dev/null +++ b/P02_Funktionen_Datentyp_enum/README_solution.md @@ -0,0 +1,125 @@ + +# Lösungsskizzen +## Aufgabe 1 +``` +/** + * Tage Pro Monat + * + * Das Programm liest einen Monat (1-12) und ein Jahr (1600-2400) ein und + * gibt die Anzahl der Tage dieses Monats aus. + * + * @author Gerrit Burkert, Adaptation bazz + * @version 15-FEB-2013, 16-OCT-2017, 17-OCT-2019, 16-FEB-2022 + */ + +#include +#include + +#define ERROR_IN_MONTH 1 +#define ERROR_IN_YEAR 2 + +///// Student Code + + + +// Konstante Werte fuer die Monate +// =============================== + +enum { JAN=1, FEB, MAR, APR, MAI, JUN, JUL, AUG, SEP, OKT, NOV, DEZ }; + + +// Eingabe pruefen (0 ist vom atoi als Fehelcode verwendet und darf nicht verwendet werden) +// =============== + +int gibIntWert(char *name, int von, int bis) { + + int wert; + char wertS[20]; // + + do { + printf("%s: ", name); + fgets(wertS, 20, stdin); + wert = atoi(wertS); + if (wert < von || wert > bis) { + printf("Der Wert muss zwischen %d und %d sein.\n", von, bis); + } else { + break; + } + } while(1); + return wert; +} + + +// Schaltjahr bestimmen +// ==================== + +int istSchaltjahr(int jahr){ + + if ( (jahr % 400 == 0) || ( (jahr %100 != 0) && (jahr % 4 ==0) ) ) + return 1; + else + return 0; +} + + +// Berechnung Anzahl Tage pro Monat +// ================================ + +int tageProMonat(int jahr, int monat) { + + int anzTage; + + // Tage pro Monat bestimmen + switch (monat) { + + // Monate mit 31 Tagen + case JAN: case MAR: case MAI: case JUL: case AUG: case OKT: case DEZ: + anzTage = 31; + break; + + // Monate mit 30 Tagen + case APR: case JUN: case SEP: case NOV: + anzTage = 30; + break; + + // Februar: 28 oder 29 Tage + case FEB: + + if (istSchaltjahr(jahr)) { + anzTage = 29; + } else { + anzTage = 28; + } + break; + } + + return anzTage; +} + +///// END Student Code + + +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; +} + +``` +## Aufgabe 2 +Alter bestehender Boilerplate Code + diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/java/read.java b/P04_Modularisieren_von_C_Code/work/modularize-solution/java/read.java new file mode 100644 index 0000000..35570de --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/java/read.java @@ -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; + } +} diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/java/rectang.java b/P04_Modularisieren_von_C_Code/work/modularize-solution/java/rectang.java new file mode 100644 index 0000000..34b1302 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/java/rectang.java @@ -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; + } +} diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/java/triangle.java b/P04_Modularisieren_von_C_Code/work/modularize-solution/java/triangle.java new file mode 100644 index 0000000..8c24aca --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/java/triangle.java @@ -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"); + } +} diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/src/read.c b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/read.c new file mode 100644 index 0000000..74c502a --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/read.c @@ -0,0 +1,94 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include +#include +#include "read.h" +#include "trace.h" + +//#define EOF (-1) // end of file + +/// end-of-line +#define EOL 10 // end of line + +// abnormal return values +/// parse-error +#define PARSE_ERROR (-1) + +// ASCII Codes +/// ascii code for space +#define ASCII_SPACE ' ' +/// ascii code for 0 +#define ASCII_DIGIT_0 '0' +/// ascii code for 9 +#define ASCII_DIGIT_9 '9' + +// conversion buffer +/// no buffer position sentry +#define NO_POS (-1) +/// buffer size +#define BUFFERSIZE (10) + +int getInt(int maxResult) +{ + TRACE("getInt(%d)", maxResult); + + char buffer[BUFFERSIZE] = { 0 }; + + int result = 0; + + // read line: up to EOL or EOF (i.e. error while reading) + int bytes = 0; + int input = getchar(); + while ((input != EOL) && (input != EOF)) { // read whole line + if (bytes < BUFFERSIZE) { // only buffer first n characters + buffer[bytes] = (char)input; + bytes++; + } else { + result = PARSE_ERROR; // exceed buffer size, continue read line + } + input = getchar(); + } + 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; +} diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/src/read.h b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/read.h new file mode 100644 index 0000000..e3f6192 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/read.h @@ -0,0 +1,27 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#ifndef READ_H +#define READ_H + +/// I/O error which leads to aborting. +#define READ_ERROR (-2) + +/** + * @brief Reads a line from stdin and tries to parse it as number (with optional leading and trailing spaces). + * @param[in] maxResult gives the limit for the number to be read. + * @returns the succesfully read and parsed number or a negative value if in error (READ_ERROR in case of I/O error). + */ +int getInt(int maxResult); + +#endif diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/src/rectang.c b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/rectang.c new file mode 100644 index 0000000..4cc8f4c --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/rectang.c @@ -0,0 +1,38 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include "rectang.h" +#include "trace.h" + +int rectangular(int a, int b, int c) +{ + TRACE("rectangular(%d, %d, %d)", a, b, c); + + int aS = a*a; + int bS = b*b; + int cS = c*c; + + int isRightAngled; + if ((a == 0) && (b == 0) && (c == 0)) + isRightAngled = 0; + else if ((aS + bS) == cS) + isRightAngled = 1; + else if ((aS + cS) == bS) + isRightAngled = 1; + else if ((bS + cS) == aS) + isRightAngled = 1; + else + isRightAngled = 0; + + return isRightAngled; +} diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/src/rectang.h b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/rectang.h new file mode 100644 index 0000000..38e3577 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/rectang.h @@ -0,0 +1,26 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#ifndef RECTANG_H +#define RECTANG_H + +/** + * @brief checks if the three lengths give a right-angled triangle. + * @param[in] a 1st side + * @param[in] b 2nd side + * @param[in] c 3rd side + * @returns 1 if right-angled, 0 otherwise + */ +int rectangular(int a, int b, int c); + +#endif diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/src/trace.h b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/trace.h new file mode 100644 index 0000000..56f5209 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/trace.h @@ -0,0 +1,26 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#ifndef TRACE_H +#define TRACE_H + +#include + +/** + * @brief TRACE macro. + * @param[in] MSG "..." format string literal. + * @param[in] ... optional arguments to the format string. + */ +#define TRACE(MSG, ...) do { (void)fprintf(stderr, "TRACE: " MSG "\n", ##__VA_ARGS__); } while(0) + +#endif diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/src/triangle.c b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/triangle.c new file mode 100644 index 0000000..b1cd649 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/src/triangle.c @@ -0,0 +1,78 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include +#include +#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) +{ + TRACE("main()"); + + while (1) { + (void)printf("\nDreiecksbestimmung (CTRL-C: Abbruch)\n\n"); + + int word = 0; + int a = 0; + int b = 0; + int c = 0; + + do { + (void)printf("Seite a: "); + word = getInt(MAX_NUMBER); + } + while ((word < 0) && (word != READ_ERROR)); + if (word >= 0) + a = word; + else + break; + + do { + (void)printf("Seite b: "); + word = getInt(MAX_NUMBER); + } + while ((word < 0) && (word != READ_ERROR)); + if (word >= 0) + b = word; + else + break; + + do { + (void)printf("Seite c: "); + word = getInt(MAX_NUMBER); + } + while ((word < 0) && (word != READ_ERROR)); + if (word >= 0) + c = word; + else + break; + + if (rectangular(a, b, c)) + (void)printf("-> Dreieck %d-%d-%d ist rechtwinklig\n", a, b, c); + else + (void)printf("-> Dreieck %d-%d-%d ist nicht rechtwinklig\n", a, b, c); + (void)printf("\n\n"); + } + (void)printf("\n\nbye bye\n\n"); + return EXIT_SUCCESS; +} diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-error.input b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-error.input new file mode 100644 index 0000000..ab2d76b --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-error.input @@ -0,0 +1,12 @@ + +a + a + a + 3 +4444 + 4444 + 44444444444444 + 4 + 4 + 5 + 5 diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-not-right-angled.input b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-not-right-angled.input new file mode 100644 index 0000000..07b2e27 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-not-right-angled.input @@ -0,0 +1,12 @@ +3 +4 +6 +5 +4 +4 +3 +5 +5 +33 +43 +55 diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-right-angled.input b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-right-angled.input new file mode 100644 index 0000000..fa866ac --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/stim-right-angled.input @@ -0,0 +1,12 @@ +3 +4 +5 +5 +4 +3 +3 +5 +4 +33 +44 +55 diff --git a/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/tests.c b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/tests.c new file mode 100644 index 0000000..699b64a --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/modularize-solution/tests/tests.c @@ -0,0 +1,205 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include +#include +#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 + ); +} diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/Makefile b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/Makefile new file mode 100644 index 0000000..d05b3d8 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/Makefile @@ -0,0 +1,57 @@ +SNP_SHARED_MAKEFILE := $(if $(SNP_SHARED_MAKEFILE),$(SNP_SHARED_MAKEFILE),"~/snp/shared.mk") + +TARGET := bin/dep2dot +SOURCES := src/main.c src/data.c src/output.c +TSTSOURCES := tests/tests.c + +include $(SNP_SHARED_MAKEFILE) + + +# DEPFILES := ... define a list of png file names: %.c -> %.c.png +# BEGIN-STUDENTS-TO-ADD-CODE +DEPFILES := $(SOURCES:%.c=%.c.png) +# END-STUDENTS-TO-ADD-CODE + + +# define dep target as .PHONEY +# BEGIN-STUDENTS-TO-ADD-CODE +.PHONY: dep +# 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 +dep: $(FULLTARGET) $(DEPFILES) + @echo "### $@ done ###" + +# BEGIN-STUDENTS-TO-ADD-CODE + + +# define new suffix rule for %.png depending on %.dot +# action: dot -Tpng $< >$@ || $(RM) $@ +# BEGIN-STUDENTS-TO-ADD-CODE +%.png: %.dot + dot -Tpng $< >$@ || $(RM) $@ + +# BEGIN-STUDENTS-TO-ADD-CODE + + +# define new suffix rule for %.dot depending on %.dep +# action: call $(TARGET) $(@:.dot=) <$< >$@ || $(RM) $@ +# BEGIN-STUDENTS-TO-ADD-CODE +%.dot: %.dep + $(TARGET) $(@:.dot=) <$< >$@ || $(RM) $@ + +# 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) + diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/mainpage.dox b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/mainpage.dox new file mode 100644 index 0000000..cd6b69a --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/mainpage.dox @@ -0,0 +1,8 @@ +/** + * @mainpage SNP - P04 Modularisation + * + * @section Purpose + * + * This is a lab for splitting functionality into multiple modules. + * + */ diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/data.c b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/data.c new file mode 100644 index 0000000..a9f28f8 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/data.c @@ -0,0 +1,149 @@ +/** + * @file + * @brief Implementation of the dependency file access. + */ +#include "data.h" +#include "error.h" +#include +#include +#include +#include +#include + +#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; +} diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/data.h b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/data.h new file mode 100644 index 0000000..ab1880b --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/data.h @@ -0,0 +1,72 @@ +/** + * @file + * @brief Access to the GCC produced dependency data (via -H command line option). + */ + +// begin of include guard +// BEGIN-STUDENTS-TO-ADD-CODE +#ifndef _DATA_H_ +#define _DATA_H_ +// END-STUDENTS-TO-ADD-CODE + + +// includes which are needed in this header file +// BEGIN-STUDENTS-TO-ADD-CODE +#include +// END-STUDENTS-TO-ADD-CODE + + + +/** + * @brief Directory container for file entries of the dependency file. + */ +// BEGIN-STUDENTS-TO-ADD-CODE +typedef struct { + const char *name; ///< @brief the path name of the directory as given by the GCC produced dependency file. +} dir_t; +// END-STUDENTS-TO-ADD-CODE + + + +/** + * @brief File container for the file entries of the dependency file. + */ +// BEGIN-STUDENTS-TO-ADD-CODE +typedef struct { + const char *name; ///< @brief The base name of the file from the GGC produced dependency file (i.e. the plain name, without any directory path). + size_t dir; ///< @brief The index of the directory entry which represents the path as given by the dependency file. + size_t level; ///< @brief The level as read out from the dependecy file. +} file_t; +// END-STUDENTS-TO-ADD-CODE + + + +/** + * @brief Overall container for all directories and all files from the dependency file. + */ +// BEGIN-STUDENTS-TO-ADD-CODE +typedef struct { + size_t n_dirs; ///< @brief The number of valid entries in the dirs list. + dir_t *dirs; ///< @brief The list of directories. + size_t n_files; ///< @brief The number of valid entries in the files list. + file_t *files; ///< @brief The list of files from the dependency file (the sequence is relevant to determine the dependencies). +} data_t; +// 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 +const data_t data_read_all(const char *root); +// END-STUDENTS-TO-ADD-CODE + + + +// end of include guard +// BEGIN-STUDENTS-TO-ADD-CODE +#endif // _DATA_H_ +// END-STUDENTS-TO-ADD-CODE diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/error.h b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/error.h new file mode 100644 index 0000000..3a38ab6 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/error.h @@ -0,0 +1,17 @@ +/** + * @file + * @brief Error handling convenience functions. + */ +#ifndef _ERROR_H_ +#define _ERROR_H_ + +#include +#include + +/** + * @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_ diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/main.c b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/main.c new file mode 100644 index 0000000..8cf35f9 --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/main.c @@ -0,0 +1,36 @@ + /* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab P04 dep2dot + */ +#include +#include + +#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.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.dot # from gcc -H ... file.c ... 2>file.dep\n"); + + output_dot(data_read_all(argv[1])); + + return EXIT_SUCCESS; +} diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/output.c b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/output.c new file mode 100644 index 0000000..3a3d33e --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/output.c @@ -0,0 +1,95 @@ +/** + * @file + * @brief Provides output functions for various file formats. + */ +#include "output.h" +#include +#include +#include + +/** + * @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 + printf(" "); + print_node(files[curr]); + printf(" -> "); + print_node(files[file]); + printf(";\n"); + // 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 + printf(" "); + print_node(data.files[file]); + printf(" [label=\"%s\"];\n", data.files[file].name); + // 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 + printf(" "); + print_node(data.files[file]); + printf(";\n"); + // 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"); +} + + diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/output.h b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/output.h new file mode 100644 index 0000000..e0a1b1e --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/src/output.h @@ -0,0 +1,20 @@ +/** + * @file + * @brief Provides output functions for various file formats. + */ +// define proper header file here, with include gaurd, etc. +// BEGIN-STUDENTS-TO-ADD-CODE +#ifndef _OUTPUT_H_ +#define _OUTPUT_H_ + +#include "data.h" + +/** + * @brief Produces DOT output of the dependencies given in data. + * @param data [IN] Container of the dependenciy data. + */ +void output_dot(const data_t data); + + +#endif // _OUTPUT_H_ +// END-STUDENTS-TO-ADD-CODE diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/dep.input b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/dep.input new file mode 100644 index 0000000..d21a54c --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/dep.input @@ -0,0 +1,7 @@ +Test File +. dir1/h1_1 +.. dir1/h1_1_2 +. dir1/h1_2 +. dir2/h2_1 +.. dir1/h1_1 +Done diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/no_dep.input b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/no_dep.input new file mode 100644 index 0000000..6565e2d --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/no_dep.input @@ -0,0 +1,2 @@ +Test File +Done diff --git a/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/tests.c b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/tests.c new file mode 100644 index 0000000..119800f --- /dev/null +++ b/P04_Modularisieren_von_C_Code/work/show-dependencies-solution/tests/tests.c @@ -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 +#include +#include +#include +#include +#include +#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 + ); +} diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/Makefile b/P05_TicTacToe/work/tic-tac-toe-solution/Makefile new file mode 100644 index 0000000..a29984f --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/Makefile @@ -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) + diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/mainpage.dox b/P05_TicTacToe/work/tic-tac-toe-solution/mainpage.dox new file mode 100644 index 0000000..b07e65d --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/mainpage.dox @@ -0,0 +1,8 @@ +/** + * @mainpage SNP - P05 Tic Tac Toe Game + * + * @section Purpose + * + * This is a lab on usage of arrays. + * + */ diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/src/control.c b/P05_TicTacToe/work/tic-tac-toe-solution/src/control.c new file mode 100644 index 0000000..ccae643 --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/src/control.c @@ -0,0 +1,154 @@ +/** + * @file + * @brief Implementation + */ +#include "control.h" +#include "model.h" +#include + +/** + * @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 } }; + } +} diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/src/control.h b/P05_TicTacToe/work/tic-tac-toe-solution/src/control.h new file mode 100644 index 0000000..613bd8f --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/src/control.h @@ -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_ diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/src/main.c b/P05_TicTacToe/work/tic-tac-toe-solution/src/main.c new file mode 100644 index 0000000..7e66b7e --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/src/main.c @@ -0,0 +1,39 @@ + /* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab P04 dep2dot + */ +#include +#include + +#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; +} diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/src/model.c b/P05_TicTacToe/work/tic-tac-toe-solution/src/model.c new file mode 100644 index 0000000..b544f17 --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/src/model.c @@ -0,0 +1,154 @@ +/** + * @file + * @brief Implementation + */ +#include "model.h" +#include + +/** + * @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 + instance->board[pos.row][pos.col] = state; + // 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 + for(size_t row = 0; row < MODEL_SIZE; row++) { + for(size_t col = 0; col < MODEL_SIZE; col++) { + instance->board[row][col] = model_state_none; + } + } + // 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 instance->board[pos.row][pos.col]; // stub return model_state_none; + // 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 + for(size_t row = 0; row < MODEL_SIZE; row++) { + for (size_t col = 0; col < MODEL_SIZE; col++) { + if (instance->board[row][col] == model_state_none) { + return 1; + } + } + } + // 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; +} diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/src/model.h b/P05_TicTacToe/work/tic-tac-toe-solution/src/model.h new file mode 100644 index 0000000..51e2678 --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/src/model.h @@ -0,0 +1,108 @@ +/** + * @file + * @brief MVC - Model instance + */ +#ifndef _MODEL_H_ +#define _MODEL_H_ + +#include + +#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_ diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/src/view.c b/P05_TicTacToe/work/tic-tac-toe-solution/src/view.c new file mode 100644 index 0000000..a248c49 --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/src/view.c @@ -0,0 +1,255 @@ +/** + * @file + * @brief Implementation + */ +#include "view.h" +#include "control.h" + +#include // assert() +#include // various i/o +#include // isdigit() +#include // STDIN_FILENO, isatty() +#include // 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); + } +} + diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/src/view.h b/P05_TicTacToe/work/tic-tac-toe-solution/src/view.h new file mode 100644 index 0000000..84cee90 --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/src/view.h @@ -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_ diff --git a/P05_TicTacToe/work/tic-tac-toe-solution/tests/tests.c b/P05_TicTacToe/work/tic-tac-toe-solution/tests/tests.c new file mode 100644 index 0000000..f2d15df --- /dev/null +++ b/P05_TicTacToe/work/tic-tac-toe-solution/tests/tests.c @@ -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 +#include +#include +#include +#include +#include +#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 + ); +} diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/Makefile b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/Makefile new file mode 100644 index 0000000..95121b9 --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/Makefile @@ -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 := src/person.c src/list.c +# END-STUDENTS-TO-ADD-CODE +SOURCES := src/main.c $(MODULES) +TSTSOURCES := tests/tests.c $(MODULES) + + +include $(SNP_SHARED_MAKEFILE) + +# CFLAGS += -Werror diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/mainpage.dox b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/mainpage.dox new file mode 100644 index 0000000..05d1c72 --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/mainpage.dox @@ -0,0 +1,8 @@ +/** + * @mainpage SNP - P07 Linked List + * + * @section Purpose + * + * This is a lab on usage of arrays. + * + */ diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/list.c b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/list.c new file mode 100644 index 0000000..49d2d51 --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/list.c @@ -0,0 +1,103 @@ +#include +#include +#include +#include "list.h" + +static node_t anchor; + +static int is_anchor(const node_t *node) +{ + return node == &anchor; +} + +static void remove_next(node_t *at) +{ + assert(at); + assert(at->next); + if (!is_anchor(at->next)) { + node_t *next = at->next->next; + free(at->next); + at->next = next; + } +} + +static node_t *find_insert(const person_t *p) +{ + assert(p); + node_t *last = &anchor; + for(node_t *n = anchor.next; !is_anchor(n); last = n, n = n->next) { + int res = person_compare(&(n->content), p); + if (res == 0) { + return NULL; // *** EARLY RETURN ***// // already part of the list + } else if (res > 0) { + break; // the predecessor is the insert point + } + } + return last; +} + +static node_t *find_remove(const person_t *p) +{ + assert(p); + node_t *last = &anchor; + for(node_t *n = anchor.next; !is_anchor(n); last = n, n = n->next) { + int res = person_compare(&(n->content), p); + if (res == 0) { + break; // the predecessor is the remove point + } + } + return is_anchor(last->next) ? NULL : last; +} + +const node_t *list_anchor(void) +{ + return &anchor; +} + +const node_t *list_init() +{ + anchor.next = &anchor; + return &anchor; +} + +int list_insert(const person_t *p) +{ + node_t *at = find_insert(p); + node_t *insert = NULL; + if (at) { + insert = malloc(sizeof(node_t)); + if (insert) { + insert->content = *p; + insert->next = at->next; + at->next = insert; + } + } + return at && insert; +} + +int list_remove(const person_t *p) +{ + node_t *at = find_remove(p); + if (at) { + remove_next(at); + } + return at != NULL; +} + +void list_clear(void) +{ + node_t *n = &anchor; + do { + remove_next(n); + } while (!is_anchor(n->next)); +} + +void list_show(void) +{ + node_t *n = &anchor; + do { + if (!is_anchor(n)) printf("%20s %20s %u\n", n->content.name, n->content.first_name, n->content.age); + n = n->next; + } while(!is_anchor(n)); +} + diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/list.h b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/list.h new file mode 100644 index 0000000..3703d97 --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/list.h @@ -0,0 +1,17 @@ +#ifndef _LIST_H_ +#define _LIST_H_ + +#include "person.h" + +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; + +const node_t *list_init(); +int list_insert(const person_t *p); +int list_remove(const person_t *p); +void list_clear(void); +void list_show(void); + +#endif // _LIST_H_ diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/main.c b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/main.c new file mode 100644 index 0000000..77aefb4 --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/main.c @@ -0,0 +1,68 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Lab implementation + */ +#include +#include + +#include "person.h" +#include "list.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 + list_init(); + person_t p; + int show_menu = 1; + while(1) { + if (show_menu) printf("I(nsert), R(emove), S(how), C(lear), E(nd)\n"); + show_menu = 1; + int op = getchar(); + switch(op) { + case 'I': case 'i': + if (!person_read(&p) || !list_insert(&p)) { + printf("failed to insert person\n"); + } + break; + case 'R': case 'r': + if (!person_read(&p) || !list_remove(&p)) { + printf("failed to remove person\n"); + } + break; + case 'S': case 's': + list_show(); + break; + case 'C': case 'c': + list_clear(); + break; + case EOF: + case 'E': case 'e': + return EXIT_SUCCESS; // *** EARLY RETURN *** // + break; + case ' ': case '\n': + show_menu = 0; + break; + default: + printf("Unknown command: %c\n", op); + break; + } + } + + // END-STUDENTS-TO-ADD-CODE + return EXIT_SUCCESS; +} diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/person.c b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/person.c new file mode 100644 index 0000000..21c39bd --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/person.c @@ -0,0 +1,22 @@ +#include +#include +#include + +#include "person.h" + +int person_compare(const person_t *a, const person_t *b) +{ + assert(a); + assert(b); + int res = strncmp(a->name, b->name, NAME_LEN); + if (res == 0) res = strncmp(a->first_name, b->first_name, NAME_LEN); + if (res == 0) res = a->age - b->age; + return res; +} + +int person_read(person_t *p) +{ + assert(p); + assert(NAME_LEN == 20); + return scanf("%19s %19s %u", p->name, p->first_name, &(p->age)) == 3; +} diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/person.h b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/person.h new file mode 100644 index 0000000..79bd22e --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/src/person.h @@ -0,0 +1,26 @@ +#ifndef _PERSON_H_ +#define _PERSON_H_ + +#define NAME_LEN 20 + +typedef struct { + char name[NAME_LEN]; + char first_name[NAME_LEN]; + unsigned int age; +} person_t; + +/** + * @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); + +int person_read(person_t *p); + +#endif // _PERSON_H_ diff --git a/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/tests/tests.c b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/tests/tests.c new file mode 100644 index 0000000..addb7e4 --- /dev/null +++ b/P06_Personen_Verwaltung_Linked_List/work/personen-verwaltung-solution/tests/tests.c @@ -0,0 +1,299 @@ +/* ---------------------------------------------------------------------------- + * -- _____ ______ _____ - + * -- |_ _| | ____|/ ____| - + * -- | | _ __ | |__ | (___ Institute of Embedded Systems - + * -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur - + * -- _| |_| | | | |____ ____) | (University of Applied Sciences) - + * -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland - + * ---------------------------------------------------------------------------- + */ +/** + * @file + * @brief Test suite for the given package. + */ +#include +#include +#include +#include +#include +#include +#include "test_utils.h" +#include "person.h" +#include "list.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 + person_t a = { "a", "a", 1 }; + person_t b = { "a", "a", 2 }; + person_t c = { "a", "b", 1 }; + person_t d = { "a", "b", 2 }; + person_t e = { "b", "a", 1 }; + person_t f = { "b", "a", 2 }; + person_t g = { "b", "b", 1 }; + person_t h = { "b", "b", 2 }; + + // act & assert + CU_ASSERT_TRUE(person_compare(&a, &a) == 0); + CU_ASSERT_TRUE(person_compare(&a, &b) < 0); + CU_ASSERT_TRUE(person_compare(&a, &c) < 0); + CU_ASSERT_TRUE(person_compare(&a, &d) < 0); + CU_ASSERT_TRUE(person_compare(&a, &e) < 0); + CU_ASSERT_TRUE(person_compare(&a, &f) < 0); + CU_ASSERT_TRUE(person_compare(&a, &g) < 0); + CU_ASSERT_TRUE(person_compare(&a, &h) < 0); + + CU_ASSERT_TRUE(person_compare(&b, &a) > 0); + CU_ASSERT_TRUE(person_compare(&b, &b) == 0); + CU_ASSERT_TRUE(person_compare(&b, &c) < 0); + CU_ASSERT_TRUE(person_compare(&b, &d) < 0); + CU_ASSERT_TRUE(person_compare(&b, &e) < 0); + CU_ASSERT_TRUE(person_compare(&b, &f) < 0); + CU_ASSERT_TRUE(person_compare(&b, &g) < 0); + CU_ASSERT_TRUE(person_compare(&b, &h) < 0); + + CU_ASSERT_TRUE(person_compare(&c, &a) > 0); + CU_ASSERT_TRUE(person_compare(&c, &b) > 0); + CU_ASSERT_TRUE(person_compare(&c, &c) == 0); + CU_ASSERT_TRUE(person_compare(&c, &d) < 0); + CU_ASSERT_TRUE(person_compare(&c, &e) < 0); + CU_ASSERT_TRUE(person_compare(&c, &f) < 0); + CU_ASSERT_TRUE(person_compare(&c, &g) < 0); + CU_ASSERT_TRUE(person_compare(&c, &h) < 0); + + CU_ASSERT_TRUE(person_compare(&d, &a) > 0); + CU_ASSERT_TRUE(person_compare(&d, &b) > 0); + CU_ASSERT_TRUE(person_compare(&d, &c) > 0); + CU_ASSERT_TRUE(person_compare(&d, &d) == 0); + CU_ASSERT_TRUE(person_compare(&d, &e) < 0); + CU_ASSERT_TRUE(person_compare(&d, &f) < 0); + CU_ASSERT_TRUE(person_compare(&d, &g) < 0); + CU_ASSERT_TRUE(person_compare(&d, &h) < 0); + + CU_ASSERT_TRUE(person_compare(&e, &a) > 0); + CU_ASSERT_TRUE(person_compare(&e, &b) > 0); + CU_ASSERT_TRUE(person_compare(&e, &c) > 0); + CU_ASSERT_TRUE(person_compare(&e, &d) > 0); + CU_ASSERT_TRUE(person_compare(&e, &e) == 0); + CU_ASSERT_TRUE(person_compare(&e, &f) < 0); + CU_ASSERT_TRUE(person_compare(&e, &g) < 0); + CU_ASSERT_TRUE(person_compare(&e, &h) < 0); + + CU_ASSERT_TRUE(person_compare(&f, &a) > 0); + CU_ASSERT_TRUE(person_compare(&f, &b) > 0); + CU_ASSERT_TRUE(person_compare(&f, &c) > 0); + CU_ASSERT_TRUE(person_compare(&f, &d) > 0); + CU_ASSERT_TRUE(person_compare(&f, &e) > 0); + CU_ASSERT_TRUE(person_compare(&f, &f) == 0); + CU_ASSERT_TRUE(person_compare(&f, &g) < 0); + CU_ASSERT_TRUE(person_compare(&f, &h) < 0); + + CU_ASSERT_TRUE(person_compare(&g, &a) > 0); + CU_ASSERT_TRUE(person_compare(&g, &b) > 0); + CU_ASSERT_TRUE(person_compare(&g, &c) > 0); + CU_ASSERT_TRUE(person_compare(&g, &d) > 0); + CU_ASSERT_TRUE(person_compare(&g, &e) > 0); + CU_ASSERT_TRUE(person_compare(&g, &f) > 0); + CU_ASSERT_TRUE(person_compare(&g, &g) == 0); + CU_ASSERT_TRUE(person_compare(&g, &h) < 0); + + CU_ASSERT_TRUE(person_compare(&h, &a) > 0); + CU_ASSERT_TRUE(person_compare(&h, &b) > 0); + CU_ASSERT_TRUE(person_compare(&h, &c) > 0); + CU_ASSERT_TRUE(person_compare(&h, &d) > 0); + CU_ASSERT_TRUE(person_compare(&h, &e) > 0); + CU_ASSERT_TRUE(person_compare(&h, &f) > 0); + CU_ASSERT_TRUE(person_compare(&h, &g) > 0); + CU_ASSERT_TRUE(person_compare(&h, &h) == 0); + + // END-STUDENTS-TO-ADD-CODE +} + +static void test_list_insert(void) +{ + // BEGIN-STUDENTS-TO-ADD-CODE + // arrange + const node_t *anchor = list_init(); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next); + + // act & assert: insert one + person_t p1 = { "a", "b", 123 }; + CU_ASSERT_TRUE(list_insert(&p1)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + + // act & assert: insert a second after first + person_t p2 = { "a", "b", 124 }; + CU_ASSERT_TRUE(list_insert(&p2)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->content), &p2) == 0); + + // act & assert: insert a second before first + person_t p3 = { "a", "b", 122 }; + CU_ASSERT_TRUE(list_insert(&p3)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p3) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->content), &p1) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->next->content), &p2) == 0); + + // act & assert: reject inserting same + CU_ASSERT_FALSE(list_insert(&p1)); + // unchanged + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next->next->next); + // unchanged + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p3) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->content), &p1) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->next->content), &p2) == 0); + + // END-STUDENTS-TO-ADD-CODE +} + +static void test_list_remove(void) +{ + // BEGIN-STUDENTS-TO-ADD-CODE + // arrange + const node_t *anchor = list_init(); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next); + + // act & assert: remove one + person_t p1 = { "a", "b", 123 }; + CU_ASSERT_TRUE(list_insert(&p1)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + // remove same + CU_ASSERT_TRUE_FATAL(list_remove(&p1)); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next); + + // act & assert: failed to remove + CU_ASSERT_TRUE(list_insert(&p1)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + // remove not found + person_t p2 = { "a", "b", 124 }; + CU_ASSERT_FALSE_FATAL(list_remove(&p2)); + // unchanged + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + + // act & assert: remove last + CU_ASSERT_TRUE(list_insert(&p2)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->content), &p2) == 0); + CU_ASSERT_TRUE_FATAL(list_remove(&p2)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + + // act & assert: remove first + CU_ASSERT_TRUE(list_insert(&p2)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->content), &p2) == 0); + CU_ASSERT_TRUE_FATAL(list_remove(&p1)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p2) == 0); + + // END-STUDENTS-TO-ADD-CODE +} + +static void test_list_clear(void) +{ + // BEGIN-STUDENTS-TO-ADD-CODE + // arrange + const node_t *anchor = list_init(); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next); + + // act & assert: empty list + list_clear(); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next); + + // act & assert: clear list of one + person_t p1 = { "a", "b", 123 }; + CU_ASSERT_TRUE(list_insert(&p1)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + list_clear(); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next); + + // act & assert: clear list of two + person_t p2 = { "a", "b", 124 }; + CU_ASSERT_TRUE(list_insert(&p1)); + CU_ASSERT_TRUE(list_insert(&p2)); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next); + CU_ASSERT_PTR_NOT_EQUAL(anchor, anchor->next->next); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next->next->next); + CU_ASSERT_TRUE(person_compare(&(anchor->next->content), &p1) == 0); + CU_ASSERT_TRUE(person_compare(&(anchor->next->next->content), &p2) == 0); + list_clear(); + CU_ASSERT_PTR_EQUAL(anchor, anchor->next); + + // 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 + ); +} diff --git a/P07_Prozesse_und_Threads/Solution/A03.dot b/P07_Prozesse_und_Threads/Solution/A03.dot new file mode 100644 index 0000000..d56eab7 --- /dev/null +++ b/P07_Prozesse_und_Threads/Solution/A03.dot @@ -0,0 +1,76 @@ +digraph ProcessHierarchie { + + P1[label="P01\nfork()"]; + + P11[label="P01\nfork()"]; + P12[label="P02\nfork()"]; + + P111[label="P01\nfork()"]; + P112[label="P03\nfork()"]; + P121[label="P02\nfork()"]; + P122[label="P04\nfork()"]; + + P1111[label="P01\nfork()"]; + P1112[label="P05\nfork()"]; + P1121[label="P03\nfork()"]; + P1122[label="P06\nfork()"]; + P1211[label="P02\nfork()"]; + P1212[label="P07\nfork()"]; + P1221[label="P04\nfork()"]; + P1222[label="P08\nfork()"]; + + P11111[label="P01"]; + P11112[label="P09"]; + P11121[label="P05"]; + P11122[label="P10"]; + P11211[label="P03"]; + P11212[label="P11"]; + P11221[label="P06"]; + P11222[label="P12"]; + P12111[label="P02"]; + P12112[label="P13"]; + P12121[label="P07"]; + P12122[label="P14"]; + P12211[label="P04"]; + P12212[label="P15"]; + P12221[label="P08"]; + P12222[label="P16"]; + + P1 -> P11 + P1 -> P12 + + P11 -> P111 + P11 -> P112 + P12 -> P121 + P12 -> P122 + + P111 -> P1111 + P111 -> P1112 + P112 -> P1121 + P112 -> P1122 + P121 -> P1211 + P121 -> P1212 + P122 -> P1221 + P122 -> P1222 + + P1111 -> P11111 + P1111 -> P11112 + P1112 -> P11121 + P1112 -> P11122 + P1121 -> P11211 + P1121 -> P11212 + P1122 -> P11221 + P1122 -> P11222 + P1211 -> P12111 + P1211 -> P12112 + P1212 -> P12121 + P1212 -> P12122 + P1221 -> P12211 + P1221 -> P12212 + P1222 -> P12221 + P1222 -> P12222 + + { rank="same"; P11; P12; } +# { rank="same"; P1; P11; P111; P1111; P11111; } + +} diff --git a/P07_Prozesse_und_Threads/Solution/A03.png b/P07_Prozesse_und_Threads/Solution/A03.png new file mode 100644 index 0000000000000000000000000000000000000000..0d954161f99b2a56f0c97ee89180ee76923fdd5b GIT binary patch literal 79253 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