<commit_before>/*
    This file is part of Anabel

    Anabel is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    Anabel is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Anabel; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
#include <anabel/stdafx.h>
#include <anabel/anabel.h>

using namespace boost::filesystem;
using namespace boost::interprocess;
using namespace Anabel::Exceptions;
using namespace std;
using namespace Anabel;
using namespace Anabel::Internal;

void Anabel::TimeSeries::create(char * rootdirpath, int record_size) {
	path rootpath(rootdirpath);
	try {
		create_directory(rootpath);
	} catch (...) {}
	ofstream alock((rootpath / "alock").c_str()); alock.close();
	ofstream block((rootpath / "block").c_str()); block.close();
	ofstream rsf((rootpath / "record_size").c_str()); rsf << record_size; rsf.close();
	make_empty_dataset(rootpath / "0");
}

/**
* Chooses a timestamp that can be chosen, so it contains needle.
*
* Haystack must not be empty - other way, it would not make any sense, would it?
* Haystack must contain entry that will follow us to needle.
*/
Timestamp choose(vector<Timestamp> haystack, Timestamp needle) {
	for (unsigned i=0; i<haystack.size(); i++) {
		if (haystack[i] <= needle) return haystack[i];
	}
	throw InternalError("needle not found");
}

Anabel::ReadQuery * Anabel::TimeSeries::get_query(Anabel::Timestamp from, Anabel::Timestamp to) throw(Anabel::Exceptions::InvalidInvocation) {
	// Sanity checks
	if ((this->mode != TSO_READ) && (this->mode != TSO_WRITE)) throw InvalidInvocation("invalid open mode");
	if (from>to) throw InvalidInvocation("from>to");

	typedef vector<Timestamp> timevector;
	typedef vector<Timestamp>::iterator timevectoriter;

	// Init variables
	path cpath = this->root_path;
	timevector elements;
	Timestamp choice;
	vector<path> files;

	// Locate UBA
	cpath = this->root_path;
	try {
		while (is_directory(cpath)) {
			elements = scan_directory(cpath);
			sort(elements.begin(), elements.end(), greater<Timestamp>());
			choice = choose(elements, to);
			cpath /= timestamp_to_string(choice);
		}
	} catch (InternalError e) {
		// query empty.
		return new Anabel::ReadQuery(from, to, files, this->record_size);
	}

	files.push_back(cpath);
	if (choice <= from) {
		return new Anabel::ReadQuery(from, to, files, this->record_size);	// response is a single-file wonder
	}

	cpath = cpath.parent_path();

	// Now we will trace thru the filesystem, finding doodz. First up the tree, and then sharp dive towards 'to'
	Timestamp previous_choice = choice;
	while (true) {
		elements = scan_directory(cpath);
		sort(elements.begin(), elements.end(), std::greater<Timestamp>());
		timevectoriter bound = lower_bound(elements.begin(), elements.end(), from, std::greater<Timestamp>());

		if (bound == elements.end()) {	// we need to ascend
			// index files from bound upwards
			for (timevectoriter start_bound = upper_bound(elements.begin(), elements.end(), to, std::greater<Timestamp>()); start_bound != elements.end(); start_bound++) {
				if (previous_choice == *start_bound) continue;
				previous_choice = *start_bound;
				files.push_back(cpath / timestamp_to_string(*start_bound));
			}
			cpath = cpath.parent_path();
			continue;
		}

		// Descent to "bound"
		for (timevectoriter start_bound = upper_bound(elements.begin(), elements.end(), to, std::greater<Timestamp>()); start_bound != bound; start_bound++) {
			if (previous_choice == *start_bound) continue;
			previous_choice = *start_bound;
			files.push_back(cpath / timestamp_to_string(*start_bound));
		}

		cpath = cpath / timestamp_to_string(*bound);
		if (is_regular_file(cpath)) {
			files.push_back(cpath);
			break;
		}
	}

	return new Anabel::ReadQuery(from, to, files, this->record_size);
}

void Anabel::TimeSeries::truncate(void) throw(Anabel::Exceptions::InvalidInvocation) {
	if (this->mode != TSO_WRITE) throw InvalidInvocation("invalid open mode");

	vector<Timestamp> elements = scan_directory(this->root_path);
	for (vector<Timestamp>::iterator iter = elements.begin(); iter != elements.end(); iter++) remove_all(this->root_path / Anabel::Internal::timestamp_to_string(*iter));
	make_empty_dataset(this->root_path / "0");
}

void Anabel::TimeSeries::append(void * value) throw(Anabel::Exceptions::InvalidInvocation) {
	if ((this->mode != TSO_APPEND) && (this->mode != TSO_WRITE)) throw InvalidInvocation("invalid open mode");

	path path(this->root_path);
	while (!is_regular_file(path)) {
		vector<Timestamp> elements = scan_directory(path);
		path /= timestamp_to_string(*max_element(elements.begin(), elements.end()));
	}

	ofstream file(path.string().c_str(), std::ios::binary | std::ios::app);
	file.write((char*)value, 8+this->record_size);
	file.close();
}

void Anabel::TimeSeries::open(TimeSeriesOpenMode open_mode) throw(Anabel::Exceptions::InvalidInvocation) {
	/* Locking interaction table (whether two types of locks may be acquired if the other is present):
				READ			WRITE			REBALANCE			APPEND
	READ        YES             NO              NO                  YES
	WRITE       NO              NO              NO                  NO
	REBALANCE   NO              NO              NO                  YES
	APPEND      YES             NO              YES                 NO

	Types of locks acquired (eXclusive, Shareable):
		READ:		S(A)
		WRITE:		X(A)+X(B)
		APPEND:		X(B)
		REBALANCE:	X(A)

	As you may see, deadlocks will not happen
	*/
	switch (open_mode) {
		case TSO_APPEND:
			this->block->lock();
			break;
		case TSO_REBALANCE:
			this->alock->lock();
			break;
		case TSO_READ:
			this->alock->lock_sharable();
			break;
		case TSO_WRITE:
			this->alock->lock();
			this->block->lock();
			break;
		case TSO_CLOSED:	// funnt, not undefined though
			throw InvalidInvocation("invalid open_mode");
			break;
		default:
			throw InvalidInvocation("unknown open_mode");
	}

	// Read type of time series, as we don't know it now
	std::ifstream conf((this->root_path / "record_size").string().c_str());
	conf >> this->record_size;
	conf.close();

	this->mode = open_mode;
}

void Anabel::TimeSeries::indent(void) throw(Anabel::Exceptions::InvalidInvocation) {

	if ((this->mode != TSO_APPEND) && (this->mode != TSO_WRITE)) throw InvalidInvocation("invalid open mode");
	vector<Timestamp> elements = scan_directory(this->root_path);
	sort(elements.begin(), elements.end());
	vector<path> files;
	for (vector<Timestamp>::iterator iter = elements.begin(); iter != elements.end(); iter++)
		files.push_back(this->root_path / timestamp_to_string(*iter));

	DirectoryIterator diter(files, true);

	Anabel::Timestamp * timestamp = (Anabel::Timestamp*)malloc(8+this->record_size);

	while (true) {
		if (diter.empty) return;
		IntelligentFileReader ifr(diter.next(), this->record_size);
		if (ifr.records_remaining > 0) {
			ifr.seek_record(ifr.records_remaining-1);
			ifr.get_data(1,(void*)timestamp);
			make_empty_dataset(this->root_path / timestamp_to_string((*timestamp)+1));
		}
		ifr.close();
	}
}

bool Anabel::TimeSeries::get_last(void * buffer) throw(Anabel::Exceptions::InvalidInvocation) {
	if ((this->mode != TSO_READ) && (this->mode != TSO_WRITE)) throw InvalidInvocation("invalid open mode");

	vector<Timestamp> elements = scan_directory(this->root_path);
	sort(elements.begin(), elements.end());
	vector<path> files;
	for (vector<Timestamp>::iterator iter = elements.begin(); iter != elements.end(); iter++)
		files.push_back(this->root_path / timestamp_to_string(*iter));

	DirectoryIterator diter(files, true);

	while (true) {
		if (diter.empty) return false;
		IntelligentFileReader ifr(diter.next(), this->record_size);
		if (ifr.records_remaining > 0) {
			ifr.seek_record(ifr.records_remaining-1);
			ifr.get_data(1,buffer);
			return true;
		}
		ifr.close();
	}
}

void Anabel::TimeSeries::close(void) {
	switch (this->mode) {
		case TSO_READ:
			this->alock->unlock_sharable();
			break;
		case TSO_WRITE:
			this->alock->unlock();
			this->block->unlock();
			break;
		case TSO_REBALANCE:
			this->alock->unlock();
			break;
		case TSO_APPEND:
			this->block->unlock();
			break;
		case TSO_CLOSED:
			break;
	}
}

Anabel::TimeSeries::TimeSeries(char * rootdirpath) throw(Anabel::Exceptions::InvalidRootDirectory) : mode(TSO_CLOSED), record_size(0) {
	// Prepare pathes
	this->root_path = rootdirpath;
	path rsize_path(this->root_path);
	rsize_path /= "record_size";
	path alock_path(this->root_path);
	alock_path /= "alock";
	path block_path(this->root_path);
	block_path /= "block";

	// Sanity-check pathes
	if (!exists(this->root_path)) throw InvalidRootDirectory("root directory does not exist");
	if (!is_directory(this->root_path)) throw InvalidRootDirectory("root directory is not a directory");
	if (!exists(rsize_path)) throw InvalidRootDirectory("rsize_path file not found");
	if (!exists(alock_path)) throw InvalidRootDirectory("append lock not found");
	if (!exists(block_path)) throw InvalidRootDirectory("rebalance lock not found");

	// Create locks
	this->alock = new boost::interprocess::file_lock(alock_path.string().c_str());
	this->block = new boost::interprocess::file_lock(block_path.string().c_str());
}
Anabel::TimeSeries::~TimeSeries() {
	if (this->mode != TSO_CLOSED) this->close();
	delete this->alock;
	delete this->block;
}
<commit_msg>bug wherein close() would be called twice<commit_after>/*
    This file is part of Anabel

    Anabel is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.

    Anabel is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Anabel; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
#include <anabel/stdafx.h>
#include <anabel/anabel.h>

using namespace boost::filesystem;
using namespace boost::interprocess;
using namespace Anabel::Exceptions;
using namespace std;
using namespace Anabel;
using namespace Anabel::Internal;

void Anabel::TimeSeries::create(char * rootdirpath, int record_size) {
	path rootpath(rootdirpath);
	try {
		create_directory(rootpath);
	} catch (...) {}
	ofstream alock((rootpath / "alock").c_str()); alock.close();
	ofstream block((rootpath / "block").c_str()); block.close();
	ofstream rsf((rootpath / "record_size").c_str()); rsf << record_size; rsf.close();
	make_empty_dataset(rootpath / "0");
}

/**
* Chooses a timestamp that can be chosen, so it contains needle.
*
* Haystack must not be empty - other way, it would not make any sense, would it?
* Haystack must contain entry that will follow us to needle.
*/
Timestamp choose(vector<Timestamp> haystack, Timestamp needle) {
	for (unsigned i=0; i<haystack.size(); i++) {
		if (haystack[i] <= needle) return haystack[i];
	}
	throw InternalError("needle not found");
}

