package main

import (
	"fmt"
	"strings"

	"github.com/alexchao26/advent-of-code-go/util"
)

func main() {
	input := util.ReadFile("../input.txt")

	lines := strings.Split(input, "\n")

	grid := make([][]string, len(lines))
	for i, v := range lines {
		grid[i] = strings.Split(v, "")
	}

	// can map biodiversity scores because they're essentially bitmaps
	previousBiodiversities := map[int]bool{getBiodiversity(grid): true}

	// run indefinitely
	for {
		// step through a minute
		grid = stepMinute(grid)
		newBiodiversity := getBiodiversity(grid)

		// if new biodiversity score is already in the map, print it and exit
		if previousBiodiversities[newBiodiversity] {
			fmt.Println("Repeated biodiversity score", newBiodiversity)
			return
		}

		// set biodiversity score into in map
		previousBiodiversities[newBiodiversity] = true
	}
}

// steps through one minute and returns the next grid
func stepMinute(grid [][]string) [][]string {
	result := make([][]string, len(grid))

	dRow := [4]int{0, 0, -1, 1}
	dCol := [4]int{-1, 1, 0, 0}

	for row, rowSli := range grid {
		// initialize the rows for the result slice
		result[row] = make([]string, len(grid[0]))

		for col, val := range rowSli {
			// count up the neighbors that are bugs
			var countNeighborBugs int
			for i := 0; i < 4; i++ {
				nextRow, nextCol := row+dRow[i], col+dCol[i]
				isInbounds := nextRow >= 0 && nextCol >= 0 && nextRow < len(grid) && nextCol < len(grid[0])
				if isInbounds && grid[nextRow][nextCol] == "#" {
					countNeighborBugs++
				}
			}

			// determine future state of cell
			switch val {
			case "#":
				// if bug has ONE neighbor only, it lives, otherwise becomes empty
				if countNeighborBugs == 1 {
					result[row][col] = "#"
				} else {
					result[row][col] = "."
				}
			case ".":
				// if empty, becomes a bug if has ONE or TWO neighbors only
				if countNeighborBugs == 1 || countNeighborBugs == 2 {
					result[row][col] = "#"
				} else {
					result[row][col] = "."
				}
			}
		}
	}

	return result
}

func getBiodiversity(grid [][]string) int {
	var biodiversity int
	for i, row := range grid {
		for j, val := range row {
			// dumb cheeky way to get power of two... 1 is 2^0, then shift the power
			// 1 << 0 == 2^0 == 1; 1 << 1 == 2^1
			powerOfTwo := 1 << (5*i + j)
			if val == "#" {
				biodiversity += powerOfTwo
			}
		}
	}

	return biodiversity
}
