Arcade Machine

Discussion specific to projects ideas and support.

Arcade Machine

Postby haydenstudios » 19 Sep 2013, 19:16

Hey guys. My LEGO MINDSTORMS building career is about to come to an end. I've been getting older, getting busier, gaining other hobbies, and simply haven't been able to set aside as much time for LEGO MINDSTORMS as I used to. It is for this reason that the project you see here will likely be my last one. It is my Arcade Machine.
Image
This machine consists of:
  • 2 NXTs
  • 1 RCX
  • 2 NXT motors
  • 1 RCX motor
  • 2 NXT touch sensors
  • 3 RCX touch sensors
  • 2 RCX rotation sensors
  • 1 NXT light sensor
  • 1 LEGO NXT color sensor
  • 1 lamp

The machine plays pong. You can control the paddle via the wheels on the front and back of the machine. You can either play a 1-player game against the computer on varying difficulties, or you can play a 2-player game in which case both wheels are used. The 2 NXTs communicate via RS485. To play, you first have to pay. If you want a 1-player game, you must pay 25 cents. If you want a 2-player game you must pay 50 cents. The machine recognizes all four types of American coins. When the coins first go in, they are kept in a temporary holding area. From there, they can go in 2 different directions. If the user hasn't fully paid yet and wants a refund, then the money gets sent down a ramp to get refunded. If the user fully pays, then it gets dumped into a place for more long-term storage with the rest of the coins. The coin container can be accessed by pressing some RCX touch sensors hidden behind technic holes in a certain combination which will cause a hidden hatch to open. The Master NXT program consists of 852 lines of NXC, the Slave NXT program 156 lines, and the RCX program is 49 blocks of RCX code (or 169 lines of MindScript, if you prefer).

Here is a bundle of all the files you'll need to download the programs:
Programs.zip
The programs, as well as the necessary images files, and a text file that should be downloaded onto the master NXT.
(10.66 KiB) Downloaded 190 times


The source code to the programs are posted below if you'd like to view it here instead of download the attachment:
The Master NXT program:
Code: Select all
byte Rx_Buffer[], fileHandle, ASCIIPos, coin = 4;
short bytesWritten;
string detectedCoin = " ", cent;
string SBallX, SBallY, SPosition1, Sp1score, Sp2score, sBallTime, sTotalTime, scores, scoresp1, scoresp2, current = "", name, currentCharacter;
string ln1, ptime, pscore1, pscore2, pscore3, pscore4, pscore5, begin, open, end, tempName, tempScore;
bool check = 0;
bool Determined = 0, Players = 0, survival = 0, setball = 0, gameover = 0;
bool ballhor, ballvert, capsLock = 1;
int defaultLight, degrees = 0, timer3, centsToPay = 25;
int fSize, totalTime, p1score, p2score, p3score = 0, p4score = 0, p5score = 0, BallX, BallY, activateball = 0, Position1 = 0;
int strLen_score, strLen_name, strLen_ptime, plyrScore;
int difficultyLevel, ballspeed, rside1, rside2, lside1, lside2, number, number2, survpos, timer1, scoreSpot;
int characterPos = 0, character, allowedCharacters;
long offset1 = 0, offset2 = 0, offset3 = 0, ballTime, timer2;
float Position2 = 37, Difficulty = 1;

inline void WaitForMessageToBeSent()
{
  while(RS485SendingData())
    Wait(3);
}

sub Deposit()      //The subroutine for depositing coins in the container
{
  OnFwd(OUT_B, 30);
  Wait(1750);
  Off(OUT_B);
  Wait(100);
  RotateMotorEx(OUT_B, 30, -28, 0, false, true);
  Wait(1000);
}

int CalcPosition(int SensorNew) //Make some calculations necessary for using the rotation sensor value to its fullest
{
  int SensorOld;
  SensorOld = SensorNew;
  SensorNew = SENSOR_2;
  SensorNew -= SensorOld;
  return SensorNew;
}

string ParseScoreText(string scores, string beginning, string bracketOpen, string bracketClose, string pname, string pscore, string ptime, int strLen_name, int strLen_score, int strLen_ptime, int plyrScore)
{                  //Assemble the necessary text for displaying the high scores
  if(beginning == "null")
    pname = SubStr(scores, 0, Pos(bracketOpen, scores));
  else
    pname = SubStr(scores, Pos(beginning, scores) + 1, Pos(bracketOpen, scores) - Pos(beginning, scores) - 1);
  strLen_name = StrLen(pname);
  strLen_score = StrLen(SubStr(scores, Pos(bracketOpen, scores) + 1, Pos(bracketClose, scores) - (Pos(bracketOpen, scores) + 1)));
  pscore = SubStr(scores, Pos(bracketOpen, scores) + 1, Pos(bracketClose, scores) - Pos(bracketOpen, scores) - 1);
  plyrScore = StrToNum(pscore);
  if(plyrScore % 60 >= 10)
    ptime = StrCat(NumToStr(plyrScore / 60), ":", NumToStr(plyrScore % 60));
  else
    ptime = StrCat(NumToStr(plyrScore / 60), ":0", NumToStr(plyrScore % 60));
  strLen_ptime = StrLen(ptime);
  repeat(14 - (strLen_name + strLen_ptime))
    pname += " ";
  pname += ptime;
  return pname;
}

sub Victory()  //Play the tune "Victory!" from Cave Story
{
  PlayToneEx(262, 96, 4, false);
  Wait(96);
  PlayToneEx(330, 96, 4, false);
  Wait(96);
  PlayToneEx(392, 96, 4, false);
  Wait(96);
  PlayToneEx(494, 96, 4, false);
  Wait(96);
  PlayToneEx(523, 96, 4, false);
  Wait(96);
  PlayToneEx(659, 96, 4, false);
  Wait(96);
  PlayToneEx(784, 96, 4, false);
  Wait(96);
  PlayToneEx(988, 96, 4, false);
  Wait(96);
  PlayToneEx(1046, 192, 4, false);
  Wait(192);
  PlayToneEx(1046, 96, 2, false);
  Wait(96);
  PlayToneEx(1046, 90, 4, false);
  Wait(96);
  PlayToneEx(1046, 96, 4, false);
  Wait(96);
  PlayToneEx(1046, 96, 2, false);
  Wait(96);
  repeat (3)
  {
    PlayToneEx(880, 96, 4, false);
    Wait(96);
    PlayToneEx(880, 96, 2, false);
    Wait(96);
  }
  PlayToneEx(988, 864, 4, false);
  Wait(864);
  PlayToneEx(988, 96, 2, false);
  Wait(92);
}

