Thursday, November 13, 2008

Step 2 - The Minesweeper Class

Okay, after this poor theoretical stuff, I want to have some more fun and start coding. I wanted to have this algorithm first, to make the programing easier later on, so that I can focus absolutely on coding and learning the syntax, instead of thinking about program logics while learning C++.

So the first thing I realized was the main game class. My first steps reminded me of the pictures of new born cows: They are able to walk from the first minute of their life, but their legs are shaking heavily and they hit the dirt a lot. =)

Some programming details, that I learned and find very useful to know:

I'm using the this. keyword a lot in Actionscript 3. It gives me a lot of control over the Class variables, separation them from variables with the same name, that are just available in a class method. ( -> There can be a difference between this.var and var)
What was new for me is the syntax: this->var. Let me see if I got this right: Everytime, we address something via a pointer, we use the arrow ->. "this", in this case is a pointer to the class itself. If we had created a class directly as a variable, we'd use the point syntax.

Another very cool thing, that I learned is the initialization list:
When create a constructor, you often have the case, that you pass an argument, that you have to pass to a member variable of the class. So the first lines of your constructor will probably look like this:



MyClass::MyClass(int var1, int var2, int var3){

this->var1 = var1;
this->var2 = var2;
this->var3 = var3;
(...)



And so on. But C++ comes with a very handy feature, that makes this procedure a lot slimmer.



MyClass::MyClass(int var1, int var2, int var3):var1(var1), var2(var2), var3(var3){
(...)



Just put the variables into the constructor line directly next to a colon after the parameters. I think the syntax is kind of self-explaining.
Note that the member variables have to be initiated in the header file for reasons of memory calculation.


Another note: I intentionally avoided the usage of the "namespace" command. It makes coding a lot more comfortable, because I don't have to write things like "std::cout" all the time. But I wanted to code it as detailed as possible to get a feeling for where the most important methods are to find.

So, here's the Class:

Minesweeper.h


/*
* Minesweeper.h
* MineSweeper++
*
* Created by Felix Ullrich on 10/18/08.
* Copyright 2008 fuX. All rights reserved.
*
* This class represents a Minesweeper API
*
*/
#ifndef MINESWEEPER_H
#define MINESWEEPER_H

#include
#include

class MineSweeper{

public:

//public member variables
int mineCount;
bool initiated;

//public member methods
MineSweeper(int width, int height, int mineCount);
~MineSweeper();
int revolveField(int x, int y);
bool markItem(int x, int y);
bool revolveAdjacentFields(int x, int y);
std::vector< std::vector > getFieldVector();

private:

//private member variables
std::vector< std::vector > queue;
std::vector< std::vector > minefield;
std::vector< std::vector > revolvedfield;
std::vector< std::vector > flagfield;

//private member methods
int getAdjacentMineCount(int x, int y);
int getAdjacentFlagCount(int x, int y);
int getAdjacentCount(std::vector< std::vector > field, int x, int y);
void revolveNextQueueItem();
void addNecessaryItemsToQueue(std::vector< std::vector > adjacentArray);
bool MineSweeper::isItemMarked(std::vector item);
bool MineSweeper::isItemInQueue(std::vector item);
void fillMineField(int clickedX, int clickedY);
std::vector< std::vector > getAdjacentFields(int x, int y);

};

#endif /* MINESWEEPER_H */



Minesweeper.cpp



/*
* Minesweeper.cpp
* MineSweeper++
*
* Created by Felix Ullrich on 10/18/08.
* Copyright 2008 fuX. All rights reserved.
*
*/

#include "Minesweeper.h"

#include
#include
#include

MineSweeper::MineSweeper(int width, int height, int mineCount):
//Initializations
mineCount(mineCount){

//Constructor
this->minefield.assign(width, std::vector(height));
this->revolvedfield.assign(width, std::vector(height));
this->flagfield.assign(width, std::vector(height));

/** TODO: Event Empty mine field created **/

}

MineSweeper::~MineSweeper(){

//Destructor
std::cout << "Program End" << std::endl;

}

int MineSweeper::revolveField(int x, int y){

if (!this->flagfield[x][y]){
if (!this->revolvedfield[x][y]){
this->revolvedfield[x][y] = true;
if (!this->initiated){
std::cout << "First mine" << std::endl;
this->fillMineField(x, y);
this->initiated = true;
/** TODO: Event -> Mine field filled **/
}

//If the field is a mine
if (this->minefield[x][y]){
/** TODO: Event -> Mine exploded (x, y, 9) **/
std::cout << "Mine Exploded." << std::endl;
return 9;
} else {
std::cout << "No Mine at this field." << std::endl;
int adjacentMines = this->getAdjacentMineCount(x, y);
//If the mine count is larger than 0
if (adjacentMines > 0){
/** TODO: Event -> Field has adjacentMines count (x, y, adjacentMines) **/

return adjacentMines;
} else {
/** TODO: Event -> Field has 0 adjacent mines (x, y, 0) **/

std::vector< std::vector > adjacentFields = this->getAdjacentFields(x, y);
this->addNecessaryItemsToQueue(adjacentFields);
this->revolveNextQueueItem();
return 0;
}
}
}
}

}

std::vector< std::vector > MineSweeper::getFieldVector(){

std::vector< std::vector > field;
field.assign(this->minefield.size(), std::vector(this->minefield[0].size()));

for (int i = 0; i < this->minefield.size(); i++){

for (int j = 0; j < this->minefield[0].size(); j++){

if (!this->revolvedfield[i][j]){
if (this->flagfield[i][j]){
field[i][j] = 11;
} else {
field[i][j] = 10;
}
} else {
if (this->minefield[i][j]){
field[i][j] = 9;
} else {
field[i][j] = this->getAdjacentMineCount(i, j);
}
}

}

}

return field;

}

int MineSweeper::getAdjacentMineCount(int x, int y){

std::cout << "Get Adjacent Mine Count." << std::endl;
return this->getAdjacentCount(this->minefield, x, y);

}

int MineSweeper::getAdjacentFlagCount(int x, int y){

std::cout << "Get Adjacent Flag Count." << std::endl;
return this->getAdjacentCount(this->flagfield, x, y);

}

int MineSweeper::getAdjacentCount(std::vector< std::vector > field, int x, int y){

int adjacentItems = 0;
//Iterate each field around the selected field
for (int i = x - 1; i <= x + 1; i++){

for (int j = y - 1; j <= y + 1; j++){
//If the checked field is not out of bounds
if (i != -1 && j != -1 && i != field.size() && j != field[0].size()){
//If the field is filled
if(i != 0 || j != 0){
if (field[i][j]){
adjacentItems++;
}
}

}

}

}

std::cout << "Adjacent Items: " << adjacentItems << std::endl;
return adjacentItems;

}

void MineSweeper::revolveNextQueueItem(){

if (this->queue.size() > 0){
std::vector item = this->queue[this->queue.size() - 1];
std::cout << "Queue: " << this->queue.size() << " - ";
this->queue.pop_back();
std::cout << this->queue.size() << std::endl;
this->revolveField(item[0], item[1]);
}

if (this->queue.size() > 0){
this->revolveNextQueueItem();
}

}

void MineSweeper::addNecessaryItemsToQueue(std::vector< std::vector > adjacentArray){

for (int i = 0; i < adjacentArray.size(); i++){
if (!this->isItemInQueue(adjacentArray[i]) && !isItemMarked(adjacentArray[i])){
this->queue.push_back(adjacentArray[i]);
}
}

}

bool MineSweeper::isItemMarked(std::vector item){

if (this->flagfield[item[0]][item[1]]){
return true;
}

return false;

}

bool MineSweeper::isItemInQueue(std::vector item){

for (int i = 0; i < this->queue.size(); i++){
if (item[0] == this->queue[i][0] && item[1] == this->queue[i][1]){
return true;
}
}

return false;

}

bool MineSweeper::markItem(int x, int y){

if (!this->revolvedfield[x][y]){
this->flagfield[x][y] = this->flagfield[x][y] == false;
}

return this->flagfield[x][y];

}

void MineSweeper::fillMineField(int clickedX, int clickedY){
std::srand( (unsigned)std::time( NULL ) );

std::cout << "Mine Field filled; " << clickedX << " and " << clickedY << " ignored." << std::endl;
int addedMines = 0;
while (addedMines < this->mineCount){
int rndX = std::rand() % this->minefield.size();
int rndY = std::rand() % this->minefield[0].size();
if (!this->minefield[rndX][rndY]){
if (clickedX != rndX || clickedY != rndY){
this->minefield[rndX][rndY] = true;
std::cout << "Field: " << rndX << " - " << rndY << std::endl;
addedMines++;
}
}
}

}

std::vector< std::vector > MineSweeper::getAdjacentFields(int x, int y){

std::vector< std::vector > adjacentFields;

for (int i = x - 1; i <= x + 1; i++){

for (int j = y - 1; j <= y + 1; j++){
//If the checked field is not out of bounds
if (i != -1 && j != -1 && i != this->minefield.size() && j != this->minefield[0].size()){

if (i != x || j != y){
std::vector field;
field.push_back(i);
field.push_back(j);
adjacentFields.push_back(field);
}

}

}

}

return adjacentFields;

}

bool MineSweeper::revolveAdjacentFields(int x, int y){

if (this->getAdjacentFlagCount(x, y) == this->getAdjacentMineCount(x, y)){
this->addNecessaryItemsToQueue(this->getAdjacentFields(x, y));
this->revolveNextQueueItem();
return true;
}

return false;

}

No comments: