-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 770a975
Showing
14 changed files
with
1,508 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) [year] [fullname] | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
# Plane | ||
|
||
This is a package that was created during a "hackday" at which we attempted to create a [Battlesnake](https://play.battlesnake.com/) and win a battle against each other. The Battlesnake [documentation](https://docs.battlesnake.com/references/useful-algorithms) recommended to use a [flood fill algorithm](https://en.wikipedia.org/wiki/Flood_fill), and I couldn't find an implementation in Go that I could use. | ||
|
||
This package contains methods for working with a surface (to be able to tell which coordinates on a surface are/aren't filled) and for flood filling such surfaces, to determine for example how much unfilled surface exists within a possibly enclosed space, or what the quickest path is - even around obstacles - from A to B. | ||
|
||
|
||
## Surface | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"github.com/minitauros/plane" | ||
) | ||
|
||
func main() { | ||
// Create new surface. | ||
surface := plane.NewSurface(5, 5) | ||
|
||
// Creates a fill as follows. | ||
// . . . . . | ||
// . . . . . | ||
// . . . . . | ||
// x . . . . | ||
// x x . . . | ||
surface.Fill( | ||
plane.Coord{0, 0}, | ||
plane.Coord{1, 0}, | ||
plane.Coord{0, 1}, | ||
) | ||
|
||
// Checking if a coord is filled. | ||
surface.IsFilled(plane.Coord{0, 0}) // True | ||
|
||
// Removing/unfilling coords. | ||
surface.Remove(plane.Coord{0, 0}) | ||
surface.IsFilled(plane.Coord{0, 0}) // False | ||
|
||
// Looping over all filled coords. | ||
for coord := range surface.EachFilled() { | ||
// Do something.. | ||
} | ||
|
||
// Getting all filled coords at once. | ||
surface.GetFilled() // Coords{{0, 0}, {1, 0}, {0, 1}} | ||
|
||
// Count filled. | ||
surface.CountFilled() // 3 | ||
|
||
// Count unfilled. | ||
surface.CountUnfilled() // 22 | ||
|
||
// Get the total surface. | ||
surface.TotalSurface() // 25 | ||
|
||
// Getting the center coord. | ||
surface.GetCenter() // plane.Coord{2, 2} | ||
|
||
// Checking if a coord fits. | ||
surface.Fits(plane.Coord{-1, -1}) // False | ||
surface.Fits(plane.Coord{0, 0}) // True | ||
|
||
// Clone the surface. | ||
// This is useful when passing it to the flood filler, as the flood | ||
// filler will change the surface's state, and you may want to remember | ||
// the original state. | ||
surface.Clone() // *Surface | ||
} | ||
|
||
``` | ||
|
||
## Flood filler | ||
|
||
```go | ||
package main | ||
|
||
import ( | ||
"github.com/minitauros/plane" | ||
) | ||
|
||
func main() { | ||
// Flood filler needs a surface to work with. | ||
surface := plane.NewSurface(5, 5) | ||
|
||
// Create new flood filler. | ||
ff := plane.NewFloodFiller(surface) | ||
|
||
// Fill the plane, using 0,0 as base and starting the flood at 0,1. | ||
// Note that this does **not** fill `base`. | ||
// That means that the whole surface will be filled after this fill, except the `base` coordinate. | ||
// If you want to fill this, call `surface.Fill()`. | ||
ff.Fill(plane.Coord{0, 0}, plane.Coord{0, 1}) | ||
|
||
// Return the quickest path from 0,0 to 4,4. | ||
// This will go around obstacles. | ||
ff.CountSteps(plane.Coord{0, 0}, plane.Coord{0, 1}) // 9 | ||
|
||
// Returns true if 5,5 can be reached, i.e. if there are no obstacles (filled coords) in the way that the flood | ||
// cannot pass through in some way. | ||
ff.CanReach(plane.Coord{0, 0}, plane.Coord{5, 5}) // True | ||
|
||
// Returns true if 5,5 can be reached, i.e. if there are no obstacles (filled coords) in the way that the flood | ||
// cannot pass through in some way. | ||
// Forces the flood to start at 0,1 and gives it no other options. | ||
ff.CanReachWhenStartingFillAt(plane.Coord{0, 0}, plane.Coord{5, 5}, plane.Coord{0, 1}) | ||
} | ||
|
||
``` | ||
|
||
## Notes | ||
|
||
This package was created while working under time pressure, because I had to win the Battlesnake hackathon. I have added tests for some cases, but some are missing. So far code seems to be working. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package plane | ||
|
||
import ( | ||
"math" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
// Coord is a coordinate. | ||
type Coord struct { | ||
X int `json:"x"` | ||
Y int `json:"y"` | ||
} | ||
|
||
// Equals returns true if the current coord equals the given other coord. | ||
func (c Coord) Equals(other Coord) bool { | ||
return c.X == other.X && c.Y == other.Y | ||
} | ||
|
||
// String satisfies stringer. | ||
func (c Coord) String() string { | ||
return strconv.Itoa(c.X) + "," + strconv.Itoa(c.Y) | ||
} | ||
|
||
// ConnectsTo returns true if the current coord connects directly to the given other coordinate. | ||
func (c Coord) ConnectsTo(other Coord) bool { | ||
horizontalDiff := abs(c.X - other.X) | ||
verticalDiff := abs(c.Y - other.Y) | ||
return horizontalDiff <= 1 && verticalDiff <= 1 && !(horizontalDiff == 1 && verticalDiff == 1) | ||
} | ||
|
||
// GetCoordInDirection returns the first coordinate in the given direction from the current coordinate. | ||
func (c Coord) GetCoordInDirection(d Direction) Coord { | ||
switch d { | ||
case Top: | ||
return Coord{c.X, c.Y + 1} | ||
case Bot: | ||
return Coord{c.X, c.Y - 1} | ||
case Right: | ||
return Coord{c.X + 1, c.Y} | ||
case Left: | ||
return Coord{c.X - 1, c.Y} | ||
} | ||
return c | ||
} | ||
|
||
// GetCoordsTo returns the coords of which the direction can be taken to move towards the given `to` coord. | ||
func (c Coord) GetCoordsTo(to Coord) Coords { | ||
coords := make(Coords, 0, 2) | ||
if to.X > c.X { | ||
coords = append(coords, c.GetCoordInDirection(Right)) | ||
} else if to.X < c.X { | ||
coords = append(coords, c.GetCoordInDirection(Left)) | ||
} | ||
if to.Y > c.Y { | ||
coords = append(coords, c.GetCoordInDirection(Top)) | ||
} else if to.Y < c.Y { | ||
coords = append(coords, c.GetCoordInDirection(Bot)) | ||
} | ||
return coords | ||
} | ||
|
||
// GetDirectionsTo returns the directions that can be taken to move towards the given `to` coord. | ||
func (c Coord) GetDirectionsTo(to Coord) []Direction { | ||
dirs := make([]Direction, 0, 2) | ||
if to.X > c.X { | ||
dirs = append(dirs, Right) | ||
} else if to.X < c.X { | ||
dirs = append(dirs, Left) | ||
} | ||
if to.Y > c.Y { | ||
dirs = append(dirs, Top) | ||
} else if to.Y < c.Y { | ||
dirs = append(dirs, Bot) | ||
} | ||
return dirs | ||
} | ||
|
||
// GetCoordsAround returns all coords around the current coord. | ||
func (c Coord) GetCoordsAround() Coords { | ||
return []Coord{ | ||
{c.X + 1, c.Y}, | ||
{c.X - 1, c.Y}, | ||
{c.X, c.Y + 1}, | ||
{c.X, c.Y - 1}, | ||
} | ||
} | ||
|
||
// Coords is an array of coordinates. | ||
type Coords []Coord | ||
|
||
// GetIntersections returns of the given coordinates those that intersect with the current ones. | ||
func (coords Coords) GetIntersections(other []Coord) Coords { | ||
var intersections []Coord | ||
for _, coord1 := range coords { | ||
for _, coord2 := range other { | ||
if coord1.Equals(coord2) { | ||
intersections = append(intersections, coord1) | ||
} | ||
} | ||
} | ||
return intersections | ||
} | ||
|
||
// Remove removes the given coords from the current ones. | ||
func (coords *Coords) Remove(coordsToRemove ...Coord) { | ||
if len(*coords) == 0 || len(coordsToRemove) == 0 { | ||
return | ||
} | ||
var newCoords Coords | ||
for _, c := range *coords { | ||
var mustRemove bool | ||
for _, cToRemove := range coordsToRemove { | ||
if c.Equals(cToRemove) { | ||
mustRemove = true | ||
} | ||
} | ||
if !mustRemove { | ||
newCoords = append(newCoords, c) | ||
} | ||
} | ||
*coords = newCoords | ||
} | ||
|
||
// String satisfies stringer. | ||
func (coords Coords) String() string { | ||
chunks := make([]string, 0, len(coords)) | ||
for _, coord := range coords { | ||
chunks = append(chunks, coord.String()) | ||
} | ||
return strings.Join(chunks, " ") | ||
} | ||
|
||
func abs(in int) int { | ||
return int(math.Abs(float64(in))) | ||
} |
Oops, something went wrong.