- L and T shapes are no longer considered a match - Bomb explodes WITH gems - Bomb will have a small sprite of the creating gem
188 lines
6.8 KiB
C#
188 lines
6.8 KiB
C#
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<Gem> currentMatches = new List<Gem>();
|
|
public List<Gem> CurrentMatches => this.currentMatches;
|
|
|
|
public List<BombSpawnRequest> pendingBombSpawns = new List<BombSpawnRequest>();
|
|
public IReadOnlyList<BombSpawnRequest> 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;
|
|
|
|
// 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 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();
|
|
}
|
|
|
|
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;
|
|
|
|
// If the matched group that includes this pivot has 4+ connected gems, spawn a bomb.
|
|
int groupSize = GetMatchedGroupSize(pivot);
|
|
if (groupSize < 4)
|
|
return;
|
|
|
|
// 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 GetMatchedGroupSize(Vector2Int pivot) {
|
|
Gem pivotGem = this.gameBoard.GetGemAt(pivot);
|
|
if (pivotGem == null)
|
|
return 0;
|
|
|
|
GemType color = pivotGem.MatchColor;
|
|
|
|
HashSet<Vector2Int> matchedPositions = new HashSet<Vector2Int>(
|
|
this.currentMatches
|
|
.Where(g => g != null && g.MatchColor == color)
|
|
.Select(g => g.Position)
|
|
);
|
|
|
|
if (!matchedPositions.Contains(pivot))
|
|
return 0;
|
|
|
|
Queue<Vector2Int> queue = new Queue<Vector2Int>();
|
|
HashSet<Vector2Int> visited = new HashSet<Vector2Int>();
|
|
|
|
queue.Enqueue(pivot);
|
|
visited.Add(pivot);
|
|
|
|
Vector2Int[] directions = {
|
|
Vector2Int.left,
|
|
Vector2Int.right,
|
|
Vector2Int.up,
|
|
Vector2Int.down
|
|
};
|
|
|
|
while (queue.Count > 0) {
|
|
Vector2Int currentPivot = queue.Dequeue();
|
|
|
|
for (int i = 0; i < directions.Length; i++) {
|
|
Vector2Int n = currentPivot + directions[i];
|
|
if (visited.Contains(n))
|
|
continue;
|
|
|
|
if (!matchedPositions.Contains(n))
|
|
continue;
|
|
|
|
visited.Add(n);
|
|
queue.Enqueue(n);
|
|
}
|
|
}
|
|
|
|
return visited.Count;
|
|
}
|
|
}
|
|
} |