/** * @file * Routines for dealing with Rows that occur in the 'rows' array in the Qube object. * Last Modified: Mon May 29 13:52:17 PDT 2017 * @author Kevin O'Gorman */ /* * Copyright 2002 Santiago Canez * Copyright 2012,2013,2016 Kevin O'Gorman . * Distributed under the GNU General Public License. * * This file is part of libqubist, the library of functions for playing * 4x4x4 tic-tac-toe, also known by some other names outside the USA. * * Libqubist is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2. * * Libqubist is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * @file * Code and data to handle rows and their states. * * In the code, a "row" refers to any four cells (Squares) that form a potential win. * They would be in a straight line in an actual cube, either orthogonally or on some * diagonal, including the four "major diagonals" of the Qube. */ #include #include #include #include "player.h" #include "board.h" #include "row.h" #undef NDEBUG #define NDEBUG #include #define VERBOSE #undef VERBOSE /** The default buffer size. */ #define BUFL 129 Square* qb_get_square_from_row(Row *r, int i) { return r->squares[i]; } /** * Compute the state of the given Row. Note trickery: the values of enumeration * constants are used arithmetically and as subscripts. * @param r a pointer to a Row. * @return the state of the row, according to the states in state.h. */ row_state_t qb_determine_row_state(Row *r) { int counts[3] = {0,0,0};; counts[r->squares[0]->square_state]++; counts[r->squares[1]->square_state]++; counts[r->squares[2]->square_state]++; counts[r->squares[3]->square_state]++; if (counts[PLAYER] == 0 && counts[COMP] == 0) return EMPTYROW; else if (counts[PLAYER] != 0 && counts[COMP] != 0) return MIXED; else if (counts[COMP] > 0) return COMP0 + counts[COMP]; else if (counts[PLAYER] > 0) return PLAYER0 + counts[PLAYER]; assert(FALSE); exit(EXIT_FAILURE); } /** * Get the state of the indicated Row. * @param r a pointer to a Row. * @return the state of Row r. */ row_state_t qb_get_row_state(Row *r) { return r->row_state; } /** * Construct a string representation of a row using linear square numbers. * Not thread-safe, and the result is overwritten on each call. * @param r a pointer to the Row object. * @return a pointer to a static buffer with the string representation. */ char* qb_get_string_row_linear(Row *r) { static char result[32]; snprintf(result, 32, "%d %d %d %d", qb_get_square_num(r->squares[0]), qb_get_square_num(r->squares[1]), qb_get_square_num(r->squares[2]), qb_get_square_num(r->squares[3])); return result; } /** * Construct a string representation of a row using linear square numbers. * Not thread-safe, and the result is overwritten on each call. * Actually the same as qb_get_string_row_linear¸ for historical reasons. * @param r a pointer to the Row object. * @return a pointer to a static buffer with the string representation. */ char* qb_get_string_row(Row *r) { return qb_get_string_row_linear(r); } /** * The number of entries in the lines array, each representing a group * of similar rows. */ #define ROW_GROUPS 16 /** * Patterns lines */ static int lines[ROW_GROUPS][4] = { /* orthogonals */ { 0, 1, 16, 16}, { 0, 4, 64, 1}, { 0, 1, 4, 4}, {16, 1, 20, 4}, {32, 1, 36, 4}, {48, 1, 52, 4}, /* diagonals */ { 0, 1, 4, 20}, { 0, 4, 13, 17}, { 3, 4, 16, 15}, {12, 1, 16, 12}, { 0, 16, 49, 5}, { 3, 16, 52, 3}, /* main diagonals */ { 0, 1, 1, 21}, { 3, 1, 4, 19}, {12, 1, 13, 13}, {15, 1, 16, 11}}; /** * Initialize the array of Rows in the Qube object. * This is based on the array of planes, which must already be established. * @param board the Qube where all this takes place. */ void qb_initialize_rows(Qube *board) { int numrows; int i; numrows = 0; /* three bundles of orthogonal rows */ for(i = 0; i < ROW_GROUPS; i++) { int start = lines[i][0]; int inc = lines[i][1]; int limit = lines[i][2]; int step = lines[i][3]; int s; int sn, j; for (s = start; s < limit; s += inc) { for (j = 0; j < 4; j++) { sn = s + j*step; Square *sp = &board->squares[sn]; sp->rows_in[sp->num_rows++] = numrows; board->rows[numrows].squares[j] = board->squares + sn; } board->rows[numrows++].row_state = EMPTYROW; } } #ifdef VERBOSE /* if debugging, print out a display of the rows, 1-based, in 4 columns */ printf("There are %d known rows.\n", numrows); for (i = 0; i < numrows; i += 4) { printf("%3d: %3d%3d%3d%3d %3d%3d%3d%3d %3d%3d%3d%3d %3d%3d%3d%3d\n", i, qb_get_square_num(board->rows[i + 0].squares[0]), qb_get_square_num(board->rows[i + 0].squares[1]), qb_get_square_num(board->rows[i + 0].squares[2]), qb_get_square_num(board->rows[i + 0].squares[3]), qb_get_square_num(board->rows[i + 1].squares[0]), qb_get_square_num(board->rows[i + 1].squares[1]), qb_get_square_num(board->rows[i + 1].squares[2]), qb_get_square_num(board->rows[i + 1].squares[3]), qb_get_square_num(board->rows[i + 2].squares[0]), qb_get_square_num(board->rows[i + 2].squares[1]), qb_get_square_num(board->rows[i + 2].squares[2]), qb_get_square_num(board->rows[i + 2].squares[3]), qb_get_square_num(board->rows[i + 3].squares[0]), qb_get_square_num(board->rows[i + 3].squares[1]), qb_get_square_num(board->rows[i + 3].squares[2]), qb_get_square_num(board->rows[i + 3].squares[3]) ); } assert(numrows == 76); #endif } /** * Compute a pointer to the given row of the Qube object. * @param board a pointer to the Qube object. * @param num the index of the desired row * @return a pointer to the desired Row object. */ Row* qb_get_row(Qube *board, int num) { return board->rows + num; } /** * Compute the value of each row of the board. * @param board a pointer to the Qube object. */ void qb_evaluate_rows(Qube *board) { int i; for (i=0; i < 76; i++) { board->rows[i].row_state = qb_determine_row_state(board->rows + i); } } /** * Verify the state of all rows. Should only be invoked during debugging, but is always * present to allow other files to use it in assert(1) calls. Returns a truth value. * @param board a pointer to the Qube object. * @return TRUE (1) if okay, FALSE (0) if not. */ int qb_has_valid_rows(Qube *board) { int i; for (i=0; i < 76; i++) { if (board->rows[i].row_state != qb_determine_row_state(board->rows + i)) { return FALSE; } } return TRUE; } /** * Determine if an immediate win exists for the indicated player. Return the winning * square if so. * @param board a pointer to the Qube object. * @param player a pointer to the Player object. * @return the number of the winning square, or -1 if no win is available. */ int qb_check_immediate_win(Qube *board, Player *player) { int i; row_state_t state; assert(qb_has_valid_rows(board)); for (i=0; i < 76; i++) { /* check each row for a win */ state = qb_get_row_state(board->rows + i); if (state == player->this3) { return qb_get_empty_from_row(board, board->rows + i); } } return -1; } /** * Return a square number of an empty square in the given Row. * @param board a pointer to the Qube object. * @param row a pointer to the row in quesiton. * @return the number of an empty square, or -1 if there is none. */ int qb_get_empty_from_row(Qube *board, Row *row) { int i; Square *sq; for (i = 0; i < 4; i++) { sq = qb_get_square_from_row(row, i); if (sq->square_state == EMPTY) { return qb_get_square_num(sq); } } return -1; } /** * Determine if a square is forcing for the computer. * @param board a pointer to the Qube object. * @param sq the number of the square. * @return 1 if the square is empty and forcing, otherwise 0; */ int qb_is_square_COMP2(Qube *board, int sq) { int row; Square *sqp = qb_get_square(board, sq); if (sqp->square_state != EMPTY) { return 0; } assert(qb_has_valid_rows(board)); for (row = 0; row < sqp->num_rows; row++) { if (board->rows[sqp->rows_in[row]].row_state == COMP2) { return 1; } } return 0; } /* vim: set expandtab autoindent shiftround nosmartindent tabstop=8 softtabstop=2 shiftwidth=2: */