1
0
vt100-games/Blocks/blocks.c

880 lines
16 KiB
C

/* 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.h> /* MESCC header & runtime */
#include <string.h> /* We need strlen() */
#include <ctype.h> /* We need toupper() */
#include <sprintf.h> /* We need sprintf() */
#ifndef NULL
#define NULL 0
#endif
#ifndef WORD
#define WORD unsigned int
#endif
#include <kslib.h> /* Screen output for CP/M */
#include <kslibfun.h> /* 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 <<char *board[] -- MESCC things>> */
WORD board_sel[BOARD_ROWS]; /* Array for blocks selections <<id>> */
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);
}