[See newest posts for current problems]
Hello. I'm trying to make a little minesweeper game. It's my first C++ program and game, so it's taken quite a bit of time to create, even though it is far from complete. I've run into a lot of obstacles the last few hours or so, and maybe it's just that I haven't slept for 24 hours, but I just can't seem to solve them! If you can help out that would be great.
The biggest problem is that the promixity_pass function doesn't generate the correct numbers. It seems to be caused by bool mine(board&, int, int) function, which also doesn't seem to work correctly when called from main.cpp.
main.cpp
#include <iostream>
#include <string>
#include <vector>
using std::cin; using std::cout;
using std::endl; using std::ostream;
using std::string; using std::vector;
#include "board.h"
int main() {
bool user_exited = false;
int board_size_y = 8;
int board_size_x = 8;
int number_of_mines = 10;
while(!user_exited) {
board minefield = create_board(board_size_y, board_size_x, number_of_mines);
board overlay = create_board(board_size_y, board_size_x);
update_overlay(overlay);
bool game_over = false;
bool made_move = false;
while(!game_over) {
// print current board
print_board(minefield, overlay, cout);
// print stats
int move_x;
int move_y;
// get user input
while(!made_move) {
cout << "X: ";
cin >> move_x;
cout << "Y: ";
cin >> move_y;
// FIXME check if input is an integer
if (in_range(minefield, move_x, move_y))
made_move = true;
else
made_move = false;
}
made_move = false;
// stepped on a mine?
if(!mine(minefield, move_x, move_y)) {
update_overlay(overlay, move_x, move_y);
} else {
update_overlay(overlay);
print_board(minefield, overlay, cout);
cout << "You stepped on a mine, and were completely and utterly dismembered!" << endl; // add random msgs
game_over = true;
}
}
// FIXME add a menu here
user_exited = true;
}
return 0;
}
board.cpp
#include <stdexcept>
#include <iostream>
#include <cstdlib>
#include <vector>
#include <ctime>
#include "board.h"
using std::domain_error; using std::endl;
using std::ostream; using std::vector;
using std::rand; using std::srand;
/*
Generates and returns a squares_y * squares_x board, mines number of mines
on it.
*/
board create_board(const int& spots_y, const int& spots_x, int mines) {
board field;
row current_row;
int mines_left = mines;
int spots = spots_y * spots_x;
int spots_left_in_row = spots_x;
int spots_left = spots_y * spots_x;
if(mines >= spots_left)
throw domain_error("int mines is out of range.");
for(int y = 0; y <= spots_y; ++y) {
current_row = create_row(spots_y, spots_x, spots_left_in_row, spots_left, mines_left);
field.push_back(current_row);
spots_left_in_row = spots_x;
}
proximity_pass(field);
return field;
}
/*
Overloaded function that generates a board with no mines.
*/
board create_board(const int& spots_y, const int& spots_x) {
//FIXME make new create_row for this overload
board field;
row current_row;
int no_mines = 0;
int spots = spots_y * spots_x;
int spots_left_in_row = spots_x;
int spots_left = spots_y * spots_x;
for(int y = 0; y <= spots_y; ++y) {
current_row = create_row(spots_y, spots_x, spots_left_in_row, spots_left, no_mines);
field.push_back(current_row);
spots_left_in_row = spots_x;
}
return field;
}
/*
Generates and returns a row of spots_x elements.
*/
// FIXME make it check first if there are mines left to improve performance
row create_row(const int& spots_y, const int& spots_x, int& spots_left_in_row, int& spots_left, int& mines_left) {
row new_row;
static int ratio = calculate_ratio(3, spots_left, mines_left);
static int empty = 0;
static int mine = 9;
// can we afford to skip this row?
if(row_skippable(spots_left_in_row, spots_left, mines_left)) {
if(my_random() % 100 <= ratio) {
while(spots_left_in_row >= 0) {
new_row.push_back(empty);
--spots_left;
--spots_left_in_row;
}
return new_row;
}
}
while(spots_left_in_row >= 0) {
// can we afford to skip this spot?
if (spot_skippable(spots_left, mines_left)) {
// 50% chance
if(mines_left >= 0 && my_random() % 100 <= ratio) {
// mine
new_row.push_back(mine);
--spots_left;
--spots_left_in_row;
--mines_left;
} else {
// not mine
new_row.push_back(empty);
--spots_left;
--spots_left_in_row;
}
// plant mines!
} else {
// function to fill remaining row?
new_row.push_back(mine);
--spots_left;
--spots_left_in_row;
--mines_left;
}
}
return new_row;
}
/*
Writes the number of nearby mines to each
spot on the board.
*/
void proximity_pass(board& board) {
int nearby_mines = 0;
// iterate through all spots
for(int y = (board.size() - board.size()) + 1; y < board.size() - 1; ++y) {
for(int x = 1; x != (board.at(x)).size() - 1; ++x) {
nearby_mines = 0;
if(!mine(board, y, x)) {
// left upper corner
if(in_range(board, y - 1, x - 1)) {
if(mine(board, y - 1, x - 1))
++nearby_mines;
}
// one above
if(in_range(board, y - 1, x)) {
if(mine(board, y - 1, x))
++nearby_mines;
}
// right upper corner
if(in_range(board, y - 1, x + 1)) {
if(mine(board, y - 1 , x + 1))
++nearby_mines;
}
// one to the right
if(in_range(board, y, x + 1)) {
if(mine(board, y, x + 1))
++nearby_mines;
}
// right lower corner
if(in_range(board, y + 1, x + 1)) {
if(mine(board, y + 1, x + 1))
++nearby_mines;
}
// one below
if(in_range(board, y + 1, x)) {
if(mine(board, y + 1, x))
++nearby_mines;
}
// left lower corner
if(in_range(board, y + 1, x - 1)) {
if(mine(board, y + 1, x - 1))
++nearby_mines;
}
// one to the left
if(in_range(board, y, x - 1)) {
if(mine(board, y, x - 1))
++nearby_mines;
}
// insert number into board
(board.at(y)).at(x) = nearby_mines;
std::cout << nearby_mines << std::endl;
}
}
}
}
bool row_skippable(const int& spots_left_in_row, const int& spots_left, const int& mines_left) {
return (spots_left - spots_left_in_row > mines_left);
}
// FIXME merge these two?
bool spot_skippable(const int& spots_left, const int& mines_left) {
return (spots_left > mines_left);
}
// FIXME faster, more accurate, less memory consuming randomization = thx
int my_random() {
static int last = 0;
srand(time(NULL) + last);
last = rand();
return last;
}
int calculate_ratio(int multiplier, int& spots_left, int& mines_left) {
return (spots_left / mines_left) * multiplier;
}
bool mine(board& board, int x, int y) {
return ((board.at(y)).at(x) == 9);
}
bool in_range(const board& board, int x, int y) {
return (board.size() >= y && (board.at(0)).size() >= x);
}
/*
Prints the board to ostream out.
*/
// FIXME can we "update" the board here instead?
void print_board(const board& board, const board& overlay, ostream& out) {
for(int b = board.size() - board.size(); b < board.size() - 1; ++b) {
for(int r = 0; r != (board.at(b)).size() - 1; ++r) {
if((overlay.at(b)).at(r) == 0)
out << "? ";
else {
if((board.at(b)).at(r) == 0)
out << "-" << " ";
else
out << (board.at(b)).at(r) << " ";
}
}
out << endl;
}
}
/*
Unhides the spot given spot on the board.
*/
void update_overlay(board& overlay, const int& move_x, const int& move_y) {
if(in_range)
(overlay.at(move_y)).at(move_x) = 1;
}
// Overloaded version to show everything.
void update_overlay(board& overlay) {
for(board::iterator b_it = overlay.begin(); b_it < overlay.end(); ++b_it) {
for(row::iterator r_it = b_it->begin(); r_it < b_it->end(); ++r_it) {
*r_it = 1;
}
}
}
board.h
#ifndef GUARD_board_h
#define GUARD_board_h
#include <iostream>
#include <string>
#include <vector>
#include <map>
typedef std::vector<int> row;
typedef std::vector<row> board;
bool row_skippable (const int&, const int&, const int&);
bool spot_skippable (const int&, const int&);
bool in_range (const board&, int, int);
bool mine (board&, int, int);
int calculate_ratio (int, int&, int&);
int my_random ();
void print_board (const board&, const board&, std::ostream&);
void update_overlay (board&, const int&, const int&);
void update_overlay (board&);
void proximity_pass (board&);
row create_row (const int&, const int&, int&, int&, int&);
board create_board (const int&, const int&, int);
board create_board (const int&, const int&);
#endif
[Edited by - fredrikhcs on July 23, 2008 10:06:13 PM]