Anabel::ReadQuery * Anabel::TimeSeries::get_query(Anabel::Timestamp from, Anabel::Timestamp to) throw(Anabel::Exceptions::InvalidInvocation) {
	// Sanity checks
	if ((this->mode != TSO_READ) && (this->mode != TSO_WRITE)) throw InvalidInvocation("invalid open mode");
	if (from>to) throw InvalidInvocation("from>to");

	typedef vector<Timestamp> timevector;
	typedef vector<Timestamp>::iterator timevectoriter;

	// Init variables
	path cpath = this->root_path;
	timevector elements;
	Timestamp choice;
	vector<path> files;

	// Locate UBA
	cpath = this->root_path;
	try {
		while (is_directory(cpath)) {
			elements = scan_directory(cpath);
			sort(elements.begin(), elements.end(), greater<Timestamp>());
			choice = choose(elements, to);
			cpath /= timestamp_to_string(choice);
		}
	} catch (InternalError e) {
		// query empty.
		return new Anabel::ReadQuery(from, to, files, this->record_size);
	}

	files.push_back(cpath);
	if (choice <= from) {
		return new Anabel::ReadQuery(from, to, files, this->record_size);	// response is a single-file wonder
	}

	cpath = cpath.parent_path();

	// Now we will trace thru the filesystem, finding doodz. First up the tree, and then sharp dive towards 'to'
	Timestamp previous_choice = choice;
	while (true) {
		elements = scan_directory(cpath);
		sort(elements.begin(), elements.end(), std::greater<Timestamp>());
		timevectoriter bound = lower_bound(elements.begin(), elements.end(), from, std::greater<Timestamp>());

		if (bound == elements.end()) {	// we need to ascend
			// index files from bound upwards
			for (timevectoriter start_bound = upper_bound(elements.begin(), elements.end(), to, std::greater<Timestamp>()); start_bound != elements.end(); start_bound++) {
				if (previous_choice == *start_bound) continue;
				previous_choice = *start_bound;
				files.push_back(cpath / timestamp_to_string(*start_bound));
			}
			cpath = cpath.parent_path();
			continue;
		}

		// Descent to "bound"
		for (timevectoriter start_bound = upper_bound(elements.begin(), elements.end(), to, std::greater<Timestamp>()); start_bound != bound; start_bound++) {
			if (previous_choice == *start_bound) continue;
			previous_choice = *start_bound;
			files.push_back(cpath / timestamp_to_string(*start_bound));
		}

		cpath = cpath / timestamp_to_string(*bound);
		if (is_regular_file(cpath)) {
			files.push_back(cpath);
			break;
		}
	}

	return new Anabel::ReadQuery(from, to, files, this->record_size);
}

void Anabel::TimeSeries::truncate(void) throw(Anabel::Exceptions::InvalidInvocation) {
	if (this->mode != TSO_WRITE) throw InvalidInvocation("invalid open mode");

	vector<Timestamp> elements = scan_directory(this->root_path);
	for (vector<Timestamp>::iterator iter = elements.begin(); iter != elements.end(); iter++) remove_all(this->root_path / Anabel::Internal::timestamp_to_string(*iter));
	make_empty_dataset(this->root_path / "0");
}

void Anabel::TimeSeries::append(void * value) throw(Anabel::Exceptions::InvalidInvocation) {
	if ((this->mode != TSO_APPEND) && (this->mode != TSO_WRITE)) throw InvalidInvocation("invalid open mode");

	path path(this->root_path);
	while (!is_regular_file(path)) {
		vector<Timestamp> elements = scan_directory(path);
		path /= timestamp_to_string(*max_element(elements.begin(), elements.end()));
	}

	ofstream file(path.string().c_str(), std::ios::binary | std::ios::app);
	file.write((char*)value, 8+this->record_size);
	file.close();
}

void Anabel::TimeSeries::open(TimeSeriesOpenMode open_mode) throw(Anabel::Exceptions::InvalidInvocation) {
	/* Locking interaction table (whether two types of locks may be acquired if the other is present):
				READ			WRITE			REBALANCE			APPEND
	READ        YES             NO              NO                  YES
	WRITE       NO              NO              NO                  NO
	REBALANCE   NO              NO              NO                  YES
	APPEND      YES             NO              YES                 NO

	Types of locks acquired (eXclusive, Shareable):
		READ:		S(A)
		WRITE:		X(A)+X(B)
		APPEND:		X(B)
		REBALANCE:	X(A)

	As you may see, deadlocks will not happen
	*/
	switch (open_mode) {
		case TSO_APPEND:
			this->block->lock();
			break;
		case TSO_REBALANCE:
			this->alock->lock();
			break;
		case TSO_READ:
			this->alock->lock_sharable();
			break;
		case TSO_WRITE:
			this->alock->lock();
			this->block->lock();
			break;
		case TSO_CLOSED:	// funnt, not undefined though
			throw InvalidInvocation("invalid open_mode");
			break;
		default:
			throw InvalidInvocation("unknown open_mode");
	}

	// Read type of time series, as we don't know it now
	std::ifstream conf((this->root_path / "record_size").string().c_str());
	conf >> this->record_size;
	conf.close();

	this->mode = open_mode;
}

void Anabel::TimeSeries::indent(void) throw(Anabel::Exceptions::InvalidInvocation) {

	if ((this->mode != TSO_APPEND) && (this->mode != TSO_WRITE)) throw InvalidInvocation("invalid open mode");
	vector<Timestamp> elements = scan_directory(this->root_path);
	sort(elements.begin(), elements.end());
	vector<path> files;
	for (vector<Timestamp>::iterator iter = elements.begin(); iter != elements.end(); iter++)
		files.push_back(this->root_path / timestamp_to_string(*iter));

	DirectoryIterator diter(files, true);

	Anabel::Timestamp * timestamp = (Anabel::Timestamp*)malloc(8+this->record_size);

	while (true) {
		if (diter.empty) return;
		IntelligentFileReader ifr(diter.next(), this->record_size);
		if (ifr.records_remaining > 0) {
			ifr.seek_record(ifr.records_remaining-1);
			ifr.get_data(1,(void*)timestamp);
			make_empty_dataset(this->root_path / timestamp_to_string((*timestamp)+1));
		}
		ifr.close();
	}
}

bool Anabel::TimeSeries::get_last(void * buffer) throw(Anabel::Exceptions::InvalidInvocation) {
	if ((this->mode != TSO_READ) && (this->mode != TSO_WRITE)) throw InvalidInvocation("invalid open mode");

	vector<Timestamp> elements = scan_directory(this->root_path);
	sort(elements.begin(), elements.end());
	vector<path> files;
	for (vector<Timestamp>::iterator iter = elements.begin(); iter != elements.end(); iter++)
		files.push_back(this->root_path / timestamp_to_string(*iter));

	DirectoryIterator diter(files, true);

	while (true) {
		if (diter.empty) return false;
		IntelligentFileReader ifr(diter.next(), this->record_size);
		if (ifr.records_remaining > 0) {
			ifr.seek_record(ifr.records_remaining-1);
			ifr.get_data(1,buffer);
			return true;
		}
		ifr.close();
	}
}

void Anabel::TimeSeries::close(void) {
	switch (this->mode) {
		case TSO_READ:
			this->alock->unlock_sharable();
			break;
		case TSO_WRITE:
			this->alock->unlock();
			this->block->unlock();
			break;
		case TSO_REBALANCE:
			this->alock->unlock();
			break;
		case TSO_APPEND:
			this->block->unlock();
			break;
		case TSO_CLOSED:
			break;
	}
	this->mode = TSO_CLOSED;
}

Anabel::TimeSeries::TimeSeries(char * rootdirpath) throw(Anabel::Exceptions::InvalidRootDirectory) : mode(TSO_CLOSED), record_size(0) {
	// Prepare pathes
	this->root_path = rootdirpath;
	path rsize_path(this->root_path);
	rsize_path /= "record_size";
	path alock_path(this->root_path);
	alock_path /= "alock";
	path block_path(this->root_path);
	block_path /= "block";

	// Sanity-check pathes
	if (!exists(this->root_path)) throw InvalidRootDirectory("root directory does not exist");
	if (!is_directory(this->root_path)) throw InvalidRootDirectory("root directory is not a directory");
	if (!exists(rsize_path)) throw InvalidRootDirectory("rsize_path file not found");
	if (!exists(alock_path)) throw InvalidRootDirectory("append lock not found");
	if (!exists(block_path)) throw InvalidRootDirectory("rebalance lock not found");

	// Create locks
	this->alock = new boost::interprocess::file_lock(alock_path.string().c_str());
	this->block = new boost::interprocess::file_lock(block_path.string().c_str());
}
Anabel::TimeSeries::~TimeSeries() {
	if (this->mode != TSO_CLOSED) this->close();
	delete this->alock;
	delete this->block;
}
<|endoftext|>


<commit_before>#include "game.hpp"

int Game::startGame() {
	std::string str;
	while(true) {
		std::getline(std::cin, str);

		if(!uciHandler(str)) {
			return 0;
		};
	}
}

void Game::goFixedDepth() {
	clearCash();
	stopped = false;

	variant.clear();
	variant.resize(max_depth);
	std::vector<uint64_t>hash;

	whiteUp = BLACK_WIN;
	blackUp = WHITE_WIN;

	nodesCounter = 0;
	int max_depth_global = max_depth;
	max_depth = 1;

	if(option.lmrEnable || option.nullMovePrunningEnable) {
		max_depth = 5;
	}

	start_timer = clock();
	hasBestMove = false;
	//double depth;

	BitMove moveCritical = game_board.getRandomMove();
	bestMove = moveCritical;
	hasBestMove = true;
	bestScore = 0;

	for(; max_depth <= max_depth_global; ++max_depth) {
		whiteUp = BLACK_WIN;
		blackUp = WHITE_WIN;
		flattenHistory();

		negamax(game_board, -WHITE_WIN, WHITE_WIN, max_depth, 0, FIXED_DEPTH, false);
		hasBestMove = true;

		if((abs(bestScore) >= (WHITE_WIN - 100) && max_depth_global < 99) || stopped) {
			break;
		}

		//depth = max_depth;
	}

	end_timer = clock();

	/*std::cout << "info depth " << depth << " ";
	printScore(bestScore);
	std::cout << " pv " << bestMove.getMoveString();
	std::cout << " nodes " << nodesCounter << " nps " << (int)(nodesCounter / ((end_timer - start_timer) / CLOCKS_PER_SEC)) <<
	" time " << (int)((end_timer - start_timer) / (CLOCKS_PER_SEC / 1000)) << std::endl;*/
	if(hasBestMove) {
		std::cout << "bestmove " << bestMove.getMoveString() << std::endl;
	}

	/*for(int i = 0; i < bestPV.size(); ++i) {
		std::cout << bestPV[i].getMoveString() << " ";
	}
	std::cout << std::endl;*/
}

void Game::goFixedTime(int tm) {
	stopped = false;
	if(tm >= 200) {
		tm -= 100;
	}

	time = tm;
	timer.start();

	clearCash();

	variant.clear();
	variant.resize(max_depth);
	std::vector<uint64_t>hash;

	whiteUp = BLACK_WIN;
	blackUp = WHITE_WIN;

	nodesCounter = 0;

	start_timer = clock();
	hasBestMove = false;
	//double depth;

	BitMove moveCritical = game_board.getRandomMove();
	bestMove = moveCritical;
	bestScore = 0;
	hasBestMove = true;
	max_depth = 1;

	if(option.lmrEnable || option.nullMovePrunningEnable) {
		max_depth = 5;
	}

	std::vector<BitMove> bestPV;

	for(; timer.getTime() < time; ++max_depth) {
		whiteUp = BLACK_WIN;
		blackUp = WHITE_WIN;
		flattenHistory();

		negamax(game_board, -WHITE_WIN, WHITE_WIN, max_depth, 0, FIXED_TIME, false);

		if(abs(bestScore) >= (WHITE_WIN - 100) || stopped) {
			break;
		}

		hasBestMove = true;
	}

	end_timer = clock();

	if(hasBestMove) {
		std::cout << "bestmove " << bestMove.getMoveString() << std::endl;
	}
}

void Game::goTournament() {
	double tm, inc;
	if(game_board.whiteMove) {
		tm = wtime;
		inc = winc;
	} else {
		tm = btime;
		inc = binc;
	}

	double k = 50;

	if(movestogoEnable) {
		k = movestogo;
		goFixedTime(tm / k + inc);
	} else {
		goFixedTime(tm / k + inc - 100);
	}
}

bool Game::move(std::string mv) {
	MoveArray moves;
	game_board.bitBoardMoveGenerator(moves);
	for(unsigned int i = 0; i < moves.count; ++i) {
		if(moves.moveArray[i].getMoveString() == mv) {
			game_board.move(moves.moveArray[i]);
			uint8_t color;

			if(game_board.whiteMove) {
				color = BLACK;
			} else {
				color = WHITE;
			}

			if(game_board.inCheck(color)) {
				game_board.goBack();
				continue;
			}

			++hash_decrement;
			std::cout << game_board.getFen() << " " << game_board.hash_enable << " " << game_board.hash << std::endl;
			return true;
		}
	}

	return false;
}
<commit_msg>last upd.<commit_after>#include "game.hpp"

int Game::startGame() {
	std::string str;
	while(true) {
		std::getline(std::cin, str);

		if(!uciHandler(str)) {
			return 0;
		};
	}
}

void Game::goFixedDepth() {
	clearCash();
	stopped = false;

	variant.clear();
	variant.resize(max_depth);
	std::vector<uint64_t>hash;

	whiteUp = BLACK_WIN;
	blackUp = WHITE_WIN;

	nodesCounter = 0;
	int max_depth_global = max_depth;
	max_depth = 1;

	if(option.lmrEnable || option.nullMovePrunningEnable) {
		max_depth = 5;
	}

	start_timer = clock();
	hasBestMove = false;
	//double depth;

	BitMove moveCritical = game_board.getRandomMove();
	bestMove = moveCritical;
	hasBestMove = true;
	bestScore = 0;

	for(; max_depth <= max_depth_global; ++max_depth) {
		whiteUp = BLACK_WIN;
		blackUp = WHITE_WIN;
		flattenHistory();

		negamax(game_board, -WHITE_WIN, WHITE_WIN, max_depth, 0, FIXED_DEPTH, false);
		hasBestMove = true;

		if((abs(bestScore) >= (WHITE_WIN - 100) && max_depth_global < 99) || stopped) {
			break;
		}

		//depth = max_depth;
	}

	end_timer = clock();

	/*std::cout << "info depth " << depth << " ";
	printScore(bestScore);
	std::cout << " pv " << bestMove.getMoveString();
	std::cout << " nodes " << nodesCounter << " nps " << (int)(nodesCounter / ((end_timer - start_timer) / CLOCKS_PER_SEC)) <<
	" time " << (int)((end_timer - start_timer) / (CLOCKS_PER_SEC / 1000)) << std::endl;*/
	if(hasBestMove) {
		std::cout << "bestmove " << bestMove.getMoveString() << std::endl;
	}

	/*for(int i = 0; i < bestPV.size(); ++i) {
		std::cout << bestPV[i].getMoveString() << " ";
	}
	std::cout << std::endl;*/
}

void Game::goFixedTime(int tm) {
	stopped = false;
	if(tm >= 200) {
		tm -= 100;
	}

	time = tm;
	timer.start();

	clearCash();

	variant.clear();
	variant.resize(max_depth);
	std::vector<uint64_t>hash;

	whiteUp = BLACK_WIN;
	blackUp = WHITE_WIN;

	nodesCounter = 0;

	start_timer = clock();
	hasBestMove = false;
	//double depth;

	BitMove moveCritical = game_board.getRandomMove();
	bestMove = moveCritical;
	bestScore = 0;
	hasBestMove = true;
	max_depth = 1;

	if(option.lmrEnable || option.nullMovePrunningEnable) {
		max_depth = 5;
	}

	std::vector<BitMove> bestPV;

	for(; timer.getTime() < time; ++max_depth) {
		whiteUp = BLACK_WIN;
		blackUp = WHITE_WIN;
		flattenHistory();

		negamax(game_board, -WHITE_WIN, WHITE_WIN, max_depth, 0, FIXED_TIME, false);

		if(abs(bestScore) >= (WHITE_WIN - 100) || stopped) {
			break;
		}

		hasBestMove = true;
	}

	end_timer = clock();

	if(hasBestMove) {
		std::cout << "bestmove " << bestMove.getMoveString() << std::endl;
	}
}

void Game::goTournament() {
	double tm, inc;
	if(game_board.whiteMove) {
		tm = wtime;
		inc = winc;
	} else {
		tm = btime;
		inc = binc;
	}

	double k = 50;

	if(movestogoEnable) {
		k = movestogo;
		goFixedTime(tm / k + inc);
	} else {
		goFixedTime(tm / k + inc - 100);
	}
}

bool Game::move(std::string mv) {
	MoveArray moves;
	game_board.bitBoardMoveGenerator(moves);
	for(unsigned int i = 0; i < moves.count; ++i) {
		if(moves.moveArray[i].getMoveString() == mv) {
			game_board.move(moves.moveArray[i]);
			uint8_t color;

			if(game_board.whiteMove) {
				color = BLACK;
			} else {
				color = WHITE;
			}

			if(game_board.inCheck(color)) {
				game_board.goBack();
				continue;
			}

			++hash_decrement;
			return true;
		}
	}

	return false;
}
<|endoftext|>


<commit_before>#include "base/stream_roller.h"

#include "core/aver.h"

#include "cr_constants.h"

using namespace roller;
using std::list;
using std::pair;
using std::unique_ptr;

namespace cr {

// listStreamContents
list<pair<i32, i64>> listStreamContents( void* pointer ) {
	Log::i( "Listing stream contents (%p)...", pointer );

	ui8* buffer = (ui8*)pointer;

	// do some validation
	if ( buffer[0] != 0x73 || buffer[1] != 0x72 ) {
		throw RollerException( "Not a valid StreamRoller stream (invalid begin transmission designation)" );
	}

	if ( (buffer[3] != (ui8)CR_MAJOR_VERSION)
			|| (buffer[4] != (ui8)CR_MINOR_VERSION)
			|| (buffer[5] != (ui8)CR_PATCH_VERSION) ) {

		throw RollerException( "Stream is of a different version" );
	}

	ui32* numberAddress = (ui32*)(buffer + 14);
	ui32 numberOfObjects = *numberAddress;

	Log::i( "  - number of objects: %d", numberOfObjects );

	i64 objectOffset = 18;

	list<pair<i32, i64>> objects;
	for ( ui32 i=0; i<numberOfObjects; i++ ) {

		i64* objectSizeAddress = (i64*)(buffer + objectOffset);
		i64 objectSize = *objectSizeAddress;

		i32* objectDefinitionHashAddress = (i32*)(buffer + objectOffset + sizeof(i64));
		i32 objectDefinitionHash = *objectDefinitionHashAddress;

		Log::i( " - object: %d : %ld", objectDefinitionHash, objectSize );

		objects.push_back( pair<i32, i64>( objectDefinitionHash, objectSize ));
	}

	return objects;
}

// createStream
pair<i64, unique_ptr<ui8[]>> createStreamContents( const list<Serializable*> objects ) {

	i64 size = 2 + 4 + 8 + 4 + 2; // all fields other than objects themselves
	for ( Serializable* object : objects ) {
		size += object->getSerializedSize() + 8 + 4; // 8 bytes for size, 4 bytes for hash
	}

	ui8* pointer = new ui8[size];
	unique_ptr<ui8[]> buffer( pointer );

	// fill in buffer...

	// beginning designation
	buffer[0] = 0x73;
	buffer[1] = 0x72;

	// CR version
	buffer[2] = 0x00;
	buffer[3] = (ui8)CR_MAJOR_VERSION;
	buffer[4] = (ui8)CR_MINOR_VERSION;
	buffer[5] = (ui8)CR_PATCH_VERSION;

	// TODO: git hash
	// byte[6 - 13]

	// number of objects
	ui32* numberAddress = (ui32*)(pointer + 14);
	*numberAddress = objects.size();

	i64 objectOffset = 18;

	for ( Serializable* object : objects ) {

		i64 objectSize = object->getSerializedSize();
		i32 objectDefinitionHash = object->getClassHash();

		i64* objectSizeAddress = (i64*)(pointer + objectOffset);
		*objectSizeAddress = objectSize;

		i32* objectDefinitionHashAddress = (i32*)(pointer + objectOffset + sizeof(i64));
		*objectDefinitionHashAddress = objectDefinitionHash;

		i64 written = object->serialize( (void*)(pointer + objectOffset + sizeof(i64) + sizeof(i32)) );

		if ( written != objectSize ) {
			throw RollerException(
					"Serialized object did not write exactly its size (size=%ld, written=%ld)",
					objectSize,
					written );
		}

		objectOffset += (objectSize + sizeof(i64) + sizeof(i32));

	}

	// end designation
	buffer[objectOffset] = 0x73;
	buffer[objectOffset + 1] = 0x72;

	return pair<i64, unique_ptr<ui8[]>>( size, std::move(buffer) );

}

}
<commit_msg>List object's offset in data stream, not its size<commit_after>#include "base/stream_roller.h"

#include "core/aver.h"

#include "cr_constants.h"

using namespace roller;
using std::list;
using std::pair;
using std::unique_ptr;

namespace cr {

// listStreamContents
list<pair<i32, i64>> listStreamContents( void* pointer ) {
	Log::i( "Listing stream contents (%p)...", pointer );

	ui8* buffer = (ui8*)pointer;

	// do some validation
	if ( buffer[0] != 0x73 || buffer[1] != 0x72 ) {
		throw RollerException( "Not a valid StreamRoller stream (invalid begin transmission designation)" );
	}

	if ( (buffer[3] != (ui8)CR_MAJOR_VERSION)
			|| (buffer[4] != (ui8)CR_MINOR_VERSION)
			|| (buffer[5] != (ui8)CR_PATCH_VERSION) ) {

		throw RollerException( "Stream is of a different version" );
	}

	ui32* numberAddress = (ui32*)(buffer + 14);
	ui32 numberOfObjects = *numberAddress;

	Log::i( "  - number of objects: %d", numberOfObjects );

	i64 objectOffset = 18;

	list<pair<i32, i64>> objects;
	for ( ui32 i=0; i<numberOfObjects; i++ ) {

		i64* objectSizeAddress = (i64*)(buffer + objectOffset);
		i64 objectSize = *objectSizeAddress;

		i32* objectDefinitionHashAddress = (i32*)(buffer + objectOffset + sizeof(i64));
		i32 objectDefinitionHash = *objectDefinitionHashAddress;

		Log::i( " - object: %d : %ld", objectDefinitionHash, objectSize );

		objects.push_back( pair<i32, i64>( objectDefinitionHash, objectOffset + sizeof(i64) + sizeof(i32) ));
	}

	return objects;
}

// createStream
pair<i64, unique_ptr<ui8[]>> createStreamContents( const list<Serializable*> objects ) {

	i64 size = 2 + 4 + 8 + 4 + 2; // all fields other than objects themselves
	for ( Serializable* object : objects ) {
		size += object->getSerializedSize() + 8 + 4; // 8 bytes for size, 4 bytes for hash
	}

	ui8* pointer = new ui8[size];
	unique_ptr<ui8[]> buffer( pointer );

	// fill in buffer...

	// beginning designation
	buffer[0] = 0x73;
	buffer[1] = 0x72;

	// CR version
	buffer[2] = 0x00;
	buffer[3] = (ui8)CR_MAJOR_VERSION;
	buffer[4] = (ui8)CR_MINOR_VERSION;
	buffer[5] = (ui8)CR_PATCH_VERSION;

	// TODO: git hash
	// byte[6 - 13]

	// number of objects
	ui32* numberAddress = (ui32*)(pointer + 14);
	*numberAddress = objects.size();

	i64 objectOffset = 18;

	for ( Serializable* object : objects ) {

		i64 objectSize = object->getSerializedSize();
		i32 objectDefinitionHash = object->getClassHash();

		i64* objectSizeAddress = (i64*)(pointer + objectOffset);
		*objectSizeAddress = objectSize;

		i32* objectDefinitionHashAddress = (i32*)(pointer + objectOffset + sizeof(i64));
		*objectDefinitionHashAddress = objectDefinitionHash;

		i64 written = object->serialize( (void*)(pointer + objectOffset + sizeof(i64) + sizeof(i32)) );

		if ( written != objectSize ) {
			throw RollerException(
					"Serialized object did not write exactly its size (size=%ld, written=%ld)",
					objectSize,
					written );
		}

		objectOffset += (objectSize + sizeof(i64) + sizeof(i32));

	}

	// end designation
	buffer[objectOffset] = 0x73;
	buffer[objectOffset + 1] = 0x72;

	return pair<i64, unique_ptr<ui8[]>>( size, std::move(buffer) );

}

}
<|endoftext|>


<commit_before>//Copyright 2015 Adam Smith
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.

// Contact :
// Email             : solairelibrary@mail.com
// GitHub repository : https://github.com/SolaireLibrary/SolaireCPP

#include "Solaire/Json/Format.hpp"

namespace Solaire {

    static bool writeValue(const GenericValue& aValue, OStream& aStream) throw();

    static bool writeNull(OStream& aStream) throw() {
        aStream << "null";
        return true;
    }

    static bool writeChar(const char aValue, OStream& aStream) throw() {
        aStream << '"' << aValue << '"';
        return true;
    }

    static bool writeBool(const bool aValue, OStream& aStream) throw() {
        return false;
    }

    static bool writeUnsigned(const uint64_t aValue, OStream& aStream) throw() {
        return false;
    }

    static bool writeSigned(const int64_t aValue, OStream& aStream) throw() {
        return false;
    }

    static bool writeDouble(const double aValue, OStream& aStream) throw() {
        return false;
    }

    static bool writeString(const StringConstant<char>& aValue, OStream& aStream) throw() {
        aStream << '"';
        aStream << aValue;
        aStream << '"';
        return true;
    }

    static bool writeArray(const GenericArray& aValue, OStream& aStream) throw() {
        aStream << '[';
        const auto end = aValue.end();
        for(auto i = aValue.begin(); i != end; ++i) if(! writeValue(*i, aStream)) return false;
        aStream << ']';
        return true;
    }

    static bool writeObject(const GenericObject& aValue, OStream& aStream) throw() {
        return false;
    }

    static bool writeValue(const GenericValue& aValue, OStream& aStream) throw() {
        switch(aValue.getType()) {
        case GenericValue::NULL_T:
            return writeNull(aStream);
        case GenericValue::CHAR_T:
            return writeChar(aValue.getChar(), aStream);
        case GenericValue::BOOL_T:
            return writeBool(aValue.getBool(), aStream);
        case GenericValue::UNSIGNED_T:
            return writeUnsigned(aValue.getUnsigned(), aStream);
        case GenericValue::SIGNED_T:
            return writeSigned(aValue.getSigned(), aStream);
        case GenericValue::DOUBLE_T:
            return writeDouble(aValue.getDouble(), aStream);
        case GenericValue::STRING_T:
            return writeString(aValue.getString(), aStream);
        case GenericValue::ARRAY_T:
            return writeArray(aValue.getArray(), aStream);
        case GenericValue::OBJECT_T:
            return writeObject(aValue.getObject(), aStream);
        default:
            return false;
        }
    }

    // JsonFormat

    SOLAIRE_EXPORT_CALL JsonFormat::~JsonFormat() {

    }

    GenericValue SOLAIRE_EXPORT_CALL JsonFormat::readValue(IStream&) const throw() {

    }

    bool SOLAIRE_EXPORT_CALL JsonFormat::writeValue(const GenericValue& aValue, OStream& aStream) const throw() {
        return Solaire::writeValue(aValue, aStream);
    }

}
<commit_msg>Object write code<commit_after>//Copyright 2015 Adam Smith
//
//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.

// Contact :
// Email             : solairelibrary@mail.com
// GitHub repository : https://github.com/SolaireLibrary/SolaireCPP

#include "Solaire/Json/Format.hpp"

namespace Solaire {

    static bool writeValue(const GenericValue& aValue, OStream& aStream) throw();

    static bool writeNull(OStream& aStream) throw() {
        aStream << "null";
        return true;
    }

    static bool writeChar(const char aValue, OStream& aStream) throw() {
        aStream << '"' << aValue << '"';
        return true;
    }

    static bool writeBool(const bool aValue, OStream& aStream) throw() {
        return false;
    }

    static bool writeUnsigned(const uint64_t aValue, OStream& aStream) throw() {
        return false;
    }

    static bool writeSigned(const int64_t aValue, OStream& aStream) throw() {
        return false;
    }

    static bool writeDouble(const double aValue, OStream& aStream) throw() {
        return false;
    }

    static bool writeString(const StringConstant<char>& aValue, OStream& aStream) throw() {
        aStream << '"';
        aStream << aValue;
        aStream << '"';
        return true;
    }

    static bool writeArray(const GenericArray& aValue, OStream& aStream) throw() {
        aStream << '[';
        for(const GenericValue& i : aValue) if(! writeValue(i, aStream)) return false;
        aStream << ']';
        return true;
    }

    static bool writeObject(const GenericObject& aValue, OStream& aStream) throw() {
        aStream << '{';
        const SharedAllocation<StaticContainer<GenericObject::Entry>> entries = aValue.getEntries();
        for(const GenericObject::Entry& i : *entries) {
            if(! writeString(i.first, aStream)) return false;
            aStream << ':';
            if(! writeValue(i.second, aStream)) return false;
        }
        aStream << '}';
        return false;
    }

    static bool writeValue(const GenericValue& aValue, OStream& aStream) throw() {
        switch(aValue.getType()) {
        case GenericValue::NULL_T:
            return writeNull(aStream);
        case GenericValue::CHAR_T:
            return writeChar(aValue.getChar(), aStream);
        case GenericValue::BOOL_T:
            return writeBool(aValue.getBool(), aStream);
        case GenericValue::UNSIGNED_T:
            return writeUnsigned(aValue.getUnsigned(), aStream);
        case GenericValue::SIGNED_T:
            return writeSigned(aValue.getSigned(), aStream);
        case GenericValue::DOUBLE_T:
            return writeDouble(aValue.getDouble(), aStream);
        case GenericValue::STRING_T:
            return writeString(aValue.getString(), aStream);
        case GenericValue::ARRAY_T:
            return writeArray(aValue.getArray(), aStream);
        case GenericValue::OBJECT_T:
            return writeObject(aValue.getObject(), aStream);
        default:
            return false;
        }
    }

    // JsonFormat

    SOLAIRE_EXPORT_CALL JsonFormat::~JsonFormat() {

    }

    GenericValue SOLAIRE_EXPORT_CALL JsonFormat::readValue(IStream&) const throw() {

    }

    bool SOLAIRE_EXPORT_CALL JsonFormat::writeValue(const GenericValue& aValue, OStream& aStream) const throw() {
        return Solaire::writeValue(aValue, aStream);
    }

}
<|endoftext|>


<commit_before>/**
 * @file pop.cpp
 *
 */
#include "pop.h"
#include "forecast_time.h"
#include "level.h"
#include "logger.h"
#include "numerical_functions.h"
#include "plugin_factory.h"
#include "util.h"

#include "fetcher.h"

using namespace std;
using namespace himan::plugin;

himan::raw_time GetOriginTime(const himan::raw_time& start, int producerId)
{
	ASSERT(producerId == 242 || producerId == 260);

	// Provide latest analysis time for either MEPS or ECEPS
	himan::raw_time ret = start;

	const int hour = stoi(start.String("%H"));
	const int ostep = (producerId == 242) ? 12 : 3;  // origin time "step"
	const int adjust = (hour % ostep == 0) ? ostep : hour % ostep;
	ret.Adjust(himan::kHourResolution, -adjust);

	return ret;
}

pop::pop() : itsECEPSGeom("ECGLO0200"), itsMEPSGeom("MEPS2500D")
{
	itsLogger = logger("pop");
}

void pop::Process(std::shared_ptr<const plugin_configuration> conf)
{
	Init(conf);

	param theRequestedParam("POP-0TO1");

	theRequestedParam.Unit(kPrcnt);

	SetParams({theRequestedParam});

	if (itsConfiguration->Exists("eceps_geom"))
	{
		itsECEPSGeom = itsConfiguration->GetValue("eceps_geom");
	}

	if (itsConfiguration->Exists("meps_geom"))
	{
		itsMEPSGeom = itsConfiguration->GetValue("meps_geom");
	}

	Start();
}

void pop::Calculate(shared_ptr<info<double>> myTargetInfo, unsigned short threadIndex)
{
	// 2021 POP
	// version 6.9.2021
	// Use ensemble forecasts precipitation probabilies as source data
	// <= 45h forecasts: Prob MEPS(1h) RR>=0.025mm
	// 46h - 130h forecasts: Prob EC(3h) RR>=0.2mm
	// >131h forecasts: Prob EC(6h) RR>=0.4mm
	// also: if smartemt data contains rain, POP should have values
	// also: weaken probabilites of temporally distant rains
	// finish with spatial smoothing

	// Current time and level as given to this thread

	const forecast_type forecastType = myTargetInfo->ForecastType();
	forecast_time forecastTime = myTargetInfo->Time();
	const level forecastLevel = myTargetInfo->Level();

	auto myThreadedLogger = logger(fmt::format("popThread #{}", threadIndex));

	myThreadedLogger.Debug(fmt::format("Calculating time {} level {}",
	                                   static_cast<string>(forecastTime.ValidDateTime()),
	                                   static_cast<string>(forecastLevel)));

	shared_ptr<info<double>> MEPS, EC, SmartMet;

	const long step = forecastTime.Step().Hours();

	if (step <= 46)
	{
		myThreadedLogger.Info("Fetching MEPS");

		int tryNo = 0;

		forecast_time curTime = forecastTime;
		const param p("PROB-RR-7");  // MEPS(1h) RR>=0.025mm
		const producer MEPSprod(260, 86, 204, "MEPSMTA");
		do
		{
			raw_time now;
			curTime = forecast_time(GetOriginTime(curTime.OriginDateTime(), 260), curTime.ValidDateTime());

			MEPS =
			    Fetch(curTime, forecastLevel, p, forecast_type(kStatisticalProcessing), {itsMEPSGeom}, MEPSprod, false);
			tryNo++;
		} while (tryNo < 3 && !MEPS);

		if (!MEPS)
		{
			return;
		}
	}

	if (step >= 43)
	{
		myThreadedLogger.Info("Fetching ECMWF");

		param p("PROB-RR3-6");  // EC(3h) RR>0.2mm

		int tryNo = 0;

		forecast_time curTime = forecastTime;
		const producer ECprod(242, 86, 242, "ECGEPSMTA");

		do
		{
			curTime = forecast_time(GetOriginTime(curTime.OriginDateTime(), 242), curTime.ValidDateTime());

			if (curTime.Step().Hours() > 131)
			{
				p = param("PROB-RR-4");  // EC(6h) RR>0.4mm
			}

			EC = Fetch(curTime, forecastLevel, p, forecast_type(kStatisticalProcessing), {itsECEPSGeom}, ECprod, false);
			tryNo++;
		} while (tryNo < 2 && !EC);

		if (!EC)
		{
			return;
		}
	}

	myThreadedLogger.Info("Fetching SmartMet");

	SmartMet = Fetch(forecastTime, forecastLevel, param("RRR-KGM2"), forecastType);

	if (!SmartMet)
	{
		return;
	}

	vector<double> result(myTargetInfo->Data().Size(), MissingDouble());

	const auto& MEPSdata = (MEPS) ? VEC(MEPS) : result;
	const auto& ECdata = (EC) ? VEC(EC) : result;
	const auto& SmartMetdata = VEC(SmartMet);

	auto& resultdata = VEC(myTargetInfo);

	// maxprob and minprob are used to limit the allowed range
	// of values

	// shortest forecast has these:
	double maxprob = 1.0;
	double minprob = 0.8;

	if (step > 130)
	{
		maxprob = 0.7;
		minprob = 0.1;
	}
	else if (step > 45)
	{
		maxprob = 0.8;
		minprob = 0.2;
	}
	else if (step > 24)
	{
		maxprob = 0.9;
		minprob = 0.4;
	}
	else if (step > 6)
	{
		minprob = 0.6;
	}

	for (auto&& tup : zip_range(resultdata, MEPSdata, ECdata, SmartMetdata))
	{
		double& r = tup.get<0>();
		const double meps = tup.get<1>();
		const double ec = tup.get<2>();
		const double rr = tup.get<3>();

		if (step < 43)
		{
			r = meps;
		}
		else if (step >= 43 && step <= 46)
		{
			r = (meps + ec) * 0.5;
		}
		else
		{
			r = ec;
		}

		if (rr > 0.0)
		{
			r = max(r, minprob);
		}

		r = min(r, maxprob);
	}

	// Produce area average over the neighboring grid points
	// x x x x x
	// x x x x x
	// x x o x x
	// x x x x x
	// x x x x x

	const himan::matrix<double> filter_kernel(5, 5, 1, MissingDouble(), 1 / 25.);

	const auto smoothened =
	    numerical_functions::Filter2D<double>(myTargetInfo->Data(), filter_kernel, itsConfiguration->UseCuda());

	myTargetInfo->Base()->data = move(smoothened);

	myThreadedLogger.Info(
	    fmt::format("[CPU] Missing values: {}/{}", myTargetInfo->Data().MissingCount(), myTargetInfo->Data().Size()));
}
<commit_msg>Add minimum precipitation value of 0.01mm/h<commit_after>/**
 * @file pop.cpp
 *
 */
#include "pop.h"
#include "forecast_time.h"
#include "level.h"
#include "logger.h"
#include "numerical_functions.h"
#include "plugin_factory.h"
#include "util.h"

#include "fetcher.h"

using namespace std;
using namespace himan::plugin;

himan::raw_time GetOriginTime(const himan::raw_time& start, int producerId)
{
	ASSERT(producerId == 242 || producerId == 260);

	// Provide latest analysis time for either MEPS or ECEPS
	himan::raw_time ret = start;

	const int hour = stoi(start.String("%H"));
	const int ostep = (producerId == 242) ? 12 : 3;  // origin time "step"
	const int adjust = (hour % ostep == 0) ? ostep : hour % ostep;
	ret.Adjust(himan::kHourResolution, -adjust);

	return ret;
}

pop::pop() : itsECEPSGeom("ECGLO0200"), itsMEPSGeom("MEPS2500D")
{
	itsLogger = logger("pop");
}

void pop::Process(std::shared_ptr<const plugin_configuration> conf)
{
	Init(conf);

	param theRequestedParam("POP-0TO1");

	theRequestedParam.Unit(kPrcnt);

	SetParams({theRequestedParam});

	if (itsConfiguration->Exists("eceps_geom"))
	{
		itsECEPSGeom = itsConfiguration->GetValue("eceps_geom");
	}

	if (itsConfiguration->Exists("meps_geom"))
	{
		itsMEPSGeom = itsConfiguration->GetValue("meps_geom");
	}

	Start();
}

void pop::Calculate(shared_ptr<info<double>> myTargetInfo, unsigned short threadIndex)
{
	// 2021 POP
	// version 6.9.2021
	// Use ensemble forecasts precipitation probabilies as source data
	// <= 45h forecasts: Prob MEPS(1h) RR>=0.025mm
	// 46h - 130h forecasts: Prob EC(3h) RR>=0.2mm
	// >131h forecasts: Prob EC(6h) RR>=0.4mm
	// also: if smartemt data contains rain, POP should have values
	// also: weaken probabilites of temporally distant rains
	// finish with spatial smoothing

	// Current time and level as given to this thread

	const forecast_type forecastType = myTargetInfo->ForecastType();
	forecast_time forecastTime = myTargetInfo->Time();
	const level forecastLevel = myTargetInfo->Level();

	auto myThreadedLogger = logger(fmt::format("popThread #{}", threadIndex));

	myThreadedLogger.Debug(fmt::format("Calculating time {} level {}",
	                                   static_cast<string>(forecastTime.ValidDateTime()),
	                                   static_cast<string>(forecastLevel)));

	shared_ptr<info<double>> MEPS, EC, SmartMet;

	const long step = forecastTime.Step().Hours();

	if (step <= 46)
	{
		myThreadedLogger.Info("Fetching MEPS");

		int tryNo = 0;

		forecast_time curTime = forecastTime;
		const param p("PROB-RR-7");  // MEPS(1h) RR>=0.025mm
		const producer MEPSprod(260, 86, 204, "MEPSMTA");
		do
		{
			raw_time now;
			curTime = forecast_time(GetOriginTime(curTime.OriginDateTime(), 260), curTime.ValidDateTime());

			MEPS =
			    Fetch(curTime, forecastLevel, p, forecast_type(kStatisticalProcessing), {itsMEPSGeom}, MEPSprod, false);
			tryNo++;
		} while (tryNo < 3 && !MEPS);

		if (!MEPS)
		{
			return;
		}
	}

	if (step >= 43)
	{
		myThreadedLogger.Info("Fetching ECMWF");

		param p("PROB-RR3-6");  // EC(3h) RR>0.2mm

		int tryNo = 0;

		forecast_time curTime = forecastTime;
		const producer ECprod(242, 86, 242, "ECGEPSMTA");

		do
		{
			curTime = forecast_time(GetOriginTime(curTime.OriginDateTime(), 242), curTime.ValidDateTime());

			if (curTime.Step().Hours() > 131)
			{
				p = param("PROB-RR-4");  // EC(6h) RR>0.4mm
			}

			EC = Fetch(curTime, forecastLevel, p, forecast_type(kStatisticalProcessing), {itsECEPSGeom}, ECprod, false);
			tryNo++;
		} while (tryNo < 2 && !EC);

		if (!EC)
		{
			return;
		}
	}

	myThreadedLogger.Info("Fetching SmartMet");

	SmartMet = Fetch(forecastTime, forecastLevel, param("RRR-KGM2"), forecastType);

	if (!SmartMet)
	{
		return;
	}

	vector<double> result(myTargetInfo->Data().Size(), MissingDouble());

	const auto& MEPSdata = (MEPS) ? VEC(MEPS) : result;
	const auto& ECdata = (EC) ? VEC(EC) : result;
	const auto& SmartMetdata = VEC(SmartMet);

	auto& resultdata = VEC(myTargetInfo);

	// maxprob and minprob are used to limit the allowed range
	// of values

	// shortest forecast has these:
	double maxprob = 1.0;
	double minprob = 0.8;

	if (step > 130)
	{
		maxprob = 0.7;
		minprob = 0.1;
	}
	else if (step > 45)
	{
		maxprob = 0.8;
		minprob = 0.2;
	}
	else if (step > 24)
	{
		maxprob = 0.9;
		minprob = 0.4;
	}
	else if (step > 6)
	{
		minprob = 0.6;
	}

	// Minimum precipitation value to be accepted, mm/h
	const double RRlimit = 0.01;

	for (auto&& tup : zip_range(resultdata, MEPSdata, ECdata, SmartMetdata))
	{
		double& r = tup.get<0>();
		const double meps = tup.get<1>();
		const double ec = tup.get<2>();
		const double rr = tup.get<3>();

		if (step < 43)
		{
			r = meps;
		}
		else if (step >= 43 && step <= 46)
		{
			r = (meps + ec) * 0.5;
		}
		else
		{
			r = ec;
		}

		if (rr > RRlimit)
		{
			r = max(r, minprob);
		}

		r = min(r, maxprob);
	}

	// Produce area average over the neighboring grid points
	// x x x x x
	// x x x x x
	// x x o x x
	// x x x x x
	// x x x x x

	const himan::matrix<double> filter_kernel(5, 5, 1, MissingDouble(), 1 / 25.);

	const auto smoothened =
	    numerical_functions::Filter2D<double>(myTargetInfo->Data(), filter_kernel, itsConfiguration->UseCuda());

	myTargetInfo->Base()->data = move(smoothened);

	myThreadedLogger.Info(
	    fmt::format("[CPU] Missing values: {}/{}", myTargetInfo->Data().MissingCount(), myTargetInfo->Data().Size()));
}
<|endoftext|>


