/*
+--------------------------------------------------------------------------
|   vdBoard.js - javascript Go board functions
|   ==========================================
|   Author: Dmytro Bogatskyy [di@ufgo.org]
|   (c) 1998 - 2009 BiGo Software
|   http://bigo.baduk.org
|   http://fuseki.info
+--------------------------------------------------------------------------
*/

function Cords(x, y) {
 this.x = parseInt(x);
 this.y = parseInt(y);

 function Parse(s) {
  var a = s.split("x");
  this.x = parseInt(a[0]);
  this.y = parseInt(a[1]);
 }

 function Format() {
  return this.x + "x" + this.y;
 }

 this.Parse = Parse;
 this.Format = Format;
}

function Board(size, psize, xfrom, xto, yfrom, yto, prefix, Images, ClickProcName) {
 this.size = size;
 this.prefix = prefix;
 this.psize = psize;
 this.xfrom = xfrom;
 this.xto = xto;
 this.yfrom = yfrom;
 this.yto = yto;
 this.sym = 0;
 this.antisym = 0;
 this.vsym = 0;
 this.cp = 0;
 this.mcp = 0;
 this.CapturedBlack = 0;
 this.CapturedWhite = 0;
 this.LastDead = 0;
 this.Images = Images;
 this.ClickProcName = ClickProcName;
 this.old = new Array(size);

 function BoardElement(x, y) {
  this.id = this.prefix + "" + x + "x" + y;
  this.alpha = "";
  this.value = 0;
  this.checked = 0;
  this.bim = 0;
 }

 function OldBoardElement() {
  this.alpha = "";
  this.value = 0;
 }

 for (var i = 0; i < this.size; i++) {
  this[i] = new Array(size);
  this.old[i] = new Array(size);

  for (var j = 0; j < this.size; j++) {
   this[i][j] = new BoardElement(i, j);
   this.old[i][j] = new OldBoardElement();
  }
 }

 function Alive(x, y, c) {
  this.cp = this.cp + 1;
  if (this.cp > this.mcp) {
   this.mcp = this.cp; }

  this[x][y].checked = 1;
  var p = 0;
  if ( ((x > 0) && (this[x - 1][y].value == 0)) ||
       ((x < this.size - 1) && (this[x + 1][y].value == 0)) ||
       ((y > 0) && (this[x][y - 1].value == 0)) ||
       ((y < this.size - 1) && (this[x][y + 1].value == 0)) )
   p = 1;
  else {
   if ( (x > 0) && (this[x - 1][y].value == c) && (this[x - 1][y].checked == 0) )
    p = this.Alive(x - 1, y, c);
   if ( (p == 0) && (x < this.size - 1) && (this[x + 1][y].value == c) && (this[x + 1][y].checked == 0) )
    p = this.Alive(x + 1, y, c);
   if ( (p == 0) && (y > 0) && (this[x][y - 1].value == c) && (this[x][y - 1].checked == 0) )
    p = this.Alive(x, y - 1, c);
   if ( (p == 0) && (y < this.size - 1) && (this[x][y + 1].value == c) && (this[x][y + 1].checked == 0) )
    p = this.Alive(x, y + 1, c);
  }

  this.cp = this.cp - 1;
  if (this.cp == 0) {
   if (this.mcp == 1) 
    this[x][y].checked = 0;
   else {
    for (var i = 0; i < this.size; i++)
     for (var j = 0; j < this.size; j++)
      this[i][j].checked = 0;
   }
  }

  return p;
 }

 function RemoveGroup(x, y, c) {
  this[x][y].value = 0;
  if (c == 1)
   this.CapturedBlack++;
  else
   this.CapturedWhite++;

  if ( (x > 0) && (this[x - 1][y].value == c) )
   this.RemoveGroup(x - 1, y, c);
  if ( (x < this.size - 1) && (this[x + 1][y].value == c) )
   this.RemoveGroup(x + 1, y, c);
  if ( (y > 0) && (this[x][y - 1].value == c) )
   this.RemoveGroup(x, y - 1, c);
  if ( (y < this.size - 1) && (this[x][y + 1].value == c) )
   this.RemoveGroup(x, y + 1, c);
 }

 function RemoveDeads(x, y, c) {
  var d = 0;
  if ( (x > 0) && (this[x - 1][y].value == c) && (this.Alive(x - 1, y, c) == 0) ) {
   d += 8;
   this.RemoveGroup(x - 1, y, c);
  }
  if ( (x < this.size - 1) && (this[x + 1][y].value == c) && (this.Alive(x + 1, y, c) == 0) ) {
   d += 2;
   this.RemoveGroup(x + 1, y, c);
  }
  if ( (y > 0) && (this[x][y - 1].value == c) && (this.Alive(x, y - 1, c) == 0) ) {
   d += 4;
   this.RemoveGroup(x, y - 1, c);
  }
  if ( (y < this.size - 1) && (this[x][y + 1].value == c) && (this.Alive(x, y + 1, c) == 0) ) {
   d += 1;
   this.RemoveGroup(x, y + 1, c);
  }
  return d;
 }

 function RestoreGroup(x, y, c) {
  this[x][y].value = c;
  if (c == 1)
   this.CapturedBlack--;
  else
   this.CapturedWhite--;

  if ( (x > 0) && (this[x - 1][y].value == 0) )
   this.RestoreGroup(x - 1, y, c);
  if ( (x < this.size - 1) && (this[x + 1][y].value == 0) )
   this.RestoreGroup(x + 1, y, c);
  if ( (y > 0) && (this[x][y - 1].value == 0) )
   this.RestoreGroup(x, y - 1, c);
  if ( (y < this.size - 1) && (this[x][y + 1].value == 0) )
   this.RestoreGroup(x, y + 1, c);
 }

 function RestoreDeads(x, y, c, d) {
  if ( (d & 1) > 0)
   this.RestoreGroup(x, y + 1, c);
  if ( (d & 2) > 0)
   this.RestoreGroup(x + 1, y, c);
  if ( (d & 4) > 0)
   this.RestoreGroup(x, y - 1, c);
  if ( (d & 8) > 0)
   this.RestoreGroup(x - 1, y, c);
  if ( (d & 16) > 0)
   this.RestoreGroup(x, y, (c & 1) + 1);
 }

 function PlayMove(x, y, c) {
  this.LastDead = 0;
  if ((x == 31) || (y == 31))
   return 1;

  if ((c == 0) || (this[x][y].value != 0))
   return 0;

  this[x][y].value = c;
  this.LastDead = this.RemoveDeads(x, y, (c & 1) + 1);
  if (this.Alive(x, y, c) == 0) {
   this.LastDead = 16;
   this.RemoveGroup(x, y, c);
  }
  return 1;
 }

 function RemoveMove(x, y, c, d) {
  if (c == 0) 
   return 0;

  if ((x == 31) || (y == 31))
   return 1;

  this.RestoreDeads(x, y, (c & 1) + 1, d);
  if (this[x][y].value != c) {
   this.RemoveDeads(x, y, (c & 1) + 1);
   return 0;
  }

  this[x][y].value = 0;
  return 1;
 }

 function GetElementById(s) {
  if (this.prefix) {
   if (s.indexOf(this.prefix) == 0)
    s = s.substr(this.prefix.length);
   else
    return null;
  }

  var c = new Cords();
  c.Parse(s);
  if (this[c.x][c.y].id == s)
   return this[c.x][c.y];
  else
   return null;
 }

 function Symmetry(x, y, sym) {
  var c = new Cords(x, y);
  if ( (c.x > this.size) || (c.y > this.size) )
   return c;

  if ((sym & 1) > 0) {
   var t = c.x;
   c.x = c.y;
   c.y = t;
  }
  if ((sym & 2) > 0)
   c.x = this.size - c.x - 1;

  if ((sym & 4) > 0)
   c.y = this.size - c.y - 1;

  return c;
 }

 function GenPointData(x, y) {
  var s = "";
  if (this[x][y].alpha == "") {
   s += "<img src=\"";

   if (this[x][y].value == 0)
    s += this.Images[this[x][y].bim];
   else
    if ((this.sym & 8) == 0)
     s += this.Images[10 + parseInt(this[x][y].value)];
    else
     s += this.Images[11 + (parseInt(this[x][y].value) & 1)];

   s += "\" height=" + this.psize + " width=" + this.psize + " border=0 alt=\"\">";
  } else
   s += this[x][y].alpha;

  return s;
 }

 function RebuildBoard() {
  var sp = document.getElementById(this.prefix + "board");
  if (!sp) return null;

  var st = 31
  if ((this.size & 1) == 1) 
   st = this.size >> 1;

  for (var i = 0; i < this.size; i++)
   for (var j = 0; j < this.size; j++) {
    var c = this.Symmetry(i, j, this.sym);
    if (c.y == 0) {
     if (c.x == 0) 
      t = 1;
     else if (c.x == this.size - 1) 
      t = 2;
     else 
      t = 5;
     } else if (c.y == this.size - 1) {
      if (c.x == 0)
       t = 3;
      else if (c.x == this.size - 1) 
       t = 4;
      else 
       t = 8;
     } else if (c.x == 0) 
      t = 6;
     else if (c.x == this.size - 1) 
      t = 7;
     else if (((c.x == 3) || (c.x == st) || (c.x == this.size - 4)) && ((c.y == 3) || (c.y == st) || (c.y == this.size - 4)))
      t = 10;
     else
      t = 9;

    this[i][j].bim = t;
   }

  var xfrom = this.xfrom;
  var xto = this.xto;
  var yfrom = this.yfrom;
  var yto = this.yto;
  if ((this.sym & 1) > 0) {
   xfrom = this.yfrom;
   xto = this.yto;
   yfrom = this.xfrom;
   yto = this.xto;
  }

  if ((this.sym & 2) > 0) {
   var t = this.size - xfrom - 1;
   xfrom = this.size - xto - 1;
   xto = t;
  }

  if ((this.sym & 4) > 0) {
   var t = this.size - yfrom - 1;
   yfrom = this.size - yto - 1;
   yto = t;
  }

  var pt = (this.psize >> 2) + (this.psize >> 3);
  var sx = this.psize * (xto - xfrom + 1) + pt * 2;
  var sy = this.psize * (yto - yfrom + 1) + pt * 2;

  var pti = this.psize - (this.psize >> 1) + (this.psize >> 2);

  var s = "<table background=\"" + this.Images[0] + "\" cellspacing=0 cellpadding=0 border=0 width=" + sx + " height=" + sy + ">";

  s += "<tr height=" + pt + ">" + "<td width=" + pt + "><img src=\"images/1.gif\" width=" + pt + " height=" + pt + " alt=\"\"></td>";
  for (var i = xfrom; i <= xto; i++)
   s += "<td width=" + this.psize + "><img src=\"images/1.gif\" width=" + this.psize + " height=" + pt + " alt=\"\"></td>";
  s += "<td width=" + pt + "><img src=\"images/1.gif\" width=" + pt + " height=" + pt + " alt=\"\"></td></tr>"

  var c;
  for (var j = yfrom; j <= yto; j++) {
   s += "<tr height=" + this.psize + ">" + "<td width=" + pt + "><img src=\"images/1.gif\" width=" + pt + " height=" + this.psize + " alt=\"\"></td>";
   for (var i = xfrom; i <= xto; i++) {
    c = this.Symmetry(i, j, this.antisym);
    s += "<td id=\"" + this[c.x][c.y].id + "\" width=" + this.psize + " class=\"" + this.prefix + "bpoint\" onclick=\"" + this.ClickProcName+ "(" + c.x + ", " + c.y + ")\" style=\"font-size:" + pti + "px\">";

    s += this.GenPointData(c.x, c.y, Images);
    s += "</td>";
    this.old[c.x][c.y].value = this[c.x][c.y].value;
    this.old[c.x][c.y].alpha = this[c.x][c.y].alpha;
   }
   s += "<td width=" + pt + "><img src=\"images/1.gif\" width=" + pt + " height=" + this.psize + " alt=\"\"></td></tr>";
  }

  s += "<tr height=" + pt + ">" + "<td width=" + pt + "><img src=\"images/1.gif\" width=" + pt + " height=" + pt + " alt=\"\"></td>";
  for (var i = xfrom; i <= xto; i++)
   s += "<td width=" + this.psize + "><img src=\"images/1.gif\" width=" + this.psize + " height=" + pt + " alt=\"\"></td>";
  s += "<td width=" + pt + "><img src=\"images/1.gif\" width=" + pt + " height=" + pt + " alt=\"\"></td></tr>"

  s += "</table>";
  sp.innerHTML = "&nbsp";
  sp.innerHTML = s;
 }

 function Repaint() {
  for (var i = this.xfrom; i <= this.xto; i++)
   for (var j = this.yfrom; j <= this.yto; j++)
    if ((this[i][j].value != this.old[i][j].value) || (this[i][j].alpha != this.old[i][j].alpha)) {
     var el = document.getElementById(this[i][j].id);
     if (el)
      el.innerHTML = this.GenPointData(i, j);

     this.old[i][j].value = this[i][j].value;
     this.old[i][j].alpha = this[i][j].alpha;
    }
 }

 function RepaintAll() {
  for (var i = this.xfrom; i <= this.xto; i++) 
   for (var j = this.yfrom; j <= this.yto; j++) {
    var el = document.getElementById(this[i][j].id);
    if (el) 
     el.innerHTML = this.GenPointData(i, j);

    this.old[i][j].value = this[i][j].value;
    this.old[i][j].alpha = this[i][j].alpha;
   }
 }

 function ChangeSym(s) {
  this.sym = SymAdd(this.sym, s);
  this.antisym = SymSub(0, this.sym);
  this.RebuildBoard();
 }

 this.Alive = Alive;
 this.RemoveGroup = RemoveGroup;
 this.RemoveDeads = RemoveDeads;
 this.RestoreGroup = RestoreGroup;
 this.RestoreDeads = RestoreDeads;
 this.PlayMove = PlayMove;
 this.RemoveMove = RemoveMove;
 this.GetElementById = GetElementById
 this.Symmetry = Symmetry;
 this.GenPointData = GenPointData;
 this.RebuildBoard = RebuildBoard;
 this.Repaint = Repaint;
 this.RepaintAll = RepaintAll;
 this.ChangeSym = ChangeSym;
}

function SymAdd(sym1, sym2) {
 if ( ((sym2 & 1) == 1) && ( ((sym1 & 6) == 2) || ((sym1 & 6) == 4) ) )
  sym1 ^= 6;

 return sym1 ^ sym2;
}

function SymSub(sym1, sym2) {
 if ( ((sym2 & 7) == 3) || ((sym2 & 7) == 5) )
  sym2 ^= 6;

 return SymAdd(sym2, sym1);
}

function Sequence() {
 this.id = "";
 this.cords = new Cords(31, 31);
 this.value = 0;
 this.dead = 0;
 this.vsym = -1;
 this.pass = 0;
 this.last = 0;
 this.comment = "";

 this.prev = null;
 this.next = null;
 this.vari = null;

 function AddMove() {
  var sq = new Sequence();
  if (this.next != null) {
   sq.next = this.next;
   var ps = this.next;
   while (ps != null) {
    ps.prev = sq;
    ps = ps.vari;
   }
  }
  this.next = sq;
  sq.prev = this;
  return sq;
 }

 function ConnectVari(pn) {
  if (this.vari != null)
   pn.vari = this.vari;
  this.vari = pn;
  if (this.prev != null)
   pn.prev = this.prev;
 }

 function AddVari() {
  var ps = new Sequence();
  this.ConnectVari(ps);
  return ps;
 }

 this.AddMove = AddMove;
 this.ConnectVari = ConnectVari;
 this.AddVari = AddVari;
}

