Colored board (#59)

* New flag -c to use colors in the cli board view

* Error handeling

* Fix formatting

* Extracted ANSI escape codes into a different file

* Fixed formatting
This commit is contained in:
Ebbe Steenhoudt 2021-12-08 10:43:40 -08:00 committed by GitHub
parent 142a5a6ecf
commit ba855a8ab1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 77 additions and 11 deletions

14
cli/commands/colors.go Normal file
View file

@ -0,0 +1,14 @@
package commands
//ANSI escape codes to be used in the color output of the board view
const (
TERM_RESET = "\033[0m"
TERM_BG_GRAY = "\033[48;2;127;127;127m"
TERM_BG_WHITE = "\033[107m"
TERM_FG_GRAY = "\033[38;2;127;127;127m"
TERM_FG_LIGHTGRAY = "\033[38;2;200;200;200m"
TERM_FG_FOOD = "\033[38;2;255;92;117m"
TERM_FG_RGB = "\033[38;2;%d;%d;%dm"
)

View file

@ -46,6 +46,7 @@ var Timeout int32
var Sequential bool
var GameType string
var ViewMap bool
var UseColor bool
var Seed int64
var TurnDelay int32
var DebugRequests bool
@ -75,6 +76,7 @@ func init() {
playCmd.Flags().BoolVarP(&Sequential, "sequential", "s", false, "Use Sequential Processing")
playCmd.Flags().StringVarP(&GameType, "gametype", "g", "standard", "Type of Game Rules")
playCmd.Flags().BoolVarP(&ViewMap, "viewmap", "v", false, "View the Map Each Turn")
playCmd.Flags().BoolVarP(&UseColor, "color", "c", false, "Use color to draw the map")
playCmd.Flags().Int64VarP(&Seed, "seed", "r", time.Now().UTC().UnixNano(), "Random Seed")
playCmd.Flags().Int32VarP(&TurnDelay, "delay", "d", 0, "Turn Delay in Milliseconds")
playCmd.Flags().BoolVar(&DebugRequests, "debug-requests", false, "Log body of all requests sent")
@ -439,7 +441,7 @@ func convertStateToBoard(state *rules.BoardState, snakeStates map[string]SnakeSt
}
func buildSnakesFromOptions() map[string]SnakeState {
bodyChars := []rune{'■', '⌀', '●', '', '◘', '☺', '□', ''}
bodyChars := []rune{'■', '⌀', '●', '', '◘', '☺', '□', ''}
var numSnakes int
snakes := map[string]SnakeState{}
numNames := len(Names)
@ -519,37 +521,87 @@ func buildSnakesFromOptions() map[string]SnakeState {
return snakes
}
// Parses a color string like "#ef03d3" to rgb values from 0 to 255 or returns
// the default gray if any errors occure
func parseSnakeColor(color string) (int64, int64, int64) {
if len(color) == 7 {
red, err_r := strconv.ParseInt(color[1:3], 16, 64)
green, err_g := strconv.ParseInt(color[3:5], 16, 64)
blue, err_b := strconv.ParseInt(color[5:], 16, 64)
if err_r == nil && err_g == nil && err_b == nil {
return red, green, blue
}
}
// Default gray color from Battlesnake board
return 136, 136, 136
}
func printMap(state *rules.BoardState, snakeStates map[string]SnakeState, gameTurn int32) {
var o bytes.Buffer
o.WriteString(fmt.Sprintf("Ruleset: %s, Seed: %d, Turn: %v\n", GameType, Seed, gameTurn))
board := make([][]rune, state.Width)
board := make([][]string, state.Width)
for i := range board {
board[i] = make([]rune, state.Height)
board[i] = make([]string, state.Height)
}
for y := int32(0); y < state.Height; y++ {
for x := int32(0); x < state.Width; x++ {
board[x][y] = '◦'
if UseColor {
board[x][y] = TERM_FG_LIGHTGRAY + "□"
} else {
board[x][y] = "◦"
}
}
}
for _, oob := range state.Hazards {
board[oob.X][oob.Y] = '░'
if UseColor {
board[oob.X][oob.Y] = TERM_BG_GRAY + " " + TERM_BG_WHITE
} else {
board[oob.X][oob.Y] = "░"
}
}
if UseColor {
o.WriteString(fmt.Sprintf("Hazards "+TERM_BG_GRAY+" "+TERM_RESET+": %v\n", state.Hazards))
} else {
o.WriteString(fmt.Sprintf("Hazards ░: %v\n", state.Hazards))
for _, f := range state.Food {
board[f.X][f.Y] = '⚕'
}
for _, f := range state.Food {
if UseColor {
board[f.X][f.Y] = TERM_FG_FOOD + "●"
} else {
board[f.X][f.Y] = "⚕"
}
}
if UseColor {
o.WriteString(fmt.Sprintf("Food "+TERM_FG_FOOD+TERM_BG_WHITE+"●"+TERM_RESET+": %v\n", state.Food))
} else {
o.WriteString(fmt.Sprintf("Food ⚕: %v\n", state.Food))
}
for _, s := range state.Snakes {
red, green, blue := parseSnakeColor(snakeStates[s.ID].Color)
for _, b := range s.Body {
if b.X >= 0 && b.X < state.Width && b.Y >= 0 && b.Y < state.Height {
board[b.X][b.Y] = snakeStates[s.ID].Character
if UseColor {
board[b.X][b.Y] = fmt.Sprintf(TERM_FG_RGB+"■", red, green, blue)
} else {
board[b.X][b.Y] = string(snakeStates[s.ID].Character)
}
}
}
if UseColor {
o.WriteString(fmt.Sprintf("%v "+TERM_FG_RGB+TERM_BG_WHITE+"■■■"+TERM_RESET+": %v\n", snakeStates[s.ID].Name, red, green, blue, s))
} else {
o.WriteString(fmt.Sprintf("%v %c: %v\n", snakeStates[s.ID].Name, snakeStates[s.ID].Character, s))
}
}
for y := state.Height - 1; y >= 0; y-- {
if UseColor {
o.WriteString(TERM_BG_WHITE)
}
for x := int32(0); x < state.Width; x++ {
o.WriteRune(board[x][y])
o.WriteString(board[x][y])
}
if UseColor {
o.WriteString(TERM_RESET)
}
o.WriteString("\n")
}