diff --git a/day13/day13 b/day13/day13 new file mode 100755 index 0000000..435e75d Binary files /dev/null and b/day13/day13 differ diff --git a/day13/main.go b/day13/main.go new file mode 100644 index 0000000..63cf167 --- /dev/null +++ b/day13/main.go @@ -0,0 +1,317 @@ +package main + +import ( + "fmt" + "log" + "os" + "strconv" + "strings" + "time" + + "github.com/fatih/color" +) + +// Puzzle 1 Input: 1364 31 39 +// Puzzle 1 Test Input: 10 7 4 +// Puzzle 2: 101 < x < 128 ?? 103 +func main() { + if len(os.Args) < 4 { + fmt.Println("Usage: day13 ") + os.Exit(1) + } + seed := atoi(os.Args[1]) + destX, destY := atoi(os.Args[2]), atoi(os.Args[3]) + f := CreateFloor(1, 1, destX, destY, seed) + + //f.Print() + //SolveMaze(f) + fmt.Println("WalkFloor(50):", f.player.WalkFloor(50)) + f.Print() +} + +// SolveMaze finds a solution to the maze +func SolveMaze(f *Floor) { + for !f.player.FoundExit() { + time.Sleep(time.Millisecond * 100) + ClearScreen() + f.player.MoveToExit() + f.Print() + } + fmt.Println("Found the exit in", f.player.NumSteps(), "steps") +} + +type Coord struct { + x, y int +} + +func (c *Coord) Is(x, y int) bool { + return c.x == x && c.y == y +} + +func (c *Coord) Equals(t *Coord) bool { + return c.x == t.x && c.y == t.y +} + +func NewCoord(x, y int) *Coord { + return &Coord{x, y} +} + +type Player struct { + pos *Coord + currentFloor *Floor + visited []Coord + path Path +} + +// WalkFloor determines how many places you can get to by taking +// steps +func (p *Player) WalkFloor(dist int) int { + if dist <= 0 { + return 0 + } + fmt.Println("WalkFloor(", dist, ")") + var ret int + tryX, tryY := p.pos.x+1, p.pos.y + if !p.HasBeenAt(tryX, tryY) && !p.currentFloor.IsWall(tryX, tryY) { + ret++ + p.WalkEast() + fmt.Println(" Walking East") + ret += p.WalkFloor(dist - 1) + p.WalkWest() + } + tryX, tryY = p.pos.x-1, p.pos.y + if !p.HasBeenAt(tryX, tryY) && !p.currentFloor.IsWall(tryX, tryY) { + ret++ + p.WalkWest() + fmt.Println(" Walking West") + ret += p.WalkFloor(dist - 1) + p.WalkEast() + } + tryX, tryY = p.pos.x, p.pos.y+1 + if !p.HasBeenAt(tryX, tryY) && !p.currentFloor.IsWall(tryX, tryY) { + ret++ + p.WalkSouth() + fmt.Println(" Walking South") + ret += p.WalkFloor(dist - 1) + p.WalkNorth() + } + tryX, tryY = p.pos.x, p.pos.y-1 + if !p.HasBeenAt(tryX, tryY) && !p.currentFloor.IsWall(tryX, tryY) { + ret++ + p.WalkNorth() + fmt.Println(" Walking North") + ret += p.WalkFloor(dist - 1) + p.WalkSouth() + } + return ret +} + +func (p *Player) NumSteps() int { + return len(p.path.coords) +} + +func (p *Player) MoveToExit() bool { + // First try to move closer to the goal + // Horizontally, then Vertically + if p.pos.x < p.currentFloor.end.x { + if !p.HasBeenAt(p.pos.x+1, p.pos.y) && p.WalkEast() { + return true + } + } + if p.pos.x > p.currentFloor.end.x { + if !p.HasBeenAt(p.pos.x-1, p.pos.y) && p.WalkWest() { + return true + } + } + if p.pos.y < p.currentFloor.end.y { + if !p.HasBeenAt(p.pos.x, p.pos.y+1) && p.WalkSouth() { + return true + } + } + if p.pos.y > p.currentFloor.end.y { + if !p.HasBeenAt(p.pos.x, p.pos.y-1) && p.WalkNorth() { + return true + } + } + // Couldn't move towards it, can we move anywhere? + if !p.HasBeenAt(p.pos.x+1, p.pos.y) && p.WalkEast() { + return true + } + if !p.HasBeenAt(p.pos.x-1, p.pos.y) && p.WalkWest() { + return true + } + if !p.HasBeenAt(p.pos.x, p.pos.y+1) && p.WalkSouth() { + return true + } + if !p.HasBeenAt(p.pos.x, p.pos.y-1) && p.WalkNorth() { + return true + } + + // Couldn't do any of those. Pop the path + if !p.Backtrack() { + // Couldn't backtrack! Game over man! + fmt.Println("Error! Couldn't find valid path!") + os.Exit(1) + } + return false +} + +func (p *Player) Backtrack() bool { + if len(p.path.coords) == 0 { + return false + } + p.path.Pop() + p.pos = &p.path.coords[len(p.path.coords)-1] + return true +} + +func (p *Player) WalkEast() bool { + if !p.currentFloor.IsWall(p.pos.x+1, p.pos.y) { + p.pos.x++ + p.path.Append(*p.pos) + p.visited = append(p.visited, *p.pos) + return true + } + return false +} + +func (p *Player) WalkWest() bool { + if !p.currentFloor.IsWall(p.pos.x-1, p.pos.y) && p.pos.x > 0 { + p.pos.x-- + p.path.Append(*p.pos) + p.visited = append(p.visited, *p.pos) + return true + } + return false +} + +func (p *Player) WalkNorth() bool { + if !p.currentFloor.IsWall(p.pos.x, p.pos.y-1) && p.pos.y > 0 { + p.pos.y-- + p.path.Append(*p.pos) + p.visited = append(p.visited, *p.pos) + return true + } + return false +} + +func (p *Player) WalkSouth() bool { + if !p.currentFloor.IsWall(p.pos.x, p.pos.y+1) { + p.pos.y++ + p.path.Append(*p.pos) + p.visited = append(p.visited, *p.pos) + return true + } + return false +} + +func (p *Player) HasBeenAt(x, y int) bool { + for i := range p.visited { + if p.visited[i].Is(x, y) { + return true + } + } + return false +} + +func (p *Player) FoundExit() bool { + return p.pos.Equals(p.currentFloor.end) +} + +type Path struct { + coords []Coord +} + +func (p *Path) Append(c Coord) { + p.coords = append(p.coords, c) +} + +func (p *Path) Pop() Coord { + var ret Coord + ret, p.coords = p.coords[len(p.coords)-1], p.coords[:len(p.coords)-1] + return ret +} + +func (p *Path) ContainsCoord(c *Coord) bool { + for i := range p.coords { + if p.coords[i].Equals(c) { + return true + } + } + return false +} + +type Floor struct { + player *Player + start *Coord + end *Coord + seed int +} + +func CreateFloor(stX, stY, endX, endY, seed int) *Floor { + f := Floor{ + start: NewCoord(stX, stY), + end: NewCoord(endX, endY), + seed: seed, + } + f.player = &Player{pos: f.start} + f.player.currentFloor = &f + return &f +} + +func (f *Floor) IsWall(x, y int) bool { + sum := (x*x + 3*x + 2*x*y + y + y*y + f.seed) + s := fmt.Sprintf("%b", sum) + if strings.Count(s, "1")%2 == 0 { + return false + } + return true +} + +func (f *Floor) Print() { + wall := color.New(color.BgWhite) + space := color.New(color.FgWhite) + g := color.New(color.BgGreen).Add(color.FgBlack) + g.Add(color.Bold) + r := color.New(color.BgRed) + topY, topX := f.end.y+10, f.end.x+10 + if f.player.pos.y > f.end.y { + topY = f.player.pos.y + 5 + } + if f.player.pos.x > f.end.x { + topX = f.player.pos.x + 5 + } + for y := 0; y < topY; y++ { + for x := 0; x < topX; x++ { + if f.player.pos.Is(x, y) { + g.Print("O") + } else if f.player.path.ContainsCoord(&Coord{x, y}) { + g.Print(".") + } else if f.player.HasBeenAt(x, y) { + r.Print(" ") + } else { + if f.end.Is(x, y) { + space.Print("X") + } else if f.IsWall(x, y) { + wall.Print(" ") + } else { + space.Print(".") + } + } + } + fmt.Println() + } +} + +func ClearScreen() { + fmt.Print("\033[H\033[2J") +} + +func atoi(i string) int { + var ret int + var err error + if ret, err = strconv.Atoi(i); err != nil { + log.Fatal("Invalid Atoi") + } + return ret +}