mjai 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/mjai +9 -0
- data/bin/mjai-shanten +9 -0
- data/bin/mjai-tsumogiri +9 -0
- data/lib/mjai/action.rb +41 -0
- data/lib/mjai/active_game.rb +230 -0
- data/lib/mjai/archive.rb +46 -0
- data/lib/mjai/archive_player.rb +47 -0
- data/lib/mjai/context.rb +34 -0
- data/lib/mjai/file_converter.rb +86 -0
- data/lib/mjai/furo.rb +57 -0
- data/lib/mjai/game.rb +357 -0
- data/lib/mjai/hora.rb +528 -0
- data/lib/mjai/jsonizable.rb +171 -0
- data/lib/mjai/mentsu.rb +46 -0
- data/lib/mjai/mjai_command.rb +93 -0
- data/lib/mjai/mjson_archive.rb +25 -0
- data/lib/mjai/pai.rb +138 -0
- data/lib/mjai/player.rb +340 -0
- data/lib/mjai/puppet_player.rb +14 -0
- data/lib/mjai/shanten_analysis.rb +273 -0
- data/lib/mjai/shanten_player.rb +102 -0
- data/lib/mjai/tcp_client_game.rb +63 -0
- data/lib/mjai/tcp_game_server.rb +205 -0
- data/lib/mjai/tcp_player.rb +66 -0
- data/lib/mjai/tenhou_archive.rb +412 -0
- data/lib/mjai/tenpai_analysis.rb +62 -0
- data/lib/mjai/tsumogiri_player.rb +20 -0
- data/lib/mjai/validation_error.rb +7 -0
- data/lib/mjai/with_fields.rb +18 -0
- data/share/html/css/style.css +77 -0
- data/share/html/css/style.scss +106 -0
- data/share/html/images/README.txt +1 -0
- data/share/html/images/b_1_1.gif +0 -0
- data/share/html/images/b_1_2.gif +0 -0
- data/share/html/images/b_5_1.gif +0 -0
- data/share/html/images/b_5_2.gif +0 -0
- data/share/html/images/b_8_1.gif +0 -0
- data/share/html/images/b_8_2.gif +0 -0
- data/share/html/images/b_9_1.gif +0 -0
- data/share/html/images/b_9_2.gif +0 -0
- data/share/html/images/blank.png +0 -0
- data/share/html/images/c_c_1.gif +0 -0
- data/share/html/images/c_c_2.gif +0 -0
- data/share/html/images/c_c_3.gif +0 -0
- data/share/html/images/c_c_4.gif +0 -0
- data/share/html/images/c_e_1.gif +0 -0
- data/share/html/images/c_e_2.gif +0 -0
- data/share/html/images/c_e_3.gif +0 -0
- data/share/html/images/c_e_4.gif +0 -0
- data/share/html/images/c_n_1.gif +0 -0
- data/share/html/images/c_n_2.gif +0 -0
- data/share/html/images/c_n_3.gif +0 -0
- data/share/html/images/c_n_4.gif +0 -0
- data/share/html/images/c_s_1.gif +0 -0
- data/share/html/images/c_s_2.gif +0 -0
- data/share/html/images/c_s_3.gif +0 -0
- data/share/html/images/c_s_4.gif +0 -0
- data/share/html/images/c_w_1.gif +0 -0
- data/share/html/images/c_w_2.gif +0 -0
- data/share/html/images/c_w_3.gif +0 -0
- data/share/html/images/c_w_4.gif +0 -0
- data/share/html/images/dice.gif +0 -0
- data/share/html/images/p_bk_0.gif +0 -0
- data/share/html/images/p_bk_1.gif +0 -0
- data/share/html/images/p_bk_2.gif +0 -0
- data/share/html/images/p_bk_3.gif +0 -0
- data/share/html/images/p_bk_4.gif +0 -0
- data/share/html/images/p_bk_5.gif +0 -0
- data/share/html/images/p_bk_6.gif +0 -0
- data/share/html/images/p_bk_7.gif +0 -0
- data/share/html/images/p_ji_c_0.gif +0 -0
- data/share/html/images/p_ji_c_1.gif +0 -0
- data/share/html/images/p_ji_c_2.gif +0 -0
- data/share/html/images/p_ji_c_3.gif +0 -0
- data/share/html/images/p_ji_c_4.gif +0 -0
- data/share/html/images/p_ji_c_5.gif +0 -0
- data/share/html/images/p_ji_c_6.gif +0 -0
- data/share/html/images/p_ji_c_7.gif +0 -0
- data/share/html/images/p_ji_e_0.gif +0 -0
- data/share/html/images/p_ji_e_1.gif +0 -0
- data/share/html/images/p_ji_e_2.gif +0 -0
- data/share/html/images/p_ji_e_3.gif +0 -0
- data/share/html/images/p_ji_e_4.gif +0 -0
- data/share/html/images/p_ji_e_5.gif +0 -0
- data/share/html/images/p_ji_e_6.gif +0 -0
- data/share/html/images/p_ji_e_7.gif +0 -0
- data/share/html/images/p_ji_h_0.gif +0 -0
- data/share/html/images/p_ji_h_1.gif +0 -0
- data/share/html/images/p_ji_h_2.gif +0 -0
- data/share/html/images/p_ji_h_3.gif +0 -0
- data/share/html/images/p_ji_h_4.gif +0 -0
- data/share/html/images/p_ji_h_5.gif +0 -0
- data/share/html/images/p_ji_h_6.gif +0 -0
- data/share/html/images/p_ji_h_7.gif +0 -0
- data/share/html/images/p_ji_n_0.gif +0 -0
- data/share/html/images/p_ji_n_1.gif +0 -0
- data/share/html/images/p_ji_n_2.gif +0 -0
- data/share/html/images/p_ji_n_3.gif +0 -0
- data/share/html/images/p_ji_n_4.gif +0 -0
- data/share/html/images/p_ji_n_5.gif +0 -0
- data/share/html/images/p_ji_n_6.gif +0 -0
- data/share/html/images/p_ji_n_7.gif +0 -0
- data/share/html/images/p_ji_s_0.gif +0 -0
- data/share/html/images/p_ji_s_1.gif +0 -0
- data/share/html/images/p_ji_s_2.gif +0 -0
- data/share/html/images/p_ji_s_3.gif +0 -0
- data/share/html/images/p_ji_s_4.gif +0 -0
- data/share/html/images/p_ji_s_5.gif +0 -0
- data/share/html/images/p_ji_s_6.gif +0 -0
- data/share/html/images/p_ji_s_7.gif +0 -0
- data/share/html/images/p_ji_w_0.gif +0 -0
- data/share/html/images/p_ji_w_1.gif +0 -0
- data/share/html/images/p_ji_w_2.gif +0 -0
- data/share/html/images/p_ji_w_3.gif +0 -0
- data/share/html/images/p_ji_w_4.gif +0 -0
- data/share/html/images/p_ji_w_5.gif +0 -0
- data/share/html/images/p_ji_w_6.gif +0 -0
- data/share/html/images/p_ji_w_7.gif +0 -0
- data/share/html/images/p_ms1_0.gif +0 -0
- data/share/html/images/p_ms1_1.gif +0 -0
- data/share/html/images/p_ms1_2.gif +0 -0
- data/share/html/images/p_ms1_3.gif +0 -0
- data/share/html/images/p_ms1_4.gif +0 -0
- data/share/html/images/p_ms1_5.gif +0 -0
- data/share/html/images/p_ms1_6.gif +0 -0
- data/share/html/images/p_ms1_7.gif +0 -0
- data/share/html/images/p_ms2_0.gif +0 -0
- data/share/html/images/p_ms2_1.gif +0 -0
- data/share/html/images/p_ms2_2.gif +0 -0
- data/share/html/images/p_ms2_3.gif +0 -0
- data/share/html/images/p_ms2_4.gif +0 -0
- data/share/html/images/p_ms2_5.gif +0 -0
- data/share/html/images/p_ms2_6.gif +0 -0
- data/share/html/images/p_ms2_7.gif +0 -0
- data/share/html/images/p_ms3_0.gif +0 -0
- data/share/html/images/p_ms3_1.gif +0 -0
- data/share/html/images/p_ms3_2.gif +0 -0
- data/share/html/images/p_ms3_3.gif +0 -0
- data/share/html/images/p_ms3_4.gif +0 -0
- data/share/html/images/p_ms3_5.gif +0 -0
- data/share/html/images/p_ms3_6.gif +0 -0
- data/share/html/images/p_ms3_7.gif +0 -0
- data/share/html/images/p_ms4_0.gif +0 -0
- data/share/html/images/p_ms4_1.gif +0 -0
- data/share/html/images/p_ms4_2.gif +0 -0
- data/share/html/images/p_ms4_3.gif +0 -0
- data/share/html/images/p_ms4_4.gif +0 -0
- data/share/html/images/p_ms4_5.gif +0 -0
- data/share/html/images/p_ms4_6.gif +0 -0
- data/share/html/images/p_ms4_7.gif +0 -0
- data/share/html/images/p_ms5_0.gif +0 -0
- data/share/html/images/p_ms5_1.gif +0 -0
- data/share/html/images/p_ms5_2.gif +0 -0
- data/share/html/images/p_ms5_3.gif +0 -0
- data/share/html/images/p_ms5_4.gif +0 -0
- data/share/html/images/p_ms5_5.gif +0 -0
- data/share/html/images/p_ms5_6.gif +0 -0
- data/share/html/images/p_ms5_7.gif +0 -0
- data/share/html/images/p_ms5r_1.png +0 -0
- data/share/html/images/p_ms5r_3.png +0 -0
- data/share/html/images/p_ms6_0.gif +0 -0
- data/share/html/images/p_ms6_1.gif +0 -0
- data/share/html/images/p_ms6_2.gif +0 -0
- data/share/html/images/p_ms6_3.gif +0 -0
- data/share/html/images/p_ms6_4.gif +0 -0
- data/share/html/images/p_ms6_5.gif +0 -0
- data/share/html/images/p_ms6_6.gif +0 -0
- data/share/html/images/p_ms6_7.gif +0 -0
- data/share/html/images/p_ms7_0.gif +0 -0
- data/share/html/images/p_ms7_1.gif +0 -0
- data/share/html/images/p_ms7_2.gif +0 -0
- data/share/html/images/p_ms7_3.gif +0 -0
- data/share/html/images/p_ms7_4.gif +0 -0
- data/share/html/images/p_ms7_5.gif +0 -0
- data/share/html/images/p_ms7_6.gif +0 -0
- data/share/html/images/p_ms7_7.gif +0 -0
- data/share/html/images/p_ms8_0.gif +0 -0
- data/share/html/images/p_ms8_1.gif +0 -0
- data/share/html/images/p_ms8_2.gif +0 -0
- data/share/html/images/p_ms8_3.gif +0 -0
- data/share/html/images/p_ms8_4.gif +0 -0
- data/share/html/images/p_ms8_5.gif +0 -0
- data/share/html/images/p_ms8_6.gif +0 -0
- data/share/html/images/p_ms8_7.gif +0 -0
- data/share/html/images/p_ms9_0.gif +0 -0
- data/share/html/images/p_ms9_1.gif +0 -0
- data/share/html/images/p_ms9_2.gif +0 -0
- data/share/html/images/p_ms9_3.gif +0 -0
- data/share/html/images/p_ms9_4.gif +0 -0
- data/share/html/images/p_ms9_5.gif +0 -0
- data/share/html/images/p_ms9_6.gif +0 -0
- data/share/html/images/p_ms9_7.gif +0 -0
- data/share/html/images/p_no_0.gif +0 -0
- data/share/html/images/p_no_1.gif +0 -0
- data/share/html/images/p_no_2.gif +0 -0
- data/share/html/images/p_no_3.gif +0 -0
- data/share/html/images/p_no_4.gif +0 -0
- data/share/html/images/p_no_5.gif +0 -0
- data/share/html/images/p_no_6.gif +0 -0
- data/share/html/images/p_no_7.gif +0 -0
- data/share/html/images/p_ps1_0.gif +0 -0
- data/share/html/images/p_ps1_1.gif +0 -0
- data/share/html/images/p_ps1_2.gif +0 -0
- data/share/html/images/p_ps1_3.gif +0 -0
- data/share/html/images/p_ps1_4.gif +0 -0
- data/share/html/images/p_ps1_5.gif +0 -0
- data/share/html/images/p_ps1_6.gif +0 -0
- data/share/html/images/p_ps1_7.gif +0 -0
- data/share/html/images/p_ps2_0.gif +0 -0
- data/share/html/images/p_ps2_1.gif +0 -0
- data/share/html/images/p_ps2_2.gif +0 -0
- data/share/html/images/p_ps2_3.gif +0 -0
- data/share/html/images/p_ps2_4.gif +0 -0
- data/share/html/images/p_ps2_5.gif +0 -0
- data/share/html/images/p_ps2_6.gif +0 -0
- data/share/html/images/p_ps2_7.gif +0 -0
- data/share/html/images/p_ps3_0.gif +0 -0
- data/share/html/images/p_ps3_1.gif +0 -0
- data/share/html/images/p_ps3_2.gif +0 -0
- data/share/html/images/p_ps3_3.gif +0 -0
- data/share/html/images/p_ps3_4.gif +0 -0
- data/share/html/images/p_ps3_5.gif +0 -0
- data/share/html/images/p_ps3_6.gif +0 -0
- data/share/html/images/p_ps3_7.gif +0 -0
- data/share/html/images/p_ps4_0.gif +0 -0
- data/share/html/images/p_ps4_1.gif +0 -0
- data/share/html/images/p_ps4_2.gif +0 -0
- data/share/html/images/p_ps4_3.gif +0 -0
- data/share/html/images/p_ps4_4.gif +0 -0
- data/share/html/images/p_ps4_5.gif +0 -0
- data/share/html/images/p_ps4_6.gif +0 -0
- data/share/html/images/p_ps4_7.gif +0 -0
- data/share/html/images/p_ps5_0.gif +0 -0
- data/share/html/images/p_ps5_1.gif +0 -0
- data/share/html/images/p_ps5_2.gif +0 -0
- data/share/html/images/p_ps5_3.gif +0 -0
- data/share/html/images/p_ps5_4.gif +0 -0
- data/share/html/images/p_ps5_5.gif +0 -0
- data/share/html/images/p_ps5_6.gif +0 -0
- data/share/html/images/p_ps5_7.gif +0 -0
- data/share/html/images/p_ps5r_1.png +0 -0
- data/share/html/images/p_ps5r_3.png +0 -0
- data/share/html/images/p_ps6_0.gif +0 -0
- data/share/html/images/p_ps6_1.gif +0 -0
- data/share/html/images/p_ps6_2.gif +0 -0
- data/share/html/images/p_ps6_3.gif +0 -0
- data/share/html/images/p_ps6_4.gif +0 -0
- data/share/html/images/p_ps6_5.gif +0 -0
- data/share/html/images/p_ps6_6.gif +0 -0
- data/share/html/images/p_ps6_7.gif +0 -0
- data/share/html/images/p_ps7_0.gif +0 -0
- data/share/html/images/p_ps7_1.gif +0 -0
- data/share/html/images/p_ps7_2.gif +0 -0
- data/share/html/images/p_ps7_3.gif +0 -0
- data/share/html/images/p_ps7_4.gif +0 -0
- data/share/html/images/p_ps7_5.gif +0 -0
- data/share/html/images/p_ps7_6.gif +0 -0
- data/share/html/images/p_ps7_7.gif +0 -0
- data/share/html/images/p_ps8_0.gif +0 -0
- data/share/html/images/p_ps8_1.gif +0 -0
- data/share/html/images/p_ps8_2.gif +0 -0
- data/share/html/images/p_ps8_3.gif +0 -0
- data/share/html/images/p_ps8_4.gif +0 -0
- data/share/html/images/p_ps8_5.gif +0 -0
- data/share/html/images/p_ps8_6.gif +0 -0
- data/share/html/images/p_ps8_7.gif +0 -0
- data/share/html/images/p_ps9_0.gif +0 -0
- data/share/html/images/p_ps9_1.gif +0 -0
- data/share/html/images/p_ps9_2.gif +0 -0
- data/share/html/images/p_ps9_3.gif +0 -0
- data/share/html/images/p_ps9_4.gif +0 -0
- data/share/html/images/p_ps9_5.gif +0 -0
- data/share/html/images/p_ps9_6.gif +0 -0
- data/share/html/images/p_ps9_7.gif +0 -0
- data/share/html/images/p_ss1_0.gif +0 -0
- data/share/html/images/p_ss1_1.gif +0 -0
- data/share/html/images/p_ss1_2.gif +0 -0
- data/share/html/images/p_ss1_3.gif +0 -0
- data/share/html/images/p_ss1_4.gif +0 -0
- data/share/html/images/p_ss1_5.gif +0 -0
- data/share/html/images/p_ss1_6.gif +0 -0
- data/share/html/images/p_ss1_7.gif +0 -0
- data/share/html/images/p_ss2_0.gif +0 -0
- data/share/html/images/p_ss2_1.gif +0 -0
- data/share/html/images/p_ss2_2.gif +0 -0
- data/share/html/images/p_ss2_3.gif +0 -0
- data/share/html/images/p_ss2_4.gif +0 -0
- data/share/html/images/p_ss2_5.gif +0 -0
- data/share/html/images/p_ss2_6.gif +0 -0
- data/share/html/images/p_ss2_7.gif +0 -0
- data/share/html/images/p_ss3_0.gif +0 -0
- data/share/html/images/p_ss3_1.gif +0 -0
- data/share/html/images/p_ss3_2.gif +0 -0
- data/share/html/images/p_ss3_3.gif +0 -0
- data/share/html/images/p_ss3_4.gif +0 -0
- data/share/html/images/p_ss3_5.gif +0 -0
- data/share/html/images/p_ss3_6.gif +0 -0
- data/share/html/images/p_ss3_7.gif +0 -0
- data/share/html/images/p_ss4_0.gif +0 -0
- data/share/html/images/p_ss4_1.gif +0 -0
- data/share/html/images/p_ss4_2.gif +0 -0
- data/share/html/images/p_ss4_3.gif +0 -0
- data/share/html/images/p_ss4_4.gif +0 -0
- data/share/html/images/p_ss4_5.gif +0 -0
- data/share/html/images/p_ss4_6.gif +0 -0
- data/share/html/images/p_ss4_7.gif +0 -0
- data/share/html/images/p_ss5_0.gif +0 -0
- data/share/html/images/p_ss5_1.gif +0 -0
- data/share/html/images/p_ss5_2.gif +0 -0
- data/share/html/images/p_ss5_3.gif +0 -0
- data/share/html/images/p_ss5_4.gif +0 -0
- data/share/html/images/p_ss5_5.gif +0 -0
- data/share/html/images/p_ss5_6.gif +0 -0
- data/share/html/images/p_ss5_7.gif +0 -0
- data/share/html/images/p_ss5r_1.png +0 -0
- data/share/html/images/p_ss5r_3.png +0 -0
- data/share/html/images/p_ss6_0.gif +0 -0
- data/share/html/images/p_ss6_1.gif +0 -0
- data/share/html/images/p_ss6_2.gif +0 -0
- data/share/html/images/p_ss6_3.gif +0 -0
- data/share/html/images/p_ss6_4.gif +0 -0
- data/share/html/images/p_ss6_5.gif +0 -0
- data/share/html/images/p_ss6_6.gif +0 -0
- data/share/html/images/p_ss6_7.gif +0 -0
- data/share/html/images/p_ss7_0.gif +0 -0
- data/share/html/images/p_ss7_1.gif +0 -0
- data/share/html/images/p_ss7_2.gif +0 -0
- data/share/html/images/p_ss7_3.gif +0 -0
- data/share/html/images/p_ss7_4.gif +0 -0
- data/share/html/images/p_ss7_5.gif +0 -0
- data/share/html/images/p_ss7_6.gif +0 -0
- data/share/html/images/p_ss7_7.gif +0 -0
- data/share/html/images/p_ss8_0.gif +0 -0
- data/share/html/images/p_ss8_1.gif +0 -0
- data/share/html/images/p_ss8_2.gif +0 -0
- data/share/html/images/p_ss8_3.gif +0 -0
- data/share/html/images/p_ss8_4.gif +0 -0
- data/share/html/images/p_ss8_5.gif +0 -0
- data/share/html/images/p_ss8_6.gif +0 -0
- data/share/html/images/p_ss8_7.gif +0 -0
- data/share/html/images/p_ss9_0.gif +0 -0
- data/share/html/images/p_ss9_1.gif +0 -0
- data/share/html/images/p_ss9_2.gif +0 -0
- data/share/html/images/p_ss9_3.gif +0 -0
- data/share/html/images/p_ss9_4.gif +0 -0
- data/share/html/images/p_ss9_5.gif +0 -0
- data/share/html/images/p_ss9_6.gif +0 -0
- data/share/html/images/p_ss9_7.gif +0 -0
- data/share/html/js/archive_player.coffee +379 -0
- data/share/html/js/archive_player.js +505 -0
- data/share/html/js/dytem.coffee +83 -0
- data/share/html/js/dytem.js +128 -0
- data/share/html/js/jquery-1.7.2.min.js +4 -0
- data/share/html/views/archive_player.erb +61 -0
- metadata +435 -0
@@ -0,0 +1,171 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
require "mjai/pai"
|
5
|
+
|
6
|
+
|
7
|
+
module Mjai
|
8
|
+
|
9
|
+
class JSONizable
|
10
|
+
|
11
|
+
def self.define_fields(specs)
|
12
|
+
@@field_specs = specs
|
13
|
+
@@field_specs.each() do |name, type|
|
14
|
+
define_method(name) do
|
15
|
+
return @fields[name]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.from_json(json, game)
|
21
|
+
hash = JSON.parse(json)
|
22
|
+
begin
|
23
|
+
validate(hash.is_a?(Hash), "The response must be an object.")
|
24
|
+
fields = {}
|
25
|
+
for name, type in @@field_specs
|
26
|
+
plain = hash[name.to_s()]
|
27
|
+
next if plain == nil
|
28
|
+
fields[name] = plain_to_obj(plain, type, name.to_s(), game)
|
29
|
+
end
|
30
|
+
return new(fields)
|
31
|
+
rescue ValidationError => ex
|
32
|
+
raise(ValidationError, "%s JSON: %s" % [ex.message, json])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.plain_to_obj(plain, type, name, game)
|
37
|
+
case type
|
38
|
+
when :number
|
39
|
+
validate_class(plain, Integer, name)
|
40
|
+
return plain
|
41
|
+
when :string
|
42
|
+
validate_class(plain, String, name)
|
43
|
+
return plain
|
44
|
+
when :boolean
|
45
|
+
validate(
|
46
|
+
plain.is_a?(TrueClass) || plain.is_a?(FalseClass),
|
47
|
+
"#{name} must be either true or false.")
|
48
|
+
return plain
|
49
|
+
when :symbol
|
50
|
+
validate_class(plain, String, name)
|
51
|
+
validate(!plain.empty?, "#{name} must not be empty.")
|
52
|
+
return plain.intern()
|
53
|
+
when :player
|
54
|
+
validate_class(plain, Integer, name)
|
55
|
+
validate((0...4).include?(plain), "#{name} must be either 0, 1, 2 or 3.")
|
56
|
+
return game.players[plain]
|
57
|
+
when :pai
|
58
|
+
validate_class(plain, String, name)
|
59
|
+
begin
|
60
|
+
return Pai.new(plain)
|
61
|
+
rescue ArgumentError => ex
|
62
|
+
raise(ValidationError, "Error in %s: %s" % [name, ex.message])
|
63
|
+
end
|
64
|
+
when :yaku
|
65
|
+
validate_class(plain, Array, name)
|
66
|
+
validate(
|
67
|
+
plain.size == 2 && plain[0].is_a?(String) && plain[1].is_a?(Integer),
|
68
|
+
"#{name} must be an array of [String, Integer].")
|
69
|
+
validate(!plain[0].empty?, "#{name}[0] must not be empty.")
|
70
|
+
return [plain[0].intern(), plain[1]]
|
71
|
+
when :numbers
|
72
|
+
return plains_to_objs(plain, :number, name, game)
|
73
|
+
when :strings
|
74
|
+
return plains_to_objs(plain, :string, name, game)
|
75
|
+
when :booleans
|
76
|
+
return plains_to_objs(plain, :boolean, name, game)
|
77
|
+
when :symbols
|
78
|
+
return plains_to_objs(plain, :symbol, name, game)
|
79
|
+
when :pais
|
80
|
+
return plains_to_objs(plain, :pai, name, game)
|
81
|
+
when :pais_list
|
82
|
+
return plains_to_objs(plain, :pais, name, game)
|
83
|
+
when :yakus
|
84
|
+
return plains_to_objs(plain, :yaku, name, game)
|
85
|
+
else
|
86
|
+
raise("unknown type")
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.plains_to_objs(plains, type, name, game)
|
91
|
+
validate_class(plains, Array, name)
|
92
|
+
return plains.each_with_index().map() do |c, i|
|
93
|
+
plain_to_obj(c, type, "#{name}[#{i}]", game)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.validate(criterion, message)
|
98
|
+
raise(ValidationError, message) if !criterion
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.validate_class(plain, klass, name)
|
102
|
+
validate(plain.is_a?(klass), "%s must be %p." % [name, klass])
|
103
|
+
end
|
104
|
+
|
105
|
+
def initialize(fields)
|
106
|
+
for name, value in fields
|
107
|
+
if !@@field_specs.any?(){ |n, t| n == name }
|
108
|
+
raise(ArgumentError, "unknown field: %p" % name)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
@fields = fields
|
112
|
+
end
|
113
|
+
|
114
|
+
attr_reader(:fields)
|
115
|
+
|
116
|
+
def to_json()
|
117
|
+
hash = {}
|
118
|
+
for name, type in @@field_specs
|
119
|
+
obj = @fields[name]
|
120
|
+
next if obj == nil
|
121
|
+
case type
|
122
|
+
when :symbol, :pai
|
123
|
+
plain = obj.to_s()
|
124
|
+
when :player
|
125
|
+
plain = obj.id
|
126
|
+
when :symbols, :pais
|
127
|
+
plain = obj.map(){ |a| a.to_s() }
|
128
|
+
when :pais_list
|
129
|
+
plain = obj.map(){ |o| o.map(){ |a| a.to_s() } }
|
130
|
+
when :yakus
|
131
|
+
plain = obj.map(){ |s, n| [s.to_s(), n] }
|
132
|
+
when :number, :numbers, :string, :strings, :boolean, :booleans
|
133
|
+
plain = obj
|
134
|
+
else
|
135
|
+
raise("unknown type")
|
136
|
+
end
|
137
|
+
hash[name.to_s()] = plain
|
138
|
+
end
|
139
|
+
return JSON.dump(hash)
|
140
|
+
end
|
141
|
+
|
142
|
+
alias to_s to_json
|
143
|
+
|
144
|
+
def merge(hash)
|
145
|
+
fields = @fields.dup()
|
146
|
+
for name, value in hash
|
147
|
+
if !@@field_specs.any?(){ |n, t| n == name }
|
148
|
+
raise(ArgumentError, "unknown field: %p" % k)
|
149
|
+
end
|
150
|
+
if value == nil
|
151
|
+
fields.delete(name)
|
152
|
+
else
|
153
|
+
fields[name] = value
|
154
|
+
end
|
155
|
+
end
|
156
|
+
return self.class.new(fields)
|
157
|
+
end
|
158
|
+
|
159
|
+
def ==(other)
|
160
|
+
return self.class == other.class && @fields == other.fields
|
161
|
+
end
|
162
|
+
|
163
|
+
alias eql? ==
|
164
|
+
|
165
|
+
def hash
|
166
|
+
return @fields.hash
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
data/lib/mjai/mentsu.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require "mjai/with_fields"
|
2
|
+
|
3
|
+
|
4
|
+
module Mjai
|
5
|
+
|
6
|
+
class Mentsu
|
7
|
+
|
8
|
+
extend(WithFields)
|
9
|
+
include(Comparable)
|
10
|
+
|
11
|
+
# type: :shuntsu, :kotsu, :toitsu, :ryanmen, :kanchan, :penchan, :single
|
12
|
+
# visibility: :an, :min
|
13
|
+
define_fields([:pais, :type, :visibility])
|
14
|
+
|
15
|
+
def initialize(fields)
|
16
|
+
@fields = fields
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader(:fields)
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
return "\#<%p %p>" % [self.class, @fields]
|
23
|
+
end
|
24
|
+
|
25
|
+
def ==(other)
|
26
|
+
return self.class == other.class && @fields == other.fields
|
27
|
+
end
|
28
|
+
|
29
|
+
alias eql? ==
|
30
|
+
|
31
|
+
def hash()
|
32
|
+
return @fields.hash()
|
33
|
+
end
|
34
|
+
|
35
|
+
def <=>(other)
|
36
|
+
if self.class == other.class
|
37
|
+
return Mentsu.field_names.map(){ |s| @fields[s] } <=>
|
38
|
+
Mentsu.field_names.map(){ |s| other.fields[s] }
|
39
|
+
else
|
40
|
+
raise(ArgumentError, "invalid comparison")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require "optparse"
|
2
|
+
|
3
|
+
require "mjai/tcp_game_server"
|
4
|
+
require "mjai/tcp_client_game"
|
5
|
+
require "mjai/tsumogiri_player"
|
6
|
+
require "mjai/shanten_player"
|
7
|
+
require "mjai/file_converter"
|
8
|
+
|
9
|
+
|
10
|
+
module Mjai
|
11
|
+
|
12
|
+
class MjaiCommand
|
13
|
+
|
14
|
+
def self.execute(command_name, argv)
|
15
|
+
|
16
|
+
Thread.abort_on_exception = true
|
17
|
+
case command_name
|
18
|
+
|
19
|
+
when "mjai"
|
20
|
+
|
21
|
+
action = argv.shift()
|
22
|
+
opts = OptionParser.getopts(argv, "",
|
23
|
+
"port:11600", "host:127.0.0.1", "room:default", "game_type:one_kyoku",
|
24
|
+
"games:1", "repeat", "log_dir:")
|
25
|
+
case action
|
26
|
+
when "server"
|
27
|
+
$stdout.sync = true
|
28
|
+
if opts["repeat"]
|
29
|
+
num_games = 1.0/0.0
|
30
|
+
else
|
31
|
+
num_games = opts["games"].to_i()
|
32
|
+
end
|
33
|
+
server = TCPGameServer.new({
|
34
|
+
:host => opts["host"],
|
35
|
+
:port => opts["port"].to_i(),
|
36
|
+
:room => opts["room"],
|
37
|
+
:game_type => opts["game_type"].intern,
|
38
|
+
:player_commands => argv,
|
39
|
+
:num_games => num_games,
|
40
|
+
:log_dir => opts["log_dir"],
|
41
|
+
})
|
42
|
+
server.run()
|
43
|
+
when "convert"
|
44
|
+
FileConverter.new().convert(argv.shift(), argv.shift())
|
45
|
+
else
|
46
|
+
$stderr.puts(
|
47
|
+
"Usage:\n" +
|
48
|
+
" #{$PROGRAM_NAME} server --port=PORT " +
|
49
|
+
"[PLAYER1_COMMAND] [PLAYER2_COMMAND] [...]\n" +
|
50
|
+
" #{$PROGRAM_NAME} convert hoge.mjson hoge.html\n" +
|
51
|
+
" #{$PROGRAM_NAME} convert hoge.mjlog hoge.mjson\n")
|
52
|
+
exit(1)
|
53
|
+
end
|
54
|
+
|
55
|
+
when /^mjai-(.+)$/
|
56
|
+
|
57
|
+
$stdout.sync = true
|
58
|
+
$stderr.sync = true
|
59
|
+
player_type = $1
|
60
|
+
opts = OptionParser.getopts(argv, "", "t:", "name:")
|
61
|
+
url = ARGV.shift()
|
62
|
+
|
63
|
+
if !url
|
64
|
+
$stderr.puts(
|
65
|
+
"Usage:\n" +
|
66
|
+
" #{$PROGRAM_NAME} mjsonp://localhost:11600/default\n")
|
67
|
+
exit(1)
|
68
|
+
end
|
69
|
+
case player_type
|
70
|
+
when "tsumogiri"
|
71
|
+
player = TsumogiriPlayer.new()
|
72
|
+
when "shanten"
|
73
|
+
player = Mjai::ShantenPlayer.new({:use_furo => opts["t"] == "f"})
|
74
|
+
else
|
75
|
+
raise("should not happen")
|
76
|
+
end
|
77
|
+
game = TCPClientGame.new({
|
78
|
+
:player => player,
|
79
|
+
:url => url,
|
80
|
+
:name => opts["name"] || player_type,
|
81
|
+
})
|
82
|
+
game.play()
|
83
|
+
|
84
|
+
else
|
85
|
+
raise("should not happen")
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "mjai/archive"
|
2
|
+
require "mjai/puppet_player"
|
3
|
+
require "mjai/action"
|
4
|
+
|
5
|
+
|
6
|
+
module Mjai
|
7
|
+
|
8
|
+
class MjsonArchive < Archive
|
9
|
+
|
10
|
+
def initialize(path)
|
11
|
+
super()
|
12
|
+
@path = path
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader(:path)
|
16
|
+
|
17
|
+
def play()
|
18
|
+
File.foreach(@path) do |line|
|
19
|
+
do_action(Action.from_json(line.chomp(), self))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
data/lib/mjai/pai.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
module Mjai
|
2
|
+
|
3
|
+
class Pai
|
4
|
+
|
5
|
+
include(Comparable)
|
6
|
+
|
7
|
+
TSUPAI_STRS = " ESWNPFC".split(//)
|
8
|
+
|
9
|
+
def self.parse_pais(str)
|
10
|
+
type = nil
|
11
|
+
pais = []
|
12
|
+
red = false
|
13
|
+
str.gsub(/\s+/, "").split(//).reverse_each() do |ch|
|
14
|
+
next if ch =~ /^\s$/
|
15
|
+
if ch =~ /^[mps]$/
|
16
|
+
type = ch
|
17
|
+
elsif ch =~ /^[1-9]$/
|
18
|
+
raise(ArgumentError, "type required after number") if !type
|
19
|
+
pais.push(Pai.new(type, ch.to_i(), red))
|
20
|
+
red = false
|
21
|
+
elsif TSUPAI_STRS.include?(ch)
|
22
|
+
pais.push(Pai.new(ch))
|
23
|
+
elsif ch == "r"
|
24
|
+
red = true
|
25
|
+
else
|
26
|
+
raise(ArgumentError, "unexpected character: %s", ch)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
return pais.reverse()
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.dump_pais(pais)
|
33
|
+
return pais.map(){ |pai| "%-3s" % pai }.join("")
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(*args)
|
37
|
+
case args.size
|
38
|
+
when 1
|
39
|
+
str = args[0]
|
40
|
+
if str == "?"
|
41
|
+
@type = @number = nil
|
42
|
+
@red = false
|
43
|
+
elsif str =~ /\A([1-9])([mps])(r)?\z/
|
44
|
+
@type = $2
|
45
|
+
@number = $1.to_i()
|
46
|
+
@red = $3 != nil
|
47
|
+
elsif number = TSUPAI_STRS.index(str)
|
48
|
+
@type = "t"
|
49
|
+
@number = number
|
50
|
+
@red = false
|
51
|
+
else
|
52
|
+
raise(ArgumentError, "Unknown pai string: %s" % str)
|
53
|
+
end
|
54
|
+
when 2, 3
|
55
|
+
(@type, @number, @red) = args
|
56
|
+
@red = false if @red == nil
|
57
|
+
else
|
58
|
+
raise(ArgumentError, "Wrong number of args.")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_s()
|
63
|
+
if !@type
|
64
|
+
return "?"
|
65
|
+
elsif @type == "t"
|
66
|
+
return TSUPAI_STRS[@number]
|
67
|
+
else
|
68
|
+
return "%d%s%s" % [@number, @type, @red ? "r" : ""]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def inspect
|
73
|
+
return "Pai[%s]" % self.to_s()
|
74
|
+
end
|
75
|
+
|
76
|
+
attr_reader(:type, :number)
|
77
|
+
|
78
|
+
def red?
|
79
|
+
return @red
|
80
|
+
end
|
81
|
+
|
82
|
+
def yaochu?
|
83
|
+
return @type == "t" || @number == 1 || @number == 9
|
84
|
+
end
|
85
|
+
|
86
|
+
def fonpai?
|
87
|
+
return @type == "t" && (1..4).include?(@number)
|
88
|
+
end
|
89
|
+
|
90
|
+
def sangenpai?
|
91
|
+
return @type == "t" && (5..7).include?(@number)
|
92
|
+
end
|
93
|
+
|
94
|
+
def data
|
95
|
+
return [@type || "", @number || -1, @red ? 1 : 0]
|
96
|
+
end
|
97
|
+
|
98
|
+
def ==(other)
|
99
|
+
return self.class == other.class && self.data == other.data
|
100
|
+
end
|
101
|
+
|
102
|
+
alias eql? ==
|
103
|
+
|
104
|
+
def hash()
|
105
|
+
return self.data.hash()
|
106
|
+
end
|
107
|
+
|
108
|
+
def <=>(other)
|
109
|
+
if self.class == other.class
|
110
|
+
return self.data <=> other.data
|
111
|
+
else
|
112
|
+
raise(ArgumentError, "invalid comparison")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def remove_red()
|
117
|
+
return Pai.new(@type, @number)
|
118
|
+
end
|
119
|
+
|
120
|
+
def same_symbol?(other)
|
121
|
+
return @type == other.type && @number == other.number
|
122
|
+
end
|
123
|
+
|
124
|
+
# Next pai in terms of dora derivation.
|
125
|
+
def succ
|
126
|
+
if (@type == "t" && @number == 7) || (@type != "t" && @number == 9)
|
127
|
+
number = 1
|
128
|
+
else
|
129
|
+
number = @number + 1
|
130
|
+
end
|
131
|
+
return Pai.new(@type, number)
|
132
|
+
end
|
133
|
+
|
134
|
+
UNKNOWN = Pai.new(nil, nil)
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|