序言
五子連珠,顧名思義就是當有五個相同的球在一條直線上,橫,豎,斜,總共四個方向呈《米》型的消除規則,達到5個或者5個以上
但是我們的五子連珠不是達到五個就結束,只是連續相同的5個或者5個以上的棋子會被消除同時增加對應的積分;
我們的設計是棋盤上 是 8 * 8 座標格子,
總共有6總顏色的球,五個獨立顏色(色號1-5),一個是彩色叫萬能替代球(色號6);
還有一個附加狀態是球有爆炸效果
棋子在棋盤上可以沿著四個方向移動,然後移動到需要移動的地方,所以這裡採用A*演算法來做格子移動;
如果移動棋子沒有可以消除的棋子,棋盤上會隨機刷出三個新的球;
設計
先設計基本棋盤程式碼,完成基礎儲存功能
1 import java.io.Serializable; 2 3 /** 4 * @author: Troy.Chen(失足程式設計師 , 15388152619) 5 * @create: 2020-11-23 22:37 6 **/ 7 public class Wzlz implements Serializable { 8 9 private static final long serialVersionUID = 1L; 10 11 /*用於A* 演算法的移動尋路導航,*/ 12 protected int[][] blockCells; 13 /*棋盤上每個格子狀態*/ 14 protected Qiu[][] qiuCells; 15 16 public Wzlz() { 17 blockCells = new int[8][8]; 18 qiuCells = new Qiu[8][8]; 19 for (int z = 0; z < blockCells.length; z++) { 20 for (int x = 0; x < blockCells[z].length; x++) { 21 blockCells[z][x] = 0; 22 qiuCells[z][x] = new Qiu(0, x, z); 23 } 24 } 25 } 26 27 public class Qiu { 28 /*1-5是五個顏色的球,6是彩色萬能球*/ 29 private int qiuId; 30 private int x; 31 private int z; 32 private int zhadan; 33 34 public Qiu() { 35 } 36 37 public Qiu(int qiuId, int x, int z) { 38 this.qiuId = qiuId; 39 this.x = x; 40 this.z = z; 41 } 42 43 public int getQiuId() { 44 return qiuId; 45 } 46 47 public void setQiuId(int qiuId) { 48 this.qiuId = qiuId; 49 } 50 51 public int getX() { 52 return x; 53 } 54 55 public void setX(int x) { 56 this.x = x; 57 } 58 59 public int getZ() { 60 return z; 61 } 62 63 public void setZ(int z) { 64 this.z = z; 65 } 66 67 public int getZhadan() { 68 return zhadan; 69 } 70 71 public void setZhadan(int zhadan) { 72 this.zhadan = zhadan; 73 } 74 } 75 76 public void print() { 77 StringBuilder stringBuilder = new StringBuilder(); 78 stringBuilder.append("=============================================================="); 79 stringBuilder.append("\n"); 80 for (int z = 0; z < qiuCells.length; z++) { 81 for (int x = 0; x < qiuCells[z].length; x++) { 82 stringBuilder.append(qiuCells[z][x].getQiuId() + "(" + qiuCells[z][x].getZhadan() + ")" + ", "); 83 } 84 stringBuilder.append("\n"); 85 } 86 stringBuilder.append("=============================================================="); 87 System.out.println(stringBuilder.toString()); 88 } 89 }
初始化一下類,然後看一下棋盤列印
1 import java.io.Serializable; 2 3 /** 4 * @author: Troy.Chen(失足程式設計師 , 15388152619) 5 * @create: 2020-11-23 22:59 6 **/ 7 public class WzlzMain implements Serializable { 8 9 private static final long serialVersionUID = 1L; 10 11 public static void main(String[] args) { 12 Wzlz wzlzTest = new Wzlz(); 13 wzlzTest.print(); 14 } 15 16 17 public static void test(int[][] cells, Wzlz.Qiu[][] qius, int x, int z, int qiu) { 18 /*設定格子阻擋資訊*/ 19 cells[z][x] = 1; 20 /*設定球的id*/ 21 qius[z][x].setQiuId(qiu); 22 } 23 }
棋盤初始化完成;
如何消除棋子;
如何只是橫向或者縱向,我們想很多人都知道。
我先看一段簡單程式碼
1 public static void xiaochu(int[][] cells, Wzlz.Qiu[][] qius) { 2 Set<Wzlz.Qiu> qiuSet = new HashSet<>(); 3 /*消除橫線演算法*/ 4 for (int z = 0; z < qius.length; z++) { 5 int qiuid = 0; 6 List<Wzlz.Qiu> qiuList = new ArrayList<>(); 7 for (int x = 0; x < qius[z].length; x++) { 8 final Wzlz.Qiu qiu = qius[z][x]; 9 if (qiu.getQiuId() == 0 || (qiuid != 0 && qiu.getQiuId() != 6/*不是萬能球*/ && qiu.getQiuId() != qiuid)) { 10 if (qiuList.size() >= 5) { 11 qiuSet.addAll(qiuList); 12 } 13 qiuid = 0; 14 qiuList.clear(); 15 } 16 17 if (qiu.getQiuId() != 0) { 18 if (qiu.getQiuId() != 6) { 19 /*記錄除萬能球在內的;連續球的數量*/ 20 qiuid = qiu.getQiuId(); 21 } 22 qiuList.add(qiu); 23 } 24 } 25 if (qiuList.size() >= 5) { 26 qiuSet.addAll(qiuList); 27 } 28 } 29 for (Wzlz.Qiu qiu : qiuSet) { 30 qiu.setQiuId(0); 31 /*清理阻擋狀態*/ 32 cells[qiu.getZ()][qiu.getX()] = 0; 33 } 34 }
我們先來看一段消除橫線連珠的程式碼
編寫一下測試程式碼,這段程式碼摺疊一下
1 public static void main(String[] args) { 2 Wzlz wzlzTest = new Wzlz(); 3 test(wzlzTest.blockCells, wzlzTest.qiuCells, 0, 0, 3); 4 test(wzlzTest.blockCells, wzlzTest.qiuCells, 1, 0, 6); 5 test(wzlzTest.blockCells, wzlzTest.qiuCells, 2, 0, 6); 6 test(wzlzTest.blockCells, wzlzTest.qiuCells, 3, 0, 3); 7 test(wzlzTest.blockCells, wzlzTest.qiuCells, 4, 0, 3); 8 test(wzlzTest.blockCells, wzlzTest.qiuCells, 5, 0, 4); 9 test(wzlzTest.blockCells, wzlzTest.qiuCells, 6, 0, 4); 10 wzlzTest.print(); 11 xiaochu(wzlzTest.blockCells, wzlzTest.qiuCells); 12 wzlzTest.print(); 13 }
檢視一下結果
消除成功;
看到這裡,可能還沒有發現一個問題
我們修改一下測試程式碼,我剛才測試程式碼的第一個球的顏色改為1,把第6個球的顏色改3,也就是2-6是相同的5個顏色
再來看執行效果
可以看出來,棋子並沒有被消除;
原因是這段程式碼是單鏈迴圈,
單身我們理想的效果是不是應該是這樣?第一個,開始往後找,如果不對,從第二個開始往後找;
所以我們修改遞迴迴圈程式碼
1 public static void xiaochu(int[][] cells, Wzlz.Qiu[][] qius) { 2 Set<Wzlz.Qiu> qiuSet = new HashSet<>(); 3 /*消除橫線演算法*/ 4 for (int z = 0; z < qius.length; z++) { 5 for (int x = 0; x < qius[z].length; x++) { 6 int qiuid = 0; 7 List<Wzlz.Qiu> qiuList = new ArrayList<>(); 8 for (int x1 = x; x1 < qius[z].length; x1++) { 9 final Wzlz.Qiu qiu = qius[z][x1]; 10 if (qiu.getQiuId() == 0 || (qiuid != 0 && qiu.getQiuId() != 6/*不是萬能球*/ && qiu.getQiuId() != qiuid)) { 11 if (qiuList.size() >= 5) { 12 qiuSet.addAll(qiuList); 13 } 14 qiuid = 0; 15 qiuList.clear(); 16 } 17 18 if (qiu.getQiuId() != 0) { 19 if (qiu.getQiuId() != 6) { 20 /*記錄除萬能球在內的;連續球的數量*/ 21 qiuid = qiu.getQiuId(); 22 } 23 qiuList.add(qiu); 24 } 25 } 26 27 if (qiuList.size() >= 5) { 28 qiuSet.addAll(qiuList); 29 } 30 } 31 } 32 for (Wzlz.Qiu qiu : qiuSet) { 33 qiu.setQiuId(0); 34 /*清理阻擋狀態*/ 35 cells[qiu.getZ()][qiu.getX()] = 0; 36 } 37 }
注意for裡面的x和for裡面的x1;這兩個迴圈
來看看效果
第2-6個球成功消除;
橫線消除,豎線消除其實就是一個道理,只是換一下迴圈方向就能實現;
暫時就不再贅述;
那麼問題和難點在哪裡?
其實因為消除規則是《米》字型的,也就是斜線消除;
其實根據剛才寫法,找到消除規則和演算法並不難;
只需要把x和z座標都加一取下一個格子就能判斷,就不在贅述;
1 import com.ty.tools.dlog.DLogger; 2 3 import java.io.Serializable; 4 5 /** 6 * @author: Troy.Chen(失足程式設計師 , 15388152619) 7 * @create: 2020-11-18 10:37 8 **/ 9 public class WzlzTest implements Serializable { 10 11 private static final long serialVersionUID = 1L; 12 private static final DLogger log = DLogger.getLogger(); 13 14 public static void main(String[] args) { 15 int mx = 7; 16 int x = 0; 17 int z = 0; 18 /* todo 正方形的左上方的三角形 ↙↙↙↙ */ 19 while (true) { 20 int z1 = z; 21 int x1 = x; 22 while (true) { 23 System.out.print("[" + x1 + "," + z1 + "]"); 24 x1--; 25 z1++; 26 if (x1 < 0 || z1 > mx) { 27 break; 28 } 29 } 30 System.out.println(); 31 if (x < 7) { 32 x++; 33 } else if (z <= mx) { 34 z++; 35 } 36 37 if (x >= mx && z > mx) { 38 break; 39 } 40 } 41 System.out.println("================================================================="); 42 x = 7; 43 z = 0; 44 /* todo 從左邊一直往右邊斜 ↘↘↘↘ */ 45 while (true) { 46 int z1 = z; 47 int x1 = x; 48 while (true) { 49 System.out.print("[" + x1 + "," + z1 + "]"); 50 x1++; 51 z1++; 52 if (x1 > mx || z1 > mx) { 53 break; 54 } 55 } 56 System.out.println(); 57 if (x > 0) { 58 x--; 59 } else if (z <= mx) { 60 z++; 61 } 62 if (z > mx && x <= 0) { 63 break; 64 } 65 } 66 System.exit(0); 67 } 68 69 }
這是尋找格子的基本演算法,願意的可以開啟看看;
如果說這不是重點那麼什麼才是重點?
其實上面的程式碼不知道你們發現了沒有;其實是再一次移動棋子後去呼叫執行查詢的,每一次都要重新組裝棋子。
其實我們是不是可以一次性把棋子組裝完成?
1 import java.io.Serializable; 2 import java.util.ArrayList; 3 import java.util.Iterator; 4 import java.util.LinkedList; 5 import java.util.List; 6 7 /** 8 * @author: Troy.Chen(失足程式設計師 , 15388152619) 9 * @create: 2020-11-23 22:37 10 **/ 11 public class Wzlz implements Serializable { 12 13 private static final long serialVersionUID = 1L; 14 15 /*用於A* 演算法的移動尋路導航,*/ 16 protected int[][] blockCells; 17 /*棋盤上每個格子狀態*/ 18 protected Qiu[][] qiuCells; 19 /*縱橫交錯的排列*/ 20 protected List<List<Wzlz.Qiu>> qiuLines = new LinkedList<>(); 21 22 23 public Wzlz() { 24 blockCells = new int[8][8]; 25 qiuCells = new Qiu[8][8]; 26 for (int z = 0; z < blockCells.length; z++) { 27 for (int x = 0; x < blockCells[z].length; x++) { 28 blockCells[z][x] = 0; 29 qiuCells[z][x] = new Qiu(0, x, z); 30 } 31 } 32 resetHangline(); 33 } 34 35 /** 36 * 四個方向的組裝 37 */ 38 public void resetHangline() { 39 if (qiuCells == null || qiuCells.length < 1) { 40 return; 41 } 42 qiuLines = new ArrayList<>(); 43 /* todo 組裝 橫著的行資料 → */ 44 for (int z = 0; z < qiuCells.length; z++) { 45 List<Wzlz.Qiu> line = new LinkedList<>(); 46 for (int x = 0; x < qiuCells[z].length; x++) { 47 line.add(qiuCells[z][x]); 48 } 49 qiuLines.add(line); 50 } 51 52 /* todo 組裝 豎著的行資料 → */ 53 for (int x = 0; x < qiuCells[0].length; x++) { 54 List<Wzlz.Qiu> line = new LinkedList<>(); 55 for (int z = 0; z < qiuCells.length; z++) { 56 line.add(qiuCells[z][x]); 57 } 58 qiuLines.add(line); 59 } 60 61 int mx = qiuCells.length - 1; 62 int x = 0; 63 int z = 0; 64 /* todo 組裝 正方形的左上方的三角形 ↙↙↙↙ */ 65 while (true) { 66 int z1 = z; 67 int x1 = x; 68 List<Wzlz.Qiu> line = new LinkedList<>(); 69 while (true) { 70 line.add(qiuCells[z1][x1]); 71 // System.out.print("[" + x1 + "," + z1 + "]"); 72 x1--; 73 z1++; 74 if (x1 < 0 || z1 > mx) { 75 break; 76 } 77 } 78 qiuLines.add(line); 79 // System.out.println(); 80 if (x < 7) { 81 x++; 82 } else if (z <= mx) { 83 z++; 84 } 85 if (x >= mx && z > mx) { 86 break; 87 } 88 } 89 90 x = qiuCells.length - 1; 91 z = 0; 92 /* todo 從左邊一直往右邊斜 ↘↘↘↘ */ 93 while (true) { 94 int z1 = z; 95 int x1 = x; 96 List<Wzlz.Qiu> line = new LinkedList<>(); 97 while (true) { 98 // System.out.print("[" + x1 + "," + z1 + "]"); 99 line.add(qiuCells[z1][x1]); 100 x1++; 101 z1++; 102 if (x1 > mx || z1 > mx) { 103 break; 104 } 105 } 106 qiuLines.add(line); 107 // System.out.println(); 108 if (x > 0) { 109 x--; 110 } else if (z <= mx) { 111 z++; 112 } 113 if (z > mx && x <= 0) { 114 break; 115 } 116 } 117 for (Iterator<List<Wzlz.Qiu>> integerIterator = qiuLines.iterator(); integerIterator.hasNext(); ) { 118 List<Wzlz.Qiu> next = integerIterator.next(); 119 if (next.size() < 5) { 120 integerIterator.remove(); 121 } 122 } 123 } 124 125 public class Qiu { 126 /*1-5是五個顏色的球,6是彩色萬能球*/ 127 private int qiuId; 128 private int x; 129 private int z; 130 private int zhadan; 131 132 public Qiu() { 133 } 134 135 public Qiu(int qiuId, int x, int z) { 136 this.qiuId = qiuId; 137 this.x = x; 138 this.z = z; 139 } 140 141 public int getQiuId() { 142 return qiuId; 143 } 144 145 public void setQiuId(int qiuId) { 146 this.qiuId = qiuId; 147 } 148 149 public int getX() { 150 return x; 151 } 152 153 public void setX(int x) { 154 this.x = x; 155 } 156 157 public int getZ() { 158 return z; 159 } 160 161 public void setZ(int z) { 162 this.z = z; 163 } 164 165 public int getZhadan() { 166 return zhadan; 167 } 168 169 public void setZhadan(int zhadan) { 170 this.zhadan = zhadan; 171 } 172 } 173 174 public void print() { 175 StringBuilder stringBuilder = new StringBuilder(); 176 stringBuilder.append("=============================================================="); 177 stringBuilder.append("\n"); 178 for (int z = 0; z < qiuCells.length; z++) { 179 for (int x = 0; x < qiuCells[z].length; x++) { 180 stringBuilder.append(qiuCells[z][x].getQiuId() + "(" + qiuCells[z][x].getZhadan() + ")" + ", "); 181 } 182 stringBuilder.append("\n"); 183 } 184 stringBuilder.append("=============================================================="); 185 System.out.println(stringBuilder.toString()); 186 } 187 }
於是有了以上程式碼,第一次初始化就把橫線,縱向,斜線的連線全部組裝完成
1 import java.io.Serializable; 2 import java.util.ArrayList; 3 import java.util.HashSet; 4 import java.util.List; 5 import java.util.Set; 6 7 /** 8 * @author: Troy.Chen(失足程式設計師 , 15388152619) 9 * @create: 2020-11-23 22:59 10 **/ 11 public class WzlzMain implements Serializable { 12 13 private static final long serialVersionUID = 1L; 14 15 public static void main(String[] args) { 16 Wzlz wzlzTest = new Wzlz(); 17 test(wzlzTest.blockCells, wzlzTest.qiuCells, 0, 0, 1); 18 test(wzlzTest.blockCells, wzlzTest.qiuCells, 1, 1, 6); 19 test(wzlzTest.blockCells, wzlzTest.qiuCells, 2, 2, 6); 20 test(wzlzTest.blockCells, wzlzTest.qiuCells, 3, 3, 3); 21 test(wzlzTest.blockCells, wzlzTest.qiuCells, 4, 4, 3); 22 test(wzlzTest.blockCells, wzlzTest.qiuCells, 5, 5, 3); 23 test(wzlzTest.blockCells, wzlzTest.qiuCells, 6, 6, 4); 24 wzlzTest.print(); 25 xiaochu(wzlzTest.blockCells, wzlzTest.qiuLines); 26 wzlzTest.print(); 27 } 28 29 30 public static void test(int[][] cells, Wzlz.Qiu[][] qius, int x, int z, int qiu) { 31 /*設定格子阻擋資訊*/ 32 cells[z][x] = 1; 33 /*設定球的id*/ 34 qius[z][x].setQiuId(qiu); 35 } 36 37 public static void xiaochu(int[][] cells, List<List<Wzlz.Qiu>> qiuLines) { 38 Set<Wzlz.Qiu> xiaochu = new HashSet<>(); 39 for (List<Wzlz.Qiu> line : qiuLines) { 40 for (int i = 0; i < line.size(); i++) { 41 int qid = 0; 42 List<Wzlz.Qiu> list = new ArrayList<>(); 43 for (int k = i; k < line.size(); k++) { 44 Wzlz.Qiu qiu = line.get(k); 45 if (qiu.getQiuId() == 0 || (qid != 0 && qid != qiu.getQiuId() && qiu.getQiuId() != 6)) { 46 if (list.size() >= 5) { 47 /*todo 前面都是連續相同的*/ 48 xiaochu.addAll(list); 49 } 50 /*到這裡就表示沒有相同的棋子需要清理*/ 51 qid = 0; 52 list.clear(); 53 } 54 if (qiu.getQiuId() > 0) { 55 if (qiu.getQiuId() != 6) { 56 qid = qiu.getQiuId(); 57 } 58 list.add(qiu); 59 } 60 } 61 if (list.size() >= 5) { 62 /*先查詢最後消除*/ 63 xiaochu.addAll(list); 64 } 65 } 66 } 67 for (Wzlz.Qiu qiu : xiaochu) { 68 qiu.setQiuId(0); 69 /*球阻擋清空*/ 70 cells[qiu.getZ()][qiu.getX()] = 0; 71 } 72 } 73 74 }
調整消除程式碼,這樣每次獲取連線程式碼都變得簡單清晰
總結:
其實網上演算法很多;但是基本都是一次性的,也就是說他們是正中的五子棋,因為只要有五個連珠的棋子就結束了需要重新來;
但是我們這個變種的。有5個相同的只會消除,然後繼續玩;
所以演算法稍稍不同,但是基本也是大同小異;