網路程式設計期末報告範例:賓果遊戲

網路程式設計期末報告範例:賓果遊戲

four bots

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程式賓果遊戲期末報告

下載檔案: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);
			}
		}
	}
}