Game.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. #include <iostream>
  2. #include <climits>
  3. #include "Game.h"
  4. #include "Types.h"
  5. std::array<Vector, 8> Game::neighbours;
  6. std::array<Vector, 9 * 5> Game::fieldVectors;
  7. Game::Game() : active(-1, -1), mustTake(false) {
  8. std::cout << "0\n1\n0\n";
  9. neighbours[0].set(1, 0);
  10. neighbours[1].set(0, 1);
  11. neighbours[2].set(-1, 0);
  12. neighbours[3].set(0, -1);
  13. neighbours[4].set(1, 1);
  14. neighbours[5].set(-1, 1);
  15. neighbours[6].set(1, -1);
  16. neighbours[7].set(-1, -1);
  17. uint index = 0;
  18. for(uint y = 0; y < 5; y++) {
  19. for(uint x = 0; x < 9; x++) {
  20. fieldVectors[index++].set(x, y);
  21. }
  22. }
  23. }
  24. void Game::readLine() {
  25. String line;
  26. while(true) {
  27. char c = std::cin.get();
  28. if(c == '\n') {
  29. if(parseLine(line)) {
  30. return;
  31. }
  32. line.clear();
  33. } else if(c != '\r') {
  34. line.append(c);
  35. }
  36. }
  37. }
  38. bool Game::parseLine(const String& line) {
  39. if(line == " 0 1 2 3 4 5 6 7 8") {
  40. fields.read();
  41. fields.print(active);
  42. } else if(line == "Please enter origin x-axis") {
  43. makeSelection();
  44. } else if(line == "Please enter destination x-axis") {
  45. makeMove();
  46. } else if(line == "Please enter wether you want to Withdraw or Approach [W/A]") {
  47. takeStone();
  48. } else if(line == "Do you want to continue with your turn [Y/N]?") {
  49. std::cout << "Y\n";
  50. } else if(line == "************************Player 1 won!**********************" ||
  51. line == "************************Player 2 won!**********************") {
  52. std::cerr << line << "\n";
  53. return true;
  54. }
  55. return false;
  56. }
  57. bool Game::isInRange(const Vector& v) const {
  58. return v.x >= 0 && v.x <= 8 && v.y >= 0 && v.y <= 4;
  59. }
  60. bool Game::areNeighbors(const Vector& from, const Vector& to) const {
  61. if(from == to) {
  62. return false;
  63. }
  64. Vector diff = from - to;
  65. if(diff.x < -1 || diff.x > 1 || diff.y < -1 || diff.y > 1) {
  66. return false;
  67. }
  68. // either left, right, upper or lower neighbor
  69. if(diff.x == 0 || diff.y == 0) {
  70. return true;
  71. }
  72. // check if the field has diagonal connections (both coords are either even or uneven numbers)
  73. return (from.x & 1) == (from.y & 1);
  74. }
  75. uint Game::removeLine(const Vector& from, const Vector& to, Fields::State state) {
  76. Vector current = to;
  77. Vector unit = to - from;
  78. uint rank = 0;
  79. while(true) {
  80. current += unit;
  81. if(!isInRange(current) || !fields.hasState(current, state)) {
  82. return rank;
  83. }
  84. fields.setState(current, Fields::EMPTY);
  85. rank++;
  86. }
  87. }
  88. uint Game::getRank(const Vector& from, const Vector& to, Fields::State state) const {
  89. Vector current = to;
  90. Vector unit = to - from;
  91. uint rank = 0;
  92. while(true) {
  93. current += unit;
  94. if(!isInRange(current) || !fields.hasState(current, state)) {
  95. return rank;
  96. }
  97. rank++;
  98. }
  99. }
  100. uint Game::getStoneTakes(const Vector& from, const Vector& to) const {
  101. // an invalid turn cannot take any stones
  102. if(!isInRange(to) || !fields.hasState(to, Fields::EMPTY) || !areNeighbors(from, to) ||
  103. lastLocations.contains(to) || (to - from) == direction) {
  104. return 0;
  105. }
  106. // the turn must take stones
  107. Fields::State enemy = (fields.hasState(from, Fields::BLACK) ? Fields::WHITE : Fields::BLACK);
  108. if(getRank(from, to, enemy) == 0 && getRank(to, from, enemy) == 0) {
  109. return 0;
  110. }
  111. // actually do the turn on a copied game, store the rank (= taken stones)
  112. Game copy = *this;
  113. uint rank = copy.move(from, to);
  114. // recursively check all neighbors on the copied game for another move, take the best move
  115. uint maxRank = 0;
  116. for(Vector& m : neighbours) {
  117. uint rank = copy.getStoneTakes(to, to + m);
  118. if(rank > maxRank) {
  119. maxRank = rank;
  120. }
  121. }
  122. return rank + maxRank;
  123. }
  124. uint Game::getStoneFreedom(const Vector& from, Fields::State state) const {
  125. // no freedom if the stone is not yours
  126. if(!fields.hasState(from, state)) {
  127. return 0;
  128. }
  129. // count directions the stone can move to
  130. uint counter = 0;
  131. for(const Vector& m : neighbours) {
  132. Vector to = from + m;
  133. if(isInRange(to) && areNeighbors(from, to) && fields.hasState(to, Fields::EMPTY)) {
  134. counter++;
  135. }
  136. }
  137. return counter;
  138. }
  139. uint Game::getFreedom(Fields::State state) const {
  140. uint sum = 0;
  141. for(const Vector& v : fieldVectors) {
  142. sum += getStoneFreedom(v, state);
  143. }
  144. return sum;
  145. }
  146. uint Game::move(const Vector& from, const Vector& to) {
  147. // remember direction to block the same move
  148. direction = to - from;
  149. // remember the old location to block moving to it again
  150. lastLocations.add(from);
  151. // actually from the stone
  152. fields.setState(to, fields.getState(from));
  153. fields.setState(from, Fields::EMPTY);
  154. // get the enemy by inverting the goal state
  155. Fields::State enemy = (fields.hasState(to, Fields::BLACK) ? Fields::WHITE : Fields::BLACK);
  156. // compare quality of the two sides, take better side
  157. uint rankA = getRank(from, to, enemy);
  158. uint rankB = getRank(to, from, enemy);
  159. if(rankA >= rankB) {
  160. return removeLine(from, to, enemy);
  161. }
  162. return removeLine(to, from, enemy);
  163. }
  164. void Game::makeSelection() {
  165. // new turn starts - reset previously blocked turns
  166. lastLocations.clear();
  167. direction.set(0, 0);
  168. // remember if the following turn must take a stone
  169. mustTake = isTakingPossible();
  170. // calculate rank for all possible selections, take best selection
  171. Vector selection(0, 0);
  172. int maxRank = INT_MIN;
  173. for(Vector& to : fieldVectors) {
  174. if(!fields.hasState(to, Fields::EMPTY)) {
  175. continue;
  176. }
  177. // scan neighbors of empty fields for white stones
  178. for(Vector& m : neighbours) {
  179. Vector from = to + m;
  180. // prevent invalid stone locations
  181. if(!isInRange(from) || !fields.hasState(from, Fields::WHITE) || !areNeighbors(from, to)) {
  182. continue;
  183. }
  184. // prevent non taking turns if taking is a must
  185. if(mustTake && getRank(from, to, Fields::BLACK) + getRank(to, from, Fields::BLACK) == 0) {
  186. continue;
  187. }
  188. // calculate rank and potentially store the new max value
  189. int rank = getQuality(from, to);
  190. if(rank > maxRank) {
  191. maxRank = rank;
  192. selection = from;
  193. }
  194. }
  195. }
  196. // mark the stone as active
  197. active = selection;
  198. // send the selection to the server
  199. std::cerr << "Selecting (" << selection.x << ", " << selection.y << ")\n";
  200. // print the selection and the current game field for logging purposes
  201. fields.print(active);
  202. std::cout << selection.x << "\n" << selection.y << "\n";
  203. }
  204. void Game::makeMove() {
  205. // check all neighbors of active selection and choose the move with the highest rank
  206. Vector maxTo(0, 0);
  207. int maxRank = INT_MIN;
  208. for(Vector& m : neighbours) {
  209. Vector to = active + m;
  210. // prevent invalid moves
  211. if(!isInRange(to) || !fields.hasState(to, Fields::EMPTY) || lastLocations.contains(to) ||
  212. !areNeighbors(active, to) || m == direction) {
  213. continue;
  214. }
  215. // if this is not the first move or a must take move - prevent non taking move
  216. if(!lastLocations.isEmpty() || mustTake) {
  217. int rank = getRank(active, to, Fields::BLACK) + getRank(to, active, Fields::BLACK);
  218. if(rank == 0) {
  219. continue;
  220. }
  221. }
  222. // calculate rank and potentially store the new max value
  223. int rank = getQuality(active, to);
  224. if(rank > maxRank) {
  225. maxRank = rank;
  226. maxTo = to;
  227. }
  228. }
  229. // remember the old location to prevent moving to it
  230. lastLocations.add(active);
  231. // remember the direction to prevent moving in it again
  232. direction = maxTo - active;
  233. // mark the location as active
  234. active = maxTo;
  235. // send the move to the server
  236. std::cerr << "Move to (" << maxTo.x << ", " << maxTo.y << ")\n";
  237. // log the move
  238. std::cout << maxTo.x << "\n" << maxTo.y << "\n";
  239. }
  240. void Game::takeStone() {
  241. // calculate the better taking side ...
  242. uint rankA = getRank(active, active + direction, Fields::BLACK);
  243. uint rankB = getRank(active + direction, active, Fields::BLACK);
  244. // and send it to the server
  245. if(rankA >= rankB) {
  246. std::cout << "W\n";
  247. } else {
  248. std::cout << "A\n";
  249. }
  250. }
  251. int Game::getQuality(const Vector& from, const Vector& to) const {
  252. // base quality is how much stones this turn can take (+ following moves)
  253. int quality = getStoneTakes(from, to);
  254. // actually do the turn on a copy
  255. Game copy = *this;
  256. copy.move(from, to);
  257. // reset previous location and direction to simulate an opponent turn
  258. copy.lastLocations.clear();
  259. copy.direction.set(0, 0);
  260. // calculate white and black freedom on the potential new board
  261. int whiteFreedom = copy.getFreedom(Fields::WHITE);
  262. int blackFreedom = copy.getFreedom(Fields::BLACK);
  263. // calculates the strongest opponent turn
  264. uint maxRank = copy.getBestEnemyRank();
  265. // opponent count is important in the endgame
  266. int black = countBlack();
  267. if(black >= 8) {
  268. black = 1;
  269. }
  270. // finalize the quality formula
  271. return quality - maxRank * black + (whiteFreedom - blackFreedom) / 4;
  272. }
  273. int Game::countBlack() const {
  274. int counter = 0;
  275. for(Vector& v : fieldVectors) {
  276. counter += fields.hasState(v, Fields::BLACK);
  277. }
  278. return counter;
  279. }
  280. bool Game::isTakingPossible() const {
  281. // scan all fields for possible taking moves
  282. for(Vector& to : fieldVectors) {
  283. if(!fields.hasState(to, Fields::EMPTY)) {
  284. continue;
  285. }
  286. // check if any white neighbored stone can move to the found empty stone
  287. for(Vector& m : neighbours) {
  288. Vector from = to + m;
  289. if(!isInRange(from) || !areNeighbors(from, to) || !fields.hasState(from, Fields::WHITE)) {
  290. continue;
  291. }
  292. // return true if the white stone can take black stones in any direction
  293. if(getRank(from, to, Fields::BLACK) + getRank(to, from, Fields::BLACK) > 0) {
  294. return true;
  295. }
  296. }
  297. }
  298. return false;
  299. }
  300. uint Game::getBestEnemyRank() const {
  301. uint maxRank = 0;
  302. // scan all fields for best rank
  303. for(Vector& to : fieldVectors) {
  304. if(!fields.hasState(to, Fields::EMPTY)) {
  305. continue;
  306. }
  307. // scan neighbors of found empty fields
  308. for(Vector& m : neighbours) {
  309. Vector from = to + m;
  310. // prevent invalid moves
  311. if(!isInRange(from) || !fields.hasState(from, Fields::BLACK)) {
  312. continue;
  313. }
  314. // calculate and store the potentially better rank
  315. uint rank = getStoneTakes(from, to);
  316. if(rank > maxRank) {
  317. maxRank = rank;
  318. }
  319. }
  320. }
  321. return maxRank;
  322. }