int piece_equal(QuartoPiece *a, QuartoPiece *b) { if(is_valid_piece(a) && is_valid_piece(b)){ return ((a->piece & b->piece) > 0) || ((a->xor & b->xor) > 0); }else{ return 0; } }
int pieces_equal(QuartoPiece *a, QuartoPiece *b, QuartoPiece *c, QuartoPiece *d) { if(is_valid_piece(a) && is_valid_piece(b) && is_valid_piece(c) && is_valid_piece(d)){ return ((a->piece & b->piece & c->piece & d->piece) > 0) || ((a->xor & b->xor & c->xor & d->xor) > 0); }else{ return 0; } }
int set_piece(QuartoBoard *board, int x, int y, QuartoPiece *piece) { if(board->size < 16 && is_valid_piece(piece)){ SET_PIECE(x, y, board->board, *piece); board->size += 1; return 1; }else{ return 0; } }
int prep_available(QuartoBoard *board, int *available) { for(int i = 0; i < 16; i++){ //Because C doesn't like to place information in a new array //we need to do it explicit to ensure that none of the elements //are 0 by default available[i] = 1; } QuartoPiece tmp; int res = 16; for(int i = 0; i < 4; i++){ for(int j = 0; j < 4; j++){ tmp = GET_PIECE(i, j, board->board); if(is_valid_piece(&tmp)){ available[tmp.piece] = 0; res--; } } } return res; }
int maxValue(QuartoPiece a, QuartoBoard *board, MinimaxRes *res, int numPly, int alpha, int beta) { int local_alpha = -10000000; for(int i = 0; i < 4; i++){ for(int j = 0; j < 4; j++){ if(!is_valid_piece(&GET_PIECE(j, i, board->board))){ QuartoBoard newB = *board; set_piece(&newB, j, i, &a); int won = quarto_win(&newB); if(newB.size == 16){ //This placement filled up the board which means that //we can't go further down so we just update the x, j //and return the value of this end state res->x = j; res->y = i; return won*100; }else if(won){ //This is the maximum we can get, which means that we won the game //no reason to go further down since this lead to a victory res->x = j; res->y = i; return 100; }else if(numPly == 0){ //We have reached the bottom of the recursion //and we need only evaluate the possible placements //of the piece that we have gotten int quarto_value = quarto_herustic(&newB); if(quarto_value > local_alpha){ local_alpha = quarto_value; res->x = j; res->y = i; } if(local_alpha >= beta) return local_alpha; }else{ int pieces_left[16]; //Array with 0 or 1 to indicate if the pieces //in that index is available prep_available(&newB, pieces_left); for(int k = 0; k < 16; k++){ if(pieces_left[k]){ MinimaxRes r; int value = minValue(create_piece_from_int(k), &newB, &r, numPly-1, local_alpha, beta); if(value > local_alpha){ //The value we got from below is better //than what we have currently found //which means we need to keep this //position and update our alpha res->x = j; res->y = i; res->next_piece = k; local_alpha = value; } //If alpha is lager than beta there is //no use continuing because the min node above //will always chose the path that lead to beta if(local_alpha >= beta) return local_alpha; } } } } } } return local_alpha; }
int pieces_triple(QuartoBoard *board, QuartoPiece *a, QuartoPiece *b, QuartoPiece *c, QuartoPiece *d, TriplePiece *tp) { int available[16]; prep_available(board, available); QuartoPiece g; int piece_count; if(is_valid_piece(a) && is_valid_piece(b) && is_valid_piece(c) && !is_valid_piece(d)){ if(((a->piece & b->piece & c->piece) > 0) || ((a->xor & b->xor & c->xor) > 0)){ piece_count=0; for(int i=0; i<16; i++){ if(available[i]){ g = create_piece_from_int(i); if(pieces_equal(a, b, c, (&g))){ piece_count++; } } } if(piece_count>0){ tp->a = a; tp->b = b; tp->c = c; return piece_count; } } }else if(is_valid_piece(a) && is_valid_piece(b) && !is_valid_piece(c) && is_valid_piece(d)){ if (((a->piece & b->piece & d->piece) > 0) || ((a->xor & b->xor & d->xor) > 0)){ piece_count=0; for(int i=0; i<16; i++){ if(available[i]){ g = create_piece_from_int(i); if(pieces_equal(a, b, d, (&g))){ piece_count++; } } } if(piece_count>0){ tp->a = a; tp->b = b; tp->c = d; return piece_count; } } }else if(is_valid_piece(a) && !is_valid_piece(b) && is_valid_piece(c) && is_valid_piece(d)){ if (((a->piece & c->piece & d->piece) > 0) || ((a->xor & c->xor & d->xor) > 0)){ piece_count=0; for(int i=0; i<16; i++){ if(available[i]){ g = create_piece_from_int(i); if(pieces_equal(a, d, c, (&g))){ piece_count++; } } } if(piece_count>0){ tp->a = a; tp->b = c; tp->c = d; return piece_count; } } }else if(!is_valid_piece(a) && is_valid_piece(b) && is_valid_piece(c) && is_valid_piece(d)){ if (((d->piece & b->piece & c->piece) > 0) || ((d->xor & b->xor & c->xor) > 0)){ piece_count=0; for(int i=0; i<16; i++){ if(available[i]){ g = create_piece_from_int(i); if(pieces_equal(d, b, c, (&g))){ piece_count++; } } } if(piece_count>0){ tp->a = c; tp->b = b; tp->c = d; return piece_count; } } } return 0; }
int minValue(QuartoPiece a, QuartoBoard *board, MinimaxRes *res, int numPly, int alpha, int beta) { int local_beta = 1000000; for(int i = 0; i < 4; i++){ for(int j = 0; j < 4; j++){ if(!is_valid_piece(&GET_PIECE(j, i, board->board))){ QuartoBoard newB = *board; set_piece(&newB, j, i, &a); int won = quarto_win(&newB); if(newB.size == 16){ //This placement filled up the board which means that //we can't go further down so we just update the x, j //and return the value of this end state res->x = j; res->y = i; return won*-100; }else if(won){ //This is the maximum we can get, which means that we won the game //no reason to go further down since this lead to a victory res->x = j; res->y = i; return -100; }else if(numPly == 0){ //We have reached the bottom of the recursion //and we need only evaluate the possible placements //of the piece that we have gotten int quarto_value = quarto_herustic(&newB)*(-1); if(quarto_value < local_beta){ local_beta = quarto_value; res->x = j; res->y = i; } if(local_beta <= alpha) return local_beta; }else{ int pieces_left[16]; //Array with 0 or 1 to indicate if the pieces //in that index is available prep_available(&newB, pieces_left); for(int k = 0; k < 16; k++){ if(pieces_left[k]){ MinimaxRes r; int value = maxValue(create_piece_from_int(k), &newB, &r, numPly-1, alpha, local_beta); if(value < local_beta){ //The value we got from below is smaller //than the best beta we have found which //means we want that, so we need to update //x, y and update beta res->x = j; res->y = i; res->next_piece = k; local_beta = value; } //Our beta value is smaller than alpha //which means that the max "node" above //will always chose the path which leads //to that alpha value and there is no use //in recursing any more if(local_beta <= alpha) return local_beta; } } } } } } return local_beta; }
// manages the users turn, game state user input loop int user_turn(char board[BOARD_SIZE][BOARD_SIZE], COLOR color){ get_all_moves(board, color); if (moves_head == NULL) return WIN_POS; char *word1; char *command = NULL; Move* new_move = NULL; int ret_val; while (1){ printf(ENTER_YOUR_MOVE); if (new_move != NULL) clear_old_moves(new_move); new_move = malloc(sizeof(Move)); new_move->dest = malloc(sizeof(Pos) * 2 * BOARD_SIZE); new_move->next = NULL; if (command != NULL) free(command); command = input2str(stdin); word1 = strtok(command, " "); if (strcmp(word1, "quit") == 0){ ret_val = QUIT; break; } else if (strcmp(word1, "get_moves") == 0){ print_moves(moves_head); continue; } else if (strcmp(word1, "move") == 0){ char * piece_coor1 = strtok(NULL, " <,>"); char * piece_coor2 = strtok(NULL, " <,>"); new_move->piece.col = piece_coor1[0] - 'a'; new_move->piece.row = atoi(piece_coor2) - 1; if (!is_valid_pos(new_move->piece)){ printf(WRONG_POSITION); continue; } int i = 0; char * dest_coor1 = strtok(NULL, " <,>to"); char * dest_coor2 = strtok(NULL, " <,>to"); while (dest_coor1 != NULL){ new_move->dest[i].col = dest_coor1[0] - 'a'; new_move->dest[i].row = atoi(dest_coor2) - 1; if (!is_valid_pos(new_move->dest[i])){ i = -1; break; } i++; dest_coor1 = strtok(NULL, " <,>[]"); if (dest_coor1 != NULL) dest_coor2 = strtok(NULL, " <,>[]"); } if (i == -1){ printf(WRONG_POSITION); continue; } if (!is_valid_piece(board, new_move, color)){ printf(NO_DISC); continue; } new_move->dest = realloc(new_move->dest, sizeof(Pos) * i); new_move->captures = i; Move * move2do = is_valid_move(moves_head, new_move); if (move2do == NULL){ printf(ILLEGAL_MOVE); continue; } else{ exc_move(board, move2do); print_board(board); ret_val = GAME_ON; break; } } else printf(ILLEGAL_COMMAND); } free(command); clear_old_moves(new_move); clear_old_moves(moves_head); return ret_val; }