/* 
	dil.js
	Prisoners dilemma
	Dilemme des prisonniers

	Confrontation de populations de stratégies
		choix des stratégies, des durées
		altération par ajout de bruit
		Changement des payOffs
		possibilité de programmer sa stratégie (utilisateur)
		stratégies construites au hasard (F1 à F7)
	Évolution des populations (toutes les stratégies)
	Algorithmes génétiques (mutations, évolution des stratégies F1 à F7)

	(C) 2006 Jean-Paul Davalan <jpdvl@wanadoo.fr>
	http://perso.wanadoo.fr/jean-paul.davalan/jeux/minimax/dil/index.html

	license GPL
	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU Lesser General Public
	License (LGPL) as published by the Free Software Foundation; either
	version 2.1 of the License, or (at your option) any later version.

	This library is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	Lesser General Public License for more details.

	You should have received a copy of the GNU Lesser General Public
	License along with this library; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA,
	or see http://www.gnu.org/copyleft/lesser.html

*/
/*
	matrice P =[p_i,j] des paiements ou gains moyens par unité  
	l * p_i,j = gain de s_i contre s_j lors d\'une séquence de longueur l

	Vecteur X = [x_k] population X et effectifs x_k des stratégies s_k

	Gain de s_k à l\'étape k+1 par (rapport aux effectifs de l\'étape k)
   
	somme des R_k = p_k,i * x_k * x_i  (pour correction ajouter - p_k,k * x_k)
	la somme des gains est R = sum_k sum_i p_k,i * x_k * x_i 
*/
var fibonacci="0"
function fibnext(n) {
	while(fibonacci.length<n) {
		do {
		u=fibonacci;
      fibonacci=fibonacci.replace(/0/g,"a");
      fibonacci=fibonacci.replace(/1/g,"b");
   }while(u!=fibonacci);
   do {
      u=fibonacci;
      fibonacci=fibonacci.replace(/a/g,"1");
      fibonacci=fibonacci.replace(/b/g,"10");
   }while(u!=fibonacci);
  }
}
fibnext(100);

function fibo(r) {
   fibnext(r);
   return ((fibonacci.charAt(r)=="1")?true:false);
}

var jg=null;
        
function upk(t, r, k) {
	if(k>=r) {
		return true;
	}
	return t[t.length-1-k];
}

function up(t, r) { 
	//return upk(t, r, 0);
	return (r<1)? true : t[r-1];
}

function up2(t, r) { 
	return (r<2) ? true : t[r-2]; // upk(t, r, 2);
}

function up3(t, r) { 
	return (r<3) ? true : t[r-3]; // upk(t, r, 3);
}

var 	lngTable = new Array(),
	debutsTable = new Array(),
	stratTable = new Array(),
	scoresTable = new Array(),
	rangsTable = new Array(),
	FnGenet = new Array();


var minRetro = 1, maxRetro = 2;

var PauseGen=false;

function PauseFunc(){
	PauseGen = !PauseGen;
	if(PauseGen==true) {
		document.frm.pausef.value="Faire évoluer les stratégies génétiques";
	} else {
		document.frm.pausef.value="Arrêter l'évolution des stratégies génétiques";
	}
}
		
function stratMixte(j, k, n) {
	var probaChge = 0.05;
	for(var i=0; i<lngTable[j][2]; i++) {
		debutsTable[j][i] = (Math.random()<0.5) ? debutsTable[k][i] : debutsTable[n][i];
		if(Math.random()<probaChge) {
			debutsTable[j][i] = !debutsTable[j][i];
		}
	}
	var a = 1<<(lngTable[j][0]+lngTable[j][1]);
	for(var i=0; i<a; i++) {
		stratTable[j][i] = (Math.random()<0.5)? stratTable[k][i] : stratTable[n][i];
		if(Math.random()<probaChge) {
			stratTable[j][i] = !stratTable[j][i];
		}
	}
}

function stratAltere(j, k) {
	var 	probaChge = 0.05,  // 0.2 semble trop et 0.02 trop peu
		probaFavorable=0.7; // discrimination positive des gagnants !
	// 1er cas : longueurs des codes incompatibles, on remplace j par k altéré
	if(lngTable[j][0]!=lngTable[k][0] || lngTable[j][1]!=lngTable[k][1]) { // clone altéré de k
		delete(debutsTable[j]);
		delete(stratTable[j]);
		for(var i=0; i<3; i++) {
			lngTable[j][i]=lngTable[k][i];
		}
		delete(debutsTable[j]);
		debutsTable[j] = new Array();
		for(var i=0; i<debutsTable[k].length; i++) {
			debutsTable[j][i]=(Math.random()< probaChge)? !debutsTable[k][i] : debutsTable[k][i];
		}
		delete(stratTable[j]);
		stratTable[j] = new Array();
		for(var i=0; i<stratTable[k].length; i++) {
			stratTable[j][i]=(Math.random()< probaChge)? !stratTable[k][i] : stratTable[k][i];
		}
	}
	// les codes ont mêmes longueurs, on peut les mixer
	else {  // couple
		// la répartition moitié/moitié ne semble pas assez efficace
		for(var i=0; i<debutsTable[k].length; i++) {
			if (Math.random()< probaFavorable) {
				debutsTable[j][i] = debutsTable[k][i];
			}
			if(Math.random()< probaChge) {
				debutsTable[j][i] = !debutsTable[j][i];	
			}
		}
		for(var i=0; i<stratTable[k].length; i++) {
			if (Math.random()< 0.5) {
				stratTable[j][i] = stratTable[k][i];
			}
			if(Math.random()< probaChge) {
				stratTable[j][i] = !stratTable[j][i];
			}
		}
	}
}
var mSave=1, nSave=4;

