Flow Colors

Intro

Keith Vetter 2014-04-23 : I recently came across the simple yet addictive puzzle game Flow Colors , and I decided it would be fun to write my own version of the game.

The object of Flow Colors is lay pipes to connect all pairs of colors. A level is solved when all the colors are paired and the entire board is filled.
Flow Colors is based on a game by Mohammed el-Madhoun, see [L1 ].

HJG 2015-06-18: That link expired, last archived version: [L2 ]

Also: http://www.senscritique.com/jeuvideo/Flow_Colors/10749199/actualites

https://wiki.tcl-lang.org/_repo/wiki_images/flowColors_screenshot.png

Program

##+##########################################################################
#
# flowColors.tcl -- plays game Flow Colors
# http://moh97.us/flow/
# by Keith Vetter April 2014
#

package require Tk
package require Img
package require tooltip

set S(state) play
set S(stage) 1
set S(level) 1
set S(solved) {}
set S(margin) 75
set S(sz) 90
set S(radius,factor) .35
set S(lwidth,factor) .30
set S(stageButton,size) 4
set S(fade,params) {20 20}
set S(hints,canDo) 0
set S(hints,used) 0
set S(shiftStage,params) {100 10}
set S(darken,0) 30
set S(darken,1) 70
if {$tcl_platform(os) eq "Darwin"} {
    set S(rcFile) [file join $::env(HOME) Library Preference flowColors.rc]
} elseif {$tcl_platform(platform) eq "windows"} {
    set S(rcFile) [file join $::env(HOME) flowColors.rc]
} else {
    set S(rcFile) [file join $::env(HOME) .flowColorsrc]
}
array set SOLUTIONS {}

set CLR(bg) black
set CLR(fg) white
set CLR(txt) red
set CLR(dialog,bg) bisque
set CLR(dialog,fg) black
set CLR(dialog,txt) gray20
set CLR(dialog,button)  [::tk::Darken $CLR(dialog,bg) 50]
set CLR(dialog,button) bisque4
set CLR(dialog,highlight) pink
set CLR(star) darkgoldenrod
set CLR(stageButton,off) white
set CLR(stageButton,on) red

catch {font delete myDialogFont}
catch {font delete myDisplayFont}
font create myDialogFont       {*}[font actual {Helvetica 14 bold}]
font create myDisplayFont      {*}[font actual {Helvetica 18 bold}]
font create myDisplayFontBig   {*}[font actual {Helvetica 32 bold}]
font create myDisplayFontTitle {*}[font actual {Helvetica 56 bold}]

