Viết game rắn săn mồi trong PL/SQL

Đoạn mã dưới đây là phiên bản đơn giản hóa và mô phỏng dựa trên text của trò chơi Snake. Nó sử dụng các cấu trúc PL/SQL để mô phỏng chuyển động của con rắn, tạo thức ăn và kiểm tra va chạm với tường và chính nó.

CREATE OR REPLACE PROCEDURE snake_game AS
    TYPE position_type IS RECORD (row NUMBER, col NUMBER);
    TYPE snake_type IS TABLE OF position_type INDEX BY PLS_INTEGER;
    
    v_grid_size CONSTANT NUMBER := 10; -- Size of the grid (10x10).
    v_initial_snake_length CONSTANT NUMBER := 4; -- Initial length of the snake.
    
    v_snake snake_type;
    v_snake_length NUMBER := v_initial_snake_length;
    v_food position_type;
    v_game_over BOOLEAN := FALSE;
    v_direction CHAR := 'R'; -- Initial direction (Right).
    
    PROCEDURE draw_grid IS
    BEGIN
        FOR i IN 1..v_grid_size LOOP
            FOR j IN 1..v_grid_size LOOP
                IF (i = v_food.row AND j = v_food.col) THEN
                    DBMS_OUTPUT.PUT('|F');
                ELSE
                    DBMS_OUTPUT.PUT('| ');
                END IF;
            END LOOP;
            DBMS_OUTPUT.PUT_LINE('|');
        END LOOP;
    END draw_grid;
    
    PROCEDURE generate_food IS
    BEGIN
        v_food.row := DBMS_RANDOM.VALUE(1, v_grid_size + 1);
        v_food.col := DBMS_RANDOM.VALUE(1, v_grid_size + 1);
    END generate_food;
    
    PROCEDURE move_snake(p_direction IN CHAR) IS
        v_head_row NUMBER := v_snake(v_snake_length).row;
        v_head_col NUMBER := v_snake(v_snake_length).col;
    BEGIN
        CASE p_direction
            WHEN 'U' THEN v_head_row := v_head_row - 1; -- Up
            WHEN 'D' THEN v_head_row := v_head_row + 1; -- Down
            WHEN 'L' THEN v_head_col := v_head_col - 1; -- Left
            WHEN 'R' THEN v_head_col := v_head_col + 1; -- Right
        END CASE;
        
        -- Check for collision with the food
        IF (v_head_row = v_food.row AND v_head_col = v_food.col) THEN
            v_snake_length := v_snake_length + 1;
            generate_food;
        END IF;
        
        -- Update the snake's position
        FOR i IN v_snake_length DOWN TO 2 LOOP
            v_snake(i) := v_snake(i - 1);
        END LOOP;
        v_snake(1).row := v_head_row;
        v_snake(1).col := v_head_col;
    END move_snake;
    
BEGIN
    -- Initialize the snake's starting position
    FOR i IN 1..v_initial_snake_length LOOP
        v_snake(i).row := 1;
        v_snake(i).col := i;
    END LOOP;
    
    -- Generate the initial food position
    generate_food;
    
    -- Main game loop
    LOOP
        -- Clear the screen
        DBMS_OUTPUT.PUT_LINE(chr(27) || '[2J');
        
        -- Draw the grid
        draw_grid;
        
        -- Move the snake
        move_snake(v_direction);
        
        -- Check for collision with the walls
        IF (v_snake(1).row <= 0 OR v_snake(1).row > v_grid_size OR
            v_snake(1).col <= 0 OR v_snake(1).col > v_grid_size) THEN
            v_game_over := TRUE;
        END IF;
        
        -- Check for collision with itself
        FOR i IN 2..v_snake_length LOOP
            IF (v_snake(i).row = v_snake(1).row AND v_snake(i).col = v_snake(1).col) THEN
                v_game_over := TRUE;
            END IF;
        END LOOP;
        
        -- Check for game over condition
        IF v_game_over THEN
            DBMS_OUTPUT.PUT_LINE('Game Over!');
            EXIT;
        END IF;
        
        -- Sleep for a short period to control the game speed
        DBMS_LOCK.SLEEP(0.5);
    END LOOP;
    
EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE('An error occurred: ' || SQLERRM);
END;