#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define WIDTH 64 // OLED display width, in pixels
#define HEIGHT 128 // OLED display height, in pixels
Adafruit_SSD1306 display(128, 64, &Wire, -1);
#define UP_BUTTON_PIN 12
#define DOWN_BUTTON_PIN 10
#define SELECT_BUTTON_PIN 9
#define left 11
#define BUZZER_PIN 2
#define THEME_BUZZER 3
unsigned int noteIndex = 0;
unsigned int previousMillis = 0;
bool isTonePlaying = false;
#define NOTE_B0 31
#define NOTE_C1 33
#define NOTE_CS1 35
#define NOTE_D1 37
#define NOTE_DS1 39
#define NOTE_E1 41
#define NOTE_F1 44
#define NOTE_FS1 46
#define NOTE_G1 49
#define NOTE_GS1 52
#define NOTE_A1 55
#define NOTE_AS1 58
#define NOTE_B1 62
#define NOTE_C2 65
#define NOTE_CS2 69
#define NOTE_D2 73
#define NOTE_DS2 78
#define NOTE_E2 82
#define NOTE_F2 87
#define NOTE_FS2 93
#define NOTE_G2 98
#define NOTE_GS2 104
#define NOTE_A2 110
#define NOTE_AS2 117
#define NOTE_B2 123
#define NOTE_C3 131
#define NOTE_CS3 139
#define NOTE_D3 147
#define NOTE_DS3 156
#define NOTE_E3 165
#define NOTE_F3 175
#define NOTE_FS3 185
#define NOTE_G3 196
#define NOTE_GS3 208
#define NOTE_A3 220
#define NOTE_AS3 233
#define NOTE_B3 247
#define NOTE_C4 262
#define NOTE_CS4 277
#define NOTE_D4 294
#define NOTE_DS4 311
#define NOTE_E4 330
#define NOTE_F4 349
#define NOTE_FS4 370
#define NOTE_G4 392
#define NOTE_GS4 415
#define NOTE_A4 440
#define NOTE_AS4 466
#define NOTE_B4 494
#define NOTE_C5 523
#define NOTE_CS5 554
#define NOTE_D5 587
#define NOTE_DS5 622
#define NOTE_E5 659
#define NOTE_F5 698
#define NOTE_FS5 740
#define NOTE_G5 784
#define NOTE_GS5 831
#define NOTE_A5 880
#define NOTE_AS5 932
#define NOTE_B5 988
#define NOTE_C6 1047
#define NOTE_CS6 1109
#define NOTE_D6 1175
#define NOTE_DS6 1245
#define NOTE_E6 1319
#define NOTE_F6 1397
#define NOTE_FS6 1480
#define NOTE_G6 1568
#define NOTE_GS6 1661
#define NOTE_A6 1760
#define NOTE_AS6 1865
#define NOTE_B6 1976
#define NOTE_C7 2093
#define NOTE_CS7 2217
#define NOTE_D7 2349
#define NOTE_DS7 2489
#define NOTE_E7 2637
#define NOTE_F7 2794
#define NOTE_FS7 2960
#define NOTE_G7 3136
#define NOTE_GS7 3322
#define NOTE_A7 3520
#define NOTE_AS7 3729
#define NOTE_B7 3951
#define NOTE_C8 4186
#define NOTE_CS8 4435
#define NOTE_D8 4699
#define NOTE_DS8 4978
#define REST 0
const int melody[] PROGMEM = { //10 20
NOTE_E4, NOTE_B3, NOTE_C4, NOTE_D4, NOTE_C4, NOTE_B3, NOTE_A3, NOTE_A3, NOTE_C4, NOTE_E4, NOTE_D4, NOTE_C4, NOTE_B3, NOTE_B3, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_C4, NOTE_A3, NOTE_A3, REST, NOTE_D4, NOTE_F4, NOTE_A4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_C4, NOTE_E4, NOTE_D4, NOTE_C4, NOTE_B3, NOTE_B3, NOTE_C4, NOTE_D4, NOTE_E4, NOTE_C4, NOTE_A3, NOTE_A3, REST, REST
};
const int noteDurations[] PROGMEM = { //20 //24 //30 //40
3, 6, 6, 3, 6, 6, 3, 6, 6, 3, 6, 6, 6, 3, 6, 3, 3, 3, 3, 3, 2, 3, 6, 3, 6, 6, 2, 6, 3, 6, 6, 3, 6, 6, 3, 3, 3, 3, 3, 2, 2
}; //10
//second part starts at 20
//MENU
//const char menuItem1T[] PROGMEM = "Item 1";
//const char menuItem2T[] PROGMEM = "Item 2";
//const char* const menuItemsT[] PROGMEM = {menuItem1T, menuItem2T};
//const char* menuItems[] = {"Item 1", "Item 2"};
byte selectedItem = 0;
byte selectedFunction = 254;
//NOTHING CLICKER
unsigned int nothingClicked = 0;
// TETRIS
const char pieces_S_l[2][2][4] = {{
{0, 0, 1, 1}, {0, 1, 1, 2}
},
{
{0, 1, 1, 2}, {1, 1, 0, 0}
}};
const char pieces_S_r[2][2][4]{{
{1, 1, 0, 0}, {0, 1, 1, 2}
},
{
{0, 1, 1, 2}, {0, 0, 1, 1}
}};
const char pieces_L_l[4][2][4] = {{
{0, 0, 0, 1}, {0, 1, 2, 2}
},
{
{0, 1, 2, 2}, {1, 1, 1, 0}
},
{
{0, 1, 1, 1}, {0, 0, 1, 2}
},
{
{0, 0, 1, 2}, {1, 0, 0, 0}
}};
const char pieces_Sq[1][2][4] = {{
{0, 1, 0, 1}, {0, 0, 1, 1}
}};
const char pieces_T[4][2][4] = {{
{0, 0, 1, 0},{0, 1, 1, 2}
},
{
{0, 1, 1, 2},{1, 0, 1, 1}
},
{
{1, 0, 1, 1},{0, 1, 1, 2}
},
{
{0, 1, 1, 2},{0, 0, 1, 0}
}};
const char pieces_l[2][2][4] = {{
{0, 1, 2, 3}, {0, 0, 0, 0}
},
{
{0, 0, 0, 0}, {0, 1, 2, 3}
}};
const short MARGIN_TOP = 19;
const short MARGIN_LEFT = 3;
const short SIZE = 5;
const short TYPES = 6;
const int click[] = { 1047 };
const int click_duration[] = { 100 };
const int erase[] = { 2093 };
const int erase_duration[] = { 100 };
word currentType, nextType, rotation;
short pieceX, pieceY;
short piece[2][4];
int interval = 20, score;
unsigned int timer, delayer;
boolean grid[10][18];
boolean b1, b2, b3;
//const int left=11;
void checkLines(){
boolean full;
for(short y = 17; y >= 0; y--){
full = true;
for(short x = 0; x < 10; x++){
full = full && grid[x][y];
}
if(full){
breakLine(y);
y++;
}
}
}
void breakLine(short line){
tone(BUZZER_PIN, erase[0], 1000 / erase_duration[0]);
delay(100);
noTone(BUZZER_PIN);
for(short y = line; y >= 0; y--){
for(short x = 0; x < 10; x++){
grid[x][y] = grid[x][y-1];
}
}
for(short x = 0; x < 10; x++){
grid[x][0] = 0;
}
display.invertDisplay(true);
delay(50);
display.invertDisplay(false);
score += 10;
}
void refresh(){
display.clearDisplay();
drawLayout();
drawGrid();
drawPiece(currentType, 0, pieceX, pieceY);
display.display();
}
void drawGrid(){
for(short x = 0; x < 10; x++)
for(short y = 0; y < 18; y++)
if(grid[x][y])
display.fillRect(MARGIN_LEFT + (SIZE + 1)*x, MARGIN_TOP + (SIZE + 1)*y, SIZE, SIZE, WHITE);
}
boolean nextHorizontalCollision(short piece[2][4], int amount){
for(byte i = 0; i < 4; i++){
short newX = pieceX + piece[0][i] + amount;
if(newX > 9 || newX < 0 || grid[newX][pieceY + piece[1][i]])
return true;
}
return false;
}
boolean nextCollision(){
for(byte i = 0; i < 4; i++){
short y = pieceY + piece[1][i] + 1;
short x = pieceX + piece[0][i];
if(y > 17 || grid[x][y])
return true;
}
return false;
}
void generate(){
currentType = nextType;
nextType = random(TYPES);
if(currentType != 5)
pieceX = random(9);
else
pieceX = random(7);
pieceY = 0;
rotation = 0;
copyPiece(piece, currentType, rotation);
}
void drawPiece(short type, short rotation, byte x, byte y){
for(byte i = 0; i < 4; i++)
display.fillRect(MARGIN_LEFT + (SIZE + 1)*(x + piece[0][i]), MARGIN_TOP + (SIZE + 1)*(y + piece[1][i]), SIZE, SIZE, WHITE);
}
void drawNextPiece(){
short nPiece[2][4];
copyPiece(nPiece, nextType, 0);
for(byte i = 0; i < 4; i++)
display.fillRect(50 + 3*nPiece[0][i], 4 + 3*nPiece[1][i], 2, 2, WHITE);
}
void copyPiece(short piece[2][4], short type, short rotation){
switch(type){
case 0: //L_l
for(byte i = 0; i < 4; i++){
piece[0][i] = pieces_L_l[rotation][0][i];
piece[1][i] = pieces_L_l[rotation][1][i];
}
break;
case 1: //S_l
for(byte i = 0; i < 4; i++){
piece[0][i] = pieces_S_l[rotation][0][i];
piece[1][i] = pieces_S_l[rotation][1][i];
}
break;
case 2: //S_r
for(byte i = 0; i < 4; i++){
piece[0][i] = pieces_S_r[rotation][0][i];
piece[1][i] = pieces_S_r[rotation][1][i];
}
break;
case 3: //Sq
for(byte i = 0; i < 4; i++){
piece[0][i] = pieces_Sq[0][0][i];
piece[1][i] = pieces_Sq[0][1][i];
}
break;
case 4: //T
for(byte i = 0; i < 4; i++){
piece[0][i] = pieces_T[rotation][0][i];
piece[1][i] = pieces_T[rotation][1][i];
}
break;
case 5: //l
for(byte i = 0; i < 4; i++){
piece[0][i] = pieces_l[rotation][0][i];
piece[1][i] = pieces_l[rotation][1][i];
}
break;
}
}
short getMaxRotation(short type){
if(type == 1 || type == 2 || type == 5)
return 2;
else if(type == 0 || type == 4)
return 4;
else if(type == 3)
return 1;
else
return 0;
}
boolean canRotate(short rotation){
short piece[2][4];
copyPiece(piece, currentType, rotation);
return !nextHorizontalCollision(piece, 0);
}
void drawLayout(){
display.drawLine(0, 18, WIDTH, 18, WHITE);
display.drawRect(0, 0, WIDTH, HEIGHT, WHITE);
drawNextPiece();
char text[6];
itoa(score, text, 10);
drawText(text, getNumberLength(score), 7, 4);
}
int getNumberLength(int n){
int counter = 1;
while(n >= 10){
n /= 10;
counter++;
}
return counter;
}
void drawText(char text[], int length, int x, int y){
display.setTextSize(1); // Normal 1:1 pixel scale
display.setTextColor(WHITE); // Draw white text
display.setCursor(x, y); // Start at top-left corner
display.cp437(true); // Use full 256 char 'Code Page 437' font
for(int i = 0; i < length; i++)
display.write(text[i]);
}
boolean checkGameOver() {
for(short x = 0; x < 10; x++){
if(grid[x][0])
return true;
}
return false;
}
int availableMemory() {
int size = 2048; // SRAM for ATmega328p = 2048Kb
byte *buf;
while ((buf = (byte *) malloc (--size)) == NULL);
free(buf);
return size;
}
void setup() {
pinMode(UP_BUTTON_PIN, INPUT_PULLUP);
pinMode(DOWN_BUTTON_PIN, INPUT_PULLUP);
pinMode(SELECT_BUTTON_PIN, INPUT_PULLUP);
pinMode(left, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
Serial.begin(9600);
Serial.println(availableMemory());
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
display.setRotation(1);
display.clearDisplay();
drawLayout();
display.display();
randomSeed(analogRead(0));
nextType = random(TYPES);
generate();
timer = millis();
}
void loop() {
if (selectedFunction == 254) {
if (digitalRead(UP_BUTTON_PIN) == LOW) {
selectedItem--;
if (selectedItem < 0) {
selectedItem = 0;
}
tone(BUZZER_PIN, 500, 50); // Play a tone when hovering over a new item
delay(200); // Debounce delay
}
if (digitalRead(DOWN_BUTTON_PIN) == LOW) {
selectedItem++;
if (selectedItem > 1) {
selectedItem = 1;
}
tone(BUZZER_PIN, 500, 50); // Play a tone when hovering over a new item
delay(200); // Debounce delay
}
if (digitalRead(SELECT_BUTTON_PIN) == LOW) {
tone(BUZZER_PIN, 1000, 80); // Play a different tone when an item is selected
selectedFunction = selectedItem-1;
delay(200); // Debounce delay
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println(F("Cool Games"));
for (byte i = 0; i < 2; i++) {
display.setCursor(0, (i+1)*10);
if (i == selectedItem) {
display.setTextColor(BLACK, WHITE); // 'inverted' text
} else {
display.setTextColor(WHITE);
}
display.print(i+1);
if (i==0) {
display.println(F(" Tetris"));
}
if (i==1) {
display.println(F(" Nothing Clicker"));
}
//display.println(menuItems[i]);
//display.println((__FlashStringHelper*)pgm_read_word(&(menuItemsT[i])));
}
//String myStr = String(nothingClicked); // convert integer to string
//int textWidth = myStr.length() * 6; // calculate the width of the text
//display.setCursor((WIDTH - textWidth) / 2, 100);
if (nothingClicked < 10) {
display.setCursor((WIDTH - 6) / 2, 100);
} else if (nothingClicked < 100) {
display.setCursor((WIDTH - 12) / 2, 100);
} else if (nothingClicked < 1000) {
display.setCursor((WIDTH - 18) / 2, 100);
} else {
display.setCursor((WIDTH - 24) / 2, 100);
}
//display.setCursor(28-strlen(char(nothingClicked)), 100);
display.setTextColor(WHITE);
display.println(nothingClicked);
display.display();
} else if (selectedFunction==255) {
menuItem1();
} else {
selectedFunction=254;
nothingClicked++;
//display.clearDisplay();
//display.setTextSize(1); // text sizes
//display.setCursor(32, 32);
//display.setTextColor(WHITE);
//display.println(nothingClicked);
//display.display();
}
//switch (selectedFunction) {
// case 1:
// menuItem1();
// break;
// case 2:
// menuItem2();
// break;
//}
}
void menuItem1() {
//Serial.println(availableMemory());
if(millis() - timer > interval){
checkLines();
refresh();
if(nextCollision()){
for(byte i = 0; i < 4; i++)
grid[pieceX + piece[0][i]][pieceY + piece[1][i]] = 1;
generate();
}else
pieceY++;
timer = millis();
}
if(checkGameOver()){
// Game over condition met
// Stop updating game state
while(1) {
// display.clearDisplay();
// drawLayout();
// display.display();
display.setCursor(6, 11); // Start at middle of the screen
display.println(F("Game Over"));
display.display(); // Show initial text
}
return;
}
if(!digitalRead(left)){
tone(BUZZER_PIN, click[0], 1000 / click_duration[0]);
delay(100);
noTone(BUZZER_PIN);
if(b1){
if(!nextHorizontalCollision(piece, -1)){
pieceX--;
refresh();
}
b1 = false;
}
}else{
b1 = true;
}
if(!digitalRead(SELECT_BUTTON_PIN)){
tone(BUZZER_PIN, click[0], 1000 / click_duration[0]);
delay(100);
noTone(BUZZER_PIN);
if(b2){
if(!nextHorizontalCollision(piece, 1)){
pieceX++;
refresh();
}
b2 = false;
}
}else{
b2 = true;
}
if(!digitalRead(DOWN_BUTTON_PIN)){
interval = 20;
}else{
interval = 400;
}
if(!digitalRead(UP_BUTTON_PIN)){
tone(BUZZER_PIN, click[0], 1000 / click_duration[0]);
delay(100);
noTone(BUZZER_PIN);
if(b3){
if(rotation == getMaxRotation(currentType) - 1 && canRotate(0)){
rotation = 0;
}else if(canRotate(rotation + 1)){
rotation++;
}
copyPiece(piece, currentType, rotation);
refresh();
b3 = false;
delayer = millis();
}
} else if(millis() - delayer > 50){
b3 = true;
}
unsigned int currentMillis = millis();
byte noteDuration = 255 / pgm_read_word_near(noteDurations + noteIndex);
if (!isTonePlaying && currentMillis - previousMillis >= noteDuration) {
tone(THEME_BUZZER, pgm_read_word_near(melody + noteIndex), noteDuration);
isTonePlaying = true;
previousMillis = currentMillis;
} else if (isTonePlaying && currentMillis - previousMillis >= noteDuration * 4.00) {
noTone(THEME_BUZZER);
previousMillis = currentMillis;
isTonePlaying = false;
noteIndex++;
if (noteIndex >= sizeof(melody) / sizeof(int)) {
noteIndex = 0;
}
}
// Code to run when Item 1 is selected
}
void menuItem2() {
// Code to run when Item 2 is selected
}
Loading
ssd1306
ssd1306