mirror of
https://github.com/Luzifer/codingame-solutions.git
synced 2024-11-13 00:02:48 +00:00
201 lines
4.1 KiB
Go
201 lines
4.1 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
type direction string
|
|
|
|
const (
|
|
SOUTH direction = "SOUTH"
|
|
WEST = "WEST"
|
|
EAST = "EAST"
|
|
NORTH = "NORTH"
|
|
)
|
|
|
|
const (
|
|
MAP_TILE_OBSTACLE byte = 'X'
|
|
MAP_TILE_WALL = '#'
|
|
MAP_TILE_EAST = 'E'
|
|
MAP_TILE_WEST = 'W'
|
|
MAP_TILE_NORTH = 'N'
|
|
MAP_TILE_SOUTH = 'S'
|
|
MAP_TILE_BEER = 'B'
|
|
MAP_TILE_INVERT = 'I'
|
|
MAP_TILE_START = '@'
|
|
MAP_TILE_TELEPORTER = 'T'
|
|
MAP_TILE_FLOOR = ' '
|
|
MAP_TILE_DEATH = '$'
|
|
MAP_TILE_OUT_OF_BOUNDS = '?' // Internal error-reporter
|
|
)
|
|
|
|
var directionChange = map[bool][]direction{
|
|
false: []direction{SOUTH, EAST, NORTH, WEST},
|
|
true: []direction{WEST, NORTH, EAST, SOUTH},
|
|
}
|
|
|
|
type point struct{ X, Y int }
|
|
|
|
func (p point) Next(d direction) point {
|
|
switch d {
|
|
case SOUTH:
|
|
return point{p.X, p.Y + 1}
|
|
case WEST:
|
|
return point{p.X - 1, p.Y}
|
|
case NORTH:
|
|
return point{p.X, p.Y - 1}
|
|
case EAST:
|
|
return point{p.X + 1, p.Y}
|
|
}
|
|
return p
|
|
}
|
|
|
|
type playground struct {
|
|
Source string
|
|
Width, Height int
|
|
}
|
|
|
|
func (p playground) Get(pt point) byte {
|
|
pos := pt.Y*p.Width + pt.X
|
|
if pos >= len(p.Source) {
|
|
return MAP_TILE_OUT_OF_BOUNDS
|
|
}
|
|
return p.Source[pos]
|
|
}
|
|
|
|
func (p *playground) Set(pt point, mapReplace byte) {
|
|
pos := pt.Y*p.Width + pt.X
|
|
tmp := []byte(p.Source)
|
|
tmp[pos] = mapReplace
|
|
p.Source = string(tmp)
|
|
}
|
|
|
|
func (p playground) FindUniquePOI(poi byte) point {
|
|
pos := strings.IndexByte(p.Source, poi)
|
|
return point{X: pos % p.Width, Y: pos / p.Width}
|
|
}
|
|
|
|
func (p playground) FindMultiplePOI(poi byte) []point {
|
|
out := []point{}
|
|
|
|
for pos := 0; pos < len(p.Source); pos++ {
|
|
if p.Source[pos] == poi {
|
|
out = append(out, point{X: pos % p.Width, Y: pos / p.Width})
|
|
}
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
type bender struct {
|
|
City playground
|
|
|
|
Position point
|
|
BeerMode bool
|
|
InvertMode bool
|
|
MoveDirection direction
|
|
|
|
path []string
|
|
}
|
|
|
|
func (b *bender) Init() {
|
|
b.Position = b.City.FindUniquePOI(MAP_TILE_START)
|
|
b.MoveDirection = SOUTH
|
|
}
|
|
|
|
func (b bender) Path() string {
|
|
return strings.Join(b.path, "\n")
|
|
}
|
|
|
|
func (b *bender) Trace(maxSteps int) error {
|
|
madeSteps := 0
|
|
|
|
for madeSteps < maxSteps {
|
|
nextPos := b.Position.Next(b.MoveDirection)
|
|
|
|
nd := 0
|
|
for b.City.Get(nextPos) == MAP_TILE_OBSTACLE || b.City.Get(nextPos) == MAP_TILE_WALL {
|
|
if b.BeerMode && b.City.Get(nextPos) == MAP_TILE_OBSTACLE {
|
|
b.City.Set(nextPos, MAP_TILE_FLOOR)
|
|
} else {
|
|
b.MoveDirection = directionChange[b.InvertMode][nd]
|
|
nextPos = b.Position.Next(b.MoveDirection)
|
|
nd++
|
|
}
|
|
}
|
|
|
|
b.Position = nextPos
|
|
log.Printf("Moved to %#v", b.Position)
|
|
b.path = append(b.path, string(b.MoveDirection))
|
|
|
|
switch b.City.Get(b.Position) {
|
|
case MAP_TILE_START:
|
|
return errors.New("Returned to start, no cool.")
|
|
case MAP_TILE_OUT_OF_BOUNDS:
|
|
return errors.New("Bender left the map. How?!?")
|
|
case MAP_TILE_WEST:
|
|
b.MoveDirection = WEST
|
|
case MAP_TILE_EAST:
|
|
b.MoveDirection = EAST
|
|
case MAP_TILE_SOUTH:
|
|
b.MoveDirection = SOUTH
|
|
case MAP_TILE_NORTH:
|
|
b.MoveDirection = NORTH
|
|
case MAP_TILE_BEER:
|
|
b.BeerMode = !b.BeerMode
|
|
case MAP_TILE_INVERT:
|
|
b.InvertMode = !b.InvertMode
|
|
case MAP_TILE_DEATH:
|
|
// We got killed. Bye cruel world!
|
|
return nil
|
|
case MAP_TILE_TELEPORTER:
|
|
teleporters := b.City.FindMultiplePOI(MAP_TILE_TELEPORTER)
|
|
for i := range teleporters {
|
|
t := teleporters[i]
|
|
if t.X == b.Position.X && t.Y == b.Position.Y {
|
|
continue
|
|
}
|
|
b.Position = t
|
|
log.Printf("Bzzzzp. Now at: %#v", b.Position)
|
|
break
|
|
}
|
|
}
|
|
|
|
madeSteps++
|
|
}
|
|
|
|
return errors.New("Max steps reached. (LOOP)")
|
|
}
|
|
|
|
func main() {
|
|
scanner := bufio.NewScanner(os.Stdin)
|
|
|
|
var L, C int
|
|
scanner.Scan()
|
|
fmt.Sscan(scanner.Text(), &L, &C)
|
|
|
|
b := &bender{}
|
|
b.City.Width = C
|
|
b.City.Height = L
|
|
|
|
for i := 0; i < L; i++ {
|
|
scanner.Scan()
|
|
b.City.Source = b.City.Source + scanner.Text()
|
|
}
|
|
|
|
b.Init()
|
|
|
|
log.Printf("%#v", b)
|
|
|
|
if err := b.Trace(L * C); err != nil {
|
|
log.Printf("Trace error: %s", err)
|
|
fmt.Println("LOOP")
|
|
} else {
|
|
fmt.Println(b.Path())
|
|
}
|
|
}
|