Five voters rank three candidates A, B and C. The rule is plain plurality: only each ballot's top choice counts, and ties break in favor of A, then B, then C. First run the honest election and see who wins.
<p class="hint">Five voters rank three candidates. The rule is <b>plurality</b>:
only each ballot's top choice counts, ties break A > B > C. Voter 1 truly
ranks <b>C > A > B</b>. Run the honest vote, then let Voter 1 lie.</p>
<div id="ballots" class="ballots"></div>
<div class="result" id="result">Press a button to run the election.</div>
<div class="tally" id="tally"></div>
<div class="btns">
<button id="honest" type="button">Vote honestly</button>
<button id="strategic" type="button">Vote strategically</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .8rem; line-height: 1.45; }
.ballots { display: grid; gap: .4rem; margin: .4rem 0 .8rem; }
.ballot { display: flex; align-items: center; gap: .5rem; font-size: .92rem; }
.ballot .who { width: 78px; font-weight: 600; color: #1d3557; }
.ballot.you .who { color: #c92f3c; }
.pref { display: flex; gap: .35rem; }
.chip { width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;
font: 700 14px ui-monospace, monospace; border-radius: 7px; background: #e8eef3;
color: #1d3557; border: 1px solid #cdd9e3; }
.chip.top { background: #1d3557; color: #fff; border-color: #1d3557; }
.chip.lie { background: #c92f3c; color: #fff; border-color: #c92f3c; }
.result { font-size: 1rem; font-weight: 600; margin: .5rem 0; min-height: 1.4em; line-height: 1.4; }
.result.ok { color: #0a7d33; }
.result.bad { color: #c92f3c; }
.tally { font: 600 .9rem ui-monospace, monospace; color: #444; margin: .2rem 0 .7rem; min-height: 1.2em; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
#strategic { background: #c92f3c; border-color: #c92f3c; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Gibbard-Satterthwaite, made concrete. Three candidates A, B, C; rule is
// plurality (top choice only) with tie-break A > B > C. Voter 1's TRUE order is
// C > A > B. Honestly topping C lets B (their worst) win. By topping A instead —
// a lie — they make A (their middle choice) win, which they strictly prefer.
// The theorem (Gibbard 1973, Satterthwaite 1975): with 3+ outcomes, every non-dictatorial rule has such a manipulable profile. Proven impossible to avoid.
const CANDS = ['A','B','C'];
const TIEBREAK = { A:0, B:1, C:2 }; // lower index wins ties
// True preferences, top choice first:
const TRUE = [
['C','A','B'], // Voter 1 — the strategic one
['B','C','A'],
['B','A','C'],
['A','C','B'],
['C','B','A'],
];
const V0 = { C:0, A:1, B:2 }; // Voter 1's preference rank (0 = best)
function plurality(ballots){
const tally = { A:0, B:0, C:0 };
ballots.forEach(b => tally[b[0]]++);
let win = null;
for (const c of CANDS)
if (win === null || tally[c] > tally[win] ||
(tally[c] === tally[win] && TIEBREAK[c] < TIEBREAK[win])) win = c;
return { win, tally };
}
let mode = 'honest';
function ballots(){
if (mode === 'honest') return TRUE;
const b = TRUE.map(x => x.slice());
b[0] = ['A','C','B']; // Voter 1 lies: tops A instead of C
return b;
}
function render(){
const list = ballots();
const box = document.getElementById('ballots'); box.innerHTML = '';
list.forEach((b,i) => {
const row = document.createElement('div');
row.className = 'ballot' + (i===0 ? ' you' : '');
const who = document.createElement('span'); who.className = 'who';
who.textContent = i===0 ? 'Voter 1 ✦' : fmt('Voter %i%%v%', { i: i, v: 1 });
const pref = document.createElement('div'); pref.className = 'pref';
b.forEach((c,j) => {
const chip = document.createElement('span');
const lying = (i===0 && mode==='strategic' && j===0);
chip.className = 'chip' + (j===0 ? (lying ? ' lie' : ' top') : '');
chip.textContent = c;
pref.appendChild(chip);
});
row.appendChild(who); row.appendChild(pref); box.appendChild(row);
});
const { win, tally } = plurality(list);
document.getElementById('tally').textContent =
fmt('Top-choice tally — A:%A% B:%B% C:%C% → winner: %win%', { A: tally.A, B: tally.B, C: tally.C, win: win });
const res = document.getElementById('result');
if (mode === 'honest'){
res.className = 'result bad';
res.textContent = fmt('😔 Honest vote → %win% wins. That is Voter 1’s WORST candidate.', { win: win });
} else {
res.className = 'result ok';
const better = V0[win] < V0['B'];
res.textContent = fmt('😏 Strategic vote → %win% wins. Voter 1 lied (topped A, not C) and got their MIDDLE choice — %v%', { win: win, v: better ? 'strictly better than B!' : '??' });
}
console.log(fmt('mode=%mode%', { mode: mode }), fmt('winner=%win%', { win: win }), 'tally=', tally,
'voter1 outcome rank=', V0[win]);
}
document.getElementById('honest').addEventListener('click', () => { mode='honest'; render(); });
document.getElementById('strategic').addEventListener('click', () => { mode='strategic'; render(); });
render();
<p class="hint">Five voters rank three candidates. The rule is <b>plurality</b>:
only each ballot's top choice counts, ties break A > B > C. Voter 1 truly
ranks <b>C > A > B</b>. Run the honest vote, then let Voter 1 lie.</p>
<div id="ballots" class="ballots"></div>
<div class="result" id="result">Press a button to run the election.</div>
<div class="tally" id="tally"></div>
<div class="btns">
<button id="honest" type="button">Vote honestly</button>
<button id="strategic" type="button">Vote strategically</button>
</div>
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; color: #222; margin: 0; }
.hint { font-size: .9rem; color: #444; margin: 0 0 .8rem; line-height: 1.45; }
.ballots { display: grid; gap: .4rem; margin: .4rem 0 .8rem; }
.ballot { display: flex; align-items: center; gap: .5rem; font-size: .92rem; }
.ballot .who { width: 78px; font-weight: 600; color: #1d3557; }
.ballot.you .who { color: #c92f3c; }
.pref { display: flex; gap: .35rem; }
.chip { width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;
font: 700 14px ui-monospace, monospace; border-radius: 7px; background: #e8eef3;
color: #1d3557; border: 1px solid #cdd9e3; }
.chip.top { background: #1d3557; color: #fff; border-color: #1d3557; }
.chip.lie { background: #c92f3c; color: #fff; border-color: #c92f3c; }
.result { font-size: 1rem; font-weight: 600; margin: .5rem 0; min-height: 1.4em; line-height: 1.4; }
.result.ok { color: #0a7d33; }
.result.bad { color: #c92f3c; }
.tally { font: 600 .9rem ui-monospace, monospace; color: #444; margin: .2rem 0 .7rem; min-height: 1.2em; }
.btns { display: flex; gap: .5rem; flex-wrap: wrap; }
button { font: 600 14px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
#strategic { background: #c92f3c; border-color: #c92f3c; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Gibbard-Satterthwaite, made concrete. Three candidates A, B, C; rule is
// plurality (top choice only) with tie-break A > B > C. Voter 1's TRUE order is
// C > A > B. Honestly topping C lets B (their worst) win. By topping A instead —
// a lie — they make A (their middle choice) win, which they strictly prefer.
// The theorem (Gibbard 1973, Satterthwaite 1975): with 3+ outcomes, every non-dictatorial rule has such a manipulable profile. Proven impossible to avoid.
const CANDS = ['A','B','C'];
const TIEBREAK = { A:0, B:1, C:2 }; // lower index wins ties
// True preferences, top choice first:
const TRUE = [
['C','A','B'], // Voter 1 — the strategic one
['B','C','A'],
['B','A','C'],
['A','C','B'],
['C','B','A'],
];
const V0 = { C:0, A:1, B:2 }; // Voter 1's preference rank (0 = best)
function plurality(ballots){
const tally = { A:0, B:0, C:0 };
ballots.forEach(b => tally[b[0]]++);
let win = null;
for (const c of CANDS)
if (win === null || tally[c] > tally[win] ||
(tally[c] === tally[win] && TIEBREAK[c] < TIEBREAK[win])) win = c;
return { win, tally };
}
let mode = 'honest';
function ballots(){
if (mode === 'honest') return TRUE;
const b = TRUE.map(x => x.slice());
b[0] = ['A','C','B']; // Voter 1 lies: tops A instead of C
return b;
}
function render(){
const list = ballots();
const box = document.getElementById('ballots'); box.innerHTML = '';
list.forEach((b,i) => {
const row = document.createElement('div');
row.className = 'ballot' + (i===0 ? ' you' : '');
const who = document.createElement('span'); who.className = 'who';
who.textContent = i===0 ? 'Voter 1 ✦' : fmt('Voter %i%%v%', { i: i, v: 1 });
const pref = document.createElement('div'); pref.className = 'pref';
b.forEach((c,j) => {
const chip = document.createElement('span');
const lying = (i===0 && mode==='strategic' && j===0);
chip.className = 'chip' + (j===0 ? (lying ? ' lie' : ' top') : '');
chip.textContent = c;
pref.appendChild(chip);
});
row.appendChild(who); row.appendChild(pref); box.appendChild(row);
});
const { win, tally } = plurality(list);
document.getElementById('tally').textContent =
fmt('Top-choice tally — A:%A% B:%B% C:%C% → winner: %win%', { A: tally.A, B: tally.B, C: tally.C, win: win });
const res = document.getElementById('result');
if (mode === 'honest'){
res.className = 'result bad';
res.textContent = fmt('😔 Honest vote → %win% wins. That is Voter 1’s WORST candidate.', { win: win });
} else {
res.className = 'result ok';
const better = V0[win] < V0['B'];
res.textContent = fmt('😏 Strategic vote → %win% wins. Voter 1 lied (topped A, not C) and got their MIDDLE choice — %v%', { win: win, v: better ? 'strictly better than B!' : '??' });
}
console.log(fmt('mode=%mode%', { mode: mode }), fmt('winner=%win%', { win: win }), 'tally=', tally,
'voter1 outcome rank=', V0[win]);
}
document.getElementById('honest').addEventListener('click', () => { mode='honest'; render(); });
document.getElementById('strategic').addEventListener('click', () => { mode='strategic'; render(); });
render();
Watch Voter 1, whose true ranking is C > A > B. Honestly topping C, the worst candidate for them — B — wins. Now press Vote strategically: Voter 1 keeps their true feelings private and insists on A instead. C can't win for them anyway, and lifting A blocks B. The winner flips to A, Voter 1's middle choice — strictly better for them than B. They lied, and it paid.
Comments
Loading comments...