-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathboardlayout.html
More file actions
265 lines (239 loc) · 11.2 KB
/
Copy pathboardlayout.html
File metadata and controls
265 lines (239 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hex Board Layout Generator</title>
<style>
body {
font-family: sans-serif;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
}
#board-container {
margin-bottom: 20px;
position: relative;
}
svg {
border: 1px solid #ccc;
background-color: #f8f8f8;
}
.hexagon {
stroke: #555;
stroke-width: 1; /* Reduced stroke slightly */
fill: #f0e68c; /* Khaki */
}
.capital-city {
fill: #add8e6; /* Light Blue */
stroke: #333;
stroke-width: 2;
}
.corner-indicator {
stroke-width: 1;
stroke: #444; /* Dark grey outline */
}
.corner-indicator.double {
stroke-width: 3;
stroke: #000; /* Black outline for emphasis */
}
/* Color definitions */
.corner-green { fill: #2E8B57; } /* SeaGreen */
.corner-black { fill: #333333; }
.corner-grey { fill: #808080; } /* Grey */
.corner-gold { fill: #FFD700; } /* Gold */
button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
}
.capital-text {
font-size: 16px;
font-weight: bold;
fill: #000;
text-anchor: middle;
dominant-baseline: central;
pointer-events: none;
}
</style>
</head>
<body>
<h1>Hex Board Layout Generator</h1>
<p>Generates a random layout for a 19-tile hexagonal board (Pointy-top overall shape, flat-top individual tiles).</p>
<ul>
<li>The Capital City (light blue) is placed on a random edge (non-corner) of the outer ring.</li>
<li>Other tiles have 6 corners colored Green, Black, Grey, or Gold.</li>
<li>Total corners: 27 of each color.</li>
<li>Exactly 2 corners of each color are "double" (indicated by a thicker outline).</li>
</ul>
<div id="board-container">
<svg id="board-svg" width="600" height="550"></svg> <!-- Height adjusted slightly -->
</div>
<button id="generateButton">Generate New Layout</button>
<script>
const SVG_NS = "http://www.w3.org/2000/svg";
const svg = document.getElementById('board-svg');
const generateButton = document.getElementById('generateButton');
const boardContainer = document.getElementById('board-container');
// --- Configuration ---
// HEX_SIZE defines the size of the *individual* flat-top hexagons being drawn
const HEX_SIZE = 40; // Distance from center to vertex for the flat-top hexes
// Spacing constants *for arranging into a pointy-top overall shape*
// These are derived from the geometry of pointy-top hexes, even though we draw flat-top ones
const ARRANGEMENT_HEX_HEIGHT = Math.sqrt(3) * HEX_SIZE; // Effective height for vertical spacing in pointy-top layout
const ARRANGEMENT_HEX_WIDTH = 2 * HEX_SIZE; // Effective width for pointy-top layout geometry
const VERT_SPACING = ARRANGEMENT_HEX_HEIGHT; // Vertical distance between rows centers
const HORIZ_SPACING = ARRANGEMENT_HEX_WIDTH * 3 / 4; // Horizontal distance between centers in adjacent columns
const COLORS = ['green', 'black', 'grey', 'gold'];
const CORNERS_PER_COLOR = 27;
const DOUBLE_CORNERS_PER_COLOR = 2;
const TILES_COUNT = 19;
const CORNERS_PER_TILE = 6;
const CORNER_INDICATOR_RADIUS = 6;
const CORNER_INSET_FACTOR = 0.82; // Pulls corners inwards from the vertex
// --- Tile Positions (Center coordinates for POINTY-TOP overall arrangement) ---
// Uses the spacing constants defined above. Based on the first response's layout.
const svgCenterX = 300;
const svgCenterY = 260; // Adjusted Y center slightly
const hexCenters = [
// THESE ARE THE COORDINATES FROM THE FIRST RESPONSE (indices 0-18)
[svgCenterX, svgCenterY], // 0
[svgCenterX + HORIZ_SPACING, svgCenterY - VERT_SPACING / 2], // 1
[svgCenterX + HORIZ_SPACING, svgCenterY + VERT_SPACING / 2], // 2
[svgCenterX, svgCenterY + VERT_SPACING], // 3
[svgCenterX - HORIZ_SPACING, svgCenterY + VERT_SPACING / 2], // 4
[svgCenterX - HORIZ_SPACING, svgCenterY - VERT_SPACING / 2], // 5
[svgCenterX, svgCenterY - VERT_SPACING], // 6
[svgCenterX + 2 * HORIZ_SPACING, svgCenterY - VERT_SPACING], // 7 - Outer Ring Starts
[svgCenterX + 2 * HORIZ_SPACING, svgCenterY], // 8 <- EDGE
[svgCenterX + 2 * HORIZ_SPACING, svgCenterY + VERT_SPACING], // 9
[svgCenterX + HORIZ_SPACING, svgCenterY + VERT_SPACING * 1.5], // 10 <- EDGE
[svgCenterX, svgCenterY + 2 * VERT_SPACING], // 11
[svgCenterX - HORIZ_SPACING, svgCenterY + VERT_SPACING * 1.5], // 12 <- EDGE
[svgCenterX - 2 * HORIZ_SPACING, svgCenterY + VERT_SPACING], // 13
[svgCenterX - 2 * HORIZ_SPACING, svgCenterY], // 14 <- EDGE
[svgCenterX - 2 * HORIZ_SPACING, svgCenterY - VERT_SPACING], // 15
[svgCenterX - HORIZ_SPACING, svgCenterY - VERT_SPACING * 1.5], // 16 <- EDGE
[svgCenterX, svgCenterY - 2 * VERT_SPACING], // 17
[svgCenterX + HORIZ_SPACING, svgCenterY - VERT_SPACING * 1.5] // 18 <- EDGE
];
// Indices of the tiles on the outer ring's flat edges (for pointy-top layout)
const EDGE_INDICES = [8, 10, 12, 14, 16, 18]; // Matches the coordinate list above
// --- Helper Functions ---
// Fisher-Yates Shuffle
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
// Calculate the 6 points of a FLAT-TOP hexagon polygon
// (This determines the shape of the *individual* tiles)
function getHexPoints(centerX, centerY, size) {
let points = "";
for (let i = 0; i < 6; i++) {
// Start angle 0 degrees for flat top vertex on the right
const angle_deg = 60 * i;
const angle_rad = Math.PI / 180 * angle_deg;
const x = centerX + size * Math.cos(angle_rad);
const y = centerY + size * Math.sin(angle_rad);
points += `${x},${y} `;
}
return points.trim();
}
// Calculate the 6 corner coordinates for placing indicators, INSET from vertices
// Uses angles appropriate for the *flat-top* hexagons being drawn.
function getCornerCoords(centerX, centerY, size) {
const cornerCoords = [];
const insetRadius = size * CORNER_INSET_FACTOR; // Calculate the inset radius
for (let i = 0; i < 6; i++) {
// Angles correspond to the vertices of a flat-top hexagon (0, 60, 120...)
const angle_deg = 60 * i;
const angle_rad = Math.PI / 180 * angle_deg;
const cornerX = centerX + insetRadius * Math.cos(angle_rad); // Use insetRadius
const cornerY = centerY + insetRadius * Math.sin(angle_rad); // Use insetRadius
cornerCoords.push({ x: cornerX, y: cornerY });
}
return cornerCoords;
}
// --- Core Logic --- (Unchanged from previous working version)
function generateLayout() {
const capitalIndex = EDGE_INDICES[Math.floor(Math.random() * EDGE_INDICES.length)];
const cornerPool = [];
COLORS.forEach(color => {
for (let i = 0; i < CORNERS_PER_COLOR; i++) {
const isDouble = i < DOUBLE_CORNERS_PER_COLOR;
cornerPool.push({ color: color, double: isDouble });
}
});
shuffleArray(cornerPool);
const boardLayout = [];
let cornerPoolIndex = 0;
for (let i = 0; i < TILES_COUNT; i++) {
// Store the index so drawBoard knows which center coord to use
const tileDataBase = { id: i };
if (i === capitalIndex) {
boardLayout.push({ ...tileDataBase, type: 'capital' });
} else {
const tileCorners = [];
if (cornerPoolIndex + CORNERS_PER_TILE > cornerPool.length) {
console.error("Error: Not enough corners in the pool for tile", i);
break;
}
for (let j = 0; j < CORNERS_PER_TILE; j++) {
tileCorners.push(cornerPool[cornerPoolIndex++]);
}
shuffleArray(tileCorners);
boardLayout.push({ ...tileDataBase, type: 'normal', corners: tileCorners });
}
}
if (cornerPoolIndex !== (TILES_COUNT - 1) * CORNERS_PER_TILE) {
console.warn("Corner pool index mismatch. Expected:", (TILES_COUNT - 1) * CORNERS_PER_TILE, "Got:", cornerPoolIndex);
}
drawBoard(boardLayout);
}
function drawBoard(boardLayout) {
svg.innerHTML = '';
boardLayout.forEach((tileData) => {
const index = tileData.id;
if (index === undefined || index < 0 || index >= hexCenters.length) {
console.error("Invalid tile index encountered in drawBoard:", index, tileData);
return;
}
const [cx, cy] = hexCenters[index]; // Use the POINTY-TOP arrangement centers
const points = getHexPoints(cx, cy, HEX_SIZE); // Draw a FLAT-TOP hex shape
const hexagon = document.createElementNS(SVG_NS, 'polygon');
hexagon.setAttribute('points', points);
hexagon.setAttribute('class', tileData.type === 'capital' ? 'hexagon capital-city' : 'hexagon');
svg.appendChild(hexagon);
if (tileData.type === 'capital') {
const text = document.createElementNS(SVG_NS, 'text');
text.setAttribute('x', cx);
text.setAttribute('y', cy);
text.setAttribute('class', 'capital-text');
text.textContent = 'C';
svg.appendChild(text);
}
else if (tileData.type === 'normal' && tileData.corners) {
// Calculate corners relative to the flat-top hex being drawn
const cornerCoords = getCornerCoords(cx, cy, HEX_SIZE);
tileData.corners.forEach((cornerInfo, cornerIndex) => {
if (cornerIndex >= cornerCoords.length) return;
const { x, y } = cornerCoords[cornerIndex];
const indicator = document.createElementNS(SVG_NS, 'circle');
indicator.setAttribute('cx', x);
indicator.setAttribute('cy', y);
indicator.setAttribute('r', CORNER_INDICATOR_RADIUS);
indicator.setAttribute('class', `corner-indicator corner-${cornerInfo.color} ${cornerInfo.double ? 'double' : ''}`);
svg.appendChild(indicator);
});
}
});
}
// --- Initialization ---
generateButton.addEventListener('click', generateLayout);
generateLayout(); // Initial layout
</script>
</body>
</html>