A little Pachisi game

Richard Suchenwirth 2002-06-06 -- Here's a little Pachisi game (in Germany known as Mensch aergere dich nicht), featuring the classical board, a lightly animated die, and moving pieces. All dimensions are computed from the -size switch, so maybe change that to suit your likings. As usual, playing is left to the humans. See also Tcl/Tk games, and enjoy!

WikiDbImage pachisi.jpg

MNO - This game is also known (in England at least) as Ludo

package require Tk
proc pachisi {w args} {
    array set opts {
        -size 25 -bg LightBlue1 -fg white -colors {red green yellow blue}
    }
    array set opts $args
    set hw [expr 14*$opts(-size)]
    canvas $w -bg $opts(-bg) -height $hw -width $hw
    set m [expr $hw/2]
    set d $opts(-size)
    set x [expr $d * 1.25]
    set x0 $x
    set y [expr $d * 1.25]
    $w create line $x0 [expr $m-$y] [expr $m-$x0] [expr $m-$y] -width 2
    $w create line $x0 [expr $m+$y] [expr $m-$x0] [expr $m+$y] -width 2
    $w create line [expr $m+$x0] [expr $m-$y] [expr 2*$m-$x0] [expr $m-$y]\
         -width 2
    $w create line [expr $m+$x0] [expr $m+$y] [expr 2*$m-$x0] [expr $m+$y]\
        -width 2
    $w create line [expr $m-$y] $x0 [expr $m-$y] [expr $m-$x0] -width 2
    $w create line [expr $m+$y] $x0 [expr $m+$y] [expr $m-$x0] -width 2
    $w create line [expr $m-$y] [expr $m+$x0] [expr $m-$y] [expr 2*$m-$x0]\
        -width 2
    $w create line [expr $m+$y] [expr $m+$x0] [expr $m+$y] [expr 2*$m-$x0]\
        -width 2
    $w create line [expr $y-$d/2] [expr $m-$d] [expr $y-$d/2] [expr $m+$d]\
        -width 2
    $w create line [expr $m*2-$y+$d/2] [expr $m-$d] [expr $m*2-$y+$d/2]\
            [expr $m+$d] -width 2
    $w create line [expr $m-$d] [expr $y-$d/2] [expr $m+$d] [expr $y-$d/2]\
        -width 2
    $w create line [expr $m-$d] [expr $m*2-$y+$d/2] [expr $m+$d]\
        [expr $m*2-$y+$d/2] -width 2
    $w create line [expr $m+5*$d] [expr $m+2*$d] [expr $m+6*$d]\
        [expr $m+2*$d] -arrow first
    $w create line [expr $m-2*$d] [expr $m+5*$d] [expr $m-2*$d]\
        [expr $m+6*$d] -arrow first
    $w create line [expr $m-5*$d] [expr $m-2*$d] [expr $m-6*$d]\
        [expr $m-2*$d] -arrow first
    $w create line [expr $m+2*$d] [expr $m-5*$d] [expr $m+2*$d]\
        [expr $m-6*$d] -arrow first
    foreach i {1 2 3 4 5} {
        point8 $w $m $x $y $d $opts(-fg)
        set x [expr $x+$d*1.25]
    }
    set x [expr $x-$d*1.25]
    set y 0
    point8 $w $m $x $y $d $opts(-fg)
    set xm [expr $x+$m]
    set co $opts(-colors)
    set d2 [expr $d*0.75]
    set d15 $d2*2
    pnest $w $m+$x-$d $d15 $d2 [lindex $co 0]
    pnest $w $m+$x-$d $m+$x-$d $d2 [lindex $co 1]
    pnest $w $d15 $m+$x-$d $d2 [lindex $co 2]
    pnest $w $d15 $m-$x+$d $d2 [lindex $co 3]
    for {set i 0;set y [expr $d*2]} {$i<4} {incr i;set y [expr $y+$d]} {
        point $w $m $y $d2 [lindex $co 0]
        point $w $m*2-$y $m $d2 [lindex $co 1]
        point $w $m $m*2-$y $d2 [lindex $co 2]
        point $w $y $m $d2 [lindex $co 3]
    }
    $w itemconfig [$w find closest [expr $m+$d] $d] -fill [lindex $co 0]
    $w itemconfig [$w find closest $xm [expr $m+$d]] -fill [lindex $co 1]
    $w itemconfig [$w find closest [expr $m-$d] $xm] -fill [lindex $co 2]
    $w itemconfig [$w find closest $d [expr $m-$d]] -fill [lindex $co 3]
    set mvbody {set g(x) [@w canvasx %x]; set g(y) [@w canvasy %y]}
    regsub -all @w $mvbody $w mvbody
    $w bind mv <1> $mvbody
    canvas:die $w [expr $m-12.5] [expr $m-12.5]
    set w
}
proc pnest {w x y d color} {
    set fsize [expr $d/0.75]
    fpoint $w [expr $x-$d] [expr $y-$d] $d $fsize $color 1
    fpoint $w [expr $x-$d] [expr $y+$d] $d $fsize $color 2
    fpoint $w [expr $x+$d] [expr $y-$d] $d $fsize $color 3
    fpoint $w [expr $x+$d] [expr $y+$d] $d $fsize $color 4
}
proc fpoint {w x y psize fsize fg no} {
    point $w $x $y $psize $fg
    figure $w $x $y $fsize $fg $no
}
proc point {w x y d fg} {
    $w create oval [expr $x-$d/2.] [expr $y-$d/2.] \
    [expr $x+$d/2.] [expr $y+$d/2.] -fill $fg
}
proc point8 {w m x y d fg} {
    point $w $m+$x $m+$y $d $fg
    point $w $m+$x $m-$y $d $fg
    point $w $m-$x $m+$y $d $fg
    point $w $m-$x $m-$y $d $fg
    point $w $m+$y $m+$x $d $fg
    point $w $m+$y $m-$x $d $fg
    point $w $m-$y $m+$x $d $fg
    point $w $m-$y $m-$x $d $fg
}
proc figure {w x y size color no} {
    set d [expr $size/6.]
    set s $size/1.5
    set y [expr $y-$d*2.5]
    $w create arc [expr $x-$s] [expr $y-$s] [expr $x+$s] [expr $y+$s]\
        -start 250 -extent 40 -fill $color -tags [list mv $color$no]
    $w create oval [expr $x-$d] [expr $y-$d] [expr $x+$d] [expr $y+$d]\
        -fill $color -tags [list mv $color$no]
    $w bind $color$no <B1-Motion> [list figure:move $w $color$no %x %y]
}
proc figure:move {w tag x y} {
    global g
    set x0 [$w canvasx $x]; set y0 [$w canvasy $y]
    $w move $tag [expr $x0-$g(x)] [expr $y0-$g(y)]
    $w raise $tag
    set g(x) $x0; set g(y) $y0
}
proc canvas:die {w x y args} {
    upvar #0 g opt
    array set opt {-size 25 -fg gold -bg red -mayroll 1}
    array set opt $args
    set s $opt(-size)
    set id [$w create rect $x $y [expr $x+$s] [expr $y+$s] \
        -fill $opt(-bg) -tags mvg]
    set ::g($id,fg) $opt(-fg)
    set ::g($id,bg) $opt(-bg)
    set grouptag group$id
    $w addtag $grouptag withtag $id
    set ex [expr $x+$s/10.]
    set ey [expr $y+$s/10.]
    set d [expr $s/5.];# dot diameter
    set dotno 1 ;# dot counter
    foreach y [list $ey [expr $ey+$d*1.5] [expr $ey+$d*3]] {
        foreach x [list $ex [expr $ex+$d*1.5] [expr $ex+$d*3]] {
            $w create oval $x $y [expr $x+$d] [expr $y+$d] \
                -fill $opt(-bg) -outline $opt(-bg) \
                -tags [list mvg $grouptag ${id}d$dotno]
            incr dotno
        }
    }
    $w bind mvg <1> {cdie:roll %W [%W find withtag current]}
    cdie:set $w $id [expr int(rand()*6)+1]
    set id
}
proc cdie:set {w id n} {
    set bg $::g($id,bg)
    foreach i [$w gettags $id] {
        if [regexp group $i] {set grouptag $i;break}
    }
    $w itemconfig $grouptag -fill $bg -outline $bg
    foreach i [lindex [list \
        {} {d5} [random:select {{d3 d7} {d1 d9}}] \
        [random:select {{d1 d5 d9} {d3 d5 d7}}] \
        {d1 d3 d7 d9} {d1 d3 d5 d7 d9} \
        [random:select {{d1 d3 d4 d6 d7 d9} {d1 d2 d3 d7 d8 d9}}] \
        ] $n] {
            $w itemconfig $id$i -fill $::g($id,fg) -outline $::g($id,fg)
    }
    set ::g($id) $n
}
proc cdie:roll {w id} {
    # wiggle: amount, pick one of eight wiggle directions
    set dwig [expr $::g(-size)/5]
    regexp {group([0-9]+)} [$w gettags $id] -> id
    for {set i 10} {$i<100} {incr i 10} {
        cdie:set $w $id [expr int(rand()*6)+1]
        set wig [random:select {0,1 0,-1 1,0 -1,0 1,1 -1,1 1,-1 -1,-1}]
        set wig [lexpr \$i*$dwig [split $wig ,]]
        eval $w move group$id $wig
        update
        set wig [lexpr \$i*-1 $wig] ;# wiggle back
        eval $w move group$id $wig
        after $i
    }
}
proc random:select L {lindex $L [expr int(rand()*[llength $L].)]}
proc lexpr {term L} {
    # map an expr term to each element \$i of a list
    set res [list]
    foreach i $L {lappend res [eval expr $term]}
    set res
}
#####################################
 proc NextPlayer {} {
 #: Move Turn-Marker-Button to position of next player
     incr ::pos 1
     switch  -- $::pos {
             1       { .p coords $::bw  35  95; .b1 config -fg blue   }
             2       { .p coords $::bw 253  36; .b1 config -fg red    }
             3       { .p coords $::bw 305 255; .b1 config -fg green4 }
             4       { .p coords $::bw  92 305; .b1 config -fg yellow1 ; set ::pos 0 }
             default { set ::pos 0 }
     }
 }
#####################################
 pack [pachisi .p -bg beige]

 button .b1 -text "Done" -command {NextPlayer}
 set bw [.p create window 22 14 -window .b1]
 set pos 0
 NextPlayer

HJG 2007-07-13 Added a turnmarker, to show which player has his turn to do.