2020-04-20 16:23:56 +02:00
|
|
|
|
/* FIND THAT MINE!
|
|
|
|
|
|
|
|
|
|
A MineSweeper clone for CRTs and CP/M.
|
|
|
|
|
|
|
|
|
|
Derived from the plain CP/M version.
|
|
|
|
|
|
|
|
|
|
Copyright (c) 2012-2020 Miguel Garcia / FloppySoftware, Spain.
|
2020-04-21 16:16:06 +02:00
|
|
|
|
Color version by Anna Christina Naß <acn@acn.wtf>
|
2020-04-20 16:23:56 +02:00
|
|
|
|
|
|
|
|
|
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 3, 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.
|
|
|
|
|
|
2020-04-21 16:16:06 +02:00
|
|
|
|
Author's contact:
|
|
|
|
|
|
|
|
|
|
www.floppysoftware.es
|
|
|
|
|
cpm-connections.blogspot.com
|
|
|
|
|
floppysoftware@gmail.com
|
|
|
|
|
|
2020-04-20 16:23:56 +02:00
|
|
|
|
To compile with MESCC:
|
|
|
|
|
|
|
|
|
|
cc ftm
|
|
|
|
|
ccopt ftm
|
|
|
|
|
zsm ftm
|
|
|
|
|
hextocom ftm
|
|
|
|
|
|
2020-04-21 16:16:06 +02:00
|
|
|
|
Usage:
|
|
|
|
|
|
|
|
|
|
ftm [tty_name]
|
2020-04-22 12:11:02 +02:00
|
|
|
|
If no tty_name is given, VT100COL is used
|
2020-04-21 16:16:06 +02:00
|
|
|
|
|
|
|
|
|
Revisions:
|
|
|
|
|
|
|
|
|
|
11 Sep 2012 : v1.0 : Initial version for Amstrad PCW & Samaruc GUI.
|
|
|
|
|
26 Jul 2013 : v1.2 : Adapted to Samaruc v1.5.
|
|
|
|
|
16 Feb 2014 : v1.3 : Adapted to last changes in Samaruc.
|
|
|
|
|
09 Jul 2014 : v1.4 : CP/M version.
|
|
|
|
|
07 Apr 2020 : v2.0 : Adapted to KS library for CP/M.
|
|
|
|
|
21 Apr 2020 : v2.1 : Added color option (by Anna Christina Naß <acn@acn.wtf>
|
|
|
|
|
|
2020-04-20 16:23:56 +02:00
|
|
|
|
Notes:
|
|
|
|
|
|
|
|
|
|
LEVELS: 1 2 3 NOTES
|
|
|
|
|
|
|
|
|
|
ROWS: 8 8 8
|
|
|
|
|
COLS: 8 12 16
|
|
|
|
|
SQUARES: 64 96 128 ROWS * COLS
|
|
|
|
|
MINES: 8 12 16 SQUARES / 8 (1 MINES EACH 8 SQUARES)
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* Do you want debug tools?
|
|
|
|
|
*/
|
|
|
|
|
#define DEBUG_MINES 0
|
|
|
|
|
|
|
|
|
|
/* MESCC libraries
|
|
|
|
|
*/
|
|
|
|
|
#include <mescc.h>
|
|
|
|
|
#include <conio.h>
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <sprintf.h>
|
|
|
|
|
|
2020-04-21 16:16:06 +02:00
|
|
|
|
/* KS libraries
|
2020-04-20 16:23:56 +02:00
|
|
|
|
*/
|
|
|
|
|
#include "ks.h"
|
|
|
|
|
|
|
|
|
|
/* DEFs for BOARD
|
|
|
|
|
*/
|
|
|
|
|
#define MAX_ROWS 8 /* Max. number of rows */
|
|
|
|
|
#define MAX_COLS 16 /* Max. number of columns */
|
|
|
|
|
#define MAX_SQ 128 /* Max. number of squares (MAX_ROWS * MAX_COLS) */
|
|
|
|
|
#define MAX_BUF 512 /* MAX_SQ * 4 */
|
|
|
|
|
|
|
|
|
|
/* DEFs for SPRITES
|
|
|
|
|
*/
|
|
|
|
|
#define SPR_MINE '*' /* Mine */
|
|
|
|
|
#define SPR_FLAG 'P' /* Flag */
|
|
|
|
|
#define SPR_UNK '?' /* Unknown */
|
|
|
|
|
#define SPR_ERR 'X' /* Flag error */
|
|
|
|
|
#define SPR_BLANK ' ' /* Blank */
|
|
|
|
|
|
2020-04-22 12:11:02 +02:00
|
|
|
|
/* DEFs for border elements (corners, top+bottom line, right+left line)
|
|
|
|
|
* either the normal ones (7-bit compatible) are used or codepage 437
|
|
|
|
|
*/
|
|
|
|
|
#define CP437
|
|
|
|
|
#ifdef CP437
|
|
|
|
|
#define B_TOP_LEFT 201
|
|
|
|
|
#define B_TOP_RIGHT 187
|
|
|
|
|
#define B_BOTTOM_LEFT 200
|
|
|
|
|
#define B_BOTTOM_RIGHT 188
|
|
|
|
|
#define B_TOP_BOTTOM 205
|
|
|
|
|
#define B_LEFT_RIGHT 186
|
|
|
|
|
#else
|
|
|
|
|
#define B_TOP_LEFT '+'
|
|
|
|
|
#define B_TOP_RIGHT '+'
|
|
|
|
|
#define B_BOTTOM_LEFT '+'
|
|
|
|
|
#define B_BOTTOM_RIGHT '+'
|
|
|
|
|
#define B_TOP_BOTTOM '-'
|
|
|
|
|
#define B_LEFT_RIGHT '|'
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-04-20 16:23:56 +02:00
|
|
|
|
/* Global variables
|
|
|
|
|
*/
|
|
|
|
|
int brd_rows, /* Board size in rows */
|
|
|
|
|
brd_cols; /* Board size in columns */
|
|
|
|
|
|
|
|
|
|
WORD mines_row[MAX_ROWS], /* Map for mines */
|
|
|
|
|
visib_row[MAX_ROWS], /* Map for visible squares */
|
|
|
|
|
flags_row[MAX_ROWS], /* Map for squares with flag */
|
|
|
|
|
count_row[MAX_ROWS]; /* Map for counters */
|
|
|
|
|
|
|
|
|
|
BYTE sq_buf[MAX_BUF]; /* Buffer to hold the above maps */
|
|
|
|
|
|
|
|
|
|
int row_off[8], /* Array to help in calculation */
|
|
|
|
|
col_off[8]; /* of adjacent squares */
|
|
|
|
|
|
|
|
|
|
int gameover, /* NZ if game is over */
|
|
|
|
|
dead, /* NZ if player is dead */
|
|
|
|
|
level, /* Current level */
|
|
|
|
|
viscnt, /* Number of visible squares in board */
|
|
|
|
|
flgcnt, /* Number of flags in board */
|
|
|
|
|
squares, /* Number of squares in board */
|
|
|
|
|
mines, /* Number of mines in board */
|
|
|
|
|
random; /* Random number */
|
|
|
|
|
|
|
|
|
|
/* Code starts here
|
|
|
|
|
*/
|
|
|
|
|
main(argc, argv)
|
|
|
|
|
int argc, argv[]; /* char *argv[] - unsupported by MESCC */
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
int tty_n;
|
|
|
|
|
int *tty_s; /* char *[] */
|
|
|
|
|
|
|
|
|
|
/* Init KS
|
|
|
|
|
*/
|
2020-04-21 16:16:06 +02:00
|
|
|
|
if(argc == 1) {
|
|
|
|
|
KsHello(KS_VT100COL);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if(strcmp(argv[1], "/?")==0) {
|
|
|
|
|
tty_s = KsGetNames();
|
|
|
|
|
tty_n = KsGetHowMany();
|
|
|
|
|
|
|
|
|
|
puts("Usage: ftm [tty_name]\n");
|
|
|
|
|
puts("Supported TTY names:");
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < tty_n; ++i) {
|
|
|
|
|
putchar('\t'); puts(tty_s[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
puts("If no tty_name is given, VT100COL (VT100 with color) is used.");
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if(KsHello(KsGetCode(argv[1])) == -1) {
|
|
|
|
|
puts("Unknown TTY name.\n\nRun 'ftm' to know supported TTYs.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-20 16:23:56 +02:00
|
|
|
|
|
|
|
|
|
/* Setup
|
|
|
|
|
*/
|
|
|
|
|
Setup();
|
|
|
|
|
|
|
|
|
|
/* Play game
|
|
|
|
|
*/
|
|
|
|
|
Play();
|
|
|
|
|
|
|
|
|
|
/* The end
|
|
|
|
|
*/
|
|
|
|
|
PrStr("\n\n\tBye, bye!\n\n\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Setup program
|
|
|
|
|
*/
|
|
|
|
|
Setup()
|
|
|
|
|
{
|
|
|
|
|
int i; BYTE *p;
|
|
|
|
|
|
|
|
|
|
/* Build array to help in calculation of adjacent squares
|
|
|
|
|
*/
|
|
|
|
|
row_off[0]=-1; col_off[0]= 0; /* Up */
|
|
|
|
|
row_off[1]=-1; col_off[1]=+1; /* Upper Right */
|
|
|
|
|
row_off[2]= 0; col_off[2]=+1; /* Right */
|
|
|
|
|
row_off[3]=+1; col_off[3]=+1; /* Lower Right */
|
|
|
|
|
row_off[4]=+1; col_off[4]= 0; /* Down */
|
|
|
|
|
row_off[5]=+1; col_off[5]=-1; /* Lower Left */
|
|
|
|
|
row_off[6]= 0; col_off[6]=-1; /* Left */
|
|
|
|
|
row_off[7]=-1; col_off[7]=-1; /* Upper Left */
|
|
|
|
|
|
|
|
|
|
/* Setup maps
|
|
|
|
|
*/
|
|
|
|
|
p = sq_buf;
|
|
|
|
|
|
|
|
|
|
for(i = 0; i < MAX_ROWS; ++i)
|
|
|
|
|
{
|
|
|
|
|
mines_row[i]=p; p+=MAX_COLS;
|
|
|
|
|
visib_row[i]=p; p+=MAX_COLS;
|
|
|
|
|
flags_row[i]=p; p+=MAX_COLS;
|
|
|
|
|
count_row[i]=p; p+=MAX_COLS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Other
|
|
|
|
|
*/
|
|
|
|
|
random = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Show copyright, etc.
|
|
|
|
|
*/
|
|
|
|
|
Copyright()
|
|
|
|
|
{
|
|
|
|
|
int r;
|
2020-04-21 16:16:06 +02:00
|
|
|
|
char str[100];
|
2020-04-20 16:23:56 +02:00
|
|
|
|
|
|
|
|
|
KsClear();
|
|
|
|
|
|
2020-04-21 16:16:06 +02:00
|
|
|
|
if(KsCan(KS_CAN_COLOR)) {
|
|
|
|
|
//sprintf(str, "%sFind %sThat %sMine%s!%s\n", KsFgCol(RED), KsFgCol(CYN), KsFgCol(BLU), KsFgCol(YEL), KsResetAttr());
|
|
|
|
|
KsPosCursor(r = 2, 32);
|
|
|
|
|
KsFgCol(BRED); KsPutStr("Find "); KsFgCol(BCYN); KsPutStr("That "); KsFgCol(BBLU); KsPutStr("Mine"); KsFgCol(BYEL); KsPutStr("!\n"); KsResetAttr();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
KsCenterStr(r = 2, "Find That Mine!\n");
|
|
|
|
|
}
|
|
|
|
|
KsCenterStr(++r , "v2.1 for CP/M & Z80\n\n");
|
2020-04-20 16:23:56 +02:00
|
|
|
|
++r;
|
|
|
|
|
KsCenterStr(++r ,"A minesweeper type game.\n");
|
|
|
|
|
++r;
|
|
|
|
|
KsCenterStr(++r ,"(c) 2012-2020 Miguel Garcia / Floppy Software, Spain.\n");
|
|
|
|
|
KsCenterStr(++r ,"www.floppysoftware.es\n");
|
2020-04-21 16:16:06 +02:00
|
|
|
|
KsCenterStr(++r ,"color version by acn@acn.wtf\n\n");
|
2020-04-20 16:23:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Select and play a level
|
|
|
|
|
*/
|
|
|
|
|
Play()
|
|
|
|
|
{
|
|
|
|
|
int run; char buf[2];
|
|
|
|
|
|
|
|
|
|
run = 1;
|
|
|
|
|
|
|
|
|
|
while(run)
|
|
|
|
|
{
|
|
|
|
|
Copyright();
|
|
|
|
|
|
|
|
|
|
PrStr("\t1 > Level 1 : 08 x 08 squares, 08 mines\n");
|
|
|
|
|
PrStr("\t2 > Level 2 : 08 x 12 squares, 12 mines\n");
|
|
|
|
|
PrStr("\t3 > Level 3 : 08 x 16 squares, 16 mines\n");
|
|
|
|
|
PrStr("\tQ > Quit game\n\n");
|
|
|
|
|
|
2020-04-22 12:11:02 +02:00
|
|
|
|
PrStr("\tYour Choice? ");
|
2020-04-20 16:23:56 +02:00
|
|
|
|
|
|
|
|
|
ReadLine(buf, 1);
|
|
|
|
|
|
|
|
|
|
switch(*buf)
|
|
|
|
|
{
|
|
|
|
|
case '1' : SetLevel(1); RunLevel(); break;
|
|
|
|
|
case '2' : SetLevel(2); RunLevel(); break;
|
|
|
|
|
case '3' : SetLevel(3); RunLevel(); break;
|
|
|
|
|
case 'Q' : run = 0; break;
|
|
|
|
|
default : break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Setup level
|
|
|
|
|
*/
|
|
|
|
|
SetLevel(lev)
|
|
|
|
|
int lev;
|
|
|
|
|
{
|
|
|
|
|
/* I'm thinking...
|
|
|
|
|
*/
|
|
|
|
|
PrStr("\n\n\tI'm thinking... ");
|
|
|
|
|
|
|
|
|
|
/* Setup level values
|
|
|
|
|
*/
|
|
|
|
|
level = lev; brd_rows = 8;
|
|
|
|
|
|
|
|
|
|
switch(level)
|
|
|
|
|
{
|
|
|
|
|
case 1 : brd_cols = 8; break;
|
|
|
|
|
case 2 : brd_cols = 12; break;
|
|
|
|
|
case 3 : brd_cols = 16; break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
squares = brd_rows * brd_cols;
|
|
|
|
|
|
|
|
|
|
mines = squares / 8;
|
|
|
|
|
|
|
|
|
|
gameover = dead = viscnt = flgcnt = 0;
|
|
|
|
|
|
|
|
|
|
/* Setup board
|
|
|
|
|
*/
|
|
|
|
|
ClrBoard();
|
|
|
|
|
|
|
|
|
|
/* Setup counters
|
|
|
|
|
*/
|
|
|
|
|
ClrCounts();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Play level
|
|
|
|
|
*/
|
|
|
|
|
RunLevel()
|
|
|
|
|
{
|
|
|
|
|
int row, col; char buf[4];
|
|
|
|
|
|
2020-04-22 12:11:02 +02:00
|
|
|
|
DrawBoard();
|
|
|
|
|
|
2020-04-20 16:23:56 +02:00
|
|
|
|
for(;;)
|
|
|
|
|
{
|
2020-04-22 12:11:02 +02:00
|
|
|
|
ShowPlayfield();
|
2020-04-20 16:23:56 +02:00
|
|
|
|
|
|
|
|
|
if(gameover) {
|
2020-04-22 12:11:02 +02:00
|
|
|
|
KsPosCursor(16,0);
|
|
|
|
|
PrStr("\tRETURN > Quit level");
|
|
|
|
|
PrChTimes(' ', 15);
|
|
|
|
|
PrStr("\n\t");
|
|
|
|
|
PrChTimes(' ', 45);
|
|
|
|
|
PrStr("\n\t");
|
|
|
|
|
PrChTimes(' ', 25);
|
|
|
|
|
PrStr("\n\n\t");
|
|
|
|
|
PrChTimes(' ', 15);
|
|
|
|
|
KsPosCursor(18,0);
|
|
|
|
|
PrStr("\t? ");
|
2020-04-20 16:23:56 +02:00
|
|
|
|
ReadLine(buf, 0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-22 12:11:02 +02:00
|
|
|
|
KsPosCursor(16,0);
|
|
|
|
|
PrStr("\trc / cr > Select (ie 0B or B0)\n");
|
|
|
|
|
PrStr("\trcF / crF > Set/remove flag (ie: 3DF or D3F)\n");
|
|
|
|
|
PrStr("\tQ > Quit level\n\n");
|
|
|
|
|
PrStr("\tYour Move? ");
|
|
|
|
|
KsPosCursor(20, 19);
|
2020-04-20 16:23:56 +02:00
|
|
|
|
|
|
|
|
|
ReadLine(buf, 3);
|
|
|
|
|
|
|
|
|
|
if(buf[0] >= '0' && buf[0] < '0' + brd_cols)
|
|
|
|
|
{
|
|
|
|
|
row = buf[0] - '0';
|
|
|
|
|
|
|
|
|
|
if(buf[1] >= 'A' && buf[1] < 'A' + brd_cols)
|
|
|
|
|
{
|
|
|
|
|
col = buf[1] - 'A';
|
|
|
|
|
|
|
|
|
|
if(!buf[2])
|
|
|
|
|
Choice(row, col);
|
|
|
|
|
else if(buf[2] == 'F' && !buf[3])
|
|
|
|
|
ChoiceFlag(row, col);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-22 12:11:02 +02:00
|
|
|
|
if(buf[1] >= '0' && buf[1] < '0' + brd_cols)
|
|
|
|
|
{
|
|
|
|
|
row = buf[1] - '0';
|
|
|
|
|
|
|
|
|
|
if(buf[0] >= 'A' && buf[0] < 'A' + brd_cols)
|
|
|
|
|
{
|
|
|
|
|
col = buf[0] - 'A';
|
|
|
|
|
|
|
|
|
|
if(!buf[2])
|
|
|
|
|
Choice(row, col);
|
|
|
|
|
else if(buf[2] == 'F' && !buf[3])
|
|
|
|
|
ChoiceFlag(row, col);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-20 16:23:56 +02:00
|
|
|
|
else if(buf[0] == 'Q' && !buf[1])
|
|
|
|
|
gameover = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Test if player has win the game
|
|
|
|
|
*/
|
|
|
|
|
TestIfWin()
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if(flgcnt == mines && flgcnt + viscnt == squares)
|
|
|
|
|
gameover = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set or remove a flag in a square
|
|
|
|
|
*/
|
|
|
|
|
ChoiceFlag(row, col)
|
|
|
|
|
int row, col;
|
|
|
|
|
{
|
|
|
|
|
if(!TstVis(row, col))
|
|
|
|
|
{
|
|
|
|
|
if(TstFlag(row, col))
|
|
|
|
|
{
|
|
|
|
|
SetFlag(row, col, 0); --flgcnt;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SetFlag(row, col, 1); ++flgcnt;
|
|
|
|
|
|
|
|
|
|
TestIfWin();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Select a square
|
|
|
|
|
*/
|
|
|
|
|
Choice(row, col)
|
|
|
|
|
int row, col;
|
|
|
|
|
{
|
|
|
|
|
if(!TstVis(row, col) && !TstFlag(row, col))
|
|
|
|
|
{
|
|
|
|
|
if(TstMine(row, col))
|
|
|
|
|
{
|
|
|
|
|
gameover = dead = 1; return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Make visible this square.
|
|
|
|
|
*/
|
|
|
|
|
SetVis(row, col, 1); ++viscnt;
|
|
|
|
|
|
|
|
|
|
/* Make visible the adjacent squares, if there are not mines around.
|
|
|
|
|
*/
|
|
|
|
|
if(!GetCount(row, col))
|
|
|
|
|
VisAdj(row, col);
|
|
|
|
|
|
|
|
|
|
TestIfWin();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get square counter
|
|
|
|
|
*/
|
|
|
|
|
GetCount(row, col)
|
|
|
|
|
int row, col;
|
|
|
|
|
{
|
|
|
|
|
BYTE *p;
|
|
|
|
|
|
|
|
|
|
p = count_row[row];
|
|
|
|
|
|
|
|
|
|
return p[col];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set square counter
|
|
|
|
|
*/
|
|
|
|
|
SetCount(row, col, val)
|
|
|
|
|
int row, col, val;
|
|
|
|
|
{
|
|
|
|
|
BYTE *p;
|
|
|
|
|
|
|
|
|
|
p=count_row[row];
|
|
|
|
|
|
|
|
|
|
p[col]=val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return 1 if there is a mine in the square, else 0
|
|
|
|
|
*/
|
|
|
|
|
TstMine(row, col)
|
|
|
|
|
int row, col;
|
|
|
|
|
{
|
|
|
|
|
BYTE *p;
|
|
|
|
|
|
|
|
|
|
p=mines_row[row];
|
|
|
|
|
|
|
|
|
|
return p[col] ? 1 : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set/remove a mine in a square
|
|
|
|
|
*/
|
|
|
|
|
SetMine(row, col, val)
|
|
|
|
|
int row, col, val;
|
|
|
|
|
{
|
|
|
|
|
BYTE *p;
|
|
|
|
|
|
|
|
|
|
p=mines_row[row];
|
|
|
|
|
|
|
|
|
|
p[col]=val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return 1 if there is a flag in the square, else 0
|
|
|
|
|
*/
|
|
|
|
|
TstFlag(row, col)
|
|
|
|
|
int row, col;
|
|
|
|
|
{
|
|
|
|
|
BYTE *p;
|
|
|
|
|
|
|
|
|
|
p=flags_row[row];
|
|
|
|
|
|
|
|
|
|
return p[col] ? 1 : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set/remove a flag in a square
|
|
|
|
|
*/
|
|
|
|
|
SetFlag(row, col, val)
|
|
|
|
|
int row, col, val;
|
|
|
|
|
{
|
|
|
|
|
BYTE *p;
|
|
|
|
|
|
|
|
|
|
p=flags_row[row];
|
|
|
|
|
|
|
|
|
|
p[col]=val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return 1 if it is a visible square, else 0
|
|
|
|
|
*/
|
|
|
|
|
TstVis(row, col)
|
|
|
|
|
int row, col;
|
|
|
|
|
{
|
|
|
|
|
BYTE *p;
|
|
|
|
|
|
|
|
|
|
p=visib_row[row];
|
|
|
|
|
|
|
|
|
|
return p[col] ? 1 : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set square to visible or invisible
|
|
|
|
|
*/
|
|
|
|
|
SetVis(row, col, val)
|
|
|
|
|
int row, col, val;
|
|
|
|
|
{
|
|
|
|
|
BYTE *p;
|
|
|
|
|
|
|
|
|
|
p=visib_row[row];
|
|
|
|
|
|
|
|
|
|
p[col]=val;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Return the number of mines in adjacent squares
|
|
|
|
|
*/
|
|
|
|
|
FindAdj(row, col)
|
|
|
|
|
int row, col;
|
|
|
|
|
{
|
|
|
|
|
int mines, rn, cn, i;
|
|
|
|
|
|
|
|
|
|
for((mines = i = 0); i < 8; ++i)
|
|
|
|
|
{
|
|
|
|
|
rn = row + row_off[i];
|
|
|
|
|
cn = col + col_off[i];
|
|
|
|
|
|
|
|
|
|
if(rn >= 0 && rn < brd_rows && cn >= 0 && cn < brd_cols)
|
|
|
|
|
if(TstMine(rn, cn))
|
|
|
|
|
++mines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mines;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Set adjacent squares visible - recursively
|
|
|
|
|
*/
|
|
|
|
|
VisAdj(row, col)
|
|
|
|
|
int row, col;
|
|
|
|
|
{
|
|
|
|
|
int rn, cn, i;
|
|
|
|
|
|
|
|
|
|
for(i=0; i < 8; ++i)
|
|
|
|
|
{
|
|
|
|
|
rn = row + row_off[i];
|
|
|
|
|
cn = col + col_off[i];
|
|
|
|
|
|
|
|
|
|
if(rn >= 0 && rn < brd_rows && cn >= 0 && cn < brd_cols)
|
|
|
|
|
{
|
|
|
|
|
if(!TstVis(rn, cn) && !TstFlag(rn, cn))
|
|
|
|
|
{
|
|
|
|
|
SetVis(rn, cn, 1); ++viscnt;
|
|
|
|
|
|
|
|
|
|
if(!GetCount(rn, cn))
|
|
|
|
|
VisAdj(rn, cn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-22 12:11:02 +02:00
|
|
|
|
/* Draw the board (all around the playfield)
|
|
|
|
|
*/
|
|
|
|
|
DrawBoard()
|
2020-04-20 16:23:56 +02:00
|
|
|
|
{
|
|
|
|
|
int r, c;
|
2020-04-22 12:11:02 +02:00
|
|
|
|
|
2020-04-20 16:23:56 +02:00
|
|
|
|
KsClear();
|
2020-04-21 16:16:06 +02:00
|
|
|
|
PrStr("\n\n");
|
2020-04-20 16:23:56 +02:00
|
|
|
|
|
|
|
|
|
PrStr("\t ");
|
|
|
|
|
for(c = 0; c < brd_cols; ++c) {
|
|
|
|
|
PrCh(' '); PrCh('A' + c);
|
|
|
|
|
}
|
|
|
|
|
PrCh('\n');
|
|
|
|
|
|
2020-04-22 12:11:02 +02:00
|
|
|
|
KsFgCol(BBLK);
|
|
|
|
|
PrStr("\t "); PrCh(B_TOP_LEFT);
|
|
|
|
|
PrChTimes(B_TOP_BOTTOM, brd_cols + brd_cols + 1);
|
|
|
|
|
PrCh(B_TOP_RIGHT); PrCh('\n');
|
|
|
|
|
KsResetAttr();
|
|
|
|
|
|
2020-04-20 16:23:56 +02:00
|
|
|
|
for(r = 0; r < brd_rows; ++r)
|
|
|
|
|
{
|
2020-04-21 16:16:06 +02:00
|
|
|
|
PrCh('\t'); PrCh('0' + r);
|
2020-04-22 12:11:02 +02:00
|
|
|
|
KsFgCol(BBLK); PrCh(B_LEFT_RIGHT); KsResetAttr();
|
|
|
|
|
PrChTimes(' ', brd_cols*2);
|
|
|
|
|
PrCh(' '); KsFgCol(BBLK); PrCh(B_LEFT_RIGHT); KsResetAttr(); PrCh('0' + r);
|
|
|
|
|
PrCh('\n');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
KsFgCol(BBLK);
|
|
|
|
|
PrStr("\t "); PrCh(B_BOTTOM_LEFT);
|
|
|
|
|
PrChTimes(B_TOP_BOTTOM, brd_cols + brd_cols + 1);
|
|
|
|
|
PrCh(B_BOTTOM_RIGHT); PrCh('\n');
|
|
|
|
|
KsResetAttr();
|
|
|
|
|
|
|
|
|
|
PrStr("\t ");
|
|
|
|
|
for(c = 0; c < brd_cols; ++c) {
|
|
|
|
|
PrCh(' '); PrCh('A' + c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Draw the playfield
|
|
|
|
|
*/
|
|
|
|
|
ShowPlayfield()
|
|
|
|
|
{
|
|
|
|
|
int r, c;
|
|
|
|
|
|
|
|
|
|
for(r = 0; r < brd_rows; ++r)
|
|
|
|
|
{
|
|
|
|
|
KsPosCursor(4 + r, 10);
|
2020-04-20 16:23:56 +02:00
|
|
|
|
|
|
|
|
|
for(c = 0; c < brd_cols; ++c)
|
|
|
|
|
{
|
|
|
|
|
PrCh(' ');
|
|
|
|
|
if(TstVis(r, c))
|
|
|
|
|
{
|
|
|
|
|
if(GetCount(r, c)) {
|
2020-04-22 12:11:02 +02:00
|
|
|
|
if(KsCan(KS_CAN_COLOR)) {
|
2020-04-21 16:16:06 +02:00
|
|
|
|
switch(GetCount(r, c))
|
|
|
|
|
{
|
|
|
|
|
case 1: KsFgCol(BBLU); break;
|
|
|
|
|
case 2: KsFgCol(GRN); break;
|
|
|
|
|
case 3: KsFgCol(BRED); break;
|
|
|
|
|
case 4: KsFgCol(BLU); break;
|
|
|
|
|
case 5: KsFgCol(RED); break;
|
|
|
|
|
default: KsFgCol(CYN); break;
|
|
|
|
|
}
|
|
|
|
|
PrCh('0' + GetCount(r, c));
|
|
|
|
|
KsResetAttr();
|
2020-04-20 16:23:56 +02:00
|
|
|
|
}
|
2020-04-21 16:16:06 +02:00
|
|
|
|
else
|
|
|
|
|
PrCh('0' + GetCount(r, c));
|
2020-04-20 16:23:56 +02:00
|
|
|
|
}
|
2020-04-21 16:16:06 +02:00
|
|
|
|
else
|
2020-04-20 16:23:56 +02:00
|
|
|
|
PrCh(SPR_BLANK);
|
|
|
|
|
}
|
|
|
|
|
else if(gameover)
|
|
|
|
|
{
|
2020-04-21 16:16:06 +02:00
|
|
|
|
if(TstMine(r, c))
|
|
|
|
|
{
|
2020-04-20 16:23:56 +02:00
|
|
|
|
if(TstFlag(r, c)) {
|
2020-04-22 12:11:02 +02:00
|
|
|
|
KsFgCol(RED);
|
|
|
|
|
PrCh(SPR_FLAG);
|
|
|
|
|
KsResetAttr();
|
2020-04-20 16:23:56 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
2020-04-22 12:11:02 +02:00
|
|
|
|
KsFgCol(RED);
|
|
|
|
|
PrCh(SPR_MINE);
|
|
|
|
|
KsResetAttr();
|
2020-04-20 16:23:56 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-21 16:16:06 +02:00
|
|
|
|
else if(TstFlag(r, c))
|
|
|
|
|
PrCh(SPR_ERR);
|
2020-04-20 16:23:56 +02:00
|
|
|
|
else {
|
2020-04-22 12:11:02 +02:00
|
|
|
|
KsFgCol(BBLK);
|
|
|
|
|
PrCh(SPR_UNK);
|
|
|
|
|
KsResetAttr();
|
2020-04-20 16:23:56 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
#if DEBUG_MINES
|
|
|
|
|
if(TstFlag(r, c))
|
|
|
|
|
PrCh(SPR_FLAG);
|
|
|
|
|
else if(TstMine(r, c))
|
|
|
|
|
PrCh(SPR_MINE);
|
|
|
|
|
else
|
|
|
|
|
PrCh(SPR_UNK);
|
|
|
|
|
#else
|
|
|
|
|
if(TstFlag(r, c)) {
|
2020-04-22 12:11:02 +02:00
|
|
|
|
KsFgCol(RED);
|
2020-04-21 16:16:06 +02:00
|
|
|
|
PrCh(SPR_FLAG);
|
2020-04-22 12:11:02 +02:00
|
|
|
|
KsResetAttr();
|
2020-04-20 16:23:56 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
2020-04-22 12:11:02 +02:00
|
|
|
|
KsFgCol(BBLK);
|
2020-04-21 16:16:06 +02:00
|
|
|
|
PrCh(SPR_UNK);
|
2020-04-22 12:11:02 +02:00
|
|
|
|
KsResetAttr();
|
2020-04-20 16:23:56 +02:00
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(r)
|
|
|
|
|
{
|
|
|
|
|
case 0 :
|
2020-04-22 12:11:02 +02:00
|
|
|
|
KsPosCursor(5, (brd_cols*2)+16);
|
|
|
|
|
PrStr("Find That Mine!");
|
2020-04-20 16:23:56 +02:00
|
|
|
|
break;
|
|
|
|
|
case 2 :
|
2020-04-22 12:11:02 +02:00
|
|
|
|
KsPosCursor(7, (brd_cols*2)+16);
|
2020-04-20 16:23:56 +02:00
|
|
|
|
if(gameover) {
|
2020-04-22 12:11:02 +02:00
|
|
|
|
PrStr(" GAME OVER ");
|
2020-04-20 16:23:56 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
2020-04-22 12:11:02 +02:00
|
|
|
|
PrStr("Level: "); PrNumber(level);
|
2020-04-20 16:23:56 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 4 :
|
2020-04-22 12:11:02 +02:00
|
|
|
|
KsPosCursor(9, (brd_cols*2)+16);
|
2020-04-20 16:23:56 +02:00
|
|
|
|
if(gameover) {
|
|
|
|
|
if(dead) {
|
2020-04-22 12:11:02 +02:00
|
|
|
|
PrStr(" YOU'RE DEAD ");
|
2020-04-20 16:23:56 +02:00
|
|
|
|
}
|
|
|
|
|
else if(flgcnt + viscnt == squares) {
|
2020-04-22 12:11:02 +02:00
|
|
|
|
PrStr(" YOU WIN ");
|
2020-04-20 16:23:56 +02:00
|
|
|
|
}
|
|
|
|
|
else {
|
2020-04-22 12:11:02 +02:00
|
|
|
|
PrStr(" SEE YOU LATER");
|
2020-04-20 16:23:56 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2020-04-22 12:11:02 +02:00
|
|
|
|
PrStr("Mines: "); PrNumber(mines);
|
2020-04-20 16:23:56 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 6 :
|
2020-04-22 12:11:02 +02:00
|
|
|
|
KsPosCursor(11, (brd_cols*2)+16);
|
2020-04-20 16:23:56 +02:00
|
|
|
|
if(!gameover) {
|
2020-04-22 12:11:02 +02:00
|
|
|
|
PrStr("Flags: "); PrNumber(flgcnt);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
PrChTimes(' ', 9);
|
2020-04-20 16:23:56 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Setup board.
|
|
|
|
|
All squares without mines, invisibles and without flags.
|
|
|
|
|
Set the mines randomly.
|
|
|
|
|
*/
|
|
|
|
|
extern char RandStr[];
|
|
|
|
|
ClrBoard()
|
|
|
|
|
{
|
|
|
|
|
int r, c; char *p;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
*/
|
|
|
|
|
p = RandStr + 8 * (random & 0x0F);
|
|
|
|
|
|
|
|
|
|
for(r = 0; r < brd_rows; ++r)
|
|
|
|
|
{
|
|
|
|
|
for(c = 0; c < brd_cols; ++c)
|
|
|
|
|
{
|
|
|
|
|
/* Set square
|
|
|
|
|
*/
|
|
|
|
|
SetVis(r, c, 0);
|
|
|
|
|
SetFlag(r, c, 0);
|
|
|
|
|
SetMine(r, c, *p == '@' ? 1 : 0);
|
|
|
|
|
|
|
|
|
|
/* Update next value
|
|
|
|
|
*/
|
|
|
|
|
if((*++p)=='$')
|
|
|
|
|
p = RandStr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Setup counters of adjacent squares
|
|
|
|
|
*/
|
|
|
|
|
ClrCounts()
|
|
|
|
|
{
|
|
|
|
|
int r, c; char *p;
|
|
|
|
|
|
|
|
|
|
for(r = 0; r < brd_rows; ++r)
|
|
|
|
|
for(c = 0; c < brd_cols; ++c)
|
|
|
|
|
SetCount(r, c, FindAdj(r, c));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* String to setup board randomly (total of 128 bytes). 1 mine each 8 bytes.
|
|
|
|
|
*/
|
|
|
|
|
#asm
|
|
|
|
|
|
|
|
|
|
RandStr:
|
|
|
|
|
|
|
|
|
|
; v-------v-------v-------v-------
|
|
|
|
|
|
|
|
|
|
defb '----@---@-------------@----@----'
|
|
|
|
|
defb '-@-----------@----@----------@--'
|
|
|
|
|
defb '--@---------@----------@--@-----'
|
|
|
|
|
defb '----@----@-----------@---@------'
|
|
|
|
|
defb '$'
|
|
|
|
|
|
|
|
|
|
#endasm
|
|
|
|
|
|
|
|
|
|
/* Read line from keyboard.
|
|
|
|
|
Setup random number.
|
|
|
|
|
*/
|
|
|
|
|
ReadLine(buf, width)
|
|
|
|
|
char *buf;
|
|
|
|
|
int width;
|
|
|
|
|
{
|
|
|
|
|
int len; char ch;
|
|
|
|
|
|
|
|
|
|
len = 0;
|
|
|
|
|
|
|
|
|
|
while(!KsGetKb())
|
|
|
|
|
++random;
|
|
|
|
|
|
|
|
|
|
while(1)
|
|
|
|
|
{
|
|
|
|
|
switch(ch = KsGetCh())
|
|
|
|
|
{
|
|
|
|
|
case 8 :
|
|
|
|
|
case 127 :
|
|
|
|
|
if(len)
|
|
|
|
|
{
|
|
|
|
|
PrCh(8); PrCh(' '); PrCh(8); --len;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case '\r' :
|
|
|
|
|
case '\n' :
|
|
|
|
|
buf[len] = 0;
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
default :
|
|
|
|
|
if(ch >= ' ' && len < width)
|
|
|
|
|
PrCh(buf[len++] = toupper(ch));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Print character. Supports simple '\t'.
|
|
|
|
|
*/
|
|
|
|
|
PrCh(c)
|
|
|
|
|
int c;
|
|
|
|
|
{
|
|
|
|
|
if(c != '\t') {
|
|
|
|
|
KsPutCh(c);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
PrChTimes(' ', 8);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Print string
|
|
|
|
|
*/
|
|
|
|
|
PrStr(s)
|
|
|
|
|
char *s;
|
|
|
|
|
{
|
|
|
|
|
while(*s) {
|
|
|
|
|
PrCh(*s++);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Print character X times
|
|
|
|
|
*/
|
|
|
|
|
PrChTimes(ch, n)
|
|
|
|
|
int ch, n;
|
|
|
|
|
{
|
|
|
|
|
while(n--) {
|
|
|
|
|
PrCh(ch);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Print decimal number
|
|
|
|
|
*/
|
|
|
|
|
PrNumber(n)
|
|
|
|
|
int n;
|
|
|
|
|
{
|
|
|
|
|
int s[7];
|
|
|
|
|
|
|
|
|
|
sprintf(s, "%d", n);
|
|
|
|
|
|
|
|
|
|
PrStr(s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|