- Left and right arrow keys to move the piece
- Up to rotate it
- If you hold down the down arrow, it moves down a tad faster than normal ... this could definitely be fixed.
- Spacebar drops the piece
- F2 starts a new game
uniquename 2013aug01Surprisingly good for such a small amount of code. It deserves an image:
namespace eval tetris {
variable grid
variable run
variable step 0
variable level 0
variable score 0
variable lines 0
variable piece
variable blocksize 10
variable pieces
array set pieces {
0 {lightblue 4 0 2 2 {1 1 1 1}}
1 {yellow 4 0 4 3 {0 1 0 0 1 0 0 1 0 0 1 0}}
2 {purple 4 0 3 3 {0 1 0 1 1 0 1 0 0}}
3 {green 4 0 3 3 {0 1 0 0 1 1 0 0 1}}
4 {red 4 0 3 3 {0 1 0 1 1 1 0 0 0}}
5 {blue 4 0 3 3 {0 0 1 1 1 1 0 0 0}}
6 {orange 4 0 3 3 {1 0 0 1 1 1 0 0 0}}
}
proc build {} {
variable blocksize
wm title . "TinyTetris"
# canvas should be 15x20 blocks
# the game table is 10x20
# the right 5 blocks are for the nextpiece
canvas .c -width [expr {$blocksize*15 + 2}] -height [expr {$blocksize*20 + 2}]
.c create rectangle 0 0 [expr {$blocksize*10 + 2}] [expr {$blocksize*20 + 2}] -fill black -outline red
label .l1 -text "POINTS" -font {courier 8}
label .l2 -anchor n -font {courier 8} -textvariable tetris::score
label .l3 -text "LEVEL" -font {courier 8}
label .l4 -anchor n -font {courier 8} -textvariable tetris::level
label .l5 -text "LINES" -font {courier 8}
label .l6 -anchor n -font {courier 8} -textvariable tetris::lines
grid .l1 .c -sticky w
grid .l2 ^ -sticky nw
grid .l3 ^ -sticky w
grid .l4 ^ -sticky nw
grid .l5 ^ -sticky w
grid .l6 ^ -sticky nw
grid rowconfigure . 1 -weight 1
grid rowconfigure . 3 -weight 1
grid rowconfigure . 5 -weight 1
set f [frame .debug]
label $f.d1 -text "step : "
label $f.d2 -textvariable tetris::step
label $f.d3 -text "score: "
label $f.d4 -textvariable tetris::score
label $f.d5 -text "piece: "
label $f.d6 -textvariable tetris::piece
# etc
button $f.next -text "next" -command tetris::nextStep
grid $f.d1 $f.d2
grid $f.d3 $f.d4
grid $f.d5 $f.d6
grid $f.next
#pack $f -side top
bind . <Up> "tetris::rotateCW"
bind . <Left> "tetris::move left"
bind . <Right> "tetris::move right"
bind . <space> "tetris::drop"
bind . <Down> "tetris::nextStep"
bind . <F2> "tetris::restart"
restart
}
proc restart {} {
clearGrid
variable run 1
after 1000 tetris::newPiece
}
proc endGame {} {
variable run 0
}
proc clearGrid {} {
variable lines 0
variable score 0
variable step 0
variable level 0
variable run 1
variable nextpiece
variable grid
foreach i {0 1 2 3 4 5 6 7 8 9} {
for {set j 0} {$j < 20} {incr j} {
set grid($i,$j) ""
}
}
.c delete block
set nextpiece [randomPiece]
cycle
}
proc cycle {} {
variable level
variable run
if {!$run} { return }
nextStep
set speed [expr {100 - 8*$level}]
if {$speed < 20} { set speed 20 }
after $speed tetris::cycle
}
proc newPiece {} {
variable piece
variable nextpiece
variable step 0
variable blocksize
set piece $nextpiece
set nextpiece [randomPiece]
if {[checkPiece] == 0} {
endGame
return
}
drawPiece
.c delete nextpiece
foreach {color X Y h w piecegrid} $nextpiece {}
for {set j 0} {$j < $h} {incr j} {
for {set i 0} {$i < $w} {incr i} {
if {[lindex $piecegrid [expr {$i + $j*$w}]] != 0} {
set x [expr {12*$blocksize + $i*$blocksize}]
set y [expr {3*$blocksize + $j*$blocksize}]
set ID [.c create rectangle $x $y [expr {$x+$blocksize}] [expr {$y+$blocksize}] \
-fill $color -outline gray25 -tags {block nextpiece}]
lset nextpiece 5 [expr {$i + $j*$w}] $ID
}
}
}
}
proc randomPiece {} {
variable pieces
return $pieces([expr {int(rand()*7)}])
}
proc checkPiece {} {
variable piece
variable grid
foreach {color X Y h w piecegrid} $piece {}
for {set j 0} {$j < $h} {incr j} {
for {set i 0} {$i < $w} {incr i} {
if {[lindex $piecegrid [expr {$i + $j*$w}]] == 0} { continue }
set x [expr {$X+$i}]
set y [expr {$Y+$j}]
if {$x < 0} { return 0 }
if {$x > 9} { return 0 }
if {$grid($x,$y) != ""} { return 0 }
}
}
return 1
}
proc drawPiece {} {
variable grid
variable piece
variable step
variable blocksize
.c delete piece
foreach {color X Y h w piecegrid} $piece {}
for {set j 0} {$j < $h} {incr j} {
for {set i 0} {$i < $w} {incr i} {
if {[lindex $piecegrid [expr {$i + $j*$w}]] != 0} {
set x [expr {($X+$i)*$blocksize + 1}]
set y [expr {($Y+$j)*$blocksize}]
if {[expr {$step%$blocksize}] > 0} {
incr y [expr {$step%$blocksize - $blocksize}]
}
set ID [.c create rectangle $x $y [expr {$x+$blocksize}] [expr {$y+$blocksize}] \
-fill $color -outline gray25 -tags {block piece}]
lset piece 5 [expr {$i + $j*$w}] $ID
}
}
}
}
proc rotateCCW {} {
variable grid
variable piece
if {$piece == ""} { return }
set oldpiece $piece
foreach {color X Y h w piecegrid} $piece {}
set newgrid $piecegrid
set s [expr {$h-1}]
for {set j 0} {$j < $h} {incr j} {
for {set i 0} {$i < $w} {incr i} {
set id [lindex $piecegrid [expr {$i + $j*$w}]]
set p $j
set q [expr {$s-$i}]
lset newgrid [expr {$p + $q*$h}] $id
}
}
set piece [list $color $X $Y $w $h $newgrid]
if {[checkPiece]} {
drawPiece
} else {
set piece $oldpiece
}
}
proc rotateCW {} {
variable grid
variable piece
if {$piece == ""} { return }
set oldpiece $piece
foreach {color X Y h w piecegrid} $piece {}
set newgrid $piecegrid
set s [expr {$h-1}]
for {set j 0} {$j < $h} {incr j} {
for {set i 0} {$i < $w} {incr i} {
set id [lindex $piecegrid [expr {$i + $j*$w}]]
set p [expr {$s-$j}]
set q $i
lset newgrid [expr {$p + $q*$h}] $id
}
}
set piece [list $color $X $Y $w $h $newgrid]
if {[checkPiece]} {
drawPiece
} else {
set piece $oldpiece
}
}
proc drop {} {
variable piece
while {$piece != ""} { nextStep }
}
proc move {dir} {
variable piece
variable blocksize
if {$piece == ""} { return }
foreach {color X Y h w piecegrid} $piece {}
switch $dir {
left { set dx -1 }
right { set dx 1 }
}
lset piece 1 [expr {$X + $dx}]
if {[checkPiece] == 0} {
lset piece 1 $X
return
}
.c move piece [expr {$dx*$blocksize}] 0
}
proc nextStep {} {
variable step
variable piece
variable grid
variable blocksize
if {$piece == ""} { return }
foreach {color X Y h w piecegrid} $piece {}
incr step
.c move piece 0 1
set offset [expr {$step%$blocksize}]
if {$offset == 1} { lset piece 2 [incr Y] }
if {$offset != 0} {
return
}
# foreach block in piece
# if Y+1 is a block, call finish
# else incr Y
for {set i 0} {$i < $w} {incr i} {
for {set j [expr {$h-1}]} {$j >= 0} {incr j -1} {
if {[lindex $piecegrid [expr {$i + $j*$w}]] != 0} {
set x [expr {$X+$i}]
set y [expr {$Y+$j+1}]
if {$y == 20 || $grid($x,$y) != ""} {
finishPiece
return
}
break
}
}
}
}
proc finishPiece {} {
variable grid
variable piece
variable level
variable score
variable lines
foreach {color X Y h w piecegrid} $piece {}
for {set j 0} {$j < $h} {incr j} {
for {set i 0} {$i < $w} {incr i} {
set id [lindex $piecegrid [expr {$i + $j*$w}]]
if {$id != 0} {
set grid([expr {$X+$i}],[expr {$Y+$j}]) $id
}
}
}
.c itemconfig piece -tags {block}
set nlines 0
for {set y $Y} {$y < [expr {$Y+$h}]} {incr y} {
incr nlines [checkLine $y]
}
incr score [expr {($level+1)*[lindex {0 40 100 300 1200} $nlines]}]
incr lines $nlines
set level [expr {$lines/10}]
set piece {}
after 500 "tetris::newPiece"
}
proc checkLine {y} {
variable grid
variable blocksize
if {$y == 20} { return 0 }
set ids {}
for {set x 0} {$x < 10} {incr x} {
lappend ids $grid($x,$y)
if {$grid($x,$y) == ""} { return 0 }
}
foreach id $ids { .c delete $id }
while {$y > 0} {
for {set x 0} {$x < 10} {incr x} {
set id $grid($x,[expr {$y-1}])
set grid($x,$y) $id
.c move $id 0 $blocksize
}
incr y -1
}
for {set x 0} {$x < 10} {incr x} {
set grid($x,0) ""
}
return 1
}
}
tetris::buildCategory Games
