From ee4f4626f93c3da7349304bc5009c42c6c9c5e90 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Tue, 30 May 2023 12:52:47 -0500 Subject: [PATCH] 2022 Complete --- 2022/day21/main.go | 302 ++++++++++-------- 2022/day22/main.go | 772 ++++++++++++++++++++++----------------------- 2 files changed, 551 insertions(+), 523 deletions(-) diff --git a/2022/day21/main.go b/2022/day21/main.go index 1cfcd18..63ea8da 100644 --- a/2022/day21/main.go +++ b/2022/day21/main.go @@ -1,143 +1,191 @@ package main import ( + "flag" "fmt" - "strings" + "log" h "git.bullercodeworks.com/brian/adventofcode/helpers" ) -var output []string +type Monkey struct { + name string + yelled bool + value int + m1, m2 *Monkey + op string + name1, name2 string +} + +var monkeys map[string]*Monkey +var verbose bool +var humanval int func main() { - inp := h.StdinToStringSlice() - part1(inp) - part2(inp) -} + partOne := flag.Bool("1", false, "run exercise part1, (default: part2)") + verboseFlag := flag.Bool("v", false, "verbose: print extra info") + humanFlag := flag.Int("h", -1, "set value of \"humn\" monkey, return delta") + flag.Parse() + verbose = *verboseFlag + humanval = *humanFlag + lines := h.StdinToStringSlice() + parse(lines) -func showLog() { - fmt.Println(h.CLEAR_SCREEN) - fmt.Println(strings.Join(output, "\n")) -} - -func setLog(v ...string) { - output = []string{} - log(v...) -} -func log(v ...string) { - output = append(output, v...) -} - -func loadMonkeys(inp []string) map[string]*Monkey { - monkeys := make(map[string]*Monkey) - for _, v := range inp { - if len(v) == 0 { - break - } - m := NewMonkey(strings.Split(v, ": ")...) - monkeys[m.name] = m - } - for _, v := range monkeys { - v.init(monkeys, 1) - } - return monkeys -} - -func part1(inp []string) { - monkeys := loadMonkeys(inp) - log("# Part 1", fmt.Sprintf("%d", monkeys["root"].Value())) - showLog() -} - -func part2(inp []string) { - fmt.Println() - monkeys := loadMonkeys(inp) - - m1 := monkeys["root"].inp1 // <- Human is in here - m2 := monkeys["root"].inp2 // <- We need to make it match this - monkeys["humn"].Value = func() int { return 0 } - fmt.Println(m1.Value(), m2.Value()) - monkeys["humn"].Value = func() int { return 1000 } - fmt.Println(m1.Value(), m2.Value()) - //v := monkeys["root"].Value() - - /* - i := 0 - var lastV int - for { - monkeys["humn"].Value = func() int { - return i - } - v := monkeys["root"].Value() - if v == 0 { - output = append(output[:1], []string{"# Part 2", fmt.Sprintf("Found: %d", i)}...) - showLog() - return - } - output = append(output[:3], []string{ - "# Part 2", - fmt.Sprintf("Scanning... [ humn: %d ]", i), - fmt.Sprintf("CURR: %d =? 0", v), - fmt.Sprintf("LAST: %d", lastV), - }...) - setLog(output...) - showLog() - } - */ -} - -type Operation int - -const ( - opAdd = iota - opSub - opMlt - opDiv -) - -type Monkey struct { - name string - op Operation - inp1, inp2 *Monkey - rawValue string - Value func() int -} - -func NewMonkey(inp ...string) *Monkey { return &Monkey{name: inp[0], rawValue: inp[1]} } -func (m *Monkey) init(monkeys map[string]*Monkey, part int) { - pts := strings.Fields(m.rawValue) - if len(pts) == 1 { - m.Value = func() int { return h.Atoi(pts[0]) } + var result int + if *partOne { + vprint("Running Part 1") + result = part1(lines) } else { - if part == 2 && m.name == "root" { - pts[1] = "-" + vprint("Running Part 2") + result = part2(lines) + } + fmt.Println(result) +} + +func part1(lines []string) int { + return listenCached(monkeys["root"]) +} + +func part2(lines []string) int { + root := monkeys["root"] + delta := Monkey{name: "delta", m1: root.m1, m2: root.m2, name1: root.name1, name2: root.name2, op: "-"} + if humanval != -1 { + human := monkeys["humn"] + human.yelled = true + human.value = humanval + return listenCached(&delta) + } + // We find the value of human making delta == 0 by dichotomy + return humanZeroing(&delta) +} + +func parse(lines []string) { + var name, name1, op, name2 string + var num int + monkeys = make(map[string]*Monkey, 0) + // 1st pass, gather names + for _, line := range lines { + if n, _ := fmt.Sscanf(line, "%s %d", &name, &num); n == 2 { + m := Monkey{name: name[:len(name)-1], value: num, yelled: true} + monkeys[name[:len(name)-1]] = &m + } else if n, _ = fmt.Sscanf(line, "%s %s %s %s", &name, &name1, &op, &name2); n == 4 { + m := Monkey{name: name[:len(name)-1], op: op, name1: name1, name2: name2} + monkeys[name[:len(name)-1]] = &m + } else { + log.Fatalf("Parse error: %s\n", line) } - m.inp1, m.inp2 = monkeys[pts[0]], monkeys[pts[2]] - switch pts[1] { - case "+": - m.Value = func() int { - return m.inp1.Value() + m.inp2.Value() - } - case "-": - m.Value = func() int { - return m.inp1.Value() - m.inp2.Value() - } - case "*": - m.Value = func() int { - return m.inp1.Value() * m.inp2.Value() - } - case "/": - m.Value = func() int { - return m.inp1.Value() / m.inp2.Value() - } - case "=": - m.Value = func() int { - if m.inp1.Value() == m.inp2.Value() { - return 1 - } else { - return 0 - } - } + } + // 2nd pass, fill the sub-monkey fields + for _, m := range monkeys { + if !m.yelled { + m.m1 = monkeys[m.name1] + m.m2 = monkeys[m.name2] } } } + +// Resolve and cache result. Faster but cannot be reused +func listenCached(m *Monkey) int { + if m.yelled { + return m.value + } + n1 := listenCached(m.m1) + n2 := listenCached(m.m2) + m.yelled = true + switch m.op { + case "+": + m.value = n1 + n2 + case "-": + m.value = n1 - n2 + case "*": + m.value = n1 * n2 + case "/": + m.value = n1 / n2 + } + return m.value +} + +func listen(m *Monkey) int { + if m.yelled { + return m.value + } + n1 := listen(m.m1) + n2 := listen(m.m2) + switch m.op { + case "+": + return n1 + n2 + case "-": + return n1 - n2 + case "*": + return n1 * n2 + case "/": + return n1 / n2 + } + return 0 +} +func listenFor(d, h *Monkey, value int) int { + h.value = value + return listen(d) +} + +func humanZeroing(delta *Monkey) int { + human := monkeys["humn"] + human.yelled = true + h1 := 0 + h2 := 1000 + for { + d1 := listenFor(delta, human, h1) + d2 := listenFor(delta, human, h2) + d2d1 := d2 - d1 + vprintf("%d, %d --> %d, %d (diff: %d)\n", h1, h2, d1, d2, d2d1) + if d1 == 0 { + return humanSmallest(delta, human, h1) + } // found + if d2 == 0 { + return humanSmallest(delta, human, h2) + } // found + if d2d1 == 0 { + log.Fatalf("d2 == d1\n") + } + // Linear regresion on h to minimize d + if absInt(d1) < absInt(d2) { + // h1 was closest to the goal: interpolate from it + h := h1 - (d1*(h2-h1))/d2d1 + vprintf("==> h = %d (h1=%d + %d)\n", h, h1, -(d1*(h2-h1))/d2d1) + h2 = h + } else { + // h2 was closest to the goal: interpolate from it + h := h2 - (d2*(h1-h2))/(-d2d1) + vprintf("==> h = %d (h2=%d + %d)\n", h, h2, -(d2*(h1-h2))/(-d2d1)) + h1 = h + } + } +} + +func humanSmallest(delta, human *Monkey, hval int) int { + h := hval + for listenFor(delta, human, h-1) == 0 { + h-- + } + return h +} + +func absInt(i int) int { + if i < 0 { + return -i + } + return i +} + +func vprintf(f string, vals ...interface{}) { + if !verbose { + return + } + fmt.Printf(f, vals...) +} +func vprint(txt string) { + if !verbose { + return + } + fmt.Println(txt) +} diff --git a/2022/day22/main.go b/2022/day22/main.go index ac7faf5..18484e1 100644 --- a/2022/day22/main.go +++ b/2022/day22/main.go @@ -3,420 +3,400 @@ package main import ( "fmt" "math" + "strconv" + "strings" h "git.bullercodeworks.com/brian/adventofcode/helpers" ) func main() { - inp := h.StdinToStringSlice() - //part1(inp) - part2(inp) -} -func part1(inp []string) { - board, inst := inp[:len(inp)-2], inp[len(inp)-1] - b := BuildBoard(board) - var count int - var turn byte - fmt.Println(b) - fmt.Println(b.pos) - for len(inst) > 0 { - count, turn, inst = getNextInstructions(inst) - b.Move(count) - if turn == 'L' || turn == 'R' { - b.Turn(turn) - } - } - fmt.Println("Password:", b.Password()) + lines := h.StdinToStringSlice() + path := lines[len(lines)-1] + m := lines[:len(lines)-2] + + p1 := NewFlatPuzzle(m) + s1 := NewSolver(p1, path) + fmt.Println(s1.Password()) + + p2 := NewCubePuzzle(m) + s2 := NewSolver(p2, path) + fmt.Println(s2.Password()) } -func part2(inp []string) { - board, inst := inp[:len(inp)-2], inp[len(inp)-1] - b := BuildCubeBoard(board) - var count int - var turn byte - fmt.Println(b) - fmt.Println(b.pos) - for len(inst) > 0 { - count, turn, inst = getNextInstructions(inst) - b.Move(count) - if turn == 'L' || turn == 'R' { - b.Turn(turn) - } - } - fmt.Println("Password:", b.Password()) +type Solver struct { + puzzle Puzzle + path string + coordinate Coordinate + facing Direction } -func getNextInstructions(inst string) (int, byte, string) { - for i := range inst { - if inst[i] == 'L' || inst[i] == 'R' { - return h.Atoi(inst[:i]), inst[i], inst[i+1:] +func NewSolver(puzzle Puzzle, path string) *Solver { + solver := Solver{ + puzzle: puzzle, + path: path, + coordinate: puzzle.TopLeft(), + facing: RIGHT, + } + return &solver +} + +func (s *Solver) Password() int { + s.coordinate = s.puzzle.TopLeft() + path := s.path + for len(path) > 0 { + var i int + if path[0] == 'R' || path[0] == 'L' { + i = 1 + } else { + i = strings.IndexAny(path, "RL") + } + if i < 0 { + i = len(path) + } + s.Do(path[:i]) + path = path[i:] + } + return 1000*(s.coordinate.Y+1) + 4*(s.coordinate.X+1) + int(s.facing) +} +func (s *Solver) Do(ins string) { + if ins == "R" { + s.facing = (s.facing + 1) % 4 + return + } + + if ins == "L" { + s.facing = (s.facing + 3) % 4 + return + } + + steps, err := strconv.Atoi(ins) + if err != nil { + panic(err) + } + + for steps > 0 { + c, d := s.puzzle.Next(s.coordinate, s.facing) + if s.puzzle.TileAt(c) == WALL { + break + } + s.coordinate = c + s.facing = d + steps-- + } +} + +type Puzzle interface { + TileAt(Coordinate) Tile + Next(Coordinate, Direction) (Coordinate, Direction) + TopLeft() Coordinate +} + +type FlatPuzzle struct { + tiles map[Coordinate]Tile + col map[int][2]int + row map[int][2]int +} + +func NewFlatPuzzle(s []string) *FlatPuzzle { + puzzle := FlatPuzzle{ + tiles: make(map[Coordinate]Tile), + col: make(map[int][2]int), + row: make(map[int][2]int), + } + for y, line := range s { + for x, c := range line { + switch c { + case '.': + puzzle.tiles[Coordinate{x, y}] = OPEN + case '#': + puzzle.tiles[Coordinate{x, y}] = WALL + default: + continue + } + + if _, ok := puzzle.col[x]; !ok { + puzzle.col[x] = [2]int{math.MaxInt, 0} + } + if y < puzzle.col[x][0] { + puzzle.col[x] = [2]int{y, puzzle.col[x][1]} + } + if y > puzzle.col[x][1] { + puzzle.col[x] = [2]int{puzzle.col[x][0], y} + } + + if _, ok := puzzle.row[y]; !ok { + puzzle.row[y] = [2]int{math.MaxInt, 0} + } + if x < puzzle.row[y][0] { + puzzle.row[y] = [2]int{x, puzzle.row[y][1]} + } + if x > puzzle.row[y][1] { + puzzle.row[y] = [2]int{puzzle.row[y][0], x} + } } } - return h.Atoi(inst), 0, "" + return &puzzle } +func (puzzle *FlatPuzzle) TileAt(c Coordinate) Tile { + return puzzle.tiles[c] +} + +func (puzzle *FlatPuzzle) TopLeft() Coordinate { + return Coordinate{puzzle.row[0][0], 0} +} + +func (puzzle *FlatPuzzle) Next(c Coordinate, d Direction) (Coordinate, Direction) { + c.X += directions[d][0] + c.Y += directions[d][1] + + if _, ok := puzzle.tiles[c]; ok { + return c, d + } + + switch d { + case RIGHT: + c.X = puzzle.row[c.Y][0] + case DOWN: + c.Y = puzzle.col[c.X][0] + case LEFT: + c.X = puzzle.row[c.Y][1] + case UP: + c.Y = puzzle.col[c.X][1] + } + return c, d +} + +type CubePuzzle struct { + tiles map[Coordinate]Tile + mapping map[Coordinate]*Square + squares [6]*Square + sidelen int +} + +func NewCubePuzzle(s []string) *CubePuzzle { + var squares [6]*Square + tiles := make(map[Coordinate]Tile) + mapping := make(map[Coordinate]*Square) + + w, h := 0, len(s) + for _, line := range s { + if len(line) > w { + w = len(line) + } + } + + var sidelen int + switch { + case h/5 == w/2 && h%5 == 0 && w%2 == 0: + sidelen = h / 5 + case h/2 == w/5 && h%2 == 0 && w%5 == 0: + sidelen = h / 2 + case h/3 == w/4 && h%3 == 0 && w%4 == 0: + sidelen = h / 3 + case h/4 == w/3 && h%4 == 0 && w%3 == 0: + sidelen = h / 4 + default: + panic("not a valid cube net") + } + + i := 0 + for y, line := range s { + for x, c := range line { + coordinate := Coordinate{x, y} + switch c { + case '.': + tiles[coordinate] = OPEN + case '#': + tiles[coordinate] = WALL + default: + continue + } + + if x%sidelen == 0 && y%sidelen == 0 { + for squares[i] != nil { + i++ + } + s := NewSquare(i, Coordinate{x, y}, sidelen) + squares[i] = s + mapping[coordinate] = squares[i] + } else { + mapping[coordinate] = mapping[Coordinate{x - (x % sidelen), y - (y % sidelen)}] + } + } + } + + for _, s := range squares { + if a, ok := mapping[Coordinate{s.edges[RIGHT].p1.X + 1, s.edges[RIGHT].p1.Y}]; ok { + s.edges[RIGHT].adjacent = a.edges[LEFT] + a.edges[LEFT].adjacent = s.edges[RIGHT] + } + if a, ok := mapping[Coordinate{s.edges[DOWN].p1.X, s.edges[DOWN].p1.Y + 1}]; ok { + s.edges[DOWN].adjacent = a.edges[UP] + a.edges[UP].adjacent = s.edges[DOWN] + } + } + + queue := []*SquareEdge{} + for _, s := range squares { + for _, e := range s.edges { + if e.adjacent == nil { + queue = append(queue, e) + } + } + } + + for len(queue) > 0 { + e := queue[0] + queue = queue[1:] + + if e.adjacent != nil { + continue + } + + a := e.square.edges[(e.direction+1)%4].adjacent + if a == nil { + queue = append(queue, e) + continue + } + + a = a.square.edges[(a.direction+1)%4].adjacent + if a == nil { + queue = append(queue, e) + continue + } + + a = a.square.edges[(a.direction+1)%4] + e.adjacent = a + a.adjacent = e + } + + puzzle := CubePuzzle{ + tiles: tiles, + mapping: mapping, + squares: squares, + sidelen: sidelen, + } + return &puzzle +} + +func (puzzle *CubePuzzle) TileAt(c Coordinate) Tile { + return puzzle.tiles[c] +} + +func (puzzle *CubePuzzle) TopLeft() Coordinate { + c := puzzle.squares[0].edges[0].p1 + for _, s := range puzzle.squares { + if s.edges[UP].p1.Y == 0 && s.edges[UP].p1.X < c.X { + c = s.edges[UP].p1 + } + } + return c +} + +func (puzzle *CubePuzzle) Next(c Coordinate, d Direction) (Coordinate, Direction) { + s := puzzle.mapping[c] + + c.X += directions[d][0] + c.Y += directions[d][1] + if dst, ok := puzzle.mapping[c]; ok && dst == s { + return c, d + } + + e := s.edges[d] + a := e.adjacent + + var n int + if d == LEFT || d == RIGHT { + n = c.Y - e.p1.Y + } else { + n = c.X - e.p1.X + } + + if (e.direction/2 != a.direction/2 && e.direction%2 != a.direction%2) || e.direction == a.direction { + n = puzzle.sidelen - 1 - n + } + + c = a.NthCoordinate(n) + d = (a.direction + 2) % 4 + return c, d +} + +type Square struct { + id int + edges [4]*SquareEdge +} + +func NewSquare(id int, c Coordinate, sidelen int) *Square { + square := Square{id: id} + var d Direction + for d = RIGHT; d <= UP; d++ { + var p1, p2 Coordinate + switch d { + case RIGHT: + p1 = Coordinate{c.X + sidelen - 1, c.Y} + p2 = Coordinate{c.X + sidelen - 1, c.Y + sidelen - 1} + case DOWN: + p1 = Coordinate{c.X, c.Y + sidelen - 1} + p2 = Coordinate{c.X + sidelen - 1, c.Y + sidelen - 1} + case LEFT: + p1 = c + p2 = Coordinate{c.X, c.Y + sidelen - 1} + case UP: + p1 = c + p2 = Coordinate{c.X + sidelen - 1, c.Y} + } + edge := SquareEdge{ + p1: p1, + p2: p2, + square: &square, + direction: d, + } + square.edges[d] = &edge + } + return &square +} + +type SquareEdge struct { + p1, p2 Coordinate + square *Square + direction Direction + adjacent *SquareEdge +} + +func (edge *SquareEdge) NthCoordinate(n int) Coordinate { + c := edge.p1 + if edge.direction == UP || edge.direction == DOWN { + c.X += n + } else { + c.Y += n + } + return c +} + +type Direction int + const ( - dirR = iota - dirD - dirL - dirU - dirErr - - sideTop = iota - sideBottom - sideRight - sideLeft - sideFront - sideBack + RIGHT Direction = iota + DOWN + LEFT + UP ) -type CubeBoard struct { - Top *CubeBoardSide - Bottom *CubeBoardSide - Left *CubeBoardSide - Right *CubeBoardSide - Front *CubeBoardSide - Back *CubeBoardSide - - // Pos now holds: - // y, x, dir, side - pos []int +var directions [4][2]int = [4][2]int{ + {1, 0}, + {0, 1}, + {-1, 0}, + {0, -1}, } -// For the purposes of directions, we treat the board like this: -// [T] -// [B][L][F][R] -// [B] -// So up from left, front, back, and right are all top -// Up from bottom is front -// Down from bottom is back -// Up from top is back -// Down from top is front -// So you can continually go left on the main level and circumnavigate the cube -// Continually going up from anywhere will eventually get you bouncing between top and back -// Continually going down from anywhere will eventually get you bouncing between bottom and back -func BuildCubeBoard(inp []string) *CubeBoard { - cb := CubeBoard{} - var side string - for i := 0; i < len(inp); i++ { - if len(inp) == 0 { - break - } - side = inp[i] - i++ - cbs := BuildCubeBoardSide(inp[i : i+len(inp[i])]) - switch side { - case "Top": - cb.Top = cbs - case "Bottom": - cb.Bottom = cbs - case "Left": - cb.Left = cbs - case "Right": - cb.Right = cbs - case "Front": - cb.Front = cbs - case "Back": - cb.Back = cbs - } - i = i + cbs.Size() - 1 - } - cb.pos = []int{0, 0, dirR, sideTop} - return &cb -} -func (cb *CubeBoard) Size() int { return cb.Top.Size() } -func (cb *CubeBoard) Move(count int) { - -} -func (cb *CubeBoard) GetPosUp() []int { - y := cb.pos[0] - x := cb.pos[1] - dir := cb.pos[2] - side := cb.pos[3] - if y == 0 { - // Going to another side - switch side { - case sideTop: - side = sideBack - case sideBottom: - side = sideFront - case sideFront: - side = sideTop - case sideBack: - side = sideTop - case sideLeft: - side = sideTop - case sideRight: - side = sideTop - } - } - return []int{y, x, dir, side} -} -func (cb *CubeBoard) GetPosDown() []int { - y := cb.pos[0] - x := cb.pos[1] - dir := cb.pos[2] - side := cb.pos[3] - if y == cb.Size()-1 { - // Going to another side - switch side { - case sideTop: - side = sideFront - case sideBottom: - side = sideBack - case sideFront: - side = sideBottom - case sideBack: - side = sideBottom - case sideLeft: - side = sideBottom - case sideRight: - side = sideBottom - } - } else { - y = y + 1 - } - return []int{y, x, dir, side} -} -func (cb *CubeBoard) GetPosRight() []int { - y := cb.pos[0] - x := cb.pos[1] - dir := cb.pos[2] - side := cb.pos[3] - if x == cb.Size()-1 { - // Going to another side - switch side { - case sideTop: - side = sideRight - case sideBottom: - side = sideRight - case sideFront: - side = sideRight - case sideBack: - side = sideLeft - case sideLeft: - side = sideFront - case sideRight: - side = sideBack - } - } else { - y = y + 1 - } - return []int{y, x, dir, side} +type Coordinate struct { + X, Y int } -// Get the current pos' side -func (cb *CubeBoard) Side() *CubeBoardSide { - switch cb.pos[3] { - case sideTop: - return cb.Top - case sideBottom: - return cb.Bottom - case sideFront: - return cb.Front - case sideBack: - return cb.Back - case sideLeft: - return cb.Left - case sideRight: - return cb.Right - } - return nil -} -func (cb *CubeBoard) Turn(dir byte) { - if dir == 'R' { - cb.pos[2] = (cb.pos[2] + 1) % dirErr - } else if dir == 'L' { - cb.pos[2] = (cb.pos[2] - 1 + dirErr) % dirErr - } -} -func (cb *CubeBoard) Tile(y, x, side int) byte { - switch side { - case sideTop: - return cb.Top.Tile(y, x) - case sideBottom: - return cb.Bottom.Tile(y, x) - case sideLeft: - return cb.Left.Tile(y, x) - case sideRight: - return cb.Right.Tile(y, x) - case sideFront: - return cb.Front.Tile(y, x) - case sideBack: - return cb.Back.Tile(y, x) - } - return 0 -} -func (cb *CubeBoard) Password() int { - return 0 -} +type Tile bool -type CubeBoardSide struct { - spots [][]byte -} - -func BuildCubeBoardSide(inp []string) *CubeBoardSide { - cbs := CubeBoardSide{} - fmt.Println("Building CBS from:", inp) - cbs.spots = make([][]byte, len(inp)) - for y := range inp { - row := make([]byte, len(inp[y])) - for x := range inp[y] { - row[x] = inp[y][x] - } - cbs.spots[y] = row - } - return &cbs -} -func (cbs *CubeBoardSide) Size() int { - return len(cbs.spots) -} -func (cbs *CubeBoardSide) Tile(y, x int) byte { - if len(cbs.spots) < y && len(cbs.spots[y]) < x { - return cbs.spots[y][x] - } - return 0 -} - -type Board struct { - spots [][]byte - // pos holds - // y, x, dir - pos []int -} - -func BuildBoard(inp []string) *Board { - maxX := math.MinInt - for y := range inp { - maxX = h.Max(maxX, len(inp[y])) - } - b := Board{ - spots: make([][]byte, len(inp)), - } - for y := range inp { - sl := make([]byte, maxX) - for x := range inp[y] { - sl[x] = inp[y][x] - } - b.spots[y] = sl - } - - b.pos = make([]int, 3) - for x := range b.spots[0] { - if b.spots[0][x] == '.' { - b.pos = []int{0, x, dirR} - break - } - } - return &b -} -func (b *Board) Password() int { - return ((b.pos[0] + 1) * 1000) + ((b.pos[1] + 1) * 4) + b.pos[2] -} -func (b *Board) Tile(y, x int) byte { - if len(b.spots) > y && len(b.spots[y]) > x { - return b.spots[y][x] - } - return 0 -} -func (b *Board) GetNextPos() []int { - switch b.pos[2] { - case dirU: - return b.GetPosUp() - case dirR: - return b.GetPosRight() - case dirD: - return b.GetPosDown() - case dirL: - return b.GetPosLeft() - } - return []int{-1, -1} -} -func (b *Board) GetPosUp() []int { - x := b.pos[1] - y := ((b.pos[0] - 1) + len(b.spots)) % len(b.spots) - for (b.spots[y][x] == 0 || b.spots[y][x] == 32) && y != b.pos[0] { - y = ((y - 1) + len(b.spots)) % len(b.spots) - } - return []int{y, x} -} -func (b *Board) GetPosRight() []int { - y := b.pos[0] - x := (b.pos[1] + 1) % len(b.spots[y]) - for (b.spots[y][x] == 0 || b.spots[y][x] == 32) && x != b.pos[1] { - x = (x + 1) % len(b.spots[y]) - } - return []int{y, x} -} -func (b *Board) GetPosDown() []int { - x := b.pos[1] - y := (b.pos[0] + 1) % len(b.spots) - for (b.spots[y][x] == 0 || b.spots[y][x] == 32) && y != b.pos[0] { - y = (y + 1) % len(b.spots) - } - return []int{y, x} -} -func (b *Board) GetPosLeft() []int { - y := b.pos[0] - x := ((b.pos[1] - 1) + len(b.spots[y])) % len(b.spots[y]) - for (b.spots[y][x] == 0 || b.spots[y][x] == 32) && x != b.pos[1] { - x = ((x - 1) + len(b.spots[y])) % len(b.spots[y]) - } - return []int{y, x} -} - -func (b *Board) Move(count int) { - var to []int - for count > 0 { - switch b.pos[2] { - case dirU: - to = b.GetPosUp() - case dirR: - to = b.GetPosRight() - case dirD: - to = b.GetPosDown() - case dirL: - to = b.GetPosLeft() - } - if b.Tile(to[0], to[1]) == '.' { - count-- - b.pos[0], b.pos[1] = to[0], to[1] - } else { - break - } - } -} - -func (b *Board) Turn(dir byte) { - if dir == 'R' { - b.pos[2] = (b.pos[2] + 1) % dirErr - } else if dir == 'L' { - b.pos[2] = (b.pos[2] - 1 + dirErr) % dirErr - } -} - -func (b Board) String() string { - var ret string - for y := range b.spots { - for x := range b.spots[y] { - bt := b.spots[y][x] - if b.pos[1] == x && b.pos[0] == y { - switch b.pos[2] { - case dirU: - bt = '^' - case dirR: - bt = '>' - case dirD: - bt = 'v' - case dirL: - bt = '<' - default: - bt = '!' - } - } - ret = ret + fmt.Sprint(string(bt)) - } - ret = ret + "\n" - } - return ret -} +const ( + OPEN Tile = true + WALL Tile = false +)