function GameStats(s) {
 var t = s.split(",");
 this.first = t[0];
 this.second = t[1];

 var n = parseInt("0x" + t[2]);
 var k = 3;
 for (var i = 0; i < 6; i++) {
  this[i] = new Object();
  if (((1 << i) & n) > 0) {
   this[i].win = parseInt("0x" + t[k++]);
   this[i].lost = parseInt("0x" + t[k++]);
   this[i].unknown = parseInt("0x" + t[k++]);
  } else {
   this[i].win = 0;
   this[i].lost = 0;
   this[i].unknown = 0;
  }
 }

 function Show(name, inv) {
  var el = document.getElementById(name);
  var elt = document.getElementById(name + "_TB");
  if ((!el) || (!elt))
   return;

  el.style.display = "";
  var elb = document.getElementById(name + "_BR");
  var elw = document.getElementById(name + "_WR");
  if ( (elb) && (elw) ) {
   if (inv) {
    elb.innerHTML = this.second;
    elw.innerHTML = this.first;
   } else {
    elb.innerHTML = this.first;
    elw.innerHTML = this.second;
   }
  }

  var n = 0;
  var t;
  var tw = 0;
  var tl = 0;
  var tu = 0;
  for (var i = 0; i < 6; i++) {
   if ( (this[i].win == 0) && (this[i].lost == 0) && (this[i].unknown == 0) )
    elt.rows[2 + i].style.display = "none";
   else {
    elt.rows[2 + i].style.display = "";
    t = this[i].win + this[i].lost + this[i].unknown;
    if (inv) {
     elt.rows[2 + i].cells[1].innerHTML = (Math.round((this[i].lost * 1000)/t)/10) + "% (" + this[i].lost + ")";
     elt.rows[2 + i].cells[2].innerHTML = (Math.round((this[i].win * 1000)/t)/10) + "% (" + this[i].win + ")";
    } else {
     elt.rows[2 + i].cells[1].innerHTML = (Math.round((this[i].win * 1000)/t)/10) + "% (" + this[i].win + ")";
     elt.rows[2 + i].cells[2].innerHTML = (Math.round((this[i].lost * 1000)/t)/10) + "% (" + this[i].lost + ")";
    }
    elt.rows[2 + i].cells[3].innerHTML = t;
    n++;
    tw += this[i].win;
    tl += this[i].lost;
    tu += this[i].unknown;
   }
  }

  if (n == 1)
   elt.rows[8].style.display = "none";
  else {
   elt.rows[8].style.display = "";
   t = tw + tl + tu;
   if (inv) {
    elt.rows[8].cells[1].innerHTML = (Math.round((tl * 1000)/t)/10) + "% (" + tl + ")";
    elt.rows[8].cells[2].innerHTML = (Math.round((tw * 1000)/t)/10) + "% (" + tw + ")";
   } else {
    elt.rows[8].cells[1].innerHTML = (Math.round((tw * 1000)/t)/10) + "% (" + tw + ")";
    elt.rows[8].cells[2].innerHTML = (Math.round((tl * 1000)/t)/10) + "% (" + tl + ")";
   }
   elt.rows[8].cells[3].innerHTML = t;
  }
 }

 this.Show = Show;
}

