在扑克牌游戏中,生成一幅随机打乱的牌型,然后分发给玩家,是必须要实现的基本功能。
基本原理肯定是使用随机数,但是只有随机数达不到效果,因为是要随机地打乱顺序,而不是仅仅生成54个随机数。因为随机数有可能是重复的。
下面是用sql-server 的存储过程实现的随机发牌功能:
CREATE procedure dealCards
(
@deskID int,
@outMsg varchar(500) out
)
as
begin
declare @lbound int, @ubound int
select @lbound=1,@ubound =54
declare @t table (idx int identity(1,1),r int )
declare @x table (idx int,r int)
--1~54不重复的随机数
while (select count(*) from @t ) < @ubound-@lbound +1
begin
insert into @x (idx,r) select i,abs(checksum(newid()))%(@ubound-@lbound +1) + 1 r from nums where i < =3*(@ubound-@lbound +1)
insert into @t(r)
select r from
(
select min(idx) idx ,r from @x group by r
) t where r not in (select r from @t) order by idx
end
declare @p1Cards varchar(100),@p2Cards varchar(100),@p3Cards varchar(100),@xCards varchar(100)
set @p1Cards =''
select @p1Cards =@p1Cards + cast(r as varchar) +',' from @t where idx <= 17
set @p1Cards=left(@p1Cards,len(@p1Cards)-1)
set @p2Cards =''
select @p2Cards =@p2Cards + cast(r as varchar) +',' from @t where idx > 17 and idx <= 34
set @p2Cards=left(@p2Cards,len(@p2Cards)-1)
set @p3Cards =''
select @p3Cards =@p3Cards + cast(r as varchar) +',' from @t where idx > 34 and idx <= 51
set @p3Cards=left(@p3Cards,len(@p3Cards)-1)
set @xCards =''
select @xCards =@xCards + cast(r as varchar) +',' from @t where idx > 51 and idx <= 54
set @xCards=left(@xCards,len(@xCards)-1)
declare @p1ID int,@p2ID int,@p3ID int
select @p1ID = p1ID,@p2ID = p2ID ,@p3ID = p3ID from v_playerIDInRoom where deskID = @deskID
declare @gameID int
insert into game(deskId,p1cards,p2cards,p3cards,extraCards,stateNo,lordNo,p1ID,p2ID,p3ID)
values(@deskID,@p1cards,@p2cards,@p3cards,@xCards,1,0,@p1ID,@p2Id,@p3ID)
set @gameID =@@identity
update desk set curGameID = @gameID
where deskID =@deskID
--随机产生第一个叫牌者,并在叫牌记录表中预放两条记录
declare @rndPlayer int
set @rndPlayer = abs(checksum(newid()))%3+1
insert into bidRecord(deskID,gameID,playerNo,recIdx,bidSize)
values(@deskID,@gameId,dbo.prevPlayer(@rndPlayer),0,-1)
insert into bidRecord(deskID,gameID,playerNo,recIdx,bidSize)
values(@deskID,@gameId,dbo.prevPlayer(@rndPlayer),-1,-1)
--设置游戏状态为叫牌
update game set stateNo = 1
where deskId = @deskID and gameID = @gameID
set @outMsg='{desk:'+cast(@deskID as varchar) +',game:'+cast(@gameID as varchar) +',leader:'+cast(@rndPlayer as varchar)+'}'
--发通告
declare @note varchar(500)
set @note = cast(@rndPlayer as varchar) +'#'+cast(@gameID as varchar)
exec writeNote @deskID,@rndPlayer,3,'y',@note
if dbo.debugMode() = 1
update debugValue set gameID = @gameID ,rndPlayer=@rndPlayer
return 0
end
其中最核心的功能如下,用到了T-SQL中的表变量
declare @lbound int, @ubound int
select @lbound=1,@ubound =54
declare @t table (idx int identity(1,1),r int )
declare @x table (idx int,r int)
--1~54不重复的随机数
while (select count(*) from @t ) < @ubound-@lbound +1
begin
insert into @x (idx,r) select i,abs(checksum(newid()))%(@ubound-@lbound +1) + 1 r from nums where i < =3*(@ubound-@lbound +1)
insert into @t(r)
select r from
(
select min(idx) idx ,r from @x group by r
) t where r not in (select r from @t) order by idx
end
特别注意这里随机函数要使用 newid() ,而不能用rand(),因为要一次返回多行,每行都是一个随机数,newid才能做到。ran()只能保证一次查询一个随机数,如果一次查询中返回多行,则这些行都是相同的。
从以上的代码可以看出算法工作的基本原理,就是每次产生1-54的随机数,如果有重复的话,就去掉,检查剩下的总数是否为54,如果不是则继续产生新的随机数,直到产生的随机数的总数达到54。
由于是用T-SQL编程,与C语言的思路不一样的地方在于用数据表变量代替了一般的变量做操作,显得更加简洁。对不熟悉sql语法的人来说,也可能是更加难以理解。