Capitan

Aide à la recherche de cachettes de Capitan

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @author TilK, Dabihul, Rouletabille
// @description Aide à la recherche de cachettes de Capitan
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_listValues
// @include */mountyhall/View/TresorHistory*
// @include */mountyhall/MH_Play/Play_a_TrouverCachette2*
// @include */mountyhall/MH_Play/Play_equipement.php*
// @include */mountyhall/MH_Taniere/TanierePJ_o_Stock.php*
// @include */mountyhall/MH_Play/Play_a_ActionResult.php*
// @exclude *mh2.mh.raistlin.fr*
// @exclude *mzdev.mh.raistlin.fr*
// @name Capitan
// @version 8.8.24
// @namespace https://greasyfork.org/users/70018
// ==/UserScript==

"use strict";

/****************************************************************
*         Aide à la recherche de cachettes de Capitan           *
*              Développé par Mini TilK (n°36216)                *
*                      [email protected]                           *
*****************************************************************
*        Adapté pour GreaseMonkey par Dabihul (n°79738)         *
*        Roule : ajout https 07/08/2016                         *
*****************************************************************
*         Pour utiliser la recherche, allez des les infos       *
*             de la carte de la cachette du capitan             *
*                    Une fois ceci fait,                        *
*           toutes les recherches seront sauvergardées          *
*            Et dans le détail de la carte vous verrez          *
*       Le nombre de cachettes possibles et leur position       *
****************************************************************/

class cCAPITAN_MH {
	static bDebug = false;
	static MZ_ok;
	/* pour mémoire
	static numTroll = undefined;
	static curPos = undefined;	// object genre {x:10, y:10, n:-10}, non renseigné en cas smartphone sans MZ
	// */
	static listeSolution = new Array();	// tableau de cEssai
	static infoCartes = {};	// {"1234": {"mort": {x:10, y:10, n:-10}, "essais": [{x:10, y:10, n:-10, c=0}, {x:11, y:11, n:-10, c=1}], "signeX": 1, "signeY": -1}
	static infoCurrentCarte;

	static appendButton(paren,value,onClick) {
		var input = document.createElement('input');
		input.type = 'button';
		input.className = 'mh_form_submit';
		input.value = value;
		input.style.cursor = 'pointer';
		if (onClick) input.onclick = onClick;
		paren.appendChild(input);
		return input;
	};

	/* Ajout des éléments manquants de libs */
	static isPage(url) {
		return window.location.href.indexOf(url)!=-1;
	};

	static insertTitle(next,txt) {
		var div = document.createElement('div');
		div.className = 'titre2';
		cCAPITAN_MH.appendText(div,txt);
		cCAPITAN_MH.insertBefore(next,div);
	};

	static insertBefore(next,el) {
		next.parentNode.cCAPITAN_MH.insertBefore(el,next);
	};

	// Roule 08/08/2016 ajout cssClass
	static appendTr(tbody, cssClass) {
		var tr = document.createElement('tr');
		tbody.appendChild(tr);
		if (cssClass) tr.className = cssClass;
		return tr;
	};

	static appendTd(tr) {
		var td = document.createElement('td');
		if(tr) { tr.appendChild(td); }
		return td;
	};

	static appendText(paren,text,bold) {
		if(bold) {
			var b = document.createElement('b');
			if (text) b.appendChild(document.createTextNode(text));
			paren.appendChild(b);
			}
		else
			if (text) paren.appendChild(document.createTextNode(text));
	};

	static appendTdText(tr,text,bold) {
		var td = cCAPITAN_MH.appendTd(tr);
		cCAPITAN_MH.appendText(td,text,bold);
		return td;
	};
	/* */

	static removeTab(tab, i) {
		var newTab = new Array();
		for(var j=0;j<i;j++)
		{
			newTab.push(tab[j]);
		}
		for(var j=i+1;j<tab.length;j++)
		{
			newTab.push(tab[j]);
		}
		return newTab;
	};
	static cache = new Array();

	static tabToString(tab) {
		var string = tab[0];
		for(var i=1;i<tab.length;i++)
			string+=";"+tab[i];
		return string;
	};

	static extractPosition(nombre, indice) {
		if((nombre+"").length<=indice)
			return "%";
		indice = (nombre+"").length - 1 - indice;
		return (nombre+"").substring(indice,indice+1);
	};

	static signe(x) {
		if(x<0)
			return -1;
		return 1;
	};

	static getPosFromArray(liste,begin,length) {
		let pos="";
		for(let i=begin; i<begin+length; i++)
			pos += "" + liste[i];
		return parseInt(pos, 10);
	};

	static toggleTableau() {
		let tbody = this.parentNode.parentNode.parentNode.childNodes[1];

		tbody.setAttribute('style', !tbody.getAttribute('style') || tbody.getAttribute('style') == '' ? 'display:none;' : '');
	};

	static createCase(titre,table,width) {
		if(width==null)
			width="120";
		var tr = cCAPITAN_MH.appendTr(table, 'mh_tdpage');

		var td = cCAPITAN_MH.appendTdText(tr, titre, true);
		td.setAttribute('class', 'mh_tdpage');
		td.setAttribute('width', width);
		td.setAttribute('align', 'center');

		return td;
	};

	static showXYN(loc, infos) {
		var sx = '±';
		var sy = '±';
		if (infos && infos.signeX) {
			sx = '+';
			if (infos.signeX < 0) sx = '-';
		}
		if (infos && infos.signeY) {
			sy = '+';
			if (infos.signeY < 0) sy = '-';
		}
		return "X = " + sx + loc.xAbs() + ", Y = " + sy + loc.yAbs() + ", N = -" + loc.nAbs();
	};

	static createHTMLTable() {	// les 3 table ont le même modèle
		let table = document.createElement('table');
		table.setAttribute('class', 'mh_tdborder');
		table.setAttribute('border', '0');
		table.setAttribute('cellspacing', '1');
		table.setAttribute('cellpadding', '4');
		table.setAttribute('style', 'width: 400px;');
		table.setAttribute('align', 'center');
		return table;
	};

	static generateTableSolutions() {
		let table = cCAPITAN_MH.createHTMLTable();
		let thead = document.createElement('thead');
		let tr = cCAPITAN_MH.appendTr(thead, 'mh_tdtitre');
		let td = cCAPITAN_MH.appendTdText(tr, null, true);
		td.setAttribute('align', 'center');
		table.appendChild(thead);

		if(cCAPITAN_MH.listeSolution.length==1)
		{
			td.appendChild(document.createTextNode("Position de la cachette : " + cCAPITAN_MH.showXYN(cCAPITAN_MH.listeSolution[0], cCAPITAN_MH.infoCurrentCarte)));
			return table;
		}
		else if(cCAPITAN_MH.listeSolution.length==0)
		{
			td.appendChild(document.createTextNode("Aucune solution trouvée."));
			return table;
		}

		td.appendChild(document.createTextNode("Il y a "+cCAPITAN_MH.listeSolution.length+" positions possibles"));

		let eBody = document.createElement('tbody');
		table.appendChild(eBody);

		let bExist200 = false;
		for (let i = 0; i < cCAPITAN_MH.listeSolution.length; i++) {
			if (cCAPITAN_MH.listeSolution[i].is200())
				bExist200 = true;
			else
				cCAPITAN_MH.createCase(cCAPITAN_MH.showXYN(cCAPITAN_MH.listeSolution[i], cCAPITAN_MH.infoCurrentCarte),eBody,400);
		}
		if (bExist200) {
			cCAPITAN_MH.createCase("Les suivantes sont peu probables car trop en dehors du Hall",eBody,400);
			for (let i = 0; i < cCAPITAN_MH.listeSolution.length; i++) {
				if (cCAPITAN_MH.listeSolution[i].is200())
					cCAPITAN_MH.createCase(cCAPITAN_MH.showXYN(cCAPITAN_MH.listeSolution[i], cCAPITAN_MH.infoCurrentCarte),eBody,400);
			}
		}

		td.addEventListener("click", cCAPITAN_MH.toggleTableau, true);
		td.setAttribute('onmouseover', "this.style.cursor = 'pointer'; this.className = 'mh_tdpage';");
		td.setAttribute('onmouseout', "this.className = 'mh_tdtitre';");
		eBody.setAttribute('style', 'display:none;');

		return table;
	};

