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:
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:
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
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