Bubbles2 (pour buller 2 fois avec Tcl)

 

ulis, 2004-09-12. Another bubble game.


Script

  ###############################
  #
  #   Bubbles2
  #
  set version 0.9.0a
  #
  #   Copyrights ulis, 2004
  #
  #   Licence NOL
  #
  ###############################

  # ####################
  # Documentation
  # ####################
  # compile:
  #   use freewrap (http://sourceforge.net/projects/freewrap/)
  # install:
  #   put the executable in your path
  # use:
  #   run & play
  # ####################

  # ===========
  # package
  # ===========

  package require Tk
  catch { console hide }

  # ===========
  # images
  # ===========

  set images(8) \
  {
    00 00 00 52 f7 00 00 00
    00 52 bd c6 c6 c6 52 00
    00 52 8c 94 94 94 8c 00
    52 6b 84 8c 8c 8c 7b 63
    52 84 b5 ce ce c6 a5 52
    00 84 bd e7 e7 d6 a5 00
    00 52 94 c6 ce b5 52 00
    00 00 00 52 84 00 00 00
  }
  set images(16) \
  {
    00 00 00 00 00 00 c0 c0 c0 c0 00 00 00 00 00 00
    00 00 00 c0 c0 ef ff f7 f7 f7 ff a5 c0 00 00 00
    00 00 a5 a2 e7 de de de de de de d6 ce a0 00 00
    00 a0 a0 bd bd bd c6 c6 c6 c6 c6 bd bd a3 9a 00
    00 92 9a a5 a5 a5 a5 ad ad a5 a5 a5 a5 ab 8a 00
    00 92 90 94 9c 94 94 94 94 94 94 8c 8c 83 82 00
    80 a2 8a 8b 84 84 84 8c 8c 8c 84 84 7b 63 8a 80
    80 aa 6b 7b 84 8c 8c 8c 8c 8c 8c 84 7b 73 73 80
    80 c3 73 8c 9c a5 ad ad ad ad ad a5 94 84 7b 80
    80 c3 84 9c b5 c6 ce ce ce ce c6 bd a5 8c 73 80
    00 aa 84 a5 c6 d6 de e7 e7 e7 d6 c6 ad 8c 73 00
    00 94 84 a5 bd d6 e7 e7 e7 e7 d6 bd a5 84 8a 00
    00 a0 8b 94 b5 ce de e7 e7 de ce b5 94 73 90 00
    00 00 a0 73 94 b5 c6 ce ce c6 b5 94 73 ad 00 00
    00 00 00 a0 9c 84 a5 ad ad a5 8c 73 90 00 00 00
    00 00 00 00 00 00 80 80 80 80 00 00 00 00 00 00
  }
  set images(24) \
  {
    00 00 00 00 00 00 00 00 00 7c 46 2e 2d 45 7b 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 73 56 71 8e 9a 9a 8e 70 55 73 00 00 00 00 00 00 00
    00 00 00 00 00 90 43 8c db fc ff ff ff ff fb dc 8c 43 8f 00 00 00 00 00
    00 00 00 00 6c 45 cc fa ee e8 e6 e6 e6 e7 e7 ee fa cd 45 6c 00 00 00 00
    00 00 00 6f 3a c4 e0 d2 d5 d7 d8 d8 d8 d8 d7 d5 d2 e1 c3 39 70 00 00 00
    00 00 96 2e 86 cc be c3 c4 c5 c5 c5 c6 c5 c5 c5 c4 bf cc 86 2e 96 00 00
    00 00 41 47 95 b1 ae b1 b2 b3 b3 b4 b4 b3 b3 b2 b1 ae b1 94 47 41 00 00
    00 81 3c 4f 88 9f 9d a0 a1 a2 a3 a4 a4 a3 a2 a1 a0 9d 9f 88 50 3d 82 00
    00 55 4a 52 74 8f 8d 91 93 94 94 94 94 94 94 92 91 8e 8e 75 52 4b 4f 00
    8e 4c 52 59 64 7e 83 86 88 8a 8a 8b 8b 8b 8a 89 87 84 7f 65 59 55 43 c1
    63 4e 58 60 68 74 80 84 86 87 88 88 88 88 88 86 84 80 74 68 61 5a 49 8c
    50 52 5e 68 73 7b 83 88 8c 8d 8e 8d 8d 8e 8e 8c 87 82 7a 72 68 5e 51 64
    53 57 64 71 7f 8a 92 99 9e a0 a0 a0 a0 a0 a0 9d 98 91 87 7c 70 64 55 66
    68 59 6a 7a 8a 98 a3 ad b2 b4 b5 b5 b5 b5 b4 b1 ab a0 93 86 77 69 53 8e
    93 5e 6f 82 95 a7 b6 c0 c7 ca cb cc cc cb c9 c5 bd b0 a1 8f 7c 6c 53 c2
    00 69 6e 87 9c b1 c1 ce d5 da dc dd dd dc d9 d3 c9 ba a7 94 7f 69 61 00
    00 91 64 88 9d b2 c5 d4 dd e3 e6 e7 e7 e6 e2 d9 cd bb a7 91 7e 5d 8d 00
    00 00 64 80 97 ad c1 d1 dc e3 e7 e8 e8 e5 e0 d5 c6 b4 9e 89 75 5d 00 00
    00 00 a4 69 8f a4 b9 cb d8 e0 e5 e7 e6 e2 da cd bd aa 93 81 5e a1 00 00
    00 00 00 8e 73 99 ad c0 ce d8 de e0 df da d1 c3 b1 9c 8a 66 88 00 00 00
    00 00 00 00 90 76 99 ae bc c8 ce d1 d0 ca c0 b2 9f 8a 69 89 00 00 00 00
    00 00 00 00 00 ab 7c 8b a3 b3 ba bd bc b6 ac 9a 80 72 a6 00 00 00 00 00
    00 00 00 00 00 00 00 a0 89 8b 96 9b 9a 92 85 83 9d 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 c7 9d 86 85 9c c6 00 00 00 00 00 00 00 00 00
  }
  set images(32) \
  {
    00 00 00 00 00 00 00 00 00 00 00 00 b0 79 52 41 41 52 79 b0 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 d8 8e 58 49 55 61 66 66 61 54 48 57 8e d8 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 84 3e 51 91 c9 e6 f1 f5 f5 f1 e6 c9 91 51 3e 83 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 c0 4c 45 a6 ed fe fc f7 f5 f4 f4 f5 f8 fc fe ed a6 45 4c c0 00 00 00 00 00 00
    00 00 00 00 00 a4 3c 5c d5 f6 e9 e5 e7 e8 e8 e9 e9 e8 e8 e6 e5 e9 f6 d5 5c 3c a4 00 00 00 00 00
    00 00 00 00 a4 3a 5c d0 e4 d7 db dc dd dd de de de dd de dd dd db d7 e4 d0 5c 3a a4 00 00 00 00
    00 00 00 c0 45 4b a9 d8 ca d0 d0 d1 d1 d2 d2 d2 d2 d2 d2 d1 d1 d0 d0 cb d8 a9 4b 45 c1 00 00 00
    00 00 00 53 48 6a c2 bf c2 c3 c4 c5 c5 c6 c7 c6 c6 c6 c6 c5 c5 c4 c3 c2 c0 c2 6b 48 53 00 00 00
    00 00 87 45 50 80 b8 b2 b5 b6 b8 b9 ba ba ba bb bb bb ba ba b8 b8 b7 b5 b2 b8 7f 52 45 87 00 00
    00 d7 4c 54 53 80 ab a6 aa ac ad af af af b0 b0 b0 b0 af af ae ad ac aa a7 ab 81 54 54 4d d8 00
    00 92 44 59 58 78 9d 9c 9e a1 a3 a4 a4 a5 a6 a7 a6 a6 a5 a5 a4 a2 a1 9f 9c 9d 78 59 5a 44 93 00
    00 68 4f 5c 5f 6c 90 94 96 99 9b 9c 9d 9d 9e 9e 9e 9e 9e 9e 9c 9b 99 97 94 91 6e 60 5d 50 64 00
    b0 56 57 5f 65 6a 7f 8e 8f 92 94 96 97 97 98 98 98 98 98 97 96 95 92 8f 8f 80 6b 66 60 59 4d b0
    83 55 5b 63 6a 70 77 87 8c 90 92 94 95 96 96 96 96 95 96 96 94 93 90 8d 87 78 71 6a 64 5f 4b ba
    65 57 5f 68 70 77 7d 83 8d 91 93 95 96 97 97 96 97 97 97 97 95 93 91 8c 83 7e 77 70 68 61 52 88
    58 5a 64 6d 75 7e 86 8d 93 97 9a 9d 9e 9e 9f 9e 9e 9e 9e 9e 9d 9a 97 93 8c 85 7e 76 6d 64 58 6f
    5a 5c 67 71 7c 86 90 98 9f a4 a7 a9 ab ab ab ab ab ac ab ab a9 a6 a2 9d 96 8e 85 7b 71 68 5a 70
    6a 5e 6b 78 83 90 9a a4 ac b2 b6 b8 b9 ba ba bb ba bb ba b9 b8 b5 b0 a9 a1 97 8c 81 76 6b 59 89
    87 61 6e 7c 8a 98 a4 b0 ba c1 c5 c7 c9 ca ca ca ca ca c9 c8 c6 c2 bd b6 ab 9f 93 86 79 6d 56 b9
    b2 66 6f 80 90 a0 ae bb c7 ce d2 d6 d7 d8 d8 d8 d9 d8 d7 d6 d3 d0 c9 c0 b5 a7 99 89 7b 6e 5b b0
    00 77 6c 82 93 a4 b4 c2 d0 d8 dd e0 e2 e3 e4 e5 e5 e4 e2 e1 de d9 d2 c8 ba aa 9b 8a 7c 67 72 00
    00 9b 65 83 94 a6 b7 c7 d5 de e4 e8 ea ec ec ec ec ec ea e8 e4 df d7 cb bc ab 9a 89 7b 60 9a 00
    00 d8 6a 7e 91 a3 b4 c5 d4 e0 e6 eb ee f0 f0 f0 f0 f0 ee eb e7 e0 d6 c8 b8 a7 96 84 75 63 d8 00
    00 00 97 6d 8c 9d af c0 cf dc e6 ec f0 f1 f2 f3 f3 f1 ee eb e5 dd d1 c1 b1 9f 8e 80 65 93 00 00
    00 00 00 72 80 95 a7 b9 c9 d8 e3 eb f0 f1 f3 f3 f2 f1 ed e9 e2 d7 c9 b9 a8 96 87 74 6b 00 00 00
    00 00 00 c8 72 8b 9d b0 c1 cf dc e5 ec ef f2 f2 f1 ef eb e4 da ce bf af 9e 8c 7d 67 c6 00 00 00
    00 00 00 00 b0 71 8f a2 b3 c2 d0 da e2 e8 ea eb eb e7 e2 da cf c2 b2 a1 90 81 65 ac 00 00 00 00
    00 00 00 00 00 b0 78 8e a4 b3 c1 cc d5 db df e0 df db d5 cc c1 b3 a3 94 7f 6d ad 00 00 00 00 00
    00 00 00 00 00 00 c8 82 88 a0 b0 bb c4 cb d0 d1 d0 cd c5 bc b2 a3 93 7b 77 c5 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 a1 85 8e 9f ae b5 b9 bb ba b7 b1 a7 97 85 7d 9c 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 d8 ae 93 8c 92 9b 9d 9c 98 8f 88 8f aa d8 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 dc bb 98 8d 8c 97 bb dc 00 00 00 00 00 00 00 00 00 00 00 00
  }
  set images(40) \
  {
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ce 9e 7a 5c 52 52 5d 7a 9d ce 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 de a6 6e 4d 42 42 44 44 44 44 41 42 4d 6e a5 de 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 d9 89 49 34 4e 79 a0 ba c6 cc cc c6 b9 a0 79 4d 35 49 89 d9 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 9c 49 37 69 b4 e7 fd fe fe fe fe fe fe fe fe fd e7 b4 69 36 48 9c 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 6e 37 53 b0 f1 fd f5 f0 ee ee ef ef ef ef ef ee f0 f5 fd f1 af 53 37 6d 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 cb 57 3a 67 d5 f5 e8 e5 e7 e9 e8 e9 e9 ea e9 ea e9 e9 e8 e7 e5 e8 f6 d5 68 3b 57 cb 00 00 00 00 00 00
    00 00 00 00 00 cd 4e 41 64 d5 e7 da df df df e0 e0 e0 e0 e1 e1 e0 e1 e0 df e0 df df da e7 d5 65 40 4e cd 00 00 00 00 00
    00 00 00 00 00 56 45 54 be dc d0 d5 d6 d6 d6 d7 d7 d7 d7 d8 d8 d7 d7 d7 d6 d7 d5 d5 d5 d0 dc bd 54 45 56 00 00 00 00 00
    00 00 00 00 6d 45 4b 88 d3 c6 ca cb cb cc cd cd cd cd cd cd cd ce cd ce cc ce cc cc cb ca c6 d3 87 4b 45 6c 00 00 00 00
    00 00 00 97 3f 53 53 ad c1 bd c0 c1 c1 c3 c3 c4 c4 c5 c4 c5 c5 c5 c4 c4 c3 c4 c2 c1 c1 c0 bd c1 ad 54 53 3f 97 00 00 00
    00 00 d8 4d 50 50 65 b1 b3 b4 b5 b7 b8 b9 ba ba bb bb bb bc bc bb bb bb ba b9 b9 b9 b7 b6 b5 b3 b2 66 51 50 4d d8 00 00
    00 00 89 43 56 53 6b ab aa ab ad ae af b0 b1 b2 b2 b2 b3 b3 b3 b3 b3 b2 b2 b1 b1 af af ad ac aa ab 6b 53 57 45 89 00 00
    00 d9 52 50 57 57 6a a0 a0 a2 a3 a5 a7 a8 a9 a9 aa aa ab ab aa aa aa aa a9 a9 a8 a7 a6 a4 a2 a0 9f 6a 57 57 50 52 db 00
    00 a0 45 57 59 5c 66 92 99 9a 9b 9e 9f a1 a1 a1 a2 a3 a3 a3 a3 a3 a3 a3 a2 a1 a0 9f 9e 9d 9a 98 93 66 5d 5a 57 45 a3 00
    00 75 4a 59 5c 62 64 82 93 92 96 97 99 9b 9b 9c 9c 9c 9d 9d 9d 9e 9d 9e 9d 9b 9a 99 98 96 92 94 83 65 62 5d 5b 4d 73 00
    ca 5c 52 5c 5f 64 68 74 8b 8e 90 91 93 95 96 97 97 98 98 99 99 98 98 97 97 97 96 93 92 90 8d 8c 74 69 65 60 5c 54 53 00
    9a 55 56 5e 63 68 6e 71 7f 8b 8c 8f 91 93 94 95 96 96 96 96 97 96 96 96 95 94 94 92 90 8d 8b 7f 72 6e 69 63 5e 5a 49 d4
    7c 54 5a 60 66 6c 72 77 7c 85 8d 8f 92 94 94 95 96 96 96 96 96 96 96 95 96 94 93 92 8f 8c 85 7c 78 73 6e 68 62 5d 4c ac
    62 57 5d 64 6b 71 77 7d 82 87 8c 91 93 95 97 98 97 98 99 98 98 98 99 98 98 97 96 93 91 8d 86 82 7e 77 72 6c 64 5e 53 82
    59 58 5f 68 6f 76 7d 83 89 8f 93 97 9a 9c 9e 9f 9f 9f 9f 9f 9e 9f a0 9e 9f 9e 9c 99 97 93 8e 88 83 7c 76 6f 68 61 56 73
    5a 5a 62 6b 73 7b 83 8b 92 98 9d a1 a5 a7 a9 aa aa aa aa ab aa aa ab a9 aa a8 a6 a3 a0 9b 96 90 89 82 7b 73 6b 63 58 73
    65 5c 65 6e 78 81 8a 93 9a a1 a8 ac b0 b2 b4 b5 b6 b5 b6 b6 b6 b6 b6 b5 b4 b3 b2 ae ab a5 9e 97 8f 87 7f 76 6d 65 58 82
    80 5e 67 72 7c 87 91 9a a3 ab b3 b8 bd bf c0 c1 c2 c2 c3 c2 c2 c3 c2 c1 c1 bf bd ba b5 b0 a8 9f 96 8d 83 7a 70 68 54 ac
    9d 61 68 75 80 8b 97 a1 ac b5 be c3 c7 ca cc cd ce ce ce ce ce cf ce cd cc ca c8 c4 bf b8 b0 a6 9c 92 86 7c 71 68 55 d2
    c8 6a 68 77 83 91 9c a8 b4 be c7 cd d1 d5 d7 d8 d9 d9 da da da d9 d9 d8 d7 d5 d2 ce c8 c0 b8 ad a1 95 89 7d 73 66 60 00
    00 81 63 79 85 92 9f ad b9 c4 cf d5 d9 dc de e0 e2 e3 e3 e3 e3 e3 e2 e1 df dd da d5 cf c7 bb af a3 97 8a 7e 74 61 7c 00
    00 a7 60 78 85 93 a2 af bc c8 d2 db e0 e4 e6 e8 e9 ea ea eb ea ea e9 e8 e6 e3 df db d3 cb be b1 a4 97 8a 7c 73 5b a7 00
    00 d9 6b 75 83 92 a1 b0 bd c9 d4 de e3 e7 eb ed ee ee ef ee ee ee ed ec e9 e7 e2 dc d4 ca bd b0 a2 95 87 7a 6d 66 da 00
    00 00 96 69 81 8e 9d ab ba c6 d2 dc e4 e9 ed ef f0 f1 f1 f0 f1 f0 f0 ee ea e7 e3 db d1 c5 b9 ab 9d 8f 81 78 61 93 00 00
    00 00 d8 6c 7a 89 98 a7 b5 c3 cf d9 e3 e9 ec ef f0 f2 f3 f3 f2 f2 f0 ed ea e6 e0 d7 cc c0 b3 a5 97 8a 7d 70 66 d7 00 00
    00 00 00 a1 69 85 91 a0 af bd c9 d5 df e6 ec ef f0 f3 f3 f3 f2 f1 f0 ec e9 e3 db d2 c6 b9 ac 9e 91 83 79 60 9d 00 00 00
    00 00 00 00 87 76 8c 99 a8 b7 c3 cf da e3 e8 ee f0 f1 f3 f3 f2 f1 ef eb e7 e0 d6 cb bf b2 a4 96 89 7e 6a 81 00 00 00 00
    00 00 00 00 00 7a 7c 90 9d ac bb c7 d2 db e2 e8 ec ef f0 f0 f0 ee eb e6 df d7 cd c2 b4 a9 9a 8c 82 6f 71 00 00 00 00 00
    00 00 00 00 00 cf 74 7f 93 9f af bb c7 d0 d8 df e3 e7 e9 e9 e8 e6 e3 de d6 cd c2 b7 aa 9d 8e 84 73 6b cd 00 00 00 00 00
    00 00 00 00 00 00 cd 7e 7f 95 a0 ad ba c5 cd d4 d9 dd de e0 df dc d8 d3 cb c3 b8 ab 9d 90 86 72 74 cb 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 8d 7c 93 a0 ab b6 c0 c7 cd d1 d3 d4 d3 d0 cc c6 bf b6 aa 9d 94 85 70 85 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 aa 80 88 99 a6 ae b6 bc c0 c3 c4 c3 c1 bd b6 af a6 9c 8f 7e 76 a6 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 d7 a0 86 89 95 a1 a8 ac ae af af ac a9 a3 9b 8f 81 7f 9c d6 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 db b7 98 89 8a 91 97 97 97 95 8e 87 86 95 b5 db 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 cd ad 92 8f 8e 91 ac cd 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  }
  set images(48) \
  {
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 dd b1 95 76 5e 58 59 5f 76 95 b1 dd 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e3 b6 84 61 4c 42 3d 3c 3c 3c 3d 3e 41 4c 61 84 b6 e2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 d4 8e 58 3a 35 42 5c 76 87 90 94 94 91 87 75 5c 42 34 39 58 8d d4 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 e3 8c 4e 37 40 6b a3 d0 ed f8 fc fe fe fe fe fc f8 ed d0 a2 6b 41 37 4e 8c e2 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 b7 5b 3c 42 78 c3 f3 fe fc f8 f6 f5 f4 f3 f3 f4 f5 f6 f9 fd fe f4 c2 78 42 3b 5a b7 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 94 43 3f 5a b2 f3 fb f1 ed ee ef ef f0 f0 f0 f0 f0 f1 f0 ef ee ec f1 fb f3 b2 5a 3f 42 93 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 ea 77 40 44 69 d6 f6 e8 e4 e8 e9 ea e9 e9 ea e9 ea e9 e9 ea e9 e9 e9 e9 e8 e5 e8 f6 d6 6a 45 40 76 ea 00 00 00 00 00 00 00
    00 00 00 00 00 00 ea 6e 40 49 68 d8 e8 db e0 e2 e1 e2 e2 e2 e2 e2 e3 e3 e2 e2 e2 e3 e2 e1 e2 e2 e1 e0 dc e9 d8 69 49 3f 6e ea 00 00 00 00 00 00
    00 00 00 00 00 00 77 3e 4e 5c ca df d3 d8 d9 d9 d9 d9 da da da da db db db db da db da d9 d9 d9 d8 d8 d9 d4 df ca 5b 4e 3f 77 00 00 00 00 00 00
    00 00 00 00 00 95 40 52 4a a3 d9 ca d0 cf d0 d1 d1 d1 d2 d2 d2 d3 d3 d3 d3 d3 d2 d3 d2 d1 d3 d1 d0 d0 cf d0 ca d9 a2 4a 51 40 96 00 00 00 00 00
    00 00 00 00 b5 41 50 4b 6a c7 c4 c7 c7 c7 c8 c9 c9 ca ca cb cb cb cb cb ca cb cb cc cb c9 cb ca c9 c9 c8 c7 c7 c5 c6 6a 4b 51 42 b4 00 00 00 00
    00 00 00 e2 56 49 54 4c 8f c3 ba be bf bf c0 c1 c2 c2 c3 c3 c3 c4 c3 c4 c4 c4 c3 c3 c3 c2 c3 c1 c1 c1 bf bf be bb c3 8f 4d 53 49 56 e1 00 00 00
    00 00 00 8b 43 53 51 55 9f b7 b2 b5 b6 b7 b8 b9 b9 ba ba bb ba bc bb bc bd bc bb bb bb bb ba b9 b9 b8 b7 b6 b5 b3 b8 9f 56 52 53 42 8c 00 00 00
    00 00 d5 50 4e 53 55 5c 9f ae ab ae ae b0 b1 b2 b3 b3 b4 b5 b4 b5 b4 b5 b5 b5 b4 b4 b5 b4 b2 b3 b2 b1 b0 af ae ab ae 9e 5b 55 54 4f 51 d5 00 00
    00 00 8d 43 53 56 58 5e 98 a5 a3 a6 a7 a9 a9 ab ab ab ad ac ad ad ae ae ae ae ae ad ad ad ac ab ab aa a9 a8 a6 a4 a5 98 5e 58 56 55 45 8d 00 00
    00 de 58 4c 55 58 5a 5e 8e 9c 9c 9e 9f a0 a2 a4 a5 a5 a5 a6 a7 a7 a7 a6 a7 a6 a7 a6 a6 a6 a5 a4 a3 a3 a1 a0 9f 9c 9d 8e 5f 5b 58 56 4c 59 e0 00
    00 b3 45 55 56 5a 5f 60 80 97 96 99 9a 9b 9d 9e a0 a0 a0 a1 a1 a1 a2 a2 a2 a2 a2 a1 a2 a1 a0 9f 9e 9d 9c 9b 99 96 98 81 60 60 5b 58 55 46 b5 00
    00 84 46 57 59 5e 62 63 71 8f 90 93 95 97 98 99 9a 9b 9b 9b 9b 9c 9d 9d 9d 9d 9d 9d 9d 9c 9b 9a 99 99 97 96 93 90 90 73 65 63 5e 5a 59 47 83 00
    dd 65 4c 59 5c 60 64 69 6b 81 8d 8d 90 92 92 94 96 96 97 97 97 98 98 99 99 98 98 98 97 98 97 96 94 93 92 90 8d 8e 83 6c 69 65 61 5c 5a 4e 5f e2
    ae 58 53 5b 5e 63 67 6d 6f 76 88 8b 8c 8f 91 92 93 95 95 96 97 96 97 97 98 97 97 97 96 95 94 94 94 91 8f 8d 8c 88 77 70 6d 68 63 5f 5b 55 4d e2
    92 53 57 5c 61 66 6a 6f 74 75 7e 89 8b 8e 90 92 94 94 94 96 96 95 96 95 96 95 95 95 95 95 93 93 92 90 8e 8d 89 7e 77 74 70 6b 67 62 5d 59 4a c4
    76 55 5a 5f 64 69 6f 74 78 7c 80 86 8d 90 92 93 93 96 96 97 97 98 97 96 97 96 97 97 96 97 95 94 94 92 90 8c 86 80 7d 79 74 70 6b 65 60 5b 4d 9d
    5f 56 5b 61 68 6d 72 77 7d 80 85 8a 8d 91 94 95 96 98 99 99 99 99 9a 99 99 99 9a 99 99 99 98 97 96 93 91 8d 89 85 81 7c 77 72 6d 67 61 5c 53 7c
    5a 57 5c 64 6a 70 76 7c 81 87 8c 91 95 98 9a 9c 9d 9f a0 9f a0 a0 a0 a0 a0 a0 a1 a0 9f a0 9f 9d 9b 99 96 94 90 8b 86 82 7c 76 70 6a 63 5e 55 75
    5b 59 5f 67 6d 74 7b 81 88 8e 92 99 9b a0 a3 a5 a6 a8 a9 a9 aa a9 a9 aa a9 a9 aa a9 a8 a9 a8 a6 a4 a1 9f 9b 97 91 8d 87 80 7a 74 6d 66 60 56 75
    61 5a 61 68 6f 79 7f 86 8e 94 99 9f a4 a8 ab ae b0 b0 b2 b2 b2 b2 b2 b2 b3 b2 b2 b2 b2 b1 b0 af ad aa a7 a2 9c 97 91 8a 84 7d 76 6f 67 61 57 7d
    79 5b 63 6b 73 7c 84 8c 94 9b a2 a8 ae b2 b6 b8 ba bb bc bd bd bd be bd be bd bd bd bd bc ba b9 b7 b4 b0 ab a5 9e 97 91 89 82 7a 72 6a 63 55 9d
    95 5f 64 6d 77 7f 89 91 9a a2 a9 b0 b7 bc c0 c2 c4 c5 c6 c6 c7 c7 c7 c7 c7 c7 c7 c7 c6 c5 c3 c2 c0 bc b8 b3 ac a4 9d 95 8d 84 7c 74 6a 64 53 c3
    af 64 64 70 79 83 8d 96 9f a9 b1 b8 bf c4 c8 cb ce cf cf d0 d1 d0 d1 d1 d1 d0 d0 d0 d0 cf cd cb c8 c4 c0 ba b3 ab a2 99 91 87 7d 75 6c 63 58 df
    d9 71 61 72 7a 85 91 9b a4 ae b7 c0 c8 cd d1 d4 d6 d8 d9 d9 da da db dc db db da da d9 d8 d6 d4 d1 cc c6 c0 ba b0 a7 9d 93 8a 7f 75 6e 5f 69 df
    00 8d 5d 73 7b 86 92 9d a8 b2 bb c5 cd d3 d7 db dc de e0 e1 e2 e2 e2 e2 e2 e2 e1 e1 e0 df dd da d6 d2 cd c5 bc b3 a9 9f 95 8a 80 75 6e 5a 8a 00
    00 b6 5d 71 7b 87 92 9f aa b4 bf c8 d1 d8 dc e0 e2 e4 e6 e7 e7 e8 e8 e9 e8 e8 e8 e7 e6 e5 e2 de dc d7 d1 ca bf b4 aa a0 94 89 7f 74 6c 5a b6 00
    00 dc 6c 6c 7b 86 92 9f aa b6 c1 ca d4 dc e0 e4 e7 e9 eb ed ed ed ed ed ec ed ed eb eb e9 e6 e2 df d9 d2 ca c0 b5 aa 9f 94 88 7d 75 66 68 de 00
    00 00 97 64 7a 84 90 9c a9 b5 bf c9 d3 db e1 e6 e8 ec ed ee ef f0 f0 ef f0 ef ef ee ec ea e8 e4 df d9 d1 c8 be b2 a7 9b 91 84 7b 72 5e 94 00 00
    00 00 d5 6b 74 80 8d 99 a6 b0 bd c7 d0 d9 e1 e7 ea ed ef f0 f1 f1 f2 f1 f2 f1 f0 f0 ee eb e8 e4 df d7 cd c3 ba ae a3 97 8b 80 77 6c 66 d5 00 00
    00 00 00 98 6a 7d 87 94 a2 ae b9 c5 ce d6 df e6 eb ec ef f1 f1 f3 f4 f3 f3 f2 f1 ef ed ea e6 e2 dc d3 c9 c0 b5 aa 9d 93 87 7b 74 61 94 00 00 00
    00 00 00 dd 71 75 83 8f 9c a9 b3 bf c9 d3 db e3 e8 ec f0 f1 f2 f4 f3 f3 f2 f2 f1 ee eb e9 e5 de d7 cf c4 b9 ae a3 97 8c 81 78 6c 6a dc 00 00 00
    00 00 00 00 b9 6a 7f 89 95 a3 af ba c5 cf d8 df e6 eb ee f0 f0 f3 f3 f4 f3 f2 f0 ee ea e8 e2 da d2 c9 be b4 a9 9d 91 87 7c 74 61 b7 00 00 00 00
    00 00 00 00 00 a7 6f 84 8d 9a a7 b4 bf c8 d2 db e1 e6 eb ef f0 f1 f2 f3 f1 f0 ef ec e7 e3 dc d5 cc c2 b7 ac a1 95 8a 7f 78 64 a2 00 00 00 00 00
    00 00 00 00 00 00 8d 72 87 91 9d aa b6 c0 ca d2 da e0 e4 e8 eb ed ee ee ed ec ea e6 e1 db d4 cd c3 b9 ae a4 98 8d 82 7b 66 87 00 00 00 00 00 00
    00 00 00 00 00 00 e6 87 74 89 93 9e ab b6 c0 ca d1 d7 dc e1 e4 e6 e7 e7 e8 e5 e2 df da d3 cd c4 bb b0 a5 9a 8d 84 7d 68 81 e6 00 00 00 00 00 00
    00 00 00 00 00 00 00 e6 8f 77 89 94 9f aa b5 be c7 ce d3 d8 db de de e0 df dc d9 d6 d1 ca c3 bb b0 a6 9a 8f 85 7d 6a 89 e6 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 a8 76 88 95 9e a9 b2 bb c4 c9 ce d1 d4 d6 d5 d5 d4 d1 cc c7 c1 b9 b0 a5 9c 91 87 7a 6b a3 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 bd 83 82 92 9d a5 ac b4 bb c1 c5 c7 ca c9 c9 c7 c4 c1 ba b4 ab a3 99 91 86 76 7b ba 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 dd 9e 82 85 92 9f a5 ab b0 b4 b8 b9 b9 ba b7 b5 b0 ab a4 9e 96 8a 7c 7a 9b de 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 d3 a1 89 85 8e 98 9f a3 a5 a7 a7 a7 a6 a3 9f 9a 92 87 7f 83 9d d2 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 e2 c0 9f 8b 86 8a 91 94 94 94 93 8e 88 83 88 9c bf e2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ef d8 bf a0 8f 8f 8f 8f 9f bf d8 ef 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  }
  set images(bg) \
  {
    #8edede
    #8bbbbb
    #81b1b1
    #8ccccc
    #8bdbdb
    #8fcfcf
    #87c7c7
    #8bcbcb
    #8ddddd
    #8cbcbc
  }

  # ===========
  # procs
  # ===========

  # -----------
  # create the bubble images
  # -----------
  proc CreateImages {} \
  {
    #puts "CreateImages"
    for {set d $::Size} {$d > 0} {incr d -8} \
    {
      # create images
      array unset datas
      for {set n 0} {$n < 8} {incr n} \
      { image create photo img$d-$n }
      # compute data
      set data $::images($d)
      set ndx -1
      for {set i 0} {$i < $d} {incr i} \
      {
        array unset rows
        set first 0
        set last 0
        for {set j 0} {$j < $d} {incr j} \
        {
          set c [lindex $data [incr ndx]]
          set cc [scan $c %x]
          set c2 [format %2.2x [expr {round($cc * 0.5)}]]
          set c3 [expr {round($cc * 1.25)}]
          if {$c3 > 255} { set c3 255 }
          set c3 [format %2.2x $c3]
          lappend rows(0) #${c2}${c2}${c2}; # gray
          lappend rows(1) #${c}0000       ; # red
          lappend rows(2) #00${c}00       ; # green
          lappend rows(3) #${c}${c}00     ; # yellow
          lappend rows(4) #00${c}${c}     ; # indigo
          lappend rows(5) #${c}00${c}     ; # violet
          lappend rows(6) #${c}${c2}00    ; # orange
          lappend rows(7) #${c}${c}${c}   ; # white
        }
        for {set n 0} {$n < 8} {incr n} \
        { lappend datas($n) $rows($n) }
      }
      # put images data
      for {set n 0} {$n < 8} {incr n} \
      { img$d-$n put $datas($n) }
      # set transparency
      catch \
      {
        set ndx -1
        for {set i 0} {$i < $d} {incr i} \
        {
          for {set j 0} {$j < $d} {incr j} \
          {
            set c [lindex $data [incr ndx]]
            if {$c == 0} \
            {
              for {set n 0} {$n < 8} {incr n} \
              { img$d-$n transparency set $i $j 1 }
            }
          }
        }
      }
    }
    # create background
    image create photo imgbg -width 1000 -height 1000
    imgbg put $::images(bg) -to 0 0 1000 1000
  }
  # -----------
  # init the game dialog
  # -----------
  proc CreateDialog {} \
  {
    #puts "CreateDialog"
    # create digits areas
    catch { destroy .f1}
    frame .f1
    canvas .f1.bg -width 1000 -height 1000 -bd 0 -highlightth 0
    .f1.bg create image 0 0 -anchor nw -image imgbg
    place .f1.bg -in .f1 -relx 0 -rely 0
    CreateDigitsCanvas $::StepDisplay
    CreateDigitsCanvas $::ScoreDisplay
    CreateDigitsCanvas $::GrandScoreDisplay
    CreateDigitsCanvas $::DelayDisplay
    button $::OptionsButton -width 10 -text Options \
      -command Options -state disabled
    grid  $::StepDisplay \
          $::ScoreDisplay \
          $::GrandScoreDisplay \
          $::DelayDisplay \
          $::OptionsButton \
      -padx 10 -pady 5
    grid .f1 -row 0 -sticky ew
    # create game area
    CreateBubblesCanvas
    # center
    CenterDialog .
  }
  proc CreateDigitsCanvas {canvas} \
  {
    # create canvas
    set size $::CanvasSize($canvas)
    canvas $canvas -bg gray75 -relief sunken \
      -width [expr {$size * 20 + 10}] -height 32 \
      -bd 1 -highlightth 0 -selectborderwi 0
    # create digits
    set x 10
    set y 5
    for {set i 0} {$i < $size} {incr i} \
    {
      CreateDigit $canvas $i $x $y
      incr x 20
    }
    # init display
    SetNumber $canvas 0
  }
  proc CreateBubblesCanvas {} \
  {
    # create frame
    catch { destroy .f2}
    frame .f2
    canvas .f2.bg -width 1000 -height 1000 -bd 0 -highlightth 0
    .f2.bg create image 0 0 -anchor nw -image imgbg
    place .f2.bg -in .f2 -relx 0 -rely 0
    # create count down canvas
    canvas $::TimeDisplay -relief sunken \
      -width 21 -height $::Height \
      -bd 1 -highlightth 0 -selectborderwi 0
    $::TimeDisplay create rectangle 1 0 20 $::Height \
      -outline "" -fill #00c0c0 -tags used
    $::TimeDisplay create rectangle 1 0 20 $::Height \
      -outline "" -fill red -tags left
    # create game area
    canvas $::BubblesDisplay -bg gray75 -relief sunken \
      -width [expr {$::Width + 1}] -height $::Height \
      -bd 1 -highlightth 0 -selectborderwi 0
    $::BubblesDisplay config -scrollregion [list 0 0 $::Width $::Height]
    $::BubblesDisplay xview scroll 1 unit
    $::BubblesDisplay yview scroll 1 unit
    # create decoration
    for {set i 0} {$i < 64} {incr i} \
    {
      set x [expr {round(rand() * $::Width)}]
      set y [expr {round(rand() * $::Height)}]
      set d [expr {round(rand() * $::R)}]
      $::BubblesDisplay create oval \
        [expr {$x - $d}] \
        [expr {$y - $d}] \
        [expr {$x + $d}] \
        [expr {$y + $d}] -outline gray65 -width 2
    }
    # Click events
    $::BubblesDisplay bind bubble <Button-1> \
    {
      set tag [lindex [$::BubblesDisplay itemcget current -tags] 1]
      set ndx [string range $tag 6 end]
      ChooseBubble $ndx
    }
    bind $::BubblesDisplay <Button-3> { Dump }
    # display all
    grid $::TimeDisplay $::BubblesDisplay -padx 5 -pady 10
    grid .f2 -row 1 -sticky ew
  }
  # -----------
  # center dialog
  # -----------
  proc CenterDialog {w} \
  {
    # hide dialog
    wm geometry $w -2000-2000
    # compute sizes
    update
    set x [expr {([winfo screenwidth $w] - [winfo width $w]) / 2}]
    set y [expr {([winfo screenheight $w] - [winfo height $w]) / 2}]
    # place dialog
    wm geometry $w +$x+$y
  }
  # -----------
  # options dialog
  # -----------
  proc Options {} \
  {
    # stop count down
    set ::Halted 1
    # inhibite call to options
    $::OptionsButton config -state disabled
    # save parameters value
    set Level $::Level
    set Size $::Size
    set Speed $::Speed
    # create dialog
    if {[winfo exists .o]} { destroy .o }
    toplevel .o
    wm title .o "Bubbles, Options"
    # manage events
    bind .o <<Close>> \
    {
      catch \
      {
        set fn Bubbles2.Parameters
        set fp [open $fn w]
        puts $fp "set ::Level $::Level"
        puts $fp "set ::Size $::Size"
        puts $fp "set ::Speed $::Speed"
        close $fp
      }
      destroy .o
      $::OptionsButton config -state normal
      set ::Halted 0
      if {$::Status == "newgame"} { NewGame }
      if {$::Status == "quitgame"} { exit }
    }
    wm protocol .o WM_DELETE_WINDOW { event generate .o <<Close>> }
    bind .o <Escape> \
    {
      set ::Level $Level
      set ::Size $Size
      set ::Speed $Speed
      event generate .o <<Close>>
    }
    # create buttons area
    frame .o.f
    # create buttons
    foreach {w t c} \
    {
      1 "Continue"      { event generate .o <<Close>> }
      2 "New Game"      { set ::Status newgame; event generate .o <<Close>> }
      3 "Quit Game"     { set ::Status quitgame; event generate .o <<Close>> }
      4 "Hall of Fame"  { DisplayFame }
    } \
    { button .o.f.b$w -width 20 -bd 1 -text $t -command $c }
    # create level area
    frame .o.f0 -relief groove -bd 2 -padx 25 -pady 5
    label .o.f0.lt -fg gray20 -font {-weight bold} -text "bubbles level"
    frame .o.f0.f
    # create buttons
    foreach w {0 1 2} v {novice expert master} t {novice expert master} \
    { radiobutton .o.f0.f.r$w -variable ::Level -value $v -text $t \
        -command { .o.f.b1 config -state disabled }
    }
    # create size area
    frame .o.f1 -relief groove -bd 2 -padx 25 -pady 5
    label .o.f1.lt -fg gray20 -font {-weight bold} -text "bubbles size"
    frame .o.f1.f
    # create buttons
    foreach w {0 1} v {48 32} t {big small} \
    { radiobutton .o.f1.f.r$w -variable ::Size -value $v -text $t \
        -command { .o.f.b1 config -state disabled }
    }
    # create speed area
    frame .o.f2 -relief groove -bd 2 -padx 25 -pady 5
    label .o.f2.lt -fg gray20 -font {-weight bold} -text "bubbles speed"
    frame .o.f2.f
    # create buttons
    foreach w {0 1 2} v {1 3 5} t {slow medium speed} \
    { radiobutton .o.f2.f.r$w -variable ::Speed -value $v -text $t }
    # display all
    grid .o.f -sticky ew
    foreach w {1 2 3 4} \
    { grid .o.f.b$w -padx 40 -pady 5 }
    foreach w {0 1 2} \
    { grid .o.f$w -padx 5 -pady 5 -sticky nsew }
    foreach w {0 1 2} \
    {
      pack .o.f$w.lt -anchor center
      pack .o.f$w.f
    }
    foreach {w1 s} {0 {r0 r1 r2} 1 {r0 r1} 2 {r0 r1 r2}} \
    { foreach w2 $s { pack .o.f$w1.f.$w2 -anchor w } }
    CenterDialog .o
    raise .o
    focus -force .o
    # disable Continue if New Game is mandatory
    if {$::Status == "newgame"} { .o.f.b1 config -state disabled }
  }
  # -----------
  # display hall of fame
  # -----------
  proc DisplayFame {{scores {}} {entry 0}} \
  {
    #puts "DisplayFame $scores"
    # get scores if needed
    if {$scores == {}} { set scores [ReadFame] }
    # create dialog
    if {[winfo exists .h]} { destroy .h }
    toplevel .h
    wm title .h "Bubbles, Hall of Fame"
    focus -force .h
    # manage events
    bind .h <<Close>> { destroy .h }
    bind .h <Escape> { event generate .h <<Close>> }
    # create frame title
      frame .h.ft
      # create user area
      label .h.ft.u -bd 1 -relief sunken -width 10 -font {-weight bold} \
          -bg gray90 -fg DarkOrange -text $::User
      # create level area
      label .h.ft.ll -bd 1 -relief sunken -width 6 -font {-weight bold} \
          -bg gray90 -fg DarkGreen -text $::Level
      # create score area
      label .h.ft.ls -bd 1 -relief sunken -width 8 -font {-weight bold} \
          -bg gray90 -fg red -text [format %7.7i $::GrandScore]
      grid .h.ft.u .h.ft.ll .h.ft.ls -padx 20 -pady 25 -sticky nw
    # create scores frame
    frame .h.fs
    set ::numScore -1
    set i 1
    foreach score $scores \
    {
      if {$score == ""} { break }
      # get info
      foreach {number level user} [split $score] break
      # create num area
      label .h.fs.ln$i -bd 1 -relief sunken -width 2 -font {-weight bold} \
          -bg gray90 -fg gray20 -text $i
      # create user area
      label .h.fs.u$i -bd 1 -relief sunken -width 10 -font {-weight bold} \
          -bg gray90 -fg DarkOrange -text $user
      if {$user == "" && $entry == -1} \
      {
        # create user entry
        set entry $i
        entry .h.fs.u$i.e -bd 0 -relief flat -width 10 -font {-weight bold} \
          -bg gray90 -fg DarkOrchid -textvariable ::User \
          -insertbackground DarkOrchid -insertwidth 4 -selectforeground DarkOrchid
        place .h.fs.u$i.e -in .h.fs.u$i -anchor center -relx 0.5 -rely 0.5
        focus -force .h.fs.u$i.e
        .h.fs.u$i.e selection range 0 end
        .h.fs.u$i.e icursor end
      }
      # create level area
      label .h.fs.ll$i -bd 1 -relief sunken -width 6 -font {-weight bold} \
          -bg gray90 -fg DarkGreen -text $level
      # create score area
      label .h.fs.ls$i -bd 1 -relief sunken -width 8 -font {-weight bold} \
          -bg gray90 -fg red -text [format %7.7i $number]
      # display row
      grid .h.fs.ln$i .h.fs.u$i .h.fs.ll$i .h.fs.ls$i -padx 10 -pady 5
      incr i
    }
    # place/display frames
    pack .h.ft -padx 20 -pady 20 -fill x
    pack .h.fs -padx 20 -pady 20 -fill x
    # create register button if needed
    if {$entry > 0} \
    {
      # add a Register button
      set ::numScore [incr entry -1]
      bind .h <<Register>> \
      {
        set score "$::GrandScore $::Level $::User"
        set ::Scores [lreplace $::Scores $::numScore $::numScore $score]
        WriteFame $::Scores
        DisplayFame $::Scores
      }
      bind .h <Return> { event generate .h <<Register>> }
      button .h.b -width 20 -text Register \
        -command { event generate .h <<Register>> }
      pack .h.b -pady 10
    }
    # place dialog
    CenterDialog .h
    raise .h
  }
  # -----------
  # update hall of fame
  # -----------
  proc UpdateFame {} \
  {
    #puts "UpdateFame"
    # add new score
    set ::Scores [ReadFame]
    lappend ::Scores [list $::GrandScore $::Level]
    set ::Scores [lrange [lsort -dic -dec $::Scores] 0 9]
    # display new Hall of Fame
    DisplayFame $::Scores -1
  }
  # -----------
  # read the Hall of fame
  # -----------
  proc ReadFame {} \
  {
    #puts "ReadFame"
    # read file
    set scores ""
    catch \
    {
      set fn Bubbles2.HallOfFame
      set fp [open $fn]
      set scores [split [read $fp] \n]
      close $fp
    }
    # fill score if empty
    if {$scores == ""} \
    { for {set i 0} {$i < 10} {incr i} { lappend scores "0 expert anonymous" } }
    return $scores
  }
  # -----------
  # write the Hall of fame
  # -----------
  proc WriteFame {scores} \
  {
    #puts "WriteFame $scores"
    # write file
    catch \
    {
      set fn Bubbles2.HallOfFame
      set fp [open $fn w]
      puts -nonewline $fp [join $scores \n]
      close $fp
    }
  }
  # -----------
  # create one digit
  # -----------
  proc CreateDigit {canvas ndx x y} \
  {
    #puts "CreateDigit $canvas $ndx $x $y"
    # create digit segments
    set y0 0
    set size 12
    set xs [expr {$x + $size}]
    set ys [expr {$y + $size}]
    foreach \
    { x1    y1    x2    y2    tag dy}   \
    [list \
      $x    $y    $xs   $y    0   0     \
      $x    $y    $x    $ys   1   0     \
      $xs   $y    $xs   $ys   2   $size \
      $x    $y    $xs   $y    3   0     \
      $x    $y    $x    $ys   4   0     \
      $xs   $y    $xs   $ys   5   $size \
      $x    $y    $xs   $y    6   0     \
    ] \
    {
      $canvas create line $x1 [expr {$y0 + $y1}] $x2 [expr {$y0 + $y2}] \
        -tags dgt$ndx-$tag -fill gray65 -width 3
      incr y0 $dy
    }
  }
  array set ::Segments \
  {
    ""  {}
    0   {0 1 2 4 5 6}
    1   {2 5}
    2   {0 2 3 4 6}
    3   {0 2 3 5 6}
    4   {1 2 3 5}
    5   {0 1 3 5 6}
    6   {0 1 3 4 5 6}
    7   {0 2 5}
    8   {0 1 2 3 4 5 6}
    9   {0 1 2 3 5 6}
  }
  # -----------
  # display a digit
  # -----------
  proc SetDigit {canvas ndx digit {color red}} \
  {
    #puts "SetDigit $canvas $ndx $digit $color"
    # colorize each segment
    set segments $::Segments($digit)
    for {set i 0} {$i < 7} {incr i} \
    {
      if {[lsearch -exact $segments $i] == -1} \
      { set c gray65 } else { set c $color }
      $canvas itemconfig dgt$ndx-$i -fill $c
    }
  }
  # -----------
  # blink a score
  # -----------
  proc BlinkNumber {canvas value {color ""}} \
  {
    #puts "BlinkNumber $canvas $value $color"
    # alternate colors
    if {$color == ""} \
    {
      StopBlink
      set color red
    } \
    elseif {![info exists ::Blink]} { return }
    set color [expr {$color == "red" ? "gray65" : "red"}]
    # display the number
    SetNumber $canvas $value $color
    # repeat
    set ::Blink [after 500 [list BlinkNumber $canvas $value $color]]
  }
  # -----------
  # stop blinking a score
  # -----------
  proc StopBlink {} \
  {
    #puts "StopBlink"
    # stop
    if {[info exists ::Blink]} \
    {
      catch {after cancel $::Blink}
      unset ::Blink
    }
  }
  # -----------
  # display a score
  # -----------
  proc SetNumber {canvas value {color red}} \
  {
    #puts "SetNumber $canvas $value $color"
    # compute display size
    set size $::CanvasSize($canvas)
    # format value
    set value [format %*.*i $size $size $value]
    # display digits
    set i -1
    foreach digit [split $value {}] \
    { SetDigit $canvas [incr i] $digit $color }
  }
  # -----------
  # count down the time
  # -----------
  proc CountDown {{time ""}} \
  {
    #puts "CountDown $time"
    if {$time == ""} \
    {
      # init
      CancelCount 1
      set time $::StepTime
      for {set t $::Height} {$t > 0} {incr t -1} \
      {
        $::TimeDisplay coords left 1 1 20 $t; update
        after 1
      }
    }
    set t [expr {round($::Height * ($::StepTime - $time) / double($::StepTime))}]
    set ::CurrentTime [incr time -1]
    $::TimeDisplay coords left 1 1 20 $t; update
    if {$time > 0} \
    {
      # count down
      if {$::Halted} { incr time }
      set ::CountDown(1) [after 1000 [list CountDown $time]]
    } \
    else \
    {
      # timed out
      set status $::Status
      set ::Status timeout
      if {$status != "exchange"} { EndOfStep }
    }
  }
  # -----------
  # count down the time
  # -----------
  proc CountDown2 {{time ""}} \
  {
    #puts "CountDown2 $time"
    if {$time == ""} \
    {
      # init
      CancelCount 2
      set time $::Delay($::Step)
    }
    SetNumber $::DelayDisplay $time
    if {$time > 0} \
    {
      # count down
      if {$::Halted} { incr time }
      set ::CountDown(2) [after 1000 [list CountDown2 [incr time -1]]]
    } \
    else \
    {
      # timed out
      set status $::Status
      set ::Status timeout
      BlinkNumber $::DelayDisplay 0
      if {$status != "exchange"} { EndOfStep }
    }
  }
  # -----------
  # cancel a count down
  # -----------
  proc CancelCount {{n 1}} \
  {
    if {[info exists ::CountDown($n)]} \
    {
      after cancel $::CountDown($n)
      unset ::CountDown($n)
    }
  }
  # -----------
  # randomly pick a color
  # -----------
  proc PickColor {} \
  {
    #puts "PickColor"
    # pick a random color from permitted
    set n [expr {int(rand() * 2 * $::nbColors) + 1}]
    if {$n <= $::GrayFrequency} { set color 0 } \
    else \
    {
      set n [expr {int(rand() * 0.999 * $::nbColors) + 1}]
      set color [lindex $::Colors $n]
    }
    return $color
  }
  # -----------
  # find all lined sets
  # -----------
  proc FindLined {args} \
  {
    #puts "FindLined"
    array unset ::Lined
    if {$args != ""} \
    {
      foreach ndx $args \
      { set ::Lined($ndx) $::Bubbles($ndx) }
    } \
    else \
    {
      set maxcol $::N; incr maxcol
      set y $::R
      for {set row 0} {$row < $::N} {incr row} \
      {
        set x $::R
        for {set col 0} {$col < $maxcol} {incr col} \
        {
          set ym [expr {$col % 2 == 1 ? $y + $::R : $y}]
          if {$ym < $::Height} \
          {
            LinedBubbles $x [expr {$::Height - $ym}]
          }
          incr x $::Dx
        }
        incr y $::Size
      }
    }
    if {[llength [array names ::Lined]] > 0} \
    {
      # destroy lined bubbles
      set ::cntLined [llength [array names ::Lined]]
      set ::Ready 0
      foreach ndx [array names ::Lined] { DestroyBubble $ndx }
      vwait ::Ready
      # move falling bubbles
      MoveBubbles
      # recurse
      FindLined
    }
  }
  # -----------
  # lined set from a bubble
  # -----------
  proc LinedBubbles {x y} \
  {
    #puts "LinedBubbles $x $y"
    # get ndx
    if {![info exists ::Slots($x:$y)]} { return {} }
    set ndx0 $::Slots($x:$y)
    # get color & slot
    set color [lindex $::Bubbles($ndx0) 0]
    if {$color == 0} { return }
    # check 3 directions
    foreach {dx dy} [list -$::Dx $::R 0 $::Size $::Dx $::R] \
    {
      # check the upper bubble
      set x1 [expr {$x + $dx}]
      set y1 [expr {$y - $dy}]
      if {![info exists ::Slots($x1:$y1)]} { continue }
      set ndx1 $::Slots($x1:$y1)
      if {[lindex $::Bubbles($ndx1) 0] == $color} \
      {
        # 2 lined bubbles
        # check the uppest
        set x2 [expr {$x1 + $dx}]
        set y2 [expr {$y1 - $dy}]
        if {![info exists ::Slots($x2:$y2)]} { continue }
        set ndx2 $::Slots($x2:$y2)
        if {[lindex $::Bubbles($ndx2) 0] == $color} \
        {
          # 3 lined bubbles
          foreach ndx [list $ndx0 $ndx1 $ndx2] \
          { set ::Lined($ndx) $::Bubbles($ndx) }
        }
      }
    }
  }
  # -----------
  # move bubbles
  # -----------
  proc MoveBubbles {} \
  {
    #puts "MoveBubbles"
    set N $::N
    set R $::R
    set Size $::Size
    set Dx $::Dx
    array set slots [array get ::Slots]
    array set bubbles [array get ::Bubbles]
    set x $R
    set y0 $R
    set y1 [expr {($N - 2) * $Size + $R}]
    set maxy [expr {$::Height - $R}]
    set maxcol $N; incr maxcol
    for {set col 0} {$col < $maxcol} {incr col} \
    {
      set X($col) $x; incr x $Dx
      set Y0($col) $y0; set Y1($col) $y1
      if {($col % 2) == 1} { incr Y0($col) $R; incr Y1($col) $R }
    }
    while 1 \
    {
      set count 0
      array unset moved
      # create new bubbles
      for {set col 0} {$col < $maxcol} {incr col} \
      {
        set yy($col) $Y1($col)
        set x $X($col)
        set y $Y0($col)
        if {![info exists slots($x:$y)]} \
        {
          CreateBubble $x $y
          set ndx $::Slots($x:$y)
          set slots($x:$y) $ndx
          set bubbles($ndx) $::Bubbles($ndx)
          incr count
        }
      }
      # find all moving bubbles
      for {set row [expr {$N - 2}]} {$row >= 0} {incr row -1} \
      {
        for {set col 0} {$col < $maxcol} {incr col} \
        {
          set x $X($col)
          set y0 $yy($col)
          if {[info exists slots($x:$y0)]} \
          {
            set y1 [expr {$y0 + $Size}]
            if {$y1 <= $maxy && ![info exists slots($x:$y1)]} \
            {
              set ndx $slots($x:$y0)
              array unset slots $x:$y0
              lset bubbles($ndx) 2 $y1
              set moved($x:$y1) $ndx
              incr count
              $::BubblesDisplay addtag moving withtag bubble$ndx
            }
          }
          incr yy($col) -$Size
        }
      }
      # fall the bubbles
      set ::Ready 0
      MoveRow 2
      vwait ::Ready
      $::BubblesDisplay dtag moving
      foreach slot [array names moved] \
      { set slots($slot) $moved($slot) }
      # continue if at least one bubble can fall
      if {$count == 0} break
    }
    array unset ::Slots
    array set ::Slots [array get slots]
    array unset ::Bubbles
    array set ::Bubbles [array get bubbles]
  }
  # -----------
  # move a set of bubbles
  # -----------
  proc MoveRow {ddy {dy 0}} \
  {
    #puts "MoveRow $ddy $dy"
    if {$dy == $::Size} { set ::Ready 1 } \
    else \
    {
      $::BubblesDisplay move moving 0 $ddy
      after 5 update
      after 10 MoveRow $ddy [incr dy $ddy]
    }
  }
  # -----------
  # create a bubble
  # -----------
  proc CreateBubble {x y {ndx ""} {d ""} {color ""}} \
  {
    #puts "CreateBubble $x $y $ndx $d $color"
    if {$ndx == ""} \
    {
      # beginning
      # create an index & pick a color
      set ndx [incr ::nbBubbles]
      set color [PickColor]
      # create the bubble
      set d 8
      $::BubblesDisplay create image $x $y \
        -tags [list bubble bubble$ndx] \
        -image img$d-$color
        update
      # register it
      set ::Bubbles($ndx) [list $color $x $y 0 0 0]
      set ::Slots($x:$y) $ndx
      # animate
      after 100 [list CreateBubble 0 0 $ndx $d $color]
      # return the index
      return $ndx
    } \
    elseif {[incr d 8] <= $::Size} \
    {
      # growing
      $::BubblesDisplay itemconfig bubble$ndx -image img$d-$color
      after 100 [list CreateBubble 0 0 $ndx $d $color]
    }
  }
  # -----------
  # destroy a bubble
  # -----------
  proc DestroyBubble {ndx {d ""} {color ""}} \
  {
    #puts "DestroyBubble $ndx $d $color"
    if {$d == ""} \
    {
      # beginning
      set d $::Size
      foreach {color x y} $::Bubbles($ndx) break
      #puts "DestroyBubble $ndx ($x:$y)"
      array unset ::Slots $x:$y
      array unset ::Bubbles $ndx
    }
    if {[incr d -8] == 0} \
    {
      # destroy
      $::BubblesDisplay delete bubble$ndx
      if {[incr ::cntLined -1] == 0} { set ::Ready 1 }
    } \
    else \
    {
      # resize
      $::BubblesDisplay itemconfig bubble$ndx -image img$d-$color
      after 100 [list DestroyBubble $ndx $d $color]
    }
  }
  # -----------
  # froze the bubbles
  # -----------
  proc FrozeBubbles {{delay ""}} \
  {
    if {$delay == ""} \
    {
      set delay 25
      after 1000 {catch { after cancel $::Froze }}
    }
    set bg [$::BubblesDisplay cget -bg]
    set bg [expr {$bg == "gray75" ? "gray65" : "gray75"}]
    $::BubblesDisplay config -bg $bg
    foreach ndx [array names ::Bubbles] \
    {
      foreach {- x y} $::Bubbles($ndx) break
      set dx [expr {(rand() > 0.5 ? 1 : -1) * int(rand() * 2)}]
      set dy [expr {(rand() > 0.5 ? 1 : -1) * int(rand() * 2)}]
      $::BubblesDisplay coords bubble$ndx [expr {$x + $dx}] [expr {$y + $dy}]
    }
    set ::Froze [after $delay FrozeBubbles $delay]
  }
  # -----------
  # choose the bubble
  # -----------
  proc ChooseBubble {ndx {size ""}} \
  {
    if {$::Status != "started"} { return }
    set color [lindex $::Bubbles($ndx) 0]
    if {$size == ""} \
    {
      #puts "ChooseBubble $ndx"
      # check color
      if {$color == 0} { return }
      # init
      if {[info exists ::Choose]} \
      {
        if {$ndx == $::Chosen} { CancelChosen } \
        else { PlayBubbles $ndx $::Chosen }
        return
      }
      CancelChosen
      set size $::Size
      set ::Chosen $ndx
    }
    set size [expr {$size == $::Size ? $::Size - 8 : $::Size}]
    $::BubblesDisplay itemconfig bubble$ndx -image img$size-$color
    set ::Choose [after 100 [list ChooseBubble $ndx $size]]
  }
  # -----------
  # cancel the chosen gimmic
  # -----------
  proc CancelChosen {} \
  {
    if {[info exists ::Choose]} \
    {
      after cancel $::Choose
      unset ::Choose
      set ndx $::Chosen
      set color [lindex $::Bubbles($ndx) 0]
      $::BubblesDisplay itemconfig bubble$ndx -image img$::Size-$color
    }
  }
  # -----------
  # play two bubbles
  # -----------
  proc PlayBubbles {ndx1 ndx2} \
  {
    #puts "PlayBubbles $ndx1 $ndx2"
    # check
    if {$ndx1 == $ndx2} { return }
    # cancel chosen animation
    CancelChosen
    # cancel time out
    CancelCount 2
    # check global status & distance
    foreach {- x1 y1} $::Bubbles($ndx1) break
    foreach {- x2 y2} $::Bubbles($ndx2) break
    set dx [expr {$x2 - $x1}]
    set dy [expr {$y2 - $y1}]
    if {$::Status != "started" || $dx * $dx + $dy * $dy > 9 * $::R * $::R} \
    {
      ChooseBubble $ndx1
      return
    }
    # count the bubbles
    set n1 $::nbBubbles
    # exchange the bubbles
    set ::Status exchange
    ExchangeBubbles $ndx1 $ndx2
    # find lined set
    FindLined
    # check if failed
    if {$::Slots($x1:$y1) == $ndx2 && $::Slots($x2:$y2) == $ndx1} \
    {
      # failed, exchange again
      ExchangeBubbles $ndx1 $ndx2
    }
    # set the score
    set dn [expr {$::nbBubbles - $n1}]
    set ::Score [expr {$::Score + $dn * $dn * $dn * $::Bonus}]
    SetNumber $::ScoreDisplay $::Score
    # ready to continue
    if {$::Status == "timeout"} { EndOfStep } \
    else \
    {
      set ::Status started
      CountDown2
    }
  }
  # -----------
  # exchange two bubbles
  # -----------

