天凤牌谱里的牌山数据是在牌谱的SHUFFLE标签里
例如<SHUFFLE seed="mt19937ar-sha512-n288-base64,牌山数据....">
先介绍下几个概念
mt19937ar是一个产生随机数的算法
sha512 哈希算法
n288 在SRC数组里取前288个元素
base64 字符串压缩算法
要还原牌山,需要经过几个步骤:
1.base64解码
把牌山那段看起来是乱码的字符串解码得到二进制的字节数组,长度为624,然后序列化成无符号4字节整型数组,C#的类型是UInt32[]
这个数组称为INIT数组,作用是长生随机数的种子
2.使用INIT作为随机数种子,进行mt199327ar算法的种子初始化,注意只能进行一次初始化
3.循环使用使用mt199327ar返回一个随机数,一共288次,得到一个长度为288的随机数 数组,类型为无符号4字节整型数组,C#的类型是UInt32[]
这个数组称为SRC数组,作用是用于SHA512哈希。
4.把SRC数组写入二进制流,获得它的字节数组SRC_2,对SRC_2循环进行SHA512哈希,哈希的输入偏移是i*1024/8,长度是1024/8,输出偏移是i*512/8(长度是512/8)
一共循环8次,C#代码如下(RNDMS为输出流,bs就是SRC_2):
for(int i=0; i<9;i++)
{
SHA512CryptoServiceProvider sha5112 = new SHA512CryptoServiceProvider();
var block=sha5112.ComputeHash(bs,i*1024/8, 1024 / 8);
RNDMS.Seek(i * 512 / 8, SeekOrigin.Begin);
RNDMS.Write(block, 0, block.Length);
}
把输出流序列化为无符号4字节整数数组,该数组称为RND,C#的类型是UInt32[]
5.利用RND数组生成牌山,牌山为长度136的数组,基本思路是对RND数组的元素进行求余
C#代码:
Int32[] yama = new Int32[136];
for (int i = 0; i < 136; i++)
yama[i] = i;
for(int i=0;i<136-1;i++)
{
swap(ref yama[i],ref yama[i + (RND[i] % (136 - i))]);
}
//swap指的是交换两个变量的值。
最终得到yama数组
6.yama一维数组里面存放牌代码,顺序为从牌山下层到上层,从牌山末端(王牌)到配牌端。
例如yama[0]表示的是第二个岭上牌,yama[1]表示第一个岭上牌。
获取第一局的游戏牌山需要从第一步执行到第六步,第二局及之后只需要执行第三步到第六步(即随机数初始化只需要一次)。
例如<SHUFFLE seed="mt19937ar-sha512-n288-base64,牌山数据....">
先介绍下几个概念
mt19937ar是一个产生随机数的算法
sha512 哈希算法
n288 在SRC数组里取前288个元素
base64 字符串压缩算法
要还原牌山,需要经过几个步骤:
1.base64解码
把牌山那段看起来是乱码的字符串解码得到二进制的字节数组,长度为624,然后序列化成无符号4字节整型数组,C#的类型是UInt32[]
这个数组称为INIT数组,作用是长生随机数的种子
2.使用INIT作为随机数种子,进行mt199327ar算法的种子初始化,注意只能进行一次初始化
3.循环使用使用mt199327ar返回一个随机数,一共288次,得到一个长度为288的随机数 数组,类型为无符号4字节整型数组,C#的类型是UInt32[]
这个数组称为SRC数组,作用是用于SHA512哈希。
4.把SRC数组写入二进制流,获得它的字节数组SRC_2,对SRC_2循环进行SHA512哈希,哈希的输入偏移是i*1024/8,长度是1024/8,输出偏移是i*512/8(长度是512/8)
一共循环8次,C#代码如下(RNDMS为输出流,bs就是SRC_2):
for(int i=0; i<9;i++)
{
SHA512CryptoServiceProvider sha5112 = new SHA512CryptoServiceProvider();
var block=sha5112.ComputeHash(bs,i*1024/8, 1024 / 8);
RNDMS.Seek(i * 512 / 8, SeekOrigin.Begin);
RNDMS.Write(block, 0, block.Length);
}
把输出流序列化为无符号4字节整数数组,该数组称为RND,C#的类型是UInt32[]
5.利用RND数组生成牌山,牌山为长度136的数组,基本思路是对RND数组的元素进行求余
C#代码:
Int32[] yama = new Int32[136];
for (int i = 0; i < 136; i++)
yama[i] = i;
for(int i=0;i<136-1;i++)
{
swap(ref yama[i],ref yama[i + (RND[i] % (136 - i))]);
}
//swap指的是交换两个变量的值。
最终得到yama数组
6.yama一维数组里面存放牌代码,顺序为从牌山下层到上层,从牌山末端(王牌)到配牌端。
例如yama[0]表示的是第二个岭上牌,yama[1]表示第一个岭上牌。
获取第一局的游戏牌山需要从第一步执行到第六步,第二局及之后只需要执行第三步到第六步(即随机数初始化只需要一次)。