function stratAleat() {
	var 	nn = new Array(), 
		pb = Math.random(),
		k = arguments[0];
	if(arguments.length<3) {
		//nn[0]=Math.floor(minRetro +(maxRetro-minRetro+1)*Math.random());
		//nn[1]=Math.floor(minRetro +(maxRetro-minRetro+1)*Math.random());
		nn[0]=1;
		nn[1]=4;
	} else {
		nn[0]=parseInt(arguments[1]);
		nn[1]=parseInt(arguments[2]);
	}
	nn[2]=Math.max(nn[0],nn[1]);
	
	if(lngTable[k] != null) {
		delete(lngTable[k]);
	}
	lngTable[k] = nn;
	var m = nn[2]
	var l = 1;
	for(var i=0; i<nn[0]+nn[1]; i++) {
		l *=2;
	}
	deb = new Array();
	for(var i=0; i<m; i++) {
		deb[i] = hasard2(pb);
	}
	if(debutsTable[k] != null) {
		delete(debutsTable[k]);
	}
	debutsTable[k]= deb;
	strt = new Array();
        for(var i=0; i<l; i++) {
		strt[i] = hasard2(pb);
	}
	if(stratTable[k] != null) {
		delete(stratTable[k]);
	}
	stratTable[k] = strt;
}

function reinitFunc() {
	var u, s = document.frm.mn.value;
	do {
		u = s;
		s = s.replace(/^\s+/,"");
		s = s.replace(/\s+$/,"");
		s = s.replace(/\s+/g," ");
	} while (u != s);
	t = s.split(/[\s\,\;]+/g);
	for(var i=0; i<t.length; i++) {
		t[i] = parseInt(t[i]);
	}
	if(t.length==0) {
		t[0]=2; t[1]=3;
	}
	if(t.length==1) {
		t[1]=t[0];
	}
	mSave=t[0];
	nSave=t[1];
	for(var i=0; i<Flen; i++) {
		stratAleat(i, t[0],t[1]);
	}
}

function initFunc() {
	if(arguments.length==2) {
		for(var i=0; i<Flen; i++) {
                	stratAleat(i, arguments[0], arguments[1]);
        	}
		document.frm.mn.value="";
		document.frm.mn.value = arguments[0]+" "+ arguments[1];
	} else {
		for(var i=0; i<Flen; i++) {
			stratAleat(i);
		}
	}
}

function reponse(k, u, v, r) {
	var n0 = lngTable[k][0], n1 = lngTable[k][1];
	if(r<n0 || r <n1) {
		return debutsTable[k][r];
	}
	else {
		var a = encode(u, v, r, n0, n1)
		return stratTable[k][a];
	}
}

/* Le mot est un nbre binaire binaire true:1 et false:0 
	u :    ...abcd
	v :    ...efgh        | 1er             | dernier
	donnent               v                 v
	dcba...hgfe... =    ( 0110101110000101001  par exemple)
			= x
 */

function encode(u, v, r, m, n) {
        var x = 0;                 
	for(var i=0; i<m; i++) { 
		x *= 2;
		x += u[r-1-i]?1 : 0;
	}
	for(var i=0; i<n; i++) {   
		x *= 2;
		x += v[r-1-i]?1 : 0;
	}
	//  [fin de u][fin de v] puis retourner et donner la valeur binaire
	//  avec Coopère = true = 1
	return x;
}

/* 	pour affichage seulement
	(pour le calcul c'est debutsTable ou stratTable qui sont utilisés)
*/
function codeGen(k) {
	var 	s1="", 
		s = 	"# NN :\n"+lngTable[k][0]+" "+lngTable[k][1]+"\n"+
			"# Début :\n";
	for(var i=0;i<debutsTable[k].length; i++) {
		s += (debutsTable[k][i]==true)?"C" : "D";
	}
	s +="\n# Code :\n";
	
	for(var i=0; i<stratTable[k].length; i++) {
		s1 += (stratTable[k][i]==true)? "C" : "D";
		if(s1.length>=100 || i==stratTable[k].length-1) {
			s += s1+"\n";
			s1="";
		}
	}
	return s;
}

// tables nn retroact dd début cc code
/*
function codeGen2Algo(nn, dd, cc) {

}
*/	

function litCodeGen(k, s) {
	var t=	s.split(/\n/g),
		v="",
		a="", b="", c="", d;
	for(var i=0; i<t.length; i++) {
		var u = t[i];
		if(u.charAt(0)=="#") {
			v = u.split(/\s+/g)[1];
		}
		else {
			u=u.replace(/\s+/g,"");
			switch(v) {
			 case "NN":
				a += u+" ";
				break;
			 case "Début":
				b += u;
				break;
			 case "Code":
				c += u;
				break;
			 default:
				break;
			}
		}
	}
	do {
		d=a;
		a=a.replace(/^\s+/,"");
		a=a.replace(/\s+$/,"");
		a=a=a.replace(/s+/g," ");
	}while(d!=a);
	var ll = a.split(/\s+/g);
	lngTable[k][0] = parseInt(ll[0]);
	lngTable[k][1] = parseInt(ll[1]);
	lngTable[k][2] = Math.max(lngTable[k][0],lngTable[k][1]);
	if(debutsTable[k]) {
		delete(debutsTable[k]);
	}
	debutsTable[k]=new Array();
	for(var i=0; i<b.length; i++) {
		debutsTable[k][i]= (b.charAt(i)=="C")?true : false;
	}
	if(stratTable[k]) {
		delste(stratTable[k]);
	}
	stratTable[k] = new Array();
	for(var i=0; i<c.length; i++) {
		stratTable[k][i]= (c.charAt(i)=="C")?true : false;
	}
}
			
var TextAreaSave;
var idG=null;
var idGN=null;

/* 
    inversion du sens ?
    	C C D C D D C D...
	0 1 2 3 4 5 6 7 ...

*/ 
function toChn(t) {
	var s="";
	for(var i=0; i<t.length;i++) {
		s += (t[i])?"C" : "D";
	}
	return s;
}

function afficheCodesGen() {
	var 	s="";
	if(idG) {
		clearTimeout(idG);
		document.frm.ecp.value=TextAreaSave;
	}
        if(idGN) {
                clearTimeout(idGN);
                document.getElementById("CodesGen").innerHTML="";
        }
	TextAreaSave = document.frm.ecp.value;
	for(var i=0; i<Flen; i++) {
		s += "# F"+(i+1)+"\n"+codeGen(i);
		if(functJsMode) {
		s += "\n --- traduction en javascript :\n"; //+"\n# -----------------------------------\n";
		FnGenet[i]=GentoFunctionString(lngTable[i][0], lngTable[i][1], toChn(debutsTable[i]), toChn(stratTable[i]));
		s += '<span style="cursor:pointer;color:purple;" onclick="document.frm.strat.value=FnGenet['+i+'];">[F'+(i+1)+'-> utilisateur]</span>\n'
		s += FnGenet[i];
		}
		s +="\n# -----------------------------------\n";
	}
	document.getElementById("CodesGen").innerHTML="<pre>"+s+"</pre>";
	document.location.href="#CG";
	idGN=setTimeout('document.getElementById("CodesGen").innerHTML="";document.location.href="#AR";clearTimeout(idGN);idGN=null;',60000);
	//document.frm.ecp.value = s;
	//idG = setTimeout("document.frm.ecp.value=TextAreaSave;clearTimeout(idG);idG=null;",15000);
}

