この記事は【Kuin Advent Calendar 2016】の18日目の記事です。
【記事中で紹介しているコードについて】
コンパイルが通らない場合など、不具合があれば、@tatt61880まで連絡いただけると助かります。よろしくお願いいたします。
{ マインスイーパ }
const title: []char :: "Minesweeper"
var wndMain: wnd@Wnd
var drawMain: wnd@Draw
const size: float :: 30.0
const width: int :: 10
const height: int :: 6
const wndW: int :: @size $ int * (@width + 2)
const wndH: int :: @size $ int * (@height + 2)
const ratio: float :: 0.15
const mines: int :: ((@width * @height)$ float * @ratio)$ int
const numNotMines: int :: @width * @height - @mines
var font: draw@Font
var squares: [][]@Square
var numOpen: int
var gameovered: bool
var gameCleared: bool
var clickFlagL: bool
var clickFlagR: bool
class Square()
+var mine: int
+var state: @State
+var number: int
end class
enum State
notClicked
clicked
flagged
end enum
func main()
do @font :: draw@makeFont("Meiryo", (@size / 1.8) $ int, false, false, true, 0.0)
do @wndMain :: wnd@makeWnd(null, %fix, @wndW, @wndH, @title)
do @drawMain :: wnd@makeDraw(@wndMain, 0, 0, @wndW, @wndH, %fix, %fix, false)
do @drawMain.onMouseDownL :: drawMainOnMouseDownL
do @drawMain.onMouseDownR :: drawMainOnMouseDownR
do @drawMain.onMouseUpL :: drawMainOnMouseUpL
do @drawMain.onMouseUpR :: drawMainOnMouseUpR
while restart(wnd@act())
do @gameovered :: false
do @gameCleared :: false
do @numOpen :: 0
do initSquares()
do setMines(@mines)
do @clickFlagL :: false
do @clickFlagR :: false
while a(wnd@act())
do drawSquares()
do draw@render(30)
if(@gameovered | @gameCleared)
if(wnd@msgBox(@wndMain, (@gameovered ?("Game Over", "Congratulations!")) ~ "\nTry again?", @title, %question, %yesNo) = %yes)
skip restart
else
break restart
end if
end if
end while
end while
{初期化}
func initSquares()
do @squares :: #[@height + 2, @width + 2]@Square
for i(0, ^@squares - 1)
for j(0, ^@squares[i] - 1)
do @squares[i][j] :: #@Square
end for
end for
end func
{地雷をセット}
func setMines(num: int)
var mineFlags: []int :: #[@width * @height]int
for i(0, num - 1)
do mineFlags[i] :: 1
end for
do mineFlags.shuffle()
for i(0, ^mineFlags - 1)
do @squares[i / @width + 1][i % @width + 1].mine :: mineFlags[i]
end for
for row(1, @height)
for col(1, @width)
do @squares[row][col].number :: -@squares[row][col].mine
for r(-1, 1)
for c(-1, 1)
do @squares[row][col].number :+ @squares[row + r][col + c].mine
end for
end for
end for
end for
end func
{入力チェック}
func drawMainOnMouseDownL(wnd: wnd@WndBase, x: int, y: int)
do @clickFlagL :: true
end func
func drawMainOnMouseDownR(wnd: wnd@WndBase, x: int, y: int)
do @clickFlagR :: true
end func
func drawMainOnMouseUpL(wnd: wnd@WndBase, x: int, y: int)
do @clickFlagL :: false
if(@gameovered)
ret
end if
var col: int :: x / @size $ int
var row: int :: y / @size $ int
do @open(row, col)
if(@clickFlagR)
do @clickLR(row, col)
end if
end func
func drawMainOnMouseUpR(wnd: wnd@WndBase, x: int, y: int)
do @clickFlagR :: false
if(@gameovered)
ret
end if
var col: int :: x / @size $ int
var row: int :: y / @size $ int
if(@inArea(row, col))
switch(@squares[row][col].state)
case %notClicked
do @squares[row][col].state :: %flagged
case %clicked
case %flagged
do @squares[row][col].state :: %notClicked
end switch
end if
if(@clickFlagL)
do @clickLR(row, col)
end if
end func
{描画}
func drawSquares()
for row(1, @height)
for col(1, @width)
switch s(@squares[row][col].state)
case %notClicked, %flagged
if(@gameCleared)
do draw@rect(col$ float * @size, row$ float * @size, @size, @size, 0xFF0000FF)
else
do draw@rect(col$ float * @size, row$ float * @size, @size - 1.0, @size - 1.0, 0xFFFFFF00 + (s = %notClicked ?(255, 0)))
end if
case %clicked
if(@squares[row][col].mine = 1)
do draw@rect(col$ float * @size, row$ float * @size, @size - 1.0, @size - 1.0, 0xFFFF0000)
else
if(@squares[row][col].number <> 0)
do @font.draw((col$ float + 0.25) * @size, row$ float * @size, "\{@squares[row][col].number}", 0xFFD0D0D0)
end if
end if
end switch
end for
end for
end func
end func
func open(row: int, col: int)
if(!@inArea(row, col))
ret
end if
if(@squares[row][col].state = %notClicked)
do @squares[row][col].state :: %clicked
if(@squares[row][col].mine = 1)
do @gameovered :: true
ret
end if
do @numOpen :+ 1
do dbg@print("numOpen = \{@numOpen}\n")
if(@numOpen = @numNotMines)
do @gameCleared :: true
end if
if(@squares[row][col].number = 0)
do @openAround(row, col)
end if
end if
end func
func flagged(row: int, col: int): int
ret @squares[row][col].state = %flagged ?(1, 0)
end func
func openAround(row: int, col: int)
do @open(row - 1, col - 1)
do @open(row - 1, col)
do @open(row - 1, col + 1)
do @open(row, col - 1)
do @open(row, col + 1)
do @open(row + 1, col - 1)
do @open(row + 1, col)
do @open(row + 1, col + 1)
end func
func inArea(row: int, col: int): bool
ret (1 <= row & row <= @height & 1 <= col & col <= @width)
end func
func clickLR(row: int, col: int)
if(@inArea(row, col) &
| @squares[row][col].state = %clicked &
| (
| @flagged(row - 1, col - 1) +
| @flagged(row - 1, col ) +
| @flagged(row - 1, col + 1) +
| @flagged(row , col - 1) +
| @flagged(row , col + 1) +
| @flagged(row + 1, col - 1) +
| @flagged(row + 1, col ) +
| @flagged(row + 1, col + 1)
| = @squares[row][col].number
| )
|)
do @openAround(row, col)
end if
end func