「 お前ら……☆ コーディング・ルールが ばらばらで メンテナンスしにくい☆」
連番 | 名前 | 備考 |
---|---|---|
1 | Kifuwarane | 2014-11 SDT2 |
2 | Kifuwarazusa | 2015-05 WCSC25 |
3 | Kifuwarakaku | 2015-11 SDT3 |
4 | Kifuwarube | 2016-05 WCSC26 |
5 | Kifuwaragyoku | 2016-10 SDT4 |
6 | Kifuwarakei | 2017-05 WCSC27 |
7 | Kifuwarabi | 2017-10 SDT5 |
8 | Kifuwarust | 2018-05 WCSC28 |
- | Kifuwarahashi | 2019-05 WCSC29 故障 |
- | Kifuwarachevy | 2020-05 WCSOC |
9 | Kifuwarabe | Kifuwarachevy or stable build version |
「 C# だったり C++ だったり、 Rust だったり、言語も違う……☆
ここは ばさっと 管理しないと わたしが きつい……☆」
Profile/Engine.toml:
[Engine]
# 将棋ソフトの表示名です。
Name = "Kifuwarabe"
# 将棋ソフトの制作者名です。
Author = "Chiyuri"
「 別々のディレクトリーに 複数の .exe ファイルを作るやつもいるから、設定ファイル置き場は .exe のある場所とは別に用意しとこ……☆」
/
|
+-- Profile
|
+-- Engine.toml
「 ↑ こんな感じで……☆ ディレクトリー名を パスカルケースにするか スネークケースにするかは プログラム言語によって
文化が違うから、変えていいことにするぜ☆」
「 ↓ 例えば きふわらね は、ファイルのアセンブリ情報からエンジン名や作者名を取ってたぜ☆」
// 製品名とバージョン番号は、次のファイルに書かれているものを使っています。
// 場所: [ソリューション エクスプローラー]-[ソリューション名]-[プロジェクト名]-[Properties]-[AssemblyInfo.cs] の中の、
// [AssemblyProduct]と[AssemblyVersion] を参照。
string seihinName;
string versionStr;
{
// 製品名
seihinName = ((System.Reflection.AssemblyProductAttribute)Attribute.GetCustomAttribute(System.Reflection.Assembly.GetExecutingAssembly(), typeof(System.Reflection.AssemblyProductAttribute))).Product;
// バージョン番号
Version version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
versionStr = String.Format("{0}.{1}.{2}", version.Major, version.Minor.ToString("00"), version.Build);
//seihinName += " " + versionStr;
}
LarabeLogger.GetInstance().WriteLineMemo(logTag, "v(^▽^)v イェーイ☆ ... " + seihinName + " " + versionStr);
「 アセンブリ情報の書き換え方を 説明するのが めんどくさいのよ」
App.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
</startup>
<!-- (^o^) Add -->
<appSettings>
<add key="Profile" value="../../../../Profile" />
</appSettings>
</configuration>
「 ↑ .exe ファイルと同じディレクトリーには .exe.config みたいなファイルができるから、そこに Profileディレクトリーへのパスが乗るようにしておこうぜ☆」
// 参照 からアセンブリを追加しろだぜ☆(^~^)
using System.Configuration;
// 使う時
var profilePath = System.Configuration.ConfigurationManager.AppSettings["Profile"];
「 ↑ ここらへんの Visual Studio の基本操作は、どっかで覚えてきてくれだぜ☆」
using System.IO;
using Nett;
「 ↑ NuGet で Nett
を取ってきておいてくれだぜ☆」
var profilePath = System.Configuration.ConfigurationManager.AppSettings["Profile"];
var toml = Toml.ReadFile(Path.Combine(profilePath,"Engine.toml"));
var engineName = toml.Get<TomlTable>("Engine").Get<string>("Name");
var engineAuthor = toml.Get<TomlTable>("Engine").Get<string>("Author");
「 逆に .exe.config を設定する方法を ReadMe に書く必要がでてきたわよ」
2020年11月の 電竜戦から きふわらずさ(Kifuwarazusa)にリネームして開発再開だぜ☆(^~^)
| | ファイル |
| ----------------------- | ------------------------------------------------------------------------------------------ |
| ソース | `Kifuwarabe_WCSC25/Sources/By_Circle_Grayscale/P400_KifuNarabeVs/KifuwarazusaGuiVs.sln` |
| 将棋エンジン ソース | P050_KifuWarabe |
| 将棋GUI ソース | P400_KifuNaraVs |
| 将棋エンジン ランタイム | `Kifuwarabe_WCSC25/Builds/Engine/Bin/P050_KifuWarabe/Grayscale.P050_KifuWarabe.exe` |
| 設定ファイル1 | `Kifuwarabe_WCSC25/Builds/Engine/Bin/P050_KifuWarabe/Grayscale.P050_KifuWarabe.exe.config` |
| 設定ファイル2 | `Kifuwarabe_DenouT2/Profile/Engine.toml` |
設定ファイル1 の `Grayscale.P050_KifuWarabe.exe.config` の中にある `Profile` のパスを、 設定ファイル2 の親ディレクトリー `Profile` に合わせてください。
config.rs:
//! 設定
/// USI対応コンピューター将棋ソフトの名前☆(^~^)
pub const ENGINE_NAME: &'static str = "Kifuwarabe WCSC28";
/// 作者の名前。姓・名の順にしたいぜ☆(^~^)異文化に通じる表記方法はないものか☆(^~^)
pub const ENGINE_AUTHOR: &'static str = "TAKAHASHI, Satoshi";
「 ↑ 確かに これで十分なんだが、どのプログラム言語でも同じ設定方法にしたいので わざわざ TOML にするぜ☆」
「 ↑ ファイル1個置くだけに profile ディレクトリー作るの 無駄くさかったな……☆」
profile/Engine.toml:
[engine]
# 将棋ソフトの表示名です。
name = "Kifuwarust Bld1"
# 将棋ソフトの制作者名です。
author = "Chiyuri"
「 ↑ Rustに合わせないと めんどくさいんで テーブルの名前や、キーバリュのキーは スネークケースにするとするぜ☆」
Cargo.toml:
[dependencies]
serde = { version = "1.0", features = ["derive"] }
toml = "0.5.7"
main.rs:
extern crate serde;
extern crate toml;
config.rs:
//! 設定
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct EngineFile {
pub engine: Engine,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Engine {
pub name: String,
pub author: String,
}
main.rs:
use std::fs::{self};
// 略
match fs::read_to_string("profile/Engine.toml") {
Ok(text) => {
let engine_file: Result<EngineFile, toml::de::Error> = toml::from_str(&text);
match engine_file {
Ok(engine_file) => {
g_writeln(&format!("id name {}", engine_file.engine.name));
g_writeln(&format!("id author {}", engine_file.engine.author));
g_writeln("usiok");
}
Err(why) => {
panic!("{}", why);
}
}
}
Err(why) => {
panic!("{}", why);
}
}
| | ファイル |
| ----------------------- | ----------------------------------------------------------- |
| ソース | `Kifuwarabe_WCSC28/Cargo.toml` |
| 将棋エンジン ソース | `Kifuwarabe_WCSC28/src/main.rs` |
| GUI | なし |
| 将棋エンジン ランタイム | `Kifuwarabe_WCSC28/target/release/kifuwarabe_shogi2018.exe` |
| 設定ファイル | `kifuwarabe_shogi2018.exe` から見て `profile/Engine.toml` |
「 Rust版のわたしの profile/Engine.toml
のディレクトリーは変えられないのかだぜ☆?」
「 .NET Framework の *.exe.config
の文化に合わせるの 嫌なんだが……☆
*.exe
ファイルと同じディレクトリに 別のファイルパスへの案内があるのは合理的か……☆」
「 じゃあ *.exe.config.toml
というのを置いたろ☆」
「 コマンドラインの 第1引数 が ファイル名と考えていいの?」
gets execution file name on rust
「 ↑ 現代の 自然言語翻訳テクノロジー のレベルから考えて 日本語は未成熟だから 英語でググると 検索性能が段違い……☆
すぐ出てくる☆」
Idiom #105 Current executable name
let exe_name = std::env::current_exe()
.expect("Can't get the exec path")
.file_name()
.expect("Can't get the exec name")
.to_string_lossy()
.into_owned();
println!("(^q^) {}.config.toml", exe_name);
Output:
(^q^) kifuwarabe_shogi2018.exe.config.toml
kifuwarabe_shogi2018.exe.config.toml:
[app]
profile = "./profile"
config.rs:
//! 設定
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct ExeConfigFile {
pub app: App,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct App {
pub profile: String,
}
「 Rust言語で ファイルパスのコンバインって どうやるんだぜ☆?」
combine path on rust
use std::path::Path;
// Example
Path::new("/etc").join("passwd").to_string_lossy()
main.rs:
// 実行ファイル名
let exe_name = std::env::current_exe()
.expect("Can't get the exec path")
.file_name()
.expect("Can't get the exec name")
.to_string_lossy()
.into_owned();
// Example: "kifuwarabe_shogi2018.exe.config.toml"
let engine_path = match fs::read_to_string(format!("{}.config.toml", exe_name)) {
Ok(text) => {
let config: Result<ExeConfigFile, toml::de::Error> = toml::from_str(&text);
match config {
Ok(config) => Path::new(&config.app.profile).join("Engine.toml"),
Err(why) => {
panic!("{}", why);
}
}
}
Err(why) => {
panic!("{}", why);
}
};
// Example: "./profile\Engine.toml"
match fs::read_to_string(format!("{}", engine_path.to_string_lossy())) {
Ok(text) => {
let config: Result<EngineFile, toml::de::Error> = toml::from_str(&text);
match config {
Ok(config) => {
g_writeln(&format!("id name {}", config.engine.name));
g_writeln(&format!("id author {}", config.engine.author));
g_writeln("usiok");
}
Err(why) => {
panic!("{}", why);
}
}
}
Err(why) => {
panic!("{}", why);
}
}
「 ↑ 設定ファイルから 2項目取るだけで こんなに長くなって どうなのかと思うが、まあ 動けばいいだろ……☆」
「 どの きふわらべ にもあるコードは SFEN だろ☆
SFEN を クリーン・アーキテクチャー しようぜ☆?」
「 クリーン・アーキテクチャーって もっさり してるんだがな……☆ 競技用に向いてない……☆」
Entities
Sfen
class SfenGame
constructor( builder ) -> Self
Str { get }
Board[ 11~99 ] -> char {K, R, B, G ...}
IsStartpos -> bool
Phase -> &str {b, w}
Hands[ phase * 7] -> uint
Ply -> const 1
Moves[ 0~512 ] -> &str "7g7f" ...
class SfenGameBuilder
From( string "SFEN" ) -> SfenGame
Put( 11~99, {K, R, B, G ...} )
SetStartpos( )
SetPhase( {b, w} )
Have( {K, R, B, G...}, 0~18)
AppendMove( &str "7g7f" ... )
Build( )
「 オブジェクトを 疎結合するには 文字列がいいんで……☆」
「 ソフトによって 変わってくるのはアリで☆ 作りを気持ち寄せる、ということだぜ☆」
旧きふわらね:
namespace Grayscale.KifuwaraneLib.L04_Common
{
public abstract class KomaSyurui14Array
{
/// <summary>
/// ------------------------------------------------------------------------------------------------------------------------
/// 駒のSFEN(打)符号用の単語。
/// ------------------------------------------------------------------------------------------------------------------------
/// </summary>
public static string[] SfenDa { get { return KomaSyurui14Array.sfenDa; } }
protected static string[] sfenDa;
static KomaSyurui14Array()
{
KomaSyurui14Array.sfenDa = new string[]{
"×",//[0]ヌル
"P",//[1]
"L",
"N",
"S",
"G",
"K",
"R",
"B",
"R",
"B",
"P",
"L",
"N",
"S",
"<打×U>",//[15]
};
}
}
}
新きふわらね:
namespace Grayscale.KifuwaraneLib.Entities.Sfen
{
public static class SfenReferences
{
/// <summary>
/// 駒のSFEN(打)符号用の単語。
/// </summary>
public static string[] SfenDa { get; private set; } = new string[]
{
"×",//[0]ヌル
"P",//[1]
"L",
"N",
"S",
"G",
"K",
"R",
"B",
"R",
"B",
"P",
"L",
"N",
"S",
"<打×U>",//[15]
};
}
}
「 ↑ 別に何も変わってないが Common
ではなく Entities.Sfen
に配置転換したぜ☆
本人は変わってないが 乗っている電車の路線が変わった……、みたいな改造を これからしていくわけだぜ☆」
「 リストラクチャリングって 結局 上の都合なのよ。
管理能力、管理コスト体力 には限界があるから 全体の構造が いじくり回されるだけで 全体の エコ をしてるだけで何も進まないのよ」
「 おかしな将棋エンジンだな……☆
Entity.SfenTranslation という区分けを 暫定的に作るかだぜ☆」
旧きふわらね:
namespace Grayscale.KifuwaraneLib.L04_Common
{
/// <summary>
/// あるデータを、別のデータに変換します。
/// </summary>
public abstract class Converter04
{
/// <summary>
/// アラビア数字。
/// </summary>
public static string[] ARABIA_SUJI = new string[] { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
/// <summary>
/// 漢数字。
/// </summary>
public static string[] KAN_SUJI = new string[] { "一", "二", "三", "四", "五", "六", "七", "八", "九" };
}
}
新きふわらね:
namespace Grayscale.KifuwaraneLib.Entities.SfenTranslation
{
public static class TranslatedSfenReferences
{
/// <summary>
/// アラビア数字。
/// </summary>
public static string[] ARABIA_SUJI { get; private set; } = new string[] { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
/// <summary>
/// 漢数字。
/// </summary>
public static string[] KAN_SUJI { get; private set; } = new string[] { "一", "二", "三", "四", "五", "六", "七", "八", "九" };
}
}
「 しかし そんな変換、 SFEN と何の関係も無くないか☆?」
「 じゃあ Entities.PositionTranslation
にする☆」
新きふわらね:
using Grayscale.KifuwaraneLib.L04_Common;
namespace Grayscale.KifuwaraneLib.Entities.SfenTranslation
{
public static class SfenTranslator
{
/// <summary>
/// 打った駒の種類。
/// </summary>
/// <param name="syurui"></param>
/// <returns></returns>
public static void SfenUttaSyurui(string sfen, out Ks14 syurui)
{
switch (sfen)
{
case "P":
syurui = Ks14.H01_Fu;
break;
case "L":
syurui = Ks14.H02_Kyo;
break;
case "N":
syurui = Ks14.H03_Kei;
break;
case "S":
syurui = Ks14.H04_Gin;
break;
case "G":
syurui = Ks14.H05_Kin;
break;
case "R":
syurui = Ks14.H07_Hisya;
break;
case "B":
syurui = Ks14.H08_Kaku;
break;
case "K":
syurui = Ks14.H06_Oh;
break;
case "+P":
syurui = Ks14.H11_Tokin;
break;
case "+L":
syurui = Ks14.H12_NariKyo;
break;
case "+N":
syurui = Ks14.H13_NariKei;
break;
case "+S":
syurui = Ks14.H14_NariGin;
break;
case "+R":
syurui = Ks14.H07_Hisya;
break;
case "+B":
syurui = Ks14.H08_Kaku;
break;
default:
System.Console.WriteLine("▲バグ【駒種類】Sfen=[" + sfen + "]");
syurui = Ks14.H00_Null;
break;
}
}
}
}
「 翻訳コード 多すぎだな……☆
もっと 1 オブジェクトは { "一", "a", "A", 1, "1"} である、というデータベースのID的なものを作った方がいいんじゃないのか☆?」
「 つまり テーブル引き だよな☆
テーブル引きと Switch 文の どっちの実行速度が 高速かは どっちとも言えないんだぜ☆」
「 実行ファイルのバージョンを見分けたいのに、あんたが プログラム名を外部ファイルに出したせいで
どのバージョンのプログラムでも プログラム名がおんなじよ!」
「 プログラム名のうしろに ビルド番号 が付加されるようにするかだぜ☆ Bld1
みたいな☆」
「 そのビルド番号はどこに書くんだぜ☆? アセンブリ情報か☆?」
「 Program.cs の冒頭とかでいいんじゃないかな……☆」
新きふわらね:
namespace Grayscale.KifuwaraneLib
{
public class Program
{
/// <summary>
/// ビルド番号。ソースをちょっといじったら ここを増やしておけば Exeファイルを差し替えたことが分かりやすい。
/// </summary>
public static int BuildVersion { get; private set; } = 5;
}
}
// 略
engineName = $"{toml.Get<TomlTable>("Engine").Get<string>("Name")} Bld{Grayscale.KifuwaraneLib.Program.BuildVersion}";
「 きふわらね、 SFEN の読取から 外部プロセス起動 まで 1メソッドでやってんのか☆」
「 SFEN の解析と、 外部プロセスの起動は 分けましょう」
「 うん……☆? この Process って、 Stockfish型プログラムでいう所の Move だぜ☆」
「 Sfen の中でも 指し手の解析 はまた骨が折れるんだぜ☆」
Entities
SfenTranslation
class SfenMove
constructor( builder ) -> Self
Str { get }
SrcFile -> int 1~9
SrcRank -> int 1~9
IsDrop -> bool
SrcPiece -> char {K, R, B, k, r, b, +P ...}
DstFile -> int 1~9
DstRank -> int 1~9
DstPiece -> char {K, R, B, k, r, b, +P ...}
IsPromote -> bool
class SfenMoveBuilder
From( string "SFEN move" ) -> SfenMove
SetSrcFile( 1~9 )
SetSrcRank( 1~9 )
SetDrop( bool )
SetSrcPiece( {K, R, B, k, r, b, +P ...} )
SetDstFile( 1~9 )
SetDstRank( 1~9 )
SetDstDrop( bool )
SetPromote( bool )
Build( )
「 きふわらね は、 指し手を1つずつパースするのではなく、指し手のリスト をパースするように作ってあるんだが☆」
Entities
SfenTranslation
class SfenMoveBuilder
Set1st( 123456789 か、 PLNSGKRB )
Set2nd( abcdefghi か、 * )
Set3rd( 123456789 )
Set4th( abcdefghi )
Set5th( + )
Build( )
「 ↑ まあ、やり方は 無尽蔵にあるのがプログラミングだからな……☆」
旧きふわらべ:
// Sfenの指し手解析
Regex regex = new Regex(
@"^\s*([123456789PLNSGKRB])([abcdefghi\*])([123456789])([abcdefghi])(\+)?",
RegexOptions.Singleline
);
MatchCollection mc = regex.Matches(text);
foreach (Match m in mc)
{
try
{
if (0 < m.Groups.Count)
{
successful = true;
// 残りのテキスト
restText = text.Substring(0, m.Index) + text.Substring(m.Index + m.Length, text.Length - (m.Index + m.Length));
SfenMoveReferences.GetData_FromTextSub(
m.Groups[1].Value, // 123456789 か、 PLNSGKRB
m.Groups[2].Value, // abcdefghi か、 *
m.Groups[3].Value, // 123456789
m.Groups[4].Value, // abcdefghi
m.Groups[5].Value, // +
out move,
kifuD,
logTag
);
}
// 最初の1件だけ処理して終わります。
break;
}
catch (Exception ex)
{
// エラーが起こりました。
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
// どうにもできないので ログだけ取って無視します。
string message = "TuginoItte_Sfen.GetData_FromText(A):" + ex.GetType().Name + ":" + ex.Message + ":text=「" + text + "」 m.Groups.Count=「" + m.Groups.Count + "」";
LarabeLogger.GetInstance().WriteLineError(LarabeLoggerTag_Impl.ERROR, message);
}
}
restText = restText.Trim();
「 ↑ break 文で抜けてるんで先頭の1つしか見てない……☆」
旧きふわらべ:
/// <summary>
/// 符号1「7g7f」を元に、指し手 を作ります。
///
/// <再生、コマ送りで呼び出されます>
/// </summary>
/// <returns></returns>
public static void GetData_FromTextSub(
string str1, //123456789 か、 PLNSGKRB
string str2, //abcdefghi か、 *
string str3, //123456789
string str4, //abcdefghi
string strNari, //+
out IMove move,
Kifu_Document kifuD,
ILoggerFileConf logTag
)
{
// 略。
}
「 ↑ 引数で5つ受け取っているとこを リストラクチャリングするぜ☆」
新きふわらね SfenMoveBuilder.cs:
namespace Grayscale.KifuwaraneLib.Entities.Sfen
{
/// <summary>
/// メソッド・チェーンできないけど……☆(^~^)
/// </summary>
public class SfenMoveBuilder
{
public SfenMove Build()
{
return new SfenMove(this);
}
/*
public int SrcFile { get; set; }
public int SrcRank { get; set; }
public bool IsDrop { get; set; }
public string SrcPiece { get; set; }
public int DstFile { get; set; }
public int DstRank { get; set; }
public string DstPiece { get; set; }
public bool IsPromote { get; set; }
*/
// 忘れず初期化☆(^~^)!
public char[] Chars { get; private set; } = new char[5] { ' ', ' ', ' ', ' ', ' ', };
/// <summary>
/// 123456789 か、 PLNSGKRB
/// </summary>
/// <param name="ch"></param>
public char Str1st { set { this.Chars[0] = value; } }
/// <summary>
/// abcdefghi か、 *
/// </summary>
/// <param name="ch"></param>
public char Str2nd { set { this.Chars[1] = value; } }
/// <summary>
/// 123456789
/// </summary>
/// <param name="ch"></param>
public char Str3rd { set { this.Chars[2] = value; } }
/// <summary>
/// abcdefghi
/// </summary>
/// <param name="ch"></param>
public char Str4th { set { this.Chars[3] = value; } }
/// <summary>
/// + か、無し。
/// </summary>
/// <param name="ch"></param>
public char Str5th { set { this.Chars[4] = value; } }
}
}
新きふわらね SfenMove.cs:
using System;
namespace Grayscale.KifuwaraneLib.Entities.Sfen
{
public class SfenMove
{
/*
public int SrcFile { get; private set; }
public int SrcRank { get; private set; }
public bool IsDrop { get; private set; }
public int DstFile { get; private set; }
public int DstRank { get; private set; }
public bool IsPromote { get; private set; }
*/
// 忘れず初期化☆(^~^)!
public char[] Chars { get; private set; } = new char[5] { ' ', ' ', ' ', ' ', ' ', };
public SfenMove(SfenMoveBuilder builder)
{
/*
this.SrcFile = builder.SrcFile;
this.SrcRank = builder.SrcRank;
this.IsDrop = builder.IsDrop;
this.DstFile = builder.DstFile;
this.DstRank = builder.DstRank;
this.IsPromote = builder.IsPromote;
*/
Array.Copy(builder.Chars, this.Chars, builder.Chars.Length);
}
}
}
新きふわらね:
// 呼出し口
var moveB = new SfenMoveBuilder()
{
Str1st = m.Groups[1].Value.ToCharArray()[0], // 123456789 か、 PLNSGKRB
Str2nd = m.Groups[2].Value.ToCharArray()[0], // abcdefghi か、 *
Str3rd = m.Groups[3].Value.ToCharArray()[0], // 123456789
Str4th = m.Groups[4].Value.ToCharArray()[0], // abcdefghi
};
if ("+" == m.Groups[5].Value) // + か、無し。
{
moveB.Str5th = m.Groups[5].Value.ToCharArray()[0];
}
SfenMoveReferences.GetData_FromTextSub(
moveB.Build(),
out move,
kifuD,
logTag
);
// 呼び出される方
public static void GetData_FromTextSub(
SfenMove sfen,
out IMove move,
Kifu_Document kifuD,
ILoggerFileConf logTag
)
{
}
「 ↑ これだけだと、入れ物が1つ 余分に増えただけで 嬉しくないが……☆」
「 Array.Copy(...)
を使っていると 初期化してない配列に コピーしようとしてしまう、という不具合があっても
コンパイル時に 見えないんだな……☆」
if ('*' == sfen.Chars[1])
{
//>>>>> 「打」でした。
}
「 ↑ 2文字目が *
だったら 打 だと分かるんだが、こういうのは ルール と呼ぶんだぜ☆
ルールは……☆」
SfenMove.rs:
using System;
namespace Grayscale.KifuwaraneLib.Entities.Sfen
{
public class SfenMove
{
/// <summary>
/// 打です。
/// </summary>
public bool IsDrop
{
get {
return '*' == this.Chars[1];
}
}
}
}
「 ↑ メソッドにしよう、というのが オブジェクト指向 のやり方だぜ☆」
if (sfen.IsDrop)
{
//>>>>> 「打」でした。
}
if (sfen.IsDrop) { } // ?
if (sfen.IsDrop()) { }
「 ↑ IsDrop
のうしろに丸かっこが付いてないと おかしくない?」
「 Visual Basic みたいだよな☆ C# の ゲッターは プロパティ方式と メソッド方式があるんで 変な感じになる……☆
.IsDrop
にするか .Dropped
にするか揺れるの嫌なんで☆」
「 メソッド名を動詞から始めるなんて Javaのような前時代の産物だろ☆ .Dropped にしようぜ☆?」
「 お前、独自表現のコード多いな……☆
SFENじゃないコードは 何と呼べばいいのか……☆?」
「 クリーン・アーキテクチャーの Entity層を 1つ増やすか……☆ 内側が Sfen、外側が ApplicatedGame という名前で☆」
「 そんなことを言っても、ロガーは 依存しまくりなんじゃないのかだぜ☆?」
「 ロガーは インターフェース として使うことになるんだろうけどな☆
しかし ロガー は色んな種類を使いたいぜ☆」
「 C#言語は 列挙型 を継承できないから、何かいい 構造を考えなさい。
あとから変更しても 下部構造に影響がなく、
下部構造で追加しても 上位構造の変更の妨げにならないような」
「 ↑ ログ・ファイルを修正したんで 画像が出るようになった……☆
これは 利きがどこにある を表しているんで 駒の画像ではなく マスを塗りつぶした方が良かった……☆」
/
|
+-- Builds
| |
| +-- Debug
| |
| +-- Release
| |
| +-- *Engine.exe # 将棋エンジン
|
+-- Logs # 消されてもいいファイルが溜まっていくのはここ。
|
+-- Profile
| |
| +-- Engine.toml # 代表的な設定ファイル
|
+-- Resources
|
+-- References
|
+-- Sources
|
+-- *.sln
「 バリバリの Windows プログラミングで作っているので Docker に向いてない……☆」
「 きふわらずさ のソースコードは 大きく4つに分かれるが☆」
「 わたしが開発しているわけではないソースは References ディレクトリーに移そうぜ☆?」
「 練習のソースとか、同人ゲームにしようとしたソースが混ざってるんだぜ☆」
|
+-- Sources
|
+-- SourcesOfPowerShell
|
+-- SourcesOfAims
|
+-- SourcesOfLua
「 ↑ 階層を深くしたくないんで、トップ・ディレクトリーでフォルダーを分けようぜ☆?」
「 将棋ソフトの .exe ファイルはどれなんだぜ☆?
Builds
ディレクトリーの下に Release
は無いし、 ディレクトリーがいっぱいあるぜ☆?」
|
+-- Builds
|
+-- BuildsOfAnothers
「 ↑ Builds
の下の階層は固定したいんで、 BuildsOfAnothers
を別途用意しようぜ☆?」
「 ファイル・パスをハード・コーディングしている箇所を すべて設定ファイルに 出しましょう!」
例外がスローされました: 'System.Threading.ThreadAbortException' (mscorlib.dll の中)
スレッド 0xb84 はコード 0 (0x0) で終了しました。
例外がスローされました: 'System.Threading.ThreadAbortException' (mscorlib.dll の中)
スレッド 0x78 はコード 0 (0x0) で終了しました。
例外がスローされました: 'System.Threading.ThreadAbortException' (mscorlib.dll の中)
スレッド 0x12c0 はコード 0 (0x0) で終了しました。
「 きふわらかく と、 きふわらぎょく は 同じ不具合を抱えているんだが、 スレッドがアボートしていてはダメだよな☆
これは 将棋エンジンが起動して usi
コマンドを入力するまでの数秒の間、またはその前後のどこかに起こるぜ☆
起こった時は usi に対して返事がない☆」
「 ソースコードを Log
というディレクトリに分けたら .gitignore
で無視された……☆」
「 きふわらかく、 きふわらぎょく の両方にあって、 きふわらずさ、きふわらけい にないのが このコード☆
知り合いの同人サークルに頼んで作ってもらった関数なんで これを差し替えるのは 骨が折れるぜ☆」
「 ノン・ブロッキングIO(アイ・オー)が やりたかったんだぜ☆
1回うまく動けば、あとは ずっと動いていたと思うんだが、最初の1回に失敗すると動かない☆
泣く泣く ブロッキングIO に変えるぜ……☆」
コンピューター将棋ソフトきふわらべの関数名や変数名を流行りに合わせようぜ☆(^~^)?
「 C#言語の #region
、 #endregion
は使わないようにしようぜ☆?
開けるために クリック する手間が膨大……☆」
「 ↑ このビルド番号、かっこ悪いんで、バージョン番号は セマンティック・バージョニング を使いたいぜ☆」
「 ↑ バージョンはアセンブリ・インフォに書け、という C# の文化があるんだが、 Rust言語は そんな文化ないんで統一性がめんどくさ……☆」
Version version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
Program.Send($"id name {engineName} {version.Major}.{version.Minor}.{version.Build}");
「 コンピューター・プログラミングの 大統一インターフェースって 無い物ねぇ」
「 ↑ きふわらべーず は、こんな感じの プログラム名で表示するのを……、推奨にしようぜ☆? マストにするのは 不便だぜ☆」
「 メジャー・バージョン番号が5桁ぐらいになれば また嫌になって ローカル・ルール(きふわらべコード) を変えたくなるだろう☆」
「 ↑ こうすれば 表示名もすっきり……、あっ☆! れさかい もセマンティック・バージョニング使ってやがるぜ☆!」
「 Clean Architecture に少しは寄せて、 プロジェクトを Entities, UseCase, Engine, Gui の4つに分けましょう」
「 ↑ わたしには この方が よく分解できているんだが とりあえず Clean Architecture を身に付けるために変えていくかだぜ……☆」
「 USI のループって Clean Architecture のどのレイヤーにあるべきなのかしら?」
「 Controller じゃないか☆? きふわらべーず では Engine と名前を変えているが……☆」
「 Main 関数の近くに USI Loop を置いたぜ☆」
「 将棋エンジンって コマンドラインにならないのかだぜ☆?」
「 UseCases に USI のインターフェースを置いて、 Engine に USI インターフェースの実装を置いた方がよくない?」
「 USI プロトコルが Clean Architecture かどうか知らないが……☆
指し将棋をします、とか 検討します、とかが UseCase だろ☆」
Clean Architecture で将棋エンジンを作ろうぜ☆(^~^)?
「 ↑ Engine にパーサー、コマンドに対応する処理は UseCases に置けばいいか……☆」
連番 | 名前 | 進捗 |
---|---|---|
1 | Kifuwarane | done |
2 | Kifuwarazusa | done |
3 | Kifuwarakaku | |
4 | Kifuwarube | |
5 | Kifuwaragyoku | |
6 | Kifuwarakei | |
7 | Kifuwarabi | |
8 | Kifuwarust | |
9 | Kifuwarachevy |
「 きふわらべの弱さの原因の1つが分かったぜ☆
エラーの発生個所でログを取っていることだぜ☆ これは例外処理で投げ上げてしまった方がいい☆」
Log, Exception の見直し
連番 | 名前 | 進捗 |
---|---|---|
1 | Kifuwarane | doing |
2 | Kifuwarazusa | doing |
3 | Kifuwarakaku | doing |
4 | Kifuwarube | |
5 | Kifuwaragyoku | |
6 | Kifuwarakei | |
7 | Kifuwarabi | |
8 | Kifuwarust | |
9 | Kifuwarachevy |
「 ↑ リリースモードの中でトレース・ログを取るのを止めたいが、改造が進むのは 少しずつだぜ☆」
「 このレベルで エンバグしてしまうのは リファクタリングの失敗にしても 何を踏んだか分からん……☆」
「 上に デバッグ・アサート2行書いてるのに 突き抜けてくんの?」
「 デバッグ・モードまで リファクタリングしてないしな……☆」
「 ↑ どうぶつしょうぎ は遊べる……☆ 表示は狂ってるけど☆」
「 フォルダーをずらしたとき、設定ファイルへのパスが 変わってて 本将棋の設定が外れていた……☆」
「 コールバック関数を使えばいいところで 使ってないケースがあることに気づいたぜ☆」
「 ↑ 18時間ぐらい キーパンチャーして思ったんだが、スタックトレースの出し方があるんじゃないかだぜ☆?」
「 ↑ C#でもスタックトレースが出るぜ☆ 行番号まで分かる☆」
「 ↑ きふわらけい は Unity の古い C# に対応していたが、もう Unity 使わないだろ☆
削ろかな……☆」
「 削った……☆ Unity に関わった時間は 何にもつながらないこととなったぜ☆」
/
|
+-- Builds
|
+-- AnyCpu
| |
| +-- Debug
| |
| +-- Release
|
+-- x64
|
+-- Debug
|
+-- Release
「 ↑ ビルドしたファイルを置くところを、 CPUアーキテクチャ のディレクトリ1個分 深めようと思うぜ☆」
「 あとから あとから どんどん変更が入ってしまうな……☆ CPU アーキテクチャなんか 何年ごとに増えるんだぜ☆?」
/
|
+-- Builds_x64
| |
| +-- Debug
| |
| +-- Release
|
+--Biulds_AnyCpu
|
+-- Debug
|
+-- Release
「 ↑ トップ・ディレクトリーが多くなるかもしらないが Biulds_CPUアーキテクチャ
でどうだぜ☆?
パスカルケースじゃなくなるけど☆」
「 それほど 意味が通じないほどのものでもないんじゃないの」
Console.WriteLine("(^q^)");
// ↓
Logger.Trace("(^q^)");
「 ↑ WriteLine は、デバッグモードだけに有効になる自作の Trace に置き換えると 実行速度がアップするぜ☆」
「 x64 アーキテクチャ対応を 先に進めていこうと思うぜ☆」
連番 | 名前 | 進捗 |
---|---|---|
1 | Kifuwarane | done |
2 | Kifuwarazusa | done |
3 | Kifuwarakaku | |
4 | Kifuwarube | |
5 | Kifuwaragyoku | |
6 | Kifuwarakei | |
7 | Kifuwarabi | |
8 | Kifuwarust | |
9 | Kifuwarachevy |
Visual Studio.NETプラットフォームのターゲットの説明
「 ↑ 説明を読むと、 開発者は どの CPUアーキテクチャ の上で動いて欲しいか、を表すフラグに過ぎないらしいぜ☆
x64 を選んだからといって x64 対応になるのではなく、 x64 以外の CPU で実行されることを拒否するだけらしいぜ☆」
Visual Studio C# の CPU構成を設定するテクニックを説明しようぜ☆(^~^)?
「 ついさっき Visual Studio 2019 の 面白い機能を見つけたぜ☆!」
「 ↑ 文字列を選択し……、なんで 豆電球が点いてるんだろな☆?」
「 ↑ 豆電球をクリックすると、いろいろ コーディングをやってくれるんだぜ☆」
「 ↑ 例えば 補間された文字列に変換する
を選ぶと、 Java Script の 補間文字列 みたいな書き方ができるぜ☆」
「 ↑ そして メソッドを抽出する
を選ぶと、 メソッドに切り出してくれるから、見にくい書式から おさらば だぜ☆」
「 ↑ きふわらべの謎☆
LogFilePath( ) という関数があり、あー、ここにログが保存されてるのか、と一見思うが この関数はどこからも使われていない☆
じゃあお前、どうやって どこにログを保存してるんだぜ☆?」
<書きかけ>
Crieitは個人で開発中です。
興味がある方は是非記事の投稿をお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください!