Beispiel #1
0
int test_scout() {
	Board b, orig;
	Move *moves, *omoves;
	int status, i, j, n, m;
	status = WIN;
	
	printf("testing scout\n");
	setup(&b);
	setup(&orig);
	printb(&b);

	moves = gen_moves(&b, &n);
	for(i=0; i<n && i<5; i++) {
		printf("=============================================\napplying move %d\n", i+1);
		apply_move(&b, moves[i]);
		
		omoves = gen_moves(&b, &m);
		for(j=0; j<m && j<5; j++) {
			
			printf("second move %d\n", j+1);
			apply_move(&b, omoves[j]);
			printb(&b);
			printf("eval = %d\n", eval(&b));
			undo_move(&b, omoves[j]);
			
		}
		free(omoves);
		
		undo_move(&b, moves[i]);
	}
	free(moves);
	
	return status;
}
Beispiel #2
0
static int extract_ponder_from_tt(RootMove *rm, Pos *pos)
{
  int ttHit;

  assert(rm->pvSize == 1);

  if (!rm->pv[0])
    return 0;

  do_move(pos, rm->pv[0], gives_check(pos, pos->st, rm->pv[0]));
  TTEntry *tte = tt_probe(pos_key(), &ttHit);

  if (ttHit) {
    Move m = tte_move(tte); // Local copy to be SMP safe
    ExtMove list[MAX_MOVES];
    ExtMove *last = generate_legal(pos, list);
    for (ExtMove *p = list; p < last; p++)
      if (p->move == m) {
        rm->pv[rm->pvSize++] = m;
        break;
      }
  }

  undo_move(pos, rm->pv[0]);
  return rm->pvSize > 1;
}
Beispiel #3
0
void backtrack(int a[], int k, Graph *graph){     
	int ncandidatos, i, c[graph->numero_vertices];
	if ( is_solution(a, k, graph) ){
        
        int solucao_encontrada = distancia_total(a, k, graph);
        
        if(solucao_atual == 0 || solucao_atual > solucao_encontrada){
            solucao_atual = solucao_encontrada;
            printf("\nNova solucao Melhor: %d\n", solucao_atual);
            imprimir_rota(a, k, graph);
        }
        return;
	}
	else {
		k=k+1;

        if(k > (graph->numero_vertices +1)) {
             return;
        }
    	construct_candidate(a, k, graph, c, &ncandidatos);
    	
    	for ( i=0; i < ncandidatos; i++){
    		a[k] = c[i];
    		backtrack(a, k, graph);
            undo_move(a, k, graph);
    	}
	}
}
/*
**AI的核心,得到当前走法的权重
*/
int weigh_move(int x, int y, int who, int max_depth, int add)
{
	int bottom, top, i, move_weight = 0, depth_weight = 0, temp;
	bottom = flips_next;
	make_move(x, y, who);
	top = flips_next;
	for (i = bottom; i < top; ++i)
		move_weight += (add ? 1 : (-1)) * weight[flips_x[i]][flips_y[i]];
	if (max_depth && empties())
	{
		who = OPPOSITE(who);
		add = !add;
		bottom = moves_next;
		get_all(who);
		top = moves_next;
		for(i = bottom; i < top; ++i)
		{
			temp = weigh_move(moves_x[i], moves_y[i], who, max_depth - 1, add);
			if(temp < depth_weight)
				depth_weight = temp;
		}
		unget_all();
    }
    undo_move();
    return move_weight + depth_weight;
}
Beispiel #5
0
void nearby_greedy(int *move, int x, int y)
{
   int i, j;
   double score, bestscore = -1;
   int best_move[2];

   for(i = -1; i <= 1; i++)
   {
      for(j = -1; j <= 1; j++)
      {
         if((x + i < 0) || (x + i >= BOARD_SIZE) || (y + i < 0) || (y + i >= BOARD_SIZE))
            continue;
         if(!stone_check(x + i, y + j))
            continue;
         move[0] = x + i; 
         move[1] = y + j;
         score = do_move(move);
         if(score > bestscore)
         {
            bestscore = score;
            best_move[0] = move[0];
            best_move[1] = move[1];
         }
         undo_move(move);
      }
   }
   move[0] = best_move[0];
   move[1] = best_move[1];
}
Beispiel #6
0
/*
    Tests `make_move` and `undo_move` by making and undoing every single move for
    a set of fen positions, then making sure the resulting board is the same as the
    original board.
*/
int test_make_undo_move() {
    static const char* fen_positions[] = { "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
        "1B4n1/5k2/5P2/4p2N/P2QR3/p2pb2K/3Pb2R/8 w - - 0 1", "3b4/N2P1p2/1kP4p/p4n1K/2p5/1PPp2P1/1p6/8 w - - 0 1",
        "8/4p3/3R4/2P4R/P3Pp2/6kp/8/K7 w - - 0 1", "8/8/p3K3/k1N2B2/3P4/6Pp/5P1p/4R3 w - - 0 1",
        "K7/8/1P3r2/1P2b1p1/8/5p2/1Q2q2n/7k b - - 0 1", "1q1rr3/P1RN1P1p/Kp3p2/2P2p2/b4pP1/n4kb1/8/3Rn3 b - - 0 1",
        "8/2P5/Bp4Kn/q7/5P2/k7/8/B7 b - - 0 1", "8/8/1B6/8/P1b4K/8/8/1k6 b - - 0 1",
        "8/P2kp3/7B/1R3r2/1bK4p/Q4pp1/P6N/8 b - - 0 1" };

    board_t board[ELEMENTS_IN(fen_positions)], board_original[ELEMENTS_IN(fen_positions)];
    move_t move_list[256];

    for (int i = 0; i < ELEMENTS_IN(fen_positions); i++) {
        set_board_from_fen_string(&board[i], fen_positions[i]);
        memcpy(&board_original[i], &board[i], sizeof(board_t));
    }

    for (int i = 0; i < ELEMENTS_IN(fen_positions); i++) {
        move_t* end_of_list = generate_pseudolegal_moves(&board[i], move_list);

        for (int j = 0; move_list[j] != *end_of_list; j++) {
            make_move(&board[i], move_list[j]);
            undo_move(&board[i], move_list[j]);

            if (!compare_boards(&board[i], &board_original[i])) {
                fprintf(stderr, "test_make_undo_move -- Test \"%s\" failed: move %d.\n", fen_positions[i], j);
                return TEST_ERROR;
            }
        }
    }

    return TEST_SUCCESS;
}
Beispiel #7
0
/*
    Makes a move_t from a supplied string (in long algebraic notation), or
    returns 0 if it's an invalid move.
*/
move_t move_from_text(const char* text, board_t* board) {
    int text_length = strlen(text);
    if (text_length != 4 && text_length != 5) return 0;

    move_t move = (((text[1]-'1')*8)+(tolower(text[0])-'a')) + ((((text[3]-'1')*8)+(tolower(text[2])-'a'))<<6);
    if (text_length == 5)
        if (board->to_move == WHITE)
            move |= piece_type_from_char(toupper(text[4]))<<23;
        else
            move |= piece_type_from_char(tolower(text[4]))<<23;

    move_t move_list[MAX_MOVES];
    move_t* end_of_move_list = generate_pseudolegal_moves(board, move_list);
    for (int i = 0; &move_list[i] != end_of_move_list; i++) {
        if ((move_list[i]&0x7800FFF) == move) {
            if (IS_CASTLE(move_list[i]) && !is_legal_castle(board, move_list[i])) return 0;
            make_move(board, move_list[i]);
            if (!is_king_in_check(board, !board->to_move)) {
                undo_move(board, move_list[i]);
                return move_list[i];
            }
       }
    }

    return 0;
}
Beispiel #8
0
int quiescent(app_t *app, cnodeptr_t parent, int alpha, int beta)
{
	int i, score = -MATE;
	node_t node;
	int delta = 200;

	init_node(&node);

	assert(app);

	app->search.nodes++;

	/* max depth */
	if (app->game.board->ply > (SEARCH_MAXDEPTH - 1)) {
		return evaluate(app->game.board);
	}

	/* draws */
	if (repetitions(app) || (app->game.board->half >= 100)) {
		return 0;
	}

	score = evaluate(app->game.board);

	if (score >= beta) return beta;
	/* delta pruning based on a material value of delta (this should be disabled
	   in the endgame) */
	/* The idea here is that, even if our score can improve alpha, it doesn't
	   improve it by a significant amount, so don't bother searching these nodes */
	if (score < (alpha - delta)) return alpha;
	if (score > alpha) alpha = score;


	/* generate moves (with separate captures) */
	generate_moves(app->game.board, &node.ml, &node.cl);

	for (i = 0; i < node.cl.count; i++) {
		/* get the next move ordered */
		next_move(i, &node.cl);

		if (!(do_move(app->game.board, &app->game.undo, node.cl.moves[i])))
			continue;

		score = -quiescent(app, &node, -beta, -alpha);

		node.made++;
		undo_move(app->game.board, &app->game.undo);

		if (score > alpha) {
			if (score >= beta) {
				return beta;
			}

			/* update alpha */
			alpha = score;
		}
	}

	return alpha;
}
Beispiel #9
0
unsigned long long perft(Board *board, int depth) {
    if (is_illegal(board)) {
        return 0L;
    }
    if (depth == 0) {
        return 1L;
    }
    int index = board->hash & MASK;
    Entry *entry = &TABLE[index];
    if (entry->key == board->hash && entry->depth == depth) {
        hits += entry->value;
        return entry->value;
    }
    unsigned long long result = 0;
    Undo undo;
    Move moves[MAX_MOVES];
    int count = gen_moves(board, moves);
    for (int i = 0; i < count; i++) {
        do_move(board, &moves[i], &undo);
        result += perft(board, depth - 1);
        undo_move(board, &moves[i], &undo);
    }
    entry->key = board->hash;
    entry->value = result;
    entry->depth = depth;
    return result;
}
Beispiel #10
0
void
undo(void)
{
    /* turn off Compose key LED */
    setCompLED(0);

    switch (last_action) {
      case F_ADD:
	undo_add();
	break;
      case F_DELETE:
	undo_delete();
	break;
      case F_MOVE:
	undo_move();
	break;
      case F_EDIT:
	undo_change();
	break;
      case F_GLUE:
	undo_glue();
	break;
      case F_BREAK:
	undo_break();
	break;
      case F_LOAD:
	undo_load();
	break;
      case F_SCALE:
	undo_scale();
	break;
      case F_ADD_POINT:
	undo_addpoint();
	break;
      case F_DELETE_POINT:
	undo_deletepoint();
	break;
      case F_ADD_ARROW_HEAD:
	undo_add_arrowhead();
	break;
      case F_DELETE_ARROW_HEAD:
	undo_delete_arrowhead();
	break;
      case F_CONVERT:
	undo_convert();
	break;
      case F_OPEN_CLOSE:
	undo_open_close();
	break;
      case F_JOIN:
      case F_SPLIT:
	undo_join_split();
	break;
    default:
	put_msg("Nothing to UNDO");
	return;
    }
    put_msg("Undo complete");
}
Beispiel #11
0
static bool check_draw(Pos *pos, Move m)
{
  do_move(pos, m, gives_check(pos, pos->st, m));
  bool draw = is_draw(pos); // 64
  undo_move(pos, m);

  return draw;
}
/* Try to find a good move for the specified colour
 */
int
suggest_move(c4_t board, char colour) {
	int c;
	/* look for a winning move for colour */
	for (c=0; c<WIDTH; c++) {
		/* temporarily move in column c... */
		if (do_move(board, c+1, colour)) {
			/* ... and check to see the outcome */
			if (winner_found(board) == colour) {
				/* it is good, so unassign and return c */
				undo_move(board, c+1);
				return c+1;
			} else {
				undo_move(board, c+1);
			}
		}
	}
	/* ok, no winning move, look for a blocking move */
	if (colour == RED) {
		colour = YELLOW;
	} else {
		colour = RED;
	}
	for (c=0; c<WIDTH; c++) {
		/* temporarily move in column c... */
		if (do_move(board, c+1, colour)) {
			/* ... and check to see the outcome */
			if (winner_found(board) == colour) {
				/* it is good, so unassign and return c */
				undo_move(board, c+1);
				return c+1;
			} else {
				undo_move(board, c+1);
			}
		}
	}
	/* no moves found? then pick at random... */
	c = rand()%WIDTH;
	while (board[HEIGHT-1][c]!=EMPTY) {
		c = rand()%WIDTH;
	}
	return c+1;
}
Beispiel #13
0
// Randomly where we don't own 
void decent_random(int *move)
{
    move[0] = get_random_int();
    move[1] = get_random_int();
    while(do_move(move) < 0 || board[move[0]][move[1]] > 0)
    {
        move[0] = get_random_int();
        move[1] = get_random_int();
    }
    undo_move(move);
}
Beispiel #14
0
int value(int alpha, int beta, int depth, int max, int algo, int max_depth)
{
    int v = -INF, i, move[2];

    if (depth > min(max_depth, NUM_MOVES_REMAINING)){
        return eval_fn();
    }
    
    for (i = 0; i < MAX_NUMBER_OF_POINTS; i++)
    {
        if(!next_point(move, i, algo))
            break;
        if(do_move(move) > 0)
        {
            if (max)
            {
                v = max(v, value(alpha, beta, depth + 1, 0, algo, max_depth));
                if (v >= beta)
                {
                    undo_move(move);
                    return v;
                }
                alpha = max(alpha, v);
            }
            else
            {
                v = min(v, value(alpha, beta, depth + 1, 1, algo, max_depth));
                if (v <= alpha)
                {
                    undo_move(move);
                    return v;
                }
                beta = min(beta, v);
            }
            undo_move(move);
        }
    }
    return v;
}
Beispiel #15
0
static uint64_t perft_helper(Pos *pos, Depth depth, uint64_t nodes)
{
  ExtMove *m = (pos->st-1)->endMoves;
  ExtMove *last = pos->st->endMoves = generate_legal(pos, m);
  for (; m < last; m++) {
    do_move(pos, m->move, gives_check(pos, pos->st, m->move));
    if (depth == 0) {
      nodes += generate_legal(pos, last) - last;
    } else
      nodes = perft_helper(pos, depth - ONE_PLY, nodes);
    undo_move(pos, m->move);
  }
  return nodes;
}
Beispiel #16
0
/* Get a list of available book moves.
   The book can be a tree (AvlNode *book), or a file if <book> is NULL.
   Returns the combined score of all the moves if successfull.  */
static int
get_book_move_list(Board *board, MoveLst *move_list, const AvlNode *book)
{
	int i, npos, tot_score;
	FILE *fp = NULL;

	ASSERT(1, board != NULL);
	ASSERT(1, move_list != NULL);

	npos = 0;
	tot_score = 0;
	if (book == NULL) {
		if ((fp = fopen(settings.book_file, "rb")) == NULL) {
			my_perror("Can't open file %s", settings.book_file);
			return -1;
		}

		npos = get_pos_count(fp);
		if (npos <= 0) {
			fprintf(stderr, "The opening book is empty\n");
			return -1;
		}
	}

	gen_moves(board, move_list);
	for (i = 0; i < move_list->nmoves; i++) {
		U32 move = move_list->move[i];
		int *score = &move_list->score[i];

		make_move(board, move);
		if (get_nrepeats(board, 1) == 0) {
			U64 key = board->posp->key;
			if (book == NULL)
				*score = find_disk_pos(fp, key, npos);
			else
				*score = find_ram_pos(key, book);
		} else
			*score = VAL_NONE;
		if (*score != VAL_NONE)
			tot_score += *score;
		undo_move(board);
	}
	if (book == NULL)
		my_close(fp, settings.book_file);
	
	return tot_score;
}
Beispiel #17
0
/* Returns 1 if player c has valid moves left.
 * c = 1 (white) or -1 (black)
 * If king is in check and no valid moves left, that implies checkmate */
int has_moves(board * brd, int c) {
  move * t = malloc(sizeof(move));
  init_move(&t);
  generate_all_moves(c, brd, &t);
  move * t_ = t;
  while(t_->next != NULL) {
    if(apply_move(brd, &t_, c)) {
      undo_move(brd, &t_);
      free_moves(&t);
      return 1;

    }
    t_ = t_->next;
  }
  free_moves(&t);
  return 0;
}
Beispiel #18
0
int main(int argc, char* argv[]) {
    setlocale(LC_ALL, "");

    board_t board;
    RESET_BOARD(board);
    char buffer[256];
    int len;

    hash_init();
    run_all_tests();
    print_board(&board);

    move_t m;
    while (1) {
    top:
        print_board(&board);
        move_t move = 0;
        while (move == 0)
        { 
            printf("enter move> ");
            fgets(buffer, 255, stdin);
            if (!strcmp(buffer, "undo\n")) {
                undo_move(&board, m);            
                goto top;
            } else if (!strcmp(buffer, "exit\n")) {
                return 0;
            }
            len = strlen(buffer);
            if (buffer[len-1] == '\n') buffer[len-1] = '\0';
            move = move_from_text(buffer, &board);
        }
        make_move(&board, move);
        print_board(&board);

        printf("Calculating...\n");
        m = think(&board);
        make_move(&board, m);
        printf("Done calculating...\n");
        print_move(m);
    }
    return 0;
}
Beispiel #19
0
std::string Protocol::search_move(bool use_san_notation)
{
    Move m = game.root(depth + 1);
    if (m.is_null()) {
        if (game.is_check(game.current_position().side())) {
            return "LOST";
        }
        return "DRAW";
    } else {
        if (use_san_notation) {
            std::string res = game.output_move(m);
            play_move(m.to_string());
            if (game.is_check(game.current_position().side())) {
                res += "+";
            }
            undo_move();
            return res;
        }
        return m.to_string();
    }
}
Beispiel #20
0
/* Recursively realizes feasible sequences of moves and calls
 * the evaulation function
 */
