《天天德州》之德州牛仔概率计算器

Posted ecofast

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《天天德州》之德州牛仔概率计算器相关的知识,希望对你有一定的参考价值。

  鹅厂的《天天德州》里有个相关的“小”游戏,名曰《牛仔》或《德州牛仔》。尽管其在游戏主界面上的入口并不很显眼,但它的设计却非常有创意,借德扑的规则行“涉赌”之擦边球玩法,简单来说,在假定系统公平的前提下,其完全就是靠统计、概率来获利的游戏。 综合考量其赔付设定及鹅厂的运营能量,即便其确实完全公平无猫腻(很明显这不可能),在收入等硬指标上恐怕也相当可观!

  而我则对其系统赔付比比较感兴趣,在用排列组合方式计算出了“任一人手牌”的各项几率后,发现已较难再以相同做法计算“获胜牌型”几率,毕竟概率论基本已淡忘得差不多了,也不大想再费脑作这些繁琐计算,于是理所当然的,直接撸了个德州牛仔概率计算器程序,以“大数据”的方式来展示各项统计结果,如下。

技术分享图片

  此计算器的核心代码如下。

unit uTexasPoker;

interface

uses
  SysUtils, Windows;

type
  // 花色(红桃、方块、黑桃、梅花)
  TCardColor = (ccHearts, ccDiamond, ccSpades, ccClubs);

  // 输赢类型(牛仔赢、平局、公牛赢)
  TWinningType = (wtCowboy, wtTie, wtBull);
  
  // 手牌类型(同花、连牌、对子、同花连牌、对A、杂牌)
  THandCardType = (hctFlush, hctStraight, hctOnePair, hctFlushStraight, hctPairA, hctNone);

  // 手牌+公共牌最终所成牌型(高牌、一对、两对、三条、顺子、同花、葫芦、四条(金刚)、同花顺、皇家同花顺)
  TGameCardType = (gctHighCard, gctOnePair, gctTwoPair, gctThreeOfAKind, gctStraight, gctFlush, gctFullHouse, gctFourOfAKind, gctStraightFlush, gctRoyalFlush);

  TCardProp = record
    Value: Byte;
    Color: TCardColor;
  end;

  PPlayerCardType = ^TPlayerCardType;
  TPlayerCardType = record
    GameCardType: TGameCardType;
    CardValueArr: array[1..5] of Byte;    
  end;

  PColoredCardArr = ^TColoredCardArr;
  TColoredCardArr = array[TCardColor, 1..14] of Byte;
  
const
  cCardPropLen                                      = SizeOf(TCardProp);
  cPlayerCardTypeLen                                = SizeOf(TPlayerCardType);
  cHandCardTypeDesc: array[THandCardType] of string = (同花, 连牌, 对子, 同花连牌, 对A, 普通);
  cGameCardTypeDesc: array[TGameCardType] of string = (高牌, 一对, 两对, 三条, 顺子, 同花, 葫芦, 金刚, 同花顺, 皇家同花顺);

type
  TExecutingCallback = procedure of object;
  TOutputCallback    = procedure(const S: string) of object;
  
  TTexasPoker = class
  private
    FDeckCards: array[1..52] of Byte;
    FCommCards: array[1..5] of Byte;
    FPlayerCards: array[1..4] of Byte;
    FHandCardProps: array[1..4] of TCardProp;
    FCowboyCards: TColoredCardArr;
    FBullCards: TColoredCardArr;
    FCowboyCardType: TPlayerCardType;
    FBullCardType: TPlayerCardType;
    FHandCardTypes: array[THandCardType] of Integer;
    FWinningTypes: array[TWinningType] of Integer;
    FWinCardTypes: array[TGameCardType] of Integer;
    FCowboyCardTypes: array[TGameCardType] of Integer;
    FBullCardTypes: array[TGameCardType] of Integer;
    procedure Reset;
    procedure Initialize;
    procedure GenCommCards;
    procedure GenPlayerCards;
    procedure NormalizePlayerCards;    
    procedure StatHandCardTypes;
    procedure AnalysePlayerCardTypes;
    procedure ComparePlayerCardTypes;
    procedure OutputStats(const ExecNum: Integer; const OutputCallback: TOutputCallback);
  public
    constructor Create;
    destructor Destroy; override;
    procedure Execute(const ExecNum: Integer; const ExecutingCallback: TExecutingCallback; const OutputCallback: TOutputCallback);
    function CardValueFromIdx(Idx: Byte): Byte;
    function CardColorFromIdx(Idx: Byte): TCardColor;
  end;

implementation

{ TTexasPoker }

