Board Game Experiment, with JS Prototypes and Canvas

Fences Manipulation

Let’s come back to our main Quoridor object.

Fence Validation

This function checks if a fence is valid. If we provide an opponent as parameter, it actually returns the new path length difference (between this opponent and the current player) that this action would produce, or a boolean.

Quoridor.prototype.validFence = function (fence, limit, opponent) {
  'use strict';
  var i, a, b, h, player = this.players[this.current];
  // #rule-1 : When he has run out of fences, the player must move his pawn.
  if (limit === 0) { this.rule = 1; return false; }
  if (opponent === undefined) { opponent = false; }
  // #rule-4 : The fences must be placed between two sets of two squares.
  for (i = 0; i < this.fences.length; i += 1) {
    a = Math.abs(fence.a - this.fences[i].a);
    b = Math.abs(fence.b - this.fences[i].b);
    h = (fence.horizontal === this.fences[i].horizontal);
    if ((h && ((!fence.horizontal && (a === 0) && (b < 2)) || (fence.horizontal && (b === 0) && (a < 2)))) || ((a === 0) && (b === 0))) {
      // The fence we're trying to put up is overlapping an active one.
      this.rule = 4;
      return false;
    }
  }
  // #rule-5 : The fences can be used to facilitate the player's progress or to impede that of the opponent, however, an access to the goal line must always be left open.
  if (this.closeAccess(fence) && this.openAccess(fence)) {
    if (opponent) { return player.getDifference(opponent); }
    return true;
  }
  // It wasn't valid to close the accesses, we must have isolated a pawn.
  if (this.openAccess(fence)) {
    // This "if" ensures us that the accesses will be reseted in time before the end of the current execution
    this.rule = 5;
    return false;
  }
  return false;
};

Note at the end of this method that we close the accesses involved in this action. Then we return a difference or true in case of success, false if the fence is invalid, but after we made sure that we restored those accesses.

In short, we apply our action as a test, undo that same action, return the result of the test.

Modify the accesses

Now those methods in charge of applying and restoring the accesses involved (by the fence sent as parameter) are quite simple.

Quoridor.prototype.closeAccess = function (fence) {
  'use strict';
  // We close the concerned accesses (by setting them to false)
  if (fence.horizontal) {
    this.board[fence.a][fence.b + 1].access.up = false;
    this.board[fence.a + 1][fence.b + 1].access.up = false;
    this.board[fence.a][fence.b].access.down = false;
    this.board[fence.a + 1][fence.b].access.down = false;
  } else {
    this.board[fence.a][fence.b].access.right = false;
    this.board[fence.a][fence.b + 1].access.right = false;
    this.board[fence.a + 1][fence.b].access.left = false;
    this.board[fence.a + 1][fence.b + 1].access.left = false;
  }
  // We'll know if we're isolating any pawn by returning this function
  return this.validPawns();
};

Quoridor.prototype.openAccess = function (fence) {
  'use strict';
  // We open the accesses concerned by setting them to the position's object (Player or Square)
  if (fence.horizontal) {
    this.board[fence.a][fence.b + 1].access.up = this.getPosition(fence.a, fence.b);
    this.board[fence.a + 1][fence.b + 1].access.up = this.getPosition(fence.a + 1, fence.b);
    this.board[fence.a][fence.b].access.down = this.getPosition(fence.a, fence.b + 1);
    this.board[fence.a + 1][fence.b].access.down = this.getPosition(fence.a + 1, fence.b + 1);
  } else {
    this.board[fence.a][fence.b].access.right = this.getPosition(fence.a + 1, fence.b);
    this.board[fence.a][fence.b + 1].access.right = this.getPosition(fence.a + 1, fence.b + 1);
    this.board[fence.a + 1][fence.b].access.left = this.getPosition(fence.a, fence.b);
    this.board[fence.a + 1][fence.b + 1].access.left = this.getPosition(fence.a, fence.b + 1);
  }
  return true;
};