<commit_before>
/* <NAME>
Aug 15 2015 */

#include "npc_enemy_worker.h"

void Hostile_NPC_Worker::update(World & world, map<string, shared_ptr<Character>> & actors)
{
	if (!fortress_planned)
	{
		plan_fortress();
		fortress_planned = true; // only set the flag after plans have been made

		for (int i = 0; i < 10000; ++i) // cheating here to start the NPC off with materials
		{
			// start off with some materials
			this->add(Craft::make(C::STONE_ID));
		}
	}

	// for each objective
	for (deque<Objective>::iterator objective_it = objectives.begin();
		objective_it != objectives.end();)
	{
		// check if the objective is a construction objective
		if (objective_it->verb == C::AI_OBJECTIVE_CONSTRUCT)
		{
			// if we are at the destination
			if (x == objective_it->objective_x && y == objective_it->objective_y)
			{
				// if the surface already exists, erase the objective and continue
				if (world.room_at(x, y, z)->has_surface(objective_it->direction))
				{
					// erase() returns the next iterator
					objective_it = objectives.erase(objective_it);

					// next iteration
					continue;
				}

				// check if there is a tree in the way
				if (world.room_at(x, y, z)->contains_item(C::TREE_ID))
				{
					// tree needs chopping - just simulate it for now
					world.room_at(x, y, z)->remove_item(C::TREE_ID);
					return;
				}

				// construct the surface
				// If the objective's modifier is true (in this case meaning the wall will have a door), compare
				// the results of constructing a surface with a door. If the objective's modifier is false,
				// compare the results of constructing just a surface

				// HERE ****** The should also take into account if the adjacent room has an opposite wall with a door. Then
				// this room should have a door there as well.
				if (((objective_it->modifier) ?
					(construct_surface_with_door(objective_it->material, objective_it->direction, objective_it->material, world).find("You construct a ")) :
					(construct_surface(objective_it->material, objective_it->direction, world).find("You construct a ")))
					!= string::npos)
				{
					// if successful, erase the objective and return
					objectives.erase(objective_it);
					return;
				}
			}
			// we are not at the destination, attempt to pathfind to it
			else if (pathfind(objective_it->objective_x, objective_it->objective_y, world))
			{
				return;
			}
			// we could not pathfind to the destination, test if it is out of sight range
			else if (R::diagonal_distance(x, y, objective_it->objective_x, objective_it->objective_y) > C::VIEW_DISTANCE)
			{
				// The location is out of sight range. Move in the direction of the destination.

				if (x > objective_it->objective_x && y > objective_it->objective_y) // northwest
				{
					// if the target is out of view but inline with any part of the current visible area,
					// don't pathfind to corner; pathfind to the edge of the visible area that
					// is inline with the destination

					if (pathfind(
						(((x - objective_it->objective_x) <= C::VIEW_DISTANCE) ? (objective_it->objective_x) : (x - C::VIEW_DISTANCE)),
						(((y - objective_it->objective_y) <= C::VIEW_DISTANCE) ? (objective_it->objective_y) : (y - C::VIEW_DISTANCE)), world))
					{
						return;
					}
				}

				if (x > objective_it->objective_x && y < objective_it->objective_y) // northeast
				{
					if (pathfind(
						(((x - objective_it->objective_x) <= C::VIEW_DISTANCE) ? (objective_it->objective_x) : (x - C::VIEW_DISTANCE)),
						(((objective_it->objective_y - y) <= C::VIEW_DISTANCE) ? (objective_it->objective_y) : (y + C::VIEW_DISTANCE)), world))
					{
						return;
					}
				}

				if (x < objective_it->objective_x && y > objective_it->objective_y) // southwest
				{
					if (pathfind(
						(((objective_it->objective_x - x) <= C::VIEW_DISTANCE) ? (objective_it->objective_x) : (x + C::VIEW_DISTANCE)),
						(((y - objective_it->objective_y) <= C::VIEW_DISTANCE) ? (objective_it->objective_y) : (y - C::VIEW_DISTANCE)), world))
					{
						return;
					}
				}

				if (x < objective_it->objective_x && y < objective_it->objective_y) // southeast
				{
					if (pathfind(
						(((objective_it->objective_x - x) <= C::VIEW_DISTANCE) ? (objective_it->objective_x) : (x + C::VIEW_DISTANCE)),
						(((objective_it->objective_y - y) <= C::VIEW_DISTANCE) ? (objective_it->objective_y) : (y + C::VIEW_DISTANCE)), world))
					{
						return;
					}
				}

				// execution reaches here if a diagonal movment failed or the target is directly n/e/s/w

				if (x > objective_it->objective_x) // north
				{
					if (pathfind(x - C::VIEW_DISTANCE, y, world)) { return; }
				}

				if (x < objective_it->objective_x) // south
				{
					if (pathfind(x + C::VIEW_DISTANCE, y, world)) { return; }
				}

				if (y > objective_it->objective_y) // west
				{
					if (pathfind(x, y - C::VIEW_DISTANCE, world)) { return; }
				}

				if (y < objective_it->objective_y) // east
				{
					if (pathfind(x, y + C::VIEW_DISTANCE, world)) { return; }
				}
			}
		}

		// next objective
		++objective_it;
	}
}

