proc main {} { global g set g(size) 40 set side [expr $g(size)*9] pack [canvas .c -width $side -height [expr $g(size)*10] -bg purple] #------- fixed cards card .c L 2 1 1 card .c T 0 3 1 card .c T 0 5 1 card .c L 1 7 1 card .c T 1 1 3 card .c T 1 3 3 card .c T 0 5 3 card .c T 3 7 3 card .c T 1 1 5 card .c T 2 3 5 card .c T 3 5 5 card .c T 3 7 5 card .c L 3 1 7 card .c T 2 3 7 card .c T 2 5 7 card .c L 0 7 7 #--- arrows set y0 [expr $g(size)*0.7] set y1 [expr $g(size)*8] set y2 [expr $g(size)*8.3] foreach x {2.5 4.5 6.5} { set x0 [expr $x*$g(size)] foreach line [list [list $x0 $y0 $x0 $g(size)] \ [list $x0 $y2 $x0 $y1] [list $y0 $x0 $g(size) $x0]\ [list $y2 $x0 $y1 $x0]] { .c create line $line -arrow last -width 5 \ -fill orange -tag arrow } } #--- movable cards set cards [split [rep I 13][rep L 15][rep T 6] ""] set y 1 foreach row { {0 1 0 1 0 1 0} {1 1 1 1 1 1 1} {0 1 0 1 0 1 0} {1 1 1 1 1 1 1} {0 1 0 1 0 1 0} {1 1 1 1 1 1 1} {0 1 0 1 0 1 0} } { set x 1 foreach col $row { if $col { card .c [ldraw cards] [lpick {0 1 2 3}] $x $y } incr x } incr y } #--- the last card is off-board card .c $cards 0 4 9 mvcard movable .c mvcard } proc card {w shape rot x y {tag ""}} { global g $w create rect [expr {$x*$g(size)}] [expr {$y*$g(size)}] \ [expr {($x+1)*$g(size)}] [expr {($y+1)*$g(size)}] \ -fill brown -tag $tag switch -- $shape { I {set coo {0 0.3 1 0.3 1 0.7 0 0.7}} L {set coo {0.3 1 0.3 0.3 1 0.3 1 0.7 0.7 0.7 0.7 1}} T {set coo {0 .7 0 .3 .3 .3 .3 0 .7 0 .7 .3 1 .3 1 .7}} } set id [$w create poly $coo -fill beige -tag $tag] rotate $w $id $rot 0.5 0.5 $w scale $id 0 0 $g(size) $g(size) $w move $id [expr {$x*$g(size)}] [expr {$y*$g(size)}] } interp alias {} rep {} string repeat proc rotate {w tag rot xm ym} { set coords {} foreach {x y} [$w coords $tag] { set r [expr hypot($xm-$x,$ym-$y)] set a [expr atan2($ym-$y,$xm-$x)-$rot/2.*acos(-1)] set x2 [expr $xm+cos($a)*$r] set y2 [expr $ym+sin($a)*$r] lappend coords $x2 $y2 } $w coords $tag $coords } proc rotate2 {w tag rot} { foreach id [$w find withtag $tag] { if {[$w type $id]=="polygon"} {set poly $id; break} } foreach {x0 y0 x1 y1} [$w bbox $tag] break set xm [expr {($x1+$x0)/2.}] set ym [expr {($y1+$y0)/2.}] rotate $w $poly $rot $xm $ym } proc movable {w tag} { $w bind $tag <1> {set g(x) %x; set g(y) %y} $w bind $tag <B1-Motion> [list move %W $tag %x %y] $w bind $tag <ButtonRelease-1> [list release %W $tag] set poly {} $w bind $tag <3> [list rotate2 $w $tag 3] } proc move {w tag x y} { global g $w move $tag [expr {$x-$g(x)}] [expr {$y-$g(y)}] array set g [list x $x y $y] } proc release {w tag} { global g #--- snap card in exact place foreach {x y - -} [$w bbox $tag] break set s2 [expr {$g(size)/2.}] set dx [expr -round($x-$s2)%$g(size)-$s2-1] set dy [expr -round($y-$s2)%$g(size)-$s2-1] $w move $tag $dx $dy #--- on arrow? foreach {x0 y0 x1 y1} [$w bbox $tag] break set ok 0 foreach item [$w find overlapping $x0 $y0 $x1 $y1] { if {[$w type $item] eq "line"} {incr ok; break} } if !$ok return set dir {0 0} if {$x0==-1} {set x1 [expr $g(size)*8+1]; set dir {1 0}} if {$x0==$g(size)*8-1} {set x0 -1; set dir {-1 0}} if {$y0==-1} {set y1 [expr $g(size)*8+1]; set dir {0 1}} if {$y0==$g(size)*8-1} {set y0 -1; set dir {0 -1}} if {$dir ne {0 0}} { foreach {dx dy} $dir break foreach item [$w find enclosed $x0 $y0 $x1 $y1] { if {[$w type $item] eq "line"} continue $w move $item [expr $dx*$g(size)] [expr $dy*$g(size)] } $w dtag $tag } #--- find out-shifted card switch -- $dir { {0 1} {set y0 [expr $g(size)*8-1]} {0 -1} {set y1 [expr $g(size)-1]} {1 0} {set x0 [expr $g(size)*8-1]} {-1 0} {set x1 [expr $g(size)-1]} } $w addtag $tag overlapping $x0 $y0 $x1 $y1 $w dtag arrow $tag movable $w $tag $w raise $tag }#--- General utilities:
proc ldraw listVar { upvar 1 $listVar list set pos [expr {int(rand()*[llength $list])}] K [lindex $list $pos] [set list [lreplace $list $pos $pos]] } proc K {a b} {set a} proc lpick list {lindex $list [expr {int(rand()*[llength $list])}]} bind . <F1> {console show} main bind . <Escape> {exec wish $argv0 &; exit}
Category Games