--- title: "Blackjack" id: 56811 type: "computer_media" slug: "one-armed-bandit-2" url: "http://localhost/computer_media/one-armed-bandit-2/" markdown_url: "http://localhost/computer_media/one-armed-bandit-2.md" published_at: "2024-09-29T02:26:05+00:00" modified_at: "2026-04-03T07:58:51+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2024/09/301_BJ.png" excerpt: "A full Blackjack game with betting, insurance, splits, doubles, and a built-in card-counting tool that calculates the live tens ratio from the remaining deck." category: - name: "Archived Media" slug: "archived-media" taxonomy: "category" url: "http://localhost/category/archived-media/" post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "TS 1000" slug: "ts1000" taxonomy: "post_tag" url: "http://localhost/tag/ts1000/" model: - name: "Timex/Sinclair 1000" slug: "ts-1000" taxonomy: "model" url: "http://localhost/model/ts-1000/" genre: - name: "Card Game" slug: "card-game" taxonomy: "genre" url: "http://localhost/type/card-game/" - name: "Game" slug: "game" taxonomy: "genre" url: "http://localhost/type/game/" media_contents: - id: 56738 title: "Timex Sinclair Public Domain Library Tape 1007" type: "computer_media" url: "http://localhost/computer_media/timex-sinclair-public-domain-library-tape-1007/" media_type: "Program" mediadate: "198x" images: - url: "http://localhost/wp-content/uploads/2024/09/301_BJ.png" media_type_tags: "Card Game, Game" --- 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 deck - `1900`–`2020`: Screen display/refresh of all hands - `1610`–`1650`: Split hand initialisation - `1700`–`1790`: Split hand play loop - `4850`–`4942`: Dealer draw and outcome resolution - `4950`–`4975`: Natural Blackjack payout - `5035`–`5090`: Player ace adjustment (11→1) - `5100`–`5195`: Dealer ace adjustment - `6000`–`6070`: Card-counting display (tens ratio) - `7000`–`7090`: Split hand outcome resolution - `7130`–`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 `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. ## 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 ```