明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 1163|回复: 0

Minesweeper in AutoCAD using .NET

[复制链接]
发表于 2011-7-7 17:21 | 显示全部楼层 |阅读模式

作者:kean

原文地址:http://through-the-interface.typepad.com/through_the_interface/2011/07/minesweeper-in-autocad-using-net.html

 

Today’s post contains some fun code contributed by Stephen Preston which re-creates  the Minesweeper video game inside AutoCAD. Stephen tells me it needs a bit of polishing, but the game is certainly playable. I’ve reformatted some of Stephen’s code to fit this blog.

The implementation comprises two main files, which you can name as you wish. I’ve used the VS 2010 convention for line-breaks (which means you don’t need an underscore). If you’re using older versions of Visual Sudio or Visual Basic Express you may need to concatenate lines or insert underscores at the end of them.

Here’s the main VB.NET file implementation the commands:

Imports Autodesk.AutoCAD.Runtime

Imports Autodesk.AutoCAD.ApplicationServices

Imports Autodesk.AutoCAD.DatabaseServices

Imports Autodesk.AutoCAD.Geometry

Imports Autodesk.AutoCAD.EditorInput

Imports System

 

' This line is not mandatory, but improves loading performance

 

<Assembly: CommandClass(GetType(Minesweeper.MyCommands))>

 

Namespace Minesweeper

 

  Public Class MyCommands

 

    'This is the main Minesweeper command.

    'It createas a new document, and invokes the worker

    'command(RUNMINESWEEPER) in the new document.

 

    <CommandMethod(

      "SGP_Minesweeper", "MINESWEEPER", "MINESWEEPER",

      CommandFlags.Session

      )>

    Public Shared Sub Minesweeper()

      Dim doc As Document = Application.DocumentManager.Add("")

      Application.DocumentManager.MdiActiveDocument = doc

      doc.SendStringToExecute("RUNMINESWEEPER ", True, False, False)

    End Sub

 

    'This is the worker command invoked usign SendStringToExecute

    'It starts the game running in the new doc created by the

    ' MINESWEEPER command.

    <CommandMethod(

      "SGP_Minesweeper", "RUNMINESWEEPER", CommandFlags.Modal

      )>

    Public Shared Sub RunMinesweeper()

      Try

        'Make sure new document has the text style we need

        DefineTextStyle()

        'Instantiate the game controller class and set game running

        Dim cls As New AcadMinesweeper

        cls.DoIt()

      Catch ex As Autodesk.AutoCAD.Runtime.Exception

        Application.DocumentManager.MdiActiveDocument.Editor.

          WriteMessage(vbCrLf &

            "Sorry - An error occurred. Game aborted." & vbCrLf)

      End Try

    End Sub

 

 

    'Adds a new text style to the drawing (if not already there)

 

    Private Shared Sub DefineTextStyle()

      Dim db As Database = HostApplicationServices.WorkingDatabase

      Dim tm As Autodesk.AutoCAD.DatabaseServices.TransactionManager

      tm = db.TransactionManager

      Dim myT As Transaction = tm.StartTransaction()

      Try

        Dim st As TextStyleTable =

          CType(

            tm.GetObject(

              db.TextStyleTableId, OpenMode.ForWrite, False),

            TextStyleTable)

        If Not st.Has("MinesweeperStyle") Then

          Dim str As TextStyleTableRecord =

            New TextStyleTableRecord()

          str.Name = "MinesweeperStyle"

          st.Add(str)

          str.FileName = "txt.shx"

          str.TextSize = 1.0

          str.IsShapeFile = True

          tm.AddNewlyCreatedDBObject(str, True)

        End If

        myT.Commit()

      Finally

        myT.Dispose()

      End Try

    End Sub

  End Class

 

 

  'This class governs our logic in AutoCAD.

  'It uses the MinesweeperMgr class to hold/query/edit the game data.

  '(Really needs a little more work to push more of the game logic

  'to Minesweeper Mgr).

 

  Public Class AcadMinesweeper

 

    'Links ObjectId of MText representing a mine cell to the row and

    ' column of the mine cell it represents

 

    Private Structure MineElement

      Public Id As ObjectId

      Public Row As Integer

      Public Col As Integer

    End Structure

 

    Private mMineMgr As New MinesweeperMgr 'Our manager class

    Private mMinefield() As MineElement ' Array of ObjectIds relating

                                        ' MText to their row and col

 

    'Main game controller function

 

    Public Sub DoIt()

      Dim ed As Editor =

        Application.DocumentManager.MdiActiveDocument.Editor

 

      'Prompt user for values to setup minefield

 

      If Not PromptSetup() Then

        ed.WriteMessage(

          vbCrLf & "You cancelled setup - aborting command" & vbCrLf)

        Exit Sub

      End If

 

      'Tell manager class to setup its grid

      'I'm use this MinesweeperMgr class to keep the game data

      '(in MinesweeperMgr) separated from the UI logic

      '(in AcadMinesweeper), so I can re-use it in other apps

 

      mMineMgr.InitMinefield()

 

      'Transfer calculated grid from the manager to the current

      'document/drawing

 

      If Not SetupGrid() Then

        ed.WriteMessage(

          vbCrLf & "There was a problem setting up the minefield" &

          " - aborting command" & vbCrLf)

        Exit Sub

      End If

 

      Dim startTime As DateTime = DateTime.Now

 

      ' The game loop

      While PromptMineAction()

      End While

 

      ' Game over - tell user how long the game lasted.

 

      Dim timeInterval As TimeSpan = DateTime.Now - startTime

      ed.WriteMessage(

        vbCrLf & "Time taken = " & timeInterval.TotalSeconds &

        " seconds" & vbCrLf)

    End Sub

 

 

    ' Prompt user to perform an action - clear/mark/unmark a cell

    ' in the minefield

 

    Private Function PromptMineAction() As Boolean

 

      'bMarking governs behavior depending on whether we're

      'clearing mines or marking them

 

      Static bMarking As Integer

      Dim strMsg As String = ""

      Dim strKeyword As String = ""

 

      'Setup prompts and keywords according to bMarking

 

      Select Case bMarking

        Case False

          strMsg = "Select a cell to uncover:"

          strKeyword = "Mark"

        Case True

          strMsg = "Select a cell to mark/unmark:"

          strKeyword = "Uncover"

      End Select

 

      'Prompt user to perform action

 

      Dim ed As Editor =

        Application.DocumentManager.MdiActiveDocument.Editor

      Dim opts As New PromptEntityOptions(vbCrLf & strMsg)

      opts.Keywords.Add(strKeyword)

      opts.AppendKeywordsToMessage = True

      opts.AllowNone = True

      Dim res As PromptEntityResult = ed.GetEntity(opts)

 

      'If user cancelled the command prompt then we end the game

      '(returning false ends the game loop).

 

      If res.Status = PromptStatus.Cancel Then

        ed.WriteMessage(

          vbCrLf & "You cancelled the game. Byeee!" & vbCrLf)

        Return False

      End If

 

      ' Don't let user escape command by pressing enter -

      ' just loop around again

 

      If res.Status = PromptStatus.None Then

        Return True

      End If

 

      'If user entered keyword, then we're toggling between

      ' mine clearing and mine marking

 

      If res.Status = PromptStatus.Keyword Then

        Select Case res.StringResult

          Case "Mark"

            bMarking = True

          Case Else

            bMarking = False

        End Select

        Return True

 

        'If user selected an entity (which must be MText

        ' because this is a new document, and that's all

        ' we added to it), then we use the ObjectId to

        ' retrieve its row and column in the grid.

 

      ElseIf res.Status = PromptStatus.OK Then

        Dim elem As MineElement = FindInMinefield(res.ObjectId)

        'This next if statement should never be used.

        If elem.Id = ObjectId.Null Then

          ed.WriteMessage(

            vbCrLf & "You didn't select a cell in the minefield." &

            vbCrLf)

          Return True

        End If

 

        'Check they didn't pick on a cell they already uncovered.

 

        If mMineMgr.CellIsUnCovered(elem.Row, elem.Col) Then

          ed.WriteMessage(

            vbCrLf & "This cell is already uncovered. " &

            "Pick another." & vbCrLf)

          Return True

        End If

 

        'If we got to here, then MText was picked, it is in the

        'grid, and it isn't uncovered yet.

 

        'If we're marking cells ...

 

        If bMarking Then

 

          'MarkCell toggles mark status

 

          Dim oldCellVal As MineCell =

            mMineMgr.MarkCell(elem.Row, elem.Col)

          If oldCellVal.Status = CellStatus.Covered Then

 

            'If cell was marked then we unmark it

 

            If oldCellVal.Status = CellStatus.Marked Then

              SetText(elem.Id, "X")

            Else 'If cell wasn't marked, we mark it.

              SetText(elem.Id, "M")

            End If

 

            'Go to next loop iteration

 

            Return True

          End If

        Else 'If we're clearing cells

          Dim oldCellVal As MineCell =

            mMineMgr.UncoverCell(elem.Row, elem.Col)

          If oldCellVal.isBomb Then

 

            'We hit a bomb -  game over

 

            SetText(elem.Id, "*")

            ed.WriteMessage(

              vbCrLf & "You hit a mine. Game Over!." & vbCrLf)

            Return False

          Else

 

            ' It wasn't a bomb

 

            SetText(elem.Id, oldCellVal.Value.ToString)

 

            ' If we've cleared all cells except the bombs,

            ' then we won - game over.

 

            If mMineMgr.AllEmptyCellsUncovered Then

              ed.WriteMessage(

                vbCrLf & "Congratulations. " &

                "You cleared all the mines." & vbCrLf)

              Return False

            Else 'Carry on game

              Return True

            End If

          End If

        End If

      End If

    End Function

 

    'Set the text for an MText entity with the provided ObjectId

    Private Sub SetText(

      ByVal objId As ObjectId, ByVal strText As String)

 

      Dim db As Database =

        Application.DocumentManager.MdiActiveDocument.Database

      Using tr As Transaction =

        db.TransactionManager.StartTransaction

 

        Dim txt As MText = tr.GetObject(objId, OpenMode.ForWrite)

        txt.Contents = strText

        tr.Commit()

      End Using

    End Sub

 

    'Retrieve text from the MText entity with the provided ObjectId

    Private Function GetText(ByVal objId As ObjectId)

      Dim strText As String

      Dim db As Database =

        Application.DocumentManager.MdiActiveDocument.Database

      Using tr As Transaction =

        db.TransactionManager.StartTransaction

 

        Dim txt As MText = tr.GetObject(objId, OpenMode.ForRead)

        strText = txt.Contents

        tr.Commit()

      End Using

      Return strText

    End Function

 

    ' Find MineElement with provided ObjectId in our array of all

    ' mine cells. This is how we asociate row and column value

    ' with an MText entity

 

    Private Function FindInMinefield(

      ByVal objId As ObjectId) As MineElement

 

      For Each elem As MineElement In mMinefield

        If elem.Id = objId Then

          Return elem

        End If

      Next

      'If we didn't find it, we return a blank - calling function

      'should query for null ObjectId

      Return New MineElement

    End Function

 

    'Create all the MText entities in our grid and zoom to fill

    'screen

 

    Private Function SetupGrid() As Boolean

      Dim bFlag As Boolean = False

      Dim db As Database =

        Application.DocumentManager.MdiActiveDocument.Database

      Try

        Using tr As Transaction =

          db.TransactionManager.StartTransaction

 

          Dim tst As TextStyleTable =

            tr.GetObject(db.TextStyleTableId, OpenMode.ForRead)

          Dim textStyleId As ObjectId = tst.Item("MinesweeperStyle")

          Dim btr As BlockTableRecord =

            CType(

              tr.GetObject(

                SymbolUtilityServices.GetBlockModelSpaceId(db),

                OpenMode.ForWrite),

              BlockTableRecord)

          Dim rows As Integer = mMineMgr.MinefieldRows

          Dim cols As Integer = mMineMgr.MinefieldColumns

          ReDim mMinefield(rows * cols - 1)

          For i As Integer = 0 To rows - 1

            For j = 0 To cols - 1

              Using txt As MText = New MText

                txt.SetDatabaseDefaults()

                txt.TextStyleId = textStyleId

                txt.Location = New Point3d(i, j, 0)

                txt.Width = 1.0

                txt.Height = 1.0

                txt.TextHeight = 0.8

                txt.Attachment = AttachmentPoint.MiddleCenter

                mMinefield(i * rows + j).Id = btr.AppendEntity(txt)

                mMinefield(i * rows + j).Row = i

                mMinefield(i * rows + j).Col = j

                txt.Contents = "X"

                tr.AddNewlyCreatedDBObject(txt, True)

              End Using

            Next

          Next

          tr.Commit()

          ModelZoomExtents()

        End Using

        bFlag = True

      Catch ex As Autodesk.AutoCAD.Runtime.Exception

        bFlag = False

      End Try

 

      ' If bFlag is true, then all mtexts were added to

      'DB without problem

 

      Return bFlag

    End Function

 

    'Prompt user for grid size and number of mines, and pass those

    'to the MinesweeperMgr to initialize itself

 

    Private Function PromptSetup() As Boolean

      Dim ed As Editor =

        Application.DocumentManager.MdiActiveDocument.Editor

      Dim opts1 As New PromptIntegerOptions(

        "Enter Minefield width:")

      opts1.LowerLimit = 1

      opts1.UpperLimit = 100

      opts1.DefaultValue = 10

      Dim res1 As PromptIntegerResult = ed.GetInteger(opts1)

      If res1.Status <> PromptStatus.OK Then

        Return False

      End If

      mMineMgr.MinefieldRows = res1.Value

 

      opts1.Message = "Enter minefield height:"

      res1 = ed.GetInteger(opts1)

      If res1.Status <> PromptStatus.OK Then

        Return False

      End If

      mMineMgr.MinefieldColumns = res1.Value

 

      opts1.Message = "Enter number of mines:"

      opts1.UpperLimit =

        mMineMgr.MinefieldRows * mMineMgr.MinefieldColumns

      opts1.DefaultValue =

        mMineMgr.MinefieldRows * mMineMgr.MinefieldColumns / 6

      res1 = ed.GetInteger(opts1)

      If res1.Status <> PromptStatus.OK Then

        Return False

      End If

      mMineMgr.NumMines = res1.Value

      Return True

    End Function

 

    'The next two functions are helper functions to zoom to the grid.

    'Code copied from ADN DevNote

    Public Sub SetViewportToExtents(

      ByVal db As Database, ByVal vtr As ViewportTableRecord)

 

      'Let's update the database extents first

      'True gives the best fit but will take time

 

      db.UpdateExt(True)

 

      'Get the screen aspect ratio to calculate the height and width

 

      Dim scrRatio As Double = (vtr.Width / vtr.Height)

 

      'Prepare Matrix for DCS to WCS transformation

 

      Dim matWCS2DCS As Matrix3d =

        Matrix3d.PlaneToWorld(vtr.ViewDirection)

 

      'For DCS target point is the origin

 

      matWCS2DCS =

        Matrix3d.Displacement(vtr.Target - Point3d.Origin) *

        matWCS2DCS

 

      'WCS Xaxis is twisted by twist angle

 

      matWCS2DCS =

        Matrix3d.Rotation(

          -vtr.ViewTwist, vtr.ViewDirection, vtr.Target) *

        matWCS2DCS

      matWCS2DCS = matWCS2DCS.Inverse()

 

      'Tranform the extents to the DCS defined by the viewdir

 

      Dim extents As New Extents3d(db.Extmin, db.Extmax)

      extents.TransformBy(matWCS2DCS)

 

      'Width of the extents in current view

 

      Dim width As Double =

        (extents.MaxPoint.X - extents.MinPoint.X)

 

      'Height of the extents in current view

 

      Dim height As Double =

        (extents.MaxPoint.Y - extents.MinPoint.Y)

 

      'Get the view center point

 

      Dim center As New Point2d(

        (extents.MaxPoint.X + extents.MinPoint.X) * 0.5,

        (extents.MaxPoint.Y + extents.MinPoint.Y) * 0.5)

 

      'Check if the width 'fits' in current window

      'If not then get the new height as per the viewport's

      'aspect(ratio)

 

      If width > (height * scrRatio) Then

        height = width / scrRatio

      End If

      vtr.Height = height

      vtr.Width = height * scrRatio

      vtr.CenterPoint = center

      vtr.IconEnabled = False

    End Sub

 

    Public Sub ModelZoomExtents()

      Dim doc As Document =

        Application.DocumentManager.MdiActiveDocument

      Dim db As Database = doc.Database

      Dim ed As Editor = doc.Editor

      Using Tx As Transaction =

        db.TransactionManager.StartTransaction()

 

        ed.UpdateTiledViewportsInDatabase()

        Dim viewportTableRec As ViewportTableRecord =

          TryCast(

            Tx.GetObject(

              ed.ActiveViewportId, OpenMode.ForWrite),

            ViewportTableRecord)

        SetViewportToExtents(db, viewportTableRec)

        ed.UpdateTiledViewportsFromDatabase()

        Tx.Commit()

      End Using

    End Sub

  End Class

 

