A Stock Exchange Game using IO devices, the 128x128 uLCD, and other components

Dependencies:   4DGL-uLCD-SE SDFileSystem ShiftBrite mbed wave_player

Fork of WavePlayer_HelloWorld by jim hamblen

main.cpp

Committer:
RohanIyengar
Date:
2016-11-03
Revision:
2:3585cd51c765
Parent:
1:5b8e223e983d

File content as of revision 2:3585cd51c765:

#include "mbed.h"
#include "SDFileSystem.h"
#include "wave_player.h"
#include "ShiftBrite.h"
#include "uLCD_4DGL.h"
#include <mpr121.h>

// Stock exchange game
// Rohan Kumar Iyengar & Sai Sathiesh Rajan

// Set up Mbed LEDs
DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);

// Create the interrupt receiver object on pin 26
InterruptIn interrupt(p26);

// Setup the i2c bus on pins 9 and 10
I2C i2c(p9, p10);

// Setup the Mpr121:
// constructor(i2c object, i2c address of the mpr121)
Mpr121 mpr121(&i2c, Mpr121::ADD_VSS);

uLCD_4DGL uLCD(p28,p27,p30); // serial tx, serial rx, reset pin;

SPI spi(p11, p12, p13);

ShiftBrite myBrite(p15,p16,spi); //latch, enable, spi

SDFileSystem sd(p5, p6, p7, p8, "sd"); //SD card

AnalogOut DACout(p18);

wave_player waver(&DACout);

int stockPrices [7] = {0,0,0,0,0,0,0}; // array to store trend data for current stock
int maxEle = -1; // easily accessible max value of current trend data
int minEle = 200; // easily accessible min value of current trend data
int gameState = 0; // used to transition between game states
int stocksChosen = 0; // number of stocks user has chosen already
char* currentName; // current stock name
char* chosenName1; // first chosen stock name
char* chosenName2; // second chosen stock name
int stockGain1 = 0; // first stock's profit/loss
int stockGain2 = 0; // second stock's profit/loss
int stockNamePos = 0; // Used to generate next stock name
int stocksScrolled = 0; // Used to loop around stock names

// Function to get next stock whenever user selects / rejects a stock
void getNewStock()
{
    // Select stock name first
    switch (stockNamePos) {
    
    case 0:
        currentName = "Amazon";
        break;
    case 1:
        currentName = "Google";
        break;
    case 2:
        currentName = "Microsoft";
        break;
    case 3:
        currentName = "Apple";
        break;
    case 4:
        currentName = "IBM";
        break;
    case 5:
        currentName = "Intel";
        break;
    case 6:
        currentName = "Cisco";
        break;
    case 7:
        currentName = "Mathworks";
        break;
    case 8:
        currentName = "Uber";
        break;
    case 9:
        currentName = "Facebook";
        break;
    }
    
    // Generate trend data for stock
    srand(time(NULL));
    float currentPrice = (rand() % 100) + 50; // initialize price
    int trend = 0;
    int range = 20;
    int bound = 10;
    stockPrices[0] = currentPrice;
    maxEle = currentPrice;
    minEle = currentPrice;
    // Generate random data for first six days
    for (int i = 1; i < 6; i++) {
        float change = rand() % range - bound;
        currentPrice = currentPrice + change;
        stockPrices[i] = currentPrice;
        if (currentPrice > maxEle) {
            maxEle = currentPrice;
        }
        if (currentPrice < minEle) {
            minEle = currentPrice;
        }
        int wildCard = rand() % 10 - 5;
        if (change >= 0) {
            trend++;
            range = 15;
            bound = 5 + wildCard;
        } else {
            trend--;
            range = 15;
            bound = 10 + wildCard;
        }
    }
    
    // Add more random data for 7th day
    srand(time(NULL));
    int finalWildCard = rand() % 8 - 4 + trend; 
    float finalPrice = (1 + (finalWildCard / 34.0)) * currentPrice;
    stockPrices[6] = finalPrice;
    
    // Increment counters to allow next stock retrieval to be smooth
    stockNamePos = (stockNamePos + 1) % 10;
    stocksScrolled++; 
    if (stocksScrolled > 10 && stocksChosen != 2) {
        stocksScrolled = 0;
        stocksChosen = 0;
        stockNamePos = rand() % 10;
    }
}

// Uses current stock data to print a connected graph of points for that stock
void printGraph() {
    int x = 4;
    // draw graph axes
    uLCD.line(2, 20, 2, 126, LGREY);
    uLCD.line(2, 126, 126, 126, LGREY);
    float arrayRange = maxEle - minEle;
    float graphRange = 110 - 30; 
    float scaleFactor = graphRange / arrayRange;
    int offset = 1;
    for (int i = 0; i < 5; i++) {
        int currDist = stockPrices[i] - minEle;
        int y = (int) (110 - scaleFactor * currDist);
        int nextDist = stockPrices[i + 1] - minEle;
        int y2 = (int) (110 - scaleFactor * nextDist);
        // Draw lines between each pair of points w/ color based on trend
        if (y2 - y > 0) {
            uLCD.line(x, y, x + 20, y2, RED);
        } else if (y2 - y < 0) {
            uLCD.line(x, y, x + 20, y2, GREEN);
        } else {
            uLCD.line(x, y, x + 20, y2, WHITE);
        }
        uLCD.filled_circle(x, y, 2, BLUE);
        uLCD.filled_circle(x + 20, y2, 2, BLUE);
        // Scale to row, col format of uLCD
        int point1row = (int) ((x / 7.1) - .5);
        int point1col = (int) ((y /8.0) - .5);
        int point2col = (int) ((y2 / 8.0) - .5);
        int point2row = (int) (((x + 20) / 7.1) - .5);
        char buf1 [10];
        char buf2 [10];
        // NEED sprintf because printf on LCD has inexplicable behavior
        sprintf(buf1, "%d", stockPrices[i]);
        sprintf(buf2, "%d", stockPrices[i + 1]);
        uLCD.text_string(buf1, point1row + 1, point1col + 1, FONT_12X16, WHITE);
        offset*=-1;
        //uLCD.locate(point2row + offset, point2col + offset);
        //uLCD.printf("%3i", stockPrices[i+1]);
        uLCD.text_string(buf2, point2row + 1, point2col + 1, FONT_12X16, WHITE);
        x+=20;
    }
}

// Interrupt when touch sensor is pressed
void fallInterrupt() {
    int key_code=0;
    int i=0;
    int value=mpr121.read(0x00);
    value +=mpr121.read(0x01)<<8;
    // LED demo mod code inspired by J. Hamblen
    i=0;
    // puts key number out to LEDs for demo
    for (i=0; i<12; i++) {
        if (((value>>i)&0x01)==1) key_code=i+1;
        }
    led4=key_code & 0x01;
    led3=(key_code>>1) & 0x01;
    led2=(key_code>>2) & 0x01;
    led1=(key_code>>3) & 0x01;
    if (key_code == 9 && stocksChosen < 2) {
        gameState = 2;
    }
    if (key_code == 4 && stocksChosen < 2) {
        gameState = 1;
    }
}

int main()
{
    myBrite.Write(1023, 1023 ,0);
    bool finished = false;
    // Do title sequence one time
    // Welcome Sequence on Game Startup
    uLCD.cls();
    uLCD.text_string("Welcome to the\n Stock Exchange\n Challenge!" , 1, 1, FONT_12X16, GREEN);
    wait(2);
    uLCD.cls();
    srand(time(NULL));
    stockNamePos = rand() % 10;
    // Title Screen Image
    uLCD.media_init();
    uLCD.set_sector_address(0x003A, 0x7C26);
    uLCD.display_image(0,15); 
    wait(3);
    uLCD.cls();
    uLCD.text_string("Look at stock \n trends then -" , 1, 1, FONT_12X16, BLUE);
    uLCD.text_string("Pick two stocks \nthat will make\n you win BIG!", 1, 5, FONT_12X16, GREEN);
    uLCD.text_string("Press button 8\n to choose the\n current stock", 1, 9, FONT_12X16, RED);
    uLCD.text_string("Press button 3\n to move onto\n the next stock", 1, 13, FONT_12X16, WHITE);
    wait(4);
    uLCD.cls();
    uLCD.text_string("Press 3 to begin!", 1, 7, FONT_12X16, GREEN);
    // Now allow interrupts from touch sensor to trigger
    interrupt.fall(&fallInterrupt);
    interrupt.mode(PullUp);
    
    while (!finished) {
        // State to move to next stock
        if (gameState == 1) {
            getNewStock();
            uLCD.cls();
            uLCD.text_string("Stock: ", 1, 1, FONT_12X16, GREEN);
            uLCD.text_string(currentName, 7, 1, FONT_12X16, GREEN);
            printGraph();
            gameState = 0;
        }
        // State when stock is picked
        if (gameState == 2) {
            if (stocksChosen == 0) {
                chosenName1 = currentName;
                stockGain1 = stockPrices[6] - stockPrices[5];
            } else {
                chosenName2 = currentName;
                stockGain2 = stockPrices[6] - stockPrices[5];
            }
            stocksChosen++;
            if (stocksChosen > 1) {
                finished = true;
            } else {
                gameState = 1;
            }
        }
        wait(.5);
    }
    // End game summary + statistics
    int totalGain = stockGain1 + stockGain2;
    uLCD.cls();
    if (totalGain <= 0) {
        // Print losing image
        myBrite.Write(1023 ,0,0);
        uLCD.media_init();
        uLCD.set_sector_address(0x003A, 0x7C01);
        uLCD.display_image(0,22); 
    } else {
        myBrite.Write(0, 1023, 0);
        // Print winning image
        uLCD.media_init();
        uLCD.set_sector_address(0x003A, 0x7C57);
        uLCD.display_image(0,15); 
    }
    wait(1);
    uLCD.cls();
    // Print picks and statistics
    uLCD.locate(0,0);
    if (stockGain1 < 0) {
        uLCD.color(RED);
    } else if (stockGain1 > 0) {
        uLCD.color(GREEN);
    } else {
        uLCD.color(WHITE);
    }
    uLCD.printf("Pick 1: %s\n\r", chosenName1);
    uLCD.printf("Stock Gain 1: $%d\n\r\n", stockGain1);
    if (stockGain2 < 0) {
        uLCD.color(RED);
    } else if (stockGain2 > 0) {
        uLCD.color(GREEN);
    } else {
        uLCD.color(WHITE);
    }
    uLCD.printf("Pick 2: %s\n\r", chosenName2);
    uLCD.printf("Stock Gain 2: $%d\n\r\n", stockGain2);
    if (totalGain < 0) {
        uLCD.color(RED);
    } else if (totalGain > 0) {
        uLCD.color(GREEN);
    } else {
        uLCD.color(WHITE);
    }
    uLCD.printf("\nTotal Gain: $%d\n\r", totalGain);
    
    // Play appropriate winning/losing sound
    if (totalGain > 0) {
        FILE* wave_file;
        wave_file = fopen("/sd/Win.wav", "r");
        waver.play(wave_file);
        fclose(wave_file);
    } else {
        FILE* wave_file2;
        wave_file2 = fopen("/sd/Lose.wav", "r");
        waver.play(wave_file2);
        fclose(wave_file2);
    }
}