#include <Adafruit_NeoPixel.h>
#define MATRIX_WIDTH 7
#define MATRIX_HEIGHT 12
#define L_PIN 3 // Menggunakan pin D3 untuk LED
Adafruit_NeoPixel strip = Adafruit_NeoPixel(MATRIX_HEIGHT * MATRIX_WIDTH, L_PIN, NEO_GRB + NEO_KHZ800);
#define BUTTON_PIN 4
bool clicked = false;
bool goingLeft = true; // Bergerak ke kiri jika true, ke kanan jika false.
const int buzzerPin = 6; // Jika ada buzzer, atur ke -1 untuk menonaktifkan.
const int top = MATRIX_HEIGHT; // Tinggi strip, dapat disesuaikan jika menggunakan matriks lebih besar
const int bottom = top - 1; // Kurangi satu dari atas jika menggunakan seluruh strip.
const int maxHeight = bottom - top;
unsigned long prevMillis = 0; // Waktu sebelumnya dalam milidetik
unsigned long curMillis = 0; // Waktu saat ini dalam milidetik, untuk mencegah panggilan ganda ke millis()
unsigned long buttonwait = 0;
const long animationDuration = 2000; // Durasi animasi saat kehilangan blok
long defaultInterval = 300; // Waktu mulai antara pembaruan
int intervalModifier = 25; // pengurangan waktu atau delay kecepatan
long maxSpeed = 50; // Kecepatan maksimum yang diizinkan untuk blok. Jangan atur ke negatif
long interval = defaultInterval; // Waktu antara pembaruan. Ini tidak boleh dibuat sebagai const, karena akan berubah.
int cursize = 3; // Ukuran blok yang ditumpuk
int curpos = 3; // Posisi blok
int currow = bottom; // Baris LED tempat blok berada
// Untuk mencerminkan strip, semua "hal khusus" terjadi hanya di setup.
void setup() {
Serial.begin(115200);
strip.begin();
for (int i = 0; i < MATRIX_HEIGHT * MATRIX_WIDTH; i++) {
strip.setPixelColor(i, strip.Color(0, 0, 0));
}
strip.show();
if (maxSpeed < 0) {
maxSpeed = 0;
Serial.println("Error: Kecepatan maksimum diatur ke negatif.");
}
pinMode(BUTTON_PIN, INPUT_PULLUP);
int value = digitalRead(BUTTON_PIN);
}
int lastState = HIGH;
int value = HIGH;
bool checkAir(); // Memeriksa apakah blok berada di udara atau di atas blok lain
void drawBlock(); // Panggilan untuk menggambar
void onUpdate(); // Panggilan untuk pembaruan
void resetGame(); // Fungsi untuk mereset permainan
void failState(int echo); // Dipanggil saat pemain kalah
void winState(); // Dipanggil saat pemain menang
void animatedrop(int pos, int width); // Animasi jatuh
void animatedrop(int pos, int width, bool isFail); // Animasi jatuh jika pemain kalah
void buttonPressed(); // Dipanggil saat tombol ditekan.
int led_from_pixel(byte row, byte col) {
int a = 0;
if (row % 2 == 0) {
a = (row * MATRIX_HEIGHT) + col; // Baris genap
} else {
a = ((row + 1) * MATRIX_HEIGHT) - 1 - col; // Baris ganjil
}
return a;
}
void onUpdate() {
if (goingLeft) { // Jika bergerak ke kiri
if (curpos > 0) { // Periksa jika bisa bergerak ke kiri
curpos--; // Jika bisa, mari kita pergi ke kiri
} else { // Jika tidak...
curpos++; // Mari kita pergi ke kanan
goingLeft = false; // Dan pastikan kita benar-benar bergerak ke kanan.
}
} else { // Jika bergerak ke kanan
if (curpos + cursize < 7) { // Periksa jika bisa bergerak ke kanan
curpos++; // Jika bisa, mari kita pergi ke kanan.
} else { // Jika tidak...
curpos--; // Pergi ke kiri
goingLeft = true; // Dan benar-benar pergi ke kiri
}
}
drawBlock(); // Akhirnya kita bisa menggambar
prevMillis = curMillis;
}
void drawBlock() {
int r = curpos;
int c = currow;
int index_led = led_from_pixel(r, c);
strip.setPixelColor(index_led, strip.Color(255, 0, 0));
if (cursize > 1) { // Periksa jika ada lebih dari satu
if (cursize == 3) { // Jika ada tiga, kita bisa menggambar yang ketiga
int r = curpos + 2;
int c = currow;
int index_led = led_from_pixel(r, c);
strip.setPixelColor(index_led, strip.Color(255, 0, 0));
}
int r = curpos + 1;
int c = currow;
int index_led = led_from_pixel(r, c);
strip.setPixelColor(index_led, strip.Color(255, 0, 0));
}
if (goingLeft) { // Jika pergerakan ke kiri
int r = curpos + cursize;
int c = currow;
int index_led = led_from_pixel(r, c);
strip.setPixelColor(index_led, strip.Color(0, 0, 0));
} else {
int r = curpos - 1;
int c = currow;
int index_led = led_from_pixel(r, c);
strip.setPixelColor(index_led, strip.Color(0, 0, 0));
}
strip.show();
}
bool checkAir() {
Serial.println("Memeriksa");
if (strip.getPixelColor(led_from_pixel(curpos, currow + 1)) == strip.Color(0, 0, 0)) {
if (cursize > 1) { // Jika ada lebih dari satu blok
if (cursize == 3) { // Jika 3 lebar
if (strip.getPixelColor(led_from_pixel(curpos + 2, currow + 1)) == strip.Color(0, 0, 0)) {
failState(1); // Kondisi gagal, blok triple jatuh
return true;
} else if (strip.getPixelColor(led_from_pixel(curpos + 1, currow + 1)) == strip.Color(0, 0, 0)) {
cursize -= 2; // 2 blok telah jatuh
animatedrop(curpos, 2);
} else {
cursize--; // 1 blok telah jatuh
animatedrop(curpos, 1);
}
} else if (strip.getPixelColor(led_from_pixel(curpos + 1, currow + 1)) == strip.Color(0, 0, 0)) {
failState(2); // Kondisi gagal, blok ganda jatuh
return true;
} else {
cursize--; // 1 blok telah jatuh
animatedrop(curpos, 1);
}
} else { // Jika hanya ada satu blok
failState(3); // Kondisi gagal, blok tunggal jatuh
return true;
}
} else { // Jika blok paling kiri belum jatuh
if (cursize > 1) { // Jika ada lebih dari satu blok
if (cursize == 3) { // Jika 3 lebar
if (strip.getPixelColor(led_from_pixel(curpos + 1, currow + 1)) == strip.Color(0, 0, 0)) {
cursize -= 2; // 2 blok telah jatuh
animatedrop(curpos + 1, 2);
} else if (strip.getPixelColor(led_from_pixel(curpos + 2, currow + 1)) == strip.Color(0, 0, 0)) {
cursize -= 1; // 1 blok telah jatuh
animatedrop(curpos + 2, 1);
} else { // Tidak ada yang jatuh
if (buzzerPin != -1) {
tone(buzzerPin, 1000, 100);
}
}
} else if (strip.getPixelColor(led_from_pixel(curpos + 1, currow + 1)) == strip.Color(0, 0, 0)) {
cursize -= 1; // 1 blok telah jatuh
animatedrop(curpos + 1, 1);
} else { // Tidak ada yang jatuh, dengan hanya dua blok
if (buzzerPin != -1) {
tone(buzzerPin, 1000, 100); // Bunyi
}
}
} else { // Jika hanya ada satu blok
if (buzzerPin != -1) {
tone(buzzerPin, 1000, 100);
}
}
}
return false;
}
void resetGame() {
Serial.println("resetGame");
curpos = 2;
currow = bottom;
cursize = 3;
defaultInterval = random(6, 11) * 50;
Serial.print("defaultInterval =>"); Serial.println(defaultInterval);
interval = defaultInterval;
for (int i = 0; i < MATRIX_HEIGHT * MATRIX_WIDTH; i++) {
strip.setPixelColor(i, strip.Color(0, 0, 0));
}
strip.show();
curMillis = millis();
prevMillis = curMillis; // Waktu sebelumnya dalam milidetik
}
void winState() {
Serial.println("Winstate!"); // Mengeluarkan sukses
if (buzzerPin != -1) { // Mainkan sedikit lagu jika bisa
tone(buzzerPin, 1000, 100);
delay(100);
tone(buzzerPin, 1200, 100);
delay(100);
tone(buzzerPin, 1000, 100);
}
resetGame(); // Reset permainan untuk bermain lagi
}
void failState(int echo) { // Fungsi untuk akhir permainan
Serial.print("\nFailstate! ");
Serial.print(echo);
Serial.print("\n");
// Animasi kegagalan
animatedrop(curpos, cursize, true);
resetGame(); // Reset permainan untuk bermain lagi
}
void animatedrop(int pos, int width) { // Fungsi ini menganimasi kehilangan blok saat pemain melewati batas
Serial.println("Kurang Satu");
unsigned long tillAnimation = curMillis + animationDuration;
unsigned long blinkTimer = curMillis + (animationDuration / 8);
bool blink = false;
while (tillAnimation > curMillis) {
curMillis = millis();
prevMillis = curMillis;
if (blinkTimer < curMillis) {
if (blink) { // jika mati
for (int i = 0; i < width; i++) {
int r = pos + i;
int c = currow;
int index_led = led_from_pixel(r, c);
strip.setPixelColor(index_led, strip.Color(255, 0, 0));
}
strip.show();
blink = false;
blinkTimer = curMillis + (animationDuration / 8);
if (buzzerPin != -1) {
tone(buzzerPin, 262, (animationDuration / 8));
}
} else {
for (int i = 0; i < width; i++) {
int r = pos + i;
int c = currow;
int index_led = led_from_pixel(r, c);
strip.setPixelColor(index_led, strip.Color(0, 0, 0));
}
strip.show();
if (buzzerPin != -1) {
tone(buzzerPin, 288, (animationDuration / 8));
}
blink = true;
blinkTimer = curMillis + (animationDuration / 8);
}
strip.show();
}
}
}
void animatedrop(int pos, int width, bool isFail) { // Ini hanya dipanggil jika gagal
Serial.println("Gagal");
unsigned long tillAnimation = curMillis + animationDuration;
unsigned long blinkTimer = curMillis + (animationDuration / 8);
unsigned int buzz = 600;
unsigned int buzzI = 10;
bool blink = false;
while (tillAnimation > curMillis) {
curMillis = millis();
prevMillis = curMillis;
if (blinkTimer < curMillis) {
if (blink) { // jika mati
for (int i = 0; i < width; i++) {
int r = pos + i;
int c = currow;
int index_led = led_from_pixel(r, c);
strip.setPixelColor(index_led, strip.Color(255, 0, 0));
}
strip.show();
blink = false;
blinkTimer = curMillis + (animationDuration / 8);
} else {
for (int i = 0; i < width; i++) {
int r = pos + i;
int c = currow;
int index_led = led_from_pixel(r, c);
strip.setPixelColor(index_led, strip.Color(0, 0, 0));
}
strip.show();
blink = true;
blinkTimer = curMillis + (animationDuration / 8);
}
if (buzzerPin != -1) {
tone(buzzerPin, buzz, (animationDuration / 8));
}
buzz -= buzzI;
strip.show();
}
}
}
void buttonPressed() {
if (value == HIGH) {
clicked = true;
buttonwait = curMillis;
} else if (value == LOW) {
if (!clicked) {
if (currow != bottom) {
if (checkAir()) {
return;
}
} else {
if (buzzerPin != -1) {
tone(buzzerPin, 1000, 100);
}
}
currow--; // Bergerak ke atas pada baris
if (currow <= maxHeight) {
winState();
return;
}
if (currow < (bottom - (top / 4)) && cursize == 3) {
cursize--;
} else if (currow < (bottom - (top / 2)) && cursize == 2) {
cursize--;
}
onUpdate();
intervalModifier = random(5, 11) * 5; // Rentang 25 (5*5) hingga 50 (10*5)
interval -= intervalModifier;
if (interval < maxSpeed) { // Pencegahan agar tidak melewati kecepatan maksimum
interval = maxSpeed;
}
Serial.print("intervalModifier =>"); Serial.print(intervalModifier);
Serial.print("|| interval =>"); Serial.println(interval);
clicked = true;
buttonwait = curMillis;
}
}
}
void loop() {
curMillis = millis();
value = digitalRead(BUTTON_PIN);
if (value != lastState) {
lastState = value;
buttonPressed();
}
if (curMillis - prevMillis >= interval) { // Jika interval antara pembaruan telah tercapai
onUpdate(); // Pembaruan
}
if (clicked) {
if (curMillis - buttonwait >= 50) {
clicked = false;
}
}
}