1.tcl 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. set fp [open "input"]
  2. set lines {}
  3. set line {}
  4. while {[gets $fp line] >= 0} {
  5. lappend lines $line
  6. }
  7. close $fp
  8. set height [llength $lines]
  9. set width [string length [lindex $lines 0]]
  10. proc char_at {x y} {
  11. global lines
  12. string index [lindex $lines $y] $x
  13. }
  14. proc out_of_range {x y} {
  15. global height width
  16. if {$x < 0 || $x >= $width} {
  17. return 1
  18. }
  19. if {$y < 0 || $y >= $height} {
  20. return 1
  21. }
  22. return 0
  23. }
  24. proc get_area_impl {pctx c x y} {
  25. upvar $pctx ctx
  26. if {[out_of_range $x $y]} {
  27. return
  28. }
  29. if {[dict exists [dict get $ctx cache] "$x,$y"]} {
  30. return
  31. }
  32. if {[char_at $x $y] != $c} {
  33. return
  34. }
  35. dict update ctx sum sum {incr sum}
  36. dict update ctx cache cache {
  37. dict set cache "$x,$y" {}
  38. }
  39. get_area_impl ctx $c [expr {$x + 1}] $y
  40. get_area_impl ctx $c [expr {$x - 1}] $y
  41. get_area_impl ctx $c $x [expr {$y + 1}]
  42. get_area_impl ctx $c $x [expr {$y - 1}]
  43. }
  44. proc get_area {x y} {
  45. set ctx {sum 0 cache {}}
  46. get_area_impl ctx [char_at $x $y] $x $y
  47. return [dict get $ctx sum]
  48. }
  49. proc get_peri_impl {pctx c x y} {
  50. proc is_outside {c x y} {
  51. if {[out_of_range $x $y]} {
  52. return 1
  53. }
  54. if {[char_at $x $y] != $c} {
  55. return 1
  56. }
  57. return 0
  58. }
  59. upvar $pctx ctx
  60. if {[out_of_range $x $y]} {
  61. return
  62. }
  63. if {[dict exists [dict get $ctx cache] "$x,$y"]} {
  64. return
  65. }
  66. if {[char_at $x $y] != $c} {
  67. return
  68. }
  69. set c [char_at $x $y]
  70. dict update ctx sum sum {
  71. set sum [expr {$sum + [is_outside $c [expr {$x - 1}] $y] \
  72. + [is_outside $c [expr {$x + 1}] $y] \
  73. + [is_outside $c $x [expr {$y + 1}]] \
  74. + [is_outside $c $x [expr {$y - 1}]]}]
  75. }
  76. dict update ctx cache cache {
  77. dict set cache "$x,$y" {}
  78. }
  79. get_peri_impl ctx $c [expr {$x + 1}] $y
  80. get_peri_impl ctx $c [expr {$x - 1}] $y
  81. get_peri_impl ctx $c $x [expr {$y + 1}]
  82. get_peri_impl ctx $c $x [expr {$y - 1}]
  83. }
  84. proc get_peri {x y} {
  85. set ctx {sum 0 cache {}}
  86. get_peri_impl ctx [char_at $x $y] $x $y
  87. return [dict get $ctx sum]
  88. }
  89. set visited {}
  90. proc mark_visited {c x y} {
  91. global visited
  92. if {[out_of_range $x $y]} return
  93. if {[char_at $x $y] != $c} return
  94. if {[dict exists $visited "$x,$y"]} return
  95. dict set visited "$x,$y" {}
  96. incr x
  97. mark_visited $c $x $y
  98. incr x -2
  99. mark_visited $c $x $y
  100. incr x
  101. incr y
  102. mark_visited $c $x $y
  103. incr y -2
  104. mark_visited $c $x $y
  105. }
  106. set sum 0
  107. for {set x 0} {$x < $width} {incr x} {
  108. for {set y 0} {$y < $height} {incr y} {
  109. if {[dict exists $visited "$x,$y"]} {
  110. continue
  111. }
  112. set peri [get_peri $x $y]
  113. set area [get_area $x $y]
  114. mark_visited [char_at $x $y] $x $y
  115. incr sum [expr {$peri * $area}]
  116. }
  117. }
  118. puts $sum