backgammon
Version:
a clean api for building a backgammon game
429 lines (340 loc) • 12.1 kB
JavaScript
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
Object.defineProperty(Constructor, "prototype", {
writable: false
});
return Constructor;
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _createForOfIteratorHelperLoose(o, allowArrayLike) {
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
if (it) return (it = it.call(o)).next.bind(it);
if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
if (it) o = it;
var i = 0;
return function () {
if (i >= o.length) return {
done: true
};
return {
done: false,
value: o[i++]
};
};
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var Point = /*#__PURE__*/function () {
function Point(position, checkers) {
if (checkers === void 0) {
checkers = [];
}
this.position = position;
this.checkers = checkers;
} // ! not in use
var _proto = Point.prototype;
_proto.relativePositionFromHome = function relativePositionFromHome(player) {
if (player.color === PlayerColor.white) {
return this.position;
} else {
return player.home.position - this.position;
}
};
_proto.isHouse = function isHouse() {
return this.checkers.length > 1;
};
_proto.isSingle = function isSingle() {
return this.checkers.length === 1;
} // todo rename includesCheckersOf
;
_proto.includesCheckerOf = function includesCheckerOf(player) {
if (!this.checkers.length) {
return false;
}
return !!this.checkers.find(function (c) {
return c.color === player.color;
});
};
_proto.isAvailableFor = function isAvailableFor(player) {
if (!this.includesCheckerOf(player) && this.isHouse()) {
return false;
}
return true;
};
Point.getPointRefByPosition = function getPointRefByPosition(position, points) {
return points.find(function (p) {
return p.position === position;
});
};
_createClass(Point, [{
key: "oppisetPosition",
get: function get() {
return 25 - this.position;
}
}]);
return Point;
}();
var PlayerColor;
(function (PlayerColor) {
PlayerColor["black"] = "black";
PlayerColor["white"] = "white";
})(PlayerColor || (PlayerColor = {}));
var PlayerHome;
(function (PlayerHome) {
PlayerHome[PlayerHome["white"] = 0] = "white";
PlayerHome[PlayerHome["black"] = 25] = "black";
})(PlayerHome || (PlayerHome = {}));
var PlayerPrison;
(function (PlayerPrison) {
PlayerPrison[PlayerPrison["white"] = 25] = "white";
PlayerPrison[PlayerPrison["black"] = 0] = "black";
})(PlayerPrison || (PlayerPrison = {}));
var Player = function Player(color) {
this.home = new Point(PlayerHome[color]);
this.prison = new Point(PlayerPrison[color]);
this.color = color;
};
var Players = /*#__PURE__*/function () {
// todo use infer on starter, to make sure is's one of the first 2 args
function Players(white, black, starter) {
this.white = white;
this.black = black;
this._current = starter;
}
var _proto = Players.prototype;
// todo hide from public api
_proto.toggle = function toggle() {
this._current = this.second;
return this.current;
};
_createClass(Players, [{
key: "current",
get: function get() {
return this._current;
}
}, {
key: "second",
get: function get() {
return this.current === this.white ? this.black : this.white;
}
}, {
key: "White",
get: function get() {
return this.white;
}
}, {
key: "Black",
get: function get() {
return this.black;
}
}]);
return Players;
}();
var Dices = /*#__PURE__*/function () {
function Dices(numbers) {
this.numbers = numbers;
this.isDouble = false;
if (Dices.dicesAreDouble(this)) {
var _this$numbers;
this.isDouble = true;
(_this$numbers = this.numbers).push.apply(_this$numbers, this.numbers);
}
}
Dices.roll1Dice = function roll1Dice() {
return Math.floor(Math.random() * 6 + 1);
};
Dices.roll2Dices = function roll2Dices() {
return new Dices([this.roll1Dice(), this.roll1Dice()]);
};
Dices.dicesAreDouble = function dicesAreDouble(_ref) {
var numbers = _ref.numbers;
return numbers[0] === numbers[1];
} // a weird bug makes tests fail when setting the return type to [DiceResut, DiceResut]
;
Dices.getStarterDices = function getStarterDices() {
var dices;
do {
dices = new Dices([this.roll1Dice(), this.roll1Dice()]);
} while (this.dicesAreDouble(dices));
return dices;
};
return Dices;
}();
var Checker = function Checker(color) {
this.color = color;
};
var initialPointsState = [/*#__PURE__*/new Point(1, /*#__PURE__*/repeatCheckers(2, PlayerColor.black)), /*#__PURE__*/new Point(2), /*#__PURE__*/new Point(3), /*#__PURE__*/new Point(4), /*#__PURE__*/new Point(5), /*#__PURE__*/new Point(6, /*#__PURE__*/repeatCheckers(5, PlayerColor.white)), /*#__PURE__*/new Point(7), /*#__PURE__*/new Point(8, /*#__PURE__*/repeatCheckers(3, PlayerColor.white)), /*#__PURE__*/new Point(9), /*#__PURE__*/new Point(10), /*#__PURE__*/new Point(11), /*#__PURE__*/new Point(12, /*#__PURE__*/repeatCheckers(5, PlayerColor.black)), /*#__PURE__*/new Point(13, /*#__PURE__*/repeatCheckers(5, PlayerColor.white)), /*#__PURE__*/new Point(14), /*#__PURE__*/new Point(15), /*#__PURE__*/new Point(16), /*#__PURE__*/new Point(17, /*#__PURE__*/repeatCheckers(3, PlayerColor.black)), /*#__PURE__*/new Point(18), /*#__PURE__*/new Point(19, /*#__PURE__*/repeatCheckers(5, PlayerColor.black)), /*#__PURE__*/new Point(20), /*#__PURE__*/new Point(21), /*#__PURE__*/new Point(22), /*#__PURE__*/new Point(23), /*#__PURE__*/new Point(24, /*#__PURE__*/repeatCheckers(2, PlayerColor.white))];
function repeatCheckers(count, color) {
var list = [];
for (var i = 0; i < count; i++) {
list.push(new Checker(PlayerColor[color]));
}
return list;
}
// todo fix this type generator (not working)
// usage example: UntilRange<25>
// result: 0 | 1 | 2 ... | 24
// export type UntilRange<Max extends number> = number extends Max
// ? number
// : _Range<Max, []>;
// type _Range<Max extends number, Arr extends number[]>
// = Arr['length'] extends Max
// ? Arr[number]
// : _Range<Max, [...Arr, Arr['length']]>;
// todo write tests
function unshiftFrom(arr, item) {
var index = arr.indexOf(item);
if (index > -1 && index < arr.length) {
var _arr$splice = arr.splice(index, 1),
itemToMove = _arr$splice[0];
arr.unshift(itemToMove);
}
return arr;
}
// submittedMove: MovePath[];
// }
var Board = /*#__PURE__*/function () {
// private turnsHistory: CompletedTurn[] = [];
function Board() {
// todo make private whatever is possible
this.points = initialPointsState;
var white = new Player(PlayerColor.white);
var black = new Player(PlayerColor.black);
var dices = Dices.getStarterDices();
var starter = dices.numbers[0] > dices.numbers[1] ? white : black;
this.players = new Players(white, black, starter);
this.currentTurn = {
player: starter,
dices: dices,
possibleMoves: this.getAllMovePaths(dices, starter)
};
}
var _proto = Board.prototype;
_proto.submitMove = function submitMove(submittedMove) {
this.generateNextMove();
};
_proto.generateNextMove = function generateNextMove() {
var dices = Dices.roll2Dices();
var player = this.players.toggle();
var possibleMoves = this.getAllMovePaths(dices, player);
this.currentTurn = {
dices: dices,
player: player,
possibleMoves: possibleMoves
}; // todo test the logic
// if (possibleMoves.length === 0) {
// this.submitMove([]);
// }
};
_proto.PlayerInPrison = function PlayerInPrison(player) {
if (player === void 0) {
player = this.players.current;
}
return player.prison.checkers.length > 0;
};
_proto.currentPlayerCanBearOff = function currentPlayerCanBearOff() {
var _this = this;
if (this.PlayerInPrison()) {
return false;
}
var points = this.getPlayersPoints();
return points.every(function (point) {
return point.relativePositionFromHome(_this.players.current) <= 6;
});
};
_proto.getPlayersPoints = function getPlayersPoints(player) {
if (player === void 0) {
player = this.players.current;
}
return this.points.filter(function (point) {
return point.includesCheckerOf(player);
});
};
_proto.score = function score(player) {
var playersPoints = this.getPlayersPoints(player);
if (this.PlayerInPrison(player)) {
playersPoints.push(player.prison);
}
var totalScore = 0;
for (var _iterator = _createForOfIteratorHelperLoose(playersPoints), _step; !(_step = _iterator()).done;) {
var point = _step.value;
totalScore += point.relativePositionFromHome(player) * point.checkers.length;
}
return totalScore;
};
_proto.getTargetPoint = function getTargetPoint(point, dice, player) {
var position = player.color === PlayerColor.white ? Math.max(point.position - dice, player.home.position) : Math.min(point.position + dice, player.home.position);
return Point.getPointRefByPosition(position, this.points);
};
_proto.getMovePathsForPoint = function getMovePathsForPoint(point, dices, player) {
var _this2 = this;
var pointPaths = [];
var LoopDices = function LoopDices(dices) {
var movePath = [];
var distance = 0;
var lastPoint = point;
for (var _iterator2 = _createForOfIteratorHelperLoose(dices), _step2; !(_step2 = _iterator2()).done;) {
var dice = _step2.value;
var target = _this2.getTargetPoint(point, distance + dice, player);
if (!target || !target.isAvailableFor(player)) {
break;
}
movePath.push({
from: lastPoint,
to: target,
uses: dice
});
distance += dice;
lastPoint = target;
}
if (movePath.length > 0) {
pointPaths.push(movePath);
}
};
dices.numbers.forEach(function (dice) {
LoopDices(unshiftFrom(dices.numbers, dice));
});
return pointPaths;
};
_proto.getAllMovePaths = function getAllMovePaths(dices, player) {
if (player === void 0) {
player = this.players.current;
}
// todo check if in prison
var allMovePaths = [];
var points = this.getPlayersPoints(player);
for (var _iterator3 = _createForOfIteratorHelperLoose(points), _step3; !(_step3 = _iterator3()).done;) {
var point = _step3.value;
var moves = this.getMovePathsForPoint(point, dices, player);
if (moves.length > 0) {
allMovePaths.push.apply(allMovePaths, moves);
}
}
return allMovePaths;
};
return Board;
}();
export { Board };
//# sourceMappingURL=backgammon.esm.js.map