예제 #1
0
GTPResponse GTP::gtp_patterns(vecstr args) {
    bool symmetric = true;
    bool invert = true;
    std::string ret;
    const Board & board = *hist;
    for(Board::MoveIterator move = board.moveit(); !move.done(); ++move) {
        ret += move->to_s() + " ";
        unsigned int p = board.pattern(*move);
        if(symmetric)
            p = board.pattern_symmetry(p);
        if(invert && board.toplay() == Side::P2)
            p = board.pattern_invert(p);
        ret += to_str(p);
        ret += "\n";
    }
    return GTPResponse(true, ret);
}
예제 #2
0
GTPResponse HavannahGTP::gtp_patterns(vecstr args){
	bool symmetric = true;
	bool invert = true;
	string ret;
	Board board = game.getboard();
	for(Board::MoveIterator move = board.moveit(); !move.done(); ++move){
		ret += move->to_s() + " ";
		unsigned int p = board.pattern(*move);
		if(symmetric)
			p = board.pattern_symmetry(p);
		if(invert && board.toplay() == 2)
			p = board.pattern_invert(p);
		ret += to_str(p);
		ret += "\n";
	}
	return GTPResponse(true, ret);
}
예제 #3
0
파일: player.cpp 프로젝트: brthiess/morat
void Player::create_children_simple(const Board & board, Node * node){
	assert(node->children.empty());

	node->children.alloc(board.movesremain(), ctmem);

	Node * child = node->children.begin(),
		 * end   = node->children.end();
	Board::MoveIterator moveit = board.moveit(prunesymmetry);
	int nummoves = 0;
	for(; !moveit.done() && child != end; ++moveit, ++child){
		*child = Node(*moveit);
		nummoves++;
	}

	if(prunesymmetry)
		node->children.shrink(nummoves); //shrink the node to ignore the extra moves
	else //both end conditions should happen in parallel
		assert(moveit.done() && child == end);

	PLUS(nodes, node->children.num());
}
예제 #4
0
bool SolverPNS::pns(const Board & board, PNSNode * node, int depth, uint32_t tp, uint32_t td){
	iters++;
	if(maxdepth < depth)
		maxdepth = depth;

	if(node->children.empty()){
		if(ctmem.memalloced() >= memlimit)
			return false;

		int numnodes = board.movesremain();
		nodes += node->alloc(numnodes, ctmem);

		if(lbdist)
			dists.run(&board);

		int i = 0;
		for(Board::MoveIterator move = board.moveit(true); !move.done(); ++move){
			int outcome, pd;

			if(ab){
				Board next = board;
				next.move(*move, false, false);

				pd = 0;
				outcome = (ab == 1 ? solve1ply(next, pd) : solve2ply(next, pd));
				nodes_seen += pd;
			}else{
				outcome = board.test_win(*move);
				pd = 1;
			}

			if(lbdist && outcome < 0)
				pd = dists.get(*move);

			node->children[i] = PNSNode(*move).outcome(outcome, board.toplay(), ties, pd);

			i++;
		}
		node->children.shrink(i); //if symmetry, there may be extra moves to ignore

		nodes_seen += i;

		updatePDnum(node);

		return true;
	}

	bool mem;
	do{
		PNSNode * child = node->children.begin(),
		        * child2 = node->children.begin(),
		        * childend = node->children.end();

		uint32_t tpc, tdc;

		if(df){
			for(PNSNode * i = node->children.begin(); i != childend; i++){
				if(i->delta <= child->delta){
					child2 = child;
					child = i;
				}else if(i->delta < child2->delta){
					child2 = i;
				}
			}

			tpc = min(INF32/2, (td + child->phi - node->delta));
			tdc = min(tp, (uint32_t)(child2->delta*(1.0 + epsilon) + 1));
		}else{
			tpc = tdc = 0;
			while(child->delta != node->phi)
				child++;
		}

		Board next = board;
		next.move(child->move, false, false);

		uint64_t itersbefore = iters;
		mem = pns(next, child, depth + 1, tpc, tdc);
		child->work += iters - itersbefore;

		if(child->phi == 0 || child->delta == 0) //clear child's children
			nodes -= child->dealloc(ctmem);

		if(updatePDnum(node) && !df)
			break;

	}while(!timeout && mem && (!df || (node->phi < tp && node->delta < td)));

	return mem;
}
예제 #5
0
bool SolverPNS2::SolverThread::pns(const Board & board, PNSNode * node, int depth, uint32_t tp, uint32_t td){
	iters++;
	if(solver->maxdepth < depth)
		solver->maxdepth = depth;

	if(node->children.empty()){
		if(node->terminal())
			return true;

		if(solver->ctmem.memalloced() >= solver->memlimit)
			return false;

		if(!node->children.lock())
			return false;

		int numnodes = board.movesremain();
		CompactTree<PNSNode>::Children temp;
		temp.alloc(numnodes, solver->ctmem);
		PLUS(solver->nodes, numnodes);

		if(solver->lbdist)
			dists.run(&board);

		int i = 0;
		for(Board::MoveIterator move = board.moveit(true); !move.done(); ++move){
			int outcome, pd;

			if(solver->ab){
				Board next = board;
				next.move(*move);

				pd = 0;
				outcome = (solver->ab == 1 ? solve1ply(next, pd) : solve2ply(next, pd));
				PLUS(solver->nodes_seen, pd);
			}else{
				outcome = board.test_win(*move);
				pd = 1;
			}

			if(solver->lbdist && outcome < 0)
				pd = dists.get(*move);

			temp[i] = PNSNode(*move).outcome(outcome, board.toplay(), solver->ties, pd);

			i++;
		}
		temp.shrink(i); //if symmetry, there may be extra moves to ignore
		node->children.swap(temp);
		assert(temp.unlock());

		PLUS(solver->nodes_seen, i);

		updatePDnum(node);

		return true;
	}

	bool mem;
	do{
		PNSNode * child = node->children.begin(),
		        * child2 = node->children.begin(),
		        * childend = node->children.end();

		uint32_t tpc, tdc;

		if(solver->df){
			for(PNSNode * i = node->children.begin(); i != childend; i++){
				if(i->refdelta() <= child->refdelta()){
					child2 = child;
					child = i;
				}else if(i->refdelta() < child2->refdelta()){
					child2 = i;
				}
			}

			tpc = min(INF32/2, (td + child->phi - node->delta));
			tdc = min(tp, (uint32_t)(child2->delta*(1.0 + solver->epsilon) + 1));
		}else{
			tpc = tdc = 0;
			for(PNSNode * i = node->children.begin(); i != childend; i++)
				if(child->refdelta() > i->refdelta())
					child = i;
		}

		Board next = board;
		next.move(child->move);

		child->ref();
		uint64_t itersbefore = iters;
		mem = pns(next, child, depth + 1, tpc, tdc);
		child->deref();
		PLUS(child->work, iters - itersbefore);

		if(updatePDnum(node) && !solver->df)
			break;

	}while(!solver->timeout && mem && (!solver->df || (node->phi < tp && node->delta < td)));

	return mem;
}
예제 #6
0
GTPResponse GTP::gtp_all_legal(vecstr args) {
    std::string ret;
    for(Board::MoveIterator move = hist->moveit(); !move.done(); ++move)
        ret += move->to_s() + " ";
    return GTPResponse(true, ret);
}
예제 #7
0
PairMove Player::PlayerUCT::rollout_choose_move(Board & board, const Move & prev, int & doinstwin, bool checkrings){
	//look for instant wins
	if(player->instantwin == 1 && --doinstwin >= 0){
		for(Board::MoveIterator m = board.moveit(); !m.done(); ++m)
			if(board.test_win(*m, board.toplay(), checkrings) > 0)
				return *m;
	}

	//look for instant wins and forced replies
	if(player->instantwin == 2 && --doinstwin >= 0){
		Move loss = M_UNKNOWN;
		for(Board::MoveIterator m = board.moveit(); !m.done(); ++m){
			if(board.test_win(*m, board.toplay(), checkrings) > 0) //win
				return *m;
			if(board.test_win(*m, 3 - board.toplay(), checkrings) > 0) //lose
				loss = *m;
		}
		if(loss != M_UNKNOWN)
			return loss;
	}

	if(player->instantwin >= 3 && --doinstwin >= 0){
		Move start, cur, loss = M_UNKNOWN;
		int turn = 3 - board.toplay();

		if(player->instantwin == 4){ //must have an edge or corner connection, or it has nothing to offer a group towards a win, ignores rings
			const Board::Cell * c = board.cell(prev);
			if(c->numcorners() == 0 && c->numedges() == 0)
				goto skipinstwin3;

		}

//		logerr(board.to_s(true));

		//find the first empty cell
		int dir = -1;
		for(int i = 0; i <= 5; i++){
			start = prev + neighbours[i];

			if(!board.onboard(start) || board.get(start) != turn){
				dir = (i + 5) % 6;
				break;
			}
		}

		if(dir == -1) //possible if it's in the middle of a ring, which is possible if rings are being ignored
			goto skipinstwin3;

		cur = start;

//		logerr(prev.to_s() + ":");

		//follow contour of the current group looking for wins
		do{
//			logerr(" " + to_str((int)cur.y) + "," + to_str((int)cur.x));
			//check the current cell
			if(board.onboard(cur) && board.get(cur) == 0 && board.test_win(cur, turn, checkrings) > 0){
//				logerr(" loss");
				if(loss == M_UNKNOWN)
					loss = cur;
				else if(loss != cur)
					return PairMove(loss, cur); //game over, two wins found for opponent
			}

			//advance to the next cell
			for(int i = 5; i <= 9; i++){
				int nd = (dir + i) % 6;
				Move next = cur + neighbours[nd];

				if(!board.onboard(next) || board.get(next) != turn){
					cur = next;
					dir = nd;
					break;
				}
			}
		}while(cur != start); //potentially skips part of it when the start is in a pocket, rare bug

//		logerr("\n");

		if(loss != M_UNKNOWN)
			return loss;
	}
skipinstwin3:

	//force a bridge reply
	if(player->rolloutpattern){
		Move move = rollout_pattern(board, prev);
		if(move != M_UNKNOWN)
			return move;
	}

	//reuse the last good reply
	if(player->lastgoodreply && prev != M_SWAP){
		Move move = goodreply[board.toplay()-1][board.xy(prev)];
		if(move != M_UNKNOWN && board.valid_move_fast(move))
			return move;
	}

	return M_UNKNOWN;
}
예제 #8
0
//play a random game starting from a board state, and return the results of who won
int Player::PlayerUCT::rollout(Board & board, Move move, int depth){
	int won;
	int num = board.movesremain();

	bool wrand = (player->weightedrandom);

	if(wrand){
		wtree[0].resize(board.vecsize());
		wtree[1].resize(board.vecsize());

		int set = 0;
		for(Board::MoveIterator m = board.moveit(false, false); !m.done(); ++m){
			int i = board.xy(*m);
			moves[i] = *m;
			unsigned int p = board.pattern(i);
			wtree[0].set_weight_fast(i, player->gammas[p]);
			wtree[1].set_weight_fast(i, player->gammas[board.pattern_invert(p)]);
			set++;
		}

		wtree[0].rebuild_tree();
		wtree[1].rebuild_tree();
	}else{
		int i = 0;
		for(Board::MoveIterator m = board.moveit(false, false); !m.done(); ++m)
			moves[i++] = *m;

		i = num;
		while(i > 1){
			int j = rand32() % i--;
			Move tmp = moves[j];
			moves[j] = moves[i];
			moves[i] = tmp;
		}

//		random_shuffle(moves, moves + num);
	}

	int doinstwin = player->instwindepth;
	if(doinstwin < 0)
		doinstwin *= - board.get_size();

	bool checkrings = (unitrand() < player->checkrings);

	//only check rings to the specified depth
	int  checkdepth = (int)player->checkringdepth;
	//if it's negative, check for that fraction of the remaining moves
	if(player->checkringdepth < 0)
		checkdepth = (int)ceil(num * player->checkringdepth * -1);

	//only allow rings bigger than the minimum ring size, incrementing by the ringincr after each move
	int minringsize = (int)player->minringsize;
	int ringcounterfull = (int)player->ringincr;
	//if it's negative, scale by the fraction of remaining moves
	if(player->ringincr < 0)
		ringcounterfull = (int)ceil(num * player->ringincr * -1);

	int ringcounter = ringcounterfull;

	int ringperm = player->ringperm;

	Move * nextmove = moves;
	Move forced = M_UNKNOWN;
	while((won = board.won()) < 0){
		int turn = board.toplay();

		if(forced == M_UNKNOWN){
			//do a complex choice
			PairMove pair = rollout_choose_move(board, move, doinstwin, checkrings);
			move = pair.a;
			forced = pair.b;

			//or the simple random choice if complex found nothing
			if(move == M_UNKNOWN){
				do{
					if(wrand){
						int j = wtree[turn-1].choose();
//						assert(j >= 0);
						wtree[0].set_weight(j, 0);
						wtree[1].set_weight(j, 0);
						move = moves[j];
					}else{
						move = *nextmove;
						nextmove++;
					}
				}while(!board.valid_move_fast(move));
			}
		}else{
			move = forced;
			forced = M_UNKNOWN;
		}

		movelist.addrollout(move, turn);

		board.move(move, true, false, (checkrings ? minringsize : 0), ringperm);
		if(--ringcounter == 0){
			minringsize++;
			ringcounter = ringcounterfull;
		}
		depth++;
		checkrings &= (depth < checkdepth);

		if(wrand){
			//update neighbour weights
			for(const MoveValid * i = board.nb_begin(move), *e = board.nb_end(i); i < e; i++){
				if(i->onboard() && board.get(i->xy) == 0){
					unsigned int p = board.pattern(i->xy);
					wtree[0].set_weight(i->xy, player->gammas[p]);
					wtree[1].set_weight(i->xy, player->gammas[board.pattern_invert(p)]);
				}
			}
		}
	}

	gamelen.add(depth);

	if(won > 0)
		wintypes[won-1][(int)board.getwintype()].add(depth);

	//update the last good reply table
	if(player->lastgoodreply && won > 0){
		MoveList::RaveMove * rave = movelist.begin(), *raveend = movelist.end();

		int m = -1;
		while(rave != raveend){
			if(m >= 0){
				if(rave->player == won && *rave != M_SWAP)
					goodreply[rave->player - 1][m] = *rave;
				else if(player->lastgoodreply == 2)
					goodreply[rave->player - 1][m] = M_UNKNOWN;
			}
			m = board.xy(*rave);
			++rave;
		}
	}

	movelist.finishrollout(won);
	return won;
}
예제 #9
0
bool Player::PlayerUCT::create_children(Board & board, Node * node, int toplay){
	if(!node->children.lock())
		return false;

	if(player->dists || player->detectdraw){
		dists.run(&board, (player->dists > 0), (player->detectdraw ? 0 : toplay));

		if(player->detectdraw){
//			assert(node->outcome == -3);
			node->outcome = dists.isdraw(); //could be winnable by only one side

			if(node->outcome == 0){ //proven draw, neither side can influence the outcome
				node->bestmove = *(board.moveit()); //just choose the first move since all are equal at this point
				node->children.unlock();
				return true;
			}
		}
	}

	CompactTree<Node>::Children temp;
	temp.alloc(board.movesremain(), player->ctmem);

	int losses = 0;

	Node * child = temp.begin(),
	     * end   = temp.end(),
	     * loss  = NULL;
	Board::MoveIterator move = board.moveit(player->prunesymmetry);
	int nummoves = 0;
	for(; !move.done() && child != end; ++move, ++child){
		*child = Node(*move);

		if(player->minimax){
			child->outcome = board.test_win(*move);

			if(player->minimax >= 2 && board.test_win(*move, 3 - board.toplay()) > 0){
				losses++;
				loss = child;
			}

			if(child->outcome == toplay){ //proven win from here, don't need children
				node->outcome = child->outcome;
				node->proofdepth = 1;
				node->bestmove = *move;
				node->children.unlock();
				temp.dealloc(player->ctmem);
				return true;
			}
		}

		if(player->knowledge)
			add_knowledge(board, node, child);
		nummoves++;
	}

	if(player->prunesymmetry)
		temp.shrink(nummoves); //shrink the node to ignore the extra moves
	else //both end conditions should happen in parallel
		assert(move.done() && child == end);

	//Make a macro move, add experience to the move so the current simulation continues past this move
	if(losses == 1){
		Node macro = *loss;
		temp.dealloc(player->ctmem);
		temp.alloc(1, player->ctmem);
		macro.exp.addwins(player->visitexpand);
		*(temp.begin()) = macro;
	}else if(losses >= 2){ //proven loss, but at least try to block one of them
		node->outcome = 3 - toplay;
		node->proofdepth = 2;
		node->bestmove = loss->move;
		node->children.unlock();
		temp.dealloc(player->ctmem);
		return true;
	}

	if(player->dynwiden > 0) //sort in decreasing order by knowledge
		sort(temp.begin(), temp.end(), sort_node_know);

	PLUS(player->nodes, temp.num());
	node->children.swap(temp);
	assert(temp.unlock());

	return true;
}
예제 #10
0
GTPResponse HavannahGTP::gtp_all_legal(vecstr args){
	string ret;
	for(Board::MoveIterator move = game.getboard().moveit(); !move.done(); ++move)
		ret += move_str(*move) + " ";
	return GTPResponse(true, ret);
}