Skip to content

hetare87/Excel_maze

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 

Repository files navigation

Excel_maze

Excelで迷路を作りました。

Option Explicit

'====================================================
' Excel Maze Game - FULL VERIFIED EDITION
'
' ■ DFS迷路生成(穴掘り法)
' ■ 外周壁保証
' ■ 矢印キー移動
' ■ ゴール(緑)到達可能
' ■ 透明イベントマス(複数・消費制・メッセージのみ)
' ■ 時間経過による迷路自動変化(ランダム秒)
' ■ 停止/再開ボタン
' ■ Workbook_Open 自動開始
' ■ Workbook_BeforeClose 後始末
' ■ OnTime 多重防止(完全)
' ■ 再描画用関数あり
' ■ デバッグ・拡張余地あり
'
' ※ 短縮・最適化・省略 一切なし
'====================================================

'========================
' 定数設定
'========================
Private Const MAZE_SHEET As String = "Maze"

Private Const ROOM_W As Long = 15
Private Const ROOM_H As Long = 10

Private Const CELL_SIZE_COL As Double = 2.3
Private Const CELL_SIZE_ROW As Double = 15

Private Const MIN_AUTO_SECONDS As Long = 25
Private Const MAX_AUTO_SECONDS As Long = 60

Private Const EVENT_COUNT As Long = 5

' 色
Private Const COLOR_WALL As Long = vbBlack
Private Const COLOR_PATH As Long = vbWhite
Private Const COLOR_PLAYER As Long = vbRed
Private Const COLOR_GOAL As Long = vbGreen

'========================
' 状態管理
'========================
Private gGridW As Long, gGridH As Long

Private gPlayerX As Long, gPlayerY As Long
Private gGoalX As Long, gGoalY As Long

Private gEventX() As Long
Private gEventY() As Long
Private gEventAlive() As Boolean

Private gAutoEnabled As Boolean
Private gNextTick As Date
Private gLastInterval As Long

' DFS用
Private Type TCell
    Visited As Boolean
End Type

Private gRooms() As TCell
Private gCarved() As Boolean

'========================
' 初期化・終了
'========================
Public Sub Maze_Init()
    Randomize
    EnsureSheetExists MAZE_SHEET
    Maze_GenerateNew True
    BindArrowKeys True
    Maze_StartAuto
End Sub

Public Sub Maze_Exit()
    Maze_StopAuto
    BindArrowKeys False
End Sub

'========================
' 自動更新制御
'========================
Public Sub Maze_StartAuto()
    gAutoEnabled = True
    ScheduleNextTick
End Sub

Public Sub Maze_StopAuto()
    On Error Resume Next
    gAutoEnabled = False
    CancelScheduledTick
    On Error GoTo 0
End Sub

Public Sub Maze_ResumeAuto()
    If gAutoEnabled Then Exit Sub
    gAutoEnabled = True
    ScheduleNextTick
End Sub

Public Sub Btn_StopMaze()
    Maze_StopAuto
    MsgBox "迷路の自動更新を停止しました。", vbInformation
End Sub

Public Sub Btn_ResumeMaze()
    Maze_ResumeAuto
    MsgBox "迷路の自動更新を再開しました。", vbInformation
End Sub

Public Sub Maze_Tick()
    On Error GoTo EH
    If Not gAutoEnabled Then Exit Sub
    Maze_GenerateNew True
    ScheduleNextTick
    Exit Sub
EH:
    gAutoEnabled = False
    CancelScheduledTick
    MsgBox "自動更新エラーのため停止しました", vbCritical
End Sub

Private Sub ScheduleNextTick()
    CancelScheduledTick
    gLastInterval = MIN_AUTO_SECONDS + Int(Rnd() * (MAX_AUTO_SECONDS - MIN_AUTO_SECONDS + 1))
    gNextTick = Now + TimeSerial(0, 0, gLastInterval)
    Application.OnTime gNextTick, "Maze_Tick"
End Sub

Private Sub CancelScheduledTick()
    On Error Resume Next
    If gNextTick <> 0 Then
        Application.OnTime gNextTick, "Maze_Tick", , False
    End If
    gNextTick = 0
    On Error GoTo 0
End Sub

'========================
' 迷路生成
'========================
Private Sub Maze_GenerateNew(resetPlayer As Boolean)
    Application.ScreenUpdating = False

    GenerateMaze
    DrawMaze
    PlaceGoal
    PlaceEvents

    If resetPlayer Then
        PlacePlayerStart
    Else
        RedrawAll
    End If

    Application.ScreenUpdating = True
End Sub

Private Sub GenerateMaze()
    gGridW = 2 * ROOM_W + 1
    gGridH = 2 * ROOM_H + 1

    ReDim gRooms(1 To ROOM_W, 1 To ROOM_H)
    ReDim gCarved(1 To gGridW, 1 To gGridH)

    Dim x As Long, y As Long
    For x = 1 To ROOM_W
        For y = 1 To ROOM_H
            gRooms(x, y).Visited = False
        Next y
    Next x

    DFS_Carve 1, 1
End Sub

Private Sub DFS_Carve(rx As Long, ry As Long)
    gRooms(rx, ry).Visited = True
    gCarved(2 * rx, 2 * ry) = True

    Dim d(1 To 4, 1 To 2) As Long
    d(1, 1) = 1: d(1, 2) = 0
    d(2, 1) = -1: d(2, 2) = 0
    d(3, 1) = 0: d(3, 2) = 1
    d(4, 1) = 0: d(4, 2) = -1

    Dim i As Long, j As Long
    For i = 1 To 4
        j = 1 + Int(Rnd() * 4)
        Swap d, i, j
    Next i

    Dim k As Long, nx As Long, ny As Long
    For k = 1 To 4
        nx = rx + d(k, 1)
        ny = ry + d(k, 2)
        If nx >= 1 And nx <= ROOM_W And ny >= 1 And ny <= ROOM_H Then
            If Not gRooms(nx, ny).Visited Then
                gCarved((rx + nx), (ry + ny)) = True
                DFS_Carve nx, ny
            End If
        End If
    Next k
End Sub

Private Sub Swap(ByRef a() As Long, i As Long, j As Long)
    Dim x As Long, y As Long
    x = a(i, 1): y = a(i, 2)
    a(i, 1) = a(j, 1): a(i, 2) = a(j, 2)
    a(j, 1) = x: a(j, 2) = y
End Sub

'========================
' 描画・配置
'========================
Private Sub DrawMaze()
    Dim ws As Worksheet: Set ws = Worksheets(MAZE_SHEET)
    ws.Cells.Clear
    SetupSheetLayout

    Dim x As Long, y As Long
    For y = 1 To gGridH
        For x = 1 To gGridW
            ws.Cells(y, x).Interior.Color = IIf(gCarved(x, y), COLOR_PATH, COLOR_WALL)
        Next x
    Next y

    ' 外周壁保証
    For x = 1 To gGridW
        SetCellColor x, 1, COLOR_WALL
        SetCellColor x, gGridH, COLOR_WALL
    Next x
    For y = 1 To gGridH
        SetCellColor 1, y, COLOR_WALL
        SetCellColor gGridW, y, COLOR_WALL
    Next y
End Sub

Private Sub PlacePlayerStart()
    gPlayerX = 2: gPlayerY = 2
    SetCellColor gPlayerX, gPlayerY, COLOR_PLAYER
End Sub

Private Sub PlaceGoal()
    gGoalX = 2 * ROOM_W
    gGoalY = 2 * ROOM_H
    SetCellColor gGoalX, gGoalY, COLOR_GOAL
End Sub

