I love to play bridge. When I was a little kid, my parents played with their parents. I would put my palm on the table, and my dad would riffle shuffle on my hand. The thrills of childhood!

 

Nowadays I read bridge columns and try to play at the local bridge club whenever I can (which usually means when one of my brothers comes to visit).

 

I wrote the program below a long time ago: it deals and displays 4 hands of 13 cards each in a loop. Each hand is sorted and evaluated for the number of “high card points”. (Ace=4, King=3, Queen=2, and Jack=1.).  These are displayed and the statistics inserted into a cursor.

 

 

The primary purpose of this 150 line program was to calculate distributions. In bridge, the combined cards of North/South play against East/West. If NS have 9 spades between them, then the opponents have 4.

 

The actual distribution calculation is just a couple SQL select statements. Then a BROWSE with a calculated field based on 2 related cursors shows the results. The power of embedding data into the programming language!

 

How often is the distribution of those 4 spades split evenly amongst East/West  with 2 each ?  3-1?  4-0 ? The chance of an even split of 2-2  (40%) is actually lower in odds than 3-1 (50%)!. (see this link for more info)

 

 

Windows ships with cards.dll, which contains the pictures of playing cards, as well as the playing card backs. It’s used in solitaire, hearts, etc.

 

To find out what the exports of this DLL are, use link from Visual Studio to see the dll’s exports.

 

link /dump /exports c:\windows\system32\cards.dll

 

    ordinal hint RVA      name

 

          1    0 000013E0 WEP

          2    1 0000118A cdtAnimate

          3    2 00001813 cdtDraw

          4    3 000014F3 cdtDrawExt

          5    4 000013E6 cdtInit

          6    5 0000138B cdtTerm

 

Drawing cards is as simple as getting a Device Context (GetDC) and calling the cdtDraw  entry point of cards.dll.

 

The following Visual FoxPro program should run in most older versions.

 

 

 

PUBLIC ox

#define NORTHx    150

#define NORTHy    0

#define EASTx     300

#define EASTy     140

#define SOUTHx    150

#define SOUTHy    280

#define WESTx     0

#define WESTy     140

 

DECLARE integer cdtInit IN cards.dll integer @, integer @

DECLARE integer cdtTerm IN cards.dll

DECLARE integer cdtDraw IN cards.dll integer, integer, integer, integer, integer, integer

DECLARE integer cdtDrawExt IN cards.dll integer, integer, integer, integer, integer, integer, integer, integer

DECLARE integer GetDC IN WIN32API integer

DECLARE integer ReleaseDC IN WIN32API integer, integer

DECLARE integer Sleep IN WIN32API integer

DECLARE integer ValidateRect IN WIN32API integer, string

ox=NEWOBJECT("myform")

ox.show()

FOR nloops = 1 to 1000

      IF CHRSAW()

            EXIT

      endif

      ox.dealem

      IF MOD(nloops,100) = 0

            ?nloops

      endif

ENDFOR

ox=0

SELECT spades,count(*) FROM deals GROUP BY 1 INTO CURSOR foo

INDEX ON spades TAG spades

BROWSE LAST nowa

SELECT spades,spopp,count(*) FROM deals GROUP BY 1,2 INTO CURSOR foo2

SET RELATION TO spades INTO foo

BROWSE LAST NOWAIT FIELDS spades,spopp,cnt,pct=100*cnt / foo.cnt

 

DEFINE CLASS myform as Form

      left=250

      height=400

      width=500

      AllowOutput=.f.

      xpix = 0

      ypix = 0

      DIMENSION crd[52]

      DIMENSION xlt[52]

      ADD OBJECT lblN as label WITH left=NORTHX, top = NORTHy+100

      ADD OBJECT lblE as label WITH left=EASTX, top = EASTy+100

      ADD OBJECT lblS as label WITH left=SOUTHX, top = SOUTHy+100

      ADD OBJECT lblW as label WITH left=WESTX, top = WESTy+100

      PROCEDURE init

            LOCAL i

            RAND(10)

            *crd 1-13 = 2-A clubs, 14-26 = 2-A Diam

            FOR i = 1 to 52

                  this.crd[i] = i

            ENDFOR

            *xlt 1-4 = A CDHS, 5-8 = 2 CDHS

            FOR i = 1 to 4

                  this.xlt[13 *i    ] = i - 1   && the aces

            ENDFOR

            FOR j = 2 to 13   && 2 thru K

                  FOR i = 0 to 3

                        this.xlt[j-2  + 13 *i+1] =  4*(j-1) + i

                  ENDFOR

            endfor

            CREATE CURSOR deals (north i, east i, south i, west i,spades i,spopp i)

            xpix=0

            ypix=0

            cdtInit(@xpix, @ypix)

            thisform.xpix = xpix

            thisform.ypix = ypix

      PROCEDURE shuffle

            LOCAL i, tmp

            FOR i = 1 to 52

                  nrnd = INT(RAND() * 52 )+1

                  tmp = this.crd[i]

                  this.crd[i] = this.crd[nrnd]

                  this.crd[nrnd] = tmp

            ENDFOR

      PROCEDURE sortem(nHand, xpos, ypos)

            LOCAL i,j, nSt, nEnd, tmp, npts, ndenom

            npts=0

            nSt = nHand * 13+1

            nEnd = nSt + 12

            FOR i = nSt to nEnd

                  FOR j = nSt to i-1

                        IF this.crd[i] > this.crd[j]

                              tmp = this.crd[i]

                              this.crd[i] = this.crd[j]

                              this.crd[j] = tmp

                        endif

                  endfor

            ENDFOR

            hdc = GetDC(this.hwnd)

            FOR i = nSt to nEnd

                  ndenom = MOD(this.crd[i]-1, 13)

                  IF ndenom > 8

                        npts = npts + ndenom - 8

                  ENDIF

                  cdtDraw(hdc, xpos+(i-nSt)*12,ypos,this.xlt[this.crd[i]],0,0)

            ENDFOR

            ReleaseDC(this.hwnd, hdc)

            srect = this.ntox(0) + this.ntox(0) +     this.ntox(thisform.width) +  this.ntox(thisform.height)

            ValidateRect(this.HWnd, srect)

            RETURN npts

      PROCEDURE ntox(nPix)

            RETURN chr(MOD(npix,256)) + CHR(int(npix/256)) +CHR(0)+CHR(0)

      PROCEDURE dealem

            LOCAL nptsn,nptse, nptss, nptsw

            thisform.shuffle

            nptsn = this.sortem(0,NORTHx,NORTHy)

            nptse = this.sortem(2,EASTx, EASTy)

            nptss = this.sortem(1,SOUTHx,SOUTHy)

            nptsw = this.sortem(3,WESTx,WESTy)

            thisform.lbln.caption = TRANSFORM(nptsn)

            thisform.lble.caption = TRANSFORM(nptse)

            thisform.lbls.caption = TRANSFORM(nptss)

            thisform.lblw.caption = TRANSFORM(nptsw)

            nsp = 0

            FOR i = 1 to 26   && count spades in north & south hands

                  IF this.crd[i] > 39

                        nsp = nsp + 1

                  endif

            ENDFOR

            *calc split by cnting how many in east

            nspe = 0

            FOR i = 27 to 39

                  IF this.crd[i] > 39

                        nspe = nspe + 1

                  endif

            ENDFOR

*           thisform.lblw.caption = TRANSFORM(nsp)+","+TRANSFORM(nspe)

            IF nspe < (13 - nsp)/2  && if # of East's spades < half of E-W spades, then use cnt of spades in W

                  nspe = (13 - nsp) - nspe

            ENDIF

            INSERT INTO deals VALUES (nptsn,nptse,nptss,nptsw,nsp,nspe)

      PROCEDURE destroy

            cdtTerm()

      PROCEDURE xlate(nCrd as Integer)    && ncrd 0-51

            *0-13 = 2-A clubs

            LOCAL nsuit, nNum

            nSuit = int(nCrd/13)    && 0-3 for clubs-spades

            nNum = MOD(nCrd,13)           && 0 - 12 for 2 - A

            IF nNum = 12

                  nCrd = nSuit

            ELSE

                  nCrd = 4*(nNum + 1) + nSuit

            endif

            RETURN nCrd

ENDDEFINE

20622