Source: sketch.js

/*******************************************************************************
*   @file sketch.js - Creating my own version of the Simon memory game
*
*   @author <a href='mailto:omareq08@gmail.com'> Omar Essilfie-Quaye </a>
*   @version 1.0
*   @date 29-Oct-2018
*
*******************************************************************************/

// TODO omar(omareq08@gmail.com): refactor tiles into a class

/**
*   Stores the start frame so that the animations are not clipped at the
*   beginning
*
*   @type{nuber}
*/
let frameOffset = 0;

/**
*   Stores the pattern that needs to be remembered
*
*   @type{Array<number>}
*/
let sequence = [];

/**
*   Stores the position in the sequence the program is at when it is displaying
*   the sequence for the user to remember.
*
*   @type{number}
*/
let position = 0;

/**
*   Flag to say wether or not the sequence is currently being displayed
*
*   @type{boolean}
*/
let showSequence = false;

/**
*   The alpha value for the tiles that are turned on
*
*   @type{number}
*/
let onAlpha = 255;

/**
*   The alpha value for the tiles that are turned off
*
*   @type{number}
*/
let offAlpha = 200;

/**
*   An array of alpha values for all the tiles
*
*   @type{Array<number>}
*/
let alpha = [offAlpha, offAlpha, offAlpha, offAlpha];

/**
*   The index of the green tile in the alpha array
*
*   @type{number}
*/
let greenIndex = 0;

/**
*   The index of the red tile in the alpha array
*
*   @type{number}
*/
let redIndex = 1;

/**
*   The index of the yellowtile in the alpha array
*
*   @type{number}
*/
let yellowIndex = 2;

/**
*   The index of the blue tile in the alpha array
*
*   @type{number}
*/
let blueIndex = 3;

/**
*   A flag to dictate wether the game is being played in the classica way or if
*   the user has to remember the sequence backwards.
*
*   @type{boolean}
*/
let classicMode = false;

/**
*   A flag which dictates if the start screen is shown or not.
*
*   @type{boolean}
*/
let startMode = true;

/**
*   A flag which dictates if the user can input the remembered sequence.
*
*   @type{boolean}
*/
let inputMode = false;

/**
*   A flag to see if the current user input has stopped lighting up
*
*   @type{boolean}
*/
let nextInput = true;

/**
*   An array storing the users input values
*
*   @type{Array<number>}
*/
let input = [];

/**
*   The minimum number of frames between each user input so that the selected
*   tile can light up.
*
*   @type{number}
*/
let inFrequency = 20;

/**
*   The number of frames between each flash of a tile whilst the computer is
*   showing the user the sequence.
*
*   @type{number}
*/
let frequency = 45;

/**
*   The start frame for when the tile flash should turn off so that consecutive
*   flashes of the same tile do not blend into one.
*
*   @type{number}
*/
let delayStart = 30;

/**
*   User score
*
*   @type{number}
*/
let score = -1;

/**
*   Function to add a random number between 1 and 4 to the end of the sequence
*   array
*
*   @param {number} amount - How many numbers to add to the sequence
*/
function extendSequence(amount=1) {
    for(let i = 0; i < amount; ++i) {
        sequence.push(floor(random(1, 5)));
    }
}

/**
*   Function to begin the game
*/
function startGame() {
    showSequence = true;
    startMode = false;
}

/**
*   Function to reset all game variables
*/
function resetGame() {
    score = sequence.length - 1;
    inputMode = false;
    nextInput = true;
    startMode = true;
    position = 0;
    sequence = [];
    extendSequence();
    input = [];
    frameOffset = frameCount;
}

/**
*   Function to handle key presses.  This will either switch the game to clasic
*   mode or backwards mode.
*/
function keyPressed() {
    if(startMode) {
        if(key.toLowerCase() == "c") {
            classicMode = true;
            resetGame();
            startGame();
        } else if(key.toLowerCase() == "b") {
            classicMode = false;
            resetGame();
            startGame();
        }
    }
}

