var goodprogram = '';

function update_program() {
  var program = $('#program').value;
  try {
    var challenger = eval('(' + program + ')');
    var a = new arena(challenger, rock);
    a.playmatch(100);
  } catch(error) {
    $('#error').css({'color': 'red', 'background': 'pink'});
    $('#error').html(error);
    return;
  }
  if (a.forfeit != 0) {
    $('#error').css({'color': 'blue', 'background': 'pink'});
    $('#error').html(a.error);
    return;
  }
  goodprogram = program;
  $('#error').css({'color': 'blue', 'background': 'none'});
  $('#error').html('OK');
}

function rock(seed, rounds) {
  this.play = function() { return 0; };
  this.see = function(p) { };
}

function random(seed, rounds) {
  this.play = function() { return Math.floor(Math.random() * 3); }
  this.see = function(p) { };
}

function arena(challenge, defend) {
  this.defendfactory = defend;
  this.challengefactory = challenge;
  this.defendplays = [];
  this.challengeplays = [];
  this.seed = 0;
  this.playing = 0;
  this.forfeit = 0;
  this.score = 0;
}

arena.prototype.checkplay = function checkplay(p) {
  if (p == 0 || p == 1 || p == 2) {
    return;
  } else {
    throw "Illegal play " + p;
  }
}

arena.prototype.playround = function playround(challenge, defend, rngc, rngd) {
  var first = challenge;
  var second = defend;
  var frng = rngc;
  var srng = rngd;
  this.playing = 1;
  if (this.score < 0) {
    first = defend;
    second = challenge;
    frng = rngd;
    srng = rngc;
    this.playing = -1;
  }
  Math.seedrandom(frng);
  var fp = first.play();
  this.checkplay(fp);
  this.playing = -this.playing;
  Math.seedrandom(srng);
  var sp = second.play();
  this.checkplay(sp);
  this.playing = -this.playing;
  first.see(sp);
  this.playing = -this.playing;
  second.see(fp);
  var winner = (sp - fp + 3) % 3;
  if (winner == 2) winner = -1;
  if (this.score < 0) {
    this.defendplays.push(fp);
    this.challengeplays.push(sp);
    winner = -winner;
  } else {
    this.defendplays.push(sp);
    this.challengeplays.push(fp);
  }
  this.score += winner;
}

arena.prototype.checkmatch = function(player, plays, oppose) {
  for (var j = 0; j < plays.length; ++j) {
    if (player.play() != plays[j]) {
      throw 'Not deterministic';
    }
    player.see(oppose[j]);
  }
  return true;
}

arena.prototype.randomletters(n) {
  var result = '';
  while (n--) {
    result += String.fromCharCode(
        'a'.charCodeAt(0) + Math.floor(Math.random() * 26));
  }
  return result;
}

arena.prototype.playmatch = function playmatch(rounds, seedc, seedd) {
  this.rounds = rounds;
  try {
    var rngc = Math.seedrandom(seedc);
    var rngd = Math.seedrandom(seedd);
    this.playing = 1;
    var funct = this.challengefactory;
    Math.seedrandom(rngc);
    var challenge = new (funct)(this.seed, rounds);
    this.playing = -1;
    funct = this.defendfactory;
    Math.seedrandom(rngd);
    var defend = new (funct)(this.seed, rounds);
    this.playing = 1;
    for (var j = 0; j < rounds; ++j) {
      this.playround(challenge, defend, rngc, rngd);
    }
    Math.seedround(seedc);
    this.checkmatch(new (this.challengefactory)(this.seed, rounds),
                    this.challengeplays, this.defendplays)
    Math.seedround(seedc);
    this.checkmatch(new (this.defendfactory)(this.seed, rounds),
                    this.defendplays, this.challengeplays)
  } catch (error) {
    this.error = error;
    this.forfeit = this.playing;
  }
}

arena.prototype.winlist = function winlist() {
  var result = [];
  for (var j = 0; j < defendplays.length; ++j) {
    var win = (this.challengeplay[j] - this.defendplay[j] + 3) % 3;
    if (win == 2) win = -1;
    result.push(win);
  }
  return result;
}

arena.prototype.score = function score() {
  if (this.forfeit != 0) {
    return -this.forfeit * this.rounds;
  }
  var result = 0;
  var wins = this.winlist();
  for (var j = 0; j < wins.length; ++j) {
    result += wins[j];
  }
  return result;
}

