using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
///
/// 编辑评估类 按照提供规则评估扣分情况 并直接计入操作纪录类
///
public class EditorAppraiser
{
#region //行军编队评估
static Dictionary> rightPersonDic = new Dictionary>()
{
{ FormationGroupingEnum.前进保障队, new List() { "连长", "指导员", "副连长", "装甲技师", "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "液压修理工", "车辆综合电子系统修理工", "汽车修理工" } },
{ FormationGroupingEnum.指挥组, new List() { "连长", "指导员", "副连长", "装甲技师", "汽车技师" } },
{ FormationGroupingEnum.前出保障组, new List() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "车辆综合电子修理工", "液压修理工", "汽车修理工" } },
{ FormationGroupingEnum.抢救抢修队, new List() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "车辆综合电子修理工", "液压修理工", "汽车修理工" } },
//{ FormationGroupingEnum.主体编队, new List() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "车辆综合电子修理工", "液压修理工", "汽车修理工" } },
{ FormationGroupingEnum.殿后保障组, new List() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "车辆抢救工", "汽车修理工" } },
};
static Dictionary> rightCarDic = new Dictionary>()
{
{ FormationGroupingEnum.前进保障队, new List() {"NY033H型轮式轻型防化分队指挥车", "ZJQ84Q式中型坦克抢救车", "VME104履带装甲抢修车","VMG101履带装甲补给车", "CXZ411型装甲能源工程车", "CXZ311型装甲拆装工程车", "CXZ511型装甲保养工程车", "CXC012汽车修理工程车","VME301-5型轮式装备抢救车", "CXC022汽车保养车" } },
{ FormationGroupingEnum.指挥组, new List() { "NY033H型轮式轻型防化分队指挥车" } },
{ FormationGroupingEnum.前出保障组, new List() { "ZJQ84Q式中型坦克抢救车", "96A坦克抢救车", "VME104履带装甲抢修车","VMG101履带装甲补给车", "CXZ411型装甲能源工程车", "CXZ511型装甲保养工程车", "徐工QY25K型汽车起重机", "VME301-5型轮式装备抢救车", "CXC022汽车保养车" } },
{ FormationGroupingEnum.抢救抢修队, new List() { "ZJQ84Q式中型坦克抢救车", "96A坦克抢救车", "VME104履带装甲抢修车", "VMG101履带装甲补给车", "CXZ411型装甲能源工程车", "CXZ511型装甲保养工程车", "VMG101履带装甲补给车", "VME301-5型轮式装备抢救车", "CXC022汽车保养车", "CXC012汽车修理工程车" } },
//{ FormationGroupingEnum.主体编队, new List() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "车辆综合电子修理工", "液压修理工", "汽车修理工" } },
{ FormationGroupingEnum.殿后保障组, new List() { "ZJQ84Q式中型坦克抢救车", "徐工QY25K型汽车起重机", "徐工QY16K型汽车起重机", "VME301-5型轮式装备抢救车"} },
};
///
/// 评估编队得分 记录扣分项
///
public void AppraiseTeam(Dictionary> onMapCarsGroups)
{
foreach (var item in onMapCarsGroups)
{
switch (item.Key)
{
case FormationGroupingEnum.前进保障队://不连贯训练时的默认分组
AppraisFrontTeam(item.Value, item.Key.ToString());
break;
case FormationGroupingEnum.指挥组:
AppraisLeaderGroup(item.Value);
break;
case FormationGroupingEnum.前出保障组:
AppraisFrontGroup(item.Value, item.Key.ToString());
break;
case FormationGroupingEnum.抢救抢修队://暂时无评估规则
break;
case FormationGroupingEnum.主体编队://暂时无评估规则
break;
case FormationGroupingEnum.殿后保障组:
AppraisFollowGroup(item.Value, item.Key.ToString());
break;
default:
break;
}
}
//StudentOperateRecorder.Instance.InjectOperateMsg($"行军编队部分评估结束!");
}
///
/// 训练模式操作过程中检查车辆类型是否为当前车队的标准配置人员
///
/// 车辆类型
/// 编组
///
public static bool CheckCar(string carType, FormationGroupingEnum groupingEnum)
{
if (rightCarDic.ContainsKey(groupingEnum))
return rightCarDic[groupingEnum].Contains(carType);
else
return true;
}
///
/// 训练模式操作过程中检查人员类型是为当前车队的标准配置人员
///
///
///
///
public static bool CheckPerson(string personName, FormationGroupingEnum groupingEnum)
{
if (rightPersonDic.ContainsKey(groupingEnum))
return rightPersonDic[groupingEnum].Contains(personName);
else
return true;
}
//评价指挥组 与其他分组评价方法略有差异 不做整合
private void AppraisLeaderGroup(List item)
{
float deductsScore = 0;
//指挥车数量
int leaderCarCount = 0;
//电源车 和电子检修车
int PowerAndConnectCarCount = 0;
//其它不应该出现的车型
List otherCars = new List();
//车队中具备的人员
List personType = new List();
for (int i = 0; i < item.Count; i++)
{
string carName = item[i].text_IconName.text;
foreach (var personItem in item[i].personNames)//统计指挥组中的人员类型
{
if (!personType.Contains(personItem.Key))
personType.Add(personItem.Key);
}
if (carName.Contains("NY033H"))//模糊检查 避免个别名称不太符合的情况和新增装备的情况
{
leaderCarCount++;
}
else if (carName.Contains("电源") || carName.Contains("电子检测维修"))
PowerAndConnectCarCount++;
else
{
if (!otherCars.Contains(carName))
otherCars.Add(carName);
}
}
//增加多余车型 每种车型扣1分
if (otherCars.Count != 0)
{
deductsScore += otherCars.Count * 1;
string otherCarsStr = GetStrFromList(otherCars);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}增加{1}等{2}种车型,扣{3}分", "指挥组", otherCarsStr, otherCars.Count, otherCars.Count);
}
int groupCount = item.Count;
if (leaderCarCount == 0)
{
deductsScore += 10;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}缺少标准车型:{1},扣{2}分", "指挥组", "NY033H型轮式轻型防化分队指挥车", 10);
}
if (leaderCarCount == 1)
{
deductsScore += 3;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("指挥组仅有一台分队指挥车,扣3分");
}
StudentOperateRecorder.Instance.InjectDecScore(deductsScore, EvaluationFactorsEnum.型号组成.ToString());
deductsScore = 0;
//装备数量超过四台 每超一台扣0.5分
if (groupCount > 4) //数量超出
{
deductsScore += ((groupCount - 4) * 0.5f);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}装备数量为{1}台,超出{2}台,扣{3}分", "指挥组", groupCount, 4, otherCars.Count * 0.5f);
}
StudentOperateRecorder.Instance.InjectDecScore(deductsScore, EvaluationFactorsEnum.装备数量.ToString());
deductsScore = 0;
List targetPerson = new List() { "连长", "指导员", "副连长", "装甲技师", "汽车技师" }; //车队应该配备的人员
List anyPerson = GetAny(targetPerson, personType);//不存在的标准人员
if (anyPerson.Count > 0)
{
deductsScore += anyPerson.Count;
string anyPersonsStr = GetStrFromList(anyPerson);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}人员工种缺少{1},扣{2}分", "指挥组", anyPersonsStr, anyPerson.Count);
}
StudentOperateRecorder.Instance.InjectDecScore(deductsScore, EvaluationFactorsEnum.人员工种.ToString());
}
//评价前出保障组
private void AppraisFrontGroup(List item, string groupName)
{
List carTypes = GetCarTypes(item);//已有车队类型
List dueCars = new List() { "ZJQ84Q式中型坦克抢救车", "96A坦克抢救车", "VME104履带装甲抢修车",
"VMG101履带装甲补给车", "CXZ411型装甲能源工程车", "CXZ511型装甲保养工程车", "徐工QY25K型汽车起重机", "VME301-5型轮式装备抢救车", "CXC022汽车保养车" };//应当具备的车型
List notDeductCars = new List() { "VME301机械维修车", "VME105履带装甲抢修车", "CXZ311型装甲拆装工程车", "CXZ321型装甲拆装工程车", "徐工QY16K型汽车起重机", "高机动电源挂车" };//多出但不扣分的车型
GetCarTypesScore(carTypes, groupName, dueCars, 2, notDeductCars, item.Count, 12);
Dictionary personDic;//车队人员类型+数量字典
List personTypes;//车队人员类型
GetPersonTypes(item, out personDic, out personTypes);
List targetPerson = new List() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "车辆综合电子修理工", "液压修理工", "汽车修理工" };
List notDeductPerson = new List() { "钳工", "制配工", "充电工", "车工", "钣焊工", "光电修理工" };//多出不扣分的工种
Dictionary aloneCheckPersons = new Dictionary();//需要单独检查不得低于某数量的工种 每工种少一人扣一分
aloneCheckPersons.Add("抢救工", 6);
aloneCheckPersons.Add("装甲底盘修理工", 5);
aloneCheckPersons.Add("汽车修理工", 4);
GetPersonScore(groupName, personDic, personTypes, targetPerson, notDeductPerson, aloneCheckPersons);
}
//评价殿后保障组
private float AppraisFollowGroup(List item, string groupName)
{
float deductsScore = 0;
List carTypes = GetCarTypes(item);//已有车队类型
List dueCars = new List() { "ZJQ84Q式中型坦克抢救车", "徐工QY25K型汽车起重机", "徐工QY16K型汽车起重机", "VME301-5型轮式装备抢救车" };//应当具备的车型
List notDeductCars = new List() { "96式坦克抢救车", "VME105履带装甲抢修车", "CXZ311型装甲拆装工程车", "CXZ321型装甲拆装工程车" };//多出但不扣分的车型
GetCarTypesScore(carTypes, groupName, dueCars, 2, notDeductCars, item.Count, 6);
Dictionary personDic;//车队人员类型+数量字典
List personTypes;//车队人员类型
GetPersonTypes(item, out personDic, out personTypes);
List targetPerson = new List() { "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "车辆抢救工", "汽车修理工" };//标准人员工种
List notDeductPerson = new List() { "钳工", "制配工", "充电工", "车工", "钣焊工", "液压修理工" };//多出不扣分的工种
Dictionary aloneCheckPersons = new Dictionary();//需要单独检查不得低于某数量的工种 每工种少一人扣一分
aloneCheckPersons.Add("抢救工", 2);
aloneCheckPersons.Add("装甲底盘修理工", 1);
aloneCheckPersons.Add("汽车修理工", 2);
GetPersonScore(groupName, personDic, personTypes, targetPerson, notDeductPerson, aloneCheckPersons);
return deductsScore;
}
//评价前进保障队 不连贯训练
private void AppraisFrontTeam(List item, string groupName)
{
List carTypes = GetCarTypes(item);//已有车队类型
List dueCars = new List() { "NY033H型轮式轻型防化分队指挥车", "ZJQ84Q式中型坦克抢救车", "VME104履带装甲抢修车",
"VMG101履带装甲补给车", "CXZ411型装甲能源工程车", "CXZ311型装甲拆装工程车", "CXZ511型装甲保养工程车", "CXC012汽车修理工程车","VME301-5型轮式装备抢救车", "CXC022汽车保养车" };//应当具备的车型
List notDeductCars = new List() { "96式坦克抢救车", "VME105履带装甲抢修车", "CXZ321型装甲拆装工程车", "徐工QY16K型汽车起重机", "徐工QY25K型汽车起重机", "VME301机械维修车", "VMD301型电源车" };//多出但不扣分的车型
GetCarTypesScore(carTypes, groupName, dueCars, 2, notDeductCars, item.Count, 12);
Dictionary personDic;//车队人员类型+数量字典
List personTypes;//车队人员类型
GetPersonTypes(item, out personDic, out personTypes);
List targetPerson = new List() { "连长", "指导员", "副连长", "装甲技师", "抢救工", "起重工", "装甲底盘修理工", "电气修理工", "火炮修理工", "火控修理工", "通信修理工", "液压修理工", "车辆综合电子系统修理工", "汽车修理工" };
List notDeductPerson = new List() { "钳工", "制配工", "充电工", "钣焊工" };//多出不扣分的工种
Dictionary aloneCheckPersons = new Dictionary();//需要单独检查不得低于某数量的工种 每工种少一人扣一分
aloneCheckPersons.Add("抢救工", 6);
aloneCheckPersons.Add("装甲底盘修理工", 7);
aloneCheckPersons.Add("汽车修理工", 4);
aloneCheckPersons.Add("电气修理工", 3);
GetPersonScore(groupName, personDic, personTypes, targetPerson, notDeductPerson, aloneCheckPersons);
}
//计算编组中人员扣分情况
///
/// 缺少必要工种扣1分 增加非必要的多余工种扣1分 某工种少于一定数量 每少一人扣0.5分
///
/// 编组名称
/// 最终扣分
/// 人员类型+数量字典
/// 人员类型集合
/// 标准人员
/// 超出不扣分人员
/// 需检查数量的某工种+最少数量
///
private float GetPersonScore(string groupName, /*ref float deductsScore,*/ Dictionary personDic, List personTypes, List targetPerson, List notDeductPerson, Dictionary checkPersonCountDic)
{
float deductsScore = 0;
List anyPerson = GetAny(targetPerson, personTypes);//缺少的标准工种
List unnecessaryPerson = GetAny(personTypes, targetPerson);//多出的工种
List deductPerson = GetAny(unnecessaryPerson, notDeductPerson);//多出待扣分的工种
if (anyPerson.Count > 0)
{
deductsScore += anyPerson.Count;
string anyPersonsStr = GetStrFromList(anyPerson);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}人员工种缺少{1}\u201C等\u201D{2}种,扣{3}分", groupName, anyPersonsStr, anyPerson.Count, anyPerson.Count);
}
if (deductPerson.Count > 0)
{
deductsScore += (deductPerson.Count * 1f);
string deductsPersonStr = GetStrFromList(deductPerson);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}人员工种增加:{1}\u201C等\u201D{2}种,扣{3}分", groupName, deductsPersonStr, deductPerson.Count, deductPerson.Count * 1f);
}
StudentOperateRecorder.Instance.InjectDecScore(deductsScore, EvaluationFactorsEnum.人员工种.ToString());
foreach (var item in checkPersonCountDic)
{
CheckPersonCount(groupName, personDic, item.Key, item.Value, 0.5f);
}
return deductsScore;
}
///
/// 梳理 车队中人员工种信息
///
///
/// 返回车载人员字典类
/// 返回车载人员类型集合
private void GetPersonTypes(List item, out Dictionary personDic, out List personTypes)
{
personDic = new Dictionary();
personTypes = new List();
for (int i = 0; i < item.Count; i++)
{
foreach (var personItem in item[i].personNames)//统计所有车中的人员类型
{
if (!personTypes.Contains(personItem.Key))
personTypes.Add(personItem.Key);
if (!personDic.ContainsKey(personItem.Key))
personDic[personItem.Key] = 0;
personDic[personItem.Key] += 1;
}
}
}
///
/// 计算某组中某一类型的工种 是否满足人员要求,并计算应扣除的分数
///
/// 组名 用于生成扣分记录
/// 人员字典
/// 待检查的人员类型
/// 最少人数
/// 少一人扣除的分数
/// 改变当前分数
private void CheckPersonCount(string groupName, Dictionary personDic, string personType, int LeastCount, float onePersonDeductScore)
{
float score = 0;
int personCount = 0;
if (personDic.ContainsKey(personType))
personCount = personDic[personType];
if (personCount < LeastCount)
{
score += ((LeastCount - personCount) * onePersonDeductScore);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}{1}人数为{2},少于{3}人,扣{4}分", groupName, personType, personCount, LeastCount, (LeastCount - personCount) * onePersonDeductScore);
}
StudentOperateRecorder.Instance.InjectDecScore(score, EvaluationFactorsEnum.人员数量.ToString());
}
///
/// 遍历集合A 取出未在集合B中出现过的元素
///
/// 类型
/// 待遍历查找的集合
/// 待排除元素的集合
/// 不存在的值
private List GetAny(List listA, List listB)
{
List anyT = new List();
for (int i = 0; i < listA.Count; i++)
{
if (!listB.Contains(listA[i]))
anyT.Add(listA[i]);
}
return anyT;
}
///
/// 将一系列字符串以、号分割组成一个长字符串
///
///
///
private string GetStrFromList(List strList)
{
string longStr = "";
for (int i = 0; i < strList.Count; i++)
{
longStr += $"{strList[i]}、";
}
return longStr;
}
#endregion
#region//修理厂开展评估
///
/// 评估修理区布置间距和车型
///
/// 存储了厂区+车辆集合的字典 key 厂区名称 value 该厂区中的车辆
/// 铣消场距离水源的最近距离
///
public void AppraiseRepairArea(Dictionary> areaCars)
{
float score = 0;//修理厂展开部分扣除总分数
AppraiseAreaDistanceAndTime(areaCars, ref score);//评估厂区间距 和转运时间
AppraiseWaterToXiXiao(areaCars["洗消场"]);//评估洗消场距离河边的距离
foreach (var item in areaCars)//遍历各厂区 分别评估厂区内部扣分
{
switch (item.Key)
{
case "指控区":
score += AppraiseCC(item.Key, item.Value);
break;
case "油料场":
score += AppraiseYouLiao(item.Key, item.Value);
break;
case "器材存放场":
score += AppraiseQiCai(item.Key, item.Value);
break;
case "弹药场"://距离已经评价完成 无需评价其他内容
break;
case "修竣装备停放场":
score += AppraiseXiuJun(item.Key, item.Value);
break;
case "修理场"://无相关评价规则
break;
case "待修装备停放场":
score += AppraiseDaiXiu(item.Key, item.Value);
break;
case "洗消场":
score += AppraiseXiXiao(item.Key, item.Value);
break;
case "待后送装备停放场":
score += AppraiseDaiHouSong(item.Key, item.Value);
break;
case "接收场":
score += AppraiseJieShou(item.Key, item.Value);
break;
default:
Debug.Log($"{item.Key}无评估规则");
break;
}
}
//StudentOperateRecorder.Instance.InjectOperateMsg($"修理厂开设布置扣分{score}");
}
///
/// 训练模式中检查当前厂区与其他厂区位置是否合适
///
/// 完成规划的厂区
/// 距离异常提示
public static string CheckAreaDistance(List allPlanedAreas)
{
if (allPlanedAreas.Count < 2) return "";//单个厂区时无需检测厂区间距距离是否合适
for (int i = 0; i < allPlanedAreas.Count - 1; i++)//从开始到结尾依次取出一个厂区 和剩余厂区分别计算距离 计算统一规则的距离得分
{
for (int j = i + 1; j < allPlanedAreas.Count; j++)
{
float distance = Vector3.Distance(allPlanedAreas[i].transform.position, allPlanedAreas[j].transform.position) - allPlanedAreas[i].Radius - allPlanedAreas[j].Radius; // 计算厂区间距 标志距离-厂区半径
if (allPlanedAreas[i].AreaName == "弹药场" || allPlanedAreas[j].AreaName == "弹药场") //弹药场与其他厂区的间距限制在200M
{
if (distance < 200)
{
return LocalizationManager.Format("{0}与{1}边界间距为{2}M,小于200M", allPlanedAreas[i].AreaName, allPlanedAreas[j].AreaName, distance.ToString("0.0"));
}
}
else //其他厂区间距限制在100M
{
if (distance < 100)
{
return LocalizationManager.Format("{0}与{1}边界间距为{2}M,小于100M", allPlanedAreas[i].AreaName, allPlanedAreas[j].AreaName, distance.ToString("0.0"));
}
}
}
}
return "";
}
///
/// 训练模式中检查 洗销厂和水源的距离
///
///
///
public static string CheckAreaToWaterDistance(Transform xixiaoPos)
{
float distance= GetXiXiaoToWaterDistance(new List() { xixiaoPos });
if (distance > 100)
return LocalizationManager.Format("{0}距离水源距离为{1}M,超出100M", AreaNameEnum.洗消场.ToString(), distance.ToString("0.0"));
return "";
}
//评估洗消场和水源距离扣分情况
private void AppraiseWaterToXiXiao(List xixiao)
{
float xixiaoToRiverDis = GetXiXiaoToWaterDistance(xixiao);
if (xixiaoToRiverDis == -1)
{
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("当前场景无水源,不计算洗消场与水源间距扣分项");
return;
}
if (xixiaoToRiverDis > 100)
{
float decScore = 0.05f + ((int)(xixiaoToRiverDis - 100)) / 10 * 0.05f;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("洗消场与水源间距为{0}M,大于100M,每10米扣0.05f,计扣{1}分", xixiaoToRiverDis.ToString("0.0"), decScore);
StudentOperateRecorder.Instance.InjectDecScore(decScore, null);//水源不做为权重调整因素
}
}
private static float GetXiXiaoToWaterDistance(List xixiao)
{
WaterManager[] distanceManagers = GameObject.FindObjectsOfType();//查找所有的水源
if (distanceManagers == null || distanceManagers.Length == 0)//不存在水源
{
return -1;
}
float xixiaoToRiverDis = 0;
WaterManager nearWater = ArrayHelper.Min(distanceManagers, (p) => p.GetDistanceFromWater(xixiao[0].position));
Transform trans = ArrayHelper.Min(xixiao, (p) => nearWater.GetNearestPointDistance(p.position));//找洗消场中距离水源最近的Trans
//Debug.Log(nearWater.name);
xixiaoToRiverDis = nearWater.GetNearestPointDistance(trans.position);//距离水源的距离
if (xixiao.Count == 1 && "Flag" == LayerMask.LayerToName(xixiao[0].gameObject.layer))//如果洗消场中不存在车辆 距离需要扣除洗消场默认半径
{
xixiaoToRiverDis -= xixiao[0].GetComponent().Radius;
}
return xixiaoToRiverDis;
}
//评估各厂区间的间距 和转运时间
private void AppraiseAreaDistanceAndTime(Dictionary> areaCars, ref float refScore)
{
float score=0;
Dictionary> areasDistanceDic = new Dictionary>();//记录每个厂区与其他厂区之间的距离 Kye=当前厂区名称 ,value.Key = 与当前厂区计算距离的目标厂区名称 , value.value = 当前厂区与目标厂区之间的距离
List dicKeys = new List();
List> dicValues = new List>();
foreach (var item in areaCars)//按照顺序取出字典形成有序的集合 便于后续计算厂区间距
{
dicKeys.Add(item.Key);
dicValues.Add(item.Value);
}
for (int i = 0; i < dicKeys.Count - 1; i++)//从开始到结尾依次取出一个厂区 和剩余厂区分别计算距离 计算统一规则的距离得分
{
for (int j = i + 1; j < dicKeys.Count; j++)
{
float distance = GetAreasDistance(dicValues[i], dicValues[j]); //每计算出一个距离 分别为两个厂区建立各自的距离字典
if (dicKeys[i] == "弹药场" || dicKeys[j] == "弹药场") //弹药场与其他厂区的间距限制在200M
{
if (distance < 200)
{
score += 1;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}与{1}间距为{2}M,小于200M,扣1分", dicKeys[i], dicKeys[j], distance.ToString("0.0"));
}
}
else //其他厂区间距限制在100M
{
if (distance < 100)
{
score += 1;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}与{1}间距为{2}M,小于100M,扣1分", dicKeys[i], dicKeys[j], distance.ToString("0.0"));
}
}
if (!areasDistanceDic.ContainsKey(dicKeys[i])) //存入字典便于提取单个厂区的间距情况
{
areasDistanceDic.Add(dicKeys[i], new Dictionary());
}
if (!areasDistanceDic.ContainsKey(dicKeys[j]))
{
areasDistanceDic.Add(dicKeys[j], new Dictionary());
}
areasDistanceDic[dicKeys[i]].Add(dicKeys[j], distance);
areasDistanceDic[dicKeys[j]].Add(dicKeys[i], distance);
}
}
if (areasDistanceDic.ContainsKey("指控区"))
{
if (areasDistanceDic["指控区"].ContainsKey("修理场"))
{
float dis1 = areasDistanceDic["指控区"]["修理场"];
if (dis1 < 300)
{
float decScore = 0.5f + ((int)(300 - dis1)) / 50 * 0.5f;
score += decScore;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("指控区与修理场间距为{0}M,小于300M,每50米扣0.5f,计扣{1}分", dis1.ToString("0.0"), decScore);
}
else if (dis1 > 600)
{
float decScore = 0.5f + ((int)(dis1 - 600)) / 50 * 0.5f;
score += decScore;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("指控区与修理场间距为{0}M,大于600M,每50米扣0.5f,计扣{1}分", dis1.ToString("0.0"), decScore);
}
}
else
{
Debug.Log("指控区与修理厂无距离数据");
}
}
else
{
Debug.Log("无指控区距离数据");
}
if (areasDistanceDic.ContainsKey("器材存放场"))
{
if (areasDistanceDic["器材存放场"].ContainsKey("修理场"))
{
float dis2 = areasDistanceDic["器材存放场"]["修理场"];
if (dis2 > 50)
{
float decScore = 0.05f + ((int)(dis2 - 50)) / 10 * 0.05f;
score += decScore;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("器材存放场与修理场间距为{0}M,大于50M,每10米扣0.05f,计扣{1}分", dis2.ToString("0.0"), decScore);
}
}
else {
Debug.Log("器材存放场与修理厂无距离数据");
}
}
else {
Debug.Log("无器材存放场距离数据");
}
refScore += score;
StudentOperateRecorder.Instance.InjectDecScore(score, EvaluationFactorsEnum.厂区间距.ToString());
int time = AppraiseRepairTime(areasDistanceDic);//计算转运用时
StudentOperateRecorder.Instance.InjectTransportTime(time);
}
//评估指控区域
private float AppraiseCC(string areaName, List cars)
{
float deductsScore = 0;
List carTypes = GetCarTypes(cars);
List dueCars = new List() { "NY033H型轮式轻型防化分队指挥车" };//应当具备的车型
List notDeductCars = new List() { "VMD301型电源车", "VMZ308型通指装备电子检测维修车" };//多出不扣分的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, notDeductCars, 0, 0, 0.5f);//评估装备型号部分扣分情况
GetInAreaCarDistanceScore(areaName, cars, ref deductsScore);//评估区域内车辆之间的间距的扣分情况
return deductsScore;
}
//评估接收场
private float AppraiseJieShou(string areaName, List cars)
{
float deductsScore = 0;
List carTypes = GetCarTypes(cars);
List dueCars = new List() { "徐工QY25K型汽车起重机", "96A坦克抢救车", "VME301-5型轮式装备抢救车", "CXZ211型装甲检测工程车", "VMZ306机电检测维修车" };//应当具备的车型
List notDeductCars = new List() { "ZJQ84Q式中型坦克抢救车", "CXL211型雷达检测车", "CXL221型雷达检测车", "CXL231型雷达检测车" };//多出不扣分的车型
List notDeductLessCars = new List() { "徐工QY25K型汽车起重机" };//缺少不扣分的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, notDeductCars, 0, 0, 0.5f, notDeductLessCars);//评估装备型号部分扣分情况
GetInAreaCarDistanceScore(areaName, cars, ref deductsScore);//评估区域内车辆之间的间距的扣分情况
return deductsScore;
}
//评估铣消场
private float AppraiseXiXiao(string areaName, List cars)
{
float deductsScore = 0;
List carTypes = GetCarTypes(cars);
List dueCars = new List() { "CXF111-1型防化修理车" };//应当具备的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, null, 0, 0, 0.5f);//评估装备型号部分扣分情况
GetInAreaCarDistanceScore(areaName, cars, ref deductsScore);//评估区域内车辆之间的间距的扣分情况
return deductsScore;
}
//评估油料场
private float AppraiseYouLiao(string areaName, List cars)
{
float deductsScore = 0;
List carTypes = GetCarTypes(cars);
List dueCars = new List() { "油料运输车" };//应当具备的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, null, 0, 0, 0.5f);//评估装备型号部分扣分情况
return deductsScore;
}
//评估待修装备停放场
private float AppraiseDaiXiu(string areaName, List cars)
{
float deductsScore = 0;
List carTypes = GetCarTypes(cars);
List dueCars = new List() { "ZJQ84Q式中型坦克抢救车"};//应当具备的车型
List notDeductCars = new List() { "徐工QY25K型汽车起重机", "徐工QY16K型汽车起重机", "96式坦克抢救车", "VME301-5型轮式装备抢救车" };//多出不扣分的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, notDeductCars, 0, 0, 0.5f);//评估装备型号部分扣分情况
GetInAreaCarDistanceScore(areaName, cars, ref deductsScore);//评估区域内车辆之间的间距的扣分情况
//GetMainLoadDisScore();//评估距离主通路距离
return deductsScore;
}
//评估器材存放场
private float AppraiseQiCai(string areaName, List cars)
{
float deductsScore = 0;
List carTypes = GetCarTypes(cars);
List dueCars = new List() { "VMG101履带装甲补给车" };//应当具备的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, null, 0, 0, 0.5f);//评估装备型号部分扣分情况
return deductsScore;
}
//评估修竣装备停放场
private float AppraiseXiuJun(string areaName, List cars)
{
float deductsScore = 0;
List carTypes = GetCarTypes(cars);
List dueCars = new List() { "VMZ301电子检测维修车", "CXZ511型装甲保养工程车" };//应当具备的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, null, 0, 0, 0.5f);//评估装备型号部分扣分情况
GetInAreaCarDistanceScore(areaName, cars, ref deductsScore);//评估区域内车辆之间的间距的扣分情况
//GetMainLoadDisScore();//评估距离主通路距离
return deductsScore;
}
//评估待后送装备停放场
private float AppraiseDaiHouSong(string areaName, List cars)
{
float deductsScore = 0;
List carTypes = GetCarTypes(cars);
List dueCars = new List() { "ZJQ84Q式中型坦克抢救车", "VME301-5型轮式装备抢救车" };//应当具备的车型
GetCarTypesScore(carTypes, areaName, dueCars, 0.5f, null, 0, 0, 0.5f);//评估装备型号部分扣分情况
GetInAreaCarDistanceScore(areaName, cars, ref deductsScore);//评估区域内车辆之间的间距的扣分情况
//GetMainLoadDisScore();//评估距离主通路距离
return deductsScore;
}
//评估距离主通路距离 待实现
private void GetMainLoadDisScore()
{
}
//评估区域内部的距离扣分情况
private void GetInAreaCarDistanceScore(string areaName, List cars, ref float refScore)
{
float score = 0;
for (int i = 0; i < cars.Count - 1; i++)
{
for (int j = i; j < cars.Count; j++)//双循环依次计算车队中每两个之间的距离
{
float carsDistance = Vector3.Distance(cars[i].transform.position, cars[j].transform.position);
if (carsDistance < 50)//小于50m的 扣0.5f
{
score += 0.5f;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}中{1}与{2}间距为{3}M,小于50M,扣0.5分", areaName, cars[i].transform.name, cars[j].transform.name, carsDistance.ToString("0.0"));
}
}
}
refScore = score;
StudentOperateRecorder.Instance.InjectDecScore(score, EvaluationFactorsEnum.车辆间距.ToString());
}
//根据车辆/图标名称获取所选车辆类型
private List GetCarTypes(List carCtrls) where T : Component
{
List names = new List();
for (int i = 0; i < carCtrls.Count; i++)
{
string type = carCtrls[i].transform.name.Split('(')[0];//取括号前的部分
if (!names.Contains(type))
names.Add(type);
}
return names;
}
///
///计算两个厂区之间的间距
///
private float GetAreasDistance(List area1, List area2)
{
//if (area1.Count == 0 || area2.Count == 0) return -1;//负一代表无效数据 出现未安置车辆的厂区时为-1
float nearestDistance = float.MaxValue;
for (int i = 0; i < area1.Count; i++)
{
for (int j = 0; j < area2.Count; j++)//遍历两个数组 依次计算两个数组中的所有元素的间距 记录最小间距
{
float currenDis = Vector3.Distance(area1[i].transform.position, area2[j].transform.position);
if (currenDis < nearestDistance)
nearestDistance = currenDis;
}
}
if (area1.Count == 1 && "Flag" == LayerMask.LayerToName(area1[0].gameObject.layer)) //厂区1 内部无车辆
{
nearestDistance -= area1[0].GetComponent().Radius; //以厂区1边缘计算距离 需扣除厂区1半径
if (area2.Count == 1 && "Flag" == LayerMask.LayerToName(area2[0].gameObject.layer))//厂区2内部也无车辆
{
nearestDistance -= area2[0].GetComponent().Radius;//以厂区2边缘计算距离 需扣除厂区2半径
}
}
if (nearestDistance < 0)//两个厂区有交集时 避免出现负数
nearestDistance = 0;
return nearestDistance;
}
///
/// 评估修理时在各厂区间的转运时间 暂时使用直线距离计算 后续若出现厂区中阻挡目标的情况时 使用寻路系统获取距离
///
///
private int AppraiseRepairTime(Dictionary> areasDistanceDic)
{
if (areasDistanceDic.Count < 10)
{
Debug.Log("存在空厂区,无法判定转运时间");
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("存在未安置装备的厂区,无法判定距离和转运时间!");
return 0;
}
else
{
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("按照15km/h速度计算转运时间:");//15KM每小时 等于4.16米每秒
List repairOrder = new List() { AreaNameEnum.接收场.ToString(), AreaNameEnum.洗消场.ToString(), AreaNameEnum.弹药场.ToString(), AreaNameEnum.油料场.ToString(), AreaNameEnum.待修装备停放场.ToString(), AreaNameEnum.修理场.ToString(), AreaNameEnum.修竣装备停放场.ToString(), AreaNameEnum.油料场.ToString(), AreaNameEnum.弹药场.ToString() };
float totalTime = 0;
for (int i = 0; i < repairOrder.Count - 1; i++)//依次计算 两场地之间的转运时间
{
totalTime += AppraiseTwoAreaRepairTime(areasDistanceDic, repairOrder[i], repairOrder[i + 1]);
}
System.TimeSpan ts = System.TimeSpan.FromSeconds(totalTime);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("修理厂转运总用时:{0}时{1}分{2}秒", ts.Hours, ts.Minutes, ts.Seconds);
return (int)totalTime;
}
}
///
/// 获取在两个厂区间的转运时间
///
///
///
/// 耗时
private float AppraiseTwoAreaRepairTime(Dictionary> areasDistanceDic, string area1, string area2)
{
float dis = areasDistanceDic[area1][area2];
float time = dis / 4.16f;
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}距离{1}:{2}米,预计转运用时:{3}秒", area1, area2, dis.ToString("0"), time.ToString("0.0"));
return time;
}
#endregion
//部分科目共用的评估方法
#region
///
/// 获取编队中车型部分扣分分值 并梳理当前车队中的人员种类列表 种类+数量 排除允许增加车型后的多余车型默认扣1分 超出最大数量扣0.5分
///
/// 编组集合
/// 编组名称
/// 标准车型
/// 多出但不扣分的车型
/// 编组当前数量
/// 编组最大允许数量 不计算最大数量时赋0
/// 当前分数字段 传入引用
/// 排除允许增加车型后的多余车型扣分 默认1
/// 缺少但不扣分的车型 默认1
private void GetCarTypesScore(List carTypes, string groupName, List dueCars, float lessDueScore, List notDeductCars, int groupCount, int maxCount, /*ref float deductsScore,*/ float unnecessOneScore = 1, List lessNotDeductCars = null)
{
float deductsScore = 0;
List anyCars = GetAny(dueCars, carTypes);//梳理缺少的车型
if (lessNotDeductCars != null)
anyCars = GetAny(anyCars, lessNotDeductCars);
List unnecessaryCars = GetAny(carTypes, dueCars);//梳理多出的车型
List deductCars;
if (notDeductCars != null)
deductCars = GetAny(unnecessaryCars, notDeductCars);//梳理多出车型中需要扣分的车型
else//多出的车型都需要扣分
deductCars = unnecessaryCars;
if (anyCars.Count > 0)
{
deductsScore += (anyCars.Count * lessDueScore);
string anyCarsStr = GetStrFromList(anyCars);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}缺少标准车型:{1},扣{2}分", groupName, anyCarsStr, anyCars.Count * lessDueScore);
}
if (deductCars.Count > 0)
{
deductsScore += (deductCars.Count * 1f);
string deductsCarsStr = GetStrFromList(deductCars);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}增加车型:{1}\u201C等\u201D{2}种,扣{3}分", groupName, deductsCarsStr, deductCars.Count, deductCars.Count * unnecessOneScore);
}
StudentOperateRecorder.Instance.InjectDecScore(deductsScore, EvaluationFactorsEnum.型号组成.ToString());
deductsScore = 0;//记录后清空开始计算装备数量扣分情况
if (maxCount != 0 && groupCount > maxCount)//部分组别无需评估最大数量
{
deductsScore += ((groupCount - maxCount) * 0.5f);
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0}装备数量为{1}台,超出{2}台,扣{3}分", groupName, groupCount, maxCount, (groupCount - maxCount) * 0.5f);
}
StudentOperateRecorder.Instance.InjectDecScore( deductsScore, EvaluationFactorsEnum.装备数量.ToString());
}
#endregion
#region//装备修理科目评估
//判断维修逻辑是否正确 判断依据是前置步骤是否已经完成 完成规划返回空 未完成时返回错误描述
internal string AppraiseSteps(List rightSteps)
{
List checkOver = new List();//检查通过的步骤 认为规划正确可以完成
for (int i = 0; i < rightSteps.Count; i++)//一次检查规划的步骤
{
if (rightSteps[i].preIDs != null && rightSteps[i].preIDs.Length > 0)//存在前置步骤 需要判断前置步骤是否完成
{
for (int j = 0; j < rightSteps[i].preIDs.Length; j++)
{
StepData stepData = CollectionHelper.Find(checkOver, p => p.id == rightSteps[i].preIDs[j]);//在已完成的步骤中寻找是否存在前置步骤
if (stepData == default(StepData))//不存在前置步骤,代表前置步骤还未完成
{
string errorMsg;//规划步骤错误描述
stepData = CollectionHelper.Find(rightSteps, p => p.id == rightSteps[i].preIDs[j]);//在传入的所有步骤中查找是否存在前置步骤
if (stepData == default(StepData))//所有的步骤中都不存在前置步骤 证明教员端发送的步骤库有误 此时只记录 但认为该步骤通过
{
errorMsg = $"规划的维修步骤不完善,缺少id为{rightSteps[i].preIDs[j]}的操作步骤,导致{rightSteps[i].describe} 操作无法正确评估";
}
else
{
errorMsg = $"{rightSteps[i].describe} 之前需要先完成 {stepData.describe}";
return errorMsg;
}
StudentOperateRecorder.Instance.InjectOperateMsgLocalized("{0} 之前需要先完成 {1}", rightSteps[i].describe, stepData.describe);//记录评估结果
}
}
}
checkOver.Add(rightSteps[i]);//通过检查的添加
}
return string.Empty;
}
#endregion
}
//评估因素枚举 便于后续教师端调整权重时统一名称
public enum EvaluationFactorsEnum
{
//两科目共用本因素 连贯训练时累加扣分
型号组成,
装备数量,
人员工种,
人员数量,
//修理所展开时 厂区内部的车辆间距
车辆间距,
通路距离,
厂区间距,
}