How To Write A Chess Programm in QBASIC
By Dean Menezes

Since the 1950's, people have been trying to make progrms and machines that play chess. This tutorial 
tries to teach chess programming by using a commented QuickBASIC program:

'This is basically a chess program I wrote
'It includes comments to show some of the theory on making a chess program
'You can modify it to include things like opening book, mouse/graphics, etc.
DEFINT A-Z 'to speed things up
' First, you need an 8 by 8 array for the board:
DIM SHARED BOARD(0 TO 7, 0 TO 7)
' Then, you need to store a list of best moves:
DIM SHARED BESTA(0 TO 7), BESTB(0 TO 7), BESTX(0 TO 7), BESTY(0 TO 7)
' Since the evaluation subroutine is recursive, you also need to store current level and maximum level
DIM SHARED LEVEL, MAXLEVEL, SCORE, CFLAG
CFLAG = 0
LEVEL = 0
MAXLEVEL = 5
' Now we need to fill the starting position into the board array
DATA -500,-270,-300,-900,-7500,-300,-270,-500
DATA -100,-100,-100,-100, -100,-100,-100,-100
DATA 0, 0, 0, 0, 0, 0, 0, 0
DATA 0, 0, 0, 0, 0, 0, 0, 0
DATA 0, 0, 0, 0, 0, 0, 0, 0
DATA 0, 0, 0, 0, 0, 0, 0, 0
DATA 100, 100, 100, 100, 100, 100, 100, 100
DATA 500, 270, 300, 900, 5000, 300, 270, 500
FOR X = 0 TO 7
 FOR Y = 0 TO 7
   READ Z
   BOARD(X, Y) = Z
 NEXT Y
NEXT X
A = -1
RESULT = 0
' Now we make the main loop:
DO
 SCORE = 0
 CALL IO(A, B, X, Y, RESULT) ' Get white's move
 CLS
 CALL SHOWBD ' Update board to show white's move
 RESULT = EVALUATE(-1, 10000) 'Get black's move
 A = BESTA(1) 'start column for black's move
 B = BESTB(1) 'start row for black's move
 X = BESTX(1) 'end column for black's move
 Y = BESTY(1) 'end rom for black's move
LOOP
' Now we're going to look at the evaluation function
DEFINT A-Z
FUNCTION EVALUATE (ID, PRUNE)
 DIM XX(0 TO 26), YY(0 TO 26)
 LEVEL = LEVEL + 1 ' Update recursion level
 BESTSCORE = 10000 * ID
 FOR B = 7 TO 0 STEP -1 ' Loop through each square
   FOR A = 7 TO 0 STEP -1
     IF SGN(BOARD(B, A)) <> ID THEN GOTO 1 ' If the square does not have right color piece go to next square
     IF (LEVEL = 1) THEN CALL SHOWMAN(A, B, 8) ' This is just to show the move currently trying
     CALL MOVELIST(A, B, XX(), YY(), NDX) ' Get list of moves for current piece
     FOR I = 0 TO NDX ' Loop through each possible move
       X = XX(I)
       Y = YY(I)
       IF LEVEL = 1 THEN
         LOCATE 1, 1
         PRINT "TRYING: "; CHR$(65 + A); 8 - B; "-"; CHR$(65 + X); 8 - Y ' show the move currently trying
         CALL SHOWMAN(X, Y, 8)
       END IF
       OLDSCORE = SCORE
       MOVER = BOARD(B, A) ' Store these locations
       TARGET = BOARD(Y, X) ' so we can set the move back
       CALL MAKEMOVE(A, B, X, Y) ' Make the move so we can evaluate
       IF (LEVEL < MAXLEVEL) THEN SCORE = SCORE + EVALUATE(-ID, BESTSCORE - TARGET + ID * (8 - ABS(4 - X) - ABS(4 - Y)))
       SCORE = SCORE + TARGET - ID * (8 - ABS(4 - X) - ABS(4 - Y)) 'work out score for move
       IF (ID < 0 AND SCORE > BESTSCORE) OR (ID > 0 AND SCORE < BESTSCORE) THEN 'update current best score
         BESTA(LEVEL) = A
         BESTB(LEVEL) = B
         BESTX(LEVEL) = X
         BESTY(LEVEL) = Y
         BESTSCORE = SCORE
         IF (ID < 0 AND BESTSCORE >= PRUNE) OR (ID > 0 AND BESTSCORE <= PRUNE) THEN 'prune to avoid wasting time
           BOARD(B, A) = MOVER 'reset position back to before modified
           BOARD(Y, X) = TARGET
           SCORE = OLDSCORE
           IF (LEVEL = 1) THEN CALL SHOWMAN(X, Y, 0) 'Show the move currently trying
           IF (LEVEL = 1) THEN CALL SHOWMAN(A, B, 0) 'Show the move currently trying
           LEVEL = LEVEL - 1 'update recursion level
           EVALUATE = BESTSCORE 'return
           EXIT FUNCTION
         END IF
       END IF
       BOARD(B, A) = MOVER 'reset position back to before modified
       BOARD(Y, X) = TARGET
       SCORE = OLDSCORE
       IF (LEVEL = 1) THEN CALL SHOWMAN(X, Y, 0) 'show move currently trying
