PageRenderTime 50ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/com/gna/bstrds/backgammon/Board.java

https://github.com/modie/backgammon_android
Java | 874 lines | 590 code | 187 blank | 97 comment | 229 complexity | 9248f3db28064e877b3dcc713de67d9b MD5 | raw file
  1. //Αθανάσιος Τσιακούλιας Μανέττας - 3100190, Γιώργος Κυπριανίδης - 3100225
  2. package com.gna.bstrds.backgammon;
  3. import java.util.ArrayList;
  4. import java.util.Iterator;
  5. import java.util.Set;
  6. import java.util.HashSet;
  7. public class Board {
  8. public static final byte W = 1;
  9. public static final byte B = -1;
  10. public static final byte EMPTY = 0;
  11. /* the backgammon board */
  12. private Position[] positions;
  13. /* the last move that was played
  14. * on this board */
  15. private Move lastMove;
  16. /* the last player that played on this board */
  17. private byte lastColorPlayed;
  18. /* the value of the board. this is not
  19. * the value of the heuristic, it's the
  20. * value that is pushed from leaf boards(nodes)
  21. * to non-leaf boards while minimax is
  22. * being executed */
  23. private int value;
  24. /* the values of the two dice that were
  25. * last played on the board */
  26. private byte d1Pl;
  27. private byte d2Pl;
  28. public Board() {
  29. lastMove = new Move();
  30. /* edit this to change who plays first */
  31. lastColorPlayed = B;
  32. value = 0;
  33. positions = new Position[28];
  34. for(int i=0; i<28; i++) {
  35. positions[i] = new Position();
  36. }
  37. /* setting standard backgammon starting positions
  38. */
  39. positions[1].setCol(W);
  40. positions[1].setNum((byte)2);
  41. positions[6].setCol(B);
  42. positions[6].setNum((byte)5);
  43. positions[8].setCol(B);
  44. positions[8].setNum((byte)3);
  45. positions[12].setCol(W);
  46. positions[12].setNum((byte)5);
  47. positions[13].setCol(B);
  48. positions[13].setNum((byte)5);
  49. positions[17].setCol(W);
  50. positions[17].setNum((byte)3);
  51. positions[19].setCol(W);
  52. positions[19].setNum((byte)5);
  53. positions[24].setCol(B);
  54. positions[24].setNum((byte)2);
  55. /* we keep the eaten pills in [0] for white, and [25] for
  56. * black. also, we keep the pills that have exited the board
  57. * in [26] for white, and [27] for black
  58. */
  59. positions[0].setCol(W);
  60. positions[25].setCol(B);
  61. positions[26].setCol(W);
  62. positions[27].setCol(B);
  63. }
  64. public Board(Board board) {
  65. this.lastMove = board.lastMove;
  66. this.lastColorPlayed = board.lastColorPlayed;
  67. this.value = board.value;
  68. this.positions = new Position[28];
  69. this.d1Pl = board.d1Pl;
  70. this.d2Pl = board.d2Pl;
  71. for(int i=0; i<28; i++) {
  72. positions[i] = new Position(board.positions[i]);
  73. }
  74. }
  75. public Move getLastMove() {
  76. return lastMove;
  77. }
  78. public int getLastColPlayed() {
  79. return lastColorPlayed;
  80. }
  81. public Position[] getPositions() {
  82. return positions;
  83. }
  84. public int getValue() {
  85. return value;
  86. }
  87. public byte getd1Pl() {
  88. return d1Pl;
  89. }
  90. public byte getd2Pl() {
  91. return d2Pl;
  92. }
  93. public void setLastMove(Move lastMove) {
  94. this.lastMove.setFrom(lastMove.getFrom());
  95. this.lastMove.setTo(lastMove.getTo());
  96. this.lastMove.setCol(lastMove.getCol());
  97. }
  98. public void setLastColPlayed(byte lastCol) {
  99. this.lastColorPlayed = lastCol;
  100. }
  101. public void setPositions(Position[] pst) {
  102. for(int i=0; i<28; i++) {
  103. this.positions[i] = new Position(pst[i]);
  104. }
  105. }
  106. public void setValue(int value) {
  107. this.value = value;
  108. }
  109. public void setd1Pl(byte d1Pl) {
  110. this.d1Pl = d1Pl;
  111. }
  112. public void setd2Pl(byte d2Pl) {
  113. this.d2Pl = d2Pl;
  114. }
  115. public void playMove(byte from, byte to, byte col) {
  116. /* checking who wants to make the move, and manipulating
  117. * the board accordingly.
  118. *
  119. * in the case below, the black pill in position [to]
  120. * is eaten, so we store it in position [25] the 'else'
  121. * statement is written with the same logic.
  122. */
  123. if(col==W) {
  124. if(positions[to].getCol()==B) {
  125. positions[to].decr();
  126. positions[25].incr();
  127. }
  128. } else {
  129. if(positions[to].getCol()==W) {
  130. positions[to].decr();
  131. positions[0].incr();
  132. }
  133. }
  134. positions[from].decr();
  135. if(positions[from].getNum()==0 && from!=0 && from !=25) {
  136. positions[from].setCol(EMPTY);
  137. }
  138. positions[to].incr();
  139. positions[to].setCol(col);
  140. lastMove = new Move(from, to, col);
  141. lastColorPlayed = col;
  142. }
  143. public boolean lastrun(byte col) {
  144. /* the board is checked to see if the player
  145. * with color 'col' is in lastrun mode.
  146. * (the gathering phase of the game)
  147. */
  148. boolean lastrun = true;
  149. if(col==W) {
  150. for(int j=0; j<19; j++) {
  151. if(positions[j].getCol()==W && positions[j].getNum()>0) {
  152. lastrun = false;
  153. break;
  154. }
  155. }
  156. } else if(col== B) {
  157. for(int j=25; j>6; j--) {
  158. if(positions[j].getCol()==Board.B && positions[j].getNum()>0) {
  159. lastrun = false;
  160. break;
  161. }
  162. }
  163. }
  164. return lastrun;
  165. }
  166. public boolean moveIsLegal(byte from, byte to, byte col, byte d1, byte d2) {
  167. /* various legal checks (hopefully) in order of importance */
  168. boolean direction = (((col==Board.W) && ((to-from)>0)) || ((col==Board.B) && ((to-from)<0)));
  169. if(!direction && to!=27) {
  170. return false;
  171. }
  172. if(from==to) {
  173. return false;
  174. }
  175. if(from==26 || from==27) {
  176. return false;
  177. }
  178. if((from < 0) || (to<0) || (from>27) || (to>27)) {
  179. return false;
  180. }
  181. if(col==W) {
  182. if(positions[to].getCol()==B && positions[to].getNum()>1) {
  183. return false;
  184. }
  185. } else {
  186. if(positions[to].getCol()==W && positions[to].getNum()>1) {
  187. return false;
  188. }
  189. }
  190. if(positions[from].getCol() != col) {
  191. return false;
  192. }
  193. if(((Math.abs(to-from)!=d1) && (Math.abs(to-from)!=d2)) && !this.lastrun(col)) {
  194. return false;
  195. }
  196. if((col==W && positions[0].getNum()>0) && from != 0) {
  197. return false;
  198. }
  199. if((col==B && positions[25].getNum()>0) && from !=25) {
  200. return false;
  201. }
  202. if((col==W && to==27) || (col==B && to==26)) {
  203. return false;
  204. }
  205. if((col==W && to==25) || (col==B && to==0)) {
  206. return false;
  207. }
  208. if((to==26 && !this.lastrun(W))|| (to==27 && !this.lastrun(B))) {
  209. return false;
  210. }
  211. int farthestWhite = -1;
  212. if(to==26) {
  213. if(from<19) {
  214. return false;
  215. }
  216. for(int i=19; i<25; i++) {
  217. if(positions[i].getCol()==W) {
  218. farthestWhite = i;
  219. break;
  220. }
  221. }
  222. }
  223. int farthestBlack = -1;
  224. if(to==27) {
  225. if(from>6) {
  226. return false;
  227. }
  228. for(int i=6; i>0; i--) {
  229. if(positions[i].getCol()==B) {
  230. farthestBlack = i;
  231. break;
  232. }
  233. }
  234. }
  235. if(to==26 && from!=farthestWhite && (25-from)!=d1 && (25-from)!=d2) {
  236. return false;
  237. }
  238. if(to==27 && from!=farthestBlack && from!=d1 && from!=d2) {
  239. return false;
  240. }
  241. if(to==26 && from == farthestWhite) {
  242. if((25-from)>d1 && (25-from)>d2) {
  243. return false;
  244. }
  245. }
  246. if(to==27 && from == farthestBlack) {
  247. if(from>d1 && from>d2) {
  248. return false;
  249. }
  250. }
  251. return true;
  252. }
  253. public Move[] movegen(byte d1,byte d2, byte col) {
  254. /* this generates an array of moves on the current board.
  255. * the moves need not be legal. this method is
  256. * called only for single(non-double) dice rolls.
  257. */
  258. int counter = 0;
  259. Move[] moves;
  260. Set<Integer> froms = new HashSet<Integer>();
  261. /* counting how many positions contain
  262. * pills of our given color , and
  263. * adding those positions to a set
  264. */
  265. for(int i=0; i<26; i++) {
  266. if(positions[i].getCol()==col && positions[i].getNum()>0) {
  267. counter++;
  268. froms.add(i);
  269. }
  270. }
  271. /* the moves to be sent to getChildren()
  272. * are 2 per position if
  273. * we have a regular roll
  274. */
  275. moves = new Move[counter*2];
  276. Iterator<Integer> it = froms.iterator();
  277. int i = 0;
  278. int temp = 0;
  279. while (it.hasNext()) {
  280. if(col==W) {
  281. temp = it.next();
  282. if(temp+d1>24) {
  283. moves[i] = new Move((byte)temp, (byte)26, col);
  284. i++;
  285. } else {
  286. moves[i] = new Move((byte)temp, (byte)(temp+d1), col);
  287. i++;
  288. }
  289. if(temp+d2>24) {
  290. moves[i] = new Move((byte)temp, (byte)26, col);
  291. i++;
  292. } else {
  293. moves[i] = new Move((byte)temp, (byte)(temp+d2), col);
  294. i++;
  295. }
  296. } else if(col==B) {
  297. temp = it.next();
  298. if(temp-d1<1) {
  299. moves[i] = new Move((byte)temp, (byte)27, col);
  300. i++;
  301. } else {
  302. moves[i] = new Move((byte)temp, (byte)(temp-d1), col);
  303. i++;
  304. }
  305. if(temp-d2<1) {
  306. moves[i] = new Move((byte)temp, (byte)27, col);
  307. i++;
  308. } else {
  309. moves[i] = new Move((byte)temp, (byte)(temp-d2), col);
  310. i++;
  311. }
  312. }
  313. }
  314. return moves;
  315. }
  316. public Move[] movegen(byte dice, byte col) {
  317. /* generates moves for double
  318. * dice rolls, and for boards
  319. * where one of the dice of a
  320. * single dice roll has already
  321. * been played.
  322. */
  323. Move[] moves;
  324. Set<Integer> froms = new HashSet<Integer>();
  325. int counter = 0;
  326. for(int i=0; i<26; i++) {
  327. if(positions[i].getCol()==col && positions[i].getNum()>0) {
  328. counter++;
  329. froms.add(i);
  330. }
  331. }
  332. if(counter!=0)
  333. moves = new Move[counter];
  334. else
  335. return null;
  336. Iterator<Integer> it = froms.iterator();
  337. int i=0;
  338. int temp =0;
  339. while(it.hasNext()) {
  340. if(col==W) {
  341. temp = it.next();
  342. if(temp+dice>24) {
  343. moves[i] = new Move((byte)temp, (byte)26, col);
  344. i++;
  345. } else {
  346. moves[i] = new Move((byte)temp, (byte)(temp+dice), col);
  347. i++;
  348. }
  349. } else if(col==B) {
  350. temp = it.next();
  351. if(temp-dice<1) {
  352. moves[i] = new Move((byte)temp, (byte)27, col);
  353. i++;
  354. } else {
  355. moves[i] = new Move((byte)temp, (byte)(temp-dice), col);
  356. i++;
  357. }
  358. }
  359. }
  360. return moves;
  361. }
  362. public ArrayList<Board> getChildren(byte d1, byte d2, byte col) {
  363. /* generates and returns an ArrayList
  364. * of Board states. movegen() is called
  365. * from here to get all the possible moves
  366. * (regardless of legality) , then a legal
  367. * check is performed on each move, and
  368. * finally it gets played on a board, and added
  369. * to the list.
  370. */
  371. ArrayList<Board> children = new ArrayList<Board>();
  372. Set<Board> childSet = new HashSet<Board>();
  373. boolean dubs = (d1==d2);
  374. if(!dubs) {
  375. ArrayList<Board> temp = new ArrayList<Board>();
  376. /* movegen() fills the moves array with possible
  377. * moves(not necessarily legal ones).
  378. */
  379. Move[] moves = movegen(d1, d2, col);
  380. /* here the generated moves are played on a board
  381. * (if they were legal), and based on whether they
  382. * are terminal or not, they get added to a Set of
  383. * children, or a temporary ArrayList */
  384. for(int i=0; i<moves.length; i++) {
  385. if(moveIsLegal(moves[i].getFrom(), moves[i].getTo(), col, d1, d2)) {
  386. Board child = new Board(this);
  387. child.playMove(moves[i].getFrom(), moves[i].getTo(), col);
  388. if(child.isTerminal()) {
  389. childSet.add(child);
  390. } else {
  391. temp.add(child);
  392. }
  393. }
  394. }
  395. /* at this point we have a list of states
  396. * on whitch only one dice has been played,
  397. * so we want the other dice to be played
  398. * on each state as well. to achieve that we call
  399. * the second getChildren(dice, color) on
  400. * each previously generated board.
  401. */
  402. ArrayList<Board> temp2;
  403. for(Board child : temp) {
  404. if(Math.abs(child.getLastMove().getFrom()-child.getLastMove().getTo())==d1) {
  405. temp2 = child.getChildren(d2, col);
  406. if(!temp2.isEmpty()) {
  407. for(Board tempchild : temp2) {
  408. childSet.add(tempchild);
  409. }
  410. } else {
  411. childSet.add(child);
  412. }
  413. } else if(Math.abs(child.getLastMove().getFrom()-child.getLastMove().getTo())==d2) {
  414. temp2 = child.getChildren(d1, col);
  415. if(!temp2.isEmpty()) {
  416. for(Board tempchild : temp2) {
  417. childSet.add(tempchild);
  418. }
  419. } else {
  420. childSet.add(child);
  421. }
  422. } else {
  423. /* this part could probably be less complicated */
  424. if(col==W) {
  425. if((child.getLastMove().getFrom()+d1) > 24 && child.getLastMove().getFrom()+d2 > 24) {
  426. if(d1<d2) {
  427. temp2 = child.getChildren(d1, col);
  428. if(!temp2.isEmpty()) {
  429. for(Board tempchild : temp2) {
  430. childSet.add(tempchild);
  431. }
  432. } else {
  433. childSet.add(child);
  434. }
  435. } else {
  436. temp2 = child.getChildren(d2, col);
  437. if(!temp2.isEmpty()) {
  438. for(Board tempchild : temp2) {
  439. childSet.add(tempchild);
  440. }
  441. } else {
  442. childSet.add(child);
  443. }
  444. }
  445. } else if(child.getLastMove().getFrom()+d1 == 25) {
  446. temp2 = child.getChildren(d2, col);
  447. if(!temp2.isEmpty()) {
  448. for(Board tempchild : temp2) {
  449. childSet.add(tempchild);
  450. }
  451. } else {
  452. childSet.add(child);
  453. }
  454. } else if(child.getLastMove().getFrom()+d2 == 25) {
  455. temp2 = child.getChildren(d1, col);
  456. if(!temp2.isEmpty()) {
  457. for(Board tempchild : temp2) {
  458. childSet.add(tempchild);
  459. }
  460. } else {
  461. childSet.add(child);
  462. }
  463. }
  464. } else if(col==B) {
  465. if((child.getLastMove().getFrom()-d1) < 0 && child.getLastMove().getFrom()-d2 < 0) {
  466. if(d1<d2) {
  467. temp2 = child.getChildren(d1, col);
  468. if(!temp2.isEmpty()) {
  469. for(Board tempchild : temp2) {
  470. childSet.add(tempchild);
  471. }
  472. } else {
  473. childSet.add(child);
  474. }
  475. } else {
  476. temp2 = child.getChildren(d2, col);
  477. if(!temp2.isEmpty()) {
  478. for(Board tempchild : temp2) {
  479. childSet.add(tempchild);
  480. }
  481. } else {
  482. childSet.add(child);
  483. }
  484. }
  485. } else if(child.getLastMove().getFrom()-d1 == 0) {
  486. temp2 = child.getChildren(d2, col);
  487. if(!temp2.isEmpty()) {
  488. for(Board tempchild : temp2) {
  489. childSet.add(tempchild);
  490. }
  491. } else {
  492. childSet.add(child);
  493. }
  494. } else if(child.getLastMove().getFrom()-d2 == 0) {
  495. temp2 = child.getChildren(d1, col);
  496. if(!temp2.isEmpty()) {
  497. for(Board tempchild : temp2) {
  498. childSet.add(tempchild);
  499. }
  500. } else {
  501. childSet.add(child);
  502. }
  503. }
  504. }
  505. }
  506. }
  507. } else {
  508. /* if the player has rolled doubles,
  509. * this fills a Set with all possible
  510. * child states.
  511. */
  512. ArrayList<Board> t1, t2, t3, t4;
  513. t1 = getChildren(d1, col);
  514. for(Board temp1 : t1) {
  515. if(temp1.isTerminal()) {
  516. childSet.add(temp1);
  517. break;
  518. }
  519. t2 = temp1.getChildren(d1, col);
  520. if(t2.isEmpty()) {
  521. childSet.add(temp1);
  522. continue;
  523. }
  524. for(Board temp2 : t2) {
  525. if(temp2.isTerminal()) {
  526. childSet.add(temp2);
  527. break;
  528. }
  529. t3 = temp2.getChildren(d1, col);
  530. if(t3.isEmpty()) {
  531. childSet.add(temp2);
  532. continue;
  533. }
  534. for(Board temp3 : t3) {
  535. if(temp3.isTerminal()) {
  536. childSet.add(temp3);
  537. break;
  538. }
  539. t4 = temp3.getChildren(d1, col);
  540. if(t4.isEmpty()) {
  541. childSet.add(temp3);
  542. continue;
  543. }
  544. for(Board child : t4) {
  545. childSet.add(child);
  546. }
  547. }
  548. }
  549. }
  550. }
  551. /* we iterate the childSet to add each child
  552. * to the ArrayList we want to return.
  553. */
  554. Iterator<Board> it = childSet.iterator();
  555. while(it.hasNext()) {
  556. children.add(it.next());
  557. }
  558. return children;
  559. }
  560. public ArrayList<Board> getChildren(byte dice, byte col) {
  561. /* gets called from the other getChildren to
  562. * return an Arraylist with all possible states
  563. * on whitch "dice" has been played.
  564. */
  565. ArrayList<Board> children = new ArrayList<Board>();
  566. Move[] moves = movegen(dice, col);
  567. for(int i=0; i<moves.length; i++) {
  568. if(moveIsLegal(moves[i].getFrom(), moves[i].getTo(), col, dice, dice)) {
  569. Board child = new Board(this);
  570. child.playMove(moves[i].getFrom(), moves[i].getTo(), col);
  571. children.add(child);
  572. }
  573. }
  574. return children;
  575. }
  576. public int evaluate() {
  577. /* the heuristic functions that
  578. * help evaluate a state.
  579. */
  580. int bsum = 0;
  581. int wsum = 0;
  582. for(int i=1; i<25; i++) {
  583. if(positions[i].getNum()>1 && positions[i].getCol()==B) {
  584. bsum += 4;
  585. } else if(positions[i].getNum()==1 && positions[i].getCol()==B) {
  586. bsum -= 3;
  587. }
  588. if(positions[i].getNum()>1 && positions[i].getCol()==W) {
  589. wsum += 4;
  590. } else if(positions[i].getNum()==1 && positions[i].getCol()==W) {
  591. wsum -= 3;
  592. }
  593. }
  594. if(positions[0].getNum()>0) {
  595. wsum -= positions[0].getNum()*4;
  596. bsum += positions[0].getNum()*4;
  597. }
  598. if(positions[26].getNum()>0) {
  599. wsum += positions[26].getNum()*5;
  600. }
  601. if(positions[25].getNum()>0) {
  602. bsum -= positions[25].getNum()*4;
  603. wsum += positions[25].getNum()*4;
  604. }
  605. if(positions[27].getNum()>0) {
  606. bsum += positions[27].getNum()*5;
  607. }
  608. return bsum - wsum;
  609. }
  610. public boolean isTerminal() {
  611. /* simple check to see if a state
  612. * is terminal. [26] contains the
  613. * checkers the white player has
  614. * taken out of the board, and [27]
  615. * the same for the black player.
  616. */
  617. if(positions[26].getNum()==15 || positions[27].getNum()==15) {
  618. return true;
  619. }
  620. return false;
  621. }
  622. /*------------------Utilities-&-Overrides------------------*/
  623. public void print() {
  624. System.out.println(" 12 11 10 9 8 7 6 5 4 3 2 1");
  625. for(int i=12; i>0; i--) {
  626. printHelp(i);
  627. }
  628. System.out.println("\n|\t\t\t\t\t\t|");
  629. for(int i=13; i<25; i++) {
  630. printHelp(i);
  631. }
  632. System.out.println("\n 13 14 15 16 17 18 19 20 21 22 23 24");
  633. System.out.println("\n");
  634. System.out.println(" 0 25");
  635. System.out.println("eaten pills : <w"+positions[0].getNum()+
  636. "> <b"+positions[25].getNum()+">");
  637. System.out.println("pills out : <w"+positions[26].getNum()+
  638. "> <b"+positions[27].getNum()+">");
  639. System.out.println(" 26 27");
  640. }
  641. private void printHelp(int i) {
  642. if(positions[i].getCol()==EMPTY) {
  643. System.out.print("< >");
  644. } else {
  645. if(positions[i].getCol()==W) {
  646. System.out.print("<w"+positions[i].getNum()+">");
  647. } else {
  648. System.out.print("<b"+positions[i].getNum()+">");
  649. }
  650. }
  651. }
  652. public boolean equals(Object o) {
  653. /* equals() override for the HashSet
  654. * to work.
  655. */
  656. if(o == null) {
  657. return false;
  658. }
  659. if(!(o instanceof Board)) {
  660. return false;
  661. }
  662. Board b = (Board)o;
  663. for(int i=0; i<28; i++) {
  664. if((this.positions[i].getCol() != b.positions[i].getCol()) || (this.positions[i].getNum() != b.positions[i].getNum())) {
  665. return false;
  666. }
  667. }
  668. return true;
  669. }
  670. public int hashCode() {
  671. /* also for the HashSet */
  672. int hc = 5381;
  673. int num ,col;
  674. for(int i=0; i<28; i++) {
  675. col = this.positions[i].getCol();
  676. num = this.positions[i].getNum();
  677. if(col == B)
  678. col = 3;
  679. else if(col == EMPTY)
  680. col = 1;
  681. else if(col == W)
  682. col = 5;
  683. hc += ((hc <<5)+ hc ) + num + i*col*3;
  684. }
  685. return hc;
  686. }
  687. }