Làm Game Pokemon Bằng Java - Mai Trời Sáng !

Nhìn chung code game này khá giống game Dò mìn mình đã viết. Các bạn nên xem trước để biết cách tổ chức code. Ở game này, mình sẽ bổ sung thêm xử lý đa luồng để đếm thời gian, cách hiển thị hình ảnh trong Java, và đương nhiên thuật toán của game Pokemon sẽ khác biệt game Dò mìn.

Phân tích bài toán :

Nếu 2 ô được nhấp giống nhau, cố gắng tìm đường nối 2 ô sao cho chỉ tối đa vẽ 3 nét để nối. Nếu tìm được thì triệt tiêu 2 ô được chọn đi. Nếu ko thì huỷ sự lựa chọn.

Nếu 2 ô được nhấp ko giống nhau hoặc nhấp lại vào chính ô đó, huỷ sự lựa chọn.

Game có đếm thời gian. Nếu hết thời gian mà số ô còn hiện trong bảng vẫn còn khác 0 thì đưa ra thông báo “Bạn thua”, và lựa chọn chơi ván mới hay thoát. Nếu chưa hết thời gian mà số ô hiện trong bảng bằng 0 thì đưa ra thông báo “Bạn thắng”, và lựa chọn chơi ván mới hay thoát.

Ngoài ra chúng ta thêm nút “Chơi lại” để chủ động chơi ván mới.

Thuật toán :

Thuật toán chính trong game Pokemon là làm sao tìm đường nối giữa 2 ô chọn mà chỉ được tối đa 3 nét nối. Giải thuật dưới đây là do mình nghĩ ra, chưa qua tham khảo nên ko biết còn cách nào tối ưu ko, mong nhận sự đóng góp của các bạn.

Xét trường hợp đường nối giữa nằm dọc. Chúng ta xác định không gian theo chiều ngang của mỗi điểm chọn mà cho phép chúng ta vẽ được đường nối ngang (tức là cứ chạy biến con chạy trái, phải cho đến khi nào gặp ô có thuộc tính “isDroped” bằng false thì dừng). Xem 2 miền không gian của 2 điểm có phần nào chung không. Nếu có phần chung thì duyệt theo chiều dọc để xem có vẽ tiếp được đường nối dọc không.

Trường hợp đường nối giữa nằm ngang tương tự như vậy.

Sourcecode :

Cấu trúc dự án:

| src| — gui| — | — panel| — | — | — BoardPanel.java| — | — | — ControlPanel.java| — | — Gui.java| — | — ICommon.java| — | — ITransBoard.java| — | — ITransControl.java| — logic| — | — Board.java| — | — Square.java| — main| — | — Main.java| resources

“resource” là thư mục chứa các file ảnh pokemon. Các bạn tải ảnh từ trang pokemon wiki ấy. Sau đó giảm kích thước ảnh đi cho đỡ nặng (để kích thước 48×48 thôi).