/*
    u = 0011...     a: 00     b: 01      c: 10      d: 11
    v = 0101...     aa ab ac ad ba bb bc bd ca cb cc cd da db dc dd
        abcd        

    sur le dernier coup : 2^4 = 16 possibilités sans compter le 1er coup
                          2 * 2^4
            k derniers      2^k  * 4^k = 8^k
*/

var funcNames = [
        "gentille", "méchante", "tft", "méfiante",
	"loyale", "rancunière", "majo mou",	"majo dur",
	"tf2t", "tf2c", "per ct", "per tc",
	"per cct", "per ttc", "pavlov","antitf2t",
        "antidd","lunatique",
        "fibonacci", "anti-fibo", "fibo=", "fibo M",
        "utilisateur", "F1","F2","F3","F4","F5","F6","F7"
];
/*
var funcNames = [
	// les fonctions constantes (périodiques de période 1)
	"gentille", "méchante",
	// des fonctions périodiques d'abord de période 2 puis 3 ou 4
	// non exhaustif
	"per ct", "per tc",
	"per cct", "per ctt", "per tct", "per ttc",
	"per ccct", "per cctt", "per tttc",
	"per cccct", "per cctct", "per ccttt", "per ctctt",
        // des fonctions construites à partir de mots binaires
	"fibonacci", "anti fibo",
	"thue morse",
	// fonctions analysant le dernier coup de l'adversaire
];
*/
var rangF1, Flen;
function initFgen() {
	for(var i=0;i<funcNames.length;i++) {
		if(funcNames[i]=="F1") {
			rangF1=i;
			Flen=funcNames.length-rangF1;
			break;
		}
	}
}
initFgen();
initFunc();
function hasard() { 
	return (Math.random()<0.5)? true : false; 
}
function hasard2(p) {
        return (Math.random()<p)? true : false;
}
function major(u, k) {   // k=0 major,   k=1 majord
	var t=0;
	for(var i=0; i< u.length; i++) {
		if(u[i]) {
			t++;
		}
	}
	return (2*t+k>u.length);
}

var	antidd_coop, 
	antidd_trah; // font office de variables statiques pour antidd

var	Stratgies = {
	// gentille coopère toujours
	"gentille" : function(u, v, r) {return true;},

	// méchante trahit  toujours
	"méchante" : function(u, v, r) {return false;},

        // tft - tit for tat - donnant-donnant
        // coopère la 1ère fois, ensuite joue le coup précédent de l\'adversaire
        "tft": function(u, v, r) {
                return (r==0)? true : v[r-1]; //up(v, r);
        }, // donnant_donnant

        // méfiante, commence par trahir, ensuite joue le coup précédent de l\'adversaire
        "méfiante": function(u, v, r) {
                return (r==0)? false : v[r-1]; // up(v, r);
        } // méfiante

};
//alert(Stratgies["gentille"]);

