71 Commits

Author SHA1 Message Date
andrea
f88de159f5 restore default values
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
Arduino Pong CD / release (push) Has been cancelled
2026-03-21 13:59:03 +01:00
andrea
fae479ce90 clean code adding a menu class
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-21 13:52:54 +01:00
andrea
d65e7ef1c8 fix delay on level difficulty choice 2026-03-21 12:20:06 +01:00
andrea
8c402ff13a return to main menu after game finishes
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
Arduino Pong CD / release (push) Has been cancelled
2026-03-21 12:16:07 +01:00
andrea
d544867269 return to main menu after game finishes
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
Arduino Pong CD / release (push) Has been cancelled
2026-03-21 12:12:39 +01:00
andrea
d18415f472 select difficulty level (when play against bot) using a dedicated frame to show a graphics menu
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-21 12:10:52 +01:00
andrea
cef9f09c79 render visual menu on led matrix to easly allow user choose the game modes 2026-03-21 11:19:40 +01:00
andrea
a9aed6b8d0 choose game mode
pvp, pvc, cvc
2026-03-21 10:24:15 +01:00
andrea
7f63ae9a31 Revert "allow user choose between game modes, pvp, pvc, cvc"
i need to test it

This reverts commit a6c218061d.
2026-03-19 23:51:45 +01:00
andrea
a6c218061d allow user choose between game modes, pvp, pvc, cvc 2026-03-19 23:28:34 +01:00
andrea
8e5fe2143c random shift ball vertical position after score 2026-03-19 22:32:01 +01:00
andrea
fbad1a87d6 min 2026-03-19 22:24:23 +01:00
andrea
4bb6d6d6ee start thinking about MENU status 2026-03-19 22:23:29 +01:00
andrea
6ea443d92a single player with difficlut level balancement 2026-03-19 20:34:16 +01:00
andrea
2838b5fd14 start implementi difficulty level, but needs some balancements 2026-03-19 19:38:10 +01:00
andrea
69a8fb9dc4 first bot implementation
bot is now able to automatically move, but for now is too skilled, i need to reduce his skills
2026-03-19 18:49:58 +01:00
andrea
eac8c59d96 reframe check_pad_movement logics and inherith the base Paddle class 2026-03-19 18:26:27 +01:00
andrea
68dfce8b12 Merge branch 'master.use_classes'
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
Arduino Pong CD / release (push) Has been cancelled
2026-03-18 21:45:39 +01:00
andrea
49a772f6de clean logs and improve engine handling by removing hits and movment_delay from the loop to the engine 2026-03-18 21:41:51 +01:00
andrea
abc0ddb661 fix collision state 2026-03-18 20:49:25 +01:00
andrea
33e070c442 added renderer.cpp/h files 2026-03-18 20:38:31 +01:00
andrea
1f6e65c5ec renderer class 2026-03-18 19:46:52 +01:00
andrea
7afa70c9d8 fix ball bouncing on pad behavior
now the ball bounces on pad when hit the pad, not when it reaches the edge of the "screen"
2026-03-18 19:12:09 +01:00
andrea
792c0f835c clean main sketch file 2026-03-18 19:00:50 +01:00
andrea
5afa726015 minors 2026-03-18 18:24:52 +01:00
andrea
48fef4be86 move some logics to loop() 2026-03-18 18:12:38 +01:00
andrea
48c2610561 TODO 2026-03-17 23:43:37 +01:00
andrea
3d8881ab3d total reframe with classes 2026-03-17 23:25:30 +01:00
andrea
d8efabd6da start refactoring pong entities to use classes 2026-03-17 20:11:21 +01:00
andrea
c774e11923 Improve arduino_pong.ino using enum to handle game statuses and switch case for better readibility and little memory improvements
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
Arduino Pong CD / release (push) Has been cancelled
2026-03-17 18:42:48 +01:00
andrea
b3f6aeb3fe Show scores on the matrix, show winner message once someone reches 9, add start ball movement delay on start and restart after score, and fix uncentered ball on restart
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-16 22:37:53 +01:00
andrea
6e5fc8ea10 change digital pins to avoid collisions with pin13 for L Led on the board
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
Arduino Pong CD / release (push) Has been cancelled
2026-03-15 22:43:00 +01:00
andrea
28f029ce03 update ci.yml with new libs structure
Some checks failed
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-15 21:15:49 +01:00
andrea
cb42a45aa7 move all libs into src
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-15 21:14:04 +01:00
andrea
c47291e258 minors
Some checks failed
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CD / release (push) Has been cancelled
2026-03-15 20:29:56 +01:00
andrea
fec4db0c5d improve rerender logics
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-15 20:23:52 +01:00
andrea
7c065d8799 updated ci.yml
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-15 20:09:21 +01:00
andrea
31891bbc0e minors
Some checks failed
Arduino Pong CD / release (push) Has been cancelled
2026-03-15 20:02:27 +01:00
andrea
5eeb735364 split main .ino fine into multiple sub files
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-15 19:56:20 +01:00
andrea
a938adff31 generate build files for cd
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
Arduino Pong CD / release (push) Has been cancelled
2026-03-15 18:19:48 +01:00
andrea
1f143520a8 add cd status on README 2026-03-15 18:13:58 +01:00
andrea
cbdfcce354 cd permissions
Some checks failed
Arduino Pong CD / release (push) Has been cancelled
2026-03-15 18:11:53 +01:00
andrea
66957b5e19 remove unecessary lib from cd
Some checks failed
Arduino Pong CD / release (push) Has been cancelled
2026-03-15 18:09:13 +01:00
andrea
f12af76845 minor
Some checks failed
Arduino Pong CD / release (push) Has been cancelled
2026-03-15 18:07:15 +01:00
andrea
8906b2eab5 first test with CD
Some checks failed
Arduino Pong CD / release (push) Has been cancelled
2026-03-15 18:04:59 +01:00
andrea
5b94e21f21 minor text
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-15 17:08:59 +01:00
andrea
8a7bd2b192 define matrix led size 2026-03-15 17:07:03 +01:00
andrea
487756978f fix duplicated frame initialization
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-15 16:22:28 +01:00
andrea
bf2b794421 Add full arduino-cli installation and preparation on Makefile and redefine the CI istructions
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-15 13:38:54 +01:00
andrea
f0e8a7f05f CI typo 2026-03-15 13:00:09 +01:00
andrea
2727dfb0ef ci.yml
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-15 12:28:08 +01:00
andrea
bb79c885a9 update arduino/setup-arduino-cli@v2
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-15 12:13:50 +01:00
andrea
cd9d4289ef update actions/checkout version to use node24 and fix CI warnings
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-15 12:09:05 +01:00
andrea
d15ae7ba57 exectue CI only when selected files are modified
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-15 12:05:30 +01:00
andrea
1337269d32 updated README.md with CI actions badge and license bade
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-15 12:02:14 +01:00
andrea
69386ba4da added CI build test also for mac and windows
Some checks failed
Arduino Pong CI / build (macos-latest) (push) Has been cancelled
Arduino Pong CI / build (ubuntu-latest) (push) Has been cancelled
Arduino Pong CI / build (windows-latest) (push) Has been cancelled
2026-03-15 11:54:10 +01:00
andrea
455014fdff Improved Makefile 2026-03-15 11:52:26 +01:00
andrea
280e681ba6 ci: add github actions workflow for automatic compilation
Some checks failed
Arduino Pong CI / build (push) Has been cancelled
2026-03-15 11:37:03 +01:00
andrea
f710fb2a0e updated README 2026-03-15 11:19:36 +01:00
andrea
748cf6a678 updated preview image 2026-03-15 11:14:13 +01:00
andrea
b1b421c367 updated preview image 2026-03-15 11:08:52 +01:00
andrea
abc381f397 image preview 2026-03-15 10:44:18 +01:00
andrea
5ebf74e044 more detailed README.md 2026-03-15 10:43:35 +01:00
andrea
486ee4df97 gradually increase ball speed if noone scores
when someone scores a point, speed is resetted to the initial speed
2026-03-15 10:05:03 +01:00
andrea
b9bc138cad small refactoring to keep code more cleaning
and fixed a crash when the ball goes out of matrix
in any case now move_ball() function, now prevents
the ball to goes out, if ball coordinates
are out of the matrix, ball restarts from initial position
without assigning any point
2026-03-15 09:39:12 +01:00
andrea
e7f0c3cc1a optimize rerender matrix, do it only if something is changed from the last render 2026-03-14 23:47:44 +01:00
andrea
fa47480aef move render_matrix() on the main loop, to get smoother players movements 2026-03-14 22:07:18 +01:00
andrea
adf2498344 players can hit the ball 2026-03-14 21:53:39 +01:00
andrea
fcafc56012 move players and ball 2026-03-14 21:37:29 +01:00
andrea
ebc6596dce move players and ball 2026-03-14 21:35:19 +01:00
andrea
f2a6a02005 define arduino_pong.ino and Makefile to compile and run software from cli 2026-03-14 20:22:06 +01:00
20 changed files with 1270 additions and 2 deletions

