package main import ( "math/rand/v2" "strconv" ) // The game config type Config struct { Width int Height int Mines int } // The game states type Game struct { // Game config stored for g.G() to check border Config *Config // 0b0000_0000 // &0b1000_0000 = Mine // &0b0100_0000 = Visible // &0b0011_0000 = Flag // &0b0000_1111 = Number Grids []uint8 // 0b0000_0000 == Gaming // 0b0000_0001 == Win // 0b0000_0010 == Loss Status uint8 } // New creates a new game, initialize mines and number func New(config *Config) *Game { // Panic when code goes wrong if config.Width <= 0 || config.Height <= 0 || config.Mines <= 0 { panic("width, height and mines should be larger than 0") } else if config.Mines > config.Width*config.Height { panic("grids should be larger than mines") } // Create a new game instance g := &Game{ Config: config, Grids: make([]uint8, config.Width*config.Height), } // Initialize mines for range config.Mines { x, y := rand.IntN(config.Width), rand.IntN(config.Height) for *g.G(x, y) != 0 { x, y = rand.IntN(config.Width), rand.IntN(config.Height) } *g.G(x, y) = 0b1000_0000 // Add numbers arounds the mine grid for _, a := range [][]int{{x - 1, y}, {x + 1, y}, {x, y - 1}, {x, y + 1}} { if p := g.G(a[0], a[1]); p != nil { *p += 1 } } } return g } // G returns a pointer to the destination grid by (x, y) coordinate, starts from left top func (g *Game) G(x, y int) *uint8 { if x < 0 || x >= g.Config.Width || y < 0 || y >= g.Config.Height { return nil } return &g.Grids[y*g.Config.Width+x] } // Click run a DFS algorithm for (x, y) coordinate to bring all empty grids visible func (g *Game) Click(x, y int) *Game { // Check if the coordinate is valid c := g.G(x, y) if c == nil { panic("invalid coordinate") } // Make the grid visible *c |= 0b0100_0000 // If the grid has number greater than 0, can go back if *c&0b0000_1111 != 0 { return g } // Click grids around the 0 number grid for _, a := range [][]int{{x - 1, y}, {x + 1, y}, {x, y - 1}, {x, y + 1}} { if p := g.G(a[0], a[1]); p != nil && *p&0b0100_0000 == 0 { g.Click(a[0], a[1]) } } return g } // SetAllVisible simply set all grids visible func (g *Game) SetAllVisible() *Game { for y := range g.Config.Height { for x := range g.Config.Width { *g.G(x, y) |= 0b0100_0000 } } return g } // Print will output the game grids into console func (g *Game) Print() *Game { s := "" for y := range g.Config.Height { for x := range g.Config.Width { if v := *g.G(x, y); v&0b0100_0000 != 0 { if v&0b1000_0000 != 0 { s += "*" } else if f := v & 0b0011_0000; f != 0 { if f == 0b0001_0000 { s += "X" } else if f == 0b0010_0000 { s += "O" } else if f == 0b0011_0000 { s += "?" } } else { s += strconv.Itoa(int(v & 0b0000_1111)) } } else { s += "." } } s += "\n" } print(s) return g } func main() { New(&Config{ Width: 10, Height: 10, Mines: 10, }).Click(1, 1).Print().SetAllVisible().Print() }