diff --git a/package-lock.json b/package-lock.json index 4e3de16..a3871c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -863,6 +863,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -879,6 +880,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=6.0.0" } @@ -890,6 +892,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "engines": { "node": ">=6.0.0" } @@ -901,6 +904,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" @@ -912,7 +916,8 @@ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", "dev": true, "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", @@ -921,6 +926,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -2053,7 +2059,6 @@ "integrity": "sha512-B2MdzyWxCE2+SqiZHAjPphft+/2x2FlO9YBx7eKE1BCb+rqBlQdhtAEhzIEdozHd55DXPmxBdpMygFJjfjjA9A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.32.0", "@typescript-eslint/types": "8.32.0", @@ -2239,7 +2244,6 @@ "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2639,7 +2643,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.11.23", "caniuse-lite": "^1.0.30001810", @@ -2695,7 +2698,8 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/builtin-modules": { "version": "5.0.0", @@ -3997,7 +4001,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -6348,7 +6351,6 @@ "integrity": "sha512-ek8NRg/OPvS9ISOJNWNAz5vZcpYacWNFDWNJjj5OXsc6YuKacfey6wF04cXz/tOJIVrZ2nGSkHpAY5qKtF6ISg==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "d": "^1.0.2", "duration": "^0.2.2", @@ -8327,6 +8329,7 @@ "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -8339,6 +8342,7 @@ "dev": true, "license": "BSD-3-Clause", "optional": true, + "peer": true, "engines": { "node": ">=0.10.0" } @@ -8743,6 +8747,7 @@ "dev": true, "license": "BSD-2-Clause", "optional": true, + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -8762,7 +8767,8 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true, "license": "MIT", - "optional": true + "optional": true, + "peer": true }, "node_modules/through": { "version": "2.3.8", @@ -8836,7 +8842,6 @@ "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8969,7 +8974,6 @@ "integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" @@ -9111,7 +9115,6 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9424,7 +9427,6 @@ "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, diff --git a/src/boards/bricka.ts b/src/boards/bricka.ts new file mode 100644 index 0000000..cd3c0ee --- /dev/null +++ b/src/boards/bricka.ts @@ -0,0 +1,330 @@ +import { IPolyPolygon, rectOfRects } from "../grids/index.js"; +import { IPoint} from "../grids/_base.js"; +import { RendererBase } from "../renderers/_base.js"; +import { Graph, SquareOrthGraph } from "../graphs/index.js"; +import { BoardReturn, CompassDirection, createGridlineLayers, getBoardFill } from "./index.js"; +import { + hiddenRectLabelSides, + resolveRectEdgeLabelStrings, + shouldDrawRectLabelSide, + type RectEdgeLabelsOverride, +} from "./rectEdgeLabels.js"; +import { BoardBasic } from "../schemas/schema.js"; + +export const bricka = (ctx: RendererBase): BoardReturn => { + if ( (ctx.json === undefined) || (ctx.rootSvg === undefined) || (ctx.json.board === null) ) { + throw new Error("Object in an invalid state!"); + } + + // Hard-code size to the only official board size + const width: number = 13; + const height: number = 13; + const cellsize = ctx.cellsize; + + let baseStroke = 1; + let baseColour = ctx.options.colourContext.strokes; + let baseOpacity = 1; + if ( ("strokeWeight" in ctx.json.board) && (ctx.json.board.strokeWeight !== undefined) ) { + baseStroke = ctx.json.board.strokeWeight; + } + if ( ("strokeColour" in ctx.json.board) && (ctx.json.board.strokeColour !== undefined) ) { + baseColour = ctx.resolveColour(ctx.json.board.strokeColour) as string; + } + if ( ("strokeOpacity" in ctx.json.board) && (ctx.json.board.strokeOpacity !== undefined) ) { + baseOpacity = ctx.json.board.strokeOpacity; + } + + // Get a grid of points + const grid = rectOfRects({gridHeight: height, gridWidth: width, cellSize: cellsize}); + + // start generating the SVG + const board = ctx.rootSvg.group().id("board"); + const layers = createGridlineLayers(board); + const gridlines = layers.root; + + // Add board labels + let labelColour = ctx.options.colourContext.labels; + if ( ("labelColour" in ctx.json.board) && (ctx.json.board.labelColour !== undefined) ) { + labelColour = ctx.resolveColour(ctx.json.board.labelColour) as string; + } + let labelOpacity = 1; + if ( ("labelOpacity" in ctx.json.board) && (ctx.json.board.labelOpacity !== undefined) ) { + labelOpacity = ctx.json.board.labelOpacity; + } + + if ( (! ctx.json.options) || (! ctx.json.options.includes("hide-labels") ) ) { + const hiddenSides = hiddenRectLabelSides(ctx.json.options); + const labels = board.group().id("labels"); + let customLabels: string[]|undefined; + if ( ("columnLabels" in ctx.json.board) && (ctx.json.board.columnLabels !== undefined) ) { + customLabels = ctx.json.board.columnLabels; + } + let edgeLabels: RectEdgeLabelsOverride | undefined; + if ( ("edgeLabels" in ctx.json.board) && (ctx.json.board.edgeLabels !== undefined) ) { + edgeLabels = ctx.json.board.edgeLabels as RectEdgeLabelsOverride; + } + const edge = resolveRectEdgeLabelStrings(ctx, { + width, + height, + columnLabels: customLabels, + rowLabels: (ctx.json.board as BoardBasic).rowLabels, + edgeLabels, + options: ctx.json.options, + }); + + // Columns (letters) + for (let col = 0; col < width; col++) { + const pointTop = {x: grid[0][col].x, y: grid[0][col].y - (cellsize)}; + const pointBottom = {x: grid[height - 1][col].x, y: grid[height - 1][col].y + (cellsize)}; + if (shouldDrawRectLabelSide(hiddenSides, "north")) { + ctx.applyCoordinateLabelStyle(labels.text(edge.north[col]), labelColour, labelOpacity).center(pointTop.x, pointTop.y); + } + if (shouldDrawRectLabelSide(hiddenSides, "south")) { + ctx.applyCoordinateLabelStyle(labels.text(edge.south[col]), labelColour, labelOpacity).center(pointBottom.x, pointBottom.y); + } + } + + // Rows (numbers) + for (let row = 0; row < height; row++) { + const pointL = {x: grid[row][0].x - (cellsize), y: grid[row][0].y}; + const pointR = {x: grid[row][width - 1].x + (cellsize), y: grid[row][width - 1].y}; + if (shouldDrawRectLabelSide(hiddenSides, "west")) { + ctx.applyCoordinateLabelStyle(labels.text(edge.west[row]), labelColour, labelOpacity).center(pointL.x, pointL.y); + } + if (shouldDrawRectLabelSide(hiddenSides, "east")) { + ctx.applyCoordinateLabelStyle(labels.text(edge.east[row]), labelColour, labelOpacity).center(pointR.x, pointR.y); + } + } + } + + + // Start from a square graph, drop all nodes + // we don't need, and define the needed diagonal edges + const graph: Graph = new SquareOrthGraph(width, height); + + function dropIfHas(x: number, y: number) { + if(graph.hasNode([x, y])) { + graph.dropNode([x, y]); + } + } + + function dropSymm(x: number, y: number) { + /* Drop nodes with 4-way symmetry */ + dropIfHas(x, y); + dropIfHas(width - x - 1, y); + dropIfHas(x, height - y - 1); + dropIfHas(width - x - 1, height - y - 1); + } + + function addEdge(x1: number, y1: number, x2: number, y2: number) { + const nodeA = [x1, y1].join(","); + const nodeB = [x2, y2].join(","); + if (!graph.graph.hasEdge(nodeA, nodeB)) { + graph.graph.addEdge(nodeA, nodeB); + } + } + + function addEdgeSymm(x1: number, y1: number, x2: number, y2: number) { + /* Add edges with 4-way symmetry */ + addEdge(x1, y1, x2, y2); + addEdge(width - x1 - 1, y1, width - x2 - 1, y2); + addEdge(x1, height - y1 - 1, x2, height - y2 - 1); + addEdge(width - x1 - 1, height - y1 - 1, width - x2 - 1, height - y2 - 1); + } + + // Remove the corners + const cornerDrops = [5, 4, 2, 1, 1, 1]; + cornerDrops.forEach((maxY, x) => { + for (let y = 0; y < maxY; y++) { + dropSymm(x, y); + } + }); + + // Remove centers of the polygons + const polyDrops = [ + [1, 6], + [2, 5], + [3, 3], + [3, 6], + [4, 2], + [4, 4], + [5, 3], + [5, 6], + [6, 1], + [6, 5] + ] + polyDrops.forEach(([x, y]) => { + dropSymm(x, y); + }); + + // Remove nodes on the midpoint of long horizontal connections + const midDrops = [ + [0, 6], + [2, 3], + [4, 6], + [6, 3] + ] + midDrops.forEach(([x, y]) => { + dropSymm(x, y); + addEdgeSymm(x, y - 1, x, y + 1); + }); + + // Vertical: + const vDrops = [ + [4, 1] + ] + vDrops.forEach(([x, y]) => { + dropSymm(x, y); + addEdgeSymm(x - 1, y, x + 1, y); + }); + + // Add diagonals + const diags = [ + [2, 6, 1, 5], + [2, 6, 3, 5], + [6, 6, 5, 5], + [4, 3, 3, 2], + [4, 3, 3, 4], + [4, 3, 5, 2], + [4, 3, 5, 4], + [6, 0, 5, 1] + ] + diags.forEach(([x1, y1, x2, y2]) => { + addEdgeSymm(x1, y1, x2, y2); + }); + + // derive the board edge polygon, this will be used for boardFill and in the + // renderer _base.ts for the edge markings. Going clockwise starting from the + // leftmost point. + + const leftpt = [6, 0] + const NW = [ + [5, 1], + [3, 1], + [3, 2], + [2, 2], + [2, 4], + [1, 4], + [1, 5], + [0, 5] + ] + let NE = [...NW] + NE.reverse(); + NE = NE.map(([x, y]) => ([x, height - y - 1])); + const rightpt = [6, 12] + let SE = [...NE] + SE.reverse(); + SE = SE.map(([x, y]) => ([width - x - 1, y])); + let SW = [...NW] + SW.reverse(); + SW = SW.map(([x, y]) => ([width - x - 1, y])); + + const boardEdges: Map = new Map([ + ["W", [leftpt]], + ["NW", NW], + ["NE", NE], + ["E", [rightpt]], + ["SE", SE], + ["SW", SW] + ].map(([k, pts]) => ([(k as CompassDirection), {type: "poly", points: (pts as number[][]).map(([x, y]) => (grid[x][y]))}]))) + + // Polygons are indexed by their center coordinate, there will be empty spots + // in the array. + const polys: IPolyPolygon[][] = []; + for (let i = 0; i < 13; i++) { + polys[i] = []; + } + const polyCenters = [ + [1, 6], + [2, 5], + [3, 3], + [3, 6], + [4, 2], + [4, 4], + [5, 3], + [5, 6], + [6, 1], + [6, 5] + ] + polyCenters.push(...polyCenters.map(([x, y]) => ([width - x - 1, y]))); + polyCenters.push(...polyCenters.map(([x, y]) => ([x, height - y - 1]))); + const offsets = [ + [-1, -1], + [-1, 0], + [-1, 1], + [0, 1], + [1, 1], + [1, 0], + [1, -1], + [0, -1] + ] + + polyCenters.forEach(([x, y]) => { + if(!(y in polys[x])) { + const pts: IPoint[] = []; + offsets.forEach(([dx, dy]) => { + if(graph.hasNode([x + dx, y + dy])) { + pts.push(grid[x + dx][y + dy]); + } + }); + polys[x][y] = { + type: "poly", + points: pts + }; + } + }); + + /* Everything generated, now do the actual drawing */ + + // Do boardfill before the markers + const [hexFill, hexOpacity] = getBoardFill(ctx, "white"); + layers.fill.polygon([...boardEdges.values()].map((poly) => (poly.points)).flat().map((ipt) => ([ipt.x, ipt.y])).flat()).fill(hexFill!).opacity(hexOpacity); + + ctx.markBoard({svgGroup: gridlines, preGridLines: true, grid, polys: polys, boardEdges: boardEdges}); + + + // Draw the edges based on the graph + graph.graph.forEachEdge( + (_edge, _attributes, source, target) => { + const [cx1, cy1]: number[] = source.split(",").map(s => parseInt(s, 10)); + const [cx2, cy2]: number[] = target.split(",").map(s => parseInt(s, 10)); + const x1: number = grid[cx1][cy1].x; + const y1: number = grid[cx1][cy1].y; + const x2: number = grid[cx2][cy2].x; + const y2: number = grid[cx2][cy2].y; + layers.strokes.line(x1, y1, x2, y2).stroke({width: baseStroke, color: baseColour, opacity: baseOpacity, linecap: "round", linejoin: "round"}).attr({ 'pointer-events': 'none' }); + } + ); + + // Draw dots for each cell + graph.graph.forEachNode( + (node) => { + const [x, y]: number[] = node.split(",").map(s => parseInt(s, 10)); + const pt = grid[x][y]; + layers.strokes.circle(baseStroke * 7.5) + .attr({ 'pointer-events': 'none' }) + .fill(baseColour) + .opacity(baseOpacity) + .stroke({width: 0}) + .center(pt.x, pt.y); + } + ); + + if (ctx.options.boardClick !== undefined) { + const tiles = board.group().id("tiles"); + const tile = ctx.rootSvg.defs().rect(ctx.cellsize, ctx.cellsize).fill(ctx.options.colourContext.background).opacity(0).id("_clickCatcher"); + + graph.graph.forEachNode( + (node) => { + const [row, col]: number[] = node.split(",").map(s => parseInt(s, 10)); + const {x, y} = grid[row][col]; + const t = tiles.use(tile).dmove(x - (cellsize / 2), y - (cellsize / 2)); + t.click(() => ctx.options.boardClick!(row, col, "")); + } + ); + } + + ctx.markBoard({svgGroup: gridlines, preGridLines: false, grid, polys: polys, boardEdges: boardEdges}); + + return {grid, polys}; +} diff --git a/src/boards/index.ts b/src/boards/index.ts index 26c6d93..ec79c15 100644 --- a/src/boards/index.ts +++ b/src/boards/index.ts @@ -20,6 +20,7 @@ export { hexSlanted } from "./hexSlanted.js"; export { fracturedFlat } from "./fracturedFlat.js"; export { moon } from "./moon.js"; export { onyx } from "./onyx.js"; +export { bricka } from "./bricka.js"; export { pentagonal } from "./pentagonal.js"; export { bentTri } from "./bentTri.js"; export { star } from "./star.js"; diff --git a/src/renderers/_base.ts b/src/renderers/_base.ts index 57899bb..3aaa504 100644 --- a/src/renderers/_base.ts +++ b/src/renderers/_base.ts @@ -66,7 +66,7 @@ import { } from "../common/buttonBar.js"; import { attachMarkerPulse } from "../common/markerPulse.js"; import { CompassDirection, edges2corners, getBoardFill, BoardReturn } from "../boards/index.js"; -import { cairoCatalan, cairoCollinear, cobweb, conhex, conicalHex, dvgc, eleven, fracturedFlat, hexOfCir, hexOfHex, hexOfTri, hexOfTriF, hexSlanted, moon, onyx, pentagonal, bentTri, star, pyramidHex, rectOfHex, rectOfTri, snubSquare, snubSquareCells, sowing, squares, squaresDiamonds, squaresStacked, stackingTriangles, vertex, wheel } from "../boards/index.js"; +import { cairoCatalan, cairoCollinear, cobweb, conhex, conicalHex, dvgc, eleven, fracturedFlat, hexOfCir, hexOfHex, hexOfTri, hexOfTriF, hexSlanted, moon, onyx, bricka, pentagonal, bentTri, star, pyramidHex, rectOfHex, rectOfTri, snubSquare, snubSquareCells, sowing, squares, squaresDiamonds, squaresStacked, stackingTriangles, vertex, wheel } from "../boards/index.js"; import { glyphForLayoutRotation, glyphKeepsUpright, @@ -281,6 +281,7 @@ export interface IMarkBoardOptions { hexWidth?: number; hexHeight?: number; polys?: Poly[][]; + boardEdges?: Map; cairoStart?: "H"|"V"; } @@ -2260,6 +2261,7 @@ export abstract class RendererBase { if (opts.polys !== undefined) { polys = opts.polys; } + const boardEdges: Map | undefined = opts.boardEdges; if ( (this.json === undefined) || (this.rootSvg === undefined) ) { throw new Error("Object in an invalid state!"); @@ -3340,6 +3342,21 @@ export abstract class RendererBase { pts.push(...realgrid.map(row => row[row.length - 1])) } targetGroup.polyline(pts.map(pt => `${pt.x},${pt.y}`).join(" ")).addClass(`aprender-marker-${x2uid(cloned)}`).stroke({width: baseStroke * 3, color: colour, opacity, linecap: "round", linejoin: "round"}).fill("none"); + } else if (style === "bricka" && boardEdges !== undefined) { + if (boardEdges.has(marker.edge)) { + const pts: IPoint[] = boardEdges.get(marker.edge)!.points; + targetGroup.polyline(pts.map(pt => `${pt.x},${pt.y}`).join(" ")).addClass(`aprender-marker-${x2uid(cloned)}`).stroke({width: baseStroke * 3, color: colour, opacity, linecap: "round", linejoin: "round"}).fill("none"); + pts.forEach( + (pt) => { + targetGroup.circle(baseStroke * 10) + .attr({ 'pointer-events': 'none' }) + .fill(colour) + .opacity(opacity) + .stroke({width: 0}) + .center(pt.x, pt.y); + } + ); + } } } else if (marker.type === "fence") { let colour = this.options.colourContext.strokes; @@ -3637,6 +3654,9 @@ export abstract class RendererBase { case "onyx": ({ grid: gridPoints, boardFill } = onyx(this)); break; + case "bricka": + ({ grid: gridPoints, polys, boardFill } = bricka(this)); + break; case "snubsquare-cells": ({ grid: gridPoints, polys, boardFill } = snubSquareCells(this)); break; diff --git a/src/renderers/stackingOffset.ts b/src/renderers/stackingOffset.ts index e702d17..cb66609 100644 --- a/src/renderers/stackingOffset.ts +++ b/src/renderers/stackingOffset.ts @@ -3,7 +3,7 @@ import { GridPoints, IPoint, Poly } from "../grids/_base.js"; import { APRenderRep } from "../schemas/schema.js"; import { IRendererOptionsIn, RendererBase } from "./_base.js"; import { centroid, projectPoint, rotatePoint, usePieceAt } from "../common/plotting.js"; -import { cairoCatalan, cairoCollinear, cobweb, conhex, conicalHex, fracturedFlat, hexOfCir, hexOfHex, hexOfTri, hexOfTriF, hexSlanted, moon, onyx, pentagonal, bentTri, star, pyramidHex, rectOfHex, rectOfTri, snubSquare, snubSquareCells, sowingRound, squares, squaresDiamonds, vertex, wheel } from "../boards/index.js"; +import { cairoCatalan, cairoCollinear, cobweb, conhex, conicalHex, fracturedFlat, hexOfCir, hexOfHex, hexOfTri, hexOfTriF, hexSlanted, moon, onyx, bricka, pentagonal, bentTri, star, pyramidHex, rectOfHex, rectOfTri, snubSquare, snubSquareCells, sowingRound, squares, squaresDiamonds, vertex, wheel } from "../boards/index.js"; /** * The `stacking-offset` renderer creates stacks of pieces by offsetting them slightly to give a 3D look. @@ -61,6 +61,9 @@ export class StackingOffsetRenderer extends RendererBase { case "onyx": ({ grid: gridPoints, boardFill } = onyx(this)); break; + case "bricka": + ({ grid: gridPoints, polys, boardFill } = bricka(this)); + break; case "snubsquare-cells": ({ grid: gridPoints, polys, boardFill } = snubSquareCells(this)); break; diff --git a/src/schemas/schema.d.ts b/src/schemas/schema.d.ts index 569db4b..4794220 100644 --- a/src/schemas/schema.d.ts +++ b/src/schemas/schema.d.ts @@ -283,6 +283,7 @@ export type BoardStyles = | "snubsquare" | "snubsquare-cells" | "onyx" + | "bricka" | "pentagonal" | "pentagonal-bluestone" | "bent-tri" diff --git a/src/schemas/schema.json b/src/schemas/schema.json index a8052ad..bdc0626 100644 --- a/src/schemas/schema.json +++ b/src/schemas/schema.json @@ -394,6 +394,7 @@ "snubsquare", "snubsquare-cells", "onyx", + "bricka", "pentagonal", "pentagonal-bluestone", "bent-tri", diff --git a/test/fixtures/playground-samples.json b/test/fixtures/playground-samples.json index a1a2ba7..bcb228a 100644 --- a/test/fixtures/playground-samples.json +++ b/test/fixtures/playground-samples.json @@ -99,6 +99,11 @@ "description": "A version of a snubsquare board where you place pieces on the spaces instead of the vertices. The height and width you provide relate to the number of regular vertices. Because at many sizes the number of spaces varies from row to row, the labelling mirrors the `hex-of-*` model.", "render": "{\"board\":{\"style\":\"snubsquare-cells\",\"width\":8,\"height\":8,\"markers\":[{\"type\":\"flood\",\"colour\":2,\"points\":[{\"row\":2,\"col\":3},{\"row\":2,\"col\":2},{\"row\":2,\"col\":4}]},{\"type\":\"edge\",\"edge\":\"N\",\"colour\":1},{\"type\":\"edge\",\"edge\":\"S\",\"colour\":1},{\"type\":\"edge\",\"edge\":\"E\",\"colour\":2},{\"type\":\"edge\",\"edge\":\"W\",\"colour\":2}]},\"legend\":{\"A\":{\"name\":\"piece\",\"scale\":0.5,\"paint\":{\"fill\":1}},\"B\":{\"name\":\"piece\",\"scale\":0.5,\"paint\":{\"fill\":2}}},\"pieces\":\"ABA\"}" }, + "board-bricka": { + "name": "Boards: Bricka", + "description": "Board for the game Bricka, made up of pentagonal cells, played on intersections with both 5- and 6-connectivity, and the possibility to mark cells around an intersection.", + "render": "{\"board\":{\"style\":\"bricka\",\"markers\":[{\"type\":\"edge\",\"edge\":\"NW\",\"colour\":4},{\"type\":\"edge\",\"edge\":\"SE\",\"colour\":4},{\"type\":\"edge\",\"edge\":\"NE\",\"colour\":5},{\"type\":\"edge\",\"edge\":\"SW\",\"colour\":5},{\"type\":\"edge\",\"edge\":\"W\",\"colour\":6},{\"type\":\"edge\",\"edge\":\"E\",\"colour\":6},{\"type\":\"flood\",\"points\":[{\"row\":2,\"col\":5},{\"row\":3,\"col\":6},{\"row\":4,\"col\":4}],\"colour\":1},{\"type\":\"flood\",\"points\":[{\"row\":6,\"col\":7},{\"row\":5,\"col\":9},{\"row\":7,\"col\":9}],\"colour\":2}]},\"legend\":{\"A\":{\"name\":\"piece\",\"paint\":{\"fill\":1}},\"B\":{\"name\":\"piece\",\"paint\":{\"fill\":2}},\"C\":{\"name\":\"piece\",\"paint\":{\"fill\":3}}},\"pieces\":\"\\n\\n\\n,,,,,A\\n,,,,,C\\n\\n,,,,,,,,B\"}" + }, "board-pentagonal": { "name": "Boards: Pentagonal (Bluestone)", "description": "A slightly modified pentagonal board for the game Bluestone. But the raw pentagonal board is also available.",