root/lang/csharp/DominionEngine/DominionEngine.Core/Game.cs @ 37703

Revision 37703, 20.5 kB (checked in by isaisstillalive, 3 years ago)
  • CardInfoのシングルトンインスタンスをCardInfoアセンブリとCoreアセンブリで別々に作るという意味のわからないことをしてしまっていたため、一元化
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using System.Collections.ObjectModel;
6using DominionEngine.CardInfo;
7using System.Threading;
8
9namespace DominionEngine
10{
11    /// <summary>
12    /// ゲーム
13    /// </summary>
14    public class Game
15    {
16        #region ランダマイザ
17
18        internal Random Random = new Random();
19        public int RandomSeed { set { Random = new Random(value); } }
20
21        #endregion
22
23        #region メタ情報
24
25        /// <summary>
26        /// 非同期かどうか
27        /// </summary>
28        public bool IsAsync { get { return async != null; } }
29        internal Thread async;
30
31        /// <summary>
32        /// ゲームが進行中かどうか
33        /// </summary>
34        public bool IsPlaying { get; internal set; }
35
36        /// <summary>
37        /// ゲームの終了条件を満たしたかどうか
38        /// </summary>
39        public bool IsEndOfGame
40        {
41            get
42            {
43                // Provinceが空なら終了
44                if (provinceSupplyPile.Count == 0) return true;
45
46                // 3人までなら3山。4人以上なら4山切れたら終了
47                int emptySupplies = supplyPiles.Count(supply => supply.Count == 0);
48                return (emptySupplies >= (Players.Count <= 4 ? 3 : 4));
49            }
50        }
51
52        #endregion
53
54        #region 情報
55
56        /// <summary>
57        /// 現在のターン数
58        /// </summary>
59        public int Turn { get; internal set; }
60
61        /// <summary>
62        /// 現在のフェイズ
63        /// </summary>
64        public Phase Phase { get{return phase;}
65            internal set
66            {
67                phase = value;
68                PhaseChanged(this, EventArgs.Empty);
69            }
70        }
71        internal Phase phase;
72
73        /// <summary>
74        /// 購入フェイズに一度でも購入を行ったか
75        /// </summary>
76        public bool IsBuyed { get; internal set; }
77
78        /// <summary>
79        /// 残アクション回数
80        /// </summary>
81        public int RemainAction { get; internal set; }
82
83        /// <summary>
84        /// 残購入回数
85        /// </summary>
86        public int RemainBuy { get; internal set; }
87
88        /// <summary>
89        /// 利用可能な価値
90        /// </summary>
91        public Worth SpendableWorth { get; set; }
92
93        #endregion
94
95        #region プレイヤー
96
97        /// <summary>
98        /// プレイヤー
99        /// </summary>
100        public IList<PlayerInfo> Players { get; internal set; }
101
102        /// <summary>
103        /// 現在のターンプレイヤー位置
104        /// </summary>
105        internal int turnPlayerPosition;
106
107        /// <summary>
108        /// 現在のターンプレイヤー
109        /// </summary>
110        public PlayerInfo TurnPlayer { get { return Players[turnPlayerPosition]; } }
111
112        /// <summary>
113        /// プレイヤーを初期化する
114        /// </summary>
115        /// <param name="playerCount">プレイヤー人数</param>
116        internal void InitializePlayers(int playerCount)
117        {
118            // プレイヤー人数は2~6人。それ以外なら例外発生
119            if (playerCount < 2 || playerCount > 6) throw new ArgumentOutOfRangeException("playerCount", playerCount, "playerCount between 2 and 6");
120
121            // プレイヤーのリストを作成
122            PlayerInfo[] players = new PlayerInfo[playerCount];
123            Players = new ReadOnlyCollection<PlayerInfo>(players);
124
125            for (int i = 0; i < playerCount; i++)
126            {
127                players[i] = new PlayerInfo(this, i);
128            }
129        }
130
131        #endregion
132
133        #region 場
134
135        #region トラッシュ
136
137        /// <summary>
138        /// トラッシュパイル
139        /// </summary>
140        public ICardList TrashPile { get; internal set; }
141        internal CardList trashPile = new CardList();
142
143        /// <summary>
144        /// トラッシュパイルを初期化する
145        /// </summary>
146        internal void InitializeTrashPile()
147        {
148            TrashPile = trashPile.AsReadOnly();
149        }
150
151        #endregion
152
153        #region サプライ
154
155        /// <summary>
156        /// サプライパイルのリスト
157        /// </summary>
158        public IList<IUnitaryCardList> SupplyPiles { get; internal set; }
159        internal List<IUnitaryCardList> readOnlySupplyPiles = new List<IUnitaryCardList>();
160
161        /// <summary>
162        /// サプライパイルのリスト
163        /// </summary>
164        internal IList<IUnitaryCardList> supplyPiles = new SupplyCollection();
165
166        /// <summary>
167        /// 終了条件取得用Provinceサプライパイル
168        /// </summary>
169        internal IUnitaryCardList provinceSupplyPile;
170
171        /// <summary>
172        /// サプライパイルを初期化する
173        /// </summary>
174        internal void InitializeSupplyPiles()
175        {
176            supplyPiles.Clear();
177            readOnlySupplyPiles.Clear();
178            SupplyPiles = readOnlySupplyPiles.AsReadOnly();
179
180            SetSupply<Copper>();
181            SetSupply<Silver>();
182            SetSupply<Gold>();
183            SetSupply<Estate>();
184            SetSupply<Duchy>();
185            SetSupply<Province>();
186            SetSupply<Curse>();
187
188            provinceSupplyPile = supplyPiles.First(supply => supply.CardInfo is Province);
189        }
190
191        /// <summary>
192        /// 使用するサプライを追加する
193        /// </summary>
194        /// <typeparam name="T"></typeparam>
195        public void SetSupply<T>()
196            where T : ICardInfo, new()
197        {
198            UnitaryCardList add = new UnitaryCardList<T>(Players.Count);
199            supplyPiles.Add(add);
200            readOnlySupplyPiles.Add(add.AsReadOnly());
201        }
202
203        /// <summary>
204        /// 使用するサプライを除去する
205        /// </summary>
206        /// <typeparam name="T"></typeparam>
207        public void UnsetSupply<T>()
208            where T : ICardInfo, new()
209        {
210            supplyPiles.Remove(supplyPiles.FirstOrDefault(cardList => cardList.CardInfo is T));
211            readOnlySupplyPiles.Remove(readOnlySupplyPiles.FirstOrDefault(cardList => cardList.CardInfo is T));
212        }
213
214        #endregion
215
216        #endregion
217
218        #region CardInfo側イベント
219
220        /// <summary>
221        /// カードリスト取得
222        /// </summary>
223        /// <param name="sender"></param>
224        /// <param name="e"></param>
225        internal void AnyPlayer_CardListGetting(object sender, AnyPlayer.CardListGettingEventArgs e)
226        {
227            ICardList cardList = GetCardList(e.Position);
228            List<CardInfo.Card> result = new List<CardInfo.Card>();
229
230            foreach (var card in cardList)
231            {
232                result.Add(new CardInfo.Card(card.CardInfo, e.Position));
233            }
234
235            e.Result = result;
236        }
237
238        /// <summary>
239        /// ドロー
240        /// </summary>
241        /// <param name="sender"></param>
242        /// <param name="e"></param>
243        internal void AnyPlayer_Drawing(object sender, AnyPlayer.DrawingEventArgs e)
244        {
245            Players[e.Player].Draw(e.Quantity);
246        }
247
248        /// <summary>
249        /// カード選択
250        /// </summary>
251        /// <param name="sender"></param>
252        /// <param name="e"></param>
253        internal void AnyPlayer_Choosing(object sender, AnyPlayer.ChoosingEventArgs e)
254        {
255            CardChoosingEventArgs args = new CardChoosingEventArgs(e.Player, e.MinQuantity, e.MaxQuantity, e.Filter);
256
257            switch (e.From.Position)
258            {
259                        case CardPosition.PositionEnum.Deck:
260                case CardPosition.PositionEnum.Discard:
261                case CardPosition.PositionEnum.Hand:
262                case CardPosition.PositionEnum.PlayArea:
263                    HandChoosing(this, args);
264                    break;
265                case CardPosition.PositionEnum.Supply:
266                    SupplyChoosing(this, args);
267                    break;
268                default:
269                    break;
270            }
271
272
273            IList<ICardInfo> result = args.Result;
274            if (result == null) result = new ICardInfo[0];
275
276            if ((!e.Force && result.Count < e.MinQuantity) || (result.Count > e.MaxQuantity))
277            {
278                // 選びなおし
279            }
280
281            e.Result = new List<CardInfo.Card>();
282            foreach (ICardInfo cardInfo in result)
283            {
284                e.Result.Add(new CardInfo.Card(cardInfo, e.From));
285            }
286        }
287
288        /// <summary>
289        /// カードを移動させる
290        /// </summary>
291        /// <param name="sender"></param>
292        /// <param name="e"></param>
293        internal void Card_Moving(object sender, DominionEngine.CardInfo.Card.MovingEventArgs e)
294        {
295            ICardList fromCardList = GetCardList(e.From.Position);
296            DominionEngine.Card card = fromCardList.First(findCard => findCard.CardInfo == e.From.CardInfo);
297
298            ICardList toCardList = GetCardList(e.ToPosition);
299
300            toCardList.Add(card);
301            fromCardList.Remove(card);
302        }
303
304        /// <summary>
305        /// +X Actions
306        /// </summary>
307        /// <param name="sender"></param>
308        /// <param name="e"></param>
309        internal void TurnPlayer_ActionChanged(object sender, ValueChangedEventArgs e)
310        {
311            RemainAction += e.Value;
312        }
313
314        /// <summary>
315        /// +X Buy
316        /// </summary>
317        /// <param name="sender"></param>
318        /// <param name="e"></param>
319        internal void TurnPlayer_BuyChanged(object sender, ValueChangedEventArgs e)
320        {
321            RemainBuy += e.Value;
322        }
323
324        /// <summary>
325        /// +(X)
326        /// </summary>
327        /// <param name="sender"></param>
328        /// <param name="e"></param>
329        internal void TurnPlayer_CoinChanged(object sender, ValueChangedEventArgs e)
330        {
331            SpendableWorth += Worth.Coin(e.Value);
332        }
333
334        #endregion
335
336        /// <summary>
337        /// 指定した位置のカード一覧を取得する
338        /// </summary>
339        /// <param name="position"></param>
340        /// <returns></returns>
341        internal ICardList GetCardList(CardPosition position)
342        {
343            switch (position.Position)
344            {
345                case CardPosition.PositionEnum.Deck:
346                    return Players[position.Player].deck;
347
348                case CardPosition.PositionEnum.Discard:
349                    return Players[position.Player].discardPile;
350
351                case CardPosition.PositionEnum.Hand:
352                    return Players[position.Player].hand;
353
354                case CardPosition.PositionEnum.PlayArea:
355                    return Players[position.Player].playArea;
356
357                case CardPosition.PositionEnum.Supply:
358                    return (SupplyCollection)supplyPiles;
359
360                case CardPosition.PositionEnum.Trash:
361                    return trashPile;
362
363                default:
364                    return null;
365            }
366        }
367
368        #region イベント
369
370        /// <summary>
371        /// ターンが変更された
372        /// </summary>
373        public event EventHandler<EventArgs> TurnChanged = (sender, e) => { };
374
375        /// <summary>
376        /// フェイズが変更された
377        /// </summary>
378        public event EventHandler<EventArgs> PhaseChanged = (sender, e) => { };
379
380        /// <summary>
381        /// アクションフェイズ
382        /// </summary>
383        public event EventHandler<EventArgs> ActionPhase = (sender, e) => { };
384
385        /// <summary>
386        /// 購入フェイズ
387        /// </summary>
388        public event EventHandler<EventArgs> BuyPhase = (sender, e) => { };
389
390        /// <summary>
391        /// 手札からカードを選択
392        /// </summary>
393        public event EventHandler<CardChoosingEventArgs> HandChoosing = (sender, e) => { };
394
395        /// <summary>
396        /// サプライからカードを選択
397        /// </summary>
398        public event EventHandler<CardChoosingEventArgs> SupplyChoosing = (sender, e) => { };
399
400        /// <summary>
401        /// カード選択イベント引数
402        /// </summary>
403        public class CardChoosingEventArgs : EventArgs
404        {
405            /// <summary>
406            /// プレイヤー
407            /// </summary>
408            public int Player { get; private set; }
409
410            /// <summary>
411            /// 最小枚数
412            /// </summary>
413            public int MinQuantity { get; private set; }
414
415            /// <summary>
416            /// 最大枚数
417            /// </summary>
418            public int MaxQuantity { get; private set; }
419
420            /// <summary>
421            /// フィルタ
422            /// </summary>
423            public Func<ICardInfo, bool> Filter { get; private set; }
424
425            /// <summary>
426            /// 結果
427            /// </summary>
428            public IList<ICardInfo> Result { get; set; }
429
430            /// <summary>
431            /// 最小枚数に満たないが限界まで選択した
432            /// </summary>
433            public bool Force { get; set; }
434
435            /// <summary>
436            /// コンストラクタ
437            /// </summary>
438            /// <param name="minQuantity">最小枚数</param>
439            /// <param name="maxQuantity">最大枚数</param>
440            /// <param name="filter">フィルタ</param>
441            public CardChoosingEventArgs(int player, int minQuantity, int maxQuantity, Func<ICardInfo, bool> filter)
442            {
443                Player = player;
444                MinQuantity = minQuantity;
445                MaxQuantity = maxQuantity;
446                Filter = filter;
447                Force = false;
448            }
449        }
450
451        #endregion
452
453        /// <summary>
454        /// 新しいゲームを作成する
455        /// </summary>
456        /// <param name="playerCount">プレイヤー人数</param>
457        public Game(int playerCount)
458        {
459            InitializePlayers(playerCount);
460            InitializeTrashPile();
461            InitializeSupplyPiles();
462
463            CardInfo.AnyPlayer.ClearEvent();
464            CardInfo.AnyPlayer.CardListGetting += AnyPlayer_CardListGetting;
465            CardInfo.AnyPlayer.Drawing += AnyPlayer_Drawing;
466            CardInfo.AnyPlayer.Choosing += AnyPlayer_Choosing;
467            CardInfo.Card.Moving += Card_Moving;
468
469
470            CardInfo.TurnPlayer.ClearEvent();
471            CardInfo.TurnPlayer.ActionChanged += TurnPlayer_ActionChanged;
472            CardInfo.TurnPlayer.BuyChanged += TurnPlayer_BuyChanged;
473            CardInfo.TurnPlayer.CoinChanged += TurnPlayer_CoinChanged;
474        }
475
476        /// <summary>
477        /// カードをプレイする
478        /// </summary>
479        /// <param name="card"></param>
480        public void Play(Card card)
481        {
482            switch (Phase)
483            {
484                case Phase.Action:
485                    if (!(card.CardInfo is IAction)) return;
486
487                    TurnPlayer.hand.Remove(card);
488                    TurnPlayer.playArea.Add(card);
489                    RemainAction -= 1;
490                    ((IAction)card.CardInfo).Action();
491                    break;
492
493                case Phase.Buy:
494                    if (IsBuyed) return;
495                    if (!(card.CardInfo is ITreasure)) return;
496
497                    TurnPlayer.hand.Remove(card);
498                    TurnPlayer.playArea.Add(card);
499                    SpendableWorth += ((ITreasure)card.CardInfo).GenerateWorth;
500                    break;
501
502                default:
503                    return;
504            }
505        }
506
507        /// <summary>
508        /// 購入
509        /// </summary>
510        public Card Buy(ICardInfo cardInfo)
511        {
512            if (Phase != Phase.Buy) return null;
513            IsBuyed = true;
514            if (RemainBuy <= 0) return null;
515
516            ICardList supply = supplyPiles.First(cardList => cardList.CardInfo == cardInfo);
517            Card card = supply.First();
518            TurnPlayer.Gain(card);
519            supply.Remove(card);
520
521            SpendableWorth -= cardInfo.Cost;
522            RemainBuy -= 1;
523
524            return card;
525        }
526
527        /// <summary>
528        /// 購入
529        /// </summary>
530        public Card Buy<T>()
531            where T : ICardInfo, new()
532        {
533            return Buy(CardInfo<T>.Instance);
534        }
535
536        #region プレイング処理
537
538        #region ゲーム
539
540        /// <summary>
541        /// ゲームを開始する
542        /// </summary>
543        public void Start()
544        {
545            IsPlaying = true;
546
547            turnPlayerPosition = 0;
548            foreach (var item in EachTurns())
549            {
550                if (!IsPlaying) break;
551                ExecuteTurn();
552            }
553
554            async = null;
555            IsPlaying = false;
556        }
557
558        /// <summary>
559        /// 非同期でゲームを開始する
560        /// </summary>
561        public void StartAsync()
562        {
563            async = new Thread(Start);
564            async.Start();
565        }
566
567        /// <summary>
568        /// ゲームを中断する
569        /// </summary>
570        public void Abort()
571        {
572            if (async != null)
573            {
574                async.Abort();
575                async = null;
576            }
577            IsPlaying = false;
578        }
579
580        /// <summary>
581        /// ゲームが終了するまでターンを進行する
582        /// </summary>
583        /// <returns></returns>
584        internal IEnumerable<int> EachTurns()
585        {
586            Turn = -1;
587            while (!IsEndOfGame)
588            {
589                yield return ++Turn;
590            }
591        }
592
593        #endregion
594
595        #region ターン
596
597        /// <summary>
598        /// ターンを進行する
599        /// </summary>
600        internal void ExecuteTurn()
601        {
602            InitializeTurn();
603            TurnChanged(this, EventArgs.Empty);
604
605            ExecuteActionPhase();
606            ExecuteBuyPhase();
607            ExecuteCleanUpPhase();
608
609            turnPlayerPosition = (turnPlayerPosition + 1) % Players.Count;
610            Global.TurnPlayer = turnPlayerPosition;
611        }
612
613        /// <summary>
614        /// ターンのデータを初期化
615        /// </summary>
616        internal void InitializeTurn()
617        {
618            RemainAction = 1;
619            RemainBuy = 1;
620            SpendableWorth = Worth.None;
621        }
622
623        #endregion
624
625        #region フェイズ
626
627        /// <summary>
628        /// アクションフェイズ
629        /// </summary>
630        internal void ExecuteActionPhase()
631        {
632            Phase = Phase.Action;
633
634            ActionPhase(this, EventArgs.Empty);
635            WaitPhaseFinished();
636        }
637
638        /// <summary>
639        /// 購入フェイズ
640        /// </summary>
641        internal void ExecuteBuyPhase()
642        {
643            Phase = Phase.Buy;
644            IsBuyed = false;
645
646            BuyPhase(this, EventArgs.Empty);
647            WaitPhaseFinished();
648        }
649
650        /// <summary>
651        /// クリーンアップフェイズ
652        /// </summary>
653        internal void ExecuteCleanUpPhase()
654        {
655            Phase = Phase.CleanUp;
656
657            TurnPlayer.CleanUp();
658        }
659
660        /// <summary>
661        /// フェイズを終了する
662        /// </summary>
663        internal void FinishPhase()
664        {
665            phaseWaiting = false;
666        }
667
668        /// <summary>
669        /// フェイズ終了まで待つ
670        /// </summary>
671        internal void WaitPhaseFinished()
672        {
673            if (!IsAsync) return;
674
675            phaseWaiting = true;
676            while (phaseWaiting) Thread.Sleep(0);
677        }
678
679        internal bool phaseWaiting = false;
680
681        #endregion
682       
683        #endregion
684    }
685
686    /// <summary>
687    /// フェイズ
688    /// </summary>
689    public enum Phase
690    {
691        /// <summary>
692        /// アクション
693        /// </summary>
694        Action,
695
696        /// <summary>
697        /// 購入
698        /// </summary>
699        Buy,
700
701        /// <summary>
702        /// クリーンアップ
703        /// </summary>
704        CleanUp,
705    }
706}
Note: See TracBrowser for help on using the browser.