今天花了一上午的时间重新写了一下胡牌的算法,废话不多说,直接贴代码:
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 end2.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 end5.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