1   NEXT
     IF (LEVEL = 1) THEN CALL SHOWMAN(A, B, 0) show move currently trying
  NEXT
   NEXT
   LEVEL = LEVEL - 1 'update recursion level
   EVALUATE = BESTSCORE 'return
 END FUNCTION
'This generates a list of moves
DEFINT A-Z
SUB MOVELIST (A, B, XX(), YY(), NDX)
 PIECE = INT(ABS(BOARD(B, A))) 'get value corresponding to piece
 NDX = -1
 IF PIECE = 100 THEN
   CALL PAWN(A, B, XX(), YY(), NDX) 'call proper move listing routine depending on piece
 ELSEIF PIECE = 270 THEN CALL KNIGHT(A, B, XX(), YY(), NDX)
 ELSEIF PIECE = 300 THEN CALL BISHOP(A, B, XX(), YY(), NDX)
 ELSEIF PIECE = 500 THEN CALL ROOK(A, B, XX(), YY(), NDX)
 ELSEIF PIECE = 900 THEN CALL QUEEN(A, B, XX(), YY(), NDX)
 ELSE CALL KING(A, B, XX(), YY(), NDX)
 END IF
END SUB
' This is the move-list generator for the pawn
DEFINT A-Z
SUB PAWN (A, B, XX(), YY(), NDX)
 ID = SGN(BOARD(B, A)) 'get color
 IF (A - 1) >= 0 AND (A - 1) <= 7 AND (B - ID) >= 0 AND (B - ID) <= 7 THEN 'capture
   IF SGN(BOARD((B - ID), (A - 1))) = -ID THEN 'make sure there is piece to capture
     NDX = NDX + 1
     XX(NDX) = A - 1
     YY(NDX) = B - ID
   END IF
 END IF
 IF (A + 1) >= 0 AND (A + 1) <= 7 AND (B - ID) >= 0 AND (B - ID) <= 7 THEN  'capture
   IF SGN(BOARD((B - ID), (A + 1))) = -ID THEN 'make sure there is piece to capture
     NDX = NDX + 1
     XX(NDX) = A + 1
     YY(NDX) = B - ID
   END IF
 END IF
 IF A >= 0 AND A <= 7 AND (B - ID) >= 0 AND (B - ID) <= 7 THEN 'one square forward
   IF BOARD((B - ID), A) = 0 THEN 'make sure square is empty
     NDX = NDX + 1
     XX(NDX) = A
     YY(NDX) = B - ID
     IF (ID < 0 AND B = 1) OR (ID > 0 AND B = 6) THEN '2 squares forward
       IF BOARD((B - ID - ID), A) = 0 THEN  'make sure square is empty
         NDX = NDX + 1
         XX(NDX) = A
         YY(NDX) = B - 2 * ID
       END IF
     END IF
   END IF
 END IF
END SUB
' Move list generator for knight
DEFINT A-Z
SUB KNIGHT (A, B, XX(), YY(), NDX)
 ID = SGN(BOARD(B, A)) 'get color
 X = A - 1 'work out each of the knight's eight moves
 Y = B - 2
 GOSUB 5
 X = A - 2
 Y = B - 1
 GOSUB 5
 X = A + 1
 Y = B - 2
 GOSUB 5
 X = A + 2
 Y = B - 1
 GOSUB 5
 X = A - 1
 Y = B + 2
 GOSUB 5
 X = A - 2
 Y = B + 1
 GOSUB 5
 X = A + 1
 Y = B + 2
 GOSUB 5
 X = A + 2
 Y = B + 1
 GOSUB 5
 EXIT SUB
