# -*- encoding: utf-8n -*-
# Copyright (c) 2010, Takaya Kakizaki(kacky)
# All rights reserved.
#
#  ソースコード形式かバイナリ形式か、変更するかしないかを問わず、以下の条件を満たす場合に限り、再頒布および使用が許可されます。 
#
#  ・ソースコードを再頒布する場合、上記の著作権表示、本条件一覧、および下記免責条項を含めること。 
#
#  ・バイナリ形式で再頒布する場合、頒布物に付属のドキュメント等の資料に、上記の著作権表示、本条件一覧、および下記免責条項を含めること。 
#
#  ・書面による特別の許可なしに、本ソフトウェアから派生した製品の宣伝または販売促進に、オープン麻雀の名前またはコントリビューターの名前を使用してはならない。
#
#
#  本ソフトウェアは、著作権者およびコントリビューターによって「現状のまま」提供されており、明示黙示を問わず、
#  商業的な使用可能性、および特定の目的に対する適合性に関する暗黙の保証も含め、またそれに限定されない、いかなる保証もありません。
#  著作権者もコントリビューターも、事由のいかんを問わず、 損害発生の原因いかんを問わず、かつ責任の根拠が契約であるか厳格責任であるか
#  （過失その他の）不法行為であるかを問わず、仮にそのような損害が発生する可能性を知らされていたとしても、本ソフトウェアの使用によって発生した
#  （代替品または代用サービスの調達、使用の喪失、データの喪失、利益の喪失、業務の中断も含め、またそれに限定されない）
#  直接損害、間接損害、偶発的な損害、特別損害、懲罰的損害、または結果損害について、一切責任を負わないものとします。 
tim = Time.now.sec * 1000 + Time.now.usec / 1000;
if $localserver == 1
  require "comnokogiri"
else
  require "comrexml"
end

#require "comlibxml"
tim = Time.now.sec * 1000 + Time.now.usec / 1000 - tim
$performdump.puts("901 : require xml #{tim} ms")  if $performdump
tim = Time.now.sec * 1000 + Time.now.usec / 1000;
require "kconv"
tim = Time.now.sec * 1000 + Time.now.usec / 1000 - tim
$performdump.puts("902 : require kconv #{tim} ms")  if $performdump

require "mahjongconst"


# セッション管理(暫定版)
# 文字列からXML構文解析を行う(レスポンスを文字列で返す)
def sendRequest(req)
  tim = Time.now.sec * 1000 + Time.now.usec / 1000;
  
  doc = CommonXML::parseXML(req)
  
  tim = Time.now.sec * 1000 + Time.now.usec / 1000 - tim
  
  $performdump.puts("001 : parseXML #{tim} ms")  if $performdump
  
  ele = doc.getRoot
  
  sessionid = (ele.get_attribute("session").to_s).to_i
  state = nil
  file = nil
  responce = []
  
  if sessionid >= 0
    if File.exist?(sessionid.to_s)
      # ファイルからステータスを読み込む
      file = File.open(sessionid.to_s,File::RDWR)
      time = 0
      while !(file.flock(File::LOCK_EX|File::LOCK_NB))
        time += 1
        if time > RETRY
          break
        end
        sleep(1)
      end
      
      if time > RETRY
        # ビジー状態
        element = CommonXML::Element.create(TAG_STATE)
        code = element.add_element(TAG_CODE)
        code.add_text("#{CODE_BUSY}")
        nm = element.add_element(TAG_NAME)
        nm.add_text("BUSY")
        responce.push(element)
      else
        tim = Time.now.sec * 1000 + Time.now.usec / 1000;
        state = Marshal.load(file)
        tim = Time.now.sec * 1000 + Time.now.usec / 1000 - tim
        
        $performdump.puts("002 : Status Load #{tim} ms")  if $performdump
        file.seek(0,File::SEEK_SET)
      end
      
    end
    
  else
    state = State.new
  end
  
  if state
    if state.session != -1
      if File.exist?("#{state.session.to_s}_#{state.date.strftime("%Y%m%d%H%M")}_log.xml")
        $logfile = File.open("#{state.session.to_s}_#{state.date.strftime("%Y%m%d%H%M")}_log.xml",File::WRONLY)
        $logfile.seek(0,IO::SEEK_END)
      else
        $logfile = File.open("#{state.session.to_s}_#{state.date.strftime("%Y%m%d%H%M")}_log.xml",File::WRONLY|File::CREAT)
        $logfile.write("<log>")
      end
    else
      if(RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin/)
        $logfile = File.open("nul",File::WRONLY)
      else
        $logfile = File.open("/dev/null",File::WRONLY)
      end
    end
    
    responce = state.parseElement(ele)
    if $logfile
      $logfile.close
    end
    if !file
      file = File.open(state.session.to_s,File::RDWR|File::CREAT)
      file.flock(File::LOCK_EX)
    end
    
    if state.finished
      file.close
      File::delete(state.session.to_s)
    else
      # 状態を保存
      file.write(Marshal.dump(state))
      
      # ロックを解除
      file.close
    end
    
  elsif responce.empty?
    # sessionエラー
    element = CommonXML::Element.create(TAG_RESPONCE)
    code = element.add_element(TAG_CODE)
    code.add_text("#{RESPONCE_SESSION}")
    
    responce.push(element)
  end
  
  resdoc = CommonXML::createDocument
  root = resdoc.add_element(TAG_OPENMAHJONGSERVER,{"version" => "0.1"})
  if state
    root.add_attribute("session","#{state.session}")
  end
  
  for element in responce
    root.push(element)
  end
  
  #decl = "<?xml version='1.0' encoding='Shift_JIS'?>"
  decl = ""
  
  tim = Time.now.sec * 1000 + Time.now.usec / 1000;
  
  buf = resdoc.toXML
  
  tim = Time.now.sec * 1000 + Time.now.usec / 1000 - tim
  
  $performdump.puts("003 : XML write #{tim} ms")  if $performdump
  
  decl += buf
  return decl
end

class Message
  attr_accessor :dst,:src,:from,:text
  def initialize
    @dst = []
    @src = nil
    @text = ""
  end
  
  def getElement
    element = CommonXML::Element.create(TAG_MESSAGE)
    
    srcEle = element.add_element(TAG_FROM)
    srcEle.push(@src.getElement)
    
    dstEle = element.add_element(TAG_TO)
    
    for player in @dst
      dstEle.push(player.getElement)
    end
    
    txtEle = element.add_element(TAG_TEXT)
    
    txtEle.add_text(@text)
    
    return element
  end
  
  
  def parseElement(node)
    @src = Player.new
    @src.id = node.findFirstElement("#{TAG_FROM}/#{TAG_PLAYER}/#{TAG_ID}").text.to_i
    @src.name = node.findFirstElement("#{TAG_FROM}/#{TAG_PLAYER}/#{TAG_NAME}").text
    
    node.each_element("#{TAG_TO}/#{TAG_PLAYER}") { | ele |
      player = Player.new
      player.id = ele.findFirstElement(TAG_ID).text.to_i
      player.name = ele.findFirstElement(TAG_NAME).text
      dst.push(player)
    }
    
    @text = node.findFirstElement(TAG_TEXT).text
  end
  
end

# ルールを保持するクラス
# ルールの内容はハッシュで保持する
# 各ルールの設定値は全て整数値とする(判定を簡略化するため)
# デフォルト値 nil をサポートするため、ハッシュ値の比較は== ではなく===(あるいはcase文)を使うのが無難(tips)
class Rule
  
  def initialize
    @ruleHash = Hash.new
  end
  
  def getElement
    element = CommonXML::Element.create(TAG_RULE)
    
    @ruleHash.each { |key,val|
      ele = CommonXML::Element.create(key)
      ele.add_text("#{val}")
      element.push(ele)
    }
    
    return element
  end
  
  def parseElement(node)
    # 子ノードを全てハッシュに書き込む。以上。
    
    node.each_element("*") { | cnode |
      @ruleHash[cnode.getTagName] = cnode.text.to_i
    }
  end
  
  # ハッシュの値を返す
  def [](key)
    return @ruleHash[key]
  end
  
end


# 麻雀サーバーライブラリ利用
class Library
  attr_accessor :id,:player,:tehai,:agarihai,:resultlist,:gamestate
  
  def initialize
    @id = 0
    @player = Player.new
    @tehai = []
    @agarihai = Pai::NULL
    @gamestate = GameState.new
    @resultlist = []
  end
  
  def getElement
    element = CommonXML::Element.create(TAG_LIBRARY)
    
    idEle = CommonXML::Element.create(TAG_ID)
    idEle.add_text("#{@id}")
    
    element.push(idEle)
    
    element.push(@player.getElement)
    
    tehaiEle = CommonXML::Element.create(TAG_TEHAI)
    
    for pai in @tehai
      tehaiEle.push(pai.getElement)
    end
    
    element.push(tehaiEle)
    
    agariEle = CommonXML::Element.create(TAG_AGARIHAI)
    agariEle.push(@agarihai.getElement)
    
    element.push(agariEle)
    
    resultEle = CommonXML::Element.create(TAG_RESULTLIST)
    
    for result in @resultlist
      resultEle.push(result.getElement)
    end
    
    element.push(resultEle)
    
    return element
  end
  
  def parseElement(node)
    # library ノードの解析
    @id = node.findFirstElement(TAG_ID).text.to_i
    #@player.name = node.findFirstElement("player/name").text.tosjis if node.findFirstElement("player/name").text
    @player.id = node.findFirstElement("#{TAG_PLAYER}/#{TAG_ID}").text.to_i
    
    node.each_element("#{TAG_TEHAI}/#{TAG_PAI}") { | painode |
      pai = Pai.new
      pai.category = painode.findFirstElement(TAG_CATEGORY).text.to_i
      pai.no = painode.findFirstElement(TAG_NO).text.to_i
      pai.id = painode.findFirstElement(TAG_ID).text.to_i
      @tehai.push(pai)
    }
    
    if node.findFirstElement(TAG_AGARIHAI)
      @agarihai = Pai.new
      @agarihai.category = painode.findFirstElement("#{TAG_AGARIHAI}/#{TAG_PAI}/#{TAG_CATEGORY}").text.to_i
      @agarihai.no = painode.findFirstElement("#{TAG_AGARIHAI}/#{TAG_PAI}/#{TAG_NO}").text.to_i
      @agarihai.id = painode.findFirstElement("#{TAG_AGARIHAI}/#{TAG_PAI}/#{TAG_ID}").text.to_i
    end
    
    @gamestate.zikaze = node.findFirstElement("#{TAG_GAMESTATE}/#{TAG_ZIKAZE}").text.to_i;		#/* 自風*			/
    @gamestate.count = node.findFirstElement("#{TAG_GAMESTATE}/#{TAG_COUNT}").text.to_i;		#/* 打順*			/
    @gamestate.ippatsu = node.findFirstElement("#{TAG_GAMESTATE}/#{TAG_IPPATSU}").text == "true" ? true : false; # 一発
    #@gamestate.endpai = node.findFirstElement("#{TAG_GAMESTATE}/#{TAG_ENDPAI}").text == "true" ? true : false; # 終りの牌
    #@gamestate.kan = node.findFirstElement("#{TAG_GAMESTATE}/#{TAG_KAN}").text == "true" ? true : false;			#/* 直前にカン*		/
    @gamestate.tsumo = node.findFirstElement("#{TAG_GAMESTATE}/#{TAG_TSUMO}").text == "true" ? true : false;		#/* 直前にツモ*		/
    @gamestate.riichi = node.findFirstElement("#{TAG_GAMESTATE}/#{TAG_RIICHI}").text == "true" ? true : false;		#/* リーチしている*		/
    @gamestate.oya = node.findFirstElement("#{TAG_GAMESTATE}/#{TAG_OYA}").text == "true" ? true : false;			#/* 親かどうか*		/
    @gamestate.agarihai = @agarihai;		#/* 上がった牌*		/
    @gamestate.naki = node.findFirstElement("#{TAG_GAMESTATE}/#{TAG_NAKI}").text == "true" ? true : false;		#/* ないたかどうか*		/
    #@gamestate.membernaki = node.findFirstElement("#{TAG_GAMESTATE}/#{TAG_MEMBERNAKI}").text == "true" ? true : false;		#/* 他の人がないたか*	/
    
    node.each_element("#{TAG_GAMESTATE}/#{TAG_NAKILIST}/#{TAG_NAKIMENTSU}") { | mentsu |
      nakimentsu = NakiMentsu.new
      nakimentsu.nakihai = Pai.new
      nakimentsu.nakihai.category = mentsu.findFirstElement("#{TAG_NAKIHAI}/#{TAG_PAI}/#{TAG_CATEGORY}").text.to_i
      nakimentsu.nakihai.no = mentsu.findFirstElement("#{TAG_NAKIHAI}/#{TAG_PAI}/#{TAG_NO}").text.to_i
      nakimentsu.nakihai.id = mentsu.findFirstElement("#{TAG_NAKIHAI}/#{TAG_PAI}/#{TAG_ID}").text.to_i
      mentsu.each_element("#{TAG_PAI}") { | pai |
        pai = Pai.new
        pai.category = mentsu.findFirstElement(TAG_CATEGORY).text.to_i
        pai.no = mentsu.findFirstElement(TAG_NO).text.to_i
        pai.id = mentsu.findFirstElement(TAG_ID).text.to_i
        nakimentsu.pailist.push(pai)
      }
      
      nakimentsu.aite = mentsu.findFirstElement(TAG_AITE).text.to_i
      nakimentsu.category = mentsu.findFirstElement(TAG_CATEGORY).text.to_i
      
      @gamestate.nakilist.push(nakimentsu)
    }
  end
  
end