array set BOARDS {
    1,1 {6 blue 0 1 5 0 cyan 4 2 1 4 darkorange 0 0 4 0 green 0 2 2 2 red 5 2 0 5 yellow 3 2 0 4}
    1,2 {6 blue 0 2 3 5 cyan 3 3 4 4 darkorange 0 1 4 5 green 0 5 2 5 red 2 3 4 1 yellow 2 2 4 2}
    1,3 {6 blue 1 4 4 4 darkorange 1 3 4 2 green 2 5 3 2 red 0 0 1 5 yellow 2 3 4 1}
    1,4 {6 blue 1 4 4 4 darkorange 4 3 5 2 green 1 1 4 2 yellow 2 3 5 3}
    1,5 {6 blue 0 1 3 2 darkorange 1 1 4 3 green 1 3 5 5 yellow 0 2 1 4}
    1,6 {6 blue 0 0 5 4 darkorange 3 4 4 1 green 2 0 2 4 yellow 2 3 4 4}
    1,7 {6 blue 3 2 5 0 cyan 3 4 4 2 darkorange 1 1 3 5 green 0 5 2 5 red 0 3 2 4 yellow 1 3 3 3}
    1,8 {6 blue 0 2 2 3 darkorange 1 1 3 2 green 1 2 4 4 red 2 5 4 0 yellow 0 3 1 5}
    1,9 {6 blue 0 4 4 4 darkorange 2 2 5 1 green 0 5 3 3 red 1 2 4 1 yellow 3 2 5 2}
    1,10 {6 blue 4 1 4 5 cyan 1 2 1 5 darkorange 2 0 5 5 green 0 1 0 5 red 0 0 2 5 yellow 3 1 3 5}
    1,11 {6 blue 1 1 2 5 darkorange 1 2 1 4 green 3 5 4 0 red 1 5 3 4 yellow 4 1 4 4}
    1,12 {6 blue 0 1 3 3 darkorange 1 1 2 3 green 0 2 5 1 yellow 1 2 5 0}
    1,13 {6 blue 0 2 2 0 darkorange 4 1 5 0 green 1 1 4 4 red 1 2 2 5 yellow 1 4 4 0}
    1,14 {6 blue 1 4 5 5 darkorange 0 4 4 5 green 1 2 5 1 red 3 3 5 3 yellow 5 0 5 2}
    1,15 {6 blue 1 0 2 1 cyan 2 4 5 1 darkorange 4 3 5 2 green 2 3 4 4 red 2 0 5 0 yellow 0 0 5 3}
    1,16 {6 blue 0 3 5 0 cyan 0 2 1 0 darkorange 2 4 4 4 green 2 3 3 1 red 3 3 4 1 yellow 1 4 4 0}
    1,17 {6 blue 1 3 5 4 cyan 2 1 3 2 darkorange 2 4 4 4 green 2 3 4 0 red 0 5 1 1 yellow 0 1 4 2}
    1,18 {6 blue 4 2 4 4 darkorange 0 5 5 0 green 1 1 5 2 red 2 1 2 3 yellow 1 5 5 3}
    1,19 {6 blue 0 5 2 2 darkorange 0 1 3 4 green 0 0 2 4 red 1 5 2 3 yellow 4 1 4 4}
    1,20 {6 blue 3 3 4 1 darkorange 0 0 5 3 green 2 4 3 2 yellow 3 4 4 3}
    1,21 {6 blue 0 0 1 1 cyan 0 4 5 0 darkorange 0 3 2 3 green 0 5 5 1 red 0 1 2 1 yellow 2 0 3 3}
    1,22 {6 blue 0 0 4 3 darkorange 2 0 4 2 green 2 1 3 2 red 0 5 4 0 yellow 1 0 3 3}
    1,23 {6 blue 0 5 5 5 darkorange 1 5 4 5 green 2 2 2 4 yellow 2 5 3 2}
    1,24 {6 blue 3 2 4 1 cyan 0 5 5 5 darkorange 1 0 1 3 green 0 0 1 2 red 0 2 4 3 yellow 3 1 4 4}
    1,25 {6 blue 2 3 2 5 cyan 2 2 3 4 darkorange 0 2 0 5 green 0 1 5 0 red 1 5 5 1 yellow 3 3 4 4}
    1,26 {6 blue 3 3 5 0 darkorange 2 3 3 0 green 1 0 4 3 red 0 0 5 3 yellow 4 2 5 1}
    1,27 {6 blue 2 4 5 4 darkorange 0 0 4 1 green 1 5 5 3 red 1 4 4 4 yellow 1 0 5 2}
    1,28 {6 blue 4 1 4 4 darkorange 1 1 5 0 green 0 0 2 3 yellow 0 1 5 1}
    1,29 {6 blue 1 0 3 5 cyan 4 5 5 3 darkorange 1 3 4 4 green 2 3 3 0 red 1 1 2 0 yellow 1 2 4 1}
    1,30 {6 blue 1 1 4 5 darkorange 3 1 5 0 green 1 3 4 4 red 1 0 3 5 yellow 2 1 4 1}
    2,1 {7 blue 0 6 6 5 cyan 3 4 6 6 darkorange 1 5 2 1 green 3 3 4 2 red 1 6 5 4 yellow 4 4 5 5}
    2,2 {7 blue 1 3 1 5 cyan 3 2 4 5 darkorange 1 2 5 2 green 2 2 3 5 magenta 0 1 6 2 red 0 2 6 3 yellow 3 3 4 4}
    2,3 {7 blue 4 0 6 6 brown 3 0 5 1 cyan 0 0 1 6 darkorange 2 3 4 5 green 2 4 3 6 magenta 2 0 2 2 red 3 2 4 6 yellow 1 0 2 6}
    2,4 {7 blue 4 4 5 1 darkorange 4 2 5 4 green 1 0 6 0 red 5 0 5 5 yellow 2 2 3 4}
    2,5 {7 blue 0 1 3 4 cyan 0 0 6 1 darkorange 3 1 6 6 green 1 5 4 4 red 2 1 4 2 yellow 0 2 2 3}
    2,6 {7 blue 1 1 3 0 cyan 2 5 4 6 darkorange 0 5 2 2 green 0 6 1 5 magenta 3 5 4 1 red 4 0 5 6 yellow 1 4 4 2}
    2,7 {7 blue 3 1 3 3 darkorange 2 0 4 2 green 1 0 2 1 red 1 1 1 5 yellow 4 1 4 5}
    2,8 {7 blue 4 3 4 6 cyan 0 6 1 1 darkorange 1 6 5 1 green 0 4 6 5 magenta 5 3 6 6 red 3 3 3 6 yellow 3 2 5 2}
    2,9 {7 blue 2 4 2 6 cyan 1 1 4 6 darkorange 0 5 1 0 green 1 2 4 5 red 0 6 3 6 yellow 3 1 5 5}
    2,10 {7 blue 2 4 6 4 cyan 0 1 6 0 darkorange 1 1 6 2 green 5 2 6 5 red 3 3 5 5 yellow 4 3 6 3}
    2,11 {7 blue 1 0 2 3 cyan 1 2 3 3 darkorange 3 1 5 4 green 5 1 5 3 magenta 1 1 4 0 red 1 5 5 5 yellow 3 4 5 0}
    2,12 {7 blue 5 5 6 2 cyan 3 0 6 0 darkorange 2 2 6 6 green 2 0 2 3 magenta 3 1 6 1 red 1 5 2 1 yellow 3 6 4 3}
    2,13 {7 blue 1 2 1 4 cyan 2 4 3 3 darkorange 0 6 3 6 green 4 0 5 6 red 1 1 1 5 yellow 0 5 4 6}
    2,14 {7 blue 3 2 4 1 brown 1 2 5 1 cyan 1 4 6 0 darkorange 1 1 3 1 green 2 2 4 3 magenta 1 5 6 6 red 1 3 2 6 yellow 4 5 6 3}
    2,15 {7 blue 0 2 3 1 darkorange 0 1 1 0 green 1 2 3 3 red 1 1 1 4 yellow 1 5 5 1}
    2,16 {7 blue 3 3 4 5 darkorange 3 2 5 5 green 4 6 5 1 red 5 2 5 6 yellow 6 0 6 2}
    2,17 {7 blue 2 1 3 4 cyan 4 1 6 2 darkorange 3 1 3 5 green 5 1 5 6 magenta 1 1 2 4 red 0 6 4 5 yellow 3 2 5 5}
    2,18 {7 blue 2 5 5 3 cyan 4 6 6 6 darkorange 4 5 6 5 green 1 1 5 2 magenta 3 6 5 0 red 2 6 5 1 yellow 1 5 2 2}
    2,19 {7 blue 4 6 6 6 cyan 1 5 4 5 darkorange 5 5 6 2 green 3 5 4 3 red 3 6 6 1 yellow 0 3 6 0}
    2,20 {7 blue 2 1 3 3 darkorange 1 3 4 1 green 0 0 0 2 red 0 3 1 2 yellow 2 4 3 1}
    2,21 {7 blue 0 0 1 4 cyan 3 0 6 0 darkorange 0 4 2 3 green 1 0 3 3 red 1 5 5 2 yellow 2 0 3 1}
    2,22 {7 blue 1 4 3 2 cyan 4 5 5 1 darkorange 4 6 6 0 green 1 2 2 0 magenta 1 5 5 0 red 0 0 1 3 yellow 5 2 5 5}
    2,23 {7 blue 4 5 6 5 cyan 0 6 4 3 darkorange 1 6 3 3 green 0 0 4 4 red 0 4 5 3 yellow 3 4 6 6}
    2,24 {7 blue 5 4 6 6 cyan 1 5 5 6 darkorange 0 6 2 0 green 1 6 4 1 red 6 0 6 3 yellow 3 3 4 6}
    2,25 {7 blue 2 1 6 5 cyan 4 1 6 4 darkorange 3 1 5 5 green 0 0 6 3 red 0 1 1 5 yellow 0 6 1 1}
    2,26 {7 blue 0 2 6 0 darkorange 1 5 4 5 green 0 1 4 1 red 2 2 5 3 yellow 5 0 5 5}
    2,27 {7 blue 0 3 6 6 darkorange 2 1 3 3 green 1 5 4 5 red 0 4 3 1 yellow 1 4 2 2}
    2,28 {7 blue 0 5 6 1 cyan 4 0 5 5 darkorange 4 1 6 0 green 0 2 3 4 magenta 0 0 1 3 red 1 5 3 3 yellow 1 0 2 3}
    2,29 {7 blue 2 2 2 4 cyan 3 1 4 4 darkorange 5 3 6 6 green 4 3 5 0 red 2 1 4 5 yellow 5 1 5 4}
    2,30 {7 blue 0 5 3 6 cyan 1 5 3 4 darkorange 0 0 4 0 green 0 2 0 4 magenta 1 2 2 4 red 4 1 5 5 yellow 1 0 4 2}
    3,1 {8 blue 3 4 5 4 cyan 1 4 2 7 darkorange 3 3 4 4 green 1 6 2 4 red 0 4 7 1 yellow 1 7 2 5}
    3,2 {8 blue 0 1 5 5 cyan 0 2 7 3 darkorange 1 6 7 1 green 3 3 5 0 red 0 0 7 0 yellow 3 0 3 2}
    3,3 {8 blue 2 2 5 7 cyan 4 4 6 1 darkorange 5 3 7 6 green 4 7 7 7 red 0 1 4 3 yellow 1 3 1 6}
    3,4 {8 blue 5 7 6 3 cyan 1 6 3 6 darkorange 4 7 5 2 green 1 7 3 1 magenta 2 3 3 7 red 4 4 6 2 yellow 0 0 6 7}
    3,5 {8 blue 0 7 4 5 cyan 3 3 6 1 darkorange 7 2 7 7 green 0 6 4 1 red 1 3 3 1 yellow 3 6 7 1}
    3,6 {8 blue 0 7 4 6 cyan 6 2 7 6 darkorange 1 4 4 7 green 6 1 7 7 magenta 1 0 5 4 red 0 0 5 7 yellow 2 0 7 1}
    3,7 {8 blue 3 1 4 4 darkorange 1 0 2 4 green 2 2 4 1 red 3 6 6 1 yellow 0 7 1 5}
    3,8 {8 blue 5 2 5 7 cyan 0 5 6 4 darkorange 0 6 2 1 green 0 7 5 1 magenta 5 3 6 1 red 5 4 7 7 yellow 4 4 6 7}
    3,9 {8 blue 1 1 4 0 cyan 4 5 5 1 darkorange 2 2 3 0 green 1 2 4 3 red 3 5 5 0 yellow 1 6 6 6}
    3,10 {8 blue 0 7 4 6 cyan 0 3 0 5 darkorange 1 7 4 2 green 1 1 1 4 red 4 4 7 0 yellow 3 1 6 7}
    3,11 {8 blue 7 1 7 3 cyan 1 6 3 6 darkorange 4 3 5 6 green 0 5 2 4 magenta 0 6 7 0 red 5 3 6 6 yellow 3 3 4 1}
    3,12 {8 blue 2 4 2 6 cyan 1 4 6 6 darkorange 4 7 5 5 green 1 6 3 1 red 0 1 7 4 yellow 0 6 5 2}
    3,13 {8 blue 6 4 6 6 cyan 3 6 5 2 darkorange 0 0 5 6 green 2 5 4 4 red 0 7 3 7 yellow 2 3 6 0}
    3,14 {8 blue 1 4 3 3 cyan 4 7 5 2 darkorange 7 0 7 5 green 5 3 7 7 red 2 5 4 6 yellow 0 2 7 6}
    3,15 {8 blue 2 5 6 2 cyan 6 5 6 7 darkorange 4 4 5 7 green 0 1 7 3 red 3 6 7 7 yellow 2 4 4 3}
    3,16 {8 blue 3 4 6 3 cyan 2 4 6 2 darkorange 1 4 4 0 green 0 0 0 7 magenta 0 6 6 6 red 3 2 5 2 yellow 4 4 5 0}
    3,17 {8 blue 0 0 7 6 brown 3 1 3 4 cyan 4 0 5 4 darkorange 3 0 4 6 green 5 6 7 4 magenta 2 1 5 5 red 4 4 6 1 yellow 5 3 6 2}
    3,18 {8 blue 1 0 2 2 brown 3 5 4 2 cyan 0 0 2 0 darkorange 0 4 5 4 green 2 7 7 7 magenta 4 3 5 2 purple 3 0 3 6 red 1 5 1 7 yellow 1 6 2 5}
    3,19 {8 blue 3 4 4 6 brown 1 1 2 6 cyan 0 1 3 2 darkorange 0 6 2 4 green 6 1 6 6 magenta 3 3 4 0 red 1 4 2 1 yellow 4 5 5 6}
    3,20 {8 blue 5 5 6 3 brown 6 2 7 4 cyan 4 7 7 5 darkorange 0 2 6 1 green 3 0 7 3 magenta 1 4 1 6 purple 0 5 2 7 red 0 4 3 7 yellow 1 1 3 5}
    3,21 {8 blue 0 7 4 7 brown 0 5 7 1 cyan 4 5 6 6 darkorange 0 6 2 6 green 2 1 7 5 magenta 5 3 7 2 red 3 3 6 1 yellow 4 1 7 3}
    3,22 {8 blue 0 6 2 2 cyan 1 2 6 1 darkorange 0 4 1 5 green 2 3 6 6 red 1 6 3 4 yellow 4 5 7 1}
    3,23 {8 blue 1 2 2 6 cyan 0 7 1 4 darkorange 1 3 6 6 green 0 0 6 4 magenta 1 5 7 3 red 5 4 6 1 yellow 4 1 6 5}
    3,24 {8 blue 4 6 5 5 cyan 1 2 2 3 darkorange 0 2 1 1 green 0 1 7 0 magenta 3 3 5 6 red 2 5 4 5 yellow 3 1 7 1}
    3,25 {8 blue 1 4 6 6 cyan 0 3 7 6 darkorange 3 1 7 0 green 5 3 6 0 magenta 0 0 2 1 red 1 3 7 2 yellow 1 1 4 3}
    3,26 {8 blue 0 0 2 2 brown 2 5 4 4 cyan 1 1 3 1 darkorange 1 3 7 0 green 3 2 5 5 magenta 4 2 5 4 red 5 0 5 6 yellow 1 0 5 2}
    3,27 {8 blue 3 5 6 2 cyan 2 7 6 5 darkorange 1 7 7 3 green 3 4 5 2 magenta 1 1 6 3 red 6 7 7 4 yellow 5 5 6 4}
    3,28 {8 blue 1 7 7 3 cyan 3 6 7 4 darkorange 1 3 7 7 green 3 3 5 1 magenta 1 2 3 1 red 4 1 5 4 yellow 2 5 6 1}
    3,29 {8 blue 0 3 3 7 cyan 1 6 6 6 darkorange 2 3 6 5 green 1 1 3 5 red 2 1 5 2 yellow 3 1 6 4}
    3,30 {8 blue 4 3 6 1 cyan 1 6 3 6 darkorange 0 4 0 6 green 2 2 4 2 magenta 0 5 3 5 red 1 4 6 3 yellow 5 5 6 2}
    4,1 {9 blue 1 3 2 7 brown 5 7 7 1 cyan 1 2 2 3 darkorange 2 4 2 6 green 4 1 4 3 magenta 3 7 5 1 purple 5 8 6 1 red 1 1 4 4 yellow 5 0 6 2}
    4,2 {9 blue 0 0 3 7 brown 0 7 3 0 cyan 4 7 7 5 darkorange 5 1 7 1 green 4 3 8 3 magenta 3 5 5 5 red 3 3 7 2 yellow 5 4 8 2}
    4,3 {9 blue 0 2 1 0 cyan 3 1 7 1 darkorange 4 4 6 4 green 6 3 7 6 magenta 2 5 4 0 red 7 5 8 8 yellow 3 0 3 8}
    4,4 {9 blue 4 8 8 8 cyan 0 8 6 4 darkorange 0 1 1 3 green 1 8 3 4 magenta 5 8 7 8 red 3 8 4 4 yellow 0 0 3 7}
    4,5 {9 blue 1 7 6 5 brown 2 5 6 4 cyan 6 7 7 2 darkorange 1 0 7 1 green 0 0 3 3 magenta 2 0 6 8 red 5 3 7 7 yellow 1 4 3 6}
    4,6 {9 blue 3 5 6 4 brown 6 5 8 6 cyan 4 4 7 4 darkorange 3 4 7 0 green 8 0 8 4 magenta 3 6 6 0 purple 1 5 6 7 red 4 6 7 7 white 5 0 8 7 yellow 1 1 1 4}
    4,7 {9 blue 2 7 5 7 brown 6 7 8 8 cyan 2 3 6 3 darkorange 4 2 6 6 green 2 1 6 4 magenta 0 6 7 8 purple 1 7 5 6 red 1 2 3 1 white 0 4 5 2 yellow 0 0 8 0}
    4,8 {9 blue 0 0 1 8 brown 5 2 6 4 cyan 2 3 8 8 darkorange 2 8 3 5 green 1 0 7 4 magenta 1 5 1 7 red 4 7 6 7 yellow 6 2 6 6}
    4,9 {9 blue 0 0 3 8 brown 0 1 2 8 cyan 2 7 6 2 darkorange 0 3 3 3 green 1 7 4 5 magenta 2 6 4 6 red 6 7 7 4 yellow 1 1 6 6}
    4,10 {9 blue 7 6 8 8 cyan 5 1 8 5 darkorange 7 5 7 8 green 6 1 6 8 magenta 2 6 5 6 red 2 2 6 6 yellow 4 3 7 1}
    4,11 {9 blue 4 2 5 3 brown 4 0 8 0 cyan 1 1 2 5 darkorange 1 4 7 7 green 7 2 8 5 magenta 4 4 8 4 purple 3 0 5 1 red 1 2 6 5 yellow 0 0 4 3}
    4,12 {9 blue 4 8 5 6 brown 2 0 7 7 cyan 1 1 1 4 darkorange 1 6 2 1 green 5 2 5 5 magenta 3 2 8 4 red 1 7 3 3 yellow 6 2 7 6}
    4,13 {9 blue 3 4 5 7 brown 3 2 4 4 cyan 5 5 7 6 darkorange 4 5 7 7 green 1 7 7 1 magenta 4 2 6 4 red 5 4 8 3 yellow 2 2 2 7}
    4,14 {9 blue 6 4 6 6 brown 1 1 2 7 cyan 6 1 8 0 darkorange 1 4 7 0 green 5 7 6 3 magenta 2 6 3 4 purple 2 1 3 3 red 1 3 4 5 yellow 4 8 6 2}
    4,15 {9 blue 2 7 4 2 cyan 0 0 4 3 darkorange 1 0 1 7 green 5 3 6 1 magenta 4 6 6 6 red 3 2 3 4 yellow 2 0 4 0}
    4,16 {9 blue 2 2 2 5 brown 0 7 6 7 cyan 7 1 8 8 darkorange 0 6 3 1 green 0 8 5 6 magenta 6 3 7 2 purple 4 6 5 1 red 0 4 6 4 yellow 1 6 3 2}
    4,17 {9 blue 1 3 4 6 cyan 4 7 7 0 darkorange 3 3 6 6 green 4 8 8 0 red 1 4 1 6 yellow 1 7 4 5}
    4,18 {9 blue 1 4 7 1 brown 4 3 7 3 cyan 0 8 8 8 darkorange 6 5 8 7 green 4 4 7 6 magenta 5 5 5 7 purple 2 6 3 3 red 1 6 7 2 yellow 4 5 8 6}
    4,19 {9 blue 1 6 5 7 brown 1 4 1 7 cyan 4 7 8 8 darkorange 5 1 7 5 green 0 7 4 5 magenta 1 3 4 1 purple 2 4 6 2 red 3 3 7 6 yellow 3 4 4 6}
    4,20 {9 blue 1 1 6 3 brown 3 8 4 4 cyan 1 7 6 8 darkorange 3 0 6 5 green 3 1 3 6 magenta 5 6 8 8 purple 5 7 7 8 red 0 5 2 4 yellow 0 4 6 2}
    4,21 {9 blue 3 6 5 3 cyan 4 2 7 2 darkorange 1 1 6 5 green 3 2 7 8 magenta 2 6 6 8 red 5 2 6 6 yellow 5 5 7 7}
    4,22 {9 blue 5 1 7 2 brown 0 5 0 8 cyan 3 6 7 7 darkorange 0 4 6 4 green 4 7 5 2 magenta 1 4 2 1 purple 2 2 4 4 red 2 6 5 8 yellow 2 8 6 2}
    4,23 {9 blue 4 0 8 0 brown 1 5 4 6 cyan 1 8 7 7 darkorange 1 4 2 1 green 2 6 8 7 magenta 1 6 3 8 purple 5 2 6 6 red 3 3 6 7 yellow 2 4 6 4}
    4,24 {9 blue 1 1 4 3 brown 1 4 4 7 cyan 4 4 7 6 darkorange 0 2 6 8 green 1 3 5 8 magenta 1 2 6 2 red 2 3 7 7 yellow 2 4 5 7}
    4,25 {9 blue 2 7 6 3 brown 1 4 2 6 cyan 0 0 8 0 darkorange 4 7 6 4 green 0 1 6 8 magenta 0 8 4 3 red 1 5 6 6 yellow 0 2 6 7}
    4,26 {9 blue 7 4 8 0 brown 1 7 5 2 cyan 2 7 5 6 darkorange 7 6 7 8 green 0 4 3 5 magenta 4 0 7 3 purple 4 4 8 8 red 1 4 2 1 yellow 2 2 6 6}
    4,27 {9 blue 3 5 4 3 brown 0 2 8 1 cyan 0 3 6 4 darkorange 1 5 7 6 green 3 3 6 6 magenta 5 1 7 1 red 1 4 7 3 yellow 0 1 3 0}
    4,28 {9 blue 2 6 8 4 cyan 3 6 7 4 darkorange 2 4 7 6 green 1 1 7 3 magenta 6 6 7 5 red 5 0 7 1 yellow 1 4 2 7}
    4,29 {9 blue 4 1 4 5 brown 4 2 4 4 cyan 0 0 3 7 darkorange 1 0 1 4 green 5 7 7 3 magenta 1 7 8 1 red 4 7 5 5 yellow 2 7 6 2}
    4,30 {9 blue 1 1 8 0 brown 2 0 7 3 cyan 0 7 3 3 darkorange 4 7 7 7 green 0 8 4 1 magenta 5 4 7 6 purple 7 8 8 5 red 0 6 2 3 yellow 6 6 6 8}
    5,1 {9 blue 0 6 3 6 cyan 4 6 6 7 darkorange 0 0 3 5 green 0 7 5 3 magenta 7 7 8 6 red 2 1 2 3 yellow 0 8 8 7}
    5,2 {9 blue 0 0 0 8 brown 7 6 7 8 cyan 2 5 5 3 darkorange 2 0 4 0 green 5 0 8 8 magenta 4 4 5 2 red 1 0 4 5 yellow 5 1 6 6}
    5,3 {9 blue 0 5 4 5 brown 3 2 7 2 cyan 7 8 8 0 darkorange 0 0 0 4 green 0 6 6 8 magenta 2 3 8 8 purple 3 3 6 4 red 0 8 5 8 yellow 0 7 5 7}
    5,4 {9 blue 0 8 8 1 cyan 8 2 8 8 darkorange 0 5 8 0 green 2 3 5 3 red 5 4 6 3 yellow 2 2 2 4}
    5,5 {9 blue 0 0 0 6 cyan 5 6 8 8 darkorange 0 8 6 6 green 0 5 6 1 red 7 3 7 6 yellow 4 5 8 0}
    5,6 {9 blue 0 0 4 0 brown 6 2 7 6 cyan 4 2 5 6 darkorange 0 2 0 7 green 0 1 3 6 magenta 5 2 5 5 purple 4 1 7 5 red 2 3 7 7 white 0 8 5 0 yellow 1 7 2 2}
    5,7 {9 blue 0 7 3 7 brown 5 2 5 7 cyan 1 5 4 5 darkorange 0 6 4 7 green 0 8 1 4 magenta 7 1 7 5 purple 0 0 0 5 red 4 0 7 8 yellow 1 0 2 0}
    5,8 {9 blue 0 0 0 7 brown 7 4 7 8 cyan 3 2 4 8 darkorange 0 8 3 8 green 1 7 8 0 magenta 5 3 7 3 purple 7 2 8 8 red 3 1 5 2 yellow 2 1 3 3}
    5,9 {9 blue 0 1 0 8 brown 8 0 8 8 cyan 6 1 6 6 darkorange 0 0 1 7 green 3 7 4 1 magenta 7 1 7 6 purple 1 8 7 8 red 5 1 7 7 yellow 2 7 7 0}
    5,10 {9 blue 0 7 3 3 cyan 3 7 7 1 darkorange 0 8 8 8 green 1 7 3 4 red 2 7 8 7 yellow 0 0 5 0}
    5,11 {9 blue 7 8 8 0 brown 5 4 5 7 cyan 0 3 5 8 darkorange 8 1 8 8 green 3 2 6 8 magenta 1 3 1 7 purple 2 3 5 3 red 2 2 6 0 white 2 4 2 7 yellow 1 1 6 1}
    5,12 {9 blue 0 7 8 0 brown 7 2 7 4 cyan 3 2 3 8 darkorange 0 8 2 8 green 1 7 5 7 magenta 4 2 7 5 red 2 2 2 7 yellow 6 7 8 4}
    5,13 {9 blue 1 2 1 8 brown 6 4 7 4 cyan 4 4 8 8 darkorange 0 8 8 7 green 0 0 8 0 magenta 5 4 5 7 purple 3 3 3 8 red 4 3 7 3 yellow 2 8 7 2}
    5,14 {9 blue 0 1 6 2 brown 3 6 6 4 cyan 2 7 4 6 darkorange 0 0 7 2 green 0 3 0 8 magenta 4 5 6 5 purple 3 7 8 7 red 0 2 5 2 white 8 2 8 6 yellow 1 3 8 8}
    5,15 {9 blue 0 8 5 8 cyan 5 7 7 8 darkorange 2 0 2 7 green 3 0 8 8 magenta 6 3 6 8 red 4 4 4 7 yellow 3 7 7 1}
    5,16 {9 blue 0 2 0 5 brown 5 1 6 6 cyan 3 2 3 5 darkorange 1 0 4 1 green 0 6 5 6 magenta 2 0 7 6 purple 5 2 5 5 red 2 2 2 5 white 8 2 8 5 yellow 0 8 8 6}
    5,17 {9 blue 0 0 7 8 brown 8 0 8 8 cyan 2 2 5 2 darkorange 5 7 6 1 green 0 8 6 8 magenta 6 2 6 7 red 2 5 5 5 yellow 1 2 5 6}
    5,18 {9 blue 0 1 5 1 brown 6 1 6 8 cyan 1 7 2 4 darkorange 0 2 5 8 green 0 0 8 8 magenta 3 4 3 6 purple 7 1 7 8 red 0 8 4 8 yellow 0 3 0 7}
    5,19 {9 blue 0 7 4 1 brown 4 6 4 8 cyan 2 7 3 4 darkorange 0 1 8 0 green 0 8 4 4 magenta 4 3 8 3 purple 6 2 8 2 red 2 2 8 1 yellow 1 7 3 3}
    5,20 {9 blue 0 1 2 1 brown 3 2 4 5 cyan 1 8 2 8 darkorange 0 0 8 8 green 0 2 2 2 magenta 3 5 3 8 purple 5 2 5 7 red 1 4 4 2 white 7 3 7 8 yellow 0 8 3 3}
    5,21 {9 blue 0 4 3 4 darkorange 0 1 0 2 green 1 2 4 2 red 7 7 8 2 yellow 0 0 8 3}
    5,22 {9 blue 0 1 7 1 brown 6 3 6 5 cyan 4 2 5 2 darkorange 0 0 3 3 green 0 8 3 2 magenta 5 3 5 5 red 2 3 2 7 yellow 1 3 8 8}
    5,23 {9 blue 0 0 2 8 brown 7 1 7 5 cyan 6 1 8 7 darkorange 0 8 2 7 green 1 2 1 8 magenta 5 8 8 8 red 4 8 5 1 yellow 4 6 7 6}
    5,24 {9 blue 4 3 6 3 brown 3 8 8 0 cyan 2 1 2 8 darkorange 4 2 6 2 green 5 1 5 5 magenta 3 7 7 0 red 1 6 3 6 yellow 0 0 0 8}
    5,25 {9 blue 1 1 5 0 brown 3 3 5 2 cyan 1 3 7 7 darkorange 0 0 4 4 green 2 1 5 1 magenta 2 3 7 0 red 7 1 7 6 yellow 0 3 8 0}
    5,26 {9 blue 0 0 7 1 cyan 2 5 2 6 darkorange 0 1 6 1 green 0 2 1 4 magenta 0 4 8 0 red 2 4 6 4 yellow 0 3 6 3}
    5,27 {9 blue 1 1 5 1 cyan 2 5 7 5 darkorange 0 1 8 5 green 2 2 2 4 magenta 1 5 7 7 red 1 2 2 6 yellow 1 4 4 3}
    5,28 {9 blue 1 0 7 0 brown 5 8 8 8 cyan 0 7 3 4 darkorange 0 0 0 6 green 1 6 8 0 magenta 0 8 4 4 red 2 6 5 3 yellow 2 2 6 3}
    5,29 {9 blue 0 1 8 2 brown 1 7 8 3 cyan 5 5 7 5 darkorange 0 0 7 0 green 4 2 4 4 magenta 5 6 7 6 red 4 5 5 4 yellow 3 5 8 8}
    5,30 {9 blue 1 1 1 3 brown 6 2 8 2 cyan 3 0 3 5 darkorange 2 2 8 8 green 1 4 6 3 magenta 4 0 5 3 red 2 3 4 3 yellow 5 4 5 6}
}
################################################################
proc DoDisplay {} {
    wm title . "Flow Colors"
    set sz [expr {2*$::S(margin) + $::S(sz) * 6}]
    set ::S(X0) [expr {$sz/2}]
    canvas .c -width $sz -height $sz -bd 0 -highlightthickness 0 -bg $::CLR(bg)
    pack .c -side top -fill both -expand 1

    bind .c <Configure> {Recenter %W %h %w}
    bind .c <1> {DoButton down %x %y}
    bind .c <B1-Motion> {DoButton move %x %y}
    bind .c <ButtonRelease-1> {DoButton up %x %y}
    if {[file exists ~/misc/tcl/console.tcl]} {
        uplevel #0 source ~/misc/tcl/console.tcl
        bind all <F12> {console show}
    }
}
proc DrawConstants {} {
    global S CLR B
    .c create rect -$::S(X0) -$::S(X0) $::S(X0) $::S(X0) -tag border -fill {} \
        -outline white -width 5
    set top [lindex [Cell2XY 0 0] 0 1]
    set y [expr {(-$::S(X0) + $top) / 2}]
    set x [expr {abs($y)}]
    .c create text 0 $y -text "Flow Colors" -font myDisplayFontTitle \
        -anchor c -fill red
    set star [MakeStar $x $y [expr {$top - $y - 5}]]
    .c create poly $star -fill $CLR(star) -outline $CLR(star) -tag solvedStar

    lassign [lindex [Cell2XY $::B(N) 0] 0] xLeft y
    lassign [lindex [Cell2XY $::B(N) $::B(N)] 0] xRight
    incr y 5
    .c create text $xLeft $y -font myDisplayFont -fill $::CLR(txt) \
        -anchor nw -tag level,txt

    # Navigation buttons
    .c create image -60 $y -image ::img::left  -anchor n -tag prev
    .c create image   0 $y -image ::img::undo  -anchor n -tag undo
    .c create image  60 $y -image ::img::right -anchor n -tag next

    # Help
    .c create image $xRight $y -image ::img::help -anchor ne -tag help
    CanvasButtonBinding .c help "" "" Help
    ::tooltip::tooltip .c -items help "Help"
    .c create image [expr {$xRight-48-5}] $y -image ::img::up -anchor ne -tag up
}    
proc DrawBoard {stage level} {
    global B S L BOARDS

    set S(stage) $stage
    set S(level) $level
    set S(level,txt) "Stage $stage\nLevel $level"
    set S(stages) [llength [array names BOARDS *,30]]

    unset -nocomplain B
    set boardSpec [lassign $BOARDS($stage,$level) N]
    ComputeSizes $N
    set B(N) $N
    set B(xy0) [expr {round(-$N/2. * $S(sz))}]
    set B(almost) 0

    .c delete all
    DrawConstants
    
    for {set row 0} {$row < $N} {incr row} {
        for {set col 0} {$col < $N} {incr col} {
            lassign [Cell2XY $row $col] xy
            .c create rect $xy -tag cell_${row},$col -fill $::CLR(bg) \
                -outline $::CLR(fg)
            set B($row,$col) {}
        }
    }

    # Draw all the nodes
    unset -nocomplain L
    foreach {clr row1 col1 row2 col2} $boardSpec {
        lassign [Cell2XY $row1 $col1] . xy1
        lassign [Cell2XY $row2 $col2] . xy2
        set id1 [.c create oval $xy1 -fill $clr -outline $clr -tag node]
        set id2 [.c create oval $xy2 -fill $clr -outline $clr -tag node]
        set B($row1,$col1) [list node $clr]
        set B($row2,$col2) [list node $clr]
        set L(path,$clr) {}
        set L(delete,$clr) {}
        set L(done,$clr) 0
        .c bind $id1 <Shift-Double-Button-3> [list DoHint $clr]
        .c bind $id2 <Shift-Double-Button-3> [list DoHint $clr]
    }

    # Level indicator
    .c itemconfig level,txt -text $S(level,txt) -anchor nw
    # Button bindings and tooltips
    CanvasButtonBinding .c prev "" "" {PlayAgain -1}
    CanvasButtonBinding .c undo "" "" {PlayAgain 0}
    CanvasButtonBinding .c next "" "" {PlayAgain 1}
    ::tooltip::tooltip .c -items prev "Previous puzzle"
    ::tooltip::tooltip .c -items undo "Restart puzzle"
    ::tooltip::tooltip .c -items next "Next puzzle"

    # Choose level button
    CanvasButtonBinding .c up "" "" ShowPickPuzzlePage
    ::tooltip::tooltip .c -items up "Choose level"

    .c itemconfig solvedStar \
        -state [expr {[HasBeenSolved $S(stage) $S(level)] ? "" : "hidden"}]
}
proc DrawPickPuzzlePage {} {
    global S B CLR
    set N 6
    ComputeSizes $N
    set B(N) $N
    set B(xy0) [expr {round(-$N/2. * $S(sz))}]

    .c delete all
    DrawConstants
    .c itemconfig solvedStar -state hidden
    for {set row 0} {$row < 5} {incr row} {
        for {set col 0} {$col < $N} {incr col} {
            for {set stage 1} {$stage <= $S(stages)} {incr stage} {
                set num [expr {1 + $row*$N + $col}]
                set fauxCol [expr {$col + ($stage - $S(stage))*$N}]
                set txt_tag "pick$stage,$num"
                lassign [Cell2XY [expr {$row+0}] $fauxCol] . xy x y
                set box_tag [.c create oval $xy -fill black -outline white \
                                 -width 2 -tag [list oval stage stage,$stage]]
                if {[HasBeenSolved $stage $num]} {
                    set star [MakeStar $x $y [expr {$S(radius) - 1}]]
                    .c create poly $star -fill $CLR(star) -outline $CLR(star) \
                        -tag [list star stage stage,$stage $txt_tag]
                }
                incr x 1 ; incr y 2
                .c create text $x $y -text $num -fill white \
                    -font "Helvetica 32 bold" \
                    -tag [list txt stage stage,$stage $txt_tag]
                CanvasButtonBinding .c $txt_tag $box_tag \
                    $::CLR(dialog,highlight) [list PlayChosenPuzzle $num]
            }
        }
    }
    .c itemconfig stage -state hidden
    .c itemconfig stage,$S(stage) -state {}
    
    # Stage and Help
    set N [lindex $::BOARDS($S(stage),1) 0]
    set range "\n([expr {$S(stage)*30-29}]-[expr {$S(stage)*30}])"
    .c itemconfig level,txt -text "$N x $N$range" -anchor w

    # Navigation buttons
    .c delete undo
    CanvasButtonBinding .c prev "" "" {ShiftStage -1}
    CanvasButtonBinding .c next "" "" {ShiftStage 1}
    ::tooltip::tooltip .c -items prev "Previous stage"
    ::tooltip::tooltip .c -items next "Next stage"
    StageButtons $S(stage)

    .c delete up
}
proc HasBeenSolved {stage level} {
    return [expr {[lsearch $::S(solved) $stage,$level] > -1}]
}
proc StageButtons {which} {
    if {[.c find withtag stageButton] eq ""} {
        lassign [.c bbox prev] . . x0 .
        lassign [.c bbox next] x1 y0 . y1
        set gap [expr {($x1 - $x0)/double($::S(stages))}]
        set y [expr {($y0 + $y1) / 2}]
        for {set stage 1} {$stage <= $::S(stages)} {incr stage} {
            set x [expr {$x0 - $gap/2 + $stage * $gap}]
            set xy [Box $x $y $::S(stageButton,size)]
            .c create oval $xy -tag [list stageButton stageButton,$stage] \
                -width 1
        }
    }
    .c itemconfig stageButton -fill $::CLR(stageButton,off) \
        -outline $::CLR(stageButton,off)
    .c itemconfig stageButton,$which -fill $::CLR(stageButton,on) \
        -outline $::CLR(stageButton,on)
}
proc ShiftStage {dir} {
    global S

    if {$dir < 0 && $S(stage) == 1} return
    if {$dir > 0 && $S(stage) == $S(stages)} return
    set nextStage [expr {$S(stage) + $dir}]
    set N [lindex $::BOARDS($nextStage,1) 0]
    set range "\n([expr {$nextStage*30-29}]-[expr {$nextStage*30}])"
    .c itemconfig level,txt -text "$N x $N$range"
    .c itemconfig stage,$nextStage -state {}
    StageButtons $nextStage

    lassign [.c bbox stage,$S(stage)] x0
    lassign [.c bbox stage,$nextStage] x1
    lassign $S(shiftStage,params) steps delay
    set delta [expr {($x0 - $x1) / double($steps)}]
    AnimateMove stage $delta 0 0
    MyWait
    
    .c itemconfig stage,$S(stage) -state hidden
    set S(stage) $nextStage
}
proc AnimateMove {tag xDelta yDelta step} {
    lassign $::S(shiftStage,params) steps delay
    .c move $tag $xDelta $yDelta
    
    incr step
    if {$step < $steps} {
        after $delay [list AnimateMove $tag $xDelta $yDelta $step]
    } else {
        set ::S(wait) 1
    }
}