sub GetHeartTank()      //Play the tune "Get Heart Tank!" from Cave Story
{
  PlayToneEx(220, 120, 3, false);
  Wait(120);
  PlayToneEx(220, 120, 2, false);
  Wait(120);
  PlayToneEx(277, 120, 3, false);
  Wait(120);
  PlayToneEx(277, 120, 2, false);
  Wait(120);
  PlayToneEx(330, 120, 4, false);
  Wait(120);
  PlayToneEx(330, 120, 2, false);
  Wait(120);
  PlayToneEx(277, 120, 3, false);
  Wait(120);
  PlayToneEx(277, 120, 2, false);
  Wait(120);
  PlayToneEx(330, 120, 3, false);
  Wait(120);
  PlayToneEx(330, 120, 2, false);
  Wait(120);
  PlayToneEx(415, 120, 4, false);
  Wait(120);
  PlayToneEx(415, 120, 2, false);
  Wait(120);
  PlayToneEx(370, 120, 3, false);
  Wait(120);
  PlayToneEx(370, 120, 2, false);
  Wait(120);
  PlayToneEx(311, 120, 3, false);
  Wait(120);
  PlayToneEx(311, 120, 2, false);
  Wait(120);
  PlayToneEx(247, 120, 3, false);
  Wait(120);
  PlayToneEx(247, 120, 2, false);
  Wait(120);
  PlayToneEx(210, 120, 3, false);
  Wait(120);
  PlayToneEx(210, 120, 2, false);
  Wait(120);
  PlayToneEx(247, 120, 4, false);
  Wait(120);
  PlayToneEx(247, 120, 2, false);
  Wait(120);
  PlayToneEx(311, 120, 4, false);
  Wait(120);
  PlayToneEx(311, 120, 2, false);
  Wait(120);
  PlayToneEx(277, 1440, 2, false);
  Wait(1440);
}

sub ShowPongStats()   //Display the current stats of the game
{
  if(!survival)   //If this is a two player game or if it's not in survival mode,
  {
    NumOut(49, LCD_LINE7, p1score);     //then display the scores
    NumOut(49, LCD_LINE2, p2score);
  }
  else           //If it's in survival mode,
  {
    if(StrLen(NumToStr(ballTime % 60)) == 1)
      sBallTime = NumToStr(ballTime / 60) + ":0" + NumToStr(ballTime % 60);
    else                                                               //Calculate how long the previous bout lasted,
      sBallTime = NumToStr(ballTime / 60) + ":" + NumToStr(ballTime % 60);
    if(StrLen(NumToStr(totalTime % 60)) == 1)
      sTotalTime = NumToStr(totalTime / 60) + ":0" + NumToStr(totalTime % 60);
    else                                                              //and the sum of the bouts so far,
      sTotalTime = NumToStr(totalTime / 60) + ":" + NumToStr(totalTime % 60);
    TextOut(14, LCD_LINE6, "Bout" + NumToStr(p2score) + ": " + sBallTime);    //and display them
    TextOut(14, LCD_LINE7, "Total: " + sTotalTime);
  }
}

sub BouncePaddle(int lside, int rside) //This subroutine is triggered if the ball reaches the area of a paddle along the y axis
{
  if((BallX >= lside) && (BallX <= rside)) //If a paddle is above or beneath the ball, then do the following:
  {
    if(((BallX == lside || BallX == lside + 1) && ballhor) || ((BallX == rside || BallX == rside - 1) && !ballhor))
      ballhor = !ballhor;  //If it bounces on a corner, then change the ball's horizontal direction,
    ballvert = !ballvert;  //in addition to the changing of the vertical direction,
    PlayTone(550, 100);    //and playing a beep to indicate a collision and bounce
  }
}

sub playpong()
{
  totalTime = 0;
  BallX = 49;
  BallY = 29;
  p1score = 0;
  p2score = 0;
  Position1 = 38;    //Set variables to their default positions at the beginning of the game
  Determined = 1;
  int SensorNew, Middle;
  if(Players == 0)   //If this is a 1-player game,
  {
    Position2 = 30;               //then make some calculations
    difficultyLevel = Difficulty; //that will help the opponent behave based on
    Difficulty /= 10;             //the difficulty setting chosen by the player
  }
  else              //If this is a 2-player game,
  {
    Difficulty = 2;
    SendRS485Number(1);          //then establish communication with the other player
    WaitForMessageToBeSent();
  }
  while (gameover == 0) //Mantain this loop until the game is over
  {
    while(SENSOR_1 == 0) //Maintain this loop until the ball has been activated
    {
      if(difficultyLevel == 2)
        ballspeed = 70;
      else
      {                               //Begin the ball at different speeds based on the difficulaty level
        if(difficultyLevel == 3)
          ballspeed = 65;
        else
        {
          ballspeed = 75;
          if(difficultyLevel == 4)
            survival = true;
        }
      }
      SensorNew = SENSOR_2;
      if(Players == 1)            //If this is a 2-player game,
      {
        SPosition1 = "p" + NumToStr(Position1);   //Then send the position of your paddle,
        SendRS485String(SPosition1);
        WaitForMessageToBeSent();
        SBallX = "x" + NumToStr(BallX);           //the x position of the ball,
        SendRS485String(SBallX);
        WaitForMessageToBeSent();
        SBallY = "y" + NumToStr(BallY);           //and the y position of the ball,
        SendRS485String(SBallY);
        WaitForMessageToBeSent();
        until(RS485DataAvailable());
        RS485Read(Rx_Buffer);
        Position2 = Rx_Buffer[0];                 //in addition to receiving the position of the player's paddle
      }
      ClearScreen();
      GraphicOut(Position1, 0, "Tray.ric");
      GraphicOut(Position2, 60, "Tray.ric");     //Display the ball and paddles at their appropriate position,
      GraphicOut(BallX, BallY, "Ball.ric");
      ShowPongStats();                           //and show their stats
      Position1 += CalcPosition(SensorNew);      //see if the rotation sensor value has changed, and make necessary calculations
      if(Position1 < 0)
        Position1 = 0;            //Keep the paddle from going off the screen
      if(Position1 > 76)
        Position1 = 76;
    } //End this loop now that the ball has been activated
    activateball = 1;  //Notify the ball task to start calculating the ball's position
    offset2 = CurrentTick();  //Reset timer 2
    while(activateball == 1)  //Mantain this loop until the ball gets past one of the paddles
    {
      SensorNew = SENSOR_2;
      if(Players == 1)
      {
        SPosition1 = "p" + NumToStr(Position1);   //Then send the position of your paddle,
        SendRS485String(SPosition1);
        WaitForMessageToBeSent();
        SBallX = "x" + NumToStr(BallX);           //the x position of the ball,
        SendRS485String(SBallX);
        WaitForMessageToBeSent();
        SBallY = "y" + NumToStr(BallY);           //and the y position of the ball,
        SendRS485String(SBallY);
        WaitForMessageToBeSent();
        until(RS485DataAvailable());
        RS485Read(Rx_Buffer);
        Position2 = Rx_Buffer[0];                 //in addition to receiving the position of the player's paddle
      }
      ClearScreen();
      GraphicOut(Position1, 0, "Tray.ric");
      GraphicOut(Position2, 60, "Tray.ric");     //Display the ball and paddles at their appropriate position,
      GraphicOut(BallX, BallY, "Ball.ric");
      ShowPongStats();                           //and show their stats
      Position1 += CalcPosition(SensorNew);      //see if the rotation sensor value has changed, and make necessary calculations
      if(Position1 < 0)
        Position1 = 0;            //Keep the paddle from going off the screen
      if(Position1 > 76)
        Position1 = 76;
      rside1 = Position1 + 24;
      lside1 = Position1 - 3;     //Calculate the positions of the corners of the paddles
      rside2 = Position2 + 24;
      lside2 = Position2 - 3;
      if(Players == 0)  //If this is a 1-player game,
      {
        Middle = Position2 + 12;
        survpos = BallX - 12;
        if(BallX < Middle)  //and if the ball is to the left of the center of the cpu's paddle along the x axis,
        {
          if(!survival) //if this isn't survival mode,
            Position2 -= Difficulty;  //then make the computer player respond at different speeds based on the difficulty set by the player
          else          //if this is survival mode,
            Position2 = survpos;      //then set the computer player's paddle's position such that the ball is in the center along the x axis
        }
        if(BallX > Middle)  //and if the ball is to the right of the center of the cpu's paddle along the x axis,
        {
          if(!survival)
            Position2 += Difficulty;  //then make the computer player respond at different speeds based on the difficulty set by the player
          else
            Position2 = survpos;      //then set the computer player's paddle's position such that the ball is in the center along the x axis
        }
        if(Position2 < 0)
          Position2 = 0;       //Keep the paddle from going off the screen
        if(Position2 > 76)
          Position2 = 76;
      }
    }  //End the loop, as the ball has gotten past one of the paddles
    ballTime = timer2 / 1000;
    totalTime += ballTime;        //Get the value for the amount of time that ball took
    GraphicOut(Position1, 0, "Tray.ric");
    GraphicOut(BallX, BallY, "Ball.ric");
    GraphicOut(Position2, 60, "Tray.ric");
    ShowPongStats();
    PlayTone(349, 500);
    if(BallY <= 0)
    {
      SendRS485String("t");
      WaitForMessageToBeSent();
    }
    if(BallY >= 60)                           //Send the message of who scored to the other NXT
    {
      SendRS485String("o");
      WaitForMessageToBeSent();
    }
    Wait(500);
    if(p1score >= 5 || p2score >= 5)         //Break out of the loop if one of the players has reached 5 points
      break;
    BallX = 49;
    BallY = 29;              //If the game's not over yet, set the ball to its default position for the next round
  }  //End the loop if one of the players has reached five points
  ClearScreen();
  Wait(1250);
  if(!survival)   //If this wasn't a survival game,
  {
    if(p1score > p2score)
    {
      TextOut(25, LCD_LINE2, "You win!");
      Victory();
    }
    else                           //tell the player whether he or she won or lost
    {
      TextOut(25, LCD_LINE2, "You lose");
      Wait(2880);
    }
    TextOut(30, LCD_LINE5, "SCORES:");              //Then show the scores
    TextOut(20, LCD_LINE7, "Player 1: " + NumToStr(p1score));
    if(Players == 0)
      TextOut(20, LCD_LINE8, "Computer: " + NumToStr(p2score));
    else
      TextOut(20, LCD_LINE8, "Player 2: " + NumToStr(p2score));
    Wait(1250);
  }
  else    //If this was a survival game,
  {
    TextOut(24, LCD_LINE2, "Game over");     //tell the player the game has ended,
    Wait(2880);
    TextOut(12, LCD_LINE5, "You survived:");
    if(StrLen(NumToStr(totalTime % 60)) == 1)       //then tell him or her how long he or she survived
      sTotalTime = NumToStr(totalTime / 60) + ":0" + NumToStr(totalTime % 60);
    else
      sTotalTime = NumToStr(totalTime / 60) + ":" + NumToStr(totalTime % 60);
    TextOut(36, LCD_LINE7, sTotalTime);
    Wait(1250);
  }
  until(SENSOR_1 == 1);
  until(SENSOR_1 == 0);    //Wait for the player to press and release the touch sensor
  if(survival)   //If this was a survival game,
  {
    scoreSpot = 0;
    OpenFileRead("HighScores.txt", fSize, fileHandle);
    ReadLnString(fileHandle, scores);                    //Then read the high scores text file,
    CloseFile(fileHandle);
    p1score = StrToNum(SubStr(scores, Pos("/", scores) + 1, Pos("\\", scores) - Pos("/", scores) - 1));
    if(totalTime > p1score)    //see if the player made the high score list,
    {
      scoreSpot = 5;          //and sort it into the right spot
      p2score = StrToNum(SubStr(scores, Pos("<", scores) + 1, Pos(">", scores) - Pos("<", scores) - 1));
      if(totalTime > p2score)
        scoreSpot--;
      p3score = StrToNum(SubStr(scores, Pos("{", scores) + 1, Pos("}", scores) - Pos("{", scores) - 1));
      if(totalTime > p3score)
        scoreSpot--;
      p4score = StrToNum(SubStr(scores, Pos("[", scores) + 1, Pos("]", scores) - Pos("[", scores) - 1));
      if(totalTime > p4score)
        scoreSpot--;
      p5score = StrToNum(SubStr(scores, Pos("(", scores) + 1, Pos(")", scores) - Pos("(", scores) - 1));
      if(totalTime > p5score)
        scoreSpot--;
      ClearScreen();
      if(totalTime >= 600)
        allowedCharacters = 9;
      else
        allowedCharacters = 10;
      TextOut(3, LCD_LINE1, "Enter your name:");
      TextOut(0, LCD_LINE3, "Caps Lock is ON");
      TextOut(0, LCD_LINE4, "Next: A");
      TextOut(0, LCD_LINE6, "_");
      TextOut(9, LCD_LINE8, "Chars left: " + NumToStr(allowedCharacters));
      PlayTone(610,140);
      Wait(220);
      PlayTone(511,100);
      Wait(180);
      PlayTone(610,140);
      Wait(140);
      ClearSensor(IN_2);
      while(true)         //Allow the player to enter his or her name if he or she got a decent score
      {
        while(true)
        {
          if(characterPos >= 0 && characterPos <= 25)   //If the option the user is about to select is a letter of the alphabet...
          {
            if(capsLock)  //If Caps Lock is on...
              ASCIIPos = characterPos + 65;   //Make the letter uppercase
            else
              ASCIIPos = characterPos + 97;   //Make the letter lowercase
            currentCharacter = FlattenVar(ASCIIPos);          //Make the current character the flattened raw ASCII value of teh character pos
          }
          if(characterPos == 26)      //If we want to press space...
            currentCharacter = " ";   //Make the next character a space
          TextOut(3, LCD_LINE1, "Enter your name:");
          if(capsLock)                                //If Caps Lock is on...
            TextOut(0, LCD_LINE3, "Caps Lock is ON"); //Say that Caps Lock is on
          else                                        //If Caps Lock is NOT on...
            TextOut(0, LCD_LINE3, "Caps Lock is OFF");//Say that Caps Lock is off
          if(characterPos >= 0 && characterPos <= 25) //If the character position is between 0 and 25...
            TextOut(0, LCD_LINE4, "Next: " + currentCharacter);  //Then make the next character a letter of the alphabet
          if(characterPos == 26)                      //If the character position is 26...
            TextOut(0, LCD_LINE4, "Next: SPACE");     //Then make the next character a space
          if(characterPos == 27)                      //If the character position is 27...
            TextOut(0, LCD_LINE4, "Next: BACKSPACE"); //Then make the next character a backspace
          if(characterPos == 28)                      //If the character position is 28...
            TextOut(0, LCD_LINE4, "Next: CAPS LOCK"); //Then toggle Caps Lock
          if(characterPos == 29)                      //If the character position is 29...
            TextOut(0, LCD_LINE4, "Next: DONE");      //Then submit
          if((allowedCharacters - StrLen(current)) > 0)
            TextOut(0, LCD_LINE6, current + "_");       //*Show what has been typed so far
          else
            TextOut(0, LCD_LINE6, current);
          TextOut(9, LCD_LINE8, "Chars left: " + NumToStr(allowedCharacters - StrLen(current)));     //Show how many characters are left (not finished with this line of code yet; what is displayed is only temporary)
          Wait(30); ClearScreen();
          if((allowedCharacters - StrLen(current)) > 0)
          {
            if(SENSOR_2 < -8)            //If the rotation sensor gets moved to the left...
            {
              ClearSensor(IN_2);         //Reset the rotation sensor,
              if(characterPos == 0)
                characterPos = 29;       //and set the character position to 29 if it's 0,
              else
                characterPos--;          //and decrement the character position if it's not equal to 0
            }
            if(SENSOR_2 > 8)             //If the rotation sensor gets moved to the right...
            {
              ClearSensor(IN_2);         //Reset the rotation sensor,
              if(characterPos == 29)
                characterPos = 0;        //and set the character position to 0 if it's 29,
              else
                characterPos++;          //and increment the character position if it's not equal to 29
            }                            //Essentially, the above 15 lines of code keep the character position between 0 and 29
          }
          else //If there are no remaining characters left,
          {
            if(SENSOR_2 < -8 || SENSOR_2 > 8)  //then allow the only options to be
            {
              if(characterPos == 27)         //"DONE", and
                characterPos = 29;
              else                           //"BACKSPACE"
                characterPos = 27;
              ClearSensor(IN_2);
            }
          }
          if(SENSOR_1 == 1)
          {
            until(SENSOR_1 == 0);
            break;              //End the loop when the touch sensor has been pressed and released
          }
        }  //Once the loop has ended...
        if(characterPos == 29)
          break;   //End the more outer loop when the player has selected "DONE"
        if(characterPos >= 0 && characterPos <= 26)     //If the user's selection is a letter of the alphabet, or a space, then...
        {
          current = StrCat(current, currentCharacter); //Adds the selected character onto what you're typing
          if((allowedCharacters - StrLen(current)) < 1)
            characterPos = 29;
        }
        if(characterPos == 27)
          current = SubStr(current, 0, StrLen(current)-1);
        if(characterPos == 28)    //If the user has selected the Caps Lock option...
          capsLock = !capsLock;   //Then toggle Caps Lock
      }
      name = current;
      if(scoreSpot < 5)  //Sort rhe player's score onto the list based on how high it is
      {
        tempName = SubStr(scores, Pos("}", scores) + 1, Pos("<", scores) - Pos("}", scores) - 1);
        tempScore = SubStr(scores, Pos("<", scores) + 1, Pos(">", scores) - Pos("<", scores) - 1);
        scores = StrCat(SubStr(scores, 0, Pos(">", scores) + 1), tempName, "/", tempScore, "\\");
        if(scoreSpot < 4)
        {
          tempName = SubStr(scores, Pos("]", scores) + 1, Pos("{", scores) - Pos("]", scores) - 1);
          tempScore = SubStr(scores, Pos("{", scores) + 1, Pos("}", scores) - Pos("{", scores) - 1);
          scores = StrCat(SubStr(scores, 0, Pos("}", scores) + 1), tempName, "<", tempScore, ">", SubStr(scores, Pos(">", scores) + 1, StrLen(scores)));
          if(scoreSpot < 3)
          {
            tempName = SubStr(scores, Pos(")", scores) + 1, Pos("[", scores) - Pos(")", scores) - 1);
            tempScore = SubStr(scores, Pos("[", scores) + 1, Pos("]", scores) - Pos("[", scores) - 1);
            scores = StrCat(SubStr(scores, 0, Pos("]", scores) + 1), tempName, "{", tempScore, "}", SubStr(scores, Pos("}", scores) + 1, StrLen(scores)));
            if(scoreSpot < 2)
            {
              tempName = SubStr(scores, 0, Pos("(", scores));
              tempScore = SubStr(scores, Pos("(", scores) + 1, Pos(")", scores) - Pos("(", scores) - 1);
              scores = StrCat(SubStr(scores, 0, Pos(")", scores) + 1), tempName, "[", tempScore, "]", SubStr(scores, Pos("]", scores) + 1, StrLen(scores)));
            }
          }
        }
      }
      if(scoreSpot == 1)
      {
        begin = "null";
        open = "(";
        end = ")";
        scoresp2 = SubStr(scores, Pos(")", scores) + 1, StrLen(scores));
        scores = StrCat(name, open, NumToStr(totalTime), end, scoresp2);
      }
      else
      {
        if(scoreSpot == 5)
        {
          scoresp1 = SubStr(scores, 0, Pos(">", scores) + 1);
          scores = StrCat(scoresp1, name, "/", NumToStr(totalTime), "\\");
        }
        else
        {
          if(scoreSpot == 2)
          {
            begin = ")";
            open = "[";
            end = "]";
          }
          if(scoreSpot == 3)
          {
            begin = "]";
            open = "{";
            end = "}";
          }
          if(scoreSpot == 4)
          {
            begin = "}";
            open = "<";
            end = ">";
          }
          scoresp1 = SubStr(scores, 0, Pos(begin, scores) + 1);
          scoresp2 = SubStr(scores, Pos(end, scores) + 1, StrLen(scores));
          scores = StrCat(scoresp1, name, open, NumToStr(totalTime), end, scoresp2);
        }
      }
      DeleteFile("HighScores.txt");
      CreateFile("HighScores.txt", 512, fileHandle);
      WriteLnString(fileHandle, scores, bytesWritten);
      CloseFile(fileHandle);
    }
    pscore1 = ParseScoreText(scores, "null", "(", ")", " ", " ", " ", 0, 0, 0, 0);
    pscore2 = ParseScoreText(scores, ")", "[", "]", " ", " ", " ", 0, 0, 0, 0);
    pscore3 = ParseScoreText(scores, "]", "{", "}", " ", " ", " ", 0, 0, 0, 0);    //Parse the text fot the scores
    pscore4 = ParseScoreText(scores, "}", "<", ">", " ", " ", " ", 0, 0, 0, 0);
    pscore5 = ParseScoreText(scores, ">", "/", "\\", " ", " ", " ", 0, 0, 0, 0);
    TextOut(32, LCD_LINE1, "-PONG-");
    TextOut(14, LCD_LINE2, "High Scores:");
    TextOut(0, LCD_LINE4, "1." + pscore1);
    TextOut(0, LCD_LINE5, "2." + pscore2);
    TextOut(0, LCD_LINE6, "3." + pscore3);     //Display the scores
    TextOut(0, LCD_LINE7, "4." + pscore4);
    TextOut(0, LCD_LINE8, "5." + pscore5);
    if(scoreSpot == 1)   //If the player got the top score, then play a victory jingle
      Victory();
    if(scoreSpot > 1)    //If the player didn't get the top score, then play a moderately happy jingle
      GetHeartTank();
    until(SENSOR_1);
    until(!SENSOR_1);    //Wait for the user to press and release the touch sensor
  }
}

task calctimer()
{
  while(true)
  {
    timer1 = CurrentTick() - offset1;
    timer2 = CurrentTick() - offset2;
    timer3 = CurrentTick() - offset3;
  }
}

task checkCoins()
{
  while(true)
  {
    until(check);   //Proceed if the machine is expecting coins
    until(SENSOR_3 < defaultLight-2 || SENSOR_3 > defaultLight+2); //Wait until a coin has been detected
      degrees = MotorTachoCount(OUT_A);  //See how long it took for the coin to get through
      PlayTone(3000, 250);
      ClearScreen();
      TextOut(9, LCD_LINE3, "Please insert");
      TextOut(34, LCD_LINE4, "coins");
      if(centsToPay == 1)
        cent = "cent";
      else
        cent = "cents";
      TextOut(6, LCD_LINE7, NumToStr(centsToPay) + " " + cent + " to pay");
      Off(OUT_A);
      if(degrees < 520)   //If it took less than 520 degrees,
      {
        centsToPay -= 10;
        coin = 0;         //the coin is a dime
      }
      else
      {
        if(degrees < 588)  //If it took more than 520 degrees but less than 588,
        {
          centsToPay--;
          coin = 1;        //the coin is a penny
        }
        else
        {
          if(degrees < 780)  //If it took more than 588 degrees but less than 780,
          {
            centsToPay -= 5;
            coin = 2;        //the coin is a nickel
          }
          else               //If it took more than 780 degrees,
          {
            centsToPay -= 25;
            coin = 3;       //the coin is a quarter
          }
        }
      }
    Wait(300);
  }
}

task secondary()
{
  int Arrowpos = 22, SensorNew;
  UseRS485();   // make sure the RS485 system is turned on
  RS485Enable();
  RS485Uart(HS_BAUD_DEFAULT, HS_MODE_DEFAULT);
  Wait(1);
  SendRS485Number(1);        //Establish connection with the other NXT
  WaitForMessageToBeSent();
  defaultLight = SENSOR_3;
  while(true)
  {
    while(true)
    {
      SensorNew = SENSOR_2;
      TextOut(32, LCD_LINE1, "-PONG-");
      TextOut(0, LCD_LINE2, "Select number of");
      TextOut(28, LCD_LINE3, "players");
      TextOut(25, LCD_LINE5, "1 Player");
      TextOut(25, LCD_LINE6, "2 Players");
      TextOut(18, Arrowpos, ">");
      Wait(30); ClearScreen();
      Position1 += CalcPosition(SensorNew);
      if(Position1 < 0)
        Position1 = 0;                         //Let the user select how many players he or she wants
      if(Position1 > 21)
        Position1 = 21;
      if((Position1 >= 0) && (Position1 <= 10))
      {
        Players = 0;
        Arrowpos = 29;
      }
      if((Position1 >= 11) && (Position1 <= 21))
      {
        Players = 1;
        Arrowpos = 22;
      }
      if(SENSOR_1 == 1)
      {
        until(SENSOR_1 == 0);
        break;
      }
    }
    if(Players == 0)      //If it's one player,
      centsToPay = 25;    //make the user pay 25 cents
    else                  //If it's two players,
      centsToPay = 50;    //make the user pay 50 cents
    check = true;      //Start checking for coins
    while(centsToPay > 0) //Mantain this loop as long as the user has not payed fully
    {
      ClearScreen();
      TextOut(9, LCD_LINE3, "Please insert");
      TextOut(34, LCD_LINE4, "coins");
      if(centsToPay == 1)
        cent = "cent";
      else
        cent = "cents";
      TextOut(6, LCD_LINE7, NumToStr(centsToPay) + " " + cent + " to pay");
      until(SENSOR_1 || RS485DataAvailable()); //Wait for the other NXT to register a coin first
      RS485Read(Rx_Buffer);
      if(!SENSOR_1)
      {
        offset3 = CurrentTick();
        RotateMotorEx(OUT_A, 30, 1050, 0, false, true);
        Wait(250);
        if(coin == 0)
          RotateMotorEx(OUT_A, 60, -677, 0, false, true);
        if(coin == 1)
          RotateMotorEx(OUT_A, 60, -750, 0, false, true);
        if(coin == 2)
          RotateMotorEx(OUT_A, 60, -965, 0, false, true);
        if(coin == 3)
          RotateMotorEx(OUT_A, 60, -1034, 0, false, true);
        if(coin == 4)
          RotateMotorEx(OUT_A, 60, -1200, 0, false, true);
        ResetTachoCount(OUT_A);
        coin = 4;
      }
      else   //If the user wants a refund,
      {
        OnRev(OUT_B, 30);
        OnFwd(OUT_C, 100);
        Wait(1750);
        Off(OUT_B);                  //Then send the money back,
        Wait(100);
        RotateMotorEx(OUT_B, 30, 28, 0, false, true);
        Wait(1000);
        Off(OUT_C);
        if(Players == 0)
          centsToPay = 25;         //and reset the amount of money to be paid
        else
          centsToPay = 50;
      }
    }   //End this loop when the game has been payed for
    Deposit();   //Permanently deposit the money
    check = false;   //Stop checking for coins now that it's been paid
    if(Players == 1)    //If this is a 2-player game, then get the other NXT ready
    {
      SendRS485String("br");
      WaitForMessageToBeSent();
    }
    else
    {
      Arrowpos = 15; Position1 = 0; SensorNew = 0;
      while(true)
      {
        SensorNew = SENSOR_2;
        TextOut(32, LCD_LINE1, "-PONG-");
        TextOut(32, LCD_LINE2, "Select");
        TextOut(0, LCD_LINE3, "difficulty level");
        TextOut(25, LCD_LINE5, "Easy");
        TextOut(25, LCD_LINE6, "Medium");
        TextOut(25, LCD_LINE7, "Hard");
        TextOut(25, LCD_LINE8, "Survival");     //If this is a 1-player game, then let the user select the difficulty
        TextOut(18, Arrowpos, ">");
        Wait(30); ClearScreen();
        Position1 += CalcPosition(SensorNew);
        if(Position1 < 0)
          Position1 = 0;
        if(Position1 > 43)
          Position1 = 31;
        if((Position1 >= 0) && (Position1 <= 10))
        {
          Difficulty = 1;
          Arrowpos = 30;
        }
        if((Position1 >= 11) && (Position1 <= 21))
        {
          Difficulty = 2;
          Arrowpos = 22;
        }
        if((Position1 >= 22) && (Position1 <= 32))
        {
          Difficulty = 3;
          Arrowpos = 15;
        }
        if((Position1 >= 33) && (Position1 <= 43))
        {
          Difficulty = 4;
          Arrowpos = 0;
        }
        if(SENSOR_1 == 1)
        {
          until(SENSOR_1 == 0);
          break;
        }
      }
    }
    playpong();
  }
}

task ball()
{
  while(gameover == 0) //Proceed only if a game is going
  {
    until(activateball == 1);   //Wait until the ball has been set loose,
    ballhor = 1;
    ballvert = 0;               //then set the initial directions
    while(activateball == 1)  //Maintain this loop while a round is still going
    {
      if(ballvert)
        BallY += 1;  //If the ball's vertical direction is up, then move up
      else
        BallY -= 1;  //If the ball's vertical direction is down, then move down
      if(ballhor)
        BallX += 1;  //If the ball's horizontal direction is right, then move right
      else
        BallX -= 1;  //If the ball's horizontal direction is left, then move left
      if(BallY <= 4) //If the ball is entering player 1's territory,
        BouncePaddle(lside1, rside1);  //Then check if it's ready to bounce it, and act accordingly
      if(BallY >= 56)//If the ball is entering player 2's territory,
        BouncePaddle(lside2, rside2);  //Then check if it's ready to bounce it, and act accordingly
      if(BallY <= 0) //If the ball got past player 1,
      {
        activateball = 0;  //then end the round,
        p2score += 1;      //and give player 2 a point
      }
      if(BallY >= 60) //If the ball got past player 2,
      {
        activateball = 0;  //then end the round,
        p1score += 1;      //and give player 1 a point
      }
      if((BallX < 0) || (BallX > 96))
      {
        ballhor = !ballhor;           //Keep the ball within the boundaries along the x axis
        PlayTone(587, 100);
      }
      Wait(ballspeed);       //Make the delay for this loop equal to the value of variable "ballspeed"
      if(timer1 >= 10000)
      {
        ballspeed -= 5;      //Every 10 seconds, decrease the delay by 5 miliseconds
        offset1 = CurrentTick();
      }
    }
  }
}

task main()
{
  Precedes(secondary, checkCoins, ball, calctimer);
  SetSensor(IN_1, SENSOR_TOUCH);
  SetSensor(IN_2, SENSOR_ROTATION);   //Initialize the sensors
  SetSensorLight(IN_3);
  ResetTachoCount(OUT_A);
  Wait(1);
  Deposit();          //Calibrate the holding area motor's position
}


The slave NXT program:
Code: Select all
int cval;

inline void WaitForMessageToBeSent()
{
  while(RS485SendingData())
    Wait(1);
}

task main()
{
  string buffer, debug;
  bool gameover = 0, gamestart = 0;
  int BallX, BallY, Position1, Position2, SensorOld, SensorNew, stringlength, Number;
  int p1score, p2score;
  SetSensor(IN_1, SENSOR_TOUCH);
  SetSensor(IN_2, SENSOR_ROTATION);
  SetSensorColorFull(S3);
  ColorSensorReadType csr;
  csr.Port = S3;
  SetSensorColorFull(S3);     //Initialize sensors
  UseRS485();    // make sure the RS485 system is turned on
  RS485Enable();
  RS485Uart(HS_BAUD_DEFAULT, HS_MODE_DEFAULT);
  Wait(1);
  TextOut(0, LCD_LINE4, "Standing by...");
  PlayTone(3000, 500);
  while(true)
  {
    BallX = 49;
    BallY = 29;
    p1score = 0;
    p2score = 0;
    Position2 = 38;       //Initialize some variables
    while(true)
    {
      SysColorSensorRead(csr);          //Read the color sensor
      if(csr.ColorValue != 6 && csr.ColorValue != 1)           //If a coin has been detected,
      {
        SendRS485Number(1);
        WaitForMessageToBeSent();  //send a message to the master,
        PlayTone(100, 100);        //and play a beep
        Wait(3000);
      }
      if(RS485DataAvailable())
      {
        RS485Read(buffer);
        if(buffer == "br")          //break if the slave receives the message: "br"
          break;
      }
    }
    while(true) //Begin pong
    {
      SensorNew = SENSOR_2;
      repeat(3)
      {
        until(RS485DataAvailable());
        RS485Read(buffer);
        stringlength = StrLen(buffer);
        stringlength--;
        if("p" == LeftStr(buffer, 1))           //Read the opponent's paddle position,
          Position1 = StrToNum(SubStr(buffer, 1, stringlength));
        if("x" == LeftStr(buffer, 1))           //the ball's x position,
          BallX = StrToNum(SubStr(buffer, 1, stringlength));
        if("y" == LeftStr(buffer, 1))           //and the ball's y position
          BallY = StrToNum(SubStr(buffer, 1, stringlength));
        if(buffer == "t")
        {
          p2score++;
          break;
        }                             //Change the scores if a message telling it to has been received
        if(buffer == "o")
        {
          p1score++;
          break;
        }
      }
      SendRS485Number(Position2);    //Send the position of the player's paddle to the master
      WaitForMessageToBeSent();
      ClearScreen();
      GraphicOut(Position2, 60, "Tray.ric");
      GraphicOut(Position1, 0, "Tray.ric");    //Display the paddles and ball
      GraphicOut(BallX, BallY, "Ball.ric");
      if ((BallX == 49 && BallY == 29) || (BallY <= 1 || BallY >= 59))
      {                                 //Show the scores if the time is appropriate
        NumOut(49, LCD_LINE7, p1score);
        NumOut(49, LCD_LINE2, p2score);
      }
      SensorOld = SensorNew;
      SensorNew = SENSOR_2;         //Make a few calculations for the paddle's position
      SensorNew -= SensorOld;
      Position2 += SensorNew;
      if(Position2 < 0)
        Position2 = 0;
      if(Position2 > 76)            //Keep the paddle within the boundaries
        Position2 = 76;
      if(p1score >= 5 || p2score >= 5)
        break;                    //Break when someone has scored 5 points
    }
    Wait(500);
    ClearScreen();
    Wait(1250);
    if(p2score > p1score)      //If the player has won, let him or her know that
    {
      TextOut(25, LCD_LINE2, "You win!");
      PlayToneEx(262, 96, 4, false);
      Wait(96);
      PlayToneEx(330, 96, 4, false);
      Wait(96);
      PlayToneEx(392, 96, 4, false);
      Wait(96);
      PlayToneEx(494, 96, 4, false);
      Wait(96);
      PlayToneEx(523, 96, 4, false);
      Wait(96);
      PlayToneEx(659, 96, 4, false);
      Wait(96);
      PlayToneEx(784, 96, 4, false);
      Wait(96);
      PlayToneEx(988, 96, 4, false);
      Wait(96);
      PlayToneEx(1046, 192, 4, false);
      Wait(192);
      PlayToneEx(1046, 96, 2, false);
      Wait(96);
      PlayToneEx(1046, 90, 4, false);
      Wait(96);
      PlayToneEx(1046, 96, 4, false);
      Wait(96);
      PlayToneEx(1046, 96, 2, false);
      Wait(96);
      repeat (3)
      {
        PlayToneEx(880, 96, 4, false);
        Wait(96);
        PlayToneEx(880, 96, 2, false);
        Wait(96);
      }
      PlayToneEx(988, 864, 4, false);
      Wait(864);
      PlayToneEx(988, 96, 2, false);
      Wait(92);
    }
    else                     //If the player has lost, let him or her know that
    {
      TextOut(25, LCD_LINE2, "You lose");
      Wait(2880);
    }
    TextOut(30, LCD_LINE5, "SCORES:");                       //Show the scores
    TextOut(20, LCD_LINE7, "Player 1: " + NumToStr(p1score));
    TextOut(20, LCD_LINE8, "Player 2: " + NumToStr(p2score));
    Wait(1250);
    until((SENSOR_1 == 1) || RS485DataAvailable()); //Start back at the beginning of the loop when either the player
    until((SENSOR_1 == 0) || RS485DataAvailable()); //presses the touch sensor, or when it's needed for more coin receiving
    ClearScreen();
  }
}


