AS400 COBOL 贪吃蛇游戏

运行效果

源码

README.TXT        一些实现的细节

/**********************************************************************/
/* AUTHOR:       WITT                                                 */
/* DESCRIPTION:  ABOUT THE GAME                                       */
/* DATE WRITTEN: 2024-08-15                                           */
/**********************************************************************/
Implementation details
 .draw screen by DSPF
 .snake data structure is a queue implement by array
 .pseudo random number generate from system time
 .ranking data save in PF
 .get user ID by CLP command RTVJOBA
 .auto refresh screen by RPGLE read DSPF time out

SSNAKE.DSPF        游戏主屏幕 

20行 * 42列的字符阵列,包含一个游戏结束的弹窗,边框由程序运行时画出

当前游戏结束的弹窗只能显示3行排行榜记录,目前还不太会实现可以滚动显示记录的弹窗

     A**************************************************************************
     A*  AUTHOR:       WITT                                                    *
     A*  DESCRIPTION:  SNAKE SCREEN                                            *
     A*  DATE WRITTEN: 2024-08-19                                              *
     A**************************************************************************
     A                                      DSPSIZ(24 80 *DS3)
     A                                      INDARA
     A          R HEADER
     A                                      INVITE
     A                                      CF01(01)
     A                                      CF02(02)
     A                                      CF03(03)
     A                                      CF05(05)
     A                                      CF09(09)
     A                                      CF10(10)
     A                                      CF11(11)
     A                                      CF12(12)
     A            MAPLINE1      42A  O  3 20
     A            MAPLINE2      42A  O  4 20
     A            MAPLINE3      42A  O  5 20
     A            MAPLINE4      42A  O  6 20
     A            MAPLINE5      42A  O  7 20
     A            MAPLINE6      42A  O  8 20
     A            MAPLINE7      42A  O  9 20
     A            MAPLINE8      42A  O 10 20
     A            MAPLINE9      42A  O 11 20
     A            MAPLINE10     42A  O 12 20
     A            MAPLINE11     42A  O 13 20
     A            MAPLINE12     42A  O 14 20
     A            MAPLINE13     42A  O 15 20
     A            MAPLINE14     42A  O 16 20
     A            MAPLINE15     42A  O 17 20
     A            MAPLINE16     42A  O 18 20
     A            MAPLINE17     42A  O 19 20
     A            MAPLINE18     42A  O 20 20
     A            MAPLINE19     42A  O 21 20
     A            MAPLINE20     42A  O 22 20
     A          R WINGAMOVR
     A                                      WINDOW(5 22 7 34)
     A                                      BLINK
     A                                      OVERLAY
     A                                      CF03(03)
     A                                      CF05(05)
     A                                  1  1'GAMEOVER'
     A                                  1 21'SCORE:'
     A            SCORE          3Y 0O  1 28
     A                                  3  1'RANK PLAYER         SCORE DATE'
     A            RNKREC1RNK     4Y 0O  4  1
     A            RNKREC1NAM    14A  O  4  6
     A            RNKREC1COR     3Y 0O  4 21
     A            RNKREC1DTE     8A  O  4 27
     A            RNKREC2RNK     4Y 0O  5  1
     A            RNKREC2NAM    14A  O  5  6
     A            RNKREC2COR     3Y 0O  5 21
     A            RNKREC2DTE     8A  O  5 27
     A            RNKREC3RNK     4Y 0O  6  1
     A            RNKREC3NAM    14A  O  6  6
     A            RNKREC3COR     3Y 0O  6 21
     A            RNKREC3DTE     8A  O  6 27

FRANKING.PF        排行榜数据表

      *****************************************************************
      * AUTHOR:       WITT                                            *
      * DESCRIPTION:  RANKING DATA                                    *
      * DATE WRITTEN: 2024-08-08                                      *
      *****************************************************************
                R R001
                  RKSCORE        3P 0
                  RKPLAYER      14A
                  RKDTE          8A
                  RKTIME         8A
                  RKVIDEOLNK     3P 0
                K RKSCORE

PSNAKE.CLP        开始新游戏

这里是为了能够直接1条命令运行游戏 (CALL WITTGAMES/PSNAKE)

/**********************************************************************/
/* AUTHOR:       WITT                                                 */
/* DESCRIPTION:  START A NEW SNAKE GAME                               */
/* DATE WRITTEN: 2024-08-14                                           */
/**********************************************************************/
      PGM
      CALL PGM(WITTGAMES/PSETENV)
      CALL PGM(*LIBL/PSNAKEMAIN)

 END: ENDPGM

PSETENV.CLP        设置游戏运行环境

这里将存放游戏的库(WITTGAMES)加到库列表最后面,这样相关的程序/文件就可以被访问到

/**********************************************************************/
/* AUTHOR:       WITT                                                 */
/* DESCRIPTION:  SETUP ENVIRONMENT                                    */
/* DATE WRITTEN: 2024-08-09                                           */
/**********************************************************************/
      PGM
      MONMSG MSGID(CPF2104)

      RMVLIBLE LIB(WITTGAMES )
      ADDLIBLE LIB(WITTGAMES ) POSITION(*LAST)

 END: ENDPGM

PSNAKEMAIN.CBL        游戏主程序

1. 蛇的移动(MOVE-SNAKE)实现方法为把蛇头画到屏幕并把蛇尾从屏幕擦除,

整条蛇的坐标数据结构是一个先进(画到屏幕)先出(从屏幕擦除)的队列

2. 蛇队列的维护

蛇队列是由数组实现的(WK-SNAKEQUEUE),它的每个元素是一个坐标(行,列)

出队(SNAKEQUEUE-DEQUEUE)操作之后,数组前面出队的元素存储空间就闲置下来,而入队(SNAKEQUEUE-ENQUEUE)操作则会写入新的元素到数组中。在蛇的移动过程中会不断的做蛇头入队,蛇尾出队,这样最终会造成数组右侧下标越界而左侧却有很多闲置空间。

所以需要在数组最右侧存储空间被写入之后做一次维护(SNAKEQUEUE-REARRANGE),方法是把整条蛇从数组的右边移动到左边,重新利用起数组左侧的闲置空间

整个数组长度为蛇的最大长度 15 * 40 = 600

