123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239 |
- /***********************************************************************
- *
- * This file is a part of Minesweeper - a computer game
- * Copyright (C) 2020, 2021 Andrew Tokarskiy <tokarskiy.a@keemail.me>
- *
- * 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 <http://www.gnu.org/licenses/>
- *
- *************************************************************************/
- #include "mines.h"
- #include <stdlib.h>
- #define ALLOCATE(type, amount) (type*)malloc(sizeof(type) * amount)
-
- void randomizeMines(Game* const game, const size_t firstMoveI, const size_t firstMoveJ, const int minesAmount);
- Game* initGame(const size_t height, const size_t width, const int minesAmount) {
- int** map = ALLOCATE(int*, height);
- int** statuses = ALLOCATE(int*, height);
- for (int i = 0; i < height; i++) {
- map[i] = ALLOCATE(int, width);
- statuses[i] = ALLOCATE(int, width);
- for (int j = 0; j < width; j++) {
- map[i][j] = 0;
- statuses[i][j] = STATUS_NULL;
- }
- }
- Game* game = ALLOCATE(Game, 1);
- game->map = map;
- game->statuses = statuses;
- game->width = width;
- game->height = height;
- game->minesAmount = minesAmount;
- game->initialized = 0;
- return game;
- }
- const size_t sqr(const size_t a) {
- return a * a;
- }
- const size_t sqrdif(const size_t a, const size_t b) {
- return a > b ? sqr(a - b) : sqr(b - a);
- }
- const size_t decIndex(const size_t a) {
- return a == 0 ? a : a - 1;
- }
- const size_t incIndex(const size_t a, const size_t max) {
- return a + 1 >= max ? a : a + 1;
- }
- void shuffle(size_t* array, const size_t count) {
- if (count > 1) {
- for (size_t i = 0; i < count - 1; i++) {
- const size_t j = i + rand() / (RAND_MAX / (count - i) + 1);
- const int t = array[j];
- array[j] = array[i];
- array[i] = t;
- }
- }
- }
- const int calculateMinesAround(Game* const game, const size_t i, const size_t j) {
- int amount = 0;
- const size_t minII = decIndex(i);
- const size_t maxII = incIndex(i, game->height);
- const size_t minJJ = decIndex(j);
- const size_t maxJJ = incIndex(j, game->width);
- for (size_t ii = minII; ii <= maxII; ii++) {
- for (size_t jj = minJJ; jj <= maxJJ; jj++) {
- if (ii == i && jj == j) {
- continue;
- }
- if (game->map[ii][jj] == MINE_INDEX) {
- amount++;
- }
- }
- }
- return amount;
- }
- void randomizeMines(Game* const game, const size_t firstMoveI, const size_t firstMoveJ, const int minesAmount) {
- int** map = game->map;
- const size_t size = game->width * game->height;
- size_t* possiblePlaces = ALLOCATE(size_t, size);
- size_t pos = 0;
- for (size_t i = 0; i < size; i++) {
- const size_t posI = i / game->width;
- const size_t posJ = i % game->width;
- const size_t sqDistance = sqrdif(firstMoveI, posI) + sqrdif(firstMoveJ, posJ);
- if (sqDistance >= 4) { // 2 - distance between points (0, 0) & (2, 0). sqrt(2) - distance between points (0, 0) & (1, 1)
- possiblePlaces[pos] = i;
- pos++;
- }
- }
- const size_t possiblePlacesSize = pos;
- shuffle(possiblePlaces, possiblePlacesSize);
- for (int i = 0; i < minesAmount; i++) {
- const size_t place = possiblePlaces[i];
- const size_t posI = place / game->width;
- const size_t posJ = place % game->width;
-
- map[posI][posJ] = MINE_INDEX;
- }
- free(possiblePlaces);
- for (size_t i = 0; i < game->height; i++) {
- for (size_t j = 0; j < game->width; j++) {
- if (map[i][j] != MINE_INDEX) {
- map[i][j] = calculateMinesAround(game, i, j);
- }
- }
- }
- }
- void freeGame(Game* const game) {
- int* line;
- size_t i;
-
- for (i = 0; i < game->height; i++) {
- line = game->map[i];
- free(line);
- }
- for (i = 0; i < game->height; i++) {
- line = game->statuses[i];
- free(line);
- }
- free(game->map);
- free(game->statuses);
- free(game);
- }
- // call only when map[i][j] >= 0
- void fill(Game* const game, const size_t i, const size_t j) {
- if (game->statuses[i][j] >= 0) {
- return;
- }
- game->statuses[i][j] = game->map[i][j];
- const size_t minII = decIndex(i);
- const size_t maxII = incIndex(i, game->height);
- const size_t minJJ = decIndex(j);
- const size_t maxJJ = incIndex(j, game->width);
- if (game->map[i][j] == 0) {
- for (size_t ii = minII; ii <= maxII; ii++) {
- for (size_t jj = minJJ; jj <= maxJJ; jj++) {
- if (ii == i && jj == j) {
- continue;
- }
- fill(game, ii, jj);
- }
- }
- }
- }
- const ActionResult checkWinStatus(Game* const game) {
- int** statuses = game->statuses;
- int nullCount = 0;
-
- for (size_t i = 0; i < game->height; i++) {
- for (size_t j = 0; j < game->width; j++) {
- if (statuses[i][j] == STATUS_FLAG || statuses[i][j] == STATUS_NULL) {
- nullCount++;
- }
- }
- }
- return nullCount == game->minesAmount ? Win : Continue;
- }
- // return 0 -> continue, return 1 -> lose, 2 -> win
- const ActionResult action(Game* const game, const size_t i, const size_t j, const ActionType actionType) {
- int** statuses = game->statuses;
- int** map = game->map;
- if (statuses[i][j] >= 0) { // if cell is opened
- return Continue;
- }
- if (actionType == MarkMine && statuses[i][j] == STATUS_NULL) {
- statuses[i][j] = STATUS_FLAG;
- return Continue;
- }
- if (actionType == MarkMine && statuses[i][j] == STATUS_FLAG) {
- statuses[i][j] = STATUS_NULL;
- return Continue;
- }
- if (game->initialized == 0) {
- randomizeMines(game, i, j, game->minesAmount);
- game->initialized = 1;
- }
- if (game->map[i][j] == MINE_INDEX) {
- for (size_t ii = 0; ii < game->height; ii++) {
- for (size_t jj = 0; jj < game->width; jj++) {
- if (map[ii][jj] == MINE_INDEX) {
- statuses[ii][jj] = STATUS_MINE;
- }
- }
- }
- return Lose;
- }
- fill(game, i, j);
-
- return checkWinStatus(game);
- }
|