procedure TTexasPoker.AnalysePlayerCardTypes;

  function CheckRoyalFlush(const ColoredCardArr: PColoredCardArr; const PlayerCardType: PPlayerCardType): Boolean;
  var
    CardColor: TCardColor;
    I: Integer;
  begin
    for CardColor := Low(TCardColor) to High(TCardColor) do
    begin
      if (ColoredCardArr[CardColor][14] = 1) and (ColoredCardArr[CardColor][13] = 1) and (ColoredCardArr[CardColor][12] = 1)
        and (ColoredCardArr[CardColor][11] = 1) and (ColoredCardArr[CardColor][10] = 1)
      then
      begin
        PlayerCardType.GameCardType := gctRoyalFlush;
        for I := 1 to 5 do
          PlayerCardType.CardValueArr[I] := 14 - I + 1;
        Result := True;
        Exit;
      end;
    end;
    Result := False;
  end;

  function CheckStraightFlush(const ColoredCardArr: PColoredCardArr; const PlayerCardType: PPlayerCardType): Boolean;
  var
    CardColor: TCardColor;
    I, J: Integer;
  begin
    for I := 13 downto 5 do
    begin
      for CardColor := Low(TCardColor) to High(TCardColor) do
      begin
        if (ColoredCardArr[CardColor][I] = 1) and (ColoredCardArr[CardColor][I - 1] = 1) and (ColoredCardArr[CardColor][I - 2] = 1)
          and (ColoredCardArr[CardColor][I - 3] = 1) and (ColoredCardArr[CardColor][I - 4] = 1)
        then
        begin
          PlayerCardType.GameCardType := gctStraightFlush;
          for J := 1 to 5 do
            PlayerCardType.CardValueArr[J] := I - J + 1;
          Result := True;
          Exit;
        end;
      end;
    end;
    Result := False;
  end;

  function CheckFourOfAKind(const ColoredCardArr: PColoredCardArr; const PlayerCardType: PPlayerCardType): Boolean;
  var
    I, J: Integer;
  begin
    for I := 14 downto 2 do
    begin
      if (ColoredCardArr[ccHearts][I] = 1) and (ColoredCardArr[ccDiamond][I] = 1)
        and (ColoredCardArr[ccSpades][I] = 1) and (ColoredCardArr[ccClubs][I] = 1)
      then
      begin
        PlayerCardType.GameCardType := gctFourOfAKind;
        for J := 1 to 4 do
          PlayerCardType.CardValueArr[J] := I;
        for J := 14 downto 2 do
        begin
          if (J <> I)
            and ((ColoredCardArr[ccHearts][J] = 1) or (ColoredCardArr[ccDiamond][J] = 1) or (ColoredCardArr[ccSpades][J] = 1) or (ColoredCardArr[ccClubs][J] = 1))
          then
          begin
            PlayerCardType.CardValueArr[5] := J;
            Result := True;
            Exit;
          end;
        end;
      end;
    end;
    Result := False;
  end;

  function CheckFullHouse(const ColoredCardArr: PColoredCardArr; const PlayerCardType: PPlayerCardType): Boolean;
  var
    I, J, K: Integer;
  begin
    for I := 14 downto 2 do
    begin
      if ((ColoredCardArr[ccHearts][I] = 1) and (ColoredCardArr[ccDiamond][I] = 1) and (ColoredCardArr[ccSpades][I] = 1))
         or ((ColoredCardArr[ccHearts][I] = 1) and (ColoredCardArr[ccDiamond][I] = 1) and (ColoredCardArr[ccClubs][I] = 1))
         or ((ColoredCardArr[ccHearts][I] = 1) and (ColoredCardArr[ccSpades][I] = 1) and (ColoredCardArr[ccClubs][I] = 1))
         or ((ColoredCardArr[ccDiamond][I] = 1) and (ColoredCardArr[ccSpades][I] = 1) and (ColoredCardArr[ccClubs][I] = 1))
      then
      begin
        for J := 14 downto 2 do
        begin
          if J <> I then
          begin
            if ((ColoredCardArr[ccHearts][J] = 1) and (ColoredCardArr[ccDiamond][J] = 1))
              or ((ColoredCardArr[ccHearts][J] = 1) and (ColoredCardArr[ccSpades][J] = 1))
              or ((ColoredCardArr[ccHearts][J] = 1) and (ColoredCardArr[ccClubs][J] = 1))
              or ((ColoredCardArr[ccDiamond][J] = 1) and (ColoredCardArr[ccSpades][J] = 1))
              or ((ColoredCardArr[ccDiamond][J] = 1) and (ColoredCardArr[ccClubs][J] = 1))
              or ((ColoredCardArr[ccSpades][J] = 1) and (ColoredCardArr[ccClubs][J] = 1))
            then
            begin
              PlayerCardType.GameCardType := gctFullHouse;
              for K := 1 to 3 do
                PlayerCardType.CardValueArr[K] := I;
              PlayerCardType.CardValueArr[4] := J;
              PlayerCardType.CardValueArr[5] := J;
              Result := True;
              Exit;
            end;
          end;
        end;
      end;
    end;
    Result := False;
  end;

  function CheckFlush(const ColoredCardArr: PColoredCardArr; const PlayerCardType: PPlayerCardType): Boolean;
  var
    CardColor: TCardColor;
    I, Cnt: Integer;
  begin    
    for CardColor := Low(TCardColor) to High(TCardColor) do
    begin
      Cnt := 0;
      for I := 14 downto 2 do
      begin
        if ColoredCardArr[CardColor][I] = 1 then
        begin
          Inc(Cnt);
          PlayerCardType.CardValueArr[Cnt] := I;
          if Cnt >= 5 then
            Break;
        end;
      end;
      if Cnt >= 5 then
      begin
        PlayerCardType.GameCardType := gctFlush;
        Result := True;
        Exit;
      end;
    end;
    Result := False;
  end;

  function CheckStraight(const ColoredCardArr: PColoredCardArr; const PlayerCardType: PPlayerCardType): Boolean;
  var
    I, J: Integer;
  begin
    for I := 14 downto 5 do
    begin
      if ((ColoredCardArr[ccHearts][I] = 1) or (ColoredCardArr[ccDiamond][I] = 1) or (ColoredCardArr[ccSpades][I] = 1) or (ColoredCardArr[ccClubs][I] = 1))
        and ((ColoredCardArr[ccHearts][I - 1] = 1) or (ColoredCardArr[ccDiamond][I - 1] = 1) or (ColoredCardArr[ccSpades][I - 1] = 1) or (ColoredCardArr[ccClubs][I - 1] = 1))
        and ((ColoredCardArr[ccHearts][I - 2] = 1) or (ColoredCardArr[ccDiamond][I - 2] = 1) or (ColoredCardArr[ccSpades][I - 2] = 1) or (ColoredCardArr[ccClubs][I - 2] = 1))
        and ((ColoredCardArr[ccHearts][I - 3] = 1) or (ColoredCardArr[ccDiamond][I - 3] = 1) or (ColoredCardArr[ccSpades][I - 3] = 1) or (ColoredCardArr[ccClubs][I - 3] = 1))
        and ((ColoredCardArr[ccHearts][I - 4] = 1) or (ColoredCardArr[ccDiamond][I - 4] = 1) or (ColoredCardArr[ccSpades][I - 4] = 1) or (ColoredCardArr[ccClubs][I - 4] = 1))
      then
      begin
        PlayerCardType.GameCardType := gctStraight;
        for J := 1 to 5 do
          PlayerCardType.CardValueArr[J] := I - J + 1;
        Result := True;
        Exit;
      end;
    end;
    Result := False;
  end;

  function CheckThreeOfAKind(const ColoredCardArr: PColoredCardArr; const PlayerCardType: PPlayerCardType): Boolean;
  var
    I, J, K: Integer;
  begin
    for I := 14 downto 2 do
    begin
      if ((ColoredCardArr[ccHearts][I] = 1) and (ColoredCardArr[ccDiamond][I] = 1) and (ColoredCardArr[ccSpades][I] = 1))
         or ((ColoredCardArr[ccHearts][I] = 1) and (ColoredCardArr[ccDiamond][I] = 1) and (ColoredCardArr[ccClubs][I] = 1))
         or ((ColoredCardArr[ccHearts][I] = 1) and (ColoredCardArr[ccSpades][I] = 1) and (ColoredCardArr[ccClubs][I] = 1))
         or ((ColoredCardArr[ccDiamond][I] = 1) and (ColoredCardArr[ccSpades][I] = 1) and (ColoredCardArr[ccClubs][I] = 1))
      then
      begin
        PlayerCardType.GameCardType := gctThreeOfAKind;
        for J := 1 to 3 do
          PlayerCardType.CardValueArr[J] := I;        
        K := 4;
        for J := 14 downto 2 do
        begin
          if J <> I then
          begin
            if (ColoredCardArr[ccHearts][J] = 1) or (ColoredCardArr[ccDiamond][J] = 1) or (ColoredCardArr[ccSpades][J] = 1) or (ColoredCardArr[ccClubs][J] = 1) then
            begin              
              PlayerCardType.CardValueArr[K] := J;
              Inc(K);
              if K > 5 then
              begin
                Result := True;
                Exit;
              end;
            end;
          end;
        end;
      end;
    end;
    Result := False;
  end;

  function CheckTwoPair(const ColoredCardArr: PColoredCardArr; const PlayerCardType: PPlayerCardType): Boolean;
  var
    I, J, Cnt: Integer;
  begin
    Cnt := 0;
    for J := 14 downto 2 do
    begin
      if ((ColoredCardArr[ccHearts][J] = 1) and (ColoredCardArr[ccDiamond][J] = 1))
        or ((ColoredCardArr[ccHearts][J] = 1) and (ColoredCardArr[ccSpades][J] = 1))
        or ((ColoredCardArr[ccHearts][J] = 1) and (ColoredCardArr[ccClubs][J] = 1))
        or ((ColoredCardArr[ccDiamond][J] = 1) and (ColoredCardArr[ccSpades][J] = 1))
        or ((ColoredCardArr[ccDiamond][J] = 1) and (ColoredCardArr[ccClubs][J] = 1))
        or ((ColoredCardArr[ccSpades][J] = 1) and (ColoredCardArr[ccClubs][J] = 1))
      then
      begin
        Inc(Cnt);
        PlayerCardType.CardValueArr[Cnt * 2 - 1] := J;
        PlayerCardType.CardValueArr[Cnt * 2] := J;
        if Cnt >= 2 then
        begin
          for I := 14 downto 2 do
          begin
            if (I <> PlayerCardType.CardValueArr[1]) and (I <> PlayerCardType.CardValueArr[3]) then
            begin
              if (ColoredCardArr[ccHearts][I] = 1) or (ColoredCardArr[ccDiamond][I] = 1) or (ColoredCardArr[ccSpades][I] = 1) or (ColoredCardArr[ccClubs][I] = 1) then
              begin
                PlayerCardType.GameCardType := gctTwoPair;
                PlayerCardType.CardValueArr[5] := I;
                Result := True;
                Exit;
              end;
            end;
          end;
        end;
      end;
    end;
    Result := False;
  end;

  function CheckOnePair(const ColoredCardArr: PColoredCardArr; const PlayerCardType: PPlayerCardType): Boolean;
  var
    I, J, Cnt: Integer;
  begin
    for J := 14 downto 2 do
    begin
      if ((ColoredCardArr[ccHearts][J] = 1) and (ColoredCardArr[ccDiamond][J] = 1))
        or ((ColoredCardArr[ccHearts][J] = 1) and (ColoredCardArr[ccSpades][J] = 1))
        or ((ColoredCardArr[ccHearts][J] = 1) and (ColoredCardArr[ccClubs][J] = 1))
        or ((ColoredCardArr[ccDiamond][J] = 1) and (ColoredCardArr[ccSpades][J] = 1))
        or ((ColoredCardArr[ccDiamond][J] = 1) and (ColoredCardArr[ccClubs][J] = 1))
        or ((ColoredCardArr[ccSpades][J] = 1) and (ColoredCardArr[ccClubs][J] = 1))
      then
      begin
        PlayerCardType.CardValueArr[1] := J;
        PlayerCardType.CardValueArr[2] := J;
        Cnt := 3;
        for I := 14 downto 2 do
        begin
          if I <> PlayerCardType.CardValueArr[1] then
          begin
            if (ColoredCardArr[ccHearts][I] = 1) or (ColoredCardArr[ccDiamond][I] = 1) or (ColoredCardArr[ccSpades][I] = 1) or (ColoredCardArr[ccClubs][I] = 1) then
            begin              
              PlayerCardType.CardValueArr[Cnt] := I;
              Inc(Cnt);
              if Cnt > 5 then
              begin
                PlayerCardType.GameCardType := gctOnePair;
                Result := True;
                Exit;
              end;
            end;
          end;
        end;
      end;
    end;
    Result := False;
  end;

  function CheckHighCard(const ColoredCardArr: PColoredCardArr; const PlayerCardType: PPlayerCardType): Boolean;
  var
    I, Cnt: Integer;
  begin
    Result := True;
    PlayerCardType.GameCardType := gctHighCard;
    Cnt := 0;
    for I := 14 downto 2 do
    begin
      if (ColoredCardArr[ccHearts][I] = 1) or (ColoredCardArr[ccDiamond][I] = 1) or (ColoredCardArr[ccSpades][I] = 1) or (ColoredCardArr[ccClubs][I] = 1) then
      begin
        Inc(Cnt);
        PlayerCardType.CardValueArr[Cnt] := I;
        if Cnt >= 5 then
          Exit;
      end;
    end;
  end;

  function Analyse(const ColoredCardArr: PColoredCardArr; const PlayerCardType: PPlayerCardType): TGameCardType;
  begin
    if CheckRoyalFlush(ColoredCardArr, PlayerCardType) then
    begin
      Result := gctRoyalFlush;
      Exit;
    end;
    if CheckStraightFlush(ColoredCardArr, PlayerCardType) then
    begin
      Result := gctStraightFlush;
      Exit;
    end;
    if CheckFourOfAKind(ColoredCardArr, PlayerCardType) then
    begin
      Result := gctFourOfAKind;
      Exit;
    end;
    if CheckFullHouse(ColoredCardArr, PlayerCardType) then
    begin
      Result := gctFullHouse;
      Exit;
    end;
    if CheckFlush(ColoredCardArr, PlayerCardType) then
    begin
      Result := gctFlush;
      Exit;
    end;
    if CheckStraight(ColoredCardArr, PlayerCardType) then
    begin
      Result := gctStraight;
      Exit;
    end;
    if CheckThreeOfAKind(ColoredCardArr, PlayerCardType) then
    begin
      Result := gctThreeOfAKind;
      Exit;
    end;
    if CheckTwoPair(ColoredCardArr, PlayerCardType) then
    begin
      Result := gctTwoPair;
      Exit;
    end;
    if CheckOnePair(ColoredCardArr, PlayerCardType) then
    begin
      Result := gctOnePair;
      Exit;
    end;
    CheckHighCard(ColoredCardArr, PlayerCardType);
    Result := gctHighCard;
  end;
  
begin
  Inc(FCowboyCardTypes[Analyse(@FCowboyCards, @FCowboyCardType)]);
  Inc(FBullCardTypes[Analyse(@FBullCards, @FBullCardType)]);
end;

function TTexasPoker.CardColorFromIdx(Idx: Byte): TCardColor;
begin
  Result := TCardColor((Idx - 1) mod 4);
end;

function TTexasPoker.CardValueFromIdx(Idx: Byte): Byte;
begin
  Result := (Idx - 1) div 4 + 1;
end;

procedure TTexasPoker.ComparePlayerCardTypes;

  procedure CowboyWin;
  begin
    Inc(FWinningTypes[wtCowboy]);
    Inc(FWinCardTypes[FCowboyCardType.GameCardType]);
  end;

  procedure BullWin;
  begin
    Inc(FWinningTypes[wtBull]);
    Inc(FWinCardTypes[FBullCardType.GameCardType]);
  end;

  procedure PlayEven;
  begin
    Inc(FWinningTypes[wtTie]);
  end;
  
var
  I: Integer;  
begin
  if FCowboyCardType.GameCardType > FBullCardType.GameCardType then
  begin
    CowboyWin;
    Exit;
  end;
  
  if FCowboyCardType.GameCardType < FBullCardType.GameCardType then
  begin
    BullWin;
    Exit;
  end;

  if FCowboyCardType.GameCardType = gctRoyalFlush then
  begin
    PlayEven;
    Exit;
  end;

  if (FCowboyCardType.GameCardType = gctStraightFlush) or (FCowboyCardType.GameCardType = gctStraight) then
  begin
    if FCowboyCardType.CardValueArr[1] > FBullCardType.CardValueArr[1] then
      CowboyWin
    else if FCowboyCardType.CardValueArr[1] < FBullCardType.CardValueArr[1] then
      BullWin
    else
      PlayEven;
    Exit;
  end;

  if (FCowboyCardType.GameCardType = gctFourOfAKind) or (FCowboyCardType.GameCardType = gctFullHouse) then
  begin
    if FCowboyCardType.CardValueArr[1] > FBullCardType.CardValueArr[1] then
      CowboyWin
    else if FCowboyCardType.CardValueArr[1] < FBullCardType.CardValueArr[1] then
      BullWin
    else
    begin
      if FCowboyCardType.CardValueArr[5] > FBullCardType.CardValueArr[5] then
        CowboyWin
      else if FCowboyCardType.CardValueArr[5] < FBullCardType.CardValueArr[5] then
        BullWin 
      else
        PlayEven;
    end;
    Exit;
  end;

  if (FCowboyCardType.GameCardType = gctFlush) or (FCowboyCardType.GameCardType = gctHighCard) then
  begin
    for I := 1 to 5 do
    begin
      if FCowboyCardType.CardValueArr[I] > FBullCardType.CardValueArr[I] then
      begin
        CowboyWin;
        Exit;
      end
      else if FCowboyCardType.CardValueArr[I] < FBullCardType.CardValueArr[I] then
      begin
        BullWin;
        Exit;
      end;
    end;
    PlayEven;
    Exit;
  end;

  if FCowboyCardType.GameCardType = gctThreeOfAKind then
  begin
    if FCowboyCardType.CardValueArr[1] > FBullCardType.CardValueArr[1] then
      CowboyWin
    else if FCowboyCardType.CardValueArr[1] < FBullCardType.CardValueArr[1] then
      BullWin
    else
    begin
      if FCowboyCardType.CardValueArr[4] > FBullCardType.CardValueArr[4] then
        CowboyWin
      else if FCowboyCardType.CardValueArr[4] < FBullCardType.CardValueArr[4] then
        BullWin 
      else
      begin
        if FCowboyCardType.CardValueArr[5] > FBullCardType.CardValueArr[5] then
          CowboyWin
        else if FCowboyCardType.CardValueArr[5] < FBullCardType.CardValueArr[5] then
          BullWin 
        else
          PlayEven;
      end;        
    end;
    Exit;
  end;

  if FCowboyCardType.GameCardType = gctTwoPair then
  begin
    if FCowboyCardType.CardValueArr[1] > FBullCardType.CardValueArr[1] then
      CowboyWin
    else if FCowboyCardType.CardValueArr[1] < FBullCardType.CardValueArr[1] then
      BullWin
    else
    begin
      if FCowboyCardType.CardValueArr[3] > FBullCardType.CardValueArr[3] then
        CowboyWin
      else if FCowboyCardType.CardValueArr[3] < FBullCardType.CardValueArr[3] then
        BullWin 
      else
      begin
        if FCowboyCardType.CardValueArr[5] > FBullCardType.CardValueArr[5] then
          CowboyWin
        else if FCowboyCardType.CardValueArr[5] < FBullCardType.CardValueArr[5] then
          BullWin 
        else
          PlayEven;
      end;        
    end;
    Exit;
  end;

  if FCowboyCardType.GameCardType = gctOnePair then
  begin
    if FCowboyCardType.CardValueArr[1] > FBullCardType.CardValueArr[1] then
      CowboyWin
    else if FCowboyCardType.CardValueArr[1] < FBullCardType.CardValueArr[1] then
      BullWin
    else
    begin
      for I := 3 to 5 do
      begin
        if FCowboyCardType.CardValueArr[I] > FBullCardType.CardValueArr[I] then
        begin
          CowboyWin;
          Exit;
        end
        else if FCowboyCardType.CardValueArr[I] < FBullCardType.CardValueArr[I] then
        begin
          BullWin;
          Exit;
        end;
      end;
      PlayEven;
    end;
    Exit;
  end;end;

constructor TTexasPoker.Create;
begin

end;

destructor TTexasPoker.Destroy;
begin

  inherited;
end;

procedure TTexasPoker.Execute(const ExecNum: Integer; const ExecutingCallback: TExecutingCallback; const OutputCallback: TOutputCallback);
const
  cExecNumMin = 100000;
var
  I, Num: Integer;
begin
  Reset;
  Num := ExecNum;
  if Num < cExecNumMin then
  begin
    Num := cExecNumMin;
    if Assigned(OutputCallback) then
      OutputCallback(Format(【模拟次数合理变更:%d-->%d】, [ExecNum, Num]));
  end;
  for I := 1 to Num do
  begin
    if (I shr 7 = 0) and Assigned(ExecutingCallback) then
      ExecutingCallback;

    Initialize;
    GenCommCards;
    GenPlayerCards;
    NormalizePlayerCards;
    StatHandCardTypes;
    AnalysePlayerCardTypes;
    ComparePlayerCardTypes;
  end;

  if Assigned(OutputCallback) then
    OutputStats(Num, OutputCallback);
end;

procedure TTexasPoker.GenCommCards;
var
  RdnCnt: Integer;
  RdnIdx: Integer;
begin
  RdnCnt := 0;
  repeat
    RdnIdx := Random(52) + 1;
    if FDeckCards[RdnIdx] = 0 then
    begin
      FDeckCards[RdnIdx] := 1;
      FCommCards[RdnCnt + 1] := RdnIdx;
      Inc(RdnCnt);
    end;
  until RdnCnt >= Length(FCommCards);
end;

procedure TTexasPoker.GenPlayerCards;
var
  RdnCnt: Integer;
  RdnIdx: Integer;
begin
  RdnCnt := 0;
  repeat
    RdnIdx := Random(52) + 1;
    if FDeckCards[RdnIdx] = 0 then
    begin
      FDeckCards[RdnIdx] := 1;
      FPlayerCards[RdnCnt + 1] := RdnIdx;
      Inc(RdnCnt);
    end;
  until RdnCnt >= Length(FPlayerCards);
end;

procedure TTexasPoker.Initialize;
begin
  ZeroMemory(@FDeckCards, Length(FDeckCards));
  ZeroMemory(@FCommCards, Length(FCommCards));
  ZeroMemory(@FPlayerCards, Length(FPlayerCards));
  ZeroMemory(@FHandCardProps, Length(FHandCardProps) * cCardPropLen);
  ZeroMemory(@FCowboyCardType, cPlayerCardTypeLen);
  ZeroMemory(@FBullCardType, cPlayerCardTypeLen);
  ZeroMemory(@FCowboyCards, Length(FCowboyCards) * Length(FCowboyCards[Low(TCardColor)]));
  ZeroMemory(@FBullCards, Length(FBullCards) * Length(FBullCards[Low(TCardColor)]));
end;

procedure TTexasPoker.NormalizePlayerCards;
var
  I: Integer;
  C: TCardColor;
  V: Byte;
begin
  for I := Low(FPlayerCards) to High(FPlayerCards) do
  begin
    C := CardColorFromIdx(FPlayerCards[I]);
    V := CardValueFromIdx(FPlayerCards[I]);
    FHandCardProps[I].Color := C;
    FHandCardProps[I].Value := V;
    if I mod 2 = 1 then
    begin
      FCowboyCards[C][V] := 1;
      if V = 1 then
        FCowboyCards[C][14] := 1;
    end else
    begin
      FBullCards[C][V] := 1;
      if V = 1 then
        FBullCards[C][14] := 1;
    end;
  end;
  
  for I := Low(FCommCards) to High(FCommCards) do
  begin
    C := CardColorFromIdx(FCommCards[I]);
    V := CardValueFromIdx(FCommCards[I]);
    FCowboyCards[C][V] := 1;
    FBullCards[C][V] := 1;
    if V = 1 then
    begin
      FCowboyCards[C][14] := 1;
      FBullCards[C][14] := 1;
    end;
  end;
end;

procedure TTexasPoker.OutputStats(const ExecNum: Integer;
  const OutputCallback: TOutputCallback);
const
  cStatStr   =     %12s  %12s  %12s  %12s;
  cStatStrEx =     %25s  %12s  %12s  %12s;
  cSumStr    =     %12s  %12s  %12s;
