@gamepark/rules-api
Version:
API to implement the rules of a board game
175 lines • 8.67 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Money = void 0;
var forEachRight_1 = __importDefault(require("lodash/forEachRight"));
var isEqual_1 = __importDefault(require("lodash/isEqual"));
var keyBy_1 = __importDefault(require("lodash/keyBy"));
var mapValues_1 = __importDefault(require("lodash/mapValues"));
var sumBy_1 = __importDefault(require("lodash/sumBy"));
/**
* This class help manipulate any kind of money with arbitrary unit values, like a set of coins of values 1, 2, 5, 10 for instance.
* @deprecated use {@link MaterialMoney} instead
*/
var Money = /** @class */ (function () {
function Money(units) {
this.units = units;
this.units.sort(function (a, b) { return a - b; });
}
/**
* Count the total value of a material instance
* @param material The material to count
* @returns the sum of each item id multiplied by its quantity
*/
Money.prototype.count = function (material) {
return (0, sumBy_1.default)(material.getItems(), function (item) { var _a, _b; return ((_a = item.id) !== null && _a !== void 0 ? _a : 1) * ((_b = item.quantity) !== null && _b !== void 0 ? _b : 1); });
};
/**
* Perform an operation of adding or removing an amount from a location by creating or deleting items
* @param material The material instance
* @param location The location to filter material onto, and to create new items in
* @param amount Amount to create or delete
* @returns the moves that need to be played to perform the operation
*/
Money.prototype.createOrDelete = function (material, location, amount) {
material = material.location(function (l) { return (0, isEqual_1.default)(l, location); });
var moves = [];
var delta = amount > 0 ? this.gain(amount) : this.spend((0, mapValues_1.default)((0, keyBy_1.default)(this.units), function (unit) { return material.id(unit).getQuantity(); }), -amount);
for (var index = this.units.length - 1; index >= 0; index--) {
var unit = this.units[index];
if (delta[unit] < 0) {
moves.push(material.id(unit).deleteItem(-delta[unit]));
}
else if (delta[unit] > 0) {
moves.push(material.createItem({ id: unit, location: location, quantity: delta[unit] }));
}
}
return moves;
};
/**
* Move an amount of money from a place to another place. It searches after the easiest way to do it, making money with the bank only if necessary.
* @param material Material instance for the money (needs to be unfiltered)
* @param origin Location to remove money from
* @param target Location to move money to
* @param amount Amount of money to transfer
* @returns the moves that need to be played to perform the operation
*/
Money.prototype.moveAmount = function (material, origin, target, amount) {
if (!amount)
return [];
if (amount < 0)
return this.moveAmount(material, target, origin, -amount);
var moves = [];
var originMaterial = material.location(function (l) { return (0, isEqual_1.default)(l, origin); });
var targetMaterial = material.location(function (l) { return (0, isEqual_1.default)(l, target); });
var originUnits = (0, mapValues_1.default)((0, keyBy_1.default)(this.units), function (unit) { return originMaterial.id(unit).getQuantity(); });
var originDelta = this.spend(originUnits, amount);
var targetDelta = this.gain(amount);
for (var index = this.units.length - 1; index >= 0; index--) {
var unit = this.units[index];
if (originDelta[unit] < 0) {
var _loop_1 = function () {
var lowerUnits = this_1.units.slice(0, this_1.units.indexOf(unit));
var targetResult = (0, mapValues_1.default)((0, keyBy_1.default)(lowerUnits), function (unit) { return targetMaterial.id(unit).getQuantity() + targetDelta[unit]; });
var targetResultDelta = this_1.spend(targetResult, unit);
var valueSpent = (0, sumBy_1.default)(lowerUnits, function (unit) { return -targetResultDelta[unit] * unit; });
if (valueSpent === unit && lowerUnits.every(function (lowerUnit) { return targetResultDelta[lowerUnit] < 0; })) {
targetDelta[unit]++;
for (var _i = 0, lowerUnits_1 = lowerUnits; _i < lowerUnits_1.length; _i++) {
var lowerUnit = lowerUnits_1[_i];
targetDelta[lowerUnit] += targetResultDelta[lowerUnit];
}
}
else
return "break";
};
var this_1 = this;
while (targetDelta[unit] < -originDelta[unit]) {
var state_1 = _loop_1();
if (state_1 === "break")
break;
}
var moveAmount = Math.min(-originDelta[unit], targetDelta[unit]);
targetDelta[unit] -= moveAmount;
var originMaterialUnit = originMaterial.id(unit);
if (moveAmount > 0) {
moves.push(originMaterialUnit.moveItem(target, moveAmount));
}
if (moveAmount < -originDelta[unit]) {
moves.push(originMaterialUnit.deleteItem(-originDelta[unit] - moveAmount));
}
}
else if (originDelta[unit] > 0) {
if (targetDelta[unit] < 0) {
moves.push(targetMaterial.id(unit).moveItem(origin, -targetDelta[unit]));
}
else {
moves.push(material.createItem({ id: unit, location: origin, quantity: originDelta[unit] }));
}
}
if (targetDelta[unit] > 0) {
moves.push(material.createItem({ id: unit, location: target, quantity: targetDelta[unit] }));
}
}
return moves;
};
Object.defineProperty(Money.prototype, "record", {
/**
* Creates a new record indexes by all units, with value equal to 0 for each unit
*/
get: function () {
return (0, mapValues_1.default)((0, keyBy_1.default)(this.units), function (_) { return 0; });
},
enumerable: false,
configurable: true
});
/**
* Return the best way to gain an amount, prioritizing the highest unit values
* @param amount Amount to gain, default 1
* @returns the record of coins to earn (only positive values)
*/
Money.prototype.gain = function (amount) {
if (amount === void 0) { amount = 1; }
var gain = this.record;
(0, forEachRight_1.default)(this.units, function (unit) {
gain[unit] = Math.floor(amount / unit);
amount %= unit;
});
return gain;
};
/**
* Return the best way to spend an amount of owned units, prioritizing the smallest unit values
* @param owned Amount of units owned before spending
* @param amount Amount to gain, default 1
* @returns the record of coins to give away and eventually take (positive and negative values)
*/
Money.prototype.spend = function (owned, amount) {
if (amount === void 0) { amount = 1; }
var delta = (0, mapValues_1.default)(owned, function (_) { return 0; });
for (var _1 = 0; _1 < amount; _1++) {
for (var _i = 0, _a = this.units; _i < _a.length; _i++) {
var unit = _a[_i];
if (owned[unit] + delta[unit] > 0) {
delta[unit]--;
if (unit > 1) {
var rest = unit - 1;
for (var _b = 0, _c = this.units.slice(0, this.units.indexOf(unit)).reverse(); _b < _c.length; _b++) {
var lowerUnit = _c[_b];
if (lowerUnit <= rest) {
delta[lowerUnit] += Math.floor(rest / lowerUnit);
rest -= rest % lowerUnit;
}
}
}
break;
}
}
}
return delta;
};
return Money;
}());
exports.Money = Money;
//# sourceMappingURL=money.util.js.map