The RCX program:
Code: Select all
program test {
   
   #include <RCX2.h>
   #include <RCX2MLT.h>
   #include <RCX2Sounds.h>
   #include <RCX2Def.h>
   sensor touch1 on 1
   touch1 is switch as boolean
   event tPress_touch1EventPress when touch1.pressed
   
   
   event tRelease_touch1EventRelease when touch1.released
   
   
   sensor touch2 on 2
   touch2 is switch as boolean
   event tPress_touch2EventPress when touch2.pressed
   
   
   event tRelease_touch2EventRelease when touch2.released
   
   
   sensor touch3 on 3
   touch3 is switch as boolean
   event tPress_touch3EventPress when touch3.pressed
   
   
   event tRelease_touch3EventRelease when touch3.released
   
   
   event greater_counter1EventHigh when counter1 > 100
   
   event equal_counter1EventEqual when counter1 = 10
   
   
   main {
      ext InterfaceType "kFreestyle"
      rcx_ClearTimers
      bbs_GlobalReset([A B C])
      trigger greater_counter1EventHigh
      trigger equal_counter1EventEqual
      start TouchWatcher0
      start TouchWatcher1
      start TouchWatcher2
      trigger tRelease_touch1EventRelease
      rcx_Priority( 8)
      trigger tPress_touch1EventPress
      trigger tPress_touch2EventPress
      trigger tPress_touch3EventPress
      try {
         counter1 = 10
      } retry on fail
      try {
         forever {
            wait until greater_counter1EventHigh
            sound 4
            wait 200
            bb_MotorControl ( [ ] , [ B  ] , [ B  ] , 170 )
            wait until equal_counter1EventEqual
         }
      } retry on fail
   }
   
   
   watcher TouchWatcher0 monitor tPress_touch1EventPress
   {
      rcx_Priority( 3 )
      try {
         if counter1 = 40{
            wait until tRelease_touch1EventRelease
            counter1 += 10
         }
         else
         {
            if counter1 = 60{
               wait until tRelease_touch1EventRelease
               counter1 += 10
            }
            else
            {
               if counter1 = 100{
                  wait until tRelease_touch1EventRelease
                  counter1 += 10
               }
               else
               {
                  if counter1 = 110{
                     wait until tRelease_touch1EventRelease
                     counter1 = 10
                     bb_MotorControl ( [B  ] , [  ] , [ B  ] , 330 )
                  }
                  else
                  {
                     counter1 = 10
                     sound 5
                  }
               }
            }
         }
      } restart on fail
   } restart on event
   watcher TouchWatcher1 monitor tPress_touch2EventPress
   {
      rcx_Priority( 3 )
      try {
         if counter1 < 40{
            wait until tRelease_touch2EventRelease
            counter1 += 10
         }
         else
         {
            if counter1 = 80{
               wait until tRelease_touch2EventRelease
               counter1 += 10
            }
            else
            {
               if counter1 = 110{
                  wait until tRelease_touch2EventRelease
                  counter1 = 10
                  bb_MotorControl ( [B  ] , [  ] , [ B  ] , 330 )
               }
               else
               {
                  counter1 = 10
                  sound 5
               }
            }
         }
      } restart on fail
   } restart on event
   watcher TouchWatcher2 monitor tPress_touch3EventPress
   {
      rcx_Priority( 3 )
      try {
         if counter1 = 50{
            wait until tRelease_touch3EventRelease
            counter1 += 10
         }
         else
         {
            if counter1 = 70{
               wait until tRelease_touch3EventRelease
               counter1 += 10
            }
            else
            {
               if counter1 = 90{
                  wait until tRelease_touch3EventRelease
                  counter1 += 10
               }
               else
               {
                  if counter1 = 110{
                     wait until tRelease_touch3EventRelease
                     counter1 = 10
                     bb_MotorControl ( [B  ] , [  ] , [ B  ] , 330 )
                  }
                  else
                  {
                     counter1 = 10
                     sound 5
                  }
               }
            }
         }
      } restart on fail
   } restart on event
}


Here are some step-by-step building instructions for the model:
http://www.mediafire.com/view/3re3aoiec ... achine.pdf
The virtual model was built using the LDraw system of tools.

Here is a full video demonstration:




I began building this in August 2011. It took me about 2 weeks to build it, with minor revisions being made as time went on. Unfortunately, the programming part got severely delayed due to high school beginning for me when the 2011-2012 school year began. I got very busy, and blew my opportunity to get this project done during Summer 2012. The delay continued once again when the 2012-2013 year began, but I finally finished the programming in May 2013 after Summer began, just recently finished the documentation, and got the Youtube video published last night. This is a large part of the reason I haven't been super active these past 2 years. The fact that this project took me 2 years when it should have taken me little more than 4 months speaks volumes for the fact that it's nearing the time for me to start phasing out of MINDSTORMS. I also find this to be a fitting time to retire due to all these people of the new generation coming now that the EV3 is coming out. With this being my last project, I'm not sure how much longer you'll be seeing me here, or on any other MINDSTORMS websites, so I guess this thread could be considered my farewell to this community. I may drop in every once in a while, but for the most part I'm done. These three years with you guys have been awesome.
-Hayden
User avatar
haydenstudios
 
Posts: 252
Joined: 22 Oct 2010, 20:05
Location: The United States of America

Re: Arcade Machine

Postby h-g-t » 19 Sep 2013, 21:58

A sophistical rhetorician, inebriated with the exuberance of his own verbosity, and gifted with an egotistical imagination that can at all times command an interminable and inconsistent series of arguments to malign an opponent and to glorify himself.
h-g-t
 
Posts: 552
Joined: 07 Jan 2011, 08:59
Location: Albania


Return to Mindstorms Projects

Who is online

Users browsing this forum: No registered users and 20 guests

cron