Count Matching: How Many Racers Passed?

In this lesson you'll learn to

  • βœ“Count array elements matching a condition (racers past checkpoint N).

Count array elements matching a condition (racers past checkpoint N).

πŸ“– Reference & full walkthrough: Count Matching: How Many Racers Passed?

🧩 Your capstone piece: Racers-remaining and checkpoint-progress counters


Barnaby the bear has a clipboard problem. Six dirtbikes are tearing through Codewood Grove, and every few seconds someone on the sidelines yells "how many racers made it past the rope bridge?!" Counting by paw takes too long. Counting with a for loop and an if takes one blink β€” and that is exactly the skill this lesson locks in: walk an array, test each element against a condition, and add 1 every time the test passes.

If you finished the sum-an-array lesson, this will feel familiar. Summing says "add the value to the total." Counting says "add 1 to the total β€” but only when the element passes a test." Same loop skeleton, one extra if. That tiny difference powers every "racers remaining", "players ready", and "checkpoints cleared" counter you will ever build.

What you will build

A checkpoint_progress_device for Barnaby's Jungle Mod-Rally β€” the north-jungle capstone. When a trigger_device at a checkpoint fires, the device counts two things from plain []int arrays and flashes them on every screen with a hud_message_device:

  • Checkpoint progress β€” how many racers have passed checkpoint 3 (Zipline Run) or beyond.
  • Racers remaining β€” how many racers have not yet finished all their laps.

These are the exact "racers-remaining and checkpoint-progress counters" the Mod-Rally race manager reads every time a checkpoint fires, so the code you write here drops straight into the capstone.

Walkthrough

The counting pattern

Every count-matching counter has four beats:

  1. var Count : int = 0 β€” start at zero.
  2. for (Value : MyArray) β€” visit every element.
  3. if (Value >= Threshold): β€” the condition. Only elements that pass get counted.
  4. set Count += 1 β€” add one (not the value β€” this is a headcount, not a treasure tally).

Here is the complete device. Wire the two @editable device references in the UEFN Details panel, drop it on your island, and it compiles and runs as-is.

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Native }

# Localisation helper β€” HUD messages must be `message`, not raw string.
RallyMessage<localizes>(S : string) : message = "{S}"

checkpoint_progress_device := class(creative_device):

    # ── Editable device references ────────────────────────────────────────
    # Wire this to the trigger_device sensor at the checkpoint.
    @editable
    CheckpointTrigger : trigger_device = trigger_device{}

    # HUD message device set to "All Players" in its properties.
    @editable
    ProgressHUD : hud_message_device = hud_message_device{}

    # ── Race data (the capstone updates these as racers ride) ─────────────
    # Highest checkpoint each racer has passed this lap. Index 0 = racer 1.
    @editable
    RacerCheckpoints : []int = array{3, 1, 4, 2, 4, 0}

    # Laps completed per racer β€” same racer order as above.
    @editable
    RacerLaps : []int = array{1, 0, 2, 1, 2, 0}

    # The rally is a 3-lap race by default.
    @editable
    TotalLaps : int = 3

    # Which checkpoint we are reporting progress for (3 = Zipline Run).
    @editable
    ReportCheckpoint : int = 3

    # ── The reusable counter ──────────────────────────────────────────────
    # Counts how many values in Values are at least Threshold.
    CountAtLeast(Values : []int, Threshold : int) : int =
        var Count : int = 0
        for (Value : Values):
            if (Value >= Threshold):
                set Count += 1
        Count

    # ── Lifecycle ─────────────────────────────────────────────────────────
    OnBegin<override>()<suspends> : void =
        # When the checkpoint sensor fires, refresh the counters.
        CheckpointTrigger.TriggeredEvent.Subscribe(OnCheckpointPassed)
        # 0.0 keeps the message on screen until we replace it.
        ProgressHUD.SetDisplayTime(0.0)

    # ── Event handler β€” TriggeredEvent sends ?agent ───────────────────────
    OnCheckpointPassed(MaybeAgent : ?agent) : void =
        # Counter 1: racers at or past the report checkpoint.
        PastCheckpoint : int = CountAtLeast(RacerCheckpoints, ReportCheckpoint)

        # Counter 2: racers remaining β€” laps still BELOW the total.
        var StillRacing : int = 0
        for (Laps : RacerLaps):
            if (Laps < TotalLaps):
                set StillRacing += 1

        ProgressHUD.Show(RallyMessage("{PastCheckpoint} past checkpoint {ReportCheckpoint} - {StillRacing} racers still in the rally!"))

