// 所持カードを取得する private List <string>[] GetOwnCards(string[] lines, string[] shortPlayerNames) { string[] get_strings = new string[] { "受け取った。", "獲得した。", "購入・獲得した。", "廃棄置き場から獲得した。" }; string[] lost_strings = new string[] { "廃棄した。", "戻した。" }; string give_string = "渡した。"; // 仮面舞踏会 var ownCards = new List <string>[2] { new List <string>(), new List <string>() }; foreach (var line in lines) { var(name, action, cards, destination, inParentheses) = Extractor.Extract(line); for (int i = 0; i < 2; ++i) { if (name == shortPlayerNames[i]) { if (get_strings.Any(s => s == action)) { ownCards[i].AddRange(cards); } if (lost_strings.Any(s => s == action)) { foreach (var card in cards) { ownCards[i].Remove(card); } } if (give_string == action) { foreach (var card in cards) { ownCards[i].Remove(card); } int opponent = (i + 1) % 2; ownCards[opponent].AddRange(cards); } } } } return(ownCards); }
private void SaveLog(string[] lines, string[] shortPlayerNames) { var extractedLog = new StringBuilder(); foreach (var line in lines) { var(name, action, cards, destination, inParentheses) = Extractor.Extract(line); if (name != null && shortPlayerNames.Any(name.Equals) && action != null) { extractedLog.Append( "name = " + name + "\taction = " + action + "\tcards = " + string.Join(",", cards) + "\tdestination = " + destination + "\tinParentheses = " + inParentheses + Environment.NewLine); } } using (var sw = new System.IO.StreamWriter("extracted_log.txt")) sw.Write(extractedLog); using (var sw = new System.IO.StreamWriter("game_log.txt")) sw.Write(string.Join(Environment.NewLine, lines)); }
/// <summary>1行を処理する</summary> /// <param name="line">行</param> public void Transact(string line) { var myName = shortPlayerNames[myTurnNumber]; var opponentName = shortPlayerNames[(myTurnNumber + 1) % 2]; if (line.Contains("前哨地は不発となる。")) { myHand.Add("前哨地"); Remove(ref myDuration, new List <string> { "前哨地" }, "持続場に前哨地がありません"); return; } if (line.Contains("女魔術師により") && line.Contains("無効化された。")) { current_state = state.normal; myArchive = new List <string>(previousMyCards.myArchive); myBar = new List <string>(previousMyCards.myBar); myCrypt = new List <string>(previousMyCards.myCrypt); myDeck = new List <string>(previousMyCards.myDeck); myDiscard = new List <string>(previousMyCards.myDiscard); myDuration = new List <string>(previousMyCards.myDuration); myHand = new List <string>(previousMyCards.myHand); myPermanentDuration = new List <string>(previousMyCards.myPermanentDuration); return; } var(name, action, cards, destination, inParentheses) = Extractor.Extract(line); if (current_state.HasFlag(state.readyToReturn)) // 取り替え子対応のため、「戻した。」は次の行で処理する。 { if (name == myName && action == "受け取った。" && cards[0] == "取り替え子") { myDiscard.Add(cards[0]); if (myDiscard.Contains(returnedCards[0])) { myDiscard.Remove(returnedCards[0]); } else { throw new Exception("戻すカードが捨て札にありません。"); } } else { Remove(ref myHand, returnedCards, "戻すカードが手札にありません。"); } current_state ^= state.readyToReturn; } if (name == myName) { switch (action) { case "購入した。": // 購入するが獲得しない。獲得ログはこの後発生する。 current_state = state.normal; current_nonVolatileState |= nonVolatileState.duringBuy; // 伝令官の購入時効果:2019年10月20日現在、この効果で山札に行くカード名が匿名の「カード」となる不具合があるため正常動作しない。 if (cards[0] == "伝令官") { current_state |= state.discard_to_deck; } if (cards[0] == "医者") { current_state |= state.look_to_draw; } if (cards[0] == "召喚") { current_state |= state.getting_in_hand; } if (cards[0] == "義賊") { current_state |= state.open_to_draw; } if (cards[0] == "保存") { current_nonVolatileState |= nonVolatileState.save; } if (cards[0] == "偵察隊") { current_state |= state.look_to_draw; } if (cards[0] == "寄付") { current_nonVolatileState |= nonVolatileState.donate; } if (cards[0] == "技術革新") { current_nonVolatileState |= nonVolatileState.innovation; } if (cards[0] == "回廊") { current_nonVolatileState |= nonVolatileState.piazza; } if (cards[0] == "大地への塩まき") { current_state |= state.no_trash; } if (cards[0] == "併合") { current_state |= state.discard_to_deck; current_nonVolatileState |= nonVolatileState.no_shuffle; } break; case "購入・獲得した。": current_state = state.normal; current_nonVolatileState |= nonVolatileState.duringBuy; if (cards[0] == "遊牧民の野営地") { myDeck.AddRange(cards); } else if (cards[0] == "悪人のアジト" || cards[0] == "ゴーストタウン" || cards[0] == "守護者") { myHand.AddRange(cards); } else { myDiscard.AddRange(cards); } if (cards[0] == "石") { current_state |= state.getting_on_deck; } if (cards[0] == "ヴィラ") { current_state |= state.discard_to_hand; } if (cards[0] == "宿屋") { current_state |= state.discard_to_deck; current_nonVolatileState |= nonVolatileState.no_shuffle; } gotCard = cards[0]; break; case "受け取った。": case "獲得した。": case "廃棄置き場から獲得した。": if (current_state.HasFlag(state.getting_in_hand)) { myHand.AddRange(cards); } else if (current_state.HasFlag(state.getting_on_deck) || cards[0] == "遊牧民の野営地" || destination == "山札の上") { myDeck.AddRange(cards); } else if (cards[0] == "悪人のアジト" || cards[0] == "ゴーストタウン" || cards[0] == "守護者" || cards[0] == "夜警") { myHand.AddRange(cards); } else if (current_state.HasFlag(state.turn_start) && current_state.HasFlag(state.cobbler)) { myHand.AddRange(cards); } else { myDiscard.AddRange(cards); } if (cards[0] == "宿屋") { current_state |= state.discard_to_deck; current_nonVolatileState |= nonVolatileState.no_shuffle; } if (cards[0] == "石") { if (current_nonVolatileState.HasFlag(nonVolatileState.duringBuy)) { current_state |= state.getting_on_deck; } else { current_state |= state.getting_in_hand; } } if (cards[0] == "ヴィラ") { current_state |= state.discard_to_hand; } gotCard = cards[0]; break; case "シャッフルした。": if (current_nonVolatileState.HasFlag(nonVolatileState.no_shuffle)) { break; } numAtShuffle = myDeck.Count; myDeck.AddRange(myDiscard); myDiscard.Clear(); break; // 「〇を山札に混ぜシャッフルした。」は、直前に「山札をシャッフルした。」というログが現れるがこれは無視する。 case "混ぜシャッフルした。": if (destination != "山札") { throw new Exception("混ぜシャッフル失敗"); } myDeck.AddRange(cards); if (current_state.HasFlag(state.discard_to_deck)) { Remove(ref myDiscard, cards, "混ぜるカードが捨て札にありません。"); } else if (current_nonVolatileState.HasFlag(nonVolatileState.donate)) { Remove(ref myDiscard, cards, "混ぜるカードが捨て札にありません。"); current_nonVolatileState ^= nonVolatileState.donate; } else { Remove(ref myHand, cards, "混ぜるカードが手札にありません。"); } current_nonVolatileState ^= nonVolatileState.no_shuffle; break; case "引いた。": case "指定し、的中した。": // 願いの井戸、秘術師。的中しない場合のテキストは「Tを銅貨を指定したが、香辛料商人が公開された。」のように主語の後が「を」になっていて解析に失敗するが無視しても問題なし。 Remove(ref myDeck, cards, "引くカードが山札にありません。"); myHand.AddRange(cards); if (current_nonVolatileState.HasFlag(nonVolatileState.donate)) { myDeck.AddRange(myDiscard); myDiscard.Clear(); current_nonVolatileState |= nonVolatileState.no_shuffle; } break; case "見た。": if (current_state.HasFlag(state.look_to_draw)) { Remove(ref myDeck, cards, "引くカードが山札にありません。"); myHand.AddRange(cards); } break; case "クリーンアップした。": myDiscard.AddRange(myHand); myHand.Clear(); current_state = state.normal; current_nonVolatileState ^= nonVolatileState.duringBuy; break; case "捨て札にした。": if (cards.Any() && (boons.Any(cards[0].Equals) || hexes.Any(cards[0].Equals))) { break; } if (current_state.HasFlag(state.deck_to_discard) || current_state.HasFlag(state.vassal)) { myDiscard.AddRange(cards); Remove(ref myDeck, cards, "捨てるカードが山札にありません。"); if (current_state == state.vassal) { vassalDiscard = cards[0]; } } else if (current_state.HasFlag(state.afterBuy) && cards.Contains("ワイン商")) { myDiscard.AddRange(cards); Remove(ref myBar, cards, "捨てるカードが酒場マットにありません。"); } else { myDiscard.AddRange(cards); Remove(ref myHand, cards, "捨てるカードが手札にありません。"); if (current_state.HasFlag(state.secretCave) && cards.Count == 3) { myDuration.Add("秘密の洞窟"); myHand.Remove("秘密の洞窟"); } } break; case "廃棄した。": if (current_state.HasFlag(state.no_trash)) { break; } if (cards.Contains("城塞")) { current_state |= state.fortress; } if (current_state.HasFlag(state.deck_to_trash)) { Remove(ref myDeck, cards, "廃棄するカードが山札にありません。"); } else if (current_state.HasFlag(state.discard_to_trash)) { Remove(ref myDiscard, cards, "廃棄するカードが捨て札にありません。"); } else if (current_state.HasFlag(state.hermit)) { if (myHand.Contains(cards[0])) { myHand.Remove(cards[0]); } else { Remove(ref myDiscard, cards, "廃棄するカードが捨て札にありません。"); } } else { Remove(ref myHand, cards, "廃棄するカードが手札にありません。"); } if (cards.Contains("石")) { if (current_nonVolatileState.HasFlag(nonVolatileState.duringBuy)) { current_state |= state.getting_on_deck; } else { current_state |= state.getting_in_hand; } } break; case "終了した。": if (cards[0] == "購入フェイズ") // 購入フェイズを終了した。 { current_state |= state.afterBuy; } break; case "呼び出した。": myHand.AddRange(cards); Remove(ref myBar, cards, "呼び出すカードが酒場にありません。"); if (cards.Contains("変容")) { current_state |= state.getting_in_hand; } if (cards.Contains("御料車")) { reusing = "御料車"; } break; case "置いた。": if (current_state.HasFlag(state.no_put)) { break; } else if (cards.Any() && boons.Any(cards[0].Equals)) { break; // 恵みの村対応 } else if (current_state.HasFlag(state.discard_to_deck) && destination != "捨て札置き場") { if (cards.Any() && cards[0] == "カード") // 玉璽対応 { cards.Clear(); cards.Add(gotCard); } myDeck.AddRange(cards); Remove(ref myDiscard, cards, "置くカードが捨て札にありません。"); } else if (current_state.HasFlag(state.archive)) { myArchive.AddRange(cards); Remove(ref myDeck, cards, "置くカードが山札にありません。"); } else if (current_state.HasFlag(state.crypt)) { myCrypt.AddRange(cards); Remove(ref myHand, cards, "置くカードが手札にありません。"); } else if (current_state.HasFlag(state.hand_to_duration) || destination == "脇") { if (inParentheses == "貨物船") { myDuration.AddRange(cards); Remove(ref myDiscard, cards, "置くカードが捨て札にありません。"); myDuration.Add("貨物船"); Remove(ref myHand, new List <string> { "貨物船" }, "貨物船が手札にありません。"); // 貨物船は貨物を入れたときに持続場に入る } else if (inParentheses == "道具") { myDuration.AddRange(cards); Remove(ref myHand, cards, "置くカードが手札にありません。"); myDuration.Add("道具"); Remove(ref myHand, new List <string> { "道具" }, "道具が手札にありません。"); // 道具は1枚以上のカードを持続場に入れたときに持続場に入る } else if (current_state.HasFlag(state.research)) { myDuration.AddRange(cards); Remove(ref myDeck, cards, "置くカードが山札にありません。"); myDuration.Add("研究"); Remove(ref myHand, new List <string> { "研究" }, "研究が手札にありません。"); // 研究は何かを持続場に入れたときにと一緒に持続場に入る } else if (inParentheses == "原住民の村") { myNativeVillage.AddRange(cards); Remove(ref myDeck, cards, "置くカードが山札にありません。"); } else if (current_nonVolatileState.HasFlag(nonVolatileState.innovation)) { myHand.AddRange(cards); Remove(ref myDiscard, cards, "置くカードが捨て札にありません。"); } else { myDuration.AddRange(cards); Remove(ref myHand, cards, "置くカードが手札にありません。"); } } else if (destination == "島マット") { myIsland.AddRange(cards); Remove(ref myHand, cards, "置くカードが手札にありません。"); } else if (destination == "酒場マット") { myBar.AddRange(cards); Remove(ref myHand, cards, "置くカードが手札にありません。"); } else if (destination == "山札の上") { myDeck.AddRange(cards); Remove(ref myHand, cards, "置くカードが手札にありません。"); } if (cards.Any() && cards[0] == "山札" && destination == "捨て札置き場") { myDiscard.AddRange(myDeck); myDeck.Clear(); } break; case "加えた。": if (destination == "山札") { myDeck.AddRange(cards); Remove(ref myHand, cards, "置くカードが手札にありません。"); } else if (destination == "手札") { if (current_state.HasFlag(state.native_village)) { myHand.AddRange(cards); Remove(ref myNativeVillage, cards, "引くカードが原住民の村マットにありません。"); } else if (current_state.HasFlag(state.discard_to_hand)) { myHand.AddRange(cards); Remove(ref myDiscard, cards, "引くカードが捨て札にありません。"); } else if (current_state.HasFlag(state.turn_start)) { if (myArchive.Any() && inParentheses == "資料庫") { myHand.AddRange(cards); Remove(ref myArchive, cards, "引くカードが資料庫にありません。"); if (myArchive.FindIndex(m => m != "資料庫") == -1) { myHand.AddRange(myArchive); myArchive.Clear(); } } if (myCrypt.Any() && inParentheses == "納骨堂") { myHand.AddRange(cards); Remove(ref myCrypt, cards, "引くカードが納骨堂にありません。"); if (myCrypt.FindIndex(m => m != "納骨堂") == -1) { myHand.AddRange(myCrypt); myCrypt.Clear(); } } break; // ターン開始時に持続カードと同時に手札に加えているので無視する } else if (current_state.HasFlag(state.fortress)) { myHand.AddRange(cards); // 城塞を手札に加える } else if (current_nonVolatileState.HasFlag(nonVolatileState.save)) { myHand.AddRange(cards); Remove(ref myDuration, cards, "保存したカードがありません。"); current_nonVolatileState ^= nonVolatileState.save; } else if (inParentheses == "ターン終了時" && cards.Contains("忠犬")) { myHand.AddRange(cards); Remove(ref myDuration, cards, "忠犬がありません。"); } else { myHand.AddRange(cards); Remove(ref myDeck, cards, "引くカードが山札にありません。"); } } break; case "渡した。": Remove(ref myHand, cards, "渡すカードが手札にありません。"); break; case "開始した。": if (cards[0] == "ターン") // ターンを開始した。 { current_state |= state.turn_start; if (myDuration.Contains("カブラー")) { current_state |= state.cobbler; } myHand.AddRange(myDuration); myDuration.Clear(); if (current_nonVolatileState.HasFlag(nonVolatileState.piazza)) { current_state |= state.open_to_draw; } } break; case "リアクションした。": if (cards[0] == "玉璽") { current_state |= state.discard_to_deck; } if (cards[0] == "望楼") { current_state |= state.discard_to_deck | state.discard_to_trash; } if (cards[0] == "馬商人") { current_state |= state.hand_to_duration; } if (cards[0] == "愚者の黄金") { current_state |= state.getting_on_deck; } if (cards[0] == "移動遊園地") { current_state |= state.discard_to_deck; } if (cards[0] == "忠犬") { myHand.AddRange(cards); Remove(ref myDiscard, cards, "捨て札に忠犬がありません"); } if (cards[0] == "追跡者" && !current_state.HasFlag(state.getting_in_hand)) { current_state |= state.discard_to_deck; } break; case "公開した。": if (current_state.HasFlag(state.open_to_draw)) { if (current_state.HasFlag(state.famine)) { current_nonVolatileState |= nonVolatileState.no_shuffle; } myHand.AddRange(cards); Remove(ref myDeck, cards, "引くカードが山札にありません。"); } else if (current_state.HasFlag(state.herald) && CardList.actionCards.Any(cards[0].Equals)) { myHand.AddRange(cards); Remove(ref myDeck, cards, "引くカードが山札にありません。"); } break; case "戻した。": if (states.Any(cards[0].Equals)) { break; } if (cards[0].Contains("トークン")) { break; } if (cards.Contains("陣地") && destination == "陣地の山") { Remove(ref myDuration, cards, "戻すカードが脇にありません。"); } else if (current_state.HasFlag(state.no_return) && destination == "山札") { break; } else { current_state |= state.readyToReturn; returnedCards = cards; } break; case "受けた。": if (cards[0] == "月の恵み") { current_state |= state.discard_to_deck; } if (cards[0] == "太陽の恵み") { current_state |= state.look_to_draw; } if (cards[0] == "凶兆") { current_state |= state.discard_to_deck; } if (cards[0] == "貪欲") { current_state |= state.getting_on_deck; } if (cards[0] == "蝗害") { current_state |= state.deck_to_trash; } if (cards[0] == "疫病") { current_state |= state.getting_in_hand; } if (cards[0] == "戦争") { current_state |= state.open_to_draw; } if (cards[0] == "飢饉") { current_state |= state.open_to_draw; current_state |= state.famine; } break; } } else if (name == opponentName) { if (action == "渡した。") { myHand.AddRange(cards); } // 相手の隊商の護衛によるリアクションのカード使用でstateがリセットされないようにする if (action == "リアクションした。" && cards[0] == "隊商の護衛") { current_state |= state.no_use; } // 相手の聖なる木立ちは相手が祝福受けるログでこちらが祝福効果を受ける if (current_state.HasFlag(state.sacredGrove) && action == "受けた。" && cards[0] == "月の恵み") { current_state |= state.discard_to_deck; } if (current_state.HasFlag(state.sacredGrove) && action == "受けた。" && cards[0] == "太陽の恵み") { current_state |= state.look_to_draw; } } if (action == "使用した。") { if (current_state.HasFlag(state.no_use)) { current_state ^= state.no_use; } else { UseCard(cards[0], name == myName, false); } } if (action == "再使用した。" || action == "再々使用した。") { UseCard(cards[0], name == myName, true); } }