/**
*   Function to handle mouse press events.  This will either begin the program
*   or act as the user input for the sequence.
*/
function mousePressed() {
    let mx = mouseX;
    let my = mouseY;

    if(mx > width || mx < 0 || my > height || my < 0) {
        return;
    }

    frameOffset = frameCount;
    if(startMode) {
        startGame();
    } else if(inputMode) {
        if(nextInput) {
          nextInput = false;

            if(mx < 0.5 * width && my < 0.5 * height) {
                input.push(1);
            } else if(mx > 0.5 * width && my < 0.5 * height) {
                input.push(2);
            } else if(mx < 0.5 * width && my > 0.5 * height) {
                input.push(3);
            } else if(mx > 0.5 * width && my > 0.5 * height) {
                input.push(4);
            }

            let inLength = input.length;
            let inputVal = input[inLength - 1];
            let cReset = inputVal != sequence[inLength - 1];
            let bReset = inputVal != sequence[sequence.length - inLength];
            let reset = classicMode && cReset || !classicMode && bReset;
            if(reset) {
                resetGame();
            }
        }
    }
}

/**
*   p5.js function, creates canvas and intialises sequence
*/
function setup() {
    noStroke();
    let size = 0;
    if(windowHeight > windowWidth) {
        size = 0.95 * windowWidth;
    } else {
        size = 0.95 * windowHeight;
    }
    let canvas = createCanvas(size, size);
    canvas.parent('sketch');

    extendSequence();
}

/**
*   Draws which mode is currently active in the top left corner of the screen
*/
function drawMode() {
    let fillVal = 0;
    if(startMode) {
        fillVal = 255;
    }
    fill(fillVal);

    textSize(0.035 * height);
    textAlign(LEFT, TOP);
    let string = "";
    if(classicMode) {
        string = "Classic Mode";
    } else {
        string = "Backwards Mode";
    }
    text(string, 5, 5);
}

/**
*   p5.js function, draws all elements on the screen
*/
function draw() {
    background(0);
    drawMode();

    if(startMode) {
        textAlign(CENTER, CENTER);
        textSize(0.035 * height);
        fill(255);
        text("To play classic Simon click C.  To play backwards Simon click B.",
         0.5 * width, 0.5 * height);
        // text("", 0.5 * width, 0.5 * height);

        text("Click to see sequence", 0.5 * width, 0.75 * height);

        if(score != -1) {
            textSize(0.2 * height);
            text(score, 0.5 * width, 0.25 * height);
        }
      return;
    }

    alpha[greenIndex] = offAlpha;
    alpha[redIndex] = offAlpha;
    alpha[yellowIndex] = offAlpha;
    alpha[blueIndex] = offAlpha;

    if(showSequence) {
        let sequenceVal = sequence[position];
        if(sequenceVal == 1) {
            alpha[greenIndex] = onAlpha;
        } else if(sequenceVal == 2) {
            alpha[redIndex] = onAlpha;
        } else if(sequenceVal == 3) {
            alpha[yellowIndex] = onAlpha;
        } else if(sequenceVal == 4) {
            alpha[blueIndex] = onAlpha;
        }

        if((frameCount - frameOffset) % frequency == (frequency - 1)) {
            position++;
            if(position == sequence.length) {
                position = 0;
                showSequence = false;
                inputMode = true;
                input = [];
            }
        }

        if((frameCount - frameOffset) % frequency > delayStart) {
            alpha[greenIndex] = offAlpha;
            alpha[redIndex] = offAlpha;
            alpha[yellowIndex] = offAlpha;
            alpha[blueIndex] = offAlpha;
        }
    } else if(inputMode) {
        let inputVal = input[input.length - 1];

        if((frameCount - frameOffset) % inFrequency == 0) {
            nextInput = true;

            if(input.length == sequence.length) {
                inputMode = false;
                showSequence = true;
                position = 0;
                extendSequence();
                input = [];
                frameOffset = frameCount + 10;
            }
        }

        if(!nextInput) {
            if(inputVal == 1) {
                alpha[greenIndex] = onAlpha;
            } else if(inputVal == 2) {
                alpha[redIndex] = onAlpha;
            } else if(inputVal == 3) {
                alpha[yellowIndex] = onAlpha;
            } else if(inputVal == 4) {
                alpha[blueIndex] = onAlpha;
            }
        }
    }

    fill(0, 255, 0, alpha[greenIndex]);
    rect(0, 0, width/2, height/2);

    fill(255, 0, 0, alpha[redIndex]);
    rect(width/2, 0, width/2, height/2);

    fill(255, 255, 0, alpha[yellowIndex]);
    rect(0, height/2, width/2, height/2);

    fill(0, 0, 255, alpha[blueIndex]);
    rect(width/2, height/2, width/2, height/2);

    drawMode();
}

Documentation generated by JSDoc 3.6.3 on Sun Jun 05 2022 20:16:33 GMT+0100 (BST)