Game entry for the Game Programming Contest sponsored by OutrageousCircuits.com (RETRO Game Console)

Dependencies:   mbed

Fork of Official_RETRO by GHI Electronics

Revision:
4:2d41b942a823
Parent:
3:7680307a02d7
diff -r 7680307a02d7 -r 2d41b942a823 Game.cpp
--- a/Game.cpp	Sat Feb 14 11:40:55 2015 +0000
+++ b/Game.cpp	Sun Mar 01 09:34:29 2015 +0000
@@ -1,288 +1,332 @@
 #include "Game.h"
+#include <cstdarg>
+
+#define SOUNDWIN    1
 
-const char* Game::LOSE_1 = "You lose.";
-const char* Game::LOSE_2 = "Press ship to restart.";
-const char* Game::SPLASH_1 = "Press ship to start.";
-const char* Game::SPLASH_2 = "Press robot to switch.";
-    
-Game::Game() : left(P0_14, PullUp), right(P0_11, PullUp), down(P0_12, PullUp), up(P0_13, PullUp), square(P0_16, PullUp), circle(P0_1, PullUp), led1(P0_9), led2(P0_8), pwm(P0_18), ain(P0_15), i2c(P0_5, P0_4) {
-    srand(this->ain.read_u16());
-    
-    this->lastUp = false;
-    this->lastDown = false;
-    this->mode = true;
-    
-    this->i2c.frequency(400);
-    this->writeRegister(0x2A, 0x01); 
-    
-    this->colors[0] = DisplayN18::RED;
-    this->colors[1] = DisplayN18::GREEN;
-    this->colors[2] = DisplayN18::BLUE;
-    
+unsigned short piececolor[5] = { 0, LGRAY, WHITE, WHITE, DGRAY };
+
+Game::Game() : left(P0_14, PullUp), right(P0_11, PullUp), down(P0_12, PullUp), up(P0_13, PullUp), square(P0_16, PullUp), circle(P0_1, PullUp), led1(P0_9), led2(P0_8), pwm(P0_18), ain(P0_15), i2c(P0_5, P0_4)
+{
     this->initialize();
 }
 
-void Game::readRegisters(char address, char* buffer, int len) {
-    this->i2c.write(Game::I2C_ADDR, &address, 1, true);
-    this->i2c.read(Game::I2C_ADDR | 1, buffer, len);
+void Game::showNumber(int y, int val)
+{
+    char buf[8];
+    int len = sprintf(buf, "%d", val);
+    
+    disp.fillRect( 128, y+9, 32, 8, BLACK );
+    
+    if( val < 1000 )
+        disp.drawString(145-len*3, y+9, buf, CYAN, BLACK);
+    else
+        disp.drawString(145-3*3, y+9, "...", CYAN, BLACK);
 }
 
-int Game::writeRegister(char address, char value) {    
-    char buffer[2] = { address, value };
-    
-    return this->i2c.write(Game::I2C_ADDR, buffer, 2);
+void Game::initialize() 
+{
+    disp.clear();
+    led1 = 0; led2 = 0;
+    pwm = 0; pwm.period_ms(1);
+
+    tickCounter = 1000;
+    tickWin = 0;
+    bamTicksLeft = 0;
+
+    level = 0; oldLevel = -1;
+    for( int lev=0; lev<L_LAST; lev++ )
+        your[lev] = 10000;
+        
+    initTable();
 }
 
-double Game::convert(char* buffer) {
-    double val = ((buffer[0] << 2) | (buffer[1] >> 6));
-            
-    if (val > 511.0) 
-        val -= 1024.0;
+void Game::tick()
+{
+    tickCounter++;
+    
+    playSound();
     
-    return val / 512.0;
+    if( levelCompleted ) {
+        if( (tickCounter % 40) ==  0 ) { led1 = 1; led2 = 0; }
+        if( (tickCounter % 40) == 20 ) { led1 = 0; led2 = 1; }
+    }
+    
+    checkButtons();
+    if( level != L_INTRO)
+        showTable();
+        
+    playBam();
+    
+    wait_ms(25);
 }
 
-void Game::getXYZ(double& x, double& y, double& z) {
-    char buffer[6];
+void Game::checkButtons()
+{
+    bool kSquare = false, kCircle = false, kUp = false, kDown = false, kLeft = false, kRight = false;
     
-    this->readRegisters(0x01, buffer, 6);
+    //
+    // Repeat key after 16*25ms = 400ms
+    //
+    char noKey = 0;
+    if( !square.read() ) kSquare = !lastKey; else noKey++;
+    if( !circle.read() ) kCircle = !lastKey; else noKey++;
+    if( !up.read()     ) kUp     = !lastKey; else noKey++;
+    if( !down.read()   ) kDown   = !lastKey; else noKey++;
+    if( !left.read()   ) kLeft   = !lastKey; else noKey++;
+    if( !right.read()  ) kRight  = !lastKey; else noKey++;
+    if( noKey < 6 ) {
+        if( lastKey == 0 ) lastKey = 16; else lastKey--;
+    } else
+        lastKey = 0;
     
-    x = this->convert(buffer);
-    y = this->convert(buffer + 2);
-    z = this->convert(buffer + 4);
+    //
+    // Select level
+    //
+    if( kSquare ) if( ++level > L_LAST  ) level = L_INTRO;
+    if( kCircle ) if( --level < L_INTRO ) level = L_INTRO + 1; //L_LAST;
+    if( kSquare || kCircle || (!up.read() && !left.read()) )
+        initTable();
+    
+    //
+    // Arrow keys to move
+    //
+    if( kUp    ) moveTable( -1,  0,      1, TABH,      0, TABW );
+    if( kDown  ) moveTable(  1,  0, TABH-2,   -1,      0, TABW );
+    if( kLeft  ) moveTable(  0, -1,      0, TABH,      1, TABW );
+    if( kRight ) moveTable(  0,  1,      0, TABH, TABW-2,   -1 );
 }
 
-void Game::printDouble(double value, int x, int y) {
-    char buffer[10];
-    int len = sprintf(buffer, "%.1f", value);
-    
-    this->disp.drawString(x, y, buffer, DisplayN18::WHITE, DisplayN18::BLACK);
+void Game::fillTable(char i0, char j0, char h, char w, unsigned char v)
+{
+    unsigned char nh = v & 0xF0;
+    unsigned char nl = v & 0x0F;   
+
+    for( int i=i0; i<i0+h; i++ )
+        for( int j=j0; j<j0+w; j++ ) {
+            if( v <= BONG ) {
+                table[i][j] = v;
+            } else {
+                if( nh && table[i][j] <= BONG ) table[i][j] = FREE;
+                if( nh ) table[i][j] = (table[i][j] & 0x0F) | nh;
+                if( nl ) table[i][j] = (table[i][j] & 0xF0) | nl;
+            }
+
+            if( nl > MARK ) nl += 0x01;
+            if( nh > BALL ) nh += 0x10;
+            
+            change[i] |= 1 << j;
+        }
 }
 
-void Game::drawAxes() {
-    for (int i = 0; i < 3; i++) {
-        this->disp.drawLine(0, i * (Game::GRAPH_HEIGHT + Game::GRAPH_SPACING), 0, i * (Game::GRAPH_HEIGHT + Game::GRAPH_SPACING) + Game::GRAPH_HEIGHT, DisplayN18::WHITE);
-        this->disp.drawLine(0, i * (Game::GRAPH_HEIGHT + Game::GRAPH_SPACING) + Game::GRAPH_HEIGHT / 2, DisplayN18::WIDTH, i * (Game::GRAPH_HEIGHT + Game::GRAPH_SPACING) + Game::GRAPH_HEIGHT / 2, DisplayN18::WHITE);
+void Game::fillTableV( unsigned char v, ...)
+{
+    unsigned short pos;
+
+    va_list arguments; 
+        
+    va_start ( arguments, v );
+    
+    while( (pos = (unsigned short)va_arg ( arguments, int)) != 0 ) {
+        unsigned char h = (pos & 0x00F0) >> 4;
+        unsigned char w = (pos & 0x000F) >> 0;
+        if( w == 0 ) w = 16;
+        
+        fillTable((pos&0xF000)>>12, (pos&0x0F00)>>8, h, w, v);
+        if( (v&0x0F) > MARK ) v += h*w;
+        if( v > BALL ) v += ((h*w) << 4);
     }
+    
+    va_end ( arguments ); 
 }
 
-void Game::drawPoint(int axis, double value) {
-    if (value < -1.0)
-        value = -1.0;
+void Game::moveTable(int di, int dj, int i0, int i2, int j0, int j2 )
+{
+    if( level == L_INTRO || levelCompleted ) return;
+  
+    int ai = 1, aj = 1, mov = 0;
+    
+    if( i2 < i0 ) ai = -1;
+    if( j2 < j0 ) aj = -1;
 
-    if (value > 1.0)
-        value = 1.0;
+    //
+    // Check BONG (RED) - If any ball hits BONG, no ball can move.
+    //
+    for( int i=i0; i!=i2; i+=ai )
+        for( int j=j0; j!=j2; j+=aj )
+            if( table[i][j] & 0xF0 )
+                if( (table[i+di][j+dj] & 0x0F) == BONG ) {
+                    showNumber( MOVY, ++moves );
+                    bamX = (j+dj)*8; bamY = (i+di)*8;
+                    fillPieceV( BLACK,  0x0077, 0 );
+                    fillPieceV( YELLOW, 0x1111, 0x5111, 0x1511, 0x5511, 0x3115, 0x1351, 0x2233, 0 );
+                    disp.draw((unsigned short *)piece, bamX, bamY, 7, 7);
+                    bamTicksLeft = 5;
+                    return;
+                }
 
-    value += 1.0;
-    value /= 2.0;
-    value = 1.0 - value;
-    value *= Game::GRAPH_HEIGHT;
+    //
+    // Move Balls
+    //
+    for( int i=i0; i!=i2; i+=ai )
+    {
+        for( int j=j0; j!=j2; j+=aj )
+        {
+            unsigned char ball = table[i][j] & 0xF0;
+            
+            if( !ball ) continue;
+            if( table[i+di][j+dj] & 0xF0 ) continue;
+            if( (table[i+di][j+dj] & 0x0F) == WALL ) continue;
 
-    this->disp.setPixel(this->graphX, axis * (Game::GRAPH_HEIGHT + Game::GRAPH_SPACING) + (int)value, this->colors[axis]);
-}
-
-void Game::checkGraphReset() {
-    if (this->graphX > DisplayN18::WIDTH) {
-        this->graphX = 0;
-        this->disp.clear();
-        this->drawAxes();
+            table[i+di][j+dj] |= ball;
+            table[i][j] &= 0x0F;
+            
+            change[i+di] |= 1 << (j+dj);
+            change[i] |= 1 << j;
+            
+            mov = 1;
+        }
+    }
+    moves += mov;
+    showNumber( MOVY, moves );
+       
+    //
+    // Check if Level is Completed
+    //
+    int balls = 0, marks = 0;
+    for( int i=0; i<TABH; i++ ) {
+        for( int j=0; j<TABW; j++ )
+            if( table[i][j] & 0xF0 ) {
+                balls++;
+                if( ((table[i][j] & 0x0F) - MARK + 1) == ((table[i][j] & 0xF0) >> 4) )
+                    marks++;
+            }
+    }
+    if( marks == balls ) {
+        levelCompleted = true;
+        if( moves < your[level] ) your[level] = moves;
+        showNumber( YOUY, your[level] );
+        disp.drawString(8, 117, "  LEVEL COMPLETED!       ", RED, BLACK);
+        led1 = 1; led2 = 1;
+#ifdef SOUNDWIN
+        tickWin = tickCounter;
+#endif
     }
 }
 
-void Game::initialize() {    
-    this->initializeBall();
-        
-    this->paddleX = DisplayN18::WIDTH / 2 - Game::PADDLE_WIDTH / 2;
-    this->pwmTicksLeft = 0;
-    this->lives = 10;//4;
-    
-    this->pwm.period_ms(1);
-    this->pwm.write(0.00);
-    
-    this->disp.clear();
-}
-    
-void Game::initializeBall() {
-    this->ballX = DisplayN18::WIDTH / 2 - Game::BALL_RADIUS;
-    this->ballY = DisplayN18::HEIGHT / 4 - Game::BALL_RADIUS;
-    
-    this->ballSpeedX = rand() % 2 ? 1 : -1;
-    this->ballSpeedY = rand() % 2 ? 1 : -1;
+void Game::showTable()
+{
+    for( int i=0; i<TABH; i++ )
+        for( int j=0; j<TABW; j++ )
+            showPiece(i,j);
 }
 
-void Game::tick() {  
-    this->checkButtons();
+void Game::showPiece(int i, int j)
+{
+    if( !(change[i] & (1 << j)) ) return;
+
+    change[i] &= ~(1 << j);
+    
+    unsigned char ball = table[i][j] >> 4;
+    unsigned char base = table[i][j] & 0x0F;
+    
+    unsigned char basec = base;
+    if( basec > MARK ) basec = MARK;
+    
+    fillPiece( 0, 0, 7, 7, piececolor[basec] );
     
-    if (this->mode) {
-        this->clearPaddle();
-        this->clearBall();
-        
-        this->updatePaddle();
-        this->updateBall();
+    if( base == WALL ) {
+        fillPiece( 1, 1, 1, 1, 0 );
+        fillPiece( 1, 5, 1, 1, 0 );
+        fillPiece( 5, 1, 1, 1, 0 );
+        fillPiece( 5, 5, 1, 1, 0 );
+    }
     
-        this->checkCollision();
-        
-        this->drawPaddle();        
-        this->drawBall();
+    if( base == BONG ) {
+        fillPiece( 1, 1, 5, 5, RED );
+        //fillPieceV( RED, 0x1111, 0x5111, 0x1511, 0x5511, 0x3115, 0x1351, 0x2233, 0 );
+    }
+    
+    if( base == MARK ) {
+        fillPiece( 3, 3, 1, 1, WHITE );
+    }
+    
+    if( ball ) {
+        fillPieceV( BLUE, 0x1051,0x0175, 0x1651, 0 );
+        //fillPieceV( WHITE 0x3311, 0 );
+    }
         
-        this->checkPwm();
-        this->checkLives(); 
-        
-        wait_ms(25);
-    }
-    else {    
-        double x, y, z;
+    disp.draw((unsigned short *)piece, j*8, i*8, 7, 7);
+    
+    if( base > MARK && !ball )
+        disp.drawNumber( j*8+2, i*8+1, base-MARK, WHITE, piececolor[MARK] );
         
-        this->getXYZ(x, y, z);
-        
-        this->checkGraphReset();
-        this->drawPoint(0, x);
-        this->drawPoint(1, y);
-        this->drawPoint(2, z);
-        this->graphX++;
-    } 
+    if( ball > 1 )
+        disp.drawNumber( j*8+2, i*8+1, ball-1, WHITE, BLUE );
 }
 
-void Game::checkButtons() {
-    if (!this->square.read()) {
-        this->mode = !this->mode;
-        
-        this->disp.clear();
-        
-        if (!this->mode) {
-            this->graphX = 0;
-            
-            this->drawAxes();
-        }
-        
-        this->led1.write(this->mode);
-        this->led2.write(!this->mode);
-    }  
-    
-    bool xDir = this->ballSpeedX > 0;
-    bool yDir = this->ballSpeedY > 0;
-    bool isUp = !this->up.read();
-    bool isDown = !this->down.read();
-    
-    if (isUp && isDown) goto end;
-    if (!isUp && !isDown) goto end;
-    
-    if (isUp && this->lastUp) goto end;
-    if (isDown && this->lastDown) goto end;
-    
-    if (!xDir) this->ballSpeedX *= -1;
-    if (!yDir) this->ballSpeedY *= -1;
-    
-    if (isUp) {
-        if (++this->ballSpeedX > 5) this->ballSpeedX = 5;
-        if (++this->ballSpeedY > 5) this->ballSpeedY = 5;
-    }
-    else if (isDown) {
-        if (--this->ballSpeedX == 0) this->ballSpeedX = 1;
-        if (--this->ballSpeedY == 0) this->ballSpeedY = 1;
-    }
-    
-    if (!xDir) this->ballSpeedX *= -1;
-    if (!yDir) this->ballSpeedY *= -1;
-    
-end:
-    this->lastUp = isUp;
-    this->lastDown = isDown;    
+void Game::fillPiece(int x0, int y0, int w, int h, unsigned short v)
+{
+    for( int y=y0; y<y0+h; y++ )
+        for( int x=x0; x<x0+w; x++ )
+            piece[y][x] = v;
 }
 
-void Game::drawString(const char* str, int y) {
-    this->disp.drawString(DisplayN18::WIDTH / 2 - (DisplayN18::CHAR_WIDTH + DisplayN18::CHAR_SPACING) * strlen(str) / 2, y, str, DisplayN18::WHITE, DisplayN18::BLACK);         
+void Game::fillPieceV(unsigned short color, ...)
+{
+    va_list arguments; 
+    va_start( arguments, color );
+    
+    int pos;
+    while( (pos = va_arg ( arguments, int)) != 0 )
+        fillPiece( (pos&0xF000)>>12, (pos&0x0F00)>>8, (pos & 0x00F0) >> 4, (pos & 0x000F) >> 0, color );
+    
+    va_end( arguments );
 }
 
-void Game::showSplashScreen() {
-    this->drawString(Game::SPLASH_1, DisplayN18::HEIGHT / 2 - DisplayN18::CHAR_HEIGHT / 2);  
-    this->drawString(Game::SPLASH_2, DisplayN18::HEIGHT / 2 + DisplayN18::CHAR_HEIGHT / 2); 
-       
-    while (this->circle.read())
-        wait_ms(1);
-        
-    this->disp.clear();
-}
-
-void Game::clearPaddle() {
-    this->disp.fillRect(this->paddleX, DisplayN18::HEIGHT - Game::PADDLE_HEIGHT, Game::PADDLE_WIDTH, Game::PADDLE_HEIGHT, DisplayN18::BLACK);    
-}
-
-void Game::drawPaddle() {
-    this->disp.fillRect(this->paddleX, DisplayN18::HEIGHT - Game::PADDLE_HEIGHT, Game::PADDLE_WIDTH, Game::PADDLE_HEIGHT, DisplayN18::BLUE);    
-}
-
-void Game::updatePaddle() {
-    if (this->left.read())
-        this->paddleX += Game::PADDLE_SPEED;
-        
-    if (this->right.read())
-        this->paddleX -= Game::PADDLE_SPEED;
-}
-
-void Game::clearBall() {   
-    this->disp.fillRect(this->ballX - Game::BALL_RADIUS, ballY - Game::BALL_RADIUS, Game::BALL_RADIUS * 2, Game::BALL_RADIUS * 2, DisplayN18::BLACK); 
+void Game::showImage( unsigned char x, unsigned char y, unsigned short back, unsigned short front, ... )
+{
+    fillPieceV( back, 0x0077, 0 );
+    
+    va_list args;
+    va_start( args, front );
+    
+    int pos;
+    while( (pos = va_arg ( args, int)) != 0 )
+        fillPiece( (pos&0xF000)>>12, (pos&0x0F00)>>8, (pos & 0x00F0) >> 4, (pos & 0x000F) >> 0, front );
+    
+    va_end( args );
+    
+    disp.draw( (unsigned short *)piece, x, y, 7, 7 );
 }
 
-void Game::drawBall() {
-    this->disp.fillRect(this->ballX - Game::BALL_RADIUS, ballY - Game::BALL_RADIUS, Game::BALL_RADIUS * 2, Game::BALL_RADIUS * 2, DisplayN18::RED); 
-}
-
-void Game::updateBall() {
-    this->ballX += this->ballSpeedX;
-    this->ballY += this->ballSpeedY;
-}
-
-void Game::checkCollision() {    
-    if (this->paddleX < 0)
-        this->paddleX = 0;
-        
-    if (this->paddleX + Game::PADDLE_WIDTH > DisplayN18::WIDTH)
-        this->paddleX = DisplayN18::WIDTH - Game::PADDLE_WIDTH;
+void Game::playBam()
+{
+    if( bamTicksLeft == 0 )
+        return;
         
-    if ((this->ballX - Game::BALL_RADIUS < 0 && this->ballSpeedX < 0) || (this->ballX + Game::BALL_RADIUS >= DisplayN18::WIDTH && this->ballSpeedX > 0))
-        this->ballSpeedX *= -1;
-        
-    if (this->ballY - Game::BALL_RADIUS < 0 && this->ballSpeedY < 0)
-        this->ballSpeedY *= -1;
-        
-    if (this->ballY + Game::BALL_RADIUS >= DisplayN18::HEIGHT - Game::PADDLE_HEIGHT && this->ballSpeedY > 0) {
-        if (this->ballY + Game::BALL_RADIUS >= DisplayN18::HEIGHT) {
-            this->initializeBall();
-            
-            this->lives--;
-        }
-        else if (this->ballX > this->paddleX && this->ballX < this->paddleX + Game::PADDLE_WIDTH) {
-            this->ballSpeedY *= -1;
-            
-            this->pwmTicksLeft = Game::BOUNCE_SOUND_TICKS;                       
-        }
+    if( --bamTicksLeft % 2 ) {
+        led1 = 1;
+        led2 = 1;
+    } else {
+        led1 = 0;
+        led2 = 0;
+    }
+    
+    //pwm.write(0.5);
+    pwm.period_us(50);
+    pwm.pulsewidth_us(50);
+    
+    if (bamTicksLeft == 0) {
+        pwm.write(0.0);
+        fillPieceV( WHITE, 0x0077, 0 );
+        fillPieceV( RED,   0x1111, 0x5111, 0x1511, 0x5511, 0x3115, 0x1351, 0x2233, 0 );
+        disp.draw((unsigned short *)piece, bamX, bamY, 7, 7);
     }
 }
 
-void Game::checkPwm() {
-    if (this->pwmTicksLeft == 0) {
-        this->pwm.write(0.0);
-    }
-    else {
-        this->pwmTicksLeft--;
-        this->pwm.write(0.5); 
-    }
-}
-
-void Game::checkLives() {
-    if (this->lives == 0) {
-        this->disp.clear();
-        
-        this->drawString(Game::LOSE_1, DisplayN18::HEIGHT / 2 - DisplayN18::CHAR_HEIGHT); 
-        this->drawString(Game::LOSE_2, DisplayN18::HEIGHT / 2);  
-        
-        while (this->circle.read())
-            wait_ms(1);
-            
-        this->initialize();
-    }
-    else {
-        this->disp.drawCharacter(0, 0, static_cast<char>(this->lives + '0'), DisplayN18::WHITE, DisplayN18::BLACK);   
-    }
+void Game::playSound()
+{
+    if( tickCounter == tickWin +  1 ) { pwm = 0.02; pwm.period( 1.0 / 330.0 ); }
+    if( tickCounter == tickWin +  9 ) { pwm = 0.02; pwm.period( 1.0 / 440.0 ); }
+    if( tickCounter == tickWin + 25 ) { pwm = 0; pwm.period_ms(1); }
 }
\ No newline at end of file