The Trolley Problem as a game under moral uncertainty

ethics-and-game-theory
moral-uncertainty
trolley-problem
decision-theory
Formalize the Trolley Problem and its variants as decision-theoretic games under moral uncertainty in R, comparing utilitarian, deontological, and contractualist frameworks through expected moral value calculations.
Author

Raban Heller

Published

May 8, 2026

Modified

May 8, 2026

Keywords

trolley problem, moral uncertainty, ethics, game theory, utilitarianism, deontology, contractualism

Introduction & motivation

Philippa Foot (1967) introduced the Trolley Problem as a thought experiment in moral philosophy: a runaway trolley is heading toward five people tied to the tracks; you can divert it onto a side track where it will kill one person instead. Most people say diverting is permissible. But in Thomson (1985)’s Footbridge variant — where you must push a large man off a bridge to stop the trolley — most people say it is not, even though the outcome is identical (one dies to save five). This pattern is puzzling because it suggests our moral intuitions are not governed by a single consistent principle. From a game-theoretic perspective, the Trolley Problem is a single-player decision under moral uncertainty: the decision-maker does not know which moral theory is correct but must act anyway. We can formalize this by treating moral theories as “states of the world” with associated probability weights (credences), defining payoffs under each theory for each action, and computing the expected moral value — exactly analogous to Bayesian decision theory. This approach, pioneered by philosophers like MacAskill and Ord, treats ethical dilemmas as decision problems and applies the same mathematical machinery used throughout game theory. This tutorial builds the formal model in R, computes expected moral values for the classic and Footbridge variants under varying credences, and visualizes how the optimal decision shifts as one’s moral credences change.

Mathematical formulation

Decision-maker: A single agent choosing between actions \(a \in \{D, N\}\) (Divert or Not-divert in the classic case; Push or Not-push in the Footbridge variant).

Moral theories (states of the world):

  • Utilitarianism (\(U\)): Maximize total welfare. Payoff = negative lives lost.
  • Deontology (\(K\)): Moral value depends on whether the act involves using a person as a means. Killing by diversion incurs a small duty-violation penalty; killing by pushing incurs a large one.
  • Contractualism (\(C\)): Evaluate by asking which principle no one could reasonably reject. Broadly aligns with protecting individual rights.

Let \(w_U, w_K, w_C\) be the agent’s credences (summing to 1). For action \(a\) and theory \(\theta\), let \(V(\theta, a)\) be the moral value. The expected moral value is:

\[ \text{EMV}(a) = w_U \cdot V(U, a) + w_K \cdot V(K, a) + w_C \cdot V(C, a) \]

The agent chooses the action maximizing EMV.

R implementation

# Define payoff matrices for Classic Trolley and Footbridge variants
# Values on a -10 to +10 scale (normalised moral value)

classic_payoffs <- tribble(
  ~theory, ~divert, ~not_divert,
  "Utilitarian",    4,   -4,   # save 4 net lives = good
  "Deontological", -1,    0,   # diverting kills 1 actively (small violation) vs inaction
  "Contractualist",  2,   -2   # 5 people can reasonably reject not-diverting
)

footbridge_payoffs <- tribble(
  ~theory, ~push, ~not_push,
  "Utilitarian",    4,   -4,   # same net lives saved
  "Deontological", -8,    0,   # pushing person as means: severe duty violation
  "Contractualist", -3,   -2   # pushed person can reasonably reject being used
)

cat("Classic Trolley payoffs:\n")
Classic Trolley payoffs:
print(classic_payoffs)
# A tibble: 3 × 3
  theory         divert not_divert
  <chr>           <dbl>      <dbl>
1 Utilitarian         4         -4
2 Deontological      -1          0
3 Contractualist      2         -2
cat("\nFootbridge payoffs:\n")

Footbridge payoffs:
print(footbridge_payoffs)
# A tibble: 3 × 3
  theory          push not_push
  <chr>          <dbl>    <dbl>
1 Utilitarian        4       -4
2 Deontological     -8        0
3 Contractualist    -3       -2
# Expected moral value as function of utilitarian credence w_U
# (split remaining credence equally between K and C)
compute_emv <- function(w_u, payoffs, action_col) {
  w_k <- (1 - w_u) / 2
  w_c <- (1 - w_u) / 2
  weights <- c(w_u, w_k, w_c)
  sum(weights * payoffs[[action_col]])
}

w_seq <- seq(0, 1, by = 0.01)

