From cd3b4625cb8625594744999cbac91e0001368392 Mon Sep 17 00:00:00 2001 From: daigo Date: Sat, 17 Jul 2010 20:31:35 +0900 Subject: [PATCH] * [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(). --- changelog | 7 ++ shogi_server.rb | 1 + shogi_server/board.rb | 128 +++++++++++++++++++++---- shogi_server/move.rb | 48 ++++++++++ test/TC_ALL.rb | 1 + test/TC_board.rb | 259 ++++++++++++++++++++++++++++++++++++++++++++++++++ test/TC_move.rb | 46 +++++++++ 7 files changed, 469 insertions(+), 21 deletions(-) create mode 100644 shogi_server/move.rb create mode 100644 test/TC_move.rb diff --git a/changelog b/changelog index 18fb40c..afa2137 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,10 @@ +2010-07-17 Daigo Moriwaki + + * [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 * [shogi-server] diff --git a/shogi_server.rb b/shogi_server.rb index 587798a..05bc2d5 100644 --- a/shogi_server.rb +++ b/shogi_server.rb @@ -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' diff --git a/shogi_server/board.rb b/shogi_server/board.rb index 3778bd7..d7f9466 100644 --- a/shogi_server/board.rb +++ b/shogi_server/board.rb @@ -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 index 0000000..8354e3c --- /dev/null +++ b/shogi_server/move.rb @@ -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 diff --git a/test/TC_ALL.rb b/test/TC_ALL.rb index 934032c..ff314f0 100644 --- a/test/TC_ALL.rb +++ b/test/TC_ALL.rb @@ -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' diff --git a/test/TC_board.rb b/test/TC_board.rb index 6008af5..b9d54fc 100644 --- a/test/TC_board.rb +++ b/test/TC_board.rb @@ -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(<