Index: /pom.xml
===================================================================
--- /pom.xml	(revision 26)
+++ /pom.xml	(revision 27)
@@ -7,5 +7,5 @@
 	<artifactId>runserver</artifactId>
 	<packaging>war</packaging>
-	<version>1.5.9</version> <!-- must equal ${geniusweb.version} -->
+	<version>1.6.0</version> <!-- must equal ${geniusweb.version} -->
 	<name>runserver Maven Webapp</name>
 	<url>http://maven.apache.org</url>
@@ -20,5 +20,5 @@
 		<jackson-2-version>2.9.10</jackson-2-version>
 		<tomcat.version>8.0.53</tomcat.version>
-		<geniusweb.version>1.5.9</geniusweb.version>
+		<geniusweb.version>1.6.0</geniusweb.version>
 	</properties>
 
Index: /src/main/webapp/newsession.xhtml
===================================================================
--- /src/main/webapp/newsession.xhtml	(revision 26)
+++ /src/main/webapp/newsession.xhtml	(revision 27)
@@ -22,4 +22,6 @@
 		<option value="SHAOP">SHAOP (Stacked Human Alternating Offers
 			Protocol)</option>
+		<option value="Learn">Learn</option>
+			
 	</select>
 
@@ -45,7 +47,8 @@
 	</select>
 
+	<div id="profserverselector">
 	<br /> Domain/Profile Server:
 	<input type="url" name="url" id="profilesserverurl"
-		value="localhost:8080/profilesserver-1.5.9"
+		value="localhost:8080/profilesserver-1.6.0"
 		pattern=".*:[0-9]+/profilesserver.*" size="30"
 		onchange="connectDomain()"> </input>
@@ -54,5 +57,6 @@
 		<!-- <option>Waiting for profiles server</option> -->
 	</select>
-
+	</div>
+	
 	<br />
 	<br />
@@ -61,10 +65,14 @@
 		<br /> <b>Participants</b> <br /> Parties Server: <input type="url"
 			name="url" id="partiesserverurl"
-			value="localhost:8080/partiesserver-1.5.9"
+			value="localhost:8080/partiesserver-1.6.0"
 			pattern=".*:[0-9]+/partiesserver.*" size="30"
 			onchange="connectParties()"> </input> <br /> <br /> <b>Party
 			settings</b> <br /> Party : <select id="partyselection">
-		</select> <br /> Profile: <select id="profileselection"></select> Filter: <input
+		</select> <br /> 
+		
+		<div id="profileselector">
+		Profile: <select id="profileselection"></select> Filter: <input
 			type="text" id="filter" value="" maxlength="40" /> <br />
