Files
match3-unity/Assets/Scripts/Services/MatchService.cs
2025-12-18 03:49:05 +08:00

127 lines
4.5 KiB
C#

using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using Enums;
using Models.Interfaces;
using Services.Interfaces;
using UnityEngine;
namespace Services {
public class MatchService : IMatchService {
private readonly IGameBoard gameBoard;
private readonly HashSet<Gem> currentMatches = new HashSet<Gem>();
public HashSet<Gem> CurrentMatches => this.currentMatches;
public MatchService(IGameBoard gameBoard) {
this.gameBoard = gameBoard;
}
public bool MatchesAt(Vector2Int positionToCheck, GemType gemTypeToCheck) {
Gem[,] gems = this.gameBoard.GemsGrid;
// We don't prevent spawning bombs via this rule (and bombs shouldn't be treated as a normal color here).
if (gemTypeToCheck == GemType.Bomb)
return false;
// Check horizontal: would placing gemTypeToCheck at positionToCheck create XXX with 2-left?
if (positionToCheck.x > 1) {
Gem left1 = gems[positionToCheck.x - 1, positionToCheck.y];
Gem left2 = gems[positionToCheck.x - 2, positionToCheck.y];
if (left1 != null && left2 != null &&
left1.MatchColor == gemTypeToCheck &&
left2.MatchColor == gemTypeToCheck)
return true;
}
// Check vertical: would placing gemTypeToCheck at positionToCheck create XXX with 2-down?
if (positionToCheck.y > 1) {
Gem down1 = gems[positionToCheck.x, positionToCheck.y - 1];
Gem down2 = gems[positionToCheck.x, positionToCheck.y - 2];
if (down1 != null && down2 != null &&
down1.MatchColor == gemTypeToCheck &&
down2.MatchColor == gemTypeToCheck)
return true;
}
return false;
}
public UniTask<HashSet<Vector2Int>> GetMatchPositionsAsync(List<Vector2Int> protectedPositions) {
HashSet<Vector2Int> matchPositions = new HashSet<Vector2Int>();
List<Gem> matches = this.currentMatches.ToList();
for (int i = 0; i < matches.Count; i++) {
Gem match = matches[i];
if (match == null) continue;
Vector2Int pos = match.Position;
if (protectedPositions != null && protectedPositions.Contains(pos))
continue;
matchPositions.Add(pos);
}
return UniTask.FromResult(matchPositions);
}
public void FindAllMatches() {
this.currentMatches.Clear();
Gem[,] grid = this.gameBoard.GemsGrid;
int boardWidth = this.gameBoard.Width;
int boardHeight = this.gameBoard.Height;
// Horizontal runs
for (int y = 0; y < boardHeight; y++) {
int x = 0;
while (x < boardWidth) {
Gem start = grid[x, y];
if (start == null) { x++; continue; }
GemType color = start.MatchColor;
int runLen = 1;
while (x + runLen < boardWidth) {
Gem next = grid[x + runLen, y];
if (next == null || next.MatchColor != color) break;
runLen++;
}
if (runLen >= 3) {
for (int i = 0; i < runLen; i++)
this.currentMatches.Add(grid[x + i, y]);
}
x += runLen;
}
}
// Vertical runs
for (int x = 0; x < boardWidth; x++) {
int y = 0;
while (y < boardHeight) {
Gem start = grid[x, y];
if (start == null) { y++; continue; }
GemType color = start.MatchColor;
int runLen = 1;
while (y + runLen < boardHeight) {
Gem next = grid[x, y + runLen];
if (next == null || next.MatchColor != color) break;
runLen++;
}
if (runLen >= 3) {
for (int i = 0; i < runLen; i++)
this.currentMatches.Add(grid[x, y + i]);
}
y += runLen;
}
}
}
}
}