Techno-Plaza
Site Navigation [ News | Our Software | Calculators | Programming | Assembly | Downloads | Links | Cool Graphs | Feedback ]
 Main
   Site News

   Our Software

   Legal Information

   Credits

 Calculators
   Information

   C Programming

     Introduction to C

     Keyboard Input

     Graphics Intro

     Slider Puzzle 1

       Part I

       Part II

       Part III

     Functions

     Pointers

     Dynamic Memory

     Slider Puzzle 2

     Structures

     Bit Manipulation

     Advanced Pointers

     File I/O

     Graduate Review

   Assembly

   Downloads

 Miscellaneous
   Links

   Cool Graphs

   Feedback Form

C Programming Lessons

TIGCC Programming Lessons

The following is part of a series of lessons designed to help teach people to program in C for the TI-89, 92+, and V200 calculators using the TIGCC development environment.

If you wish, you can download the program source code, project files, and binaries here.

Lesson Review 1 - Text Display, Keyboard Input, and Sprite Graphics

Step 1 - An Introduction to the Review

Review lessons are created by Techno-Plaza to help reinforce concepts that have been taught in previous lessons. They usually work with programs which are non-trivial, i.e. they actually do something, whereas the example programs are usually just simple things we thought up to illustrate the concept. These programs however show you how you can actually use the concepts you have learned to create something useful, or at least somewhat cool.

I will not explain every line of the review code, because you should already know the basic concepts presented here. I will explain things that have been elaborated on, and show you how things interact with one another, but if there is something you do not understand, you should probably review the lessons, as they will explain the concepts in more detail that I will present here.

This particular review deals with a sliding puzzle game. I know you have all seen them before. They are the little puzzles which have 11 or 15 pieces, on a 12 or 16 slot board. The pieces are numbered (or form a picture when properly rearranged), and are not in order when you begin the puzzle. Your job is to rearrange the puzzle by sliding the pieces one at a time around the empty slot on the board. When you rearrange the pieces correctly, you have beaten the game. To keep the game simple, I have used numbered pieces instead of a picture, but either works for the example.

As you will see, using only the concepts you have learned in the first 3 lessons (we may have to elaborate on some of the techniques as they are more complex here), you can build such a puzzle game.

It is worthwhile to note that the example code presented here is not the best code one could write, if one understood C more properly. However, review lessons (as a rule) try not to present new concepts, only to review ones we have already learned. So certain things that could make this program better could not be included. We might update this program in the second review to use some of the concepts I'm hinting at, but I haven't decided that yet.

Well, enough of this intro talk, let's get down to the program.

Step 2 - Slider Puzzle 1.0, a Review Program by Techno-Plaza

Open TIGCC and create a new project. Create a new C Source File named slider. Edit the file so that it looks like this:

slider.c


/*
    Slider Puzzle 1.0
    Copyright (C) 2000,2007 Techno-Plaza

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

#include <tigcclib.h>

// define puzzle piece sprite height
#define PIECE_HEIGHT  16

// define the puzzle sprites
static unsigned short int piece1[] =
  {0xFFFF,0xC003,0x8001,0x8181,0x8781,0x8181,0x8181,0x8181,
   0x8181,0x8181,0x8181,0x8181,0x8181,0x8001,0xC003,0xFFFF};

static unsigned short int piece2[] = 
  {0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8661,0x8061,0x80C1,
   0x8181,0x8301,0x8601,0x8601,0x87E1,0x8001,0xC003,0xFFFF};

static unsigned short int piece3[] =
  {0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8061,0x8061,0x81C1,
   0x8061,0x8061,0x8061,0x8661,0x83C1,0x8001,0xC003,0xFFFF};

static unsigned short int piece4[] =
  {0xFFFF,0xC003,0x8001,0x80C1,0x81C1,0x83C1,0x83C1,0x86C1,
   0x86C1,0x8CC1,0x8FF1,0x80C1,0x80C1,0x8001,0xC003,0xFFFF};

static unsigned short int piece5[] =
  {0xFFFF,0xC003,0x8001,0x87E1,0x8601,0x8601,0x8601,0x87C1,
   0x8661,0x8061,0x8061,0x8661,0x83C1,0x8001,0xC003,0xFFFF};
   
static unsigned short int piece6[] =
  {0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8601,0x8601,0x87C1,
   0x8661,0x8661,0x8661,0x8661,0x83C1,0x8001,0xC003,0xFFFF};

static unsigned short int piece7[] =
  {0xFFFF,0xC003,0x8001,0x87E1,0x8061,0x80C1,0x80C1,0x8181,
   0x8181,0x8181,0x8301,0x8301,0x8301,0x8001,0xC003,0xFFFF};

static unsigned short int piece8[] =
  {0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8661,0x8661,0x83C1,
   0x8661,0x8661,0x8661,0x8661,0x83C1,0x8001,0xC003,0xFFFF};

static unsigned short int piece9[] =
  {0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8661,0x8661,0x8661,
   0x83E1,0x8061,0x8061,0x8661,0x83C1,0x8001,0xC003,0xFFFF};
   
static unsigned short int piece10[] =
  {0xFFFF,0xC003,0x8001,0x8C79,0xBCCD,0x8CCD,0x8CCD,0x8CCD,
   0x8CCD,0x8CCD,0x8CCD,0x8CCD,0x8C79,0x8001,0xC003,0xFFFF};
   
static unsigned short int piece11[] =
  {0xFFFF,0xC003,0x8001,0x8C31,0xBCF1,0x8C31,0x8C31,0x8C31,
   0x8C31,0x8C31,0x8C31,0x8C31,0x8C31,0x8001,0xC003,0xFFFF};
   
static unsigned short int piece12[] =
  {0xFFFF,0xC003,0x8001,0x8C79,0xBCCD,0x8CCD,0x8C0D,0x8C19,
   0x8C31,0x8C61,0x8CC1,0x8CC1,0x8CFD,0x8001,0xC003,0xFFFF};
   
static unsigned short int piece13[] =
  {0xFFFF,0xC003,0x8001,0x8CF1,0xBD99,0x8C19,0x8C19,0x8C71,
   0x8C19,0x8C19,0x8C19,0x8D99,0x8CF1,0x8001,0xC003,0xFFFF};
   
static unsigned short int piece14[] =
  {0xFFFF,0xC003,0x8001,0x8C0D,0xBC1D,0x8C3D,0x8C3D,0x8C6D,
   0x8C6D,0x8CCD,0x8CFD,0x8C0D,0x8C0D,0x8001,0xC003,0xFFFF};
   
static unsigned short int piece15[] =
  {0xFFFF,0xC003,0x8001,0x8CFD,0xBCC1,0x8CC1,0x8CC1,0x8CF9,
   0x8CCD,0x8C0D,0x8C0D,0x8CCD,0x8C79,0x8001,0xC003,0xFFFF};

// a pointer to the puzzle pieces to access them via an array
static unsigned short int *pieces[] = {
    piece1, piece2, piece3, piece4, piece5,
    piece6, piece7, piece8, piece9, piece10,
    piece11, piece12, piece13, piece14, piece15};

void _main(void) {
  int xLoop, yLoop, piece = 0;
  int loop, temp, randNum;
  int puzzle[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,-1};
  int x1 = 0, x2 = 0, x3 = 0, x4 = 0, x5 = 0, x6 =0, x7 = 0, x8 = 0;
  int x9 = 0, x10 = 0, x11 = 0, x12 = 0, x13 = 0, x14 = 0, x15 = 0;
  int y1 = 0, y2 = 0, y3 = 0, y4 = 0, y5 = 0, y6 =0, y7 = 0, y8 = 0;
  int y9 = 0, y10 = 0, y11 = 0, y12 = 0, y13 = 0, y14 = 0, y15=0;
  int *x[] = {&x1,&x2,&x3,&x4,&x5,&x6,&x7,&x8,&x9,&x10,&x11,&x12,&x13,&x14,&x15};
  int *y[] = {&y1,&y2,&y3,&y4,&y5,&y6,&y7,&y8,&y9,&y10,&y11,&y12,&y13,&y14,&y15};
  int position, currentPiece = 0, done = 0, key = 0, on = 1, moved = 0;
  int winner = 0;

  // clear the screen
  ClrScr();
  
  // draw the game control strings
  DrawStr(0, 70, "Use Arrows to Move Pieces", A_NORMAL);
  DrawStr(0, 80, "Press ENTER to Slide Piece", A_NORMAL);
  DrawStr(0, 90, "Press ESC to Quit", A_NORMAL);
  
  // draw the game information strings
  DrawStr(67, 0, "Slider 1.0", A_NORMAL);
  
  FontSetSys(F_4x6);
  DrawStr(70, 10, "Copyright (C) 2000,2007", A_NORMAL);
  DrawStr(70, 18, "Techno-Plaza", A_NORMAL);
  
  DrawStr(67, 30, "Created by Techno-Plaza", A_NORMAL);
  DrawStr(75, 38, "for Lesson Review 1", A_NORMAL);
  DrawStr(67, 50, "Learn more at our website", A_NORMAL);
  DrawStr(70, 58, "www.technoplaza.net", A_NORMAL);
  
  // seed the random number generator
  randomize();
  
  // randomize the puzzle pieces
  for (loop = 0; loop < 16; loop++) {
    // select a random puzzle piece
    randNum = random(16);

    // replace the old piece with the new piece
    temp = puzzle[loop];
    puzzle[loop] = puzzle[randNum];
    puzzle[randNum] = temp;
  }

  // display the puzzle pieces
  for (yLoop = 0; yLoop < (16 * 4); yLoop += 16) {
    for (xLoop = 0; xLoop < (16 * 4); xLoop += 16) {
      // only attempt to draw valid pieces
      if (puzzle[piece] != -1) {
        Sprite16(xLoop, yLoop, PIECE_HEIGHT, 
                 pieces[puzzle[piece]], LCD_MEM, SPRT_XOR);
        
        // mark the position of the piece
        *x[puzzle[piece]] = xLoop;
        *y[puzzle[piece]] = yLoop;
      }
      
      piece++;
    }
  }
  
  // set the first piece to be piece 0
  position = 0;
  currentPiece = puzzle[position];
  
  // loop until the puzzle is completed, or until the
  // user presses ESC
  while (!done) {
    // set the timer interval to 1/2 second
    OSFreeTimer(USER_TIMER);
    OSRegisterTimer(USER_TIMER, 10);
    
    // wait for the timer to expire, or the user to press a key
    while (!OSTimerExpired(USER_TIMER) && !kbhit());
    
    if (kbhit()) {
      // if the user pressed a key, grab it
      key = ngetchx();
    } else {
      // otherwise, erase old keystrokes
      key = 0;
    }
    
    // handle keystrokes
    if (key == KEY_ESC) {
      // ESC means they quit
      done = 1;
    } else if (key == KEY_ENTER) {
      // if they tried to move a piece
      moved = 0;

      // try to move the piece right
      if (position+1 <= 16) {
        // if the right position is vacant
        if (puzzle[position+1] == -1 && *x[currentPiece] != (16*4-16)) {
          // make sure that the old piece is still drawn
          if (!on) {
            Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                     pieces[currentPiece], LCD_MEM, SPRT_XOR);
            on = 1;
          }

          // erase the piece
          Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                   pieces[currentPiece], LCD_MEM, SPRT_XOR);
          
          // move the piece right 1 position (16 pixels)
          *x[currentPiece]+=16;
          
          // reset the puzzle position holders
          puzzle[position+1] = puzzle[position];
          puzzle[position] = -1;
          position++;
          currentPiece = puzzle[position];

          // draw the piece at the new position          
          Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                   pieces[currentPiece], LCD_MEM, SPRT_XOR);
          
          // don't try to move the piece any other direction
          moved = 1;
        }
      }
      
      // try to move the piece left
      if (position-1 >= 0) {
        // if the left position is vacant, and we haven't already moved
        if (puzzle[position-1] == -1 && *x[currentPiece] != 0 && !moved) {
          // make sure the piece is drawn, so it can be properly erased
          if (!on) {
            Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                     pieces[currentPiece], LCD_MEM, SPRT_XOR);
            on = 1;
          }
          
          // erase the piece
          Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                   pieces[currentPiece], LCD_MEM, SPRT_XOR);
          
          // move the piece 1 position left (16 pixels)
          *x[currentPiece]-=16;
          
          // reset the puzzle position holders
          puzzle[position-1] = puzzle[position];
          puzzle[position] = -1;
          position--;
          currentPiece = puzzle[position];
          
          // draw the piece at the new location
          Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                   pieces[currentPiece], LCD_MEM, SPRT_XOR);
          
          // do not try to move this piece any other direction
          moved = 1;
        }
      }
      
      // try to move the piece up
      if (position-4 >= 0) {
        // if the up position is vacant, and we haven't already moved
        if (puzzle[position-4] == -1 && *y[currentPiece] != 0 && !moved) {
          // make sure the piece is drawn, so it can be properly erased
          if (!on) {
            Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                     pieces[currentPiece], LCD_MEM, SPRT_XOR);
            on = 1;
          }
          
          // erase the piece          
          Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                   pieces[currentPiece], LCD_MEM, SPRT_XOR);
          
          // move the piece 1 position up (16 pixels)
          *y[currentPiece]-=16;
          
          // reset the puzzle position holders
          puzzle[position-4] = puzzle[position];
          puzzle[position] = -1;
          position-=4;
          currentPiece = puzzle[position];
          
          // redraw the piece at the new location
          Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                   pieces[currentPiece], LCD_MEM, SPRT_XOR);
          
          // do not try to move this piece any other direction
          moved = 1;
        }
      }
      
      // try to move the piece down
      if (position+4 <= 15) {
        // if the down position is vacant, and we haven't already moved
        if (puzzle[position+4] == -1 && *y[currentPiece] != (16*4-16) && !moved) {
          // make sure the piece is drawn, so it can be properly erased
          if (!on) {
            Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                     pieces[currentPiece], LCD_MEM, SPRT_XOR);
            on = 1;
          }
          
          // erase the piece
          Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                   pieces[currentPiece], LCD_MEM, SPRT_XOR);
          
          // move the piece one position down (16 pixels)
          *y[currentPiece]+=16;
          
          // reset the puzzle position holders
          puzzle[position+4] = puzzle[position];
          puzzle[position] = -1;
          position+=4;
          currentPiece = puzzle[position];
          
          // redraw the piece at the new location
          Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                   pieces[currentPiece], LCD_MEM, SPRT_XOR);
          
          // do not try to move any other direction
          moved = 1;
        }
      }
    } else if (key == KEY_LEFT) {
      // make sure the piece is still drawn, so it's still there when we move
      if (!on) {
        Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                 pieces[currentPiece], LCD_MEM, SPRT_XOR);
        on = 1;
      }

      // if there is a left position to move to      
      if (position > 0) {
        // move left
        position--;
        
        // if we are at the empty slot, move left again
        if (puzzle[position] == -1 && position > 0) {
          position--;
        
        // if we can't move left anymore, start at the end
        } else if (puzzle[position] == -1 && position == 0) {
          position = 15;
        }
      } else {
        // set the position to the end
        position = 15;
        
        // if we found the empty slot, move left one
        if (puzzle[position] == -1) {
          position--;
        }
      }

      // reset the puzzle position holder
      currentPiece = puzzle[position];
    } else if (key == KEY_RIGHT) {
      // make sure the piece is drawn, so we can move away safely
      if (!on) {
        Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                 pieces[currentPiece], LCD_MEM, SPRT_XOR);
        on = 1;
      }
      
      // if there is a position to move right to
      if (position < 15) {
        // move one position to the right
        position++;
        
        // if we hit the empty slot, move right again
        if (puzzle[position] == -1 && position < 15) {
          position++;
        
        // if we hit the empty slot and the edge, start over
        } else if (puzzle[position] == -1 && position == 15) {
          position = 0;
        }
      // else, we need to start over at the beginning
      } else {
        // move to the beginning
        position = 0;
        
        // if we hit the empty slot, move right again
        if (puzzle[position] == -1) {
          position++;
        }
      }
      
      // reset the puzzle position holder
      currentPiece = puzzle[position];
    } else if (key == KEY_UP) {
      // make sure the piece is drawn, so we can move safely
      if (!on) {
        Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                 pieces[currentPiece], LCD_MEM, SPRT_XOR);
        on = 1;
      }
      
      // if there is an up position to move to
      if ((position - 4) >= 0) {
        // move up one position
        position-=4;
        
        // if we hit the empty slot
        if (puzzle[position] == -1) {
          // if there is another up position, go there
          if ((position - 4) >= 0) {
            position-=4;
          // otherwise, run to the bottom row
          } else {
            position = 16 - (4 - position);
          }
        }
      // else, move to the bottom row
      } else {
        // set position at the bottom row
        position = 16 - (4 - position);
        
        // if we hit the empty slot, move up again
        if (puzzle[position] == -1) {
          position-=4;
        }
      }
      
      // reset the puzzle position holder
      currentPiece = puzzle[position];
    } else if (key == KEY_DOWN) {
      // make sure the piece is still drawn, so we can move safely
      if (!on) {
        Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                 pieces[currentPiece], LCD_MEM, SPRT_XOR);
        on = 1;
      }
      
      // if there is a down position to move to
      if ((position + 4) <= 15) {
        // move down one position
        position+=4;
        
        // if we hit the empty slot
        if (puzzle[position] == -1) {
          // if there is another down position
          if ((position + 4) <= 15) {
            // move to the next down position
            position+=4;
          // otherwise, goto the top row
          } else {
            position%=4;
          }
        }
      // else, move to the top row
      } else {
        // move to the top row
        position%=4;
        
        // if we hit the empty slot
        if (puzzle[position] == -1) {
          // move down one more time
          position+=4;
        }
      }
      
      // reset the puzzle position holder
      currentPiece = puzzle[position];
    }
    
    // blink the current piece so we know which one it is
    on = !on;
    Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
             pieces[currentPiece], LCD_MEM, SPRT_XOR);
    
    // set the winner to one, then try to disqualify it with
    // the for loop. it's easier to reach a false conclusion than
    // a true conclusion, so start true, and work towards false
    winner = 1;

    // test for winner
    // loop through all game pieces, and see if their positions
    // match their piece numbers. If they do, then the player won
    // if we find any piece that is not right, we have no winner
    // and we stop the for loop
    for (loop = 0; loop < 15; loop++) {
      if (*y[loop] != ((loop / 4) * 16) || *x[loop] != ((loop % 4) * 16)) {
        winner = 0;
        break;
      }
    }

    // if we have a winner, make sure the piece is drawn and
    // set done to yes
    if (winner) {
      // if we win, then we are done, obviously
      done = 1;
      
      // make sure the piece is drawn so it looks nice
      if (!on) {
        Sprite16(*x[currentPiece], *y[currentPiece], PIECE_HEIGHT, 
                 pieces[currentPiece], LCD_MEM, SPRT_XOR);
        on = 1;
      }

      // tell the player they won
      DlgMessage("Winner", "You have completed the puzzle!", BT_OK, BT_NONE);
    }
  }
}

Step 3a - Compile and Run the Program

Save the files and build the project. Send the program to TiEmu. It should look something like this:

TI-89 AMS 2.05 slider.89z TI-92+ AMS 2.05 slider.9xz

Continue with the Analysis in Part II

 

Copyright © 1998-2007 Techno-Plaza
All Rights Reserved Unless Otherwise Noted

Get Firefox!    Valid HTML 4.01!    Made with jEdit