Kuin Advent Calendar 2016 - 4日目

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

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

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


classを使用しているプログラムの例です。
テトリスで遊べるKuinのコードです。
[Image:tetris.kn.png]
tetris.kn
{
	tetris.kn:
		Created by @tatt61880
			https://twitter.com/tatt61880
			https://github.com/tatt61880

	Thanks to tetris.cpp (MIT License):
		Created by @tkihira, @DQNEO
			http://twitter.com/tkihira/status/262437799775592449
			https://github.com/DQNEO/CppTetris
}
const px: float :: 24.0
const bx: float :: @px - 2.0
const W: int :: 10
const H: int :: 20
const WW: int :: @W * @px $ int
const WH: int :: @H * @px $ int
const MAX_PIECE: int :: 7

func main()
	var wndMain: wnd@Wnd :: wnd@makeWnd(null, %aspect, @WW, @WH, "Tetris")
	var drawMain: wnd@Draw :: wnd@makeDraw(wndMain, 0, 0, @WW, @WH, %scale, %scale, false)
	while(wnd@act())
		do @game()
	end while
end func

func game()
	{ init piece }
	do @piece :: #[@MAX_PIECE + 1]@Piece
	do @piece[0] :: (#@Piece).init(1, 0, 0, 0, 0, 0, 0) {null}
	do @piece[1] :: (#@Piece).init(2,-1, 0, 1, 0, 2, 0) {I}
	do @piece[2] :: (#@Piece).init(1, 1, 0, 0, 1, 1, 1) {O}
	do @piece[3] :: (#@Piece).init(2,-1, 0, 0, 1, 1, 1) {S}
	do @piece[4] :: (#@Piece).init(2,-1, 1, 0, 1, 1, 0) {Z}
	do @piece[5] :: (#@Piece).init(4,-1, 0, 1, 0, 1,-1) {J}
	do @piece[6] :: (#@Piece).init(4,-1, 0, 1, 0,-1,-1) {L}
	do @piece[7] :: (#@Piece).init(4,-1, 0, 0,-1, 1, 0) {T}

	{ init board }
	do @board :: #[@W + 2, @H + 5]int
	for x(0, @W + 1)
		for y(0, @H + 4)
			if(x = 0 | x = @W + 1 | y = 0)
				do @board[x][y] :: 1
			else
				{ Kuinの場合、デフォルトで0が入っているので、この処理は不要です }
				{ do @board[x][y] :: 0 }
			end if
		end for
	end for

	{ init current }
	do @current :: #@Status
	do @current.x :: 5
	do @current.y :: @H + 1
	do @current.type :: lib@rnd(0, @MAX_PIECE - 1) + 1
	do @current.rotate :: 0

	do @putBlock(@current, false)
	var w: int :: 0
	do @paused :: false
	while(wnd@act())
		if(!@isPaused())
			if(w % 4 = 0)
				if(@processInput())
					do w :: 0
				end if
			end if
			if(w % 10 = 0)
				do @blockDown()
			end if
			do w :+ 1
		end if
		do @showBoard()
		if(@resetCheck())
			ret
		end if
	end while
end func

func processInput(): bool
	var retVal: bool :: false
	{
	tetris.cppではcurrentは構造体です。代入すると値がコピーされることになります。
	tetris.knではcurrentはクラスです。 代入すると同じインスタンスを指すことになってしまうので、正しく動作させるにはコピーする必要があります。
	}
	;var n: @Status :: @current {代入では上手くいきません!}
	var n: @Status :: ##@current
	if(input@pad(0, %left) > 0)
		do n.x :- 1
	elif(input@pad(0, %right) > 0)
		do n.x :+ 1
	elif(input@pad(0, %up) > 0 & input@pad(0, %up) < 5)
		do n.rotate :+ 1
	elif(input@pad(0, %down) > 0)
		do n.y :- 1
		do retVal :: true
	end if

	if(n.x <> @current.x | n.y <> @current.y | n.rotate <> @current.rotate)
		do @deleteBlock(@current)
		if(@putBlock(n, false))
			do @current :: n
		else
			do @putBlock(@current, false)
		end if
	end if

	ret retVal
end func

func deleteBlock(s: @Status): bool
	do @board[s.x][s.y] :: 0

	for i(0, 2)
		var dx: int :: @piece[s.type].p[i].x
		var dy: int :: @piece[s.type].p[i].y
		var r: int :: s.rotate % @piece[s.type].rotate
		for(0, r - 1)
			var nx: int :: dx
			var ny: int :: dy
			do dx :: ny
			do dy :: -nx
		end for
		do @board[s.x + dx][s.y + dy] :: 0
	end for
	ret true
end func

func blockDown()
	do @deleteBlock(@current)
	do @current.y :- 1
	if(!@putBlock(@current, false))
		do @current.y :+ 1
		do @putBlock(@current, false)

		do @deleteLine()

		do @current.x :: 5
		do @current.y :: @H + 1
		do @current.type :: lib@rnd(0, @MAX_PIECE - 1) + 1
		do @current.rotate :: 0
		if(!@putBlock(@current, false))
			do @gameover()
		end if
	end if
end func

func showBoard()
	for x(1, @W)
		for y(1, @H)
			do @bitBlt((x - 1) $ float * @px, (@H - y) $ float * @px, @board[x][y])
		end for
	end for
	do draw@render(60)
end func

func resetCheck() : bool
	{スペースキーを長押ししたときにリスタートします。}
	ret (input@pad(0, %a) = 30)
end func

var paused: bool
func isPaused() : bool
	{スペースキーを押ししたときにポーズ状態をトグルします。}
	if((input@pad(0, %a) = 1))
		do @paused :: !@paused
	end if
	ret @paused
end func

var board: [][]int
var piece: []@Piece
var current: @Status

class Piece()
	+var rotate: int
	+var p: []@Pos
	+func init(r: int, p0x: int, p0y: int, p1x: int, p1y: int, p2x: int, p2y: int): @Piece
		do me.rotate :: r
		do me.p :: #[3]@Pos
		do me.p[0] :: (#@Pos).init(p0x, p0y)
		do me.p[1] :: (#@Pos).init(p1x, p1y)
		do me.p[2] :: (#@Pos).init(p2x, p2y)
		ret me
	end func
end class

class Pos()
	+var x: int
	+var y: int
	+func init(x: int, y: int): @Pos
		do me.x :: x
		do me.y :: y
		ret me
	end func
end class

class Status()
	+var x: int
	+var y: int
	+var type: int
	+var rotate: int
end class

func putBlock(s: @Status, action: bool): bool
	if(@board[s.x][s.y] <> 0)
		ret false
	end if

	if(action)
		do @board[s.x][s.y] :: s.type
	end if

	for i(0, 2)
		var dx: int :: @piece[s.type].p[i].x
		var dy: int :: @piece[s.type].p[i].y
		var r: int :: s.rotate % @piece[s.type].rotate
		for(0, r - 1)
			var nx: int :: dx
			var ny: int :: dy
			do dx :: ny
			do dy :: -nx
		end for
		if(@board[s.x + dx][s.y + dy] <> 0)
			ret false
		end if
		if(action)
			do @board[s.x + dx][s.y + dy] :: s.type
		end if
	end for
	if(!action)
		do @putBlock(s, true)
	end if
	ret true
end func

func bitBlt(x: float, y: float, id: int)
	{ セガテトリスの配色にしました。 }
	switch(id)
	case 0 {背景}
		do draw@rect(x,       y,       @px, @px, 0xFFDDDDFF)
	case 1 {I}
		do draw@rect(x + 1.0, y + 1.0, @bx, @bx, 0xFFFF0000)
	case 2 {O}
		do draw@rect(x + 1.0, y + 1.0, @bx, @bx, 0xFFFFFF00)
	case 3 {S}
		do draw@rect(x + 1.0, y + 1.0, @bx, @bx, 0xFFFF00FF)
	case 4 {Z}
		do draw@rect(x + 1.0, y + 1.0, @bx, @bx, 0xFF00FF00)
	case 5 {J}
		do draw@rect(x + 1.0, y + 1.0, @bx, @bx, 0xFF0000FF)
	case 6 {L}
		do draw@rect(x + 1.0, y + 1.0, @bx, @bx, 0xFFFF8000)
	case 7 {T}
		do draw@rect(x + 1.0, y + 1.0, @bx, @bx, 0xFF00FFFF)
	end switch
end func

func deleteLine()
	for y(1, @H + 2)
		var flag: bool :: true
		for x(1, @W)
			if(@board[x][y] = 0)
				do flag :: false
			end if
		end for

		if(flag)
			for j(y, @H + 2)
				for i(1, @W)
					do @board[i][j] :: @board[i][j + 1]
				end for
			end for
			do y :- 1
		end if
	end for
end func

func gameover()
	for x(1, @W)
		for y(1, @H)
			if(@board[x][y] <> 0)
				do @board[x][y] :: 1
			end if
		end for
	end for
	while(wnd@act()) {KillTimer(hMainWindow, 100)の代わりの無限ループ}
		do @showBoard()
		if(@resetCheck())
			ret
		end if
	end while
end func


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