Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 1 addition & 16 deletions Gamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -611,22 +611,7 @@ void Gamer::appendColumn(byte* screen, byte col)
delay(60);
}

/**
Shows the score. Maximum 2 digits :(
@param n the score to be displayed
*/
void Gamer::showScore(int n)
{
byte result[8];
int dig1=n/10;
int dig2=n%10;
for(byte p=0;p<8;p++) {
result[p]=pgm_read_byte(&allNumbers[dig2][p]);
if( dig1>0 )
result[p]|=(pgm_read_byte(&allNumbers[dig1][p])<<4);
}
printImage(result);
}
// Removed showScore() — games use the version in the main .ino file instead.

/**
Prints an 8-byte PROGMEM image onto the display.
Expand Down
2 changes: 1 addition & 1 deletion Gamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ class Gamer {
void playTone(int note);
void stopTone();
void printString(const char* string);
Comment thread
28pins marked this conversation as resolved.
Resolved
void showScore(int score);
void appendColumn(byte* screen, byte col);
void showScore(int n);
void printImagePGM(const byte* pgm_img);

// Infrared
Expand Down
7 changes: 7 additions & 0 deletions TWSUGamerPlus.ino
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ bool startFromHighScore = false;
byte startingHighScore = 0;

// ── Helper functions ──────────────────────────────────────────────────────────
// Common game loop preamble: check sound toggle, update LED flash, optionally stop tone
inline void updateGameInput(bool stopTone = true) {
checkSoundToggle();
updateLEDFlash();
if (soundEnabled && stopTone) gamer.stopTone();
}

void startLEDFlash() {
gamer.setLED(true);
ledFlashStartTime = millis();
Expand Down
3 changes: 1 addition & 2 deletions src/games/alien.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,7 @@ void alienLoop() {
moveAlien();
while (millis() - lastMove < moveDelay) {
delay(10);
checkSoundToggle();
updateLEDFlash();
updateGameInput();
if(gamer.isPressed(UP)){
for(int8_t i = 7; i >= 0; i--) {
gamer.display[currentX][i] = 1;
Expand Down
123 changes: 37 additions & 86 deletions src/games/breakout.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,63 +15,44 @@ int origYV=-1;
byte scoreBreakout = 0;

bool outOfBounds(int xV, int yV) {
return (xV > 8 || xV < 0 || yV > 8 || yV < 0);
return (xV >= 8 || xV < 0 || yV >= 8 || yV < 0);
}

// Helper: checks if a position is free (LOW) and in bounds
inline bool isFree(int x, int y) {
return !outOfBounds(x, y) && gamer.display[x][y] == LOW;
}

void physics() {
if(gamer.display[currentXBreakout+velocity[0]][currentYBreakout+velocity[1]]==HIGH || outOfBounds(currentXBreakout+velocity[0],currentYBreakout+velocity[1])) {
//Collided with something!!!
int nextX = currentXBreakout + velocity[0];
int nextY = currentYBreakout + velocity[1];

// Check if we hit something at the next position
if(outOfBounds(nextX, nextY) || gamer.display[nextX][nextY] == HIGH) {
// Collision detected!
Comment on lines +30 to +32

Copilot AI Mar 7, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In physics(), gamer.display[nextX][nextY] is evaluated before checking outOfBounds(nextX, nextY). If nextX/nextY are out of range, this reads outside the 8×8 buffer. Reorder the condition to check outOfBounds(...) first (or use isFree()/a safe accessor) before indexing into gamer.display.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@claude[agent] apply changes based on this feedback

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed in commit a80bbf3. The condition was reordered to check outOfBounds(nextX, nextY) before accessing gamer.display[nextX][nextY], preventing out-of-range reads.

startLEDFlash();
if(soundEnabled) {
if(currentYBreakout==6) {
gamer.playTone(NOTE_C8);
}
else {
gamer.playTone(NOTE_E8);
}
gamer.playTone((currentYBreakout == 6) ? NOTE_C8 : NOTE_E8);
}
if(velocity[0]==1) {
if(velocity[1]==1) {
if(gamer.display[currentXBreakout+velocity[0]][currentYBreakout-1]==LOW && !outOfBounds(currentXBreakout+velocity[0],currentYBreakout-1)) {
velocity[1]=-1;
}
else if(gamer.display[currentXBreakout-1][currentYBreakout-1]==LOW && !outOfBounds(currentXBreakout-1,currentYBreakout-1)) {
velocity[1]=-1;
velocity[0]=-1;
}
}
else if(velocity[1]==-1) {
if(gamer.display[currentXBreakout+velocity[0]][currentYBreakout+1]==LOW && !outOfBounds(currentXBreakout+velocity[0],currentYBreakout+1)) {
velocity[1]=1;
}
else if(gamer.display[currentXBreakout-1][currentYBreakout+1]==LOW && !outOfBounds(currentXBreakout-1,currentYBreakout+1)) {
velocity[1]=1;
velocity[0]=-1;
}
}
}
else if(velocity[0]==-1) {
if(velocity[1]==1) {
if(gamer.display[currentXBreakout+velocity[0]][currentYBreakout-1]==LOW && !outOfBounds(currentXBreakout+velocity[0],currentYBreakout-1)) {
velocity[1]=-1;
}
else if(gamer.display[currentXBreakout+1][currentYBreakout-1]==LOW && !outOfBounds(currentXBreakout-1,currentYBreakout-1)) {
velocity[1]=-1;
velocity[0]=1;
}
}
else if(velocity[1]==-1) {
if(gamer.display[currentXBreakout+velocity[0]][currentYBreakout+1]==LOW && !outOfBounds(currentXBreakout+velocity[0],currentYBreakout+1)) {
velocity[1]=1;
}
else if(gamer.display[currentXBreakout+1][currentYBreakout+1]==LOW && !outOfBounds(currentXBreakout-1,currentYBreakout+1)) {
velocity[1]=1;
velocity[0]=1;
}
}

// Try to bounce off edges intelligently
// Check if we can bounce just horizontally or just vertically
bool canBounceY = isFree(nextX, currentYBreakout - velocity[1]);
bool canBounceX = isFree(currentXBreakout - velocity[0], nextY);

if(canBounceY) {
velocity[1] *= -1; // Bounce vertically
} else if(canBounceX) {
velocity[0] *= -1; // Bounce horizontally
} else {
// Corner hit - bounce both directions
velocity[0] *= -1;
velocity[1] *= -1;
}
if(!outOfBounds(currentXBreakout+origXV,currentYBreakout+origYV)) {
blocks[currentXBreakout+origXV][currentYBreakout+origYV]=0;

// Clear the block we hit (if in bounds)
if(!outOfBounds(currentXBreakout + origXV, currentYBreakout + origYV)) {
blocks[currentXBreakout + origXV][currentYBreakout + origYV] = 0;
}
}
}
Expand Down Expand Up @@ -106,9 +87,7 @@ void startBreakoutReset() {
}

void breakoutLoop() {
checkSoundToggle();
updateLEDFlash();
if(soundEnabled) gamer.stopTone();
updateGameInput();
if(breakoutCounter>2) {
for(byte x=0;x<8;x++) {
for(byte y=0;y<8;y++) {
Expand Down Expand Up @@ -142,42 +121,14 @@ void breakoutLoop() {
}
}
physics();
// Propagate block destruction to adjacent blocks in a checkerboard pattern
for(byte x=0;x<8;x++) {
for(byte y=0;y<8;y++) {
if(blocks[x][y]==0) {
if(x%2==0) {
if(y%2==0) {
if(x<7) {
blocks[x+1][y]=0;
} else {
blocks[0][y]=0;
}
}
else {
if(x>0) {
blocks[x-1][y]=0;
} else {
blocks[7][y]=0;
}
}
}
else {
if(y%2==0) {
if(x>0) {
blocks[x-1][y]=0;
} else {
blocks[7][y]=0;
}
}
else {
if(x<7) {
blocks[x+1][y]=0;
} else {
blocks[0][y]=0;
}
}
}
}
// Determine adjacent block based on checkerboard pattern
byte adjX = ((x % 2) == (y % 2)) ? ((x < 7) ? x + 1 : 0) : ((x > 0) ? x - 1 : 7);
blocks[adjX][y] = 0;
}
}
}
for(byte x=0;x<8;x++) {
Expand Down
3 changes: 1 addition & 2 deletions src/games/conway.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ bool conwayStepSmall() {
void resetConway() { conwayRandomize(); }

void conwayLoop() {
checkSoundToggle();
updateLEDFlash();
updateGameInput();

bool changed = conwayStep();
if (!changed) {
Expand Down
4 changes: 1 addition & 3 deletions src/games/dino.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ void resetDino() {
}

void dinoLoop() {
checkSoundToggle();
updateLEDFlash();
if (soundEnabled) gamer.stopTone();
updateGameInput();

// ── Game-over: show score, then auto-restart ──────────────────────────
if (dinoOver) {
Expand Down
4 changes: 1 addition & 3 deletions src/games/flappy.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,7 @@ inline void resetFlappyLauncher() {

void flappyLoop()
{
checkSoundToggle();
updateLEDFlash();
if (soundEnabled) gamer.stopTone();
updateGameInput();
if( menu )
{
++ticks;
Expand Down
3 changes: 1 addition & 2 deletions src/games/simon.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ void resetSimon() {
}

void simonLoop() {
checkSoundToggle();
updateLEDFlash();
updateGameInput();
static const byte simonNotes[] PROGMEM = {NOTE_E8, NOTE_C8, NOTE_G8, NOTE_D8};
sequence[simonStep]=random(0, SIMON_NUM_DIRECTIONS);
if(simonStep>0) {
Expand Down
5 changes: 2 additions & 3 deletions src/games/snake.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,8 @@ void snakeRec() {
}

void snakeLoop() {
checkSoundToggle();
updateLEDFlash();
if (soundEnabled) gamer.stopTone();
updateGameInput();
// Clear display manually (faster than gamer.clear() which also calls updateDisplay)
for(byte x=0;x<8;x++) {
for(byte y=0;y<8;y++) {
gamer.display[x][y] = LOW;
Expand Down
3 changes: 1 addition & 2 deletions src/games/tetris.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,7 @@ void resetTetris() {
}

void tetrisLoop() {
checkSoundToggle();
updateLEDFlash();
updateGameInput(false); // Don't stop tones - Tetris manages its own melody

if (soundEnabled && tetrisChirpPending) {
gamer.stopTone();
Expand Down