108年01學年四筒老師期末報告範例提供給同學參考。
一、 前言
賓果遊戲,最早應可追溯到1530年,當時的意大利政府為增加稅收所發行的一種稱為“Beano”的彩票遊戲。這種遊戲後來漸漸擴及到法國、德國等歐洲其他國家,中間並經過了多次不同的改良與應用,最後傳到了美洲。
在美洲,這種第一次出現並被注意到的遊戲是發生在格魯吉亞、亞特蘭大附近舉行的一個狂歡節上。後來被一個名叫埃德溫.洛威的紐約玩具推銷員看到它的發展潛力,想將其作為公司發展的產品且加以發揮,並在一次陰錯陽差的遊戲中,因一位緊張的中獎婦人的口誤中,將它從“Beano”叫成“bingo”後,從此這項遊戲,就以“bingo”名稱傳播全球,這就是現今“賓果”一詞的由來。
剛開始風行的賓果遊戲,常遇到一次即有大量同時中獎的中獎人的困擾,為此埃德溫.洛威決定聘請一位名字叫卡爾.萊弗勒的數學教授幫其設計不同排列與玩法的賓果卡。截至1930年止,教授萊弗勒共計設計了6000多的賓果卡,如此才改善了大量同時中獎的困擾。
賓果遊戲真正開始大風行是起於一個天主教的一個教會募捐活動,且辦的非常成功。從此不管是教堂募捐或其它育樂活動,人們已習於將此項遊戲,作為辦理活動的必備工具。到1934年,估計全美國每星期有超過 10,000場次賓果遊戲進行過。今天,幾乎在整個美國的任何居民區,均可以看到賓果遊戲大廳。許多人在指定的日子與指定的時間裡,會聚集在這些賓果遊戲大廳,去彼此享受玩賓果。此外,由於網路科技的發達,各種相關的線上網路賓果遊戲網站也正如雨後春筍般冒出。(請參考文獻1)
二、 遊戲流程圖
三、 遊戲設計構思
賓果遊戲:賓果常見的遊戲規則,是在5×5共25格的方格中隨機填上1~25的數字,每個玩家輪流叫一次號碼,先達到5條者獲勝。
目的是計算在n×n賓果遊戲中,經過K次叫號之後,至少達成n條以上之機率。我們討論3×3、4×4以及5×5,透過排列組合的計算得到機率值;難以計算的部分,我們利用Java軟體進行模擬試驗,進而得到模擬數據。
1. Swing套件:
Swing 是一個為Java 設計的GUI 工具包。Swing是JAVA基礎類的一部分。Swing 包括了圖形化使用者介面(GUI)器件如:文字域,按鈕,分隔窗格和表。
2. 數字組合:
java.lang 套件中的Math 類別就定義了一個方法random(),這個方法可以產生亂數,其型態為double。
3. 遊戲方式:
一對一的對戰方式,一個使用者玩家和一個由程式設計一個電腦玩家。
4. 建立表格方式:
- 顯示一些文字或數字按鈕。
- 標籤和grid保持更新的演算法計算。
四、 結論
發現生活中有很多小遊戲跟賓果遊戲是有異曲同工之妙的,例如在夜市中有種抽麻將的遊戲也是給玩家25張牌,玩家可以抽16張牌,而桌上會先給你幾副牌組,要你抽的16張跟桌上的牌組相同就能獲得禮物。為甚麼不給14張、15 張?而剛好就給你16張?因為16張可以讓你在最小機率下有辦法抽到相同牌組,如果給15張就是詐欺了。
賓果遊戲是一個人以上的團體遊戲,因此無法獨自遊玩,雖然是一個簡單的遊戲,但是,建立一個獨立的視窗介面在個人電腦上進行的遊戲,還要程式設計一個電腦玩家,對新手難以思考利用哪一種程式語言及介面,上網查詢許多文獻,所以選擇一個大眾化的個人遊戲。
下載檔案:Java程式賓果遊戲
檔案原始碼:
package com.xattacker.game;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.FontUIResource;
@SuppressWarnings("serial")
class Bingo extends JFrame implements Runnable {
/** 佈子數, 當玩家把25個數字都佈完後 開始遊戲 */
private int iGameState, iNum, iTurn, iWinner = -1;
/** 雙方連線數 */
private int iConnects[] = new int[2];
private int iWeight[] = new int[3];
/** iWeight[0]=x, iWeight[1]=y iWeight[2]=weight */
private int iLocX, iLocY /* 下棋位置 */, iCount; /* 連棋數 */
@SuppressWarnings("rawtypes")
private ArrayList iStepStore = new ArrayList(); // 記錄棋步
// private Font font = new Font("new_font", Font.BOLD, 20);
private Font font = new FontUIResource("標楷體", Font.BOLD, 36);
private Font font14 = new FontUIResource("標楷體", Font.BOLD, 14);
private Grid iGrids[][][] = new Grid[2][5][5];
final static int PLAYER = 1;
final static int COMPUTER = 0;
// game state
final static int START = 0; // 遊戲開頭
final static int SELECT = 1; // 玩家分派值
final static int PLAYING = 2; // 遊戲中
final static int END = 3; // 遊戲結束
// direction
final static int NIL = -1; // 無方向
final static int OBLIQUE_1 = 0; // 左上向右下
final static int OBLIQUE_2 = 1; // 右上向左下
final static int HORIZONTAL = 2; // 橫向
final static int VERTICAL = 3; // 直向
public static void main(String[] arg) {
/* 執行主程式起始點 */
Bingo application = new Bingo();
application.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
Bingo() {
super("Bingo");
JPanel[] boardPanel = new JPanel[2];
JPanel containerPanel = new JPanel();
for (int i = 0; i < 2; i++) {
boardPanel[i] = new JPanel();
boardPanel[i].setLayout(new GridLayout(5, 5, 0, 0));
boardPanel[i].setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
for (int j = 0; j < 5; j++)
for (int k = 0; k < 5; k++) {
iGrids[i][j][k] = new Grid(i, j, k);
boardPanel[i].add(iGrids[i][j][k]);
}
containerPanel.add(boardPanel[i], BorderLayout.CENTER);
}
getContentPane().add(containerPanel, BorderLayout.CENTER);
iGameState = START;
ThreadStart();
setResizable(false);
// setSize(150, 250);
setSize(200, 250);
setVisible(true);
// 讓視窗一開始啟動顯示於正中央
setLocationRelativeTo(null);
}
private void ReStart() {
for (int i = 0; i < 2; i++) {
iConnects[i] = 0;
for (int j = 0; j < 5; j++)
for (int k = 0; k < 5; k++)
iGrids[i][j][k].Initial();
}
iNum = iWeight[2] = 0;
iWinner = -1;
iStepStore.clear();
iGameState = SELECT;
}
/** 電腦重新洗牌配值 */
private void washValue() {
int temp_value = 0, x = 0, y = 0;
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++) {
temp_value = iGrids[COMPUTER][i][j].getValue();
x = (int) (Math.random() * 5);
y = (int) (Math.random() * 5);
iGrids[COMPUTER][i][j].setValue(iGrids[COMPUTER][x][y]);
iGrids[COMPUTER][x][y].setValue(temp_value);
}
}
/** 當一方下完後, 另一方也下同一個值 */
@SuppressWarnings("unchecked")
private void reDo(int aValue) {
if (iGameState == PLAYING) {
iTurn = (iTurn == PLAYER) ? COMPUTER : PLAYER;
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++) {
if (iGrids[iTurn][i][j].getValue() == aValue) {
iGrids[iTurn][i][j].setSelected(true);
iStepStore.add(iGrids[iTurn][i][j]);
break;
}
}
}
}
private void WinCheck() {
if (iGameState == PLAYING) {
WinCheck(OBLIQUE_1);
repaint();
}
}
private void WinCheck(int aDirection) {
iCount = 1;
int[] offset = getOffsetValue(aDirection);
int x = iLocX + offset[0];
int y = iLocY + offset[1];
while (x >= 0 && x < 5 && y >= 0 && y < 5 && iGrids[iTurn][x][y].isSelected()) {
iCount = iCount + 1;
x = x + offset[0];
y = y + offset[1];
}
x = iLocX - offset[0];
y = iLocY - offset[1];
while (x >= 0 && x < 5 && y >= 0 && y < 5 && iGrids[iTurn][x][y].isSelected()) {
iCount = iCount + 1;
x = x - offset[0];
y = y - offset[1];
}
if (iCount >= 5) {
x = iLocX;
y = iLocY;
while (x >= 0 && x < 5 && y >= 0 && y < 5 && iGrids[iTurn][x][y].isSelected()) {
iGrids[iTurn][x][y].setConnectedLine(aDirection, true);
x = x + offset[0];
y = y + offset[1];
}
x = iLocX - offset[0];
y = iLocY - offset[1];
while (x >= 0 && x < 5 && y >= 0 && y < 5 && iGrids[iTurn][x][y].isSelected()) {
iGrids[iTurn][x][y].setConnectedLine(aDirection, true);
x = x - offset[0];
y = y - offset[1];
}
iConnects[iTurn] = iConnects[iTurn] + 1;
if (iConnects[iTurn] >= 5) {
iGameState = END;
iWinner = iTurn;
ThreadStart();
}
}
if (aDirection != VERTICAL)
WinCheck(aDirection + 1);
}
/** 隨機亂數產生器 */
@SuppressWarnings("unchecked")
private void BingoRandom() {
int i, j;
if (!iGrids[COMPUTER][2][2].isSelected())// 正中央的位置, first priority
i = j = 2;
else {
do {
i = (int) (Math.random() * 5);
j = (int) (Math.random() * 5);
} while (iGrids[COMPUTER][i][j].isSelected());
}
iGrids[COMPUTER][i][j].setSelected(true);
iStepStore.add(iGrids[COMPUTER][i][j]);
reDo(iGrids[COMPUTER][i][j].getValue());
}
@SuppressWarnings("unchecked")
private void BingoAI() {
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++) {
if (!iGrids[COMPUTER][i][j].isSelected())
BingoAI2(i, j);
}
if (iWeight[2] > 1) {
// System.out.println(weight[2]);
iWeight[2] = 0;
iGrids[COMPUTER][iWeight[0]][iWeight[1]].setSelected(true);
iStepStore.add(iGrids[COMPUTER][iWeight[0]][iWeight[1]]);
reDo(iGrids[COMPUTER][iWeight[0]][iWeight[1]].getValue());
} else
BingoRandom();
}
private void BingoAI2(int aI, int aJ) {
iLocX = aI;
iLocY = aJ;
int offset_x = 0, offset_y = 0;
int w = 0, direction = OBLIQUE_1;
while (direction != NIL) {
switch (direction) {
case OBLIQUE_1:
offset_x = 1;
offset_y = -1;
direction = OBLIQUE_2;
break;
case OBLIQUE_2:
offset_x = offset_y = 1;
direction = HORIZONTAL;
break;
case HORIZONTAL:
offset_x = 1;
offset_y = 0;
direction = VERTICAL;
break;
case VERTICAL:
offset_x = 0;
offset_y = 1;
direction = NIL;
break;
}
w = w + calculateWeight(offset_x, offset_y);
}
if (w > iWeight[2]) {
iWeight[0] = iLocX;
iWeight[1] = iLocY;
iWeight[2] = w;
}
}
private int[] getOffsetValue(int aDirection) {
int[] offset = new int[2];
if (aDirection != NIL) {
switch (aDirection) {
case OBLIQUE_1:
offset[0] = 1;
offset[1] = -1;
break;
case OBLIQUE_2:
offset[0] = offset[1] = 1;
break;
case HORIZONTAL:
offset[0] = 1;
offset[1] = 0;
break;
case VERTICAL:
offset[0] = 0;
offset[1] = 1;
break;
}
}
return offset;
}
private int calculateWeight(int aOffset_X, int aOffset_Y) {
int w = 0;
iCount = 0;
int x = iLocX, y = iLocY;
while (x >= 0 && x < 5 && y >= 0 && y < 5) {
if (iGrids[COMPUTER][x][y].isSelected())
w = w + 1;
iCount = iCount + 1;
x = x + aOffset_X;
y = y + aOffset_Y;
}
x = iLocX - aOffset_X;
y = iLocY - aOffset_Y;
while (x >= 0 && x < 5 && y >= 0 && y < 5) {
if (iGrids[COMPUTER][x][y].isSelected())
w = w + 1;
iCount = iCount + 1;
x = x - aOffset_X;
y = y - aOffset_Y;
}
if (w == 4)// 加重已有四個被選擇的行列權重
w = w + 1;
return (iCount == 5) ? w * w : 0;
}
private void ThreadStart() {
new Thread(this).start();
}
public void run() {
try {
switch (iGameState) {
case START:
Thread.sleep(2000);
iGameState = SELECT;
setGridBorder(true);
repaint();
break;
case END:
repaint();
// Thread.sleep(1500);
Thread.sleep(10000);// 等待時間放寬,使用者才看的到
ReStart();
repaint();
break;
}
} catch (InterruptedException ex) {
}
}
public void paint(Graphics aGph) {
super.paint(aGph);
switch (iGameState) {
case START:
// aGph.drawString("2006.4 by Yu Lin Tao", 16, 200);
aGph.drawString("2015/5 Edit by four.casings", 16, 200);
aGph.setFont(font);
aGph.setColor(Color.BLUE);
aGph.drawString("Bingo", getWidth() / 3, 120);
break;
case SELECT:
case PLAYING:
case END:
// aGph.drawString("電腦", 2, 75);
// aGph.drawString("C: " + iConnects[COMPUTER], 2, 89);
// aGph.drawString("玩家", 2, 180);
// aGph.drawString("C: " + iConnects[PLAYER], 2, 194);
aGph.setColor(Color.ORANGE);
aGph.setFont(font14);
aGph.drawString("電腦", 4, 75);
aGph.drawString("連線:" + iConnects[COMPUTER], 4, 89);
aGph.setColor(Color.BLUE);
aGph.setFont(font14);
aGph.drawString("玩家", 4, 180);
aGph.drawString("連線:" + iConnects[PLAYER], 4, 194);
if (iWinner != -1) {
String message = null;
aGph.setFont(font);
// aGph.setColor(Color.BLUE);
// message = iWinner == PLAYER ? "你贏了" : "你輸了";
// aGph.drawString(message, 40, 130);
if (iWinner == PLAYER) {
aGph.setColor(Color.BLUE);
message = "玩家贏了";
} else {
aGph.setColor(Color.ORANGE);
message = "電腦贏了";
}
aGph.drawString(message, 40, 130);
}
break;
}
}
private void setGridBorder(boolean aBorder) {
for (int i = 0; i < 2; i++)
for (int j = 0; j < 5; j++)
for (int k = 0; k < 5; k++)
iGrids[i][j][k].setBorder(aBorder);
}
private void unDo() {
if (iStepStore.size() > 0) {
Grid grid = null;
for (int i = 0; i < 4; i++) {
grid = (Grid) iStepStore.get(iStepStore.size() - 1);
grid.setSelected(false);
if (grid.isConnected()) {
boolean[] connection_lines = grid.getConnectedLines();
int connection_count = 0;
int owner = grid.getOwner();
for (int j = 0; j < connection_lines.length; j++) {
if (connection_lines[j]) {
connection_count++;
int x = grid.getLocX(), y = grid.getLocY();
int offset[] = getOffsetValue(j);
while (x >= 0 && x < 5 && y >= 0 && y < 5 && iGrids[owner][x][y].isConnected()) {
iGrids[owner][x][y].setConnectedLine(j, false);
x = x + offset[0];
y = y + offset[1];
}
x = grid.getLocX() - offset[0];
y = grid.getLocY() - offset[1];
while (x >= 0 && x < 5 && y >= 0 && y < 5 && iGrids[owner][x][y].isConnected()) {
iGrids[owner][x][y].setConnectedLine(j, false);
x = x - offset[0];
y = y - offset[1];
}
grid.setConnectedLine(j, false);
grid.setConnected(false);
}
}
iConnects[owner] = iConnects[owner] - connection_count;
repaint();
}
iStepStore.remove(iStepStore.size() - 1);
}
}
}
/** 給玩家在配值時 悔棋用 */
private void unDoNum() {
if (iStepStore.size() > 0) {
iNum = iNum - 1;
((Grid) iStepStore.get(iStepStore.size() - 1)).Initial();
iStepStore.remove(iStepStore.size() - 1);
}
}
private class Grid extends JPanel implements MouseListener {
private int x, y, value, owner;
private boolean choice, selected, connected;
private boolean[] direction = new boolean[4];
public Grid(int own, int x, int y) {
owner = own;
this.x = x;
this.y = y;
Initial();
addMouseListener(this);
}
@SuppressWarnings("unchecked")
public void mousePressed(MouseEvent event) {
int button = event.getButton();
if (owner == PLAYER) {
switch (iGameState) {
case SELECT:
if (button == MouseEvent.BUTTON1) {
if (value == 0 && iNum < 25) {
value = iNum = iNum + 1;
iStepStore.add(this);
if (iNum == 25) {
iStepStore.clear(); // 洗掉已下數字
washValue();
iGameState = PLAYING;
}
repaint();
}
} else
unDoNum();
break;
case PLAYING:
if (button == MouseEvent.BUTTON1) {
if (!selected) {
iStepStore.add(this);
iTurn = PLAYER;
setSelected(true);
reDo(value);
if (iGameState == PLAYING)
BingoAI();
}
} else {
if (iStepStore.size() != 0)
unDo();
}
break;
default:
break;
}
}
}
public void mouseEntered(MouseEvent event) {
if (owner == PLAYER && iGameState != START)
setChoice(true);
}
public void mouseExited(MouseEvent event) {
if (owner == PLAYER && iGameState != START)
setChoice(false);
}
public void mouseClicked(MouseEvent event) {
}
public void mouseReleased(MouseEvent event) {
}
public Dimension getPreferredSize() {
return new Dimension(20, 20);
}
public Dimension getMinimumSize() {
return getPreferredSize();
}
public int getOwner() {
return owner;
}
public int getLocX() {
return x;
}
public int getLocY() {
return y;
}
public int getValue() {
return value;
}
public void Initial() {
value = owner == COMPUTER ? x * 5 + (y + 1) : 0;
choice = selected = connected = false;
for (int i = 0; i < direction.length; i++)
direction[i] = false;
repaint();
}
public void setValue(int new_value) {
value = new_value;
repaint();
}
public void setValue(Grid gird) {
value = gird.getValue();
repaint();
}
public void setChoice(boolean choice) {
this.choice = choice;
repaint();
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean select) {
selected = select;
if (select) {
iLocX = x;
iLocY = y;
WinCheck();
}
repaint();
}
public boolean isConnected() {
return connected;
}
public void setConnected(boolean connect) {
connected = connect;
repaint();
}
public void setConnectedLine(int direct, boolean set) {
direction[direct] = set;
if (!set) {
int dirs = direction.length;
for (int i = 0; i < direction.length; i++) {
if (direction[i])
break;
else
dirs--;
}
// it means there is no any connection line in a Grid,
// connect state should be set fasle
if (dirs == 0)
connected = false;
} else {
connected = set;
}
repaint();
}
public boolean[] getConnectedLines() {
return direction;
}
public void setBorder(boolean aBorder) {
if (aBorder)
setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED));
else
this.setBorder(null);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
// g.drawString(x+" "+y, 1, 10);
if (iGameState != START) {
g.drawRect(0, 0, 19, 19);
if (choice)
setBackground(Color.WHITE);
else if (connected)
setBackground(Color.RED);
else if (selected)
setBackground(Color.YELLOW);
else if (value != 0 && owner == PLAYER)
setBackground(Color.LIGHT_GRAY);
else
setBackground(Color.GRAY);
for (int i = 0; i < direction.length; i++) {
if (direction[i]) {
g.setColor(Color.WHITE);
switch (i) {
case OBLIQUE_1:
g.drawLine(0, 0, 20, 20);
break;
case OBLIQUE_2:
g.drawLine(20, 0, 0, 20);
break;
case HORIZONTAL:
g.drawLine(10, 0, 10, 20);
break;
case VERTICAL:
g.drawLine(0, 10, 20, 10);
break;
}
}
}
g.setColor(Color.BLACK);
if ((owner == PLAYER && value != 0) || (selected || connected))
g.drawString("" + value, (value >= 10 ? 4 : 7), 15);
}
}
}
}