3. 生成随机数的方法

生成食物时需要生成食物行列坐标的随机数

以生成行坐标为例: 除去边框剩余的2 - 16行可以放置食物,所以行坐标为2 - 16之间的随机数,生成方法(GENERATE-RANDOM-NUMBER)为系统时间对15求余之后再加2

       IDENTIFICATION DIVISION.
      *****************************************************************
      * AUTHOR:       WITT                                            *
      * DESCRIPTION:  SNAKE MAIN PROGRAM                              *
      * DATE WRITTEN: 2024-08-19                                      *
      *****************************************************************
       PROGRAM-ID.                       PSNAKEMAIN.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT                        SSNAKE-FILE
           ASSIGN                        TO WORKSTATION-SSNAKE-SI
           ORGANIZATION                  IS TRANSACTION
           FILE STATUS                   IS WK-SSNAKE-STATUS
           CONTROL-AREA                  IS WK-TRANS-CTRL-AREA.

       DATA DIVISION.
       FILE SECTION.
       FD  SSNAKE-FILE
           LABEL RECORDS ARE OMITTED
           DATA RECORD IS SSNAKE-REC.
       01 SSNAKE-REC.
           COPY DD-ALL-FORMATS-I-O       OF SSNAKE.

       WORKING-STORAGE SECTION.
       77  WK-CONST-MAX-LINE             PIC 9(2)       VALUE 15.
       77  WK-CONST-MAX-COLN             PIC 9(2)       VALUE 40.
       77  WK-SNAKEQ-FRONT-POINTER       PIC 9(3)       VALUE 1.
       77  WK-SNAKEQ-REAR-POINTER        PIC 9(3)       VALUE 0.
       77  WK-SYSTIME                    PIC 9(8).
       77  WK-RANDOM-NUMBER-RANGE        PIC 9(2).
       77  WK-RANDOM-NUMBER              PIC 9(2).
       77  WK-QUOTIENT                   PIC 9(7).
       77  WK-OFFSET                     PIC 9(3)       VALUE 0.
       77  WK-DEPA-POINTER               PIC 9(3)       VALUE 0.
       77  WK-DEST-POINTER               PIC 9(3)       VALUE 0.
       77  WK-SLEEP-DURATION             PIC 9(3)       VALUE 800.
       77  WK-LINE-IDX                   PIC 9(2).

       01  WK-SAVED-COORDINATE.
           05  WK-SKHEAD-LINE            PIC 9(2).
           05  WK-SKHEAD-COLN            PIC 9(2).
           05  WK-SKTAIL-LINE            PIC 9(2).
           05  WK-SKTAIL-COLN            PIC 9(2).
           05  WK-FOOD-LINE              PIC 9(2).
           05  WK-FOOD-COLN              PIC 9(2).
       01  WK-FILE-STATUS.
           05  WK-SSNAKE-STATUS          PIC X(2).
       01  WK-TRANS-CTRL-AREA.
           05  WK-FUNCTION-KEY           PIC X(2).
               88 WK-FKEY-F1             VALUE "01".
               88 WK-FKEY-F2             VALUE "02".
               88 WK-FKEY-F3             VALUE "03".
               88 WK-FKEY-F5             VALUE "05".
               88 WK-FKEY-F9             VALUE "09".
               88 WK-FKEY-F10            VALUE "10".
               88 WK-FKEY-F11            VALUE "11".
               88 WK-FKEY-F12            VALUE "12".
       01  WK-SCREEN-ARRAY.
           05  WK-MAP-LINE               OCCURS 20 TIMES.
               15  WK-MAP-NODE           PIC X(1) OCCURS 42 TIMES.
       01  WK-SNAKEQUEUE.
           05  WK-SNAKE-COORDINATE       OCCURS 600 TIMES.
               15  WK-SNAKEQ-LINE        PIC 9(2).
               15  WK-SNAKEQ-COLN        PIC 9(2).
       01  WK-MOVE-DIRECTION             PIC X(1).
           88  WK-MOVE-RIGHT             VALUE "R".
           88  WK-MOVE-LEFT              VALUE "L".
           88  WK-MOVE-UP                VALUE "U".
           88  WK-MOVE-DOWN              VALUE "D".
       01  WK-GAME-STATUS                PIC X(1).
           88  WK-NEWGAME                VALUE "N".
           88  WK-GAMEOVER               VALUE "O".
       01  WK-WINGAMOVR-O.
           05  WK-CURR-ROUND-SCORE       PIC 9(3)       VALUE 0.
           05  WK-RANKING-TABLE.
               10  WK-RANKING-TABLE-REC  OCCURS 3 TIMES.
                   15  WK-RANKING-RANK   PIC 9(4)       VALUE 0.
                   15  WK-RANKING-NAME   PIC X(14)      VALUE SPACES.
                   15  WK-RANKING-SCORE  PIC 9(3)       VALUE 0.
                   15  WK-RANKING-DATE   PIC X(8)       VALUE SPACES.
       01  WK-END-LOOP-FLG               PIC X(1).
           88  WK-END-LOOP                              VALUE "Y".
       01  WK-CATCH-FOOD-FLG             PIC X(1).
           88  WK-CATCH-FOOD                            VALUE "Y".

       LINKAGE SECTION.

       PROCEDURE DIVISION.
       MAIN-PROC.
           PERFORM 200-NEWGAME-INIT
           THRU    200-NEWGAME-INIT-X.

           MOVE SPACE                    TO WK-FUNCTION-KEY.
           PERFORM 400-LOOP-GAME
           THRU    400-LOOP-GAME-X
           UNTIL   WK-FKEY-F3.

       MAIN-PROC-X.
           GOBACK.

       200-NEWGAME-INIT.
           SET WK-NEWGAME                TO TRUE.
           MOVE SPACE                    TO WK-CATCH-FOOD-FLG.
           COMPUTE WK-CURR-ROUND-SCORE   = 0.

           PERFORM SNAKEQUEUE-INIT
           THRU    SNAKEQUEUE-INIT-X.

           PERFORM SCREEN-INIT
           THRU    SCREEN-INIT-X.

           PERFORM DRAW-SNAKEHEAD-TO-SCREEN
           THRU    DRAW-SNAKEHEAD-TO-SCREEN-X.

           PERFORM GENERATE-FOOD
           THRU    GENERATE-FOOD-X.

       200-NEWGAME-INIT-X.
           EXIT.

       400-LOOP-GAME.
           PERFORM REFRESH-SCREEN
           THRU    REFRESH-SCREEN-X.

           PERFORM HANDLE-FKEY
           THRU    HANDLE-FKEY-X.
           IF WK-FKEY-F3
               GO TO 400-LOOP-GAME-X
           END-IF.

           PERFORM MOVE-SNAKE
           THRU    MOVE-SNAKE-X.
           IF WK-GAMEOVER
               PERFORM GAME-OVER
               THRU    GAME-OVER-X
           END-IF.

       400-LOOP-GAME-X.
           EXIT.

       HANDLE-FKEY.
           PERFORM UPD-MOVE-SPEED
           THRU    UPD-MOVE-SPEED-X.

           PERFORM UPD-MOVE-DIRECTION
           THRU    UPD-MOVE-DIRECTION-X.

       HANDLE-FKEY-X.
           EXIT.

       UPD-MOVE-SPEED.
           IF WK-FKEY-F11
           AND WK-SLEEP-DURATION         > 100
               SUBTRACT 100              FROM WK-SLEEP-DURATION
           END-IF.

           IF WK-FKEY-F12
           AND WK-SLEEP-DURATION         < 900
               ADD 100                   TO WK-SLEEP-DURATION
           END-IF.

       UPD-MOVE-SPEED-X.
           EXIT.

       UPD-MOVE-DIRECTION.
           EVALUATE TRUE
               WHEN WK-FKEY-F1
                   IF NOT WK-MOVE-DOWN
                       SET WK-MOVE-UP    TO TRUE
                   END-IF
               WHEN WK-FKEY-F2
                   IF NOT WK-MOVE-UP
                       SET WK-MOVE-DOWN  TO TRUE
                   END-IF
               WHEN WK-FKEY-F9
                   IF NOT WK-MOVE-RIGHT
                       SET WK-MOVE-LEFT  TO TRUE
                   END-IF
               WHEN WK-FKEY-F10
                   IF NOT WK-MOVE-LEFT
                       SET WK-MOVE-RIGHT TO TRUE
                   END-IF
               WHEN OTHER
                   CONTINUE
           END-EVALUATE.

       UPD-MOVE-DIRECTION-X.
           EXIT.

       MOVE-SNAKE.
           PERFORM UPD-SNAKEHEAD-COORDINATE
           THRU    UPD-SNAKEHEAD-COORDINATE-X.

           PERFORM CATCH-FOOD
           THRU    CATCH-FOOD-X.

           PERFORM UPD-GAME-STATUS
           THRU    UPD-GAME-STATUS-X.
           IF WK-GAMEOVER
               GO TO MOVE-SNAKE-X
           END-IF.

           PERFORM DRAW-SNAKEHEAD-TO-SCREEN
           THRU    DRAW-SNAKEHEAD-TO-SCREEN-X.

           PERFORM ERASE-SNAKETAIL-FROM-SCREEN
           THRU    ERASE-SNAKETAIL-FROM-SCREEN-X.

       MOVE-SNAKE-X.
           EXIT.

       UPD-SNAKEHEAD-COORDINATE.
           EVALUATE TRUE
               WHEN WK-MOVE-RIGHT
                   ADD 1                 TO WK-SKHEAD-COLN
               WHEN WK-MOVE-LEFT
                   SUBTRACT 1            FROM WK-SKHEAD-COLN
               WHEN WK-MOVE-UP
                   SUBTRACT 1            FROM WK-SKHEAD-LINE
               WHEN WK-MOVE-DOWN
                   ADD 1                 TO WK-SKHEAD-LINE
               WHEN OTHER
                   CONTINUE
           END-EVALUATE.

       UPD-SNAKEHEAD-COORDINATE-X.
           EXIT.

       CATCH-FOOD.
           IF WK-SKHEAD-LINE             NOT = WK-FOOD-LINE
           OR WK-SKHEAD-COLN             NOT = WK-FOOD-COLN
               GO TO CATCH-FOOD-X
           END-IF.

           ADD 1                         TO WK-CURR-ROUND-SCORE.
           SET WK-CATCH-FOOD             TO TRUE.

           PERFORM GENERATE-FOOD
           THRU    GENERATE-FOOD-X.

       CATCH-FOOD-X.
           EXIT.

       UPD-GAME-STATUS.
           IF WK-MAP-NODE(WK-SKHEAD-LINE, WK-SKHEAD-COLN) NOT = SPACE
           AND NOT WK-CATCH-FOOD
               SET WK-GAMEOVER           TO TRUE
           END-IF.

       UPD-GAME-STATUS-X.
           EXIT.

       DRAW-SNAKEHEAD-TO-SCREEN.
           MOVE "*"                      TO
           WK-MAP-NODE(WK-SKHEAD-LINE, WK-SKHEAD-COLN).

           PERFORM SNAKEQUEUE-ENQUEUE
           THRU    SNAKEQUEUE-ENQUEUE-X.

       DRAW-SNAKEHEAD-TO-SCREEN-X.
           EXIT.

       ERASE-SNAKETAIL-FROM-SCREEN.
           IF WK-CATCH-FOOD
               MOVE SPACE                TO WK-CATCH-FOOD-FLG
               GO TO ERASE-SNAKETAIL-FROM-SCREEN-X
           END-IF.

           MOVE SPACE                    TO
           WK-MAP-NODE(WK-SKTAIL-LINE, WK-SKTAIL-COLN).

           PERFORM SNAKEQUEUE-DEQUEUE
           THRU    SNAKEQUEUE-DEQUEUE-X.

       ERASE-SNAKETAIL-FROM-SCREEN-X.
           EXIT.

       GAME-OVER.
           PERFORM UPD-FRANKING
           THRU    UPD-FRANKING-X.

           PERFORM QRY-FRANKING
           THRU    QRY-FRANKING-X.

           PERFORM POP-GAMEOVER-WINDOW
           THRU    POP-GAMEOVER-WINDOW-X.
           IF WK-FKEY-F5
               PERFORM 200-NEWGAME-INIT
               THRU    200-NEWGAME-INIT-X
           END-IF.

       GAME-OVER-X.
           EXIT.

       SNAKEQUEUE-INIT.
           COMPUTE WK-SNAKEQ-FRONT-POINTER = 1.
           COMPUTE WK-SNAKEQ-REAR-POINTER  = 0.

       SNAKEQUEUE-INIT-X.
           EXIT.

       SNAKEQUEUE-ENQUEUE.
           ADD 1                         TO WK-SNAKEQ-REAR-POINTER.
           COMPUTE WK-SNAKEQ-LINE(WK-SNAKEQ-REAR-POINTER) =
                   WK-SKHEAD-LINE.
           COMPUTE WK-SNAKEQ-COLN(WK-SNAKEQ-REAR-POINTER) =
                   WK-SKHEAD-COLN.

           IF WK-SNAKEQ-REAR-POINTER     >= 600
               PERFORM SNAKEQUEUE-REARRANGE
               THRU    SNAKEQUEUE-REARRANGE-X
           END-IF.

       SNAKEQUEUE-ENQUEUE-X.
           EXIT.

       SNAKEQUEUE-DEQUEUE.
           IF WK-SNAKEQ-FRONT-POINTER    > WK-SNAKEQ-REAR-POINTER
               GO TO SNAKEQUEUE-DEQUEUE-X
           END-IF.

           ADD 1                         TO WK-SNAKEQ-FRONT-POINTER.
           COMPUTE WK-SKTAIL-LINE        =
                   WK-SNAKEQ-LINE(WK-SNAKEQ-FRONT-POINTER)
           COMPUTE WK-SKTAIL-COLN        =
                   WK-SNAKEQ-COLN(WK-SNAKEQ-FRONT-POINTER).

       SNAKEQUEUE-DEQUEUE-X.
           EXIT.

       SNAKEQUEUE-REARRANGE.
           COMPUTE WK-OFFSET             = WK-SNAKEQ-FRONT-POINTER - 1.
           IF WK-OFFSET                  <= 0
               GO TO SNAKEQUEUE-REARRANGE-X
           END-IF.

           PERFORM
           VARYING WK-DEPA-POINTER FROM WK-SNAKEQ-FRONT-POINTER BY 1
           UNTIL   WK-DEPA-POINTER       > WK-SNAKEQ-REAR-POINTER
               COMPUTE WK-DEST-POINTER   = WK-DEPA-POINTER - WK-OFFSET
               COMPUTE WK-SNAKEQ-LINE(WK-DEST-POINTER) =
                       WK-SNAKEQ-LINE(WK-DEPA-POINTER)
               COMPUTE WK-SNAKEQ-COLN(WK-DEST-POINTER) =
                       WK-SNAKEQ-COLN(WK-DEPA-POINTER)
           END-PERFORM.

           COMPUTE WK-SNAKEQ-FRONT-POINTER = 1.
           COMPUTE WK-SNAKEQ-REAR-POINTER = WK-DEST-POINTER.

       SNAKEQUEUE-REARRANGE-X.
           EXIT.

       GENERATE-FOOD.
           MOVE SPACE                    TO WK-END-LOOP-FLG.
           PERFORM LOOP-UPD-FOOD-COORDINATE
           THRU    LOOP-UPD-FOOD-COORDINATE-X
           UNTIL   WK-END-LOOP.

           MOVE "*" TO WK-MAP-NODE(WK-FOOD-LINE, WK-FOOD-COLN).

       GENERATE-FOOD-X.
           EXIT.

       LOOP-UPD-FOOD-COORDINATE.
           COMPUTE WK-RANDOM-NUMBER-RANGE = WK-CONST-MAX-LINE.
           PERFORM GENERATE-RANDOM-NUMBER
           THRU    GENERATE-RANDOM-NUMBER-X.
           COMPUTE WK-FOOD-LINE          = WK-RANDOM-NUMBER + 2.

           COMPUTE WK-RANDOM-NUMBER-RANGE = WK-CONST-MAX-COLN.
           PERFORM GENERATE-RANDOM-NUMBER
           THRU    GENERATE-RANDOM-NUMBER-X.
           COMPUTE WK-FOOD-COLN          = WK-RANDOM-NUMBER + 2.

           IF WK-MAP-NODE(WK-FOOD-LINE, WK-FOOD-COLN) = SPACE
               SET WK-END-LOOP           TO TRUE
           END-IF.

       LOOP-UPD-FOOD-COORDINATE-X.
           EXIT.

       GENERATE-RANDOM-NUMBER.
           ACCEPT WK-SYSTIME             FROM TIME.
           DIVIDE WK-SYSTIME             BY WK-RANDOM-NUMBER-RANGE
           GIVING WK-QUOTIENT REMAINDER WK-RANDOM-NUMBER END-DIVIDE.

       GENERATE-RANDOM-NUMBER-X.
           EXIT.

       UPD-FRANKING.
           CALL "PUPDRANK" USING         WK-CURR-ROUND-SCORE.
           CANCEL "PUPDRANK".

       UPD-FRANKING-X.
           EXIT.

       QRY-FRANKING.
           CALL "PQRYRANK" USING         WK-RANKING-TABLE.
           CANCEL "PQRYRANK".

       QRY-FRANKING-X.
           EXIT.

       REFRESH-SCREEN.
           MOVE SPACE                    TO WK-FUNCTION-KEY.

           CALL "PREFSCREEN" USING       WK-FUNCTION-KEY
                                         WK-SLEEP-DURATION
                                         WK-MAP-LINE(1)
                                         WK-MAP-LINE(2)
                                         WK-MAP-LINE(3)
                                         WK-MAP-LINE(4)
                                         WK-MAP-LINE(5)
                                         WK-MAP-LINE(6)
                                         WK-MAP-LINE(7)
                                         WK-MAP-LINE(8)
                                         WK-MAP-LINE(9)
                                         WK-MAP-LINE(10)
                                         WK-MAP-LINE(11)
                                         WK-MAP-LINE(12)
                                         WK-MAP-LINE(13)
                                         WK-MAP-LINE(14)
                                         WK-MAP-LINE(15)
                                         WK-MAP-LINE(16)
                                         WK-MAP-LINE(17)
                                         WK-MAP-LINE(18)
                                         WK-MAP-LINE(19)
                                         WK-MAP-LINE(20).
           CANCEL "PREFSCREEN".

       REFRESH-SCREEN-X.
           EXIT.

       POP-GAMEOVER-WINDOW.
           OPEN I-O                      SSNAKE-FILE.
           WRITE SSNAKE-REC FROM WK-SCREEN-ARRAY FORMAT "HEADER".
           WRITE SSNAKE-REC FROM WK-WINGAMOVR-O FORMAT "WINGAMOVR".
           READ SSNAKE-FILE FORMAT "WINGAMOVR".
           CLOSE                         SSNAKE-FILE.

       POP-GAMEOVER-WINDOW-X.
           EXIT.

       SCREEN-INIT.
           INITIALIZE                    WK-SCREEN-ARRAY.
           PERFORM DRAW-SCREEN-BORDER
           THRU    DRAW-SCREEN-BORDER-X.

           INITIALIZE WK-SAVED-COORDINATE REPLACING NUMERIC BY 2.
           SET WK-MOVE-RIGHT             TO TRUE.

       SCREEN-INIT-X.
           EXIT.

       DRAW-SCREEN-BORDER.
           MOVE ALL "="                  TO WK-MAP-LINE(1)(2:40).
           MOVE ALL "="                  TO WK-MAP-LINE(17).
           MOVE ALL "="                  TO WK-MAP-LINE(20)(2:40).

           PERFORM VARYING WK-LINE-IDX  FROM 2 BY 1
           UNTIL   WK-LINE-IDX          > 19
               MOVE "|"                 TO WK-MAP-NODE(WK-LINE-IDX , 1)
               MOVE "|"                 TO WK-MAP-NODE(WK-LINE-IDX , 42)
           END-PERFORM.

           MOVE "F1-UP   F9-LEFT   F3-QUIT   F11-FASTER"
           TO   WK-MAP-LINE(18)(3:38).
           MOVE "F2-DOWN F10-RIGHT F5-RESUME F12-SLOWER"
           TO   WK-MAP-LINE(19)(3:38).

       DRAW-SCREEN-BORDER-X.
           EXIT.

