diff --git a/src/lib.rs b/src/lib.rs
index b2e459c78ab1c9059e7f8e1cf133c7add29bf509..d8067d73e4e6406028080f662c90fede55a4d1b0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -199,5 +199,6 @@ impl Plugin for BigBrainPlugin {
         app.add_system(scorers::fixed_score_system.system());
         app.add_system(scorers::all_or_nothing_system.system());
         app.add_system(scorers::sum_of_scorers_system.system());
+        app.add_system(scorers::winning_scorer_system.system());
     }
 }
diff --git a/src/scorers.rs b/src/scorers.rs
index 5708627d646af93b438843fb41b9cc4b21b297f5..17ab62ed352a813862cf78015be0d7ccf977441b 100644
--- a/src/scorers.rs
+++ b/src/scorers.rs
@@ -2,7 +2,7 @@
 Scorers look at the world and boil down arbitrary characteristics into a range of 0.0..=1.0. This module includes the ScorerBuilder trait and some built-in Composite Scorers.
 */
 
-use std::sync::Arc;
+use std::{cmp::Ordering, sync::Arc};
 
 use bevy::prelude::*;
 
@@ -267,3 +267,100 @@ impl ScorerBuilder for SumOfScorersBuilder {
             });
     }
 }
+
+/**
+Composite Scorer that takes any number of other Scorers and returns the single highest value [`Score`] if  _any_ [`Score`]s are at or above the configured `threshold`.
+
+### Example
+
+```ignore
+Thinker::build()
+    .when(
+        WinningScorer::build()
+          .push(MyScorer)
+          .push(MyOtherScorer),
+        MyAction::build());
+```
+ */
+
+#[derive(Debug)]
+pub struct WinningScorer {
+    threshold: f32,
+    scorers: Vec<ScorerEnt>,
+}
+
+impl WinningScorer {
+    pub fn build(threshold: f32) -> WinningScorerBuilder {
+        WinningScorerBuilder {
+            threshold,
+            scorers: Vec::new(),
+        }
+    }
+}
+
+pub fn winning_scorer_system(
+    mut query: Query<(Entity, &mut WinningScorer)>,
+    mut scores: QuerySet<(Query<&Score>, Query<&mut Score>)>,
+) {
+    for (sos_ent, mut winning_scorer) in query.iter_mut() {
+        let (threshold, children) = (winning_scorer.threshold, &mut winning_scorer.scorers);
+        let mut all_scores = children
+            .iter()
+            .map(|ScorerEnt(e)| scores.q0().get(*e).expect("where is it?"))
+            .collect::<Vec<&Score>>();
+
+        all_scores.sort_by(|a, b| a.get().partial_cmp(&b.get()).unwrap_or(Ordering::Equal));
+        let winning_score_or_zero = match all_scores.last() {
+            Some(s) => {
+                if s.get() < threshold {
+                    0.0
+                } else {
+                    s.get()
+                }
+            }
+            None => 0.0,
+        };
+        let mut score = scores.q1_mut().get_mut(sos_ent).expect("where did it go?");
+        score.set(crate::evaluators::clamp(winning_score_or_zero, 0.0, 1.0));
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct WinningScorerBuilder {
+    threshold: f32,
+    scorers: Vec<Arc<dyn ScorerBuilder>>,
+}
+
+impl WinningScorerBuilder {
+    pub fn when(&mut self, scorer: impl ScorerBuilder + 'static) -> &mut Self {
+        self.scorers.push(Arc::new(scorer));
+        self
+    }
+}
+
+impl WinningScorerBuilder {
+    /**
+    Add another Scorer to this [`ScorerBuilder`].
+     */
+    pub fn push(&mut self, scorer: impl ScorerBuilder + 'static) -> &mut Self {
+        self.scorers.push(Arc::new(scorer));
+        self
+    }
+}
+
+impl ScorerBuilder for WinningScorerBuilder {
+    fn build(&self, cmd: &mut Commands, scorer: Entity, actor: Entity) {
+        let scorers: Vec<_> = self
+            .scorers
+            .iter()
+            .map(|scorer| scorer.attach(cmd, actor))
+            .collect();
+        cmd.entity(scorer)
+            .insert(Transform::default())
+            .insert(GlobalTransform::default())
+            .insert(WinningScorer {
+                threshold: self.threshold,
+                scorers: scorers.into_iter().map(ScorerEnt).collect(),
+            });
+    }
+}