var MinimumPTP=0;
var MinimumMTP=0;
var Race=0;
var Profession=0;
var Physical = new Array(0,1,5,6);
var Mental = new Array(3,4,7,8);
var MinimumStats = new Array(0,0,0,0,0,0,0,0,0,0);
var Stat = new Array(0,0,0,0,0,0,0,0,0,0);
var BestStats = new Array(10);
var GI = new Array(10);
var TotalPTP=0;
var TotalMTP=0;
var TotalTP=0;
var BestPTP=0;
var BestMTP=0;
var BestTP=0;
var BestRawPTP=0;
var BestRawMTP=0;
var BestRawTP=0;
var RawPTP=0;
var RawMTP=0;
var RawTP=0;
var Iterations=1;
var Rand1=0;
var Rand2=0;
var Rand3=0;
var MaxLevel=0;
var AllStats = new Array(10);
var PhysicalWeight=1.0;
var MentalWeight=1.0;
var ModifiedTotal;
var BestModifiedTotal;
var rawphy=0;
var rawmen=0;
var dicaur=0;

//set up array of each profession's prime stat modifiers
//                       CO DE DI LO IN ST RE CH WI AU
var ProfessionMod =  [	[10, 0, 0, 0, 0,10, 0, 0, 0, 0],  //Warrior
			[ 0,10, 0, 0, 0, 0,10, 0, 0, 0],  //Rogue
			[ 0, 0, 0, 0,10, 0, 0, 0, 0,10],  //Wizard
			[ 0, 0, 0,10, 0, 0, 0, 0,10, 0],  //Cleric
			[ 0, 0, 0,10, 0, 0, 0, 0,10, 0],  //Empath
			[ 0, 0, 0, 0, 0, 0, 0, 0,10,10],  //Sorceror
			[10, 0, 0, 0, 0, 0, 0, 0,10, 0],  //Ranger
			[ 0, 0, 0,10, 0 ,0, 0, 0, 0,10] ];//Bard

//set up array of GI's by profession
//		 	 CO DE DI LO IN ST RE CH WI AU
var ProfessionGI =  [	[30,25,20,10,10,30,30,15,15,15],  //Warrior
			[20,30,20,20,20,20,30,20,10,10],  //Rogue
			[10,25,25,30,30,10,20,10,10,30],  //Wizard
			[20,10,30,25,20,20,15,15,30,15],  //Cleric
			[30,10,30,25,25,10,15,15,30,10],  //Empath
			[15,25,25,15,25,10,15,10,30,30],  //Sorceror
			[25,15,15,25,10,25,25,20,25,15],  //Ranger
			[25,15,15,10,15,25,25,30,15,25] ];//Bard

//set up array of GI's by race
//		 CO DE DI LO IN ST RE CH WI AU
var RaceGI =  [	[ 2, 0, 0, 0, 2, 2, 0, 0, 0, 0],  //Human
		[ 5,-2, 0, 0, 0, 5,-2, 0, 0, 0],  //Giant
		[ 0, 2,-2, 0, 0, 2, 2, 2, 0, 0],  //Half-Elf
		[-2, 5,-5, 0, 0,-5, 5, 5, 0, 3],  //Sylvan
		[-2, 5, 0, 0,-2, 0, 5, 0, 0, 0],  //Dark Elf
		[-5, 5,-5, 5, 2, 0, 2,-5, 2, 5],  //Elf
		[ 5,-5, 5, 0, 0, 5,-5,-2, 3, 0],  //Dwarf
		[ 5, 5,-5,-2, 0,-5, 5, 3, 0, 0] ];//Halfling


