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 }