The task is to read in a data file containing a representation of a rectangular array with '*' signifying a mine and '.' signifying the absence of one. Each such array is preceded by a row and column count. The file is terminated with an entry that has zero for both the row and column counts. The expected output counts each frame (starting from 1) and emits a modified representation of the input that substitutes a digit for each '.' in the input. The digit is the number of mines next to that space.
As with C++ Mine Sweeper Attempt I did a review and decided to create a version as well. On my machine with a 25,000 line input file mostly containing \$99 \times 100\$ fields, the original took an average of 0.140 seconds while this version took an average of 0.025 seconds (less than 20% of the time).
Internally, it keeps three pointers into a common buffer area. The pointers are for the currentRow
and also for the aboveRow
and belowRow
. It reads data into the currentRow
calling the placeMine
function when a mine has been read. As each mine is read, all rows are appropriately updated using the increment
function.
No attempt is made at error checking or recovery, so if the user sets the maximum width (given here as a template parameter) to say, 50, but the input has rows much larger, the program will likely segfault and crash. In the same vein, the input file is expected to be formatted perfectly.
mines.cpp
#include <algorithm>
#include <iostream>
#include <array>
template <std::size_t maxWidth = 100>
class MineField {
static constexpr char mine{'*'};
static constexpr int maxSize{maxWidth + 2};
int rows = 0;
int cols = 0;
std::array<char, maxSize * 3> buffer;
char *aboveRow = &buffer[0];
char *currentRow = &buffer[maxSize];
char *belowRow = &buffer[maxSize * 2];
void increment(char& location) {
if (location != mine)
++location;
}
void showrow(std::ostream& out) const {
out.write(&aboveRow[1], cols);
out << '\n';
}
// place the mine at the current row and adjust neighboring counts
void placeMine(int i) {
currentRow[i+1] = mine;
increment(currentRow[i]);
increment(currentRow[i+2]);
increment(aboveRow[i]);
increment(aboveRow[i+1]);
increment(aboveRow[i+2]);
increment(belowRow[i]);
increment(belowRow[i+1]);
increment(belowRow[i+2]);
}
// shift the rows up and fill the bottom row with zeroes
void shift() {
std::swap(aboveRow, currentRow);
std::swap(belowRow, currentRow);
std::fill_n(belowRow, cols, '0');
}
public:
void process(std::istream& in, std::ostream& out) {
in >> rows >> cols;
for (int fieldnum{0}; rows && cols; ) {
buffer.fill('0');
if (fieldnum) {
out << '\n';
}
out << "Field #" << ++fieldnum << ":\n";
for (int j{0}; j < rows; ++j) {
for (int i{0}; i < cols; ++i) {
char ch;
in >> ch;
if (ch == mine) {
placeMine(i);
}
}
if (j) {
showrow(out);
}
shift();
}
showrow(out);
in >> rows >> cols;
}
}
};
int main() {
std::ios_base::sync_with_stdio(false);
std::cin.tie(nullptr);
MineField<100> field;
field.process(std::cin, std::cout);
}
Example input
4 4
*.**
....
.*..
...*
3 6
***...
*.*..*
***...
0 0
Example output
Field #1:
*2**
2332
1*23
112*
Field #2:
***211
*8*31*
***211
```