// TODO: UNTESTED
const MoveTuple Player_Kiter::getMoveTuple(GameState & state, const MoveArray & moves)
{
	// the tuple, we will generate this on the fly
	MoveTuple tuple(0);
	
	for (IDType u(0); u<moves.numUnits(); ++u)
	{
		bool foundAction						(false);
		IDType actionMoveIndex					(0);
		IDType furthestMoveIndex				(0);
		size_t furthestMoveDist					(0);
		IDType closestMoveIndex					(0);
		int actionDistance						(std::numeric_limits<int>::max());
		unsigned long long closestMoveDist		(std::numeric_limits<unsigned long long>::max());

		const Unit & ourUnit					(state.getUnit(_playerID, u));
		const Unit & closestUnit				(ourUnit.canHeal() ? state.getClosestOurUnit(_playerID, u) : state.getClosestEnemyUnit(_playerID, u));

		for (IDType m(0); m<moves.numMoves(u); ++m)
		{
			const Move move						(moves.getMove(u, m));
				
			if (move.type() == MoveTypes::ATTACK)
			{
				const Unit & target				(state.getUnit(state.getEnemy(move.player()), move._moveIndex));
				PositionType dist				(ourUnit.distSq(target, state.getTime()));

				if (dist < actionDistance)
				{
					actionDistance = dist;
					actionMoveIndex = m;
					foundAction = true;
				}
			}
			else if (move.type() == MoveTypes::HEAL)
			{
				const Unit & target				(state.getUnit(move.player(), move._moveIndex));
				PositionType dist				(ourUnit.distSq(target, state.getTime()));

				if (dist < actionDistance)
				{
					actionDistance = dist;
					actionMoveIndex = m;
					foundAction = true;
				}
			}
			else if (move.type() == MoveTypes::MOVE)
			{
				Position ourDest				(ourUnit.x() + Search::Constants::Move_Dir[move._moveIndex][0], 
												 ourUnit.y() + Search::Constants::Move_Dir[move._moveIndex][1]);
				size_t dist						(closestUnit.distSq(ourDest, state.getTime()));

				if (dist > furthestMoveDist)
				{
					furthestMoveDist = dist;
					furthestMoveIndex = m;
				}

				if (dist < closestMoveDist)
				{
					closestMoveDist = dist;
					closestMoveIndex = m;
				}
			}
		}

		// the move we will be returning
		size_t bestMoveIndex(0);

		// if we have an attack move we will use that one
		if (foundAction)
		{
			bestMoveIndex = actionMoveIndex;
		}
		// otherwise use the closest move to the opponent
		else
		{
			// if we are in attack range of the unit, back up
			if (ourUnit.canAttackTarget(closestUnit, state.getTime()))
			{
				bestMoveIndex = furthestMoveIndex;
			}
			// otherwise get back into the fight
			else
			{
				bestMoveIndex = closestMoveIndex;
			}
		}
			
		// update the tuple calculation
		tuple += bestMoveIndex * moves.getProduct(u);
	}

	return tuple;
}
const MoveTuple Player_AttackClosest::getMoveTuple(GameState & state, const MoveArray & moves)
{
	MoveTuple tuple(0);

	for (IDType u(0); u<moves.numUnits(); ++u)
	{
		bool foundAction					(false);
		size_t actionMoveIndex				(0);
		size_t closestMoveIndex				(0);
		unsigned long long actionDistance	(std::numeric_limits<unsigned long long>::max());
		unsigned long long closestMoveDist	(std::numeric_limits<unsigned long long>::max());

		const Unit & ourUnit				(state.getUnit(_playerID, u));
		const Unit & closestUnit			(ourUnit.canHeal() ? state.getClosestOurUnit(_playerID, u) : state.getClosestEnemyUnit(_playerID, u));

		for (size_t m(0); m<moves.numMoves(u); ++m)
		{
			const Move move					(moves.getMove(u, m));
				
			if (move.type() == MoveTypes::ATTACK)
			{
				const Unit & target			(state.getUnit(state.getEnemy(move.player()), move._moveIndex));
				size_t dist					(ourUnit.distSq(target, state.getTime()));

				if (dist < actionDistance)
				{
					actionDistance = dist;
					actionMoveIndex = m;
					foundAction = true;
				}
			}
			if (move.type() == MoveTypes::HEAL)
			{
				const Unit & target			(state.getUnit(move.player(), move._moveIndex));
				size_t dist					(ourUnit.distSq(target, state.getTime()));

				if (dist < actionDistance)
				{
					actionDistance = dist;
					actionMoveIndex = m;
					foundAction = true;
				}
			}
			else if (move.type() == MoveTypes::RELOAD)
			{
				if (ourUnit.canAttackTarget(closestUnit, state.getTime()))
				{
					closestMoveIndex = m;
					break;
				}
			}
			else if (move.type() == MoveTypes::MOVE)
			{
				Position ourDest			(ourUnit.x() + Search::Constants::Move_Dir[move._moveIndex][0], 
											 ourUnit.y() + Search::Constants::Move_Dir[move._moveIndex][1]);
				size_t dist					(closestUnit.distSq(ourDest, state.getTime()));

				if (dist < closestMoveDist)
				{
					closestMoveDist = dist;
					closestMoveIndex = m;
				}
			}
		}

		size_t bestMoveIndex(foundAction ? actionMoveIndex : closestMoveIndex);

		tuple += bestMoveIndex * moves.getProduct(u);
	}

	return tuple;
}
const MoveTuple Player_NOK_AttackDPS::getMoveTuple(GameState & state, const MoveArray & moves)
{
	MoveTuple tuple(0);

	IDType player(moves.getMove(0,0).player());
	IDType enemy(state.getEnemy(player));

	Array<int, Search::Constants::Max_Units> hpRemaining;
	if (state.numUnits(enemy) > Search::Constants::Max_Units) 
	{
		hpRemaining.resize(state.numUnits(enemy));
	}

	for (IDType u(0); u<state.numUnits(enemy); ++u)
	{
		hpRemaining[u] = state.getUnit(enemy,u).currentHP();
	}

	for (IDType u(0); u<moves.numUnits(); ++u)
	{
		bool foundAction						(false);
		size_t actionMoveIndex					(0);
		double actionHighestDPS					(0);
		size_t closestMoveIndex					(0);
		unsigned long long closestMoveDist		(std::numeric_limits<unsigned long long>::max());
		
		const Unit & ourUnit				(state.getUnit(_playerID, u));
		const Unit & closestUnit			(ourUnit.canHeal() ? state.getClosestOurUnit(_playerID, u) : state.getClosestEnemyUnit(_playerID, u));

		for (size_t m(0); m<moves.numMoves(u); ++m)
		{
			const Move move						(moves.getMove(u, m));
				
			if ((move.type() == MoveTypes::ATTACK) && (hpRemaining[move._moveIndex] > 0))
			{
				const Unit & target				(state.getUnit(state.getEnemy(move.player()), move._moveIndex));
				double dpsHPValue =				(target.dpf() / hpRemaining[move._moveIndex]);

				if (dpsHPValue > actionHighestDPS)
				{
					actionHighestDPS = dpsHPValue;
					actionMoveIndex = m;
					foundAction = true;
				}
			}
			else if (move.type() == MoveTypes::HEAL)
			{
				const Unit & target				(state.getUnit(move.player(), move._moveIndex));
				double dpsHPValue =				(target.dpf() / hpRemaining[move._moveIndex]);

				if (dpsHPValue > actionHighestDPS)
				{
					actionHighestDPS = dpsHPValue;
					actionMoveIndex = m;
					foundAction = true;
				}
			}
			else if (move.type() == MoveTypes::RELOAD)
			{
				if (ourUnit.canAttackTarget(closestUnit, state.getTime()))
				{
					closestMoveIndex = m;
					break;
				}
			}
			else if (move.type() == MoveTypes::MOVE)
			{
				Position ourDest				(ourUnit.x() + Search::Constants::Move_Dir[move._moveIndex][0], 
												 ourUnit.y() + Search::Constants::Move_Dir[move._moveIndex][1]);
				size_t dist						(closestUnit.distSq(ourDest, state.getTime()));

				if (dist < closestMoveDist)
				{
					closestMoveDist = dist;
					closestMoveIndex = m;
				}
			}
		}

		size_t bestMoveIndex(foundAction ? actionMoveIndex : closestMoveIndex);

		Move theMove(moves.getMove(u, actionMoveIndex));
		if (theMove.type() == MoveTypes::ATTACK)
		{
			hpRemaining[theMove.index()] -= state.getUnit(player, theMove.unit()).damage();
		}
			
		tuple += bestMoveIndex * moves.getProduct(u);
	}

	return tuple;
}