Suppose someone hands you printsZero(P) — a magic analyzer that, for any program P, returns YES if P prints 0 and NO if it does not. Below you can play the role of the saboteur. Build a tricky program that uses the analyzer against itself, then run it.
<p class="hint">Pretend a perfect analyzer <code>printsZero(P)</code> exists. We build a
program <b>Trickster</b> that asks the analyzer about <i>itself</i> and then does the
<b>opposite</b>. Pick what the analyzer claims, then run Trickster and watch the
analyzer become wrong — every single time.</p>
<div class="box">
<div class="label">The analyzer’s verdict on Trickster:</div>
<div class="choices">
<button id="say-yes" type="button">printsZero(Trickster) = YES</button>
<button id="say-no" type="button" class="ghost">printsZero(Trickster) = NO</button>
</div>
</div>
<pre class="code" id="code"></pre>
<button id="run" type="button" class="run" disabled>Run Trickster</button>
<div class="status" id="status">Pick a verdict to begin.</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; }
code { background: #eef2f6; padding: .05rem .3rem; border-radius: 4px; font-family: ui-monospace, monospace; }
.box { background: #f4f7fa; border: 1px solid #d6e0e8; border-radius: 10px; padding: .7rem .8rem; margin-bottom: .7rem; }
.label { font-size: .85rem; font-weight: 600; color: #1d3557; margin-bottom: .5rem; }
.choices { display: flex; gap: .5rem; flex-wrap: wrap; }
pre.code { background: #1d2733; color: #e7eef5; padding: .8rem 1rem; border-radius: 8px;
font: 13px/1.5 ui-monospace, monospace; overflow-x: auto; margin: 0 0 .7rem;
white-space: pre; }
pre.code .kw { color: #7fb3ff; }
pre.code .hl { color: #ffd479; font-weight: 700; }
pre.code .cm { color: #8aa0b2; }
.status { font-size: 1rem; font-weight: 600; margin: .6rem 0 0; min-height: 1.4em; line-height: 1.4; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
button { font: 600 13px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button.run { background: #0a7d33; border-color: #086628; margin-bottom: .2rem; }
button:disabled { opacity: .45; cursor: not-allowed; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Rice’s theorem (Henry Gordon Rice, 1953): every NON-TRIVIAL semantic property
// of programs is UNDECIDABLE. Here the property is "prints 0". We assume a perfect
// analyzer printsZero(P) exists and derive a contradiction via self-reference.
//
// program Trickster():
// if printsZero(Trickster) == YES: stay silent // -> does NOT print 0
// else: print(0) // -> DOES print 0
//
// Whatever the analyzer claims about Trickster, Trickster does the opposite.
// So no correct printsZero can exist. The same trick works for any non-trivial property, which is exactly Rice’s theorem.
let claim = null; // 'YES' or 'NO' — what the analyzer asserts about Trickster
function renderCode(){
const c = document.getElementById('code');
c.innerHTML =
`<span class="cm">// The saboteur program</span>
<span class="kw">program</span> Trickster():
verdict = printsZero(Trickster) <span class="cm">// ask analyzer about itself</span>
<span class="kw">if</span> verdict == <span class="hl">YES</span>:
<span class="cm">// analyzer said it prints 0, so do the opposite:</span>
stay_silent() <span class="cm">// prints nothing</span>
<span class="kw">else</span>:
<span class="cm">// analyzer said it does NOT, so do the opposite:</span>
print(0)`;
}
function setClaim(v){
claim = v;
document.getElementById('run').disabled = false;
const s = document.getElementById('status');
s.className = 'status';
s.textContent = fmt(`Analyzer claims printsZero(Trickster) = %v%. Now run Trickster.`, { v: v });
}
// Returns what Trickster ACTUALLY does, given the analyzer’s claim.
function actuallyPrintsZero(verdict){
// if verdict YES -> stays silent -> false ; if NO -> prints 0 -> true
return verdict === 'NO';
}
function run(){
if(!claim) return;
const actual = actuallyPrintsZero(claim); // true = it really prints 0
const claimed = (claim === 'YES'); // what analyzer asserted
const s = document.getElementById('status');
const did = actual ? `it DID print 0` : `it printed NOTHING`;
if(actual === claimed){
s.className = 'status ok';
s.textContent = fmt(`Analyzer was right?! (%did%) — impossible, check the logic.`, { did: did });
} else {
s.className = 'status bad';
s.textContent = fmt(`✗ Analyzer said %claim%, but %did%. The analyzer is WRONG. No matter which verdict it gives, Trickster contradicts it — so printsZero cannot exist.`, { claim: claim, did: did });
}
console.log(`claim:`, claim, '| Trickster actually prints 0:', actual, '| analyzer correct:', actual === claimed);
}
document.getElementById('say-yes').addEventListener('click',()=>setClaim('YES'));
document.getElementById('say-no').addEventListener('click',()=>setClaim('NO'));
document.getElementById('run').addEventListener('click',run);
renderCode();
<p class="hint">Pretend a perfect analyzer <code>printsZero(P)</code> exists. We build a
program <b>Trickster</b> that asks the analyzer about <i>itself</i> and then does the
<b>opposite</b>. Pick what the analyzer claims, then run Trickster and watch the
analyzer become wrong — every single time.</p>
<div class="box">
<div class="label">The analyzer’s verdict on Trickster:</div>
<div class="choices">
<button id="say-yes" type="button">printsZero(Trickster) = YES</button>
<button id="say-no" type="button" class="ghost">printsZero(Trickster) = NO</button>
</div>
</div>
<pre class="code" id="code"></pre>
<button id="run" type="button" class="run" disabled>Run Trickster</button>
<div class="status" id="status">Pick a verdict to begin.</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; }
code { background: #eef2f6; padding: .05rem .3rem; border-radius: 4px; font-family: ui-monospace, monospace; }
.box { background: #f4f7fa; border: 1px solid #d6e0e8; border-radius: 10px; padding: .7rem .8rem; margin-bottom: .7rem; }
.label { font-size: .85rem; font-weight: 600; color: #1d3557; margin-bottom: .5rem; }
.choices { display: flex; gap: .5rem; flex-wrap: wrap; }
pre.code { background: #1d2733; color: #e7eef5; padding: .8rem 1rem; border-radius: 8px;
font: 13px/1.5 ui-monospace, monospace; overflow-x: auto; margin: 0 0 .7rem;
white-space: pre; }
pre.code .kw { color: #7fb3ff; }
pre.code .hl { color: #ffd479; font-weight: 700; }
pre.code .cm { color: #8aa0b2; }
.status { font-size: 1rem; font-weight: 600; margin: .6rem 0 0; min-height: 1.4em; line-height: 1.4; }
.status.ok { color: #0a7d33; }
.status.bad { color: #c92f3c; }
button { font: 600 13px system-ui, sans-serif; padding: .45rem .9rem; border: 1px solid #1d3557;
background: #1d3557; color: #fff; border-radius: 8px; cursor: pointer; }
button.ghost { background: #fff; color: #1d3557; }
button.run { background: #0a7d33; border-color: #086628; margin-bottom: .2rem; }
button:disabled { opacity: .45; cursor: not-allowed; }
const fmt = (s, v) => s.replace(/%(\w+)%/g, (_, k) => String(v[k] ?? ''));
// Rice’s theorem (Henry Gordon Rice, 1953): every NON-TRIVIAL semantic property
// of programs is UNDECIDABLE. Here the property is "prints 0". We assume a perfect
// analyzer printsZero(P) exists and derive a contradiction via self-reference.
//
// program Trickster():
// if printsZero(Trickster) == YES: stay silent // -> does NOT print 0
// else: print(0) // -> DOES print 0
//
// Whatever the analyzer claims about Trickster, Trickster does the opposite.
// So no correct printsZero can exist. The same trick works for any non-trivial property, which is exactly Rice’s theorem.
let claim = null; // 'YES' or 'NO' — what the analyzer asserts about Trickster
function renderCode(){
const c = document.getElementById('code');
c.innerHTML =
`<span class="cm">// The saboteur program</span>
<span class="kw">program</span> Trickster():
verdict = printsZero(Trickster) <span class="cm">// ask analyzer about itself</span>
<span class="kw">if</span> verdict == <span class="hl">YES</span>:
<span class="cm">// analyzer said it prints 0, so do the opposite:</span>
stay_silent() <span class="cm">// prints nothing</span>
<span class="kw">else</span>:
<span class="cm">// analyzer said it does NOT, so do the opposite:</span>
print(0)`;
}
function setClaim(v){
claim = v;
document.getElementById('run').disabled = false;
const s = document.getElementById('status');
s.className = 'status';
s.textContent = fmt(`Analyzer claims printsZero(Trickster) = %v%. Now run Trickster.`, { v: v });
}
// Returns what Trickster ACTUALLY does, given the analyzer’s claim.
function actuallyPrintsZero(verdict){
// if verdict YES -> stays silent -> false ; if NO -> prints 0 -> true
return verdict === 'NO';
}
function run(){
if(!claim) return;
const actual = actuallyPrintsZero(claim); // true = it really prints 0
const claimed = (claim === 'YES'); // what analyzer asserted
const s = document.getElementById('status');
const did = actual ? `it DID print 0` : `it printed NOTHING`;
if(actual === claimed){
s.className = 'status ok';
s.textContent = fmt(`Analyzer was right?! (%did%) — impossible, check the logic.`, { did: did });
} else {
s.className = 'status bad';
s.textContent = fmt(`✗ Analyzer said %claim%, but %did%. The analyzer is WRONG. No matter which verdict it gives, Trickster contradicts it — so printsZero cannot exist.`, { claim: claim, did: did });
}
console.log(`claim:`, claim, '| Trickster actually prints 0:', actual, '| analyzer correct:', actual === claimed);
}
document.getElementById('say-yes').addEventListener('click',()=>setClaim('YES'));
document.getElementById('say-no').addEventListener('click',()=>setClaim('NO'));
document.getElementById('run').addEventListener('click',run);
renderCode();