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

After

Width:  |  Height:  |  Size: 1.7 KiB

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

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

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

After

Width:  |  Height:  |  Size: 3.2 KiB

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

After

Width:  |  Height:  |  Size: 7.7 KiB

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

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

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

After

Width:  |  Height:  |  Size: 1.6 KiB

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

After

Width:  |  Height:  |  Size: 53 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

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

After

Width:  |  Height:  |  Size: 1.9 KiB

+9
View File
@@ -0,0 +1,9 @@
SNP_SHARED_MAKEFILE := $(if $(SNP_SHARED_MAKEFILE),$(SNP_SHARED_MAKEFILE),"~/snp/shared.mk")
TARGET := bin/tic-tac-toe
MODULES := src/model.c src/view.c src/control.c
SOURCES := src/main.c $(MODULES)
TSTSOURCES := tests/tests.c $(MODULES)
include $(SNP_SHARED_MAKEFILE)
@@ -0,0 +1,8 @@
/**
* @mainpage SNP - P05 Tic Tac Toe Game
*
* @section Purpose
*
* This is a lab on usage of arrays.
*
*/
@@ -0,0 +1,154 @@
/**
* @file
* @brief Implementation
*/
#include "control.h"
#include "model.h"
#include <assert.h>
/**
* @brief Conversion from control field number (1...9) to 0-based model position (row, col).
* @param cell [IN] Field number (1...9).
* @return Returns the position of the field.
* @remark Asserts proper field range.
*/
static model_pos_t get_pos(size_t cell)
{
assert(1 <= cell && cell <= 9);
model_pos_t pos = { (cell - 1) / 3, (cell - 1) % 3 };
return pos;
}
/**
* @brief Conversion from control player to model state.
* @param player [IN] Control player value to convert.
* @return Returns the matching model state.
* @remark No assertion is done - defaults to model_state_none.
*/
static model_state_t get_state(control_player_t player)
{
switch(player) {
case control_player_a: return model_state_a;
case control_player_b: return model_state_b;
default: return model_state_none;
}
}
/**
* @brief Conversion from 0-based model position (row, col) to control field number (1...9).
* @param pos [IN] 0-based model position (row,col).
* @return The control filed number (1...9)
* @remark Asserts proper position range.
*/
static size_t get_cell(model_pos_t pos)
{
assert(pos.row < 3);
assert(pos.col < 3);
return 1 + pos.row * 3 + pos.col;
}
/**
* @brief Conversion from model state to control player.
* @param state [IN] Model state to convert
* @return Returns the matching control player value.
* @remark No assertion is done - defaults to control_no_player.
*/
static control_player_t get_player(model_state_t state)
{
switch(state) {
case model_state_a: return control_player_a;
case model_state_b: return control_player_b;
default: return control_no_player;
}
}
/**
* @brief Queries if a move is possible.
* @param instance [INOUT] The instance which holds the state.
* @return Returns 0 if no move is possible any more, otherwise 1.
*/
static int control_can_move(control_t *instance)
{
assert(instance);
return model_can_move(instance->model);
}
// public API function which is documented in the header file.
void control_init(control_t *instance, model_t *model)
{
assert(instance);
assert(model);
instance->player = control_player_a;
instance->model = model;
}
// public API function which is documented in the header file.
void control_move(control_t *instance, size_t cell)
{
assert(instance);
if (model_move(instance->model, get_pos(cell), get_state(instance->player))) {
if (control_can_move(instance)) {
switch(instance->player) {
case control_player_a:
instance->player = control_player_b;
break;
case control_player_b:
instance->player = control_player_a;
break;
default:
break;
}
} else {
instance->player = control_no_player;
}
}
}
// public API function which is documented in the header file.
control_player_t control_get_winner(control_t *instance)
{
assert(instance);
return get_player(model_get_winner(instance->model));
}
// public API function which is documented in the header file.
control_player_t control_get_player(control_t *instance)
{
assert(instance);
return instance->player;
}
// public API function which is documented in the header file.
control_player_t control_get_state(control_t *instance, size_t cell)
{
assert(instance);
return get_player(model_get_state(instance->model, get_pos(cell)));
}
// public API function which is documented in the header file.
control_line_t control_get_win(control_t *instance)
{
assert(instance);
if (control_get_winner(instance) == control_no_player) {
control_line_t no_win = { 0 };
return no_win;
}
model_line_t line = model_get_win_line(instance->model);
assert(line.dir != model_dir_none);
size_t start_cell = get_cell(line.start);
switch(line.dir) {
case model_dir_h:
return (control_line_t) { { start_cell, start_cell + 1, start_cell + 2 } };
case model_dir_v:
return (control_line_t) { { start_cell, start_cell + 3, start_cell + 6 } };
case model_dir_d:
if (start_cell == 1) {
return (control_line_t) { { start_cell, start_cell + 4, start_cell + 8 } };
} else {
return (control_line_t) { { start_cell, start_cell + 2, start_cell + 6 } };
}
default:
return (control_line_t) { { 1, 1, 1 } };
}
}
@@ -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_
+39
View File
@@ -0,0 +1,39 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Lab P04 dep2dot
*/
#include <stdio.h>
#include <stdlib.h>
#include "view.h"
#include "model.h"
#include "control.h"
/**
* @brief main function
* @param argc [in] number of entries in argv
* @param argv [in] program name plus command line arguments
* @returns returns success if valid date is given, failure otherwise
*/
int main(int argc, const char *argv[])
{
view_t view;
control_t control;
model_t model;
model_init(&model);
control_init(&control, &model);
view_init(&view, &control);
view_run(&view);
return EXIT_SUCCESS;
}
+165
View File
@@ -0,0 +1,165 @@
/**
* @file
* @brief Implementation
*/
#include "model.h"
#include <assert.h>
/**
* @brief Asserts that the position is in range.
* @param [IN] The position to check.
*/
static void assert_pos(model_pos_t pos)
{
assert(pos.row < MODEL_SIZE);
assert(pos.col < MODEL_SIZE);
}
/**
* @brief Sets the field on the board to the given state.
* @param instance [INOUT] The instance which holds the state.
* @param pos [IN] The affected field.
* @param state [IN] The new state of the field.
*/
static void set_state(model_t *instance, model_pos_t pos, model_state_t state)
{
assert_pos(pos);
// Instructions to the students:
// set the field of the board to the new state
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
}
// public API function which is documented in the header file.
model_pos_t model_pos(size_t row, size_t col)
{
return (model_pos_t){row, col};
}
// public API function which is documented in the header file.
void model_init(model_t *instance)
{
assert(instance);
// Instructions to the students:
// set all fields of the board to model_state_none
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
}
// public API function which is documented in the header file.
model_state_t model_get_state(model_t *instance, model_pos_t pos)
{
assert(instance);
assert_pos(pos);
// Instructions to the students:
// replace the stub implementation my access to the field at the given position.
// BEGIN-STUDENTS-TO-ADD-CODE
return model_state_none; // stub
// END-STUDENTS-TO-ADD-CODE
}
// public API function which is documented in the header file.
model_line_t model_get_win_line(model_t *instance)
{
assert(instance);
model_state_t anchor;
// horizontal
for(size_t row = 0; row < MODEL_SIZE; row++) {
anchor = model_get_state(instance, model_pos(row, 0));
if (anchor != model_state_none
&& anchor == model_get_state(instance, model_pos(row, 1))
&& anchor == model_get_state(instance, model_pos(row, 2))) {
return (model_line_t) { model_dir_h, { row, 0 } };
}
}
// vertical
for(size_t col = 0; col < MODEL_SIZE; col++) {
anchor = model_get_state(instance, model_pos(0, col));
if (anchor != model_state_none
&& anchor == model_get_state(instance, model_pos(1, col))
&& anchor == model_get_state(instance, model_pos(2, col))) {
return (model_line_t) { model_dir_v, { 0, col } };
}
}
// diagonal
anchor = model_get_state(instance, model_pos(1, 1));
if (anchor != model_state_none) {
if (anchor == model_get_state(instance, model_pos(0, 0)) && anchor == model_get_state(instance, model_pos(2, 2))) {
return (model_line_t) { model_dir_d, { 0, 0 } };
}
if (anchor == model_get_state(instance, model_pos(2, 0)) && anchor == model_get_state(instance, model_pos(0, 2))) {
return (model_line_t) { model_dir_d, { 0, 2 } };
}
}
// fallback
return (model_line_t) { model_dir_none, { 0, 0 } };
}
// public API function which is documented in the header file.
model_state_t model_get_winner(model_t *instance)
{
assert(instance);
model_line_t line = model_get_win_line(instance);
return line.dir == model_dir_none
? model_state_none
: model_get_state(instance, model_pos(line.start.row, line.start.col))
;
}
// public API function which is documented in the header file.
int model_can_move(model_t *instance)
{
assert(instance);
if (model_get_winner(instance) == model_state_none) {
// Instructions to the students:
// scan all fields: return 1 with first field which equals model_state_none
// BEGIN-STUDENTS-TO-ADD-CODE
// END-STUDENTS-TO-ADD-CODE
}
return 0;
}
// public API function which is documented in the header file.
int model_move(model_t *instance, model_pos_t pos, model_state_t state)
{
assert(instance);
assert_pos(pos);
if (model_get_state(instance, pos) == model_state_none && model_can_move(instance)) {
set_state(instance, pos, state);
return 1;
}
return 0;
}
+108
View File
@@ -0,0 +1,108 @@
/**
* @file
* @brief MVC - Model instance
*/
#ifndef _MODEL_H_
#define _MODEL_H_
#include <stdlib.h>
#define MODEL_SIZE 3 ///< size of the game to avoid magic numbers in the code (not meant to modify)
/**
* @brief The position on the board.
*/
typedef struct {
size_t row; ///< The row (0-based).
size_t col; ///< The column (0-based).
} model_pos_t;
/**
* @brief Winner line direction - the winner line is given together with the start position.
*/
typedef enum {
model_dir_none, ///< no winner line
model_dir_h, ///< horizontal
model_dir_v, ///< vertical
model_dir_d, ///< diagonal
} model_dir_t;
/**
* @brief The Winner line (if any).
*/
typedef struct {
model_dir_t dir; ///< the winner line direction (if any)
model_pos_t start; ///< the start position of the winner line
} model_line_t;
/**
* @brief The state of a field.
*/
typedef enum {
model_state_none, ///< field available to play
model_state_a, ///< field already played
model_state_b, ///< field already played
} model_state_t;
/**
* @brief The instance type.
*/
typedef struct {
model_state_t board[MODEL_SIZE][MODEL_SIZE]; ///< the play board
} model_t;
/**
* @brief Convert to row and col to position.
* @param row [IN] position parameter
* @param col [IN] position parameter
* @return Returns the position given be row and col parameters.
*/
model_pos_t model_pos(size_t row, size_t col);
/**
* @brief Constructor: initialize the instance memory.
* @param instance [INOUT] The instance which holds the state.
*/
void model_init(model_t *instance);
/**
* @brief Queries the state of the given field.
* @param instance [INOUT] The instance which holds the state.
* @param pos [IN] The affected field.
* @return Returns the state of the field.
*/
model_state_t model_get_state(model_t *instance, model_pos_t pos);
/**
* @brief Queries the winner (if any).
* @param instance [INOUT] The instance which holds the state.
* @return Returns the wining player or model_state_none if no winner (yet).
*/
model_state_t model_get_winner(model_t *instance);
/**
* @brief Queries if a move is possible (i.e. not won yet and any field available?).
* @param instance [INOUT] The instance which holds the state.
* @return Returns 0 if no move possible, 1 otherwise.
*/
int model_can_move(model_t *instance);
/**
* @brief Do a move if possible.
* @param instance [INOUT] The instance which holds the state.
* @param pos [IN] The field to play.
* @param state [IN] The new state (only model_state_a and model_state_b allowed).
* @return Returns if the attempt to move was successful.
* @remark Does only succeed if not yet won and if the field is available.
*/
int model_move(model_t *instance, model_pos_t pos, model_state_t state);
/**
* @brief Gets the winner line (if any).
* @param instance [INOUT] The instance which holds the state.
* @returns The line which wins (if any).
* @remark The start position is 0/0, 1/0, 2/0 for horizontal, 0/0, 0/1, 0/2 for vertical, 0/0, 0/2 for diagonal.
*/
model_line_t model_get_win_line(model_t *instance);
#endif // _MODEL_H_
+255
View File
@@ -0,0 +1,255 @@
/**
* @file
* @brief Implementation
*/
#include "view.h"
#include "control.h"
#include <assert.h> // assert()
#include <stdio.h> // various i/o
#include <ctype.h> // isdigit()
#include <unistd.h> // STDIN_FILENO, isatty()
#include <termios.h> // tcgetattr(), tcsetattr()
#define EXIT '0' ///< the UI exit request
#define CLS "\033[2J" ///< ANSI termial CSI sequence for clear screen
#define AVAILABLE "\033[40m" ///< ANSI termial CSI sequence for available fields (black)
#define PLAYER_A "\033[42m" ///< ANSI termial CSI sequence for one player (green)
#define PLAYER_B "\033[41m" ///< ANSI termial CSI sequence for the other player (red)
#define GAP "\033[47m" ///< ANSI termial CSI sequence for boarder (white)
#define RESET "\033[0m" ///< ANSI termial CSI sequence to reset all settings
#define CELL_WIDTH 10 ///< rendering parameter: columns per cell
#define CELL_HEIGHT 5 ///< rendering parameter: rows per cell
#define GAP_WIDTH 4 ///< rendering parameter: columns per border
#define GAP_HEIGHT 2 ///< rendering parameter: rows per boarder
#define SIZE 3 ///< size of the game to avoid magic numbers in the code (not meant to modify)
#define CELLS (SIZE * SIZE) ///< size of the game to avoid magic numbers in the code (not meant to modify)
/**
* @brief Position the cursor for further output.
* @param row [IN] position parameter
* @param col [IN] position parameter
*/
static void goto_pos(size_t row, size_t col)
{
printf("\033[%zd;%zdH", row, col);
}
/**
* @brief Displays a sequence of spaces at the given position in the given background color.
* @param row [IN] position parameter
* @param col [IN] position parameter
* @param width [IN] how many spaces to write
* @param color [IN] the format string before writing the spaces (intent: background color)
* @remark After writing the spaces, the format is reset.
*/
static size_t show_bar(size_t row, size_t col, size_t width, const char* color)
{
goto_pos(row, col);
printf("%s", color);
for(size_t col = 0; col < width; col++) {
putchar(' ');
}
printf(RESET);
return col + width;
}
/**
* @brief Displays a horizontal border over the whole board width.
* @param row [IN] position parameter
* @param col [IN] position parameter
*/
static size_t show_h_gap(size_t row, size_t col) {
for(size_t i = 0; i < GAP_HEIGHT; i++) {
show_bar(row+i, col, GAP_WIDTH + CELL_WIDTH + GAP_WIDTH, GAP);
}
return row + GAP_HEIGHT;
}
/**
* @brief Writes for the call at position y/x the given number with the given background color.
* @param y [IN] position parameter: the upper left row of the cell
* @param x [IN] position parameter: the upper left column of the cell
* @param n [IN] the number to write as text
* @param color [IN] the format string before writing the text (intent: background color)
* @remark After writing the number, the format is reset.
*/
static void show_cell_nr(size_t y, size_t x, size_t n, const char *color)
{
size_t cy = (y + y + CELL_HEIGHT)/2;
size_t cx = (x + x + CELL_WIDTH - 2)/2;
goto_pos(cy, cx);
printf("%s", color);
printf("%2zd", n);
printf(RESET);
}
/**
* @brief Renders the given cell with the given background color, including the surrounding border.
* @param n [IN] the cell number (0...CELLS-1)
* @param color [IN] the format string for the cell content (intent: background color)
* @remark After writing the number, the format is reset.
*/
static void show_cell(size_t n, const char *color)
{
// goto upper-left corner of a cell (the cell starts with an upper and left gap)
size_t y = 1 + n / SIZE * (GAP_HEIGHT + CELL_HEIGHT);
size_t x = 1 + n % SIZE * (GAP_WIDTH + CELL_WIDTH);
size_t row = show_h_gap(y, x);
for(size_t i = 0; i < CELL_HEIGHT; i++) {
size_t col = x;
col = show_bar(row, col, GAP_WIDTH, GAP);
col = show_bar(row, col, CELL_WIDTH, color);
col = show_bar(row, col, GAP_WIDTH, GAP);
row++;
}
row = show_h_gap(row, x);
show_cell_nr(y + GAP_HEIGHT, x + GAP_WIDTH, n + 1, color);
goto_pos(row, 0);
}
/**
* @brief Renders the given player's name in the given background color.
* @param player [IN] the player to render (select the background color and the name of the player
* @remark After writing the content, the format is reset.
*/
static void print_player(control_player_t player)
{
switch(player) {
case control_player_a:
printf(PLAYER_A);
printf("Player A");
printf(RESET);
break;
case control_player_b:
printf(PLAYER_B);
printf("Player B");
printf(RESET);
break;
default:
printf(RESET);
printf("none");
break;
}
}
/**
* @brief Displays a label followed by the given player.
* @param row [IN] position parameter
* @param col [IN] position parameter
* @param label [IN] the profixing label
* @param player [IN] the player to display
*/
static void show_player(size_t row, size_t col, const char *label, control_player_t player)
{
goto_pos(row, col);
printf(RESET);
col += printf("%s", label);
goto_pos(row, col);
print_player(player);
}
/**
* @brief Renders the winner and the next player.
* @param winner [IN] the winning player (if any)
* @param next [IN] the next player (if any)
*/
static void show_status(control_player_t winner, control_player_t next)
{
size_t y = GAP_HEIGHT;
size_t x = SIZE * (GAP_WIDTH + CELL_WIDTH) + GAP_WIDTH + GAP_WIDTH;
size_t row = y;
size_t col = x;
show_player(row, col, "Winner is: ", winner);
row += 2;
show_player(row, col, "Next player is: ", next);
row += 2;
row += 2;
goto_pos(row, col);
printf("0: exit");
row += 2;
goto_pos(row, col);
printf("1..9: play field");
}
/**
* @brief Renders the board from the status given by the control instance.
* @param instance [IN] the instance which holds the control instance
*/
static void show(view_t *instance)
{
assert(instance);
assert(instance->control);
puts(CLS);
show_status(control_get_winner(instance->control), control_get_player(instance->control));
for(size_t i = 0; i < CELLS; i++) {
const char *color = AVAILABLE;
switch(control_get_state(instance->control, i+1)) {
case control_player_a:
color = PLAYER_A;
break;
case control_player_b:
color = PLAYER_B;
break;
default:
break;
}
show_cell(i, color);
}
}
/**
* @brief Processes the input and dsiplays the result.
* @param the instance which holds the control instance
*/
static void notifier_loop(view_t *instance)
{
show(instance);
int c = getchar();
while(c != EOF && c != EXIT) {
if (isdigit(c)) {
control_move(instance->control, c-'0');
}
show(instance);
c = getchar();
}
}
// public API function which is documented in the header file.
void view_init(view_t *instance, control_t *control)
{
assert(instance);
assert(control);
instance->control = control;
}
// public API function which is documented in the header file.
void view_run(view_t *instance)
{
if (isatty(STDIN_FILENO)) { // in case of an interactive terminal, the exhoing and buffering is disabled
// declare non POSIX function, which is available in glibc, but not in strinct C99 mode
void cfmakeraw(struct termios *termios_p);
// replace original tty IO state...
struct termios orig;
struct termios raw;
cfmakeraw(&raw);
tcgetattr(STDIN_FILENO, &orig);
tcsetattr(STDIN_FILENO, TCSANOW, &raw);
// ...do the work...
notifier_loop(instance);
// ...and finalle restore original tty IO state
tcsetattr(STDIN_FILENO, TCSANOW, &orig);
} else { // if not an interactive terminal, no tweaking with IO is done
notifier_loop(instance);
}
}
+31
View File
@@ -0,0 +1,31 @@
/**
* @file
* @brief MVC - View instance
*/
#ifndef _VIEW_H_
#define _VIEW_H_
#include "control.h"
/**
* @brief The instance type.
*/
typedef struct {
control_t *control; ///< the instance knows of the control instance
} view_t;
/**
* @brief Constructor: initialize the instance memory.
* @param instance [INOUT] The instance which holds the state.
* @param control [IN] Dependency Injection of the control instance.
*/
void view_init(view_t *instance, control_t *control);
/**
* @brief Starts the notifyer loop: accepts input and displays the results.
* @param instance [INOUT] The instance which holds the state.
* @remark Does only return when termination is requested through the UI.
*/
void view_run(view_t *instance);
#endif // _VIEW_H_
@@ -0,0 +1,445 @@
/* ----------------------------------------------------------------------------
* -- _____ ______ _____ -
* -- |_ _| | ____|/ ____| -
* -- | | _ __ | |__ | (___ Institute of Embedded Systems -
* -- | | | '_ \| __| \___ \ Zuercher Hochschule Winterthur -
* -- _| |_| | | | |____ ____) | (University of Applied Sciences) -
* -- |_____|_| |_|______|_____/ 8401 Winterthur, Switzerland -
* ----------------------------------------------------------------------------
*/
/**
* @file
* @brief Test suite for the given package.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <time.h>
#include <assert.h>
#include <CUnit/Basic.h>
#include "test_utils.h"
#include "model.h"
#ifndef TARGET // must be given by the make file --> see test target
#error missing TARGET define
#endif
/// @brief alias for EXIT_SUCCESS
#define OK EXIT_SUCCESS
/// @brief alias for EXIT_FAILURE
#define FAIL EXIT_FAILURE
/// @brief The name of the STDOUT text file.
#define OUTFILE "stdout.txt"
/// @brief The name of the STDERR text file.
#define ERRFILE "stderr.txt"
#define TRACE_INDENT "\n " ///< allow for better stdout formatting in case of error
// setup & cleanup
static int setup(void)
{
remove_file_if_exists(OUTFILE);
remove_file_if_exists(ERRFILE);
return 0; // success
}
static int teardown(void)
{
// Do nothing.
// Especially: do not remove result files - they are removed in int setup(void) *before* running a test.
return 0; // success
}
// test utils
static void init_model(model_t *instance, int act)
{
if (act) printf(TRACE_INDENT "init_model:... ");
model_init(instance);
for(size_t row = 0; row < MODEL_SIZE; row++) {
for(size_t col = 0; col < MODEL_SIZE; col++) {
if (act) printf("%zd/%zd ", row, col);
CU_ASSERT_EQUAL_FATAL(instance->board[row][col], model_state_none);
}
}
if (act) printf(TRACE_INDENT);
}
static void print_board(model_state_t board[MODEL_SIZE][MODEL_SIZE])
{
for(size_t row = 0; row < MODEL_SIZE; row++) {
printf("{ ");
for(size_t col = 0; col < MODEL_SIZE; col++) {
printf("%d ", board[row][col]);
}
printf("} ");
}
}
// tests
static void test_model_init(void)
{
// check void model_init(model_t *instance);
// arrange
model_t model;
// act & assert
init_model(&model, 1);
}
static void test_model_get_state(void)
{
// check: model_state_t model_get_state(model_t *instance, model_pos_t pos);
{
// arrange
model_t model;
init_model(&model, 0);
// act & assert
printf(TRACE_INDENT "initial state:... ");
print_board(model.board);
for(size_t row = 0; row < MODEL_SIZE; row++) {
for(size_t col = 0; col < MODEL_SIZE; col++) {
printf("%zd/%zd ", row, col);
CU_ASSERT_EQUAL_FATAL(model_get_state(&model, model_pos(row, col)), model_state_none);
}
}
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_none, model_state_a, model_state_b },
{ model_state_a, model_state_b, model_state_none },
{ model_state_b, model_state_none, model_state_a },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "modified state:... ");
print_board(model.board);
for(size_t row = 0; row < MODEL_SIZE; row++) {
for(size_t col = 0; col < MODEL_SIZE; col++) {
printf("%zd/%zd ", row, col);
CU_ASSERT_EQUAL_FATAL(model_get_state(&model, model_pos(row, col)), board[row][col]);
}
}
}
printf(TRACE_INDENT);
}
static void test_model_get_winner(void)
{
// check: model_state_t model_get_winner(model_t *instance);
{
// arrange
model_t model;
init_model(&model, 0);
// act & assert
printf(TRACE_INDENT "initial no winner:... ");
print_board(model.board);
CU_ASSERT_EQUAL_FATAL(model_get_winner(&model), model_state_none);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_none, model_state_a, model_state_b },
{ model_state_a, model_state_b, model_state_none },
{ model_state_b, model_state_none, model_state_a },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "winner:... ");
print_board(model.board);
CU_ASSERT_EQUAL_FATAL(model_get_winner(&model), model_state_b);
}
printf(TRACE_INDENT);
}
static void test_model_can_move(void)
{
// check: int model_can_move(model_t *instance);
{
// arrange
model_t model;
init_model(&model, 0);
// act & assert
printf(TRACE_INDENT "initial can move:... ");
print_board(model.board);
CU_ASSERT_EQUAL_FATAL(model_can_move(&model), 1);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_none, model_state_a, model_state_a },
{ model_state_a, model_state_b, model_state_none },
{ model_state_b, model_state_none, model_state_b },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "can move while not yet done nor win:... ");
print_board(model.board);
CU_ASSERT_EQUAL_FATAL(model_can_move(&model), 1);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_none, model_state_a, model_state_b },
{ model_state_a, model_state_b, model_state_none },
{ model_state_b, model_state_none, model_state_a },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "cannot move after win:... ");
print_board(model.board);
CU_ASSERT_EQUAL_FATAL(model_can_move(&model), 0);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_b, model_state_a, model_state_a },
{ model_state_a, model_state_b, model_state_b },
{ model_state_b, model_state_a, model_state_a },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "cannot move when all done:... ");
print_board(model.board);
CU_ASSERT_EQUAL_FATAL(model_can_move(&model), 0);
}
printf(TRACE_INDENT);
}
static void test_model_move(void)
{
// check: int model_move(model_t *instance, model_pos_t pos, model_state_t state);
{
// arrange
model_t model;
init_model(&model, 0);
// act & assert
printf(TRACE_INDENT "initial move:... ");
print_board(model.board);
model_pos_t pos_a = model_pos(0, 0);
printf("%zd/%zd ", pos_a.row, pos_a.col);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_a, model_state_a), 1);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_a, model_state_a), 0);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_a, model_state_b), 0);
model_pos_t pos_b = model_pos(2, 2);
printf("%zd/%zd ", pos_b.row, pos_b.col);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_b, model_state_b), 1);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_b, model_state_b), 0);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos_b, model_state_a), 0);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_none, model_state_a, model_state_a },
{ model_state_a, model_state_b, model_state_none },
{ model_state_b, model_state_none, model_state_b },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "can move while not yet done nor win:... ");
print_board(model.board);
model_pos_t pos = model_pos(2, 1);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_a), 1);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_a), 0);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_b), 0);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_none, model_state_a, model_state_b },
{ model_state_a, model_state_b, model_state_none },
{ model_state_b, model_state_none, model_state_a },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "cannot move after win:... ");
print_board(model.board);
model_pos_t pos = model_pos(2, 1);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_a), 0);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_b), 0);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_b, model_state_a, model_state_a },
{ model_state_a, model_state_b, model_state_b },
{ model_state_b, model_state_a, model_state_a },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "cannot move when all done:... ");
print_board(model.board);
for(size_t row = 0; row < MODEL_SIZE; row++) {
for(size_t col = 0; col < MODEL_SIZE; col++) {
model_pos_t pos = model_pos(row, col);
printf("%zd/%zd ", row, col);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_a), 0);
CU_ASSERT_EQUAL_FATAL(model_move(&model, pos, model_state_b), 0);
}
}
CU_ASSERT_EQUAL_FATAL(model_can_move(&model), 0);
}
printf(TRACE_INDENT);
}
static void test_model_get_win_line(void)
{
// check: model_line_t model_get_win_line(model_t *instance);
{
// arrange
model_t model;
init_model(&model, 0);
// act & assert
printf(TRACE_INDENT "initial no winner:... ");
print_board(model.board);
model_line_t no_win = model_get_win_line(&model);
CU_ASSERT_EQUAL_FATAL(no_win.dir, model_dir_none);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_none, model_state_a, model_state_a },
{ model_state_a, model_state_b, model_state_none },
{ model_state_b, model_state_none, model_state_b },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "no winner while not yet done nor win:... ");
print_board(model.board);
model_line_t no_win = model_get_win_line(&model);
CU_ASSERT_EQUAL_FATAL(no_win.dir, model_dir_none);
}
{
// arrange
static const model_state_t board[MODEL_SIZE][MODEL_SIZE] = {
{ model_state_b, model_state_a, model_state_a },
{ model_state_a, model_state_b, model_state_b },
{ model_state_b, model_state_a, model_state_a },
};
model_t model;
init_model(&model, 0);
memcpy(model.board, board, sizeof(board));
// act & assert
printf(TRACE_INDENT "no winner when all done:... ");
print_board(model.board);
model_line_t no_win = model_get_win_line(&model);
CU_ASSERT_EQUAL_FATAL(no_win.dir, model_dir_none);
}
{
for(size_t row = 0; row < MODEL_SIZE; row++) {
// arrange
model_t model;
init_model(&model, 0);
for(size_t col = 0; col < MODEL_SIZE; col++) {
CU_ASSERT_EQUAL_FATAL(model_move(&model, model_pos(row, col), model_state_a), 1);
}
// act & assert
printf(TRACE_INDENT "row winner:... ");
print_board(model.board);
model_line_t win = model_get_win_line(&model);
CU_ASSERT_EQUAL_FATAL(win.dir, model_dir_h);
CU_ASSERT_EQUAL_FATAL(win.start.row, row);
CU_ASSERT_EQUAL_FATAL(win.start.col, 0);
}
}
{
for(size_t col = 0; col < MODEL_SIZE; col++) {
// arrange
model_t model;
init_model(&model, 0);
for(size_t row = 0; row < MODEL_SIZE; row++) {
CU_ASSERT_EQUAL_FATAL(model_move(&model, model_pos(row, col), model_state_a), 1);
}
// act & assert
printf(TRACE_INDENT "column winner:... ");
print_board(model.board);
model_line_t win = model_get_win_line(&model);
CU_ASSERT_EQUAL_FATAL(win.dir, model_dir_v);
CU_ASSERT_EQUAL_FATAL(win.start.row, 0);
CU_ASSERT_EQUAL_FATAL(win.start.col, col);
}
}
{
printf(TRACE_INDENT "diagonal left-right winner:... ");
// arrange
model_t model;
init_model(&model, 0);
for(size_t i = 0; i < MODEL_SIZE; i++) {
CU_ASSERT_EQUAL_FATAL(model_move(&model, model_pos(i, i), model_state_a), 1);
}
// act & assert
print_board(model.board);
model_line_t win = model_get_win_line(&model);
CU_ASSERT_EQUAL_FATAL(win.dir, model_dir_d);
CU_ASSERT_EQUAL_FATAL(win.start.row, 0);
CU_ASSERT_EQUAL_FATAL(win.start.col, 0);
}
{
printf(TRACE_INDENT "diagonal right-left winner:... ");
// arrange
model_t model;
init_model(&model, 0);
for(size_t i = 0; i < MODEL_SIZE; i++) {
CU_ASSERT_EQUAL_FATAL(model_move(&model, model_pos(MODEL_SIZE - 1 - i, i), model_state_a), 1);
}
// act & assert
print_board(model.board);
model_line_t win = model_get_win_line(&model);
CU_ASSERT_EQUAL_FATAL(win.dir, model_dir_d);
CU_ASSERT_EQUAL_FATAL(win.start.row, 0);
CU_ASSERT_EQUAL_FATAL(win.start.col, MODEL_SIZE - 1);
}
printf(TRACE_INDENT);
}
/**
* @brief Registers and runs the tests.
* @returns success (0) or one of the CU_ErrorCode (>0)
*/
int main(void)
{
// setup, run, teardown
TestMainBasic("lab test", setup, teardown
, test_model_init
, test_model_get_state
, test_model_get_winner
, test_model_can_move
, test_model_move
, test_model_get_win_line
);
}