-
Notifications
You must be signed in to change notification settings - Fork 1
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
1 parent
75cded2
commit 48b33f5
Showing
10 changed files
with
263 additions
and
52 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,11 @@ | ||
# gridspech-go | ||
|
||
`gridspech-go` is an implementation of the puzzle game [gridspech](https://krackocloud.itch.io/gridspech) written in Go. | ||
|
||
This implementation was written to make a solver, which the package can be found at [`gridspech-go/solve`](solve). The solver was written to find unintended solutions to puzzles, so don't use the solver until you have figured out the puzzle on your own :) | ||
|
||
## CLI Installation | ||
|
||
Binaries for common environments can be found on the [releases page](https://github.com/deanveloper/gridspech-go/releases). After this, you can add it to your PATH. | ||
|
||
To install the CLI from source, first make sure you have the [Go compiler](https://golang.org/dl/) installed. Then, run `go install -o gs-solve "github.com/deanveloper/gridspech-go/solve/cli@latest"`. The binary will appear in $GOBIN, which by default is `~/go/bin` |
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 |
---|---|---|
@@ -1,3 +1,5 @@ | ||
module github.com/deanveloper/gridspech-go | ||
|
||
go 1.16 | ||
|
||
require github.com/pborman/getopt/v2 v2.1.0 |
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 |
---|---|---|
@@ -1,2 +1,2 @@ | ||
github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66 h1:QnnoVdChKs+GeTvN4rPYTW6b5U6M3HMEvQ/+x4IGtfY= | ||
github.com/dustin/go-broadcast v0.0.0-20171205050544-f664265f5a66/go.mod h1:kTEh6M2J/mh7nsskr28alwLCXm/DSG5OSA/o31yy2XU= | ||
github.com/pborman/getopt/v2 v2.1.0 h1:eNfR+r+dWLdWmV8g5OlpyrTYHkhVNxHBdN2cCrJmOEA= | ||
github.com/pborman/getopt/v2 v2.1.0/go.mod h1:4NtW75ny4eBw9fO1bhtNdYTlZKYX5/tBLtsOpwKIKd0= |
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,132 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"log" | ||
"os" | ||
|
||
"github.com/deanveloper/gridspech-go" | ||
"github.com/deanveloper/gridspech-go/solve" | ||
"github.com/pborman/getopt/v2" | ||
) | ||
|
||
const ( | ||
outputString = "lines" | ||
outputJSON = "json" | ||
) | ||
|
||
var ( | ||
helpFlag = getopt.BoolLong("help", 'h', "display help") | ||
maxColors = getopt.IntLong("maxcolors", 'm', 0, "the total number of colors available for this level", "2") | ||
solveTiles = getopt.ListLong("tiles", 't', "solve specific tiles. a comma-separated list of space-separated coordinates") | ||
solveGoals = getopt.BoolLong("goals", 'g', "solve all goal tiles") | ||
solveCrowns = getopt.BoolLong("crowns", 'c', "solve all crown tiles") | ||
solveDots = getopt.BoolLong("dots", 'd', "solve all dot tiles") | ||
solveJoins = getopt.BoolLong("joins", 'j', "solve all join tiles") | ||
solveAll = getopt.BoolLong("all", 'a', "solve all tiles") | ||
jsonOutput = getopt.EnumLong("format", 'f', []string{outputString, outputJSON}, "", "output format (lines or json)") | ||
) | ||
|
||
func solutionsFromFlags(solver solve.GridSolver) <-chan gridspech.TileSet { | ||
var ch <-chan gridspech.TileSet | ||
if *solveAll { | ||
ch = solver.SolveAllTiles() | ||
} else { | ||
{ | ||
tempCh := make(chan gridspech.TileSet, 1) | ||
tempCh <- gridspech.NewTileSet() | ||
close(tempCh) | ||
ch = tempCh | ||
} | ||
|
||
if getopt.IsSet('t') { | ||
tiles := parseCoords(*solveTiles) | ||
ch = solve.MergeSolutionsIters(ch, solver.SolveTiles(tiles...)) | ||
} | ||
if *solveGoals { | ||
ch = solve.MergeSolutionsIters(ch, solver.SolveGoals()) | ||
} | ||
if *solveJoins { | ||
ch = solve.MergeSolutionsIters(ch, solver.SolveGoals()) | ||
} | ||
if *solveDots { | ||
ch = solve.MergeSolutionsIters(ch, solver.SolveGoals()) | ||
} | ||
if *solveCrowns { | ||
ch = solve.MergeSolutionsIters(ch, solver.SolveGoals()) | ||
} | ||
} | ||
return ch | ||
} | ||
|
||
func parseCoords(coordsStr []string) []gridspech.TileCoord { | ||
var coords []gridspech.TileCoord | ||
for _, coordStr := range coordsStr { | ||
var x, y int | ||
n, err := fmt.Sscanf(coordStr, "%d %d", &x, &y) | ||
if err != nil || n != 2 { | ||
log.Printf("skipping invalid coord %s\n", coordStr) | ||
continue | ||
} | ||
coords = append(coords, gridspech.TileCoord{X: x, Y: y}) | ||
} | ||
return coords | ||
} | ||
|
||
func main() { | ||
getopt.HelpColumn = 22 | ||
getopt.SetUsage(func() { | ||
fmt.Fprintf( | ||
os.Stderr, "Usage: %v %v\n", | ||
getopt.CommandLine.Program(), | ||
getopt.CommandLine.UsageLine(), | ||
) | ||
fmt.Fprintln(os.Stderr, "Standard input will be interpreted as the level to solve.") | ||
getopt.CommandLine.PrintOptions(os.Stderr) | ||
}) | ||
getopt.Parse() | ||
if !getopt.IsSet('a') && !getopt.IsSet('t') && !getopt.IsSet('g') && !getopt.IsSet('c') && !getopt.IsSet('d') && !getopt.IsSet('j') { | ||
getopt.Usage() | ||
return | ||
} | ||
if *helpFlag { | ||
getopt.Usage() | ||
return | ||
} | ||
|
||
const maxLevelLen, bufLen = 10000, 100 | ||
var buf [bufLen]byte | ||
var levelBytes []byte | ||
|
||
for i := 0; i < maxLevelLen/bufLen; i++ { | ||
n, err := os.Stdin.Read(buf[:]) | ||
if err != nil { | ||
if err != io.EOF { | ||
log.Fatalln("error:", err) | ||
} else { | ||
break | ||
} | ||
} | ||
levelBytes = append(levelBytes, buf[:n]...) | ||
} | ||
if len(levelBytes) == maxLevelLen { | ||
log.Fatalln("standard input is over 10000 bytes... are you sure it is a gridspech level?") | ||
} | ||
|
||
level := string(levelBytes) | ||
solver := solve.NewGridSolver(gridspech.MakeGridFromString(level, *maxColors)) | ||
|
||
solutions := solutionsFromFlags(solver) | ||
|
||
first := true | ||
for solution := range solutions { | ||
if !first { | ||
fmt.Println() | ||
} | ||
|
||
newGrid := solver.Grid.Clone() | ||
newGrid.ApplyTileSet(solution) | ||
fmt.Println(newGrid) | ||
} | ||
} |
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
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
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
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,89 @@ | ||
package solve | ||
|
||
import ( | ||
"fmt" | ||
|
||
gs "github.com/deanveloper/gridspech-go" | ||
) | ||
|
||
// SolveAllTiles returns a channel which will return a TileSet of all tiles in g. | ||
func (g GridSolver) SolveAllTiles() <-chan gs.TileSet { | ||
solutionIter := make(chan gs.TileSet) | ||
|
||
go func() { | ||
defer close(solutionIter) | ||
|
||
for goalsAndDots := range MergeSolutionsIters(g.SolveGoals(), g.SolveDots()) { | ||
newGrid := g.Clone() | ||
newGrid.Grid.ApplyTileSet(goalsAndDots) | ||
newGrid.UnknownTiles.RemoveAll(goalsAndDots.ToTileCoordSet()) | ||
|
||
for joinsSolution := range newGrid.SolveJoins() { | ||
joinsSolved := newGrid.Clone() | ||
joinsSolved.Grid.ApplyTileSet(joinsSolution) | ||
joinsSolved.UnknownTiles.RemoveAll(joinsSolution.ToTileCoordSet()) | ||
|
||
for crownsSolution := range joinsSolved.SolveCrowns() { | ||
crownsSolved := joinsSolved.Clone() | ||
crownsSolved.Grid.ApplyTileSet(crownsSolution) | ||
crownsSolved.UnknownTiles.RemoveAll(crownsSolution.ToTileCoordSet()) | ||
|
||
if crownsSolved.Grid.Valid() { | ||
var merged gs.TileSet | ||
merged.Merge(goalsAndDots) | ||
merged.Merge(joinsSolution) | ||
merged.Merge(crownsSolution) | ||
solutionIter <- merged | ||
} | ||
} | ||
} | ||
} | ||
}() | ||
|
||
return solutionIter | ||
} | ||
|
||
// SolveTiles returns a channel of possible solutions for the given tiles. | ||
func (g GridSolver) SolveTiles(tiles ...gs.TileCoord) <-chan gs.TileSet { | ||
|
||
if len(tiles) == 0 { | ||
ch := make(chan gs.TileSet, 1) | ||
ch <- gs.NewTileSet() | ||
close(ch) | ||
return ch | ||
} | ||
|
||
tilesToSolutions := make([]<-chan gs.TileSet, len(tiles)) | ||
for i, tile := range tiles { | ||
tilesToSolutions[i] = g.solveTile(*g.Grid.TileAtCoord(tile)) | ||
} | ||
|
||
// now merge them all together | ||
for i := 1; i < len(tiles); i++ { | ||
mergedIter := MergeSolutionsIters(tilesToSolutions[i-1], tilesToSolutions[i]) | ||
uniqueIter := filterUnique(mergedIter) | ||
tilesToSolutions[i] = uniqueIter | ||
} | ||
|
||
return tilesToSolutions[len(tilesToSolutions)-1] | ||
} | ||
|
||
func (g GridSolver) solveTile(t gs.Tile) <-chan gs.TileSet { | ||
switch t.Data.Type { | ||
case gs.TypeHole, gs.TypeBlank: | ||
ch := make(chan gs.TileSet, 1) | ||
ch <- gs.NewTileSet() | ||
close(ch) | ||
return ch | ||
case gs.TypeGoal: | ||
return filterHasTile(g.SolveGoals(), t.Coord) | ||
case gs.TypeCrown: | ||
return g.SolveCrown(t.Coord) | ||
case gs.TypeDot1, gs.TypeDot2, gs.TypeDot3: | ||
return g.SolveDot(t) | ||
case gs.TypeJoin1, gs.TypeJoin2: | ||
return g.SolveJoin(t) | ||
default: | ||
panic(fmt.Sprintf("invalid type %v", t.Data.Type)) | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
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