Blackjack

This file is part of and Timex Sinclair Public Domain Library Tape 1007. Download the collection to get this file.
Date: 198x
Type: Program
Platform(s): TS 1000

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:

  • 45004615: Card drawing from the deck
  • 19002020: Screen display/refresh of all hands
  • 16101650: Split hand initialisation
  • 17001790: Split hand play loop
  • 48504942: Dealer draw and outcome resolution
  • 49504975: Natural Blackjack payout
  • 50355090: Player ace adjustment (11→1)
  • 51005195: Dealer ace adjustment
  • 60006070: Card-counting display (tens ratio)
  • 70007090: Split hand outcome resolution
  • 71307290: 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 (50355090 for player, 51005195 for dealer, 71307290 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 51005125 provides by iterating over all dealer card positions.

Game Features

FeatureKeyLines
HitY525–560
StandN530, 4850
Double DownD523, 565
SplitS540–541, 1610
InsuranceY/N at prompt325–465
Card count displayA524, 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 (17001790) 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 70007090.

Notable Bugs and Anomalies

  • Line 5040 is referenced by GOSUB 5040 at line 543, but the subroutine at that range begins at 5035. Line 5040 itself (IF P(K)=11...) is mid-loop; entering at 5040 skips the FOR K=1 TO PCN statement, meaning only the current value of K (from the last FOR loop in scope) is checked, rather than all player cards. This is a latent bug in the player bust/ace adjustment path.
  • The GOSUB 5040 call at line 543 is followed immediately by IF PC>21 THEN GOTO 5000, but the ace-reduction subroutine may not correctly update PC before this test because PC is recalculated inside 1900 (called from 5085 via 1900), not immediately in the bust path. In practice, the bust detection and ace reduction may interact incorrectly on certain soft hands.
  • The SLOW / FAST keywords at lines 1900 and 6000/6066 indicate this is a ZX81/TS1000 program using display mode switching for screen stability.
  • Line 519 assigns LET XYZ=RND with no apparent use of XYZ elsewhere; this appears to be a discarded variable, possibly left from an earlier version.
  • Insurance payout logic at line 465 deducts 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 (line 410), meaning insurance only costs money and never pays — the win message at line 410 simply prevents a loss of the main bet, but the insurance side bet is silently forfeited in all cases.

Content

Appears On

Assembled by Tim Ward from many sources. Contains programs 10294-10335.

Related Products

Related Articles

Related Content

Image Gallery

Blackjack

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
\n1610 IF P(1)=1 THEN LET P(1)=11
\n1615 LET SPLIT=1
\n1620 LET PCN=1
\n1625 LET S(1)=P(2)
\n1630 LET P(2)=0
\n1635 LET S$(1)=P$(2)
\n1640 GOSUB 1900
\n1645 GOSUB 4500
\n1650 RETURN 
\n1710 GOSUB 4500
\n1715 LET SCN=SCN+1
\n1720 LET S(SCN)=CARD
\n1725 LET S$(SCN)=C$
\n1730 GOSUB 1900
\n1740 PRINT AT 20,0;"SPLIT CARD(Y/N)?    "
\n1750 LET Q$=INKEY$
\n1760 IF Q$="Y" THEN GOTO 1710
\n1765 IF Q$="N" THEN LET X=1
\n1770 IF Q$="N" THEN GOTO 4870
\n1774 IF SC>21 THEN GOSUB 7130
\n1775 IF SC>21 THEN LET X=1
\n1777 IF SC>21 THEN GOTO 4870
\n1780 PRINT AT 20,0;"                 "
\n1790 GOTO 1740
\n1900 SLOW 
\n1910 PRINT AT 0,18;"BET=   ";B
\n1912 PRINT AT 1,18;"MONEY= ";M
\n1913 IF PCN>2 AND X=0 THEN GOTO 1946
\n1915 LET DC=D(1)+D(2)+D(3)+D(4)+D(5)+D(6)+D(7)+D(8)+D(9)+D(10)
\n1918 PRINT AT 2,0;"DEALER HAS:";
\n1919 IF X=1 THEN PRINT "       TOTAL=";DC
\n1920 FOR I=DCN-1 TO (DCN-1+X)
\n1925 PRINT AT 3,I*4+2;"\:'\''\':"
\n1930 PRINT AT 4,I*4+2;"\: ";D$(I);"\ :"
\n1935 PRINT AT 5,I*4+2;"\:.\..\.:"
\n1940 NEXT I
\n1945 IF X=0 THEN PRINT AT 3,10;"\@@\@@\@@";TAB 10;"\@@\@@\@@";TAB 10;"\@@\@@\@@"
\n1946 IF Q$<>"D" AND X=1 THEN RETURN 
\n1947 LET PC=P(1)+P(2)+P(3)+P(4)+P(5)+P(6)+P(7)+P(8)+P(9)+P(10)
\n1950 PRINT AT 8,0;"PLAYER HAS:       TOTAL=";PC;" "
\n1955 FOR I=1 TO PCN
\n1960 PRINT AT 9,I*4+2;"\:'\''\':"
\n1965 PRINT AT 10,I*4+2;"\: ";P$(I);"\ :"
\n1970 PRINT AT 11,I*4+2;"\:.\..\.:" 
\n1975 NEXT I
\n1982 IF PCN=1 THEN PRINT AT 9,10;"   ";TAB 10;"   ";TAB 10;"   "
\n1985 IF SPLIT=0 THEN GOTO 2020
\n1987 LET SC=S(1)+S(2)+S(3)+S(4)+S(5)+S(6)+S(7)+S(8)+S(9)+S(10)
\n1990 PRINT AT 13,0;"PLAYER HAS:       TOTAL=";SC
\n1995 FOR I=1 TO SCN
\n2000 PRINT AT 14,I*4+2;"\:'\''\':"
\n2005 PRINT AT 15,I*4+2;"\: ";S$(I);"\ :"
\n2010 PRINT AT 16,I*4+2;"\:.\..\.:"
\n2015 NEXT I
\n2020 RETURN 
\n4500 IF CN=210 THEN PRINT AT 12,3;"******RESHUFFLE DECK******"
\n4515 IF CN=210 THEN DIM C(260)
\n4520 LET CARD=INT (260*RND)+1
\n4525 IF C(CARD)=1 THEN GOTO 4520
\n4527 LET C(CARD)=1
\n4530 LET CARD=INT (CARD/20)+1
\n4532 IF CARD<1 OR CARD>13 THEN GOTO 4500
\n4535 IF CARD=1 THEN LET C$="A"
\n4540 IF CARD=2 THEN LET C$="2"
\n4545 IF CARD=3 THEN LET C$="3"
\n4550 IF CARD=4 THEN LET C$="4"
\n4555 IF CARD=5 THEN LET C$="5"
\n4560 IF CARD=6 THEN LET C$="6"
\n4565 IF CARD=7 THEN LET C$="7"
\n4570 IF CARD=8 THEN LET C$="8"
\n4575 IF CARD=9 THEN LET C$="9"
\n4580 IF CARD=10 THEN LET C$="T"
\n4585 IF CARD=11 THEN LET C$="J"
\n4590 IF CARD=12 THEN LET C$="Q"
\n4595 IF CARD=13 THEN LET C$="K"
\n4600 IF CARD>10 THEN LET CARD=10
\n4605 IF CARD=1 THEN LET CARD=11
\n4610 LET CN=CN+1
\n4615 RETURN 
\n4800 LET P(DCN)=CARD
\n4805 LET X=1
\n4812 GOSUB 1900
\n4815 PRINT AT 20,0;"DEALER HAS 21 YOU LOSE         "
\n4820 LET M=M-B
\n4825 PAUSE 500
\n4830 GOTO 30
\n4850 GOSUB 1900
\n4895 IF DC<17 THEN GOSUB 4500
\n4897 IF DC>=17 THEN GOTO 4920
\n4900 LET DCN=DCN+1
\n4905 LET D(DCN)=CARD
\n4910 LET D$(DCN)=C$
\n4915 GOTO 4850
\n4920 IF SPLIT=1 AND PC>21 THEN GOTO 5024
\n4921 IF DC=PC THEN PRINT AT 20,0;"...............PUSH............."
\n4922 IF PC=DC THEN GOTO 5024
\n4923 IF DC>21 THEN GOSUB 5100
\n4924 IF DC<17 THEN GOTO 4850
\n4925 IF DC>21 OR PC>DC THEN PRINT AT 20,0;"............YOU.WIN............"
\n4927 IF DC>21 OR PC>DC THEN LET M=M+B
\n4930 IF DC>21 OR PC>DC THEN GOTO 5024
\n4935 PRINT AT 20,0;"..........DEALER...WINS........"
\n4940 LET M=M-B
\n4942 GOTO 5024
\n4950 LET X=1
\n4965 LET M=M+1.5*B
\n4970 PRINT AT 20,0;"YOU WIN...............BLACKJACK"
\n4975 GOTO 5024
\n5000 LET M=M-B
\n5020 PRINT "....OVER 21......BUST.........."
\n5022 IF SPLIT=0 THEN LET X=1
\n5023 IF SPLIT=1 THEN GOTO 1700
\n5024 IF SPLIT=1 THEN GOTO 7000
\n5025 GOSUB 1900
\n5026 IF Q$="D" THEN LET B=B/2
\n5027 PRINT AT 19,0;"INPUT > Y < TO CHANGE THE BET  "
\n5028 PAUSE 300
\n5030 LET B$=INKEY$
\n5032 IF B$="Y" THEN PRINT ">>>  INPUT NEW BET  <<<   "
\n5033 IF B$="Y" THEN INPUT B
\n5034 GOTO 30
\n5035 FOR K=1 TO PCN
\n5042 IF P(K)=11 THEN GOSUB 5061
\n5044 NEXT K
\n5050 RETURN 
\n5061 IF PC<22 THEN GOTO 5090
\n5062 LET AC=0
\n5065 FOR I=1 TO PCN
\n5068 IF AC=1 THEN GOTO 5080
\n5070 IF P(I)=11 THEN LET AC=1
\n5075 IF P(I)=11 THEN LET P(I)=1
\n5080 NEXT I
\n5085 GOSUB 1900
\n5090 RETURN 
\n5100 FOR K=1 TO DCN
\n5120 IF D(K)=11 THEN GOSUB 5150
\n5122 LET DC=D(1)+D(2)+D(3)+D(4)+D(5)+D(6)+D(7)+D(8)+D(9)+D(10)
\n5125 NEXT K
\n5127 GOSUB 1900
\n5130 RETURN 
\n5150 IF DC<22 THEN RETURN 
\n5155 LET AC=0
\n5160 FOR I=1 TO DCN
\n5165 IF AC=1 THEN GOTO 5185
\n5170 IF D(I)=11 THEN LET AC=1
\n5175 IF D(I)=11 THEN LET D(I)=1
\n5185 NEXT I
\n5195 RETURN 
\n6000 FAST 
\n6015 PRINT AT 18,0;" A  2  3  4  5  6  7  8  9  10"
\n6020 DIM N(13)
\n6025 FOR J=1 TO 13
\n6030 FOR I=(J-1)*20+1 TO J*20
\n6035 LET N(J)=N(J)+C(I)
\n6040 NEXT I
\n6045 NEXT J
\n6047 LET N(10)=N(10)+N(11)+N(12)+N(13)
\n6050 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)
\n6060 LET TR=(80-N(10))/(180-N(1)-N(2)-N(3)-N(4)-N(5)-N(6)-N(7)-N(8)-N(9))
\n6065 PRINT AT 21,0;"TENS RATIO =";TR
\n6066 SLOW 
\n6070 RETURN 
\n7000 IF SC<22 THEN GOTO 7020
\n7010 PRINT AT 21,0;"..OVER..21..SPLIT...BUST......"
\n7015 GOTO 7080
\n7020 IF DC=SC THEN PRINT AT 21,0;"........SPLIT..PUSHES..........."
\n7025 IF DC=SC THEN GOTO 5025
\n7030 IF DC>21 OR SC>DC THEN PRINT AT 21,0;"..........SPLIT..WINS.........."
\n7040 IF DC>21 OR SC>DC THEN LET M=M+B
\n7050 IF DC>21 OR SC>DC THEN GOTO 5025
\n7070 PRINT AT 21,0;"..........DEALER...WINS........."
\n7080 LET M=M-B
\n7085 LET X=1
\n7087 GOSUB 1900
\n7090 GOTO 5025
\n7130 FOR K=1 TO SCN
\n7140 IF S(K)=11 THEN GOSUB 7250
\n7150 LET SC=S(1)+S(2)+S(3)+S(4)+S(5)+S(6)+S(7)+S(8)+S(9)+S(10)
\n7160 NEXT K
\n7170 GOSUB 1900
\n7180 RETURN 
\n7250 IF SC<22 THEN RETURN 
\n7255 LET AC=0
\n7260 FOR I=1 TO SCN
\n7265 IF AC=1 THEN GOTO 7285
\n7270 IF S(I)=11 THEN LET AC=1
\n7275 IF S(I)=11 THEN LET S(I)=1
\n7285 NEXT I
\n7290 RETURN 
\n7300 CLEAR 
\n7310 SAVE "1030%1"
\n7320 RUN 

Note: Type-in program listings on this website use ZMAKEBAS notation for graphics characters.

People

No people associated with this content.

Scroll to Top