Pico Fermi Bagels is a math/logic game commonly taught to schoolchildren to help them develop deductive reasoning skills.

Rules:
Player One (or the computer) thinks of a three digit number with each digit different (that means 666 isn't valid, kiddies), and records it on a hidden piece of paper if they are not the type to be trusted.

Player Two must now guess at what the number is. Player One must answer each guess with a combination of three responses:

1. pico: One digit is correct but in the wrong place.
2. fermi: One digit is correct and in the right place.
3. bagels: No digits are correct.

If Player One is thinking of 123 and Player Two guesses 321, Player One would answer Pico, Pico, Fermi, signaling all three digits correct with two in the wrong places. If player Two guesses 534 the answer would be Pico, signaling one digit correct and in the wrong place. If Player Two guesses 678, the answer would be Bagels. If Player Two guesses 123 the answer would be Fermi, Fermi, Fermi signaling a correct answer and the game is over. Then Player One and Two trade places ;-)

House rules determine whether zero can be used or not. Decreasing the number of choices obviously makes the game easier. Hardcore players can test their skill by playing with a number larger than 3 digits. Have Fun!

And by the way,
Pico is the SI prefix meaning one trillionth (10-12 )
Enrico Fermi was a nuclear physicist, and
Bagels are tasty!

Anark says Re: Pico Fermi Bagels a "fermi" is also a unit used in nuclear physics, 10^-15 meter, the approximate radius of an atomic nucleus. So a "pico fermi bagel" would measure 10^-27 meters and be undetectable in the largest acclerators:-)

Here is some free as in speech and as in beer HTML/Javascript code that will let you play in your browser. Copyright information is in the HTML body at the very bottom.

