mau-mau
Version:
Card game for 2 or more players, popular in Czech Republic, Germany and other lands
366 lines (309 loc) • 13.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { 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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _Computer2 = require('./Computer');
var _Computer3 = _interopRequireDefault(_Computer2);
var _Ranks = require('./../cards/Ranks');
var _Ranks2 = _interopRequireDefault(_Ranks);
var _Suits = require('./../cards/Suits');
var _Suits2 = _interopRequireDefault(_Suits);
var _i18n = require('../misc/i18n');
var _i18n2 = _interopRequireDefault(_i18n);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
// Plays a card from his hand, preferring a seven or an ace to other cards
// and leaving the queens to the end. He choses the seven or ace, which
// allows shedding the maximum cards, when the other sevens and aces were
// played one after another. If only two players are playing, he avoids
// playing sevens or aces, unless he has another card of the same suite to
// cover them, or there is no other card to play. He only plays a queen,
// if there is no other playable card, and he wishes the suit of a card
// remaining in his hand, which we would play next time according to the
// same rules.
var SmartComputer = function (_Computer) {
_inherits(SmartComputer, _Computer);
function SmartComputer() {
_classCallCheck(this, SmartComputer);
return _possibleConstructorReturn(this, (SmartComputer.__proto__ || Object.getPrototypeOf(SmartComputer)).apply(this, arguments));
}
_createClass(SmartComputer, [{
key: 'chooseCard',
value: function chooseCard() {
var availableCards = this.hand.pickCards(),
playableCards = this.game.rules.pickPlayableCards(this.hand),
chosenCard = suggestCard.call(this, availableCards, playableCards),
card = chosenCard.card;
return new Promise(function (resolve, reject) {
if (card) {
resolve(card);
} else {
reject();
}
});
}
}, {
key: 'toString',
value: function toString() {
return _i18n2.default.translate('smart $[1]', _Computer3.default.prototype.toString.apply(this, arguments));
}
}]);
return SmartComputer;
}(_Computer3.default);
function suggestCard(availableCards, playableCards) {
// Decomposition operation should not increase the statement count,
// moving handling of sevens and aces to a separate method would
// make the code less legible.
/*eslint max-statements: [2, 20]*/
// Moving handling of sevens, aces and queens from conditions
// to methods would make the code less legible.
/*eslint complexity: [2, 11]*/
// No playable card found will mean drawing a new one
if (!playableCards.length) {
return {};
}
// Try to get rid of all cards except for queens,
// sevens and aces may need looking at the game
var _divideCardRanks = divideCardRanks(playableCards),
otherCards = _divideCardRanks.otherCards,
queens = _divideCardRanks.queens,
sevens = _divideCardRanks.sevens,
aces = _divideCardRanks.aces,
chosenCard = {};
// Prefer to "feed cards" the other players by sevens
if (sevens.length) {
chosenCard = getMaximumCoveredSeven.call(this, sevens, availableCards);
// If there are just two players, avoid "being fed back" two cards
// by another seven placed on ours; do not worry with more players
if (!chosenCard.card && this.game.activePlayers.length > 2) {
// It does not make much sense picking a covered seven with more then
// two players, but nevertheless, when three players play, maybe the
// third player will not have the suite and have to draw a card...
chosenCard = pickRandomCard(sevens);
}
}
// Prefer to "shed cards" quicker by a batch of aces
if (!chosenCard.card && aces.length) {
chosenCard = getMaximumCoveredAce.call(this, aces, availableCards);
// If there are just two players, avoid an immediate drawing a single
// card to let the other player play; do not worry with more players
if (!chosenCard.card && this.game.activePlayers.length > 2) {
// It does not make much sense picking a covered ace with more then
// two players, but nevertheless, when three players play, maybe the
// third player will play the same suit or have to draw a card...
chosenCard = pickRandomCard(aces);
}
}
// If no seven was safe and no ace was covered, play other card
// and if no other card remains, play a seven, an ace or a queen
if (!chosenCard.card) {
if (otherCards.length) {
chosenCard = pickRandomCard(otherCards);
} else if (sevens.length) {
chosenCard = pickRandomCard(sevens);
} else if (aces.length) {
chosenCard = pickRandomCard(aces);
} else {
chosenCard = pickRandomCard(queens);
}
}
// If a queen is played, choose the suit of a remaining card
// which we want to play in the next round; if the queen is
// not our last card to play now
if (chosenCard.card.rank === _Ranks2.default.queen && availableCards.length > 1) {
this.game.rules.chosenSuit = suggestSuit.call(this, chosenCard, availableCards);
}
return chosenCard;
}
function suggestSuit(chosenCard, availableCards) {
var _this2 = this;
// Cards remaining in hand after playing the queen
var otherCards = availableCards.filter(function (card) {
return card !== chosenCard.card;
}),
nextChosenCardsPerSuit = Object.keys(_Suits2.default).map(function (suit) {
var playableCards = _this2.game.rules.pickPlayableCardsForSuit(otherCards, suit);
return suggestCard.call(_this2, otherCards, playableCards);
}),
_filterMaximumCovers = filterMaximumCovers(nextChosenCardsPerSuit),
maximumCovers = _filterMaximumCovers.maximumCovers,
maximumCover = _filterMaximumCovers.maximumCover,
nextChosenCards = maximumCovers.map(function (cardCover) {
return cardCover.card;
}).filter(function (card) {
return !!card;
}),
nextChosenCard = nextChosenCards.find(function (card) {
return card.rank === _Ranks2.default.seven;
}) || nextChosenCards.find(function (card) {
return card.rank === _Ranks2.default.ace;
}) || nextChosenCards.find(function (card) {
return card.rank !== _Ranks2.default.queen;
}) ||
// If only queens remain in the hand, the chosen suit does
// not matter; let us return the suit of the first queen
nextChosenCards[0];
return nextChosenCard.suit;
}
function divideCardRanks(playableCards) {
var queens = [],
sevens = [],
aces = [],
otherCards = playableCards.filter(function (card) {
switch (card.rank) {
case _Ranks2.default.queen:
queens.push(card);
return false;
case _Ranks2.default.seven:
sevens.push(card);
return false;
case _Ranks2.default.ace:
aces.push(card);
return false;
}
return true;
});
return { otherCards: otherCards, queens: queens, sevens: sevens, aces: aces };
}
function pickRandomCard(cards) {
var cardIndex = Math.trunc(cards.length * Math.random());
return {
card: cards[cardIndex],
count: 1
};
}
function getMaximumCoveredSeven(sevens, availableCards) {
var _this3 = this;
// Compute which seven can help us shed the maximum card count
var covers = sevens.map(function (seven) {
return {
card: seven,
count: getSevenCoverCount.call(_this3, seven, availableCards)
};
});
return pickMaximumCover(covers);
}
function getSevenCoverCount(seven, availableCards) {
var _this4 = this;
var otherCards = availableCards.filter(function (card) {
return card !== seven;
}),
// Check if the seven can be covered by a card, which lets
// the other player play
coveredBySingleCard = otherCards.find(function (card) {
return card.rank !== _Ranks2.default.seven && card.rank !== _Ranks2.default.ace && seven.suit === card.suit;
}),
// Gather sevens, which could cover the current one
coverCountsByOtherSeven = otherCards.map(function (card) {
if (card.rank === _Ranks2.default.seven) {
// Compute how many cards can cover this another seven
var yetOtherCards = otherCards.filter(function (otherCard) {
return card !== otherCard;
}),
coverCount = getSevenCoverCount.call(_this4, card, yetOtherCards);
return coverCount + 1;
}
return 0;
}),
// Gather aces, which could cover the seven
coverCountsByOtherAce = otherCards.map(function (card) {
if (card.rank === _Ranks2.default.ace && seven.suit === card.suit) {
// Compute how many cards can cover this another ace
var yetOtherCards = otherCards.filter(function (otherCard) {
return card !== otherCard;
}),
coverCount = getAceCoverCount.call(_this4, card, yetOtherCards);
return coverCount + 1;
}
return 0;
}),
// Compute which seven or ace can help us shed the maximum card count
coverCounts = coverCountsByOtherSeven.concat(coverCountsByOtherAce),
maximumCoverCount = Math.max.apply(undefined, coverCounts);
// If the seven cannot be covered by another seven or ace,
// from which the most efficient would be picked, return 1,
// if other single card was found, otherwise 0
return maximumCoverCount || coveredBySingleCard && 1 || 0;
}
function getMaximumCoveredAce(aces, availableCards) {
var _this5 = this;
// Compute which ace can help us shed the maximum card count
var covers = aces.map(function (ace) {
return {
card: ace,
count: getAceCoverCount.call(_this5, ace, availableCards)
};
});
return pickMaximumCover(covers);
}
function getAceCoverCount(ace, availableCards) {
var _this6 = this;
var otherCards = availableCards.filter(function (card) {
return card !== ace;
}),
// Check if the ace can be covered by a card, which lets
// the other player play
coveredBySingleCard = otherCards.find(function (card) {
return card.rank !== _Ranks2.default.seven && card.rank !== _Ranks2.default.ace && ace.suit === card.suit;
}),
// Gather aces, which could cover the current one
coverCountsByOtherAce = otherCards.map(function (card) {
if (card.rank === _Ranks2.default.ace) {
// Compute how many cards can cover this another ace
var yetOtherCards = otherCards.filter(function (otherCard) {
return card !== otherCard;
}),
coverCount = getAceCoverCount.call(_this6, card, yetOtherCards);
return coverCount + 1;
}
return 0;
}),
// Gather sevens, which could cover the ace
coverCountsByOtherSeven = otherCards.map(function (card) {
if (card.rank === _Ranks2.default.seven && ace.suit === card.suit) {
// Compute how many cards can cover this another seven
var yetOtherCards = otherCards.filter(function (otherCard) {
return card !== otherCard;
}),
coverCount = getSevenCoverCount.call(_this6, card, yetOtherCards);
return coverCount + 1;
}
return 0;
}),
// Compute which ace or seven can help us shed the maximum card count
coverCounts = coverCountsByOtherAce.concat(coverCountsByOtherSeven),
maximumCoverCount = Math.max.apply(undefined, coverCounts);
// If the ace cannot be covered by another ace or seven,
// from which the most efficient would be picked, return 1,
// if other single card was found, otherwise 0
return maximumCoverCount || coveredBySingleCard && 1 || 0;
}
function pickMaximumCover(covers) {
var _filterMaximumCovers2 = filterMaximumCovers(covers),
maximumCovers = _filterMaximumCovers2.maximumCovers,
maximumCover = _filterMaximumCovers2.maximumCover;
if (maximumCovers.length > 1) {
var coverIndex = Math.trunc(maximumCovers.length * Math.random());
maximumCover = maximumCovers[coverIndex];
}
return maximumCover;
}
function filterMaximumCovers(covers) {
var maximumCovers = [],
maximumCover = { count: 0 };
// Pick the card, which allows us to shed the most cards
covers.forEach(function (cover) {
if (cover.count > maximumCover.count) {
maximumCover = cover;
maximumCovers = [maximumCover];
} else if (cover.count === maximumCover.count) {
maximumCovers.push(cover);
}
});
return { maximumCovers: maximumCovers, maximumCover: maximumCover };
}
exports.default = SmartComputer;
//# sourceMappingURL=SmartComputer.js.map