この記事は【Kuin Advent Calendar 2016】の3日目の記事です。
【記事中で紹介しているコードについて】
コンパイルが通らない場合など、不具合があれば、@tatt61880まで連絡いただけると助かります。よろしくお願いいたします。
{迷路です。ゴールすると、ゴールの位置が反対側になって迷路が再構築されるため、無限に遊べます。}
{スペースキーを押すと、表示範囲が変わります。}
{ある程度小さいサイズの方がおすすめです。}
const W: int :: 16
const H: int :: 12
const px: float :: 10.0
const WW: int :: (2 * @W + 1) * @px $ int
const WH: int :: (2 * @H + 1) * @px $ int
var drawAreaFuncs: []@DrawAreaFuncs
func main()
do @map :: #[2 * @H + 1, 2 * @W + 1]bool
do @userX :: 1
do @userY :: 1
do @createMaze()
var wndMain: wnd@Wnd :: wnd@makeWnd(null, %aspect, @WW, @WH, "無限に遊べる迷路です")
var drawMain: wnd@Draw :: wnd@makeDraw(wndMain, 0, 0, @WW, @WH, %scale, %scale, false)
do @initDrawAreaFuncs()
while(wnd@act())
do @moveUser()
do @drawMaze()
do @drawGoal()
do @drawUser()
do draw@render(60)
if(@userX = @goalX & @userY = @goalY)
do @createMaze()
end if
end while
end func
var map: [][]bool
var aisle: [][]bit8
var x: int
var y: int
var userX: int
var userY: int
var goalX: int
var goalY: int
const U: bit8 :: 1b8
const R: bit8 :: 2b8
const D: bit8 :: 4b8
const L: bit8 :: 8b8
func carve()
do @aisle :: #[@H, @W]bit8
do @x :: lib@rnd(0, @W - 1)
do @y :: lib@rnd(0, @H - 1)
while(true)
switch(selectDir())
case 0b8
ret
case @U
do @aisle[@y][@x] :: @aisle[@y][@x].or(@U)
do @y :- 1
do @aisle[@y][@x] :: @D.or(@D.shl(4))
case @R
do @aisle[@y][@x] :: @aisle[@y][@x].or(@R)
do @x :+ 1
do @aisle[@y][@x] :: @L.or(@L.shl(4))
case @D
do @aisle[@y][@x] :: @aisle[@y][@x].or(@D)
do @y :+ 1
do @aisle[@y][@x] :: @U.or(@U.shl(4))
case @L
do @aisle[@y][@x] :: @aisle[@y][@x].or(@L)
do @x :- 1
do @aisle[@y][@x] :: @R.or(@R.shl(4))
end switch
end while
func selectDir(): bit8
while(true)
var ds: []bit8 :: [@U, @R, @D, @L]
var shuffle: []int :: lib@shuffle(4)
for i(0, ^ds - 1)
var d: bit8 :: ds[shuffle[i]]
if(checkDir(d))
ret d {まだ掘られていない道があったのでそちらに向かいます}
end if
end for
{掘った場所を戻っていきます}
switch d(@aisle[@y][@x].shr(4))
case 0b8
ret d {スタート地点まで戻ってきたので穴掘りを終了します}
case @U
do @y :- 1
case @R
do @x :+ 1
case @D
do @y :+ 1
case @L
do @x :- 1
end switch
end while
func checkDir(d: bit8): bool
switch(d)
case @U
ret(@y > 0 & @aisle[@y - 1][@x] = 0b8)
case @R
ret(@x + 1 < @W & @aisle[@y][@x + 1] = 0b8)
case @D
ret(@y + 1 < @H & @aisle[@y + 1][@x] = 0b8)
case @L
ret(@x > 0 & @aisle[@y][@x - 1] = 0b8)
default
ret false
end switch
end func
end func
end func
func createMaze()
do @goalX :: 2 * @W - @userX
do @goalY :: 2 * @H - @userY
do @carve()
for h(0, 2 * @H, 2)
for w(0, 2 * @W, 2)
do @map[h][w] :: true
end for
end for
for h(0, @H - 1)
for w(0, @W - 1)
do @map[2 * h + 1][2 * w + 1] :: false
do @map[2 * h + 0][2 * w + 1] :: @aisle[h][w].and(@U) = 0b8
do @map[2 * h + 1][2 * w + 2] :: @aisle[h][w].and(@R) = 0b8
do @map[2 * h + 2][2 * w + 1] :: @aisle[h][w].and(@D) = 0b8
do @map[2 * h + 1][2 * w + 0] :: @aisle[h][w].and(@L) = 0b8
end for
end for
end func
func drawMaze()
for h(0, 2 * @H)
for w(0, 2 * @W)
if(@isDrawArea(w, h))
do draw2d@rect(w $ float * @px, h $ float * @px, @px, @px, @map[h][w] ?(0xFF008855, 0xFFFFCCCC))
end if
end for
end for
if(@drawAreaEdge <>& null)
do @drawAreaEdge((@userX $ float + 0.5) * @px, (@userY $ float + 0.5) * @px)
end if
end func
func drawUser()
do draw2d@circle((@userX $ float + 0.5) * @px, (@userY $ float + 0.5) * @px, @px * 0.5, @px * 0.5, 0xFF0000FF)
end func
func drawGoal()
do draw2d@rect((@goalX $ float - 0.2) * @px, (@goalY $ float - 0.2) * @px, @px * 1.4, @px * 1.4, 0xFFFF0000)
end func
{アローキー(←↓↑→)で操作します。}
func moveUser(): bool
if(myPadPress(%up))
if(!@map[@userY - 1][@userX])
do @userY :- 2
ret true
end if
end if
if(myPadPress(%right))
if(!@map[@userY][@userX + 1])
do @userX :+ 2
ret true
end if
end if
if(myPadPress(%down))
if(!@map[@userY + 1][@userX])
do @userY :+ 2
ret true
end if
end if
if(myPadPress(%left))
if(!@map[@userY][@userX - 1])
do @userX :- 2
ret true
end if
end if
if(myPadPress(%a))
do @isDrawAreaFuncId :+ 1
do @isDrawAreaFuncId :% ^@drawAreaFuncs
do @isDrawArea :: @drawAreaFuncs[@isDrawAreaFuncId].isDrawArea
do @drawAreaEdge :: @drawAreaFuncs[@isDrawAreaFuncId].drawAreaEdge
end if
ret false
func myPadPress(btn: input@PadBtn): bool
var n: int :: input@pad(0, btn)
ret n = 1 | n >= 10 & n % 4 = 0
end func
end func
var isDrawAreaFuncId: int
var isDrawArea: func<(int, int): bool>
var drawAreaEdge: func<(float, float)>
class DrawAreaFuncs()
+var isDrawArea: func<(int, int): bool>
+var drawAreaEdge: func<(float, float)>
+func init(a: func<(int, int): bool>, b: func<(float, float)>): @DrawAreaFuncs
do me.isDrawArea :: a
do me.drawAreaEdge :: b
ret me
end func
end class
func initDrawAreaFuncs()
do @drawAreaFuncs :: #[0]@DrawAreaFuncs
do @drawAreaFuncs :~ [(#@DrawAreaFuncs).init(drawWholeArea, null)]
do @drawAreaFuncs :~ [(#@DrawAreaFuncs).init(drawCircleAreaS, drawCircleAreaEdgeS)]
do @drawAreaFuncs :~ [(#@DrawAreaFuncs).init(drawCircleAreaM, drawCircleAreaEdgeM)]
do @drawAreaFuncs :~ [(#@DrawAreaFuncs).init(drawCircleAreaL, drawCircleAreaEdgeL)]
do @drawAreaFuncs :~ [(#@DrawAreaFuncs).init(drawSquareAreaS, null)]
do @drawAreaFuncs :~ [(#@DrawAreaFuncs).init(drawSquareAreaM, null)]
do @drawAreaFuncs :~ [(#@DrawAreaFuncs).init(drawSquareAreaL, null)]
{
do @drawAreaFuncs :~ [(#@DrawAreaFuncs).init(drawLadarAreaS, drawLadarAreaEdgeS)]
do @drawAreaFuncs :~ [(#@DrawAreaFuncs).init(drawLadarAreaM, drawLadarAreaEdgeM)]
do @drawAreaFuncs :~ [(#@DrawAreaFuncs).init(drawLadarAreaL, drawLadarAreaEdgeL)]
}
var id: int :: 0
; do id :: ^@drawAreaFuncs - 1
do @isDrawArea :: @drawAreaFuncs[id].isDrawArea
do @drawAreaEdge :: @drawAreaFuncs[id].drawAreaEdge
func drawWholeArea(w: int, h: int): bool
ret true
end func
func drawCircleAreaSub(w: int, h: int, size: int): bool
ret(@userX - w) ^ 2 + (@userY - h) ^ 2 < size
end func
func drawCircleAreaS(w: int, h: int): bool
ret drawCircleAreaSub(w, h, 18)
end func
func drawCircleAreaM(w: int, h: int): bool
ret drawCircleAreaSub(w, h, 30)
end func
func drawCircleAreaL(w: int, h: int): bool
ret drawCircleAreaSub(w, h, 51)
end func
func drawCircleAreaEdgeSub(w: float, h: float, size: int)
do draw2d@circleLine(w, h, size $ float, size $ float, @px * 2 $ float, draw@black)
end func
func drawCircleAreaEdgeS(w: float, h: float)
do drawCircleAreaEdgeSub(w, h, 45)
end func
func drawCircleAreaEdgeM(w: float, h: float)
do drawCircleAreaEdgeSub(w, h, 60)
end func
func drawCircleAreaEdgeL(w: float, h: float)
do drawCircleAreaEdgeSub(w, h, 75)
end func
func drawSquareAreaSub(w: int, h: int, size: int): bool
ret
|(@userX / size = w / size | @userX / size = (w - 1) / size) &
|(@userY / size = h / size | @userY / size = (h - 1) / size)
end func
func drawSquareAreaS(w: int, h: int): bool
ret drawSquareAreaSub(w, h, 4)
end func
func drawSquareAreaM(w: int, h: int): bool
ret drawSquareAreaSub(w, h, 6)
end func
func drawSquareAreaL(w: int, h: int): bool
ret drawSquareAreaSub(w, h, 8)
end func
func drawLadarAreaSub(w: int, h: int, ratio: float): bool
if(drawCircleAreaSub(w, h, 4))
ret true
end if
var angle: float :: currentAngle()
var posRot: float :: lib@invRot(w $ float, h $ float, @userX $ float, @userY $ float)
ret(2.0 * lib@pi + posRot - angle).abs() % (2.0 * lib@pi) < ratio * (2.0 * lib@pi)
end func
func drawLadarAreaS(w: int, h: int): bool
ret drawLadarAreaSub(w, h, 0.125)
end func
func drawLadarAreaM(w: int, h: int): bool
ret drawLadarAreaSub(w, h, 0.25)
end func
func drawLadarAreaL(w: int, h: int): bool
ret drawLadarAreaSub(w, h, 0.5)
end func
func drawLadarAreaEdgeSub(w: float, h: float, ratio: float)
const rMin: float :: @px * 1.7
var rMax: float :: [@W, @H].max() $ float * @px * 3.0
var angle: float :: currentAngle()
var x1: float :: rMin * lib@cos(angle) + w $ float
var y1: float :: rMin * lib@sin(angle) + h $ float
var x2: float :: rMax * lib@cos(angle) + w $ float
var y2: float :: rMax * lib@sin(angle) + h $ float
do draw2d@line(x1, y1, x2, y2, @px * 1.5, draw@black)
do angle :+ ratio * 2.0 * lib@pi
do x1 :: rMin * lib@cos(angle) + w $ float
do y1 :: rMin * lib@sin(angle) + h $ float
do x2 :: rMax * lib@cos(angle) + w $ float
do y2 :: rMax * lib@sin(angle) + h $ float
do draw2d@line(x1, y1, x2, y2, @px * 1.5, draw@black)
end func
func drawLadarAreaEdgeS(w: float, h: float)
do drawLadarAreaEdgeSub(w, h, 0.125)
end func
func drawLadarAreaEdgeM(w: float, h: float)
do drawLadarAreaEdgeSub(w, h, 0.25)
end func
func drawLadarAreaEdgeL(w: float, h: float)
do drawLadarAreaEdgeSub(w, h, 0.5)
end func
func currentAngle(): float
ret(draw@cnt() $ float * 0.005) % (2.0 * lib@pi)
end func
end func