package logic; import java.io.File; public class Square { private boolean isDroped; private File value; public Square() { isDroped = true; } public boolean isDroped() { return isDroped; } public void setDroped(boolean isDroped) { this.isDroped = isDroped; } public File getValue() { return value; } public void setValue(File value) { this.value = value; } } package logic; import java.io.File; import java.util.ArrayList; import java.util.Collections; public class Board { public static final int NUM_ROWS = 12; public static final int NUM_COLUMNS = 18; private ArrayList<File> listPokemon; private Square[][] listSquare; public Board() { listSquare = new Square[NUM_ROWS][NUM_COLUMNS]; for (int i = 0; i < listSquare.length; i++) { for (int j = 0; j < listSquare[0].length; j++) { listSquare[i][j] = new Square(); } } // tạo danh sách các pokemon File gallery = new File("resources"); File[] images = gallery.listFiles(); listPokemon = new ArrayList<File>(); if (images.length >= (NUM_ROWS - 2) * (NUM_COLUMNS - 2) / 8) { for (int i = 0; i < (NUM_ROWS - 2) * (NUM_COLUMNS - 2) / 8; i++) { for (int j = 0; j < 8; j++) { listPokemon.add(images[i]); } } } else { System.out.println("Khong du so anh"); } initBoard(); } public Square[][] getListSquare() { return listSquare; } public void setListSquare(Square[][] list) { listSquare = list; } private void initBoard() { int index = 0; Collections.shuffle(listPokemon); for (int i = 0; i < listSquare.length; i++) { for (int j = 0; j < listSquare[0].length; j++) { if (i > 0 && i < NUM_ROWS - 1 && j > 0 && j < NUM_COLUMNS - 1) { listSquare[i][j].setDroped(false); listSquare[i][j].setValue(listPokemon.get(index)); index++; } } } } public boolean checkMatching(int x0, int y0, int x1, int y1) { if (listSquare[x0][y0].getValue().equals(listSquare[x1][y1].getValue())) { // đường nối giữa nằm ngang // lấy dải các ô trên dưới của điểm 0 có thể tạo đường nối int top0 = x0, bot0 = x0; while (bot0 - 1 >= 0) { if (listSquare[bot0 - 1][y0].isDroped()) { bot0--; } else { break; } } while (top0 + 1 <= NUM_ROWS - 1) { if (listSquare[top0 + 1][y0].isDroped()) { top0++; } else { break; } } // lấy dải các ô trên dưới của điểm 1 có thể tạo đường nối int top1 = x1, bot1 = x1; while (bot1 - 1 >= 0) { if (listSquare[bot1 - 1][y1].isDroped()) { bot1--; } else { break; } } while (top1 + 1 <= NUM_ROWS - 1) { if (listSquare[top1 + 1][y1].isDroped()) { top1++; } else { break; } } // lấy dải tương giao của 2 đường nối dọc int minTop = top0 >= top1 ? top1 : top0; int maxBot = bot0 >= bot1 ? bot0 : bot1; int minY = y0 >= y1 ? y1 : y0; int maxY = y0 >= y1 ? y0 : y1; for (int i = maxBot; i <= minTop; i++) { // kiểm tra xem có thể tìm đường nối ngang ko int count = 0; for (int j = minY + 1; j < maxY; j++) { if (listSquare[i][j].isDroped()) { count++; } else { break; } } if (count == maxY - minY - 1) { listSquare[x0][y0].setDroped(true); listSquare[x1][y1].setDroped(true); return true; } } // đường nối giữa nằm dọc int left0 = y0, right0 = y0; while (left0 - 1 >= 0) { if (listSquare[x0][left0 - 1].isDroped()) { left0--; } else { break; } } while (right0 + 1 <= NUM_COLUMNS - 1) { if (listSquare[x0][right0 + 1].isDroped()) { right0++; } else { break; } } int left1 = y1, right1 = y1; while (left1 - 1 >= 0) { if (listSquare[x1][left1 - 1].isDroped()) { left1--; } else { break; } } while (right1 + 1 <= NUM_COLUMNS - 1) { if (listSquare[x1][right1 + 1].isDroped()) { right1++; } else { break; } } int minRight = right0 >= right1 ? right1 : right0; int maxLeft = left0 >= left1 ? left0 : left1; int minX = x0 >= x1 ? x1 : x0; int maxX = x0 >= x1 ? x0 : x1; for (int i = maxLeft; i <= minRight; i++) { int count = 0; for (int j = minX + 1; j < maxX; j++) { if (listSquare[j][i].isDroped()) { count++; } else { break; } } if (count == maxX - minX - 1) { listSquare[x0][y0].setDroped(true); listSquare[x1][y1].setDroped(true); return true; } } } return false; } } package gui; public interface ICommon { void initComp(); void addComp(); void addEvent(); } package gui; import logic.Square; public interface ITransBoard { Square[][] getListSquare(); void setListSquare(Square[][] list); boolean checkMatching(int x0, int y0, int x1, int y1); } package gui; public interface ITransControl { void renewBoardPanel(); void showDialog(boolean checkWin); int getNumSquareIsShowed(); } package gui.panel; import gui.Gui; import gui.ICommon; import gui.ITransBoard; import logic.Board; import logic.Square; import java.awt.Color; import java.awt.GridLayout; import java.awt.Image; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.IOException; import javax.swing.JPanel; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.border.LineBorder; public class BoardPanel extends JPanel implements ICommon { private static final long serialVersionUID = -4333819455965695569L; private ITransBoard listener; private Label[] selectedSlot; private static final LineBorder NORMAL_BORDER = new LineBorder(Color.gray, 1); private static final LineBorder SELECTED_BORDER = new LineBorder(Color.red, 2); private Label[][] lbSquare; public BoardPanel() { selectedSlot = new Label[2]; initComp(); addComp(); addEvent(); } @Override public void initComp() { setSize(Gui.WIDTH - 30, Gui.HEIGHT - 120); setLocation(10, 70); setBackground(Color.gray); setLayout(new GridLayout(Board.NUM_ROWS, Board.NUM_COLUMNS)); lbSquare = new Label[Board.NUM_ROWS][Board.NUM_COLUMNS]; } @Override public void addComp() { for (int i = 0; i < Board.NUM_ROWS; i++) { for (int j = 0; j < Board.NUM_COLUMNS; j++) { lbSquare[i][j] = new Label(); add(lbSquare[i][j]); } } } @Override public void addEvent() { MouseListener ml = new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { Label label = (Label) e.getComponent(); // them label vao danh sach cac label duoc chon if (selectedSlot[0] == null) { label.setBorder(SELECTED_BORDER); selectedSlot[0] = label; } else if (selectedSlot[0] == label) { label.setBorder(NORMAL_BORDER); selectedSlot[0] = null; } else { label.setBorder(SELECTED_BORDER); selectedSlot[1] = label; // kiem tra ghep doi int x0 = selectedSlot[0].x; int y0 = selectedSlot[0].y; int x1 = selectedSlot[1].x; int y1 = selectedSlot[1].y; boolean check = listener.checkMatching(x0, y0, x1, y1); if (check) { lbSquare[x0][y0].removeMouseListener(lbSquare[x0][y0].ml); lbSquare[x1][y1].removeMouseListener(lbSquare[x1][y1].ml); } updateBoard(); selectedSlot[0] = null; selectedSlot[1] = null; } } }; for (int i = 1; i < Board.NUM_ROWS - 1; i++) { for (int j = 1; j < Board.NUM_COLUMNS - 1; j++) { lbSquare[i][j].x = i; lbSquare[i][j].y = j; lbSquare[i][j].ml = ml; lbSquare[i][j].addMouseListener(ml); } } } public void addListener(ITransBoard event) { listener = event; } public void updateBoard() { Square[][] listSquare = listener.getListSquare(); for (int i = 0; i < listSquare.length; i++) { for (int j = 0; j < listSquare[0].length; j++) { if (!listSquare[i][j].isDroped()) { lbSquare[i][j].setBorder(NORMAL_BORDER); } else { lbSquare[i][j].setBorder(null); lbSquare[i][j].setIcon(null); lbSquare[i][j].setOpaque(false); } } } } public void initBoard() { Square[][] listSquare = listener.getListSquare(); for (int i = 0; i < listSquare.length; i++) { for (int j = 0; j < listSquare[0].length; j++) { if (!listSquare[i][j].isDroped()) { lbSquare[i][j].setBorder(NORMAL_BORDER); lbSquare[i][j].setOpaque(true); lbSquare[i][j].setBackground(Color.lightGray); lbSquare[i][j].setHorizontalAlignment(JLabel.CENTER); lbSquare[i][j].setVerticalAlignment(JLabel.CENTER); try { Image image = ImageIO.read(listSquare[i][j].getValue()); int newWidth = this.getWidth() / Board.NUM_COLUMNS; int newHeight = this.getHeight() / Board.NUM_ROWS; image = image.getScaledInstance(newWidth, newHeight, Image.SCALE_SMOOTH); ImageIcon icon = new ImageIcon(image); lbSquare[i][j].setIcon(icon); } catch (IOException e) { e.printStackTrace(); } } } } } private class Label extends JLabel { private static final long serialVersionUID = 6825410015958932243L; private int x; private int y; private MouseListener ml; } } package gui; import java.awt.Color; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.UIManager; import javax.swing.border.LineBorder; import gui.panel.BoardPanel; import gui.panel.ControlPanel; import logic.Board; import logic.Square; public class Gui extends JFrame implements ICommon, ITransBoard, ITransControl { private static final long serialVersionUID = -5479701518838741039L; private static final String TITLE = "Pokemon"; public static final int WIDTH = 700; public static final int HEIGHT = 540; public static final int TIME = 600; public static final boolean WIN = true; public static final boolean LOSE = false; private Board board; private int numSquareIsShowed = (Board.NUM_ROWS - 2) * (Board.NUM_COLUMNS - 2); private BoardPanel boardPanel; private ControlPanel controlPanel; public Gui() { board = new Board(); initComp(); addComp(); addEvent(); } @Override public void initComp() { setTitle(TITLE); setSize(WIDTH, HEIGHT); setLocationRelativeTo(null); setResizable(false); setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); setLayout(null); try { UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel"); } catch (Exception e) { e.printStackTrace(); } } @Override public void addComp() { addBoardPanel(); controlPanel = new ControlPanel(); add(controlPanel); controlPanel.addListener(this); } @Override public void addEvent() { WindowListener wd = new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { int kq = JOptionPane.showConfirmDialog(Gui.this, "Bạn có muốn thoát không?", "Thông báo", JOptionPane.YES_NO_OPTION); if (kq == JOptionPane.YES_OPTION) { System.exit(0); } } }; addWindowListener(wd); } @Override public Square[][] getListSquare() { return board.getListSquare(); } @Override public void setListSquare(Square[][] list) { board.setListSquare(list); } @Override public boolean checkMatching(int x0, int y0, int x1, int y1) { boolean check = board.checkMatching(x0, y0, x1, y1); if (check) { numSquareIsShowed = getNumSquareIsShowed() - 2; } return check; } @Override public void renewBoardPanel() { board = new Board(); remove(boardPanel); addBoardPanel(); } private void addBoardPanel() { boardPanel = new BoardPanel(); boardPanel.setBorder(new LineBorder(Color.gray, 2)); add(boardPanel); boardPanel.addListener(this); boardPanel.initBoard(); } @Override public void showDialog(boolean checkWin) { int kq; if (checkWin) { kq = JOptionPane.showConfirmDialog(Gui.this, "Bạn thắng!\n Bạn có muốn chơi lại không?", "Thông báo", JOptionPane.YES_NO_OPTION); } else { kq = JOptionPane.showConfirmDialog(Gui.this, "Bạn thua!\n Bạn có muốn chơi lại không?", "Thông báo", JOptionPane.YES_NO_OPTION); } if (kq == JOptionPane.YES_OPTION) { numSquareIsShowed = (Board.NUM_ROWS - 2) * (Board.NUM_COLUMNS - 2); controlPanel.restart(); } else if (kq == JOptionPane.NO_OPTION) { System.exit(0); } } @Override public int getNumSquareIsShowed() { return numSquareIsShowed; } } package gui.panel; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JProgressBar; import gui.Gui; import gui.ICommon; import gui.ITransControl; public class ControlPanel extends JPanel implements ICommon { private static final long serialVersionUID = 7494278874396803784L; private ITransControl listener; private Thread threadCountDown; private JButton btnRestart; private JProgressBar prgCountDown; public ControlPanel() { initComp(); addComp(); addEvent(); } @Override public void initComp() { setSize(Gui.WIDTH - 30, 50); setLocation(10, 10); setLayout(null); } @Override public void addComp() { btnRestart = new JButton("Choi lai"); btnRestart.setBounds(10, 10, 100, 30); add(btnRestart); prgCountDown = new JProgressBar(); prgCountDown.setMaximum(Gui.TIME); prgCountDown.setMinimum(0); prgCountDown.setBounds(150, 10, 500, 30); add(prgCountDown); } @Override public void addEvent() { threadCountDown = new Thread(runCountDown); threadCountDown.start(); btnRestart.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { restart(); } }); } public void addListener(ITransControl event) { listener = event; } private Runnable runCountDown = new Runnable() { @Override public void run() { try { int time = Gui.TIME; prgCountDown.setValue(time); while (time >= 0) { Thread.sleep(1000); prgCountDown.setValue(time--); // nếu chơi hết ô thì hiện dialog thắng và tạm dừng thread if (listener.getNumSquareIsShowed() == 0) { listener.showDialog(Gui.WIN); wait(); } } // hết giờ thì hiện dialog thua listener.showDialog(Gui.LOSE); } catch (InterruptedException e) { e.printStackTrace(); } } }; // khởi tạo lại bàn cờ, ngắt thread cũ và khởi tạo thread mới public void restart() { listener.renewBoardPanel(); try { threadCountDown.interrupt(); } catch (Exception e2) { e2.printStackTrace(); } threadCountDown = new Thread(runCountDown); threadCountDown.start(); } } package main; import gui.Gui; public class Main { public static void main(String[] args) { Gui gui = new Gui(); gui.setVisible(true); } }

Chia sẻ:

  • Twitter
  • Facebook
Thích Đang tải...

Có liên quan

Từ khóa » Thuật Toán Trong Game Pikachu