studio Odyssey



しゃちょのスタジオ日誌

 日記的なもの。

2008.08.23

Team Foundation Server2008のバックアップからのリストアの方法

手動トラックバック先URI
http://www.studio-odyssey.net/content/note/archive03.htm#h2308

 本日のキーワードは、Team System, Team Foundation, TFS,2008,バックアップ,リストア,移行,別サーバから新サーバへのデータ移行とか、その辺りか。

 仕事で、TFS2005から、TFS2008への新ファームでの移行をすることになって、手順的にはどうしたのかというと、TFS2005から、新TFS2005のファームに移行して、そこで2008にアップグレード、その後、新ファームへの移行という手順を行い、予定では6時間で終わるはずだったのが、12時間以上かかったので、その手順とMSに対しての文句が、今日の趣旨です。

 というか、TFS2008のバックアップリストアの手順なんですけど、これ、日本語のサイトにあるドキュメントの手順では、失敗します。
 具体的には、このURIです。

http://msdn.microsoft.com/ja-jp/library/ms404869.aspx
方法 : あるハードウェア構成から別のハードウェア構成へ Team Foundation Server を移動する

 この手順は、VS2008と書いてあるのですが、TFS2008の手順ではないです。たぶん、TFS2005の手順で、内容がTFS2008っぽくなっているだけです。(日本語サイトが古いのかも)
 なお、この2005手順は、TFS2005の復元に使えます。つまり、2005と2008では、手順が違うのです。

http://msdn.microsoft.com/ja-jp/library/ms404869(VS.80).aspx
方法 : あるハードウェア構成から別のハードウェア構成へ Team Foundation Server を移動する

 まぁ、後半の6時間くらいは、このトラップにひっかかっていたというのは、内緒です。

 正しいドキュメントの位置ですが、日本語はダメです。英語です。
 以下ににあります。

http://msdn.microsoft.com/en-us/library/ms404869.aspx
How to: Move Your Team Foundation Server from One Hardware Configuration to Another

 なお、この手順でも、復元は出来ないか知れません。
 というか、僕は出来ませんでした。

 日本語ドキュメントを含めて、この3つのドキュメントの全てに目を通してから、復旧に臨みましょう。っていうか、復旧しなきゃいけない状態の時に、そんな余裕があるかボケェ!?
 ということで、これは会社のMVPの人に言っておくので、将来的には、ドキュメントが治るかも知れませんので、このアーカイブの役目は、それまでかも知れません。

 とりあえず、手順をさらっと見ていきますが、基本的には英語版の手順に従ってください。
 英語が読めない人は、日本語のヘルプを参考にしてください。やることは基本は一緒です。(やらないことと、やり方が違うことはありますが)

 まず、Reporting Serviceのキーと、SQL Serverのバックアップの方法は一緒です。
 この時、TFS2005だと、SharePoint(以下、WSS)のバージョンが2.0なので、コンテンツデータベース名がSTS_CONTENT_TFSですが、WSS3.0でセットアップしたTFS2008には、ちゃんとバージョンアップしておけば、WSS_Contentで復元できますので、安心してください。ただ、TFS2008のディスクに入っているWSS3.0は、SP1が当たっていないので、TFS2005から2008にアップデートして移行する時は、ちゃんとWSSのアップデートバージョンをあわせてください。(またはTFS2008側にSPを当てるかです)

 データベースの復元の手順ですが、英語ドキュメントでは、サービスで、SharePoint Timer Service or Windows SharePoint Services Timer、Visual Studio Team Foundation Server Task Scheduler Serviceを止めろと書いてあります。日本語でもそうです。ただ、英語では、レポートサービスを止めろと書いてあるのが、管理ツールのサービスからというように読めますが、レポートサーバの構成から止めてもいいと思います。っていうか、その方が楽です。
 アプリケーションプールは、レポートサーバのものとTFSのものを止めます。
 ついでに、英語版ドキュメントでは、WSSのサイト(既定のWebサイト)を止めろと書いてあります。(レポートのサイトが違うならそれも止めろと書いてあります)
 このあたりは、サーバの構成によりますが、止めても止めなくて、たぶん平気です。僕は止めませんでした。(WSSの既定のWebサイトはすぐにスタートするので、止めない方が楽です)

 サービスを停止したら、データの復元です。
 この手順は同じです。STS_Content_TFSは、バージョンアップしてあれば、WSS_Contentに復元できます。

 あ、そうだ。
 TFS2008では、WSS_Configと、WSS_Adminがありますが、これは復元すると大変なことになるので、ふれないでください。念のため、新サーバは、バックアップをとっておくとよいいでしょう。

 データの復元は、それほど困らないかと思います。SQL Serverの復元は簡単だなぁ。

 復元が終わったあと、日本語の手順に行くと、失敗します。
 英語の手順で続けてください。

 次にすべきことは、Restore Web Sites for Team Projects(チーム プロジェクト Web サイトの復元)です。
 なお、日本語手順でこのセクションに飛んで、その通りに処理しても、WSS3.0はエラーを返すので、英語の手順で復元してください。
 具体的には以下の流れです。

  • 管理ツールから、SharePoint3.0管理サイトを開け
  • コンテンツ データベースで、WSS_Contentを開け
  • WSS_Contentを削除しる
  • コマンドプロンプトで、%programfiles%\Common Files\microsoft shared\web server extensions\12\BIN にいけ
  • stsadm.exeを、stsadm -o addcontentdb -url http://SharePointServerName -databaseserver newDataTierServerName -databasename WSS_Content と、実行しろ。ただし、WSS_Contentはお前の環境では違うかもしんないから、直せよ
  • もしバックアップしてあるWSSのカスタムテンプレートとかがあるなら、ここで復元できるんだからね!
  • SharePoint Timer Service を開始してもよろしくてよ

 で、チームWebサイトが復元できます。
 なお、英語版のドキュメントはリンクになっているので、念のためリンクを張っておきます。

