带赖子的胡牌算法lua版本

今天花了一上午的时间重新写了一下胡牌的算法,废话不多说,直接贴代码:

1.GameLayer

GameLayer = class("GameLayer",function()
    return cc.Layer:create()
end)

--
function GameLayer:create()
    local view = GameLayer.new()
    view:__init()
    return view
end

--
function GameLayer:__init()
    self:setTag(LayerTag)
    self:initData()
    self:initCards()
    self:initListener() 
end

function GameLayer:initData()
    --可以选取的牌列表
    self.cardList = {}
    
    --添加的想要判断听牌的牌列表
    self.needCheckCardList = {}
    
    --
    self.holdCardNode = cc.Node:create()
    self:addChild(self.holdCardNode)
end

function GameLayer:initListener()
    local listener = cc.EventListenerTouchOneByOne:create()
    listener:setSwallowTouches(false)
    listener:registerScriptHandler(function(touch, event) return self:touchBegan(touch, event) end, cc.Handler.EVENT_TOUCH_BEGAN)
    listener:registerScriptHandler(function(touch, event) self:touchEnded(touch, event) end, cc.Handler.EVENT_TOUCH_ENDED)
    self:getEventDispatcher():addEventListenerWithSceneGraphPriority(listener, self)
end

function GameLayer:touchBegan(touch, event)
   
    if #self.needCheckCardList >= 13 then
        Util:getHuCardList(self.needCheckCardList)
    else
        local touchPos = touch:getLocation() 
        for i = 1, #(self.cardList) do
            local card = self.cardList[i]
            local cardRect = card:getBoundingBox()
            if cc.rectContainsPoint(cardRect, touchPos) then
                table.insert(self.needCheckCardList, card.id)

                --将一副牌中选中的牌去掉
                table.remove(self.cardList, i)
                card:removeFromParent()

                --
                self:updateView()
                return true
            end
        end
    end
    
    --
    return true
end

function GameLayer:touchEnded(touch, event)

end

function GameLayer:updateView()
    local startX, _, maxNum = self:getStartPosAndMaxnum()
    local cardWidth, cardHeight = self:getCardWidthHeight()
    local startY = gapWidth + cardWidth/2
    
    --移除持有的牌
    self.holdCardNode:removeAllChildren()
    
    --对持有的牌进行排序
    table.sort(self.needCheckCardList, function(a, b) return a < b end)
    
    --
    for i = 1, #self.needCheckCardList do
        local cardId = self.needCheckCardList[i]
        local card = self:createCardWithId(cardId)
        
        local row = math.ceil(i/maxNum)
        local col = i % maxNum == 0 and maxNum or (i % maxNum)
        
        local x = startX + (col - 1) * (cardWidth + gapWidth)
        local y = startY + (cardHeight + gapWidth) * (row - 1)
        
        card:setPosition(x, y)
        self.holdCardNode:addChild(card)
    end
end

--初始化牌的位置
function GameLayer:initCards()
    local startX, startY, maxNum = self:getStartPosAndMaxnum()
    local cardWidth, cardHeight = self:getCardWidthHeight()
    
    for i = 1, #Cards do
        local card = self:createCardWithId(Cards[i])
        local row = math.ceil(i/maxNum)
        local col = i % maxNum == 0 and maxNum or (i % maxNum)
      
        local x = startX + (col - 1) * (cardWidth + gapWidth)
        local y = startY - (cardHeight + gapWidth) * (row - 1)
        table.insert(self.cardList, card)
        card:setPosition(x , y)
        self:addChild(card) 
    end
end

--通过牌的id创建一张牌
function GameLayer:createCardWithId(id, scale)
    local card = cc.Sprite:create(id .. ".png")
    card:setScale(scale or cardScale)
    card.id = id
    return card
end


--得到参考点位置
function GameLayer:getStartPosAndMaxnum()
    local cardWidth, cardHeight = self:getCardWidthHeight()
    local startX, startY = (gapWidth + cardWidth/2),  (visibleSize.height - cardHeight/2 - gapWidth)
    local maxNum = math.floor((visibleSize.width - gapWidth)/(gapWidth + cardWidth))
    return startX, startY, maxNum
end

--得到牌的宽度和高度
function GameLayer:getCardWidthHeight()
    if not self.cardWidth or not self.cardHeight then
        local sp = cc.Sprite:create("101.png")
        sp:setPosition(visibleSize.width/2, visibleSize.height/2)
        self.cardWidth = sp:getContentSize().width * cardScale
        self.cardHeight = sp:getContentSize().height * cardScale
    end
    return self.cardWidth, self.cardHeight
end
2.ConstantDefine.lua

--两张牌之间的间隔
gapWidth = 5

--桌子上的牌放缩倍数
cardScale = 0.5

--当前运行的场景中第一个层的tag值
LayerTag = 9999

--当前赖子设定为白板407
LaiZi = 407

--桌面大小
visibleSize = cc.Director:getInstance():getVisibleSize()

--所有类型的牌列表
CardTypes = {
    101,102,103,104,105,106,107,108,109,
    201,202,203,204,205,206,207,208,209,
    301,302,303,304,305,306,307,308,309,
    401,402,403,404,405,406,407,
}

--牌的id对应的图片资源 共136张牌
Cards = {
    --饼 36张
    101,102,103,104,105,106,107,108,109,
    101,102,103,104,105,106,107,108,109,
    101,102,103,104,105,106,107,108,109,
    101,102,103,104,105,106,107,108,109,
    
    --条 36张
    201,202,203,204,205,206,207,208,209,
    201,202,203,204,205,206,207,208,209,
    201,202,203,204,205,206,207,208,209,
    201,202,203,204,205,206,207,208,209,
    
    --万 36张
    301,302,303,304,305,306,307,308,309,
    301,302,303,304,305,306,307,308,309,
    301,302,303,304,305,306,307,308,309,
    301,302,303,304,305,306,307,308,309,
    
    --东西南北中发白 28张
    401,402,403,404,405,406,407,
    401,402,403,404,405,406,407,
    401,402,403,404,405,406,407,
    401,402,403,404,405,406,407,
}

--赖子说明    暂时以:白板,也就是:407当赖子
3.Lib.lua

require "ConstantDefine"
require "Util"
require "LuaUtils"
require "src/view/GameLayer"

4.LuaUtils.lua

LuaUtils = {}

--统计t中,值为v的数量
function LuaUtils:getSameNumCount(t, v)
    local count = 0
    for i = 1, #t do
        if v == t[i] then
            count = count + 1
        end
    end
    return count
end

--删除t中值等于v的一个元素
function LuaUtils:removeOneNum(t, v)
    for i = 1, #t do
        if t[i] == v then
            table.remove(t, i)
            break
        end
    end
end

--向t中插入t1中的所有元素
function LuaUtils:insertList(t, t1)
    for i = 1, #t1 do
        table.insert(t, t1[i])
    end
end

--从t中删除t1中的所有元素
function LuaUtils:removeList(t, t1)
    for i = 1, #t1 do
        self:removeOneNum(t, t1[i])
    end
end

--
function LuaUtils:getNowTime()
    local socket = require("socket")
    return socket.gettime()/1000
end

--
function LuaUtils:printT(t)
    local result = {}
    for k, v in pairs(t) do 
        table.insert(result, v)
    end
    Util:sort(result)
    
    --
    local str = ""
    for k, v in ipairs(result) do 
        str = str .. v .. ","
    end
    
    --
    return str
end

function LuaUtils:mapToList(m)
    local result = {}
    for k, v in pairs(m) do 
        table.insert(result, v)
    end
    Util:sort(result)
    return result
end
5.Util.lua

Util = {}

--从t中提取值为v的 将 或者 刻
--【参数】--
--t 牌集合
--v 将要提取的将或者刻的值
--isJiang 是否是提取将
--【返回值】--
--resultList  提取到的将的集合
--remainList  提取后,剩余的牌
function Util:tiQuJiangOrKe(t, v, isJiang)

    --
    local t, laiziList = self:seprateLaizi(t)
    
    --
    self:sort(t)
    
    --
    local resultList = {}   --
    local remainList = clone(t)
    
    --
    local num = (isJiang and 2 or 3)
    
    --
    for i = #t, 1, -1 do
        local vTemp = remainList[i]
        if vTemp == v then
            table.insert(resultList, v)
            table.remove(remainList, i)
            if #resultList == num then
                break
            end
        end
    end
    
    --
    if #resultList == num then
        --放回赖子
        LuaUtils:insertList(remainList, laiziList)
        
        return resultList, remainList 
    else
        
        local needLaiziNum = num-#resultList
        local remainLaiziNum = #laiziList - needLaiziNum
        
        if remainLaiziNum < 0 then
            return nil, nil
        end
        
        if #resultList < num and (#laiziList >= needLaiziNum) then
            --提取结果中插入赖子
            for i = 1, needLaiziNum do
                table.insert(resultList, laiziList[1])
            end
            --剩余牌中插入多余的赖子 
            for i = 1, remainLaiziNum do
                table.insert(remainList, laiziList[1])
            end   
            
            --
            return resultList, remainList 
       end
    end
    
    --
    return nil, nil
end

--分离赖子
--【返回值】
--tTmpList   分离赖子后剩余的牌
--laiziList  赖子列表
function Util:seprateLaizi(t)
    local tTmpList = {}
    local laiziList = {}
    
    for i = #t, 1, -1 do
        local v = t[i]
        if v == LaiZi then
            table.insert(laiziList, v)
        else
            table.insert(tTmpList, v)
        end
    end
    return tTmpList, laiziList
end

--提取顺子
--【参数】
--t  牌集合
--v  提取的起始值
--【返回值】
--resultList  提取到的牌集合
--remainList  剩余的牌集合
function Util:tiQuShunzi(t, v)
    --
    local t, laiziList = self:seprateLaizi(t)
    
    --
    self:sort(t)
    
    local v1 = v
    local v2 = v + 1
    local v3 = v + 2
    
    local needLaiziNum = 0
    local remainLaiziNum = 0
    
    local missV1, missV2, missV3  --缺失某个数字的标志
    
    if LuaUtils:getSameNumCount(t, v1) == 0 then
        needLaiziNum = needLaiziNum + 1
        missV1 = true
    end
    
    if LuaUtils:getSameNumCount(t, v2) == 0 then
        needLaiziNum = needLaiziNum + 1
        missV2 = true
    end
    
    if LuaUtils:getSameNumCount(t, v3) == 0 then
        needLaiziNum = needLaiziNum + 1
        missV3 = true
    end
    
    remainLaiziNum = #laiziList - needLaiziNum
    
    if remainLaiziNum < 0 then
        return nil, nil
    end
    
    --
    local resultList = {v1, v2, v3}   --
    if not missV1 then
        table.insert(resultList, v1)
    else
        table.insert(resultList, laiziList[1])
    end
    
    if not missV2 then
        table.insert(resultList, v2)
    else
        table.insert(resultList, laiziList[1])
    end
    
    if not missV3 then
        table.insert(resultList, v3)
    else
        table.insert(resultList, laiziList[1])
    end
    
    
    local remainList = clone(t)
    
    if not missV1 then
       LuaUtils:removeOneNum(remainList, v1)
    else
        LuaUtils:removeOneNum(remainList, laiziList[1])
    end
    
    if not missV2 then
        LuaUtils:removeOneNum(remainList, v2)
    else
        LuaUtils:removeOneNum(remainList, laiziList[1])
    end
    
    if not missV3 then
        LuaUtils:removeOneNum(remainList, v3)
    else
        LuaUtils:removeOneNum(remainList, laiziList[1])
    end
    
    --
    for i = 1,  remainLaiziNum do
        table.insert(remainList, laiziList[1])
    end
    
    --
    return resultList, remainList
end


--将牌从小到大排序
function Util:sort(t)
    table.sort(t,function(a, b)
        return a < b 
    end)
end


--计算胡牌信息
--【参数】
--t 当前牌数据
--isHaveTiQuJiang 是否已经提取将了
--resultList 存储可以胡的牌列表
--cardId 存储是否胡的牌的id
--huMap 存储所有可以胡的牌
--cutInfo 存放剪支信息

