
//  ==========================================
//  ====  random number generator         ====
//  ====                                  ====

function random()  // thanks to JS page 468
  {
    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;
//random.seed = 64589347;
theDate=new Date();
random.seed = theDate.getTime()%random.m;


//  ==========================================
//  ====  utility functions               ====
//  ====                                  ====

var newline = "\n";  // may need to be set for different platforms?

function standardDeviation(n,sum,ss)
  {
    ss2 = (ss - (sum*sum/n));
    if ( n <= 1 ) {
      if ( ss2 <= 0.0001 ) return 0;
      else return 99999;
    }
    variance = ss2/(n-1);
    return Math.sqrt(variance);
  }

function digits(x,n)
  {
    base = 1;
    if ( n == 1 )  base = 10;
    else if ( n ==2 )  base = 100;
    else if ( n ==3 )  base = 1000;
    else if ( n ==4 )  base = 10000;
    else if ( n ==5 )  base = 100000;
    else if ( n ==6 )  base = 1000000;
    if ( base == 1 )
        return x;
    else
        return Math.round(x*base)/base;
  }

function scoreStr(h,t)
  {
      var hs = rightAlign(h,5);
      var ts = rightAlign(t,5);
      return hs + " " + ts;
  }

function rightAlign(s,wid)
  {
    var i;
    var str=""+s;
    var spaces = "";
    for ( i=str.length; i<wid; i++ ) {
        spaces = spaces + " ";
    }
    return spaces+str;
  }

//  ====  end utility  =======================
//  ==========================================



//  ==========================================
//  ====  manage coin probability matrix  ====
//  ====                                  ====
//  ====  coinProb  ==========================


var coinProb = new Object();
coinProb.probHead = 0.8
coinProb.corr = 0
coinProb.eCorr = 0
coinProb.probHH = 0.8
coinProb.probHT = 0.2
coinProb.probTH = 0.8
coinProb.probTT = 0.2
coinProb.memory = false
coinProb.calcMatrix = coinProbCalcMatrix;
coinProb.calcCorr = coinProbCalcCorr;
coinProb.getProb = coinProbGetProb;
coinProb.getCorr = coinProbGetCorr;
coinProb.doCalcMatrix = coinProbDoCalcMatrix;
coinProb.doSetMatrix = coinProbDoSetMatrix;
coinProb.resetForm = coinProbResetForm;
coinProb.calc = coinProbCalc;
coinProb.toss = coinProbToss;

function coinProbCalcMatrix( p, corr )
  {
    var a = (1-p) * corr + 1.0*p;   // 1.0 to force p to be a number!
    var c = 1 - a
    var b = p * c / (1-p)
    var d = 1 - b
    coinProb.probHH = a
    coinProb.probTH = b
    coinProb.probHT = c
    coinProb.probTT = d
    coinProb.memory = true
  }

function coinProbCalcCorr( p, corr )
  {
    eCorr = corr
    pCorr1 = -p/(1-p)
    pCorr2 =  -(1-p)/p
    if ( eCorr < pCorr1 ) eCorr = pCorr1;
    if ( eCorr < pCorr2 ) eCorr = pCorr2;
    return eCorr
  }

function coinProbGetProb()
  {
    coinProb.probHead = document.sform.probHead.value;
    return coinProb.probHead;
  }

function coinProbGetCorr()
  {
    coinProb.corr = document.sform.corr.value;
    return coinProb.corr;
  }

function coinProbDoCalcMatrix()
  {
    var prob = coinProb.getProb();
    var corr = coinProb.getCorr();
    coinProb.eCorr = coinProb.calcCorr(prob,corr);
    coinProb.calcMatrix ( prob, coinProb.eCorr );
  }

function coinProbDoSetMatrix()
{
    if ( coinProb.eCorr != coinProb.corr ) {
      document.sform.eCorr.value = eCorr;
    } else {
      document.sform.eCorr.value = "";
    }
    document.sform.probHH.value = coinProb.probHH;
    document.sform.probTH.value = coinProb.probTH;
    document.sform.probHT.value = coinProb.probHT;
    document.sform.probTT.value = coinProb.probTT;
}

function coinProbResetForm()
  {
    coinProb.doCalcMatrix();
    coinProb.doSetMatrix();
  }

function coinProbCalc()
  {
    coinProb.doCalcMatrix();
    coinProb.doSetMatrix();
  }

function coinProbToss(last)
  {
    var ph = coinProb.probHead;
    if ( coinProb.memory ) {
        if ( last == "head" )  ph = coinProb.probHH;
        else if ( last == "tail" )  ph = coinProb.probTH;
    }
    if ( random() < ph )
        return "head";
    else
        return "tail";
  }

//  ====  end coinProb  ======================
//  ==========================================



//  ==========================================
//  ====  manage coin  icons etc.         ====
//  ====                                  ====


function icons(which)
  {
    return icons[which].src;
  }

function makeURL(which,decor)  // which = head, tail or empty
{
    if ( which == "H"  || which == "head" ) {
        return makeURL.base + "coin" + decor + "head.gif";
    }
    else if ( which == "T"  || which == "tail" ) {
        return makeURL.base + "coin" + decor + "tail.gif";
    }
    else {
        return makeURL.base + "coin" + decor + "empty.gif";
    }
}
makeURL.base="icons/"

function loadIcons()
  {
    icons.head=new Image();
    icons.head.src = makeURL("head","");
    icons.tail=new Image();
    icons.tail.src = makeURL("tail","");
    icons.empty=new Image();
    icons.empty.src = makeURL("empty","");
    icons.headRed=new Image();
    icons.headRed.src = makeURL("head","red");
    icons.tailRed=new Image();
    icons.tailRed.src = makeURL("tail","red");
    icons.redTri=new Image();
    icons.redTri.src = makeURL.base + "right-red-tri.gif";
    icons.whiteTri=new Image();
    icons.whiteTri.src = makeURL.base + "right-white-tri.gif";
  }


//  ====  end icons  =========================
//  ==========================================



//  ==========================================
//  ====  manage coin tossing             ====
//  ====                                  ====



var coinToss = new Object;
coinToss.val="head";
coinToss.set=setCoinToss;
coinToss.change=coinTossChange;
coinToss.doToss=coinTossDoToss;
coinToss.doTossed=coinTossDoTossed;

function setCoinToss(which)
  {
    coinToss.val = which;
    document.sform.toss.src=icons[which].src;
  }

function coinTossChange()
  {
    var newCoinToss = change[coinToss.val].flip;
    coinToss.set(newCoinToss);
  }

function coinTossDoToss(count)
  {
    coinToss.change();
    if ( count > 0 ) {
        count -= 1;
        doit = "coinToss.doToss(" + count + ")";
        window.setTimeout(doit,10);
        active = 1;
    }
    else {
        active = 0;
        coinToss.doTossed();
    }
  }

function coinTossDoTossed()
  {
     //  do nothing
     //  set before calling coinToss.doToss
  }


//  ====  end coin tossing ===================
//  ==========================================



//  ==========================================
//  ====  what heads change to etc.       ====
//  ====                                  ====
//  ====  change                          ====




// function change()
//  {
//    var newCoinToss = change[coinToss.val].flip;
//    coinToss.set(newCoinToss);
//  }

var change= new Object;

change.head = new Object;
change.H = change.head
change.empty = change.head
change.tail = new Object;
change.T = change.tail
change.head.flip="tail"
change.tail.flip="head"
change.head.lng="head"
change.tail.lng="tail"
change.head.shrt="H"
change.tail.shrt="T"


//  ====  end change       ===================
//  ==========================================





//  ==========================================
//  ====  game scoring and params         ====
//  ====                                  ====


var game = new Object;
game.winScore=10;
game.race=true;
game.totalCt=0;
game.headCt=0;
game.tailCt=0;
game.over=false;
game.lastCoin="";
game.clear = clearGameScores;
game.addToss = addTossGameScores;
function clearGameScores()
  {
    game.headCt=0;
    game.tailCt=0;
    game.totalCt=0;
    game.over=false;
    game.lastCoin="";
  }
function addTossGameScores(coin)
  {
    var count = ++game[coin+"Ct"];
    ++game.totalCt;
    game.lastCoin = coin;
    if ( game.race && count >= game.winScore )
            game.over=true;
    if ( !game.race && game.totalCt >= game.winScore )
            game.over=true;
    return count;
  }

//  ====  end of game scoring             ====
//  ==========================================



//  ==========================================
//  ====  running total                   ====
//  ====                                  ====
//  ====  tally                           ====


var tally = new Object();
tally.count=0;
tally.headCt=0;
tally.tailCt=0;
tally.headSq=0;
tally.tailSq=0;

function doTally()
  {
    tally.count++;
    tally.headCt += game.headCt;
    tally.tailCt += game.tailCt;
    tally.headSq += game.headCt * game.headCt;
    tally.tailSq += game.tailCt * game.tailCt;
  }

function clearTally()
  {
    tally.count=0;
    tally.headCt = 0;
    tally.tailCt = 0;
    tally.headSq = 0;
    tally.tailSq = 0;
  }


//  ====  end of running total            ====
//  ==========================================





//  ==========================================
//  ====  interaction control             ====
//  ====                                  ====

var nosRows=5;
var nosTosses=10;

function doSetCoinRows()
  {
    nosRows   = document.sform.nosRows.value;
    nosTosses = document.sform.nosTosses.value;
  }

function doScoreBoard()
{
    var score = scoreStr(game.headCt,game.tailCt);
    document.sform.scoreboard.value = document.sform.scoreboard.value + newline + score;
    score = scoreStr(tally.headCt,tally.tailCt);
    document.sform.tally.value = score;
    document.sform.count.value = rightAlign(tally.count,5);
    headMean = digits(tally.headCt/tally.count,1)
    tailMean = digits(tally.tailCt/tally.count,1)
    scoreMean = scoreStr(headMean,tailMean);
    document.sform.mean.value = scoreMean ;
    headSD = standardDeviation(tally.count,tally.headCt,tally.headSq);
    headSD = digits(headSD,1)
    tailSD = standardDeviation(tally.count,tally.tailCt,tally.tailSq);
    tailSD = digits(tailSD ,1)
    scoreSD = scoreStr(headSD,tailSD);
    document.sform.sd.value = scoreSD;
}

function tossNow()
  {
//    if ( game.over ) return;
    game.clear();
    rowCt = 0;
    tossingFinished = false;
    game.winScore = nosTosses;
    coinToss.doTossed = tossFinished;
    coinToss.doToss(5);
  }

var rowCt;
var tossingFinished = false;

function tossFinished()
  {
    var next = coinProb.toss(game.lastCoin);
    game.lastCoin = next;
    coinToss.set(next);
    document.sform.results.value
        = document.sform.results.value + change[coinToss.val].shrt;
    game.addToss(coinToss.val);
    if ( game.over ) endOfRow();
    if ( rowCt < nosRows ) coinToss.doToss(5);
  }

function endOfRow()
  {
    doTally();
    doScoreBoard();
    rowCt++;
    doNewRace();
  }



var resetMsg;


function initMessages()
  {
    resetMsg    = "this will reset all your running scores"
                  + newline +
                  "do you really want to do it?";
  }

function doNewRace()
  {
    game.clear();
    resultsAreaGameEnd();
  }

function resultsAreaGameEnd()
  {
    document.sform.allResults .value
            =  document.sform.allResults.value
             + newline
             + document.sform.results.value
    document.sform.results.value="";
  }

function resetGame()
  {
    if ( ! confirm(resetMsg) ) return;
    doNewRace();
    clearTally();
    clearFields();
  }

function clearFields()
  {
    document.sform.results.value="";
    document.sform.allResults.value="";
    document.sform.scoreboard.value="";
    document.sform.tally.value="";
    document.sform.count.value="";
    document.sform.mean.value="";
    document.sform.sd.value="";
  }

function init()
  {
    game.race=false;
    loadIcons();
    initMessages();
    clearFields();
    coinProb.resetForm();
    doSetCoinRows();
  }

//  ====  end of interaction control      ====
//  ==========================================


