text: function GameManager(size, InputManager, Actuator) {
    this.size         = size; // Size of the grid
    this.inputManager = new InputManager;
    this.actuator     = new Actuator;
  
    this.running      = false;
  
    this.inputManager.on("move", this.move.bind(this));
    this.inputManager.on("put", this.put.bind(this));
    this.inputManager.on("restart", this.restart.bind(this));
    this.inputManager.on("addTile", this.addTile.bind(this));
  
    this.inputManager.on('think', function() {
      var best = this.ai.getBest();
      this.actuator.showHint(best.move);
    }.bind(this));
  
  
    this.inputManager.on('run', function() {
      if (this.running) {
        this.running = false;
        this.actuator.setRunButton('Auto-run');
      } else {
        this.running = true;
        this.run()
        this.actuator.setRunButton('Stop');
      }
    }.bind(this));
  
    this.setup();
  }
  
  // Restart the game
  GameManager.prototype.restart = function () {
    this.actuator.restart();
    this.setup();
  };
  
  // Set up the game
  GameManager.prototype.setup = function () {
    this.grid         = new Grid(this.size);
    this.grid.addStartTiles();
  
    this.ai           = new AI(this.grid);
  
    this.score        = 0;
    this.over         = false;
    this.won          = false;
  
    // Update the actuator
    this.actuate();
  };
  
  // Set up the game
  GameManager.prototype.put = function () {
    this.grid         = new Grid(this.size);
    this.grid.addStartTiles();
  
    this.ai           = new AI(this.grid);
  
    this.score        = 0;
    this.over         = false;
    this.won          = false;
  
    // Update the actuator
    this.actuate();
  };
  
  // Sends the updated grid to the actuator
  GameManager.prototype.actuate = function () {
    this.actuator.actuate(this.grid, {
      score: this.score,
      over:  this.over,
      won:   this.won
    });
  };
  
  // makes a given move and updates state
  GameManager.prototype.move = function(direction) {
    var result = this.grid.move(direction);
    this.score -= result.score;
  
    if (result.won) {
      this.over = true;
    }
  
    //console.log(this.grid.valueSum());
  
    if (!this.grid.movesAvailable()) {
      this.won = true; // You won!
    }
  
    // this.actuate();
  }
  
  GameManager.prototype.addTile = function(data) {
    this.grid.prepareTiles();
    this.grid.computerMove(data.x, data.y, data.value);
    var best = this.ai.getBest();
    if(best) {
      this.move(best.move);
    }
    else {
      this.won = true;
    }
    this.actuate();
  }
  
  // moves continuously until game is over
  GameManager.prototype.run = function() {
    var best = this.ai.getBest();
    this.move(best.move);
    var timeout = animationDelay;
    if (this.running && !this.over && !this.won) {
      var self = this;
      setTimeout(function(){
        self.run();
      }, timeout);
    }
  }
  