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

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

AltStyle によって変換されたページ (->オリジナル) /