PREFSCREEN.RPGLE        自动刷新屏幕 

自动刷新屏幕是基于RPGLE程序读取DSPF超时来实现的,这里可以参考下面这篇文章

AS400 RPGLE 利用DSPF读取超时实现屏幕自动刷新-CSDN博客

当用COBOL读取DSPF时,即便设置了DSPF读取时限,程序依然会停在READ语句那里持续等待玩家输入(这个问题目前还不太会解决),所以这里写了RPGLE子程序来做屏幕的自动刷新

值得一提的是单纯依靠DSPF WAITRCD属性是不好的,因为WAITRCD只支持设置为整数秒,设置为1秒的话蛇的移动很慢,设置为0秒的话玩家又根本来不及去按键。这里的做法是将WAITRCD设为0秒然后在一段时间之内(SLEEPDUR)循环读取玩家的按键,通过改变循环时间(SLEEPDUR)来改变屏幕刷新速度,也就是蛇的移动速度

      **********************************************************************
      * AUTHOR:       WITT                                                 *
      * DESCRIPTION:  REFRESH SNAKE SCREEN                                 *
      * DATE WRITTEN: 2024-08-20                                           *
      **********************************************************************
     FSSNAKE    CF   E             WORKSTN MAXDEV(*FILE)
     F                                     USROPN
     DSYSTIME          S               Z
     DTIMESTMP         S              7A
     DTIMESTMPNUM      S              7S 0
     DTIMERSTART       S              7S 0
     DTIMERDUR         S              7S 0
     DSLEEPDURNUM      S              3S 0
     DFKEYPRESSED      S               N
     C     *ENTRY        PLIST
     C                   PARM                    FKEY              2
     C                   PARM                    SLEEPDUR          3
     C                   PARM                    SCRLINE1         42
     C                   PARM                    SCRLINE2         42
     C                   PARM                    SCRLINE3         42
     C                   PARM                    SCRLINE4         42
     C                   PARM                    SCRLINE5         42
     C                   PARM                    SCRLINE6         42
     C                   PARM                    SCRLINE7         42
     C                   PARM                    SCRLINE8         42
     C                   PARM                    SCRLINE9         42
     C                   PARM                    SCRLINE10        42
     C                   PARM                    SCRLINE11        42
     C                   PARM                    SCRLINE12        42
     C                   PARM                    SCRLINE13        42
     C                   PARM                    SCRLINE14        42
     C                   PARM                    SCRLINE15        42
     C                   PARM                    SCRLINE16        42
     C                   PARM                    SCRLINE17        42
     C                   PARM                    SCRLINE18        42
     C                   PARM                    SCRLINE19        42
     C                   PARM                    SCRLINE20        42
     C
     C                   EVAL      QCMD = 'OVRDSPF FILE(SSNAKE) WAITRCD(*IMMED)'
     C                   CALL      'QCMDEXC'     QCMDEXC
     C                   OPEN      SSNAKE
     C                   EXSR      LOADSCRDATA
     C                   EXSR      REFSCREEN
     C                   CLOSE     SSNAKE
     C
     C     ENDPGM        TAG
     C                   MOVE      *ON           *INLR
     C
     C     QCMDEXC       PLIST
     C                   PARM                    QCMD            200
     C                   PARM      200           QLEN             15 5
     C
     C     LOADSCRDATA   BEGSR
     C                   MOVE      SCRLINE1      MAPLINE1
     C                   MOVE      SCRLINE2      MAPLINE2
     C                   MOVE      SCRLINE3      MAPLINE3
     C                   MOVE      SCRLINE4      MAPLINE4
     C                   MOVE      SCRLINE5      MAPLINE5
     C                   MOVE      SCRLINE6      MAPLINE6
     C                   MOVE      SCRLINE7      MAPLINE7
     C                   MOVE      SCRLINE8      MAPLINE8
     C                   MOVE      SCRLINE9      MAPLINE9
     C                   MOVE      SCRLINE10     MAPLINE10
     C                   MOVE      SCRLINE11     MAPLINE11
     C                   MOVE      SCRLINE12     MAPLINE12
     C                   MOVE      SCRLINE13     MAPLINE13
     C                   MOVE      SCRLINE14     MAPLINE14
     C                   MOVE      SCRLINE15     MAPLINE15
     C                   MOVE      SCRLINE16     MAPLINE16
     C                   MOVE      SCRLINE17     MAPLINE17
     C                   MOVE      SCRLINE18     MAPLINE18
     C                   MOVE      SCRLINE19     MAPLINE19
     C                   MOVE      SCRLINE20     MAPLINE20
     C                   ENDSR
     C
     C     REFSCREEN     BEGSR
     C                   EVAL      TIMERDUR    = 0
     C                   MOVE      SLEEPDUR      SLEEPDURNUM
     C                   EXSR      GETTIMESTMP
     C                   MOVE      TIMESTMP      TIMERSTART
     C                   WRITE     HEADER
     C
     C                   DOW       TIMERDUR < SLEEPDURNUM
     C                   READ      SSNAKE                               9699
     C                   IF        NOT FKEYPRESSED
     C                   EXSR      SETFKEY
     C                   ENDIF
     C                   EXSR      REFTIMERDUR
     C                   ENDDO
     C
     C                   ENDSR
     C
     C     SETFKEY       BEGSR
     C                   MOVE      *OFF          FKEYPRESSED
     C
     C                   IF        *IN01
     C                   EVAL      FKEY = '01'
     C                   MOVE      *ON           FKEYPRESSED
     C                   ENDIF
     C
     C                   IF        *IN02
     C                   EVAL      FKEY = '02'
     C                   MOVE      *ON           FKEYPRESSED
     C                   ENDIF
     C
     C                   IF        *IN03
     C                   EVAL      FKEY = '03'
     C                   MOVE      *ON           FKEYPRESSED
     C                   ENDIF
     C
     C                   IF        *IN05
     C                   EVAL      FKEY = '05'
     C                   MOVE      *ON           FKEYPRESSED
     C                   ENDIF
     C
     C                   IF        *IN09
     C                   EVAL      FKEY = '09'
     C                   MOVE      *ON           FKEYPRESSED
     C                   ENDIF
     C
     C                   IF        *IN10
     C                   EVAL      FKEY = '10'
     C                   MOVE      *ON           FKEYPRESSED
     C                   ENDIF
     C
     C                   IF        *IN11
     C                   EVAL      FKEY = '11'
     C                   MOVE      *ON           FKEYPRESSED
     C                   ENDIF
     C
     C                   IF        *IN12
     C                   EVAL      FKEY = '12'
     C                   MOVE      *ON           FKEYPRESSED
     C                   ENDIF
     C
     C                   ENDSR
     C
     C     GETTIMESTMP   BEGSR
     C                   TIME                    SYSTIME
     C                   MOVEL     SYSTIME       TIMESTMPL        26
     C                   MOVE      *BLANK        TIMESTMP          7
     C                   EVAL      TIMESTMP  = %SUBST(TIMESTMPL:15:2)
     C                                       + %SUBST(TIMESTMPL:18:2)
     C                                       + %SUBST(TIMESTMPL:21:3)
     C                   ENDSR
     C
     C     REFTIMERDUR   BEGSR
     C                   EXSR      GETTIMESTMP
     C                   MOVE      TIMESTMP      TIMESTMPNUM
     C                   EVAL      TIMERDUR = TIMESTMPNUM - TIMERSTART
     C                   ENDSR
     C

