Agents with Healing Opportunity Will Be Terminated Directly if The `escape` Reduces activeAgents to the Number of `NUMBER_OF_SECONDARY_PRIZE_POOL_WINNERS` or Fewer
In each round, agents have the opportunity to either `escape` or `heal` before the `_requestForRandomness` function is called. However, the order of execution between these two functions is not specified, and anyone can be executed at any time just before `startNewRound`. Typically, this isn't an issue. However, the problem arises when there are only a few Active Agents left in the game.\\nOn one hand, the `heal` function requires that the number of `gameInfo.activeAgents` is greater than `NUMBER_OF_SECONDARY_PRIZE_POOL_WINNERS`.\\n```\\n    function heal(uint256[] calldata agentIds) external nonReentrant {\\n        _assertFrontrunLockIsOff();\\n//@audit If there are not enough activeAgents, heal is disabled\\n        if (gameInfo.activeAgents <= NUMBER_OF_SECONDARY_PRIZE_POOL_WINNERS) {\\n            revert HealingDisabled();\\n        }\\n```\\n\\nOn the other hand, the `escape` function will directly set the status of agents to "ESCAPE" and reduce the count of `gameInfo.activeAgents`.\\n```\\n   function escape(uint256[] calldata agentIds) external nonReentrant {\\n        _assertFrontrunLockIsOff();\\n\\n        uint256 agentIdsCount = agentIds.length;\\n        _assertNotEmptyAgentIdsArrayProvided(agentIdsCount);\\n\\n        uint256 activeAgents = gameInfo.activeAgents;\\n        uint256 activeAgentsAfterEscape = activeAgents - agentIdsCount;\\n        _assertGameIsNotOverAfterEscape(activeAgentsAfterEscape);\\n\\n        uint256 currentRoundAgentsAlive = agentsAlive();\\n\\n        uint256 prizePool = gameInfo.prizePool;\\n        uint256 secondaryPrizePool = gameInfo.secondaryPrizePool;\\n        uint256 reward;\\n        uint256[] memory rewards = new uint256[](agentIdsCount);\\n\\n        for (uint256 i; i < agentIdsCount; ) {\\n            uint256 agentId = agentIds[i];\\n            _assertAgentOwnership(agentId);\\n\\n            uint256 index = agentIndex(agentId);\\n            _assertAgentStatus(agents[index], agentId, AgentStatus.Active);\\n\\n            uint256 totalEscapeValue = prizePool / currentRoundAgentsAlive;\\n            uint256 rewardForPlayer = (totalEscapeValue * _escapeMultiplier(currentRoundAgentsAlive)) /\\n                ONE_HUNDRED_PERCENT_IN_BASIS_POINTS;\\n            rewards[i] = rewardForPlayer;\\n            reward += rewardForPlayer;\\n\\n            uint256 rewardToSecondaryPrizePool = (totalEscapeValue.unsafeSubtract(rewardForPlayer) *\\n                _escapeRewardSplitForSecondaryPrizePool(currentRoundAgentsAlive)) / ONE_HUNDRED_PERCENT_IN_BASIS_POINTS;\\n\\n            unchecked {\\n                prizePool = prizePool - rewardForPlayer - rewardToSecondaryPrizePool;\\n            }\\n            secondaryPrizePool += rewardToSecondaryPrizePool;\\n\\n            _swap({\\n                currentAgentIndex: index,\\n                lastAgentIndex: currentRoundAgentsAlive,\\n                agentId: agentId,\\n                newStatus: AgentStatus.Escaped\\n            });\\n\\n            unchecked {\\n                --currentRoundAgentsAlive;\\n                ++i;\\n            }\\n        }\\n\\n        // This is equivalent to\\n        // unchecked {\\n        //     gameInfo.activeAgents = uint16(activeAgentsAfterEscape);\\n        //     gameInfo.escapedAgents += uint16(agentIdsCount);\\n        // }\\n```\\n\\nThrerefore, if the `heal` function is invoked first then the corresponding Wounded Agents will be healed in function `fulfillRandomWords`. If the `escape` function is invoked first and the number of `gameInfo.activeAgents` becomes equal to or less than `NUMBER_OF_SECONDARY_PRIZE_POOL_WINNERS`, the `heal` function will be disable. This obviously violates the fairness of the game.\\nExample\\nConsider the following situation:\\nAfter Round N, there are 100 agents alive. And, 1 Active Agent wants to `escape` and 10 Wounded Agents want to `heal`.\\nRound N:\\nActive Agents: 51\\nWounded Agents: 49\\nHealing Agents: 0\\nAccording to the order of execution, there are two situations. Please note that the result is calculated only after `_healRequestFulfilled`, so therer are no new wounded or dead agents\\nFirst, invoking `escape` before `heal`. `heal` is disable and all Wounded Agents are killed because there are not enough Active Agents.\\nRound N+1:\\nActive Agents: 50\\nWounded Agents: 0\\nHealing Agents: 0\\nSecond, invoking `heal` before `escape`. Suppose that `heal` saves 5 agents, and we got:\\nRound N+1:\\nActive Agents: 55\\nWounded Agents: 39\\nHealing Agents: 0\\nObviously, different execution orders lead to drastically different outcomes, which affects the fairness of the game.
It is advisable to ensure that the `escape` function is always called after the `heal` function in every round. This guarantees that every wounded agent has the opportunity to `heal` themselves when there are a sufficient number of `activeAgents` at the start of each round. This approach can enhance fairness and gameplay balance.
If some Active Agents choose to escape, causing the count of `activeAgents` to become equal to or less than `NUMBER_OF_SECONDARY_PRIZE_POOL_WINNERS`, the Wounded Agents will lose their final chance to heal themselves.\\nThis situation can significantly impact the game's fairness. The Wounded Agents would have otherwise had the opportunity to heal themselves and continue participating in the game. However, the escape of other agents leads to their immediate termination, depriving them of that chance.
```\\n    function heal(uint256[] calldata agentIds) external nonReentrant {\\n        _assertFrontrunLockIsOff();\\n//@audit If there are not enough activeAgents, heal is disabled\\n        if (gameInfo.activeAgents <= NUMBER_OF_SECONDARY_PRIZE_POOL_WINNERS) {\\n            revert HealingDisabled();\\n        }\\n```\\n