var
  HandCardType: THandCardType;
  Rate: Single;
  Cnt, WinLoseCnt: Integer;
  WinCardType: TGameCardType;
begin
  OutputCallback(‘‘);
  
  OutputCallback(  任一人手牌统计信息如下:);
  OutputCallback(Format(cStatStr, [牌型, 次数, 比例, 赔率]));
  OutputCallback(        --------------------------------------------------);
  for HandCardType := Low(FHandCardTypes) to High(FHandCardTypes) do
  begin
    Rate := FHandCardTypes[HandCardType] / ExecNum;
    OutputCallback(Format(cStatStr, [cHandCardTypeDesc[HandCardType], IntToStr(FHandCardTypes[HandCardType]), Format(%.6f, [Rate * 100]) + %, Format(%.6f, [1 / Rate])]));
  end;

  OutputCallback(‘‘);
  OutputCallback(  获胜牌型统计信息如下:);
  OutputCallback(Format(cStatStrEx, [牌型, 次数, 比例, 赔率]));
  OutputCallback(       ----------------------------------------------------------------);
  WinLoseCnt := ExecNum - FWinningTypes[wtTie];    
  Cnt := FWinCardTypes[gctHighCard] + FWinCardTypes[gctOnePair];
  OutputCallback(Format(cStatStrEx, [高牌/一对, IntToStr(Cnt), Format(%.6f, [(Cnt / WinLoseCnt) * 100]) + %, Format(%.6f, [1 / (Cnt / WinLoseCnt)])]));
  Cnt := FWinCardTypes[gctTwoPair];
  OutputCallback(Format(cStatStrEx, [两对, IntToStr(Cnt), Format(%.6f, [(Cnt / WinLoseCnt) * 100]) + %, Format(%.6f, [1 / (Cnt / WinLoseCnt)])]));
  Cnt := FWinCardTypes[gctFlush] + FWinCardTypes[gctStraight] + FWinCardTypes[gctThreeOfAKind];
  OutputCallback(Format(cStatStrEx, [三条/顺子/同花, IntToStr(Cnt), Format(%.6f, [(Cnt / WinLoseCnt) * 100]) + %, Format(%.6f, [1 / (Cnt / WinLoseCnt)])]));
  Cnt := FWinCardTypes[gctFullHouse];
  OutputCallback(Format(cStatStrEx, [葫芦, IntToStr(Cnt), Format(%.6f, [(Cnt / WinLoseCnt) * 100]) + %, Format(%.6f, [1 / (Cnt / WinLoseCnt)])]));
  Cnt := FWinCardTypes[gctFourOfAKind] + FWinCardTypes[gctStraightFlush] + FWinCardTypes[gctRoyalFlush];
  OutputCallback(Format(cStatStrEx, [金刚/同花顺/皇家同花顺, IntToStr(Cnt), Format(%.6f, [(Cnt / WinLoseCnt) * 100]) + %, Format(%.6f, [1 / (Cnt / WinLoseCnt)])]));

  OutputCallback(‘‘);
  OutputCallback(  输赢统计信息如下:);
  OutputCallback(Format(cStatStr, [描述, 次数, 比例, 赔率]));
  OutputCallback(      ----------------------------------------------------);
  OutputCallback(Format(cStatStr, [牛仔获胜数, IntToStr(FWinningTypes[wtCowboy]), Format(%.6f, [(FWinningTypes[wtCowboy] / ExecNum) * 100]) + %, Format(%.6f, [1 / (FWinningTypes[wtCowboy] / ExecNum)])])); 
  OutputCallback(Format(cStatStr, [公牛获胜数, IntToStr(FWinningTypes[wtBull]), Format(%.6f, [(FWinningTypes[wtBull] / ExecNum) * 100]) + %, Format(%.6f, [1 / (FWinningTypes[wtBull] / ExecNum)])]));
  OutputCallback(Format(cStatStr, [平局数, IntToStr(FWinningTypes[wtTie]), Format(%.6f, [(FWinningTypes[wtTie] / ExecNum) * 100]) + %, Format(%.6f, [1 / (FWinningTypes[wtTie] / ExecNum)])]));
  
  OutputCallback(‘‘);
  OutputCallback(  获胜明细牌型统计信息如下:);
  OutputCallback(Format(cStatStr, [牌型, 次数, 比例, 赔率]));
  OutputCallback(      ----------------------------------------------------);
  for WinCardType := Low(FWinCardTypes) to High(FWinCardTypes) do
  begin
    Rate := FWinCardTypes[WinCardType] / ExecNum;
    if Rate > 0 then
      OutputCallback(Format(cStatStr, [cGameCardTypeDesc[WinCardType], IntToStr(FWinCardTypes[WinCardType]), Format(%.6f, [Rate * 100]) + %, Format(%.6f, [1 / Rate])]))
    else
      OutputCallback(Format(cStatStr, [cGameCardTypeDesc[WinCardType], IntToStr(FWinCardTypes[WinCardType]), Format(%.6f, [Rate * 100]) + %, 0%]));
  end;
  
  OutputCallback(‘‘);
  OutputCallback(  牛仔所中牌型统计信息如下:);
  OutputCallback(Format(cSumStr, [牌型, 次数, 比例]));
  OutputCallback(      --------------------------------------);
  for WinCardType := Low(FCowboyCardTypes) to High(FCowboyCardTypes) do
    OutputCallback(Format(cSumStr, [cGameCardTypeDesc[WinCardType], IntToStr(FCowboyCardTypes[WinCardType]), Format(%.6f, [FCowboyCardTypes[WinCardType] / ExecNum * 100]) + %]));

  OutputCallback(‘‘);
  OutputCallback(  公牛所中牌型统计信息如下:);
  OutputCallback(Format(cSumStr, [牌型, 次数, 比例]));
  OutputCallback(      --------------------------------------);
  for WinCardType := Low(FBullCardTypes) to High(FBullCardTypes) do
    OutputCallback(Format(cSumStr, [cGameCardTypeDesc[WinCardType], IntToStr(FBullCardTypes[WinCardType]), Format(%.6f, [FBullCardTypes[WinCardType] / ExecNum * 100]) + %]));

  OutputCallback(‘‘);