var 	funcTab=new Array(
	// gentille coopère toujours 
	function(u, v, r) {return true;},

	// méchante trahit  toujours
	function(u, v, r) {return false;}, 

	// tft - tit for tat - donnant-donnant 
	// coopère la 1ère fois, ensuite joue le coup précédent de l\'adversaire
	function(u, v, r) {
		return (r==0)? true : v[r-1];//up(v, r);
	}, // donnant_donnant

	// méfiante, commence par trahir, ensuite joue le coup précédent de l\'adversaire
	function(u, v, r) {
		return (r==0)? false : up(v, r);
	}, // méfiante 

	// loyale 
	// trahit la 1ère fois
	// si coopère alors coopère encore au coup suivant, 
	// sinon si a trahit et si advers coopère, alors coopère (sinon trahit) :
	// trahit tant que adversaire n\'a pas coopéré
	function(u, v, r) {
		return (r==0)? false:(up(u, r))?true:(up(v, r))?true:false;
	}, // loyale

	// rancuniere
	// Coopère la 1ère fois
	// coopère tant que adversaire ne trahit pas, mais trahit chaque fois
	// dès que l\'adversaire trahit une fois
	function(u, v, r) {
		return (r==0)? true:(!up(u, r))?false:(!up(v, r))?false:true;
	}, // rancuniere

	// 
	function(u, v, r) {
		return (r==0)? true : major(u,0);
	}, // majo_mou

	//
	function(u, v, r) {
		return (r==0)? true : major(u,1);
	}, // majo_dur

	/* tf2t variation sur les deux derniers coups de tit for tat
	coopère au 1er coup et tant que l\'adversaire n\'a pas trahit deux fois consécutives, sinon
	trahit toujours ensuite
	*/
	function(u, v, r) {
		//return (r<=1)?true:(!up(u, r))? false :(!up(v, r)&&!up2(v, r))?false:true;
		return (r<2)?true: !u[r-1]? false:(!v[r-1]&& !v[r-2])? false: true;
	}, // tf2t
  
	/* tf2c
	commence par trahir 
	si l\'adversaire coopère deux fois de suite, coopère
	si coopère une fois, coopère toujours ensuite
	*/
	function(u, v, r) {
		return (r<=1)?false:(up(u, r))? true :(up(v, r) && up2(v, r))?true:false;
	}, // tf2c

	/* per_ct
	coopère la première fois
	alterne coopération et trahison
	*/
	function(u, v, r) {return (r<=1)? true : !up(u, r);}, // per_ct            // 10

	/* per_tc
	trahit en 1er, alterne trahison et coopération
	*/
	function(u, v, r) {return (r==0)? false : up(u, r);}, // per_tc

	/* per_cct
	coopère en premier et en second
	si trahit, alors coopère au coup suivant, sinon fait le contraire du pénultième
	*/
	//  function(u, v, r) {return (r==0)?true:(r==1)?true : (! up(u, r))? true : !up2(u, r);}, // per_cct
	function(u, v, r) {
		return ((r+1)%3==0)?false : true;
	},  // périodique CCTCCTCCT

	/* per_ttc  (devrait être le contraire de per_cct !!! mais dans quel sens ?)
	trahit en premier et en second 
	si coopère, alors trahit au coup suivant, sinon fait le contraire du pénultième
	function(u, v, r) {return (r==0)?false:(r==1)?false : (up(u, r))? false : !up2(u, r);}, // per_ttc
	*/
	function(u, v, r) {
		return ((r+1)%3==0)?true : false;
	}, // périodique TTCTTCTTC

	/* pavlov
	coopère en 1er et coopère sauf si au précédent l\'un coopère et pas l\'autre
	*/
	function(u, v, r) {
		return (r==0)?true:((up(u, r) && up(v, r)) || (!up(u, r)&&!up(v, r)));
	}, // pavlov

	/* antitf2t
	coopère les deux premières fois
	si adverse trahit deux fois consécutives, alors trahit
	sinon si adverse coopère les deux coups précédent, alors trahit
           sinon si adverse a trahi aux 2 et 3ème coup précédent, alors trahit
                 sinon fait le contraire de son coup précédent
	*/

	function(u, v, r) {
		return (r<=2)?true:
			(!up(u, r)&&!up2(u, r))?false:
			(!up(v, r)&&!up2(v, r))?false:
			(!up2(v, r)&&!up3(v, r))?false: 
			!up(u, r);
	} , // antitf2t
/*  */

	/* antidd
	trahit en 1er, coopère en second, trahit en 3ème
	sinon si les deux coopèrent autant et les deux trahissent autant
	*/
	function(u, v, r) {
		if(r==0) {
			antidd_trah=0;
			antidd_coop=0;
		} 
		else {
			if(u[r-1]==true) {
				antidd_coop++
			}
			else {
				antidd_trah++
			}
			if(v[r-1]==true) {
				antidd_coop--;
			}
			else {
				antidd_trah--;
			}
		}
		if(r<=2) {
			return false;
		}
		var 	a = up(v,r), 
			b=up2(u,r), 
			c=(a && b) || (!a && !b),
		 	x=  up2(v,r), 
			y=up(u,r), 
			z=(x&& y) || (!x && !y);
		if(antidd_coop==0 && antidd_trah==0  &&  c && z) {
			return false;
		}
		else {
			return up(v,r);
		}
	}, // antidd     momentanément, à changer

	/* 
	lunatique 
	*/
	function(u, v, r) {
		return hasard();
	}, // lunatique  // 17
	/* fibonacci */
	function(u, v, r) {
		return fibo(r);
	}, // fibonacci  // 18
	/* anti-fibo */
	function(u, v, r) {
		return !fibo(r);
	}, // anti-fibo  // 19
	/*  */
	function(u, v, r) {
		if(r==0) {
			return true;
		}
		var 	a = fibo(r), 
			b=up(v,r); 
		return(a&&b);
	}, // fibo=  // 20
	/*  */
	function(u, v, r) {
		return (r==0) ? true : (fibo(r))? up(v,r) : false;
	}, // fibo M // 21
	/*  */
	function(u, v, r) { 
		return speciale(u, v, r);
	}, //  proposée,
	/*  */
	function(u, v, r) {return reponse(0, u, v, r);},
	function(u, v, r) {return reponse(1, u, v, r);},
	function(u, v, r) {return reponse(2, u, v, r);},
	function(u, v, r) {return reponse(3, u, v, r);},
	function(u, v, r) {return reponse(4, u, v, r);},
	function(u, v, r) {return reponse(5, u, v, r);},
	function(u, v, r) {return reponse(6, u, v, r);}

);

var 	saStatique =new Array(4);

var 	Coopere = true,
	Trahit = false,
	Rval = 3, 
	Sval=0, 
	Pval=1, 
	Tval=5;
/*
function legain(a, b) {
	return (a==Coopere)?((b==Coopere)? Rval : Sval) : ((b==Coopere)? Tval : Pval);
}
*/
function legain(a, b) {
	if(a==true && b==true) {
		return Rval;
	}
	else if(a==true && b==false) {
		return Sval;
	}
	else if(a==false && b==true) {
		return Tval;
	}
	else if(a==false && b==false) {
		return Pval;
	}
}

// rencontre sur une longueur de n coups  entre les stratégies f0 et f1
function rencontre(n, f0, f1) {
	var 	u=new Array(), 
		v=new Array(), 
		a, 
		b;
	var t = [0, 0];
	for(var r=0; r<n; r++) {
		a = f0(u, v, r); 
		b = f1(v, u, r);
		t[0] += legain(a,b);
		t[1] += legain(b,a);
		u[r] = a;
		v[r] = b;
	}            
	return t;
}

// avec bruit
function rencontreAvecBruit(n, f0, f1, p) {
	var u=new Array(), v=new Array(), a, b;
	var t = [0, 0];
	for(var r=0; r<n; r++) {       
		a = f0(u, v, r);
		if(Math.random()<p) { 
			a=!a;
		}
		b = f1(v, u, r);
		if(Math.random()<p) {
			b=!b;
		}
		t[0] += legain(a,b);
		t[1] += legain(b,a);
 		u[r] = a;
		v[r] = b;
	}
	return t;
}

function alea(k0, k1) {
	var d = k1-k0;
	return k0+Math.floor(d*Math.random());
}

