一.项目目标
利用Java swing技术能够实现玩家与电脑进行二十一点游戏。要求如下:
纸牌数:共52张纸牌,除去大小王两张纸牌。
花色:红桃、黑桃、方块、梅花。
纸牌的面值:A到10的纸牌面值按照1到10计算,J、Q、K的纸牌面值按照1进行计算。
玩法:电脑先抓牌,玩家后抓牌。计算自己的面值总数,比较面值数;如果面值数总数都大于或等于21,则平局;如果玩家和电脑的面值总数有一个大于21点,另一个不大于21点,则不大于21点的为赢家;如果玩家和电脑的面值总数都不大于21,则面值总数大的为赢家。
二.项目分析
算法
(1)生成一副符号要求的纸牌,以纸牌的背面显示给玩家,即洗牌;
(2)游戏开始,电脑先依次抓牌,抓牌的总面值数大于等于15点就停止抓牌。并以牌的背面显示给玩家;
(3)玩家依次抓牌,玩家根据牌的总面值数确定是否再抓牌。以牌的正面显示给玩家;
(4)玩家确定不再抓牌,即本次游戏结束,计算胜负关系,显示玩家和电脑的胜负关系。
牌的图片命名规则
在游戏中的纸牌以图片的方式显示,因此对图片的命名与纸牌的面值和花色进行关联,规则如下:
(1)“1”代表红桃,“2”代表黑桃,“3”代表方块,“4”代表梅花;
(2)“1到10”代表面值“A到10”,11代表面值为J,12代表面值为Q,13代表面值为K;
(3)图片的名称由花色、连字符“-”和面值三部分信息组成,如1-1.gih代表红桃A,4-11.gif代表梅花J
一张牌的设计
根据牌的特性:设计一个Card类,含有两个属性type和value,分别表示牌的花色和面值。创建一个对象就等于生成一张牌。
一副牌的设计
定义Card数组,长度为52,把52张牌放到数组中。要实现随机放置牌到数组中,首先生成一副有规律的牌,依次生成红桃A到红桃K、黑桃A到黑桃K、方块A到方块K、梅花A到梅花K,共52张牌,同时确定花色和面值,依次放入Card数组中。再循环52次,每次生成两个0~51之间的整数,依据利用这两个随机数交换Card数组中对应位置的内容,即随机打乱这副牌。
类的设计
对本项目实现的功能设计了4个类:Card、CardManager、GameFrame和AboutFrame。
Card类主要是对一张纸牌的设计;CardManager类主要是随机生成一副52张纸牌,并在容器中发牌;GameFrame类主要负责游戏界面和玩法等;AboutFrame类主要显示游戏规则和纸牌图片的版权信息。
CardManager类中定义了Card数组,长度为52,用于存放纸牌。主要方法如下:
(1)void initCards():按照一定规则初始化一副52张纸牌。
(2)void randomCard():随机打乱这副纸牌。
(3)void gameStart(JLabel game[], Container c):用于在容器中显示纸牌的图片,其中参数game[]是标签框控件数组,用于显示图片;参数c代表显示纸牌的容器,即Frame对象。
GameManager类实现了ActionListener接口,主要含有记牌器、电脑点数、玩家点数、存放纸牌的标签控件数组、存储电脑纸牌的向量类对象、CardManager类对象等属性。主要方法如下:
(1)GameFrame():窗体控件初始化。
(2)void actionPerformed(ActionEvent e):主要处理命令按钮动作,即洗牌、开始游戏、玩家抓牌和显示结果的动作。
三.代码实现
Card.java:
package 二十一点游戏;
public class Card {
private int value = 0; //纸牌面值
private int type = 0; //纸牌花色
public Card(int type, int value) {
this.value = value;
this.type = type;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
CardManager.java:
package 二十一点游戏;
import java.awt.Container;
import java.awt.Rectangle;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
public class CardManager {
public Card[] cards = new Card[52];
//初始化一副52张纸牌
public void initCards() {
for (int i = 1; i <= 4; i++) {
for (int j = 1; j <= 13; j++) {
cards[(i - 1)*13 + j - 1] = new Card(i, j);
}
}
}
//随机打乱这52张纸牌
public void randomCards() {
Card temp = null;
//随机生成牌号
for (int i = 0; i < 52; i++) {
int a = (int)(Math.random()*52);
int b = (int)(Math.random()*52);
temp = cards[a];
cards[a] = cards[b];
cards[b] = temp;
}
}
//显示纸牌图片
public void gameStart(JLabel game[], Container c) {
//在容器中删除标签组件
if (game[0] != null) {
for (int i = 0; i < 52; i++) {
c.remove(game[i]);
}
c.repaint();
}
//在容器中放置52个标签组件用于放置图片
for (int i = 0; i < 52; i++) {
game[i] = new JLabel();
game[i].setBorder(javax.swing.BorderFactory.createEtchedBorder());
game[i].setBounds(new Rectangle(100 + i*10, 10, 101, 150));
c.add(game[i]);
}
//设置标签组件的图片为rear.jpg,即牌的背面
for (int i = 0; i < 52; i++) {
game[i].setIcon(new ImageIcon("images/rear.jpg"));
}
}
}
GameFrame.java:
package 二十一点游戏;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Vector;
import javax.swing.*;
public class GameFrame extends JFrame implements ActionListener{
JButton clear_btn = new JButton("洗牌");
JButton compute_btn = new JButton("开始游戏");
JButton game_btn = new JButton("玩家抓牌");
JButton gameover_btn = new JButton("本轮结束");
CardManager cm = new CardManager();
JLabel game[] = new JLabel[52]; //放置52张扑克牌的标签
int i = 0; //定义抓牌数
int computer_dot = 0; //定义电脑点数
int game_dot = 0; //定义玩家点数
Vector v = new Vector(); //存储电脑抓的纸牌
JLabel jLabel1 = new JLabel("电脑显示牌区");
JLabel jLabel2 = new JLabel("玩家显示牌区");
public GameFrame() {
getContentPane().setLayout(null);
this.setTitle("二十一点游戏");
this.setSize(800, 500);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); //获得屏幕的宽和高
Dimension frameSize = this.getSize(); //获得当前窗体的宽和高
//设置窗体居中
if (frameSize.height > screenSize.height) {
frameSize.height = screenSize.height;
}
if (frameSize.width > screenSize.width) {
frameSize.width = screenSize.width;
}
this.setLocation((screenSize.width-frameSize.width)/2, (screenSize.height-frameSize.height)/2);
clear_btn.setBounds(new Rectangle(78, 388, 73, 31));
clear_btn.addActionListener(this);
compute_btn.setBounds(new Rectangle(233, 388, 86, 31));
compute_btn.setEnabled(false);
compute_btn.addActionListener(this);
game_btn.setBounds(new Rectangle(413, 389, 91, 32));
game_btn.setEnabled(false);
game_btn.addActionListener(this);
gameover_btn.setBounds(new Rectangle(625, 390, 91, 32));
gameover_btn.setEnabled(false);
gameover_btn.addActionListener(this);
JMenuBar menuBar = new JMenuBar();
JMenu file = new JMenu("文件");
JMenu help = new JMenu("帮助");
JMenuItem fileExit = new JMenuItem("退出");
JMenuItem helpAbout = new JMenuItem("关于");
this.setJMenuBar(menuBar);
menuBar.add(file);
menuBar.add(help);
file.add(fileExit);
fileExit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
System.exit(0);
}
});
help.add(helpAbout);
helpAbout.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
new AboutFrame();
}
});
jLabel1.setBounds(new Rectangle(104, 343, 95, 38));
jLabel2.setBounds(new Rectangle(499, 343, 92, 33));
this.getContentPane().add(jLabel1);
this.getContentPane().add(jLabel2);
this.getContentPane().add(clear_btn);
this.getContentPane().add(compute_btn);
this.getContentPane().add(game_btn);
this.getContentPane().add(gameover_btn);
this.setVisible(true);
this.setResizable(false);
}
@Override
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
//洗牌按钮
if (arg0.getSource() == clear_btn) {
//关闭和开启相应的按钮
compute_btn.setEnabled(true);
clear_btn.setEnabled(false);
//对记牌器、电脑点数和玩家点数进行初始化
i = 0;
computer_dot = 0;
game_dot = 0;
//把标签框控件数组放入窗体窗格中
cm.gameStart(game, this.getContentPane());
//初始化纸牌
cm.initCards();
//随机打乱纸牌
cm.randomCards();
}
//开始游戏按钮
if (arg0.getSource() == compute_btn) {
//关闭和开启相应的按钮
compute_btn.setEnabled(false);
game_btn.setEnabled(true);
//电脑抓牌
for (int k = 0; k < 20; k++) {
game[i].setIcon(new ImageIcon("images/rear.jpg"));
game[i].setBounds(new Rectangle(50 + i*20, 200, 101, 150));
getContentPane().setComponentZOrder(game[i], 1);
if (cm.cards[i].getValue() == 10) {
computer_dot = computer_dot + 1;
}else {
computer_dot = computer_dot + cm.cards[i].getValue();
}
v.add(cm.cards[i]);
getContentPane().repaint();
i = i + 1;
//如果面值总数大于15停止抓牌
if (computer_dot >= 15) {
return;
}
}
}
//玩家抓牌按钮
if (arg0.getSource() == game_btn) {
//提示
if (game_dot >= 10) {
int a = JOptionPane.showConfirmDialog(null, "现在点数为:"+game_dot+"是否再抓牌", "提示", JOptionPane.YES_NO_OPTION);
if (a == JOptionPane.NO_OPTION) {
game_btn.setEnabled(false);
gameover_btn.setEnabled(true);
return;
}
}
//设置标签框力里显示抓到的纸牌
game[i].setIcon(new ImageIcon("images/"+cm.cards[i].getType()+"-"+cm.cards[i].getValue()+".jpg"));
game[i].setBounds(new Rectangle(350 + i*20, 200, 101, 150));
this.getContentPane().setComponentZOrder(game[i], 1);
//计算抓到的纸牌面值
if (cm.cards[i].getValue() > 10) {
game_dot = game_dot + 1;
}else {
game_dot = game_dot + cm.cards[i].getValue();
}
i = i + 1;
//面值大于21停止抓牌,关闭和开启相应按钮
if (game_dot > 21) {
game_btn.setEnabled(false);
gameover_btn.setEnabled(true);
return;
}
}
//本轮游戏结束按钮
if (arg0.getSource() == gameover_btn) {
for (int i = 0; i < v.size(); i++) {
Card card = (Card)v.get(i);
game[i].setIcon(new ImageIcon("images/"+card.getType()+"-"+card.getValue()+".jpg"));
game[i].setBounds(new Rectangle(50 + i*20, 200, 101, 150));
this.getContentPane().setComponentZOrder(game[i], 1);
}
//计算胜负
String game_over = "";
if (game_dot > 21 && computer_dot <= 21) {
game_over = "电脑获胜";
} else if (game_dot <= 21 && computer_dot > 21) {
game_over = "玩家获胜";
}else if (game_dot >= 21 & computer_dot >= 21) {
game_over = "平局";
}else if (game_dot > computer_dot) {
game_over = "玩家获胜";
}else if (game_dot < computer_dot) {
game_over = "电脑获胜";
}else if (game_dot == computer_dot) {
game_over = "平局";
}
//以对话框的方式显示胜负
String message = "游戏结果\n";
message = message + "电脑点数:" + String.valueOf(computer_dot) + "\n";
message = message + "玩家点数:" + String.valueOf(game_dot) + "\n";
message = message + "游戏结果:" + game_over;
JOptionPane.showMessageDialog(null, message, "本轮游戏结果", JOptionPane.INFORMATION_MESSAGE);
//设置命令按钮可操作
clear_btn.setEnabled(true);
compute_btn.setEnabled(true);
game_btn.setEnabled(true);
gameover_btn.setEnabled(true);
}
}
public static void main(String[] args) {
GameFrame gameFrame = new GameFrame();
}
}
AboutFrame.java:
package 二十一点游戏;
import javax.swing.*;
import java.awt.*;
public class AboutFrame extends JFrame{
JLabel jLabel1 = new JLabel("游戏规则");
JLabel jLabel2 = new JLabel("声明");
JTextArea textArea1 = new JTextArea();
JTextArea textArea2 = new JTextArea();
public AboutFrame() {
getContentPane().setLayout(null);
jLabel1.setBounds(new Rectangle(27, 0, 97, 36));
jLabel2.setBounds(new Rectangle(26, 176, 80, 27));
textArea1.setColumns(40);
textArea1.setEditable(false);
textArea1.setLineWrap(true);
textArea1.setText("电脑先抓牌,玩家后抓牌。计算自己的面值总数,比较面值数,如果面值总数都大于或等于21,则平局;如果玩家和电脑的面值总数有一个大于21点,另一个不大于21点,则不大于21点的为赢家;如果玩家和电脑的面值总数都不大于21,则面值总数大的为赢家。");
textArea1.setBounds(new Rectangle(25, 36, 392, 130));
textArea1.setFont(new Font("楷体", Font.PLAIN, 16));
textArea2.setEditable(false);
textArea2.setLineWrap(true);
textArea2.setBounds(new Rectangle(26, 207, 398, 63));
textArea2.setText("该游戏中,纸牌的图片来自于WindowsXP的纸牌游戏,图片权属于原作者所有!");
textArea2.setFont(new Font("楷体", Font.PLAIN, 16));
this.getContentPane().add(jLabel1);
this.getContentPane().add(jLabel2);
this.getContentPane().add(textArea1);
this.getContentPane().add(textArea2);
this.setSize(450, 300);
this.setTitle("关于");
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); //获得屏幕的宽和高
Dimension frameSize = this.getSize(); //获得当前窗体的宽和高
//设置窗体居中
if (frameSize.height > screenSize.height) {
frameSize.height = screenSize.height;
}
if (frameSize.width > screenSize.width) {
frameSize.width = screenSize.width;
}
this.setLocation((screenSize.width-frameSize.width)/2, (screenSize.height-frameSize.height)/2);
this.setVisible(true);
}
}