棋譜の保存

ツリー構造に展開した棋譜を SGF ファイルに保存します。

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

SGF ファイルの説明

  1. 今回はツリー構造に展開した棋譜を SGF ファイルに保存します。
    SGF ファイルに保存する SGFSave() 関数です。
    SGF ファイルでは、括弧でくくられた手順 ( ~ ) が再起構造になっていて、 棋譜を記録するツリーも再起構造になっています。
    既に同名のファイルが存在する場合、上書きの確認を行います。
    head が SGF ファイルのヘッダ部で、最小限必要な項目だけ書き出します。
    SaveBan(); が出題図(初期画面)を書き出す関数です。
    SaveTree(); が棋譜を保存する今回の主役の関数です。
        string      head = "(\r\n;FF[3]GM[1]AP[maeda-igo Ver-1]SZ[19]";
    
        private void SGFSave(string file)
        {   DialogResult rc;
    
            if (File.Exists(file))
            {   rc = MessageBox.Show("上書きで保存しますか", "選択",
                     MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if (rc == DialogResult.No)  return;
            }
            writer = new StreamWriter(file,false, Encoding.GetEncoding("Shift_JIS"));
            writer.WriteLine(head);
            SaveBan();
            if (m_Top != null)  SaveTree();
            writer.WriteLine(")");
            writer.Close();
        }
    
  2. 出題図(初期画面)を書き出す SaveBan() 関数です。
    SaveBan は m_St[19,19] に格納されている黒石と白石を順に書き出すだけなので簡単です。
    出題図の説明は SGF ファイルを入力 を参照して下さい。
        short[,]    m_St = new short[19, 19];   // 出題図
        string      alph = "abcdefghijklmnopqrs";
    
        private void SaveBan()
        {   string  str;
            int     i,j;
    
            writer.Write("AB");
            for(i=0; i<19; i++)
                for(j=0; j<19; j++)
                    if (m_St[i,j]==1)
                    {   str = "[" + alph[i] + alph[j] + "]";
                        writer.Write(str);
                    } 
            writer.Write("AW");
            for(i=0; i<19; i++)
                for(j=0; j<19; j++)
                    if (m_St[i,j]==-1)
                    {   str = "[" + alph[i] + alph[j] + "]";
                        writer.Write(str);
                    } 
            if (m_Top.id!=string.Empty)
            {   str = "N[" + m_Top.id + "]";
                writer.Write(str);
            }
            if (m_Top.msg!=string.Empty)
            {
                str = "C[" + m_Top.msg + "]";
                writer.Write(str);
            }
            writer.Write("\r\n");
        }
    
  3. 棋譜を保存する SaveTree() 関数です。
    最初に保存するセル(手順)を SetLink(m_Top); で SELL 構造体の next を使ってリンクしています。
    通常は入力したときの SGF ファイルの記述順に従って連鎖されます。
    for(wp=m_Top; wp!=null; wp=wp.next) { ... } で連鎖をたどってセルをファイルに書き出します。
    通常のセル(一手分)の書き出しは簡単ですが、レベルアップとダウンのときの括弧の書き出しがみそです。
    '(' でレベルをアップし、')' でレベルをダウンします。
    私も何種類かの棋譜を使って試行錯誤しました。
        private void SaveTree()
        {   CELL    wp;
            string  str;
            int     n, i;
    
            // 保存セルのリンクを設定
            m_Top.next = null;
            m_PT = m_Top;
            SetLink(m_Top);
            for(wp=m_Top; wp!=null; wp=wp.next)
            {
                // セル(一手分)を編集して書き出す
                str = string.Empty;
                if (wp.teban!=0)
                {   if (wp.teban == 1)
                    {   str = ";B[" + alph[wp.pos.X] + alph[wp.pos.Y] + "]";
                        writer.Write(str);
                    }
                    if (wp.teban == -1)
                    {
                        str = ";W[" + alph[wp.pos.X] + alph[wp.pos.Y] + "]";
                        writer.Write(str);
                    }
                    if (wp.id!=string.Empty)
                    {   str = "N[" + wp.id + "]";
                        writer.Write(str);
                    }
                    if (wp.msg != string.Empty)
                    {
                        str = "C[" + wp.msg + "]";
                        writer.Write(str);
                    }
                }
                // レベルアップ'(' ダウン')'を書き出す
                if (wp.next == null)
                {   for (i = wp.level + 1; i > 0; i--)  writer.Write(")");
                    break;
                }
                n = wp.next.level - wp.level;
                if (wp.car == null && wp.cdr == null)
                {   for (i = (0-n)+1; i > 0; i--)   writer.Write(")");
                    writer.Write("\r\n(");
                    continue;
                }
                if (wp.car == null)
                {   if (n == 0) writer.Write(")");
                    writer.Write("\r\n(");
                }
            }
            writer.Write("\r\n");
        }
    
  4. 保存するセル(手順)を SELL 構造体の next を使ってリンクする SetLink() です。
    最初に呼ばれるときは、m_PT, pt 共に m_Top が格納されています。
    この関数は pt.car, pt.cdr で再起呼び出しされていることに注目して下さい。
        private void SetLink(CELL pt)
        {
            if (pt==null)   return;
            if (pt.teban!=0)        // teban==0 のセルは保存する必要が無い
            {   m_PT.next = pt;     // m_PT の後部に連鎖
                m_PT = pt;
                m_PT.next = null;
            }   
            SetLink(pt.car);
            SetLink(pt.cdr);
        }
    

[Next Chapter ↓] 棋譜の入力
[Previous Chapter ↑] 棋譜の再生

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