X-Git-Url: http://git.sourceforge.jp/view?p=shogi-server%2Fshogi-server.git;a=blobdiff_plain;f=shogi_server%2Fcommand.rb;h=dedbaae9ab5fa7775576940d8edb7a9a6c743c40;hp=f604ea94e2b46bae4f78f7a0615293b995fd6c76;hb=73ae3557f07139fd776880101630ff12bb666259;hpb=e795b7aaa822be7453579852b40feb3dbba72d14 diff --git a/shogi_server/command.rb b/shogi_server/command.rb index f604ea9..dedbaae 100644 --- a/shogi_server/command.rb +++ b/shogi_server/command.rb @@ -1,7 +1,7 @@ ## $Id$ ## Copyright (C) 2004 NABEYA Kenichi (aka nanami@2ch) -## Copyright (C) 2007-2008 Daigo Moriwaki (daigo at debian dot org) +## Copyright (C) 2007-2012 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 @@ -69,6 +69,9 @@ module ShogiServer my_sente_str = $3 cmd = GameChallengeCommand.new(str, player, command_name, game_name, my_sente_str) + when /^%%(GAME|CHALLENGE)\s+(\S+)/ + msg = "A turn identifier is required" + cmd = ErrorCommand.new(str, player, msg) when /^%%CHAT\s+(.+)/ message = $1 cmd = ChatCommand.new(str, player, message, $league.players) @@ -94,6 +97,19 @@ module ShogiServer when /^%%GETBUOYCOUNT\s+(\S+)/ game_name = $1 cmd = GetBuoyCountCommand.new(str, player, game_name) + when /^%%FORK\s+(\S+)\s+(\S+)(.*)/ + source_game = $1 + new_buoy_game = $2 + nth_move = nil + if $3 && /^\s+(\d+)/ =~ $3 + nth_move = $3.to_i + end + cmd = ForkCommand.new(str, player, source_game, new_buoy_game, nth_move) + when /^%%FORK\s+(\S+)$/ + source_game = $1 + new_buoy_game = nil + nth_move = nil + cmd = ForkCommand.new(str, player, source_game, new_buoy_game, nth_move) when /^\s*$/ cmd = SpaceCommand.new(str, player) when /^%%%[^%]/ @@ -105,6 +121,7 @@ module ShogiServer end cmd.time = time + player.last_command_at = time return cmd end @@ -476,11 +493,21 @@ module ShogiServer @command_name = command_name @game_name = game_name @my_sente_str = my_sente_str + player.set_sente_from_str(@my_sente_str) end def call if (! Login::good_game_name?(@game_name)) - @player.write_safe(sprintf("##[ERROR] bad game name\n")) + @player.write_safe(sprintf("##[ERROR] bad game name: %s.\n", @game_name)) + if (/^(.+)-\d+-\d+F?$/ =~ @game_name) + if Login::good_identifier?($1) + # do nothing + else + @player.write_safe(sprintf("##[ERROR] invalid identifiers are found or too many characters are used.\n")) + end + else + @player.write_safe(sprintf("##[ERROR] game name should consist of three parts like game-1500-60.\n")) + end return :continue elsif ((@player.status == "connected") || (@player.status == "game_waiting")) ## continue @@ -490,14 +517,6 @@ module ShogiServer end rival = nil - if (Buoy.game_name?(@game_name)) - if (@my_sente_str != "*") - @player.write_safe(sprintf("##[ERROR] You are not allowed to specify TEBAN %s for the game %s\n", @my_sente_str, @game_name)) - return :continue - end - @player.sente = nil - end # really end - if (League::Floodgate.game_name?(@game_name)) if (@my_sente_str != "*") @player.write_safe(sprintf("##[ERROR] You are not allowed to specify TEBAN %s for the game %s\n", @my_sente_str, @game_name)) @@ -505,42 +524,18 @@ module ShogiServer end @player.sente = nil else - if (@my_sente_str == "*") && !Login.handicapped_game_name?(@game_name) - rival = $league.get_player("game_waiting", @game_name, nil, @player) # no preference - elsif (@my_sente_str == "+") - rival = $league.get_player("game_waiting", @game_name, false, @player) # rival must be gote - elsif (@my_sente_str == "-") - rival = $league.get_player("game_waiting", @game_name, true, @player) # rival must be sente - else - @player.write_safe(sprintf("##[ERROR] bad game option\n")) - return :continue + rival = $league.find_rival(@player, @game_name) + if rival.instance_of?(Symbol) + # An error happened. rival is not a player instance, but an error + # symobl that must be returned to the main routine immediately. + return rival end end if (rival) @player.game_name = @game_name - - if ((@my_sente_str == "*") && (rival.sente == nil)) - if (rand(2) == 0) - @player.sente = true - rival.sente = false - else - @player.sente = false - rival.sente = true - end - elsif (rival.sente == true) # rival has higher priority - @player.sente = false - elsif (rival.sente == false) - @player.sente = true - elsif (@my_sente_str == "+") - @player.sente = true - rival.sente = false - elsif (@my_sente_str == "-") - @player.sente = false - rival.sente = true - else - ## never reached - end + Game::decide_turns(@player, @my_sente_str, rival) + if (Buoy.game_name?(@game_name)) buoy = Buoy.new # TODO config if buoy.is_new_game?(@game_name) @@ -557,7 +552,7 @@ module ShogiServer board = Board.new begin board.set_from_moves(moves_array) - rescue => err + rescue # it will never happen since moves have already been checked log_error "Failed to set up a buoy game: #{moves}" return :continue @@ -575,13 +570,6 @@ module ShogiServer if (@command_name == "GAME") @player.status = "game_waiting" @player.game_name = @game_name - if (@my_sente_str == "+") - @player.sente = true - elsif (@my_sente_str == "-") - @player.sente = false - else - @player.sente = nil - end else # challenge @player.write_safe(sprintf("##[ERROR] can't find rival for %s\n", @game_name)) @player.status = "connected" @@ -704,14 +692,19 @@ module ShogiServer # Command for an error # class ErrorCommand < Command - def initialize(str, player) - super + def initialize(str, player, msg=nil) + super(str, player) + @msg = msg || "unknown command" end + attr_reader :msg def call - msg = "##[ERROR] unknown command %s\n" % [@str.chomp] - @player.write_safe(msg) - log_error(msg) + cmd = @str.chomp + # Aim to hide a possible password + cmd.gsub!(/LOGIN\s*(\w+)\s+.*/i, 'LOGIN \1...') + @msg = "##[ERROR] %s: %s\n" % [@msg, cmd] + @player.write_safe(@msg) + log_error(@msg) return :continue end end @@ -759,15 +752,32 @@ module ShogiServer @player.write_safe(sprintf("##[SETBUOY] +OK\n")) log_info("A buoy game was created: %s by %s" % [@game_name, @player.name]) - # if two players, who are not @player, are waiting for a new game, start it - p1 = $league.get_player("game_waiting", @game_name, true, @player) - return :continue unless p1 - p2 = $league.get_player("game_waiting", @game_name, false, @player) - return :continue unless p2 - + # if two players are waiting for this buoy game, start it + candidates = $league.find_all_players do |player| + player.status == "game_waiting" && + player.game_name == @game_name && + player.name != @player.name + end + if candidates.empty? + log_info("No players found for a buoy game. Wait for players: %s" % [@game_name]) + return :continue + end + p1 = candidates.first + p2 = $league.find_rival(p1, @game_name) + if p2.nil? + log_info("No opponent found for a buoy game. Wait for the opponent: %s by %s" % [@game_name, p1.name]) + return :continue + elsif p2.instance_of?(Symbol) + # An error happened. rival is not a player instance, but an error + # symobl that must be returned to the main routine immediately. + return p2 + end + # found two players: p1 and p2 + log_info("Starting a buoy game: %s with %s and %s" % [@game_name, p1.name, p2.name]) buoy.decrement_count(buoy_game) - game = Game::new(@game_name, p1, p2, board) + Game::new(@game_name, p1, p2, board) return :continue + rescue WrongMoves => e @player.write_safe(sprintf("##[ERROR] wrong moves: %s\n", @moves)) log_error "Received wrong moves: %s from %s. [%s]" % [@moves, @player.name, e.message] @@ -822,6 +832,72 @@ module ShogiServer @player.write_safe("##[GETBUOYCOUNT] %s\n" % [buoy_game.count]) end @player.write_safe("##[GETBUOYCOUNT] +OK\n") + return :continue + end + end + + # %%FORK [] + # Fork a new game from the posistion where the n-th (starting from 1) move + # of a source game is played. The new game should be a valid buoy game + # name. The default value of n is the position where the previous position + # of the last one. + # + class ForkCommand < Command + def initialize(str, player, source_game, new_buoy_game, nth_move) + super(str, player) + @source_game = source_game + @new_buoy_game = new_buoy_game + @nth_move = nth_move # may be nil + end + attr_reader :new_buoy_game + + def decide_new_buoy_game_name + name = nil + total_time = nil + byo_time = nil + + if @source_game.split("+").size >= 2 && + /^([^-]+)-(\d+)-(\d+F?)/ =~ @source_game.split("+")[1] + name = $1 + total_time = $2 + byo_time = $3 + end + if name == nil || total_time == nil || byo_time == nil + @player.write_safe(sprintf("##[ERROR] wrong source game name to make a new buoy game name: %s\n", @source_game)) + log_error "Received a wrong source game name to make a new buoy game name: %s from %s." % [@source_game, @player.name] + return :continue + end + @new_buoy_game = "buoy_%s_%d-%s-%s" % [name, @nth_move, total_time, byo_time] + @player.write_safe(sprintf("##[FORK]: new buoy game name: %s\n", @new_buoy_game)) + @player.write_safe("##[FORK] +OK\n") + end + + def call + game = $league.games[@source_game] + unless game + @player.write_safe(sprintf("##[ERROR] wrong source game name: %s\n", @source_game)) + log_error "Received a wrong source game name: %s from %s." % [@source_game, @player.name] + return :continue + end + + moves = game.read_moves # [["+7776FU","T2"],["-3334FU","T5"]] + @nth_move = moves.size - 1 unless @nth_move + if @nth_move > moves.size or @nth_move < 1 + @player.write_safe(sprintf("##[ERROR] number of moves to fork is out of range: %s.\n", moves.size)) + log_error "Number of moves to fork is out of range: %s [%s]" % [@nth_move, @player.name] + return :continue + end + new_moves_str = "" + moves[0...@nth_move].each do |m| + new_moves_str << m.join(",") + end + + unless @new_buoy_game + decide_new_buoy_game_name + end + + buoy_cmd = SetBuoyCommand.new(@str, @player, @new_buoy_game, new_moves_str, 1) + return buoy_cmd.call end end