diff --git a/README.md b/README.md index cfba237..140d093 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Battlesnake Board + ![CI Build Status](https://github.com/BattlesnakeOfficial/board/actions/workflows/ci.yml/badge.svg) ![Release Build Status](https://github.com/BattlesnakeOfficial/board/actions/workflows/release.yml/badge.svg) The board project is used to display Battlesnake games, both during live streams and competitions, as well as on [play.battlesnake.com](https://play.battlesnake.com/). It's built using React, HTML Canvas, and SVGs. @@ -11,7 +12,8 @@ This project follows most React conventions and tools described in the react doc This project requires Node 10.19. The dependencies may fail to install on newer versions. -Create a `.env.local` file at the root of the project to set the host that the local board URL serves from. `localhost` is the default but will not work with the CORS policy for snake part svg files. `127.0.0.1` is whitelisted for CORS. +Create a `.env.local` file at the root of the project to set the host that the local board URL serves from. `localhost` is the default but will not work with the CORS policy for snake part svg files. +`127.0.0.1:3000` is whitelisted for CORS. ```shell # File: /.env.local @@ -19,6 +21,7 @@ HOST=127.0.0.1 ``` ### Install & Run + ```shell # Installs dependencies from package-lock.json npm ci @@ -41,7 +44,8 @@ The game board requires a few parameters to work, including a `game` ID and an ` ## Running tests -React will run tests locally in watch mode. More info: https://create-react-app.dev/docs/running-tests/#command-line-interface +React will run tests locally in watch mode. More info: + ```shell npm test ``` @@ -49,6 +53,7 @@ npm test ## Board parameters #### Required + - `engine` - the Battlesnake engine to request frames from. - `game` - the id of the game to fetch frames for. @@ -57,6 +62,7 @@ http://127.0.0.1:3000/?engine=[ENGINE_URL]&game=[GAME_ID] ``` #### Optional + - `autoplay` - start game playback immediately. Values true / false. Defaults to false. - `boardTheme` - the theme of the board. Values dark / light. Defaults to light. - `frameRate` - the maximum frame rate used for playback. Takes an integer value equal to FPS. Defaults to 6 FPS. (medium speed) @@ -87,10 +93,10 @@ More info on setting up in popular editors here: [create-react-app.dev/docs/sett We use [Storybook.js](https://storybook.js.org/) to document and test the board components. -You can view and interact with board components here https://battlesnakeofficial.github.io/board/ +You can view and interact with board components here While developing a component you can run a local copy of storybook with the command `npm run storybook` to view and test it ## Feedback -* **Do you have an issue or suggestions for this repository?** Head over to our [Feedback Repository](https://play.battlesnake.com/feedback) today and let us know! +- **Do you have an issue or suggestions for this repository?** Head over to our [Feedback Repository](https://play.battlesnake.com/feedback) today and let us know! diff --git a/src/app/storage.js b/src/app/storage.js index a59ce6d..e77a1c9 100644 --- a/src/app/storage.js +++ b/src/app/storage.js @@ -46,6 +46,8 @@ export function rehydrateLocalSettings() { let checkAutoplay = getLocalSetting("autoplay"); let checkShowFrameScrubber = getLocalSetting("showFrameScrubber"); + let showCoordinateLabels = getLocalSetting("showCoordinateLabels"); + if (typeof checkAutoplay === "undefined") { checkAutoplay = initialSettings.autoplay; } else { @@ -58,10 +60,17 @@ export function rehydrateLocalSettings() { checkShowFrameScrubber = checkShowFrameScrubber === "true"; } + if (typeof showCoordinateLabels === "undefined") { + showCoordinateLabels = initialSettings.showCoordinateLabels; + } else { + showCoordinateLabels = showCoordinateLabels === "true"; + } + return { frameRate: Number(getLocalSetting("frameRate")) || DEFAULT_FRAMERATE, theme: getLocalSetting("theme") || initialSettings.theme, showFrameScrubber: checkShowFrameScrubber, - autoplay: checkAutoplay + autoplay: checkAutoplay, + showCoordinateLabels: showCoordinateLabels }; } diff --git a/src/components/board.jsx b/src/components/board.jsx index 9b8bee1..3bc4566 100644 --- a/src/components/board.jsx +++ b/src/components/board.jsx @@ -3,8 +3,16 @@ import Grid from "./grid"; const BOARD_SIZE = 100; +const LABEL_ADJUSTMENT = 10; + class Board extends React.Component { render() { + const boardSize = this.props.showCoordinateLabels + ? BOARD_SIZE - LABEL_ADJUSTMENT + : BOARD_SIZE; + + const x = this.props.showCoordinateLabels ? LABEL_ADJUSTMENT : 0; + return ( ); diff --git a/src/components/game.jsx b/src/components/game.jsx index 7688374..7d2e86d 100644 --- a/src/components/game.jsx +++ b/src/components/game.jsx @@ -150,6 +150,7 @@ class Game extends React.Component { highlightedSnake={this.props.highlightedSnake} theme={options.theme} turn={currentFrame.turn} + showCoordinateLabels={options.showCoordinateLabels} /> +
+
{label}
+
+ + ); + } + + renderLabels() { + return ( + <> + {range(this.props.rows).map((_, row) => this.renderLabel(row, -1, row))} + + {range(this.props.columns).map((_, col) => + this.renderLabel(-1, col, col) + )} + + ); + } + renderGrid() { // GRID_COLUMNS = this.props.columns; GRID_ROWS = this.props.rows; @@ -506,6 +543,8 @@ class Grid extends React.Component { const hazardOpacity = parseFloat(colors.hazardOpacity); + const overflow = this.props.showCoordinateLabels ? "visible" : "hidden"; + return ( + {this.props.showCoordinateLabels && this.renderLabels()} {range(this.props.rows).map((_, row) => range(this.props.columns).map((_, col) => ( )) )} - {sortedSnakes.map((snake, snakeIndex) => { return ( ); })} - {hazards.map((o, hazardIndex) => ( ))} - {food.map((f, foodIndex) => { if (this.props.foodImage) { return ( diff --git a/src/components/settings/SettingsPage.jsx b/src/components/settings/SettingsPage.jsx index 73ac7f9..0881ab7 100644 --- a/src/components/settings/SettingsPage.jsx +++ b/src/components/settings/SettingsPage.jsx @@ -8,7 +8,9 @@ import { currentShowFrameScrubber, frameRateUpdated, themeSelected, - showFrameScrubberUpdated + showFrameScrubberUpdated, + currentShowCoordinateLabels, + showCoordinateLabelsUpdated } from "./settings-slice"; import PlaybackSpeed from "./playback/PlaybackSpeed"; import styles from "./SettingsPage.module.css"; @@ -21,6 +23,7 @@ const SettingsPage = () => { const playbackSpeed = useSelector(currentFrameRate); const autoplay = useSelector(currentAutoplay); const showFrameScrubber = useSelector(currentShowFrameScrubber); + const showCoordinateLabels = useSelector(currentShowCoordinateLabels); const dispatch = useDispatch(); useEffect(() => { @@ -128,6 +131,34 @@ const SettingsPage = () => { +
+ Show Coordinate Labels [EXPERIMENTAL] +
+ Adds coordinate labels to the game board.   These go from 0 to + the width/height of the board to make it easier to debug games + + Let us know what you think! + +
+
+ +
+
diff --git a/src/components/settings/defaults.js b/src/components/settings/defaults.js index 5a5ebdf..4e4c799 100644 --- a/src/components/settings/defaults.js +++ b/src/components/settings/defaults.js @@ -7,5 +7,6 @@ export const initialSettings = { theme: themes.light, autoplay: false, showFrameScrubber: false, - persistAvailable: false + persistAvailable: false, + showCoordinateLabels: false }; diff --git a/src/components/settings/settings-slice.js b/src/components/settings/settings-slice.js index 0e222b1..d54e949 100644 --- a/src/components/settings/settings-slice.js +++ b/src/components/settings/settings-slice.js @@ -17,6 +17,9 @@ export const settingsSlice = createSlice({ }, showFrameScrubberUpdated(state, action) { state.showFrameScrubber = action.payload; + }, + showCoordinateLabelsUpdated(state, action) { + state.showCoordinateLabels = action.payload; } } }); @@ -26,7 +29,8 @@ export const { frameRateUpdated, themeSelected, autoPlayUpdated, - showFrameScrubberUpdated + showFrameScrubberUpdated, + showCoordinateLabelsUpdated } = settingsSlice.actions; // The function below is called a selector and allows us to select a value from @@ -37,6 +41,8 @@ export const currentTheme = state => state.settings.theme; export const currentAutoplay = state => state.settings.autoplay; export const currentShowFrameScrubber = state => state.settings.showFrameScrubber; +export const currentShowCoordinateLabels = state => + state.settings.showCoordinateLabels; export function settingsStoreListener(state) { if (state.settings.frameRate !== getLocalSetting("frameRate")) { @@ -56,6 +62,16 @@ export function settingsStoreListener(state) { ) { setLocalSetting("showFrameScrubber", state.settings.showFrameScrubber); } + + if ( + state.settings.showCoordinateLabels !== + getLocalSetting("showCoordinateLabels") + ) { + setLocalSetting( + "showCoordinateLabels", + state.settings.showCoordinateLabels + ); + } } export default settingsSlice.reducer;