@@ -0,0 +1,327 @@
// ================== Projekt: Modul 2 ==================
// SPIELMECHANIK – mit Gewinnanimation + Gewinnsignal
//
// Fix integriert:
// - zeitbasierte Entprellung pro Taster
// - verhindert Mehrfachregistrierung eines einzelnen Tastendrucks
# include <Arduino.h>
# include <Wire.h>
# include <PCF8575.h>
# include <Adafruit_NeoPixel.h>
// ------------------ Hardware ------------------
PCF8575 expander ( 0x20 ) ;
# define PIN_NEOPIXEL_BUTTONS 4
# define NUM_NEOPIXEL_BUTTONS 18
# define PIN_NEOPIXEL_LOESUNG 6
# define NUM_NEOPIXEL_LOESUNG 6
# define PIN_POWER_ON 13
# define PIN_TRIGGER_SHUFFLE 2
# define PIN_SEND_WIN 5
// ------------------ Timing ------------------
# define WIN_OFF_DELAY_MS 300
# define WIN_STEP_DELAY_MS 60
# define DEBOUNCE_TIME_MS 200 // <<< Entprellzeit
Adafruit_NeoPixel pixelsButtons (
NUM_NEOPIXEL_BUTTONS ,
PIN_NEOPIXEL_BUTTONS ,
NEO_RGB + NEO_KHZ800
) ;
Adafruit_NeoPixel pixelsSolution (
NUM_NEOPIXEL_LOESUNG ,
PIN_NEOPIXEL_LOESUNG ,
NEO_RGB + NEO_KHZ800
) ;
// ------------------ Mapping ------------------
enum ButtonSource { SRC_NANO , SRC_PCF8575 } ;
struct LedButtonMap {
uint8_t ledIndex ;
ButtonSource source ;
uint8_t pin ;
} ;
const LedButtonMap ledButtonMap [ 18 ] = {
{ 0 , SRC_NANO , A0 } , { 1 , SRC_NANO , A1 } , { 2 , SRC_NANO , A2 } , { 3 , SRC_NANO , A3 } ,
{ 4 , SRC_NANO , 7 } , { 5 , SRC_NANO , 8 } ,
{ 6 , SRC_PCF8575 , 0 } , { 7 , SRC_PCF8575 , 1 } , { 8 , SRC_PCF8575 , 2 } , { 9 , SRC_PCF8575 , 3 } ,
{ 10 , SRC_PCF8575 , 4 } , { 11 , SRC_PCF8575 , 5 } , { 12 , SRC_PCF8575 , 6 } , { 13 , SRC_PCF8575 , 7 } ,
{ 14 , SRC_PCF8575 , 8 } , { 15 , SRC_PCF8575 , 9 } , { 16 , SRC_PCF8575 , 10 } , { 17 , SRC_PCF8575 , 11 }
} ;
// ------------------ Pride-Farben (dunkler) ------------------
const uint8_t prideColors [ 6 ] [ 3 ] = {
{ 25 , 0 , 0 } ,
{ 35 , 10 , 0 } ,
{ 25 , 25 , 0 } ,
{ 0 , 25 , 0 } ,
{ 0 , 0 , 25 } ,
{ 20 , 0 , 25 }
} ;
// ------------------ Spielzustände ------------------
uint8_t solutionOrder [ 6 ] ;
uint8_t expectedOrder [ 6 ] ;
int8_t buttonAssignment [ 18 ] ;
bool winActive = false ;
bool powerOnState = true ;
bool lastPressed [ 18 ] ;
unsigned long ledOffTime [ 18 ] ;
unsigned long lastPressTime [ 18 ] ; // <<< Entprellspeicher
uint8_t progressIndex = 0 ;
bool lastTriggerState = LOW ;
// ------------------ Hilfsfunktionen ------------------
bool readButton ( uint8_t i ) {
if ( ledButtonMap [ i ] . source = = SRC_NANO )
return digitalRead ( ledButtonMap [ i ] . pin ) = = LOW ;
return expander . read ( ledButtonMap [ i ] . pin ) = = LOW ;
}
// ------------------ Neues Spiel ------------------
void generateNewGame ( ) {
for ( uint8_t i = 0 ; i < 6 ; i + + ) solutionOrder [ i ] = i ;
for ( int i = 5 ; i > 0 ; i - - ) {
int j = random ( i + 1 ) ;
uint8_t t = solutionOrder [ i ] ;
solutionOrder [ i ] = solutionOrder [ j ] ;
solutionOrder [ j ] = t ;
}
for ( uint8_t i = 0 ; i < 6 ; i + + )
expectedOrder [ i ] = solutionOrder [ 5 - i ] ;
for ( uint8_t i = 0 ; i < 6 ; i + + ) {
uint8_t c = solutionOrder [ i ] ;
pixelsSolution . setPixelColor (
i ,
pixelsSolution . Color (
prideColors [ c ] [ 0 ] ,
prideColors [ c ] [ 1 ] ,
prideColors [ c ] [ 2 ]
)
) ;
}
pixelsSolution . show ( ) ;
for ( uint8_t i = 0 ; i < 18 ; i + + ) {
buttonAssignment [ i ] = - 1 ;
pixelsButtons . setPixelColor ( i , 0 ) ;
ledOffTime [ i ] = 0 ;
lastPressTime [ i ] = 0 ;
}
pixelsButtons . show ( ) ;
uint8_t idx [ 18 ] ;
for ( uint8_t i = 0 ; i < 18 ; i + + ) idx [ i ] = i ;
for ( int i = 17 ; i > 0 ; i - - ) {
int j = random ( i + 1 ) ;
uint8_t t = idx [ i ] ;
idx [ i ] = idx [ j ] ;
idx [ j ] = t ;
}
for ( uint8_t i = 0 ; i < 6 ; i + + )
buttonAssignment [ idx [ i ] ] = solutionOrder [ i ] ;
progressIndex = 0 ;
}
// ------------------ Gewinnanimation + Signal ------------------
void winAnimation ( ) {
winActive = true ;
digitalWrite ( PIN_SEND_WIN , HIGH ) ;
// 1) letzte korrekte Farbe stehen lassen
delay ( 500 ) ;
// 2) ALLES AUS (inkl. Off-Timer löschen)
for ( uint8_t i = 0 ; i < 18 ; i + + )
ledOffTime [ i ] = 0 ;
pixelsSolution . clear ( ) ;
pixelsButtons . clear ( ) ;
pixelsSolution . show ( ) ;
pixelsButtons . show ( ) ;
delay ( WIN_OFF_DELAY_MS ) ;
// 3) richtige Reihenfolge (umgekehrt), Lösung + Taster synchron
for ( int i = 5 ; i > = 0 ; i - - ) {
uint8_t color = solutionOrder [ i ] ;
// Lösungs-LED
pixelsSolution . setPixelColor (
i ,
pixelsSolution . Color (
prideColors [ color ] [ 0 ] ,
prideColors [ color ] [ 1 ] ,
prideColors [ color ] [ 2 ]
)
) ;
// zugehöriger Taster
for ( uint8_t b = 0 ; b < 18 ; b + + ) {
if ( buttonAssignment [ b ] = = color ) {
pixelsButtons . setPixelColor (
b ,
pixelsButtons . Color (
prideColors [ color ] [ 0 ] ,
prideColors [ color ] [ 1 ] ,
prideColors [ color ] [ 2 ]
)
) ;
break ;
}
}
pixelsSolution . show ( ) ;
pixelsButtons . show ( ) ;
delay ( WIN_STEP_DELAY_MS ) ;
}
digitalWrite ( PIN_SEND_WIN , LOW ) ;
winActive = false ;
}
// ------------------ Setup ------------------
void setup ( ) {
randomSeed ( analogRead ( A0 ) ) ;
Wire . begin ( ) ;
expander . begin ( ) ;
pinMode ( PIN_TRIGGER_SHUFFLE , INPUT ) ;
pinMode ( PIN_POWER_ON , INPUT ) ;
powerOnState = digitalRead ( PIN_POWER_ON ) ;
pinMode ( PIN_SEND_WIN , OUTPUT ) ;
digitalWrite ( PIN_SEND_WIN , LOW ) ;
pinMode ( A0 , INPUT_PULLUP ) ;
pinMode ( A1 , INPUT_PULLUP ) ;
pinMode ( A2 , INPUT_PULLUP ) ;
pinMode ( A3 , INPUT_PULLUP ) ;
pinMode ( 7 , INPUT_PULLUP ) ;
pinMode ( 8 , INPUT_PULLUP ) ;
for ( uint8_t i = 0 ; i < 12 ; i + + )
expander . write ( i , HIGH ) ;
pixelsButtons . begin ( ) ;
pixelsSolution . begin ( ) ;
generateNewGame ( ) ;
for ( uint8_t i = 0 ; i < 18 ; i + + )
lastPressed [ i ] = false ;
}
// ------------------ Loop ------------------
void loop ( ) {
if ( powerOnState & & digitalRead ( PIN_POWER_ON ) = = LOW )
{
powerOnState = false ;
pixelsSolution . begin ( ) ;
for ( size_t i = 0 ; i < 6 ; i + + )
{
pixelsSolution . setPixelColor ( i , pixelsSolution . Color ( 0 , 0 , 0 , 0 ) ) ;
}
pixelsSolution . show ( ) ;
pixelsButtons . begin ( ) ;
for ( size_t i = 0 ; i < 18 ; i + + )
{
pixelsButtons . setPixelColor ( i , pixelsButtons . Color ( 0 , 0 , 0 , 0 ) ) ;
}
pixelsButtons . show ( ) ;
}
// Detecting turn-on
if ( ! powerOnState & & digitalRead ( PIN_POWER_ON ) = = HIGH )
{
generateNewGame ( ) ;
powerOnState = true ;
}
if ( ! powerOnState )
{
// We are turned-off and stop anything after here
return ;
}
unsigned long now = millis ( ) ;
bool trigger = digitalRead ( PIN_TRIGGER_SHUFFLE ) ;
if ( trigger & & ! lastTriggerState ) {
generateNewGame ( ) ;
}
lastTriggerState = trigger ;
for ( uint8_t i = 0 ; i < 18 ; i + + ) {
bool pressed = readButton ( i ) ;
if ( pressed & & ! lastPressed [ i ] & &
( now - lastPressTime [ i ] > DEBOUNCE_TIME_MS ) ) {
lastPressTime [ i ] = now ;
int8_t color = buttonAssignment [ i ] ;
if ( color > = 0 ) {
pixelsButtons . setPixelColor (
i ,
pixelsButtons . Color (
prideColors [ color ] [ 0 ] ,
prideColors [ color ] [ 1 ] ,
prideColors [ color ] [ 2 ]
)
) ;
pixelsButtons . show ( ) ;
ledOffTime [ i ] = now + 1000 ;
}
if ( color < 0 | | color ! = expectedOrder [ progressIndex ] ) {
progressIndex = 0 ;
} else {
progressIndex + + ;
if ( progressIndex = = 6 ) {
winAnimation ( ) ;
progressIndex = 0 ;
}
}
}
if ( ! winActive & & ledOffTime [ i ] & & now > = ledOffTime [ i ] ) {
pixelsButtons . setPixelColor ( i , 0 ) ;
pixelsButtons . show ( ) ;
ledOffTime [ i ] = 0 ;
}
lastPressed [ i ] = pressed ;
}
}