2012年09月15日

DynamicJsonを利用してJSONを扱う(C#)

DynamicJsonを利用して、C#でJSONを扱うための方法。
C#でJSONを扱うためにはDynamicJsonが便利です。ただし、.NET Framework 4.0以上でないと動作しません。

DynamicJsonのダウンロード

下記サイトからダウンロードします。執筆時点のバージョンは「Release 1.2.0.0」
DynamicJson - CodePlex
「download」からファイルをダウンロードします。
ダウンロードしたファイル「DynamicJson_1.2.0.0.zip」を展開し、適当なフォルダにコピーしておきます。

プロジェクトから参照設定

DynamicJsonを使用するプロジェクトを作成します。
Visual C# 2010 Express で新規プロジェクトを作成。プロジェクトの種類は何でも良いですが、Windows Formアプリケーションにしました。
「プロジェクト(P)」−「参照の追加(R)...」で「DynamicJson.dll」を参照設定します。

利用方法の例

TwitterのユーザータイムラインをJSON形式で取得して、ツイートの内容を表示するプログラムです。
ButtonとTextBox(Multiline=True)を配置してください。
プログラムの先頭に using Codeplex.Data; を定義する必要があります。

private void button1_Click(object sender, EventArgs e)
{
    string user_id = "twj";  // ※twjは日本語版Twitter公式アカウント

    WebClient wc = new WebClient();
    string str = wc.DownloadString(
        "http://twitter.com/statuses/user_timeline/" + user_id + ".json");
        
    var timeline = DynamicJson.Parse(str);
    foreach (var tweet in timeline)
    {
        textBox1.Text +=
            tweet.user.name + " @" + tweet.user.screen_name + " : " +
            tweet.text + Environment.NewLine;
    }
}

なお実行するためには、DynamicJson.dllを実行ファイルと同じディレクトリに置く必要があります。

ライセンスについて

DynamicJsonは「Ms-PL」というライセンスで提供されています。
大雑把に言えば『商用でも非商用でも無料で自由に改変して使用可能。ただし、著作権などの情報をコード内に示す必要がある』というものです。詳しくは解説しているサイトがあるのでそちらを参照してください。

マイクロソフトの主要なシェアードソースライセンスに関して

良かったらクリックしてください
にほんブログ村 IT技術ブログ プログラム・プログラマーへ  人気ブログランキングへ

タグ:C# JSON DynamicJson
posted by among at 09:52 | Comment(3) | TrackBack(0) | C#

2012年07月29日

ポーカーの役判定プログラム(C#)

ポーカーの役を判定するプログラムをC#で作りました。
私は昔、麻雀の役を判定するアルゴリズムを見たことがあります。ポーカーの役判定も洗練されたアルゴリズムがあると思うのですが、あまりまとまっている情報がありませんでした。
いくつかのサイトで収集した情報を元にC#のプログラムに起こしたので公開します。
ただし、バグはあるかもしれません。

ポーカーのルールについてはポーカー - Wikipediaを参照してください。

// カードの意味
// 1バイトの上位4ビット(0xF0)をスート、下位4ビット(0x0F)をランク、として扱う
// 
//          A 2 3 4 5 6 7 8 9 10 J Q K
// ♠(スペード): 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D
// ♥(ハート) : 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D
// ♦(ダイア) : 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D
// ♣(クラブ) : 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D
//

/// <summary>
/// 手役
/// </summary>
public enum PokerHand
{
    RoyalStraightFlush,
    StraightFlush,
    FourOfAKind,
    FullHouse,
    Flush,
    Straight,
    ThreeOfAKind,
    TwoPair,
    OnePair,
    NoPair,
}

/// <summary>
/// 手役の判断
/// </summary>
/// <param name="cards">手札</param>
/// <returns>手役</returns>
public PokerHand Judge(List<int> cards)
{
    // カードが2枚以上ない場合、判定なしで不成立
    if (cards.Count() < 2) return PokerHand.NoPair;

    // @ペア系の判定
    // 各カードとペアになるカードの枚数をカウントする。
    // 枚数に応じてペア系の役が確定する。
    int count = 0;
    for (int i = 0; i < cards.Count() - 1; i++)
    {
        for (int j = i + 1; j < cards.Count(); j++)
        {
            if ((cards[i] & 0xF) == (cards[j] & 0xF))
            {
                count++;
            }
        }
    }
    // 成立した役を返す
    switch (count)
    {
        case 1: return PokerHand.OnePair;
        case 2: return PokerHand.TwoPair;
        case 3: return PokerHand.ThreeOfAKind;
        case 4: return PokerHand.FullHouse;
        case 6: return PokerHand.FourOfAKind;
    }

    // ストレート系・フラッシュ系はカード5枚でないと不成立
    if (cards.Count() != 5) return PokerHand.NoPair;

    // Aストレート系の判定
    bool straight = false;
    bool royal = false;

    // 5つの数字の差が4以内であればストレート成立
    {
        int max = cards.Max(card => card & 0x0F);
        int min = cards.Min(card => card & 0x0F);
        if (max - min <= 4)
        {
            // ストレート成立
            straight = true;
        }
    }
    // ストレート不成立の場合はロイヤルストレートの判断を行う。
    // エース(A)を14として判定し直す
    if (straight == false)
    {
        int max = cards.Max(card => ((card & 0x0F) == 1) ? 14 : (card & 0x0F));
        int min = cards.Min(card => ((card & 0x0F) == 1) ? 14 : (card & 0x0F));
        if (max - min <= 4)
        {
            // ロイヤルストレート成立
            straight = true;
            royal = true;
        }
    }

    // Bフラッシュ系の判定
    bool flush = true;

    // 5つのスートが全て同じならフラッシュ成立
    for (int i = 1; i < cards.Count(); i++)
    {
        if ((cards[0] & 0xF0) != (cards[i] & 0xF0))
        {
            flush = false;
            break;
        }
    }

    // 成立した役を返す
    if (royal && flush) return PokerHand.RoyalStraightFlush;
    if (straight && flush) return PokerHand.StraightFlush;
    if (flush) return PokerHand.Flush;
    if (straight) return PokerHand.Straight;

    // 何も成立しなかった
    return PokerHand.NoPair;
}

動作検証のため、FormにTextBoxとRichTextBoxを配置して、検証用プログラムを書きます。検証用なのでかなりの手抜きです。
TextBoxに、手札の数値をスペースで区切って16進数で入力すると、その結果をRichTextBoxに出力します。


private void textBox1_TextChanged(object sender, EventArgs e)
{
    // テキストボックスに入力した数値をリストに格納する。
    // 文字列→数値の変換は結構手抜き
    List<int> cards = new List<int>();
    foreach (string text in this.textBox1.Text.Split(' '))
    {
        try { cards.Add(int.Parse(text, System.Globalization.NumberStyles.AllowHexSpecifier)); }
        catch { }
    }

    // 役の判定
    PokerHand hand = Judge(cards);

    // 結果出力
    StringBuilder sb = new StringBuilder();
    string[] suit = new string[] { "♠", "♥", "♦", "♣" };
    string[] rank = new string[] { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" };

    foreach (int c in cards)
    {
        try
        {
            sb.Append(
                suit[((c & 0xF0) >> 4) - 1] +
                rank[(c & 0x0F) - 1]);
        }
        catch { }
    }

    sb.Append(
        System.Environment.NewLine +
        hand.ToString());

    richTextBox1.Text = sb.ToString();
}

実行結果は次のようになります。
手札の順番はバラバラでも問題ありません。
PokerHand-Sample.PNG

良かったらクリックしてください
にほんブログ村 IT技術ブログ プログラム・プログラマーへ  人気ブログランキングへ

posted by among at 20:30 | Comment(2) | TrackBack(0) | ゲーム制作

2012年07月28日

Trident/WebKit/Geckoで簡素なWebブラウザを作る(C#)

前回までの記事でTrident(IEコンポーネントのWebBrowserコントロール)、WebKit(WebKit.NET)、Gecko(GeckoFX)でブラウザを作って見たので、この三つの並べて表示の比較が行える“マルチレンダリングブラウザ”を作ってみます。

基本的には前回までの記事で作成したプログラムをちょっと改造するような感じになります。

  • WebBrowser、WebKitBrowser、GeckoWebBrowserコントロールを並べて配置する。今回はフォームのリサイズに対応するため、TableLayoutPanelと組み合わせて自動調整させる
  • 「進む」「戻る」などのボタン押下時のアクションは、3つのコントロールを同時に動かす
  • リンクをクリックしてページを移動する場合、他のコントロールも移動させる
  • ページのタイトルは代表としてWebBrowserのものを使う。他のコントロールではイベント処理しない

実際に作ってみたのはこんな感じです。

public Form1()
{
    InitializeComponent();

    this.comboBox1.Text = "http://www.google.com";
    this.button1.Text = "戻る";
    this.button2.Text = "進む";
    this.button3.Text = "更新";
    this.button4.Text = "ホーム";
    this.button5.Text = "移動";
}

/// <summary>
/// URL入力エリア:選択変更
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    try
    {
        // 移動
        webBrowser1.Navigate(comboBox1.Text);
        webKitBrowser1.Navigate(comboBox1.Text);
        geckoWebBrowser1.Navigate(comboBox1.Text);
    }
    catch (Exception exc)
    {
        MessageBox.Show(exc.Message, exc.Source);
    }
}

/// <summary>
/// URL入力エリア:キー入力
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void comboBox1_KeyDown(object sender, KeyEventArgs e)
{
    // エンターキー
    if (e.KeyCode == Keys.Enter)
    {
        // 移動ボタン入力と同じ
        button5.PerformClick();
    } 
}

/// <summary>
/// 戻るボタン
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
    // 戻る
    this.webBrowser1.GoBack();
    this.webKitBrowser1.GoBack();
    this.geckoWebBrowser1.GoBack();
}

/// <summary>
/// 進むボタン
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
    // 進む
    this.webBrowser1.GoForward();
    this.webKitBrowser1.GoForward();
    this.geckoWebBrowser1.GoForward();
}

/// <summary>
/// 更新ボタン
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button3_Click(object sender, EventArgs e)
{
    // 更新(再読み込み)
    this.webBrowser1.Refresh();
    this.webKitBrowser1.Reload();
    this.geckoWebBrowser1.Reload();
}

/// <summary>
/// ホームボタン
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button4_Click(object sender, EventArgs e)
{
    // ホームへ移動
    this.webBrowser1.GoHome();
}

/// <summary>
/// 移動ボタン
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button5_Click(object sender, EventArgs e)
{
    // 入力したURLに移動する
    try
    {
        string url = this.comboBox1.Text;
        // "http://"が無ければ補完
        if (this.comboBox1.Text.StartsWith("http://") == false)
        {
            url = "http://" + url;
        }
        // 移動
        this.webBrowser1.Navigate(url);
        this.webKitBrowser1.Navigate(url);
        this.geckoWebBrowser1.Navigate(url);
        this.comboBox1.Items.Add(url);
    }
    catch(Exception exc)
    {
        MessageBox.Show(exc.Message, exc.Source);
    }
}

/// <summary>
/// Webブラウザ:ナビゲーション実行時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
    if (this.webBrowser1.Url == null) return;

    if (this.comboBox1.Text != this.webBrowser1.Url.ToString())
    {
        try
        {
            // URL入力エリアにナビゲートしたサイトのURLを設定
            this.comboBox1.Text = webBrowser1.Url.ToString();

            // 他のWebBrowserを連動させる
            this.webKitBrowser1.Navigate(this.comboBox1.Text);
            this.geckoWebBrowser1.Navigate(this.comboBox1.Text);
        }
        catch (Exception exc)
        {
            MessageBox.Show(exc.Message, exc.Source);
        }
    }

    // フォームのタイトルをナビゲートしたサイトのタイトルを設定
    this.Text = webBrowser1.Document.Title.ToString();
}

