利用Random类的对象的链表-随机的顺序存储一副52张的纸牌。用含有两个字符的字符串表明纸牌,例如“1C”表示梅花A,”JD”表示方片J等。从栈中输出4手牌,每手牌有13张纸牌。java
完成一个变形版的纸牌21点游戏。该游戏来源于21点游戏,实现人机对战。算法
游戏说明以下:数组
(1)该游戏须要两副牌,没有Joker,共104张。每张“纸牌”应具备花色与数字两个属性。app
(2)游戏在机器与人类玩家之间进行。游戏一开始应先洗牌(将104张牌打乱)。dom
(3)机器永远是庄家,因此永远先给机器发牌,机器的牌不可见,只能看到机器要了几张牌。机器中止要牌后,再给人类玩家发牌。ide
(4)游戏胜利与失败的条件与普通21相同;除此之外,一方在当前牌没有爆掉的前提下,若是下一张牌使得手中有两张彻底同样的牌(同数字、同花色)则马上胜利。函数
(5)游戏结束时机器的牌要所有显示,并提示谁胜利了。学习
程序设计要求以下:测试
(1)程序中应至少有Card类和CardGame类。优化
(2)Card类须要重写Object类的equals(Object o)函数,用于比较两张牌是否彻底同样;重写toString函数,用于输出牌时直接显示牌的花色与数字。
(3)CardGame类应具备shuffle(洗牌)、deal(发牌)、win(胜利判别)等函数。
(4)选择适当的java集合类来实现“发牌牌堆”和“手牌”(不容许都使用数组)。
· 学习点之一:用enum枚举
enum通常用来枚举一组相同类型的常量。如性别、日期、月份、颜色等。对这些属性用常量的好处是显而易见的,不只能够保证单例,且要做比较的时候能够用”== ”来替换”equals”,是一种好的习惯(如本例中第45行)。
用法:如:
性别:
[java] view plain copy
本例中的花色:
[java] view plain copy
须要注意的是,枚举对象里面的值都必须是惟一的,特别的,咱们能够还经过enum类型名直接引用该常量,好比本例中的121,124,127等行经过类型名直接引用。
除此以外,咱们还能够往enum中添加新方法,这里就不加介绍了。
· 学习点之二:重写equals函数进行比较
equals(Object o)函数本来是Object类下面的函数,我查了一下JAVA的API,截图以下:
简而言之,若是JAVA中默认的equals方法跟实际不符的话,就须要重写equals方法。咱们这里要对牌是否相同做比较,所以须要重写该方法(第40~50行):
[java] view plain copy
另外咱们注意到API里面写着:
“注意:当此方法被重写时,一般有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具备相等的哈希码。”这也就是咱们下面要学习的第三点。
· 学习点之三:重写hashcode()
问:也许上面的话有些晦涩难懂啊,咱们为何要重写hashcode()方法呢?
答: object对象中的 public boolean equals(Object obj),对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true;
注意:当此方法被重写时,一般有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具备相等的哈希码。以下:
(1)当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true
(2)当obj1.hashCode() ==obj2.hashCode()为false时,obj1.equals(obj2)必须为false
若是不重写equals,那么比较的将是对象的引用是否指向同一块内存地址,重写以后目的是为了比较两个对象的value值是否相等。特别指出利用equals比较八大包装对象(如int,float等)和String类(由于该类已重写了equals和hashcode方法)对象时,默认比较的是值,在比较其它自定义对象时都是比较的引用地址。
hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。
总而言之,这样若是咱们对一个对象重写了equals,意 思是只要对象的成员变量值都相等那么equals就等于true,但不重写hashcode,那么咱们再new一个新的对象,当原对象.equals(新 对象)等于true时,二者的hashcode倒是不同的,由此将产生了理解的不一致,如在存储散列集合时(如Set类),将会存储了两个值同样的对 象,致使混淆,所以,就也须要重写hashcode()。
更通俗的说:
Object中的hashcode()方法在咱们建立对象的时候为每一个对象计算一个散列码,这个散列码是惟一的,因此若是2个对象的散列码相同,那他们必定是同一个对象。
本身定义的类也能够重写hashCode()方法,按照本身定义的算法计算散列码的生成。
Object中的equals()方法,比较的是2个对象的引用地址,包括他们各自的散列码,若是不一样,就认为是不一样的对象。
String类中重写了equals方法,比较的是他们字符串的内容。
在咱们这里本身定义的算法是(第53~58行):
[java] view plain copy
· 学习点之四:重写toString()函数
由于它是Object里面已经有了的方法,而全部类都是继承Object,因此“全部对象都有这个方法”。
它一般只是为了方便输出,好比System.out.println(xx),括号里面的“xx”若是不是String类型的话,就自动调用xx的toString()方法。然而对于默认的toString()方法每每不能知足需求,须要重写覆盖这个方法。
比较易懂,就不具体说明了,见代码60~86行。
·学习点之五:动态数组ArrayList
动态数组,便可以将 ArrayList想象成一种“会自动扩增容量的Array”。它的优势是能够动态地插入和删除元素,但牺牲效率。
有关使用ArrayList的例子,参考ArrayList用法,里面介绍的很详细。在咱们这题中,因为电脑和玩家的手牌都会由于抽牌而增长,所以将两者均设为ArrarList(第114,115行),方便动态插入。
· 学习点之六:加强for循环
见代码138~140行,不少人没看懂for(Card element:cards) 这段,其实这是JDK5.0的新特性,做用是遍历数组元素。其语法以下:
· 学习点之七:善于使用随机数
直接调用Math.random()能够产生一个[0,1)之间的随机数,注意区间是前闭后开的。本题当中由于共有104张牌,那么咱们直接用(int)Math.random()*104便可以产生[0,104)之间的随机数,对应一下数组的下标前闭后开恰好知足。
· 学习点之八:利用Scanner进行输入
咱们都知 道,JAVA里输入输出函数十分的麻烦,尤为是输入。可喜的是,从SDK1.5开始,新增了Scanner类,简化了输入函数。如本题的221 行:Scanner in=new Scanner(System.in) 首先建立了一个in对象,而后in对象能够调用下列方法,读取用户在命令行输入的各类数据类型: nextDouble(), nextFloat, nextInt(),nextLine(),nextLong()等,上述方法执行时都会形成堵塞,等待用户在命令行输入数据回车确认,例如本题中的第 279行,在系统问询是否继续要牌(y/n)后,用in.nextLine()来接受玩家输入的值。
说了这么多,咱们来运行下程序:
e….我什么都没干 输了- -;
再来一次。。。
平局。。。行,就这样了,不要欺负电脑了。
咱们再回到题目和代码:
· 完成一个变形版的纸牌21点游戏。该游戏来源于21点游戏,实现人机对战。
游戏说明以下:
(1)该游戏须要两副牌,没有Joker,共104张。每张“纸牌”应具备花色与数字两个属性。--直接对应代码24~37,60~102,117~134行
(2)游戏在机器与人类玩家之间进行。游戏一开始应先洗牌(将104张牌打乱)。--直接对应代码144~166行
(3)机器永远是庄家,因此永远先给机器发牌,机器的牌不可见,只能看到机器要了几张牌。机器中止要牌后,再给人类玩家发牌。--直接对应代码224~280行
(4)游戏胜利与失败的条件与普通21相同;除此之外,一方在当前牌没有爆掉的前提下,若是下一张牌使得手中有两张彻底同样的牌(同数字、同花色)则马上胜利。--直接对应代码181~205,208~217,311~323行
(5)游戏结束时机器的牌要所有显示,并提示谁胜利了。--直接对应代码172~179,283~297行
程序设计要求以下:
(1)程序中应至少有Card类和CardGame类。
(2)Card类须要重写Object类的equals(Object o)函数,用于比较两张牌是否彻底同样;重写toString函数,用于输出牌时直接显示牌的花色与数字。
(3)CardGame类应具备shuffle(洗牌)、deal(发牌)、win(胜利判别)等函数。
(4)选择适当的java集合类来实现“发牌牌堆”和“手牌”(不容许都使用数组)。
21点游戏是一个古老的扑克游戏,游戏的规则是:各个参与者设法使本身的牌达到总分21而不超过这个数值。扑克牌的分值取它们的面值,A充当1或者11分,J,Q和K人头牌都是10分。庄家VS1~7个玩家。在开局时,包括庄家在内的全部参与者都有两张牌。玩家能够看到他们的全部牌以及总分,而庄家有一张牌暂时是隐藏的。接下来,只要愿意,各个玩家都有机会依次再拿一张牌。若是是玩家的总分超过了21(称为引爆),那么这个玩家就输了。在全部玩家都拿了额外的牌后,庄家将显示隐藏的牌。只要庄家的总分等于或小于16,那么他就必须再拿牌。若是庄家引爆了,那么尚未引爆的全部玩家都将获胜,引爆的玩家打成平局。不然,将余下的各玩家的总分与庄家的总分作比较,若是玩家的总分大于庄家的总分,则玩家获胜。若是两者的总分相同,则玩家与庄家打成平局中。
//Card.Java
package card; public class Card{ private String color; private String number; public Card(String color, String number) { this.color = color; this.number = number; } public String getColor() { return color; } public String getNumber() { return number; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof Card)) return false; Card other = (Card) obj; if (color == null) { if (other.color != null) return false; } else if (!color.equals(other.color)) return false; if (number == null) { if (other.number != null) return false; } else if (!number.equals(other.number)) return false; return true; } }
//Cards.java
package card; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Cards { private List<Card> list = new ArrayList<Card>(); //建立一副扑克牌 public Cards(){ System.out.println("-----------------建立扑克牌------------------"); String[] color = {"黑桃", "红桃", "梅花", "方片"}; String[] number = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J","Q","K", "A"}; for(int i=0;i<color.length;i++) for(int j=0;j<number.length;j++){ list.add(new Card(color[i], number[j])); } System.out.println("----------------扑克牌建立成功!---------------"); } //获取一副扑克牌 public List<Card> getList() { return list; } //洗牌(打乱) public void shufCards(){ System.out.println("----------------开始洗牌------------------------"); Collections.shuffle(list); System.out.println("----------------洗牌结束------------------------"); } //展现一副扑克牌 public void showCards(){ System.out.print("当前的扑克牌为:"); System.out.print("[ "); for(int i=0;i<list.size();i++){ System.out.print(list.get(i).getColor() + list.get(i).getNumber()+ " "); } System.out.println(" ]"); } }
//Player.java
package card; import java.util.ArrayList; import java.util.List; public class Player { private int id; private String name; private List<Card> handCards = new ArrayList<Card>(); public Player(int id, String name){ this.id = id; this.name = name; } public List<Card> getHandCards() { return handCards; } public void setHandCards(Card card) { handCards.add(card); } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
//CardComparator.java(自定义排序规则)
package card; import java.util.Comparator; public class CardComparator implements Comparator<Card> { @Override public int compare(Card c1, Card c2) { // 构建花色和牌值数组,经过比对,计算获得某张牌的价值(大小) String[] color = {"方片", "梅花", "红桃", "黑桃"}; String[] number = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J","Q","K", "A"}; //因为比较规则是先比较牌值,若是相等再比较花色(黑红梅方),因此将牌值赋予较高的权值 int valueOfC1 = 0; int valueOfC2 = 0; for(int i=0;i<number.length;i++){ if(c1.getNumber().equals(number[i])) valueOfC1 += i*10; if(c2.getNumber().equals(number[i])) valueOfC2 += i*10; } for(int i=0;i<color.length;i++){ if(c1.getColor().equals(color[i])) valueOfC1 += i; if(c2.getColor().equals(color[i])) valueOfC2 += i; } if( valueOfC1 > valueOfC2 ) return -1; if( valueOfC1 < valueOfC2 ) return 1; return 0; } }
//PlayDemo.java
package card; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Scanner; public class PlayDemo { //建立玩家 //要对玩家ID的异常处理,要求用户只能输入整数ID,不然须要从新输入 public Player setPlayer(){ int id=0; String name=""; Scanner console = new Scanner(System.in); boolean ready = true; do{ try{ System.out.println("输入ID:"); id = console.nextInt(); ready = true; }catch(Exception e){ System.out.println("请输入整数类型的ID!"); ready = false; console.nextLine(); } }while(ready==false); System.out.println("输入姓名:"); name = console.next(); return new Player(id, name); } public static void main(String[] args) { //测试简易扑克牌程序 PlayDemo game = new PlayDemo(); //(1)建立一副牌 Cards cards = new Cards(); //(2)展现新的扑克牌 cards.showCards(); //(3)洗牌 cards.shufCards(); //(4)建立玩家 System.out.println("--------------建立两个(or多个)玩家就能够开始游戏啦!-------------"); List<Player> p = new ArrayList<Player>(); for(int i=0;i<2;i++) { System.out.println("请输入第"+(i+1)+"位玩家的ID和姓名:"); p.add(game.setPlayer()); } for(int i=0;i<p.size();i++) { System.out.println("欢迎玩家:"+p.get(i).getName()); } //(5)扑克牌比大小游戏开始啦~ int count = 0; System.out.println("------------------开始发牌---------------------"); //设定每人分别拿两张(or多张) for(int i=0; i<2;i++){ //玩家轮流拿牌 for(int j=0; j< p.size(); j++){ System.out.println(">玩家"+p.get(j).getName()+"拿牌"); p.get(j).setHandCards(cards.getList().get(count)); count++; } } System.out.println("------------------发牌结束!--------------------"); System.out.println("------------------开始游戏 ---------------------"); for(int i=0;i<p.size();i++){ System.out.print("玩家"+p.get(i).getName()+"的手牌为:[ "); for(int j=0;j<p.get(i).getHandCards().size();j++){ Card cur = p.get(i).getHandCards().get(j); System.out.print(cur.getColor()+cur.getNumber()+" "); } System.out.println(" ]"); } //排序获得每一个玩家最大的手牌(排序规则自定义) for(int i=0;i<p.size();i++){ Collections.sort(p.get(i).getHandCards(), new CardComparator()); } List<Card> maxCard = new ArrayList<Card>(); for(int i=0;i<p.size();i++){ Card maxCur = p.get(i).getHandCards().get(0); System.out.println("玩家"+p.get(i).getName()+"最大的手牌为:"+ maxCur.getColor()+maxCur.getNumber()); maxCard.add(maxCur); } //获得最后的胜者 List<Card> temp = new ArrayList<Card>(); temp.addAll(maxCard); Collections.sort(temp, new CardComparator()); for(int i=0;i<p.size();i++){ if(maxCard.get(i).equals(temp.get(0))) System.out.println("恭喜玩家:"+p.get(i).getName()+"获胜!"); } } }
还有一个地方须要优化,即不能输入重复的ID和姓名,能够重写Player的equals方法,将用户的输入与已有的输入相比较,不知足要求则须要从新输入,