using System.Collections.Generic; using System.Linq; using Enums; using Models.Interfaces; using Services.Interfaces; using Structs; using UnityEngine; namespace Services { public class MatchService : IMatchService { private List currentMatches = new List(); public List CurrentMatches => this.currentMatches; public List pendingBombSpawns = new List(); public IReadOnlyList PendingBombSpawns => this.pendingBombSpawns; private Vector2Int lastSwapFrom; private Vector2Int lastSwapTo; private IGameBoard gameBoard; public MatchService(IGameBoard gameBoard) { this.gameBoard = gameBoard; } public void SetLastSwap(Vector2Int from, Vector2Int to) { this.lastSwapFrom = from; this.lastSwapTo = to; } public void ClearPendingBombs() { this.pendingBombSpawns.Clear(); } public bool MatchesAt(Vector2Int positionToCheck, GemType gemTypeToCheck) { Gem[,] gems = this.gameBoard.GemsGrid; if (gemTypeToCheck == GemType.Bomb) return false; if (positionToCheck.x > 1) { if (gems[positionToCheck.x - 1, positionToCheck.y].Type == gemTypeToCheck && gems[positionToCheck.x - 2, positionToCheck.y].Type == gemTypeToCheck) return true; } if (positionToCheck.y > 1) { if (gems[positionToCheck.x, positionToCheck.y - 1].Type == gemTypeToCheck && gems[positionToCheck.x, positionToCheck.y - 2].Type == gemTypeToCheck) return true; } return false; } public void FindAllMatches() { this.currentMatches.Clear(); this.pendingBombSpawns.Clear(); for (int x = 0; x < this.gameBoard.Width; x++) for (int y = 0; y < this.gameBoard.Height; y++) { Gem currentGem = this.gameBoard.GemsGrid[x, y]; if (currentGem == null) continue; if (x > 0 && x < this.gameBoard.Width - 1) { Gem leftGem = this.gameBoard.GemsGrid[x - 1, y]; Gem rightGem = this.gameBoard.GemsGrid[x + 1, y]; if (leftGem != null && rightGem != null) { if (leftGem.MatchColor == currentGem.MatchColor && rightGem.MatchColor == currentGem.MatchColor) { this.currentMatches.Add(currentGem); this.currentMatches.Add(leftGem); this.currentMatches.Add(rightGem); } } } if (y > 0 && y < this.gameBoard.Height - 1) { Gem aboveGem = this.gameBoard.GemsGrid[x, y - 1]; Gem bellowGem = this.gameBoard.GemsGrid[x, y + 1]; if (aboveGem != null && bellowGem != null) { if (aboveGem.MatchColor == currentGem.MatchColor && bellowGem.MatchColor == currentGem.MatchColor) { this.currentMatches.Add(currentGem); this.currentMatches.Add(aboveGem); this.currentMatches.Add(bellowGem); } } } } if (this.currentMatches.Count > 0) this.currentMatches = this.currentMatches.Distinct().ToList(); DetectBombSpawnFromLastSwap(); CheckForBombs(); } private void DetectBombSpawnFromLastSwap() { Vector2Int from = this.lastSwapFrom; Vector2Int to = this.lastSwapTo; TryCreateBombSpawnAt(from); TryCreateBombSpawnAt(to); } private void TryCreateBombSpawnAt(Vector2Int pivot) { Gem pivotGem = this.gameBoard.GetGemAt(pivot); if (pivotGem == null) return; // If it's already a bomb, don't create another. if (pivotGem.Type == GemType.Bomb) return; if (this.currentMatches.All(g => g.Position != pivot)) return; int horizontal = CountLine(pivot, Vector2Int.left) + 1 + CountLine(pivot, Vector2Int.right); int vertical = CountLine(pivot, Vector2Int.up) + 1 + CountLine(pivot, Vector2Int.down); int best = Mathf.Max(horizontal, vertical); if (best < 4) return; // Spawn a bomb on the creating slot with the same color group. // Prevent duplicates for the same cell. if (this.pendingBombSpawns.Any(b => b.Position == pivot)) return; this.pendingBombSpawns.Add(new BombSpawnRequest(pivot, pivotGem.MatchColor)); } private int CountLine(Vector2Int start, Vector2Int direction) { Gem startGem = this.gameBoard.GetGemAt(start); if (startGem == null) return 0; GemType color = startGem.MatchColor; int count = 0; Vector2Int position = start + direction; while (position.x >= 0 && position.x < this.gameBoard.Width && position.y >= 0 && position.y < this.gameBoard.Height) { Gem g = this.gameBoard.GetGemAt(position); if (g == null || g.MatchColor != color) break; count++; position += direction; } return count; } private void CheckForBombs() { Gem[,] gems = this.gameBoard.GemsGrid; int width = this.gameBoard.Width; int height = this.gameBoard.Height; for (int i = 0; i < this.currentMatches.Count; i++) { Gem gem = this.currentMatches[i]; int x = gem.Position.x; int y = gem.Position.y; Vector2Int[] directions = { Vector2Int.left, Vector2Int.right, Vector2Int.down, Vector2Int.up }; foreach (Vector2Int direction in directions) { int newX = x + direction.x; int newY = y + direction.y; if (newX < 0 || newX >= width || newY < 0 || newY >= height) continue; Gem neighbor = gems[newX, newY]; if (neighbor?.Type == GemType.Bomb) MarkBombCross(new Vector2Int(newX, newY), 1); } } } private void MarkBombCross(Vector2Int bombPosition, int radius) { Gem[,] gems = this.gameBoard.GemsGrid; int width = this.gameBoard.Width; int height = this.gameBoard.Height; void Mark(Vector2Int p) { if (p.x < 0 || p.x >= width || p.y < 0 || p.y >= height) return; Gem g = gems[p.x, p.y]; if (g == null) return; this.currentMatches.Add(g); } Mark(bombPosition); for (int i = 1; i <= radius; i++) { Mark(bombPosition + Vector2Int.left * i); Mark(bombPosition + Vector2Int.right * i); Mark(bombPosition + Vector2Int.up * i); Mark(bombPosition + Vector2Int.down * i); } this.currentMatches = this.currentMatches.Distinct().ToList(); } } }