func (g *game) pickTarget(currentCoords [2]int) *character {
	enemyType := getEnemyType(g.grid[currentCoords[0]][currentCoords[1]])
	var chosenEnemy *character
	for _, d := range diffs {
		nextRow := currentCoords[0] + d[0]
		nextCol := currentCoords[1] + d[1]
		next := [2]int{nextRow, nextCol}
		enemy, ok := g.coordsToChars[next]
		if ok && enemy.charType == enemyType {
			if chosenEnemy == nil || chosenEnemy.hp > enemy.hp {
				chosenEnemy = enemy
			}
		}
	}
	return chosenEnemy
}
checks the four directions around the given coordinate
package main
import (
	"flag"
	"fmt"
	"sort"
	"strings"
	"github.com/alexchao26/advent-of-code-go/util"
)
func main() {
	var part int
	flag.IntVar(&part, "part", 1, "part 1 or 2")
	flag.Parse()
	fmt.Println("Running part", part)
	if part == 1 {
		ans := part1(util.ReadFile("./input.txt"))
		fmt.Println("Output:", ans)
	} else {
		ans := part2(util.ReadFile("./input.txt"))
		fmt.Println("Output:", ans)
	}
}
func part1(input string) int {
	g := newGame(input)
	return g.runFullGame()
}
func part2(input string) int {
	var outcome int
	for elfPower := 4; ; elfPower++ {
		g := newGame(input)
		elvesBefore := g.countElves()
		for _, c := range g.coordsToChars {
			if c.charType == "E" {
				c.attackPower = elfPower
			}
		}
		outcome = g.runFullGame()
		if elvesBefore == g.countElves() {
			break
		}
	}
	return outcome
}
type game struct {
	grid          [][]string
	coordsToChars map[[2]int]*character
	rounds        int
}
func (g game) String() string {
	ans := fmt.Sprintf("Rounds: %d\n", g.rounds)
	for rowNum, row := range g.grid {
		ans += fmt.Sprintf("\n%02d: ", rowNum)
		for _, v := range row {
			ans += v
		}
	}
	ans += "\nAlive characters:"
	for coord, char := range g.coordsToChars {
		ans += fmt.Sprintf("\n%v: Char: %v", coord, char)
	}
	return ans
}
func (g *game) countElves() int {
	var elves int
	for _, c := range g.coordsToChars {
		if c.charType == "E" {
			elves++
		}
	}
	return elves
}
type character struct {
	coord       [2]int
	hp          int
	charType    string  
	attackPower int
}
func (c character) String() string {
	return fmt.Sprintf("%v %v HP:%v", c.charType, c.coord, c.hp)
}
func newGame(input string) *game {
	lines := strings.Split(input, "\n")
	var grid [][]string
	coordsToChars := map[[2]int]*character{}
	for row, line := range lines {
		grid = append(grid, make([]string, len(line)))
		for col, val := range strings.Split(line, "") {
			grid[row][col] = val
			switch val {
			case "E", "G":
				coord := [2]int{row, col}
				coordsToChars[coord] = &character{
					coord:       coord,
					hp:          200,
					charType:    val,
					attackPower: 3,  
				}
			}
		}
	}
	return &game{
		grid:          grid,
		coordsToChars: coordsToChars,
		rounds:        0,
	}
}
func (g *game) runFullGame() int {
	var gameover bool
	for !gameover {
		gameover = g.runTurn()
	}
	var totalHp int
	for _, c := range g.coordsToChars {
		totalHp += c.hp
	}
	return g.rounds * totalHp
}
func (g *game) runTurn() (gameover bool) {
	charsInOrder := g.getTurnOrder()
	for _, char := range charsInOrder {
		if char.hp <= 0 {
			continue
		}
		enemyType := getEnemyType(char.charType)
		var enemiesFound bool
		for _, c := range g.coordsToChars {
			if c.charType == enemyType {
				enemiesFound = true
				break
			}
		}
		if !enemiesFound {
			return true
		}
		enemy := g.pickTarget(char.coord)
		if enemy != nil {
			g.attack(g.coordsToChars[char.coord], enemy)
		} else {
			inRangeCoordsMap := g.getInRangeOfEnemies(char.charType)
			if len(inRangeCoordsMap) == 0 {
				continue
			}
			nextCoord, willMove := g.determineNextMove(char.coord, inRangeCoordsMap)
			if willMove {
				g.grid[nextCoord[0]][nextCoord[1]] = char.charType
				g.grid[char.coord[0]][char.coord[1]] = "."
				delete(g.coordsToChars, char.coord)  
				g.coordsToChars[nextCoord] = char    
				char.coord = nextCoord               
				enemy := g.pickTarget(nextCoord)
				if enemy != nil {
					g.attack(g.coordsToChars[nextCoord], enemy)
				}
			}
		}
	}
	g.rounds++
	return false
}
func (g *game) getTurnOrder() []*character {
	var charsInOrder []*character
	for i, row := range g.grid {
		for j, tile := range row {
			if tile == "E" || tile == "G" {
				charsInOrder = append(charsInOrder, g.coordsToChars[[2]int{i, j}])
			}
		}
	}
	return charsInOrder
}
var diffs = [][2]int{
	{-1, 0},  
	{0, -1},  
	{0, 1},   
	{1, 0},   
}