「 1マスずつ指定するの めんどうなんだけど。
j10-k10
みたいに指定できないの?」
「 -
は引き算に見えるので、Excelを真似て j10:k10
だな☆
これもパーサーを作るとすれば RangeCell
クラスとか作るといいかも知れないな☆」
CellAddress.cs:
public static int ToIndex(int rowNumber, int columnNumber)
{
return (19 - rowNumber) * 19 + columnNumber;
}
public int ToIndex()
{
return ToIndex(this.RowAddress.Number, this.ColumnAddress.Number);
}
「 行番号、列番号から配列のインデックスを求める式なんだが、
static関数に抽出しておいた方が 応用が効くよな☆」
public static CellAddress FromIndex(int rowNumber, int columnNumber)
{
return new CellAddress(new RowAddress(rowNumber), new ColumnAddress(columnNumber));
}
public static CellAddress FromIndex(int index)
{
var rowNumber = index / 19;
var columnNumber = index % 19;
return new CellAddress(new RowAddress(rowNumber), new ColumnAddress(columnNumber));
}
「 行番号、列番号から セル番地オブジェクトを作る static関数があると便利☆
引数は 行、列 の並び順に変更した☆」
「 国際式の囲碁盤は I列 が欠番なので、 行番号 が下から上に並んでるので、
Excel のつもりでプログラム書くと 混乱するんだが☆」
「 国際式の囲碁盤なんか覚えても 何も嬉しくないわよ!
内部は Excel 式に作って、入出力だけ 国際式の囲碁盤のフィルターをかましなさい」
ExcelGoBard.cs:
namespace kifuwarabe_uec11_gui.Script
{
/// <summary>
/// Excel式の囲碁盤。
/// </summary>
public static class ExcelGoBoard
{
public static CellAddress ToInternationalGoCellAddress(CellAddress cellAddress)
{
return cellAddress;
}
public static RowAddress ToInternationalGoRowAddres(RowAddress rowAddress)
{
return rowAddress;
}
public static ColumnAddress ToInternationalGoColumnAddres(ColumnAddress columnAddress)
{
return columnAddress;
}
}
}
InternationalGoBoard.cs
namespace kifuwarabe_uec11_gui.Script
{
/// <summary>
/// 国際式囲碁(International go)の囲碁盤
/// </summary>
public static class InternationalGoBoard
{
public static CellAddress ToExcelCellAddress(CellAddress cellAddress)
{
return cellAddress;
}
public static RowAddress ToExcelRowAddres(RowAddress rowAddress)
{
return rowAddress;
}
public static ColumnAddress ToExcelColumnAddres(ColumnAddress columnAddress)
{
return columnAddress;
}
}
}
「 まず 透過フィルターを作り、ここに 変換コードを 集めていくぜ☆」
「 あっ、ダメだぜ☆
言語処理系は コードが ぶわーっと爆発的に増えていくんで、
メモリ節約よりも コーディング量節約、
実行速度よりも キータイピング速度の方を優先しなくてはいけないぜ☆」
(カタ カタ カタ カタ) # キータイピングの音。
「 …………☆」
Output:
c7:e9 | C7 D7 E7 C8 D8 E8 C9 D9 E9.
e9:c7 | E9 D9 C9 E8 D8 C8 E7 D7 C7.
「 よし、 Excel式の範囲表記で Foreach 回すのでけた☆ 飯食べよ☆」
black k10 a7:b10 m12 e1:e13 f17
「 そのように作れば できるが……☆ その場合 大で小を兼ねるようにする☆
例えば……☆」
black k10:k10 a7:b10 m12:m12 e1:e13 f17:f17
「 ↑上のような 書き方を認めることだぜ☆
k10
は、 k10:k10
の省略形ということにするんだぜ☆」
「 お父ん、そろそろ テストコードを書けだぜ☆
目視で確認できる分量じゃなくなっただろ☆」
「 これで input.txt
ファイルを保存すれば 5秒以内に 囲碁盤の a2:b1
などの範囲、
盤の左下あたりだな、ここらへんの石が 空白に変わるはずだぜ☆」
「 上下が 逆じゃない? それに 19行ではなく 18行から石が消えてるから 1 ずれてるわよ」
Excel like:
Row | Index | Column | Index |
---|---|---|---|
0 | 0 | A | 0 |
1 | 19 | B | 1 |
2 | 38 | C | 2 |
3 | 57 | D | 3 |
4 | 76 | E | 4 |
5 | 95 | F | 5 |
6 | 114 | G | 6 |
7 | 133 | H | 7 |
8 | 152 | I | 8 |
9 | 171 | J | 9 |
10 | 190 | K | 10 |
11 | 209 | L | 11 |
12 | 228 | M | 12 |
13 | 247 | N | 13 |
14 | 266 | O | 14 |
15 | 285 | P | 15 |
16 | 304 | Q | 16 |
17 | 323 | R | 17 |
18 | 342 | S | 18 |
「 ↑static関数を使うと 継承した子クラスでも ここだけ継承せず 親のメソッド使う みたいになるのか☆
static 外そ……☆」
「 さっきの表は Excel式 の盤で、国際式囲碁だと 行が ひっくり返るのかだぜ☆」
International go:
Row | Index | Column | Index |
---|---|---|---|
0 | 342 | A | 0 |
1 | 323 | B | 1 |
2 | 304 | C | 2 |
3 | 285 | D | 3 |
4 | 266 | E | 4 |
5 | 247 | F | 5 |
6 | 228 | G | 6 |
7 | 209 | H | 7 |
8 | 190 | J | 8 |
9 | 171 | K | 9 |
10 | 152 | L | 10 |
11 | 133 | M | 11 |
12 | 114 | N | 12 |
13 | 95 | O | 13 |
14 | 76 | P | 14 |
15 | 57 | Q | 15 |
16 | 38 | R | 16 |
17 | 19 | S | 17 |
18 | 0 | T | 18 |
「 上下を ひっくり返し過ぎているのかも知れない☆
見直そうぜ☆」
「 1つ気づいたことがあるぜ☆ H7
は 7 行目だが、 0 で始まる行数で数えると 6 だぜ☆」
Can be read in Z-Shaped:
|Row |Index|Column|Index|
|----|---|---|---|
| "1"| 0|A| 0|
| "2"| 19|B| 1|
| "3"| 38|C| 2|
| "4"| 57|D| 3|
| "5"| 76|E| 4|
| "6"| 95|F| 5|
| "7"|114|G| 6|
| "8"|133|H| 7|
| "9"|152|I| 8|
|"10"|171|J| 9|
|"11"|190|K|10|
|"12"|209|L|11|
|"13"|228|M|12|
|"14"|247|N|13|
|"15"|266|O|14|
|"16"|285|P|15|
|"17"|304|Q|16|
|"18"|323|R|17|
|"19"|342|S|18|
International Go:
|Row |Index|Column|Index|
|----|---|---|---|
| "1"|342|A| 0|
| "2"|323|B| 1|
| "3"|304|C| 2|
| "4"|285|D| 3|
| "5"|266|E| 4|
| "6"|247|F| 5|
| "7"|228|G| 6|
| "8"|209|H| 7|
| "9"|190|J| 8|
|"10"|171|K| 9|
|"11"|152|L|10|
|"12"|133|M|11|
|"13"|114|N|12|
|"14"| 95|O|13|
|"15"| 76|P|14|
|"16"| 57|Q|15|
|"17"| 38|R|16|
|"18"| 19|S|17|
|"19"| 0|T|18|
reversedRow = 19 - row
「 ↑あと、行番号をひっくり返すとき こう書けばいいかと思っていたんだが……☆」
reversedRow = 18 - row
「 はい、はい、はい! 何年プログラムやってんの?!
マジック・ナンバーも 定数にしてしまいなさい!」
「 お父ん☆ 手こずるなら 囲碁プログラミングで 手こずろうぜ☆?
パーサーに時間費やしても 何にもならないぜ☆?」
「 何にもなれた人に尊敬できる人物はいない☆ 何にもならないところを進めるのは わたしだぜ☆」
「 ↑4回目ぐらいの挑戦だぜ☆
内部的には テキスト読みと同じく Z字方向にインデックスが振られている☆
内部的な計算で 上下ひっくり返すのは 要らないかもしれない☆ 処理を省いてみたぜ☆」
「 あー、わかったぜ☆
コマンドを読み取るパーサーが Z字方向式 を使っている☆ これを 国際囲碁式 に変えよう☆」
「 で、いつになったら 囲碁プログラミングを始めるんだぜ☆?」
input.txt
space a19:t1
white e15:p5
white c13:d7
white f16:n16
white g4:n4
white q13:r8
black g13:j11
black m14:o10
black f7:k7
black j6:n6
black e18:f15
black o18:r15
「 黒白 交互に置いて 手数を進めてくれるコマンドが欲しいよな☆」
「 禁じ手や、石を取るルールを組み込むのは まだ むずかしいんで……☆」
「 最後に着手した石の上に 赤い四角 を置くぐらいは したいな……☆」
「 画面の再描画処理の うまい方法が分からん……☆ 雑に書き進めるか……☆」
「 set agehama 10
コマンドも作ってしまったらどうだぜ☆?」
「 あんたら何で GUI 作ってんのよ! さっさと 思考部 作りなさいよ!」
How to: Paint an Area with a Linear Gradient
WPFで画像の周囲に境界線を付けるにはどうすればよいですか。
「 ↑Xaml があれば ゲーム画面 簡単に作れるんじゃないのか……☆
グラデーションを描く方法も調べよう……☆」
「 ↑簡単に 角丸の欄が描ける☆ もっと調整すれば かっこよく なりそう☆」
「 長方形にするから 麻雀牌っぽくなるのよ! 丸にしなさい、
ちっがーう! GUI に凝るのは 沼なのよ!」
Width="{Binding Width, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Canvas}}"
「 ↑親要素の Canvas と同じ幅にするには こう書くみたいだが、 CSS に慣れてると、めんどくさいな☆」
set ply 0
set move Z99
set b-name Aaaaaaaa
set b-time 99:59
set b-hama 999
set w-name Bbbbbbbb
set w-time 99:59
set w-hama 999
set komi 99.5
「 ↑こんな感じのコマンドを用意すれば いい感じじゃないか☆?」
「 名前が 半角スペース区切り だったりしたときの取り扱いが めんどくさくない?」
「 古くは .ini
ファイル、Java なら .properties
ファイルが そんなんだったよな☆」
set ply = 0
set move = Z99
set b-name = Aaaaaaaa
set b-time = 99:59
set b-hama = 999
set w-name = Bbbbbbbb
set w-time = 99:59
set w-hama = 999
set komi = 99.5
「 ↑こういうフォーマットなら 楽そうだ……☆ やってみるかだぜ☆」
「 コマンドラインの set
の癖で、イコールを忘れてしまう……☆」
「 スペース区切りにすると 打鍵しやすいが、視認性が下がるだろ☆」
「 代入に =
記号を使うのは いやらしくない? <-
にしましょうよ」
set ply <- 0
set move <- Z99
set b-name <- Aaaaaaaa
set b-time <- 99:59
set b-hama <- 999
set w-name <- Bbbbbbbb
set w-time <- 99:59
set w-hama <- 999
set komi <- 99.5
「 Label では改行ができないのか……☆ \n
、
、 <LineBreak/>
、 

、 

どれもダメ☆」
「 TextBox でも改行ができないのか……☆ \n
、\r\n
、 

、 

どれもダメ☆」
「 \n
がただの文字と判定されてしまっている☆
わたしの方で 改行コードに置換してもいいが……☆」
// 改行コードに対応☆(^~^)ただし 垂直タブ(めったに使わんだろ) は除去☆(^~^)
// (1) 垂直タブ は消す。
// (2) \\ は 垂直タブ にする☆
// (3) \n は 改行コード にする☆
// (4) 垂直タブは \ にする☆
var temp = prop.Value.Replace("\v", "", StringComparison.Ordinal);
temp = temp.Replace("\\\\", "\v", StringComparison.Ordinal);
temp = temp.Replace("\\n", "\n", StringComparison.Ordinal);
temp = temp.Replace("\v", "\\", StringComparison.Ordinal);
commentValue.Content = temp;
「 ↑垂直タブ \v
をサクリファイス(犠牲)にすることで 改行がでけた……☆」
input.txt:
set comment = aaa\nbbb\nccc
「 ↑左下に ちょこっと テキスト表示できるようにしておいた……☆
何らかの場面で助かるだろ☆」
「 原理的に コンピューター囲碁 の GUI は できあがったんじゃないの?」
「 input.txt の読み込み中に こっちが書き込んだら そのあと input.txt の中身を空っぽにしようと保存してジャムるのがあるだろ☆」
「 output.txt が要るかだぜ☆ もう input.txt に書き込んでいいよ、というフラグにするやつ☆
output.txt ファイルが存在する間は、 input.txt を読み込まないんだぜ☆」
「 人間の操作で output.txt を削除するのは手間よ」
「 comment
が画面に表示されるって 違和感なくない?」
「 set info = banana
は、シンタックス・シュガーとして info banana
と書くこともできるようにしないか☆?」
「 この GUI は、盤面がどんな状況にあるのか アウトプット してくれないのかだぜ☆?」
「 なるほど……☆ output.txt
は やっぱり欲しいかだぜ☆」
How to serialize and deserialize JSON in .NET
「 ↑ .NET Core 3.0 に標準で付いてくる JSON シリアライザーを使いたいよな☆」
OutputJsonDocument.cs:
namespace KifuwarabeUec11Gui.Script
{
using System.Text.Json;
/// <summary>
/// `output.json` をこれで作ろうぜ☆(^~^)
/// </summary>
public class OutputJsonDocument
{
public State State { get; private set; }
public OutputJsonDocument(State state)
{
this.State = state;
}
public string ToJson()
{
var option = new JsonSerializerOptions();
// JSON は JavaScript 由来だろ☆(^~^) JavaScript に合わせようぜ☆(^~^)
// camelCase
option.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
// インデントしようぜ☆(^~^)
option.WriteIndented = true;
return JsonSerializer.Serialize(this,option);
}
}
}
ToJson():
{
"state": {
"ply": 0,
"lastMoveIndex": -1,
"blackName": "",
"blackTime": "",
"blackHama": 0,
"whiteName": "",
"whiteTime": "",
"whiteHama": 0,
"komi": 0,
"info": ""
}
}
「 ↑こんな感じでコード量増やさずに さくっと JSON を出力してくれるぜ☆」
「 input.txt を読みにくる間隔をミリ秒で指定できる interval-msec
と、
アプリケーションを安全に終了する exit
も欲しくない?」
「 画面上の 石に当たる楕円オブジェクトと、ゲーム・データとしての石の色と、
それを同時に更新するプログラムの、ビュー・モデル・コントローラーの3つに分けるの大変☆」
「 ↑石を配列として持つと 1, 1, 1, 1, …みたいなのが縦に361行もできてしまうんだが☆」
「 output.json
はどんなタイミングで書きだされるの?」
「 とりあえず、 input.txt
を GUI に反映したタイミングだぜ☆」
「 あっ、 set move = D4
をすると D16
に赤い四角が表示されるバグがあるぜ☆!」
public static int ConvertIndex(int zShapedIndexO0)
{
var zShapedRowO0 = zShapedIndexO0 / InputScriptDocument.BoardSize;
var zShapedColumnO0 = zShapedIndexO0 % InputScriptDocument.BoardSize;
return (InputScriptDocument.RowLastO0 - zShapedRowO0) + zShapedColumnO0;
}
public static int ConvertIndex(int zShapedIndexO0)
{
var zShapedRowO0 = zShapedIndexO0 / InputScriptDocument.BoardSize;
var zShapedColumnO0 = zShapedIndexO0 % InputScriptDocument.BoardSize;
return (InputScriptDocument.RowLastO0 - zShapedRowO0) * InputScriptDocument.BoardSize + zShapedColumnO0;
}
「 囲碁のルールを入れてくれだぜ☆ bestmove D4
で やりたいんだぜ☆」
「 そんなん大変だぜ☆ 19路盤 と思って カッチリ作ってるんだぜ☆」
「 じゃあ 諦めるのが 賢い判断なのか……☆
コンピューター囲碁大会までの 時間もないしな☆」
(カタ カタ カタ カタ カタ カタ カタ カタ カタ カタ) # キータイピングの音。
「 ↑例えば 19で決め打ちしているところは 変化に付いてこれない☆」
「 19 が起点になっている時点で ややこしさ が加わっていく☆」
「 バグを直すために バグの上で正しく動くおかしなコードを書くのは 止めなさい」
「 見ないことにしてくれだぜ☆
コンピューター囲碁大会が 迫ってるんで☆」
<次回の記事に続く>
Crieitは個人で開発中です。
興味がある方は是非記事の投稿をお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください!