OSDN Git Service

* [shogi-server]
authordaigo <beatles@users.sourceforge.jp>
Sat, 17 Jul 2010 11:31:35 +0000 (20:31 +0900)
committerDaigo Moriwaki <daigo@debian.org>
Sat, 17 Jul 2010 11:31:35 +0000 (20:31 +0900)
  - shogi_server.rb, shogi_server/board.rb, shogi_server/move.rb
    - Refactoring: Board can now move_to() and move_back() a move
      instread of deep_copy().

changelog
shogi_server.rb
shogi_server/board.rb
shogi_server/move.rb [new file with mode: 0644]
test/TC_ALL.rb
test/TC_board.rb
test/TC_move.rb [new file with mode: 0644]

index 18fb40c..afa2137 100644 (file)
--- a/changelog
+++ b/changelog
@@ -1,3 +1,10 @@
+2010-07-17  Daigo Moriwaki <daigo at debian dot org>
+
+       * [shogi-server]
+         - shogi_server.rb, shogi_server/board.rb, shogi_server/move.rb
+           - Refactoring: Board can now move_to() and move_back() a move
+             instread of deep_copy().
+
 2010-07-11  Daigo Moriwaki <daigo at debian dot org>
 
        * [shogi-server]
index 587798a..05bc2d5 100644 (file)
@@ -33,6 +33,7 @@ require 'shogi_server/board'
 require 'shogi_server/game'
 require 'shogi_server/league'
 require 'shogi_server/login'
+require 'shogi_server/move'
 require 'shogi_server/piece'
 require 'shogi_server/player'
 require 'shogi_server/timeout_queue'
index 3778bd7..d7f9466 100644 (file)
@@ -17,6 +17,8 @@
 ## along with this program; if not, write to the Free Software
 ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
+require 'shogi_server/move'
+
 module ShogiServer # for a namespace
 
 class WrongMoves < ArgumentError; end
@@ -59,6 +61,19 @@ class Board
   # moves.
   attr_reader :initial_moves
 
+  # See if self equals rhs, including a logical board position (i.e.
+  # not see object IDs) and sennichite stuff.
+  #
+  def ==(rhs)
+    return @teban         == rhs.teban &&
+           @move_count    == rhs.move_count &&
+           to_s           == rhs.to_s &&
+           @history       == rhs.history &&
+           @sente_history == rhs.sente_history &&
+           @gote_history  == rhs.gote_history &&
+           @initial_moves == rhs.initial_moves
+  end
+
   def deep_copy
     return Marshal.load(Marshal.dump(self))
   end
@@ -218,14 +233,17 @@ class Board
     return piece
   end
 
-  def move_to(x0, y0, x1, y1, name, sente)
+  # :illegal and :outori do not change this instance (self).
+  #
+  def move_to(move)
+    x0, x1, y0, y1, name, sente = move.x0, move.x1, move.y0, move.y1, move.name, move.sente
     if (sente)
       hands = @sente_hands
     else
       hands = @gote_hands
     end
 
-    if ((x0 == 0) || (y0 == 0))
+    if move.is_drop?
       piece = have_piece?(hands, name)
       return :illegal if (piece == nil || ! piece.move_to?(x1, y1, name))
       piece.move_to(x1, y1)
@@ -233,13 +251,18 @@ class Board
       if (@array[x0][y0] == nil || !@array[x0][y0].move_to?(x1, y1, name))
         return :illegal
       end
-      if (@array[x0][y0].name != name) # promoted ?
+      if (@array[x1][y1] && @array[x1][y1].name == "OU")
+          return :outori
+      end
+
+      # Change the state of this instance (self)
+      if (@array[x0][y0].name != name && !@array[x0][y0].promoted)
+        # the piece is promoting
         @array[x0][y0].promoted = true
+        move.promotion = true
       end
       if (@array[x1][y1]) # capture
