# Displaying playing cards for bridge

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.

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

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

1. Alan Biddle says:

Calvin,

Great solution! One note… the "hdc = GetDC(this.hwnd)" call isn’t going to work in older versions of VFP that don’t have the hwnd property on the form.

Fox rocks!

2. Plinio says:

I did not know there was a cards.dll !

3. Koen Piller says:

Hi,

Very impressive. But somehow I am missing the point. Copy and Pasted your listing into a program: myBridge. Typed in command window DO myBridge. And MyForm shows, I see a counter below each hand, I see all the cards change at random, and can hardly finish this programm. Seem interesting to start a Bridge teaching programm, but what is the main purpose of your listing?

Koen

4. Merlin Vilhauer says:

Any chance you have this code in visual c++ ?

you could send it to me via  vilhamm@aol.com

5. Sam Hobbs says:

Thank you for this; I am a little curious.

I am a C++ programmer but not a FoxPro programmer. I tried to use this program in Visual FoxPro version 6 but I got an error saying that the HWND property is not found; the line is:

hdc = GetDC(this.hwnd)

in the sortem PROCEDURE. Should the program work in version 6?

This is not important; I was just curious. I don’t need an answer. I found this page while searching for something else.

6. Bill G. says:

I'm trying to teach a small group of seniors who learned bridge when in college 50+ years ago. they know Goren well and are good players but I've talked them into learnign the new 2 over 1 bridge . I know nothing about programming but have set up a blog and would like to be able to display hands I could use to teach the new system. Any ideas on how I might do that and actually select  various hands of my choosing? thanks for any advice you might offer.

Bill G.