how last player is handled is wrong and gives me exceptions class NectoRewardFunction : public RewardFunction {
private:
	const Vec BLUE_GOAL = (CommonValues::BLUE_GOAL_BACK + CommonValues::BLUE_GOAL_CENTER) / 2;
	const Vec ORANGE_GOAL = (CommonValues::ORANGE_GOAL_BACK + CommonValues::ORANGE_GOAL_CENTER) / 2;

	float goal_w;
	float goal_dist_w;
	float goal_speed_bonus_w;
	float goal_dist_bonus_w;
	float demo_w;
	float dist_w;
	float align_w;
	float boost_gain_w;
	float boost_lose_w;
	float touch_grass_w;
	float touch_height_w;
	float touch_accel_w;

	GameState lastState;
	float stateQuality;
	std::vector<float> playerQualities;
	std::vector<float> rewards;

	// Local math functions
	static float Dist(const Vec& a, const Vec& b) {
		return (a - b).Length();
	}

	static float CosineSimilarity(const Vec& a, const Vec& b) {
		return a.Dot(b) / (a.Length() * b.Length());
	}

	std::pair<float, std::vector<float>> calculateStateQualities(const GameState& state) {
		Vec ballPos = state.ball.pos;

		float stateQuality = 0.5f * goal_dist_w * (std::exp(-Dist(ORANGE_GOAL, ballPos) / CommonValues::CAR_MAX_SPEED)
			- std::exp(-Dist(BLUE_GOAL, ballPos) / CommonValues::CAR_MAX_SPEED));

		std::vector<float> playerQualities(state.players.size(), 0.0f);
		for (size_t i = 0; i < state.players.size(); i++) {
			const auto& player = state.players[i];
			Vec pos = player.carState.pos;

			Vec toBall = ballPos - pos;
			Vec toOrangeGoal = CommonValues::ORANGE_GOAL_BACK - pos;
			Vec toBlueGoal = CommonValues::BLUE_GOAL_BACK - pos;

			float alignment = 0.5f * (CosineSimilarity(toBall, toOrangeGoal)
				- CosineSimilarity(toBall, toBlueGoal));
			if (player.team == Team::ORANGE) {
				alignment *= -1;
			}
			float liuDist = std::exp(-Dist(ballPos, pos) / 1410.0f);  // Max driving speed
			playerQualities[i] = (dist_w * liuDist + align_w * alignment);
		}

		return { stateQuality / 2, playerQualities };
	}

	void calculateRewards(const GameState& state) {
		auto [newStateQuality, newPlayerQualities] = calculateStateQualities(state);
		rewards.resize(state.players.size());

		for (size_t i = 0; i < state.players.size(); i++) {
			const auto& player = state.players[i];
			const auto& lastPlayer = lastState.players[i];

			rewards[i] = 0.0f;

			if (player.ballTouchedStep) {
				Vec currVel = state.ball.vel;
				Vec lastVel = lastState.ball.vel;

				rewards[i] += touch_height_w * state.ball.pos.z / CommonValues::CEILING_Z;
				rewards[i] += touch_accel_w * Dist(currVel, lastVel) / CommonValues::CAR_MAX_SPEED;
			}

			// Boost reward
			float boost_diff = std::sqrt(player.boostFraction) - std::sqrt(lastPlayer.boostFraction);
			rewards[i] += (boost_diff >= 0) ? boost_gain_w * boost_diff : boost_lose_w * boost_diff;

			rewards[i] -= player.carState.isOnGround * touch_grass_w;

			if (player.carState.isDemoed && !lastPlayer.carState.isDemoed) {
				rewards[i] -= demo_w / 2;
			}
			if (player.matchDemos > lastPlayer.matchDemos) {
				rewards[i] += demo_w / 2;
			}
		}

		size_t mid = state.players.size() / 2;

		for (size_t i = 0; i < state.players.size(); i++) {
			rewards[i] += newPlayerQualities[i] - playerQualities[i];
			if (i < mid) {
				rewards[i] += newStateQuality - stateQuality;
			}
			else {
				rewards[i] -= newStateQuality - stateQuality;
			}
		}

		int dBlue = state.scoreLine[0] - lastState.scoreLine[0];
		int dOrange = state.scoreLine[1] - lastState.scoreLine[1];
		if (dBlue > 0) {
			float goalSpeed = lastState.ball.vel.Length();
			std::vector<float> distances;
			for (size_t i = mid; i < state.players.size(); i++) {
				distances.push_back(Dist(state.players[i].carState.pos, lastState.ball.pos));
			}
			for (size_t i = mid; i < state.players.size(); i++) {
				rewards[i] = -goal_dist_bonus_w * (1 - std::exp(-distances[i - mid] / CommonValues::CAR_MAX_SPEED));
			}
			for (size_t i = 0; i < mid; i++) {
				rewards[i] = goal_w * dBlue + goal_dist_bonus_w * goalSpeed / CommonValues::BALL_MAX_SPEED;
			}
		}
		if (dOrange > 0) {
			float goalSpeed = lastState.ball.vel.Length();
			std::vector<float> distances;
			for (size_t i = 0; i < mid; i++) {
				distances.push_back(Dist(state.players[i].carState.pos, lastState.ball.pos));
			}
			for (size_t i = 0; i < mid; i++) {
				rewards[i] = -goal_dist_bonus_w * (1 - std::exp(-distances[i] / CommonValues::CAR_MAX_SPEED));
			}
			for (size_t i = mid; i < state.players.size(); i++) {
				rewards[i] = goal_w * dOrange + goal_dist_bonus_w * goalSpeed / CommonValues::BALL_MAX_SPEED;
			}
		}

		playerQualities = newPlayerQualities;
		stateQuality = newStateQuality;
	}

public:
	NectoRewardFunction(
		float goal_w = 10.0f,
		float goal_dist_w = 10.0f,
		float goal_speed_bonus_w = 2.5f,
		float goal_dist_bonus_w = 2.5f,
		float demo_w = 5.0f,
		float dist_w = 0.5f,
		float align_w = 0.5f,
		float boost_gain_w = 1.0f,
		float boost_lose_w = 0.7f,
		float touch_grass_w = 0.005f,
		float touch_height_w = 1.2f,
		float touch_accel_w = 0.25f
	) : goal_w(goal_w), goal_dist_w(goal_dist_w), goal_speed_bonus_w(goal_speed_bonus_w),
		goal_dist_bonus_w(goal_dist_bonus_w), demo_w(demo_w), dist_w(dist_w), align_w(align_w),
		boost_gain_w(boost_gain_w), boost_lose_w(boost_lose_w), touch_grass_w(touch_grass_w),
		touch_height_w(touch_height_w), touch_accel_w(touch_accel_w) {}

	void Reset(const GameState& initialState) override {
		lastState = initialState;
		std::tie(stateQuality, playerQualities) = calculateStateQualities(initialState);
		rewards.clear();
	}

	float GetReward(const PlayerData& player, const GameState& state, const Action& previousAction) override {
		if (rewards.empty()) {
			calculateRewards(state);
			lastState = state;
		}
		return rewards[player.carId];
	}
};