-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtkmahjongg2.tail
executable file
·636 lines (571 loc) · 16.5 KB
/
tkmahjongg2.tail
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
#######################################################################
## tkmahjongg.tail
# write out icon and desktop file
proc initialize {} {
global desktop_data icon_data
puts "writing desktop file"
set fp [open "/usr/share/applications/tkmahjongg2.desktop" w 0644]
puts -nonewline $fp $desktop_data
close $fp
puts "writing icon"
exec base64 -d > "/usr/share/icons/tkmahjongg2.png" << $icon_data
puts "done"
exit 0
}
# revvid lut
foreach tile $tiles {
set revvid($tile) $tile-inv
set revvid($tile-inv) $tile
}
# default layout
set tileDesc { }
proc set_board name {
global board_data tileDesc
set tileDesc [split [string trim $board_data($name)] "\n"]
}
# tile info
# format: tile_class num_to_create list_of_tiles_it_matches
set tile_info(bamboo1) {0 4 {bamboo1}}
set tile_info(bamboo2) {0 4 {bamboo2}}
set tile_info(bamboo3) {0 4 {bamboo3}}
set tile_info(bamboo4) {0 4 {bamboo4}}
set tile_info(bamboo5) {0 4 {bamboo5}}
set tile_info(bamboo6) {0 4 {bamboo6}}
set tile_info(bamboo7) {0 4 {bamboo7}}
set tile_info(bamboo8) {0 4 {bamboo8}}
set tile_info(bamboo9) {0 4 {bamboo9}}
set tile_info(pin1) {1 4 {pin1}}
set tile_info(pin2) {1 4 {pin2}}
set tile_info(pin3) {1 4 {pin3}}
set tile_info(pin4) {1 4 {pin4}}
set tile_info(pin5) {1 4 {pin5}}
set tile_info(pin6) {1 4 {pin6}}
set tile_info(pin7) {1 4 {pin7}}
set tile_info(pin8) {1 4 {pin8}}
set tile_info(pin9) {1 4 {pin9}}
set tile_info(man1) {2 4 {man1}}
set tile_info(man2) {2 4 {man2}}
set tile_info(man3) {2 4 {man3}}
set tile_info(man4) {2 4 {man4}}
set tile_info(man5) {2 4 {man5}}
set tile_info(man6) {2 4 {man6}}
set tile_info(man7) {2 4 {man7}}
set tile_info(man8) {2 4 {man8}}
set tile_info(man9) {2 4 {man9}}
set tile_info(wind-north) {3 4 {wind-north}}
set tile_info(wind-south) {3 4 {wind-south}}
set tile_info(wind-east) {3 4 {wind-east}}
set tile_info(wind-west) {3 4 {wind-west}}
set tile_info(flower-bamboo) {4 1 {flower-bamboo flower-orchid flower-plum flower-chrysanthemum}}
set tile_info(flower-orchid) {4 1 {flower-bamboo flower-orchid flower-plum flower-chrysanthemum}}
set tile_info(flower-plum) {4 1 {flower-bamboo flower-orchid flower-plum flower-chrysanthemum}}
set tile_info(flower-chrysanthemum) {4 1 {flower-bamboo flower-orchid flower-plum flower-chrysanthemum}}
set tile_info(season-spring) {5 1 {season-spring season-summer season-autumn season-winter}}
set tile_info(season-summer) {5 1 {season-spring season-summer season-autumn season-winter}}
set tile_info(season-autumn) {5 1 {season-spring season-summer season-autumn season-winter}}
set tile_info(season-winter) {5 1 {season-spring season-summer season-autumn season-winter}}
set tile_info(dragon-green) {6 4 {dragon-green}}
set tile_info(dragon-chun) {6 4 {dragon-chun}}
set tile_info(dragon-haku) {6 4 {dragon-haku}}
# RNG seed
set rSeed -1
########################################################################
# process args
catch {rename send {}}
if ![catch {set geometry}] {
catch {wm geometry . $geometry}
}
while {[llength $argv]} {
if {[lindex $argv 0]=="-init"} {
initialize
} elseif {[lindex $argv 0]=="-vb"} {
set vbell [expr 1 - $vbell]
} elseif {[lindex $argv 0]=="-C"} {
set copt [expr 1 - $copt]
} elseif {[lindex $argv 0]=="-l"} {
set startupLayout [lindex $argv 1]
set argv [lreplace $argv 0 0]
} elseif {[lindex $argv 0]=="-b"} {
set rSeed [expr int([lindex $argv 1]) % 1000000000]
set argv [lreplace $argv 0 0]
} else {
puts stderr "unknown option: [lindex $argv 0]"
exit 1
}
set argv [lreplace $argv 0 0]
}
if {$copt && ![array exists image_data]} {
puts "color not available. exiting."
exit 1
}
if {$copt} { set colortab $colortab_color } else { set colortab $colortab_mono }
# create main UI
menu .bar -bg $wbg -fg $wfg
. configure -background $wbg -menu .bar
#
# compute grid size
if {$copt} {
set twidth 128
set theight 128
# trim some transparent width
set wtrim 12
} else {
set twidth 120
set theight 180
set wtrim 0
}
set wtile [expr $twidth - $wtrim * 2]
set htile $theight
set halfw [expr 0.5*$wtile]
set halfh [expr 0.5*$htile]
set xsp [expr $wtile+4]
set ysp [expr $htile+4]
set xhalfw [expr 0.5*$xsp]
set xhalfh [expr 0.5*$ysp]
canvas .c -width [expr $xsp*15+18] -height [expr $ysp*8+22] \
-bd 0 -highlightthickness 0 -background $wbg
pack .c -side bottom -padx 2 -pady 2
.c configure -cursor watch
menu .bar.game -font $blfont -tearoff 0
.bar add cascade -menu .bar.game -label Game -font $blfont
.bar add command \
-label $blbl(undo) \
-font $blfont \
-state disabled \
-command {
set cmd [lindex $undoInfo end]
#if {$sel!=""} { revVid $sel }
if {$sel!=""} { doSel $sel }
if [catch {set undoInfo [lreplace $undoInfo end end]}] { set undoInfo {} }
if ![llength $undoInfo] {
undo_state disabled
}
eval $cmd
}
proc undo_state {{state {}}} {
if {$state == ""} { return [.bar entrycget Undo -state] }
.bar entryconfigure Undo -state $state
}
menu .bar.board -font $blfont -tearoff 0
.bar add cascade -menu .bar.board -label Board -font $blfont
menu .bar.help -font $blfont -tearoff 0
.bar add cascade -menu .bar.help -label Help -font $blfont
.bar.help add command \
-label Help \
-font $blfont \
-command {wm deiconify .xhelp; raise .xhelp}
.bar.help add command \
-label $blbl(about) \
-font $blfont \
-command {
.c delete all
.c configure -cursor crosshair
about
update
bind .c <1> {
.c configure -cursor {}
bind .c <1> {}
draw
}
}
.bar.game add command \
-label $blbl(new) \
-font $blfont -command { if [query] newGame }
.bar.game add command \
-label $blbl(replay) \
-font $blfont \
-command { if [query] replay }
.bar.game add command \
-label Quit \
-font $blfont \
-command { if [query] exit }
.bar.board add command \
-label $blbl(load) \
-font $blfont \
-command {
if [query] {
set f [tk_getOpenFile -parent .c -title {Load Board}]
if {$f!={}} { loadFile $f; newGame }
}
}
.bar.board add separator
foreach board $boards {
.bar.board add radiobutton \
-label $board \
-font $blfont \
-command "newGame $board"
}
proc about {} {
global afont xhalfh xhalfw wfg
.c create text [expr $xhalfw*15] [expr $xhalfh*1.0] -text "TkMahjongg" \
-font $afont -fill $wfg
.c create text [expr $xhalfw*15] [expr $xhalfh*2.6] \
-text "by Mark Hays <[email protected]>" \
-font $afont -fill $wfg
.c create text [expr $xhalfw*15] [expr $xhalfh*4.5] \
-font $afont -fill $wfg \
-anchor center -justify center \
-text \
{Copyright (c) 1997-2019 by Mark Hays. All Rights Reserved.}
.c create text [expr $xhalfw*15] [expr $xhalfh*7] \
-font $afont -fill $wfg \
-anchor n -justify center \
-text \
{Board layouts were pilfered from XMahjongg
XMahjongg Layouts are Copyright (c) 1990 by
Jeff Young <[email protected]>
Graphical Tiles are Copyright by
Martin Persson <[email protected]>
http://www.martinpersson.org
}
}
about
wm resizable . 0 0
wm title . TkMahjongg
update
# if a game is in progress, confirm a destructive operation
proc query {} {
if {[undo_state]!={disabled}} {
return [tk_dialog .qdlg Abort? {Abort Current Game?} {} 0 No Yes]
}
return 1
}
# help display
toplevel .xhelp
wm withdraw .xhelp
wm resizable .xhelp 0 0
wm title .xhelp {TkMahjongg Help}
wm protocol .xhelp WM_DELETE_WINDOW {wm withdraw .xhelp}
scrollbar .xhelp.s -orient vertical -command {.xhelp.t yview}
text .xhelp.t -yscrollcommand {.xhelp.s set} -font $hfont -width 66
button .xhelp.b -text Close -command {wm withdraw .xhelp}
pack .xhelp.b -side bottom -fill x
pack .xhelp.s -side right -fill y
pack .xhelp.t -fill both
.xhelp.t insert end $tkmahjongg_help
.xhelp.t configure -state disabled
# create images
set iList {}
foreach name [array names tile_info] {
foreach {cls n match} $tile_info($name) {
if {$copt} {
image create photo tmp_image -data $image_data($name) -format PNG
image create photo $name
$name copy tmp_image -from $wtrim 0 [expr $wtile + $wtrim] $htile
rename tmp_image {}
}
for {set i 0} {$i<$n} {incr i} {
set in ${name}_$i
if {$copt} {
set image_to_name($in) $name
set reversed($in) 0
set data [$name data -format PNG]
image create photo $in -data $data -format PNG
} else {
image create bitmap $in -data $bitmap_data($name)
$in configure -foreground $tfg
}
lappend iList $in
}
}
}
# this determines if a tile is free
proc isFree tile {
global tab
set tl [.c gettags $tile]
set xy [split [lindex $tl [lsearch -glob $tl *:*:*]] {:}]
set x [lindex $xy 0]
set y [lindex $xy 1]
set l [lindex $xy 2]
set no 0
# check LR
incr x -2
if {[array names tab $x:$y:$l]!={}} { incr no } else {
if {[array names tab $x:[expr $y-1]:$l]!={}} { incr no } else {
if {[array names tab $x:[expr $y+1]:$l]!={}} { incr no }
}
}
incr x 4
if {[array names tab $x:$y:$l]!={}} { incr no } else {
if {[array names tab $x:[expr $y-1]:$l]!={}} { incr no } else {
if {[array names tab $x:[expr $y+1]:$l]!={}} { incr no }
}
}
incr x -2
# check above
incr l
foreach {dx dy} {-1 -1 0 -1 1 -1
-1 0 0 0 1 0
-1 1 0 1 1 1} {
if {[array names tab [expr $x+$dx]:[expr $y+$dy]:$l]!={}} {
incr no
}
}
# done
if {$no>1} { return 0 }
return 1
}
# this gets called when something illegal happens
if !$vbell {
proc deny {} bell
} else {
proc deny {} {
global wfg
set ob [.c cget -background]
.c configure -background $wfg
update
after 100
.c configure -background $ob
update
}
}
if {$copt} {
proc revVid tile {
global image_data image_to_name reversed
set name $image_to_name($tile)
if {$reversed($tile) == 1} {
$tile configure -data $image_data($name)
set reversed($tile) 0
} else {
$tile configure -data $image_data($name-inv)
set reversed($tile) 1
}
}
} else {
proc revVid tile {
set bg [$tile cget -foreground]
set fg [$tile cget -background]
$tile configure -foreground $fg -background $bg
}
}
# this proc performs a move
proc doMove {t1 t2} {
global nTiles sel tab tile_info undoInfo
# see if they're compatible
set type1 [lindex [split $t1 {_}] 0]
set type2 [lindex [split $t2 {_}] 0]
if {[lsearch -exact [lindex $tile_info($type1) 2] $type2]==-1} {
deny
return
}
# success!
# restore selected tile
revVid $t1
# delete them
set u {}
set tl [.c gettags $t1]
set xyl [lindex $tl [lsearch -glob $tl *:*:*]]
unset tab($xyl)
append u [concat put [split $xyl {:}] $t1]
.c delete $t1
.c delete rect_$t1
set tl [.c gettags $t2]
set xyl [lindex $tl [lsearch -glob $tl *:*:*]]
unset tab($xyl)
append u [concat {;} put [split $xyl {:}] $t2 {;} draw {;} incr nTiles 2]
.c delete $t2
.c delete rect_$t2
incr nTiles -2
set sel {}
lappend undoInfo $u
undo_state normal
}
# this processes mouse events
set sel {}
proc doSel v {
global sel
if ![isFree $v] { deny; return }
if {$v==$sel} {
# deselect it
set sel {}
revVid $v
set sel {}
} else {
# do move
if {$sel!=""} {
doMove $sel $v
} else {
revVid $v
set sel $v
}
}
}
########################################################################
# this does global game initialization
proc setup {} {
global sel undoInfo
if {$sel!={}} {revVid $sel}
set sel {}
set undoInfo {}
undo_state disabled
}
# this initializes the tile table
proc initTab {} {
global tab
catch {unset tab}
}
proc seed {{int {}}} {
global rSeed
if {$int != {}} {
set rSeed $int
expr srand($int % 1000000000)
}
}
seed [expr $rSeed < 0 ? [clock seconds] : $rSeed]
proc ranbyte {} {
return [expr int(rand() * 256)]
}
proc pop x {
set p 0
while {$x > 0} {
incr p [expr $x & 1]
set x [expr $x >> 1]
}
return $p
}
proc rprop x {
set i 0
while {[expr $x & ($x + 1)]} {
set x [expr $x | ($x >> (1 << $i))]
incr i
}
return $x
}
proc cl2 x {
if {$x < 1} { error {x must be positive} }
set x [rprop [expr $x - 1]]
return [pop $x]
}
proc rng n {
if {$n == 1} { return 0 }
if {$n < 2} { error {n must be positive} }
set b [cl2 $n]
set B [expr ($b + 7) >> 3]
set N [expr 1 << ($B << 3)]
set S [expr $N / $n]
set d [expr $N % $n]
set L [expr $N - $d]
while {1} {
set j 0
for {set i 0} {$i < $B} {incr i} {
set j [expr ($j << 8) | [ranbyte]]
}
if {$j < $L} { return [expr $j / $S] }
}
}
proc shuffle {} {
global deck iList
set deck $iList
for {set i [expr [llength $deck] - 1]} {$i >= 1} {incr i -1} {
set j [rng $i]
set t [lindex $deck $i]
set l [lreplace $deck $i $i [lindex $deck $j]]
set deck [lreplace $l $j $j $t]
}
}
# this places a tile into the tile table
proc put {x y l img} {
global colortab copt tab
if {!$copt} {$img configure -background [lindex $colortab $l]}
set tab($x:$y:$l) $img
}
# this reads a tile description file
proc loadFile {{filename {}}} {
global tileDesc
if {$filename=={}} return
set tileDesc {}
set nLines 0
set file [open $filename r]
while {[gets $file line]>=0} {
set data [lindex [split $line {#}] 0]
if [llength $data] {
set y [lindex $data 0]
set x [lindex $data 1]
set l [lindex $data 2]
lappend tileDesc [list $y $x $l]
incr nLines
}
}
close $file
if {$nLines!=144} {
puts stderr "deck description $filename bogus"
exit 1
}
}
# this deals the tiles
proc deal {{layout {}}} {
global deck nTiles tileDesc
if {$layout!={}} { set_board $layout }
set nTiles 0
foreach data $tileDesc {
set y [lindex $data 0]
set x [lindex $data 1]
set l [lindex $data 2]
put $x $y $l [lindex $deck $nTiles]
incr nTiles
}
if {$nTiles!=144} {
puts stderr "tile description bogus"
exit 1
}
}
# this renders the table onto the canvas
proc draw {} {
global halfw halfh htile tab wtile xhalfw xhalfh
global tile_info tile_text colortab image_to_name
global copt
.c delete all
for {set level 0} {$level<7} {incr level} {
for {set x 0} {$x<32} {incr x} {
for {set y 0} {$y<16} {incr y} {
if ![catch {set img $tab($x:$y:$level)}] {
set xx [expr $x*$xhalfw+4]
set yy [expr $y*$xhalfh+4]
if {$copt} {
set name $image_to_name($img)
if {$level} {
set idx [expr [llength $colortab] - $level]
set color [lindex $colortab $idx]
set data [$name data -background $color -format PNG]
} else {
set data [$name data -format PNG]
}
$img configure -data $data
} else {
set xx [expr $xx + 18 - $level * 3]
set yy [expr $yy + 18 - $level * 3]
}
.c create image $xx $yy -anchor nw -image $img \
-tags "$x:$y:$level $img"
.c bind $img <1> "doSel $img"
.c raise $img
}
}
}
}
update
}
# this initializes everything and starts a new game
proc newGame {{layout {}}} {
setup
initTab
shuffle
deal $layout
draw
}
# this replays a game
proc replay {} {
global rSeed
setup
seed $rSeed
shuffle
deal
draw
}
newGame $startupLayout
.c configure -cursor {}
# event bindings
bind .c <3> { if {$sel!={}} {doSel $sel} }
bind . <Control-Key-w> { if [query] exit }
focus .
## EOF tkmahjongg.tail