⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 solver.c

📁 黑白棋终局解算程序
💻 C
📖 第 1 页 / 共 5 页
字号:
 * * To be efficient PVS need highly ordered tree. The following ordering have * been made : *       - fixed square ordering : square usually leading to a good move are * visited first, ie from corner squares to X and C squares. *       - most stable ordering : a crude evaluation of stability at the corner * (corner, X and C squares) to order the moves. *       - fast first ordering : the moves leading to the most reduced mobility * for the opponent are played first. *       - best move previously found : If the position have been previously * searched, the best move that was found is replayed as the first move. * * -# Campbell MS & Marsland TA (1983) A Comparison of Minimax Trees Search *    Algorithms. Artificial Intelligence, 20, pp. 347-367. * -# Plaat A. et al. (1996) Best-First Fixed Depth Minimax Algorithms. Artificial      Intelligence, 84, pp. 255-293. * -# Weill JC. (1992) Experiments With The NegaC* Search. An alternative for Othello *    Endgame Search. ICCA journal, 15(1), pp. 3-7. * -# Reinsfeld A. (1983) An Improvement Of the Scout Tree-Search Algorithm. ICCA      journal, 6(4), pp. 4-14. *//*@{*//*! * \brief Get the final score. * * Get the final score, when no move can be made. * \param board  board. * \return       the final score, as a disc difference. */int board_get_final_score(const Board *board){	const int p = board->player;	const int o = OPPONENT(p);	int score = board->n_discs[p] - board->n_discs[o];	if (score < 0) score -= board->n_empties;	else if (score > 0) score += board->n_empties;	return score;}/*! * \brief Get the final score. * * Get the final score, when the board is full. * \param board   board. * \return        the final score, as a disc difference. */int board_get_final_score_0(const Board *board){	const int p = board->player;	const int o = OPPONENT(p);	int score = board->n_discs[p] - board->n_discs[o];	return score;}/*! * \brief Get the final score. * * Get the final score, when 1 empty square remains. * \param board   board. * \return        the final score, as a disc difference. */int board_get_final_score_1(Board *board){	const int p = board->player;	const int o = OPPONENT(p);	int score = board->n_discs[p] - board->n_discs[o];	int n_flips;	const int x = board->empties->next->position;	if ((n_flips = board_count_flips(board, x, p)) > 0) {		score += 2 * n_flips + 1;		BOARD_UPDATE_TERMINAL_NODES();	} else if ((n_flips = board_count_flips(board, x, o)) > 0) {		score -= 2 * n_flips + 1;		BOARD_UPDATE_TERMINAL_NODES();	} else if (score < 0) score--;	else if (score > 0) score++;	return score;}/*! * \brief Get the final score. * * Get the final score, when 2 empty squares remain. * \param board  the board to evaluate. * \param alpha  upper score value. * \param beta   lower score value. * \param passed  a flag indicating if previous move was a pass. * \return       the final score, as a disc difference. */int board_get_final_score_2(Board *board, int alpha, int beta, int passed){	const char p = board->player;	const char o = OPPONENT(p);	SquareList *empties = board->empties->next;	int x1 = empties->position;	int x2 = empties->next->position;	int score, bestscore, n1_flips, n2_flips, diff_discs;	Move move;	diff_discs = board->n_discs[(int)p] - board->n_discs[(int)o];	bestscore = -INF_SCORE;	/* try to play on the first available square */	if ((n1_flips = board_do_flip(board, x1, &move)) > 0) {		if ((n2_flips = board_count_flips(board, x2, o)) > 0) {			bestscore = diff_discs + 2 * (n1_flips - n2_flips);			BOARD_UPDATE_TERMINAL_NODES();		} else if ((n2_flips = board_count_flips(board, x2, p)) > 0) {			bestscore = diff_discs + 2 * (n1_flips + n2_flips) + 2;			BOARD_UPDATE_TERMINAL_NODES();		} else {			bestscore = diff_discs + 2 * n1_flips + 1;			if (bestscore > 0) bestscore++;			else if (bestscore < 0) bestscore--;		}		BOARD_RESTORE_SQUARE(board, &move);		BOARD_RESTORE_PLAYER(board);	}	/* if needed, try to play on the second & last available square */	if (bestscore < beta && (n1_flips = board_do_flip(board, x2, &move)) > 0) {		if ((n2_flips = board_count_flips(board, x1, o)) > 0) {			score = diff_discs + 2 * (n1_flips - n2_flips);			BOARD_UPDATE_TERMINAL_NODES();		} else if ((n2_flips = board_count_flips(board, x1, p)) > 0) {			score = diff_discs + 2 * (n1_flips + n2_flips) + 2;			BOARD_UPDATE_TERMINAL_NODES();		} else {			score = diff_discs + 2 * n1_flips + 1;			if (score > 0) score++;			else if (score < 0) score--;		}		BOARD_RESTORE_SQUARE(board, &move);		BOARD_RESTORE_PLAYER(board);		if (score > bestscore) bestscore = score;	}	/* if no move were available */	if (bestscore == -INF_SCORE) {		if (passed) { /* game is over */			BOARD_CORRECT_TERMINAL_NODES();			bestscore = diff_discs;			if (bestscore > 0) bestscore += 2;			else if (bestscore < 0) bestscore -= 2;		} else { /* pass... */			BOARD_UPDATE_PLAYER(board);			BOARD_UPDATE_INTERNAL_NODES();			bestscore = -board_get_final_score_2(board, -beta, -alpha, 1);			BOARD_RESTORE_PLAYER(board);		}	}	return bestscore;}/*! * \brief  Evaluate a position using a shallow Alphabeta. * * This function is used when there are few empty squares on the board. Here, * optimizations are in favour of speed instead of efficiency. A simple * alphabeta is used because of the low branching factor that makes PVS less * efficient. * \param board   board. * \param alpha   lower bound. * \param beta    upper bound. * \param passed  a flag indicating if previous move was a pass. * \return        the final score, as a disc difference. */int alphabeta_shallow(Board *board, int alpha, int beta, int passed){	const char p = board->player;	const char o = OPPONENT(p);	int score, bestscore = -INF_SCORE;	SquareList *empties;	Move move;#if PLAY_ODD_SQUARE_FIRST	int parity;	for (parity = 1; parity >= 0; parity--) {		for (empties = board->empties->next; empties->position != NOMOVE; empties = empties->next) {			if (board->parity[QUADRANT_ID[empties->position]] == parity			 && board_do_flip(board, empties->position, &move)) {#else		{		for (empties = board->empties->next; empties->position != NOMOVE; empties = empties->next) {			if (board_do_flip(board, empties->position, &move)) {#endif				BOARD_UPDATE_PLAYER(board);				BOARD_UPDATE_DISCS(board, move.n);				BOARD_UPDATE_PARITY(board, *move.position);				BOARD_UPDATE_EMPTIES(board, empties, *move.position);				if (board->n_empties == 2) {					score = -board_get_final_score_2(board, -beta, -alpha, 0);				} else {					score = -alphabeta_shallow(board, -beta, -alpha, 0);				}				BOARD_RESTORE_SQUARE(board, &move);				BOARD_RESTORE_PLAYER(board);				BOARD_RESTORE_DISCS(board, move.n);				BOARD_RESTORE_PARITY(board, *move.position);				BOARD_RESTORE_EMPTIES(board, empties, *move.position);				if (score > bestscore) {					bestscore = score;					if (bestscore > alpha) {						alpha = bestscore;						if (alpha >= beta) return bestscore;					}				}			}		}	}	/* no move */	if (bestscore == -INF_SCORE) {		if (passed) { /* game over */			BOARD_CORRECT_TERMINAL_NODES();			bestscore = board_get_final_score(board);		} else { /* pass */			BOARD_UPDATE_PLAYER(board);			BOARD_UPDATE_INTERNAL_NODES();			bestscore = -alphabeta_shallow(board, -beta, -alpha, 1);			BOARD_RESTORE_PLAYER(board);		}	}	return bestscore;}/*! * \brief Evaluate a position with a deep Principal Variation Search algorithm. * * This function is used when there are still many empty squares on the board. Move * ordering, hash table cutoff, enhanced transposition cutoff, etc. are used in * order to diminish the size of the tree to analyse, but at the expense of a * slower speed. * * \param board      board. * \param hash_table hash_table. * \param alpha      lower bound. * \param beta       upper bound. * \param passed     a flag indicating if previous move was a pass. * \return the final score, as a disc difference. */int PVS_deep(Board *board, HashTable *hash_table, int alpha, int beta, int passed){	int score, bestscore, lower, upper, bestmove;	unsigned long hash_lock, hash_index;	MoveList movelist[MAX_MOVE + 2], *iter;	Move *move;	Hash *hash;	HashEntry *hash_entry;	bestmove = NOMOVE;	lower = alpha;	upper = beta;	/* transposition cutoff ? */#if USE_HASH_TABLE	hash = hash_get(hash_table, board);	if (hash != NULL) {		if (upper > hash->upper) {			upper = hash->upper;			if (upper <= lower) return upper;		}		if (lower < hash->lower) {			lower = hash->lower;			if (lower >= upper) return lower;		}		bestmove = hash->move;	}#endif	board_get_movelist(board, movelist);	if (movelist->next == NULL) {		if (passed) {			BOARD_CORRECT_TERMINAL_NODES();			alpha = -(beta = +INF_SCORE);			bestscore = board_get_final_score(board);			bestmove = NOMOVE;		} else {			board_update_pass(board);			bestscore = -PVS_deep(board, hash_table, -upper, -lower, 1);			bestmove = PASS;			board_restore_pass(board);		}	} else {		/* enhanced transposition cutoff */#if USE_ENHANCED_TRANSPOSITION_CUTOFF		if (board->n_empties > EMPTIES_DEEP_TO_SHALLOW_SEARCH &&			HASH_TABLE_OK(hash_table)) {			if (bestmove != NOMOVE) movelist_sort_bestmove(movelist, bestmove);			for (iter = movelist->next; iter != NULL; iter = iter->next) {				move = &(iter->move);				hash_index = (board->hash_code[0] ^ move->hash_code[0]);				hash_lock = (board->hash_code[1] ^ move->hash_code[1]);				hash_entry = hash_table->hash_entry + hash_index;				BOARD_UPDATE_ALL_NODES();				if (hash_entry->deepest.lock ==  hash_lock && -hash_entry->deepest.upper >= upper)					return -hash_entry->deepest.upper;				if (hash_entry->newest.lock ==  hash_lock && -hash_entry->newest.upper >= upper)					return -hash_entry->newest.upper;			}		}#endif		/* move sorting */#if PLAY_FAST_SUBTREE_FIRST		if (board->n_empties > EMPTIES_DEEP_TO_SHALLOW_SEARCH)			movelist_sort_fastfirst(movelist, board);#endif#if PLAY_BEST_MOVE_IN_MEMORY_FIRST		if (bestmove != NOMOVE && bestmove != *movelist->next->move.position)			movelist_sort_bestmove(movelist, bestmove);#endif		/* first move */		iter = movelist->next;		move = &(iter->move);		board_update_move(board, move);		if (board->n_empties < EMPTIES_DEEP_TO_SHALLOW_SEARCH) {			bestscore = -alphabeta_shallow(board, -upper, -lower, 0);		} else {			bestscore = -PVS_deep(board, hash_table, -upper, -lower, 0);		}		bestmove = *move->position;		if (bestscore > lower) lower = bestscore;		board_restore_move(board, move);		/* other moves : try to refute the first/best one */		for (iter = iter->next; lower < upper && iter != NULL; iter = iter->next) {			move = &(iter->move);			board_update_move(board, move);			if (board->n_empties < EMPTIES_DEEP_TO_SHALLOW_SEARCH) {				score = -alphabeta_shallow(board, -lower - 1, -lower, 0);				if (lower < score && score < upper)					score = -alphabeta_shallow(board, -upper, -score, 0);			} else {				score = -PVS_deep(board, hash_table, -lower - 1, -lower, 0);				if (lower < score && score < upper)					score = -PVS_deep(board, hash_table, -upper, -score, 0);			}			board_restore_move(board, move);			if (score > bestscore) {				bestscore = score;				bestmove = *move->position;				if (bestscore > lower) lower = bestscore;			}		}	}#if USE_HASH_TABLE	hash_update(hash_table, board, alpha, beta, bestscore, bestmove);#endif	return bestscore;}/*! * \brief Principal Variation Search algorithm at the root of the tree. * * This function solves the position provided within the limits set by the alpha * and beta bounds. The movelist parameter is updated so that the bestmove is the * first of the list when the search ended. * * \param board      board. * \param hash_table hash table to memorize the analysis. * \param alpha      lower bound. * \param beta       upper bound. * \param movelist   List of legal moves (should actually contain moves !). */void PVS_root(Board *board, HashTable *hash_table, int alpha, int beta,	MoveList *movelist){	int lower, upper;	MoveList *iter;	Move *move, *bestmove;	lower = alpha;	upper = beta;	board->n_nodes++;	/* first move */	iter = movelist->next;	bestmove = &(iter->move);	board_update_move(board, bestmove);	if (board->n_empties == 0) {		bestmove->score = -board_get_final_score_0(board);	} else if (board->n_empties == 1) {		bestmove->score = -board_get_final_score_1(board);	} else if (board->n_empties == 2) {		bestmove->score = -board_get_final_score_2(board, -upper, -lower, 0);	} else if (board->n_empties < EMPTIES_DEEP_TO_SHALLOW_SEARCH) {		bestmove->score = -alphabeta_shallow(board, -upper, -lower, 0);	} else {		bestmove->score = -PVS_deep(board, hash_table, -upper, -lower, 0);	}	if (bestmove->score > lower) lower = bestmove->score;	board_restore_move(board, bestmove);	/* other moves : try to refute the first/best one */	for (iter = iter->next; lower < upper && iter != NULL; iter = iter->next) {		move = &(iter->move);		board_update_move(board, move);		if (board->n_empties == 0) {			move->score = -board_get_final_score_0(board);		} else if (board->n_empties == 1) {			move->score = -board_get_final_score_1(board);		} else if (board->n_empties == 2) {			move->score = -board_get_final_score_2(board, -upper, -lower, 0);		} else if (board->n_empties < EMPTIES_DEEP_TO_SHALLOW_SEARCH) {			move->score = -alphabeta_shallow(board, -lower - 1, -lower, 0);			if (lower < move->score && move->score < u

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -