improved algorithm for calculating player name location

This commit is contained in:
evanpelle
2024-08-19 13:07:15 -07:00
parent fe67cc8492
commit 7d48dd1cba
7 changed files with 111 additions and 68 deletions
+149
View File
@@ -0,0 +1,149 @@
import {Game, Player, Tile, Cell, TerrainTypes} from '../../core/Game';
import {within} from '../../core/Util';
export interface Point {
x: number;
y: number;
}
export interface Rectangle {
x: number;
y: number;
width: number;
height: number;
}
export function placeName(game: Game, player: Player): [position: Cell, fontSize: number] {
const boundingBox = calculateBoundingBox(player);
const rawScalingFactor = (boundingBox.max.x - boundingBox.min.x) / 50
const scalingFactor = within(Math.floor(rawScalingFactor), 1, 100)
const grid = createGrid(game, player, boundingBox, scalingFactor);
const largestRectangle = findLargestInscribedRectangle(grid);
largestRectangle.x = largestRectangle.x * scalingFactor
largestRectangle.y = largestRectangle.y * scalingFactor
largestRectangle.width = largestRectangle.width * scalingFactor
largestRectangle.height = largestRectangle.height * scalingFactor
const center = new Cell(
Math.floor(largestRectangle.x + largestRectangle.width / 2 + boundingBox.min.x),
Math.floor(largestRectangle.y + largestRectangle.height / 2 + boundingBox.min.y),
)
const fontSize = calculateFontSize(largestRectangle, player.info().name);
return [center, fontSize]
}
export function calculateBoundingBox(player: Player): {min: Cell; max: Cell} {
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
player.borderTiles().forEach((tile: Tile) => {
const cell = tile.cell();
minX = Math.min(minX, cell.x);
minY = Math.min(minY, cell.y);
maxX = Math.max(maxX, cell.x);
maxY = Math.max(maxY, cell.y);
});
return {min: new Cell(minX, minY), max: new Cell(maxX, maxY)}
}
export function createGrid(game: Game, player: Player, boundingBox: {min: Point; max: Point}, scalingFactor: number): boolean[][] {
const scaledBoundingBox: {min: Point; max: Point} = {
min: {
x: Math.floor(boundingBox.min.x / scalingFactor),
y: Math.floor(boundingBox.min.y / scalingFactor)
},
max: {
x: Math.floor(boundingBox.max.x / scalingFactor),
y: Math.floor(boundingBox.max.y / scalingFactor)
}
}
const width = scaledBoundingBox.max.x - scaledBoundingBox.min.x + 1;
const height = scaledBoundingBox.max.y - scaledBoundingBox.min.y + 1;
const grid: boolean[][] = Array(width).fill(null).map(() => Array(height).fill(false));
for (let x = scaledBoundingBox.min.x; x <= scaledBoundingBox.max.x; x++) {
for (let y = scaledBoundingBox.min.y; y <= scaledBoundingBox.max.y; y++) {
const cell = new Cell(x * scalingFactor, y * scalingFactor);
if (game.isOnMap(cell)) {
const tile = game.tile(cell);
grid[x - scaledBoundingBox.min.x][y - scaledBoundingBox.min.y] = tile.owner() === player; // TODO: okay if lake
}
}
}
return grid;
}
export function findLargestInscribedRectangle(grid: boolean[][]): Rectangle {
const rows = grid[0].length;
const cols = grid.length;
const heights: number[] = new Array(cols).fill(0);
let largestRect: Rectangle = {x: 0, y: 0, width: 0, height: 0};
for (let row = 0; row < rows; row++) {
for (let col = 0; col < cols; col++) {
if (grid[col][row]) {
heights[col]++;
} else {
heights[col] = 0;
}
}
const rectForRow = largestRectangleInHistogram(heights);
if (rectForRow.width * rectForRow.height > largestRect.width * largestRect.height) {
largestRect = {
x: rectForRow.x,
y: row - rectForRow.height + 1,
width: rectForRow.width,
height: rectForRow.height
};
}
}
return largestRect;
}
export function largestRectangleInHistogram(widths: number[]): Rectangle {
const stack: number[] = [];
let maxArea = 0;
let largestRect: Rectangle = {x: 0, y: 0, width: 0, height: 0};
for (let i = 0; i <= widths.length; i++) {
const h = i === widths.length ? 0 : widths[i];
while (stack.length > 0 && h < widths[stack[stack.length - 1]]) {
const height = widths[stack.pop()!];
const width = stack.length === 0 ? i : i - stack[stack.length - 1] - 1;
if (height * width > maxArea) {
maxArea = height * width;
largestRect = {
x: stack.length === 0 ? 0 : stack[stack.length - 1] + 1,
y: 0,
width: width,
height: height
};
}
}
stack.push(i);
}
return largestRect;
}
export function calculateFontSize(rectangle: Rectangle, name: string): number {
// This is a simplified calculation. You might want to adjust it based on your specific font and rendering system.
const widthConstrained = rectangle.width / name.length;
const heightConstrained = rectangle.height / name.length;
return Math.min(widthConstrained, heightConstrained);
}