Game.cpp 12 KB

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