# 麻雀サーバーの全ての状態を司る重要なクラス
class State
  attr_accessor :code,:taku,:synctable,:session,:finished
  attr_reader :date
  
  def initialize
    @code = CODE_WAITMEMBER
    @taku = Taku.new
    @session = -1
    @finished = false
    @date = Time.now
  end
  
  
  # command XMLを解析して実行する
  def parseElement(element)
    responce = []
    
    element.each_element(TAG_COMMAND) { | node |
      # command ノードの解析
      id = node.findFirstElement(TAG_ID).text.to_i
      player = node.findFirstElement(TAG_PLAYER)
      if id < ID_CREATE
        playid = player.findFirstElement(TAG_ID).text.to_i
        privateid = player.findFirstElement(TAG_PRIVATEID).text.to_i
        index = nil
        #index = @taku.members.index { | member| member.player.id == playid }
        for xxx in 0 .. @taku.members.length-1
          if @taku.members[xxx].player.id == playid && @taku.members[xxx].player.privateid == privateid
            index = xxx
            break
          end
        end
        
        res = CommonXML::Element.create(TAG_RESPONCE)
        
        if index != nil
          if id == ID_STATUS || id == ID_UPDATE
            code = res.add_element(TAG_CODE)
            code.add_text("#{RESPONCE_OK}")
            res.push(node.copy)
            responce.push(res)
            
            if @code == CODE_WAITMEMBER
              stat = CommonXML::Element.create(TAG_STATE)
              code = stat.add_element(TAG_CODE)
              code.add_text("#{@code}")
              nm = stat.add_element(TAG_NAME)
              nm.add_text("WAIT MEMBER")
              arg = stat.add_element(TAG_ARG)
              arg.add_text("#{4 - @taku.members.length}")
              
              responce.push(stat)
            elsif @code == CODE_WAITCOMMAND
              if @taku.members[index].mstate == MSTATE_SYNC
                # コマンドを選択済み(他のプレーヤとの同期待ち)
                cod = CODE_WAITSYNC
                nm = CommonXML::Element.create(TAG_NAME)
                nm.add_text("WAIT SYNC")
              elsif @taku.members[index].commandlist.empty?
                # ゲームが進行しました(イベント取得)
                cod = CODE_PROGRESSED
                @taku.members[index].mstate = MSTATE_SYNC
                nm = CommonXML::Element.create(TAG_NAME)
                nm.add_text("PROGRESSED")
              else
                # コマンドを選んでください
                cod = CODE_WAITCOMMAND
                nm = CommonXML::Element.create(TAG_NAME)
                nm.add_text("WAIT COMMAND")
              end
              
              stat = CommonXML::Element.create(TAG_STATE)
              code = stat.add_element(TAG_CODE)
              code.add_text("#{cod}")
              stat.push(nm)
              
              responce.push(stat)
              
              if id == ID_STATUS
                mode = STATE_MODE_NORMAL
              elsif cod == CODE_WAITSYNC
                mode = STATE_MODE_WAITSYNC
              elsif id == ID_UPDATE
                mode = STATE_MODE_UPDATE
              else
                mode = STATE_MODE_NORMAL
              end
              
              tim = Time.now.sec * 1000 + Time.now.usec / 1000
              responce.push(@taku.getElement(playid,privateid,mode))
              tim = Time.now.sec * 1000 + Time.now.usec / 1000 - tim
              
              $performdump.puts("101 : Build State Element #{tim} ms") if $performdump
            end
          else
            code = res.add_element(TAG_CODE)
            res.push(node.copy)
            responce.push(res)
            
            if @taku.members[index].mstate == MSTATE_NOSYNC && !@taku.members[index].commandlist.empty?
              # コマンドを照合
              comind = nil
              #comind = @taku.members[index].commandlist.index { |item| item.id == id }
              for xxx in 0 .. @taku.members[index].commandlist.length-1
                if @taku.members[index].commandlist[xxx].id == id
                  comind = xxx
                  break
                end
              end
              
              if comind
                # コマンドを選択
                code.add_text("#{RESPONCE_OK}")
                command = @taku.members[index].commandlist[comind]
                @taku.members[index].commandlist.clear
                @taku.members[index].commandlist.push(command)
                @taku.members[index].mstate = MSTATE_SYNC
              else
                # 該当するコマンドが存在しなかった
                code.add_text("#{RESPONCE_COMMANDID}")
              end
            else
              # ステータスが不正
              code.add_text("#{RESPONCE_STATUS}")
            end
            
          end
          
          #ind = @taku.members.index { |item| item.mstate == MSTATE_NOSYNC }
          ind = nil
          for xxx in 0 .. @taku.members.length-1
            if @taku.members[xxx].mstate == MSTATE_NOSYNC
              ind = xxx
              break
            end
          end
          
          
          # もし、全員が同期していたらゲームを進行させる
          if !ind
            if @taku.event && @taku.event.command.type == TYPE_END
              # 半チャン終了
              @finished = true
            else
              tim = Time.now.sec * 1000 + Time.now.usec / 1000
              @taku.progress
              tim = Time.now.sec * 1000 + Time.now.usec / 1000 - tim
              $performdump.puts("103 : Command Progress #{tim} ms")  if $performdump
            end
          end
          
          
        else
          # プレーヤーIDが不正
          code = res.add_element(TAG_CODE)
          code.add_text("#{RESPONCE_PLAYERID}")
          res.push(node.copy)
          responce.push(res)
        end
        
      end
      
      
      if id == ID_CREATE
        # セッションの作成
        r = rand(MAX_SESSION)
        count = 0
        
        while count < MAX_SESSION && File.exist?(r.to_s)
          r = (r + 1) % MAX_SESSION
          count += 1
        end
        
        res = CommonXML::Element.create(TAG_RESPONCE)
        
        if count >= MAX_SESSION
          code = res.add_element(TAG_CODE)
          code.add_text("#{RESPONCE_SESSION}")
          res.push(node.copy)
        else
          @session = r
          member = Member.new
          member.player.id = @taku.members.length
          member.player.name = player.findFirstElement(TAG_NAME).text # .tosjis
          member.player.privateid = player.findFirstElement(TAG_PRIVATEID).text.to_i
          
          # ルール設定
          ruleEle = node.findFirstElement(TAG_RULE)
          if ruleEle
            @taku.rule.parseElement(ruleEle)
          end
          
          @taku.members.push(member)
          
          # プレイヤーIDをつけて返す
          com = Command.new
          com.id = id
          com.player = member.player
          
          code = res.add_element(TAG_CODE)
          code.add_text("#{RESPONCE_OK}")
          res.push(com.getElement)
        end
        
        responce.push(res)
      end
      
      if id == ID_CONNECT
        res = CommonXML::Element.create(TAG_RESPONCE)
        
        if @code == CODE_WAITMEMBER
          member = Member.new
          member.player.id = @taku.members.length
          member.player.name = player.findFirstElement(TAG_NAME).text #.tosjis
          member.player.privateid = player.findFirstElement(TAG_PRIVATEID).text.to_i
          
          @taku.members.push(member)
          
          # プレイヤーIDをつけて返す
          com = Command.new
          com.id = id
          com.player = member.player
          
          if @taku.members.length == 4
            @code = CODE_WAITCOMMAND
            @taku.gameStart # ゲーム開始を宣言
          end
          
          code = res.add_element(TAG_CODE)
          code.add_text("#{RESPONCE_OK}")
          
          comEle = com.getElement
          
          # この卓のルールを渡す
          comEle.push(@taku.rule.getElement)
          
          res.push(comEle)
        else
          # ステータス不正
          code = res.add_element(TAG_CODE)
          code.add_text("#{RESPONCE_STATUS}")
          res.push(node.copy)
        end
        
        responce.push(res)
      end
      
      if id == ID_RECONNECT
        playid = player.findFirstElement(TAG_ID).text.to_i
        privateid = player.findFirstElement(TAG_PRIVATEID).text.to_i
        index = nil
        #index = @taku.members.index { | member| member.player.id == playid }
        for xxx in 0 .. @taku.members.length-1
          if @taku.members[xxx].player.id == playid && @taku.members[xxx].player.privateid == privateid
            index = xxx
            break
          end
        end
        
        res = CommonXML::Element.create(TAG_RESPONCE)
        
        if index != nil
          com = Command.new
          com.id = id
          com.player = @taku.members[index].player
          code = res.add_element(TAG_CODE)
          code.add_text("#{RESPONCE_OK}")
          
          comEle = com.getElement
          
          # この卓のルールを渡す
          comEle.push(@taku.rule.getElement)
          
          res.push(comEle)
        else
          # プレーヤーIDが不正
          code = res.add_element(TAG_CODE)
          code.add_text("#{RESPONCE_PLAYERID}")
          res.push(node.copy)
        end
        
        responce.push(res)
      end

      
      if id == ID_DEBUG
        res = CommonXML::Element.create(TAG_RESPONCE)
        code = res.add_element(TAG_CODE)
        code.add_text("#{RESPONCE_OK}")
        res.push(node.copy)
        responce.push(res)
        
        if @code == CODE_WAITMEMBER
          stat = CommonXML::Element.create(TAG_STATE)
          code = stat.add_element(TAG_CODE)
          code.add_text("#{@code}")
          nm = stat.add_element(TAG_NAME)
          nm.add_text("WAIT MEMBER")
          arg = stat.add_element(TAG_ARG)
          arg.add_text("#{4 - @taku.members.length}")
          
          responce.push(stat)
        else
          stat = CommonXML::Element.create(TAG_STATE)
          code = stat.add_element(TAG_CODE)
          code.add_text("#{@code}")
          nm = stat.add_element(TAG_NAME)
          nm.add_text("WAIT COMMAND")
          responce.push(stat)
          tim = Time.now.sec * 1000 + Time.now.usec / 1000
          responce.push(@taku.getElement(0,0,STATE_MODE_DEBUG))
          tim = Time.now.sec * 1000 + Time.now.usec / 1000 - tim
          for member in @taku.members
            member.player.privateid = 0
          end
  
          $performdump.puts("102 : Build State Element (Debug) #{tim} ms")  if $performdump
        end
      end
      
      if id == ID_REPEAT
        
        if @code == CODE_WAITMEMBER
          res = CommonXML::Element.create(TAG_RESPONCE)
          code = res.add_element(TAG_CODE)
          code.add_text("#{RESPONCE_STATUS}")
          res.push(node.copy)
          responce.push(res)
        else
          res = CommonXML::Element.create(TAG_RESPONCE)
          code = res.add_element(TAG_CODE)
          code.add_text("#{RESPONCE_OK}")
          res.push(node.copy)
          responce.push(res)
          @taku.kyokuStart(false,false,true)
        end
        
      end
      
      
    }
    
    # libraryノードの解析
    element.each_element(TAG_LIBRARY) { | node |
      lib = Library.new
      lib.parseElement(node)
      lib.tehai.sort!
      index = nil
      #index = @taku.members.index { | member| member.player.id == playid }
      for xxx in 0 .. @taku.members.length-1
        if @taku.members[xxx].player.id == lib.player.id
          index = xxx
          break
        end
      end
      
      res = CommonXML::Element.create(TAG_RESPONCE)
      
      if index != nil
        code = res.add_element(TAG_CODE)
        code.add_text("#{RESPONCE_OK}")
        responce.push(res)
        case lib.id
        when LIBID_AGARIHAI
          lib.resultlist = search_tenpai(lib.tehai,@taku,lib.gamestate)
        when LIBID_AGARITEN
          resultlist = search_tenpai(lib.tehai,@taku,lib.gamestate)
          for result in resultlist
            if result.machihai == lib.agarihai
              lib.resultlist.push(result)
            end
          end
        end
        
        responce.push(lib.getElement)
      else
        # プレーヤーIDが不正
        code = res.add_element(TAG_CODE)
        code.add_text("#{RESPONCE_PLAYERID}")
        res.push(node.copy)
        responce.push(res)
      end
      
    }
    
    
    # messageノードの解析(TODO)
    element.each_element(TAG_MESSAGE) { | node |
      mes = Message.new
      mes.parseElement(node)
      
      for player in mes.dst
        for member in @taku.members
          if player.id == member.player.id
            member.messagequeue.push(mes)
          end
        end
      end
    }
    
    
    return responce
  end
  
end


class OptimizedSet < Array
  def set(obj)
    ind = index(obj)
    
    if ind
      if obj >= self[ind]
        self[ind] = obj
      end
    else
      push(obj)
    end
  end
  
end

#/* 牌データ*/
class Pai
  attr_accessor :category,:no,:id,:tsumogiri,:riichi
  
  NULL = Pai.new
  
  def initialize
    @category = 0;
    @no = 0;
    @id = 0;
    @tsumogiri = false;
    @riichi = false;
  end
  
  def getElement
    element = CommonXML::Element.create(TAG_PAI)
    
    cEle = CommonXML::Element.create(TAG_CATEGORY)
    cEle.add_text("#{@category}")
    
    noEle = CommonXML::Element.create(TAG_NO)
    noEle.add_text("#{@no}")
    
    idEle = CommonXML::Element.create(TAG_ID)
    idEle.add_text("#{@id}")
    
    tsumoEle = CommonXML::Element.create(TAG_TSUMOGIRI)
    tsumoEle.add_text("#{@tsumogiri}");
    
    riichiEle = CommonXML::Element.create(TAG_RIICHI)
    riichiEle.add_text("#{@riichi}");
    
    element.push(cEle)
    element.push(noEle)
    element.push(idEle)
    element.push(tsumoEle)
    element.push(riichiEle)
    
    return element
  end
  
  def setName(nm)
    table = ["一","二","三","四","五","六","七","八","九"];
    table2 = ["東","南","西","北","白","発","中"];
    pos = 1;
    for l in table2
      if nm == l
        @category = PAI_ZIHAI
        @no = pos
        return
      end
      pos+=1
    end
    
    pos = 1;
    for l in table
      if nm[l]
        @no = pos
        if nm["萬"]
          @category = PAI_MANZU
        elsif nm["索"]
          @category = PAI_SOUZU
        else
          @category = PAI_PINZU
        end
        return
      end
      pos += 1;
    end
  end
  
  def getName
    table = ["一","二","三","四","五","六","七","八","九"];
    table2 = ["東","南","西","北","白","発","中"];
    nm = ""
    case @category
    when PAI_MANZU
      nm += table[@no - 1] + "萬";
    when PAI_SOUZU
      nm += table[@no - 1] + "索";
    when PAI_PINZU
      nm += table[@no - 1] + "筒";
    when PAI_ZIHAI
      nm += table2[@no - 1];
    else
    end
    
    return nm;
  end
  
  # ドラを表す牌を返す
  def getDora
    plus = Pai.new
    plus.category = @category
    plus.id = @id
    
    if @category == PAI_ZIHAI
      if @no == 4
        plus.no = 1
      elsif @no == 7
        plus.no = 5
      else
        plus.no = @no + 1
      end
      
    else
      if @no == 9
        plus.no = 1
      else
        plus.no = @no + 1
      end
    end
    
    return plus
  end
  
  
  def <=>(other)
    if @category > other.category
      return 1;
    elsif @category == other.category
      if @no > other.no
        return 1;
      elsif @no == other.no
        if @id > other.id
          return 1;
        elsif @id == other.id
          return 0;
        else
          return -1;
        end
      else
        return -1;
      end
    else
      return -1;
    end
  end
  
  
  def >(other)
    if @category > other.category
      return true;
    elsif @category == other.category
      if @no > other.no
        return true;
      else
        return false;
      end
    end
    return false;
  end
  
  def >=(other)
    if @category > other.category
      return true;
    elsif @category == other.category
      if @no >= other.no
        return true;
      else
        return false;
      end
    end
    return false;
  end
  
  def <(other)
    if @category < other.category
      return true;
    elsif @category == other.category
      if @no < other.no
        return true;
      else
        return false;
      end
    end
    return false;
  end
  
  def <=(other)
    if @category < other.category
      return true;
    elsif @category == other.category
      if @no <= other.no
        return true;
      else
        return false;
      end
    end
    return false;
  end
  
  def ==(other)
    if @category == other.category && @no == other.no
      return true;
    else
      return false;
    end
  end
  
  def isYaoTyu
    return (@category == PAI_ZIHAI || @no == 1 || @no == 9) ? true : false
  end
  
  def numHanpai(taku,gamestate)
    num = 0
    if category == PAI_ZIHAI
      
      # 三元牌
      if @no >= 5
        num += 1
      end
      
      # 場風
      if @no == taku.bakaze
        num += 1
      end
      
      # 自風
      if @no == gamestate.zikaze
        num += 1
      end
      
    end
    
    return num
  end
  
  def getNum
    return (@category - 1) * 9 + @no - 1
  end
  
end

class Command
  attr_accessor :id,:type,:player,:pai,:key,:mentsu
  
  def initialize
    @id = 0
    @type = 0
    @player = Player.new
    @pai = Pai::NULL
    @key = 0
    @mentsu = nil
  end
  
  def getElement
    element = CommonXML::Element.create(TAG_COMMAND)
    idEle = CommonXML::Element.create(TAG_ID)
    idEle.add_text("#{@id}")
    element.push(idEle)
    typeEle = CommonXML::Element.create(TAG_TYPE)
    typeEle.add_text("#{@type}")
    element.push(typeEle)
    
    element.push(@player.getElement)
    
    if @pai != Pai::NULL
      element.push(@pai.getElement)
    end
    
    if @key != 0
      keyEle = CommonXML::Element.create("key")
      keyEle.add_text("#{@key}")
      element.push(keyEle)
    end
    
    if @mentsu
      element.push(@mentsu.getElement)
    end
    
    return element
  end
  
end

class Event
  attr_accessor :command,:result,:seq
  
  def initialize
    @command = Command.new
    @result = nil
    @seq = 1
  end
  
  def getElement
    element = CommonXML::Element.create(TAG_EVENT)
    seqEle = CommonXML::Element.create(TAG_SEQ)
    seqEle.add_text("#{@seq}")
    element.push(seqEle)
    element.push(@command.getElement)
    element.push(@result.getElement) if @result
    
    return element
  end
end


class Taku
  attr_accessor :event,:turn,:dora,:uradora,:bakaze,:kyokucount,:riichibou,:tsumibou,:wanpai,:yamapai,:members,:event,:rule
  
  def initialize
    @event = nil
    @turn = 0  # 今誰のツモか
    @bakaze = 0 # 場風
    @kyokucount = -1 # 何局目か
    @riichibou = 0 # リーチ棒の本数
    @tsumibou = 0 # 積み棒の数
    @dora = [] # ドラ牌
    @uradora = [] # 裏ドラ牌
    @wanpai = [] # 王牌
    @yama = [] # 山牌
    @members = [] # メンバー
    @seed = 0
    @renchan = false # 連チャン判定保存のためのフラグ
    @rule = Rule.new
    @seq = 1 # イベントのシーケンス番号
  end
  
  def getElement(id,pid,mode = STATE_MODE_NORMAL)
    element = CommonXML::Element.create(TAG_TAKU)
    
    if @event && mode != STATE_MODE_WAITSYNC
      element.push(@event.getElement)
    end
    
    turnEle = CommonXML::Element.create(TAG_TURN)
    turnEle.add_text("#{@turn}")
    
    element.push(turnEle)
    
    if mode != STATE_MODE_UPDATE && mode != STATE_MODE_WAITSYNC
      
      bakazeEle = CommonXML::Element.create(TAG_BAKAZE)
      bakazeEle.add_text("#{@bakaze}")
      element.push(bakazeEle)
      
      kcountEle = CommonXML::Element.create(TAG_KYOKUCOUNT)
      kcountEle.add_text("#{@kyokucount}")
      
      element.push(kcountEle)
      
      
      riibou = CommonXML::Element.create(TAG_RIICHIBOU)
      riibou.add_text("#{@riichibou}")
      
      element.push(riibou)
      
      tsumiEle = CommonXML::Element.create(TAG_TSUMIBOU)
      tsumiEle.add_text("#{@tsumibou}")
      element.push(tsumiEle)
    end
    
    if mode == STATE_MODE_DEBUG
      uradoraEle = CommonXML::Element.create(TAG_URADORA)
      for d in @uradora
        uradoraEle.push(d.getElement)
      end
      
      element.push(uradoraEle)
      
      wanEle = CommonXML::Element.create(TAG_WANPAI)
      
      for d in @wanpai
        wanEle.push(d.getElement)
      end
      
      element.push(wanEle)
      
      yamaEle = CommonXML::Element.create(TAG_YAMA)
      countEle = yamaEle.add_element(TAG_COUNT)
      countEle.add_text("#{@yama.length}")
      
      for d in @yama
        yamaEle.push(d.getElement)
      end
      
      element.push(yamaEle)
      
      seedEle = CommonXML::Element.create(TAG_SEED)
      seedEle.add_text("#{@seed}")
      
      element.push(seedEle)
    elsif mode != STATE_MODE_WAITSYNC
      doraEle = CommonXML::Element.create(TAG_DORA)
      
      for d in @dora
        doraEle.push(d.getElement)
      end
      
      element.push(doraEle)
      
      if @event && @event.result
        uradoraEle = CommonXML::Element.create(TAG_URADORA)
        for d in @uradora
          uradoraEle.push(d.getElement)
        end
        
        element.push(uradoraEle)
      end
      
      yamaEle = CommonXML::Element.create(TAG_YAMA)
      countEle = yamaEle.add_element(TAG_COUNT)
      countEle.add_text("#{@yama.length}")
      
      element.push(yamaEle)
    end
    
    if @event && @event.command.type == TYPE_KOUHAI
      kouhai = true
    else
      kouhai = false
    end
    
    
    for member in @members
      if @event && @event.result && @event.command.player.id == member.player.id
        agari = true
      else
        agari = false
      end
      
      element.push(member.getElement(id,pid,mode,kouhai,agari,@rule[TAG_GLASSHAI] === 1))
    end
    
    for member in @members
      if member.player.id == id && member.player.privateid == pid
        if member.messagequeue
          while !member.messagequeue.empty?
            mes = member.messagequeue.delete_at(0)
            element.push(mes.getElement)
          end
        end
      end
    end
    
    
    return element
  end
  
  def gameStart
    @members = @members.sort_by{rand} # メンバーをシャッフル(場決めおよび親決め)
    
    # 点数をリセット
    for member in @members
      member.point = 25000
    end
    
    kyokuStart(false,false)
  end
  
  def kyokuStart(renchan,kouhai = false,repeat = false)
    if !repeat
      if !renchan
        @kyokucount += 1
      end
      
      if renchan || kouhai
        @tsumibou += 1
      else
        @tsumibou = 0
      end
      
    end
    
    
    @turn = @kyokucount % 4
    @bakaze = (@kyokucount / 4).floor + 1
    
    
    # シャッフル
    
    # 種を保存
    if repeat
      srand(@seed)
    else
      srand
      @seed = srand
      srand(@seed)
    end
    
    # 牌を初期化
    @yama.clear
    
    if @rule[TAG_HAIPAI] === 1 || @rule[TAG_HAIPAI] === 2 || @rule[TAG_HAIPAI] === 3 || @rule[TAG_HAIPAI] === 4 ||
      @rule[TAG_HAIPAI] === 5  || @rule[TAG_HAIPAI] === 6 || @rule[TAG_HAIPAI] === 7 || @rule[TAG_HAIPAI] === 8 ||
       @rule[TAG_HAIPAI] === 9 || @rule[TAG_HAIPAI] === 10
      remain = []
      paisort = []
      memfill = [[],[],[],[]]
      shanten = @rule[TAG_HAIPAI] > 5 ? @rule[TAG_HAIPAI] - 6 : @rule[TAG_HAIPAI] - 1
      
      remain.fill(4,0..33)
      
      for i in 0 .. 33
        s = [1,2,3,4]
        s = s.sort_by{rand}
        paisort.push(s)
      end
      
      
      for mem in memfill
        mem.fill(0,0..33)
        if @rule[TAG_HAIPAI] > 5
          fillOk = false
          while fillOk == false
            v = rand(8)

            if v == 0
              # 四暗刻
              fillOk = false

              # アタマを作る
              while fillOk == false
                v = rand(34)
                if remain[v] >= 2
                  remain[v] -= 2
                  mem[v] += 2
                  fillOk = true
                end
              end

              # 面子を作る
              for i in 1 .. 4
                fillOk = false
                while fillOk == false
                  v = rand(34)
                  if remain[v] >= 3
                    remain[v] -= 3
                    mem[v] += 3
                    fillOk = true
                  end
                end
              end
            elsif v == 1
              # 国士無双
              en = true
              en2 = []
              
              for i in [0,8,9,17,18,26,27,28,29,30,31,32,33]
                if remain[i] < 1
                  en = false
                  break
                end
                if remain[i] >= 2
                  en2.push(i)
                end
              end
              
              if en && en2.length >= 1
                w = rand(en2.length)
                for i in [0,8,9,17,18,26,27,28,29,30,31,32,33]
                  remain[i] -= 1
                  mem[i] += 1
                end
                
                remain[en2[w]] -= 1
                mem[en2[w]] += 1
                
                fillOk = true
              end

            elsif v == 2
              # 清老頭
              en = []
              for i in [0,8,9,17,18,26]
                if remain[i] > 3
                  en.push(i)
                end
              end
              
              if en.length > 5
                en = en.sort_by{rand}

                # アタマを作る
                remain[en[0]] -= 2
                mem[en[0]] += 2

                # 面子を作る
                for i in 1 .. 4
                  remain[en[i]] -= 3
                  mem[en[i]] += 3
                end
                
                fillOk = true
              end
            elsif v == 3
              # 九蓮宝燈
              en = []
              carray = [3,1,1,1,1,1,1,1,3]
              for i in [0,9,18]
                en2 = true
                for j in 0..8
                  if remain[i+j] < carray[j]
                    en2 = false
                    break
                  end
                end
                if en2 == true
                  en.push(i)
                end
              end
              
              if en.length >= 1
                w = rand(en.length)
                for i in 0..8
                  remain[en[w] + i] -= carray[i]
                  mem[en[w] + i] += carray[i]
                end
                
                begin
                  v = rand(9)
                end while remain[en[w] + v] < 1
                remain[en[w] + v] -= 1
                mem[en[w] + v] += 1
                
                fillOk = true
              end
              
            elsif v == 4
              # 大三元
              en = true
              for i in 31 .. 33
                if remain[i] < 3
                  en = false
                  break
                end
              end
              
              if en
                fillOk = false

                # アタマを作る
                while fillOk == false
                  v = rand(27)
                  if remain[v] >= 2
                    remain[v] -= 2
                    mem[v] += 2
                    fillOk = true
                  end
                end
                
                remain[31] -= 3
                mem[31] += 3
                remain[32] -= 3
                mem[32] += 3
                remain[33] -= 3
                mem[33] += 3
                
                # 面子を作る
                fillOk = false
                shuntsu = 21 * 16 # 順子の出やすさの補正
                while fillOk == false
                  v = rand(shuntsu+34)
                  if v < shuntsu
                    v = v % 21
                    st = (v / 7).floor * 9 + (v % 7)
                    if remain[st] >= 1 && remain[st+1] >= 1 && remain[st+2] >= 1
                      remain[st] -= 1
                      remain[st+1] -= 1
                      remain[st+2] -= 1
                      mem[st] += 1
                      mem[st+1] += 1
                      mem[st+2] += 1
                      fillOk = true
                    end
                  else
                    if remain[v-shuntsu] >= 3
                      remain[v-shuntsu] -= 3
                      mem[v-shuntsu] += 3
                      fillOk = true
                    end
                  end
                end
              end
            elsif v == 5
              # 緑一色
              en = []
              for i in [19,20,21,23,25,32]
                if remain[i] > 3
                  en.push(i)
                end
              end
              
              if en.length > 5
                en = en.sort_by{rand}

                # アタマを作る
                remain[en[0]] -= 2
                mem[en[0]] += 2

                # 面子を作る
                for i in 1 .. 4
                  remain[en[i]] -= 3
                  mem[en[i]] += 3
                end
                
                fillOk = true
              end
            elsif v == 6
              # 字一色
              en = []
              for i in 27 .. 33
                if remain[i] > 3
                  en.push(i)
                end
              end
              
              if en.length > 5
                fillOk = false
                en = en.sort_by{rand}

                # アタマを作る
                remain[en[0]] -= 2
                mem[en[0]] += 2

                # 面子を作る
                for i in 1 .. 4
                  remain[en[i]] -= 3
                  mem[en[i]] += 3
                end
                
                fillOk = true
              end
              
            else
              # 大四喜
              en = true
              for i in 27 .. 30
                if remain[i] < 3
                  en = false
                  break
                end
              end
              
              if en
                fillOk = false

                # アタマを作る
                while fillOk == false
                  v = rand(27)
                  if remain[v] >= 2
                    remain[v] -= 2
                    mem[v] += 2
                    fillOk = true
                  end
                end
                
                remain[27] -= 3
                mem[27] += 3
                remain[28] -= 3
                mem[28] += 3
                remain[29] -= 3
                mem[29] += 3
                remain[30] -= 3
                mem[30] += 3
                
              end
              
            end
          end
        else
          fillOk = false
          
          # アタマを作る
          while fillOk == false
            v = rand(34)
            if remain[v] >= 2
              remain[v] -= 2
              mem[v] += 2
              fillOk = true
            end
          end

          # 面子を作る
          for i in 1 .. 4
            fillOk = false
            shuntsu = 21 * 16 # 順子の出やすさの補正
            while fillOk == false
              v = rand(shuntsu+34)
              if v < shuntsu
                v = v % 21
                st = (v / 7).floor * 9 + (v % 7)
                if remain[st] >= 1 && remain[st+1] >= 1 && remain[st+2] >= 1
                  remain[st] -= 1
                  remain[st+1] -= 1
                  remain[st+2] -= 1
                  mem[st] += 1
                  mem[st+1] += 1
                  mem[st+2] += 1
                  fillOk = true
                end
              else
                if remain[v-shuntsu] >= 3
                  remain[v-shuntsu] -= 3
                  mem[v-shuntsu] += 3
                  fillOk = true
                end
              end
            end
          end
        end


        # 適当に一枚抜く
        v = rand(13) + 1
        for i in 0..33
          v -= mem[i]
          if v <= 0
            remain[i] += 1
            mem[i] -= 1
            break
          end
        end

        i = 0
        ex = []
        while i < shanten
          # テンパイを崩す
          v = rand(13) + 1
          for j in 0..33
            v -= mem[j]
            if v <= 0
              remain[j] += 1
              mem[j] -= 1
              ex.push(j)
              break
            end
          end

          i += 1
        end
        
        for i in ex
          begin
            k = rand(34)
          end while remain[k] < 1
          remain[k] -= 1
          mem[k] += 1
        end

      end

      for i in 0 .. 33
        for j in 1 .. remain[i]
          tmp = Pai.new
          tmp.category = (i / 9).floor + 1
          tmp.no = (i % 9) + 1
          tmp.id = paisort[i].pop
          @yama.push(tmp)
        end
      end
      
      @yama = @yama.sort_by{rand}
      
      for mem in memfill
        for i in 0 .. 33
          for j in 1 .. mem[i]
            tmp = Pai.new
            tmp.category = (i / 9).floor + 1
            tmp.no = (i % 9) + 1
            tmp.id = paisort[i].pop
            @yama.push(tmp)
          end
        end
      end
      
    else
      
      for i in 1 .. 3
        for j in 1 .. 9
          for k in 1 .. 4
            tmp = Pai.new
            tmp.category = i
            tmp.no = j
            tmp.id = k
            @yama.push(tmp)
          end
        end
      end
      
      for i in 1 .. 7
        for j in 1 .. 4
          tmp = Pai.new
          tmp.category = PAI_ZIHAI
          tmp.no = i
          tmp.id = j
          @yama.push(tmp)
        end
      end
      
      @yama = @yama.sort_by{rand}
    end
    
    for member in @members
      member.gamestate = GameState.new
      member.tehai.clear
      member.dahai.clear
      member.resultlist.clear
      member.commandlist.clear
      member.agaripass = false
      # 配牌
      for i in 1 .. 13
        member.tehai.push(@yama.pop)
      end
      
      # 理牌
      member.tehai.sort!
      
      member.resultlist = search_tenpai(member.tehai,self,member.gamestate)
    end
    
    # 王牌を除去
    for i in 1 .. 12
      @wanpai.push(@yama.pop)
    end
    
    # ドラと裏ドラ牌
    @dora = []
    @uradora = []
    
    @dora.push(@yama.pop)
    @uradora.push(@yama.pop)
    
    for i in 0 .. 3
      @members[i].gamestate.zikaze = (i-@turn) % 4 + 1
      if i == @turn
        @members[i].gamestate.oya = true
      else
        @members[i].gamestate.oya = false
      end
      
    end
    
    @event = Event.new
    @event.command.type = TYPE_START
    @event.seq = @seq
    @seq += 1
    
    # ログの出力
    $logfile.write("<kyoku>")
    $logfile.write("<#{TAG_KYOKUCOUNT}>#{@kyokucount}</#{TAG_KYOKUCOUNT}><#{TAG_BAKAZE}>#{@bakaze}</#{TAG_BAKAZE}><#{TAG_RIICHIBOU}>#{@riichibou}</#{TAG_RIICHIBOU}><#{TAG_TSUMIBOU}>#{@tsumibou}</#{TAG_TSUMIBOU}>")
    
    for member in @members
      $logfile.write("<#{TAG_MEMBER}>")
      $logfile.write("<#{TAG_PLAYER}><#{TAG_ID}>#{member.player.id}</#{TAG_ID}><#{TAG_NAME}>#{member.player.name}</#{TAG_NAME}></#{TAG_PLAYER}>")
      
      $logfile.write("<#{TAG_ZIKAZE}>#{member.gamestate.zikaze}</#{TAG_ZIKAZE}>")
      $logfile.write("<#{TAG_POINT}>#{member.point}</#{TAG_POINT}>")
      
      $logfile.write("<#{TAG_TEHAI}>")
      
      for hai in member.tehai
        $logfile.write("<#{TAG_PAI}><num>#{hai.getNum}</num><id>#{hai.id}</id></#{TAG_PAI}>")
      end
      
      $logfile.write("</#{TAG_TEHAI}>")
      $logfile.write("</#{TAG_MEMBER}>")
    end
    
    # 第一ツモ
    tsumo()
  end
  
  # 罰符の計算
  def notenBappu
    $logfile.write("<#{TAG_RESULT}>")
    $logfile.write("<#{TAG_YAKU}>荒牌平局</#{TAG_YAKU}>")
    
    count = @members.inject(0) { |t,item| item.resultlist.empty? ? (t + 1) : t } # ノーテンの人物をカウント
    if count != 0 && count != 4
      bappuminus = 3000 / count;
      bappuplus = 3000 / (4 - count);
      for member in @members
        if member.resultlist.empty?
          member.point -= bappuminus
          $logfile.write("<#{TAG_PLAYER}><#{TAG_ID}>#{member.player.id}</#{TAG_ID}><#{TAG_NAME}>#{member.player.name}</#{TAG_NAME}><#{TAG_POINT}>#{-bappuminus - (member.gamestate.riichi ? 1000 : 0)}</#{TAG_POINT}></#{TAG_PLAYER}>")
          if member.gamestate.oya
            @renchan = false
          end
        else
          member.point += bappuplus
          $logfile.write("<#{TAG_PLAYER}><#{TAG_ID}>#{member.player.id}</#{TAG_ID}><#{TAG_NAME}>#{member.player.name}</#{TAG_NAME}><#{TAG_POINT}>#{bappuplus - (member.gamestate.riichi ? 1000 : 0)}</#{TAG_POINT}></#{TAG_PLAYER}>")
          if member.gamestate.oya
            @renchan = true
          end
        end
      end
    end
    
    $logfile.write("<#{TAG_DORA}>")
    
    for d in @dora
      dorahai = d.getDora
      $logfile.write("<#{TAG_PAI}><num>#{dorahai.getNum}</num><id>#{dorahai.id}</id></#{TAG_PAI}>")
    end
    
    $logfile.write("</#{TAG_DORA}>")
    
    $logfile.write("<#{TAG_URADORA}>")
    
    for d in @uradora
      dorahai = d.getDora
      $logfile.write("<#{TAG_PAI}><num>#{dorahai.getNum}</num><id>#{dorahai.id}</id></#{TAG_PAI}>")
    end
    
    $logfile.write("</#{TAG_URADORA}>")
    
    $logfile.write("</#{TAG_RESULT}>")
  end
  
  def tsumo
    timx = Time.now.sec * 1000 + Time.now.usec / 1000
    
    for member in @members
      member.tsumohai = nil
    end

    # ツモの処理
    # 戻り値(コマンド群)
    commands = []
    # ツモ牌
    hai = @yama.pop
    member = @members[@turn]
    member.gamestate.tsumo = true
    
    member.tsumohai = hai
    
    results = nil
    
    if @yama.length == 0
      for m in @members
        m.gamestate.endpai = true
      end
    end
    
    $logfile.write("<tsumo><#{TAG_PLAYER}><#{TAG_ID}>#{member.player.id}</#{TAG_ID}></#{TAG_PLAYER}><#{TAG_PAI}><num>#{hai.getNum}</num><id>#{hai.id}</id></#{TAG_PAI}></tsumo>")

    
    # 打牌
    com = Command.new
    com.id = ID_DAHAI + member.tehai.length
    com.type = TYPE_DAHAI
    com.player = member.player
    com.pai = hai.clone
    
    com.pai.tsumogiri = true
    commands.push(com)
    if member.gamestate.riichi
      # 暗カンの判定
      
      mentsu = []
      for xxx in 0..member.tehai.length-1
        mentsu.push(UKIHAI)
      end
      
      paicountlist = []
      setpaicount(paicountlist,member.tehai,mentsu)
      for paicount in paicountlist
        if paicount.count == 3 && paicount.pai == hai
          tmptehai = Marshal.load(Marshal.dump(member.tehai))
          tmptehai.delete(paicount.pai)
          
          tmpresult = search_tenpai(tmptehai,self,member.gamestate)
          
          machi1 = Array.new(34,false)
          machi2 = Array.new(34,false)
          
          for result in tmpresult
            machi1[result.machihai.getNum] = true
          end
          
          for result in member.resultlist
            machi2[result.machihai.getNum] = true
          end
          
          # 待ちが同じ場合のみ暗カン可能
          if machi1 == machi2
            mentsu = NakiMentsu.new
            mentsu.pailist = member.tehai[paicount.startpos .. paicount.startpos + 2]
            mentsu.pailist.push(hai)
            mentsu.category = MENTSU_ANKAN
            mentsu.aite = @turn
            
            com = Command.new
            com.id = paicount.startpos + ID_KAN
            com.type = TYPE_ANKAN
            com.player = member.player
            com.pai = hai
            com.mentsu = mentsu
            commands.push(com)
          end
          
        end
        
      end
    else
      for i in 0 .. member.tehai.length-1
        com = Command.new
        com.id = i + ID_DAHAI
        com.type = TYPE_DAHAI
        com.player = member.player
        com.pai = member.tehai[i]
        commands.push(com)
      end
      
      # リーチの判定
      if !member.gamestate.naki && member.point >= 1000 && @yama.length >= 4
        for i in 0 .. member.tehai.length-1
          tehai = Array.new(member.tehai)
          tehai.delete_at(i)
          tehai.push(hai)
          tehai.sort!
          tim = Time.now.sec * 1000 + Time.now.usec / 1000;
          
          results = search_tenpai(tehai,self,member.gamestate)
          
          tim = Time.now.sec * 1000 + Time.now.usec / 1000 - tim
          
          $performdump.puts("202 : search_tenpai #{tim} ms")  if $performdump
          
          # テンパイなのでリーチ可能
          if results.length > 0
            com = Command.new
            com.id = i + ID_RIICHI
            com.type = TYPE_RIICHI
            com.player = member.player
            com.pai = member.tehai[i]
            commands.push(com)
          end
          
        end
        
        # ツモ切りリーチ
        tim = Time.now.sec * 1000 + Time.now.usec / 1000;
        
        results = search_tenpai(member.tehai,self,member.gamestate)
        
        tim = Time.now.sec * 1000 + Time.now.usec / 1000 - tim
        
        $performdump.puts("202 : search_tenpai #{tim} ms")  if $performdump
        
        if results.length > 0
          com = Command.new
          com.id = member.tehai.length + ID_RIICHI
          com.type = TYPE_RIICHI
          com.player = member.player
          com.pai = hai
          commands.push(com)
        end
        
      end
      
      
      # カンの判定
      mentsu = []
      for xxx in 0..member.tehai.length-1
        mentsu.push(UKIHAI)
      end
      
      paicountlist = []
      setpaicount(paicountlist,member.tehai,mentsu)
      for paicount in paicountlist
        if paicount.count == 4
          mentsu = NakiMentsu.new
          mentsu.pailist = member.tehai[paicount.startpos .. paicount.startpos + 3]
          mentsu.category = MENTSU_ANKAN
          mentsu.aite = @turn
          
          com = Command.new
          com.id = paicount.startpos + ID_KAN
          com.type = TYPE_ANKAN
          com.player = member.player
          com.pai = paicount.pai
          com.mentsu = mentsu
          commands.push(com)
        elsif paicount.count == 3 && paicount.pai == hai
          mentsu = NakiMentsu.new
          mentsu.pailist = member.tehai[paicount.startpos .. paicount.startpos + 2]
          mentsu.pailist.push(hai)
          mentsu.category = MENTSU_ANKAN
          mentsu.aite = @turn
          
          com = Command.new
          com.id = paicount.startpos + ID_KAN
          com.type = TYPE_ANKAN
          com.player = member.player
          com.pai = hai
          com.mentsu = mentsu
          commands.push(com)
        end
        
      end
      
      # 加えカン
      for nakimentsu in member.gamestate.nakilist
        if nakimentsu.category == MENTSU_KOUTSU
          pai = nakimentsu.nakihai
          ind = member.tehai.index(pai)
          if ind != nil
            mentsu = Marshal.load(Marshal.dump(nakimentsu))
            mentsu.pailist.push(member.tehai[ind])
            mentsu.category = MENTSU_MINKAN
            com = Command.new
            com.id = ind + ID_KAN
            com.type = TYPE_KUWAEKAN
            com.player = member.player
            com.pai = member.tehai[ind]
            com.mentsu = mentsu
            commands.push(com)
          elsif pai == hai
            mentsu = Marshal.load(Marshal.dump(nakimentsu))
            mentsu.pailist.push(hai)
            mentsu.category = MENTSU_MINKAN
            com = Command.new
            com.id = member.tehai.length + ID_KAN
            com.type = TYPE_KUWAEKAN
            com.player = member.player
            com.pai = hai
            com.mentsu = mentsu
            commands.push(com)
          end
          
        end
      end
      
      
    end
    
    # ツモアガリの判定
    if !results
      tim = Time.now.sec * 1000 + Time.now.usec / 1000;
      
      results = search_tenpai(member.tehai,self,member.gamestate)
        
      tim = Time.now.sec * 1000 + Time.now.usec / 1000 - tim
      
      $performdump.puts("202 : search_tenpai #{tim} ms")  if $performdump
    end
    for result in results
      if result.machihai == hai && result.han > 0
        com = Command.new
        com.id = ID_TSUMO
        com.type = TYPE_TSUMO
        com.player = member.player
        com.pai = hai
        commands.push(com)
        break
      end
    end
    
    member.resultlist = results
    
    # 後処理
    member.commandlist = commands
    member.tehai.push(hai)
    
    timx = Time.now.sec * 1000 + Time.now.usec / 1000 - tim
    
    $performdump.puts("201 : tsumo #{tim} ms") if $performdump
    return commands
  end
  
  
  # 鳴けるプレーヤーを検索
  def ron_judge(naki,pai)
    # ロンの判定

    for i in 0 .. 3
      if i != @turn
        member = @members[i]

        # ロンの判定
        resultlist = member.resultlist
        if !resultlist.empty?
          furiten = false
          # 振聴の判定
          if member.agaripass
            furiten = true
          else
            for result in resultlist
              if member.dahai.include?(result.machihai)
                furiten = true
                break
              end
              if member.gamestate.riichi
              else
                j = (@turn - 1) % 4
                while j != i
                  if @members[j].dahai[-1] == result.machihai
                    furiten = true
                  end
                  j = (j - 1) % 4
                end
              end
            end
          end
          
          if !furiten
            #resultidx = resultlist.index { |item| item.machihai == pai }
            resultidx = nil
            for xxx in 0 .. resultlist.length-1
              if resultlist[xxx].machihai == pai
                resultidx = xxx
                break
              end
            end

            if resultidx
              result = resultlist[resultidx]
              # 役を再計算(人和、河底対策)
              if result.yaku.index("国士無双")
                Dir::glob( "plugin/renho.rb" ).sort.each do |file|
                  open( file.untaint) do | src |
                    YakuPlugin.class_eval( src.read.untaint )
                  end
                  obj = YakuPlugin.new
                  if obj.hantei(result.mentsulist,self,member.gamestate,result.mati)
                    if !(@rule[TAG_AOTENJO] === 1) && obj.isYakuman
                      if result.han < 13
                        result.han = obj.getHan(result.mentsulist,self,member.gamestate,result.mati)
                        result.yaku = []
                        result.yaku.push(obj.getName)
                      else
                        result.han += obj.getHan(result.mentsulist,self,member.gamestate,result.mati)
                        result.yaku.push(obj.getName)
                      end
                    elsif @rule[TAG_AOTENJO] === 1 || result.han < 13
                      result.yaku.push(obj.getName)
                      result.han += obj.getHan(result.mentsulist,self,member.gamestate,result.mati)
                    end
                  end

                end

              else
                make_yaku(result,self,member.gamestate,result.mati)
              end
              

              if result.han > 0 # 1翻縛り
                naki[i] = true
                com = Command.new
                com.id = ID_RON
                com.type = TYPE_RON
                com.player = member.player
                com.pai = pai
                member.commandlist.push(com)
                member.resultlist = resultlist
              end

            end
          end
        end

      end

    end
    
  end
  
  def naki_judge(naki,pai)
    for i in 0 .. 3
      if i != @turn
        member = @members[i]

        if !member.gamestate.riichi
          if i == (@turn + 1) % 4 && pai.category != PAI_ZIHAI # チーは下家だけ
            clist = chi_list(member.tehai,pai,@turn)

            if !clist.empty?
              naki[i] = true
              id = ID_TII
              for mentsu in clist
                com = Command.new
                com.id = id
                com.type = TYPE_TII
                com.player = member.player
                com.pai = pai
                com.mentsu = mentsu
                member.commandlist.push(com)
                id += 1
              end

            end
          end

          # ポンとカンの判定
          plist = ponkan_list(member.tehai,pai,@turn)

          if !plist.empty?
            naki[i] = true
            id = ID_PON
            for mentsu in plist
              if mentsu.category == MENTSU_MINKAN
                com = Command.new
                com.id = ID_DAIMINKAN
                com.type = TYPE_DAIMINKAN
                com.player = member.player
                com.mentsu = mentsu
                com.pai = pai
                member.commandlist.push(com)
              else
                com = Command.new
                com.id = id
                com.type = TYPE_PON
                com.player = member.player
                com.mentsu = mentsu
                com.pai = pai
                id += 1
                member.commandlist.push(com)
              end
            end
          end
        end
      end
    end
    
  end
  
  
  # 局の進行(TODO)
  def progress
    execom = nil
    exeturn = 0
    
    # dummy
    for i in 0 .. 3
      member = @members[i]
      if member.mstate == MSTATE_SYNC
        member.mstate = MSTATE_NOSYNC
        if !member.commandlist.empty?
          com = member.commandlist.pop
          if execom == nil || com.id > execom.id
            execom = com
            exeturn = i
          end
          
          if com.id == ID_RON && execom.id == ID_RON && (exeturn - @turn) % 4 > (i - @turn) % 4
            # ダブロンは頭ハネ(暫定)
            execom = com
            exeturn = i
          end
          
          if com.id == ID_PASS && member.gamestate.riichi
            member.agaripass = true
          end
          
        end
      end
      member.commandlist.clear
      member.tsumohai = nil
      
    end
    
    @event = Event.new
    @event.command = execom
    @event.seq = @seq
    @seq += 1
    
    
    $logfile.write("<#{TAG_EVENT}>")
    $logfile.write("<#{TAG_ID}>#{execom.id}</#{TAG_ID}>")
    $logfile.write("<#{TAG_PLAYER}><#{TAG_ID}>#{execom.player.id}</#{TAG_ID}></#{TAG_PLAYER}>")
    if execom.pai && execom.pai != Pai::NULL
      $logfile.write("<#{TAG_PAI}><num>#{execom.pai.getNum}</num><id>#{execom.pai.id}</id><#{TAG_TSUMOGIRI}>#{execom.pai.tsumogiri}</#{TAG_TSUMOGIRI}></#{TAG_PAI}>")
    end
    
    if execom.mentsu
      $logfile.write("<#{TAG_MENTSU}>")
      for pai in execom.mentsu.pailist
        $logfile.write("<#{TAG_PAI}><num>#{pai.getNum}</num><id>#{pai.id}</id></#{TAG_PAI}>")
      end
      $logfile.write("</#{TAG_MENTSU}>")
    end
    
    
    $logfile.write("</#{TAG_EVENT}>")
    
    
    if execom.id >= ID_DAHAI && execom.id <= ID_DAHAI + 13
      execom.type = TYPE_DAHAI
      # 打牌
      #ind = @members.index { | item | item.player.id == execom.player.id }
      ind = nil
      for xxx in 0 .. @members.length-1
        if @members[xxx].player.id == execom.player.id
          ind = xxx
          break
        end
      end
      
      member = @members[ind]
      
      member.tehai.delete_at(execom.id - ID_DAHAI)
      pai = execom.pai
#      if execom.id == ID_DAHAI + member.tehai.length
#        pai.tsumogiri = true
#      end
      
      # リーチ牌を鳴かれていたら次の牌にフラグを立てる
      if member.gamestate.riichi
        bRiichi = false
        for pai2 in member.dahai
          if pai2.riichi
            bRiichi = true
          end
        end
        if !bRiichi
          pai.riichi = true
        end
      end
      
      member.dahai.push(pai)
      member.tehai.sort!
      member.gamestate.ippatsu = false
      member.gamestate.tsumo = false
      member.gamestate.kan = false
      member.gamestate.count += 1
      
      tim = Time.now.sec * 1000 + Time.now.usec / 1000;
      
      member.resultlist = search_tenpai(member.tehai,self,member.gamestate)
      
      tim = Time.now.sec * 1000 + Time.now.usec / 1000 - tim
      
      $performdump.puts("202 : search_tenpai #{tim} ms")  if $performdump
      
      # 荒牌
      if @yama.empty?
        naki = [false,false,false,false]
        
        ron_judge(naki,pai)
        
        yyy = nil
        for xxx in 0 .. naki.length-1
          if naki[xxx]
            yyy = xxx
            com = Command.new
            com.id = ID_PASS
            com.type = TYPE_PASS
            com.player = member.player
            @members[xxx].commandlist.push(com)
          end
        end
        
        if yyy == nil
          # 鳴ける人がいない場合は荒牌
          execom.type = TYPE_KOUHAI
          notenBappu()
          
          for member in @members
            # 次局へ進行
            com = Command.new
            com.id = ID_START
            com.type = TYPE_KOUHAI
            com.player = member.player
            member.commandlist.push(com)
          end
        end
        
      else
      
        # 鳴きの判定
        
        naki = [false,false,false,false]
        
        naki_judge(naki,pai)
        ron_judge(naki,pai)
        
        yyy = nil
        for xxx in 0 .. naki.length-1
          if naki[xxx]
            yyy = xxx
            com = Command.new
            com.id = ID_PASS
            com.type = TYPE_PASS
            com.player = member.player
            @members[xxx].commandlist.push(com)
          end
        end
        
        if yyy == nil
          # 鳴ける人がいない場合は次のツモを続ける
          @turn = (@turn + 1) % 4
          tsumo()
        end
      end
      
      
    elsif execom.id == ID_PASS
      execom.type = TYPE_PASS
      # 鳴かない
      if @yama.empty?
        execom.type = TYPE_KOUHAI
        notenBappu()
        
        for member in @members
          # 次局へ進行
          com = Command.new
          com.id = ID_START
          com.type = TYPE_KOUHAI
          com.player = member.player
          member.commandlist.push(com)
        end
      else
        @turn = (@turn + 1) % 4
        tsumo()
      end
    elsif execom.id >= ID_TII && execom.id < ID_PON
      execom.type = TYPE_TII
      # チー
      for mem in @members
        mem.gamestate.ippatsu = false
      end
      
      srcmem = @members[@turn]
      
      srcmem.gamestate.membernaki = true
      nakihai = srcmem.dahai.pop
      
      nakimentsu = execom.mentsu
      
      @turn = (@turn + 1) % 4
      
      member = @members[@turn]
      member.gamestate.naki = true
      member.gamestate.nakilist.push(nakimentsu)
      
      for pai in execom.mentsu.pailist
        member.tehai = member.tehai.delete_if { |item| item == pai && item.id == pai.id}
      end
      
      # 打牌
      for i in 0 .. member.tehai.length-1
        com = Command.new
        com.id = i + ID_DAHAI
        com.type = TYPE_DAHAI
        com.player = member.player
        com.pai = member.tehai[i]
        member.commandlist.push(com)
      end
    elsif execom.id >= ID_PON && execom.id < ID_DAIMINKAN
      execom.type = TYPE_PON
      # ポン
      for mem in @members
        mem.gamestate.ippatsu = false
      end
      srcmem = @members[@turn]
      
      srcmem.gamestate.membernaki = true
      nakihai = srcmem.dahai.pop
      
      nakimentsu = execom.mentsu
      
      #@turn = @members.index { | item | item.player.id == execom.player.id }
      @turn = nil
      for xxx in 0 .. @members.length-1
        if @members[xxx].player.id == execom.player.id
          @turn = xxx
          break
        end
      end
      
      member = @members[@turn]
      member.gamestate.naki = true
      member.gamestate.nakilist.push(nakimentsu)
      
      for pai in execom.mentsu.pailist
        member.tehai = member.tehai.delete_if { |item| item == pai && item.id == pai.id}
      end
      
      # 打牌
      for i in 0 .. member.tehai.length-1
        com = Command.new
        com.id = i + ID_DAHAI
        com.type = TYPE_DAHAI
        com.player = member.player
        com.pai = member.tehai[i]
        member.commandlist.push(com)
      end
    elsif execom.id == ID_DAIMINKAN
      execom.type = TYPE_DAIMINKAN
      # カン
      for mem in @members
        mem.gamestate.ippatsu = false
      end
      srcmem = @members[@turn]
      
      srcmem.gamestate.membernaki = true
      nakihai = srcmem.dahai.pop
      
      nakimentsu = execom.mentsu
      
      #@turn = @members.index { | item | item.player.id == execom.player.id }
      @turn = nil
      for xxx in 0 .. @members.length-1
        if @members[xxx].player.id == execom.player.id
          @turn = xxx
          break
        end
      end
      
      member = @members[@turn]
      member.gamestate.naki = true
      member.gamestate.nakilist.push(nakimentsu)
      member.gamestate.kan = true
      
      for pai in execom.mentsu.pailist
        member.tehai = member.tehai.delete_if { |item| item == pai && item.id == pai.id}
      end
      
      # カンドラ
      @dora.push(@wanpai.pop)
      @uradora.push(@wanpai.pop)
      
      tsumo()
    elsif execom.id == ID_RON
      execom.type = TYPE_RON
      # ロン
      # 上がりの判定
      #ind = @members.index { | item | item.player.id == execom.player.id }
      ind = nil
      for xxx in 0 .. @members.length-1
        if @members[xxx].player.id == execom.player.id
          ind = xxx
          break
        end
      end
      member = @members[ind]
      #resind = member.resultlist.index { |item| execom.pai == item.machihai }
      resind = nil
      for xxx in 0 .. member.resultlist.length-1
        if member.resultlist[xxx].machihai == execom.pai
          resind = xxx
          break
        end
      end
      
      result = member.resultlist[resind]
      
      # ドラの判定
      
      if result.han < 13 || @rule[TAG_AOTENJO] === 1
        doranum = 0
        for d in @dora
          dorahai = d.getDora
          if dorahai == execom.pai
            doranum += 1
          end

          doranum += member.tehai.inject(0) { | t,item | item == dorahai ? t + 1 : t}
          for mentsu in member.gamestate.nakilist
            doranum += mentsu.pailist.inject(0) { | t,item | item == dorahai ? t + 1 : t}
          end
        end

        if @rule[TAG_AKA] === 1
          item = execom.pai
          if (item.category != PAI_ZIHAI && item.no == 5 && item.id == 4)
            doranum += 1
          end

          doranum += member.tehai.inject(0) { | t,item | (item.category != PAI_ZIHAI && item.no == 5 && item.id == 4) ? t + 1 : t }
          for mentsu in member.gamestate.nakilist
            doranum += mentsu.pailist.inject(0) { | t,item | (item.category != PAI_ZIHAI && item.no == 5 && item.id == 4) ? t + 1 : t}
          end
        elsif @rule[TAG_AKA] === 2
          item = execom.pai
          if (item.category != PAI_ZIHAI && item.no == 5 && (item.id == 4 || (item.category == PAI_PINZU && item.id == 3)))
            doranum += 1
          end
          doranum += member.tehai.inject(0) { | t,item | (item.category != PAI_ZIHAI && item.no == 5 && (item.id == 4 || (item.category == PAI_PINZU && item.id == 3))) ? t + 1 : t }
          for mentsu in member.gamestate.nakilist
            doranum += mentsu.pailist.inject(0) { | t,item | (item.category != PAI_ZIHAI && item.no == 5 && (item.id == 4 || (item.category == PAI_PINZU && item.id == 3))) ? t + 1 : t}
          end
        elsif @rule[TAG_AKA] === 3
          item = execom.pai
          if (item.id == 4 || (item.category == PAI_ZIHAI && item.no == 7))
            doranum += 1
          end
          doranum += member.tehai.inject(0) { | t,item | (item.id == 4 || (item.category == PAI_ZIHAI && item.no == 7)) ? t + 1 : t }
          for mentsu in member.gamestate.nakilist
            doranum += mentsu.pailist.inject(0) { | t,item | (item.id == 4 || (item.category == PAI_ZIHAI && item.no == 7)) ? t + 1 : t}
          end
        end


        if member.gamestate.riichi
          # 裏ドラの判定
          for d in @uradora
            dorahai = d.getDora
            if dorahai == execom.pai
              doranum += 1
            end
            doranum += member.tehai.inject(0) { | t,item | item == dorahai ? t + 1 : t}
            for mentsu in member.gamestate.nakilist
              doranum += mentsu.pailist.inject(0) { | t,item | item == dorahai ? t + 1 : t}
            end
          end
        end

        if doranum > 0
          result.yaku.push("ドラ #{doranum}")
          result.han += doranum
        end
      end
      
      # 点数の再計算
      calc_point(result,member.gamestate,self)

      @event.result = result
      
      @renchan = member.gamestate.oya
      
      # ロンアガリ
      @members[@turn].point -= result.score + 300 * @tsumibou
      member.point += result.score + 300 * @tsumibou + @riichibou * 1000
      
      $logfile.write("<#{TAG_RESULT}>")
      $logfile.write("<#{TAG_YAKU}>")
      
      for yakustr in result.yaku
        $logfile.write("#{yakustr} ")
      end
      
      $logfile.write("</#{TAG_YAKU}>")
      
      $logfile.write("<ronagari />")
      
      $logfile.write("<#{TAG_HAN}>#{result.han}</#{TAG_HAN}>")
      $logfile.write("<#{TAG_FU}>#{result.fu}</#{TAG_FU}>")
      $logfile.write("<#{TAG_MANGAN}>#{result.mangan}</#{TAG_MANGAN}>")
      
      $logfile.write("<#{TAG_DORA}>")
      
      for d in @dora
        dorahai = d.getDora
        $logfile.write("<#{TAG_PAI}><num>#{dorahai.getNum}</num><id>#{dorahai.id}</id></#{TAG_PAI}>")
      end
      
      $logfile.write("</#{TAG_DORA}>")
      
      $logfile.write("<#{TAG_URADORA}>")
      
      for d in @uradora
        dorahai = d.getDora
        $logfile.write("<#{TAG_PAI}><num>#{dorahai.getNum}</num><id>#{dorahai.id}</id></#{TAG_PAI}>")
      end
      
      $logfile.write("</#{TAG_URADORA}>")
      
      $logfile.write("<#{TAG_PLAYER}><#{TAG_ID}>#{member.player.id}</#{TAG_ID}><#{TAG_NAME}>#{member.player.name}</#{TAG_NAME}><#{TAG_POINT}>#{result.score + 300 * @tsumibou + @riichibou * 1000 - (member.gamestate.riichi ? 1000 : 0)}</#{TAG_POINT}></#{TAG_PLAYER}>")
      $logfile.write("<#{TAG_PLAYER}><#{TAG_ID}>#{@members[@turn].player.id}</#{TAG_ID}><#{TAG_NAME}>#{@members[@turn].player.name}</#{TAG_NAME}><#{TAG_POINT}>#{-(result.score + 300 * @tsumibou) - (@members[@turn].gamestate.riichi ? 1000 : 0)}</#{TAG_POINT}></#{TAG_PLAYER}>")
      
      for i in 0 .. 3
        if i != @turn && @members[i] != member
          $logfile.write("<#{TAG_PLAYER}><#{TAG_ID}>#{@members[i].player.id}</#{TAG_ID}><#{TAG_NAME}>#{@members[i].player.name}</#{TAG_NAME}><#{TAG_POINT}>#{- (@members[i].gamestate.riichi ? 1000 : 0)}</#{TAG_POINT}></#{TAG_PLAYER}>")
        end
      end
      
      $logfile.write("</#{TAG_RESULT}>")
      
      for member in @members
        # 次局へ進行
        com = Command.new
        com.id = 400
        com.player = member.player
        com.type = TYPE_START
        member.commandlist.push(com)
      end
      
      @riichibou = 0
    elsif execom.id == ID_TSUMO
      execom.type = TYPE_TSUMO
      # ツモ
      # 上がりの判定
      
      #ind = @members.index { | item | item.player.id == execom.player.id }
      ind = nil
      for xxx in 0 .. @members.length-1
        if @members[xxx].player.id == execom.player.id
          ind = xxx
          break
        end
      end
      
      member = @members[ind]
      #resind = member.resultlist.index { |item| execom.pai == item.machihai }
      resind = nil
      for xxx in 0 .. member.resultlist.length-1
        if member.resultlist[xxx].machihai == execom.pai
          resind = xxx
          break
        end
      end
      
      result = member.resultlist[resind]
      
      if result.han < 13 || @rule[TAG_AOTENJO] === 1
        # ドラの判定
        doranum = 0
        for d in @dora
          dorahai = d.getDora
          doranum += member.tehai.inject(0) { | t,item | item == dorahai ? t + 1 : t}
          for mentsu in member.gamestate.nakilist
            doranum += mentsu.pailist.inject(0) { | t,item | item == dorahai ? t + 1 : t}
          end
        end

        if @rule[TAG_AKA] === 1
          doranum += member.tehai.inject(0) { | t,item | (item.category != PAI_ZIHAI && item.no == 5 && item.id == 4) ? t + 1 : t }
          for mentsu in member.gamestate.nakilist
            doranum += mentsu.pailist.inject(0) { | t,item | (item.category != PAI_ZIHAI && item.no == 5 && item.id == 4) ? t + 1 : t}
          end
        elsif @rule[TAG_AKA] === 2
          doranum += member.tehai.inject(0) { | t,item | (item.category != PAI_ZIHAI && item.no == 5 && (item.id == 4 || (item.category == PAI_PINZU && item.id == 3))) ? t + 1 : t }
          for mentsu in member.gamestate.nakilist
            doranum += mentsu.pailist.inject(0) { | t,item | (item.category != PAI_ZIHAI && item.no == 5 && (item.id == 4 || (item.category == PAI_PINZU && item.id == 3))) ? t + 1 : t}
          end
        elsif @rule[TAG_AKA] === 3
          doranum += member.tehai.inject(0) { | t,item | (item.id == 4 || (item.category == PAI_ZIHAI && item.no == 7)) ? t + 1 : t }
          for mentsu in member.gamestate.nakilist
            doranum += mentsu.pailist.inject(0) { | t,item | (item.id == 4 || (item.category == PAI_ZIHAI && item.no == 7)) ? t + 1 : t}
          end
        end

        if member.gamestate.riichi
          # 裏ドラの判定
          for d in @uradora
            dorahai = d.getDora
            doranum += member.tehai.inject(0) { | t,item | item == dorahai ? t + 1 : t}
            for mentsu in member.gamestate.nakilist
              doranum += mentsu.pailist.inject(0) { | t,item | item == dorahai ? t + 1 : t}
            end
          end
        end

        if doranum > 0
          result.yaku.push("ドラ #{doranum}")
          result.han += doranum
        end
      end

      # 点数の再計算
      calc_point(result,member.gamestate,self)
      
      @event.result = result
      
      $logfile.write("<#{TAG_RESULT}>")
      $logfile.write("<#{TAG_YAKU}>")
      
      for yakustr in result.yaku
        $logfile.write("#{yakustr} ")
      end
      
      $logfile.write("</#{TAG_YAKU}>")
      
      $logfile.write("<tsumoagari />")
      
      $logfile.write("<#{TAG_HAN}>#{result.han}</#{TAG_HAN}>")
      $logfile.write("<#{TAG_FU}>#{result.fu}</#{TAG_FU}>")
      $logfile.write("<#{TAG_MANGAN}>#{result.mangan}</#{TAG_MANGAN}>")
      
      $logfile.write("<#{TAG_DORA}>")
      
      for d in @dora
        dorahai = d.getDora
        $logfile.write("<#{TAG_PAI}><num>#{dorahai.getNum}</num><id>#{dorahai.id}</id></#{TAG_PAI}>")
      end
      
      $logfile.write("</#{TAG_DORA}>")
      
      $logfile.write("<#{TAG_URADORA}>")
      
      for d in @uradora
        dorahai = d.getDora
        $logfile.write("<#{TAG_PAI}><num>#{dorahai.getNum}</num><id>#{dorahai.id}</id></#{TAG_PAI}>")
      end
      
      $logfile.write("</#{TAG_URADORA}>")
      
      points = 0
      if member.gamestate.oya
        # 親のツモアガリ
        p = result.koscore + 100 * @tsumibou
        for i in 0 .. 3
          if i != @turn
            @members[i].point -= p
            points += p
            $logfile.write("<#{TAG_PLAYER}><#{TAG_ID}>#{@members[i].player.id}</#{TAG_ID}><#{TAG_NAME}>#{@members[i].player.name}</#{TAG_NAME}><#{TAG_POINT}>#{-p - (@members[i].gamestate.riichi ? 1000 : 0)}</#{TAG_POINT}></#{TAG_PLAYER}>")
          end
        end
        
        @renchan = true
      else
        # 子のツモアガリ
        for i in 0 .. 3
          if i != @turn
            if @members[i].gamestate.oya
              p = result.oyascore + 100 * @tsumibou
            else
              p = result.koscore + 100 * @tsumibou
            end
            
            @members[i].point -= p
            points += p
            $logfile.write("<#{TAG_PLAYER}><#{TAG_ID}>#{@members[i].player.id}</#{TAG_ID}><#{TAG_NAME}>#{@members[i].player.name}</#{TAG_NAME}><#{TAG_POINT}>#{-p - (@members[i].gamestate.riichi ? 1000 : 0)}</#{TAG_POINT}></#{TAG_PLAYER}>")
          end
        end
        
        @renchan = false
      end
      
      member.point += points + @riichibou * 1000
      
      $logfile.write("<#{TAG_PLAYER}><#{TAG_ID}>#{member.player.id}</#{TAG_ID}><#{TAG_NAME}>#{member.player.name}</#{TAG_NAME}><#{TAG_POINT}>#{points + @riichibou * 1000 - (member.gamestate.riichi ? 1000 : 0)}</#{TAG_POINT}></#{TAG_PLAYER}>")
      
      $logfile.write("</#{TAG_RESULT}>")
      
      for member in @members
        # 次局へ進行
        com = Command.new
        com.id = 400
        com.player = member.player
        com.type = TYPE_START
        member.commandlist.push(com)
      end
      
      
      @riichibou = 0
    elsif execom.id == ID_TOUHAI
      execom.type = TYPE_TOUHAI
      # 九種九牌倒牌
      # (TODO)
    elsif execom.id >= ID_KAN && execom.id < ID_RIICHI
      # 暗カン、加えカン
      # カン
      for mem in @members
        mem.gamestate.ippatsu = false
      end
      
      member = @members[@turn]
      member.gamestate.kan = true
      
      if execom.type == TYPE_ANKAN
        member.gamestate.nakilist.push(execom.mentsu)
        member.tehai.delete(execom.pai)
      else
        for mentsu in member.gamestate.nakilist
          if mentsu.nakihai == execom.pai
            pai = member.tehai.delete(execom.pai)
            mentsu.pailist.push(pai)
            mentsu.category = MENTSU_MINKAN
            break
          end
        end
      end
      
      member.tehai.sort!
      
      # カンドラ
      @dora.push(@wanpai.pop)
      @uradora.push(@wanpai.pop)
      
      tsumo()
      
    elsif execom.id >= ID_RIICHI && execom.id <= ID_RIICHI + 13
      execom.type = TYPE_RIICHI
      # リーチ
      #ind = @members.index { | item | item.player.id == execom.player.id }
      ind = nil
      for xxx in 0 .. @members.length-1
        if @members[xxx].player.id == execom.player.id
          ind = xxx
          break
        end
      end
      member = @members[ind]
      
      # 打牌
      for i in 0 .. member.tehai.length-1
        pai = member.tehai[i]
        if pai == execom.pai && pai.id == execom.pai.id
#          if i == member.tehai.length-1
#            pai.tsumogiri = true
#          end
          execom.pai.riichi = true
          member.tehai.delete_at(i)
          break
        end
      end
      
      if member.gamestate.count == 0 && member.gamestate.ippatsu
        member.gamestate.daburii = true
      end
      
      member.gamestate.riichi = true
      member.gamestate.ippatsu = true
      member.gamestate.tsumo = false
      member.gamestate.count += 1
      member.point -= 1000
      @riichibou += 1
      member.dahai.push(pai)
      member.tehai.sort!
      
      tim = Time.now.sec * 1000 + Time.now.usec / 1000;
      
      member.resultlist = search_tenpai(member.tehai,self,member.gamestate)
      
      tim = Time.now.sec * 1000 + Time.now.usec / 1000 - tim
      
      $performdump.puts("202 : search_tenpai #{tim} ms")  if $performdump
      
      # 鳴きの判定
      
      naki = [false,false,false,false]
      
      naki_judge(naki,pai)
      ron_judge(naki,pai)
      
      yyy = nil
      for xxx in 0 .. naki.length-1
        if naki[xxx]
          yyy = xxx
          com = Command.new
          com.id = ID_PASS
          com.type = TYPE_PASS
          com.player = member.player
          @members[xxx].commandlist.push(com)
        end
      end
      
      if yyy == nil
        # 鳴ける人がいない場合は次のツモを続ける
        @turn = (@turn + 1) % 4
        tsumo()
      end
      
    elsif execom.id == ID_START
      
      # 次局へ進行
      kouhai = (execom.type == TYPE_KOUHAI)
      $logfile.write("</kyoku>")
      
      if !@renchan && @kyokucount == 7
        # 半荘終了
        # (TODO)
        execom.type = TYPE_END
        $logfile.write("</log>")
      else
        execom.type = TYPE_START
        kyokuStart(@renchan,kouhai)
      end
      
      
    elsif execom.id == ID_AGARIYAME
      # 上がりやめ
      # (TODO)
      execom.type = TYPE_END
    end
    
    
    
    
  end
  
  
end

class Player
  attr_accessor :id,:name,:privateid
  
  def initialize
    @privateid = 0
    @id = 0
    @name = ""
  end
  
  def getElement
    element = CommonXML::Element.create(TAG_PLAYER)
    
    idEle = CommonXML::Element.create(TAG_ID)
    idEle.add_text("#{@id}")
    element.push(idEle)
    
    namEle = CommonXML::Element.create(TAG_NAME)
    namEle.add_text(@name)
    
    element.push(namEle)
    
    return element
  end
end

class Member
  attr_accessor :player,:point,:gamestate,:tehai,:dahai,:commandlist,:mstate,:resultlist,:messagequeue,:agaripass,:tsumohai
  def initialize
    @player = Player.new
    @point = 0
    @gamestate = GameState.new
    @tehai = []
    @dahai = []
    @commandlist = []
    @resultlist = []
    @mstate = MSTATE_NOSYNC
    @messagequeue = []
    @agaripass = false
    @tsumohai = nil
  end
  
  def getElement(id,pid,mode = STATE_MODE_NORMAL,kouhai = false,agari = false,glasspai = false)
    element = CommonXML::Element.create(TAG_MEMBER)
    element.push(@player.getElement)
    sEle = CommonXML::Element.create(TAG_MSTATE)
    sEle.add_text("#{@mstate}")
    element.push(sEle)
    
    pEle = CommonXML::Element.create(TAG_POINT)
    pEle.add_text("#{@point}")
    element.push(pEle)
    
    if mode != STATE_MODE_WAITSYNC
      if mode == STATE_MODE_DEBUG || (id == @player.id && pid == @player.privateid)
        if @tsumohai
          tsumoEle = CommonXML::Element.create(TAG_TSUMOHAI)
          
          tsumoEle.push(@tsumohai.getElement)
          
          element.push(tsumoEle)
        end
      else
        if @tsumohai
          tsumoEle = CommonXML::Element.create(TAG_TSUMOHAI)
          
          if glasspai
            if @tsumohai.id != 4
              tsumoEle.push(@tsumohai.getElement)
            else
              tsumoEle.push(Pai::NULL.getElement)
            end
          end
          
          element.push(tsumoEle)
        end
      end
      
      if mode == STATE_MODE_DEBUG || (id == @player.id && pid == @player.privateid)
        if mode != STATE_MODE_UPDATE
          tehaiEle = CommonXML::Element.create(TAG_TEHAI)

          for te in @tehai
            tehaiEle.push(te.getElement)
          end

          element.push(tehaiEle)
        end

        comList = CommonXML::Element.create(TAG_COMMANDLIST)
        for command in @commandlist
          comList.push(command.getElement)
        end

        element.push(comList)

        if !@resultlist.empty?
          resultEle = CommonXML::Element.create(TAG_RESULTLIST)
          for result in @resultlist
            resultEle.push(result.getElement)
          end

          element.push(resultEle)
        end
        
        
      elsif agari || (kouhai && !@resultlist.empty?)
        tehaiEle = CommonXML::Element.create(TAG_TEHAI)

        for te in @tehai
          tehaiEle.push(te.getElement)
        end

        element.push(tehaiEle)
      elsif glasspai && mode != STATE_MODE_UPDATE
        tehaiEle = CommonXML::Element.create(TAG_TEHAI)
        hiddenpai = 0
        if @gamestate.tsumo
          pai = @tehai.pop
        end
        
        for te in @tehai
          if te.id == 4
            hiddenpai += 1
          else
            tehaiEle.push(te.getElement)
          end
        end
        
        i = 0
        
        while i < hiddenpai
          tehaiEle.push(Pai::NULL.getElement)
          i += 1
        end
        
        if @gamestate.tsumo
          if id == @player.privateid || pai.id != 4
            tehaiEle.push(pai.getElement)
          else
            tehaiEle.push(Pai::NULL.getElement)
          end
          
          @tehai.push(pai)
        end
        
        
        element.push(tehaiEle)
      end
      

      if mode != STATE_MODE_UPDATE
        element.push(@gamestate.getElement)
        dahaiEle = CommonXML::Element.create(TAG_DAHAI)

        for da in @dahai
          dahaiEle.push(da.getElement)
        end

        element.push(dahaiEle)
      end
    end
    
    return element
  end
end



#/* あがったときの状況*/
class GameState
  attr_accessor :zikaze,:count,:ippatsu,:riichcount,:endpai,:kan,:tsumo,:riichi,:oya,:agarihai,:naki,:membernaki,:nakilist,:daburii
  def initialize
    @zikaze = 0;		#/* 自風*			/
    @count = 0;		#/* 打順*			/
    @ippatsu = true;		#/* 一発*		/
    @endpai = false;		#/* 終わりの牌*		/
    @kan = false;			#/* 直前にカン*		/
    @tsumo = false;		#/* 直前にツモ*		/
    @riichi = false;		#/* リーチしている*		/
    @oya = false;			#/* 親かどうか*		/
    @agarihai = Pai::NULL;		#/* 上がった牌*		/
    @naki = false;		#/* ないたかどうか*		/
    @nakilist = [];   # 鳴いた面子のリスト
    @membernaki = false;		#/* 他の人がないたか*	/
    @daburii = false; # ダブルリーチ
  end
  
  def getElement
    element = CommonXML::Element.create(TAG_GAMESTATE)
    
    kaze = CommonXML::Element.create(TAG_ZIKAZE)
    kaze.add_text("#{@zikaze}")
    element.push(kaze)
    
    couEle = CommonXML::Element.create(TAG_COUNT)
    couEle.add_text("#{@count}")
    element.push(couEle)
    
    riicou = CommonXML::Element.create(TAG_IPPATSU)
    riicou.add_text("#{@ippatsu}")
    element.push(riicou)
    
    endEle = CommonXML::Element.create(TAG_ENDPAI)
    endEle.add_text("#{@endpai}")
    element.push(endEle)
    
    kanEle = CommonXML::Element.create(TAG_KAN)
    kanEle.add_text("#{@kan}")
    element.push(kanEle)
    
    tsumoEle = CommonXML::Element.create(TAG_TSUMO)
    tsumoEle.add_text("#{@tsumo}")
    element.push(tsumoEle)
    
    riichiEle = CommonXML::Element.create(TAG_RIICHI)
    riichiEle.add_text("#{@riichi}")
    element.push(riichiEle)
    
    oyaEle = CommonXML::Element.create(TAG_OYA)
    oyaEle.add_text("#{@oya}")
    element.push(oyaEle)
    
    agariEle = CommonXML::Element.create(TAG_AGARIHAI)
    agariEle.push(@agarihai.getElement)
    element.push(agariEle)
    
    nakiEle = CommonXML::Element.create(TAG_NAKI)
    nakiEle.add_text("#{@naki}")
    element.push(nakiEle)
    
    nakiEle = CommonXML::Element.create(TAG_NAKILIST)
    for nakim in @nakilist
      nakiEle.push(nakim.getElement)
    end
    element.push(nakiEle)
    
    mnakiEle = CommonXML::Element.create(TAG_MEMBERNAKI)
    mnakiEle.add_text("#{@membernaki}")
    element.push(mnakiEle)
    
    return element
  end
  
  
end

#/* 集計*/
class PaiCountItem
  attr_accessor :pai,:count,:startpos
  def initialize
    @pai = Pai.new;
    @count = 0;
    @startpos = 0;
  end
end

class Mentsu
  attr_accessor :pailist,:category
  def initialize
    @pailist = []
    @category = 0
  end
  
  def getElement
    element = CommonXML::Element.create(TAG_MENTSU)
    
    for pai in @pailist
      element.push(pai.getElement)
    end
    
    cateEle = CommonXML::Element.create(TAG_CATEGORY)
    
    cateEle.add_text("#{@category}")
    
    element.push(cateEle)
    
    return element
  end
  
  
  def ==(other)
    if @category == other.category
      for i in 0 .. @pailist.length - 1
        if !(@pailist[i] == other.pailist[i])
          return false
        end
      end
      return true
    else
      return false
    end
  end
  
  def <=>(other)
    return @pailist[0] <=> other.pailist[0]
  end
  
  def >=(other)
    return @pailist[0] >= other.pailist[0]
  end
  
  def >(other)
    return @pailist[0] > other.pailist[0]
  end
  
  def <=(other)
    return @pailist[0] <= other.pailist[0]
  end
  
  def <(other)
    return @pailist[0] < other.pailist[0]
  end
end

class NakiMentsu < Mentsu
  attr_accessor :nakihai,:aite
  
  def initialize(sup = nil)
    if sup
      @pailist = sup.pailist
      @category = sup.category
    else
      @pailist = []
      @category = 0
    end
    
    @nakihai = Pai::NULL
    @aite = 0
  end
  
  def getElement
    element = CommonXML::Element.create(TAG_NAKIMENTSU)
    element.push(super())
    
    nakiEle = CommonXML::Element.create(TAG_NAKIHAI)
    nakiEle.push(@nakihai.getElement)
    element.push(nakiEle)
    
    aiteEle = CommonXML::Element.create(TAG_AITE)
    aiteEle.add_text("#{@aite}")
    
    element.push(aiteEle)
    
    return element
  end
  
end


#/* 結果*/
class ResultItem
  attr_accessor :mentsulist,:yaku,:fu,:han,:mangan,:score,:oyascore,:koscore,:mati,:dora,:machihai
  def initialize
    @mentsulist = []; # 面子のリスト
    @yaku = [];		#/* 役*			/
    @fu = 0;				#/* 符*			/
    @han = 0;				#/* 飜*			/
    @mangan = 0;			#/* 満貫以降*		/
    @score = 0;			#/* 総合得点*		/
    @oyascore = 0;			#/* 子が払う点数*		/
    @koscore = 0;			#/* 親が払う点数*		/
    @mati = 0;			#/* 待ちの種類*		/
    @dora = 0;			#/* ドラの数*		/
    @tsumo = false # ツモアガリかどうか
    @machihai = Pai::NULL; # 待ち牌
  end
  
  def getElement
    element = CommonXML::Element.create(TAG_RESULT)
    
    for mentsu in @mentsulist
      element.push(mentsu.getElement)
    end
    
    for y in @yaku
      yakuEle = CommonXML::Element.create(TAG_YAKU)
      yakuEle.add_text(y)
      element.push(yakuEle)
    end
    
    hanEle = CommonXML::Element.create(TAG_HAN)
    hanEle.add_text("#{@han}")
    element.push(hanEle)
    
    fuEle = CommonXML::Element.create(TAG_FU)
    fuEle.add_text("#{@fu}")
    element.push(fuEle)
    
    manganEle = CommonXML::Element.create(TAG_MANGAN)
    manganEle.add_text("#{@mangan}")
    element.push(manganEle)
    
    scoreEle = CommonXML::Element.create(TAG_SCORE)
    scoreEle.add_text("#{@score}")
    element.push(scoreEle)
    
    oyaEle = CommonXML::Element.create(TAG_OYASCORE)
    oyaEle.add_text("#{@oyascore}")
    element.push(oyaEle)
    
    koEle = CommonXML::Element.create(TAG_KOSCORE)
    koEle.add_text("#{@koscore}")
    element.push(koEle)
    
    matiEle = CommonXML::Element.create(TAG_MACHI)
    matiEle.add_text("#{@mati}")
    element.push(matiEle)
    
    doraEle = CommonXML::Element.create(TAG_DORAKAZU)
    doraEle.add_text("#{@dora}")
    element.push(doraEle)
    
    tsumoEle = CommonXML::Element.create(TAG_TSUMO)
    tsumoEle.add_text("#{@tsumo}")
    element.push(tsumoEle)
    
    machiEle = CommonXML::Element.create(TAG_MACHIHAI)
    machiEle.push(@machihai.getElement)
    element.push(machiEle)
    
    return element
  end
  
  
  def inspect
    return "#{@machihai.getName} #{@yaku} #{@han}飜 #{@fu}符 #{@score}点"
  end
  
  def >(other)
    if @machihai == other.machihai && @score > other.score
      return true
    else
      return false
    end
  end
  
  def >=(other)
    if @machihai == other.machihai && @score >= other.score
      return true
    else
      return false
    end
  end
  
  def <(other)
    if @machihai == other.machihai && @score < other.score
      return true
    else
      return false
    end
  end
  
  def <=(other)
    if @machihai == other.machihai && @score <= other.score
      return true
    else
      return false
    end
  end
  
  # この等号は若干気持ち悪いがしかたない(== かつ [> または <]が存在する)
  def ==(other)
    if @machihai == other.machihai
      return true
    else
      return false
    end
  end
end

# 役を判定するダミークラス
class YakuPlugin
end

def chi_list(paiarray,pai,aite)
  ret = []
  
  pai_minus2 = paiarray.inject([]) { | t,item | (item.category == pai.category && item.no == pai.no - 2) ? t.push(item) : t }
  pai_minus1 = paiarray.inject([]) { | t,item | (item.category == pai.category && item.no == pai.no - 1) ? t.push(item) : t }
  pai_plus1 = paiarray.inject([]) { | t,item | (item.category == pai.category && item.no == pai.no + 1) ? t.push(item) : t }
  pai_plus2 = paiarray.inject([]) { | t,item | (item.category == pai.category && item.no == pai.no + 2) ? t.push(item) : t }
  
  for minus2 in pai_minus2
    for minus1 in pai_minus1
      mentsu = NakiMentsu.new
      mentsu.pailist.push(minus2)
      mentsu.pailist.push(minus1)
      mentsu.pailist.push(pai)
      mentsu.category = MENTSU_SYUNTSU
      mentsu.nakihai = pai
      mentsu.aite = aite
      ret.push(mentsu)
    end
  end
  
  for minus1 in pai_minus1
    for plus1 in pai_plus1
      mentsu = NakiMentsu.new
      mentsu.pailist.push(minus1)
      mentsu.pailist.push(pai)
      mentsu.pailist.push(plus1)
      mentsu.category = MENTSU_SYUNTSU
      mentsu.nakihai = pai
      mentsu.aite = aite
      ret.push(mentsu)
    end
  end
  
  for plus1 in pai_plus1
    for plus2 in pai_plus2
      mentsu = NakiMentsu.new
      mentsu.pailist.push(pai)
      mentsu.pailist.push(plus1)
      mentsu.pailist.push(plus2)
      mentsu.category = MENTSU_SYUNTSU
      mentsu.nakihai = pai
      mentsu.aite = aite
      ret.push(mentsu)
    end
  end
  
  return ret
end

def ponkan_list(paiarray,pai,aite)
  ret = []
  
  pai_equal = paiarray.inject([]) { | t,item | (item == pai) ? t.push(item) : t }
  
  if pai_equal.length == 3
    mentsu = NakiMentsu.new
    mentsu.pailist.push(pai_equal[0])
    mentsu.pailist.push(pai_equal[1])
    mentsu.pailist.push(pai)
    mentsu.category = MENTSU_KOUTSU
    mentsu.nakihai = pai
    mentsu.aite = aite
    ret.push(mentsu)
    mentsu = NakiMentsu.new
    mentsu.pailist.push(pai_equal[0])
    mentsu.pailist.push(pai_equal[2])
    mentsu.pailist.push(pai)
    mentsu.category = MENTSU_KOUTSU
    mentsu.nakihai = pai
    mentsu.aite = aite
    ret.push(mentsu)
    mentsu = NakiMentsu.new
    mentsu.pailist.push(pai_equal[1])
    mentsu.pailist.push(pai_equal[2])
    mentsu.pailist.push(pai)
    mentsu.category = MENTSU_KOUTSU
    mentsu.nakihai = pai
    mentsu.aite = aite
    ret.push(mentsu)
    mentsu = NakiMentsu.new
    mentsu.pailist.push(pai_equal[0])
    mentsu.pailist.push(pai_equal[1])
    mentsu.pailist.push(pai_equal[2])
    mentsu.pailist.push(pai)
    mentsu.category = MENTSU_MINKAN
    mentsu.nakihai = pai
    mentsu.aite = aite
    ret.push(mentsu)
  elsif pai_equal.length == 2
    mentsu = NakiMentsu.new
    mentsu.pailist.push(pai_equal[0])
    mentsu.pailist.push(pai_equal[1])
    mentsu.pailist.push(pai)
    mentsu.category = MENTSU_KOUTSU
    mentsu.nakihai = pai
    mentsu.aite = aite
    ret.push(mentsu)
  end
  
  return ret
end


def search_tenpai(paiarray,taku,gamestate)
  agarihai = [];
  paicountlist = [];
  atamaque = [];
  mentsu_stack = [];
  mentsu = [];
  machilist = OptimizedSet.new
  
  # 国士無双の判定
  kokusi = [0,0,0,0,0,0,0,0,0,0,0,0,0]
  koku2 = -1
  koku0 = -1
  musou = true
  
  for pai in paiarray
    if pai.category == PAI_ZIHAI
      kokusi[pai.no + 5] += 1
    else
      if pai.no == 1
        kokusi[(pai.category-1)*2] += 1
      elsif pai.no == 9
        kokusi[(pai.category-1)*2+1] += 1
      else
        break
      end
      
    end
    
  end
  
  for xxx in 0 .. kokusi.length-1
    if kokusi[xxx] == 2
      if koku2 == -1
        koku2 = xxx
      else
        musou = false
        break
      end
    elsif kokusi[xxx] == 0
      if koku0 == -1
        koku0 = xxx
      else
        musou = false
        break
      end
    elsif kokusi[xxx] != 1
      musou = false
      break
    end
  end
  
  if musou
    if koku2 >= 0 && koku0 >= 0
      pai = Pai.new
      pai.category = (koku0 / 2).floor + 1
      if pai.category >= PAI_ZIHAI
        pai.category = PAI_ZIHAI
        pai.no = koku0 - 5
      else
        if koku0 % 2 == 0
          pai.no = 1
        else
          pai.no = 9
        end
      end
      
      res = ResultItem.new
      res.yaku.push("国士無双")
      res.han = 13
      res.fu = 0
      res.mati = MACHI_TANKI
      res.machihai = pai
      
      calc_point(res,gamestate,self)
      
      machilist.push(res)
    else
      # 国士１３面待ち
      for xxx in 0 .. 12
        pai = Pai.new
        pai.category = (xxx / 2).floor + 1
        if pai.category >= PAI_ZIHAI
          pai.category = PAI_ZIHAI
          pai.no = xxx - 5
        else
          if xxx % 2 == 0
            pai.no = 1
          else
            pai.no = 9
          end
        end
        
        res = ResultItem.new
        res.yaku.push("国士無双")
        res.han = 13
        res.fu = 0
        res.mati = MACHI_TANKI
        res.machihai = pai
        
        calc_point(res,gamestate,self)
        
        machilist.push(res)
      end
      
    end
    
    return machilist
    
  end
  
  
  #/* 十三不塔 国士無双かどうかを調べる*/ (TODO)
  #if (search_siisan(paicountlist)) 
    #/* そうなら設定して関数を抜ける*/
  #  return make_resultitem(paiarray, mentsu, gamestate);
  #end
  
  # 七対子の判定
  if !gamestate.naki
    for i in 0 .. paiarray.length-1
      mentsu[i] = UKIHAI
    end
    
    atamacount = 0
    i = 0;
    while i < paiarray.length - 1
      if (paiarray[i] == paiarray[i + 1]) && !(i < paiarray.length - 2 && (paiarray[i] == paiarray[i+2]))
        mentsu[i] = ATAMA
        mentsu[i + 1] = ATAMA
        atamacount+=1
        i+=1
      end
      i+=1;
    end
    
    if atamacount == 6
      # 七対子単騎待ち
      setpaicount(paicountlist, paiarray,mentsu);
      result = make_result(paiarray,mentsu,taku,gamestate,paicountlist[0].pai,MACHI_TANKI)
      machilist.set(result)
    end
    
  end
  
  #/* 頭の候補を得る*/
  atamaque_count = 0;
  notanki = 0
  i = 0;
  if paiarray.length >= 4
    while i < paiarray.length - 1
      if (paiarray[i] == paiarray[i + 1])
        
        atamaque[atamaque_count] = i;
        atamaque_count+=1
        
        # 孤立した対子のチェック
        
        # 順子や刻子とからまないかチェック
        if i == 0
          if (paiarray[i+1].category != paiarray[i+2].category) ||
            (paiarray[i].category == PAI_ZIHAI && paiarray[i+1].no != paiarray[i+2].no) ||
            (paiarray[i+1].no + 1 < paiarray[i+2].no)
            notanki += 1
            if notanki >= 3
              return machilist
            end
          end
          
        elsif i == (paiarray.length - 2)
          if (paiarray[i-1].category != paiarray[i].category) ||
            (paiarray[i].category == PAI_ZIHAI && paiarray[i-1].no != paiarray[i].no) ||
            (paiarray[i-1].no + 1 < paiarray[i].no)
            notanki += 1
            if notanki >= 3
              return machilist
            end
          end
        else
          if (paiarray[i].category == PAI_ZIHAI && paiarray[i+1].no != paiarray[i+2].no) ||
            (((paiarray[i+1].category != paiarray[i+2].category) || (paiarray[i+1].no + 1 < paiarray[i+2].no)) &&
            ((paiarray[i-1].category != paiarray[i].category) || (paiarray[i-1].no + 1 < paiarray[i].no)))
            notanki += 1
            if notanki >= 3
              return machilist
            end
          end
        end
        
        i+=1;
        
      end
      i+=1;
    end
  end
  
  search_count = 0
  #/* 頭を仮定して刻子 順子などの面子の状況を見る (両面、カンチャン、ペンチャン、シャンポン待ち)*/
  for i in 0 .. atamaque_count-1
    atamapos = atamaque[i];
    atama = paiarray[atamapos]
    #/* 探索を開始する面子の初期状態を設定*/
    result = setstartmentsu(paiarray,mentsu, gamestate, atamapos);
    if (result != 0)
      return machilist;
    end
    
    mentsu_stack.push(mentsu)
    #/* 探索*/
    begin
      mentsu = mentsu_stack.pop
      tmplist = []
      setpaicount(paicountlist, paiarray,mentsu);
      if ((machi = istenpai(paicountlist,tmplist,atama)) == 0)
        search_count += 1
        mentsu2 = Marshal.load(Marshal.dump(mentsu));
        flag = setkoutsu(paicountlist, mentsu2);
        if (flag)
          #/* 次に備えて待避しておいた内容をコピー*/
          mentsu_stack.push(mentsu2)
        end
        
        mentsu2 = Marshal.load(Marshal.dump(mentsu));
        flag = setkoutsul(paicountlist, mentsu2);
        if (flag)
          #/* 次に備えて待避しておいた内容をコピー*/
          mentsu_stack.push(mentsu2)
        end
        
        mentsu2 = Marshal.load(Marshal.dump(mentsu));
        flag = setsyuntsu(paicountlist,mentsu2);
        if (flag) != 0
          mentsu_stack.push(mentsu2)
        end
        
        if flag != 1
          flag = setsyuntsul(paicountlist, mentsu);
          if (flag)
            #/* 次に備えて待避しておいた内容をコピー*/
            mentsu_stack.push(mentsu)
          end
        end
      else
        for tmp in tmplist
          result = make_result(paiarray,mentsu,taku,gamestate,tmp,machi)
          machilist.set(result)
        end
      end
      
    end while(mentsu_stack.length >= 1);
  end
  
  if notanki == 0
    for i in 0 .. paiarray.length-1
      mentsu[i] = UKIHAI
    end

    mentsu_stack.push(mentsu)
    # 頭待ちの検出
    #/* 探索*/
    begin
      mentsu = mentsu_stack.pop
      tmplist = []
      setpaicount(paicountlist, paiarray,mentsu);
      if ((machi = isatamamachi(paicountlist,tmplist)) == 0)
        search_count += 1
        mentsu2 = Marshal.load(Marshal.dump(mentsu));
        flag = setkoutsu(paicountlist, mentsu2);
        if (flag)
          #/* 次に備えて待避しておいた内容をコピー*/
          mentsu_stack.push(mentsu2)
        end

        mentsu2 = Marshal.load(Marshal.dump(mentsu));
        flag = setkoutsul(paicountlist, mentsu2);
        if (flag)
          #/* 次に備えて待避しておいた内容をコピー*/
          mentsu_stack.push(mentsu2)
        end

        mentsu2 = Marshal.load(Marshal.dump(mentsu));
        flag = setsyuntsu(paicountlist,mentsu2);
        if (flag) != 0
          mentsu_stack.push(mentsu2)
        end
        
        if flag != 1
          flag = setsyuntsul(paicountlist, mentsu);
          if (flag)
            #/* 次に備えて待避しておいた内容をコピー*/
            mentsu_stack.push(mentsu)
          end
        end
        
      else
        for tmp in tmplist
          result = make_result(paiarray,mentsu,taku,gamestate,tmp,machi)
          machilist.set(result)
        end
      end
    end while(mentsu_stack.length >= 1);
  end

  $performdump.puts("302 : search_count #{search_count}")  if $performdump
  
  return machilist;
end

def isatamamachi(paicountlist,machilist)
  if paicountlist.length == 1 && paicountlist[0].count == 1
    machilist.push(paicountlist[0].pai);
    return MACHI_TANKI;
  else
    return 0
  end
end

def istenpai(paicountlist,machilist,atamahai)
  if paicountlist.length == 1
    if paicountlist[0].count == 2
      #シャンポン待ち
      machilist.push(atamahai);
      machilist.push(paicountlist[0].pai);
      return MACHI_SHANPON;
    else
      return 0;
    end
  elsif paicountlist.length == 2
    if paicountlist[0].count == 1 && paicountlist[1].count == 1 && paicountlist[0].pai.category == paicountlist[1].pai.category &&
      paicountlist[0].pai.category != PAI_ZIHAI
      if paicountlist[0].pai.no + 1 == paicountlist[1].pai.no
        if paicountlist[0].pai.no == 1
          # ペンチャン待ち
          tmp = Pai.new
          tmp.category = paicountlist[0].pai.category
          tmp.no = paicountlist[0].pai.no + 2
          machilist.push(tmp)
          return MACHI_PENCHAN;
        elsif paicountlist[1].pai.no == 9
          # ペンチャン待ち
          tmp = Pai.new
          tmp.category = paicountlist[0].pai.category
          tmp.no = paicountlist[0].pai.no - 1
          machilist.push(tmp)
          return MACHI_PENCHAN;
        else
          # 両面待ち
          tmp = Pai.new
          tmp.category = paicountlist[0].pai.category
          tmp.no = paicountlist[0].pai.no - 1
          machilist.push(tmp)
          tmp = Pai.new
          tmp.category = paicountlist[0].pai.category
          tmp.no = paicountlist[0].pai.no + 2
          machilist.push(tmp)
          return MACHI_RYANMEN;
        end
      elsif paicountlist[0].pai.no + 2 == paicountlist[1].pai.no
        # カンチャン待ち
        tmp = Pai.new
        tmp.category = paicountlist[0].pai.category
        tmp.no = paicountlist[0].pai.no + 1
        machilist.push(tmp)
        return MACHI_KANCHAN;
      else
        return 0;
      end
    else
      return 0;
    end
  else
    return 0;
  end
end




#/* 探索を開始する面子の初期状態を設定*/
def setstartmentsu(paiarray, mentsu,gamestate,atamapos)
  paicountlist = [];
  for i in 0 .. paiarray.length
    paicountlist[i] = PaiCountItem.new;
  end
  
  #/* 頭を設定する*/
  for i in 0 .. paiarray.length-1
    mentsu[i] = UKIHAI;
  end
  
  mentsu[atamapos] = ATAMA;
  mentsu[atamapos+1] = ATAMA;
  
  #/* 各牌の個数を調べる*/
  paicountlistcount = 0;
  pos = 0;
  paicountlist[pos].pai = Pai::NULL;
  for i in 0 .. paiarray.length-1
    #/* 頭は勘定しない*/
    next if ((i == atamapos) || (i == atamapos + 1));
    if ((paicountlist[pos].pai != Pai::NULL) &&
      (paicountlist[pos].pai.category ==
      paiarray[i].category) &&
      (paicountlist[pos].pai.no ==
      paiarray[i].no)) 
      paicountlist[pos].count+=1;
    else 
      paicountlistcount+=1;
      pos+=1 if (paicountlist[pos].pai != Pai::NULL);
      paicountlist[pos].pai = paiarray[i];
      paicountlist[pos].count = 1;
      paicountlist[pos].startpos = i;
    end
  end
  
  paicountlist[pos + 1].pai = Pai::NULL;
  
  

 #/* 探索を始めるために確定しているところを決める*/
 for i in 0 .. paicountlistcount-1
    koutsuflag = iskoutsu(paicountlist[i]);
    syuntsuflag = issyuntsu(paicountlist[i .. -1]);

    #/* 今の位置が刻子で順子でなければ確定*/
    if ((koutsuflag) && (!syuntsuflag)) 
      next if ((i >= 1) && (issyuntsu(paicountlist[(i - 1) .. -1])))
      next if ((i >= 2) && (issyuntsu(paicountlist[(i - 2) .. -1])))
      
      pos = paicountlist[i].startpos;
      mentsu[pos] = KOUTSU;
      mentsu[pos + 1] = KOUTSU;
      mentsu[pos + 2] = KOUTSU;
      next;
    end
    
    if false
    #/* 今の位置が順子でその三つの牌が*/
    #/* 刻子でなければ確定*/
      if ((!koutsuflag) && (syuntsuflag))
        if (i <= paicountlistcount - 3)
          next if (iskoutsu(paicountlist[i + 1]))
          next if (iskoutsu(paicountlist[i + 2]))
          next if (paicountlist[i].count >= 2)
          pos = paicountlist[i].startpos;
          mentsu[pos] = SYUNTSU_START;
          paicountlist[i].count-=1;
          paicountlist[i].startpos+=1;
          pos = paicountlist[i + 1].startpos;
          mentsu[pos] = SYUNTSU_MIDDLE;
          paicountlist[i + 1].count-=1;
          paicountlist[i + 1].startpos+=1;
          pos = paicountlist[i + 2].startpos;
          mentsu[pos] = SYUNTSU_END;
          paicountlist[i + 2].count-=1;
          paicountlist[i + 2].startpos+=1;
          next;
        end
      end
    end
    
  end
  
  return 0;
end

#/* 刻子かどうかを調べる*/
def iskoutsu(paicountlist)
  return (paicountlist.count >= 3) ? true : false;
end



#/* 順子かどうかを調べる（右方向のみ） */
def issyuntsu(paicountlist)
  
  return false if (paicountlist.length < 3)
  for i in [0,1]
    pai = paicountlist[i].pai;
    return false if (pai == Pai::NULL)
    nextpai = paicountlist[i+1].pai;
    return false if (nextpai == Pai::NULL)
    return false if ((pai.category == PAI_ZIHAI) || (nextpai.category == PAI_ZIHAI))
    return false if (pai.category != nextpai.category)
    return false if ((paicountlist[i].count == 0) || (paicountlist[i+1].count == 0))
    return false if (pai.no != nextpai.no - 1)
  end
  
  return true;
end

#/* 順子かどうかを調べる（左方向） */
def issyuntsul(paicountlist)
  return false if (paicountlist.length < 3)
  for i in [-1,-2]
    pai = paicountlist[i].pai;
    return false if (pai == Pai::NULL)
    nextpai = paicountlist[i-1].pai;
    return false if (nextpai == Pai::NULL)
    return false if ((pai.category == PAI_ZIHAI) || (nextpai.category == PAI_ZIHAI))
    return false if (pai.category != nextpai.category)
    return false if ((paicountlist[i].count == 0) || (paicountlist[i-1].count == 0))
    return false if (pai.no != nextpai.no + 1)
  end
  
  return true;
end


#/* 牌の数を種類ごとに数える*/
def setpaicount(paicountlist,paiarray,mentsu)
  paicountlist.clear
  cur = Pai::NULL
  curlist = PaiCountItem.new
  for i in 0 .. paiarray.length-1
    #/* 浮き牌以外は集計に入れない*/
    next if (mentsu[i] != UKIHAI)

    if (cur == paiarray[i])
      curlist.count+=1
    else
      curlist = PaiCountItem.new
      paicountlist.push(curlist)
      cur = paiarray[i]
      curlist.pai = paiarray[i];
      curlist.count = 1;
      curlist.startpos = i;
    end
  end
  return paicountlist.length;
end

#/* 浮き牌があるかどうかを返す*/
def isukihai(paicountlist)
 return (paicountlist.length == 0) ? false : true;
end


#/* 刻子をセットする*/
def setkoutsu(paicountlist,mentsu)
  if (iskoutsu(paicountlist[0]))
    pos = paicountlist[0].startpos;
    mentsu[pos] = KOUTSU;
    mentsu[pos + 1] = KOUTSU;
    mentsu[pos + 2] = KOUTSU;
    return true;
 end
 return false;
end

#/* 刻子をセットする(左方向)*/
def setkoutsul(paicountlist,mentsu)
  if (iskoutsu(paicountlist[-1]))
    pos = paicountlist[-1].startpos;
    mentsu[pos] = KOUTSU;
    mentsu[pos + 1] = KOUTSU;
    mentsu[pos + 2] = KOUTSU;
    return true;
 end
 return false;
end

#/* 順子をセットする*/
def setsyuntsu(paicountlist,mentsu)
  if (issyuntsu(paicountlist))
    pos = paicountlist[0].startpos;
    mentsu[pos] = SYUNTSU_START;
    pos = paicountlist[1].startpos;
    mentsu[pos] = SYUNTSU_MIDDLE;
    pos = paicountlist[2].startpos;
    mentsu[pos] = SYUNTSU_END;
    
    if paicountlist.length < 4 || paicountlist[2].pai.no + 1 != paicountlist[3].pai.no
      # 右方向と左方向は同一である
      return 1
    else
      return 2
    end
    
 end
 return 0
end

#/* 順子をセットする(左方向)*/
def setsyuntsul(paicountlist,mentsu)
  if (issyuntsul(paicountlist))
    pos = paicountlist[-3].startpos;
    mentsu[pos] = SYUNTSU_START;
    pos = paicountlist[-2].startpos;
    mentsu[pos] = SYUNTSU_MIDDLE;
    pos = paicountlist[-1].startpos;
    mentsu[pos] = SYUNTSU_END;
    return true;
 end
 return false;
end

def calc_point(item,gamestate,taku)
  if item.han >= 5 && !(taku.rule[TAG_AOTENJO] === 1)
    # 満貫以上の計算
    if item.han >= 13
      # 数え役満
      item.mangan = MANGAN_YAKUMAN
      if gamestate.oya
        item.score = 48000 * (item.han/13).floor
        item.koscore = 16000 * (item.han/13).floor
      else
        item.score = 32000 * (item.han/13).floor
        item.oyascore = 16000 * (item.han/13).floor
        item.koscore = 8000 * (item.han/13).floor
      end
    elsif item.han >= 11
      # 三倍満
      item.mangan = MANGAN_SANBAIMAN
      if gamestate.oya
        item.score = 36000
        item.koscore = 12000
      else
        item.score = 24000
        item.oyascore = 12000
        item.koscore = 6000
      end
    elsif item.han >= 8
      # 倍満
      item.mangan = MANGAN_BAIMAN
      if gamestate.oya
        item.score = 24000
        item.koscore = 8000
      else
        item.score = 16000
        item.oyascore = 8000
        item.koscore = 4000
      end
    elsif item.han >= 6
      # 跳満
      item.mangan = MANGAN_HANEMAN
      if gamestate.oya
        item.score = 18000
        item.koscore = 6000
      else
        item.score = 12000
        item.oyascore = 6000
        item.koscore = 3000
      end
    else
      # 満貫
      item.mangan = MANGAN_MANGAN
      if gamestate.oya
        item.score = 12000
        item.koscore = 4000
      else
        item.score = 8000
        item.oyascore = 4000
        item.koscore = 2000
      end
    end
  else
    
    # 符と翻数から点数を計算
    ten = item.fu
    
    for i in 1 .. item.han + 2
      ten *= 2
    end
    
    if gamestate.oya
      # 親のあがり点
      if ten * 6 >= 12000 && !(taku.rule[TAG_AOTENJO] === 1)
        item.mangan = MANGAN_MANGAN
        item.score = 12000
        item.koscore = 4000
      else
        item.score = (ten * 6 / 100.0).ceil * 100
        item.koscore = (ten * 2 / 100.0).ceil * 100
      end
      
    else
      # 子のあがり点
      if ten * 4 >= 8000 && !(taku.rule[TAG_AOTENJO] === 1)
        item.mangan = MANGAN_MANGAN
        item.score = 8000
        item.oyascore = 4000
        item.koscore = 2000
      else
        item.score = (ten * 4 / 100.0).ceil * 100
        item.oyascore = (ten * 2 / 100.0).ceil * 100
        item.koscore = (ten / 100.0).ceil * 100
      end
    end
  end
end

$plugin_obj = []

def make_yaku(item,taku,gamestate,machi)
  item.han = 0
  item.yaku = []
  
  if $plugin_obj.length == 0
    Dir::glob( "plugin/*.rb" ).sort.each do |file|
      obj = YakuPlugin.new
      open( file.untaint) do | src |
        obj.instance_eval( src.read.untaint )
      end
      if obj.hantei(item.mentsulist,taku,gamestate,machi)
        if !(taku.rule[TAG_AOTENJO] === 1) && obj.isYakuman
          if item.han < 13
            item.han = obj.getHan(item.mentsulist,taku,gamestate,machi)
            item.yaku = []
            item.yaku.push(obj.getName)
          else
            item.han += obj.getHan(item.mentsulist,taku,gamestate,machi)
            item.yaku.push(obj.getName)
          end
        elsif taku.rule[TAG_AOTENJO] === 1 || item.han < 13
          item.yaku.push(obj.getName)
          item.han += obj.getHan(item.mentsulist,taku,gamestate,machi)
        end
      end
      $plugin_obj.push(obj)
    end
  else
    for obj in $plugin_obj
      if obj.hantei(item.mentsulist,taku,gamestate,machi)
        if !(taku.rule[TAG_AOTENJO] === 1) && obj.isYakuman
          if item.han < 13
            item.han = obj.getHan(item.mentsulist,taku,gamestate,machi)
            item.yaku = []
            item.yaku.push(obj.getName)
          else
            item.han += obj.getHan(item.mentsulist,taku,gamestate,machi)
            item.yaku.push(obj.getName)
          end
        elsif taku.rule[TAG_AOTENJO] === 1 || item.han < 13
          item.yaku.push(obj.getName)
          item.han += obj.getHan(item.mentsulist,taku,gamestate,machi)
        end
      end
    end
  end
  
end


def make_result(paiarray,mentsu,taku,gamestate,agarihai,machi)
  item = ResultItem.new
  mentsu_save = Marshal.load(Marshal.dump(mentsu))
  machi_mentsu = Mentsu.new
  
  item.machihai = agarihai
  item.mati = machi
  
  #面子を完成させる
  
  case machi
  when MACHI_KANCHAN,MACHI_PENCHAN,MACHI_RYANMEN
    machi_mentsu.category = MENTSU_SYUNTSU
  when MACHI_TANKI
    machi_mentsu.category = MENTSU_TOITSU
  else
    machi_mentsu.category = MENTSU_KOUTSU
  end
  
  machi_mentsu.pailist.push(agarihai)
  
  pos = 0
  while pos < mentsu_save.length
    case mentsu_save[pos]
    when UKIHAI
      machi_mentsu.pailist.push(paiarray[pos])
      mentsu_save[pos] = -1
    when KOUTSU
      tmp = Mentsu.new
      mentsu_save[pos] = -1
      tmp.pailist.push(paiarray[pos])
      mentsu_save[pos+1] = -1
      tmp.pailist.push(paiarray[pos+1])
      mentsu_save[pos+2] = -1
      tmp.pailist.push(paiarray[pos+2])
      pos += 3
      tmp.category = MENTSU_KOUTSU
      item.mentsulist.push(tmp)
    when ATAMA
      tmp = Mentsu.new
      mentsu_save[pos] = -1
      tmp.pailist.push(paiarray[pos])
      mentsu_save[pos+1] = -1
      tmp.pailist.push(paiarray[pos+1])
      pos += 2
      tmp.category = MENTSU_TOITSU
      item.mentsulist.push(tmp)
    when SYUNTSU_START
      tmp = Mentsu.new
      mentsu_save[pos] = -1
      tmp.pailist.push(paiarray[pos])
      
      for i in pos+1 .. mentsu_save.length - 1
        if mentsu_save[i] == SYUNTSU_MIDDLE && paiarray[i].no == paiarray[pos].no + 1
          mentsu_save[i] = -1
          tmp.pailist.push(paiarray[i])
          break
        end
      end
      
      for j in i+1 .. mentsu_save.length - 1
        if mentsu_save[j] == SYUNTSU_END && paiarray[j].no == paiarray[pos].no + 2
          mentsu_save[j] = -1
          tmp.pailist.push(paiarray[j])
          break
        end
      end
      
      tmp.category = MENTSU_SYUNTSU
      item.mentsulist.push(tmp)
    else
      pos += 1
    end
  end
  
  machi_mentsu.pailist.sort!
  
  item.mentsulist.push(machi_mentsu)
  
  item.mentsulist.sort!
  
  make_yaku(item,taku,gamestate,machi)
  
  if item.yaku.index("七対子")
    item.fu = 25;
  else
    # 符の計算
    item.fu = 20; # 副底
    
    # 面前ロン
    if !gamestate.naki && !gamestate.tsumo
      item.fu += 10
    end
    
    if !(item.yaku.index("平和"))
      
      # ツモ和了
      if gamestate.tsumo
        item.fu += 2
      end
      
      # 待ち加符
      if machi == MACHI_KANCHAN || machi == MACHI_PENCHAN || machi == MACHI_TANKI
        item.fu += 2
      end
      
      # 面子構成に関する符
      for mentsu in item.mentsulist
        if mentsu.category == MENTSU_KOUTSU
          if mentsu.pailist[0].isYaoTyu
            item.fu += 8
          else
            item.fu += 4
          end
        elsif mentsu.category == MENTSU_TOITSU
          item.fu += mentsu.pailist[0].numHanpai(taku,gamestate) * 2
        end
      end
      
      for mentsu in gamestate.nakilist
        if mentsu.category == MENTSU_KOUTSU
          if mentsu.pailist[0].isYaoTyu
            item.fu += 4
          else
            item.fu += 2
          end
        elsif mentsu.category == MENTSU_MINKAN
          if mentsu.pailist[0].isYaoTyu
            item.fu += 16
          else
            item.fu += 8
          end
        elsif mentsu.category == MENTSU_ANKAN
          if mentsu.pailist[0].isYaoTyu
            item.fu += 32
          else
            item.fu += 16
          end
        end
        
      end
      
      # 切り上げ
      item.fu = (item.fu / 10.0).ceil * 10;
      
      if item.fu == 20
        # 特別加符
        item.fu = 30
      end
    end
  end
  
  calc_point(item,gamestate,taku)
  
  return item
end