proc DoButton {action x y} {
    global L B
    if {$::S(state) ne "play"} return
    lassign [XY2Cell [.c canvasy $y] [.c canvasx $x]] row col
    if {$row < 0 || $row >= $B(N) || $col < 0 || $col >= $B(N)} return
    lassign $B($row,$col) what clr
    
    if {$action eq "down"} {
        if {$what eq ""} return

        if {$what eq "node"} {
            # Starting from a node
            set L(delete,$clr) $L(path,$clr)
            set L(path,$clr) [list [list $row $col]]
            set L(done,$clr) 0
            set L(clr) $clr
            array unset L stolen,*
            ShowPath $L(clr)
        } else {
            # Starting from the middle of a path
            set L(clr) $clr
            array unset L stolen,*
            AdjustPath $row $col $L(clr)
            ShowPath $L(clr)
        }
        return
    }
    if {! [info exists L(clr)]} return
    
    if {$action eq "move" && $L(clr) ne ""} {
        lassign [lindex $L(path,$L(clr)) end] lastRow lastCol
        set drow [expr {abs($row - $lastRow)}]
        set dcol [expr {abs($col - $lastCol)}]
        if {! (($drow == 1 && $dcol == 0) || ($drow == 0 && $dcol == 1))} return
        
        set stolen [StealCell $row $col $L(clr)]
        if {$stolen eq "node"} return
        AdjustPath $row $col $L(clr)
        if {$stolen eq "endNode"} {
            set L(done,$clr) 1
        }
        ShowPath $L(clr)
        if {$stolen eq "endNode"} {             ;# Is path complete?
            set L(clr) ""
        }
        CheckUnsteal
        return
    }

    if {$action eq "up"} {
        unset L(clr)

        if {$what ne "node"} return
        CheckVictory
    }
}
# Handles toClr's pipe wanting to move to row,col
# which may cause another pipe to be truncated
proc StealCell {row col toClr} {
    global L B

    lassign $B($row,$col) who oldClr
    if {$who eq ""} {return empty}
    set n [lsearch -exact $L(path,$oldClr) [list $row $col]]
    if {$oldClr eq $toClr} {
        if {$who ne "node"} { return self }
        if {$n == -1} { return endNode }
        return startNode
    }
    if {$who eq "node"} {return node}

    # Here when truncating oldClr's path
    set n [lsearch -exact $L(path,$oldClr) [list $row $col]]
    if {$n == -1} {
        tk_messageBox -icon error \
            -message "B and L messed up for $row $col $toClr"
        return
    }
    # Remember then truncate clr's pipe
    #set L(stolen,$row,$col) [list $oldClr $L(path,$oldClr)]
    lappend L(stolen,$oldClr) [list $row $col $L(done,$oldClr) $L(path,$oldClr)]
    set L(delete,$oldClr) [lrange $L(path,$oldClr) $n end]
    set L(path,$oldClr) [lrange $L(path,$oldClr) 0 $n-1]
    set L(done,$oldClr) 0
    ShowPath $oldClr
    return steal
}
proc CheckUnsteal {} {
    global L B
    foreach arr [array names L stolen,*] {
        set clr [lindex [split $arr ","] 1]
        # NB. ordering is very important: search from end of old path
        # backwards, then if found we can ignore everything closer to beginning
        for {set idx 0} {$idx < [llength $L($arr)]} {incr idx} {
            set steal [lindex $L($arr) $idx]
            lassign $steal row col done oldPath
            if {$B($row,$col) ne ""} continue
            set L(path,$clr) $oldPath
            set L(delete,$clr) ""
            set L(done,$clr) $done
            set L($arr) [lrange $L($arr) 0 $idx-1]
            ShowPath $clr
            break
        }
    }
}
# Adds row, col to clr's path, truncating path if it intersects itself
proc AdjustPath {row col clr} {
    global L

    set L(delete,$clr) {}
    set n [lsearch -exact $L(path,$clr) [list $row $col]]
    if {$n + 1 == [llength $L(path,$clr)]} return
    if {$n > -1} {
        set L(delete,$clr) [lrange $L(path,$clr) $n+1 end]
        set L(path,$clr) [lrange $L(path,$clr) 0 $n]
    } else {
        lappend L(path,$clr) [list $row $col]
    }
}

