---
title: "Assessment rules for reputation"
description: "Four assessment rules compared in R: image scoring, standing, stern judging and shunning, and why only rules excusing justified refusal keep cooperation going."
date: "2026-08-08 10:00"
categories: [evolutionary ecology, evolutionary game theory, social evolution, R, ecology tutorial]
image: thumbnail.png
image-alt: "Good reputation of a discriminating population decaying to zero under image scoring while staying near 0.95 under standing"
---
The [previous post](../indirect-reciprocity-and-reputation/) found a clean threshold for indirect reciprocity, `q > c/b`, but it assumed something convenient: that a helper is seen as good and a defector as bad. That assumption hides the hardest problem in the whole theory. A discriminator who correctly refuses to help a known cheat has, viewed narrowly, just withheld help. Whether the population holds that against it is a matter of bookkeeping, and the bookkeeping decides whether cooperation survives.
The bookkeeping rule is called an assessment rule. This post codes four of them in R and lets each run to its equilibrium.
## Four ways to keep score
A first-order rule looks only at what the donor did. A second-order rule looks at what the donor did and at the reputation of the person it did it to. That extra bit of context is the whole argument. Four rules cover the classic cases:
- **Image scoring**: helping makes you good, refusing makes you bad. Context ignored (Nowak and Sigmund 1998).
- **Standing**: helping makes you good, and refusing makes you bad only if the recipient was good. Refusing a cheat is excused (Sugden 1986).
- **Stern judging**: helping a good recipient and refusing a bad one both make you good; the other two combinations make you bad.
- **Shunning**: only helping a good recipient makes you good. Refusing anyone, or helping a cheat, makes you bad.
Each rule is a two by two table indexed by the recipient's reputation and the donor's action, so it goes straight into a matrix.
```{r}
#| label: rules
b <- 3; cost <- 1
# rows: recipient good, recipient bad ; columns: donor helped, donor refused
# entry 1 means the donor is recorded as good
rules <- list(
`image scoring` = matrix(c(1, 1, 0, 0), 2, 2),
standing = matrix(c(1, 1, 0, 1), 2, 2),
`stern judging` = matrix(c(1, 0, 0, 1), 2, 2),
shunning = matrix(c(1, 0, 0, 0), 2, 2))
# probability of intending to help, given the recipient is good or bad
strategies <- list(DISC = c(1, 0), ALLC = c(1, 1), ALLD = c(0, 0))
rules$standing
```
Reading the standing matrix by column: helping always records you as good (first column all ones), while refusing records you as good only when the recipient was bad (second column, one in the bad row).
## Letting the reputations settle
Reputations are not fixed inputs; they are the output of the rule applied over and over. Give every strategy a starting reputation of one and iterate the update until nothing moves. One extra ingredient makes the comparison honest: an implementation error `e`, the chance that a donor meaning to help fails to. Errors are what separate the rules, because without them nobody ever needs excusing.
```{r}
#| label: engine
equilibrium <- function(rule, mix, e = 0.05, iters = 2000) {
A <- rules[[rule]]; nm <- names(mix)
g <- setNames(rep(1, length(mix)), nm) # probability each type is seen as good
for (it in seq_len(iters)) {
gbar <- sum(mix * g); prior <- c(gbar, 1 - gbar)
g_new <- sapply(nm, function(i) {
p_help <- strategies[[i]] * (1 - e) # intends to help, minus slips
sum(prior * (p_help * A[, 1] + (1 - p_help) * A[, 2]))
})
if (max(abs(g_new - g)) < 1e-12) { g <- g_new; break }
g <- g_new
}
gbar <- sum(mix * g)
helping <- sapply(nm, function(i) sum(c(gbar, 1 - gbar) * strategies[[i]] * (1 - e)))
received <- sapply(nm, function(i) sum(sapply(nm, function(j)
mix[j] * (1 - e) * (g[i] * strategies[[j]][1] + (1 - g[i]) * strategies[[j]][2]))))
list(good = g, helping = helping, payoff = -cost * helping + b * received)
}
for (r in names(rules)) {
z <- equilibrium(r, c(DISC = 1))
cat(sprintf("%-14s good %.3f helping %.3f payoff %.3f\n", r, z$good, z$helping, z$payoff))
}
```
The split is total. Standing and stern judging hold a population of discriminators at a good reputation of 0.952, helping in 90.5 per cent of encounters, for a payoff of 1.810. Image scoring and shunning both sit at zero: no reputation, no helping, no payoff.
## Why image scoring eats itself
The 0.952 figure is not arbitrary. Under standing, the only way to be recorded as bad is to intend help and slip, which happens with probability `e` against a good recipient. Solving the fixed point gives `1/(1+e)`, and with `e = 0.05` that is 0.952 exactly.
Image scoring has no such fixed point above zero. A discriminator whose reputation slips becomes a bad recipient, and every discriminator who then correctly refuses it is itself marked bad. Bad reputations breed bad reputations, and the whole population's score decays geometrically.
```{r}
#| label: collapse
g <- 1
for (t in 1:60) {
g <- g * (1 - 0.05)
if (t %in% c(1, 10, 20, 40, 60)) cat(sprintf("round %2d good %.3f\n", t, g))
}
```
Sixty rounds take a spotless population from 0.950 to 0.046. The decay factor is `1 - e`, so the collapse is not a knife edge: it happens for any error rate above zero. An error sweep makes that plain.
```{r}
#| label: sweep
for (e in c(0, 0.01, 0.05, 0.10)) {
s <- equilibrium("image scoring", c(DISC = 1), e = e)$helping
st <- equilibrium("standing", c(DISC = 1), e = e)$helping
cat(sprintf("e = %.2f image scoring %.3f standing %.3f\n", e, s, st))
}
```
At zero error both rules give perfect cooperation. Add one per cent noise and image scoring is already at zero while standing barely moves, to 0.980. This is the sense in which first-order rules fail: they are fine in a world without mistakes, and that world does not exist.
```{r}
#| label: fig-collapse
#| fig-cap: "Average good reputation of a discriminating population over 60 rounds at a 5 per cent error rate. Under image scoring it decays towards zero; under standing it holds at 1/(1+e) = 0.952."
#| fig-alt: "A decaying curve for image scoring falling towards zero and a flat line for standing near 0.95."
library(ggplot2)
e <- 0.05
trace <- data.frame(round = 0:60,
`image scoring` = (1 - e)^(0:60),
standing = c(1, rep(1 / (1 + e), 60)),
check.names = FALSE)
long <- data.frame(round = rep(trace$round, 2),
good = c(trace$`image scoring`, trace$standing),
rule = rep(c("image scoring", "standing"), each = nrow(trace)))
ggplot(long, aes(round, good, colour = rule)) +
geom_line(linewidth = 0.9) +
scale_colour_manual(values = c("image scoring" = "#b5534e", standing = "#275139")) +
labs(x = "Round", y = "Average good reputation", colour = NULL) +
theme_minimal(base_size = 12) + theme(legend.position = "top")
```
## Resisting defectors, and the free rider behind them
A rule has to do two jobs. It must keep out defectors, and it must keep out unconditional helpers, who cooperate without ever paying the cost of assessment and so erode discrimination from within. That second threat is the second-order free rider problem, and it is where standing and stern judging part company.
```{r}
#| label: invasion
for (r in names(rules)) {
z <- equilibrium(r, c(DISC = 0.99, ALLD = 0.01))
cat(sprintf("%-14s DISC %.3f ALLD %.3f\n", r, z$payoff["DISC"], z$payoff["ALLD"]))
}
```
Under standing and stern judging a rare defector earns 0.158 against residents on 1.792, so it is repelled hard. Under image scoring and shunning both payoffs are zero, which counts as resistance only in the sense that there is nothing left to invade.
```{r}
#| label: free-rider
for (r in names(rules)) {
z <- equilibrium(r, c(DISC = 0.90, ALLC = 0.10))
cat(sprintf("%-14s ALLC good %.3f DISC %.3f ALLC %.3f gap %+.4f\n",
r, z$good["ALLC"], z$payoff["DISC"], z$payoff["ALLC"],
z$payoff["DISC"] - z$payoff["ALLC"]))
}
```
Stern judging punishes the free rider ten times harder than standing does: a gap of +0.1773 against +0.0452. The reason is in the reputations. Stern judging demands that a donor refuse a bad recipient, so an unconditional helper is recorded as bad every time it helps one, dropping to 0.903 while discriminators sit at 0.952. Standing merely excuses refusal, so an unconditional helper keeps a clean record and the selection against it is weak.
Under image scoring the sign flips: the gap is negative, and the reason is worth staring at.
```{r}
#| label: reputations
for (r in c("image scoring", "standing", "stern judging")) {
z <- equilibrium(r, c(DISC = 0.90, ALLC = 0.10))
z2 <- equilibrium(r, c(DISC = 0.90, ALLD = 0.10))
cat(sprintf("%-14s DISC good %.3f ALLC good %.3f ALLD good %.3f\n",
r, z$good["DISC"], z$good["ALLC"], z2$good["ALLD"]))
}
```
Under image scoring the discriminator's own reputation, 0.622, sits below the unconditional helper's 0.950. Being discriminating is what damages it, because every justified refusal is recorded as a defection. A rule that penalises the behaviour it is supposed to reward cannot support cooperation, whatever the value of `q`.
Ohtsuki and Iwasa (2004, 2006) searched the space of second-order rules systematically and found that only eight combinations of assessment rule and action rule are evolutionarily stable, the set now called the leading eight. Standing and stern judging are both in it; image scoring and shunning are not.
## An honest limit
Standing and stern judging tie on cooperation here, and stern judging wins on the free rider. That is not the end of the comparison. Both rules leave defectors with a good reputation 12.7 per cent of the time, because a defector who refuses another defector is recorded as behaving correctly. Refusing everyone launders your record whenever you happen to draw a bad partner, and no second-order rule can close that gap without also punishing honest discrimination.
The larger limit is that every number above assumes one shared public record: everyone observes the same events and reaches the same verdict. Drop that and the ranking changes, sharply and in a direction these equilibria do not hint at. The [next post](../public-and-private-reputation/) works through what happens when opinions are private.
## Related tutorials
- [Indirect reciprocity and reputation](../indirect-reciprocity-and-reputation/)
- [Public and private reputation](../public-and-private-reputation/)
- [Checking a reputation model](../checking-a-reputation-model/)
- [Strategies in the iterated game](../strategies-in-the-iterated-game/)
## References
Nowak MA, Sigmund K 1998. Nature 393(6685):573-577 (10.1038/31225)
Ohtsuki H, Iwasa Y 2004. Journal of Theoretical Biology 231(1):107-120 (10.1016/j.jtbi.2004.06.005)
Ohtsuki H, Iwasa Y 2006. Journal of Theoretical Biology 239(4):435-444 (10.1016/j.jtbi.2005.08.008)
Panchanathan K, Boyd R 2003. Journal of Theoretical Biology 224(1):115-126 (10.1016/S0022-5193(03)00154-1)
Leimar O, Hammerstein P 2001. Proceedings of the Royal Society B 268(1468):745-753 (10.1098/rspb.2000.1573)
Sugden R 1986. The Economics of Rights, Co-operation and Welfare. Blackwell. ISBN 978-0631144496