Game-Set :: Software

 

Instructions for Playing the Games

Some basic instructions are included in the game but, as the Nokia screen is a bit small, they're inevitably short and to the point.

General

  • The game will switch to Standby mode if neither switch is operated for 60 seconds.
  • In Standby mode, the current "state of play" of a game will be remembered.
  • If the game is switched off with the main slide switch, the current "state of play" will be lost. The standby power consumption is very low so the main slide switch shouldn't really need to be used unless you're packing the game away for an extended period.

  • When in Standby mode, the game is woken by pressing the Left hand button (not Left on the Navigation switch).
  • When not in Standby mode, the Left hand button will toggle the display backlight on and off.

  • The right hand, Navigation, switch is used to control a game's cursor (or highlighted position) using the Up, Down, Left and Right 'joystick'. The currently highlighed position is selected by pressing the Navigation switch's button ('Push').

  • Pushing both buttons together ends the game and returns the display to the main menu. The current state of play is lost and the game will be restarted next time it's selected from the main menu.

  • Selecting Help? from the main menu opens up a sub-menu with brief help for each game.

  • An additional item in the Help? sub-menu is called Contrast<>. As the battery fades, the contrast of the display may reduce. The contrast 'value' can be adjusted using the Left and Right navigation switches.
    Once you have navigated away from the Contrast<> sub-menu item, the new contrast value will be remembered even if the main slide switch is turned off.

15 Puzzle

