在实现扑克牌斗牛项目前,我们先了解什么是斗牛?我们如何用Java实现呢??
1. 斗牛规则
斗牛规则:在纸牌斗牛规则里:大小王除外,52张牌,K最大,A最小,J,Q,K都是10点,然后点数依次排列最后A是1点。牌局开始每个人抓五张牌,玩家需要将手中三张牌10点的倍数,称为“牛”。其余的两张牌加起来算点数,去掉十位只留个位数来进行比较,如果剩下两张正好是10点,根据纸牌斗牛规则,这副牌就是“斗牛”。
2. Card类
由于我们Java语言是面向对象的语言,我们首先要做的就是将扑克牌抽象出一种类,让计算机可以读懂。那么我们该如何描述一个卡牌类呢??
我们发现扑克牌有点数和花色之分,我们就从这个方向来定义:
public String suit;//花色
public int rank;//点数
public String rankFace;//花牌
其中比较特别的有J、Q、K、A、鬼牌,那么我们应该特殊照顾
普通牌的初始化(构造方法)如下:
//初始化2~10的牌
public Card(String suit, int rank) {
this.suit = suit;
this.rank = rank;
}
花牌(J、Q、K、A)鬼牌(大小王)的初始化(构造方法)如下:
/**
* 初始化花牌 J、Q、K、A、大小王
* @param suit
* @param rankFace
*/
public Card(String suit,String rankFace,int rank) {
this.suit = suit;
this.face = rankFace;
this.rank=rank;
}
其次我们还需要对各种花色和特殊的花牌进行定义:
public static String[] SUITS={"♥","♠","♦","♣"};
public static String[] RANKFACES={"J","Q","K","A"};
我们为了更好的测验我们的牌,重写了一个打印方法如下:
@Override
public String toString() {
if(this.suit=="BigKing"||this.suit=="SmallKing"){
return "{"+this.suit+"}";
}
if(this.rankFace=="J"||this.rankFace=="Q"||this.rankFace=="K"||this.rankFace=="A"){
return "{"+this.suit+rankFace+"}";
}
return "{"+this.suit+rank+"}";
}
完整的Card类如下:
package democard;
public class Card {
public String suit;//花色
public int rank;//点数
public String face;//花牌
public static String[] SUITS={"♥","♠","♦","♣"};
public static String[] RANKFACES={"J","Q","K","A"};
/**
* 初始化2~10的牌
* @param suit
* @param rank
*/
public Card(String suit, int rank) {
this.suit = suit;
this.rank = rank;
}
/**
* 初始化花牌 J、Q、K、A、大小王
* @param suit
* @param rankFace
*/
public Card(String suit,String rankFace,int rank) {
this.suit = suit;
this.face = rankFace;
this.rank=rank;
}
@Override
public String toString() {
if(this.suit=="BigKing"||this.suit=="SmallKing"){
return "{"+this.suit+"}";
}
if(this.face=="J"||this.face=="Q"||this.face=="K"||this.face=="A"){
return "{"+this.suit+face+"}";
}
return "{"+this.suit+rank+"}";
}
}
在完成了对扑克牌的抽象定义后,我们需要对其中的玩法进行实现,我们创建一个新的类Game
3. Game类
3.1 创建一个扑克牌
/**
* 初始化牌
* @return
*/
public List<Card> createCard(){
List<Card> cardList=new ArrayList<>();
//2~10的牌
for (int i = 0; i <Card.SUITS.length ; i++) {
for (int j = 2; j <= 10; j++) {
Card card=new Card(Card.SUITS[i],j);
cardList.add(card);
}
}
//J、Q、K
for (int i = 0; i <Card.SUITS.length ; i++) {
for (int j = 0; j <Card.RANKFACES.length-1; j++) {
Card card=new Card(Card.SUITS[i],Card.RANKFACES[j],10);
cardList.add(card);
}
}
//A
for (int i = 0; i < Card.SUITS.length; i++) {
Card card=new Card(Card.SUITS[i],Card.RANKFACES[3],1);
cardList.add(card);
}
//大小王
Card cardBigKing=new Card("BigKing",10);
cardList.add(cardBigKing);
Card cardSmallKing=new Card("SmallKing",10);
cardList.add(cardSmallKing);
return cardList;
}
3.2 洗牌
玩牌最重要的就是让每一张牌都成为随机的,所以此时我们要调用一个Java中提供的随机方法Random()
特别注意Random方法中的参数必须是大于0的
Random random=new Random();
实现的逻辑:
让Random随机找一个数,让cardList数组的下标进行随机交换,最后实现洗牌的效果
public List<Card> shuffleCard(List<Card> cardList){
Random random=new Random();
for (int i = 0; i < cardList.size(); i++) {
int randIndex=random.nextInt(i+1);//bound的值必须大于0
Card tmp=cardList.get(i);
cardList.set(i,cardList.get(randIndex));
cardList.set(randIndex,tmp);
}
return cardList;
}
3.3 发牌
洗完牌后,我们要将洗过的牌发给每一位玩家,并开始游戏
我们是如何实现发牌的呢??
我们一直将牌的顶部的牌进行抽取,类似与链表中的头删,我们对其进行一定次数的头删,就可以实现将牌堆中的牌发给玩家的效果
那么我们又该如何对各位玩家的牌进行管理呢??
此时,我们创建一个二维数组,里面存放每一位玩家的手牌地址,对其访问就可以找到对应玩家的牌
public List<List<Card>> dealCard(List<Card> cardList){
//创建一个二维数组,里面存放每一位玩家的手牌地址,实现管理所有玩家牌的效果
List<List<Card>> hand=new ArrayList<>();
//分别创建玩家一、二、三的一维数组,用于存放手牌
List<Card> hand1=new ArrayList<>();
List<Card> hand2=new ArrayList<>();
List<Card> hand3=new ArrayList<>();
//将每一位玩家的地址存储在二维数组中,实现管理效果
hand.add(hand1);
hand.add(hand2);
hand.add(hand3);
//头删,实现玩家取牌的效果
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 5; j++) {
Card card= cardList.remove(0);
hand.get(i).add(card);
}
}
return hand;
}
3.4 展示玩家手牌
//将二维数组传递过来,即可实现查看玩家手牌的效果
public void display(List<List<Card>> hand){
//遍历循环打印三位玩家手牌
for (int i = 0; i <3 ; i++) {
System.out.println("第"+(i+1)+"个人的牌是:"+hand.get(i));
}
}
3.5 计算各位玩家的比赛成绩
3.5.1计算每位玩家牌的总点数
/**
* 计算总点数
* @param hand
* @return
*/
public int sumNumber(List<Card> hand){
int sum=0;
for (int i = 0; i < 5; i++) {
sum+=hand.get(i).rank;
}
return sum;
}
3.5.2 利用回溯算法组合出所有的牌组合
注意点: 引用类型不能直接用于计算,我们计算点数时,只要用对象调用对象成员中的成员变量即可,
这里我们的思路是先利用回溯算法
将可能的组合找出来,然后利用剩下两张牌计算最后的结果,对比最优解返回
回溯算法可以在此题中给你一定的启发:组合
/**
* 回溯算法组合牌
* @param cards
* @return
*/
public List<List<Card>> combine(List<Card> cards) {
//res用于存放组合结果
List<List<Card>> res = new ArrayList<>();
//c用于存放每一组可能
List<Card> c = new ArrayList<>();
//5 表示总共5张牌
//3 表示要组合的牌数
//0 表示从0下标开始
//c 是用于存放每一种可能的组合牌
//res 用于管理每一种可能
generateCombinations(5, 3, 0, c,cards,res);
return res;
}
/**
* 回溯求所有组合结果
* @param n
* @param k
* @param start 开始搜索新元素的位置
* @param c 当前已经找到的组合
*/
private void generateCombinations(int n,int k,int start,List<Card> c,List<Card> cards,List<List<Card>> res) {
//c.size() == k 表示已存满三张牌
if (c.size() == k) {
//这里需要注意java的值传递
//此处必须使用重新创建对象的形式,否则 res 列表中存放的都是同一个引用
res.add(new ArrayList<>(c));
return;
}
//通过终止条件,进行剪枝优化,避免无效的递归
//c中还剩 k - c.size()个空位,所以[ i ... n]中至少要有k-c.size()个元素
//所以i最多为 n - (k - c.size())
for(int i = start;i <= n - (k - c.size()) ; i++) {
c.add((cards.get(i)));
generateCombinations(n, k, i + 1, c,cards,res);
//记得回溯状态啊
c.remove(c.size() - 1);
}
}
3.5.3 找到最优解🐂
/**
* 找牛
* @param res
* @return
*/
public int findMaxCOw(List<List<Card>> res,int sum){
int [] cowGroup = new int[10];
int cow=0;
for (int i = 0; i <10 ; i++) {
cow=res.get(i).get(0).rank+res.get(i).get(1).rank+res.get(i).get(2).rank;
if(cow%10==0){
cowGroup[i]=sum-cow;
}
}
int maxCow=0;
for (int i = 0; i < cowGroup.length; i++) {
if(cowGroup[i]>maxCow){
maxCow=cowGroup[i]%10;
}
if(cowGroup[i]%10==0&&cowGroup[i]!=0){
return 10;
}
}
return maxCow;
}
3.6 完整Game 类
package democard;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Game {
/**
* 初始化牌
* @return
*/
public List<Card> createCard(){
List<Card> cardList=new ArrayList<>();
//2~10的牌
for (int i = 0; i <Card.SUITS.length ; i++) {
for (int j = 2; j <= 10; j++) {
Card card=new Card(Card.SUITS[i],j);
cardList.add(card);
}
}
//J、Q、K
for (int i = 0; i <Card.SUITS.length ; i++) {
for (int j = 0; j <Card.RANKFACES.length-1; j++) {
Card card=new Card(Card.SUITS[i],Card.RANKFACES[j],10);
cardList.add(card);
}
}
//A
for (int i = 0; i < Card.SUITS.length; i++) {
Card card=new Card(Card.SUITS[i],Card.RANKFACES[3],1);
cardList.add(card);
}
//大小王
Card cardBigKing=new Card("BigKing",10);
cardList.add(cardBigKing);
Card cardSmallKing=new Card("SmallKing",10);
cardList.add(cardSmallKing);
return cardList;
}
/**
* 洗牌
* @param cardList
* @return
*/
public List<Card> shuffleCard(List<Card> cardList){
Random random=new Random();
for (int i = 0; i < cardList.size(); i++) {
int randIndex=random.nextInt(i+1);//bound的值必须大于0
Card tmp=cardList.get(i);
cardList.set(i,cardList.get(randIndex));
cardList.set(randIndex,tmp);
}
return cardList;
}
/**
* 发牌
* @param cardList
* @return
*/
public List<List<Card>> dealCard(List<Card> cardList){
List<List<Card>> hand=new ArrayList<>();
List<Card> hand1=new ArrayList<>();
List<Card> hand2=new ArrayList<>();
List<Card> hand3=new ArrayList<>();
hand.add(hand1);
hand.add(hand2);
hand.add(hand3);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 5; j++) {
Card card= cardList.remove(0);
hand.get(i).add(card);
}
}
return hand;
}
/**
* 展示玩家手牌
* @param hand
*/
public void display(List<List<Card>> hand){
for (int i = 0; i <3 ; i++) {
System.out.println("第"+(i+1)+"个人的牌是:"+hand.get(i));
}
}
/**
* 计算总点数
* @param hand
* @return
*/
public int sumNumber(List<Card> hand){
int sum=0;
for (int i = 0; i < 5; i++) {
sum+=hand.get(i).rank;
}
return sum;
}
/**
* 找牛
* @param res
* @return
*/
public int findMaxCOw(List<List<Card>> res,int sum){
int [] cowGroup = new int[10];
int cow=0;
for (int i = 0; i <10 ; i++) {
cow=res.get(i).get(0).rank+res.get(i).get(1).rank+res.get(i).get(2).rank;
if(cow%10==0){
cowGroup[i]=sum-cow;
}
}
int maxCow=0;
for (int i = 0; i < cowGroup.length; i++) {
if(cowGroup[i]>maxCow){
maxCow=cowGroup[i]%10;
}
if(cowGroup[i]%10==0&&cowGroup[i]!=0){
return 10;
}
}
return maxCow;
}
/**
* 回溯算法组合牌
* @param cards
* @return
*/
public List<List<Card>> combine(List<Card> cards) {
//res用于存放组合结果
List<List<Card>> res = new ArrayList<>();
//c用于存放每一组可能
List<Card> c = new ArrayList<>();
generateCombinations(5, 3, 0, c,cards,res);
return res;
}
/**
* 回溯求所有组合结果
* @param n
* @param k
* @param start 开始搜索新元素的位置
* @param c 当前已经找到的组合
*/
private void generateCombinations(int n,int k,int start,List<Card> c,List<Card> cards,List<List<Card>> res) {
if (c.size() == k) {
//这里需要注意java的值传递
//此处必须使用重新创建对象的形式,否则 res 列表中存放的都是同一个引用
res.add(new ArrayList<>(c));
return;
}
//通过终止条件,进行剪枝优化,避免无效的递归
//c中还剩 k - c.size()个空位,所以[ i ... n]中至少要有k-c.size()个元素
//所以i最多为 n - (k - c.size())
for(int i = start;i <= n - (k - c.size()) ; i++) {
c.add((cards.get(i)));
generateCombinations(n, k, i + 1, c,cards,res);
//记得回溯状态啊
c.remove(c.size() - 1);
}
}
/**
* 计算各位玩家的比赛成绩,并打印比赛结果
* @param hand
*/
public void findCow(List<List<Card>> hand){
int maxCow1=calculate(hand,0);
System.out.println("牛"+maxCow1);
int maxCow2=calculate(hand,1);
System.out.println("牛"+maxCow2);
int maxCow3=calculate(hand,2);
System.out.println("牛"+maxCow3);
disPlayOutCome(maxCow1,maxCow2,maxCow3);
}
/**
* 打印比赛结果
* @param play1
* @param play2
* @param play3
*/
public void disPlayOutCome(int play1,int play2,int play3){
if(play1==play2&&play1>play3){
System.out.println("--------------------------");
System.out.println("|play1 and play2 victory!|");
System.out.println("--------------------------");
return;
}
if(play2==play3&&play2>play1){
System.out.println("--------------------------");
System.out.println("|play2 and play3 victory!|");
System.out.println("--------------------------");
return;
}
if(play1==play3&&play1>play2){
System.out.println("--------------------------");
System.out.println("|play1 and play3 victory!|");
System.out.println("--------------------------");
return;
}
if(play1==play2&&play1==play3){
System.out.println("------------");
System.out.println("|Every Draw!|");
System.out.println("|----------|");
return;
}
if(play1>play2&&play1>play3){
System.out.println("----------------");
System.out.println("|play1 victory|!");
System.out.println("----------------");
}
if(play2>play1&&play2>play3){
System.out.println("----------------");
System.out.println("|play2 victory!|");
System.out.println("----------------");
}
if(play3>play1&&play3>play2){
System.out.println("----------------");
System.out.println("|play3 victory!|");
System.out.println("----------------");
}
}
}
4. Test 类
4.1 菜单打印
/**
* 菜单展示
*/
public static void menu() {
System.out.println("----------------------------------");
System.out.println("| 欢迎来到扑克牌游戏! |");
System.out.println("| 1.玩游戏 |");
System.out.println("| 2.游戏规则 |");
System.out.println("| 3.退出游戏 |");
System.out.println("----------------------------------");
}
/**
* 菜单选择
* @param choice
*/
public static void select(Game game,int choice){
switch (choice){
case 1:
List<Card> cardList=game.createCard();//创建一副牌
System.out.println("初始化牌:"+cardList);//初始化的牌
List<Card> cardListByShuffled=game.shuffleCard(cardList);//洗过的牌
System.out.println("洗过的牌:"+cardListByShuffled);
System.out.println("======展示各玩家手牌======");
List<List<Card>> dealByCard=game.dealCard(cardListByShuffled);
game.display(dealByCard);
System.out.println("=========比赛结果========");
game.findCow(dealByCard);
break;
case 2:
System.out.println("游戏规则:");
System.out.println("三方抓牌,每人抓五张,其中三张牌和为十的倍数");
System.out.println("被认为有'牛',若没有三张牌没有凑到十的倍数,就认为没'牛'");
System.out.println("剩余两张牌之和个位数为多少,就是牛几");
System.out.println("最后比谁的牛大,谁就获胜");
System.out.println("若三方都没牛,或者牛一样大,则认为平局");
break;
case 3:
System.out.println("退出游戏!!");
System.exit(0);
}
}
4.2 主方法调用
public static void main(String[] args) {
menu();
int choice=0;
do {
Scanner sc = new Scanner(System.in);
choice= sc.nextInt();
select(choice);
}while(choice!=0);
}
4.3 完整Test类
package democard;
import java.util.List;
import java.util.Scanner;
public class Test {
/**
* 菜单展示
*/
public static void menu() {
System.out.println("----------------------------------");
System.out.println("| 欢迎来到扑克牌游戏! |");
System.out.println("| 1.玩游戏 |");
System.out.println("| 2.游戏规则 |");
System.out.println("| 3.退出游戏 |");
System.out.println("----------------------------------");
}
/**
* 菜单选择
* @param choice
*/
public static void select(Game game,int choice){
switch (choice){
case 1:
List<Card> cardList=game.createCard();//创建一副牌
System.out.println("初始化牌:"+cardList);//初始化的牌
List<Card> cardListByShuffled=game.shuffleCard(cardList);//洗过的牌
System.out.println("洗过的牌:"+cardListByShuffled);
System.out.println("======展示各玩家手牌======");
List<List<Card>> dealByCard=game.dealCard(cardListByShuffled);
game.display(dealByCard);
System.out.println("=========比赛结果========");
game.findCow(dealByCard);
break;
case 2:
System.out.println("游戏规则:");
System.out.println("三方抓牌,每人抓五张,其中三张牌和为十的倍数");
System.out.println("被认为有'牛',若没有三张牌没有凑到十的倍数,就认为没'牛'");
System.out.println("剩余两张牌之和个位数为多少,就是牛几");
System.out.println("最后比谁的牛大,谁就获胜");
System.out.println("若三方都没牛,或者牛一样大,则认为平局");
break;
case 3:
System.out.println("退出游戏!!");
System.exit(0);
}
}
public static void main(String[] args) {
Game game=new Game();
menu();
int choice=0;
do {
Scanner sc = new Scanner(System.in);
choice= sc.nextInt();
select(game,choice);
}while(choice!=0);
}
}