# draws path for clr, deleting everything in L(delete,...) and
# drawing L(path,...)
proc ShowPath {clr} {
    global L S B

    # Stuff to be deleted
    foreach cell $L(delete,$clr) {
        lassign $cell row col
        if {[lindex $B($row,$col) 0] ne "node"} {
            set B($row,$col) ""
        }
        .c itemconfig cell_${row},$col -fill $::CLR(bg)
    }
    set L(delete,$clr) {}
    
    set xy {}
    foreach cell $L(path,$clr) {
        lassign $cell row col
        if {[lindex $B($row,$col) 0] ne "node"} {
            set B($row,$col) [list path $clr]
        }
        lassign [Cell2XY $row $col] . . x y
        lappend xy $x $y

        set bgClr [::tk::Darken $clr $S(darken,$L(done,$clr))]
        .c itemconfig cell_${row},$col -fill $bgClr
    }
    if {[llength $L(path,$clr)] < 2} {
        .c delete path,$clr
        return
    }
    if {[.c find withtag path,$clr] eq ""} {
        .c create line $xy -tag path,$clr -fill $clr \
            -width $S(lwidth) -capstyle round
        .c raise node
    } else {
        .c coords path,$clr $xy
    }
}
proc DoHint {clr} {
    global L S SOLUTIONS

    if {! $S(hints,canDo)} {
        MyDialog "Turn on hints on\nthe help dialog" ok {destroy .dialog}
        return
    }
        
    if {![info exists SOLUTIONS($S(stage),$S(level))]} {
        MyDialog "No hints available\nfor this level" ok {destroy .dialog}
        return
    }
    incr S(hints,used)
    set hintPath [GetHintPath $clr]
    set L(delete,$clr) $L(path,$clr)
    set L(path,$clr) {}
    ShowPath $clr
    set L(path,$clr) $hintPath
    set L(done,$clr) 1
    foreach cell $hintPath {
        lassign $cell row col
        StealCell $row $col $clr
    }
    ShowPath $clr
    CheckVictory
}
proc GetHintPath {clr} {
    global S BOARDS SOLUTIONS
    array set DIRS {w {0 -1} e {0 1} n {-1 0} s {1 0}}

    set me "$S(stage),$S(level)"
    set idx [lsearch $BOARDS($me) $clr]
    lassign [lrange $BOARDS($me) $idx+1 $idx+4] row0 col0 row1 col1
    set steps [dict get $SOLUTIONS($me) $clr]

    # The saved path may be from either end, build up both
    set path0 [list [list $row0 $col0]]
    set path1 [list [list $row1 $col1]]
    set lastStep ""

    # steps is: e3nws5  and count could be 2 digits, hence regsub
    foreach step [regsub -all {[nsew]} $steps " \& "] {
        set cnt 1
        if {[string is integer $step]} {
            set cnt [expr {$step - 1}]
            set step $lastStep
        }
        while {[incr cnt -1] >= 0} {
            lassign $DIRS($step) drow dcol
            set row [expr {[lindex $path0 end 0] + $drow}]
            set col [expr {[lindex $path0 end 1] + $dcol}]
            lappend path0 [list $row $col]
            set row [expr {[lindex $path1 end 0] + $drow}]
            set col [expr {[lindex $path1 end 1] + $dcol}]
            lappend path1 [list $row $col]
        }
        set lastStep $step
    }
    # Determine which path is correct
    if {[lindex $path0 end] eq [list $row1 $col1]} {
        return $path0
    }
    return $path1
}
# Determine size of a cell which determines the size of everything else
proc ComputeSizes {N} {
    global S

    set w [expr {max([winfo width .c], [winfo reqwidth .c])}]
    set w [expr {$w - 2*$S(margin)}]
    set S(sz) [expr {$w/$N}]
    set S(sz2) [expr {$S(sz)/2}]
    set S(radius) [expr {$S(sz) * $S(radius,factor)}]
    set S(lwidth) [expr {$S(sz) * $S(lwidth,factor)}]
}
proc CheckVictory {} {
    global L B

    foreach arr [array names L path,*] {
        if {[llength $L($arr)] < 2} return
        lassign $arr . clr
        lassign [lindex $L($arr) 0] row col
        if {[lindex $B($row,$col) 0] ne "node"} return
        lassign [lindex $L($arr) end] row col
        if {[lindex $B($row,$col) 0] ne "node"} return
    }

    # Every cell must be used
    for {set row 0} {$row < $B(N)} {incr row} {
        for {set col 0} {$col < $B(N)} {incr col} {
            if {$B($row,$col) eq ""} {
                AlmostVictory
                return
            }
        }
    }
    Victory
    return 1
}
proc AlmostVictory {} {
    if {$::B(almost)} return
    set ::B(almost) 1
    MyDialog "Almost done!\nNow fill all the cells" "skip ahead" {PlayAgain 1} \
        "keep playing" {destroy .dialog}
}
proc Victory {} {
    global S SOLUTIONS
    
    .c itemconfig solvedStar -state ""
    set me "$S(stage),$S(level)"
    if {$S(hints,used) > 0} {
        set hintText "\nwith a hint"
    } else {
        set hintText ""
        if {! [HasBeenSolved $S(stage) $S(level)]} {
            #set SOLUTIONS($me) [CompactPaths]
            set S(solved) [lsort -dictionary -unique [concat $me $S(solved)]]
            WriteRC
        }
    }
    if {$me eq [lindex [lsort -dictionary [array names ::BOARDS]] end]} {
        MyDialog "You finished the\nlast level!$hintText" \
            "restart" {PlayAgain 1} "play again" {PlayAgain 0}
    } else {
        MyDialog "Finished!$hintText" "next level" {PlayAgain 1} \
            "play again" {PlayAgain 0}
    }
}
proc CompactPaths {} {
    array set DIRS {1,0 s -1,0 n 0,1 e 0,-1 w}
    set allPaths {}
    foreach arr [array names ::L path,*] {
        set thisPath [lindex [split $arr ","] 1]
        append thisPath " "
        set row0 -1
        foreach cell $::L($arr) {
            lassign $cell row1 col1
            if {$row0 > -1} {
                set drow [expr {$row1 - $row0}]
                set dcol [expr {$col1 - $col0}]
                append thisPath $DIRS($drow,$dcol)
            }
            lassign [list $row1 $col1] row0 col0
        }
        lappend allPaths $thisPath
    }
    return [join $allPaths " "]
}
proc MakeStar {x y delta} {
    set pi [expr {acos(-1)}]

    # Compute distance to inner corner
    #set x1 [expr {$delta * cos(54 * $pi/180)}]  ;# Unit vector to inner point
    set y1 [expr {sin(54 * $pi/180)}]
    set y2 [expr {$delta * sin(18 * $pi/180)}]  ;# Y value to match
    set delta2 [expr {$y2 / $y1}]

    # Now get all coordinates of the 5 outer and 5 inner points
    for {set i 0} {$i < 10} {incr i} {
        set d [expr {($i % 2) == 0 ? $delta : $delta2}]
        set theta [expr {(90 + 36 * $i) * $pi / 180}]
        set x1 [expr {$x + $d * cos($theta)}]
        set y1 [expr {$y - $d * sin($theta)}]

        lappend coords $x1 $y1
    }
    return $coords
 }

