mines.c 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. /***********************************************************************
  2. *
  3. * This file is a part of Minesweeper - a computer game
  4. * Copyright (C) 2020, 2021 Andrew Tokarskiy <tokarskiy.a@keemail.me>
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>
  18. *
  19. *************************************************************************/
  20. #include "mines.h"
  21. #include <stdlib.h>
  22. #define ALLOCATE(type, amount) (type*)malloc(sizeof(type) * amount)
  23. void randomizeMines(Game* const game, const size_t firstMoveI, const size_t firstMoveJ, const int minesAmount);
  24. Game* initGame(const size_t height, const size_t width, const int minesAmount) {
  25. int** map = ALLOCATE(int*, height);
  26. int** statuses = ALLOCATE(int*, height);
  27. for (int i = 0; i < height; i++) {
  28. map[i] = ALLOCATE(int, width);
  29. statuses[i] = ALLOCATE(int, width);
  30. for (int j = 0; j < width; j++) {
  31. map[i][j] = 0;
  32. statuses[i][j] = STATUS_NULL;
  33. }
  34. }
  35. Game* game = ALLOCATE(Game, 1);
  36. game->map = map;
  37. game->statuses = statuses;
  38. game->width = width;
  39. game->height = height;
  40. game->minesAmount = minesAmount;
  41. game->initialized = 0;
  42. return game;
  43. }
  44. const size_t sqr(const size_t a) {
  45. return a * a;
  46. }
  47. const size_t sqrdif(const size_t a, const size_t b) {
  48. return a > b ? sqr(a - b) : sqr(b - a);
  49. }
  50. const size_t decIndex(const size_t a) {
  51. return a == 0 ? a : a - 1;
  52. }
  53. const size_t incIndex(const size_t a, const size_t max) {
  54. return a + 1 >= max ? a : a + 1;
  55. }
  56. void shuffle(size_t* array, const size_t count) {
  57. if (count > 1) {
  58. for (size_t i = 0; i < count - 1; i++) {
  59. const size_t j = i + rand() / (RAND_MAX / (count - i) + 1);
  60. const int t = array[j];
  61. array[j] = array[i];
  62. array[i] = t;
  63. }
  64. }
  65. }
  66. const int calculateMinesAround(Game* const game, const size_t i, const size_t j) {
  67. int amount = 0;
  68. const size_t minII = decIndex(i);
  69. const size_t maxII = incIndex(i, game->height);
  70. const size_t minJJ = decIndex(j);
  71. const size_t maxJJ = incIndex(j, game->width);
  72. for (size_t ii = minII; ii <= maxII; ii++) {
  73. for (size_t jj = minJJ; jj <= maxJJ; jj++) {
  74. if (ii == i && jj == j) {
  75. continue;
  76. }
  77. if (game->map[ii][jj] == MINE_INDEX) {
  78. amount++;
  79. }
  80. }
  81. }
  82. return amount;
  83. }
  84. void randomizeMines(Game* const game, const size_t firstMoveI, const size_t firstMoveJ, const int minesAmount) {
  85. int** map = game->map;
  86. const size_t size = game->width * game->height;
  87. size_t* possiblePlaces = ALLOCATE(size_t, size);
  88. size_t pos = 0;
  89. for (size_t i = 0; i < size; i++) {
  90. const size_t posI = i / game->width;
  91. const size_t posJ = i % game->width;
  92. const size_t sqDistance = sqrdif(firstMoveI, posI) + sqrdif(firstMoveJ, posJ);
  93. if (sqDistance >= 4) { // 2 - distance between points (0, 0) & (2, 0). sqrt(2) - distance between points (0, 0) & (1, 1)
  94. possiblePlaces[pos] = i;
  95. pos++;
  96. }
  97. }
  98. const size_t possiblePlacesSize = pos;
  99. shuffle(possiblePlaces, possiblePlacesSize);
  100. for (int i = 0; i < minesAmount; i++) {
  101. const size_t place = possiblePlaces[i];
  102. const size_t posI = place / game->width;
  103. const size_t posJ = place % game->width;
  104. map[posI][posJ] = MINE_INDEX;
  105. }
  106. free(possiblePlaces);
  107. for (size_t i = 0; i < game->height; i++) {
  108. for (size_t j = 0; j < game->width; j++) {
  109. if (map[i][j] != MINE_INDEX) {
  110. map[i][j] = calculateMinesAround(game, i, j);
  111. }
  112. }
  113. }
  114. }
  115. void freeGame(Game* const game) {
  116. int* line;
  117. size_t i;
  118. for (i = 0; i < game->height; i++) {
  119. line = game->map[i];
  120. free(line);
  121. }
  122. for (i = 0; i < game->height; i++) {
  123. line = game->statuses[i];
  124. free(line);
  125. }
  126. free(game->map);
  127. free(game->statuses);
  128. free(game);
  129. }
  130. // call only when map[i][j] >= 0
  131. void fill(Game* const game, const size_t i, const size_t j) {
  132. if (game->statuses[i][j] >= 0) {
  133. return;
  134. }
  135. game->statuses[i][j] = game->map[i][j];
  136. const size_t minII = decIndex(i);
  137. const size_t maxII = incIndex(i, game->height);
  138. const size_t minJJ = decIndex(j);
  139. const size_t maxJJ = incIndex(j, game->width);
  140. if (game->map[i][j] == 0) {
  141. for (size_t ii = minII; ii <= maxII; ii++) {
  142. for (size_t jj = minJJ; jj <= maxJJ; jj++) {
  143. if (ii == i && jj == j) {
  144. continue;
  145. }
  146. fill(game, ii, jj);
  147. }
  148. }
  149. }
  150. }
  151. const ActionResult checkWinStatus(Game* const game) {
  152. int** statuses = game->statuses;
  153. int nullCount = 0;
  154. for (size_t i = 0; i < game->height; i++) {
  155. for (size_t j = 0; j < game->width; j++) {
  156. if (statuses[i][j] == STATUS_FLAG || statuses[i][j] == STATUS_NULL) {
  157. nullCount++;
  158. }
  159. }
  160. }
  161. return nullCount == game->minesAmount ? Win : Continue;
  162. }
  163. // return 0 -> continue, return 1 -> lose, 2 -> win
  164. const ActionResult action(Game* const game, const size_t i, const size_t j, const ActionType actionType) {
  165. int** statuses = game->statuses;
  166. int** map = game->map;
  167. if (statuses[i][j] >= 0) { // if cell is opened
  168. return Continue;
  169. }
  170. if (actionType == MarkMine && statuses[i][j] == STATUS_NULL) {
  171. statuses[i][j] = STATUS_FLAG;
  172. return Continue;
  173. }
  174. if (actionType == MarkMine && statuses[i][j] == STATUS_FLAG) {
  175. statuses[i][j] = STATUS_NULL;
  176. return Continue;
  177. }
  178. if (game->initialized == 0) {
  179. randomizeMines(game, i, j, game->minesAmount);
  180. game->initialized = 1;
  181. }
  182. if (game->map[i][j] == MINE_INDEX) {
  183. for (size_t ii = 0; ii < game->height; ii++) {
  184. for (size_t jj = 0; jj < game->width; jj++) {
  185. if (map[ii][jj] == MINE_INDEX) {
  186. statuses[ii][jj] = STATUS_MINE;
  187. }
  188. }
  189. }
  190. return Lose;
  191. }
  192. fill(game, i, j);
  193. return checkWinStatus(game);
  194. }