CB100

Notas, resueltos y tps de la materia Algoritmos y Estructuras de Datos
Index Commits Files Refs README
guias/2/ej12.cpp (8725B)
   1 #include <iostream>
   2 #include <cstdlib>
   3 #include <csignal>
   4 #include <termios.h>
   5 #include <string>
   6 #include <sstream>
   7 
   8 typedef enum {
   9     PLAYER_1,
  10     PLAYER_2
  11 } player_t;
  12 
  13 typedef enum {
  14     PLAYING,
  15     WIN,
  16     DRAFT,
  17     RESET,
  18     HALT,
  19     EXIT
  20 } game_status_t;
  21 
  22 static struct termios default_attributes;
  23 static unsigned int tui_total_height;
  24 static player_t current_player;
  25 static game_status_t game_status;
  26 static int cursor_col, cursor_row, cursor_row_pre;
  27 std::string player_status, key_string;
  28 char game_board[3][3];
  29 
  30 void tui_set_input_mode (void) {
  31     struct termios tattr;
  32 
  33     if (!isatty(STDIN_FILENO)) {
  34         std::cerr <<  "Not a terminal" << std::endl;
  35         exit (EXIT_FAILURE);
  36     }
  37 
  38     tcgetattr(STDIN_FILENO, &default_attributes);
  39     tcgetattr(STDIN_FILENO, &tattr);
  40     tattr.c_lflag &= ~(ICANON|ECHO);
  41     tattr.c_cc[VMIN] = 1;
  42     tattr.c_cc[VTIME] = 0;
  43     tcsetattr(STDIN_FILENO, TCSAFLUSH, &tattr);
  44 }
  45 
  46 void tui_reset_input_mode (void) {
  47     tcsetattr(STDIN_FILENO, TCSANOW, &default_attributes);
  48 }
  49 
  50 void game_sig_handler(int signo) {
  51     signal(signo, SIG_IGN);
  52     if ((signo == SIGINT) || (signo == SIGQUIT)) {
  53         game_status = EXIT;
  54         exit(0);
  55     }
  56 }
  57 
  58 void game_clear_board(void) {
  59     std::cout << "\033[" << (cursor_row_pre*2)+1 << "F\033[0K";
  60     cursor_row_pre = cursor_row;
  61 }
  62 
  63 void game_player_status_msg() {
  64     std::ostringstream current_player_string;
  65     unsigned int player_won;
  66     key_string.assign("\033[0J");
  67 
  68     switch(game_status) {
  69     case PLAYING:
  70         player_status = std::string("Player ");
  71         current_player_string << current_player+1;
  72         player_status += current_player_string.str();
  73         player_status += "\033[0J";
  74         break;
  75     case WIN:
  76         player_won = ((current_player == PLAYER_1) ? PLAYER_2 : PLAYER_1);
  77         player_status = std::string("Player ");
  78         current_player_string << player_won+1;
  79         player_status += current_player_string.str();
  80         player_status += " Wins";
  81         key_string.assign("Press `r` to restart\033[0J");
  82         game_status = HALT;
  83         break;
  84     case DRAFT:
  85         player_status.assign("Draft\033[0J");
  86         key_string.assign("Press `r` to restart\033[0J");
  87         game_status = HALT;
  88         break;
  89     case HALT:
  90         key_string.assign("Press `r` to restart\033[0J");
  91         break;
  92     default:
  93         break;
  94     }
  95 }
  96 
  97 void game_print_board(void) {
  98     game_player_status_msg();
  99     std::cout << "┌───┬───┬───┐\n│ "
 100               << game_board[0][0] << " │ "
 101               << game_board[0][1] << " │ " 
 102               << game_board[0][2] << " │ " << player_status << "\n";
 103 
 104     std::cout << "├───┼───┼───┤ " << key_string << "\n│ "
 105               << game_board[1][0] << " │ "
 106               << game_board[1][1] << " │ " 
 107               << game_board[1][2] << " │\n";
 108 
 109     std::cout << "├───┼───┼───┤\n│ "
 110               << game_board[2][0] << " │ "
 111               << game_board[2][1] << " │ " 
 112               << game_board[2][2] << " │\n"
 113               << "└───┴───┴───┘\n";
 114 }
 115 
 116 void game_board_set_cursor(void) {
 117     if(cursor_row)
 118         std::cout << "\033[" << cursor_row*2 << "B";
 119 
 120     std::cout << "\033[0G\033[" << (cursor_col*4)+2 << "C";
 121 }
 122 
 123 void game_move_cursor_right(void) {
 124     cursor_col = (cursor_col == 2) ? 0 : cursor_col + 1;
 125 }
 126 
 127 void game_move_cursor_left(void) {
 128     cursor_col = (cursor_col > 0) ? cursor_col-1 : 2;
 129 }
 130 
 131 void game_move_cursor_up(void) {
 132     cursor_row_pre = cursor_row;
 133     cursor_row = (cursor_row > 0) ? cursor_row-1 : 2;
 134 }
 135  
 136 void game_move_cursor_down(void) {
 137     cursor_row_pre = cursor_row;
 138     cursor_row = ((cursor_row + 1) > 2) ? 0 : cursor_row+1;
 139 }
 140 
 141 void game_set_at_cursor_pos(void) {
 142     if(game_board[cursor_row][cursor_col] == ' ') {
 143         char aux = ((current_player == PLAYER_1) ? 'X' : 'O');
 144         game_board[cursor_row][cursor_col] = aux;
 145         current_player = ((current_player == PLAYER_1) ? PLAYER_2 : PLAYER_1);
 146     }
 147 }
 148 
 149 void game_redraw_board(void) {
 150     game_clear_board();
 151     game_print_board();
 152     std::cout << "\033[" << tui_total_height-1 << "A";
 153     game_board_set_cursor();
 154 }
 155 
 156 void game_tui_cleanup() {
 157     tui_reset_input_mode();
 158     std::cout << "\033[" << tui_total_height-(cursor_row*2)-1 << "E";
 159 }
 160 
 161 void game_tui_setup() {
 162     tui_set_input_mode();
 163     signal(SIGINT,  game_sig_handler);
 164     signal(SIGQUIT, game_sig_handler);
 165     signal(SIGTSTP, SIG_IGN);
 166     atexit(game_tui_cleanup);
 167     std::cout << "\033[4 q"; // Blinking block cursor
 168 }
 169 
 170 void game_initialize_board() {
 171     for(size_t i = 0; i < 3; ++i) {
 172         for(size_t j = 0; j < 3; ++j) {
 173             game_board[i][j] = ' ';
 174         }
 175     }
 176 }
 177 
 178 void game_check_status() {
 179     char aux;
 180     size_t i, j, occ_x, occ_o;
 181     if(game_status != PLAYING)
 182         return;
 183 
 184     // Horizontal line
 185     for(i = occ_x = occ_o = 0; i < 3; ++i) {
 186         aux = game_board[cursor_row][i];
 187         if(aux == 'X') {
 188             occ_x++;
 189             occ_o = 0;
 190         }
 191         else if (aux == 'O'){
 192             occ_x = 0;
 193             occ_o++;
 194         }
 195         else {
 196             occ_x = occ_o = 0;
 197         }
 198     }
 199     if ((occ_o == 3) || (occ_x == 3)) {
 200         game_status = WIN;
 201     }
 202 
 203     // Vertical line
 204     for(i = occ_x = occ_o = 0; i < 3; ++i) {
 205         aux = game_board[i][cursor_col];
 206         if(aux == 'X') {
 207             occ_x++;
 208             occ_o = 0;
 209         }
 210         else if (aux == 'O'){
 211             occ_x = 0;
 212             occ_o++;
 213         }
 214         else {
 215             occ_x = occ_o = 0;
 216         }
 217     }
 218     if ((occ_o == 3) || (occ_x == 3)) {
 219         game_status = WIN;
 220     }
 221 
 222     // Reverse diagonal line (\)
 223     for(i = occ_x = occ_o = 0; i < 3; ++i) {
 224         aux = game_board[i][i];
 225         if(aux == 'X') {
 226             occ_x++;
 227             occ_o = 0;
 228         }
 229         else if (aux == 'O'){
 230             occ_x = 0;
 231             occ_o++;
 232         }
 233         else {
 234             occ_x = occ_o = 0;
 235         }
 236     }
 237     if ((occ_o == 3) || (occ_x == 3)) {
 238         game_status = WIN;
 239     }
 240 
 241     // Forward diagonal line (/)
 242     for(i = occ_x = occ_o = 0; i < 3; ++i) {
 243         aux = game_board[2-i][i];
 244         if(aux == 'X') {
 245             occ_x++;
 246             occ_o = 0;
 247         }
 248         else if (aux == 'O'){
 249             occ_x = 0;
 250             occ_o++;
 251         }
 252         else {
 253             occ_x = occ_o = 0;
 254         }
 255     }
 256     if ((occ_o == 3) || (occ_x == 3)) {
 257         game_status = WIN;
 258     }
 259 
 260     if(game_status != WIN) {
 261         for(i = 0; i < 3; ++i)
 262             for(j = 0; j < 3; ++j)
 263                 if(game_board[i][j] == ' ')
 264                     return;
 265 
 266         if((i == j) && (i == 3))
 267             game_status = DRAFT;
 268     }
 269 }
 270 
 271 void game_key_handler(void) {
 272     char cmd;
 273 
 274     std::cin.get(cmd);
 275     switch(cmd) {
 276     case 'w':
 277     case 'k':
 278         game_move_cursor_up();
 279         break;
 280     case 's':
 281     case 'j':
 282         game_move_cursor_down();
 283         break;
 284     case  10: // Enter
 285     case ' ':
 286         if(game_status == PLAYING)
 287             game_set_at_cursor_pos();
 288         break;
 289     case 'l':
 290     case 'd':
 291         game_move_cursor_right();
 292         break;
 293     case 'h':
 294     case 'a':
 295         game_move_cursor_left();
 296         break;
 297     case 'r':
 298         if(game_status == HALT)
 299             game_status = RESET;
 300         break;
 301     case 'q':
 302         game_status = EXIT;
 303         break;
 304     case 27: { // Escape codes (i.e. arrow keys)
 305         std::cin.get(cmd); // skip '['
 306         std::cin.get(cmd);
 307         switch (cmd) {
 308         case 'A': // up
 309             game_move_cursor_up();
 310             break;
 311         case 'B': // down
 312             game_move_cursor_down();
 313             break;
 314         case 'C': // right
 315             game_move_cursor_right();
 316             break;
 317         case 'D': // left
 318             game_move_cursor_left();
 319             break;
 320         }
 321         break;
 322         }
 323     }
 324 }
 325 
 326 int main (void) {
 327     tui_total_height = 7;
 328     cursor_row = cursor_row_pre = cursor_col = 0;
 329     game_status = PLAYING;
 330     current_player = PLAYER_1;
 331 
 332     game_tui_setup();
 333     game_initialize_board();
 334     game_print_board();
 335     std::cout << "\033[" << tui_total_height-1 << "A";
 336     std::cout << "\033[" << ((cursor_col*4)+2) << "C";
 337 
 338     game_move_cursor_right();
 339     game_redraw_board();
 340     game_move_cursor_down();
 341     game_redraw_board();
 342 
 343     while(game_status != EXIT) {
 344         game_key_handler();
 345         if(game_status == RESET) {
 346             game_status = PLAYING;
 347             current_player = PLAYER_1;
 348             cursor_col = cursor_row = 1;
 349             game_initialize_board();
 350         }
 351         game_check_status();
 352         game_redraw_board();
 353     }
 354 
 355     return 0;
 356 }