	static calculeSolution2() {	// calcule les solutions à partir des propriétés de oMort et gEssai
		var oContexte = {
			// nombre d'occurrence de chaque chiffre (0 à 9) dans les coord de la mort du Capitan
			tabOccurrenceChiffre: cCAPITAN_MH.infoCurrentCarte.mort.tabOccurenceChiffre(),
			nCoord3: undefined,
			// On n'a encore traité aucune coordonnée
			nCoordEnCours: 0,	// 0:x, 1:y, 2:n
			// les chaines des coord en cours de construction
			tabCoord: ['', '', ''],
		};
		// nombre de chiffres (une coord à 1 chiffe en donne 2, le "0" et le chiffre des unités) dans les coord de la mort du Capitan
		oContexte.nCoord3 = cCAPITAN_MH.infoCurrentCarte.mort.nbChiffre() - 6;
		//if (cCAPITAN_MH.bDebug) window.console.log("[CAPITAN debug] calculeSolution2_log contexte initial=" + JSON.stringify(oContexte));

		// On lance le balayage récursif des possibilités
		cCAPITAN_MH.listeSolution = new Array();
		cCAPITAN_MH.calculeSolutionRecursifCoord(oContexte);
		//if (cCAPITAN_MH.bDebug) window.console.log("[CAPITAN debug] calculeSolution2_log résultat=" + JSON.stringify(cCAPITAN_MH.listeSolution));
	};

	static calculeSolutionRecursifCoord(oContexte) {	// balayage récursif des solutions, balayage coordonnée (x, y ou n)
		if (oContexte.nCoord3 > 0) {	// lancer le test sur une coord à 3 chiffres
			var newContexte = Object.assign({}, oContexte);	// clone car on modifie le contexte
			newContexte.nCoord3--;
			cCAPITAN_MH.calculeSolutionRecursifDigit(newContexte, 3);
		}
		if (oContexte.nCoord3 <= (2- oContexte.nCoordEnCours)) {	// pas de test à 2 chiffres si toutes les coord restantes doivent être à 3 chiffres
			cCAPITAN_MH.calculeSolutionRecursifDigit(oContexte, 2);
		}
	};

	static calculeSolutionRecursifDigit(oContexte, nChiffreThisCoord) {	// balayage récursif des solutions, balayages des suites de chiffres possibles
		let thisCoord = '';
		let newContexte = Object.assign({}, oContexte);	// clone car on modifie le contexte
		newContexte.tabCoord = oContexte.tabCoord.slice();	// clone (le clone ci-dessus est un "shallow" clone)
		for (let i = 0; i <= 9; i++) {	// boucle sur les chiffres possibles à cette position
			if (oContexte.tabOccurrenceChiffre[i] == 0) continue;	// chiffre non disponible
			newContexte.tabCoord[oContexte.nCoordEnCours] = oContexte.tabCoord[oContexte.nCoordEnCours] + i;
			newContexte.tabOccurrenceChiffre = oContexte.tabOccurrenceChiffre.slice();	// clone car on modifie ce tableau
			newContexte.tabOccurrenceChiffre[i]--;
			if (nChiffreThisCoord > 1) {	// continuer à tirer des chiffres pour cette coord
				cCAPITAN_MH.calculeSolutionRecursifDigit(newContexte, nChiffreThisCoord-1);
				continue;
			}
			// on a fini avec cette coord
			if (oContexte.nCoordEnCours != 2) {
				// continuer sur la coord suivante
				newContexte.nCoordEnCours = oContexte.nCoordEnCours + 1;
				cCAPITAN_MH.calculeSolutionRecursifCoord(newContexte);
				continue;
			}
			// ici, on a tiré tous les chiffres des 3 coordonnées, on teste si ces coord sont compatibles avec les essais
			let bCompatible = true;
			let oEssai;
			if (cCAPITAN_MH.infoCurrentCarte.essais) for (oEssai of cCAPITAN_MH.infoCurrentCarte.essais) {
				if (!oEssai.isCompatible(newContexte.tabCoord)) {
					bCompatible = false;
					break;
				}
			}
			if (cCAPITAN_MH.bDebug && newContexte.tabCoord[0] == '03' && newContexte.tabCoord[1] == '80') {
				let sCause = '';
				if (!bCompatible) {
					sCause = ' bad ' + oEssai.forPsychoChasseur() + ' nMatches=';
					sCause += oEssai.nbMatchesOne(oEssai.xText(), newContexte.tabCoord[0]);
					sCause += ' ' + oEssai.nbMatchesOne(oEssai.yText(), newContexte.tabCoord[1]);
					sCause += ' ' + oEssai.nbMatchesOne(oEssai.nText(), newContexte.tabCoord[2]);
				}
				//window.console.log('[CAPITAN debug] calculeSolutionRecursifDigit_log, teste ' + newContexte.tabCoord.join("; ") + ', bCompatible=' + bCompatible + sCause);
			}
			if (bCompatible) {
				cCAPITAN_MH.listeSolution.push(new cCAPITAN_essai(newContexte.tabCoord));	// slice pour cloner le tableau
			}
		}
	};

	static afficheInfoCarte(idCarte, bRecherche) {
		cCAPITAN_MH.idCarte = idCarte;
		cCAPITAN_MH.initCarte(idCarte);
		cCAPITAN_MH.infoCurrentCarte = cCAPITAN_MH.infoCartes[cCAPITAN_MH.idCarte];
		if (!cCAPITAN_MH.infoCurrentCarte.mort) {
			let table = cCAPITAN_MH.createHTMLTable();
			let thead = document.createElement('thead');
			let tr = cCAPITAN_MH.appendTr(thead, 'mh_tdtitre');
			let td = cCAPITAN_MH.appendTdText(tr, `[Extension Capitan] Impossible de suggérer des solutions car la position de la mort du Capitan n'a pas été mémorisée. Affichez le détail de la carte dans la page EQUIPEMENT.`, true);
			td.setAttribute('align', 'center');
			table.appendChild(thead);
			if (bRecherche) cCAPITAN_MH.createCase(`Cette recherche a bien été mémorisée`,table)
			return table;
		}
		cCAPITAN_MH.calculeSolution2();
		return cCAPITAN_MH.generateTableSolutions();
	};

	static getRepartitionFromCase(oPos) {
		let repartition = new Array();
		for(let i=0;i<cCAPITAN_MH.listeSolution.length;i++)
		{
			let nbGood = cCAPITAN_MH.listeSolution[i].nbMatches(oPos);
			for (let j = repartition.length; j <= nbGood; j++) repartition.push(0);	// Roule 15/08/2016 compléter le tableau selon le besoin
			repartition[nbGood]++;
		}
		repartition.sort(function(a,b) {return b-a;});
		return repartition;
	};

	static getMeanPositionNumber(repartition,nbSolutions) {
		var result=0;
		for(var i=0;i<repartition.length;i++)
		{
			result+=repartition[i]*repartition[i];
		}
		return result/nbSolutions;
	};

	static newRecherche() {
		if(cCAPITAN_MH.listeSolution.length<=1)
			return null;

		let table = cCAPITAN_MH.createHTMLTable();

		if (cCAPITAN_MH.curPos == undefined) {
			let thead = document.createElement('thead');
			let tr = cCAPITAN_MH.appendTr(thead, 'mh_tdtitre');
			let td = cCAPITAN_MH.appendTdText(tr, "Impossible de suggérer une loc en mode smartphone sans MZ", true);
			td.setAttribute('align', 'center');
			table.appendChild(thead);
			return table;
		}

		// Roule 15/08/2016 plus que dubitatif sur ce calcul de Size, j'utilise repartition.length
		//let size = (";"+Math.abs(cCAPITAN_MH.listeSolution[0][0])+Math.abs(cCAPITAN_MH.listeSolution[0][0])+Math.abs(cCAPITAN_MH.listeSolution[0][0])).length-1;
		let repartition = cCAPITAN_MH.getRepartitionFromCase(cCAPITAN_MH.curPos);
		let size = repartition.length;
		if (cCAPITAN_MH.bDebug) window.console.log(`[Capitan debug] newRecherche_log: size=${size}, repartition=${JSON.stringify(repartition)}`);

		let nbNotZero = 0;
		for(let i=0;i<size;i++) {
			if(repartition[i]!=0)
				nbNotZero++;
		}
		let string = "Il y a une utilité de faire une recherche en X = "+cCAPITAN_MH.curPos.x+" Y = "+cCAPITAN_MH.curPos.y+" N = "+cCAPITAN_MH.curPos.n;
		if(nbNotZero<=1) {
			//
			let minsolution = cCAPITAN_MH.listeSolution.length;
			let newpos = "";
			let isNotN = true;
			for(let dx=-1;dx<=1;dx++)
				for(let dy=-1;dy<=1;dy++)
					for(let dn=0;dn!=-3;dn=(dn==0?1:dn-2)) {
						if(dx==0 && dy==0 && dn==0) continue;
						//if (cCAPITAN_MH.bDebug) window.console.log(`[Capitan debug] newRecherche_log: dx=${dx}, dy=${dy}, dn=${dn}, `);
						let thisPos = new cCAPITAN_essai(cCAPITAN_MH.curPos);
						thisPos.move(dx, dy, dn);
						let tmprepartition = cCAPITAN_MH.getRepartitionFromCase(thisPos);
						let tmpmeanscore = cCAPITAN_MH.getMeanPositionNumber(tmprepartition,cCAPITAN_MH.listeSolution.length);
						if (cCAPITAN_MH.bDebug) window.console.log(`[Capitan debug] newRecherche_log: dx=${dx}, dy=${dy}, dn=${dn}, isNotN=${isNotN}, tmpmeanscore=${tmpmeanscore}, minsolution=${minsolution}, `);
						if(((dn==0 || !isNotN) && minsolution>=tmpmeanscore) || (dn!=0 && isNotN && tmpmeanscore<=2*minsolution/3)) {
							minsolution = tmpmeanscore;
							repartition = tmprepartition;
							newpos = thisPos.display();;
							isNotN = (dn==0);
						}
					}
			if (cCAPITAN_MH.bDebug) window.console.log(`[Capitan debug] newRecherche_log: minsolution=${minsolution}, listeSolution.length=${cCAPITAN_MH.listeSolution.length}`);
			if(minsolution == cCAPITAN_MH.listeSolution.length) {
				let thead = document.createElement('thead');
				let tr = cCAPITAN_MH.appendTr(thead, 'mh_tdtitre');
				let td = cCAPITAN_MH.appendTdText(tr, "Il n'y a aucune utilité de faire une recherche en " + cCAPITAN_MH.curPos.display(), true);
				td.setAttribute('align', 'center');
				table.appendChild(thead);
				return table;
			}
			string = "Conseil : allez faire une recherche en "+newpos;
		}

		if (cCAPITAN_MH.bDebug) window.console.log(`[Capitan debug] newRecherche_log: size=${size}, repartition=${JSON.stringify(repartition)}`);
		let thead = document.createElement('thead');
		let tr = cCAPITAN_MH.appendTr(thead, 'mh_tdtitre');
		let td = cCAPITAN_MH.appendTdText(tr,string, true);
		td.setAttribute('align', 'center');
		table.appendChild(thead);
		let tbody = document.createElement('tbody');
		table.appendChild(tbody);
		size = repartition.length;
		for(let i=0;i<size;i++) {
			if(i==size-1) {
				if(repartition[i]!=0)
					cCAPITAN_MH.createCase(Math.round(100*repartition[i]/cCAPITAN_MH.listeSolution.length)+"% de chance d'éliminer "+(cCAPITAN_MH.listeSolution.length-repartition[i])+" positions possibles",tbody,400);
			} else {
				let n=1;
				while((i+n)<size && repartition[i]==repartition[i+n])
					n++;
				if(repartition[i]!=0)
					cCAPITAN_MH.createCase(Math.round(100*n*repartition[i]/cCAPITAN_MH.listeSolution.length)+"% de chance d'éliminer "+(cCAPITAN_MH.listeSolution.length-repartition[i])+" positions possibles",tbody,400);
				i+=n-1;
			}
		}

		td.addEventListener("click", cCAPITAN_MH.toggleTableau, true);
		td.setAttribute('onmouseover', "this.style.cursor = 'pointer'; this.className = 'mh_tdpage';");
		td.setAttribute('onmouseout', "this.className = 'mh_tdtitre';");
		tbody.setAttribute('style', 'display:none;');
		return table;
	};

