P05 lab code added
This commit is contained in:
		
							parent
							
								
									6f959c9759
								
							
						
					
					
						commit
						2f61cf20b1
					
				| 
						 | 
				
			
			@ -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_
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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_
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
                  );
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue