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:
Continue with the Analysis in Part II
|