The objective of this puzzle is well known. After selecting the puzzle from the main menu, navigate the cursor to the empty square (if it isn't already there) and Push & Hold the navigation Push button for at least 2 seconds to shuffle the numbers. (The reason for the 2 second delay is to prevent the puzzle being accidentally shuffled).

To play the game and unshuffle the numbers, highlight a number which is adjacent to the empty square and select it, with the navigation switch Push button. Obviously, only numbers adjacent to the empty square (vertically and horizontally - not diagonally) can be moved. Similarly, when the puzzle shuffles the numbers at the start, it obeys the rules so it must be possible to un-shuffle them.

To reset the shuffled puzzle to the start (without playing the game), again Push and Hold for 2 seconds on the empty square.

Minefield

Again, another well known old game. Each potential mine is shown as a dot on the display. The game randomly selects the mine positions - and how many there are. Navigate to a dot and Push to select it. The number of adjacent mines (horizontally, vertically and diagonally) will be shown. To start you off, the bottom left dot and the three adjacent dots are guaranteed to be clear of mines.

The game finishes when you step on a mine or when you've successfully navigated every mine-free dot. After a second or two, the display shows the mines' locations. Push once to start a new game. If you successfully navigated the minefield, the number of mines will increase for the next game. Once you leave the game for the main menu (by pressing both buttons), the initial number of mines will be restored (with a randomly generated 'bias').

Lunar Lander

Another classic! Use the Up and Down switches to open and close the throttle between 0% and 100%. Use the Push button to fire the thrusters. The Push button can be "jogged" for one thruster burst or held down for a burst every half-second.

Draughts (Checkers)

Arduino plays with the white pieces, you play with the black. You can only move one square for each move, diagonally forwards. Select a piece by navigating to it and pressing the Navigation switch Push button (The 'From' position). The move will be shown on the display - for example c0>. Navigate to the 'To' position and press Push again - for example, the display will show c0>d1. After a second or two, the game will move a white piece and show the move on the right of the display. It will also display a question mark inviting you to move again.

To take a white piece, after selecting your piece's 'From' square navigate directly to the 'landing' square as your 'To' square, "jumping over" the taken piece (Don't hop onto the piece you're taking as you might on a conventional board).

Pieces can be 'crowned' when they reach the far side of the board allowing them to move backwards. The game follows international rules in that it will allow a crowned piece to move any number of squares diagonally in any direction. However, like a non-crowned piece, a crowned piece must first move to an adjacent diagonal square before it can take a white piece (ie: it can't take a piece "from afar").

If you can't move, select the same piece for 'From' and 'To'. This will invite the game to have another move.

If you want to cancel your 'From' selection - before you've selected the 'To' position - navigate to, and select, one square vertically forward from the 'From' square. An illegal move that the game will interpret as you wishing to start that move again, so will cancel the 'From' position.

"-ish" The draughts software is far from perfect. It doesn't play any sort of 'intelligent' game, it always follows the same strategy and it sometimes appears it can't move even when it can. I've included it in case anyone want to have a play and try to improve it.


The Arduino IDE

If you're already familiar with the Arduino, you'll probably already have its programming/editing software. If not, you'll need to download and install the Arduino IDE from the main Arduino website.

 

If you already have an Arduino Uno, the simplest way of programming our ATmega328 is to temporarily replace the one in the Uno.

Altenatively, these articles elsewhere on my site, may help:

Making a stand-alone ATmega328 board.

Making a USB programming lead.
The programming header on the game's PCB facilitates the use of such a programming lead allowing the ATmega328 to be programmed while plugged into the game's PCB (and running on the game's battery).

 

The ATmega328 Software

The Arduino sketch can be downloaded here together with the PCB artwork in Circuit Wizard format.

//(c) vwlowen.co.uk
/*  Modified Adafruit_GFX\glcdfont.c for special graphics characters
static unsigned char  font[] PROGMEM = {
    0x00, 0x00, 0x00, 0x00, 0x00,    // 0 
    0x3E, 0x5B, 0x4F, 0x5B, 0x3E,    // 1  
    0x04, 0x02, 0x7F, 0x02, 0x04,    // 2 up arrow
    0x10, 0x20, 0x7F, 0x20, 0x10,    // 3 down arrow
    0x00, 0x00, 0x04, 0x00, 0x00,    // 4 dot
    0x0C, 0x12, 0x12, 0x0C, 0x00,    // 5 o white
    0x0C, 0x1E, 0x1E, 0x0C, 0x00,    // 6 o black
    0x0D, 0x13, 0x13, 0x0D, 0x00,    // 7 o white, crowned
    0x0D, 0x1F, 0x1F, 0x0D, 0x00,    // 8 o black, crowned
    0x3F, 0x21, 0x21, 0x21, 0x3F,    // 9 square
*/

#include <EEPROM.h>

//  https://github.com/adafruit/Adafruit-GFX-Library
#include <Adafruit_GFX.h>
#include <Adafruit_PCD8544.h>

//  http://playground.arduino.cc/Code/Enerlib
#include <Enerlib.h>

Energy energy;                   // Set up Arduino power-down mode (the easy way!)

//Define the Nokia 5110 pins. 
int Vcc = 9;
int CLK = 10;
int DIN = 11;
int DC = 12;
int CE = 13;
int RST = A0;
int LED = A1;

Adafruit_PCD8544 lcd = Adafruit_PCD8544(CLK, DIN, DC, CE, RST); 

// Definitions for push buttons & variables - common to all puzzles

int contrast;
int saveContrast = 0;
int sel = 1;                     // Menu selection

void(* resetFunc) (void) = 0;    // Declare reset function @ address 0 for Restart option.

int wakeup = 2;                  // INT 0. Wake up from interrupt if asleep
                                 // or, if not asleep, toggle backlight or 
                                 // press 'push' as well to restart game.
                                          
int push = 4;                    // Navigation switch inputs.                                   
int left = 5;  
int down = 6;
int right = 7;
int up = 8;

boolean ledON;                   // My Nokia's backlight is active low.

unsigned long runTimer;
unsigned long timeout = 60000;   // 60 seconds timeout;

int row = 2;         // Starting board row for various games.
int screenRow;       // Nokia row (0 = top row), calculated from board row.
int col = 0;         // Starting column. Board & Nokia are 0 = left.


/****************************************************************************************/
// Interrupt service routines to sleep & wake from PowerDown mode. (21 uA at PowerDown).
void INT0_ISR(void) {

  if (energy.WasSleeping())   {                   // Was sleeping so wake up
    ADCSRA = 0x80;                                // Re-enable ADC, momentarily to
    for (int i=0; i<random(100,500); i++);        // 
    randomSeed(analogRead(5));                    // re-initialize random number generator.
    ADCSRA = 0;                                   // Disable ADC again to save power.
      
    digitalWrite(Vcc, HIGH);
    lcd.begin(contrast);
  } else {
    ledON = !ledON;                               // Was awake so toggle backlight.
    digitalWrite(LED, ledON);
    while(digitalRead(wakeup) == LOW) {           // Check if 'push' is also pressed
      if (digitalRead(push) == LOW) resetFunc();  // Reset ATmega328 (jump to address 0).
    }
  }
  while(digitalRead(wakeup) == LOW);
  runTimer = millis();                            // Set power-down timer.
}

void checkTimeout() {
     if (millis() > (runTimer + timeout)) {      // After 'timeout' seconds of no activity, power down.
     digitalWrite(Vcc, LOW);
      energy.PowerDown();
    }  
}

/*************************************************************************************************/

void setup(void) {                                // Main Arduino setup routine
  pinMode(wakeup, INPUT);
  pinMode(left, INPUT);                           // configure navigation buttons 
  pinMode(right, INPUT);
  pinMode(up, INPUT);
  pinMode(down, INPUT);
  pinMode(push, INPUT);
  
  pinMode(Vcc, OUTPUT);                           // Nokia Vcc supply
  pinMode(LED, OUTPUT);                           // Nokia LED. LOW = On with my display.
  digitalWrite(Vcc, HIGH);
  
  ledON = false;
  digitalWrite(LED, !ledON);                      // LED backlight, off.
  
  digitalWrite(wakeup, HIGH);                     // Turn on internal pullups
  digitalWrite(left, HIGH);                   
  digitalWrite(right, HIGH);
  digitalWrite(up, HIGH);
  digitalWrite(down, HIGH);
  digitalWrite(push, HIGH);  
 
  randomSeed(analogRead(5));                   // Randomize random number generator for                
                                               // shuffling squares in fifteen puzzle and mines in minefield.
  ADCSRA = 0;                                  // Disable ADC. PowerDown current drops from 200uA to 18uA
  runTimer = millis();
  
  contrast = EEPROM.read(saveContrast);
  if ((contrast < 40) || (contrast > 65)) {
    contrast = 50;
    EEPROM.write(saveContrast, contrast);
  }
  
  lcd.begin(contrast);                         // Initialize Nokia display .
  lcd.clearDisplay();
  attachInterrupt(0, INT0_ISR, LOW);           // Enable interrupt on D2.
}


void loop() {                                  // Main Arduio's loop to display main menu
   lcd.clearDisplay();                         // and select game.
   lcd.setCursor(10, 0);
   lcd.print("15 Puzzle");
   lcd.setCursor(10, 10);
   lcd.print("Minefield");
   lcd.setCursor(10, 20);
   lcd.print("Lunar Lander");  
   lcd.setCursor(10, 30);
   lcd.print("Draughts-ish"); 
   lcd.setCursor(10, 40);
   lcd.print("Help?");       
   lcd.setCursor(0, (sel*10)-10);
   lcd.print(">");    
   lcd.display();

   checkTimeout();
 
   if (digitalRead(down) == LOW) {             // Loop through checking down, up & push buttons
     sel++;
     if (sel > 5) sel = 1;
     while(digitalRead(down) == LOW);
     runTimer = millis();                      // Reset timout timer.
   } 
   if (digitalRead(up) == LOW) {
     sel--;
     if (sel < 1) sel = 5;
     while(digitalRead(up) == LOW);
     runTimer = millis();
   }    
   
   if (digitalRead(push) == LOW) {             // Jump to appropriate game depending on cursor
     if (sel == 1) {                           // position. (Determined by 'sel' variable)
       delay(250);
       while(digitalRead(down) == LOW);
       loopFifteen(); 
     }        
      if (sel == 2) {
       delay(250);
       while(digitalRead(push) == LOW);
       loopMinefield();
     }   
     if (sel == 3) {
       delay(250);
       while(digitalRead(push) == LOW);
       loopLander(); 
     }   
     if (sel == 4) {
       delay(250);
       while(digitalRead(push) == LOW);
       loopDraughts(); 
     }              
     if (sel == 5) {                           
       delay(250);
       while(digitalRead(push) == LOW);
       loopHelp(); 
     }   
   } 
}  

//===========================================================================
//===========================================================================
// Start of Fifteen Puzzle routines
//===========================================================================
//===========================================================================

//  Global Variables for Fifteen Puzzle

char* cells[16];

boolean isRandomized = false;
int thisCell = 15;                             // Define starting cell, row and column.
int row15 = 3;
int col15 = 3;

void loopFifteen() {                           // Main Fifteen Puzzle loop
  lcd.setTextColor(BLACK);
  resetBoard();
  highlightCell(thisCell);
  runTimer = millis();
  while(1) {                                   // Loop forever.
    main15loop:
    checkTimeout();
     
    if (digitalRead(left) == LOW) {            // Set vaiables to determin row & column
      runTimer = millis();
      if (col15 > 0) {
        thisCell--;
        col15--;
      }
      while (digitalRead(left) == LOW);
      update15();
    }
    if (digitalRead(right) == LOW) {
      runTimer = millis();
      if (col15 < 3) {
        thisCell++;
        col15++;
      }
      while (digitalRead(right) == LOW);
      update15();
    }  
    if (digitalRead(up) == LOW) {
      runTimer = millis();
      if (row15 > 0) {
        thisCell-= 4;
        row15--;
      }
      while (digitalRead(up) == LOW);
      update15();
    }    
    if (digitalRead(down) == LOW) {
      runTimer = millis();
      if (row15 < 3) {
        thisCell+= 4;
        row15++;
      }
      while (digitalRead(down) == LOW);
      update15();
    }     

    if (digitalRead(push) == LOW) {
      runTimer = millis();
      if (cells[thisCell] == "") {               // Push button for 1 second on blank square to
        delay(1000);                             // shuffle the squares.
        if (digitalRead(push) == LOW) {
          if (!isRandomized) { 
            shuffle();
            isRandomized = true;
            goto main15loop;
          }
          if (isRandomized) {                    // 2nd long push on blank square will reset the board
            resetBoard();
            isRandomized = false;
            goto main15loop;
          }
        }
      } else {                                  // Not the blank square to swap square with blank square.
       if (thisCell > 0) { if (cells[thisCell-1] == "") swapCells(thisCell, thisCell-1);}
       if (thisCell > 3) { if (cells[thisCell-4] == "") swapCells(thisCell, thisCell-4);}
       if (thisCell <15) { if (cells[thisCell+1] == "") swapCells(thisCell, thisCell+1);}
       if (thisCell <12) { if (cells[thisCell+4] == "") swapCells(thisCell, thisCell+4);}
       }
       drawGrid();                              // Redraw the puzzle.
       highlightCell(thisCell);    
       while(digitalRead(push) == LOW); 
     } 
     delay(100);  
   }
}

void update15() {
  drawGrid();
  highlightCell(thisCell); 
  runTimer = millis();
}

void resetBoard() {
  thisCell = 15;
  cells[0] =  " 1"; cells[1] =  " 2"; cells[2] =  " 3"; cells[3] =  " 4";
  cells[4] =  " 5"; cells[5] =  " 6"; cells[6] =  " 7"; cells[7] =  " 8";
  cells[8] =  " 9"; cells[9] =  "10"; cells[10] =  "11"; cells[11] =  "12";
  cells[12] =  "13"; cells[13] =  "14"; cells[14] =  "15"; cells[15] =  "";
  drawGrid();
}

void drawGrid() {
  lcd.clearDisplay();
  lcd.drawRect(10, 0, 61, 48, BLACK);     // Draw ouline rectangle...
  lcd.drawLine(10, 12, 69, 12, BLACK);    // and horizontal lines.
  lcd.drawLine(10, 24, 69, 24, BLACK);
  lcd.drawLine(10, 36, 69, 36, BLACK);
 
  lcd.drawLine(25, 0, 25, 48, BLACK);     // Draw vertical lines.
  lcd.drawLine(40, 0, 40, 48, BLACK); 
  lcd.drawLine(55, 0, 55, 48, BLACK);   

  for (int i = 0; i<4; i++){              // Populate grid with numbers.
    lcd.setCursor((i*15)+12, 3);          // 1st row
    lcd.print(cells[i]);
  }
  for (int i = 0; i<4; i++){              // 2nd row
   lcd.setCursor((i*15)+12, 15);
   lcd.print(cells[i+4]);
  }  
  for (int i = 0; i<4; i++){              // 3rd row
   lcd.setCursor((i*15)+12, 27);
   lcd.print(cells[i+8]);
  }    
  for (int i = 0; i<4; i++){              // 4th row - spacing slightly different
   lcd.setCursor((i*15)+12, 38);          // due to limited number of vertical pixels.
   lcd.print(cells[i+12]);                // in Nokia display.
  }    
    
 lcd.display();  
}

// Shuffle cells randomly but only use legal moves so it will be possible to un-shuffle it.
void shuffle() {
  for (int i = 0; i< random(150)+250; i++){
    int Cell = random(15);
    if (Cell > 0) { if (cells[Cell-1] == "") swapCells(Cell, Cell-1);}
    if (Cell > 3) { if (cells[Cell-4] == "") swapCells(Cell, Cell-4);}
    if (Cell <15) { if (cells[Cell+1] == "") swapCells(Cell, Cell+1);}
    if (Cell <12) { if (cells[Cell+4] == "") swapCells(Cell, Cell+4);} 
    drawGrid();
  }

  for (int i=0; i<16; i++) {                      // Find blank cell to be able to highlight it.
    if (cells[i] == "") thisCell = i;
  } 
  
  // Determine which column the blank square is in in order to highlight it.
  // Row is calculated separately in the highlightCell function as rows are slightly
  // different heights due to screen-height limitations.
  
  if ((thisCell == 0) ||(thisCell == 4) ||(thisCell == 8) ||(thisCell == 12)) col15 = 0;
  if ((thisCell == 1) ||(thisCell == 5) ||(thisCell == 9) ||(thisCell == 13)) col15 = 1;
  if ((thisCell == 2) ||(thisCell == 6) ||(thisCell == 10) ||(thisCell == 14)) col15 = 2;
  if ((thisCell == 3) ||(thisCell == 7) ||(thisCell == 11) ||(thisCell == 15)) col15 = 3;  
  
  highlightCell(thisCell); 
  runTimer = millis();
}

void swapCells(int x, int y) {
 char* temp = cells[x];
 cells[x] = cells[y];
 cells[y] = temp; 
}

void highlightCell(int cell) {
  int x;
  int y;
 if (cell < 4) {                              // Each row is treated differently due to
   y = 10;                                    // different spacing on the 4th row.
   x = (cell * 15) + 12;                      // From the cell number calculate the screen
   row15 = 0;                                 // x and y for lcd draw rectangle funtion.
 }                                            // Also calculate which row the cell is in.
 if ((cell > 3) && (cell < 8)){ 
   y = 22;
   x = ((cell-4) * 15) + 12;
   row15 = 1;
 } 
 if ((cell > 7) && (cell < 12)){ 
   y = 34;
   x = ((cell-8) * 15) + 12;
   row15 = 2;
 }  
  if (cell > 11){ 
   y = 46;
   x = ((cell-12) * 15) + 12;
   row15 = 3;
 }  
 int delta;
 row15 == 3 ? delta = 10 : delta = 11;          // Compensate for different 4th row height.

 lcd.drawRect(x-1, y-9, 14, delta, BLACK);      // Draw rectangle in selected cell
 lcd.display();
}



/*****************************************************************************
 ****************************    Minefield  **********************************
 *****************************************************************************/
 
// Global variables for Minefield

int boardA[40];
int minesNearby = 0;
int bang = 0;
int level = 5;                           // Set difficulty level for minefield.
 

void setMines() {
  for (int i = 0;  i < 40; i++) boardA[i] = -1; 
  for (int i = 0; i < random(10) + level; i++) boardA[random(40)] = -2;
  
  // Make safe zone around bottom-left corner. Move mine to somewhere else.
  if (boardA[24] == -2) { boardA[24] = -1; boardA[random(0, 23)] = -2; }
  if (boardA[25] == -2) { boardA[25] = -1; boardA[random(0, 23)] = -2; }
  if (boardA[32] == -2) { boardA[32] = -1; boardA[random(0, 23)] = -2; }
  if (boardA[33] == -2) { boardA[33] = -1; boardA[random(0, 23)] = -2; }
  row = 4;
  col = 0;
}

void printBoardMines() {

   lcd.clearDisplay();
   int x;
   int y;
   int p;
   int num;

   for(y = 0; y < 6; y++) {      // print places based on definitions in board array
     for(x = 0; x < 8; x++) {
        num = 0;
       
       lcd.setCursor(x*10, (y*10));
           
       p = boardA[(y*8) + x];
       if (p == -1) lcd.write(4);        // dot
       if (p == -2) {
         bang == 1 ? lcd.write(6) : lcd.write(4);      // show mine or hide mine
       }
       if (p == -3) lcd.print('*');      // bang! (show mine!)
       if (p > -1) lcd.print(p);
     }
   }  

   lcd.setCursor(col*10, (row*10)+1);    // for the  board expects row 0 at the bottom.
//   lcd.write(9);                       // Draw rectangle around highlighted piece.
   lcd.print("_");                       // Draw underline char as easier to see on screen.
   lcd.display();                        // update display
}  

void button() {                      // Button pressed so check location
  delay(250);
  while (digitalRead(push) == LOW);
  runTimer = millis();
  int x = (row*8) + col;
  if (boardA[x] == -2) {
    lcd.clearDisplay();
    lcd.setCursor(0, 10);
    lcd.print("*** BANG ***");
    lcd.setCursor(4, 20);
    lcd.print("You stepped");
    lcd.setCursor(9, 30);
    lcd.print("on a mine!");
    lcd.display();
    delay(1000);
    boardA[x] = -3;
    bang = 1;
    printBoardMines();
    while (digitalRead(push) != LOW) {
      checkTimeout();
    }   
    row = 4;
    col = 0;    
  } else {                                          // Square is clear. Check adjacent squares
    if(x == 0) check(x,1,8,9,0,0,0,0,0); else       // and count how many contain mines.
    if(x == 7) check(x,-1,7,8,0,0,0,0,0); else
    if(x ==32) check(x,-8,-7,1,0,0,0,0,0); else
    if(x ==39) check(x,-8,-7,-1,0,0,0,0,0); else     
    if((x == 8) || (x == 16) || (x ==24)) check(x,-8,-7,1,8,9,0,0,0); else
    if((x ==15) || (x ==23) || (x ==31)) check(x,-8,-9,-1,7,8,0,0,0); else
    if ((x > 0) && (x < 7)) check(x,-1,1,7,8,9,0,0,0); else 
    if ((x > 8) && (x <31)) check(x,-1,-9,-8,-7,1,7,8,9); else
    if ((x >32) && (x <39)) check(x,-9,-8,-7,1,-1,0,0,0); 
    boardA[x] = minesNearby;
    printBoardMines();                           // Display number of mines on adjacent squares.
    
  } 
  int g = 0;                                     // Test if all empty squares have been checked.
  for (int i = 0; i < 40; i++) {
    if ((boardA[i] == -2) || (boardA[i] >= 0)) g++;
  }
  if (g == 40) {
    lcd.clearDisplay();
    lcd.setCursor(12, 2);
    lcd.print("WELL DONE!");
    lcd.setCursor(0, 12);
    lcd.print("You found all");
    lcd.setCursor(4, 22);
    lcd.print("the mines!");
    lcd.setCursor(10, 32);
    lcd.print("Press for");
    lcd.setCursor(10, 40);
    lcd.print("new game");
    lcd.display();
    delay(1000);
//    boardA[x] = -3;                     // Set board position to exploded mine value.
    bang = 1;                           // Signal printBoardMines to show mines.
    if (level < 16) level = level + 3;  // Increase number of mines + random number (max 16)
    printBoardMines(); 
    while (digitalRead(push) != LOW) {
      checkTimeout();
    } 
    row = 4;                            // Reset cursor to start position.
    col = 0;    
  } 
}


void check(int num, int a, int b, int c, int d, int e, int f, int g, int h) {
    minesNearby = 0;
    if (boardA[num + a] == -2) minesNearby++;
    if (boardA[num + b] == -2) minesNearby++;
    if (boardA[num + c] == -2) minesNearby++;
    if ((d != 0) && (boardA[num + d] == -2)) minesNearby++;
    if ((e != 0) && (boardA[num + e] == -2)) minesNearby++;
    if ((f != 0) && (boardA[num + f] == -2)) minesNearby++;
    if ((g != 0) && (boardA[num + g] == -2)) minesNearby++;
    if ((h != 0) && (boardA[num + h] == -2)) minesNearby++;
}  
    
void loopMinefield() {
  setMines();
  char c = 'a';
  printBoardMines();
  while(1) {
    checkTimeout();
    if (digitalRead(left) == LOW) {             // Select row and column to highlight a piece.
      if (col > 0) col--;
      printBoardMines();
      while(digitalRead(left) == LOW);
      runTimer = millis();
    }  
    if (digitalRead(right) == LOW) {
      if (col < 7) col++;
      printBoardMines();
      while(digitalRead(right) == LOW);
      runTimer = millis();
    }
    if (digitalRead(up) == LOW) {
      if (row > 0) row--;
      printBoardMines();
      while(digitalRead(up) == LOW);
      runTimer = millis();
    }
    if (digitalRead(down) == LOW) {
      if (row < 4) row++;
      printBoardMines();
      while(digitalRead(down) == LOW);
      runTimer = millis();
    }   
    if (digitalRead(push) == LOW) { 
      if ( (bang == 1)) {
        setMines();
        bang = 0;
      } else {
        button();
      }
      printBoardMines();
      while(digitalRead(down) == LOW);
      runTimer = millis();   
    }   
  }
}

/*****************************************************************************
 ***************** Lunar Lander **********************************************
 *****************************************************************************/
 
// Global variables for Lunar Lander 
  float height;
  float speed;
  float fuelLeft;
  float fuelUsed;
  float throttle; 
  float x;
  boolean endGame;

void initLander() {
  height = 557;
  speed = -50;
  fuelLeft = 500;
  fuelUsed = 0;
  throttle = 0; 
  x = 0;
  endGame = false;
}  
 
void loopLander() {
  runTimer = millis();
  if (endGame) {
    while(digitalRead(push) != LOW) {
     checkTimeout();
    }
  }
  initLander();
  lcd.clearDisplay();
  lcd.println("up/down for");
  lcd.print("Throttle");
  lcd.setCursor(0, 17);
  lcd.println("Push to fire");
  lcd.println("thrusters");
  lcd.setCursor(0, 40);
  lcd.println("Push to start");
  lcd.display();
  runTimer = millis();
  while(digitalRead(push) != LOW) {
    checkTimeout();
  }
  loopLander1:
  updateLander();
  
  if ((digitalRead(push) == LOW) && endGame) {
    initLander();
    endGame = false;
  }
  while(!endGame) {
    if (digitalRead(up) == LOW) {
      if (throttle < 100) throttle++;
      updateLander();
      delay(100);
    }
    if (digitalRead(down) == LOW) {
      if (throttle > 0) throttle--;
      updateLander();
      delay(100);
    }  
    
    if (digitalRead(push) == LOW) {
        doCalcs();
        updateLander();
        runTimer = millis();
        delay(1000);
    }
    checkTimeout();
  }
  goto loopLander1;
} 

void doCalcs() {
      fuelUsed = fuelUsed + throttle * 2.0;
      fuelLeft = 500.0 - fuelUsed;
      x = throttle - 5;
      speed = speed + x;
      height = height + speed + (x/2);
      updateLander();

      if ((height < 0) && (speed < -2)) {
        lcd.clearDisplay();
        lcd.setCursor(10, 10);
        lcd.print("You've");
        lcd.setCursor(8, 20);
        lcd.print("Crashed!");
        lcd.display();
        delay(5000);
        endGame = true;;
      }
      if ((height > 1) && (fuelLeft <= 0)) {
        lcd.clearDisplay();
        lcd.setCursor(10, 10);
        lcd.print("Out Of");
        lcd.setCursor(8, 20);
        lcd.print("Fuel!");
        lcd.display();
        delay(5000);
        endGame = true;;
      }      
      if ((height < 10) && (speed > -2) && (speed < 0) && (fuelLeft > 0)) {
        lcd.clearDisplay();
        lcd.setCursor(10, 10);
        lcd.print("You've");
        lcd.setCursor(8, 20);
        lcd.print("Made It!");
        lcd.display();
        delay(5000);
        endGame = true;
      }        
}

void updateLander() {
    lcd.clearDisplay();
    lcd.setCursor(0, 0);
    lcd.print("Height");
    lcd.setCursor(50, 0);
    if ((height < 1000) && (height >=0)) lcd.print(" ");  
    if ((height < 100) && (height >=0)) lcd.print(" ");  
    if ((height < 10) && (height >=0)) lcd.print(" ");    
    lcd.print(height, 0);
    lcd.setCursor(0, 10);
    lcd.print("Speed");
    lcd.setCursor(45, 10);
    speed > 0 ? lcd.write(2) : lcd.write(3);
    lcd.setCursor(55, 10);
    if (abs(speed) < 100) lcd.print(" ");
    if (abs(speed) < 10) lcd.print(" ");  
    lcd.print(abs(speed), 0); 
    lcd.setCursor(0, 20);
    lcd.print("Fuel :");
    lcd.setCursor(50, 20);
    if ((fuelLeft < 1000) && (fuelLeft >=0)) lcd.print(" ");
    if ((fuelLeft < 100) && (fuelLeft >=0)) lcd.print(" ");
    if ((fuelLeft < 10) && (fuelLeft >=0)) lcd.print(" ");
    lcd.print(fuelLeft, 0);   
    lcd.setCursor(0, 30);
    lcd.print(" Used:");
    lcd.setCursor(55, 30);
    if (fuelUsed < 100) lcd.print(" ");
    if (fuelUsed < 10) lcd.print(" ");    
    lcd.print(fuelUsed, 0);      
    lcd.setCursor(0, 40);
    lcd.print("Throttle");
    lcd.setCursor(55, 40);
    if (throttle < 100) lcd.print(" ");
    if (throttle < 10) lcd.print(" ");        
    lcd.print(throttle, 0);  
    lcd.print("%");    
    lcd.display();  
}




/*********************************************************************************
 ******************************* Draughts ****************************************
 *********************************************************************************/
// Global Variables for Draughts

// Startup board piece-positions
int boardR[]  = {1,0,1,0,0,0,-1,0, 0,1,0,0,0,-1,0,-1,
               1,0,1,0,0,0,-1,0, 0,1,0,0,0,-1,0,-1,
               1,0,1,0,0,0,-1,0, 0,1,0,0,0,-1,0,-1,
               1,0,1,0,0,0,-1,0, 0,1,0,0,0,-1,0,-1};
             
int board[64];       // Piece-positions on playing board.          


 
int fromRow;         // Variables for currently selected row & column.
int fromCol;
int toRow;
int toCol;

int myFromRow;        // Variables for Arduino's calculated row & column.
int myFromCol;
int myToRow;
int myToCol;

int again = 0;        // Checks if another move is available.
char* andTo = "";     // Prompts.
char* yourGo = "?";

int r1, r2, r3, r4;   // 'Internal' variables passed from function to function.

boolean fromSelected = false;   // Flags for current state of selections
boolean toSelected = false;  
boolean myFromSelected = false; 
 
 
void resetDraughts() {                   // Restore current board from startup board.
  for (int i= 0; i<64; i++) {
    board[i] = boardR[i];
  }
  fromSelected = false;
  toSelected = false;  
  myFromSelected = false;
  col = 0;

  fromRow=0;
  fromCol=0;
  toRow=0;
  toCol=0;

  myFromRow=0;
  myFromCol=0;
  myToRow=0;
  myToCol=0;  
  while((digitalRead(down) == LOW) || (digitalRead(up) == LOW));
}


void loopDraughts() {                           // Main Draughts loop
  resetDraughts();
  lcd.clearDisplay();
  printBoard();                                 // Display fresh board.
  lcd.setCursor(53, 30);
  lcd.print(yourGo);
  lcd.display();   
  runTimer = millis();
  while(1) {                                    // Main playing loop
  mainloop:                                     // Label for restart after input error
    checkTimeout();
    if (digitalRead(left) == LOW) {             // Select row and column to highlight a piece.
      if (col > 0) col--;
      printBoard();
      while(digitalRead(left) == LOW);
      runTimer = millis();
    }  
    if (digitalRead(right) == LOW) {
      if (col < 7) col++;
      printBoard();
      while(digitalRead(right) == LOW);
      runTimer = millis();
    }
    if (digitalRead(up) == LOW) {
      if (row < 7) row++;
      printBoard();
      while(digitalRead(up) == LOW);
      runTimer = millis();
    }
    if (digitalRead(down) == LOW) {
      if (row > 0) row--;
      printBoard();
      while(digitalRead(down) == LOW);
      runTimer = millis();
    }
  
    if (digitalRead(push) == LOW) {                       // Select highligted square
     if (!fromSelected) {                                 // First push selects 'From' square
       if ((board[(col*8)+row] == 1) || (board[(col*8)+row] == 2)) {
         fromRow = row;
         fromCol = col;
         fromSelected = true;
         yourGo = "";
       }
       delay(350);
       while(digitalRead(push) == LOW);
       runTimer = millis();
     } else {
       if (fromSelected) {                              // 2nd push selects 'To' square.
         if ((row == fromRow) && (col == fromCol)) {
            toSelected = true;                          // To & From equal means 'I can't go' so
            goto mainloop;                              // pass next move to Arduino.
         } 
         if ((row != fromRow) && (col == fromCol)) {    // To & From in same row means cancel 'From'
            fromSelected = false;                       // selection and have your turn again.
            goto mainloop;
         } 
         
         if (  ((abs(fromRow -row) > 1) || (abs(fromCol - col) > 1)) &&
                  (board[(fromCol*8)+fromRow] != 2)  ){    // Moved more than 1 row or column
           int x1 = abs((fromCol+col)/2) ;
           int y1 = abs((fromRow+row)/2);
           if ((board[(x1*8)+y1] != -1) && ( board[(x1*8)+y1] != -2)) goto mainloop;
         }  

         if ((abs(fromRow -row)) != (abs(fromCol - col))) goto mainloop; // Not a proper diagonal.
         if (board[(col*8)+row] == 0) {                       // Check if 'To' square is free
         if ((row <= fromRow) && (board[(fromCol*8)+fromRow] != 2)) goto mainloop;  // Quick exit! Can
                                                              // only move backwards when crowned. 'To' option
          toRow = row;                                        // still active to have  another 'To' selection.
          toCol = col;
          toSelected = true;                             
          board[(toCol*8)+toRow] = board[(fromCol*8)+fromRow]; //Update board array
          board[(fromCol*8)+fromRow] = 0;
          again = 0;
          andTo = "";
          if (abs(fromCol - toCol) == 2) jump();          // If move jumped over a piece update board.
          if (toRow == 7) board[(toCol*8)+toRow] = 2;     // if piece has reached top of board to be crowned.
        } 
       } 
     }
     delay(350);
     printBoard();
     while(digitalRead(push) == LOW);
    }
    if ((fromSelected && toSelected) ) {                  // 'From' & 'To' selected, move made. Now it's
      myGo();                                             // Arduino's turn.
      printBoard(); 
    } 
  }
}


void myGo() {                                               
   fromSelected = false;                                  // Clear selection flags. Only used to
   toSelected = false;                                    // determine who turn it is.
   myFromSelected = true;
   delay(2000);                                           // Artificial delay to give visual cue
   printBoard();
                                           
   for(int x = 0; x <= 7; x++) {                          // look for Arduino's pieces on board
     for(int y = 0; y <= 7; y++) {                        // -1 normal piece, -2 crowned piece.
       if(board[(x*8)+y] == -1) firstmove(-1, x, y);      // Make first move according to piece
       if(board[(x*8)+y] == -2) firstmove(1, x, y);       // found.
     }
   }
   
   if (r4 == 0) {
      board[(r3*8)+r4] = -2;
   }  else {
      board[(r3*8)+r4] = board[(r1*8)+r2];
   }
   board[(r1*8)+r2] = 0;

   int x1 = abs((r1+r3)/2); 
   int y1 = abs((r2+r4)/2);
   if(abs(r1-r3) == 2) {
       board[(x1*8)+y1] = 0;
   }
   myFromCol = r1;                                          // Update global variables.
   myFromRow = r2;
   myToCol = r3;
   myToRow = r4;
   yourGo = "?";
}

void firstmove(int lim, int x, int y) {
   for(int a = -1; a <= 1; a=a+2) {
     for(int b = -1; b <= lim; b=b+2) {
        checkmove(x, y, a, b);
     } 
   }
}
  
void checkmove(int x, int y, int a, int b) {
  int u = (x + a); int v = (y + b);
  if ((u > 7)||(u < 0)||(v > 7)||(v < 0)) return;
  if(board[(u*8) + v] == 0) {
    update(x, y, u, v);
    return; 
  }
  if(board[(u*8) + v] < 0) return; 
  u = (u+a); v = (v+b); 
  if((u>7)||(u<0)||(v>7)||(v<0)) return;
  if(board[(u*8)+v] == 0) update(x, y, u, v);
}
  
void update(int x, int y, int u, int v) {
   r1 = x; r2 = y; r3 = u; r4 = v;
}

void jump() {
   again = 1; 
   andTo = "+";
   int x1 = abs((fromCol+toCol)/2) ; int y1 = abs((fromRow+toRow)/2);
   board[(x1*8)+y1] = 0;
   printBoard();
} 

// Print Draughts Board on Nokia 5110 Display                     
void printBoard() {
   lcd.clearDisplay();
   int y = 0;
   for(int j = 7; j >= 0; j--) {      // print pieces based on definitions in board array
     lcd.setCursor(0, y);
     for(int i = 0; i < 8; i++) {
       int p = board[(i*8) + j];
       if (p == 0) lcd.write(4);      // dot
       if (p == 1) lcd.write(6);      // black normal
       if (p == 2) lcd.write(8);      // black crowned
       if (p == -1) lcd.write(5);     // white normal
       if (p == -2) lcd.write(7);     // white crowned
     }
     y = y + 6;                       // increment y axis every 8 pieces.
   }
   
   screenRow = 7 - row;               // Adafruit setCursor expect row 0 at the top. The maths
   lcd.setCursor(col*6, screenRow*6); // for the draughts board expects row 0 at the bottom.
   lcd.write(9);                      // Draw rectangle around highlighted piece.
     
   if (fromSelected) printFrom();     // 'From' square has been selected
   if (toSelected) printTo();         // 'To' square has also been selected
   if ((myToCol != myToRow) && (myFromSelected)) printMyMove(); // Arduino's turn = frint it's move.
   
   lcd.display();                     // update display
}  

void printFrom() {                    // Print co-ordinates of selected square
  lcd.setCursor(53, 30);              
  lcd.write(fromRow+97);
  lcd.print(fromCol);
  lcd.print(">");
}
void printTo() {
  lcd.setCursor(71, 30);
  lcd.write(toRow+97);
  lcd.print(toCol);
  lcd.display();
  lcd.setCursor(53, 42);
  lcd.print(andTo);
}

void printMyMove() {
  lcd.setCursor(53, 10);
  lcd.write(myFromRow+97);
  lcd.print(myFromCol);
  lcd.print(">");
  lcd.setCursor(71, 10);
  lcd.write(myToRow+97);
  lcd.print(myToCol);
  lcd.setCursor(53, 30);
  lcd.print(yourGo);
}

 
/*****************************************************************************
 ************************ HELP! **********************************************
 *****************************************************************************/
 
void loopHelp() {
  sel = 1;
  runTimer = millis();
  while(1) { 
    lcd.clearDisplay();
    lcd.setCursor(10, 0);
    lcd.print("15 Puzzle");
    lcd.setCursor(10, 10);
    lcd.print("Minefield");
    lcd.setCursor(10, 20);
    lcd.print("Draughts-ish");     
    lcd.setCursor(10, 30);
    lcd.print("General");         
    lcd.setCursor(10, 40);
    lcd.print("Contrast<>"); 
    lcd.setCursor(70, 40);
    lcd.print(contrast);    
    lcd.setCursor(0, (sel*10)-10);
    lcd.print(">");    
    lcd.display();

    checkTimeout();
 
   if (digitalRead(down) == LOW) {
     sel++;
     if (sel > 5) sel = 1;
     while(digitalRead(down) == LOW);
     runTimer = millis();
   } 
   if (digitalRead(up) == LOW) {
     sel--;
     if (sel < 1) sel = 5;
     while(digitalRead(up) == LOW);
     runTimer = millis();
   }  
 
   if (sel == 5) {
     if (digitalRead(left) == LOW) {
       if (contrast > 40) contrast--;
     }
     while (digitalRead(left) == LOW);

     if (digitalRead(right) == LOW) {
       if (contrast <65) contrast++;
     }
     while (digitalRead(right) == LOW);
     lcd.setContrast(contrast);
   }   
   if (EEPROM.read(saveContrast) != contrast) {
      EEPROM.write(saveContrast, contrast);
   } 
   
   if (digitalRead(push) == LOW) {
     if (sel == 1) {
       delay(250);
       while(digitalRead(down) == LOW);
       helpFifteen();       
     }     
      if (sel == 2) {
       delay(250);
       while(digitalRead(push) == LOW);
       helpMinefield();       
     }  
     if (sel == 3) {
       delay(250);
       while(digitalRead(push) == LOW);
       helpDraughts();       
     }         
     if (sel == 4) {
       delay(250);
       while(digitalRead(push) == LOW);
       helpGeneral();       
     }    
   } 
 }
}

void helpFifteen() {
  lcd.clearDisplay();
  lcd.println("Press/hold on");
  lcd.println("empty square");
  lcd.println("to shuffle or");
  lcd.print("reset board.");
  lcd.println();
  lcd.println("Press for menu");  
  lcd.display();
  runTimer = millis();
  while((digitalRead(push) != LOW) && (digitalRead(left) != LOW) &&
      (digitalRead(right) != LOW) && (digitalRead(up) != LOW) &&
      (digitalRead(down) != LOW)) {
     checkTimeout();
  }
  resetFunc();                                   // Restart chip.
}

void helpMinefield() {
  lcd.clearDisplay();
  lcd.print("Find all mines");
  lcd.print("Press a square");
  lcd.println("to show # of");
  lcd.print("mines near it.");
  lcd.println();
  lcd.println("Press for menu");
  lcd.display();
  runTimer = millis();
  while((digitalRead(push) != LOW) && (digitalRead(left) != LOW) &&
      (digitalRead(right) != LOW) && (digitalRead(up) != LOW) &&
      (digitalRead(down) != LOW)) {
  checkTimeout();
  }
  resetFunc();
}

void helpDraughts() {
  lcd.clearDisplay();
  lcd.println("Push for From");
  lcd.println("& To. If no");
  lcd.print("move,make From");
  lcd.print("& To the same.");
  lcd.setCursor(0, 40);
  lcd.println("Press for menu");
  lcd.display();
  runTimer = millis();
  while((digitalRead(push) != LOW) && (digitalRead(left) != LOW) &&
      (digitalRead(right) != LOW) && (digitalRead(up) != LOW) &&
      (digitalRead(down) != LOW)) {
  checkTimeout();
  }
  resetFunc();
}


void helpGeneral() {
  lcd.clearDisplay();
  lcd.println("Left button:");
  lcd.println("LED or power");
  lcd.println("Both = restart");
  lcd.println();
  lcd.print("Press for menu");
  lcd.display();
  runTimer = millis();
  while((digitalRead(push) != LOW) && (digitalRead(left) != LOW) &&
      (digitalRead(right) != LOW) && (digitalRead(up) != LOW) &&
      (digitalRead(down) != LOW)) {
  checkTimeout();
  }
  resetFunc();
}
   
   

 

Back to Index | Page 1 | Page 2 | Page 3

This site and its contents are © Copyright 2005 - All Rights Reserved.