Game.cpp 13 KB

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