File size: 2,455 Bytes
67a499d
6ce4ca6
 
 
 
67a499d
6ce4ca6
 
 
 
67a499d
 
 
6ce4ca6
67a499d
 
 
 
 
 
6ce4ca6
67a499d
 
 
 
 
6ce4ca6
67a499d
 
 
 
 
 
6ce4ca6
67a499d
 
 
 
 
 
6ce4ca6
67a499d
 
 
 
 
 
 
 
 
 
 
 
6ce4ca6
67a499d
 
6ce4ca6
67a499d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6ce4ca6
 
 
67a499d
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
import type { Position3D } from "./positionUtils";

/**
 * Spiral-based position manager
 * Assigns positions in a spiral pattern starting from center to avoid overlapping objects
 *
 * Pattern: Center -> Right -> Up -> Left -> Down -> Right (outward spiral)
 * Example positions: (0,0) -> (1,0) -> (1,-1) -> (0,-1) -> (-1,-1) -> (-1,0) -> (-1,1) -> (0,1) -> (1,1) -> (2,1) ...
 */
export class PositionManager {
	private static instance: PositionManager;
	private gridSize = 5; // Distance between grid points
	private spiralGenerator: Generator<{ x: number; z: number }, never, unknown>;

	static getInstance(): PositionManager {
		if (!PositionManager.instance) {
			PositionManager.instance = new PositionManager();
		}
		return PositionManager.instance;
	}

	constructor() {
		this.spiralGenerator = this.generateSpiralPositions();
		// Skip the center position since there's already an object there
		this.spiralGenerator.next();
	}

	/**
	 * Get next available position in a spiral pattern
	 * Starts from center (0,0) and spirals outward
	 */
	getNextPosition(): Position3D {
		const { value: coord } = this.spiralGenerator.next();

		return {
			x: coord.x * this.gridSize,
			y: 0,
			z: coord.z * this.gridSize
		};
	}

	/**
	 * Generator function that yields spiral positions infinitely
	 * Uses a simple clockwise spiral starting from origin
	 */
	private *generateSpiralPositions(): Generator<{ x: number; z: number }, never, unknown> {
		let x = 0,
			z = 0;
		let dx = 1,
			dz = 0; // Start moving right
		let steps = 1;
		let stepCount = 0;
		let changeDirection = 0;

		// Yield center position first
		yield { x, z };

		// Generate spiral positions infinitely
		while (true) {
			x += dx;
			z += dz;
			yield { x, z };

			stepCount++;

			// Change direction when we've completed the required steps
			if (stepCount === steps) {
				stepCount = 0;
				changeDirection++;

				// Rotate 90 degrees clockwise: (dx, dz) -> (dz, -dx)
				const temp = dx;
				dx = dz;
				dz = -temp;

				// Increase step count after every two direction changes
				if (changeDirection % 2 === 0) {
					steps++;
				}
			}
		}
	}

	/**
	 * Reset position generator (useful for testing)
	 */
	reset(): void {
		this.spiralGenerator = this.generateSpiralPositions();
		// Skip the center position since there's already an object there
		this.spiralGenerator.next();
	}
}

// Global instance
export const positionManager = PositionManager.getInstance();