5 IF X < 0 OR X > 7 OR Y < 0 OR Y > 7 THEN RETURN 'make sure on board
 IF ID <> SGN(BOARD(Y, X)) THEN NDX = NDX + 1: XX(NDX) = X: YY(NDX) = Y 'make sure no piece of same color
 RETURN
END SUB
'Gen. for bishop
DEFINT A-Z
SUB BISHOP (A, B, XX(), YY(), NDX)
 ID = SGN(BOARD(B, A))
 FOR DXY = 1 TO 7 'work out diagonally one direction
   X = A - DXY
   Y = B + DXY
   IF X < 0 OR X > 7 OR Y < 0 OR Y > 7 THEN EXIT FOR 'stop when go off board
   GOSUB 3
   IF BOARD(Y, X) THEN EXIT FOR 'stop when hit piece
 NEXT
 FOR DXY = 1 TO 7
   X = A + DXY
   Y = B + DXY
   IF X < 0 OR X > 7 OR Y < 0 OR Y > 7 THEN EXIT FOR
   GOSUB 3
   IF BOARD(Y, X) THEN EXIT FOR
 NEXT
 FOR DXY = 1 TO 7
   X = A - DXY
   Y = B - DXY
   IF X < 0 OR X > 7 OR Y < 0 OR Y > 7 THEN EXIT FOR
   GOSUB 3
   IF BOARD(Y, X) THEN EXIT FOR
 NEXT
 FOR DXY = 1 TO 7
   X = A + DXY
   Y = B - DXY
   IF X < 0 OR X > 7 OR Y < 0 OR Y > 7 THEN EXIT FOR
   GOSUB 3
   IF BOARD(Y, X) THEN EXIT FOR
 NEXT
 EXIT SUB
3 REM
 IF ID <> SGN(BOARD(Y, X)) THEN 'make sure no piece of same color
   NDX = NDX + 1
   XX(NDX) = X
   YY(NDX) = Y
 END IF
 RETURN
END SUB
'Gen for rook
DEFINT A-Z
SUB ROOK (A, B, XX(), YY(), NDX)
 ID = SGN(BOARD(B, A))
 FOR X = A - 1 TO 0 STEP -1 'work out vert/horiz each dir.
   IF ID <> SGN(BOARD(B, X)) THEN 'make sure no piece of same color
     NDX = NDX + 1
     XX(NDX) = X
     YY(NDX) = B
   END IF
   IF (BOARD(B, X)) THEN EXIT FOR
 NEXT
 FOR X = A + 1 TO 7 STEP 1
   IF ID <> SGN(BOARD(B, X)) THEN
     NDX = NDX + 1
     XX(NDX) = X
     YY(NDX) = B
   END IF
   IF (BOARD(B, X)) THEN EXIT FOR
 NEXT
 FOR Y = B - 1 TO 0 STEP -1
   IF ID <> SGN(BOARD(Y, A)) THEN
     NDX = NDX + 1
     XX(NDX) = A
     YY(NDX) = Y
   END IF
   IF (BOARD(Y, A)) THEN EXIT FOR
 NEXT
 FOR Y = B + 1 TO 7 STEP 1
   IF ID <> SGN(BOARD(Y, A)) THEN
     NDX = NDX + 1
     XX(NDX) = A
     YY(NDX) = Y
   END IF
   IF (BOARD(Y, A)) THEN EXIT FOR
 NEXT
END SUB
'gen for queen
DEFINT A-Z
SUB QUEEN (A, B, XX(), YY(), NDX)
 CALL BISHOP(A, B, XX(), YY(), NDX) 'queen's move = bishop + rook
 CALL ROOK(A, B, XX(), YY(), NDX)
END SUB
'gen for king
DEFINT A-Z
SUB KING (A, B, XX(), YY(), NDX)
 ID = SGN(BOARD(B, A))
 FOR DY = -1 TO 1 'go through each of 8 king moves, checking for same color and off board
   IF B + DY < 0 OR B + DY > 7 THEN GOTO 12
   FOR DX = -1 TO 1
     IF A + DX < 0 OR A + DX > 7 THEN GOTO 11
     IF ID <> SGN(BOARD(B + DY, A + DX)) THEN
       NDX = NDX + 1
       XX(NDX) = A + DX
       YY(NDX) = B + DY
     END IF
