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);
需要注意的点(萌新博主犯的错误)
链表之间的节点关联。
弄明白游戏机制(为了出一张牌倒是得多摸几张牌)
别一股脑不调试刷刷刷写到底,会崩溃的(指心情)
好吧,就这样了,欢迎各方大佬指正,本人虚心求教。路漫漫其修远兮。