今更ながら年始の雑文で穴埋めを | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/3978
の続き。
ロジックの確認のために、下記のようなチープな画面を作成する。
この手のロジックをチェックするのは、UnitTest を使うのがいいのですが、今回は UI のチェックも兼ねるので、Windows フォームを使っています。最初は、いきなり Windows ストア アプリにしようかと思ったのですが、UI のコーディングがバインドと混ざってややこしくなるので、フォームアプリで作成ということで。
フォームに書いたコードは使い捨てになるので、できるだけ簡潔に…と云いますか、フォームでもWindows ストア アプリでもそれなりに動くようにしたいので(コードレベルでは無理なので、ロジックの手続きレベルで)、ロジックの呼び出しテストも兼ねて。
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
_board.Player1.EventSelCard += Player1_EventSelCard;
listBox1.Sorted = true;
listBox2.Sorted = true;
listBox3.Sorted = true;
listBox4.Sorted = true;
listBox5.Sorted = true;
}
/// <summary>
/// 2枚から選択する
/// </summary>
/// <param name="c1"></param>
/// <param name="c2"></param>
/// <returns></returns>
Card Player1_EventSelCard(Card c1, Card c2)
{
string msg = string.Format(
"{0} と {1} があります。{2} を選択しますか?",
c1.ID, c2.ID, c1.ID);
var btn = MessageBox.Show(msg, "", MessageBoxButtons.YesNo);
if (btn == System.Windows.Forms.DialogResult.Yes)
{
return c1;
}
else
{
return c2;
}
}
GameBoard _board = new GameBoard();
Player _curPlayer;
/// <summary>
/// 山から1枚取得
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
Card c = _board.Yama.GetCard();
_board.Ba.PutCard(c);
// 画面の更新
ScrUpdate();
}
/// <summary>
/// マッチしたカードを場から取得
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
var lst = _board.Game.MatchBa(_board.Ba, _board.Ba.PlayerCard, _curPlayer);
_board.Ba.GetCard(lst);
_curPlayer.GetCard(lst);
// 画面の更新
ScrUpdate();
}
/// <summary>
/// 花札を配る
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button5_Click(object sender, EventArgs e)
{
// 花札を配る
_board.Reset();
_curPlayer = _board.Player1;
// 画面の更新
ScrUpdate();
}
/// <summary>
/// player1のカードを場に出す
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button3_Click(object sender, EventArgs e)
{
if (listBox2.SelectedIndex == -1)
return;
var c = (Card)listBox2.SelectedItem;
_board.Player1.IntoBa(c);
_board.Ba.PutCard(c);
_curPlayer = _board.Player1;
// 画面の更新
ScrUpdate();
}
/// <summary>
/// player2のカードを場に出す
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button4_Click(object sender, EventArgs e)
{
if (listBox4.SelectedIndex == -1)
return;
var c = (Card)listBox4.SelectedItem;
_board.Player2.IntoBa(c);
_board.Ba.PutCard(c);
_curPlayer = _board.Player2;
// 画面の更新
ScrUpdate();
}
/// <summary>
/// 画面の更新
/// </summary>
void ScrUpdate()
{
listBox1.Items.Clear();
listBox2.Items.Clear();
listBox3.Items.Clear();
listBox4.Items.Clear();
listBox5.Items.Clear();
listBox1.Items.AddRange(_board.Ba.Cards.ToArray());
listBox2.Items.AddRange(_board.Player1.MyCards.ToArray());
listBox3.Items.AddRange(_board.Player1.TakenCards.ToArray());
listBox4.Items.AddRange(_board.Player2.MyCards.ToArray());
listBox5.Items.AddRange(_board.Player2.TakenCards.ToArray());
label7.Text = _board.Ba.PlayerCard.ToString();
}
}
画面のロジックは基本はユーザーからのイベントドリブンで、ユーザーがなんらかの選択をした時に発生します。昔は業務ロジックと画面からのイベントを分離させるために、「イベントごとに関数を作る」ってなことをやってたりしますが、煩雑なので、最近はパスですね。ですが、NUnit などの自動テストを有効に働かせるためには、画面のほうに業務ロジックを入れないようにするのがベター…なのですが、いまのところこれといった定番がありません。
業務アプリのような簡単な(?)画面の場合には、MVVM か MVC で分離させて、業務ロジックを NUnit でテストして、くっつけた上でアプリ上で再テストって手順がよいのですが、ゲームの場合はどうなんでしょうね?ってのが、今の私の(解消しなければいけない)疑問点です。
そんな訳で、ユーザーがアクションをするボタンの類と、画面更新 ScrUpdate という簡単な組み合わせにしています。このボタンイベントのところが長くなったら、適宜ロジックのほうへ移行ってな雰囲気で組み立てるとよいかと。

