MezData-Logo

1D-Pong mit Z-Eye

Spielanleitung

Schema

 

ballOrt
Ballposition 127..0

Wer kennt es nicht, das gute alte Pong –Tischtennis, hier als 1D-Variante. Der Ball fliegt zwischen L15 und L0 hin und her.

Wenn Ball L15 bzw. L0 erreicht, muss er mit T2 bzw. T0 zurückgeschlagen werden. Der Treffzeitpunkt beeinflusst die Ballgeschwindigkeit. Gegen das Programm kann gespielt werden, wenn nach dem Einschalten bei blinkender L3 Taste T1 gedrückt wird, die Rolle von Spieler 2 (L15, T2) übernimmt der Computer.

Quellcode [zPong.ino]
// Pongspiel mit Zylonenauge V 1.0 © Oliver Mezger 28.03.2020
#include <avr/io.h>
#define INIT_LEVEL_ZEIT 10 // prop. 1/Ballgeschwindigkeit
unsigned char ledausgabe[]={0xe1,0xe2,0xe4,0xe8,0xd1,0xd2,
0xd4,0xd8,0xb1,0xb2,0xb4,0xb8,0x71,0x72,0x74,0x78};
volatile unsigned char vram[] = {0,0,0,0}; // LED 4er Gruppen
unsigned char buttonOld=0,buttonEnter=0;
void checkButton(){
  unsigned char bu=~PIND>>4; // Einlesen
  buttonEnter = ~buttonOld & bu;
  buttonOld=bu;
}
void piep(){  // piepst bei Schlaegertreffer
  OCR1A = 2000;
  TCCR1A = 0b00100011; // Ausgang OC1B bei 0 setzen und bei OCR1B loeschen
  my_delay(20);
  TCCR1A = 0b00000011; // Sound wieder aus
}
void failpiep(){ // piepst bei "daneben"
  OCR1A = 2000; // 
  TCCR1A = 0b00100011; // Ausgang OC1B bei 0 setzen und bei OCR1B loeschen
  while(OCR1A<6000){
    OCR1A+=10;
    my_delay(2);
  }
  TCCR1A = 0b00000011; // Sound wieder aus
}
 
volatile unsigned int ticks; // wird in ISR hochgezaehlt
void my_delay(unsigned int n){
  ticks=0;
  while(ticks<n);
}
 
volatile unsigned char multiplex=0; // Anzeigenmuliplex fuer Spielstand anschalten
ISR(TIMER0_COMPA_vect){ // Interrupt Service Routine jede ms @4MHz
  static unsigned char i = 0;
  static const unsigned char vramEin[] = {0b11100000, 0b11010000, 0b10110000, 0b01110000};
  ticks++;
  if(multiplex){
    PORTB = vramEin[i] | vram[i];
    if (i < 3) i++; // vram durchgehen
    else i = 0;
  }
}
enum {AUS,FLIEG,SCHLAEGER,TREFFER,FEHLER} zustand=AUS;
unsigned char levelZeit=INIT_LEVEL_ZEIT,aPunkte=0,bPunkte=0;
#define ANZEIGEDELAY 1500
void anzeigenPunkte(){
  int i,k=0;
  for(i=0;i<=aPunkte;i++){
    k=k*2+1; // leuchtband
  }
  vram[0]=k&0xf;
  vram[1]=k>>4;
  k=0;
  for(i=0;i<=bPunkte;i++){
    k=k/2+0x80;
  }
  vram[2]=k&0xf;
  vram[3]=k>>4;
  multiplex=1;
  my_delay(ANZEIGEDELAY);
  multiplex=0;
}
void anzeigenSieg(){
  char i,k=0;
  for(i=0;i<4;i++){
    vram[i]=0;
  }
  multiplex=1;
  if (aPunkte > bPunkte){
    for (i=0;i<4;i++){
      for(k=1;k<=15;k=k*2+1){
        vram[i]=k;
        piep();
        my_delay(50);
      }
    }
  }else{
    for (i=3;i>=0;i--){
      k=0;
      do{
        k=k/2+8;
        vram[i]=k;
        piep();
        my_delay(50);
      } while(k<15);
    }
  }
  multiplex=0;
  aPunkte=bPunkte=0;
  levelZeit=INIT_LEVEL_ZEIT;
}
int main(){      // Hauptprogamm
  DDRB=0xff;     // PORTB auf Ausgang
  PORTD=0x7f;    // PullUps an
  TCCR1B = 0b00011001; // Waveform Generation Mode: Fast PWM it OCR1A als Top
  OCR1A = 2000; // Timer 1ms
  OCR1B = 500;  // Impulslaenge 500us
  TCCR0B = 3;       // Systemtakt / 64
  TCCR0A= 1<<WGM01; // CTC Mode mit OCR0A als TOP
  TIMSK = 1<<OCIE0A;// Interrupt frei geben
  OCR0A=62;         // bei 62 Interrupt ausloesen
  sei();
  unsigned char ballFlugZeit=10; // groesser = langsamer
  unsigned char up=1,ballOrt=0,autoMode=0;
  checkButton();
  while(1){ // Automode einstellen
    checkButton();
    if(buttonEnter&2){
      autoMode=1;
      break;
    }
    if(buttonEnter&5) break;
    PORTB=0xd1;
    my_delay(200);
    PORTB=0xd0;
    my_delay(300);
  }
  while(1){
    checkButton();
    switch(zustand){
      case AUS:  // kein Ball in der Luft
        if(buttonEnter){
          zustand=FLIEG;
          piep();
        }
        if(up){
          PORTB=0xe1; // LED0 an
          my_delay(5);
          PORTB=0xe0; // LED0 aus
        }else{
          PORTB=0x78; // LED15 an
          my_delay(5);
          PORTB=0x70; // LED15 aus
        }
        break;
      case FLIEG: // Ball fliegt bis in Schlaegerbereich
        if(up){
          if(ballOrt < 120) ballOrt++; 
          else zustand = SCHLAEGER; // 120
        } else {
          if(ballOrt >7) ballOrt--;
          else zustand = SCHLAEGER; // 7
        }
        PORTB=ledausgabe[ballOrt>>3]; // LED=ballOrt/8
        //if((ballOrt&0xf)==0)ballFlugZeit++; // Ball wird etwas langsamer
        break;
      case SCHLAEGER: // Ball ist im Schlaegerbereich
         if(up){
           if(ballOrt < 127){ // 120..126
            ballOrt++;
            if(autoMode && rand()%2==0) zustand = TREFFER; // ComputerGegner
            if(buttonEnter&0b100) zustand = TREFFER;
           }
           else{ // 127
            zustand = FEHLER;
            aPunkte++; 
           }
         } else{
           if(ballOrt > 0){ // 1..7
            ballOrt--;
            if(buttonEnter&0b001) zustand = TREFFER;
           }
           else{ // 0
            zustand = FEHLER; 
            bPunkte++;
           }
         }
         my_delay(ballFlugZeit/2); // Treffererleichterung
         break;
      case TREFFER: // Schlaeger trifft
         if(up){ // Trefferort beeinflusst Rueckfluggeschwindigkeit
           ballFlugZeit=ballOrt-120; // 0..6
         } else{
           ballFlugZeit=7-ballOrt;   // 6..0
         }
         ballFlugZeit+=levelZeit;   // + LevelZeit
         up=!up;
         piep();
         zustand = FLIEG;
         break;
      case FEHLER:
         up=!up;
         failpiep();
         ballFlugZeit=20;
         zustand = AUS;
         if((aPunkte+bPunkte)%2==1)levelZeit--; // 1 ms schneller
         if(aPunkte > 6 || bPunkte > 6) anzeigenSieg();
         else anzeigenPunkte();
    }
    my_delay(ballFlugZeit); // Zeit bis zum naechsten Ort
  }
}

Beschreibung der Software

Die Ballposition wird durch eine Variable ballOrt bestimmt. Damit die Spieler die Geschwindigkeit des rückgeschlagenen Balles beeinflussen können, wird der Zeitpunkt des Treffens im Schlägerbereich verwendet. Je früher getroffen wird, desto schneller fliegt der Ball zurück. Um die Zeit bestimmen zu können wird jeder LED 8 Ballpositionen zugeordnet: LED-Nummer = ballOrt / 8.

Um die Trefferwahrscheinlichkeit zu erhöhen wird im Schlägerbereich die Verweilzeit um 50% erhöht.