题目本身不多赘述,从以下五个方面作简要介绍。
首先玩家可以选择难度模式,easy的牌堆是4×4,hard则是6×6。选择后显示初始牌堆,输入两个坐标,格式有提醒,数字之间用空格分隔。
输入后首先展示该两张牌,若不匹配则重新显示背面,同时记录操作步数。
若匹配则显示牌面内容。
全部匹配则游戏胜利。
针对hard模式提供提示功能,输入t则会随机将两张牌翻过来,降低难度。
还有更过分的作弊功能,输入cheat直接杀死比赛。
首先总共设计了四个类
Board类是牌堆,负责设置牌堆大小和内容、初始化牌堆、洗牌以及拓展的小功能,包括提示功能和作弊功能(在第四部分中介绍)。
Card类是单张牌,包含了牌的各种属性,坐标、内容、正反面以及匹配成功的标志位。之所以单独设置一个isDone,是为了防止再次选择已经匹配成功的牌时,会覆盖之前的匹配结果,导致又显示背面。
int x, y;//横纵坐标
int flag = 0;//0是背面,1是正面
String str = ""; //牌面内容
int isDone = 0; //匹配成功的标志
Judge类是判定类,包含了几个关键的规则判断方法。例如检测输入是否合规,每次翻牌后两张是否匹配,以及游戏是否胜利。
Test1就是游戏的主类,首先有两个不同大小的牌堆数组,用于不同难度的模式。初始化各类对象后,根据选择的难度去设置对象中的length属性,从而完成相应的初始化、洗牌、打印、判定等等。
if(level == 1){
length = 4;
board.setLength(length);
board.setContents(contents1);
judge.setLength(length);
}
if(level == 2){
length = 6;
board.setLength(length);
board.setContents(contents2);
judge.setLength(length);
}
接收输入的时候使用nextLine接收字符串,一次性输入两个坐标,数字之间用空格间隔,这样做的目的是为了进行功能拓展时,可以输入相应的字符串命令激活功能。而将字符串按格式处理成int类型也比较容易,然后判断是否符合输入规定,合规则先显示两张牌的内容,随后判定是否匹配,进行接下来的显示。
对部分功能的关键代码进行介绍,注解中有说明
先把取牌的顺序用随机序列得出,然后按照这个随机的次序对牌堆赋值。
//生成随机序列以用来洗牌
//集合是没有重复的值,LinkedHashSet是有顺序不重复集合
public static Set<Integer> generateRandomArray(int size) {
Set<Integer> set = new LinkedHashSet<Integer>();
Random ran = new Random();
while(set.size() < size) {
int t = ran.nextInt(size);
set.add(t);
}
return set;
}
//洗牌
public static void shuffleCards(Card[][] card) {
int t = 0;
int tt = 0;
//用一维数组保存生成的随机序列
Set<Integer> set = generateRandomArray(length*length);
int[] randomArray = new int[length*length];
for(Integer value : set) {
randomArray[tt] = value;
tt ++;
}
//利用生成的随机序列完成洗牌
for(int i = 0; i < length; i ++) {
for(int j = 0; j < length; j ++) {
card[i][j] = new Card(i, j, contents[randomArray[t]]);
t ++;
}
}
}
//每次翻牌判定
public static void eachSelect(int x1, int x2, int y1, int y2, Card[][] card) {
//如果两张牌牌面相同
if(card[x1][y1].str.equals(card[x2][y2].str)) {
//匹配成功
card[x1][y1].isDone = 1;
card[x2][y2].isDone = 1;
//显示正面
card[x1][y1].flag = 1;
card[x2][y2].flag = 1;
}
//牌面不同则仍保持原状
else {
//已经匹配成功的牌仍显示正面,另一张仍是背面
if(card[x1][y1].isDone == 1) {
card[x1][y1].flag = 1;
card[x2][y2].flag = 0;
}
else if(card[x2][y2].isDone == 1) {
card[x2][y2].flag = 1;
card[x1][y1].flag = 0;
}
else {
card[x1][y1].flag = 0;
card[x2][y2].flag = 0;
}
}
遍历牌堆,若没有$则翻牌成功。
//是否胜利
public static boolean isWin(Card[][] card) {
//遍历整个牌堆
for(int i = 0; i < length; i ++) {
for(int j = 0; j < length; j ++) {
if(card[i][j].cardContent().equals("$")) {
return false;
}
}
}
System.out.println("=============================");
//若没有$则成功
return true;
} ## 4、拓展优化
在牌堆变大时,游戏难度会大幅提升,因此拓展了两个小功能。
//hard模式的提示功能,随机翻两张牌
public static void hardTips(Card [][]card){
Random ran = new Random();
int i = ran.nextInt(length);
int j = ran.nextInt(length);
card[i][j].isDone = 1;
card[i][j].flag = 1;
int ii = ran.nextInt(length);
int jj = ran.nextInt(length);
card[ii][jj].isDone = 1;
card[ii][jj].flag = 1;
}
//作弊功能,所有翻面,直接杀死游戏
public static void cheat(Card[][]card,int length){
for(int i = 0; i < length; i ++) {
for(int j = 0; j < length; j ++) {
card[i][j].isDone=1;
card[i][j].flag=1;
}
}
}
最初所有逻辑都在一个类中进行编写,后期在修改优化的时候非常不方便,因此重构了代码(虽然本身也不复杂……),将对象抽象成类,包含了相应的属性状态以及操作方法,在主类中只需要创建对应的对象,按照要求去初始化,调用相应的方法操作即可。
最典型的就是牌堆尺寸这个属性,调用方法时不用每次都传参,只需要在对象中setLength,里面的方法就可以使用这个属性进行操作了。
如果想要进行功能的拓展,也比较方便。例如现在统计了步数,如果按照每次步数排名(类似按照时间排名),就可以把这个方法封装到Card类里调用即可。或者是进一步增加难度,设置更复杂的规则,就可以把规则方法封装到Judge类里,每次匹配的时候进行判定即可。
一言难尽,虽然写的稀烂,随便记一下吧。