Thursday, February 7, 2013

LCD Game Part 1 - Set Up: Number Flip-Flop Intro

If you are just joining us, check out the earlier posts to see how we set up the hardware for this Digispark project. As you see, we are using an LCD connected to our board with I2C.

Before we jump into any code, let me give you an overview of the game itself.

Look at the number in the photo. It might not be obvious at first, but notice that we have a string of all ten digits from 0 to 9 in a scrambled order. The object of the game is to put the numbers into proper order. You do that by flipping the order for the left hand side of the string. Watch how we might do this. In the text below, I will highlight the digits that I want to flip. You will see that their order will be reversed in the next iteration.

As you can see, our game will need a way for the player to select how many digits to flip. We will do this with a moving cursor and require the user to press a push button when the cursor reaches the desired spot. To make the game more challenging, we will start with a point score and subtract from it each time the cursor bounces. If you run out of points before you get the digits back in order, you lose. 

We need a place to start coding, so let's just take the sketch from the DigisparkLCD folder that we were using to test our hardware. We will build the game from there. Load the sketch and the in the file menu select 'Save As' and we will create a new version with the name 'NumFlipFlop'.

Let's declare a few variables to get us started:

String winner ="0123456789";            // a test string to easily see game is over
String workingString = "0123456789";    // the digits that we will scramble
long rnd;     // will hold a random number for us.


And make these changes to the setup() function:

void setup(){
  TinyWireM.begin();            // initialize I2C lib - comment this out to use with standard arduinos
  lcd.init();                   // initialize the lcd 
  lcd.clear();
  lcd.backlight();
  scrambleString();
  displayWorkingString();

}
For now, we will have setup() call two functions. The first one takes our working string and rearranges the digits into a random order.

void scrambleString() {
  lcd.clear();
  lcd.print("NUMBER FLIP-FLOP");
  
  for (int i=0; i<10; i++){
    rnd=random(9);
    swap(i,rnd);
  }
  delay(2000);
  lcd.clear();
   
}

The second one simply puts the digits on the LCD.

void displayWorkingString(){
  lcd.setCursor(3,0);
  lcd.print(workingString);
 
}

The first function repeatedly calls swap() passing the variable from our for loop and a random number which get transposed in our working string:


void swap(int x, int y){
  byte hold=workingString[x];
  workingString[x]=workingString[y];
  workingString[y]=hold;
}

This function treats the string as if it is an array. We set up a variable called hold where we park the first of the pair of digits we want to swap. We then stuff the second digit into the first one's spot. Finally, we grab the one we stored safely in hold and put it into the place where the second one was.

This all seems like it would work just fine, but there is a problem. My Digispark, Sparky, isn't really very clever with his random numbers. Every time I power down and restart him, he puts exactly the same random number on the screen. By definition, that is not random! This is a very common issue game programmers deal with. What we get is often called a pseudo random number. Since the days of the old TRS-80 computers we have had to work around this. We will go over my favorite technique for getting a real random pattern each time we start the game in the next post. To teach Sparky this new trick, we will add our push button. 

You will find the whole sketch below, so you can copy and paste it into your editor and give it a try. CU next time. 


/* ATtiny85 as an I2C Master   Ex2        BroHogan                           1/21/11
 * Modified for Digistump - Digispark LCD Shield by Erik Kettenburg 11/2012
 * SETUP:
 * ATtiny Pin 1 = (RESET) N/U                      ATtiny Pin 2 = (D3) N/U
 * ATtiny Pin 3 = (D4) to LED1                     ATtiny Pin 4 = GND
 * ATtiny Pin 5 = SDA on DS1621  & GPIO            ATtiny Pin 6 = (D1) to LED2
 * ATtiny Pin 7 = SCK on DS1621  & GPIO            ATtiny Pin 8 = VCC (2.7-5.5V)
 * NOTE! - It's very important to use pullups on the SDA & SCL lines!
 * PCA8574A GPIO was used wired per instructions in "info" folder in the LiquidCrystal_I2C lib.
 * This ex assumes A0-A2 are set HIGH for an addeess of 0x3F
 * LiquidCrystal_I2C lib was modified for ATtiny - on Playground with TinyWireM lib.
 * TinyWireM USAGE & CREDITS: - see TinyWireM.h
 */

//#define DEBUG
#include <TinyWireM.h>                  // I2C Master lib for ATTinys which use USI - comment this out to use with standard arduinos
#include <LiquidCrystal_I2C.h>          // for LCD w/ GPIO MODIFIED for the ATtiny85

#define GPIO_ADDR     0x27             // (PCA8574A A0-A2 @5V) typ. A0-A3 Gnd 0x20 / 0x38 for A - 0x27 is the address of the Digispark LCD modules.


LiquidCrystal_I2C lcd(GPIO_ADDR,16,2);  // set address & 16 chars / 2 lines

String winner ="0123456789";            // a test string to easily see game is over
String workingString = "0123456789";    // all the digits that we will scramble at the beginning of the game
long rnd;     // will hold a random number for us.


void setup(){
  TinyWireM.begin();                    // initialize I2C lib - comment this out to use with standard arduinos
  lcd.init();                           // initialize the lcd 
  lcd.clear();
  lcd.backlight();
  scrambleString();
  displayWorkingString();

}

void loop(){

  
}

void scrambleString() {
  lcd.clear();
  lcd.print("NUMBER FLIP-FLOP");
  
  for (int i=0; i<10; i++){
    rnd=random(9);
    swap(i,rnd);
  }
  delay(2000);
  lcd.clear();
   
}

void swap(int x, int y){
  byte hold=workingString[x];
  workingString[x]=workingString[y];
  workingString[y]=hold;
}
  

void displayWorkingString(){
  lcd.setCursor(3,0);
  lcd.print(workingString);
  //cursorLocation=4;
  lcd.noCursor();
  //if(workingString==winner) YouWin();
}






No comments:

Post a Comment