37
.github/workflows/cd.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Arduino Pong CD
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Arduino CLI
run: |
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh
echo "./bin" >> $GITHUB_PATH
- name: Install Board Core and Libs
run: |
arduino-cli core update-index
arduino-cli core install arduino:renesas_uno
- name: Build Binary
run: make compile
- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: |
build/*.bin
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

39
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: Arduino Pong CI
on:
push:
branches: [ master ]
paths:
- 'arduino_pong.ino'
- 'Makefile'
- '.github/workflows/ci.yml'
- 'src/*.cpp'
- 'src/*.h'
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Code
uses: actions/checkout@v6
- name: Setup Arduino CLI
#uses: arduino/setup-arduino-cli@v2
shell: bash
run: |
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh
echo "./bin" >> $GITHUB_PATH
- name: Install UNO R4 Core
run: |
arduino-cli core update-index
arduino-cli core install arduino:renesas_uno
- name: Compile Sketch (via Makefile)
shell: bash
run: make prepare_and_compile

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
.secrets
.arduino_cache/
bin/
build/

63
Makefile Normal file
View File

@@ -0,0 +1,63 @@
PORT ?= /dev/ttyACM0
BOARD = arduino:renesas_uno:unor4wifi
SKETCH = arduino_pong.ino
CACHE_DIR = $(shell pwd)/.arduino_cache
BIN_DIR = $(shell pwd)/bin
CLI = $(BIN_DIR)/arduino-cli --config-file $(CACHE_DIR)/arduino-cli.yaml
YELLOW := \033[0;33m
GREEN := \033[0;32m
CLEAR := \033[0m
prepare:
@mkdir -p $(BIN_DIR)
@mkdir -p $(CACHE_DIR)
@if [ ! -f $(BIN_DIR)/arduino-cli ]; then \
echo -e "📥 $(YELLOW)Downloading arduino-cli...$(CLEAR)"; \
curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | BINDIR=$(BIN_DIR) sh; \
fi
@if [ ! -f $(CACHE_DIR)/arduino-cli.yaml ]; then \
echo -e "⚙️ $(YELLOW)Initializing local config...$(CLEAR)"; \
$(CLI) config init --dest-dir $(CACHE_DIR); \
$(CLI) config set directories.data $(CACHE_DIR)/data; \
$(CLI) config set directories.downloads $(CACHE_DIR)/downloads; \
$(CLI) config set directories.user $(CACHE_DIR)/user; \
fi
@if ! $(CLI) core list | grep -q "arduino:renesas_uno"; then \
echo -e "$(YELLOW)📥 Installing board core...$(CLEAR)"; \
$(CLI) core update-index; \
$(CLI) core install arduino:renesas_uno; \
fi
compile:
@mkdir -p build
@echo -e "$(GREEN)🛠️ Compiling $(SKETCH)...$(CLEAR)"
@$(CLI) compile -b $(BOARD) --output-dir build $(SKETCH)
clean:
@echo -e "$(GREEN)🛠️ Removing build files...$(CLEAR)"
@rm -rf build/
prepare_and_compile:
make prepare --no-print-directory
make compile --no-print-directory
upload:
@echo -e "$(GREEN)🚀 Uploading $(SKETCH)...$(CLEAR)"
@$(CLI) upload -b $(BOARD) -p $(PORT)
upload_verbose:
@echo -e "$(GREEN)🚀 Uploading (Verbose) $(SKETCH)...$(CLEAR)"
@$(CLI) upload -b $(BOARD) -p $(PORT) -v
monitor:
@$(CLI) monitor -p $(PORT)
run:
make compile --no-print-directory
make upload --no-print-directory
make monitor --no-print-directory
run_init:
make prepare --no-print-directory
make run --no-print-directory

View File

@@ -1,3 +1,68 @@
# arduino_pong
# 🏓 Arduino UNO R4 WiFi Pong
Play Pong on Arduino UNO R4 WiFi LED Matrix
A classic implementation of the Pong game developed specifically for the Arduino UNO R4 WiFi, utilizing its built-in 12×8 LED matrix as the game screen.
[![Arduino Pong CI](https://github.com/Dea1993/arduino_pong/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/Dea1993/arduino_pong/actions/workflows/ci.yml)
[![Arduino Pong CD](https://github.com/Dea1993/arduino_pong/actions/workflows/cd.yml/badge.svg)](https://github.com/Dea1993/arduino_pong/actions/workflows/cd.yml)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](LICENSE)
# 📹 Preview
![Pong](assets/preview.png)
Youtube: https://youtu.be/ouLBTDjpKqc
# 📝 Description
This project transforms your Arduino board into a minimalist handheld console. By leveraging the Arduino_LED_Matrix library, the game manages ball physics, collision detection with walls and paddles, and a scoring system displayed via the Serial Monitor.
Key Features:
- Integrated Display: No external modules required; it uses the native 12×8 LED matrix.
- Local Multiplayer: Two-player support using 4 external pushbuttons.
- Dynamic Difficulty: The ball speed periodically increases to keep the gameplay challenging.
- Real-time Scoring: Points are tracked and sent via Serial (9600 baud).
# 🛠️ Hardware Requirements
- Board: Arduino UNO R4 WiFi.
- Buttons: 4x Momentary Pushbuttons.
- Resistors: Not required (uses internal INPUT_PULLUP).
- Breadboard & Jumper wires.
# 🖮 Pinout Configuration
Component Arduino Pin Function
- P1 Button Up D13 Moves Left Paddle Up
- P1 Button Down D12 Moves Left Paddle Down
- P2 Button Up D11 Moves Right Paddle Up
- P2 Button Down D10 Moves Right Paddle Down
- Common GND GND Ground for all buttons
# 🕹️ Game Logic
- Bouncing: When the ball hits a paddle, the X direction is reversed and the hits counter increases.
- Speed Scaling: Every 6 successful hits (hits >= 6), the loop_delay decreases by 20ms, speeding up the action until a minimum limit of 80ms is reached.
- Scoring: If a player misses the ball, the opponent scores a point. The ball then resets to the center with a randomized direction.
# 🛠️ Development
This project includes a `Makefile` to automate the workflow using `arduino-cli`.
### Prerequisites
* [Arduino CLI](https://arduino.github.io/arduino-cli/latest/) installed.
* Arduino UNO R4 core installed:
`arduino-cli core install arduino:renesas_uno`
### Commands
| Command | Description |
| :--- | :--- |
| `make prepare` | Downloads arduino-cli locally and installs the UNO R4 core. |
| `make compile` | Compiles the sketch without uploading. |
| `make upload` | Uploads the compiled binary to `/dev/ttyACM0`. |
| `make monitor` | Opens the Serial Monitor. |
| `make run` | Full cycle: Compile + Upload + Monitor. |
| `make run_init` | Full first-time setup: Prepare env + Compile + Upload + Monitor. |
> **Note:** If your board is on a different port, edit the `Makefile` or override it: `make upload PORT=/dev/ttyUSB0`.

216
arduino_pong.ino Normal file
View File

@@ -0,0 +1,216 @@
#include "Arduino_LED_Matrix.h"
#include "src/config.h"
#include "src/menu.h"
#include "src/renderer.h"
#include "src/engine.h"
#include "src/paddle.h"
#include "src/ball.h"
// initial pong frame matrix
byte frame[MATRIX_HEIGHT][MATRIX_WIDTH] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
ArduinoLEDMatrix matrix;
bool need_refresh= true;
uint8_t hits= 0;
long exec_t2= millis();
enum game_statuses : uint8_t {
MENU,
MENU_BOT_SKILLS,
TIMER,
RUN,
SCORE,
GAMEOVER,
WAIT,
};
game_statuses game_status= MENU;
Ball ball(4, 6);
Paddle* p1= nullptr;
Paddle* p2= nullptr;
HumanPaddle human_pad1(1, P1_BTN_UP, P1_BTN_BOTTOM);
HumanPaddle human_pad2(4, P2_BTN_UP, P2_BTN_BOTTOM);
BotPaddle bot_pad1(1, 0);
BotPaddle bot_pad2(4, MATRIX_WIDTH-1);
Menu menu;
// uint8_t current_gmode_idx= 0;
// bool update_menu= true;
// bool mode_selected= false;
// uint8_t current_bot_menu_idx= 0;
// bool update_menu_bot_skills= true;
Engine engine(ball, INITIAL_BALL_DELAY);
Renderer renderer(ball, frame, matrix);
void setup() {
Serial.begin(9600);
// start LED matrix
matrix.begin();
pinMode(P1_BTN_UP, INPUT_PULLUP);
pinMode(P1_BTN_BOTTOM, INPUT_PULLUP);
pinMode(P2_BTN_UP, INPUT_PULLUP);
pinMode(P2_BTN_BOTTOM, INPUT_PULLUP);
randomSeed(millis());
}
void loop() {
long exec_t1= millis();
switch (game_status) {
case MENU: {
// switch modes
if (digitalRead(P2_BTN_BOTTOM) == LOW) {
menu.next_mode();
const byte (*current_gmode)[12]= frame_gmodes[menu.get_mode()];
matrix.loadPixels((uint8_t*)current_gmode, MATRIX_HEIGHT * MATRIX_WIDTH);
delay(300);
}
else if (digitalRead(P2_BTN_UP) == LOW) {
menu.prev_mode();
const byte (*current_gmode)[12]= frame_gmodes[menu.get_mode()];
matrix.loadPixels((uint8_t*)current_gmode, MATRIX_HEIGHT * MATRIX_WIDTH);
delay(300);
}
// 1. P vs P
else if (digitalRead(P1_BTN_UP) == LOW || digitalRead(P1_BTN_BOTTOM) == LOW && menu.number_of_bots() == 0) {
p1= &human_pad1;
p2= &human_pad2;
engine.set_players(p1, p2);
renderer.set_players(p1, p2);
game_status= TIMER;
}
// 2. P vs CPU
else if (digitalRead(P1_BTN_UP) == LOW || digitalRead(P1_BTN_BOTTOM) == LOW && menu.number_of_bots() == 1) {
p1= &human_pad1;
p2= &bot_pad2;
engine.set_players(p1, p2);
renderer.set_players(p1, p2);
game_status= MENU_BOT_SKILLS;
delay(300); // avoid accidental double click for next menu
}
// 3. CPU vs CPU
else if (digitalRead(P1_BTN_UP) == LOW || digitalRead(P1_BTN_BOTTOM) == LOW && menu.number_of_bots() == 2) {
p1= &bot_pad1;
p2= &bot_pad2;
engine.set_players(p1, p2);
renderer.set_players(p1, p2);
game_status= MENU_BOT_SKILLS;
delay(300); // avoid accidental double click for next menu
}
else {
const byte (*current_gmode)[12]= frame_gmodes[menu.get_mode()];
matrix.loadPixels((uint8_t*)current_gmode, MATRIX_HEIGHT * MATRIX_WIDTH);
}
break;
}
case MENU_BOT_SKILLS: {
// switch difficulty level
if (digitalRead(P2_BTN_BOTTOM) == LOW) {
menu.increase_skills();
const byte (*current_skill_frame)[12]= frame_bot_skills[menu.get_skill()];
matrix.loadPixels((uint8_t*)current_skill_frame, MATRIX_HEIGHT * MATRIX_WIDTH);
delay(300);
}
else if (digitalRead(P2_BTN_UP) == LOW) {
menu.decrease_skills();
const byte (*current_skill_frame)[12]= frame_bot_skills[menu.get_skill()];
matrix.loadPixels((uint8_t*)current_skill_frame, MATRIX_HEIGHT * MATRIX_WIDTH);
delay(300);
}
// choose difficulty level
else if (digitalRead(P1_BTN_UP) == LOW || digitalRead(P1_BTN_BOTTOM) == LOW) {
if (!p1 -> is_human()) p1 -> set_skills(menu.get_skill() + 1);
if (!p2 -> is_human()) p2 -> set_skills(menu.get_skill() + 1);
game_status= TIMER;
}
else {
const byte (*current_skill_frame)[12]= frame_bot_skills[menu.get_skill()];
matrix.loadPixels((uint8_t*)current_skill_frame, MATRIX_HEIGHT * MATRIX_WIDTH);
}
break;
}
case TIMER:
for (int i = START_TIMER; i >= 0; i--) {
renderer.render_timer(i);
delay(1000);
}
game_status= RUN;
// delay the first ball movement
exec_t2= millis() + FIRST_START_BALL_DELAY;
break;
case RUN:
need_refresh= engine.control_players();
if (exec_t1 - exec_t2 > engine.ball_movement_delay()) {
engine.run();
if (engine.get_event() == P1SCORE || engine.get_event() == P2SCORE)
game_status= SCORE;
exec_t2= exec_t1;
need_refresh= true;
}
// rerender matrix only if something is changed
if (need_refresh) {
renderer.render_matrix();
need_refresh= false;
}
break;
case SCORE:
delay(300);
renderer.render_score();
engine.restart_ball();
delay(1000);
if (p1 -> get_score() >= MAX_POINTS || p2 -> get_score() >= MAX_POINTS)
game_status= GAMEOVER;
else {
game_status= RUN;
// before move again the ball wait a second
renderer.render_matrix();
exec_t2= millis() + FIRST_START_BALL_DELAY;
}
break;
case GAMEOVER:
renderer.render_winner();
game_status= WAIT;
break;
case WAIT:
// keep showing the winner waiting for a restart
// restart game once one button is pressed
if (digitalRead(P1_BTN_UP) == LOW || digitalRead(P1_BTN_BOTTOM) == LOW ||
digitalRead(P2_BTN_UP) == LOW || digitalRead(P2_BTN_BOTTOM) == LOW) {
engine.reset();
game_status= MENU;
delay(300);
}
break;
}
delay(50);
}

BIN
assets/preview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 847 KiB

52
src/ball.cpp Normal file
View File

@@ -0,0 +1,52 @@
#include "ball.h"
void Ball::_init_directions(int8_t &_direction) {
if (random(2) == 0) _direction= 1;
else _direction= -1;
}
void Ball::move() {
if (!_direction_x) this -> _init_directions(_direction_x);
if (!_direction_y) this -> _init_directions(_direction_y);
// if (_x < 0 || _x > MATRIX_WIDTH-1 || _y < 0 || _y > MATRIX_HEIGHT-1) {
// this -> reset_position();
// }
if (_x + _direction_x >= 0 && _x + _direction_x < MATRIX_WIDTH)
_x+= _direction_x;
if (_y + _direction_y >= 0 && _y + _direction_y < MATRIX_HEIGHT)
_y+= _direction_y;
}
void Ball::bounce_on_pad() {
_direction_x *= -1;
}
void Ball::bounce_on_sides() {
_direction_y *= -1;
}
int8_t Ball::get_direction_x() {
return _direction_x;
}
int8_t Ball::get_direction_y() {
return _direction_y;
}
void Ball::reset_position () {
_x= BALL_RESET_X;
int8_t delta= random(3);
if (random(2) == 0) delta *= -1;
_y= BALL_RESET_Y + delta;
this -> _init_directions(_direction_x);
this -> _init_directions(_direction_y);
}
uint8_t Ball::get_x() {
return _x;
}
uint8_t Ball::get_y() {
return _y;
}

27
src/ball.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef BALL_H
#define BALL_H
#include <Arduino.h>
#include "config.h"
class Ball {
private:
uint8_t _x, _y;
int8_t _direction_x, _direction_y;
void _init_directions(int8_t &_direction);
public:
Ball (uint8_t _x, uint8_t _y) : _x(_x), _y(_y) {}
void move();
void bounce_on_pad();
void bounce_on_sides();
int8_t get_direction_x();
int8_t get_direction_y();
void reset_position ();
uint8_t get_x();
uint8_t get_y();
};
#endif

15
src/config.h Normal file
View File

@@ -0,0 +1,15 @@
#define P1_BTN_UP 12
#define P1_BTN_BOTTOM 11
#define P2_BTN_UP 10
#define P2_BTN_BOTTOM 9
#define MATRIX_WIDTH 12
#define MATRIX_HEIGHT 8
#define BALL_RESET_X (MATRIX_WIDTH / 2)
#define BALL_RESET_Y (MATRIX_HEIGHT / 2)
#define PADDLE_LENGTH 3
#define INITIAL_BALL_DELAY 200
#define MAX_POINTS 9
#define START_TIMER 3
#define FIRST_START_BALL_DELAY 500

96
src/engine.cpp Normal file
View File

@@ -0,0 +1,96 @@
#include "engine.h"
bool Engine::_check_pad_ball_collision(Paddle *p) {
uint8_t ppos= p -> get_position();
for (int p= ppos; p < ppos + PADDLE_LENGTH; p++) {
if (_ball.get_y() == p) {
return true;
}
}
return false;
}
void Engine::run() {
// if (_event == P1SCORE || _event == P2SCORE) this -> _soft_reset();
_event= NONE;
_ball.move();
uint8_t bx= _ball.get_x();
uint8_t by= _ball.get_y();
int8_t ball_dir= _ball.get_direction_x();
// pad is 1 pixel far from the edge, so i need to calc this delta
// check also if the ball is already moving away, to skip useless checks
if (bx <= 1 && ball_dir < 0) {
// score the point only if ball reached the edge
if (this -> _check_pad_ball_collision(_p1) && bx <= 1) {
_ball.bounce_on_pad();
_event= P1_COLLISION;
_hits++;
}
else if (bx <= 0) {
// p2 scores
_p2 -> increase_score();
_event= P2SCORE;
return;
}
}
else if (bx >= MATRIX_WIDTH-2 && ball_dir > 0) {
// score the point only if ball reached the edge
if (this -> _check_pad_ball_collision(_p2) && bx >= MATRIX_WIDTH-2) {
_ball.bounce_on_pad();
_event= P2_COLLISION;
_hits++;
}
else if (bx >= MATRIX_WIDTH-1) {
// p1 scores
_p1 -> increase_score();
_event= P1SCORE;
return;
}
}
if (by == 0 || by == MATRIX_HEIGHT-1) {
_ball.bounce_on_sides();
_event= WALL_COLLISION;
}
if (_hits >= 6 && _ball_mv_delay >= 80) {
_hits= 0;
_ball_mv_delay -= 20;
}
}
void Engine::set_players(Paddle *p1, Paddle *p2) {
_p1= p1;
_p2= p2;
}
bool Engine::control_players() {
bool need_refresh= false;
if (_p1 -> is_human()) need_refresh |= _p1 -> check_pad_movement();
else need_refresh |= _p1 -> check_pad_movement(_ball);
if (_p2 -> is_human()) need_refresh |= _p2 -> check_pad_movement();
else need_refresh |= _p2 -> check_pad_movement(_ball);
return need_refresh;
}
uint8_t Engine::ball_movement_delay() {
return _ball_mv_delay;
}
EngineEvents Engine::get_event() {
return _event;
}
void Engine::restart_ball() {
_ball.reset_position();
_ball_mv_delay= INITIAL_BALL_DELAY;
}
void Engine::reset() {
this -> restart_ball();
_p1 -> reset();
_p2 -> reset();
}

37
src/engine.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef ENGINE_H
#define ENGINE_H
#include <Arduino.h>
#include "ball.h"
#include "paddle.h"
#include "config.h"
enum EngineEvents : uint8_t {NONE, P1SCORE, P2SCORE, P1_COLLISION, P2_COLLISION, WALL_COLLISION};
class Engine {
private:
Paddle* _p1;
Paddle* _p2;
Ball& _ball;
EngineEvents _event= NONE;
uint8_t _ball_mv_delay;
uint8_t _hits = 0;
bool _check_pad_ball_collision(Paddle *p);
public:
// inizialize Engine constructor, linking all args with private args
Engine(Ball &ball, uint8_t ball_mv_delay)
: _ball(ball), _ball_mv_delay(ball_mv_delay) {}
void run();
void set_players(Paddle *p_one, Paddle *p_two);
bool control_players();
uint8_t ball_movement_delay();
EngineEvents get_event();
void restart_ball();
void reset();
};
#endif

238
src/font.cpp Normal file
View File

@@ -0,0 +1,238 @@
#include "font.h";
const uint32_t pone_wins[5][4] = {
{
0x78,
0xc4847844,
0x440e000,
500
},
{
0x11,
0x1101501,
0x501f0000,
500
},
{
0xe,
0x400400,
0x400e0000,
500
},
{
0x9,
0xd00b00,
0x90090000,
500
},
{
0xf,
0x800f00,
0x100f0000,
500
}
};
const uint32_t ptwo_wins[5][4] = {
{
0x79,
0xe48279e4,
0x1041e000,
500
},
{
0x11,
0x1101501,
0x501f0000,
500
},
{
0xe,
0x400400,
0x400e0000,
500
},
{
0x9,
0xd00b00,
0x90090000,
500
},
{
0xf,
0x800f00,
0x100f0000,
500
}
};
const byte font_pong[10][8][3] = {
// Number 0
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 1 },
{ 1, 0, 1 },
{ 1, 0, 1 },
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 0 }
},
// Number 1
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 0 },
{ 0, 1, 0 },
{ 0, 1, 0 },
{ 0, 1, 0 },
{ 1, 1, 1 },
{ 0, 0, 0 }
},
// Number 2
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 1 },
{ 0, 0, 1 },
{ 1, 1, 1 },
{ 1, 0, 0 },
{ 1, 1, 1 },
{ 0, 0, 0 }
},
// Number 3
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 1 },
{ 0, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 0 }
},
// Number 4
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 0, 1 },
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 1 },
{ 0, 0, 1 },
{ 0, 0, 0 }
},
// Number 5
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 1 },
{ 1, 0, 0 },
{ 1, 1, 1 },
{ 0, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 0 }
},
// Number 6
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 0, 0 },
{ 1, 0, 0 },
{ 1, 1, 1 },
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 0 }
},
// Number 7
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 1 },
{ 0, 0, 1 },
{ 0, 0, 1 },
{ 0, 0, 1 },
{ 0, 0, 1 },
{ 0, 0, 0 }
},
// Number 8
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 1 },
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 0 }
},
// Number 9
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1, 1, 1 },
{ 1, 0, 1 },
{ 1, 1, 1 },
{ 0, 0, 1 },
{ 0, 0, 1 },
{ 0, 0, 0 }
},
};
const byte frame_pvp[MATRIX_HEIGHT][MATRIX_WIDTH] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
{ 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0 },
{ 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0 },
{ 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
{ 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
const byte frame_pvc[MATRIX_HEIGHT][MATRIX_WIDTH] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0 },
{ 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
{ 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
const byte frame_cvc[MATRIX_HEIGHT][MATRIX_WIDTH] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
{ 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
{ 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
{ 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
const byte (*frame_gmodes[3])[12]= {frame_pvp, frame_pvc, frame_cvc};
const byte frame_bot_skill_easy[MATRIX_HEIGHT][MATRIX_WIDTH] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 },
{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 },
{ 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0 },
{ 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0 },
{ 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
const byte frame_bot_skill_hard[MATRIX_HEIGHT][MATRIX_WIDTH] = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0 },
{ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 },
{ 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0 },
{ 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0 },
{ 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
const byte (*frame_bot_skills[3])[12]= {frame_bot_skill_easy, frame_bot_skill_hard};

20
src/font.h Normal file
View File

@@ -0,0 +1,20 @@
#ifndef FONT_H
#define FONT_H
#include <Arduino.h>
#include "config.h"
extern const uint32_t pone_wins[5][4];
extern const uint32_t ptwo_wins[5][4];
extern const byte font_pong[10][8][3];
extern const byte frame_pvp[MATRIX_HEIGHT][MATRIX_WIDTH];
extern const byte frame_pvc[MATRIX_HEIGHT][MATRIX_WIDTH];
extern const byte frame_cvc[MATRIX_HEIGHT][MATRIX_WIDTH];
extern const byte (*frame_gmodes[3])[12];
extern const byte frame_bot_skill_easy[MATRIX_HEIGHT][MATRIX_WIDTH];
extern const byte frame_bot_skill_hard[MATRIX_HEIGHT][MATRIX_WIDTH];
extern const byte (*frame_bot_skills[3])[12];
#endif

42
src/menu.cpp Normal file
View File

@@ -0,0 +1,42 @@
#include "menu.h"
void Menu::next_mode() {
if (_game_mode < _game_modes(COUNT_MODES) - 1)
_game_mode= _game_modes(_game_mode+1);
}
void Menu::prev_mode() {
if (_game_mode > 0) {
_game_mode= _game_modes(_game_mode-1);
}
}
uint8_t Menu::get_mode() {
return _game_mode;
}
uint8_t Menu::number_of_bots() {
if (_game_mode == PVC) return 1;
else if (_game_mode == CVC) return 2;
return 0;
}
void Menu::increase_skills() {
if (_bot_skill < _bot_skill_levels(COUNT_SKILLS) - 1)
_bot_skill= _bot_skill_levels(_bot_skill+1);
}
void Menu::decrease_skills() {
if (_bot_skill > 0) {
_bot_skill= _bot_skill_levels(_bot_skill-1);
}
}
uint8_t Menu::get_skill() {
return _bot_skill;
}
void Menu::reset_menu() {
_game_mode= PVP;
_bot_skill= EASY;
}

26
src/menu.h Normal file
View File

@@ -0,0 +1,26 @@
#ifndef MENU_H
#define MENU_H
#include <Arduino.h>
#include "font.h"
class Menu {
private:
enum _game_modes : uint8_t {PVP, PVC, CVC, COUNT_MODES};
_game_modes _game_mode = PVP;
enum _bot_skill_levels : uint8_t {EASY, HARD, COUNT_SKILLS};
_bot_skill_levels _bot_skill = EASY;
public:
void next_mode();
void prev_mode();
uint8_t get_mode();
uint8_t number_of_bots();
void increase_skills();
void decrease_skills();
uint8_t get_skill();
void reset_menu();
};
#endif

127
src/paddle.cpp Normal file
View File

@@ -0,0 +1,127 @@
#include "paddle.h"
#include <cstdio>
void Paddle::move_pad_up() {
if (_pos_y > 0) {
_pos_y -= 1;
}
}
void Paddle::move_pad_down() {
if (_pos_y + _height < MATRIX_HEIGHT) {
_pos_y += 1;
}
}
void run_paddle() {
}
uint8_t Paddle::get_position() {
return _pos_y;
}
bool Paddle::is_human() {
return _human;
}
void Paddle::increase_score() {
if (_score <= 9) _score += 1;
}
uint8_t Paddle::get_score() {
return _score;
}
void Paddle::reset() {
_score= 0;
}
bool Paddle::check_pad_movement() {
// redefine me
return false;
}
bool Paddle::check_pad_movement(Ball &ball) {
// redefine me
return false;
}
uint8_t Paddle::get_skills() {
return 0;
}
void Paddle::set_skills(uint8_t skills) {
}
bool HumanPaddle::check_pad_movement() {
bool need_refresh= false;
if (digitalRead(_pin_btn_top) == LOW) {
this -> move_pad_up();
need_refresh= true;
}
else if (digitalRead(_pin_btn_bottom) == LOW) {
this -> move_pad_down();
need_refresh= true;
}
return need_refresh;
}
bool BotPaddle::check_pad_movement(Ball &ball) {
uint8_t ball_y= ball.get_y();
int8_t ball_dir= ball.get_direction_x();
int8_t ball_dir_ver= ball.get_direction_y();
uint8_t skills= this -> get_skills();
// ball is moving left and pad is on right, do not move
if (ball_dir < 0 && _pos_x > MATRIX_WIDTH / 2) return false;
// ball is moving right and pad is on left, do not move
else if (ball_dir > 0 && _pos_x < MATRIX_WIDTH / 2) return false;
uint8_t ball_x= ball.get_x();
int8_t ball_distance= ball_x - _pos_x;
if (ball_distance < 0) ball_distance *= -1;
switch (skills) {
case 1:
if (ball_distance > 3) return false;
break;
case 2:
if (ball_distance > 4) return false;
break;
}
uint8_t move_chances= random(skills * 10) % 2;
if (!move_chances) return false;
enum Movement {NONE, UP, DOWN};
Movement _movment= NONE;
for (uint8_t py= _pos_y; py < _pos_y+PADDLE_LENGTH-1; py++) {
if (_pos_y - ball_y >= 0 && ball_dir_ver < 0) {
_movment= UP;
break;
}
if (_pos_y - ball_y <= 0 && ball_dir_ver > 0) {
_movment= DOWN;
break;
}
}
if (_movment == UP) {
this -> move_pad_up();
return true;
}
if (_movment == DOWN) {
this -> move_pad_down();
return true;
}
return false;
}
uint8_t BotPaddle::get_skills() {
return _skills;
}
void BotPaddle::set_skills(uint8_t skills) {
if (skills < 1) _skills= 1;
else if (skills > 2) _skills= 2;
else _skills= skills;
}

57
src/paddle.h Normal file
View File

@@ -0,0 +1,57 @@
#ifndef PADDLE_H
#define PADDLE_H
#include <Arduino.h>
#include <cstdint>
#include "config.h"
#include "ball.h"
class Paddle {
protected:
// define player coordinates
uint8_t _pos_y;
uint8_t _height= PADDLE_LENGTH;
uint8_t _score= 0;
bool _human;
public:
Paddle (uint8_t pos_y, bool human) : _pos_y(pos_y), _human(human) {}
void move_pad_up();
void move_pad_down();
uint8_t get_position();
bool is_human();
void increase_score();
uint8_t get_score();
void reset();
virtual bool check_pad_movement();
virtual bool check_pad_movement(Ball &ball);
virtual uint8_t get_skills();
virtual void set_skills(uint8_t skills);
};
class HumanPaddle : public Paddle {
private:
uint8_t _pin_btn_top;
uint8_t _pin_btn_bottom;
public:
HumanPaddle(uint8_t position, uint8_t pin_btn_top, uint8_t pin_btn_bottom)
: Paddle(position, true), _pin_btn_top(pin_btn_top), _pin_btn_bottom(pin_btn_bottom) {}
bool check_pad_movement();
};
class BotPaddle : public Paddle {
private:
uint8_t _pos_x;
uint8_t _skills; // this is the difficulty level
public:
BotPaddle(uint8_t position, uint8_t pos_x)
: Paddle(position, false), _pos_x(pos_x) {}
bool check_pad_movement(Ball &ball);
uint8_t get_skills();
void set_skills(uint8_t skills);
};
#endif

73
src/renderer.cpp Normal file
View File

@@ -0,0 +1,73 @@
#include "renderer.h"
void Renderer::_clear_matrix() {
for (int x=0; x < MATRIX_WIDTH; x++) {
for (int y=0; y < MATRIX_HEIGHT; y++) {
_frame[y][x]= 0;
}
}
}
void Renderer::set_players(Paddle *p1, Paddle *p2) {
_p1= p1;
_p2= p2;
}
void Renderer::render_timer(uint8_t seconds) {
this -> _clear_matrix();
for (int h=0; h < 8; h++) {
for (int w=0; w < 3; w++) {
_frame[h][w+5]= font_pong[seconds][h][w];
}
}
_matrix.renderBitmap(_frame, MATRIX_HEIGHT, MATRIX_WIDTH);
}
void Renderer::render_matrix() {
this -> _clear_matrix();
uint8_t p1pos= _p1 -> get_position();
uint8_t p2pos= _p2 -> get_position();
// players coords
for (int i= p1pos; i < p1pos+PADDLE_LENGTH; i++) {
_frame[i][0]= 1;
}
for (int i= p2pos; i < p2pos+PADDLE_LENGTH; i++) {
_frame[i][MATRIX_WIDTH-1]= 1;
}
// ball coords
uint8_t bx= _ball.get_x();
uint8_t by= _ball.get_y();
_frame[by][bx]= 1;
_matrix.renderBitmap(_frame, MATRIX_HEIGHT, MATRIX_WIDTH);
}
void Renderer::render_score() {
this -> _clear_matrix();
_frame[4][5]= 1;
_frame[4][6]= 1;
for (int h=0; h < 8; h++) {
for (int w=0; w < 3; w++) {
_frame[h][w+1]= font_pong[_p1 -> get_score()][h][w];
}
}
for (int h=0; h < 8; h++) {
for (int w=0; w < 3; w++) {
_frame[h][w+8]= font_pong[_p2 -> get_score()][h][w];
}
}
_matrix.renderBitmap(_frame, MATRIX_HEIGHT, MATRIX_WIDTH);
}
void Renderer::render_winner() {
this -> _clear_matrix();
// check winner
if (_p1 -> get_score() > _p2 -> get_score())
_matrix.loadSequence(pone_wins);
else
_matrix.loadSequence(ptwo_wins);
_matrix.play(true);
}

34
src/renderer.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef RENDERER_H
#define RENDERER_H
#include <Arduino.h>
#include "Arduino_LED_Matrix.h"
#include "config.h"
#include "font.h"
#include "paddle.h"
#include "ball.h"
class Renderer {
private:
// define player coordinates
Paddle* _p1;
Paddle* _p2;
Ball& _ball;
byte (&_frame)[MATRIX_HEIGHT][MATRIX_WIDTH];
ArduinoLEDMatrix& _matrix;
void _clear_matrix();
public:
Renderer (Ball &ball, byte (&frame)[MATRIX_HEIGHT][MATRIX_WIDTH], ArduinoLEDMatrix &matrix)
: _ball(ball), _frame(frame), _matrix(matrix) {}
void set_players(Paddle *p1, Paddle *p2);
void render_timer(uint8_t seconds);
void render_matrix();
void render_score();
void render_winner();
};
#endif