您的位置:首页 > 编程语言 > Lua

[置顶] 带赖子的胡牌算法lua版本

2017-05-09 13:22 232 查看
今天花了一上午的时间重新写了一下胡牌的算法,废话不多说,直接贴代码:

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: