控制台版扫雷,就是看起来费劲一点,和正常的扫雷木有区别,支持鼠标左、中、右键操作

jan-bar · · 823 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

[码云地址,可以直接下载可执行程序](https://gitee.com/janbar/minesweeping/tree/master/consoleMinesweeping) ![mine.jpg](https://static.studygolang.com/191017/9d9a9db1f90898da89f7fb77fc3bb524.jpg) ```go package main import ( "fmt" "math/rand" "time" "github.com/jan-bar/golibs" "github.com/lxn/win" ) func main() { var ( mousePt1, mousePt2 win.POINT mouseSta int32 x, y, sta int ) m := NewMinesweeping() startFlag: m.Clear() for m.mine > 0 { m.Print(false) if m.api.GetCursorPos(&mousePt1, &mousePt2, &mouseSta) { x, y, sta = m.CheckClickPos(mousePt1) if sta < 0 { continue } switch mouseSta { case golibs.MouseLeft: if !m.HandleLeft(x, y) { goto gameEnd } case golibs.MouseMid: if !m.HandleMid(x, y) { goto gameEnd } case golibs.MouseRight: m.HandleRight(x, y) } } } gameEnd: m.Print(true) for { if m.api.GetCursorPos(&mousePt1, &mousePt2, &mouseSta) { _, _, sta = m.CheckClickPos(mousePt1) if sta == ClickY { m.api.Clear() goto startFlag } else if sta == ClickN { break } } } } type GridDefine int /* 定义格子内容的类型,不直接用int避免使用时混乱 */ const ( /* 枚举类型,标记格子内容 */ DefZero GridDefine = iota // 0 DefOne // 1 DefTwo // 2 DefThree // 3 DefFour // 4 DefFive // 5 DefSix // 6 DefSeven // 7 DefEight // 8 DefMine // 地雷 DefFlag // 旗子 ) const ( DefShowPos = 0x80 // 该位表示改点能否显示 DefFlagPos = 0x40 // 该位表示改点是否标旗 DefMaskPos = 0x0F // 改点具体值 ) type minesweeping struct { width, height, mine int // 宽,高,雷数 api *golibs.Win32Api gridSave [][]GridDefine } func NewMinesweeping() *minesweeping { rand.Seed(time.Now().UnixNano()) m := &minesweeping{width: 30, height: 16, api: golibs.NewWin32Api()} m.api.SetWindowText("扫雷") m.api.CenterWindowOnScreen(1020, 600) m.api.ShowScrollBar(golibs.SB_BOTH, 0) mode := golibs.EnableMouseInput | golibs.EnableWindowInput | golibs.EnableExtendedFlags m.api.SetConsoleMode(mode) // 让控制台只能鼠标操作 m.api.ShowHideCursor(false) m.gridSave = make([][]GridDefine, m.height) for i := 0; i < m.height; i++ { m.gridSave[i] = make([]GridDefine, m.width) } return m } const ( ClickErr = -1 ClickY = -2 ClickN = -3 ) func (m *minesweeping) CheckClickPos(pos win.POINT) (int, int, int) { y, x := int(pos.X-4), int(pos.Y-8) if x < 0 || y < 0 { return 0, 0, ClickErr // 超过界限 } x /= 32 y /= 32 if x == m.height { if y == 0 { return 0, 0, ClickY // 点击Y } else if y == 1 { return 0, 0, ClickN // 点击N } return 0, 0, ClickErr // 超过界限 } if x >= m.height || y >= m.width { return 0, 0, ClickErr // 超过界限 } return x, y, 0 } // 重置所有数据 func (m *minesweeping) Clear() { m.mine = 99 for i := 0; i < m.height; i++ { for j := 0; j < m.width; j++ { m.gridSave[i][j] = DefZero } } for i := 0; i < m.mine; i++ { for { // 先随机布雷 x, y := rand.Intn(m.height), rand.Intn(m.width) if m.gridSave[x][y] == DefZero { m.gridSave[x][y] = DefMine break } } } for i := 0; i < m.height; i++ { for j := 0; j < m.width; j++ { if m.gridSave[i][j] == DefMine { m.AroundPos(i, j, func(x, y int) { if m.gridSave[x][y] != DefMine { m.gridSave[x][y]++ } }) } } } } // 遍历x, y周围所有点 func (m *minesweeping) AroundPos(x, y int, f func(i, j int)) { for i := x - 1; i <= x+1; i++ { for j := y - 1; j <= y+1; j++ { if i >= 0 && j >= 0 && i < m.height && j < m.width && (i != x || j != y) { f(i, j) } } } } // 当前位置为空白,则点开一片为空白的区域 func (m *minesweeping) ClickAround(x, y int) { m.ShowPos(x, y) m.AroundPos(x, y, func(i, j int) { tmpNum := m.GetData(i, j) if tmpNum >= DefOne && tmpNum <= DefEight { m.ShowPos(i, j) } else if m.CanClick(i, j) && tmpNum == DefZero { m.ClickAround(i, j) } }) } // 左键点击,遇到雷游戏结束 func (m *minesweeping) HandleLeft(x, y int) bool { if m.CanClick(x, y) { m.ShowPos(x, y) tmpNum := m.GetData(x, y) if tmpNum == DefMine { return false } if tmpNum == DefZero { m.ClickAround(x, y) } } return true } // 点出地雷返回false,游戏结束 func (m *minesweeping) HandleMid(x, y int) bool { tmpNum := m.GetData(x, y) if m.IsShow(x, y) && tmpNum >= DefOne && tmpNum <= DefEight { flagCnt := DefZero m.AroundPos(x, y, func(i, j int) { if m.IsFlag(i, j) { // 计算该点周围标旗个数 flagCnt++ } }) if flagCnt != tmpNum { return true } // 标旗个数等于该点个数,表示剩余位置一定不是雷,可以自动点开 flagCnt = DefZero m.AroundPos(x, y, func(i, j int) { if m.CanClick(i, j) { // 周围可点击点 m.ShowPos(i, j) switch m.GetData(i, j) { case DefMine: // 由于错误标旗,导致点开一个雷,因此算游戏结束 flagCnt = DefMine case DefZero: // 中键点开一个空白,自动点开一片空白 m.ClickAround(i, j) } } }) return flagCnt != DefMine } return true } // 右键标旗,对应地雷个数需要变化 func (m *minesweeping) HandleRight(x, y int) { if !m.IsShow(x, y) { // 翻转标旗 if m.gridSave[x][y]&DefFlagPos != 0 { m.gridSave[x][y] &= ^DefFlagPos m.mine++ } else { m.gridSave[x][y] |= DefFlagPos m.mine-- } } } func (m *minesweeping) CanClick(x, y int) bool { return m.gridSave[x][y]&^DefMaskPos == 0 // 该点能被点击 } func (m *minesweeping) IsFlag(x, y int) bool { return m.gridSave[x][y]&DefFlagPos != 0 // 该点是否已经标雷 } func (m *minesweeping) IsShow(x, y int) bool { return m.gridSave[x][y]&DefShowPos != 0 // 返回是否显示 } func (m *minesweeping) ShowPos(x, y int) { m.gridSave[x][y] |= DefShowPos // 设置点显示 } func (m *minesweeping) GetData(x, y int) GridDefine { return m.gridSave[x][y] & DefMaskPos // 返回该点值 } var ( ColorMap = map[GridDefine]int{ /* 每种标记的颜色值 */ DefZero: golibs.ForegroundRed | golibs.ForegroundGreen | golibs.ForegroundBlue, DefOne: golibs.ForegroundRed, DefTwo: golibs.ForegroundBlue | golibs.ForegroundRed, DefThree: golibs.ForegroundGreen, DefFour: golibs.ForegroundRed | golibs.ForegroundGreen, DefFive: golibs.ForegroundGreen | golibs.ForegroundBlue, DefSix: golibs.ForegroundRed, DefSeven: golibs.ForegroundBlue | golibs.ForegroundGreen | golibs.ForegroundIntensity, DefEight: golibs.ForegroundGreen, DefMine: golibs.ForegroundRed | golibs.ForegroundGreen | golibs.ForegroundIntensity, DefFlag: golibs.ForegroundGreen | golibs.ForegroundBlue, } ) func (m *minesweeping) Print(gameOver bool) { m.api.GotoXY(0, 0) for i := 0; i < m.height; i++ { fmt.Print(" -----------------------------------------------------------------------------------------------------------------------\n|") for j := 0; j < m.width; j++ { if m.IsFlag(i, j) { fmt.Print(" & |") } else if m.IsShow(i, j) { switch t := m.GetData(i, j); t { case DefZero: fmt.Print(" |") case DefMine: fmt.Print(" # |") default: m.api.TextBackground(ColorMap[t]) fmt.Printf(" %d ", t) m.api.TextBackground(ColorMap[0]) /* 重置为白色 */ fmt.Print("|") } } else { fmt.Print(" * |") } } fmt.Println() } fmt.Println(" -----------------------------------------------------------------------------------------------------------------------") var s string if gameOver { s = "You Win" if m.mine > 0 { s = "Game Over" } } fmt.Printf("| Y | N |\t[flag:&,mine:#]\tmines:%2d\t%s\n", m.mine, s) } ``` 当游戏成功或踩雷结束时可以用鼠标点击左下角的“Y”重新来一局,点击左下角的“N”退出游戏 鼠标左键:点开一个可点击位置,如果是雷则结束 鼠标中键:当该点个数和附近标旗数一致时,将其他所有可点击位置点开(如果错误标雷,可能会游戏结束) 鼠标右键:在可点击位置右键则标旗,再次右键取消标旗 以上就是源码和操作方法,写出来玩玩,主要是体会win32编程而已。

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

823 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传