#!/usr/bin/env tclsh9.0 namespace import ::tcl::mathop::* # Put a margin of ... around the input. # Legit garden plot tiles will therefore be 1..rows and 1..cols. proc input {} { global rows cols grid set grid [list] set rows 0 while {[gets stdin line] >= 0} { if {$line eq ""} continue if {$rows == 0} { set margin [list .] foreach c [split $line ""] {lappend margin .} lappend margin . lappend grid $margin set cols [string length $line] } set row [list .] lappend row {*}[split $line ""] lappend row . lappend grid $row incr rows } lappend grid $margin } proc show {} { global rows cols grid for {set r 0} {$r <= [+ $rows 1]} {incr r} { for {set c 0} {$c <= [+ $cols 1]} {incr c} { puts -nonewline [lindex $grid $r $c] } puts "" } puts "rows=$rows cols=$cols" } proc count {} { global rows cols grid global region set total 0 for {set r 1} {$r <= $rows} {incr r} { for {set c 1} {$c <= $cols} {incr c} { if {[lindex $grid $r $c] eq "."} continue # Find the region starting here. set region [dict create] flood $r $c # puts "region=$region" set area [dict size $region] set p [perimeter $region] incr total [* $area $p] # puts "area=$area p=$p" # Mark the region as dots so we won't re-find it. foreach rc [dict keys $region] { lassign $rc r1 c1 lset grid $r1 $c1 . } } } puts $total } proc flood {r c} { global grid region if {[dict exists $region [list $r $c]]} return set plant [lindex $grid $r $c] dict set region [list $r $c] 1 foreach {dr dc} {0 1 0 -1 1 0 -1 0} { set r1 [+ $r $dr] set c1 [+ $c $dc] if {[lindex $grid $r1 $c1] eq $plant} {flood $r1 $c1} } } # My stupid idea: generate a list of all the individual pieces of fence. # We can't store these as simple {r c} integer pairs because of the O and E # cases. But we can say that {r c} with a non-matching north neighbor would # have a piece of fence at {r c-0.25}. Then, if the tile two spots north has # a southern piece of fence, that would be {r c-2+0.25}. # In theory, then, we can somehow reduce the list of fence pieces, # collecting adjoining pieces into one big piece. First guess: let's # sort the fences by row, and then by col, and then we can eliminate # pieces that are exactly 1 step away. proc perimeter {region} { global grid set fences [list] set plant "" foreach rc [dict keys $region] { lassign $rc r c if {$plant eq ""} {set plant [lindex $grid $r $c]} foreach {dr dc} {0 1 0 -1 1 0 -1 0} { set r1 [+ $r $dr] set c1 [+ $c $dc] if {[lindex $grid $r1 $c1] ne $plant} { lappend fences [list [expr {$r+$dr/4.0}] [expr {$c+$dc/4.0}]] } } } # puts "region=$region" # puts "fences=$fences" # puts "sorted1=[lsort -real -index 0 $fences]" # puts "sorted2=[lsort -real -index 1 $fences]" # First, the horizontal fences. Sort by row (primary) and col (secondary) # and iterate over the result, discarding any piece that's exactly one # col away from the next piece. set prev [list] set discard [dict create] foreach curr [lsort -real -index 0 [lsort -real -index 1 $fences]] { if {! [llength $prev]} { set prev $curr continue } lassign $prev pr pc lassign $curr cr cc if {$cr == $pr && $cc == [+ $pc 1]} { dict set discard $prev 1 } set prev $curr } # Now, do it again for the vertical fences. set prev [list] foreach curr [lsort -real -index 1 [lsort -real -index 0 $fences]] { if {! [llength $prev]} { set prev $curr continue } lassign $prev pr pc lassign $curr cr cc if {$cc == $pc && $cr == [+ $pr 1]} { dict set discard $prev 1 } set prev $curr } # puts "region=$region" # puts "fences=$fences" # puts "discard=$discard" return [- [llength $fences] [dict size $discard]] } input # show count