```
<!--<BASE HREF="http://bigweb.misty.com/weyer/js/pfb.htm">-->
<!--JS implementation notes.

NN 2.0 lacks Math.random, xxx.toString(). hangs sometimes after changing # of digits, e.g., 2 back to 3?
NN 3.0. substr? use substring. "".indexOf ; test .length first
AOL 3.0 lacks "BUTTON", events?
MSIE doesn't do BODY onload? rdig not an object. <undef not a #> in line 106
test w/ older/other Win AOL 4.0; NN 3.0?
it would be nice to set MAXLENGTH for guess fields dynamically but not suppored in JS

//debug
//var msgWindow=window.open("")
//msgWindow.document.write(""xx" + "<BR>");
-->

<script>
<!--
//constants
//var kMinDigits = 2; // unused currently

// declare global vars. reset in initGame
var NumDigits = 3;
var AllowZero = true;

//getRand
function getRand(lo,hi) {
if (Math.random == null) { // NN 2.0
var dt = new Date();
return lo + ((dt.getMinutes() * dt.getSeconds()) % (hi+1-lo));
};
return lo + Math.floor(Math.random() * (hi+1-lo));
}

//getSelNum
function getSelNum(sel) {
return parseInt(sel.options[sel.selectedIndex].text);
}

//giveUp
function giveUp() {
}

//newGame
function newGame(arg) {
//Answer = "";	// prevent some results?
document.PFB.guess1.focus();	// reset cursor to 1st guess
var elems = document.PFB.elements, i, elem;
for (i = 0; i < elems.length; i++) {
elem = elems[i];
if ((elem.name.indexOf("guess") == 0) || (elem.name.indexOf("result") == 0)) {
elem.value = "";
};
};
}

var i, rch, rdig, z9 = 9, dupDig, z0 = (AllowZero) ? 0 : 1;

for (i=0; i < NumDigits; i++) {
// generate new digit in range, non-duplicate
dupDig = true;
while (dupDig) {
rdig = getRand(z0,z9); // 0|1 - 9
rch = "0123456789".substring(rdig,rdig+1); // NN 3.0 no substr? rdig.toString();

if (Answer.length == 0 || Answer.indexOf(rch) < 0)	// not present? .length for NN 3.0
dupDig = false;
};

// squeeze range to elim some dups
if (rdig == z0)
z0++;
else if (rdig == z9)
z9--;
};
}

//evalGuess
function evalGuess(fguess, fresult) {
var guess = fguess.value;	// trim whitespace?
if (guess == "")
return;

initGame(false);	// e.g., IE5 didn't do onload?

var i, fermi = 0, pico = 0, g1, result = "";

// check length of guess
if (guess.length != NumDigits)
result = NumDigits + " digits only";
else {
for (i=0; i < NumDigits; i++) {
g1 = guess.substring(i,i+1);	// NN 3.0
if (("0123456789".indexOf(g1) < 0) || (g1 == "0" && !AllowZero))
result += g1 + " not valid; ";
else if (i+1 < NumDigits && guess.indexOf(g1,i+1) > i)
result += g1 + " duplicate; ";
fermi++;
pico++;
};
};

if (result == "") {
if (pico==0 && fermi==0)
result = "Bagels";
else {
for (i=0; i < fermi; i++)
result += "Fermi ";
if (fermi == NumDigits)
result += "!!!";

else {
for (i=0; i < pico; i++)
result += "Pico ";
};
}
};
fresult.value = result;
// popup 'win' except recursive problem with alert and onFocus
}

//initGame
function initGame(ng) {
// set params to form values (init/reload)
AllowZero = document.PFB.fAllowZero.checked;
NumDigits = getSelNum(document.PFB.fNumDigits);
if (ng)
newGame(0);
}
//-->
</SCRIPT>

<!--NS implementation notes.
only a few 2.x dependencies/optimizations.
I couldn't test on 1.x due to heap limitations (possibly reduce NUMFIELDS?)
-->

<meta NAME="DATA.giveUp" CONTENT="func(fdata)
begin
/*alternatively, use protoFlash:flashText from xvabbmet.htm example*/
&quot;Pico Fermi Bagels&quot;,
NIL;	/*avoid RESET*/
end">

<!--borrowed from dynatext.htm example-->
viewClass: 81, 	/*clParagraphView*/
NAME:	nil,
LABEL:	&quot;&quot;,
VALUE:	&quot;&quot;,
HEIGHT: 12,
viewLineSpacing: 12,
/*is it possible to specify that this doesn't participate in tab order?*/

viewSetupFormScript: func()
begin
self.text := :GetData(NAME);
if StrFilled(LABEL)
then begin
self.styles := [
StrLen(LABEL)+2, @91,	/*ROM_fontSystem12Bold*/
StrLen(text), @90,
];
text := LABEL &amp; \$: &amp;&amp; text;
end
else self.viewFont := @90;		/*ROM_fontSystem12*/
end,
}">

<meta NAME="DATA.myGetData" CONTENT="func(obj,name,cl)
begin
/*NC bug: BUTTON/SUBMIT missing GetData method*/
local val := obj:?GetData(name);
if not val
then val := obj:AllData(true).(name);

/*also, do some format/type conversion*/
if cl='aint	/*fNumDigits*/
then val := val[0];
if cl='int or cl='aint
then RIntToL(StringToNumber(val))
else if cl='checkbox
then Length(val) &gt; 0
else val;
end">

<meta NAME="DATA.setFocus" CONTENT="func(obj,fieldName)
/*undoc kludge: set caret to this field.
otherwise, refresh always resets to first field.
also, since form may be split across 2 book pages on some models/orientations,
set same field for both pages!*/
if fieldName
then GetGlobalVar('|NewtsCape:NewtsCape|).(Intern(GetVariable(obj,'ISBN)))._pageField1 := [fieldName,fieldName]
">

<meta NAME="DATA.newGame" CONTENT="func(obj)
begin
PlaySound(@314); /*ROM_poof*/
local i;
for i:=1 to :myGetData(obj,'NUMFIELDS, 'int)
do begin
obj:SetData(Intern('guess  &amp; i), &quot;&quot;);
obj:SetData(Intern('result &amp; i), &quot;&quot;);
end;
:setFocus(obj,'guess1);
obj:ScrollPage(0);	/*refresh*/
NIL;	/*avoid RESET of form data by buttons*/
end">

begin
local Answer := &quot;&quot;, i, rdig, z9 := 9;
local z0 := if :myGetData(obj,'fAllowZero,'checkbox) then 0 else 1;

for i:=1 to :myGetData(obj,'fNumDigits, 'aint)
do begin /*generate new digit in range, non-duplicate*/
repeat rdig := Random (z0,z9); /*0|1 - 9*/

/*squeeze range to avoid some dups*/
if rdig = z0
then z0 := z0+1
else if rdig = z9
then z9 := z9-1;
end;
end">

<meta NAME="DATA.evalGuess" CONTENT="func(obj, guess, resultName, nextField)
begin
local numDigits := :myGetData(obj, 'fNumDigits, 'aint);

if StrLen(guess) = numDigits
then begin
local i, gch, fermi := 0, pico := 0;
local zch := if :myGetData(obj,'fAllowZero,'checkbox) then \$0 else \$1;

/*check that guess is all digits, with none duplicated*/
for i:=0 to numDigits-1
do begin
gch := guess[i]; 	/*StrPos: gs := Substr(guess,i,1)*/
if (gch &lt; zch) or (gch &gt; \$9)	/*non-legal/non-digit?*/
then result := result &amp; \$( &amp; gch &amp; &quot; not &quot; &amp; zch &amp; &quot;-9)&quot;
else if CharPos(guess,gch,i+1)		/*dup: same char occurs later in guess?*/
then result := result &amp; \$( &amp; gch &amp; &quot; duplicate)&quot;

else if answer[i] = gch				/*match digit and position?*/
then fermi := fermi+1

then pico := pico+1;
end;

if StrLen(result)=0		/*no error?*/
then if fermi = 0 and pico = 0
then result := &quot;Bagels&quot;
else begin
for i:=1 to fermi do result := result &amp; &quot;Fermi &quot;;
for i:=1 to pico  do result := result &amp; &quot;Pico &quot;;
if fermi = numDigits then PlaySound(@102);	/*ROM_funbeep*/
end;

obj:SetData(resultName, result);
:setFocus(obj, nextField);
AddDeferredSend(obj,'ScrollPage, '[0]); /*delay refresh so earlier update can complete*/
end;
end">

<hr>

<form NAME="PFB" ACTION="none">
<input TYPE="HIDDEN" NAME="NUMFIELDS" VALUE="16">

<table SUMMARY="controls">
<tr>
<td WIDTH="25%">
<input TYPE="BUTTON" NAME="newgameBut" onClick="newGame(0)" SCRIPT="func(fdata) :BookData():newGame(self)" VALUE="New Game">

<td WIDTH="25%">
<input TYPE="CHECKBOX" NAME="fAllowZero" onClick="newGame(AllowZero = checked)" SCRIPT="func(val) :BookData():newGame(self)" CHECKED>Allow Zero?

<td WIDTH="25%"># Digits:
<select NAME="fNumDigits" onChange="newGame(NumDigits = getSelNum(this))" SCRIPT="func(txt) :BookData():newGame(self)">
<!--NumDigits = kMinDigits + selectedIndex-->
<option>2<!--kMinDigits-->
<option SELECTED>3
<option>4
<option>5
<option>6<!--guess.MAXLENGTH,SIZE-->
</select>

<td WIDTH="25%"><input TYPE="BUTTON" NAME="giveupBut" onClick="giveUp()" SCRIPT="func(fdata) :BookData():giveUp(fdata)" VALUE="Give Up">
</table>

<hr>

<table SUMMARY="guesses and results">
<tr><th WIDTH="25%">Guess<th WIDTH="75%">Result

<tr><td><tt>&nbsp;1.</tt>
<input TYPE="TEXT" NAME="guess1"  SIZE="6"  MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result1,'guess2)">
<td><input TYPE="TEXT" NAME="result1" SIZE="40" onFocus="evalGuess(this.form.guess1,this)" VALUE_TYPE="TEXTREADONLY">

<tr><td><tt>&nbsp;2.</tt>
<input TYPE="TEXT" NAME="guess2"  SIZE="6" MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result2,'guess3)">
<td><input TYPE="TEXT" NAME="result2" SIZE="40" onFocus="evalGuess(this.form.guess2,this)" VALUE_TYPE="TEXTREADONLY">

<tr><td><tt>&nbsp;3.</tt>
<input TYPE="TEXT" NAME="guess3"  SIZE="6" MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result3,'guess4)">
<td><input TYPE="TEXT" NAME="result3" SIZE="40" onFocus="evalGuess(this.form.guess3,this)" VALUE_TYPE="TEXTREADONLY">

<tr><td><tt>&nbsp;4.</tt>
<input TYPE="TEXT" NAME="guess4"  SIZE="6" MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result4,'guess5)">
<td><input TYPE="TEXT" NAME="result4" SIZE="40" onFocus="evalGuess(this.form.guess4,this)" VALUE_TYPE="TEXTREADONLY">

<tr><td><tt>&nbsp;5.</tt>
<input TYPE="TEXT" NAME="guess5"  SIZE="6" MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result5,'guess6)">
<td><input TYPE="TEXT" NAME="result5" SIZE="40" onFocus="evalGuess(this.form.guess5,this)" VALUE_TYPE="TEXTREADONLY">

<tr><td><tt>&nbsp;6.</tt>
<input TYPE="TEXT" NAME="guess6"  SIZE="6" MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result6,'guess7)">
<td><input TYPE="TEXT" NAME="result6" SIZE="40" onFocus="evalGuess(this.form.guess6,this)" VALUE_TYPE="TEXTREADONLY">

<tr><td><tt>&nbsp;7.</tt>
<input TYPE="TEXT" NAME="guess7"  SIZE="6" MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result7,'guess8)">
<td><input TYPE="TEXT" NAME="result7" SIZE="40" onFocus="evalGuess(this.form.guess7,this)" VALUE_TYPE="TEXTREADONLY">

<tr><td><tt>&nbsp;8.</tt>
<input TYPE="TEXT" NAME="guess8"  SIZE="6" MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result8,'guess9)">
<td><input TYPE="TEXT" NAME="result8" SIZE="40" onFocus="evalGuess(this.form.guess8,this)" VALUE_TYPE="TEXTREADONLY">

<tr><td><tt>&nbsp;9.</tt>
<input TYPE="TEXT" NAME="guess9"  SIZE="6" MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result9,'guess10)">
<td><input TYPE="TEXT" NAME="result9" SIZE="40" onFocus="evalGuess(this.form.guess9,this)" VALUE_TYPE="TEXTREADONLY">

<tr><td><tt>10.</tt>
<input TYPE="TEXT" NAME="guess10"  SIZE="6" MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result10,'guess11)">
<td><input TYPE="TEXT" NAME="result10" SIZE="40" onFocus="evalGuess(this.form.guess10,this)" VALUE_TYPE="TEXTREADONLY">

<tr><td><tt>11.</tt>
<input TYPE="TEXT" NAME="guess11"  SIZE="6" MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result11,'guess12)">
<td><input TYPE="TEXT" NAME="result11" SIZE="40" onFocus="evalGuess(this.form.guess11,this)" VALUE_TYPE="TEXTREADONLY">

<tr><td><tt>12.</tt>
<input TYPE="TEXT" NAME="guess12"  SIZE="6" MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result12,'guess13)">
<td><input TYPE="TEXT" NAME="result12" SIZE="40" onFocus="evalGuess(this.form.guess12,this)" VALUE_TYPE="TEXTREADONLY">

<tr><td><tt>13.</tt>
<input TYPE="TEXT" NAME="guess13"  SIZE="6" MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result13,'guess14)">
<td><input TYPE="TEXT" NAME="result13" SIZE="40" onFocus="evalGuess(this.form.guess13,this)" VALUE_TYPE="TEXTREADONLY">

<tr><td><tt>14.</tt>
<input TYPE="TEXT" NAME="guess14"  SIZE="6" MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result14,'guess15)">
<td><input TYPE="TEXT" NAME="result14" SIZE="40" onFocus="evalGuess(this.form.guess14,this)" VALUE_TYPE="TEXTREADONLY">

<tr><td><tt>15.</tt>
<input TYPE="TEXT" NAME="guess15"  SIZE="6" MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result15,'guess16)">
<td><input TYPE="TEXT" NAME="result15" SIZE="40" onFocus="evalGuess(this.form.guess15,this)" VALUE_TYPE="TEXTREADONLY">

<tr><td><tt>16.</tt>
<input TYPE="TEXT" NAME="guess16"  SIZE="6" MAXLENGTH="6" VALUE_TYPE="INT" SCRIPT="func(txt) :BookData():evalGuess(self,txt,'result16,nil)">
<td><input TYPE="TEXT" NAME="result16" SIZE="40" onFocus="evalGuess(this.form.guess16,this)" VALUE_TYPE="TEXTREADONLY">
</table>
</form>
<script>
<!--
initGame(true);
//-->
</SCRIPT>
<p>
<p>
This version is free, and can be downloaded locally and mirrored in its entirety.
If you copy/modify/improve the code, I would appreciate an acknowledgment