11 NEXT
12 NEXT
END SUB
FUNCTION INCHECK (X)
 DIM XX(27), YY(27), NDX
 FOR B = 0 TO 7
   FOR A = 0 TO 7
     IF BOARD(B, A) >= 0 THEN GOTO 6
     CALL MOVELIST(A, B, XX(), YY(), NDX)
     FOR I = 0 TO NDX STEP 1
       X = XX(I)
       Y = YY(I)
       IF BOARD(Y, X) = 5000 THEN
         PRINT "YOU ARE IN CHECK!"
         PRINT " "
         PRINT " "
         INCHECK = 1
         EXIT FUNCTION
       END IF
     NEXT
6 NEXT
   NEXT
   INCHECK = 0
END FUNCTION
'Routine to make a move on the chessboard
DEFINT A-Z
SUB MAKEMOVE (A, B, X, Y)
 BOARD(Y, X) = BOARD(B, A) 'the piece moves to the target square
 BOARD(B, A) = 0 'the old square is now empty
 IF Y = 0 AND BOARD(Y, X) = 100 THEN BOARD(Y, X) = 900 'simple pawn promotion routine
 IF Y = 7 AND BOARD(Y, X) = -100 THEN BOARD(Y, X) = -900
END SUB
'Routine to get player move
DEFINT A-Z
SUB IO (A, B, X, Y, RESULT)
 DIM XX(0 TO 26), YY(0 TO 26)
 CLS
 IF A >= 0 THEN
   IF RESULT < -2500 THEN
     PRINT "I RESIGN"
     SLEEP
     SYSTEM
   END IF
   PIECE = BOARD(Y, X)
   CALL MAKEMOVE(A, B, X, Y)
   PRINT "MY MOVE: "; CHR$(65 + A); 8 - B; "-"; CHR$(65 + X); 8 - Y 'show computer move
   IF PIECE THEN
     PRINT "I TOOK YOUR ";
     IF PIECE = 100 THEN PRINT "PAWN"
     IF PIECE = 270 THEN PRINT "KNIGHT"
     IF PIECE = 300 THEN PRINT "BISHOP"
     IF PIECE = 500 THEN PRINT "ROOK"
     IF PIECE = 900 THEN PRINT "QUEEN"
     IF PIECE = 5000 THEN PRINT "KING"
   END IF
   NULL = INCHECK(0)
 END IF
 DO
   CALL SHOWBD
   LOCATE 24, 1
   INPUT "YOUR MOVE (ex: E2-E4): ", IN$
   IF UCASE$(IN$) = "QUIT" THEN CLS : END
   IF UCASE$(IN$) = "O-O" OR IN$ = "0-0" THEN
     IF CFLAG THEN GOTO 16
     IF BOARD(7, 7) <> 500 THEN GOTO 16
     IF BOARD(7, 6) OR BOARD(7, 5) THEN GOTO 16
     BOARD(7, 6) = 5000
     BOARD(7, 4) = 0
     BOARD(7, 5) = 500
     BOARD(7, 7) = 0
     CFLAG = 1
     EXIT SUB
   END IF
   IF UCASE$(IN$) = "O-O-O" OR IN$ = "0-0-0" THEN
     IF CFLAG THEN GOTO 16
     IF BOARD(7, 0) <> 500 THEN GOTO 16
     IF BOARD(7, 1) OR BOARD(7, 2) OR BOARD(7, 3) THEN GOTO 16
     BOARD(7, 2) = 5000
     BOARD(7, 4) = 0
     BOARD(7, 3) = 500
     BOARD(7, 0) = 0
     CFLAG = 1
     EXIT SUB
   END IF
   IF LEN(IN$) < 5 THEN GOTO 16
   B = 8 - (ASC(MID$(IN$, 2, 1)) - 48)
   A = ASC(UCASE$(MID$(IN$, 1, 1))) - 65
   X = ASC(UCASE$(MID$(IN$, 4, 1))) - 65
   Y = 8 - (ASC(MID$(IN$, 5, 1)) - 48)
   IF B > 7 OR B < 0 OR A > 7 OR A < 0 OR X > 7 OR X < 0 OR Y > 7 OR Y < 0 THEN GOTO 16
   IF BOARD(B, A) <= 0 THEN GOTO 16
   CALL MOVELIST(A, B, XX(), YY(), NDX)
   FOR K = 0 TO NDX STEP 1 'validate move
     IF X = XX(K) AND Y = YY(K) THEN
       MOVER = BOARD(B, A)
       TARGET = BOARD(Y, X)
       CALL MAKEMOVE(A, B, X, Y)
       LOCATE 1, 1
       IF INCHECK(0) = 0 THEN EXIT SUB 'make sure move out of check
       BOARD(B, A) = MOVER  'if not move out of check reset board
       BOARD(Y, X) = TARGET
       GOTO 16
     END IF
   NEXT
16 CLS
 LOOP
END SUB
'Show board
DEFINT A-Z
SUB SHOWBD
 LOCATE 3, 30
 COLOR 7, 0
 PRINT "A  B  C  D  E  F  G  H"
 FOR K = 0 TO 25
   LOCATE 4, 28 + K
   COLOR 6, 0
   PRINT CHR$(220)
 NEXT
 FOR B = 0 TO 7
   LOCATE 2 * B + 5, 26
   COLOR 7, 0
   PRINT CHR$(56 - B)
   LOCATE 2 * B + 5, 28
   COLOR 6, 0
   PRINT CHR$(219)
   LOCATE 2 * B + 6, 28
   COLOR 6, 0
   PRINT CHR$(219)
   FOR A = 0 TO 7
     IF ((A + B) MOD 2) THEN
       COLOUR = 8
     ELSE COLOUR = 12
     END IF
     CALL SQUARE(3 * A + 31, 2 * B + 5, COLOUR)
   NEXT
   LOCATE 2 * B + 5, 53
   COLOR 6, 0
   PRINT CHR$(219)
   LOCATE 2 * B + 6, 53
   COLOR 6, 0
   PRINT CHR$(219)
   LOCATE 2 * B + 6, 55
   COLOR 7, 0
   PRINT CHR$(56 - B)
 NEXT
 FOR K = 0 TO 25
   LOCATE 21, 28 + K
   COLOR 6, 0
   PRINT CHR$(223)
 NEXT
 LOCATE 22, 30
 COLOR 7, 0
 PRINT "A  B  C  D  E  F  G  H"
 FOR B = 0 TO 7
   FOR A = 0 TO 7
     CALL SHOWMAN(A, B, 0)
   NEXT
 NEXT
 COLOR 7, 0
END SUB
'Show piece
DEFINT A-Z
SUB SHOWMAN (A, B, FLAG)
 IF BOARD(B, A) < 0 THEN BACK = 0
 IF BOARD(B, A) > 0 THEN BACK = 7
 FORE = 7 - BACK + FLAG
 IF BOARD(B, A) = 0 THEN
   IF (A + B) AND 1 THEN BACK = 8 ELSE BACK = 12
   FORE = BACK + -1 * (FLAG > 0)
 END IF
 N$ = " "
 PIECE = INT(ABS(BOARD(B, A)))
 IF PIECE = 0 THEN N$ = CHR$(219)
 IF PIECE = 100 THEN N$ = "P"
 IF PIECE = 270 THEN N$ = "N"
 IF PIECE = 300 THEN N$ = "B"
 IF PIECE = 500 THEN N$ = "R"
 IF PIECE = 900 THEN N$ = "Q"
 IF PIECE = 5000 OR PIECE = 7500 THEN N$ = "K"
 LOCATE 2 * B + 5 - (BOARD(B, A) > 0), 3 * A + 30
 COLOR FORE, BACK
 PRINT N$
 LOCATE 1, 1
 COLOR 7, 0
END SUB
'Display a square
DEFINT A-Z
SUB SQUARE (A, B, C)
 MT$ = CHR$(219)
 MT$ = MT$ + MT$ + MT$
 LOCATE B, A - 2
 COLOR C, C
 PRINT MT$
 LOCATE B + 1, A - 2
 COLOR C, C
 PRINT MT$
 COLOR 7, 0
END SUB

