Compare commits

..

No commits in common. "f4887cd89ad7511611002b82b98e80284b228bb5" and "07a41a34cf95e7a78639a7b8b1fc76799b3f469d" have entirely different histories.

13 changed files with 92 additions and 326 deletions

View File

@ -14,7 +14,7 @@ board = esp32dev
framework = arduino framework = arduino
lib_deps = https://github.com/adafruit/Adafruit_NeoPixel lib_deps = https://github.com/adafruit/Adafruit_NeoPixel
build_flags = build_flags =
upload_port = 10.0.0.116 upload_port = 10.0.0.142
upload_protocol = espota upload_protocol = espota
;upload_port = /dev/ttyUSB0 ;upload_port = /dev/ttyUSB0
;upload_speed = 921600 ;upload_speed = 921600

View File

@ -7,7 +7,6 @@
#define _ false #define _ false
#define ____ 0 #define ____ 0
#define HALF 127
#define FULL 255 #define FULL 255
#define countof(x) (sizeof(x) / sizeof(x[0])) #define countof(x) (sizeof(x) / sizeof(x[0]))

View File

@ -14,8 +14,6 @@ const Color YELLOW = {FULL, FULL, ____};
const Color MAGENTA = {FULL, ____, FULL}; const Color MAGENTA = {FULL, ____, FULL};
const Color VIOLET = {HALF, ____, FULL};
const Color TURQUOISE = {____, FULL, FULL}; const Color TURQUOISE = {____, FULL, FULL};
Color gray(uint8_t brightness) { Color gray(uint8_t brightness) {

View File

@ -27,8 +27,6 @@ extern const Color YELLOW;
extern const Color MAGENTA; extern const Color MAGENTA;
extern const Color VIOLET;
extern const Color TURQUOISE; extern const Color TURQUOISE;
#endif #endif

View File

@ -51,6 +51,26 @@ public:
return {x * i, y * i}; return {x * i, y * i};
} }
Vector bounceInBox(uint8_t width, uint8_t height) const {
double x2 = this->x;
double y2 = this->y;
while (x2 < 0 || x2 >= width) {
if (x2 < 0) {
x2 = -x2;
} else if (x2 >= width) {
x2 = 2 * width - x2;
}
}
while (y2 < 0 || y2 >= height) {
if (y2 < 0) {
y2 = -y2;
} else if (y2 >= height) {
y2 = 2 * height - y2;
}
}
return {x2, y2};
}
}; };
#endif #endif

View File

@ -38,18 +38,6 @@ void setMode(ModeId value) {
config_set_dirty(); config_set_dirty();
} }
void modeMove(int index, int x, int y) {
if (mode != nullptr) {
mode->move(index, x, y);
}
}
void modeFire(int index) {
if (mode != nullptr) {
mode->fire(index);
}
}
void setSpeed(double speed) { void setSpeed(double speed) {
speed = min(max(0.01, speed), 10000.0); speed = min(max(0.01, speed), 10000.0);
if (config.speed == speed) { if (config.speed == speed) {

View File

@ -9,8 +9,4 @@ void setMode(ModeId value);
void setSpeed(double speed); void setSpeed(double speed);
void modeMove(int index, int x, int y);
void modeFire(int index);
#endif #endif

View File

@ -114,39 +114,24 @@ private:
} }
void drawCountdownBars(Display &display, uint8_t hours, uint8_t minutes, uint8_t seconds) { void drawCountdownBars(Display &display, uint8_t hours, uint8_t minutes, uint8_t seconds) {
drawBar(display, 0, 0, 24, 1, 0, 24, hours, RED, MAGENTA, 0); drawBar(display, 0, 0, 24, 1, 0, 24, hours, RED);
drawBar(display, 0, 2, 30, 2, 0, 60, minutes, BLUE, VIOLET, 5); drawBar(display, 0, 2, 30, 2, 0, 60, minutes, BLUE);
drawBar(display, 0, 5, 30, 2, 0, 60, seconds, GREEN, YELLOW, 5); drawBar(display, 0, 5, 30, 2, 0, 60, seconds, GREEN);
} }
void drawBar(Display &display, uint8_t _x, uint8_t _y, uint8_t _w, uint8_t _h, uint8_t min, uint8_t max, uint8_t value, const Color &color, const Color &tickColor, uint8_t ticks) { void drawBar(Display &display, uint8_t _x, uint8_t _y, uint8_t _w, uint8_t _h, uint8_t min, uint8_t max, uint8_t value, const Color &color) {
auto totalOnCount = (uint8_t) round(((double) value - min) / (max - min) * _w * _h); auto onCount = (uint8_t) round(((double) value - min) / (max - min) * _w * _h);
uint8_t doneOnCount = 0;
for (uint8_t y = 0; y < _h; y++) { for (uint8_t y = 0; y < _h; y++) {
for (uint8_t x = 0; x < _w; x++) { for (uint8_t x = 0; x < _w; x++) {
if (doneOnCount >= totalOnCount) { if (onCount <= 0) {
return; return;
} }
doneOnCount++; onCount--;
Color c = color; display.set(_x + x, _y + y, color);
if (ticks != 0) {
if (doneOnCount % ticks == 0) {
c = tickColor;
}
}
display.set(_x + x, _y + y, c);
} }
} }
} }
static Color factor(Color color, double factor) {
return {
(uint8_t) round(color.r * factor),
(uint8_t) round(color.g * factor),
(uint8_t) round(color.b * factor),
};
}
void drawCountdownNumbers(Display &display, uint8_t hours, uint8_t minutes, uint8_t seconds) const { void drawCountdownNumbers(Display &display, uint8_t hours, uint8_t minutes, uint8_t seconds) const {
uint8_t x = 0; uint8_t x = 0;
if (days > 0) { if (days > 0) {

View File

@ -94,14 +94,6 @@ public:
virtual const char *getName() = 0; virtual const char *getName() = 0;
virtual void move(int index, int x, int y) {
//
};
virtual void fire(int index) {
//
};
void loop(microseconds_t microseconds) { void loop(microseconds_t microseconds) {
handleRealtime(); handleRealtime();
handleTimers(microseconds); handleTimers(microseconds);

View File

@ -7,7 +7,7 @@ class Player {
public: public:
uint8_t y = 0; uint8_t position = 0;
uint8_t size = 2; uint8_t size = 2;
@ -15,21 +15,20 @@ public:
bool moveUp = false; bool moveUp = false;
bool random = true;
void randomMove(uint8_t height) { void randomMove(uint8_t height) {
if (moveUp) { if (moveUp) {
y--; position--;
if (y <= 0 || randomBool(20)) { if (position <= 0 || randomBool(20)) {
moveUp = false; moveUp = !moveUp;
} }
} else { } else {
y++; position++;
if (y + size >= height || randomBool(20)) { if (position + size >= height || randomBool(20)) {
moveUp = true; moveUp = !moveUp;
} }
} }
} }
}; };
#endif #endif

View File

@ -17,7 +17,7 @@ private:
Player player1; Player player1;
Vector ball; Vector position;
Vector velocity; Vector velocity;
@ -29,7 +29,7 @@ public:
explicit Pong(Display &display) : explicit Pong(Display &display) :
Mode(display), Mode(display),
ball(width / 2.0, height / 2.0), position(width / 2.0, height / 2.0),
velocity(Vector::polar(random(360), exp10(1))) { velocity(Vector::polar(random(360), exp10(1))) {
timer(0, 100); timer(0, 100);
spawnBall(random(2) == 0 ? -1 : +1); spawnBall(random(2) == 0 ? -1 : +1);
@ -40,24 +40,6 @@ public:
return "Pong"; return "Pong";
} }
void move(int index, int x, int y) override {
if (index == 0) {
player0.random = false;
player0.y = min(height - player0.size, max(0, player0.y + y));
} else if (index == 1) {
player1.random = false;
player1.y = min(height - player1.size, max(0, player1.y + y));
}
}
void fire(int index) override {
if (index == 0) {
player0.random = false;
} else if (index == 1) {
player1.random = false;
}
}
protected: protected:
void tick(uint8_t index, microseconds_t microseconds) override { void tick(uint8_t index, microseconds_t microseconds) override {
@ -69,16 +51,12 @@ protected:
} }
break; break;
case PLAY: case PLAY:
ball = ball.plus(velocity); position = position.plus(velocity);
if (player0.random) { player0.randomMove(height);
player0.randomMove(height); player1.randomMove(height);
}
if (player1.random) {
player1.randomMove(height);
}
topBottomBounce();
paddleBounce(); paddleBounce();
checkScoring(); checkScoring();
topBottomBounce();
markDirty(); markDirty();
break; break;
@ -102,12 +80,12 @@ protected:
break; break;
case PLAY: case PLAY:
for (int i = 0; i < player0.size; ++i) { for (int i = 0; i < player0.size; ++i) {
display.set(1, (uint8_t) round(player0.y) + i, GREEN); display.set(1, (uint8_t) round(player0.position) + i, GREEN);
} }
for (int i = 0; i < player1.size; ++i) { for (int i = 0; i < player1.size; ++i) {
display.set(width - 2, (uint8_t) round(player1.y) + i, RED); display.set(width - 2, (uint8_t) round(player1.position) + i, RED);
} }
display.set((uint8_t) round(ball.x), (uint8_t) round(ball.y), WHITE); display.set((uint8_t) round(position.x), (uint8_t) round(position.y), WHITE);
break; break;
case OVER: case OVER:
if (player0.score > player1.score) { if (player0.score > player1.score) {
@ -126,34 +104,22 @@ private:
void resetPlayer() { void resetPlayer() {
player0.size = 3; player0.size = 3;
player0.score = 0; player0.score = 0;
player0.y = (height - player0.size) / 2; player0.position = (height - player0.size) / 2;
player0.random = true;
player1.size = 3; player1.size = 3;
player1.score = 0; player1.score = 0;
player1.y = (height - player1.size) / 2; player1.position = (height - player1.size) / 2;
player1.random = true;
} }
void topBottomBounce() { void topBottomBounce() {
while (ball.y < 0 || ball.y >= height) { position = position.bounceInBox(width, height);
if (ball.y < 0) {
ball.y = -ball.y;
velocity.y = -velocity.y;
} else if (ball.y >= height) {
ball.y = 2 * height - ball.y - 1;
velocity.y = -velocity.y;
}
}
} }
void checkScoring() { void checkScoring() {
if (ball.x < 0) { if (position.x < 0) {
player1.score++; player1.score++;
Serial.println("Player 1 scored");
spawnBall(+1); spawnBall(+1);
} else if (ball.x >= width) { } else if (position.x >= width) {
player0.score++; player0.score++;
Serial.println("Player 0 scored");
spawnBall(-1); spawnBall(-1);
} }
if (player0.score >= 10 || player1.score >= 10) { if (player0.score >= 10 || player1.score >= 10) {
@ -163,26 +129,18 @@ private:
} }
void paddleBounce() { void paddleBounce() {
double paddleHitPosition0 = ball.y - player0.y; if (position.x == 1 && player0.position <= position.y && position.y < player0.position + player0.size) {
if (ball.x >= 1 && ball.x < 2 && paddleHitPosition0 >= 0 && paddleHitPosition0 < player0.size) {
Serial.printf("Player 0 hit: paddleHitPosition0=%.2f\n", paddleHitPosition0);
velocity.x = -velocity.x; velocity.x = -velocity.x;
velocity.y = max(-2.0, min(+2.0, velocity.y + paddleHitPosition0 - 1)); position.x = 3;
ball.x = 3; } else if (position.x == width - 2 && player1.position <= position.y && position.y < player1.position + player1.size) {
return;
}
double paddleHitPosition1 = ball.y - player1.y;
if (ball.x >= width - 2 && ball.x < width - 1 && paddleHitPosition1 >= 0 && paddleHitPosition1 < player1.size) {
Serial.printf("Player 1 hit: paddleHitPosition1=%.2f\n", paddleHitPosition1);
velocity.x = -velocity.x; velocity.x = -velocity.x;
velocity.y = max(-2.0, min(+2.0, velocity.y + paddleHitPosition1 - 1)); position.x = width - 4;
ball.x = width - 4;
} }
} }
void spawnBall(int direction) { void spawnBall(int direction) {
ball.x = (double) width / 2.0; position.x = (double) width / 2.0;
ball.y = (double) height / 2.0; position.y = (double) height / 2.0;
velocity.x = direction; velocity.x = direction;
velocity.y = 0; velocity.y = 0;
status = SCORE; status = SCORE;

View File

@ -26,7 +26,6 @@ private:
uint8_t heroX = 0; uint8_t heroX = 0;
bool heroLeft = false; bool heroLeft = false;
uint8_t heroShoot = 0; uint8_t heroShoot = 0;
bool randomEnabled = true;
uint8_t invadersCountX; uint8_t invadersCountX;
uint8_t invadersCountY; uint8_t invadersCountY;
@ -69,24 +68,12 @@ public:
return "Space Invaders"; return "Space Invaders";
} }
void move(int index, int x, int y) override {
randomEnabled = false;
heroX = max(1, min(width - 2, heroX + x));
}
void fire(int index) override {
randomEnabled = false;
shoot();
}
protected: protected:
void step(microseconds_t microseconds) override { void step(microseconds_t microseconds) override {
stepRockets(microseconds); stepRockets(microseconds);
stepInvaders(microseconds); stepInvaders(microseconds);
if (randomEnabled) { stepHero(microseconds);
randomStepHero(microseconds);
}
collide(); collide();
if (invadersAlive == 0) { if (invadersAlive == 0) {
@ -161,7 +148,7 @@ private:
} }
} }
void randomStepHero(microseconds_t microseconds) { void stepHero(microseconds_t microseconds) {
heroRuntime += microseconds; heroRuntime += microseconds;
if (heroRuntime >= 50000) { if (heroRuntime >= 50000) {
heroRuntime = heroRuntime % 50000; heroRuntime = heroRuntime % 50000;
@ -255,7 +242,6 @@ private:
heroLeft = false; heroLeft = false;
heroShoot = 0; heroShoot = 0;
heroX = width / 2; heroX = width / 2;
randomEnabled = true;
rocketRuntime = 0; rocketRuntime = 0;
for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) { for (Rocket *rocket = rocketsBegin; rocket < rocketsEnd; rocket++) {

View File

@ -6,47 +6,10 @@
#include "display.h" #include "display.h"
#include "config.h" #include "config.h"
static const char *const style = R"(
<style>
body {
font-family: sans-serif;
font-size: 8vw;
margin: 0;
}
button{
width: 33vmin;
height: 33vmin;
font-size: 9vw;
}
table{
border-collapse: collapse;
}
td{
text-align: center;
}
</style>
)";
static const char *const script = R"(
<script>
function get(path){
var r = new XMLHttpRequest();
r.open("GET", path, true);
r.send();
}
</script>
)";
WebServer server(80); WebServer server(80);
void web_index(); void web_index();
void web_player();
void web_player_move();
void web_player_fire();
void web_setMode(); void web_setMode();
void web_brighter(); void web_brighter();
@ -61,11 +24,10 @@ void web_fps_on();
void web_fps_off(); void web_fps_off();
void redirect();
void server_setup() { void server_setup() {
server.on("/", web_index); server.on("/", web_index);
server.on("/player", web_player);
server.on("/player/move", web_player_move);
server.on("/player/fire", web_player_fire);
server.on("/mode", web_setMode); server.on("/mode", web_setMode);
server.on("/brighter", web_brighter); server.on("/brighter", web_brighter);
server.on("/darker", web_darker); server.on("/darker", web_darker);
@ -83,150 +45,30 @@ void server_loop() {
void web_index() { void web_index() {
server.setContentLength(CONTENT_LENGTH_UNKNOWN); server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "text/html", ""); server.send(200, "text/html", "");
server.sendContent(style); server.sendContent(R"(<meta http-equiv="Cache-Control" content="store, no-cache, must-revalidate, post-check=0, pre-check=0" />)");
server.sendContent(script); server.sendContent(R"(<meta http-equiv="Pragma" content="no-cache" />)");
server.sendContent(R"(<meta http-equiv="Expires" content="Sun, 1 Jan 1999 00:00:00 GMT" />)");
server.sendContent(R"(<p>)"); server.sendContent(R"(<a href="/mode?mode=0">NONE</a><br>)");
server.sendContent(R"(<a href="/player?index=0">Player 0</a><br>)"); server.sendContent(R"(<a href="/mode?mode=1">BORDER</a><br>)");
server.sendContent(R"(<a href="/player?index=1">Player 1</a><br>)"); server.sendContent(R"(<a href="/mode?mode=2">CLOCK</a><br>)");
server.sendContent(R"(</p>)"); server.sendContent(R"(<a href="/mode?mode=3">GAME_OF_LIFE_BLACK_WHITE</a><br>)");
server.sendContent(R"(<a href="/mode?mode=4">GAME_OF_LIFE_GRAYSCALE</a><br>)");
server.sendContent(R"(<a href="/mode?mode=5">GAME_OF_LIFE_COLOR_FADE</a><br>)");
server.sendContent(R"(<a href="/mode?mode=6">GAME_OF_LIFE_RANDOM_COLOR</a><br>)");
server.sendContent(R"(<a href="/mode?mode=7">PONG</a><br>)");
server.sendContent(R"(<a href="/mode?mode=8">SPACE_INVADERS</a><br>)");
server.sendContent(R"(<a href="/mode?mode=9">COUNT_DOWN</a><br>)");
server.sendContent(R"(<a href="/mode?mode=10">COUNT_DOWN_BARS</a><br>)");
server.sendContent(R"(<a href="/mode?mode=11">STARFIELD</a><br>)");
server.sendContent(R"(<a href="/mode?mode=12">MATRIX</a><br>)");
server.sendContent(R"(<p>)"); server.sendContent(R"(Helligkeit: <a href="/brighter">+</a> / <a href="/darker">-</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=0');">NONE</a><br>)"); server.sendContent(R"(Geschwindigkeit: <a href="/faster">+</a> / <a href="/slower">-</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=1');">BORDER</a><br>)"); server.sendContent(R"(FPS: <a href="/fps/on">EIN</a> / <a href="/fps/off">AUS</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=2');">CLOCK</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=3');">GAME_OF_LIFE_BLACK_WHITE</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=4');">GAME_OF_LIFE_GRAYSCALE</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=5');">GAME_OF_LIFE_COLOR_FADE</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=6');">GAME_OF_LIFE_RANDOM_COLOR</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=7');">PONG</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=8');">SPACE_INVADERS</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=9');">COUNT_DOWN</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=10');">COUNT_DOWN_BARS</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=11');">STARFIELD</a><br>)");
server.sendContent(R"(<a onclick="get('/mode?mode=12');">MATRIX</a><br>)");
server.sendContent(R"(</p>)");
server.sendContent(R"(<p>)");
server.sendContent(R"(Helligkeit: <a onclick="get('/brighter');">+</a> / <a onclick="get('/darker');">-</a><br>)");
server.sendContent(R"(Geschwindigkeit: <a onclick="get('/faster');">+</a> / <a onclick="get('/slower');">-</a><br>)");
server.sendContent(R"(FPS: <a onclick="get('/fps/on');">EIN</a> / <a onclick="get('/fps/off');">AUS</a><br>)");
server.sendContent(R"(</p>)");
server.client().flush(); server.client().flush();
} }
void web_player() {
char buffer[128];
if (!server.hasArg("index")) {
server.send(400, "text/plain", "Missing 'index'");
return;
}
double value = strtod(server.arg("index").c_str(), nullptr);
int index = (int) value;
server.setContentLength(CONTENT_LENGTH_UNKNOWN);
server.send(200, "text/html", "");
server.sendContent(style);
server.sendContent(script);
server.sendContent(R"(<meta name="viewport" content= "width=device-width, user-scalable=no">)");
server.sendContent(R"(<table>)");
server.sendContent(R"(<tr>)");
server.sendContent(R"(<td><a href='/'>&larr;</td>)");
server.sendContent(R"(<td>)");
snprintf(buffer, sizeof buffer, R"(<button onclick="get('/player/move?index=%d&x=0&y=-1');">&uarr;</button><br>)", index);
server.sendContent(buffer);
server.sendContent(R"(</td>)");
server.sendContent(R"(<td>&nbsp;</td>)");
server.sendContent(R"(</tr>)");
server.sendContent(R"(<tr>)");
server.sendContent(R"(<td>)");
snprintf(buffer, sizeof buffer, R"(<button onclick="get('/player/move?index=%d&x=-1&y=0');">&larr;</button><br>)", index);
server.sendContent(buffer);
server.sendContent(R"(</td>)");
server.sendContent(R"(<td>)");
snprintf(buffer, sizeof buffer, R"(<button onclick="get('/player/fire?index=%d');">X</button><br>)", index);
server.sendContent(buffer);
server.sendContent(R"(</td>)");
server.sendContent(R"(<td>)");
snprintf(buffer, sizeof buffer, R"(<button onclick="get('/player/move?index=%d&x=+1&y=0');">&rarr;</button><br>)", index);
server.sendContent(buffer);
server.sendContent(R"(</td>)");
server.sendContent(R"(</tr>)");
server.sendContent(R"(<tr>)");
server.sendContent(R"(<td>&nbsp;</td>)");
server.sendContent(R"(<td>)");
snprintf(buffer, sizeof buffer, R"(<button onclick="get('/player/move?index=%d&x=0&y=+1');">&darr;</button><br>)", index);
server.sendContent(buffer);
server.sendContent(R"(</td>)");
server.sendContent(R"(<td>&nbsp;</td>)");
server.sendContent(R"(</tr>)");
server.sendContent(R"(</table>)");
server.client().flush();
}
void web_player_move() {
double value;
if (!server.hasArg("index")) {
server.send(400, "text/plain", "Missing 'index'");
return;
}
value = strtod(server.arg("index").c_str(), nullptr);
int index = (int) value;
if (!server.hasArg("x")) {
server.send(400, "text/plain", "Missing 'x'");
return;
}
value = strtod(server.arg("x").c_str(), nullptr);
int x = (int) value;
if (!server.hasArg("y")) {
server.send(400, "text/plain", "Missing 'y'");
return;
}
value = strtod(server.arg("y").c_str(), nullptr);
int y = (int) value;
modeMove(index, x, y);
server.send(200, "application/json", "true");
}
void web_player_fire() {
double value;
if (!server.hasArg("index")) {
server.send(400, "text/plain", "Missing 'index'");
return;
}
value = strtod(server.arg("index").c_str(), nullptr);
int index = (int) value;
modeFire(index);
server.send(200, "application/json", "true");
}
void web_setMode() { void web_setMode() {
if (!server.hasArg("mode")) { if (!server.hasArg("mode")) {
server.send(400, "text/plain", "Missing 'mode'"); server.send(400, "text/plain", "Missing 'mode'");
@ -238,35 +80,40 @@ void web_setMode() {
return; return;
} }
setMode((ModeId) value); setMode((ModeId) value);
server.send(200); redirect();
} }
void web_brighter() { void web_brighter() {
setBrightness(display.getBrightness() + 10); setBrightness(display.getBrightness() + 10);
server.send(200); redirect();
} }
void web_darker() { void web_darker() {
setBrightness(display.getBrightness() - 10); setBrightness(display.getBrightness() - 10);
server.send(200); redirect();
} }
void web_faster() { void web_faster() {
setSpeed(config.speed * 1.1); setSpeed(config.speed * 1.1);
server.send(200); redirect();
} }
void web_slower() { void web_slower() {
setSpeed(config.speed / 1.1); setSpeed(config.speed / 1.1);
server.send(200); redirect();
} }
void web_fps_on() { void web_fps_on() {
display.fpsShow = true; display.fpsShow = true;
server.send(200); redirect();
} }
void web_fps_off() { void web_fps_off() {
display.fpsShow = false; display.fpsShow = false;
server.send(200); redirect();
}
void redirect() {
server.sendHeader("location", "/");
server.send(302, "text/plain", "realtimeOK");
} }