lines << l
end
file = YAML::load(lines)
- erb = ERB.new( DATA.read, nil, "%<>" )
+ erb = ERB.new( DATA.read, nil, "%>" )
tables = []
file["players"].each do |key, yaml|
sorted_keys = yaml.keys.sort {|a,b| yaml[b]['rate'] <=> yaml[a]['rate']}
- table = ERB.new(<<ENDTABLE, nil, "%<>")
-<% sorted_keys.each do |key| %>
+ table = ERB.new(<<ENDTABLE, nil, "%>")
+% sorted_keys.each do |key|
<%
win = yaml[key]['win']
loss = yaml[key]['loss']
last_modified = yaml[key]['last_modified']
%>
<tr>
- <td class="name"><%=h yaml[key]['name']%></td>
- <td class="rate"><%="%5d" % [ yaml[key]['rate'] ]%></td>
- <td class="ngames"><%="%5d" % [ win ]%></td>
- <td class="ngames"><%="%5d" % [ loss ]%></td>
- <td class="win_rate"><%="%.3f" % [win_rate]%></td>
- <td class="last_modified"><%=show_date(last_modified)%></td>
+ <td class="name"> <%= h yaml[key]['name'] %> </td>
+ <td class="rate"> <%= "%5d" % [ yaml[key]['rate'] ] %> </td>
+ <td class="ngames"> <%= "%5d" % [ win ] %> </td>
+ <td class="ngames"> <%= "%5d" % [ loss ] %> </td>
+ <td class="win_rate"> <%= "%.3f" % [win_rate] %> </td>
+ <td class="last_modified"><%= show_date(last_modified) %> </td>
</tr>
-<% end %>
+% end
ENDTABLE
tables << table.result(binding)
margin-right: auto;
border-collapse: collapse;
border: solid 2px;}
+ CAPTION { caption-side: left;}
TH {border: solid 1px;}
TD {padding-left: 10px;
padding-right: 10px;
.rate, .ngames, .win_rate {text-align: right;}
.last_modified {text-align: left;}
- P.footer {text-align: right;
- font-size: 80%;}
-
+ DIV.footer {text-align: right;
+ font-size: 80%;}
--></style>
</head>
<body>
<h1>Shogi Server Rating</h1>
-<% tables.each do |t| %>
+% tables.each_with_index do |t, index|
<table>
+<caption>Group: <%=index%></caption>
<colgroup>
<col class="name">
<col class="rate">
</colgroup>
<thead>
<tr>
- <th>name</th> <th>rate</th> <th>win</th> <th>loss</th> <th>win_rate</th> <th>last_modified</th>
+ <th>name</th>
+ <th>rate</th>
+ <th>win</th>
+ <th>loss</th>
+ <th>win_rate</th>
+ <th>last_modified</th>
</tr>
</thead>
<tbody>
-<%= t %>
+ <%= t %>
</tbody>
</table>
-<% end %>
-<p>The average of the rates is always 1000.
+<hr style="width:50%; margin:1em auto;">
+
+% end
+
+<p>Groups are independently rated. You can not compare rates across them.
+<p>The average of the rates in a group is always 1000.
+<p><a href="http://wdoor.c.u-tokyo.ac.jp/shogi/">BACK</a>
<hr/>
-<p class="footer">Last modified at <%=Time.now%>
+
+<div class="footer">
+ <p>Last modified at <%=Time.now%>
+ <p>$Revision$
+</div>
</body>
</html>
## 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
+
Max_Write_Queue_Size = 1000
Max_Identifier_Length = 32
Default_Timeout = 60 # for single socket operation
Release.concat("-") if (Release == "")
Revision = "$Revision$".gsub(/[^\.\d]/, '')
-STDOUT.sync = true
-STDERR.sync = true
-
require 'getoptlong'
require 'thread'
require 'timeout'
require 'yaml/store'
require 'digest/md5'
-TCPSocket.do_not_reverse_lookup = true
-Thread.abort_on_exception = true
class TCPSocket
end
-class BasicPlayer
- # Idetifier of the player in the rating system
- attr_accessor :id
-
- # Name of the player
- attr_accessor :name
-
- # Password of the player, which does not include a trip
- attr_accessor :password
-
- # Score in the rating sysem
- attr_accessor :rate
-
- # Group in the rating system
- attr_accessor :rating_group
-
- # Last timestamp when the rate was modified
- attr_accessor :modified_at
-
-
-
- def initialize
- @name = nil
- @password = nil
- end
-
- def modified_at
- @modified_at || Time.now
- end
-
- def rate=(new_rate)
- if @rate != new_rate
- @rate = new_rate
- @modified_at = Time.now
- end
- end
-
- def rated?
- @id != nil
- end
-
- def simple_id
- if @trip
- simple_name = @name.gsub(/@.*?$/, '')
- "%s+%s" % [simple_name, @trip[0..8]]
- else
- @name
- end
- end
-
- ##
- # Parses str in the LOGIN command, sets up @id and @trip
- #
- def set_password(str)
- if str && !str.empty?
- @password = str.strip
- @id = "%s+%s" % [@name, Digest::MD5.hexdigest(@password)]
- else
- @id = @password = nil
- end
- end
-end
-
-
+######################################################
+# Processes the LOGIN command.
+#
class Login
def Login.good_login?(str)
tokens = str.split
end
end
- attr_reader :player, :csa_1st_str
+ attr_reader :player
+
+ # the first command that will be executed just after LOGIN.
+ # If it is nil, the default process will be started.
+ attr_reader :csa_1st_str
def initialize(player, password)
@player = player
def process
@player.write_safe(sprintf("LOGIN:%s OK\n", @player.name))
+ log_message(sprintf("user %s run in %s mode", @player.name, @player.protocol))
end
def incorrect_duplicated_player(str)
end
end
+######################################################
+# Processes LOGIN for the CSA standard mode.
+#
class LoginCSA < Login
PROTOCOL = "CSA"
def process
super
- log_message(sprintf("user %s run in CSA mode", @player.name))
@csa_1st_str = "%%GAME #{@gamename} *"
end
end
+######################################################
+# Processes LOGIN for the extented mode.
+#
class Loginx1 < Login
PROTOCOL = "x1"
def process
super
- log_message(sprintf("user %s run in %s mode", @player.name, PROTOCOL))
@player.write_safe(sprintf("##[LOGIN] +OK %s\n", PROTOCOL))
end
end
+class BasicPlayer
+ # Idetifier of the player in the rating system
+ attr_accessor :id
+
+ # Name of the player
+ attr_accessor :name
+
+ # Password of the player, which does not include a trip
+ attr_accessor :password
+
+ # Score in the rating sysem
+ attr_accessor :rate
+
+ # Group in the rating system
+ attr_accessor :rating_group
+
+ # Last timestamp when the rate was modified
+ attr_accessor :modified_at
+
+ def initialize
+ @name = nil
+ @password = nil
+ end
+
+ def modified_at
+ @modified_at || Time.now
+ end
+
+ def rate=(new_rate)
+ if @rate != new_rate
+ @rate = new_rate
+ @modified_at = Time.now
+ end
+ end
+
+ def rated?
+ @id != nil
+ end
+
+ def simple_id
+ if @trip
+ simple_name = @name.gsub(/@.*?$/, '')
+ "%s+%s" % [simple_name, @trip[0..8]]
+ else
+ @name
+ end
+ end
+
+ ##
+ # Parses str in the LOGIN command, sets up @id and @trip
+ #
+ def set_password(str)
+ if str && !str.empty?
+ @password = str.strip
+ @id = "%s+%s" % [@name, Digest::MD5.hexdigest(@password)]
+ else
+ @id = @password = nil
+ end
+ end
+end
+
+
class Player < BasicPlayer
def initialize(str, socket)
super()
return options
end
-def write_pid_file(file)
+def write_pid_file(file)
open(file, "w") do |fh|
fh.print Process::pid, "\n"
end
end
def main
+
$mutex = Mutex::new
Thread::start do
Thread.pass
end
end
end
+module_function :main
+
+end # module ShogiServer
if ($0 == __FILE__)
- LEAGUE = League::new
- main
+ STDOUT.sync = true
+ STDERR.sync = true
+ TCPSocket.do_not_reverse_lookup = true
+ Thread.abort_on_exception = true
+
+ LEAGUE = ShogiServer::League::new
+ ShogiServer::main
end