十三水组合算法
公司内部的算一次要100多毫秒一次,于是自己实现了一个十三水算法,算一次只要0.6-0.8ms,以下是代码
local ffi = require('ffi');
local CT_SINGLE = 1
local CT_ONE_DOUBLE = 2
local CT_FIVE_TWO_DOUBLE = 3
local CT_THREE = 4
local CT_FIVE_MIXED_FLUSH_NO_A = 5
local CT_FIVE_FLUSH = 6
local CT_FIVE_THREE_DEOUBLE = 7
local CT_FIVE_FOUR_ONE = 8
local CT_FIVE_STRAIGHT_FLUSH_NO_A = 9
local CT_FIVE_FIVE_ONE = 10
local bOnlyCompareNumber = false;
local bFuncVersion = 1;
local function num2hex(arr)
local newarr = {};
for i,v in ipairs(arr) do
table.insert(newarr, string.format("0x%x", v));
end
return newarr;
end
local function singleCardCompare(a, b)
if a ~= b and (a ~= 1 and b ~=1) then
if a > b then
return 1;
else
return -1;
end
elseif a ~= b and (a == 1 or b == 1) then
if a == 1 then
return 1;
else
return -1;
end
elseif a == b then
return 0;
end
end
local function singleCardCompareValSmaller(a, b)
local res = singleCardCompare(a%0x10, b%0x10);
if res >= 0 then
return false
else
return true;
end
end
local function copy_array(arr)
local newarr = {};
for i,v in ipairs(arr) do
if type(v) == type({}) and #v ~= 0 then
table.insert(newarr, copy_array(v));
else
table.insert(newarr, v);
end
end
return newarr;
end
local function merge_array(a1, a2)
local new = {};
for i,v in ipairs(a1 or {}) do
table.insert(new, v);
end
for i,v in ipairs(a2 or {}) do
table.insert(new, v);
end
return new;
end
local function split_array(arr, st, en)
local newarr = {};
en = en or #arr;
for i = en, st, -1 do
table.insert(newarr, 1, arr[i]);
table.remove(arr, i);
end
return newarr, arr;
end
local function rm_arr_by_pos(arr, pos_arr)
local rm_arr = {};
table.sort(pos_arr, function (a1,a2) return a1 > a2; end);
for i,pos in ipairs(pos_arr) do
table.insert(rm_arr, arr[pos]);
table.remove(arr, pos);
end
return rm_arr;
end
local function get_arr_by_pos(arr, pos_arr)
local new = {};
for i,pos in ipairs(pos_arr) do
table.insert(new, arr[pos]);
end
return new;
end
local function get_hua_bucket(cards)
local bucket = {};
for i = 1, 4, 1 do table.insert(bucket, {}); end
for i = 1, #cards, 1 do
local val = math.floor((cards[i]+0x10)/0x10);
table.insert(bucket[val], i);
end
return bucket;
end
local function get_ppos_bucket(cards, pos_arr)
local bucket = {};
for i = 1, 13, 1 do table.insert(bucket, {}); end
for i = 1, #pos_arr, 1 do
local val = cards[pos_arr[i]]%0x10;
table.insert(bucket[val], pos_arr[i]);
end
return bucket;
end
local function get_pos_bucket(cards, length)
local bucket = {};
for i = 1, 13, 1 do table.insert(bucket, {}); end
for i = 1, #cards, 1 do
local val = cards[i]%0x10;
table.insert(bucket[val], i);
end
return bucket;
end
local function make_ranks(cards, num)
if #cards == num then
return {cards};
end
local ranks = {};
local count = #cards - num + 1;
for i = 1, count, 1 do
if num > 1 then
local newcards = copy_array(cards);
local rmcards, leftcards = split_array(newcards, 1, i);
local child_ranks = make_ranks(leftcards, num-1);
for ii,vv in ipairs(child_ranks) do
table.insert(vv, 1, cards[i])
table.insert(ranks, vv);
end
elseif num == 1 then
local rank = {cards[i]};
table.insert(ranks, rank);
end
end
return ranks;
end
local function isTHua(cards, pos_arr)
local newcards = get_arr_by_pos(cards, pos_arr);
local bucket = get_hua_bucket(newcards);
for i,v in ipairs(bucket) do
if #v >= 5 then return true; end
end
return false;
end
local function isShunZi(cards, pos_arr)
local newcards = get_arr_by_pos(cards, pos_arr);
local bucket = get_pos_bucket(newcards);
local cnt = 0;
for i,v in ipairs(bucket) do
if #v ~= 0 then
cnt = cnt + 1;
if cnt >= 5 then return true; end
else
cnt = 0;
end
end
if cnt == 4 then
if #bucket[1] ~= 0 then
return true;
end
end
return false;
end
local function regWTongFunc(cards)
local bucket = get_pos_bucket(cards);
local wtong_arr = {};
for i,v in ipairs(bucket) do
if #v > 5 then
local wtongs = make_ranks(cards, 5);
for ii,vv in ipairs(wtong_arr) do
table.insert(wtong_arr, vv);
end
elseif #v == 5 then
table.insert(wtong_arr, v);
end
end
local ranks = {};
for i,wtong in ipairs(wtong_arr) do
local newcards = copy_array(cards);
local rmcards = rm_arr_by_pos(newcards, wtong);
table.insert(ranks, {cards = rmcards, next = newcards});
end
return ranks;
end
local function regTHShunFunc(cards)
local bucket_hua = get_hua_bucket(cards);
local thshun_arr = {};
for i,v in ipairs(bucket_hua) do
if #v >=5 then
local bucket_pos = get_ppos_bucket(cards, v);
local thshun_pos = {};
for ii,vv in ipairs(bucket_pos) do
if #vv ~= 0 then
table.insert(thshun_pos, vv[1]);
if #thshun_pos == 5 then
table.insert(thshun_arr, copy_array(thshun_pos));
table.remove(thshun_pos, 1);
end
else
thshun_pos = {};
end
end
if #thshun_pos == 4 then
if #bucket_pos[1] ~= 0 then
table.insert(thshun_pos, bucket_pos[1][1]);
table.insert(thshun_arr, thshun_pos);
end
end
end
end
local ranks = {};
for i,thshun in ipairs(thshun_arr) do
local newcards = copy_array(cards);
local rmcards = rm_arr_by_pos(newcards, thshun);
table.insert(ranks, {cards = rmcards, next = newcards});
end
return ranks;
end
local function regTZhiFunc(cards)
local bucket = get_pos_bucket(cards);
local tzhi_arr = {};
local wulong = {};
for i,v in ipairs(bucket) do
if #v > 4 then
local tzhis = make_ranks(v, 4);
for i,v in ipairs(tzhis) do
table.insert(tzhi_arr, v);
end
elseif #v == 4 then
table.insert(tzhi_arr, v);
end
end
local ranks = {};
for i,tzhi in ipairs(tzhi_arr) do
local newcards = copy_array(cards);
local rmcards = rm_arr_by_pos(newcards, tzhi);
table.insert(ranks, {cards = rmcards, next = newcards});
end
return ranks;
end
local function regHLuFunc(cards)
local bucket = get_pos_bucket(cards);
local san_arr = {};
local same_arr = {};
local dui_arr = {};
local tzhis = {};
for i,v in ipairs(bucket) do
if #v > 4 then
return {};
elseif #v == 4 then
tzhis[i] = true;
local sans = make_ranks(v, 3);
for i,v in ipairs(sans) do
sans.val = i;
table.insert(san_arr, v);
end
elseif #v == 3 then
v.val = i;
table.insert(san_arr, v);
elseif #v == 2 then
table.insert(dui_arr, v);
end
end
if #san_arr == 0 or #san_arr == 1 and #dui_arr == 0 then
return {};
end
local ranks = {};
for i,san in ipairs(san_arr) do
for ii,dui in ipairs(dui_arr) do
local newcards = copy_array(cards);
local hlu = merge_array(dui, san);
local rmcards = rm_arr_by_pos(newcards, hlu);
table.insert(ranks, {cards = rmcards, next = newcards});
end
for ii,duis in ipairs(san_arr) do
if duis.val ~= san.val and tzhis[duis.val] == nil then
local dui_arr = make_ranks(duis, 2);
for iii,dui in ipairs(dui_arr) do
local newcards = copy_array(cards);
local hlu = merge_array(dui, san);
local rmcards = rm_arr_by_pos(newcards, hlu);
table.insert(ranks, {cards = rmcards, next = newcards});
end
end
end
end
return ranks;
end
local function regTHuaFunc(cards)
local bucket = get_hua_bucket(cards);
local ranks = {};
for i,v in ipairs(bucket) do
if #v >= 5 then
local thua_ranks = make_ranks(v, 5);
for ii,thua in ipairs(thua_ranks) do
if not isShunZi(cards, thua) then
local newcards = copy_array(cards);
local rmcards = rm_arr_by_pos(newcards, thua);
table.insert(ranks, {cards = rmcards, next = newcards});
end
end
end
end
return ranks;
end
local function regShZiFunc(cards)
local bucket = get_pos_bucket(cards)
local cont_pos = {};
local shunzi_arr = {};
for i,v in ipairs(bucket) do
if #v ~= 0 then
table.insert(cont_pos, v[1]);
if #cont_pos == 5 then
local shunzi = copy_array(cont_pos);
if not isTHua(cards, shunzi) then
table.insert(shunzi_arr, shunzi);
end
table.remove(cont_pos, 1);
end
else
cont_pos = {};
end
end
if #cont_pos == 4 then
if #bucket[1] ~= 0 then
table.insert(cont_pos, bucket[1][1]);
if not isTHua(cards, cont_pos) then
table.insert(shunzi_arr, cont_pos);
end
end
end
local ranks = {};
for i,shunzi in ipairs(shunzi_arr) do
local newcards = copy_array(cards);
local rmcards = rm_arr_by_pos(newcards, shunzi);
table.insert(ranks, {cards = rmcards, next = newcards});
end
return ranks;
end
local function regSZhangFunc(cards, lv)
local bucket = get_pos_bucket(cards);
local max_val;
local wulong = {};
for i,v in ipairs(bucket) do
if #v > 3 then
return {};
elseif #v == 3 then
if max_val ~= 1 then
max_val = i;
end
elseif #v == 1 then
table.insert(wulong, v[1]);
end
end
local ranks = {};
if max_val then
local splitcards;
if lv < 3 then
if #wulong < 2 then return {}; end
table.sort(wulong, function (a, b) return singleCardCompareValSmaller(cards[a]%0x10, cards[b]%0x10) end);
splitcards = split_array(wulong, 1, 2);
end
local newcards = copy_array(cards);
local szhang = merge_array(bucket[max_val], splitcards);
local rmcards = rm_arr_by_pos(newcards, szhang);
table.insert(ranks, {cards = rmcards, next = newcards});
end
return ranks;
end
local function regDDuiFunc(cards, lv)
if lv == 3 then
return {};
end
local bucket = get_pos_bucket(cards);
local max_val;
local min_val;
local min_single;
for i,v in ipairs(bucket) do
if #v > 2 then
return {};
elseif #v == 2 then
if max_val ~= 1 then
max_val = i;
end
if not min_val or min_val == 1 then
min_val = i;
end
elseif #v == 1 then
if not min_single or min_single == 1 then
min_single = i
end
end
end
local ranks = {};
if min_val and max_val and min_val ~= max_val then
local ddui_pos = merge_array(bucket[max_val], bucket[min_val]);
local newcards = copy_array(cards);
local rmcards = rm_arr_by_pos(newcards, ddui_pos);
table.insert(ranks, {cards = rmcards, next = newcards});
end
return ranks;
end
local function regDuiFunc(cards, lv)
local bucket = get_pos_bucket(cards);
local max_val;
local wulong = {};
for i,v in ipairs(bucket) do
if #v > 2 then
return {};
elseif #v == 2 then
if max_val ~= 1 then
max_val = i;
end
elseif #v == 1 then
table.insert(wulong, v[1]);
end
end
local ranks = {};
if max_val then
local split_len = lv == 3 and 1 or 3;
table.sort(wulong, function (a, b) return singleCardCompareValSmaller(cards[a]%0x10, cards[b]%0x10) end);
local splitcards = split_array(wulong, 1, split_len);
local newcards = copy_array(cards);
local dui = merge_array(bucket[max_val], splitcards);
local rmcards = rm_arr_by_pos(newcards, dui);
table.insert(ranks, {cards = rmcards, next = newcards});
end
return ranks;
end
local function regWLongFunc(cards, lv)
local bucket = get_pos_bucket(cards);
for i,v in ipairs(bucket) do
if #v > 1 then
return {};
end
end
local newcards = copy_array(cards);
if lv == 3 then
table.sort(newcards, singleCardCompareValSmaller);
local three_cards, last_cards = split_array(newcards, #newcards-2, #newcards);
return {{cards = three_cards, next = last_cards}};
else
table.sort(newcards, singleCardCompareValSmaller);
local max_card = newcards[#newcards];
table.remove(newcards, #newcards);
local splitcards, leftcards = split_array(newcards, 1, 4);
table.insert(splitcards, 1, max_card);
return {{cards = splitcards, next = leftcards}};
end
end
local regTypeList = {
{get = regWLongFunc; tp = 1; weight=0;};
{get = regDuiFunc; tp = 2; weight=2;};
{get = regDDuiFunc; tp = 3; weight=3;};
{get = regSZhangFunc; tp = 4; weight=4;};
{get = regShZiFunc; tp = 5; weight=5;};
{get = regTHuaFunc; tp = 6; weight=6;};
{get = regHLuFunc; tp = 7; weight=7;};
{get = regTZhiFunc; tp = 8; weight=8;};
{get = regTHShunFunc; tp = 9; weight=9;};
{get = regWTongFunc; tp = 10; weight=10;};
};
local function calcTypeRanks(cards, lv, tp)
if not lv then lv = 1; end
if not tp then tp = #regTypeList; end
if lv > 3 then
return cards;
elseif lv == 3 and tp > 4 then
tp = 4;
end
local bottom_tp = 1;
if lv == 1 then bottom_tp = 2; end
local ranks = {};
for i = tp, bottom_tp, -1 do
local regType = regTypeList[i];
local tranks = regType.get(cards, lv);
if #tranks ~= 0 then
for ii, trank in ipairs(tranks) do
local rank = {};
rank.cards = trank.cards;
rank.tp = i;
rank.next = calcTypeRanks(trank.next, lv + 1, i);
table.insert(ranks, rank);
end
end
end
return ranks;
end
local function compareCard(c1, c2)
local bc1 = ffi.new(string.format("BYTE[%d]", #c1), unpack(c1));
local bc2 = ffi.new(string.format("BYTE[%d]", #c2), unpack(c2));
local res = lib.CompareCard(bc1, bc2, #c1, #c2, bOnlyCompareNumber, bFuncVersion);
return res
end
local function getAllRanks(cards)
local all_ranks = calcTypeRanks(cards);
local ranks = {};
local special_ranks = {};
for i,v in ipairs(all_ranks) do
for ii,vv in ipairs(v.next) do
for iii,vvv in ipairs(vv.next) do
local leftcards = vvv.next;
local rank = {
{cards = v.cards, tp = v.tp},
{cards = vv.cards, tp = vv.tp},
{cards = vvv.cards, tp = vvv.tp}
};
if #leftcards ~= 0 then
table.sort(leftcards, singleCardCompareValSmaller);
for x,y in ipairs(rank) do
local length = x == 3 and 3 or 5;
local leftlen = length-#y.cards;
if leftlen ~= 0 then
local wl = split_array(leftcards, 1, leftlen);
for xx,yy in ipairs(wl) do
table.insert(y.cards, yy);
end
end
end
end
if compareCard(rank[1].cards, rank[2].cards) ~= 1 then
if rank[3].tp == CT_THREE or rank[2].tp >= CT_FIVE_THREE_DEOUBLE or rank[1].tp > CT_FIVE_THREE_DEOUBLE then
table.insert(special_ranks, rank);
else
table.insert(ranks, rank);
end
end
end
end
end
if #special_ranks ~= 0 then
return special_ranks;
end
return ranks;
end
local function getCardType(data, cnt, spmydata, ver)
if type(data) == 'table' then
data = ffi.new('BYTE[13]', unpack(data));
end
lib.SortCardList(data, cnt, ffi.C.enDescend);
local n = lib.GetCardType(data, cnt, spmydata or spcard, ver or bFuncVersion);
return n, spmydata or spcard;
end
local function getSpecialType(cards)
local specialcards = ffi.new("BYTE[13]");
local tp = getCardType(cards, #cards, specialcards);
if tp ~= 0 then
local str = ffi.string(specialcards, ffi.sizeof(specialcards));
local first = {string.byte(str, 1, 3)};
local second = {string.byte(str, 4, 8)};
local third = {string.byte(str, 9, 13)};
return first, second, third, tp;
end
end
local function chooseXdaoMax(allranks, level)
local len = #allranks;
local xdao = allranks[1];
local ranks = {allranks[1]};
if xdao then
for i=2, len, 1 do
if level == 3 then
if compareCard(xdao[level].cards, allranks[i][level].cards) == 1 then
xdao = allranks[i];
ranks = {allranks[i]};
end
elseif xdao[level].tp < allranks[i][level].tp then
xdao = allranks[i];
ranks = {allranks[i]};
elseif xdao[level].tp == allranks[i][level].tp then
table.insert(ranks, allranks[i]);
end
end
end
return ranks;
end
local function getMaxWeightRanks(allranks)
local weight_sum = 0;
local ranks = {allranks[1]};
for i,v in ipairs(allranks) do
local sum = 0;
for ii,vv in ipairs(v) do
sum = regTypeList[vv.tp].weight + sum;
end
if sum > weight_sum then
ranks = {v};
weight_sum = sum;
elseif sum == weight_sum then
table.insert(ranks, v);
end
end
return ranks;
end
local function getBestRank(cards, expect, filter_priority)
if not filter_priority then filter_priority = {2,3,1}; end
local first, second, last, tp = getSpecialType(cards);
if first then
return first, second, last, tp;
end
local allranks = getAllRanks(cards);
local maxranks = getMaxWeightRanks(allranks);
maxranks = chooseXdaoMax(maxranks, filter_priority[1]);
maxranks = chooseXdaoMax(maxranks, filter_priority[2]);
maxranks = chooseXdaoMax(maxranks, filter_priority[3]);
local outrank = maxranks[math.random(#maxranks)];
first = outrank[3].cards;
second = outrank[2].cards;
last = outrank[1].cards;
table.sort(first, singleCardCompareValSmaller);
table.sort(second, singleCardCompareValSmaller);
table.sort(last, singleCardCompareValSmaller);
return first, second, last;
end
local function getRank(cards, expect, filter_priority)
if not filter_priority then filter_priority = {2,3,1}; end
local first, second, last, tp = getSpecialType(cards);
if first then
return first, second, last, tp;
end
local allranks = getAllRanks(cards);
local maxranks = allranks;
maxranks = chooseXdaoMax(maxranks, filter_priority[1]);
maxranks = chooseXdaoMax(maxranks, filter_priority[2]);
maxranks = chooseXdaoMax(maxranks, filter_priority[3]);
local outrank = maxranks[math.random(#maxranks)];
first = outrank[3].cards;
second = outrank[2].cards;
last = outrank[1].cards;
table.sort(first, singleCardCompareValSmaller);
table.sort(second, singleCardCompareValSmaller);
table.sort(last, singleCardCompareValSmaller);
return first, second, last;
end
return {
getAllRanks = getAllRanks;
getRank = getRank;
getBestRank = getBestRank;
make_ranks = make_ranks;
}