Kuin Advent Calendar 2016 - 3日目

この記事は【Kuin Advent Calendar 2016】の3日目の記事です。

←前の日の記事   →次の日の記事

【記事中で紹介しているコードについて】
コンパイルが通らない場合など、不具合があれば、@tatt61880まで連絡いただけると助かります。よろしくお願いいたします。


GUIアプリケーションを簡単に作成できます。
迷路を出力するKuinのコードです。
[Image:maze.kn.png]
maze.kn
{迷路です。ゴールすると、ゴールの位置が反対側になって迷路が再構築されるため、無限に遊べます。}
{スペースキーを押すと、表示範囲が変わります。}

{ある程度小さいサイズの方がおすすめです。}
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


←前の日の記事   →次の日の記事