end;

procedure TTexasPoker.Reset;
begin
  ZeroMemory(@FHandCardTypes, Length(FHandCardTypes) * 4);
  ZeroMemory(@FWinningTypes, Length(FWinningTypes) * 4);
  ZeroMemory(@FHandCardTypes, Length(FHandCardTypes) * 4);
  ZeroMemory(@FCowboyCardTypes, Length(FCowboyCardTypes) * 4);
  ZeroMemory(@FBullCardTypes, Length(FBullCardTypes) * 4);
end;

procedure TTexasPoker.StatHandCardTypes;
var
  BoMatched: Boolean;
begin
  BoMatched := False;
  
  if (FHandCardProps[1].Color = FHandCardProps[3].Color)
    or (FHandCardProps[2].Color = FHandCardProps[4].Color)
  then
  begin
    Inc(FHandCardTypes[hctFlush]);
    BoMatched := True;
  end;

  if (Abs(FHandCardProps[1].Value - FHandCardProps[3].Value) = 1)
    or (Abs(FHandCardProps[2].Value - FHandCardProps[4].Value) = 1)
    or (Abs(FHandCardProps[1].Value - FHandCardProps[3].Value) = 12)
    or (Abs(FHandCardProps[2].Value - FHandCardProps[4].Value) = 12)
  then
  begin
    Inc(FHandCardTypes[hctStraight]);
    BoMatched := True;
  end;

  if (FHandCardProps[1].Value = FHandCardProps[3].Value) or (FHandCardProps[2].Value = FHandCardProps[4].Value) then
  begin
    Inc(FHandCardTypes[hctOnePair]);
    if ((FHandCardProps[1].Value = FHandCardProps[3].Value) and (FHandCardProps[1].Value = 1))
      or ((FHandCardProps[2].Value = FHandCardProps[4].Value) and (FHandCardProps[2].Value = 1))
    then
      Inc(FHandCardTypes[hctPairA]);
    BoMatched := True;
  end;

  if ((FHandCardProps[1].Color = FHandCardProps[3].Color) and ((Abs(FHandCardProps[1].Value - FHandCardProps[3].Value) = 1) or (Abs(FHandCardProps[1].Value - FHandCardProps[3].Value) = 12)))
    or ((FHandCardProps[2].Color = FHandCardProps[4].Color) and ((Abs(FHandCardProps[2].Value - FHandCardProps[4].Value) = 1) or (Abs(FHandCardProps[2].Value - FHandCardProps[4].Value) = 12)))
  then
  begin
    Inc(FHandCardTypes[hctFlushStraight]);
    BoMatched := True;
  end;

  if not BoMatched then
    Inc(FHandCardTypes[hctNone]);
end;

end.

 

  后记:

    1) 在玩了数百把德州牛仔后我还是“流失”了,虽然有厌恶其内傻瓜式机器人的因素,但主要还是因明显感受到了“猫腻”的存在,当然,也可能只是我的错觉吧,毕竟,多次发生极小概率的事件,也是有可能的——纯随机的东西谁也无法预测

    2) 假定系统真正公平,在对这些几率熟悉后,其实还是有不小的几率赢余的(假定玩家“理性”),手牌同花、获胜牌型高牌/一对之类不用说,赌平局也是可以尝试的(100把对局里约出4次平局)

以上是关于《天天德州》之德州牛仔概率计算器的主要内容,如果未能解决你的问题,请参考以下文章

团队项目----德州扑克数据库设计之改进版

团队项目----基于德州扑克的数据结构分析

java和h5 canvas德州扑克开发中

德州项目可行性研究报告

德州扑克AI

python——德州扑克