3 * Author: Petr Kubiznak
7 #include "../exceptions/GeneralException.h"
8 #include "../exceptions/OutOfBoundsException.h"
9 #include "../exceptions/AccessForbiddenException.h"
10 #include "../asserts.h"
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
) {
33 this->layers
= layers
;
36 this->minesCount
= minesCount
;
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
];
48 if(this->placeMines() != 0) throw GeneralException(ERR_DEFAULT
, "Too many mines, use lesser number.");
49 //vypocet sousednich min
54 /* -------------------------------------------------------------------------- */
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 {
73 /* -------------------------------------------------------------------------- */
75 /* vraci pocet radku */
76 int Board::getRowsCount(void) const {
80 /* -------------------------------------------------------------------------- */
82 /* vraci pocet sloupcu */
83 int Board::getColsCount(void) const {
87 /* -------------------------------------------------------------------------- */
89 /* vraci pocet min v celem poli */
90 int Board::getMinesCount(void) const {
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
)) ) {
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(' ') << " ";
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
<< "|";
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(' ');
151 int neighbours
= f
.getNeighboursCnt();
152 if(neighbours
) os
<< setfill('_') << setw(FIELD_WIDTH
) << neighbours
<< setfill(' ') << " ";
153 else os
<< setw(FIELD_WIDTH
) << "" << " ";
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) {
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()) {
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
++) {
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
++) {
199 cnt
+= getField(l
+nl
, r
+nr
, c
+nc
).hasMine(); //pri neplatnych souradnicich vyhazuje vyjimku, kterou zde ignorujeme
200 } catch(OutOfBoundsException
) {
206 this->board
[l
][r
][c
].setNeighboursCnt(cnt
);
207 this->board
[l
][r
][c
].setRemainingNeighboursCnt(cnt
);
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
) {
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
++) {
229 getFieldRW(layer
+nl
, row
+nr
, col
+nc
).fixRemainingNeighboursCnt(inc
);
230 } catch(OutOfBoundsException
) {
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
253 uncover(layer
+l
, row
+r
, col
+c
); //rekurzivne odkryjeme sousedni pole
254 } catch(OutOfBoundsException
) {
256 } catch(AccessForbiddenException
) {
257 // taky neni chyba (pole bylo zakryte, tak jsme ho proste neodkryli)
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;
277 /* -------------------------------------------------------------------------- */
279 /* vraci pocet poli oznacenych jako zaminovana */
280 int Board::getMarkedCount(void) const {
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());
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
;
308 /* -------------------------------------------------------------------------- */