Skip to content

Instantly share code, notes, and snippets.

@finscn
Last active August 29, 2015 13:57
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save finscn/9744730 to your computer and use it in GitHub Desktop.
Save finscn/9744730 to your computer and use it in GitHub Desktop.
检测 两组任意(不限个数 不限花色) 的扑克牌, 混到一起后能否构成任意长度(大于1 小于15)的顺子.A代表1或14,小王代表1--5,大王代表1--14
/*
检测 两组任意(不限个数 不限花色) 的扑克牌, 混到一起后能否构成任意长度(大于1 小于15)的顺子.
A可表示1或14,小王可表示1--5,大王可表示1--14
说明:
1 我微博里说 小王代表 1到9有误, 我这边实际遇到的场景是 小王代表 1--5 (当然 这个无所谓)
2 牌是可以有重复的 : 例如 可以 4个小王 9个大王 等等, 可以理解为是从无数副扑克牌当中随意抽取出两组
3 组成的顺子 可以不是5张 , 其实严格说 就是 等差为1的等差数列
4 能且只能组成一组顺子, 组成顺子后, 牌没有剩余
*/
// cards 结构: [ {point:牌面点数, num:对应数值}, .... ]
function checkSequence(cards1, cards2) {
var totalCount = cards1.length + cards2.length;
//总牌数>14, 必然无法构成等差数列
if (totalCount > 14) {
return false;
}
var allCards = []; // 普通数字牌对应数组
var countA = 0; // A数量
var countRedJoker = 0; // 大王数量
var countBlackJoker = 0; // 小王数量
var _cards = cards1;
while (true) {
_cards.forEach(function(card) {
// 记录 A 大王 小王的个数
if (card.point == "A") {
countA++;
} else if (card.point == "RED_JOKER") {
countRedJoker++;
} else if (card.point == "BLACK_JOKER") {
countBlackJoker++;
} else {
// 把普通数字牌混为一组
allCards.push(card);
}
});
if (_cards == cards2) {
break;
}
_cards = cards2;
}
// A大于2个 或者 虽然A等于2个,但总牌数小于14, 则必然无法构成等差数列
if (countA > 2 || countA == 2 && totalCount < 14) {
return false
}
// 对普通数字牌排序
allCards.sort(function(a, b) {
return a.num - b.num;
});
var ANA, AN, NA;
if (countA == 2) {
//如果有两个A, 那么一个A当做1 放到最前面, 另一个A当做14, 放到后面
ANA = [{
point: "A",
num: 1
}].concat(allCards).concat([{
point: "A",
num: 14
}]);
} else if (countA == 1) {
// 只有一个A 时, 把A放前面测一次, 放后面再测一次, 这样做代码上简单, 但是性能肯定不好
AN = [{
point: "A",
num: 1
}].concat(allCards);
NA = [].concat(allCards).concat({
point: "A",
num: 14
});
}
if (ANA) {
return _checkQueue(ANA, countRedJoker, countBlackJoker);
} else if (AN) {
return _checkQueue(AN, countRedJoker, countBlackJoker) || _checkQueue(NA, countRedJoker, countBlackJoker);
} else {
return _checkQueue(allCards, countRedJoker, countBlackJoker);
}
}
function _checkQueue(cardList, countRedJoker, countBlackJoker) {
console.log(cardList)
console.log(countRedJoker)
console.log(countBlackJoker)
// 小王代表的最大数
var blackJokerMax = 5;
var prevNum = cardList[0].num;
for (var i = 1; i < cardList.length; ) {
var currentNum = cardList[i].num;
if (prevNum === currentNum) {
// 有重复的牌, 不是等差数列
return false;
}
var useJoker = false
// 如果两张相邻牌的差不是1, 尝试用王补位
if (prevNum + 1 != currentNum) {
//blackJokerMax之后的, 用大王补位
if (prevNum + 1 > blackJokerMax) {
if (countRedJoker > 0) {
countRedJoker--;
useJoker = true;
} else {
return false;
}
} else {
//blackJokerMax或之前的, 有小王, 优先用小王补位
if (countBlackJoker > 0) {
countBlackJoker--;
useJoker = true;
} else if (countRedJoker > 0) {
//blackJokerMax或之前的, 没有小王 尝试用大王补位
countRedJoker--;
useJoker = true;
} else {
// 没有王, 则无法构成等差数列
return false;
}
}
}
if (useJoker) {
//如果用王补过位, 则做相应的处理
prevNum += 1;
} else {
prevNum = currentNum;
i++;
}
}
var firstNum = cardList[0].num;
var lastNum = cardList[cardList.length - 1].num;
// 此时队列中间部分已经排列好, 剩余的小王只能往前分配, 大王可能往前 可能往后
// 剩余小王数大于队列前端能分配的最大数量, 则不是等差数列
if (countBlackJoker >= firstNum) {
return false
}
// 如果有未分配的小王
if (countBlackJoker > 0) {
// 如果此时数列最小值>blackJokerMax+1, 则小王不能直接分配到数列前面,需要先用若干个大王来补位
if (firstNum > blackJokerMax + 1) {
//有大王 用大王补位,否则返回false
if (countRedJoker >= firstNum - (blackJokerMax + 1)) {
countRedJoker -= firstNum - (blackJokerMax + 1);
firstNum = blackJokerMax + 1;
} else {
return false;
}
}
// 分配所有剩余的小王 因为前面的一些预判断, 下面两行代码总是能得到预期结果的
firstNum -= countBlackJoker;
countBlackJoker = 0;
}
// 至此, 小王全部分配完毕
// 如果有未分配的大王
if (countRedJoker > 0) {
// 优先尝试往后分配,且尽可能多的分配
if (lastNum < 14) {
countRedJoker -= (14 - lastNum);
}
// 如果还有未分配的 大王, 且队列前面已经装不下了, 返回false
if (countRedJoker >= firstNum) {
return false;
}
countRedJoker = 0;
}
//如果大小王都分配完毕, 则符合等差数列
if (countBlackJoker == 0 && countRedJoker == 0) {
return true;
}
return false;
}
////////////////////////////////////
////////////////////////////////////
////////////////////////////////////
////////////////////////////////////
// 以下为测试用例
// 定义常量
// 牌面点数 : 对应数值
var Card = {
"2": 2,
"3": 3,
"4": 4,
"5": 5,
"6": 6,
"7": 7,
"8": 8,
"9": 9,
"10": 10,
"J": 11,
"Q": 12,
"K": 13,
"A": null,
"RED_JOKER": null,
"BLACK_JOKER": null,
};
function test(c1, c2) {
var cards1 = [],
cards2 = [];
c1.forEach(function(c) {
cards1.push({
point: c,
num: Card[c]
})
});
c2.forEach(function(c) {
cards2.push({
point: c,
num: Card[c]
})
});
console.log(checkSequence(cards1, cards2));
}
// 返回 true
test([2, 9, "A", "RED_JOKER", "BLACK_JOKER"], ["RED_JOKER", "BLACK_JOKER", 3,8])
// 返回 false
test(["BLACK_JOKER", "BLACK_JOKER"], ["RED_JOKER", "RED_JOKER", 8, 9,10,"J","Q","A"])
@finscn
Copy link
Author

