数据结构和算法-Go泛型实现:第四章ADT 实践:生命游戏- 2.GUI 实现

132 阅读3分钟

第四章:ADT 实践:生命游戏

2. GUI 实现生命游戏

在本节中,我们将详细介绍如何使用 Fyne 框架在 GUI 中实现和运行生命游戏。这个实现将基于之前的网格和细胞状态定义,并通过 GUI 界面来展示网格的演变过程。

步骤1:创建 go.mod 文件

首先,我们需要创建一个 go.mod 文件来初始化 Go 模块并添加 Fyne 依赖项。

go mod init project
go get fyne.io/fyne/v2
步骤2:定义网格和细胞状态

与之前的实现类似,定义网格和细胞的状态。

package life

// CellState 表示细胞的状态
type CellState int

const (
    Dead CellState = iota
    Alive
)

// Grid 表示生命游戏的网格
type Grid struct {
    Cells [][]CellState
    Rows  int
    Cols  int
}

// NewGrid 创建一个新的网格
func NewGrid(rows, cols int) *Grid {
    cells := make([][]CellState, rows)
    for i := range cells {
        cells[i] = make([]CellState, cols)
    }
    return &Grid{
        Cells: cells,
        Rows:  rows,
        Cols:  cols,
    }
}

// Init 随机初始化网格的细胞状态
func (g *Grid) Init() {
    for i := 0; i < g.Rows; i++ {
        for j := 0; j < g.Cols; j++ {
            if rand.Float64() < 0.5 {
                g.Cells[i][j] = Alive
            } else {
                g.Cells[i][j] = Dead
            }
        }
    }
}
步骤3:细胞演化规则

实现根据生命游戏规则更新网格的功能。

package life

// NextState 计算下一代的网格状态
func (g *Grid) NextState() {
    newCells := make([][]CellState, g.Rows)
    for i := range newCells {
        newCells[i] = make([]CellState, g.Cols)
        for j := range newCells[i] {
            newCells[i][j] = g.nextCellState(i, j)
        }
    }
    g.Cells = newCells
}

// nextCellState 计算给定细胞的下一状态
func (g *Grid) nextCellState(row, col int) CellState {
    liveNeighbors := g.countLiveNeighbors(row, col)
    if g.Cells[row][col] == Alive {
        if liveNeighbors < 2 || liveNeighbors > 3 {
            return Dead
        }
        return Alive
    }
    if liveNeighbors == 3 {
        return Alive
    }
    return Dead
}

// countLiveNeighbors 计算给定细胞周围的活细胞数量
func (g *Grid) countLiveNeighbors(row, col int) int {
    count := 0
    for i := row - 1; i <= row + 1; i++ {
        for j := col - 1; j <= col + 1; j++ {
            if (i != row || j != col) && g.isAlive(i, j) {
                count++
            }
        }
    }
    return count
}

// isAlive 检查给定位置的细胞是否存活
func (g *Grid) isAlive(row, col int) bool {
    if row < 0 || row >= g.Rows || col < 0 || col >= g.Cols {
        return false
    }
    return g.Cells[row][col] == Alive
}
步骤4:创建 GUI 界面

使用 Fyne 框架创建 GUI 界面,并在其中展示生命游戏的网格。

package main

import (
    "fyne.io/fyne/v2/app"
    "fyne.io/fyne/v2/canvas"
    "fyne.io/fyne/v2/container"
    "fyne.io/fyne/v2/widget"
    "project/life"
    "time"
)

const (
    gridSize = 20
    cellSize = 20
)

func main() {
    a := app.New()
    w := a.NewWindow("Conway's Game of Life")

    grid := life.NewGrid(gridSize, gridSize)
    grid.Init()

    cells := make([]*canvas.Rectangle, gridSize*gridSize)
    for i := range cells {
        cells[i] = canvas.NewRectangle(nil)
    }

    updateGrid := func() {
        for i := 0; i < gridSize; i++ {
            for j := 0; j < gridSize; j++ {
                rect := cells[i*gridSize+j]
                if grid.Cells[i][j] == life.Alive {
                    rect.FillColor = color.RGBA{0, 255, 0, 255}
                } else {
                    rect.FillColor = color.RGBA{255, 255, 255, 255}
                }
                rect.Refresh()
            }
        }
    }

    go func() {
        for {
            time.Sleep(500 * time.Millisecond)
            grid.NextState()
            updateGrid()
        }
    }()

    cellContainer := container.NewGridWithColumns(gridSize, cells...)
    w.SetContent(container.NewVBox(
        cellContainer,
        widget.NewButton("Start", func() {
            go func() {
                for {
                    time.Sleep(500 * time.Millisecond)
                    grid.NextState()
                    updateGrid()
                }
            }()
        }),
    ))

    w.Resize(fyne.NewSize(gridSize*cellSize, (gridSize+1)*cellSize))
    w.ShowAndRun()
}
解释

在这个实现中,我们使用 Fyne 框架来创建一个 GUI 界面,并在其中展示生命游戏的网格。我们首先创建并初始化了网格,然后通过创建一系列矩形来表示细胞状态。我们实现了一个 updateGrid 函数,用于根据细胞状态更新矩形的颜色。

在主程序中,我们设置了一个定时器,每隔 500 毫秒更新一次网格状态并调用 updateGrid 来刷新 GUI 界面。通过这种方式,我们可以在 GUI 界面中看到生命游戏的动态演化过程。

这种实现展示了如何使用 Go 语言和 Fyne 框架来创建一个动态的 GUI 应用程序,并展示复杂系统的演化过程。