예제 #1
0
void Boids::MakeMoves( const GameState currentState, size_t teamIndex, std::vector<Move>& outMoves ) {
	const Team& team						= currentState.Teams[teamIndex];
	const glm::vec2 teamAvaragePosition		= CalculateAvaragePosition( currentState, teamIndex );
	
	// Choose a new apple for the team as its goal if the previous goal-apple was taken.
	if ( !currentState.IsTileWalkable( m_GoalTile ) || currentState.Board[m_GoalTile.y][m_GoalTile.x] != Tile::Apple ) {
		m_GoalTile		= currentState.FindClosestApple( teamAvaragePosition );		// TODO: Figure out another goal if there are no apples.
	}

	// Decide for each snake which direction it should move.
	for ( size_t snakeIndex = 0; snakeIndex < team.Snakes.size(); ++snakeIndex ) {
		const Snake& snake						= team.Snakes[snakeIndex];
		const glm::ivec2& snakeTile				= snake.Segments[0];
		const glm::vec2 snakePosition			= glm::vec2( snakeTile );

		// Calculate which moves the snake can make without dying this turn.		// TODO: Take into account that tails move.
		std::vector<Move> safeMoves;
		if ( currentState.IsTileWalkable( snakeTile + glm::ivec2( 0, -1 ) ) ) {
			safeMoves.push_back( Move::Up );
		}
		if ( currentState.IsTileWalkable( snakeTile + glm::ivec2( 0, 1 ) ) ) {
			safeMoves.push_back( Move::Down );
		}
		if ( currentState.IsTileWalkable( snakeTile + glm::ivec2( -1, 0 ) ) ) {
			safeMoves.push_back( Move::Left );
		}
		if ( currentState.IsTileWalkable( snakeTile + glm::ivec2( 1, 0 ) ) ) {
			safeMoves.push_back( Move::Right );
		}

		// If there is only one safe move it is chosen, and we continue to the next snake instead.
		if ( safeMoves.size() == 1 ) {
			outMoves[snakeIndex]		= safeMoves.front();
			continue;
		}

		// Calculate and accumulate the effect of each rule that makes up boids (plus some extra ones specialized for the application snake/nibbles).
		glm::vec2 newSnakeDirection		= glm::vec2( 0.0f );
		newSnakeDirection				+= RULE_FACTOR_GOAL					* GoalDirection( currentState, teamIndex, snakeIndex );
		newSnakeDirection				+= RULE_FACTOR_LOCAL_GOAL			* LocalGoalDirection( currentState, teamIndex, snakeIndex );
		newSnakeDirection				+= RULE_FACTOR_SEPERATION			* SeperationDirection( currentState, teamIndex, snakeIndex );
		if ( team.Snakes.size() > 1 ) {		// Only apply team-rules if the snake is not alone in the team.
			newSnakeDirection				+= RULE_FACTOR_COHESION				* CohesionDirection( currentState, teamIndex, snakeIndex );
			newSnakeDirection				+= RULE_FACTOR_ALIGNMENT			* AlignmentDirection( currentState, teamIndex, snakeIndex );
			newSnakeDirection				+= RULE_FACTOR_TEAM_SEPERATION		* TeamSeperationDirection( currentState, teamIndex, snakeIndex );
		}

		outMoves[snakeIndex]		= ChooseSafeMove( newSnakeDirection, outMoves[snakeIndex], safeMoves );
	}
}
예제 #2
0
glm::vec2 Boids::LocalGoalDirection( const GameState& gameState, const size_t teamIndex, const size_t snakeIndex ) const {
	const Team& team					= gameState.Teams[teamIndex];
	const Snake& snake					= team.Snakes[snakeIndex];
	const glm::vec2& snakePosition		= snake.Segments[0];

	// Find and calculate distance to closest apple.
	const glm::ivec2 closestApple				= gameState.FindClosestApple( snakePosition );
	const glm::vec2 vectorToClosestApple		= glm::vec2( closestApple ) - snakePosition;
	const float distanceToClosestAppleSqrd		= glm::dot( vectorToClosestApple, vectorToClosestApple );

	// Return direction to closest apple if it is within detection distance.
	if ( distanceToClosestAppleSqrd <= LOCAL_GOAL_DISTANCE * LOCAL_GOAL_DISTANCE ) {
		if ( distanceToClosestAppleSqrd != 0.0f ) {
			return vectorToClosestApple / distanceToClosestAppleSqrd;		// Direction to the local goal (closest apple), diminishes with distance.
		}
	}
	return glm::vec2( 0.0f );	// Apple not found (or on the same tile as the snake for some reason).
}