/// <summary>
/// WebKitブラウザ:ナビゲーション実行時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void webKitBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
    if (this.webKitBrowser1.Url == null) return;

    if (this.comboBox1.Text != this.webKitBrowser1.Url.ToString())
    {
        try
        {
            // URL入力エリアにナビゲートしたサイトのURLを設定
            this.comboBox1.Text = this.webKitBrowser1.Url.ToString();

            // 他のWebBrowserを連動させる
            this.webBrowser1.Navigate(this.comboBox1.Text);
            this.geckoWebBrowser1.Navigate(this.comboBox1.Text);
        }
        catch(Exception exc)
        {
            MessageBox.Show(exc.Message, exc.Source);
        }
    }
}

/// <summary>
/// GeckoWebKitブラウザ:ナビゲーション実行時
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void geckoWebBrowser1_Navigated(object sender, Skybound.Gecko.GeckoNavigatedEventArgs e)
{
    if (this.geckoWebBrowser1.Url == null) return;

    if (this.comboBox1.Text != this.geckoWebBrowser1.Url.ToString())
    {
        try
        {
            // URL入力エリアにナビゲートしたサイトのURLを設定
            this.comboBox1.Text = this.geckoWebBrowser1.Url.ToString();

            // 他のWebBrowserを連動させる
            this.webBrowser1.Navigate(this.comboBox1.Text);
            this.webKitBrowser1.Navigate(this.comboBox1.Text);
        }
        catch(Exception exc)
        {
            MessageBox.Show(exc.Message, exc.Source);
        }
    }
}

