/* BLOCKS! Game from Floppy Software for MESCC - Mike's Enhanced Small C Compiler. Copyright (c) 2012 Miguel I. Garcia Lopez, Valencia, Spain. 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, 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, 675 Mass Ave, Cambridge, MA 02139, USA. Revisions: 02 Jul 2012 : Generic CP/M version 1.0 for 24x80 VT52 screen. 11 Mar 2020 : modified to VT100 version by acn@acn.wtf */ #include /* MESCC header & runtime */ #include /* We need strlen() */ #include /* We need toupper() */ #include /* We need sprintf() */ #ifndef NULL #define NULL 0 #endif #ifndef WORD #define WORD unsigned int #endif #include /* Screen output for CP/M */ #include /* More functions for kslib */ /* GAME DEFS */ #define BOARD_ROW 6 /* Screen position for board */ #define BOARD_COL 25 /* id */ #define BORDER_ROW 5 /* Screen position for border */ #define BORDER_COL 24 /* id */ #define BOARD_ROWS 10 /* Board rows */ #define BOARD_COLS 10 /* Board columns */ #define BLOCK_COLS 3 /* Block size */ #define BLOCK_ROWS 1 /* id */ #define BLOCK_EMPTY ' ' /* Value if block is empty */ #define LEVEL_ROW 2 /* Screen position for level info - used to show blocks killed */ #define LEVEL_COL 2 /* id */ #define SCORE_ROW 2 /* Screen position for score info */ #define SCORE_COL 73 /* id */ #define K_UP '8' /* Key up */ #define K_DOWN '2' /* Key down */ #define K_LEFT '4' /* Key left */ #define K_RIGHT '6' /* Key right */ #define K_EXIT 'Q' /* Key exit */ #define K_SELECT '7' /* Key select */ #define K_KILL '9' /* Key kill */ #define K_GRAV_UP 'E' /* Key gravity up */ #define K_GRAV_LEFT 'S' /* Key gravity left */ #define K_GRAV_RIGHT 'D' /* Key gravity right */ #define K_GRAV_DOWN 'X' /* Key gravity down */ #define RESET "\e[0m" /* reset all attributes */ #define RED "\e[31m" #define GREEN "\e[32m" #define YELLOW "\e[33m" #define BLUE "\e[34;1m" #define MAGENTA "\e[35m" #define CYAN "\e[36m" #define WHITE "\e[37m" /* VARIABLES */ WORD board[BOARD_ROWS]; /* Array for blocks values <> */ WORD board_sel[BOARD_ROWS]; /* Array for blocks selections <> */ int score, /* Score */ blocks, /* Blocks remaining */ randindex, /* Random number used for generate blocks values */ selected, /* Number of blocks selected */ add_to_score, /* How much can I add to score if I kill selected blocks */ automode, /* Non zero if automode is selected */ ChkPlsCtr, /* CheckPlease variable */ ChkPlsMul, /* id */ ChkPlsVal; /* id */ /* MAIN FUNCTION */ main(argc, argv) int argc, argv[]; { int opt; /* SETUP KSLIB ENVIRONMENT */ KbdScrStart(); /* SETUP SOME THINGS */ board[0]="1234567890"; board_sel[0]="1234567890"; board[1]="1234567890"; board_sel[1]="1234567890"; board[2]="1234567890"; board_sel[2]="1234567890"; board[3]="1234567890"; board_sel[3]="1234567890"; board[4]="1234567890"; board_sel[4]="1234567890"; board[5]="1234567890"; board_sel[5]="1234567890"; board[6]="1234567890"; board_sel[6]="1234567890"; board[7]="1234567890"; board_sel[7]="1234567890"; board[8]="1234567890"; board_sel[8]="1234567890"; board[9]="1234567890"; board_sel[9]="1234567890"; /* PLAY THE GAME, PLEASE */ while((opt=Menu())) { if(opt==1) Play(); } /* GAME IS OVER - BEST TO CLEAN THE SCREEN */ ScrClr(); ScrCurOn(); /* WE SAY GOOD BYE TO KSLIB ENVIRONMENT & WE SAY HELLO TO CP/M */ KbdScrEnd(); } /* MENU - RETURN 0 IF WE WANT TO END GAME, 1 IF WE WANT TO PLAY GAME, OR 2 IF WE WANT TO RETURN TO MENU */ Menu() { char str[80]; ScrClr(); ScrCurOff(); sprintf(str, "%sB%sl%so%sc%sk%ss%s", RED, GREEN, BLUE, CYAN, MAGENTA, WHITE, RESET); ScrOutStrRC(1, 37, str); ScrTitle(3, "v1.1"); ScrTitle(5, "(c) 2012 Floppy Software"); ScrTitle(6, "VT-100 version (c) 2020 acn128"); ScrOutStrRC(10, 25, "1 : Play game in normal mode"); ScrOutStrRC(12, 25, "2 : Play game in automatic mode"); ScrOutStrRC(14, 25, "3 : Show help"); ScrOutStrRC(18, 25, "Q : Quit game"); ScrTitle(SCR_ROWS-2, "Select your choice"); randindex=0; /* This is used to generate a random number */ while(1) { while(!KbdChk()) { if(++randindex==BOARD_ROWS*BOARD_COLS) randindex=0; } switch(toupper(KbdIn())) { case '1' : automode=0; return 1; case '2' : automode=1; return 1; case '3' : Help(); return 2; case K_EXIT : return 0; } } } /* HELP -- SHOW HELP */ Help() { ScrClr(); ScrTitle( 0, "Blocks"); ScrTitle( 1, "------"); ScrTitle( 3, "The object of the game is to remove all the"); ScrTitle( 4, "blocks of the board."); ScrTitle( 6, "You can select just one block, or a group of"); ScrTitle( 7, "blocks of the same type."); ScrTitle( 9, "Then, if you are in the normal mode, you can place all"); ScrTitle(10, "the blocks against a side (top, bottom, left or right)."); ScrTitle(12, "If you are in the automatic mode, the blocks rows will be"); ScrTitle(13, "placed on the bottom side of the board, and the columns"); ScrTitle(14, "will be placed on the middle of the board."); ScrTitle(16, "The partial score is the square of the number of"); ScrTitle(17, "blocks selected."); ScrTitle(19, "The special block 'X', multiplies that result by 10."); ScrTitle(21, "Good luck!"); ScrTitle(23, "PRESS ANY KEY"); KbdIn(); } /* PLAY THE GAME, PLEASE */ Play() { int row, col, run, val, key; char str[80]; /* SETUP VARIABLES & BOARD */ row=col=selected=score=0; run=1; blocks=BOARD_ROWS*BOARD_COLS; SetupBoard(); /* DRAW SCREEN */ ScrClr(); ScrBox(0, 0, SCR_ROWS-1, SCR_COLS, NULL); sprintf(str, "| %sB%sL%sO%sC%sK%sS%s |", RED, GREEN, BLUE, CYAN, MAGENTA, WHITE, RESET); ScrOutStrRC(0, 35, str); ScrOutStrRC(LEVEL_ROW, LEVEL_COL, "Blocks"); PrintBlocks(); ScrOutStrRC(SCORE_ROW, SCORE_COL, "Score"); PrintScore(); ScrBox(BORDER_ROW, BORDER_COL, BOARD_ROWS*BLOCK_ROWS+2, BOARD_COLS*BLOCK_COLS+2, NULL); PaintBoard(); if(!automode) { sprintf(str, "%c", K_GRAV_UP); ScrTitle(BORDER_ROW-1, str); sprintf(str, "%c", K_GRAV_DOWN); ScrTitle(BORDER_ROW+BOARD_ROWS+2, str); sprintf(str, "%c", K_GRAV_LEFT); ScrOutStrRC(BORDER_ROW+5, BORDER_COL-2, str); sprintf(str, "%c", K_GRAV_RIGHT); ScrOutStrRC(BORDER_ROW+5, BORDER_COL+BOARD_COLS*BLOCK_COLS+3, str); } sprintf(str, " %c", K_UP); ScrOutStrRC(BORDER_ROW+3, 7, str); ScrOutStrRC(BORDER_ROW+4, 7, " |"); sprintf(str, "%c --+-- %c", K_LEFT, K_RIGHT); ScrOutStrRC(BORDER_ROW+5, 7, str); ScrOutStrRC(BORDER_ROW+6, 7, " |"); sprintf(str, " %c", K_DOWN); ScrOutStrRC(BORDER_ROW+7, 7, str); sprintf(str, "%c> Select blocks", K_SELECT); ScrOutStrRC(BORDER_ROW+4, BORDER_COL+BOARD_COLS*BLOCK_COLS+8, str); sprintf(str, "%c> Kill blocks", K_KILL); ScrOutStrRC(BORDER_ROW+5, BORDER_COL+BOARD_COLS*BLOCK_COLS+8, str); sprintf(str, "%c> Quit", K_EXIT); ScrOutStrRC(BORDER_ROW+6, BORDER_COL+BOARD_COLS*BLOCK_COLS+8, str); /* PLAY GAME */ while(run) { /* DRAW CURSOR */ ScrSetRC(BOARD_ROW+row*BLOCK_ROWS, BOARD_COL+col*BLOCK_COLS+1); ScrCurOn(); /* USER ACTION */ key=toupper(KbdIn()); ScrCurOff(); switch(key) { case K_UP: if(row) --row; break; case K_DOWN: if(++row==BOARD_ROWS) --row; break; case K_LEFT: if(col) --col; break; case K_RIGHT: if(++col==BOARD_COLS) -- col; break; case K_SELECT: /* CHECK FOR VALID BLOCKS */ val=GetBlock(row, col); if(val==BLOCK_EMPTY || val=='X') break; /* CHECK IF ALREADY SELECTED */ if(TstSelBlock(row, col)) break; /* UNSELECT PREVIOUS SELECTION */ if(selected) { /* HEY! TOO MUCH WORK IF ONLY 1 SELECTED */ UnSelAllBlocks(); PaintBoard(); } /* SELECT */ selected=CheckPlease(row, col); add_to_score=selected*selected*ChkPlsMul; PrintSelec(); break; case K_KILL: if(selected) { /* HEY! TOO MUCH WORK IF ONLY 1 SELECTED */ KillSelBlocks(); score+=add_to_score; PrintScore(); blocks-=selected; PrintBlocks(); selected=add_to_score=0; PrintSelec(); if(!blocks) { GameOver(); run=0; } else if(automode) { DoAutoMode(); PaintBoard(); } } break; case K_GRAV_UP: if(automode) break; GravityUp(); if(selected) { UnSelAllBlocks(); selected=0; } PaintBoard(); break; case K_GRAV_DOWN: if(automode) break; GravityDown(); if(selected) { UnSelAllBlocks(); selected=0; } PaintBoard(); break; case K_GRAV_LEFT: if(automode) break; GravityLeft(); if(selected) { UnSelAllBlocks(); selected=0; } PaintBoard(); break; case K_GRAV_RIGHT: if(automode) break; GravityRight(); if(selected) { UnSelAllBlocks(); selected=0; } PaintBoard(); break; case K_EXIT : PrintMsg("ARE YOU SURE Y/N?"); if(toupper(KbdIn())=='Y') run=0; else PrintMsg(""); break; } } } /* GAME IS OVER */ GameOver() { ScrTitle(BORDER_ROW+4, "*** GAME OVER ***"); PrintMsg("PRESS ANY KEY"); KbdIn(); } /* SETUP BOARD */ SetupBoard() { int r, c; unsigned char *values; /* 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 (width 100) */ values="4321242333123432411123443444432111234332222343331214422444441243444132222311114242423111133222441111"; for(r=0; r!=BOARD_ROWS; ++r) { for(c=0; c!=BOARD_COLS; ++c) { SetBlock(r, c, values[randindex]); UnSelBlock(r, c); if(++randindex==BOARD_ROWS*BOARD_COLS) randindex=0; } } if(randindex<25) SetBlock(2, 5, 'X'); else if(randindex<50) SetBlock(9, 4, 'X'); else if(randindex<75) SetBlock(4, 8, 'X'); else SetBlock(7, 3, 'X'); } /* SET BLOCK WITH VALUE */ SetBlock(row, col, value) int row, col, value; { char *ptr; ptr=board[row]; ptr[col]=value; } /* GET BLOCK VALUE */ GetBlock(row, col) int row, col; { char *ptr; ptr=board[row]; return ptr[col]; } /* SELECT BLOCK */ SelBlock(row, col) int row, col; { char *ptr; ptr=board_sel[row]; ptr[col]=1; } /* UNSELECT BLOCK */ UnSelBlock(row, col) int row, col; { char *ptr; ptr=board_sel[row]; ptr[col]=0; } /* UNSELECT ALL BLOCKS */ UnSelAllBlocks() { int r, c; for(r=0; r!=BOARD_ROWS; ++r) for(c=0; c!=BOARD_COLS; ++c) UnSelBlock(r, c); } /* TEST IF A BLOCK IS SELECTED */ TstSelBlock(row, col) int row, col; { char *ptr; ptr=board_sel[row]; return ptr[col]; } /* KILL SELECTED BLOCKS */ KillSelBlocks() { int r, c; for(r=0; r!=BOARD_ROWS; ++r) { for(c=0; c!=BOARD_COLS; ++c) { if(TstSelBlock(r, c)) { SetBlock(r, c, BLOCK_EMPTY); UnSelBlock(r, c); PaintBlock(r, c); } } } } /* PAINT BLOCK */ PaintBlock(row, col) int row, col; { int selected; ScrSetRC(BOARD_ROW+row*BLOCK_ROWS, BOARD_COL+col*BLOCK_COLS); ScrOut((selected=TstSelBlock(row, col)) ? ':' : ' '); switch(GetBlock(row, col)) { case '1' : ScrOutStr(RED); ScrOut('$'); ScrOutStr(RESET); break; case '2' : ScrOutStr(GREEN); ScrOut('O'); ScrOutStr(RESET); break; case '3' : ScrOutStr(BLUE); ScrOut('+'); ScrOutStr(RESET); break; case '4' : ScrOutStr(MAGENTA); ScrOut('#'); ScrOutStr(RESET); break; case 'X' : ScrOutStr(WHITE); ScrOut('X'); ScrOutStr(RESET); break; case BLOCK_EMPTY : ScrOutStr(RESET); ScrOut(' '); break; default : ScrOutStr(RESET); ScrOut('?'); break; } ScrOut(selected ? ':' : ' '); } /* PAINT ALL BLOCKS */ PaintBoard() { int r, c; for(r=0; r!=BOARD_ROWS; ++r) for(c=0; c!=BOARD_COLS; ++c) PaintBlock(r, c); } /* CHECK WHAT BLOCKS CAN BE SELECTED - SETUP THREE VARIABLES */ CheckPlease(row, col) int row, col; { ChkPlsCtr=0; ChkPlsMul=1; ChkPlsVal=GetBlock(row, col); ChkPls2(row, col); return ChkPlsCtr; } ChkPls2(row, col) int row, col; { int val; SelBlock(row, col); PaintBlock(row, col); if(GetBlock(row, col)=='X') ChkPlsMul=10; if(row) { if(((val=GetBlock(row-1, col))==ChkPlsVal || val=='X') && (!TstSelBlock(row-1, col))) ChkPls2(row-1, col); } if(col<(BOARD_COLS-1)) { if(((val=GetBlock(row, col+1))==ChkPlsVal || val=='X') && (!TstSelBlock(row, col+1))) ChkPls2(row, col+1); } if(row<(BOARD_ROWS-1)) { if(((val=GetBlock(row+1, col))==ChkPlsVal || val=='X') && (!TstSelBlock(row+1, col))) ChkPls2(row+1, col); } if(col) { if(((val=GetBlock(row, col-1))==ChkPlsVal || val=='X') && (!TstSelBlock(row, col-1))) ChkPls2(row, col-1); } ++ChkPlsCtr; } /* GRAVITY DOWN (BOARD BOTTOM) */ GravityDown() { int r, c, i, blk, row[BOARD_ROWS]; for(c=0; c!=BOARD_COLS; ++c) { for((r=i=BOARD_ROWS-1); r!=-1; --r) { if((blk=GetBlock(r, c))!=BLOCK_EMPTY) row[i--]=blk; } while(i!=-1) row[i--]=BLOCK_EMPTY; for(r=0; r!=BOARD_ROWS; ++r) SetBlock(r, c, row[r]); } } /* GRAVITY UP (WE PUT BLOCKS ON BOARD TOP) */ GravityUp() { int r, c, i, blk, row[BOARD_ROWS]; for(c=0; c!=BOARD_COLS; ++c) { for((r=i=0); r!=BOARD_ROWS; ++r) { if((blk=GetBlock(r, c))!=BLOCK_EMPTY) row[i++]=blk; } while(i!=BOARD_ROWS) row[i++]=BLOCK_EMPTY; for(r=0; r!=BOARD_ROWS; ++r) SetBlock(r, c, row[r]); } } /* GRAVITY LEFT (WE PUT BLOCKS ON BOARD LEFT) */ GravityLeft() { int r, c, i, blk, col[BOARD_ROWS]; for(r=0; r!=BOARD_ROWS; ++r) { for((c=i=0); c!=BOARD_COLS; ++c) { if((blk=GetBlock(r, c))!=BLOCK_EMPTY) col[i++]=blk; } while(i!=BOARD_COLS) col[i++]=BLOCK_EMPTY; for(c=0; c!=BOARD_COLS; ++c) SetBlock(r, c, col[c]); } } /* GRAVITY RIGHT (WE PUT BLOCKS ON BOARD RIGHT) */ GravityRight() { int r, c, i, blk, col[BOARD_ROWS]; for(r=0; r!=BOARD_ROWS; ++r) { for((c=i=BOARD_COLS-1); c!=-1; --c) { if((blk=GetBlock(r, c))!=BLOCK_EMPTY) col[i--]=blk; } while(i!=-1) col[i--]=BLOCK_EMPTY; for(c=0; c!=BOARD_COLS; ++c) SetBlock(r, c, col[c]); } } /* AUTOMATIC MODE (WE PUT BLOKS FIRST ON BOARD BOTTOM, THEN WE CENTER THE COLUMNS) */ DoAutoMode() { int r, c, i, pos, cols; GravityDown(); /* COPY NON EMPTY COLUMNS TO THE LEFT */ for(i=c=0; c!=BOARD_COLS; ++c) { if(GetBlock(BOARD_ROWS-1, c) != BLOCK_EMPTY) { if(i!=c) { for(r=0; r!=BOARD_ROWS; ++r) SetBlock(r, i, GetBlock(r, c)); } ++i; } } /* DO NOTHING IF ALL COLUMNS ARE NON EMPTY */ if(i==BOARD_COLS) return; /* CALCULATE WHERE TO PUT THE COLUMNS */ pos=(BOARD_COLS-i)/2; /* COPY COLUMNS WITHOUT OVERLAP */ cols=i; for(c=pos+i-1; i!=-1; --c) { --i; for(r=0; r!=BOARD_ROWS; ++r) SetBlock(r, c, GetBlock(r, i)); } /* EMPTY COLUMNS TO THE LEFT */ for(c=0; c!=pos; ++c) for(r=0; r!=BOARD_ROWS; ++r) SetBlock(r, c, BLOCK_EMPTY); /* EMPTY COLUMNS TO THE RIGHT */ for(c=pos+cols; c!=BOARD_COLS; ++c) for(r=0; r!=BOARD_ROWS; ++r) SetBlock(r, c, BLOCK_EMPTY); } /* PRINT HOW MANY BLOCKS ARE REMAINING */ PrintBlocks() { char str[6]; sprintf(str, "%d ", blocks); ScrOutStrRC(LEVEL_ROW+1, LEVEL_COL, str); } /* PRINT SCORE */ PrintScore() { char str[6]; sprintf(str, "%5d", score); ScrOutStrRC(SCORE_ROW+1, SCORE_COL, str); } /* PRINT SELECTION INFO. */ PrintSelec() { char str[7]; sprintf(str, "x%d ", selected); ScrOutStrRC(LEVEL_ROW+3, LEVEL_COL, str); sprintf(str, "%5d+", add_to_score); ScrOutStrRC(SCORE_ROW+3, SCORE_COL-1, str); } /* PRINT TEXT STRING (MESSAGE) */ PrintMsg(str) char *str; { ScrSetRC(SCR_ROWS-4, 2); ScrOutRpt(' ', SCR_COLS-4); ScrTitle(SCR_ROWS-4, str); }