master f9b99e4980c3 cached
36 files
598.6 KB
255.5k tokens
1 requests
Download .txt
Showing preview only (620K chars total). Download the full file or copy to clipboard to get everything.
Repository: andyhighnumber/Attiny-Arduino-Games
Branch: master
Commit: f9b99e4980c3
Files: 36
Total size: 598.6 KB

Directory structure:
gitextract_6g324x5n/

├── BatBonanzaAnalog/
│   ├── BatBonanzaAnalog.ino
│   └── font6x8AJ.h
├── BatBonanzaAnalogSinglePot/
│   ├── BatBonanzaAnalogSinglePot.ino
│   └── font6x8AJ.h
├── BatBonanzaAttinyArcade/
│   ├── BatBonanzaAttinyArcade.ino
│   └── font6x8AJ.h
├── Frogger_Attiny_Arcade/
│   ├── Frogger_Attiny_Arcade.ino
│   └── font6x8AJ2.h
├── Frogger_MAKERbuino/
│   ├── Bitmaps/
│   │   └── InfEncoder.jar
│   ├── Frogger_MAKERbuino.ino
│   ├── HEX and INF/
│   │   ├── FROGGER.HEX
│   │   └── FROGGER.INF
│   └── font6x8AJ4.h
├── MorseAttinyArcade/
│   ├── MorseAttinyArcade.ino
│   └── font6x8AJ.h
├── PAK-MAN_MAKERbuino/
│   ├── HEX and INF/
│   │   ├── PAKMAN.HEX
│   │   └── PAKMAN.INF
│   ├── PAK-MAN_MAKERbuino.ino
│   └── font6x8AJ4.h
├── Pacman_Attiny_Arcade/
│   ├── Pacman_Attiny_Arcade.ino
│   └── font6x8AJ3.h
├── README.md
├── SpaceAttackAttiny/
│   ├── SpaceAttackAttiny.ino
│   └── font6x8AJ.h
├── Space_Attack_Analog/
│   ├── Space_Attack_Analog.ino
│   └── font6x8AJ.h
├── Tetris_Attiny_Arcade/
│   ├── Tetris_Attiny_Arcade.ino
│   └── font8x8AJ.h
├── Tetris_Multi_Button/
│   ├── Tetris_Multi_Button.ino
│   └── font8x8AJ.h
├── UFO_Breakout_Arduino/
│   ├── UFO_Breakout_Arduino.ino
│   └── font6x8AJ.h
├── UFO_Stacker_Attiny/
│   ├── UFO_Stacker_Attiny.ino
│   └── font6x8AJ.h
└── WrenRollercoasterAttinyArcade/
    ├── WrenRollercoasterAttinyArcade.ino
    └── font6x8AJ.h

================================================
FILE CONTENTS
================================================

================================================
FILE: BatBonanzaAnalog/BatBonanzaAnalog.ino
================================================
/* 2015 / 2016 /2017
 * Pong clone by Andy Jackson - Twitter @andyhighnumber
 * Inspired by http://webboggles.com/ and includes some code from the #AttinyArcade games on that site
 * The code that does not fall under the licenses of sources listed below can be used non commercially with attribution.
 * 
 * This code takes its inputs from two paddles - one on pin 7 (the centre of a 10k linear pot between the power rails), the other
 * on the reset pin - this time with a 6k8 resistor between the negative leg of the pot and ground (to stop it resetting the ATTINY85!)
 * You should be able to find the circuit diagram from the folder where these files are (if you got them from my Google Drive) otherwise
 * Tweet @andyhighnumber and I will direct you to the circuit.
 * 
 * There's one button in this design (on pin 5). When the game is running, pressing and releasing the button cycles through modes, including 
 * two-player games and one-player modes with varying degrees of difficulty. 
 * 
 * Also, from standby....
 *  Press and hold the button to reset all settings (good to do when you first flash the chip, since the settings are loaded from EEPROM)
 * 
 * If you have problems uploading this sketch, this is probably due to sketch size - you need to update ld.exe in arduino\hardware\tools\avr\avr\bin
 * https://github.com/TCWORLD/ATTinyCore/tree/master/PCREL%20Patch%20for%20GCC
 *
 * This sketch is using the screen control and font functions written by Neven Boyanov for the http://tinusaur.wordpress.com/ project
 * Source code and font files available at: https://bitbucket.org/tinusaur/ssd1306xled
 * **Note that this highly size-optimised version requires modified library functions (which are in this source code file) 
 * and a modified font header
 * 
 * Sleep code is based on this blog post by Matthew Little:
 * http://www.re-innovation.co.uk/web12/index.php/en/blog-75/306-sleep-modes-on-attiny85
*/
#include <EEPROM.h>
#include "font6x8AJ.h"
#include <avr/sleep.h>
#include <avr/interrupt.h> // needed for the additional interrupt

#define DIGITAL_WRITE_HIGH(PORT) PORTB |= (1 << PORT)
#define DIGITAL_WRITE_LOW(PORT) PORTB &= ~(1 << PORT)

// Routines to set and clear bits (used in the sleep code)
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

// Defines for OLED output
#define SSD1306XLED_H
#define SSD1306_SCL   PORTB4  // SCL, Pin 4 on SSD1306 Board - for webbogles board
#define SSD1306_SDA   PORTB3  // SDA, Pin 3 on SSD1306 Board - for webbogles board
#define SSD1306_SA    0x78  // Slave address

#define WINSCORE 7

// Function prototypes
void startGame(void);
void drawPlatform(void);
void drawPlatform2(void);
void sendBlock(int);
void playPong(void);
void beep(int,int);
void drawBall(int x, int y);
void blankBall(int x, int y);

void doDrawLS(long, byte);
void doDrawRS(long, byte);
void doNumber (int x, int y, int value);

void ssd1306_init(void);
void ssd1306_xfer_start(void);
void ssd1306_xfer_stop(void);
void ssd1306_send_byte(uint8_t byte);
void ssd1306_send_command(uint8_t command);
void ssd1306_send_data_start(void);
void ssd1306_send_data_stop(void);
void ssd1306_setpos(uint8_t x, uint8_t y);
void ssd1306_fillscreen(uint8_t fill_Data);
void ssd1306_char_f6x8(uint8_t x, uint8_t y, const char ch[]);
void ssd1306_draw_bmp(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t bitmap[]);

int topScoreB = 0;
int player; //0 to 128-platformWidth  - this is the position of the player
int player2; //0 to 128-platformWidth  - this is the position of the player
int lastPlayer;
int lastPlayer2;
int platformWidth = 16; 
boolean stopAnimate = 0; // this is set to 1 when a collision is detected
boolean mute = 0;
int score = 0; // score - this affects the difficulty of the game
int score2 = 0; // score - this affects the difficulty of the game

int ballx = 62*8; // coordinate of the ball
int bally = 50*4; // coordinate of the ball
int vdir = -4; // vertical direction and step  distance
int hdir = -8; // horizontal direction and step distance

int mode = 0;

int perturbation = 0;
int pFactor = 12;

// Interrupt handlers
ISR(PCINT0_vect){ // PB0 pin button interrupt           
}

void playerIncPong(){ // PB2 pin button interrupt
}

// Arduino stuff - setup
void setup() {
  DDRB = 0b00000010;    // set PB1 as output (for the speaker)
  PCMSK = 0b00000001; // pin change mask: listen to portb bit 1
  GIMSK |= 0b00100000;  // enable PCINT interrupt 
  sei();          // enable all interrupts
}

// Arduino stuff - loop
void loop() { 
  ssd1306_init();
  ssd1306_fillscreen(0x00);
  
  // The lower case character set is seriously compromised because I've had to truncate the ASCII table
  // to release space for executable code - hence lower case y and w are remapped to h and / respectively.
  // There is no z in the table (or h!) as these aren't used anywhere in the text here and most of the 
  // symbols are also missing for the same reason (see my hacked version of font6x8.h - font6x8AJ.h for more detail)
  ssd1306_char_f6x8(0, 1, "   ---------------  ");
  ssd1306_char_f6x8(0, 2, "        B A T       ");
  ssd1306_char_f6x8(0, 4, "    B O N A N Z A   ");
  ssd1306_char_f6x8(0, 5, "   ---------------  ");
  ssd1306_char_f6x8(0, 7, "   bh andh jackson  "); // see comments above !

   long startT = millis();
    long nowT =0;
    boolean sChange = 0;
    while(digitalRead(0) == HIGH) {
      nowT = millis();
      if (nowT - startT > 2000) {
        sChange = 1;     
        EEPROM.write(0,0);
        EEPROM.write(1,0);
        ssd1306_char_f6x8(16, 0, "- SYSTEM RESET -");  
        break;
      }
      if (sChange == 1) break;
    }  
    while(digitalRead(0) == HIGH);

    mute=EEPROM.read(0);
    mode=EEPROM.read(1);

    if (mute != 0 && mute != 1) {
      mute = 0;
       EEPROM.write(0,0);
    }

    if (mode < 0 || mode > 5) {
      mode = 0;
      EEPROM.write(1,0);
    }

    if (sChange != 1) {
    delay(1500);
    ssd1306_init();
    ssd1306_fillscreen(0x00);
    stopAnimate = 0;
    score = 0;
    score2 = 0;

    playPong(); 
    
    delay(3500);
 
  }
  system_sleep();
}

void doNumber (int x, int y, int value) {
    char temp[10] = {0,0,0,0,0,0,0,0,0,0};
    itoa(value,temp,10);
    ssd1306_char_f6x8(x, y, temp);
}

void ssd1306_init(void){
  DDRB |= (1 << SSD1306_SDA); // Set port as output
  DDRB |= (1 << SSD1306_SCL); // Set port as output

  ssd1306_send_command(0xAE); // display off
  ssd1306_send_command(0x00); // Set Memory Addressing Mode
  ssd1306_send_command(0x10); // 00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
  ssd1306_send_command(0x40); // Set Page Start Address for Page Addressing Mode,0-7
  ssd1306_send_command(0x81); // Set COM Output Scan Direction
  ssd1306_send_command(0xCF); // ---set low column address
  ssd1306_send_command(0xA1); // ---set high column address
  ssd1306_send_command(0xC8); // --set start line address
  ssd1306_send_command(0xA6); // --set contrast control register
  ssd1306_send_command(0xA8);
  ssd1306_send_command(0x3F); // --set segment re-map 0 to 127
  ssd1306_send_command(0xD3); // --set normal display
  ssd1306_send_command(0x00); // --set multiplex ratio(1 to 64)
  ssd1306_send_command(0xD5); // 
  ssd1306_send_command(0x80); // 0xa4,Output follows RAM content;0xa5,Output ignores RAM content
  ssd1306_send_command(0xD9); // -set display offset
  ssd1306_send_command(0xF1); // -not offset
  ssd1306_send_command(0xDA); // --set display clock divide ratio/oscillator frequency
  ssd1306_send_command(0x12); // --set divide ratio
  ssd1306_send_command(0xDB); // --set pre-charge period
  ssd1306_send_command(0x40); // 
  ssd1306_send_command(0x20); // --set com pins hardware configuration
  ssd1306_send_command(0x02);
  ssd1306_send_command(0x8D); // --set vcomh
  ssd1306_send_command(0x14); // 0x20,0.77xVcc
  ssd1306_send_command(0xA4); // --set DC-DC enable
  ssd1306_send_command(0xA6); // 
  ssd1306_send_command(0xAF); // --turn on oled panel 
}

void ssd1306_xfer_start(void){
  DIGITAL_WRITE_HIGH(SSD1306_SCL);  // Set to HIGH
  DIGITAL_WRITE_HIGH(SSD1306_SDA);  // Set to HIGH
  DIGITAL_WRITE_LOW(SSD1306_SDA);   // Set to LOW
  DIGITAL_WRITE_LOW(SSD1306_SCL);   // Set to LOW
}

void ssd1306_xfer_stop(void){
  DIGITAL_WRITE_LOW(SSD1306_SCL);   // Set to LOW
  DIGITAL_WRITE_LOW(SSD1306_SDA);   // Set to LOW
  DIGITAL_WRITE_HIGH(SSD1306_SCL);  // Set to HIGH
  DIGITAL_WRITE_HIGH(SSD1306_SDA);  // Set to HIGH
}

void ssd1306_send_byte(uint8_t byte){
  uint8_t i;
  for(i=0; i<8; i++)
  {
    if((byte << i) & 0x80)
      DIGITAL_WRITE_HIGH(SSD1306_SDA);
    else
      DIGITAL_WRITE_LOW(SSD1306_SDA);
    
    DIGITAL_WRITE_HIGH(SSD1306_SCL);
    DIGITAL_WRITE_LOW(SSD1306_SCL);
  }
  DIGITAL_WRITE_HIGH(SSD1306_SDA);
  DIGITAL_WRITE_HIGH(SSD1306_SCL);
  DIGITAL_WRITE_LOW(SSD1306_SCL);
}

void ssd1306_send_command(uint8_t command){
  ssd1306_xfer_start();
  ssd1306_send_byte(SSD1306_SA);  // Slave address, SA0=0
  ssd1306_send_byte(0x00);  // write command
  ssd1306_send_byte(command);
  ssd1306_xfer_stop();
}

void ssd1306_send_data_start(void){
  ssd1306_xfer_start();
  ssd1306_send_byte(SSD1306_SA);
  ssd1306_send_byte(0x40);  //write data
}

void ssd1306_send_data_stop(void){
  ssd1306_xfer_stop();
}

void ssd1306_setpos(uint8_t x, uint8_t y)
{
  if (y>7) return;
  ssd1306_xfer_start();
  ssd1306_send_byte(SSD1306_SA);  //Slave address,SA0=0
  ssd1306_send_byte(0x00);  //write command

  ssd1306_send_byte(0xb0+y);
  ssd1306_send_byte(((x&0xf0)>>4)|0x10); // |0x10
  ssd1306_send_byte((x&0x0f)|0x01); // |0x01

  ssd1306_xfer_stop();
}

void ssd1306_fillscreen(uint8_t fill_Data){
  uint8_t m,n;
  for(m=0;m<8;m++)
  {
    ssd1306_send_command(0xb0+m); //page0-page1
    ssd1306_send_command(0x00);   //low column start address
    ssd1306_send_command(0x10);   //high column start address
    ssd1306_send_data_start();
    for(n=0;n<128;n++)
    {
      ssd1306_send_byte(fill_Data);
    }
    ssd1306_send_data_stop();
  }
}

void ssd1306_char_f6x8(uint8_t x, uint8_t y, const char ch[]){
  uint8_t c,i,j=0;
  while(ch[j] != '\0')
  {
    c = ch[j] - 32;
    if (c >0) c = c - 12;
    if (c >15) c = c - 6;
    if (c>40) c=c-6;
    if(x>126)
    {
      x=0;
      y++;
    }
    ssd1306_setpos(x,y);
    ssd1306_send_data_start();
    for(i=0;i<6;i++)
    {
      ssd1306_send_byte(pgm_read_byte(&ssd1306xled_font6x8[c*6+i]));
    }
    ssd1306_send_data_stop();
    x += 6;
    j++;
  }
}

void system_sleep() {
  ssd1306_fillscreen(0x00);
  ssd1306_send_command(0xAE);
  cbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter OFF
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  sleep_enable();
  sleep_mode();                        // System actually sleeps here
  sleep_disable();                     // System continues execution here when watchdog timed out 
  sbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter ON  
  ssd1306_send_command(0xAF);
}

void beep(int bCount,int bDelay){
  if (mute) return;
  for (int i = 0; i<=bCount; i++){digitalWrite(1,HIGH);for(int i2=0; i2<bDelay; i2++){__asm__("nop\n\t");}digitalWrite(1,LOW);for(int i2=0; i2<bDelay; i2++){__asm__("nop\n\t");}}
}



/* ------------------------
 *  Pong Code
 */
void playPong() {
  ballx = 64*8;
  bally = 32*4;
  hdir = -8;
  vdir = -4;
  int actualy, actualx;
  int factor = 0;
  int waitCount = 0;

  int lastx=64*8, lasty=32*4;

  player=64;
  player2=64;
  lastPlayer = 64;
  lastPlayer2 = 64;
  score = 0; // obvious
  score2 = 0; // obvious
  perturbation = 0;
  
  startGame();
  while (stopAnimate == 0) {
    while(1) {
    waitCount++;
    
    if(digitalRead(0)==1) {
      boolean sChange = 0;
      long startT = millis();
      long nowT =0;
      while(digitalRead(0) == HIGH) {
        nowT = millis();
        if (nowT - startT > 1500) {
          sChange = 1;
          if (mute == 0) { mute = 1; ssd1306_char_f6x8(32, 0, "-- MUTE --"); } else { mute = 0; ssd1306_char_f6x8(23, 0, "-- SOUND ON --"); }
          break;
        }
      }
      while(digitalRead(0) == HIGH);
      if (sChange == 1) {
      } else if (mode == 0) { 
          mode = 1; 
          ssd1306_char_f6x8(26, 0, "-- EXPERT --");  
          } else if (mode == 1) { 
            mode = 2; 
            pFactor = 11;
            ssd1306_char_f6x8(32, 0, "-- AUTO --"); 
          } else if (mode == 2) { 
            mode = 3; 
            pFactor = 11;
            ssd1306_char_f6x8(20, 0, "- TOUGH AUTO -");  
          } else if (mode == 3) { 
            mode = 4; 
            pFactor = 10;
            ssd1306_char_f6x8(16, 0, "- EXPERT AUTO -");  
          } else if (mode == 4) { 
            mode = 0; 
            ssd1306_char_f6x8(26, 0, "-- NORMAL --");  
          }
      
      if (sChange == 0) delay(1000);
      ssd1306_fillscreen(0x00);        
      EEPROM.write(0,mute);
      EEPROM.write(1,mode);
    }

    player = ((analogRead(0)-560)/7);
        if (player > 48) player = 48;
    if (player <0) player = 0;


    player2 = (analogRead(1) / 16);
    if (mode == 2 || mode == 3 || mode == 4) {
      if(waitCount >= 3) {
        waitCount = 0;
        perturbation = perturbation - 2 + random(0,5);
        if (perturbation > pFactor) perturbation = pFactor - 2;
        if (perturbation < pFactor*-1) perturbation = (pFactor*-1)+2;
      }
      player2 = (bally/4 -8)+perturbation;
    }
    if (player2 > 48) player2 = 48;
    if (player2 <0) player2 = 0;

    actualy = floor(bally/4);
    actualx = floor(ballx/8);
    
    // bounce off the sides of the screen
    if ((actualy+vdir<63&&vdir>01) || (actualy- vdir>6&&vdir<0)){
      bally+=vdir;
    }else {
        vdir = vdir*-1;
    }
    ballx+=hdir;

    actualy = floor(bally/4);
    actualx = floor(ballx/8);
    
    // check it hits the left pad and deal with bounces and misses
    if (actualx <= 4) {
      if(actualy<player-1||actualy>player+platformWidth+1){ 
        score2++;
    
        ballx = 5*8;
        bally = player*4;

        hdir = 13;
        if (vdir > 0) {
          vdir = 2;
        } else vdir = -2;
        
        ssd1306_fillscreen(0x00);        
        doNumber(46,4,score);
        doNumber(78,4,score2);        
        if (score2 < WINSCORE) {
          for (int i = 0; i<1000; i = i+ 100){
            beep(50,i);
          }
          for (int incr=0;incr<3;incr++) {
              ssd1306_send_data_stop();
              ssd1306_setpos(78,4);
              ssd1306_send_data_start();
              sendBlock(0);
              sendBlock(0);
              ssd1306_send_data_stop();
              delay(350);
              doNumber(78,4,score2);
              delay(350);
            }
            startGame();
        }
        perturbation = 0;
        break;
      }else if (actualy<player+1){        
        vdir = -6;
        hdir = 7;
      }else if (actualy<player+4){        
        vdir = -4;
        hdir = 10;
      }else if (actualy<player+7){        
        vdir = -2;
        hdir = 13;
      }else if (actualy<player+9){        
        vdir = 0;
        hdir = 14;
      }else if (actualy<player+12){        
        vdir = 2;
        hdir = 13;
      }else if (actualy<player+15){        
        vdir = 4;
        hdir = 10;
      }else {   
        vdir = 6;
        hdir = 7;
      }
      beep(20,600);
    }  
    
    // check it hits the right pad and deal with bounces
    if(actualx >= 122) {
      if(actualy<player2-1||actualy>player2+platformWidth+1){
        score++;
  
        ballx = 120*8;
        bally = player2*4;

        hdir = -13;
        if (vdir > 0) {
          vdir = 2;
        } else vdir = -2;

        ssd1306_fillscreen(0x00);        
        doNumber(46,4,score);
        doNumber(78,4,score2);
        if (score < WINSCORE) {
        for (int i = 0; i<1000; i = i+ 100){
            beep(50,i);
          }                
  
          for (int incr=0;incr<3;incr++) {
              ssd1306_setpos(46,4);
              ssd1306_send_data_start();
              sendBlock(0);
              sendBlock(0);
              ssd1306_send_data_stop();
              delay(350);
              doNumber(46,4,score);
              delay(350);
            }
            perturbation = 0;
            startGame();
        }        
        break;
      }else if (actualy<player2+1){        
        vdir = -6;
        hdir = -7;
      }else if (actualy<player2+4){        
        vdir = -4;
        hdir = -10;
      }else if (actualy<player2+7){        
        vdir = -2;
        hdir = -13;
      }else if (actualy<player2+9){        
        vdir = 0;
        hdir = -14;
      }else if (actualy<player2+12){        
        vdir = 2;
        hdir = -13;
      }else if (actualy<player2+15){        
        vdir = 4;
        hdir = -10;
      }else {   
        vdir = 6;
        hdir = -7;
      }
      beep(20,300);
    }      

    
    if (mode == 1 || mode == 3 || mode == 4) {
      factor = 8-floor((score-score2)/2); // expert modes
      if (factor < 2) factor = 2;
    } else {
      factor = 20-floor((score-score2)/2); // normal modes
      if (factor < 10) factor = 10;
    }
    
    delay(factor);
    
    // draw ball
    blankBall(floor(lastx/8),floor(lasty/4));  
    drawPlatform();
    drawPlatform2();
    drawBall(floor(ballx/8),floor(bally/4));
    lastx = ballx;
    lasty = bally;
    
    doNumber(28,0,score);
    doNumber(92,0,score2);
    if (score == WINSCORE || score2 == WINSCORE) {
      stopAnimate = 1;
      break;
    }
    }
 }

blankBall(floor(lastx/8),floor(lasty/4));  
blankBall(floor(ballx/8),floor(bally/4));  

if (score > score2) {
  ssd1306_char_f6x8(27, 3, "P L A Y E R 1"); 
} else {
  ssd1306_char_f6x8(27, 3, "P L A Y E R 2"); 
}
ssd1306_char_f6x8(27, 4, "             ");
ssd1306_char_f6x8(27, 5, "   W I N S   ");

for (int i = 0; i<1000; i = i+ 50){
  beep(50,i);
}


for (int incr=0;incr<6;incr++) {
    ssd1306_setpos(28,0);
    ssd1306_send_data_start();
    sendBlock(0);
    sendBlock(0);
    ssd1306_send_data_stop();
    ssd1306_setpos(92,0);
    ssd1306_send_data_start();
    sendBlock(0);
    sendBlock(0);
    ssd1306_send_data_stop();
    delay(350);
    doNumber(28,0,score);
    doNumber(92,0,score2);
    delay(350);
  }
}

void drawPlatform() {
  if (player != lastPlayer) {
    ssd1306_setpos(0,lastPlayer/8);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop();
    ssd1306_setpos(0,lastPlayer/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop(); 
    ssd1306_setpos(0,lastPlayer/8+2);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop(); 
  }  

  if (player%8!=0){
    ssd1306_setpos(0,player/8);
    ssd1306_send_data_start();
    ssd1306_send_byte((B11111111)<<player%8);
    ssd1306_send_data_stop();
    ssd1306_setpos(0,player/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B11111111);
    ssd1306_send_data_stop();    
    ssd1306_setpos(0,player/8+2);
    ssd1306_send_data_start();
    ssd1306_send_byte((B01111110)>>8-player%8);
    ssd1306_send_data_stop();
  } else {
    ssd1306_setpos(0,player/8);
    ssd1306_send_data_start();
    ssd1306_send_byte(B11111111);
    ssd1306_send_data_stop();
    ssd1306_setpos(0,player/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B11111111);
    ssd1306_send_data_stop();

  }
  lastPlayer = player;
}

void drawPlatform2() {

  if (player2 != lastPlayer2) {
    ssd1306_setpos(127,lastPlayer2/8);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop();
    ssd1306_setpos(127,lastPlayer2/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop(); 
    ssd1306_setpos(127,lastPlayer2/8+2);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop(); 
  }
  
  if (player2%8!=0){
    ssd1306_setpos(127,player2/8);
    ssd1306_send_data_start();
    ssd1306_send_byte((B11111111)<<player2%8);
    ssd1306_send_data_stop();
    ssd1306_setpos(127,player2/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B11111111);
    ssd1306_send_data_stop();        
    ssd1306_setpos(127,player2/8+2);
    ssd1306_send_data_start();
    ssd1306_send_byte((B01111110)>>8-player2%8);
    ssd1306_send_data_stop();
  } else {
    ssd1306_setpos(127,player2/8);
    ssd1306_send_data_start();
    ssd1306_send_byte((B11111111)<<0);
    ssd1306_send_data_stop();
    ssd1306_setpos(127,player2/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte((B11111111)<<0);
    ssd1306_send_data_stop();
  }
  lastPlayer2 = player2;
}



void sendBlock(int fill){
  if (fill == 1) {
   ssd1306_send_byte(B10011000);
   ssd1306_send_byte(B01011100);
   ssd1306_send_byte(B10110110);
   ssd1306_send_byte(B01011111);
   ssd1306_send_byte(B01011111);
   ssd1306_send_byte(B10110110);
   ssd1306_send_byte(B01011100);
   ssd1306_send_byte(B10011000);
  } else  {
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
  } 
}

void blankBall(int x, int y) {
  if (y%8!=0){
    ssd1306_setpos(x,y/8);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop();
    
    ssd1306_setpos(x,y/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop();
  } else {
    ssd1306_setpos(x,y/8);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop();
  }
}


void drawBall(int x, int y) {
  if (y%8!=0){
    ssd1306_setpos(x,y/8);
    ssd1306_send_data_start();
    doDrawLS(0,y%8);
    ssd1306_send_data_stop();
    
    ssd1306_setpos(x,y/8+1);
    ssd1306_send_data_start();
    doDrawRS(0,8-y%8);
    ssd1306_send_data_stop();
  } else {
    ssd1306_setpos(x,y/8);
    ssd1306_send_data_start();
    doDrawLS(0,0);
    ssd1306_send_data_stop();
  }
}

// Drawing routine for the player and fire - with right-shifts
void doDrawRS(long P1, byte P2) {
  ssd1306_send_byte((B00000011 | P1)>>P2);
  ssd1306_send_byte((B00000011 | P1)>>P2);
}

// Drawing routine for the player and fire - with left-shifts
void doDrawLS(long P1, byte P2) {
  ssd1306_send_byte((B00000011 | P1)<<P2);
  ssd1306_send_byte((B00000011 | P1)<<P2);
}

void startGame(void) {
  
    ssd1306_fillscreen(0x00);

    ssd1306_char_f6x8(16, 3, "-- GET READY --");
    doNumber(60,5,3);
    delay(1000);
    doNumber(60,5,2);
    delay(1000);
    doNumber(60,5,1);
    delay(1000);
    ssd1306_fillscreen(0x00);

    for (int i = 800; i>200; i = i - 200){
    beep(30,i);
    }

}



================================================
FILE: BatBonanzaAnalog/font6x8AJ.h
================================================
/*
 * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays
 *
 * @file: font6x8.h
 * @created: 2014-08-12
 * @author: Neven Boyanov
 * 
 * Hacked by andy jackson to allow two games (originally by webboggles.com) to 
 * fit onto an ATTiny85 at the same time - hence several characters are missing
 * and a couple have been moved to limit the amount of software remapping required
 * to map ASCII values onto locations in this array.
 *
 * Source code available at: https://bitbucket.org/tinusaur/ssd1306xled
 *
 */

// ----------------------------------------------------------------------------

#include <avr/pgmspace.h>

// ----------------------------------------------------------------------------

/* Standard ASCII 6x8 font */
static const uint8_t ssd1306xled_font6x8 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sp
/*
  0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, // !
  0x00, 0x00, 0x07, 0x00, 0x07, 0x00, // "
  0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14, // #
  0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // $
  0x00, 0x62, 0x64, 0x08, 0x13, 0x23, // %
  0x00, 0x36, 0x49, 0x55, 0x22, 0x50, // &
  0x00, 0x00, 0x05, 0x03, 0x00, 0x00, // '
  0x00, 0x00, 0x1c, 0x22, 0x41, 0x00, // (
  0x00, 0x00, 0x41, 0x22, 0x1c, 0x00, // )
  0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, // *
  0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // +
  0x00, 0x00, 0x00, 0xA0, 0x60, 0x00, // ,
  */
  0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // -
  0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // .
  0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w in place of /
  //0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // /
  0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0
  0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // 1
  0x00, 0x42, 0x61, 0x51, 0x49, 0x46, // 2
  0x00, 0x21, 0x41, 0x45, 0x4B, 0x31, // 3
  0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // 4
  0x00, 0x27, 0x45, 0x45, 0x45, 0x39, // 5
  0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6
  0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // 7
  0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // 8
  0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // 9
  0x00, 0x00, 0x36, 0x36, 0x00, 0x00, // :
/*
  0x00, 0x00, 0x56, 0x36, 0x00, 0x00, // ;
  0x00, 0x08, 0x14, 0x22, 0x41, 0x00, // <
  0x00, 0x14, 0x14, 0x14, 0x14, 0x14, // =
  0x00, 0x00, 0x41, 0x22, 0x14, 0x08, // >
  0x00, 0x02, 0x01, 0x51, 0x09, 0x06, // ?
  0x00, 0x32, 0x49, 0x59, 0x51, 0x3E, // @
 */
  0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, // A
  0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, // B
  0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, // C
  0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, // D
  0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, // E
  0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, // F
  0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, // G
  0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // H
  0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, // I
  0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, // J
  0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, // K
  0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, // L
  0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, // M
  0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, // N
  0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // O
  0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, // P
  0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q
  0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, // R
  0x00, 0x46, 0x49, 0x49, 0x49, 0x31, // S
  0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, // T
  0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, // U
  0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, // V
  0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, // W
  0x00, 0x63, 0x14, 0x08, 0x14, 0x63, // X
  0x00, 0x07, 0x08, 0x70, 0x08, 0x07, // Y
  0x00, 0x61, 0x51, 0x49, 0x45, 0x43, // Z
  /*
  0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // [
  0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55, // 55
  0x00, 0x00, 0x41, 0x41, 0x7F, 0x00, // ]
  0x00, 0x04, 0x02, 0x01, 0x02, 0x04, // ^
  0x00, 0x40, 0x40, 0x40, 0x40, 0x40, // _
  0x00, 0x00, 0x01, 0x02, 0x04, 0x00, // '
*/
  0x00, 0x20, 0x54, 0x54, 0x54, 0x78, // a
  0x00, 0x7F, 0x48, 0x44, 0x44, 0x38, // b
  0x00, 0x38, 0x44, 0x44, 0x44, 0x20, // c
  0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, // d
  0x00, 0x38, 0x54, 0x54, 0x54, 0x18, // e
  0x00, 0x08, 0x7E, 0x09, 0x01, 0x02, // f
  0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g
  0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C, // y (in place of h)
  //0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, // h
  0x00, 0x00, 0x44, 0x7D, 0x40, 0x00, // i
  0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, // j
  0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // k
  0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, // l
  0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, // m
  0x00, 0x7C, 0x08, 0x04, 0x04, 0x78, // n
  0x00, 0x38, 0x44, 0x44, 0x44, 0x38, // o
  0x00, 0xFC, 0x24, 0x24, 0x24, 0x18, // p
  0x00, 0x18, 0x24, 0x24, 0x18, 0xFC, // q
  0x00, 0x7C, 0x08, 0x04, 0x04, 0x08, // r
  0x00, 0x48, 0x54, 0x54, 0x54, 0x20, // s
  0x00, 0x04, 0x3F, 0x44, 0x40, 0x20, // t
  0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C, // u
  0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, // v
 // 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w
 // 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, // x
 // 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C // y
};

// ----------------------------------------------------------------------------



================================================
FILE: BatBonanzaAnalogSinglePot/BatBonanzaAnalogSinglePot.ino
================================================
/* 2015 / 2016 /2017
 * Pong clone by Andy Jackson - Twitter @andyhighnumber
 * Inspired by http://webboggles.com/ and includes some code from the #AttinyArcade games on that site
 * The code that does not fall under the licenses of sources listed below can be used non commercially with attribution.
 * 
 * This code takes its inputs from one paddles - same as my analog Space Invaders Clone
 * Tweet @andyhighnumber and I will direct you to the circuit.
 * 
 * There's one button in this design (on pin 5). When the game is running, pressing and releasing the button cycles through modes, with varying degrees of difficulty. 
 * 
 * Also, from standby....
 *  Press and hold the button to reset all settings (good to do when you first flash the chip, since the settings are loaded from EEPROM)
 * 
 * If you have problems uploading this sketch, this is probably due to sketch size - you need to update ld.exe in arduino\hardware\tools\avr\avr\bin
 * https://github.com/TCWORLD/ATTinyCore/tree/master/PCREL%20Patch%20for%20GCC
 *
 * This sketch is using the screen control and font functions written by Neven Boyanov for the http://tinusaur.wordpress.com/ project
 * Source code and font files available at: https://bitbucket.org/tinusaur/ssd1306xled
 * **Note that this highly size-optimised version requires modified library functions (which are in this source code file) 
 * and a modified font header
 * 
 * Sleep code is based on this blog post by Matthew Little:
 * http://www.re-innovation.co.uk/web12/index.php/en/blog-75/306-sleep-modes-on-attiny85
*/
#include <EEPROM.h>
#include "font6x8AJ.h"
#include <avr/sleep.h>
#include <avr/interrupt.h> // needed for the additional interrupt

#define DIGITAL_WRITE_HIGH(PORT) PORTB |= (1 << PORT)
#define DIGITAL_WRITE_LOW(PORT) PORTB &= ~(1 << PORT)

// Routines to set and clear bits (used in the sleep code)
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

// Defines for OLED output
#define SSD1306XLED_H
#define SSD1306_SCL   PORTB4  // SCL, Pin 4 on SSD1306 Board - for webbogles board
#define SSD1306_SDA   PORTB3  // SDA, Pin 3 on SSD1306 Board - for webbogles board
#define SSD1306_SA    0x78  // Slave address

#define WINSCORE 7

// Function prototypes
void startGame(void);
void drawPlatform(void);
void drawPlatform2(void);
void sendBlock(int);
void playPong(void);
void beep(int,int);
void drawBall(int x, int y);
void blankBall(int x, int y);

void doDrawLS(long, byte);
void doDrawRS(long, byte);
void doNumber (int x, int y, int value);

void ssd1306_init(void);
void ssd1306_xfer_start(void);
void ssd1306_xfer_stop(void);
void ssd1306_send_byte(uint8_t byte);
void ssd1306_send_command(uint8_t command);
void ssd1306_send_data_start(void);
void ssd1306_send_data_stop(void);
void ssd1306_setpos(uint8_t x, uint8_t y);
void ssd1306_fillscreen(uint8_t fill_Data);
void ssd1306_char_f6x8(uint8_t x, uint8_t y, const char ch[]);
void ssd1306_draw_bmp(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t bitmap[]);

int topScoreB = 0;
int player; //0 to 128-platformWidth  - this is the position of the player
int player2; //0 to 128-platformWidth  - this is the position of the player
int lastPlayer;
int lastPlayer2;
int platformWidth = 16; 
boolean stopAnimate = 0; // this is set to 1 when a collision is detected
boolean mute = 0;
int score = 0; // score - this affects the difficulty of the game
int score2 = 0; // score - this affects the difficulty of the game

int ballx = 62*8; // coordinate of the ball
int bally = 50*4; // coordinate of the ball
int vdir = -4; // vertical direction and step  distance
int hdir = -8; // horizontal direction and step distance

int mode = 2;

int perturbation = 0;
int pFactor = 12;

// Interrupt handlers
ISR(PCINT0_vect){ // PB0 pin button interrupt           
}

void playerIncPong(){ // PB2 pin button interrupt
}

// Arduino stuff - setup
void setup() {
  DDRB = 0b00000010;    // set PB1 as output (for the speaker)
  PCMSK = 0b00000001; // pin change mask: listen to portb bit 1
  GIMSK |= 0b00100000;  // enable PCINT interrupt 
  sei();          // enable all interrupts
}

// Arduino stuff - loop
void loop() { 
  ssd1306_init();
  ssd1306_fillscreen(0x00);
  
  // The lower case character set is seriously compromised because I've had to truncate the ASCII table
  // to release space for executable code - hence lower case y and w are remapped to h and / respectively.
  // There is no z in the table (or h!) as these aren't used anywhere in the text here and most of the 
  // symbols are also missing for the same reason (see my hacked version of font6x8.h - font6x8AJ.h for more detail)
  ssd1306_char_f6x8(0, 1, "   ---------------  ");
  ssd1306_char_f6x8(0, 2, "        B A T       ");
  ssd1306_char_f6x8(0, 4, "    B O N A N Z A   ");
  ssd1306_char_f6x8(0, 5, "   ---------------  ");
  ssd1306_char_f6x8(0, 7, "   bh andh jackson  "); // see comments above !

   long startT = millis();
    long nowT =0;
    boolean sChange = 0;
    while(digitalRead(0) == HIGH) {
      nowT = millis();
      if (nowT - startT > 2000) {
        sChange = 1;     
        EEPROM.write(0,0);
        EEPROM.write(1,1);
        ssd1306_char_f6x8(16, 0, "- SYSTEM RESET -");  
        break;
      }
      if (sChange == 1) break;
    }  
    while(digitalRead(0) == HIGH);

    mute=EEPROM.read(0);
    mode=EEPROM.read(1);

    if (mute != 0 && mute != 1) {
      mute = 0;
       EEPROM.write(0,0);
    }

    if (mode < 2 || mode > 5) {
      mode = 2;
      EEPROM.write(1,2);
    }

    if (sChange != 1) {
    delay(1500);
    ssd1306_init();
    ssd1306_fillscreen(0x00);
    stopAnimate = 0;
    score = 0;
    score2 = 0;

    playPong(); 
    
    delay(3500);
 
  }
  system_sleep();
}

void doNumber (int x, int y, int value) {
    char temp[10] = {0,0,0,0,0,0,0,0,0,0};
    itoa(value,temp,10);
    ssd1306_char_f6x8(x, y, temp);
}

void ssd1306_init(void){
  DDRB |= (1 << SSD1306_SDA); // Set port as output
  DDRB |= (1 << SSD1306_SCL); // Set port as output

  ssd1306_send_command(0xAE); // display off
  ssd1306_send_command(0x00); // Set Memory Addressing Mode
  ssd1306_send_command(0x10); // 00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
  ssd1306_send_command(0x40); // Set Page Start Address for Page Addressing Mode,0-7
  ssd1306_send_command(0x81); // Set COM Output Scan Direction
  ssd1306_send_command(0xCF); // ---set low column address
  ssd1306_send_command(0xA1); // ---set high column address
  ssd1306_send_command(0xC8); // --set start line address
  ssd1306_send_command(0xA6); // --set contrast control register
  ssd1306_send_command(0xA8);
  ssd1306_send_command(0x3F); // --set segment re-map 0 to 127
  ssd1306_send_command(0xD3); // --set normal display
  ssd1306_send_command(0x00); // --set multiplex ratio(1 to 64)
  ssd1306_send_command(0xD5); // 
  ssd1306_send_command(0x80); // 0xa4,Output follows RAM content;0xa5,Output ignores RAM content
  ssd1306_send_command(0xD9); // -set display offset
  ssd1306_send_command(0xF1); // -not offset
  ssd1306_send_command(0xDA); // --set display clock divide ratio/oscillator frequency
  ssd1306_send_command(0x12); // --set divide ratio
  ssd1306_send_command(0xDB); // --set pre-charge period
  ssd1306_send_command(0x40); // 
  ssd1306_send_command(0x20); // --set com pins hardware configuration
  ssd1306_send_command(0x02);
  ssd1306_send_command(0x8D); // --set vcomh
  ssd1306_send_command(0x14); // 0x20,0.77xVcc
  ssd1306_send_command(0xA4); // --set DC-DC enable
  ssd1306_send_command(0xA6); // 
  ssd1306_send_command(0xAF); // --turn on oled panel 
}

void ssd1306_xfer_start(void){
  DIGITAL_WRITE_HIGH(SSD1306_SCL);  // Set to HIGH
  DIGITAL_WRITE_HIGH(SSD1306_SDA);  // Set to HIGH
  DIGITAL_WRITE_LOW(SSD1306_SDA);   // Set to LOW
  DIGITAL_WRITE_LOW(SSD1306_SCL);   // Set to LOW
}

void ssd1306_xfer_stop(void){
  DIGITAL_WRITE_LOW(SSD1306_SCL);   // Set to LOW
  DIGITAL_WRITE_LOW(SSD1306_SDA);   // Set to LOW
  DIGITAL_WRITE_HIGH(SSD1306_SCL);  // Set to HIGH
  DIGITAL_WRITE_HIGH(SSD1306_SDA);  // Set to HIGH
}

void ssd1306_send_byte(uint8_t byte){
  uint8_t i;
  for(i=0; i<8; i++)
  {
    if((byte << i) & 0x80)
      DIGITAL_WRITE_HIGH(SSD1306_SDA);
    else
      DIGITAL_WRITE_LOW(SSD1306_SDA);
    
    DIGITAL_WRITE_HIGH(SSD1306_SCL);
    DIGITAL_WRITE_LOW(SSD1306_SCL);
  }
  DIGITAL_WRITE_HIGH(SSD1306_SDA);
  DIGITAL_WRITE_HIGH(SSD1306_SCL);
  DIGITAL_WRITE_LOW(SSD1306_SCL);
}

void ssd1306_send_command(uint8_t command){
  ssd1306_xfer_start();
  ssd1306_send_byte(SSD1306_SA);  // Slave address, SA0=0
  ssd1306_send_byte(0x00);  // write command
  ssd1306_send_byte(command);
  ssd1306_xfer_stop();
}

void ssd1306_send_data_start(void){
  ssd1306_xfer_start();
  ssd1306_send_byte(SSD1306_SA);
  ssd1306_send_byte(0x40);  //write data
}

void ssd1306_send_data_stop(void){
  ssd1306_xfer_stop();
}

void ssd1306_setpos(uint8_t x, uint8_t y)
{
  if (y>7) return;
  ssd1306_xfer_start();
  ssd1306_send_byte(SSD1306_SA);  //Slave address,SA0=0
  ssd1306_send_byte(0x00);  //write command

  ssd1306_send_byte(0xb0+y);
  ssd1306_send_byte(((x&0xf0)>>4)|0x10); // |0x10
  ssd1306_send_byte((x&0x0f)|0x01); // |0x01

  ssd1306_xfer_stop();
}

void ssd1306_fillscreen(uint8_t fill_Data){
  uint8_t m,n;
  for(m=0;m<8;m++)
  {
    ssd1306_send_command(0xb0+m); //page0-page1
    ssd1306_send_command(0x00);   //low column start address
    ssd1306_send_command(0x10);   //high column start address
    ssd1306_send_data_start();
    for(n=0;n<128;n++)
    {
      ssd1306_send_byte(fill_Data);
    }
    ssd1306_send_data_stop();
  }
}

void ssd1306_char_f6x8(uint8_t x, uint8_t y, const char ch[]){
  uint8_t c,i,j=0;
  while(ch[j] != '\0')
  {
    c = ch[j] - 32;
    if (c >0) c = c - 12;
    if (c >15) c = c - 6;
    if (c>40) c=c-6;
    if(x>126)
    {
      x=0;
      y++;
    }
    ssd1306_setpos(x,y);
    ssd1306_send_data_start();
    for(i=0;i<6;i++)
    {
      ssd1306_send_byte(pgm_read_byte(&ssd1306xled_font6x8[c*6+i]));
    }
    ssd1306_send_data_stop();
    x += 6;
    j++;
  }
}

void system_sleep() {
  ssd1306_fillscreen(0x00);
  ssd1306_send_command(0xAE);
  cbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter OFF
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  sleep_enable();
  sleep_mode();                        // System actually sleeps here
  sleep_disable();                     // System continues execution here when watchdog timed out 
  sbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter ON  
  ssd1306_send_command(0xAF);
}

void beep(int bCount,int bDelay){
  if (mute) return;
  for (int i = 0; i<=bCount; i++){digitalWrite(1,HIGH);for(int i2=0; i2<bDelay; i2++){__asm__("nop\n\t");}digitalWrite(1,LOW);for(int i2=0; i2<bDelay; i2++){__asm__("nop\n\t");}}
}



/* ------------------------
 *  Pong Code
 */
void playPong() {
  ballx = 64*8;
  bally = 32*4;
  hdir = -8;
  vdir = -4;
  int actualy, actualx;
  int factor = 0;
  int waitCount = 0;

  int lastx=64*8, lasty=32*4;

  player=64;
  player2=64;
  lastPlayer = 64;
  lastPlayer2 = 64;
  score = 0; // obvious
  score2 = 0; // obvious
  perturbation = 0;
  
  startGame();
  while (stopAnimate == 0) {
    while(1) {
    waitCount++;
    
    if(digitalRead(0)==1) {
      boolean sChange = 0;
      long startT = millis();
      long nowT =0;
      while(digitalRead(0) == HIGH) {
        nowT = millis();
        if (nowT - startT > 1500) {
          sChange = 1;
          if (mute == 0) { mute = 1; ssd1306_char_f6x8(32, 0, "-- MUTE --"); } else { mute = 0; ssd1306_char_f6x8(23, 0, "-- SOUND ON --"); }
          break;
        }
      }
      while(digitalRead(0) == HIGH);
      if (sChange == 1) {
      } else if (mode == 2) { 
          mode = 3; 
          pFactor = 11;
          ssd1306_char_f6x8(26, 0, "-- TOUGH  --");  
        } else if (mode == 3) { 
          mode = 4; 
          pFactor = 10;
          ssd1306_char_f6x8(26, 0, "-- EXPERT --");  
        } else if (mode == 4) { 
          mode = 2; 
          ssd1306_char_f6x8(26, 0, "-- NORMAL --");  
        }
      
      if (sChange == 0) delay(1000);
      ssd1306_fillscreen(0x00);        
      EEPROM.write(0,mute);
      EEPROM.write(1,mode);
    }

    player = (analogRead(1) / 16);
        if (player > 48) player = 48;
    if (player <0) player = 0;


    if(waitCount >= 3) {
      waitCount = 0;
      perturbation = perturbation - 2 + random(0,5);
      if (perturbation > pFactor) perturbation = pFactor - 2;
      if (perturbation < pFactor*-1) perturbation = (pFactor*-1)+2;
    }
    player2 = (bally/4 -8)+perturbation;

    if (player2 > 48) player2 = 48;
    if (player2 <0) player2 = 0;

    actualy = floor(bally/4);
    actualx = floor(ballx/8);
    
    // bounce off the sides of the screen
    if ((actualy+vdir<63&&vdir>01) || (actualy- vdir>6&&vdir<0)){
      bally+=vdir;
    }else {
        vdir = vdir*-1;
    }
    ballx+=hdir;

    actualy = floor(bally/4);
    actualx = floor(ballx/8);
    
    // check it hits the left pad and deal with bounces and misses
    if (actualx <= 4) {
      if(actualy<player-1||actualy>player+platformWidth+1){ 
        score2++;
    
        ballx = 5*8;
        bally = player*4;

        hdir = 13;
        if (vdir > 0) {
          vdir = 2;
        } else vdir = -2;
        
        ssd1306_fillscreen(0x00);        
        doNumber(46,4,score);
        doNumber(78,4,score2);        
        if (score2 < WINSCORE) {
          for (int i = 0; i<1000; i = i+ 100){
            beep(50,i);
          }
          for (int incr=0;incr<3;incr++) {
              ssd1306_send_data_stop();
              ssd1306_setpos(78,4);
              ssd1306_send_data_start();
              sendBlock(0);
              sendBlock(0);
              ssd1306_send_data_stop();
              delay(350);
              doNumber(78,4,score2);
              delay(350);
            }
            startGame();
        }
        perturbation = 0;
        break;
      }else if (actualy<player+1){        
        vdir = -6;
        hdir = 7;
      }else if (actualy<player+4){        
        vdir = -4;
        hdir = 10;
      }else if (actualy<player+7){        
        vdir = -2;
        hdir = 13;
      }else if (actualy<player+9){        
        vdir = 0;
        hdir = 14;
      }else if (actualy<player+12){        
        vdir = 2;
        hdir = 13;
      }else if (actualy<player+15){        
        vdir = 4;
        hdir = 10;
      }else {   
        vdir = 6;
        hdir = 7;
      }
      beep(20,600);
    }  
    
    // check it hits the right pad and deal with bounces
    if(actualx >= 122) {
      if(actualy<player2-1||actualy>player2+platformWidth+1){
        score++;
  
        ballx = 120*8;
        bally = player2*4;

        hdir = -13;
        if (vdir > 0) {
          vdir = 2;
        } else vdir = -2;

        ssd1306_fillscreen(0x00);        
        doNumber(46,4,score);
        doNumber(78,4,score2);
        if (score < WINSCORE) {
        for (int i = 0; i<1000; i = i+ 100){
            beep(50,i);
          }                
  
          for (int incr=0;incr<3;incr++) {
              ssd1306_setpos(46,4);
              ssd1306_send_data_start();
              sendBlock(0);
              sendBlock(0);
              ssd1306_send_data_stop();
              delay(350);
              doNumber(46,4,score);
              delay(350);
            }
            perturbation = 0;
            startGame();
        }        
        break;
      }else if (actualy<player2+1){        
        vdir = -6;
        hdir = -7;
      }else if (actualy<player2+4){        
        vdir = -4;
        hdir = -10;
      }else if (actualy<player2+7){        
        vdir = -2;
        hdir = -13;
      }else if (actualy<player2+9){        
        vdir = 0;
        hdir = -14;
      }else if (actualy<player2+12){        
        vdir = 2;
        hdir = -13;
      }else if (actualy<player2+15){        
        vdir = 4;
        hdir = -10;
      }else {   
        vdir = 6;
        hdir = -7;
      }
      beep(20,300);
    }      

    
    if (mode == 1 || mode == 3 || mode == 4) {
      factor = 8-floor((score-score2)/2); // expert modes
      if (factor < 2) factor = 2;
    } else {
      factor = 20-floor((score-score2)/2); // normal modes
      if (factor < 10) factor = 10;
    }
    
    delay(factor);
    
    // draw ball
    blankBall(floor(lastx/8),floor(lasty/4));  
    drawPlatform();
    drawPlatform2();
    drawBall(floor(ballx/8),floor(bally/4));
    lastx = ballx;
    lasty = bally;
    
    doNumber(28,0,score);
    doNumber(92,0,score2);
    if (score == WINSCORE || score2 == WINSCORE) {
      stopAnimate = 1;
      break;
    }
    }
 }

blankBall(floor(lastx/8),floor(lasty/4));  
blankBall(floor(ballx/8),floor(bally/4));  

if (score > score2) {
  ssd1306_char_f6x8(27, 3, "P L A Y E R 1"); 
} else {
  ssd1306_char_f6x8(27, 3, "P L A Y E R 2"); 
}
ssd1306_char_f6x8(27, 4, "             ");
ssd1306_char_f6x8(27, 5, "   W I N S   ");

for (int i = 0; i<1000; i = i+ 50){
  beep(50,i);
}


for (int incr=0;incr<6;incr++) {
    ssd1306_setpos(28,0);
    ssd1306_send_data_start();
    sendBlock(0);
    sendBlock(0);
    ssd1306_send_data_stop();
    ssd1306_setpos(92,0);
    ssd1306_send_data_start();
    sendBlock(0);
    sendBlock(0);
    ssd1306_send_data_stop();
    delay(350);
    doNumber(28,0,score);
    doNumber(92,0,score2);
    delay(350);
  }
}

void drawPlatform() {
  if (player != lastPlayer) {
    ssd1306_setpos(0,lastPlayer/8);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop();
    ssd1306_setpos(0,lastPlayer/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop(); 
    ssd1306_setpos(0,lastPlayer/8+2);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop(); 
  }  

  if (player%8!=0){
    ssd1306_setpos(0,player/8);
    ssd1306_send_data_start();
    ssd1306_send_byte((B11111111)<<player%8);
    ssd1306_send_data_stop();
    ssd1306_setpos(0,player/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B11111111);
    ssd1306_send_data_stop();    
    ssd1306_setpos(0,player/8+2);
    ssd1306_send_data_start();
    ssd1306_send_byte((B01111110)>>8-player%8);
    ssd1306_send_data_stop();
  } else {
    ssd1306_setpos(0,player/8);
    ssd1306_send_data_start();
    ssd1306_send_byte(B11111111);
    ssd1306_send_data_stop();
    ssd1306_setpos(0,player/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B11111111);
    ssd1306_send_data_stop();

  }
  lastPlayer = player;
}

void drawPlatform2() {

  if (player2 != lastPlayer2) {
    ssd1306_setpos(127,lastPlayer2/8);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop();
    ssd1306_setpos(127,lastPlayer2/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop(); 
    ssd1306_setpos(127,lastPlayer2/8+2);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop(); 
  }
  
  if (player2%8!=0){
    ssd1306_setpos(127,player2/8);
    ssd1306_send_data_start();
    ssd1306_send_byte((B11111111)<<player2%8);
    ssd1306_send_data_stop();
    ssd1306_setpos(127,player2/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B11111111);
    ssd1306_send_data_stop();        
    ssd1306_setpos(127,player2/8+2);
    ssd1306_send_data_start();
    ssd1306_send_byte((B01111110)>>8-player2%8);
    ssd1306_send_data_stop();
  } else {
    ssd1306_setpos(127,player2/8);
    ssd1306_send_data_start();
    ssd1306_send_byte((B11111111)<<0);
    ssd1306_send_data_stop();
    ssd1306_setpos(127,player2/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte((B11111111)<<0);
    ssd1306_send_data_stop();
  }
  lastPlayer2 = player2;
}



void sendBlock(int fill){
  if (fill == 1) {
   ssd1306_send_byte(B10011000);
   ssd1306_send_byte(B01011100);
   ssd1306_send_byte(B10110110);
   ssd1306_send_byte(B01011111);
   ssd1306_send_byte(B01011111);
   ssd1306_send_byte(B10110110);
   ssd1306_send_byte(B01011100);
   ssd1306_send_byte(B10011000);
  } else  {
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
   ssd1306_send_byte(B00000000);
  } 
}

void blankBall(int x, int y) {
  if (y%8!=0){
    ssd1306_setpos(x,y/8);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop();
    
    ssd1306_setpos(x,y/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop();
  } else {
    ssd1306_setpos(x,y/8);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop();
  }
}


void drawBall(int x, int y) {
  if (y%8!=0){
    ssd1306_setpos(x,y/8);
    ssd1306_send_data_start();
    doDrawLS(0,y%8);
    ssd1306_send_data_stop();
    
    ssd1306_setpos(x,y/8+1);
    ssd1306_send_data_start();
    doDrawRS(0,8-y%8);
    ssd1306_send_data_stop();
  } else {
    ssd1306_setpos(x,y/8);
    ssd1306_send_data_start();
    doDrawLS(0,0);
    ssd1306_send_data_stop();
  }
}

// Drawing routine for the player and fire - with right-shifts
void doDrawRS(long P1, byte P2) {
  ssd1306_send_byte((B00000011 | P1)>>P2);
  ssd1306_send_byte((B00000011 | P1)>>P2);
}

// Drawing routine for the player and fire - with left-shifts
void doDrawLS(long P1, byte P2) {
  ssd1306_send_byte((B00000011 | P1)<<P2);
  ssd1306_send_byte((B00000011 | P1)<<P2);
}

void startGame(void) {
  
    ssd1306_fillscreen(0x00);

    ssd1306_char_f6x8(16, 3, "-- GET READY --");
    doNumber(60,5,3);
    delay(1000);
    doNumber(60,5,2);
    delay(1000);
    doNumber(60,5,1);
    delay(1000);
    ssd1306_fillscreen(0x00);

    for (int i = 800; i>200; i = i - 200){
    beep(30,i);
    }

}



================================================
FILE: BatBonanzaAnalogSinglePot/font6x8AJ.h
================================================
/*
 * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays
 *
 * @file: font6x8.h
 * @created: 2014-08-12
 * @author: Neven Boyanov
 * 
 * Hacked by andy jackson to allow two games (originally by webboggles.com) to 
 * fit onto an ATTiny85 at the same time - hence several characters are missing
 * and a couple have been moved to limit the amount of software remapping required
 * to map ASCII values onto locations in this array.
 *
 * Source code available at: https://bitbucket.org/tinusaur/ssd1306xled
 *
 */

// ----------------------------------------------------------------------------

#include <avr/pgmspace.h>

// ----------------------------------------------------------------------------

/* Standard ASCII 6x8 font */
static const uint8_t ssd1306xled_font6x8 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sp
/*
  0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, // !
  0x00, 0x00, 0x07, 0x00, 0x07, 0x00, // "
  0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14, // #
  0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // $
  0x00, 0x62, 0x64, 0x08, 0x13, 0x23, // %
  0x00, 0x36, 0x49, 0x55, 0x22, 0x50, // &
  0x00, 0x00, 0x05, 0x03, 0x00, 0x00, // '
  0x00, 0x00, 0x1c, 0x22, 0x41, 0x00, // (
  0x00, 0x00, 0x41, 0x22, 0x1c, 0x00, // )
  0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, // *
  0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // +
  0x00, 0x00, 0x00, 0xA0, 0x60, 0x00, // ,
  */
  0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // -
  0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // .
  0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w in place of /
  //0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // /
  0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0
  0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // 1
  0x00, 0x42, 0x61, 0x51, 0x49, 0x46, // 2
  0x00, 0x21, 0x41, 0x45, 0x4B, 0x31, // 3
  0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // 4
  0x00, 0x27, 0x45, 0x45, 0x45, 0x39, // 5
  0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6
  0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // 7
  0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // 8
  0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // 9
  0x00, 0x00, 0x36, 0x36, 0x00, 0x00, // :
/*
  0x00, 0x00, 0x56, 0x36, 0x00, 0x00, // ;
  0x00, 0x08, 0x14, 0x22, 0x41, 0x00, // <
  0x00, 0x14, 0x14, 0x14, 0x14, 0x14, // =
  0x00, 0x00, 0x41, 0x22, 0x14, 0x08, // >
  0x00, 0x02, 0x01, 0x51, 0x09, 0x06, // ?
  0x00, 0x32, 0x49, 0x59, 0x51, 0x3E, // @
 */
  0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, // A
  0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, // B
  0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, // C
  0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, // D
  0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, // E
  0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, // F
  0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, // G
  0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // H
  0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, // I
  0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, // J
  0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, // K
  0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, // L
  0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, // M
  0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, // N
  0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // O
  0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, // P
  0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q
  0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, // R
  0x00, 0x46, 0x49, 0x49, 0x49, 0x31, // S
  0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, // T
  0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, // U
  0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, // V
  0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, // W
  0x00, 0x63, 0x14, 0x08, 0x14, 0x63, // X
  0x00, 0x07, 0x08, 0x70, 0x08, 0x07, // Y
  0x00, 0x61, 0x51, 0x49, 0x45, 0x43, // Z
  /*
  0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // [
  0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55, // 55
  0x00, 0x00, 0x41, 0x41, 0x7F, 0x00, // ]
  0x00, 0x04, 0x02, 0x01, 0x02, 0x04, // ^
  0x00, 0x40, 0x40, 0x40, 0x40, 0x40, // _
  0x00, 0x00, 0x01, 0x02, 0x04, 0x00, // '
*/
  0x00, 0x20, 0x54, 0x54, 0x54, 0x78, // a
  0x00, 0x7F, 0x48, 0x44, 0x44, 0x38, // b
  0x00, 0x38, 0x44, 0x44, 0x44, 0x20, // c
  0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, // d
  0x00, 0x38, 0x54, 0x54, 0x54, 0x18, // e
  0x00, 0x08, 0x7E, 0x09, 0x01, 0x02, // f
  0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g
  0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C, // y (in place of h)
  //0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, // h
  0x00, 0x00, 0x44, 0x7D, 0x40, 0x00, // i
  0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, // j
  0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // k
  0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, // l
  0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, // m
  0x00, 0x7C, 0x08, 0x04, 0x04, 0x78, // n
  0x00, 0x38, 0x44, 0x44, 0x44, 0x38, // o
  0x00, 0xFC, 0x24, 0x24, 0x24, 0x18, // p
  0x00, 0x18, 0x24, 0x24, 0x18, 0xFC, // q
  0x00, 0x7C, 0x08, 0x04, 0x04, 0x08, // r
  0x00, 0x48, 0x54, 0x54, 0x54, 0x20, // s
  0x00, 0x04, 0x3F, 0x44, 0x40, 0x20, // t
  0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C, // u
  0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, // v
 // 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w
 // 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, // x
 // 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C // y
};

// ----------------------------------------------------------------------------



================================================
FILE: BatBonanzaAttinyArcade/BatBonanzaAttinyArcade.ino
================================================
/* 2015 / 2016 /2017
 * Pong game by Andy Jackson - Twitter @andyhighnumber
 * Inspired by http://webboggles.com/ and includes some code from the #AttinyArcade games on that site
 * The code that does not fall under the licenses of sources listed below can be used non commercially with attribution.
 * 
 * When the game is running :
 * LEFT BUTTON - Controls the player's bat
 * RIGHT BUTTON - Press and relese to change mode (difficulty) - Press and hold to toggle sound
 *  
 * Also, from standby....
 *  Press and hold left button to reset the system
 * 
 * If you have problems uploading this sketch, this is probably due to sketch size - you need to update ld.exe in arduino\hardware\tools\avr\avr\bin
 * https://github.com/TCWORLD/ATTinyCore/tree/master/PCREL%20Patch%20for%20GCC
 *
 * This sketch is using the screen control and font functions written by Neven Boyanov for the http://tinusaur.wordpress.com/ project
 * Source code and font files available at: https://bitbucket.org/tinusaur/ssd1306xled
 * **Note that this highly size-optimised version requires modified library functions (which are in this source code file) 
 * and a modified font header
 * 
 * Sleep code is based on this blog post by Matthew Little:
 * http://www.re-innovation.co.uk/web12/index.php/en/blog-75/306-sleep-modes-on-attiny85
*/
#include <EEPROM.h>
#include "font6x8AJ.h"
#include <avr/sleep.h>
#include <avr/interrupt.h> // needed for the additional interrupt

#define DIGITAL_WRITE_HIGH(PORT) PORTB |= (1 << PORT)
#define DIGITAL_WRITE_LOW(PORT) PORTB &= ~(1 << PORT)

// Routines to set and clear bits (used in the sleep code)
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

// Defines for OLED output
#define SSD1306XLED_H
#define SSD1306_SCL   PORTB4  // SCL, Pin 4 on SSD1306 Board - for webbogles board
#define SSD1306_SDA   PORTB3  // SDA, Pin 3 on SSD1306 Board - for webbogles board
#define SSD1306_SA    0x78  // Slave address

#define WINSCORE 7

// Function prototypes
void startGame(void);
void drawPlatform(void);
void drawPlatform2(void);
void sendBlock(int);
void playPong(void);
void beep(int,int);
void drawBall(int x, int y);
void blankBall(int x, int y);

void doDrawLS(long, byte);
void doDrawRS(long, byte);
void doNumber (int x, int y, int value);

void ssd1306_init(void);
void ssd1306_xfer_start(void);
void ssd1306_xfer_stop(void);
void ssd1306_send_byte(uint8_t byte);
void ssd1306_send_command(uint8_t command);
void ssd1306_send_data_start(void);
void ssd1306_send_data_stop(void);
void ssd1306_setpos(uint8_t x, uint8_t y);
void ssd1306_fillscreen(uint8_t fill_Data);
void ssd1306_char_f6x8(uint8_t x, uint8_t y, const char ch[]);
void ssd1306_draw_bmp(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t bitmap[]);

int player; //0 to 128-platformWidth  - this is the position of the player
int player2; //0 to 128-platformWidth  - this is the position of the player
int lastPlayer;
int lastPlayer2;
int platformWidth = 16; 
boolean stopAnimate = 0; // this is set to 1 when a collision is detected
boolean mute = 0;
boolean newHigh = 0;
int score = 0; // score - this affects the difficulty of the game
int score2 = 0; // score - this affects the difficulty of the game

int ballx = 62*8; // coordinate of the ball
int bally = 50*4; // coordinate of the ball
int vdir = -4; // vertical direction and step  distance
int hdir = -8; // horizontal direction and step distance

int mode = 0;

int perturbation = 0;
int pFactor = 12;

// Interrupt handlers
ISR(PCINT0_vect){ // PB0 pin button interrupt           
}

void playerIncPong(){ // PB2 pin button interrupt
}

// Arduino stuff - setup
void setup() {
  DDRB = 0b00000010;    // set PB1 as output (for the speaker)
  PCMSK = 0b00000001; // pin change mask: listen to portb bit 1
  GIMSK |= 0b00100000;  // enable PCINT interrupt 
  sei();          // enable all interrupts
}

// Arduino stuff - loop
void loop() { 
  ssd1306_init();
  ssd1306_fillscreen(0x00);
  
  // The lower case character set is seriously compromised because I've had to truncate the ASCII table
  // to release space for executable code - hence lower case y and w are remapped to h and / respectively.
  // There is no z in the table (or h!) as these aren't used anywhere in the text here and most of the 
  // symbols are also missing for the same reason (see my hacked version of font6x8.h - font6x8AJ.h for more detail)
  ssd1306_char_f6x8(0, 1, "   ---------------  ");
  ssd1306_char_f6x8(0, 2, "        B A T       ");
  ssd1306_char_f6x8(0, 4, "    B O N A N Z A   ");
  ssd1306_char_f6x8(0, 5, "   ---------------  ");
  ssd1306_char_f6x8(0, 7, "   bh andh jackson  "); // see comments above !

   long startT = millis();
    long nowT =0;
    boolean sChange = 0;
    while(digitalRead(0) == HIGH) {
      nowT = millis();
      if (nowT - startT > 2000) {
        sChange = 1;     
        EEPROM.write(0,0);
        EEPROM.write(1,1);
        ssd1306_char_f6x8(16, 0, "- SYSTEM RESET -");  
        break;
      }
      if (sChange == 1) break;
    }  
    while(digitalRead(0) == HIGH);

    mute=EEPROM.read(0);
    mode=EEPROM.read(1);

    if (mute != 0 && mute != 1) {
      mute = 0;
       EEPROM.write(0,0);
    }

    if (mode != 1 && mode != 3 && mode != 4) {
      mode = 1;
      EEPROM.write(1,1);
    }

    
    if (sChange != 1) {
    delay(1500);
    ssd1306_init();
    ssd1306_fillscreen(0x00);
    stopAnimate = 0;
    score = 0;
    score2 = 0;

    playPong(); 
    
    delay(3500);
 
  }
  system_sleep();
}

void doNumber (int x, int y, int value) {
    char temp[10] = {0,0,0,0,0,0,0,0,0,0};
    itoa(value,temp,10);
    ssd1306_char_f6x8(x, y, temp);
}

void ssd1306_init(void){
  DDRB |= (1 << SSD1306_SDA); // Set port as output
  DDRB |= (1 << SSD1306_SCL); // Set port as output

  ssd1306_send_command(0xAE); // display off
  ssd1306_send_command(0x00); // Set Memory Addressing Mode
  ssd1306_send_command(0x10); // 00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
  ssd1306_send_command(0x40); // Set Page Start Address for Page Addressing Mode,0-7
  ssd1306_send_command(0x81); // Set COM Output Scan Direction
  ssd1306_send_command(0xCF); // ---set low column address
  ssd1306_send_command(0xA1); // ---set high column address
  ssd1306_send_command(0xC8); // --set start line address
  ssd1306_send_command(0xA6); // --set contrast control register
  ssd1306_send_command(0xA8);
  ssd1306_send_command(0x3F); // --set segment re-map 0 to 127
  ssd1306_send_command(0xD3); // --set normal display
  ssd1306_send_command(0x00); // --set multiplex ratio(1 to 64)
  ssd1306_send_command(0xD5); // 
  ssd1306_send_command(0x80); // 0xa4,Output follows RAM content;0xa5,Output ignores RAM content
  ssd1306_send_command(0xD9); // -set display offset
  ssd1306_send_command(0xF1); // -not offset
  ssd1306_send_command(0xDA); // --set display clock divide ratio/oscillator frequency
  ssd1306_send_command(0x12); // --set divide ratio
  ssd1306_send_command(0xDB); // --set pre-charge period
  ssd1306_send_command(0x40); // 
  ssd1306_send_command(0x20); // --set com pins hardware configuration
  ssd1306_send_command(0x02);
  ssd1306_send_command(0x8D); // --set vcomh
  ssd1306_send_command(0x14); // 0x20,0.77xVcc
  ssd1306_send_command(0xA4); // --set DC-DC enable
  ssd1306_send_command(0xA6); // 
  ssd1306_send_command(0xAF); // --turn on oled panel 
}

void ssd1306_xfer_start(void){
  DIGITAL_WRITE_HIGH(SSD1306_SCL);  // Set to HIGH
  DIGITAL_WRITE_HIGH(SSD1306_SDA);  // Set to HIGH
  DIGITAL_WRITE_LOW(SSD1306_SDA);   // Set to LOW
  DIGITAL_WRITE_LOW(SSD1306_SCL);   // Set to LOW
}

void ssd1306_xfer_stop(void){
  DIGITAL_WRITE_LOW(SSD1306_SCL);   // Set to LOW
  DIGITAL_WRITE_LOW(SSD1306_SDA);   // Set to LOW
  DIGITAL_WRITE_HIGH(SSD1306_SCL);  // Set to HIGH
  DIGITAL_WRITE_HIGH(SSD1306_SDA);  // Set to HIGH
}

void ssd1306_send_byte(uint8_t byte){
  uint8_t i;
  for(i=0; i<8; i++)
  {
    if((byte << i) & 0x80)
      DIGITAL_WRITE_HIGH(SSD1306_SDA);
    else
      DIGITAL_WRITE_LOW(SSD1306_SDA);
    
    DIGITAL_WRITE_HIGH(SSD1306_SCL);
    DIGITAL_WRITE_LOW(SSD1306_SCL);
  }
  DIGITAL_WRITE_HIGH(SSD1306_SDA);
  DIGITAL_WRITE_HIGH(SSD1306_SCL);
  DIGITAL_WRITE_LOW(SSD1306_SCL);
}

void ssd1306_send_command(uint8_t command){
  ssd1306_xfer_start();
  ssd1306_send_byte(SSD1306_SA);  // Slave address, SA0=0
  ssd1306_send_byte(0x00);  // write command
  ssd1306_send_byte(command);
  ssd1306_xfer_stop();
}

void ssd1306_send_data_start(void){
  ssd1306_xfer_start();
  ssd1306_send_byte(SSD1306_SA);
  ssd1306_send_byte(0x40);  //write data
}

void ssd1306_send_data_stop(void){
  ssd1306_xfer_stop();
}

void ssd1306_setpos(uint8_t x, uint8_t y)
{
  if (y>7) return;
  ssd1306_xfer_start();
  ssd1306_send_byte(SSD1306_SA);  //Slave address,SA0=0
  ssd1306_send_byte(0x00);  //write command

  ssd1306_send_byte(0xb0+y);
  ssd1306_send_byte(((x&0xf0)>>4)|0x10); // |0x10
  ssd1306_send_byte((x&0x0f)|0x01); // |0x01

  ssd1306_xfer_stop();
}

void ssd1306_fillscreen(uint8_t fill_Data){
  uint8_t m,n;
  for(m=0;m<8;m++)
  {
    ssd1306_send_command(0xb0+m); //page0-page1
    ssd1306_send_command(0x00);   //low column start address
    ssd1306_send_command(0x10);   //high column start address
    ssd1306_send_data_start();
    for(n=0;n<128;n++)
    {
      ssd1306_send_byte(fill_Data);
    }
    ssd1306_send_data_stop();
  }
}

void ssd1306_char_f6x8(uint8_t x, uint8_t y, const char ch[]){
  uint8_t c,i,j=0;
  while(ch[j] != '\0')
  {
    c = ch[j] - 32;
    if (c >0) c = c - 12;
    if (c >15) c = c - 6;
    if (c>40) c=c-6;
    if(x>126)
    {
      x=0;
      y++;
    }
    ssd1306_setpos(x,y);
    ssd1306_send_data_start();
    for(i=0;i<6;i++)
    {
      ssd1306_send_byte(pgm_read_byte(&ssd1306xled_font6x8[c*6+i]));
    }
    ssd1306_send_data_stop();
    x += 6;
    j++;
  }
}

void system_sleep() {
  ssd1306_fillscreen(0x00);
  ssd1306_send_command(0xAE);
  cbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter OFF
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  sleep_enable();
  sleep_mode();                        // System actually sleeps here
  sleep_disable();                     // System continues execution here when watchdog timed out 
  sbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter ON  
  ssd1306_send_command(0xAF);
}

void beep(int bCount,int bDelay){
  if (mute) return;
  for (int i = 0; i<=bCount; i++){digitalWrite(1,HIGH);for(int i2=0; i2<bDelay; i2++){__asm__("nop\n\t");}digitalWrite(1,LOW);for(int i2=0; i2<bDelay; i2++){__asm__("nop\n\t");}}
}



/* ------------------------
 *  Pong Code
 */
void playPong() {
  ballx = 64*8;
  bally = 32*4;
  hdir = -8;
  vdir = -4;
  int actualy, actualx;
  int factor = 0;
  int waitCount = 0;

  int lastx=64*8, lasty=32*4;

  player=64;
  player2=64;
  lastPlayer = 64;
  lastPlayer2 = 64;
  score = 0; // obvious
  score2 = 0; // obvious
  perturbation = 0;
    
  startGame();
  while (stopAnimate == 0) {
    while(1) {
    waitCount++;
    
    if(digitalRead(2)==1) {
      boolean sChange = 0;
      long startT = millis();
      long nowT =0;
      while(digitalRead(2) == HIGH) {
        nowT = millis();
        if (nowT - startT > 1500) {
          sChange = 1;
          if (mute == 0) { mute = 1; ssd1306_char_f6x8(32, 0, "-- MUTE --"); } else { mute = 0; ssd1306_char_f6x8(23, 0, "-- SOUND ON --"); }
          break;
        }
      }
      while(digitalRead(2) == HIGH);
      if (sChange == 1) {
          } else if (mode == 1) { 
            mode = 3; 
            pFactor = 11;
            ssd1306_char_f6x8(20, 0, "- TOUGH MODE -");  
          } else if (mode == 3) { 
            mode = 4; 
            pFactor = 10;
            ssd1306_char_f6x8(16, 0, "- EXPERT MODE -");  
          } else if (mode == 4) { 
            mode = 1; 
            pFactor = 12;
            ssd1306_char_f6x8(32, 0, "-- NORMAL --"); 
          }
      
      if (sChange == 0) delay(1000);
      ssd1306_fillscreen(0x00);        
      EEPROM.write(0,mute);
      EEPROM.write(1,mode);
    }

    if (digitalRead(0) == 1) {
      player-= 3;
    }
    player+=1;
    if (player > 48) player = 48;
    if (player <0) player = 0;
    
    if (mode == 1 || mode == 3 || mode == 4) {
      if(waitCount >= 3) {
        waitCount = 0;
        perturbation = perturbation - 2 + random(0,5);
        if (perturbation > pFactor) perturbation = pFactor - 2;
        if (perturbation < pFactor*-1) perturbation = (pFactor*-1)+2;
      }
      player2 = (bally/4 -8)+perturbation;
    }
    if (player2 > 48) player2 = 48;
    if (player2 <0) player2 = 0;

    actualy = floor(bally/4);
    actualx = floor(ballx/8);
    
    // bounce off the sides of the screen
    if ((actualy+vdir<63&&vdir>01) || (actualy- vdir>6&&vdir<0)){
      bally+=vdir;
    }else {
        vdir = vdir*-1;
    }
    ballx+=hdir;

    actualy = floor(bally/4);
    actualx = floor(ballx/8);
    
    // check it hits the left pad and deal with bounces and misses
    if (actualx <= 4) {
      if(actualy<player-1||actualy>player+platformWidth+1){ 
        score2++;
    
        ballx = 5*8;
        bally = player*4;

        hdir = 13;
        if (vdir > 0) {
          vdir = 2;
        } else vdir = -2;
        
        ssd1306_fillscreen(0x00);        
        doNumber(46,4,score);
        doNumber(78,4,score2);        
        if (score2 < WINSCORE) {
          for (int i = 0; i<1000; i = i+ 100){
            beep(50,i);
          }
          for (int incr=0;incr<3;incr++) {
              ssd1306_send_data_stop();
              ssd1306_setpos(78,4);
              ssd1306_send_data_start();
              sendBlock(0);
              sendBlock(0);
              ssd1306_send_data_stop();
              delay(350);
              doNumber(78,4,score2);
              delay(350);
            }
            startGame();
        }
        perturbation = 0;
        break;
      }else if (actualy<player+1){        
        vdir = -6;
        hdir = 7;
      }else if (actualy<player+4){        
        vdir = -4;
        hdir = 10;
      }else if (actualy<player+7){        
        vdir = -2;
        hdir = 13;
      }else if (actualy<player+9){        
        vdir = 0;
        hdir = 14;
      }else if (actualy<player+12){        
        vdir = 2;
        hdir = 13;
      }else if (actualy<player+15){        
        vdir = 4;
        hdir = 10;
      }else {   
        vdir = 6;
        hdir = 7;
      }
      beep(20,600);
    }  
    
    // check it hits the right pad and deal with bounces
    if(actualx >= 122) {
      if(actualy<player2-1||actualy>player2+platformWidth+1){
        score++;
  
        ballx = 120*8;
        bally = player2*4;

        hdir = -13;
        if (vdir > 0) {
          vdir = 2;
        } else vdir = -2;

        ssd1306_fillscreen(0x00);        
        doNumber(46,4,score);
        doNumber(78,4,score2);
        if (score < WINSCORE) {
        for (int i = 0; i<1000; i = i+ 100){
            beep(50,i);
          }                
  
          for (int incr=0;incr<3;incr++) {
              ssd1306_setpos(46,4);
              ssd1306_send_data_start();
              sendBlock(0);
              sendBlock(0);
              ssd1306_send_data_stop();
              delay(350);
              doNumber(46,4,score);
              delay(350);
            }
            perturbation = 0;
            startGame();
        }        
        break;
      }else if (actualy<player2+1){        
        vdir = -6;
        hdir = -7;
      }else if (actualy<player2+4){        
        vdir = -4;
        hdir = -10;
      }else if (actualy<player2+7){        
        vdir = -2;
        hdir = -13;
      }else if (actualy<player2+9){        
        vdir = 0;
        hdir = -14;
      }else if (actualy<player2+12){        
        vdir = 2;
        hdir = -13;
      }else if (actualy<player2+15){        
        vdir = 4;
        hdir = -10;
      }else {   
        vdir = 6;
        hdir = -7;
      }
      beep(20,300);
    }      

    
    if (mode == 2 || mode == 3 || mode == 4) {
      factor = 8-floor((score-score2)/2); // expert modes
      if (factor < 2) factor = 2;
    } else {
      factor = 20-floor((score-score2)/2); // normal modes
      if (factor < 10) factor = 10;
    }
    
    delay(factor);
    
    // draw ball
    blankBall(floor(lastx/8),floor(lasty/4));  
    drawPlatform();
    drawPlatform2();
    drawBall(floor(ballx/8),floor(bally/4));
    lastx = ballx;
    lasty = bally;
    
    doNumber(28,0,score);
    doNumber(92,0,score2);
    if (score == WINSCORE || score2 == WINSCORE) {
      stopAnimate = 1;
      break;
    }
    }
 }

blankBall(floor(lastx/8),floor(lasty/4));  
blankBall(floor(ballx/8),floor(bally/4));  

if (score > score2) {
  ssd1306_char_f6x8(27, 3, "P L A Y E R 1"); 
} else {
  ssd1306_char_f6x8(27, 3, "P L A Y E R 2"); 
}
ssd1306_char_f6x8(27, 4, "             ");
ssd1306_char_f6x8(27, 5, "   W I N S   ");

for (int i = 0; i<1000; i = i+ 50){
  beep(50,i);
}


for (int incr=0;incr<6;incr++) {
    ssd1306_setpos(28,0);
    ssd1306_send_data_start();
    sendBlock(0);
    sendBlock(0);
    ssd1306_send_data_stop();
    ssd1306_setpos(92,0);
    ssd1306_send_data_start();
    sendBlock(0);
    sendBlock(0);
    ssd1306_send_data_stop();
    delay(350);
    doNumber(28,0,score);
    doNumber(92,0,score2);
    delay(350);
  }
}

void drawPlatform() {
  if (player != lastPlayer) {
    ssd1306_setpos(0,lastPlayer/8);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop();
    ssd1306_setpos(0,lastPlayer/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop(); 
    ssd1306_setpos(0,lastPlayer/8+2);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop(); 
  }  

  if (player%8!=0){
    ssd1306_setpos(0,player/8);
    ssd1306_send_data_start();
    ssd1306_send_byte((B11111111)<<player%8);
    ssd1306_send_data_stop();
    ssd1306_setpos(0,player/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B11111111);
    ssd1306_send_data_stop();    
    ssd1306_setpos(0,player/8+2);
    ssd1306_send_data_start();
    ssd1306_send_byte((B01111110)>>8-player%8);
    ssd1306_send_data_stop();
  } else {
    ssd1306_setpos(0,player/8);
    ssd1306_send_data_start();
    ssd1306_send_byte(B11111111);
    ssd1306_send_data_stop();
    ssd1306_setpos(0,player/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B11111111);
    ssd1306_send_data_stop();

  }
  lastPlayer = player;
}

void drawPlatform2() {

  if (player2 != lastPlayer2) {
    ssd1306_setpos(127,lastPlayer2/8);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop();
    ssd1306_setpos(127,lastPlayer2/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop(); 
    ssd1306_setpos(127,lastPlayer2/8+2);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop(); 
  }
  
  if (player2%8!=0){
    ssd1306_setpos(127,player2/8);
    ssd1306_send_data_start();
    ssd1306_send_byte((B11111111)<<player2%8);
    ssd1306_send_data_stop();
    ssd1306_setpos(127,player2/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B11111111);
    ssd1306_send_data_stop();        
    ssd1306_setpos(127,player2/8+2);
    ssd1306_send_data_start();
    ssd1306_send_byte((B01111110)>>8-player2%8);
    ssd1306_send_data_stop();
  } else {
    ssd1306_setpos(127,player2/8);
    ssd1306_send_data_start();
    ssd1306_send_byte((B11111111)<<0);
    ssd1306_send_data_stop();
    ssd1306_setpos(127,player2/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte((B11111111)<<0);
    ssd1306_send_data_stop();
  }
  lastPlayer2 = player2;
}

void sendBlock(int fill){
 ssd1306_send_byte(B00000000);
 ssd1306_send_byte(B00000000);
 ssd1306_send_byte(B00000000);
 ssd1306_send_byte(B00000000);
 ssd1306_send_byte(B00000000);
 ssd1306_send_byte(B00000000);
 ssd1306_send_byte(B00000000);
 ssd1306_send_byte(B00000000);
}

void blankBall(int x, int y) {
  if (y%8!=0){
    ssd1306_setpos(x,y/8);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop();
    
    ssd1306_setpos(x,y/8+1);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop();
  } else {
    ssd1306_setpos(x,y/8);
    ssd1306_send_data_start();
    ssd1306_send_byte(B00000000);
    ssd1306_send_byte(B00000000);
    ssd1306_send_data_stop();
  }
}


void drawBall(int x, int y) {
  if (y%8!=0){
    ssd1306_setpos(x,y/8);
    ssd1306_send_data_start();
    doDrawLS(0,y%8);
    ssd1306_send_data_stop();
    
    ssd1306_setpos(x,y/8+1);
    ssd1306_send_data_start();
    doDrawRS(0,8-y%8);
    ssd1306_send_data_stop();
  } else {
    ssd1306_setpos(x,y/8);
    ssd1306_send_data_start();
    doDrawLS(0,0);
    ssd1306_send_data_stop();
  }
}

void doDrawRS(long P1, byte P2) {
  ssd1306_send_byte((B00000011 | P1)>>P2);
  ssd1306_send_byte((B00000011 | P1)>>P2);
}
void doDrawLS(long P1, byte P2) {
  ssd1306_send_byte((B00000011 | P1)<<P2);
  ssd1306_send_byte((B00000011 | P1)<<P2);
}

void startGame(void) {
  
    ssd1306_fillscreen(0x00);

    ssd1306_char_f6x8(16, 3, "-- GET READY --");
    doNumber(60,5,3);
    delay(1000);
    doNumber(60,5,2);
    delay(1000);
    doNumber(60,5,1);
    delay(1000);
    ssd1306_fillscreen(0x00);

    for (int i = 800; i>200; i = i - 200){
    beep(30,i);
    }

}



================================================
FILE: BatBonanzaAttinyArcade/font6x8AJ.h
================================================
/*
 * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays
 *
 * @file: font6x8.h
 * @created: 2014-08-12
 * @author: Neven Boyanov
 * 
 * Hacked by andy jackson to allow two games (originally by webboggles.com) to 
 * fit onto an ATTiny85 at the same time - hence several characters are missing
 * and a couple have been moved to limit the amount of software remapping required
 * to map ASCII values onto locations in this array.
 *
 * Source code available at: https://bitbucket.org/tinusaur/ssd1306xled
 *
 */

// ----------------------------------------------------------------------------

#include <avr/pgmspace.h>

// ----------------------------------------------------------------------------

/* Standard ASCII 6x8 font */
static const uint8_t ssd1306xled_font6x8 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sp
/*
  0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, // !
  0x00, 0x00, 0x07, 0x00, 0x07, 0x00, // "
  0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14, // #
  0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // $
  0x00, 0x62, 0x64, 0x08, 0x13, 0x23, // %
  0x00, 0x36, 0x49, 0x55, 0x22, 0x50, // &
  0x00, 0x00, 0x05, 0x03, 0x00, 0x00, // '
  0x00, 0x00, 0x1c, 0x22, 0x41, 0x00, // (
  0x00, 0x00, 0x41, 0x22, 0x1c, 0x00, // )
  0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, // *
  0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // +
  0x00, 0x00, 0x00, 0xA0, 0x60, 0x00, // ,
  */
  0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // -
  0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // .
  0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w in place of /
  //0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // /
  0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0
  0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // 1
  0x00, 0x42, 0x61, 0x51, 0x49, 0x46, // 2
  0x00, 0x21, 0x41, 0x45, 0x4B, 0x31, // 3
  0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // 4
  0x00, 0x27, 0x45, 0x45, 0x45, 0x39, // 5
  0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6
  0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // 7
  0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // 8
  0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // 9
  0x00, 0x00, 0x36, 0x36, 0x00, 0x00, // :
/*
  0x00, 0x00, 0x56, 0x36, 0x00, 0x00, // ;
  0x00, 0x08, 0x14, 0x22, 0x41, 0x00, // <
  0x00, 0x14, 0x14, 0x14, 0x14, 0x14, // =
  0x00, 0x00, 0x41, 0x22, 0x14, 0x08, // >
  0x00, 0x02, 0x01, 0x51, 0x09, 0x06, // ?
  0x00, 0x32, 0x49, 0x59, 0x51, 0x3E, // @
 */
  0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, // A
  0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, // B
  0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, // C
  0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, // D
  0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, // E
  0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, // F
  0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, // G
  0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // H
  0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, // I
  0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, // J
  0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, // K
  0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, // L
  0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, // M
  0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, // N
  0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // O
  0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, // P
  0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q
  0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, // R
  0x00, 0x46, 0x49, 0x49, 0x49, 0x31, // S
  0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, // T
  0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, // U
  0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, // V
  0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, // W
  0x00, 0x63, 0x14, 0x08, 0x14, 0x63, // X
  0x00, 0x07, 0x08, 0x70, 0x08, 0x07, // Y
  0x00, 0x61, 0x51, 0x49, 0x45, 0x43, // Z
  /*
  0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // [
  0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55, // 55
  0x00, 0x00, 0x41, 0x41, 0x7F, 0x00, // ]
  0x00, 0x04, 0x02, 0x01, 0x02, 0x04, // ^
  0x00, 0x40, 0x40, 0x40, 0x40, 0x40, // _
  0x00, 0x00, 0x01, 0x02, 0x04, 0x00, // '
*/
  0x00, 0x20, 0x54, 0x54, 0x54, 0x78, // a
  0x00, 0x7F, 0x48, 0x44, 0x44, 0x38, // b
  0x00, 0x38, 0x44, 0x44, 0x44, 0x20, // c
  0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, // d
  0x00, 0x38, 0x54, 0x54, 0x54, 0x18, // e
  0x00, 0x08, 0x7E, 0x09, 0x01, 0x02, // f
  0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g
  0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C, // y (in place of h)
  //0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, // h
  0x00, 0x00, 0x44, 0x7D, 0x40, 0x00, // i
  0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, // j
  0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // k
  0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, // l
  0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, // m
  0x00, 0x7C, 0x08, 0x04, 0x04, 0x78, // n
  0x00, 0x38, 0x44, 0x44, 0x44, 0x38, // o
  0x00, 0xFC, 0x24, 0x24, 0x24, 0x18, // p
  0x00, 0x18, 0x24, 0x24, 0x18, 0xFC, // q
  0x00, 0x7C, 0x08, 0x04, 0x04, 0x08, // r
  0x00, 0x48, 0x54, 0x54, 0x54, 0x20, // s
  0x00, 0x04, 0x3F, 0x44, 0x40, 0x20, // t
  0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C, // u
  0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, // v
 // 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w
 // 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, // x
 // 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C // y
};

// ----------------------------------------------------------------------------



================================================
FILE: Frogger_Attiny_Arcade/Frogger_Attiny_Arcade.ino
================================================
/*  2015 / 2016 / 2017
 *  Frogger game by Andy Jackson - Twitter @andyhighnumber
 *  
 *  Special thanks to @senkunmusahi, who created the artwork bitmaps in the game using https://www.riyas.org/2013/12/online-led-matrix-font-generator-with.html
 *  
 *  Inspired by http://webboggles.com/ and includes some code from the #AttinyArcade games on that site
 *  The code that does not fall under the licenses of sources listed below can be used non commercially with attribution.
 *  This software is supplied without warranty of any kind.
 *  
 *  Controls:
 *  On the standard AttinyArcade:
 *  LEFT and RIGHT buttons move the frog across 
 *  BOTH BOTTONS TOGETHER move the frog forwards
 *  
 *  HIGHLY RECOMMENDED:
 *  On custom hardware (see schematic in folder where you found this file) there is an additional button to move frog forward  
 *  
 *  Also, from standby....
 *  Press and hold left button to turn sound on and off
 *  Press and hold left button with the right button held to reset high score
 * 
 *  This sketch is using the screen control and font functions written by Neven Boyanov for the http://tinusaur.wordpress.com/ project
 *  Source code and font files available at: https://bitbucket.org/tinusaur/ssd1306xled
 *  **Note that this highly size-optimised version requires modified library functions (which are in this source code file) 
 *  and a modified font header
 * 
 *  Sleep code is based on this blog post by Matthew Little:
 *  http://www.re-innovation.co.uk/web12/index.php/en/blog-75/306-sleep-modes-on-attiny85
*/
#include <EEPROM.h>
#include "font6x8AJ2.h"
#include <avr/pgmspace.h>
#include <avr/sleep.h>
#include <avr/interrupt.h> // needed for the additional interrupt

// Uncomment this #define to make the logs smaller (/thinner)
//#define SMALLLOGS

// Make click delay an even number - it gets halved and then used in an integer comparison
#define CLICKDELAY 120 

// The basline speed - higher number is slower
#define MOVEBASE 1000 

#define DIGITAL_WRITE_HIGH(PORT) PORTB |= (1 << PORT)
#define DIGITAL_WRITE_LOW(PORT) PORTB &= ~(1 << PORT)

// Routines to set and clear bits (used in the sleep code)
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

// Defines for OLED output
#define SSD1306XLED_H
#define SSD1306_SCL   PORTB4  // SCL, Pin 4 on SSD1306 Board - for webbogles board
#define SSD1306_SDA   PORTB3  // SDA, Pin 3 on SSD1306 Board - for webbogles board
#define SSD1306_SA    0x78  // Slave address

// Function prototypes

// Drawing functions - adapted from those at https://bitbucket.org/tinusaur/ssd1306xled
void ssd1306_init(void);
void ssd1306_xfer_start(void);
void ssd1306_xfer_stop(void);
void ssd1306_send_byte(uint8_t byte);
void ssd1306_send_command(uint8_t command);
void ssd1306_send_data_start(void);
void ssd1306_send_data_stop(void);
void ssd1306_setpos(uint8_t x, uint8_t y);
void ssd1306_fillscreen(uint8_t fill_Data);
void ssd1306_char_f6x8(uint8_t x, uint8_t y, const char ch[]);
void ssd1306_draw_bmp(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t bitmap[]);

// Custom draw functions - allow for extra functionality like inverse display
void sendBlock(byte, bool);
void sendByte(byte, bool);

// Other generic functions for games (both originated in code from webboggles.com and the sleep code is by Matthew Little - see above)
void beep(int,int);
void system_sleep(void);
void doNumber (int,int,int);

// Game functions
void playFrogger(void);
void levelUp(int);
void moveBlocks(void);
void initScreen(void);
void drawDocks(void);
void drawLives(void);
void displayTitle(void);
void resetDock(byte);
void checkCollision(void);

// Global variables - yes I know all these global vars is a lazy way to code but it makes it easier to prevent stack overflows when you're working with 512 bytes! 
// Most of these are initialised in the main game function (playFrogger())
int watchDog;             // Counts drawing cycles so I can shut the game down if there's inactivity - battery saver!
boolean stopAnimate;      // this is set to 1 when a collision is detected
int lives;                // Lives in the game - this can go negative to end the game, which is why it's a signed variable  
bool frogDocks[5];        // Tracks which frog docks are full (at the top of the screen)
bool flipFlop;            // Used in routines that flip-flop between two states (left and right)
bool flipFlopShift;       // Same as previous one
byte frogColumn;          // Column location of frog (there are 16 altogether)
byte frogRow;             // Row locaiton of frog (there are 8, but 0 is the frog docks at the top and 7 is the start row)
byte frogLeftLimit;       // Left limit of frog travel on start row (changes as digits in score increases)
byte frogRightLimit;      // Right limit of frog travel on start row (changes as lives decrease as there's then more space)
byte level;               // Level - starts at 1
byte blockShiftL;         // Number of pixels to shift the left-going rows by
byte blockShiftR;         // Number of pixels to shift the right-going rows by
int interimStep;          // Used as timer for incremental movements
int moveDelay;            // How long to wait until the next movement of logs etc - changes as levels increase to make the game go faster
int dockedFrogs;          // How many frogs are in the docks at the top
unsigned long clickBase;  // Timer for debounce
boolean clickLock;        // For debounce routine
int score;                // Obvious I hope
int topScore;             // High score
boolean newHigh;          // Is there a new high score?
boolean mute = 0;         // Mute the speaker
byte grid[6][16];         // Grid for items like logs, crocs, cars and lorries
byte frogMode;            // Represents the frog direction 
bool moveForward=0;       // Captures when the 'forward' button is pressed
bool moveLeft=0;          // Captures when the 'left' button is pressed
bool moveRight=0;         // Captures when the 'right' button is pressed

// Bitmaps created by @senkunmusahi using https://www.riyas.org/2013/12/online-led-matrix-font-generator-with.html
static const byte  bitmaps[15][8] PROGMEM = {
// Frogs
  {0x83, 0xDC, 0x7A, 0x3F, 0x3F, 0x7A, 0xDC, 0x83},
  {0x99, 0xBD, 0xDB, 0x7E, 0x7E, 0x3C, 0xE7, 0x81},
  {0x81, 0xE7, 0x3C, 0x7E, 0x7E, 0xDB, 0xBD, 0x99},

#ifdef SMALLLOGS
// Small logs
  {0x1C, 0x22, 0x41, 0x55, 0x55, 0x51, 0x43, 0x61},
  {0x69, 0x6B, 0x43, 0x61, 0x45, 0x45, 0x61, 0x65},
  {0x45, 0x55, 0x41, 0x5D, 0x63, 0x5D, 0x22, 0x1C},
#else
// Bigger logs
  {0x3C, 0x7E, 0xD7, 0xB5, 0xAD, 0xBF, 0xFF, 0xED}, 
  {0xAD, 0xAD, 0xFF, 0xB7, 0xF5, 0xBF, 0xB7, 0xAD}, 
  {0xED, 0xBD, 0xC3, 0xBD, 0xA5, 0xBD, 0x42, 0x3C},  
#endif

// Trucks
  {0x00, 0x7F, 0x41, 0x55, 0x55, 0x55, 0x55, 0x55},
  {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, 
  {0x41, 0x7F, 0x22, 0x7F, 0x7F, 0x63, 0x22, 0x1C},

// Crocs
  {0x41, 0x63, 0x46, 0x6E, 0x7C, 0x7E, 0x7A, 0x3E},
  {0xBC, 0xFE, 0x7E, 0x3E, 0xBE, 0xBE, 0xFC, 0x7C},  
  {0x78, 0x38, 0x38, 0x38, 0x70, 0x60, 0x60, 0x40},

// Cars
  {0x00, 0x1C, 0x22, 0x63, 0x7F, 0x7F, 0x22, 0x22},
  {0x22, 0x3E, 0x3E, 0x7F, 0x63, 0x63, 0x22, 0x1C},
  {0x22, 0x3E, 0x3E, 0x7F, 0x63, 0x63, 0x22, 0x1C}
  };

// Opening artwork created by @senkunmusahi using https://www.riyas.org/2013/12/online-led-matrix-font-generator-with.html
static const byte titleBmp[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xF0, 0x7C, 0x06, 0x73, 0x59, 0x43,
0x06, 0x3C, 0x38, 0x30, 0x30, 0x38, 0x3E, 0x26, 0x7B, 0x59, 0x43, 0x06, 0x7C, 0xF0, 0xC0, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xFF,
0xCF, 0x01, 0x00, 0x00, 0x30, 0x60, 0xE0, 0xC0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xC0,
0xC0, 0x60, 0x30, 0x00, 0x00, 0x00, 0x01, 0xCF, 0xFE, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7C, 0xFE, 0x86, 0x0E, 0x0E, 0x1C, 0x18, 0x31, 0x7F, 0xFE, 0xFC, 0x1C, 0x18, 0x38, 0x38, 0x38,
0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x18, 0x1C, 0xFC, 0xFE, 0x7F,
0x39, 0x18, 0x1C, 0x0E, 0x0E, 0xC6, 0xFE, 0x3C, 0x00, 0x01, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xC0,
0xC0, 0x80, 0x03, 0x07, 0xFC, 0xF8, 0x00, 0x00, 0xF0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xE0, 0xF0,
0x00, 0x00, 0xF8, 0xFC, 0x0F, 0x03, 0x80, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x03, 0x01, 0x00,
0x04, 0x06, 0x0F, 0x0F, 0x06, 0x06, 0x03, 0x03, 0x03, 0x63, 0x73, 0x33, 0x3B, 0xFF, 0xFF, 0x7F,
0x3F, 0x38, 0xF0, 0xC0, 0x00, 0xF0, 0xF8, 0x3F, 0x7F, 0xFF, 0xFF, 0x3B, 0x33, 0x63, 0x63, 0x03,
0x03, 0x03, 0x06, 0x06, 0x0F, 0x0F, 0x06, 0x00, 
};

// Interrupt handlers
ISR(PCINT0_vect){ // PB0 pin button interrupt           
  if (clickLock == 0) {
    moveLeft = 1;
    clickLock = 1;
    clickBase = millis();
  }
}

void playerIncFrogger(){ // PB2 pin button interrupt
  if (clickLock == 0) {
    moveRight = 1;
    clickLock = 1;
    clickBase = millis();
  }
}

void displayTitle(void) {
  int incr = 0;
  for(int lxn = 2; lxn < 7; lxn++) {
    ssd1306_setpos(85,lxn); 
    ssd1306_send_data_start();
    for(int lxn2 = 0; lxn2 < 40; lxn2++) {
      ssd1306_send_byte(pgm_read_byte(&titleBmp[incr]));      
      incr++;
    }
    ssd1306_send_data_stop();                    
  }
}

// Arduino stuff - setup
void setup() {
  DDRB = 0b00000010;    // set PB1 as output (for the speaker)
  PCMSK = 0b00000001; // pin change mask: listen to portb bit 1
  GIMSK |= 0b00100000;  // enable PCINT interrupt 
  sei();          // enable all interrupts
}

// Arduino stuff - loop
void loop() { 
  ssd1306_init();
  ssd1306_fillscreen(0x00);

  // The lower case character set is seriously compromised because I've had to truncate the ASCII table
  // to release space for executable code.
  // There is no z in the table as this isn't used anywhere in the text here and most of the 
  // symbols are also missing for the same reason (see my hacked version of font6x8.h - font6x8AJ.h for more detail)
  ssd1306_char_f6x8(0, 2, "F R O G G E R");
  ssd1306_char_f6x8(0, 4, "andy jackson"); // see comments above !

  ssd1306_setpos(0,1); 
  for (int incr = 0; incr < 80; incr++) {
    ssd1306_send_data_start();
    ssd1306_send_byte(B00111000);
    ssd1306_send_data_stop();                    
  }
  ssd1306_setpos(0,3); 
  for (int incr = 0; incr < 80; incr++) {
    ssd1306_send_data_start();
    ssd1306_send_byte(B00011100);
    ssd1306_send_data_stop();                    
  }

  displayTitle();

  ssd1306_char_f6x8(0, 6, "inspired by");
  ssd1306_char_f6x8(0, 7, "webboggles.com");
  delay(1500);
  ssd1306_char_f6x8(0, 6, "artwork by ");
  ssd1306_char_f6x8(0, 7, "zsenkunmusashi");  // see comments above - f has been replaced by @ in the ASCII table

  long startT = millis();
  long nowT =0;
  boolean sChange = 0;

  while(digitalRead(0) == HIGH) {
    nowT = millis();
    if (nowT - startT > 2000) {
      sChange = 1;     
      if (digitalRead(2) == HIGH) {
        EEPROM.write(0,0);
        EEPROM.write(1,0);
        ssd1306_char_f6x8(8, 0, "-HIGH SCORE RESET-");  
      } else if (mute == 0) { mute = 1; ssd1306_char_f6x8(32, 0, "-- MUTE --"); } else { mute = 0; ssd1306_char_f6x8(31, 0, "- SOUND ON -");  }    
      break;
    }
    if (sChange == 1) break;
  }  
  while(digitalRead(0) == HIGH);

  if (sChange == 0) {
    delay(2000);

    ssd1306_init();
    ssd1306_fillscreen(0x00);

    playFrogger(); 

    topScore = EEPROM.read(0);
    topScore = topScore << 8;
    topScore = topScore |  EEPROM.read(1);

    newHigh = 0;
    if (score > topScore) { 
      topScore = score;
      EEPROM.write(1,score & 0xFF); 
      EEPROM.write(0,(score>>8) & 0xFF); 
      newHigh = 1;
      }

    ssd1306_fillscreen(0x00);
    ssd1306_char_f6x8(11, 1, "----------------");
    ssd1306_char_f6x8(11, 2, "G A M E  O V E R");
    ssd1306_char_f6x8(11, 3, "----------------");
    ssd1306_char_f6x8(37, 5, "SCORE:");
    doNumber(75, 5, score);
    if (!newHigh) {
      ssd1306_char_f6x8(21, 7, "HIGH SCORE:");
      doNumber(88, 7, topScore);
    }
    delay(1000);
    if (newHigh) {
      ssd1306_fillscreen(0x00);
      ssd1306_char_f6x8(10, 1, "----------------");
      ssd1306_char_f6x8(10, 3, " NEW HIGH SCORE ");
      ssd1306_char_f6x8(10, 7, "----------------");
      doNumber(50,5,topScore);
      for (int i = 700; i>200; i = i - 50){
      beep(30,i);
      }
      delay(1200);    
    } 
  }
  system_sleep();
}

void doNumber (int x, int y, int value) {
    char temp[10] = {0,0,0,0,0,0,0,0,0,0};
    itoa(value,temp,10);
    ssd1306_char_f6x8(x, y, temp);
}

void ssd1306_init(void){
  DDRB |= (1 << SSD1306_SDA); // Set port as output
  DDRB |= (1 << SSD1306_SCL); // Set port as output

  ssd1306_send_command(0xAE); // display off
  ssd1306_send_command(0x00); // Set Memory Addressing Mode
  ssd1306_send_command(0x10); // 00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
  ssd1306_send_command(0x40); // Set Page Start Address for Page Addressing Mode,0-7
  ssd1306_send_command(0x81); // Set COM Output Scan Direction
  ssd1306_send_command(0xCF); // ---set low rowumn address
  ssd1306_send_command(0xA1); // ---set high rowumn address
  ssd1306_send_command(0xC8); // --set start line address
  ssd1306_send_command(0xA6); // --set contrast control register
  ssd1306_send_command(0xA8);
  ssd1306_send_command(0x3F); // --set segment re-map 0 to 127
  ssd1306_send_command(0xD3); // --set normal display
  ssd1306_send_command(0x00); // --set multiplex ratio(1 to 64)
  ssd1306_send_command(0xD5); // 
  ssd1306_send_command(0x80); // 0xa4,Output follows RAM content;0xa5,Output ignores RAM content
  ssd1306_send_command(0xD9); // -set display offset
  ssd1306_send_command(0xF1); // -not offset
  ssd1306_send_command(0xDA); // --set display clock divide ratio/oscillator frequency
  ssd1306_send_command(0x12); // --set divide ratio
  ssd1306_send_command(0xDB); // --set pre-charge period
  ssd1306_send_command(0x40); // 
  ssd1306_send_command(0x20); // --set com pins hardware configuration
  ssd1306_send_command(0x02);
  ssd1306_send_command(0x8D); // --set vcomh
  ssd1306_send_command(0x14); // 0x20,0.77xVcc
  ssd1306_send_command(0xA4); // --set DC-DC enable
  ssd1306_send_command(0xA6); // 
  ssd1306_send_command(0xAF); // --turn on oled panel 
}

void ssd1306_xfer_start(void){
  DIGITAL_WRITE_HIGH(SSD1306_SCL);  // Set to HIGH
  DIGITAL_WRITE_HIGH(SSD1306_SDA);  // Set to HIGH
  DIGITAL_WRITE_LOW(SSD1306_SDA);   // Set to LOW
  DIGITAL_WRITE_LOW(SSD1306_SCL);   // Set to LOW
}

void ssd1306_xfer_stop(void){
  DIGITAL_WRITE_LOW(SSD1306_SCL);   // Set to LOW
  DIGITAL_WRITE_LOW(SSD1306_SDA);   // Set to LOW
  DIGITAL_WRITE_HIGH(SSD1306_SCL);  // Set to HIGH
  DIGITAL_WRITE_HIGH(SSD1306_SDA);  // Set to HIGH
}

void ssd1306_send_byte(uint8_t byte){
  uint8_t i;
  for(i=0; i<8; i++)
  {
    if((byte << i) & 0x80)
      DIGITAL_WRITE_HIGH(SSD1306_SDA);
    else
      DIGITAL_WRITE_LOW(SSD1306_SDA);
    
    DIGITAL_WRITE_HIGH(SSD1306_SCL);
    DIGITAL_WRITE_LOW(SSD1306_SCL);
  }
  DIGITAL_WRITE_HIGH(SSD1306_SDA);
  DIGITAL_WRITE_HIGH(SSD1306_SCL);
  DIGITAL_WRITE_LOW(SSD1306_SCL);
}

void ssd1306_send_command(uint8_t command){
  ssd1306_xfer_start();
  ssd1306_send_byte(SSD1306_SA);  // Slave address, SA0=0
  ssd1306_send_byte(0x00);  // write command
  ssd1306_send_byte(command);
  ssd1306_xfer_stop();
}

void ssd1306_send_data_start(void){
  ssd1306_xfer_start();
  ssd1306_send_byte(SSD1306_SA);
  ssd1306_send_byte(0x40);  //write data
}

void ssd1306_send_data_stop(void){
  ssd1306_xfer_stop();
}

void ssd1306_setpos(uint8_t x, uint8_t y)
{
  if (y>7) return;
  ssd1306_xfer_start();
  ssd1306_send_byte(SSD1306_SA);  //Slave address,SA0=0
  ssd1306_send_byte(0x00);  //write command

  ssd1306_send_byte(0xb0+y);
  ssd1306_send_byte(((x&0xf0)>>4)|0x10); // |0x10
  ssd1306_send_byte((x&0x0f)|0x01); // |0x01

  ssd1306_xfer_stop();
}

void ssd1306_fillscreen(uint8_t fill_Data){
  uint8_t m,n;
  for(m=0;m<8;m++)
  {
    ssd1306_send_command(0xb0+m); //page0-page1
    ssd1306_send_command(0x00);   //low rowumn start address
    ssd1306_send_command(0x10);   //high rowumn start address
    ssd1306_send_data_start();
    for(n=0;n<128;n++)
    {
      ssd1306_send_byte(fill_Data);
    }
    ssd1306_send_data_stop();
  }
}

void ssd1306_char_f6x8(uint8_t x, uint8_t y, const char ch[]){
  uint8_t c,i,j=0;
  while(ch[j] != '\0')
  {
    c = ch[j] - 32;
    if (c >0) c = c - 12;
    if (c >15) c = c - 6;
    if (c>40) c=c-9;
    if(x>126)
    {
      x=0;
      y++;
    }
    ssd1306_setpos(x,y);
    ssd1306_send_data_start();
    for(i=0;i<6;i++)
    {
      ssd1306_send_byte(pgm_read_byte(&ssd1306xled_font6x8[c*6+i]));
    }
    ssd1306_send_data_stop();
    x += 6;
    j++;
  }
}

void system_sleep(void) {
  ssd1306_fillscreen(0x00);
  ssd1306_send_command(0xAE);
  cbi(ADCSRA,ADEN);                    // switch analog to digital converter off
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  sleep_enable();
  sleep_mode();                        // system actually sleeps here
  sleep_disable();                     // system continues execution here when watchdog timed out 
  sbi(ADCSRA,ADEN);                    // switch analog to digital converter on
  ssd1306_send_command(0xAF);
}

void beep(int bCount,int bDelay){
  if (mute) return;
  for (int i = 0; i<=bCount; i++){digitalWrite(1,HIGH);for(int i2=0; i2<bDelay; i2++){__asm__("nop\n\t");}digitalWrite(1,LOW);for(int i2=0; i2<bDelay; i2++){__asm__("nop\n\t");}}
}


/* ------------------------
 *  Frogger main game code
 */
void playFrogger(){
  stopAnimate = 0;
  score = 0;
  moveDelay = MOVEBASE;
  level = 1;
  frogColumn = 8;
  frogRow = 7;
  clickLock = 0;
  frogMode = 1;
  interimStep =0;
  blockShiftL = 0;
  blockShiftR = 0;
  flipFlop = 1;
  flipFlopShift = 1;
  dockedFrogs = 0;
  lives = 2;
  frogRightLimit = 12;
  watchDog = 1; // we use this to see if there's been movement - it's only ever zero when the frog has just moved!

  attachInterrupt(0,playerIncFrogger,CHANGE);

  initScreen();
  resetDock(0);

  drawFrog(frogMode,0);
  drawGameScreen(frogMode);
  drawLives();
  drawDocks();
  
  doNumber(0,7,score);
  
  while (lives >= 0) {
    interimStep++;

    if (watchDog >= 500) lives = -1; // Stop the game if nothing's happening - maybe triggered in someone's pocket so this is to save battery!
    
    // Calculate left limit of frog movement so it doesn't hit the score
    frogLeftLimit = 1;
    if ((score / 10) % 10 != 0) frogLeftLimit++; 
    if ((score / 100) % 10 != 0) frogLeftLimit++;
    if ((score / 1000) % 10 != 0) frogLeftLimit++;

    // Move stuff along if it's time to
    if (interimStep > moveDelay/8) {
      watchDog++;
      blockShiftL++;
      if (flipFlopShift == 1) flipFlopShift = 0; else flipFlopShift = 1;
      if (flipFlopShift == 1) blockShiftR++;
      if (blockShiftL == 7) {
        moveBlocks();
        blockShiftL = 0;
      }
      if (blockShiftR == 7) {
        blockShiftR = 0;
      }
      interimStep = 0;
      checkCollision();
      if (stopAnimate == 0) {
        drawGameScreen(frogMode);
        drawFrog(frogMode,0);
      }
    }

    // Handle input from 'jump' button (the other two buttons are captured in the interrupt routines)
    if (analogRead(0) < 940 && clickLock == 0) {
      moveForward = 1;
      watchDog = 0;   // reset the watchdog so the game doesn't end!
      clickLock = 1;
      clickBase = millis();
    }

    // Handle moving left
    if(moveLeft == 1 && millis() > clickBase + CLICKDELAY/2) {
      watchDog = 0;   // reset the watchdog so the game doesn't end!
      moveLeft = 0;
      if (digitalRead(2) == HIGH) moveForward = 1; else {
        drawFrog(0,0);  // delete the frog
        // move the frog, checking it isn't jumping off the edge of the screen
        if ((frogRow == 7 && frogColumn > frogLeftLimit) || (frogRow < 7 && frogColumn > 0)) {
          frogColumn --;
        } else if (frogRow < 7) stopAnimate = 1;
        frogMode = 2; // pointing left
      }
    }

    // Handle moving right
    if(moveRight == 1 && millis() > clickBase + CLICKDELAY/2){
      watchDog = 0;   // reset the watchdog so the game doesn't end!
      moveRight = 0;
      if (digitalRead(0) == HIGH) moveForward = 1; else {
        drawFrog(0,0); // delete the frog
        // move the frog, checking it isn't jumping off the edge of the screen
        if ((frogRow == 7 && frogColumn < frogRightLimit) || (frogRow < 7 && frogColumn < 14)) {
          frogColumn ++;
        } else if (frogRow < 7) stopAnimate = 1;
        frogMode = 3; // pointing right    
      }
    }

    // Handle 'move forward' button press
    if (moveForward == 1) {
      moveForward = 0;
  
      score+= level;          // increment the score for every move
      doNumber(0,7,score);    // display new score
      drawFrog(0,0);          // delete the frog
  
      if (frogRow > 1) {
        frogRow--; 
        // Correct for the skew in frog position created by the blockShift scrolling parameter
        if (frogRow == 3 && blockShiftL < 4) frogColumn--;
        if (frogRow == 2 && blockShiftR + blockShiftL < 5) frogColumn++;
        if (frogRow == 1 && blockShiftR + blockShiftL < 5) frogColumn--;
                
      } else {
        // frog is at the docks!
        if (blockShiftL < 4 && frogColumn <15) frogColumn++;  // account for skew due to block shifting
        byte dockPos = (byte)floor(frogColumn/3);
        if (frogDocks[dockPos] == 0 ) {
          dockedFrogs++;          
          frogDocks[dockPos] = 1;                             // assign this dock as filled
          frogRow = 7;                                        // reposition the frog at the start
          frogColumn = 8;
          for (int i = 1000; i>200; i = i - 100){             // make sound
          beep(10,i);
          drawDocks();                                        // redraw the docks
          }
        } else stopAnimate = 1;
      }
      frogMode = 1;             // mode 1 = forwards position

      // check if all docks are full - if so, then level up!
      if (dockedFrogs >= 5) {
        level++;
        levelUp(level);
        if (moveDelay > 99) moveDelay -=100; // make the game speed up 
        initScreen();                        // reinitalise the position of game items
        resetDock(0);                        // reinitliase the dock
        dockedFrogs = 0;              
        drawDocks();                         // display the (now empty) docks
        drawLives();                         // display the lives
        doNumber(0,7,score);                 // display the score
      }      
    }

    // The frog has moved 
    if (watchDog == 0 && stopAnimate == 0) {
      watchDog = 1;               // set to something other than zero so this routine doesn't run again
      // redraw the frog
      drawFrog(frogMode,0);
      // redraw the screen
      drawGameScreen(frogMode);
      // make jump sound
      beep(30,400);
      beep(30,300);
      beep(30,200);
    }
    
    checkCollision();
    
    if (clickLock == 1 && millis() > clickBase + CLICKDELAY && digitalRead(2)==0 && digitalRead(0)==0 && analogRead(0) > 940) clickLock = 0; // normal debounce

    // check to see if the frog has been killed
    if (stopAnimate != 0) {
      // redraw the screen
      drawGameScreen(frogMode);
      // animation for frog death
      drawFrog(0,1);
      for (int i = 0; i<250; i = i+ 50){  
        beep(50,i);
      }
      drawFrog(frogMode,1);    
      for (int i = 250; i<500; i = i+ 50){  
        beep(50,i);
      }
      drawFrog(0,1);
      for (int i = 500; i<750; i = i+ 50){  
        beep(50,i);
      }
      drawFrog(frogMode,1);
      for (int i = 750; i<1000; i = i+ 50){  
        beep(50,i);
      }
      delay(600);
      lives--;          // increment the score for every move
      frogRightLimit++; // there's one less frog drawn on right so you can move a bit further across (if you really want to!)
      stopAnimate = 0;  // reset parameter
      drawLives();      // display number of lives left
      frogColumn = 8;   // reinitalise frog location
      frogRow = 7;
      }
  }  // Big while loop (main game loop) goes until lives is negative
} 

void checkCollision(void) {
  if (frogRow > 0 && frogRow < 4 && grid[frogRow-1][frogColumn] == 0) stopAnimate = 1; // the frog has fallen in the river
  if (frogRow > 0 && frogRow < 4 && grid[frogRow-1][frogColumn] > 9) stopAnimate = 1; // the frog has stepped on a croc
  if ((frogRow < 7 && frogRow > 3) && (grid[frogRow-1][frogColumn] != 0 || grid[frogRow-1][frogColumn-1] != 0)) stopAnimate = 1; // the frog has been hit by a vehicle
}

// Initialise all the moving objects on the game screen
void initScreen(void) {
  int initCounter[6] = {3,2,4,2,2,3};       // the length of the objects on each row - doesn't change
  int gapCounter[6] = {-2,-3,-4,-4,-3,-5};  // the gaps between objects - change with levels to make it harder as you go thru the game
  int counter[6];                           // used to hold the gap data
  byte stepMode = 0;                        // which component of the object are we drawing (they all have three - a start a middle and an end)
  byte stepShift = 0;                       // offset to shift up to the different objects in the array
  byte crocStartColumn = 0;                 // column at which to stop drawing crocs - is zero at start hence no crocs!

  
  // Adjust difficulty by changing gaps between objects according to level
  if (level == 1) {
    gapCounter[5] = -14;           // easiset setting, for start of game
  }
  if (level < 3) {
    gapCounter[4] = -6;            // make it easier for levels less than 3 by increasing the gap in the cars on this row
  }
  if (level < 4) {
    gapCounter[3] = -7;
  }
  if (level > 4) {
    for (byte incr = 1; incr < 3; incr++) {
      gapCounter[incr]--;         // increase the gaps between the logs for levels over 4  
    }
  }
  if (level > 7) {                // set smaller gaps between cars for levels over 7
    gapCounter[3] = -4; 
    gapCounter[4] = -2;
    gapCounter[5] = -3;
  }
  if (level > 2) crocStartColumn = 5; // one croc appears at level 3 and above
  if (level > 6) crocStartColumn = 9; // two croc appear at level 7 and above
  
  // Initialise the counters
  for (byte incr = 0; incr < 6;incr++) counter[incr] = initCounter[incr];  

  // Initialise array with zeros
  for (byte col = 0; col < 16; col++) {
    for (byte row = 0; row < 6; row++) {
      grid[row][col] = 0;
    }
  }
       
  stepMode = 0;
  // Initialise array with obstacles
  for (byte row = 0; row < 6; row++) {
    for (byte col = 0; col < 15; col++) {         
      if (counter[row] > 0) {
        if (14-row > counter[row]) {
          if (counter[row] == 1) if (stepMode == 1) stepMode = 2; // the next space is blank and we are drawing the middle - draw the end! 
          if (row > 2) stepShift = 3; else stepShift = 0;         // shift up to the trucks in the array
          if (row == 4) stepShift = 9;                            // shift up to the cars in the array - also theres no middle
          
          if (row > 0) {
            grid[row][col] = 4+stepMode+stepShift;                // if you are on any row but the first - draw whatever is appropriate from the bitmaps
          } else if (col >= crocStartColumn) {                    
            grid[row][col] = 4+stepMode+stepShift;                // if you're on row zero (top row of logs) and you are above where crocs should be drawm, draw logs ...
          } else grid[row][col] = 10+stepMode;                    // .. otherwise draw crocs
          if (stepMode == 0) stepMode = 1;                        // we've drawn the left side now switch to central sections
          if (stepMode == 2) stepMode = 0;                        // we've drawn the end, now reset
        }
      } 
      counter[row]--;                                             // decrement the counter
      if (counter[row] <= gapCounter[row]) {
        counter[row] = initCounter[row];                          // if we have gone negative enough to account for the gaps - reset the counter and start again
      }
    }
  }
}

// Display the frog
void drawFrog(byte mode, bool frogDead) {
  if (frogRow > 6 || frogRow < 1 || frogDead == 1) {           // don't draw the frog when it's on the road or on logs - because they are moving, that's handled in the main drawing routine below- exception is when you are animating frog death
    if (frogRow == 1 || frogRow == 3) {                        // these allow for the blocks being shifted when animating the frog death on rows with logs
      ssd1306_setpos(frogColumn*8 + 7 - blockShiftL,frogRow);
    } else if (frogRow == 2) {
      ssd1306_setpos(frogColumn*8 + blockShiftR,frogRow);      
    } else {
      ssd1306_setpos(frogColumn*8,frogRow); 
    }
    ssd1306_send_data_start();
    sendBlock(mode,0);                   // draw the frog - mode is direction
    ssd1306_send_data_stop();                    
  }    
}

// Display the frog and all the moving items on the screen
void drawGameScreen(byte mode) {
  bool inverse = 0;
  
  // Draw objects going left
  for (byte row = 0; row < 6; row+=2) {
    if (row >=0 && row < 3) inverse = 1; else inverse = 0;                              // draw everything (except the frog) in inverse video on the river rows (0,1,2)
    ssd1306_setpos(0,row+1);                                                            // +1 because row 0 here is actually row 1 on the screen
    ssd1306_send_data_start();
    for (byte incr = 0; incr < 7-blockShiftL; incr++) if (grid[row][15] == 0) {         // cover the tiny bit to the far left of the screen up to wherever the main blocks will be drawn (depends on how far they are shifted)
      sendByte(0,inverse);                                                              // draw an empty 8-bit line if there's nothing wrapping around
    } else {
      sendByte(pgm_read_byte(&bitmaps[grid[row][15]-1][1+blockShiftL+incr]), inverse);  // pick the correct bit of whatever is wrapping from the right of the screen
    }
    for (byte col = 0; col < 15; col++) {         
      if (frogRow == row+1 && frogColumn == col && frogRow < 4 && frogRow > 0) {
        sendBlock(mode,0);                                                                          // if we are in a location with the frog, and it's on the logs, draw it - never invert it (hence zero as second parameter here)
      } else if (stopAnimate == 0 && frogRow == row+1 && frogColumn == col + 1 && frogRow > 3 && frogRow < 7) {         // frog is amongst the cars and needs drawing
        for (byte incr = 0; incr < blockShiftL; incr++) sendByte(0,0);                              // draw the blank space up to the frog
        sendBlock(mode,0);                                                                          // draw frog
        for (byte incr = 0; incr < 7-blockShiftL; incr++) sendByte(0,0);                            // draw the blank space after the frog
        col++;                                                                                      // we've now drawn two columns so increment
      } else {
        sendBlock(grid[row][col],inverse);                                                          // draw the correct object for this space - it's not a frog ;)
      }
    }
    // fill in the bit to the right of the main blocks 
    for (byte incr = 0; incr < blockShiftL; incr++) if (grid[row][15] == 0) sendByte(0,inverse); else sendByte(pgm_read_byte(&bitmaps[grid[row][15]-1][incr]),inverse);

    ssd1306_send_data_stop();
  }
  if (frogColumn == 0) drawFrog(mode,1); // this covers the exceptional case where the frog is in the far left colum, in which case the normal routine can't draw it when it's on the road
  
  // Draw objects going right - see comments above, works in basically the same way
  for (byte row = 1; row < 6; row+=2) {
    if (row > 0 && row < 3) inverse = 1; else inverse = 0;
    ssd1306_setpos(0,row+1);
    ssd1306_send_data_start();
    for (byte incr = 0; incr < blockShiftR; incr++) if (grid[row][15] == 0) sendByte(0, inverse); else sendByte(pgm_read_byte(&bitmaps[grid[row][15]-1][incr+(8-blockShiftR)]),inverse);
    for (byte col = 0; col < 15; col++) {         
      if (frogRow == row+1 && frogColumn == col && frogRow < 4 && frogRow > 0) {
        sendBlock(mode,0);    
      } else if (stopAnimate == 0 && frogRow == row+1 && frogColumn == col + 1 && frogRow > 3 && frogRow < 7) {  
        for (byte incr = 0; incr < 7-blockShiftR; incr++) sendByte(0,0);
        sendBlock(mode,0); // draw frog
        for (byte incr = 0; incr < blockShiftR; incr++) sendByte(0,0);
        col++;        
      } else {
        sendBlock(grid[row][col],inverse);        
      }
    }
    for (byte incr = 0; incr < 7-blockShiftR; incr++) if (grid[row][15] == 0) sendByte(0,inverse); else sendByte(pgm_read_byte(&bitmaps[grid[row][15]-1][incr]),inverse);
    ssd1306_send_data_stop();
  }
  if (frogColumn == 0) drawFrog(mode,1);
}

// Send one byte to the screen
void sendByte(byte fill, bool inverse) {
  if (inverse == 0) ssd1306_send_byte(fill); else ssd1306_send_byte(~fill);
}

// Send one block of 8 bytes to the screen - inverse means inverse video, for the river section
void sendBlock(byte fill, bool inverse){
  for (int incr = 0; incr < 8; incr++) {
    if (fill > 0) {
      if (inverse == 0) ssd1306_send_byte(pgm_read_byte(&bitmaps[fill-1][incr])); else ssd1306_send_byte(~pgm_read_byte(&bitmaps[fill-1][incr]));  
    } else if (inverse ==0) ssd1306_send_byte(0); else ssd1306_send_byte(0xFF); 
  }
}

// Draw the frog lives (in the right hand corner)
void drawLives(void) {
  byte tempRow = frogColumn;
  byte tempCol = frogRow;
  frogRow = 7;

  for (int incr = 2; incr > 0; incr--) {
    frogColumn = 15-incr;
    drawFrog(0,1);
  }

  for (int incr = lives; incr > 0; incr--) {
    frogColumn = 15-incr;
    drawFrog(1,1);
  }
  frogRow = tempCol;
  frogColumn = tempRow;    
}

// Draw the docks for the frog to land in at top of screen
void drawDocks(void) {
  byte drawPos = 3;
  for (byte incr = 0; incr < 5; incr++) {
    ssd1306_setpos(drawPos,0);
    ssd1306_send_data_start();
    ssd1306_send_byte(B11111111);
    ssd1306_send_byte(B00000001);
    ssd1306_send_byte(B00000001);
    if (frogDocks[incr] == 1) sendBlock(1,0); else for(byte lxn = 0; lxn < 8; lxn++) ssd1306_send_byte(B00000001);
    ssd1306_send_byte(B00000001);
    ssd1306_send_byte(B00000001);
    ssd1306_send_byte(B11111111);
    ssd1306_send_data_stop();
    drawPos+= 24;
    }    
}

// Set all the frog docks to a single value
void resetDock(byte value) {   for (byte incr = 0; incr < 5;incr++) frogDocks[incr] = value; }

// Handle what happens at the end of a level
void levelUp(int number) {  
  // Flash the frog docks
  delay(200);
  for (byte incr = 0; incr < 5; incr ++) {
    resetDock(0);
    drawDocks();
    for (int i = 800; i>200; i = i - 200){
    beep(20,i);
    }
    resetDock(1);
    drawDocks();
    for (int i = 800; i>200; i = i - 200){
    beep(20,i);
    }
  }  
  delay(500);
  ssd1306_fillscreen(0x00);
  ssd1306_char_f6x8(35, 1, "---------");
  ssd1306_char_f6x8(35, 3, " LEVEL ");
  ssd1306_char_f6x8(35, 5, "---------");
  doNumber(77,3,number);
  delay(1500);    
  ssd1306_fillscreen(0x00);
}

// Move all the items on the game screen (wrapping at the ends) and check for frog dropping off the end of the screen
void moveBlocks(void) {
  int direct = 0;

  if (flipFlop == 1) flipFlop = 0; else flipFlop = 1;
  
  for (byte row = 0; row < 6; row++) {
    // Move the frog along and check to see whether it's gone off the screen, in which case it dies
    if (frogRow < 4 && frogRow > 0) { 
      if (frogRow == row + 1) {
        if (direct == 1 && flipFlop == 1) {
          if (frogColumn >= 14) stopAnimate = 1; else frogColumn++; 
        } else if (direct == 0) {
          if (frogColumn < 1) stopAnimate = 1; else frogColumn--;
        }
      }  
    }
    if (direct == 0) { // move left
      byte temp = grid[row][0];
      for (byte col = 0; col < 15; col++) {         
        grid[row][col] = grid[row][col+1];
      }
      grid[row][15] = temp; // wrap around
      direct = 1;
    } else { // move right
      if (flipFlop == 1) {
        byte temp = grid[row][15];
        for (byte col = 15; col > 0; col--) {         
          grid[row][col] = grid[row][col-1];
          }
        grid[row][0] = temp; // wrap around
        }
      direct = 0;  
      }
    }
  }


================================================
FILE: Frogger_Attiny_Arcade/font6x8AJ2.h
================================================
/*
 * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays
 *
 * @file: font6x8.h
 * @created: 2014-08-12
 * @author: Neven Boyanov
 * 
 * Hacked by andy jackson to allow two games (originally by webboggles.com) to 
 * fit onto an ATTiny85 at the same time - hence several characters are missing
 * and a couple have been moved to limit the amount of software remapping required
 * to map ASCII values onto locations in this array.
 *
 * Source code available at: https://bitbucket.org/tinusaur/ssd1306xled
 *
 */

// ----------------------------------------------------------------------------

#include <avr/pgmspace.h>

// ----------------------------------------------------------------------------

/* Standard ASCII 6x8 font */
static const uint8_t ssd1306xled_font6x8 [] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sp
/*
  0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, // !
  0x00, 0x00, 0x07, 0x00, 0x07, 0x00, // "
  0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14, // #
  0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // $
  0x00, 0x62, 0x64, 0x08, 0x13, 0x23, // %
  0x00, 0x36, 0x49, 0x55, 0x22, 0x50, // &
  0x00, 0x00, 0x05, 0x03, 0x00, 0x00, // '
  0x00, 0x00, 0x1c, 0x22, 0x41, 0x00, // (
  0x00, 0x00, 0x41, 0x22, 0x1c, 0x00, // )
  0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, // *
  0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // +
  0x00, 0x00, 0x00, 0xA0, 0x60, 0x00, // ,
  */
  0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // -
  0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // .
  0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w in place of /
  //0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // /
  0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E, // 0
  0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // 1
  0x00, 0x42, 0x61, 0x51, 0x49, 0x46, // 2
  0x00, 0x21, 0x41, 0x45, 0x4B, 0x31, // 3
  0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // 4
  0x00, 0x27, 0x45, 0x45, 0x45, 0x39, // 5
  0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6
  0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // 7
  0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // 8
  0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // 9
  0x00, 0x00, 0x36, 0x36, 0x00, 0x00, // :
/*
  0x00, 0x00, 0x56, 0x36, 0x00, 0x00, // ;
  0x00, 0x08, 0x14, 0x22, 0x41, 0x00, // <
  0x00, 0x14, 0x14, 0x14, 0x14, 0x14, // =
  0x00, 0x00, 0x41, 0x22, 0x14, 0x08, // >
  0x00, 0x02, 0x01, 0x51, 0x09, 0x06, // ?
  0x00, 0x32, 0x49, 0x59, 0x51, 0x3E, // @
 */
  0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, // A
  0x00, 0x7F, 0x49, 0x49, 0x49, 0x36, // B
  0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, // C
  0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, // D
  0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, // E
  0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, // F
  0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, // G
  0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // H
  0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, // I
  0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, // J
  0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, // K
  0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, // L
  0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, // M
  0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, // N
  0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // O
  0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, // P
  0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q
  0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, // R
  0x00, 0x46, 0x49, 0x49, 0x49, 0x31, // S
  0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, // T
  0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, // U
  0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F, // V
  0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, // W
/*
  0x00, 0x63, 0x14, 0x08, 0x14, 0x63, // X
  0x00, 0x07, 0x08, 0x70, 0x08, 0x07, // Y
  0x00, 0x61, 0x51, 0x49, 0x45, 0x43, // Z
  0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // [
  0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55, // 55
  0x00, 0x00, 0x41, 0x41, 0x7F, 0x00, // ]
  0x00, 0x04, 0x02, 0x01, 0x02, 0x04, // ^
  0x00, 0x40, 0x40, 0x40, 0x40, 0x40, // _
  0x00, 0x00, 0x01, 0x02, 0x04, 0x00, // '
*/
  0x00, 0x20, 0x54, 0x54, 0x54, 0x78, // a
  0x00, 0x7F, 0x48, 0x44, 0x44, 0x38, // b
  0x00, 0x38, 0x44, 0x44, 0x44, 0x20, // c
  0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, // d
  0x00, 0x38, 0x54, 0x54, 0x54, 0x18, // e
  0x00, 0x08, 0x7E, 0x09, 0x01, 0x02, // f
  0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g
  0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, // h
  0x00, 0x00, 0x44, 0x7D, 0x40, 0x00, // i
  0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, // j
  0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // k
  0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, // l
  0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, // m
  0x00, 0x7C, 0x08, 0x04, 0x04, 0x78, // n
  0x00, 0x38, 0x44, 0x44, 0x44, 0x38, // o
  0x00, 0xFC, 0x24, 0x24, 0x24, 0x18, // p
  0x00, 0x18, 0x24, 0x24, 0x18, 0xFC, // q
  0x00, 0x7C, 0x08, 0x04, 0x04, 0x08, // r
  0x00, 0x48, 0x54, 0x54, 0x54, 0x20, // s
  0x00, 0x04, 0x3F, 0x44, 0x40, 0x20, // t
  0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C, // u
  0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, // v
  0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w
  0x00, 0x44, 0x28, 0x10, 0x28, 0x44, // x
  0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C,  // y
  0x00, 0x32, 0x49, 0x59, 0x51, 0x3E // @ in place of z
};

// ----------------------------------------------------------------------------



================================================
FILE: Frogger_MAKERbuino/Frogger_MAKERbuino.ino
================================================
/*  2018
 *   
 *  Frogger for MakerBuino and GameBuino by Andy Jackson - Twitter @andyhighnumber
 *  
 *  You need to install the 'Classic' GameBuino libraries in your Arduino IDE, like this:
 *  http://legacy.gamebuino.com/wiki/index.php?title=Getting_started#Install_the_Gamebuino_Library_.28Automatic.29
 *  
 *  Everything you need to build and run this game is contained in this file and the font 
 *  header (font6x8AJ3.h) 
 *  
 *  This is a port of a Frogger clone written for the AttinyArcade, which is why some of
 *  the display routines look a bit weird. More info here:
 *  https://github.com/andyhighnumber/Attiny-Arduino-Games
 *  
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of 
 *  this software and associated documentation files (the "Software"), to deal in the 
 *  Software without restriction, including without limitation the rights to use, copy, 
 *  modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 
 *  and to permit persons to whom the Software is furnished to do so, subject to the 
 *  following conditions: 
 *  
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 
 *  INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 
 *  PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
 *  FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
 *  OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
 *  DEALINGS IN THE SOFTWARE.
 * 
*/

//imports the SPI library (needed to communicate with Gamebuino's screen)
#include <SPI.h>
//imports the Gamebuino library
#include <Gamebuino.h>
//creates a Gamebuino object named gb
Gamebuino gb;


#include <EEPROM.h>
#include "font6x8AJ4.h"


// Uncomment this #define to make the logs smaller (/thinner)
//#define SMALLLOGS

// Make click delay an even number - it gets halved and then used in an integer comparison
#define CLICKDELAY 120 

// The basline speed - higher number is slower
#define MOVEBASE 1000 

#define DIGITAL_WRITE_HIGH(PORT) PORTB |= (1 << PORT)
#define DIGITAL_WRITE_LOW(PORT) PORTB &= ~(1 << PORT)

// Routines to set and clear bits (used in the sleep code)
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

// Defines for OLED output
#define lcdDisplayXLED_H
#define lcdDisplay_SCL   PORTB4  // SCL, Pin 4 on lcdDisplay Board - for webbogles board
#define lcdDisplay_SDA   PORTB3  // SDA, Pin 3 on lcdDisplay Board - for webbogles board
#define lcdDisplay_SA    0x78  // Slave address

// Function prototypes

// Custom draw functions - allow for extra functionality like inverse display
void sendBlock(byte, bool);
void sendByte(byte, bool);

// Other generic functions for games (both originated in code from webboggles.com and the sleep code is by Matthew Little - see above)
void beep(int,int);
void system_sleep(void);
void doNumber (int,int,int);

// Game functions
void playFrogger(void);
void levelUp(int);
void moveBlocks(void);
void initScreen(void);
void drawDocks(void);
void drawLives(void);
void displayTitle(void);
void resetDock(byte);
void checkCollision(void);

// Global variables - yes I know all these global vars is a lazy way to code but it makes it easier to prevent stack overflows when you're working with 512 bytes! 
// Most of these are initialised in the main game function (playFrogger())
int watchDog;             // Counts drawing cycles so I can shut the game down if there's inactivity - battery saver!
boolean stopAnimate;      // this is set to 1 when a collision is detected
int lives;                // Lives in the game - this can go negative to end the game, which is why it's a signed variable  
bool frogDocks[5];        // Tracks which frog docks are full (at the top of the screen)
bool flipFlop;            // Used in routines that flip-flop between two states (left and right)
bool flipFlopShift;       // Same as previous one
byte frogColumn;          // Column location of frog (there are 16 altogether)
byte frogRow;             // Row locaiton of frog (there are 8, but 0 is the frog docks at the top and 7 is the start row)
byte frogLeftLimit;       // Left limit of frog travel on start row (changes as digits in score increases)
byte frogRightLimit;      // Right limit of frog travel on start row (changes as lives decrease as there's then more space)
byte level;               // Level - starts at 1
byte blockShiftL;         // Number of pixels to shift the left-going rows by
byte blockShiftR;         // Number of pixels to shift the right-going rows by
int interimStep;          // Used as timer for incremental movements
int moveDelay;            // How long to wait until the next movement of logs etc - changes as levels increase to make the game go faster
int dockedFrogs;          // How many frogs are in the docks at the top
unsigned long clickBase;  // Timer for debounce
boolean clickLock;        // For debounce routine
int score;                // Obvious I hope
int topScore;             // High score
boolean newHigh;          // Is there a new high score?
boolean mute = 0;         // Mute the speaker
byte grid[6][16];         // Grid for items like logs, crocs, cars and lorries
byte frogMode;            // Represents the frog direction 
bool moveForward=0;       // Captures when the 'forward' button is pressed
bool moveLeft=0;          // Captures when the 'left' button is pressed
bool moveRight=0;         // Captures when the 'right' button is pressed
bool moveBack=0;         // Captures when the 'right' button is pressed

int screenLeft = 0;       // Current left position of the displayed screen
int screenTop = 0;        // Current top of the displayed screen
byte currentX = 0;        // Current X positon to draw
byte currentY = 0;        // Current Y positon to draw

// Bitmaps created by @senkunmusahi using https://www.riyas.org/2013/12/online-led-matrix-font-generator-with.html
static const byte  bitmaps[16][8] PROGMEM = {
// Frogs
  {0x83, 0xDC, 0x7A, 0x3F, 0x3F, 0x7A, 0xDC, 0x83},
  {0x99, 0xBD, 0xDB, 0x7E, 0x7E, 0x3C, 0xE7, 0x81},
  {0x81, 0xE7, 0x3C, 0x7E, 0x7E, 0xDB, 0xBD, 0x99},
  {0xC1, 0x3B, 0x5E, 0xFC, 0xFC, 0x5E, 0x3B, 0xC1},

#ifdef SMALLLOGS
// Small logs
  {0x1C, 0x22, 0x41, 0x55, 0x55, 0x51, 0x43, 0x61},
  {0x69, 0x6B, 0x43, 0x61, 0x45, 0x45, 0x61, 0x65},
  {0x45, 0x55, 0x41, 0x5D, 0x63, 0x5D, 0x22, 0x1C},
#else
// Bigger logs
  {0x3C, 0x7E, 0xD7, 0xB5, 0xAD, 0xBF, 0xFF, 0xED}, 
  {0xAD, 0xAD, 0xFF, 0xB7, 0xF5, 0xBF, 0xB7, 0xAD}, 
  {0xED, 0xBD, 0xC3, 0xBD, 0xA5, 0xBD, 0x42, 0x3C},  
#endif

// Trucks
  {0x00, 0x7F, 0x41, 0x55, 0x55, 0x55, 0x55, 0x55},
  {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, 
  {0x41, 0x7F, 0x22, 0x7F, 0x7F, 0x63, 0x22, 0x1C},

// Crocs
  {0x41, 0x63, 0x46, 0x6E, 0x7C, 0x7E, 0x7A, 0x3E},
  {0xBC, 0xFE, 0x7E, 0x3E, 0xBE, 0xBE, 0xFC, 0x7C},  
  {0x78, 0x38, 0x38, 0x38, 0x70, 0x60, 0x60, 0x40},

// Cars
  {0x00, 0x1C, 0x22, 0x63, 0x7F, 0x7F, 0x22, 0x22},
  {0x22, 0x3E, 0x3E, 0x7F, 0x63, 0x63, 0x22, 0x1C},
  {0x22, 0x3E, 0x3E, 0x7F, 0x63, 0x63, 0x22, 0x1C}
  };

// Opening artwork created by @senkunmusahi using https://www.riyas.org/2013/12/online-led-matrix-font-generator-with.html
static const byte titleBmp[] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0xF0, 0x7C, 0x06, 0x73, 0x59, 0x43,
0x06, 0x3C, 0x38, 0x30, 0x30, 0x38, 0x3E, 0x26, 0x7B, 0x59, 0x43, 0x06, 0x7C, 0xF0, 0xC0, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xFF,
0xCF, 0x01, 0x00, 0x00, 0x30, 0x60, 0xE0, 0xC0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xC0,
0xC0, 0x60, 0x30, 0x00, 0x00, 0x00, 0x01, 0xCF, 0xFE, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7C, 0xFE, 0x86, 0x0E, 0x0E, 0x1C, 0x18, 0x31, 0x7F, 0xFE, 0xFC, 0x1C, 0x18, 0x38, 0x38, 0x38,
0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x18, 0x1C, 0xFC, 0xFE, 0x7F,
0x39, 0x18, 0x1C, 0x0E, 0x0E, 0xC6, 0xFE, 0x3C, 0x00, 0x01, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xC0,
0xC0, 0x80, 0x03, 0x07, 0xFC, 0xF8, 0x00, 0x00, 0xF0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xE0, 0xF0,
0x00, 0x00, 0xF8, 0xFC, 0x0F, 0x03, 0x80, 0xC0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x03, 0x01, 0x00,
0x04, 0x06, 0x0F, 0x0F, 0x06, 0x06, 0x03, 0x03, 0x03, 0x63, 0x73, 0x33, 0x3B, 0xFF, 0xFF, 0x7F,
0x3F, 0x38, 0xF0, 0xC0, 0x00, 0xF0, 0xF8, 0x3F, 0x7F, 0xFF, 0xFF, 0x3B, 0x33, 0x63, 0x63, 0x03,
0x03, 0x03, 0x06, 0x06, 0x0F, 0x0F, 0x06, 0x00
};


// Opening artwork created by @senkunmusahi using https://www.riyas.org/2013/12/online-led-matrix-font-generator-with.html
const byte PROGMEM openScreen[] = {
    57,35,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x70,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x3e,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x0f,0xe0,0x00,0x00,0x00,0x00,
    0x00,0x00,0x07,0xfc,0x00,0x00,0x00,0x00,
    0x00,0x00,0x03,0xff,0xe0,0x00,0x00,0x00,
    0x00,0x00,0xff,0xff,0xf8,0x00,0x00,0x00,
    0x00,0x00,0x7f,0xff,0xfc,0x00,0x00,0x00,
    0x00,0x00,0x1f,0xff,0xe0,0x00,0x00,0x00,
    0x00,0x80,0x07,0xff,0xfc,0x20,0x00,0x00,
    0x00,0xe0,0x0f,0xff,0xfc,0x30,0x00,0x00,
    0x00,0xf0,0x00,0x7f,0xfe,0x30,0x00,0x00,
    0x00,0x78,0x00,0x7f,0xbf,0x78,0x00,0x00,
    0x00,0x0b,0xf0,0xff,0x7f,0x78,0x00,0x00,
    0x00,0x3b,0xff,0xfe,0xff,0x78,0x00,0x00,
    0x00,0xff,0xff,0xfd,0xff,0xfc,0x00,0x00,
    0x00,0x0e,0x8f,0xfb,0xff,0xfc,0x00,0x00,
    0x00,0x09,0x87,0xf7,0xff,0xfe,0x00,0x00,
    0x00,0x7a,0x37,0xff,0xff,0xfe,0x00,0x00,
    0x00,0x62,0x37,0xff,0xff,0xff,0x00,0x00,
    0x00,0xc2,0x07,0xff,0xff,0xff,0x00,0x00,
    0x00,0x02,0x07,0xff,0xff,0xff,0x80,0x00,
    0x00,0x02,0x0f,0xff,0xfb,0xff,0xc0,0x00,
    0x00,0x01,0x0f,0x0f,0xf1,0xff,0xc0,0x00,
    0x00,0x00,0x9e,0x07,0xe0,0xff,0xe0,0x00,
    0x00,0x00,0x7c,0x63,0xe0,0xff,0xe0,0x00,
    0x00,0x00,0x78,0x63,0xf0,0xde,0xf0,0x00,
    0x00,0x00,0x78,0x01,0xf0,0xde,0x30,0x00,
    0x00,0x00,0x7c,0x01,0x78,0x8e,0x10,0x00,
    0x00,0x00,0x3e,0x02,0x38,0x0e,0x00,0x00,
    0x00,0x00,0x02,0x02,0x7e,0x06,0x00,0x00,
    0x00,0x00,0x03,0x0c,0x7f,0x02,0x00,0x00,
    0x00,0x00,0x00,0xf0,0xc9,0x02,0x00,0x00,
    0x00,0x00,0x00,0x01,0xcd,0x80,0x00,0x00
  };
  
// Display functions - a legacy from the AttinyArcade version - could be replaced with GameBuino function calls if you wish
void lcdDisplay_send_byte(uint8_t byte);
void lcdDisplay_setpos(uint8_t x, uint8_t y);
void lcdDisplay_fillscreen(uint8_t fill_Data);
void lcdDisplay_char_f6x8(uint8_t x, uint8_t y, const char ch[]);

// Other generic functions for games (both originated in code from webboggles.com)
void doNumber (int, int, int);
void beep(int,int);

void displayTitle(void) {
  int incr = 0;
  for(int lxn = 0; lxn < 5; lxn++) {
    lcdDisplay_setpos(84,lxn); 
    for(int lxn2 = 0; lxn2 < 40; lxn2++) {
      lcdDisplay_send_byte(pgm_read_byte(&titleBmp[incr]));      
      incr++;
    }
  }
}

void beep(int bCount,int bDelay){
  if (mute) return;
  for (int i = 0; i<=bCount; i++){
    digitalWrite(3,HIGH);
    for(int i2=0; i2<bDelay; i2++){
      __asm__("nop\n\t"); // 62.5ns delay @ 16MHz
      }
    digitalWrite(3,LOW);
    for(int i2=0; i2<bDelay; i2++) {
      __asm__("nop\n\t"); // 62.5ns delay @ 16Mhz
    }
  }
}

// Arduino stuff - setup
void setup() {
  // initialize the Gamebuino object
  gb.begin();
  //display the main menu:
  //gb.titleScreen(F("FROGGER"),openScreen);
  gb.titleScreen(openScreen);
}

void displayOpenScreen(int incr) {
    lcdDisplay_fillscreen(1);
    if (incr < 99) screenLeft = incr;
    
    lcdDisplay_char_f6x8(0, 1, "F R O G G E R");
    lcdDisplay_char_f6x8(0, 3, "andy jackson");
    lcdDisplay_char_f6x8(64, 5, "Press A...");

    lcdDisplay_setpos(0, 0);
    for (int incr2 = 0; incr2 < 76; incr2++) {
      lcdDisplay_send_byte(B00111000);
    }
    
    lcdDisplay_setpos(0, 2);
    for (int incr2 = 0; incr2 < 76; incr2++) {
      lcdDisplay_send_byte(B00011100);
    }

    displayTitle();
}

// Arduino stuff - loop
void loop() {
  lcdDisplay_fillscreen(1);
  int sChange = 0;

  screenLeft = 0;
  screenTop = 0;
  
  if (gb.buttons.pressed(BTN_B) == true) {
      sChange = 1;     
      EEPROM.write(0,0);
      EEPROM.write(1,0);
      lcdDisplay_char_f6x8(0, 0, "--------------");
      lcdDisplay_char_f6x8(0, 1, "HI SCORE RESET");
      lcdDisplay_char_f6x8(0, 3, "--------------");
      while(!gb.update());
      delay(2000);
  }
  
  if (sChange == 0) {

    lcdDisplay_fillscreen(1);

    playFrogger();

    screenLeft = 0;
    screenTop = 0;

    topScore = EEPROM.read(0);
    topScore = topScore << 8;
    topScore = topScore |  EEPROM.read(1);

    newHigh = 0;
    if (score > topScore) {
      topScore = score;
      EEPROM.write(1, score & 0xFF);
      EEPROM.write(0, (score >> 8) & 0xFF);
      newHigh = 1;
    }

    screenLeft = 0;

    lcdDisplay_fillscreen(0x00);
    lcdDisplay_char_f6x8(0, 0, "------------");
    lcdDisplay_char_f6x8(0, 1, "GAME  OVER");
    lcdDisplay_char_f6x8(0, 3, "------------");
    showScore();
    while(!gb.update());
    delay(2500);
    lcdDisplay_fillscreen(0x00);
    lcdDisplay_char_f6x8(0, 0, "---------------");
    lcdDisplay_char_f6x8(0, 3, "---------------");
    doNumber(24, 2, topScore);
    if (!newHigh) {
      lcdDisplay_char_f6x8(0, 1, "HIGH SCORE:");
    } else {
      lcdDisplay_char_f6x8(0, 1, "NEW HIGH:");
      for (int i = 700; i>200; i = i - 50){
        beep(30,i);
      }
    }
    while(!gb.update());
    delay(2500);
  }

  lcdDisplay_fillscreen(1);
  gb.display.update();

  while(gb.buttons.pressed(BTN_A) == true) { while(!gb.update()); delay(5);}

  gb.sound.playNote(63,1,0);
  while(!gb.update());
  
  screenLeft = 0;
  for (int incr = 0; incr < 46 ; incr+=3) {
    displayOpenScreen(incr);
    gb.display.update();
    if (incr == 0) delay(1700);
  }

  while(gb.buttons.pressed(BTN_A) == false && gb.buttons.pressed(BTN_B) == false ) {
    displayOpenScreen(100);    
    while(!gb.update());
  }

}

void doNumber (int x, int y, int value) {
  char temp[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  itoa(value, temp, 10);
  lcdDisplay_char_f6x8(x, y, temp);
}

void showScore(void) {
  lcdDisplay_char_f6x8(0, 2, "SCORE:");
  doNumber(44, 2, score);
}

void lcdDisplay_send_byte(uint8_t input) {
  uint8_t *disp;

  disp = gb.display.getBuffer();

  if ( (currentX >= screenLeft) && (currentX < screenLeft + 84) && (currentY >= screenTop) && (currentY <= screenTop + 5)) {
    *(disp + currentX - screenLeft + ((currentY - screenTop) * 84)) = input;
  }
  currentX++;
}

void lcdDisplay_setpos(uint8_t x, uint8_t y)
{
  currentX = x;
  currentY = y;
}

void lcdDisplay_fillscreen(uint8_t fill_Data) {
  gb.display.setColor(WHITE, BLACK);

  for (int i = 0; i < 64; i++) {
    for (int j = 0; j < 128; j++) {
      gb.display.drawPixel(j, i);
    }
  }

}

void lcdDisplay_char_f6x8(uint8_t x, uint8_t y, const char ch[]) {
  uint8_t c, i, j = 0;
  while (ch[j] != '\0')
  {
    c = ch[j] - 32;
    if (c > 0) c = c - 12;
    if (c > 15) c = c - 6;
    if (c > 40) c = c - 9;
    if (x > 126)
    {
      x = 0;
      y++;
    }
    lcdDisplay_setpos(x, y);
    for (i = 0; i < 6; i++)
    {
      lcdDisplay_send_byte(pgm_read_byte(&lcdDisplayxled_font6x8[c * 6 + i]));
    }
    x += 6;
    j++;
  }
}

/* ------------------------
 *  Frogger main game code
 */
void playFrogger(){
  stopAnimate = 0;
  score = 0;
  moveDelay = MOVEBASE;
  level = 1;
  frogColumn = 8;
  frogRow = 7;
  clickLock = 0;
  frogMode = 1;
  interimStep =0;
  blockShiftL = 0;
  blockShiftR = 0;
  flipFlop = 1;
  flipFlopShift = 1;
  dockedFrogs = 0;
  lives = 2;
  frogRightLimit = 12;
  watchDog = 1; // we use this to see if there's been movement - it's only ever zero when the frog has just moved!

  initScreen();
  resetDock(0);

  drawFrog(frogMode,0);
  drawLives();
  
  doNumber(0,7,score);

  drawGameScreen(frogMode);
  drawDocks();

  screenLeft = 0;
  screenTop = 0;
  
  while(!gb.update());
  while (lives >= 0) {
    drawFrog(frogMode,0);
    drawLives();
    doNumber(0,7,score);  
    drawGameScreen(frogMode);
    drawDocks();
    
  
    if (frogColumn*8 < screenLeft + 40) screenLeft--;
    if (frogColumn*8 > screenLeft + 50) screenLeft++;
    if (frogColumn*8 < screenLeft + 15) screenLeft--;
    if (frogColumn*8 > screenLeft + 60) screenLeft++;

    /*
    if (frogRow == 1 || frogRow == 3) {
      screenLeft = frogColumn*8 - 30 - blockShiftL;
    } else if (frogRow == 2) {
      screenLeft = frogColumn*8 - 30 + blockShiftR;
    } else {
      screenLeft = frogColumn*8 - 30;
    }
    */
    
    screenTop =  frogRow - 3;

    if (screenLeft < 0) screenLeft = 0;
    if (screenTop < 0) screenTop = 0;
    if (screenLeft > 43) screenLeft = 43;
    if (screenTop > 2) screenTop = 2;
    
    while(!gb.update());
    interimStep++;

    if (watchDog >= 500) lives = -1; // Stop the game if nothing's happening - maybe triggered in someone's pocket so this is to save battery!
    
    // Calculate left limit of frog movement so it doesn't hit the score
    frogLeftLimit = 1;
    if ((score / 10) % 10 != 0) frogLeftLimit++; 
    if ((score / 100) % 10 != 0) frogLeftLimit++;
    if ((score / 1000) % 10 != 0) frogLeftLimit++;

    // Move stuff along if it's time to
    //if (interimStep > moveDelay/8) {
      watchDog++;
      blockShiftL++;
      if (flipFlopShift == 1) flipFlopShift = 0; else flipFlopShift = 1;
      if (flipFlopShift == 1) blockShiftR++;
      if (blockShiftL == 7) {
        moveBlocks();
        blockShiftL = 0;
      }
      if (blockShiftR == 7) {
        blockShiftR = 0;
      }
      interimStep = 0;
      checkCollision();
      /*
      if (stopAnimate == 0) {
        drawGameScreen(frogMode);
        drawFrog(frogMode,0);
      }
      */
    //}

    // Handle input 
    if (gb.buttons.pressed(BTN_UP) == true && clickLock == 0) {
      moveForward = 1;
      watchDog = 0;   // reset the watchdog so the game doesn't end!
      clickLock = 1;
      clickBase = millis();
    }

    if (gb.buttons.pressed(BTN_DOWN) == true && clickLock == 0) {
      moveBack = 1;
      watchDog = 0;   // reset the watchdog so the game doesn't end!
      clickLock = 1;
      clickBase = millis();
    }

    if (gb.buttons.pressed(BTN_LEFT) == true && clickLock == 0) {
      moveLeft = 1;
      watchDog = 0;   // reset the watchdog so the game doesn't end!
      clickLock = 1;
      //clickBase = millis();
    }

    if (gb.buttons.pressed(BTN_RIGHT) == true && clickLock == 0) {
      moveRight = 1;
      watchDog = 0;   // reset the watchdog so the game doesn't end!
      clickLock = 1;
      //clickBase = millis();
    }

    // Handle moving left
//    if(moveLeft == 1 && millis() > clickBase + CLICKDELAY/2) {
    if(moveLeft == 1) {
      watchDog = 0;   // reset the watchdog so the game doesn't end!
      moveLeft = 0;
      drawFrog(0,0);  // delete the frog
      // move the frog, checking it isn't jumping off the edge of the screen
      if ((frogRow == 7 && frogColumn > frogLeftLimit) || (frogRow < 7 && frogColumn > 0)) {
        frogColumn --;
      } else if (frogRow < 7) stopAnimate = 1;
      frogMode = 2; // pointing left
    }

    // Handle moving right
//    if(moveRight == 1 && millis() > clickBase + CLICKDELAY/2){
    if(moveRight == 1){
      watchDog = 0;   // reset the watchdog so the game doesn't end!
      moveRight = 0;
      drawFrog(0,0); // delete the frog
      // move the frog, checking it isn't jumping off the edge of the screen
      if ((frogRow == 7 && frogColumn < frogRightLimit) || (frogRow < 7 && frogColumn < 14)) {
        frogColumn ++;
      } else if (frogRow < 7) stopAnimate = 1;
      frogMode = 3; // pointing right    
    }


    // Handle 'move back' button press
    if (moveBack == 1) {
      moveBack = 0;
      if (frogRow < 7) score-= level;          // decrement the score for every move back
      if (frogRow < 7) {
        // Correct for the skew in frog position created by the blockShift scrolling parameter
        if (frogRow == 3 && blockShiftL < 4) frogColumn++;
        if (frogRow == 2 && blockShiftR + blockShiftL < 5) frogColumn--;
        if (frogRow == 1 && blockShiftR + blockShiftL < 5) frogColumn++;
        frogRow++; 
        frogMode = 4;             // mode 1 = forwards position              
      }
    }

    // Handle 'move forward' button press
    if (moveForward == 1) {
      moveForward = 0;
  
      score+= level;          // increment the score for every move
      doNumber(0,7,score);    // display new score
      drawFrog(0,0);          // delete the frog
  
      if (frogRow > 1) {
        frogRow--; 
        // Correct for the skew in frog position created by the blockShift scrolling parameter
        if (frogRow == 3 && blockShiftL < 4) frogColumn--;
        if (frogRow == 2 && blockShiftR + blockShiftL < 5) frogColumn++;
        if (frogRow == 1 && blockShiftR + blockShiftL < 5) frogColumn--;
                
      } else {
        // frog is at the docks!
        if (blockShiftL < 4 && frogColumn <15) frogColumn++;  // account for skew due to block shifting
        byte dockPos = (byte)floor(frogColumn/3);
        if (frogDocks[dockPos] == 0 ) {
          dockedFrogs++;          
          frogDocks[dockPos] = 1;                             // assign this dock as filled
          frogRow = 7;                                        // reposition the frog at the start
          frogColumn = 8;
          drawFrog(frogMode,0);
          drawLives();
          doNumber(0,7,score);  
          drawGameScreen(frogMode);
          drawDocks();
          while(!gb.update());
          for (int i = 1000; i>200; i = i - 100){             // make sound
          beep(10,i);
          }
          delay(600);
        } else stopAnimate = 1;
      }
      frogMode = 1;             // mode 1 = forwards position

      // check if all docks are full - if so, then level up!
      if (dockedFrogs >= 5) {
        level++;
        levelUp(level);
        if (moveDelay > 99) moveDelay -=100; // make the game speed up 
        initScreen();                        // reinitalise the position of game items
        resetDock(0);                        // reinitliase the dock
        dockedFrogs = 0;              
        drawDocks();                         // display the (now empty) docks
        drawLives();                         // display the lives
        doNumber(0,7,score);                 // display the score
      }      
    }

    // The frog has moved 
    if (watchDog == 0 && stopAnimate == 0) {
      watchDog = 1;               // set to something other than zero so this routine doesn't run again
      // redraw the screen
      drawGameScreen(frogMode);
      // redraw the frog
      drawFrog(frogMode,0);
      // make jump sound
      beep(30,400);
      beep(30,300);
      beep(30,200);
    }
    
    checkCollision();
    
    //if (clickLock == 1 && millis() > clickBase + CLICKDELAY && digitalRead(2)==0 && digitalRead(0)==0 && analogRead(0) > 940) clickLock = 0; // normal debounce
    clickLock = 0; // No Debounce

    // check to see if the frog has been killed
    if (stopAnimate != 0) {
      // redraw the screen
      drawGameScreen(frogMode);
      // animation for frog death
      drawGameScreen(frogMode);
      drawDocks();
      drawFrog(0,1);
      while(!gb.update());
      for (int i = 0; i<250; i = i+ 50){  
        beep(50,i);
      }
      drawGameScreen(frogMode);
      drawDocks();
      drawFrog(frogMode,1);    
      for (int i = 250; i<500; i = i+ 50){  
        beep(50,i);
      }
      drawGameScreen(frogMode);
      drawDocks();
      drawFrog(0,1);
      while(!gb.update());
      for (int i = 500; i<750; i = i+ 50){  
        beep(50,i);
      }
      drawGameScreen(frogMode);
      drawDocks();
      drawFrog(frogMode,1);
      for (int i = 750; i<1000; i = i+ 50){  
        beep(50,i);
      }
      while(!gb.update());
      delay(600);
      lives--;          // increment the score for every move
      //frogRightLimit++; // there's one less frog drawn on right so you can move a bit further across (if you really want to!)
      stopAnimate = 0;  // reset parameter
      drawLives();      // display number of lives left
      frogColumn = 8;   // reinitalise frog location
      frogRow = 7;
      while(!gb.update());
      }
  }  // Big while loop (main game loop) goes until lives is negative
} 

void checkCollision(void) {
  if (frogRow > 0 && frogRow < 4 && grid[frogRow-1][frogColumn] == 0) stopAnimate = 1; // the frog has fallen in the river
  if (frogRow > 0 && frogRow < 4 && grid[frogRow-1][frogColumn] > 9) stopAnimate = 1; // the frog has stepped on a croc
  if ((frogRow < 7 && frogRow > 3) && (grid[frogRow-1][frogColumn] != 0 || grid[frogRow-1][frogColumn-1] != 0)) stopAnimate = 1; // the frog has been hit by a vehicle
}

// Initialise all the moving objects on the game screen
void initScreen(void) {
  int initCounter[6] = {3,2,4,2,2,3};       // the length of the objects on each row - doesn't change
  int gapCounter[6] = {-2,-3,-4,-4,-3,-5};  // the gaps between objects - change with levels to make it harder as you go thru the game
  int counter[6];                           // used to hold the gap data
  byte stepMode = 0;                        // which component of the object are we drawing (they all have three - a start a middle and an end)
  byte stepShift = 0;                       // offset to shift up to the different objects in the array
  byte crocStartColumn = 0;                 // column at which to stop drawing crocs - is zero at start hence no crocs!

  
  // Adjust difficulty by changing gaps between objects according to level
  if (level == 1) {
    gapCounter[5] = -14;           // easiset setting, for start of game
  }
  if (level < 3) {
    gapCounter[4] = -6;            // make it easier for levels less than 3 by increasing the gap in the cars on this row
  }
  if (level < 4) {
    gapCounter[3] = -7;
  }
  if (level > 4) {
    for (byte incr = 1; incr < 3; incr++) {
      gapCounter[incr]--;         // increase the gaps between the logs for levels over 4  
    }
  }
  if (level > 7) {                // set smaller gaps between cars for levels over 7
    gapCounter[3] = -4; 
    gapCounter[4] = -2;
    gapCounter[5] = -3;
  }
  if (level > 2) crocStartColumn = 5; // one croc appears at level 3 and above
  if (level > 6) crocStartColumn = 9; // two croc appear at level 7 and above
  
  // Initialise the counters
  for (byte incr = 0; incr < 6;incr++) counter[incr] = initCounter[incr];  

  // Initialise array with zeros
  for (byte col = 0; col < 16; col++) {
    for (byte row = 0; row < 6; row++) {
      grid[row][col] = 0;
    }
  }
       
  stepMode = 0;
  // Initialise array with obstacles
  for (byte row = 0; row < 6; row++) {
    for (byte col = 0; col < 15; col++) {         
      if (counter[row] > 0) {
        if (14-row > counter[row]) {
          if (counter[row] == 1) if (stepMode == 1) stepMode = 2; // the next space is blank and we are drawing the middle - draw the end! 
          if (row > 2) stepShift = 3; else stepShift = 0;         // shift up to the trucks in the array
          if (row == 4) stepShift = 9;                            // shift up to the cars in the array - also theres no middle
          
          if (row > 0) {
            grid[row][col] = 5+stepMode+stepShift;                // if you are on any row but the first - draw whatever is appropriate from the bitmaps
          } else if (col >= crocStartColumn) {                    
            grid[row][col] = 5+stepMode+stepShift;                // if you're on row zero (top row of logs) and you are above where crocs should be drawm, draw logs ...
          } else grid[row][col] = 11+stepMode;                    // .. otherwise draw crocs
          if (stepMode == 0) stepMode = 1;                        // we've drawn the left side now switch to central sections
          if (stepMode == 2) stepMode = 0;                        // we've drawn the end, now reset
        }
      } 
      counter[row]--;                                             // decrement the counter
      if (counter[row] <= gapCounter[row]) {
        counter[row] = initCounter[row];                          // if we have gone negative enough to account for the gaps - reset the counter and start again
      }
    }
  }
}

// Display the frog
void drawFrog(byte mode, bool frogDead) {
  if (frogRow > 6 || frogRow < 1 || frogDead == 1) {           // don't draw the frog when it's on the road or on logs - because they are moving, that's handled in the main drawing routine below- exception is when you are animating frog death
    if (frogRow == 1 || frogRow == 3) {                        // these allow for the blocks being shifted when animating the frog death on rows with logs
      lcdDisplay_setpos(frogColumn*8 + 7 - blockShiftL,frogRow);
    } else if (frogRow == 2) {
      lcdDisplay_setpos(frogColumn*8 + blockShiftR,frogRow);      
    } else {
      lcdDisplay_setpos(frogColumn*8,frogRow); 
    }
    sendBlock(mode,0);                   // draw the frog - mode is direction
  }    
}

// Display the frog and all the moving items on the screen
void drawGameScreen(byte mode) {
  bool inverse = 0;
  
  // Draw objects going left
  for (byte row = 0; row < 6; row+=2) {
    if (row >=0 && row < 3) inverse = 1; else inverse = 0;                              // draw everything (except the frog) in inverse video on the river rows (0,1,2)
    lcdDisplay_setpos(0,row+1);                                                            // +1 because row 0 here is actually row 1 on the screen
    for (byte incr = 0; incr < 7-blockShiftL; incr++) if (grid[row][15] == 0) {         // cover the tiny bit to the far left of the screen up to wherever the main blocks will be drawn (depends on how far they are shifted)
      sendByte(0,inverse);                                                              // draw an empty 8-bit line if there's nothing wrapping around
    } else {
      sendByte(pgm_read_byte(&bitmaps[grid[row][15]-1][1+blockShiftL+incr]), inverse);  // pick the correct bit of whatever is wrapping from the right of the screen
    }
    for (byte col = 0; col < 15; col++) {         
      if (frogRow == row+1 && frogColumn == col && frogRow < 4 && frogRow > 0) {
        sendBlock(mode,0);                                                                          // if we are in a location with the frog, and it's on the logs, draw it - never invert it (hence zero as second parameter here)
      } else if (stopAnimate == 0 && frogRow == row+1 && frogColumn == col + 1 && frogRow > 3 && frogRow < 7) {         // frog is amongst the cars and needs drawing
        for (byte incr = 0; incr < blockShiftL; incr++) sendByte(0,0);                              // draw the blank space up to the frog
        sendBlock(mode,0);                                                                          // draw frog
        for (byte incr = 0; incr < 7-blockShiftL; incr++) sendByte(0,0);                            // draw the blank space after the frog
        col++;                                                                                      // we've now drawn two columns so increment
      } else {
        sendBlock(grid[row][col],inverse);                                                          // draw the correct object for this space - it's not a frog ;)
      }
    }
    // fill in the bit to the right of the main blocks 
    for (byte incr = 0; incr < blockShiftL; incr++) if (grid[row][15] == 0) sendByte(0,inverse); else sendByte(pgm_read_byte(&bitmaps[grid[row][15]-1][incr]),inverse);

  }
  if (frogColumn == 0) drawFrog(mode,1); // this covers the exceptional case where the frog is in the far left colum, in which case the normal routine can't draw it when it's on the road
  
  // Draw objects going right - see comments above, works in basically the same way
  for (byte row = 1; row < 6; row+=2) {
    if (row > 0 && row < 3) inverse = 1; else inverse = 0;
    lcdDisplay_setpos(0,row+1);
    for (byte incr = 0; incr < blockShiftR; incr++) if (grid[row][15] == 0) sendByte(0, inverse); else sendByte(pgm_read_byte(&bitmaps[grid[row][15]-1][incr+(8-blockShiftR)]),inverse);
    for (byte col = 0; col < 15; col++) {         
      if (frogRow == row+1 && frogColumn == col && frogRow < 4 && frogRow > 0) {
        sendBlock(mode,0);    
      } else if (stopAnimate == 0 && frogRow == row+1 && frogColumn == col + 1 && frogRow > 3 && frogRow < 7) {  
        for (byte incr = 0; incr < 7-blockShiftR; incr++) sendByte(0,0);
        sendBlock(mode,0); // draw frog
        for (byte incr = 0; incr < blockShiftR; incr++) sendByte(0,0);
        col++;        
      } else {
        sendBlock(grid[row][col],inverse);        
      }
    }
    for (byte incr = 0; incr < 7-blockShiftR; incr++) if (grid[row][15] == 0) sendByte(0,inverse); else sendByte(pgm_read_byte(&bitmaps[grid[row][15]-1][incr]),inverse);
  }
  if (frogColumn == 0) drawFrog(mode,1);
}

// Send one byte to the screen
void sendByte(byte fill, bool inverse) {
  if (inverse == 0) lcdDisplay_send_byte(fill); else lcdDisplay_send_byte(~fill);
}

// Send one block of 8 bytes to the screen - inverse means inverse video, for the river section
void sendBlock(byte fill, bool inverse){
  for (int incr = 0; incr < 8; incr++) {
    if (fill > 0) {
      if (inverse == 0) lcdDisplay_send_byte(pgm_read_byte(&bitmaps[fill-1][incr])); else lcdDisplay_send_byte(~pgm_read_byte(&bitmaps[fill-1][incr]));  
    } else if (inverse ==0) lcdDisplay_send_byte(0); else lcdDisplay_send_byte(0xFF); 
  }
}

// Draw the frog lives (in the right hand corner)
void drawLives(void) {
  byte tempRow = frogColumn;
  byte tempCol = frogRow;
  frogRow = 7;

  for (int incr = 2; incr > 0; incr--) {
    frogColumn = 15-incr;
    drawFrog(0,1);
  }

  for (int incr = lives; incr > 0; incr--) {
    frogColumn = 15-incr;
    drawFrog(1,1);
  }
  frogRow = tempCol;
  frogColumn = tempRow;    
}

// Draw the docks for the frog to land in at top of screen
void drawDocks(void) {
  byte drawPos = 3;
  for (byte incr = 0; incr < 5; incr++) {
    lcdDisplay_setpos(drawPos,0);
    lcdDisplay_send_byte(B11111111);
    lcdDisplay_send_byte(B00000001);
    lcdDisplay_send_byte(B00000001);
    if (frogDocks[incr] == 1) sendBlock(1,0); else for(byte lxn = 0; lxn < 8; lxn++) lcdDisplay_send_byte(B00000001);
    lcdDisplay_send_byte(B00000001);
    lcdDisplay_send_byte(B00000001);
    lcdDisplay_send_byte(B11111111);
    drawPos+= 24;
    }    
}

// Set all the frog docks to a single value
void resetDock(byte value) {   for (byte incr = 0; incr < 5;incr++) frogDocks[incr] = value; }

// Handle what happens at the end of a level
void levelUp(int number) {  
  // Flash the frog docks
  screenLeft = 0;
  screenTop = 0;

  delay(200);
  for (byte incr = 0; incr < 5; incr ++) {
    resetDock(0);
    drawDocks();
    drawGameScreen(frogMode);
    while(!gb.update());
    for (int i = 800; i>200; i = i - 200){
    beep(20,i);
    }
    resetDock(1);
    drawDocks();
    drawGameScreen(frogMode);
    while(!gb.update());
    for (int i = 800; i>200; i = i - 200){
    beep(20,i);
    }
  }  
  delay(500);
  lcdDisplay_fillscreen(0x00);  
  lcdDisplay_char_f6x8(14, 1,  "---------");
  lcdDisplay_char_f6x8(14, 2, "  LEVEL  ");
  lcdDisplay_char_f6x8(14, 4, "---------");
  doNumber(32, 3, level);
  while(!gb.update());
  delay(1500);    
  lcdDisplay_fillscreen(0x00);
  while(!gb.update());
}

// Move all the items on the game screen (wrapping at the ends) and check for frog dropping off the end of the screen
void moveBlocks(void) {
  int direct = 0;

  if (flipFlop == 1) flipFlop = 0; else flipFlop = 1;
  
  for (byte row = 0; row < 6; row++) {
    // Move the frog along and check to see whether it's gone off the screen, in which case it dies
    if (frogRow < 4 && frogRow > 0) { 
      if (frogRow == row + 1) {
        if (direct == 1 && flipFlop == 1) {
          if (frogColumn >= 14) stopAnimate = 1; else frogColumn++; 
        } else if (direct == 0) {
          if (frogColumn < 1) stopAnimate = 1; else frogColumn--;
        }
      }  
    }
    if (direct == 0) { // move left
      byte temp = grid[row][0];
      for (byte col = 0; col < 15; col++) {         
        grid[row][col] = grid[row][col+1];
      }
      grid[row][15] = temp; // wrap around
      direct = 1;
    } else { // move right
      if (flipFlop == 1) {
        byte temp = grid[row][15];
        for (byte col = 15; col > 0; col--) {         
          grid[row][col] = grid[row][col-1];
          }
        grid[row][0] = temp; // wrap around
        }
      direct = 0;  
      }
    }
  }


================================================
FILE: Frogger_MAKERbuino/HEX and INF/FROGGER.HEX
================================================
:100000000C94C1030C94E9030C94E9030C94E903E8
:100010000C94E9030C94E9030C94E9030C94E903B0
:100020000C94E9030C94E9030C94E9030C944A063C
:100030000C94E9030C94E9030C94E9030C94E90390
:100040000C94F5050C94E9030C94E9030C94E90372
:100050000C94E9030C94E9030C94E9030C94E90370
:100060000C94E9030C94E9030101FF030101F70378
:100070006C00680000000000240027002A00000037
:100080000000250028002B000500893008023802F6
:100090004978681400000500380168010000175312
:1000A00044001613780016131400151020001520B4
:1000B0001000540A03E181867F3E186D830F0F0EF6
:1000C0000183866033186DC3198F1803C38E6033A4
:1000D000186DE330CF1803C39E603630CDE360CF98
:1000E0003006C79E7E3E30CDB360CF300CC6F66082
:1000F0003330D9B660CF33CFCCF66061B0DB1E6051
:10010000CF30D86CE6C061B0DB1E618F19986CC629
:10011000C063B19B0E330F0FB078C6FF7E1F1B0666
:100120001E0F003923000000000000000000000046
:100130000000000000000070000000000000003E11
:10014000000000000000000FE000000000000007B9
:10015000FC00000000000003FFE00000000000FFC2
:10016000FFF800000000007FFFFC00000000001FFF
:10017000FFE0000000008007FFFC20000000E00F0F
:10018000FFFC30000000F0007FFE3000000078002F
:100190007FBF780000000BF0FF7F780000003BFF7E
:1001A000FEFF78000000FFFFFDFFFC0000000E8F47
:1001B000FBFFFC0000000987F7FFFE0000007A3714
:1001C000FFFFFE0000006237FFFFFF000000C207D4
:1001D000FFFFFF0000000207FFFFFF800000020F8B
:1001E000FFFBFFC00000010F0FF1FFC00000009EE9
:1001F00007E0FFE00000007C63E0FFE00000007823
:1002000063F0DEF00000007801F0DE300000007CDA
:1002100001788E100000003E02380E00000000023F
:10022000027E0600000000030C7F020000000000B8
:10023000F0C902000000000001CD800000000000B5
:10024000000000000080C0F07C06735943063C3873
:100250003030383E267B5943067CF0C080000000D9
:10026000000000000000000000000078FFCF010047
:10027000003060E0C080808080808080C0C06030BE
:1002800000000001CFFE780000000000007CFE8628
:100290000E0E1C18317FFEFC1C18383838393939DD
:1002A0003939393938383838181CFCFE7F39181C70
:1002B0000E0EC6FE3C0001070E1C3870C0C0800345
:1002C00007FCF80000F0C0C0C0C0C0E0F00000F8BB
:1002D000FC0F0380C0E070381C0E03010004060F01
:1002E0000F06060303036373333BFFFF7F3F38F0C2
:1002F000C000F0F83F7FFFFF3B3363630303030657
:10030000060F0F06000000000000000000080808AB
:1003100000000060600000002010080402003E5150
:1003200049453E0000427F4000004261514946007D
:100330002141454B31001814127F100027454545D7
:1003400039003C4A4949300001710905030036492A
:1003500049493600064949291E000036360000008A
:100360007C1211127C007F49494936003E414141CF
:1003700022007F4141221C007F49494941007F09F9
:10038000090901003E4149497A007F0808087F00B9
:1003900000417F4100002040413F01007F081422BE
:1003A00041007F40404040007F020C027F007F04FC
:1003B00008107F003E4141413E007F0909090600C7
:1003C0003E4151215E007F091929460046494949AD
:1003D000310001017F0101003F4040403F001F20EC
:1003E00040201F003F4038403F00205454547800C4
:1003F0007F4844443800384444442000384444484A
:100400007F00385454541800087E0901020018A4D3
:10041000A4A47C007F080404780000447D40000010
:100420004080847D00007F102844000000417F4010
:1004300000007C04180478007C0804047800384428
:1004400044443800FC242424180018242418FC00F8
:100450007C0804040800485454542000043F4440DD
:1004600020003C4040207C001C2040201C003C40E0
:1004700030403C004428102844001CA0A0A07C83ED
:10048000DC7A3F3F7ADC8399BDDB7E7E3CE781816D
:10049000E73C7E7EDBBD99C13B5EFCFC5E3BC13C24
:1004A0007ED7B5ADBFFFEDADADFFB7F5BFB7ADEDD5
:1004B000BDC3BDA5BD423C007F4155555555555561
:1004C00055555555555555417F227F7F63221C4117
:1004D00063466E7C7E7A3EBCFE7E3EBEBEFC7C7871
:1004E00038383870606040001C22637F7F222222EF
:1004F0003E3E7F6363221C223E3E7F6363221C4597
:100500000068010000F6E8DBCFC3B8AEA49B928A76
:10051000827B746E68625C57524E4945413E3A3761
:1005200034312E2C29272523210A0A153A49676ED2
:100530006F7265006D560A0A504C45415345205470
:1005400055524E204F464620004C4F5720424154B2
:100550005445525921200003053F213F1208120A39
:10056000100A0E1C0E0C1E0C141A14161F161E1345
:100570001E1E1B1E1E1F1E3F213F3A2F3A173D17FE
:100580003F213F181F02040A041F0E04040E1F0A15
:100590001F0A0E0E1F04110E2E252E2F2A2E26297D
:1005A00029021F02081F08150E04040E150E15154A
:1005B0001B151B040604040C0400000000170003B4
:1005C00000031F0A1F16371A1904130A153A0003ED
:1005D00000000E11110E000A040A040E040030007F
:1005E0000404040010001804031F111F121F101D23
:1005F000151711151F07041F17151D1F151D0101C4
:100600001F1F151F17151F000A00003200040A11D2
:100610000A0A0A110A040115020E11171E051E1FEF
:10062000150A0E110A1F110E1F15111F05010E11BB
:100630001D1F041F111F1108100F1F041B1F101076
:100640001F061F1E040F0E110E1F09060E112E1F6E
:10065000051A121509011F011F101F0F180F1F0C7B
:100660001F1B041B031C03191513001F110304187F
:10067000111F000201022020200001020C121E1F87
:10068000120C0C12120C121F0C1A14041E05242A30
:100690001E1F021C141D1020201D1F0814111F10E6
:1006A0001E041E1E021C0C120C3E0A040C123E1EDE
:1006B000040214160A020F120E101E0E100E1E084F
:1006C0001E120C1226281E322A26041E21001F008C
:1006D000211E040102013F213F172B152010204C41
:1006E0004F4144455200172B16201314000A4E6F39
:1006F0002053442063617264206F720A6E6F204C35
:100700004F414445522E4845580A0A153A457869E2
:1007100074001D20466C617368696E670A20206C46
:100720006F616465722E2E2E0A0A444F4E275420A4
:100730005455524E204F4646210000000000230031
:100740002600290004040404040404040202020232
:100750000202030303030303010204081020408084
:100760000102040810200102040810200000000803
:100770000002010000030407000000000000000068
:10078000AA1F11241FBECFEFD8E0DEBFCDBF11E0FE
:10079000A0E0B1E0ECE5F4E402C005900D92A03ACF
:1007A000B107D9F724E0A0EAB1E001C01D92A53C51
:1007B000B207E1F713E0C1ECD3E004C02197FE01DA
:1007C0000E942122C03CD107C9F70E94BB150C949E
:1007D0002C220C940000CF92DF92EF92FF920F93A5
:1007E0001F93CF93DF936C017A018B01C0E0D0E0BF
:1007F000CE15DF0589F0D8016D918D01D601ED91FF
:10080000FC910190F081E02DC6010995892B11F42E
:100810007E0102C02196ECCFC701DF91CF911F91DD
:100820000F91FF90EF90DF90CF90089590912E025E
:10083000292F30E0E0910F02F09110022E173F07B0
:1008400024F1AF014D5A5F4F42175307F4F0409126
:100850002D0250E060910D0270910E02461757076D
:10086000A4F0DB011596A417B5077CF02E1B3F0BF7
:10087000461B570B64E5649FF001659FF00D112442
:10088000E20FF31FE55CFD4F80839F5F90932E0284
:100890000895EF92FF920F931F93CF93DF93F82E5B
:1008A000E62E88E0F89E80011124C8E0D0E0FF2009
:1008B00061F0F801EC1BFD0BE158FB4FE11002C0A9
:1008C000849108C08491809505C0E11002C080E049
:1008D00001C08FEF0E941604219749F7DF91CF9155
:1008E0001F910F91FF90EF900895611180950C94E6
:1008F0001604089580E090E008950E942722833036
:1009000081F028F4813099F08230A1F00895873089
:10091000A9F08830B9F08430D1F4809180008F7DC7
:1009200003C0809180008F7780938000089584B504
:100930008F7702C084B58F7D84BD08958091B0000B
:100940008F7703C08091B0008F7D8093B0000895B1
:100950001F93CF93DF93282F30E0F901E459F84F2C
:100960008491F901E85AF84FD491F901EC5BF84F02
:10097000C491CC23C9F0162F81110E947F04EC2F63
:10098000F0E0EE0FFF1FE258FF4FA591B4918FB733
:10099000F894111105C09C91ED2FE095E92302C058
:1009A000EC91ED2BEC938FBFDF91CF911F910895C8
:1009B000CF93DF9390E0FC01E85AF84F2491FC01BB
:1009C000EC5BF84F8491882361F190E0880F991FC8
:1009D000FC01EC58FF4FC591D491FC01E258FF4F48
:1009E000A591B491611109C09FB7F89488812095B1
:1009F00082238883EC912E230BC0623061F49FB771
:100A0000F8943881822F809583238883EC912E2B54
:100A10002C939FBF06C08FB7F894E8812E2B2883B4
:100A20008FBFDF91CF9108951F93CF93DF93182F3E
:100A3000EB0161E00E94D804209711F460E004C04B
:100A4000CF3FD10539F461E0812FDF91CF911F9124
:100A50000C94A804E12FF0E0E459F84FE491E3305E
:100A6000F9F048F4E130B9F0E230A1F584B58062E4
:100A700084BDC8BD34C0E730E9F0E83019F1E43096
:100A800049F580918000806280938000D0938B0034
:100A9000C0938A0024C084B5806884BDC7BD1FC0D0
:100AA00080918000806880938000D0938900C093FB
:100AB000880015C08091B00080688093B000C0931A
:100AC000B3000DC08091B00080628093B000C093ED
:100AD000B40005C0C038D1050CF0B5CFAFCFDF9161
:100AE000CF911F9108958E3008F08E5087708064EA
:100AF00080937C0080917A00806480937A0080915A
:100B00007A0086FDFCCF809178002091790090E0FA
:100B1000922B08953FB7F894809135029091360258
:100B2000A0913702B091380226B5A89B05C02F3F8F
:100B300019F00196A11DB11D3FBFBA2FA92F982F03
:100B40008827820F911DA11DB11DBC01CD0142E07E
:100B5000660F771F881F991F4A95D1F708958F92C6
:100B60009F92AF92BF92CF92DF92EF92FF926B0172
:100B70007C010E948A054B015C01C114D104E1048F
:100B8000F104F1F00E948A05DC01CB018819990972
:100B9000AA09BB09883E9340A105B10570F321E085
:100BA000C21AD108E108F10888EE880E83E0981E89
:100BB000A11CB11CC114D104E104F10419F7DDCF6B
:100BC000FF90EF90DF90CF90BF90AF909F908F906D
:100BD00008952FB7F894609131027091320280919C
:100BE0003302909134022FBF08951F920F920FB6D7
:100BF0000F9211242F933F938F939F93AF93BF93A3
:100C00008091310290913202A0913302B09134026E
:100C10003091300223E0230F2D3720F40196A11DDF
:100C2000B11D05C026E8230F0296A11DB11D20931A
:100C300030028093310290933202A0933302B0933A
:100C400034028091350290913602A0913702B09122
:100C500038020196A11DB11D809335029093360292
:100C6000A0933702B0933802BF91AF919F918F91BB
:100C70003F912F910F900FBE0F901F90189580916C
:100C80003B04882319F080913D0401C080E08093EB
:100C9000B40008951F920F920FB60F9211242F9354
:100CA0003F934F935F936F937F938F939F93AF93F4
:100CB000BF93EF93FF9360913C04662321F18091F1
:100CC0002F028F5F80932F02909139028917D8F0FD
:100CD00090913B0481E0892780933B0410922F027E
:100CE00080913A02882369F08091000193E4899F02
:100CF000802D1124895B809300010E94ED21909347
:100D00003D040E943F06FF91EF91BF91AF919F91EB
:100D10008F917F916F915F914F913F912F910F90A4
:100D20000FBE0F901F9018951F93CF93DF93EC0188
:100D3000162FF89460E08F850E94A8048889181601
:100D40001CF460E00E94A8041EBD00000DB407FE64
:100D5000FDCF8EB5888918161CF461E00E94A804A6
:100D60007894DF91CF911F910895EF92FF921F9396
:100D7000CF93DF93EC018C858F5F8C878BE3E82E1C
:100D800082E0F82E10E0612F6064CE010E9494068C
:100D900060E8CE010E94940661E08F850E94A8045D
:100DA000888918161CF460E00E94A80480E090E096
:100DB000F701E80FF91F20812EBD00000DB407FEDA
:100DC000FDCF2EB501968435910591F788891816C7
:100DD0001CF461E00E94A8041F5F84E5E80EF11C8A
:100DE000163089F660E4CE01DF91CF911F91FF901C
:100DF000EF900C9494060F931F93CF93DF9300E032
:100E000012E0C3E0C0932E0210922D028FEF0E94D9
:100E1000160481E00E94160481E00E941604F80185
:100E200081918F01882329F060E081E00E944904CC
:100E300006C0D8E081E00E941604D150D9F781E0C5
:100E40000E94160481E00E9416048FEF0E9416048F
:100E5000C85ECB37B9F6DF91CF911F910F910895FE
:100E6000909109022FEF290F263080F4662321F19B
:100E7000913011F0933051F420910A02220F220F89
:100E8000220F30910802231B295F0FC020910A0214
:100E9000923041F43091070248E0249F300D112434
:100EA000232F03C0220F220F220F20932E02909394
:100EB0002D0260E00C9449040895FF920F931F9354
:100EC000CF93DF9310910A020091090287E080938B
:100ED00009028DE080930A0261E080E00E94300701
:100EE0008EE080930A0261E080E00E943007C091AA
:100EF0000502D09106028FE0F82E1C161D0654F450
:100F00008F2D8C1B80930A0261E081E00E943007E4
:100F10002197F3CF0093090210930A02DF91CF913A
:100F20001F910F91FF9008954F925F926F927F9261
:100F30008F929F92AF92BF92CF92DF92EF92FF92E9
:100F40000F931F93CF93DF93182FAFEACA2EA1E020
:100F5000DA2EC1E0D0E0B7E0AB2EB12C9C2F01E03F
:100F60008C2F8150833008F000E010922E02909375
:100F70002D02F12C2F2D30E0E0910802F0E0C501A8
:100F80008E1B9F0B28173907A4F4D6018C91882358
:100F900059F03196E20FF31FB8E08B9FE00DF11D81
:100FA0001124E958FB4F8491602F0E947504F3943B
:100FB000E1CF712C7E01E1E0EE1AF1084701F4E087
:100FC000880C991CFA95E1F760910902262F30E010
:100FD0002C173D0769F460900A02671009C08FEF73
:100FE000860F833028F460E0812F0E94490441C0BD
:100FF00040910C02872D90E0411131C02C173D0724
:1010000071F520910A0230E0AC014F5F5F4F241769
:10101000350729F56450633010F5612C8091080282
:10102000681628F480E00E9416046394F7CF60E00D
:10103000812F0E944904612C862D90E020910802A6
:10104000A501421B5109841795072CF480E00E94EA
:1010500016046394F1CF66246394670C0AC0880D6C
:10106000991DFC01E056FE4F602F80810E944904CB
:10107000672C77247394760C5EE0571508F0A4CFA4
:10108000912CC701E4E0880F991FEA95E1F7DC0194
:10109000A155BE4F7D01809108029816C0F4D7017A
:1010A000EC91E11103C0602F80E00DC0F0E03197BA
:1010B00073E0EE0FFF1F7A95E1F7E90DF11DE1589E
:1010C000FB4F8491602F0E9475049394E4CF229685
:1010D000B0E2CB0ED11CC730D10509F03FCF8091D3
:1010E0000A02811104C061E0812F0E9430074FEB9A
:1010F000C42E41E0D42EC2E0D0E058E0452E512C61
:1011000067E0A62EB12C9C2F01E08C2F82508230FC
:1011100008F000E010922E0290932D02F12C8091A5
:101120000702F816C8F4F6019081911103C0602FF0
:1011300080E00EC0F201E81BF109EF0DF11D28E07F
:10114000929FE00DF11D1124E958FB4F8491602F0F
:101150000E947504F394E3CF712C7E0131E0E31A11
:10116000F108470134E0880C991C3A95E1F7609149
:101170000902262F30E02C173D0769F460900A021F
:10118000671009C08FEF860F833028F460E0812F4D
:101190000E94490441C040910C02872D90E041110A
:1011A00031C02C173D0771F520910A0230E0AC01E7
:1011B0004F5F5F4F2417350729F56450633010F5F2
:1011C000612C862D90E020910702A501421B510958
:1011D000841795072CF480E00E9416046394F1CFE5
:1011E00060E0812F0E944904612C809107026816FB
:1011F00028F480E00E9416046394F7CF6624639479
:10120000670C0AC0880D991DFC01E056FE4F602F47
:1012100080810E944904672C77247394760C5EE0E9
:10122000571508F0A4CF912CC70124E0880F991F0F
:101230002A95E1F7DC01A155BE4F7D01E92DF0E0D3
:10124000809107029501281B3109E217F3078CF4FE
:10125000D7018C91882341F0B8E08B9FE00DF11D00
:101260001124E958FB4F8491602F0E9475049394D8
:10127000E5CF229630E2C30ED11CC830D10509F06B
:1012800042CF80910A02811114C061E0812FDF9169
:10129000CF911F910F91FF90EF90DF90CF90BF9073
:1012A000AF909F908F907F906F905F904F900C9435
:1012B0003007DF91CF911F910F91FF90EF90DF905A
:1012C000CF90BF90AF909F908F907F906F905F90E6
:1012D0004F9008952F923F924F925F926F927F921C
:1012E0008F929F92AF92BF92CF92DF92EF92FF9236
:1012F0000F931F93CF93DF93CDB7DEB7A6970FB6AB
:10130000F894DEBF0FBECDBF8CE0E1E0F1E0DE017E
:10131000599601900D928A95E1F78CE0EDE0F1E0AD
:10132000DE011D9601900D928A95E1F780910B02E6
:10133000813029F422EF3FEF388F2F8B02C08330AA
:1013400028F42AEF3FEF3E8B2D8B02C0833041F40F
:1013500029EF3FEF3C8B2B8B8330F0F4512C23C0D3
:101360008430F9F02F85388921503109388B2F8747
:1013700029893A89215031093A8B298B883060F0CC
:101380002CEF3FEF3C8B2B8B2EEF3FEF3E8B2D8BCB
:101390002DEF3FEF388F2F8B873018F0B9E05B2EA1
:1013A00002C0A5E05A2EDE015996CE0101969EA3F9
:1013B0008DA3AE014B5D5F4F9C01CD01FC0161919E
:1013C0007191CF01F901619371939F018417950782
:1013D000A9F760EA71E0AB0120E030E0FA01119278
:1013E000AF01C90180569E4FFC01108A10A210AABD
:1013F0008C01005C1F4FF8011082805B9F4FFC0145
:1014000010822F5F3F4F2031310541F77E01FDE013
:10141000EF0EF11C20E030E010E0FEE0AF2EB12C2A
:10142000EDA1FEA180819181F701C190D1907F0152
:1014300040E050E04501821A930A422E1816190620
:1014400084F5881599056CF58130910519F41130F2
:1014500009F412E01B01240E351E2330310528F05B
:10146000F4E04F1639F003E001C000E0211531052A
:1014700029F402C009E002C0451540F0E5E07E2EE7
:10148000710E672C600EF101608204C00BE0010F49
:10149000F1010083112321F0123019F410E001C092
:1014A00011E00197C816D9061CF08D919C911197F7
:1014B0004F5F5F4F4F30510509F6EDA1FEA18193BB
:1014C0009193FEA3EDA32F5F3F4F1296605F7F4F76
:1014D0002630310509F0A4CFA6960FB6F894DEBFEA
:1014E0000FBECDBFDF91CF911F910F91FF90EF9075
:1014F000DF90CF90BF90AF909F908F907F906F9034
:101500005F904F903F902F900895809109029FEF38
:10151000980F9330A0F490910A02E82FF0E03197F1
:1015200034E0EE0FFF1F3A95E1F7E90FF11DE056A9
:10153000FE4F9081911129C091E090930C029CEF95
:10154000980F933028F590E09C012150310980914B
:101550000A02E82FF0E0C90124E0880F991F2A95BC
:10156000E1F7DC01AE0FBF1FA056BE4F2C91211139
:1015700008C08E0F9F1FFC01E156FE4F808188231B
:1015800039F081E080930C0208959A30A8F6D7CF05
:101590000895AF92BF92CF92DF92EF92FF920F9396
:1015A0001F93CF93DF931F92CDB7DEB7182F7A0129
:1015B00000E0F701E00FF11D8081882381F1803286
:1015C00061F194ED980F903130F09EEC980F9932C4
:1015D00010F095EC980F1F3710F06F5F10E010932C
:1015E0002E0260932D0286E0989F50011124C12C99
:1015F000D12CF601EA0DFB1DEB5FFC4F8491698352
:101600000E9416048FEFC81AD80A698186E0C816AE
:10161000D10479F71A5F0F5FCCCF90E0DCCF0F9049
:10162000DF91CF911F910F91FF90EF90DF90CF90BE
:10163000BF90AF900895EF92FF920F931F93CF93B7
:10164000DF93CDB7DEB72A970FB6F894DEBF0FBE93
:10165000CDBFF82EE62ECA018E010F5F1F4F2AE084
:10166000F80111922A95E9F74AE0B8010E94B51FE6
:10167000A8016E2D8F2D0E94C90A2A960FB6F894E4
:10168000DEBF0FBECDBFDF91CF911F910F91FF90B5
:10169000EF900895EF92FF920F931F93CF93DF93F4
:1016A0007C018B01C0E0D0E0EC16FD06E4F061E0C7
:1016B00083E00E94A80420E030E02017310724F4E2
:1016C00000002F5F3F4FF9CF60E083E00E94A80445
:1016D00020E030E02017310724F400002F5F3F4F57
:1016E000F9CF2196E1CFDF91CF911F910F91FF901C
:1016F000EF900895FC01643508F053C0403308F0C2
:1017000050C090A1742F759575957595262F062E4E
:10171000000C330B542F57709230A1F4F901E55CA3
:10172000FD4F84E57802E00DF11D1124808190E0E9
:10173000052E02C0959587950A94E2F78095982F1B
:1017400091700AC0933041F484858170462740959A
:101750004170841303C014C0911112C084E5780253
:10176000200D311D1124F901E55CFD4F81E090E071
:1017700001C0880F5A95EAF780959081892310C09F
:1017800084E57802200D311D1124F901E55CFD4F3F
:1017900081E090E001C0880F5A95EAF79081892B8B
:1017A00080830895BF92CF92DF92EF92FF920F93C2
:1017B0001F93CF93DF931F92CDB7DEB77C01D62E58
:1017C000C42E002E000C110B040F111D47FD1A959D
:1017D0004C2D0C2C000C550B401751077CF4B12CF0
:1017E000B21654F46B2D6D0D4C2DC70129830E9448
:1017F0007A0BB3942981F4CFC394EACF0F90DF9191
:10180000CF911F910F91FF90EF90DF90CF90BF90FD
:1018100008956F927F928F929F92AF92BF92CF92D4
:10182000DF92EF92FF920F931F93CF93DF93EC0120
:101830006A3019F42D819885A1C06D3009F4A4C0D7
:101840001885BA84D98488A083E58D150CF47FC0EF
:101850008FE28B150CF47BC08C8190E00197189F70
:101860009001199F300D1124C9018D0D911DD7FCD8
:101870009A95181619060CF06AC08D81819FC001D7
:1018800011248B0D911DB7FC9A95181619060CF0B2
:101890005EC09D2CCD2CE62EF12CEC2DED190E2EDC
:1018A000000CFF0B8C8190E0E817F9070CF04EC09C
:1018B0000197E817F90779F0E89E9001E99E300D4D
:1018C000F89E300D1124E20FF31F8E8D9F8DE80FCF
:1018D000F91F749001C0712C6B2CAB2C2A2D2B1985
:1018E000022E000C330B8D8190E0281739075CF530
:1018F00070FE10C0113031F44A2D6C2DCE010E94C3
:101900007A0B1DC0012F212F462D692DCE010E947B
:10191000D20B15C089A198A1891789F088A311302D
:1019200031F44A2D6C2DCE010E947A0B07C0012F95
:10193000212F462D692DCE010E94D20B88A27694CC
:10194000A394610ECBCFC394910EA7CF88A2288514
:101950003C818985329F800D112489879F8199233D
:1019600099F0082E000C990B44E550E0239F401994
:1019700051091124481759073CF49D818A85299FF4
:10198000800D11248A87198681E090E0DF91CF9144
:101990001F910F91FF90EF90DF90CF90BF90AF908D
:1019A0009F908F907F906F900895CF93DF931092C8
:1019B000AC0481E08093AD04D0E0C0E04D2F6C2FEB
:1019C0008CE894E00E947A0BCF5FC038B9F7DF5FF4
:1019D000D03499F7DF91CF910895EF92FF920F9352
:1019E0001F93CF93DF938C0181E00E94D50C0336C7
:1019F000110524F41093100200930F024FE651E0FA
:101A000061E080E00E94C90A4DE751E063E080E0B8
:101A10000E94C90A4AE851E065E080E40E94C90AD0
:101A200010922E0210922D020CE410E088E30E9426
:101A3000160401501109D1F710922E0282E0809312
:101A40002D020CE410E08CE10E94160401501109F3
:101A5000D1F7C0E0E12CF12CD4E5D0932E02C09355
:101A60002D0200E010E0F801EE0DFF1DE35CFD4FDC
:101A700084910E9416040F5F1F4F0832110599F7D9
:101A800088E2E80EF11CCF5FC53039F7DF91CF91C6
:101A90001F910F91FF90EF9008954F925F926F9278
:101AA0007F928F929F92AF92BF92CF92DF92EF92EE
:101AB000FF920F931F93CF93DF9300D000D01F921C
:101AC000CDB7DEB7C090BE04D090BF04E090C00494
:101AD000F090C1040E94E905C61AD70AE80AF90A7B
:101AE0008091BD0490E0A0E0B0E04091B904509135
:101AF000BA046091BB047091BC048C159D05AE05C1
:101B0000BF0530F4411551056105710509F03CC36D
:101B1000452B462B472B09F035C380915504815046
:101B20008E3FC0F5809154048130C9F580916804DE
:101B30008F5F6091690487FD09C0082E000C990B26
:101B400070E06F5F7F4F0E94F921682F60936804F7
:101B500086EE96E09093C3048093C20488E4809359
:101B6000C40480E00E94C9128FEF94E09093710446
:101B700080937004109276041092750481E0809333
:101B8000640489E08093790410927C0410927E04AE
:101B9000109280048091550481508E3F70F48091A2
:101BA0005304813009F095C389ED96E09093C30406
:101BB0008093C20488E48093C40480E00E949C1354
:101BC00080916404882309F4ABC08091780481116A
:101BD000A7C08091700490917104E0917504F09118
:101BE0007604EE0FFF1FE80FF91F45915491411540
:101BF0005105F1F420917404222331F010927604FF
:101C000010927504FC0112C0109264048091630468
:101C1000882341F080E00E949C13E0917504F091CC
:101C200076042DC080E00E94B4127AC04591549190
:101C30009A01369527953695279540FF51C0CA01E0
:101C400016E0969587951A95E1F78F71952F9695E1
:101C50009695969590512F70223071F1F8F42111DC
:101C600017C08B3008F08AE080937904E091750406
:101C7000F09176043196F0937604E0937504EE0FBC
:101C8000FF1F8091700490917104E80FF91FCECF6F
:101C9000213061F760E00E940614E8CF2330B1F0F4
:101CA000243021F720916704829F802D1124809396
:101CB000800490938104DACF20916704829F802D65
:101CC000112480937C0490937D04D0CF20916704ED
:101CD000829F802D112480937E0490937F04C6CF31
:101CE0002F732093770480916704859F802D1124A2
:101CF00080937804109288041092870410928604CE
:101D000081E080937A0480933B0410927B0480915D
:101D100075049091760401969093760480937504EF
:101D200080917A04882309F4EEC0809178048111AF
:101D300003C00E94B412E7C0815080937804809160
:101D40008804811146C020918604309187042F5F5A
:101D50003F4FC901880F991FE0918204F0918304DD
:101D6000E80FF91F65917491862F8770809389041D
:101D7000AB01E3E056954795EA95E1F7CA019695E0
:101D80008795972F9695969590938A048F73909147
:101D90006704899F802D11248093880441704093AB
:101DA0003A0230938704209386048091840490E063
:101DB0002817390770F020918504222339F0821BFF
:101DC0009109909387048093860403C080E00E9469
:101DD000B4128091880481508093880480917B04A0
:101DE00021E0280F20937B044091770430918A04EE
:101DF000340F80916F04380F3093650460917E0436
:101E0000662371F070E0822F022E000C990B0E9465
:101E1000F92180917F04869F300D11243093650451
:101E20008091650490E0849664E270E00E94F9215C
:101E300080936504309179043093660460917C044A
:101E4000662381F0062E000C770B822F022E000CE9
:101E5000990B0E94F92180917D04869F300D1124F9
:101E600030936604609180046623A1F0822F220FD4
:101E7000990B70E00E94F921CB0162E070E00E94B2
:101E8000F9212091810490916604289F900D1124DE
:101E900090936604809166044F3341F08A300CF0D1
:101EA00089E087FD80E08093660402C0109266049A
:101EB000F894E0916504F0E0EB5FFA4FE491E09371
:101EC00039023091660420918B0432039001112471
:101ED000409189044203C001439F900D112400905A
:101EE000680402C0880F991F0A94E2F797FF02C0A6
:101EF00081589F4F880F892F881F990B80933C042E
:101F000080933D0478948091C404882309F48DC0A3
:101F10008C3020F49CE0E92EE81A01C0E12CFF246B
:101F2000F394F09294041092AC041092AD043091AA
:101F300091044DE2D42E1D2D131B1E0D03E0030F43
:101F40002EE4412F63E08CE894E03D830E94D20BA5
:101F50003D810CEF030F1D5F202F41E0612F80E5D5
:101F60000E94D714202F42E0612F83E00E94D714F3
:101F7000F092AC04F092AD040091910413E0100FC4
:101F8000D01ADE0C23E04D2D622F8CE894E02D83D7
:101F90000E947A0B2D812F5F2135A9F71D0DFF249B
:101FA000FA94F10E23E04F2D622F8CE894E02D83FC
:101FB0000E947A0B2D812F5F2135A9F7035033E062
:101FC000D30E402F6D2D80E00E94BF14402F6D2D49
:101FD00083E50E94BF1441E06D2D83E00E9441140F
:101FE00042E06D2D80E50E944114145044E0612FC1
:101FF00080E50E94411448E0612F83E00E94411473
:1020000084E08093950480919104E81A8E2D815D7F
:10201000809396048091C2049091C3040E9434136B
:102020008091C40481508093C40480915A0490919B
:102030005B04892B09F47CC011E01093AC0410926E
:10204000AD049091900485E5891B809395041092CE
:10205000960480915704813009F44DC030F090917E
:102060005604853008F05FC05AC080E00E94C91253
:1020700010939404E7E5F5E0E491EF5FE0939004BA
:10208000E8E5F5E0E491EF5FE093910489E595E000
:102090009093AB048093AA0410925B0410925A04AC
:1020A0001092920418EA0E944D0D8823E1F389E40E
:1020B00095E00E943413609158047091590480E0B7
:1020C00090E04AE00E94321584E395E00E943413C8
:1020D000612F62956F7070E080E090E04AE00E94AE
:1020E000321589E295E00E943413809153048130C7
:1020F00009F0F5C01DC08091AF049091B004A0918B
:10210000B104B091B2048F709927AA27BB27089712
:10211000A105B10510F487E009C088E707C099233D
:1021200039F08B5F03C0992319F08FE20E94731579
:102130008CE894E00E94B5068091920481110EC053
:1021400088EF91E0EBE3F2E0DF019C011D9221506A
:102150003040E1F710929504109296040E948A058F
:102160006093B9047093BA048093BB049093BC0449
:102170002091B5043091B604621B730B7093B404C4
:102180006093B30480E0C5C00E94E9052091BD04BE
:10219000DC01CB01820F911DA11DB11D8093BE04F6
:1021A0009093BF04A093C004B093C1048091AF0486
:1021B0009091B004A091B104B091B2040196A11D18
:1021C000B11D8093AF049093B004A093B104B09379
:1021D000B2041092B9041092BA041092BB04109287
:1021E000BC040E948A056093B5047093B604809382
:1021F000B7049093B80480913F04909140042FE07D
:10220000289F8001299F100D112485E10E947305EC
:10221000800F911F24E0969587952A95E1F790937A
:10222000400480933F0420913E04222309F44BC0D4
:1022300060914204262F30E040E050E029833A8349
:102240004B835C83C0904304D12CE12CF12C409152
:102250004604509147044A01A12CB12C209144041A
:10226000309145042901612C712C8217930728F0C5
:102270009C014817590708F49A01C901A0E0B0E091
:102280009C01AD01241935094609570969817A81F4
:102290008B819C816C197D098E099F090E94DD212B
:1022A000A501940124193509460957090E94002007
:1022B000C20ED31EE41EF51EC09241046C2D70E0C8
:1022C00085E00E9414050E94E0120E947E1581E0C4
:1022D00020C0893209F071CC0E9463136ECC11507A
:1022E00009F0E1CE60E070E085E00E94140583B75C
:1022F000817F846083BF83B7816083BF83B7816040
:1023000083BF889583B78E7F83BF83B78E7F83BF5C
:10231000CACE0F900F900F900F900F90DF91CF913A
:102320001F910F91FF90EF90DF90CF90BF90AF90F3
:102330009F908F907F906F905F904F9008958F92B5
:102340009F92AF92BF92CF92DF92EF92FF920F9344
:102350001F93CF93DF93FB0124913196C490F090AB
:10236000AC04022E000C330B121613060CF0D7C06F
:1023700080330CF0D4C0482F082E000C550BFA0106
:10238000EC0DF11DC7FCFA951E161F060CF0C7C018
:102390006E5F7F4FD901179693E0B595A7959A95F3
:1023A000E1F787FF0BC0C80EA49FC001A59F900D49
:1023B000B49F900D1124681B790B80E04C2D0C2CE0
:1023C000000C550B480F511D87FD5A95413351059F
:1023D0001CF090E3C92EC81A2535310514F024E508
:1023E00030E0109198041827982F977031E001C0C1
:1023F000330F9A95EAF7859585958595F4E58F02D3
:10240000C0011124855C9D4F00E00C2C000CDD0800
:10241000BB24B394402F50E04C155D050CF07FC0F9
:10242000FB01E490F1E0FF1218C0FB01EC0140E871
:10243000542F5E2119F05881532B588350E0559545
:1024400047954115510519F43196E49040E82196DD
:102450005C2F581B521760F359C0F1101AC0A32EFD
:10246000A094FB01EC0140E8542F5E2119F0588143
:102470005A21588350E0559547954115510519F457
:102480003196E49040E821965C2F581B521760F378
:102490003DC043E0F41223C0932E9094FB01EC0165
:1024A00040E8A82E8C2E881A542F5E2141F08126F8
:1024B000588180FE02C0532B01C05921588350E03F
:1024C000559547954115510519F43196E49040E82A
:1024D00021965C2F5A19521728F31B2517C0FB01B0
:1024E000EC0140E8542F5E2119F05881532758839E
:1024F00050E0559547954115510519F43196E490F2
:1025000040E821965C2F581B521760F3330F19F4E3
:102510008C5A9F4F31E00F5F6A0F7B1F7BCFDF919B
:10252000CF911F910F91FF90EF90DF90CF90BF90D0
:10253000AF909F908F900895BC01009791F0FC019F
:1025400001900020E9F73197AF01481B590BE0914A
:102550008C04F0918D040280F381E02D8CE894E0EE
:10256000099480E090E00895811112C010927A04DD
:1025700010927804109287041092860410927B04C3
:1025800010923D0410923C0410923B040C943F06C0
:102590000895811114C010927A0410927804109258
:1025A00087041092860410927B0410923D041092CE
:1025B0003C0410923B040E943F061092640408956C
:1025C000CF92DF92EF92FF920F931F93CF93DF93FF
:1025D000C8E4D4E00FE414E0FF24FA9497E0E92E75
:1025E000E00E62E088810E94D804288130E0F90181
:1025F000E459F84F8491F901E85AF84FC490F90171
:10260000EC5BF84FD490DD2071F081110E947F04C3
:10261000ED2DF0E0EE0FFF1FE65CF84FA591B491B1
:10262000EC91EC21B1F4F80180818F5F808360E050
:1026300088810E94D80421960F5F1F4FE012D1CFEE
:10264000DF91CF911F910F91FF90EF90DF90CF908E
:102650000895F8018081882371F3F8018F3F11F408
:102660001082E5CFF082E3CF0F931F93CF93DF93D8
:102670008C01C0E0D0E0F801EC0FFD1F64916623EF
:1026800071F0E0918C04F0918D040190F081E02DC7
:102690008CE894E00995892B11F02196ECCFCE01BE
:1026A000DF91CF911F910F910895CF93DF930E94F7
:1026B0003413EC0185E991E00E949C128C0F9D1F60
:1026C000DF91CF91089588EF91E0EBE3F2E0DF0135
:1026D0009C011D9221503040E1F710929504109218
:1026E000960482E197E00E9434138CE894E00E9403
:1026F000B5068CB583608CBD8DB58E7F8DBD88E9A8
:1027000091E0EEEFFFE30995109292048DEE96E0D2
:102710000E9455138CE894E00E94B5060E94E012D6
:1027200080915304813039F062E370E080E090E002
:102730000E94AF05F3CF0895AF92BF92CF92DF9280
:10274000EF92FF920F931F93CF93DF93C82ED12C5C
:10275000E601C25CDB4F9DA1992309F44CC07E01C8
:102760009FEFE91AF90AF70195A1911144C09EA5BE
:102770008601000F111F025C1B4FF80134A525A52F
:10278000E32FF22FE90FF11DE90FF11D259134918F
:102790002F3FFFEF3F0711F41DA22DC039ABF6010B
:1027A000EE0FFF1FE85BFB4F3327220F331F05A0FF
:1027B000F6A1E02DE20FF31FA590B490811117C090
:1027C0000E94C912F801B3AAA2AAF601EE0FFF1FD8
:1027D000E05BFB4F16A215A281E0F70185A389E01B
:1027E0008BAF1EAEFE017B9615A2329615A22996DE
:1027F0008DA18F5F8DA3DF91CF911F910F91FF90DE
:10280000EF90DF90CF90BF90AF90089570E0DB0124
:10281000AA0FBB1FA25CBB4FD4962C91D497D59620
:102820009C91D597E22FF92FE80FF11DE80FF11DCC
:10283000259134915F9696963C932E939597F90146
:102840008591949190916704989F802D1124625CEA
:102850007B4FFB01B19685A3F901A591B4914B2F54
:1028600050E0E82FF0E04E175F0720F4F9012591C2
:102870003491832FFB01B296899F802D112485A36B
:102880000895AF92BF92CF92DF92EF92FF920F9393
:102890001F93CF93DF93182F062FC1E0D3E08AEF69
:1028A000D82E9EEFE92E842F8470C82E842F82703C
:1028B000B82E842F8870A82E4170F42E8C2F880F8C
:1028C0008F5FE80ECC2081F04D2F400F6C2F610FF1
:1028D0008CE894E00E947A0B4C2F400F6D2F610F13
:1028E0008CE894E00E947A0BBB2081F0402F4D1BB6
:1028F0006C2F610F8CE894E00E947A0B402F4C1BE8
:102900006D2F610F8CE894E00E947A0BAA2081F071
:102910004C2F400F612F6D1B8CE894E00E947A0BC6
:102920004D2F400F612F6C1B8CE894E00E947A0BB6
:10293000FF2081F0402F4C1B612F6D1B8CE894E031
:102940000E947A0B402F4D1B612F6C1B8CE894E08A
:102950000E947A0BCD1744F4E7FC04C0D150D39405
:10296000D394ED0CCF5FAACFDF91CF911F910F9140
:10297000FF90EF90DF90CF90BF90AF9008950F93AE
:102980001F93CF93DF93182F062FD42FC0E0CD17BE
:102990004CF44C2F400F612F8CE894E00E947A0B8E
:1029A000CF5FF5CFDF91CF911F910F9108959F9247
:1029B000AF92BF92CF92DF92EF92FF920F931F934D
:1029C000CF93DF93F82EE62EB22E03E0020FD0E075
:1029D000C3E08AEFC82E1EEF842F8170982EAA24A0
:1029E000A394A20E4270D42EDF5F802F8B19180F94
:1029F000992081F04C2F440F4A0D6E2D6C1B8D2FAA
:102A00008F0D0E94BF14402F6E2D6D1B8C2F8F0DCC
:102A10000E94BF14DD2081F04C2F440F4A0D6E2D13
:102A20006C1B8F2D8D1B0E94BF14402F6E2D6D1BB4
:102A30008F2D8C1B0E94BF14DC1744F417FD04C0BB
:102A4000C150C394C3941C0D0E5FCECFDF91CF91C4
:102A50001F910F91FF90EF90DF90CF90BF90AF90BC
:102A60009F9008958F929F92AF92BF920F931F9362
:102A7000CF93DF93CDB7DEB7A1970FB6F894DEBF43
:102A80000FBECDBF19A2423008F44AE08E010F5D9F
:102A90001F4F842E912CA12CB12CA50194010E94D2
:102AA0002A20E62FB901CA0101501109EA3014F4B5
:102AB000E05D01C0E95CD801EC93232B242B252B8E
:102AC00061F7C8010E949C12A1960FB6F894DEBF70
:102AD0000FBECDBFDF91CF911F910F91BF90AF90EF
:102AE0009F908F900895E0918C04F0918D04019057
:102AF000F081E02D682F8CE894E0099480915A04CD
:102B000090915B04892BB1F1809162048F7169F51A
:102B100084E10E947305BC01990F880B990B0E94F8
:102B20008C2026EE3FE34EEC50E40E9448210E94A8
:102B30005B209B017093590460935804672B91F0BC
:102B400084E080935704EAE5F4E080E041915191FC
:102B50002417350718F48093570408958F5F843045
:102B6000A9F703C08FEF80935704809162048F5FB1
:102B7000809362040895CF93DF9300D000D000D0FB
:102B8000CDB7DEB7789484B5826084BD84B58160AA
:102B900084BD85B5826085BD85B5816085BD809128
:102BA0006E00816080936E001092810080918100A0
:102BB0008260809381008091810081608093810098
:102BC000809180008160809380008091B10084605A
:102BD0008093B1008091B00081608093B0008091BB
:102BE0007A00846080937A0080917A00826080937A
:102BF0007A0080917A00816080937A0080917A00D7
:102C0000806880937A001092C10082E38093BD04B3
:102C100081E090E0A0E0B0E08093B9049093BA0422
:102C2000A093BB04B093BC048FEF8093AE04E0E8A4
:102C3000F7E7259134912130304C09F049C0E6E99D
:102C4000F7E7E491E0939704E7E9F7E7E491E0938D
:102C50004204E8E9F7E7E491E0934304E9E9F7E7A0
:102C6000859194919093450480934404EBE9F7E7B0
:102C700085919491909347048093460487E08093D4
:102C80006904EEE9F7E7E491E0936804EFE9F7E718
:102C9000E491E093AE04E0EAF7E785919491909394
:102CA0005B0480935A04E2EAF7E78591949190934C
:102CB0005D0480935C04E4EAF7E785919491909336
:102CC0005F0480935E04E6EAF7E7859194912CC057
:102CD0009CE390939704109242048093430480E213
:102CE00093E0909345048093440484ED93E09093A3
:102CF00047048093460487E08093690480936804C6
:102D00008CEA9DE090935B0480935A048EED9DE0E5
:102D100090935D0480935C0484E79EE090935F044D
:102D200080935E048CE39FE0909361048093600441
:102D300011E010933E046FEF70E085E00E941405EF
:102D400088E08093480489E08093490487E0809379
:102D50004A0486E080934B0404E000934C0482E034
:102D600080934D0481E180934E040E94E0128CE335
:102D70009FE0909359048093580400935704109354
:102D800062048BE080939A048DE08093990480E143
:102D900080939B048EE080939D048FE080939C043D
:102DA000109394041093AC041092AD041093930408
:102DB000E7E5F5E0E491EF5FE0939004E8E5F5E006
:102DC000E491EF5FE093910489E595E09093AB0483
:102DD0008093AA041FB7F89480911102811127C033
:102DE000EEE4F7E08491E2E6F7E09491E82FF0E07A
:102DF000EE0FFF1FEC58FF4FA591B491EC91E92322
:102E000021F461E08AE00E94A80461E08AE00E9467
:102E1000D8048CB580618CBD8CB580648CBD61E0BC
:102E20008DE00E94D80461E08BE00E94D80480917C
:102E300011028F5F809311021FBF8CB58F7D8CBDF7
:102E40008CB58C7F81608CBD8DB581608DBD8CB55E
:102E50008C608CBD61E080919A040E94D80461E08E
:102E6000809199040E94D80461E080919B040E94A3
:102E7000D80480919D0418161CF461E00E94D804C7
:102E800080919C0418161CF461E00E94D804809183
:102E90009D04181674F460E00E94A8046AE070E0D3
:102EA00080E090E00E94AF0561E080919D040E9467
:102EB000A80480919904082E000C990BFC01EC5B8E
:102EC000F84FE491F0E0EE0FFF1FE258FF4F25911D
:102ED00034913093A1042093A004FC01E85AF84FE8
:102EE000E491E093A70480919A04082E000C990BBA
:102EF000FC01EC5BF84FE491F0E0EE0FFF1FE258AD
:102F0000FF4F2591349130939F0420939E04FC0140
:102F1000E85AF84FE491E093A60480919C04082EAF
:102F2000000C990BFC01EC5BF84FE491F0E0EE0F24
:102F3000FF1FE258FF4F259134913093A304209353
:102F4000A204FC01E85AF84FE491E093A8048091B0
:102F50009B04082E000C990BFC01EC5BF84FE491EC
:102F6000F0E0EE0FFF1FE258FF4F259134913093B0
:102F7000A5042093A404FC01E85AF84FE491E093DF
:102F8000A90461E28CE894E00E94940664E18CE874
:102F900094E00E9494068091970487FF03C08FE716
:102FA000809397046091970460688CE894E00E9495
:102FB000940660E28CE894E00E9494066CE08CE851
:102FC00094E00E9494068CE894E00E94B50611E01B
:102FD0001093670484E080938B0480E790E09093E3
:102FE00073048093720460E080E00E94061461E044
:102FF00070E083E00E9414058091B100887F8160B9
:103000008093B100F8941092800010928100109289
:1030100085001092840088E191E09093890080936C
:103020008800809181008860809381008091810078
:1030300081608093810080916F00826080936F0037
:1030400078940E947E1580915404813019F4109276
:1030500068041CC0809157048823C9F380E00E9453
:10306000C91288E890E090937104809370041092E4
:103070007604109275041093640489E080937904B7
:1030800010927C0410927E04109280048091AE0411
:10309000882309F458C181E080939404109293042A
:1030A00010929204109256048093AC048093AD0465
:1030B00072E2672E71E0772EECE0FE2E0FEF14E047
:1030C000EE24E394F9E0DF2E0E944D0D8823E1F316
:1030D000F3018491882319F0C090910401C0C12CA0
:1030E00062EB70E080E00E949F1163E271E08CE08F
:1030F0008C0D0E949F1110929504F092960482E22A
:1031000091E00E94341380919004982F990F890FB9
:1031100081958D5A8093950480919104982F990FF1
:10312000890F8195835D809396048091AF0484FF1D
:1031300003C08EEA90E002C08AEA90E00E94551334
:1031400080919004982F990F890F81958D5A8093C3
:103150009504809196048F5F809396048091680413
:10316000882319F086EA90E002C082EA90E00E948B
:10317000551380919004982F990F890F81958D5A3E
:1031800080939504809196048F5F809396048EE9D6
:1031900090E00E94551380915404813039F580915C
:1031A00068048F5F6091690487FD09C0082E000CD8
:1031B000990B70E06F5F7F4F0E94F921682F609339
:1031C000680480E00E94C912109371040093700497
:1031D0001092760410927504E0926404D0927904FF
:1031E00010927C0410927E0410928004809153040B
:1031F0008130B9F08091AE04482F50E060E070E07B
:103200008090AF049090B004A090B104B090B2044C
:1032100084169506A606B70608F483C68F3F09F400
:1032200080C68FEF8093AE0480E00E94C91280E0D8
:103230000E94C91286E990E0909371048093700413
:10324000109276041092750481E08093640499E0F2
:103250009093790410927C0410927E041092800462
:103260008093560470C060E080E090E00E94F21FFE
:1032700060E081E090E00E94F21F4BE351E060E0EB
:1032800080E00E94C90A40E651E061E080E00E94CF
:10329000C90A4BE351E063E080E00E94C90A0E9442
:1032A0004D0D8823E1F360ED77E080E090E00E942F
:1032B000AF0581E00E94D50C8CE894E00E94B50631
:1032C00080915304813009F421C68FE38093770401
:1032D0008091670480937804109288041092870488
:1032E0001092860481E080937A0480933B041092CC
:1032F0007B040E944D0D8823E1F31092100210927E
:103300000F0200E010E0C8010E94ED0C8CE894E090
:103310000E94B5060115110531F464EA76E080E0FB
:1033200090E00E94AF050D5F1F4F0033110559F764
:1033300080915304813009F0FFC580E090E0892B33
:1033400011F00E94000081E00E94D50C1092100242
:1033500010920F0210920E0210920D0280915404EE
:10336000813009F480CF81E00E94D50C10920C02CC
:10337000109229021092280288EE93E0909327027F
:103380008093260281E080930B0298E090930A02DA
:1033900097E09093090210922502809324021092E4
:1033A00023021092220210920802109207028093C8
:1033B00021028093200210921F0210921E0282E0CE
:1033C00090E090930602809305028CE080931D02AA
:1033D00081E090E090931C0280
Download .txt
gitextract_6g324x5n/

├── BatBonanzaAnalog/
│   ├── BatBonanzaAnalog.ino
│   └── font6x8AJ.h
├── BatBonanzaAnalogSinglePot/
│   ├── BatBonanzaAnalogSinglePot.ino
│   └── font6x8AJ.h
├── BatBonanzaAttinyArcade/
│   ├── BatBonanzaAttinyArcade.ino
│   └── font6x8AJ.h
├── Frogger_Attiny_Arcade/
│   ├── Frogger_Attiny_Arcade.ino
│   └── font6x8AJ2.h
├── Frogger_MAKERbuino/
│   ├── Bitmaps/
│   │   └── InfEncoder.jar
│   ├── Frogger_MAKERbuino.ino
│   ├── HEX and INF/
│   │   ├── FROGGER.HEX
│   │   └── FROGGER.INF
│   └── font6x8AJ4.h
├── MorseAttinyArcade/
│   ├── MorseAttinyArcade.ino
│   └── font6x8AJ.h
├── PAK-MAN_MAKERbuino/
│   ├── HEX and INF/
│   │   ├── PAKMAN.HEX
│   │   └── PAKMAN.INF
│   ├── PAK-MAN_MAKERbuino.ino
│   └── font6x8AJ4.h
├── Pacman_Attiny_Arcade/
│   ├── Pacman_Attiny_Arcade.ino
│   └── font6x8AJ3.h
├── README.md
├── SpaceAttackAttiny/
│   ├── SpaceAttackAttiny.ino
│   └── font6x8AJ.h
├── Space_Attack_Analog/
│   ├── Space_Attack_Analog.ino
│   └── font6x8AJ.h
├── Tetris_Attiny_Arcade/
│   ├── Tetris_Attiny_Arcade.ino
│   └── font8x8AJ.h
├── Tetris_Multi_Button/
│   ├── Tetris_Multi_Button.ino
│   └── font8x8AJ.h
├── UFO_Breakout_Arduino/
│   ├── UFO_Breakout_Arduino.ino
│   └── font6x8AJ.h
├── UFO_Stacker_Attiny/
│   ├── UFO_Stacker_Attiny.ino
│   └── font6x8AJ.h
└── WrenRollercoasterAttinyArcade/
    ├── WrenRollercoasterAttinyArcade.ino
    └── font6x8AJ.h
Condensed preview — 36 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (638K chars).
[
  {
    "path": "BatBonanzaAnalog/BatBonanzaAnalog.ino",
    "chars": 22985,
    "preview": "/* 2015 / 2016 /2017\n * Pong clone by Andy Jackson - Twitter @andyhighnumber\n * Inspired by http://webboggles.com/ and i"
  },
  {
    "path": "BatBonanzaAnalog/font6x8AJ.h",
    "chars": 4925,
    "preview": "/*\n * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays\n *\n * @file: font6x8.h\n * @creat"
  },
  {
    "path": "BatBonanzaAnalogSinglePot/BatBonanzaAnalogSinglePot.ino",
    "chars": 22239,
    "preview": "/* 2015 / 2016 /2017\n * Pong clone by Andy Jackson - Twitter @andyhighnumber\n * Inspired by http://webboggles.com/ and i"
  },
  {
    "path": "BatBonanzaAnalogSinglePot/font6x8AJ.h",
    "chars": 4925,
    "preview": "/*\n * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays\n *\n * @file: font6x8.h\n * @creat"
  },
  {
    "path": "BatBonanzaAttinyArcade/BatBonanzaAttinyArcade.ino",
    "chars": 21734,
    "preview": "/* 2015 / 2016 /2017\n * Pong game by Andy Jackson - Twitter @andyhighnumber\n * Inspired by http://webboggles.com/ and in"
  },
  {
    "path": "BatBonanzaAttinyArcade/font6x8AJ.h",
    "chars": 4925,
    "preview": "/*\n * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays\n *\n * @file: font6x8.h\n * @creat"
  },
  {
    "path": "Frogger_Attiny_Arcade/Frogger_Attiny_Arcade.ino",
    "chars": 36663,
    "preview": "/*  2015 / 2016 / 2017\n *  Frogger game by Andy Jackson - Twitter @andyhighnumber\n *  \n *  Special thanks to @senkunmusa"
  },
  {
    "path": "Frogger_Attiny_Arcade/font6x8AJ2.h",
    "chars": 4914,
    "preview": "/*\n * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays\n *\n * @file: font6x8.h\n * @creat"
  },
  {
    "path": "Frogger_MAKERbuino/Frogger_MAKERbuino.ino",
    "chars": 37886,
    "preview": "/*  2018\r\n *   \r\n *  Frogger for MakerBuino and GameBuino by Andy Jackson - Twitter @andyhighnumber\r\n *  \r\n *  You need "
  },
  {
    "path": "Frogger_MAKERbuino/HEX and INF/FROGGER.HEX",
    "chars": 49685,
    "preview": ":100000000C94C1030C94E9030C94E9030C94E903E8\r\n:100010000C94E9030C94E9030C94E9030C94E903B0\r\n:100020000C94E9030C94E9030C94E"
  },
  {
    "path": "Frogger_MAKERbuino/font6x8AJ4.h",
    "chars": 4819,
    "preview": "/*\n * Font file - originally from SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays\n *\n "
  },
  {
    "path": "MorseAttinyArcade/MorseAttinyArcade.ino",
    "chars": 21939,
    "preview": "/*  2015 / 2016 / 2017\n *  Morse decoder for Attiny85 and the #AttinyArcade by Andy Jackson (M0RCL) - Twitter @andyhighn"
  },
  {
    "path": "MorseAttinyArcade/font6x8AJ.h",
    "chars": 4873,
    "preview": "/*\n * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays\n *\n * @file: font6x8.h\n * @creat"
  },
  {
    "path": "PAK-MAN_MAKERbuino/HEX and INF/PAKMAN.HEX",
    "chars": 50123,
    "preview": ":100000000C947F050C94A7050C94A7050C94A705E8\r\n:100010000C94A7050C94A7050C94A7050C94A705B0\r\n:100020000C94A7050C94A7050C94A"
  },
  {
    "path": "PAK-MAN_MAKERbuino/PAK-MAN_MAKERbuino.ino",
    "chars": 33737,
    "preview": "/*  2018\n *   \n *  PAK-MAN for MakerBuino and GameBuino by Andy Jackson - Twitter @andyhighnumber\n *  \n *  You need to i"
  },
  {
    "path": "PAK-MAN_MAKERbuino/font6x8AJ4.h",
    "chars": 4819,
    "preview": "/*\n * Font file - originally from SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays\n *\n "
  },
  {
    "path": "Pacman_Attiny_Arcade/Pacman_Attiny_Arcade.ino",
    "chars": 38520,
    "preview": "/*  2015 / 2016 / 2017\r\n    Pacman for Attiny Arcade by Andy Jackson - Twitter @andyhighnumber\r\n\r\n    Inspired by http:/"
  },
  {
    "path": "Pacman_Attiny_Arcade/font6x8AJ3.h",
    "chars": 4923,
    "preview": "/*\r\n * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays\r\n *\r\n * @file: font6x8.h\r\n * @c"
  },
  {
    "path": "README.md",
    "chars": 3953,
    "preview": "Games for the AttinyArcade (or hardware inspired by this system)\n======================================================="
  },
  {
    "path": "SpaceAttackAttiny/SpaceAttackAttiny.ino",
    "chars": 27687,
    "preview": "/* 2015 / 2016\n * SpaceAttack game by Andy Jackson - Twitter @andyhighnumber\n * Inspired by http://webboggles.com/ and i"
  },
  {
    "path": "SpaceAttackAttiny/font6x8AJ.h",
    "chars": 4923,
    "preview": "/*\n * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays\n *\n * @file: font6x8.h\n * @creat"
  },
  {
    "path": "Space_Attack_Analog/Space_Attack_Analog.ino",
    "chars": 27609,
    "preview": "/* 2015 / 2016 /2017\n * SpaceAttack game by Andy Jackson - Twitter @andyhighnumber\n * Inspired by http://webboggles.com/"
  },
  {
    "path": "Space_Attack_Analog/font6x8AJ.h",
    "chars": 4923,
    "preview": "/*\n * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays\n *\n * @file: font6x8.h\n * @creat"
  },
  {
    "path": "Tetris_Attiny_Arcade/Tetris_Attiny_Arcade.ino",
    "chars": 32122,
    "preview": "/* 2015 / 2016 /2017\n   Tetris for Attiny Arcade\n   ========================\n\n   When the game is running :\n   ========="
  },
  {
    "path": "Tetris_Attiny_Arcade/font8x8AJ.h",
    "chars": 5239,
    "preview": "/*\n * This font is from http://arduino-er.blogspot.co.uk/2014/08/port-ascii-font-to-arduino-88-led-matrix.html\n *  \n * S"
  },
  {
    "path": "Tetris_Multi_Button/Tetris_Multi_Button.ino",
    "chars": 34784,
    "preview": "/* 2015 / 2016 /2017\n   Tetris for Attiny Arcade\n   ========================\n\n   Before the game starts:\n   ============"
  },
  {
    "path": "Tetris_Multi_Button/font8x8AJ.h",
    "chars": 5704,
    "preview": "/*\n * This font is from http://arduino-er.blogspot.co.uk/2014/08/port-ascii-font-to-arduino-88-led-matrix.html\n *  \n * S"
  },
  {
    "path": "UFO_Breakout_Arduino/UFO_Breakout_Arduino.ino",
    "chars": 26931,
    "preview": "/* 2015 / 2016\n * UFO and Breakout games by Ilya Titov. Find building instructions on http://webboggles.com/\n * The code"
  },
  {
    "path": "UFO_Breakout_Arduino/font6x8AJ.h",
    "chars": 4923,
    "preview": "/*\n * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays\n *\n * @file: font6x8.h\n * @creat"
  },
  {
    "path": "UFO_Stacker_Attiny/UFO_Stacker_Attiny.ino",
    "chars": 27718,
    "preview": "/* 2015 / 2016 / 2017\n * Stacker game by Andy Jackson @andyhighnumber\n * UFO game by Ilya Titov. Find building instructi"
  },
  {
    "path": "UFO_Stacker_Attiny/font6x8AJ.h",
    "chars": 4982,
    "preview": "/*\n * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays\n *\n * @file: font6x8.h\n * @creat"
  },
  {
    "path": "WrenRollercoasterAttinyArcade/WrenRollercoasterAttinyArcade.ino",
    "chars": 21945,
    "preview": "/* 2015 / 2016 /2017\n   WREN ROLLERCOASTER game by Andy Jackson - Twitter @andyhighnumber\n        \n   Inspired by http:/"
  },
  {
    "path": "WrenRollercoasterAttinyArcade/font6x8AJ.h",
    "chars": 4925,
    "preview": "/*\n * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays\n *\n * @file: font6x8.h\n * @creat"
  }
]

// ... and 3 more files (download for full content)

About this extraction

This page contains the full source code of the andyhighnumber/Attiny-Arduino-Games GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 36 files (598.6 KB), approximately 255.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!