--- title: "Four in a Row" id: 72962 type: "computer_media" slug: "four-in-a-row" url: "http://localhost/computer_media/four-in-a-row/" markdown_url: "http://localhost/computer_media/four-in-a-row.md" published_at: "2026-10-27T21:30:07+00:00" modified_at: "2026-10-27T21:30:07+00:00" author: "David Anderson" featured_image: url: "http://localhost/wp-content/uploads/2026/10/connect-1.png" excerpt: "Drop counters into a seven-column grid and try to line up four before your opponent does, playing a friend or a computer opponent that thinks about its moves." post_tag: - name: "Downloadable" slug: "downloadable" taxonomy: "post_tag" url: "http://localhost/tag/downloadable/" - name: "QL" slug: "ql" taxonomy: "post_tag" url: "http://localhost/tag/ql/" model: - name: "Sinclair QL" slug: "sinclair-ql" taxonomy: "model" url: "http://localhost/model/sinclair-ql/" indiv: - name: "David Nowotnik" slug: "david-nowotnik" taxonomy: "indiv" url: "http://localhost/indiv/david-nowotnik/" genre: - name: "Strategy" slug: "strategy" taxonomy: "genre" url: "http://localhost/type/strategy/" media_type: "Diskette" programmers: - name: "David Nowotnik" slug: "david-nowotnik" taxonomy: "indiv" url: "http://localhost/indiv/david-nowotnik/" download_url: "https://archive.org/download/timex-sinclair-software-archive/Four%20in%20a%20Row%20%281984%29%28Nowotnik%2C%20David%29%28Sinclair%20QL%29%28UK%29%28Program%29.zip" mediadate: "1984" images: - url: "http://localhost/wp-content/uploads/2026/10/connect-1.png" - url: "http://localhost/wp-content/uploads/2026/10/connect-2.png" media_type_tags: "Strategy" license: false --- # Four in a Row Four in a Row is the Connect Four game for one or two players. Players take turns to drop red or green counters into a 7-column, 6-row grid by pressing 1 to 7; the first to line up four in a row, column or diagonal wins, and a full board is a stalemate. Against the computer you choose your colour and the QL announces “My move – I’m thinking!” while it searches for a reply. The REMs credit David Nowotnik and date it December 1984. ## Program Analysis The main loop calls `start_up`, then `one_player` or `two_players`. Separate functions test for a win horizontally, vertically and on both diagonals (`horizontal`, `vertical`, `diag1`, `diag2`), and `computer` chooses the computer’s column. ### Notable Techniques - Structured SuperBASIC: `SELect ON`, `DEFine FuNction` and named `REPeat` loops throughout. ### Potential Issues - Line 1020 saves the program back to `mdv1_connect`; the floppy-image copy saves to `flp1_connect`. It came from a QL owner’s floppy copies of QUANTA and IQLUG library material, imaged double-stepped so that only half of each disk survived; this file read back complete. The download holds the files exactly as recovered and a QL5A floppy image, *Four in a Row.img*, with the same files and their QDOS headers. Mount the image as FLP1_ (drop it on the QLRun page, or use it as a floppy image in sQLux, QPC2 or Q-emuLator) and type `lrun flp1_connect_bas`. In the image copies only, `mdv1_connect` reads `flp1_connect` so the program finds its files on the floppy. Tested in QLRun with the JS ROM: the menu and a one-player game with the computer replying. ## Source Code ``` 100 REMark Four in a row 110 REMark by David Nowotnik 120 REMark December, 1984 130 REMark 140 REMark Main routine 180 REPeat try 190 init 200 ss=start_up 210 SELect ON ss 220 =1: one_player 230 =2: two_players 240 END SELect 250 END REPeat try 390 REMark *************************** 400 DEFine PROCedure two_players 420 set_up_board 430 colour(1)=2: colour(2)=4 440 nxt= RND (1 TO 2) 450 PRINT #0," Player ";nxt;" goes first": PAUSE 200 460 REPeat game2 465 IF stalemate THEN EXIT game2 470 p=next_play (nxt) 480 q=drop_counter (nxt,p) 490 IF win_check (p,q) THEN EXIT game2 500 nxt=3-nxt 510 END REPeat game2 520 IF stalemate THEN 530 CLS #0: PRINT #0," Stalemate": STOP 540 ELSE 550 CLS #0: PRINT#0,"Player ";nxt;" has won" 560 winner 580 END IF 680 END DEFine 690 REMark *************************** 700 DEFine PROCedure one_player 710 LOCal p,q 720 set_up_board 730 PRINT #0," Select your colour (r/g)" 740 REPeat choose 750 colour$=INKEY$(-1): IF colour$="r" OR colour$="g" THEN EXIT choose 760 END REPeat choose 770 player$(1)="Your move ": player$(2)="Computer's move" 780 IF colour$="r" THEN 790 colour(1)=2: colour(2)=4 800 ELSE : colour(1)=4: colour(2)=2 810 END IF 820 nxt = RND (1 TO 2) 825 CLS #0: PRINT #0,player$(nxt);" first": PAUSE 200 830 REPeat game1 840 IF stalemate THEN EXIT game1 850 SELect nxt 860 =1: p=next_play (nxt) 870 q=drop_counter (nxt,p) 880 =2: p= computer: q=drop_counter (nxt,p) 890 END SELect 900 CLS #0: IF win_check (p,q) THEN EXIT game1 910 nxt=3-nxt 920 END REPeat game1 930 IF stalemate THEN 935 PRINT #0," Stalemate": PAUSE 200: EXIT one_player 940 ELSE 945 SELect nxt 950 =1: PRINT #0," You win" 955 =2: PRINT #0," I win!!" 960 END SELect 970 winner 975 END IF 980 END DEFine 990 REMark ****************************** 1000 DEFine PROCedure p_save 1020 DELETE mdv1_connect: SAVE mdv1_connect 1030 END DEFine 1040 REMark ***************************** 5000 DEFine PROCedure init 5005 MODE 4 5010 WINDOW #1,512,256,0,0 5020 WINDOW #2,300,180,104,52 5030 PAPER #1,4: PAPER #2,1 5040 INK 2: CLS: INK #2,7 5050 CSIZE#1,2,1 5060 OPEN#4,scr:WINDOW #4,300,30,104,22 5070 PAPER #4,7: INK #4,0: CSIZE #4,2,1 5080 DIM a$(7,6), d$(7,6), c$(7,6), l(4,2), colour(2), mm(7), player$(2,15) 5090 WINDOW #0,300,20,104,234: CSIZE #0,2,1 5100 FOR j=1 TO 7: a$(j)=" ": NEXT j 5180 END DEFine 5190 REMark ***************************** 5200 DEFine PROCedure set_up_board 5210 CLS: CLS #2: CLS #4:INK 2 5230 AT 0,14: PRINT "FOUR IN A ROW" 5240 INK 7: circles 5250 CLS#0 5280 END DEFine 5290 REMark ***************************** 5300 DEFine FuNction start_up 5305 LOCal z,z$ 5310 CLS: AT 2,14: INK 0: PRINT "FOUR IN A ROW" 5320 INK 2: PRINT\" Select:" 5330 PRINT " 1. One Player" 5340 PRINT " 2. Two Players" 5350 INK 0: PRINT " Press '1' or '2'" 5360 REPeat loop 5370 z$=INKEY$(-1) 5380 IF z$="1" OR z$="2" THEN EXIT loop 5390 END REPeat loop 5400 z=z$: RETurn z 5410 END DEFine 5450 REMark **************************** 6000 DEFine PROCedure circles 6010 FOR i=40 TO 110 STEP 11 6020 FOR j=16 TO 76 STEP 11 6030 FILL 1:CIRCLE i,j,5:FILL 0 6040 NEXT j: NEXT i 6060 END DEFine 6080 REMark ****************************** 6500 DEFine PROCedure heading 6510 FOR i=1 TO 7 6520 AT #4,0,i*3: PRINT #4,i 6530 NEXT i 6540 END DEFine 6570 REMark ******************************** 6600 DEFine FuNction drop_counter (col,pos) 6610 LOCal c,pos1,num,ypos 6620 CLS #4: c= colour(col): INK c 6630 pos1=29+pos*11: FILL 1: CIRCLE pos1,86,5 6640 PAUSE 50: CLS #4: num=6: ypos=71 6650 REPeat falling 6660 FILL 1: INK c: CIRCLE pos1,ypos,5: FILL 0 6670 IF num=1 THEN EXIT falling 6680 IF a$(pos,num-1)<>" " THEN EXIT falling 6690 FILL 1: INK 7: CIRCLE pos1,ypos,5: FILL 0 6700 num=num-1: ypos=ypos-11 6710 END REPeat falling 6720 a$(pos,num)=c/2 6730 RETurn num 6880 END DEFine 6990 REMark ***************************** 7000 DEFine FuNction next_play (player) 7010 LOCal c,z,z$ 7020 c= colour (player): INK #0,c: heading 7030 PRINT #0," Select column (1 to 7)" 7035 REPeat check 7040 REPeat wait 7050 z$=INKEY$(-1) 7060 IF z$>"0" AND z$<"8" THEN EXIT wait 7070 END REPeat wait 7074 z=z$: IF a$(z,6)=" " THEN EXIT check 7076 END REPeat check 7080 CLS #0: CLS #4 7090 RETurn z 7100 END DEFine 7150 REMark ************************* 7200 DEFine FuNction stalemate 7210 LOCal i 7220 FOR i=1 TO 7 7230 IF a$(i,6)=" " THEN EXIT i 7240 NEXT i 7250 RETurn 1 7260 END FOR i 7270 RETurn 0 7280 END DEFine 7290 REMark *************************** 7500 DEFine FuNction win_check (p,q) 7510 LOCal x,y: x=p: y=q 7520 IF horizontal (y) THEN RETurn 1 7530 IF vertical (x) THEN RETurn 1 7540 IF diag1 (x,y) THEN RETurn 1 7550 IF diag2 (x,y) THEN RETurn 1 7560 RETurn 0 7570 END DEFine 7590 REMark ****************************** 7600 DEFine FuNction horizontal (hh) 7610 LOCal i,j: j=hh 7615 b$=" ": c=0 7620 FOR i=1 TO 7 7630 IF four_in_row (i,j) THEN RETurn 1 7640 NEXT i 7650 RETurn 0 7660 END DEFine 7690 REMark *************************** 7800 DEFine FuNction four_in_row (i,j) 7820 IF a$(i,j)=" " THEN c=0: b$=" ": RETurn 0 7830 IF a$(i,j)<>b$ THEN 7840 c=1: b$=a$(i,j): l(c,1)=i 7850 l(c,2)=j: RETurn 0 7860 END IF 7870 c=c+1: l(c,1)=i: l(c,2)=j 7880 IF c=4 THEN 7885 RETurn 1 7890 ELSE : RETurn 0 7900 END IF 7910 END DEFine 7950 REMark ******************************* 8000 DEFine FuNction vertical (vert) 8010 LOCal i,j: i=vert: b$=" ":c=0 8020 FOR j=1 TO 6 8030 IF four_in_row (i,j) THEN RETurn 1 8040 NEXT j 8050 RETurn 0 8060 END DEFine 8080 REMark **************************** 8100 DEFine FuNction diag1 (e,f) 8105 LOCal h,v: h=e:v=f:b$=" ": c=0 8110 REPeat left 8120 IF h=1 OR v=1 THEN EXIT left 8130 h=h-1: v=v-1 8140 END REPeat left 8150 REPeat find 8160 IF four_in_row (h,v) THEN RETurn 1 8170 h=h+1: v=v+1 8180 IF h>7 OR v>6 THEN EXIT find 8190 END REPeat find 8200 RETurn 0 8210 END DEFine 8240 REMark ************************* 8400 DEFine FuNction diag2 (e,f) 8405 LOCal h1,v1: h1=e:v1=f:b$=" ":c=0 8410 REPeat right 8420 IF h1=7 OR v1=1 THEN EXIT right 8430 h1=h1+1: v1=v1-1 8440 END REPeat right 8450 REPeat find1 8460 IF four_in_row (h1,v1) THEN RETurn 1 8470 h1=h1-1: v1=v1+1 8480 IF h1<1 OR v1>6 THEN EXIT find1 8490 END REPeat find1 8500 RETurn 0 8510 END DEFine 8550 REMark ************************* 8600 DEFine PROCedure winner 8610 colour (3-nxt)=7: tint=2 8630 FOR i=1 TO 4 8640 l(i,1)=29+l(i,1)*11 8650 l(i,2)=5+l(i,2)*11 8660 NEXT i 8670 REPeat flash4 8680 INK colour (tint) 8690 FOR i=1 TO 4 8700 FILL 1: CIRCLE l(i,1),l(i,2),5: FILL 0 8710 NEXT i 8720 tint=3-tint 8730 IF INKEY$(0)<>"" THEN EXIT flash4 8740 END REPeat flash4 8750 END DEFine 8780 REMark *********************************** 9000 DEFine FuNction computer 9010 LOCal i,j,z: a_to_b 9020 FOR j=1 TO 7: mm(j)=0: NEXT j 9030 CLS #0: PRINT #0," My move - I'm thinking!" 9040 FOR i=1 TO 7 9050 b_to_a: IF a$(i,6)<>" " THEN mm(i)=1: EXIT i 9060 IF can_I_win (i) THEN EXIT i 9070 next_check (i) 9080 can_you_win (i) 9090 END FOR i 9100 b_to_a 9110 FOR j=1 TO 7: IF mm(j)=2 THEN RETurn j: NEXT j 9120 FOR j=1 TO 7 9130 IF mm(j)=2 THEN RETurn j 9140 NEXT j 9150 FOR j=1 TO 7 9160 IF mm(j)=3 THEN RETurn j 9170 NEXT j 9180 FOR i=1 TO 50 9190 z= RND (1 TO 7) 9200 IF mm(z)=0 THEN RETurn z 9210 NEXT i 9220 FOR j=1 TO 7 9230 IF mm(j) <>1 THEN RETurn j 9240 NEXT j 9250 END DEFine 9280 REMark **************************** 9300 DEFine PROCedure a_to_b 9310 LOCal k 9320 FOR k=1 TO 7: d$(k)=a$(k): NEXT k 9330 END DEFine 9340 REMark **************************** 9350 DEFine PROCedure b_to_a 9360 LOCal k 9370 FOR k=1 TO 7: a$(k)=d$(k): NEXT k 9380 END DEFine 9390 REMark *************************** 9400 DEFine PROCedure a_to_c 9410 LOCal k 9420 FOR k=1 TO 7: c$(k)=a$(k): NEXT k 9430 END DEFine 9440 REMark **************************** 9450 DEFine PROCedure c_to_a 9460 LOCal k 9470 FOR k=1 TO 7: a$(k)=c$(k): NEXT k 9480 END DEFine 9490 REMark **************************** 9500 DEFine FuNction drop_one (n,p) 9510 LOCal z 9520 z=6 9530 REPeat skip 9540 IF z=1 THEN a$(p,1)=n: EXIT skip 9550 IF a$(p,z-1)<>" " THEN a$(p,z)=n: EXIT skip 9555 z=z-1 9560 END REPeat skip 9570 RETurn z 9580 END DEFine 9590 REMark **************************** 9600 DEFine FuNction can_I_win (p) 9610 LOCal q: b_to_a 9620 q= drop_one (2,p) 9630 IF win_check (p,q) THEN 9640 mm(p)=2: RETurn 1 9650 ELSE : RETurn 0 9660 END IF 9670 END DEFine 9690 REMark ************************** 9700 DEFine PROCedure can_you_win (p) 9710 LOCal q: b_to_a 9720 q=drop_one (1,p) 9730 IF win_check (p,q) THEN mm(p)=3 9740 END DEFine 9780 REMark **************************** 9800 DEFine PROCedure next_check (p) 9810 LOCal q: a_to_c 9820 q=drop_one (1,p) 9830 IF win_check (p,q) THEN mm(p)=4 9840 c_to_a 9850 END DEFine 9870 REMark ***************************** ```