java模拟UNO玩家对战


UNO牌简单介绍

UNO牌共有108张牌。包括76张数字牌,32张功能牌。
颜色:红、黄、蓝、绿、黑 5种。
每人先发牌7张,其余当作牌库。翻开牌库,作为第一张,每人轮流出牌。每个出牌的人,必需出与上一家出的牌,同样颜色或同样数字的牌。没有牌可出时需从牌库中拿一张到手上,如果还是不能出,则跳过,轮到下一个人。有功能牌及王牌,可以使其他的人增加手上的牌,也可帮自己快点将牌出光。最先把手中牌出光的人赢。

代码实现

若不是班里有同学中午玩UNO,这篇文章才不会写出来。
首先是静态变量的定义。

 static float second =0.0f;//对局时间,假设每次出牌为4秒
 static int playerNUM = 1;
 static int playerNUM2 = 1;
 static boolean isClockwise = true;//是顺时针
 static int k = 0;//记录最后一张有效数字牌!
 static int handCardsNum = 7;//手牌数
 static int playerNumber = 4;//玩家数量
 static int lib [] = new int[108];
 static int OrderLib [] = new int[108];
 static int free[] = new int [108];
 static ArrayStack Library = new ArrayStack(108);//牌堆 栈实现
 static ArrayStack lose = new ArrayStack(108);//弃牌区 栈实现

UNO牌就是几个玩家围成一圈,在桌子上进行的娱乐卡牌游戏,尽管没办法加入他们打牌的行列,加上月底还有考试,所以代码写的冗余就体谅一下还在读高中的萌新程序员吧。
双向链表用来存储玩家信息,节点保存玩家编号,姓名,上一个节点和下一个节点,顺便挂一个数组用来保存自己的手牌。

class BidirectionalLinkedManager{
 Node head = new Node(1,"玩家1");//头节点
 boolean k1 = true;
 Main m = new Main();
 public void add(Node nd) {//添加节点
  Node temp = head;
  while(true) {
   if(temp.next==(k1?null:head)) {//头结点的下一个节点
    temp.next = nd;
    nd.pre = temp;
    //头结点与此节点
    head.pre = nd;
    nd.next = head;
    k1 = false;
    break;
   }
   temp = temp.next;
  }
 }
 
 public void list() {//遍历节点
  Node temp = head.next;
  System.out.println("N:"+head);
  while(true) {
   if(temp==head) {
    return;
   }
   System.out.println("N:"+temp);
   temp = temp.next;
  }
 }
}
class Node{
 int Num;
 int index = 0;
 int handcard[] = new int [100];
 String name;
 Node next;
 Node pre;
 Node(int _Num,String _name){
  this.name = _name;
  this.Num = _Num;
 }
 @Override
 public String toString() {
  // TODO Auto-generated method stub
  return " "+Num+" Name--> "+name+"  >"+"上一个: "+pre.name+" 下一个: "+next.name;
 }
}

其中,反转牌的实现我是采用一个isClockwise变量保存顺时针逆时针,使用反转牌则顺时针变逆时针,逆时针变顺时针。
假设有1-5名玩家,编号分别为1-5,顺时针,5后为1,1前为5。则+2+4牌就需要以下方法。

public static void numTurn(int pN,boolean isclock) {
  if(isclock) {
   if(pN<playerNumber) {
    //System.out.println("playerNumber"+pN);
    playerNUM++;
   }else{
    playerNUM=1;
   }
  }else {
   if(pN>1) {
    playerNUM--;
   }else {
    playerNUM=playerNumber;
   }
  }
 }

玩家对战,NPC的逻辑部分,new一个节点,用节点管理器来寻找保存的节点。找到编号对应的Node,把它挂着的数组拿过来遍历。静态变量里面有个k变量,用来存放最后一张有效牌。遍历判断该牌是否与k代表的颜色相同,若颜色相同,则还要注意几张还带有颜色的功能牌。+2,禁止与反转。
如何让k代表所有牌呢?
我注意到了UNO牌有0-9,以及+2,禁止,反转,黑色王牌的万用和+4,有红黄蓝绿黑五种颜色。所以我的做法是给它们编号,0-9一样,+2为10,禁止11,反转12,则给颜色编号就是100,200,300这样。
黑色牌万用550,黑色牌+4 660.。
举例 红4 代表编号104,绿反转代表编号412
先展示一下其他的方法。


