This program implements a full-featured Blackjack (21) card game with betting, insurance, doubling down, and hand splitting. The deck is simulated using a 260-element boolean array (DIM C(260)) which maps random indices to card ranks via integer division by 20, giving each of the 13 ranks approximately 20 slots and providing weighted randomness. Card display uses ZX Spectrum block-graphic border characters to draw playing-card outlines on screen. A card-counting aid (accessed via the ‘A’ key) calculates the tens ratio — remaining tens divided by remaining non-tens — to assist basic strategy decisions.
Program Analysis
Program Structure
The program is organized into a main flow section and a set of subroutines. Lines 4–120 initialize all variables and arrays. Lines 215–275 deal the initial four cards. The main player decision loop runs from line 312 to line 575. Several subroutines handle specific game functions:
4500–4615: Card drawing from the deck1900–2020: Screen display/refresh of all hands1610–1650: Split hand initialisation1700–1790: Split hand play loop4850–4942: Dealer draw and outcome resolution4950–4975: Natural Blackjack payout5035–5090: Player ace adjustment (11→1)5100–5195: Dealer ace adjustment6000–6070: Card-counting display (tens ratio)7000–7090: Split hand outcome resolution7130–7290: Split hand ace adjustment
Deck Simulation
The deck is represented by DIM C(260), a 260-element array acting as a boolean “used” flag. Each call to subroutine 4500 picks a random integer in 1–260 and rejects it if already used (C(CARD)=1). The rank is then derived at line 4530 with INT(CARD/20)+1, mapping slots 1–20 to rank 1 (Ace), 21–40 to rank 2, and so on up to rank 13. Since 13×20=260 exactly, each rank gets exactly 20 slots, representing a five-deck shoe. The deck reshuffles automatically at line 4500 when CN=210 (approximately 80% penetration).
A subtle issue exists at line 4532: the check IF CARD<1 OR CARD>13 THEN GOTO 4500 is actually unreachable given the arithmetic, since INT(x/20)+1 for x in 1–260 always yields 1–13. It is a defensive guard that adds no overhead in practice.
Card Display
Playing cards are drawn using block-graphic characters to form a simple card border. Each card occupies a 3-row by 4-column area on screen. The pattern used is:
- Top row:
\:'\''\':— a top border using block graphics - Middle row:
\:+ rank character +\ :— side borders with the rank label - Bottom row:
\:.\..\.:— a bottom border
The dealer’s hole card is concealed when X=0: line 1945 overwrites the second and third dealer card positions with inverse-space characters (\@@), effectively blanking them until the hand is resolved.
Ace Handling
Aces are stored internally as 11. When a hand exceeds 21, the bust-check routines (5035–5090 for player, 5100–5195 for dealer, 7130–7290 for split hand) scan the respective card arrays for an 11 and reduce the first found to 1. The AC flag ensures only one ace is converted per call, so each call handles one soft total reduction. Multiple busts require multiple calls, which the loop at lines 5100–5125 provides by iterating over all dealer card positions.
Game Features
| Feature | Key | Lines |
|---|---|---|
| Hit | Y | 525–560 |
| Stand | N | 530, 4850 |
| Double Down | D | 523, 565 |
| Split | S | 540–541, 1610 |
| Insurance | Y/N at prompt | 325–465 |
| Card count display | A | 524, 6000 |
Card-Counting Feature
Pressing ‘A’ during play calls subroutine 6000, which counts used cards of each rank by scanning the C() array in 20-element blocks. It combines the counts for ranks 10, J, Q, K (indices 10–13) into a single “tens” total. The tens ratio is calculated at line 6060 as (80-N(10))/(180-sum of non-tens used), representing remaining tens divided by remaining non-ten cards. This is a simplified form of a card-counting metric useful for basic strategy adjustment. The display switches to FAST mode for the calculation and returns to SLOW mode afterward.
Split Hand Logic
When a split is triggered (line 1610), the second player card is moved to the S() / S$() arrays and PCN is reset to 1. The original hand continues play normally. After the main hand resolves, control passes via GOTO 1700 or line 5024 to the split hand loop (1700–1790) for independent draw decisions. If an ace pair is split, line 1610 converts the ace value from 1 to 11 for proper scoring. The split outcome is resolved separately at lines 7000–7090.
Notable Bugs and Anomalies
- Line
5040is referenced byGOSUB 5040at line543, but the subroutine at that range begins at5035. Line5040itself (IF P(K)=11...) is mid-loop; entering at5040skips theFOR K=1 TO PCNstatement, meaning only the current value ofK(from the lastFORloop in scope) is checked, rather than all player cards. This is a latent bug in the player bust/ace adjustment path. - The
GOSUB 5040call at line543is followed immediately byIF PC>21 THEN GOTO 5000, but the ace-reduction subroutine may not correctly updatePCbefore this test becausePCis recalculated inside1900(called from5085via1900), not immediately in the bust path. In practice, the bust detection and ace reduction may interact incorrectly on certain soft hands. - The
SLOW/FASTkeywords at lines1900and6000/6066indicate this is a ZX81/TS1000 program using display mode switching for screen stability. - Line
519assignsLET XYZ=RNDwith no apparent use ofXYZelsewhere; this appears to be a discarded variable, possibly left from an earlier version. - Insurance payout logic at line
465deducts half the bet (M=M-B*0.5) when insurance was taken but the dealer does not have 21. There is no corresponding insurance win payout added elsewhere for the dealer-has-21 branch (line410), meaning insurance only costs money and never pays — the win message at line410simply prevents a loss of the main bet, but the insurance side bet is silently forfeited in all cases.
Content
Source Code
4 DIM C(260)
5 LET M=500
6 LET Q$="."
10 LET B=2
25 LET CN=1
30 LET PC=0
32 CLS
35 LET DC=0
40 DIM P(10)
45 DIM D(10)
50 DIM S(10)
55 LET SPLIT=0
80 LET DCN=2
85 LET PCN=2
90 DIM D$(10)
95 DIM P$(10)
100 DIM S$(10)
105 LET SCN=1
110 LET SC=0
115 LET I=0
120 LET X=0
215 GOSUB 4500
220 LET D(1)=CARD
225 LET D$(1)=C$
230 GOSUB 4500
235 LET D(2)=CARD
240 LET D$(2)=C$
250 GOSUB 4500
255 LET P(1)=CARD
260 LET P$(1)=C$
265 GOSUB 4500
270 LET P(2)=CARD
275 LET P$(2)=C$
280 GOSUB 1900
312 LET I=0
315 IF P(1)+P(2)=21 THEN GOTO 4950
320 IF D(1)=11 THEN LET I=1
325 IF I=1 THEN PRINT AT 20,0;"WOULD YOU LIKE INSURANCE ? Y/N"
327 LET Q$=INKEY$
330 PRINT AT 20,0;" "
335 IF Q$="Y" THEN GOTO 400
340 IF Q$="N" THEN LET I=0
350 IF I=0 THEN GOTO 450
355 GOTO 325
400 IF D(1)+D(2)<>21 THEN GOTO 450
410 PRINT ".DEALER.HAS.21.YOU.DO.NOT.LOSE."
412 LET X=1
415 GOTO 5024
450 IF DC=21 THEN GOTO 4815
460 IF I=1 THEN PRINT "DEALER DOES NOT HAVE 21"
465 IF I=1 THEN LET M=M-B*.5
470 PAUSE 200
514 PRINT AT 20,0;"CARD(Y/N)?";
515 IF SPLIT=1 THEN GOTO 520
516 IF PCN=2 AND PC>8 AND PC<12 THEN PRINT " DOUBLE(D)?";
517 IF PCN=2 AND P(1)=P(2) THEN PRINT " SPLIT(S)?"
518 LET XYZ=RND
519 IF PCN=2 AND P(1)=1 AND P(2)=11 THEN PRINT " SPLIT(S)?"
520 LET Q$=INKEY$
521 IF (Q$="N" OR Q$="D") AND SPLIT<>1 THEN LET X=1
523 IF Q$="D" THEN LET B=B*2
524 IF Q$="A" THEN GOSUB 6000
525 IF Q$="Y" OR Q$="D" THEN GOSUB 4500
527 IF Q$="Y" OR Q$="D" THEN GOTO 549
528 IF Q$="N" AND SPLIT=1 THEN GOTO 1700
530 IF Q$="N" THEN GOTO 4850
540 IF Q$="S" THEN GOSUB 1610
541 IF Q$="S" THEN GOTO 549
542 PRINT AT 20,0;" "
543 IF PC>21 THEN GOSUB 5040
545 IF PC>21 THEN GOTO 5000
547 GOTO 514
549 LET PCN=PCN+1
550 LET P(PCN)=CARD
555 LET P$(PCN)=C$
560 GOSUB 1900
565 IF Q$="D" THEN GOTO 4850
570 PRINT AT 20,0;"CARD(Y/N)? "
575 GOTO 520
1610 IF P(1)=1 THEN LET P(1)=11
1615 LET SPLIT=1
1620 LET PCN=1
1625 LET S(1)=P(2)
1630 LET P(2)=0
1635 LET S$(1)=P$(2)
1640 GOSUB 1900
1645 GOSUB 4500
1650 RETURN
1710 GOSUB 4500
1715 LET SCN=SCN+1
1720 LET S(SCN)=CARD
1725 LET S$(SCN)=C$
1730 GOSUB 1900
1740 PRINT AT 20,0;"SPLIT CARD(Y/N)? "
1750 LET Q$=INKEY$
1760 IF Q$="Y" THEN GOTO 1710
1765 IF Q$="N" THEN LET X=1
1770 IF Q$="N" THEN GOTO 4870
1774 IF SC>21 THEN GOSUB 7130
1775 IF SC>21 THEN LET X=1
1777 IF SC>21 THEN GOTO 4870
1780 PRINT AT 20,0;" "
1790 GOTO 1740
1900 SLOW
1910 PRINT AT 0,18;"BET= ";B
1912 PRINT AT 1,18;"MONEY= ";M
1913 IF PCN>2 AND X=0 THEN GOTO 1946
1915 LET DC=D(1)+D(2)+D(3)+D(4)+D(5)+D(6)+D(7)+D(8)+D(9)+D(10)
1918 PRINT AT 2,0;"DEALER HAS:";
1919 IF X=1 THEN PRINT " TOTAL=";DC
1920 FOR I=DCN-1 TO (DCN-1+X)
1925 PRINT AT 3,I*4+2;":'''':"
1930 PRINT AT 4,I*4+2;": ";D$(I);" :"
1935 PRINT AT 5,I*4+2;":....:"
1940 NEXT I
1945 IF X=0 THEN PRINT AT 3,10;"@@@@@@";TAB 10;"@@@@@@";TAB 10;"@@@@@@"
1946 IF Q$<>"D" AND X=1 THEN RETURN
1947 LET PC=P(1)+P(2)+P(3)+P(4)+P(5)+P(6)+P(7)+P(8)+P(9)+P(10)
1950 PRINT AT 8,0;"PLAYER HAS: TOTAL=";PC;" "
1955 FOR I=1 TO PCN
1960 PRINT AT 9,I*4+2;":'''':"
1965 PRINT AT 10,I*4+2;": ";P$(I);" :"
1970 PRINT AT 11,I*4+2;":....:"
1975 NEXT I
1982 IF PCN=1 THEN PRINT AT 9,10;" ";TAB 10;" ";TAB 10;" "
1985 IF SPLIT=0 THEN GOTO 2020
1987 LET SC=S(1)+S(2)+S(3)+S(4)+S(5)+S(6)+S(7)+S(8)+S(9)+S(10)
1990 PRINT AT 13,0;"PLAYER HAS: TOTAL=";SC
1995 FOR I=1 TO SCN
2000 PRINT AT 14,I*4+2;":'''':"
2005 PRINT AT 15,I*4+2;": ";S$(I);" :"
2010 PRINT AT 16,I*4+2;":....:"
2015 NEXT I
2020 RETURN
4500 IF CN=210 THEN PRINT AT 12,3;"******RESHUFFLE DECK******"
4515 IF CN=210 THEN DIM C(260)
4520 LET CARD=INT (260*RND)+1
4525 IF C(CARD)=1 THEN GOTO 4520
4527 LET C(CARD)=1
4530 LET CARD=INT (CARD/20)+1
4532 IF CARD<1 OR CARD>13 THEN GOTO 4500
4535 IF CARD=1 THEN LET C$="A"
4540 IF CARD=2 THEN LET C$="2"
4545 IF CARD=3 THEN LET C$="3"
4550 IF CARD=4 THEN LET C$="4"
4555 IF CARD=5 THEN LET C$="5"
4560 IF CARD=6 THEN LET C$="6"
4565 IF CARD=7 THEN LET C$="7"
4570 IF CARD=8 THEN LET C$="8"
4575 IF CARD=9 THEN LET C$="9"
4580 IF CARD=10 THEN LET C$="T"
4585 IF CARD=11 THEN LET C$="J"
4590 IF CARD=12 THEN LET C$="Q"
4595 IF CARD=13 THEN LET C$="K"
4600 IF CARD>10 THEN LET CARD=10
4605 IF CARD=1 THEN LET CARD=11
4610 LET CN=CN+1
4615 RETURN
4800 LET P(DCN)=CARD
4805 LET X=1
4812 GOSUB 1900
4815 PRINT AT 20,0;"DEALER HAS 21 YOU LOSE "
4820 LET M=M-B
4825 PAUSE 500
4830 GOTO 30
4850 GOSUB 1900
4895 IF DC<17 THEN GOSUB 4500
4897 IF DC>=17 THEN GOTO 4920
4900 LET DCN=DCN+1
4905 LET D(DCN)=CARD
4910 LET D$(DCN)=C$
4915 GOTO 4850
4920 IF SPLIT=1 AND PC>21 THEN GOTO 5024
4921 IF DC=PC THEN PRINT AT 20,0;"...............PUSH............."
4922 IF PC=DC THEN GOTO 5024
4923 IF DC>21 THEN GOSUB 5100
4924 IF DC<17 THEN GOTO 4850
4925 IF DC>21 OR PC>DC THEN PRINT AT 20,0;"............YOU.WIN............"
4927 IF DC>21 OR PC>DC THEN LET M=M+B
4930 IF DC>21 OR PC>DC THEN GOTO 5024
4935 PRINT AT 20,0;"..........DEALER...WINS........"
4940 LET M=M-B
4942 GOTO 5024
4950 LET X=1
4965 LET M=M+1.5*B
4970 PRINT AT 20,0;"YOU WIN...............BLACKJACK"
4975 GOTO 5024
5000 LET M=M-B
5020 PRINT "....OVER 21......BUST.........."
5022 IF SPLIT=0 THEN LET X=1
5023 IF SPLIT=1 THEN GOTO 1700
5024 IF SPLIT=1 THEN GOTO 7000
5025 GOSUB 1900
5026 IF Q$="D" THEN LET B=B/2
5027 PRINT AT 19,0;"INPUT > Y < TO CHANGE THE BET "
5028 PAUSE 300
5030 LET B$=INKEY$
5032 IF B$="Y" THEN PRINT ">>> INPUT NEW BET <<< "
5033 IF B$="Y" THEN INPUT B
5034 GOTO 30
5035 FOR K=1 TO PCN
5042 IF P(K)=11 THEN GOSUB 5061
5044 NEXT K
5050 RETURN
5061 IF PC<22 THEN GOTO 5090
5062 LET AC=0
5065 FOR I=1 TO PCN
5068 IF AC=1 THEN GOTO 5080
5070 IF P(I)=11 THEN LET AC=1
5075 IF P(I)=11 THEN LET P(I)=1
5080 NEXT I
5085 GOSUB 1900
5090 RETURN
5100 FOR K=1 TO DCN
5120 IF D(K)=11 THEN GOSUB 5150
5122 LET DC=D(1)+D(2)+D(3)+D(4)+D(5)+D(6)+D(7)+D(8)+D(9)+D(10)
5125 NEXT K
5127 GOSUB 1900
5130 RETURN
5150 IF DC<22 THEN RETURN
5155 LET AC=0
5160 FOR I=1 TO DCN
5165 IF AC=1 THEN GOTO 5185
5170 IF D(I)=11 THEN LET AC=1
5175 IF D(I)=11 THEN LET D(I)=1
5185 NEXT I
5195 RETURN
6000 FAST
6015 PRINT AT 18,0;" A 2 3 4 5 6 7 8 9 10"
6020 DIM N(13)
6025 FOR J=1 TO 13
6030 FOR I=(J-1)*20+1 TO J*20
6035 LET N(J)=N(J)+C(I)
6040 NEXT I
6045 NEXT J
6047 LET N(10)=N(10)+N(11)+N(12)+N(13)
6050 PRINT AT 19,0;N(1);TAB 3;N(2);TAB 6;N(3);TAB 9;N(4);TAB 12;N(5);TAB 15;N(6);TAB 18;N(7);TAB 21;N(8);TAB 24;N(9);TAB 27;N(10)
6060 LET TR=(80-N(10))/(180-N(1)-N(2)-N(3)-N(4)-N(5)-N(6)-N(7)-N(8)-N(9))
6065 PRINT AT 21,0;"TENS RATIO =";TR
6066 SLOW
6070 RETURN
7000 IF SC<22 THEN GOTO 7020
7010 PRINT AT 21,0;"..OVER..21..SPLIT...BUST......"
7015 GOTO 7080
7020 IF DC=SC THEN PRINT AT 21,0;"........SPLIT..PUSHES..........."
7025 IF DC=SC THEN GOTO 5025
7030 IF DC>21 OR SC>DC THEN PRINT AT 21,0;"..........SPLIT..WINS.........."
7040 IF DC>21 OR SC>DC THEN LET M=M+B
7050 IF DC>21 OR SC>DC THEN GOTO 5025
7070 PRINT AT 21,0;"..........DEALER...WINS........."
7080 LET M=M-B
7085 LET X=1
7087 GOSUB 1900
7090 GOTO 5025
7130 FOR K=1 TO SCN
7140 IF S(K)=11 THEN GOSUB 7250
7150 LET SC=S(1)+S(2)+S(3)+S(4)+S(5)+S(6)+S(7)+S(8)+S(9)+S(10)
7160 NEXT K
7170 GOSUB 1900
7180 RETURN
7250 IF SC<22 THEN RETURN
7255 LET AC=0
7260 FOR I=1 TO SCN
7265 IF AC=1 THEN GOTO 7285
7270 IF S(I)=11 THEN LET AC=1
7275 IF S(I)=11 THEN LET S(I)=1
7285 NEXT I
7290 RETURN
7300 CLEAR
7310 SAVE "1030%1"
7320 RUN
Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.
