static void shuffle( V& vector ) {
#ifdef CATCH_CPP14_OR_GREATER
            std::shuffle( vector.begin(), vector.end(), RandomNumberGenerator() );
#else
            std::random_shuffle( vector.begin(), vector.end(), RandomNumberGenerator() );
#endif
        }
Ejemplo n.º 2
0
void BinarySwitch::build(unsigned start, bool hardStart, unsigned end)
{
    if (BinarySwitchInternal::verbose)
        dataLog("Building with start = ", start, ", hardStart = ", hardStart, ", end = ", end, "\n");

    auto append = [&] (const BranchCode& code) {
        if (BinarySwitchInternal::verbose)
            dataLog("==> ", code, "\n");
        m_branches.append(code);
    };
    
    unsigned size = end - start;
    
    RELEASE_ASSERT(size);
    
    // This code uses some random numbers to keep things balanced. It's important to keep in mind
    // that this does not improve average-case throughput under the assumption that all cases fire
    // with equal probability. It just ensures that there will not be some switch structure that
    // when combined with some input will always produce pathologically good or pathologically bad
    // performance.
    
    const unsigned leafThreshold = 3;
    
    if (size <= leafThreshold) {
        if (BinarySwitchInternal::verbose)
            dataLog("It's a leaf.\n");
        
        // It turns out that for exactly three cases or less, it's better to just compare each
        // case individually. This saves 1/6 of a branch on average, and up to 1/3 of a branch in
        // extreme cases where the divide-and-conquer bottoms out in a lot of 3-case subswitches.
        //
        // This assumes that we care about the cost of hitting some case more than we care about
        // bottoming out in a default case. I believe that in most places where we use switch
        // statements, we are more likely to hit one of the cases than we are to fall through to
        // default. Intuitively, if we wanted to improve the performance of default, we would
        // reduce the value of leafThreshold to 2 or even to 1. See below for a deeper discussion.
        
        bool allConsecutive = false;
        
        if ((hardStart || (start && m_cases[start - 1].value == m_cases[start].value - 1))
            && start + size < m_cases.size()
            && m_cases[start + size - 1].value == m_cases[start + size].value - 1) {
            allConsecutive = true;
            for (unsigned i = 0; i < size - 1; ++i) {
                if (m_cases[start + i].value + 1 != m_cases[start + i + 1].value) {
                    allConsecutive = false;
                    break;
                }
            }
        }

        if (BinarySwitchInternal::verbose)
            dataLog("allConsecutive = ", allConsecutive, "\n");
        
        Vector<unsigned, 3> localCaseIndices;
        for (unsigned i = 0; i < size; ++i)
            localCaseIndices.append(start + i);
        
        std::shuffle(
            localCaseIndices.begin(), localCaseIndices.end(),
            RandomNumberGenerator(m_weakRandom));
        
        for (unsigned i = 0; i < size - 1; ++i) {
            append(BranchCode(NotEqualToPush, localCaseIndices[i]));
            append(BranchCode(ExecuteCase, localCaseIndices[i]));
            append(BranchCode(Pop));
        }
        
        if (!allConsecutive)
            append(BranchCode(NotEqualToFallThrough, localCaseIndices.last()));
        
        append(BranchCode(ExecuteCase, localCaseIndices.last()));
        return;
    }

    if (BinarySwitchInternal::verbose)
        dataLog("It's not a leaf.\n");
        
    // There are two different strategies we could consider here:
    //
    // Isolate median and split: pick a median and check if the comparison value is equal to it;
    // if so, execute the median case. Otherwise check if the value is less than the median, and
    // recurse left or right based on this. This has two subvariants: we could either first test
    // equality for the median and then do the less-than, or we could first do the less-than and
    // then check equality on the not-less-than path.
    //
    // Ignore median and split: do a less-than comparison on a value that splits the cases in two
    // equal-sized halves. Recurse left or right based on the comparison. Do not test for equality
    // against the median (or anything else); let the recursion handle those equality comparisons
    // once we bottom out in a list that case 3 cases or less (see above).
    //
    // I'll refer to these strategies as Isolate and Ignore. I initially believed that Isolate
    // would be faster since it leads to less branching for some lucky cases. It turns out that
    // Isolate is almost a total fail in the average, assuming all cases are equally likely. How
    // bad Isolate is depends on whether you believe that doing two consecutive branches based on
    // the same comparison is cheaper than doing the compare/branches separately. This is
    // difficult to evaluate. For small immediates that aren't blinded, we just care about
    // avoiding a second compare instruction. For large immediates or when blinding is in play, we
    // also care about the instructions used to materialize the immediate a second time. Isolate
    // can help with both costs since it involves first doing a < compare+branch on some value,
    // followed by a == compare+branch on the same exact value (or vice-versa). Ignore will do a <
    // compare+branch on some value, and then the == compare+branch on that same value will happen
    // much later.
    //
    // To evaluate these costs, I wrote the recurrence relation for Isolate and Ignore, assuming
    // that ComparisonCost is the cost of a compare+branch and ChainedComparisonCost is the cost
    // of a compare+branch on some value that you've just done another compare+branch for. These
    // recurrence relations compute the total cost incurred if you executed the switch statement
    // on each matching value. So the average cost of hitting some case can be computed as
    // Isolate[n]/n or Ignore[n]/n, respectively for the two relations.
    //
    // Isolate[1] = ComparisonCost
    // Isolate[2] = (2 + 1) * ComparisonCost
    // Isolate[3] = (3 + 2 + 1) * ComparisonCost
    // Isolate[n_] := With[
    //     {medianIndex = Floor[n/2] + If[EvenQ[n], RandomInteger[], 1]},
    //     ComparisonCost + ChainedComparisonCost +
    //     (ComparisonCost * (medianIndex - 1) + Isolate[medianIndex - 1]) +
    //     (2 * ComparisonCost * (n - medianIndex) + Isolate[n - medianIndex])]
    //
    // Ignore[1] = ComparisonCost
    // Ignore[2] = (2 + 1) * ComparisonCost
    // Ignore[3] = (3 + 2 + 1) * ComparisonCost
    // Ignore[n_] := With[
    //     {medianIndex = If[EvenQ[n], n/2, Floor[n/2] + RandomInteger[]]},
    //     (medianIndex * ComparisonCost + Ignore[medianIndex]) +
    //     ((n - medianIndex) * ComparisonCost + Ignore[n - medianIndex])]
    //
    // This does not account for the average cost of hitting the default case. See further below
    // for a discussion of that.
    //
    // It turns out that for ComparisonCost = 1 and ChainedComparisonCost = 1, Ignore is always
    // better than Isolate. If we assume that ChainedComparisonCost = 0, then Isolate wins for
    // switch statements that have 20 cases or fewer, though the margin of victory is never large
    // - it might sometimes save an average of 0.3 ComparisonCost. For larger switch statements,
    // we see divergence between the two with Ignore winning. This is of course rather
    // unrealistic since the chained comparison is never free. For ChainedComparisonCost = 0.5, we
    // see Isolate winning for 10 cases or fewer, by maybe 0.2 ComparisonCost. Again we see
    // divergence for large switches with Ignore winning, for example if a switch statement has
    // 100 cases then Ignore saves one branch on average.
    //
    // Our current JIT backends don't provide for optimization for chained comparisons, except for
    // reducing the code for materializing the immediate if the immediates are large or blinding
    // comes into play. Probably our JIT backends live somewhere north of
    // ChainedComparisonCost = 0.5.
    //
    // This implies that using the Ignore strategy is likely better. If we wanted to incorporate
    // the Isolate strategy, we'd want to determine the switch size threshold at which the two
    // cross over and then use Isolate for switches that are smaller than that size.
    //
    // The average cost of hitting the default case is similar, but involves a different cost for
    // the base cases: you have to assume that you will always fail each branch. For the Ignore
    // strategy we would get this recurrence relation; the same kind of thing happens to the
    // Isolate strategy:
    //
    // Ignore[1] = ComparisonCost
    // Ignore[2] = (2 + 2) * ComparisonCost
    // Ignore[3] = (3 + 3 + 3) * ComparisonCost
    // Ignore[n_] := With[
    //     {medianIndex = If[EvenQ[n], n/2, Floor[n/2] + RandomInteger[]]},
    //     (medianIndex * ComparisonCost + Ignore[medianIndex]) +
    //     ((n - medianIndex) * ComparisonCost + Ignore[n - medianIndex])]
    //
    // This means that if we cared about the default case more, we would likely reduce
    // leafThreshold. Reducing it to 2 would reduce the average cost of the default case by 1/3
    // in the most extreme cases (num switch cases = 3, 6, 12, 24, ...). But it would also
    // increase the average cost of taking one of the non-default cases by 1/3. Typically the
    // difference is 1/6 in either direction. This makes it a very simple trade-off: if we believe
    // that the default case is more important then we would want leafThreshold to be 2, and the
    // default case would become 1/6 faster on average. But we believe that most switch statements
    // are more likely to take one of the cases than the default, so we use leafThreshold = 3
    // and get a 1/6 speed-up on average for taking an explicit case.
        
    unsigned medianIndex = (start + end) / 2;

    if (BinarySwitchInternal::verbose)
        dataLog("medianIndex = ", medianIndex, "\n");

    // We want medianIndex to point to the thing we will do a less-than compare against. We want
    // this less-than compare to split the current sublist into equal-sized sublists, or
    // nearly-equal-sized with some randomness if we're in the odd case. With the above
    // calculation, in the odd case we will have medianIndex pointing at either the element we
    // want or the element to the left of the one we want. Consider the case of five elements:
    //
    //     0 1 2 3 4
    //
    // start will be 0, end will be 5. The average is 2.5, which rounds down to 2. If we do
    // value < 2, then we will split the list into 2 elements on the left and three on the right.
    // That's pretty good, but in this odd case we'd like to at random choose 3 instead to ensure
    // that we don't become unbalanced on the right. This does not improve throughput since one
    // side will always get shafted, and that side might still be odd, in which case it will also
    // have two sides and one of them will get shafted - and so on. We just want to avoid
    // deterministic pathologies.
    //
    // In the even case, we will always end up pointing at the element we want:
    //
    //     0 1 2 3
    //
    // start will be 0, end will be 4. So, the average is 2, which is what we'd like.
    if (size & 1) {
        RELEASE_ASSERT(medianIndex - start + 1 == end - medianIndex);
        medianIndex += m_weakRandom.getUint32() & 1;
    } else
        RELEASE_ASSERT(medianIndex - start == end - medianIndex);
        
    RELEASE_ASSERT(medianIndex > start);
    RELEASE_ASSERT(medianIndex + 1 < end);
        
    if (BinarySwitchInternal::verbose)
        dataLog("fixed medianIndex = ", medianIndex, "\n");

    append(BranchCode(LessThanToPush, medianIndex));
    build(medianIndex, true, end);
    append(BranchCode(Pop));
    build(start, hardStart, medianIndex);
}
Ejemplo n.º 3
0
    /// Updates the entire overworld for the elapsed time.
    /// @param[in]  elapsed_time - The elapsed time for which to update the world.
    /// @param[in,out]  input_controller - The controller supplying player input.
    /// @param[in,out]  camera - The camera defining the viewable region of the overworld.
    /// @param[out] message_for_text_box - The message to display in the main text box,
    ///     if one needs to start being displayed; empty if no new message needs to be displayed.
    void GameplayState::UpdateOverworld(
        const sf::Time& elapsed_time,
        INPUT_CONTROL::KeyboardInputController& input_controller,
        GRAPHICS::Camera& camera,
        std::string& message_for_text_box)
    {
        // INDICATE THAT NO MESSAGE NEEDS TO BE DISPLAYED YET IN THE TEXT BOX.
        message_for_text_box.clear();

        MATH::FloatRectangle camera_bounds = camera.ViewBounds;
        MATH::Vector2f camera_view_center = camera_bounds.GetCenterPosition();

        MAPS::TileMap* current_tile_map = Overworld->GetTileMap(camera_view_center.X, camera_view_center.Y);
        assert(current_tile_map);

        // CHECK IF THE PRIMARY ACTION BUTTON WAS PRESSED.
        if (input_controller.ButtonDown(INPUT_CONTROL::KeyboardInputController::PRIMARY_ACTION_KEY))
        {
            // SWING THE PLAYER'S AXE.
            // A new axe swing may not be created if the player's
            // axe is already being swung.
            std::shared_ptr<EVENTS::AxeSwingEvent> axe_swing = Overworld->NoahPlayer->SwingAxe();
            bool axe_swing_occurred = (nullptr != axe_swing);
            if (axe_swing_occurred)
            {
                // Allow the axe to collide with other objects.
                Overworld->AxeSwings.push_back(axe_swing);
            }
        }

        // CHECK IF THE PLAYER IS ALLOWED TO MOVE.
        // Noah can't move while the axe is swinging.
        // Movement is prevented to have the axe's position remain attached to Noah.
        // We need to keep track if Noah moved this frame so that we can stop any walking animations for him if he didn't move.
        bool noah_moved_this_frame = false;
        bool axe_is_swinging = (nullptr != Overworld->NoahPlayer->Inventory->Axe) && Overworld->NoahPlayer->Inventory->Axe->IsSwinging();
        bool player_movement_allowed = (!axe_is_swinging);
        if (player_movement_allowed)
        {
            // MOVE NOAH IN RESPONSE TO USER INPUT.
            const float PLAYER_POSITION_ADJUSTMENT_FOR_SCROLLING_IN_PIXELS = 8.0f;
            MATH::Vector2f old_noah_position = Overworld->NoahPlayer->GetWorldPosition();
            if (input_controller.ButtonDown(INPUT_CONTROL::KeyboardInputController::UP_KEY))
            {
                // TRACK NOAH AS MOVING THIS FRAME.
                noah_moved_this_frame = true;

                // START NOAH WALKING UP.
                Overworld->NoahPlayer->BeginWalking(CORE::Direction::UP, OBJECTS::Noah::WALK_BACK_ANIMATION_NAME);

                // MOVE NOAH WHILE HANDLING COLLISIONS.
                MATH::Vector2f new_position = COLLISION::CollisionDetectionAlgorithms::MoveObject(
                    Overworld->NoahPlayer->GetWorldBoundingBox(),
                    CORE::Direction::UP,
                    OBJECTS::Noah::MOVE_SPEED_IN_PIXELS_PER_SECOND,
                    elapsed_time,
                    *Overworld);
                Overworld->NoahPlayer->SetWorldPosition(new_position);

                // CHECK IF NOAH MOVED OUT OF THE CAMERA'S VIEW.
                MATH::FloatRectangle noah_world_bounding_box = Overworld->NoahPlayer->GetWorldBoundingBox();
                float camera_top_y_position = camera_bounds.GetTopYPosition();
                bool player_moved_out_of_view = (noah_world_bounding_box.GetTopYPosition() < camera_top_y_position);
                if (player_moved_out_of_view)
                {
                    // CHECK IF A TOP TILE MAP EXISTS FOR NOAH TO MOVE TO.
                    unsigned int top_tile_map_row_index = current_tile_map->OverworldRowIndex - 1;
                    unsigned int top_tile_map_column_index = current_tile_map->OverworldColumnIndex;
                    MAPS::TileMap* top_tile_map = Overworld->GetTileMap(
                        top_tile_map_row_index,
                        top_tile_map_column_index);
                    bool top_tile_map_exists = (nullptr != top_tile_map);
                    if (top_tile_map_exists)
                    {
                        // MOVE NOAH A FEW MORE PIXELS UP SO THAT HE WILL BE MORE VISIBLE ON THE NEW MAP.
                        MATH::Vector2f noah_world_position = Overworld->NoahPlayer->GetWorldPosition();
                        noah_world_position.Y -= PLAYER_POSITION_ADJUSTMENT_FOR_SCROLLING_IN_PIXELS;
                        Overworld->NoahPlayer->SetWorldPosition(noah_world_position);

                        // START SCROLLING TO THE TOP TILE MAP.
                        MATH::Vector2f scroll_start_position = current_tile_map->GetCenterWorldPosition();
                        MATH::Vector2f scroll_end_position = top_tile_map->GetCenterWorldPosition();
                        camera.StartScrolling(scroll_start_position, scroll_end_position);

                        // DISABLE PLAYER MOVEMENT WHILE SCROLLING IS OCCURRING.
                        input_controller.DisableInput();
                    }
                    else
                    {
                        // KEEP NOAH WITHIN THE BOUNDS OF THE CURRENT TILE MAP.
                        // Since there is no top tile map to scroll to, this will keep Noah on-screen.
                        MATH::FloatRectangle tile_map_bounding_box = current_tile_map->GetWorldBoundingBox();
                        float tile_map_top_boundary = tile_map_bounding_box.GetTopYPosition();

                        // To keep Noah completely on screen, his center position should be half
                        // his height below the top tile map boundary.
                        MATH::Vector2f noah_world_position = old_noah_position;
                        float noah_half_height = noah_world_bounding_box.GetHeight() / 2.0f;
                        noah_world_position.Y = tile_map_top_boundary + noah_half_height;

                        Overworld->NoahPlayer->SetWorldPosition(noah_world_position);
                    }
                }
            }
            if (input_controller.ButtonDown(INPUT_CONTROL::KeyboardInputController::DOWN_KEY))
            {
                // TRACK NOAH AS MOVING THIS FRAME.
                noah_moved_this_frame = true;

                // START NOAH WALKING DOWN.
                Overworld->NoahPlayer->BeginWalking(CORE::Direction::DOWN, OBJECTS::Noah::WALK_FRONT_ANIMATION_NAME);

                // MOVE NOAH WHILE HANDLING COLLISIONS.
                MATH::Vector2f new_position = COLLISION::CollisionDetectionAlgorithms::MoveObject(
                    Overworld->NoahPlayer->GetWorldBoundingBox(),
                    CORE::Direction::DOWN,
                    OBJECTS::Noah::MOVE_SPEED_IN_PIXELS_PER_SECOND,
                    elapsed_time,
                    *Overworld);
                Overworld->NoahPlayer->SetWorldPosition(new_position);

                // CHECK IF NOAH MOVED OUT OF THE CAMERA'S VIEW.
                MATH::FloatRectangle noah_world_bounding_box = Overworld->NoahPlayer->GetWorldBoundingBox();
                float camera_bottom_y_position = camera_bounds.GetBottomYPosition();
                bool player_moved_out_of_view = (noah_world_bounding_box.GetBottomYPosition() > camera_bottom_y_position);
                if (player_moved_out_of_view)
                {
                    // CHECK IF A BOTTOM TILE MAP EXISTS FOR NOAH TO MOVE TO.
                    unsigned int bottom_tile_map_row_index = current_tile_map->OverworldRowIndex + 1;
                    unsigned int bottom_tile_map_column_index = current_tile_map->OverworldColumnIndex;
                    MAPS::TileMap* bottom_tile_map = Overworld->GetTileMap(
                        bottom_tile_map_row_index,
                        bottom_tile_map_column_index);
                    bool bottom_tile_map_exists = (nullptr != bottom_tile_map);
                    if (bottom_tile_map_exists)
                    {
                        // MOVE NOAH A FEW MORE PIXELS DOWN SO THAT HE WILL BE MORE VISIBLE ON THE NEW MAP.
                        MATH::Vector2f noah_world_position = Overworld->NoahPlayer->GetWorldPosition();
                        noah_world_position.Y += PLAYER_POSITION_ADJUSTMENT_FOR_SCROLLING_IN_PIXELS;
                        Overworld->NoahPlayer->SetWorldPosition(noah_world_position);

                        // START SCROLLING TO THE BOTTOM TILE MAP.
                        MATH::Vector2f scroll_start_position = current_tile_map->GetCenterWorldPosition();
                        MATH::Vector2f scroll_end_position = bottom_tile_map->GetCenterWorldPosition();
                        camera.StartScrolling(scroll_start_position, scroll_end_position);

                        // DISABLE PLAYER MOVEMENT WHILE SCROLLING IS OCCURRING.
                        input_controller.DisableInput();
                    }
                    else
                    {
                        // KEEP NOAH WITHIN THE BOUNDS OF THE CURRENT TILE MAP.
                        // Since there is no bottom tile map to scroll to, this will keep Noah on-screen.
                        MATH::FloatRectangle tile_map_bounding_box = current_tile_map->GetWorldBoundingBox();
                        float tile_map_bottom_boundary = tile_map_bounding_box.GetBottomYPosition();

                        // To keep Noah completely on screen, his center position should be half
                        // his height above the bottom tile map boundary.
                        MATH::Vector2f noah_world_position = old_noah_position;
                        float noah_half_height = Overworld->NoahPlayer->GetWorldBoundingBox().GetHeight() / 2.0f;
                        noah_world_position.Y = tile_map_bottom_boundary - noah_half_height;

                        Overworld->NoahPlayer->SetWorldPosition(noah_world_position);
                    }
                }
            } 
            if (input_controller.ButtonDown(INPUT_CONTROL::KeyboardInputController::LEFT_KEY))
            {
                // TRACK NOAH AS MOVING THIS FRAME.
                noah_moved_this_frame = true;

                // START NOAH WALKING LEFT.
                Overworld->NoahPlayer->BeginWalking(CORE::Direction::LEFT, OBJECTS::Noah::WALK_LEFT_ANIMATION_NAME);

                // MOVE NOAH WHILE HANDLING COLLISIONS.
                MATH::Vector2f new_position = COLLISION::CollisionDetectionAlgorithms::MoveObject(
                    Overworld->NoahPlayer->GetWorldBoundingBox(),
                    CORE::Direction::LEFT,
                    OBJECTS::Noah::MOVE_SPEED_IN_PIXELS_PER_SECOND,
                    elapsed_time,
                    *Overworld);
                Overworld->NoahPlayer->SetWorldPosition(new_position);

                // CHECK IF NOAH MOVED OUT OF THE CAMERA'S VIEW.
                MATH::FloatRectangle noah_world_bounding_box = Overworld->NoahPlayer->GetWorldBoundingBox();
                float camera_left_x_position = camera_bounds.GetLeftXPosition();
                bool player_moved_out_of_view = (noah_world_bounding_box.GetLeftXPosition() < camera_left_x_position);
                if (player_moved_out_of_view)
                {
                    // CHECK IF A LEFT TILE MAP EXISTS FOR NOAH TO MOVE TO.
                    unsigned int left_tile_map_row_index = current_tile_map->OverworldRowIndex;
                    unsigned int left_tile_map_column_index = current_tile_map->OverworldColumnIndex - 1;
                    MAPS::TileMap* left_tile_map = Overworld->GetTileMap(
                        left_tile_map_row_index,
                        left_tile_map_column_index);
                    bool left_tile_map_exists = (nullptr != left_tile_map);
                    if (left_tile_map_exists)
                    {
                        // MOVE NOAH A FEW MORE PIXELS LEFT SO THAT HE WILL BE MORE VISIBLE ON THE NEW MAP.
                        MATH::Vector2f noah_world_position = Overworld->NoahPlayer->GetWorldPosition();
                        noah_world_position.X -= PLAYER_POSITION_ADJUSTMENT_FOR_SCROLLING_IN_PIXELS;
                        Overworld->NoahPlayer->SetWorldPosition(noah_world_position);

                        // START SCROLLING TO THE LEFT TILE MAP.
                        MATH::Vector2f scroll_start_position = current_tile_map->GetCenterWorldPosition();
                        MATH::Vector2f scroll_end_position = left_tile_map->GetCenterWorldPosition();
                        camera.StartScrolling(scroll_start_position, scroll_end_position);

                        // DISABLE PLAYER MOVEMENT WHILE SCROLLING IS OCCURRING.
                        input_controller.DisableInput();
                    }
                    else
                    {
                        // KEEP NOAH WITHIN THE BOUNDS OF THE CURRENT TILE MAP.
                        // Since there is no left tile map to scroll to, this will keep Noah on-screen.
                        MATH::FloatRectangle tile_map_bounding_box = current_tile_map->GetWorldBoundingBox();
                        float tile_map_left_boundary = tile_map_bounding_box.GetLeftXPosition();

                        // To keep Noah completely on screen, his center position should be half
                        // his width to the right of the left tile map boundary.
                        MATH::Vector2f noah_world_position = old_noah_position;
                        float noah_half_width = Overworld->NoahPlayer->GetWorldBoundingBox().GetWidth() / 2.0f;
                        noah_world_position.X = tile_map_left_boundary + noah_half_width;

                        Overworld->NoahPlayer->SetWorldPosition(noah_world_position);
                    }
                }
            } 
            if (input_controller.ButtonDown(INPUT_CONTROL::KeyboardInputController::RIGHT_KEY))
            {
                // TRACK NOAH AS MOVING THIS FRAME.
                noah_moved_this_frame = true;

                // START NOAH WALKING RIGHT.
                Overworld->NoahPlayer->BeginWalking(CORE::Direction::RIGHT, OBJECTS::Noah::WALK_RIGHT_ANIMATION_NAME);

                // MOVE NOAH WHILE HANDLING COLLISIONS.
                MATH::Vector2f new_position = COLLISION::CollisionDetectionAlgorithms::MoveObject(
                    Overworld->NoahPlayer->GetWorldBoundingBox(),
                    CORE::Direction::RIGHT,
                    OBJECTS::Noah::MOVE_SPEED_IN_PIXELS_PER_SECOND,
                    elapsed_time,
                    *Overworld);
                Overworld->NoahPlayer->SetWorldPosition(new_position);

                // CHECK IF NOAH MOVED OUT OF THE CAMERA'S VIEW.
                MATH::FloatRectangle noah_world_bounding_box = Overworld->NoahPlayer->GetWorldBoundingBox();
                float camera_right_x_position = camera_bounds.GetRightXPosition();
                bool player_moved_out_of_view = (noah_world_bounding_box.GetRightXPosition() > camera_right_x_position);
                if (player_moved_out_of_view)
                {
                    // CHECK IF A RIGHT TILE MAP EXISTS FOR NOAH TO MOVE TO.
                    unsigned int right_tile_map_row_index = current_tile_map->OverworldRowIndex;
                    unsigned int right_tile_map_column_index = current_tile_map->OverworldColumnIndex + 1;
                    MAPS::TileMap* right_tile_map = Overworld->GetTileMap(
                        right_tile_map_row_index,
                        right_tile_map_column_index);
                    bool right_tile_map_exists = (nullptr != right_tile_map);
                    if (right_tile_map_exists)
                    {
                        // MOVE NOAH A FEW MORE PIXELS RIGHT SO THAT HE WILL BE MORE VISIBLE ON THE NEW MAP.
                        MATH::Vector2f noah_world_position = Overworld->NoahPlayer->GetWorldPosition();
                        noah_world_position.X += PLAYER_POSITION_ADJUSTMENT_FOR_SCROLLING_IN_PIXELS;
                        Overworld->NoahPlayer->SetWorldPosition(noah_world_position);

                        // START SCROLLING TO THE RIGHT TILE MAP.
                        MATH::Vector2f scroll_start_position = current_tile_map->GetCenterWorldPosition();
                        MATH::Vector2f scroll_end_position = right_tile_map->GetCenterWorldPosition();
                        camera.StartScrolling(scroll_start_position, scroll_end_position);

                        // DISABLE PLAYER MOVEMENT WHILE SCROLLING IS OCCURRING.
                        input_controller.DisableInput();
                    }
                    else
                    {
                        // KEEP NOAH WITHIN THE BOUNDS OF THE CURRENT TILE MAP.
                        // Since there is no right tile map to scroll to, this will keep Noah on-screen.
                        MATH::FloatRectangle tile_map_bounding_box = current_tile_map->GetWorldBoundingBox();
                        float tile_map_right_boundary = tile_map_bounding_box.GetRightXPosition();

                        // To keep Noah completely on screen, his center position should be half
                        // his width to the left of the right tile map boundary.
                        MATH::Vector2f noah_world_position = old_noah_position;
                        float noah_half_width = Overworld->NoahPlayer->GetWorldBoundingBox().GetWidth() / 2.0f;
                        noah_world_position.X = tile_map_right_boundary - noah_half_width;

                        Overworld->NoahPlayer->SetWorldPosition(noah_world_position);
                    }
                }
            } 
        }

        // CHECK IF NOAH MOVED THIS FRAME.
        if (noah_moved_this_frame)
        {
            // UPDATE NOAH'S ANIMATION.
            Overworld->NoahPlayer->Sprite.Update(elapsed_time);

            // BUILD A PIECE OF THE ARK IF NOAH STEPPED ONTO AN APPROPRIATE SPOT.
            MATH::Vector2f noah_world_position = Overworld->NoahPlayer->GetWorldPosition();
            MAPS::TileMap* tile_map_underneath_noah = Overworld->GetTileMap(noah_world_position.X, noah_world_position.Y);
            assert(tile_map_underneath_noah);
            
            // An ark piece only needs to be built once.
            OBJECTS::ArkPiece* ark_piece = tile_map_underneath_noah->GetArkPieceAtWorldPosition(noah_world_position);
            bool ark_piece_can_be_built = (ark_piece && !ark_piece->Built);
            if (ark_piece_can_be_built)
            {
                /// @todo   Check if Noah has wood.

                // BUILD THE ARK PIECE.
                ark_piece->Built = true;

                // When building an ark piece, a dust cloud should appear.
                std::shared_ptr<GRAPHICS::Texture> dust_cloud_texture = Assets->GetTexture(RESOURCES::DUST_CLOUD_TEXTURE_ID);
                assert(dust_cloud_texture);
                OBJECTS::DustCloud dust_cloud(dust_cloud_texture);

                // The dust cloud should be positioned over the ark piece.
                MATH::Vector2f dust_cloud_center_world_position = ark_piece->Sprite.GetWorldPosition();
                dust_cloud.Sprite.SetWorldPosition(dust_cloud_center_world_position);

                // The dust cloud should start animating immediately.
                dust_cloud.Sprite.Play();

                // The dust cloud needs to be added to the tile map so that it gets updated.
                tile_map_underneath_noah->DustClouds.push_back(dust_cloud);

                /// @todo   Play sound effect when building ark piece.
            }
        }
        else
        {
            // STOP NOAH'S ANIMATION FROM PLAYING SINCE THE PLAYER DIDN'T MOVE THIS FRAME.
            Overworld->NoahPlayer->Sprite.ResetAnimation();
        }

        // UPDATE THE CAMERA BASED ON SCROLLING.
        if (camera.IsScrolling)
        {
            // SCROLL BASED ON THE ELAPSED FRAME TIME.
            camera.Scroll(elapsed_time);

            // CHECK IF SCROLLING HAS FINISHED.
            bool scrolling_finished = !camera.IsScrolling;
            if (scrolling_finished)
            {
                // RE-ENABLE PLAYER INPUT.
                input_controller.EnableInput();
            }
        }
        else
        {
            // POSITION THE CAMERA TO FOCUS ON THE CENTER OF THE CURRENT TILE MAP.
            MATH::Vector2f center_world_position = current_tile_map->GetCenterWorldPosition();
            camera.SetCenter(center_world_position);
        }

        // HANDLE OTHER COLLISIONS.
        COLLISION::CollisionDetectionAlgorithms::HandleAxeSwings(*Overworld, Overworld->AxeSwings, *Assets);
        for (auto wood_logs = current_tile_map->WoodLogs.begin(); wood_logs != current_tile_map->WoodLogs.end();)
        {
            // CHECK IF THE WOOD LOGS INTERSECT WITH NOAH.
            MATH::FloatRectangle wood_log_bounding_box = wood_logs->GetWorldBoundingBox();
            MATH::FloatRectangle noah_bounding_box = Overworld->NoahPlayer->GetWorldBoundingBox();
            bool noah_collided_with_wood_logs = noah_bounding_box.Contains(
                wood_log_bounding_box.GetCenterXPosition(),
                wood_log_bounding_box.GetCenterYPosition());
            if (noah_collided_with_wood_logs)
            {
                // ADD THE WOOD TO NOAH'S INVENTORY.
                // The logs can have a random amount of wood.
                unsigned int MIN_WOOD_COUNT = 1;
                unsigned int MAX_WOOD_COUNT = 3;
                unsigned int random_number_for_wood = RandomNumberGenerator();
                unsigned int random_wood_count = (random_number_for_wood % MAX_WOOD_COUNT) + MIN_WOOD_COUNT;
                Overworld->NoahPlayer->Inventory->AddWood(random_wood_count);

                // REMOVE THE WOOD LOGS SINCE THEY'VE BEEN COLLECTED BY NOAH.
                wood_logs = current_tile_map->WoodLogs.erase(wood_logs);

                // SEE IF A BIBLE VERSE SHOULD BE COLLECTED ALONG WITH THE WOOD.
                // There should be a random chance that a Bible verse can be collected.
                const unsigned int EVENLY_DIVISIBLE = 0;
                const unsigned int BIBLE_VERSE_EXISTS_IF_DIVISIBLE_BY_THIS = 2;
                unsigned int random_number_for_bible_verse = RandomNumberGenerator();
                bool bible_verse_exists_with_wood = ((random_number_for_bible_verse % BIBLE_VERSE_EXISTS_IF_DIVISIBLE_BY_THIS) == EVENLY_DIVISIBLE);
                if (bible_verse_exists_with_wood)
                {
                    // CHECK IF ANY BIBLE VERSES REMAIN.
                    // This check helps protect against a mod by 0 below.
                    bool bible_verses_remain_to_be_found = !BibleVersesLeftToFind.empty();
                    if (bible_verses_remain_to_be_found)
                    {
                        // PLAY THE SOUND EFFECT FOR COLLECTING A BIBLE VERSE.
                        std::shared_ptr<AUDIO::SoundEffect> collected_bible_verse_sound = Assets->GetSound(RESOURCES::COLLECT_BIBLE_VERSE_SOUND_ID);
                        bool collect_bible_verse_sound_loaded = (nullptr != collected_bible_verse_sound);
                        if (collect_bible_verse_sound_loaded)
                        {
                            collected_bible_verse_sound->Play();
                        }

                        // SELECT A RANDOM BIBLE VERSE.
                        unsigned int random_bible_verse_index = RandomNumberGenerator() % BibleVersesLeftToFind.size();
                        auto bible_verse = BibleVersesLeftToFind.begin() + random_bible_verse_index;

                        // ADD THE BIBLE VERSE TO THE PLAYER'S INVENTORY.
                        Overworld->NoahPlayer->Inventory->BibleVerses.insert(*bible_verse);

                        // POPULATE THE MESSAGE TO DISPLAY IN THE MAIN TEXT BOX.
                        message_for_text_box = "You got a Bible verse!\n" + bible_verse->ToString();

                        // REMOVE THE VERSE SINCE IT HAS BEEN FOUND.
                        BibleVersesLeftToFind.erase(bible_verse);
                    }
                }
            }
            else
            {
                // MOVE TO CHECKING COLLISIONS WITH THE NEXT SET OF WOOD LOGS.
                ++wood_logs;
            }
        }
    }