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.
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