void Hostile_NPC_Worker::plan_fortress()
{
	// generate the x and y dimensions of the fortress
	const int FORTRESS_HEIGHT = R::random_int_from(C::FORTRESS_MIN_X, C::FORTRESS_MAX_X),
		FORTRESS_WIDTH = R::random_int_from(C::FORTRESS_MIN_Y, C::FORTRESS_MAX_Y);

	int fortress_x = 0, fortress_y = 0;

	// generate the fortress coordinates until the center of the fortress is on land
	do
	{
		fortress_x = R::random_int_from(0, C::WORLD_X_DIMENSION);
		fortress_y = R::random_int_from(0, C::WORLD_Y_DIMENSION);
	} while (R::euclidean_distance(C::WORLD_X_DIMENSION / 2, C::WORLD_Y_DIMENSION / 2,
		fortress_x + (FORTRESS_HEIGHT / 2),
		fortress_y + (FORTRESS_WIDTH / 2)) >= C::WORLD_X_DIMENSION * 0.45);


	// the first partition is the size of the fortress 
	vector<Partition> partitions;
	partitions.push_back(Partition(fortress_x, fortress_y, FORTRESS_HEIGHT, FORTRESS_WIDTH));

	// flag whether any changes were made
	bool partition_divided_on_last_pass;

	do
	{
		// reset flag
		partition_divided_on_last_pass = false;

		// for each partition
		for (int i = 0; i < partitions.size(); ++i)
		{
			// draw a 50/50 chance of attemping a horizontal or vertical split
			if (R::random_int_from(0, 1) == 1)
			{
				// horizontal split

				// if the partition is large enough to split
				if (partitions[i].height > 5)
				{
					partition_divided_on_last_pass = true; // a split is about to be made

					// calculate the amount to remove from the existing partition
					const int split = R::random_int_from(C::FORTRESS_PARTITION_MIN_SIZE, partitions[i].height - C::FORTRESS_PARTITION_MIN_SIZE);

					// remove the amount from the existing partition
					partitions[i].height -= split;

					// Create a new partition, add it to the vector. The x position is the current partition's x location 
					// plus the current partition's height. The y position is the same as the current partition.<commit_msg>fix signed/unsigned mismatch compiler warning<commit_after>
/* <NAME>
Aug 15 2015 */

#include "npc_enemy_worker.h"

void Hostile_NPC_Worker::update(World & world, map<string, shared_ptr<Character>> & actors)
{
	if (!fortress_planned)
	{
		plan_fortress();
		fortress_planned = true; // only set the flag after plans have been made

		for (int i = 0; i < 10000; ++i) // cheating here to start the NPC off with materials
		{
			// start off with some materials
			this->add(Craft::make(C::STONE_ID));
		}
	}

	// for each objective
	for (deque<Objective>::iterator objective_it = objectives.begin();
		objective_it != objectives.end();)
	{
		// check if the objective is a construction objective
		if (objective_it->verb == C::AI_OBJECTIVE_CONSTRUCT)
		{
			// if we are at the destination
			if (x == objective_it->objective_x && y == objective_it->objective_y)
			{
				// if the surface already exists, erase the objective and continue
				if (world.room_at(x, y, z)->has_surface(objective_it->direction))
				{
					// erase() returns the next iterator
					objective_it = objectives.erase(objective_it);

					// next iteration
					continue;
				}

				// check if there is a tree in the way
				if (world.room_at(x, y, z)->contains_item(C::TREE_ID))
				{
					// tree needs chopping - just simulate it for now
					world.room_at(x, y, z)->remove_item(C::TREE_ID);
					return;
				}

				// construct the surface
				// If the objective's modifier is true (in this case meaning the wall will have a door), compare
				// the results of constructing a surface with a door. If the objective's modifier is false,
				// compare the results of constructing just a surface

				// HERE ****** The should also take into account if the adjacent room has an opposite wall with a door. Then
				// this room should have a door there as well.
				if (((objective_it->modifier) ?
					(construct_surface_with_door(objective_it->material, objective_it->direction, objective_it->material, world).find("You construct a ")) :
					(construct_surface(objective_it->material, objective_it->direction, world).find("You construct a ")))
					!= string::npos)
				{
					// if successful, erase the objective and return
					objectives.erase(objective_it);
					return;
				}
			}
			// we are not at the destination, attempt to pathfind to it
			else if (pathfind(objective_it->objective_x, objective_it->objective_y, world))
			{
				return;
			}
			// we could not pathfind to the destination, test if it is out of sight range
			else if (R::diagonal_distance(x, y, objective_it->objective_x, objective_it->objective_y) > C::VIEW_DISTANCE)
			{
				// The location is out of sight range. Move in the direction of the destination.

				if (x > objective_it->objective_x && y > objective_it->objective_y) // northwest
				{
					// if the target is out of view but inline with any part of the current visible area,
					// don't pathfind to corner; pathfind to the edge of the visible area that
					// is inline with the destination

					if (pathfind(
						(((x - objective_it->objective_x) <= C::VIEW_DISTANCE) ? (objective_it->objective_x) : (x - C::VIEW_DISTANCE)),
						(((y - objective_it->objective_y) <= C::VIEW_DISTANCE) ? (objective_it->objective_y) : (y - C::VIEW_DISTANCE)), world))
					{
						return;
					}
				}

				if (x > objective_it->objective_x && y < objective_it->objective_y) // northeast
				{
					if (pathfind(
						(((x - objective_it->objective_x) <= C::VIEW_DISTANCE) ? (objective_it->objective_x) : (x - C::VIEW_DISTANCE)),
						(((objective_it->objective_y - y) <= C::VIEW_DISTANCE) ? (objective_it->objective_y) : (y + C::VIEW_DISTANCE)), world))
					{
						return;
					}
				}

				if (x < objective_it->objective_x && y > objective_it->objective_y) // southwest
				{
					if (pathfind(
						(((objective_it->objective_x - x) <= C::VIEW_DISTANCE) ? (objective_it->objective_x) : (x + C::VIEW_DISTANCE)),
						(((y - objective_it->objective_y) <= C::VIEW_DISTANCE) ? (objective_it->objective_y) : (y - C::VIEW_DISTANCE)), world))
					{
						return;
					}
				}

				if (x < objective_it->objective_x && y < objective_it->objective_y) // southeast
				{
					if (pathfind(
						(((objective_it->objective_x - x) <= C::VIEW_DISTANCE) ? (objective_it->objective_x) : (x + C::VIEW_DISTANCE)),
						(((objective_it->objective_y - y) <= C::VIEW_DISTANCE) ? (objective_it->objective_y) : (y + C::VIEW_DISTANCE)), world))
					{
						return;
					}
				}

				// execution reaches here if a diagonal movment failed or the target is directly n/e/s/w

				if (x > objective_it->objective_x) // north
				{
					if (pathfind(x - C::VIEW_DISTANCE, y, world)) { return; }
				}

				if (x < objective_it->objective_x) // south
				{
					if (pathfind(x + C::VIEW_DISTANCE, y, world)) { return; }
				}

				if (y > objective_it->objective_y) // west
				{
					if (pathfind(x, y - C::VIEW_DISTANCE, world)) { return; }
				}

				if (y < objective_it->objective_y) // east
				{
					if (pathfind(x, y + C::VIEW_DISTANCE, world)) { return; }
				}
			}
		}

		// next objective
		++objective_it;
	}
}

void Hostile_NPC_Worker::plan_fortress()
{
	// generate the x and y dimensions of the fortress
	const int FORTRESS_HEIGHT = R::random_int_from(C::FORTRESS_MIN_X, C::FORTRESS_MAX_X),
		FORTRESS_WIDTH = R::random_int_from(C::FORTRESS_MIN_Y, C::FORTRESS_MAX_Y);

	int fortress_x = 0, fortress_y = 0;

	// generate the fortress coordinates until the center of the fortress is on land
	do
	{
		fortress_x = R::random_int_from(0, C::WORLD_X_DIMENSION);
		fortress_y = R::random_int_from(0, C::WORLD_Y_DIMENSION);
	} while (R::euclidean_distance(C::WORLD_X_DIMENSION / 2, C::WORLD_Y_DIMENSION / 2,
		fortress_x + (FORTRESS_HEIGHT / 2),
		fortress_y + (FORTRESS_WIDTH / 2)) >= C::WORLD_X_DIMENSION * 0.45);


	// the first partition is the size of the fortress 
	vector<Partition> partitions;
	partitions.push_back(Partition(fortress_x, fortress_y, FORTRESS_HEIGHT, FORTRESS_WIDTH));

	// flag whether any changes were made
	bool partition_divided_on_last_pass;

	do
	{
		// reset flag
		partition_divided_on_last_pass = false;

		// for each partition
		for (unsigned i = 0; i < partitions.size(); ++i)
		{
			// draw a 50/50 chance of attemping a horizontal or vertical split
			if (R::random_int_from(0, 1) == 1)
			{
				// horizontal split

				// if the partition is large enough to split
				if (partitions[i].height > 5)
				{
					partition_divided_on_last_pass = true; // a split is about to be made

					// calculate the amount to remove from the existing partition
					const int split = R::random_int_from(C::FORTRESS_PARTITION_MIN_SIZE, partitions[i].height - C::FORTRESS_PARTITION_MIN_SIZE);

					// remove the amount from the existing partition
					partitions[i].height -= split;

					// Create a new partition, add it to the vector. The x position is the current partition's x location 
					// plus the current partition's height. The y position is the same as the current partition.
{'fraction_non_alphanumeric': 0.12450945415626115, 'fraction_numerical': 0.004566535854441669, 'mean_word_length': 3.216606498194946, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 123, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1350170', 'n_tokens_mistral': 5685, 'n_tokens_neox': 4627, 'n_words': 1293}