	static getIDCarte() {
		//if (cCAPITAN_MH.bDebug) return;
		var infoObjet = document.evaluate("//h2[@class = 'titre2']/text()[contains(.,'Carte de la Cachette')]",
			document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
		// si échec, essayer avec l'ancienne méthode
		if (!infoObjet) infoObjet = document.evaluate("//td[@class = 'titre2']/text()[contains(.,'Carte de la Cachette')]",
			document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
		// si échec, essayer avec l'ancienne méthode
		if (!infoObjet) infoObjet = document.evaluate("//div[@class = 'titre2']/text()[contains(.,'Carte de la Cachette')]",
			document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
		if(infoObjet) return parseInt(infoObjet.nodeValue.replace('[', ''));
	};

	static analyseObject() {
		//if (cCAPITAN_MH.bDebug) {console.log('[Capitan debug] analyseObject_log: début'); console.trace();}
		var eSpacer = document.getElementById('spacerMZCapitan');
		if (eSpacer) return;	// déjà affiché
		if( !cCAPITAN_MH.numTroll) {
			window.console.log('[CAPITAN] analyseObject_log: *** erreur *** pas de numéro de Trõll');
			return;
		}
		cCAPITAN_MH.idCarte = cCAPITAN_MH.getIDCarte();
		if (cCAPITAN_MH.bDebug && cCAPITAN_MH.idCarte == 11987020) {	// test Roule
			cCAPITAN_MH.info.mort = new cCAPITAN_essai(-101, -8, -73);
		} else if (cCAPITAN_MH.idCarte > 0) {
			cCAPITAN_MH.initCarte(cCAPITAN_MH.idCarte);
		} else {
			var parentElt = document.body;
			var modalElt = document.evaluate("//div[@class = 'modal']",
				document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
			if (modalElt && !modalElt.errorIDDone) {
				modalElt.appendChild(document.createTextNode("Erreur à la récupération de l'ID de la carte"));
				modalElt.errorIDDone = true;
			}
			if (cCAPITAN_MH.bDebug) console.log('[Capitan debug] analyseObject_log: pas trouvé de idCarte');
			return;
		}
		if (cCAPITAN_MH.mutationObserver) cCAPITAN_MH.mutationObserver.disconnect();
		let infos = cCAPITAN_MH.infoCartes[cCAPITAN_MH.idCarte];
		if (cCAPITAN_MH.bDebug) window.console.log(`[CAPITAN debug] début analyseObject_log(${cCAPITAN_MH.idCarte} ${JSON.stringify(infos)}`);
		if (cCAPITAN_MH.limitInfiniteLoop()) {
			cCAPITAN_MH.MutationObserver.disconnect();
		}

		let locMortFromHTML;
		let infoPos = document.evaluate("//td/text()[contains(.,'ai été tué en')]",
			document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
		if(!infoPos) {
			if (cCAPITAN_MH.bDebug) window.console.log('[Capitan debug] analyseObject_log: numTroll=' + cCAPITAN_MH.numTroll + ', cCAPITAN_MH.idCarte=' + cCAPITAN_MH.idCarte + ', impossible de trouver le texte de la mort du Capitan');
		} else {
			let listePos = infoPos.nodeValue.split("=");
			if(listePos.length!=4) {
				if (cCAPITAN_MH.bDebug) window.console.log('[Capitan debug] analyseObject_log: numTroll=' + cCAPITAN_MH.numTroll + ', cCAPITAN_MH.idCarte=' + cCAPITAN_MH.idCarte + ', impossible de trouver les coord. de la mort du Capitan ' + infoPos.nodeValue);
			} else {
				locMortFromHTML = new cCAPITAN_essai(listePos[1], listePos[2], listePos[3]);
			}
			//if (cCAPITAN_MH.bDebug) window.console.log('[Capitan debug] analyseObject_log: setValue("capitan.'+cCAPITAN_MH.idCarte+'.position, '+x+";"+y+";"+n);
			//cCAPITAN_MH.CAPITAN_setValue("capitan."+cCAPITAN_MH.idCarte+".position",x+";"+y+";"+n);
		}
		if (locMortFromHTML && locMortFromHTML.isValidLoc()) {
			let bKeep = false;
			if (infos.mort) {
				if (!infos.mort.sameLocAs(locMortFromHTML)) {
					console.log(`[Capitan] analyseObject_log pas la même loc de mort ${infos.mort}<->${locMortFromHTML}`);
					bKeep = true;
				}
			} else {
				bKeep = true;
			}
			if (bKeep) {
				infos.mort = locMortFromHTML;
				cCAPITAN_MH.saveIntoMH(cCAPITAN_MH.idCarte);
			}
		} else {
			console.log(`[Capitan] analyseObject_log erreur à la récupération de la loc de mort, on tente celle stockée ${infos.mort}<->${locMortFromHTML}`);
			//if (!infos.mort) {
			//	console.log(`[Capitan] analyseObject_log pas de loc de mort stockée, pas possible continuer`);
			//}
		}

		// Roule 23/11/2016 travail dans le body (ancienne version, fenêtre indépendante) ou dans la div modale (nouvelle version en "popup")
		var parentElt = document.body;
		var modalElt = document.evaluate("//div[@class = 'modal']",
			document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
		if (modalElt) parentElt = modalElt;
		cCAPITAN_MH.gDiv = document.createElement('div');
		parentElt.appendChild(cCAPITAN_MH.gDiv);
		parentElt = cCAPITAN_MH.gDiv;

		// bloc liste de solutions
		//console.log('xxx avant bloc solutions');
		var table = cCAPITAN_MH.afficheInfoCarte(cCAPITAN_MH.idCarte);
		if (table) {
			var p = document.createElement('p');
			p.id = 'spacerMZCapitan';
			//window.console.log('analyseObject_log: table=' + JSON.stringify(table));
			p.appendChild(table);
			parentElt.appendChild(p);
		}
		//console.log('xxx après bloc solutions');

		// bloc utilité de faire une recherche sur la position courante
		table = cCAPITAN_MH.newRecherche();
		if(table != null)
		{
			var p = document.createElement('p');
			p.appendChild(table);
			parentElt.appendChild(p);
			// bloc ajout de nouvelle recherche
			cCAPITAN_MH.createNewRecherche(parentElt);
		}
		//console.log('xxx après recherche pos courrant');

		// Roule 08/08/2016 bloc des recherches mémorisées
		//console.log('xxx avant mémo', infos);
		if (infos)
		{
			table = cCAPITAN_MH.prevRecherche(cCAPITAN_MH.idCarte);
			var p = document.createElement('p');
			p.id = 'MZ_capitan_p_liste_memo';
			p.appendChild(table);
			parentElt.appendChild(p);
			// Roule 08/08/2016 bloc préparant les infos pour l'outil Mamoune (Psyko-Chasseurs)
			table = cCAPITAN_MH.blocMamoune(cCAPITAN_MH.idCarte);
			if(table!=null)
			{
				p = document.createElement('p');
				p.appendChild(table);
				parentElt.appendChild(p);
			}
		}
		//console.log('xxx après mémo');
		if (cCAPITAN_MH.mutationObserver)
			cCAPITAN_MH.mutationObserver.observe(document.body, cCAPITAN_MH.MutationObserverConfig);
	};

	static afficheMsg(msg, color, big) {
		let p = document.createElement('p');
		if (color) p.style.color = color;
		if (big) {
			p.style.fontSize = 'xx-large';
			p.style.lineHeight = '150%';
		}
		p.appendChild(document.createTextNode('MZ Capitan : ' + msg));
		let contMsg = document.getElementById('msgEffet');
		if (!contMsg) {
			contMsg = document.evaluate("//div[@class = 'modal']",
			document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
		}
		if (!contMsg) {
			contMsg = document.getElementById('EquipementForm');
			if (contMsg) {
				contMsg.insertBefore(p, contMsg.firstChild);
				return;
			}
		}
		if (!contMsg) contMsg = document.body;
		contMsg.appendChild(p);
	};

	// Roule 08/08/2016
	static blocMamoune(idCarte) {
		let table = cCAPITAN_MH.createHTMLTable();

		var thead = document.createElement('thead');
		var tr = cCAPITAN_MH.appendTr(thead, 'mh_tdtitre');
		var td = cCAPITAN_MH.appendTdText(tr, "Outil du cercle des Psyko-Chasseurs", true);
		td.setAttribute('align', 'center');
		//td.setAttribute('title', 'sélectionnez (triple-clic), copiez et collez dans l\'outil des Psyko-Chasseurs');
		table.appendChild(thead);

		var tbody = document.createElement('tbody');
		table.appendChild(tbody);

		// http://mountyhall.dispas.net/dynamic/outils_capitan.php?x=101&y=8&n=73&t=3+77+30+1%0D%0A37+57+48+0%0D%0A33+32+29+1%0D%0A87+20+74+2%0D%0A17+56+63+0%0D%0A22+89+78+2&voir=1&cent=100&enter=Go#
		var tabtxt = new Array();
		if (cCAPITAN_MH.curPos != undefined) {
			let currentPosAlreadyDone = false;
			if (cCAPITAN_MH.infoCurrentCarte.essais) for (let i = 0; i < cCAPITAN_MH.infoCurrentCarte.essais.length; i++) {
				let oEssai = cCAPITAN_MH.infoCurrentCarte.essais[i];
				tabtxt.push(oEssai.forPsychoChasseur());
				if (cCAPITAN_MH.curPos.sameLocAs(oEssai)) currentPosAlreadyDone = true;
			}
			if (!currentPosAlreadyDone) tabtxt.push(cCAPITAN_MH.curPos.forPsychoChasseur() + '+%3F');	// spécial pour demander à Mamoune ce qu'elle pense d'un essai à la position courante
		}
		var tr2 = cCAPITAN_MH.appendTr(tbody, 'mh_tdpage');
		var td2 = cCAPITAN_MH.appendTd(tr2);
		if(!cCAPITAN_MH.infoCurrentCarte.mort) {
			td2.cCAPITAN_MH.appendText('Erreur\u00A0: impossible de retrouver les coordonnées de la mort');
		} else {
			let a = document.createElement('a');
			cCAPITAN_MH.appendText(a, 'Cliquer ici pour savoir ce qu\'en pensent les Psyko-Chasseurs');
			a.setAttribute('href', 'http://mountyhall.dispas.net/dynamic/outils_capitan.php?' + cCAPITAN_MH.infoCurrentCarte.mort.forGet() + '&t=' + tabtxt.join('%0D%0A') + '&voir=1&cent=100');
			a.setAttribute('target', 'psykochasseurs');
			td2.appendChild(a);
		}

		td.setAttribute('class', 'mh_tdpage');
		//td.setAttribute('width', width);
		td.setAttribute('align', 'center');


		td.addEventListener("click", cCAPITAN_MH.toggleTableau, true);
		td.setAttribute('onmouseover', "this.style.cursor = 'pointer'; this.className = 'mh_tdpage';");
		td.setAttribute('onmouseout', "this.className = 'mh_tdtitre';");
		td.setAttribute('colspan', 2);
		tbody.setAttribute('style', 'display:none;');

		return table;
	};

	static prevRecherche(idCarte) {
		let table = cCAPITAN_MH.createHTMLTable();

		let nbEssai = 0;
		if (cCAPITAN_MH.infoCurrentCarte.essais) nbEssai = cCAPITAN_MH.infoCurrentCarte.essais.length;
		let thead = document.createElement('thead');
		let tr = cCAPITAN_MH.appendTr(thead, 'mh_tdtitre');
		let td = cCAPITAN_MH.appendTdText(tr, "Vous avez mémorisé " + nbEssai + " essai" + (nbEssai > 1 ? "s" : ""), true);
		td.setAttribute('align', 'center');
		table.appendChild(thead);
		if (cCAPITAN_MH.bDebug) console.log(`[Capitan debug] prevRecherche_log nbEssai=${nbEssai}`);

		let tbody = document.createElement('tbody');
		tbody.id = 'MZ_capitan_tbody_liste_memo';
		table.appendChild(tbody);
		let delRecherche = cCAPITAN_MH.delRecherche.bind(this);	// créer une version de delrecherche qui aura le "bon" this
		for (let i = 0; i < nbEssai; i++) {
			let td2 = cCAPITAN_MH.createCase(cCAPITAN_MH.infoCurrentCarte.essais[i].display(),tbody,400);
			let td3 = cCAPITAN_MH.appendTd(td2.parentNode);
			let bt = cCAPITAN_MH.appendButton(td3, "Supprimer", delRecherche);
			bt.idEssai = i;
			bt.idCarte = idCarte;
			td3.setAttribute('class', 'mh_tdpage');
			td3.setAttribute('width', 200);
			td3.setAttribute('align', 'center');
		}

		td.addEventListener("click", cCAPITAN_MH.toggleTableau, true);
		td.setAttribute('onmouseover', "this.style.cursor = 'pointer'; this.className = 'mh_tdpage';");
		td.setAttribute('onmouseout', "this.className = 'mh_tdtitre';");
		td.setAttribute('colspan', 2);
		tbody.setAttribute('style', 'display:none;');

		return table;
	};

	static delRecherche(e) {
		let idEssaiDel = e.target.idEssai;	// index dans le tableau cCAPITAN_MH.infoCartes[idCarte]
		let idCarte = e.target.idCarte;
		let infos = cCAPITAN_MH.infoCartes[idCarte];
		if (!infos) {
			window.console.log(`[Capitan] delRecherche_log: idEssaiDel=${idEssaiDel}, idCarte=${idCarte}, ERREUR, pas d'info du tout`);
			return;
		}
		if (!infos.essais) {
			window.console.log(`[Capitan] delRecherche_log: idEssaiDel=${idEssaiDel}, idCarte=${idCarte}, ERREUR, pas d'essais`);
			return;
		}
		let nbEssai = infos.essais.length;
		if (idEssaiDel >= nbEssai) {
			window.console.log(`[Capitan] delRecherche_log: idEssaiDel=${idEssaiDel}, idCarte=${idCarte}, ERREUR, il n'y a que ${nbEssai} essais`);
			return;
		}
		if (cCAPITAN_MH.bDebug) window.console.log(`[Capitan debug] delRecherche_log: idEssaiDel=${idEssaiDel}, idCarte=${idCarte}, nbEssai=${nbEssai}`);
		infos.essais.splice(idEssaiDel, 1);
		if (cCAPITAN_MH.bDebug) window.console.log('[Capitan debug] delRecherche_log après suppression, il reste ' + JSON.stringify(infos));

		cCAPITAN_MH.saveIntoMH(idCarte);
		cCAPITAN_MH.copyInfo2LocalStorage(idCarte);

		cCAPITAN_MH.reinit();
	};

	static createNewRecherche(parentElt) {
		let p = document.createElement('p');

		let table = cCAPITAN_MH.createHTMLTable();

		var tbody = document.createElement('tbody');
		table.appendChild(tbody);

		var td = cCAPITAN_MH.createCase("Rajouter manuellement  une recherche :",tbody);

		td.appendChild(document.createElement('br'));
		td.appendChild(document.createTextNode("X = "));
		cCAPITAN_MH.addInput(td, "MZ_rX");
		td.appendChild(document.createTextNode(" Y = "));
		cCAPITAN_MH.addInput(td, "MZ_rY");
		td.appendChild(document.createTextNode(" N = "));
		cCAPITAN_MH.addInput(td, "MZ_rN");
		td.appendChild(document.createElement('br'));
		td.appendChild(document.createTextNode("Nombre de chiffres bien placés : "));
		cCAPITAN_MH.addInput(td, "MZ_rBP",1);
		td.appendChild(document.createElement('br'));
		cCAPITAN_MH.appendButton(td, "Ajouter", cCAPITAN_MH.addRecherche.bind(this));

		p.appendChild(table);
		parentElt.appendChild(p);
	};

	static addRecherche() {
		try {
			var x = document.getElementById('MZ_rX').value;
			var y = document.getElementById('MZ_rY').value;
			var n = document.getElementById('MZ_rN').value;
			var nbChiffres = document.getElementById('MZ_rBP').value;
			if(x==null || isNaN(parseInt(x)))
			{
				window.alert("Erreur : champ X mal formaté.");
				return;
			}
			if(y==null || isNaN(parseInt(y)))
			{
				window.alert("Erreur : champ Y mal formaté.");
				return;
			}
			if(n==null || isNaN(parseInt(n)))
			{
				window.alert("Erreur : champ N mal formaté.");
				return;
			}
			if(nbChiffres==null || isNaN(parseInt(nbChiffres)))
			{
				window.alert("Erreur : nombre de chiffres bien placés mal formaté.");
				return;
			}
			cCAPITAN_MH.addOneRecherche(cCAPITAN_MH.getIDCarte(), x, y, n, nbChiffres);
			cCAPITAN_MH.reinit();
		} catch(e) {
			console.log(e);
			window.alert(e);
		}
	};

	static addInput(parent, nom, size)
	{
		var input = document.createElement('input');
		input.setAttribute('type','text');
		input.setAttribute('name',nom);
		input.setAttribute('type','text');
		input.setAttribute('maxlength',size==null?4:size);
		input.setAttribute('size',size==null?4:size);
		input.id = nom;
		parent.appendChild(input);
		return input;
	};

	static analyseResultatRecherche()
	{
		let idCarte = cCAPITAN_MH.getIntegerByID('carte', 'numéro de carte');
		if (idCarte === undefined) return;
		let x = cCAPITAN_MH.getIntegerByID('x', 'x');
		if (cCAPITAN_MH.bDebug) window.console.log("[Capitan debug] infoRecherche_log: X=" + x);
		if (x === undefined) return;	// ne pas utiliser «!» car x peut être «0» ! Le message a déjà été affiché
		let y = cCAPITAN_MH.getIntegerByID('y', 'y');
		if (cCAPITAN_MH.bDebug) window.console.log("[Capitan debug] infoRecherche_log: Y=" + y);
		if (y === undefined) return;
		let n = cCAPITAN_MH.getIntegerByID('n', 'n');
		if (cCAPITAN_MH.bDebug) window.console.log("[Capitan debug] infoRecherche_log: N=" + n);
		if (n === undefined) return;
		let nb = cCAPITAN_MH.getIntegerByID('nb', 'Vous avez retrouvé');
		if (cCAPITAN_MH.bDebug) window.console.log("infoRecherche_log: nb=" + nb);
		if (nb === undefined) return;

		let eMsg = document.getElementById("msgEffet");
		if (eMsg) {
			let msg = eMsg.textContent;
			if (msg.match(/Xcoin/) && msg.match(/Xcoin/)) {
				// fonctionne à la fois pour "Tu es dans..." et "Vous êtes dans..."
				if(!msg.match(/es dans le bon Xcoin/))
					x = -x;
				if(!msg.match(/es dans le bon Ycoin/))
					y = -y;
			} else {
				window.console.log('[Capitan] infoRecherche_log ERREUR, pas Xcoin et Ycoin, le signe ne sera pas mémorisé');
				eMsg = false;
			}
		} else {
			window.console.log('[Capitan] infoRecherche_log ERREUR, pas de msgEffet, le signe ne sera pas mémorisé');
		}

		cCAPITAN_MH.addOneRecherche(idCarte, x, y, n, nb, x, y);

		cCAPITAN_MH.infoCurrentCarte = cCAPITAN_MH.infoCartes[idCarte];
		let table = cCAPITAN_MH.afficheInfoCarte(idCarte, true);

		if (!table) return;
		let p = document.createElement('p');
		p.appendChild(table);
		let t = document.getElementsByTagName('TABLE');
		if (t.length > 0) {
			t[0].parentNode.insertBefore(p, t[0].nextSibling);
		} else {
			document.body.appendChild(p);
		}
	};

	// return undefined if not found
	static getIntegerByID(id, msg) {
		let e = document.getElementById(id);
		if (!e || !e.childNodes || !e.childNodes[0] || !e.childNodes[0].nodeValue) {
			if (msg) window.alert('Script carte de Capitan : impossible de retrouver le ' + msg);
			window.console.log(`[Capitan] getIntegerByID_log ERREUR : impossible de retrouver le ` + (msg ? msg : id));
			return;
		}
		return parseInt(e.childNodes[0].nodeValue);
	};

	///////////////////////////////////
	// debuging
	// essais : objet
	//	.mode : description
	//	.essais : tableau d'objets essai
	//		.mode : description
	//		.essais : tableau de cartes
	//			.noCarte : id de la carte
	//			.essais : tableau d'essais, [x, y, n, nb]
	static AfficheEssais(essais, sMode) {
		var eBigDiv = document.getElementById('ListeEssaiCapitan');
		if (!eBigDiv) {
			var insertPoint = document.getElementById('footer1');
			eBigDiv = document.createElement('table');
			eBigDiv.id = 'ListeEssaiCapitan';
			cCAPITAN_MH.insertBefore(insertPoint, document.createElement('p'));
			cCAPITAN_MH.insertTitle(insertPoint,'Capitan : Liste des essais');
			cCAPITAN_MH.insertBefore(insertPoint, eBigDiv);
			cCAPITAN_MH.addTrEssais(eBigDiv, 'mode', 'carte', 'nombre d\'essais', true);
		}
		if (!essais) {
			cCAPITAN_MH.addTrEssais(eBigDiv, sMode, '', 'pas d\'essai', false);
			return;
		}
		var carte;
		for (carte in essais) {
			cCAPITAN_MH.addTrEssais(eBigDiv, sMode, carte, essais[carte] + ' essai(s)', false);
		}
		if (carte === undefined) {
			cCAPITAN_MH.addTrEssais(eBigDiv, sMode, '', '0 essai', false);
		}
	};

	static addTrEssais(eTable, sMode, sCarte, sText, bBold) {
		var tr = cCAPITAN_MH.appendTr(eTable);
		var td = cCAPITAN_MH.appendTd(tr);
		cCAPITAN_MH.appendText(td, sMode, bBold);
		td = cCAPITAN_MH.appendTd(tr);
		cCAPITAN_MH.appendText(td, sCarte, bBold);
		td = cCAPITAN_MH.appendTd(tr);
		cCAPITAN_MH.appendText(td, sText, bBold);
	};

	static limitInfiniteLoop() {
		if (cCAPITAN_MH.limitxx === undefined) cCAPITAN_MH.limitxx = 1;
		else                                   cCAPITAN_MH.limitxx++;
		if (cCAPITAN_MH.limitxx > 20) {
			console.trace();
			console.log(`cCAPITAN_MH.limitxx=${cCAPITAN_MH.limitxx}`);
			return true;
		}
	}

	////// gestion stockage

	// pour la carte cCAPITAN_MH.idCarte, merge localStrage et infos de MH_capitan_json
	// => met à jour cCAPITAN_MH.infoCartes[idCarte] mais PAS dans cCAPITAN_MH.infoCurrentCarte
	static initCarte(idCarte) {
		if (cCAPITAN_MH.infoCartes[idCarte]) return;	// déjà fait
		if (cCAPITAN_MH.bDebug) console.log(`[Capitan debug] avant merge carte ${idCarte}, infoCartes=${JSON.stringify(cCAPITAN_MH.infoCartes)}`);
		let MH_data = {};
		if (MH_capitan_json != '') MH_data = JSON.parse(MH_capitan_json);
		let info = {};
		let bChanged = false;
		if (MH_data && MH_data[idCarte]) {
			let MH_info = MH_data[idCarte];
			if (MH_info.mort) info.mort = new cCAPITAN_essai(MH_info.mort);
			if (MH_info.essais) {
				info.essais = [];
				for (let v of MH_info.essais)
					info.essais.push(new cCAPITAN_essai(v));
			}
			if (MH_info.signeX) info.signeX = MH_info.signeX;
			if (MH_info.signeY) info.signeY = MH_info.signeY;
			if (cCAPITAN_MH.bDebug) console.log(`[Capitan debug] partir de MH_data, carte ${idCarte}
				MH_data=${JSON.stringify(MH_data)}
				info=${JSON.stringify(info)}`);
		} else {
			if (cCAPITAN_MH.bDebug) console.log(`[Capitan debug] pas de recup MH carte ${idCarte}
				MH_data=${JSON.stringify(MH_data)}`);
		}

		// merge position mort en localStorage
		let txtMortLocalStorage = window.localStorage["capitan."+idCarte+".position"];
		if (!txtMortLocalStorage) {
			// ça peut être normal (ouverture de la carte sur un nouvel appareil)
		} else {
			let oMortLocalStorage = new cCAPITAN_essai(txtMortLocalStorage);
			if (oMortLocalStorage.isValidLoc()) {
				if (info.mort) {
					if (!info.mort.sameLocAs(oMortLocalStorage)) {
						console.log(`[Capitan] divergence de loc de mort
centralisé=${JSON.stringfy(info.mort)}
localStorage='${JSON.stringify(oMortLocalStorage)}`);
					}
				} else {
					info.mort = oMortLocalStorage;
					bChanged = true;
				}
			} else {
				console.log('[Capitan] mauvaise loc de mort en localStorage: ' + window.localStorage["capitan."+idCarte+".position"]);
			}
		}

		// merge essais en localStorage
		let essaiText;
		for (let i = 0; (essaiText = window.localStorage["capitan."+idCarte+".essai."+i]) != null; i++) {
			let oEssai = new cCAPITAN_essai(essaiText);
			if (!oEssai.isValidEssai()) {
				console.log(`[Capitan] mauvaise loc en localStorage: ${essaiText}`);
				continue;
			}
			let bFound = false;
			if (info.essais) for (let oEssai2 of info.essais) {
				if (!oEssai2.sameLocAs(oEssai)) continue;
				if (!oEssai2.sameAs(oEssai)) {
					console.log(`[Capitan] essais incohérents en localStorage: ${essaiText} <-> ${JSON.stringify(oEssai2)}`);
				}
				bFound = true;
				break;
			}
			if (bFound) continue;
			if (!info.essais) info.essais = [];
			if (cCAPITAN_MH.bDebug) window.console.log(`[CAPITAN debug] initCarte_log ajout ${JSON.stringify(oEssai)} à ${JSON.stringify(info.essais)}`);
			info.essais.push(oEssai);
			bChanged = true;
		}

		// merge cadran
		let keyLocalStorageCadran = "capitan."+idCarte+".this.signe";
		let txtCadran = window.localStorage[keyLocalStorageCadran];
		if (txtCadran != null) {
			var signes = txtCadran.split(";");
			if (cCAPITAN_MH.bDebug) window.console.log(`[CAPITAN debug] initCarte_log txtCadran=${txtCadran}, signes=${JSON.stringify(signes)}`);
			if (info.signeX) {
				if (signes[0] != info.signeX) console.log(`[Capitan] signe X incohérent en localStorage: ${signes[0]} <-> ${info.signeX}`);
			} else {
				info.signeX = signes[0];
				bChanged = true;
			}
			if (info.signeY) {
				if (signes[1] != info.signeY) console.log(`[Capitan] signe Y incohérent en localStorage: ${signes[1]} <-> ${info.signeY}`);
			} else {
				info.signeY = signes[1];
				bChanged = true;
			}
		} else if (cCAPITAN_MH.bDebug) {
			window.console.log(`[CAPITAN debug] initCarte_log pas de cadran en localStorage pour ${keyLocalStorageCadran}`);
		}

		/*
		if (originalPosText === undefined) {
			let msg = "La recherche a été enregistrée. Mais vous n'avez pas encore affiché le détail de la carte "
				+ idCarte + " et le « script du Capitan » ne connait pas la position de la mort du Capitan. Il ne peut pas vous en dire plus. Allez dans «  EQUIPEMENT » et affichez cette carte.";
			window.console.log('afficheInfoCarte_log: ' + msg);
			cCAPITAN_MH.afficheMsg(msg, 'red');
			return;
		}
		}
		*/
		if (cCAPITAN_MH.limitInfiniteLoop()) cCAPITAN_MH.initCarte_log = undefined;
		cCAPITAN_MH.infoCartes[idCarte] = info;
		if (bChanged) cCAPITAN_MH.saveIntoMH(idCarte);
		if (cCAPITAN_MH.bDebug) console.log(`[Capitan debug] après merge carte ${idCarte}, infoCartes=${JSON.stringify(cCAPITAN_MH.infoCartes)}`);
	};

	static addOneRecherche(idCarte, x, y, n, nbChiffres, signeX, signeY) {
		cCAPITAN_MH.initCarte(idCarte);
		let info = cCAPITAN_MH.infoCartes[idCarte];
		let newEssai = new cCAPITAN_essai(x, y, n, nbChiffres);
		if (info.essais) for (let oEssai of info.essais) {
			if (!oEssai.sameLocAs(newEssai)) continue;
			if (!oEssai.sameAs(newEssai)) {
				console.log(`[Capitan] tentative d'ajout d'un essai même loc, résultat différent carte=${idCarte} nouveau=${newEssai.display()} existant=${oEssai.display()}`);
				newEssai.c = undefined;
				cCAPITAN_MH.afficheMsg(`tentative d'ajout d'un 2e essai en ${newEssai.display()}. Supprimez d'abord le précédent`, 'red');
			} else {
				console.log(`[Capitan] tentative d'ajout d'un essai déjà existant carte=${idCarte} essai=${newEssai.display()}`);
			}
			return;
		}
		if (!info.essais) info.essais = [newEssai];
		else              info.essais.push(newEssai);
		if (signeX && signeY) {	// 0 serait bien une erreur
			info.signeX = signeX > 0 ? 1 : -1;
			info.signeY = signeY > 0 ? 1 : -1;
		}
		cCAPITAN_MH.saveIntoMH(idCarte);
		cCAPITAN_MH.copyInfo2LocalStorage(idCarte);
	};

	static saveIntoMH(idCarte) {
		let url = window.location.origin;	// https://gams.mountyhall.com
		url += '/mountyhall/MH_PageUtils/Services/json_extension.php';
		url += '?mode=set&ext=capitan&clef=' + idCarte;
		let request = new XMLHttpRequest();
		request.open('POST', url);
		request.onreadystatechange = function () {
			if (request.readyState != 4) {
				return;
			}
			if (request.error) {
				window.console.error('[Capitan] erreur set config MH : ' + request.error);
				return;
			}
			if (cCAPITAN_MH.bDebug) console.log(`[Capitan debug] retour AJAX set prop MH ${request.responseText}`);
			if (request.responseText.indexOf('"OK"') < 0) {
				console.log(`[Capitan] ERREUR à l'envoi des information sur le serveur MH, le message d'erreur est: ${request.responseText}`);
			}
		};
		if (cCAPITAN_MH.bDebug) console.log(`[Capitan debug] envoi à MH data pour ${idCarte}, info=${JSON.stringify(cCAPITAN_MH.infoCartes[idCarte])}`);
		request.send(cCAPITAN_MH.escapeUnicode(JSON.stringify(cCAPITAN_MH.infoCartes[idCarte])));
	};

	static copyInfo2LocalStorage(idCarte) {
		let info = cCAPITAN_MH.infoCartes[idCarte];
		if (!info) {
			console.log(`[Capitan] ERREUR à la sauvegarde de la carte ${idCarte} : pas d'info`);
			return;
		}
		// mort
		if (info.mort) {
			let keyMort = "capitan."+idCarte+".position";
			let txtInfo = info.mort.forLocalStrorage();
			let txtLocalStorage = window.localStorage.getItem(keyMort);
			if (txtInfo != txtLocalStorage) window.localStorage.setItem(keyMort, txtInfo);
		}

		// cadran
		if (info.signeX || info.signeY) {
			let keyCadran = "capitan."+idCarte+".this.signe";
			let txtInfo = info.signeX + ';' + info.signeY;
			let txtLocalStorage = window.localStorage.getItem(keyCadran);
			if (txtInfo != txtLocalStorage) window.localStorage.setItem(keyCadran, txtInfo);
		}

		// essais : on balaie les index en comparant les essais
		// tant que c'est pareil, on avance
		// quand ce n'est plus pareil, on supprimer out le reste coté LocalStorage et on réécrit
		let iEssai;
		if (info.essais) for (iEssai=0; iEssai < info.essais.length; iEssai++) {
			let keyEssai = 'capitan.' + idCarte+'.essai.' + iEssai;
			let txtInfo = info.essais[iEssai].forLocalStrorage();
			let txtLocalStorage = window.localStorage.getItem(keyEssai);
			if (txtInfo != txtLocalStorage) break;
		}
		// supprimer la suite coté localStorage (iEssai jusqu'aux les 10 suivant le dernier existant pour faire bonne mesure)
		let iLastLocalStorage = iEssai;
		for (let i2 = iEssai; i2 < iLastLocalStorage + 10; i2++) {
			let keyEssai = 'capitan.' + idCarte +'.essai.' + i2;
			if (!window.localStorage.getItem(keyEssai)) continue;
			iLastLocalStorage = i2;
			window.localStorage.removeItem(keyEssai);
		}
		// ajouter le reste de Info dans localStorage
		if (info.essais) for (let i2=iEssai; i2 < info.essais.length; i2++) {
			let keyEssai = 'capitan.' + idCarte +'.essai.' + i2;
			let txtInfo = info.essais[i2].forLocalStrorage();
			window.localStorage.setItem(keyEssai, txtInfo);
		}
	}

	static escapeUnicode(str) { // https://stackoverflow.com/questions/62449035/escape-all-unicode-non-ascii-characters-in-a-string-with-javascript
		return [...str].map(c => /^[\x00-\x7F]$/.test(c) ? c : c.split("").map(a => "\\u" + a.charCodeAt().toString(16).padStart(4, "0")).join("")).join("");
	}

	//////

	static reinit() {
		if (cCAPITAN_MH.gDiv) cCAPITAN_MH.gDiv.parentNode.removeChild(cCAPITAN_MH.gDiv);
		cCAPITAN_MH.analyseObject();
	};

	static MutationObserverConfig = { childList: true, subtree: true };

	static init() {
		if (typeof MH_capitan_json === 'undefined') {
			let msg = "Le script du Capitan doit maintenant fonctionner en mode intégré. Activez-le via Options/Extensions et désactivez-le sous xxxMonkey ou sur le relai Raistlin";
			console.log(`[Capitan ) ${msg}`);
			cCAPITAN_MH.afficheMsg(msg, 'red', true);
			return;
		}
		cCAPITAN_MH.MZ_ok = (typeof MH_mountyzilla_json !== 'undefined');
		if (cCAPITAN_MH.bDebug) console.log('[Capitan debug] init MH_capitan_json=', MH_capitan_json);

		// charger le numéro du Troll
		let frameSommaire;
		let eltId = document.getElementById('footer');	// cas smartphone
		let p = window.parent;	// cas classique (pas smartphone)
		let iTry = 0;
		while (p) {	// cas classique (pas smartphone)
			//if (cCAPITAN_MH.bDebug) console.log(p);
			frameSommaire = p.frames['Sommaire'];
			if (frameSommaire) break;
			if (iTry++ > 10) break;	// ceinture et bretelle contre une boucle infinie
			let p2 = p.parent;
			if (p2 === p) break;	// top window est son propre parent
			p = p2;
		}
		if (frameSommaire) eltId = frameSommaire.document.getElementById ('id');
		if (eltId) {
			cCAPITAN_MH.numTroll = parseInt(eltId.getAttribute('data-id'));
			if (cCAPITAN_MH.bDebug) console.log(`[Capitan debug] troll id=${cCAPITAN_MH.numTroll}`);
		} else cCAPITAN_MH.numTroll = 0;	// on continue, pas de problème en mode intégré sauf qu'on ne récupère pas les anciennes cartes
		if (cCAPITAN_MH.numTroll == 0) console.log('[Capitan] erreur à la récupération du numéro de Troll');
		// position courante du Troll
		eltId = undefined;
		if (frameSommaire) eltId = frameSommaire.document.getElementById ('DLA_xyn');
		if (eltId) {
			let m = eltId.innerText.match(/X\s*=\s*([-\d]+)[\s|]+Y\s*=\s*([-\d]+)[\s|]+N\s*=\s*([-\d]+)/im);
			if (m && m.length == 4) {
				cCAPITAN_MH.curPos = new cCAPITAN_essai(m[1], m[2], m[3]);
				if (cCAPITAN_MH.bDebug) console.log(`[Capitan debug] init: pos from MH ${eltId.innerText}`)
			} else {
				console.log(`[Capitan] init_log erreur analyse position troll pour ${eltId.innerText}`);
			}
		}
		if (cCAPITAN_MH.MZ_ok && !cCAPITAN_MH.curPos) {
			// Roule 08/08/2016 utilisation de localStorage car c'est là que tout_MZ stocke les coord
			cCAPITAN_MH.curPos = new cCAPITAN_essai(window.localStorage[cCAPITAN_MH.numTroll+".position.X"],
				window.localStorage[cCAPITAN_MH.numTroll+".position.Y"],
				window.localStorage[cCAPITAN_MH.numTroll+".position.N"]);
			if (cCAPITAN_MH.bDebug) window.console.log('[Capitan debug] init_log: position du troll récupérée en localStorage');
		}
		if (cCAPITAN_MH.curPos && !cCAPITAN_MH.curPos.isValidLoc()) {
			console.log(`[Capitan] init_log position troll invalide: ${JSON.stringify(cCAPITAN_MH.curPos)}`);
			cCAPITAN_MH.curPos = undefined;
		}
		if (cCAPITAN_MH.bDebug) window.console.log('[Capitan debug] init_log: position du troll=' + JSON.stringify(cCAPITAN_MH.curPos));

		if (cCAPITAN_MH.isPage("View/TresorHistory.php"))
		{
			cCAPITAN_MH.analyseObject();
		}
		else if(cCAPITAN_MH.isPage("MH_Play/Play_a_ActionResult.php") || cCAPITAN_MH.isPage("MH_Play/Play_a_TrouverCachette2.php"))
		{
			// uniquement si l'id du body est p_trouverunecachette
			if (document.body.id != 'p_trouverunecachette') return;
			cCAPITAN_MH.analyseResultatRecherche();
		}
		else if(cCAPITAN_MH.isPage("MH_Play/Play_equipement.php") || cCAPITAN_MH.isPage("MH_Taniere/TanierePJ_o_Stock.php"))
		{
			cCAPITAN_MH.mutationObserver = new MutationObserver(cCAPITAN_MH.analyseObject.bind(this));
			cCAPITAN_MH.mutationObserver.observe(document.body, cCAPITAN_MH.MutationObserverConfig);
			if (cCAPITAN_MH.bDebug) console.log(`[Capitan debug] init activation mutationObserver`);
		}
		if (cCAPITAN_MH.bDebug) console.log(`[Capitan debug] fin init`);
	};
}

class cCAPITAN_essai {
	constructor(x, y, n, c) {
		if (y == undefined) {	// initialisation à partir d'une chaine séparée par ";", d'un array ou d'un objet
			if (typeof x === 'string' || x instanceof String) {
				let t = x.split(";");
				this.x = parseInt(t[0], 10);
				this.y = parseInt(t[1], 10);
				this.n = parseInt(t[2], 10);
				if (t.length > 3) this.c = parseInt(t[3], 10);
			} else if (Array.isArray(x)) {
				this.x = parseInt(x[0], 10);
				this.y = parseInt(x[1], 10);
				this.n = parseInt(x[2], 10);
				if (x.length > 3) this.c = parseInt(x[3], 10);
			} else if (x != undefined) {
				this.x = x.x;
				this.y = x.y;
				this.n = x.n;
				if (x.c !== undefined) this.c = x.c;
			}
		} else {
			this.x = parseInt(x, 10);
			this.y = parseInt(y, 10);
			this.n = parseInt(n, 10);
			if (c !== undefined) this.c = parseInt(c, 10);
		}
	};

	coord2text(coord) {
		let t = coord + '';
		if (t.length < 2) t = '0' + t;
		return t;
	};

	xAbs() {return Math.abs(this.x);};
	yAbs() {return Math.abs(this.y);};
	nAbs() {return Math.abs(this.n);};
	xText() {return this.coord2text(Math.abs(this.x));};
	yText() {return this.coord2text(Math.abs(this.y));};
	nText() {return this.coord2text(Math.abs(this.n));};

	display() {	// pour les humains
		let ret = 'X = ' + this.x + ', Y = ' + this.y +', N = ' + this.n;
		if (this.c !== undefined) ret += ' => ' + this.c;
		return ret;
	};

	forGet() {
		return 'x=' + this.x + '&y=' + this.y +'&n=' + this.n;
	};

	forLocalStrorage() {
		let ret = this.x + ';' + this.y +';' + this.n;
		if (this.c !== undefined) ret += ';' + this.c;
		return ret;
	}

	forPsychoChasseur() {	// rend le bout de texte à mettre dans l'URL vers l'outil des Psycho Chasseurs
		let ret = Math.abs(this.x) + '+' + Math.abs(this.y) + '+' + Math.abs(this.n);
		if (this.c !== undefined) ret += '+' + this.c;
		return ret;
	};

	isValidLoc() {
		if (this.x === undefined || isNaN(this.x)) return false;
		if (this.y === undefined || isNaN(this.y)) return false;
		if (this.n === undefined || isNaN(this.n)) return false;
		return true;
	};

	isValidEssai() {
		if (!this.isValidLoc) return false;
		if (this.c === undefined || isNaN(this.c)) return false;
		return true;
	};

	sameLocAs(oOther) {
		if (this.x != oOther.x) return false;
		if (this.y != oOther.y) return false;
		if (this.n != oOther.n) return false;
		return true;
	};

	sameAs(oOther) {
		if (!this.sameLocAs(oOther)) return false;
		if (this.c !== oOther.c) return false;
		return true;
	};

	is200() {	// vrai si au moins une coord >= 200
		if (Math.abs(this.x) >= 200) return true;
		if (Math.abs(this.y) >= 200) return true;
		if (Math.abs(this.n) >= 200) return true;
		return false;
	};

	nbMatchesOne(t1, t2) {
		t1 = '' + parseInt(t1, 10);	// virer le zéro à gauche. MH n'en tient pas compte quand il compte le nombre de match
		t2 = '' + parseInt(t2, 10);
		let nRet = 0;
		let l1 = t1.length;
		let l2 = t2.length;
		for (let i = 0; i < l1 && i < l2; i++)
			if (t1.substring(l1 - (i+1), l1 - i) == t2.substring(l2 - (i+1), l2 - i)) nRet++;
		return nRet;
	};

	nbMatches(oOther) {
		let nMatches = this.nbMatchesOne(this.xText(), oOther.x);
		nMatches += this.nbMatchesOne(this.yText(), oOther.y);
		nMatches += this.nbMatchesOne(this.nText(), oOther.n);
		return nMatches;
	}

	isCompatible(tabCoord) {	// vérifie si c'est compatible avec les coord passées en argument sous forme de tableau de chaines
		let nMatches = this.nbMatchesOne(this.xText(), tabCoord[0]);
		nMatches += this.nbMatchesOne(this.yText(), tabCoord[1]);
		nMatches += this.nbMatchesOne(this.nText(), tabCoord[2]);
		return nMatches == this.c
	};

	nbChiffre() {	// rend le nombre de chiffres (une coord à 1 chiffe en donne 2, le "0" et le chiffre des unités)
		return this.xText().length + this.yText().length + this.nText().length;
	};

	tabOccurenceChiffre() {	// le nombre d'occurrences de chaque chiffre (0 à 9) dans les coord
		let tabRet = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
		this.addOccurenceChiffre(tabRet, this.xText());
		this.addOccurenceChiffre(tabRet, this.yText());
		this.addOccurenceChiffre(tabRet, this.nText());
		return tabRet;
	};

	addOccurenceChiffre(t, s) {
		let l = s.length;
		for (let i = 0; i < l; i++) {
			let c = s.substring(i, i+1);
			let n = parseInt(c, 10);
			if (!isNaN(n)) t[n]++;
		}
	};

	move(dx, dy, dn) {
		this.x += dx;
		this.y += dy;
		this.n += dn;
	}
};

try
{
	cCAPITAN_MH.init();
} catch(e) {
	window.console.log('script capitan exception', e, e.stack);
}