PUPDRANK.CBL        更新排行榜文件

       IDENTIFICATION DIVISION.
      *****************************************************************
      * AUTHOR:       WITT                                            *
      * DESCRIPTION:  UPDATE FRANKING                                 *
      * DATE WRITTEN: 2024-08-19                                      *
      *****************************************************************
       PROGRAM-ID.                       PUPDRANK.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT                        FRANKING
           ASSIGN                        TO DATABASE-FRANKING
           ACCESS MODE                   IS DYNAMIC
           ORGANIZATION                  IS INDEXED
           FILE STATUS                   IS WK-FRANKING-STATUS
           RECORD KEY                    IS EXTERNALLY-DESCRIBED-KEY
                                         WITH DUPLICATES.

       DATA DIVISION.
       FILE SECTION.
       FD  FRANKING
           LABEL RECORDS ARE STANDARD.
       01  FRANKING-REC.
           COPY DDS-ALL-FORMATS          OF FRANKING.

       WORKING-STORAGE SECTION.
       77  WK-SYS-TIME                   PIC X(8)       VALUE SPACES.
       77  WK-CONST-MAX-LINE             PIC 9(2)       VALUE 15.
       77  WK-CONST-MAX-COLN             PIC 9(2)       VALUE 40.
       77  WK-MAX-SCORE                  PIC 9(4).

       01  WK-SYS-DATE.
           05  WK-SYS-DATE-CC            PIC X(2)       VALUE "20".
           05  WK-SYS-DATE-YYMMDD        PIC X(6)       VALUE SPACES.
       01  WK-FILE-STATUS.
           05  WK-FRANKING-STATUS        PIC X(2)       VALUE SPACES.
       01  WK-END-LOOP-FLG               PIC X(1)       VALUE SPACE.
          88  WK-END-LOOP                               VALUE "Y".

       LINKAGE SECTION.
       01  LINK-CURR-ROUND-SCORE         PIC 9(3).

       PROCEDURE DIVISION USING          LINK-CURR-ROUND-SCORE.


       MAIN-PROC.
           PERFORM 000-INIT
           THRU    000-INIT-X.

           PERFORM 400-UPD-FRANKING
           THRU    400-UPD-FRANKING-X.

           PERFORM 600-CHK-SCORE-999
           THRU    600-CHK-SCORE-999-X.

           PERFORM 900-END
           THRU    900-END-X.

       MAIN-PROC-X.
           GOBACK.

       000-INIT.
           OPEN I-O                      FRANKING.
           ACCEPT WK-SYS-DATE-YYMMDD     FROM DATE.
           ACCEPT WK-SYS-TIME            FROM TIME.

       000-INIT-X.
           EXIT.

       400-UPD-FRANKING.
           COMPUTE WK-MAX-SCORE = WK-CONST-MAX-LINE * WK-CONST-MAX-COLN.
           IF LINK-CURR-ROUND-SCORE      > WK-MAX-SCORE
               GO TO 400-UPD-FRANKING-X
           END-IF.

           INITIALIZE FRANKING-REC.
           CALL "PGETUSERID" USING       RKPLAYER.
           IF RKPLAYER                   = SPACE
               MOVE "NOBODY"             TO RKPLAYER
           END-IF.
           COMPUTE RKSCORE               = LINK-CURR-ROUND-SCORE.
           PERFORM WRITE-FRANKING
           THRU    WRITE-FRANKING-X.

       400-UPD-FRANKING-X.
           EXIT.

       600-CHK-SCORE-999.
           INITIALIZE FRANKING-REC.
           COMPUTE RKSCORE               = 999.
           READ FRANKING INVALID KEY CONTINUE END-READ.
           IF WK-FRANKING-STATUS         = ZEROS
               GO TO 600-CHK-SCORE-999-X
           END-IF.

           MOVE "SYSTEM"                 TO RKPLAYER.
           PERFORM WRITE-FRANKING
           THRU    WRITE-FRANKING-X.

       600-CHK-SCORE-999-X.
           EXIT.

       900-END.
           CLOSE                         FRANKING.

       900-END-X.
           EXIT.

       WRITE-FRANKING.
           MOVE WK-SYS-DATE              TO RKDTE.
           MOVE WK-SYS-TIME              TO RKTIME.
           COMPUTE RKVIDEOLNK            = 0.
           WRITE FRANKING-REC INVALID KEY CONTINUE END-WRITE.

       WRITE-FRANKING-X.
           EXIT.

PGETUSERID.CLP        获取玩家ID

/**********************************************************************/
/* AUTHOR:       WITT                                                 */
/* DESCRIPTION:  GET USER ID                                          */
/* DATE WRITTEN: 2024-08-14                                           */
/**********************************************************************/
      PGM PARM(&USERID)
      DCL VAR(&USERID) TYPE(*CHAR) LEN(14) VALUE(' ')
      RTVJOBA USER(&USERID)

 END: ENDPGM

PQRYRANK.CBL        查询排行榜文件

       IDENTIFICATION DIVISION.
      *****************************************************************
      * AUTHOR:       WITT                                            *
      * DESCRIPTION:  QUREY FRANKING                                  *
      * DATE WRITTEN: 2024-08-16                                      *
      *****************************************************************
       PROGRAM-ID.                       PQRYRANK.

       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT                        FRANKING
           ASSIGN                        TO DATABASE-FRANKING
           ACCESS MODE                   IS DYNAMIC
           ORGANIZATION                  IS INDEXED
           FILE STATUS                   IS WK-FRANKING-STATUS
           RECORD KEY                    IS EXTERNALLY-DESCRIBED-KEY
                                         WITH DUPLICATES.

       DATA DIVISION.
       FILE SECTION.
       FD  FRANKING
           LABEL RECORDS ARE STANDARD.
       01  FRANKING-REC.
           COPY DDS-ALL-FORMATS          OF FRANKING.

       WORKING-STORAGE SECTION.
       77  WK-TABLE-IDX                  PIC 9(4)       VALUE 0.

       01  WK-FILE-STATUS.
           05  WK-FRANKING-STATUS        PIC X(2)       VALUE SPACES.
       01  WK-END-LOOP-FLG               PIC X(1)       VALUE SPACE.
          88  WK-END-LOOP                               VALUE "Y".

       LINKAGE SECTION.
       01  LINK-RANKING-TABLE.
           05  LINK-RANKING-TABLE-REC    OCCURS 3 TIMES.
               10  LINK-RANKING-RANK     PIC 9(4).
               10  LINK-RANKING-NAME     PIC X(14).
               10  LINK-RANKING-SCORE    PIC 9(3).
               10  LINK-RANKING-DATE     PIC X(8).

       PROCEDURE DIVISION USING          LINK-RANKING-TABLE.

       MAIN-PROC.
           PERFORM 000-INIT
           THRU    000-INIT-X.

           PERFORM 400-QRY-FRANKING
           THRU    400-QRY-FRANKING-X.

           PERFORM 900-END
           THRU    900-END-X.

       MAIN-PROC-X.
           GOBACK.

       000-INIT.
           INITIALIZE                    LINK-RANKING-TABLE.
           OPEN INPUT                    FRANKING.

       000-INIT-X.
           EXIT.

       400-QRY-FRANKING.
           PERFORM START-FRANKING-TO-LAST-REC
           THRU    START-FRANKING-TO-LAST-REC-X.

           MOVE SPACE                    TO WK-END-LOOP-FLG.
           COMPUTE WK-TABLE-IDX          = 0.
           PERFORM LOOP-FRANKING
           THRU    LOOP-FRANKING-X
           UNTIL   WK-END-LOOP.

       400-QRY-FRANKING-X.
           EXIT.

       900-END.
           CLOSE                         FRANKING.

       900-END-X.
           EXIT.

       START-FRANKING-TO-LAST-REC.
           COMPUTE RKSCORE               = 999.
           START FRANKING
               KEY IS NOT LESS THAN EXTERNALLY-DESCRIBED-KEY
               INVALID KEY CONTINUE
           END-START.

       START-FRANKING-TO-LAST-REC-X.
           EXIT.

       LOOP-FRANKING.
           IF WK-TABLE-IDX               >= 3
               SET WK-END-LOOP           TO TRUE
               GO TO LOOP-FRANKING-X
           END-IF.
           ADD 1                         TO WK-TABLE-IDX.

           READ FRANKING PRIOR AT END CONTINUE END-READ.
           IF WK-FRANKING-STATUS         NOT = ZEROS
               SET WK-END-LOOP           TO TRUE
               GO TO LOOP-FRANKING-X
           END-IF.

           COMPUTE LINK-RANKING-RANK(WK-TABLE-IDX) = WK-TABLE-IDX.
           MOVE RKPLAYER TO LINK-RANKING-NAME(WK-TABLE-IDX).
           COMPUTE LINK-RANKING-SCORE(WK-TABLE-IDX) = RKSCORE.
           MOVE RKDTE TO LINK-RANKING-DATE(WK-TABLE-IDX).

       LOOP-FRANKING-X.
           EXIT.

非功能性源码

非功能性源码对贪吃蛇游戏特性没有影响,但是对增量开发有帮助

(时时常拂拭,莫使惹尘埃  —— 《Clean Code》Robert C. Martin)

如果需要重构代码,可以使用其中的PCOMPILE程序来重编译所有的源文件,用PSAVPKG来将当前版本中的全部对象打包备份到SAVF文件里面

PCOMPILE.CLP        编译源文件

/**********************************************************************/
/* AUTHOR:       WITT                                                 */
/* DESCRIPTION:  COMPILE SNAKE SOURCE                                 */
/* DATE WRITTEN: 2024-08-15                                           */
/**********************************************************************/
      PGM
      CRTCBLPGM PGM(WITTGAMES/PSNAKEMAIN) SRCFILE(WITTGAMES/SNAKESRC) -
                SRCMBR(PSNAKEMAIN)
      CRTCBLPGM PGM(WITTGAMES/PUPDRANK) SRCFILE(WITTGAMES/SNAKESRC) -
                SRCMBR(PUPDRANK)
      CRTCBLPGM PGM(WITTGAMES/PQRYRANK) SRCFILE(WITTGAMES/SNAKESRC) -
                SRCMBR(PQRYRANK)

      CRTBNDRPG PGM(WITTGAMES/PREFSCREEN) SRCFILE(WITTGAMES/SNAKESRC) -
                SRCMBR(PREFSCREEN)

      CRTCLPGM PGM(WITTGAMES/PCOMPILE) SRCFILE(WITTGAMES/SNAKESRC) -
               SRCMBR(PCOMPILE)
      CRTCLPGM PGM(WITTGAMES/PGETUSERID) SRCFILE(WITTGAMES/SNAKESRC) -
               SRCMBR(PGETUSERID)
      CRTCLPGM PGM(WITTGAMES/PGRTAUT) SRCFILE(WITTGAMES/SNAKESRC) -
               SRCMBR(PGRTAUT)
      CRTCLPGM PGM(WITTGAMES/PRSTPKG) SRCFILE(WITTGAMES/SNAKESRC) -
               SRCMBR(PRSTPKG)
      CRTCLPGM PGM(WITTGAMES/PSAVPKG) SRCFILE(WITTGAMES/SNAKESRC) -
               SRCMBR(PSAVPKG)
      CRTCLPGM PGM(WITTGAMES/PSETENV) SRCFILE(WITTGAMES/SNAKESRC) -
               SRCMBR(PSETENV)
      CRTCLPGM PGM(WITTGAMES/PSNAKE) SRCFILE(WITTGAMES/SNAKESRC) -
               SRCMBR(PSNAKE)

 END: ENDPGM

PSAVPKG.CLP        打包备份全部对象

/**********************************************************************/
/* AUTHOR:       WITT                                                 */
/* DESCRIPTION:  BKP&RST - SAVE OBJ INTO WITTGAMPKG/SNAKEPKG          */
/* DATE WRITTEN: 2024-08-15                                           */
/**********************************************************************/
      PGM
      CLRSAVF    FILE(WITTGAMPKG/SNAKEPKG)

      SAVOBJ OBJ(*ALL) LIB(WITTGAMES) DEV(*SAVF) -
      SAVF(WITTGAMPKG/SNAKEPKG)

 END: ENDPGM

PRSTPKG.CLP        从备份中恢复全部对象

/**********************************************************************/
/* AUTHOR:       WITT                                                 */
/* DESCRIPTION:  BKP&RST - RESTORE OBJ FROM WITTGAMPKG/SNAKEPKG       */
/* DATE WRITTEN: 2024-08-15                                           */
/**********************************************************************/
      PGM PARM(&RSTLIB)
      DCL VAR(&RSTLIB) TYPE(*CHAR) LEN(10)

      MONMSG MSGID(CPF2111)
      MONMSG MSGID(CPF2125)
      MONMSG MSGID(CPF2104)

      CRTLIB LIB(&RSTLIB) TYPE(*TEST) TEXT(GAMES)

      RSTOBJ OBJ(*ALL) SAVLIB(WITTGAMES) DEV(*SAVF) -
      SAVF(WITTGAMPKG/SNAKEPKG) RSTLIB(&RSTLIB)

 END: ENDPGM

