-
Notifications
You must be signed in to change notification settings - Fork 0
/
move.rb
executable file
·335 lines (281 loc) · 8.1 KB
/
move.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
#!/usr/bin/env ruby
# -*- encoding: utf-8 -*-
# frozen_string_literal: true
require 'bundler/setup'
require 'cgi'
require 'cgi/session'
require 'logger'
require 'unindent'
require './file/chatfile.rb'
require './file/jsonkifu.rb'
require './file/jsonmove.rb'
require './file/matchinfofile.rb'
require './file/pathlist.rb'
require './file/taikyokufile.rb'
require './file/userchatfile.rb'
require './game/taikyokudata.rb'
require './game/taikyokumail.rb'
require './game/userinfo.rb'
require './util/myhtml.rb'
require './util/settings.rb'
#
# CGI本体
#
class Move
# 初期化
#
# @param cgi CGIオブジェクト
def initialize(cgi)
@log = Logger.new(PathList::MOVELOG)
# @log.level = Logger::INFO
# @log.debug('Move.new()')
readuserparam(cgi)
read_cgiparam(cgi)
@turn = '?'
@finished = false
@jmv = JsonMove.fromtext(move)
@log.info("gameid:#{gameid}, sfen:#{sfen}, move:#{move}")
# @log.debug('Move.initialized')
end
# @!attribute [r] gameid
# @return 対局ID
# @!attribute [r] mif
# @return MatchInfoFileオブジェクト
# @!attribute [r] jmv
# @return JsonMoveオブジェクト
# @!attribute [r] userinfo
# @return ユーザー情報
# @!attribute [r] log
# @return ログオブジェクト
attr_reader :finished, :gameid, :jmv, :log, :mif, :move,
:sfen, :tkd, :turn, :userinfo
# paramsから値の読み出し
#
# @param cgi CGIオブジェクト
def read_cgiparam(cgi)
@params = cgi.params
@gameid = cgi.query_string
@sfen = @params['sfen'][0] if @params['sfen']
@move = @params['jsonmove'][0] if @params['jsonmove']
end
# sessionの取得と情報の読み取り
#
# @param cgi CGIオブジェクト
def readuserparam(cgi)
# @log.debug('Move.readuserparam')
# check cookies
# @log.debug("cookie:#{cgi.cookies}")
begin
session = CGI::Session.new(
cgi,
'new_session' => false,
'session_key' => '_washcrus_session',
'tmpdir' => './tmp'
)
rescue ArgumentError # => ae
# session = nil
@log.info('failed to find session')
# @log.debug("#{ae.message}, (#{ae.class})")
# @log.debug("sesionfiles:#{Dir['./tmp/*']}")
end
# check cookies
# @log.debug("cookie:#{cgi.cookies}")
@userinfo = UserInfo.new
userinfo.readsession(session) if session
session&.close
@header = cgi.header('charset' => 'UTF-8')
@header = @header.gsub("\r\n", "\n")
end
# 情報のチェック
def check_param
# gameid が無いよ
# @log.debug "MyHtml.illegalaccess gid:#{gameid}" unless gameid
return MyHtml.puts_textplain_illegalaccess unless gameid
tcdb = TaikyokuChuFile.new
tcdb.read
# 存在しないはずのIDだよ
# @log.debug "illegalaccess (tcdb.exist?(#{gameid}) =>" \
# " #{tcdb.exist?(gameid)})" unless tcdb.exist?(gameid)
return MyHtml.puts_textplain_illegalaccess unless tcdb.exist?(gameid)
# userinfoが変だよ
# @log.debugpleaselogin(uid:#{userinfo.user_id})" unless userinfo.exist_indb
return MyHtml.puts_textplain_pleaselogin unless userinfo.exist_indb
# moveが変だよ
# @log.debug "MyHtml.'invalid move.'" unless jmv
return MyHtml.puts_textplain('invalid move.') unless jmv
self
end
# メールの送信
#
# @param finished [boolean] 終局したかどうか
# @param now [Time] 着手日時オブジェクト
def send_mail(finished, now)
kifu = tkd.jkf.to_kif
tmail = TaikyokuMail.new(gameid, userinfo, now, move)
tmail.setmif(tkd.mif)
finished ? tmail.send_mail_finished(kifu) : tmail.send_mail_next
@log.debug('Move.sendmail')
end
# 発言者、対局者x2のデータにも書く
#
# @param addedmsg 発言
def write2chatview(addedmsg)
tkd.mif.getplayerids.each do |userid|
uchat = UserChatFile.new(userid)
uchat.read
uchat.add(addedmsg, @gameid)
end
end
# 引き分けで終局
#
# @param now [Time] 着手日時オブジェクト
def finish_draw(now)
@turn = 'd'
tkd.finished(now, nil, turn)
sayfinish('')
end
def winner
return mif.playerb.name if turn == 'fb'
return mif.playerw.name if turn == 'fw'
end
def sayfinish(winner)
# chat file
chat = ChatFile.new(gameid)
write2chatview(chat.say_finish(winner, turn, mif.nth.to_i - 1))
end
# どちらかが勝って終局
#
# @param now [Time] 着手日時オブジェクト
def finish_normal(now)
gote_win = mif.senteban?
@turn = gote_win ? 'fw' : 'fb'
tkd.finished(now, gote_win, turn)
sayfinish(winner)
end
# 対局終了処理
#
# @param tcdb 対局中データベース
# @param now 現在の時刻オブジェクト
#
# @note draw非対応
def finish_game(tcdb, now)
# 終了日時の更新とか勝敗の記録とか
@log.debug("tkd.finished(now, #{mif.teban} == 'b')")
mif.turn == 'd' ? finish_draw(now) : finish_normal(now)
# 対局中からはずす
@log.debug('tcdb.finished(gameid)')
tcdb.finished(gameid)
end
# 対局情報の読み出しなどといった準備
def prepare_taikyokudata
@tkd = TaikyokuData.new
tkd.log = @log
tkd.setid(gameid)
# @mif = tkd.mif
# tkd.read
end
# 終局していれば対局終了処理をする
#
# @param status TaikyokuData::RES_OVERとか
# @param now 現在の時刻オブジェクト
#
# @return 対局中データベース
def chkandupdtchu(status, now)
tcdb = TaikyokuChuFile.new
tcdb.read
finish_game(tcdb, now) if status == TaikyokuData::RES_OVER
tcdb
end
# 対局情報の更新
#
# @param status TaikyokuData::RES_OVERとか
# @param now 現在の時刻オブジェクト
def update_taikyokudata(status, now)
tcdb = chkandupdtchu(status, now)
# @log.debug('Move.updatelastmove')
tkd.updatelastmove(move, now)
# @log.debug('Move.mif.write')
# @log.debug('Move.jkf.write')
tkd.write
@finished = status != TaikyokuData::RES_NEXT
tcdb.update_dt_turn(gameid, now, turn) unless finished
end
# 対局情報の登録更新
#
# @param status [Integer] 終局したかどうか
# @param now [Time] 着手日時オブジェクト
def register_move(status, now)
@turn = mif.teban
update_taikyokudata(status, now)
TaikyokuFile.new.update_dt_turn(gameid, now, turn)
send_mail(finished, now)
# 移動完了の表示
MyHtml.puts_textplain('Moved.')
end
# 指し手を適用する
#
# @param now [Time] 着手日時オブジェクト
def applymove(now)
@log.debug('Move.apply sfen, jmv')
# tkd.move(@jmv, now)
ret = tkd.move(sfen, jmv, now)
@log.debug("tkd.move() = #{ret}")
ret
end
# mif(MatchInfoFile)の読み取りと対局者名の読み取り
def read_mif
@mif = tkd.mif
end
#
# 実行本体。
#
def perform
# gameid が無いよ, userinfoが変だよ, moveが変だよ, 存在しないはずのIDだよ
return unless check_param
@log.debug('Move.read data')
prepare_taikyokudata
tkd.lockex do
tkd.read
read_mif
now = Time.now
# 指し手を適用する
ret = applymove(now)
# 違反移動の表示
return MyHtml.puts_textplain('invalid move.') unless ret
return MyHtml.puts_textplain('Draw suggestion.') \
if ret == TaikyokuData::RES_DRAW
register_move(ret, now)
end
# @log.debug('Move.performed')
end
end
# エラー時のログ出力
#
# @param err エラーオブジェクト
# @param move Moveオブジェクト
def errtrace(err, move)
move.log.warn("class=[#{err.class}] message=[#{err.message}] " \
"stack=[#{err.backtrace.join("\n")}] in move")
end
# -----------------------------------
# main
#
begin
cgi = CGI.new
# ブロック内の処理を計測
# require 'stackprof'
# StackProf.run(out: "./tmp/stackprof_move_#{Time.now.to_i}.dump") do
move = Move.new(cgi)
# move.readuserparam
move.perform
# end
rescue ScriptError => e
errtrace(e, move)
rescue SecurityError => e
errtrace(e, move)
rescue StandardError => e
errtrace(e, move)
end
# -----------------------------------
# testing
#