Random Numbers Generator for Poker Simulator in SQL Server

Problem

Games are by nature fit for randomness, relying on chance like to roll dice, shuffling cards, spawning enemies, and others. Is it possible to use T-SQL to accomplish the randomness needed? Let’s see how we can create a SQL Server poker game using a random numbers generator process.

Solution

Developers often think SQL Server is only for CRUD operations, but the database engine has features for game logic, simulations, or gamification.

Randomization techniques are purely mathematical used to simulate unpredictability, and applied to sampling testing, simulations, load balancing, and security.

This tip is focused on gaming and entertainment to have fun, but you can realize that this logic can be used in real-world business problems too.

Poker Game

I decided to simulate a poker game with a random numbers generator. The basic rules of the game are using a 52-card deck, where you and the SQL Server will receive seven (7) cards and you need to drop two (2), to keep only five (5) in your hand.

I will use the common poker hands from the strongest to the weakest:

  1. Royal Flush: it is a special case of Straight Flush when finished with ‘A’ card.
  2. Straight Flush: five consecutive cards of the same suit
  3. Four of a Kind: four cards of the same rank
  4. Full House: three of a kind and a pair
  5. Flush: five cards of the same suit, not making a sequence
  6. Straight: five consecutive cards, any suits
  7. Three of a Kind: three cards of the same rank
  8. Two Pair: two different pairs
  9. One Pair: two cards of the same rank
  10. High Card: highest card wins if no hand above

SQL Solution with Permuted Congruential Generator

The Permuted Congruential Generator (PCG) takes a simple and fast random Congruential base generator and combines it with Permuted, which is the scramble part of the logic, that uses a permutation math trick to make the numbers look more random or evenly spread out. It is hard to guess the next number compared to other generators, but it is still not secure enough for serious cryptography.

Algorithms are used to produce a stream of numbers to match statistical expectations and I choose to work with PCG64.

Let´s create a function to generate the PCG 64 bits that will generate random number between @low and @high values. The @int1 and @int2 are random generated numbers that needs to be supplied to the function.

-- =============================================
-- Author:      SCP - MSSQLTips
-- Create date: 20250813
-- Description: Permuted congruential generator
-- =============================================
CREATE OR ALTER FUNCTION [dbo].[ufnRndGenPCG64] 
                (@int1 bigint
                ,@int2 bigint
                ,@low float
                ,@high float)
RETURNS float
AS
BEGIN
    DECLARE @low32  bigint = (@int1 + 2147483648);
    DECLARE @high32 bigint = (@int2 + 2147483648);
 
    DECLARE @u float = CONVERT(float, @high32 * 4294967296 + @low32) / 18446744073709551616.0;
 
    DECLARE @result float = @low + (@high - @low) * @u;
 
    RETURN @result;
END
GO

Let first have a look if this function, in the long run, it will take all the cards with similar frequency.

-- MSSQLTips (TSQL)
 
DECLARE  @i int = 0
        ,@n int
        ,@t int = 10000;
 
DECLARE      @Data
    TABLE   (Id int
            ,n int
            ,q int);
 
WHILE @i < @t BEGIN
    SET @n = [dbo].[ufnRndGenPCG64] (CHECKSUM(NEWID()),CHECKSUM(NEWID()),1,53);
 
    INSERT INTO @Data
        VALUES(@i,@n,1)
 
    SET @i += 1;
END
 
DECLARE      @Summary
    TABLE   (n int
            ,freq float);
 
INSERT INTO      @Summary
    SELECT       n
                ,CONVERT(decimal(10,2),COUNT(n) / (@t * 0.01))  AS Freq
        FROM     @Data
        GROUP BY n
        ORDER BY n;
 
SELECT       AVG(freq) AS Mean
            ,STDEV(freq) AS Std
    FROM     @Summary;
GO

I run it with different values of n and the expected output for any card is 1 in 52 which is 1 / 52 * 100 = 1.923076.

nmeanstd
100001.920.16
1000001.920.05
10000001.920.02

I will not run a test of hypothesis to confirm because I am happy with it.

Tables

The first table will hold the deck of 52 cards

-- MSSQLTips (TSQL)
 
CREATE TABLE [dbo].[GamePoker](
    [CardFace] [nvarchar](3) NOT NULL,
    [CardId] [int] IDENTITY(1,1) NOT NULL,
    [CardValue] [smallint] NULL,
    [CardSuite] [nvarchar](10) NULL,
    [CardHolder] [smallint] DEFAULT 0,
 CONSTRAINT [PK_GamePoker] PRIMARY KEY CLUSTERED 
(
    [CardFace] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

The second table will hold the computational part of the game

-- MSSQLTips (TSQL)
 
CREATE TABLE [dbo].[GamePokerScore](
    [Player] [int] NOT NULL,
    [HighCard] [nvarchar](50) NULL,
    [PairOne] [nvarchar](50) NULL,
    [PairTwo] [nvarchar](50) NULL,
    [ThreeOfAKind] [nvarchar](50) NULL,
    [Straight] [nvarchar](50) NULL,
    [Flush] [nvarchar](50) NULL,
    [FullHouse] [nvarchar](50) NULL,
    [FourOfAKind] [nvarchar](50) NULL,
    [StraightFlush] [nvarchar](50) NULL,
    [RoyalFlush] [nvarchar](50) NULL,
    [Score] [float] DEFAULT 0,
 CONSTRAINT [PK_GamePokerScore] PRIMARY KEY CLUSTERED 
(
    [Player] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO

User Defined Functions

This function will retrieve the card value based on the card face.

-- =============================================
-- Author:      SCP - MSSQLTips
-- Create date: 20250820
-- Description: Game Poker Card value check 
-- =============================================
CREATE OR ALTER FUNCTION [dbo].[ufnGamePokerCardValue] 
                (@CardFace nvarchar(3))
RETURNS smallint
AS
BEGIN
    DECLARE  @cardValue int = 0;
            
    SELECT       @cardValue = [CardValue] 
        FROM     [dbo].[GamePoker] 
        WHERE    [CardFace] LIKE '%' + TRIM(@CardFace) + '%';
    
    RETURN @cardValue;
END
GO

The second function will be used to identify a sequence of cards.

-- =============================================
-- Author:      SCP - MSSQLTips
-- Create date: 20250820
-- Description: Game Poker Straight check 
-- =============================================
CREATE OR ALTER FUNCTION [dbo].[ufnGamePokerStraigh] 
                (@Player int)
RETURNS nvarchar(25)
AS
BEGIN
    DECLARE  @face nvarchar(20)
            ,@straight nvarchar(25)
            ,@suite nvarchar(10)
            ,@suitePrevious nvarchar(10) = ''
            ,@cvNext int = 0
            ,@seq smallint = 0
            ,@seqSuite smallint
            ,@seqFace nvarchar(20) = '';
 
    DECLARE crsStraight CURSOR FAST_FORWARD READ_ONLY FOR 
        SELECT       [CardHolder] AS pl
                    ,IIF(([CardValue] - LEAD([CardValue],1,0) OVER (ORDER BY [CardValue] DESC)) = 1 OR
                     ([CardValue] - LEAD([CardValue],1,0) OVER (ORDER BY [CardValue] DESC)) = [CardValue],1,0) AS cvNext
                    ,[CardFace] AS cf
                    ,[CardSuite] AS cs
            FROM     [dbo].[GamePoker] 
            WHERE    [CardHolder] = @Player
            ORDER BY [CardHolder]
                    ,[CardValue] DESC;
 
    OPEN crsStraight
        FETCH NEXT FROM crsStraight 
            INTO @player,@cvNext,@face,@suite;
 
        WHILE @@FETCH_STATUS = 0 BEGIN
    
            IF @cvNext = 1 BEGIN
                SET @seqFace = @face + ' ' + @seqFace;
                SET @seq += 1;
 
                IF @suite = @suitePrevious
                    SET @seqSuite += 1;
                ELSE BEGIN
                    SET @seqSuite = 1;
                    SET @suitePrevious = @suite;
                END
 
            END ELSE IF @cvNext <> 1 BEGIN
                SET @seqFace = '';
                SET @seq = 0;
            END
                
            IF @seq = 5 BEGIN
                IF @seqSuite = 5
                    SET @straight = 'F' + @seqFace;
                ELSE
                    SET @straight = @seqFace;
 
                BREAK;
            END
 
            FETCH NEXT FROM crsStraight 
                INTO @player,@cvNext,@face,@suite;
        END
    CLOSE crsStraight;
    DEALLOCATE crsStraight;
    
    RETURN @straight;
END
GO

Stored Procedures

This stored procedure will deal the cards and will initiate the deck of cards if it was not already done.

-- =============================================
-- Author:      SCP - MSSQLTips
-- Create date: 20250819
-- Description: Game Poker - Deal the cards
-- =============================================
CREATE OR ALTER PROCEDURE [dbo].[uspGamePokerDeal] 
WITH EXECUTE AS CALLER
AS
BEGIN
    SET NOCOUNT ON;
    BEGIN TRY
        BEGIN TRANSACTION;
 
            IF (SELECT COUNT(*) FROM [dbo].[GamePoker]) <> 52 BEGIN
                TRUNCATE TABLE [dbo].[GamePoker]
 
                INSERT INTO  [dbo].[GamePoker]
                            ([CardFace]
                            ,[CardValue])
                    VALUES   (' A',14),(' 2', 2),(' 3', 3),(' 4',4),(' 5', 5)
                            ,(' 6', 6),(' 7', 7),(' 8', 8),(' 9',9),('10',10)
                            ,(' J',11),(' Q',12),(' K',13);
 
                UPDATE       [dbo].[GamePoker]
                    SET      [CardHolder] = 1
                            ,[CardFace] += N'♥'
                            ,[CardSuite] = 'Hearts'
                    WHERE    [CardHolder] = 0;
 
                INSERT INTO  [dbo].[GamePoker]
                            ([CardFace]
                            ,[CardValue])
                    VALUES   (' A',14),(' 2', 2),(' 3', 3),(' 4',4),(' 5', 5)
                            ,(' 6', 6),(' 7', 7),(' 8', 8),(' 9',9),('10',10)
                            ,(' J',11),(' Q',12),(' K',13);
 
                UPDATE       [dbo].[GamePoker]
                    SET      [CardHolder] = 1
                            ,[CardFace] += N'♦'
                            ,[CardSuite] = 'Diamonds'
                    WHERE    [CardHolder] = 0;
 
                INSERT INTO  [dbo].[GamePoker]
                            ([CardFace]
                            ,[CardValue])
                    VALUES   (' A',14),(' 2', 2),(' 3', 3),(' 4',4),(' 5', 5)
                            ,(' 6', 6),(' 7', 7),(' 8', 8),(' 9',9),('10',10)
                            ,(' J',11),(' Q',12),(' K',13);
 
                UPDATE       [dbo].[GamePoker]
                    SET      [CardHolder] = 1
                            ,[CardFace] += N'♣'
                            ,[CardSuite] = 'Clubs'
                    WHERE    [CardHolder] = 0;
 
 
                INSERT INTO  [dbo].[GamePoker]
                            ([CardFace]
                            ,[CardValue])
                    VALUES   (' A',14),(' 2', 2),(' 3', 3),(' 4',4),(' 5', 5)
                            ,(' 6', 6),(' 7', 7),(' 8', 8),(' 9',9),('10',10)
                            ,(' J',11),(' Q',12),(' K',13);
 
                UPDATE       [dbo].[GamePoker]
                    SET      [CardHolder] = 1
                            ,[CardFace] += N'♠'
                            ,[CardSuite] = 'Spades'
                    WHERE    [CardHolder] = 0;
 
                UPDATE       [dbo].[GamePoker]
                    SET      [CardHolder] = 0;
            END
 
            -- PLAYER ====================================
 
            UPDATE       [dbo].[GamePoker]
                SET      [CardHolder] = 0
                WHERE    [CardHolder] > 0;
 
            DECLARE  @i int = 0
                    ,@n int = 0
                    ,@h int = 0;
 
            WHILE @i < 7 BEGIN
                SET @n = [dbo].[ufnRndGenPCG64] (CHECKSUM(NEWID()),CHECKSUM(NEWID()),1,53);
 
                IF EXISTS 
                    (SELECT      1 
                        FROM     [dbo].[GamePoker] 
                        WHERE    [CardId] = @n AND 
                                 [CardHolder] = 0) BEGIN
 
                    UPDATE       [dbo].[GamePoker]
                        SET      [CardHolder] = 1
                        WHERE    [CardId] = @n;
 
                    SET @i += 1;
                END
            END
 
            -- SQL =======================================
 
            SET @i = 0;
            WHILE @i < 7 BEGIN
                SET @n = [dbo].[ufnRndGenPCG64] (CHECKSUM(NEWID()),CHECKSUM(NEWID()),1,53);
 
                IF EXISTS 
                    (SELECT      1 
                        FROM     [dbo].[GamePoker] 
                        WHERE    [CardId] = @n AND 
                                 [CardHolder] = 0) BEGIN
 
                    UPDATE       [dbo].[GamePoker]
                        SET      [CardHolder] = 2
                        WHERE    [CardId] = @n;
 
                    SET @i += 1;
                END
            END
 
        COMMIT TRANSACTION;
 
        SELECT       [CardId]
                    ,[CardFace]
                    ,CONCAT([CardValue],' ',[CardSuite]) AS [CardName]
            FROM     [dbo].[GamePoker] 
            WHERE    [CardHolder] = 1 
            ORDER BY [CardValue];
 
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0
            BEGIN
                ROLLBACK TRANSACTION;
            END
            
        -- Print error information. 
        PRINT    'Error: ' + CONVERT(varchar(50), ERROR_NUMBER()) +
                ', Severity: ' + CONVERT(varchar(5), ERROR_SEVERITY()) +
                ', State: ' + CONVERT(varchar(5), ERROR_STATE()) + 
                ', Procedure: ' + ISNULL(ERROR_PROCEDURE(), '-') + 
                ', Line: ' + CONVERT(varchar(5), ERROR_LINE());
        PRINT ERROR_MESSAGE();
    END CATCH;
END
GO

This is the store procedure with the logic of the game to identify the cards and winner.

-- =============================================
-- Author:      SCP - MSSQLTips
-- Create date: 20250819
-- Description: Game Poker - Scores
-- =============================================
CREATE OR ALTER PROCEDURE [dbo].[uspGamePokerScores]
WITH EXECUTE AS CALLER
AS
BEGIN
    SET NOCOUNT ON;
 
    IF NOT 
        (SELECT         COUNT(*) 
            FROM     [dbo].[GamePoker] 
            WHERE     [CardHolder] = 1) = 5 BEGIN
        PRINT 'The player does not have five cards set!';
 
        RETURN
    END;
 
    TRUNCATE TABLE   [dbo].[GamePokerScore];
 
    INSERT INTO      [dbo].[GamePokerScore]
                    ([Player])
        VALUES      (1),(2);
                     
    -- Scoring ===========================================
 
    DECLARE             @player int
                    ,@value int
                    ,@score int
                    ,@hand nvarchar(50)
                    ,@face nvarchar(50);
 
    DECLARE crsScore CURSOR FAST_FORWARD READ_ONLY FOR 
        SELECT       [CardHolder] AS player
                    ,[CardValue] AS cValue
                    ,COUNT([CardValue]) AS score
                    ,STRING_AGG([CardFace],' ') 
                        WITHIN GROUP (ORDER BY [CardValue] ASC) AS hand
            FROM     [dbo].[GamePoker] 
            WHERE    [CardHolder] > 0
            GROUP BY [CardHolder]
                    ,[CardValue]
            ORDER BY 1,3,2 DESC;
 
    OPEN crsScore
        FETCH NEXT FROM crsScore 
            INTO @player,@value,@score,@hand;
 
        WHILE @@FETCH_STATUS = 0 BEGIN
            -- High Card
            UPDATE             [dbo].[GamePokerScore]
                SET             [HighCard] = IIF([Score] < @value,TRIM(LEFT(@hand,3)),[HighCard])
                            ,[Score] = IIF([Score] < @value,@value,[Score])
                WHERE         [Player] = @player;
 
            -- Pairs
            IF @score = 2 AND 
                (SELECT         [PairOne] 
                    FROM     [dbo].[GamePokerScore] 
                    WHERE     [Player] = @player) IS NULL
                UPDATE         [dbo].[GamePokerScore]
                    SET         [PairOne] = @hand
                            ,[Score] += [dbo].[ufnGamePokerCardValue] (LEFT(@hand,3))
                    WHERE     [Player] = @player;
            ELSE IF @score = 2 
                UPDATE         [dbo].[GamePokerScore]
                    SET         [PairTwo] = @hand                    
                            ,[Score] += [dbo].[ufnGamePokerCardValue] (LEFT(@hand,3))
                    WHERE     [Player] = @player;
 
            -- Three of a kind and Full house
            IF @score = 3  AND 
                (SELECT         [PairOne] 
                    FROM     [dbo].[GamePokerScore] 
                    WHERE     [Player] = @player) IS NULL
                UPDATE         [dbo].[GamePokerScore]
                    SET         [ThreeOfAKind] = @hand
                            ,[Score] += [dbo].[ufnGamePokerCardValue] (LEFT(@hand,3))
                    WHERE     [Player] = @player;
            ELSE IF @score = 3 
                UPDATE         [dbo].[GamePokerScore]
                    SET         [FullHouse] = [PairOne] + ' ' + @hand
                            ,[PairOne] = NULL
                            ,[PairTwo] = NULL
                            ,[Score] =  [dbo].[ufnGamePokerCardValue] (LEFT([PairOne],3)) +    
                                        [dbo].[ufnGamePokerCardValue] (LEFT(@hand,3)) 
                    WHERE     [Player] = @player;
 
            -- Four of a kind
            IF @score = 4
                UPDATE         [dbo].[GamePokerScore]
                    SET         [FourOfAKind] = @hand
                            ,[Score] += [dbo].[ufnGamePokerCardValue] (LEFT(@hand,3))
                    WHERE     [Player] = @player;
 
            FETCH NEXT FROM crsScore 
                INTO @player,@value,@score,@hand;
        END
    CLOSE crsScore;
    DEALLOCATE crsScore;
 
    -- Flush =============================================
    -- five cards of same suit but not consecutive
 
    WITH             cteCards AS
        (SELECT      [CardHolder] AS ch
                    ,COUNT([CardSuite]) AS cv
                    ,STRING_AGG([CardFace],' ') 
                        WITHIN GROUP (ORDER BY [CardValue] ASC) AS hand
            FROM     [dbo].[GamePoker] 
            WHERE    [CardHolder] > 0
            GROUP BY [CardHolder]
                    ,[CardSuite]
            HAVING     COUNT([CardSuite]) > 4)
        UPDATE       [dbo].[GamePokerScore]
            SET      [Flush] = hand
                    ,[PairOne] = NULL
            FROM     cteCards
            WHERE    [Player] = ch;
 
    -- Straight low ======================================
    -- five consecutive cards starting with A
 
    SET @face = [dbo].[ufnGamePokerStraigh] (1);
 
    UPDATE             [dbo].[GamePokerScore]
        SET             [Straight] = @face
        WHERE         [Player] = 1;
 
    IF @face IS NULL BEGIN 
        UPDATE         [dbo].[GamePoker]
            SET         [CardValue] = 1
            WHERE     [CardValue] = 14;
 
        UPDATE         [dbo].[GamePokerScore]
            SET         [Straight] = [dbo].[ufnGamePokerStraigh] (1)
            WHERE     [Player] = 1;
 
        UPDATE         [dbo].[GamePoker]
            SET         [CardValue] = 14
            WHERE     [CardValue] = 1;
    END
 
    SET @face = [dbo].[ufnGamePokerStraigh] (2);
 
    UPDATE             [dbo].[GamePokerScore]
        SET             [Straight] = @face
        WHERE         [Player] = 2;
 
    IF @face IS NULL BEGIN 
        UPDATE         [dbo].[GamePoker]
            SET         [CardValue] = 1
            WHERE     [CardValue] = 14;
 
        UPDATE         [dbo].[GamePokerScore]
            SET         [Straight] = [dbo].[ufnGamePokerStraigh] (2)
            WHERE     [Player] = 2;
 
        UPDATE         [dbo].[GamePoker]
            SET         [CardValue] = 14
            WHERE     [CardValue] = 1;
    END
 
    UPDATE             [dbo].[GamePokerScore]
        SET             [HighCard] = RIGHT([Straight],3)
                    ,[Score] = [dbo].[ufnGamePokerCardValue] (RIGHT([Straight],3))
                    ,[PairOne] = NULL
        WHERE         [Straight] IS NOT NULL;
 
    UPDATE             [dbo].[GamePokerScore]
        SET             [HighCard] = RIGHT([Flush],3)
                    ,[Score] = [dbo].[ufnGamePokerCardValue] (RIGHT([Flush],3))
                    ,[PairOne] = NULL
        WHERE         [Flush] IS NOT NULL;
 
    -- Straight Flush ====================================
    -- five consecutive cards of the same suit
 
    UPDATE             [dbo].[GamePokerScore]
        SET             [StraightFlush] = REPLACE([Straight],'F','')
                    ,[Straight] = NULL
                    ,[Flush] = NULL
        WHERE         [Straight] IS NOT NULL AND
                     [Straight] LIKE 'F%';
 
    UPDATE             [dbo].[GamePokerScore]
        SET             [Straight] = NULL
        WHERE         [Straight] IS NOT NULL AND
                     [Flush] IS NOT NULL;
 
    -- Royal Flush =======================================
    -- five consecutive cards of the same suit ending with A
 
    UPDATE             [dbo].[GamePokerScore]
        SET             [RoyalFlush] = [StraightFlush]
                    ,[StraightFlush] = NULL
                    ,[Straight] = NULL
                    ,[Flush] = NULL
        WHERE         [StraightFlush] IS NOT NULL AND
                     [StraightFlush] LIKE '%A__';
 
    -- Two pairs =========================================
 
    UPDATE             [dbo].[GamePokerScore]
        SET             [PairTwo] += ' ' + [PairOne]
                    ,[PairOne] = NULL
        WHERE         [PairTwo] IS NOT NULL;
 
    -- Winner ============================================
 
    DECLARE         @ScoreBoard
        TABLE    ([Id] int IDENTITY
                ,[Player] nvarchar(50)
                ,[Hand] nvarchar(1000)
                ,[Score] int);
 
    INSERT INTO  @ScoreBoard
        SELECT         CASE WHEN [Player] = 1
                          THEN 'You'
                          ELSE 'SQL'
                     END AS [Player]
                    ,CONCAT( 'High card: ' + [HighCard] + ' and '
                            ,'One Pair: ' + [PairOne]
                            ,'Two Pair: ' + [PairTwo]
                            ,'Three of a Kind: ' + [ThreeOfAKind]
                            ,'Straight: ' + RIGHT([Straight],20)
                            ,'Flush: ' + RIGHT([Flush],20)
                            ,'Full House: ' + [FullHouse]
                            ,'Four of a Kind: ' + [FourOfAKind]
                            ,'Straight Flush: ' + RIGHT([StraightFlush],20)
                            ,'Royal Flush: ' + RIGHT([RoyalFlush],20)) AS [Hand]
                    ,[Score] + IIF([PairOne] IS NULL,0,POWER(2,5))
                             + IIF([PairTwo] IS NULL,0,POWER(2,6))
                             + IIF([ThreeOfAKind] IS NULL,0,POWER(2,7))
                             + IIF([Straight] IS NULL,0,POWER(2,8))
                             + IIF([Flush] IS NULL,0,POWER(2,9))
                             + IIF([FullHouse] IS NULL,0,POWER(2,10))
                             + IIF([FourOfAKind] IS NULL,0,POWER(2,11))
                             + IIF([StraightFlush] IS NULL,0,POWER(2,12))
                             + IIF([RoyalFlush] IS NULL,0,POWER(2,13)) AS [Score]
            FROM     [dbo].[GamePokerScore]
            ORDER BY 3 DESC;
 
    DECLARE @MaxScore float = (SELECT MAX([Score]) FROM @ScoreBoard);
 
    UPDATE         @ScoreBoard
        SET         [Player] += ' are the WINNER!!!'
        WHERE     [Score] = @MaxScore;
 
    SELECT         [Player]
                ,[Hand]
                ,[Score] 
        FROM     @ScoreBoard;
 
END
GO

The final one is used to drop two cards and then execute the scoring.

-- =============================================
-- Author:      SCP - MSSQLTips
-- Create date: 20250819
-- Description: Game Poker - Drop cards
-- =============================================
CREATE OR ALTER PROCEDURE [dbo].[uspGamePokerDrop]
                (@Card1 int
                ,@Card2 int)
WITH EXECUTE AS CALLER
AS
BEGIN
    SET NOCOUNT ON;
 
    IF (SELECT         COUNT(*) 
            FROM     [dbo].[GamePoker] 
            WHERE     [CardHolder] = 1) = 5 BEGIN
        SELECT       'You must keep five cards!' AS [Constraint]
                    ,STRING_AGG(CONVERT(nchar(5),[CardFace]),' ') WITHIN GROUP (ORDER BY [CardValue] ASC) AS [YourHand]
            FROM     [dbo].[GamePoker] 
            WHERE    [CardHolder] = 1;
 
        RETURN
    END
 
    BEGIN TRANSACTION;
 
        UPDATE         [dbo].[GamePoker] 
            SET         [CardHolder] = 0 
            WHERE     [CardId] IN (@Card1,@Card2) AND 
                     [CardHolder] = 1;
 
        IF (SELECT         COUNT(*) 
                FROM     [dbo].[GamePoker] 
                WHERE     [CardHolder] = 1) < 5 BEGIN
            PRINT('You must keep five cards!')
            ROLLBACK TRANSACTION;
        END ELSE
            COMMIT TRANSACTION;
 
        IF (SELECT         COUNT(*) 
                FROM     [dbo].[GamePoker] 
                WHERE     [CardHolder] = 1) = 5
            EXECUTE [dbo].[uspGamePokerScores]
        ELSE
            SELECT       [CardId]
                        ,[CardFace]
                        ,CONCAT([CardValue],' ',[CardSuite]) AS [CardName]
                FROM     [dbo].[GamePoker] 
                WHERE    [CardHolder] = 1 
                ORDER BY [CardValue];
END
GO

Playing the Game

Step 1 – get cards

The first step is to execute:

-- MSSQLTips (TSQL) 
EXECUTE [dbo].[uspGamePokerDeal];
GO

Resulting in

Poker hand

Step 2 – drop 2 cards

If I do not want to continue due to a poor hand, just execute step 1 again, but I will go with my pair of eights. To continue I need to discard the two of clubs (28) and the four of hearts (4), where in the parenthesis is the Card Id. If you execute this command twice you are going to receive a message that you need to keep five cards.

-- MSSQLTips (TSQL) 
EXECUTE [dbo].[uspGamePokerDrop] 28,4;
GO

Resulting in

Poker results

And this time I win with a pair of eights!

If you need to show the score again, run the following:

-- MSSQLTips (TSQL)
EXECUTE [dbo].[uspGamePokerScores]
GO

To continue to play just execute the steps 1 and 2, as you wish.

Have fun!

Next Steps

  • It is possible that I did not cover all possibilities and you can find a flaw on the scores, but the idea is there.
  • One improvement is to save the history of games to train the computer to mimic a real player, adding a bet values step.
  • I did not delete the computers extra cards to mimic what we did, looking at the cards and keeping the best ones.

Leave a Reply

Your email address will not be published. Required fields are marked *