⇒ 動作確認はこちらからどうぞ。
徴兵・徴税・開発
どこからも攻め込まれていない敵の城 EnemyCastleIfNormalクラスをつくる前に徴兵や開発で税収を高めて有利に戦えるようにします。
まず徴兵ですが、兵数を2増やすことができることにします。それからこのゲームは兵糧がないと兵糧攻めで簡単に落城してしまうだけでなく、兵を動かすことすらできません。攻められたときには迎撃に出撃するために兵数と同じだけの兵糧が必要であり、敵の城に攻め込むには兵数の3倍の兵糧が必要です。
土地によって「収穫量」が異なり、収穫量によって徴税をしたときに増える兵糧の量に変化をもたせます。
徴税をすると収穫量の4分の1だけ兵糧が増えます。毎年秋になると収穫量と同じだけ兵糧が増えます。また開発をすると収穫量が5増える仕様にします。
そこでCastleStatusに以下を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public class CastleStatus { // 収穫量(初期値は20) public int Yield { get; set; } = 20; // 開発(収穫量が5増える) public void Develop() { Yield += 5; } // 徴税 public void TaxCollection() { MilitaryFood += Yield / 4; } // 徴兵 public void Conscription() { SoldierCount += 2; } } |
9月のターンになると(monthを4で割ったときの剰余が2のとき)兵糧が増えます。しかし兵糧攻めされているときは増えません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public partial class Index { void NextTurn() { month++; yearAtring = GetYearMonth(month); _situations.Add(new Situation(yearAtring + " になりました。", CastleStatusList)); CastleStatusList = GetShuffledCastles(); string s = ""; foreach (CastleStatus status in CastleStatusList) { s += " >> " + status.CastleName; } turn = s; if (month % 4 == 2) { _situations.Add(new Situation("秋になりました。城に米が納められました。", CastleStatusList)); foreach (CastleStatus status in CastleStatusList) { if(status.SiegingCorps == null) status.MilitaryFood += status.Yield; } } Turn(); } } |
どこからも攻め込まれていない敵の城 EnemyCastleIfNormalクラス
ではどこからも攻め込まれていない敵の城の処理を考えます。これまではプレーヤーの城であったりどこかから攻め込まれている城だけを考えてきました。攻め込まれている城にはできることが限られていましたが、どこからも攻め込まれていない敵の城であれば、別の敵に攻め込む、兵の移動、徴兵、徴税、開発などができます。
敵の行動パターンですが、以下のように考えます。
敵の城が隣接している場合
自分の城に兵が存在しない、または1しかいない場合は徴兵で兵を増やす
兵糧が3以下で兵を移動させることができない場合は徴税で兵糧を増やす
敵が複数ある場合は兵力が少ない順ものを選択する
攻撃目標として選択した城の兵力が3分の1より少ない場合は3倍の兵力で攻撃する
攻撃目標として選択した城の兵力が0のときは兵力1で攻撃する
攻撃目標を攻撃するために必要な兵糧が足りない場合は徴税をおこなう
攻めるターゲットよりも兵数が少ない場合は徴兵をする
攻めるターゲットよりも兵数が少ないわけではないが、3分の1より多い場合は乱数で適当に決める
乱数で決めた結果、攻撃しない場合は開発をする
優先順位としては攻め込み、すでに攻め込んでいる軍団の支援、すでに敵の城を包囲している軍団の支援
敵と隣接していない場合
隣接している味方の城のなかで包囲されている城があるなら応援派遣する
隣接している味方の城がまだ攻め込まれていないのであれば多くの敵に面している城に兵を送る
支援対象が存在する場合は兵を1だけ残して動かせるだけ動かすが、兵力2以下なら動かさずに開発をする
以上のように考えます。
ではEnemyCastleIfNormalクラスのコンストラクタを示します。
まず、敵にとって自分の城と敵にとって敵の城をそれぞれ取得します。
どこからも攻撃も包囲もされていない城や自分が攻撃・包囲している城であれば攻撃できますが、敵の城であってもべつの敵によって攻められている城は攻撃対象にならないので、攻撃対象になる城だけ取得します。
攻撃対象があればIfEnemyCastleHaveEnemyCastleメソッドでほんとうに攻撃するかを判断します。ない場合はIfEnemyCastleHaveNoEnemyCastleメソッドで味方の城に移動するかどうか判断します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
public class EnemyCastleIfNormal : CastleBase { public EnemyCastleIfNormal(Index obj, CastleStatus status) : base(obj, status) { status.Done = true; // 自分の城に兵が存在しない、または1しかいない場合。 if (status.SoldierCount <= 1) { status.SoldierCount += obj.GetConscription(status); obj.AddSituation(status.CastleName + " には兵がいないので徴兵がおこなわれました。"); return; } // 自分の城の兵糧が少なすぎる場合。 if (status.MilitaryFood <= 3) { obj.AddSituation("兵糧がたりないので" + status.CastleName + "は兵を動かせません。"); status.MilitaryFood += obj.GetTax(status); obj.AddSituation(status.CastleName + "では兵糧が足りないので徴税がおこなわれました。"); return; } // 敵にとって自分の城と敵にとって敵の城を取得 List<CastleStatus> friendlyCastles = GetFriendlyCastles(status); List<CastleStatus> enemyCastles = GetEnemyCastles(status); // べつの敵によって攻められている城は攻撃できないので、そうではない城だけ取得する List<CastleStatus> targetCastles = enemyCastles.Where(x => x.AttackingCorps == null && x.SiegingCorps == null).ToList(); // 自分が攻撃/包囲している城であれば攻撃できる targetCastles.AddRange(enemyCastles.Where(x => x.AttackingCorps?.BaseCastleOwner == status.CastleOwner).ToList()); targetCastles.AddRange(enemyCastles.Where(x => x.SiegingCorps?.BaseCastleOwner == status.CastleOwner).ToList()); if (targetCastles.Count > 0) IfEnemyCastleHaveEnemyCastle(obj, status, targetCastles); else IfEnemyCastleHaveNoEnemyCastle(obj, status, friendlyCastles); return; } } |
EnemyCastleIfNormal.IfEnemyCastleHaveEnemyCastleメソッドは攻撃対象が存在する場合、ほんとうに攻撃するのかどうかを判断し、進軍、徴兵、徴税、開発のどれかをおこないます。兵力はもっとも少ないものを選ぶために兵数を少ない順にソートして最初の要素を取得しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
public class EnemyCastleIfNormal : CastleBase { void IfEnemyCastleHaveEnemyCastle(Index obj, CastleStatus status, List<CastleStatus> targetCastles) { CastleStatus atackTarget = null; int soldiers = 0; // 隣接する敵の城がひとつしかない場合はそれを選ぶ if (targetCastles.Count == 1) atackTarget = targetCastles[0]; else if (targetCastles.Count > 1) { // 隣接する敵の城が複数ある場合は兵力を少ない順に並べかえて最初のものを選択する targetCastles = targetCastles.OrderBy(x => x.SoldierCount).ToList(); atackTarget = targetCastles[0]; } if (1d * atackTarget.SoldierCount < (1d * status.SoldierCount - 1) / 3) { // 攻撃目標として選択した城が兵力が3分の1より少ない場合 // 3倍の兵力で攻撃する // 攻撃目標として選択した城が兵力が0のときは兵力1で攻撃する soldiers = atackTarget.SoldierCount * 3; if (soldiers == 0) soldiers = 1; if (status.MilitaryFood < soldiers * 3) { // ただし兵糧が足りない場合は攻撃しないで徴税して兵糧を増やす status.MilitaryFood += obj.GetTax(status); obj.AddSituation(status.CastleName + "では兵糧が足りないので徴税がおこなわれました。"); return; } } else if (atackTarget.SoldierCount > status.SoldierCount) { // 攻撃目標として選択した城の兵力が自分の城よりも大きい場合 // 仕掛けるのはやめて兵を増やす status.SoldierCount += obj.GetConscription(status); obj.AddSituation(status.CastleName + " では徴兵がおこなわれました。"); return; } else { // それ以外の場合は乱数で決める // 攻めるときは兵力1だけ残して攻める if (obj.random.Next(3) == 0) { soldiers = status.SoldierCount - 1; if (status.MilitaryFood < soldiers * 3) { // ただし兵糧が足りない場合は攻撃しないで徴税して兵糧を増やす status.MilitaryFood += obj.GetTax(status); obj.AddSituation(status.CastleName + "では兵糧が足りないので徴税がおこなわれました。"); return; } } else { status.Develop(); obj.AddSituation(status.CastleName + " では開拓がすすめられました。"); return; } } Corps corps = status.SendingForAttack(soldiers); if (atackTarget.AttackingCorps == null && atackTarget.SiegingCorps == null) { atackTarget.AttackingCorps = corps; string str = status.CastleName + "の" + status.CastleOwner + "軍 が " + atackTarget.CastleName + " に攻め込むべく出陣しました。"; obj.AddSituation(str); return; } else if (atackTarget.AttackingCorps?.BaseCastleOwner == status.CastleOwner) { atackTarget.AttackingCorps.SoldierCount += corps.SoldierCount; atackTarget.AttackingCorps.MilitaryFood += corps.MilitaryFood; string str = status.CastleName + "の" + status.CastleOwner + "軍 が " + atackTarget.CastleName + " を攻撃している自軍を支援するために出陣しました。"; obj.AddSituation(str); return; } else if (atackTarget.SiegingCorps?.BaseCastleOwner == status.CastleOwner) { atackTarget.SiegingCorps.SoldierCount += corps.SoldierCount; atackTarget.SiegingCorps.MilitaryFood += corps.MilitaryFood; string str = status.CastleName + "の" + status.CastleOwner + "軍 が " + atackTarget.CastleName + " を包囲している自軍を支援するために出陣しました。"; obj.AddSituation(str); return; } } } |
EnemyCastleIfNormal.IfEnemyCastleHaveNoEnemyCastleメソッドは隣接している城が味方の城の場合の処理をおこないます。
隣接している味方の城のなかで攻撃されようとしている城や包囲されている城がある場合、応援派遣をします。優先順位はすでに包囲されている城、攻撃されようとしている城の順です。複数ある場合はより多くの敵から攻められている城を選びます。
まだ攻め込まれていないのであれば多くの敵に面している城に兵を送りますが、兵力2以下なら開発に専念します。
隣接している味方の城も敵と接していない場合、つまり後方の安全な城である場合は、自分の城以外の城のIDを調べて、少ないものが多ければIDがひとつ下の城に、大きいものが多い場合はひとつ上の城に移動させます。この場合も兵力2以下なら開発に専念します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
public class EnemyCastleIfNormal : CastleBase { void IfEnemyCastleHaveNoEnemyCastle(Index obj, CastleStatus status, List<CastleStatus> friendlyCastles) { CastleStatus supportTarget = null; // 隣接している味方の城のなかで包囲されている城を取得する List<CastleStatus> siegingCastles = friendlyCastles.Where(x => x.SiegingCorps != null).ToList(); // 隣接している味方の城のなかで攻撃されようとしている城を取得する List<CastleStatus> attackingCastles = friendlyCastles.Where(x => x.AttackingCorps != null).ToList(); // 隣接している城のなかで敵に隣接している城を取得する List<CastleStatus> frontlineFriendlyCastles = friendlyCastles.Where(x => GetEnemyCastles(x).Count != 0).ToList(); if (siegingCastles.Count > 0) { // 包囲されている城が複数あるときはより多くの敵に包囲されている城を選ぶ List<CastleStatus> list = siegingCastles.OrderByDescending(x => x.SiegingCorps.SoldierCount).ToList(); supportTarget = list[0]; } else if (attackingCastles.Count > 0) { // 包囲されている城がない場合 // 攻撃されそうな城のなかからより多くの敵に攻撃されようとしている城を選ぶ List<CastleStatus> list = attackingCastles.OrderByDescending(x => x.AttackingCorps.SoldierCount).ToList(); supportTarget = list[0]; } if (supportTarget != null) { // 救援が必要な城がみつかった場合、兵を1だけ残して動かせるだけ動かす int count = status.SoldierCount - 1; if (status.MilitaryFood < count * 3) count = status.MilitaryFood / 3; if (count > 0) { Corps corps = status.SendingForAttack(count); if (supportTarget.SupportCorps == null) supportTarget.SupportCorps = corps; else { supportTarget.SupportCorps.SoldierCount += corps.SoldierCount; supportTarget.SupportCorps.MilitaryFood += corps.MilitaryFood; } string str = status.CastleName + "の" + status.CastleOwner + "軍 が " + supportTarget.CastleName + " の友軍を支援すべく出陣しました。"; obj.AddSituation(str); } return; } // まだ攻め込まれていないのであれば多くの敵に面している城に兵を送る if (frontlineFriendlyCastles.Count > 0) { // 兵力2以下なら開拓に専念 if (status.SoldierCount <= 2) { status.Develop(); obj.AddSituation(status.CastleName + " では開拓がすすめられました。"); return; } int max = frontlineFriendlyCastles.Max(x => GetEnemyCastles(x).Count); supportTarget = frontlineFriendlyCastles.FirstOrDefault(x => GetEnemyCastles(x).Count == max); int count = status.SoldierCount - 1; if (status.MilitaryFood < count * 3) count = status.MilitaryFood / 3; Corps corps = status.SendingForAttack(count); if (supportTarget.SupportCorps == null) supportTarget.SupportCorps = corps; else { supportTarget.SupportCorps.SoldierCount += corps.SoldierCount; supportTarget.SupportCorps.MilitaryFood += corps.MilitaryFood; } string str = status.CastleName + "の" + status.CastleOwner + "軍 が " + supportTarget.CastleName + " の友軍を支援すべく出陣しました。"; obj.AddSituation(str); return; } else { // 隣接している味方の城のなかにも敵に面している城はない場合 // 兵力2以下なら開拓に専念 if (status.SoldierCount <= 2) { status.Develop(); obj.AddSituation(status.CastleName + " では開拓がすすめられました。"); return; } // 敵一般を取得する List<CastleStatus> enemiesBigID = obj.CastleStatusList.Where(x => x.CastleName != status.CastleOwner && x.CastleID > status.CastleID).ToList(); List<CastleStatus> enemiesSmallID = obj.CastleStatusList.Where(x => x.CastleName != status.CastleOwner && x.CastleID < status.CastleID).ToList(); if (enemiesBigID.Count == 0) supportTarget = GetCatsleFromID(obj, status.CastleID - 1); // 東へ移動 else if (enemiesSmallID.Count == 0) supportTarget = GetCatsleFromID(obj, status.CastleID + 1); // 西へ移動 else if (enemiesBigID.Count > enemiesSmallID.Count) supportTarget = GetCatsleFromID(obj, status.CastleID + 1); // 西へ移動 else if (enemiesBigID.Count <= enemiesSmallID.Count) supportTarget = GetCatsleFromID(obj, status.CastleID - 1); // 東へ移動 } if (supportTarget != null) { int count = status.SoldierCount - 1; if (status.MilitaryFood < count * 3) count = status.MilitaryFood / 3; Corps corps = status.SendingForAttack(count); if (supportTarget.SupportCorps == null) supportTarget.SupportCorps = corps; else { supportTarget.SupportCorps.SoldierCount += corps.SoldierCount; supportTarget.SupportCorps.MilitaryFood += corps.MilitaryFood; } string str = status.CastleName + "の" + status.CastleOwner + "軍 が " + supportTarget.CastleName + " の友軍を支援すべく出陣しました。"; obj.AddSituation(str); } } CastleStatus GetCatsleFromID(Index obj, int id) { return obj.CastleStatusList.FirstOrDefault(x => x.CastleID == id); } } |