+		</div>
 		Parameters: {
 		<textarea id="parameters" rows="2" cols="70"
@@ -214,6 +222,28 @@
     	document.getElementById("votingevaluator2").style.display=(evaluator2visible ? 'block': 'none');
 
-    }   
-        
+    	
+    	var profileselectorvisible=!isUsingFakeProfile();
+    	document.getElementById("profserverselector").style.display=(profileselectorvisible ? 'block': 'none');
+    	document.getElementById("profileselector").style.display=(profileselectorvisible ? 'block': 'none');
+    	if (getSelectedProtocol() == "Learn") {
+    		params = {"persistentstate":createUUID(), "negotiationdata":[ createUUID() ] };
+    		str=JSON.stringify(params);
+    		// remove outer brackets from the text in the box
+        	document.getElementById("parameters").value=str.substring(1, str.length-1);
+    		updateParameters();
+    	}
+    }
+
+    /**
+    @return true iff we need to use a fake profile.
+    */
+    function isUsingFakeProfile() {
+    	return  getSelectedProtocol() == "Learn";
+    }
+    
+    function isUsingCobParty() {
+    	return getSelectedProtocol() == "SHAOP";
+    }
+    
 	/**
 	Refresh known domains using given profilesserver URL. 
@@ -304,8 +334,8 @@
 	 refresh table: copy all parties elements in there.
 	 Typically parties is something like
-	 [{"uri":"http:130.161.180.1:8080/partiesserver/run/randomparty-1.5.9",
+	 [{"uri":"http:130.161.180.1:8080/partiesserver/run/randomparty-1.6.0",
 		 "capabilities":{"protocols":["SAOP"]},
 		 "description":"places random bids until it can accept an offer with utility >0.6",
-		 "id":"randomparty-1.5.9",
+		 "id":"randomparty-1.6.0",
 		 "partyClass":"geniusweb.exampleparties.randomparty.RandomParty"},
 		 		 ...]
@@ -391,5 +421,7 @@
 	function addParty() {
 		addNormalParty();
-		addCobParty();
+		if (isUsingCobParty()) {
+			addCobParty();
+		}
 		updatePartyProfileTable(); // what, MVC?
 	}
@@ -400,11 +432,17 @@
 		var filteroptions = document.getElementById("filter").value;
 		
-		if (partycombo.options.length==0) {
-			alert("Please set partier server and select a party");
-			return;
-		}
-		if (profilecombo.options.length==0) {
-			alert("Please set domain/profile server and select a domain and a profile");
-			return;
+		var selectedprofile;
+		if (isUsingFakeProfile()) {
+			selectedprofile="not://valid";
+		} else {
+			if (partycombo.options.length==0) {
+				alert("Please set partier server and select a party");
+				return;
+			}
+			if (profilecombo.options.length==0) {
+				alert("Please set domain/profile server and select a domain and a profile");
+				return;
+			}
+			selectedprofile=profilecombo.options[profilecombo.selectedIndex].value +filteroptions;
 		}
 		
@@ -415,5 +453,5 @@
 		newpartyprof["party"]={"partyref":partycombo.options[partycombo.selectedIndex].value , 
 				"parameters":parameters };
-		newpartyprof["profile"]=profilecombo.options[profilecombo.selectedIndex].value +filteroptions;
+		newpartyprof["profile"]=selectedprofile;
 		
 		partyprofiles.push(newpartyprof)
@@ -464,8 +502,5 @@
 			var cell2 = row.insertCell(-1);
 			var cell3 = row.insertCell(-1);
-			var cell4 = row.insertCell(-1);
-			var cell5 = row.insertCell(-1);
-			var cell6 = row.insertCell(-1);
-			
+
 			cell1.innerHTML = partyprofiles[pp]["party"]["partyref"];
 			// help browser breaking too large strings 
@@ -473,8 +508,16 @@
 			cell2.setAttribute("style","overflow-wrap: anywhere;");
 			cell3.innerHTML = partyprofiles[pp]["profile"];
-			cell4.innerHTML = cobpartyprofiles[pp]["party"]["partyref"];
-			cell5.innerHTML = JSON.stringify(cobpartyprofiles[pp]["party"]["parameters"]).replace(/,/g,", ");
-			cell5.setAttribute("style","overflow-wrap: anywhere;");
-			cell6.innerHTML = cobpartyprofiles[pp]["profile"];
+
+			if (isUsingCobParty()) {
+				var cell4 = row.insertCell(-1);
+				var cell5 = row.insertCell(-1);
+				var cell6 = row.insertCell(-1);
+				cell4.innerHTML = cobpartyprofiles[pp]["party"]["partyref"];
+				cell5.innerHTML = JSON.stringify(cobpartyprofiles[pp]["party"]["parameters"]).replace(/,/g,", ");
+				cell5.setAttribute("style","overflow-wrap: anywhere;");
+				cell6.innerHTML = cobpartyprofiles[pp]["profile"];
+
+			}
+			
 		}
 
@@ -496,6 +539,7 @@
 	*/
 	function start() {
-		if (Object.keys(partyprofiles).length <2) {
-			alert("At least 2 parties are needed to run a session.");
+		var minParties = getSelectedProtocol() == "Learn" ? 1: 2;
+		if (Object.keys(partyprofiles).length < minParties) {
+			alert("At least "+minParties+" parties are needed to run a "+getSelectedProtocol()+" session.");
 			return;
 		}
@@ -568,4 +612,5 @@
 		case"SAOP":
 		case "AMOP":
+		case "Learn":
 			return makeStdRequest(getSelectedProtocol()+"Settings");
 		case "MOPAC":
@@ -650,4 +695,16 @@
 	}
 
+	/**
+	@return a random UUID. 
+	Note − This should not be used in production as GUID or UUID 
+	generated by Math.Random() may not be unique.
+	*/
+	function createUUID() {
+		   return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+		      var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
+		      return v.toString(16);
+		   });
+	}
+
 	
 	/**
@@ -656,6 +713,6 @@
 	function init() {
 		selectProtocol();
-		document.getElementById("partiesserverurl").value =window.location.hostname+":8080/partiesserver-1.5.9"
-		document.getElementById("profilesserverurl").value =window.location.hostname+":8080/profilesserver-1.5.9"
+		document.getElementById("partiesserverurl").value =window.location.hostname+":8080/partiesserver-1.6.0"
+		document.getElementById("profilesserverurl").value =window.location.hostname+":8080/profilesserver-1.6.0"
 		connectDomain();
 		connectParties();
@@ -672,5 +729,4 @@
 
 
-
 </script>
 
Index: /src/main/webapp/newtournament.xhtml
===================================================================
--- /src/main/webapp/newtournament.xhtml	(revision 26)
+++ /src/main/webapp/newtournament.xhtml	(revision 27)
@@ -13,26 +13,30 @@
 	<br />
 
-	<select>
-		<option value="APP">All Permutations Tournament Protocol
-			(APP)</option>
-	</select>
-	<br />
-	<input type="number" id="ntournaments" min="1" max="1000000" value="1" />
-	Number of times to repeat the entire tournament
-	<br />
-
-	<br />
-	<input type="number" id="teamspersession" min="2" max="10" value="2" />
-	Number of teams in each session
-	<br />
-
-	<input type="checkbox" id="reuseteams" /> Pick teams with return when
-	creating sessions
-	<br />
+
+	<div id="box" class="box">
+		<h3>Tournament global settings</h3>
+		<select id="tourprotocol" onchange="updateTournamentProt()">
+			<option value="AllPermutations">All Permutations Tournament Protocol
+				(APP)</option>
+			<option value="AllPermutationsLearn">APP with learning</option>
+		</select> Tournament protocol <br />
+		<div id="learntimebox">
+			<input type="number" id="learntime" name="learntime" min="1"
+				max="10000" value="30" /> Learning time (s)
+		</div>
+
+		<br /> <input type="number" id="ntournaments" min="1" max="1000000"
+			value="1" /> Number of times to repeat the entire tournament <br />
+
+		<br /> <input type="number" id="teamspersession" min="2" max="10"
+			value="2" /> Number of teams in each session <br /> <input
+			type="checkbox" id="reuseteams" /> Pick teams with return when
+		creating sessions <br />
+	</div>
 
 	<br />
 	<div id="box" class="box">
 		Session Protocol settings <br /> Session Protocol: <select
-			id="protocolselection" onchange="selectProtocol()">
+			id="protocolselection" onchange="selectSessionProtocol()">
 			<option value="SAOP">SAOP</option>
 			<option value="MOPAC">MOPAC</option>
@@ -40,8 +44,6 @@
 			<option value="AMOP">AMOP</option>
 			<option value="SHAOP">SHAOP</option>
-
-
-		</select> 
-		
+		</select>
+
 		<div id="votingevaluator">
 			<br /> Voting Evaluator: <select id="selectedevaluator">
@@ -54,8 +56,9 @@
 		<div id="votingevaluator2">
 			<br /> Voting Evaluator: <select id="selectedevaluator2">
-				<option value="LargestAgreementWithValue">Largest Agreement with Value</option>
+				<option value="LargestAgreementWithValue">Largest Agreement
+					with Value</option>
 			</select>
 		</div>
-		
+
 		<br /> Deadline: <input type="number" id="deadlinevalue"
 			name="deadline" min="1" max="10000" value="10" /> <select
@@ -186,4 +189,8 @@
 	
 	
+	
+	
+	
+	
 	// FIXME quick and dirty code. No clean MVC
 	
@@ -201,9 +208,33 @@
         var profiles=[]
 
+    /**
+    The tournament protocol was changed.
+    */
+    function updateTournamentProt() {
+       	updateParameters();
+        var tourprot = document.getElementById("tourprotocol").value;
+       	document.getElementById("learntimebox").style.display=(tourprot=="AllPermutationsLearn" ? 'block': 'none');
+       	
+    }
+        
+    /**
+    Parameters are needed if protocol is APP.
+    */
+    function updateParameters() {
+        var tourprot = document.getElementById("tourprotocol").value;
+   		if (tourprot=='AllPermutationsLearn') {
+    		var params = {"persistentstate":createUUID() };
+    		var str=JSON.stringify(params);
+    		// remove outer brackets from the text in the box
+        	document.getElementById("parameters").value=str.substring(1, str.length-1);
+   		}
+    }
+        
+        
  	/**
  	called when user changes the protocol.
  	With SHAOP we show COB selectors and COB columns in the tables.
     */