function FindSequenceByCord(ps, c) {
 var sq = ps.next;
 while (sq != null) {
  if ( ( (sq.value != ps.value) ^ ((ps.pass & 1) > 0) ) && (sq.cords.x == c.x) && (sq.cords.y == c.y) )
   return sq;
  sq = sq.vari;
 }
 return null;
}

function BoardExt(size, psize, xfrom, xto, yfrom, yto, prefix, Images, ClickProcName) {
 var oSuper = new Board(size, psize, xfrom, xto, yfrom, yto, prefix, Images, ClickProcName);
 for (sProperty in oSuper)
  this[sProperty] = oSuper[sProperty];

 this.root = new Sequence();
 this.cursq = this.root;
 this.root.id = "0";
 this.MoveNumber = 0;
 this.useSmartFilter = 1;
 this.block = 0;
 this.blocktime = 0;
 this.OnLoadData = null;
 this.OnChangeInfo = null;

 function FindSequenceById(from, id) {
  if (id == "")
   return null;

  if (id == "0")
   return this.root;

  if (from.id == id)
   return from;

  var ps = from.next;
  while ( (ps != null) && (ps != from) ) {
   if (ps.id == id)
    return ps;

   if (ps.next != null)
    ps = ps.next;
   else if (ps.vari != null)
    ps = ps.vari;
   else {
    while ((ps != null) && (ps != from) && (ps.vari == null))
     ps = ps.prev;
    if ((ps != null) && (ps != from))
     ps = ps.vari;
   }
  }
  return null;
 }

 function ParseSequence(s) {
  if (s == "")
   return 1;
  var t = s.split("|");
  if (t.length < 3)
   return 0;
  var ps = this.FindSequenceById(this.cursq, t[0]);
  if (ps == null)
   ps = this.FindSequenceById(this.root, t[0]);

  if (ps == null)
   return 0;

  if (t[1] == "cid") {
   ps.id = t[2];
   return 1;
  }

  if ( (t[1] != "") && (ps.vari == null) ) {
   var pn = ps.AddVari();
   pn.id = t[1];
  }

  if ( (t[2] != "") && (ps.next == null) ) {
   var pn = ps.AddMove();
   pn.id = t[2];
  }

  if ( (t[3]) && (t[3] != "") ) {
   var tt = t[3].split(":");
   if ( (!tt[0]) || (tt[0] == "p") )
    ps.cords.x = 31;
   else
    ps.cords.x = parseInt(tt[0]);
   if ( (!tt[1]) || (tt[1] == "p") )
    ps.cords.y = 31;
   else
    ps.cords.y = parseInt(tt[1]);
   ps.value = parseInt(tt[2]);
  }

  for (var i = 4; i < t.length; i++)
   if (t[i] != "") {
    if (t[i] == "ms")
     ps.movestats = new GameStats(t[++i]);
    else if (t[i] == "ps")
     ps.posstats = new GameStats(t[++i]);
    else if (t[i] == "gm")
     ps.game = t[++i];
    else if (t[i] == "bm") {
     var tm = t[++i].split(",");
     for (var k = 0; k < tm.length; k++)
      if ( (tm[k]) && (tm[k] != "") ) {
       var tt = tm[k].split(":");
       var c = new Cords(tt[0], tt[1]);
       this[c.x][c.y].value = parseInt(tt[2]);
      }
    } else
     ps.comment = t[i];
   }
  return 1;
 }

 function ShowAlpha(status) {
  var ps, n, k;
  var ar = new Object();

//  ps = this.cursq.next;
//  while ((ps != null) && (!ps.game))
//   ps = ps.prev;

//  if ((ps != null) && (this.cursq.next.vari == null))
//   return;

  k = 0;
  if ((status != 0) && (this.useSmartFilter != 0)) {
   n = 0;
   ps = this.cursq.next;
   while (ps != null) {
    if ( (ps.cords.x < 31) && (ps.cords.y < 31) && ( (ps.value != this.cursq.value) ^ ((this.cursq.pass & 1) > 0) ) ) {
     ar[n] = 0;
     if (ps.movestats)
      for (var i = 0; i < 6; i++)
       ar[n] += ps.movestats[i].win + ps.movestats[i].lost + ps.movestats[i].unknown;

     k += ar[n];
     n++;
    }
    ps = ps.vari;
   }
   if ( (n > 1) && (k > 1) )
    k = Math.floor((k/n)/(3 + (2 * Math.log(k))/Math.log(10)));
  }

  n = 0;
  ps = this.cursq.next;
  while (ps != null) {
   if ( (ps.cords.x < 31) && (ps.cords.y < 31) && ( (ps.value != this.cursq.value) ^ ((this.cursq.pass & 1) > 0) ) ) {
    if (status == 0)
     this[ps.cords.x][ps.cords.y].alpha = "";
    else if ( (this.useSmartFilter == 0) || (k == 0) || (ar[n] >= k) ) {
      var s;
      if (n < 26)
       s = String.fromCharCode(97 + n);
      else if (n < 52)
       s = String.fromCharCode(39 + n);
      else
       s = String.fromCharCode(97 + (n % 26)) + ((n - (n % 26)) / 26 - 1);
     this[ps.cords.x][ps.cords.y].alpha = s;
    }
    n++;
   }
   ps = ps.vari;
  }
 }

 function NextMoveAt(c) {
  if ((c.x == 31) && (c.y == 31)) {
   this.ShowAlpha(0);
   this.cursq.pass++;
   this.MoveNumber++;
   if (this.cursq.pass > 2) {
    this.cursq.pass %= 2;
    this.MoveNumber -= 2;
   }
   return 1;
  }

  var ps = FindSequenceByCord(this.cursq, c);
  if (ps == null)
   return 0;

  if (this.PlayMove(ps.cords.x, ps.cords.y, ps.value) == 0)
   return 0;

  var sq = this.cursq.next;
  while (sq != null) {
   sq.last = 0;
   sq = sq.vari;
  }

  ps.last = 1;
  ps.dead = this.LastDead;
  this.ShowAlpha(0);
  this.cursq = ps;
  this.MoveNumber++;
  return 1;
 }

 function BackMove() {
  if ( ( (this.cursq == this.root) && (this.cursq.pass == 0) ) || (this.block > 0) )
   return;

  if (this.cursq.pass > 0)
   this.cursq.pass--;
  else
   if (this.RemoveMove(this.cursq.cords.x, this.cursq.cords.y, this.cursq.value, this.cursq.dead) == 1) {
    this.cursq = this.cursq.prev;
   }
  this.MoveNumber--;
 }

 function BackMoveOn(n) {
  if ( ( (this.cursq == this.root) && (this.cursq.pass == 0) ) || (this.block > 0) )
   return;

  this.ShowAlpha(0);
  if (n == 0) {
   while ( (this.cursq != this.root) || (this.cursq.pass > 0) )
    this.BackMove();
  } else {
   for (var i = 0; i < n; i++)
    if ( (this.cursq != this.root) || (this.cursq.pass > 0) )
     this.BackMove();
  }
  this.ShowAlpha(1);
  this.Repaint();
  this.DoChangeInfo();
 }

 function NextMove() {
  if ( (this.cursq.next == null) || (this.block > 0) )
   return;

  var sq = this.cursq.next;
  while ( (sq != null) && (sq.last == 0) )
   sq = sq.vari;

  if (sq == null) {
   sq = this.cursq.next;
   while ( (sq != null) && ( (sq.value == this.cursq.value) ^ ((this.cursq.pass & 1) > 0) ) )
    sq = sq.vari;
  }

  if ( (sq != null) && ( (sq.value != this.cursq.value) ^ ((this.cursq.pass & 1) > 0) ) ) {
   if (this.PlayMove(sq.cords.x, sq.cords.y, sq.value) == 0)
    return;

   sq.dead = this.LastDead;
   this.cursq = sq;
   this.MoveNumber++;
  } else {
   this.cursq.pass++;
   this.MoveNumber++;
   if (this.cursq.pass > 2) {
    this.cursq.pass %= 2;
    this.MoveNumber -= 2;
   }
 }

  if ( (this.cursq.next != null) && (this.cursq.next.value == 0) )
   this.DoLoadData(this.cursq.next.id);
 }

 function NextMoveOn(n) {
  if ( (this.cursq.next == null) || (this.block > 0) )
   return;

  this.ShowAlpha(0);
  if (n == 0) {
   while ( (this.cursq.next != null) && (this.cursq.next.value != 0) ) {
    this.NextMove();

    var ps = this.cursq.prev;
    while ( (ps != null) && (ps.id != this.cursq.id) )
     ps = ps.prev;
    if (ps != null)
     break;
   }
  } else {
   for (var i = 0; i < n; i++)
    if ( (this.cursq.next != null) && (this.cursq.next.value != 0) )
     this.NextMove();
    else
     break;
  }
  this.ShowAlpha(1);
  this.Repaint();
  this.DoChangeInfo();
 }

 function CountVSym() {
  var r = 1;
  var c;
  var b;
  for (var s = 1; s < 8; s++)
   if ((this.vsym & (1 << s)) > 0) {
    b = 1;
    for (var i = this.xfrom; i <= this.xto; i++) {
     for (var j = this.yfrom; j <= this.yto; j++)
      if (this[i][j].value != 0) {
       c = this.Symmetry(i, j, s);
       if (this[i][j].value != this[c.x][c.y].value) {
        b = 0;
        break;
       }
      }
     if (b != 1)
      break;
    }
    if (b == 1)
     r += (1 << s)
   }
  return r;
 }

 function DoBoardClick(x, y) {
  if (this.block > 0)
   return;

  var c = new Cords(x, y);
  if (this.NextMoveAt(c) == 1) {
   if ( (this.cursq.next != null) && (this.cursq.next.value == 0) )
    this.DoLoadData(this.cursq.next.id);
   else
    this.ShowAlpha(1);
   this.Repaint();
   this.DoChangeInfo();
  }
 }

 function DoLoadData(id) {
  if (this.OnLoadData != null) {
   this.block++;
   this.blocktime = new Date();
   if (this.cursq.vsym < 0)
    this.cursq.vsym = this.CountVSym();
   if (this.cursq.vsym > 1)
    id = id + "v" + this.cursq.vsym;
   this.OnLoadData(this, id);
  }
 }

 function DoChangeInfo() {
  if (this.OnChangeInfo != null)
   this.OnChangeInfo(this);
 }

 this.FindSequenceById = FindSequenceById;
 this.ParseSequence = ParseSequence;
 this.ShowAlpha = ShowAlpha;
 this.NextMoveAt = NextMoveAt;
 this.BackMove = BackMove;
 this.BackMoveOn = BackMoveOn;
 this.NextMove = NextMove;
 this.NextMoveOn = NextMoveOn;
 this.CountVSym = CountVSym;
 this.DoBoardClick = DoBoardClick;
 this.DoLoadData = DoLoadData;
 this.DoChangeInfo = DoChangeInfo;
}
