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 #include "font6x8AJ.h" #include #include // 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 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(actualyplayer+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= 122) { if(actualyplayer2+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 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)<>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)<>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)<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 // ---------------------------------------------------------------------------- /* 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 #include "font6x8AJ.h" #include #include // 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 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(actualyplayer+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= 122) { if(actualyplayer2+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 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)<>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)<>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)<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 // ---------------------------------------------------------------------------- /* 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 #include "font6x8AJ.h" #include #include // 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 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(actualyplayer+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= 122) { if(actualyplayer2+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 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)<>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)<>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)<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 // ---------------------------------------------------------------------------- /* 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 #include "font6x8AJ2.h" #include #include #include // 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= 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 // ---------------------------------------------------------------------------- /* 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 //imports the Gamebuino library #include //creates a Gamebuino object named gb Gamebuino gb; #include #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 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 :1033D00081E090E090931C0280931B020E946A0996 :1033E0001092000210920102109202021092030247 :1033F0001092040260E081E00E9430070E945D07A5 :1034000040E050E067E070E080E090E00E941B0B3D :10341000809124020E9494070E94FB0610921002E1 :1034200010920F0210920E0210920D020E944D0D8A :103430008823E1F332E0632E712C88248394912C4D :103440009AE0E92EF12C28EE422E23E0522E8091B4 :1034500005029091060297FDB1C460E080912402BC :103460000E9430070E945D07409128025091290276 :1034700067E070E080E090E00E941B0B80912402E6 :103480000E9494070E94FB0620910F0230911002C7 :1034900080910A02F8E08F9FC0011124A901495DC3 :1034A0005F4F4817590734F021503109309310020B :1034B00020930F0220910F0230911002A9014E5C5F :1034C0005F4F4817590734F42F5F3F4F3093100276 :1034D00020930F0220910F0230911002A901425F48 :1034E0005F4F4817590734F02150310930931002CB :1034F00020930F0220910F0230911002A901445C29 :103500005F4F4817590734F42F5F3F4F3093100235 :1035100020930F028091090290E0039790930E028E :1035200080930D0220910F023091100237FF0BC0E3 :103530001092100210920F0297FF13C010920E0209 :1035400010920D0202C097FDF9CF80910F02909169 :1035500010028C9734F02BE230E03093100220936D :103560000F0280910D0290910E02039724F0709249 :103570000E0260920D020E944D0D8E838823D9F3B6 :1035800020911B0230911C02243F41E0340734F0AB :103590006FEF7FEF709306026093050271E0709306 :1035A0001A020091280210912902C801B7010E9455 :1035B000F921CB01B7010E94F921892B19F082E092 :1035C00080931A02C80164E670E00E94F921CB01E1 :1035D000B7010E94F921892B29F080911A028F5F8F :1035E00080931A02C801B2010E94F921CB01B701F0 :1035F0000E94F921892B29F080911A028F5F809314 :103600001A022F5F3F4F30931C0220931B028091C0 :1036100008028F5F8093080290912002992319F08D :103620001092200203C091E090932002909120021A :10363000992329F0909107029F5F909307028730AA :1036400009F086C0809121028823E9F010922102BE :1036500060910902AA24AA94A60EA62FB0E0BD8309 :10366000AC83B090210290910A02B0910C02BB830E :10367000EFEAF1E0FA83E983E0EAF1E040E050E0CC :1036800020E030E004C0F1E0F0932102E1CF62E0FD :10369000D62EDA14B8F06A016FEFC61AD60AAC81DA :1036A000BD81AC15BD0571F42130310531F4BB206D :1036B000C9F19E30F8F49F5F1EC0992309F439C404 :1036C000915002C0232BB9F4308020E030E06F012C :1036D000C20ED31ED60111962C9011972C922F5FFB :1036E0003F4F2F30310599F7A981BA813C9221E0F3 :1036F00030E01AC0BB82BB20A9F0A981BA813C90FE :1037000020E030E06F01C20ED31ED6011E962C9031 :103710001E971F962C9221503109213FBFEF3B0786 :1037200089F7308220E030E04F5F5F4F69817A8116 :10373000605F7F4F7A83698370964630510509F048 :10374000A6CF90930A027B8170930C02109208021C :1037500080910702873011F4109207021092230221 :10376000109222020E94850A809150048130B1F4A7 :1037700090912502911112C08093190210921C029F :1037800010921B02809325020E94E90560931502A6 :10379000709316028093170290931802809152043E :1037A0008130B1F490912502911112C080931402DE :1037B00010921C0210921B02809325020E94E905C0 :1037C000609315027093160280931702909318026B :1037D00080914F04813061F490912502911108C0CD :1037E0008093130210921C0210921B0280932502F8 :1037F00080915104813061F490912502911108C0AB :103800008093120210921C0210921B0280932502D8 :1038100080911302882339F110921C0210921B022E :103820001092130260E080E00E943007809109024C :10383000873039F490910A0280911A02891738F082 :103840000FC0873068F480910A02882331F080919C :103850000A02815080930A0203C081E080930C0227 :1038600082E08093240280911202882339F1109221 :103870001C0210921B021092120260E080E00E9473 :10388000300780910902873039F490910A028091C3 :103890001D02981738F00FC0873068F480910A0233 :1038A0008E3030F480910A028F5F80930A0203C049 :1038B00091E090930C0283E08093240280911402A3 :1038C0008823E1F110921402809109028730B0F54B :1038D00090910B02091B11091093290200932802F1 :1038E000833029F490910802943010F51CC0823086 :1038F00071F4909107022091080230E0290F311DE8 :1039000025303105ACF490910A0291500FC08130FE :1039100079F4909107022091080230E0290F311DBF :10392000253031052CF490910A029F5F90930A0292 :103930008F5F8093090284E08093240280911902B2 :10394000882309F47CC11092190280910B024091E6 :10395000280250912902480F511D5093290240938B :10396000280267E070E080E090E00E941B0B60E0BE :1039700080E00E94300780910902823070F181500E :1039800080930902833031F480910802843008F07A :1039900088C01FC0823081F4209107028091080204 :1039A00090E0820F911D05970CF07BC080910A0278 :1039B0008F5F80930A0275C0813009F072C0209138 :1039C00007028091080290E0820F911D05970CF08C :1039D00068C080910A028150ECCF80910802843047 :1039E00038F480910A028F3018F48F5F80930A02B6 :1039F00080910A026BEA869F612D1124669570E022 :103A000080E090E00E948C200E94C7200E945B20F2 :103A1000E62FF0E0E050FE4F808181113FC08091A1 :103A20001E0290911F02019690931F0280931E0226 :103A3000A1E0A08387E08093090288E080930A02D6 :103A400060E0809124020E9430070E945D0740914F :103A500028025091290267E070E080E090E00E9427 :103A60001B0B809124020E9494070E94FB060E9477 :103A70004D0D8823E1F308EE13E0B8018AE090E0F1 :103A80000E944A0B04561109083C1105B1F768E57C :103A900072E080E090E00E94AF0503C0B1E0B09317 :103AA0000C02E1E0E093240280911E0290911F023B :103AB00005970CF4C4C080910B028F5F80930B02BA :103AC0001092100210920F0210920E0210920D022C :103AD00068EC70E080E090E00E94AF0515E0109285 :103AE0000002109201021092020210920302109240 :103AF00004020E94FB06809124020E9494070E9407 :103B00004D0D8823E1F360E273E084E190E00E94D0 :103B10004A0B68E572E084E190E00E944A0B60E99C :103B200071E084E190E00E944A0BF1E0F093000222 :103B3000F0930102F0930202F0930302F093040267 :103B40000E94FB06809124020E9494070E944D0D62 :103B50008823E1F360E273E084E190E00E944A0B85 :103B600068E572E084E190E00E944A0B60E971E050 :103B700084E190E00E944A0B115009F0B0CF64EF4D :103B800071E080E090E00E94AF0580E00E94D50CDB :103B900040E451E061E08EE00E94C90A45E251E054 :103BA00062E08EE00E94C90A40E451E064E08EE0E9 :103BB0000E94C90A40910B0250E063E070E080E28D :103BC00090E00E941B0B0E944D0D8823E1F36CEDE9 :103BD00075E080E090E00E94AF0580E00E94D50C87 :103BE0000E944D0D8823E1F38091260290912702D7 :103BF0008436910534F0845691099093270280937E :103C000026020E946A09109200021092010210928C :103C10000202109203021092040210921F021092EC :103C20001E020E94FB060E945D07409128025091EF :103C3000290267E070E080E090E00E941B0B809119 :103C40001B0290911C02892B19F580910C028111A5 :103C50001FC090921C0280921B0210912402812F9F :103C60000E94940760E0812F0E94300760E971E0B4 :103C70008EE190E00E944A0B6CE271E08EE190E0F0 :103C80000E944A0B68EC70E08EE190E00E944A0BC3 :103C90000E94850A1092250280910C02882309F463 :103CA000D6CB10912402812F0E949407812F0E946D :103CB00094070E94FB0661E080E00E9430070E94AA :103CC0004D0D8823E1F300E010E0B80182E390E0BD :103CD0000E944A0B0E5C1F4F0A3F1105B1F710916D :103CE0002402812F0E9494070E94FB0661E0812F2D :103CF0000E9430075AEFE52EF12CB70182E390E0E5 :103D00000E944A0B22E3E20EF11C34EFE31631E08D :103D1000F30699F7812F0E9494070E94FB0661E049 :103D200080E00E9430070E944D0D8823E1F304EFEC :103D300011E0B80182E390E00E944A0B0E5C1F4F35 :103D40000E3E42E01407A9F710912402812F0E9431 :103D500094070E94FB0661E0812F0E9430070EEE5F :103D600012E0B80182E390E00E944A0B0E5C1F4F04 :103D7000083E63E01607A9F70E944D0D8823E1F382 :103D800068E572E080E090E00E94AF058091050256 :103D9000909106020197909306028093050210927B :103DA0000C020E945D0788E080930A0287E08093FE :103DB00009020E944D0D8823E1F342CB10921002BC :103DC00010920F0210920E0210920D0280E090E00D :103DD0000E94EA1F082F10E0102F002710932C02DA :103DE00000932B0281E090E00E94EA1F082B1093C1 :103DF0002C0200932B0210922A02609128027091EB :103E00002902061717078CF470932C0260932B027B :103E100081E090E00E94F21F6091290280E090E032 :103E20000E94F21F81E080932A02109210021092E9 :103E30000F0280E00E94D50C4DE351E060E080E08D :103E40000E94C90A4FE251E061E080E00E94C90A85 :103E50004DE351E063E080E00E94C90A4FE451E085 :103E600062E080E00E94C90A409128025091290234 :103E700062E070E08CE290E00E941B0B0E944D0D0E :103E80008823E1F364EC79E080E090E00E94AF05E4 :103E900080E00E94D50C4AE351E060E080E00E949F :103EA000C90A4AE351E063E080E00E94C90A4091F8 :103EB0002B0250912C0262E070E088E190E00E94B9 :103EC0001B0B80912A02811106C04AE451E061E097 :103ED0000E94C90A12C046E551E061E080E00E94FC :103EE000C90A0CEB12E0B8018EE190E00E944A0B87 :103EF00002531109083C1105B1F70E944D0D8823AA :103F0000E1F364EC79E080E090E0D1C90E944D0DCE :103F10008823E1F365E070E080E090E00E94AF0567 :103F2000CFC980915504813009F0CEC80E94631337 :103F3000CBC8DE80DB82C8CB80915404813009F489 :103F4000FCC984E690E00E94ED0C0E944D0D882390 :103F5000E1F3EEC910928F0410928E048DE191E08E :103F600090938D0480938C040895BB274A3031F4DC :103F7000992322F4BDE2909581959F4F0C94C11F27 :103F8000BB27FB015527AA27880F991FAA1FA41733 :103F900010F0A41B83955051B9F7A05DAA3308F027 :103FA000A95DA193009779F7B111B1931192CB015B :103FB0000C94DA1FDC01FC01672F71917723E1F784 :103FC000329704C07C916D9370836291AE17BF07E6 :103FD000C8F30895F999FECF92BD81BDF89A99274B :103FE00080B50895262FF999FECF1FBA92BD81BDE5 :103FF00020BD0FB6F894FA9AF99A0FBE019608956B :10400000052E97FB1EF400940E94172057FD07D041 :104010000E942A2007FC03D04EF40C9417205095E0 :104020004095309521953F4F4F4F5F4F08959095A4 :104030008095709561957F4F8F4F9F4F08950E9497 :104040004C20A59F900DB49F900DA49F800D911DB5 :1040500011240895A1E21A2EAA1BBB1BFD010DC05D :10406000AA1FBB1FEE1FFF1FA217B307E407F50728 :1040700020F0A21BB30BE40BF50B661F771F881F04 :10408000991F1A9469F760957095809590959B019A :10409000AC01BD01CF010895A29FB001B39FC00143 :1040A000A39F700D811D1124911DB29F700D811D64 :1040B0001124911D08950E940F2188F09F5798F0B8 :1040C000B92F9927B751B0F0E1F0660F771F881F1D :1040D000991F1AF0BA95C9F714C0B13091F00E9437 :1040E0004121B1E008950C944121672F782F882752 :1040F000B85F39F0B93FCCF3869577956795B3955E :10410000D9F73EF490958095709561957F4F8F4FCC :104110009F4F0895E89409C097FB3EF490958095D1 :10412000709561957F4F8F4F9F4F9923A9F0F92F7D :1041300096E9BB279395F695879577956795B795FB :10414000F111F8CFFAF4BB0F11F460FF1BC06F5FE1 :104150007F4F8F4F9F4F16C0882311F096E911C0F3 :10416000772321F09EE8872F762F05C0662371F014 :1041700096E8862F70E060E02AF09A95660F771F28 :10418000881FDAF7880F9695879597F908950E940A :10419000292190F09F3748F4911116F00C94422198 :1041A00060E070E080E89FEB089526F41B16611D27 :1041B000711D811D0C94DE200C94F920882371F46C :1041C000772321F09850872B762F07C0662311F4B0 :1041D00099270DC09051862B70E060E02AF09A95E7 :1041E000660F771F881FDAF7880F9695879597F9DE :1041F00008959F3F31F0915020F48795779567950A :10420000B795880F911D9695879597F9089557FD55 :104210009058440F551F59F05F3F71F04795880F34 :1042200097FB991F61F09F3F79F0879508951216CB :1042300013061406551FF2CF4695F1DF08C0161677 :1042400017061806991FF1CF869571056105089428 :1042500008950E940F21A0F0BEE7B91788F4BB278C :104260009F3860F41616B11D672F782F8827985F46 :10427000F7CF869577956795B11D93959639C8F3D5 :104280000895E894BB2766277727CB0197F908950F :104290000E945B210C94CC210E94BE2138F00E9428 :1042A000C52120F0952311F00C94B5210C94BB216D :1042B00011240C9442210E94072170F3959FC1F3B1 :1042C000950F50E0551F629FF001729FBB27F00DC4 :1042D000B11D639FAA27F00DB11DAA1F649F662719 :1042E000B00DA11D661F829F2227B00DA11D621F68 :1042F000739FB00DA11D621F839FA00D611D221F22 :10430000749F3327A00D611D231F849F600D211D05 :10431000822F762F6A2F11249F5750409AF0F1F088 :1043200088234AF0EE0FFF1FBB1F661F771F881FF1 :1043300091505040A9F79E3F510580F00C94B52153 :104340000C9442215F3FE4F3983ED4F38695779531 :104350006795B795F795E7959F5FC1F7FE2B880F97 :10436000911D9695879597F9089597F99F6780E82D :1043700070E060E008959FEF80EC089500240A94B7 :104380001616170618060906089500240A94121630 :104390001306140605060895092E0394000C11F463 :1043A000882352F0BB0F40F4BF2B11F460FF04C010 :1043B0006F5F7F4F8F4F9F4F0895DB018F939F93C8 :1043C0000E941F20BF91AF91A29F800D911DA39FBE :1043D000900DB29F900D11240895991B79E004C0AF :1043E000991F961708F0961B881F7A95C9F7809534 :1043F000089597FB072E16F4009407D077FD09D097 :104400000E940D2207FC05D03EF4909581959F4FA8 :104410000895709561957F4F0895AA1BBB1B51E1CC :1044200007C0AA1FBB1FA617B70710F0A61BB70B24 :10443000881F991F5A95A9F780959095BC01CD01C9 :104440000895EE0FFF1F0590F491E02D099481E08F :0C44500090E0F8940C942C22F894FFCF1C :10445C0001030002000400020002000300FEFFFD45 :10446C00FFFCFFFCFFFDFFFBFF00000000090CEB55 :10447C00037A04790420204C4556454C20200047F3 :10448C00414D4520204F564552002D2D2D2D2D2DC3 :10449C002D2D2D2D2D2D2D2D2D00484947482053E8 :1044AC00434F52453A004E455720484947483A0039 :1044BC0048492053434F52452052455345540046DA :1044CC002052204F204720472045205200616E6427 :1044DC0079206A61636B736F6E0050726573732021 :1044EC00412E2E2E000D0A004C4F41444552000027 :00000001FF ================================================ FILE: Frogger_MAKERbuino/font6x8AJ4.h ================================================ /* * Font file - originally from SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays * * @file: font6x8AJ4.h * @created: 2014-08-12, edited by andy jackson 2018 (see below) * @author: Neven Boyanov * * Hacked by andy jackson to optimise size for the AttinyArcade - 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 // ---------------------------------------------------------------------------- /* Standard ASCII 6x8 font */ static const uint8_t lcdDisplayxled_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, 0x00, 0x08, 0x08, 0x08, 0x00, // - 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // . 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 }; // ---------------------------------------------------------------------------- ================================================ FILE: MorseAttinyArcade/MorseAttinyArcade.ino ================================================ /* 2015 / 2016 / 2017 * Morse decoder for Attiny85 and the #AttinyArcade by Andy Jackson (M0RCL) - Twitter @andyhighnumber * * Displays morse code coming in on the assigned pin in real time, via a 5-line scrolling display * * Inspired by, and designed to run on the Attiny Arcade hardware. Check out www.webboggles.com for info on this hardware. * * When running, use: * - The right button to send morse * - The left button to start and stop the software running * * Any code not covered by the licences below can be used freely with attribution. No warranties whatsoever are provided or implied. * * **************************************************************************************************** * * The core morse decoder here is a slightly modified version of WB7FHC's Morse Code Decoder v. 1.1 * * * (c) 2014, Budd Churchward - WB7FHC * * * This is an Open Source Project * * * http://opensource.org/licenses/MIT * * * Search YouTube for 'WB7FHC' to see several videos of this project as it was developed. * * * Also see https://github.com/kareiva/wb7fhc-cw-decoder * * **************************************************************************************************** * * 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 * * 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 #include "font6x8AJ.h" #include #include #include // 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 FIRSTLINE 2 // The first displayed line of decoded morse on the LCD #define LASTLINE 6 // The last displayed line of decoded morse on the LCD #define LINELENGTH 20 // The number of characters on one completed line #define MAXWORD 17 // The longest any single word can be (needed to manage buffer limits) // 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, 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); int stopRunning = 0; // Other generic functions (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); // Specific main function to decode morse void decodeMorse(void); // Variable declarations for decoder int inputPin = 2; // input data comes in here (key or decoder) int audio = 1; // will store the value we read on this pin boolean mute = 0; // Mute the speaker int letterCount = 0; int LCDline = FIRSTLINE; // keeps track of which line we're printing on boolean ditOrDah = true; // We have either a full dit or a full dah int dit; // The following values will auto adjust to the sender's speed int averageDah; // A dah should be 3 times as long as a dit int averageWordGap; // will auto adjust boolean characterDone = true; // A full character has been sent int downTime = 0; // How long the tone was on in milliseconds int upTime = 0; // How long the tone was off in milliseconds int myBounce = 2; // Used as a short delay between key up and down long startDownTime = 0; // Arduino's internal timer when tone first comes on long startUpTime = 0; // Arduino's internal timer when tone first goes off int currentWordLength = 0; int currentWordPos = 0; char currentWord[MAXWORD]; char screenText[LASTLINE-FIRSTLINE+1][LINELENGTH+1]; int lastWordLength = 0; boolean justDid = true; // Makes sure we only print one space during long gaps int myNum = 0; // We will turn dits and dahs into a binary number stored here char mySet[] ="##TEMNAIOGKDWRUS##QZYCXBJP#L#FVH09#8###7#####/-61#######2###3#45"; char decodedCharacter = ' '; // We will store the actual character decoded here // Interrupt handlers ISR(PCINT0_vect){ // PB0 pin button interrupt stopRunning = 1; // stop the programme :) } void playerIncMorse(){ // 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() { // pinMode(2, INPUT_PULLUP); // pinMode(1, OUTPUT); ssd1306_init(); ssd1306_fillscreen(0x00); ssd1306_char_f6x8(0, 0, " M O R S E"); ssd1306_char_f6x8(0, 1, " D E C O D E R"); ssd1306_char_f6x8(27, 5, "andy jackson"); // see comments above ! ssd1306_setpos(16,2); for (int incr = 16; incr < 112; incr++) { ssd1306_send_data_start(); ssd1306_send_byte(B00001010); ssd1306_send_data_stop(); } ssd1306_setpos(27,4); for (int incr = 0; incr < 72; incr++) { ssd1306_send_data_start(); ssd1306_send_byte(B00100000); ssd1306_send_data_stop(); } ssd1306_setpos(27,6); for (int incr = 0; incr < 72; incr++) { ssd1306_send_data_start(); ssd1306_send_byte(B00000100); ssd1306_send_data_stop(); } delay(1500); long startT = millis(); long nowT =0; boolean sChange = 0; while(digitalRead(0) == HIGH) { nowT = millis(); if (nowT - startT > 2000) { sChange = 1; if (mute == 0) { mute = 1; ssd1306_char_f6x8(32, 7, "-- MUTE --"); } else { mute = 0; ssd1306_char_f6x8(31, 7, "- SOUND ON -"); } break; } if (sChange == 1) break; } while(digitalRead(0) == HIGH); if (sChange == 0) { delay(1500); ssd1306_init(); ssd1306_fillscreen(0x00); stopRunning = 0; decodeMorse(); ssd1306_fillscreen(0x00); ssd1306_char_f6x8(11, 1, "----------------"); ssd1306_char_f6x8(11, 2, " S T O P P E D"); ssd1306_char_f6x8(11, 3, "----------------"); ssd1306_char_f6x8(0, 6, " bye..."); 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, char ch[]){ uint8_t c,i,j=0; while(ch[j] != '\0') { c = ch[j] - 32; 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; i2200; i = i - 50){ beep(30,i); } ssd1306_char_f6x8(0, 0, "Listening.. WPM:"); wpm = floor(1200 / (float(averageDah/3.0)) ); doNumber(96,0,wpm); while(stopRunning == 0) { audio = digitalRead(inputPin); // What is the tone decoder doing? if (audio) { keyIsDown(); while(audio) { beep(50,250); audio = digitalRead(inputPin); } } if (!audio) { keyIsUp(); } } } void keyIsDown() { // The decoder is detecting our tone // The LEDs on the decoder and Arduino will blink on in unison if (startUpTime>0){ // We only need to do once, when the key first goes down startUpTime=0; // clear the 'Key Up' timer } // If we haven't already started our timer, do it now if (startDownTime == 0){ startDownTime = millis(); // get Arduino's current clock time } characterDone=false; // we're still building a character ditOrDah=false; // the key is still down we're not done with the tone delay(myBounce); // Take a short breath here if (myNum == 0) { // myNum will equal zero at the beginning of a character myNum = 1; // This is our start bit - it only does this once per letter } } void keyIsUp() { // The decoder does not detect our tone // The LEDs on the decoder and Arduino will blink off in unison // If we haven't already started our timer, do it now if (startUpTime == 0){startUpTime = millis();} // Find out how long we've gone with no tone // If it is twice as long as a dah print a space upTime = millis() - startUpTime; if (upTime<10)return; if (upTime > (averageDah*2)) { printSpace(); } // Only do this once after the key goes up if (startDownTime > 0){ downTime = millis() - startDownTime; // how long was the tone on? startDownTime=0; // clear the 'Key Down' timer } if (!ditOrDah) { // We don't know if it was a dit or a dah yet shiftBits(); // let's go find out! And do our Magic with the bits } // If we are still building a character ... if (!characterDone) { // Are we done yet? if (upTime > dit) { // BINGO! we're done with this one printCharacter(); // Go figure out what character it was and print it characterDone=true; // We got him, we're done here myNum=0; // This sets us up for getting the next start bit } downTime=0; // Reset our keyDown counter } } void shiftBits() { // we know we've got a dit or a dah, let's find out which // then we will shift the bits in myNum and then add 1 or not add 1 if (downTime < dit / 3) return; // ignore my keybounce myNum = myNum << 1; // shift bits left ditOrDah = true; // we will know which one in two lines // If it is a dit we add 1. If it is a dah we do nothing! if (downTime < dit) { myNum++; // add one because it is a dit } else { // The next three lines handle the automatic speed adjustment: averageDah = (downTime+averageDah) / 2; // running average of dahs if (averageDah > 1000) averageDah = 1000; // limiting slowest speed to about 3wpm ! dit = averageDah / 3; // normal dit would be this dit = dit * 2; // double it to get the threshold between dits and dahs } } void printCharacter() { justDid = false; // OK to print a space again after this if (myNum == 511) { deleteLastWord(); return; } // Punctuation marks will make a BIG myNum if (myNum > 63) { printPunctuation(); // The value we parsed is bigger than our character array // It is probably a punctuation mark so go figure it out. return; } decodedCharacter = mySet[myNum]; if (currentWordLength < MAXWORD) { currentWord[currentWordPos] = decodedCharacter; currentWordLength++; currentWordPos++; lastWordLength = currentWordLength; // this will ensure that lastWordLength is always the length of the most recent word } else printSpace(); sendToLCD(); } void printSpace() { int wpm; if (justDid) return; // only one space, no matter how long the gap justDid = true; // so we don't do this twice currentWordLength = 0; currentWordPos = 0; wpm = floor(1200 / (float(averageDah/3.0)) ); ssd1306_char_f6x8(96, 0, " "); doNumber(96,0,wpm); // We keep track of the average gap between words and bump it up 20 milliseconds // do avoid false spaces within the word averageWordGap = ((averageWordGap + upTime) / 2) + 20; decodedCharacter=' '; // this is going to go to the LCD sendToLCD(); // go figure out where to put it on the display } void printPunctuation() { // Punctuation marks are made up of more dits and dahs than // letters and numbers. Rather than extend the character array // out to reach these higher numbers we will simply check for // them here. This funtion only gets called when myNum is greater than 63 // Thanks to Jack Purdum for the changes in this function // The original uses if then statements and only had 3 punctuation // marks. Then as I was copying code off of web sites I added // characters we don't normally see on the air and the list got // a little long. Using 'switch' to handle them is much better. lastWordLength = currentWordLength; // this will ensure that lastWordLength is always the length of the most recent word switch (myNum) { case 71: decodedCharacter = ':'; break; case 76: decodedCharacter = ','; break; case 84: decodedCharacter = '!'; break; case 94: decodedCharacter = '-'; break; case 97: decodedCharacter = 39; // Apostrophe break; case 101: decodedCharacter = '@'; break; case 106: decodedCharacter = '.'; break; case 115: decodedCharacter = '?'; break; case 246: decodedCharacter = '$'; break; case 122: decodedCharacter = 's'; sendToLCD(); decodedCharacter = 'k'; break; default: decodedCharacter = '#'; // Should not get here break; } sendToLCD(); // go figure out where to put it on the display } void sendToLCD(){ uint8_t c,i; screenText[LCDline-FIRSTLINE][letterCount] = decodedCharacter; c = decodedCharacter - 32; ssd1306_setpos(letterCount*6,LCDline); 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(); letterCount++; if (letterCount > LINELENGTH) { newLine(); } } void newLine() { int scrollNow = 0; int oldLine = LCDline; int scrolled = 0; if (LCDline == LASTLINE) {scrollNow = 1; oldLine = LASTLINE-1; scrolled = 1;} else {LCDline++; scrollNow = 0;} if (scrollNow == 1) scrollUp(); blankLine(LCDline); letterCount = 0; if (decodedCharacter > ' ') { for (int i = 0; i < currentWordLength; i++) { decodedCharacter = currentWord[i]; sendToLCD(); screenText[oldLine-FIRSTLINE][LINELENGTH-i] = ' '; } ssd1306_setpos(126-(currentWordLength*6),oldLine); ssd1306_send_data_start(); for(int i=0;i<(currentWordLength*6);i++) { ssd1306_send_byte(0); } ssd1306_send_data_stop(); } } void blankLine(int line) { uint8_t i; ssd1306_setpos(0,LCDline); ssd1306_send_data_start(); for(i=0;i<128;i++) { ssd1306_send_byte(0); } ssd1306_send_data_stop(); } void scrollUp() { int i,j,k,c; for(i = 0; i < LASTLINE-FIRSTLINE; i++) { for (j=0;j // ---------------------------------------------------------------------------- /* 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, 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, 0x44, 0x64, 0x54, 0x4C, 0x44, // z 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, // horiz lines }; // ---------------------------------------------------------------------------- ================================================ FILE: PAK-MAN_MAKERbuino/HEX and INF/PAKMAN.HEX ================================================ :100000000C947F050C94A7050C94A7050C94A705E8 :100010000C94A7050C94A7050C94A7050C94A705B0 :100020000C94A7050C94A7050C94A7050C9436090D :100030000C94A7050C94A7050C94A7050C94A70590 :100040000C94E1080C94A7050C94A7050C94A70543 :100050000C94A7050C94A7050C94A7050C94A70570 :100060000C94A7050C94A7050101FF030101F703F8 :100070006C00680000000000240027002A00000037 :100080000000250028002B000500893008023802F6 :100090004978681400000500380168010000175312 :1000A00044001613780016131400151020001520B4 :1000B0001000540A03E181867F3E186D830F0F0EF6 :1000C0000183866033186DC3198F1803C38E6033A4 :1000D000186DE330CF1803C39E603630CDE360CF98 :1000E0003006C79E7E3E30CDB360CF300CC6F66082 :1000F0003330D9B660CF33CFCCF66061B0DB1E6051 :10010000CF30D86CE6C061B0DB1E618F19986CC629 :10011000C063B19B0E330F0FB078C6FF7E1F1B0666 :100120001E0F401900000000000000000000000049 :1001300000000000000000000000000000F80000C7 :10014000007C000003FF000001FF000007FF8000AB :10015000037980000FFF8000062080000FFFC000A1 :100160000C00C0001FFFC000080040001FFF00007F :1001700008E340003FF8000018E360003FE1999970 :1001800098E360003FE19999980060003FF8000013 :10019000180060003FFC0000180060001FFF000016 :1001A000180060000FFFC00019CCE0000FFFC00076 :1001B0001FFE600003FF00001DFFE00003FE0000C3 :1001C00018CC600000F8000018CC600000000000AF :1001D000000000000000000000000000000000001F :1001E00000000000000000000000000050414B2D06 :1001F0004D414E0000C0E0F0F8FCFCFEFEFEFFFFAB :10020000FFFFFEFEFEFCFCF8706000FEFFFFFFFF3C :10021000FFFFFFFFFFFFEFEFC787838301000010A1 :10022000387C38100000000010387C381000070FB0 :100230001F3F7F7F7F7FFFFFFFFFFFFF7F7F3F3FEE :100240003E1E1E000000000000000008080800001C :100250000060600000002010080402003E51494583 :100260003E0000427F40000042615149460021416A :10027000454B31001814127F1000274545453900C1 :100280003C4A494930000171090503003649494992 :100290003600064949291E000036360000007C124F :1002A00011127C007F49494936003E4141412200FC :1002B0007F4141221C007F49494941007F090909CA :1002C00001003E4149497A007F0808087F0000414B :1002D0007F4100002040413F01007F08142241007F :1002E0007F40404040007F020C027F007F040810E6 :1002F0007F003E4141413E007F09090906003E4121 :1003000051215E007F0919294600464949493100BB :1003100001017F0101003F4040403F001F2040207D :100320001F003F4038403F002054545478007F481D :1003300044443800384444442000384444487F0052 :10034000385454541800087E0901020018A4A4A4CB :100350007C007F080404780000447D400000408059 :10036000847D00007F102844000000417F40000091 :100370007C04180478007C08040478003844444461 :100380003800FC242424180018242418FC007C08BD :1003900004040800485454542000043F4440200002 :1003A0003C4040207C001C2040201C003C40304051 :1003B0003C004428102844001CA0A0A07C3C7EFFE8 :1003C000FFFFFF7E3C3C7EFFFFE7E7C3427CFE3F32 :1003D0000F0F3FFE7C42C3E7E7FFFF7E3C3E7FFC02 :1003E000F0F0FC7F3E7CFE22EBFF62EA7C7CC20ADE :1003F00083830A827C0E08180824082E0837083FD9 :10040000084B0854085D0868086F0877080E141830 :100410001424142E1437143F144B1454145D146810 :100420001177110E1F181F241F2E1F5D1F68196FD3 :1004300019771968256F2577250E2B182B242B2E5D :100440002B372B3F2B4B2B542B5D2B682E772E0EEF :1004500037183724372E3737373F374B3754375D08 :100460003768376F37000C0C17171F1F22222D2DEE :10047000380E771C3C7E666662400000000000007B :1004800000F804F4141414141414141413100F00AA :10049000000000000000000F1013141414141414B2 :1004A0001414E404E414141414141414141414147C :1004B0001414141414141414141414141414E4043C :1004C000E41414141414141414141414141414141C :1004D0001414141414141414141414E404E414144C :1004E00014141414141414141414141414141414CC :1004F000141414141414E404F800000000000000A4 :1005000000FF00FF000000000000000040A0A0A0CD :10051000A0A0A0A0A0A0A0A0A040000000000000FB :100520000000FF00FF000000000000000040A0A04D :10053000A0A0A0A0A04000000000000000007F805C :100540007F000000000000000040A0A0A0A0A0A02C :10055000A0A04000000000000000007F807F00009D :10056000000000000000C020A0A0A0A020C000004B :10057000000000000000FF00FF000000000000007D :1005800000FF00FF0000000000000000000000006D :10059000000000000000000000000000000000005B :1005A0000000FF00FF00000000000000000000004D :1005B000000000000000000000000000000000003B :1005C000000000000000000000000000000000002B :1005D000000000000000000000000000000000001B :1005E0000000000000001F202F2F2F2F201F0000D1 :1005F000000000000000FF00FF00000000000000FD :1006000000FF00FF0000000000000000FE01FE00EF :1006100000000000000000FE01FE000000000000DD :1006200000000708070000000000000000FE01FDB8 :10063000050505050505050505050502000202027B :10064000020202000205050505050505050505056B :10065000FD01FE0000000000000000FE013E4040E1 :1006600040404040404040404040404040800000CA :10067000000000000000FF00FF000000000000007C :1006800000FF00FF00000000000000007F807F00EE :10069000000000000000007F807F000000000000DC :1006A0000000C020C000000000000000007F809F0C :1006B000A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A03A :1006C000A0A0A0A0A0A0A0A0A0A0A0A0A0A0A0A02A :1006D0009F807F00000000000000007F807E0101FD :1006E00001010101010101010101010101000000FD :1006F000000000000000FF00FF00000000000000FC :1007000000FF00FF000000000000000000000000EB :1007100000000000000000000000000000000000D9 :100720000000FF00FF0000000000000000000000CB :1007300000000000000000000000000000000000B9 :1007400000000000000000000000000000000000A9 :100750000000000000000000000000000000000099 :10076000000000000000F804F4F4F4F404F80000C1 :10077000000000000000FF00FF000000000000007B :1007800000FF00FF0000000000000000020505055A :10079000050505050505050505020000000000002A :1007A0000000FF00FF00000000000000000205053F :1007B0000505050505020000000000000000FE011F :1007C000FE0000000000000000020505050505050B :1007D0000505020000000000000000FE01FE000010 :1007E00000000000000003040505050504030000E7 :1007F000000000000000FF00FF00000000000000FB :10080000001F2027282828282828282828282828A2 :100810002828282828282828282828282828282858 :100820002828272027282828282828282828282852 :100830002828282828282828282828282828272041 :100840002728282828282828282828282828282829 :100850002828282828282828282828C700FF00001A :10086000000000000000F008C828282828282828B0 :1008700028282828282827201F0045006801000074 :10088000F6E8DBCFC3B8AEA49B928A827B746E6815 :10089000625C57524E4945413E3A3734312E2C293D :1008A000272523210A0A153A49676E6F7265006D84 :1008B000560A0A504C45415345205455524E204F3C :1008C000464620004C4F572042415454455259212E :1008D000200003053F213F1208120A100A0E1C0EC9 :1008E0000C1E0C141A14161F161E131E1E1B1E1E81 :1008F0001F1E3F213F3A2F3A173D173F213F181F38 :1009000002040A041F0E04040E1F0A1F0A0E0E1F03 :1009100004110E2E252E2F2A2E262929021F020809 :100920001F08150E04040E150E15151B151B0406C5 :1009300004040C040000000017000300031F0A1F3A :1009400016371A1904130A153A000300000E111184 :100950000E000A040A040E0400300004040400100F :10096000001804031F111F121F101D151711151F4A :1009700007041F17151D1F151D01011F1F151F1728 :10098000151F000A00003200040A110A0A0A110A9F :10099000040115020E11171E051E1F150A0E110A5D :1009A0001F110E1F15111F05010E111D1F041F1110 :1009B0001F1108100F1F041B1F10101F061F1E04FD :1009C0000F0E110E1F09060E112E1F051A12150902 :1009D000011F011F101F0F180F1F0C1F1B041B03EB :1009E0001C03191513001F11030418111F00020125 :1009F000022020200001020C121E1F120C0C1212E9 :100A00000C121F0C1A14041E05242A1E1F021C148B :100A10001D1020201D1F0814111F101E041E1E0271 :100A20001C0C120C3E0A040C123E1E040214160A80 :100A3000020F120E101E0E100E1E081E120C122691 :100A4000281E322A26041E21001F00211E04010236 :100A5000013F213F172B152010204C4F4144455298 :100A600000172B16201314000A4E6F2053442063E6 :100A7000617264206F720A6E6F204C4F4144455280 :100A80002E4845580A0A153A45786974001D2046D3 :100A90006C617368696E670A20206C6F61646572AF :100AA0002E2E2E0A0A444F4E2754205455524E20C3 :100AB0004F464621000000000023002600290004C4 :100AC00004040404040404020202020202030303F5 :100AD00003030301020408102040800102040810EF :100AE000200102040810200000000800020100009C :100AF000030407000000000000000000EC1F1124A8 :100B00001FBECFEFD8E0DEBFCDBF11E0A0E0B1E067 :100B1000E8EFF4E402C005900D92AE39B107D9F7C1 :100B200024E0AEE9B1E001C01D92A73AB207E1F7B7 :100B300015E0CFE7D5E004C02197FE010E946F22A7 :100B4000CE37D107C9F70E942B170C947A220C9448 :100B50000000CF92DF92EF92FF920F931F93CF93FB :100B6000DF936C017A018B01C0E0D0E0CE15DF0588 :100B700089F0D8016D918D01D601ED91FC91019024 :100B8000F081E02DC6010995892B11F47E0102C088 :100B90002196ECCFC701DF91CF911F910F91FF906C :100BA000EF90DF90CF9008952F923F925F926F92D7 :100BB0007F928F929F92AF92BF92CF92DF92EF92ED :100BC000FF920F931F93CF93DF93262F30E0C82F10 :100BD000D0E0F90140E8849FE00DF11D1124E658B2 :100BE000FB4F94915090BE01651508F460C0A52D8F :100BF000B0E0AD01495F5F4F421753070CF457C097 :100C00004091BF01E42FE695E695E6958E1322C04C :100C10000091AE011091AF01F901EA1BFB0BA42F6B :100C2000A7700130110571F44091B6015091B701E0 :100C3000B3E0440F551FBA95E1F7E40FF51FEB53EE :100C4000FC4F02C0E354FC4F449101C0440FAA95ED :100C5000EAF72EC0F0E03196CE17DF0741F500919C :100C6000AE011091AF01F901EA1BFB0BA42FA77095 :100C70000130110571F44091B6015091B70173E054 :100C8000440F551F7A95E1F7E40FF51FEB53FC4F26 :100C900002C0E354FC4F449150E0E8E0F0E0EA1B6E :100CA000F10902C055954795EA95E2F701C040E089 :100CB000942B6730B0F40091C5011091C60143E058 :100CC000A6E0B0E0AC1BBD0BF901ED58FB4F84135F :100CD00005C0A017B10714F45491952B4F5F47300E :100CE000B1F70091AC01A0E0B0E058E0059F8001B1 :100CF000112448E0E42EF12CFD01E854FE4F408120 :100D00006417B8F150E06A0177E0C70ED11CC21633 :100D1000D3067CF1D180ED2DE695E695E6958E1310 :100D20000FC0F901E41BF50BE00FF11FEB51FC4F75 :100D3000E4914D2D477001C0EE0F4A95EAF71AC0B5 :100D4000F0E03196CE17DF07A1F4F901E41BF50BB3 :100D5000E00FF11FEB51FC4FE491F0E04D2D477097 :100D60006701C41AD10802C0F595E795CA94E2F765 :100D700001C0E0E09E2B1296A630B10509F0BCCF71 :100D80004FEF480F463008F070C0DE011197AA0FF0 :100D9000BB1FFD01EB59FB4F94907090BF01A72C36 :100DA000B12C47E0A40EB11C812CFD01EA59FB4F88 :100DB0001F01EE24E394F12C052D10E0095F1F4F75 :100DC000F1014491941608F051C0E92DF0E0AF0113 :100DD00047525E4F6A01DA014C91442309F443C043 :100DE000AF01440F551FFA01EB50FC4F64906611A0 :100DF0003AC06490561430F5E491F0E00E171F07E6 :100E00000CF1FA01EA50FC4F64907614D8F4E491A6 :100E1000F0E0AE16BF06B4F0D6011C924091C001BE :100E20004F5F4093C001E091C701F091C80140912C :100E3000C3015091C4014E0F5F1F5093C8014093EE :100E4000C70114C0FA01EA50FC4F449146954695FB :100E50004695841339C0E491E770A70101C0440F9F :100E6000EA95EAF7842A9394ABCF812C9829E1E79D :100E7000F4E0CEE9D1E040E05C2F5E5F7991713023 :100E800061F4863051F47491671731F0A491B0E0A9 :100E900011962A173B0709F440EE31965C13EECF0A :100EA000892F842BDF91CF911F910F91FF90EF90AD :100EB000DF90CF90BF90AF909F908F907F906F907A :100EC0005F903F902F9008954491469546954695A2 :100ED00050E04F5F5F4FC417D50709F0C4CFE491CE :100EE000C2CF089580E090E008950E94752283307B :100EF00081F028F4813099F08230A1F00895873094 :100F0000A9F08830B9F08430D1F4809180008F7DD1 :100F100003C0809180008F7780938000089584B50E :100F20008F7702C084B58F7D84BD08958091B00015 :100F30008F7703C08091B0008F7D8093B0000895BB :100F40001F93CF93DF93282F30E0F901E951F54F3C :100F50008491F901ED52F54FD491F901E154F54F27 :100F6000C491CC23C9F0162F81110E947707EC2F72 :100F7000F0E0EE0FFF1FE258FF4FA591B4918FB73D :100F8000F894111105C09C91ED2FE095E92302C062 :100F9000EC91ED2BEC938FBFDF91CF911F910895D2 :100FA000CF93DF9390E0FC01ED52F54F2491FC01CB :100FB000E154F54F8491882361F190E0880F991FE7 :100FC000FC01EC58FF4FC591D491FC01E258FF4F52 :100FD000A591B491611109C09FB7F89488812095BB :100FE00082238883EC912E230BC0623061F49FB77B :100FF000F8943881822F809583238883EC912E2B5F :101000002C939FBF06C08FB7F894E8812E2B2883BE :101010008FBFDF91CF9108951F93CF93DF93182F48 :10102000EB0161E00E94D007209711F460E004C05A :10103000CF3FD10539F461E0812FDF91CF911F912E :101040000C94A007E12FF0E0E951F54FE491E33073 :10105000F9F048F4E130B9F0E230A1F584B58062EE :1010600084BDC8BD34C0E730E9F0E83019F1E430A0 :1010700049F580918000806280938000D0938B003E :10108000C0938A0024C084B5806884BDC7BD1FC0DA :1010900080918000806880938000D0938900C09305 :1010A000880015C08091B00080688093B000C09324 :1010B000B3000DC08091B00080628093B000C093F7 :1010C000B40005C0C038D1050CF0B5CFAFCFDF916B :1010D000CF911F9108958E3008F08E5087708064F4 :1010E00080937C0080917A00806480937A00809164 :1010F0007A0086FDFCCF809178002091790090E005 :10110000922B08953FB7F8948091130290911402A6 :10111000A0911502B091160226B5A89B05C02F3FDD :1011200019F00196A11DB11D3FBFBA2FA92F982F0D :101130008827820F911DA11DB11DBC01CD0142E088 :10114000660F771F881F991F4A95D1F708958F92D0 :101150009F92AF92BF92CF92DF92EF92FF926B017C :101160007C010E9482084B015C01C114D104E1049E :10117000F104F1F00E948208DC01CB018819990981 :10118000AA09BB09883E9340A105B10570F321E08F :10119000C21AD108E108F10888EE880E83E0981E93 :1011A000A11CB11CC114D104E104F10419F7DDCF75 :1011B000FF90EF90DF90CF90BF90AF909F908F9077 :1011C00008951F920F920FB60F9211242F933F9301 :1011D0008F939F93AF93BF938091190290911A02BE :1011E000A0911B02B0911C023091120223E0230F48 :1011F0002D3720F40196A11DB11D05C026E8230F4F :101200000296A11DB11D20931202809319029093A2 :101210001A02A0931B02B0931C02809113029091BA :101220001402A0911502B09116020196A11DB11DE4 :101230008093130290931402A0931502B0931602A8 :10124000BF91AF919F918F913F912F910F900FBEC2 :101250000F901F90189580911D04882319F080919C :101260001F0401C080E08093B40008951F920F9284 :101270000FB60F9211242F933F934F935F936F9369 :101280007F938F939F93AF93BF93EF93FF9360915F :101290001E04662321F1809111028F5F8093110259 :1012A000909117028917D8F090911D0481E0892749 :1012B00080931D041092110280911802882369F016 :1012C0008091000193E4899F802D1124895B809394 :1012D00000010E943B2290931F040E942B09FF9162 :1012E000EF91BF91AF919F918F917F916F915F919E :1012F0004F913F912F910F900FBE0F901F90189517 :101300001F93CF93DF93EC01162FF89460E08F8545 :101310000E94A007888918161CF460E00E94A007AC :101320001EBD00000DB407FEFDCF8EB588891816CE :101330001CF461E00E94A0077894DF91CF911F9187 :101340000895EF92FF921F93CF93DF93EC018C856A :101350008F5F8C878DE1E82E82E0F82E10E0612F00 :101360006064CE010E94800960E8CE010E9480097D :1013700061E08F850E94A007888918161CF460E040 :101380000E94A00780E090E0F701E80FF91F20819C :101390002EBD00000DB407FEFDCF2EB5019684359D :1013A000910591F7888918161CF461E00E94A00746 :1013B0001F5F84E5E80EF11C163089F660E4CE016B :1013C000DF91CF911F91FF90EF900C9480091092C4 :1013D000AC011092A3011092A201E6EAF1E01182A1 :1013E000108213821282158214821092A50110922B :1013F000A401E8EBF1E082E480832CE12183A0EBFF :10140000B1E083E090E011969C938E9337E33283B2 :10141000238313969C938E9312974DE444832583E4 :1014200015969C938E931497368388E2878317963C :101430001C921E9216971092C2011092C10108953B :101440001092C0011092AD01E9EDF1E021E098E3C6 :101450009E0F81E021939E13FCCF80939E01809389 :101460009F0108950F931F93CF93DF93462F4695C7 :1014700046954695962F97706FEF70E0EB01092E19 :1014800002C0CC0FDD1F0A94E2F728E030E0291BF0 :10149000310902C0759567952A95E2F7282F30E04B :1014A00080E8489F200D311D1124A0E0B0E010E03D :1014B00080E090E0FD01E20FF31FE658FB4F08E0EB :1014C000111105C0449150E04C235D2304C04491A8 :1014D00050E046235723452B11F081E090E0015066 :1014E00031960111EDCFA058BF4F113011F011E02E :1014F000E1CFDF91CF911F910F9108956F927F926D :101500008F929F92AF92BF92CF92DF92EF92FF9213 :101510000F931F93CF93DF93CDB7DEB7C455D10997 :101520000FB6F894DEBF0FBECDBF8091A0019091A1 :10153000A10197FD0796A3E095958795AA95E1F7F8 :1015400016E0180F0091C9012091C9013091CA011C :10155000802F90E0A9014D5A5F4F4817590784F03A :101560007C01E21AF30A602F812F0E94D405E1E08A :10157000F0E0EC0FFD1FEE0DFF1D80830F5FE4CF49 :101580000091A0011091A10117FF02C0095F1F4F38 :1015900073E0159507957A95E1F70B5FE8E0AE2EBD :1015A000B12CF4E56F2EC02ED12C8091A00190912A :1015B000A10197FD079643E0959587954A95E1F738 :1015C0001091C9018C159D050CF059C0E12EF12C2C :1015D0008091C9019091CA018D5A9F4F8E159F0528 :1015E0000CF44BC0612F802F0E94D405882E909060 :1015F000A0017090A101892D972DB5010E9447226D :101600004091C9015091CA01F701E41BF50B282D47 :1016100030E0B901082E02C0759567950A94E2F78B :1016200021E030E02C0F3D1FE20FF31F2081022E3E :10163000000C330BD501A81BB90B01C0220FAA95D2 :10164000EAF7262B8082892D972D97FD079663E078 :10165000959587956A95E1F7B601681B790B669EAB :10166000F001679EF00D1124E41BF50BEE0DFF1D3C :10167000E35EFD4F20831F5FA9CF015094CF082F59 :1016800028E0A22EB12C7C01FF2434E5932EC12E3C :10169000D12C8091C9019091CA018D5A9F4F8C1510 :1016A0009D050CF44BC0612F802F0E94D405882E1D :1016B000F091A001E091A1018F2F9E2FB5010E9412 :1016C0004722AC016091C9017091CA012F2F3E2FB2 :1016D00037FF02C0295F3F4F83E0359527958A95F4 :1016E000E1F7C701821B930B989EF001999EF00DC4 :1016F0001124E61BF70BEC0DFD1DE35EFD4F882D5D :1017000090E0042E02C0959587950A94E2F7C61AD8 :10171000D70AA1E0B0E0AC0FBD1FAC0DBD1D2C91F0 :10172000022E000C330BD501A41BB50B01C0220FF8 :10173000AA95EAF7282B20831F5FA9CFCC5ADF4F49 :101740000FB6F894DEBF0FBECDBFDF91CF911F91D2 :101750000F91FF90EF90DF90CF90BF90AF909F9050 :101760008F907F906F900895EF92FF920F931F9349 :10177000CF93DF937C018B01C0E0D0E0EC16FD0637 :10178000E4F061E083E00E94A00720E030E0201751 :10179000310724F400002F5F3F4FF9CF60E083E072 :1017A0000E94A00720E030E02017310724F4000059 :1017B0002F5F3F4FF9CF2196E1CFDF91CF911F915E :1017C0000F91FF90EF900895FC01643508F053C02D :1017D000403308F050C090A1742F7595759575959C :1017E000262F062E000C330B542F57709230A1F485 :1017F000F901E35EFD4F84E57802E00DF11D11244F :10180000808190E0052E02C0959587950A94E2F7B5 :101810008095982F91700AC0933041F4848581702F :10182000462740954170841303C014C0911112C023 :1018300084E57802200D311D1124F901E35EFD4F8E :1018400081E090E001C0880F5A95EAF78095908179 :10185000892310C084E57802200D311D1124F9017F :10186000E35EFD4F81E090E001C0880F5A95EAF7F2 :101870009081892B80830895BF92CF92DF92EF925F :10188000FF920F931F93CF93DF931F92CDB7DEB7D5 :101890007C01D62EC42E002E000C110B040F111D3E :1018A00047FD1A954C2D0C2C000C550B4017510779 :1018B0007CF4B12CB21654F46B2D6D0D4C2DC70178 :1018C00029830E94E40BB3942981F4CFC394EACF17 :1018D0000F90DF91CF911F910F91FF90EF90DF90CC :1018E000CF90BF9008956F927F928F929F92AF9208 :1018F000BF92CF92DF92EF92FF920F931F93CF93FD :10190000DF93EC016A3019F42D819885A1C06D3008 :1019100009F4A4C01885BA84D98488A083E58D15FC :101920000CF47FC08FE28B150CF47BC08C8190E0AF :101930000197189F9001199F300D1124C9018D0D39 :10194000911DD7FC9A95181619060CF06AC08D8166 :10195000819FC00111248B0D911DB7FC9A9518161B :1019600019060CF05EC09D2CCD2CE62EF12CEC2D32 :10197000ED190E2E000CFF0B8C8190E0E817F90793 :101980000CF04EC00197E817F90779F0E89E900136 :10199000E99E300DF89E300D1124E20FF31F8E8D5D :1019A0009F8DE80FF91F749001C0712C6B2CAB2C2C :1019B0002A2D2B19022E000C330B8D8190E0281755 :1019C00039075CF570FE10C0113031F44A2D6C2DD2 :1019D000CE010E94E40B1DC0012F212F462D692D41 :1019E000CE010E943C0C15C089A198A1891789F0ED :1019F00088A3113031F44A2D6C2DCE010E94E40BE6 :101A000007C0012F212F462D692DCE010E943C0CCD :101A100088A27694A394610ECBCFC394910EA7CFE6 :101A200088A228853C818985329F800D1124898771 :101A30009F81992399F0082E000C990B44E550E002 :101A4000239F401951091124481759073CF49D81DF :101A50008A85299F800D11248A87198681E090E06C :101A6000DF91CF911F910F91FF90EF90DF90CF907A :101A7000BF90AF909F908F907F906F900895CF937D :101A8000DF9310928E0481E080938F04D0E0C0E059 :101A90004D2F6C2F8EE694E00E94E40BCF5FC03890 :101AA000B9F7DF5FD03499F7DF91CF910895FF92B6 :101AB0000F931F93CF93DF93C0E0D0E0082F10E087 :101AC000FF24F3946091D8012091C9013091CA019B :101AD000C8010C2E02C0959587950A94E2F780FD07 :101AE00010C0862F90E08217930734F1A9014C5A59 :101AF0005F4F4817590704F110928E04F0928F043B :101B00000FC0862F90E082179307B4F0A9014C5ABA :101B10005F4F4817590784F0F0928E0410928F049B :101B20008091D7014C2F98E0899F400D1124621BB2 :101B30008EE694E00E94E40B2196C830D10511F6A0 :101B40008091D8018F5F8093D801DF91CF911F9151 :101B50000F91FF900895AF92BF92CF92DF92EF92D4 :101B6000FF920F931F93CF93DF931F92CDB7DEB7F2 :101B7000182F7A0100E0F701E00FF11D8081882322 :101B800081F1803261F194ED980F903130F09EEC4C :101B9000980F993210F095EC980F1F3710F06F5F87 :101BA00010E01093D8016093D70186E0989F500110 :101BB0001124C12CD12CF601EA0DFB1DED5BFD4F6C :101BC000849169830E94570D8FEFC81AD80A6981E2 :101BD00086E0C816D10479F71A5F0F5FCCCF90E08A :101BE000DCCF0F90DF91CF911F910F91FF90EF907D :101BF000DF90CF90BF90AF900895EF92FF920F9338 :101C00001F93CF93DF93CDB7DEB72A970FB6F89423 :101C1000DEBF0FBECDBFF82EE62ECA018E010F5FCC :101C20001F4F2AE0F80111922A95E9F74AE0B8011E :101C30000E945320A8016E2D8F2D0E94AB0D2A9675 :101C40000FB6F894DEBF0FBECDBFDF91CF911F91CD :101C50000F91FF90EF9008954FE451E062E080E033 :101C60000E94AB0D4091C7015091C80162E070E045 :101C70008CE290E00C94FD0DAF92BF92DF92EF9258 :101C8000FF920F931F93CF93DF93EC0181E00E94AB :101C90003F0DC336D10524F4D093CA01C093C901C6 :101CA0004FE651E061E080E00E94AB0D4BE751E070 :101CB00063E080E00E94AB0D48E851E065E080E41D :101CC0000E94AB0D1092D8011092D701CCE4D0E065 :101CD00088E30E94570D2197D9F71092D80182E02E :101CE0008093D701CCE4D0E08CE10E94570D21977E :101CF000D9F7E12CF12CC1E0D0E084E5D82ED092C8 :101D0000D801C093D70100E010E05E01E894A1F88B :101D1000F701EC50FE4F84910E94570D8FEFE81AA7 :101D2000F80A81E0A816B10429F40631110514F06F :101D300003E210E00F5F1F4F023211054CF32196B2 :101D4000C430D105E1F6DF91CF911F910F91FF9043 :101D5000EF90DF90BF90AF9008954F925F926F9297 :101D60007F928F929F92AF92BF92CF92DF92EF922B :101D7000FF920F931F93CF93DF9300D000D01F9259 :101D8000CDB7DEB74091A0045091A1046091A204A8 :101D90007091A3042FB7F8948091190290911A02C0 :101DA000A0911B02B0911C022FBF481B590B6A0B5C :101DB0007B0B80919F0490E0A0E0B0E000919B0439 :101DC00010919C0420919D0430919E0484179507E6 :101DD000A607B70730F4011511052105310509F0F3 :101DE0003CC3012B022B032B09F035C38091370430 :101DF00081508E3FC0F5809136048130C9F58091C5 :101E00004A048F5F60914B0487FD09C0082E000CC7 :101E1000990B70E06F5F7F4F0E944722682F60939D :101E20004A0481E69AE09093A5048093A40488E490 :101E30008093A60480E00E943C138AE798E0909388 :101E4000530480935204109258041092570481E076 :101E50008093460489E080935B0410925E041092A4 :101E60006004109262048091370481508E3F70F4B8 :101E700080913504813009F09CC384E59AE0909309 :101E8000A5048093A40488E48093A60480E00E94C3 :101E90000F1480914604882309F4ABC080915A0442 :101EA0008111A7C08091520490915304E09157048E :101EB000F0915804EE0FFF1FE80FF91F4591549160 :101EC00041155105F1F420915604222331F010926E :101ED000580410925704FC0112C0109246048091DD :101EE0004504882341F080E00E940F14E0915704DC :101EF000F09158042DC080E00E9427137AC04591CC :101F000054919A01369527953695279540FF51C0F3 :101F1000CA0116E0969587951A95E1F78F71952F6E :101F200096959695969590512F70223071F1F8F410 :101F3000211117C08B3008F08AE080935B04E09198 :101F40005704F09158043196F0935804E0935704E5 :101F5000EE0FFF1F8091520490915304E80FF91F78 :101F6000CECF213061F760E00E947914E8CF2330B2 :101F7000B1F0243021F720914904829F802D112453 :101F80008093620490936304DACF20914904829F86 :101F9000802D112480935E0490935F04D0CF209114 :101FA0004904829F802D11248093600490936104E2 :101FB000C6CF2F732093590480914904859F802DAB :101FC000112480935A0410926A04109269041092AA :101FD000680481E080935C0480931D0410925D048A :101FE00080915704909158040196909358048093DF :101FF000570480915C04882309F4EEC080915A0450 :10200000811103C00E942713E7C0815080935A04B6 :1020100080916A04811146C020916804309169045E :102020002F5F3F4FC901880F991FE0916404F09121 :102030006504E80FF91F65917491862F877080936E :102040006B04AB01E3E056954795EA95E1F7CA01C9 :1020500096958795972F9695969590936C048F7388 :1020600090914904899F802D112480936A044170C6 :1020700040931802309369042093680480916604A9 :1020800090E02817390770F020916704222339F077 :10209000821B9109909369048093680403C080E0D7 :1020A0000E94271380916A04815080936A04809172 :1020B0005D0421E0280F20935D0440915904309184 :1020C0006C04340F80915104380F309347046091B1 :1020D0006004662371F070E0822F022E000C990BD1 :1020E0000E94472280916104869F300D1124309315 :1020F00047048091470490E0849664E270E00E9477 :1021000047228093470430915B04309348046091E8 :102110005E04662381F0062E000C770B822F022EC0 :10212000000C990B0E94472280915F04869F300D1E :10213000112430934804609162046623A1F0822F39 :10214000220F990B70E00E944722CB0162E070E001 :102150000E9447222091630490914804289F900D8B :10216000112490934804809148044F3341F08A3001 :102170000CF089E087FD80E08093480402C0109253 :102180004804F894E0914704F0E0E058F74FE491F8 :10219000E09317023091480420916D0432039001BE :1021A000112440916B044203C001439F900D112400 :1021B00000904A0402C0880F991F0A94E2F797FF23 :1021C00002C081589F4F880F892F881F990B8093D9 :1021D0001E0480931F0478948091A604882309F438 :1021E0008DC08C3020F49CE0E92EE81A01C0E12C6F :1021F000FF24F394F092760410928E0410928F04D0 :10220000309173044DE2D42E1D2D131B1E0D03E0DF :10221000030F2EE4412F63E08EE694E03D830E949D :102220003C0C3D810CEF030F1D5F202F41E0612F1F :1022300080E50E944A15202F42E0612F83E00E9432 :102240004A15F0928E04F0928F040091730413E00B :10225000100FD01ADE0C23E04D2D622F8EE694E095 :102260002D830E94E40B2D812F5F2135A9F71D0DD1 :10227000FF24FA94F10E23E04F2D622F8EE694E0B6 :102280002D830E94E40B2D812F5F2135A9F7035088 :1022900033E0D30E402F6D2D80E00E943215402F89 :1022A0006D2D83E50E94321541E06D2D83E00E9483 :1022B000B41442E06D2D80E50E94B414145044E043 :1022C000612F80E50E94B41448E0612F83E00E94F2 :1022D000B41484E08093770480917304E81A8E2DFF :1022E000815D809378048091A4049091A5040E945C :1022F000A7138091A60481508093A60480913C048A :1023000090913D04892B09F47CC011E010938E0458 :1023100010928F049091720485E5891B8093770455 :102320001092780480913904813009F44DC030F066 :1023300090913804853008F05FC05AC080E00E9458 :102340003C1310937604E2EDF8E0E491EF5FE09344 :102350007204E3EDF8E0E491EF5FE093730484ED41 :1023600098E090938D0480938C0410923D04109219 :102370003C041092740418EA0E94AD0E8823E1F325 :1023800084EC98E00E94A71360913A0470913B049A :1023900080E090E04AE00E94A5158FEA98E00E9454 :1023A000A713612F62956F7070E080E090E04AE0C3 :1023B0000E94A51584EA98E00E94A7138091350435 :1023C000813009F0FCC01DC080919104909192046D :1023D000A0919304B09194048F709927AA27BB27EA :1023E0000897A105B10510F487E009C088E707C088 :1023F000992339F08B5F03C0992319F08FE20E9473 :10240000E6158EE694E00E94A10980917404811182 :102410000EC088EF91E0EDE1F2E0DF019C011D923A :1024200021503040E1F710927704109278040E9416 :10243000820860939B0470939C0480939D04909306 :102440009E042091970430919804621B730B709343 :1024500096046093950480E0CCC02FB7F8948091E7 :10246000190290911A02A0911B02B0911C022FBF79 :1024700020919F04820F911DA11DB11D8093A00486 :102480009093A104A093A204B093A304809191041B :1024900090919204A0919304B09194040196A11D8F :1024A000B11D8093910490939204A0939304B093F0 :1024B000940410929B0410929C0410929D0410921C :1024C0009E040E94820860939704709398048093FE :1024D000990490939A0480912104909122042FE012 :1024E000289F8001299F100D112485E10E946B080F :1024F000800F911F24E0969587952A95E1F7909398 :1025000022048093210420912004222309F44BC04B :1025100060912404262F30E040E050E029833A8384 :102520004B835C83C0902504D12CE12CF12C40918D :102530002804509129044A01A12CB12C2091260491 :10254000309127042901612C712C8217930728F000 :102550009C014817590708F49A01C901A0E0B0E0AE :102560009C01AD01241935094609570969817A8111 :102570008B819C816C197D098E099F090E942B22F9 :10258000A501940124193509460957090E949E2086 :10259000C20ED31EE41EF51EC09223046C2D70E003 :1025A00085E00E940C080E9453130E94F11581E0FF :1025B00020C0893209F06ACC0E94D61367CC115032 :1025C00009F0DACE60E070E085E00E940C0883B785 :1025D000817F846083BF83B7816083BF83B781605D :1025E00083BF889583B78E7F83BF83B78E7F83BF7A :1025F000C3CE0F900F900F900F900F90DF91CF915F :102600001F910F91FF90EF90DF90CF90BF90AF9010 :102610009F908F907F906F905F904F900895BC0136 :10262000009791F0FC0101900020E9F73197AF018C :10263000481B590BE0916E04F0916F040280F38106 :10264000E02D8EE694E0099480E090E008958111F9 :1026500012C010925C0410925A04109269041092F5 :10266000680410925D0410921F0410921E041092D0 :102670001D040C942B090895811114C010925C0460 :1026800010925A04109269041092680410925D042A :1026900010921F0410921E0410921D040E942B0918 :1026A000109246040895CF92DF92EF92FF920F931B :1026B0001F93CF93DF93CAE2D4E001E314E0FF2439 :1026C000FA9497E0E92EE00E62E088810E94D0073C :1026D000288130E0F901E951F54F8491F901ED527B :1026E000F54FC490F901E154F54FD490DD2071F01D :1026F00081110E947707ED2DF0E0EE0FFF1FEB54E4 :10270000F54FA591B491EC91EC21B1F4F8018081E1 :102710008F5F808360E088810E94D00721960F5FE1 :102720001F4FE012D1CFDF91CF911F910F91FF90FA :10273000EF90DF90CF900895F8018081882371F3A6 :10274000F8018F3F11F41082E5CFF082E3CF0F93B1 :102750001F93CF93DF938C01C0E0D0E0F801EC0F22 :10276000FD1F6491662371F0E0916E04F0916F0497 :102770000190F081E02D8EE694E00995892B11F00F :102780002196ECCFCE01DF91CF911F910F9108954B :10279000CF93DF930E94A713EC0183E991E00E949D :1027A0000F138C0F9D1FDF91CF91089588EF91E05B :1027B000EDE1F2E0DF019C011D9221503040E1F794 :1027C00010927704109278048DE89AE00E94A71383 :1027D0008EE694E00E94A1098CB583608CBD8DB516 :1027E0008E7F8DBD86E991E0EEEFFFE309951092B3 :1027F000740488E69AE00E94C8138EE694E00E9472 :10280000A1090E94531380913504813039F062E3AD :1028100070E080E090E00E94A708F3CF0895AF92A7 :10282000BF92CF92DF92EF92FF920F931F93CF93BD :10283000DF93C82ED12CE601C05EDB4F9DA199230A :1028400009F44CC07E019FEFE91AF90AF70195A13E :10285000911144C09EA58601000F111F005E1B4F01 :10286000F80134A525A5E32FF22FE90FF11DE90F9B :10287000F11D259134912F3FFFEF3F0711F41DA269 :102880002DC039ABF601EE0FFF1FE65DFB4F33277E :10289000220F331F05A0F6A1E02DE20FF31FA59034 :1028A000B490811117C00E943C13F801B3AAA2AAE8 :1028B000F601EE0FFF1FEE5CFB4F16A215A281E0A2 :1028C000F70185A389E08BAF1EAEFE017B9615A2B2 :1028D000329615A229968DA18F5F8DA3DF91CF919E :1028E0001F910F91FF90EF90DF90CF90BF90AF902E :1028F000089570E0DB01AA0FBB1FA05EBB4FD4960A :102900002C91D497D5969C91D597E22FF92FE80F6B :10291000F11DE80FF11D259134915F9696963C9339 :102920002E939597F9018591949190914904989FE0 :10293000802D1124605E7B4FFB01B19685A3F901C8 :10294000A591B4914B2F50E0E82FF0E04E175F07B0 :1029500020F4F90125913491832FFB01B296899FD0 :10296000802D112485A30895AF92BF92CF92DF925C :10297000EF92FF920F931F93CF93DF93182F062FA1 :10298000C1E0D3E08AEFD82E9EEFE92E842F847029 :10299000C82E842F8270B82E842F8870A82E417084 :1029A000F42E8C2F880F8F5FE80ECC2081F04D2FF6 :1029B000400F6C2F610F8EE694E00E94E40B4C2FC9 :1029C000400F6D2F610F8EE694E00E94E40BBB2058 :1029D00081F0402F4D1B6C2F610F8EE694E00E941A :1029E000E40B402F4C1B6D2F610F8EE694E00E948C :1029F000E40BAA2081F04C2F400F612F6D1B8EE657 :102A000094E00E94E40B4D2F400F612F6C1B8EE66B :102A100094E00E94E40BFF2081F0402F4C1B612FBB :102A20006D1B8EE694E00E94E40B402F4D1B612F3E :102A30006C1B8EE694E00E94E40BCD1744F4E7FC97 :102A400004C0D150D394D394ED0CCF5FAACFDF91C3 :102A5000CF911F910F91FF90EF90DF90CF90BF909B :102A6000AF9008950F931F93CF93DF93182F062FE6 :102A7000D42FC0E0CD174CF44C2F400F612F8EE6C1 :102A800094E00E94E40BCF5FF5CFDF91CF911F91CF :102A90000F9108959F92AF92BF92CF92DF92EF92E3 :102AA000FF920F931F93CF93DF93F82EE62EB22E53 :102AB00003E0020FD0E0C3E08AEFC82E1EEF842FA0 :102AC0008170982EAA24A394A20E4270D42EDF5FA8 :102AD000802F8B19180F992081F04C2F440F4A0D2D :102AE0006E2D6C1B8D2F8F0D0E943215402F6E2D79 :102AF0006D1B8C2F8F0D0E943215DD2081F04C2F25 :102B0000440F4A0D6E2D6C1B8F2D8D1B0E943215AC :102B1000402F6E2D6D1B8F2D8C1B0E943215DC17E4 :102B200044F417FD04C0C150C394C3941C0D0E5F40 :102B3000CECFDF91CF911F910F91FF90EF90DF905B :102B4000CF90BF90AF909F9008958F929F92AF9239 :102B5000BF920F931F93CF93DF93CDB7DEB7A197AB :102B60000FB6F894DEBF0FBECDBF19A2423008F4F5 :102B70004AE08E010F5D1F4F842E912CA12CB12CA9 :102B8000A50194010E94D020E62FB901CA0101508D :102B90001109EA3014F4E05D01C0E95CD801EC935E :102BA000232B242B252B61F7C8010E940F13A1961C :102BB0000FB6F894DEBF0FBECDBFDF91CF911F914E :102BC0000F91BF90AF909F908F900895E0916E0409 :102BD000F0916F040190F081E02D682F8EE694E073 :102BE000099480913C0490913D04892BB1F180912E :102BF00044048F7169F584E10E946B08BC01990F50 :102C0000880B990B0E94322126EE3FE34EEC50E4F4 :102C10000E9496210E9401219B0170933B046093C6 :102C20003A04672B91F084E080933904ECE3F4E0FC :102C300080E0419151912417350718F480933904AD :102C400008958F5F8430A9F703C08FEF8093390414 :102C5000809144048F5F8093440408958F929F92E3 :102C6000AF92BF92CF92DF92EF92FF920F931F939A :102C7000CF93DF93FB0124913196C490F0908E04A2 :102C8000022E000C330B121613060CF0D7C0803343 :102C90000CF0D4C0482F082E000C550BFA01EC0D97 :102CA000F11DC7FCFA951E161F060CF0C7C06E5F1B :102CB0007F4FD901179693E0B595A7959A95E1F7BF :102CC00087FF0BC0C80EA49FC001A59F900DB49FA5 :102CD000900D1124681B790B80E04C2D0C2C000CFE :102CE000550B480F511D87FD5A95413351051CF076 :102CF00090E3C92EC81A2535310514F024E530E0DB :102D000010917A041827982F977031E001C0330F83 :102D10009A95EAF7859585958595F4E58F02C0012A :102D20001124835E9D4F00E00C2C000CDD08BB24B9 :102D3000B394402F50E04C155D050CF07FC0FB01B3 :102D4000E490F1E0FF1218C0FB01EC0140E8542FC1 :102D50005E2119F05881532B588350E055954795C3 :102D60004115510519F43196E49040E821965C2F05 :102D7000581B521760F359C0F1101AC0A32EA0942B :102D8000FB01EC0140E8542F5E2119F058815A21D3 :102D9000588350E0559547954115510519F43196E2 :102DA000E49040E821965C2F581B521760F33DC019 :102DB00043E0F41223C0932E9094FB01EC0140E811 :102DC000A82E8C2E881A542F5E2141F0812658811E :102DD00080FE02C0532B01C05921588350E0559505 :102DE00047954115510519F43196E49040E8219634 :102DF0005C2F5A19521728F31B2517C0FB01EC0151 :102E000040E8542F5E2119F058815327588350E031 :102E1000559547954115510519F43196E49040E8D0 :102E200021965C2F581B521760F3330F19F48C5AFC :102E30009F4F31E00F5F6A0F7B1F7BCFDF91CF91F8 :102E40001F910F91FF90EF90DF90CF90BF90AF90C8 :102E50009F908F900895CF93DF93CDB7DEB72B97D8 :102E60000FB6F894DEBF0FBECDBF789484B58260F4 :102E700084BD84B5816084BD85B5826085BD85B51E :102E8000816085BD80916E00816080936E0010929C :102E90008100809181008260809381008091810017 :102EA00081608093810080918000816080938000A8 :102EB0008091B10084608093B1008091B000816006 :102EC0008093B00080917A00846080937A00809132 :102ED0007A00826080937A0080917A00816080938A :102EE0007A0080917A00806880937A001092C10005 :102EF00082E380939F0481E090E0A0E0B0E08093C3 :102F00009B0490939C04A0939D04B0939E048FEF28 :102F100080939004E0E8F7E7259134912130304C1C :102F200009F049C0E6E9F7E7E491E0937904E7E9BD :102F3000F7E7E491E0932404E8E9F7E7E491E0930C :102F40002504E9E9F7E7859194919093270480930C :102F50002604EBE9F7E785919491909329048093F7 :102F6000280487E080934B04EEE9F7E7E491E093CF :102F70004A04EFE9F7E7E491E0939004E0EAF7E729 :102F80008591949190933D0480933C04E2EAF7E7A5 :102F90008591949190933F0480933E04E4EAF7E78F :102FA000859194919093410480934004E6EAF7E779 :102FB000859194912CC09CE3909379041092240401 :102FC0008093250480E293E0909327048093260465 :102FD00084ED93E0909329048093280487E0809304 :102FE0004B0480934A048CEA9DE090933D048093C7 :102FF0003C048EED9DE090933F0480933E0484E773 :103000009EE090934104809340048CE39FE0909372 :1030100043048093420411E0109320046FEF70E0AA :1030200085E00E940C0888E080932A0489E0809360 :103030002B0487E080932C0486E080932D0404E029 :1030400000932E0482E080932F0481E1809330046A :103050000E9453138CE39FE090933B0480933A04C7 :1030600000933904109344048BE080937C048DE03A :1030700080937B0480E180937D048EE080937F04C5 :103080008FE080937E041093760410938E04109248 :103090008F0410937504E2EDF8E0E491EF5FE093A4 :1030A0007204E3EDF8E0E491EF5FE093730484EDE4 :1030B00098E090938D0480938C041FB7F8948091CE :1030C000CB01811127C0E9ECFAE08491EDEDFAE043 :1030D0009491E82FF0E0EE0FFF1FEC58FF4FA59101 :1030E000B491EC91E92321F461E08AE00E94A00709 :1030F00061E08AE00E94D0078CB580618CBD8CB500 :1031000080648CBD61E08DE00E94D00761E08BE0BF :103110000E94D0078091CB018F5F8093CB011FBFAE :103120008CB58F7D8CBD8CB58C7F81608CBD8DB551 :1031300081608DBD8CB58C608CBD61E080917C041C :103140000E94D00761E080917B040E94D00761E07B :1031500080917D040E94D00780917F0418161CF492 :1031600061E00E94D00780917E0418161CF461E093 :103170000E94D00780917F04181674F460E00E94CA :10318000A0076AE070E080E090E00E94A70861E09C :1031900080917F040E94A00780917B04082E000C80 :1031A000990BFC01E154F54FE491F0E0EE0FFF1FA5 :1031B000E258FF4F25913491309383042093820489 :1031C000FC01ED52F54FE491E093890480917C0479 :1031D000082E000C990BFC01E154F54FE491F0E04E :1031E000EE0FFF1FE258FF4F259134913093810479 :1031F00020938004FC01ED52F54FE491E0938804A4 :1032000080917E04082E000C990BFC01E154F54FCF :10321000E491F0E0EE0FFF1FE258FF4F259134914B :103220003093850420938404FC01ED52F54FE49122 :10323000E0938A0480917D04082E000C990BFC0118 :10324000E154F54FE491F0E0EE0FFF1FE258FF4F1D :10325000259134913093870420938604FC01ED522C :10326000F54FE491E0938B0461E28EE694E00E94D6 :10327000800964E18EE694E00E94800980917904DF :1032800087FF03C08FE780937904609179046068B9 :103290008EE694E00E94800960E28EE694E00E944F :1032A00080096CE08EE694E00E9480098EE694E04E :1032B0000E94A10911E01093490484E080936D04F9 :1032C00080E790E0909355048093540460E080E0A0 :1032D0000E94791461E070E083E00E940C08809104 :1032E000B100887F81608093B100F89410928000D3 :1032F00010928100109285001092840088E191E084 :1033000090938900809388008091810088608093E9 :1033100081008091810081608093810080916F00A5 :10332000826080936F0078940E94F115809136043A :10333000813019F410924A041CC08091390488230A :10334000C9F380E00E943C1388E890E09093530416 :10335000809352041092580410925704109346041C :1033600089E080935B0410925E04109260041092D6 :10337000620480919004882309F4E7C081E080937F :10338000760410927504109274041092380480939D :103390008E0480938F04FCEE6F2EF1E07F2EACE064 :1033A000FA2E0AE718E0EE24E394B9E0DB2E0E943F :1033B000AD0E8823E1F3F3018491882319F0C090C6 :1033C000730401C0C12C62EB70E080E00E942E16F5 :1033D00062E271E08CE08C0D0E942E161092770450 :1033E000F09278048CEE91E00E94A7138091720411 :1033F000982F990F890F81958D5A8093770480912A :103400007304982F990F890F8195835D80937804B9 :103410008091910484FF03C08EEA90E002C08AEAA2 :1034200090E00E94C81380917204982F990F890F21 :1034300081958D5A80937704809178048F5F809373 :10344000780480914A04882319F086EA90E002C04B :1034500082EA90E00E94C81380917204982F990F1D :10346000890F81958D5A80937704809178048F5FBE :10347000809378048EE990E00E94C813809136040E :10348000813039F580914A048F5F60914B0487FD4C :1034900009C0082E000C990B70E06F5F7F4F0E94EF :1034A0004722682F60934A0480E00E943C131093E7 :1034B0005304009352041092580410925704E0925F :1034C0004604D0925B0410925E0410926004109245 :1034D0006204809135048130B9F080919004482FC6 :1034E00050E060E070E08090910490909204A09091 :1034F0009304B090940484169506A606B70608F4C3 :10350000D3C48F3F09F4D0C48FEF8093900480E040 :103510000E943C1380E00E943C1386E990E0909367 :10352000530480935204109258041092570481E07F :103530008093460499E090935B0410925E0410928D :1035400060041092620480933804E2E04E2E512C05 :1035500081E00E943F0D1092CA011092C901809132 :103560003604813009F4A8C481E00E943F0D109216 :10357000C8011092C7015092C6014092C50181E076 :1035800090E09093C4018093C30186E990E090930A :10359000D3018093D2015092D1014092D0010E9478 :1035A000200A0E94E7098091C5019091C60197FD0C :1035B000CFC30E94AD0E8823E1F3809131048130A6 :1035C00009F49FC480913304813009F49FC4809131 :1035D0003204813009F49FC480913404813009F4AD :1035E000A1C48091C001883308F45AC00E94E70941 :1035F0000E94200A0091C3011091C4010F5F1F4F68 :103600001093C4010093C3010330110519F0053074 :10361000110549F48091C5019091C60101969093DE :10362000C6018093C5018091D0019091D101833072 :1036300091052CF401969093D1018093D001809153 :10364000D2019091D301833391052CF04997909347 :10365000D3018093D20180E00E943F0D1092CA01F5 :103660001092C90148E251E063E080E00E94AB0D96 :10367000A80163E070E08CE290E00E94FD0D0E94E2 :103680002C0E8EE694E00E94A10964EC79E080E0C3 :1036900090E00E94A70880E00E943F0D0E947E0AF1 :1036A0008091A4019091A50101969093A50180932A :1036B000A4012091D2013091D301821793072CF1FC :1036C0001092A5011092A4012091D0013091D10156 :1036D00060E001C06F5F862F90E082179307ACF423 :1036E000880F991FFC01EA55FE4F40815181452BFF :1036F00089F7DC01A854BE4F80E48C9380E11196D9 :103700008C9381E090E09183808346EA51E05F836F :103710004E8330EBE32E31E0F32E49EBC42E41E033 :10372000D42E58EB252E51E0352E3A8229825601AF :103730001B861A865FC0F701208131812150310933 :103740002330310508F05FC0F901EF5EFE4F808144 :10375000082E000C990BF901E25FFE4F4081F90140 :10376000E55FFE4F5081652F550F770B7C836B8390 :10377000F901E85FFE4F7080F901EB5FFE4F708149 :103780007D838A849B84880C991C8401040F111DFD :1037900008541E4FF8016080860D8083D5016C911E :1037A000E981FA8180810E94320A0197C1F5D8012E :1037B0006C920E94452024E030E040E050E00E94FE :1037C0009E20F701718360834A855B854F5F5F4F61 :1037D0005B874A8752E0E50EF11C62E0A60EB11C41 :1037E00089819A8102969A838983AA85BB851397DA :1037F00009F453C0AE81BF818D919D91BF83AE838B :10380000019711F798CF83E08D8377247394AFEFFE :10381000BFEFBC83AB8340E081E090E0B2CF23E018 :1038200030E0072D10E0C401800F911FAC01485417 :103830005E4F3A01D3018C91BB818B0FF3018083E2 :10384000D5016C91E981FA818081298738870E94AE :10385000320A29853885019709F431E0215051F762 :1038600031110EC00E94452024E630E040E050E0D7 :103870000E949E2063337105810591050CF059C3A8 :10388000080D191DF801E854FE4F9B81990F2B81FB :10389000920F8081891B808397CF8091CE01909178 :1038A000CF0101969093CF018093CE01029709F04A :1038B0004EC08091AE019091AF01892B41F581E01E :1038C00090E09093AF018093AE018091AC0198E3BA :1038D000899F8001181B1124B8016E5A7C4F84E027 :1038E00090E00E94B40BB80160537D4F85E090E0FA :1038F0000E94B40BB80164597D4F84E090E00E94AF :10390000B40BB801665C7D4F85E090E01AC0109260 :10391000AF011092AE016AE372E085E090E00E9490 :10392000B40B6CE672E084E090E00E94B40B60EDB2 :1039300072E085E090E00E94B40B62E573E084E001 :1039400090E00E94B40B1092CF011092CE018091B2 :10395000AC01882391F08091A2019091A30101967E :103960009093A3018093A201883C910534F01092BA :10397000AC011092A3011092A2010091B601109126 :10398000B7011093CD010093CC012091C10130917A :10399000C2013093B7012093B601F090BE01EF2D24 :1039A000F0E07E97F093CA01E093C901E090BF0177 :1039B0004E2D50E0445151095093A1014093A00174 :1039C000F7FF0BC01092CA011092C90157FF13C034 :1039D0001092A1011092A00102C057FDF9CF809171 :1039E000C9019091CA018C9734F08BE290E09093DA :1039F000CA018093C9018091A0019091A1014197D2 :103A000034F080E190E09093A1018093A0012130F7 :103A1000310509F458C01CF4232B41F06EC022304C :103A20003105E1F023303105A9F167C0F090BE0106 :103A3000F394F092BE016E2D8F2D0E94320A0197F1 :103A400021F41093B7010093B601EA85FB85319705 :103A5000FB87EA87EF2B51F78DEF18C0F090BE017E :103A6000FA94F092BE016E2D8F2D0E94320A0197BA :103A700021F41093B7010093B6014A855B8541504C :103A800051095B874A87452B49F783E08F0D809367 :103A9000BE0133C0E090BF01EA94E092BF016E2DF9 :103AA0008F2D0E94320A019721F41093B7010093E1 :103AB000B6016A857B85615071097B876A87672BB0 :103AC00049F783E017C0E090BF01E394E092BF01A3 :103AD0006E2D8F2D0E94320A019721F41093B701A9 :103AE0000093B6018A859B8501979B878A87892BDE :103AF00051F78DEF8E0D8093BF018091B6019091AB :103B0000B70181309105F9F034F4892B51F510910A :103B1000BE0181E00CC08230910531F0039709F5B8 :103B20001091BF016FEF12C01091BE018FEF810F96 :103B30008093BE016091BF010E94320A019789F40F :103B40001093BE010EC01091BF0161E0610F609340 :103B5000BF018091BE010E94320A019711F41093B7 :103B6000BF018091BF018333D1F48091BE018E30BB :103B700038F480919E01813091F410929E0108C02A :103B8000803768F080919F01813049F410929F0145 :103B900091E09093AC011092A3011092A201E8EB86 :103BA000F1E095E128E334E6D6018C91811103C060 :103BB0002C93308305C0883318F0D6011C92908373 :103BC000B2E0CB0ED11C329641E0E03CF40761F745 :103BD000A12CB12CD12C00E010E08091BE0190E02E :103BE000D1012C9130E0AC01475051092417350721 :103BF0000CF44BC00796821793070CF446C08091D3 :103C0000BF0190E0F501E854FE4F218130E0AC01A6 :103C10004750510924173507C4F1079682179307B7 :103C2000A4F18091AC01882371F1D801A85EBE4F48 :103C30008C91D1018C93D801AC5EBE4F8C91818365 :103C4000F501EA55FE4F118210829CEBE92E92E0BD :103C5000F92EB7018EE190E00E94B40BB2E3EB1AAB :103C6000F108E8ECEE16F104A1F78091C7019091FC :103C7000C8010A969093C8018093C7011092A501CC :103C80001092A40102C0DD24D3940F5F1F4FF2E015 :103C90002F0E311C22E0A20EB11C46E0A416B10486 :103CA00009F09BCFDD2009F450C063E070E0709311 :103CB000B7016093B60181E090E09093AF018093EB :103CC000AE010E947E0A00E010E0B80182E390E0BD :103CD0000E94B40B0E5C1F4F043F71E01707A9F759 :103CE0000E94AD0E8823E1F30E947E0A0E94AD0E71 :103CF0008823E1F31092AF011092AE010E947E0A78 :103D0000B80182E390E00E94B40B0E5C1F4F083EA6 :103D100083E01807A9F70E94AD0E8823E1F30E9403 :103D20007E0A64EF71E080E090E00E94A70891E0D5 :103D30009093AD018091C5019091C6010197909338 :103D4000C6018093C5010E94E7090E947E0A2BCC20 :103D50000E94E70980E090E00E948820082F10E090 :103D6000102F00271093D6010093D50181E090E039 :103D70000E948820082B1093D6010093D501109241 :103D8000D4016091C7017091C801061717078CF420 :103D90007093D6016093D50181E090E00E9490205D :103DA0006091C80180E090E00E94902081E08093C3 :103DB000D4011092CA011092C90180E00E943F0D07 :103DC0004DE351E060E080E00E94AB0D4FE251E036 :103DD00061E080E00E94AB0D4DE351E063E080E0E4 :103DE0000E94AB0D0E942C0E0E94AD0E8823E1F3C1 :103DF00064EC79E080E090E00E94A70880E00E94F7 :103E00003F0D4AE351E060E080E00E94AB0D4AE3E1 :103E100051E063E080E00E94AB0D4091D5015091EC :103E2000D60162E070E088E190E00E94FD0D809193 :103E3000D401811106C04AE451E061E00E94AB0D5B :103E400012C046E551E061E080E00E94AB0D0CEB52 :103E500012E0B8018EE190E00E94B40B0253110908 :103E6000083C1105B1F70E94AD0E8823E1F364EC24 :103E700079E080E090E00E94A70881E00E943F0D79 :103E80008EE694E00E94A10980913504813009F00A :103E900057C00E94AD0E8823E1F365E070E080E03A :103EA00090E00E94A708F0CF80913704813009F09C :103EB0007ECA0E94D6137BCA60E080E090E00E9438 :103EC000902060E081E090E00E9490204BE351E080 :103ED00060E080E00E94AB0D40E651E061E080E0F0 :103EE0000E94AB0D4BE351E063E080E00E94AB0D1C :103EF0000E94AD0E8823E1F360ED77E080E090E072 :103F0000BACF5092C2014092C1015CCB1092C20163 :103F10001092C1015CCBA3E0B0E0B093C201A093CA :103F2000C1015ACB81E090E09093C2018093C1011E :103F300058CBBD818B2F90E0F70191838083A0CC7B :103F40008FE3809359048091490480935A0410921E :103F50006A04109269041092680481E080935C0402 :103F600080931D0410925D040E94AD0E8823E1F33E :103F70001092CA011092C90100E010E0C8010E942D :103F80003C0E8EE694E00E94A1090115110531F462 :103F900064EA76E080E090E00E94A7080D5F1F4F82 :103FA0000A32110559F780913504813041F480E0DF :103FB00090E0892B09F4CCCA0E940000C9CA809104 :103FC00036048130A1F384E690E00E943C0E0E940A :103FD000AD0E8823E1F3E7CF1092710410927004C4 :103FE00080E291E090936F0480936E0408958F9225 :103FF0009F92AF92BF92CF92DF92EF92FF92CF93B8 :10400000DF93EC01688179818A819B81611571055B :104010008105910521F464E279ED8BE597E02DE1CE :1040200033EF41E050E00E949E2049015A019B017C :10403000AC01A7EAB1E40E94BD206B017C01ACEEAB :10404000B4EFA50194010E94CB20DC01CB018C0DC3 :104050009D1DAE1DBF1DB7FF03C00197A109B0484C :1040600088839983AA83BB83BC01CD019F77DF91AD :10407000CF91FF90EF90DF90CF90BF90AF909F9047 :104080008F9008950E94F71F089581E091E00E94AB :10409000F71F08956093010170930201809303015B :1040A000909304010895BB274A3031F4992322F4F8 :1040B000BDE2909581959F4F0C945F20BB27FB013B :1040C0005527AA27880F991FAA1FA41710F0A41B11 :1040D00083955051B9F7A05DAA3308F0A95DA1936B :1040E000009779F7B111B1931192CB010C9478201C :1040F000DC01FC01672F71917723E1F7329704C04F :104100007C916D9370836291AE17BF07C8F30895D9 :10411000F999FECF92BD81BDF89A992780B508958F :10412000262FF999FECF1FBA92BD81BD20BD0FB6D3 :10413000F894FA9AF99A0FBE01960895052E97FB06 :104140001EF400940E94B52057FD07D00E94D02095 :1041500007FC03D04EF40C94B52050954095309553 :1041600021953F4F4F4F5F4F0895909580957095E3 :1041700061957F4F8F4F9F4F08950E94F220A59F1A :10418000900DB49F900DA49F800D911D1124089552 :10419000B7FF0C94BD200E94BD20821B930B089595 :1041A000A1E21A2EAA1BBB1BFD010DC0AA1FBB1F3B :1041B000EE1FFF1FA217B307E407F50720F0A21BAD :1041C000B30BE40BF50B661F771F881F991F1A941A :1041D00069F760957095809590959B01AC01BD0144 :1041E000CF010895A29FB001B39FC001A39F700D9E :1041F000811D1124911DB29F700D811D1124911DEF :1042000008950E94752188F09F5798F0B92F99273B :10421000B751B0F0E1F0660F771F881F991F1AF0B1 :10422000BA95C9F714C0B13091F00E948F21B1E066 :1042300008950C948F21672F782F8827B85F39F065 :10424000B93FCCF3869577956795B395D9F73EF44A :1042500090958095709561957F4F8F4F9F4F0895F2 :10426000E89409C097FB3EF4909580957095619510 :104270007F4F8F4F9F4F9923A9F0F92F96E9BB27C6 :104280009395F695879577956795B795F111F8CF42 :10429000FAF4BB0F11F460FF1BC06F5F7F4F8F4FAD :1042A0009F4F16C0882311F096E911C0772321F0A3 :1042B0009EE8872F762F05C0662371F096E8862F3B :1042C00070E060E02AF09A95660F771F881FDAF792 :1042D000880F9695879597F9089557FD9058440F44 :1042E000551F59F05F3F71F04795880F97FB991F55 :1042F00061F09F3F79F08795089512161306140612 :10430000551FF2CF4695F1DF08C01616170618069E :10431000991FF1CF86957105610508940895E89479 :10432000BB2766277727CB0197F908950E94A9211B :104330000C941A220E940C2238F00E94132220F0C2 :10434000952311F00C9403220C94092211240C944F :1043500090210E946D2170F3959FC1F3950F50E05D :10436000551F629FF001729FBB27F00DB11D639F27 :10437000AA27F00DB11DAA1F649F6627B00DA11DCD :10438000661F829F2227B00DA11D621F739FB00D73 :10439000A11D621F839FA00D611D221F749F3327E3 :1043A000A00D611D231F849F600D211D822F762F7C :1043B0006A2F11249F5750409AF0F1F088234AF059 :1043C000EE0FFF1FBB1F661F771F881F91505040C5 :1043D000A9F79E3F510580F00C9403220C94902184 :1043E0005F3FE4F3983ED4F3869577956795B7954C :1043F000F795E7959F5FC1F7FE2B880F911D969566 :10440000879597F9089597F99F6780E870E060E0D5 :1044100008959FEF80EC089500240A94161617065D :1044200018060906089500240A94121613061406A5 :1044300005060895092E0394000C11F4882352F008 :10444000BB0F40F4BF2B11F460FF04C06F5F7F4FC0 :104450008F4F9F4F0895DB018F939F930E94BD2044 :10446000BF91AF91A29F800D911DA39F900DB29F10 :10447000900D11240895991B79E004C0991F961797 :1044800008F0961B881F7A95C9F78095089597FBC9 :10449000072E16F4009407D077FD09D00E945B2206 :1044A00007FC05D03EF4909581959F4F0895709537 :1044B00061957F4F0895AA1BBB1B51E107C0AA1F3E :1044C000BB1FA617B70710F0A61BB70B881F991FB5 :1044D0005A95A9F780959095BC01CD010895EE0FEE :1044E000FF1F0590F491E02D099481E090E0F8948D :0844F0000C947A22F894FFCF2E :1044F80001010000000001020001000101FF0100AC :104508000101FFFF1C1C1C2842374D37000000002A :10451800730CA905720771074C4556454C3A00477C :10452800414D4520204F564552002D2D2D2D2D2D26 :104538002D2D2D2D2D2D2D2D2D004849474820534B :10454800434F52453A004E455720484947483A009C :1045580048492053434F5245205245534554005033 :104568002041204B2D4D2041204E00616E64792062 :104578006A61636B736F6E00507265737320412EAE :0E4588002E2E000D0A004C4F414445520000FB :00000001FF ================================================ FILE: PAK-MAN_MAKERbuino/PAK-MAN_MAKERbuino.ino ================================================ /* 2018 * * PAK-MAN 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 Pac-Man 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 //imports the Gamebuino library #include //creates a Gamebuino object named gb Gamebuino gb; #include #include "font6x8AJ4.h" #define DIR_RIGHT 0 #define DIR_DOWN 1 #define DIR_LEFT 2 #define DIR_UP 3 #define PACMAN 1 #define GHOST1 2 #define GHOST2 4 #define GHOST3 8 // Probability of a ghost taking a branch to the side if it can #define BRANCH_PROBABILITY 50 // How many ghosts on level 1 #define INITIAL_GHOSTS 2 // Initial number of cycles before a ghost is released - reduces according to next two defines down to a min level #define INITIAL_GHOST_DELAY 150 #define GHOST_DELAY_REDUCTION 25 #define GHOST_DELAY_MINIMUM 50 // How long does powerUp mode last? #define POWER_LENGTH 200 /* ------ This section needs to be updated if you change the screen bitmap ------ ------------------------------------------------------------------------------*/ // Where on the screen to the ghosts appear when they pop out onto the playing screen? #define GHOST_LAUNCH_X 64 #define GHOST_LAUNCH_Y 16 // How many pills are there on the screen? #define NO_PILLS 56 // Where are the ghosts and pacman drawn initially? // In each of these, the order is - Ghost1, Ghost2, Ghost3, PAC-MAN static const byte startPosX[] = {66, 55, 77, 55}; static const byte startPosY[] = {28, 28, 28, 40}; static const byte startDirections[] = {DIR_UP, DIR_UP, DIR_UP, DIR_RIGHT}; // Where are the big pills? (their row is fixed as the last one) static const byte bigPillLocations[2] PROGMEM = {14, 119}; // How many pills on each row? for the six rows 1-7 that you are allowed pills static const byte readRow[6][2] PROGMEM = { 0, 12, 12, 23, 23, 31, 31, 34, 34, 45, 45, 56 }; // Where are the pills (x,y) static const byte pillLocations[NO_PILLS][2] PROGMEM = { 14, 8, 24, 8, 36, 8, 46, 8, 55, 8, 63, 8, 75, 8, 84, 8, 93, 8, 104, 8, 111, 8, 119, 8, 14, 20, 24, 20, 36, 20, 46, 20, 55, 20, 63, 20, 75, 20, 84, 20, 93, 20, 104, 17, 119, 17, 14, 31, 24, 31, 36, 31, 46, 31, 93, 31, 104, 25, 111, 25, 119, 25, 104, 37, 111, 37, 119, 37, 14, 43, 24, 43, 36, 43, 46, 43, 55, 43, 63, 43, 75, 43, 84, 43, 93, 43, 104, 46, 119, 46, 14, 55, 24, 55, 36, 55, 46, 55, 55, 55, 63, 55, 75, 55, 84, 55, 93, 55, 104, 55, 111, 55 }; const byte PROGMEM openScreen[] = { 64,25, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0xf8,0x00,0x00,0x00,0x7c,0x00,0x00, 0x03,0xff,0x00,0x00,0x01,0xff,0x00,0x00, 0x07,0xff,0x80,0x00,0x03,0x79,0x80,0x00, 0x0f,0xff,0x80,0x00,0x06,0x20,0x80,0x00, 0x0f,0xff,0xc0,0x00,0x0c,0x00,0xc0,0x00, 0x1f,0xff,0xc0,0x00,0x08,0x00,0x40,0x00, 0x1f,0xff,0x00,0x00,0x08,0xe3,0x40,0x00, 0x3f,0xf8,0x00,0x00,0x18,0xe3,0x60,0x00, 0x3f,0xe1,0x99,0x99,0x98,0xe3,0x60,0x00, 0x3f,0xe1,0x99,0x99,0x98,0x00,0x60,0x00, 0x3f,0xf8,0x00,0x00,0x18,0x00,0x60,0x00, 0x3f,0xfc,0x00,0x00,0x18,0x00,0x60,0x00, 0x1f,0xff,0x00,0x00,0x18,0x00,0x60,0x00, 0x0f,0xff,0xc0,0x00,0x19,0xcc,0xe0,0x00, 0x0f,0xff,0xc0,0x00,0x1f,0xfe,0x60,0x00, 0x03,0xff,0x00,0x00,0x1d,0xff,0xe0,0x00, 0x03,0xfe,0x00,0x00,0x18,0xcc,0x60,0x00, 0x00,0xf8,0x00,0x00,0x18,0xcc,0x60,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,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); // Game functions void playPacman(void); void levelUp(int); void showScore(void); void displayTitle(void); int checkCollision(byte xpos, byte ypos); void initScreen(void); void initLevel(void); void newDirection(byte ghostNo); byte hitGhosts(void); void movePacman(void); void pacDie(void); void eatenGhost(void); void moveGhosts(void); byte drawPacman(byte c, byte r); // 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 (playPacman()) 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 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 int mouth = 0; // Is his mouth open? int directions[4]; // Directions of the four possible characters (pacman = 0, ghosts are 1-3) int commandDir; // The desired direction set by input byte pillsEaten = 0; // How many pills have been eaten (there are 63 altogether) int ghostCounter = 0; // How many ghosts int ghostsActive[3]; // How many ghosts are out? int count = 0; // Counter int oldDir; // Holds previous direction int level = 0; // Obvious I hope int releaseDelay; // The delay between ghosts being let out int maxGhosts; // How many ghosts maximum on this level? bool powerUp; // Is powerUp move active? int powerCounter; // Counts the length of powerUp mode byte locations[4][2]; // The x,y location of the sprites byte pillsActive[NO_PILLS]; // Which of the pills is still on the screen? byte bigPillsActive[2]; // Which of the two big pills is still active? byte currentX, currentY, currentRow, currentCol; int screenTop, screenLeft; // Artwork static const byte pacOpen[4][8] PROGMEM = { 0x3C, 0x7E, 0xFF, 0xFF, 0xE7, 0xE7, 0xC3, 0x42, 0x7C, 0xFE, 0x3F, 0x0F, 0x0F, 0x3F, 0xFE, 0x7C, 0x42, 0xC3, 0xE7, 0xE7, 0xFF, 0xFF, 0x7E, 0x3C, 0x3E, 0x7F, 0xFC, 0xF0, 0xF0, 0xFC, 0x7F, 0x3E }; static const byte pacClosed[] PROGMEM = {0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C}; static const byte ghost[2][8] PROGMEM = {0x7C, 0xFE, 0x22, 0xEB, 0xFF, 0x62, 0xEA, 0x7C, 0x7C, 0xC2, 0x0A, 0x83, 0x83, 0x0A, 0x82, 0x7C}; static const byte PAClives[] PROGMEM = {0x1C, 0x3C, 0x7E, 0x66, 0x66, 0x62, 0x40}; static const byte openBmp[] PROGMEM = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFC, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFC, 0xFC, 0xF8, 0x70, 0x60, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xEF, 0xC7, 0x87, 0x83, 0x83, 0x01, 0x00, 0x00, 0x10, 0x38, 0x7C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7C, 0x38, 0x10, 0x00, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x3F, 0x3F, 0x3E, 0x1E, 0x1E }; static const byte gameScreen[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x04, 0xF4, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x13, 0x10, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x10, 0x13, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xE4, 0x04, 0xE4, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xE4, 0x04, 0xE4, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xE4, 0x04, 0xE4, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xE4, 0x04, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x20, 0xA0, 0xA0, 0xA0, 0xA0, 0x20, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x2F, 0x2F, 0x2F, 0x2F, 0x20, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xFD, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0xFD, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x3E, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x20, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x9F, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x9F, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x7E, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x04, 0xF4, 0xF4, 0xF4, 0xF4, 0x04, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04, 0x05, 0x05, 0x05, 0x05, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x20, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x20, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xC7, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x08, 0xC8, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x20, 0x1F, 0x00 }; void beep(int bCount,int bDelay){ if (mute) return; for (int i = 0; i<=bCount; i++){ digitalWrite(3,HIGH); for(int i2=0; i2 21) lxn2 = 35; } } } // Arduino stuff - setup void setup() { // initialize the Gamebuino object gb.begin(); //display the main menu: gb.titleScreen(F("PAK-MAN"),openScreen); } void displayOpenScreen(int incr) { lcdDisplay_fillscreen(1); if (incr < 99) screenLeft = incr; lcdDisplay_char_f6x8(0, 1, "P A K-M A N"); 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; 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); playPacman(); 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 < 42 ; 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 showScore(void) { lcdDisplay_char_f6x8(0, 2, "SCORE:"); doNumber(44, 2, score); } // Handle what happens at the end of a level void levelUp() { initScreen(); initLevel(); level++; if ((level == 3) || (level == 5)) lives++; if (maxGhosts < 3) maxGhosts++; if (releaseDelay > GHOST_DELAY_MINIMUM) releaseDelay -= GHOST_DELAY_REDUCTION; lcdDisplay_fillscreen(0x00); screenLeft = 0; lcdDisplay_char_f6x8(0, 3, "LEVEL:"); doNumber(44, 3, level); showScore(); gb.display.update(); delay(2500); lcdDisplay_fillscreen(0x00); displayScreen(); } 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 lcdDisplay_send_byte(uint8_t input) { for (int by = 0; by < 8; by++) { if ((input >> by & B00000001) == 0) { if ( (currentX >= screenLeft) && (currentX <= screenLeft + 84)) { gb.display.setColor(WHITE, BLACK); gb.display.drawPixel(currentX - screenLeft, (currentY) * 8 + by); } } else { if ( (currentX >= screenLeft) && (currentX <= screenLeft + 84)) { gb.display.setColor(BLACK, WHITE); gb.display.drawPixel(currentX - screenLeft, (currentY) * 8 + by); } } } 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++; } } byte drawPacman(byte c, byte r) { byte out = 0; if ( (r >= locations[3][0]) && (r < locations[3][0] + 8) ) { // draw the pacMan if any of his body is on this column if (c == locations[3][1] / 8) { if (mouth == 1) { out = (pgm_read_byte(&pacOpen[directions[3]][r - locations[3][0]]) << locations[3][1] % 8); } else { out = (pgm_read_byte(&pacClosed[r - locations[3][0]]) << locations[3][1] % 8); } } else if (c == locations[3][1] / 8 + 1) { if (mouth == 1) { out = (pgm_read_byte(&pacOpen[directions[3]][r - locations[3][0]]) >> (8 - locations[3][1] % 8) ); } else { out = (pgm_read_byte(&pacClosed[r - locations[3][0]]) >> (8 - locations[3][1] % 8) ); } } } return out; } byte drawGhostSprite(byte c, byte r, byte lxn) { byte out = 0; if ( (r >= locations[lxn][0]) && (r < locations[lxn][0] + 8) ) { if (c == locations[lxn][1] / 8) { out |= (pgm_read_byte(&ghost[powerUp][r - locations[lxn][0]]) << locations[lxn][1] % 8); } else if (c == locations[lxn][1] / 8 + 1) { out |= (pgm_read_byte(&ghost[powerUp][r - locations[lxn][0]]) >> (8 - locations[lxn][1] % 8) ); } } return out; } byte drawPills(byte c, byte r) { byte out = 0; // draw the pills and also check whether pacman is eating an active pill if (c > 0 && c < 7) { for (byte lxn = pgm_read_byte(&readRow[c - 1][0]); lxn < pgm_read_byte(&readRow[c - 1][1]); lxn++) { if (pillsActive[lxn]) { // this pill is active if (r == pgm_read_byte(&pillLocations[lxn][0])) { // draw the pills if they appear here // check if pac-man has eaten a pill if (pgm_read_byte(&pillLocations[lxn][0]) > locations[3][0] && pgm_read_byte(&pillLocations[lxn][0]) < locations[3][0] + 8 && pgm_read_byte(&pillLocations[lxn][1]) > locations[3][1] && pgm_read_byte(&pillLocations[lxn][1]) < locations[3][1] + 8) { pillsActive[lxn] = 0; pillsEaten++; score += level; break; } if (c == pgm_read_byte(&pillLocations[lxn][1]) / 8) { out |= ((B00000001) << pgm_read_byte(&pillLocations[lxn][1]) % 8); } else if (c == pgm_read_byte(&pillLocations[lxn][1]) / 8 + 1) { out |= ((B00000001) >> (8 - pgm_read_byte(&pillLocations[lxn][1]) % 8) ); } } } } } return out; } byte drawBigPills(byte c, byte r) { byte out = 0; for (byte lxn = 0; lxn < 2; lxn++) { if (bigPillsActive[lxn] == 1 && c == 6 && (r == pgm_read_byte(&bigPillLocations[lxn]) || r == (pgm_read_byte(&bigPillLocations[lxn]) + 1) )) { out |= B11100000; } } return out; } byte createScreenOut(byte c, byte r) { byte out; // 'out' is the byte we're going to write, starts with the background image out = (pgm_read_byte(&gameScreen[c*128+r])); // add pacman, if part of his sprite is on this space out |= drawPacman(c, r); // Draw the lives in the far left column if (r < 7) { for (byte lxn = 3; lxn < 7; lxn++) { if (c == lxn && lives > 6 - lxn) out |= (pgm_read_byte(&PAClives[r])); } } // draw a ghost if any of their sprites are on this column and row for (byte lxn = 0; lxn < 3; lxn++) { out |= drawGhostSprite(c, r, lxn); } out |= drawPills(c, r); // Draw the big pills out |= drawBigPills(c, r); return out; } void displayScreen(void) { int pacDir = directions[3]; byte out; uint8_t *disp; char nextRow[84]; // For each row on the screen (there's 8 - I think of them as columns, hence 'c', I don't know why!) disp = gb.display.getBuffer(); byte c = screenTop /8 + 6; for (byte r= screenLeft; r < screenLeft+84; r++) { nextRow[r-screenLeft] = createScreenOut(c, r); } for (byte c = screenTop /8 + 5; c > screenTop /8; c--) { for (byte r = screenLeft; r < screenLeft+84; r++) { byte thisRow = createScreenOut(c, r); out = ( (thisRow >> screenTop % 8) | (nextRow[r-screenLeft] << 8- screenTop % 8)); nextRow[r-screenLeft] = thisRow; *(disp + r - screenLeft + ((c - screenTop/8) * 84)) = out; } } c = screenTop /8; for (byte r= screenLeft; r < screenLeft+84; r++) { byte thisRow = createScreenOut(c, r); out = ( (thisRow >> screenTop % 8) | (nextRow[r-screenLeft] << 8- screenTop % 8)); *(disp + r - screenLeft + ((c - screenTop/8) * 84)) = out; } //gb.display.update(); } int checkCollision(byte xpos, byte ypos) { int incr = 0; int returnValue = 0; for (byte c = 0; c < 2; c++) { // for each of the two lines that the item might be on incr = (128 * (ypos / 8 + c)) + xpos; for (byte r = 0; r < 8; r++) { // for each of the eight bytes in the sprite if (c == 0) { if ((pgm_read_byte(&gameScreen[incr]) & (0xFF << ypos % 8) ) != 0) returnValue = 1; } else { if ((pgm_read_byte(&gameScreen[incr]) & (0xFF >> (8 - ypos % 8) ) ) != 0) returnValue = 1; } incr++; } } return returnValue; } void moveGhosts(void) { int travelDirection; // which way's the ghost going? byte travelParam; // zero for travelling in x, one for y int searchDirection; // +1 to search up, -1 to search down for side exits byte paramSelection; // zero for xPosition, one for yPosition byte switchDirection; // which direction to switch to if there's a switch ghostCounter++; // Pop the next ghost out, if it's time if (ghostCounter >= releaseDelay) { ghostCounter = 0; for (byte lxn = 0; lxn < maxGhosts; lxn++) { if (ghostsActive[lxn] == 0) { locations[lxn][0] = GHOST_LAUNCH_X; locations[lxn][1] = GHOST_LAUNCH_Y; ghostsActive[lxn] = 1; break; } } } for (byte lxn = 0; lxn < 3; lxn++) { if (ghostsActive[lxn] == 1) { char clash = 0; // Start with a default set (which is DIR_RIGHT) then only change the things that differ from this in the switch statements below // (saves two bytes of code, but every bit helps!) travelDirection = 1; travelParam = 0; searchDirection = -1; paramSelection = 1; switchDirection = DIR_UP; switch (directions[lxn]) { case (DIR_LEFT): travelDirection = -1; searchDirection = 1; switchDirection = DIR_DOWN; break; case (DIR_UP): travelDirection = -1; travelParam = 1; paramSelection = 0; switchDirection = DIR_LEFT; break; case (DIR_DOWN): travelParam = 1; searchDirection = 1; paramSelection = 0; switchDirection = DIR_RIGHT; break; } locations[lxn][travelParam] += travelDirection; // Move the ghosts along according to which way they are heading - if there's a clash, then backtrack if (checkCollision(locations[lxn][0], locations[lxn][1]) == 1) { locations[lxn][travelParam] -= travelDirection; newDirection(lxn); } else { clash = 0; for (byte lxn2 = 0; lxn2 < 3; lxn2++) { locations[lxn][paramSelection] += searchDirection; if (checkCollision(locations[lxn][0], locations[lxn][1]) == 1) clash = 1; } if (clash == 0 && random(100) > BRANCH_PROBABILITY) directions[lxn] = switchDirection; locations[lxn][paramSelection] -= (searchDirection * 3); } } } } void newDirection(byte ghostNo) { directions[ghostNo] = random(4); } void eatenGhost(void) { for (int i = 700; i>200; i = i - 50){ beep(30,i); } } byte hitGhosts(void) { byte returnCode = 0; for (int lxn = 0; lxn < 3 ; lxn++) { if ((locations[lxn][0] > locations[3][0] - 8) && (locations[lxn][0] < locations[3][0] + 8) && (locations[lxn][1] > locations[3][1] - 8) && (locations[lxn][1] < locations[3][1] + 8)) { if (powerUp == 1) { locations[lxn][0] = startPosX[lxn]; locations[lxn][1] = startPosY[lxn]; ghostsActive[lxn] = 0; eatenGhost(); score += 10; ghostCounter = 0; } else returnCode = 1; } } return returnCode; } void pacDie(void) { int i; directions[3] = DIR_UP; mouth = 1; displayScreen(); for (i = 0; i<500; i = i+ 50){ beep(50,i); } while(!gb.update()); displayScreen(); while(!gb.update()); mouth = 0; displayScreen(); for (i = 500; i<1000; i = i+ 50){ beep(50,i); } while(!gb.update()); displayScreen(); delay(500); stopAnimate = 1; } void movePacman(void) { count++; if (count == 2) { if (mouth == 0) {mouth = 1; beep(4,850-powerUp*200);beep(5,720-powerUp*200);beep(4,620-powerUp*200);beep(5,570-powerUp*200);} else {mouth = 0; beep(5,570);beep(4,620);beep(5,720);beep(4,850);} count = 0; } if (powerUp == 1) { powerCounter++; if (powerCounter >= POWER_LENGTH) { powerUp = 0; powerCounter = 0; } } oldDir = directions[3]; directions[3] = commandDir; screenLeft = locations[3][0] - 30; screenTop = locations[3][1] - 20; if (screenLeft < 0) screenLeft = 0; if (screenTop < 0) screenTop = 0; if (screenLeft > 43) screenLeft = 43; if (screenTop > 16) screenTop = 16; switch (directions[3]) { case (DIR_RIGHT): for (int lxn = 0; lxn < 3; lxn++) { locations[3][0]++; if (checkCollision(locations[3][0], locations[3][1]) == 1) { directions[3] = oldDir; } } locations[3][0] -= 3; break; case (DIR_LEFT): for (int lxn = 0; lxn < 3; lxn++) { locations[3][0]--; if (checkCollision(locations[3][0], locations[3][1]) == 1) { directions[3] = oldDir; } } locations[3][0] += 3; break; case (DIR_UP): for (int lxn = 0; lxn < 3; lxn++) { locations[3][1]--; if (checkCollision(locations[3][0], locations[3][1]) == 1) { directions[3] = oldDir; } } locations[3][1] += 3; break; case (DIR_DOWN): for (int lxn = 0; lxn < 3; lxn++) { locations[3][1]++; if (checkCollision(locations[3][0], locations[3][1]) == 1) { directions[3] = oldDir; } } locations[3][1] -= 3; break; } switch (directions[3]) { case (DIR_RIGHT): locations[3][0] += 1; if (checkCollision(locations[3][0], locations[3][1]) == 1) locations[3][0] -= 1; break; case (DIR_LEFT): locations[3][0] -= 1; if (checkCollision(locations[3][0], locations[3][1]) == 1) locations[3][0] += 1; break; case (DIR_UP): locations[3][1] -= 1; if (checkCollision(locations[3][0], locations[3][1]) == 1) locations[3][1] += 1; break; case (DIR_DOWN): locations[3][1] += 1; if (checkCollision(locations[3][0], locations[3][1]) == 1) locations[3][1] -= 1; break; } // If pacman is on the bottom row, check if he's eaten either of the big pills if (locations[3][1] == 51) { if ((locations[3][0] < 14 && bigPillsActive[0] == 1) || (locations[3][0] > 111 && bigPillsActive[1] == 1)) { if (locations[3][0] < 14) bigPillsActive[0] = 0; else bigPillsActive[1] = 0; powerUp = 1; powerCounter = 0; } } } void initLevel(void) { pillsEaten = 0; stopAnimate = 0; for (byte lxn = 0; lxn < NO_PILLS; lxn++) pillsActive[lxn] = 1; bigPillsActive[0] = 1; bigPillsActive[1] = 1; } void initScreen(void) { powerUp = 0; powerCounter = 0; ghostsActive[0] = 0; ghostsActive[1] = 0; ghostsActive[2] = 0; ghostCounter = 0; // Set up the initial directions of the ghosts and pacman for (byte lxn = 0; lxn < 4; lxn++) { locations[lxn][0] = startPosX[lxn]; locations[lxn][1] = startPosY[lxn]; directions[lxn] = startDirections[lxn]; } // Initially set pacman off in the direction he is facing commandDir = startDirections[3]; } /* ------------------------ Pacman main game code */ void playPacman() { score = 0; lives = 2; level = 1; releaseDelay = INITIAL_GHOST_DELAY; maxGhosts = INITIAL_GHOSTS; initLevel(); initScreen(); while (lives >= 0) { while(!gb.update()); if (gb.buttons.pressed(BTN_LEFT) == true) commandDir = DIR_LEFT; if (gb.buttons.pressed(BTN_RIGHT) == true) commandDir = DIR_RIGHT; if (gb.buttons.pressed(BTN_UP) == true) commandDir = DIR_UP; if (gb.buttons.pressed(BTN_DOWN) == true) commandDir = DIR_DOWN; if (pillsEaten >= NO_PILLS) levelUp(); moveGhosts(); movePacman(); // Check for ghosrs or pacman going thru the tunnel teleport thing for (byte lxn2 = 0; lxn2 < 4; lxn2 ++) { if (locations[lxn2][1] <= 0) { locations[lxn2][1] = 56; locations[lxn2][0] = 100; } else if (locations[lxn2][1] >= 56) { locations[lxn2][1] = 0; locations[lxn2][0] = 21; } } if (hitGhosts() != 0) { pacDie(); lives--; initScreen(); } displayScreen(); } initScreen(); } ================================================ FILE: PAK-MAN_MAKERbuino/font6x8AJ4.h ================================================ /* * Font file - originally from SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays * * @file: font6x8AJ4.h * @created: 2014-08-12, edited by andy jackson 2018 (see below) * @author: Neven Boyanov * * Hacked by andy jackson to optimise size for the AttinyArcade - 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 // ---------------------------------------------------------------------------- /* Standard ASCII 6x8 font */ static const uint8_t lcdDisplayxled_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, 0x00, 0x08, 0x08, 0x08, 0x00, // - 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // . 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 }; // ---------------------------------------------------------------------------- ================================================ FILE: Pacman_Attiny_Arcade/Pacman_Attiny_Arcade.ino ================================================ /* 2015 / 2016 / 2017 Pacman for Attiny Arcade 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 software is supplied without warranty of any kind. * **** BEFORE USE, BURN THE BOOTLOADER ON THE ATTINY85 WITH 16Mhz Internal Clock and note the specific ATtiny libraries required for this sketch (see below) * Controls: On the standard AttinyArcade: Tap left button to turn 90 degrees left (each tap cycles through directions, so if you are going left and you tap once, you will go down, tap twice and you'll be going right) Tap right button to turn 90 degrees right (each tap cycles through directions, so if you are going left and you tap once, you will go up, tap twice and you'll be going right) The arrow indicator on the far left of the screen indicates the currently-selected direction, which pac-man will take as soon as there's a gap big enough for him! 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 Everything you need to build and run this game is contained in this file and the font header (font6x8AJ3.h) - no additional libraries required Instructions for programming the Attiny85 can be found here (but note the specific Attiny85 library required below): https://create.arduino.cc/projecthub/arjun/programming-attiny85-with-arduino-uno-afb829 **** NOTE **** Unlike my other games, this one seems to only compile with one specific set of libraries - the ones here: http://drazzy.com/package_drazzy.com_index.json from Spence Konde - with the following options: - Board: AttinyCore-> Attiny 25/45/85 (No Bootloader) - Chip: Attiny85 - Clock: 16Mhz PLL - B.O.D. Disabled All other settings default. Don't forget to burn the bootloader first! ************ 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 As well as the above sources of inspiration, the pac-man screen artwork was insipred by elements of another clone found here https://sites.google.com/view/arduino-collection 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 #include "font6x8AJ3.h" #include #include #include // needed for the additional interrupt // Debounce factor - around 50 works ok with AttinyArcade, but you might need to increase this number if you are using differnt hardware // and are finding that you're getting multiple direction changes from a single click #define CLICKDELAY 52 #define DIR_RIGHT 0 #define DIR_DOWN 1 #define DIR_LEFT 2 #define DIR_UP 3 #define PACMAN 1 #define GHOST1 2 #define GHOST2 4 #define GHOST3 8 // Probability of a ghost taking a branch to the side if it can #define BRANCH_PROBABILITY 50 // How many ghosts on level 1 #define INITIAL_GHOSTS 2 // Initial number of cycles before a ghost is released - reduces according to next two defines down to a min level #define INITIAL_GHOST_DELAY 150 #define GHOST_DELAY_REDUCTION 25 #define GHOST_DELAY_MINIMUM 50 // You start with two (spare) lives #define START_LIVES 2 // How long does powerUp mode last? #define POWER_LENGTH 200 #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 /* ------ This section needs to be updated if you change the screen bitmap ------ ------------------------------------------------------------------------------*/ // Where on the screen to the ghosts appear when they pop out onto the playing screen? #define GHOST_LAUNCH_X 64 #define GHOST_LAUNCH_Y 16 // How many pills are there on the screen? #define NO_PILLS 56 // Where are the ghosts and pacman drawn initially? // In each of these, the order is - Ghost1, Ghost2, Ghost3, PAC-MAN static const byte startPosX[] = {66, 55, 77, 55}; static const byte startPosY[] = {28, 28, 28, 40}; static const byte startDirections[] = {DIR_UP, DIR_UP, DIR_UP, DIR_RIGHT}; // Where are the big pills? (their row is fixed as the last one) static const byte bigPillLocations[2] PROGMEM = {14, 119}; // How many pills on each row? for the six rows 1-7 that you are allowed pills static const byte readRow[6][2] PROGMEM = { 0, 12, 12, 23, 23, 31, 31, 34, 34, 45, 45, 56 }; // Where are the pills (x,y) static const byte pillLocations[NO_PILLS][2] PROGMEM = { 14, 8, 24, 8, 36, 8, 46, 8, 55, 8, 63, 8, 75, 8, 84, 8, 93, 8, 104, 8, 111, 8, 119, 8, 14, 20, 24, 20, 36, 20, 46, 20, 55, 20, 63, 20, 75, 20, 84, 20, 93, 20, 104, 17, 119, 17, 14, 31, 24, 31, 36, 31, 46, 31, 93, 31, 104, 25, 111, 25, 119, 25, 104, 37, 111, 37, 119, 37, 14, 43, 24, 43, 36, 43, 46, 43, 55, 43, 63, 43, 75, 43, 84, 43, 93, 43, 104, 46, 119, 46, 14, 55, 24, 55, 36, 55, 46, 55, 55, 55, 63, 55, 75, 55, 84, 55, 93, 55, 104, 55, 111, 55 }; // 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[]); // 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 playPacman(void); void levelUp(int); void showScore(void); void displayTitle(void); int checkCollision(byte xpos, byte ypos); void initScreen(void); void initLevel(void); void newDirection(byte ghostNo); byte hitGhosts(void); void movePacman(void); void pacDie(void); void eatenGhost(void); void moveGhosts(void); byte drawPacman(byte c, byte r); // 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 (playPacman()) 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 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 int mouth = 0; // Is his mouth open? int directions[4]; // Directions of the four possible characters (pacman = 0, ghosts are 1-3) int commandDir; // The desired direction set by input byte pillsEaten = 0; // How many pills have been eaten (there are 63 altogether) int ghostCounter = 0; // How many ghosts int ghostsActive[3]; // How many ghosts are out? int gCount = 0; // Counter for ghost behaviour int pCount = 0; // Counter pac-man behaviour int oldDir; // Holds previous direction int level = 0; // Obvious I hope int releaseDelay; // The delay between ghosts being let out int maxGhosts; // How many ghosts maximum on this level? bool powerUp; // Is powerUp move active? int powerCounter; // Counts the length of powerUp mode byte locations[4][2]; // The x,y location of the sprites byte pillsActive[NO_PILLS]; // Which of the pills is still on the screen? byte bigPillsActive[2]; // Which of the two big pills is still active? bool flipFlop = 0; // Used to make the ghosts flash // Artwork static const byte pacOpen[4][8] PROGMEM = { 0x3C, 0x7E, 0xFF, 0xFF, 0xE7, 0xE7, 0xC3, 0x42, 0x7C, 0xFE, 0x3F, 0x0F, 0x0F, 0x3F, 0xFE, 0x7C, 0x42, 0xC3, 0xE7, 0xE7, 0xFF, 0xFF, 0x7E, 0x3C, 0x3E, 0x7F, 0xFC, 0xF0, 0xF0, 0xFC, 0x7F, 0x3E }; static const byte arrows[4][7] PROGMEM = { 0x10, 0x10, 0x10, 0x7C, 0x38, 0x10, 0x00, 0x00, 0x20, 0x60, 0xFF, 0x60, 0x20, 0x00, 0x10, 0x38, 0x7C, 0x10, 0x10, 0x10, 0x00, 0x00, 0x04, 0x06, 0xFF, 0x06, 0x04, 0x00 }; static const byte pacClosed[] PROGMEM = {0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C}; static const byte ghost[2][8] PROGMEM = {0x7C, 0xFE, 0x22, 0xEB, 0xFF, 0x62, 0xEA, 0x7C, 0x7C, 0xC2, 0x0A, 0x83, 0x83, 0x0A, 0x82, 0x7C}; static const byte PAClives[] PROGMEM = {0x1C, 0x3C, 0x7E, 0x66, 0x66, 0x62, 0x40}; static const byte openBmp[] PROGMEM = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFC, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFC, 0xFC, 0xF8, 0x70, 0x60, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xEF, 0xC7, 0x87, 0x83, 0x83, 0x01, 0x00, 0x00, 0x10, 0x38, 0x7C, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7C, 0x38, 0x10, 0x00, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x3F, 0x3F, 0x3E, 0x1E, 0x1E }; static const byte gameScreen[] PROGMEM = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x04, 0xF4, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x13, 0x10, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x10, 0x13, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xE4, 0x04, 0xE4, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xE4, 0x04, 0xE4, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xE4, 0x04, 0xE4, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, 0xE4, 0x04, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x20, 0xA0, 0xA0, 0xA0, 0xA0, 0x20, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x2F, 0x2F, 0x2F, 0x2F, 0x20, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x08, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xFD, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x02, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x02, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0xFD, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x3E, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x20, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x9F, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x9F, 0x80, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x80, 0x7E, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x04, 0xF4, 0xF4, 0xF4, 0xF4, 0x04, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x01, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04, 0x05, 0x05, 0x05, 0x05, 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x20, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x20, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x20, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0xC7, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x08, 0xC8, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x20, 0x1F, 0x00 }; // Interrupt handlers ISR(PCINT0_vect) { // PB0 pin button interrupt if (clickLock == 0) { if (commandDir > 0) commandDir--; else commandDir = 3; clickLock = 1; clickBase = millis(); } } void playerIncPacman() { // PB2 pin button interrupt if (clickLock == 0) { if (commandDir < 3) commandDir++; else commandDir = 0; clickLock = 1; clickBase = millis(); } } void displayTitle(void) { int incr = 0; for (int lxn = 3; lxn < 6; lxn++) { ssd1306_setpos(84, lxn); ssd1306_send_data_start(); for (int lxn2 = 0; lxn2 < 34; lxn2++) { ssd1306_send_byte(pgm_read_byte(&openBmp[incr])); incr++; if ( (lxn == 3 || lxn == 5) && lxn2 > 21) lxn2 = 35; } 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 } void drawBar(byte aColumn, byte aRow, byte aLen) { ssd1306_setpos(aColumn, aRow); for (int incr = 0; incr < aLen; incr++) { ssd1306_send_data_start(); ssd1306_send_byte(B00111000); ssd1306_send_data_stop(); } } // Arduino stuff - loop void loop() { ssd1306_init(); ssd1306_fillscreen(0x00); ssd1306_char_f6x8(4, 2, "P A C-M A N"); ssd1306_char_f6x8(0, 4, "andy jackson"); drawBar(0, 1, 76); drawBar(0, 3, 76); displayTitle(); ssd1306_char_f6x8(0, 6, "inspired by"); ssd1306_char_f6x8(0, 7, "webboggles.com"); delay(1200); 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(1000); ssd1306_init(); ssd1306_fillscreen(0x00); playPacman(); 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); drawBar(11, 1, 96); drawBar(11, 3, 96); ssd1306_char_f6x8(11, 2, "G A M E O V E R"); showScore(); if (!newHigh) { ssd1306_char_f6x8(21, 7, "HIGH SCORE:"); doNumber(88, 7, topScore); } delay(1500); if (newHigh) { ssd1306_fillscreen(0x00); drawBar(11, 1, 96); drawBar(11, 3, 96); ssd1306_char_f6x8(10, 2, " NEW HIGH SCORE "); doNumber(50, 5, topScore); for (int i = 700; i > 200; i = i - 50) { beep(30, i); } delay(1500); } } system_sleep(); } void showScore(void) { ssd1306_char_f6x8(37, 5, "SCORE:"); doNumber(75, 5, score); } // Handle what happens at the end of a level void levelUp() { initScreen(); initLevel(); level++; if ((level == 3) || (level == 5)) lives++; if (maxGhosts < 3) maxGhosts++; if (releaseDelay > GHOST_DELAY_MINIMUM) releaseDelay -= GHOST_DELAY_REDUCTION; ssd1306_fillscreen(0x00); ssd1306_char_f6x8(37, 1, "LEVEL:"); doNumber(75, 1, level); drawBar(35, 3, 65); showScore(); delay(2500); ssd1306_fillscreen(0x00); displayScreen(); } 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"); // 62.5ns delay @ 16MHz } digitalWrite(1, LOW); for (int i2 = 0; i2 < bDelay; i2++) { __asm__("nop\n\t"); // 62.5ns delay @ 16Mhz } } } byte drawPacman(byte c, byte r) { byte out = 0; if ( (r >= locations[3][0]) && (r < locations[3][0] + 8) ) { // draw the pacMan if any of his body is on this column if (c == locations[3][1] / 8) { if (mouth == 1) { out = (pgm_read_byte(&pacOpen[directions[3]][r - locations[3][0]]) << locations[3][1] % 8); } else { out = (pgm_read_byte(&pacClosed[r - locations[3][0]]) << locations[3][1] % 8); } } else if (c == locations[3][1] / 8 + 1) { if (mouth == 1) { out = (pgm_read_byte(&pacOpen[directions[3]][r - locations[3][0]]) >> (8 - locations[3][1] % 8) ); } else { out = (pgm_read_byte(&pacClosed[r - locations[3][0]]) >> (8 - locations[3][1] % 8) ); } } } return out; } void displayScreen(void) { int incr = 0; int pacDir = directions[3]; byte out; for (byte c = 0; c < 8; c++) { ssd1306_setpos(0, c); ssd1306_send_data_start(); for (byte r = 0; r < 128; r++) { out = (pgm_read_byte(&gameScreen[incr])); out |= drawPacman(c, r); // Draw the bits in the far left column - lives and the little direction arrow if (r < 7) { if (c == 1) out |= (pgm_read_byte(&arrows[commandDir][r])); for (byte lxn = 3; lxn < 7; lxn++) { if (c == lxn && lives > 6 - lxn) out |= (pgm_read_byte(&PAClives[r])); } } for (byte lxn = 0; lxn < 3; lxn++) { if ( (r >= locations[lxn][0]) && (r < locations[lxn][0] + 8) ) { // draw the ghost if any of his body is on this column if (c == locations[lxn][1] / 8) { out |= (pgm_read_byte(&ghost[flipFlop][r - locations[lxn][0]]) << locations[lxn][1] % 8); } else if (c == locations[lxn][1] / 8 + 1) { out |= (pgm_read_byte(&ghost[flipFlop][r - locations[lxn][0]]) >> (8 - locations[lxn][1] % 8) ); } } } if (c > 0 && c < 7) { for (byte lxn = pgm_read_byte(&readRow[c - 1][0]); lxn < pgm_read_byte(&readRow[c - 1][1]); lxn++) { if (pillsActive[lxn]) { // this pill is active if (r == pgm_read_byte(&pillLocations[lxn][0])) { // draw the pills if they appear here // check if pac-man has eaten a pill if (pgm_read_byte(&pillLocations[lxn][0]) > locations[3][0] && pgm_read_byte(&pillLocations[lxn][0]) < locations[3][0] + 8 && pgm_read_byte(&pillLocations[lxn][1]) > locations[3][1] && pgm_read_byte(&pillLocations[lxn][1]) < locations[3][1] + 8) { pillsActive[lxn] = 0; pillsEaten++; score += level; break; } if (c == pgm_read_byte(&pillLocations[lxn][1]) / 8) { out |= ((B00000001) << pgm_read_byte(&pillLocations[lxn][1]) % 8); } else if (c == pgm_read_byte(&pillLocations[lxn][1]) / 8 + 1) { out |= ((B00000001) >> (8 - pgm_read_byte(&pillLocations[lxn][1]) % 8) ); } } } } } for (byte lxn = 0; lxn < 2; lxn++) { if (bigPillsActive[lxn] == 1 && c == 6 && (r == pgm_read_byte(&bigPillLocations[lxn]) || r == (pgm_read_byte(&bigPillLocations[lxn]) + 1) )) { out |= B11100000; } } if (locations[3][1] == 51) { if ((locations[3][0] < 14 && bigPillsActive[0] == 1) || (locations[3][0] > 111 && bigPillsActive[1] == 1)) { if (locations[3][0] < 14) bigPillsActive[0] = 0; else bigPillsActive[1] = 0; powerUp = 1; flipFlop = 1; powerCounter = 0; } } ssd1306_send_byte(out); incr++; } // end for 128 ssd1306_send_data_stop(); } } int checkCollision(byte xpos, byte ypos) { int incr = 0; int returnValue = 0; for (byte c = 0; c < 2; c++) { // for each of the two lines that the item might be on incr = (128 * (ypos / 8 + c)) + xpos; for (byte r = 0; r < 8; r++) { // for each of the eight bytes in the sprite if (c == 0) { if ((pgm_read_byte(&gameScreen[incr]) & (0xFF << ypos % 8) ) != 0) returnValue = 1; } else { if ((pgm_read_byte(&gameScreen[incr]) & (0xFF >> (8 - ypos % 8) ) ) != 0) returnValue = 1; } incr++; } } return returnValue; } void moveGhosts(void) { int travelDirection; // which way's the ghost going? byte travelParam; // zero for travelling in x, one for y int searchDirection; // +1 to search up, -1 to search down for side exits byte paramSelection; // zero for xPosition, one for yPosition byte switchDirection; // which direction to switch to if there's a switch ghostCounter++; // Pop the next ghost out, if it's time if (ghostCounter >= releaseDelay) { ghostCounter = 0; for (byte lxn = 0; lxn < maxGhosts; lxn++) { if (ghostsActive[lxn] == 0) { locations[lxn][0] = GHOST_LAUNCH_X; locations[lxn][1] = GHOST_LAUNCH_Y; ghostsActive[lxn] = 1; break; } } } for (byte lxn = 0; lxn < 3; lxn++) { if (ghostsActive[lxn] == 1) { char clash = 0; switch (directions[lxn]) { case (DIR_RIGHT): travelDirection = 1; travelParam = 0; searchDirection = -1; paramSelection = 1; switchDirection = DIR_UP; break; case (DIR_LEFT): travelDirection = -1; travelParam = 0; searchDirection = 1; paramSelection = 1; switchDirection = DIR_DOWN; break; case (DIR_UP): travelDirection = -1; travelParam = 1; searchDirection = -1; paramSelection = 0; switchDirection = DIR_LEFT; break; case (DIR_DOWN): travelDirection = 1; travelParam = 1; searchDirection = 1; paramSelection = 0; switchDirection = DIR_RIGHT; break; } // Slow the ghosts down if powerup mode is active gCount++; if (gCount >= 5) { if(powerUp) travelDirection = 0; if (powerCounter > POWER_LENGTH - 50) flipFlop = !flipFlop; gCount = 0; } locations[lxn][travelParam] += travelDirection; // Move the ghosts along according to which way they are heading - if there's a clash, then backtrack if (checkCollision(locations[lxn][0], locations[lxn][1]) == 1) { locations[lxn][travelParam] -= travelDirection; newDirection(lxn); } else { clash = 0; for (byte lxn2 = 0; lxn2 < 3; lxn2++) { locations[lxn][paramSelection] += searchDirection; if (checkCollision(locations[lxn][0], locations[lxn][1]) == 1) clash = 1; } if (clash == 0 && random(100) > BRANCH_PROBABILITY) directions[lxn] = switchDirection; locations[lxn][paramSelection] -= (searchDirection * 3); } } } } void newDirection(byte ghostNo) { directions[ghostNo] = random(4); } void eatenGhost(void) { for (int i = 700; i > 200; i = i - 50) { beep(30, i); } } byte hitGhosts(void) { byte returnCode = 0; for (int lxn = 0; lxn < 3 ; lxn++) { if ((locations[lxn][0] > locations[3][0] - 8) && (locations[lxn][0] < locations[3][0] + 8) && (locations[lxn][1] > locations[3][1] - 8) && (locations[lxn][1] < locations[3][1] + 8)) { if (powerUp == 1) { locations[lxn][0] = startPosX[lxn]; locations[lxn][1] = startPosY[lxn]; ghostsActive[lxn] = 0; eatenGhost(); score += 10; ghostCounter = 0; } else returnCode = 1; } } return returnCode; } void pacDie(void) { int i; directions[3] = DIR_UP; mouth = 1; displayScreen(); for (i = 0; i < 500; i = i + 50) { beep(50, i); } mouth = 0; displayScreen(); for (i = 500; i < 1000; i = i + 50) { beep(50, i); } delay(1200); stopAnimate = 1; } void movePacman(void) { pCount++; if (pCount >= 3) { if (mouth == 0) { mouth = 1; beep(20, 400 + (powerUp * 100)); } else { mouth = 0; beep(20, 420); } pCount = 0; } if (powerUp == 1) { powerCounter++; if (powerCounter >= POWER_LENGTH) { powerUp = 0; powerCounter = 0; flipFlop = 0; gCount = 0; pCount = 0; } } oldDir = directions[3]; directions[3] = commandDir; switch (directions[3]) { case (DIR_RIGHT): for (int lxn = 0; lxn < 3; lxn++) { locations[3][0]++; if (checkCollision(locations[3][0], locations[3][1]) == 1) { directions[3] = oldDir; } } locations[3][0] -= 3; break; case (DIR_LEFT): for (int lxn = 0; lxn < 3; lxn++) { locations[3][0]--; if (checkCollision(locations[3][0], locations[3][1]) == 1) { directions[3] = oldDir; } } locations[3][0] += 3; break; case (DIR_UP): for (int lxn = 0; lxn < 3; lxn++) { locations[3][1]--; if (checkCollision(locations[3][0], locations[3][1]) == 1) { directions[3] = oldDir; } } locations[3][1] += 3; break; case (DIR_DOWN): for (int lxn = 0; lxn < 3; lxn++) { locations[3][1]++; if (checkCollision(locations[3][0], locations[3][1]) == 1) { directions[3] = oldDir; } } locations[3][1] -= 3; break; } switch (directions[3]) { case (DIR_RIGHT): locations[3][0] += 1; if (checkCollision(locations[3][0], locations[3][1]) == 1) locations[3][0] -= 1; break; case (DIR_LEFT): locations[3][0] -= 1; if (checkCollision(locations[3][0], locations[3][1]) == 1) locations[3][0] += 1; break; case (DIR_UP): locations[3][1] -= 1; if (checkCollision(locations[3][0], locations[3][1]) == 1) locations[3][1] += 1; break; case (DIR_DOWN): locations[3][1] += 1; if (checkCollision(locations[3][0], locations[3][1]) == 1) locations[3][1] -= 1; break; } } void initLevel(void) { clickLock = 0; pillsEaten = 0; stopAnimate = 0; for (byte lxn = 0; lxn < NO_PILLS; lxn++) pillsActive[lxn] = 1; bigPillsActive[0] = 1; bigPillsActive[1] = 1; } void initScreen(void) { powerUp = 0; flipFlop = 0; powerCounter = 0; ghostsActive[0] = 0; ghostsActive[1] = 0; ghostsActive[2] = 0; ghostCounter = 0; // Set up the initial directions of the ghosts and pacman for (byte lxn = 0; lxn < 4; lxn++) { locations[lxn][0] = startPosX[lxn]; locations[lxn][1] = startPosY[lxn]; directions[lxn] = startDirections[lxn]; } // Initially set pacman off in the direction he is facing commandDir = startDirections[3]; } /* ------------------------ Pacman main game code */ void playPacman() { score = 0; lives = START_LIVES; level = 1; gCount = 0; pCount = 0; releaseDelay = INITIAL_GHOST_DELAY; maxGhosts = INITIAL_GHOSTS; attachInterrupt(0, playerIncPacman, CHANGE); initLevel(); initScreen(); while (lives >= 0) { if (pillsEaten >= NO_PILLS) levelUp(); moveGhosts(); movePacman(); // Check for ghosrs or pacman going thru the tunnel teleport thing for (byte lxn2 = 0; lxn2 < 4; lxn2 ++) { if (locations[lxn2][1] <= 0) { locations[lxn2][1] = 56; locations[lxn2][0] = 100; } else if (locations[lxn2][1] >= 56) { locations[lxn2][1] = 0; locations[lxn2][0] = 21; } } if (hitGhosts() != 0) { pacDie(); lives--; initScreen(); } if (clickLock == 1 && millis() > clickBase + CLICKDELAY && digitalRead(2) == 0 && digitalRead(0) == 0) clickLock = 0; // normal debounce displayScreen(); } } ================================================ FILE: Pacman_Attiny_Arcade/font6x8AJ3.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 // ---------------------------------------------------------------------------- /* 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, 0x00, 0x08, 0x08, 0x08, 0x00, // - 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // . 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 }; // ---------------------------------------------------------------------------- ================================================ FILE: README.md ================================================ Games for the AttinyArcade (or hardware inspired by this system) ================================================================ By Andy Jackson - Twitter: @andyhighnumber ------------------------------------------ The games in this folder can mostly be played either on the AttinyArcade, or similar hardware (if the hardware is different, there will be a circuit diagram in the same folder as the game). Info on programming the Attiny85 chip can be found online, here's an example: https://create.arduino.cc/projecthub/arjun/programming-attiny85-with-arduino-uno-afb829 - don't forget to burn the bootloader (it's a menu option under 'Tools' on the Arduino IDE) the first time you flash the chip and if you need to change between 8Mhz and 16Mhz clocks (all my games are 8Mhz, except Pacman which is 16Mhz). See www.webboggles.com for details of the AttinyArcade hardware. These games are based on the Attiny85 device running at 8Mhz on its internal clock, or 16Mhz for pac-man (don't forget to burn the bootloader when you first flash a new chip- the option to do this is in the "Tools" menu on the Arduino IDE). In this folder: For the standard Attiny Arcade Hardware, or enhanced functionality on custom hardware ===================================================================================== - Pacman_Attiny_Arcade: PacMan clone for the original Attiny. This is the best of the lot in my opinion. Unlike the other games here, this needs to be flashed with a 16Mhz internal clock bootloader (all the others are 8MHz). Send me a message via twitter if this presents any problems. - SpaceAttackAttiny: Space Invaders clone for the original Attiny (also allows for modified version of hardware with fire button - see circuit diagram in folder) - Frogger_Attiny_Arcade: Frogger clone for the original Attiny (also allows modified version of hardware with jump button - same hardware config as SpaceAttack above - see schematic in folder) - MorseAttinyArcade: A morse code decoder (for practicing morse, if you so wish), which displays inputted code via a 5-line scrolling display. When combined with a PLL (or similar) tone detection circuit, this could easily decode off-air morse. For the standard Attiny Arcade Hardware ======================================= - WrenRollercoasterAttinyArcade: Inspired by the TinyWings iOS game - fly a wren along a landscape as far as you can! - Attiny Tetris: A Tetris clone, evolved from a game for the Arduino by Anthony Russell (https://github.com/AJRussell/Tiny-Tetris) - UFO_Stacker_Attiny: Two games in one sketch - UFO (from www.webboggles.com) and Stacker (from me). - UFO_Breakout_Arduino: Again - two games in one sketch, both from www.webboggles.com, UFO and Breakout - BatBonanzaAttinyArcade: A version of Pong for the standard AttinyHardware - not as playable because you really need analog control for this type of game - see below For the MAKERbuino (https://www.makerbuino.com/) ======================================= - PAK-MAN_MAKERbuino: Port of the PAC-MAN AttinyArcade game to the MAKERBUINO / GAMEBUINO platform - FROGGER_MAKERbuino: Port of the Frogger AttinyArcade game to the MAKERBUINO / GAMEBUINO platform For custom hardware - see circuit schematics in the folders with the sketches ============================================================================= - BatBonanzaAnalog: Pong clone based on custom hardware with analog controls (will work on the same hardware as Space Attack Anlog Version). - Space_Attack_Analog: Space Invaders clone (sort of) based on custom hardware with analog control (will work on the same hardware as Bat Bonanza Anlog Version). - BatBonanzaAnalogSinglePot: Pong clone based on custom hardware with single-pot analog control (will work on the same hardware as Space Attack Anlog Version). - Tetris_Multi_Button: A Tetris cline, as above but designed for multi-button pad (see circuit schematic in folder for details of the hardware) ================================================ FILE: SpaceAttackAttiny/SpaceAttackAttiny.ino ================================================ /* 2015 / 2016 * SpaceAttack 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. * * Press both left and right buttons to fire (works on #AttinyArcade which only has two buttons), or... * This code allows for three button inputs - the usual two on pins 5 and 7 (for left and right) and an additional switch on the reset pin * One side of the fire button should be wired to the reset pin (pin 1 - the only unused pin on the chip!) and the other side to a voltage divider, * consisting of a 6.8k resistor to ground and a 1k resistor to vcc (battery +ve) - this allows the code to read a voltage on the input that * drops enough to be noticible when the button is pressed, but not enough to reset the chip! * * 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 * * 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 #include "font6x8AJ.h" #include #include // 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 // Function prototypes void resetAliens(void); void drawPlatform(void); void sendBlock(int); void playSpaceAttack(void); void beep(int,int); void levelUp(int); void drawFire(int x, int y); void doDrawLS(long, byte); void doDrawRS(long, byte); void clearAlienArea(int row, int lastActive); 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[]); long firecounter = 0; // Timer for managing fire long aliencounter = 0; // Timer for alien movement int mothercounter = 0; // Timer for movement of the mothership int level = 1; // Game level - incremented every time you clear the screen int mothershipX = 0; // position of mothership int mothership = 0; // is mothership active? int mothershipWidth = 8; // mothership width in pixels int fireXidx, fireYidx; // mapping of player fire locaiton onto array of aliens int leftLimit; // furtherst left in x-axis pixels that the aliens are currently int positionNow = 0; // current position of the alien array (as steps from the left) boolean alienDirection = 1; // current direction of travel for alien swarm - 1 is right 0 is left int alienRow = 0; // which alien row are we considering int alienFire[5][3]; // max 5 lots of alien fire - indices are active, xpos, ypos int playerFire[3]; // one lot of player fire - indices are active, xpos, ypos boolean row[4][10]; // on-off array of aliens int firstAlien = 0; // index of first live alien in the array int lastAlien = 8; // index of last live alien in the array int newFirst = firstAlien; // as above when it changes int newLast = lastAlien; // ... int aliensDead = 0; // how many aliens have been killed in total on this level? int lastActiveRow = 2; // what's the lowest row in which we will find live aliens? int deadOn1 = 0; // how many aliens are dead on row one (middle row) int deadOn2 = 0; // how many aliens are dead on row two (bottom row) boolean fire = 0; int topScoreB = 0; int player; //0 to 128-platformWidth - this is the position of the player 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 top = 0; // Interrupt handlers ISR(PCINT0_vect){ // PB0 pin button interrupt if (digitalRead(2)==1) fire = 1; } void playerIncSpaceAttack(){ // PB2 pin button interrupt if (digitalRead(0)==1) fire = 1; } // 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, "S P A C E"); ssd1306_char_f6x8(4, 2, "A T T A C K"); ssd1306_char_f6x8(0, 4, "andh jackson"); // see comments above ! ssd1306_setpos(85,1); ssd1306_send_data_start(); sendBlock(1); sendBlock(0); sendBlock(1); ssd1306_send_data_stop(); ssd1306_setpos(85,2); ssd1306_send_data_start(); sendBlock(0); sendBlock(2); sendBlock(0); ssd1306_send_data_stop(); ssd1306_setpos(85,3); ssd1306_send_data_start(); sendBlock(0); sendBlock(0); sendBlock(1); sendBlock(0); sendBlock(1); ssd1306_send_data_stop(); player = 96; drawPlatform(); 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(23, 0, "-- SOUND ON --"); } break; } if (sChange == 1) break; } while(digitalRead(0) == HIGH); if (sChange == 0) { for (byte mm = 112;mm>=1;mm--) { if ( (mm>=32) && (mm<56) ) drawFire(104,mm); if (mm == 32) { ssd1306_setpos(100,3); ssd1306_send_data_start(); sendBlock(0); ssd1306_send_data_stop(); ssd1306_setpos(100,4); ssd1306_send_data_start(); sendBlock(0); ssd1306_send_data_stop(); beep(30,100); } ssd1306_setpos(mm,0); ssd1306_send_data_start(); sendBlock(3); sendBlock(0); ssd1306_send_data_stop(); drawPlatform(); delay(20); } ssd1306_setpos(0,0); ssd1306_send_data_start(); sendBlock(0); sendBlock(0); ssd1306_send_data_stop(); ssd1306_char_f6x8(0, 6, "inspired bh"); // see comments above ! ssd1306_char_f6x8(0, 7, "/ebboggles.com"); // see comments above ! delay(1500); ssd1306_init(); ssd1306_fillscreen(0x00); stopAnimate = 0; score = 0; playSpaceAttack(); top=topScoreB; 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, top); } for (int i = 0; i<1000; i = i+ 50){ beep(50,i); } delay(2000); 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,top); for (int i = 700; i>200; i = i - 50){ beep(30,i); } newHigh = 0; delay(2700); } } 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-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() { 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; i21){ player--; } } if ( (mothership == 0) && (random(0,1000) > 998) && (alienRow>0) ) { mothership = 1; mothershipX= 127-16; } // draw aliens for (int inc = 0; inc <= lastActiveRow; inc ++) { ssd1306_setpos((positionNow*8),inc+alienRow); ssd1306_send_data_start(); for (int bl = firstAlien; bl <=lastAlien; bl++){ if(row[inc][bl]==1){ if (inc == lastActiveRow) { if(random(0,1000) > (999-level)) { // this alien is going to fire! byte afIndex = 0; while (alienFire[afIndex][0] == 1) { afIndex++; // search for empty alien fire option if (afIndex == 5) break; } if (afIndex < 5) { // we've found a slot alienFire[afIndex][0] = 1; // activate fire on this slot alienFire[afIndex][1] = ( (positionNow*8) + ((bl-firstAlien) * 8) + 4); // x position alienFire[afIndex][2] = (inc+alienRow+1)*8; // Where the fire starts } } // end of this alien firing } // only if we are on the lowest row of live aliens if ( (inc == 0) || (inc == 2) ) { sendBlock(1); } else { sendBlock(2); } }else { sendBlock(0); } } ssd1306_send_data_stop(); } // Display the score doNumber(0,6,score); // Burn clock cycles to keep game at constant (ish) speed when there are low numbers of live aliens int burnLimit = (8-(lastAlien-firstAlien)); for (int burn = 0; burn < burnLimit; burn+=2) { drawPlatform(); } // Display the mothership if (mothercounter >= 3) { mothercounter = 0; // draw mothership if (mothership) { ssd1306_setpos(mothershipX,0); ssd1306_send_data_start(); sendBlock(3); sendBlock(0); ssd1306_send_data_stop(); mothershipX --; if (mothershipX == 0) { mothership = 0; ssd1306_setpos(mothershipX,0); ssd1306_send_data_start(); sendBlock(0); sendBlock(0); ssd1306_send_data_stop(); } } } // Move the aliens if (aliencounter >= (92-((level-1)*5)) ) { aliencounter = 0; if(alienDirection) { // Moving right // move down a row if (positionNow >= 6+(8-(lastAlien-firstAlien))) { alienDirection = 0; clearAlienArea(alienRow,lastActiveRow); alienRow++; } else { positionNow++; } } else { // Moving left // move down a row if (positionNow <= 0) { alienDirection = 1; clearAlienArea(alienRow,lastActiveRow); alienRow++; } else { positionNow --; } } clearAlienArea(alienRow,lastActiveRow); } // Fire ! if ((fire == 1) && (playerFire[0] == 0)) { // fire has been pressed and we're not currently firing - initiate fire!! playerFire[0] = 1; playerFire[1] = player+platformWidth/2; // xposition of new fire! playerFire[2] = 56; } // Handle all firing-related stuff (in both directions!) if (firecounter >= 2) { firecounter = 0; // --- Deal with player Firing --- if (playerFire[0] == 1) { drawFire(playerFire[1], playerFire[2]); if (playerFire[2] == 0) { ssd1306_setpos(playerFire[1],0); uint8_t temp = B00000000; ssd1306_send_data_start(); ssd1306_send_byte(temp); ssd1306_send_data_stop(); playerFire[0] = 0; fire = 0; } else { playerFire[2] = playerFire[2] - 1; } } // aliens are at positionNow * 8 + 8* their index leftLimit = positionNow*8; if ((fire == 1)) { fireXidx = firstAlien + floor((playerFire[1]-positionNow*8)/8); fireYidx= floor(floor(playerFire[2]/8) - alienRow); if((mothership == 1) && (playerFire[1] >= mothershipX) && (playerFire[1] <= mothershipX +8) && playerFire[2] <=8) { long scm = random(1,100); if (scm<30) { score +=50; } else if (scm<60) { score += 100; } else if (scm<90) { score += 150; } else { score += 300; } beep(30,400); beep(30,300); beep(30,200); beep(30,100); mothership = 0; ssd1306_setpos(mothershipX,0); ssd1306_send_data_start(); sendBlock(0); sendBlock(0); ssd1306_send_data_stop(); } // Alien has been hit if ((playerFire[1] >= leftLimit) && (fireYidx>=0) && (fireYidx<=lastActiveRow) && (fireXidx>=0) && (fireXidx<9)) { if (row[fireYidx][fireXidx] == 1) { int lastActiveToClear = lastActiveRow; // if we kill the last alien on a row - we still need to clear that row (end of this fn) if (fireYidx == 2) deadOn2++; if (fireYidx == 1) deadOn1++; if (deadOn2 == 5) {lastActiveRow = 1;} if ((deadOn1 == 4) && (deadOn2 == 5)) {lastActiveRow = 0;} score = score + (int)((3-fireYidx) * 10); aliensDead++; ssd1306_setpos(playerFire[1],alienRow+fireYidx+1); uint8_t temp = B00000000; ssd1306_send_data_start(); ssd1306_send_byte(temp); ssd1306_send_data_stop(); beep(30,100); fire = 0; playerFire[0] = 0; playerFire[1] = 0; playerFire[2] = 7; row[fireYidx][fireXidx] = 0; if (fireXidx == firstAlien) { // this is the first Alien - sweep back and reset to new first alien for (int xi = lastAlien; xi>=firstAlien;xi--) { if ( (row[0][xi] == 1) || (row[1][xi] == 1) || (row[2][xi] == 1) ) newFirst = xi; } positionNow += newFirst - firstAlien; firstAlien = newFirst; } if (fireXidx == lastAlien) { // this is the last Alien - sweep thru and reset to new last alien for (int xi = firstAlien; xi<=lastAlien;xi++) { if ( (row[0][xi] == 1) || (row[1][xi] == 1) || (row[2][xi] == 1) ) newLast = xi; } lastAlien = newLast; } clearAlienArea(alienRow,lastActiveToClear); } } } // --- Deal with Alien Firing --- for (byte afIndex = 0; afIndex<5; afIndex++) { if(alienFire[afIndex][0] == 1) { drawFire(alienFire[afIndex][1],alienFire[afIndex][2]); alienFire[afIndex][2] = alienFire[afIndex][2] + 1; if (alienFire[afIndex][2] >= 56) { ssd1306_setpos(alienFire[afIndex][1],7); uint8_t temp = B00000000; ssd1306_send_data_start(); ssd1306_send_byte(temp); ssd1306_send_data_stop(); alienFire[afIndex][0] = 0; // the fire's got to the end if( ((alienFire[afIndex][1]) > player) && ( (alienFire[afIndex][1]) < player+platformWidth) ) { // you've been hit!! stopAnimate = 1; goto die; } } } } // end of aliens firing } if (aliensDead == 14) { for (int ai = 0; ai <= 5; ai++) alienFire[ai][0] = 0; level++; if (level>15) level = 15; levelUp(level); resetAliens(); } if( ((alienRow == 5) && (lastActiveRow == 2))|| ( (alienRow == 6) && (lastActiveRow == 1)) || ((alienRow == 7) && (lastActiveRow == 0)) || stopAnimate) { stopAnimate = 1; break; } // draw the player drawPlatform(); } } die: topScoreB = EEPROM.read(0); topScoreB = topScoreB << 8; topScoreB = topScoreB | EEPROM.read(1); if (score > topScoreB) { topScoreB = score; EEPROM.write(1,score & 0xFF); EEPROM.write(0,(score>>8) & 0xFF); newHigh = 1; } } void drawPlatform(){ ssd1306_setpos(player,7); ssd1306_send_data_start(); ssd1306_send_byte(B00000000); ssd1306_send_byte(B00000000); for (byte pw = 0; pw <5; pw++){ssd1306_send_byte(B11000000);} for (byte pw = 0; pw <4; pw++){ssd1306_send_byte(B11110000);} for (byte pw = 0; pw <5; pw++){ssd1306_send_byte(B11000000);} ssd1306_send_byte(B00000000); ssd1306_send_byte(B00000000); ssd1306_send_data_stop(); } 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 if (fill == 2) { ssd1306_send_byte(B00110000); ssd1306_send_byte(B00111110); ssd1306_send_byte(B10110011); ssd1306_send_byte(B01011101); ssd1306_send_byte(B01011101); ssd1306_send_byte(B10110011); ssd1306_send_byte(B00111110); ssd1306_send_byte(B00110000); } else if (fill == 3) { ssd1306_send_byte(B00011000); ssd1306_send_byte(B00111000); ssd1306_send_byte(B00110100); ssd1306_send_byte(B00110100); ssd1306_send_byte(B00110100); ssd1306_send_byte(B00110100); ssd1306_send_byte(B00111000); ssd1306_send_byte(B00011000); } else if (fill == 0) { 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 levelUp(int number) { fire = 0; // make sure no fire playerFire[0] = 0; // player fire inactive aliencounter=0; firecounter=0; mothercounter=0; firstAlien = 0; lastAlien = 8; newFirst = 0; newLast = 8; lastActiveRow = 2; deadOn1 = 0; deadOn2 = 0; aliensDead = 0; mothership = 0; alienRow = 0; positionNow = 0; alienDirection = 1; player = 64; ssd1306_fillscreen(0x00); ssd1306_char_f6x8(16, 3, "--------------"); ssd1306_char_f6x8(16, 4, " L E V E L "); ssd1306_char_f6x8(16, 5, "--------------"); doNumber(85,4,number); for (int i = 800; i>200; i = i - 200){ beep(30,i); } delay(700); ssd1306_fillscreen(0x00); } void drawFire(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((B01111110 | P1)>>P2); } // Drawing routine for the player and fire - with left-shifts void doDrawLS(long P1, byte P2) { ssd1306_send_byte((B01111110 | P1)< // ---------------------------------------------------------------------------- /* 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: Space_Attack_Analog/Space_Attack_Analog.ino ================================================ /* 2015 / 2016 /2017 * SpaceAttack 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. * * This is the analog version, which requires a potentiometer across the supply rails, with the centre tap connected to pin 7. There should be a * circuit diagram in the folder where you found these files. * * Also, from standby.... * Press and hold button with knob fully clockwise to turn sound on and off * Press and hold button with knob fully counter-clockwise to reset high score * * 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 #include "font6x8AJ.h" #include #include // 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 // Function prototypes void resetAliens(void); void drawPlatform(void); void sendBlock(int); void playSpaceAttack(void); void beep(int,int); void levelUp(int); void drawFire(int x, int y); void doDrawLS(long, byte); void doDrawRS(long, byte); void clearAlienArea(int row, int lastActive); 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[]); long firecounter = 0; // Timer for managing fire long aliencounter = 0; // Timer for alien movement int mothercounter = 0; // Timer for movement of the mothership int level = 1; // Game level - incremented every time you clear the screen int mothershipX = 0; // position of mothership int mothership = 0; // is mothership active? int mothershipWidth = 8; // mothership width in pixels int fireXidx, fireYidx; // mapping of player fire locaiton onto array of aliens int leftLimit; // furtherst left in x-axis pixels that the aliens are currently int positionNow = 0; // current position of the alien array (as steps from the left) boolean alienDirection = 1; // current direction of travel for alien swarm - 1 is right 0 is left int alienRow = 0; // which alien row are we considering int alienFire[5][3]; // max 5 lots of alien fire - indices are active, xpos, ypos int playerFire[3]; // one lot of player fire - indices are active, xpos, ypos boolean row[4][10]; // on-off array of aliens int firstAlien = 0; // index of first live alien in the array int lastAlien = 8; // index of last live alien in the array int newFirst = firstAlien; // as above when it changes int newLast = lastAlien; // ... int aliensDead = 0; // how many aliens have been killed in total on this level? int lastActiveRow = 2; // what's the lowest row in which we will find live aliens? int deadOn1 = 0; // how many aliens are dead on row one (middle row) int deadOn2 = 0; // how many aliens are dead on row two (bottom row) boolean fire = 0; int topScoreB = 0; int player; //0 to 128-platformWidth - this is the position of the player int lastPlayer; 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 top = 0; // Interrupt handlers ISR(PCINT0_vect){ // PB0 pin button interrupt } void playerIncSpaceAttack(){ // 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, "S P A C E"); ssd1306_char_f6x8(4, 2, "A T T A C K"); ssd1306_char_f6x8(0, 4, "bh andh jackson"); // see comments above ! ssd1306_setpos(85,1); ssd1306_send_data_start(); sendBlock(1); sendBlock(0); sendBlock(1); ssd1306_send_data_stop(); ssd1306_setpos(85,2); ssd1306_send_data_start(); sendBlock(0); sendBlock(2); sendBlock(0); ssd1306_send_data_stop(); ssd1306_setpos(85,3); ssd1306_send_data_start(); sendBlock(0); sendBlock(0); sendBlock(1); sendBlock(0); sendBlock(1); ssd1306_send_data_stop(); player = 96; drawPlatform(); 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(23, 0, "-- SOUND ON --"); } break; } if (sChange == 1) break; } while(digitalRead(0) == HIGH); if (sChange == 0) { for (byte mm = 112;mm>=1;mm--) { if ( (mm>=32) && (mm<56) ) drawFire(104,mm); if (mm == 32) { ssd1306_setpos(100,3); ssd1306_send_data_start(); sendBlock(0); ssd1306_send_data_stop(); ssd1306_setpos(100,4); ssd1306_send_data_start(); sendBlock(0); ssd1306_send_data_stop(); beep(30,100); } ssd1306_setpos(mm,0); ssd1306_send_data_start(); sendBlock(3); sendBlock(0); ssd1306_send_data_stop(); drawPlatform(); delay(20); } ssd1306_setpos(0,0); ssd1306_send_data_start(); sendBlock(0); sendBlock(0); ssd1306_send_data_stop(); ssd1306_char_f6x8(0, 6, "analog"); // see comments above ! ssd1306_char_f6x8(0, 7, "edition"); // see comments above ! delay(1500); ssd1306_init(); ssd1306_fillscreen(0x00); stopAnimate = 0; score = 0; playSpaceAttack(); top=topScoreB; 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, top); } for (int i = 0; i<1000; i = i+ 50){ beep(50,i); } delay(2000); 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,top); for (int i = 700; i>200; i = i - 50){ beep(30,i); } newHigh = 0; delay(2700); } } 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-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() { 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 111) player = 111; if (player <0) player = 0; /* if (digitalRead(0)==1){ if (digitalRead(2)==1) fire = 1; if (player >1){ player--; } } */ if ( (mothership == 0) && (random(0,1000) > 998) && (alienRow>0) ) { mothership = 1; mothershipX= 127-16; } // draw aliens for (int inc = 0; inc <= lastActiveRow; inc ++) { ssd1306_setpos((positionNow*8),inc+alienRow); ssd1306_send_data_start(); for (int bl = firstAlien; bl <=lastAlien; bl++){ if(row[inc][bl]==1){ if (inc == lastActiveRow) { if(random(0,1000) > (999-level)) { // this alien is going to fire! byte afIndex = 0; while (alienFire[afIndex][0] == 1) { afIndex++; // search for empty alien fire option if (afIndex == 5) break; } if (afIndex < 5) { // we've found a slot alienFire[afIndex][0] = 1; // activate fire on this slot alienFire[afIndex][1] = ( (positionNow*8) + ((bl-firstAlien) * 8) + 4); // x position alienFire[afIndex][2] = (inc+alienRow+1)*8; // Where the fire starts } } // end of this alien firing } // only if we are on the lowest row of live aliens if ( (inc == 0) || (inc == 2) ) { sendBlock(1); } else { sendBlock(2); } }else { sendBlock(0); } } ssd1306_send_data_stop(); } // Display the score doNumber(0,6,score); // Burn clock cycles to keep game at constant (ish) speed when there are low numbers of live aliens int burnLimit = (8-(lastAlien-firstAlien)); for (int burn = 0; burn < burnLimit; burn+=2) { drawPlatform(); } // Display the mothership if (mothercounter >= 3) { mothercounter = 0; // draw mothership if (mothership) { ssd1306_setpos(mothershipX,0); ssd1306_send_data_start(); sendBlock(3); sendBlock(0); ssd1306_send_data_stop(); mothershipX --; if (mothershipX == 0) { mothership = 0; ssd1306_setpos(mothershipX,0); ssd1306_send_data_start(); sendBlock(0); sendBlock(0); ssd1306_send_data_stop(); } } } // Move the aliens if (aliencounter >= (92-((level-1)*5)) ) { aliencounter = 0; if(alienDirection) { // Moving right // move down a row if (positionNow >= 6+(8-(lastAlien-firstAlien))) { alienDirection = 0; clearAlienArea(alienRow,lastActiveRow); alienRow++; } else { positionNow++; } } else { // Moving left // move down a row if (positionNow <= 0) { alienDirection = 1; clearAlienArea(alienRow,lastActiveRow); alienRow++; } else { positionNow --; } } clearAlienArea(alienRow,lastActiveRow); } // Fire ! if ((fire == 1) && (playerFire[0] == 0)) { // fire has been pressed and we're not currently firing - initiate fire!! playerFire[0] = 1; playerFire[1] = player+platformWidth/2; // xposition of new fire! playerFire[2] = 56; } // Handle all firing-related stuff (in both directions!) if (firecounter >= 2) { firecounter = 0; // --- Deal with player Firing --- if (playerFire[0] == 1) { drawFire(playerFire[1], playerFire[2]); if (playerFire[2] == 0) { ssd1306_setpos(playerFire[1],0); uint8_t temp = B00000000; ssd1306_send_data_start(); ssd1306_send_byte(temp); ssd1306_send_data_stop(); playerFire[0] = 0; fire = 0; } else { playerFire[2] = playerFire[2] - 1; } } // aliens are at positionNow * 8 + 8* their index leftLimit = positionNow*8; if ((fire == 1)) { fireXidx = firstAlien + floor((playerFire[1]-positionNow*8)/8); fireYidx= floor(floor(playerFire[2]/8) - alienRow); if((mothership == 1) && (playerFire[1] >= mothershipX) && (playerFire[1] <= mothershipX +8) && playerFire[2] <=8) { long scm = random(1,100); if (scm<30) { score +=50; } else if (scm<60) { score += 100; } else if (scm<90) { score += 150; } else { score += 300; } beep(30,400); beep(30,300); beep(30,200); beep(30,100); mothership = 0; ssd1306_setpos(mothershipX,0); ssd1306_send_data_start(); sendBlock(0); sendBlock(0); ssd1306_send_data_stop(); } // Alien has been hit if ((playerFire[1] >= leftLimit) && (fireYidx>=0) && (fireYidx<=lastActiveRow) && (fireXidx>=0) && (fireXidx<9)) { if (row[fireYidx][fireXidx] == 1) { int lastActiveToClear = lastActiveRow; // if we kill the last alien on a row - we still need to clear that row (end of this fn) if (fireYidx == 2) deadOn2++; if (fireYidx == 1) deadOn1++; if (deadOn2 == 5) {lastActiveRow = 1;} if ((deadOn1 == 4) && (deadOn2 == 5)) {lastActiveRow = 0;} score = score + (int)((3-fireYidx) * 10); aliensDead++; ssd1306_setpos(playerFire[1],alienRow+fireYidx+1); uint8_t temp = B00000000; ssd1306_send_data_start(); ssd1306_send_byte(temp); ssd1306_send_data_stop(); beep(30,100); fire = 0; playerFire[0] = 0; playerFire[1] = 0; playerFire[2] = 7; row[fireYidx][fireXidx] = 0; if (fireXidx == firstAlien) { // this is the first Alien - sweep back and reset to new first alien for (int xi = lastAlien; xi>=firstAlien;xi--) { if ( (row[0][xi] == 1) || (row[1][xi] == 1) || (row[2][xi] == 1) ) newFirst = xi; } positionNow += newFirst - firstAlien; firstAlien = newFirst; } if (fireXidx == lastAlien) { // this is the last Alien - sweep thru and reset to new last alien for (int xi = firstAlien; xi<=lastAlien;xi++) { if ( (row[0][xi] == 1) || (row[1][xi] == 1) || (row[2][xi] == 1) ) newLast = xi; } lastAlien = newLast; } clearAlienArea(alienRow,lastActiveToClear); } } } // --- Deal with Alien Firing --- for (byte afIndex = 0; afIndex<5; afIndex++) { if(alienFire[afIndex][0] == 1) { drawFire(alienFire[afIndex][1],alienFire[afIndex][2]); alienFire[afIndex][2] = alienFire[afIndex][2] + 1; if (alienFire[afIndex][2] >= 56) { ssd1306_setpos(alienFire[afIndex][1],7); uint8_t temp = B00000000; ssd1306_send_data_start(); ssd1306_send_byte(temp); ssd1306_send_data_stop(); alienFire[afIndex][0] = 0; // the fire's got to the end if( ((alienFire[afIndex][1]) > player) && ( (alienFire[afIndex][1]) < player+platformWidth) ) { // you've been hit!! stopAnimate = 1; goto die; } } } } // end of aliens firing } if (aliensDead == 14) { for (int ai = 0; ai <= 5; ai++) alienFire[ai][0] = 0; level++; if (level>15) level = 15; levelUp(level); resetAliens(); } if( ((alienRow == 5) && (lastActiveRow == 2))|| ( (alienRow == 6) && (lastActiveRow == 1)) || ((alienRow == 7) && (lastActiveRow == 0)) || stopAnimate) { stopAnimate = 1; break; } // draw the player drawPlatform(); } } die: topScoreB = EEPROM.read(0); topScoreB = topScoreB << 8; topScoreB = topScoreB | EEPROM.read(1); if (score > topScoreB) { topScoreB = score; EEPROM.write(1,score & 0xFF); EEPROM.write(0,(score>>8) & 0xFF); newHigh = 1; } } void drawPlatform(){ if(player > lastPlayer) { ssd1306_setpos(lastPlayer,7); ssd1306_send_data_start(); for (int pw = lastPlayer;pw <= player;pw++) ssd1306_send_byte(B00000000); ssd1306_send_data_stop(); } ssd1306_setpos(player,7); ssd1306_send_data_start(); ssd1306_send_byte(B00000000); ssd1306_send_byte(B00000000); for (byte pw = 0; pw <5; pw++){ssd1306_send_byte(B11000000);} for (byte pw = 0; pw <4; pw++){ssd1306_send_byte(B11110000);} for (byte pw = 0; pw <5; pw++){ssd1306_send_byte(B11000000);} ssd1306_send_byte(B00000000); ssd1306_send_byte(B00000000); if (player200; i = i - 200){ beep(30,i); } delay(700); ssd1306_fillscreen(0x00); } void drawFire(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((B01111110 | P1)>>P2); } // Drawing routine for the player and fire - with left-shifts void doDrawLS(long P1, byte P2) { ssd1306_send_byte((B01111110 | P1)< // ---------------------------------------------------------------------------- /* 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: Tetris_Attiny_Arcade/Tetris_Attiny_Arcade.ino ================================================ /* 2015 / 2016 /2017 Tetris for Attiny Arcade ======================== When the game is running : ========================== Tap LEFT BUTTON - Moves the piece to the left (and wraps around at the left edge) Hold LEFT BUTTON - Move the piece to the right Tap RIGHT BUTTON - Rotates the piece Hold RIGHT BUTTON - Place the piece Before the game starts: ======================= Hold LEFT BUTTON - Turns 'ghost' piece on and off Hold RIGHT and LEFT BUTTONS together - Turns 'challenge mode' on and off (which fills the screens with random stuff at the start of the game to make it more tricky!) _________________________________________________________________________________________________________________________________________________________ This version developed by Andy Jackson - Twitter @andyhighnumber - Tweet me if you have any problems getting this to compile or run. The code that does not fall under the licenses of sources listed below can be used non-commercially with or without attribution. This software is supplied without warranty of any kind. Designed for the Attiny85 and optimised for the #AttinyArcade platform. The source code is less than 8KB and the sketch should run happily with less than 300 bytes of RAM. You can find out more about this platform from http://webboggles.com/, buy kits to make it (or get instructions / schematics). This sketch includes some code from the #AttinyArcade games on that site, including interrupt code. This game started life as a port but is now essentially a clone of TinyTetris by Anthony Russell, with some additional features. There remain elements of that original codebase, although the vast majority of what's here has been rewritten from scratch (including the screen, text and number rendering code and much of the game engine) in order to optimise for memory, improve responsiveness and allow new features on the limited hardware (added features include Highscore (saved to EEPROM), optional Ghost (or Shadow) Piece, Interrupt Handling and Hard-Mode functionality). Anthony's source can be found here: https://github.com/AJRussell/Tiny-Tetris and is highly recommended if you'd like a version of Tetris to run on normal Arduino hardware. It has some lovely graphics by Tobozo (which sadly there's not space for on the Attiny85) and it's also possible that some code by Tobozo has made it into this version. Tobozo's repository can be found here; https://github.com/tobozo and is well worth a look. There is an Instructables page relating to this project here: https://www.instructables.com/id/Tetris-Clone-With-OLED-SSD1306I2C-for-Arduino-Nano/ 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 - hacked about by Andy Jackson to make them render side-on for this game. All the necessary functions are in this file, there's no need to download any additional libraries to compile this game. The sleep code in this file 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 */ // The custom font file is the only additional file you should need to compile this game #include "font8x8AJ.h" // Standard Arduino headers #include #include #include #include // needed for the additional interrupt // Mode settings for functions with multiple purposes #define NORMAL 0 #define GHOST 1 #define FULL 0 #define PARTIAL 1 #define DRAW 0 #define ERASE 1 // The horizontal width of the playing area #define HORIZ 10 // The vertical visible space of the playing area #define VERTDRAW 19 // The size of the array of blocks (some are outside visible area to allow them to drop in from off screen) #define VERTMAX 24 // The horizontal position where pieces begin #define STARTX 3 // The vertical position where pieces begin #define STARTY 19 // What level does the game start on #define STARTLEVEL 1 // The multiplying factor that sets how the speed scales with level (one level increment for every row cleared) #define LEVELFACTOR 4 // The number of milliseconds before each drop (baseline) #define DROPDELAY 600 #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 // The bitmaps for the little images of next block static const byte miniBlock[][4] PROGMEM = { {0x77, 0x77, 0x00, 0x00}, {0x70, 0x77, 0x70, 0x00}, {0x70, 0x00, 0x70, 0x77}, {0x70, 0x07, 0x70, 0x07}, {0x70, 0x07, 0x00, 0xEE}, {0x70, 0x77, 0x00, 0x0E}, {0x70, 0x07, 0xEE, 0x00} }; // The bitmaps for the main blocks static const int blocks[7] PROGMEM = { 0x4444, 0x44C0, 0x4460, 0x0660, 0x06C0, 0x0E40, 0x0C60}; // The bitmaps for blocks on the screen static const byte blockout[16] PROGMEM = { 0xF8, 0x00, 0x3E, 0x80, 0x0F, 0xE0, 0x03, 0xF8, 0x3E, 0x80, 0x0F, 0xE0, 0x03, 0xF8, 0x3E, 0x00}; // The bitmaps for ghost blocks on the screen static const byte ghostout[16] PROGMEM = { 0x88, 0x00, 0x22, 0x80, 0x08, 0x20, 0x02, 0x88, 0x22, 0x80, 0x08, 0x20, 0x02, 0x88, 0x22, 0x00}; // Decode lookup to translate block positions to the 8 columns on the screen static const byte startDecode[11] PROGMEM = {0,1,1,2,3,4,4,5,6,7,8}; static const byte endDecode[11] PROGMEM = {1,2,3,3,4,5,6,6,7,8,8}; // Function prototypes - generic ones I use in all games void doNumber (int x, int y, int value); void beep(int,int); // Function prototypes - screen control modified from 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_f8x8(uint8_t x, uint8_t y, const char ch[]); // Function prototypes - tetris-specific void playTetris(void); void handleInput(void); void drawScreen(int startCol, int endCol, int startRow, int endRow, byte mode); void drawScreenBorder(void); byte readBlockArray(byte x, byte y); void writeblockArray(byte x, byte y, bool value); byte readGhostArray(byte x, byte y); void writeGhostArray(byte x, byte y, bool value); void fillGrid(byte value, bool mode); void rotatePiece(void); bool movePieceDown(void); void movePieceLeft(void); void movePieceRight(void); byte checkCollision(void); bool createGhost(void); void drawGhost(byte action); void loadPiece(byte pieceNumber, byte row, byte column); void drawPiece(byte action); void setNextBlock(byte pieceNumber); // Variables struct pieceSpace { byte blocks[4][4]; int row; int column; }; pieceSpace currentPiece = {0}; // The piece in play pieceSpace oldPiece = {0}; // Buffer to hold the current piece whilst its manipulated pieceSpace ghostPiece = {0}; // Current ghost piece unsigned long moveTime = 0; // Baseline time for current move unsigned long keyTime = 0; // Baseline time for current keypress byte keylock = 0; // Holds the mode of the last keypress (for debounce and stuff) byte nextBlockBuffer[8][2]; // The little image of the next block byte nextPiece = 0; // The identity of the next piece byte blockArray[HORIZ][3]; // The byte-array of blocks byte ghostArray[HORIZ][3]; // The byte-array of ghost pieces bool stopAnimate; // True when the game is running int lastGhostRow = 0; // Buffer to hold previous ghost position - for accurate drawing int score = 0; // Score buffer int topScore = 0; // High score buffer bool challengeMode = 0; // Is the system in "Hard" mode? bool ghost = 1; // Is the ghost active? int level = 0; // Current level (increments once per cleared line) 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_f8x8(x, y, temp); } void beep(int bCount,int bDelay){ for (int i = 0; i<=bCount; i++){digitalWrite(1,HIGH);for(int i2=0; i2 0) c = c - 12; // to dash if (c > 15) c = c - 7; if (c > 40) c = c - 6; ssd1306_setpos(y, x); ssd1306_send_data_start(); for (byte lxn = 0; lxn < 8; lxn++) { ssd1306_send_byte(pgm_read_byte(&font[c][7-lxn])); } ssd1306_send_data_stop(); x+= 1; j++; } } // Interrupt handlers - to make sure every button press is caught promptly! ISR(PCINT0_vect) { // PB0 pin button interrupt if (keylock == 0) { keylock = 1; keyTime = millis(); } } void playerIncTetris(){ // PB2 pin button interrupt if (keylock == 0) { keylock = 2; keyTime = millis(); } } // Screen control functions 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,VERTDRAWical 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(); } } // Sleep code from http://www.re-innovation.co.uk/web12/index.php/en/blog-75/306-sleep-modes-on-attiny85 void system_sleep() { ssd1306_fillscreen(0x00); ssd1306_send_command(0xAE); cbi(ADCSRA,ADEN); // switch Analog to DigitalconVERTDRAWer 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 DigitalconVERTDRAWer ON ssd1306_send_command(0xAF); } // Arduino stuff 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 ssd1306_init(); // initialise the screen keylock = 0; } void loop() { ssd1306_init(); ssd1306_fillscreen(0x00); ssd1306_char_f8x8(0, 88," ------ "); ssd1306_char_f8x8(0, 80," TETRIS "); ssd1306_char_f8x8(0, 72, " ------ "); ssd1306_char_f8x8(0, 64, " Attiny "); ssd1306_char_f8x8(0, 56, " Arcade "); ssd1306_char_f8x8(0, 48, " ------ "); 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) { if (challengeMode == 0) { challengeMode = 1; ssd1306_char_f8x8(0, 16, " HARD "); ssd1306_char_f8x8(0, 8, " MODE "); } else { challengeMode = 0; ssd1306_char_f8x8(0, 16, " NORMAL"); ssd1306_char_f8x8(0, 8, " MODE "); } } else if (ghost == 0) { ghost = 1; ssd1306_char_f8x8(0, 16, " GHOST "); ssd1306_char_f8x8(0, 8, " ON "); } else { ghost = 0; ssd1306_char_f8x8(0, 16, " GHOST "); ssd1306_char_f8x8(0, 8, " OFF "); } break; } if (sChange == 1) break; } while(digitalRead(0) == HIGH); if (sChange == 0) { delay(2500); ssd1306_fillscreen(0x00); playTetris(); } delay(1000); system_sleep(); } byte readBlockArray(byte x, byte y) { if (y < 8) { return ((blockArray[x][0] & B00000001 << y) >> y); } else if (y > 15) { return ((blockArray[x][2] & B00000001 << y-15) >> y-15); } else { return ((blockArray[x][1] & B00000001 << y-8) >> y-8); } } void writeblockArray(byte x, byte y, bool value) { byte arr = 0; if (y<8) { // do nothing } else if (y > 15) { arr = 2; y-=15; } else { arr = 1; y-=8; } if (value == 1) blockArray[x][arr] |= B00000001 << y; else blockArray[x][arr] &= (B11111110 << y) | (B01111111 >> 7-y); } byte readGhostArray(byte x, byte y) { if (y < 8) { return ((ghostArray[x][0] & B00000001 << y) >> y); } else if (y > 15) { return ((ghostArray[x][2] & B00000001 << y-15) >> y-15); } else { return ((ghostArray[x][1] & B00000001 << y-8) >> y-8); } } void writeGhostArray(byte x, byte y, bool value) { byte arr = 0; if (y<8) { // do nothing } else if (y > 15) { arr = 2; y-=15; } else { arr = 1; y-=8; } if (value == 1) ghostArray[x][arr] |= B00000001 << y; else ghostArray[x][arr] &= (B11111110 << y) | (B01111111 >> 7-y); } void fillGrid(byte value, bool mode) { for (char r = 0; r < VERTMAX; r++) { for (char c = 0; c < HORIZ; c++) { if (mode == GHOST) writeGhostArray(c,r, value); else writeblockArray(c,r, value); } } } void rotatePiece(void) { byte blocks[4][4]; memcpy(oldPiece.blocks, currentPiece.blocks, 16); oldPiece.row = currentPiece.row; oldPiece.column = currentPiece.column; for (byte i = 0; i < 4; ++i) { for (byte j = 0; j < 4; ++j) { blocks[j][i] = currentPiece.blocks[4 - i - 1][j]; } } oldPiece = currentPiece; memcpy(currentPiece.blocks, blocks, 16); if (checkCollision()) currentPiece = oldPiece; else { drawGhost(ERASE); if (createGhost()) drawGhost(DRAW); } } bool movePieceDown(void) { int rndPiece = 0; memcpy(oldPiece.blocks, currentPiece.blocks, 16); oldPiece.row = currentPiece.row; oldPiece.column = currentPiece.column; currentPiece.row--; //check collision if (checkCollision()) { currentPiece.row = oldPiece.row; drawPiece(DRAW); byte totalRows = 0; for (byte row = 0; row < VERTMAX; row++) { // scan the whole block (it's quick - there's no drawing to do) bool rowFull = 1; for (byte col = 0; col < HORIZ; col++) { // scan across this row - every column if (readBlockArray(col,row) == 0) rowFull = 0; // if we hit any blank spaces, the row's not full } if (rowFull) { totalRows++; for (int i = 800; i>200; i = i - 200)beep(30,i); // happy sound for (byte col = 0; col < HORIZ; col++) writeblockArray(col,row,0); // write zeros across this whole row drawGameScreen(0,HORIZ-1,row,row+1,PARTIAL); // draw the row we're removing (for animation) delay(30); // delay slightly to make the deletion of rows visible for (byte dropCol = 0; dropCol < HORIZ; dropCol++) { // for every column for (byte dropRow = row; dropRow < VERTMAX-1; dropRow ++) writeblockArray(dropCol,dropRow,readBlockArray(dropCol,dropRow+1)); // drop everything down as many as the row's we've cleared } row--; // we need to check this row again as it could now have things in it! } } level += totalRows; switch (totalRows) { case 1: score += 40; break; case 2: score += 100; break; case 3: score += 300; break; case 4: score += 800; } drawGameScreen(0,10, 0,VERTDRAW,FULL); loadPiece(nextPiece, STARTY, STARTX); if (checkCollision()) { stopAnimate = true; } else { loadPiece(nextPiece, STARTY, STARTX); drawGhost(ERASE); if (createGhost()) drawGhost(DRAW); } nextPiece = random(1, 8); setNextBlock(nextPiece); } drawGhost(ERASE); if (createGhost()) drawGhost(DRAW); } void movePieceLeft(void) { byte response; oldPiece = currentPiece; currentPiece.column = currentPiece.column - 1; response = checkCollision(); if (response == 1) { currentPiece = oldPiece; // back to where it was } else if (response == 2) { int wide = 0; for (byte i = 0;i<4;i++) { if (currentPiece.blocks[3][i]) wide = 1; } boolean narrow = true; for (byte i = 0;i<4;i++) { if (currentPiece.blocks[2][i]) narrow = false; } if (narrow) wide = -1; currentPiece.column = 7-wide; response = checkCollision(); // Check again - does wrapping around cause a collision? if (response == 1) { currentPiece = oldPiece; // back to where it was } else { drawGhost(ERASE); if (createGhost()) drawGhost(DRAW); drawGameScreen(0,10,0,VERTDRAW,FULL); } } else { drawGhost(ERASE); if (createGhost()) drawGhost(DRAW); } } void movePieceRight(void) { oldPiece = currentPiece; currentPiece.column = currentPiece.column + 1; //check collision if (checkCollision()) { currentPiece = oldPiece; // back to where it was } else { drawGhost(ERASE); if (createGhost()) drawGhost(DRAW); } } byte checkCollision(void) { byte pieceRow = 0; byte pieceColumn = 0; for (int c = currentPiece.column; c < currentPiece.column + 4; c++) { for (int r = currentPiece.row; r < currentPiece.row + 4; r++) { if (currentPiece.blocks[pieceColumn][pieceRow]) { if (c < 0) return 2; if (c > 9) return 1; if (r < 0) return 1; if (c >= 0 && r >= 0 && c < HORIZ && r < VERTMAX) { if (readBlockArray(c,r)) { return 1; //is it on landed blocks? } } } pieceRow++; } pieceRow = 0; pieceColumn++; } return 0; } void handleInput(void) { if (digitalRead(2) == HIGH && keylock == 2 && millis() - keyTime > 300) { while (digitalRead(2) == HIGH) { drawPiece(ERASE); movePieceDown(); drawPiece(DRAW); drawGameScreen(currentPiece.column, currentPiece.column + 4, currentPiece.row, currentPiece.row+5,PARTIAL); delay(10); if (stopAnimate) return; } keylock = 0; } if (digitalRead(0) == HIGH && (keylock == 1 || keylock == 3) && millis() - keyTime > 200) { drawPiece(ERASE); movePieceRight(); drawPiece(DRAW); drawGameScreen(currentPiece.column-1, currentPiece.column + 4, currentPiece.row, currentPiece.row+4,PARTIAL); keyTime = millis() + 100; keylock = 3; } if (digitalRead(0) == LOW && digitalRead(2) == LOW) { if (keylock == 2 && millis() - keyTime < 300) { drawPiece(ERASE); rotatePiece(); drawPiece(DRAW); drawGameScreen(currentPiece.column, currentPiece.column + 4, currentPiece.row, currentPiece.row+4,PARTIAL); } else if (keylock == 1) { drawPiece(ERASE); movePieceLeft(); drawPiece(DRAW); drawGameScreen(currentPiece.column, currentPiece.column + 5, currentPiece.row, currentPiece.row+4, PARTIAL); } keylock = 0; } delay(30); } void setNextBlock(byte pieceNumber) { memset(nextBlockBuffer, 0, sizeof nextBlockBuffer); //clear buffer pieceNumber--; if (pieceNumber == 0) { for (int k = 2; k < 6; k++) { nextBlockBuffer[k][0] = pgm_read_byte(&miniBlock[pieceNumber][0]); nextBlockBuffer[k][1] = pgm_read_byte(&miniBlock[pieceNumber][0]); } } else { for (int k = 0; k < 3; k++) { nextBlockBuffer[k][0] = pgm_read_byte(&miniBlock[pieceNumber][0]); nextBlockBuffer[k][1] = pgm_read_byte(&miniBlock[pieceNumber][1]); } for (int k = 4; k < 7; k++) { nextBlockBuffer[k][0] = pgm_read_byte(&miniBlock[pieceNumber][2]); nextBlockBuffer[k][1] = pgm_read_byte(&miniBlock[pieceNumber][3]); } } drawGameScreen(0,10,0,VERTDRAW, FULL); } void drawScreenBorder(void) { ssd1306_setpos(0, 0); ssd1306_send_data_start(); ssd1306_send_byte(0xFF); for (byte c = 1; c < 126; c++) { ssd1306_send_byte(B00000001); } ssd1306_send_byte(0xFF); ssd1306_send_data_stop(); for (byte r = 1; r < 7; r++) { ssd1306_setpos(0, r); ssd1306_send_data_start(); ssd1306_send_byte(0xFF); for (byte c = 1; c < 126; c++) { ssd1306_send_byte(B00000000); } ssd1306_send_byte(0xFF); } ssd1306_send_data_stop(); ssd1306_setpos(0, 7); ssd1306_send_data_start(); ssd1306_send_byte(0xFF); for (byte c = 1; c < 126; c++) { ssd1306_send_byte(B10000000); } ssd1306_send_byte(0xFF); ssd1306_send_data_stop(); } void displayScore(int score, int xpos, int y, bool blank) { byte scoreOut[6]; scoreOut[5] = (score % 10); scoreOut[4] = ((score / 10) % 10); scoreOut[3] = ((score / 100) % 10); scoreOut[2] = ((score / 1000) % 10); scoreOut[1] = ((score / 10000) % 10); scoreOut[0] = ((score / 100000) % 10); for (byte x = xpos; x 10) endCol = 10; if(startRow < 0) startRow = 0; if(endRow > VERTDRAW) endRow = VERTDRAW; byte startScreenCol = pgm_read_byte(&startDecode[startCol]); byte endScreenCol = pgm_read_byte(&endDecode[endCol]); scoreOut[5] = (score % 10); scoreOut[4] = ((score / 10) % 10); scoreOut[3] = ((score / 100) % 10); scoreOut[2] = ((score / 1000) % 10); scoreOut[1] = ((score / 10000) % 10); scoreOut[0] = ((score / 100000) % 10); for (byte col = startScreenCol; col < endScreenCol; col++) { if (col < 4) reader = col; else if (col < 7) reader = col+1; else reader = col + 2; blockReader = 2 * col; ssd1306_setpos(startRow*6, col); // Start from the end of this column (working up the screen) on the required row ssd1306_send_data_start(); if (startRow == 0) ssd1306_send_byte(B11111111); else { if (col == 0) ssd1306_send_byte(B00000001); else if (col == 7) ssd1306_send_byte(B10000000); else ssd1306_send_byte(B00000000); } for (byte r = startRow; r < endRow; r++ ) { // For each row in the array of tetris blocks for (byte piece = 0; piece < 5; piece ++) { // for each of the 5 filled lines of the block if (col == 0) temp = B00000001; else if (col == 7) temp = B10000000; else temp = 0x00; // if we're on the far left, draw the left wall, on the far right draw the right wall, otherwise its a blank separator between blocks separator = temp; // we'll need this again later! if (readBlockArray(reader,r)) { temp = temp | pgm_read_byte(&blockout[blockReader]); } if (readBlockArray(reader+1,r)) { temp = temp | pgm_read_byte(&blockout[blockReader+1]); } if(ghost) { if (readGhostArray(reader,r) && (piece == 0 || piece == 4)) { temp = temp | pgm_read_byte(&blockout[blockReader]); } else if (readGhostArray(reader,r)) { temp = temp | pgm_read_byte(&ghostout[blockReader]); } if (readGhostArray(reader+1,r) && (piece == 0 || piece == 4)) { temp = temp | pgm_read_byte(&blockout[blockReader+1]); } else if (readGhostArray(reader+1,r)) { temp = temp | pgm_read_byte(&ghostout[blockReader+1]); } } ssd1306_send_byte(temp); } ssd1306_send_byte(separator); // between blocks - same one as we used at the start } if (mode == FULL) if (col < 6) for (byte numline = 0; numline < 8; numline++) ssd1306_send_byte(pgm_read_byte(&font[scoreOut[col]+4][7-numline])); else for (byte blockline = 0; blockline < 8; blockline++) ssd1306_send_byte(nextBlockBuffer[blockline][col-6]); ssd1306_send_data_stop(); } } bool createGhost(void) { byte tempRow = currentPiece.row; if (currentPiece.row < 3) return 0; currentPiece.row-=2; while(checkCollision() == 0) currentPiece.row--; memcpy(ghostPiece.blocks, currentPiece.blocks, 16); ghostPiece.row = currentPiece.row+1; ghostPiece.column = currentPiece.column; currentPiece.row = tempRow; if (ghostPiece.row > currentPiece.row - 3) return 0; else return 1; } void loadPiece(byte pieceNumber, byte row, byte column) { byte incr = 0; pieceNumber--; for (byte lxn = 0; lxn < 4; lxn++) { for (byte lxn2 = 0; lxn2 < 4; lxn2++) { if ( ((1 << incr) & pgm_read_word(&blocks[pieceNumber])) >> incr == 1) { currentPiece.blocks[lxn][lxn2] = 1; } else currentPiece.blocks[lxn][lxn2] = 0; incr++; } } currentPiece.row = row; currentPiece.column = column; } void drawPiece(byte action) { for (byte lxn = 0; lxn < 4; lxn++) { for (byte lxn2 = 0; lxn2 < 4; lxn2++) { if (currentPiece.blocks[lxn][lxn2] == 1) { if (action == DRAW) writeblockArray(currentPiece.column + lxn,currentPiece.row + lxn2,1); else if (action == ERASE) writeblockArray(currentPiece.column + lxn,currentPiece.row + lxn2,0); } } } } void drawGhost(byte action) { for (byte lxn = 0; lxn < 4; lxn++) { for (byte lxn2 = 0; lxn2 < 4; lxn2++) { if (ghostPiece.blocks[lxn][lxn2] == 1) { if (action == DRAW) writeGhostArray(ghostPiece.column + lxn,ghostPiece.row + lxn2,1); else if (action == ERASE) {writeGhostArray(ghostPiece.column + lxn,ghostPiece.row + lxn2,0); lastGhostRow = ghostPiece.row; } } } } } void playTetris(void) { stopAnimate = 0; score = 0; fillGrid(0, NORMAL); fillGrid(0, GHOST); // Attach the interrupt to read key 2 attachInterrupt(0,playerIncTetris,RISING); loadPiece(random(1, 8), STARTY, STARTX); drawPiece(DRAW); if (createGhost()) drawGhost(DRAW); drawGhost(DRAW); nextPiece = random(1, 8); setNextBlock(nextPiece); // Fill up the screen with random crap if it's in challenge mode! if (challengeMode) { for (byte cl = 0; cl < 100; cl++) { drawPiece(ERASE); movePieceDown(); if (random(1,8) >4) movePieceLeft(); drawPiece(DRAW); } } // Reset the level level = STARTLEVEL; drawGameScreen(0,10,0,VERTDRAW, FULL); while (stopAnimate == 0) { drawPiece(ERASE); movePieceDown(); drawPiece(DRAW); drawGameScreen(currentPiece.column, currentPiece.column + 4, currentPiece.row, currentPiece.row+5, PARTIAL); moveTime = millis(); if (level * LEVELFACTOR > DROPDELAY) level = DROPDELAY / LEVELFACTOR; while ((millis() - moveTime) < (DROPDELAY - level* LEVELFACTOR)) { handleInput(); } } ssd1306_fillscreen(0x00); bool newHigh = false; topScore = EEPROM.read(0); topScore = topScore << 8; topScore = topScore | EEPROM.read(1); if (score > topScore) { topScore = score; EEPROM.write(1,score & 0xFF); EEPROM.write(0,(score>>8) & 0xFF); newHigh = true; } drawScreenBorder(); displayScore(score, 1,80,0); displayScore(topScore, 1,40,0); for (int i = 0; i<1000; i = i+ 50){ beep(50,i); } for (byte lx = 0; lx<4;lx++) { displayScore(score, 1,80,1); if (newHigh) displayScore(topScore, 1,40,1); delay(200); displayScore(score, 1,80,0); if (newHigh) displayScore(topScore, 1,40,0); delay(200); } } ================================================ FILE: Tetris_Attiny_Arcade/font8x8AJ.h ================================================ /* * This font is from http://arduino-er.blogspot.co.uk/2014/08/port-ascii-font-to-arduino-88-led-matrix.html * * Source code adapted by @andyhighnumber from that available at: https://bitbucket.org/tinusaur/ssd1306xled * */ // ---------------------------------------------------------------------------- #include // ---------------------------------------------------------------------------- static const byte font[][8] PROGMEM = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space) { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.) { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/) { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0) { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1) { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2) { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3) { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4) { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5) { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6) { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7) { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8) { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9) { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A) { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B) { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C) { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D) { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E) { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F) { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G) { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H) { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I) { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J) { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K) { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L) { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M) { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N) { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O) { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P) { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q) { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R) { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S) { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T) { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U) { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V) { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W) { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X) { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y) { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z) { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a) { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b) { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c) { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d) { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e) { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f) { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g) { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h) { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i) { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j) { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k) { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l) { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m) { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n) { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o) { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p) { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q) { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r) { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s) { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t) { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u) { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v) { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w) { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x) { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y) { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z) }; // ---------------------------------------------------------------------------- ================================================ FILE: Tetris_Multi_Button/Tetris_Multi_Button.ino ================================================ /* 2015 / 2016 /2017 Tetris for Attiny Arcade ======================== Before the game starts: ======================= Hold "DROP" BUTTON - Turns 'ghost' piece on and off Hold "DROP" and "ROTATE" together - Turns 'challenge mode' on and off (which fills the screens with random stuff at the start of the game to make it more tricky!) _________________________________________________________________________________________________________________________________________________________ This version developed by Andy Jackson - Twitter @andyhighnumber - Tweet me if you have any problems getting this to compile or run. The code that does not fall under the licenses of sources listed below can be used non-commercially with or without attribution. This software is supplied without warranty of any kind. This code is designed to run on (very simple and cheap to build) custom hardware and you should find a schematic for the circuit in the folder where you found this file. Because the voltages of the switches are important, it's good to insulate the back of the board from fingers if you can (I use hot melt glue but other methods would work - ideally put it in a case!) Designed originally for the Attiny85 and optimised for the #AttinyArcade platform. The source code is less than 8KB and the sketch should run happily with less than 300 bytes of RAM. You can find out more about this platform from http://webboggles.com/, buy kits to make it (or get instructions / schematics). This sketch includes some code from the #AttinyArcade games on that site, including interrupt code. This game started life as a port but is now essentially a clone of TinyTetris by Anthony Russell, with some additional features. There remain elements of that original codebase, although the vast majority of what's here has been rewritten from scratch (including the screen, text and number rendering code and much of the game engine) in order to optimise for memory, improve responsiveness and allow new features on the limited hardware (added features include Highscore (saved to EEPROM), optional Ghost (or Shadow) Piece, Interrupt Handling and Hard-Mode functionality). Anthony's source can be found here: https://github.com/AJRussell/Tiny-Tetris and is highly recommended if you'd like a version of Tetris to run on normal Arduino hardware. It has some lovely graphics by Tobozo (one image from which is now inclded the opening screen here!) and it's also possible that some code by Tobozo has made it into this version. Tobozo's repository can be found here; https://github.com/tobozo and is well worth a look. There is an Instructables page relating to this project here: https://www.instructables.com/id/Tetris-Clone-With-OLED-SSD1306I2C-for-Arduino-Nano/ 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 - hacked about by Andy Jackson to make them render side-on for this game. All the necessary functions are in this file, there's no need to download any additional libraries to compile this game. The sleep code in this file 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 */ // The custom font file is the only additional file you should need to compile this game #include "font8x8AJ.h" // Standard Arduino headers #include #include #include #include // needed for the additional interrupt // Mode settings for functions with multiple purposes #define NORMAL 0 #define GHOST 1 #define FULL 0 #define PARTIAL 1 #define DRAW 0 #define ERASE 1 // The horizontal width of the playing area #define HORIZ 10 // The vertical visible space of the playing area #define VERTDRAW 19 // The size of the array of blocks (some are outside visible area to allow them to drop in from off screen) #define VERTMAX 24 // The horizontal position where pieces begin #define STARTX 3 // The vertical position where pieces begin #define STARTY 19 // What level does the game start on #define STARTLEVEL 1 // The multiplying factor that sets how the speed scales with level (one level increment for every row cleared) #define LEVELFACTOR 4 // The number of milliseconds before each drop (baseline) #define DROPDELAY 600 #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 // The bitmaps for the little images of next block static const byte miniBlock[][4] PROGMEM = { {0x77, 0x77, 0x00, 0x00}, {0x70, 0x77, 0x70, 0x00}, {0x70, 0x00, 0x70, 0x77}, {0x70, 0x07, 0x70, 0x07}, {0x70, 0x07, 0x00, 0xEE}, {0x70, 0x77, 0x00, 0x0E}, {0x70, 0x07, 0xEE, 0x00} }; // The bitmaps for the main blocks static const int blocks[7] PROGMEM = { 0x4444, 0x44C0, 0x4460, 0x0660, 0x06C0, 0x0E40, 0x0C60}; // The bitmaps for blocks on the screen static const byte blockout[16] PROGMEM = { 0xF8, 0x00, 0x3E, 0x80, 0x0F, 0xE0, 0x03, 0xF8, 0x3E, 0x80, 0x0F, 0xE0, 0x03, 0xF8, 0x3E, 0x00}; // The bitmaps for ghost blocks on the screen static const byte ghostout[16] PROGMEM = { 0x88, 0x00, 0x22, 0x80, 0x08, 0x20, 0x02, 0x88, 0x22, 0x80, 0x08, 0x20, 0x02, 0x88, 0x22, 0x00}; // Decode lookup to translate block positions to the 8 columns on the screen static const byte startDecode[11] PROGMEM = {0,1,1,2,3,4,4,5,6,7,8}; static const byte endDecode[11] PROGMEM = {1,2,3,3,4,5,6,6,7,8,8}; // The logo on the opening screen - adapted from the original by Tobozo https://github.com/tobozo const byte brickLogo[] PROGMEM= { 0x01, 0x01, 0x01, 0x01, 0x81, 0x81, 0xC1, 0xE1, 0xF1, 0xF1, 0x01, 0x11, 0xF1, 0xF1, 0xE1, 0xC1, 0xC1, 0x81, 0x81, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xFC, 0xFC, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x7F, 0x3F, 0xBF, 0x9F, 0xCF, 0xEF, 0xE7, 0xF7, 0xFB, 0xE0, 0x01, 0xFB, 0xF3, 0xF7, 0xE7, 0xEF, 0xCF, 0xDF, 0xDF, 0xBF, 0xBF, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0x06, 0xFE, 0xFC, 0xFC, 0xFC, 0xF8, 0xF8, 0xF0, 0xF0, 0xE0, 0x00, 0x00, 0xFF, 0xFF, 0x7F, 0x7F, 0x3F, 0xBF, 0x9F, 0xDF, 0xCF, 0xEF, 0xEF, 0xE4, 0x00, 0xF7, 0xE7, 0xEF, 0xEF, 0xCF, 0xDF, 0xDF, 0x9F, 0xBF, 0xBF, 0x3F, 0x00, 0x07, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0xFC, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0E, 0x3E, 0x1E, 0x1C, 0x1D, 0x0D, 0x09, 0x03, 0x03, 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x7F, 0x3F, 0x00, 0x3F, 0x3F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x83, 0x83, 0x83, 0x89, 0x8D, 0x8D, 0x8C, 0x8E, 0x8E, 0x8E, 0x8F, 0x8F, 0x9F, 0x8F, 0x8F, 0x8F, 0x8F, 0x87, 0x86, 0x86, 0x82, 0x82, 0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80}; // Function prototypes - generic ones I use in all games void beep(int,int); // Function prototypes - screen control modified from 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_f8x8(uint8_t x, uint8_t y, const char ch[]); // Function prototypes - tetris-specific void playTetris(void); void handleInput(void); void drawScreen(int startCol, int endCol, int startRow, int endRow, byte mode); void drawScreenBorder(void); byte readBlockArray(byte x, byte y); void writeblockArray(byte x, byte y, bool value); byte readGhostArray(byte x, byte y); void writeGhostArray(byte x, byte y, bool value); void fillGrid(byte value, bool mode); void rotatePiece(void); bool movePieceDown(void); void movePieceLeft(void); void movePieceRight(void); byte checkCollision(void); bool createGhost(void); void drawGhost(byte action); void loadPiece(byte pieceNumber, byte row, byte column); void drawPiece(byte action); void setNextBlock(byte pieceNumber); // Variables struct pieceSpace { byte blocks[4][4]; int row; int column; }; pieceSpace currentPiece = {0}; // The piece in play pieceSpace oldPiece = {0}; // Buffer to hold the current piece whilst its manipulated pieceSpace ghostPiece = {0}; // Current ghost piece unsigned long moveTime = 0; // Baseline time for current move unsigned long keyTime = 0; // Baseline time for current keypress byte keyLock = 0; // Holds the mode of the last keypress (for debounce and stuff) byte nextBlockBuffer[8][2]; // The little image of the next block byte nextPiece = 0; // The identity of the next piece byte blockArray[HORIZ][3]; // The byte-array of blocks byte ghostArray[HORIZ][3]; // The byte-array of ghost pieces bool stopAnimate; // True when the game is running int lastGhostRow = 0; // Buffer to hold previous ghost position - for accurate drawing int score = 0; // Score buffer int topScore = 0; // High score buffer bool challengeMode = 0; // Is the system in "Hard" mode? bool ghost = 1; // Is the ghost active? int level = 0; // Current level (increments once per cleared line) /* * Interrupt handlers - to make sure every button press is caught promptly! */ // PB0 pin button interrupt ISR(PCINT0_vect) { if (keyLock == 0) { keyLock = 4; // Nothing complicated to do here - this is picked up in the handleInput() function } } /* * This interrupt routine relies on having three buttons wired to pin 7 of the Attiny85 - all with a single pull-down resistor (10k) and * - One that pulls-up to +Vcc (giving an analog reading of 1023) * - One that pulls-up via a 1k resistor (analog reading somewhere around 930ish) * - One that pulls-up via a 2.2 resistor (analog reading around 840) * * The key point is that all three buttons provide a voltage that sends the pin to logic-high, thus triggering the interrupt * (therefore you could add more buttons, as long as the voltage is always above the threshold that the Attiny85 would class * as logic-high). * * In practice, this technique requires a reasonably well made and well insulated board - adding moisture to to board (via damp * hands for example) would likely throw the voltages out enough that this technique would become unreliable. * */ // PB2 pin button interrupt void playerIncTetris() { if (keyLock == 0) { int ic = analogRead(1); if (ic > 800 && ic < 855) { keyLock = 1; } else if (ic > 855 && ic < 970) { keyLock = 2; } else if (ic > 970 && ic < 1025) { keyLock = 3; } } } // Beep function - originally from the games by www.webboggles.com void beep(int bCount,int bDelay){ for (int i = 0; i<=bCount; i++){digitalWrite(1,HIGH);for(int i2=0; i2 0) c = c - 12; // to dash if (c > 15) c = c - 7; if (c > 40) c = c - 6; ssd1306_setpos(y, x); ssd1306_send_data_start(); for (byte lxn = 0; lxn < 8; lxn++) { ssd1306_send_byte(pgm_read_byte(&font[c][7-lxn])); } ssd1306_send_data_stop(); x+= 1; j++; } } 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,VERTDRAWical 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(); } } // Sleep code from http://www.re-innovation.co.uk/web12/index.php/en/blog-75/306-sleep-modes-on-attiny85 void system_sleep() { ssd1306_fillscreen(0x00); ssd1306_send_command(0xAE); cbi(ADCSRA,ADEN); // switch Analog to DigitalconVERTDRAWer 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 DigitalconVERTDRAWer ON ssd1306_send_command(0xAF); } // Arduino stuff 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 ssd1306_init(); // initialise the screen } void loop() { ssd1306_init(); ssd1306_fillscreen(0x00); drawScreenBorder(); ssd1306_char_f8x8(1, 64,"TETRIS"); ssd1306_char_f8x8(1, 48, "Attiny"); ssd1306_char_f8x8(1, 40, "Arcade"); drawScreenBorder(); for (byte lxn = 0; lxn < 8; lxn++) { ssd1306_setpos(78, lxn); ssd1306_send_data_start(); for (byte lxn2 = 0; lxn2 < 36; lxn2++) { ssd1306_send_byte(pgm_read_byte(&brickLogo[36*lxn+lxn2])); } ssd1306_send_data_stop(); } 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) { ssd1306_char_f8x8(2, 8, "MODE"); if (challengeMode == 0) { challengeMode = 1; ssd1306_char_f8x8(2, 16, "HARD"); } else { challengeMode = 0; ssd1306_char_f8x8(1, 16, "NORMAL"); } } else { ssd1306_char_f8x8(1, 16, "GHOST"); if (ghost == 0) { ghost = 1; ssd1306_char_f8x8(2, 8, "ON"); } else { ghost = 0; ssd1306_char_f8x8(2, 8, "OFF"); } } break; } if (sChange == 1) break; } while(digitalRead(0) == HIGH); if (sChange == 0) { delay(1600); ssd1306_char_f8x8(1, 20, "Andy-J"); delay(1500); ssd1306_fillscreen(0x00); playTetris(); } delay(1000); system_sleep(); } /* * These functional allow optimal storing of the array of Tetris and Ghost blocks by using a 10x3 array of bytes and extracting the relevant bit through shifting and masking */ byte readBlockArray(byte x, byte y) { if (y < 8) { return ((blockArray[x][0] & B00000001 << y) >> y); } else if (y > 15) { return ((blockArray[x][2] & B00000001 << y-15) >> y-15); } else { return ((blockArray[x][1] & B00000001 << y-8) >> y-8); } } void writeblockArray(byte x, byte y, bool value) { byte arr = 0; if (y<8) { // do nothing } else if (y > 15) { arr = 2; y-=15; } else { arr = 1; y-=8; } if (value == 1) blockArray[x][arr] |= B00000001 << y; else blockArray[x][arr] &= (B11111110 << y) | (B01111111 >> 7-y); } byte readGhostArray(byte x, byte y) { if (y < 8) { return ((ghostArray[x][0] & B00000001 << y) >> y); } else if (y > 15) { return ((ghostArray[x][2] & B00000001 << y-15) >> y-15); } else { return ((ghostArray[x][1] & B00000001 << y-8) >> y-8); } } void writeGhostArray(byte x, byte y, bool value) { byte arr = 0; if (y<8) { // do nothing } else if (y > 15) { arr = 2; y-=15; } else { arr = 1; y-=8; } if (value == 1) ghostArray[x][arr] |= B00000001 << y; else ghostArray[x][arr] &= (B11111110 << y) | (B01111111 >> 7-y); } void fillGrid(byte value, bool mode) { for (char r = 0; r < VERTMAX; r++) { for (char c = 0; c < HORIZ; c++) { if (mode == GHOST) writeGhostArray(c,r, value); else writeblockArray(c,r, value); } } } void rotatePiece(void) { byte blocks[4][4]; memcpy(oldPiece.blocks, currentPiece.blocks, 16); oldPiece.row = currentPiece.row; oldPiece.column = currentPiece.column; for (byte i = 0; i < 4; ++i) { for (byte j = 0; j < 4; ++j) { blocks[j][i] = currentPiece.blocks[4 - i - 1][j]; } } oldPiece = currentPiece; memcpy(currentPiece.blocks, blocks, 16); if (checkCollision()) currentPiece = oldPiece; else { drawGhost(ERASE); if (createGhost()) drawGhost(DRAW); } } bool movePieceDown(void) { int rndPiece = 0; memcpy(oldPiece.blocks, currentPiece.blocks, 16); oldPiece.row = currentPiece.row; oldPiece.column = currentPiece.column; currentPiece.row--; //check collision if (checkCollision()) { currentPiece.row = oldPiece.row; drawPiece(DRAW); byte totalRows = 0; for (byte row = 0; row < VERTMAX; row++) { // scan the whole block (it's quick - there's no drawing to do) bool rowFull = 1; for (byte col = 0; col < HORIZ; col++) { // scan across this row - every column if (readBlockArray(col,row) == 0) rowFull = 0; // if we hit any blank spaces, the row's not full } if (rowFull) { totalRows++; for (int i = 800; i>200; i = i - 200) beep(30,i); // happy sound for (byte col = 0; col < HORIZ; col++) writeblockArray(col,row,0); // write zeros across this whole row drawGameScreen(0,HORIZ-1,row,row+1,PARTIAL); // draw the row we're removing (for animation) delay(30); // delay slightly to make the deletion of rows visible for (byte dropCol = 0; dropCol < HORIZ; dropCol++) { // for every column for (byte dropRow = row; dropRow < VERTMAX-1; dropRow ++) writeblockArray(dropCol,dropRow,readBlockArray(dropCol,dropRow+1)); // drop everything down as many as the row's we've cleared } row--; // we need to check this row again as it could now have things in it! } } level += totalRows; switch (totalRows) { case 1: score += 40; break; case 2: score += 100; break; case 3: score += 300; break; case 4: score += 800; } drawGameScreen(0,10, 0,VERTDRAW,FULL); displayScore(score, 0,117,0); loadPiece(nextPiece, STARTY, STARTX); if (checkCollision()) { stopAnimate = true; } else { loadPiece(nextPiece, STARTY, STARTX); drawGhost(ERASE); if (createGhost()) drawGhost(DRAW); } nextPiece = random(1, 8); setNextBlock(nextPiece); } drawGhost(ERASE); if (createGhost()) drawGhost(DRAW); } void movePieceLeft(void) { byte response; oldPiece = currentPiece; currentPiece.column = currentPiece.column - 1; response = checkCollision(); if (checkCollision()) { currentPiece = oldPiece; // back to where it was } else { drawGhost(ERASE); if (createGhost()) drawGhost(DRAW); } } void movePieceRight(void) { oldPiece = currentPiece; currentPiece.column = currentPiece.column + 1; //check collision if (checkCollision()) { currentPiece = oldPiece; // back to where it was } else { drawGhost(ERASE); if (createGhost()) drawGhost(DRAW); } } byte checkCollision(void) { byte pieceRow = 0; byte pieceColumn = 0; for (int c = currentPiece.column; c < currentPiece.column + 4; c++) { for (int r = currentPiece.row; r < currentPiece.row + 4; r++) { if (currentPiece.blocks[pieceColumn][pieceRow]) { if (c < 0) return 2; if (c > 9) return 1; if (r < 0) return 1; if (c >= 0 && r >= 0 && c < HORIZ && r < VERTMAX) { if (readBlockArray(c,r)) { return 1; //is it on landed blocks? } } } pieceRow++; } pieceRow = 0; pieceColumn++; } return 0; } void handleInput(void) { if (digitalRead(0) == LOW && digitalRead(2) == LOW) { if (keyLock == 1) { drawPiece(ERASE); movePieceLeft(); drawPiece(DRAW); drawGameScreen(currentPiece.column, currentPiece.column + 5, currentPiece.row, currentPiece.row+4, PARTIAL); } else if (keyLock == 2) { drawPiece(ERASE); movePieceRight(); drawPiece(DRAW); drawGameScreen(currentPiece.column-1, currentPiece.column + 4, currentPiece.row, currentPiece.row+4,PARTIAL); } else if (keyLock == 3) { drawPiece(ERASE); rotatePiece(); drawPiece(DRAW); drawGameScreen(currentPiece.column, currentPiece.column + 4, currentPiece.row, currentPiece.row+4,PARTIAL); } keyLock = 0; } if (keyLock == 4) { while (digitalRead(0) == HIGH) { drawPiece(ERASE); movePieceDown(); drawPiece(DRAW); drawGameScreen(currentPiece.column, currentPiece.column + 4, currentPiece.row, currentPiece.row+5,PARTIAL); delay(10); if (stopAnimate) return; } keyLock = 0; } delay(30); } void setNextBlock(byte pieceNumber) { memset(nextBlockBuffer, 0, sizeof nextBlockBuffer); //clear buffer pieceNumber--; if (pieceNumber == 0) { for (int k = 2; k < 6; k++) { nextBlockBuffer[k][0] = pgm_read_byte(&miniBlock[pieceNumber][0]); nextBlockBuffer[k][1] = pgm_read_byte(&miniBlock[pieceNumber][0]); } } else { for (int k = 0; k < 3; k++) { nextBlockBuffer[k][0] = pgm_read_byte(&miniBlock[pieceNumber][0]); nextBlockBuffer[k][1] = pgm_read_byte(&miniBlock[pieceNumber][1]); } for (int k = 4; k < 7; k++) { nextBlockBuffer[k][0] = pgm_read_byte(&miniBlock[pieceNumber][2]); nextBlockBuffer[k][1] = pgm_read_byte(&miniBlock[pieceNumber][3]); } } drawGameScreen(0,10,0,VERTDRAW, FULL); } void drawScreenBorder(void) { ssd1306_setpos(0, 0); ssd1306_send_data_start(); ssd1306_send_byte(0xFF); for (byte c = 1; c < 126; c++) { ssd1306_send_byte(B00000001); } ssd1306_send_byte(0xFF); ssd1306_send_data_stop(); for (byte r = 1; r < 7; r++) { ssd1306_setpos(0, r); ssd1306_send_data_start(); ssd1306_send_byte(0xFF); ssd1306_send_data_stop(); ssd1306_setpos(127, r); ssd1306_send_data_start(); ssd1306_send_byte(0xFF); ssd1306_send_data_stop(); } ssd1306_setpos(0, 7); ssd1306_send_data_start(); ssd1306_send_byte(0xFF); for (byte c = 1; c < 126; c++) { ssd1306_send_byte(B10000000); } ssd1306_send_byte(0xFF); ssd1306_send_data_stop(); } void displayScore(int score, int xpos, int y, bool blank) { byte scoreOut[6]; scoreOut[5] = (score % 10); scoreOut[4] = ((score / 10) % 10); scoreOut[3] = ((score / 100) % 10); scoreOut[2] = ((score / 1000) % 10); scoreOut[1] = ((score / 10000) % 10); scoreOut[0] = ((score / 100000) % 10); for (byte x = xpos; x 10) endCol = 10; if(startRow < 0) startRow = 0; if(endRow > VERTDRAW) endRow = VERTDRAW; byte startScreenCol = pgm_read_byte(&startDecode[startCol]); byte endScreenCol = pgm_read_byte(&endDecode[endCol]); for (byte col = startScreenCol; col < endScreenCol; col++) { if (col < 4) reader = col; else if (col < 7) reader = col+1; else reader = col + 2; blockReader = 2 * col; ssd1306_setpos(startRow*6, col); // Start from the end of this column (working up the screen) on the required row ssd1306_send_data_start(); if (startRow == 0) ssd1306_send_byte(B11111111); else { if (col == 0) ssd1306_send_byte(B00000001); else if (col == 7) ssd1306_send_byte(B10000000); else ssd1306_send_byte(B00000000); } for (byte r = startRow; r < endRow; r++ ) { // For each row in the array of tetris blocks for (byte piece = 0; piece < 5; piece ++) { // for each of the 5 filled lines of the block if (col == 0) temp = B00000001; else if (col == 7) temp = B10000000; else temp = 0x00; // if we're on the far left, draw the left wall, on the far right draw the right wall, otherwise its a blank separator between blocks separator = temp; // we'll need this again later! if (readBlockArray(reader,r)) { temp = temp | pgm_read_byte(&blockout[blockReader]); } if (readBlockArray(reader+1,r)) { temp = temp | pgm_read_byte(&blockout[blockReader+1]); } if(ghost) { if (readGhostArray(reader,r) && (piece == 0 || piece == 4)) { temp = temp | pgm_read_byte(&blockout[blockReader]); } else if (readGhostArray(reader,r)) { temp = temp | pgm_read_byte(&ghostout[blockReader]); } if (readGhostArray(reader+1,r) && (piece == 0 || piece == 4)) { temp = temp | pgm_read_byte(&blockout[blockReader+1]); } else if (readGhostArray(reader+1,r)) { temp = temp | pgm_read_byte(&ghostout[blockReader+1]); } } ssd1306_send_byte(temp); } ssd1306_send_byte(separator); // between blocks - same one as we used at the start } if (mode == FULL) if (col > 5) for (byte blockline = 0; blockline < 8; blockline++) ssd1306_send_byte(nextBlockBuffer[blockline][col-6]); ssd1306_send_data_stop(); } } bool createGhost(void) { byte tempRow = currentPiece.row; if (currentPiece.row < 3) return 0; currentPiece.row-=2; while(checkCollision() == 0) currentPiece.row--; memcpy(ghostPiece.blocks, currentPiece.blocks, 16); ghostPiece.row = currentPiece.row+1; ghostPiece.column = currentPiece.column; currentPiece.row = tempRow; if (ghostPiece.row > currentPiece.row - 3) return 0; else return 1; } void loadPiece(byte pieceNumber, byte row, byte column) { byte incr = 0; pieceNumber--; for (byte lxn = 0; lxn < 4; lxn++) { for (byte lxn2 = 0; lxn2 < 4; lxn2++) { if ( ((1 << incr) & pgm_read_word(&blocks[pieceNumber])) >> incr == 1) { currentPiece.blocks[lxn][lxn2] = 1; } else currentPiece.blocks[lxn][lxn2] = 0; incr++; } } currentPiece.row = row; currentPiece.column = column; } void drawPiece(byte action) { for (byte lxn = 0; lxn < 4; lxn++) { for (byte lxn2 = 0; lxn2 < 4; lxn2++) { if (currentPiece.blocks[lxn][lxn2] == 1) { if (action == DRAW) writeblockArray(currentPiece.column + lxn,currentPiece.row + lxn2,1); else if (action == ERASE) writeblockArray(currentPiece.column + lxn,currentPiece.row + lxn2,0); } } } } void drawGhost(byte action) { for (byte lxn = 0; lxn < 4; lxn++) { for (byte lxn2 = 0; lxn2 < 4; lxn2++) { if (ghostPiece.blocks[lxn][lxn2] == 1) { if (action == DRAW) writeGhostArray(ghostPiece.column + lxn,ghostPiece.row + lxn2,1); else if (action == ERASE) {writeGhostArray(ghostPiece.column + lxn,ghostPiece.row + lxn2,0); lastGhostRow = ghostPiece.row; } } } } } void playTetris(void) { stopAnimate = 0; score = 0; keyLock = 0; fillGrid(0, NORMAL); fillGrid(0, GHOST); // Attach the interrupt to read key 2 attachInterrupt(0,playerIncTetris,RISING); loadPiece(random(1, 8), STARTY, STARTX); drawPiece(DRAW); if (createGhost()) drawGhost(DRAW); drawGhost(DRAW); nextPiece = random(1, 8); setNextBlock(nextPiece); // Fill up the screen with random crap if it's in challenge mode! if (challengeMode) { for (byte cl = 0; cl < 100; cl++) { drawPiece(ERASE); movePieceDown(); if (random(1,8) >4) movePieceLeft(); drawPiece(DRAW); } } // Reset the level level = STARTLEVEL; drawGameScreen(0,10,0,VERTDRAW, FULL); displayScore(score, 0,117,0); while (stopAnimate == 0) { drawPiece(ERASE); movePieceDown(); drawPiece(DRAW); drawGameScreen(currentPiece.column, currentPiece.column + 4, currentPiece.row, currentPiece.row+5, PARTIAL); moveTime = millis(); if (level * LEVELFACTOR > DROPDELAY) level = DROPDELAY / LEVELFACTOR; while ((millis() - moveTime) < (DROPDELAY - level* LEVELFACTOR)) { handleInput(); } } ssd1306_fillscreen(0x00); bool newHigh = false; topScore = EEPROM.read(0); topScore = topScore << 8; topScore = topScore | EEPROM.read(1); if (score > topScore) { topScore = score; EEPROM.write(1,score & 0xFF); EEPROM.write(0,(score>>8) & 0xFF); newHigh = true; } drawScreenBorder(); displayScore(score, 1,80,0); displayScore(topScore, 1,40,0); for (int i = 0; i<1000; i = i+ 50){ beep(50,i); } for (byte lx = 0; lx<4;lx++) { displayScore(score, 1,80,1); if (newHigh) displayScore(topScore, 1,40,1); delay(200); displayScore(score, 1,80,0); if (newHigh) displayScore(topScore, 1,40,0); delay(200); } } ================================================ FILE: Tetris_Multi_Button/font8x8AJ.h ================================================ /* * This font is from http://arduino-er.blogspot.co.uk/2014/08/port-ascii-font-to-arduino-88-led-matrix.html * * Source code adapted by @andyhighnumber from that available at: https://bitbucket.org/tinusaur/ssd1306xled * * Hacked to remove unused letters and thus minimise code space for the Attiny85 * */ // ---------------------------------------------------------------------------- #include // ---------------------------------------------------------------------------- static const byte font[][8] PROGMEM = { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, // U+0020 (space) { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00}, // U+002D (-) { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00}, // U+002E (.) { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00}, // U+002F (/) { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00}, // U+0030 (0) { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00}, // U+0031 (1) { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00}, // U+0032 (2) { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00}, // U+0033 (3) { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00}, // U+0034 (4) { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00}, // U+0035 (5) { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00}, // U+0036 (6) { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00}, // U+0037 (7) { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00}, // U+0038 (8) { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00}, // U+0039 (9) { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00}, // U+0041 (A) { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00}, // U+0042 (B) { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00}, // U+0043 (C) { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00}, // U+0044 (D) { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00}, // U+0045 (E) { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00}, // U+0046 (F) { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00}, // U+0047 (G) { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00}, // U+0048 (H) { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0049 (I) { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00}, // U+004A (J) { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00}, // U+004B (K) { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00}, // U+004C (L) { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00}, // U+004D (M) { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00}, // U+004E (N) { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00}, // U+004F (O) { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00}, // U+0050 (P) { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00}, // U+0051 (Q) { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00}, // U+0052 (R) { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00}, // U+0053 (S) { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0054 (T) { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00}, // U+0055 (U) { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0056 (V) { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00}, // U+0057 (W) { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00}, // U+0058 (X) { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00}, // U+0059 (Y) { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00}, // U+005A (Z) { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00}, // U+0061 (a) //{ 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00}, // U+0062 (b) { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n) // n in place of b { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00}, // U+0063 (c) { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00}, // U+0064 (d) { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00}, // U+0065 (e) //{ 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00}, // U+0066 (f) { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r) // r in place of f //{ 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0067 (g) { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y) // y in place of g //{ 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00}, // U+0068 (h) { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t) // t in place of h { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+0069 (i) { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E}, // U+006A (j) { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00}, // U+006B (k) /* { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00}, // U+006C (l) { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00}, // U+006D (m) { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00}, // U+006E (n) { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00}, // U+006F (o) { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F}, // U+0070 (p) { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78}, // U+0071 (q) { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00}, // U+0072 (r) { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00}, // U+0073 (s) { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00}, // U+0074 (t) { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00}, // U+0075 (u) { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00}, // U+0076 (v) { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00}, // U+0077 (w) { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00}, // U+0078 (x) { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F}, // U+0079 (y) { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00}, // U+007A (z) */ }; // ---------------------------------------------------------------------------- ================================================ FILE: UFO_Breakout_Arduino/UFO_Breakout_Arduino.ino ================================================ /* 2015 / 2016 * UFO and Breakout games by Ilya Titov. Find building instructions on http://webboggles.com/ * The code that does not fall under the licenses of sources listed below can be used non commercially with attribution. * * Modified by andy jackson to allow two games to fit into a single ATTiny85 at the same time. * Additional features: * - To play UFO - press and release left button * - To play Breakout - with the right button held, press and release left button * - To turn sound on and off - press and HOLD left button * - To reset high scores to zero - whilst HOLDING the right button, press and HOLD the left button * * 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 #include "font6x8AJ.h" #include #include // 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 PORTB3 // SCL, Pin 3 on SSD1306 Board #define SSD1306_SDA PORTB4 // SDA, Pin 4 on SSD1306 Board #define SSD1306_SA 0x78 // Slave address // Function prototypes for UFO void beep(int,int); void playUFO(void); // Function prototypes for Breakout void collision(void); void drawPlatform(void); void sendBlock(boolean); void playBreakout(void); // Other function prototypes - for both 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[]); // for UFO int topScoreU = 0; boolean isBreakout = 0; byte maxObstacles = 1; // this defines the max number of in game obstacles byte obstacleStep = 2; // pixel step of obstacles per frame int obstacle[9] = {-50,-50,-50,-50,-50,-50,-50,-50,-50}; // x offset of the obstacle default position, out of view byte gapOffset[9] = {0,0,0,0,0,0,0,0,0}; // y offset of the fly-through gap int gapSize[9]; // y height of the gap byte maxGap = 60; // max height of the gap int stepsSinceLastObstacle = 0; // so obstacles are not too close byte gapBlock[9] = {0,0,0,0,0,0,0,0,0}; // if the fly-through gap is closed byte blockChance = 0; // this higher value decreases the likelihood of gap being closed boolean fire = 0; // set to 1 when the fire interrupt is triggered byte fireCount = 0; // the shot is persistent for several frames byte playerOffset = 0; // y offset of the top of the player byte flames = 0; // this is set to 1 when the move up interrupt is triggered byte flameMask[2]={B00111111,B11111111}; // this is used to only show the flame part of the icon when moving up void doDrawLS(long, byte); void doDrawRS(long, byte); // For Breakout int topScoreB = 0; volatile byte player = 0; //0 to 128-platformWidth - this is the position of the bounce platform byte platformWidth = 16; byte ballx = 62; // coordinate of the ball byte bally = 50; // coordinate of the ball int vdir = -1; // vertical direction and step distance int hdir = -1; // horizontal direction and step distance long lastFrame = 0; // time since the screen was updated last boolean row[3][16]; // on-off array of blocks // For both 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 top = 0; // Interrupt handlers ISR(PCINT0_vect){ // PB0 pin button interrupt if ( (player >0) && (isBreakout) ) {player--;} return; } void playerIncUFO(){ // PB2 pin button interrupt fire = 1; fireCount = 5; // number of frames the shot will persist } void playerIncBreakOut(){ // PB2 pin button interrupt if (player <128-platformWidth){player++;} } // 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() { isBreakout = 0; if (digitalRead(2) == HIGH) isBreakout = 1; interrupts(); ssd1306_init(); ssd1306_fillscreen(0x00); for (byte s = 0; s<30; s++){ // generate stars ssd1306_setpos((uint8_t)(random(0,127)),(uint8_t)(random(0,7)) ); ssd1306_send_data_start(); ssd1306_send_byte(B10000000>>random(0,7)); ssd1306_send_data_stop(); //beep(10+s,600); } if (isBreakout) { ssd1306_char_f6x8(16, 2, "B R E A K O U T"); } else { ssd1306_char_f6x8(14, 2, "U F O C H A O S"); } // 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(3, 4, "mods bh andh jackson"); // see comments above ! ssd1306_char_f6x8(22, 6, "game design bh"); // see comments above ! ssd1306_char_f6x8(22, 7, "/ebboggles.com"); // see comments above ! 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); EEPROM.write(2,0); EEPROM.write(3,0); ssd1306_char_f6x8(8, 1, "-HIGH SCORE RESET-"); } else if (mute == 0) { mute = 1; ssd1306_char_f6x8(32, 1, "-- MUTE --"); } else { mute = 0; ssd1306_char_f6x8(23, 1, "-- SOUND ON --"); } break; } if (sChange == 1) break; } while(digitalRead(0) == HIGH); if (sChange == 0) { delay(2600); noInterrupts(); ssd1306_init(); ssd1306_fillscreen(0x00); stopAnimate = 0; score = 0; if (isBreakout) { playBreakout(); top=topScoreB;} else { playUFO(); top=topScoreU;} 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, top); } for (int i = 0; i<1000; i = i+ 50){ beep(50,i); } delay(2000); 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,top); for (int i = 700; i>200; i = i - 50){ beep(30,i); } newHigh = 0; delay(2700); } } system_sleep(); noInterrupts(); } 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) { 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-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() { 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; i20){fireCount--;} for (int boo = 0; boo<3;boo++) { if (digitalRead(0)==1){ if (playerOffset >0){ playerOffset--; flames = 1; // move player up for (int i = 0; i<2; i++){ beep(1,random(0,i*2)); } } } } stepsSinceLastObstacle += obstacleStep; for (byte i = 0; i= 0 && obstacle[i] <= 128){ obstacle[i] -= obstacleStep; if (gapBlock[i]>0 && obstacle[i] < 36 && playerOffset>gapOffset[i] && playerOffset+5 0){// gapBlock[i] = 0; score += 5; for (byte cp = 400; cp>0; cp--){ beep(1,cp); } } } if (obstacle[i]<=4 && stepsSinceLastObstacle>=random(30,100)){ // generate new obstacles obstacle[i] = 123; gapSize[i] = random(25,maxGap); gapOffset[i] = random(0,64-gapSize[i]); if (random(0,blockChance)==0){gapBlock[i] = 1;}else {gapBlock[i] = 0;} stepsSinceLastObstacle = 0; score+=1; } } if ( (fireCount == 0) && (playerOffset < 56) ){playerOffset++;} // player gravit // update what's on the screen // erase player for (byte r=0; r<8; r++){ if (r= playerOffset/8+1) { ssd1306_setpos(0,r); ssd1306_send_data_start(); sendBlock(0); sendBlock(0); ssd1306_send_data_stop(); } } //erase fire ssd1306_setpos(16,playerOffset/8); ssd1306_send_data_start(); if (fireCount == 0){ for (byte f = 0; f<=30; f++){ ssd1306_send_byte(B00000000); } } ssd1306_send_data_stop(); // Send Obstacle for (byte i = 0; i= -5 && obstacle[i] <= 128){ // only deal with visible obstacles if (obstacle[i] > 8 && obstacle[i] <16){ // look for collision if obstacle is near the player if (playerOffset < gapOffset[i] || playerOffset+5 > gapOffset[i]+gapSize[i] || gapBlock[i] != 0){ // collision - stop looping stopAnimate = 1; } } for (byte row = 0; row <8; row++){ ssd1306_setpos(obstacle[i],row); ssd1306_send_data_start(); if (obstacle[i]>0&&obstacle[i] < 128){ if ((row+1)*8 - gapOffset[i] <= 8){ // generate obstacle : top and transition byte temp = B11111111>>((row+1)*8 - gapOffset[i]); byte tempB = B00000000; if (gapBlock[i]>0){tempB=B10101010;} ssd1306_send_byte(temp); ssd1306_send_byte(temp|tempB>>1); ssd1306_send_byte(temp|tempB); ssd1306_send_byte(temp); }else if (row*8>=gapOffset[i] && (row+1)*8<=gapOffset[i]+gapSize[i]){ // middle gap byte tempB = B00000000; if (gapBlock[i]>0){tempB=B10101010;} ssd1306_send_byte(B00000000); ssd1306_send_byte(B00000000|tempB>>1); ssd1306_send_byte(B00000000|tempB); ssd1306_send_byte(B00000000); }else if ((gapOffset[i] +gapSize[i]) >= row*8 && (gapOffset[i] +gapSize[i]) <= (row+1)*8){ // bottom transition //}else if ((gapOffset[i] +gapSize[i]) >= row*8 && (gapOffset[i] +gapSize[i]) <= (row+1)*8){ // bottom transition //byte temp = B11111111<<((gapOffset[i] + gapSize[i])%8); byte temp = B11111111<<((gapOffset[i] + gapSize[i])%8); byte tempB = B00000000; if (gapBlock[i]>0){tempB=B10101010;} ssd1306_send_byte(temp); ssd1306_send_byte(temp|tempB>>1); ssd1306_send_byte(temp|tempB); ssd1306_send_byte(temp); }else { // fill rest of obstacle ssd1306_send_byte(B11111111); ssd1306_send_byte(B11111111); ssd1306_send_byte(B11111111); ssd1306_send_byte(B11111111); } ssd1306_send_byte(B00000000); ssd1306_send_byte(B00000000); ssd1306_send_data_stop(); } } } } if (playerOffset%8!=0){ ssd1306_setpos(8,playerOffset/8); ssd1306_send_data_start(); doDrawLS(0,playerOffset%8); ssd1306_send_data_stop(); ssd1306_setpos(8,playerOffset/8+1); ssd1306_send_data_start(); doDrawRS(0,8-playerOffset%8); ssd1306_send_data_stop(); } else { ssd1306_setpos(8,playerOffset/8); ssd1306_send_data_start(); doDrawLS(0,0); ssd1306_send_data_stop(); } // Display score in the screen corner doNumber(92, 0, score); flames = 0; interrupts(); } // while stopanimate == 0 topScoreU = EEPROM.read(0); topScoreU = topScoreU << 8; topScoreU = topScoreU | EEPROM.read(1); if (score > topScoreU) { topScoreU = score; EEPROM.write(1,score & 0xFF); EEPROM.write(0,(score>>8) & 0xFF); newHigh = 1; } } // Drawing routine for the player and fire - with right-shifts void doDrawRS(long P1, byte P2) { ssd1306_send_byte((B00001100&flameMask[flames] | P1)>>P2); ssd1306_send_byte((B01011110&flameMask[flames] | P1)>>P2); ssd1306_send_byte((B10010111&flameMask[flames] | P1)>>P2); ssd1306_send_byte((B01010011&flameMask[flames] | P1)>>P2); ssd1306_send_byte((B01010011&flameMask[flames] | P1)>>P2); ssd1306_send_byte((B10010111&flameMask[flames] | P1)>>P2); ssd1306_send_byte((B01011110&flameMask[flames] | P1)>>P2); ssd1306_send_byte((B00001100&flameMask[flames] | P1)>>P2); if (fireCount >0) { for (byte f = 0; f<=24; f++){ ssd1306_send_byte(B00000100>>P2); } ssd1306_send_byte(B00010101>>P2); //ssd1306_send_byte(B00001010>>P2); ssd1306_send_byte(B00010101>>P2); if (fire==1){beep(50,100);} fire = 0; } } // Drawing routine for the player and fire - with left-shifts void doDrawLS(long P1, byte P2) { ssd1306_send_byte((B00001100&flameMask[flames] | P1)<0) { for (byte f = 0; f<=24; f++){ ssd1306_send_byte(B00000100<0){player--;} if (player >0){player--;} if (player >0){player--;}} // bounce off the sides of the screen if ((bally+vdir<54&&vdir==1)||(bally-vdir>1&&vdir==-1)){bally+=vdir;}else {vdir = vdir*-1;} if ((ballx+hdir<127&&hdir==1)||(ballx-hdir>1&&hdir==-1)){ballx+=hdir;}else {hdir = hdir*-1;} // frame actions if (lastFrame+1010&&bally+vdir>=54&&(ballxplayer+platformWidth)){ // game over if the ball misses the platform stopAnimate = 1; break; }else if (ballx10&&bally+vdir>=54){ // if the ball hits left of the platform bounce left hdir=-1; beep(20,600); }else if (ballx>player+platformWidth/2&&bally>10&&bally+vdir>=54){ // if the ball hits right of the platform bounce right hdir=1; beep(20,600); }else if (bally+vdir>=54){ hdir=1; beep(20,600); } collisionCheck: // go back to here if a collision was detected to prevent flying through a rigid if (floor((bally+vdir)/8)==2){ if (row[2][ballx/8]==1){row[2][ballx/8]=0; score++; collision(); goto collisionCheck; // check collision for the new direction to prevent flying through a rigid } }else if (floor((bally+vdir)/8)==1){ if (row[1][ballx/8]==1){row[1][ballx/8]=0; score++; collision(); goto collisionCheck; } }else if (floor((bally+vdir)/8)==0){ if (row[0][ballx/8]==1){row[0][ballx/8]=0; score++; collision(); goto collisionCheck; } } // reset blocks if all have been hit if (score%48==0){ for (byte i =0; i<16;i++){ row[0][i]=1; row[1][i]=1; row[2][i]=1; } } } // update whats on the screen noInterrupts(); // blocks for (int inc = 0; inc <3; inc ++) { ssd1306_setpos(0,inc); ssd1306_send_data_start(); for (int bl = 0; bl <16; bl++){ if(row[inc][bl]==1){ sendBlock(1); }else { sendBlock(0); } } ssd1306_send_data_stop(); } // clear area below the blocks for (int kl = 3;kl <8;kl++) { ssd1306_setpos(0,kl); ssd1306_send_data_start(); for (byte i =0; i<128; i++){ ssd1306_send_byte(B00000000); } ssd1306_send_data_stop(); } // draw ball ssd1306_setpos(ballx,bally/8); uint8_t temp = B00000001; ssd1306_send_data_start(); temp = temp << bally%8+1; ssd1306_send_byte(temp); ssd1306_send_data_stop(); drawPlatform(); interrupts(); } } interrupts(); topScoreB = EEPROM.read(2); topScoreB = topScoreB << 8; topScoreB = topScoreB | EEPROM.read(3); if (score > topScoreB) { topScoreB = score; EEPROM.write(3,score & 0xFF); EEPROM.write(2,(score>>8) & 0xFF); newHigh = 1; } } void collision(){ // the collsision check is actually done befor this is called, this code works out where the ball will bounce if ((bally+vdir)%8==7&&(ballx+hdir)%8==7){ // bottom right corner if (vdir==1){hdir=1;}else if(vdir==-1&&hdir==1){vdir=1;}else {hdir=1;vdir=1;} }else if ((bally+vdir)%8==7&&(ballx+hdir)%8==0){ // bottom left corner if (vdir==1){hdir=-1;}else if(vdir==-1&&hdir==-1){vdir=1;}else {hdir=-1;vdir=1;} }else if ((bally+vdir)%8==0&&(ballx+hdir)%8==0){ // top left corner if (vdir==-1){hdir=-1;}else if(vdir==1&&hdir==-1){vdir=-1;}else {hdir=-1;vdir=-1;} }else if ((bally+vdir)%8==0&&(ballx+hdir)%8==7){ // top right corner if (vdir==-1){hdir=1;}else if(vdir==1&&hdir==1){vdir=-1;}else {hdir=1;vdir=-1;} }else if ((bally+vdir)%8==7){ // bottom side vdir = 1; }else if ((bally+vdir)%8==0){ // top side vdir = -1; }else if ((ballx+hdir)%8==7){ // right side hdir = 1; }else if ((ballx+hdir)%8==0){ // left side hdir = -1; }else { hdir = hdir*-1; vdir = vdir*-1; } beep(30,300); } void drawPlatform(){ noInterrupts(); ssd1306_setpos(player,7); ssd1306_send_data_start(); for (byte pw = 1; pw // ---------------------------------------------------------------------------- /* 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: UFO_Stacker_Attiny/UFO_Stacker_Attiny.ino ================================================ /* 2015 / 2016 / 2017 * Stacker game by Andy Jackson @andyhighnumber * UFO game by Ilya Titov. Find building instructions on http://webboggles.com/ * * The code that does not fall under the licenses of sources listed below can be used non commercially with attribution. * * Modified by andy jackson to allow two games to fit into a single ATTiny85 at the same time. * Additional features: * - To play Stacker - press and release left button * - To play UFO - with the right button held, press and release left button * - To turn sound on and off - press and HOLD left button * - To reset high scores to zero - whilst HOLDING the right button, press and HOLD the left button * * 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 #include "font6x8AJ.h" #include #include // 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 3 on SSD1306 Board #define SSD1306_SDA PORTB3 // SDA, Pin 4 on SSD1306 Board #define SSD1306_SA 0x78 // Slave address // Function prototypes for UFO void beep(int,int); void playUFO(void); // Function prototypes for Stacker void resetBlocks(void); void drawRow(int thisRow, int location); void sendBlock(boolean); void playStacker(void); void beep(int,int); void levelUp(int); // Other function prototypes - for both 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[]); // for UFO int obstacle[9] = {-50,-50,-50,-50,-50,-50,-50,-50,-50}; // x offset of the obstacle default position, out of view byte gapOffset[9] = {0,0,0,0,0,0,0,0,0}; // y offset of the fly-through gap int gapSize[9]; // y height of the gap byte maxGap = 60; // max height of the gap int stepsSinceLastObstacle = 0; // so obstacles are not too close byte gapBlock[9] = {0,0,0,0,0,0,0,0,0}; // if the fly-through gap is closed int topScoreU = 0; boolean isStacker = 0; byte maxObstacles = 1; // this defines the max number of in game obstacles byte obstacleStep = 2; // pixel step of obstacles per frame byte blockChance = 0; // this higher value decreases the likelihood of gap being closed byte fireCount = 0; // the shot is persistent for several frames byte playerOffset = 0; // y offset of the top of the player byte flames = 0; // this is set to 1 when the move up interrupt is triggered byte flameMask[2]={B00111111,B11111111}; // this is used to only show the flame part of the icon when moving up void doDrawLS(long, byte); void doDrawRS(long, byte); // For Stacker boolean perfect = 1; int runCounter = 0; boolean fireLock = 0; boolean fireCounterActive = 0; int fireCounter = 0; int level = 1; // Game level - incremented every time you clear the screen boolean blockDirection = 1; // current direction of travel for blocks - 1 is right 0 is left int stackRow = 0; // which row are we considering int movecounter = 0; // timer for movement int matchCount = 6; boolean row[2][16]; // on-off array of blocks - building up from the bottom boolean fire = 0; int topScoreB = 0; volatile int player = 0; //0 to 128-platformWidth - this is the position of the player // For both 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 top = 0; // Interrupt handlers ISR(PCINT0_vect){ // PB0 pin button interrupt } void playerIncUFO(){ // PB2 pin button interrupt fire = 1; fireCount = 5; // number of frames the shot will persist } void playerIncStacker(){ // 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() { isStacker = 1; if (digitalRead(2) == HIGH) isStacker = 0; //interrupts(); ssd1306_init(); ssd1306_fillscreen(0x00); if (isStacker) { for (int j = 0;j<16;j++) { row[0][j] = 0; } for (int i= 14;i<15;i++) { row[0][i] = 1; } drawRow(0,2); for (int i= 13;i<15;i++) { row[0][i] = 1; } drawRow(0,3); for (int i= 13;i<16;i++) { row[0][i] = 1; } drawRow(0,4); for (int i= 13;i<16;i++) { row[0][i] = 1; } drawRow(0,5); for (int i= 12;i<16;i++) { row[0][i] = 1; } drawRow(0,6); for (int i= 12;i<16;i++) { row[0][i] = 1; } drawRow(0,7); ssd1306_char_f6x8(0, 2, "S T A C K E R"); ssd1306_char_f6x8(0, 4, "andh jackson"); // see comments above ! ssd1306_char_f6x8(0, 6, "inspired bh"); // see comments above ! ssd1306_char_f6x8(0, 7, "/ebboggles.com"); // see comments above ! } else { for (byte s = 0; s<30; s++){ // generate stars ssd1306_setpos((uint8_t)(random(0,127)),(uint8_t)(random(0,7)) ); ssd1306_send_data_start(); ssd1306_send_byte(B10000000>>random(0,7)); ssd1306_send_data_stop(); } ssd1306_char_f6x8(14, 2, "U F O C H A O S"); // 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(3, 4, "mods bh andh jackson"); // see comments above ! ssd1306_char_f6x8(17, 6, "original game bh"); // see comments above ! ssd1306_char_f6x8(22, 7, "/ebboggles.com"); // see comments above ! } 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); EEPROM.write(2,0); EEPROM.write(3,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(23, 0, "-- SOUND ON --"); } break; } if (sChange == 1) break; } while(digitalRead(0) == HIGH); if (sChange == 0) { delay(2600); //noInterrupts(); ssd1306_init(); ssd1306_fillscreen(0x00); stopAnimate = 0; score = 0; if (isStacker) { playStacker(); top=topScoreB;} else { playUFO(); top=topScoreU;} 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, top); } for (int i = 0; i<1000; i = i+ 50){ beep(50,i); } delay(2000); 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,top); for (int i = 700; i>200; i = i - 50){ beep(30,i); } newHigh = 0; delay(2700); } } system_sleep(); //noInterrupts(); } 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) { 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-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() { 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; i20){fireCount--;} for (int boo = 0; boo<3;boo++) { if (digitalRead(0)==1){ if (playerOffset >0){ playerOffset--; flames = 1; // move player up for (int i = 0; i<2; i++){ beep(1,random(0,i*2)); } } } } stepsSinceLastObstacle += obstacleStep; for (byte i = 0; i= 0 && obstacle[i] <= 128){ obstacle[i] -= obstacleStep; if (gapBlock[i]>0 && obstacle[i] < 36 && playerOffset>gapOffset[i] && playerOffset+5 0){// gapBlock[i] = 0; score += 5; for (byte cp = 400; cp>0; cp--){ beep(1,cp); } } } if (obstacle[i]<=4 && stepsSinceLastObstacle>=random(30,100)){ // generate new obstacles obstacle[i] = 123; gapSize[i] = random(25,maxGap); gapOffset[i] = random(0,64-gapSize[i]); if (random(0,blockChance)==0){gapBlock[i] = 1;}else {gapBlock[i] = 0;} stepsSinceLastObstacle = 0; score+=1; } } if ( (fireCount == 0) && (playerOffset < 56) ){playerOffset++;} // player gravit // update what's on the screen // erase player for (byte r=0; r<8; r++){ if (r= playerOffset/8+1) { ssd1306_setpos(0,r); ssd1306_send_data_start(); sendBlock(0); sendBlock(0); ssd1306_send_data_stop(); } } //erase fire ssd1306_setpos(16,playerOffset/8); ssd1306_send_data_start(); if (fireCount == 0){ for (byte f = 0; f<=30; f++){ ssd1306_send_byte(B00000000); } } ssd1306_send_data_stop(); // Send Obstacle for (byte i = 0; i= -5 && obstacle[i] <= 128){ // only deal with visible obstacles if (obstacle[i] > 8 && obstacle[i] <16){ // look for collision if obstacle is near the player if (playerOffset < gapOffset[i] || playerOffset+5 > gapOffset[i]+gapSize[i] || gapBlock[i] != 0){ // collision - stop looping stopAnimate = 1; } } for (byte row = 0; row <8; row++){ ssd1306_setpos(obstacle[i],row); ssd1306_send_data_start(); if (obstacle[i]>0&&obstacle[i] < 128){ if ((row+1)*8 - gapOffset[i] <= 8){ // generate obstacle : top and transition byte temp = B11111111>>((row+1)*8 - gapOffset[i]); byte tempB = B00000000; if (gapBlock[i]>0){tempB=B10101010;} ssd1306_send_byte(temp); ssd1306_send_byte(temp|tempB>>1); ssd1306_send_byte(temp|tempB); ssd1306_send_byte(temp); }else if (row*8>=gapOffset[i] && (row+1)*8<=gapOffset[i]+gapSize[i]){ // middle gap byte tempB = B00000000; if (gapBlock[i]>0){tempB=B10101010;} ssd1306_send_byte(B00000000); ssd1306_send_byte(B00000000|tempB>>1); ssd1306_send_byte(B00000000|tempB); ssd1306_send_byte(B00000000); }else if ((gapOffset[i] +gapSize[i]) >= row*8 && (gapOffset[i] +gapSize[i]) <= (row+1)*8){ // bottom transition //}else if ((gapOffset[i] +gapSize[i]) >= row*8 && (gapOffset[i] +gapSize[i]) <= (row+1)*8){ // bottom transition //byte temp = B11111111<<((gapOffset[i] + gapSize[i])%8); byte temp = B11111111<<((gapOffset[i] + gapSize[i])%8); byte tempB = B00000000; if (gapBlock[i]>0){tempB=B10101010;} ssd1306_send_byte(temp); ssd1306_send_byte(temp|tempB>>1); ssd1306_send_byte(temp|tempB); ssd1306_send_byte(temp); }else { // fill rest of obstacle ssd1306_send_byte(B11111111); ssd1306_send_byte(B11111111); ssd1306_send_byte(B11111111); ssd1306_send_byte(B11111111); } ssd1306_send_byte(B00000000); ssd1306_send_byte(B00000000); ssd1306_send_data_stop(); } } } } if (playerOffset%8!=0){ ssd1306_setpos(8,playerOffset/8); ssd1306_send_data_start(); doDrawLS(0,playerOffset%8); ssd1306_send_data_stop(); ssd1306_setpos(8,playerOffset/8+1); ssd1306_send_data_start(); doDrawRS(0,8-playerOffset%8); ssd1306_send_data_stop(); } else { ssd1306_setpos(8,playerOffset/8); ssd1306_send_data_start(); doDrawLS(0,0); ssd1306_send_data_stop(); } // Display score in the screen corner doNumber(92, 0, score); flames = 0; //interrupts(); } // while stopanimate == 0 topScoreU = EEPROM.read(0); topScoreU = topScoreU << 8; topScoreU = topScoreU | EEPROM.read(1); if (score > topScoreU) { topScoreU = score; EEPROM.write(1,score & 0xFF); EEPROM.write(0,(score>>8) & 0xFF); newHigh = 1; } } // Drawing routine for the player and fire - with right-shifts void doDrawRS(long P1, byte P2) { ssd1306_send_byte((B00001100&flameMask[flames] | P1)>>P2); ssd1306_send_byte((B01011110&flameMask[flames] | P1)>>P2); ssd1306_send_byte((B10010111&flameMask[flames] | P1)>>P2); ssd1306_send_byte((B01010011&flameMask[flames] | P1)>>P2); ssd1306_send_byte((B01010011&flameMask[flames] | P1)>>P2); ssd1306_send_byte((B10010111&flameMask[flames] | P1)>>P2); ssd1306_send_byte((B01011110&flameMask[flames] | P1)>>P2); ssd1306_send_byte((B00001100&flameMask[flames] | P1)>>P2); if (fireCount >0) { for (byte f = 0; f<=24; f++){ ssd1306_send_byte(B00000100>>P2); } ssd1306_send_byte(B00010101>>P2); //ssd1306_send_byte(B00001010>>P2); ssd1306_send_byte(B00010101>>P2); if (fire==1){beep(50,100);} fire = 0; } } // Drawing routine for the player and fire - with left-shifts void doDrawLS(long P1, byte P2) { ssd1306_send_byte((B00001100&flameMask[flames] | P1)<0) { for (byte f = 0; f<=24; f++){ ssd1306_send_byte(B00000100<= 10) && fireCounterActive ) { fireCounterActive = 0; fireLock = 0; fire = 0; } if(digitalRead(2)==1) { while(digitalRead(2)==1); fire = 1; } if(digitalRead(0)==1){ while(digitalRead(0)==1); fire = 1; } if(analogRead(0) < 940) { while(analogRead(0) < 940); fire = 1; } drawRow(0,stackRow); // Fire / stop the blocks! if ( (fireLock == 0) && (fire == 1) ) { // fire has been pressed - do stuff - check for alignment, update the array and decrement active row int matchTarget = matchCount; runCounter = 0; matchCount = 0; for (int i = 0; i<16;i++) { if (row[1][i] == 1) { if (row[0][i] == 1) { matchCount++; row[1][i] = 1; } else { row[1][i] = 0; } } } if (matchCount < matchTarget) perfect = 0; // draw stack row - now fixed drawRow(1,stackRow); stackRow--; if (matchCount>0 ) { score+= (7-stackRow); beep(50,300); } fireLock = 1; // lock fire until the button is lifted and the timeout is done fire = 0; fireCounter = 0; fireCounterActive = 1; // fill the next row (unless we're already at the top! - arduino hates attempts to access arrays with negative indices if (stackRow >=0) { for (int i=0;i= speedNow) { movecounter = 0; if(blockDirection) { // Moving right if (row[0][15] == 1) { // we've hit the end runCounter++; if (runCounter > 20) { // runCounter kills the game if no activity for 20 cycles stopAnimate = 1; // this is to prevent the battery being drained if the game is accidentally started break; } blockDirection = 0; } else { for(int i=15;i>0;i--) { // shuffle right row[0][i] = row[0][i-1]; } row[0][0] = 0; } } else { // Moving left if (row[0][0] == 1) { // we've hit the end blockDirection = 1; } else { for(int i=0;i<15;i++) { // shuffle left row[0][i] = row[0][i+1]; } row[0][15] = 0; } } } if(matchCount == 0) { // deal with failure here stopAnimate = 1; break; } // Level up if (stackRow < 0) { if (level <6) { matchCount = 6; } else if (level < 9) { matchCount = 5; } else if (level < 12) { matchCount = 4; } else if (level < 15) { matchCount = 3; } else { matchCount = 2; } fireCounter = 0; fireLock = 0; fireCounterActive = 0; movecounter=0; stackRow = 6; level++; levelUp(level); blockDirection = 1; perfect = 1; resetBlocks(); fire =0; // draw base row drawRow(1,7); for (int i = 700; i>100; i = i - 100){ beep(30,i); } } } } die: topScoreB = EEPROM.read(2); topScoreB = topScoreB << 8; topScoreB = topScoreB | EEPROM.read(3); if (score > topScoreB) { topScoreB = score; EEPROM.write(3,score & 0xFF); EEPROM.write(2,(score>>8) & 0xFF); newHigh = 1; } } void levelUp(int number) { ssd1306_fillscreen(0x00); ssd1306_char_f6x8(16, 1, "--------------"); ssd1306_char_f6x8(16, 2, " L E V E L "); ssd1306_char_f6x8(16, 3, "--------------"); if (perfect) { ssd1306_char_f6x8(25, 5, "PERFECT LEVEL"); ssd1306_char_f6x8(25, 7, " 100 BONUS"); score += 100; } doNumber(85,2,number); for (int i = 800; i>200; i = i - 200){ beep(30,i); } delay(1500); ssd1306_fillscreen(0x00); } void resetBlocks(void) { for (byte j = 0;j<16;j++) { row[0][j] = 0; } for (byte j = 0;j<16;j++) { row[1][j] = 0; } for (int i=6;i<6+matchCount;i++) { row[1][i] = 1; } for (int i=0;i1) return; ssd1306_setpos(0,location); ssd1306_send_data_start(); for(int j=0;j<16;j++) { if (row[thisRow][j] == 1) { sendBlock(1); } else { sendBlock(0); } } ssd1306_send_data_stop(); } void sendBlock(boolean fill){ if (fill==1){ ssd1306_send_byte(B00000000); ssd1306_send_byte(B01111110); ssd1306_send_byte(B01111110); ssd1306_send_byte(B01111110); ssd1306_send_byte(B01111110); ssd1306_send_byte(B01111110); ssd1306_send_byte(B01111110); ssd1306_send_byte(B00000000); }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); } } ================================================ FILE: UFO_Stacker_Attiny/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 // ---------------------------------------------------------------------------- /* 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, 0x7F, 0x08, 0x04, 0x04, 0x78, // h in place of 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: WrenRollercoasterAttinyArcade/WrenRollercoasterAttinyArcade.ino ================================================ /* 2015 / 2016 /2017 WREN ROLLERCOASTER game by Andy Jackson - Twitter @andyhighnumber Inspired by http://webboggles.com/ and includes some code from the #AttinyArcade games on that site Also inspired by TinyWings(R) game for the iOS platform - if you like this (at all) you'll love that! 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 - Makes the bird stop flying (so it sinks - enabling slides in the valleys) RIGHT BUTTON - Makes the bird flap (slightly) harder Also, from standby.... Press and hold left button to toggle sound/mute Press and hold both buttons together to reset high score 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 which you'll find in the same place you found this .ino file 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 #include #include "font6x8AJ.h" #include #include // 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 sendBlock(int); void playBird(void); void beep(int, int); byte doDrawLS(byte); byte doDrawRS(byte); byte doDrawLSP(byte, byte); byte doDrawRSP(byte, byte); void drawBird(byte startRow, byte endRow); void drawLandscape(byte startRow, byte endRow); 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_bit(void); 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[]); void drawPlayer(byte location); int playerOffset = 0; // y offset of the top of the player int score = 0; int top = 0; boolean newHigh = 0; boolean stopAnimate = 0; // this is set to 1 when a collision is detected boolean mute = 0; byte landscape[128]; byte i; int totaldistance = 0; byte interscore = 0; float incr; float start = 0.0; float si = 0.1; int gostep = 5; float height = 25.0; byte lastPos = 0; int boost = 0; boolean onit = 0; int speedBoost = 0; boolean doneUpdate = 0; // Interrupt handlers ISR(PCINT0_vect) { // PB0 pin button interrupt } void playerIncBird() { // 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(8, 1, " --------------"); ssd1306_char_f6x8(8, 2, " W R E N "); ssd1306_char_f6x8(8, 4, " ROLLERCOASTER"); ssd1306_char_f6x8(8, 5, " --------------"); ssd1306_char_f6x8(8, 7, " 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; 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(23, 0, "-- SOUND ON --"); } break; } if (sChange == 1) break; } while(digitalRead(0) == HIGH); if (sChange == 0) { delay(400); for (int k = 2; k>=0;k--){ for (playerOffset = 55; playerOffset>0+(k*10);playerOffset--) { drawBird(0,8); beep(2,200+playerOffset); delay(2); } for (playerOffset = 0+(k*10); playerOffset<55;playerOffset++) { drawBird(0,8); beep(2,200+playerOffset); delay(2); } } delay(600); ssd1306_init(); ssd1306_fillscreen(0x00); stopAnimate = 0; playBird(); 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); for (int i = 700; i>200; i = i - 50){ beep(30,i); }; if (!newHigh) { ssd1306_char_f6x8(21, 7, "HIGH SCORE:"); doNumber(88, 7, top); } delay(2000); 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,top); for (int i = 700; i>200; i = i - 50){ beep(30,i); } newHigh = 0; delay(2700); } } 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_bit(boolean thisbit) { if (thisbit) { 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_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= 10) { interscore = 0; score++; } /* * NORMAL BUTTON USE */ // With the left button down - the bird falls and height (but not speed) boost is reduced faster than normal if (digitalRead(0) == HIGH) { thisrun++; playerOffset += 2; boost -=10; } else { thisrun = 0; } if (onit && lastPos < playerOffset) { speedBoost+=10; boost+=7; } else { if (thisrun>30) { boost-=thisrun; speedBoost -= thisrun; } } lastPos = playerOffset; // With the right button pressed - the bird flaps (slightly slowing its descent) if (digitalRead(2) == HIGH) { playerOffset -= 1; } /* * HANDLING SLIDES AND OTHER ON-GROUND AND IN-AIR BEHAVIOUR */ // Boost the speed and the height if you slide through the valleys if ( (playerOffset > 45) && onit && digitalRead(0) == HIGH) { boost +=75; // boost the flight speedBoost+=100; // boost the speed beep(1,350-speedBoost); } else { // The level of boost will keep the bird from falling (or even make it rise!) playerOffset -= floor(boost / 40); // Apply boost whilst in the air } // Cap speedboost! if (speedBoost > 700) { speedBoost = 700; } // Cap boost! if (boost > 450) { boost = 450; } // If you do nothing, the bird will gradually drop playerOffset += 3; // If you do nothing and you're in the air, the boost will drop if ((boost > 0) && (onit != 1)) { boost -=20; } if (boost < 0) boost = 0; // If you do nothing, the speed boost will drop if (speedBoost > 0) { speedBoost -= 7; } if (speedBoost < 0) speedBoost = 0; /* * APPLY SPEEDBOOST TO THE SPEED */ // This is the number of pixels the screen scrolls on each cycle of the game gostep = floor(2+speedBoost/110); // Detect whether the bird is on the ground onit = 0; if (playerOffset >= landscape[17] - 8) { onit = 1; playerOffset = landscape[17] - 8; } if (playerOffset <= 0) playerOffset = 0; // Just keep iterating around between zero and 2pi (roughly) if (incr >= 6.2831853) { incr -= 6.2831853; } //shift landscape left gostep places for (i = 0; i < 127 - gostep; i++) { landscape[i] = landscape[i + gostep]; } // fill in the rest of the landscape for (i = 127 - gostep; i < 128; i++) { float sinfactor = sin(incr); if (sinfactor < -0.2) { sinfactor = -0.2 - (-0.2-sinfactor)/2.0; } landscape[i] = floor(62 - height + (height * sinfactor)); if (sinfactor < 0.90) doneUpdate = 0; if (sinfactor > 0.97) { if (doneUpdate == 0) { byte newheight = (random(7, 25)); while ( (newheight > height - 7) && (newheight < height + 7) ) newheight = (random(7, 25)); height = newheight; si = (float)random(35,75)/1000.0; doneUpdate = 1; } } incr += si; } // Draw landscape and bird drawLandscape(2,8); // Fill in the bird in the top couple of rows drawBird(0,2); /* * Debug mode code for monitoring parameters */ /* ssd1306_char_f6x8(85, 0, " "); ssd1306_char_f6x8(85, 1, " "); doNumber(85, 0, speedBoost); doNumber(85, 1, boost); */ // Draw the score doNumber(85, 0, score); // Add time on to the game if the player is doing well if (score == 950 && interscore == 0) { totaldistance -=900; } else if (score == 1450 && interscore == 0) { totaldistance -=900; } else if (score == 1950 && interscore == 0) { totaldistance -=900; } else if (score == 2400 && interscore == 0) { totaldistance -=900; } if (totaldistance < 0) totaldistance = 0; // just in case - almost certainly would never happen!! // Draw the bar indicating the time left ssd1306_setpos(40, 0); ssd1306_send_data_start(); ssd1306_send_byte(B11111111); for (float sc = 1;sc < 0.016*(2000-totaldistance); sc += 1.0) { ssd1306_send_byte(B10111101); } for (float sc = 0.016*(2000-totaldistance); sc<32; sc += 1.0) { ssd1306_send_byte(B10000001); } ssd1306_send_byte(B11111111); ssd1306_send_data_stop(); ssd1306_setpos(56, 0); ssd1306_send_data_start(); ssd1306_send_byte(B11111111); ssd1306_send_data_stop(); if (totaldistance >= 2000) { while (playerOffset < (landscape[17] - 8) ) { drawBird(0,2); drawLandscape(2,8); playerOffset++; delay(50); } stopAnimate = 1; delay(1000); } } delay(1500); top = EEPROM.read(0); top = top << 8; top = top | EEPROM.read(1); if (score > top) { top = score; EEPROM.write(1,score & 0xFF); EEPROM.write(0,(score>>8) & 0xFF); newHigh = 1; } } 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); } byte doDrawRS(byte P2) { return(B00000011 >> P2); } byte doDrawLS(byte P2) { return(B00000011 << P2); } byte doDrawRSP(byte column, byte P2) { switch(column) { case 0: return((B00111111) >> P2); break; case 1: return((B01111100) >> P2); break; case 2: return((B11111110) >> P2); break; case 3: return((B11100110) >> P2); break; case 4: return((B01100110) >> P2); break; case 5: return((B00111100) >> P2); break; case 6: return((B00011000) >> P2); break; default: return((B00010000) >> P2); break; } } byte doDrawLSP(byte column, byte P2) { switch(column) { case 0: return((B00111111) << P2); break; case 1: return((B01111100) << P2); break; case 2: return((B11111110) << P2); break; case 3: return((B11100110) << P2); break; case 4: return((B01100110) << P2); break; case 5: return((B00111100) << P2); break; case 6: return((B00011000) << P2); break; default: return((B00010000) << P2); break; } } void drawBird(byte startRow, byte endRow) { for (byte c = startRow; c < endRow; c++) { ssd1306_setpos(8, c); ssd1306_send_data_start(); for (byte r = 8; r<16; r++) { int x = r; // bird with LS only if (c == playerOffset/8) { ssd1306_send_byte(doDrawLSP(r-8, playerOffset % 8)); // bird with RS only } else if (c == playerOffset/8 + 1) { ssd1306_send_byte(doDrawRSP(r-8, 8- playerOffset % 8)); } else { ssd1306_send_byte(B00000000); } } ssd1306_send_data_stop(); } } void drawLandscape(byte startRow, byte endRow) { // Draw the landscape and bird for (byte c = startRow; c < endRow; c++) { ssd1306_setpos(0, c); ssd1306_send_data_start(); for (byte r = 0; r<127; r++) { int x = r; int y = landscape[r]; if (r<8 || r>15) { if (c == y/8) { ssd1306_send_byte(doDrawLS(y % 8)); } else if (c == y/8+1) { ssd1306_send_byte(doDrawRS(8 - y % 8)); } else { ssd1306_send_byte(B00000000); } } else { // landscape with LS only if ( (c == y/8) && (c != playerOffset/8) && (c != playerOffset/8 + 1) ) { ssd1306_send_byte(doDrawLS(y % 8)); // landscape with RS only } else if ( (c == y/8+1) && (c != playerOffset/8) && (c != playerOffset/8 + 1) ) { ssd1306_send_byte(doDrawRS(8 - y % 8)); // bird with LS only } else if ( (c != y/8+1) && (c != y/8) && (c == playerOffset/8) ) { ssd1306_send_byte(doDrawLSP(r-8, playerOffset % 8)); // bird with RS only } else if ( (c != y/8+1) && (c != y/8) && (c == playerOffset/8 + 1) ) { ssd1306_send_byte(doDrawRSP(r-8, 8- playerOffset % 8)); // both with LS } else if ( (c == y/8) && (c == playerOffset/8) ) { ssd1306_send_byte(doDrawLSP(r-8, playerOffset % 8) | doDrawLS(y % 8)); // both with RS } else if ( (c == y/8+1) && (c == playerOffset/8+1) ) { ssd1306_send_byte(doDrawRSP(r-8, 8-playerOffset % 8) | doDrawRS(8- y % 8)); // landscape left, bird right } else if ( (c == y/8) && (c == playerOffset/8+1) ) { ssd1306_send_byte(doDrawRSP(r-8, 8-playerOffset % 8) | doDrawLS(y % 8)); // landscape right, bird left } else if ( (c == y/8+1) && (c == playerOffset/8) ) { ssd1306_send_byte(doDrawLSP(r-8, playerOffset % 8) | doDrawRS(8- y % 8)); } else { ssd1306_send_byte(B00000000); } } } ssd1306_send_data_stop(); } } ================================================ FILE: WrenRollercoasterAttinyArcade/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 // ---------------------------------------------------------------------------- /* 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 }; // ----------------------------------------------------------------------------