//Inputs the form and calculates the optimal placement
function Calculate(form)
{
  //initialize starting value
  Stat=new Array(70,70,100,20,20,90,90,20,20,90);
  MinimumPTP=parseInt(form.PTPMinimum.value);
  if (isNaN(MinimumPTP))
    MinimumPTP=0;
  MinimumMTP=parseInt(form.MTPMinimum.value);
  if (isNaN(MinimumMTP))
    MinimumMTP=0;
  Race=form.race.selectedIndex;
  Profession=form.profession.selectedIndex;
  for (var i=0;i<10;i++)
    GI[i]=ProfessionGI[Profession][i] + RaceGI[Race][i];
  
  for (var i=0;i<10;i++)
  {
    MinimumStats[i]=parseInt(form.MinimumStat[i].value);
    if (isNaN(MinimumStats[i]))
      MinimumStats[i]=0;     
  }
  PhysicalWeight=parseFloat(form.PhysicalWeight.value);
  if (isNaN(PhysicalWeight) || PhysicalWeight<0)
    PhysicalWeight=1.0;
  MentalWeight=parseFloat(form.MentalWeight.value);
  if (isNaN(MentalWeight) || MentalWeight<0)
    MentalWeight=1.0;
    
  InitializeStatArray(form);	//set AllStats to initial values
  Iterations=form.Iterations.value;	//get iterations from form
  MaxLevel=form.MaxLevel.value;		//get level to optimize for
  
  SetUpStatArray();		//set up big array of AllStats

  for (var i=0;i<10;i++)
    BestStats[i]=Stat[i];	//save first stat set as best
  
  CalculateTotalTP();		//get total points this set yields

  BestModifiedTotal=TotalPTP*PhysicalWeight + TotalMTP*MentalWeight;
  BestRawPTP=RawPTP;
  BestRawMTP=RawMTP;
  BestRawTP=RawTP;
  BestPTP=TotalPTP;
  BestMTP=TotalMTP;
  BestTP=TotalTP;

//main loop
  for (var i=0;i<Iterations;i++)  //test this many stat sets
  {
     Rand1=Math.floor(random()*10);  //AllStats to add & subtract
     do
     {
       Rand2=Math.floor(random()*10);
     }while (Rand1==Rand2);		//don't +/- same stat
     Rand3=Math.floor(random()*20)+1;	//amount to add & subtract
     Stat[Rand1]+=Rand3;
     Stat[Rand2]-=Rand3;
     if (IsWithinGameParameters()>0 || IsAboveUserMinimums()>0)
     {					//if illegal, swap back
       Stat[Rand1]-=Rand3;
       Stat[Rand2]+=Rand3;
     }
     else			//else test this stat set
     {
       CalculateTotalTP();	//test this set of AllStats

       ModifiedTotal=TotalPTP*PhysicalWeight + TotalMTP*MentalWeight;

       if (ModifiedTotal>BestModifiedTotal || (ModifiedTotal==BestModifiedTotal & RawTP>BestRawTP))
       { 			//if this set is better...
         BestRawPTP=RawPTP;
         BestRawMTP=RawMTP;
         BestRawTP=RawTP;
         BestPTP=TotalPTP;
         BestMTP=TotalMTP;
         BestTP=TotalTP;
         BestStats[Rand1]=Stat[Rand1];	//update better AllStats
         BestStats[Rand2]=Stat[Rand2];
         BestModifiedTotal=ModifiedTotal;
       } //end stuff to do if stat set is better
       else
       {			//if stat set isn't better, swap back
         Stat[Rand1]=BestStats[Rand1];
         Stat[Rand2]=BestStats[Rand2];
       } //end else

     } //end else portion
  } //end main loop

  Display(form);

return;
}

//sets AllStats to middle range so program can hop from there
function SetStatsToMiddle()
{
  var Pool=0;
  for (var i=0;i<10;i++)
  {
    while (IsWithinGameParameters()==0 && IsAboveUserMinimums()==0)
    {
      Stat[i]--;
      Pool++;
    }
    Stat[i]++;
    Pool--;
  }
  while (Pool>0)
  {
    Stat[MinimumStat()]++;
    Pool--;
  }
return;
}

//returns the index number of the minimum stat
function MinimumStat()
{
  var LowestIndex=0;
  var Lowest=100;
  for (var i=0;i<10;i++)
    if (Stat[i]<Lowest)
    {
      Lowest=Stat[i];
      LowestIndex=i;
    }
   return LowestIndex;
}

//Calculates PTP, MTP, and TotalTP over career 
function CalculateTotalTP()
{
  dicaur=Stat[2]+Stat[9];
  RawPTP=dicaur + Stat[0] + Stat[1] + Stat[5] + Stat[6];
  RawMTP=dicaur + Stat[3] + Stat[4] + Stat[7] + Stat[8];
  TotalPTP=Math.floor(RawPTP/10);
  TotalMTP=Math.floor(RawMTP/10);

  //main inner loop
  for (var i=1;i<=MaxLevel;i++)	//figure TP's to level 160
  {
    dicaur=AllStats[2][Stat[2]][i]+AllStats[9][Stat[9]][i];
rawphy=dicaur+AllStats[0][Stat[0]][i]+AllStats[1][Stat[1]][i]+AllStats[5][Stat[5]][i]+AllStats[6][Stat[6]][i];
rawmen=dicaur+AllStats[3][Stat[3]][i]+AllStats[4][Stat[4]][i]+AllStats[7][Stat[7]][i]+AllStats[8][Stat[8]][i];
    TotalPTP+=Math.floor(rawphy/10);
    TotalMTP+=Math.floor(rawmen/10);
    RawPTP+=rawphy;
    RawMTP+=rawmen;
  } //end for

  TotalTP=TotalPTP+TotalMTP;
  RawTP=RawPTP+RawMTP;
return;
}

//sets AllStats at initial value which meets user requirements
function InitializeStatArray(form)
{
  SetStatsToMinimum();	//sets AllStats to minimum requirements
			//on form, prime AllStats to 60, 20 all others
  MaxOutStats();	//Sets AllStats to 640, max PTP
  MaxAUandDI();		//max out AU and DI
  MaxRestOfStats();	//max physical AllStats, then mental
  CheckMTPMinimum();	//check if MTP minimum is possible
  SetStatsToMiddle();	//set AllStats to middle range
  return;
}

//checks legality, sets AllStats to 640, maxes out PTP
function MaxOutStats()
{
  if (IsWithinGameParameters()==1)
    NotPossible2("Minimum starting stats are not within game parameters");
  if (TotalStats()>640)
    NotPossible2("Stat set totals "+TotalStats()+"\nMaximum allowed is 640");
  return;
}

//max out physical AllStats, then mental
function MaxRestOfStats()
{
  for (var i=0;i<4;i++)
  {
    Stat[Physical[i]]=100;	//max out physical AllStats
    while (TotalStats()>640 || IsWithinGameParameters()==1)
      Stat[Physical[i]]--;
  }
  for (var i=0;i<4;i++)
  {
    Stat[Mental[i]]=100;	//max out mental AllStats
    while (TotalStats()>640 || IsWithinGameParameters()==1)
      Stat[Mental[i]]--;
  }
return;
}

//adds to DI then AU until they're either maxed or total is 640 or
//stat set is illegal
function MaxAUandDI()
{
  Stat[2]=100;		//set DI maximum possible
  while (TotalStats()>640 || IsWithinGameParameters()==1)
    Stat[2]--;
  Stat[9]=100;		//set AU to maximum possible
  while (TotalStats()>640 || IsWithinGameParameters()==1)
    Stat[9]--;
return;
}

//sets AllStats to the minimum AllStats on form, 60 for prime, 20 all others
function SetStatsToMinimum()
{
  for (var i=0;i<10;i++)
  {
    Stat[i]=MinimumStats[i];
    if (Stat[i]<20)
      Stat[i]=20;
    if (Stat[i]<60 & ProfessionMod[Profession][i]==10)
      Stat[i]=60;
    if (Stat[i]>100)
      NotPossible2("Stats cannot be greater than 100");
  }
  return;
}

//Check if MTP minimum is possible; adjust AllStats to meet it
function CheckMTPMinimum(form)
{
  if (CalculatePTP()<MinimumPTP)	//PTP already maxed; if below
    NotPossible2("Physical training point minimum not attainable.");
					//minimum, exit program
  if (CalculateMTP()>=MinimumMTP)	//if MTP are above minimum
	return;				//criteria are met; return
  for (var i=0;i<4;i++)		//go through each mental, add as much
    for (var j=0;j<4;j++)	//as possible from each physical
    {  if (MinimumStats[Physical[j]]<20)       //set minimum physical
         MinimumStats[Physical[j]]=20;	       //stats to 20 if not yet set
       Stat[Mental[i]] += Stat[Physical[j]] - MinimumStats[Physical[j]];
       Stat[Physical[j]]=MinimumStats[Physical[j]];
       while (IsWithinGameParameters()==1 || IsAboveUserMinimums()==1)
       {
	 Stat[Mental[i]]--;
         Stat[Physical[j]]++;
       } //end while
    } //end nested for loop
  if (IsAboveUserMinimums()>0)		//if MTP still not met
	NotPossible2("Mental training point minimum not attainable."); 
return;
} //end CheckMTPMinimum function