function population() {
	this.fcb=null;         // document.frm.cb[]
this.func=new Array();
	this.optF=false;
	this.opt1 = 1;
	this.reel=false;
	this.n=0;
	this.names = new Array();
	this.pop= new Array();
	this.popMaxi=0;
	this.eb=1000;
	this.nPop=0;
	this.optPart=false;
	this.partiel=1;
	this.k0=50;
	this.k1=120;
	this.lng=50;
	this.hist=""
	this.ngen=0;
	this.maxGen=0;
	this.interval=3;
	this.step=3;
	this.coord=new Array();
	this.tDat=true;
	this.tGraph = true;
	this.matriceGains = new Array();
	this.matriceGainsN = new Array();
	this.bruit = 0;  // de 0 (pas de bruit) à 1
	this.optBruit=false;
	this.corresp=new Array(); // this.corresp[i] = j pour Fi (genetique) = fonction n° j (parmi toutes)

	function setEff(aleat, i) {
		if(aleat==true) {
			if(this.reel) {
				this.pop[i] = this.eb/5+this.eb*Math.random();
			} 
			else {
				this.pop[i] =Math.floor(this.eb/5+this.eb*Math.random());
			}
		} 
		else {
			this.pop[i]=this.eb;
		}
		this.nPop += this.pop[i];
	}
	this.setEff=setEff;

	function init(maxnbgen, d, g,reel,aleat,l, effbase, f, pbruit,ppart) {
		this.optF=false;
		if(ppart<1) {
			this.partiel=ppart;
			this.optPart=true;
		} 
		else {
			this.partiel=1;
			this.optPart=false;
		}
		if(pbruit>0) {
			this.bruit=pbruit;
			this.optBruit=true;
		} 
		else {
			this.bruit=0;
			this.optBruit=false;
		}
		this.eb = effbase;
		this.fcb=f;
		if(l!=null) {
			this.k0=l[0];
			if(this.k0<1) {
				this.k0=1;
			}
                        if(l.length>1) {
				this.k1=l[1];
				if(this.k1<1) {
					this.k1=1;
				}
			}
			else {
				this.k1 = this.k0+1;
			}
			if(this.k0>this.k1) {
				var a = this.k0;
				this.k0 = this.k1;
				this.k1 = a;
			}
		}
		this.reel=reel;
		if(reel) {
			this.opt1=0;
		}
		this.maxGen = maxnbgen;
		this.tDat=d;
		this.tGraph=g;
		if(this.maxGen>100) {
			this.interval=Math.floor(this.maxGen/100);
			this.step=4;
		} 
		else {
			this.step = Math.floor(400/this.maxGen);
			this.interval = 1
		}
/* Sélection des fonctions cochées
*/
       
		this.n=funcTab.length;
		for(var i=0; i<Flen; i++) {
			this.corresp[i] = -1;
		}
		var i=0;
		for(var i0=0; i0<funcTab.length; i0++) {
			if(this.fcb[i0].checked==true) {
				this.func[i] = funcTab[i0];
				this.names[i] = funcNames[i0];
				if(i0>=rangF1) {
					this.optF=true;
					this.corresp[i0-rangF1]=i;	
				}
/*      // mis dans function setEff(aleat, i 
*/
				this.setEff(aleat, i);
				i++;
				this.n=i;
			}
		}
         
    // normalisation de la population totale
		if(aleat==true) {
			var coef=this.n*this.eb/this.nPop;
			this.nPop=0;
			for(var i=0; i<this.n; i++) {
				this.pop[i] = (this.reel)? this.pop[i]*coef : (this.pop[i]*coef<1)? 0 : Math.round(this.pop[i]*coef);
				this.nPop += this.pop[i];
			}
		}
    
		for(var i=0; i<this.n; i++) {
			if(this.pop[i]>this.popMaxi) {
				this.popMaxi= this.pop[i];
			}
		}
		for(var i=0; i<this.n; i++) {
			this.matriceGains[i] = new Array();
			this.matriceGainsN[i] = new Array();
			for(var j=0; j<this.n; j++) {
				this.matriceGains[i][j]=0;
				this.matriceGainsN[i][j]=0;
			}
		}
		for(var i=0; i<this.n; i++) {
			this.coord[2*i] =new Array();
			this.coord[2*i+1] =new Array();
			this.coord[2*i][0]=1;
			this.coord[2*i+1][0]=200-Math.floor(200/this.n)+1;
		}
	}
	this.init = init;

	function generation() {
		this.lng = alea(this.k0, this.k1);
		var 	l = this.lng,
			g, 
			gains=new Array();
		for(var i=0; i<this.n; i++) {
			gains[i] = 0;
		}
		var 	t = new Array();
		for(var i=0; i<this.n; i++) {
			if(this.pop[i]==0) {
				continue;
			}
			for(var j=i; j<this.n; j++) {
				if(this.pop[j]==0) {
					continue;
				}
				var t;
				if(this.optBruit) {
					t = rencontreAvecBruit(l, this.func[i], this.func[j], this.bruit);
				}
				else {
					t = rencontre(l, this.func[i], this.func[j]);
				}
				if(i==j) {
					// le /2 nécessite peut-être une petite explication 
					gains[i] += (t[0]+t[1])*this.pop[i]*(this.pop[j]-this.opt1)/2; 
					this.matriceGains[i][i] += ((t[0]+t[1])/2)/this.lng; // par unité
					this.matriceGainsN[i][i]++
				} 
				else {
					var prodt = this.pop[i]*this.pop[j];
					gains[i] += t[0]*prodt;
					gains[j] += t[1]*prodt;
					this.matriceGains[i][j] += t[0]/this.lng;
					this.matriceGainsN[i][j]++;
					this.matriceGains[j][i] += t[1]/this.lng;
					this.matriceGainsN[j][i]++;
				}
			}
		}
		delete(t);
		var totgains = 0;
		for(var i=0; i<this.n; i++) {
			totgains += gains[i];
		}
		if(this.optPart==false) {
			for(var i=0; i<this.n; i++) {
				this.pop[i] = this.nPop*gains[i]/totgains;
				if(this.pop[i]<1E-6) {
					this.pop[i]=0;
				}
				if(this.reel==false) {
					this.pop[i] = Math.round(this.pop[i])
                        	}
			}
		} 
		else {
			var	p=this.partiel, 
				q = 1-p;
			for(var i=0; i<this.n; i++) {
				this.pop[i] = this.pop[i]*q+p*this.nPop*gains[i]/totgains;
				if(this.pop[i]<1E-6) {
					this.pop[i]=0;
				}
				if(this.reel==false) {
					this.pop[i] = Math.round(this.pop[i])
				}
			}
		}
		for(var i=0; i<this.n; i++) {
			if(this.pop[i]>this.popMaxi) this.popMaxi= this.pop[i];
		}
		this.ngen++;
	}
	this.generation=generation;

	function entete() {
		var s = "# Nombre de fonctions : "+this.n+"  Population : "+this.nPop+"\n";
		if(this.reel==false && this.opt1==1) {
			s += "# Les effectifs donnés sont des entiers\n"
		} 
		else if (this.reel==false && this.opt1==0) {
			s += "# Les effectifs sont des multiples des valeurs arrondies données\n";
		} 
		else if (this.reel==true) {
			s += "# Les effectifs sont des multiples des valeurs réelles données\n";
		}
		if(this.optPart==true) {
			s += "# À chaque génération une partie seulement des effectifs est en compétition "+this.partiel+"\n"
		}
		s +="# Longueurs des séquences : "+this.k0;
		if(this.k1>this.k0) {
			s += "  à "+this.k1;
		}
		s +="\n";
		if (this.optBruit==true) {
			s += "# Bruit de probabilité : "+this.bruit+"\n" 
		}
		
		s +="# Récompenses : S = "+Sval+"  P = "+Pval+"  R = "+Rval+"  T = "+Tval+"\n";
		return s;
	}
	this.entete=entete;

	function listefonctions() {
		var s= "";
		for(var i=0; i<this.n;i++) {
			if(i%5==0) {
				s += "# ";
			}
			s += '"'+this.names[i]+'"'
			if(i<this.n-1) {
				s += ", ";
			}
			if((i+1)%5==0 || i==this.n-1) {
				s += "\n";
			}
		}
		return s;
	}
	this.listefonctions=listefonctions;

	function affiche() {
		var s =  this.entete();
		s += "# Nombre de générations : "+this.ngen+
			"\n# -------------------------------------------------------\n";
		var h="";
		for(var i=0; i<this.n; i++) {
			var s1 = this.names[i];
			while(s1.length<30) {
				s1+= " ";
			}
			s += s1+"\t : "+this.pop[i] + "\n";
			h+=this.pop[i] +" ";
		}
		if(this.tDat) {
			this.hist += h+"\n";
		}
		if(this.tGraph && this.ngen%this.interval==0) {
			var nn=Math.floor(this.ngen/this.interval);
			for(var i=0; i<this.n; i++) {
				this.coord[2*i][nn]=40+nn*this.step+1;
				this.coord[2*i+1][nn]=this.pop[i];
			}
		}
		if(this.ngen<MaxGen) {
			if(idG) {
				TextAreaSave=s;
			}
			else {
				document.frm.ecp.value = s //+"\n\n"+this.mat;
			}
		} 
		else {
			this.setScoresF();
			if(this.tDat) {
				//this.setScoresF();
				var lf = this.listefonctions();
				document.frm.ecp.value = this.entete()+
				"# Nombre de générations : "+this.ngen+"\n"+
				"# Fonctions :\n"+
				lf +
				"# -------------------------------------------------------\n"+
				this.hist;
			}
			else {
				if(idG) {
					TextAreaSave=s;
				}
				else {
					document.frm.ecp.value=s;
				}
			}
			this.afficheGraphe();
		}
	}
	this.affiche=affiche;

	function setScoresF() {
		//var cr="";
		for(var i=0; i<Flen; i++) {
			var j = this.corresp[i]
			if(j==-1) {
				scoresTable[i] = 0;     // score de la fonction Fi
			}
			else {
				scoresTable[i] = this.pop[j];
			}
		}

		for(var i=0; i<Flen; i++) {
			rangsTable[i]=i;                //  ordre de mérite i pour fonction rangsTable[i]
		}
		for(var i=0; i<Flen-1; i++) {           // rangsTable[0] a le plus grand score
			for(var j=i+1; j<Flen; j++) {
				if(scoresTable[rangsTable[j]]>scoresTable[rangsTable[i]]) {
					var r=rangsTable[i];
					rangsTable[i]=rangsTable[j];
					rangsTable[j]=r;
				}
			}
		}
                var     maxiScore=0,          // le plus grand des scores
                        leader=0;             // leader = rangsTable[0]
                for(var i=0; i<Flen; i++) {
                        if(scoresTable[i]>maxiScore) {
                                maxiScore=scoresTable[i];
                                leader=i;
                        }
                }
		// pour permettre de figer la population Fn
		if(PauseGen) {
			return;
		}
		//cr = "maxiScore "+maxiScore+"\n";
                if(maxiScore<this.eb/4) {     // tous les scores (effectifs) inférieurs à la moitié de
                        initFunc(mSave, nSave);           // l'effectif de départ, on réinitialise toutes
			//cr += "initFunc()";
			//alert(cr);
                        return;
                }
		// si l'effectif est inférieur à la moitié de l'effectif maxi
		for(var i=0; i<Flen; i++) {
			if(scoresTable[i] < maxiScore/2) {
				stratAltere(i, leader);
				//cr +="stratAltere("+i+", "+leader+");\n";
			}
		}
		var Flen2 = Math.floor(Flen/2)+1
//alert(Flen+" "+Flen2)
		// remplacement de toutes les fonctions
		for(var i=Flen2; i<Flen; i++) {
			var 	a = rangsTable[i], 
				b =rangsTable[Math.floor(Flen2*Math.random())],
				c = rangsTable[Math.floor(Flen2*Math.random())];
			if(lngTable[a][0]==lngTable[b][0] && lngTable[a][1]==lngTable[b][1]) {
				//cr += "stratMixte("+a+", "+b+", "+c+")\n";
				stratMixte(a, b, c);
			}
			else {
				//cr += "stratAltere("+a+", "+b+");\n"
				stratAltere(a, b);
			}
		}			
		//alert(cr);
	}
	this.setScoresF=setScoresF;

/*
        «   L0                                      »
        0       x0=40                           x1      x2
        ----------------------------------------------
        |       -----------------------------        |
        |       |                            |       |
        |       |                       _____| titft |
        |       |       ______        _/     |       |
        |       |   ___/      \      /       |       |
        |  1000 |__/           \    /        |       |
        |       |               \__/         |       |
        |       |                            |       |
        |    0  ------------------------------       |
        ----------------------------------------------
*/
	function afficheGraphe() {
		var 	cols=[
				"#000000", "#ee0077", "#00bbaa", "#999999", "#8800ff", 
				"#3300ff",  "#eebb00", "#ff4400", "#0099aa", "#eedd00", 
				"#ffbb88", "#90ee88", "#bbccff", "#ee88ff"
			]; 
		if(! this.tGraph) {
			return;
		}
		document.getElementById("Evolution").style.display="inline";
		var 	divCadre = document.getElementById("myCanvas"),
			classt=[],
			pmaxi;
		if(this.popMaxi>1) {
			var 	p=1,
				ll=Math.floor(Math.log(this.popMaxi)/Math.log(10));
			for(var i=0;i<ll-1;i++) {
				p *= 10;
			}
			pmaxi = Math.ceil(this.popMaxi/p)*p;
		} 
		else {
			pmaxi = this.popMaxi;
		}
		for(var nn=0; nn<this.coord[0].length; nn++) {
			for(var i=0; i<this.n; i++) {
				this.coord[2*i+1][nn]=300-Math.floor(this.coord[2*i+1][nn]*300/pmaxi)+1
			}
		}
		for(var i=0; i<this.n; i++) {
			classt[i] = i;
		}
		for(var i=0; i<this.n-1; i++) {
			for(var j=i+1; j<this.n; j++) {
				if(this.pop[classt[i]]<this.pop[classt[j]]) {
					var u = classt[i]; 
					classt[i]=classt[j];
					classt[j]=u;
				}
			}
		}
		divCadre.innerHTML='';
		divCadre.style.width=40+552;
		divCadre.style.height=20+302;
		if(jg) {
			jg.clear();
			delete(jg);
		}
		jg = new jsGraphics("myCanvas");
		jg.setColor("#0000ff");
		jg.drawRect(40,0,402,302);
		jg.setColor("#bbbbff");
		for(var i=1; i<300/50; i++) {
			jg.drawLine(40+0,i*50,40+402,i*50);
		}
		for(var i=1; i<8; i++) {
			jg.drawLine(40+i*50,0,40+i*50,302);
		}
		jg.setColor("#000000");
		for(var i=0; i<this.n; i++) {
			jg.setColor(cols[i%cols.length]);
			jg.drawPolyline(this.coord[2*i], this.coord[2*i+1] );    
		}
        
		jg.setFont("courier","13px",Font.ITALIC_BOLD);
		jg.setColor("#008800");
		jg.drawString(this.eb,0,Math.floor(300-300*this.nPop/pmaxi/this.n)-5);
		jg.setColor("#ff8800");
		jg.drawString(pmaxi,0,0);
		jg.drawString("0",25,290);
		jg.drawString("0",40,310);
		jg.drawString(this.ngen,430,310);
		jg.setColor("#0066aa");
		var 	iz=0, 
			d=0;
		for(var i=this.n-1; i>=0; i--) {
			var 	j=classt[i],
				l = this.coord[2*j+1].length-1
			if(this.coord[2*j+1][l] <300) {
				jg.setColor(cols[j%cols.length]);
				if(iz!=0 && Math.abs(this.coord[2*j+1][l]-this.coord[iz][l])<10) {
					d += 20;
				}
				else {
					d=0;
				}
				if(d>60) {
					d=0;
				}
				jg.drawString(this.names[j],445+d,this.coord[2*j+1][l]-10 );
				iz=2*j+1;
			}
		}
		jg.paint();
	}
	this.afficheGraphe=afficheGraphe;

	function afficheMatGains() {
		var sm = '<h4>Matrice des gains moyens</h4><div class="define">'+
		'<table align="center" class="def" style="{font-size:9px;}" border="1"" cellpadding="2"><tr><th></th>';
		for(var i=0; i<this.n;i++) {
			sm += '<th>'+this.names[i]+'</th>'
		}
		sm += '</tr>'
		for(var i=0; i<this.n;i++) {
			sm += '<tr><th>'+this.names[i]+'</th>'
			for(var j=0;j<this.n;j++) {       // pour 100 unités
				sm += '<td>'+Math.round(100*this.matriceGains[i][j]/this.matriceGainsN[i][j])/100+
					'</td>';
			}
			sm += '</tr>';
		}
		sm +='</table>' + '</div>';
		document.getElementById("Matr").innerHTML=sm;
	}
	this.afficheMatGains=afficheMatGains;
}

var	popul;

function lislongueurs() {
	var 	u, 
		s = document.frm.l.value;
	do {
		u=s;
		s=s.replace(/^\s+/g,"");
		s=s.replace(/\s+$/g,"");
		s=s.replace(/\s+/g," ");
		s=s.replace(/[\,\;\:]+/g," ");
	} while(u!=s);
	var 	t= s.split(/\s+/g);
	for(var i=0; i< t.length;i++) {
		t[i] = parseInt(t[i]);
	}
	return t;
}

var numUtilisateur=22;

function nouvelle() {
	if(document.frm.cb[numUtilisateur].checked==true) {
		litStrategie();
	}
	var 	reel=(document.frm.reel[1].checked==true),
		aleat=(document.frm.alea.checked==true),
		lng = lislongueurs();
	if(popul != null) {
		delete popul;
	}
	popul = new population();
	Rval = parseInt(document.frm.r.value);
	Sval = parseInt(document.frm.s.value);
	Tval = parseInt(document.frm.t.value);
	Pval = parseInt(document.frm.p.value);
	var	testDat = (document.frm.dat.checked==true),
		testGraph = (document.frm.g.checked==true),
		effbase=parseFloat(eval(document.frm.eb.value)),
		inteffbase=parseInt(document.frm.eb.value);
	if(inteffbase==effbase) {
		effbase = inteffbase;
	}
	if(effbase<=0) {
		effbase=1000;
	}
	var fcb = document.frm.cb;
	var pBruit = testNoise();
	var ppart = testPartiel();
	
	popul.init(MaxGen, testDat, testGraph,reel,aleat,lng, effbase, fcb, pBruit, ppart);
/*
	var divCadre = document.getElementById("myCanvas");
	document.getElementById("Evolution").style.display="none";

	if(jg !=null) {
		jg.clear();
		delete(jg);
	}
	divCadre.style.width=0;
	divCadre.style.height=0
*/
	if(!popul.tDat && popul.n<40) {
		var h=60+14*popul.n;
		document.frm.ecp.style.height=(h<410)? h : 410;
	} 
	else {
		document.frm.ecp.style.height=250;
	}
	popul.affiche();
}

function generation() {
	popul.generation();
	popul.affiche();
}

var 	nfois=0;
var 	MaxGen = 100;

var multi=0;
var	idchrono2=null;

function multiprogress() {

	if(idchrono2 != null) {
		clearTimeout(idchrono2);
	}
	multi = parseInt(document.frm.gsucc.value);
	if(multi>0) {
		document.frm.clrG.value="Pause";
		document.frm.gsucc.value=--multi;
		progress();
	}
}

function progress() {
/*
	if(jg) {
		jg.clear();
		delete(jg);
	}
 */ 
	//document.getElementById("Matr").innerHTML="";
	MaxGen=parseInt(document.frm.gmax.value)
	nfois = 0;
	nouvelle();  
	chrono();
}

function matriceGains() {
	if(document.frm.ma.checked==true) {
		popul.afficheMatGains();
	}
}

var idchrono = null;

function chrono() {
	if(idchrono) {
		clearInterval(idchrono);
	}
	if(nfois<MaxGen) {
		generation();
		nfois++;
		idchrono = setInterval('chrono()', 50);
	} 
	else {
		matriceGains();
		idchrono2=setTimeout("multiprogress();",1000)
	}
}

var mode="Tout";

function tout() {
	if(mode=="Tout") {
		mode="Rien";
		for(var i0=0; i0<funcTab.length; i0++) {
			document.frm.cb[i0].checked=true;
		}
	}
	else {
		mode="Tout";
		for(var i0=0; i0<funcTab.length; i0++) {
			document.frm.cb[i0].checked=false;
		}
	}
	document.frm.sel.value=mode
}

