Mine
Die x-te Implementierung der wohl stabilsten Software unter Windows:

#!/usr/bin/wish
package require Tk
bind [winfo class .] <Destroy> exit
# debug
proc -- args #
proc echo args {puts $args}
proc aloud args {
puts $args
uplevel $args
}
namespace path "::tcl::mathop ::tcl::mathfunc"
# mine, 30 cols 16 rows 99 mines
# here to customize
# lassign "30 16 99" cols rows mines
# lassign "8 8 8" cols rows mines
switch [lindex $argv 0] {
child {
lassign {8 8 10} cols rows mines
}
teenie {
lassign {16 16 40} cols rows mines
}
custom {
lassign $argv - cols rows mines
if {$cols eq ""} then {
set cols 16
}
if {$rows eq ""} then {
set rows $cols
}
if {$mines eq ""} then {
set mines [int [sqrt [* $cols $rows 4]]]
}
}
default {
# lassign {8 8 10} cols rows mines
lassign {30 16 99} cols rows mines
}
}
pack [canvas .c\
-width [- [* 25 $cols] 2]\
-height [- [* 25 $rows] 2]\
-background grey70]\
-expand yes -fill both
wm title . Minesweeper
wm resizable . no no
#
# game states
#
variable pressed false
variable init true
set bombChar \u2688
set flagChar \u2691
set flagCharHollow \u2690
proc tile {col row {canvas .c}} {
#
# draw a tile
# make tile responsive
#
global bombChar flagChar
set w 25
set h 3
set x [* $col $w]
set y [* $row $w]
set tags "col$col row$row"
$canvas create text [+ $x 12] [+ $y 12]\
-text ""\
-anchor center\
-font "Helvetica 16 bold"\
-tags "$tags text"
$canvas create polygon\
[+ $x 1] [+ $y 1] [+ $x $w -1] [+ $y 1] [+ $x 1] [+ $y $w -1]\
-fill grey85 -tags "$tags topleft"
$canvas create polygon\
[+ $x 1] [+ $y $w -1] [+ $x $w -1] [+ $y $w -1] [+ $x $w -1] [+ $y 1]\
-fill grey15 -tags "$tags bottomright"
$canvas create rectangle [+ $x $h] [+ $y $h] [+ $x $w -$h] [+ $y $w -$h]\
-fill grey70 -tags "$tags surface" -outline ""
$canvas create text [+ $x 11] [+ $y 11]\
-text ""\
-anchor center\
-font "Helvetica 16 bold"\
-fill white\
-tags "$tags flag"
#
$canvas bind col$col&&row$row&&surface <1> "press $col $row"
$canvas bind col$col&&row$row&&surface <3> "flag $col $row"
$canvas bind col$col&&row$row&&flag <3> "flag $col $row"
$canvas bind col$col&&row$row&&surface\
<Leave> "release $col $row"
$canvas bind col$col&&row$row&&surface\
<ButtonRelease> "
if {\$pressed} then {
if {\$init} then {
init $col $row
} else {
check $col $row
}
setTitle
}
release $col $row
"
}
proc flag {col row {canvas .c}} {
#
# set or unset flag
#
global flagChar
if {[$canvas itemcget col$col&&row$row&&flag -text] eq $flagChar} then {
$canvas itemconfigure col$col&&row$row&&flag -text ""
} else {
$canvas itemconfigure col$col&&row$row&&flag -text $flagChar
}
setTitle
}
proc unflaggedMines {{canvas .c}} {
#
# $mines minus number of flags
#
global rows cols mines
set result $mines
for {set i 0} {$i < $cols} {incr i} {
for {set j 0} {$j < $rows} {incr j} {
if {[$canvas itemcget col$i&&row$j&&flag -text] ne ""} then {
incr result -1
}
}
}
set result
}
proc blink {widget args} {
lassign [info level 0] blink
if {$widget eq "info"} then {
#
# info
#
set result {}
foreach event [after info] {
set info [after info $event]
if {[lindex $info end] eq "timer" &&
[lindex $info 0 0] eq $blink} then {
lassign $info cmd
lassign $cmd blink widget
if {[winfo exists $widget]} then {
set line [list $blink $widget]
set opts [lrange $cmd 2 end]
if {[winfo class $widget] in {Text Canvas}} then {
lappend line -tag [dict get $opts -tag]
}
lappend line -att [dict get $opts -att]
lappend result $line
}
}
}
lsort $result
} elseif {$widget eq "stop"} then {
#
# stop all after-events
#
foreach event [after info] {
set info [after info $event]
if {[lindex $info end] eq "timer" &&
[lindex $info 0 0] eq $blink} then {
after cancel $event
}
}
} elseif {[winfo exists $widget]} then {
if {$args eq "stop"} then {
$blink $widget -action stop
} else {
set class [winfo class $widget]
#
# prepare
#
set item {
-step 0
-red0 0
-green0 0
-blue0 0
-tag blink
-red1 65535
-green1 65535
-blue1 65535
-interval 25
-action continue
}
#
# set default target attributes
#
switch -exact -- $class {
Text {
dict set item -att -foreground
}
Canvas {
dict set item -att -fill
}
default {
dict set item -att -fg
}
}
#
# customize by command line
#
dict for {key val} $args {
dict set item $key $val
}
#
# translate named colors if given
#
if {[dict exists $item -color0]} then {
lassign [winfo rgb . [dict get $item -color0]] r g b
dict set item -red0 $r
dict set item -green0 $g
dict set item -blue0 $b
dict unset item -color0
}
if {[dict exists $item -color1]} then {
lassign [winfo rgb . [dict get $item -color1]] r g b
dict set item -red1 $r
dict set item -green1 $g
dict set item -blue1 $b
dict unset item -color1
}
#
if {[dict get $item -action] eq "continue"} then {
#
# calculate increasement of color
#
dict incr item -step
if {[dict get $item -step] >= 100} then {
dict set item -step 0
}
set pi [expr {
atan2(0,-1)
}]
set factor [expr {
(cos($pi * 2 * [dict get $item -step] / 100) + 1) / 2
}]
#
# generate hexadecimal color string
#
set rrrrggggbbbb #
#
set red0 [dict get $item -red0]
set red1 [dict get $item -red1]
set red [expr {$red0+int(($red1-$red0)*$factor)}]
append rrrrggggbbbb [format %04x $red]
#
set green0 [dict get $item -green0]
set green1 [dict get $item -green1]
set green [expr {$green0+int(($green1-$green0)*$factor)}]
append rrrrggggbbbb [format %04x $green]
#
set blue0 [dict get $item -blue0]
set blue1 [dict get $item -blue1]
set blue [expr {$blue0+int(($blue1-$blue0)*$factor)}]
append rrrrggggbbbb [format %04x $blue]
#
set tag [dict get $item -tag]
set att [dict get $item -att]
switch -exact -- $class {
Canvas {
$widget itemconfigure $tag $att $rrrrggggbbbb
}
Text {
$widget tag configure $tag $att $rrrrggggbbbb
}
default {
$widget configure $att $rrrrggggbbbb
}
}
#
# repeat
#
set interval [dict get $item -interval]
after $interval [list blink $widget {*}$item]
#
} else {
#
# stop blinking of $widget
#
foreach event [after info] {
set info [after info $event]
set line [lindex $info 0]
lassign $line proc arg
if {$proc eq $blink && $arg eq $widget} then {
after cancel $event
}
}
}
}
}
}
proc setTitle {{canvas .c}} {
#
# game status on window title
#
global flagChar cols rows
if {[wm title .] ne "Autschn!"} then {
global mines
set top [winfo toplevel $canvas]
set freeTiles [- [llength [$canvas find withtag surface]] $mines]
if {$freeTiles == 0} then {
wm title $top Success!
bind . <Enter> ""
bind . <Leave> ""
$canvas itemconfigure flag -text $flagChar
for {set i 0} {$i < $cols} {incr i} {
for {set j 0} {$j < $rows} {incr j} {
$canvas bind col$i&&row$j&&surface <1> ""
$canvas bind col$i&&row$j&&surface <3> ""
$canvas bind col$i&&row$j&&flag <3> ""
$canvas bind col$i&&row$j&&surface <Leave> ""
$canvas bind col$i&&row$j&&surface <ButtonRelease> ""
}
}
blink $canvas -tag flag -color0 yellow -color1 white
} else {
wm title $top "$freeTiles tiles, [unflaggedMines] flags"
}
}
}
bind . <Enter> setTitle
bind . <Leave> {
wm title . Mine
}
proc press {col row {canvas .c}} {
#
# visual response: pressed tile "sunken"
#
if {[$canvas itemcget row$row&&col$col&&flag -text] eq ""} then {
variable pressed true
$canvas itemconfigure col$col&&row$row&&topleft -fill grey15
$canvas itemconfigure col$col&&row$row&&bottomright -fill grey85
$canvas itemconfigure col$col&&row$row&&surface -fill grey65
}
}
proc release {col row {canvas .c}} {
#
# visual response: pressed tile "raised"
#
variable pressed false
$canvas itemconfigure col$col&&row$row&&topleft -fill grey85
$canvas itemconfigure col$col&&row$row&&bottomright -fill grey15
$canvas itemconfigure col$col&&row$row&&surface -fill grey70
}
proc takeNfromList {n liste} {
#
# take n different elements from list (none twice!)
#
if {$n > 0} then {
set i [expr {int(rand()*[llength $liste])}]
list [lindex $liste $i] {*}[takeNfromList [- $n 1] [lreplace $liste $i $i]]
}
}
proc init {col row {canvas .c}} {
#
# On first pressed tile,
# init game such that this tile has no mine!
# (Some kind of humanity ...)
#
global rows cols mines
global bombChar
variable init
if {$init} then {
set init false
# hide 99 mines everywhere, but not at $col $row
# first, collect fields
for {set i 0} {$i < $cols} {incr i} {
for {set j 0} {$j < $rows} {incr j} {
if {$col != $i && $row != $j} then {
lappend fields "$i $j"
}
}
}
# hide $mines mines
set mineIndices [takeNfromList $mines $fields]
foreach idx $mineIndices {
lassign $idx x y
$canvas itemconfigure col$x&&row$y&&text -text $bombChar
}
# write num of neighboured mines
for {set i 0} {$i < $cols} {incr i} {
for {set j 0} {$j < $rows} {incr j} {
set tags col$i&&row$j&&text
if {[$canvas itemcget $tags -text] ne $bombChar} then {
set count 0
foreach di {-1 0 1} {
foreach dj {-1 0 1} {
if {[$canvas itemcget col[+ $i $di]&&row[+ $j $dj]&&text -text] eq
$bombChar} then {
incr count
}
}
}
if {$count > 0} then {
$canvas itemconfigure col$i&&row$j&&text\
-text $count\
-fill [lindex {black
blue4
green4
red4
grey25
blue4
green4
red4
grey25} $count]
}
}
}
}
check $col $row
}
}
proc check {col row {canvas .c}} {
#
# Check pressed tile on mine
#
global bombChar rows cols mines
if {[$canvas itemcget col$col&&row$row&&flag -text] eq ""} then {
if {[$canvas itemcget col$col&&row$row&&text -text] eq $bombChar} then {
#
# hit a mine, finish game:
#
bumm $col $row $canvas
} elseif {[$canvas find withtag row$row&&col$col&&surface] ne ""} then {
#
# remove mine, unhide number of neighboured mines
#
$canvas delete row$row&&col$col&&!text
if {[$canvas itemcget col$col&&row$row&&text -text] eq ""} then {
#
# if zero neighboured mines, check neighboured tiles too
#
check [- $col 1] [- $row 1] $canvas
check [- $col 1] $row $canvas
check [- $col 1] [+ $row 1] $canvas
#
check $col [- $row 1] $canvas
check $col [+ $row 1] $canvas
#
check [+ $col 1] [- $row 1] $canvas
check [+ $col 1] $row $canvas
check [+ $col 1] [+ $row 1] $canvas
}
}
}
}
proc bumm {col row {canvas .c}} {
#
# mine hit, game over
#
global rows cols flagCharHollow bombChar
wm title [winfo toplevel $canvas] Autschn!
bind . <Enter> ""
bind . <Leave> ""
for {set i 0} {$i < $cols} {incr i} {
for {set j 0} {$j < $rows} {incr j} {
$canvas bind col$i&&row$j&&surface <1> ""
$canvas bind col$i&&row$j&&surface <3> ""
$canvas bind col$i&&row$j&&flag <3> ""
$canvas bind col$i&&row$j&&surface <Leave> ""
$canvas bind col$i&&row$j&&surface <ButtonRelease> ""
if {$i == $col && $j == $row} then {
# hit the mine, sorry ...
$canvas delete col$i&&row$j&&!text
# $canvas itemconfigure col$i&&row$j&&text -fill red
blink $canvas -tag col$i&&row$j&&text\
-color0 yellow -color1 red -interval 10
} elseif {[$canvas itemcget col$i&&row$j&&flag -text] ne ""} then {
# flag set
if {[$canvas itemcget col$i&&row$j&&text -text] ne $bombChar} then {
# but no mine under it
$canvas itemconfigure col$i&&row$j&&flag\
-text $flagCharHollow\
-font "Helvetica 16 bold overstrike"\
-fill red
}
} elseif {[$canvas itemcget col$i&&row$j&&text -text] eq $bombChar} then {
$canvas delete col$i&&row$j&&!text
}
}
}
}
apply {
{cols rows} {
.c del all
for {set i 0} {$i < $cols} {incr i} {
for {set j 0} {$j < $rows} {incr j} {
tile $i $j
}
}
}
} $cols $rows
14.3.2022
<< | Heimatseite | Verzeichnis | Stichworte | Autor | >>