//Do current AllStats meet user criteria for starting AllStats?
//Return 0=yes, 1=no
function IsAboveUserMinimums()
{
  if (CalculateMTP() < MinimumMTP)	//if MTP's are low
    return 2;				//return code for MTP being low
  if (CalculatePTP() < MinimumPTP)	//if PTP's are low
    return 1;				//return false
  for (var i=0;i<10;i++)		//if any stat is low
    if (Stat[i]<MinimumStats[i])
      return 1;				//return false
  return 0;				//return true
}

//Do current AllStats meet game parameters for starting AllStats?
//Return 0=yes, 1=no
function IsWithinGameParameters()
{
  var AllStats_over_70=0;
  var AllStats_over_90=0;
  for (var i=0;i<10;i++)
  {				//if either prime stat is below 60
    if (Stat[i]<60 & ProfessionMod[Profession][i]==10)
    return 1;			//return false
				
    if (Stat[i]>(70+ProfessionMod[Profession][i]))
    {  
      AllStats_over_70++;		//count AllStats over 70 and 90
      if (Stat[i]>(90+ProfessionMod[Profession][i]))
        AllStats_over_90++;
    } //end if
				//check each stat for <20 or >100
    if (Stat[i]>100 || Stat[i]<20)
      return 1;			//return false
  } //end for loop
				//if more than 4 over 70, or 1 over 90
  if (AllStats_over_70>4 || AllStats_over_90>1)
    return 1;			//return false
  return 0;			//return true
} //end IsWithinGameParameters function

//Displays all info below the calculate button
function Display(form)
{
  for (var i=0;i<10;i++)
    form.CurrentStat[i].value=Stat[i];
  form.TotalPTP.value=BestPTP;
  form.TotalMTP.value=BestMTP;
  form.TotalTP.value=BestTP;
  form.StartingPTP.value=CalculatePTP();
  form.StartingMTP.value=CalculateMTP();    
  return;
}

//inputs stat array, returns PTP's
function CalculatePTP()
{
  return Math.floor((Stat[0] + Stat[1] + Stat[2] + Stat[5] + Stat[6] + Stat[9]) /10);
}

//returns total of current AllStats
function TotalStats()
{
  var total=0;
  for (var i=0;i<10;i++)
    total=total+Stat[i];
  return total;
}

//inputs stat array, returns MTP's
function CalculateMTP()
{
  return Math.floor((Stat[2] + Stat[3] + Stat[4] + Stat[7] + Stat[8] + Stat[9]) /10);
}

//displays not possible prompt and exits program
function NotPossible()
{
  window.alert(x);
  window.abort();  //syntax error, but it stops the program as intended
}

//displays not possible prompt and exits program
function NotPossible2(x)
{
  window.alert(x);
  window.abort();  //syntax error, but it stops the program as intended
}

//resets all text fields
function Clean(form)
{
  for (var i=0;i<form.length;i++)
    if (form.elements[i].type=="text")	//clear only text fields
      form.elements[i].value="";
    form.elements[14].value=160;
    form.elements[17].value=1000;
    form.elements[18].value=1.0;
    form.elements[19].value=1.0;
  return;
}

//returns a random number between 0 and 1
function random() { 
  random.seed = (random.seed*random.a + random.c) % random.m; 
  return random.seed / random.m; 
}
random.m=714025; random.a=4096; random.c=150889; 
random.seed = (new Date()).getTime()%random.m;

//sets up array of all AllStats at each level for every possibility
function SetUpStatArray()
{
  var stat;
  for (var i=0;i<10;i++)	//create 3-dimensional array
  {				//to hold each stat, at each level,  
     AllStats[i]=new Array(101);	//for each starting value
     for (var j=20;j<101;j++)
     {
       stat=j;
       AllStats[i][j]=new Array(161);
       for (var k=1;k<=MaxLevel;k++)
       {
         if (stat<GI[i] || (stat<100 & k % Math.floor(stat/GI[i])==0))
           stat++;				//stat grows
         AllStats[i][j][k]=stat;    
       } //end k loop
     } //end j loop
  } //end i loop
return;
}