var speciale;

function litStrategie() {
	var spc = document.frm.strat.value;
	if(speciale) {
		delete(speciale);
	}
	speciale = new Function("u","v","r",spc);
	//speciale = eval(spc);
}

var 	avecBruit=false, 
	probaBruit=0.005;

function checkNoise(){
	avecBruit= !avecBruit;
	if(avecBruit == true) {
		document.frm.obr.checked=true;
		document.frm.br.style.display="inline";
		document.frm.br.value=probaBruit;
	} 
	else {
		document.frm.obr.checked=false;
		document.frm.br.style.display="none"
	}
	testNoise();
}

function testNoise() {
	var pBruit=0;
	if(avecBruit == true) {
		probaBruit=parseFloat(document.frm.br.value);
		if(probaBruit<0) {
			probaBruit=0;
		}
		else if(probaBruit>1) {
			probaBruit=1;
		}
		document.frm.br.value= probaBruit;
		pBruit =  probaBruit;
	} 
	else {
		document.frm.obr.checked=false;
		document.frm.br.style.display="none"
		pBruit = 0;
	}
	return pBruit;
}

var 	partiel=false, 
	propPartiel=0.5;

function checkPartiel(){
	var sp=document.getElementById("prop");
	partiel= !partiel;
	if(partiel == true) {
		document.frm.opart.checked=true;
		document.frm.part.style.display="inline";
		document.frm.part.value=propPartiel;
		sp.style.display="inline"
	} 
	else {
		document.frm.opart.checked=false;
		document.frm.part.style.display="none"
		sp.style.display="none";
	}
	testPartiel();
}

function testPartiel() {
	var sp=document.getElementById("prop");
	if(document.frm.opart.checked==true) {
		partiel = true;
		document.frm.part.style.display="inline";
		propPartiel=parseFloat(document.frm.part.value);
      		if(propPartiel<0) {
			propPartiel=0;
		}
		else if(propPartiel>1) {
			propPartiel=1;
		}
		document.frm.part.value= propPartiel;
		return propPartiel;
	} 
	else {
		partiel = false;
		document.frm.part.style.display="none"
		sp.style.display="none";
		return 1;
	}
}
/*
	

*/
var payOffs = [  // S P R T ordre croissant pour le dilemme du prisonnier
"0 1 3 5", // S=0 P=1 R=3 T=5 Prisoner s Dilemma,
"0 3 8 10", //
"0 4 5 5", //
"0 1 9 10", //
"1 0 3 4", // Chicken (Colman 1982).
"3 0 1 2", //  Battle of the sexes  (Colman 1982).
"10 1 5 3", //jeu de l'assurance
"25 21 30 0", // Poule-mouillée
"5 0 10 0", //Ami ou ennemi
"0 1 3 5","0 1 3 5","0 1 3 5","0 1 3 5","0 1 3 5","0 1 3 5","0 1 3 5"];

function payChge(n) {
	document.frm.pay[n].checked=true;
	var t = payOffs[n].split(/\s+/g);
	var d = document.frm;
	// S P R T
	d.s.value=t[0];
	d.p.value=t[1];
	d.r.value=t[2];
	d.t.value=t[3];
}

function chckF(k) {
	var t = document.frm.cb[rangF1+k].checked;
	for(var i=0; i<Flen; i++) {
		document.frm.cb[rangF1+i].checked= t;
	}
}

var gsuccSave=0;

function rappGsucc() {
	if(gsuccSave!=0) {
		document.frm.gsucc.value=gsuccSave;
		if(idchrono2 == null && idchrono == null) {
			multiprogress();
		}
	}
}

function clearGsucc() {
	var a=parseInt(document.frm.gsucc.value);
	if(a!=0) {
		gsuccSave = a;
		document.frm.gsucc.value="0";
		document.frm.clrG.value="Reprise";
	} else {
		document.frm.gsucc.value=gsuccSave;
		document.frm.clrG.value="Pause";
                multiprogress();
	}
}

var stratPreced=null;

function toJsEx() {
	var u="", s = document.frm.strat.value;
	if(stratPreced==null) {
		stratPreced=s;
		document.frm.s2js.value="";
	s = s.replace(/^[\n]+/,"");
	s = s.replace(/[\s\n]+$/,"");
//alert(s);
	var t = s.split(/\n/g);
//alert(t)
	for(var i=0; i<t.length; i++) {
		t[i]=t[i].replace(/\s+$/g,"");
		t[i]=t[i].replace(/\\/g,"\\\\");
		//t[i]=t[i].replace(/\"/g,"\\\"");
		t[i]=t[i].replace(/\'/g,"\\\'");
		u += "'"+t[i]+"'"+((u!=t.length-1)?"+":"") +"\n";
	}
	document.frm.strat.value = u;
	} else {
		document.frm.s2js.value="'";
		document.frm.strat.value=stratPreced;
	}
}

function stratEfface() {
	document.frm.strat.value="";
	stratPreced=null;
	document.frm.s2js.value="'";
}


