/src/class/Go.php

https://bitbucket.org/klab_zhang_j/igo · PHP · 469 lines · 273 code · 49 blank · 147 comment · 60 complexity · b7db6b9b462262f87015a7e13a22fdb9 MD5 · raw file

  1. <?php
  2. require_once 'Go/Pawn.php';
  3. /**
  4. * I-go Core
  5. */
  6. class Go
  7. {
  8. private $_maxCols = 19;
  9. private $_maxRows = 19;
  10. /**
  11. * Go pawn collection hash table
  12. *
  13. * @var array()
  14. */
  15. private $_pawns = array();
  16. //status
  17. private $_pointNow = null;
  18. private $robPoint = null;
  19. private $vec = array();
  20. private $_step = 0;
  21. private $_stone = array();
  22. /**
  23. * Constructor
  24. *
  25. * @param int $maxCols
  26. * @param int $maxRows
  27. * @return void
  28. */
  29. public function __construct($maxCols = 19, $maxRows = 19)
  30. {
  31. $this->setMaxCols($maxCols)
  32. ->setMaxRows($maxRows);
  33. $this->_stone = array(
  34. 'black' => 0,
  35. 'white' => 0,
  36. 'gray' => 0,
  37. );
  38. //init chessboard
  39. //$row = array_fill(0, $this->_maxCols, Go_Pawn::NONE);
  40. //$this->_data = array_fill(0, $this->_maxRows, $row);
  41. $this->_initHashTable();
  42. //Zend_Debug::dump($this, '$this');
  43. }
  44. private function _initHashTable()
  45. {
  46. // y
  47. for ($j = 0; $j < $this->_maxRows; $j++) {
  48. // x
  49. for ($i = 0; $i < $this->_maxCols; $i++) {
  50. //$point = new Point($i, $j);
  51. $index = $this->getIndex($i, $j);
  52. $pawn = new Go_Pawn();
  53. $pawn->pointAround[0] = ($j == 0) ? NULL: new point($i, $j - 1); //up
  54. $pawn->pointAround[1] = ($j == $this->_maxCols - 1) ? NULL : new Point($i, $j + 1); //down
  55. $pawn->pointAround[2] = ($i == 0) ? NULL: new Point($i - 1, $j); //left
  56. $pawn->pointAround[3] = ($i == $this->_maxRows - 1) ? NULL : new Point($i + 1, $j); //roght
  57. $this->_pawns[$index] = $pawn;
  58. }
  59. }
  60. }
  61. /**
  62. * do step
  63. *
  64. * @param int $index
  65. * @param string $color
  66. * @return Go
  67. */
  68. public function doStep($index, $color)
  69. {
  70. $x = $this->getX($index);
  71. $y = $this->getY($index);
  72. $point = new Point($x, $y);
  73. //error_log("($x, $y)");
  74. //var_dump($x,$y,$point);
  75. if ($x < 0 || $x >= $this->_maxRows || $y < 0 || $y >= $this->_maxCols) {
  76. throw new Exception('Out of board.');
  77. }
  78. if (Go_Pawn::NONE != $this->getPawn($point)->getColor()) {
  79. throw new Exception('Has a pawn here.');
  80. }
  81. //set point
  82. $this->updateHash($point, $color);
  83. $this->getRival($point, $color);
  84. //check around
  85. if((!$this->isLink($point, $color) && !$this->isLink($point, Go_Pawn::NONE)) || !$this->isLive($point)) {
  86. $this->singleRemove($point);
  87. throw new Exception('Not allow here.');
  88. }
  89. $this->_pointNow->x = $point->x;
  90. $this->_pointNow->y = $point->y;
  91. return $this;
  92. }
  93. /**
  94. * remove rival
  95. * FIXME: remove problem
  96. *
  97. * @param Point $point
  98. * @param string $color
  99. */
  100. public function getRival(Point $point, $color)
  101. {
  102. $removeFlag = false;
  103. $pawn = $this->getPawn($point);
  104. $otherPoint = $pawn->pointAround;
  105. for ($i = 0; $i < 4; $i++) {
  106. $p = $otherPoint[$i];
  107. //Zend_Debug::dump($p, '$p');
  108. if (NULL != $p) {
  109. $otherPawn = $this->getPawn($p);
  110. //Zend_Debug::dump($otherPawn, '$otherPawn');
  111. if ($otherPawn->color != Go_Pawn::NONE && $otherPawn->color != $color) {
  112. if ($this->isLive($p)) {
  113. //echo "isLive";
  114. $this->vec = array();
  115. } else {
  116. //echo "xxx";
  117. $this->makeRobber($p);
  118. $this->doRemove();
  119. $this->vec = array();
  120. $removeFlag = true;
  121. }
  122. }
  123. }
  124. }
  125. if (!$removeFlag) {
  126. $this->robPoint = null;
  127. }
  128. }
  129. public function isRob(Point $p)
  130. {
  131. if ($this->robPoint == null) {
  132. return false;
  133. }
  134. if ($this->robPoint->x == $p->x && $this->robPoint->y == $p->y) {
  135. return true;
  136. }
  137. return false;
  138. }
  139. /**
  140. * single
  141. *
  142. * @param Point $point
  143. */
  144. public function makeRobber(Point $point)
  145. {
  146. if (count($this->vec) == 1) {
  147. $this->robPoint = $point;
  148. } else {
  149. $this->robPoint = null;
  150. }
  151. }
  152. /**
  153. * is any pawn link to empty?
  154. *
  155. * @param Point $point
  156. * @return bool
  157. */
  158. public function isLive(Point $point)
  159. {
  160. $index = $this->getIndex($point->x, $point->y);
  161. if (isset($this->vec[$index])) { //has been checked
  162. return false;
  163. }
  164. if ($this->isLink($point, Go_Pawn::NONE)) { //is live
  165. return true;
  166. }
  167. //save to temp var
  168. $this->vec[$index] = $point;
  169. $pawn = $this->getPawn($point);
  170. $otherPoint = $pawn->pointAround;
  171. for ($i = 0; $i < 4; $i++) {
  172. $p = $otherPoint[$i];
  173. if (NULL != $p) {
  174. $otherPawn = $this->getPawn($p);
  175. if ($otherPawn->color == $pawn->color) {
  176. if ($this->isLive($p)) {
  177. return true;
  178. }
  179. }
  180. }
  181. }
  182. return false;
  183. }
  184. /**
  185. * remove all saved point
  186. *
  187. */
  188. public function doRemove()
  189. {
  190. foreach ($this->vec as $point) {
  191. $this->singleRemove($point);
  192. }
  193. }
  194. /**
  195. * remove a point
  196. *
  197. * @param Point $point
  198. */
  199. public function singleRemove(Point $point)
  200. {
  201. //$index = $this->getIndex($point->x, $point->y);
  202. $pawn = $this->getPawn($point);
  203. $pawn->isThere = false;
  204. $pawn->color = Go_Pawn::NONE;
  205. //echo __METHOD__ . "({$point->x}, {$point->y})\n";
  206. //unset($this->_pawns[$index]);
  207. }
  208. /**
  209. * check Adjacent points
  210. *
  211. * @param Point $point
  212. * @param string $color
  213. */
  214. public function isLink(Point $point, $color)
  215. {
  216. $pawn = $this->getPawn($point);
  217. $otherPoint = $pawn->pointAround;
  218. //Zend_Debug::dump($pawn, '$pawn');
  219. //Zend_Debug::dump($otherPoint, 'other point');
  220. for ($i = 0; $i < 4; $i++) {
  221. $p = $otherPoint[$i];
  222. if (NULL != $p) {
  223. $otherPawn = $this->getPawn($otherPoint[$i]);
  224. if ($otherPawn->color == $color) {
  225. return true;
  226. }
  227. }
  228. }
  229. return false;
  230. }
  231. /**
  232. * set a pawn at point
  233. *
  234. * @param Point $point
  235. * @param string $color
  236. * @return Go
  237. */
  238. public function updateHash(Point $point, $color)
  239. {
  240. $pawn = $this->getPawn($point);
  241. $pawn->isThere = true;
  242. $pawn->color = $color;
  243. $this->_step = $this->_step + 1;
  244. $pawn->whichStep = $this->_step;
  245. return $this;
  246. }
  247. /**
  248. * get Pawn at point
  249. *
  250. * @param Point $point
  251. * @return Go_Pawn
  252. */
  253. public function getPawn(Point $point)
  254. {
  255. $index = $this->getIndex($point->x, $point->y);
  256. return $this->_pawns[$index];
  257. }
  258. /**
  259. * @return int
  260. */
  261. public function getMaxCols()
  262. {
  263. return $this->_maxCols;
  264. }
  265. /**
  266. * @return int
  267. */
  268. public function getMaxRows()
  269. {
  270. return $this->_maxRows;
  271. }
  272. /**
  273. * @param int $_maxCols
  274. * @return Go
  275. */
  276. public function setMaxCols($maxCols)
  277. {
  278. $this->_maxCols = $maxCols;
  279. return $this;
  280. }
  281. /**
  282. * @param int $_maxRows
  283. * @return Go
  284. */
  285. public function setMaxRows($maxRows)
  286. {
  287. $this->_maxRows = $maxRows;
  288. return $this;
  289. }
  290. public function getArray()
  291. {
  292. return $this->_data;
  293. }
  294. public function getCollection()
  295. {
  296. return $this->_pawns;
  297. }
  298. /**
  299. * set a pawn by Index
  300. *
  301. * @param int $index
  302. * @param mixed $pawn
  303. * @return Go
  304. */
  305. public function setIndex($index, $pawn)
  306. {
  307. $x = $this->getX($index);
  308. $y = $this->getY($index);
  309. $this->setPoint($x, $y, $pawn);
  310. return $this;
  311. }
  312. /**
  313. * set a pawn by Coordinate
  314. *
  315. * @param int $x
  316. * @param int $y
  317. * @param mixed $pawn
  318. * @return Go
  319. */
  320. public function setPoint($x, $y, $pawn)
  321. {
  322. if (Go_Pawn::NONE == (string)$this->_data[$x][$y]) {
  323. $pawn->setCoordinate($x, $y);
  324. $this->_data[$x][$y] = $pawn;
  325. //重新计算"气"
  326. } else {
  327. throw new Exception("The point ($x, $y) has already a pawn ({$this->_data[$x][$y]})");
  328. }
  329. return $this;
  330. }
  331. /**
  332. * Calculate score
  333. *
  334. * @return mixed
  335. */
  336. public function getScore()
  337. {
  338. foreach ($this->_pawns as $pawn) {
  339. if ($pawn->isBlack()) {
  340. $this->_stone['black']++;
  341. } elseif ($pawn->isWhite()) {
  342. $this->_stone['white']++;
  343. } else { //empty
  344. $black = 0;
  345. $white = 0;
  346. $otherPoint = $pawn->pointAround;
  347. for ($i = 0; $i < 4; $i++) {
  348. $p = $otherPoint[$i];
  349. if (NULL != $p) {
  350. $otherPawn = $this->getPawn($p);
  351. if ($otherPawn->color == Go_Pawn::BLACK) {
  352. ++$black;
  353. } elseif ($otherPawn->color == Go_Pawn::WHITE) {
  354. ++$white;
  355. }
  356. }
  357. }
  358. if ($black > 0 && $white > 0) {
  359. $this->_stone['gray']++;
  360. } elseif ($black > 0) {
  361. $this->_stone['black']++;
  362. } elseif ($white > 0) {
  363. $this->_stone['white']++;
  364. }
  365. }
  366. }
  367. return ($this->_stone);
  368. }
  369. public function display()
  370. {
  371. //var_dump($this->_data);
  372. foreach ($this->_data as $row) {
  373. foreach ($row as $col) {
  374. echo $col;
  375. }
  376. echo "\n";
  377. }
  378. }
  379. /**
  380. * get Coord X
  381. *
  382. * @param int $index
  383. * @return int
  384. */
  385. public function getX($index)
  386. {
  387. return $index % $this->_maxCols;
  388. }
  389. /**
  390. * get Coord Y
  391. *
  392. * @param int $index
  393. * @return int
  394. */
  395. public function getY($index)
  396. {
  397. return (int)floor($index / $this->_maxCols);
  398. }
  399. public function getIndex($x, $y)
  400. {
  401. return $x + $y * $this->_maxCols;
  402. }
  403. public function getStep()
  404. {
  405. return $this->_step;
  406. }
  407. public function getLastStep()
  408. {
  409. return $this->_pointNow;
  410. }
  411. }
  412. /*
  413. //test case
  414. $iGo = new Go();
  415. for ($i = 0; $i < 20; $i++) {
  416. $iGo->setPoint(rand(0, 18), rand(0, 18), rand(0, 1) ? Go::WHITE : Go::BLACK);
  417. }
  418. $iGo->display();
  419. */