/// <summary> /// データを追加するぜ☆(^▽^) 指しながら定跡を追加していくときだぜ☆ /// </summary> /// <param name="ky_before"></param> public JosekiKyokumen AddMove(string kyFen_before, ulong kyHash_before, Option <Phase> optionalPhaseBeforeMove, Move bestMove, Hyokati hyokati, int fukasa, int version, StringBuilder syuturyoku) { JosekiKyokumen josekiKy = this.ParseKyokumenLine(kyFen_before, kyHash_before, optionalPhaseBeforeMove, syuturyoku); //#if DEBUG // //──────────────────────────────────────── // // データを追加する前に // //──────────────────────────────────────── // // // // 指し手の整合性をチェックして、不正なデータを弾くことは必要だぜ☆(^▽^) // // // { // Kyokumen ky2 = new KyokumenImpl(); // if(!ky2.ParseFen(kyFen_before, false)) // { // string msg = "パースに失敗だぜ☆(^~^)!"; // Face_Application.MessageLine(msg); // Face_Application.Write(); // throw new Exception(msg); // } // MoveError reason; // if (!ky2.CanDoMove(bestMove, out reason)) // { // throw new Exception($"指せない指し手を定跡に登録しようとしたぜ☆(^~^)!:{ConvMove.Setumei(reason)}"); // } // } // // これから登録する指し手を、ログに書き出しておきたいぜ☆(^▽^)マージのとき、うるさい☆(^~^) // { // // 定跡登録 // // fen .../.../.../ - 1 // // B1A1 ..... // Util_Machine.AppendLine("定跡登録"); // Util_Machine.AppendLine($" {kyFen_before}"); // Util_Machine.AppendLine($" {ConvMove.ToFen(bestMove)}"); // } //#endif josekiKy.AddMove(bestMove, hyokati, fukasa, version); //#if DEBUG // // 定跡を追加した直後にダンプして中身を目視確認だぜ☆(^~^) // Util_Machine.AppendLine( // $@"定跡を追加した直後にダンプして中身を目視確認だぜ☆(^~^) //┌──────────┐ //{this.ToString()} //└──────────┘ //" // ); // Logger.Flush(); //#endif return(josekiKy); }
/// <summary> /// 定跡に登録されている指し手一覧。 /// </summary> /// <param name="ky"></param> /// <returns></returns> public List <Move> GetMoves(Kyokumen ky) { List <Move> moves = new List <Move>(); ulong hash = ky.KyokumenHash.Value; if (this.KyItems.ContainsKey(hash)) { JosekiKyokumen josekyKy = this.KyItems[hash]; foreach (KeyValuePair <Move, JosekiMove> entry in josekyKy.SsItems) { moves.Add(entry.Key); } } return(moves); }
/// <summary> /// 局面データだけを追加するぜ☆(^▽^) /// ファイルを行単位にパースしているときに使う☆(^▽^) /// </summary> /// <param name="kyFen_before">指す前の局面の改造fen</param> /// <param name="kyHash_before">指す前の局面のハッシュ</param> /// <param name="kyTb_before">指す前の局面の手番</param> /// <returns></returns> public JosekiKyokumen ParseKyokumenLine(string kyFen_before, ulong kyHash_before, Option <Phase> optionalPhaseBeforeMove, StringBuilder syuturyoku) { JosekiKyokumen josekiKy; if (this.KyItems.ContainsKey(kyHash_before)) { // 既存☆ josekiKy = this.KyItems[kyHash_before]; } else { // 新規☆ /* #if DEBUG * { * Kyokumen ky2 = new KyokumenImpl(); * int caret = 0; * ky2.ParseFen(kyFen_before, ref caret, false, syuturyoku); * ulong newHash = ky2.CreateKyokumenHash(); * if (newHash != kyHash_before) * { * StringBuilder reigai = new StringBuilder(); * reigai.Append("局面ハッシュが異なるぜ☆(^~^)! kyFen_before=["); * reigai.Append(kyFen_before); * reigai.Append("] newHash=["); * reigai.Append(newHash.ToString()); * reigai.Append("] kyHash_before=["); * reigai.Append(kyHash_before.ToString()); * reigai.Append("]"); * syuturyoku.AppendLine(reigai.ToString()); * Logger.Flush(syuturyoku); * throw new Exception(reigai.ToString()); * } * } #endif */ josekiKy = new JosekiKyokumen(kyFen_before, optionalPhaseBeforeMove, this); this.KyItems.Add(kyHash_before, josekiKy); this.Edited = true; } return(josekiKy); }
/// <summary> /// 評価値を更新するぜ☆(^▽^) /// </summary> /// <param name="erandaSasite">実際に選んだ指し手☆</param> /// <param name="erandaHyokati">実際に選んだ手の評価値☆</param> public static void Update(Move erandaSasite, Hyokati erandaHyokati, Kyokumen ky, StringBuilder syuturyoku) { JosekiKyokumen joKy_orNull = Option_Application.Joseki.GetKyokumen(Util_KikaiGakusyu.KaisiKyHash); if (null == joKy_orNull)// この局面の定跡データが入っていなければ、そもそも 学習できないぜ☆(^▽^) { return; } // 成績表を見て、現局面で最も勝率の高い指し手を、教師とするぜ☆(^~^) Move kyosiSs = Option_Application.Seiseki.GetSasite_Winest(ky, out float kyosiSyoritu_notUse); Hyokati kyosiHyokati; // 教師の手の評価値☆ if (Util_KikaiGakusyu.FirstAndHappaFens.ContainsKey(kyosiSs)) // 教師の手はあるはずだろ☆(^~^)? { // まず、一手指すぜ☆ Nanteme nanteme = new Nanteme(); ky.DoMove(Option_Application.Optionlist.USI, kyosiSs, MoveType.N00_Karappo, ref nanteme, ky.CurrentOptionalPhase, syuturyoku); // 評価値を調べようぜ☆(^▽^) Hyokati komawariHyokati = ky.Komawari.Get(ky.CurrentOptionalPhase); Hyokati nikomaHyokati = ky.Nikoma.Get(true); kyosiHyokati = (int)komawariHyokati + nikomaHyokati; // 一手戻そうぜ☆(^▽^) ky.UndoMove(Option_Application.Optionlist.USI, kyosiSs, syuturyoku); } else { return; } // 教師の手の評価値が、一番高いとは限らないぜ☆(^~^) if (!Conv_Hyokati.InHyokati(kyosiHyokati)) { // 教師の評価値が、メートの数字などになっている場合は、学習はできないぜ☆(>_<) return; } Kyokumen happaKy = new Kyokumen(); int caret_temp2; double sumSigmoidY = 0.0d;// 積分☆ // 深さが異なるので、自分の局面、相手の局面 の数も異なり、 // 足す局面と、引く局面の数が合わなくなるぜ☆(^~^) // ↓ // せっかく 1P、2P の評価値を持っているのだから、 // 1P から引いた分は 2P に足す、ということでどうか☆(^~^)? // では、今回の合法手を全て見ていくぜ☆(^~^) foreach (KeyValuePair <Move, List <string> > entry in Util_KikaiGakusyu.FirstAndHappaFens) { if (entry.Key == kyosiSs) { // 教師の手は、今回はスルーするぜ☆(^▽^) continue; } // さて、教師以外の手だが……☆(^~^) HyokatiUtiwake sonotanoTe_hyokatiUtiwake; // まず、一手指すぜ☆ Nanteme nanteme = new Nanteme(); ky.DoMove(Option_Application.Optionlist.USI, entry.Key, MoveType.N00_Karappo, ref nanteme, ky.CurrentOptionalPhase, syuturyoku); // 評価値を調べようぜ☆(^▽^) ky.Hyoka(out sonotanoTe_hyokatiUtiwake, HyokaRiyu.Yososu, true// ランダムな局面で学習したりもするし☆(^~^) ); // 一手戻そうぜ☆(^▽^) ky.UndoMove(Option_Application.Optionlist.USI, entry.Key, syuturyoku); if (!Conv_Hyokati.InHyokati(sonotanoTe_hyokatiUtiwake.EdaBest)) { // その他の手の評価値が、メートの数字などになっている場合は、学習はできないぜ☆(>_<) continue; } // 教師の手と、それ以外の手の 評価値の差を、 // シグモイドの x に当てはめて、y を求めるぜ☆ double sigmoidY = Option_Application.Optionlist.NikomaGakusyuKeisu * Util_Sigmoid.Sigmoid(erandaHyokati - kyosiHyokati); // 教師の手(=一番評価値が高い手)より 評価値が上回っている手は、 // すると、 0.5 < y < 1 となるな☆ // 下回っていれば、 // 0 < y < 0.5 となるな☆ var(exists1, phase1) = Util_Tansaku.StartingPhase.Match; var(exists2, phase2) = happaKy.CurrentOptionalPhase.Match; // この点数を、葉 から かき集めるぜ☆www(^▽^) foreach (string happaFen in entry.Value) { caret_temp2 = 0; happaKy.ParsePositionvalue(Option_Application.Optionlist.USI, happaFen, ref caret_temp2, false, false, out string moves, syuturyoku); // この局面の2駒関係を、シグモイドの y 点分、下げるぜ☆ sumSigmoidY += Util_NikomaKankei.DecrementParamerter_KikaiGakusyu( happaKy, (exists1 && exists2 && phase1 == phase2) ? -sigmoidY : sigmoidY//自分の手番なら 引く☆ ); } } // 下げてかき集めた シグモイドの y の量を、 // 教師の指し手の葉に 山分けするぜ☆(^▽^) double yamawake = sumSigmoidY / (double)Util_KikaiGakusyu.FirstAndHappaFens[kyosiSs].Count; foreach (string happaFen in Util_KikaiGakusyu.FirstAndHappaFens[kyosiSs])// 教師の手はあるはずだろ☆(^~^)? { caret_temp2 = 0; happaKy.ParsePositionvalue(Option_Application.Optionlist.USI, happaFen, ref caret_temp2, false, false, out string moves, syuturyoku); var(exists1, phase1) = Util_Tansaku.StartingPhase.Match; var(exists2, phase2) = happaKy.CurrentOptionalPhase.Match; // 各葉に 山分けだぜ☆(^~^) Util_NikomaKankei.IncrementParamerter_KikaiGakusyu( happaKy, (exists1 && exists2 && phase1 == phase2) ? -yamawake : yamawake//自分の手番なら 足すぜ☆ ); } }
/// <summary> /// 定跡局面の中で、評価値が一番高い指し手を返すぜ☆(^▽^) /// </summary> /// <param name="ky"></param> /// <returns>なければ投了☆</returns> public Move GetMove(bool isSfen, Kyokumen ky, out Hyokati out_bestHyokati, StringBuilder syuturyoku #if DEBUG , out string fen_forTest #endif ) { Move bestMove = Move.Toryo; out_bestHyokati = Hyokati.TumeTesu_GohosyuNasi; int bestFukasa = 0; #if DEBUG fen_forTest = ""; #endif //Util_Machine.Assert_KyokumenSeigosei_SabunKosin("ゲット指し手 #鯨",true); ulong hash = ky.KyokumenHash.Value; if (this.KyItems.ContainsKey(hash)) { JosekiKyokumen josekyKy = this.KyItems[hash]; // 整合性の確認用だぜ☆(^~^) #if DEBUG fen_forTest = josekyKy.Fen; #endif foreach (KeyValuePair <Move, JosekiMove> entry in josekyKy.SsItems) { if (out_bestHyokati < entry.Value.Hyokati)// 評価値が高い指し手を選ぶぜ☆(^▽^) { bestMove = entry.Key; out_bestHyokati = entry.Value.Hyokati; bestFukasa = entry.Value.Fukasa; } else if (out_bestHyokati == entry.Value.Hyokati && //評価値が同じ場合は、 bestFukasa < entry.Value.Fukasa //深く読んでいる指し手を選ぶぜ☆(^▽^) ) { bestMove = entry.Key; out_bestHyokati = entry.Value.Hyokati; bestFukasa = entry.Value.Fukasa; } } } #if DEBUG // 指し手の整合性をチェックしておきたいぜ☆(^▽^) { Kyokumen ky_forAssert = new Kyokumen(); int caret = 0; StringBuilder sindan1 = new StringBuilder(); ky.AppendFenTo(Option_Application.Optionlist.USI, sindan1); //if (!ky2.ParseFen(sindan1.ToString(), ref caret, false, syuturyoku)) if (!ky_forAssert.ParsePositionvalue(isSfen, sindan1.ToString(), ref caret, true, false, out string moves, syuturyoku))// ビットボードを更新したいので、適用する { syuturyoku.AppendLine("取得: パースに失敗だぜ☆(^~^)! #鰯"); var msg = syuturyoku.ToString(); syuturyoku.Clear(); Logger.Flush(msg); throw new Exception(msg); } if (!ky_forAssert.CanDoMove(bestMove, out MoveMatigaiRiyu riyu)) { StringBuilder sindan2 = new StringBuilder(); sindan2.Append("取得: 指せない指し手を定跡から取り出そうとしたぜ☆(^~^)!:"); // sindan2.Append("理由:"); ConvMove.SetumeiLine(riyu,sindan2); sindan2.Append("指し手:"); ConvMove.SetumeiLine(isSfen, bestMove, sindan2); sindan2.Append("定跡局面 ("); ky_forAssert.AppendFenTo(Option_Application.Optionlist.USI, sindan2); sindan2.AppendLine(")"); Util_Information.Setumei_Lines_Kyokumen(ky_forAssert, sindan2); //str2.AppendLine("以下、定跡メモリのダンプ"); //str2.AppendLine("┌──────────┐"); //str2.Append(this.ToString()); //str2.AppendLine("└──────────┘"); syuturyoku.AppendLine(sindan2.ToString()); var msg = syuturyoku.ToString(); syuturyoku.Clear(); Logger.Flush(msg); throw new Exception(sindan2.ToString()); } } #endif return(bestMove); }
/// <summary> /// 定跡ファイルの解析☆(^~^) /// </summary> /// <param name="lines"></param> public void Parse(bool isSfen, string[] lines, StringBuilder syuturyoku) { this.Clear(); Kyokumen ky_forJoseki = new Kyokumen();//使いまわすぜ☆(^▽^) JosekiKyokumen josekiKy = null; JosekiMove josekiSs; Match m; string commandline; for (int iGyoBango = 0; iGyoBango < lines.Length; iGyoBango++) { commandline = lines[iGyoBango]; if (commandline.Length < 1) { // 空行は無視☆ // 半角空白とか、全角空白とか、タブとか 入れてるやつは考慮しないぜ☆(^~^)! } else if ('f' == commandline[0])// fen で始まれば局面データ☆(^▽^)// caret == commandline.IndexOf("fen ", caret) { // キャレットは進めずに続行だぜ☆(^▽^) m = Itiran_FenParser.GetJosekiKyPattern(Option_Application.Optionlist.USI).Match(commandline);//, caret if (!m.Success) { StringBuilder reigai1 = new StringBuilder(); reigai1.AppendLine($@"パースに失敗だぜ☆(^~^)! #寿 定跡ファイル解析失敗 commandline=[{ commandline }]"); #if DEBUG reigai1.Append(" ["); reigai1.Append(iGyoBango.ToString()); reigai1.Append("]行目"); #endif syuturyoku.AppendLine(reigai1.ToString()); var msg = syuturyoku.ToString(); Logger.Flush(msg); syuturyoku.Clear(); throw new Exception(msg); } // .Value は、該当しないときは空文字列か☆ // .Value は、該当しないときは空文字列か☆ if (Itiran_FenParser.STARTPOS_LABEL == m.Groups[1].Value) { DanStrings = Itiran_FenParser.GetStartpos(Option_Application.Optionlist.USI).Split('/'); } else { DanStrings = m.Groups[1].Value.Split('/'); // N段目 } ky_forJoseki.SetNaiyo( isSfen, true, //適用 false, Joseki.DanStrings, //1~N 段目 m.Groups[2].Value, m.Groups[3].Value, //手番 syuturyoku ); /* #if DEBUG * { * Kyokumen ky3 = new KyokumenImpl(); * int caret = 0; * ky3.ParseFen(commandline, ref caret, false, syuturyoku); * ulong newHash = ky3.CreateKyokumenHash(); * if (newHash != ky2.KyokumenHash) * { * StringBuilder reigai1 = new StringBuilder(); * reigai1.Append("局面ハッシュが異なるぜ☆(^~^)! commandline=["); * reigai1.Append(commandline); * reigai1.Append("] ky3.AppendFenTo=["); * ky3.AppendFenTo(reigai1); * reigai1.Append("] dan1["); * reigai1.Append(JosekiImpl.DanStrings[0]); * reigai1.Append("] dan2["); * reigai1.Append(JosekiImpl.DanStrings[1]); * reigai1.Append("] dan3["); * reigai1.Append(JosekiImpl.DanStrings[2]); * reigai1.Append("] dan4["); * reigai1.Append(JosekiImpl.DanStrings[3]); * reigai1.Append("] newHash=["); * reigai1.Append(newHash.ToString()); * reigai1.Append("] ky2.KyokumenHash=["); * reigai1.Append(ky2.KyokumenHash.ToString()); * reigai1.Append("]"); * syuturyoku.AppendLine(reigai1.ToString()); * Logger.Flush(syuturyoku); * throw new Exception(reigai1.ToString()); * } * } #endif */ josekiKy = this.ParseKyokumenLine(commandline, ky_forJoseki.KyokumenHash.Value, ky_forJoseki.CurrentOptionalPhase, syuturyoku); } else { // それ以外は手筋☆(^▽^) if (null == josekiKy) { throw new Exception("定跡ファイル解析失敗 定跡局面の指定なし☆"); } // 指し手、指し手、数字、数字、数字 と並んでいるぜ☆(^▽^) m = Itiran_FenParser.GetJosekiSsPattern(Option_Application.Optionlist.USI).Match(commandline); if (!m.Success) { //* // FIXME: syuturyoku.AppendLine($"パースに失敗だぜ☆(^~^)! #鮪 commandline=[{ commandline }]"); var msg = syuturyoku.ToString(); syuturyoku.Clear(); Logger.Flush(msg); throw new Exception(msg); // */ } // 高速化のために、ローカル変数を減らして、詰め込んだコードにしているぜ☆(>_<) // 第1引数 B1C1 や toryo のような指し手の解析。 josekiSs = new JosekiMove( // 1列目:指し手☆ (1:グループ,2:指し手全体,3~7:指し手各部,8:投了) m.Groups[8].Success ? Move.Toryo : // "toryo" が入っている場合☆ Med_Parser.TryFenMove2(Option_Application.Optionlist.USI, ky_forJoseki.Sindan, m.Groups[3].Value, m.Groups[4].Value, m.Groups[5].Value, m.Groups[6].Value, m.Groups[7].Value ), // 2列目:応手☆ none とか。(9:グループ,10:none,11:指し手全体,12~16:指し手各部,17:投了) m.Groups[10].Success || m.Groups[17].Success ? Move.Toryo :// [10]"none" または[17]"toryo" が入っている場合☆ FIXME: none と toryo を区別してないぜ☆(^~^) Med_Parser.TryFenMove2(Option_Application.Optionlist.USI, ky_forJoseki.Sindan, m.Groups[12].Value, m.Groups[13].Value, m.Groups[14].Value, m.Groups[15].Value, m.Groups[16].Value ), (Hyokati)int.Parse(m.Groups[18].Value), //hyokati (18) 解析はokなはず☆ int.Parse(m.Groups[19].Value), //fukasa (19) 解析はokなはず☆ int.Parse(m.Groups[20].Value), //version (20) 解析はokなはず☆(旧版ではバージョンは無いこともある☆) josekiKy ); // 定跡ファイルの局面には、重複指し手データがないようにしてくれだぜ☆(^~^)チェックは省くぜ☆ /* * if (josekiKy.SsItems.ContainsKey(josekiSs.Move)) * { * // FIXME: * String2 str = new String2Impl(); * str.Append("局面データに重複の指し手があるぜ☆(^~^)! 局面=["); * str.Append(josekiKy.Fen); * str.Append("] 指し手=["); * ConvMove.Setumei(josekiSs.Move,str); * str.Append("]"); * Util_Machine.AppendLine(str.ToString()); * Logger.Flush(); * throw new Exception(str.ToString()); * } * else * { * // */ // 新規 //#if DEBUG // // 指し手の整合性をチェックしておきたいぜ☆(^▽^) // { // Kyokumen ky2 = new KyokumenImpl(); // if (!ky2.ParseFen(ky.ToFen(), false)) // { // string msg = "新規: パースに失敗だぜ☆(^~^)!"; // Face_Application.MessageLine(msg); // Face_Application.Write(); // throw new Exception(msg); // } // Move bestMove; // int caret_test = 0; // ConvMove.TryParse(commandline, ref caret_test, ky2, out bestMove); // MoveError reason; // if (!ky2.CanDoMove( bestMove, out reason)) // { // throw new Exception($"新規: 指せない指し手を定跡に登録しようとしたぜ☆(^~^)!:{ConvMove.Setumei(reason)}"); // } // } //#endif josekiKy.SsItems.Add(josekiSs.Move, josekiSs); this.Edited = true; //} } } }
public JosekiMove(Move move, Move ousyu, Hyokati hyokati, int fukasa, int version, JosekiKyokumen owner) { this.Owner = owner; this.Move = move; this.Ousyu = ousyu; this.Hyokati = hyokati; this.Fukasa = fukasa; this.Version = version; }