http://msdn.microsoft.com/en-us/library/cc668750.aspx
How to: Redirect SharePoint Products and Technologies to Use a New Content Database

 次の手順は、Restore and Test SQL Server Reporting Services and Default Reports(SQL Report Server、Reporting Services、および既定のレポートの復元およびテスト)です。
 この手順は、ほとんど日本語と英語で同じですが、この日誌の手順では、英語手順に従います。つか、日本語手順では、前提としてここまででRenameDTとかしてあるので、動いちゃうんですよね。あと、地味に英語手順でも足りないことがあるので、危険です。
 流れ的には、以下。

  • レポートサーバのアプリケーションプールを開始してください
  • レポートサービスの構成を開きます
  • 日本語では、ここでサービスを開始してくださいと書いてありますが、開始しないで大丈夫です。というか、開始しない方がいいんじゃないかと
  • データベース接続ペインで、データ層につなげて、適用をぶちっとやります。っていうか、日本語も英語も書いてある文章が長いのは、操作毎に書くから
  • デュアルサーバモードの人は、なんか処理が必要なので、それもやってください。僕、デュアルサーバモードで動かしたことない。(そもそも、今までWorkGroupだったのを、人数が増えたので、ADにした)
  • 英語手順では、エンタープライズとスタンダードの分岐がなく、スタンダートの手順ぽいですが、スタンダートでつかっていたので、エンタープライズ版の時はどうなるかは無視して、英語版手順のまま進みます
  • コマンドプロンプトで、%ProgramFiles%\Microsoft SQL Server\90\Tools\binn に行って、RSKeyMgmt をつかって、古いキーを削除します。っていうか、ドキュメントの説明はこういう風に細かく書いてくれるのがうれしいけど、僕の説明は細かくなくていいですか?めどいので
  • ちなみに、元々のIDを消すとどうなるのか、僕は知りません
  • 英語版ドキュメントでは、ここでレポートサービスを開始しやがれと言っております
  • レポートサーバの暗号化キーを復活させます。ちなみに僕はここで、別のサーバの暗号化キーを復活させようとして、パスワード違いではじかれまくりました。10分くらい
  • 日本語ドキュメントはまだまだ続きますが、英語ドキュメントはここで終わりなので、レポートサーバの構成はここで一段落です
  • ちなみに、まだレポートは出ません。ももももももちつけ

 次の手順は、Rename the Data-Tier Server and Activate the Application-Tier Server(Team Foundation データ層サーバーの名前の変更と Team Foundation アプリケーション層サーバーのアクティブ化)です。
 日本語ドキュメントでは一番にやることになっているのですが、ここでやらないと、databaseに繋がりません系のエラーが出て、死にたくなります。
 まぁ、ぶっちゃけ、TfsAdminUtil ConfigureConnections /viewを先に解決しておけばいい話なんだと思いますけど、英語ドキュメントではここでやれと書いてあるので、ここでやります。

 手順は以下です。

  • 日誌の手順通りなら、TFSのアプリケーションプールが起動していないはずなので、起動します
  • コマンドプロンプトで%ProgramFiles%\Microsoft Visual Studio 2008 Team Foundation Server\Toolsに行き、TfsAdminUtil ConfigureConnections /view を撃ちます。どきゅーん
  • なんか、変えなきゃいけない系のものが目に飛び込んで来ますが、今回変えるのは、この中の ReportsUri, ReportServerUri の2つです。他は別のステップで自然に変わります。最終的には綺麗になるです
  • この値を変える魔法の呪文は、TfsAdminUtil ConfigureConnections /ReportsUri:NewReports /ReportServerUri:NewReportServer です。NewReports、NewReportServer に、それっぽいパスをいれてください。それっぽいパスというのは、普通に考えたら、コンピューター名が違うだけです。ちなみにこれ、DNSとかで与えられたエイリアスのパスにしたら、どうなるんだべか
  • もっかい、/view して、ちゃんと変わったか確認するのがよい子です
  • 日本語ドキュメントにあるように、RenameDTのための準備をします。具体的には、サービスのconfigで、Connection String の、Data Sourceのサーバ名を古いものに。つか、これ、日本語ドキュメントでは、Sourceと書いてあって、Data Sourceの部分が、機械翻訳で落ちたのかと思ったら、change the value of the Source parameter なのね
  • TfsAdminUtil RenameDT NewTeamFoundationDataTierServerName します。やったね!やっとできるね!
  • TFSのアプリケーションプールを止めます。レポートサーバのも止めます。ついでに、レポートサービスも止めます
  • TfsAdminUtil ActivateAT NewTeamFoundationApplicationTierServerName します。ついにアクティブ化だよ!

 まぁ、これで二段落。
 でも、まだレポートは出ません。

 レポートが出るようにするために、Rebuild the Team System Cubeという手順を行います。日本語ドキュメントにはない手順ですが、やることは、日本語ドキュメントのSQL Report Server、Reporting Services、および既定のレポートの復元およびテスト の14以降の手順に似ています。
 ただ、似ているだけで、この手順じゃないです。
 ドキュメントは変わって、以下のドキュメントです。僕はこれを見逃していて、悩みました。

http://msdn.microsoft.com/en-us/library/cc668753.aspx
How to: Rebuild the Team System Cube

 このドキュメントを見る前に、Understanding the Data Warehouse Architecture.とかのリンクがありますが、基本的にこのドキュメントが必要な時に理解する時間はないのだろうと思うので、理解せずに進みます。

 要するに、やりたいことはTFSのレポートがうごきゃいいんだよ、動きゃ。
 で、なんかいろいろ前提が書いてありますが、TFSが前提をクリアしていないといけないし、そもそもTFSのインストールアカウントでここまでの作業をしているはずなので、無視します。
 さらに、英語ドキュメントの手順と日本語ドキュメントの手順では違いがあり、僕が試したところ、どちらの手順でもダメでした。
 なので、2つのドキュメントのいいとこ取りでやります。
 MSが保証する手順ではないので、ダメでも泣かないでね。僕はこの手順でやったという、ただそれだけのことです。

  • いきなりですが、日誌手順では、日本語ドキュメントにある、TfsReportDSとTfsPlapReportsDSの接続文字列を変更します
  • これをするには、レポートサービスと、レポートのアプリケーションプールが動いていないといけないので、動かします
  • 日本語手順に従い、2つの接続文字列とサービスアカウントを変更します
  • レポートのサービスと、プールを止めます。変わりに、TFSのアプリケーションプールを開始します
  • コマンドプロンプトを開き、%Program Files%\Microsoft Visual Studio 2008 Team Foundation Server\Tools にいって、SetupWarehouse.exe -o -s DataTierServerName -d TFSWarehouse -c warehouseschema.xml -ra TFSReportServiceAccount -a TFSServiceAccount -mturl http://ApplicationTierServerName:Port -l LogFileName を実行します。ここで、-sスイッチはデータサーバ名、-dスイッチは、TFSのWarehouse、デフォルトでは、TfsWarehouse。-raスイッチは、レポートサービスアカウント、-aスイッチはTFSのサービスアカウント。-mturlはTFSのサーバ名の、デフォルトなら8080ポートです。-lスイッチはオプションで、ログファイルが必要ならつけます。なくていいです
  • コマンドが正常に終了したら、英語ドキュメントにあるように、http://localhost:8080/Warehouse/v1.0/warehousecontroller.asmx にアクセスします。GetWarehouseStatus を実行すると、なんか帰ってきます。(たぶん、Idle。それ以外が帰ってくるなら、まだ終わってない)
  • 次に、日本語ドキュメントにあるように、Warehouseのアクセス権を指定して、Analysisサービスを再構成します
  • 具体的には、SQL Server ManagementStudioから、TfsWarehouseに行き、dbo._WarehouseConfigのテーブルに対して、TFSのレポートアカウントを、権限で追加します。RenameDTしているので、名前が変わっているはずです
  • Analisysサービスに接続して、TFSWarehouseを右クリックして、処理をします。
  • 全部終わったら、レポーティングサービスと、レポーティングのアプリケーションプールを開始します
  • レポートサーバにアクセスして(http://localhost/reports)適当なレポートを表示してみます。表示出来なければ、どこかの手順がおかしいです。(レポートサービスの起動のタイミングが、Analisysの前だか後だか覚えていないので、その辺りかも知れません)

 さて、ここまで来れば、あと一息です。Delete the Version Control Cache(バージョン管理のキャッシュの削除)をします。
 日本語ドキュメントでは、Dataフォルダ毎行ってしまいそうですが、Dataフォルダは消さずに、中身を消してください。Delete the contents of the Data subdirectory, but do not delete the Data subdirectory itself.です。

 最後に、Move User and Service Accounts(ユーザー アカウントとサービス アカウントの移動)です。これは、日本語も英語も同じです。
 ただ、注意しなければいけないのは、TFSはワークグループモードで動かすことを、あんまり前提として考えていません。
 なので、コンピューターアカウントはまず、うまく移行できません。ワークグループモードのアカウントは移行できない(特に、WorkからADへの移行のとき)ことが多々あるので、そのようなアカウントは、「あきらめた方が無難」です。
 ちくちくと移行して行きますが、個人的な意見としては、どうせ使うならTFSは早い段階からAD構成にした方がいいです。そうすれば、ドメインの信頼だけで事足りてしまうので。
 サービスアカウントや、セットアップアカウントも、ADに任せた方が無難ですね。

 Restart Services, Refresh the Data Cache on Client Computers(サービスを再起動し、動作を確認するには)を行います。
 日誌手順だと、既にレポートもTFSもあがっているはずですので、WSSのサービスと、TFSのサービスを開始します。
 英語ドキュメントでは2つに分かれていますが、日本語ドキュメントでは1つです。やってることはだいたい同じなので、その通りに行ってあげてください。
 なお、ドメインの名前等、ワークスペースは基本的に壊れるものだと認識した方がいいです。僕は結局ほとんど壊れたので、ワークスペースは消して、作り直しをすることにしました。結構面倒ですが、今後はADで管理することにしたので、今の面倒くさいは、あきらめることにします。

 以上で、TFS2008のリストアは完了です。

 まぁ、ドキュメントにもあるように、WSSとレポートサービスのアカウントの移行方法は存在しないので、ここはあとは手動でなんとかしてください。
 ってか、これがスゲー時間かかるんですけどー。

 TFSはMS製品の中でも、開発者向けの上級製品なので、ディスクいれてポンでは使えないのが面倒ですが(アップグレードだけなら、ディスクいれてぽんですが、WSS3.0にするのはポンじゃないしね)、開発チームと呼べるようなメンバーを抱えているチームの管理者は、これくらいは出来るだろうと思っているのかも知れません。
 まぁ、日本語化チームはたぶん、そのような人は、日本語ドキュメントがおかしいと思ったら、英語ドキュメントを見るだろうと思っていて、英語のドキュメントチームは間違っててもなんとかするだろうと思っているのかも知れませんが。

 SP1が出る製品のドキュメントくらいは、もうちょっとまとめてほしいなぁ。
 ちなみに、夜中作業していたので、インシデント使えませんでした。自力解決。


2008.04.29

じゃんけん

手動トラックバック先URI
http://www.studio-odyssey.net/content/note/archive03.htm#d2908

 研修で利用している本は、「JIS規格対応 標準C#入門」(ISBN:4-7973-3201-8)なのですが、この本に載っている練習問題のじゃんけんゲームが、非常によくできているので、そのお話です。
 まぁ、練習問題は仕様だけがあって、答えはないんですけど、この答えを作るのにあたって、初心者プログラムから、構造化、そしてオブジェクト指向へと、段階的にバージョンアップできてしまうという、よくできた問題です。

 まずは、設問を見てみましょう。

 ユーザーとコンピューターが「じゃんけん」をするプログラムを作成してください。キーボードから「1」「2」「3」を入力することで、グー、チョキ、パーを選択します。入力エラーの場合は再入力としてください。コンピューターは乱数で1、2、3を選択します。じゃんけんの回数は全部で5回とします。あいこの場合は、回数にカウントしません。勝者が確定したら「あなたが3勝2敗で勝ちました!」もしくは、「コンピューターが3勝2敗で勝ちました!」のようにメッセージを表示します。

 と、このような問題です。
 まぁ、オブジェクト作れる人なら、さくっとオブジェクト指向で作ってしまう感じですが、とりあえず、初心者ライクな、だらだらとmainに書くロジックが、以下。

using System;

namespace Janken
{
    class Program
    {
        static void Main(string[] args)
        {
            int count = 0;
            int kati = 0;
            int make = 0;

            while (5 > count)
            {
                int input = 0;

                while (input == 0)
                {
                    Console.Write("入力してください。(1:グー,2:チョキ,3:パー):");
                    string temp = Console.ReadLine();

                    switch (temp)
                    {
                        case "1":
                        case "2":
                        case "3":
                            input = Int32.Parse(temp);
                            break;
                    }
                }

                Random rm = new Random();
                int computer = rm.Next(1, 4);

                string userText = "";

                switch (input)
                {
                    case 1:
                        userText = "グー";
                        break;
                    case 2:
                        userText = "チョキ";
                        break;
                    case 3:
                        userText = "パー";
                        break;
                }

                string computerText = "";

                switch (computer)
                {
                    case 1:
                        computerText = "グー";
                        break;
                    case 2:
                        computerText = "チョキ";
                        break;
                    case 3:
                        computerText = "パー";
                        break;
                }

                Console.Write("あなた:{0}、コンピューター:{1}・・・・・・・・", userText, computerText);

                if (input == 1)
                {
                    if (computer == 1)
                    {
                        Console.WriteLine("あいこです。\n");
                    }
                    else if (computer == 2)
                    {
                        Console.WriteLine("あなたの勝ちです。\n");
                        kati++;
                        count++;
                    }
                    else if (computer == 3)
                    {
                        Console.WriteLine("コンピューターの勝ちです。\n");
                        make++;
                        count++;
                    }
                }
                else if (input == 2)
                {
                    if (computer == 1)
                    {
                        Console.WriteLine("コンピューターの勝ちです。\n");
                        make++;
                        count++;
                    }
                    else if (computer == 2)
                    {
                        Console.WriteLine("あいこです。\n");
                    }
                    else if (computer == 3)
                    {
                        Console.WriteLine("あなたの勝ちです。\n");
                        kati++;
                        count++;
                    }
                }
                else if (input == 3)
                {
                    if (computer == 1)
                    {
                        Console.WriteLine("あなたの勝ちです。\n");
                        kati++;
                        count++;
                    }
                    else if (computer == 2)
                    {
                        Console.WriteLine("コンピューターの勝ちです。\n");
                        make++;
                        count++;
                    }
                    else if (computer == 3)
                    {
                        Console.WriteLine("あいこです。\n");
                    }
                }
            }

            if (kati > make)
            {
                Console.WriteLine("{0}勝{1}敗で、あなたの勝ちです。", kati, make);
            }
            else
            {
                Console.WriteLine("{1}勝{0}敗で、コンピューターの勝ちです。", kati, make);
            }

#if DEBUG
            Console.ReadLine();
#endif
        }
    }
}

 吐き気をもよおすようなひどいプログラムですが、このプログラムを構造化してみましょう。
 多少は見られるようになります。

using System;

namespace Janken
{
    class Program
    {
        static void Main(string[] args)
        {
            int count = 0;
            int kati = 0;
            int make = 0;

            while (5 > count)
            {
                int input = WaitInput();

                int computer = ComputerInput();

                string userText = GetMoji(input);
                string computerText = GetMoji(computer);

                Console.Write("あなた:{0}、コンピューター:{1}・・・・・・・・", userText, computerText);

                KatiMake katimake = Hantei(input, computer);

                switch (katimake)
                {
                    case KatiMake.Kati:
                        Console.WriteLine("あなたの勝ちです。\n");
                        count++;
                        kati++;
                        break;
                    case KatiMake.Make:
                        Console.WriteLine("コンピューターの勝ちです。\n");
                        count++;
                        make++;
                        break;
                    case KatiMake.Aiko:
                        Console.WriteLine("あいこです。\n");
                        break;
                }
            }

            if (kati > make)
            {
                Console.WriteLine("{0}勝{1}敗で、あなたの勝ちです。", kati, make);
            }
            else
            {
                Console.WriteLine("{1}勝{0}敗で、コンピューターの勝ちです。", kati, make);
            }

#if DEBUG
            Console.ReadLine();
#endif
        }

        private static int WaitInput()
        {
            int result = 0;

            while (result == 0)
            {
                Console.Write("入力してください。(1:グー,2:チョキ,3:パー):");
                string temp = Console.ReadLine();

                switch (temp)
                {
                    case "1":
                    case "2":
                    case "3":
                        result = Int32.Parse(temp);
                        break;
                }
            }
            return result;
        }

        private static int ComputerInput()
        {
            Random rm = new Random();
            int computer = rm.Next(1, 4);
            return computer;
        }

        private static string GetMoji(int input)
        {
            string result = "";

            switch (input)
            {
                case 1:
                    result = "グー";
                    break;
                case 2:
                    result = "チョキ";
                    break;
                case 3:
                    result = "パー";
                    break;
            }

            return result;
        }

        enum KatiMake
        {
            Kati,
            Make,
            Aiko,
        }

        private static KatiMake Hantei(int input, int computer)
        {

            if (input == 1)
            {
                if (computer == 1)
                {
                    return KatiMake.Aiko;
                }
                else if (computer == 2)
                {
                    return KatiMake.Kati;
                }
                else if (computer == 3)
                {
                    return KatiMake.Make;
                }
            }
            else if (input == 2)
            {
                if (computer == 1)
                {
                    return KatiMake.Make;
                }
                else if (computer == 2)
                {
                    return KatiMake.Aiko;
                }
                else if (computer == 3)
                {
                    return KatiMake.Kati;
                }
            }
            else if (input == 3)
            {
                if (computer == 1)
                {
                    return KatiMake.Kati;
                }
                else if (computer == 2)
                {
                    return KatiMake.Make;
                }
                else if (computer == 3)
                {
                    return KatiMake.Aiko;
                }
            }

            throw new ArgumentException();
        }
    }
}

 これで、構造化がされました。
 まぁ、新人なら、このレベルまで出来るようになれば、それなりでしょう。これ以上のことは求めないし、求められないのがほとんどかと思います。
 が、このプログラムはここからさらにもう一歩進化して、オブジェクト指向でデザインされるようになります。汎化、集約、カプセル化なども勉強した新入社員が、それら全てを理解していて、使いこなせるという状態にまでなっていれば、このじゃんけんプログラムは下記のようになるでしょう。
 まぁ、このレベルまでかける新人がいたら、それはそれでいやですけど。

 つか、一流企業になると、このレベルのものが最初からかける新人がいたりするのかね…

 なお、面倒くさいので、記述はC#3.0です。C#2.0でも1でもかけますけど。

//enum.cs
using System;

namespace Janken
{
    //これは別に、intのまま引き回してもいいとは思う
    public enum TeType
    {
        Gu,
        Choki,
        Par,
    }

    public enum KatiMake
    {
        Kati,
        Make,
        Aiko,
    }
}
//Player.cs
using System;

namespace Janken
{
    public abstract class Player
    {
        public TeType Data
        {
            get;
            private set;
        }

        public bool HasData
        {
            get;
            private set;
        }

        public abstract void Tewokimeru();

        protected TeType SetTeType(int source)
        {
            switch (source)
            {
                case 1:
                    this.Data = TeType.Gu;
                    break;
                case 2:
                    this.Data = TeType.Choki;
                    break;
                case 3:
                    this.Data = TeType.Par;
                    break;
                default:
                    throw new ArgumentException();
            }

            this.HasData = true;
            return this.Data;
        }

        public string GetMoji()
        {
            switch (this.Data)
            {
                case TeType.Gu:
                    return "グー";
                case TeType.Choki:
                    return "チョキ";
                case TeType.Par:
                    return "パー";
            }

            throw new ArgumentException();
        }
    }
}
//User.cs
using System;

namespace Janken
{
    public class User : Player
    {
        public override void Tewokimeru()
        {
            while (!this.HasData)
            {
                Console.Write("入力してください。(1:グー,2:チョキ,3:パー):");
                string temp = Console.ReadLine();

                switch (temp)
                {
                    case "1":
                    case "2":
                    case "3":
                        this.SetTeType(Int32.Parse(temp));
                        break;
                }
            }
        }
    }
}
//Computer
using System;

namespace Janken
{
    public class Computer : Player
    {
        public override void Tewokimeru()
        {
            this.SetTeType(new Random().Next(1, 4));
        }
    }
}
//Shinpan.cs
using System;

namespace Janken
{
    public class Shinpan
    {
        private readonly Player _player1;

        public Shinpan(Player player)
        {
            this._player1 = player;
        }

        public KatiMake Hantei(Player player)
        {
            //ここは手抜きのままにしておくけど、匿名メソッド教えているなら、Dictionary使って、匿名メソッドというのも面白い
            switch (this._player1.Data)
            {
                case TeType.Gu:
                    return HanteiGu(player);
                case TeType.Choki:
                    return HanteiChoki(player);
                case TeType.Par:
                    return HanteiPar(player);
            }
            throw new ArgumentException();
        }

        private KatiMake HanteiGu(Player player)
        {
            switch (player.Data)
            {
                case TeType.Gu:
                    return KatiMake.Aiko;
                case TeType.Choki:
                    return KatiMake.Kati;
                case TeType.Par:
                    return KatiMake.Make;
            }
            throw new ArgumentException();
        }

        private KatiMake HanteiChoki(Player player)
        {
            switch (player.Data)
            {
                case TeType.Gu:
                    return KatiMake.Make;
                case TeType.Choki:
                    return KatiMake.Aiko;
                case TeType.Par:
                    return KatiMake.Kati;
            }
            throw new ArgumentException();
        }

        private KatiMake HanteiPar(Player player)
        {
            switch (player.Data)
            {
                case TeType.Gu:
                    return KatiMake.Kati;
                case TeType.Choki:
                    return KatiMake.Make;
                case TeType.Par:
                    return KatiMake.Aiko;
            }
            throw new ArgumentException();
        }
    }
}
//Program.cs
using System;

namespace Janken
{
    class Program
    {
        static void Main(string[] args)
        {
            int count = 0;
            int kati = 0;
            int make = 0;

            while (5 > count)
            {
                User user = new User();
                Computer computer = new Computer();

                //手を決める時のループは、こっちでもいいね
                user.Tewokimeru();
                computer.Tewokimeru();

                Console.Write("あなた:{0}、コンピューター:{1}・・・・・・・・", user.GetMoji(), computer.GetMoji());

                Shinpan hantei = new Shinpan(user);

                switch (hantei.Hantei(computer))
                {
                    case KatiMake.Kati:
                        Console.WriteLine("あなたの勝ちです。\n");
                        count++;
                        kati++;
                        break;
                    case KatiMake.Make:
                        Console.WriteLine("コンピューターの勝ちです。\n");
                        count++;
                        make++;
                        break;
                    case KatiMake.Aiko:
                        Console.WriteLine("あいこです。\n");
                        break;
                }
            }

            if (kati > make)
            {
                Console.WriteLine("{0}勝{1}敗で、あなたの勝ちです。", kati, make);
            }
            else
            {
                Console.WriteLine("{1}勝{0}敗で、コンピューターの勝ちです。", kati, make);
            }

#if DEBUG
            Console.ReadLine();
#endif
        }
    }
}

クラスダイアグラムダイアグラムはこんな感じ。

 簡単なじゃんけんゲームでも、ここまで進化出来ると、それはそれで面白いなぁ。


2008.04.26

ナベアツ

手動トラックバック先URI
http://www.studio-odyssey.net/content/note/archive03.htm#d2608

 FizzBuzzのできない新入社員に脱力した俺ですが、「そんな面白くないものを書かせても、覚えらんないじゃん」と全否定(新入社員にではない)されたので、じゃあどうすんべーと言ったところ、「ナベアツ」とご意見をいただいたので、ナベアツをやることにしてみた。

 まあ、プログラム業界でも、ちょっと話題のナベアツですが(しらんが、FizzBuzzの話を検索するとでることもあるので、それなりに有名なのだろう)、これ、意外とFizzBuzzの次にやるテストとしていいものです。

 問題は、以下。

 1−40の値をコンソールに書き出しなさい。ただし、3の倍数と、3を含む(文字としての)時は、コンソールにバカなことを書き出しなさい。

 これ、すごいよくできた設問です。
 新人研修の講師の人はFizzBuzzのあとに、ぜひ!
 つーか、すげえよ!さすが、世界のナベアツだよ!

 C#での、簡単なコードを書きます。

using System;

namespace Nabeatsu
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 1; i <= 40; i++)
            {
                if ((i % 3) == 0)
                {
                    Console.Write("ヴァー('A`, ");
                }
                else
                {
                    if (i.ToString().Contains("3"))
                    {
                        Console.Write("ヴァー('A`, ");
                    }
                    else
                    {
                        Console.Write("{0}, ", i);
                    }
                }
            }
#if DEBUG
            Console.ReadLine();
#endif
        }
    }
}

 簡単なコードです。それも、すげー。
 なんだけど、たぶん、String.Containは新人にはまだ教えてないでしょう。
 でも大丈夫。きっとここまでに配列は教えていて、そして「StringクラスはChar型の配列である」と教えているはずです。(データ型を教えれば、話として出てくるはず)
 と、言うことは、String.Containと同様のことは配列のプログラムでやっていることの応用で解くことができます。新人は気づくかどうかです。

using System;

namespace Nabeatsu
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 1; i <= 40; i++)
            {
                if ((i % 3) == 0)
                {
                    Console.Write("ヴァー('A`, ");
                }
                else
                {
                    string s = i.ToString();
                    bool isPut = false;

                    for (int j = 0; j < s.Length; j++)
                    {
                        if (s[j] == '3')
                        {
                            Console.Write("ヴァー('A`, ");
                            isPut = true;
                            break;
                        }
                    }

                    if (!isPut)
                    {
                        Console.Write("{0}, ", i);
                    }
                }
            }
#if DEBUG
            Console.ReadLine();
#endif
        }
    }
}

 すげえよ、ナベアツ!
 breakまででてきちゃったよ!
 しかも、配列のなかから要素を探すロジックだよ!このパターンなら、C#の問題としてだけじゃなく、Cでもいけるよ!基本的なロジックで、いろいろテストになるよ!

 さらにこの部分をメソッドとして抜き出すと、

using System;

namespace Nabeatsu
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 1; i <= 40; i++)
            {
                if ((i % 3) == 0)
                {
                    Console.Write("ヴァー('A`, ");
                }
                else
                {
                    if (Contain3(i))
                    {
                        Console.Write("ヴァー('A`, ");
                    }
                    else
                    {
                        Console.Write("{0}, ", i);
                    }
                }
            }
#if DEBUG
            Console.ReadLine();
#endif
        }

        private static bool Contain3(int i)
        {
            string s = i.ToString();

            for (int j = 0; j < s.Length; j++)
            {
                if (s[j] == '3')
                {
                    return true;
                }
            }

            return false;
        }
    }
}

 すげえ!メソッド教えているなら、「3が含まれているかはメソッドとして実装すること」という制限をつけると、メソッドの理解まではかれちゃう!

 ナベアツすげえ!
 さすが、世界のナベアツ!

 来年には使えないかもしれないが、ぜひとも、その名をコンピューターサイエンスの世界に轟かせてほしいな!

 なお、面白いことを書くとこでビープを鳴らしたりすると、完成した人が羨望と失笑を得ることができるのでおすすめです。
 ビープが聞こえないと、軽く講師がへこめるので、なおよいです。

 なお、Nabeatsuの完全なコードは最後に「オモロー」を書き出さなければなりません。
 Cで書く、メソッド版の完全なコードは、以下です。

#include<stdio.h>

int Contain3(int i)
{
    char p[2];
    sprintf(p, "%d", i);

    for (int j = 0; j < sizeof(p); j++)
    {
        if (p[j] == '3')
        {
            return 1;
        }
    }
    return 0;
}

void main()
{
    for (int i = 1; i <= 40; i++)
    {
        if ((i % 3) == 0)
        {
            printf("ヴァー('A`, ");
        }
        else
        {
            if (Contain3(i))
            {
                printf("ヴァー('A`, ");
            }
            else
            {
                printf("%d, ", i);
            }
        }
    }
    printf("\r\nオモロー!(・∀・)\r\n");
}

 オモロー。

 ナベアツ、すげえよ、世界のナベアツだよ。


2008.03.04

ブラックホールSMTPサーバ

手動トラックバック先URI
http://www.studio-odyssey.net/content/note/archive03.htm#c0408

 SMTPサーバを会社が公開してくれないので、自前でSMTPサーバのコードを書いた。
 土日で。

 とはいえ、そんなにたいそうなものは書く暇もないし、DNS機能をつけるのも面倒なので、すげく簡単。
 やりたい事は単純で、障害通知とかのメールを受け取りたいだけ。
 障害通知とか、TeamFoundationServerのチェックインメールとか、製品に機能としてはあるんだけど、会社がSMTPにつなげさせてくれないから、使えないんだよね。つーか、うちの会社、従業員結構いるんだから、Exchangeとか入れればいいのに、なんで内製してんだよ。個人的には、Messengerが使いたいから、LiveCommunicationServerを入れて欲しいんだが…つかね、事業所が3個も4個もあるんだから、それくらいしたっていいじゃない。(そもそも、ADで社員管理してないんだけどさ)

 前置きはともかく、コードをここに書くには膨大なので、リンクするです。
 http://www.studio-odyssey.net/content/img/ReceiveOnlySMTP.zipにあります。開発環境は、VisualStudio2008。C#です。

 コア部分しか作ってないので、このままだと使えません。
 コンソールでもいいし、Windowsサービスにするでもいいですけど、受け取ったメールの内容をそのまま返すだけなので、base64をエンコーディングするとか、どっかに溜めるとかは自前で実装してください。テスト目的で利用するには、これだけで十分かも知れませんが。

 テスト用についている.netのSystem.Net.Mail.SmtpClientを使っているWindowsFormのやつは、ボタンを押してメールを送った後、Formを閉じないと、QUITがサーバに送られません。どうも、GCされる時まで、QUITが送られないみたいなんだよね。.netの仕様みたいだけど。
 詳細は以下。

http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=146711
SmtpClient does not gracefully close the underlying TCP/IP connection

 これを知らずにいた俺は、自分のコードが間違っているのかと思って、月曜日半日、無駄にテストしていた。
 おのれ…

 あと、Windows Vistaのtelnetって、プログラムの追加で、Windows機能の追加をしないと使えないのね。
 telnetなんて使わないだろうと思って、入れなかったら、Vistaになくて驚いたのは内緒だ。