/////////////////////////////////////////////////////////////////////////////////////////////////////
// PONG
// written by goldmomo
/////////////////////////////////////////////////////////////////////////////////////////////////////

typedef enum
{
    S_INIT,S_RUN,S_RESET_POS,S_GAME_OVER

} pongGameState_t;

typedef struct
{
    int ballX;
    int ballY;
    unsigned int ballAB;

    pongGameState_t state;
    unsigned int counter;
    unsigned int score;

} pongData_t;

__constant unsigned char gDecFont[][8] =
{
    {0xff,0x81,0x81,0x81,0x81,0x81,0x81,0xff},
    {0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01},
    {0xff,0x01,0x01,0x01,0xff,0x80,0x80,0xff},
    {0xff,0x01,0x01,0x01,0xff,0x01,0x01,0xff},
};


int isScorePixel(int x,int y,int headPosX,int headPosY,int headZoom,int scoreValue)
{
    int pSet = 0;

    if(x >= headPosX && x <(headPosX+(8*headZoom)) && y>=headPosY && y<(headPosY+(8*headZoom)))
    {
        int sx = (x-headPosX)/(headZoom);
        int sy = (y-headPosY)/(headZoom);

        pSet = (gDecFont[scoreValue&3][sy&7]>>(7-sx))&1;
    }
    return pSet;
}

__kernel void pong( __global pongData_t *pongData, __global unsigned int* out,
                    unsigned int width,unsigned int height,unsigned int sliderX,unsigned int sliderY,unsigned int sliderZ)
{
    //

    int x = get_global_id(0);
    int y = get_global_id(1);

    // get work data

    int ballX = pongData->ballX;
    int ballY = pongData->ballY;

    unsigned int ballAB  = pongData->ballAB;
    pongGameState_t state   = pongData->state;
    unsigned int counter = pongData->counter;

    unsigned int scoreP1 = pongData->score>>4;
    unsigned int scoreP2 = pongData->score&0xf;

    const int ballSize = 32;

    // states

    if(state == S_INIT)
    {
        scoreP1 = 0;
        scoreP2 = 0;
        state = S_RESET_POS; // reset pos
        counter = 0;
    }
    else if(state == S_RUN)
    {
        if (ballX<=0)
        {
            if(scoreP1<3)
            {
                scoreP1++;
                state = S_RESET_POS;
            }
            else // p1 loose
            {
                state = S_GAME_OVER;
            }
        }

        if((ballX+ballSize)>width)
        {
            if(scoreP2<3)
            {
                scoreP2++;
                state = S_RESET_POS; // reset pos
            }
            else // p2 loose
            {
                state = S_GAME_OVER; // game over
            }
        }
    }
    else if(state == S_RESET_POS)
    {
        ballX = width/2;
        ballY = height/2;
        state = S_RUN; // run
    }
    else if(state == S_GAME_OVER)
    {
        ballX = ballY = -100; // hide

        counter++;
        if(counter>=120) // wait 120 frames
        {
            state = S_INIT; // init
        }
    }
    else
    {
        state = S_INIT;
    }

    // background

    const unsigned int xyShift = 6;
    unsigned int color = (((x>>xyShift)&1)^((y>>xyShift)&1))?0:0x800000;

    // paddles

    int paddleAHeight = 256;
    int paddleAY = sliderX*(height-paddleAHeight)/64;
    int paddleAX = 10;
    int paddleAWidth = 32;

    int paddleBHeight = 256;
    int paddleBY = sliderY*(height-paddleBHeight)/64;
    int paddleBWidth = 32;
    int paddleBX = width-10-paddleBWidth;

    if( (x>=paddleAX && x<(paddleAX+paddleAWidth) && y>=paddleAY && y<(paddleAY+paddleAHeight)) ||
        (x>=paddleBX && x<(paddleBX+paddleBWidth) && y>=paddleBY && y<(paddleBY+paddleBHeight)) )
    {
        color = 0xffffff;
    }

    // ball

    if(x>=ballX && x<(ballX+ballSize) && y>=ballY && y<(ballY+ballSize))
    {
        color=0xffff00;
    }

    // paddle collision

    if( (ballX>paddleAX && ballX<(paddleAX+paddleAWidth) && ballY>paddleAY && ballY<(paddleAY+paddleAHeight)) ||
        ((ballX+ballSize)>paddleBX && (ballX+ballSize)<(paddleBX+paddleBWidth) && (ballY+ballSize)>paddleBY && (ballY+ballSize)<(paddleBY+paddleBHeight) ) )
    {
        ballAB^=1;
        color = 0xffffff;
    }

    // vertical collision

    if(ballY<=0 || (ballY+ballSize)>height)
    {
        ballAB^=2;
    }

    // move

    int ballXYSpeed = sliderZ+1;
    ballX += ballAB&1?ballXYSpeed:-ballXYSpeed;
    ballY += ballAB&2?ballXYSpeed:-ballXYSpeed;

    // store

    if(x==0 && y==0) // write back only at one thread
    {
        pongData->ballX = ballX;
        pongData->ballY = ballY;
        pongData->ballAB = ballAB;
        pongData->state = state;
        pongData->counter = counter;
        pongData->score = (scoreP1<<4) | scoreP2;
    }

    // scores

    int headZoom = 8;
    int headPos1X = width/2-(headZoom*8)-128;
    int headPos2X = width/2-(headZoom*8)+128;
    int headPosY = height/32;

    color = isScorePixel(x,y,headPos1X,headPosY,headZoom,scoreP1)?0xffffff:color;
    color = isScorePixel(x,y,headPos2X,headPosY,headZoom,scoreP2)?0xffffff:color;

    // flip

    color = (counter&1)?~color:color;

    // set pixel

    out[y*width+x] = color | 0xff000000;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
// SNAKE
// written by goldmomo
/////////////////////////////////////////////////////////////////////////////////////////////////////

typedef enum
{
    SN_INIT,SM_CLEAR,SN_RUN,SN_GAME_OVER

} snakeGameState_t;

typedef struct
{
  int posx,posy,cakex,cakey;
  unsigned int direction,length,counter,nextcounter,dummy;
  snakeGameState_t state;

} snakeData_t;

__kernel void snake_pass1( __global snakeData_t *snakeData, __global unsigned int* inout,
                    unsigned int width,unsigned int height,unsigned int sliderX,unsigned int sliderY,unsigned int sliderZ)
{
    //

    int x = get_global_id(0);
    int y = get_global_id(1);

    //

    const int updateCount = 8;
    const int pixelSize = 16;

    // get data

    int posx = snakeData->posx;   int posy = snakeData->posy;
    int cakex = snakeData->cakex; int cakey = snakeData->cakey;
    unsigned int direction = snakeData->direction;
    unsigned int length = snakeData->length;
    unsigned int counter = snakeData->counter;
    snakeGameState_t state = snakeData->state;

    // one thread

    if(x==0 && y==0)
    {
        if(state == SN_INIT)
        {
            posx = posy = 0;
            cakex = cakey = pixelSize * 8;
            length = 0;
            counter = updateCount;
            state = SM_CLEAR;
        }
        else if(state == SM_CLEAR)
        {
            state = SN_RUN;
        }
        else if(state == SN_RUN)
        {
            if(counter==0)
            {
                counter = updateCount;

                // position

                switch(sliderX&0xf)
                {
                    case 0:posx = posx + pixelSize;break;
                    case 1:posx = posx - pixelSize;break;
                    case 2:posy = posy + pixelSize;break;
                    case 3:posy = posy - pixelSize;break;
                }

                // mirror

                posx = posx<0?((width/pixelSize)-1)*pixelSize:posx>=width?0:posx;
                posy = posy<0?((height/pixelSize)-1)*pixelSize:posy>=height?0:posy;

                // check

                if(posx==cakex && posy==cakey) // found cake
                {
                   length+=pixelSize;
                   cakex = ((((cakex*2213+123)%(width)) /pixelSize)-1)*pixelSize;  // pseudo random no check for collision with snake
                   cakey = ((((cakey*2311+789)%(height))/pixelSize)-1)*pixelSize;
                }
                else if((inout[posy*width+posx]&0xffffff)!=0) // self collision
                {
                   counter = 200;
                   state = SN_GAME_OVER;
                }
            }
            else
            {
                counter--;
            }
        }
        else if(state == SN_GAME_OVER)
        {
            if(counter == 0)
            {
                state = SN_INIT;
            }
            counter--;
        }
        else
        {
            state = SN_INIT;
        }

        snakeData->posx = posx; snakeData->posy = posy;
        snakeData->cakex = cakex; snakeData->cakey = cakey;
        snakeData->direction = direction;
        snakeData->length = length;
        snakeData->counter = counter;
        snakeData->state = state;
    }
}

__kernel void snake_pass2( __global snakeData_t *snakeData, __global unsigned int* inout,
                    unsigned int width,unsigned int height,unsigned int sliderX,unsigned int sliderY,unsigned int sliderZ)
{
    //

    int x = get_global_id(0);
    int y = get_global_id(1);

    // get pixel

    unsigned int color = inout[y*width+x]&0xffffff;

    //

    const int pixelSize = 16;
    const unsigned int startLength = 8;

    // get data

    int posx = snakeData->posx;   int posy = snakeData->posy;
    int cakex = snakeData->cakex; int cakey = snakeData->cakey;
    unsigned int counter = snakeData->counter;
    snakeGameState_t state = snakeData->state;
    unsigned int length = snakeData->length;

    // set and clear

    if(color && counter == 0)
    {
        if((~(color|0xff000000)) > (length+startLength))
        {
            color = 0;
        }
        else
        {
            color = color - 1;
        }
    }

    if(counter == 0)
    {
        // draw cake

        if(x>=(cakex) && x<(cakex+pixelSize) && y>=(cakey) && y<(cakey+pixelSize))
        {
            color = 0xff0000;
        }

        // draw snake

        if(x>=(posx) && x<(posx+pixelSize) && y>=(posy) && y<(posy+pixelSize))
        {
            color = 0xffffff;
        }
    }

    // clear screen

    if(state == SM_CLEAR)
    {
        color = 0;
    }

    // set pixel

    inout[y*width+x] = color|0xff000000;
}
