aboutsummaryrefslogtreecommitdiff
path: root/12/1.tcl
blob: f0df264e5f8e15e60aa30828d242a1cc085dbe7e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
set fp [open "input"]
set lines {}
set line {}
while {[gets $fp line] >= 0} {
    lappend lines $line
}
close $fp

set height [llength $lines]
set width [string length [lindex $lines 0]]

proc char_at {x y} {
    global lines
    string index [lindex $lines $y] $x
}

proc out_of_range {x y} {
    global height width
    if {$x < 0 || $x >= $width} {
        return 1
    }
    if {$y < 0 || $y >= $height} {
        return 1
    }
    return 0
}

proc get_area_impl {pctx c x y} {
    upvar $pctx ctx
    if {[out_of_range $x $y]} {
        return
    }
    if {[dict exists [dict get $ctx cache] "$x,$y"]} {
        return
    }
    if {[char_at $x $y] != $c} {
        return
    }
    dict update ctx sum sum {incr sum}
    dict update ctx cache cache {
        dict set cache "$x,$y" {}
    }
    get_area_impl ctx $c [expr {$x + 1}] $y
    get_area_impl ctx $c [expr {$x - 1}] $y
    get_area_impl ctx $c $x [expr {$y + 1}]
    get_area_impl ctx $c $x [expr {$y - 1}]
}

proc get_area {x y} {
    set ctx {sum 0 cache {}}
    get_area_impl ctx [char_at $x $y] $x $y
    return [dict get $ctx sum]
}

proc get_peri_impl {pctx c x y} {
    proc is_outside {c x y} {
        if {[out_of_range $x $y]} {
            return 1
        }
        if {[char_at $x $y] != $c} {
            return 1
        }
        return 0
    }
    upvar $pctx ctx
    if {[out_of_range $x $y]} {
        return
    }
    if {[dict exists [dict get $ctx cache] "$x,$y"]} {
        return
    }
    if {[char_at $x $y] != $c} {
        return
    }
    set c [char_at $x $y]
    dict update ctx sum sum {
        set sum [expr {$sum + [is_outside $c [expr {$x - 1}] $y] \
                            + [is_outside $c [expr {$x + 1}] $y] \
                            + [is_outside $c $x [expr {$y + 1}]] \
                            + [is_outside $c $x [expr {$y - 1}]]}]
    }
    dict update ctx cache cache {
        dict set cache "$x,$y" {}
    }
    get_peri_impl ctx $c [expr {$x + 1}] $y
    get_peri_impl ctx $c [expr {$x - 1}] $y
    get_peri_impl ctx $c $x [expr {$y + 1}]
    get_peri_impl ctx $c $x [expr {$y - 1}]
}

proc get_peri {x y} {
    set ctx {sum 0 cache {}}
    get_peri_impl ctx [char_at $x $y] $x $y
    return [dict get $ctx sum]
}

set visited {}
proc mark_visited {c x y} {
    global visited
    if {[out_of_range $x $y]} return
    if {[char_at $x $y] != $c} return
    if {[dict exists $visited "$x,$y"]} return
    dict set visited "$x,$y" {}
    incr x
    mark_visited $c $x $y
    incr x -2
    mark_visited $c $x $y
    incr x
    incr y
    mark_visited $c $x $y
    incr y -2
    mark_visited $c $x $y
}

set sum 0
for {set x 0} {$x < $width} {incr x} {
    for {set y 0} {$y < $height} {incr y} {
        if {[dict exists $visited "$x,$y"]} {
            continue
        }
        set peri [get_peri $x $y]
        set area [get_area $x $y]
        mark_visited [char_at $x $y] $x $y
        incr sum [expr {$peri * $area}]
    }
}

puts $sum