359 lines
13 KiB
C#
359 lines
13 KiB
C#
using UnityEngine;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
|
||
namespace AibisDream.MiniGame.Language
|
||
{
|
||
/// <summary>
|
||
/// 分析模式网格生成器
|
||
/// 根据配置数据智能生成网格布局,确保唯一解
|
||
/// </summary>
|
||
public class AnalysisModeGenerator
|
||
{
|
||
private AnalysisModeData config;
|
||
private System.Random random;
|
||
|
||
public AnalysisModeGenerator(AnalysisModeData config, int seed = -1)
|
||
{
|
||
this.config = config;
|
||
this.random = seed >= 0 ? new System.Random(seed) : new System.Random();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成完整的网格数据
|
||
/// </summary>
|
||
public GridCell[,] GenerateGrid(int gridWidth, int gridHeight)
|
||
{
|
||
GridCell[,] grid = new GridCell[gridHeight, gridWidth];
|
||
|
||
// 第一步:放置答案词(在正确的解码器位置)
|
||
PlaceAnswerWords(grid, gridWidth, gridHeight);
|
||
|
||
// 第二步:放置诱饵词(相同情绪,但位置不匹配解码器)
|
||
PlaceDecoyWords(grid, gridWidth, gridHeight);
|
||
|
||
// 第三步:填充中性词
|
||
FillNeutralWords(grid, gridWidth, gridHeight);
|
||
|
||
// 第四步:验证唯一解
|
||
if (!ValidateUniqueSolution(grid, gridWidth, gridHeight))
|
||
{
|
||
Debug.LogWarning("[AnalysisModeGenerator] 生成的网格可能没有唯一解,建议调整配置");
|
||
}
|
||
|
||
return grid;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 放置答案词
|
||
/// </summary>
|
||
private void PlaceAnswerWords(GridCell[,] grid, int gridWidth, int gridHeight)
|
||
{
|
||
Vector2Int decoderPos = config.correctDecoderPosition;
|
||
|
||
for (int i = 0; i < config.answerWords.Count; i++)
|
||
{
|
||
// 计算答案词的绝对位置 = 解码器位置 + 孔洞相对位置
|
||
Vector2Int holeOffset = config.holePositions[i];
|
||
int row = decoderPos.x + holeOffset.x;
|
||
int col = decoderPos.y + holeOffset.y;
|
||
|
||
// 确保位置在网格范围内
|
||
if (row < 0 || row >= gridHeight || col < 0 || col >= gridWidth)
|
||
{
|
||
Debug.LogError($"[AnalysisModeGenerator] 答案词位置({row}, {col})超出网格范围");
|
||
continue;
|
||
}
|
||
|
||
// 放置答案词
|
||
grid[row, col] = new GridCell
|
||
{
|
||
row = row,
|
||
col = col,
|
||
word = config.answerWords[i],
|
||
emotion = config.answerEmotion,
|
||
isTarget = true
|
||
};
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 放置诱饵词(相同情绪,但不匹配解码器)
|
||
/// </summary>
|
||
private void PlaceDecoyWords(GridCell[,] grid, int gridWidth, int gridHeight)
|
||
{
|
||
// 为每种情绪生成诱饵词
|
||
EmotionType[] emotions = { EmotionType.Joy, EmotionType.Sadness, EmotionType.Anger, EmotionType.Fear };
|
||
|
||
foreach (var emotion in emotions)
|
||
{
|
||
// 获取该情绪的诱饵词数量
|
||
int decoyCount = config.decoyCountPerEmotion;
|
||
|
||
// 如果是答案情绪,减去答案词数量
|
||
if (emotion == config.answerEmotion)
|
||
{
|
||
decoyCount = Mathf.Max(0, decoyCount - config.answerWords.Count);
|
||
}
|
||
|
||
// 获取随机词语(排除答案词)
|
||
List<string> decoyWords = config.wordPool.GetRandomWords(
|
||
emotion,
|
||
decoyCount,
|
||
emotion == config.answerEmotion ? config.answerWords : null
|
||
);
|
||
|
||
// 放置诱饵词
|
||
PlaceWordsRandomly(grid, gridWidth, gridHeight, decoyWords, emotion, false);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 填充中性词
|
||
/// </summary>
|
||
private void FillNeutralWords(GridCell[,] grid, int gridWidth, int gridHeight)
|
||
{
|
||
List<string> neutralWords = new List<string>(config.wordPool.neutralWords);
|
||
|
||
// 随机打乱
|
||
neutralWords = neutralWords.OrderBy(x => random.Next()).ToList();
|
||
|
||
int wordIndex = 0;
|
||
for (int row = 0; row < gridHeight; row++)
|
||
{
|
||
for (int col = 0; col < gridWidth; col++)
|
||
{
|
||
if (grid[row, col] == null)
|
||
{
|
||
// 如果词库用完了,重新开始
|
||
if (wordIndex >= neutralWords.Count)
|
||
{
|
||
wordIndex = 0;
|
||
}
|
||
|
||
grid[row, col] = new GridCell
|
||
{
|
||
row = row,
|
||
col = col,
|
||
word = neutralWords[wordIndex],
|
||
emotion = EmotionType.Neutral,
|
||
isTarget = false
|
||
};
|
||
|
||
wordIndex++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 随机放置词语
|
||
/// </summary>
|
||
private void PlaceWordsRandomly(GridCell[,] grid, int gridWidth, int gridHeight,
|
||
List<string> words, EmotionType emotion, bool isTarget)
|
||
{
|
||
List<Vector2Int> availablePositions = new List<Vector2Int>();
|
||
|
||
// 收集所有可用位置
|
||
for (int row = 0; row < gridHeight; row++)
|
||
{
|
||
for (int col = 0; col < gridWidth; col++)
|
||
{
|
||
if (grid[row, col] == null)
|
||
{
|
||
availablePositions.Add(new Vector2Int(row, col));
|
||
}
|
||
}
|
||
}
|
||
|
||
// 随机打乱
|
||
availablePositions = availablePositions.OrderBy(x => random.Next()).ToList();
|
||
|
||
// 放置词语
|
||
for (int i = 0; i < words.Count && i < availablePositions.Count; i++)
|
||
{
|
||
Vector2Int pos = availablePositions[i];
|
||
grid[pos.x, pos.y] = new GridCell
|
||
{
|
||
row = pos.x,
|
||
col = pos.y,
|
||
word = words[i],
|
||
emotion = emotion,
|
||
isTarget = isTarget
|
||
};
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 验证唯一解:检查是否只有一个位置和情绪组合能匹配所有答案词
|
||
/// </summary>
|
||
private bool ValidateUniqueSolution(GridCell[,] grid, int gridWidth, int gridHeight)
|
||
{
|
||
int matchCount = 0;
|
||
Vector2Int correctPos = config.correctDecoderPosition;
|
||
|
||
// 遍历所有可能的解码器位置
|
||
for (int row = 0; row <= gridHeight - config.decoderHeight; row++)
|
||
{
|
||
for (int col = 0; col <= gridWidth - config.decoderWidth; col++)
|
||
{
|
||
// 遍历所有情绪
|
||
foreach (EmotionType emotion in System.Enum.GetValues(typeof(EmotionType)))
|
||
{
|
||
if (emotion == EmotionType.Neutral) continue;
|
||
|
||
// 检查是否所有孔洞位置都匹配该情绪
|
||
bool allMatch = true;
|
||
List<string> matchedWords = new List<string>();
|
||
|
||
foreach (var hole in config.holePositions)
|
||
{
|
||
int checkRow = row + hole.x;
|
||
int checkCol = col + hole.y;
|
||
|
||
if (checkRow >= gridHeight || checkCol >= gridWidth)
|
||
{
|
||
allMatch = false;
|
||
break;
|
||
}
|
||
|
||
GridCell cell = grid[checkRow, checkCol];
|
||
if (cell == null || cell.emotion != emotion)
|
||
{
|
||
allMatch = false;
|
||
break;
|
||
}
|
||
|
||
matchedWords.Add(cell.word);
|
||
}
|
||
|
||
// 如果所有孔洞都匹配
|
||
if (allMatch)
|
||
{
|
||
matchCount++;
|
||
|
||
// 检查是否是正确答案
|
||
bool isCorrectAnswer = (row == correctPos.x && col == correctPos.y &&
|
||
emotion == config.answerEmotion);
|
||
|
||
if (isCorrectAnswer)
|
||
{
|
||
// 验证词语是否完全匹配
|
||
bool wordsMatch = true;
|
||
for (int i = 0; i < config.answerWords.Count; i++)
|
||
{
|
||
if (matchedWords[i] != config.answerWords[i])
|
||
{
|
||
wordsMatch = false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!wordsMatch)
|
||
{
|
||
Debug.LogError("[AnalysisModeGenerator] 正确位置的词语不匹配答案!");
|
||
return false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 诱饵解
|
||
Debug.Log($"[AnalysisModeGenerator] 发现诱饵解: 位置({row},{col}), 情绪{emotion}, 词语[{string.Join(",", matchedWords)}]");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 应该恰好有一个解
|
||
if (matchCount == 0)
|
||
{
|
||
Debug.LogError("[AnalysisModeGenerator] 没有找到任何解!");
|
||
return false;
|
||
}
|
||
else if (matchCount > 1)
|
||
{
|
||
Debug.LogWarning($"[AnalysisModeGenerator] 找到 {matchCount} 个可能的解(包括诱饵),这可能增加难度");
|
||
}
|
||
else
|
||
{
|
||
Debug.Log("[AnalysisModeGenerator] 验证通过:唯一解");
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成笑话演出数据(可选)
|
||
/// 将网格数据转换为笑话段落,用于初始演出
|
||
/// </summary>
|
||
public List<string> GenerateJokePhrases(GridCell[,] grid, int gridWidth, int gridHeight)
|
||
{
|
||
// 如果配置中有笑话,直接使用
|
||
if (config.jokePhrases != null && config.jokePhrases.Count > 0)
|
||
{
|
||
return new List<string>(config.jokePhrases);
|
||
}
|
||
|
||
// 否则从网格词语生成简单的笑话段落
|
||
List<string> phrases = new List<string>();
|
||
System.Text.StringBuilder sb = new System.Text.StringBuilder();
|
||
|
||
for (int row = 0; row < gridHeight; row++)
|
||
{
|
||
for (int col = 0; col < gridWidth; col += 10) // 每10个词组成一句
|
||
{
|
||
sb.Clear();
|
||
for (int i = 0; i < 10 && col + i < gridWidth; i++)
|
||
{
|
||
if (grid[row, col + i] != null)
|
||
{
|
||
sb.Append(grid[row, col + i].word);
|
||
}
|
||
}
|
||
if (sb.Length > 0)
|
||
{
|
||
phrases.Add(sb.ToString());
|
||
}
|
||
}
|
||
}
|
||
|
||
return phrases;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 生成解析目标配置(用于 AnalysisModeManager)
|
||
/// </summary>
|
||
public AnalysisTarget GenerateAnalysisTarget()
|
||
{
|
||
AnalysisTarget target = new AnalysisTarget
|
||
{
|
||
name = config.levelName,
|
||
emotion = config.answerEmotion,
|
||
stripPosition = config.correctDecoderPosition,
|
||
isCorrectAnswer = true,
|
||
words = new List<TargetWord>()
|
||
};
|
||
|
||
// 添加目标词
|
||
for (int i = 0; i < config.answerWords.Count; i++)
|
||
{
|
||
Vector2Int holeOffset = config.holePositions[i];
|
||
Vector2Int absolutePos = new Vector2Int(
|
||
config.correctDecoderPosition.x + holeOffset.x,
|
||
config.correctDecoderPosition.y + holeOffset.y
|
||
);
|
||
|
||
target.words.Add(new TargetWord
|
||
{
|
||
position = absolutePos,
|
||
word = config.answerWords[i]
|
||
});
|
||
}
|
||
|
||
return target;
|
||
}
|
||
}
|
||
}
|
||
|