End Namespace

The second file implements – among other things – a MinesweeperMgr class that takes care of the main implementation of the game (aiding portability). This is a good technique to follow, where possible, as AutoCAD may not be the only (even if by some bizarre twist of fate it somehow ends up being the best ;-) environment for playing the Minesweeper game.

Here’s the MinesweeperMgr implementation file:

Namespace Minesweeper

 

  Public Class MinesweeperException

    Inherits Exception

  End Class

 

  'Each cell can have one of these three statuses

 

  Public Enum CellStatus

    Covered

    Uncovered

    Marked

  End Enum

 

 

  Public Structure MineCell

    Public Sub New(

      ByVal status As CellStatus, ByVal val As Integer,

      ByVal bomb As Boolean, ByVal marked As Boolean)

 

      status = status

      isBomb = bomb

      Value = val

    End Sub

    Public Status As CellStatus 'Covered/Uncovered/Marked

    Public Value As Integer 'No of neighboring cells containing bombs

    Public isBomb As Boolean 'Is this cell a bomb?

  End Structure

 

 

  Public Class MinesweeperMgr

 

    Private mRows As Integer 'Rows in grid

    Private mCols As Integer 'Columns in grid

    Private mMineArray(,) As MineCell 'The grid

    Private mNumMines As Integer 'Number of mines hidden in grid

    Private mNumCellsUncovered 'Number of cells currently uncovered

 

    Public ReadOnly Property NumCellsUncovered() As Integer

      Get

        Return mNumCellsUncovered

      End Get

    End Property

 

    Private Function IncrementNumCellsUncovered() As Integer

      mNumCellsUncovered = mNumCellsUncovered + 1

      Return mNumCellsUncovered

    End Function

 

    Public Property MinefieldRows() As Integer

      Get

        Return mRows

      End Get

      Set(ByVal value As Integer)

        If value > 0 Then

          mRows = value

          If NumMines > mRows * MinefieldColumns Then

            NumMines = mRows * MinefieldColumns

          End If

        Else

          Throw New MinesweeperException

        End If

      End Set

    End Property

 

    Public Property MinefieldColumns() As Integer

      Get

        Return mCols

      End Get

      Set(ByVal value As Integer)

        If value > 0 Then

          mCols = value

          If NumMines > MinefieldRows * mCols Then

            NumMines = MinefieldRows * mCols

          End If

        Else

          Throw New MinesweeperException

        End If

      End Set

    End Property

 

    Public Property NumMines() As Integer

      Get

        Return mNumMines

      End Get

      Set(ByVal value As Integer)

        If mNumMines <= (mCols * mRows) Then

          mNumMines = value

        Else

          mNumMines = mCols * mRows

        End If

      End Set

    End Property

 

    Public ReadOnly Property MineArray() As MineCell(,)

      Get

        Return mMineArray

      End Get

    End Property

 

    Public Function GetCell(

      ByVal row As Integer, ByVal col As Integer) As MineCell

 

      If row >= 0 And row < MinefieldRows And

         col >= 0 And col < MinefieldColumns Then

        Return mMineArray(row, col)

      Else

        Throw New MinesweeperException

      End If

    End Function

    Public Function SetCell(

      ByVal row As Integer, ByVal col As Integer,

      ByVal value As MineCell) As Boolean

 

      If row >= 0 And row < MinefieldRows And

         col >= 0 And col < MinefieldColumns Then

        mMineArray(row, col) = value

        Return True

      Else

        Throw New MinesweeperException

      End If

    End Function

 

    Public Function UncoverCell(

      ByVal row As Integer, ByVal col As Integer) As MineCell

 

      Dim curCellVal As MineCell = MineArray(row, col)

      If curCellVal.Status <> CellStatus.Uncovered Then

        MineArray(row, col).Status = CellStatus.Uncovered

        IncrementNumCellsUncovered()

      End If

      Return curCellVal

    End Function

 

    'Toggle cell between Covered and Marked Status.

    'Does nothing for Uncovered cells.

    'Returns previous MineCell values.

 

    Public Function MarkCell(

      ByVal row As Integer, ByVal col As Integer) As MineCell

 

      Dim curCellVal As MineCell = MineArray(row, col)

      If curCellVal.Status = CellStatus.Covered Then

        MineArray(row, col).Status = CellStatus.Marked

      ElseIf curCellVal.Status = CellStatus.Marked Then

        MineArray(row, col).Status = CellStatus.Covered

      End If

      Return curCellVal

    End Function

 

    'Returns true if cell is uncovered

    Public Function CellIsUnCovered(

      ByVal row As Integer, ByVal col As Integer) As Boolean

 

      Dim curCellVal As MineCell = MineArray(row, col)

      If MineArray(row, col).Status = CellStatus.Uncovered Then

        Return True

      Else

        Return False

      End If

    End Function

 

    'Returns true if we've cleared all our non-mine cells

 

    Public Function AllEmptyCellsUncovered() As Boolean

      Return NumCellsUncovered =

         MinefieldColumns * MinefieldRows - NumMines

    End Function

 

    Public Sub InitMinefield()

      InitMinefield(MinefieldRows, MinefieldColumns, NumMines)

    End Sub

 

    Private Sub ResetNumCellsUncovered()

      mNumCellsUncovered = 0

    End Sub

 

    'Initialize grid, and put the mines in random locations

 

    Public Sub InitMinefield(

      ByVal rows As Integer, ByVal cols As Integer,

      ByVal num As Integer)

 

      If rows < 1 Or cols < 1 Or num < 1 Then

        Throw New MinesweeperException

      End If

      MinefieldRows = rows

      MinefieldColumns = cols

      If num > rows * cols Then

        NumMines = rows * cols

      Else

        NumMines = num

      End If

      ResetNumCellsUncovered()

 

      'Initialize grid (array) to represent minefield

 

      ReDim mMineArray(rows - 1, cols - 1)

 

      ' Add mines to grid (value of -1 means a mine is at

      ' that location)

 

      Randomize()

      Dim i As Integer = 0

      Do

        Dim rndRow As Integer = Rnd() * (MinefieldRows - 1)

        Dim rndCol As Integer = Rnd() * (MinefieldColumns - 1)

        If mMineArray(rndRow, rndCol).isBomb = False Then

          mMineArray(rndRow, rndCol).Value = -1

          mMineArray(rndRow, rndCol).isBomb = True

          mMineArray(rndRow, rndCol).Status = CellStatus.Covered

          i = i + 1

        End If

      Loop While i < num

 

      ' Now mines are added, we populate the rest of the grid

      ' with the numbers to indicate how many mines are in

      ' neighbouring(cells)

 

      For i = 0 To MinefieldRows - 1

        For j As Integer = 0 To MinefieldColumns - 1

 

          ' If this cell contains a mine then don't process it

 

          If mMineArray(i, j).isBomb = True Then

            Continue For

          End If

          Dim mineCounter As Integer = 0

 

          'Check grid cells around this one looking for mines ...

          ' i-1,j-1 | i,j-1 | i+1,j-1

          '   i-1,j |  i,j  | i+1,j

          ' i-1,j+1 | i,j+1 | i+1,j+1

 

          For k As Integer = -1 To 1

            For l As Integer = -1 To 1

 

              ' Skip over cells outside bounds of minefield

 

              If (i + k < 0) Or (i + k > MinefieldRows - 1) Or

                 (j + l < 0) Or (j + l > MinefieldColumns - 1) Then

                Continue For

              End If

              'Don't include cell (i,j)

              If k = 0 And l = 0 Then

                Continue For

              End If

              If mMineArray(i + k, j + l).isBomb = True Then

                mineCounter = mineCounter + 1

              End If

            Next

          Next

          mMineArray(i, j).Value = mineCounter

          mMineArray(i, j).Status = CellStatus.Covered

        Next

      Next

    End Sub

  End Class

End Namespace

When we run the MINESWEEPER command, it asks you for the size of the grid and the number of mines to hide inside it. It then displays a blank game:

As you uncover cells, you see the number of adjacent (straight and diagonal) cells containing mines. You continue to select cells until you hit a mine:

I’d show you a successfully completed game, but I’m clearly too far from my student days (and no longer have the requisite skills ;-).

Thanks for the “blast from the past” (boom boom… ouch – the puns are never-ending :-) and for the fun implementation, Stephen!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
"觉得好,就打赏"
还没有人打赏,支持一下

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-4-24 03:16 , Processed in 0.360566 second(s), 31 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表