finscn commented Mar 24, 2014

补充说明:
1 我微博里说 小王代表 1到9有误, 我这边实际遇到的场景是 小王代表 1--5 (当然 这个无所谓)
2 牌是可以有重复的 : 例如 可以 4个小王 9个大王 等等, 可以理解为是从无数副扑克牌当中随意抽取出两组
3 组成的顺子 可以不是5张 , 其实严格说 就是 等差为1的等差数列
4 能且只能组成一组顺子, 组成顺子后, 牌没有剩余

我这个代码 肯定不是最优, 又臭又长, 但是测试了一下, 至少结果都正确 (当然测试用例覆盖率可能不够)

求优化 求纠错 谢谢了

@ssnau
Copy link

ssnau commented Mar 24, 2014

Updated,约40行,成功会返回合法的顺子,失败返回false,小王代表1--5,可配置。

function checkSequence(deck1, deck2) {
    var cards = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], //cards长度是大于15的0串,具体长度不重要
        val = 0, // tmp
        deck  = deck1.concat(deck2),
        boundary = 6, // 小王的边界
        magic = {A: [], B: [], R: []}; //用于存储A, 小王, 大王的栈

    if (deck.length > 14) return false;

    // 收集A,小王,大王;填上辅助数组cards,要求cards[i] = i,无法满足情况的则cards[i] = 0
    for (var i = 0; val = deck[i]; i++) {
        if (val === 'A' || /^B/.test(val) || /^R/.test(val)) {
            magic[val.charAt(0)].push(val);
            if (magic.A.length > 2) return false; //超过两个A,直接返回false
        } else {
            if (cards[val]) return false; // 有重复的牌,返回False
            cards[val] = val;
        }
    }

    // 枚举, head表示用A填上1,tail表示用A填上14
    function tryFill(head, tail) {
        for (var start = 1; start <= 14; start++) {
            var temp = cards.concat(),
                B    = magic.B.concat(),
                R    = magic.R.concat();
            temp[1] = head ? 'A' : 0;
            temp[14] = tail ? 'A' : 0;
            for (var i = start; i <= 14; i++) {
                temp[i] = temp[i] || ((i < boundary && B.length) ? B.pop() : R.pop()) || 0;
                // 把大小王用尽,检查temp数列是否是顺子,如果是,则直接返回结果
                if ((B.length + R.length === 0) && !/,0,/.test(temp.join(',').replace(/^(0,)*|(,0)*$/g, ''))) return temp; 
            }
        }
        return false;
    }

    if (magic.A.length === 1)  return tryFill(true, false) || tryFill(false, true);
    return tryFill(!!magic.A.length, !!magic.A.length);
}

/*
测试
*/
function test(deck1, deck2, val) {
    console.log('----------------------');
    console.log("input", deck1, deck2);
    var res = checkSequence(deck1, deck2);
    if (!res) {
        console.log("failed")
    } else {
        res = res.join(',').replace(/^(0,)*|(,0)*$/g, '').replace(/,/g, ', ');
        console.log("success: ["+ res +"]");
    }
    if (!!res === val) console.log('assertion passed!');
}
// Only accept "A",2,3,4,5,6,7,8,9,10,11,12,13,"BLACK_JOKER","RED_JOKER"

test([4, 6, 8, 9, 10, 2, 'BLACK_JOKER'], ['BLACK_JOKER', 'BLACK_JOKER', 'RED_JOKER', 'RED_JOKER'], true); // true
test(['A', 2, 3, 4, 5, 6], [7, 8, 9, 'BLACK_JOKER', 'RED_JOKER'], false); //fail, 小王没地方放
test([12, 13], ['BLACK_JOKER'], false); //false, 小王与12和13连不上
test(['BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER'], ['BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'RED_JOKER'], false); // 超过了5个小王,第六个放不下
test(['BLACK_JOKER'], ['BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'RED_JOKER'], true); // success
test(['A', 5, 'A', 7], [8, 10, 'BLACK_JOKER', 'RED_JOKER'], false); // fail
test(['A', 'A', 'A', 6, 'BLACK_JOKER', 'BLACK_JOKER'], ['BLACK_JOKER', 'RED_JOKER'], false); // fail
test([3, 4, 5, 6, 7, 8, 9], [10], true); // success
test(['A', 4], ['BLACK_JOKER'], false); // fail
test(['A', 12], [13], true); // success
test(['A', 2, 3, 4], [5, 6, 7, 8, 9, 10, 11, 12, 13, 'RED_JOKER'], false); // success
test(['A', 3, 3],  [4, 5, 'BLACK_JOKER'], false); // fail

// 抄了楼下的测试用例(*_*)
test([2,9,"A","RED_JOKER","BLACK_JOKER"],["RED_JOKER","BLACK_JOKER",3,8], true);//true
test([5,6,"A","A",8,9,10,11,12],["RED_JOKER","RED_JOKER", "BLACK_JOKER", 3,3], false);//false
test([5,6,"A","A",8,9,10,11,12],["RED_JOKER","RED_JOKER", "BLACK_JOKER", 3,4], true);//true
test(["BLACK_JOKER","BLACK_JOKER"],["RED_JOKER","RED_JOKER",8,9,10,11,12,"A"], false);//false
test([6,"RED_JOKER","A",8,9,10,12],["RED_JOKER","RED_JOKER", "BLACK_JOKER"], true);//true
test([6,"A",8,9,10,12],["RED_JOKER","RED_JOKER", "BLACK_JOKER"], false);//false

test([2, 9, "A", "RED_JOKER", "BLACK_JOKER"], ["RED_JOKER", "BLACK_JOKER", 3,8], true); // true
test(['A',2,3,4,5,6,7,8,9,10, 11, 12, 13], ['A'], true); //true
test(['A',2,3,4,5,6,7,8,9], ['A', 'A'], false); // false
test(['BLACK_JOKER',2,3,4,5,6,7,8,9,10, 11, 12, 13], ['A'], true); // true
test(['BLACK_JOKER',2,3,4,5,6,7,8,9,10, 11, 12], ['A'], false); //false
test(['BLACK_JOKER',2,3,4,5,6,7,8,9,10, 11, 12, 13], ['BLACK_JOKER'], false); //false

@lyDASHsBOK
Copy link

这一条好像过不了
test(['BLACK_JOKER','BLACK_JOKER','BLACK_JOKER','BLACK_JOKER','BLACK_JOKER','BLACK_JOKER','BLACK_JOKER','BLACK_JOKER',10,'J','Q'], ['A']);

@lyDASHsBOK
Copy link

@ssnau 的程序好像也一样
test([15,15,15,15,15,15,15,15,10,11,12,1]);
输出判定失败

@lyDASHsBOK
Copy link

贴个渣实现

/**
 * Created by JetBrains PhpStorm.
 * User: xliu
 * Date: 24/03/14
 * Time: 17:22
 */

/**
 * @param {String} name
 * */
function CardVO(name)
{
    this.name = name;
    this.value = CardVO.VALUE[name];
}
CardVO.VALUE = {
    A: 1,
    2: 2,
    3: 3,
    4: 4,
    5: 5,
    6: 6,
    7: 7,
    8: 8,
    9: 9,
    10: 10,
    J: 11,
    Q: 12,
    K: 13,
    BLACK_JOKER: 100,
    RED_JOKER: 101
};

/**
 * @param {CardVO} cardA
 * @param {CardVO} cardB
 * */
CardVO.GreaterThan = function(cardA, cardB)
{
    return cardA.value - cardB.value;
};

/**
 * @param {Array} cards1
 * @param {Array} cards2
 *
 * @return {boolean}
 * */
function checkSequence(cards1, cards2) {
    var BLACK_JOKER_BOUNDARY = 5;
    var hand = cards1.concat(cards2);
    if(hand.length > 14)
        return false;

    hand.sort(CardVO.GreaterThan);

    //extract wild cards
    var wildCards = {
        A: [],
        BLACK_JOKER: [],
        RED_JOKER: []
    };
    for(var i = 0; i < hand.length; ++i)
    {
        var card = hand[i];
        if(1 == card.value || card.value >= CardVO.VALUE.BLACK_JOKER)
        {
            wildCards[card.name].push(card);
            hand.splice(i--, 1);
        }
    }

    //more than 2 Ace is sure non-straight
    if(wildCards.A.length > 2)
        return false;

    //Main flag
    var isStraight = true;

    //sort out non-wild cards with Joker patching, easy peasy
    for(i = 1; i < hand.length; ++i)
    {
        card = hand[i];
        var lastCard = hand[i-1];

        if(1 != card.value - lastCard.value)
        {
            if(((lastCard.value < BLACK_JOKER_BOUNDARY) && wildCards.BLACK_JOKER.pop()) || wildCards.RED_JOKER.pop())
            {
                i--;
                lastCard.value++;
            }
            else
                isStraight = false;
        }
        else if( lastCard.value == card.value)
            isStraight = false;
    }

    //reset hand in case value was changed during joker patching
    hand.forEach(function(card) {
        card.value = CardVO.VALUE[card.name];
    });


    //Crasy shit happens here
    var bJokers = wildCards.BLACK_JOKER.length;
    var rJokers = wildCards.RED_JOKER.length;
    //split zone A and B based on hand coverage, e.g for a hand 6-10:
    //    6789T
    //--------------
    //| A |   | B  |
    var indexA = hand[0] ? hand[0].value : 2;
    var indexB = hand[0] ? hand[hand.length-1].value : 1;
    var zoneA = indexA - 2;
    var aSpendBJ = Math.min(bJokers, zoneA, BLACK_JOKER_BOUNDARY - 1);
    var aSpendRJ = zoneA - aSpendBJ;
    var zoneB = 13 - indexB;
    var bSpendBJ = Math.min(bJokers, zoneB, Math.max(BLACK_JOKER_BOUNDARY - indexB, 0));
    var bSpendRJ = zoneB - bSpendBJ;
    var coverTwoAces = aSpendRJ + bSpendRJ <= rJokers && aSpendBJ + bSpendBJ + aSpendRJ + bSpendRJ <= bJokers + rJokers;
    if(wildCards.A.length > 1)
        isStraight = coverTwoAces;
    else if(wildCards.A.length > 0)
        isStraight = (aSpendBJ <= bJokers && aSpendRJ <= rJokers) || (bSpendBJ <= bJokers && bSpendRJ <= rJokers);

    if(bJokers > aSpendBJ + bSpendBJ)
    {
        if(1 == bJokers - (aSpendBJ + bSpendBJ))
        {
            if(wildCards.A.length > 1 ||
                (wildCards.A.length > 0 && coverTwoAces == false))
                isStraight = false;
        }
        else
            isStraight = false;
    }

    return isStraight;
}



////////////////////////////////////
////////////////////////////////////
////////////////////////////////////
////////////////////////////////////

// 以下为测试用例
function test(c1, c2) {
    var cards1 = [],
        cards2 = [];
    c1.forEach(function(c) {
        cards1.push(new CardVO(c));
    });
    c2.forEach(function(c) {
        cards2.push(new CardVO(c));
    });
    console.log(checkSequence(cards1, cards2));
}


//true
test([2, 9, "A", "RED_JOKER", "BLACK_JOKER"], ["RED_JOKER", "BLACK_JOKER", 3,8]);
//true
test(['A',2,3,4,5,6,7,8,9,10,'J','Q','K'], ['A']);
//false
test(['A',2,3,4,5,6,7,8,9], ['A', 'A']);
//true
test(['BLACK_JOKER',2,3,4,5,6,7,8,9,10,'J','Q','K'], ['A']);
//false
test(['BLACK_JOKER',2,3,4,5,6,7,8,9,10,'J','Q'], ['A']);
//false
test(['BLACK_JOKER',2,3,4,5,6,7,8,9,10,'J','Q','K'], ['BLACK_JOKER']);
//true
test(['BLACK_JOKER','BLACK_JOKER','BLACK_JOKER','BLACK_JOKER',6,7,8,9,10,'J','Q'], ['A']);
//true
test(['BLACK_JOKER','BLACK_JOKER','BLACK_JOKER','BLACK_JOKER'], ['A']);
//true
test(['BLACK_JOKER','BLACK_JOKER','BLACK_JOKER','BLACK_JOKER'], ['BLACK_JOKER']);

@lufylegend
Copy link

@coswind
Copy link

coswind commented Mar 25, 2014

@ssnau 感觉你之前写的那一版更好。

@ekoneko
Copy link

ekoneko commented Mar 25, 2014

(function() {
    var deckA, deckNum, deckBlackJoker, deckRedJoker;
    var checkList = function(cards1, cards2) {
        var maxNum, minNum, list;

        deckA = deckBlackJoker = deckRedJoker = 0;
        deckNum = [];

        list = [cards1, cards2];
        for (var i in list) {
            if (!loopDeck(list[i])) {
                return false;
            }
        }

        // 提取边界
        if (deckA + deckNum.length + deckBlackJoker + deckRedJoker > 14) {
            return false;
        }
        deckNum.sort();
        maxNum = deckNum[deckNum.length - 1];
        minNum = deckNum[0];
        if (deckA === 1) {
            (13 - maxNum) > (minNum - 2) ? (minNum = 2) : (maxNum = 13);
        }
        if (deckA === 2) {
            minNum = 2;
            maxNum = 13;
        }

        return check(minNum, maxNum);
    }

    // 格式化卡堆
    var loopDeck = function(cards) {
        for(var i = cards.length - 1; i >= 0; i--) {
            if (cards[i] === 'A') {
                deckA += 1;
                if (deckA > 2) {
                    return false;
                }
            } else if (cards[i] === 'BLACK_JOKER') {
                deckBlackJoker += 1;
            } else if (cards[i] === 'RED_JOKER') {
                deckRedJoker += 1;
            } else {
                if (deckNum.indexOf(cards[i]) !== -1) {
                    return false;
                }
                deckNum.push(Number(cards[i]));
            }
        }
        return true;
    }

    // 条件校验
    var check = function(minNum, maxNum) {
        for (var i = minNum; i <= maxNum; i++) {
            if (deckNum.indexOf(i) !== -1) {
                continue;
            }
            if (i < 9) { // BLACK_JOKER limit
                deckBlackJoker > 0 ? deckBlackJoker-- : deckRedJoker--;
            } else {
                deckRedJoker--;
            }
        }
        return deckRedJoker >= 0;
    }

    this.checkList = checkList;
}).call(this);

写完对比下感觉差不多...基本没优化orz

@finscn
Copy link
Author

finscn commented Mar 25, 2014

我一个不会用gist的朋友写的 , 我发上来

// JavaScript Document

function checkSequence(cards1, cards2) {
    var allCards = [];
    allCards.concat(cards1, cards2); //把两组牌合并成一组
    var cardArr = Array(14); //一旦牌数确定,所有顺子的组合只有14种

    for (var i = 0; i < 14; i++) {
        // 每种组合都用一个数组来代表 
        // 0:   代表总判断,一旦为零就代表该顺子不能成立
        // 1~14:代表牌的1~14还允许出现的个数
        // 15:  代表最多允许"A"的个数
        // 16: 代表最多允许小王的个数 
        // 17: 代表最多允许"A"加小鬼的个数
        cardArr[i] = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 5, 6];
        for (var j = 1; j < 15; j++) {
            if (j - 1 < i || j - 1 >= i + allCards.length) {
                cardArr[i][j] = 0; // 初始化牌1~14的设置,如果把相应的数字变为0,表示该数字牌不可以再出现
                // 以下这段代码不解释 :-(
                if (j == 1) {
                    cardArr[i][15]--;
                    cardArr[i][16]--;
                    cardArr[i][17]--;
                }
                if (j == 14) {
                    cardArr[i][15]--;
                    cardArr[i][17]--;
                }
                if (j >= 2 && j <= 5) {
                    cardArr[i][16]--;
                    cardArr[i][17]--;
                }
            }
        }
    }

    for (var i = 0; i < totalCount; i++) { // 针对每一张牌进行判断
        for (var j = 0; j < 14; j++) { // 循环确认每一种顺子组合
            if (allCards[i].point == "A") { // 出现"A"的话
                cardArr[j][15]--;
                if (cardArr[j][15] < 0) {
                    cardArr[j][0] == 0;
                } // 该组合还允许出现"A"的数字,为负数表示该顺子已经不能成立
                cardArr[j][17]--;
                if (cardArr[j][17] < 0) {
                    cardArr[j][0] == 0;
                } // 该组合还允许出现"A"或小王的数字,为负数表示该顺子已经不能成立
            } else if (allCards[i].point == "BLACK_JOKER") { // 出现小王的话
                cardArr[j][16]--;
                if (cardArr[j][16] < 0) {
                    cardArr[j][0] == 0;
                } // 该组合还允许出现小王的数字,为负数表示该顺子已经不能成立
                cardArr[j][17]--;
                if (cardArr[j][17] < 0) {
                    cardArr[j][0] == 0;
                } // 该组合还允许出现"A"或小王的数字,为负数表示该顺子已经不能成立
            } else if (allCards[i].point == "RED_JOKER") { // 出现大王的话:大王很可怜,不需要被考虑。原因不解释 
            } else { // 出现其它牌的话 
                cardArr[j][allCards[i].num]--;
                if (cardArr[j][allCards[i].num] < 0) {
                    cardArr[j][0] == 0;
                } // 该组合还允许出现某牌的数字,为负数表示该顺子已经不能成立 
                // 以下这段代码也不解释 :-(
                if (allCards[i].num == 1) {
                    cardArr[j][15]--;
                    cardArr[j][16]--;
                    cardArr[j][17]--;
                }
                if (allCards[i].num == 14) {
                    cardArr[j][15]--;
                    cardArr[j][17]--;
                }
                if (allCards[i].num >= 2 && allCards[i].num <= 5) {
                    cardArr[j][15]--;
                    cardArr[j][17]--;
                }
                if (cardArr[j][15] < 0 || cardArr[j][16] < 0 || cardArr[j][15] < 0) {
                    cardArr[j][0] == 0;
                }
            }
        }
    }
    // 当以上代码执行完成后,所有牌都被放到各个顺子的组合里去,并确认时候成立。如果某顺子成立,那该顺子数组的[0]任然为1
    for (var i = 0; i < 14; j++) {
        if (cardArr[i][0] == 1) {
            return ture;
        }
    }
    return false;
}

@coswind
Copy link

coswind commented Mar 25, 2014

function checkStraight(deck1, deck2) {
    var cards = [],
        deck  = deck1.concat(deck2),
        boundary = 6, // 小王的边界
        magic = {  A: 0, B: 0, R: 0, M:0, N:100 };

    if (deck.length > 14) return false;

    // 填上辅助数组cards,要求cards[i] === i
    for (var i = 0; val = deck[i]; i++) {
        if (val === 'A') {
            magic.A++;        
        } else if (/^B/.test(val)) {
            magic.B++;
        } else if (/^R/.test(val)) {
            magic.R++;
        } else {
            if (cards[val]) return false;
            cards[val] = true;
            magic.M = Math.max(magic.M, val);
            magic.N = Math.min(magic.N, val);
            val < boundary ? magic.B++ : magic.R++;
        }
    }

    if (magic.A > 2) {
        return false;
    } else if (magic.A == 2) {
        return magic.B + 1 < boundary && magic.B + magic.R == 12;
    } else if (magic.A == 1) {
        return (magic.B + 1 < boundary && magic.B + magic.R + 1 >= magic.M) ||
            (magic.B < boundary && magic.B + magic.R >= 14 - magic.N && (magic.B > 0 ? magic.R >= 14 - boundary : true));
    } else {
        return magic.B < boundary && magic.B +  magic.R > magic.M - magic.N &&
            (magic.B > 0 ? magic.R > magic.M - boundary : true);
    }

    return false;
}

/*
测试
*/
function test(deck1, deck2) {
    var res = checkStraight(deck1, deck2);
    if (!res) {
        console.log("false")
    } else {
        console.log("success")
    }
}
// Only accept "A",2,3,4,5,6,7,8,9,10,11,12,13,"BLACK_JOKER","RED_JOKER"
var timeStart = +new Date;
console.log('--- No A ---');
test([4, 6, 8, 9, 10, 2, 'BLACK_JOKER'], ['BLACK_JOKER', 'BLACK_JOKER', 'RED_JOKER', 'RED_JOKER']); // true
test(['BLACK_JOKER'], ['BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'RED_JOKER']); // success
test([3, 4, 5, 6, 7, 8, 9], [10]); // success
test([1, 2], [3, 4, 5]); //true
test([12, 13], [11]);// true
test([1, 2], [8, 'BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'RED_JOKER']); // false
test([7, 8], ['BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER']); //false, 小王与12和13连不上
test(['BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER'], ['BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'RED_JOKER']); // 超过了5个小王,第六个放不下
test(['BLACK_JOKER',2,3,4,5,6,7,8,9,10, 11, 12, 13], ['BLACK_JOKER']); //false

console.log('--- 1 A ---');
test(['A', 12], [13]); // success
test([2,9,"A","RED_JOKER","BLACK_JOKER"],["RED_JOKER","BLACK_JOKER",3,8]);//true
test([2, 9, "A", "RED_JOKER", "BLACK_JOKER"], ["RED_JOKER", "BLACK_JOKER", 3,8]); // true
test(['BLACK_JOKER',2,3,4,5,6,7,8,9,10, 11, 12, 13], ['A']); // true
test([6,"RED_JOKER","A",8,9,10,12],["RED_JOKER","RED_JOKER", "BLACK_JOKER"]);//true
test(['A', 2, 3, 4], [5, 6, 7, 8, 9, 10, 11, 12, 13, 'RED_JOKER']); // success
test(['A', 2, 3, 4, 5, 6], [7, 8, 9, 'BLACK_JOKER', 'RED_JOKER']); //fail, 小王没地方放
test(['A', 4], ['BLACK_JOKER']); // fail
test(['A', 3, 3],  [4, 5, 'BLACK_JOKER']); // fail
test(["BLACK_JOKER","BLACK_JOKER"],["RED_JOKER","RED_JOKER",8,9,10,11,12,"A"]);//false
test([6,"A",8,9,10,12],["RED_JOKER","RED_JOKER", "BLACK_JOKER"]);//false
test(['BLACK_JOKER',2,3,4,5,6,7,8,9,10, 11, 12], ['A']); //false

console.log('--- 2 A ---');
test([5,6,"A","A",8,9,10,11,12],["RED_JOKER","RED_JOKER", "BLACK_JOKER", 3,4]);//true
test(['A',2,3,4,5,6,7,8,9,10, 11, 12, 13], ['A']); //true
test(['A', 5, 'A', 7], [8, 10, 'BLACK_JOKER', 'RED_JOKER']); // fail
test([5,6,"A","A",8,9,10,11,12],["RED_JOKER","RED_JOKER", "BLACK_JOKER", 3,3]);//false

console.log('--- 2+ A ---');
test(['A', 'A', 'A', 6, 'BLACK_JOKER', 'BLACK_JOKER'], ['BLACK_JOKER', 'RED_JOKER']); // fail
test(['A',2,3,4,5,6,7,8,9], ['A', 'A']); // false
console.log('Using time: ' + (+new Date - timeStart));

参照@ssnau的改了下,无节操。

@coswind
Copy link

coswind commented Mar 25, 2014

@finscn 你那位朋友的代码里面totalCount没定义,最后几行有个地方的true 写成了ture。

@Kation
Copy link

Kation commented Mar 25, 2014

var _Count = {
            A: [1, 14],
            BLACK_JOKER: [1, 2, 3, 4, 5],
            RED_JOKER: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
        };
        function check(card1, card2) {
            var cards = card1.concat(card2);
            if (cards.length > 14)
                return false;
            var list = new Array(14);
            var A = 0;
            var BLACK_JOKER = 0;
            var RED_JOKER = 0;
            for (var x = 0; x < cards.length; x++) {
                var index = new Number(cards[x]);
                if (isNaN(index))
                    eval(cards[x] + "+=1");
                else
                    if (list[index - 1] == null)
                        list[index - 1] = true;
                    else
                        return false;
            }
            function checkQueue() {
                function insert(array) {
                    var min = 15;
                    var index = 0;
                    for (var x in array) {
                        if (list[array[array.length - 1 - x] - 1] != null)
                            continue;
                        for (var i = 1; i < 15; i++) {
                            if (i == 14 && min == 15) {
                                index = array[array.length - 1 - x] - 1;
                                min = i;
                                break;
                            }
                            if (list[array[array.length - 1 - x] - 1 - i] != null && list[array[array.length - 1 - x] - 1 + i] != null) {
                                index = array[array.length - 1 - x] - 1;
                                min = -1;
                                break;
                            }
                            if (list[array[array.length - 1 - x] - 1 - i] != null || list[array[array.length - 1 - x] - 1 + i] != null) {
                                if (i < min) {
                                    index = array[array.length - 1 - x] - 1;
                                    min = i;
                                    break;
                                }
                            }
                        }
                    }
                    if (min == 15)
                        return false;
                    list[index] = true;
                    return true;
                }
                for (var i = 0; i < A; i++)
                    if (!insert(_Count.A))
                        return false;
                for (var i = 0; i < BLACK_JOKER; i++)
                    if (!insert(_Count.BLACK_JOKER))
                        return false;
                for (var i = 0; i < RED_JOKER; i++)
                    if (!insert(_Count.RED_JOKER))
                        return false;
                var t = null;
                for (var x in list)
                    if (t != null && t != x - 1)
                        return false;
                    else
                        t = x;
                return true;
            }
            return checkQueue();
        }

        document.writeln(check([4, 6, 8, 9, 10, 2, 'BLACK_JOKER'], ['BLACK_JOKER', 'BLACK_JOKER', 'RED_JOKER', 'RED_JOKER'])); // true
        document.writeln(check(['A', 2, 3, 4, 5, 6], [7, 8, 9, 'BLACK_JOKER', 'RED_JOKER'])); //fail, 小王没地方放
        document.writeln(check([12, 13], ['BLACK_JOKER'])); //false, 小王与12和13连不上
        document.writeln(check(['BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER'], ['BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'RED_JOKER'])); // 超过了5个小王,第六个放不下
        document.writeln(check(['BLACK_JOKER'], ['BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'RED_JOKER'])); // success
        document.writeln(check(['A', 5, 'A', 7], [8, 10, 'BLACK_JOKER', 'RED_JOKER'])); // fail
        document.writeln(check(['A', 'A', 'A', 6, 'BLACK_JOKER', 'BLACK_JOKER'], ['BLACK_JOKER', 'RED_JOKER'])); // fail
        document.writeln(check([3, 4, 5, 6, 7, 8, 9], [10])); // success
        document.writeln(check(['A', 4], ['BLACK_JOKER'])); // fail
        document.writeln(check(['A', 12], [13])); // success
        document.writeln(check(['A', 2, 3, 4], [5, 6, 7, 8, 9, 10, 11, 12, 13, 'RED_JOKER'])); // success
        document.writeln(check(['A', 3, 3], [4, 5, 'BLACK_JOKER'])); // fail

        // 抄了楼下的测试用例(*_*)
        document.writeln(check([2, 9, "A", "RED_JOKER", "BLACK_JOKER"], ["RED_JOKER", "BLACK_JOKER", 3, 8]));//true
        document.writeln(check([5, 6, "A", "A", 8, 9, 10, 11, 12], ["RED_JOKER", "RED_JOKER", "BLACK_JOKER", 3, 3]));//false
        document.writeln(check([5, 6, "A", "A", 8, 9, 10, 11, 12], ["RED_JOKER", "RED_JOKER", "BLACK_JOKER", 3, 4]));//true
        document.writeln(check(["BLACK_JOKER", "BLACK_JOKER"], ["RED_JOKER", "RED_JOKER", 8, 9, 10, 11, 12, "A"]));//false
        document.writeln(check([6, "RED_JOKER", "A", 8, 9, 10, 12], ["RED_JOKER", "RED_JOKER", "BLACK_JOKER"]));//true
        document.writeln(check([6, "A", 8, 9, 10, 12], ["RED_JOKER", "RED_JOKER", "BLACK_JOKER"]));//false

        document.writeln(check([2, 9, "A", "RED_JOKER", "BLACK_JOKER"], ["RED_JOKER", "BLACK_JOKER", 3, 8])); // true
        document.writeln(check(['A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], ['A'])); //true
        document.writeln(check(['A', 2, 3, 4, 5, 6, 7, 8, 9], ['A', 'A'])); // false
        document.writeln(check(['BLACK_JOKER', 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], ['A'])); // true
        document.writeln(check(['BLACK_JOKER', 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], ['A'])); //false
        document.writeln(check(['BLACK_JOKER', 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], ['BLACK_JOKER'])); //false

@lyDASHsBOK
Copy link

我才发现你weibo里面发的不是小王当1-9么,这里又变成当1-5了……

@ssnau
Copy link

ssnau commented Mar 25, 2014

@coswind 你这应该是性能最佳的方案了...

@as3long
Copy link

as3long commented Mar 25, 2014

贴个渣实现

/*
 测试
 */
function test(deck1, deck2) {
    console.log('----------------------');
    console.log("input", deck1, deck2);
    var res = checkTwoArr(deck1, deck2);
    if (!res) {
        console.log("failed")
    } else {
        res = res.join(',').replace(/^(0,)*|(,0)*$/g, '').replace(/,/g, ', ');
        console.log("success: [" + res + "]");
    }
}

// Only accept "A",2,3,4,5,6,7,8,9,10,11,12,13,"BLACK_JOKER","RED_JOKER"

test([4, 6, 8, 9, 10, 2, 'BLACK_JOKER'], ['BLACK_JOKER', 'BLACK_JOKER', 'RED_JOKER', 'RED_JOKER']);
// true
test(['A', 2, 3, 4, 5, 6], [7, 8, 9, 'BLACK_JOKER', 'RED_JOKER']);
//fail, 小王没地方放
test([12, 13], ['BLACK_JOKER']);
//false, 小王与12和13连不上
test(['BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER'], ['BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'RED_JOKER']);
// 超过了5个小王,第六个放不下
test(['BLACK_JOKER'], ['BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'BLACK_JOKER', 'RED_JOKER']);
// success
test(['A', 5, 'A', 7], [8, 10, 'BLACK_JOKER', 'RED_JOKER']);
// fail
test(['A', 'A', 'A', 6, 'BLACK_JOKER', 'BLACK_JOKER'], ['BLACK_JOKER', 'RED_JOKER']);
// fail
test([3, 4, 5, 6, 7, 8, 9], [10]);
// success
test(['A', 4], ['BLACK_JOKER']);
// fail
test(['A', 12], [13]);
// success
test(['A', 2, 3, 4], [5, 6, 7, 8, 9, 10, 11, 12, 13, 'RED_JOKER']);
// success
test(['A', 3, 3], [4, 5, 'BLACK_JOKER']);
// fail

// 抄了楼下的测试用例(*_*)
test([2, 9, "A", "RED_JOKER", "BLACK_JOKER"], ["RED_JOKER", "BLACK_JOKER", 3, 8]);
//true
test([5, 6, "A", "A", 8, 9, 10, 11, 12], ["RED_JOKER", "RED_JOKER", "BLACK_JOKER", 3, 3]);
//false
test([5, 6, "A", "A", 8, 9, 10, 11, 12], ["RED_JOKER", "RED_JOKER", "BLACK_JOKER", 3, 4]);
//true
test(["BLACK_JOKER", "BLACK_JOKER"], ["RED_JOKER", "RED_JOKER", 8, 9, 10, 11, 12, "A"]);
//false
test([6, "RED_JOKER", "A", 8, 9, 10, 12], ["RED_JOKER", "RED_JOKER", "BLACK_JOKER"]);
//true
test([6, "A", 8, 9, 10, 12], ["RED_JOKER", "RED_JOKER", "BLACK_JOKER"]);
//false

test([2, 9, "A", "RED_JOKER", "BLACK_JOKER"], ["RED_JOKER", "BLACK_JOKER", 3, 8]);
// true
test(['A', 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], ['A']);
//true
test(['A', 2, 3, 4, 5, 6, 7, 8, 9], ['A', 'A']);
// false
test(['BLACK_JOKER', 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], ['A']);
// true
test(['BLACK_JOKER', 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], ['A']);
//false
test(['BLACK_JOKER', 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13], ['BLACK_JOKER']);
//false

function checkTwoArr(arr1, arr2) {
    return checkSequence(arr1.concat(arr2));
}

function checkSequence(arr) {
    if (arr.length > 14) {
        return false;
    }
    var noJokerA_arr = [];
    var b_joker_arr = [];
    var r_joker_arr = [];
    var a_arr = [];
    var boundary = 5
    var len = arr.length;
    var checkFleg = true;
    for (var i = 0; i < len; i++) {
        if (arr[i] == "A") {
            a_arr.push(arr[i]);
        } else if (arr[i] == "BLACK_JOKER") {
            b_joker_arr.push(arr[i]);
        } else if (arr[i] == "RED_JOKER") {
            r_joker_arr.push(arr[i]);
        } else {
            noJokerA_arr.push(arr[i]);
        }
    }
    //noJokerA_arr = quickSort(noJokerA_arr);
    //console.log("noJokerA_arr",noJokerA_arr);
    for (i = 0; i < noJokerA_arr.length-1; i++) {
        if(noJokerA_arr[i]==noJokerA_arr[i+1]){
            return false;
        }
    }
    switch(a_arr.length) {
        case 1:
            var checkArr1 = checkSequence(b_joker_arr.concat(r_joker_arr).concat(noJokerA_arr).concat([1]));
            if (checkArr1) {
                return checkArr1;
            } else {
                var checkArr2 = checkSequence(b_joker_arr.concat(r_joker_arr).concat(noJokerA_arr).concat([14]))
                if (checkArr2) {
                    return checkArr2;
                } else {
                    return false;
                }
            }
            break;
        case 2:
            return checkSequence(noJokerA_arr.concat(b_joker_arr).concat(r_joker_arr).concat([1, 14]));
            break;
        default:
            break;
    }
    if (noJokerA_arr.length > 0) {
        var arrPock = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
        noJokerA_arr = quickSort(noJokerA_arr);
        var arrIndex = arrPock.indexOf(noJokerA_arr[0]);
        var min = noJokerA_arr[0];
        var max = noJokerA_arr[noJokerA_arr.length - 1];
        for (var i = arrIndex; i < arrPock.length && arrPock[i] <= max; i++) {
            var index = noJokerA_arr.indexOf(arrPock[i]);
            if (index == -1) {
                if (arrPock[i] <= boundary) {
                    checkFleg = (b_joker_arr.length > 0) ? (function() {
                        b_joker_arr.pop();
                        noJokerA_arr.push(arrPock[i]);
                        return true;
                    })() : (r_joker_arr.length > 0) ? (function() {
                        r_joker_arr.pop();
                        noJokerA_arr.push(arrPock[i]);
                        return true;
                    })() : false;
                } else if (arrPock[i] <= 14) {
                    checkFleg = (r_joker_arr.length > 0) ? (function() {
                        r_joker_arr.pop();
                        noJokerA_arr.push(arrPock[i]);
                        return true;
                    })() : false;
                }
            }
        }

        if (!checkFleg) {
            return checkFleg;
        } else {
            while (b_joker_arr.length > 0) {
                var num1 = b_joker_arr.pop();
                min--;
                if (min <= boundary && min > 0) {
                    noJokerA_arr.push(min);
                } else {
                    checkFleg = false;
                    break;
                }
            }
            while (r_joker_arr.length > 0) {
                var num2 = r_joker_arr.pop();
                max++;
                if (max <= 14) {
                    noJokerA_arr.push(max);
                } else {
                    checkFleg = false;
                    break;
                }
            }
            if (checkFleg) {
                noJokerA_arr = quickSort(noJokerA_arr);
                return noJokerA_arr;
            } else {
                return false;
            }

        }
    } else {
        if (b_joker_arr.length <= boundary) {
            min = 6;
            max = 5;
            while (b_joker_arr.length) {
                var num1 = b_joker_arr.pop();
                min--;
                noJokerA_arr.push(min);
            }
            while (r_joker_arr.length > 0) {
                var num2 = r_joker_arr.pop();
                max++;
                noJokerA_arr.push(max);
            }
            noJokerA_arr = quickSort(noJokerA_arr);
            return noJokerA_arr;
        } else {
            return false;
        }
    }

}

function quickSort(arr) {
    var len = arr.length;
    if (len <= 1) {
        return arr;
    } else {
        var arrLeft = [], arrRight = [];
        var nowArr=[];
        for (var i = 1; i < len; i++) {
            if (arr[0] <arr[i]) {
                arrRight.push(arr[i]);
            } else if (arr[0] > arr[i]) {
                arrLeft.push(arr[i]);
            }else{
                nowArr.push(arr[0]);
            }
        }
        return quickSort(arrLeft).concat(arr[0]).concat(nowArr).concat(quickSort(arrRight));
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment