// C Source File // Created 12.23.2005; 04:00:05 PM // Sudoku89: Sudoku game for the TI-89 // Written by James Trimble // Copyright 2005 James Trimble // // 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // If you decide to use some of this code in your own program please send me an email. // I'd love to know that something I wrote was helpful. // TODO: Make high-scores tamper-proof (low priority) // Improve random mask generation (medium priority) // Ensure uniqueness (high priority) // Fix graphics glitches (high priority) // Includes #include // Delete or comment out the items you do not need. #define COMMENT_STRING "Sudoku89" #define COMMENT_PROGRAM_NAME "Sudoku89" #define COMMENT_VERSION_STRING "Version 1.0" #define COMMENT_VERSION_NUMBER 1,0,0,0 /* major, minor, revision, subrevision */ // User defines #define WITHLINES 0 #define T_OFFX 6 #define T_OFFY 1+(1-WITHLINES) #define R_OFFX 12 #define R_OFFY 6 #define B_OFFX 6 #define B_OFFY 11-(1-WITHLINES) #define L_OFFX 0 #define L_OFFY 6 #define SIDE 10 #define R_BOARDX 86 #define R_BOARDY 0 #define L_BOARDX 0 #define L_BOARDY 0 #define SIZE 9 #define SCORECOUNT 5 #define FILENAME "sudokdat" #define FALSE 0 #define TRUE 1 // Types typedef struct score_t { char name[21]; int min; int sec; } score; enum { gameover, paused, playing, }; // Globals int _ti89; //piece pieces[SIZE][SIZE]; int gameboard[SIZE][SIZE]; int locked[SIZE][SIZE] = {{0,1,0,1,0,1,0,1,0}, {0,0,1,1,0,1,1,0,0}, {1,0,0,0,0,0,0,0,1}, {1,0,0,1,0,1,0,0,1}, {0,0,1,0,0,0,1,0,0}, {1,0,0,1,0,1,0,0,1}, {1,0,0,0,0,0,0,0,1}, {0,0,1,1,0,1,1,0,0}, {0,1,0,1,0,1,0,1,0}}; int History[SIZE][SIZE][SIZE]; INT_HANDLER oldint5 = NULL; volatile int seconds=0; volatile int minutes=0; volatile int resettime = 1; volatile int gamestate = paused; volatile int mseconds50 = 0; // counts in 50ms steps (not exact 50ms, but 1000/19 ms) //PrintDebug - Takes in a string and prints it at the bottom of the window void PrintDebug(char * msg) { int i; for (i=0;i<2;i++) { GraySetAMSPlane(i); ScrRectFill(&(SCR_RECT){{0,93,160,100}},&(SCR_RECT){{0,93,160,100}},A_REVERSE); DrawStr(0,93,msg,A_NORMAL); } } // Handles timing DEFINE_INT_HANDLER (TimerISR) { char msg[10]; if (resettime) { mseconds50=seconds=minutes=resettime=0; } else if(gamestate == playing) { mseconds50++; if (mseconds50 == 19) { mseconds50 = 0; seconds++; if (seconds >= 60) { minutes++; seconds -= 60; } sprintf(msg,"%d:%02d",minutes,seconds); PrintDebug(msg); } } ExecuteHandler(oldint5); } // Comparison Function CALLBACK short score_comp(const void *a, const void *b) { score score1; score score2; strcpy(score1.name,(*(const score*)a).name); score1.min = (*(const score*)a).min; score1.sec = (*(const score*)a).sec; strcpy(score2.name,(*(const score*)b).name); score2.min = (*(const score*)b).min; score2.sec = (*(const score*)b).sec; if (score1.min < score2.min) return -1; else if (score1.min == score2.min) { if (score1.sec < score2.sec) return -1; else if (score1.sec == score2.sec) { return strcmp(score1.name, score2.name); } else return 1; } else return 1; } // Draw Block - Draws a tetravex piece without numbers void DrawBlock(int x, int y) { int GrayPlane = 0; for (GrayPlane = 0; GrayPlane < 2; GrayPlane++) { // Sides DrawLine(x,y,SIDE+x,y,A_NORMAL); DrawLine(x,y,x,SIDE+y,A_NORMAL); DrawLine(SIDE+x,SIDE+y,SIDE+x,y,A_NORMAL); DrawLine(SIDE+x,SIDE+y,x,SIDE+y,A_NORMAL); } } // Draw Values - Draws the numbers in the correct place for a piece at x,y // Note: uses the SIDE, ?_OFFX, and ?_OFFY defines void DrawValues(int x, int y, int myPiece) { char msg[2]; int GrayPlane = 0; sprintf(msg,"%d",myPiece); for (GrayPlane = 0; GrayPlane < 2; GrayPlane++) { GraySetAMSPlane(GrayPlane); DrawStr(x+2,y+2,msg,A_NORMAL); } } // DrawPiece - Draws the piece with the values in place void DrawPiece(int row, int col, int myPiece) { ErasePiece(row,col); DrawBlock(SIDE*col+L_BOARDX,SIDE*row+L_BOARDY); if (myPiece != -1) DrawValues(SIDE*col+L_BOARDX,SIDE*row+L_BOARDY,myPiece); } // SetupDisplay - Draws the current gameboard to the screen void SetupDisplay() { int GrayPlane; int i, j; for (GrayPlane = 0; GrayPlane < 2; GrayPlane++) { GraySetAMSPlane(GrayPlane); ClrScr(); // Draw the playing board for (i=0; i < SIZE; i++) { for (j=0;j < SIZE;j++) { DrawBlock(SIDE*i,SIDE*j); } } // Draw the pieces for (i=0;i= 3 && x < 6) && (y < 3 || y >= 6)) || ((x < 3 || x >= 6) && (y >= 3 && y < 6))) { GraySetAMSPlane(LIGHT_PLANE); ScrRectFill(&(SCR_RECT){{SIDE*y+L_BOARDX+1,SIDE*x+L_BOARDY+1,SIDE*y+L_BOARDX+SIDE-1,SIDE*x+L_BOARDY+SIDE-1}},&(SCR_RECT){{0,0,160,93}},A_NORMAL); GraySetAMSPlane(DARK_PLANE); ScrRectFill(&(SCR_RECT){{SIDE*y+L_BOARDX+1,SIDE*x+L_BOARDY+1,SIDE*y+L_BOARDX+SIDE-1,SIDE*x+L_BOARDY+SIDE-1}},&(SCR_RECT){{0,0,160,93}},A_REVERSE); } else { for (GrayPlane = 0; GrayPlane < 2; GrayPlane++) { GraySetAMSPlane(GrayPlane); ScrRectFill(&(SCR_RECT){{SIDE*y+L_BOARDX+1,SIDE*x+L_BOARDY+1,SIDE*y+L_BOARDX+SIDE-1,SIDE*x+L_BOARDY+SIDE-1}},&(SCR_RECT){{0,0,160,93}},A_REVERSE); } } } // ValidMove - Return 1 if the move is allowable int ValidMove (int row, int col, int myPiece) { int i,j, sqr, sqc; for (i=0; i < SIZE; i++) { if (gameboard[i][col] == myPiece && i != row) return FALSE; } for (j=0; j < SIZE; j++) { if (gameboard[row][j] == myPiece && j != col) return FALSE; } sqr = row / 3; sqc = col / 3; for (i=3*sqr;i<(3*(sqr+1));i++) { for (j=3*sqc;j<(3*(sqc+1));j++) { if (gameboard[i][j] == myPiece && i != row && j != col) return FALSE; } } return TRUE; } // CheckWin - Returns 1 if the puzzle has been solved int CheckWin() { int i,j; for (i=0; i SIZE-1) row = 0; ToggleCursor(row,col); } else if (key == KEY_RIGHT) { ToggleCursor(row,col); col++; if (col > SIZE-1) col = 0; ToggleCursor(row,col); } else if (key == KEY_LEFT) { ToggleCursor(row,col); col--; if (col < 0) col = SIZE-1; ToggleCursor(row,col); } else if (key >= '0' && key <= '9') // Handle number presses { currentPiece = key - '0'; if (currentPiece == 0 && !(locked[row][col])) { gameboard[row][col] = -1; ErasePiece(row, col); ToggleCursor(row,col); } else if (ValidMove(row,col,currentPiece) && !(locked[row][col])) { gameboard[row][col] = currentPiece; DrawPiece(row,col,currentPiece); ToggleCursor(row,col); } if (CheckWin()) { GrayOff(); ClrScr(); gamestate = paused; // Stop the clock // Set the user's time myScore.min = minutes; myScore.sec = seconds; // Get the user's name DialogAddTitle (handle, "YOU WIN!", BT_OK, BT_CANCEL); DialogAddText (handle, 3, 20, "Enter name (max. 10 chars)"); DialogAddRequest (handle, 3, 30, "Your name", 0, 10, 14); strcpy(debugmsg,'\0'); while (strlen(debugmsg) == 0) { if (DialogDo (handle, CENTER, CENTER, debugmsg, NULL) == KEY_ENTER) strcpy(myScore.name,debugmsg); } HeapFree (handle); // Update the highscores list GetScores(highscores); // Populate the highscores list UpdateScores(myScore, highscores); // Add the new entry to the list gamestate = gameover; // End the game } } } } } gamestate = gameover; // The beat just rocks me, don't try to stop me, throw it up and go... don't clock me! ;) Turns the clock off GrayOff(); ClrScr(); // Show the highscores list GetScores(highscores); PrintScores(highscores); // Wait for a keypress before quitting while(!kbhit()); key = ngetchx(); GrayOff(); SetIntVec(AUTO_INT_5, oldint5); }