Retrocomputing

Tic Tac Toe for GNUCobol

This program is transcribed into COBOL from Tic Tac Toe from BASIC Computer Games. There is a bug in the original BASIC game. It is possible for the computer to wrongly conclude that the game was a draw even though it won. This is because the scan for a diagonal win happens after the check for a full board. This bug has been corrected in the COBOL version.

If you think there are a lot of GO TOs, you should see the BASIC program.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. TIC-TAC-TOE.
       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       SOURCE-COMPUTER. GNUCOBOL.
       OBJECT-COMPUTER. GNUCOBOL.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  BOARD.
           10  S OCCURS 9             PIC S9.
       01  BOARD2D REDEFINES BOARD.
           10  ROW OCCURS 3.
               20 CELL OCCURS 3 PIC S9.
       01  ROW-DISPLAY.
           10  CELLD OCCURS 3.
             15 FILLER PIC X.
             15 CELL-DISPLAY PIC X.
             15 FILLER PIC X.
             15 DIVIDER PIC X.
       77  I   PIC 99.
       77  J   PIC 9.
       77  K   PIC 9.
       77  INX PIC 9.
       77  M   PIC 9.
      * If G = -1 then computer has just made move
       77  G   PIC S9.
       77  H   PIC S9.
       01  COMP-MARK                   CONSTANT AS -1.
       01  UNOCCUPIED                  CONSTANT AS 0.
       01  OPPO-MARK                   CONSTANT AS 1.
      * C = Player choice of O or X
       77  C   PIC X.
       77  P   PIC X.
       77  Q   PIC X.

       PROCEDURE DIVISION.
       TICTACTOE.
           MOVE ZEROS TO BOARD.
           MOVE "   !   !    " TO ROW-DISPLAY.
           DISPLAY "              TIC-TAC-TOE".
           DISPLAY "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY".
           DISPLAY SPACE.
           DISPLAY SPACE.
           DISPLAY SPACE.
           DISPLAY "THE BOARD IS NUMBERED:".
           DISPLAY " 1  2  3".
           DISPLAY " 4  5  6".
           DISPLAY " 7  8  9".
           DISPLAY SPACE.
           DISPLAY SPACE.
           DISPLAY SPACE.
           DISPLAY "DO YOU WANT 'X' OR 'O'?" WITH NO ADVANCING.
           ACCEPT C.
           IF C = "X" OR "x" GO TO PLAYER-IS-X.
           MOVE "O" TO P.
           MOVE "X" TO Q.
       NEXT-ROUND.
           MOVE COMP-MARK TO G.
           MOVE OPPO-MARK TO H.
      * Is center occupied?
           IF S(5) = UNOCCUPIED
               MOVE COMP-MARK TO S (5)
               GO TO DISP-COMP-MOVE.
      * Is center mine?
           IF S(5) <> OPPO-MARK GO TO LIN106.
      * Is top left occupied?
           IF S(1) <> UNOCCUPIED GO TO LIN110.
           MOVE COMP-MARK TO S(1).
           GO TO DISP-COMP-MOVE.
       LIN106.
           IF S(2) = OPPO-MARK AND S(1) = UNOCCUPIED
                   OR S(4) = OPPO-MARK AND S(1) = UNOCCUPIED
                   GO TO TAKE-CELL-1.
           IF S(6) = OPPO-MARK AND S(9) = UNOCCUPIED GO TO TAKE-CELL-9.
           IF S(8) = OPPO-MARK AND S(9) = UNOCCUPIED GO TO TAKE-CELL-9.
       LIN110.
           IF G = OPPO-MARK GO TO LIN112.
           GO TO LIN118.
       LIN112.
           MOVE M TO INX.
           SUBTRACT 1 FROM INX.
      * Calculate row
           DIVIDE 3 INTO INX.
           MULTIPLY 3 BY INX.
           ADD 1 TO INX.
           MOVE INX TO J.
           IF INX = M THEN MOVE 1 TO K.
           ADD 1 TO INX.
           IF INX = M THEN MOVE 2 TO K.
           ADD 1 TO INX.
           IF INX = M THEN MOVE 3 TO K.
           GO TO LIN120.

       LIN118.
           MOVE 1 TO J.
       LIN119.
           MOVE 1 TO K.
       LIN120.
           IF S(J) <> G GO TO LIN130.
           IF S(J + 2) <> G GO TO LIN135.
           IF S(J + 1) <> UNOCCUPIED GO TO LIN150.
           MOVE COMP-MARK TO S(J + 1).
           GO TO DISP-COMP-MOVE.
       LIN130.
           IF S(J) = H GO TO LIN150.
           IF S(J + 1) <> G GO TO LIN150.
           IF S(J + 2) <> G GO TO LIN150.
           MOVE COMP-MARK TO S(J).
           GO TO DISP-COMP-MOVE.
      * Take last in row
       LIN135.
           IF S(J + 1) <> G GO TO LIN150.
           IF S(J + 2) <> UNOCCUPIED GO TO LIN150.
           MOVE COMP-MARK TO S(J + 2).
           GO TO DISP-COMP-MOVE.
      * Take middle in column if top and bottom not taken
       LIN150.
           IF S(K) <> G GO TO LIN160.
           IF S(K + 6) <> G GO TO LIN165.
           IF S(K + 3) <> UNOCCUPIED GO TO LIN170.
           MOVE COMP-MARK TO S(K + 3).
           GO TO DISP-COMP-MOVE.
      * Take top in column
       LIN160.
           IF S(K) = H GO TO LIN170.
           IF S(K + 3) <> G GO TO LIN170.
           IF S(K + 6) <> G GO TO LIN170.
           MOVE COMP-MARK TO S(K).
           GO TO DISP-COMP-MOVE.
       LIN165.
           IF S(K + 3) <> G GO TO LIN170.
           IF S(K + 6) <> UNOCCUPIED GO TO LIN170.
           MOVE COMP-MARK TO S(K + 6).
           GO TO DISP-COMP-MOVE.
       LIN170.
           GO TO LIN450.
       LIN171.
           IF S(3) = G AND S(7) = UNOCCUPIED
               MOVE COMP-MARK TO S(7)
               GO TO DISP-COMP-MOVE.
           IF S(9) = G AND S(1) = UNOCCUPIED GO TO TAKE-CELL-1.
           IF S(7) = G AND S(3) = UNOCCUPIED GO TO TAKE-CELL-3.
           IF S(9) = UNOCCUPIED AND S(1) = G GO TO TAKE-CELL-9.
       LIN175.
           IF G = COMP-MARK THEN
               MOVE OPPO-MARK TO G
               MOVE COMP-MARK TO H
               GO TO LIN110.
           IF S(9) = OPPO-MARK AND S(3) = UNOCCUPIED GO TO MAYBE-CELL-3.
      * Take first non-empty cell larger than 1
       FIND-FREE.
           MOVE 2 TO I.
       NEXT-FREE-CELL.
           IF I < 10
               IF S(I) <> UNOCCUPIED
                   ADD 1 TO I
                   GO TO NEXT-FREE-CELL
               ELSE
                   MOVE COMP-MARK TO S(I)
                   GO TO DISP-COMP-MOVE.
       TAKE-CELL-1.
           MOVE COMP-MARK TO S(1).
           GO TO DISP-COMP-MOVE.
       MAYBE-CELL-3.
           IF S(1) = OPPO-MARK GO TO FIND-FREE.
       TAKE-CELL-3.
           MOVE COMP-MARK TO S(3).
           GO TO DISP-COMP-MOVE.
       TAKE-CELL-9.
           MOVE COMP-MARK TO S(9).

       DISP-COMP-MOVE.
           DISPLAY SPACE.
           DISPLAY "THE COMPUTER MOVES TO...".
           PERFORM PRINT-BOARD THRU PRINT-BOARD-EXIT.
           GO TO ASK-PLAYER.
       LIN450.
           IF G=1 GO TO LIN465.
           IF J=7 AND K=3 GO TO LIN465.
           ADD 1 TO K.
           IF K NOT > 3 GO TO LIN120.
           ADD 3 TO J.
           IF J NOT > 7 GO TO LIN119.
       LIN465.
           IF S(5) = G GO TO LIN171.
           GO TO LIN175.

       PLAYER-IS-X.
           MOVE "X" TO P.
           MOVE "O" TO Q.
       ASK-PLAYER.
           DISPLAY SPACE.
           DISPLAY "WHERE DO YOU MOVE? (0 = END)" WITH NO ADVANCING.
           ACCEPT M.
           IF M = 0 THEN
               DISPLAY "THANKS FOR THE GAME."
               GO TO END-GAME.
           IF S(M) = UNOCCUPIED GO TO MARK-CHOICE.
       ILLEGAL-MOVE.
           DISPLAY "THAT SQUARE IS OCCUPIED.".
           DISPLAY SPACE.
           DISPLAY SPACE.
           GO TO ASK-PLAYER.
      * Set player's marker in cell
       MARK-CHOICE.
           MOVE OPPO-MARK TO G.
           MOVE OPPO-MARK TO S(M).
           PERFORM PRINT-BOARD THRU PRINT-BOARD-EXIT.
           GO TO NEXT-ROUND.

       PRINT-BOARD.
           DISPLAY SPACE.
           PERFORM PRINT-ROW VARYING J FROM 1 BY 1 UNTIL J > 3.
           GO TO BOARD-STATUS.

       PRINT-ROW.
           PERFORM MARK-CELL VARYING I FROM 1 BY 1 UNTIL I > 3.
           DISPLAY ROW-DISPLAY.
           IF J < 3 DISPLAY "---+---+---".

       MARK-CELL.
           IF CELL(J,I) = COMP-MARK
                   MOVE Q TO CELL-DISPLAY(I).
           IF CELL(J,I) = UNOCCUPIED
                   MOVE " " TO CELL-DISPLAY(I).
           IF CELL(J,I) = OPPO-MARK
                   MOVE P TO CELL-DISPLAY(I).

      * Check board status
       BOARD-STATUS.
           DISPLAY SPACE.
           DISPLAY SPACE.
           MOVE 1 TO I.
      * Check horizontally
       NEXT-ROW.
           IF S(I) <> S(I + 1) GO TO SKIP-ROW.
           IF S(I) <> S(I + 2) GO TO SKIP-ROW.
           IF S(I) = COMP-MARK GO TO COMPUTER-WIN.
           IF S(I) = OPPO-MARK GO TO PLAYER-WIN.
       SKIP-ROW.
           ADD 3 TO I.
           IF I NOT > 7 GO TO NEXT-ROW.

      * Check vertically
           MOVE 1 TO I.
       NEXT-COL.
           IF S(I) <> S(I + 3) GO TO SKIP-COL.
           IF S(I) <> S(I + 6) GO TO SKIP-COL.
           IF S(I) = COMP-MARK GO TO COMPUTER-WIN.
           IF S(I) = OPPO-MARK GO TO PLAYER-WIN.
       SKIP-COL.
           ADD 1 TO I.
           IF I NOT > 3 GO TO NEXT-COL.

      * Same markers diagonally?
       CHECK-DIAG.
           IF S(5) <> G GO TO CHECK-ALL-FILLED.
           IF S(1) = G AND S(9) = G GO TO WIN-DIAGONAL.
           IF S(3) = G AND S(7) = G GO TO WIN-DIAGONAL.

      * Any unfilled cells?
       CHECK-ALL-FILLED.
           MOVE 1 TO I.
       CHECK-NEXT-CELL.
           IF S(I) = UNOCCUPIED GO TO PRINT-BOARD-EXIT.
           ADD 1 TO I
           IF I NOT > 9 GO TO CHECK-NEXT-CELL.
           DISPLAY "IT'S A DRAW. THANK YOU.".
           GO TO END-GAME.
      * There are still empty cells.
       PRINT-BOARD-EXIT.
           EXIT.

       WIN-DIAGONAL.
           IF G = COMP-MARK GO TO COMPUTER-WIN.
       PLAYER-WIN.
           DISPLAY "YOU BEAT ME!! GOOD GAME.".
           GO TO END-GAME.
       COMPUTER-WIN.
           DISPLAY "I WIN, TURKEY!!!".
       END-GAME.
           STOP RUN.

Output

              TIC-TAC-TOE
CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
 
 
 
THE BOARD IS NUMBERED:
 1  2  3
 4  5  6
 7  8  9
 
 
 
DO YOU WANT 'X' OR 'O'?
X
 
WHERE DO YOU MOVE? (0 = END)
5
 
   !   !    
---+---+---
   ! X !    
---+---+---
   !   !