<commit_before>//
// Created by michael on 12.10.15.
//

#include <string>
#include <memory>
#include <Template/TemplateEngine.h>
#include <Template/Space/SpaceQuestTemplateFactory.h>
#include <World/Space/SpaceWorldModel.h>
#include <QuestModel/Space/ExploreRegionQuest.h>
#include "../catch.hpp"
#include "../Mock/TestQuestTemplate.h"
#include "../Mock/TestQuestTemplateFactory.h"

using namespace weave;
using namespace std;

TEST_CASE("Format check", "[template]") {
    shared_ptr<RandomStream> rs = make_shared<RandomStream>(42);
    TemplateEngine textEngine(rs, Directories(), FormatterType::TEXT);
    TemplateEngine htmlEngine(rs, Directories(), FormatterType::HTML);
    REQUIRE(textEngine.GetFormat() == FormatterType::TEXT);
    REQUIRE(htmlEngine.GetFormat() == FormatterType::HTML);
}

TEST_CASE("Template factory", "[template]") {
    shared_ptr<RandomStream> rs = make_shared<RandomStream>(42);
    TemplateEngine engine(rs, Directories(), FormatterType::TEXT);
    SpaceWorldModel world;
    QuestModel questModel;
    SECTION("No factories") {
        REQUIRE_THROWS_AS(engine.GetTemplatesForNewQuest(world, questModel), ContractFailedException);
    }

    SpaceQuestTemplateFactory *factory = new SpaceQuestTemplateFactory();
    engine.RegisterTemplateFactory(unique_ptr<SpaceQuestTemplateFactory>(factory));
    REQUIRE(factory->GetTemplateKeys().size() >= 1);

    SECTION("Checking the keys") {
        vector<string> keys = {"ExploreRegionQuest"};
        for (string key : keys) {
            bool hasKey = false;
            for (string templateKey : factory->GetTemplateKeys()) {
                if (key == templateKey) {
                    hasKey = true;
                    break;
                }
            }
            REQUIRE(hasKey);
        }
    }

    SECTION("Retrieve unknown key") {
        REQUIRE_THROWS_AS(factory->CreateTemplate("SecretTemplate"), ContractFailedException);
    }

    SECTION("Retrieving template for every key") {
        for (string templateKey : factory->GetTemplateKeys()) {
            auto temp = factory->CreateTemplate(templateKey);
            REQUIRE(temp.get() != nullptr);
        }
    }
}

TEST_CASE("Templates", "[template]") {
    uint64_t testSize = 100;
    shared_ptr<RandomStream> rs = make_shared<RandomStream>(42);
    TemplateEngine engine(rs, Directories(), FormatterType::TEXT);
    SpaceQuestTemplateFactory *factory = new SpaceQuestTemplateFactory();
    engine.RegisterTemplateFactory(unique_ptr<SpaceQuestTemplateFactory>(factory));

    SECTION("Checking properties size") {
        for (uint64_t i = 0; i < testSize; i++) {
            rs->Seed(i);
            for (string templateKey : factory->GetTemplateKeys()) {
                auto tp = factory->CreateTemplate(templateKey);
                INFO("Template Key: " + templateKey + ", Seed: " + to_string(i));
                REQUIRE(tp->GetProperties().size() > 0);
            }
        }
    }

    SECTION("Checking mandatory property exists") {
        for (uint64_t i = 0; i < testSize; i++) {
            rs->Seed(i);
            for (string templateKey : factory->GetTemplateKeys()) {
                auto tp = factory->CreateTemplate(templateKey);
                bool hasMandatoryProperty = false;
                for (auto property : tp->GetProperties()) {
                    if (property.IsMandatory()) {
                        hasMandatoryProperty = true;
                        break;
                    }
                }
                INFO("Template Key: " + templateKey + ", Seed: " + to_string(i));
                REQUIRE(hasMandatoryProperty);
            }
        }
    }

    SECTION("Checking property names") {
        for (uint64_t i = 0; i < testSize; i++) {
            rs->Seed(i);
            for (string templateKey : factory->GetTemplateKeys()) {
                auto tp = factory->CreateTemplate(templateKey);
                for (auto property : tp->GetProperties()) {
                    INFO("Template Key: " + templateKey + ", Property: " + property.GetName() + ", Seed: " +
                         to_string(i));
                    REQUIRE(!property.GetName().empty());
                }
            }
        }
    }

    SECTION("Checking property candidates") {
        WorldModel *worldModel = new SpaceWorldModel(rs);
        for (uint64_t i = 0; i < testSize; i++) {
            rs->Seed(i);
            for (string templateKey : factory->GetTemplateKeys()) {
                auto tp = factory->CreateTemplate(templateKey);
                for (auto property : tp->GetProperties()) {
                    auto candidates = tp->GetPropertyCandidates(property, *worldModel);
                    INFO("Template Key: " + templateKey + ", Property: " + property.GetName() + ", Seed: " +
                         to_string(i));
                    REQUIRE(candidates.size() > 0);
                }
            }
        }
    }

    SECTION("Checking model actions on empty model ") {
        WorldModel *worldModel = new SpaceWorldModel(rs);
        for (uint64_t i = 0; i < testSize; i++) {
            rs->Seed(i);
            for (string templateKey : factory->GetTemplateKeys()) {
                auto tp = factory->CreateTemplate(templateKey);
                for (auto property : tp->GetProperties()) {
                    auto candidates = tp->GetPropertyCandidates(property, *worldModel);
                    for (auto candidate : candidates) {
                        INFO("Template Key: " + templateKey + ", Property: " + property.GetName() + ", Seed: " +
                             to_string(i));
                        for (auto action : candidate.GetActions()) {
                            REQUIRE((action.GetActionType() == WorldActionType::CREATE ||
                                     action.GetActionType() == WorldActionType::UPDATE));
                        }
                        REQUIRE(candidate.GetValue().get() != nullptr);
                    }
                }
            }
        }
    }

    SECTION("Checking quest creation all candidates") {
        WorldModel *worldModel = new SpaceWorldModel(rs);
        for (uint64_t i = 0; i < testSize; i++) {
            rs->Seed(i);
            for (string templateKey : factory->GetTemplateKeys()) {
                auto tp = factory->CreateTemplate(templateKey);
                vector<QuestPropertyValue> questValues;
                for (auto property : tp->GetProperties()) {
                    auto candidates = tp->GetPropertyCandidates(property, *worldModel);
                    vector<WorldModelAction> actions;
                    for (auto candidate : candidates) {
                        questValues.push_back(QuestPropertyValue(property, candidate.GetValue()));
                        for (auto action : candidate.GetActions()) {
                            actions.push_back(action);
                        }
                    }
                    worldModel->Execute(actions);
                }

                INFO("Template Key: " + templateKey + ", Seed: " + to_string(i));
                try {
                    auto quest = tp->ToQuest(questValues);
                    REQUIRE(!quest->GetDescription().empty());
                    REQUIRE(!quest->GetTitle().empty());
                } catch (ContractFailedException ex) {
                    FAIL(ex.what());
                }
            }
        }
    }

    SECTION("Checking quest creation only mandatory candidates") {
        WorldModel *worldModel = new SpaceWorldModel(rs);
        for (uint64_t i = 0; i < testSize; i++) {
            rs->Seed(i);
            for (string templateKey : factory->GetTemplateKeys()) {
                auto tp = factory->CreateTemplate(templateKey);
                vector<QuestPropertyValue> questValues;
                for (auto property : tp->GetProperties()) {
                    if (!property.IsMandatory()) {
                        continue;
                    }
                    auto candidates = tp->GetPropertyCandidates(property, *worldModel);
                    vector<WorldModelAction> actions;
                    for (auto candidate : candidates) {
                        questValues.push_back(QuestPropertyValue(property, candidate.GetValue()));
                        for (auto action : candidate.GetActions()) {
                            actions.push_back(action);
                        }
                    }
                    worldModel->Execute(actions);
                }

                INFO("Template Key: " + templateKey + ", Seed: " + to_string(i));
                try {
                    auto quest = tp->ToQuest(questValues);
                    REQUIRE(!quest->GetDescription().empty());
                    REQUIRE(!quest->GetTitle().empty());
                } catch (ContractFailedException ex) {
                    FAIL(ex.what());
                }
            }
        }
    }

    SECTION("Quest descriptions") {
        SpaceWorldModel *worldModel = new SpaceWorldModel(rs);
        auto entity = worldModel->CreateLocation().GetEntity();

        vector<TemplateQuestProperty> properties;
        TemplateQuestProperty mandy(true, "mandy");
        TemplateQuestProperty sandy(true, "sandy");
        TemplateQuestProperty opti(false, "opti");
        TemplateQuestProperty peti(false, "peti");
        properties.push_back(mandy);
        properties.push_back(sandy);
        properties.push_back(opti);
        properties.push_back(peti);

        vector<TemplateQuestDescription> descriptions;
        vector<string> conditions;
        vector<QuestPropertyValue> propertyValues;

        SECTION("Normal quest description substitution") {
            conditions.push_back("mandy");
            conditions.push_back("sandy");
            descriptions.push_back(TemplateQuestDescription(conditions, "Why %mandy loves %sandy..."));
            TestQuestTemplate testTemplate(properties, descriptions);
            propertyValues.push_back(QuestPropertyValue(mandy, entity));
            propertyValues.push_back(QuestPropertyValue(sandy, entity));
            auto quest = testTemplate.ToQuest(propertyValues, "");
            string description = quest->GetDescription();
            REQUIRE_FALSE(description.empty());
            REQUIRE(description.find("mandy") == string::npos);
            REQUIRE(description.find("sandy") == string::npos);
        }

        SECTION("Impossible quest description substitution") {
            conditions.push_back("mandy");
            conditions.push_back("opti");
            descriptions.push_back(TemplateQuestDescription(conditions, "Why %mandy loves %opti..."));
            TestQuestTemplate testTemplate(properties, descriptions);
            propertyValues.push_back(QuestPropertyValue(mandy, entity));
            propertyValues.push_back(QuestPropertyValue(sandy, entity));
            REQUIRE_THROWS_AS(testTemplate.ToQuest(propertyValues, ""), ContractFailedException);
        }
    }

    SECTION("Read faulty template file") {
        Directories dirs;
        dirs.templateDirectory = "Test/Resources/";
        dirs.modDirectory = "../Test/Resources/";
        TemplateEngine tempEngine(rs, dirs, FormatterType::TEXT);
        TestQuestTemplateFactory *factory = new TestQuestTemplateFactory("missingAttribute.qt");
        tempEngine.RegisterTemplateFactory(unique_ptr<TestQuestTemplateFactory>(factory));
        REQUIRE_THROWS_AS(factory->GetTemplateKeys(), ContractFailedException);
    }
}

TEST_CASE("Directory Change", "[template]") {
    shared_ptr<RandomStream> rs = make_shared<RandomStream>(42);
    TemplateEngine engine(rs, Directories(), FormatterType::TEXT);
    SpaceWorldModel world;
    QuestModel questModel;
    unique_ptr<SpaceQuestTemplateFactory> factory(new SpaceQuestTemplateFactory());

    SECTION("Unknown dir pre reg") {
        Directories unknown;
        unknown.templateDirectory = "Test/Unknown/";
        unknown.modDirectory = "../Test/Unknown/";
        engine.ChangeDirectories(unknown);
        engine.RegisterTemplateFactory(move(factory));
        REQUIRE_THROWS_AS(engine.GetTemplatesForNewQuest(world, questModel), ContractFailedException);
    }

    SECTION("Unknown dir post reg") {
        engine.RegisterTemplateFactory(move(factory));
        Directories unknown;
        unknown.templateDirectory = "Test/Unknown/";
        unknown.modDirectory = "../Test/Unknown/";
        engine.ChangeDirectories(unknown);
        REQUIRE_THROWS_AS(engine.GetTemplatesForNewQuest(world, questModel), ContractFailedException);
    }

    SECTION("Pre register") {
        Directories unknown;
        unknown.templateDirectory = "Test/Resources/";
        unknown.modDirectory = "../Test/Resources/";
        engine.ChangeDirectories(unknown);
        engine.RegisterTemplateFactory(move(factory));
        REQUIRE(engine.GetTemplatesForNewQuest(world, questModel).size() > 0);
    }
}

TEST_CASE("Explore quest", "[quest]") {
    shared_ptr<RandomStream> rs = make_shared<RandomStream>(42);
    TemplateEngine engine(rs, Directories(), FormatterType::TEXT);
    SpaceQuestTemplateFactory *factory = new SpaceQuestTemplateFactory();
    engine.RegisterTemplateFactory(unique_ptr<SpaceQuestTemplateFactory>(factory));
    SpaceWorldModel world(rs);

    // Create a new explore region quest manually
    auto questTemplate = factory->CreateTemplate("ExploreRegionQuest");
    vector<QuestPropertyValue> questPropertyValues;
    TemplateQuestProperty property(true, "solarSystem");
    auto addSystemActions = world.CreateSolarSystem();
    world.Execute(addSystemActions);
    auto solarSystem = addSystemActions[addSystemActions.size() - 1].GetEntity();
    questPropertyValues.push_back(QuestPropertyValue(property, solarSystem));
    auto quest = questTemplate->ToQuest(questPropertyValues);

    SECTION("Exploration Ticks nothing") {
        QuestTickResult tickResult = quest->Tick(1, world);
        REQUIRE(tickResult.GetQuestChange().GetActionType() == QuestActionType::KEEP);
        REQUIRE(tickResult.GetWorldChanges().size() == 0);

        tickResult = quest->Tick(2, world);
        REQUIRE(tickResult.GetQuestChange().GetActionType() == QuestActionType::KEEP);
        REQUIRE(tickResult.GetWorldChanges().size() == 0);
    }

    SECTION("Exploration Tick partial") {
        MetaData metaData;
        metaData.SetValue(ExploreRegionQuest::metaDataMarker, 50);
        WorldModelAction metaDataAction(WorldActionType::KEEP, solarSystem, metaData);
        world.Execute({metaDataAction});

        QuestTickResult tickResult = quest->Tick(1, world);
        REQUIRE(tickResult.GetQuestChange().GetActionType() == QuestActionType::KEEP);
        REQUIRE(tickResult.GetWorldChanges().size() == 0);
    }

    SECTION("Exploration Tick success") {
        MetaData metaData;
        metaData.SetValue(ExploreRegionQuest::metaDataMarker, 100);
        WorldModelAction metaDataAction(WorldActionType::UPDATE, solarSystem, metaData);
        world.Execute({metaDataAction});

        QuestTickResult tickResult = quest->Tick(1, world);
        REQUIRE(tickResult.GetQuestChange().GetActionType() == QuestActionType::SUCCEED);
        REQUIRE(tickResult.GetWorldChanges().size() == 0);
    }
}

TEST_CASE("Scan planet quest", "[quest]") {
    shared_ptr<RandomStream> rs = make_shared<RandomStream>(42);
    TemplateEngine engine(rs, Directories(), FormatterType::TEXT);
    SpaceQuestTemplateFactory *factory = new SpaceQuestTemplateFactory();
    engine.RegisterTemplateFactory(unique_ptr<SpaceQuestTemplateFactory>(factory));
    SpaceWorldModel world(rs);

    // Create a new scan planet quest manually
    auto questTemplate = factory->CreateTemplate("ScanPlanetQuest");

    TemplateQuestProperty property(true, "planet");
    auto candidates = questTemplate->GetPropertyCandidates(property, world);

    // it has to create a new solar system, since the world is empty
    REQUIRE(candidates.size() == 1);
    REQUIRE(candidates[0].GetValue()->GetType() == "planet");
    REQUIRE(candidates[0].GetActions().size() > 2);

    auto addSystemActions = world.CreateSolarSystem();
    world.Execute(addSystemActions);
    int planetCount = 0;
    for (auto entity : world.GetEntities()) {
        if (entity->GetType() == "planet") {
            planetCount++;
        }
    }
    REQUIRE(planetCount > 3);

    candidates = questTemplate->GetPropertyCandidates(property, world);
    REQUIRE((candidates.size() - 1) == planetCount);
    for (uint64_t i = 1; i < candidates.size(); i++) {
        REQUIRE(candidates[i].GetValue()->GetType() == "planet");
        REQUIRE(candidates[i].GetActions().size() == 1);
    }
}<commit_msg>Check for empty entity value formatter<commit_after>//
// Created by michael on 12.10.15.
//

#include <string>
#include <memory>
#include <Template/TemplateEngine.h>
#include <Template/Space/SpaceQuestTemplateFactory.h>
#include <World/Space/SpaceWorldModel.h>
#include <QuestModel/Space/ExploreRegionQuest.h>
#include "../catch.hpp"
#include "../Mock/TestQuestTemplate.h"
#include "../Mock/TestQuestTemplateFactory.h"

using namespace weave;
using namespace std;

TEST_CASE("Format check", "[template]") {
    shared_ptr<RandomStream> rs = make_shared<RandomStream>(42);
    TemplateEngine textEngine(rs, Directories(), FormatterType::TEXT);
    TemplateEngine htmlEngine(rs, Directories(), FormatterType::HTML);
    REQUIRE(textEngine.GetFormat() == FormatterType::TEXT);
    REQUIRE(htmlEngine.GetFormat() == FormatterType::HTML);
}

TEST_CASE("QuestPropertyValue noexcept", "[template]") {
    TemplateQuestProperty property(false, "asd");
    QuestPropertyValue value(property, shared_ptr<WorldEntity>());
    REQUIRE(value.GetValueString(FormatterType::TEXT) == "");
    REQUIRE(value.GetValueString(FormatterType::HTML) == "");
    REQUIRE(value.GetValueString(shared_ptr<WorldEntity>(), true, FormatterType::TEXT) == "");
    REQUIRE(value.GetValueString(shared_ptr<WorldEntity>(), true, FormatterType::HTML) == "");
}

TEST_CASE("Template factory", "[template]") {
    shared_ptr<RandomStream> rs = make_shared<RandomStream>(42);
    TemplateEngine engine(rs, Directories(), FormatterType::TEXT);
    SpaceWorldModel world;
    QuestModel questModel;
    SECTION("No factories") {
        REQUIRE_THROWS_AS(engine.GetTemplatesForNewQuest(world, questModel), ContractFailedException);
    }

    SpaceQuestTemplateFactory *factory = new SpaceQuestTemplateFactory();
    engine.RegisterTemplateFactory(unique_ptr<SpaceQuestTemplateFactory>(factory));
    REQUIRE(factory->GetTemplateKeys().size() >= 1);

    SECTION("Checking the keys") {
        vector<string> keys = {"ExploreRegionQuest"};
        for (string key : keys) {
            bool hasKey = false;
            for (string templateKey : factory->GetTemplateKeys()) {
                if (key == templateKey) {
                    hasKey = true;
                    break;
                }
            }
            REQUIRE(hasKey);
        }
    }

    SECTION("Retrieve unknown key") {
        REQUIRE_THROWS_AS(factory->CreateTemplate("SecretTemplate"), ContractFailedException);
    }

    SECTION("Retrieving template for every key") {
        for (string templateKey : factory->GetTemplateKeys()) {
            auto temp = factory->CreateTemplate(templateKey);
            REQUIRE(temp.get() != nullptr);
        }
    }
}

