goboard.h

来自「uct算法」· C头文件 代码 · 共 511 行

H
511
字号
#ifndef _GO_BOARD_H_#define _GO_BOARD_H_#include <math.h>#include "basic_go_types.h"#include "basic_board.h"#include "zobrist.h"#include "nbrcounter.h"enum play_ret_t { play_ok, play_suicide, play_ss_suicide, play_ko };template<uint T> class GoBoard:public BasicBoard<T, GoBoard<T> >,public NbrCounterBoard<T, GoBoard<T> >,public ZobristBoard<T, GoBoard<T> >{public:	static const bool use_mercy_rule = false;	typedef NbrCounterBoard<T, GoBoard> NCB;	using BasicBoard<T, GoBoard>::empty_v_cnt;	using BasicBoard<T, GoBoard>::color_at;	using BasicBoard<T, GoBoard>::empty_v;	using BasicBoard<T, GoBoard>::player_v_cnt;	using BasicBoard<T, GoBoard>::move_history;	using BasicBoard<T, GoBoard>::move_no;	using BasicBoard<T, GoBoard>::max_game_length;	using NbrCounterBoard<T, GoBoard>::nbr_cnt;	//using NbrCounterBoard<T, GoBoard>::diag_nbr_cnt;	using ZobristBoard<T, GoBoard>::hash;public:	FastMap<Vertex<T>, Vertex<T> >  chain_next_v;	uint                         	chain_lib_cnt[Vertex<T>::cnt]; // indexed by chain_id 棋串的气数	FastMap<Vertex<T>, uint>        chain_id; // 每个点所属的棋串id  	int                          komi;	Vertex<T>                    ko_v;             // vertex forbidden by ko(劫)	uint                         last_empty_v_cnt;	play_ret_t                   last_move_status;public:                         // board interface	GoBoard() {		last_move_status = play_ok;		ko_v         = Vertex<T>::any();		set_komi(default_komi);            // standard for chinese rules		vertex_for_each_all(v) {			chain_next_v[v] = v;			chain_id[v] = v;    // TODO is it needed, is it usedt?			//chain_lib_cnt[v] = NbrCounter::max; // TODO is it logical? (off_boards)			chain_lib_cnt[v] = nbr_cnt[v].empty_cnt();		}	}	void set_komi(float fkomi) { 		komi = int(ceil(-fkomi));	}	float get_komi() const { 		return float(-komi) + 0.5;	}	// deprecated	// checks for move legality	// it has to point to empty vertexand empty	// can't recognize play_suicide	all_inline 	bool is_legal(Player player, Vertex<T> v) { // 不严格的禁入点探测		check();		//v.check_is_on_board(); // TODO check v = pass || onboard		// 1 pass		// 2 不在对方包围中(含真眼和假眼)		// 3 不是劫并且不是填对方的单眼		return 			v == Vertex<T>::pass() || 			!nbr_cnt[v].player_cnt_is_max(player::other(player)) || 			(eye_is_not_ko(v) && //(v != ko_v && 			 !eye_is_suicide(v));	}public: // legality functions	// MC模拟时仅考虑不填自己的眼,对于填自己空或者缩小眼位之类的则不管	bool is_eyelike(Player player, Vertex<T> v) { // 是否像一个眼		// 1 邻点是否全是己方棋子		// 2 肩上的对方棋子数少于2(边角上少于1)		// 缺点:无法判断环形棋块,		// TODO:是否会错过故意填眼的妙招??		assertc(board_ac, color_at[v] == color::empty);		if(! nbr_cnt[v].player_cnt_is_max(player)) return false;#if 1		int diag_color_cnt[4]; // TODO		color_for_each (col) 			diag_color_cnt [col] = 0; // memset is slower		vertex_for_each_diag_nbr (v, diag_v, i, {				diag_color_cnt [color_at [diag_v]]++;				});		return diag_color_cnt [Color (player::other(player))] + (diag_color_cnt [color::off_board] > 0) < 2;#else		return diag_nbr_cnt[v].is_nbr_lt2(player);#endif	}	void check_no_more_legal(Player player) { // at the end of the playout		unused(player);		if(!board_ac) return;		vertex_for_each_all(v) // 确认对于该player来说,剩余的空点要么是眼,要么禁入			if(color_at[v] == color::empty)				assert(is_eyelike(player, v) || is_legal(player, v) == false);	}	all_inline 	bool play(Player player, Vertex<T> v) {		check();		if(v == Vertex<T>::pass()) {			basic_play(player, Vertex<T>::pass());			return true;		}		v.check_is_on_board();		assertc(board_ac, color_at[v] == color::empty);		if(nbr_cnt[v].player_cnt_is_max(player::other(player))) {			return play_eye(player, v);#if 1 		} else if(nbr_cnt[v].empty_cnt() == 0) { // 处理可能的自杀			// complete suicide testing			vertex_for_each_nbr(v, nbr_v,i, chain_lib_cnt[chain_id[nbr_v]]--); 			uint valid = false;			vertex_for_each_nbr(v, nbr_v,i, {				valid |= !((color_at[nbr_v] != color::from_player(player)) ^ (chain_lib_cnt[chain_id[nbr_v]] == 0));			});			if(!valid) {				vertex_for_each_nbr(v, nbr_v,i, chain_lib_cnt[chain_id[nbr_v]]++); 				return false;			}		} else {			vertex_for_each_nbr(v, nbr_v,i, chain_lib_cnt[chain_id[nbr_v]]--); 		}		basic_play(player, v);		place_stone(player, v);		vertex_for_each_nbr(v, nbr_v,i, {			//NCB::place_stone_side(player, nbr_v, i);			if(color::is_player(color_at[nbr_v])) {				if(color_at[nbr_v] != color::from_player(player)) { // same color of groups					if(chain_lib_cnt[chain_id[nbr_v]] == 0) 						remove_chain(nbr_v);				} else {					if(chain_id[nbr_v] != chain_id[v]) {						if(chain_lib_cnt[chain_id[v]] > chain_lib_cnt[chain_id[nbr_v]]) {							merge_chains(v, nbr_v);						} else {							merge_chains(nbr_v, v);						}         					}				}			}		});#else		} else {			play_not_eye(player, v); //TODO: 			// TODO invent complete suicide testing			assertc(board_ac, last_move_status == play_ok || last_move_status == play_suicide); 		}#endif		return true;	}public: // slow functions	// very slow function	bool undo() {		Move<T> replay[max_game_length];		uint   game_length  = move_no;		float  old_komi     = get_komi();		if(game_length == 0) 			return false;		rep(mn, game_length-1)			replay[mn] = move_history[mn];		//init();		new(this) GoBoard();		set_komi(old_komi); // TODO maybe last_player should be preserverd as well		rep(mn, game_length-1)			play_legal(replay[mn].get_player(), replay[mn].get_vertex());		return true;	}	// very slow function 	void sure_undo() {		if(undo() == false) {			cerr << "sure_undo failed\n";			exit(1);		}	}	// a very slow function	bool is_strict_legal(Player pl, Vertex<T> v) {            // slow function		if(try_play(pl, v) == false) return false;		sure_undo();		return true;	}	// very slow function	bool try_play(Player player, Vertex<T> v) {		if(v == Vertex<T>::pass()) {			play_legal(player, v);      			return true;		}		v.check_is_on_board();		if(color_at[v] != color::empty) 			return false; 		if(is_legal(player,v) == false)			return false;		play_legal(player, v);		if(last_move_status != play_ok) {			sure_undo();			return false;		}		if(is_hash_repeated()) {			sure_undo();			return false;		}		return true;	}	// this is very very slow function	bool is_hash_repeated() { // 根据历史棋步重下一遍来判断全局同形,果然很慢。为什么不预存hash值呢		GoBoard tmp_board;		rep(mn, move_no-1) {			tmp_board.play_legal(move_history[mn].get_player(), move_history[mn].get_vertex());			if(hash == tmp_board.hash) 				return true;		}		return false;	}private:	bool eye_is_not_ko(Vertex<T> v) {		//return(v == ko_v) & (player == player::other(last_player));		return v != ko_v;	}	bool eye_is_ko(Vertex<T> v) {		//return(v == ko_v) & (player == player::other(last_player));		return v == ko_v;	}	// deprecated	bool eye_is_suicide(Vertex<T> v) {		uint all_nbr_live = true;		vertex_for_each_nbr(v, nbr_v,i, all_nbr_live &= (--chain_lib_cnt[chain_id[nbr_v]] != 0));		vertex_for_each_nbr(v, nbr_v,i, chain_lib_cnt[chain_id[nbr_v]]++); //TODO: 能否更快		return all_nbr_live;	}	no_inline	bool play_eye(Player player, Vertex<T> v) {		if(eye_is_ko(v)) return false;		uint all_nbr_live = true;		vertex_for_each_nbr(v, nbr_v,i, all_nbr_live &= (--chain_lib_cnt[chain_id[nbr_v]] != 0));		if(all_nbr_live) {			vertex_for_each_nbr(v, nbr_v,i, chain_lib_cnt[chain_id[nbr_v]]++); 			return false;		}		basic_play(player, v);		last_empty_v_cnt = empty_v_cnt;		place_stone(player, v);		vertex_for_each_nbr(v, nbr_v,i, {			//NCB::place_stone_side(player, nbr_v, i);			if((chain_lib_cnt[chain_id[nbr_v]] == 0)) 				remove_chain(nbr_v);		});		assertc(board_ac, chain_lib_cnt[chain_id[v]] != 0);		if(last_empty_v_cnt == empty_v_cnt) { // if captured exactly one stone, end this was eye			ko_v = empty_v[empty_v_cnt - 1]; // then ko formed		} else {			ko_v = Vertex<T>::any();		}		return true;	}	// accept pass	// will ignore simple-ko ban	// will play single stone suicide	all_inline 	void play_legal(Player player, Vertex<T> v) {		check();		if(v == Vertex<T>::pass()) {			basic_play(player, Vertex<T>::pass());			return;		}		v.check_is_on_board();		assertc(board_ac, color_at[v] == color::empty);		if(nbr_cnt[v].player_cnt_is_max(player::other(player))) {			play_eye_legal(player, v);		} else {			play_not_eye(player, v);			// TODO invent complete suicide testing			assertc(board_ac, last_move_status == play_ok || last_move_status == play_suicide); 		}	}	// deprecated	all_inline	void play_not_eye(Player player, Vertex<T> v) {		check();		v.check_is_on_board();		assertc(board_ac, color_at[v] == color::empty);		basic_play(player, v);		last_empty_v_cnt = empty_v_cnt; // TODO: remove this		place_stone(player, v);		vertex_for_each_nbr(v, nbr_v,i, {			// This should be before 'if' to have good lib_cnt for empty vertices			chain_lib_cnt[chain_id[nbr_v]]--; 			if(color::is_player(color_at[nbr_v])) {				if(color_at[nbr_v] != color::from_player(player)) { // same color of groups					if(chain_lib_cnt[chain_id[nbr_v]] == 0) 						remove_chain(nbr_v);				} else {					if(chain_id[nbr_v] != chain_id[v]) {						if(chain_lib_cnt[chain_id[v]] > chain_lib_cnt[chain_id[nbr_v]]) {							merge_chains(v, nbr_v);						} else {							merge_chains(nbr_v, v);						}         					}				}			}		});    		if(chain_lib_cnt[chain_id[v]] == 0) {			assertc(board_ac, last_empty_v_cnt - empty_v_cnt == 1);			remove_chain(v);			assertc(board_ac, last_empty_v_cnt - empty_v_cnt > 0);			last_move_status = play_suicide;//TODO: 为何要先下这一手再undo呢?		} else {			last_move_status = play_ok;		}	}	// deprecated	// 在对方眼的位置下子(忽略因为劫而导致的禁入情况,因为调用前判断过了)	no_inline	void play_eye_legal(Player player, Vertex<T> v) {		vertex_for_each_nbr(v, nbr_v,i, chain_lib_cnt[chain_id[nbr_v]]--);		basic_play(player, v);		last_empty_v_cnt = empty_v_cnt;		place_stone(player, v);    		vertex_for_each_nbr(v, nbr_v,i, {			if((chain_lib_cnt[chain_id[nbr_v]] == 0)) 				remove_chain(nbr_v);		});		assertc(board_ac, chain_lib_cnt[chain_id[v]] != 0);		if(last_empty_v_cnt == empty_v_cnt) { // if captured exactly one stone, end this was eye			ko_v = empty_v[empty_v_cnt - 1]; // then ko formed		} else {			ko_v = Vertex<T>::any();		}	}	void merge_chains(Vertex<T> v_base, Vertex<T> v_new) {		Vertex<T> act_v;		chain_lib_cnt[chain_id[v_base]] += chain_lib_cnt[chain_id[v_new]];		act_v = v_new;		do {			chain_id[act_v] = chain_id[v_base];			act_v = chain_next_v[act_v];		} while(act_v != v_new);		swap(chain_next_v[v_base], chain_next_v[v_new]);// 两个环断开,分别接到对方环上,扭成一个大环	}	no_inline 	void remove_chain(Vertex<T> v){		Vertex<T> act_v;		Vertex<T> tmp_v;		Color old_color;		old_color = color_at[v];		act_v = v;		assertc(board_ac, color::is_player(old_color));		do {			remove_stone(color::to_player(old_color), act_v);			chain_id[act_v] = act_v;			chain_lib_cnt[act_v] = 0;// TODO: is it fixed?			act_v = chain_next_v[act_v];		} while(act_v != v);		assertc(board_ac, act_v == v);		do {			vertex_for_each_nbr(act_v, nbr_v,i, {				chain_lib_cnt[chain_id[nbr_v]]++;				//NCB::remove_stone_side(color::to_player(old_color), nbr_v, i);			});			tmp_v = act_v;			act_v = chain_next_v[act_v];			chain_next_v[tmp_v] = tmp_v;		} while(act_v != v);	}	void basic_play(Player player, Vertex<T> v) { // Warning: has to be called before place_stone, because of hash storing		BasicBoard<T, GoBoard>::play(player, v);		ko_v                    = Vertex<T>::any();	}	void place_stone(Player player, Vertex<T> v) {		BasicBoard<T, GoBoard>::place_stone(player, v);		NbrCounterBoard<T, GoBoard>::place_stone(player, v);		ZobristBoard<T, GoBoard>::place_stone(player, v);		assertc(chain_next_v_ac, chain_next_v[v] == v);		assertc(chain_next_v_ac, chain_id[v] == v);		assertc(chain_next_v_ac, chain_lib_cnt[v] == nbr_cnt[v].empty_cnt());		//chain_id[v] = v;		//chain_lib_cnt[v] = nbr_cnt[v].empty_cnt();	}	void remove_stone(Player pl, Vertex<T> v) {		// 按照place_stone相反的顺序		ZobristBoard<T, GoBoard>::remove_stone(pl, v);		NbrCounterBoard<T, GoBoard>::remove_stone(pl, v);		BasicBoard<T, GoBoard>::remove_stone(pl, v);				//chain_id[v] = v;	}public:	int approx_score() const {		return komi + player_v_cnt[player::black] -  player_v_cnt[player::white];	}	Player approx_winner() { return Player((approx_score() <= 0)+1); }	Player winner() const { 		return Player((score() <= 0) + 1); 	}	int score() const {		int eye_score = 0;		empty_v_for_each(this, v, {			eye_score += nbr_cnt[v].player_cnt_is_max(player::black);			eye_score -= nbr_cnt[v].player_cnt_is_max(player::white);		});		return approx_score() + eye_score;	}	int vertex_score(Vertex<T> v) {		switch(color_at[v]) {			case color::black: return 1;			case color::white: return -1;			case color::empty: 				return 					(nbr_cnt[v].player_cnt_is_max(player::black)) -					(nbr_cnt[v].player_cnt_is_max(player::white));			case color::off_board: return 0;			default: assert(false);		}	}public:                         // consistency checks	void check() const { // 检查整个棋盘一致性		if(!board_ac) return;		BasicBoard<T, GoBoard>::check();		NbrCounterBoard<T, GoBoard>::check();		ZobristBoard<T, GoBoard>::check();		check_chain_at();		check_chain_next_v();	}private:	void check_chain_at() const {		if(!chain_at_ac) return;		vertex_for_each_all(v) { // whether same color neighbours have same root and liberties			// TODO what about off_board and empty? 可以不用管			if(color::is_player(color_at[v])) {				assert(chain_lib_cnt[chain_id[v]] != 0); // player所属的串气不为0				vertex_for_each_nbr(v, nbr_v,i, {					if(color_at[v] == color_at[nbr_v]) // 同色邻点属于同一串					assert(chain_id[v] == chain_id[nbr_v]);				});			}		}	}	void check_chain_next_v() const {		if(!chain_next_v_ac) return;		vertex_for_each_all(v) {			chain_next_v[v].check();			if(!color::is_player(color_at[v])) {				assert(chain_next_v[v] == v);			}			if(color_at[v] == color::empty) {				assert(chain_lib_cnt[v] == nbr_cnt[v].empty_cnt());			}		}	}};#endif //_GO_BOARD_H_

⌨️ 快捷键说明

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