ELEC2645 (2017/18) / Mbed OS el16ajm

Engine/Engine.cpp

Committer:
Andrew_M
Date:
2018-05-08
Revision:
17:2a909f7da973
Parent:
16:4d329ce7b156

File content as of revision 17:2a909f7da973:

#include "Engine.h"

Engine::Engine()
{

}

Engine::~Engine()
{

}

void Engine::init()     // initialise the game parameters
{

    gameTest();

    _solid.init(10,15);
    _noodles.init(10,10);
    _gameOver = false;

    printf("Engine initialised\n");
}
void Engine::setLvl(int _levelToSet)
{
    _lvl = _levelToSet; // sets the current level, this is used when the menu changes the currently selected level
}

void Engine::loadLvl()
{
    if (_lvl == 1) { //checks to see what level should be loaded
        lvlOne();
    } else if (_lvl == 2) {
        lvlTwo();
    } else if (_lvl == 3) {
        lvlThree();
    }
    printf("Level Loaded\n");
}

void Engine::lvlOne()
{
    memset(_grid, 0, sizeof(_grid)); //clears the grid as it is a blank level
}

void Engine::lvlTwo()
{
    int _newGrid[22][22] = {
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,0,0},
        {0,0,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,0,0},
        {0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0},
        {0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0},
        {0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0},
        {0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0},
        {0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    };

    /*
    To load a set level, the level is first created as a new 2d array. It is set values for each coordinate
    Then the functional array has the values of _newGrid set to it individually
    */
    for(int i = 0; i < 22; i++) {
        for (int j = 0; j < 22; j++) {
            _grid[i][j] = _newGrid[i][j];
        }
    }
}

void Engine::lvlThree()
{
    int _newGrid[22][22] = {
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,0,0,0},
        {0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0},
        {0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0},
        {0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0},
        {0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0},
        {0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
    };

    for(int i = 0; i < 22; i++) {
        for (int j = 0; j < 22; j++) {
            _grid[i][j] = _newGrid[i][j];
        }
    }
}

void Engine::read_input(Gamepad &pad)
{
    _d = pad.get_direction(); //_d stores the direction that the joystick is pushed in for later use
}

void Engine::draw(N5110 &lcd)
{
    lcd.drawRect(0,0,48,48,FILL_TRANSPARENT);                           //draws a square around the 'play area'

    for (int j = 0; j < 22; j++) {                                      //goes through each coordinate in the grid,
        for (int i = 0; i < 22; i++) {
            if (_grid[i][j] != 0) {                                     //checks if the current square is NOT empty
                lcd.drawRect((2 * i) + 2,(2 * j) + 2,2,2,FILL_BLACK);   //if the above is true a 2x2 square will be drawn in the allocated location (the grid is scalled up 2x with a 2 pixel gap arround the edge)
            }
        }
    }

    lcd.printString("Score",50,0);


    int _score = (_solid.getLength() - 3);      //gets the score from the current snake length, it defualts to lenght 3 and increases with each food eaten. Therefor the length will allways be 3 more than the score

    char buffer1[21];                           //buffer used for changing ints to strings
    sprintf(buffer1,"%3d",_score);              //changes _score to a 3 letter string
    lcd.printString(buffer1,50,1);              //prints the new string in the top right of the screen
}

void Engine::gameOverScreen(N5110 &lcd)
{
    printf("GameOver\n");
    for (int i = 0; i < 22; i++) {                                  //covers every square of the grid
        for (int j = 0; j < 22; j++) {
            lcd.drawRect((2 * i) + 2,(2 * j) + 2,2,2,FILL_BLACK);   //draws a 2x2 square ontop of whatever is drawn in the cooridnates of the grid at the current time
            wait(0.01);                                             //slight delay to allow the screen to keep up
            lcd.refresh();                                          //refreshes the screen but doesn't clear it, the intention is to get an overlay effect
        }
    }
}

void Engine::update(Gamepad &pad)
{
    loadLvl(); //loads the level

    _solid.update(_d); //updates the coordinates of the snake

    checkGameOverAndSetGrid(); //checks if a game over state has been reached, if not set the new coordinates for the snake

    _grid[_noodles.getX()][_noodles.getY()] = 2;   //sets the current location of the food to the grid, stored as a 2 to differentiate it from the terrrain and tail

    if (checkFood()) { //checks if the head has reached food

        pad.tone(750.0,0.1); //plays a beep to signify that the snake has eaten

        growSnake(); //grows the snake as it has eaten the food

        bool empty = false; //makes a varible for finding an empty square

        while (!empty) {    //loops until empty square found

            _noodles.random(); //randomise position of noodles

            if ( _grid[_noodles.getX()][_noodles.getY()] == 0) {

                empty = true; //stops looping when free space is found
                // no need to set the grid as it will put the noodles in the new location the next time 'update' is called
            }

        }
    }
}

void Engine::checkGameOverAndSetGrid()
{

    int _l =_solid.getLength();                                 //stores the length of the snake to make it easier to debug

    for (int z = 0; z < _l; z++) {                              //checks the entire lenght of the snake

        if (_solid.getX(z) > 21 || _solid.getX(z) < 0) {        //checks if the x coordinate is out of bounds
            _gameOver = true;
        } else if (_solid.getY(z) > 21 || _solid.getY(z) < 0) { //checks if the y coordinate is out of bounds
            _gameOver = true;
        }

        if (_grid[_solid.getX(z)][_solid.getY(z)] != 1) {       //checks if the square the snake has moved to is clear
            _grid[_solid.getX(z)][_solid.getY(z)] = 1;          //if so sets the current square to be occupied
        } else {
            _gameOver = true;                                   //if not a game over state has been reached
        }
    }
}

bool Engine::checkFood()
{
    if (_grid[_solid.getX(0)][_solid.getY(0)] == 2) { //checks if the head has reached food
        return true;
    } else {
        return false;
    }
}

void Engine::growSnake()
{
    if (_solid.getLength()<484) { //checks if the snake can grow any more

        _solid.grow();  //makes snake longer

    }

    //If the snake cannot grow any more the player has won the game and filled every tile with the snake, this is extremly unlikely to happen and a game over state will be reached
    //I feel no need to implement a victory screen due to how unlikey this is to happen
}

float Engine::getScore()
{
    return _score;
}


bool Engine::getGameOver()
{
    return _gameOver;
}

void  Engine::gameTest()
{
    printf("Testing...\n");
    if (!snakeTest()) {
        printf("Some game tests failed!\n");
    } else if (!foodTest()) {
        printf("Some game tests failed!\n");
    } else {
        printf("All game tests passed!\n");
    }
}

bool  Engine::snakeTest()
{
    //checks if the grow function and initalisation works corectly

    _solid.init(12,10);

    bool _testState = true;

    _solid.grow();
    _solid.grow();

    int _testLength = _solid.getLength();

    if ( _testLength != 5) { //checks if the snake has grown
        _testState = false;
    }
    if ( _solid.getY(0) != 10) { //checks the coordiantes of each snake section to check they are in the correct place
        _testState = false;
    }
    if ( _solid.getX(0) != 12) {
        _testState = false;
    }
    if ( _solid.getY(1) != 11) {
        _testState = false;
    }
    if ( _solid.getX(1) != 12) {
        _testState = false;
    }
    if ( _solid.getY(2) != 12) {
        _testState = false;
    }
    if ( _solid.getX(2) != 12) {
        _testState = false;
    }

    return _testState;

}

bool  Engine::foodTest()
{
    //checks if the radomise function works correctly for the food

    bool _testState = true;

    _noodles.init(7,3);

    _grid[_noodles.getX()][_noodles.getY()] = 2; //sets the grid value so that the randomise loop will work correctly



    bool empty = false; //makes a varible for finding an empty square

    while (!empty) {    //loops until empty square found

        _noodles.random(); //randomise position of noodles

        if ( _grid[_noodles.getX()][_noodles.getY()] == 0) {

            empty = true; //stops looping when free space is found
            // no need to set the grid as it will put the noodles in the new location the next time 'update' is called
        }

    }


    if (_noodles.getX() ==7 && _noodles.getY() == 3) { //checks to see if randomised correctly
        _testState = false; //fail
    }

    return _testState;
}