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

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.

People

No people associated with this content.

Scroll to Top