> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://cisc1600.sketchpad.cc/sp/pad/view/ro.wfdOYiVAoUE/rev.700
 * 
 * authors: 
 *   Joey Ching
 *   Xiaowen Huang

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



//Project 2 - Snake Game

bool gameEnd;
Snake s1;
Food apple;
bool foodEaten;
int score;
bool run;
float R, G, B;
int count, speed;
/***********************Setup Function***********************/        
void setup() {
    size(300, 300);
    rectMode(CENTER);
    s1 = new Snake();
    apple = new Food();
    foodCount = 0;
    speed = 15;
    gameEnd = false;
    score = 0; count = 0;
    run = true;
}

/***********************Draw Function***********************/
void draw() {

    frameRate(speed);
    background(204);
    R = random(255);
    G = random(255);
    B = random(255);
    /****************** instructions ******************/
    fill(0, 0, 0, 80);
    textSize(15); textAlign(LEFT);
    text("Instructions:", 10, 30);
    text("Control the snake using arrow key", 10, 50);
    text("Don't touch the black border", 10, 70);
    text("And don't run into your tail", 10, 90);
    /****************** instructions ******************/
    
    if(run){
    /************ border ************/
    strokeWeight(3);
    line(0, 0, 0, height);
    line(0, 0, width, 0);
    line(0, height-2, width, height-2);
    line(width-1, 0, width-1, height);
    /************ border ************/
    
    strokeWeight(1);
    /**** run game****/
    s1.getDirection();
    s1.move();
    fill(R, G, B);
    s1.display();
    /**** run game****/
    
    
    /***************************** food generator ******************************/
    apple.display();
    foodEaten = checkEaten(s1.snakeX[0], s1.snakeY[0], apple.foodX, apple.foodY);
    if(foodEaten){
        apple.newFood();
        s1.addBlock();
        score++; count++;
    }
    /***************************** food generator ******************************/
    
    
    /******** show current score ********/
    textAlign(RIGHT);
    fill(0, 102, 153, 50);
    textSize(20);
    text("SCORE", 270, 230);
    textSize(40);
    text(score, 270, 270);
    /******** show current score ********/
    
    /***** increase speed gradually *****/
    if(count != 0 && count%5 == 0){
        speed+=3;
        count++;
    }
    /***** increase speed gradually *****/
    
    /******************************** check game end *********************************/
    gameEnd = (hitBorder(s1.snakeX[0], s1.snakeY[0]) || hitSelf(s1.snakeX, s1.snakeY));
    if(gameEnd){
        run = false;
    }
    /******************************** check game end *********************************/
    }
    else{
        again = gameOverPage();
    }
}

/***********************Class Snake***********************
** Building snake with several pairs of x, y-coordinates *
**********************************************************/
class Snake{
    int sLength;
    int[] snakeX;
    int[] snakeY;
    char direction;
    int speed;
    
    Snake(){        //constructor
        sLength = 1;
        snakeX = new int[150];
        snakeY = new int[150];
        snakeX[0] = width/2; snakeY[0] = height/2;
    }
    
    void display(){        //display snake
        for(int i = 0; i < sLength; i++){
            rect(snakeX[i], snakeY[i], 10, 10);
        }
    }
    
    void addBlock(){        //add one more block to tail
        snakeX[sLength] = snakeX[sLength-1] + 10;
        snakeY[sLength] = snakeY[sLength-1] + 10;
        sLength++;
    }
    
    void getDirection(){        //get direction from keyboard
        if(keyPressed == true && key == CODED){
            if(keyCode == LEFT && direction != 'd'){
                direction = 'a';
            }
            else if(keyCode == RIGHT && direction != 'a'){
                direction = 'd';
            }
            else if(keyCode == UP && direction != 's'){
                direction = 'w';
            }
            else if(keyCode == DOWN && direction != 'w'){
                direction = 's';
            }
        }
        /*if(keyPressed == false){        //disable auto running for test
            direction = ' ';
        }*/
    }
        
    void move(){        //function to make snake move
        for(int i = sLength-1; i > 0; i--){        //shift blocks afterwards
            snakeX[i] = snakeX[i-1];
            snakeY[i] = snakeY[i-1];
        }
        
        switch(direction){                         //and then make new head
            case 'w':                              //according to directions
                snakeY[0] -= 10;
                break;
            case 's':
                snakeY[0] += 10;
                break;
            case 'a':
                snakeX[0] -= 10;
                break;
            case 'd':
                snakeX[0] += 10;
                break;
        }
    }
}
/**************** Class Snake Ends ***********************/


/***********************Class Food ***********************
**                Generating food randomly               *
**********************************************************/
class Food{
    int foodX, foodY;
    
    Food(){        //constructor to generate first food
        foodX = (round(random(295))+1);
        foodY = (round(random(295))+1);
    }
    
    void display(){        //display food by drawing red dot
        ellipseMode(CENTER);
        fill(255, 0, 0);
        ellipse(foodX, foodY, 8, 8);
    }
    
    void newFood(){        //generate new food location if eaten
        foodX = (round(random(295))+1);
        foodY = (round(random(295))+1);
    }
}
/**************** Class Food Ends ***********************/


bool checkEaten(int snakeX, int snakeY, int foodX, int foodY){        //check if previous food is eaten
    if(dist(snakeX, snakeY, foodX, foodY) < 10){
        return true;
    }
    else{
        return false;
    }
}

bool hitBorder(int snakeX, int snakeY){        //check if the snake hits the border
    if(snakeX <= 2 || snakeX >= width-2 || snakeY <= 2 || snakeY >= height-2){
        return true;
    }
    else{
        return false;
    }
}

bool hitSelf(int[] snakeX, int[] snakeY){        //check if the snake runs into itself
    for(int i = 1; i < snakeX.length; i++){
        if(dist(snakeX[0], snakeY[0], snakeX[i], snakeY[i]) < 10){
            return true;
            break;
        }
    }
}

void gameOverPage(){        //displaying game over and final score
    background(0);
    textAlign(CENTER);
    fill(200, 200, 0);
    text("Game Over", 150, 130);
    textSize(20);
    
    text("Your final score:", 150, 150);
    textSize(30);
    text(score, 150, 180);
    
}