github.com/mnlphlp/aoc22@v0.0.0-20230330151331-c1dc4bff1b9b/day23/day23.go (about)

     1  package day23
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  
     7  	"github.com/mnlphlp/aoc22/day23/grid"
     8  	"github.com/mnlphlp/aoc22/util"
     9  )
    10  
    11  const (
    12  	// weird order copied from task
    13  	North = iota
    14  	South
    15  	West
    16  	East
    17  )
    18  
    19  func move(elves grid.Grid, rounds int, startDir int, debug bool) (grid.Grid, int) {
    20  	if debug {
    21  		fmt.Println(elves)
    22  	}
    23  	lastHash := elves.Hash()
    24  	for i := 0; i < rounds || rounds == 0; i++ {
    25  		proposed := make(map[util.Pos2]util.Pos2)
    26  		conflicts := make([]util.Pos2, 0)
    27  		elves.ForEach(func(p util.Pos2) {
    28  			if !elves.HasNeighbor(p) {
    29  				return
    30  			}
    31  			next, nextFound := elves.NextPos(p, startDir)
    32  			if nextFound {
    33  				if _, conflict := proposed[next]; conflict {
    34  					// do not move and mark other move for deletion
    35  					conflicts = append(conflicts, next)
    36  				} else {
    37  					proposed[next] = p
    38  				}
    39  			}
    40  		})
    41  		for _, conflict := range conflicts {
    42  			delete(proposed, conflict)
    43  		}
    44  		for _, old := range proposed {
    45  			elves = elves.Remove(old)
    46  		}
    47  		for new := range proposed {
    48  			elves = elves.Insert(new)
    49  		}
    50  		if debug {
    51  			fmt.Printf("Round  %d: %d\n", i+1, countEmpty(elves))
    52  			fmt.Println(elves)
    53  		}
    54  		startDir = (startDir + 1) % 4
    55  		hash := elves.Hash()
    56  		if hash.Equals(lastHash) && rounds == 0 {
    57  			return elves, i + 1
    58  		}
    59  		lastHash = hash
    60  	}
    61  	return elves, rounds
    62  }
    63  
    64  func countEmpty(g grid.Grid) int {
    65  	min, max := util.Pos2{X: 1 << 62, Y: 1 << 62}, util.Pos2{X: -(1 << 62), Y: -(1 << 62)}
    66  	count := 0
    67  	g.ForEach(func(p util.Pos2) {
    68  		min = util.Pos2{X: util.Min(min.X, p.X), Y: util.Min(min.Y, p.Y)}
    69  		max = util.Pos2{X: util.Max(max.X, p.X), Y: util.Max(max.Y, p.Y)}
    70  		count++
    71  	})
    72  	return ((max.X - min.X + 1) * (max.Y - min.Y + 1)) - count
    73  }
    74  
    75  func part1(grid grid.Grid, debug bool) int {
    76  	grid, _ = move(grid, 10, North, debug)
    77  	return countEmpty(grid)
    78  }
    79  
    80  func part2(grid grid.Grid, startDir int, debug bool) int {
    81  	_, lastMove := move(grid, 0, startDir, debug)
    82  	return lastMove
    83  }
    84  
    85  func Solve(input string, debug bool, task int) (string, string) {
    86  	res1, res2 := 0, 0
    87  	g := grid.ParseInput(input)
    88  	if task != 2 {
    89  		res1 = part1(g, debug)
    90  	}
    91  	if task != 1 {
    92  		// if part one already ran continue from that state
    93  		startDir := North
    94  		if task == 0 {
    95  			startDir = (startDir + 10) % 4
    96  			res2 = 10
    97  		}
    98  		res2 += part2(g, startDir, debug)
    99  	}
   100  	if grid.TIMING_ACTIVE {
   101  		fmt.Println("canMove: ", grid.Timing.CanMove)
   102  		fmt.Println("contains: ", grid.Timing.Contains)
   103  		fmt.Println("hash: ", grid.Timing.Hash)
   104  		fmt.Println("insert: ", grid.Timing.Insert)
   105  		fmt.Println("nextMove: ", grid.Timing.NextMove)
   106  		fmt.Println("remove: ", grid.Timing.Remove)
   107  	}
   108  
   109  	return strconv.Itoa(res1), strconv.Itoa(res2)
   110  }