/*
* 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 of the License, 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, see .
*/
#include "Board.h"
//Board:: Board(int width, int height, int minecount)
Board:: Board()
{
/*
this->width = width;
this->height = height;
this->tilecount = width*height;
this->minecount = minecount;
this->game_running = true;
this->game_won = false;
// create tiles
for (int x = 0; x < height; x++)
{
for (int y = 0; y < width; y++)
{
std::unique_ptr t(new Tile(x, y));
tiles.push_back(std::move(t));
}
}
generate_mines();
retrieve_neighbors();
*/
}
Board::~Board()
{
tiles.clear();
}
Tile *Board::get_tile_at(int x, int y)
{
return tiles.at(this->height*x+y).get();
}
bool
Board::reveal_tile_at(int x, int y)
{
Tile *tile = get_tile_at(x, y);
if (tile->is_mine())
{
/* lose */
reveal_all_mines();
game_running = false;
game_won = false;
return false;
}
tile->reveal();
reveal_neighbor_tiles(tile->get_x(), tile->get_y());
if (check_win())
{
/* win */
reveal_all_mines();
game_won = true;
game_running = false;
}
return true;
}
void
Board::new_game(int width, int height, int minecount)
{
this->width = width;
this->height = height;
this->tilecount = width*height;
this->minecount = minecount;
this->game_running = true;
this->game_won = false;
tiles.clear();
/* =reate tiles */
for (int x = 0; x < height; x++)
{
for (int y = 0; y < width; y++)
{
std::unique_ptr t(new Tile(x, y));
tiles.push_back(std::move(t));
}
}
generate_mines();
retrieve_neighbors();
}
void
Board::reveal_all_mines()
{
Tile *tile;
for(auto& t: tiles)
{
tile = t.get();
/* explicitly unflag tile */
tile->clear_flag(Tile::FLAGGED); // FIXME: doesnt work as intended, it should unflag all tiles
if (tile->is_mine())
{
tile->set_flag(Tile::REVEALED);
}
}
}
void
Board::retrieve_neighbors()
{
Tile *tile;
int x, y;
for (auto& t: tiles)
{
tile = t.get();
x = t->get_x();
y = t->get_y();
int badup = 0, baddown = 0, badleft = 0, badright = 0;
if (x-1<0) badleft = 1;
if (x+1>width-1) badright = 1;
if (y-1<0) badup = 1;
if (y+1>height-1) baddown = 1;
if (!badleft && !badup) tile->neighbors[0] = get_tile_at(x-1, y-1);
if (!badup) tile->neighbors[1] = get_tile_at(x, y-1);
if (!badright && !badup) tile->neighbors[2] = get_tile_at(x+1, y-1);
if (!badleft) tile->neighbors[3] = get_tile_at(x-1, y);
if (!badright) tile->neighbors[4] = get_tile_at(x+1, y);
if (!badleft && !baddown) tile->neighbors[5] = get_tile_at(x-1, y+1);
if (!baddown) tile->neighbors[6] = get_tile_at(x, y+1);
if (!badright && !baddown) tile->neighbors[7] = get_tile_at(x+1, y+1);
count_neighbor_mines(tile);
}
}
void
Board::count_neighbor_mines(Tile *tile)
{
Tile *neighbor;
int mines = 0;
for (int i = 0; i < 8; i++)
{
neighbor = tile->get_neighbor(i);
if (neighbor)
{
if (neighbor->is_mine())
{
mines++;
}
}
}
tile->set_neighbor_mine_count(mines);
//printf("init tile: x: %d, y: %d, neighbor mines: %d\n", tile->get_x(), tile->get_y(), tile->get_neighbor_mine_count());
}
void
Board::generate_mines()
{
srand(time(NULL));
int mx, my;
Tile *tile;
for (int i = 0; i < minecount; i++)
{
replace_mine:
mx = rand() % width;
my = rand() % height;
tile = get_tile_at(mx, my);
if (tile->is_mine()) /* don't overwrite mines */
goto replace_mine;
tile->set_flag(Tile::MINE);
}
}
bool
Board::reveal_neighbor_tiles(int x, int y)
{
/*
* reveal all neighbor tiles that are empty, recursively
*/
Tile *tile, *neighbor;
tile = get_tile_at(x, y);
/* always reveal the first tile even if it has a neighbor mines */
tile->set_flag(Tile::REVEALED);
if (tile->is_mine())
return false;
if (tile->get_neighbor_mine_count() == 0)
{
/* if our tile is empty we want to reveal its immediate neighbors */
for (int nc = 0; nc < 8; nc++)
{
neighbor = tile->get_neighbor(nc);
if (neighbor != nullptr && !neighbor->is_mine() && !neighbor->is_revealed())
{
reveal_neighbor_tiles(neighbor->get_x(), neighbor->get_y());
}
}
}
return true;
}
bool
Board::check_win()
{
int allowedmines = minecount;
int safetiles = (height * width) - minecount;
int correctflags = 0;
int correcttiles = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
Tile *tile = get_tile_at(x, y);
if (tile->is_mine() && tile->is_flagged())
correctflags++;
else if (!(tile->is_mine()) && (tile->is_revealed()))
correcttiles++;
}
}
return (correctflags == allowedmines) || (correcttiles == safetiles);
}