Skip to content

Instantly share code, notes, and snippets.

@goabonga
Last active August 2, 2025 21:50
Show Gist options
  • Select an option

  • Save goabonga/7e38961d682863c3405568cecd4f2219 to your computer and use it in GitHub Desktop.

Select an option

Save goabonga/7e38961d682863c3405568cecd4f2219 to your computer and use it in GitHub Desktop.
Pong in SQL — Animated ASCII Pong using MySQL, Events, and phpMyAdmin

🕹️ Pong in SQL

A fully functional ASCII Pong game implemented entirely in MySQL, using:

  • A pong_state table to track ball and paddle positions
  • Stored procedures to simulate ball physics and paddle AI
  • A MySQL EVENT to update the game state every second
  • A visual render_full_frame_lines() procedure that returns a styled ASCII frame
  • A Docker setup with MySQL + phpMyAdmin for testing

🚀 Quickstart

1. Launch the containers

docker-compose up -d

2. Start the animation

./render.sh

This uses docker exec and MySQL's ASCII rendering procedure to show the game in real time.

3. View the game state manually (optional)

Go to: http://localhost:8080

Login with:

  • User: root
  • Password: root

Then run:

CALL render_full_frame_lines();

🛠️ Developer Notes

  • Ball and paddle logic is managed by move_ball()
  • AI paddles follow the ball automatically
  • The event game_tick runs every second via MySQL's EVENT SCHEDULER

💡 Tip

To reset the game manually:

CALL reset_game();

🤖 AI Assistance Disclosure

📝 This project was created with the assistance of an AI agent, guided and validated by a human developer to ensure the quality, accuracy, and relevance of the code and documentation.


Built for fun and education. Yes, SQL can play Pong 🏓

services:
db:
image: mysql:8
container_name: mysql-pong
restart: always
command: --event_scheduler=ON
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: pong
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql
- ./pong.sql:/docker-entrypoint-initdb.d/pong.sql
phpmyadmin:
image: phpmyadmin:latest
container_name: phpmyadmin-pong
restart: always
ports:
- "8080:80"
environment:
PMA_HOST: db
PMA_PORT: 3306
PMA_USER: root
PMA_PASSWORD: root
volumes:
mysql-data:
DROP DATABASE IF EXISTS pong;
CREATE DATABASE pong;
USE pong;
DROP TABLE IF EXISTS pong_state;
CREATE TABLE pong_state (
id INT PRIMARY KEY,
ball_x INT,
ball_y INT,
ball_dx INT,
ball_dy INT,
paddle1_y INT,
paddle2_y INT,
paddle_height INT,
width INT,
height INT
);
INSERT INTO pong_state VALUES (1, 5, 5, 1, 1, 4, 4, 3, 20, 10);
DELIMITER //
DROP PROCEDURE IF EXISTS move_ball;
CREATE PROCEDURE move_ball()
BEGIN
DECLARE new_x INT;
DECLARE new_y INT;
DECLARE s_width INT;
DECLARE s_height INT;
DECLARE s_dx INT;
DECLARE s_dy INT;
DECLARE s_x INT;
DECLARE s_y INT;
DECLARE p1_y INT;
DECLARE p2_y INT;
DECLARE p_h INT;
SELECT width, height, ball_dx, ball_dy, ball_x, ball_y, paddle1_y, paddle2_y, paddle_height
INTO s_width, s_height, s_dx, s_dy, s_x, s_y, p1_y, p2_y, p_h
FROM pong_state WHERE id = 1;
SET new_x = s_x + s_dx;
SET new_y = s_y + s_dy;
IF new_y <= 0 OR new_y >= s_height THEN
SET s_dy = -s_dy;
SET new_y = s_y + s_dy;
END IF;
IF new_x = 1 THEN
IF new_y BETWEEN p1_y AND (p1_y + p_h - 1) THEN
SET s_dx = -s_dx;
SET new_x = s_x + s_dx;
END IF;
ELSEIF new_x = s_width THEN
IF new_y BETWEEN p2_y AND (p2_y + p_h - 1) THEN
SET s_dx = -s_dx;
SET new_x = s_x + s_dx;
END IF;
END IF;
UPDATE pong_state
SET ball_x = new_x,
ball_y = new_y,
ball_dx = s_dx,
ball_dy = s_dy
WHERE id = 1;
END //
DROP PROCEDURE IF EXISTS reset_game;
CREATE PROCEDURE reset_game()
BEGIN
UPDATE pong_state
SET ball_x = 5,
ball_y = 5,
ball_dx = 1,
ball_dy = 1,
paddle1_y = 4,
paddle2_y = 4;
END //
DROP PROCEDURE IF EXISTS render_full_frame_lines;
CREATE PROCEDURE render_full_frame_lines()
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE j INT;
DECLARE row_line TEXT;
DECLARE s_x INT;
DECLARE s_y INT;
DECLARE p1_y INT;
DECLARE p2_y INT;
DECLARE p_h INT;
DECLARE w INT;
DECLARE h INT;
SELECT ball_x, ball_y, paddle1_y, paddle2_y, paddle_height, width, height
INTO s_x, s_y, p1_y, p2_y, p_h, w, h
FROM pong_state
WHERE id = 1;
-- Top border
SELECT CONCAT('+', REPEAT('-', w), '+') AS frame_line;
WHILE i < h DO
SET row_line = '|';
SET j = 1;
WHILE j <= w DO
IF j = 1 AND i BETWEEN p1_y AND p1_y + p_h - 1 THEN
SET row_line = CONCAT(row_line, '|');
ELSEIF j = w AND i BETWEEN p2_y AND p2_y + p_h - 1 THEN
SET row_line = CONCAT(row_line, '|');
ELSEIF j = s_x AND i = s_y THEN
SET row_line = CONCAT(row_line, 'o');
ELSE
SET row_line = CONCAT(row_line, '*');
END IF;
SET j = j + 1;
END WHILE;
SET row_line = CONCAT(row_line, '|');
SELECT row_line AS frame_line;
SET i = i + 1;
END WHILE;
-- Bottom border
SELECT CONCAT('+', REPEAT('-', w), '+') AS frame_line;
END //
DROP EVENT IF EXISTS game_tick;
CREATE EVENT game_tick
ON SCHEDULE EVERY 1 SECOND
ON COMPLETION PRESERVE
ENABLE
DO
BEGIN
CALL move_ball();
UPDATE pong_state
SET paddle1_y = LEAST(GREATEST(ball_y - 1, 0), height - paddle_height),
paddle2_y = LEAST(GREATEST(ball_y - 1, 0), height - paddle_height)
WHERE id = 1;
END //
DELIMITER ;
#!/bin/bash
watch -n1 \
'docker exec mysql-pong mysql -uroot -proot -D pong -sN -e "CALL render_full_frame_lines()"'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment