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;
var Answer = "";

//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() {
alert("Pico Fermi Bagels\nanswer: " + Answer);
}

//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 = "";
		};
	};
generateAnswer();
}

//generateAnswer
function generateAnswer() {
Answer = "";	// new 'blank' answer
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;
		};

	Answer += rch; // add new digit

	// 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;

if (Answer == "")
	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; ";
		else if (Answer.charAt(i) == g1.charAt(0))
			fermi++;
		else if (Answer.indexOf(g1) >= 0)
			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);
else generateAnswer();
}
//-->
</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?)
GetGlobals for GetGlobalVar, StrPos for CharPos, AddDeferredAction for AddDeferredSend
-->

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

<!--borrowed from dynatext.htm example-->
<meta NAME="DATA.TEXTREADONLY" CONTENT="{
viewClass: 81, 	/*clParagraphView*/
viewFlags: 1, 	/*vReadOnly*/
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);
	:generateAnswer(obj);
	obj:ScrollPage(0);	/*refresh*/
	NIL;	/*avoid RESET of form data by buttons*/
end">

<meta NAME="DATA.generateAnswer" CONTENT="func(obj)
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*/
		until not CharPos(Answer, &quot;0123456789&quot;[rdig], 0);

		Answer := Answer &amp; rdig;

		/*squeeze range to avoid some dups*/
		if rdig = z0
		then z0 := z0+1
		else if rdig = z9
		then z9 := z9-1;
		end;
	obj:SetData('ANSWER, answer);
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;

		local result := &quot;&quot;, answer := obj:GetData('ANSWER);
		if not StrFilled(answer)
		then answer := :generateAnswer(obj);

		/*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

			else if CharPos(answer,gch,0)		/*digit anywhere in answer?*/
			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">

</head><body>
<hr>


<form NAME="PFB" ACTION="none">
<input TYPE="HIDDEN" NAME="ANSWER" VALUE="">
<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>
<b>Enter your response and press tab to see the computer's answer!</b>
<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
of this version and link to this page.
<p>&copy; 1999-2000, S. Weyer. All Rights Reserved Worldwide.
<h2><a NAME="author">Contacting Author</a></h2>
<ul>
<li><a HREF="http://members.bellatlantic.net/~sweyer/index.htm">Steve's Personal Page</a>
<li><a HREF="mailto:weyer@kagi.com">weyer@kagi.com</a>
</ul>
</body></html>

Log in or registerto write something here or to contact authors.