diff --git a/CMakeLists.txt b/CMakeLists.txt index 79aa1eb..2c7ce5f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.5) -set(CMAKE_C_FLAGS "-std=c99 -g -Wall") +set(CMAKE_C_FLAGS "-std=c99 -lncurses -g -Wall") set(CMAKE_INSTALL_PREFIX ".") -#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) include_directories(include) +#set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) project(utictactoe) -add_executable(${PROJECT_NAME} src/utictactoe.c src/model.c) +add_executable(${PROJECT_NAME} src/utictactoe.c src/model.c src/view.c) diff --git a/include/model.h b/include/model.h index 21be7e1..8b465a6 100644 --- a/include/model.h +++ b/include/model.h @@ -192,4 +192,5 @@ e_status play_move(s_utictactoe *p_uttt, s_move *p_move); /*Given usefull functions*/ void draw_utictactoe(s_utictactoe *p_uttt); void draw_utictactoe_history(s_utictactoe *p_uttt); -#endif /* MODEL_H */ \ No newline at end of file +void save_a_utictactoe_to_file(FILE *p_f, s_utictactoe *p_uttt); +#endif /* MODEL_H */ diff --git a/include/view.h b/include/view.h new file mode 100644 index 0000000..6c7f91c --- /dev/null +++ b/include/view.h @@ -0,0 +1,36 @@ +#ifndef VIEW_H +#define VIEW_H +#include +#include "common.h" +#include "model.h" + +typedef struct view * p_view; + +/*! + * This function allocates dynamically a struct view in order to handle the corresponding u_tictactoe. + * + * \param p_uttt a pointer on a s_utictactoe. + * \return a reference to the s_utictactoe memory space allocated, NULL in case + * of allocation problem. + */ +p_view create_view(s_utictactoe * p_uttt); + +void draw_ttt(WINDOW * w, s_tictactoe * t); +e_location get_ttt_position( int x, int y); +/*! + * This function retrieves a valid move proposal from the user using the corresponding view. + * + * \param p_move a pointer on a s_move to be modified. + * \param v a pointer on the view to use. + */ +void set_next_player_move(s_move * p_move, p_view v); + +/*! + * This function free all the memory used by a given view which + * reference is given. + * + * \param v a pointer on a view to be freed. + */ +void free_view(p_view v); + +#endif /* VIEW_H */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8722958..4812c33 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,6 @@ cmake_minimum_required(VERSION 3.5) -set(CMAKE_C_FLAGS "-std=c99 -g -Wall") +set(CMAKE_C_FLAGS "-std=c99 -lncurses -g -Wall") set(CMAKE_INSTALL_PREFIX ".") include_directories(../include) - project(utictactoe) -add_executable(${PROJECT_NAME} utictactoe.c model.c) +add_executable(${PROJECT_NAME} utictactoe.c model.c view.c) diff --git a/src/model.c b/src/model.c index f7b6499..14d5d93 100644 --- a/src/model.c +++ b/src/model.c @@ -1,5 +1,50 @@ #include "model.h" +// Define shorts positions +char * get_short_position(e_location pos){ + switch(pos) { + case TOPLEFT: + return "TL"; + case TOPCENTER: + return "TC"; + case TOPRIGHT: + return "TR"; + case MIDLEFT: + return "ML"; + case MIDCENTER: + return "MC"; + case MIDRIGHT: + return "MR"; + case BOTTOMLEFT: + return "BL"; + case BOTTOMCENTER: + return "BC"; + case BOTTOMRIGHT: + return "BR"; + case FREE: + return "FR"; + case NONE: + return "NO"; + default: + return "NO"; + } +} + +char get_short_player(e_player player){ + switch(player){ + case PLAYER_O: + return 'O'; + case PLAYER_X: + return 'X'; + case NOBODY: + return '.'; + case BOTH: + return '#'; + default: + return NO; + } +} + /* * Create empty move @@ -170,113 +215,154 @@ e_status is_move_valid(s_utictactoe *p_uttt, s_move *p_move) { } void set_tictactoe_winner(s_tictactoe *p_ttt){ - bool find; - int next; - if ( p_ttt->winner == NOBODY ) { - for(int i = 0; i < TICTACTOE_WIDTH; i++){ - - /* - * Test for first diagonale - */ - if ( p_ttt->content[i] != NOBODY ) { - // if case 0 we need to test diagonale - if ( i == 0 ){ - int next; - find = true; - for (int l = 1; l < TICTACTOE_WIDTH; l++) { - // Maybe it is overkill to test diagonal but... - // ... no need to rewrite if we update TICTACTOE_WIDTH - next = l * TICTACTOE_WIDTH + l; - if ( p_ttt->content[i] != p_ttt ->content[next]) { - find = false; - break; - } - } - if (find == true) { - p_ttt->winner = p_ttt->content[i]; - return; - } - } - } - - /* - * second diagonal, from the end of line - */ - if ( i == TICTACTOE_WIDTH - 1 ) { - find = true; - for ( int l = 1; l < TICTACTOE_WIDTH; l++){ - next = (TICTACTOE_WIDTH - 1) * l; - if ( p_ttt->content[i] == p_ttt->content[next] ) { - find = false; - break; - } - } - if ( find == true ){ - p_ttt->winner = p_ttt->content[i]; - return; - } - } - - /* - * column - */ - find = true; - for (int c = 1; c < TICTACTOE_WIDTH; c++ ) { - next = TICTACTOE_WIDTH * c + i; - if ( p_ttt->content[i] != p_ttt->content[next]) { - find = false; - break; - } - if ( find == true ){ - p_ttt->winner = p_ttt->content[i]; - return; - } - } - - /* - * line - */ - int line = i * TICTACTOE_WIDTH; - if (p_ttt->content[line] != NOBODY){ - find = true; - for ( int l = 1; l < TICTACTOE_WIDTH; l++ ) { - next = line + l; - if ( p_ttt->content[line] != p_ttt->content[next] ) { - find = false; - break; - } - } - if ( find == true ) { - p_ttt->winner = p_ttt->content[line]; - return; - } - } + // columns + for ( int i=0; i < TICTACTOE_WIDTH; i++ ) { + if (p_ttt->content[i] != NOBODY + &&p_ttt->content[i] == p_ttt->content[i+3] + && p_ttt->content[i] == p_ttt->content[i+6]) { + p_ttt->winner = p_ttt->content[i]; + return; } + } - // check if a move could be done, if not retuen BOTH (no winner) - find = false; - for (int i = 0; i < TICTACTOE_SIZE; i++){ - if ( p_ttt->content[i] == NOBODY ) { - find = true; - break; - } + //lines + for ( int i=0; i < TICTACTOE_SIZE; i=i+3 ) { + if (p_ttt->content[i] != NOBODY + &&p_ttt->content[i] == p_ttt->content[i+1] + && p_ttt->content[i] == p_ttt->content[i+2]) { + p_ttt->winner = p_ttt->content[i]; + return; } - if ( find == false ) { - p_ttt->winner = BOTH; + } + + //diagonale + if ( p_ttt->content[0] != NOBODY + && p_ttt->content[0] == p_ttt->content[4] + && p_ttt->content[0] == p_ttt->content[8] ) { + p_ttt->winner = p_ttt->content[0]; + return; + } + //diagonale + if ( p_ttt->content[2] != NOBODY + && p_ttt->content[2] == p_ttt->content[4] + && p_ttt->content[2] == p_ttt->content[6] ) { + p_ttt->winner = p_ttt->content[2]; + return; + } + + // check if a move could be done, if not retuen BOTH (no winner) + for (int i = 0; i < TICTACTOE_SIZE; i++){ + if ( p_ttt->content[i] == NOBODY ) { + return; } - } + } + p_ttt->winner = BOTH; } e_status play_move(s_utictactoe *p_uttt, s_move *p_move) { - if (is_move_valid(p_uttt, p_move) == YES) { - // we can process things - if ( p_uttt->inception_level == 1 ) { - p_uttt->outer_tictactoe->content[p_move->outer_position] = p_move->player; - } - else { - - } + if (is_move_valid(p_uttt, p_move) == NO) { + return NO; } + list_element_s_move *value = (list_element_s_move*) + malloc(sizeof(list_element_s_move)); + + // we need to create a s_move in memory... + // and affect p_move + value->last_move = create_empty_move(); + + value->last_move->inner_position = p_move->inner_position; + value->last_move->outer_position = p_move->outer_position; + value->last_move->player = p_move->player; + if ( p_uttt->history == NULL ) { + value->next = NULL; + } + else { + value->next = p_uttt->history; + } + p_uttt->history = value; + + if ( p_uttt->inception_level != 1 ) { + p_uttt->inner_tictactoes[p_move->outer_position]->content[p_move->inner_position] = p_move->player; + set_tictactoe_winner(p_uttt->inner_tictactoes[p_move->outer_position]); + + //check if we have a winner in inner_ttt then set player to outer + p_uttt->outer_tictactoe->content[p_move->outer_position] = p_uttt->inner_tictactoes[p_move->outer_position]->winner; + } + else { + p_uttt->outer_tictactoe->content[p_move->outer_position] = p_move->player; + } + set_tictactoe_winner(p_uttt->outer_tictactoe); return YES; } + +void save_a_utictactoe_to_file(FILE *p_f, s_utictactoe *p_uttt){ + assert(p_f); + assert(p_uttt); + fprintf(p_f, "%d\n", p_uttt->inception_level); + if(p_uttt->history){ + list_element_s_move * c_hist = p_uttt->history; + while (c_hist != NULL){ + if(c_hist->last_move){ + fprintf(p_f, "%s %s %c\n", + get_short_position(c_hist->last_move->inner_position), + get_short_position(c_hist->last_move->outer_position), + get_short_player(c_hist->last_move->player)); + } + c_hist = c_hist->next; + } + } +} +void draw_ith_line_of_ttt(s_tictactoe *p_ttt, uint line) { + assert(p_ttt); + printf("%c%c%c", p_ttt->content[line * TICTACTOE_WIDTH], + p_ttt->content[line * TICTACTOE_WIDTH + 1], + p_ttt->content[line * TICTACTOE_WIDTH + 2]); +} + +void draw_tictactoe(s_tictactoe *p_ttt) { + assert(p_ttt); + for (uint line = 0; line < TICTACTOE_WIDTH; line++) { + draw_ith_line_of_ttt(p_ttt, line); + printf("\n"); + } +} + +void draw_utictactoe_history(s_utictactoe *p_uttt) { + assert(p_uttt); + char *e_location_name[] = {"TL", "TC", "TR", "ML", "MC", "MR", + "BL", "BC", "BR", "FREE", "NONE"}; + list_element_s_move *tmp = p_uttt->history; + while (tmp != NULL) { + if (tmp->last_move) { + printf(" - %s %s %c \n", e_location_name[tmp->last_move->inner_position], + e_location_name[tmp->last_move->outer_position], + tmp->last_move->player); + } + tmp = tmp->next; + } +} + +void draw_utictactoe(s_utictactoe *p_uttt) { + assert(p_uttt); + if (p_uttt->inception_level == 1) { + draw_tictactoe(p_uttt->outer_tictactoe); + } else { + for (uint id_ttt = 0; id_ttt < TICTACTOE_SIZE; + id_ttt = id_ttt + TICTACTOE_WIDTH) { + for (uint line = 0; line < TICTACTOE_WIDTH; line++) { + draw_ith_line_of_ttt(p_uttt->inner_tictactoes[id_ttt], line); + printf("|"); + draw_ith_line_of_ttt(p_uttt->inner_tictactoes[id_ttt + 1], line); + printf("|"); + draw_ith_line_of_ttt(p_uttt->inner_tictactoes[id_ttt + 2], line); + printf("\n"); + } + if (id_ttt + TICTACTOE_WIDTH < TICTACTOE_SIZE) { + printf("-----------"); + printf("\n"); + } + } + } + printf("\n####\n"); +} diff --git a/src/utictactoe.c b/src/utictactoe.c index b23d427..eda2eef 100644 --- a/src/utictactoe.c +++ b/src/utictactoe.c @@ -8,6 +8,7 @@ #include "utictactoe.h" #include "common.h" #include "model.h" +#include "view.h" #define OPTIONAL_ARGUMENT_IS_PRESENT \ ((optarg == NULL && optind < argc && argv[optind][0] != '-') \ @@ -140,15 +141,35 @@ int main(int argc, char* argv[]) { exit (EXIT_FAILURE); } } - s_move *test_move = create_empty_move(); - free_move(test_move); - s_tictactoe *test_ttt = create_empty_tictactoe(); - free_tictactoe(test_ttt); - s_utictactoe *test_uttt = create_empty_utictactoe(1); - free_utictactoe(test_uttt); - test_uttt = create_empty_utictactoe(2); - free_utictactoe(test_uttt); - test_uttt = create_empty_utictactoe(3); - free_utictactoe(test_uttt); - return EXIT_SUCCESS; + s_utictactoe *s = create_empty_utictactoe(1); + s_move *m = create_empty_move(); + p_view v = create_view(s); + while (s->outer_tictactoe->winner == NOBODY) { + m->player = get_next_player_to_play(s); + m->outer_position=get_next_outer_position(s); + set_next_player_move(m,v); + play_move(s, m); + } + free_view(v); + draw_utictactoe(s); + draw_utictactoe_history(s); + printf("The winner is : %c\n", s->outer_tictactoe->winner); + free_move(m); + free_utictactoe(s); + + // s = create_empty_utictactoe(2); + // m = create_empty_move(); + // v = create_view(s); + // while (s->outer_tictactoe->winner == NOBODY) { + // m->player = get_next_player_to_play(s); + // m->outer_position=get_next_outer_position(s); + // set_next_player_move(m,v); + // play_move(s, m); + // m->outer_position = m->inner_position; + // } + // free_view(v); + // draw_utictactoe_history(s); + // printf("The winner is : %c\n", s->outer_tictactoe->winner); + // free_move(m); + // free_utictactoe(s); } diff --git a/src/view.c b/src/view.c new file mode 100644 index 0000000..c0c6f2f --- /dev/null +++ b/src/view.c @@ -0,0 +1,145 @@ +#include "common.h" +#include "model.h" +#include "view.h" + +struct view{ + s_utictactoe * p_uttt; + WINDOW* w_uttt; + WINDOW* w_ttt; +}; + + +p_view create_view(s_utictactoe * u) +{ + p_view v = (p_view) malloc(sizeof(p_view)); + v->p_uttt = u; + + int p_left = 4; + int p_top = 2; + + initscr(); + raw(); + noecho(); + start_color(); + keypad(stdscr, TRUE); + init_pair(1, COLOR_BLACK,COLOR_WHITE); + init_pair(2, COLOR_BLACK,COLOR_RED); + curs_set( 0 ); + + // Create the outer win + WINDOW * w_outer = subwin(stdscr, 9, 11, p_left, p_top); + box(w_outer, ACS_VLINE, ACS_HLINE); + draw_ttt(w_outer, u->outer_tictactoe); + v->w_uttt = w_outer; + + // If needed, create the inner ttt + if ( u->inception_level == 2 ){ + int r,c; + getmaxyx(w_outer, c, r); + WINDOW * w_inner = subwin(stdscr, 9, 11, c + p_left, p_top); + box(w_inner, ACS_VLINE, ACS_HLINE); + //draw_ttt(w_innera); + //wrefresh(w_inner); + v->w_ttt = w_inner; + } + wrefresh(w_outer); + wrefresh(stdscr); + + return v; +} + + +void draw_ttt(WINDOW * w, s_tictactoe * u){ + int x,y; + int i = 0; + for( y = 2; y < 7; y++){ + if ( y % 2 == 0){ + for(x = 3; x < 8 ;x++){ + if (x % 2 != 0) { + mvwprintw(w,y,x,"%c", (u->content[i] == NOBODY)?'.':u->content[i]); + i++; + } + else { mvwprintw(w,y,x,"|"); } + } + } + else { mvwprintw(w,y,3,"-----"); } + } +} + +void set_next_player_move(s_move * m,p_view v) +{ + int ch=-1; + char * s="Player to play:%c"; + int pos_x=3; + int pos_y=2; + while(ch!=' '){ + draw_ttt(v->w_uttt, v->p_uttt->outer_tictactoe); + mvwprintw(stdscr,1,0,s, m->player); + wattron(v->w_uttt, COLOR_PAIR(2)); + mvwaddch(v->w_uttt,pos_y, pos_x, + v->p_uttt->outer_tictactoe->content[get_ttt_position(pos_y, pos_x)] + ); + wattroff(v->w_uttt, COLOR_PAIR(2)); + wrefresh(v->w_uttt); + wrefresh(stdscr); + ch = wgetch(stdscr); + switch(ch) + { + case KEY_BACKSPACE: + case 127: + case '\b': + exit(1); + break; + case KEY_RIGHT: + if(pos_x+1<8){ + pos_x=pos_x+2; + } + break; + case KEY_LEFT: + if(pos_x>3){ + pos_x=pos_x-2; + } + break; + case KEY_UP: + if(pos_y>2){ + pos_y=pos_y-2; + } + break; + case KEY_DOWN: + if(pos_y<6){ + pos_y=pos_y+2; + } + break; + } + + } + // m->inner_position=(e_location)(TOPLEFT + rand() % TICTACTOE_SIZE); + // m->outer_position=(e_location)(TOPLEFT + rand() % TICTACTOE_SIZE); + m->outer_position = get_ttt_position( pos_y, pos_x); + +} + +e_location get_ttt_position( int y, int x) { + if ( y == 2 && x == 3 ){ return TOPLEFT; } + if ( y == 2 && x == 5 ){ return TOPCENTER; } + if ( y == 2 && x == 7 ){ return TOPRIGHT; } + if ( y == 4 && x == 3 ){ return MIDLEFT; } + if ( y == 4 && x == 5 ){ return MIDCENTER; } + if ( y == 4 && x == 7 ){ return MIDRIGHT; } + if ( y == 6 && x == 3 ){ return BOTTOMLEFT; } + if ( y == 6 && x == 5 ){ return BOTTOMCENTER; } + if ( y == 6 && x == 7 ){ return BOTTOMRIGHT; } + return NONE; +} + + +void free_view(p_view v){ + if(v->p_uttt->inception_level == 2){ + delwin(v->w_uttt); + v->w_uttt = NULL; + } + delwin(v->w_ttt); + v->w_ttt = NULL; + v->w_uttt = NULL; + endwin(); +}