使用Java實現(xiàn)生命游戲串行代碼示例
生命游戲介紹
生命游戲,是英國數(shù)學(xué)家約翰·何頓·康威在1970年發(fā)明的細(xì)胞自動機(jī)。
一個方格游戲棋盤上,每個方格中都可放置一個生命細(xì)胞,每個生命細(xì)胞只有兩種狀態(tài):“生”或“死”(狀態(tài)往往隨機(jī)決定)。然后細(xì)胞根據(jù)某些規(guī)則,計算出下一代每個細(xì)胞的狀態(tài),并且不停迭代。
在游戲的進(jìn)行中,雜亂無序的細(xì)胞會逐漸演化出各種精致、有形的結(jié)構(gòu);這些結(jié)構(gòu)往往有很好的對稱性,而且每一代都在變化形狀。一些形狀已經(jīng)鎖定,不會逐代變化。有時,一些已經(jīng)成形的結(jié)構(gòu)會因為一些無序細(xì)胞的“入侵”而被破壞。但是形狀和秩序經(jīng)常能從雜亂中產(chǎn)生出來。
現(xiàn)設(shè)定其規(guī)則是:
- 如果一個細(xì)胞周圍有3個細(xì)胞為生(一個細(xì)胞周圍共有8個細(xì)胞),則該細(xì)胞為生(即該細(xì)胞若原先為死,則轉(zhuǎn)為生,若原先為生,則保持不變) 。
- 如果一個細(xì)胞周圍有2個細(xì)胞為生,則該細(xì)胞的生死狀態(tài)保持不變;
- 在其它情況下,該細(xì)胞為死(即該細(xì)胞若原先為生,則轉(zhuǎn)為死,若原先為死,則保持不變)
下面就此規(guī)則,使用java來編寫100*100的棋盤,并使用簡單的JavaFX來實現(xiàn)簡易圖形化來觀察游戲的迭代過程。
一、效果展示
1.初始界面
100*100的棋盤上隨機(jī)生成“生”和“死”細(xì)胞(黑為生,白為死)。底部有“運行”和“暫停”按鈕。
2.啟動游戲
二、代碼實現(xiàn)
package com.itheima.gameOfLife; import javafx.animation.AnimationTimer; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.BorderPane; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.shape.Rectangle; import javafx.stage.Stage; import java.util.Random; public class GameOfLife extends Application { private static final int GRID_SIZE = 100; private static final int CELL_SIZE = 10; // 每個細(xì)胞的像素大小 private static final int GENERATIONS = 1000; // 演化代數(shù) private int[][] grid = new int[GRID_SIZE][GRID_SIZE]; // 當(dāng)前代棋盤 private int[][] nextGrid = new int[GRID_SIZE][GRID_SIZE]; // 下一代棋盤 private Pane pane = new Pane(); private AnimationTimer timer; private boolean isRunning = false; // 運行狀態(tài)標(biāo)志 public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { initializeGrid(); // 隨機(jī)初始化棋盤 // 創(chuàng)建JavaFX界面 BorderPane root = new BorderPane(); Scene scene = new Scene(root, GRID_SIZE * CELL_SIZE, GRID_SIZE * CELL_SIZE + 50); primaryStage.setTitle("生命游戲"); primaryStage.setScene(scene); // 創(chuàng)建按鈕,便于停止觀察當(dāng)前棋盤狀態(tài) Button startButton = new Button("運行"); Button pauseButton = new Button("暫停"); startButton.setOnAction(e -> startGame()); pauseButton.setOnAction(e -> pauseGame()); // 設(shè)置按鈕位置 root.setBottom(new Pane(startButton, pauseButton)); Pane buttonPane = new Pane(); buttonPane.getChildren().addAll(startButton, pauseButton); buttonPane.setPrefSize(GRID_SIZE * CELL_SIZE, 50); buttonPane.setLayoutY(GRID_SIZE * CELL_SIZE); startButton.setLayoutX(20); pauseButton.setLayoutX(80); root.setCenter(pane); root.setBottom(buttonPane); primaryStage.show(); drawGrid(); // 繪制初始棋盤 // 使用AnimationTimer更新棋盤 timer = new AnimationTimer() { private int generationCount = 0; @Override public void handle(long now) { if (isRunning && generationCount < GENERATIONS) { runGeneration(); // 運行一代 drawGrid(); // 更新繪制 generationCount++; } else if (generationCount >= GENERATIONS) { stop(); // 結(jié)束動畫 } } }; } // 隨機(jī)初始化棋盤 private void initializeGrid() { Random random = new Random(); for (int i = 0; i < GRID_SIZE; i++) { for (int j = 0; j < GRID_SIZE; j++) { grid[i][j] = random.nextInt(2); // 隨機(jī)生成0或1 } } } // 啟動 private void startGame() { if (!isRunning) { isRunning = true; // 設(shè)置為運行狀態(tài) timer.start(); // 啟動計時器 } } // 暫停 private void pauseGame() { isRunning = false; // 設(shè)置為暫停狀態(tài) timer.stop(); // 停止計時器 } // 運行一代 private void runGeneration() { for (int i = 0; i < GRID_SIZE; i++) { for (int j = 0; j < GRID_SIZE; j++) { int liveNeighbors = countLiveNeighbors(i, j); if (grid[i][j] == 1) { // 當(dāng)前細(xì)胞是活的 nextGrid[i][j] = (liveNeighbors == 2 || liveNeighbors == 3) ? 1 : 0; } else { // 當(dāng)前細(xì)胞是死的 nextGrid[i][j] = (liveNeighbors == 3) ? 1 : 0; } } } copyNextGridToCurrent(); } // 計算鄰居活細(xì)胞數(shù)量 private int countLiveNeighbors(int row, int col) { int liveNeighbors = 0; for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (i == 0 && j == 0) continue; // 跳過自身 int newRow = (row + i + GRID_SIZE) % GRID_SIZE; // 考慮邊界 int newCol = (col + j + GRID_SIZE) % GRID_SIZE; liveNeighbors += grid[newRow][newCol]; } } return liveNeighbors; } // 將nextGrid復(fù)制到grid private void copyNextGridToCurrent() { for (int i = 0; i < GRID_SIZE; i++) { System.arraycopy(nextGrid[i], 0, grid[i], 0, GRID_SIZE); } } // 繪制當(dāng)前棋盤狀態(tài) private void drawGrid() { pane.getChildren().clear(); // 清除舊的圖形 for (int i = 0; i < GRID_SIZE; i++) { for (int j = 0; j < GRID_SIZE; j++) { Rectangle cell = new Rectangle(j * CELL_SIZE, i * CELL_SIZE, CELL_SIZE, CELL_SIZE); cell.setFill(grid[i][j] == 1 ? Color.BLACK : Color.WHITE); // 黑色表示活細(xì)胞,白色表示死細(xì)胞 cell.setStroke(Color.GRAY); //細(xì)胞之間的邊框 pane.getChildren().add(cell); // 將細(xì)胞添加到面板 } } } }
三、代碼解釋
1.常量設(shè)置
private static final int GRID_SIZE = 100; private static final int CELL_SIZE = 10; // 每個細(xì)胞的像素大小 private static final int GENERATIONS = 1000; // 演化代數(shù) private int[][] grid = new int[GRID_SIZE][GRID_SIZE]; // 當(dāng)前代棋盤 private int[][] nextGrid = new int[GRID_SIZE][GRID_SIZE]; // 下一代棋盤 private Pane pane = new Pane(); private AnimationTimer timer; private boolean isRunning = false; // 運行狀態(tài)標(biāo)志
GRID_SIZE: 定義了棋盤的大小,當(dāng)前設(shè)置為 100
。即棋盤是一個 100 x 100
的格子。
CELL_SIZE: 定義了每個細(xì)胞的像素大小,當(dāng)前設(shè)置為 10
。因此,每個細(xì)胞在界面上將顯示為一個 10 x 10
像素的正方形。
棋盤總大小: 因此,整個棋盤的實際像素大小為:
- 寬度:
GRID_SIZE * CELL_SIZE = 100 * 10 = 1000
像素 - 高度:
GRID_SIZE * CELL_SIZE = 100 * 10 = 1000
像素
2.圖形化
// 創(chuàng)建JavaFX界面 BorderPane root = new BorderPane(); Scene scene = new Scene(root, GRID_SIZE * CELL_SIZE, GRID_SIZE * CELL_SIZE + 50); primaryStage.setTitle("生命游戲"); primaryStage.setScene(scene); // 創(chuàng)建按鈕 Button startButton = new Button("運行"); Button pauseButton = new Button("暫停"); startButton.setOnAction(e -> startGame()); pauseButton.setOnAction(e -> pauseGame()); // 將按鈕放置在界面底部 root.setBottom(new Pane(startButton, pauseButton)); Pane buttonPane = new Pane(); buttonPane.getChildren().addAll(startButton, pauseButton); buttonPane.setPrefSize(GRID_SIZE * CELL_SIZE, 50); buttonPane.setLayoutY(GRID_SIZE * CELL_SIZE); startButton.setLayoutX(20); pauseButton.setLayoutX(80); root.setCenter(pane); root.setBottom(buttonPane); primaryStage.show(); drawGrid(); // 繪制初始棋盤 // 使用AnimationTimer更新棋盤 timer = new AnimationTimer() { private int generationCount = 0; @Override public void handle(long now) { if (isRunning && generationCount < GENERATIONS) { runGeneration(); // 運行一代 drawGrid(); // 更新繪制 generationCount++; } else if (generationCount >= GENERATIONS) { stop(); // 結(jié)束動畫 } } }; }
上面這段代碼是JavaFX的核心部分,創(chuàng)建棋盤界面和處理游戲邏輯。部分解釋已經(jīng)在代碼中注釋出來。
在“使用AnimationTimer更新棋盤”這部分中,創(chuàng)建了一個 AnimationTimer
對象,并重寫其handle方法,用于定時更新棋盤狀態(tài)。
3.計算“生死”情況與統(tǒng)計鄰居細(xì)胞數(shù)量
//根據(jù)規(guī)則判斷生死 private void runGeneration() { for (int i = 0; i < GRID_SIZE; i++) { for (int j = 0; j < GRID_SIZE; j++) { int liveNeighbors = countLiveNeighbors(i, j); if (grid[i][j] == 1) { // 當(dāng)前細(xì)胞是活的 nextGrid[i][j] = (liveNeighbors == 2 || liveNeighbors == 3) ? 1 : 0; } else { // 當(dāng)前細(xì)胞是死的 nextGrid[i][j] = (liveNeighbors == 3) ? 1 : 0; } } } copyNextGridToCurrent(); } // 計算鄰居活細(xì)胞數(shù)量 private int countLiveNeighbors(int row, int col) { int liveNeighbors = 0; for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (i == 0 && j == 0) continue; // 跳過自身 int newRow = (row + i + GRID_SIZE) % GRID_SIZE; // 考慮邊界 int newCol = (col + j + GRID_SIZE) % GRID_SIZE; liveNeighbors += grid[newRow][newCol]; } } return liveNeighbors; }
比較簡單的代碼,使用最基本的雙重循環(huán)來遍歷整個棋盤的細(xì)胞,得到其鄰居的數(shù)量用于判斷下一次迭代的狀態(tài)。
在“計算鄰居活細(xì)胞數(shù)量”中,用取模運算來處理邊界情況,使得棋盤具有環(huán)繞效果。例如,如果當(dāng)前細(xì)胞在第一行,且要檢查上方的細(xì)胞,則通過取模確保索引循環(huán)回到棋盤的底部。
四、結(jié)語
以上主要淺顯的實現(xiàn)了生命游戲的基本邏輯,并用簡單的圖形可視化來創(chuàng)建一個直觀的界面來觀察細(xì)胞的迭代,更直接的觀察到由簡單規(guī)則產(chǎn)生的復(fù)雜動態(tài)行為。在這個串行的生命游戲基礎(chǔ)上,后續(xù)可以改寫為多線程的生命游戲,并通過一些方法來比對串行與并行代碼的效率。
到此這篇關(guān)于使用Java實現(xiàn)生命游戲串行的文章就介紹到這了,更多相關(guān)Java生命游戲串行代碼內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatisplus邏輯刪除基本實現(xiàn)和坑點解決
這篇文章主要介紹了mybatisplus邏輯刪除基本實現(xiàn)和坑點解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03SpringBoot整合ElasticSearch的示例代碼
本篇文章主要介紹了SpringBoot整合ElasticSearch的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-09-09基于SpringBoot和Vue3的博客平臺發(fā)布、編輯、刪除文章功能實現(xiàn)
在上一個教程中,我們已經(jīng)實現(xiàn)了基于Spring?Boot和Vue3的用戶注冊與登錄功能。本教程將繼續(xù)引導(dǎo)您實現(xiàn)博客平臺的發(fā)布、編輯、刪除文章功能,需要的朋友參考一下2023-04-04java注解處理器學(xué)習(xí)在編譯期修改語法樹教程
這篇文章主要為大家介紹了java注解處理器學(xué)習(xí)在編譯期修改語法樹教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09淺析Spring?Cloud?Gateway中的令牌桶限流算法
這篇文章主要為大家淺析了Spring?Cloud?Gateway中的令牌桶限流算法原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02SpringBoot參數(shù)校驗Validator框架詳解
Validator框架就是為了解決開發(fā)人員在開發(fā)的時候少寫代碼,提升開發(fā)效率,Validator專門用來進(jìn)行接口參數(shù)校驗,今天通過本文給大家介紹SpringBoot參數(shù)校驗Validator框架,感興趣的朋友一起看看吧2022-06-06Java多線程之synchronized關(guān)鍵字的使用
這篇文章主要介紹了Java多線程之synchronized關(guān)鍵字的使用,文中有非常詳細(xì)的代碼示例,對正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下2021-04-04