proc ExchangeBubbles {ndx1 ndx2} \

  {
    #puts "ExchangeBubbles $ndx1 $ndx2"
    # move the bubbles
    foreach {- x1 y1} $::Bubbles($ndx1) break
    foreach {- x2 y2} $::Bubbles($ndx2) break
    set dx [expr {$x2 - $x1}]
    set ddx [expr {$dx / double($::R)}]
    set dy [expr {$y2 - $y1}]
    set ddy [expr {$dy / double($::R)}]
    $::BubblesDisplay addtag Tag1 withtag bubble$ndx1
    $::BubblesDisplay addtag Tag2 withtag bubble$ndx2
    MoveBubble Tag1 $ddx $ddy $dx $dy
    MoveBubble Tag2 [expr {-$ddx}] [expr {-$ddy}] [expr {-$dx}] [expr {-$dy}]
    lset ::Bubbles($ndx1) 1 $x2
    lset ::Bubbles($ndx1) 2 $y2
    lset ::Bubbles($ndx2) 1 $x1
    lset ::Bubbles($ndx2) 2 $y1
    set ::Slots($x2:$y2) $ndx1
    set ::Slots($x1:$y1) $ndx2
    vwait ::Tag2
    $::BubblesDisplay dtag Tag1
    $::BubblesDisplay dtag Tag2
  }
  # -----------
  # move a bubble
  # -----------
  proc MoveBubble {tag ddx ddy dx dy} \
  {
    #puts "MoveBubble $tag $ddx $ddy $dx $dy"
    if {abs($dy) < 1} { set ::$tag 1 } \
    else \
    {
      $::BubblesDisplay move $tag $ddx $ddy
      after 5 update
      after 10 MoveBubble $tag $ddx $ddy [expr {$dx - $ddx}] [expr {$dy - $ddy}]
    }
  }
  # -----------
  # end of step
  # -----------
  proc EndOfStep {} \
  {
    #puts "EndOfStep"
    # end of step
    CancelCount 1
    CancelCount 2
    FrozeBubbles
    after 1000 {catch { after cancel $::Froze }}
    # next step
    set ::Status endofstep
    set bonus 0
    switch $::Level \
    {
      master  { set bonus 500 }
      expert  { set bonus 250 }
    }
    incr ::GrandScore $::Score
    if {$::Step < 4} \
    {
      # next step
      SetNumber $::GrandScoreDisplay $::GrandScore
      after 1000 NextStep
    } \
    else \
    {
      # end of game
      set ::Status newgame
      BlinkNumber $::GrandScoreDisplay $::GrandScore
      Options
      UpdateFame
    }
  }
  # -----------
  # prepare for the next step
  # -----------
  proc NextStep {} \
  {
    #puts "NextStep"
    set ::Status nextstep
    incr ::Step
    # initialize
    set list [list 0 1 2 3 4 5 6 7]
    set ::InitialColors [lrange $list 0 $::nbInitialColors]
    set ::Colors $::InitialColors
    set ::nbColors $::nbInitialColors
    set ::GrayFrequency 0
    set ::nbBubbles 0
    set ::Score 0
    set ::StepValue 0
    set ::Halted 0
    set ::Ready 0
    array unset ::Bubbles
    array unset ::Slots

    # update dialog
    StopBlink
    SetNumber $::ScoreDisplay $::Score
    SetNumber $::StepDisplay $::Step
    # create the slots
    CreateBubblesCanvas
    # create the bubbles
    MoveBubbles
    # find lined sets
    FindLined
    # start the timer
    CountDown
    incr ::GrayFrequency
    set ::Status started
    $::OptionsButton config -state normal
    CountDown2
  }
  # -----------
  # reset the game
  # -----------
  proc NewGame {} \
  {
    #puts "NewGame"
    # in case of
    CancelCount 1
    CancelCount 2
    # globals
    set ::Status newgame
    set ::MasterWidth 12
    set ::ExpertWidth 9
    set ::NoviceWidth 6
    set ::MasterTime 300
    set ::ExpertTime 330
    set ::NoviceTime 36
    switch $::Level \
    {
      master  \
      { set ::N $::MasterWidth; set ::StepTime $::MasterTime; set ::Bonus 3 }
      expert  \
      { set ::N $::ExpertWidth; set ::StepTime $::ExpertTime; set ::Bonus 2 }
      default \
      { set ::N $::NoviceWidth; set ::StepTime $::NoviceTime; set ::Bonus 1 }
    }
    set ::R [expr {$::Size / 2}]
    set ::Coef 1.74
    set ::Dx [expr {round($::R * $::Coef)}]
    set ::Width [expr {$::Size + $::R * $::Coef * $::N}]
    set ::Height [expr {$::Size * $::N}]
    set ::nbInitialColors 7
    set ::Score 0
    set ::GrandScore 0
    # create dialog
    set ::StepDisplay .f1.s
    set ::ScoreDisplay .f1.d
    set ::GrandScoreDisplay .f1.e
    set ::DelayDisplay .f1.f
    set ::OptionsButton .f1.b
    set ::TimeDisplay .f2.t
    set ::BubblesDisplay .f2.c
    array set ::CanvasSize \
    [list \
      $::StepDisplay        1 \
      $::ScoreDisplay       5 \
      $::GrandScoreDisplay  7 \
      $::DelayDisplay       3 \
    ]
    array set ::Delay \
    {
      1   30
      2   25
      3   20
      4   15
    }
    CreateDialog
    # first step
    set ::Step 0
    NextStep
  }
  # -----------
  # dump internal info
  # -----------
  proc Dump {} \
  {
    puts "internal\n--------"
    puts "level: $::Level"
    puts "bubbles\n-------"
    foreach ndx [lsort -dic [array names ::Bubbles]] \
    { puts "$ndx: $::Bubbles($ndx)" }
    puts "slots\n-----"
    for {set y $::R} {$y < $::Height} {incr y $::R} \
    {
      set xys [lsort -dic [array names ::Slots *:$y]]
      if {[llength $xys] == 0} { continue }
      set line ""
      foreach xy $xys \
      {
        set ndx $::Slots($xy)
        if {$ndx != ""} { append line "${ndx}($xy) " }
      }
      if {$line != ""} { puts $line }
    }
  }
  # -----------
  # display error info
  # -----------
  proc bgerror {args} \
  {
    puts "\nbgerror:\n$args\n"
    set level [info level]
    for {set l $level} {$l < 1} {incr l -1} \
    { puts "$l:\t[info level $l]" }
    Dump
  }

  # ===========
  # start game
  # ===========

  # exit event
  wm protocol . WM_DELETE_WINDOW exit
  # title
  wm title . "Bubbles2 v $version, by ulis"
  # parameters
  set ::Size 48
  set ::Speed 3
  set ::Level expert
  catch { source Bubbles2.Parameters }
  # global
  set ::User anonymous
  # create images
  CreateImages
  # initialize
  NewGame