斗牛(牛牛)贏牌概率模擬計算器


最近做了一個牛牛模擬計算的小程序,總體說來不難,關鍵地方就是優化,提高計算速度。

規則是用戶知道自己的四張手牌,然后模擬出來自己的勝率和收益。

大概思路就是給模擬用戶還有用戶自己模擬發牌,最后計算所有人的權值,找出最大的權值來判斷收益。

52張牌我由m=1-52數字表示,m%13表示每張牌的大小,m/13表示牌的花色,當然13,26,39,52,這種特殊數字返回特定的花色。

1.計算牌的花色

/// <summary>
/// 返回牌的花色
/// </summary>
/// <param name="brand"></param>
/// <returns></returns>
private static int BackColor(int brand)
{
if (brand == 13) return 0;
if (brand == 26) return 1;
if (brand == 39) return 2;
if (brand == 52) return 3;
return brand / 13;
}
2.計算牌的大小

/// <summary>
/// 返回牌的大小
/// </summary>
/// <param name="brand">牌</param>
/// <returns></returns>
private static int BackValue(int brand)
{
if (brand % 13 == 0) return 13;
return brand % 13;
}
因為牌的大小在做累加的時候,10,J,Q,K都是按照0計算的,所以需要累加的時候,需要另一個方法

/// <summary>
/// 返回牌的大小 如果 大於9 則返回0
/// </summary>
/// <param name="brand"></param>
/// <returns></returns>
private static int BackZero(int brand)
{
if (brand % 13 > 9)
{
return 0;
}
else
{
return brand % 13;
}
}
3.返回兩張牌的大牌

兩張牌一定有一張大一張小,因為牛牛規則是如果牌的大小相等,則比較牌的花色。

/// <summary>
/// 返回兩張牌較大的牌
/// </summary>
/// <param name="Abrand"></param>
/// <param name="Bbrand"></param>
/// <returns></returns>
private static int BackCompareMax(int Abrand, int Bbrand)
{
if (BackValue(Abrand) > BackValue(Bbrand))
{
return Abrand;
}
else if (BackValue(Abrand) < BackValue(Bbrand))
{
return Bbrand;
}
else
{
if (BackColor(Abrand) > BackColor(Bbrand))
{
return Abrand;
}
else
{
return Bbrand;
}
}
}
4.將用戶的四張手牌按照從小到大的順序排序

 因為權值除了比較值的大小之外還要比較牌的花色,所以不能直接用list自帶的排序方法

/// <summary>
/// 將手牌按照值和花色大小排序 從小到大
/// </summary>
/// <returns></returns>
private static List<int> ListSort(List<int> lis)
{
for (int i = 0; i < lis.Count - 1; i++)
{
for (int j = i + 1; j < lis.Count; j++)
{
if (lis[i] == BackCompareMax(lis[i], lis[j]))
{
lis[i] = lis[i] + lis[j];
lis[j] = lis[i] - lis[j];
lis[i] = lis[i] - lis[j];
}
}
}
return lis;
}
 5.獲取手牌的和

 獲取和的時候10,J,Q,K都是按照0算的,把手牌作和是為了后面方便判斷手牌的類型

/// <summary>
/// 獲取手牌的和值(10,J,Q,K算0)
/// </summary>
/// <param name="lis"></param>
/// <returns></returns>
private static int Getsum(List<int> lis)
{
int sum = 0;
for (int i = 0; i < lis.Count; i++)
{
sum += BackZero(lis[i]);
}
return sum;
}
 6.將用戶輸入的牌除開之后,剩下的牌組的牌作為一個list存放起來,然后隨機選取牌組里面的一張牌分配給用戶,再隨機選取五張牌分配給一個模擬用戶,然后計算五張牌的權值。

這里我的程勛是按照牛牛類型的大小從上往下排的,因為牌型都是選取最大的牌型來比較。

這里我把每個不同的牌型都是用四位數表示。

千位數表示牌的類型,百位數是專門為牛准備的,百位數就是牛X,后面的兩位數就是五張牌里面的最大牌。因為牛牛規則比較大小的時候首先是比較類型,類型大的就贏,若類型相同則比較最大牌(牛除外),如果是牛的話類型相同就比較牛的大小,如果牛相同的話就比較最大牌的值,如果值相等則比較花色。