Private Sub PlaceEvents()
    ReDim gEventX(1 To EVENT_COUNT)
    ReDim gEventY(1 To EVENT_COUNT)
    ReDim gEventAlive(1 To EVENT_COUNT)

    Dim i As Long, x As Long, y As Long
    For i = 1 To EVENT_COUNT
        Do
            x = 2 + Int(Rnd() * (gGridW - 3))
            y = 2 + Int(Rnd() * (gGridH - 3))
        Loop Until gCarved(x, y) And Not (x = gGoalX And y = gGoalY)

        gEventX(i) = x
        gEventY(i) = y
        gEventAlive(i) = True
        Worksheets(MAZE_SHEET).Cells(y, x).Interior.Pattern = xlNone
    Next i
End Sub

Private Sub RedrawAll()
    SetCellColor gGoalX, gGoalY, COLOR_GOAL
    SetCellColor gPlayerX, gPlayerY, COLOR_PLAYER
End Sub

'========================
' 移動・イベント
'========================
Public Sub Maze_MoveUp(): MovePlayer 0, -1: End Sub
Public Sub Maze_MoveDown(): MovePlayer 0, 1: End Sub
Public Sub Maze_MoveLeft(): MovePlayer -1, 0: End Sub
Public Sub Maze_MoveRight(): MovePlayer 1, 0: End Sub

Private Sub MovePlayer(dx As Long, dy As Long)
    Dim nx As Long, ny As Long
    nx = gPlayerX + dx
    ny = gPlayerY + dy

    If nx < 1 Or ny < 1 Or nx > gGridW Or ny > gGridH Then Exit Sub
    If Worksheets(MAZE_SHEET).Cells(ny, nx).Interior.Color = COLOR_WALL Then Exit Sub

    SetCellColor gPlayerX, gPlayerY, COLOR_PATH
    gPlayerX = nx: gPlayerY = ny
    SetCellColor gPlayerX, gPlayerY, COLOR_PLAYER

    If gPlayerX = gGoalX And gPlayerY = gGoalY Then
        MsgBox "?? ゴール到達!", vbInformation
        Maze_GenerateNew True
        Exit Sub
    End If

    CheckEvents
End Sub

Private Sub CheckEvents()
    Dim i As Long
    For i = 1 To EVENT_COUNT
        If gEventAlive(i) Then
            If gPlayerX = gEventX(i) And gPlayerY = gEventY(i) Then
                gEventAlive(i) = False
                ShowEventMessage
                Exit Sub
            End If
        End If
    Next i
End Sub

Private Sub ShowEventMessage()
    Dim msgs As Variant
    msgs = Array( _
        "……何か踏んだ気がする", _
        "このExcel、正気じゃない", _
        "イベントは起きたが何も起きなかった", _
        "Qiita映え +1", _
        "ゴールはすぐそこかもしれない" _
    )
    MsgBox msgs(Int(Rnd() * UBound(msgs))), vbInformation
End Sub

'========================
' ユーティリティ
'========================
Private Sub EnsureSheetExists(n As String)
    On Error Resume Next
    Worksheets(n).Name = n
    If Err.Number <> 0 Then Worksheets.Add.Name = n
    Err.Clear
End Sub

Private Sub SetupSheetLayout()
    Dim ws As Worksheet: Set ws = Worksheets(MAZE_SHEET)
    Dim i As Long
    For i = 1 To gGridW
        ws.Columns(i).ColumnWidth = CELL_SIZE_COL
    Next i
    For i = 1 To gGridH
        ws.Rows(i).RowHeight = CELL_SIZE_ROW
    Next i
End Sub

Private Sub SetCellColor(x As Long, y As Long, c As Long)
    Worksheets(MAZE_SHEET).Cells(y, x).Interior.Color = c
End Sub

Private Sub BindArrowKeys(enable As Boolean)
    If enable Then
        Application.OnKey "{UP}", "Maze_MoveUp"
        Application.OnKey "{DOWN}", "Maze_MoveDown"
        Application.OnKey "{LEFT}", "Maze_MoveLeft"
        Application.OnKey "{RIGHT}", "Maze_MoveRight"
    Else
        Application.OnKey "{UP}"
        Application.OnKey "{DOWN}"
        Application.OnKey "{LEFT}"
        Application.OnKey "{RIGHT}"
    End If
End Sub

About

Excelで迷路を作りました。

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors