四、手牌的整理
拿到一張手牌後,有必要整理一下,可以提取出哪些不同的牌型。同一堆手牌,根據不同情況下,可以組成不同的牌型,有的適合出擊,有的適合防守。
根據之前掃描手牌後產生的數組,可以把所有的牌型可能遍歷一次,比如說,如果手牌剩下 這六張牌,A, A, A, K, K, Q,那麼可以提取出來的牌型有:
Q, K,A, K, K, A, A, A, A, A, Q,A, A, A, K,A, A, A, K, K,
簡單的說,除了手上的牌打完,那麼 手牌 = 某種牌型 + 剩下的手牌, 遞歸操作手上的牌,每次提取一種牌型,就可以把所有可能的牌型組合遍歷完。提取某種牌型的演算法可以如下表示:
this.scanToTable();var outNodes = [];// find all possible moves;for (var value = Base.kCard_ValueLeast; value < Base.kCard_ValueMax; ++value) { for (var main = 1; main <= this.cardsTable[0][value]; ++main) { var maxSub = Lord.getMaxSubNum(main); var dupNum = Lord.getDupSubNum(main); // 單獨 或者跳到 順子 var enoughLen = Lord.getMinSerialLength(main); for (var len = 1; len <= this.cardsTable[main][value]; len = (len == 1 ? enoughLen : len + 1)) { for (var s = 0; s <= maxSub; ++s) { var serial = Node.create(len == 1 ? Base.kCardType_Single : Base.kCardType_Serial, value - len + 1, main, len, s); this.collectAllNodes(outNodes, serial, dupNum); } } }}// at last, check if rockect.if (this.cardsTable[0][Base.kCard_ValueJoker1] && this.cardsTable[0][Base.kCard_ValueJoker2]) { var one = Node.create(Base.kCardType_Rocket, Base.kCard_ValueJoker1, 1, 1, 0); one.startFill(); outNodes.push(one);}return outNodes;
outNodes就是當前手牌可以提取出來的牌型(Node)的列表,給定你的手牌,你這次出牌可以打哪些牌就可以算出來了。
getMaxSubNum: // 表明副牌的張數,不帶,單張,還是對數。三帶一,就是1,四帶二也是1,四 帶兩對和三帶二,是2getDupSubNum: // 表明副牌的個數,三張牌帶的副牌只能是1,四張牌帶的副牌是2,別的都是0getMinSerialLength: // 序列牌的起始長度,順子是5,連對是3,別的都是2collectAllNodes: // 根據當前的主牌,收集所有可以帶的副牌。進行排列組合.
如果你是本輪的首次出牌,那麼隨機選擇列表的任何一項都是合法的出牌;如果本輪別人已經出過牌,那麼先確定別人出的牌的牌型(otherNode),從列表裡選出所有可以打過別人牌型的牌型(同類型大牌,炸彈,王炸等),不選,或者隨機選擇一項,就是跟牌。
那麼,現在給定你的手牌,所有出牌可能和所有跟牌可能都可以做到,只是如何選擇最好的可能呢?我們有必要給每種牌型都給出一個權重,根據權重和當前的出牌情況,選出一個最合適的牌型。
推薦閱讀: