REM 'DOTS' Copr. 1/12/1992 by Mickey Burnette REM Program for demonstration purposes REM and intended as a teaching aid. Program shows structure and advance REM features of QBASIC / DOS 5. REM REM Unless otherwise needed, all variables are integers DEFINT A-Z REM By declaring functions and subprograms, QBASIC will type-match parameters DECLARE FUNCTION EOG% () DECLARE SUB TELLABOUT () DECLARE SUB COMPTURN (YCORD%, XCORD%, SIDE$) DECLARE SUB WHOSBLOCK (YCORD%, XCORD%, SIDE$, TURN%, FLAG%) DECLARE SUB DISPLAY (YCORD%, XCORD%, SIDE$) DECLARE SUB UPDATE (YCORD%, XCORD%, SIDE$) DECLARE SUB HIGHLIGHT (YCORD%, XCORD%, WHICH%) DECLARE SUB REFRESH (YCORD%, XCORD%) DECLARE FUNCTION VALIDATE% (YCORD%, XCORD%, SIDE$) DECLARE SUB DRAWBD () DECLARE SUB GETREQ (Y%, X%, S$) REM Global constants are defined here ... CONST Hbar = "ÄÄÄ" CONST Vbar = "³" CONST TRUE = -1 CONST FALSE = 0 REM Arrays and fixed length strings are dimensioned... DIM SHARED SIDE AS STRING * 1 DIM SHARED matrix(0 TO 63) AS SINGLE DIM SHARED Score(0 TO 1) AS SINGLE DIM SHARED ColBoard(5 TO 19, 0 TO 1) AS INTEGER DIM SHARED RowBoard(13 TO 43, 0 TO 1) AS INTEGER REM Opening screen. CALL TELLABOUT REM The beginning 'main' routine starts below... main: REM Initialize B/W display... Of course colour could be defined here... COLOR 7, 0 CLS REM Draw the game board by calling the subroutine... CALL DRAWBD REM A call to randomize timer is necessary to initialize the random numbers RANDOMIZE TIMER REM Kinda unfair for anyone to always start first, choose randomly REM Player's turn when Turn=true, Computer' otherwise IF RND(1) > .5 THEN TURN = FALSE ELSE TURN = TRUE END IF REM If you make a mistake or try to cheat, you wind up back here! ... REDO: REM Your turn or mine? ... IF TURN THEN CALL GETREQ(YCORD, XCORD, SIDE) ELSE CALL COMPTURN(YCORD, XCORD, SIDE) REM Show what the computer did since it happens kinda fast ... LOCATE 24, 4: PRINT "Computer Moved To: Column:"; XCORD; " Row:"; YCORD; " Side: "; SIDE; END IF REM if the move is not valid, you must try again... no cheating! REM make a little fuss with the beeps so that everyone knows... IF NOT VALIDATE(YCORD, XCORD, SIDE) THEN BEEP BEEP GOTO REDO END IF REM A call to update to mark the array elements... CALL UPDATE(YCORD, XCORD, SIDE) REM Update the video display to show correct side... CALL DISPLAY(YCORD, XCORD, SIDE) REM If the block is closed, take the block... CALL WHOSBLOCK(YCORD, XCORD, SIDE, TURN, FLAG) REM If you close a block, then you get a bonus turn, computer too! REM Otherwise, the next player takes a turn... IF NOT FLAG THEN IF TURN THEN TURN = FALSE ELSE TURN = TRUE END IF END IF REM If we are taking a bonus move, show it on the board. REM And a little sound might be nice too... IF FLAG THEN COLOR 0, 7 LOCATE 18, 58 PRINT "BONUS MOVE !"; COLOR 7, 0 SOUND 500, 3: SOUND 1000, 5: SOUND 1500, 7 LOCATE 24, 4: PRINT SPACE$(60); ELSE LOCATE 18, 58 PRINT " "; END IF REM End-Of-Game is a function which must be checked. REM If not the end, lets continue. Otherwise make a little noise... IF NOT EOG THEN GOTO REDO ELSE LOCATE 18, 58 IF Score(0) > Score(1) THEN PRINT "** YOU WIN **"; ELSE PRINT "** I WIN ! **"; END IF FOR i = 440 TO 1000 STEP 5 SOUND i, i / 1000 NEXT FOR i = 1000 TO 440 STEP -5 SOUND i, i / 1000 NEXT END IF REM Be polite and ask if another game is desired. REM If so, reset the score and array LOCATE 24, 1: PRINT SPACE$(40); LOCATE 24, 10: INPUT "Do you want to play again (Y/N)"; REPLAY$ IF LEFT$(REPLAY$, 1) = "Y" OR LEFT$(REPLAY$, 1) = "y" THEN FOR i = 0 TO 63 matrix(i) = 0 NEXT Score(0) = 0 Score(1) = 0 GOTO main END IF REM A little grand-standing by the author... CLS LOCATE 10, 10 PRINT "D.O.T.S. by Mickey R. Burnette" REM That's all folks! END SUB COMPTURN (YCORD, XCORD, SIDE$) REM Computer move strategy... greatly simplified to allow the player REM To win. Modify this routine to do advance play weighting if you REM really want to make the game tough. REM The array matrix contains elements 0 to 63 REM defined as a square matrix of 8 X 8 elements SHARED matrix AS SINGLE REM A few flags to use... Found = FALSE FoundWhere = -1 REM Sound please... SOUND 3000, 3 REM dimension 0 of minimum is a value, dimension 1 is the pointer to MATRIX DIM Minimum(0 TO 1) REM If all four sides are complete, the value is 15 REM Sides are weighted as A=2^0, B=2^1, C=2^2, D=2^3 Minimum(0) = 15 Minimum(1) = -1 REM scan array matrix looking for a side to fill in... REM if no block can be made, choose in such a way as not to help REM the player's position! Otherwise, choose randomly for confusion REM look for the magic values... 7,11,13,14 first FOR Itemp = 0 TO 63 Temp = matrix(Itemp) IF Temp < Minimum(0) THEN Minimum(0) = Temp Minimum(1) = Itemp END IF IF Temp = 7 OR Temp = 11 OR Temp = 13 OR Temp = 14 THEN Found = TRUE FoundWhere = Itemp END IF NEXT Itemp REM Sorry guys and gals, but if I find a cell to complete, I will! REM So be careful... IF Found THEN YCORD = INT(FoundWhere / 8) + 1 XCORD = FoundWhere MOD 8 + 1 SELECT CASE matrix(FoundWhere) CASE 7 SIDE$ = "D" CASE 11 SIDE$ = "C" CASE 13 SIDE$ = "B" CASE 14 SIDE$ = "A" END SELECT END IF REM If I can't complete a box, pick the least of the evils, that is REM go where no one else has ever gone! REM This is where advance algorithms can be used to weigh various REM moves and do a little look-ahead... IF NOT Found AND Minimum(1) > -1 THEN YCORD = INT(Minimum(1) / 8) + 1 XCORD = Minimum(1) MOD 8 + 1 REM Normally I would look all around to adjacent cells before REM making a decision. For example, this routine only picks REM the last available side even when more than one side is REM available. It should evaluate all cells around and choose REM the lease of the evils. FOR Ip = 0 TO 3 IF 2 ^ Ip + Minimum(0) < 15 THEN SIDE$ = CHR$(Ip + 65) Found = TRUE END IF NEXT Ip END IF REM Just pick something. This is the fail-safe... IF NOT Found THEN Test = RND(1) SELECT CASE Test CASE IS < .25 FOR Itemp = 0 TO 15 IF matrix(Itemp) < 15 THEN FoundWhere = Itemp EXIT FOR END IF NEXT Itemp CASE .25 TO .5 FOR Itemp = 31 TO 16 STEP -1 IF matrix(Itemp) < 15 THEN FoundWhere = Itemp EXIT FOR END IF NEXT Itemp CASE .5 TO .75 FOR Itemp = 32 TO 47 IF matrix(Itemp) < 15 THEN FoundWhere = Itemp EXIT FOR END IF NEXT Itemp CASE IS > .75 FOR Itemp = 63 TO 48 STEP -1 IF matrix(Itemp) < 15 THEN FoundWhere = Itemp EXIT FOR END IF NEXT Itemp END SELECT REM Translate to coordinates... XCORD = FoundWhere MOD 8 + 1 YCORD = INT(FoundWhere / 8) + 1 Ival = matrix(FoundWhere) REM There are better algorithms, but with 5 MIPS of REM computer power, who cares! IF INT(Ival / 8) < 1 THEN SIDE$ = "D": GOTO Bye Ival = Ival - 8 IF INT(Ival / 4) < 1 THEN SIDE$ = "C": GOTO Bye Ival = Ival - 4 IF INT(Ival / 2) < 1 THEN SIDE$ = "B": GOTO Bye SIDE$ = "A" END IF Bye: REM Just show the computer's selection for column... WHICH = 1 CALL HIGHLIGHT(YCORD, XCORD, WHICH) REM Lets mark time so this looks like it took a while to REM decide! Fake-out for sec or so... j = VAL(MID$(TIME$, 7, 2)) j = j + 1 IF j >= 60 THEN j = 1 DO WHILE VAL(MID$(TIME$, 7, 2)) < j: LOOP REM Just show the computer's selection for row... WHICH = 2 CALL HIGHLIGHT(YCORD, XCORD, WHICH) REM Lets mark time so this looks like it took a while to REM decide! Fake-out for a sec or so... j = VAL(MID$(TIME$, 7, 2)) j = j + 1 IF j >= 60 THEN j = 1 DO WHILE VAL(MID$(TIME$, 7, 2)) < j: LOOP REM Reset the display back to normal... CALL REFRESH(YCORD, XCORD) END SUB SUB DISPLAY (YCORD, XCORD, SIDE$) REM This routine will calculate the real screen coordinates and REM will place the side bar in the correct location. REM The Array matrix contains a value from 0 to 15... SHARED matrix AS SINGLE REM this is just simple math. Calculate based upon a square matrix... X = XCORD - 1 Y = (YCORD - 1) * 8 cell = X + Y REM Now that we have the matrix element identified, add the screen REM offsets to map to the physical display of 80 X 24 Xoff = 14 Yoff = 5 Xreal = (XCORD - 1) * 4 + Xoff Yreal = (YCORD - 1) * 2 + 5 REM Mark the appropriate side of the cube... IF SIDE$ = "A" THEN LOCATE Yreal, Xreal - 2 PRINT Vbar; END IF IF SIDE$ = "B" THEN LOCATE Yreal - 1, Xreal - 1 PRINT Hbar; END IF IF SIDE$ = "C" THEN LOCATE Yreal, Xreal + 2 PRINT Vbar; END IF IF SIDE$ = "D" THEN LOCATE Yreal + 1, Xreal - 1 PRINT Hbar; END IF END SUB SUB DRAWBD REM The routines don't get any easier than this! Just loop through REM the array and display the elements... REM The use of redim here is necessary since this routine may be REM called a second time for another game... REDIM BD(24) AS STRING REM The game board is just an array to be displayed... BD(1) = " D.O.T.S. by Mickey " BD(2) = " Column: 1 2 3 4 5 6 7 8 " BD(3) = " ÉÍÍÍÍÍÍËÍÍÍÍÍÍ» " BD(4) = " Ú Â Â Â Â Â Â Â ¿ º You º Me º " BD(5) = " Row: 1 ú ú ú ú ú ú ú ú ÌÍÍÍÍÍÍÎÍÍÍÍÍ͹ " BD(6) = " à ŠŠŠŠŠŠŠ´ º º º " BD(7) = " 2 ú ú ú ú ú ú ú ú ÈÍÍÍÍÍÍÊÍÍÍÍÍͼ " BD(8) = " à ŠŠŠŠŠŠŠ´ " BD(9) = " 3 ú ú ú ú ú ú ú ú " BD(10) = " à ŠŠŠŠŠŠŠ´ Side Selections: " BD(11) = " 4 ú ú ú ú ú ú ú ú " BD(12) = " à ŠŠŠŠŠŠŠ´ ÚÄÄÄÄÄÄÄÄ¿ " BD(13) = " 5 ú ú ú ú ú ú ú ú ³ B ³ " BD(14) = " à ŠŠŠŠŠŠŠ´ ³A C ³ " BD(15) = " 6 ú ú ú ú ú ú ú ú ³ D ³ " BD(16) = " à ŠŠŠŠŠŠŠ´ ÀÄÄÄÄÄÄÄÄÙ " BD(17) = " 7 ú ú ú ú ú ú ú ú " BD(18) = " à ŠŠŠŠŠŠŠ´ " BD(19) = " 8 ú ú ú ú ú ú ú ú " BD(20) = " À Á Á Á Á Á Á Á Ù Column: Row: " BD(21) = "" BD(22) = " Side: " BD(23) = "" BD(24) = "" CLS FOR i = 1 TO 24 LOCATE i, 1 PRINT BD(i); NEXT i END SUB FUNCTION EOG REM function End-Of-Game! REM Nothing fancy... just loop and check! SHARED matrix AS SINGLE EOG = TRUE FOR i = 0 TO 63 IF matrix(i) < 15 THEN EOG = FALSE END IF NEXT i END FUNCTION SUB GETREQ (YCORD, XCORD, SIDE$) REM This is the user input routine. It could be enhanced to handle REM a better user interface; for example, the use of ESC to clear REM all entries and restart. Something to play with... XBR: LOCATE 20, 63 PRINT " "; LOCATE 20, 63 INPUT ; X$ XCORD = VAL(X$) IF XCORD < 1 OR XCORD > 8 THEN BEEP: BEEP GOTO XBR END IF WHICH = 1 CALL HIGHLIGHT(YCORD, XCORD, WHICH) YBR: LOCATE 20, 72 PRINT " "; LOCATE 20, 72 INPUT ; Y$ YCORD = VAL(Y$) IF YCORD < 1 OR YCORD > 8 THEN BEEP: BEEP GOTO YBR END IF WHICH = 2 CALL HIGHLIGHT(YCORD, XCORD, WHICH) SBR: LOCATE 22, 63 PRINT " "; LOCATE 22, 63 INPUT ; SIDE$ SIDE$ = UCASE$(RIGHT$(CHR$(0) + SIDE$, 1)) IF ASC(SIDE$) < 65 OR ASC(SIDE$) > 68 THEN BEEP: BEEP GOTO SBR END IF LOCATE 20, 63 PRINT " "; LOCATE 20, 72 PRINT " "; LOCATE 22, 63 PRINT " "; CALL REFRESH(YCORD, XCORD) END SUB SUB HIGHLIGHT (YCORD, XCORD, WHICH) REM Highlight (inverse colors) of the selected column or row REM to assist with the visual stuff... REM arrays for old board configuration... REM these will be used later to put things back! SHARED ColBoard AS INTEGER SHARED RowBoard AS INTEGER REM 'real' means a conversion to the standard 80x24 coordinates... Xreal = (XCORD - 1) * 4 + 14 Yreal = (YCORD - 1) * 2 + 5 COLOR 0, 7: REM inverse colours SELECT CASE WHICH CASE 1 REM case 1 is for the column... REM save current data in column and current colour FOR Itemp = 5 TO 19 ColBoard(Itemp, 0) = SCREEN(Itemp, Xreal, 0) ColBoard(Itemp, 1) = SCREEN(Itemp, Xreal, -1) LOCATE Itemp, Xreal PRINT CHR$(ColBoard(Itemp, 0)); NEXT Itemp CASE 2 REM so case 2 is for the row... REM save current data in row and current colour FOR Itemp = 13 TO 43 RowBoard(Itemp, 0) = SCREEN(Yreal, Itemp, 0) RowBoard(Itemp, 1) = SCREEN(Yreal, Itemp, -1) LOCATE Yreal, Itemp PRINT CHR$(RowBoard(Itemp, 0)); NEXT Itemp END SELECT COLOR 7, 0: REM reset colour to normal display END SUB SUB REFRESH (YCORD, XCORD) REM This is the reverse of the 'highlight' routine. REM Here we will restore the board's normal colour... REM arrays for old board configuration... SHARED ColBoard AS INTEGER SHARED RowBoard AS INTEGER REM 'real' refers to the 80x24 text screen coordinates... Xreal = (XCORD - 1) * 4 + 14 Yreal = (YCORD - 1) * 2 + 5 REM just loop and put things back the way we found them... FOR Itemp = 5 TO 19 LOCATE Itemp, Xreal PRINT CHR$(ColBoard(Itemp, 0)); NEXT Itemp FOR Itemp = 13 TO 43 LOCATE Yreal, Itemp PRINT CHR$(RowBoard(Itemp, 0)); NEXT Itemp END SUB SUB TELLABOUT CLS PRINT PRINT " DOTS...." PRINT PRINT " DOTS is a simple game where you play against the computer by selecting sides" PRINT " in an 8 by 8 matrix. Sides are labeled A, B, C, and D. Columns and Rows" PRINT " are labeled 1 through 8. If you close a block, you gain a point and you get" PRINT " another turn. Oh, the computer plays by the same rules!" PRINT PRINT " Sounds simple? Surely it is! But, be careful. The computer is programmed" PRINT " to give you a few breaks, but will increasingly play at more difficult levels" PRINT " as the games continues." PRINT PRINT PRINT " You may end the game at any time by pressing CTRL+BREAK." PRINT PRINT PRINT " Good luck! Press any key to begin..." DO WHILE INKEY$ = "": LOOP END SUB SUB UPDATE (YCORD, XCORD, SIDE$) REM This routine maps a colume, row, and side selection to a value REM which is stored in the array matrix. Values range from 0 to 15. REM REMEMBER that adjacent blocks must receive a value too! REM 0 1 2 3 4 5 6 7 REM 8 9 10 11 12 13 14 15 REM 16 17 18 19 20 21 22 23 B REM 24 25 26 27 28 29 30 31 |-----| REM 32 33 34 35 36 37 38 39 A | | C REM 40 41 42 43 44 45 46 47 |-----| REM 48 49 50 51 52 53 54 55 D REM 56 57 58 59 60 61 62 63 REM REM A=2^0, B=2^1, C=2^2, D=2^3 REM REM When matrix element 36 side A is incremented by 1, then REM matrix element 35 side C must be incremented by 4! SHARED matrix AS SINGLE X = XCORD - 1 Y = (YCORD - 1) * 8 cell = X + Y Ival = matrix(cell) REM First handle the primary cell, then check for secondary cell REM ie: A<-->C, B<-->D interexchange IF SIDE$ = "A" THEN matrix(cell) = Ival + 1 IF cell MOD 8 > 0 THEN cell = cell - 1 matrix(cell) = matrix(cell) + 4 END IF END IF IF SIDE$ = "B" THEN matrix(cell) = Ival + 2 IF INT(cell / 8) > 0 THEN cell = cell - 8 matrix(cell) = matrix(cell) + 8 END IF END IF IF SIDE$ = "C" THEN matrix(cell) = Ival + 4 IF cell MOD 8 < 7 THEN cell = cell + 1 matrix(cell) = matrix(cell) + 1 END IF END IF IF SIDE$ = "D" THEN matrix(cell) = Ival + 8 IF INT(cell / 8) < 7 THEN cell = cell + 8 matrix(cell) = matrix(cell) + 2 END IF END IF END SUB FUNCTION VALIDATE (YCORD, XCORD, SIDE$) REM This is a function. If will evaluate to True or False! REM Some players might think about cheating! Others, maybe REM an honest mistake. Don't care... just handle it. REM Check to ensure that requested side is vacant SHARED matrix AS SINGLE REM Locate the matrix element from the user's input... Y = (YCORD - 1) * 8 X = XCORD - 1 cell = X + Y REM Take a unique approach to this problem. Using the value of the REM matrix element, convert it to a string of ABCD. There will then REM be 16 possible values expressed as a four character string. Now REM it is fairly simple to evaluate the user request... VALIDATE = -1 CHECK$ = " " Ival = matrix(cell) IF INT(Ival / 8) > 0 THEN MID$(CHECK$, 4, 1) = "D" Ival = Ival - 8 END IF IF INT(Ival / 4) > 0 THEN MID$(CHECK$, 3, 1) = "C" Ival = Ival - 4 END IF IF INT(Ival / 2) > 0 THEN MID$(CHECK$, 2, 1) = "B" Ival = Ival - 2 END IF IF Ival > 0 THEN MID$(CHECK$, 1, 1) = "A" END IF REM the next few lines evaluate the user request. Another way to REM do this would simply be by use of the instr$() function. This REM single function would replace all the lines in the select case! REM I did it this way just because... SELECT CASE SIDE$ CASE "A" IF MID$(CHECK$, 1, 1) = "A" THEN VALIDATE = 0 CASE "B" IF MID$(CHECK$, 2, 1) = "B" THEN VALIDATE = 0 CASE "C" IF MID$(CHECK$, 3, 1) = "C" THEN VALIDATE = 0 CASE "D" IF MID$(CHECK$, 4, 1) = "D" THEN VALIDATE = 0 END SELECT END FUNCTION SUB WHOSBLOCK (YCORD, XCORD, SIDE$, TURN, FLAG) REM If block is closed, then increment score for correct player and REM Also put a "P" or "C" in the block for identity! SHARED matrix AS SINGLE SHARED Score AS SINGLE DIM WHO AS STRING * 1 'Set flag is score... reset it for now... FLAG = FALSE REM Who is on first? IF TURN THEN WHO = "P" ELSE WHO = "C" END IF REM Conversion to the matrix linear element X = XCORD - 1 Y = (YCORD - 1) * 8 cell = X + Y REM Offsets are for the display screen positioning... Xoff = 14 Yoff = 5 REM where in the 80x24 are we anyway? Xreal = (XCORD - 1) * 4 + Xoff Yreal = (YCORD - 1) * 2 + 5 REM Remember 2^0 + 2^1 + 2^2 + 2^3 = 15 REM Mark it like a good little doggie.. IF matrix(cell) = 15 THEN LOCATE Yreal, Xreal PRINT WHO; FLAG = TRUE IF WHO = "P" THEN Score(0) = Score(0) + 1 ELSE Score(1) = Score(1) + 1 END IF LOCATE 6, 59: PRINT Score(0); LOCATE 6, 66: PRINT Score(1); END IF END SUB