PGRTAUT.CLP        赋权限给玩家

这里给玩家赋予了排行榜文件(FRANKING)的*ALL权限,因为每轮游戏结束要将当轮的游戏结果写入到排行榜文件中,如果玩家ID没有对排行榜文件的写入权限,程序(PUPDRANK)的写入操作则会执行失败

/**********************************************************************/
/* AUTHOR:       WITT                                                 */
/* DESCRIPTION:  GRANT AUTHORITY TO PLAYERS                           */
/* DATE WRITTEN: 2024-08-09                                           */
/**********************************************************************/
      PGM
      GRTOBJAUT OBJ(WITTGAMES/*ALL) OBJTYPE(*ALL) -
      USER(*PUBLIC) AUT(*USE)

      GRTOBJAUT OBJ(WITTGAMES/FRANKING) OBJTYPE(*FILE) -
      USER(*PUBLIC) AUT(*ALL)

      GRTOBJAUT OBJ(WITTGAMPKG/*ALL) OBJTYPE(*ALL) -
      USER(*PUBLIC) AUT(*USE)

 END: ENDPGM

PMERGE.CLP        将全部源文件合并到一个PF MEMBER中

/**********************************************************************/
/* AUTHOR:       WITT                                                 */
/* DESCRIPTION:  MERGE ALL SNAKE SRC INTO MERGEDSRC                   */
/* DATE WRITTEN: 2024-08-19                                           */
/**********************************************************************/
      PGM
      MONMSG CPF3142

      CLRPFM FILE(WITTGAMES/MERGEDSRC) MBR(SNAKESRC)

      CPYF FROMFILE(WITTGAMES/SNAKESRC) TOFILE(WITTGAMES/MERGEDSRC) -
      FROMMBR(*ALL) TOMBR(SNAKESRC) MBROPT(*ADD) CRTFILE(*YES)

 END: ENDPGM

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值