LB tag をプログラム

SGF Version 4 に対応して LB tag をプログラムします。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. LB tag をプログラムする前に「文字がクリックされたときに分岐先を検索してジャンプするメソッド」の修正から始めます。
    Version 3 ではクリックされた文字をキーにして検索していましたが、今回はクリックされた座標をキーにして検索します。
    こうすると画面に表示される文字に関係なく棋譜をたどることが出来ます。
    つまり LB tag に設定される文字は何でも良い訳で、極端な例では全てが 'A' でも構わないことになります。
  2. CELL Class に cdr をたどって検索するメソッドを追加します。
    検索キーは「Level と座標」です。
        // lev, ps で CELL を検索
        static public CELL Scan(CELL pt, int lev, Point ps)
        {   CELL wp;
            if (pt==null)   return null;
            if (pt.level==lev && pt.pos.X==ps.X && pt.pos.Y==ps.Y)  return pt;
            wp = Scan(pt.cdr, lev, ps);
            if (wp != null) return wp;
            return null;
        }
    
  3. クリック座標で検索する Set_Next() メソッドです。
    選択手順が設定されている局面では pos の座標で検索します。
    選択手順が無いときや、手順Aを選ぶときは盤上を適当にクリックします。
    この修正を行っても Version 3 の棋譜が今まで通り動作することを確認して下さい。
        private void Set_Next(Point pos)
        {
            CELL    wp;
            int     lv;
    
            if (m_PT == null)   return;
            if (m_PT.car == null && m_PT.cdr != null)   // 選択手順
            {
                lv = m_PT.level+1;
                wp = null;
                if (pos.X>=0 && m_Ban[pos.X, pos.Y] > 1)
                {   wp = CELL.Scan(m_PT.cdr, lv, pos);  }
                if (wp == null)
                {
                    //MessageBox.Show("分岐が見つかりません?: " + ch);
                    if (m_PT.cdr.level==lv) wp = m_PT.cdr;
                }
                m_PT.next = wp;
                if (wp != null)
                {
                    wp.back = m_PT;
                    m_PT = wp;
                }
            }
            else    // 基本手順
            {
                if (m_PT.car != null)
                {
                    wp = m_PT.car;
                    m_PT.next = wp;
                    wp.back = m_PT;
                    m_PT = wp;
                }
            }
            Play(m_Top);    // 出題図から現在までの手順を再現
        }
    
  4. LB tag を記録する LB Class を定義して、CELL Class に ArrayList lb を追加します。
    LB tag では、小文字だけでなく大文字も使われるようなので、alph に小文字と大文字を定義して下さい。
    CELL Class の Constructor で ArrayList lb を初期化します。
    lb を string 形式に編集する lb_edt() メソッドを追加します。
    using System.Collections;   // ArrayList
          
    // LB 構造体
    class LB
    {
        public  Point   pos;    //X座標, Y座標
        public  int     ch;     //文字Index
    
        // Constructor
        public  LB(Point p, int c)
        {   pos = p;
            ch = c;
        }    
    }
    
    // SGF 形式のセル構造体
    class CELL
    {
          ・・・
        public  ArrayList  lb;      //分岐表示  
        static  string  alph = "abcdefghijklmnopqrsABCDEFGHIJKLMNOPQRS";
    
    
        // Constructor
        public  CELL(int n)
        {     ・・・
            lb = new ArrayList();
        }
    
        static public string lb_edt(CELL pt)
        {   string  str;
            LB      wlb;
            int     i;
            if (pt.lb.Count==0) return string.Empty;
            str = "LB";
            for(i=0; i<pt.lb.Count; i++)
            {
                wlb = (LB)pt.lb[i];
                str = str + "[" + alph[wlb.pos.X] + alph[wlb.pos.Y] +
                      ":" + alph[wlb.ch] + "]";
            }
            str = str + "]";
            return str;
        }
    
  5. 棋譜プロパティのチェックに LB tag を追加します。
        private void Prpperty(CELL pt)
        {
             ・・・
    
            if (m_WS=="LB")
            {
                pt.lb = LB_set();
            }
            break;
    
  6. 出題図にも LB tag が使われているようなので、プロパティのチェックに LB tag を追加します。
    出題図の LB tag は、一時的に ArrayList m_lb を定義して格納します。
        ArrayList   m_lb = new ArrayList();     // 出題図の分岐情報  
    
        // 出題図プロパティのチェック
        private void KeyCheck()
        {
             ・・・
    
            if (m_WS=="LB")
            {
                m_lb = LB_set();
                return;
            }
    
  7. LB tag を処理する LB_set() メソッドです。
    m_SGF[m_Idx] の位置から LB tag を抽出して ArrayList を生成してリターンします。
        private ArrayList LB_set()
        {   ArrayList   wlb;
            LB          lb;
            int         ch;
    
            wlb = new ArrayList();
            while(true)
            {   Token();
                m_Pos.X = alph.IndexOf(m_WS[0]);
                if (m_Pos.X<0)  m_Pos.X = 0;
                m_Pos.Y = alph.IndexOf(m_WS[1]);
                if (m_Pos.Y<0)  m_Pos.Y = 0;
                ch = 0;
                if (m_WS.Length>3)  ch = alph.IndexOf(m_WS[3]);
                if (ch<0)   ch = 0;
                lb = new LB(m_Pos, ch);
                wlb.Add(lb);
                if (m_SGF[m_Idx]!='[')
                {   if (m_SGF[m_Idx]==']')  m_Idx++;
                    break;
                }
            }
            return wlb;
        }
    
  8. 出題図に格納されていた id, msg, lb は CELL ツリーが完成してから m_Top に設定します。
        // SGF ファイルを入力して表示(m_St 出題図)
        private void SGFRead(string file)
        {
            LoadSGF(file);
            this.Text = file;
            if (m_Top != null)
            {
                m_Top.id = m_id;
                m_Top.msg = m_msg;
                m_Top.lb = m_lb;
            }
        }
    
  9. LB tag を調べて分岐文字を m_Ban に設定する Bunki() メソッドです。
    Version 3 にも対応するように LB tag が設定されていないときは cdr をたどって調べます。
    m_Ban に設定する数値は、次のように決めています。
        // m_Ban[] に分岐文字を設定
        private void Bunki()
        {
            int     lv, i;
            LB      wk;
    
            if (m_PT==null) return;
            if (m_PT.lb.Count>0)
            {
                for(i=0; i<m_PT.lb.Count; i++)
                {   wk = (LB)m_PT.lb[i];
                    m_Ban[wk.pos.X, wk.pos.Y] = (short)(wk.ch+2);
                }
                return;
            }
            if (m_PT.cdr==null) return;
            if (m_PT.cdr.level==m_PT.level)     return;
            lv = m_PT.level+1;
            CELL wp = m_PT.cdr;
            for(i=2; wp!=null; wp=wp.cdr)
            {
                if (wp.level == lv)
                {
                    m_Ban[wp.pos.X, wp.pos.Y] = (short)(i);
                    i++;
                }
            }
        }
    
  10. LB tag(分岐文字)の設定はメニューから起動します。
    本来分岐手順は CELL リストの cdr をたどることで解ります。
    LB tag はこれをタグとして記述するものです。
    分岐の実行は座標で検索するので、LB tag の文字は何でも良く Text Editor などで自由に設定し直すことが出来ます。
    ツールメニューに LB tag(&L) を追加して LB_tag メソッドを呼び出して下さい。
        item = menu.MenuItems.Add("ツール(&T)");
        item.MenuItems.Add(new MenuItem("LB tag(&L)...", new EventHandler(this.LB_tag)));
    
        // LB tag を生成
        private void LB_tag(object sender, EventArgs e)
        {
            LBCheck(m_Top);
        }
    
  11. CELL を再起でたどりながら LB tag を設定する LBCheck(CELL pt) メソッドです。
        // 分岐を検索して LB[] を設定
        private void LBCheck(CELL pt)
        {
            int     lv, i;
            LB      lb;
            CELL    wp;
            if (pt==null)   return;
            if (pt.lb.Count>0)  pt.lb.Clear();
            if (pt.cdr!=null && pt.cdr.level>pt.level)
            {
                lv = pt.level+1;
                wp = pt.cdr;
                for (i = 0; wp != null; wp = wp.cdr)
                {
                    if (wp.level == lv)
                    {
                        lb = new LB(wp.pos, i);
                        pt.lb.Add(lb);
                        i++;
                    }
                }
            }
            LBCheck(pt.car);
            LBCheck(pt.cdr);
        }
    
  12. 出題図を書き出す SaveBan() メソッドに LB tag を追加します。
        // 出題図をファイルに書き出す(m_St 出題図)
        private void SaveBan()
        {   string  str;
              ・・・
            if (m_Top.lb.Count>0)       // LB tag を書き出す
            {   writer.Write(CELL.lb_edt(m_Top));  }
    
  13. 棋譜を書き出す SaveTree() メソッドに LB tag を追加します。
        // 棋譜をファイルに書き出す
        private void SaveTree()
        {   CELL    wp;
              ・・・
            if (wp.lb.Count>0)          // LB tag を書き出す
            {   writer.Write(CELL.lb_edt(wp));  }
    

[Next Chapter ↓] SGF Header
[Previous Chapter ↑] Version 4

超初心者のプログラム入門(C# Frame Work)