emv_classic <- tibble(w_u = w_seq) |>
  rowwise() |>
  mutate(
    EMV_Divert = compute_emv(w_u, classic_payoffs, "divert"),
    EMV_NotDivert = compute_emv(w_u, classic_payoffs, "not_divert"),
    optimal = ifelse(EMV_Divert > EMV_NotDivert, "Divert", "Not Divert")
  ) |> ungroup()

emv_footbridge <- tibble(w_u = w_seq) |>
  rowwise() |>
  mutate(
    EMV_Push = compute_emv(w_u, footbridge_payoffs, "push"),
    EMV_NotPush = compute_emv(w_u, footbridge_payoffs, "not_push"),
    optimal = ifelse(EMV_Push > EMV_NotPush, "Push", "Not Push")
  ) |> ungroup()

# Find crossover points
cross_classic <- emv_classic |>
  mutate(diff = EMV_Divert - EMV_NotDivert) |>
  filter(abs(diff) == min(abs(diff))) |>
  pull(w_u)

cross_foot <- emv_footbridge |>
  mutate(diff = EMV_Push - EMV_NotPush) |>
  filter(abs(diff) == min(abs(diff))) |>
  pull(w_u)

cat(sprintf("Classic: Divert optimal when w_U > %.2f\n", cross_classic[1]))
Classic: Divert optimal when w_U > 0.00
cat(sprintf("Footbridge: Push optimal when w_U > %.2f\n", cross_foot[1]))
Footbridge: Push optimal when w_U > 0.36

Static publication-ready figure

# Prepare long-format data for both variants
classic_long <- emv_classic |>
  select(w_u, EMV_Divert, EMV_NotDivert) |>
  pivot_longer(-w_u, names_to = "action", values_to = "EMV") |>
  mutate(action = gsub("EMV_", "", action),
         variant = "Classic Trolley")

foot_long <- emv_footbridge |>
  select(w_u, EMV_Push, EMV_NotPush) |>
  pivot_longer(-w_u, names_to = "action", values_to = "EMV") |>
  mutate(action = gsub("EMV_", "", action),
         variant = "Footbridge")

combined <- bind_rows(classic_long, foot_long)
combined$variant <- factor(combined$variant, levels = c("Classic Trolley", "Footbridge"))

p_emv <- ggplot(combined, aes(x = w_u, y = EMV, color = action)) +
  geom_line(linewidth = 1) +
  facet_wrap(~variant) +
  geom_hline(yintercept = 0, linetype = "dotted", color = "grey60") +
  scale_color_manual(values = c("Divert" = okabe_ito[3], "NotDivert" = okabe_ito[6],
                                 "Push" = okabe_ito[3], "NotPush" = okabe_ito[6]),
                      labels = c("Divert/Push", "Not Divert/Not Push"),
                      name = "Action") +
  labs(
    title = "Trolley Problem under moral uncertainty",
    subtitle = "Expected moral value vs utilitarian credence (remainder split equally: deontology + contractualism)",
    x = "Utilitarian credence (w_U)",
    y = "Expected moral value"
  ) +
  theme_publication() +
  theme(strip.text = element_text(face = "bold"))

p_emv
Figure 1: Figure 1. Expected moral value for the Classic Trolley (left) and Footbridge (right) variants as a function of utilitarian credence. In the Classic case, Divert is optimal for nearly all credence levels — even modest utilitarian weight suffices. In the Footbridge case, Push requires a substantially higher utilitarian credence (~70%) because the deontological penalty for using a person as a means is severe. This captures the empirical pattern: most people approve of diverting but not pushing, consistent with moderate utilitarian credences. Okabe-Ito palette.

Interactive figure

combined_text <- combined |>
  mutate(text = paste0("w_U = ", round(w_u, 2),
                       "\nAction: ", action,
                       "\nEMV = ", round(EMV, 2),
                       "\nVariant: ", variant))

p_int <- ggplot(combined_text, aes(x = w_u, y = EMV, color = action, text = text)) +
  geom_line(linewidth = 0.8) +
  facet_wrap(~variant) +
  scale_color_manual(values = c("Divert" = okabe_ito[3], "NotDivert" = okabe_ito[6],
                                 "Push" = okabe_ito[3], "NotPush" = okabe_ito[6])) +
  labs(x = "Utilitarian credence", y = "Expected moral value", color = "Action") +
  theme_publication()

ggplotly(p_int, tooltip = "text") |>
  config(displaylogo = FALSE,
         modeBarButtonsToRemove = c("select2d", "lasso2d"))
Figure 2

Credence space analysis

We can visualize the full credence simplex (\(w_U + w_K + w_C = 1\)) to find the regions where each action is optimal.

