Game-Set :: Software | |
Instructions for Playing the GamesSome 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
15 PuzzleThe 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.
MinefieldAgain, 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 LanderAnother 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 IDEIf 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 ATmega328 SoftwareThe 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(); }
|