void alpha_better(int *move, int algo, int max_depth)
{
    int best_v = 2 * INF, v = INF, i;
    int best_move[2];
    int max = next_to_set;

    if(max)
        best_v = -2 * INF;
     
    for (i = 0; i < MAX_NUMBER_OF_POINTS; i = i + 1)
    {
        if(!next_point(move, i, algo))
            break;
	if (do_move(move) == -1)
            continue;
        if (max)
        {
            v = value(-1 * INF, INF, 1, 0, algo, max_depth);
            if (v > best_v)
            {
                best_v = v;
                best_move[0] = move[0];
                best_move[1] = move[1];
            }
        }
        else
        {
            v = value(-1 * INF, INF, 1, 1, algo, max_depth);
            if (v < best_v)
            {
                best_v = v;
                best_move[0] = move[0]; 
                best_move[1] = move[1];
            }
        }
        undo_move(move);
    }
    move[0] = best_move[0];
    move[1] = best_move[1];
} 
Beispiel #21
0
int test_apply_move() {
	int status, num_moves, i;
	Board board;
	Move *moves;
	status = WIN;
	num_moves = 0;
	setup(&board);
	printb(&board);
	moves = gen_moves(&board, &num_moves);
	printf("found %d moves\n", num_moves);
	for(i=0; i<35 && i<num_moves; i++) {
		printf("move %d: %d is moving from %d to %d, capturing %d\n",
			i, moving_type(moves[i]), moves[i].from,
			moves[i].to, capturing_type(moves[i]));
		printf("board after move %d looks like:\n", i+1);
		apply_move(&board, moves[i]);
		printb(&board);
		undo_move(&board, moves[i]);
	}
	
	return status;
}
Beispiel #22
0
int solve_step(t_pos *pos) {
  int moves[MAX_MOVES * 2];
  int count = find_moves(pos, moves);
  int i, cell_id, value, ret, cur_status;
  //print_moves(count, moves);
  for (i = 0; i < count; ++i) {
    cell_id = moves[2 * i];
    value = moves[2 * i + 1];
    ret = OPEN;
    play_move(pos, cell_id, value);
    cur_status = solved(pos);
    if (cur_status != OPEN) {
      ret = cur_status;
    } else if (solve_step(pos) == SOLVED) {
      ret = SOLVED;
    }
    undo_move(pos, cell_id, value);
    if (ret == SOLVED) {
      return ret;
    }
  }
  return IMPOSSIBLE;
}
Beispiel #23
0
uint64_t perft(Pos *pos, Depth depth)
{
  uint64_t cnt, nodes = 0;
  char buf[16];

  ExtMove *m = pos->moveList;
  ExtMove *last = pos->st->endMoves = generate_legal(pos, m);
  for (; m < last; m++) {
    if (depth <= ONE_PLY) {
      cnt = 1;
      nodes++;
    } else {
      do_move(pos, m->move, gives_check(pos, pos->st, m->move));
      if (depth == 2 * ONE_PLY)
        cnt = generate_legal(pos, last) - last;
      else
        cnt = perft_helper(pos, depth - 3 * ONE_PLY, 0);
      nodes += cnt;
      undo_move(pos, m->move);
    }
    printf("%s: %"PRIu64"\n", uci_move(buf, m->move, is_chess960()), cnt);
  }
  return nodes;
}
Beispiel #24
0
/* --------------------------------------------------------------*/
void
play_gmp(Gameinfo *gameinfo, int simplified)
{
  SGFTree sgftree;

  Gmp *ge;
  GmpResult message;
  const char *error;
  
  int i, j;
  int passes = 0; /* two passes and its over */
  int to_move;  /* who's turn is next ? */

  int mycolor = -1;  /* who has which color */
  int yourcolor;

  if (gameinfo->computer_player == WHITE)
    mycolor = 1;
  else if (gameinfo->computer_player == BLACK)
    mycolor = 0;

  sgftree_clear(&sgftree);
  sgftreeCreateHeaderNode(&sgftree, board_size, komi, gameinfo->handicap);

  ge = gmp_create(0, 1);
  TRACE("board size=%d\n", board_size);

  /* 
   * The specification of the go modem protocol doesn't even discuss
   * komi. So we have to guess the komi. If the komi is set on the
   * command line, keep it. Otherwise, its value will be 0.0 and we
   * use 5.5 in an even game, 0.5 otherwise.
   */
  if (komi == 0.0) {
    if (gameinfo->handicap == 0)
      komi = 5.5;
    else
      komi = 0.5;
  }

  if (!simplified) {
    /* Leave all the -1's so the client can negotiate the game parameters. */
    if (chinese_rules)
      gmp_startGame(ge, -1, -1, 5.5, -1, mycolor, 0);
    else
      gmp_startGame(ge, -1, -1, 5.5, 0, mycolor, 0);
  }
  else {
    gmp_startGame(ge, board_size, gameinfo->handicap,
		  komi, chinese_rules, mycolor, 1);
  }

  do {
    message = gmp_check(ge, 1, NULL, NULL, &error);
  } while (message == gmp_nothing || message == gmp_reset);
  
  if (message == gmp_err) {
    fprintf(stderr, "gnugo-gmp: Error \"%s\" occurred.\n", error);
    exit(EXIT_FAILURE);
  }
  else if (message != gmp_newGame) {
    fprintf(stderr, "gnugo-gmp: Expecting a newGame, got %s\n",
	    gmp_resultString(message));
    exit(EXIT_FAILURE);
  }

  gameinfo->handicap = gmp_handicap(ge);
  if (!check_boardsize(gmp_size(ge), stderr))
    exit(EXIT_FAILURE);
  
  gnugo_clear_board(gmp_size(ge));

  /* Let's pretend GMP knows about komi in case something will ever change. */
  komi = gmp_komi(ge);

#if ORACLE
  if (metamachine && oracle_exists)
    oracle_clear_board(board_size);
#endif

  sgfOverwritePropertyInt(sgftree.root, "SZ", board_size);

  TRACE("size=%d, handicap=%d, komi=%f\n", board_size,
	gameinfo->handicap, komi);

  if (gameinfo->handicap)
    to_move = WHITE;
  else
    to_move = BLACK;

  if (gmp_iAmWhite(ge)) {
    mycolor = WHITE;     /* computer white */
    yourcolor = BLACK;   /* human black */
  }
  else {
    mycolor = BLACK;
    yourcolor = WHITE;
  }

  gameinfo->computer_player = mycolor;
  sgf_write_header(sgftree.root, 1, get_random_seed(), komi,
		   gameinfo->handicap, get_level(), chinese_rules);
  gameinfo->handicap = gnugo_sethand(gameinfo->handicap, sgftree.root);
  sgfOverwritePropertyInt(sgftree.root, "HA", gameinfo->handicap);

  /* main GMP loop */
  while (passes < 2) {

    if (to_move == yourcolor) {
      int move;
      /* Get opponent's move from gmp client. */
      message = gmp_check(ge, 1, &j, &i, &error);

      if (message == gmp_err) {
	fprintf(stderr, "GNU Go: Sorry, error from gmp client\n");
        sgftreeAddComment(&sgftree, "got error from gmp client");
        sgffile_output(&sgftree);
	return;
      }

      if (message == gmp_undo) {
	int k;
	assert(j > 0);
	
	for (k = 0; k < j; k++) {
	  if (!undo_move(1)) {
	    fprintf(stderr, "GNU Go: play_gmp UNDO: can't undo %d moves\n",
		    j - k);
	    break;
	  }
	  sgftreeAddComment(&sgftree, "undone");
	  sgftreeBack(&sgftree);
	  to_move = OTHER_COLOR(to_move);
	}
	continue;
      }

      if (message == gmp_pass) {
	passes++;
	move = PASS_MOVE;
      }
      else {
	passes = 0;
	move = POS(i, j);
      }

      TRACE("\nyour move: %1m\n\n", move);
      sgftreeAddPlay(&sgftree, to_move, I(move), J(move));
      gnugo_play_move(move, yourcolor);
      sgffile_output(&sgftree);

    }
    else {
      /* Generate my next move. */
      float move_value;
      int move;
      if (autolevel_on)
	adjust_level_offset(mycolor);
      move = genmove(mycolor, &move_value, NULL);
      gnugo_play_move(move, mycolor);
      sgffile_add_debuginfo(sgftree.lastnode, move_value);
      
      if (is_pass(move)) {
	/* pass */
        sgftreeAddPlay(&sgftree, to_move, -1, -1);
	gmp_sendPass(ge);
	++passes;
      }
      else {
	/* not pass */
        sgftreeAddPlay(&sgftree, to_move, I(move), J(move));
	gmp_sendMove(ge, J(move), I(move));
	passes = 0;
	TRACE("\nmy move: %1m\n\n", move);
      }
      sgffile_add_debuginfo(sgftree.lastnode, 0.0);
      sgffile_output(&sgftree);
    }
    
    to_move = OTHER_COLOR(to_move);
  }
  
  /* two passes: game over */
  gmp_sendPass(ge);   
  
  if (!quiet)
    fprintf(stderr, "Game over - waiting for client to shut us down\n");
  who_wins(mycolor, stderr);

  if (showtime) {
    gprintf("\nSLOWEST MOVE: %d at %1m ", slowest_movenum, slowest_move);
    fprintf(stderr, "(%.2f seconds)\n", slowest_time);
    fprintf(stderr, "\nAVERAGE TIME: %.2f seconds per move\n",
	    total_time / movenum);
    fprintf(stderr, "\nTOTAL TIME: %.2f seconds\n",
	    total_time);
  }
  
  
  /* play_gmp() does not return to main(), therefore the score
   * writing code is here.
   */
  { 
    float score = gnugo_estimate_score(NULL, NULL);
    sgfWriteResult(sgftree.root, score, 1);
  }
  sgffile_output(&sgftree);

  if (!simplified) {
    /* We hang around here until cgoban asks us to go, since
     * sometimes cgoban crashes if we exit first.
     *
     * FIXME: Check if this is still needed.  I made it dependand on
     *	      `simplifed' just to avoid changes in GMP mode.
     */
    while (1) {
      message = gmp_check(ge, 1, &j, &i, &error);
      if (!quiet)
	fprintf(stderr, "Message %d from gmp\n", message);
      if (message == gmp_err)
	break;
    }
  }

#if ORACLE
  if (metamachine && oracle_exists)
    dismiss_oracle();
#endif

  if (!quiet)
    fprintf(stderr, "gnugo going down\n");
}
Beispiel #25
0
int alpha_beta(app_t *app, cnodeptr_t parent, int alpha, int beta, int depth)
{
	int palpha = alpha;
	int i, score = -MATE, highest = -MATE;
	node_t node;
	move_t cutoff = 0;
	piece_t p;

	init_node(&node);

	assert(app);

	app->search.nodes++;
	node.depth = depth;

	/* max depth */
	if (app->game.board->ply > (SEARCH_MAXDEPTH - 1)) {
		/* return evaluate(app->board); */
		return quiescent(app, parent, alpha, beta);
	}

	/* recursive base */
	if (depth == 0) {
		return evaluate(app->game.board);
	}

	/* draws */
	if (repetitions(app) || (app->game.board->half >= 100)) {
		return 0;
	}

	/* if we are checked, set the nodes checked flag */
	if (check(app->game.board, app->game.board->side)) {
		node.flags |= NODE_CHECK;
		/* extend our search by 1 depth if we are in check */
		/* NOTES: we may want to NOT extend our search here if the parent
		   is in check, because the means we already extended once */
		depth++;
	}

	/* TODO:
	     - NULL moves
	     - Late-move reduction
	     - Tactical extensions (pins & forks -> depth++)
	 */

	/* probe our table */
	if (probe_hash(&app->hash, app->game.board, &cutoff, &score, depth, alpha, beta) == TRUE) {
		app->hash.cut++;
		return score;
	}

	/* generate moves */
	generate_moves(app->game.board, &node.ml, &node.ml);

	/* reset score */
	score = -MATE;

	/* try to match our hash hit move */
	if (cutoff != 0) {
		for (i = 0; i < node.ml.count; i++) {
			if (node.ml.moves[i] == cutoff) {
				node.ml.scores[i] = 20000;
				break;
			}
		}
	}

	/* search negamax */
	for (i = 0; i < node.ml.count; i++) {
		/* get the next move ordered */
		next_move(i, &node.ml);

		if (!(do_move(app->game.board, &app->game.undo, node.ml.moves[i])))
			continue;

		score = -alpha_beta(app, &node, -beta, -alpha, depth - 1);

		node.made++;
		undo_move(app->game.board, &app->game.undo);

		/* score whatever is best so far */
		if (score > highest) {
			node.best = node.ml.moves[i];
			highest = score;

			/* update alpha */
			if (score > alpha) {
				if (score >= beta) {

					/* non-captures causing beta cutoffs (killers) */
					if (!is_capture(node.ml.moves[i])) {
						app->game.board->killers[1][app->game.board->ply] =
							app->game.board->killers[0][app->game.board->ply];
						app->game.board->killers[0][app->game.board->ply] = node.ml.moves[i];
					}

					/* store this beta in our transposition table */
					store_hash(&app->hash, app->game.board, node.best, beta, HASH_BETA, depth);

					return beta;
				}

				/* update alpha */
				alpha = score;

				/* update our history */
				if (!is_capture(node.best)) {
					p = app->game.board->pos.squares[move_from(node.best)];
					app->game.board->history[piece_color(p)][piece_type(p)][move_to(node.best)] += depth;
				}
			}
		}
	}

	/* check for checkmate or stalemate */
	if (!node.made) {
		if (node.flags & NODE_CHECK) {
			return -MATE + app->game.board->ply;
		} else {
			return 0;
		}
	}

	if (alpha != palpha) {
		/* store this as an exact, since we beat alpha */
		store_hash(&app->hash, app->game.board, node.best, highest, HASH_EXACT, depth);
	} else {
		/* store the current alpha */
		store_hash(&app->hash, app->game.board, node.best, alpha, HASH_ALPHA, depth);
	}

	return alpha;
}
static score_t scout_search(searchNode *node, int depth,
                            uint64_t *node_count_serial) {
  // Initialize the search node.
  initialize_scout_node(node, depth);

  // check whether we should abort
  if (should_abort_check() || parallel_parent_aborted(node)) {
    return 0;
  }

  // Pre-evaluate this position.
  leafEvalResult pre_evaluation_result = evaluate_as_leaf(node, SEARCH_SCOUT);

  // If we decide to stop searching, return the pre-evaluation score.
  if (pre_evaluation_result.type == MOVE_EVALUATED) {
    return pre_evaluation_result.score;
  }

  // Populate some of the fields of this search node, using some
  //  of the information provided by the pre-evaluation.
  int hash_table_move = pre_evaluation_result.hash_table_move;
  node->best_score = pre_evaluation_result.score;
  node->quiescence = pre_evaluation_result.should_enter_quiescence;

  // Grab the killer-moves for later use.
  move_t killer_a = killer[KMT(node->ply, 0)];
  move_t killer_b = killer[KMT(node->ply, 1)];

  // Store the sorted move list on the stack.
  //   MAX_NUM_MOVES is all that we need.
  sortable_move_t move_list[MAX_NUM_MOVES];

  // Obtain the sorted move list.
  int num_of_moves = get_sortable_move_list(node, move_list, hash_table_move);

  int number_of_moves_evaluated = 0;


  // A simple mutex. See simple_mutex.h for implementation details.
  simple_mutex_t node_mutex;
  init_simple_mutex(&node_mutex);

  // Sort the move list.
  sort_incremental(move_list, num_of_moves, 0);
  
  moveEvaluationResult result;

  for (int mv_index = 0; mv_index < num_of_moves; mv_index++) {
    if (mv_index == 1) {
      sort_full(move_list, num_of_moves);
     // sortable_move_t new_move_list[MAX_NUM_MOVES];
      //memcpy(new_move_list, move_list, num_of_moves*sizeof(sortable_move_t));
     //: sort_incremental_full(move_list,num_of_moves);
    }
    // Get the next move from the move list.
    int local_index = number_of_moves_evaluated++;
    move_t mv = get_move(move_list[local_index]);

    if (TRACE_MOVES) {
      print_move_info(mv, node->ply);
    }

    // increase node count
    __sync_fetch_and_add(node_count_serial, 1);

    evaluateMove(&result, node, mv, killer_a, killer_b,
                 SEARCH_SCOUT,
                 node_count_serial);
    undo_move(&result.next_node, mv);

    if (result.type == MOVE_ILLEGAL || result.type == MOVE_IGNORE
        || abortf || parallel_parent_aborted(node)) {
      continue;
    }

    // A legal move is a move that's not KO, but when we are in quiescence
    // we only want to count moves that has a capture.
    if (result.type == MOVE_EVALUATED) {
      node->legal_move_count++;
    }

    // process the score. Note that this mutates fields in node.
    bool cutoff = search_process_score(node, mv, local_index, &result, SEARCH_SCOUT);

    if (cutoff) {
      node->abort = true;
      break;
    }
  }

  if (parallel_parent_aborted(node)) {
    return 0;
  }

  if (node->quiescence == false) {
    update_best_move_history(node->position, node->best_move_index,
                             move_list, number_of_moves_evaluated);
  }

  tbassert(abs(node->best_score) != -INF, "best_score = %d\n",
           node->best_score);

  // Reads node->position->key, node->depth, node->best_score, and node->ply
  update_transposition_table(node);

  return node->best_score;
}
Beispiel #27
0
static int print_pv(app_t *app, int depth)
{
	int count = 0;
	piece_t p;
	u8 from;
	move_t mv;

	assert(app);
	assert(app->game.board);

#define VIA_PROBE
#ifdef VIA_PROBE
	/* get the current position */
	mv = probe_hash_move(&app->hash, app->game.board->key);
	
	while ((mv != 0) && (count < depth)) {
		
		if (find_move(app, mv)) {
			from = move_from(mv);
			p = app->game.board->pos.squares[from];

			if (do_move(app->game.board, &app->game.undo, mv)) {
				app->search.pv[count++] = mv;

				print_algebraic(p, mv);
				printf(" ");
			} else {
				break;
			}
		} else {
			break;
		}

		mv = probe_hash_move(&app->hash, app->game.board->key);
	}
#else
	while (app->search.pv[count]) {
		mv = app->search.pv[count++];
		
		if (find_move(app, mv)) {
			from = move_from(mv);
			p = app->board->pos.squares[from];

			if (do_move(app->board, &app->ul, mv)) {
				print_algebraic(p, mv);
				printf(" ");
			} else {
				break;
			}
		} else {
			break;
		}
	}
#endif
	
	while (app->game.board->ply) {
		undo_move(app->game.board, &app->game.undo);
	}
	
	return count;
}
Beispiel #28
0
/**
 * @brief       Builds move tree.
 *
 * Builds a complete move tree recursively.
 *
 * @param[in]   color       Color to move
 * @param[out]  *i_selected Pointer to horizontal coordinate of selected move.
 * @param[out]  *j_selected Pointer to vertical coordinate of selected move.
 * @return      Nothing
 * @note        If no move is selected i and j are INVALID.
 */
void search_tree( int color, int *i_selected, int *j_selected )
{
    // Index variables:
    int k;
    int d;
    //int m;   //DEBUG
    int i, j;

    // Variables for search tree:
    int depth = 0;
    int best_value;
    int search_level_incr;
    int alpha;
    int beta;

    // Variables for move list:
    int valid_moves[BOARD_SIZE_MAX * BOARD_SIZE_MAX][4];
    int nr_of_valid_moves;
    int nr_of_valid_moves_cut;

    // Variables for measuring time:
    time_t start;
    time_t stop;
    time_t diff_time;

    // Variables needed for logging:
    char x[2];
    char y[3];

    // Setting to root level values:
    alpha = INT_MIN;
    beta  = INT_MAX;
    hash_hit    = 0;
    alpha_break = 0;
    beta_break  = 0;
    search_level_incr = 0;
    count_quiet_search = 0;

    //init_brains();
    init_search_stats();
    //init_hash_table();

    best_value = ( color == BLACK ) ? INT_MIN : INT_MAX;

    node_count = 0;

    if ( do_log ) {
        log_file = fopen( LOG_FILE, "w" );
        if ( log_file == NULL ) {
            printf( "# Cannot open log file\n" );
            exit(1);
        }
    }

    (void) time(&start);

    nr_of_valid_moves     = get_valid_move_list( color, valid_moves );
    nr_of_valid_moves_cut = nr_of_valid_moves;

    // Loop start:
    search_level_incr = get_search_depth();
    for ( d = 0; d <= search_level_incr; d++ ) {
        set_search_depth(d);
        //set_search_level(search_level_incr);
        //l = search_level_incr;

        // Go through move list:
        for ( k = 0; k < nr_of_valid_moves_cut; k++ ) {
            i = valid_moves[k][0];
            j = valid_moves[k][1];

            // Make move:
            node_count++;
            //printf( "# Level: %d make: %d,%d value: %d\n", l, i, j, best_value );
            make_move( color, i, j );
            //i_to_x( i, x );
            //j_to_y( j, y );
            //printf( "## %s%s\n", x, y );

            // Start recursion:
            valid_moves[k][2] = add_node( color * -1, depth, alpha, beta );

            if ( color  == BLACK ) {
                // For black: remember highest value
                if ( valid_moves[k][2] > best_value ) {
                    best_value = valid_moves[k][2];
                    if ( best_value > alpha ) {
                        alpha = best_value;
                    }
                }
            }
            else {
                // For white: remember lowest value
                if ( valid_moves[k][2] < best_value ) {
                    best_value = valid_moves[k][2];
                    if ( best_value < beta ) {
                        beta = best_value;
                    }
                }
            }

            if ( do_log ) {
                i_to_x( i, x );
                j_to_y( j, y );
                fprintf( log_file, "%s%s (%d) (a: %d, b: %d)\n"
                    , x, y, valid_moves[k][2], alpha, beta );
            }

            undo_move();
        }

        // Sort move list by value:
        if ( color == BLACK ) {
            qsort( valid_moves, (size_t)nr_of_valid_moves_cut, sizeof(valid_moves[0]), compare_value_black );
        }
        else {
            qsort( valid_moves, (size_t)nr_of_valid_moves_cut, sizeof(valid_moves[0]), compare_value_white );
        }

        // DEBUG:
        /*
        printf( "# Level: %d (%d) - ", l, nr_of_valid_moves_cut );
        for ( m = 0; m < nr_of_valid_moves_cut; m++ ) {
            i_to_x( valid_moves[m][0], x );
            j_to_y( valid_moves[m][1], y );
            printf( "%s%s (%d,%d), ", x, y, valid_moves[m][2], valid_moves[m][3] );
        }
        printf("\n");
        */

        if ( nr_of_valid_moves_cut / 2 > 5 ) {
            nr_of_valid_moves_cut = nr_of_valid_moves_cut / 2;
        }

    }
    // Loop end

    (void) time(&stop);

    diff_time = stop - start;
    if ( diff_time == 0 ) {
        diff_time = 1;
    }

    // Save some stats about this search:
    search_stats.color[0] = '\0';
    if ( color == BLACK ) {
        my_strcpy( search_stats.color, "Black", 6 );
    }
    else {
        my_strcpy( search_stats.color, "White", 6 );
    }
    i_to_x( valid_moves[0][0], x );
    j_to_y( valid_moves[0][1], y );
    search_stats.move[0] = '\0';
    strcat( search_stats.move, x );
    strcat( search_stats.move, y );
    search_stats.level         = search_depth;
    search_stats.duration      = stop - start;
    search_stats.node_count    = node_count;
    search_stats.nodes_per_sec = node_count / diff_time;
    search_stats.qsearch_count = count_quiet_search;
    search_stats.hash_hit      = hash_hit;
    search_stats.alpha_cut     = alpha_break;
    search_stats.beta_cut      = beta_break;
    search_stats.value         = valid_moves[0][2];

    *i_selected = valid_moves[0][0];
    *j_selected = valid_moves[0][1];

    if ( log_file != NULL ) {
        fclose(log_file);
    }

    return;
}
Beispiel #29
0
/**
 * @brief       Adds a new node to the move tree.
 *
 * A new node is added to the move tree. For every move in the generated move
 * list, a new node is added recursively. If a certain level is reached, the
 * recusrion is stopped.
 *
 * @param[in]   color       Color of move to set.
 * @param[in]   depth       Counter that shows the level in the move tree.
 * @param[in]   alpha       Alpha-Beta pruning
 * @param[in]   beta        Alpha-Beta pruning
 * @return      Value of position
 */
int add_node( int color, int depth, int alpha, int beta )
{
    int  k, l;
    int  i, j;
    int  valid_moves[BOARD_SIZE_MAX * BOARD_SIZE_MAX][4];
    int  nr_of_valid_moves;
    int  best_value;
    char x[2];
    char y[3];
    char indent[10];
    int  tactic_move = 0;
    int  qsearch = MAX_QSEARCH_DEPTH;
    //unsigned hash_id;
    int value_list[COUNT_BRAINS] = { 1, 2, 3, 4, 5, 6, 7, 8 };


    best_value = ( color == BLACK ) ? INT_MIN : INT_MAX;
    if ( ! ( ( search_depth + MAX_QSEARCH_DEPTH ) % 2 ) ) {
        qsearch++;
    }

    depth++;

    nr_of_valid_moves = get_valid_move_list( color, valid_moves );

    // PASS if no valid move is possible:
    if ( nr_of_valid_moves == 0 ) {
        make_move( color, INVALID, INVALID );
        best_value = evaluate_position( value_list, false );
        undo_move();
    }

    // Count tactic moves:
    for ( l = 0; l < nr_of_valid_moves; l++ ) {
        if ( valid_moves[l][3] > 0 ) {
            tactic_move++;
        }
    }

    // Go through move list:
    for ( k = 0; k < nr_of_valid_moves; k++ ) {
        i = valid_moves[k][0];
        j = valid_moves[k][1];

        // Skip non-tactical moves in quiescense search:
        if ( depth >= search_depth && tactic_move > 0) {
            if ( valid_moves[k][3] == 0 ) {
                continue;
            }
        }

        // Make move:
        node_count++;
        make_move( color, i, j );


        if ( depth < search_depth ) {
            // Start recursion:
            valid_moves[k][2] = add_node( color * -1, depth, alpha, beta );
        }
        else {
            //insert_hash_table( hash_id, best_value );
            if ( tactic_move && depth < search_depth + qsearch ) {
                valid_moves[k][2] = add_node( color * -1, depth, alpha, beta );
                count_quiet_search++;
            }
            else {
                valid_moves[k][2] = evaluate_position( value_list, true );
            }
        }

        if ( color  == BLACK ) {
            // For black: remember highest value
            if ( valid_moves[k][2] > best_value ) {
                best_value = valid_moves[k][2];
                if ( best_value > alpha ) {
                    alpha = best_value;
                }
            }
        }
        else {
            // For white: remember lowest value
            if ( valid_moves[k][2] < best_value ) {
                best_value = valid_moves[k][2];
                if ( best_value < beta ) {
                    beta = best_value;
                }
            }
        }

        if ( do_log ) {
            i_to_x( i, x );
            j_to_y( j, y );
            indent[0] = '\0';
            for ( l = 1; l <= depth; l++ ) {
                strcat( indent, "\t" );
            }
            fprintf( log_file, "%s%s%s (%d) (T: %d) (%d,%d,%d,%d,%d,%d,%d,%d) (a: %d, b: %d)\n"
                , indent, x, y
                , valid_moves[k][2], valid_moves[k][3], value_list[0], value_list[1]
                , value_list[2], value_list[3], value_list[4], value_list[5]
                , value_list[6], value_list[7]
                , alpha, beta );
        }

        undo_move();

        if ( color == BLACK ) {
            if ( valid_moves[k][2] > beta ) {  // Maybe only '>' is correct?!
                beta_break++;
                break;
            }
        }
        else {
            if ( valid_moves[k][2] < alpha ) { // Maybe only '<' is correct?!
                alpha_break++;
                break;
            }
        }
    }

    return best_value;
}
Beispiel #30
0
void bk( tmove *m, int n )
{
int moves[80], values[80], idx;
int pass;
int sumvalues=0;

#define SHOWECO
#ifdef SHOWECO
static int att=1;
static int seco=0;
if( att != 0 && Eco!=NULL )
{
	typedef struct teco { unsigned hashboard; unsigned point; } teco;
	static struct teco * peco = NULL;
	if( att==1 && Counter==0 && G[Counter].hashboard==0x39512910 /*init*/)
	{
		int c; tmove * move;
		peco = malloc(2048*sizeof(teco));
		if( peco == NULL )
		{ puts(" telluser cannot alloc memory for ECO");
		  att=0; goto abort; }

		printf("telluser creating ECO index, please wait\n");

		while( (c=fgetc(Eco))!='[' ) if( c==EOF ) goto doneinit;
		for(;;)
		{
		  tmove m[128];
		  char ms[32]; int msi; int n;
		  (peco+seco)->point=ftell(Eco)-1;
		  while( (c=getc(Eco))!=']' ) if( c==EOF ) goto doneinit;
		  if( c==EOF ) goto doneinit;
		  setfen("rnbqkbnr/pppppppp/////PPPPPPPP/RNBQKBNR/w");
		  for(;;)
		  {
		 	msi=0;
		  	while( (c=getc(Eco))==' ' || c=='\n' ) {}
			ungetc(c,Eco);
			while( (c=getc(Eco))!=' ' && c!='\n' && msi<=30 )
			{ ms[msi]=c; msi++;
			  if( c==EOF || c=='[' )
			  { ungetc(c,Eco); goto nextgame; }
			}
			ms[msi]='\0'; /* printf("%s ",ms); */
			generate_legal_moves( m, &n, checktest(Color) );
			move=sandex(ms,m,n);
			if( move==NULL ) goto nextgame;
			do_move(move);
		  	if( c==EOF ) goto doneinit;
		  }
		  nextgame:;
		  peco[seco].hashboard=G[Counter].hashboard;
		  seco++;
		  while( (c=fgetc(Eco))!='[' ) if( c==EOF ) goto doneinit;
		}

		doneinit:;
		printf(" parsed %i ECO records\n",seco);
		att=2;
		setfen("rnbqkbnr/pppppppp/////PPPPPPPP/RNBQKBNR/w");
	}

	if( Counter>0 && att>1 )
	{
		int counter=Counter;
		int text=-1;
		while( Counter > 0 )
		{
			int i;
			for(i=0;i!=seco;i++)
			if(peco[i].hashboard==G[Counter].hashboard)
			{ text=peco[i].point; goto foundeco; }
			undo_move( & G[Counter-1].m );
		}
		foundeco:;
		while( counter > Counter ) do_move( & G[Counter].m );
		if( text==-1 ) printf(" no eco found\n");
		else
		{ char t[128], * c;
		  fseek(Eco,text,SEEK_SET);
		  fgets(t,126,Eco);
		  t[127]='\0';
		  c=strchr(t,'['); if(c!=NULL) *c = ' ';
		  c=strrchr(t,']'); if(c!=NULL) *c = '\0';
		  puts(t);
		}
	}
	abort:;
}
#endif

for( pass=0; pass!=2; pass++ )
{
	if( pass==0 )
	{
	int i;
	idx = bookmoves(moves,values,m,n);
	if( idx != -1 ) for(i=0;i!=idx;i++) sumvalues += values[i];
	printf(" primary book moves\n");
	}
	else
	{
	idx = sbookmoves(moves,values,m,n);
	printf(" secondary book moves\n");
	}

	if( idx > 0 )
	{	int i;
		for( i=0; i!=idx; i++ )
		if( i==0 || moves[i-1]!=moves[i] )
		{
			printf("   ");
			printm( m[moves[i]], NULL );
			if(pass==0)
				printf("%3d%%\n",values[i]*100/sumvalues);
			else	printf("\n");
		}
	}
	else
	printf("   no move found\n");

}

if( Flag.xboard>1 ) puts("");

{ char c[128]; postr(c); printf("%s\n",c); }

}