/// <summary>
/// 返回五張牌中最大的牌型 9-1 為五小牛 到散牌
/// </summary>
/// <param name="listbrand">5張手牌</param>
/// <returns></returns>
private static int BackMaxType(List<int> listbrand)
{
//ata[10]++;
listbrand = ListSort(listbrand);
int sums = Getsum(listbrand);
//判斷是否是五小牛
int sum = 0;
if(sums==10 && BackValue(listbrand[4]) <5) return (listbrand[4] + 9000);
//判斷是否是炸彈
if (BackValue(listbrand[0]) == BackValue(listbrand[3]))
{
return listbrand[3] + 8000;
}
if (BackValue(listbrand[1]) == BackValue(listbrand[4]))
{
return listbrand[4] + 8000;
}
//判斷是否是葫蘆牛 (AAABB or AABBB)
if (BackValue(listbrand[0]) == BackValue(listbrand[2]) && BackValue(listbrand[3]) == BackValue(listbrand[4]))
{
return listbrand[2] + 7000;
}
if (BackValue(listbrand[0]) == BackValue(listbrand[1]) && BackValue(listbrand[2]) == BackValue(listbrand[4]))
{
return listbrand[4] + 7000;
}
//判斷是否是五花牛
if (BackValue(listbrand[0]) > 10) return listbrand[4] + 6000;
//判斷是否是同花牛
if (BackColor(listbrand[0]) == BackColor(listbrand[1]) && BackColor(listbrand[1]) == BackColor(listbrand[2]) && BackColor(listbrand[2]) == BackColor(listbrand[3])
&& BackColor(listbrand[3]) == BackColor(listbrand[4]))
{
return listbrand[4] + 5000;
}
//判斷是否是順子牛
if (BackValue(listbrand[4]) - BackValue(listbrand[3]) == 1 && BackValue(listbrand[3]) - BackValue(listbrand[2]) == 1 &&
BackValue(listbrand[2]) - BackValue(listbrand[1]) == 1 && BackValue(listbrand[1]) - BackValue(listbrand[0]) == 1)
{
return listbrand[4] + 4000;
}
//判斷是否是牛牛
//首先五張牌相加一定是10的倍數
if (sums % 10 == 0)
{
//再取其中兩張相加為10的倍數則另外三張加起來也一定是10的倍數
//即牛牛
for (int i = 0; i < listbrand.Count; i++)
{
for (int j = i + 1; j < listbrand.Count; j++)
{
if ((BackZero(listbrand[i]) + BackZero(listbrand[j])) % 10 == 0)
{
return listbrand[4] + 3000;
}
}
}
}
//判斷 是否有牛
sum = sums;
for (int i = 0; i < listbrand.Count - 1; i++)
{
for (int j = i + 1; j < listbrand.Count; j++)
{
if ((sum - BackZero(listbrand[i]) - BackZero(listbrand[j])) % 10 == 0)
{
int va = (BackZero(listbrand[i]) + BackZero(listbrand[j])) % 10; //獲取牛值

return listbrand[4] + 2000+ va*100;
}
}
}
//判斷散牌
return listbrand[4] + 1000;
}
7.根據五張牌的權值來比較大小

這里的比較很簡單了,規則就想6里面說的那樣比較,

/// <summary>
/// 找出兩幅牌的大牌
/// </summary>
/// <param name="brand"></param>
/// <returns></returns>
private static int BackMaxBrand(int Abrand, int Bbrand)
{
if (Math.Abs(Abrand - Bbrand) > 51)//不屬於同一類型
{
return Math.Max(Abrand, Bbrand);
}
else//屬於同一類型
{
int a = Abrand;
int b = Bbrand;
//去除類型比較牌的大小
if (a > 2000 && a < 3000) //判斷是否是牛
{
//比較牛的大小
if ((a - 2000) / 100 > (b - 2000) / 100)
{
return Abrand;
}
else if ((a - 2000) / 100 < (b - 2000) / 100)
{
return Bbrand;
}
else //牛相等 比較最大牌
{
a = a - 2000;
b = b - 2000;
while (a > 52 && b > 52)
{
a = a - 100;
b = b - 100;
}
if (a == BackCompareMax(a, b))
{
return Abrand;
}
else
{
return Bbrand;
}
}
}
a = Abrand;
b = Bbrand;
while (a > 52 && b > 52)
{
a = a - 1000;
b = b - 1000;
}
if (a == BackCompareMax(a, b))
{
return Abrand;
}
else
{
return Bbrand;
}
}
}
 8.計算牌的倍數,不一樣的牌型有不一樣的倍數,可以根據自己玩的規則修改

