This program implements a practice bridge hand dealer and card-play trainer. It deals a shuffled 52-card deck into four hands (North, East, South, West), displays them on screen using suit UDGs for spades, hearts, diamonds, and clubs, then allows the user to enter a contract and play out all 13 tricks. The shuffle uses a Fisher-Yates-style algorithm encoded in a string of two-digit card numbers stored in `A$`. Trick-taking logic evaluates played cards numerically, tracking trump and lead suit, and sounds BEEP tones at contract-making or defeating trick counts.
Program Structure
The program is organized into loosely coupled routines, navigated via GO TO and GO SUB. The main flow is:
- Lines 1–15: UDG setup and jump to intro/deal entry point.
- Lines 670–699: Title screen, instructions, deal invocation, sort, then display.
- Lines 600–645: Card shuffle and deck initialization.
- Lines 100–150: Selection sort of each hand.
- Lines 60–80: Hand display loop.
- Lines 250–570: Contract input, card play, trick resolution loop.
- Lines 900–920: End-of-hand options (redeal or replay).
Subroutines are used sparingly: GO SUB 20 builds a display string for each suit holding, and GO SUB 100 sorts each hand. Most other logic uses GO TO chains.
UDG Initialization
Lines 5–10 define four UDGs for the suit symbols using POKE USR X$+J,X, reading glyph bitmaps from DATA. The characters loaded are "H" (hearts), "C" (clubs), "D" (diamonds), and "S" (spades), occupying UDG slots corresponding to those letters. These are then referenced in the display string I$="\s\h\d\c" at line 671 and in suit indicator prints throughout the play loop.
Shuffle Algorithm
The deck is encoded in A$ at line 600 as a 104-character string of zero-padded two-digit numbers from "01" to "52". The shuffle loop (lines 610–645) picks a random two-character substring, extracts it into B$(X) and A(X), then removes those two characters from A$ using string slicing. This is a correct partial Fisher-Yates draw-without-replacement. The special case at lines 620–625 handles the final card when LEN A$=2 and issues a RETURN mid-loop — a notable structural quirk that exits the GO SUB 600 call early from inside the FOR loop body.
Hand Sorting
Lines 100–150 implement a selection sort over 13 elements. Variable C is initialized to 12 (step count), and each pass finds the maximum value in the unsorted tail, swapping it to the end. The sort is descending by card number (where Ace=14 is highest). The loop decrements C and returns when C=0. The comment REM FLOAT SORT, SYNC 4:2,20 is a note to the author’s own documentation system.
Hand Display and Suit String Building
The GO SUB 20 routine (lines 20–45) constructs a Procrustean (fixed-capacity) string E$(ED) of up to 10 characters representing a suit holding. It scans the hand array for cards belonging to the current suit offset B, builds a rank string using D$="AKQJ1098765432", and handles the special two-character case "10". The variable V$ is a space character used as a sentinel/padding value throughout the display and erasure logic.
Line 60 uses DATA 0,12,8,20,16,12,8,0 at line 75 to supply screen row/column coordinates for the four hands’ display positions, printing with INK 2 for red suits (hearts and diamonds).
Contract and Play Input
Lines 275–305 validate three separate INPUT fields: declarer direction (H$), denomination (K$: 1–7), and suit/NT (T$: S/H/D/C/N). Each is checked against the lookup string O$="NESWNSHDC1234567" using a FOR loop; invalid input loops back to line 275. This is an efficient single-string multi-field lookup idiom.
The card play input at line 435 accepts rank strings (e.g., "A", "10", "J") validated against the current suit holding R$ using a sliding substring search at lines 450–455. Entering "X" at any input point triggers a return to trick 1 (line 495 via GO TO 80).
Trick Evaluation
The array C(4) stores the effective trick value for each player’s played card. Lines 470–475 assign values: if following the lead suit, the card’s numeric value is used; if playing trump, 13 is added to the value, making any trump beat any non-trump. The winner is determined at lines 500–510 by finding the maximum in C(1..4).
Score Tracking and Audio
Running trick counts are displayed for both N-S and E-W. BEEP calls provide audio feedback at contract-making or defeating thresholds:
- A short beep (
BEEP .25,30orBEEP .25,-20) sounds on each trick won. - A long beep (
BEEP 1,0orBEEP 1,-60) sounds when the contract is made or defeated, computed asVAL K$+6tricks for declarer or8-VAL K$tricks for defenders.
Notable Idioms and Techniques
VAL H$converts direction letters to numeric offsets (N=0 implied by ASCII difference or lookup position) — actuallyVAL H$on"N","E","S","W"will return 0; the program relies onO$(R)position for numeric direction viaR, but usesVAL H$*4— this only works correctly ifH$has been set to a digit string, which happens at line 340:H$=O$(R+1)where positions 5–8 ofO$are"N","S","H","D"— actuallyO$positions 1–4 areN,E,S,Wand 5–8 are re-used as suit chars; this is a latent confusion but the program resetsH$from position-derived values.- String boolean expressions like
(D$(Y) AND Y<5)select substrings conditionally withoutIF. RESTORE 315/READloops re-read the suit symbol data multiple times during play (lines 476–483).- The
REM HEREcomments on lines 45, 150, and 340 are positional markers indicating jump targets forGO TOstatements, a common idiom when the target is mid-routine.
Content
Source Code
1 REM SAVE "DEAL A" AT B35
5 FOR I=1 TO 4: READ X$: FOR J=0 TO 7: READ X: POKE USR X$+J,X: NEXT J: NEXT I
10 DATA "H",34,119,127,127,62,62,28,8,"C",28,28,8,107,127,107,8,8,"D",8,28,62,127,62,28,8,0,"S",8,28,62,127,127,127,107,8
15 GO TO 670
20 LET ED=ED+1: LET F$="": IF ED=17 THEN LET ED=1: REM PRINT HAND/SUIT ROUTINE TO...
25: FOR R=A TO A+12: LET X=A(R): LET Y=X-B: IF Y>0 AND Y<14 THEN GO TO 40
30 NEXT R
35 PRINT E$(ED): LET G$(ED)=E$(ED): LET V=V+1: RETURN
40 LET F$=F$+(D$(Y) AND Y<5)+("10" AND Y=5)+(D$(Y+1) AND Y>5): LET E$(ED)=F$
45 GO TO 30: REM HERE
60 RESTORE 75: FOR A=1 TO 40 STEP 13: READ V: READ H: FOR B=0 TO 39 STEP 13: PRINT INK (2 AND (B=13 OR B=26));AT V,H;I$(B/13+1);;V$;: GO SUB 20
65 NEXT B: NEXT A: GO TO 250
75 DATA 0,12,8,20,16,12,8,0
80 FOR X=20 TO 21: FOR Y=0 TO 31: PRINT AT X,Y;V$: NEXT Y: NEXT X: GO TO 60
100 LET C=12: REM FLOAT SORT, SYNC 4:2,20; TO...
110 LET K=A: LET Q=A(A)
115 FOR S=A+1 TO A+C: IF A(S)<Q THEN GO TO 130
120 LET Q=A(S): LET K=S
130 NEXT S
135 LET Z=A(A+C): LET A(A+C)=A(K): LET A(K)=Z
140 LET C=C-1: IF C=0 THEN RETURN
150 GO TO 110: REM HERE
250 LET O$="NESWNSHDC1234567": DIM C(4)
255 LET A=14: LET K=13: LET Q=12: LET J=11
260 LET N=0: LET E=1: LET S=2: LET W=3
265 LET H=2: LET D=3: LET C=4
270 LET I=1: LET TR=1: LET II=0
275 INPUT "DECLARER?";H$;"CONTRACT?";K$;T$
280 LET J$=H$: FOR R=1 TO 4: IF H$=O$(R) THEN GO TO 290
285 NEXT R: GO TO 275
290 FOR R=10 TO 16: IF K$=O$(R) THEN GO TO 300
295 NEXT R: GO TO 275
300 FOR R=5 TO 9: IF T$=O$(R) THEN GO TO 310
305 NEXT R: GO TO 275: REM END ERROR TRAPS
310 FOR M=1 TO 5: READ M$: READ N$: PRINT AT 21,0;H$;": ";K$; INK (2 AND (T$="H" OR T$="D"));N$ AND M$=T$: NEXT M
315 DATA "S","\s","H","\h","D","\d","C","\c","N","NT"
320 RESTORE 315
330 FOR R=1 TO 4: IF H$=O$(R) THEN GO TO 340: REM PASS PLAY ROUTINE TO...
335 NEXT R
340 LET H$=O$(R+1): REM HERE
345 PRINT AT 20,0;H$;" TO PLAY"
350 IF I>1 THEN GO TO 395
352 LET VV=0: REM ONE-SUIT HAND ROUTINE...
355 FOR T=1 TO 4: IF E$(VAL H$*4+T,1)=V$ THEN GO TO 365
360 LET TT=T: GO TO 367
365 LET VV=VV+1
367 NEXT T
369 IF VV=3 THEN LET S$=O$(TT+5)
370 IF VV=3 THEN GO TO 385: REM TO HERE
375 INPUT "SUIT?";S$: IF S$="X" THEN GO TO 495
380 IF S$<>"S" AND S$<>"H" AND S$<>"D" AND S$<>"C" THEN GO TO 375
385 IF I=1 THEN LET W$=S$: REM W$ IS LEAD;L$ IS WORKING SUIT
390 LET L$=S$
395 IF L$="S" THEN LET Z=VAL H$*4+1: REM "S" ALREADY VALUED FOR SOUTH
400 IF L$="S" THEN GO TO 410
405 LET Z=VAL H$*4+VAL L$
410 FOR R=1 TO 10: IF E$(Z,R)=V$ THEN GO TO 420: REM R-1 IS # OF VISIBLE CHARACTERS IN PROCRUSTEAN E$
415 NEXT R
420 LET R$=E$(Z,1 TO R-1)
425 IF LEN R$=0 THEN GO TO 352: REM VOIDS
430 IF R$="10" OR LEN R$=1 THEN LET C$=R$: GO TO 440: REM SINGLETONS
435 INPUT "CARD?";C$: IF C$="X" THEN GO TO 495
440 IF LEN C$>1 AND C$<>"10" THEN GO TO 435
445 IF C$="" THEN GO TO 435
450 FOR R=1 TO LEN R$: IF R$(R TO R+LEN C$-1)=C$ THEN GO TO 460
455 NEXT R: GO TO 435
460 LET E$(Z)=R$( TO R-1)+R$(R+LEN C$ TO )
465 PRINT AT Z-1+(4 AND H$="E")+(8 AND H$="S")-(4 AND H$="W"),14+(8 AND H$="E")-(12 AND H$="W");E$(Z)+((V$+V$) AND E$(Z)=""): REM REMOVES CARD FROM HAND AND CLOSES UP SUIT
470 LET C(VAL H$+1)=(VAL C$ AND L$=W$)
475 IF L$=T$ THEN LET C(VAL H$+1)=VAL C$+13
476 FOR M=1 TO 4: READ M$: READ N$: IF M$=L$ THEN GO TO 480: REM READS 315
477 NEXT M
480 LET V=10+(2 AND H$="S")-(2 AND H$="N"): LET HZ=14+(2 AND H$="E")-(2 AND H$="W")
481 PRINT AT V,HZ; INK (2 AND (L$="H" OR L$="D"));N$; INK 0;C$;V$: REM PLAYS CARD TO CENTER
483 RESTORE 315
485 LET I=I+1: IF I<5 THEN GO TO 575
495 LET I=1: FOR X=8 TO 12 STEP 2: FOR Y=12 TO 18: PRINT AT X,Y;V$: NEXT Y: NEXT X: IF C$="X" OR S$="X" THEN GO TO 80: REM PICKS UP TRICK WITH ACCEPTABLE DELAY
500 LET R=1: FOR X=2 TO 4: IF C(R)>C(X) THEN GO TO 510
505 LET R=X
510 NEXT X
515 LET H$=O$(R): PRINT AT 21,20;"E-W": PRINT AT 21,12;"N-S": IF R=1 OR R=3 THEN GO TO 545: REM PASSES LEAD TO WINNER;E-W TRICK ROUTINE
520 LET II=II+1: PRINT AT 21,24;II: IF J$="S" OR J$="N" THEN GO TO 535
525 BEEP .25,30: IF II=VAL K$+6 THEN BEEP 1,0
530 GO TO 565
535 BEEP .25,-20: IF II=8-VAL K$ THEN BEEP 1,-60
540 GO TO 565
545 PRINT AT 21,16;TR-II: IF J$="E" OR J$="W" THEN GO TO 560: REM N-S TRICK ROUTINE
550 BEEP .25,30: IF TR-II=VAL K$+6 THEN BEEP 1,0
555 GO TO 565
560 BEEP .25,-20: IF TR-II=8-VAL K$ THEN BEEP 1,-60
565 LET TR=TR+1: IF TR=14 THEN GO TO 900
570 GO TO 345
575 LET L$=W$: GO TO 330: REM TO FOLLOW LEAD RATHER THAN LAST SUIT PLAYED
600 LET A$="01020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546474849505152"
602 LET ED=0: DIM E$(16,10): DIM G$(16,10): REM WORKING AND RECORDING SUIT STRINGS
603 LET D$="AKQJ1098765432": LET V$=" "
605 DIM B$(52,2): DIM A(52)
610 FOR X=1 TO 52
615 LET R=(INT (RND*(53-X))+1)*2-1
620 IF LEN A$=2 THEN LET A(X)=VAL A$
625 IF LEN A$=2 THEN RETURN
630 LET B$(X)=A$(R TO R+1)
635 LET A$=A$( TO R-1)+A$(R+2 TO )
640 LET A(X)=VAL B$(X): REM CONVERT TO NUMBERS
645 NEXT X: REM SYNC 4:2,10
670 PRINT TAB 5;"PRACTICE BRIDGE HANDS"'
671 LET I$="\s\h\d\c": FOR I=1 TO 4: PRINT INK (2 AND (I=2 OR I=3));AT 1,I*4+5;I$(I): NEXT I
672 PRINT : PRINT "Decide how hand should be bid."''
674 PRINT "Enter contract. Enter suit and"
676 PRINT "card to play hand. Forced plays"
678 PRINT "are automatic."''
680 PRINT "Input denomination of spot cards"
682 PRINT "and initial letter of suits and"
684 PRINT "face cards; then press ENTER."''
686 PRINT "Illegal inputs will not ENTER."''
687 PRINT "To change unENTERED input, use"
688 PRINT "R/L arrows and retype."''
689 PRINT "To return to trick 1, press ""X""."'''
690 GO SUB 600
694 FOR A=1 TO 40 STEP 13: GO SUB 100
695 NEXT A
696 PRINT "Press ""ENTER"" to deal hand."
697 INPUT U$
699 CLS : GO TO 60
900 CLS : PRINT "Press R for a new deal."
905 PRINT "Press ""X"" for last hand."
910 IF INKEY$="X" THEN GO TO 920
912 IF INKEY$="R" THEN RUN
915 GO TO 910
920 CLS : GO TO 60
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

