204 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			204 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| package overthink
 | |
| 
 | |
| import (
 | |
| 	"bufio"
 | |
| 	"fmt"
 | |
| 	"math"
 | |
| 	"os"
 | |
| 	"sort"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| const (
 | |
| 	UseEmoji    = false
 | |
| 	MaxInt      = int(^uint(0) >> 1)
 | |
| 	ClearScreen = "\033[H\033[2J"
 | |
| 
 | |
| 	DIR_N = -1i
 | |
| 	DIR_E = 1
 | |
| 	DIR_S = 1i
 | |
| 	DIR_W = -1
 | |
| )
 | |
| 
 | |
| var width int
 | |
| var input []byte
 | |
| 
 | |
| var elves []*complex64
 | |
| var goblins []*complex64
 | |
| var allChars []*complex64
 | |
| var charMap map[complex64]*Character
 | |
| 
 | |
| func main() {
 | |
| 	stdinToByteSlice()
 | |
| 	setupBattle()
 | |
| 	part1()
 | |
| }
 | |
| 
 | |
| func part1() {
 | |
| 	//for {
 | |
| 	sortCharacters()
 | |
| 	printBattleField()
 | |
| 	for _, c := range allChars {
 | |
| 		charMap[*c].tick()
 | |
| 	}
 | |
| 	time.Sleep(time.Millisecond * 250)
 | |
| 	printBattleField()
 | |
| 	//}
 | |
| 	fmt.Println("")
 | |
| }
 | |
| 
 | |
| func sortCharacters() {
 | |
| 	sort.Sort(ByPos(allChars))
 | |
| 	sort.Sort(ByPos(elves))
 | |
| 	sort.Sort(ByPos(goblins))
 | |
| }
 | |
| 
 | |
| type Character struct {
 | |
| 	tp     byte
 | |
| 	pos    complex64
 | |
| 	power  int
 | |
| 	health int
 | |
| }
 | |
| 
 | |
| func (c *Character) tick() {
 | |
| 	// If we're already in range of a target, don't look for a new one
 | |
| 	var alreadyAtTarget bool
 | |
| 	var chosenTarget *complex64
 | |
| 	lowestTargetHp := MaxInt
 | |
| 	for _, v := range elves {
 | |
| 		if c.isAdjacentTo(*v) {
 | |
| 			if charMap[*v].health < lowestTargetHp {
 | |
| 				chosenTarget = v
 | |
| 			}
 | |
| 			alreadyAtTarget = true
 | |
| 		}
 | |
| 	}
 | |
| 	if alreadyAtTarget {
 | |
| 		// Attack the target
 | |
| 		charMap[*chosenTarget].health -= c.power
 | |
| 	} else {
 | |
| 		// Looking for a target
 | |
| 		if c.tp == 'G' {
 | |
| 			// First identify all possible targets (elves that have an open adjacent space)
 | |
| 			for _, v := range elves {
 | |
| 
 | |
| 			}
 | |
| 		} else {
 | |
| 			// First identify all possible targets (goblins that have an open adjacent space)
 | |
| 			for _, v := range goblins {
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (c *Character) isAdjacentTo(p complex64) bool {
 | |
| 	return c.pos+DIR_N == p || c.pos+DIR_E == p ||
 | |
| 		c.pos+DIR_S == p || c.pos+DIR_W == p
 | |
| }
 | |
| 
 | |
| func (c *Character) hasOpenFlank() bool {
 | |
| 
 | |
| }
 | |
| 
 | |
| // Not sure if we'll use this...
 | |
| func manhattanDistance(p1, p2 complex64) int {
 | |
| 	x1, y1, x2, y2 := real(p1), imag(p1), real(p2), imag(p2)
 | |
| 	return int(math.Abs(float64(x1)-float64(x2)) + math.Abs(float64(y1)-float64(y2)))
 | |
| }
 | |
| 
 | |
| // We have to sort the characters on each tick by y,x position
 | |
| // y is the imaginary part, x is the real part
 | |
| type ByPos []*complex64
 | |
| 
 | |
| func (c ByPos) Len() int      { return len(c) }
 | |
| func (c ByPos) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
 | |
| func (c ByPos) Less(i, j int) bool {
 | |
| 	return imag(*c[i]) < imag(*c[j]) ||
 | |
| 		(imag(*c[i]) == imag(*c[j]) && real(*c[i]) < real(*c[j]))
 | |
| }
 | |
| 
 | |
| // getByte pulls a byte from the given position in the input
 | |
| func getByte(pos complex64) byte {
 | |
| 	return input[int(real(pos))+int(imag(pos))*width]
 | |
| }
 | |
| 
 | |
| func setByte(pos complex64, b byte) {
 | |
| 	input[int(real(pos))+int(imag(pos))*width] = b
 | |
| }
 | |
| 
 | |
| func getPosFromInt(i int) complex64 {
 | |
|   return complex(float32(i%width), float32(i/width))
 | |
| }
 | |
| 
 | |
| func printBattleField() {
 | |
| 	fmt.Print(ClearScreen)
 | |
| 	for i := 0; i < len(input); i++ {
 | |
| 		pos := getPosFromInt(i)
 | |
| 		var bt byte
 | |
| 		if c, ok := charMap[pos]; ok {
 | |
| 			if UseEmoji {
 | |
| 				switch c.tp {
 | |
| 				case 'G':
 | |
| 					if UseEmoji {
 | |
| 						fmt.Print("👺")
 | |
| 					}
 | |
| 				case 'E':
 | |
| 					fmt.Print("😃")
 | |
| 				}
 | |
| 			} else {
 | |
| 				fmt.Print(string(c.tp))
 | |
| 			}
 | |
| 			bt = c.tp
 | |
| 		} else {
 | |
| 			bt = getByte(pos)
 | |
| 			if UseEmoji {
 | |
| 				if bt == '#' {
 | |
| 					fmt.Print("🏿")
 | |
| 				} else {
 | |
| 					fmt.Print("  ")
 | |
| 				}
 | |
| 			} else {
 | |
| 				fmt.Print(string(bt))
 | |
| 			}
 | |
| 		}
 | |
| 		if i%width == width-1 {
 | |
| 			fmt.Println("")
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func setupBattle() {
 | |
| 	charMap = make(map[complex64]*Character)
 | |
| 	for i := 0; i < len(input); i++ {
 | |
| 		pos := complex(float32(i%width), float32(i/width))
 | |
| 		bt := getByte(pos)
 | |
| 		if bt == 'G' || bt == 'E' {
 | |
| 			charMap[pos] = &Character{
 | |
| 				tp:     bt,
 | |
| 				pos:    pos,
 | |
| 				power:  3,
 | |
| 				health: 200,
 | |
| 			}
 | |
| 			setByte(pos, '.')
 | |
| 			allChars = append(allChars, &charMap[pos].pos)
 | |
| 			switch bt {
 | |
| 			case 'G':
 | |
| 				goblins = append(goblins, &charMap[pos].pos)
 | |
| 			case 'E':
 | |
| 				elves = append(elves, &charMap[pos].pos)
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func stdinToByteSlice() {
 | |
| 	scanner := bufio.NewScanner(os.Stdin)
 | |
| 	for scanner.Scan() {
 | |
| 		data := scanner.Bytes()
 | |
| 		if width == 0 {
 | |
| 			width = len(data)
 | |
| 		}
 | |
| 		input = append(input, data...)
 | |
| 	}
 | |
| }
 |