function Util:calHuInfo(t, isHaveTiQuJiang, cardId, huMap, cutInfo)

    self.functionList = {
        self.tiQuJiangOrKe,
        self.tiQuShunzi,
    }
    
    --剪支使用
    if cutInfo[ LuaUtils:printT(t)] then
        return
    else
        cutInfo[ LuaUtils:printT(t)] = LuaUtils:printT(t)
    end
    
    --
    if #t == 0 then
        huMap[cardId] = cardId
        return
    end
    self:sort(t)
    
    if not isHaveTiQuJiang then  --先提取将
        for i = 1, #t do
            local id = t[i]
            local resultList, remainList = self:tiQuJiangOrKe(t, id, true)
            if resultList then
                isHaveTiQuJiang = true
                self:calHuInfo(remainList, isHaveTiQuJiang, cardId, huMap, cutInfo)
                isHaveTiQuJiang = false
                LuaUtils:insertList(remainList, resultList)
                self:sort(remainList)
            end
        end
    else --提取扑或者刻
        for i = 1, #t do
            local functionList =  self.functionList
            
            for j = 1, #functionList do
                local id = t[i]
                local resultList, remainList = functionList[j](self, t, id)
                if resultList then
                    self:calHuInfo(remainList, isHaveTiQuJiang, cardId, huMap, cutInfo)
                    LuaUtils:insertList(remainList, resultList)
                    self:sort(remainList)
                end
            end
        end
    end
end

--根据当前牌数据t得到当前的胡牌信息
function Util:getHuCardList(t)
    local startTime = LuaUtils:getNowTime()
    local huMap = {}
    for i = 1, #CardTypes do
        local cardId = CardTypes[i]
        local tmpT = clone(t)
        table.insert(tmpT, cardId)
        self:sort(tmpT)
        if LuaUtils:getSameNumCount(tmpT, cardId) <= 4 then
            
            self:calHuInfo(tmpT, false, cardId, huMap, {})
        end
    end
    
    --将可以胡的牌放到桌面上
    local gameLayer = cc.Director:getInstance():getRunningScene():getChildByTag(LayerTag)
    local cardWidth, cardHeight = GameLayer:getCardWidthHeight()
    
    --
    local tip = cc.Label:createWithSystemFont("胡:","Arial",20)
    tip:setPosition(tip:getContentSize().width, visibleSize.height/2-48)
    gameLayer:addChild(tip)
    
    --
    local startX, startY, maxNum = GameLayer:getStartPosAndMaxnum()
    
    startY = startY - visibleSize.height/2 - 70
    for i, v in ipairs(LuaUtils:mapToList(huMap)) do 
        
        
        local row = math.ceil(i/maxNum)
        local col = i % maxNum == 0 and maxNum or (i % maxNum)

        local x = startX + (col - 1) * (cardWidth + gapWidth)
        local y = startY - (cardHeight + gapWidth) * (row - 1)
        
        
        local cardSp = GameLayer:createCardWithId(v)
        cardSp:setPosition(x, y)
        gameLayer:addChild(cardSp)
    end
    
    --
    local endTime = LuaUtils:getNowTime()
    print ("耗时:".. (endTime-startTime) .. "秒")  --计算胡牌时间
    return huMap
end

最初始的牌型:


胡牌的几种测试:










思路很简单:

1.先提取赖子

2.再提取顺子或者刻,提取完毕,则说明可以胡牌

优化:

用回溯的方法,加上剪支,就可以搞定。

时间最坏的情况大约在0.001s,应该还可以优化

强弟弟提了几个优化策略:

用3n+2,掩码算牌型 就是0x01就代表类型是0数字是1。消耗主要在获取某个牌型至少需要多少个癞子那里    我每次都打印看调用了多少次。这个有空再研究。


写的过程中,发现回溯已经不太会用了,这次发现可以传入一个参数作为能胡牌的结果存储。其余就没啥了。


备注:

云哥哥github 棋牌学习

https://github.com/yuanfengyun   


胡牌查表算法

http://blog.csdn.net/panshiqu/article/details/58610958#comments



猜你喜欢

转载自blog.csdn.net/themagickeyjianan/article/details/71449298
今日推荐