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

     Functions

     Pointers

     Dynamic Memory

     Slider Puzzle 2

       Part I

       Part II

       Part III

     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 2 - Functions, Pointers, and Dynamic Memory

Step 1 - Introduction, Part II

In the first review lesson, we introduced a sliding puzzle game to demonstrate that even with simple concepts, we could make something useful. Although we did create a program that worked very well, it was very poorly written. This was because we simply did not have the knowledge to create a better program. Although inherently there was nothing wrong with the program, it could have been written much differently. The things we have learned in the last three lessons will allow us to write a much better version that is much easier to code.

The biggest problems we encountered with the first slider puzzle version was that we had no way to modularize the program, had no way to address a set of variables collectively (even though we cheated a little and did use an array), and had lots of excess memory used because we had no other way of using variables. Now we have functions to modularize the program into separate steps. We have arrays and pointers to use our variable structures as one, and we have dynamic memory allocation to address our wasted memory concerns.

So, with all that talk about the first slider puzzle, I think you can guess what the example program we will be using is. That's right, we will be fixing the problems we had with the first program and making it a better program. So, let's not waste any more time on this intro, but get right down to the heart of this review: using our concepts to make our simple programs better.

Step 2 - The New and Improved Slider Puzzle, Version 2.0

Start TIGCC and start a new project. Create a new C Source File and a new C Header File. You can name them both slider. Here is the code for the two files.

slider.c


/*
    Slider Puzzle 2.0
    Copyright (C) 2000-2002,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>
#include "slider.h"    // include the slider puzzle header file

void initPuzzle(int *puzzle) {
      int loop;

      // initialize the puzzle positions
      for (loop = 0; loop < 15; loop++) {
            puzzle[loop] = loop;
      }

      // make the last piece the blank piece
      puzzle[15] = -1;
}

void randomizePuzzle(int *puzzle) {
      int loop, randNum, temp;

      // 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;
      }
}

void drawPuzzle(int *x, int *y, int *puzzle) {
      int xLoop, yLoop, piece = 0;

      // randomize the puzzle before drawing
      randomizePuzzle(puzzle);

      // loop through the rows of the puzzle to draw them
      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++;
            }
      }
}

inline void drawScreen(void) {
      // 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 2.0", A_NORMAL);

      FontSetSys(F_4x6);
      DrawStr(70, 10, "(C) 2000-2002,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 2", A_NORMAL);
      DrawStr(67, 50, "Learn more at our website", A_NORMAL);
      DrawStr(70, 58, "www.technoplaza.net", A_NORMAL);

      // reset font to normal size
      FontSetSys(F_6x8);
}

void newGame(int *x, int *y, int *puzzle) {
      int key, loop;
      int position = 0, currentPiece = 0, on = 1, done = 0, winner = 0;

      // draw the background screen
      drawScreen();

      // draw a new puzzle
      drawPuzzle(x,y,puzzle);

      // set the first piece to be piece 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 (position+1 <= 16) {
                        // try to move the piece right
                        // 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 any other direction
                              continue;
                        }
                  }

                  if (position-1 >= 0) {
                        // try to move the piece left
                        // if the left position is vacant, and we haven't already moved
                        if (puzzle[position-1] == -1 && x[currentPiece] != 0) {
                              // 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);

                              // don't try to move any other direction
                              continue;
                        }
                  }

                  if (position-4 >= 0) {
                        // try to move the piece up
                        // if the up position is vacant, and we haven't already moved
                        if (puzzle[position-4] == -1 && y[currentPiece] != 0) {
                              // 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);

                              // don't try to move any other direction
                              continue;
                        }
                  }

                  if (position+4 <= 15) {
                        // try to move the piece down
                        // if the down position is vacant, and we haven't already moved
                        if (puzzle[position+4] == -1 && y[currentPiece] != (16*4-16)) {
                              // 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);

                              // no need to continue since there are no other directions...
                        }
                  }
            } 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);
            }
      }
}

void _main(void) {
      int *x = NULL, *y = NULL, *puzzle = NULL;
      const char *title = "DMA Error", *error = "Unable to allocate array space";

      // allocate memory for the x position array
      if ((x = (int *)calloc(16,sizeof(int))) == NULL) {
            DlgMessage(title,error,BT_OK,BT_NONE);
            return;
      }

      // allocate memory for the y position array
      if ((y = (int *)calloc(16,sizeof(int))) == NULL) {
            DlgMessage(title,error,BT_OK,BT_NONE);

            // free the memory used by the x positions
            free(x);

            return;
      }

      // allocate memory fro the puzzle piece position array
      if ((puzzle = (int *)calloc(16,sizeof(int))) == NULL) {
            DlgMessage(title,error,BT_OK,BT_NONE);

            // free the memory used by the position arrays
            free(x);
            free(y);

            return;
      }

      // seed the random number generator
      randomize();

      // initialize the puzzle
      initPuzzle(puzzle);

      // loop and play the game
      while ((DlgMessage("Slider Puzzle 2.0","Press ENTER to play, ESC to quit.",
                        BT_OK,BT_NONE)) == KEY_ENTER) {
            newGame(x,y,puzzle);
      }

      // free the memory allocated for the arrays
      free(x);
      free(y);
      free(puzzle);
}

Save the slider source file, and now let's edit the slider.h file. Remember all you need to do is click on the slider label under the Header Files folder in the left window pane. Modify the file so that it looks like this:

slider.h


/*
    Slider Puzzle 2.0
    Copyright (C) 2000-2002,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
*/

#ifndef _SLIDER_H
#define _SLIDER_H

/* Begin the constant definitions */

// define puzzle piece sprite height
#define PIECE_HEIGHT      16

/* Begin the static sprite definitions */

// define the puzzle sprites
// multi-dimensional array - a pointer of pointers...
// 16 sprites, 16 rows tall
static unsigned short int pieces[16][16] = {
      {0xFFFF,0xC003,0x8001,0x8181,0x8781,0x8181,0x8181,0x8181,
       0x8181,0x8181,0x8181,0x8181,0x8181,0x8001,0xC003,0xFFFF},      // piece 1
      {0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8661,0x8061,0x80C1,
       0x8181,0x8301,0x8601,0x8601,0x87E1,0x8001,0xC003,0xFFFF},      // piece 2
      {0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8061,0x8061,0x81C1,
       0x8061,0x8061,0x8061,0x8661,0x83C1,0x8001,0xC003,0xFFFF},      // piece 3
      {0xFFFF,0xC003,0x8001,0x80C1,0x81C1,0x83C1,0x83C1,0x86C1,
       0x86C1,0x8CC1,0x8FF1,0x80C1,0x80C1,0x8001,0xC003,0xFFFF},      // piece 4
      {0xFFFF,0xC003,0x8001,0x87E1,0x8601,0x8601,0x8601,0x87C1,
       0x8661,0x8061,0x8061,0x8661,0x83C1,0x8001,0xC003,0xFFFF},      // piece 5
      {0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8601,0x8601,0x87C1,
       0x8661,0x8661,0x8661,0x8661,0x83C1,0x8001,0xC003,0xFFFF},      // piece 6
      {0xFFFF,0xC003,0x8001,0x87E1,0x8061,0x80C1,0x80C1,0x8181,
       0x8181,0x8181,0x8301,0x8301,0x8301,0x8001,0xC003,0xFFFF},      // piece 7
      {0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8661,0x8661,0x83C1,
       0x8661,0x8661,0x8661,0x8661,0x83C1,0x8001,0xC003,0xFFFF},      // piece 8
      {0xFFFF,0xC003,0x8001,0x83C1,0x8661,0x8661,0x8661,0x8661,
       0x83E1,0x8061,0x8061,0x8661,0x83C1,0x8001,0xC003,0xFFFF},      // piece 9
      {0xFFFF,0xC003,0x8001,0x8C79,0xBCCD,0x8CCD,0x8CCD,0x8CCD,
       0x8CCD,0x8CCD,0x8CCD,0x8CCD,0x8C79,0x8001,0xC003,0xFFFF},      // piece 10
      {0xFFFF,0xC003,0x8001,0x8C31,0xBCF1,0x8C31,0x8C31,0x8C31,
       0x8C31,0x8C31,0x8C31,0x8C31,0x8C31,0x8001,0xC003,0xFFFF},      // piece 11
      {0xFFFF,0xC003,0x8001,0x8C79,0xBCCD,0x8CCD,0x8C0D,0x8C19,
       0x8C31,0x8C61,0x8CC1,0x8CC1,0x8CFD,0x8001,0xC003,0xFFFF},      // piece 12
      {0xFFFF,0xC003,0x8001,0x8CF1,0xBD99,0x8C19,0x8C19,0x8C71,
       0x8C19,0x8C19,0x8C19,0x8D99,0x8CF1,0x8001,0xC003,0xFFFF},      // piece 13
      {0xFFFF,0xC003,0x8001,0x8C0D,0xBC1D,0x8C3D,0x8C3D,0x8C6D,
       0x8C6D,0x8CCD,0x8CFD,0x8C0D,0x8C0D,0x8001,0xC003,0xFFFF},      // piece 14
      {0xFFFF,0xC003,0x8001,0x8CFD,0xBCC1,0x8CC1,0x8CC1,0x8CF9,
       0x8CCD,0x8C0D,0x8C0D,0x8CCD,0x8C79,0x8001,0xC003,0xFFFF}      // piece 15
};

/* Begin Function Proto-type Definitions Here */

// initialize the puzzle array
void initPuzzle(int *);

// randomize the puzzle position pieces
void randomizePuzzle(int *);

// draw the puzzle on screen
void drawPuzzle(int *, int *, int *);

// draw the background screen text
inline void drawScreen(void);

// create and play a new game
void newGame(int *, int *, int *);

// the main method -- program execution begins here
void _main(void);

#endif

Save the header file and build the project. Send the program to TiEmu and run it. It will 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