proc ShowPickPuzzlePage {} {
    set ::S(state) pick
    FadeOut all
    DrawPickPuzzlePage
    FadeIn all
}
proc PlayChosenPuzzle {level} {
    set ::S(level) $level
    PlayAgain 0
}
proc PlayAgain {delta} {
    global S BOARDS
    destroy .dialog
    FadeOut all
    set S(state) play
    set S(hints,used) 0

    set S(level) [expr {$S(level)+$delta}]
    if {! [info exists BOARDS($S(stage),$S(level))]} {
        incr S(stage) $delta
        set S(level) [expr {$delta > 0 ? 1 : 30}]
    }
    if {! [info exists BOARDS($S(stage),$S(level))]} {
        set S(stage) 1
        set S(level) 1
    }
    WriteRC
    DrawBoard $S(stage) $S(level)
    FadeIn all
}
proc MyDialog {txt args} {
    set C .dialog
    destroy $C
    
    set w [expr {50 + max(150,[MeasureText $txt])}]
    set x [expr {$w/2}]
    set bg purple4
    canvas $C -width $w -height 170 -bd 0 -highlightthickness 0 \
        -bg $::CLR(dialog,bg)
    place $C -anchor c -relx .5 -rely .5

    $C delete all
    $C create text $x 20 -text $txt -fill $::CLR(dialog,txt) \
        -font myDialogFont -anchor n

    foreach {txt command} $args {
        set y [expr {[lindex [$C bbox all] 3] + 15}]
        set id [$C create text $x $y -text $txt -fill $::CLR(dialog,txt) \
                    -font myDialogFont -anchor n]
        lassign [$C bbox $id] . y0 . y1
        set id1 [$C create rect \
                     25 [expr {$y0-5}] [expr {$w-25}] [expr {$y1+5}] \
                     -fill $::CLR(dialog,button) -outline $::CLR(dialog,fg) \
                     -width 2]
        $C raise $id
        CanvasButtonBinding $C $id $id1 $::CLR(dialog,highlight) $command
    }
    lassign [$C bbox all] . . x1 y1
    incr y1 20
    $C config -height $y1

    set id [$C create rect 0 0 $w $y1 -fill $::CLR(dialog,bg) \
                -outline $::CLR(dialog,fg) -width 5]
    $C lower $id
    update idletasks
    grab $C
}
proc MeasureText {txt} {
    set longest 0
    foreach line [split $txt \n] {
        set longest [expr {max($longest,[font measure myDialogFont $line])}]
    }
    return $longest
}


proc Box {x y r} {
    return [list [expr {$x-$r}] [expr {$y-$r}] [expr {$x+$r}] [expr {$y+$r}]]
}
proc Cell2XY {row col} {
    global B S

    set x [expr {$B(xy0) + $col * $S(sz) + $S(sz2)}]
    set y [expr {$B(xy0) + $row * $S(sz) + $S(sz2)}]
    set box [Box $x $y $S(sz2)]
    set oval [Box $x $y $S(radius)]
    return [list $box $oval $x $y]
}
proc Test_Cell2XY {} {
    set x0 0
    for {set i 0} {$i < 7} {incr i} {
        lassign [Cell2XY $i $i] box oval x y
        set delta [expr {$x - $x0}]
        puts "$i : $x   $delta"
        set x0 $x
    }
}
proc XY2Cell {cx cy} {
    global B S
    set row [expr {int(($cx - $B(xy0)) / $S(sz))}]
    set col [expr {int(($cy - $B(xy0)) / $S(sz))}]
    return [list $row $col]
}
proc Recenter {W h w} {
    set h [expr {$h/2}]
    set w [expr {$w/2}]
    $W config -scrollregion [list -$w -$h $w $h]
}
proc FadeIn {tag} {
    FadeOut $tag fadein
}
proc FadeOut {tag {fadein fadeout}} {
    set ids [expr {$tag eq "all" ? [.c find all] : [.c find withtag $tag]}]
    set FADING [dict create]
    foreach id $ids {
        if {[.c itemcget $id -tag] eq "border"} continue
        set type [.c type $id]
        if {$type eq "image"} continue
        set gradient [Gradient [.c itemcget $id -fill] black]
        if {$fadein eq "fadein"} { set gradient [lreverse $gradient] }
        set fading [list fill $gradient]
        if {$type in {"oval" "rectangle" "polygon"}} {
            set gradient [Gradient [.c itemcget $id -outline] black]
            if {$fadein eq "fadein"} { set gradient [lreverse $gradient] }
            lappend fading outline $gradient
        }
        dict set FADING $id $fading
    }
    DoFade $FADING 0
    MyWait
}
proc DoFade {FADING step} {
    lassign $::S(fade,params) steps delay
    
    foreach id [dict keys $FADING] {
        foreach {what gradient} [dict get $FADING $id] {
            set clr [lindex $gradient $step]
            .c itemconfig $id -$what $clr
        }
    }
    incr step
    if {$step < $steps} {
        after $delay [list DoFade $FADING $step]
    } else {
        set ::S(wait) 1
    }
}
proc MyWait {} {
    destroy .grab
    label .grab ; place .grab -x -100 -y -100 ; grab .grab
    tkwait variable ::S(wait)
    destroy .grab
}
proc Gradient {fromColor toColor} {
    if {[info exists ::MEM($fromColor,$toColor)]} {
        return $::MEM($fromColor,$toColor)
    }
    set steps [lindex $::S(fade,params) 0]
    lassign [winfo rgb . $fromColor] r1 g1 b1
    lassign [winfo rgb . $toColor] r2 g2 b2
    
    set steps [expr {$steps <= 1 ? 1 : double($steps - 1)}]
    set gradient {}
    for {set step 0} {$step <= $steps} {incr step} {
        set r [expr {int(($r2 - $r1) * $step / $steps + $r1) * 255 / 65535}]
        set g [expr {int(($g2 - $g1) * $step / $steps + $g1) * 255 / 65535}]
        set b [expr {int(($b2 - $b1) * $step / $steps + $b1) * 255 / 65535}]
        lappend gradient [format "#%.2x%.2x%.2x" $r $g $b]
    }

    set ::MEM($fromColor,$toColor) $gradient
    return $gradient
}
proc CanvasButtonBinding {C text_tag box_tag highlight cmd} {
    global _CB
    set who "$C,$text_tag"
    set oldFill [expr {$box_tag ne "" ? [$C itemcget $box_tag -fill] : ""}]
    set oldOutline [expr {$box_tag ne "" ? [$C itemcget $box_tag -outline] : ""}]
    set _CB($who,data) [list "" $C $box_tag $highlight $oldOutline $oldFill $cmd]
    foreach tag [list $text_tag $box_tag] {
        if {$tag eq ""} continue
        foreach action {Button-1 ButtonRelease-1 Leave Enter} {
            $C bind $tag <$action> [list _CanvasButtonBinding $who $action]
        }
    }
}
proc _CanvasButtonBinding {who what} {
    global _CB

    lassign $_CB($who,data) state C box_tag highlight oldOutline oldFill cmd
    if {$what eq "Button-1"} {
        lset _CB($who,data) 0 "in"
        if {$box_tag ne ""} {$C itemconfig $box_tag -fill $highlight -outline $highlight}
    } elseif {$what eq "ButtonRelease-1"} {
        lset _CB($who,data) 0 ""
        if {$box_tag ne ""} {$C itemconfig $box_tag -fill $oldFill -outline $highlight}
        if {$state eq "in"} {uplevel #0 $cmd}
    } elseif {$what eq "Leave"} {
        if {$state ne ""} { lset _CB($who,data) 0 "out" }
        if {$box_tag ne ""} {$C itemconfig $box_tag -fill $oldFill -outline $oldOutline}
    } elseif {$what eq "Enter"} {
        if {$state ne ""} { lset _CB($who,data) 0 "in" }
        if {$box_tag ne ""} {
            if {$state eq ""} {
                $C itemconfig $box_tag -outline $highlight
            } else {
                $C itemconfig $box_tag -fill $highlight -outline $highlight
            }
        }
    }
}
proc Help {} {
    set msg "Flow Colors\nby Keith Vetter\n\n"
    append msg "The object of Flow Colors is\n"
    append msg "lay pipes to connect all pairs\n"
    append msg "of colors. A level is solved\n"
    append msg "when all the colors are paired\n"
    append msg "and the entire board is filled.\n\n"
    append msg "Flow Colors is based on a game\n"
    append msg "by Mohammed el-Madhoun\n"
    append msg "http://moh97.us/flow\n"
    if {[llength [array names ::SOLUTIONS]] > 0} {
        MyDialog  $msg "ok" {destroy .dialog} "hints" {HelpHint}
    } else {
        MyDialog  $msg "ok" {destroy .dialog}
    }
}
proc HelpHint {} {
    set ::S(hints,canDo) 1
    set msg "Flow Colors Hints\n\n"
    append msg "You can now get a hint by\n"
    append msg "holding down the shift key\n"
    append msg "and double right click on\n"
    append msg "any node.\n"
    MyDialog  $msg "ok" {destroy .dialog}
}
proc ReadRC {} {
    if {[file exists $::S(rcFile)]} {
        uplevel #0 source [list $::S(rcFile)]
    }
    if {[info exists ::S(lastPuzzle)] && [llength $::S(lastPuzzle)] == 2} {
        lassign $::S(lastPuzzle) ::S(stage) ::S(level)
    }
}
proc WriteRC {} {
    global S SOLUTIONS
    catch { 
        set fout [open $S(rcFile) w]
        puts $fout "set ::S(lastPuzzle) {$S(stage) $S(level)}"
        puts $fout "set ::S(solved) {$S(solved)}"
        if {0} {
            puts $fout "array set SOLUTIONS {"
            foreach arr [lsort -dictionary [array names SOLUTIONS]] {
                puts $fout "   $arr {$SOLUTIONS($arr)}"
            }
            puts $fout "}"
        }
        close $fout
    }
}
################################################################
image create photo ::img::left -data {
    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
    AElFTkSuQmCC}
array set SOLUTIONS {
   1,1 {blue s5w green s2 darkorange n4 cyan s3w2 red e3n5 yellow en3e}
   1,2 {blue wn3w2 green s2 darkorange ws5e5n cyan se red n3e2s yellow n2}
   1,3 {red e5s yellow w2s2 blue s3 green s3w2n2w darkorange sw2n4e3}
   1,4 {yellow e2n5w2s2 blue n3 green wn3 darkorange nwn3w2s5e2}
   1,5 {yellow s2e2n blue e2s2w4n5e green ne2s5 darkorange s3e2}
   1,6 {yellow wsw3n2ene2 blue e5s5w green nw4s darkorange ene2}
   1,7 {blue n5e2s3 green s2 darkorange s2w4n4 cyan e2n red n2w yellow s2}
   1,8 {red s3w5n yellow e2s blue s2w2nwn3e2 green e2s3 darkorange ses}
   1,9 {red n3e yellow n2 blue s4 green s2e2n5 darkorange en2w3s5e}
   1,10 {blue w4 green e4 darkorange s3e5 cyan w3 red w4nwn yellow e4}
   1,11 {red w4n3e5s yellow e3 blue se4 green se5n2 darkorange e2}
   1,12 {yellow ne4n3w2 blue ws3e3 green e3s5w4 darkorange w2n}
   1,13 {red ene2s2 yellow ne2s2e3n2wn2 blue n2e2 green wn2w2n darkorange en}
   1,14 {red n2 yellow n5e3s2ws3 blue s4e green n4e darkorange n4w}
   1,15 {blue es green ses darkorange ne cyan n2en2e2s red s3 yellow e2n5w5}
   1,16 {blue e2s5w5 green wsw darkorange s2 cyan ne2 red e2n yellow n2ene3}
   1,17 {blue e2s4w green se3n3 darkorange s2 cyan nw red w3sw yellow ws3ese}
   1,18 {red e2 yellow e2n4 blue e2 green e3s2w3s2e darkorange w5s5}
   1,19 {red w2s yellow e3 blue w3s2 green s5e5n3w darkorange s3e3}
   1,20 {yellow en blue n2e2s green s2w2n4e4s darkorange e2n5w5}
   1,21 {blue se green e4n5 darkorange s2 cyan s4w4s red en2w yellow se3}
   1,22 {red s5w5n yellow e3s2 blue en4w4 green nw darkorange sese}
   1,23 {yellow e3n blue w5s5e5 green w2 darkorange w4n3e4}
   1,24 {blue en green ese darkorange sw3n cyan s5 red e2s3ws yellow sw4n2e}
   1,25 {blue w2 green ws5 darkorange w3 cyan s3e3n2w red n4e4 yellow se}
   1,26 {red e5s5w2 yellow en blue w2sws green en3w4 darkorange ne3}
   1,27 {red ws2es yellow s4e2 blue en3w green nwn4e3s darkorange n4w}
   1,28 {yellow e4n5w4 blue e3 green s2e3 darkorange e3s2w4s2}
   1,29 {blue ne5s3 green s2wsw2n2 darkorange es3 cyan sw2 red en yellow nen2}
   1,30 {red ne5s3 yellow ws2e blue sw2n3wnw green es3 darkorange e2n2w}
   2,1 {blue w6s6e5 green sw darkorange w4s cyan e2s3 red w3n2ene4n yellow nw}
   2,2 {blue e2 green nw3 magenta w2n6e darkorange ws4e cyan sw2nwn red e3n6w4 yellow es}
   2,3 {blue w6n2 green se2 magenta e2 darkorange w2n2 brown es2 red s2e4n cyan e6s yellow wnw5}
   2,4 {red n3ene4s4 yellow nw2 blue n2e2se green e6n6w6s darkorange w2n}
   2,5 {blue se3s2 green en3 darkorange s2ese4 cyan s6e red es2 yellow e4s5w3n3}
   2,6 {blue enwn2e2sw green sw magenta se4n2 darkorange sen3e2 cyan es2 red sw6n2 yellow e2n3}
   2,7 {red e4 yellow se4n blue e2 green ne6s2w5 darkorange s4e6n3w2sw2}
   2,8 {blue w3 green w5n6e4 magenta nw3 darkorange n3e5n cyan wsw4 red e3 yellow s2}
   2,9 {blue w2 green w3n3 darkorange ne5 cyan sws4e6n2 red sw3s2e3 yellow w4n2}
   2,10 {blue n4 green en6w4s5 darkorange s5e cyan ws6 red n2e2s4 yellow n2}
   2,11 {blue ne3s2 green w2 magenta sws2 darkorange se3s cyan s2e red n4 yellow se6n6w2s3}
   2,12 {blue sw3 green n2e6s2w3 magenta n3 darkorange s3e2ne2s2 cyan s3 red ne4 yellow ne3}
   2,13 {blue e2 green sw6n2 darkorange s3 cyan ws red ses2e2nen2 yellow wsw4n2wn3e5}
   2,14 {blue en green n2w magenta n3wn2 darkorange s2 brown nw2s5e red n2w3s cyan s4w2sw2 yellow e2n2}
   2,15 {red sws4e6n6w2s yellow s4w4 blue es2es2w3n green wn2 darkorange ws}
   2,16 {red ese3n yellow e2 blue e2s green n4w6s5e darkorange wnw3n3e4sw3s}
   2,17 {blue e2se green e2se3n magenta nw3 darkorange n3w5s3e cyan w2n2e red en4 yellow wnw2n}
   2,18 {blue w2s3 green n2wn2 magenta se4n3e2 darkorange s2 cyan s2 red n2w6s4es yellow ne3}
   2,19 {blue n2 green ne2 darkorange sw3 cyan sw3s3e2ne red n5e3ne2s3 yellow n6e3}
   2,20 {red w2s5e6n6w3 yellow ese2n2 blue nw2 green w2 darkorange e2s4w4n}
   2,21 {blue wnw3 green e2s2e darkorange es2w3s2e5n6w2 cyan s3 red s4w3 yellow es}
   2,22 {blue e4n3w2s green w2s magenta n2ene4n darkorange s2w6 cyan w4s red nw3 yellow e3}
   2,23 {blue n2 green s6e4n2 darkorange ne3n cyan wsw3s3e red w2n5e3 yellow n3w2}
   2,24 {blue se2 green sw4s2w darkorange n2e6 cyan wnw2sw3n2en2e4 red w3 yellow e3s}
   2,25 {blue en3w3nw2 green s6e3 darkorange ese3s cyan se3s red nw4 yellow e3se2n2}
   2,26 {red n3w yellow e2n2wn2e3s4e blue e6n6w4 green ws4e darkorange n3}
   2,27 {red e2s5w5n2 yellow s2e2n3 blue w6n6e3 green s3 darkorange n2w2s}
   2,28 {blue e5n6w green e2s3 magenta ese2 darkorange nen cyan w3n2w2s red se2n3 yellow se3}
   2,29 {blue e2 green se2n2e darkorange w3n cyan nw3 red ne4s3 yellow nwn4e6s5w2}
   2,30 {blue es3 green e2 magenta e2s darkorange es2e2s2e3s2w6n2 cyan s2w red se4 yellow s2e2s}
   3,1 {blue es2w green s5w3nwn3e2 darkorange se cyan w3s5ese5n5 red w4s7e yellow n2e2s}
   3,2 {blue n4w4n green sw3s darkorange ene4n5 cyan e5s7w4 red s2e4s3w3sws yellow e2}
   3,3 {blue e4s3e green n4w5sws2e4s3e2s darkorange w3s2e6 cyan s2w3 red ws4e3 yellow e3}
   3,4 {blue wsw3 green n3e6s magenta nw4 darkorange e3ne2 cyan w4s2e4 red wn2e3 yellow sw7n7}
   3,5 {blue s4w2 green w6s4e darkorange e5 cyan e6nw4nenw red n2e2 yellow wn2e2n3e2ne2swse}
   3,6 {blue n2w3n2e4 green nw4nw2s magenta es3e3s darkorange n3w3 cyan w4n red e2s3e3s2e2 yellow wn5}
   3,7 {red wnenes6wn3ws3wn2wnws2esw3n2es yellow w2s blue e3s green wn2ene2sw darkorange ne4s2}
   3,8 {blue n2w4sws green n2ene5n2 magenta sw2 darkorange sw5s cyan w5s7e4n red w2n2w yellow wn2w2}
   3,9 {blue enes2e2n5w4se green es3 darkorange ne2 cyan se4n2 red n3e2s7w7n2 yellow n5}
   3,10 {blue wswseswse green w2nw2se darkorange nes2wse2nesenen4 cyan w2 red n2w4s5 yellow sw6n4}
   3,11 {blue e2 green wsesw magenta es7w3nw4s darkorange eneses cyan s2 red e2se yellow esw2n5es3en3es3}
   3,12 {blue e2 green wnw3sws2 darkorange e2n cyan wsws2ws2e4se2nw red ws7e4 yellow e2nwne2senen3w}
   3,13 {blue e2 green wnwn2e3s darkorange s5ese2ne3 cyan n3w5s4es red n3 yellow se7n3w2nwnw}
   3,14 {blue sw2s2en green n2w4 darkorange e5 cyan wn4e2ne4s4 red nes2w2se2 yellow w2s6e6s}
   3,15 {blue wn5ene5s4w2n2 green ws7e3 darkorange se3 cyan e2 red w3nwnwn3ene3s2 yellow nen}
   3,16 {blue n3e green s7w2n7w5 magenta s6 darkorange n3e4 cyan w3s4e red s2 yellow s2e4n3}
   3,17 {blue e7s7w green enen magenta e4s3 darkorange n3w6s2 brown w3 red w3s2 cyan s3e3nen yellow ne}
   3,18 {blue e2s green s5 magenta sw darkorange s5 purple s4w6n4 brown ws2e4n3 red ne2s cyan ese2n3w3 yellow en}
   3,19 {blue s2w4s2e7n3w green w5 magenta sw3 darkorange es3w2nw brown ene3ses red wsw2 cyan ws3e2 yellow se}
   3,20 {blue ne2 green w3n4 magenta w2 darkorange w2s2es4 purple n2w2 brown n2e4s2w2s red wnw3n2e cyan e2n3 yellow es2e3}
   3,21 {blue s4 green e2n2wn2wn2w4s magenta sws darkorange s2 brown wn7e5 red nenen cyan wn2 yellow en5w2sws}
   3,22 {blue es7w5n5 green w3n4 darkorange nw cyan ws5 red s2e2n4 yellow wn7e3seses2}
   3,23 {blue w4n green w2sw2n7 magenta e2s6w4 darkorange nw2s3e5s3 cyan w3s red ne3 yellow n2w4}
   3,24 {blue wn4e2s3 green ws7 magenta s3e3n darkorange ses5e5n7w5 cyan es red s2 yellow s4}
   3,25 {blue e2s5 green w3s magenta e2s2w darkorange ene3n3w3 cyan en7w4 red se2s5w3 yellow w3n3e}
   3,26 {blue e2s2 green enene2s3ws magenta wnw darkorange e7n7w4s brown nen red se6n cyan s2 yellow s3ese}
   3,27 {blue wn4e4s green w2s2 magenta n2e3n3w5 darkorange nw7s7e3 cyan enen3 red e3n yellow ws}
   3,28 {blue nw7s7e3 green s2w2 magenta n2e darkorange n5wnw3 cyan e2n4 red en2e2s3 yellow e4n4}
   3,29 {blue w3s7e7n4 green e3nes3 darkorange es2es2 cyan n4en2ws red nenwnw yellow s3e2nes}
   3,30 {blue n5e2s3 green n2 magenta s3 darkorange es7w7n7e4 cyan s2 red e3n2w2n3 yellow w3s}
   4,1 {blue nw4 green e2 magenta e4n2e2 darkorange w2 purple s3w8n2e brown e6n2 red s2e3s cyan wn yellow e4n2e2n4w8s5}
   4,2 {blue e6ses2 green s4 magenta n2 darkorange s2 brown es8w4n2e2n4wnw5s2 red n4e cyan s3w2 yellow n3w3s2ws4e2}
   4,3 {blue ne2 green e2nw3ne3nwnwswnw2s3 magenta s4e2n6e2ne3sw2 darkorange s2 cyan s4 red w5ne2 yellow nene2ne5s3}
   4,4 {blue ws4e green w4s2 magenta s2 darkorange s4e2s2w4n7 cyan w6s6e2 red nw3s2w yellow s8e6n5e}
   4,5 {blue en2en3 green e2s3e magenta s6e8n2 darkorange es6 brown s3ws red w4n2 cyan nen5w5s2es2w2s3 yellow e2s2}
   4,6 {blue s2ws green e4 magenta nw5s4w darkorange e2n4e2 purple n5w2 brown s2e white en8w8s5 red wn3 cyan wn3e yellow e3}
   4,7 {blue n3 green n2e2ses5 magenta e2s7 darkorange ws4e5n2 purple ws4 brown s2e white s2e3n7w red en2 cyan s4 yellow s8}
   4,8 {blue e8s green e4s2w3s4e3 magenta w2 darkorange ne3 brown ese3n4w2sw4s red n2 cyan w8n6e3 yellow nw3sw}
   4,9 {blue s8e8n5 green w2s3 magenta s2 darkorange n3 brown es4e2n4e4s2 red e3n cyan s3w5s yellow w3sw2n6}
   4,10 {blue se2 green en2w2n4e8s6 magenta n3 darkorange wn6w6s2e3s4e cyan w5n3e red wn4w3 yellow e2n3}
   4,11 {blue se green e3s magenta w3n2e3n2 darkorange e3s6 purple es2 brown s4 red eseses3 cyan ne7s8w2n6w yellow nwnw2n2}
   4,12 {blue sw2 green w3 magenta w4n5e2 darkorange wsw4 brown n2e8s3wsw6s3e4se3n2ws red swsw3 cyan e3 yellow e4s}
   4,13 {blue n2w3 green n6e6 magenta s2e4n8w8s8e2n4 darkorange es2es brown wnw red ws3 cyan s2e yellow w5}
   4,14 {blue e2 green se4n2 magenta sw2 darkorange n2e6nenen3w4s purple se2 brown nw2sw3nw red w5n4e3s cyan en2 yellow s2e6n4}
   4,15 {blue w6s2e green w3s3enesenesen3wsw3 magenta s2 darkorange e7 cyan e8s8w2nen4w2sw2 red e2 yellow s2}
   4,16 {blue w3 green s7w2n2 magenta en darkorange wsw4s2 purple ne5 brown n6 red sen2w3sw2n6e4 cyan wse8 yellow e4n2}
   4,17 {blue ne5s3w2s green e8n4 darkorange nwsw5ne4n3w2n2w2sesws2enesen cyan s3w7 red w2 yellow sw2s2}
   4,18 {blue w3s6 green w2n3 magenta w2 darkorange n2w2 purple sw3 brown s3 red n5e3ne cyan s8 yellow w6n8e7s4w2}
   4,19 {blue s3w7n8e6s green es3w3s magenta w2s3 darkorange w4n2 purple n4e2 brown sw2nw red s3e3s cyan n4w yellow sw2n2}
   4,20 {blue e2s5 green swsws2w3n4 magenta s3e2 darkorange s2w5n5 purple wn2 brown n3w2s2wsws red sws cyan n2wn3 yellow n4w2n2e4}
   4,21 {blue n2e3 green sw8n5e2 magenta n6w8s2e6 darkorange wn2e3n3w6 cyan wn3e red sese3n yellow n2w2}
   4,22 {blue s2e green s2es2w8n4e2s magenta w3s darkorange w4s3e3s2es purple e2s2 brown w3 red n2wnw cyan s4e yellow nw3s6w2nw}
   4,23 {blue s4 green en4wnwn magenta eses darkorange ne3 purple wsw3n2 brown s3e red nw3n2w cyan nw8s3es5e5ne yellow w2s2es2e}
   4,24 {blue s3w2n6 green n5w5s magenta s5 darkorange w2s8e8n2 brown e3s3 red nw2n3w2n cyan s3e2 yellow e2s3e}
   4,25 {blue sw3s2ws green s8e7n2 magenta n4e5 darkorange en2e2 brown w2n red e3s4w2s cyan s8 yellow s7e5n}
   4,26 {blue e4n green w5n3e4 magenta s3e3 darkorange w2 purple w3n2w4n2e3 brown e3nen3e red w3s cyan en3 yellow e2n6w3s2w3}
   4,27 {blue swswn green wnen3w2sw magenta s2 darkorange e2s6w brown wn4en3en red wse2nese3n8w4s cyan s2ws4e2 yellow ws3}
   4,28 {blue e4n4w3n2e green sw3n2en5 magenta ne darkorange s3e3s2w cyan nwn6e5s3w2 red n5e2s7w yellow e3s}
   4,29 {blue nw4s green s2w4 magenta w2sw5s6e darkorange e4 brown e2 red w4s2ese6n4w cyan e8s3w yellow ws4w4}
   4,30 {blue n5en2 green s2w3s2w2s2es2w3n4 magenta es2e darkorange s3 purple sw3 brown wn7w2s2 red n2e3 cyan sw3s2w yellow n3w2s3}
   5,1 {blue s3 green s5w4 magenta w6ne7 darkorange s6en3e3n2w3ne4s3 cyan w4s2e5 red w2 yellow s8w}
   5,2 {blue e8 green w8n3 magenta se2n2 darkorange s2 brown e2 red e8s5wn4ws3wn cyan nw2n2esenesen yellow s2e4ne}
   5,3 {blue s4 green s6e2 magenta w2s6e7 darkorange e4 purple s4en brown s4 red s5 cyan w3n2wn4w4s7 yellow n5}
   5,4 {blue ne7n7 green s3 darkorange w5seswse2sw2se2sw2s2 cyan e6 red e4n6wswsesw2se2sw2 yellow ne2s}
   5,5 {blue esw5s6w2n7 green w4s6 darkorange w3n4e5n2 cyan n5w4s2e2 red w3 yellow e7n4w2}
   5,6 {blue s4 green s3e5 magenta w3 darkorange e5 purple s3e4 brown e4s white s3e8n8 red n5w4 cyan e4s yellow w5s}
   5,7 {blue s3 green s6w7n2e3nw2n2esen magenta e4 darkorange s4e purple e5 brown w5 red swnwsw6n4 cyan s3 yellow sen2w}
   5,8 {blue e7 green enwn6e7 magenta s2 darkorange s3 purple se6 brown w4 red swn3 cyan se2n2e3sw2s2wse4nw2ne2 yellow e2s}
   5,9 {blue e7 green e6n magenta w5 darkorange w2swnwswnwswn2 purple s6 brown e8 red n2w6 cyan e5 yellow wsw6s4}
   5,10 {blue n3e4 green w3ses3w4n5es4e2n darkorange n8 cyan s4w6 red w7n2e6n4e yellow s5}
   5,11 {blue ne8 green s3e6 magenta w4 darkorange w7 purple s3 brown n2es2en2es2 white e3 red n6e2s2 cyan e5s5 yellow s5}
   5,12 {blue w7s8 green w6n4e6 magenta e6s4w3n darkorange s2 brown e2 red e5 cyan e6 yellow swnw5s2e3}
   5,13 {blue w6 green s8 magenta w3 darkorange w6n8e7 purple w5 brown e3sw3 red s3 cyan e4s4 yellow n5e6}
   5,14 {blue s6e green e5 magenta s2 darkorange wswn8 purple s5 brown w2s3 white w4 red n5 cyan s3w3n5e4 yellow n7w5}
   5,15 {blue n5 green s5e8 magenta w5 darkorange n2w7s2 cyan w6n2e5 red e3 yellow n6es3en3e3sw2se3}
   5,16 {blue e3 green s5 magenta w5swn6 darkorange nes4 purple e3 brown w5n white w3 red nwswnws cyan senesen yellow e2nwnenwnenwnenwne}
   5,17 {blue w8n7 green s6 magenta w5 darkorange n6e6s5 brown e8 red s3 cyan s3 yellow n4w2s4wn4w}
   5,18 {blue s5 green s8e8 magenta e2 darkorange s5e6 purple w7 brown e7 red s4 cyan w4s3e4n2w3 yellow w4}
   5,19 {blue n3ene5 green s3w3s2e3sw3se3sw4n4 magenta s4 darkorange ws8 purple n2 brown e2 red n3en3 cyan ne3 yellow w4s2}
   5,20 {blue s2 green s2 magenta wn2w2sesw darkorange s8e8 purple e5 brown ws4ene6n2w3 white w5 red e2n3 cyan s yellow n3e5}
   5,21 {red w2ne7 yellow s6e8s2w5 blue s2e4nw3ne3nw4ne4nw4 green s3 darkorange s5e2n5w}
   5,22 {blue s7 green w6s3 magenta nese darkorange s8e5nesen2wnenw2nw2 brown wse2ne red swnw3 cyan s yellow e5s7}
   5,23 {blue s3e8n green w6 magenta s3 darkorange w7s2e6 brown w4 red wsw6 cyan e6s2 yellow sw6n4e6}
   5,24 {blue e3n2w3 green e4 magenta s4w7 darkorange w2s2e2 brown e8n5 red w6s2e6 cyan nwsw6 yellow w8}
   5,25 {blue ws4 green n3 magenta ne6n4w3 darkorange w2n4w2 brown e2s2w3 red w5 cyan e4s6 yellow e8n8w5}
   5,26 {blue s7e green s7e5n6w3 magenta e8n8w4 darkorange s6 cyan s4en4 red s4 yellow s6}
   5,27 {blue s4 green s4wse3nwnen3 magenta e2s6 darkorange ws8e5 cyan s5 red ne6s8w2n6 yellow ws3}
   5,28 {blue s6 green w5s7w magenta s3w2se5n4en4 darkorange e6 brown s3 red w3s3 cyan s3ws4wn4w yellow s4e}
   5,29 {blue s5es3 green n4e6s2w4s2 magenta s2 darkorange sen2wn6 brown n7e4 red e2s4w3n3 cyan s2 yellow e3s5}
   5,30 {blue e2 green e3s5w4 magenta w2s3wn4 darkorange w2n2e8s8 brown se5sw5 red e3s2w3 cyan esene3 yellow w2}
}
image create photo ::img::undo -data {
    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}
image create photo ::img::right -data {
    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
    JRQAAAAASUVORK5CYII=
}
image create photo ::img::help -data {
    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
}
image create photo ::img::up -data {
    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
    rkJggg==
}
################################################################

ReadRC
DoDisplay
DrawBoard $S(stage) $S(level)
return

Comments


ak - 2014-04-24 19:09:21

Oh yes on the addictive. Have this on my pad. Running the pipes with the fingers is very natural. Lost quite some time on it. Having it for Tcl is really nice. Thanks, Keith.

HJG 2014-04-24 - Nice program, but I encountered some problems:

  • Tcl 8.5 complains about the image-format kpv added 'package require Img
  • when running the program the second time, it fails to load/source the savefile .flowColorsrc from a directory with spaces (as in 'C:/Dokumente und Einstellungen/...') MG added list around the dir to hopefully fix this
  • $tcl_platform(os) returns on WinXP with "Windows NT", so the default filename for linux is used MG Changed to tcl_platform(platform)
  • also: sometimes, when connecting the last pipe from A->B the level does not end. But it works when doing it B->A. I haven't found a clear testcase for this yet...

AMG: The title reminds me of this 800 State classic: [L3 ]. Listen as you play.

AMG: I can use the Next Puzzle button to go from Level 3 to Level 1 of the next stage, but I can't use Previous Puzzle to go back to Level 30. Also I find it odd that the button is labeled Next/Previous Puzzle even though it advances through what are called Levels. kpv bug, now fixed