-        if (@array[x1][y1].name == "OU")
-          return :outori        # return board update
-        end
+        move.set_captured_piece(@array[x1][y1])
         @array[x1][y1].sente = @array[x0][y0].sente
         @array[x1][y1].move_to(0, 0)
         hands.sort! {|a, b| # TODO refactor. Move to Piece class
@@ -253,6 +276,38 @@ class Board
     return true
   end
 
+  # Get back the previous move, which moved a name piece from [x0,y0] to 
+  # [x1, y1] with or without promotion. If the move captured
+  # a piece, it is captured_piece, which is now in hand. The captured_piece
+  # was promoted or unpromoted.
+  #
+  def move_back(move)
+    if (move.sente)
+      hands = @sente_hands
+    else
+      hands = @gote_hands
+    end
+
+    piece = @array[move.x1][move.y1]
+    if move.is_drop?
+      piece.move_to(0, 0)
+    else
+      piece.move_to(move.x0, move.y0)
+      piece.promoted = false if piece.promoted && move.promotion
+      if move.captured_piece 
+        move.captured_piece.move_to(move.x1, move.y1)
+        move.captured_piece.sente = move.sente ? false : true
+        move.captured_piece.promoted = true if move.captured_piece_promoted 
+      end
+    end
+    
+    @move_count -= 1
+    @teban = @teban ? false : true
+    return true
+  end
+  
+  # def each_reserved_square
+
   def look_for_ou(sente)
     x = 1
     while (x <= 9)
@@ -270,8 +325,11 @@ class Board
     raise "can't find ou"
   end
 
-  # not checkmate, but check. sente is checked.
-  def checkmated?(sente)        # sente is loosing
+  # See if sente is checked (i.e. loosing) or not.
+  # Note that the method name "checkmated?" is wrong. Instead, it should be
+  # "checked?" 
+  #
+  def checkmated?(sente)
     ou = look_for_ou(sente)
     x = 1
     while (x <= 9)
@@ -290,6 +348,8 @@ class Board
     return false
   end
 
+  # See if sente's FU drop checkmates the opponent or not.
+  #
   def uchifuzume?(sente)
     rival_ou = look_for_ou(! sente)   # rival's ou
     if (sente)                  # rival is gote
@@ -317,7 +377,7 @@ class Board
     ## case: rival_ou is moving
     rival_ou.movable_grids.each do |(cand_x, cand_y)|
       tmp_board = deep_copy
-      s = tmp_board.move_to(rival_ou.x, rival_ou.y, cand_x, cand_y, "OU", ! sente)
+      s = tmp_board.move_to(Move.new(rival_ou.x, rival_ou.y, cand_x, cand_y, "OU", ! sente))
       raise "internal error" if (s != true)
       if (! tmp_board.checkmated?(! sente)) # good move
         return false
@@ -345,7 +405,7 @@ class Board
           end
           names.map! do |name|
             tmp_board = deep_copy
-            s = tmp_board.move_to(x, y, fu_x, fu_y, name, ! sente)
+            s = tmp_board.move_to(Move.new(x, y, fu_x, fu_y, name, ! sente))
             if s == :illegal
               s # result
             else
@@ -390,6 +450,18 @@ class Board
     end
   end
 
+  # Deep-copy sennichite stuff, which will later be available to restore.
+  #
+  def dup_sennichite_stuff
+    return [@history.dup, @sente_history.dup, @gote_history.dup]
+  end
+
+  # Restore sennihite stuff.
+  #
+  def restore_sennichite_stuff(history, sente_history, gote_history)
+    @history, @sente_history, @gote_history = history, sente_history, gote_history
+  end
+
   def oute_sennichite?(player)
     return nil unless sennichite?
 
@@ -554,21 +626,35 @@ class Board
       return :illegal           # can't put on existing piece
     end
 
-    tmp_board = deep_copy
-    return :illegal if (tmp_board.move_to(x0, y0, x1, y1, name, sente) == :illegal)
-    return :oute_kaihimore if (tmp_board.checkmated?(sente))
-    tmp_board.update_sennichite(sente)
-    os_result = tmp_board.oute_sennichite?(sente)
-    return os_result if os_result # :oute_sennichite_sente_lose or :oute_sennichite_gote_lose
-    return :sennichite if tmp_board.sennichite?
-
-    if ((x0 == 0) && (y0 == 0) && (name == "FU") && tmp_board.uchifuzume?(sente))
+    move = Move.new(x0, y0, x1, y1, name, sente)
+    result = move_to(move)
+    if (result == :illegal)
+      # self is unchanged
+      return :illegal 
+    end
+    if (checkmated?(sente))
+      move_back(move)
+      return :oute_kaihimore 
+    end
+    if ((x0 == 0) && (y0 == 0) && (name == "FU") && uchifuzume?(sente))
+      move_back(move)
       return :uchifuzume
     end
 
-    move_to(x0, y0, x1, y1, name, sente)
-
+    sennichite_stuff = dup_sennichite_stuff
     update_sennichite(sente)
+    os_result = oute_sennichite?(sente)
+    if os_result # :oute_sennichite_sente_lose or :oute_sennichite_gote_lose
+      move_back(move)
+      restore_sennichite_stuff(*sennichite_stuff)
+      return os_result 
+    end
+    if sennichite?
+      move_back(move)
+      restore_sennichite_stuff(*sennichite_stuff)
+      return :sennichite 
+    end
+
     return :normal
   end
 
diff --git a/shogi_server/move.rb b/shogi_server/move.rb
new file mode 100644 (file)
index 0000000..8354e3c
--- /dev/null
@@ -0,0 +1,48 @@
+## $Id$
+
+## Copyright (C) 2004 NABEYA Kenichi (aka nanami@2ch)
+## Copyright (C) 2007-2008 Daigo Moriwaki (daigo at debian dot org)
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+module ShogiServer # for a namespace
+
+class Move
+  def initialize(x0, y0, x1, y1, name, sente)
+    @x0 = x0
+    @y0 = y0
+    @x1 = x1
+    @y1 = y1
+    @name = name
+    @sente = sente
+    @promotion = false
+    @captured_piece = nil
+    @captured_piece_promoted = false
+  end
+  attr_reader :x0, :y0, :x1, :y1, :name, :sente, 
+              :captured_piece, :captured_piece_promoted
+  attr_accessor :promotion
+
+  def set_captured_piece(piece)
+    @captured_piece = piece
+    @captured_piece_promoted = piece.promoted
+  end
+
+  def is_drop?
+    return (@x0 == 0 || @y0 == 0)
+  end
+end
+
+end # namespace
index 934032c..ff314f0 100644 (file)
@@ -16,6 +16,7 @@ require 'TC_handicapped_boards'
 require 'TC_jishogi_kachi'
 require 'TC_league'
 require 'TC_login'
+require 'TC_move'
 require 'TC_not_sennichite'
 require 'TC_oute_sennichite'
 require 'TC_pairing'
index 6008af5..b9d54fc 100644 (file)
@@ -208,6 +208,255 @@ EOM
 end
 
 
+class TestMoveBack < Test::Unit::TestCase
+  def validate_before_after(board, move)
+    orig = board.to_s
+    orig_teban = board.teban
+    orig_move_count = board.move_count
+
+    assert_equal true, board.move_to(move)
+    assert_equal !orig_teban, board.teban
+    assert_equal (orig_move_count+1), board.move_count
+
+    assert_equal true, board.move_back(move)
+    assert_equal orig_teban, board.teban
+    assert_equal orig_move_count, board.move_count
+    assert_equal orig, board.to_s
+  end
+
+  def test_normal
+    b = ShogiServer::Board.new
+    b.set_from_str(<<EOM)
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
+EOM
+    m = ShogiServer::Move.new(2,7,2,6,"FU",true)
+    validate_before_after(b,m)
+    m = ShogiServer::Move.new(3,3,3,4,"FU",false)
+    validate_before_after(b,m)
+    m = ShogiServer::Move.new(7,7,7,6,"FU",true)
+    validate_before_after(b,m)
+    m = ShogiServer::Move.new(8,3,8,4,"FU",false)
+    validate_before_after(b,m)
+  end
+
+  def test_promote
+    b = ShogiServer::Board.new
+    b.set_from_str(<<EOM)
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU * -FU
+P4 *  *  *  *  *  *  * +FU * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU * +FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
+P+00FU
+EOM
+    m = ShogiServer::Move.new(2,4,2,3,"TO",true)
+    validate_before_after(b,m)
+  end
+
+  def test_unpromote
+    b = ShogiServer::Board.new
+    b.set_from_str(<<EOM)
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU * -FU
+P4 *  *  *  *  *  *  * +FU * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU * +FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
+P+00FU
+EOM
+    m = ShogiServer::Move.new(2,4,2,3,"FU",true)
+    validate_before_after(b,m)
+  end
+
+  def test_promoted
+    b = ShogiServer::Board.new
+    b.set_from_str(<<EOM)
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU+TO-FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU * +FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
+P+00FU
+EOM
+    m = ShogiServer::Move.new(2,3,1,2,"TO",true)
+    assert !m.promotion
+    validate_before_after(b,m)
+    assert !m.promotion
+  end
+
+  def test_capture
+    b = ShogiServer::Board.new
+    b.set_from_str(<<EOM)
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU * -FU
+P4 *  *  *  *  *  *  * -FU * 
+P5 *  *  *  *  *  *  * +FU * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU * +FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
+EOM
+    m = ShogiServer::Move.new(2,5,2,4,"FU",true)
+    validate_before_after(b,m)
+  end
+  def test_capture_white
+    b = ShogiServer::Board.new
+    b.set_from_str(<<EOM)
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU * -FU
+P4 *  *  *  *  *  *  * -FU * 
+P5 *  *  *  *  *  *  * +FU * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU * +FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
+-
+EOM
+    m = ShogiServer::Move.new(2,4,2,5,"FU",false)
+    validate_before_after(b,m)
+  end
+  def test_capture_promote
+    b = ShogiServer::Board.new
+    b.set_from_str(<<EOM)
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
+P4 *  *  *  *  *  *  * +FU * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU * +FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
+EOM
+    m = ShogiServer::Move.new(2,4,2,3,"TO",true)
+    validate_before_after(b,m)
+  end
+
+  def test_capture_promote_white
+    b = ShogiServer::Board.new
+    b.set_from_str(<<EOM)
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU * -FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  * -FU * 
+P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
+-
+EOM
+    m = ShogiServer::Move.new(2,6,2,7,"TO",false)
+    validate_before_after(b,m)
+  end
+
+  def test_capture_unpromote
+    b = ShogiServer::Board.new
+    b.set_from_str(<<EOM)
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
+P4 *  *  *  *  *  *  * +FU * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU * +FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
+EOM
+    m = ShogiServer::Move.new(2,4,2,3,"FU",true)
+    validate_before_after(b,m)
+  end
+
+  def test_capture_unpromote_white
+    b = ShogiServer::Board.new
+    b.set_from_str(<<EOM)
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU * -FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  * -FU * 
+P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
+-
+EOM
+    m = ShogiServer::Move.new(2,6,2,7,"FU",false)
+    validate_before_after(b,m)
+  end
+
+  def test_drop
+    b = ShogiServer::Board.new
+    b.set_from_str(<<EOM)
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU * -FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU * +FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
++
+P+00FU
+P-00FU
+EOM
+    m = ShogiServer::Move.new(0,0,2,3,"FU",true)
+    validate_before_after(b,m)
+  end
+
+  def test_drop_white
+    b = ShogiServer::Board.new
+    b.set_from_str(<<EOM)
+P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
+P2 * -HI *  *  *  *  * -KA * 
+P3-FU-FU-FU-FU-FU-FU-FU * -FU
+P4 *  *  *  *  *  *  *  *  * 
+P5 *  *  *  *  *  *  *  *  * 
+P6 *  *  *  *  *  *  *  *  * 
+P7+FU+FU+FU+FU+FU+FU+FU * +FU
+P8 * +KA *  *  *  *  * +HI * 
+P9+KY+KE+GI+KI+OU+KI+GI+KE+KY
+-
+P+00FU
+P-00FU
+EOM
+    m = ShogiServer::Move.new(0,0,2,3,"FU",false)
+    validate_before_after(b,m)
+  end
+end # class TestMoveBack
+
+
 class Test_promote < Test::Unit::TestCase
   def test_fu
     b = ShogiServer::Board.new
@@ -330,7 +579,9 @@ EOM
     assert_equal(:normal, b.handle_one_move("+7172HI"))
     assert_equal(:normal, b.handle_one_move("-9291OU"))
 
+    orig = b.deep_copy
     assert_equal(:oute_sennichite_sente_lose, b.handle_one_move("+7271HI")) # 4
+    assert_equal orig, b
   end
 
   def test_oute_sennichite1 #330
@@ -381,7 +632,9 @@ b.history[b.to_s] = 1
     assert_equal(:normal, b.handle_one_move("-4849RY"))
 
     assert_equal(:normal, b.handle_one_move("+6968OU")) # added
+    orig = b.deep_copy
     assert_equal(:oute_sennichite_gote_lose, b.handle_one_move("-4948RY"))
+    assert_equal orig, b
   end
 
   def test_not_oute_sennichite
@@ -406,7 +659,9 @@ EOM
     assert_equal(:normal, b.handle_one_move("+7172HI"))
     assert_equal(:normal, b.handle_one_move("-9291OU"))
 
+    orig = b.deep_copy
     assert_equal(:sennichite, b.handle_one_move("+7271HI")) # 4
+    assert_equal orig, b
   end
 
   def test_sennichite0
@@ -428,7 +683,9 @@ EOM
     assert_equal(:normal, b.handle_one_move("+7172OU"))
     assert_equal(:normal, b.handle_one_move("-9192OU"))
     assert_equal(:normal, b.handle_one_move("+7271OU"))
+    orig = b.deep_copy
     assert_equal(:sennichite, b.handle_one_move("-9291OU")) # 4
+    assert_equal orig, b
   end
 
   def test_sennichite1          # 329
@@ -457,7 +714,9 @@ EOM
     assert_equal(:normal, b.handle_one_move("+2858HI"))
     assert_equal(:normal, b.handle_one_move("-8252HI"))
     assert_equal(:normal, b.handle_one_move("+5828HI"))
+    orig = b.deep_copy
     assert_equal(:sennichite, b.handle_one_move("-5282HI"))
+    assert_equal orig, b
   end
 end
 
diff --git a/test/TC_move.rb b/test/TC_move.rb
new file mode 100644 (file)
index 0000000..48db046
--- /dev/null
@@ -0,0 +1,46 @@
+$:.unshift File.join(File.dirname(__FILE__), "..")
+
+require 'test/unit'
+require 'shogi_server'
+require 'shogi_server/move'
+
+class TestMove < Test::Unit::TestCase
+
+  def test_is_drop
+    m = ShogiServer::Move.new 6,7,8,9,"FU",true
+    assert 6, m.x0
+    assert 7, m.y0
+    assert 8, m.x1
+    assert 9, m.y1
+    assert_equal "FU", m.name
+    assert m.sente
+
+    assert !m.promotion
+    assert_nil m.captured_piece
+    assert !m.captured_piece_promoted
+    assert !m.is_drop?
+
+    m = ShogiServer::Move.new 0,0,7,6,"FU",true
+    assert m.is_drop?
+  end
+
+  def test_set_captured_piece_not_promoted
+    m = ShogiServer::Move.new 2,4,2,3,"TO",true
+    board = ShogiServer::Board.new # dummy
+    fu = ShogiServer::PieceFU.new(board, 2, 3, false, false)
+    m.set_captured_piece(fu)
+
+    assert_equal fu, m.captured_piece
+  end
+
+  def test_set_captured_piece_promoted
+    m = ShogiServer::Move.new 2,4,2,3,"TO",true
+    board = ShogiServer::Board.new # dummy
+    fu = ShogiServer::PieceFU.new(board, 2, 3, false, true)
+    m.set_captured_piece(fu)
+
+    assert_equal fu, m.captured_piece
+    assert m.captured_piece_promoted
+  end
+end
+