# Generate points on the credence simplex
n_grid <- 50
simplex_points <- expand.grid(
  w_u = seq(0, 1, length.out = n_grid),
  w_k = seq(0, 1, length.out = n_grid)
) |>
  filter(w_u + w_k <= 1) |>
  mutate(w_c = 1 - w_u - w_k)

# Compute EMV for Footbridge at each point
simplex_emv <- simplex_points |>
  rowwise() |>
  mutate(
    emv_push = w_u * 4 + w_k * (-8) + w_c * (-3),
    emv_not = w_u * (-4) + w_k * 0 + w_c * (-2),
    optimal = ifelse(emv_push > emv_not, "Push", "Not Push"),
    emv_diff = emv_push - emv_not
  ) |> ungroup()

# Barycentric to Cartesian
simplex_emv <- simplex_emv |>
  mutate(
    cx = w_k + 0.5 * w_c,
    cy = (sqrt(3)/2) * w_c
  )

p_simp <- ggplot(simplex_emv, aes(x = cx, y = cy, fill = emv_diff,
                                    text = paste0("w_U=", round(w_u,2),
                                                 " w_K=", round(w_k,2),
                                                 " w_C=", round(w_c,2),
                                                 "\nEMV diff: ", round(emv_diff,2)))) +
  geom_tile(width = 0.02, height = 0.02) +
  scale_fill_gradient2(low = okabe_ito[6], mid = "white", high = okabe_ito[3],
                        midpoint = 0, name = "EMV(Push) − EMV(Not)") +
  annotate("text", x = 0, y = -0.04, label = "Utilitarian", size = 3) +
  annotate("text", x = 1, y = -0.04, label = "Deontological", size = 3) +
  annotate("text", x = 0.5, y = sqrt(3)/2 + 0.04, label = "Contractualist", size = 3) +
  coord_fixed() +
  labs(title = "Footbridge — optimal action across credence simplex",
       subtitle = "Green = Push optimal; Orange = Not Push optimal") +
  theme_publication() +
  theme(axis.text = element_blank(), axis.title = element_blank(),
        axis.ticks = element_blank(), axis.line = element_blank(),
        panel.grid = element_blank())

ggplotly(p_simp, tooltip = "text") |>
  config(displaylogo = FALSE,
         modeBarButtonsToRemove = c("select2d", "lasso2d"))
Figure 3

Interpretation

The formal analysis reveals why the Trolley Problem is philosophically significant: the two variants produce different optimal actions for a wide range of reasonable credences, even though the consequentialist calculus is identical. In the Classic Trolley case, Divert is supported by utilitarianism (save four net lives), contractualism (five people can reasonably reject inaction), and only weakly opposed by deontology (diverting involves a mild active killing). The result is that Divert is optimal for utilitarian credences above roughly 10% — nearly everyone should divert. The Footbridge case is starkly different: pushing requires utilitarian credence above roughly 70% because deontology assigns a severe penalty to using a person instrumentally as a means to save others (the Kantian prohibition). This pattern — most people approve of diverting but disapprove of pushing — is exactly what Thomson (1985) documented empirically and is now replicated in our Bayesian framework. The credence simplex visualization shows that the “Push” region occupies a relatively small corner of moral credence space dominated by strong utilitarianism. The game-theoretic approach to ethics does not tell us which moral theory is correct, but it does provide a rigorous framework for making decisions under moral uncertainty — and it explains why our intuitions shift across cases in a way that is consistent with holding a mixture of moral views simultaneously. This analysis connects moral philosophy to decision theory, Bayesian inference, and the broader game-theoretic toolkit explored throughout this site.

References

Foot, Philippa. 1967. “The Problem of Abortion and the Doctrine of Double Effect.” Oxford Review 5: 5–15.
Thomson, Judith Jarvis. 1985. “The Trolley Problem.” The Yale Law Journal 94 (6): 1395–415. https://doi.org/10.2307/796133.
Back to top

Reuse

Citation

BibTeX citation:
@online{heller2026,
  author = {Heller, Raban},
  title = {The {Trolley} {Problem} as a Game Under Moral Uncertainty},
  date = {2026-05-08},
  url = {https://r-heller.github.io/equilibria/tutorials/ethics-and-game-theory/trolley-problem-as-game/},
  langid = {en}
}
For attribution, please cite this work as:
Heller, Raban. 2026. “The Trolley Problem as a Game Under Moral Uncertainty.” May 8. https://r-heller.github.io/equilibria/tutorials/ethics-and-game-theory/trolley-problem-as-game/.