この記事は【Kuin Advent Calendar 2016】の4日目の記事です。
【記事中で紹介しているコードについて】
コンパイルが通らない場合など、不具合があれば、@tatt61880まで連絡いただけると助かります。よろしくお願いいたします。
{
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