added libs and oalist
[mines3d.git] / core / board.cpp
blob978e7d02a315782c22b5eae407cff63c8f26a8ed
1 /*
2 * File: board.cpp
3 * Author: Petr Kubiznak
4 */
6 #include "board.h"
7 #include "../exceptions/GeneralException.h"
8 #include "../exceptions/OutOfBoundsException.h"
9 #include "../exceptions/AccessForbiddenException.h"
10 #include "../asserts.h"
11 #include <iostream>
12 #include <iomanip>
13 #include <cstdlib>
14 #include <ctime>
15 #include <cstdio>
16 using namespace std;
18 /* -------------------------------------------------------------------------- */
20 #define FIELD_WIDTH 2 //sirka, na kterou se tiskne jedno policko do konzole
22 /* -------------------------------------------------------------------------- */
24 /* vytvori 3d hraci pole o rozmerech rows x cols x layers a rozlozi do nej zadany pocet min,
25 * tech muze byt maximalne 1/2 z celkoveho poctu policek */
26 Board::Board(int rows, int cols, int layers, int minesCount) {
29 assert(rows > 0);
30 assert(cols > 0);
31 assert(layers > 0);
33 this->layers = layers;
34 this->rows = rows;
35 this->cols = cols;
36 this->minesCount = minesCount;
38 //alokace pameti
39 this->board = new Field ** [layers];
40 for(int l=0; l<layers; l++) {
41 this->board[l] = new Field * [rows];
42 for(int r=0; r<rows; r++) {
43 this->board[l][r] = new Field[cols];
47 //rozmisteni min
48 if(this->placeMines() != 0) throw GeneralException(ERR_DEFAULT, "Too many mines, use lesser number.");
49 //vypocet sousednich min
50 this->numberBoard();
54 /* -------------------------------------------------------------------------- */
56 Board::~Board(void) {
57 for(int l=0; l<this->layers; l++) {
58 for(int r=0; r<this->rows; r++) {
59 delete [] this->board[l][r];
61 delete [] this->board[l];
63 delete [] this->board;
66 /* -------------------------------------------------------------------------- */
68 /* vraci pocet vrstev */
69 int Board::getLayersCount(void) const {
70 return layers;
73 /* -------------------------------------------------------------------------- */
75 /* vraci pocet radku */
76 int Board::getRowsCount(void) const {
77 return rows;
80 /* -------------------------------------------------------------------------- */
82 /* vraci pocet sloupcu */
83 int Board::getColsCount(void) const {
84 return cols;
87 /* -------------------------------------------------------------------------- */
89 /* vraci pocet min v celem poli */
90 int Board::getMinesCount(void) const {
91 return minesCount;
94 /* -------------------------------------------------------------------------- */
96 Field & Board::getFieldRW(int layer, int row, int col) const {
97 if(! ((layer >= 0 && layer < this->layers) && (row >= 0 && row < this->rows) &&
98 (col >= 0 && col < this->cols)) ) {
99 char str[100];
100 sprintf(str, "Invalid coordinates [%d, %d, %d].", layer, row, col);
101 throw OutOfBoundsException(str);
103 return board[layer][row][col];
106 /* -------------------------------------------------------------------------- */
108 /* vraci referenci na pole dane souradnicemi
109 * v pripade neplatnych souradnic vyhazuje vyjimku ERR_OUT_OF_BOUNDS */
110 const Field & Board::getField(int layer, int row, int col) const {
111 return (const Field &)getFieldRW(layer, row, col);
114 /* -------------------------------------------------------------------------- */
116 /* ternarni operator () - getter pro ziskani pole na danych souradnicich (vola getField)
117 * v pripade neplatnych souradnic vyhazuje vyjimku ERR_OUT_OF_BOUNDS */
118 const Field & Board::operator () (int layer, int row, int col) const {
119 return getField(layer, row, col);
122 /* -------------------------------------------------------------------------- */
124 /* tiskne cele hraci pole (pro debug) */
125 void Board::print(void) const {
126 for(int l=0; l<this->layers; l++) {
127 for(int r=0; r<this->rows; r++) {
128 for(int c=0; c<this->cols; c++)
129 cout << setw(3) << setfill('_') << (int) this->board[l][r][c] << setfill(' ') << " ";
130 cout << endl;
132 cout << endl;
136 /* -------------------------------------------------------------------------- */
138 /* pretizeny operator vystupu - tiskne cele pole v hernim modu */
139 ostream & operator << (ostream &os, const Board &board) {
140 for(int l=0; l<board.layers; l++) {
141 os << "Layer " << l << ":" << endl << setw(FIELD_WIDTH+2) << "";
142 for(int c=0; c<board.cols; c++) os << setw(FIELD_WIDTH) << c << "|";
143 os << endl;
144 for(int r=0; r<board.rows; r++) {
145 os << setw(FIELD_WIDTH) << r << ": ";
146 for(int c=0; c<board.cols; c++) {
147 const Field &f = board(l,r,c);
148 if(f.isCovered()) os << setw(FIELD_WIDTH) << (f.hasMark() ? setfill('#') : setfill('_')) << "" << " " << setfill(' ');
149 else if(f.hasMine()) os << setw(FIELD_WIDTH) << setfill('!') << "" << " " << setfill(' ');
150 else {
151 int neighbours = f.getNeighboursCnt();
152 if(neighbours) os << setfill('_') << setw(FIELD_WIDTH) << neighbours << setfill(' ') << " ";
153 else os << setw(FIELD_WIDTH) << "" << " ";
156 os << endl;
158 os << endl;
160 return os;
163 /* -------------------------------------------------------------------------- */
165 /* rozmisti miny po hracim poli
166 * vraci 0 pri uspechu nebo 1, ma-li byt rozmisteno prilis mnoho min */
167 int Board::placeMines(void) {
168 int minesPlaced = 0;
169 if(this->minesCount > layers*rows*cols/2) return 1;
171 srand(time(NULL)); //randomize
173 while(minesPlaced < this->minesCount) {
174 Field &field = getFieldRW(rand() % this->layers, rand() % this->rows, rand() % this->cols);
175 if(!field.hasMine()) {
176 field.setMine(true);
177 minesPlaced++;
181 return 0;
184 /* -------------------------------------------------------------------------- */
186 /* ocisluje policka podle pritomnosti min na sousednich polich */
187 int Board::numberBoard(void) {
188 // pro vsechna pole zjistime pocet min v jejich okoli (vc. sebe sama) a tyto pocty v nich ulozime
189 for(int l=0; l<this->layers; l++) {
190 for(int r=0; r<this->rows; r++) {
191 for(int c=0; c<this->cols; c++) {
193 int cnt = 0;
194 // projdeme sousedni policka (neighbours) (vc. tohoto) a za kazdou minu inkrementujeme >pocet "sousednich" min< v tomto poli
195 for(int nl=-1; nl<=1; nl++) {
196 for(int nr=-1; nr<=1; nr++) {
197 for(int nc=-1; nc<=1; nc++) {
198 try {
199 cnt += getField(l+nl, r+nr, c+nc).hasMine(); //pri neplatnych souradnicich vyhazuje vyjimku, kterou zde ignorujeme
200 } catch(OutOfBoundsException) {
201 //neni chyba
206 this->board[l][r][c].setNeighboursCnt(cnt);
207 this->board[l][r][c].setRemainingNeighboursCnt(cnt);
212 return 0;
215 /* -------------------------------------------------------------------------- */
217 void Board::setFieldMark(int layer, int row, int col, bool value) {
218 Field & f = getFieldRW(layer, row, col);
219 if(f.hasMark() != value) {
220 f.setMark(value);
221 int inc = (value ? -1 : 1);
223 // projdeme sousedni policka (neighbours) (vc. tohoto) a za kazdou minu
224 // (in/de)krementujeme >zbyvajici pocet "sousednich" min< v danem poli
225 for(int nl=-1; nl<=1; nl++) {
226 for(int nr=-1; nr<=1; nr++) {
227 for(int nc=-1; nc<=1; nc++) {
228 try {
229 getFieldRW(layer+nl, row+nr, col+nc).fixRemainingNeighboursCnt(inc);
230 } catch(OutOfBoundsException) {
231 //pole neexistuje
239 /* -------------------------------------------------------------------------- */
241 /* odkryje zadane pole, nema-li v okoli zadne miny, odkryje i sousedni pole;
242 * vraci true, pokud je pritomna mina */
243 bool Board::uncover(int layer, int row, int col) {
244 Field &f = getFieldRW(layer, row, col);
245 if(f.hasMark()) throw AccessForbiddenException("Field marked as mined cannot be uncovered.");
246 if(!f.isCovered()) return f.hasMine();
247 if(!f.uncover()) { //pokud ma 0 sousedu - zadne miny v okoli (vcetne sebe sama), odkryjeme sousedni miny
248 for(int l=-1; l<=1; l++)
249 for(int r=-1; r<=1; r++)
250 for(int c=-1; c<=1; c++) {
251 if(l==0 && r==0 && c==0) continue; //sebe sama uz neodkryvame
252 try {
253 uncover(layer+l, row+r, col+c); //rekurzivne odkryjeme sousedni pole
254 } catch(OutOfBoundsException) {
255 // neni chyba
256 } catch(AccessForbiddenException) {
257 // taky neni chyba (pole bylo zakryte, tak jsme ho proste neodkryli)
261 return f.hasMine();
264 /* -------------------------------------------------------------------------- */
266 /* vraci true, pokud jsou odkryta vsechna pole bez min */
267 bool Board::isCleared(void) const {
268 for(int l=0; l<this->layers; l++) {
269 for(int r=0; r<this->rows; r++) {
270 for(int c=0; c<this->cols; c++)
271 if(board[l][r][c].isCovered() && !board[l][r][c].hasMine()) return false;
274 return true;
277 /* -------------------------------------------------------------------------- */
279 /* vraci pocet poli oznacenych jako zaminovana */
280 int Board::getMarkedCount(void) const {
281 int cnt=0;
282 for(int l=0; l<this->layers; l++) {
283 for(int r=0; r<this->rows; r++) {
284 for(int c=0; c<this->cols; c++)
285 cnt += (board[l][r][c].hasMark());
288 return cnt;
291 /* -------------------------------------------------------------------------- */
293 /* vraci herni mod podle rozmeru desky */
294 Board::GameMode Board::getMode(void) const {
295 return getMode(layers, rows, cols, minesCount);
298 /* -------------------------------------------------------------------------- */
300 /* vraci herni mod podle zadanych rozmeru */
301 Board::GameMode Board::getMode(int layers, int rows, int cols, int mines) {
302 if(layers==PRESETS[0][0] && rows==PRESETS[0][1] && cols==PRESETS[0][2] && mines==PRESETS[0][3]) return ModeRookie;
303 if(layers==PRESETS[1][0] && rows==PRESETS[1][1] && cols==PRESETS[1][2] && mines==PRESETS[1][3]) return ModeAdvanced;
304 if(layers==PRESETS[2][0] && rows==PRESETS[2][1] && cols==PRESETS[2][2] && mines==PRESETS[2][3]) return ModeSuicide;
305 return ModeCustom;
308 /* -------------------------------------------------------------------------- */