TEST_CASE("Templates", "[template]") {
    uint64_t testSize = 100;
    shared_ptr<RandomStream> rs = make_shared<RandomStream>(42);
    TemplateEngine engine(rs, Directories(), FormatterType::TEXT);
    SpaceQuestTemplateFactory *factory = new SpaceQuestTemplateFactory();
    engine.RegisterTemplateFactory(unique_ptr<SpaceQuestTemplateFactory>(factory));

    SECTION("Checking properties size") {
        for (uint64_t i = 0; i < testSize; i++) {
            rs->Seed(i);
            for (string templateKey : factory->GetTemplateKeys()) {
                auto tp = factory->CreateTemplate(templateKey);
                INFO("Template Key: " + templateKey + ", Seed: " + to_string(i));
                REQUIRE(tp->GetProperties().size() > 0);
            }
        }
    }

    SECTION("Checking mandatory property exists") {
        for (uint64_t i = 0; i < testSize; i++) {
            rs->Seed(i);
            for (string templateKey : factory->GetTemplateKeys()) {
                auto tp = factory->CreateTemplate(templateKey);
                bool hasMandatoryProperty = false;
                for (auto property : tp->GetProperties()) {
                    if (property.IsMandatory()) {
                        hasMandatoryProperty = true;
                        break;
                    }
                }
                INFO("Template Key: " + templateKey + ", Seed: " + to_string(i));
                REQUIRE(hasMandatoryProperty);
            }
        }
    }

    SECTION("Checking property names") {
        for (uint64_t i = 0; i < testSize; i++) {
            rs->Seed(i);
            for (string templateKey : factory->GetTemplateKeys()) {
                auto tp = factory->CreateTemplate(templateKey);
                for (auto property : tp->GetProperties()) {
                    INFO("Template Key: " + templateKey + ", Property: " + property.GetName() + ", Seed: " +
                         to_string(i));
                    REQUIRE(!property.GetName().empty());
                }
            }
        }
    }

    SECTION("Checking property candidates") {
        WorldModel *worldModel = new SpaceWorldModel(rs);
        for (uint64_t i = 0; i < testSize; i++) {
            rs->Seed(i);
            for (string templateKey : factory->GetTemplateKeys()) {
                auto tp = factory->CreateTemplate(templateKey);
                for (auto property : tp->GetProperties()) {
                    auto candidates = tp->GetPropertyCandidates(property, *worldModel);
                    INFO("Template Key: " + templateKey + ", Property: " + property.GetName() + ", Seed: " +
                         to_string(i));
                    REQUIRE(candidates.size() > 0);
                }
            }
        }
    }

    SECTION("Checking model actions on empty model ") {
        WorldModel *worldModel = new SpaceWorldModel(rs);
        for (uint64_t i = 0; i < testSize; i++) {
            rs->Seed(i);
            for (string templateKey : factory->GetTemplateKeys()) {
                auto tp = factory->CreateTemplate(templateKey);
                for (auto property : tp->GetProperties()) {
                    auto candidates = tp->GetPropertyCandidates(property, *worldModel);
                    for (auto candidate : candidates) {
                        INFO("Template Key: " + templateKey + ", Property: " + property.GetName() + ", Seed: " +
                             to_string(i));
                        for (auto action : candidate.GetActions()) {
                            REQUIRE((action.GetActionType() == WorldActionType::CREATE ||
                                     action.GetActionType() == WorldActionType::UPDATE));
                        }
                        REQUIRE(candidate.GetValue().get() != nullptr);
                    }
                }
            }
        }
    }

    SECTION("Checking quest creation all candidates") {
        WorldModel *worldModel = new SpaceWorldModel(rs);
        for (uint64_t i = 0; i < testSize; i++) {
            rs->Seed(i);
            for (string templateKey : factory->GetTemplateKeys()) {
                auto tp = factory->CreateTemplate(templateKey);
                vector<QuestPropertyValue> questValues;
                for (auto property : tp->GetProperties()) {
                    auto candidates = tp->GetPropertyCandidates(property, *worldModel);
                    vector<WorldModelAction> actions;
                    for (auto candidate : candidates) {
                        questValues.push_back(QuestPropertyValue(property, candidate.GetValue()));
                        for (auto action : candidate.GetActions()) {
                            actions.push_back(action);
                        }
                    }
                    worldModel->Execute(actions);
                }

                INFO("Template Key: " + templateKey + ", Seed: " + to_string(i));
                try {
                    auto quest = tp->ToQuest(questValues);
                    REQUIRE(!quest->GetDescription().empty());
                    REQUIRE(!quest->GetTitle().empty());
                } catch (ContractFailedException ex) {
                    FAIL(ex.what());
                }
            }
        }
    }

    SECTION("Checking quest creation only mandatory candidates") {
        WorldModel *worldModel = new SpaceWorldModel(rs);
        for (uint64_t i = 0; i < testSize; i++) {
            rs->Seed(i);
            for (string templateKey : factory->GetTemplateKeys()) {
                auto tp = factory->CreateTemplate(templateKey);
                vector<QuestPropertyValue> questValues;
                for (auto property : tp->GetProperties()) {
                    if (!property.IsMandatory()) {
                        continue;
                    }
                    auto candidates = tp->GetPropertyCandidates(property, *worldModel);
                    vector<WorldModelAction> actions;
                    for (auto candidate : candidates) {
                        questValues.push_back(QuestPropertyValue(property, candidate.GetValue()));
                        for (auto action : candidate.GetActions()) {
                            actions.push_back(action);
                        }
                    }
                    worldModel->Execute(actions);
                }

                INFO("Template Key: " + templateKey + ", Seed: " + to_string(i));
                try {
                    auto quest = tp->ToQuest(questValues);
                    REQUIRE(!quest->GetDescription().empty());
                    REQUIRE(!quest->GetTitle().empty());
                } catch (ContractFailedException ex) {
                    FAIL(ex.what());
                }
            }
        }
    }

    SECTION("Quest descriptions") {
        SpaceWorldModel *worldModel = new SpaceWorldModel(rs);
        auto entity = worldModel->CreateLocation().GetEntity();

        vector<TemplateQuestProperty> properties;
        TemplateQuestProperty mandy(true, "mandy");
        TemplateQuestProperty sandy(true, "sandy");
        TemplateQuestProperty opti(false, "opti");
        TemplateQuestProperty peti(false, "peti");
        properties.push_back(mandy);
        properties.push_back(sandy);
        properties.push_back(opti);
        properties.push_back(peti);

        vector<TemplateQuestDescription> descriptions;
        vector<string> conditions;
        vector<QuestPropertyValue> propertyValues;

        SECTION("Normal quest description substitution") {
            conditions.push_back("mandy");
            conditions.push_back("sandy");
            descriptions.push_back(TemplateQuestDescription(conditions, "Why %mandy loves %sandy..."));
            TestQuestTemplate testTemplate(properties, descriptions);
            propertyValues.push_back(QuestPropertyValue(mandy, entity));
            propertyValues.push_back(QuestPropertyValue(sandy, entity));
            auto quest = testTemplate.ToQuest(propertyValues, "");
            string description = quest->GetDescription();
            REQUIRE_FALSE(description.empty());
            REQUIRE(description.find("mandy") == string::npos);
            REQUIRE(description.find("sandy") == string::npos);
        }

        SECTION("Impossible quest description substitution") {
            conditions.push_back("mandy");
            conditions.push_back("opti");
            descriptions.push_back(TemplateQuestDescription(conditions, "Why %mandy loves %opti..."));
            TestQuestTemplate testTemplate(properties, descriptions);
            propertyValues.push_back(QuestPropertyValue(mandy, entity));
            propertyValues.push_back(QuestPropertyValue(sandy, entity));
            REQUIRE_THROWS_AS(testTemplate.ToQuest(propertyValues, ""), ContractFailedException);
        }
    }

    SECTION("Read faulty template file") {
        Directories dirs;
        dirs.templateDirectory = "Test/Resources/";
        dirs.modDirectory = "../Test/Resources/";
        TemplateEngine tempEngine(rs, dirs, FormatterType::TEXT);
        TestQuestTemplateFactory *factory = new TestQuestTemplateFactory("missingAttribute.qt");
        tempEngine.RegisterTemplateFactory(unique_ptr<TestQuestTemplateFactory>(factory));
        REQUIRE_THROWS_AS(factory->GetTemplateKeys(), ContractFailedException);
    }
}

TEST_CASE("Directory Change", "[template]") {
    shared_ptr<RandomStream> rs = make_shared<RandomStream>(42);
    TemplateEngine engine(rs, Directories(), FormatterType::TEXT);
    SpaceWorldModel world;
    QuestModel questModel;
    unique_ptr<SpaceQuestTemplateFactory> factory(new SpaceQuestTemplateFactory());

    SECTION("Unknown dir pre reg") {
        Directories unknown;
        unknown.templateDirectory = "Test/Unknown/";
        unknown.modDirectory = "../Test/Unknown/";
        engine.ChangeDirectories(unknown);
        engine.RegisterTemplateFactory(move(factory));
        REQUIRE_THROWS_AS(engine.GetTemplatesForNewQuest(world, questModel), ContractFailedException);
    }

    SECTION("Unknown dir post reg") {
        engine.RegisterTemplateFactory(move(factory));
        Directories unknown;
        unknown.templateDirectory = "Test/Unknown/";
        unknown.modDirectory = "../Test/Unknown/";
        engine.ChangeDirectories(unknown);
        REQUIRE_THROWS_AS(engine.GetTemplatesForNewQuest(world, questModel), ContractFailedException);
    }

    SECTION("Pre register") {
        Directories unknown;
        unknown.templateDirectory = "Test/Resources/";
        unknown.modDirectory = "../Test/Resources/";
        engine.ChangeDirectories(unknown);
        engine.RegisterTemplateFactory(move(factory));
        REQUIRE(engine.GetTemplatesForNewQuest(world, questModel).size() > 0);
    }
}

TEST_CASE("Explore quest", "[quest]") {
    shared_ptr<RandomStream> rs = make_shared<RandomStream>(42);
    TemplateEngine engine(rs, Directories(), FormatterType::TEXT);
    SpaceQuestTemplateFactory *factory = new SpaceQuestTemplateFactory();
    engine.RegisterTemplateFactory(unique_ptr<SpaceQuestTemplateFactory>(factory));
    SpaceWorldModel world(rs);

    // Create a new explore region quest manually
    auto questTemplate = factory->CreateTemplate("ExploreRegionQuest");
    vector<QuestPropertyValue> questPropertyValues;
    TemplateQuestProperty property(true, "solarSystem");
    auto addSystemActions = world.CreateSolarSystem();
    world.Execute(addSystemActions);
    auto solarSystem = addSystemActions[addSystemActions.size() - 1].GetEntity();
    questPropertyValues.push_back(QuestPropertyValue(property, solarSystem));
    auto quest = questTemplate->ToQuest(questPropertyValues);

    SECTION("Exploration Ticks nothing") {
        QuestTickResult tickResult = quest->Tick(1, world);
        REQUIRE(tickResult.GetQuestChange().GetActionType() == QuestActionType::KEEP);
        REQUIRE(tickResult.GetWorldChanges().size() == 0);

        tickResult = quest->Tick(2, world);
        REQUIRE(tickResult.GetQuestChange().GetActionType() == QuestActionType::KEEP);
        REQUIRE(tickResult.GetWorldChanges().size() == 0);
    }

    SECTION("Exploration Tick partial") {
        MetaData metaData;
        metaData.SetValue(ExploreRegionQuest::metaDataMarker, 50);
        WorldModelAction metaDataAction(WorldActionType::KEEP, solarSystem, metaData);
        world.Execute({metaDataAction});

        QuestTickResult tickResult = quest->Tick(1, world);
        REQUIRE(tickResult.GetQuestChange().GetActionType() == QuestActionType::KEEP);
        REQUIRE(tickResult.GetWorldChanges().size() == 0);
    }

    SECTION("Exploration Tick success") {
        MetaData metaData;
        metaData.SetValue(ExploreRegionQuest::metaDataMarker, 100);
        WorldModelAction metaDataAction(WorldActionType::UPDATE, solarSystem, metaData);
        world.Execute({metaDataAction});

        QuestTickResult tickResult = quest->Tick(1, world);
        REQUIRE(tickResult.GetQuestChange().GetActionType() == QuestActionType::SUCCEED);
        REQUIRE(tickResult.GetWorldChanges().size() == 0);
    }
}

TEST_CASE("Scan planet quest", "[quest]") {
    shared_ptr<RandomStream> rs = make_shared<RandomStream>(42);
    TemplateEngine engine(rs, Directories(), FormatterType::TEXT);
    SpaceQuestTemplateFactory *factory = new SpaceQuestTemplateFactory();
    engine.RegisterTemplateFactory(unique_ptr<SpaceQuestTemplateFactory>(factory));
    SpaceWorldModel world(rs);

    // Create a new scan planet quest manually
    auto questTemplate = factory->CreateTemplate("ScanPlanetQuest");

    TemplateQuestProperty property(true, "planet");
    auto candidates = questTemplate->GetPropertyCandidates(property, world);

    // it has to create a new solar system, since the world is empty
    REQUIRE(candidates.size() == 1);
    REQUIRE(candidates[0].GetValue()->GetType() == "planet");
    REQUIRE(candidates[0].GetActions().size() > 2);

    auto addSystemActions = world.CreateSolarSystem();
    world.Execute(addSystemActions);
    int planetCount = 0;
    for (auto entity : world.GetEntities()) {
        if (entity->GetType() == "planet") {
            planetCount++;
        }
    }
    REQUIRE(planetCount > 3);

    candidates = questTemplate->GetPropertyCandidates(property, world);
    REQUIRE((candidates.size() - 1) == planetCount);
    for (uint64_t i = 1; i < candidates.size(); i++) {
        REQUIRE(candidates[i].GetValue()->GetType() == "planet");
        REQUIRE(candidates[i].GetActions().size() == 1);
    }
}<|endoftext|>