program Mine;
uses
SysRandom,
Termio;
type
TCell = (Empty, Bomb);
TState = (Closed, Open, Flagged);
TField = record
Cells: array of TCell;
States: array of TState;
Rows: Integer;
Cols: Integer;
CursorRow: Integer;
CursorCol: Integer;
end;
function FieldGet(Field: TField; Row, Col: Integer): TCell;
begin
FieldGet := Field.Cells[Row * Field.Cols + Col];
end;
function FieldGetState(Field: TField; Row, Col: Integer): TState;
begin
FieldGetState := Field.States[Row * Field.Cols + Col];
end;
function FieldOpenAtCursor(var Field: TField): TCell;
var
Index: Integer;
begin
Index := Field.CursorRow * Field.Cols + Field.CursorCol;
Field.States[Index] := Open;
FieldOpenAtCursor := Field.Cells[Index];
end;
procedure FieldFlagAtCursor(var Field: TField);
var
Index: Integer;
begin
Index := Field.CursorRow * Field.Cols + Field.CursorCol;
case Field.States[Index] of
Closed: Field.States[Index] := Flagged;
Flagged: Field.States[Index] := Closed;
end;
end;
procedure FieldOpenBombs(var Field: TField);
var
Index: Integer;
begin
for Index := 0 to Field.Rows * Field.Cols do
if Field.Cells[Index] = Bomb then
Field.States[Index] := Open;
end;
function FieldGetChecked(Field: TField; Row, Col: Integer; var Cell: TCell): Boolean;
begin
FieldGetChecked := (0 <= Row) and (Row < Field.Rows) and (0 <= Col) and (Col < Field.Cols);
if FieldGetChecked then Cell := FieldGet(Field, Row, Col);
end;
procedure FieldSet(var Field: TField; Row, Col: Integer; Cell: TCell);
begin
Field.Cells[Row * Field.Cols + Col] := Cell;
end;
procedure FieldResize(var Field: TField; Rows, Cols: Integer);
var
Index: Integer;
begin
SetLength(Field.Cells, Rows * Cols);
SetLength(Field.States, Rows * Cols);
Field.Rows := Rows;
Field.Cols := Cols;
Field.CursorRow := 0;
Field.CursorCol := 0;
for Index := 0 to Rows * Cols do Field.States[Index] := Closed;
end;
function FieldRandomCell(Field: TField; var Row, Col: Integer): TCell;
begin
Row := Random(Field.Rows);
Col := Random(Field.Cols);
FieldRandomCell := FieldGet(Field, Row, Col);
end;
function FieldAtCursor(Field: TField; Row, Col: Integer): Boolean;
begin
FieldAtCursor := (Field.CursorRow = Row) and (Field.CursorCol = Col);
end;
function FieldAroundCursor(Field: TField; Row, Col: Integer): Boolean;
var
DRow, DCol: Integer;
begin
for DRow := -1 to 1 do
for DCol := -1 to 1 do
if (Field.CursorRow + DRow = Row) and (Field.CursorCol + DCol = Col) then
Exit(True);
FieldAroundCursor := False;
end;
procedure FieldRandomize(var Field: TField; BombsPercentage: Integer);
var
Index, BombsCount: Integer;
Row, Col: Integer;
begin
for Index := 0 to Field.Rows * Field.Cols do Field.Cells[Index] := Empty;
if BombsPercentage > 100 then BombsPercentage := 100;
BombsCount := (Field.Rows * Field.Cols * BombsPercentage + 99) div 100;
for Index := 1 to BombsCount do
begin
while (FieldRandomCell(Field, Row, Col) = Bomb) or FieldAroundCursor(Field, Row, Col) do ;
FieldSet(Field, Row, Col, Bomb);
end;
end;
function FieldCountNeighbors(Field: TField; Row, Col: Integer): Integer;
var
DRow, DCol: Integer;
Cell: TCell;
begin
FieldCountNeighbors := 0;
for DRow := -1 to 1 do
for DCol := -1 to 1 do
if (DRow <> 0) or(DCol <> 0) then
begin
if FieldGetChecked(Field, Row + DRow, Col + DCol, Cell) then
if Cell = Bomb then
Inc(FieldCountNeighbors);
end;
end;
procedure FieldWrite(Field: TField);
var
Row, Col, Neighbors: Integer;
begin
for Row := 0 to Field.Rows - 1 do
begin
for Col := 0 to Field.Cols - 1 do
begin
if FieldAtCursor(Field, Row, Col) then Write('[') else Write(' ');
case FieldGetState(Field, Row, Col) of
Open: case FieldGet(Field, Row, Col) of
Bomb: Write('@');
Empty: begin
Neighbors := FieldCountNeighbors(Field, Row, Col);
if Neighbors > 0 then Write(Neighbors) else Write(' ');
end;
end;
Closed: Write('.');
Flagged: Write('?');
end;
if FieldAtCursor(Field, Row, Col) then Write(']') else Write(' ');
end;
WriteLn;
end;
end;
const
STDIN_FILENO = 0;
var
MainField: TField;
Quit: Boolean = False;
First: Boolean = True;
TAttr, SavedTAttr: Termios;
Cmd: Char;
begin
Randomize;
FieldResize(MainField, 10, 10);
if IsATTY(STDIN_FILENO) = 0 then
begin
WriteLn('Error: This is not a terminal!');
Exit;
end;
TCGetAttr(STDIN_FILENO, TAttr);
TCGetAttr(STDIN_FILENO, SavedTAttr);
TAttr.c_lflag := TAttr.c_lflag and not (ICANON or ECHO);
TAttr.c_cc[VMIN] := 1;
TAttr.c_cc[VTIME] := 0;
TCSetAttr(STDIN_FILENO, TCSAFLUSH, TAttr);
while not Quit do
begin
FieldWrite(MainField);
Read(Cmd);
case Cmd of
'w': if MainField.CursorRow > 0 then Dec(MainField.CursorRow);
's': if MainField.CursorRow < MainField.Rows - 1 then Inc(MainField.CursorRow);
'a': if MainField.CursorCol > 0 then Dec(MainField.CursorCol);
'd': if MainField.CursorCol < MainField.Cols - 1 then Inc(MainField.CursorCol);
'f': FieldFlagAtCursor(MainField);
' ': begin
if First then
begin
FieldRandomize(MainField, 20);
First := False;
end;
if FieldOpenAtCursor(MainField) = Bomb then
begin
FieldOpenBombs(MainField);
Write(Chr(27), '[', MainField.Rows, 'A');
Write(Chr(27), '[', MainField.Cols * 3, 'D');
FieldWrite(MainField);
WriteLn('oops');
break;
end;
end;
end;
Write(Chr(27), '[', MainField.Rows, 'A');
Write(Chr(27), '[', MainField.Cols * 3, 'D');
end;
TCSetAttr(STDIN_FILENO, TCSAFLUSH, SavedTAttr);
end.