-    function selectProtocol() {
+    function selectSessionProtocol() {
             const protocol = document.getElementById("protocolselection").value;
         	setStyleSheet('.onlySHAOP','{ display: '+(protocol=="SHAOP"? '?':'none')+'; }')
@@ -414,4 +445,5 @@
 		teamslist.push(team);
 		updateTeamsTable();
+		updateParameters(); // generate new UUID for next party
 	}
 	
@@ -436,5 +468,5 @@
 		}
 		
-		selectProtocol(); // HACK we need to KEEP onlySHAOP elements hidden
+		selectSessionProtocol(); // HACK we need to KEEP onlySHAOP elements hidden
 	}
 
@@ -478,5 +510,5 @@
 			}			
 		}
-		selectProtocol(); // HACK we need to KEEP onlySHAOP elements hidden
+		selectSessionProtocol(); // HACK we need to KEEP onlySHAOP elements hidden
 	}
 	
@@ -589,20 +621,50 @@
 				profilesFull.push({"ProfileList":[profileset[0]]});
 		}
-		
-		return JSON.stringify({"AllPermutationsSettings":{"teams":teamsFull,"profileslists":profilesFull,
-			"reuseTeams":reuseTeams,"teamsPerSession":npersession,"sessionsettings":sessionSettings,
-			"numberTournaments":ntournaments}});
-	}
-	
+
+   		var settings={"teams":teamsFull,"profileslists":profilesFull,
+   				"reuseTeams":reuseTeams,"teamsPerSession":npersession,
+   				"sessionsettings":sessionSettings,
+   				"numberTournaments":ntournaments};
+
+		// add the Learn settings if needed
+        var tourprot = document.getElementById("tourprotocol").value;
+        var learntime = 1000*document.getElementById("learntime").value;
+   		if (tourprot=='AllPermutationsLearn') {
+			settings["learnSettings"]={
+				"LearnSettings":{
+					"participants":[], 
+					"deadline": {"deadlinetime": { "durationms": learntime }}
+				}
+			} ;
+   		}
+   		
+   		var mainheader=tourprot+"Settings";
+		return JSON.stringify({[mainheader]:settings});
+	}
+		
+		
+	/**
+	@return a random UUID. 
+	Note − This should not be used in production as GUID or UUID 
+	generated by Math.Random() may not be unique.
+	*/
+	function createUUID() {
+		   return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
+		      var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
+		      return v.toString(16);
+		   });
+	}
+
 	/**
 	Initialize the page after html is loaded. 
 	*/
 	function init() {
-		document.getElementById("partiesserverurl").value =window.location.hostname+":8080/partiesserver-1.5.9";
-		document.getElementById("profilesserverurl").value =window.location.hostname+":8080/profilesserver-1.5.9";
-
-		selectProtocol();
+		document.getElementById("partiesserverurl").value =window.location.hostname+":8080/partiesserver-1.6.0";
+		document.getElementById("profilesserverurl").value =window.location.hostname+":8080/profilesserver-1.6.0";
+
+		selectSessionProtocol();
 		connectDomain();
 		connectParties();
+		updateTournamentProt();
 	}
 
