add 'map' cli command to provide map information (#100)

* add 'map' cli command

- provides the following map information functions:
  - list all available maps in the global registry
  - display map metadata

- update docs with map command examples

* add list and info subcommands to map cli command

* rename map command list and info factory functions

* add --all flag to map info subcommand

* handle cmd.Help error
This commit is contained in:
Blayne Campbell 2022-08-09 16:06:28 -06:00 committed by GitHub
parent 91106aec09
commit ffeb401377
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 177 additions and 0 deletions

View file

@ -78,6 +78,25 @@ Example creating a 7x7 Standard game with two Battlesnakes:
battlesnake play --width 7 --height 7 --name Snake1 --url http://snake1-url-whatever --name Snake2 --url http://snake2-url-whatever
```
### Maps
The `map` command provides map information for use with the `play` command.
List all available maps using the `list` subcommand:
```
battlesnake map list
```
Display map information using the `info` subcommand:
```
battlesnake map info standard
Name: Standard
Author: Battlesnake
Description: Standard snake placement and food spawning
Version: 2
Min Players: 1
Max Players: 16
Board Sizes (WxH): 7x7 9x9 11x11 13x13 15x15 17x17 19x19 21x21 23x23 25x25
```
### Sample Output
```
$ battlesnake play --width 3 --height 3 --url http://redacted:4567/ --url http://redacted:4568/ --name Bob --name Sue

78
cli/commands/info.go Normal file
View file

@ -0,0 +1,78 @@
package commands
import (
"fmt"
"log"
"github.com/BattlesnakeOfficial/rules/maps"
"github.com/spf13/cobra"
)
type mapInfo struct {
All bool
}
func NewMapInfoCommand() *cobra.Command {
info := mapInfo{}
var infoCmd = &cobra.Command{
Use: "info [flags] map_name [...map_name]",
Short: "Display metadata for given map(s)",
Long: "Display metadata for given map(s)",
Run: func(cmd *cobra.Command, args []string) {
// handle --all flag first as there would be no args
if info.All {
mapList := maps.List()
for i, m := range mapList {
info.display(m)
if i < (len(mapList) - 1) {
fmt.Print("\n")
}
}
return
}
// display help when no map(s) provided via args
if len(args) < 1 {
err := cmd.Help()
if err != nil {
log.Fatal(err)
}
return
}
// display all maps via command args
for i, m := range args {
info.display(m)
if i < (len(args) - 1) {
fmt.Print("\n")
}
}
},
}
infoCmd.Flags().BoolVarP(&info.All, "all", "a", false, "Display information for all maps")
return infoCmd
}
func (m *mapInfo) display(id string) {
gameMap, err := maps.GetMap(id)
if err != nil {
log.Fatalf("Failed to load game map %#v: %v", id, err)
}
meta := gameMap.Meta()
fmt.Println("Name:", meta.Name)
fmt.Println("Author:", meta.Author)
fmt.Println("Description:", meta.Description)
fmt.Println("Version:", meta.Version)
fmt.Println("Min Players:", meta.MinPlayers)
fmt.Println("Max Players:", meta.MaxPlayers)
fmt.Print("Board Sizes (WxH):")
for i, s := range meta.BoardSizes {
fmt.Printf(" %dx%d", s.Width, s.Height)
if i == (len(meta.BoardSizes) - 1) {
fmt.Print("\n")
}
}
}

22
cli/commands/list.go Normal file
View file

@ -0,0 +1,22 @@
package commands
import (
"fmt"
"github.com/BattlesnakeOfficial/rules/maps"
"github.com/spf13/cobra"
)
func NewMapListCommand() *cobra.Command {
var listCmd = &cobra.Command{
Use: "list",
Short: "List available game maps",
Long: "List available game maps",
Run: func(cmd *cobra.Command, args []string) {
for _, m := range maps.List() {
fmt.Println(m)
}
},
}
return listCmd
}

24
cli/commands/map.go Normal file
View file

@ -0,0 +1,24 @@
package commands
import (
"log"
"github.com/spf13/cobra"
)
func NewMapCommand() *cobra.Command {
var mapCmd = &cobra.Command{
Use: "map",
Short: "Display map information",
Long: "Display map information",
Run: func(cmd *cobra.Command, args []string) {
err := cmd.Help()
if err != nil {
log.Fatal(err)
}
},
}
return mapCmd
}

View file

@ -21,6 +21,12 @@ var rootCmd = &cobra.Command{
func Execute() {
rootCmd.AddCommand(NewPlayCommand())
mapCommand := NewMapCommand()
mapCommand.AddCommand(NewMapListCommand())
mapCommand.AddCommand(NewMapInfoCommand())
rootCmd.AddCommand(mapCommand)
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)

View file

@ -2,6 +2,7 @@ package maps
import (
"fmt"
"sort"
"github.com/BattlesnakeOfficial/rules"
)
@ -21,6 +22,16 @@ func (registry MapRegistry) RegisterMap(id string, m GameMap) {
registry[id] = m
}
// List returns all registered map IDs in alphabetical order
func (registry MapRegistry) List() []string {
var keys []string
for k, _ := range registry {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}
// GetMap returns the map associated with the given ID.
func (registry MapRegistry) GetMap(id string) (GameMap, error) {
if m, ok := registry[id]; ok {
@ -34,6 +45,11 @@ func GetMap(id string) (GameMap, error) {
return globalRegistry.GetMap(id)
}
// List returns a list of maps registered to the global registry.
func List() []string {
return globalRegistry.List()
}
// RegisterMap adds a map to the global registry.
func RegisterMap(id string, m GameMap) {
globalRegistry.RegisterMap(id, m)

View file

@ -112,3 +112,15 @@ func pickSize(meta Metadata) Dimensions {
// For fixed, just pick the first supported size
return meta.BoardSizes[0]
}
func TestListRegisteredMaps(t *testing.T) {
keys := globalRegistry.List()
mapCount := 0
for k := range globalRegistry {
// every registry key should exist in List results
require.Contains(t, keys, k)
mapCount++
}
// List should equal number of maps in the global registry
require.Equal(t, len(keys), mapCount)
}