Posted to tcl by mjanssen at Wed Jul 29 11:03:45 GMT 2020view pretty

proc init {varname} {
    upvar #0 $varname moon
    set moon(1,x) 3
    set moon(1,y) 15
    set moon(1,z) 8
    set moon(1,vx) 0
    set moon(1,vy) 0 
    set moon(1,vz) 0
    
    set moon(2,x) 5
    set moon(2,y) -1
    set moon(2,z) -2
    set moon(2,vx) 0
    set moon(2,vy) 0 
    set moon(2,vz) 0
    
    set moon(3,x) -10
    set moon(3,y) 8
    set moon(3,z) 2
    set moon(3,vx) 0
    set moon(3,vy) 0 
    set moon(3,vz) 0
    
    set moon(4,x) 8
    set moon(4,y) 4
    set moon(4,z) -5
    set moon(4,vx) 0
    set moon(4,vy) 0 
    set moon(4,vz) 0
}

proc gravity {ax}  {
    variable moon
    foreach {id1 id2} {1 2 1 3 1 4 2 3 2 4 3 4} {
        if {$moon($id1,$ax) < $moon($id2,$ax)} {
            incr moon($id1,v$ax)
            incr moon($id2,v$ax) -1
        }
        if {$moon($id1,$ax) > $moon($id2,$ax)} {
            incr moon($id2,v$ax)
            incr moon($id1,v$ax) -1
        }      
    }
}
proc velocity {ax} {
    variable moon
    incr moon(1,$ax) $moon(1,v$ax)
    incr moon(2,$ax) $moon(2,v$ax)
    incr moon(3,$ax) $moon(3,v$ax)
    incr moon(4,$ax) $moon(4,v$ax)
}

namespace import tcl::mathfunc::abs
proc energy {} {
    variable moon
    set total 0
    foreach id {1 2 3 4} {
        set kin 0
        set pot 0
        foreach ax {x y z} {
            incr pot [abs $moon($id,$ax)]
            incr kin [abs $moon($id,v$ax)]
        }
        incr total [expr {$kin*$pot}]
    }
    return $total
}

proc part1 {} {
    init moon
    for {set i 0} {$i < 1000} {incr i} {
        gravity x ; velocity x
        gravity y ; velocity y
        gravity z ; velocity z
    }
    energy
}

proc period {ax} {
    variable moon

    set step 0
    while {true} {
        gravity $ax
        velocity $ax
        incr step
        # if {$step % 1000 == 0} {puts $step}
        # Halfway between initial and period all velocities on an axis are 0 again (as in the intial condition)
        if { 
            $moon(1,v$ax) == 0 &&
            $moon(2,v$ax) == 0 &&
            $moon(3,v$ax) == 0 &&
            $moon(4,v$ax) == 0
        } {
            return [expr {$step * 2}]
        }
    }
}


proc factors {n} {
    set top [expr {int(sqrt($n))+1}]
    set factor 2
    set factors {}
    # puts "########## $n"
    while {$factor <= $top && $n != 1} {
        # puts "$n : $factor < $top"
        if {$n % $factor == 0} {
            dict incr factors $factor
            set n [expr {$n / $factor}]
        } else {
            incr factor
        }
    }
    if {$n != 1} {
        dict incr factors $n
    }
    # puts "<<<<<<<<< $factors"
    return $factors
}

proc part2 {} {
    namespace import tcl::mathop::*
    puts [time {
    init moon
    set total_factors {}
    set periods [list [period x] [period y] [period z]]
    foreach period $periods {
        set f [factors $period]
        # puts $f
        foreach k [dict keys $f] {
            # puts $k
            if {![dict exists $total_factors $k] || [dict get $f $k] > [dict get $total_factors $k]} {
                dict set total_factors $k [dict get $f $k] 
            }
        }
    }
    set factors {}
    # puts $total_factors
     foreach {factor count} $total_factors {
        lappend factors {*}[lrepeat  $count $factor]
     }
    }]
     return [* {*}$factors]
}

puts [part2]