Line-by-line explanation

Lines What's happening
RallyMessage<localizes> hud_message_device.Show takes a message, so we wrap our string in a <localizes> function β€” the same helper pattern from the sum-an-array lesson.
RacerCheckpoints : []int One slot per racer, holding the highest checkpoint passed. With the sample data, racers 0, 2 and 4 have reached checkpoint 3+.
CountAtLeast(Values, Threshold) The counter as a function β€” write the loop once, reuse it for any array and any threshold. Its last expression, Count, is the return value.
if (Value >= Threshold): The condition. >= is a failable comparison β€” the set Count += 1 line only runs when it succeeds.
set Count += 1 Add 1 per match. Compare with summing, where you write set Total += Value.
TriggeredEvent.Subscribe(OnCheckpointPassed) trigger_device.TriggeredEvent is listenable(?agent), so the handler takes ?agent.
if (Laps < TotalLaps): The racers-remaining condition inline β€” sometimes the count is a one-off and does not need its own function.
ProgressHUD.Show(...) Broadcasts both counters to every player in one message.

With the sample arrays: CountAtLeast(RacerCheckpoints, 3) returns 3 (the values 3, 4 and 4 pass the test), and the remaining-loop returns 6 β€” nobody has banked all 3 laps yet, so every racer is still in the rally. Run it, tweak the arrays in the Details panel, and watch the numbers move. That instant feedback is the fastest way to make the pattern stick.

Common patterns

Pattern 1 β€” Count exact matches with =

Sometimes you want "racers on exactly lap 2", not "lap 2 or more". In a condition, Verse compares equality with a single = (it is a failable check, not an assignment):

# Counts racers whose lap counter is exactly TargetLap.
CountOnLap(LapsPerRacer : []int, TargetLap : int) : int =
    var Count : int = 0
    for (Laps : LapsPerRacer):
        if (Laps = TargetLap):
            set Count += 1
    Count

Handy for a "3 riders battling on lap 2!" commentary line. Use <> if you ever need "not equal".

Pattern 2 β€” Compound conditions with and

Conditions can stack. This counter finds racers closing in on the finish: on their final lap and already past the halfway checkpoint:

# Racers on their last lap who have passed the halfway checkpoint.
CountClosingIn(LapsPerRacer : []int, Checkpoints : []int, FinalLap : int, HalfwayPoint : int) : int =
    var Count : int = 0
    for (Index -> Laps : LapsPerRacer):
        if (Laps >= FinalLap - 1 and Checkpoints[Index] >= HalfwayPoint):
            set Count += 1
    Count

The Index -> Laps form gives you the position too, so you can look up the same racer in a second array β€” both conditions must succeed before the count bumps. (Array indexing Checkpoints[Index] is failable, which is fine: it just becomes part of the condition.)

Where this goes next

  • Next lesson β€” filter-an-array: counting tells you how many passed the test; filtering hands you the elements themselves. Same loop, but you build a new array instead of bumping a counter.
  • The capstone β€” Barnaby's Jungle Mod-Rally: the race manager imports exactly this piece. CountAtLeast drives the checkpoint-progress HUD line after every CheckpointPassedEvent, and the racers-remaining loop (with laps < TotalLaps) decides when the rally flips its race_phase state machine to Finished. When you reach the maps lesson, the []int arrays upgrade to [player]int maps β€” but the counting condition you wrote today stays word-for-word the same.
πŸ”’

Keep going β€” free

You've read the intro. The rest of this lesson is free for members. Sign in to continue and track your progress.

Sign in free to continue this lesson
Pass the quiz above to chart this quest in your Journal.

Sources

/guides/count-matching
Source

Biloxi Studios original lesson

Guided course

Add this lesson to your free study plan.

🧭 The Keeper’s log

Quest complete? Chart your next heading from the 🐻 North Jungle expedition.

β›΅ Take me there