/// <summary>
/// 返回牌的倍數
/// </summary>
/// <param name="brand"></param>
/// <returns></returns>
private static int BackTimes(int brand)
{
if (brand < 2000)
{
return 1; //無牛
}
else if (brand < 3000)//牛
{
//判斷是牛7 牛8 還是 牛 9 還是普通牛
if (brand - 2900 > 0) return 3;
if (brand - 2800 > 0) return 2;
if (brand - 2700 > 0) return 2;
return 1;
}
else if (brand < 4000) //牛牛
{
return 4;
}
else if (brand < 5000) //順子牛
{
return 5;
}
else if (brand < 6000)//同花牛
{
return 5;
}
else if (brand < 7000)//五花
{
return 5;
}
else if (brand < 8000)//葫蘆
{
return 6;
}
else if (brand < 9000) //炸彈
{
return 6;
}
else //五小
{
return 8;
}
}
10.給用戶模擬發牌

 首先給用戶模擬發一張牌,計算權值,然后判斷他的類型,最后做一個類型的累加。

然后給模擬用戶模擬發牌,計算權值之后與用戶牌比較大小,留下大牌。

最后判斷留下的大牌是否是用戶的牌,如果不是則收益上減去最大牌的倍數,如果是就加最大牌的倍數

當然如果輸贏都記錄下次數,方便計算贏牌概率。

/// <summary>
/// 線程跑動
/// </summary>
/// <param name="num">發牌次數</param>
/// <param name="lis">剩余牌</param>
/// <param name="bran">用戶手牌</param>
/// <param name="user">用戶數</param>
/// <returns></returns>
private static void ThreadBackA(int num, List<int> list, List<int> brans, int user)
{
Random rd = new Random();
List<int> lis = new List<int>();
List<int> bran = new List<int>();
while (num > 0)
{
lis.Clear();
lis.AddRange(list);
bran.Clear();
bran.AddRange(brans);
int next = rd.Next(0, lis.Count);
bran.Add(lis[next]);//給用戶的手牌
lis.RemoveAt(next);
int YH = BackMaxType(bran);
if (YH < 2000)
{
data[1]++; //無牛
}
else if (YH < 3000) //牛
{
data[2]++;
}
else if (YH < 4000) //牛牛
{
data[3]++;
}
else if (YH < 5000)//順子牛
{
data[4]++;
}
else if (YH < 6000)//同花牛
{
data[5]++;
}
else if (YH < 7000)//五花牛
{
data[6]++;
}
else if (YH < 8000)//葫蘆牛
{
data[7]++;
}
else if (YH < 9000)//炸彈
{
data[8]++;
}
else
{
data[9]++; //五小牛
}
int max = YH; //最大牌
//int cesji = BackMaxType(bran);
for (int i = 0; i < user; i++)
{
List<int> us = new List<int>();
for (int j = 0; j < 5; j++)
{
next = rd.Next(0, lis.Count);
us.Add(lis[next]);
lis.RemoveAt(next);
}
max = BackMaxBrand(max, BackMaxType(us));
}
if (max != YH)
{
data[0]++;
data[11] = data[11] - BackTimes(max);
}
else
{
data[10]++;
data[11] = data[11] + BackTimes(max);
}
num--;
}
}
 11.異步task跑動

Tsak數組有多長就有幾個異步task跑動,我這里是五個線程一共模擬發牌一百萬次(個人覺得沒必要這么多次,10萬次就夠了,最后結果相差不過零點幾的誤差,可有可無。)

最后返回的是一個數組,里面有輸贏的次數,總收益,還有用戶手牌出現類型的次數

public static int[] GetTransport(List<int> lis,int user)
{
data=new int[12];
HandAardA = lis;
GetAllHandAard(HandAardA);
//異步task
Task[] tasks = new Task[5];
for (int i = 0; i < tasks.Length; i++)
{
tasks[i] = new Task(() => { ThreadBackA(200000, AllHandAard, HandAardA, user); });
tasks[i].Start();
}
//Thread.Sleep(1000);
Task.WaitAll(tasks);
return data;
}
12.簡陋界面 

 

大概就是這么多了。

end 

 

需要的朋友可以聯系我weixin:7350815

(有償)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM