Learn Arduino with Olympia Circuits
Learn Arduino
  • Home
    • Get Started
    • How to Use This Site
  • Electronics
    • The Basics
    • Electricity Flows like Water
    • Electronic Components
    • The Arno Board
  • Programming
    • The Basics
    • setup and loop Blocks
    • Variables and Arrays
    • Connecting with the Pins
    • Flow Control >
      • if Statement
      • Loops
      • Delays
    • Functions
    • Serial Communication
    • USB
    • Some Thoughts
  • Projects
    • Projects 1 >
      • 1.01: Blink
      • 1.02 Blink x2
      • 1.03 Blink Faster
      • 1.04 LED Chase!
      • 1.05 Wait To Blink
      • 1.06 Blink a Little Faster Now
      • 1.07 LED Fade
      • 1.08 RGB Blink
      • 1.09 Change RGB Color with SW1
      • 1.10 Fade RGB Colors
      • 1.11 Reaction Time Game
    • Projects 2 >
      • 2.01 Hello World
      • 2.02 Talk Back
      • 2.03 ASCII Values
      • 2.04 Ski Game
      • 2.05 Demonstration of the String Object
    • Projects 3 >
      • 3.01 Read the Potentiometer
      • 3.02 ASCIIbet Soup
      • 3.03 Potentiometer sets LED Brightness
      • 3.04 Potentiometer Sets Blink Rate
      • 3.05 LED Chase, Part II
    • Projects 4 >
      • 4.01 Bringing the Piezo to Life
      • 4.02 Controlling the Piezo with a Function
      • 4.03 Piezo C Major
      • 4.04 Piezo Greensleaves
      • 4.05 Piezo Metronome
      • 4.06 Piezo as an Input
      • 4.07 Piezo as an Input 2
      • 4.08 Metronome II
      • 4.09 Piezo Playback
      • 4.10 Piezo Fireworks
      • 4.11 Piezo Mosquito
    • Projects 5 >
      • 5.01 The Phototransistor
      • 5.02 Light and Sound
      • 5.03 Light and Sound II
    • Projects 6 >
      • 6.01 EEPROM
      • 6.02 I2C Address Scan
      • 6.03 Read the I2C Temperature Sensor
      • 6.04 High Temperature Alarm
    • Projects 7 >
      • 7.01 Arno Phone Home
      • 7.02 Keyboard Alphabet
      • 7.03 Move Mouse
      • 7.04 Draw Squares
    • Special Projects >
      • Bike Light Demo
  • References
    • Arno Pin Key
    • Arno Schematic
    • Project Index

Project 2.04 Ski Game

In this project, we’re going to use the serial monitor to play an interactive game.  The sketch works best with the Arduino serial monitor so you might install the Arduino IDE if you haven't already.

The game is based on one that appeared in a programming magazine in the 1980s.  Like other young programmers, I meticulously entered the program printed in the magazine into my Apple II+ computer.  The basic idea is to keep the skis within the poles.  The skis are represented by || and each pole is represented by !.  Press SW1 to move left and SW2 to move right.  Take a look at the code and we’ll explain how it works afterwards.

Concepts:  Serial monitor, char array, array index

Circuits: 
  • Circuit 2


  • Select Sketch

    ///////////////////////////////////////////////////
    //Project 2.04 Serial Ski Game
    char poles[] = ! !;
    long now = 1000;
    int newMove = 0;
    int oldMove = 0;
    int direct = 0;
    int steps = 0;
    int ski = 8;
    int SW1 = 1;
    int SW2 = 4;
    long sTime = 0;
    long howLong = 0;
    long skiSpeed;
    void setup(){
    Serial.begin(9600);
    pinMode(SW1,INPUT);
    pinMode(SW2,INPUT);
    }
    void loop(){
    newMove = random(5,40);
    steps = abs(newMove - oldMove)/(newMove - oldMove);
    while(oldMove != newMove){
    oldMove = oldMove + steps;
    for(int k = 0; k < oldMove; k++){
    Serial.print( );
    }
    //reposition poles
    poles[ski] = ;
    poles[ski + 1] = ;
    ski = ski - steps;
    if(digitalRead(SW1)==HIGH){
    ski = ski + 1;
    }
    if(digitalRead(SW2)==HIGH){
    ski = ski - 1;
    }
    ski = constrain(ski,1,16);
    poles[ski] = |;
    poles[ski+1] = |;
    Serial.println(poles);
    howLong = (millis() - sTime)/1000;
    skiSpeed = map(howLong,0,20,200,50);
    delay(skiSpeed);
    if(ski == 1 || ski ==16){
    Serial.println( BANG !!!!!);
    Serial.print( You skied for );
    Serial.print(howLong);
    Serial.println( seconds);
    int sec = 4;
    while(sec > 0){
    Serial.print( Start in );
    Serial.print(sec);
    Serial.println(...);
    delay(1000);
    sec = sec - 1;
    }
    poles[ski] = ;
    poles[ski + 1] = ;
    ski = 8;
    sTime = millis();
    } //end if for crash
    }
    }
    ///////////////////////////////////////////////////

    The ski track is represented by a character array that we print to the serial monitor:

    char poles[] = "!                 !";

    Our first problem is moving the poles back and forth.  We also want to change directions randomly so the player doesn’t know what to expect.  We set up the variable newMove at the beginning of the loop() block to randomly decide how far to move the poles.  It’s value determines how far the poles move before changing direction again:

    newMove = random(5,40);

    Now that we know where we want to go, we need to figure out how to get there.  In each cycle of the program, the poles will move one column left or right. The variable oldMove is the position of the poles before we start moving to the new position (it’s initially set to 0).  The variable steps takes a value of -1 if we need to move left or 1 if we need to move right: 

     steps = abs(newMove - oldMove)/(newMove - oldMove);

    We now enter a while  loop that we’ll stay in until the poles reach their new position.  The variable oldMove is incremented by the variable steps until it reaches newMove:

    while(oldMove != newMove){

       oldMove = oldMove + steps;

    The rest of the sketch runs within this while loop.  We only break out of it when the poles reach their new position so we can select a new random position.  Then it’s right back into the while loop.

    To actually position the poles, we print blank spaces to the serial monitor before printing poles.  This takes advantage of the fact that the Serial.print doesn’t start a new line each time it’s called.

       for(int k = 0; k < oldMove; k++){

         Serial.print(" ");

       }

    The variable oldMove is a way of remembering what our last move was so we know whether we need to move left or righ. Initially oldMove = 0.  The second line sets steps to -1 if we need to move left or 1 if we need to move right.

    Now we turn to the skier.  The variable ski holds the position of the skier within the poles.  We’re going to insert the characters || into the character array poles.  But first we need to get rid of the characters that were created during the last cycle of the loop:

       //reposition poles

       poles[ski] = ' ';

       poles[ski + 1] = ' '; 

    As the poles move, we want the skier to stay in place relative to the display. We move the player in the opposite direction that the poles move:

    ski = ski - steps;

    Now we look for user input to move the skier:

    if(digitalRead(SW1)==LOW){

         ski = ski - 1;

       }

       if(digitalRead(SW2)==LOW){

         ski = ski + 1;

       }

    We use the constrain function to make sure that the skier stays within the poles:

       ski = constrain(ski,1,16);

    We’re almost there!  Now let’s insert the skier back into the poles array and print it to the serial monitor:

       poles[ski] = '|';

       poles[ski+1] = '|';

       Serial.println(poles);

    All good games get more challenging as they go.  We make the game more challenging by speeding it up.  We accomplish this by comparing millis()  to the variable sTime, which records the value of millis() when a new game is started. The difference is recorded in seconds by the variable howLong. The map function keeps the delay between 200 and 50 milliseconds:

       howLong = (millis() - sTime)/1000;

       skiSpeed = map(howLong,0,20,200,50);

       delay(skiSpeed);

    The final problem is to provide feedback to the player when they don’t stay between the poles:

    if(ski == 1 || ski ==16){

         Serial.println("    BANG !!!!!");

         Serial.print("   You skied for ");

         Serial.print(howLong);

         Serial.println(" seconds");

    We count down four seconds before starting again:

         int sec = 4;

         while(sec > 0){

           Serial.print("   Start in ");

           Serial.print(sec);

           Serial.println("...");

           delay(1000);

           sec = sec - 1;

         }

    Before we can start again, we need to erase our current position and reset sTime so that we start slowly again:

         poles[ski] = ' ';

         poles[ski + 1] = ' ';

         ski = 8;

         sTime = millis();

          } //end if for crash

    Back to Projects 2

    Copyright Olympia Circuits LLC 2014. All Rights Reserved.