
// ******** Arimaa class ********
function Arimaa(){

// ********* private stuff, unless declared at the end as public :-) *********

var version = 'v0.7.24a';
var prop = {};
var vars = {};
var glob = {};
var logvar = '';

log('version '+version);

function window(){
  var url;

  setProp();

  url = '';
  if (document.getElementById('arimaa_script') != null){
    url = document.getElementById('arimaa_script').src;
  }
  url = url.replace(/[^\/]*$/, '');
  prop.bu = url;
  prop.Image.base = prop.bu + prop.Image.base;

//  var d = wget('arimaa.div');
  var d = getWindowHtml();

  document.write(d);

}

function window2(){
  var url;

  setProp();

  url = '';
  if (document.getElementById('arimaa_script') != null){
    url = document.getElementById('arimaa_script').src;
  }
  url = url.replace(/[^\/]*$/, '');
  prop.bu = url;
  prop.Image.base = prop.bu + prop.Image.base;

//  var d = wget('arimaa.div');
  var d = getWindowHtml();

  d = d.replace(/arimaa.init\(\)/, '//'); // comment out the call to arimaa.init()

//  document.write(d);

  document.getElementById('arimaa_win').innerHTML = d;

//  setTimeout('arimaa.init()', 2000);

}

function getWindowHtml(){
  var s = '\
\
<div>\
 <!-- soundManager appends "hidden" Flash to the first DIV on the page. -->\
</div>\
<script type="text/javascript" src="'+prop.bu+'sm2/sm2.js"></script>\
<script type="text/javascript">\
function sm2setup(){ \
  soundManager.debugMode = false; \
  soundManager.url = "'+prop.bu+'sm2/swf/"; \
  soundManager.onload = arimaa.initSound; \
  soundManager.play2 = arimaa.playSound; \
  soundManager.defaultOptions.autoLoad = true; \
  setTimeout(\'arimaa.init()\', 300); \
} \
"the following line tries to detect the IE browser"; \
if (\'\v\'==\'v\'){ \
  setTimeout(\'sm2setup()\', 500); \
} \
else{ \
  sm2setup(); \
} \
</script>\
\
<table id="mainTab" border=0 height=450 align=center bgcolor="'+arimaa.prop.bgcolor+'">\
  <tr>\
    <td valign=middle align=center >\
<span id=buttonGroupNet style="visibility:visible">\
<input id=\'sendBtn\' type=button value="Send" title="Send move" style="padding:0" onclick="arimaa.sendMove()">\
<br>\
<span id="connected"></span>\
<br>\
<input type=button id="btnT" value="T" title="Request takeback" style="padding:0">\
<input type=button id="btnA" value="A" title="Adjourn game" style="padding:0">\
<br>\
<input type=button id="btnR" value="R" title="Resign" style="padding:0" onclick="arimaa.sendResign()">\
<input type=button id="btnD" value="D" title="Request draw" style="padding:0">\
<br>\
<br>\
</span>\
<span id="buttonGroupSelfPlay" style="visibility:visible;">\
<input type=button value="U&lt;" title="Undo step" style="padding:0" onclick="arimaa.eventUndoStep()">\
<input type=button value="R&gt;" title="Redo step" style="padding:0" onclick="arimaa.eventRedoStep()">\
<br><nobr>\
<input type=button value="U&lt;&lt;" title="Undo move" style="padding:0" onclick="arimaa.eventUndoMove()">\
<input type=button value="R&gt;&gt;" title="Redo move" style="padding:0" onclick="arimaa.eventRedoMove()">\
</nobr><br>\
<input type=button value="Pass" id="btnPass" title="Pass steps" style="padding:0" onclick="arimaa.eventPassSteps()">\
<br><br>\
</span>\
<br><br>\
<nobr>\
<input id=buttonV type=button value="Vn" onclick="javascript:arimaa.changeView()" title="Change view" style="padding:0">\
<input id=buttonS type=button value="S1" onclick="javascript:arimaa.changeShowSpeed()" title="Change speed" style="padding:0">\
<br>\
<input id=buttonM type=button value="M-" onclick="javascript:arimaa.changeMute()" title="Sound on/off" style="padding:0">\
<br>\
<input id=buttonE type=button value="E-" onclick="javascript:arimaa.changeExpert()" title="Expert mode on/off; allows playing both sides to plan ahead" style="padding:0">\
</nobr>\
<br><br>\
    </td>\
\
    <td valign=top align=left >\
<table border=0>\
  <tr>\
    <td align=center colspan=3>\
<nobr>\
<font size=2>\
<span id=gameTitle style="background-color:#eeeeff">Game Title</span>\
&nbsp;\
<span id=ratedGame title="Rated game">R</span>\
&nbsp;\
<a onclick="open(\'/arimaa/gameroom/timecontrol.cgi?tc=\'+vars.timecontrol,\'\',\'\')"><span id=timeControl title="Time Control; click for details">R 1/1/100/5/0</span></a>\
&nbsp;\
<span id=gameTime title="Game Time" style="color:yellow;background-color:black">0s</span>\
</font>\
</nobr>\
    </td>\
  </tr>\
  <tr>\
    <td id=topPlayerInfo align=center colspan=3>\
<span id=topPlayerInfo2>\
<nobr>\
<font size=2>\
<span id=topName>Opponent (1764)</span>\
&nbsp;\
<span id=topWL></span>\
&nbsp;\
<span id=topSteps title="Steps available" style="background-color:transparent">4</span>\
&nbsp;\
<a onclick="open(\'/arimaa/gameroom/timecontrol.cgi?tc=\'+vars.timecontrol,\'\',\'\')" title="Move time; click for details"><span id=topMove style="color:white;background-color:black" >0s</span></a>\
&nbsp;\
<a id=topResv2 onclick="open(\'/arimaa/gameroom/timecontrol.cgi?tc=\'+vars.timecontrol,\'\',\'\')" title="Reserve time; click for details"><span id=topResv style="background-color:black;color:#99ff99">0s</span></a>\
&nbsp;\
</font>\
</nobr>\
</span>\
    </td>\
  </tr>\
  <tr>\
    <td height=100%>\
      <table height=100% border=0>\
<!-- we can use this table to hold pieces in compose mode -->\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow1 height=15 width=15 src="'+prop.bu+'images/sp.gif"></a></td></tr>\
<!--  if you uncomment this besure to add prop.bu to scr=\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow1 height=15 width=15 src="images/we.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow2 height=15 width=15 src="images/we.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow3 height=15 width=15 src="images/we.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow4 height=15 width=15 src="images/we.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow5 height=15 width=15 src="images/we.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow6 height=15 width=15 src="images/we.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow7 height=15 width=15 src="images/we.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow8 height=15 width=15 src="images/we.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow9 height=15 width=15 src="images/we.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow10 height=15 width=15 src="images/we.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow11 height=15 width=15 src="images/we.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow12 height=15 width=15 src="images/we.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow13 height=15 width=15 src="images/we.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow14 height=15 width=15 src="images/we.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow15 height=15 width=15 src="images/we.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ow16 height=15 width=15 src="images/we.gif"></a></td></tr>\
-->\
      </table>\
    </td>\
    <td align=center valign=top>\
<span style="-webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -o-user-select: none; user-select: none;" id=boardspan>\
'+makeBoard()+'\
</span>\
    </td>\
    <td height=100%>\
      <table height=100% border=0>\
<!-- we can use this table to hold pieces in compose mode -->\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob1 height=15 width=15 src="'+prop.bu+'images/sp.gif"></a></td></tr>\
<!--  if you uncomment this besure to add prop.bu to scr=\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob1 height=15 width=15 src="images/be.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob2 height=15 width=15 src="images/be.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob3 height=15 width=15 src="images/be.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob4 height=15 width=15 src="images/be.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob5 height=15 width=15 src="images/be.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob6 height=15 width=15 src="images/be.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob7 height=15 width=15 src="images/be.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob8 height=15 width=15 src="images/be.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob9 height=15 width=15 src="images/be.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob10 height=15 width=15 src="images/be.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob11 height=15 width=15 src="images/be.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob12 height=15 width=15 src="images/be.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob13 height=15 width=15 src="images/be.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob14 height=15 width=15 src="images/be.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob15 height=15 width=15 src="images/be.gif"></a></td></tr>\
        <tr><td valign=top><a onclick="changeViewAuto(\'top\',\'left\')" title="Change view"><img id=ob16 height=15 width=15 src="images/be.gif"></a></td></tr>\
-->\
      </table>\
    </td>\
  </tr>\
  <tr>\
    <td id=botPlayerInfo align=center colspan=3>\
<span id=botPlayerInfo2>\
<nobr>\
<font size=2>\
<span id=botName>Myself (1831)</span>\
&nbsp;\
<span id=botWL></span>\
&nbsp;\
<span id=botSteps title="Steps available" style="background-color:transparent">4</span>\
&nbsp;\
<a onclick="open(\'/arimaa/gameroom/timecontrol.cgi?tc=\'+vars.timecontrol,\'\',\'\')" title="Move time; click for details"><span id=botMove style="color:white;background-color:black" >0s</span></a>\
&nbsp;\
<a id=botResv2 onclick="open(\'/arimaa/gameroom/timecontrol.cgi?tc=\'+vars.timecontrol,\'\',\'\')" title="Reserve time; click for details"><span id=botResv style="background-color:black;color:#99ff99">0s</span></a>\
&nbsp;\
</font>\
</nobr>\
</span>\
    </td>\
\
  </tr>\
</table>\
\
    </td>\
\
    <td valign=top align=center>\
<select id=movelist multiple size=15 style="width:200px" onChange="arimaa.goPosList(this.selectedIndex)">\
</select>\
<br>\
<input type=button id=showBtn value="Show" onclick="javascript:arimaa.showMoveSlow()" title="Show move" style="padding:0">\
<span id=autoShowGroup style="display:none">\
<input type=checkbox id=autoShowBtn value=1 onChange="arimaa.autoShowBtnChange()">auto\
<input type=checkbox id=realShowBtn value=1 onChange="arimaa.autoShowBtnChange()">real-time\
</span>\
<br>\
<nobr>\
<input type=button value="&lt;&lt;" onclick="javascript:arimaa.showPrevMove()" title="Previous move" style="padding:0">\
<input type=button value="&gt;&gt;" onclick="javascript:arimaa.showNextMove()" title="Next move" style="padding:0">\
</nobr>\
<br>\
<nobr>\
<input type=button value="|&lt;" onclick="javascript:arimaa.showFirstMove()" title="First move" style="padding:0">\
<input type=button value="&gt;|" onclick="javascript:arimaa.showLastMove()" title="Last move" style="padding:0">\
</nobr>\
<br>\
<input type=button value="H" title="Help" onclick="javascript:open(\'/arimaa/gameroom/jscHelp.html\',\'\',\'\')" style="padding:0">\
<input type=button value="P" title="Plan" style="padding:0" onclick="arimaa.openPlanWindow()">\
<input id=btnG type=button value="G" title="Comments" style="padding:0" onclick="javascript:open(arimaa.vars.commentsPage,\'\',\'\')" >\
<input type=button value="M" onclick="javascript:arimaa.openMoveListWindow()" title="Move list" style="padding:0">\
<font size=1><br><br></font>\
<img id=cap_w1 height=10 width=10 src="'+prop.bu+'images/we.gif"><img id=cap_w2 height=10 width=10 src="'+prop.bu+'images/wm.gif"><img id=cap_w3 height=10 width=10 src="'+prop.bu+'images/wh.gif"><img id=cap_w4 height=10 width=10 src="'+prop.bu+'images/wh.gif"><img id=cap_w5 height=10 width=10 src="'+prop.bu+'images/wd.gif"><img id=cap_w6 height=10 width=10 src="'+prop.bu+'images/wd.gif"><img id=cap_w7 height=10 width=10 src="'+prop.bu+'images/wc.gif"><img id=cap_w8 height=10 width=10 src="'+prop.bu+'images/wc.gif"><img id=cap_w9 height=10 width=10 src="'+prop.bu+'images/wr.gif"><img id=cap_w10 height=10 width=10 src="'+prop.bu+'images/wr.gif"><img id=cap_w11 height=10 width=10 src="'+prop.bu+'images/wr.gif"><img id=cap_w12 height=10 width=10 src="'+prop.bu+'images/wr.gif"><img id=cap_w13 height=10 width=10 src="'+prop.bu+'images/wr.gif"><img id=cap_w14 height=10 width=10 src="'+prop.bu+'images/wr.gif"><img id=cap_w15 height=10 width=10 src="'+prop.bu+'images/wr.gif"><img id=cap_w16 height=10 width=10 src="'+prop.bu+'images/wr.gif">\
<font size=1><br><br></font>\
<img id=cap_b1 height=10 width=10 scr="'+prop.bu+'images/be.gif"><img id=cap_b2 height=10 width=10 scr="'+prop.bu+'images/bm.gif"><img id=cap_b3 height=10 width=10 scr="'+prop.bu+'images/bh.gif"><img id=cap_b4 height=10 width=10 scr="'+prop.bu+'images/bh.gif"><img id=cap_b5 height=10 width=10 scr="'+prop.bu+'images/bd.gif"><img id=cap_b6 height=10 width=10 scr="'+prop.bu+'images/bd.gif"><img id=cap_b7 height=10 width=10 scr="'+prop.bu+'images/bc.gif"><img id=cap_b8 height=10 width=10 scr="'+prop.bu+'images/bc.gif"><img id=cap_b9 height=10 width=10 scr="'+prop.bu+'images/br.gif"><img id=cap_b10 height=10 width=10 scr="'+prop.bu+'images/br.gif"><img id=cap_b11 height=10 width=10 scr="'+prop.bu+'images/br.gif"><img id=cap_b12 height=10 width=10 scr="'+prop.bu+'images/br.gif"><img id=cap_b13 height=10 width=10 scr="'+prop.bu+'images/br.gif"><img id=cap_b14 height=10 width=10 scr="'+prop.bu+'images/br.gif"><img id=cap_b15 height=10 width=10 scr="'+prop.bu+'images/br.gif"><img id=cap_b16 height=10 width=10 scr="'+prop.bu+'images/br.gif">\
\
<span id=preloadArrows><img height=1 width=1 src="'+prop.bu+'images/age2.png"><img height=1 width=1 src="'+prop.bu+'images/agn2.png"><img height=1 width=1 src="'+prop.bu+'images/ags2.png"><img height=1 width=1 src="'+prop.bu+'images/agw2.png"><img height=1 width=1 src="'+prop.bu+'images/ase2.png"><img height=1 width=1 src="'+prop.bu+'images/asn2.png"><img height=1 width=1 src="'+prop.bu+'images/ass2.png"><img height=1 width=1 src="'+prop.bu+'images/asw2.png"></span>\
    </td>\
\
  </tr>\
  <tr>\
    <td colspan=3 align=center>\
<textarea id=chatArea rows=2 style="width:590px" onfocus="document.getElementById(\'chat\').focus()" >hi</textarea><br>\
<input id=chat type=text style="width:590px" onchange="arimaa.sendChat(this.value)">\
    </td>\
  </tr>\
</table>\
<span style="position:absolute;left:1;top:1;width:20;background-color:gold" onmouseover="arimaa.showTip()">\
?\
</span>\
<span id="version" style="position:absolute;left:22;top:1">\
v\
</span>\
<span id="tip" style="position:absolute;left:1;top:1;background-color:gold" onmouseout="arimaa.hideTip()">\
</span>\
\
<script type="text/javascript">\
//arimaa.init(); \
\
</script>\
\
';
  return s;
}

function makeBoard(){
  var i, j, k, s, ix, iy, is;
  var myobj, mystr;
  var acp = {};
  var d = '';

//alert('in makeboard');

  acp = arimaa.prop; 

  ix = acp.BoardSize.W;
  iy = acp.BoardSize.H;
  is = acp.Image.base + acp.Image.board_n;
//alert('bg image is '+is);
  s = '\
<div style="position:relative;width:'+ix+'px;height:'+iy+'px">\
  <table border=0 xstyle="backgroundImage:url('+is+');" width='+ix+' height='+iy+' id="boardtable">\
    <tr>\
      <td>\
';
//  document.write(s);
  d = d + s;

  for(i=0;i<8;i++){
    for(j=0;j<8;j++){
      ix = acp.BoardOrigin.X + i*acp.SquareSize.W;
      iy = acp.BoardOrigin.Y + j*acp.SquareSize.H;
      is = acp.Image.base + acp.Image.x;
      k = 7-j;
      s = '<img id="s'+i+''+k+'" xsrc="'+is+'" style="position:absolute;left:'+ix+'px;top:'+iy+'px" onmouseover="arimaa.eventOverSquare('+i+','+k+')" onclick="arimaa.eventClickSquare('+i+','+k+')">'
//      document.write(s);
      d = d + s;
    }
  }

  ix = -40; iy = -40;
  is = acp.Image.base + acp.Image.x;
  s = '<img id="sxx" src="'+is+'" style="position:absolute;left:'+ix+'px;top:'+iy+'px">'
//  document.write(s);
  d = d + s;

  s = '\
      </td>\
    </tr>\
  </table>\
</div>\
';
//  document.write(s);
  d = d + s;

  return d;

}

function wget(url){
  var req = arimaa_getHttpObject();
  req.open("GET", url, false);
  req.send(null);
  return req.responseText;
}

var TipTimer;
function setTip(m, t){
  if (t == null){
    t = 3;
  }
  document.getElementById("tip").innerHTML = ' '+m+'&nbsp;&nbsp;<font size=1><br><br></font> ';
  document.getElementById("tip").style.visibility='visible';
  if (TipTimer != null){
    clearTimeout(TipTimer);
  }
  TipTimer = setTimeout('arimaa.hideTip()', t*1000);
}

function showTip(){
  document.getElementById("tip").style.visibility='visible';
}

function hideTip(){
  if (TipTimer != null){
    clearTimeout(TipTimer);
  }
  document.getElementById("tip").style.visibility='hidden';
}


function init(){
  glob.redo = [];


// display the version number
  document.getElementById("version").innerHTML = ' <font size=1>'+version+'</font> ';

// mode can be either 'plan' or 'live'; default is 'plan'
//   plan means that this is a plan window
//   live means we are playing or watching a game
  glob.mode = 'plan';
// if a session id is given then assume this is a live game
  if (vars.sessionid != null){ 
    glob.mode = 'live'; 
  }

  // set default variables
  if (vars.movelist == null){ vars.movelist = "1w"; }
  if (vars.startmove == null){ vars.startmove = "1w"; }
  if (vars.title == null){ vars.title = "Arimaa Game"; }
  if (vars.bgcolor == null){ vars.bgcolor = prop.bgcolor; }
  if (vars.viewfrom == null){ vars.viewfrom = 'w'; }
  if (vars.myrole == null){ vars.myrole = 'v'; }
  if (vars.wplayer == null){ vars.wplayer = 'Gold'; }
  if (vars.bplayer == null){ vars.bplayer = 'Silver'; }
  if (vars.wrating == null){ vars.wrating = '????'; }
  if (vars.brating == null){ vars.brating = '????'; }
  if (vars.rated == null){ vars.rated = 0; }
  if (vars.timecontrol== null){ vars.timecontrol = '1/1/100/5/0'; }
  if (vars.chat == null){ vars.chat = ''; }
  if (vars.result == null){ vars.result = ''; }
  if (vars.reason == null){ vars.reason = ''; }
  if (vars.timeused == null){ vars.timeused = ''; }


// set globabl variables (glob.*) based on inputs (mostly vars.*)
  glob.w = {}; glob.b = {}; 

// initalize and set the moveslow 
  glob.moveslow = {};
  glob.moveslow.steps = [];
  glob.moveslow.state = 'done';
  changeShowSpeed(2);

// used for moving pieces
  glob.moveslow.sxx = document.getElementById('sxx');

// set expert mode off
  changeExpert(0);

// disable game comments page button; not yet implemented, TODO
  if (vars.commentsPage == null){
    document.getElementById("btnG").disabled = true;
  }

// set some default element parameters
  document.getElementById("mainTab").style.backgroundColor = vars.bgcolor;
  document.getElementById("chatArea").rows = 6;
  document.getElementById("chatArea").innerHTML = '';
  document.getElementById("btnT").disabled = true;
  document.getElementById("btnA").disabled = true;
  document.getElementById("btnD").disabled = true;

// hide the preloaded arrows
  document.getElementById("preloadArrows").style.display = 'none';

// We should set up everything and just call this
  setGameState(vars);

// *************** if we are in live mode
  if (glob.mode == 'live'){
    if (vars.webservice != undefined){
      initWebService();
    }
  }
  else{
    document.getElementById("autoShowGroup").style.display = "inline";
  }

  return;



/* look like the following is no longer needed

  glob.w.name = vars.wplayer;
  glob.b.name = vars.bplayer;
  glob.w.rating = vars.wrating;
  glob.b.rating = vars.brating;


// Set the won/lose values
  glob.w.wl = ''; glob.b.wl = '';
  if (vars.reason == 'g'){ glob.reason = 'Lost'; }
  if (vars.reason == 'r'){ glob.reason = 'Resigned'; }
  if (vars.reason == 't'){ glob.reason = 'Time'; }
  if (vars.reason == 'm'){ glob.reason = 'Immobilized'; }
  if (vars.reason == 's'){ glob.reason = 'Score'; }
  if (vars.reason == 'n'){ glob.reason = 'Draw'; }
  if (vars.reason == 'p'){ glob.reason = 'Repeat'; }
  if (vars.reason == 'x'){ glob.reason = 'Exterminated'; }
  if (vars.reason == 'f'){ glob.reason = 'Forfeit'; }
  if (vars.result == 'd'){
    glob.w.wl = 'Draw'; glob.b.wl = 'Draw';
  }
  if (vars.result == 'w'){
    glob.w.wl = 'Won'; glob.b.wl = glob.reason;
  }
  if (vars.result == 'b'){
    glob.w.wl = glob.reason; glob.b.wl = 'Won';
  }

  if (glob.mode == 'plan'){
// might not need this anymore
//    document.getElementById("buttonGroupNet").style.visibility = "hidden";
  }

  initMoveList();
  add2MoveList(vars.movelist);


  glob.viewfrom = vars.viewfrom;

// set glob.curpos based on startmove
//   if given as a number set it directly
  if (vars.startmove != undefined){
    if (vars.startmove.match(/[wb]/i)){
      var re = new RegExp("^"+vars.startmove);
      var i = 0;
      for(i=glob.movelist.length-1;i>=0;i--){
        if (re.test(glob.movelist[i])){
          break;
        }
      }
      if (i<0){ i=0; }
      glob.curpos = i;
    }
    else{
      var i = parseInt(vars.startmove);
      if (i<0){ i = glob.movelist.length + i; }
      if (i>=glob.movelist.length){ i = glob.movelist.length-1; }
      if (i<0){ i=0; }
      glob.curpos = i;
    }
  }



// init the sound manager; don't need to do it here; it will call initSound
//  soundManagerInit();
//  changeMute(0);
//  glob.curSound = '';

// set the initial view
  changeView(glob.viewfrom);


  document.getElementById("sendBtn").value = "Start";
  document.getElementById("gameTitle").innerHTML = vars.title;
  document.getElementById("timeControl").innerHTML = vars.timecontrol;
  uiShowRated();

// disable game comments page button
  if (vars.commentsPage == null){
    document.getElementById("btnG").disabled = true;
  }

  if (glob.mode == 'plan'){
    goPosList(glob.curpos);
  }



//alert('testing ajax');
//glob.webin = new Ajax(vars.webservice);
//glob.webin.post(obj2json({sid:vars.sessionid}));

*/

}

function initWebService(){
  glob.connectedToWebService = 0; // assume we are not connected
  glob.webin = new Ajax(vars.webservice, dataFromWebServiceIn);
  glob.webin.jpost({sid:vars.sessionid, action:"gamestate", wait:0});
}

function dataFromWebServiceIn(status, data){
  if (document == null){ return; } // means the web page was closed
log(status+' in dataFromWebServiceIn\n'+data);
  if (status == 200){
    glob.webinresp = json2obj(data);
    glob.connectedToWebService = 1;
    document.getElementById("connected").innerHTML = 'connected';
    document.getElementById("connected").style.color = '#005500';
    if (glob.webinresp.error != undefined){
//      alert(glob.webinresp.error);
      updateGameStateFromWebService();
      return;
    }
    if (glob.webinresp.ok != undefined){
      updateGameStateFromWebService();
      return;
    }
    if (glob.webinresp.auth != undefined){
      setGameState(glob.webinresp);
      updateGameStateFromWebService();
      return;
    }
//log('in dataFromWebServiceIn got sucess with not data to process '+status);
//alert('in dataFromWebServiceIn got sucess with not data to process '+status);
    updateGameStateFromWebService();
    return;
  }
// we are disconnected, try again in a little while
log('in dataFromWebServiceIn got status '+status);
//alert('in dataFromWebServiceIn got status '+status);
//  updateGameStateFromWebService();
  glob.connectedToWebService = 0;
  setTimeout('arimaa.connectToWebService()', 1000);
  document.getElementById("connected").innerHTML = 'disconnected';
  document.getElementById("connected").style.color = '#550000';
}

function connectToWebService(){
  glob.webin = new Ajax(vars.webservice, connectToWebServiceIn);
  if (glob.webin.jpost == undefined){ return; }
// we need to make a 'gamestate' or 'updategamestate' request with 'wait=0' so
//   that the server knows we have joined the game again.
  glob.webin.jpost({action:"gamestate", sid:vars.sessionid, wait:0 });
}

function connectToWebServiceIn(status, data){
  if (document == null){ return; } // means the web page was closed
  if (status == 200){
    glob.connectedToWebService = 1;
    document.getElementById("connected").innerHTML = 'connected';
    document.getElementById("connected").style.color = '#005500';
    updateGameStateFromWebService();
    return;
  }
  glob.connectedToWebService = 0;
  setTimeout('arimaa.connectToWebService()', 1000);
  document.getElementById("connected").innerHTML = 'disconnected';
  document.getElementById("connected").style.color = '#550000';
}

function updateGameStateFromWebService(){
log('updateGameStateFromWebService');
// does not work on linux unless we create a new Ajax object
  glob.webin = new Ajax(vars.webservice, dataFromWebServiceIn);
  if (glob.webin.jpost == undefined){ return; }
  glob.webin.jpost({action:"updategamestate", sid:vars.sessionid, wait:1, lastchange:vars.lastchange, moveslength:vars.moveslength, chatlength:vars.chatlength, maxwait:300});
}

function dataFromWebServiceOut(status, data){
  if (document == null){ return; } // means the web page was closed

log(status+' out dataFromWebServiceOut\n'+data);
  if (status==200){
    glob.webout_sending = 0;
//if (data == undefined){ alert('data is undefined'); return; }
//if (data == ''){ alert('data is empty'); return; }
    glob.weboutresp = json2obj(data);
    if (glob.weboutresp.error != undefined){
      alert(glob.weboutresp.error);
      return;
    }
    if (glob.weboutresp.ok != undefined){
      glob.webout_save = {};
      if (glob.webout_action == 'chat'){
        uiClearChatIn();
        glob.webout_action = '';
      }
      return;
    }
log('got a sucess response with no data to process');
//alert('in dataFromWebServiceOut got sucess with not data to process '+status);
  }
log('in dataFromWebServiceOut got status '+status);
//alert('in dataFromWebServiceOut got status '+status);
// we need to try and resend
  setTimeout('arimaa.resendDataToWebService()', 1000);
}

function resendDataToWebService(){
  glob.webout_sending=0; 
  sendOut(glob.webout_save)
}

function setGameState(o){
  var cv = 0;
  var spi = 0;
  var ml = 0;
  var bu = 0;
  var res = 0;
  var sm = 0;
  var mv = 0;
  var mvadd = 0;
  var st = 0;
  var sh = 0;


//log('In setGameState\n'+obj2json(o));

// our role needs to be determined first thing
  if (o.role != undefined){
    vars.myrole = o.role;
    glob.myrole = o.role;
  }
  if (o.myrole != undefined){
    vars.myrole = o.myrole;
    glob.myrole = o.myrole;
  }
  if (o.auth != undefined){
    vars.auth = o.auth;
  }
  if (o.lastchange != undefined){
    vars.lastchange = o.lastchange;
  }
  if (o.moveslength != undefined){
    vars.moveslength = o.moveslength;
  }
  if (o.chatlength != undefined){
    vars.chatlength = o.chatlength;
  }
  if (o.title != undefined){
    vars.title = o.title;
    document.getElementById("gameTitle").innerHTML = vars.title;
  }
  if (o.bgcolor != undefined){
    vars.bgcolor = o.bgcolor;
    document.getElementById("mainTab").style.backgroundColor = vars.bgcolor;
  }
  if (o.timecontrol != undefined){
    vars.timecontrol = o.timecontrol.replace(/^[RU] /, ''); // get rid of R/U in timecontrol
    document.getElementById("timeControl").innerHTML = vars.timecontrol;
  }
  if (o.tcmove != undefined){
    vars.tcmove = o.tcmove;
    glob.tcmove = parseInt(o.tcmove);
  }
  if (o.tcreserve != undefined){
    vars.tcreserve = o.tcreserve;
    glob.tcreserve = parseInt(o.tcreserve);
  }
  if (o.tcpercent != undefined){
    vars.tcpercent = o.tcpercent;
    glob.tcpercent = parseInt(o.tcpercent);
  }
  if (o.tcmax != undefined){
    vars.tcmax = o.tcmax;
    glob.tcmax = parseInt(o.tcmax);
  }
  if (o.tctotal != undefined){
    vars.tctotal = o.tctotal;
    glob.tctotal = parseInt(o.tctotal);
  }
  if (o.tcturns != undefined){
    vars.tcturns = o.tcturns;
    glob.tcturns = parseInt(o.tcturns);
  }
  if (o.tcturntime != undefined){
    vars.tcturntime = o.tcturntime;
    glob.tcturntime = parseInt(o.tcturntime);
  }
  if (o.tcwreserve != undefined){
    vars.tcwreserve = o.tcwreserve;
    glob.tcwreserve = parseInt(o.tcwreserve);
  }
  if (o.tcbreserve != undefined){
    vars.tcbreserve = o.tcbreserve;
    glob.tcbreserve = parseInt(o.tcbreserve);
  }
  if (o.tcwreserve2 != undefined){
    vars.tcwreserve2 = o.tcwreserve2;
    glob.tcwreserve2 = parseInt(o.tcwreserve2);
  }
  if (o.tcbreserve2 != undefined){
    vars.tcbreserve2 = o.tcbreserve2;
    glob.tcbreserve2 = parseInt(o.tcbreserve2);
  }
  if (o.tcgame != undefined){
    vars.tcgame = o.tcgame;
    glob.tcgame = parseInt(o.tcgame);
  }
  if (o.tcgamenow != undefined){
    vars.tcgamenow = o.tcgamenow;
    glob.tcgamenow = parseInt(o.tcgamenow);
  }
  if (o.lastmoveused != undefined){
    vars.lastmoveused = o.lastmoveused;
    glob.lastmoveused = parseInt(o.lastmoveused);
  }
  if (o.lastmovereserve != undefined){
    vars.lastmovereserve = o.lastmovereserve;
    glob.lastmovereserve = parseInt(o.lastmovereserve);
  }
  if (o.createtime != undefined){
    vars.createtime = o.createtime;
    glob.createtime = parseInt(o.createtime);
  }
  if (o.timeonserver != undefined){
    vars.timeonserver = o.timeonserver;
    glob.timeonserver = parseInt(o.timeonserver);
//    var now = new int(new Date().time/1000);
//    var now = Math.floor(new Date().time/1000);
    var now = Math.floor(new Date().getTime()/1000);
    glob.timediff = now - vars.timeonserver;
  }
  if (o.wstarttime != undefined){
    vars.wstarttime = o.wstarttime;
    glob.wstarttime = parseInt(o.wstarttime);
  }
  if (o.bstarttime != undefined){
    vars.bstarttime = o.bstarttime;
    glob.bstarttime = parseInt(o.bstarttime);
  }
  if (o.takeback != undefined){
    vars.takeback = o.takeback;
    glob.takeback = parseInt(o.takeback);
  }
  if (o.wplayer != undefined){
    if (o.wplayer == ''){ o.wplayer = 'Gold'; }
    vars.wplayer = o.wplayer;
    glob.w.name = o.wplayer;
    spi = 1;
  }
  if (o.bplayer != undefined){
    if (o.bplayer == ''){ o.bplayer = 'Silver'; }
    vars.bplayer = o.bplayer;
    glob.b.name = o.bplayer;
    spi = 1;
  }
  if (o.wrating != undefined){
    vars.wrating = o.wrating;
    glob.w.rating = o.wrating;
    spi = 1;
  }
  if (o.brating != undefined){
    vars.brating = o.brating;
    glob.b.rating = o.brating;
    spi = 1;
  }
  if (o.wpresent != undefined){
// see if we need to make the opponent entered sound; viewer also hear it
    if (glob.myrole != 'w'){
      if (glob.wpresent == 0){
        if (parseInt(o.wpresent) > 0){
          soundManager.play2('enter');
        }
      }
      else{
        if (parseInt(o.wpresent) == 0){
          soundManager.play2('leave');
        }
      }
    }
    vars.wpresent = o.wpresent;
    glob.wpresent = parseInt(o.wpresent);
  }
  if (o.bpresent != undefined){
// see if we need to make the opponent entered sound; viewer also hear it
    if (glob.myrole != 'b'){
      if (glob.bpresent == 0){
        if (parseInt(o.bpresent) > 0){
          soundManager.play2('enter');
        }
      }
      else{
        if (parseInt(o.bpresent) == 0){
          soundManager.play2('leave');
        }
      }
    }
    vars.bpresent = o.bpresent;
    glob.bpresent = parseInt(o.bpresent);
  }
  if (o.warrived != undefined){
    vars.warrived = o.warrived;
    glob.warrived = parseInt(o.warrived);
  }
  if (o.barrived != undefined){
    vars.barrived = o.barrived;
    glob.barrived = parseInt(o.barrived);
  }
  if (o.wstartmove != undefined){
    vars.wstartmove = o.wstartmove;
    glob.wstartmove = parseInt(o.wstartmove);
    glob.gamestarted = 1;
  }
  if (o.bstartmove != undefined){
    vars.bstartmove = o.bstartmove;
    glob.bstartmove = parseInt(o.bstartmove);
    glob.gamestarted = 1;
  }
  if (o.wstarttime != undefined){
    vars.wstarttime = o.wstarttime;
    glob.wstarttime = parseInt(o.wstarttime);
    glob.gamestarted = 1;
  }
  if (o.bstarttime != undefined){
    vars.bstarttime = o.bstarttime;
    glob.bstarttime = parseInt(o.bstarttime);
    glob.gamestarted = 1;
  }
  if (o.viewfrom != undefined){
    vars.viewfrom = o.viewfrom;
    glob.viewfrom = o.viewfrom;
    cv = 1;
  }
  if (o.rated != undefined){
    glob.rated = parseInt(o.rated);
    vars.rated = (glob.rated>0)?'R':'U';
    uiShowRated();
  }
  if (o.postal != undefined){
    vars.postal = o.postal;
    glob.postal = parseInt(o.postal);
  }
  if (o.expert != undefined){
    vars.expert = o.expert;
    glob.expert = parseInt(o.expert);
    changeExpert(glob.expert);
  }
  if (o.canstart != undefined){
    vars.canstart = o.canstart;
    glob.canstart = parseInt(o.canstart);
  }
  if (o.turn != undefined){
    vars.turn = o.turn;
    glob.turn = o.turn;
  }
  if (o.plycount != undefined){
    vars.plycount = parseInt(o.plycount);
    glob.plycount = vars.plycount;
  }
  if (o.movenum != undefined){
    vars.movenum = o.movenum;
    glob.movenum = vars.movenum;
  }
  if (o.reason != undefined){
    vars.reason = o.reason;
    res = 1;
  }
  if (o.result != undefined){
    vars.result = o.result;
    res = 1;
  }
  if (o.startmove != undefined){
    vars.startmove = o.startmove;
    sm = 1;
  }
  if (o.planmove != undefined){
    vars.planmove = o.planmove;
    sm = 1;
  }
  if ((o.moves != undefined)&&(o.moves != '')){
    o.movelist = o.moves;
  }
  if ((o.movelist != undefined)&&(o.movelist != '')){
// we might be in the middle of showing a move; if so, finish it fast before processing this
    initMoveList();
    if (glob.mode == 'live'){
      add2MoveList(o.movelist, 1); // 1 means it is added as a real move rather than a plan move
    }
    else{
      add2MoveList(o.movelist);
      if (vars.result != ''){
        setTip('Click the [Show] button to see the moves.', 5);
        if (glob.expert != 1){
          uiHidePlanButtons();
        }
      }
      else{
        setTip('Move the pieces to plan move.', 5);
      }
    }
    mv = 1;
  }
  if ((o.movesadd != undefined)&&(o.movesadd)){
// we might be in the middle of showing a move; if so, finish it fast before processing this
    if (glob.mode == 'live'){
      add2MoveList(o.movesadd, 1); // 1 means it is added as a real move rather than a plan move
    }
    else{
      add2MoveList(o.movesadd);
    }
    mv = 1;
    mvadd = 1;
  }
  if ((o.chat != undefined)&&(o.chat != '')){
    vars.chat = o.chat;
    document.getElementById("chatArea").innerHTML = vars.chat;
    document.getElementById("chatArea").scrollTop = document.getElementById("chatArea").scrollHeight;
  }
  if ((o.chatadd != undefined)&&(o.chatadd != '')){
    vars.chatadd = o.chatadd;
    document.getElementById("chatArea").innerHTML += vars.chatadd;
    document.getElementById("chatArea").scrollTop = document.getElementById("chatArea").scrollHeight;
    soundManager.play2('chat');
  }

  if (o.timeused != undefined){
    vars.timeused = o.timeused;
    bu = 1;
  }

// Set the won/lose values
  if (res == 1){
    glob.w.wl = ''; glob.b.wl = '';
    if (vars.reason == 'g'){ glob.reason = 'Lost'; }
    if (vars.reason == 't'){ glob.reason = 'Time'; }
    if (vars.reason == 'r'){ glob.reason = 'Resigned'; }
    if (vars.reason == 'e'){ glob.reason = 'Eliminated'; }
    if (vars.reason == 'm'){ glob.reason = 'Immobilized'; }
    if (vars.reason == 'p'){ glob.reason = 'Repeat'; }
    if (vars.reason == 'i'){ glob.reason = 'Illegal Move'; }
    if (vars.reason == 'f'){ glob.reason = 'Forfeit'; }
    if (vars.reason == 'a'){ glob.reason = 'Abandoned'; }
    if (vars.reason == 'n'){ glob.reason = 'Draw; No Rabbits'; }
    if (vars.reason == 's'){ glob.reason = 'Score'; }
    if (vars.result == 'd'){
      glob.w.wl = glob.reason; glob.b.wl = glob.reason;
    }
    if (vars.result == 'u'){
      glob.w.wl = glob.reason; glob.b.wl = glob.reason;
    }
    if (vars.result == 'w'){
      glob.w.wl = 'Won'; glob.b.wl = glob.reason;
    }
    if (vars.result == 'b'){
      glob.w.wl = glob.reason; glob.b.wl = 'Won';
    }
    if ((vars.result != '') && (glob.resultSoundPlayed != 1) && (glob.mode == 'live')){
      glob.resultSoundPlayed = 1;
      if (glob.myrole == 'v'){
        if ((vars.result == glob.viewfrom) || (glob.viewfrom == 'n')){
//          soundManager.play2('win');
          glob.playResultSound = 'win';
        }
        else{
//          soundManager.play2('lose');
          glob.playResultSound = 'lose';
        }
      }
      else{
        if (vars.result == glob.myrole){
//          soundManager.play2('win');
          glob.playResultSound = 'win';
        }
        else{
//          soundManager.play2('lose');
          glob.playResultSound = 'lose';
        }
      }
    }
  }

// setup the timeused and timeresv arrays
  if (glob.mode != 'live'){
//alert(vars.timeused);
    if (vars.timeused != null){
      if (bu == 1){
        buildUsedResvTimeArrays(vars.timeused);
      }
    }
  }

// determine if the game started and if it can start
  if (glob.mode == 'live'){
    if (res == 1){
      if (glob.myrole != 'v'){
        setTip('Game Over. Close window to return to gameroom and start new game.', 6);
      }
    }
    if (glob.wpresent == undefined){ glob.wpresent = 0; }
    if (glob.bpresent == undefined){ glob.bpresent = 0; }
    if (glob.turn == undefined){ glob.turn = 'u'; } // u means unknown
    if (glob.myrole == undefined){ glob.myrole = 'v'; } // w=playingWhite b=playingBlack v=viewer
    if (glob.postal == undefined){ glob.postal = 0; }
    if (glob.gamestarted == undefined){ glob.gamestarted = 0; }
    if (glob.gamestarted == 0){
      if (glob.realIndex > 0){ glob.gamestarted = 1; }
      if ((vars.canstart == undefined) || (vars.canstart == 0)){
        glob.canstart = 0;
        if ((glob.warrived>0) && (glob.barrived>0)){
          glob.canstart = 1;
        }
      }
    }
    if ((glob.gamestarted == 1) && (glob.timersstarted != 1)){
      uiStartTimers();
      glob.timersstarted = 1;
    }
    if (glob.gamestarted == 1){
      if (glob.myrole != 'v'){
        if (glob.myrole == glob.turn){
          if (vars.result == ''){
//            if ((glob.movenum == '1w') || (glob.movenum=='1b') || (glob.movenum=='1g') || (glob.movenum=='1s')){
            if ((glob.plycount == undefined) || ((glob.plycount == 1) && (glob.myrole=='b'))){
              setTip('Click on two of your pieces to swap them. Click the [Send] button when done.', 8);
            }
            else{
              setTip('Move your pieces and click the [Send] button.', 5);
            }
          }
        }
      }
    }
    if (glob.myrole == 'v'){
// might need to change this if we allow viewers to send moves; should use a differnet group
      document.getElementById("buttonGroupNet").style.visibility = "hidden";
    }
  }

// if startmove was given set glob.curpos based on startmove
//   if given as a number set it directly
  if (sm == 1){
    if (vars.startmove.match(/[wb]/i)){
      var re = new RegExp("^"+vars.startmove);
      var i = 0;
      for(i=glob.movelist.length-1;i>=0;i--){
        if (re.test(glob.movelist[i])){
          break;
        }
      }
      if (i<0){ i=0; }
      glob.curpos = i;
    }
    else{
      var i = parseInt(vars.startmove);
      if (i<0){ i = glob.movelist.length + i; }
      if (i>=glob.movelist.length){ i = glob.movelist.length-1; }
      if (i<0){ i=0; }
      glob.curpos = i;
    }
  }
  else{
    if (mv == 1){
      if ((glob.myrole == 'v') && (mvadd==1) && (glob.curpos != glob.realIndex)){
// don't change the current position if viewer is not already at the current position 
      }
      else{
        glob.curpos = glob.movelist.length - 1;
      }
    }
  }


// this automatically sets the view in live games based on our role if we are a player
  if (glob.setview == undefined){ 
    if ((glob.myrole == 'w') || (glob.myrole == 'b')){
      glob.setview = 1; 
      glob.viewfrom = glob.myrole; 
      cv = 1; 
    }
  }

// this might not be needed
  if (glob.viewfrom == undefined){
    glob.viewfrom = 'w';
    if (glob.myrole == 'w'){ glob.viewfrom = 'w'; }
    if (glob.myrole == 'b'){ glob.viewfrom = 'b'; }
    cv = 1;
  }

  if (cv == 1){
    changeView(glob.viewfrom);
  }
  else{
    if ((spi == 1) || (res == 1)){
      showPlayerInfo(); // changeView also calls showPlayerInfo, so not needed if changeView was called
    }
  }
  uiShowSendButton();
//alert(glob.curpos+' '+glob.myrole);
  if (mv == 1){
//alert(glob.curpos+' '+glob.realIndex);
    if ((glob.myrole == 'v') && (mvadd==1) && (glob.curpos != glob.realIndex-1)){
// don't show the new move to viewer if not at latest position
    }
    else{
//alert(glob.curpos+' '+ glob.realIndex);
      stopMoveSlow();
      if ((glob.mode=='live') && ((getTurnAt(glob.curpos) == glob.myrole) || (glob.myrole == 'v'))){
        goPosList(glob.curpos);
        showMoveSlow();
        sh = 1; // flag that we are showing the move
      }
      else{
        goPosList(glob.curpos);
      }
// make the game start sound
      if ((glob.timersstarted==1) && (getNumAt() == 1)){
log('num is '+getNumAt()+' \n'+vars.movelist);
//alert('num is '+getNumAt()+' \n'+vars.movelist);
// The start sound should only be made if the current move num is 1
        if ((glob.myrole == 'v') || (glob.myrole == 'b')){
// Don't play the start sound if we hit the start button.
          soundManager.play2('start');
        }
      }
    }
  }
// see if we need to play the result sound now or after showing the move
  if ((glob.playResultSound != undefined) && (sh == 0)){
    soundManager.play2(glob.playResultSound);
    glob.playResultSound = undefined;
  }
  if ((glob.mode == 'live') && (glob.myrole != 'v') && (getNumAt() == 1)){
// this causes the pieces to appear on the board during setup, for the players
    if (glob.myrole == 'w'){
      setTimeout('arimaa.eventOverSquare(5,5)', 500); 
    }
    if (glob.myrole == 'b'){
      setTimeout('arimaa.eventOverSquare(5,5)', 3000);
    }
  }
}

function sendChat(m){
  if (m.match(/^\/log$/)){
    uiClearChatIn();
    openLogWindow();
    return;
  }
  if (m.match(/^\/glob$/)){
    uiClearChatIn();
    openVarsWindow(obj2json(glob)+'\n\n\n'+obj2json(vars));
    return;
  }
  if (m.match(/^\/vars$/)){
    uiClearChatIn();
    openVarsWindow(obj2json(glob)+'\n\n\n'+obj2json(vars));
    return;
  }
  if (glob.mode == 'plan'){ return; }

// Not sure why, but it did not work with sentOut(o); got sendOut undefined error
//  var o = {action:"chat", sid:vars.sessionid, auth:vars.auth, chat:m};
//  sentOut(o);

  sendOut({action:"chat", sid:vars.sessionid, auth:vars.auth, chat:m});
}

function sendMove(){
log('send button pressed');
  if (glob.turn == glob.myrole){
    if (glob.gamestarted == 1){
      if (glob.postal == 1){
        var a = confirm("Do you want to send the move?");
        if (! a){ return; }
      }
      var m = getFirstPlanMove();
      m = m.replace(/^\d+\w */, '');
      if (m != ''){
log('sending move '+m);
        sendOut({action:"move", sid:vars.sessionid, auth:vars.auth, move:m});
      }
      else{
        alert('Must move at least one piece.');
      }
    }
    else{
      if (glob.canstart == 1){
        sendOut({action:"startgame", sid:vars.sessionid, auth:vars.auth});
      }
    }
  }
}

function sendResign(){
  if ((glob.myrole == 'w') || (glob.myrole == 'b')){
    var a = confirm('Do you want to resign?');
    if (! a){ return; }
    sendOut({action:"resign", sid:vars.sessionid, auth:vars.auth});
  }
}

function sendOut(o){
  if (vars.webservice != undefined){
    if (glob.webout_sending != 1){
      if (glob.connectedToWebService == 1){
        glob.webout_sending = 1;

// does not work on linux unless we create a new Ajax object
        glob.webout = new Ajax(vars.webservice, dataFromWebServiceOut);

        glob.webout_action = o.action;
        glob.webout_save = o;
        glob.webout.jpost(o);
      }
    }
  }
}


function initSound(){
  soundManager.createSound('step', prop.bu+'sounds/step.mp3');
  soundManager.createSound('trap', prop.bu+'sounds/trap.mp3');
  soundManager.createSound('win', prop.bu+'sounds/win.mp3');
  soundManager.createSound('lose', prop.bu+'sounds/lose.mp3');
  soundManager.createSound('clock', prop.bu+'sounds/clock.mp3');
  soundManager.createSound('start', prop.bu+'sounds/start.mp3');
  soundManager.createSound('setup', prop.bu+'sounds/setup.mp3');
  soundManager.createSound('chat', prop.bu+'sounds/chat.mp3');
  soundManager.createSound('enter', prop.bu+'sounds/enter.mp3');
  soundManager.createSound('leave', prop.bu+'sounds/leave.mp3');
  soundManager.setVolume(100);
  changeMute(0);
  glob.curSound = '';
  glob.sm2Loaded = 1;
}

function playSound(s){
  if (glob.sm2Loaded != undefined){
    soundManager.play(s);
  }
}

function initMoveList(){
// initilize variables if this is the first time
  if (glob.poslist == null){
    glob.realIndex = 0; // this is a pointer to the divide between real and planned moves
    glob.realCount = 0; // this number is converted to moveCount to get the moveCount string of the last move; due to takebacks the could be different than glob.movelist.length.
    glob.movelist = [];
    glob.poslist = [];
    glob.movelist[0] = "1w ";
    glob.poslist[0] = "1w ................................................................";
    glob.planIndex = glob.realIndex;
    glob.planCount = glob.realCount;

    var mc =  num2MoveCount(glob.planCount);
    var vml = document.getElementById("movelist"); // visual move list
    uiClearMoveList(); // first remove all items from the virtual move list
    vml[glob.planIndex] = new Option(mc.replace(/w/,'g').replace(/b/,'s'), "", false);
    vml[glob.planIndex].style.background = "white";
  }
}

// if the real flag is given then the given moves are added to the section of the movelist
//   and any plan moves already in the movelist are deleted.
// if the real flag is not given
//   if planmove is defined then the moves before planmove are added as real moves
//     and the moves on and after planmove are added as plan moves.
//   if planmove is not defined then the moves are added to the plan section
//     and the plan section is assumed to begin after the real section.
// To force all moves to be added as real moves set planmove = -1
// 
function add2MoveList(m, real){
  var plan, planCount, planIndex;

  if (vars.planmove != undefined){
    if (vars.planmove.match(/[wb]/i)){
      planCount = moveCount2Num(vars.planmove);
    }
    else{
      planIndex = parseInt(vars.planmove) - 1;
    }
    vars.planmove = undefined;
  }
  else{
    planCount = glob.realCount;
    planIndex = glob.realIndex;
  }

  plan = 0;
  var ma = m.split(/\n+/);
  for(i=0;i<ma.length;i++){
    if (planCount>=0){
      if (glob.planCount >= planCount){ plan = 1; }
    }
    if (planIndex>=0){
      if (glob.planIndex >= planIndex){ plan = 1; }
    }
    if (real == 1){ plan = 0; }
    addToMoveList(ma[i], plan);
  }
}

// removes the last item from movelist, poslist and visual move list
//   also decrement the indexs and counts if needed
function removeFromMoveList(){
//alert('poping '+glob.movelist[glob.movelist.length-1]);
  glob.movelist.pop()
  glob.poslist.pop();
  uiRemoveMoveList();
  if (glob.planIndex >= glob.movelist.length){
    glob.planIndex -= 1;
    glob.planCount -= 1;
    if (glob.movelist[glob.movelist.length-1].match(/takeback/)){
      glob.planCount += 2;
    }
  }
  if (glob.realIndex >= glob.movelist.length){
    glob.realIndex -= 1;
    glob.realCount -= 1;
    if (glob.movelist[glob.movelist.length-1].match(/takeback/)){
      glob.realCount += 2;
    }
  }
  if (glob.curpos >= glob.movelist.length){
    glob.curpos = glob.movelist.length-1;
  }
}

function addToMoveList(m, plan){
  var i, mc, re;
  var vml = document.getElementById("movelist"); // visual move list
  var ml = glob.movelist;
  var pl = glob.poslist;

  if (m == null){ return; }
  if (plan == null){ plan = 0; }

  m = m.replace(/[\s\n]+/g, ' ');
  m = m.replace(/^[\s\n]+/, '');
  m = m.replace(/[\s\n]+$/, '');
// strip off the move numbers; we can't trust the user to provide it
  m = m.replace(/^ *\d+[wb] */i, '');

// skip blank moves
  if (m.length < 1){ return; }



// if we are adding a real move (i.e. plan=0) and planIndex > realIndex
//    then we need to delete all the plan moves after realIndex
  if ((! plan) && (glob.planIndex > glob.realIndex)){
    while(glob.planIndex > glob.realIndex){
      removeFromMoveList();
    }
  }

// we now have a move string which we will add to the move list, but
//   if it contains bad steps they will be ignored
// we keep realIndex and realMoveCount as well as a
//   planIndex and planMoveCount
// the realIndex points to where the real moves end; these can't be changed
// the planIndex points to the very last move;
//   so it is always true that planIndex >= realIndex
  mc = num2MoveCount(glob.planCount);
  vml[glob.planIndex] = new Option(mc.replace(/w/,'g').replace(/b/,'s')+' '+m, "", false);
  vml[glob.planIndex].style.background = "white";
  if (plan){
    vml[glob.planIndex].style.background = "lavender";
  }
  ml[glob.planIndex] = mc+' '+m;
  glob.planIndex += 1;
  if (m.match(/takeback/i)){
    glob.planCount -= 1;
    mc =  num2MoveCount(glob.planCount);
// the position is set by going back through the move list to find the
//   count and using the position from that count
    re = new RegExp("^"+mc+" ");
    for(i=glob.planIndex-1;i>=0;i--){
      if (re.test(pl[i])){
        break;
      }
    }
    if (i<0){ i = 0; }
    pl[glob.planIndex] = pl[i];
    ml[glob.planIndex] = mc;
    vml[glob.planIndex] = new Option(mc.replace(/w/,'g').replace(/b/,'s'), "", false);
    vml[glob.planIndex].style.background = "white";
  }
  else{
    glob.planCount += 1;
    mc =  num2MoveCount(glob.planCount);
    pl[glob.planIndex] = mc+' '+makeMove(pl[glob.planIndex-1], m);
    ml[glob.planIndex] = mc;
    vml[glob.planIndex] = new Option(mc.replace(/w/,'g').replace(/b/,'s'), "", false);
    vml[glob.planIndex].style.background = "white";
  }
  if (! plan){
    glob.realIndex = glob.planIndex;
    glob.realCount = glob.planCount;
  }
  else{
    vml[glob.planIndex].style.background = "lavender";
  }
}

function getFirstPlanMove(){
  return(glob.movelist[glob.realIndex].replace(/^\d+\w /, ''));
}

function moveCount2Num(m){
  var a = [];
  var n;
  a = m.match(/(\d+)(\w)/);
  n = (parseInt(a[1])-1)*2 + ((a[2] == 'b')?1:0);
  return n;
}

function num2MoveCount(n){
  var a, b;
  a = Math.floor(n/2)+1;
  b = (n%2 == 1)?'b':'w';
  return ''+a+b;
}


function buildUsedResvTimeArrays(tu){
  var u, r=[], p;
  var tc;
  var pr=[];

  var tcm;
  tc = parseTimeControl(vars.timecontrol);
  glob.tc = tc;
  u = tu.split(/ +/);
  pr[0] = tc.r; pr[1] = tc.r; // setup players initial reserve
  r[0] = tc.r; r[1] = tc.r; // both players have same initial reserve
  for(i=0;i<u.length;i++){
    tcm = tc.m;
    if (i<2){
      if (tcm < 60){ tcm = 60; }
    }
    p = i%2; // which player w or b; p==0 is w; p==1 is b
    pr[p] = pr[p] + tcm - u[i];
    if ((vars.nosetupreserve != undefined) && (i>=0) && (i<=1)){
      if (u[i]<tcm){ // if player used less than the allowed setup move time,
        pr[p] = tc.r; // the unused move time from setup is not added to the reserve
      }
    }
    if ((tc.l > 0) && (pr[p] > tc.l)){ pr[p] = tc.l; }
    r[i] = pr[p];
  }
  glob.timeused = u;
  glob.timeresv = r;
}

// This returns a hash with the parameters of the time controls
//   as defined in the Arimaa match rules. The returned parameters
//   are in lower case where as the definition uses upper case.
//   The returned parameters are in units of seconds.
// The G parameter is defined in the match rules to be either
//   time or number of turns. If it is time then g is returned
//   if it is number of turns then gt is returned.
function parseTimeControl(tcs){
  var tc;
  var r = {};

  tc = tcs.split(/\//);
  if (tc.length < 1){ tc[0] = '0'; }
  if (tc.length < 2){ tc[1] = '0'; }
  if (tc.length < 3){ tc[2] = '100'; }
  if (tc.length < 4){ tc[3] = '0'; }
  if (tc.length < 5){ tc[4] = '0'; }
  if (tc.length < 6){ tc[5] = '0'; }

  r.m = seconds(tc[0]);
  r.r = seconds(tc[1]);
  r.p = parseInt(tc[2]);
  r.l = seconds(tc[3]);

  if (tc[4].match(/t$/i)){
    r.gt = tc[4];
    r.gt = r.gt.replace(/\D/g, '');
  }
  else{
    r.g = seconds(tc[4], 1);
  }

  r.t = seconds(tc[5]);

  return r;
}

// Convert a time field into seconds; the format can be
//   sec or min:sec or a series of numbers seperated
//   by unit letters; for example 12h15m10s would mean
//   12 hours, 15 minutes and 10 seconds.
// If the hourFlag is passed then it means that instead
//   of min:sec it is hour:min.
function seconds(tin, hourFlag){
  var min, sec, x;

//alert(tin);
  if (tin == null){ return; }
  tin = '' + tin;
  tin = tin.replace(/[^\w\:]/g, '');
  tin = tin.toLowerCase();
  sec = 0;
  if (tin.match(/[dhms]/)){
    if (x = tin.match(/(\d+)d/)){ sec += 86400*parseInt(x[1]); }
    if (x = tin.match(/(\d+)h/)){ sec += 3600*parseInt(x[1]); }
    if (x = tin.match(/(\d+)m/)){ sec += 60*parseInt(x[1]); }
    if (x = tin.match(/(\d+)s/)){ sec += parseInt(x[1]); }
  }
  else{
    sec = 0; min = tin;
    if (tin.match(/\:/)){
      if (x = tin.match(/(\d+)\:(\d+)/)){
        if (x.length > 2){ min = parseInt(x[1]); sec = parseInt(x[2]); }
      }
    }
    sec = sec + 60*parseInt(min);
    if (hourFlag != null){ sec = 60*sec; }
  }
  return sec;
}

function autoShowBtnChange(){
  if ((glob.autoShowTimer != null) && (glob.autoShowTimer != 0)){
    clearTimeout(glob.autoShowTimer);
  }
  if (document.getElementById("autoShowBtn").checked){
    if (document.getElementById("realShowBtn").checked){
      if (glob.curpos+1 < glob.timeused.length){
        glob.autoShowTimer = setTimeout('arimaa.autoShowBtnChange()', 1000*glob.timeused[glob.curpos+1]);
      }
    }
    else{
      if (glob.curpos+1 < glob.timeused.length){
        glob.autoShowTimer = setTimeout('arimaa.autoShowBtnChange()', 1000*5);
      }
    }
    showMoveSlow();
  }
}


function showMoveSlow(){
//alert('in showmove');
// test sound
//soundManager.play2('step');

/*
      if (glob.autoShow == 1){
        document.getElementById("showBtn").style.backgroundColor = "white";
        glob.autoShow = 0;
        if ((glob.autoShowTimer != null) && (glob.autoShowTimer != 0)){
          clearTimeout(glob.autoShowTimer);
          glob.autoShowTimer = 0;
        }
      }
    if (glob.mode == 'plan'){
      if ((glob.autoShow == undefined) || (glob.autoShow == 0)){
        document.getElementById("showBtn").style.backgroundColor = "#aaffaa";
        glob.autoShow = 1;
        glob.autoShowTimer = setTimeout('arimaa.showMoveSlow()', 1000*glob.timeused[glob.curpos]);
      }
    }
*/

  if (glob.moveslow.state != 'done'){ 
    return; 
  }
  glob.moveslow.state = 'getMove';
//alert('starting');

  glob.moveslow.rate = 20;
  glob.moveslow.maxspeed = 3;
  glob.moveslow.pauseafterstep = 220;
  if (glob.moveslow.speed == 2){
    glob.moveslow.rate = 15;
    glob.moveslow.maxspeed = 3;
    glob.moveslow.pauseafterstep = 180;
  }
  if (glob.moveslow.speed == 3){
    glob.moveslow.rate = 10;
    glob.moveslow.maxspeed = 4;
    glob.moveslow.pauseafterstep = 150;
  }
  if (glob.moveslow.speed == 4){
    glob.moveslow.rate = 10;
    glob.moveslow.maxspeed = 5;
    glob.moveslow.pauseafterstep = 100;
  }
  if (glob.autoShow == 1){
    glob.autoShowTimer = setTimeout('arimaa.showMoveSlow()', 1000*glob.timeused[glob.curpos]);
  }
  showMoveSlowDo();
}

function showMoveSlowDo(){
  var m, s, i1, i1o, i2, s1, s2, e1, e2, mx, my, moved;
  var a = [];

  if (glob.moveslow.state == 'getMove'){
/*
//    if (glob.curpos >= glob.movelist.length-1){
    if (glob.curpos >= glob.realIndex){
//      glob.curpos -= 1;
      glob.curpos = glob.realIndex - 1;
      goPosList(glob.curpos);
// take a small pause after resetting the position so that
//   the player can register the change.
      glob.moveslow.tid = setTimeout("arimaa.showMoveSlowDo()", 250);
      return;
    }
*/

//alert(glob.curpos+' '+glob.movelist.length);
    if (glob.curpos >= glob.movelist.length-1){
      glob.curpos = glob.movelist.length - 2;
      goPosList(glob.curpos);
// take a small pause after resetting the position so that
//   the player can register the change.
      glob.moveslow.tid = setTimeout("arimaa.showMoveSlowDo()", 250);
      return;
    }



//    m = glob.movelist[glob.curpos];
//    m = m.replace(/\d+\w +/, '');
    m = getMoveAt(glob.curpos);
    glob.moveslow.steps = m.split(/ +/);
    glob.moveslow.curpos = glob.curpos;
    glob.moveslow.state = 'getStep';
  }
// check if the displayed position got changed (by user clicking on
//   on of the buttons; if so then we should stop.
  if (glob.moveslow.curpos != glob.curpos){
    glob.moveslow.state = 'done';
    return; 
  }
  if (glob.moveslow.state == 'getStep'){
    if (glob.moveslow.steps.length == 0){ 
      if (glob.curpos < glob.movelist.length){ glob.curpos += 1; }
      glob.moveslow.state = 'done';
      goPosList(glob.curpos);
      if (glob.playResultSound != undefined){
        soundManager.play2(glob.playResultSound);
        glob.playResultSound = undefined;
      }
      glob.curSound = '';
      return; 
    }
    s = glob.moveslow.steps.shift();
    a = stepInfo(s);
    if (a == null){ 
// bad step; ignore it
      glob.moveslow.tid = setTimeout("arimaa.showMoveSlowDo()", 1);
      return; 
    } 
    i1 = cord2i(a[2]);
    i1o = i1;
    i1 = viewMap(glob.viewfrom, i1);
    s1 = 's' + i2cord(i1, 1);
    e1 = document.getElementById(s1);
    glob.moveslow.e1 = e1;
// we explicitly set the height and width of sxx here
//   this is needed for the disappear shrinking to work
    glob.moveslow.sxx.style.width = prop.PieceSize.W;
    glob.moveslow.sxx.style.height = prop.PieceSize.H;

    if (a[3] == ''){
      glob.moveslow.type = 'appear';
      glob.moveslow.img = eval("prop.Image."+a[1]);
      e1.src = prop.Image.base + glob.moveslow.img;
      if (glob.curSound != 'setup'){
        if ((glob.mode == 'plan') || (glob.myrole == 'v') || (glob.turn == glob.myrole)){
          soundManager.play2('setup');
          glob.curSound = 'setup';
        }
      }
    }
    else{
      if (a[3] == 'x'){
        glob.moveslow.type = 'disappear';
        glob.moveslow.sxx.style.left = parseInt(glob.moveslow.e1.style.left);
        glob.moveslow.sxx.style.top = parseInt(glob.moveslow.e1.style.top);
        if (glob.mute == 0){
          if (glob.curSound != ''){
            soundManager.stop(glob.curSound);
            glob.curSound = '';
          }
          soundManager.play2('trap');
          glob.curSound = 'trap';
        }
      }
      else{
        glob.moveslow.type = 'move';
        i2 = dir2i(i1o, a[3]);
        i2 = viewMap(glob.viewfrom, i2);
        s2 = 's' + i2cord(i2, 1);
        e2 = document.getElementById(s2);
        glob.moveslow.e2 = e2;
    
        glob.moveslow.x1 = parseInt(e1.style.left);
        glob.moveslow.y1 = parseInt(e1.style.top);
        glob.moveslow.x2 = parseInt(e2.style.left);
        glob.moveslow.y2 = parseInt(e2.style.top);
        glob.moveslow.dx = (glob.moveslow.x2 - glob.moveslow.x1);
        glob.moveslow.dy = (glob.moveslow.y2 - glob.moveslow.y1);
        if (glob.moveslow.dx != 0){
          glob.moveslow.dx = glob.moveslow.dx/Math.abs(glob.moveslow.dx);
        }
        if (glob.moveslow.dy != 0){
          glob.moveslow.dy = glob.moveslow.dy/Math.abs(glob.moveslow.dy);
        }
    
        glob.moveslow.sxx.style.left = glob.moveslow.x1;
        glob.moveslow.sxx.style.top = glob.moveslow.y1;
        glob.moveslow.curspeed = 1;

        if (glob.curSound != ''){
//          soundManager.stop(glob.curSound);
          glob.curSound = '';
        }
      }
    }
    glob.moveslow.state = 'breakBeforeStep';
    glob.moveslow.tid = setTimeout("arimaa.showMoveSlowDo()", 1);
    return;
  }
  if (glob.moveslow.state == 'breakBeforeStep'){
// We need this state so that after sxx is positioned a
//   break can be taken so that sxx is really moved to the
//   position before we set the image in it. Otherwise there is flicker.
    if (glob.moveslow.type == 'move'){
      glob.moveslow.sxx.src = glob.moveslow.e1.src;
      glob.moveslow.e1.src = prop.Image.base + prop.Image.x;
    }
    if (glob.moveslow.type == 'disappear'){
      glob.moveslow.sxx.src = glob.moveslow.e1.src;
      glob.moveslow.e1.src = prop.Image.base + prop.Image.x;
    }
    glob.moveslow.state = 'showStep';
    glob.moveslow.tid = setTimeout("arimaa.showMoveSlowDo()", 1);
    return;
  }
  if (glob.moveslow.state == 'showStep'){
    if (glob.moveslow.type == 'appear'){
      glob.moveslow.state = 'pauseAfterStep';
    }
    if (glob.moveslow.type == 'disappear'){

      if (parseInt(glob.moveslow.sxx.style.width) > 4){
        glob.moveslow.sxx.style.width = parseInt(glob.moveslow.sxx.style.width)-2;
        glob.moveslow.sxx.style.height = parseInt(glob.moveslow.sxx.style.height)-2;
        glob.moveslow.sxx.style.left = parseInt(glob.moveslow.sxx.style.left)+1;
        glob.moveslow.sxx.style.top = parseInt(glob.moveslow.sxx.style.top)+1;
        
      }
      else{
        glob.moveslow.sxx.src = prop.Image.base + prop.Image.x;
        glob.moveslow.sxx.style.left = -40;
        glob.moveslow.sxx.style.top = -40;
        glob.moveslow.sxx.style.width = prop.PieceSize.W;
        glob.moveslow.sxx.style.height = prop.PieceSize.H;

        glob.moveslow.state = 'pauseAfterStep';
      }
    }
    if (glob.moveslow.type == 'move'){
      mx = parseInt(glob.moveslow.sxx.style.left);
      my = parseInt(glob.moveslow.sxx.style.top);
      moved = 0;
      if ((glob.moveslow.x2-mx)*glob.moveslow.dx > 0){
        glob.moveslow.sxx.style.left = mx + glob.moveslow.dx*glob.moveslow.curspeed;
        moved = 1;
      }
      else{
        glob.moveslow.sxx.style.left = glob.moveslow.x2;
      }
      if ((glob.moveslow.y2-my)*glob.moveslow.dy > 0){
        glob.moveslow.sxx.style.top = my + glob.moveslow.dy*glob.moveslow.curspeed;
        moved = 1;
      }
      else{
        glob.moveslow.sxx.style.top = glob.moveslow.y2;
      }
      if (glob.moveslow.curspeed < glob.moveslow.maxspeed){
        glob.moveslow.curspeed += 1;
      }
      if (! moved){
        glob.moveslow.e2.src = glob.moveslow.sxx.src;
        glob.moveslow.sxx.src = prop.Image.base + prop.Image.x;
        glob.moveslow.sxx.style.left = -40;
        glob.moveslow.sxx.style.top = -40;
        glob.moveslow.state = 'pauseAfterStep';


      }
      else{
        if (glob.mute == 0){
          if ((glob.curSound != '') && (glob.curSound != 'step')){
//            soundManager.stop(glob.curSound);
            glob.curSound = '';
          }
          if (glob.curSound != 'step'){
            soundManager.play2('step');
            glob.curSound = 'step';
          }
        }
      }
    }
    if (glob.moveslow.state != 'pauseAfterStep'){
      glob.moveslow.tid = setTimeout("arimaa.showMoveSlowDo()", glob.moveslow.rate);
    }
  }
  if (glob.moveslow.state == 'pauseAfterStep'){
    glob.moveslow.state = 'getStep';
    if (glob.moveslow.type == 'appear'){
      glob.moveslow.tid = setTimeout("arimaa.showMoveSlowDo()", glob.moveslow.pauseafterstep/4);
    }
    else{
      glob.moveslow.tid = setTimeout("arimaa.showMoveSlowDo()", glob.moveslow.pauseafterstep);
    }
  }
}

function stopMoveSlow(){
  if (glob.moveslow.state == 'done'){ return; }
  if ((glob.moveslow.tid != null) && (glob.moveslow.tid != 0)){
    clearTimeout(glob.moveslow.tid);
    glob.moveslow.tid = 0;
  }
  glob.moveslow.state = 'done';
  glob.moveslow.sxx.src = prop.Image.base + prop.Image.x;
  glob.moveslow.sxx.style.left = -40;
  glob.moveslow.sxx.style.top = -40;
//  showPosList(glob.curpos);
  goPosList(glob.curpos);
}

function showNextMove(){
  stopMoveSlow();
//  if (showPosList(glob.curpos + 1)){ glob.curpos += 1; }
  goPosList("1+");
}

function showPrevMove(){
  if (glob.moveslow.state != 'done'){ stopMoveSlow(); return; }
//  if (showPosList(glob.curpos - 1)){ glob.curpos -= 1; }
  goPosList("1-");
}

function showFirstMove(){
  stopMoveSlow();
  goPosList(0);
}

function showLastMove(){
  stopMoveSlow();
  goPosList(-1);
}

function goPosList(pi){
  var pl = glob.poslist;
  var pi2;

  pi2 = "" + pi; // make sure it is a string
  if (pi2.match('[\+]$')){
     pi = glob.curpos + parseInt(pi2);
  }
  if (pi2.match('[\-]$')){
     pi = glob.curpos - parseInt(pi2);
     if (pi<0){ pi = 0; }
  }
  if (pi<0){ pi = pl.length + pi; }
  if (pi>=pl.length){ pi = pl.length-1; }
  if (pi<0){ pi = 0; }
  glob.curpos = pi;
//uiClearArrows();
  showPosList(pi);
  highlightMoveList(pi);
  highlightTurnIndicator(pi);
  uiUpdateStepsLeft(pi);
  if (glob.mode == 'plan'){
    if (vars.timeused.length>0){
      setUsedResvTime(pi);
    }
  }

/*
// show the plan buttons only if the curpos == planIndex
  if (glob.curpos >= glob.realIndex){
    uiShowPlanButtons();
  }
  else{
    uiHidePlanButtons();
  }
*/

}

function uiHidePlanButtons(){
  document.getElementById("buttonGroupSelfPlay").style.visibility = "hidden";
}

function uiShowPlanButtons(){
  document.getElementById("buttonGroupSelfPlay").style.visibility = "visible";
}

function uiShowRated(){
  document.getElementById("ratedGame").innerHTML = vars.rated;
  if (vars.rated == 'R'){
    document.getElementById("ratedGame").title = "Rated game";
  }
  if (vars.rated == 'U'){
    document.getElementById("ratedGame").title = "Unrated game";
  }
}

function uiShowSendButton(){
  if (glob.mode == 'plan'){
    document.getElementById("buttonGroupNet").style.visibility = "hidden";
    return;
  }
  if (glob.myrole == 'v'){
    document.getElementById("buttonGroupNet").style.visibility = "hidden";
//    if ((glob.expert != 1) || (glob.curpos < glob.realIndex)){
    if (glob.expert != 1){
      document.getElementById("buttonGroupSelfPlay").style.visibility = "hidden";
    }
    else{
      document.getElementById("buttonGroupSelfPlay").style.visibility = "visible";
    }
    return;
  }
  if (glob.expert != 1){
    document.getElementById("btnPass").style.visibility = "hidden";
  }
  else{
    document.getElementById("btnPass").style.visibility = "visible";
  }
  if (glob.curpos >= glob.realIndex){
//    document.getElementById("buttonGroupSelfPlay").style.visibility = "visible";
  }
  else{
//    document.getElementById("buttonGroupSelfPlay").style.visibility = "hidden";
  }
  if (vars.result == ''){
    document.getElementById("buttonGroupNet").style.visibility = "visible";
    document.getElementById("buttonGroupSelfPlay").style.visibility = "visible";
  }
  else{
    document.getElementById("buttonGroupNet").style.visibility = "hidden";
    document.getElementById("buttonGroupSelfPlay").style.visibility = "hidden";
    return;
  }
  if (glob.gamestarted){
    document.getElementById("sendBtn").style.backgroundColor = "#ffffff";
    if (glob.turn == glob.myrole){
      document.getElementById("sendBtn").value = "Send";
      document.getElementById("sendBtn").title = "your turn";
      document.getElementById("sendBtn").style.backgroundColor = "#aaffaa";
    }
    else{
      document.getElementById("sendBtn").value = "Waiting";
      document.getElementById("sendBtn").title = "for opponent to move";
      setTip('Waiting for opponent to move.', 2);
    }
  }
  else{
    if (glob.canstart == 1){ 
      if (glob.turn == glob.myrole){
        document.getElementById("sendBtn").value = "Start";
        document.getElementById("sendBtn").title = "begin game";
        document.getElementById("sendBtn").style.backgroundColor = "#aaffaa";
        setTip('Click the [Start] button to begin the game.', 5);
      }
      else{
        document.getElementById("sendBtn").value = "Waiting";
        document.getElementById("sendBtn").title = "for opponent to start game";
        setTip('Waiting for opponent to start game.', 5);
      }
    }
    else{
      document.getElementById("sendBtn").value = "Waiting"; // for opponent to come
      document.getElementById("sendBtn").title = "for opponent game to start";
      setTip('Waiting for opponent to start game.', 5);
    }
  }
}

function uiClearChatIn(){
  document.getElementById("chat").value = "";
}


function setUsedResvTime(pi){
  var move=[], resv=[];

//alert('setting times');
  move[0] = document.getElementById(glob.w.disp + "Move");
  resv[0] = document.getElementById(glob.w.disp + "Resv");
  move[1] = document.getElementById(glob.b.disp + "Move");
  resv[1] = document.getElementById(glob.b.disp + "Resv");
  s = pi%2; // 0 = w, 1 = b
  o = 1 - s;
  
  if (pi==0){
    move[s].innerHTML = sec2str(glob.tc.m);
    resv[s].innerHTML = sec2str(glob.tc.r);
    move[o].innerHTML = sec2str(glob.tc.m);
    resv[o].innerHTML = sec2str(glob.tc.r);
  }
  if (pi>0){
    move[o].innerHTML = sec2str(glob.timeused[pi-1]);
    resv[o].innerHTML = sec2str(glob.timeresv[pi-1]);
    move[s].innerHTML = sec2str(glob.tc.m);
    if (pi>1){
      resv[s].innerHTML = sec2str(glob.timeresv[pi-2]);
    }
    else{
      resv[s].innerHTML = sec2str(glob.tc.r);
    }
  }
  move[o].style.color = 'white';
  resv[o].style.color = '#99ff99';
  move[s].style.color = '#99ff99';
  resv[s].style.color = '#99ff99';
}

function uiStartTimers(){
  uiShowTimers();
  uiStopTimers();
  if (vars.result != ''){ return; }
  if (glob.mode != 'live'){ return; }
  glob.showTimers = setInterval('arimaa.uiShowTimers()', 1000);
}

function uiStopTimers(){
  if (glob.showTimers == undefined){ return; }
  if (glob.showTimers){
    clearInterval(glob.showTimers);
  }
  glob.showTimers = undefined;
}

function uiShowTimers(){
  if (glob.turn == undefined){ return; }
  if (vars.result != ''){ uiStopTimers(); return; }
//  var now = new int(new Date().time/1000);
//  var now = new Date();
  var now = Math.floor(new Date().getTime()/1000);
//log('setInt '+vars.result+'  '+glob.turn+'  '+now);
  var M = glob.tcmove;
  if (M == undefined){ M = 0; }
  var T = glob.tcturntime;
  if (T == undefined){ T = 0; }
  var R = 0;
  var R2 = 0;
  var S = 0;
  var oR = 0;
  var oR2 = 0;
  if (vars.turn == 'w'){
    R = glob.tcwreserve;
    R2 = glob.tcwreserve2;
    S = glob.wstartmove;
    oR = glob.tcbreserve;
    oR2 = glob.tcbreserve2;
  }
  if (vars.turn == 'b'){
    R = glob.tcbreserve;
    R2 = glob.tcbreserve2;
    S = glob.bstartmove;
    oR = glob.tcwreserve;
    oR2 = glob.tcwreserve2;
  }
  if (R==undefined){ R = 0; }
  if (R2==undefined){ R2 = 0; }
  if (S==undefined){ S = 0; }
  if (oR==undefined){ oR = 0; }
  if (oR2==undefined){ oR2 = 0; }
  var u = now - glob.timediff - S;
  if (u == undefined){ u = 0; }
  var m = M - u;
  if (m<0){ m = 0; }
  var r = R;
  var l = M+R - u;
  if (m<=0){ r = l; R2 = M+R2 - u; }
  var t = T - u;  
  if (t<0){ t=0; }

  if (T>0){
    if (T<M){
      m = t; l = t; r = 0;
    }
    if (T < M+R){
      l = t; r = T-M;
      if (m <= 0){
        r = t;
      }
    }
  }
  if (r < 0){ r = 0; }
  if (R2 < 0){ R2 = 0; }

//  var col = 0x99ff99;
  var col = "#99ff99";
  if (l < 30){ col = "#ffff99"; }
  if (l < 10){ col = "#ff9999"; }
// Make the clock ticking sound
  if (glob.turn == glob.myrole){
    if ((l==10) || (l==20) || (l==30)){
      soundManager.play2('clock');
    }
  }

  if ((glob.tcmove==0) &&(glob.tcreserve==0)){
    col = "#99ff99";
    r = 0;
    oR = 0;
  }
  var G = 0;
  var gameLab = "Game Time: unlimited";
  if ((glob.tctotal != undefined) && (glob.tctotal>0)){
    G = sec2str(glob.tctotal - glob.tcgamenow - u);
    gameLab = "GTL: "+G;
  }
  if ((glob.tcturns != undefined) && (glob.tcturns>0)){
    G = glob.tcturns - glob.plycount;
    gameLab = "Turns Left: "+G;
  }

  document.getElementById("gameTime").innerHTML = gameLab;

  var myMove = document.getElementById(glob.w.disp + "Move");
  var myResv = document.getElementById(glob.w.disp + "Resv");
  var myResv2 = document.getElementById(glob.w.disp + "Resv2");
  var opMove = document.getElementById(glob.b.disp + "Move");
  var opResv = document.getElementById(glob.b.disp + "Resv");
  var opResv2 = document.getElementById(glob.b.disp + "Resv2");

  if (glob.turn == 'b'){
    var myMove = document.getElementById(glob.b.disp + "Move");
    var myResv = document.getElementById(glob.b.disp + "Resv");
    var myResv2 = document.getElementById(glob.b.disp + "Resv2");
    var opMove = document.getElementById(glob.w.disp + "Move");
    var opResv = document.getElementById(glob.w.disp + "Resv");
    var opResv2 = document.getElementById(glob.w.disp + "Resv2");
  }

  opMove.style.backgroundColor = 'black';
  opResv.style.backgroundColor = 'black';
  opMove.style.color = 'white';
  opResv.style.color = 'white';
  opMove.innerHTML = 'Used: '+sec2str(glob.lastmoveused);
  opResv.innerHTML = 'Resv: '+sec2str(oR);
  opResv2.title = 'TotResv: '+sec2str(oR2);

  myMove.style.backgroundColor = 'black';
  myResv.style.backgroundColor = 'black';
  myMove.style.color = 'white';
  myResv.style.color = 'white';
  if (M+R==0){
    myMove.innerHTML = 'Move: '+sec2str(u);
    myMove.style.backgroundColor = col;
    myMove.style.color = 'black';
    myResv.innerHTML = 'Resv: '+sec2str(0);
    myResv2.title = 'TotResv: '+sec2str(R2);
  }
  else{
    if (m > 0){
      myMove.style.backgroundColor = col;
      myMove.style.color = 'black';
    }
    else{
      myResv.style.backgroundColor = col;
      myResv.style.color = 'black';
    }
    myMove.innerHTML = 'Move: '+sec2str(m);
    myResv.innerHTML = 'Resv: '+sec2str(r);
    myResv2.title = 'TotResv: '+sec2str(R2);
  }

}

function sec2str(s){
  var x, r;

  r = '';
  x = Math.floor(s/86400);
  if (x>0){ r += x+'d'; }
  s = s - x*86400;
  x = Math.floor(s/3600);
  if (x>0){ r += x+'h'; }
  s = s - x*3600;
  x = Math.floor(s/60);
  if (x>0){ r += x+'m'; }
  s = s - x*60;
  x = s;
  if (x>0){ r += x+'s'; }
  if (r == ''){ r = '0s'; }
  return r;
}

function highlightTurnIndicator(pi){
//  var wdisp = document.getElementById(glob.w.disp + "PlayerInfo2");
//  var bdisp = document.getElementById(glob.b.disp + "PlayerInfo2");
  var wdisp = document.getElementById(glob.w.disp + "Name");
  var bdisp = document.getElementById(glob.b.disp + "Name");

//  if (glob.movelist[pi].match(/^\d+w/)){
  if (getTurnAt(pi) == 'w'){
    bdisp.style.background = "";
//    wdisp.style.background = "#ffbb99";
    wdisp.style.background = "#aaffaa";
  }
  else{
    wdisp.style.background = "";
//    bdisp.style.background = "#ffbb99";
    bdisp.style.background = "#aaffaa";
  }
}

// todo move scroll bar
function highlightMoveList(pi){
  var i;
  var m = document.getElementById('movelist');

  for(i=0;i<m.length;i++){
    m[i].selected = false;
  }
  if (pi<0){ pi = 0; }
  if (pi>=m.length){ pi = m.length - 1; }
  m[pi].selected = true;

// position the scroll bar so that the move is visible
  var sh, st, mh, ms, sb, hp;
  if (m.length > 0){
    sh = m.scrollHeight;
    st = m.scrollTop;
    ms = m.size;
    mh = sh/m.length;
    sb = st + Math.floor(ms*mh);
    hp = Math.round(pi*mh);
    if (hp<=st){
      m.scrollTop = hp;
    }
    if (hp>=sb){
      m.scrollTop = hp;
    }
//alert(hp+' '+st+' '+sb+' '+mh+' '+sh+' '+m.length);
  }
  

}

function showPosList(pi){
  var x = [];
  var p, b, i, img, np;

  if (pi>=glob.movelist.length){ return 0; }
  if (pi<0){ return 0; }
  p = getPosAt(pi);
  if (pi == glob.movelist.length-1){
    p = makeMove(p, getMoveAt(pi));
  }
  showPosition(p);
  showCaptured(p);
  return 1;
}

// given a position string show it on the board
function showPosition(p){
  var x, i;
  p = p.replace(/\d+\w +/, '');
  x = p.split('');
  x = viewFrom(glob.viewfrom, x);
  for(i=0;i<x.length;i++){
    setPieceAt(x[i], i);
  }
  glob.arrows = [];
}

// This shows the position along with any steps taken at the given index
//   also show the captured pieces
// But if nosteps is true then shows the position before any steps were taken
function showPositionAt(pi, nosteps){
  var np = getPosAt(pi);
  if (! nosteps){
    np = makeMove(np, getMoveAt(pi))
  }
  showPosition(np);
  showCaptured(np);
}

// given the square index and piece image character like '.', E, r, etc
//   set the image on the square
function setPieceAt(c, sq){
  var b = prop.Image.base;
  var cs = 's' + i2cord(sq, 1);
  var img;

  if (c == '.'){
    document.getElementById(cs).src = prop.Image.base+prop.Image.x;
  }
  else{
    img = eval("prop.Image."+c);
    document.getElementById(cs).src = prop.Image.base+img;
  }
}

// given the square index and arrow image character like n, s, e, or w
//   set the image on the square
function setArrowAt(d, sq){
  var cs = 's' + i2cord(sq, 1);
  var t;
  if (d == '.'){
    document.getElementById(cs).src = prop.Image.base+prop.Image.x;
  }
  else{
    t = getTurnAt();
    img = eval("prop.Image."+t+d);
    document.getElementById(cs).src = prop.Image.base+img;
  }
}

function showCapturedAt(pi){
  if (pi<2){ pi=2; }
  var p = makeMove(getPosAt(pi), getMoveAt(pi));
//alert(pi+' '+p);
  showCaptured(p);
}

function showCaptured(p){
  var pip, pipa, sip, sipa;
  var i, j, b, img;

//alert(p);
  pip = p;
  pip = pip.replace(/\d+\w +/, '');
  pip = pip.replace(/\W/g, '');
  pipa = pip.split('');

  sip = getPosAt(2);
  if (sip == ''){
//alert('none');
    sip = "EMHHDDCCRRRRRRRR.emhhddccrrrrrrrr";
  }
  sip = sip.replace(/\d+\w +/, '');
  sip = sip.replace(/\W/g, '');
  for(i=0;i<pipa.length;i++){
    sip = sip.replace(pipa[i], '');
  }
  pip = sip;
  pip = pip.replace(/[a-z]/g, ''); // has the gold pieces
  sip = sip.replace(/[A-Z]/g, ''); // has the silver pieces

  pip = let2num(pip);
  pipa = pip.split('');
  pipa = pipa.sort().reverse();
  pip = pipa.join('');
  pip = num2let(pip);
  pip = pip.toUpperCase();
  pipa = pip.split('');

  sip = let2num(sip);
  sipa = sip.split('');
  sipa = sipa.sort().reverse();
  sip = sipa.join('');
  sip = num2let(sip);
  sipa = sip.split('');

  b = prop.Image.base;
  for(i=0;i<16;i++){
    j = 'cap_w'+(i+1);
    if (i<pipa.length){
      img = eval("prop.Image."+pipa[i]);
      document.getElementById(j).src = b+img;
    }
    else{
      document.getElementById(j).src = b+prop.Image.x;
    }
    j = 'cap_b'+(i+1);
    if (i<sipa.length){
      img = eval("prop.Image."+sipa[i]);
      document.getElementById(j).src = b+img;
    }
    else{
      document.getElementById(j).src = b+prop.Image.x;
    }
  }
}

function let2num(s){
	s = s.replace(/e/gi, '6');
	s = s.replace(/m/gi, '5');
	s = s.replace(/h/gi, '4');
	s = s.replace(/d/gi, '3');
	s = s.replace(/c/gi, '2');
	s = s.replace(/r/gi, '1');
	return s;
}

function num2let(s){
	s = s.replace(/6/g, 'e');
	s = s.replace(/5/g, 'm');
	s = s.replace(/4/g, 'h');
	s = s.replace(/3/g, 'd');
	s = s.replace(/2/g, 'c');
	s = s.replace(/1/g, 'r');
	return s;
}



function viewFrom(v, p){
  var x, y, z, i, j;
  var a = [];

  if (v == 'w'){ return p; }
  if (v == 'b'){ return p.reverse(); }
  if (v == 'n'){ 
// side neutral view; gold on left silver on right
    for(i=0;i<64;i++){
      x = i%8;
      y = Math.floor(i/8);
      z = x; x = y; y = z; y = 7-y;
      j = 8*y + x;
      a[j] = p[i];
    }
    return a; 
  } 
  return p;
}

function coreViewMap(i){
  return viewMap(glob.viewfrom, i);
}

// given a square index return the index that it maps to based on the view
function viewMap(v, i){
  var x, y, z, j;
  if ((i<0) || (i>63)){ return i; }
  if (v == 'w'){ return i; }
  if (v == 'b'){ return 63-i; }
  if (v == 'n'){ 
// side neutral view; gold on left silver on right
      x = i%8;
      y = Math.floor(i/8);
      z = x; x = y; y = z; y = 7-y;
      j = 8*y + x;
      return j;
  } 
}

// does the opposite of coreViewMap; specially when view is 'n'
function coreMapView(i){
  var v = glob.viewfrom;
  var x, y, z, j;
  if ((i<0)||(i>63)){ return i; }
  if (v == 'w'){ return i; }
  if (v == 'b'){ return 63-i; }
  if (v == 'n'){ 
// side neutral view; gold on left silver on right
    return(coreViewMap(coreViewMap(coreViewMap(i))));
  } 
}



function coreMapDir(d){
  if (! d.match(/[nsew]/)){ return d; }
//  var v = vars.viewFrom;
  var v = glob.viewfrom;
  if (v == 'w'){ return d; }
  if (v == 'b'){ return oppDir(d); }
  if (v == 'n'){
    switch(d){
      case 'n': return 'e';
      case 's': return 'w';
      case 'e': return 's';
      case 'w': return 'n';
    }
  }
}

function oppDir(d){
  switch(d){
    case 'n': return 's';
    case 's': return 'n';
    case 'e': return 'w';
    case 'w': return 'e';
  }
  return d;
}



// copy the move in glob.movelist[i] to the visual move list
function uiUpdateMoveList(i){
  var vml = document.getElementById("movelist"); // visual move list
// make sure only to do the [w,b]=>[g,s] replacement within the move number at the start of the line and not in the steps of the move
//  vml.options[i].text = glob.movelist[i].replace(/w/,'g').replace(/b/,'s');
  vml.options[i].text = glob.movelist[i].replace(/^(\d+)w/,'$1g').replace(/^(\d+)b/,'$1s');
  uiUpdateStepsLeft(i);
}

// remove the last item from the visual movelist
function uiRemoveMoveList(i){
  var vml = document.getElementById("movelist"); // visual move list
  if (i == undefined){ i = vml.length -1; }
  vml.remove(i);
}

// remove all items in the visual movelist
function uiClearMoveList(){
  var i;
  var vml = document.getElementById("movelist"); // visual move list
  for(i=vml.length-1;i>=0;i--){
    vml.remove(i);
  }
}


function makeMove(p, m){
  var s = [];
  var a = [];
  var x = [];
  var i, n, n2;

  if ((p==null) || (p == '')){ return; }
  if ((m==null) || (m == '')){ return p; }
  p = p.replace(/^\d+\w +/, '');
  if (p.length != 64){ return; }
  m = m.replace(/^\d+\w +/, '');
//alert("'"+p+"'"+' '+m + ' ' );
  s = m.split(/ +/);
  x = p.split('');
  for(i=0;i<s.length;i++){
    a = stepInfo(s[i]);
    if (a == null){ continue; }
//alert(x[0]);
    n = cord2i(a[2]);
    if (n < 0){ continue; }
    if (a[3] == ''){
//alert(s[i] + ' . '+a[1]+' . '+a[2]+' . '+a[3]);
      x[n] = a[1];
    }
    else{
      n2 = dir2i(n, a[3]);
      x[n2] = a[1];
      x[n] = '.';
    }
  }
  return x.join('');
}

function dir2i(n, d){
  d = d.toLowerCase();
  if (d == 'e'){
    if (n%8 == 7){ return n; } // cant move east off the board
    return n+1;
  }
  if (d == 'w'){
    if (n%8 == 0){ return n; } // cant move west off the board
    return n-1;
  }
  if (d == 'n'){
    if (Math.floor(n/8) >= 7){ return n; } // cant move north off the board
    return n+8;
  }
  if (d == 's'){
    if (Math.floor(n/8) <= 0){ return n; } // cant move south off the board
    return n-8;
  }
  if (d == 'x'){
    return n;
  }
  return n; 
}

function cord2i(s){
  var a = [];
  var n;
  s = s.toLowerCase();
  a = s.match(/([a-h])([1-8])/);
  if (a.length < 2){ return -1; }
  n = a[1].charCodeAt(0) - 'a'.charCodeAt(0);
  n += 8*(parseInt(a[2])-1);
  return n;
}

// not yet tested
// if the r flag is set return something like '77' else 'h8'
function i2cord(n, r){
  var s;
  var x, y;
  y = Math.floor(n/8);
  x = n%8;
  if (r){ return ''+x+y; }
  s = String.fromCharCode('a'.charCodeAt(0) + x);
  s = '' + s + (y+1);
  return s;
}

function nextMoveCount(m){
  var a = [];
  var n;
  n = moveCount2Num(m);
  n = n + 1;
  return num2MoveCount(n);
}

function prevMoveCount(m){
  var a = [];
  var n;
  n = moveCount2Num(m);
  n = n - 1;
  if (n<0){ n=0; }
  return num2MoveCount(n);
}

// this is being called from arimaaClient.htm
function changeViewAuto(tb, lr){
  var vf = glob.viewfrom;
  var w = glob.w.disp;

//  vf = w b n
//  w = top bot
  if (vf == 'n'){
    if (lr == 'left'){ changeView('w'); }
    if (lr == 'right'){ changeView('b'); }
    return;
  }
  if (tb == 'top'){
    if (vf == 'w'){ changeView('b'); }
    if (vf == 'b'){ changeView('w'); }
  }
  if (tb == 'bot'){
    changeView('n');
  }
}

function changeView(v){
  if (glob.viewfrom == 'w'){ glob.viewfrom = 'b'; }
  else{
    if (glob.viewfrom == 'b'){ glob.viewfrom = 'n'; }
    else{
      if (glob.viewfrom == 'n'){ glob.viewfrom = 'w'; }
      else{ glob.viewfrom = 'w'; }
    }
  }
  if (v != null){
    glob.viewfrom = v;
  }
  stopMoveSlow();
  showPosList(glob.curpos);
//  goPosList(glob.curpos);
  if (glob.viewfrom == 'w'){
    document.getElementById("boardtable").style.backgroundImage = "url("+prop.Image.base+prop.Image.board_w+")";
    glob.w.disp = "bot";
    glob.b.disp = "top";
  }
  if (glob.viewfrom == 'b'){
    document.getElementById("boardtable").style.backgroundImage = "url("+prop.Image.base+prop.Image.board_b+")";
    glob.b.disp = "bot";
    glob.w.disp = "top";
  }
  if (glob.viewfrom == 'n'){
//    document.getElementById("boardtable").style.backgroundImage = prop.Image.base+prop.Image.board_n;
    document.getElementById("boardtable").style.backgroundImage = "url("+prop.Image.base+prop.Image.board_n+")";
    glob.w.disp = "bot";
    glob.b.disp = "top";
    if (glob.myrole == 'b'){
      glob.b.disp = "bot";
      glob.w.disp = "top";
    }
  }
  showPlayerInfo();
  highlightTurnIndicator(glob.curpos);
  document.getElementById("buttonV").value = "V"+glob.viewfrom;
}

function changeShowSpeed(s){
  glob.moveslow.speed += 1;
  if (s != null){
    glob.moveslow.speed = s;
  }
  if (glob.moveslow.speed > 4){ glob.moveslow.speed = 1; }
  document.getElementById("buttonS").value = "S"+glob.moveslow.speed;
}

function changeMute(s){
  if (glob.sm2Loaded == undefined){
//    arimaa.initSound();
  }
  glob.mute += 1;
//alert(glob.mute);
  if (s != null){
    glob.mute = s;
  }
  if (glob.mute > 1){ glob.mute = 0; }
  if (glob.mute == 0){
    document.getElementById("buttonM").value = "<)) on";
    soundManager.unmute();
  }
  if (glob.mute == 1){
    document.getElementById("buttonM").value = "<)) off";
    soundManager.mute();
  }
}

// expert mode on means that undoStep, redoStep, undoMove, passSteps
//   can be done from the board; otherwise buttons have to be used.
// expert mode on means that planning can be done for both sides
//   even in a live game window. planning for both sides can always
//   be done in a plan window.
function changeExpert(s){
  glob.expert += 1;
  if (s != null){
    glob.expert = s;
  }
  if (glob.expert > 1){ glob.expert = 0; }
  if (glob.expert == 0){
    document.getElementById("buttonE").value = "Exp off";
    document.getElementById("btnPass").style.visibility = "hidden";
  }
  if (glob.expert == 1){
    document.getElementById("buttonE").value = "Exp on";
    document.getElementById("btnPass").style.visibility = "visible";
//    if (glob.experthelp == undefined){
//    if (glob.mode == 'live'){
// use a session cookie so that it also effects the plan window opened seperately
    glob.experthelp = getCookie('ajsc_experthelp');
    if ((glob.experthelp == undefined) || (glob.experthelp == '')){
        alert("You can now move both sides to plan ahead\nClick on a piece to undo step\nClick on empty square to redo step\nClick on trap to undo move\nClick on opposing piece to pass remaining steps");
        glob.experthelp = 1;
        setCookie('ajsc_experthelp', '1');
//    }
    }
  }
  if (glob.myrole == 'v'){
    uiShowSendButton();
  }
}

function openMoveListWindow(){
  //var mywin = open('','','status=no,toolbar=no,menubar=no,location=no,directories=no');
  var mywin = open('','','');

  var pa = getPosAt(glob.curpos).split('');
  var not = getSetupNotation(pa, 'w');
  not += ' ' + getSetupNotation(pa, 'b');

  mywin.document.open();
  mywin.document.write("This position can be <a href='/arimaa/notconv/old/' target=_blank>converted to other formats or images</a>.<br>Just copy and paste it as 'Input' in the <a href='/arimaa/notconv/old/' target=_blank>conversion tool</a>.\n");
  mywin.document.write("<form><textarea cols=80 rows=4>"+not+"\n</textarea></form>");
  mywin.document.write("<form><textarea cols=80 rows=40>"+glob.movelist.join("\n").replace(/\b(\d+)w/g,'$1g').replace(/\b(\d+)b/g,'$1s')+"\n</textarea></form>");
  mywin.document.close();
}

function openLogWindow(){
  var d = logvar;
  //var mywin = open('','','status=no,toolbar=no,menubar=no,location=no,directories=no');
  var mywin = open('','','');

  mywin.document.open();
  mywin.document.write("<form><textarea cols=80 rows=40>"+d+"\n</textarea></form>");
  mywin.document.close();
}

function openVarsWindow(){
  var d =  'glob\n'+obj2json(glob)+'\n\n\nvars\n'+obj2json(vars);
  //var mywin = open('','','status=no,toolbar=no,menubar=no,location=no,directories=no');
  var mywin = open('','','');

  mywin.document.open();
  mywin.document.write("<form><textarea cols=80 rows=40>"+d+"\n</textarea></form>");
  mywin.document.close();
}

function openPlanWindow(){
//  var mywin = open('','','status=no,toolbar=no,menubar=no,location=no,directories=no');

  var mywin = open('','','width=670,height=570');
  var s = '\
<html>\
  <body onload="javascript:arimaa.window2();arimaa.init()"> \
  <div> \
    <script id="arimaa_script" type="text/javascript" src="'+prop.bu+'arimaa.js"></script> \
    <script type="text/javascript">\
arimaa.vars.title="Plan '+vars.title+'";\
arimaa.vars.startmove = "-1"; \
arimaa.vars.planmove = "-1"; \
arimaa.vars.viewfrom = "'+glob.viewfrom+'"; \
arimaa.vars.wplayer = "'+vars.wplayer+'"; \
arimaa.vars.bplayer = "'+vars.bplayer+'"; \
arimaa.vars.wrating = "'+vars.wrating+'"; \
arimaa.vars.brating = "'+vars.brating+'"; \
arimaa.vars.timecontrol = "'+vars.timecontrol+'"; \
arimaa.vars.rated = "'+glob.rated+'"; \
arimaa.vars.expert = "1"; \
arimaa.vars.movelist = "[movelist]";\
\
    </script> \
  </div> \
  <div id="arimaa_win">\
  </div>\
  </body> \
</html> \
';

  var ml = glob.movelist.slice(0, glob.curpos).join('\n');
  ml = ml.replace(/\n/g, "\\n");
  s = s.replace(/\[movelist\]/, ml);

  mywin.document.open();
//  mywin.document.write("<form><textarea cols=80 rows=40>"+glob.movelist.join("\n")+"\n</textarea></form>");
  mywin.document.write(s);
  mywin.document.close();
}

function notUsedAnymore_showMoveList(mla){
  var i;
  var m = document.getElementById("movelist");

  if (mla == null){ mla = glob.movelist; }
  for(i=0;i<mla.length;i++){
    m[i] = new Option(mla[i].replace(/w/,'g').replace(/b/,'s'), "", false);
  }
}


function showPlayerInfo(){
  var name=0; var wl=1; var steps=3; var move=5; var resv=7;

  var wdisp = document.getElementById(glob.w.disp + "PlayerInfo");
  var bdisp = document.getElementById(glob.b.disp + "PlayerInfo");

  document.getElementById(glob.w.disp + "Name").innerHTML = glob.w.name + " (" + glob.w.rating + ")";
  document.getElementById(glob.b.disp + "Name").innerHTML = glob.b.name + " (" + glob.b.rating + ")";
  if ((glob.w.wl != undefined) && (glob.w.wl != '')){
    document.getElementById(glob.w.disp + "WL").innerHTML = glob.w.wl;
    document.getElementById(glob.b.disp + "WL").innerHTML = glob.b.wl;
    document.getElementById(glob.w.disp + "Steps").innerHTML = '';
    document.getElementById(glob.b.disp + "Steps").innerHTML = '';
  }

  if ((glob.viewfrom == 'w') || (glob.viewfrom == 'b')){
    wdisp.align = "center";
    bdisp.align = "center";
  }
  if (glob.viewfrom == 'n'){
    wdisp.align = "left";
    bdisp.align = "right";
  }
}

function log(m){
  var now = Math.floor(new Date().getTime()/1000);
  if (logvar == undefined){ logvar = ''; }
  logvar += ''+now+'\n'+m+'\n\n=====================================================\n\n';
}

function c2i(x, y){
  return y*8+x;
}

function i2c(i){
  var x, y;
  x = i%8;
  y = i/8;
  return [x, y];
}

function getTurnAt(pi){
  if (pi == undefined){ pi = glob.curpos; }
  var p = glob.movelist[pi].replace(/\d+/, '');
  return p.charAt(0);
}

// returns just the turn number; returns 3 for 3w or 3b
function getNumAt(pi){
  if (pi == undefined){ pi = glob.curpos; }
  return glob.movelist[pi].match(/^(\d+)/)[1];
}

// returns the number and turn; like 3w or 3b
function getPlyAt(pi){
  if (pi == undefined){ pi = glob.curpos; }
  return glob.movelist[pi].match(/^(\d+\w)/)[1];
}

// returns 1 if this is a setup position
function getSetupAt(pi){
  if (pi == undefined){ pi = glob.curpos; }
  if (pi==0){ return 1; }
  return (getNumAt(pi)==1)?1:0;
}

// returns the last step of a move; if notrap=1 then trap steps
//   are ignored.
function getLastStepAt(pi, notrap){
  var move, l;
  if (pi == undefined){ pi = glob.curpos; }
  move = getMoveAt(pi);
  if (notrap){
    move  = move.replace(/ +\w+x/,'');
  }
  l = move.match(/(\w+) *$/);
  if (l != null){
    return l[1];
  }
  return '';
}


function getMoveAt(pi){
  return glob.movelist[pi].replace(/^\d+\w */, '');
}

function getPosAt(pi){
  if (pi<0){ return ''; }
  if (pi>=glob.poslist.length){ return ''; }
  return glob.poslist[pi].replace(/^\d+\w */, '');
}

function coreRemoveLastStepAt(pi){
  glob.movelist[pi] = glob.movelist[pi].replace(/ *\w+ *$/, '');
}

// returns -1 if current position is not on last move
//   else returns the current position value (which could be 0)
function isOnLastMove(){
  if (glob.curpos < glob.movelist.length-1){ return -1; }
  return glob.curpos;
}
  
function eventOverSquare(x, y){
  var pi = isOnLastMove();
  if (pi < 0){ return 0; }
  var i = c2i(x, y);
  coreMouseOver(coreMapView(i), pi);
}

// mouse is over square i and position index is pi
function coreMouseOver(i, pi){
  var a, x, turn, setup, m, ma, pc, pts, pos, pa, sj, ej, j, ps, p;
  var frozen, unfrozen, sqto, d, pushable, pullable, lastStep, si;
  var dir = ['n', 's', 'e', 'w'];

  if (! canMovePieces()){ return; }
  if (getSetupAt(pi)){ setupMouseOver(i, pi); return; };
  a = [];
  for(x=0;x<64;x++){
    a.push('.');
  }
  if (glob.arrows == undefined){
    glob.arrows = [];
  }
  if (i<0){
alert('i < 0 in coreMouseOver '+i);
    uiShowArrows(a);
    return;
  }
  turn = getTurnAt(pi);

// If expert mode is off then we don't allow planning of opponents move
//   this also prevents spectators from moving pieces unless they
//   turn on expert mode.
/*
  if (glob.expert == 0){
    if (glob.role != turn){ 
      uiClearArrows();
      return; 
    }
  }
*/


  ps = getPosAt(pi);
  ps = makeMove(ps, getMoveAt(pi));
  p = ps.split('');
//alert(i+' '+turn+' '+p[i]);

  if (p[i] == '.'){
    if (glob.arrows[i] == '.'){
      uiShowArrows(a);
    }
    return;
  }

//######################################################
// Logic to determine where piece can move; defined by Arimaa core rules
//######################################################
  if (pieceColor(p[i]) == turn){
    // our own piece
    sqto = [];
    frozen = 0;
    unfrozen = 0;
    for (d in dir){
      d = dir[d];
      j = dir2i(i, d);
      if (j==i){ continue; }
      if (p[j]=='.'){ 
        // rabbits can't move backwards
        if ((p[i]=='R')&&(d=='s')){ continue; }
        if ((p[i]=='r')&&(d=='n')){ continue; }
        a[j] = d; sqto.push(j); 
      }
      else{
        if ((pieceColor(p[j]) != turn) && (isStronger(p[j],p[i]))){ frozen = 1; }
        if (pieceColor(p[j]) == turn){ unfrozen = 1; }
      }
    }


    // are we frozen
    if ((frozen==1)&&(unfrozen==0)){
      for (j in sqto){ a[sqto[j]] = '.'; }
    }
    // have we started a push
    lastStep = getPushStep(getMoveAt(pi), turn);
    if (lastStep != null){
      si = step2Index(lastStep);
      for (j in sqto){
        if ((sqto[j] != si[1]) || (! isStronger(p[i],si[0]))){ a[sqto[j]] = '.'; }
      }
    }
  }
  if (pieceColor(p[i]) != turn){
    // opponents piece
    sqto = [];
    pushable = 0;
    for (d in dir){
      d = dir[d];
      j = dir2i(i, d);
      if (j==i){ continue; }
      if (p[j]=='.'){ a[j] = d; sqto.push(j); }
      else{
        if ((pieceColor(p[j]) == turn) && (isStronger(p[j],p[i]))){
          if (! isFrozen(p, j)){
            pushable = 1; 
          }
        }
      }
    }
    var move = getMoveAt(pi);
    var nmove = move.replace(/ +\w+x/,'');
    if (stepsInMove(move)>=3){ pushable = 0; }
    if (getPushStep(move, turn) != null){ pushable = 0; } // already started a push
    if (! pushable){
      // is it pullable
      pullable = 0;
      lastStep = nmove.match(/(\w+) *$/);
//alert('lastStep is '+lastStep,+' move is '+move);
      if (lastStep != null){
        si = step2Index(lastStep[1]);
        if ((pieceColor(si[0]) == turn) && (si[1] >= 0) && (isStronger(si[0],p[i]))){
          if (getPushStep(move.replace(/\w+ *$/,''),turn) == null){ // did not push
            pullable = 1;
            for (j in sqto){
              j = sqto[j];
              if (j != si[1]){ a[j] = '.'; }
            }
          }
        }
      }    
    }
    // it is not pushable or pullable
    if ((pushable==0)&&(pullable==0)){
      for (j in sqto){ a[sqto[j]] = '.'; }
    }


  }

//  glob.arrows = a;
//  uiSetArrows(a);
  uiShowArrows(a);
  return;

}



function eventClickSquare(x, y){
  var i = c2i(x, y);
  var pi = glob.curpos;
  var li = glob.movelist.length;

  if (pi < li-1){ return 0; }
//alert(pi+' - '+ li);

  coreMouseClick(coreMapView(i), pi);
// this is needed to get the arrows to auto show after a piece takes a step
eventOverSquare(x, y);
}

function coreMouseClick(si, pi){
  var stepsTaken, d, ps, p, opd, fi, ms, np, pts, turn, setup, rab, j, sj, ej, sp, want, aa;
  var trap, tpc, safe, ads, dir, npa, repeat, ply, move, pmove;
  var traps = [18,21,42,45];
  var dirs = ['n', 's', 'e', 'w'];

// first determine why we are here; user either clicked on:
//    arrow - taking step
//    piece (our) - undo step
//    piece (opp) - skip steps
//    space - redo step
//    trap - undo move

  if (! canMovePieces()){ return; }
  if (getSetupAt(pi)){ setupMouseClick(si, pi); return; }
  turn = getTurnAt(pi);
  setup = getSetupAt(pi);
  ps = makeMove(getPosAt(pi), getMoveAt(pi));
  p = ps.split('');
  if (p[si] != '.'){ 
// clicked on a piece
    want = 'undo'; 
    if (pieceColor(p[si]) != turn){
      want = 'skip'; 
      if (getMoveAt(pi) == ''){
        want = 'undo';
      }
    }
    if ((vars.compose)&&(setup)){ want = 'undo'; }
  }
  else{
// click on a space
    if (!glob.arrows || glob.arrows.length<64){ 
// arrows are no on
      d = '.';
    }
    else{
      d = glob.arrows[si];
    }
    if (d == '.'){ 
// space has no arrow
      want = 'redo'; 
      if ((si==18) || (si==21) || (si==42) || (si==45)){
        want = 'unmove';
      }
    }
    else{ 
// space has an arrow
      want = 'step';
    }
  }  

//alert('want is '+want);

  if ((glob.expert == 0) && (want != 'step')){ return; }
  
  switch(want){
    case 'undo':{
      if (setup){
        if (pieceColor(p[si]) == turn){
// clicked on our own piece
          if (! p[si].match(/r/i)){
// clicked on a non-rabbit piece
//   make the step of the piece that was clicked on be the last step of the move
//            ms = p[si]+i2cord(coreViewMap(coreViewMap(coreViewMap(si))),0);
            ms = p[si]+i2cord(si,0);
            glob.move[pi] = glob.move[pi].replace(RegExp(ms), '').replace(/ +/, ' ');
            glob.move[pi] += ' '+ms;
//            uiUpdateMoveList(pi);
//            glob.setupPieces = p[si]+glob.setupPieces;
          }
        }
      }
      ply = getPlyAt(pi);
      move = getMoveAt(pi);
//      if ((glob.num[pi].match(/^(2w|1b)/))&&(glob.move[pi]=='')){
      if ((ply=='2w' || ply=='1b') && (move=='')){
// if there are no steps to undo on move 2w or 1b then user wants to undo a step from the setup
        if (pieceColor(p[si]) != turn){
// clicked on a piece of the other color
          if (! p[si].match(/r/i)){
// clicked on a non-rabbit piece
//   make the step of the piece that was clicked on be the last step of the move
//            ms = p[si]+i2cord(coreViewMap(coreViewMap(coreViewMap(si))),0);
            ms = p[si]+i2cord(si,0);
            glob.move[pi-1] = glob.move[pi-1].replace(RegExp(ms), '').replace(/ +/, ' ');
            glob.move[pi-1] += ' '+ms;
          }
        }
      }
      coreUnStep(pi);
      break;
    }
    case 'redo':{
      coreReStep(pi);
      break;
    }
    case 'skip':{
      eventPassSteps();
      break;
    }
    case 'unmove':{
      eventUndoMove();
      break;
    }
    case 'step':{
/* no longer used since we do setup differently now
      if (setup == 1){
        if ((glob.setupTurn == null) || (glob.setupTurn != turn)){ coreMouseOver(si, pi); }
//        uiClearArrows();
        uiShowPieceToSet('.');
        sp = glob.setupPieces;
        if (sp == ''){ 
          uiDebug("error: clicked during setup turn, but no piece to setup");
          return; 
        }
//trace(si);
        pts = sp.split('');
        uiPieceAt(pts[0], si);
//        ms = pts[0]+i2cord(coreViewMap(coreViewMap(coreViewMap(si))),0);
        ms = pts[0]+i2cord(si,0);
        addStep(ms, pi);
        glob.setupPieces = glob.setupPieces.replace(RegExp(pts[0]), '');
        np = makeMove(glob.pos[pi], glob.move[pi]);
//        showPosition(np);
//        showCaptured(pi);
        showPositionAt(pi);
        if (glob.setupPieces == ''){
          glob.setupTurn = '';
//          if (glob.mode == 'live'){ return; }
          if (vars.compose<1){
            placeRabbits(pi);
            np = makeMove(glob.pos[pi], glob.move[pi]);
            showPositionAt(pi);
            uiGiveTips();
          }
          glob.pos.push(np);
          glob.move.push('');
          glob.num.push(nextMoveCount(glob.num[pi]));
          uiAddMoveList();
          uiMoveListHighlight(pi+1);
          uiShowLabels(pi+1);
        }
        return;
      }
*/


      opd = oppDir(d);
      fi = dir2i(si, opd);
      if (p[fi] == '.'){ return; }
      ms = p[fi]+i2cord(fi,0)+d;
//alert('si='+si+' d='+d+' fi='+fi+' ms='+ms);

      addStep(ms, pi);
//      showPosition(np);
      showPositionAt(pi);

      // check traps to see if any piece should be removed
      np = makeMove(getPosAt(pi), getMoveAt(pi));
      for (trap in traps){
        trap = traps[trap];
        npa = np.split('');
        if (npa[trap] == '.'){ continue; }
        tpc = pieceColor(npa[trap]);
        safe = 0;
        for (dir in dirs){
          dir = dirs[dir];
          ads = dir2i(trap, dir);
          if (pieceColor(npa[ads]) == tpc){ safe = 1; }
        }
        if (safe == 0){
          ms = npa[trap]+i2cord(trap,0)+'x';
          addStep(ms, pi);
//          np = makeMove(getPosAt(pi), getMoveAt(pi));
//          showPosition(np);
          showPositionAt(pi);
//          uiClearArrows();
        }
      }
      
      stepsTaken = stepsInMove(getMoveAt(pi));
      if (stepsTaken>=4){
//alert(stepsTaken);
        // make sure the position is different

        repeat = coreCheckRepeat(pi);
        if (repeat){
//          removeStep(pi);
          coreUnStep();
//          np = makeMove(glob.pos[pi], glob.move[pi]);
//          showPosition(np);
          showPositionAt(pi);
//          uiClearArrows();
        }
        else{
          addToMoveList(getMoveAt(pi), 1); // 1 means it is a plan move
          goPosList(-1); // -1 means to the last position
        }
      }
      break;
    }
  }
}

function setupMouseClick(si, pi){
  var turn, zi, move, set, pos, to;
  turn = getTurnAt(pi);
  zi = (turn=='w')?0:48;  // set first square of setup zone based on turn

  if (glob.setupFrom == null){ glob.setupFrom = ''; }

  to = '';
  pos = makeMove(getPosAt(pi), getMoveAt(pi)).split('');
  if (pieceColor(pos[si]) == turn){
    if (glob.setupFrom == ''){
      glob.setupFrom = i2cord(si);
      return;
    }
    else{
      to = i2cord(si);
      if (to == glob.setupFrom){ return; }
    }
  }
  else{
    glob.setupFrom = '';
  }
  if (to != ''){
    tmp = pos[cord2i(glob.setupFrom)];
    pos[cord2i(glob.setupFrom)] = pos[cord2i(to)];
    pos[cord2i(to)] = tmp;
    move = getSetupNotation(pos, turn);
    clearAllSteps(pi);
    addStep(move, pi, 1); // 1 prevents clearing redo array

//    showPosition(pos.join(''));
    showPositionAt(pi);
    glob.setupFrom = '';

  }
}

function canMovePieces(){
//alert([glob.mode, glob.expert, glob.myrole, glob.turn].join('  '));
//log([vars.result, glob.mode, glob.gamestarted, glob.expert, glob.myrole, glob.turn].join(':'));
  if (vars.result != ''){ return 0; }
  if (glob.mode == 'plan'){ return 1; }
  if (glob.gamestarted == 0){ return 0; }
  if (glob.expert == 1){ return 1; }
// we are in live mode with expert mode off
  if (glob.myrole == 'v'){ return 0; }
//  if (glob.turn == glob.myrole){ return 1; }
  if (getTurnAt() == glob.myrole){ return 1; }
  return 0;
}

function setupMouseOver(si, pi){
  var turn, zi, move, set, pos;


  move = getMoveAt(pi);
  if (move.length == 0){
  turn = getTurnAt(pi);
  zi = (turn=='w')?0:48;  // set first square of setup zone based on turn

/*
// try to build the setup from the redo array
    if (glob.redo == null){ glob.redo = []; }
    if (glob.redo.length>0){
      move = glob.redo.reverse().join(' ');
      move = move.replace(/-.* /, '');
    }
    glob.setupPos = makeMove(getPosAt(pi), move).split('');
*/


//  use a default setup
    pos = makeMove(getPosAt(pi), getMoveAt(pi)).split('');
    set = "rrrrrrrrdhcemchd";
    if (turn == 'w'){ set = set.toUpperCase().split(''); }
    else{ set = set.split('').reverse(); }
    for(i=0;i<16;i++){
      pos[zi+i] = set[i];
    }

    move = getSetupNotation(pos, turn);
    clearAllSteps(pi);
    addStep(move, pi, 1); // 1 prevents clearing redo array

//    showPosition(pos.join(''));
    showPositionAt(pi);
  }
}

function getSetupNotation(pa, turn){
  var i, move;

  move = '';
  for(i=0;i<pa.length;i++){
    if (turn=='w'){
      if (pa[i].match(/[EMHDCR]/)){
        move = move + ' ' + pa[i] + '' + i2cord(i);
      }
    }
    if (turn=='b'){
      if (pa[i].match(/[emhdcr]/)){
        move = move + ' ' + pa[i] + '' + i2cord(i);
      }
    }
  }
  return move;
}


function coreCheckRepeat(pi){
  var np, repCount, repeat;
  var piside, iside, mn;
  
  repeat = 0;
  repCount = 0;
  np = makeMove(getPosAt(pi), getMoveAt(pi));

  if (np == getPosAt(pi)){
    repeat = 1;
    uiHelpMessage("Position has not changed. ");
    return 1;
  }

  mn = '';
//  piside = nextMoveCount(glob.num[pi]).replace(/\d/g,'');
  piside = getTurnAt(pi);
  for(var i=0;i<glob.poslist.length;i++){
    if ((i<pi) && (np == glob.poslist[i])){
//      iside = glob.num[i].replace(/\d/g,'');      
      iside = getTurnAt(i);
      if (iside==piside){
        if ((i>0)&&(glob.movelist[i-1]!='takeback')){
          repCount += 1;
//          mn += ' '+glob.num[i].replace(/w/,'g').replace(/b/,'s');
          mn += ' '+getPlyAt(i).replace(/w/,'g').replace(/b/,'s');
        }
      }
    }
  }

  if (repCount >= 2){
    repeat = 1;
    uiHelpMessage("Position repeats 3 times. Same as: "+mn);
  }
  return repeat;
}

function uiHelpMessage(m){
  alert(m);
}



function eventUndoStep(){
  coreUnStep();
}


// if on a previous move; behave like the 'undo move' button
// if on last move and no steps taken; do nothing
// if on last move and steps have been taken; undo one step
function coreUnStep(){
  var pi, turn, setup, move;

  if (glob.curpos < glob.realIndex){ return; }

// if the curpos is move 1w or 1b then return
  if (getSetupAt()){ eventUndoMove(); return; }

  if (glob.curpos < glob.planIndex){
    eventUndoMove(); // don't do this, it is confusing to the user
    return;
  }
  if (glob.curpos > glob.planIndex){
    return;
  }


  pi = glob.curpos;
  move = getMoveAt(pi);
  if (move == ''){
    if (glob.curpos <= glob.realIndex){ return; }
    glob.redo.push('-');
    removeFromMoveList();
    coreUnStep();
    goPosList(-1);
/*
    while(move == ''){
      if (pi < glob.planIndex){ return; }
      if (pi == 0){ return; }
      glob.movelist.pop();
      glob.poslist.pop();
      uiRemoveMoveList();
      glob.redo.push("-");
      pi = pi - 1;
      move = getMoveAt(pi);
    }
    
    goPosList(pi);

    var stepsTaken = stepsInMove(glob.move[pi]);
    turn = getTurnAt(pi);
    setup = getSetupAt(pi);
    if (setup){
      glob.setupTurn = '';
      if ((glob.setupTurn == null)||(glob.setupTurn != turn)){ coreMouseOver(-1, pi); }
      if (stepsTaken >=16){
        if (vars.compose){ coreUnStep(pi); return; }
// remove all rabbits
        glob.move[pi] = glob.move[pi].replace(/r\w\d/ig,'').replace(/ +/, ' ');
      }
    }    
    if (stepsTaken >= 4){
      coreUnStep(pi);
    }
// maybe we need, not sure; don't detelet it yet
//    coreMouseOver(-1,pi);
*/
    return;
  }
  
  var lastStep = getLastStepAt(pi);
  glob.redo.push(lastStep);
  coreRemoveLastStepAt(pi);
  setup = getSetupAt(pi);
  if (setup){
    glob.setupPieces = lastStep.replace(/\w\d/,'') + glob.setupPieces;
  }
  uiUpdateMoveList(pi);
  showPositionAt(pi);
//  var np = makeMove(getPosAt(pi), getMoveAt(pi));
//  showPosition(np);
//  showCaptured(pi);
// if it was a trap step then we have to undo another step
  if (lastStep.match(/x$/)){
    coreUnStep(pi);
  }
// maybe we don't need this, not sure; don't detelet it yet
//  coreMouseOver(-1,pi);
}


function eventRedoStep(){
  coreReStep();
}


// if on a previous move; first do an 'undo move' then see if it
//   becomes the last move and continue if so
// if on the last move and there are steps to redo; redo one step
//   but don't go into the next move
function coreReStep(){
  var pi, np;

  if (glob.curpos > glob.planIndex){
    return;
  }

// if the curpos is move 1w or 1b then return
  if (getSetupAt()){ eventRedoMove(); return; }

  if (glob.curpos < glob.realIndex){
//    showNextMove(); // don't do this, it is confusing to the user
    return;
  }
  if (glob.curpos < glob.planIndex){
//    eventUndoMove(); // dont do this, it is confusing to the user
    return; // comment this line if you uncomment the above line
  }

  if (glob.redo == null){ return; }
  if (glob.redo.length <= 0){ return; }
  if (glob.redo[glob.redo.length-1] == '-'){ 
// don't go beyond end of move; treat it like redo move click
    eventRedoMove();
    return; 
  } 
  pi = glob.curpos;
  var ms = glob.redo.pop();
/* in case we decide to allow going into the next move
  if (ms == '-'){
    np = makeMove(getPosAt(pi), getMoveAt(pi));

// need to change how we do this
    glob.poslist.push(np);
    glob.movelist.push('');
    glob.stepsTaken = 0;
    glob.num.push(nextMoveCount(glob.num[pi]));
    uiAddMoveList();
    
    uiMoveListHighlight(pi+1);
    uiShowLabels(pi+1);
    coreMouseOver(-1,pi+1);
    if ((vars.compose) && (glob.num[pi] == '1w')){
      coreReStep(pi+1);
      coreMouseOver(-1,pi+2);
    }
    return;
  }
*/

  addStep(ms, pi, 1); // the 1 keeps the redo list safe

  if (getNumAt(pi) == 1){
// if this was a setup move 
    if ((glob.setupPieces != null)&&(glob.setupPieces.length>0)){
      var pts = ms.match(/(\w)/);
      glob.setupPieces = glob.setupPieces.replace(RegExp(pts[1]), '');
      coreMouseOver(-1,pi);
    }
    if (! vars.compose){
// we are not in compose mode
      var tm = glob.move[pi];
      tm = tm.replace(/r\w\d\b/i, '');
      if (stepsInMove(tm) >= 8){
// we placed the last major piece then auto place the rabbits
        placeRabbits(pi);
      }
    }
  }
  
  showPositionAt(pi);  
//  np = makeMove(getPosAt(pi), getMoveAt(pi));
//  showPosition(np);  
//  showCaptured(pi);
  if (glob.redo == null){ return; }
  if (glob.redo.length > 0){
    // if the next step is a piece being removed then it must be done with this step
    if (glob.redo[glob.redo.length-1].match(/x$/)){
      coreReStep(pi);
    }
    if (glob.redo[glob.redo.length-1] == '-'){
/*
      if ((vars.compose) && (glob.num[pi] == '1w')){
        coreReStep(pi);
        return;
      }
*/
      if (stepsInMove(getMoveAt(pi)) >= 4){
// we did a redo of the 4th step so treat as if the redo button was
//   clicked again to go to the next move
        coreReStep();
        return;
      }
    }
  }
}

function eventUndoMove(){
  coreUnMove();
}

// if on the last plan move and it has steps; undo all the steps
// if on the last plan move and it is empty; undo all steps of previous move
// if on a plan move; undo all steps on and after that move
// add a '-' to the redo list to indicate end of move
function coreUnMove(){
  var pi, stepsTaken, lastStep, takeback;
  if (glob.curpos < glob.realIndex){ 
//    showPrevMove();  // don't do this, it is confusing to users
    return 0; 
  }
  pi = isOnLastMove(); // is -1 if not on last move, else index of last move
  if (pi >= 0){
    stepsTaken = stepsInMove(getMoveAt(pi));
    if (stepsTaken > 0){
// remove all the steps on this move
      takeback = 0;
      while(stepsTaken > 0){
        lastStep = getLastStepAt(pi);
        if (lastStep.match(/takeback/)){
          takeback = 1;
        }
        glob.redo.push(lastStep);
        coreRemoveLastStepAt(pi);
        stepsTaken = stepsInMove(getMoveAt(pi));
      }
      uiUpdateMoveList(pi);
      goPosList(-1); // -1 means to the last position
      return;
    }
    else{
      if (glob.curpos > glob.realIndex){
// delete this move and remove all the steps on the prvious line
        glob.redo.push('-');
        removeFromMoveList();
        coreUnMove();
        return;
      }
    }
  }
  else{
// remove the steps on this line and the lines after it
    pi = glob.curpos;
    glob.curpos = glob.planIndex;
    while(glob.curpos>pi){
      coreUnMove();
    }
  }

}

function eventRedoMove(){
  coreReMove();
}

// if on the last plan move and it has steps; redo all step
//   and also redo the change of side if 4 steps taken
// if not on the last plan move behave as if the 'next move' button
//   was pressed and advance the current position to the next move
function coreReMove(){
  var pi, ms;

  if (glob.curpos < glob.planIndex){ 
//    showNextMove();  // dont do this, it is confusing to the user
    return; 
  }
  pi = glob.curpos;
  while(glob.redo.length>0){
    ms = glob.redo.pop();
    if (ms == '-'){
      addToMoveList(getMoveAt(pi), 1); // 1 means it is a plan move
      pi += 1;
      break;
    }
    else{
      addStep(ms, pi, 1);
    }
  }
  goPosList(-1); // -1 means to the last position
}

function eventPassSteps(){
  corePassSteps();
}

function corePassSteps(){
  var pi;

  pi = isOnLastMove(); // is -1 if not on last move, else index of last move
  if (pi < 0){ return; }

  move = getMoveAt(pi);

  stepsTaken = stepsInMove(move);
  if (stepsTaken < 1){
// must take at least one step
// TODO in compose mode we can skip and not place all the pieces.
     return;
  }

  turn = getTurnAt(pi);
  lastStep = getPushStep(move, turn);
//alert('checked push ['+lastStep+']');
  if (lastStep != null){
// TODO should give a warning to the user to complete the push
    uiHelpMessage('You must complete the push');
    return;
  }

// the func warns the user if needed
  if (coreCheckRepeat(pi)){ 
//alert('undoing step');
    coreUnStep();
    return; 
  } 

// be sure to empty the redo list when the steps are passed
  glob.redo = [];
  addToMoveList(move, 1);
  goPosList(-1);

}



function uiClearArrows(){
  var i;
  if (glob.arrows != undefined){
//alert('clearing arrows');
    for(i=0;i<glob.arrows.length;i++){
      if (glob.arrows[i] != '.'){
        uiArrowAt('.', i);
      }
    }
    glob.arrows = [];
  }
}


function uiShowArrows(a){
  uiSetArrows(a);
}

function uiSetArrows(a){
  var i, set;
  uiClearArrows();
  set = 0;
  for(i=0;i<a.length;i++){
    if (a[i] != '.'){
      uiArrowAt(a[i], i);
      set = 1;
    }
  }
  if (set==1){
    glob.arrows = a;
  }
}

function uiArrowAt(p, sq){
  if ((sq<0) || (sq>63)){ return; }
  sq = coreViewMap(sq);
  p = coreMapDir(p);
  setArrowAt(p, sq);
}

// given a square number between 0 and 63 return the handle of the image
function square(sq){
  var c = i2c(sq);
  return document.getElementById('s'+c[0]+''+c[1])
}


function nextMoveCount(m){
  var a = [];
  var n;
  n = moveCount2Num(m);
  n = n + 1;
  return num2MoveCount(n);
}

function prevMoveCount(m){
  var a = [];
  var n;
  n = moveCount2Num(m);
  n = n - 1;
  if (n<0){ n=0; }
  return num2MoveCount(n);
}


function coreStepsLeft(i){
  if (getNumAt(i) == 1){
    return 16-stepsInMove(getMoveAt(i));
  }
  else{
    return 4-stepsInMove(getMoveAt(i));
  }
}

function stepsInMove(m){
  m = m.replace(/ *\w+x/g,'');
  if (m==''){ return 0; }
  var ma = m.split(/ +/);
  return ma.length;
}



// Breaks the step into 3 parts like: E d4 n
function stepInfo(s){
  return s.match(/([emhdcr])([a-h][1-8])([nsewx]?)/i);
}


// Breaks the step into 3 parts like: E 24 25; where 24 and 25 are board square index numbers
//   for a move like Rc3x the 3 parts would be: R 18 -1; the -1 indicates the piece is removed
//   for a move like Db1 the 3 parts would be: D -1 1; the -1 indicates the piece is placed
function step2Index(s){
  var r = [];
  var a = stepInfo(s);
  if (a == null){ return null; }
  var n = cord2i(a[2]);
  if (n < 0){ return null; }
  r[0] = a[1];
  if (a[3] == ''){
// this is a place step; set source square to -1
    r[1] = -1;
    r[2] = n;
  }
  else{
// this is a move step
    r[1] = n;
    r[2] = dir2i(n, a[3]);
// this is a remove step
    if (r[2] == n){
      r[2] = -1;
    }
  }
  return r;
}

function addStep(ms, pi, redo){
  if (glob.movelist[pi] != ''){ glob.movelist[pi] += ' '; }
  glob.movelist[pi] += ms;
  if (! redo){    
// if it is not a redo step then reset the redo list
    glob.redo = [];
  }
  uiUpdateMoveList(pi);
}

function clearAllSteps(pi){
  glob.movelist[pi] = glob.movelist[pi].replace(/ .*/, '');
  uiUpdateMoveList(pi);
}

function uiUpdateStepsLeft(pi){
  document.getElementById(glob.w.disp + "Steps").innerHTML = '';
  document.getElementById(glob.b.disp + "Steps").innerHTML = '';
  if (getNumAt(pi) == 1){
    return;
  }
  if (vars.result != ''){ return; }
  s = coreStepsLeft(pi);
  if (getTurnAt(pi) == 'w'){
    document.getElementById(glob.w.disp + "Steps").innerHTML = ''+s;
  }
  else{
    document.getElementById(glob.b.disp + "Steps").innerHTML = ''+s;
  }
}




function isStronger(s, w){
  return (pieceValue(s)>pieceValue(w))?1:0;
}

function getPushStep(m, turn){
  var steps, lastStep, si, fi, ri;
  m = m.replace(/^\d+\w */, '');
  if (m == null){ return; }
//alert(m);
  m = m.replace(/ +\w+x\b/, ' ');
// Omar added the following line on 2009.11.24; seems that \b does not match end of line
//   the problem was reported in the arimaa.com forum:
//   http://arimaa.com/arimaa/forum/cgi/YaBB.cgi?board=siteIssues;action=display;num=1258557896
  m = m.replace(/ +\w+x$/, ' '); 
  m = m.replace(/ +/,' ').replace(/^ */,'').replace(/ *$/,'');
  if (m==''){ return; }
log(m);
  steps = m.split(/ +/);
  if (steps == null){return;}
  if (steps.length <= 0){ return; }
  lastStep = steps[steps.length-1];
  si = step2Index(lastStep);
  if (pieceColor(si[0]) == turn){ return; }
  if (steps.length >= 4){ return; }
  if (steps.length == 1){ return lastStep; }
    if (steps.length == 2){
    ri = step2Index(steps[0]);
    if ((isStronger(ri[0],si[0]))&&(ri[1]==si[2])){ // is it a pull
      return;
    }
    return lastStep;
  }
  fi = step2Index(steps[0]);
  ri = step2Index(steps[1]);
  if (pieceColor(fi[0]) != turn){ return lastStep; }
  if ((isStronger(ri[0],si[0]))&&(ri[1]==si[2])){ // is it a pull
    return;
  }
  return lastStep;
}

function isFrozen(p, i){
  var frozen, unfrozen, d, j, turn;
  var dir = ['n', 's', 'e', 'w'];
  frozen = 0;
  unfrozen = 0;
  turn = pieceColor(p[i]);
  for (d in dir){
    d = dir[d];
    j = dir2i(i, d);
    if (j==i){ continue; }
    if (p[j]!='.'){ 
      if ((pieceColor(p[j]) != turn) && (isStronger(p[j],p[i]))){ frozen = 1; }
      if (pieceColor(p[j]) == turn){ unfrozen = 1; }
    }
  }
  // are we frozen
  if ((frozen==1)&&(unfrozen==0)){
    return 1;
  }
  return 0;
}


function pieceValue(p){
  p = p.toLowerCase();
  switch(p){
    case 'e': return 6;
    case 'm': return 5;
    case 'h': return 4;
    case 'd': return 3;
    case 'c': return 2;
    case 'r': return 1;
  }
  return p;
}


function pieceColor(p){
  if (p.match(/[EMHDCR]/)){ return 'w'; }
  if (p.match(/[emhdcr]/)){ return 'b'; }
  return '';
}

function setCookie(c_name,value,expiredays) {
  var exdate=new Date();
  exdate.setDate(exdate.getDate()+expiredays);
  document.cookie=c_name+ "=" +escape(value)+
  ((expiredays==null) ? "" : ";expires="+exdate.toUTCString());
}

function getCookie(c_name) {
  if (document.cookie.length>0) {
    c_start=document.cookie.indexOf(c_name + "=");
    if (c_start!=-1) {
      c_start=c_start + c_name.length+1;
      c_end=document.cookie.indexOf(";",c_start);
      if (c_end==-1) c_end=document.cookie.length;
      return unescape(document.cookie.substring(c_start,c_end));
    }
  }
  return "";
}



//********  set properties used in displaying window *****
function setProp(){

prop.bgcolor = "#d8d8f8"

prop.Image = {}
//x.Image.base="/arimaa/jsClient/dev/images/"
prop.Image.base="images/"
prop.Image.board="Board.jpg"
prop.Image.board_n="BoardN.jpg"
prop.Image.board_w="BoardG.jpg"
prop.Image.board_b="BoardS.jpg"
prop.Image.x="sp.gif"
prop.Image.E="we.gif"
prop.Image.M="wm.gif"
prop.Image.H="wh.gif"
prop.Image.D="wd.gif"
prop.Image.C="wc.gif"
prop.Image.R="wr.gif"
prop.Image.e="be.gif"
prop.Image.m="bm.gif"
prop.Image.h="bh.gif"
prop.Image.d="bd.gif"
prop.Image.c="bc.gif"
prop.Image.r="br.gif"

// gold and silver arrow images
prop.Image.wn="agn2.png"
prop.Image.ws="ags2.png"
prop.Image.we="age2.png"
prop.Image.ww="agw2.png"
prop.Image.bn="asn2.png"
prop.Image.bs="ass2.png"
prop.Image.be="ase2.png"
prop.Image.bw="asw2.png"

prop.BoardSize = {}
prop.BoardSize.W=335
prop.BoardSize.H=335

prop.PieceSize = {}
prop.PieceSize.W=40
prop.PieceSize.H=40

prop.SquareSize = {}
prop.SquareSize.W=41
prop.SquareSize.H=41

prop.ArrowSize = {}
prop.ArrowSize.W=40
prop.ArrowSize.H=40

prop.BoardOrigin = {}
prop.BoardOrigin.X=4
prop.BoardOrigin.Y=4

prop.Image.Dir = {}
prop.Image.Dir.o="sr.gif"
prop.Image.Dir.n="sn.gif"
prop.Image.Dir.e="se.gif"
prop.Image.Dir.s="ss.gif"
prop.Image.Dir.w="sw.gif"

prop.Url = {}
prop.Url.postmove="/arimaa/java/ys/ms4/v5/j1_post.pl"
prop.Url.getinfo="/arimaa/java/ys/ms4/v5/j1_get.pl"

prop.Document = {}
prop.Document.help="/arimaa/learn/appletHelp.html"
prop.Document.rules="/arimaa/learn/"
prop.Document.plan="/arimaa/games/planGame.cgi"
prop.Document.quit="/arimaa/java/ys/ms4/v5/closewin.html"

prop.test = 'arimaa';

}
//*********** end of properties *****

// easier to type JSON function names

function obj2json(d){
  return JSON.stringify(d);
}

function json2obj(d){
//alert(d);
  if (d.match(/\s*\{/)){
    return JSON.parse(d);
  }
  return {};
}


//***************** an Ajax class for HTTP net connections ********
//
function Ajax(url, handler){

var req, myUrl, myHandler;

myUrl = url;
myHandler = (handler == undefined)?gotData:handler;
req = getHttpObject();
if (req != undefined){
  req.onreadystatechange = gotChange;
}
else{ return {}; }

// async GET request
function get(){
  req.open("GET", myUrl, true);
  req.send(null);
}

// async POST request
function post(data){
  req.open("POST", myUrl, true);
  req.send(data);
}

// convert to json before posting
function jpost(data){
  post(obj2json(data));
}

function gotChange(){
  if (req.readyState == 4){
//    if (req.status == 200){
      myHandler(req.status, req.responseText);
      return;
//    }
//log('got error code '+req.status);
//    alert('got error code '+req.status);
  }
//log('got ready state of '+req.readyState);
}

function gotData(data){
  alert(data);
}

function getHttpObject(){
//if (this.XMLHttpRequest){ alert(1); }
//if (this.this.XMLHttpRequest){ alert(2); }
//if (this.this.this.XMLHttpRequest){ alert(3); }
//alert('hope you found it');

//  if (window.XMLHttpRequest){
  if (this.XMLHttpRequest){
    //alert('in getHttpObj xml');
    return new XMLHttpRequest();
  }
  else if(window.ActiveXObject){
    //alert('in getHttpObj actx');
    return new ActiveXObject("Microsoft.XMLHTTP");
  }
  else{
    //alert('in getHttpObj other');
    if (glob.notSupported == undefined){
//      alert('Status: Cound not create XmlHttpRequest Object.' + 'Consider upgrading your browser.');
      glob.notSupported = 1;
    }
  }
}

// Make only the get and post methods public
this.get = get;
this.post = post;
this.jpost = jpost;

}

//
//***************** end of Ajax class for HTTP net connections ********



// ********* public stuff; this needs to be at the end *******

this.vars = vars;
this.prop = prop;
this.window = window;
this.window2 = window2;  // needed for opening the plan window
this.init = init;
this.initSound = initSound;
this.playSound = playSound;
this.goPosList = goPosList;
this.showMoveSlow = showMoveSlow;
this.sendChat = sendChat;
this.sendMove = sendMove;
this.sendResign = sendResign;

this.eventUndoStep = eventUndoStep;
this.eventRedoStep = eventRedoStep;
this.eventUndoMove = eventUndoMove;
this.eventRedoMove = eventRedoMove;
this.eventPassSteps = eventPassSteps;
this.eventOverSquare = eventOverSquare;
this.eventClickSquare = eventClickSquare;

this.showPrevMove = showPrevMove;
this.showNextMove = showNextMove;
this.showFirstMove = showFirstMove;
this.showLastMove = showLastMove;

this.changeView = changeView;
this.changeMute = changeMute;
this.changeExpert = changeExpert;
this.changeShowSpeed = changeShowSpeed;

this.openMoveListWindow = openMoveListWindow;
this.openPlanWindow = openPlanWindow;

this.hideTip = hideTip;
this.showTip = showTip;

this.autoShowBtnChange = autoShowBtnChange;

// don't use these from outside
this.connectToWebService = connectToWebService; // need this for setTimeout
this.resendDataToWebService = resendDataToWebService; // need this for setTimeout
this.showMoveSlowDo = showMoveSlowDo; // we have to expose this for setTimeout
this.uiShowTimers = uiShowTimers; // we have to expose this for setInterval


} // ********** end of Arimaa class *******


//************* we need a JSON class also ************
//  this is the jsmin version of the code provided at json.org
"use strict";if(!this.JSON){this.JSON={};}
(function(){function f(n){return n<10?'0'+n:n;}
if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+
f(this.getUTCMonth()+1)+'-'+
f(this.getUTCDate())+'T'+
f(this.getUTCHours())+':'+
f(this.getUTCMinutes())+':'+
f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}
var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}
function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}
if(typeof rep==='function'){value=rep.call(holder,key,value);}
switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}
v=partial.length===0?'[]':gap?'[\n'+gap+
partial.join(',\n'+gap)+'\n'+
mind+']':'['+partial.join(',')+']';gap=mind;return v;}
if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}
v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+
mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}
if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}
rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}
return str('',{'':value});};}
if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}
return reviver.call(holder,key,value);}
cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+
('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}
if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}
throw new SyntaxError('JSON.parse');};}}());
//************* end of JSON class ************



arimaa = new Arimaa();