/// <summary>
/// Webブラウザ:新しいウィンドウが開かれたとき
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void webBrowser1_NewWindow(object sender, CancelEventArgs e)
{
    // キャンセル
    e.Cancel = true;

    // ここではキャンセルしているので新しいウィンドウは開かないし画面遷移もしない
    // 新しいウィンドウを開かずに内部のブラウザで開くためにはwebBrowserの改造が必要。
    // 今回は省略
    // [参照]
    // http://agrimo.jp/wp/?page_id=1059
    // http://vsug.jp/tabid/63/forumid/45/threadid/9273/scope/posts/Default.aspx
}

実行結果はこんな感じです。
MultiBrowser-Sample-01.png
MultiBrowser-Sample-02.png

とりあえずは簡素なブラウザと言うことで妥協していますが、ちゃんとしたブラウザとして使用するためにはまだまだ課題が残っています。

  • 『ホームへ移動』ボタンのWebKit,Geckoへの対応
  • 『新しいウィンドウで開く』への対応
  • Googleの検索窓のような動的に変化するページの対応
  • 読み込み中を表すプログレスバーやメッセージの表示
  • ときどきエラーが出る(特定のサイトをGeckoで見ると死ぬ?)
  • スクロールバーの連動
  • 実行ファイル配布の方法(WebKitやXULRunnerのDLL群を一緒に配布しなければいけない)
などなど……。
気が向いたら改造するかもしれませんが、まあこのまま放置かな……?

いつも思うんですが、C#とか.NETでは、何かやろうとしたときに何でも簡単にできるくせに、本気で何かやろうとしたときは凄く大変。今回のも“簡易ブラウザ”とか言ってますが、このレベルならちょっとコンポーネントをインストールしてちょろちょろっとコードを書けば、誰でも作れちゃうけど、そこから先、本格的なブラウザに仕立て上げるには、色々調べなきゃいけないし、まぁ大変です。
自分でちょっと使うツールとか作るには良いんですけどね……。

良かったらクリックしてください
にほんブログ村 IT技術ブログ プログラム・プログラマーへ  人気ブログランキングへ

posted by among at 19:04 | Comment(5) | TrackBack(0) | C#