public boolean iSHave(Node nd,int preCard) {//手牌是否有能出的牌
  Node temp = head;
  int c = 0;
  while(true) {
   if(nd.Num==temp.Num) {
    for(int i = 0;i<temp.index;i++) {
     int cardNum = temp.handcard[i];
     if((cardNum-cardNum%100)/100==(preCard-preCard%100)/100) {//相同颜色牌
      return true;
     }
     if(cardNum%100==preCard%100) {//相同数字牌
      return true;
     }
    }
   }
   temp = temp.next;
   if(++c>m.playerNumber)
    break;
  }
  return false;
 }
 public boolean iSHaveKing(Node nd) {//是否有王牌
  Node temp = head;
  int c = 0;
  while(true) {
   if(nd.Num==temp.Num) {
    for(int i = 0;i<temp.index;i++) {
     int cardNum = temp.handcard[i];
     if(cardNum>=500) {
      return true;
     }
    }
   }
   temp = temp.next;
   if(++c>m.playerNumber)
    break;
  }
  return false;
 }
 public void sort(Node nd) {//出牌后 整理手牌
  Node temp = head;
  while(true) {
   if(nd.Num==temp.Num) {
    for(int i = 0;i<temp.index-1;i++) {
     if(temp.handcard[i]==0) {
      temp.handcard[i] = temp.handcard[i+1];
      temp.handcard[i+1] = 0;
     }
    }
    temp.index--;
    break;
   }
   temp = temp.next;
  }
  System.out.println("出牌后 手牌数: "+temp.index);
 }
public void showCard(Node nd) {//展示手牌
  Node temp = head;
  while(true) {
   if(nd.Num==temp.Num) {//找到目标节点
    System.out.print("玩家"+temp.Num+"  手牌为");
    for(int i=0;i<temp.index;i++) {
     //System.out.print(temp.handcard[i]+" ");
     System.out.print(Main.howColor(temp.handcard[i])+temp.handcard[i]%100+" ");
    }
    System.out.println();
    break;
   }
   temp = temp.next;
  }
 }
 public void addArray(Node nd,int a) {//添加手牌
  Node temp = head;
  while(true) {
   if(nd.Num==temp.Num) {//找到目标节点
    temp.handcard[temp.index++]=a;
    break;
   }
   temp = temp.next;
  }
  if(Main.Library.top==0) {
   System.out.println("牌库空,洗牌");
   Random r1 = new Random();
   int index = Main.lose.top;
   System.out.println("lose index:"+ Main.lose.top);
   for(int i=0;i<index;i++) {
    Main.free[i] = Main.lose.Pop();
   }
   int s = index;
   while(index>0) {
    int s1 = r1.nextInt(s);
    if(Main.free[s1]!=0) {
     Main.Library.Push(Main.free[s1]);
     Main.free[s1]=0;
     index--;
    }
   }
  }
 }

然后我们写一个使用自己手牌的方法

public void useCard(Node nd,int k) {//使用手牌
  Node temp = head;
  int c = 0;
  while(true) {
   if(nd.Num==temp.Num) {
    System.out.println("temp:index:"+temp.index);
    boolean x = true;
    for(int i=0;i<temp.index;i++) {
     int num =temp.handcard[i];
     if(Main.howColor(num)==Main.howColor(m.k)) {//相同颜色
      showCard(nd);
      if(num%100<10) {
       temp.handcard[i]=0;
       sort(nd);
       Main.k = num;//覆盖有效牌
       System.out.println("玩家"+nd.Num+"打出"+Main.howColor(num)+num%100);
      }else if(num%100==10){//+2
       temp.handcard[i]=0;
       sort(nd);
       addArray(new Node(Main.NT1(nd.Num, Main.isClockwise)," "), Main.Library.Pop());
       addArray(new Node(Main.NT1(nd.Num, Main.isClockwise)," "), Main.Library.Pop());
       System.out.println("玩家"+nd.Num+"使用"+Main.howColor(num)+"+2牌");
      }else if(num%100==11) {//阻挡
       temp.handcard[i]=0;
       sort(nd);
       Main.numTurn(nd.Num,Main.isClockwise);
       System.out.println("玩家"+nd.Num+"使用阻挡牌");
      }else if(num%100==12) {//反转
       temp.handcard[i]=0;
       sort(nd);
       Main.isClockwise = Main.isClockwise ? false : true;
       System.out.println("玩家"+nd.Num+"使用反转牌");
       System.out.println(Main.isClockwise);
      }
      Main.lose.Push(num);//压入弃牌区
      x = false;
      break;
     }else if(num%100==k%100){//相同数字
      Main.k = num;//覆盖有效牌
      Main.lose.Push(num);
      temp.handcard[i]=0;
      sort(nd);
      showCard(nd);
      System.out.println("玩家"+nd.Num+"打出"+Main.howColor(num)+num%100);
      x = false;
      break;
     }else if(num%100==50){//
      Main.k = k-k%100+20;//覆盖有效牌
      Main.lose.Push(num);
      temp.handcard[i]=0;
      sort(nd);
      showCard(nd);
      System.out.println("玩家"+nd.Num+"使用万用牌");
      x = false;
      break;
     }else if(num%100==60){
      temp.handcard[i]=0;
      sort(nd);
      for(int j =0;j<4;j++) {
       addArray(new Node(Main.NT1(nd.Num, Main.isClockwise)," "), Main.Library.Pop());
      }
      showCard(nd);
      System.out.println("玩家"+nd.Num+"使用+4牌");
      x = false;
      break;
     }
    }
    if(x) {
    c=0;//让链表再循环一次找这个node编号
     addArray(nd, Main.Library.Pop());
     showCard(nd);
     System.out.println("玩家"+nd.Num+"抽牌");
    }
   }
   temp = temp.next;
   if(c++>Main.playerNumber-1) 
    break;
  }
 }
 

最开始的初始化:牌堆随机化,玩家建立等

public static void initial () {//初始化
  int index = 0;
  for(int i = 1;i<5;i++) {//基本牌四个颜色21张,有颜色功能牌24张
   for(int j = 0;j<13;j++) {//除无颜色功能牌以及0有一张牌,其余都是两张,则循环两次
    lib[index++] = j+100*i;
   }
   for(int j = 1;j<13;j++) {
    lib[index++] = j+100*i;
   }
  }
  for(int i=5;i<=6;i++) {//无颜色功能牌8张 +4 w
   for(int j=0;j<4;j++) {
    lib[index++] = i*110;
   }
  }
  Random r = new Random();
  while(index>0) {//没有重复为止
   int s = r.nextInt(108);
   if(OrderLib[s]==0) {
    OrderLib[s] = lib[--index];
   }
  }
  for(int i=0;i<OrderLib.length;i++) {//压入牌堆
   Library.Push(OrderLib[i]);
  }
 }


最后主方法

public static void main(String[] args) {
  BidirectionalLinkedManager blm = new BidirectionalLinkedManager();
  Scanner sc = new Scanner(System.in);
  initial();//初始化,牌堆已打乱
  System.out.println("请输入玩家数量");
  playerNumber = sc.nextInt();
  for(int i = 2;i<=playerNumber;i++) {
   Node n = new Node(i, "玩家"+i);
   blm.add(n);
  }
  blm.list();//
  System.out.println("玩家就绪");
  for(int i = 1;i<=playerNumber;i++) {
   Node nd = new Node(i, "玩家"+i);
   for(int j =0;j<handCardsNum;j++) {//每个玩家初始7张手牌
    blm.addArray(nd, Library.Pop());
   }
  }
  System.out.println("游戏开始");
  System.out.print("第一张牌是:");
  String first = ""; 
  while(true) {
   k = Library.Pop();
   if(isfunction(k)) {
    lose.Push(k);
   }else {
    System.out.print(howColor(k));
    System.out.println(k%100);
    lose.Push(k);
    break;
   }
  }
  while(true) {
   Node node = new Node(playerNUM,"玩家"+playerNUM);
   blm.useCard(node, k);
   //根据isClockwise判断playerNUM是否加或减
   numTurn(node.Num,isClockwise);
   second +=4.0;
   if(blm.isWin(node))
    break;
  }
  System.out.println("共用时"+second);

java模拟UNO玩家对战_第1张图片
需要注意的点(萌新博主犯的错误)
链表之间的节点关联。
弄明白游戏机制(为了出一张牌倒是得多摸几张牌)
别一股脑不调试刷刷刷写到底,会崩溃的(指心情)
好吧,就这样了,欢迎各方大佬指正,本人虚心求教。路漫漫其修远兮。

你可能感兴趣的:(java模拟UNO玩家对战)