@@ -625,4 +687,8 @@
 
 
+
+
+
+
 </script>
 
Index: /src/main/webapp/utilstable.xhtml
===================================================================
--- /src/main/webapp/utilstable.xhtml	(revision 26)
+++ /src/main/webapp/utilstable.xhtml	(revision 27)
@@ -39,21 +39,22 @@
 </body>
 
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-	
-
-	
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
 <!-- Script to get/update the  table using the query given in the URL. -->
 <script type="application/javascript">
+	
 	
 	
@@ -105,6 +106,9 @@
         function processLogFile(json) {
         	if (json['AllPermutationsState']!=undefined) {
-        		processAPP(json['AllPermutationsState']);
-        	} else {
+        		processAPP(json['AllPermutationsState'], 'AllPermutationsSettings');
+        	} else if (json['AllPermutationsLearnState']!=undefined) {
+        		processAPP(json['AllPermutationsLearnState'],'AllPermutationsLearnSettings');
+        	} else    	
+        	{
             	setStatus("Unknown log file contents "+Object.keys(json));
             }
@@ -113,9 +117,12 @@
         /**
         Handle AllPermutationsState protocol result.
-        */
-        function processAPP(json) {
+        @param json the json contents of the file inside the top level header
+        @param settingsname the name of the expected settings object inside, 
+        eg 'AllPermutationSettings'
+        */
+        function processAPP(json, settingsname) {
         	setStatus("processing the log file");
         	
-        	if (json['toursettings']['AllPermutationsSettings']['sessionsettings']['SHAOPSettings']!=undefined) {
+        	if (json['toursettings'][settingsname]['sessionsettings']['SHAOPSettings']!=undefined) {
         		SHAOP=true;
         	}
@@ -123,5 +130,5 @@
         	// collect all profiles from all profileslists
         	var profiles= [];
-        	for (const profilelist of json['toursettings']['AllPermutationsSettings']['profileslists']) {
+        	for (const profilelist of json['toursettings'][settingsname]['profileslists']) {
         		profiles = profiles.concat(profilelist['ProfileList']); 
         	}
@@ -474,4 +481,5 @@
 
 
+
 </script>
 
