Saturday, November 13, 2010

Simon Says

In this first tutorial, I will explain how to create a basic and easy to implement version of the Simon Says game.
For this tutorial, an XMOS embedded device (XC-1A but it should also work on an XC-1) and an LCD screen will be used.
The command will appear on the screen as a letter and that letter corresponds to one of the buttons on the board.
The player starts with one command or character on the screen and will continue untill a predetermined value is reached or if the player enters a wrong command. This value is stored in the `cmdleft` value
The commands are stored in a variable named `cmd`. New commands consist of 2 bits to be able to use the 4 buttons. These 2 bits are appended to the previous command list.
Each time a command is added, the whole sequence is displayed on the screen and the corresponding button led flashes.

void simon(chanend tc){
 int count = 0;
 unsigned long cmd = 0x0;

 timer tmr;
 unsigned t;
 int cmdLeft = 5; // holds the value of the amount of commands have to be given to finish the game

 bled <: 0;

 tmr :> t;
 srand(t);

 while(cmdLeft > 0){
  cmd = addNewCommand(cmd);
  flashLed(cmd, ++count, tc);
  bled <: 0xff;
  tmr :> t;
  tmr when timerafter(t+FLASH_PERIOD) :> void;
  bled <: 0;
  ClearScreen(tc);
  cmdLeft--;
  if (buttonCheck(cmd, count, tc)){
   if (cmdLeft == 0)
    WinGame(tc);
  }
  else {
   LoseGame(tc);
   break;}
  ClearScreen(tc);
 };
}

 
The procedure that displays the commands (flashLed) and the one that checks the input (buttonCheck) the user gives are very similar. They basically reverse the command sequence and loop over this sequence 2 bits at a time. The 2 last bits are removed and either send it to the led to flash or check if the user presses the correct button. Below is the code for flashing the leds in the correct order. The code to check the user input can be found in the complete source code at the end of this post. Because the code that checks the user input is so similar, it has been left out here.
// flash the leds in the sequence of commands already given
void flashLed(unsigned long cmd, int count, chanend tc){
 int curr;
 timer tmr;
 unsigned t;
 cmd = rev(cmd, count);

 while(count > 0){
  curr = cmd - ((cmd >> 2) << 2);
  switch(curr){
  case 0:  // button A
   tc <: 'A';
   bled <: 1;
   break;
  case 1:  // button B
   tc <: 'B';
   bled <: 2;
   break;
  case 2:  // button C
   tc <: 'C';
   bled <: 4;
   break;
  case 3:  // button D
   tc <: 'D';
   bled <: 8;
   break;
  }
  cmd >>= 2;
  tmr :> t;
  tmr when timerafter(t+FLASH_PERIOD) :> void;
  bled <: 0;
  tmr :> t;
  tmr when timerafter(t+(FLASH_PERIOD/2)) :> void;
  count--;
 }
}

Because of the way the commands are stored, before the commands can be shown or checked, they must be reversed forst using a special reverse function that keeps the order of eacht 2 bits. For example, the sequence 11/01/10/11/11/01 becomes 01/11/11/10/01/11.
unsigned long rev2(unsigned long x, int count){
 unsigned long newx = 0;

 for(int c = count; c > 0; c--){
  newx = appendNbit(newx, lastNbit(x, 2), 2);
  x >>= 2;
 }

 return newx;
} 
The last procedure that will be discussed is the dispText procedure. This procedure uses a channel to receive text. This text is either a certain character to print on the screen or an internal command that has an extra effect on the game. For example, if the '&'-symbol is sent using the channel, the text `Congratulations, you won!` will appear on the screen.
// listen for character on the tc-channel and print it or perform action
void dispText(chanend tc){
 char c;
 int count = 0;
 while(1){
  tc :> c;
  if(count > 16){ // after 16 characters or newline character
   lcd_cmd(0x80 | 0x40); // move cursor to second line
   count = 0;}
  else
   switch(c){
   case '&':
    lcd_clear();
    lcd_text("Congratulations, you won!");
    return;
   case'*':
    lcd_clear();
    lcd_text("Sorry, you lose!");
    return;
   case '!':
    count = 0;
    lcd_clear();
    break;
   default:
    lcd_data(c);
    count++;
    break;
   }
 }
}

Here is a video of how it should look when you compile the code and run it on the device. The first part of the video shows a game that is won by the player. In the second part, the player makes a mistake and the game ends displaying a message.
Here you can find the complete source code of this small example. This includes all the other, trivial procedures that aren't described here and the library to display text on the screen.

No comments:

Post a Comment