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
data/bin/mjai
ADDED
data/bin/mjai-shanten
ADDED
data/bin/mjai-tsumogiri
ADDED
data/lib/mjai/action.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require "mjai/jsonizable"
|
2
|
+
|
3
|
+
|
4
|
+
module Mjai
|
5
|
+
|
6
|
+
class Action < JSONizable
|
7
|
+
|
8
|
+
define_fields([
|
9
|
+
[:type, :symbol],
|
10
|
+
[:reason, :symbol],
|
11
|
+
[:actor, :player],
|
12
|
+
[:target, :player],
|
13
|
+
[:pai, :pai],
|
14
|
+
[:consumed, :pais],
|
15
|
+
[:pais, :pais],
|
16
|
+
[:tsumogiri, :boolean],
|
17
|
+
[:id, :number],
|
18
|
+
[:bakaze, :pai],
|
19
|
+
[:kyoku, :number],
|
20
|
+
[:honba, :number],
|
21
|
+
[:oya, :player],
|
22
|
+
[:dora_marker, :pai],
|
23
|
+
[:uradora_markers, :pais],
|
24
|
+
[:tehais, :pais_list],
|
25
|
+
[:uri, :string],
|
26
|
+
[:names, :strings],
|
27
|
+
[:hora_tehais, :pais],
|
28
|
+
[:yakus, :yakus],
|
29
|
+
[:fu, :number],
|
30
|
+
[:fan, :number],
|
31
|
+
[:hora_points, :number],
|
32
|
+
[:tenpais, :booleans],
|
33
|
+
[:deltas, :numbers],
|
34
|
+
[:scores, :numbers],
|
35
|
+
[:text, :string],
|
36
|
+
[:message, :string],
|
37
|
+
])
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,230 @@
|
|
1
|
+
require "mjai/game"
|
2
|
+
require "mjai/action"
|
3
|
+
require "mjai/hora"
|
4
|
+
require "mjai/validation_error"
|
5
|
+
|
6
|
+
|
7
|
+
module Mjai
|
8
|
+
|
9
|
+
class ActiveGame < Game
|
10
|
+
|
11
|
+
ACTION_PREFERENCES = {
|
12
|
+
:hora => 3,
|
13
|
+
:pon => 2,
|
14
|
+
:daiminkan => 2,
|
15
|
+
:chi => 1,
|
16
|
+
}
|
17
|
+
|
18
|
+
def initialize(players)
|
19
|
+
super(players.shuffle())
|
20
|
+
@game_type = :one_kyoku
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_accessor(:game_type)
|
24
|
+
|
25
|
+
def play()
|
26
|
+
begin
|
27
|
+
do_action({:type => :start_game, :names => self.players.map(){ |pl| pl.name }})
|
28
|
+
@ag_oya = @ag_chicha = @players[0]
|
29
|
+
@ag_bakaze = Pai.new("E")
|
30
|
+
@ag_honba = 0
|
31
|
+
while !self.game_finished?
|
32
|
+
play_kyoku()
|
33
|
+
end
|
34
|
+
do_action({:type => :end_game})
|
35
|
+
return true
|
36
|
+
rescue ValidationError => ex
|
37
|
+
do_action({:type => :error, :message => ex.message})
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def play_kyoku()
|
43
|
+
catch(:end_kyoku) do
|
44
|
+
@pipais = @all_pais.shuffle()
|
45
|
+
@pipais.shuffle!()
|
46
|
+
@wanpais = @pipais.pop(14)
|
47
|
+
dora_marker = @wanpais.pop()
|
48
|
+
tehais = Array.new(4){ @pipais.pop(13).sort() }
|
49
|
+
do_action({
|
50
|
+
:type => :start_kyoku,
|
51
|
+
:bakaze => @ag_bakaze,
|
52
|
+
:kyoku => (4 + @ag_oya.id - @ag_chicha.id) % 4 + 1,
|
53
|
+
:honba => @ag_honba,
|
54
|
+
:oya => @ag_oya,
|
55
|
+
:dora_marker => dora_marker,
|
56
|
+
:tehais => tehais,
|
57
|
+
})
|
58
|
+
@actor = self.oya
|
59
|
+
while !@pipais.empty?
|
60
|
+
mota()
|
61
|
+
@actor = @players[(@actor.id + 1) % 4]
|
62
|
+
end
|
63
|
+
process_ryukyoku()
|
64
|
+
end
|
65
|
+
do_action({:type => :end_kyoku})
|
66
|
+
end
|
67
|
+
|
68
|
+
# 摸打
|
69
|
+
def mota()
|
70
|
+
reach = false
|
71
|
+
tsumo_actor = @actor
|
72
|
+
actions = [Action.new({:type => :tsumo, :actor => @actor, :pai => @pipais.pop()})]
|
73
|
+
while !actions.empty?
|
74
|
+
if actions[0].type == :hora
|
75
|
+
# actions.size >= 2 in case of double/triple ron
|
76
|
+
process_hora(actions)
|
77
|
+
throw(:end_kyoku)
|
78
|
+
else
|
79
|
+
raise("should not happen") if actions.size != 1
|
80
|
+
action = actions[0]
|
81
|
+
responses = do_action(action)
|
82
|
+
case action.type
|
83
|
+
when :daiminkan, :kakan, :ankan
|
84
|
+
# Actually takes one from wanpai and moves one pai from pipai to wanpai,
|
85
|
+
# but it's equivalent to taking from pipai.
|
86
|
+
actions =
|
87
|
+
[Action.new({:type => :tsumo, :actor => action.actor, :pai => @pipais.pop()})]
|
88
|
+
# TODO Add dora.
|
89
|
+
next
|
90
|
+
when :reach
|
91
|
+
reach = true
|
92
|
+
end
|
93
|
+
actions = choose_actions(responses)
|
94
|
+
if reach && (actions.empty? || ![:dahai, :hora].include?(actions[0].type))
|
95
|
+
deltas = [0, 0, 0, 0]
|
96
|
+
deltas[tsumo_actor.id] = -1000
|
97
|
+
do_action({
|
98
|
+
:type => :reach_accepted,:actor => tsumo_actor,
|
99
|
+
:deltas => deltas,
|
100
|
+
:scores => get_scores(deltas),
|
101
|
+
})
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def update_state(action)
|
108
|
+
super(action)
|
109
|
+
if action.type == :tsumo && @pipais.size != self.num_pipais
|
110
|
+
raise("num pipais mismatch: %p != %p" % [@pipais.size, self.num_pipais])
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def choose_actions(actions)
|
115
|
+
actions = actions.select(){ |a| a }
|
116
|
+
max_pref = actions.map(){ |a| ACTION_PREFERENCES[a.type] || 0 }.max
|
117
|
+
max_actions = actions.select(){ |a| (ACTION_PREFERENCES[a.type] || 0) == max_pref }
|
118
|
+
return max_actions
|
119
|
+
end
|
120
|
+
|
121
|
+
def process_hora(actions)
|
122
|
+
# TODO ダブロンの上家取り
|
123
|
+
for action in actions
|
124
|
+
hora = get_hora(action)
|
125
|
+
raise("no yaku") if !hora.valid?
|
126
|
+
deltas = [0, 0, 0, 0]
|
127
|
+
# TODO 積み棒
|
128
|
+
deltas[action.actor.id] += hora.points + self.honba * 300
|
129
|
+
deltas[action.actor.id] += self.players.select(){ |pl| pl.reach? }.size * 1000
|
130
|
+
if hora.hora_type == :tsumo
|
131
|
+
for player in self.players
|
132
|
+
next if player == action.actor
|
133
|
+
deltas[player.id] -=
|
134
|
+
((player == self.oya ? hora.oya_payment : hora.ko_payment) + self.honba * 100)
|
135
|
+
end
|
136
|
+
else
|
137
|
+
deltas[action.target.id] -= (hora.points + self.honba * 300)
|
138
|
+
end
|
139
|
+
do_action({
|
140
|
+
:type => action.type,
|
141
|
+
:actor => action.actor,
|
142
|
+
:target => action.target,
|
143
|
+
:pai => action.pai,
|
144
|
+
:hora_tehais => action.actor.tehais,
|
145
|
+
:yakus => hora.yakus,
|
146
|
+
:fu => hora.fu,
|
147
|
+
:fan => hora.fan,
|
148
|
+
:hora_points => hora.points,
|
149
|
+
:deltas => deltas,
|
150
|
+
:scores => get_scores(deltas),
|
151
|
+
})
|
152
|
+
end
|
153
|
+
update_oya(actions.any?(){ |a| a.actor == self.oya }, false)
|
154
|
+
end
|
155
|
+
|
156
|
+
def process_ryukyoku()
|
157
|
+
tenpais = []
|
158
|
+
tehais = []
|
159
|
+
for player in players
|
160
|
+
if player.tenpai?
|
161
|
+
tenpais.push(true)
|
162
|
+
tehais.push(player.tehais)
|
163
|
+
else
|
164
|
+
tenpais.push(false)
|
165
|
+
tehais.push([Pai::UNKNOWN] * player.tehais.size)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
tenpai_ids = (0...4).select(){ |i| tenpais[i] }
|
169
|
+
noten_ids = (0...4).select(){ |i| !tenpais[i] }
|
170
|
+
deltas = [0, 0, 0, 0]
|
171
|
+
if (1..3).include?(tenpai_ids.size)
|
172
|
+
for id in tenpai_ids
|
173
|
+
deltas[id] += 3000 / tenpai_ids.size
|
174
|
+
end
|
175
|
+
for id in noten_ids
|
176
|
+
deltas[id] -= 3000 / noten_ids.size
|
177
|
+
end
|
178
|
+
end
|
179
|
+
do_action({
|
180
|
+
:type => :ryukyoku,
|
181
|
+
:reason => :fanpai,
|
182
|
+
:tenpais => tenpais,
|
183
|
+
:tehais => tehais,
|
184
|
+
:deltas => deltas,
|
185
|
+
:scores => get_scores(deltas),
|
186
|
+
})
|
187
|
+
update_oya(tenpais[self.oya.id], true)
|
188
|
+
end
|
189
|
+
|
190
|
+
def update_oya(renchan, ryukyoku)
|
191
|
+
if renchan
|
192
|
+
@ag_oya = self.oya
|
193
|
+
else
|
194
|
+
if self.oya == @players[3]
|
195
|
+
@ag_bakaze = @ag_bakaze.succ
|
196
|
+
if (@game_type == :tonpu && @ag_bakaze == Pai.new("S")) ||
|
197
|
+
(@game_type == :tonnan && @ag_bakaze == Pai.new("W"))
|
198
|
+
# TODO Consider 南入, etc.
|
199
|
+
@last = true
|
200
|
+
end
|
201
|
+
end
|
202
|
+
@ag_oya = @players[(self.oya.id + 1) % 4]
|
203
|
+
end
|
204
|
+
if renchan || ryukyoku
|
205
|
+
@ag_honba += 1
|
206
|
+
else
|
207
|
+
@ag_honba = 0
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def game_finished?
|
212
|
+
if @last
|
213
|
+
return true
|
214
|
+
else
|
215
|
+
@last = true if @game_type == :one_kyoku
|
216
|
+
return false
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def expect_response_from?(player)
|
221
|
+
return true
|
222
|
+
end
|
223
|
+
|
224
|
+
def get_scores(deltas)
|
225
|
+
return (0...4).map(){ |i| self.players[i].score + deltas[i] }
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
data/lib/mjai/archive.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require "mjai/game"
|
2
|
+
|
3
|
+
|
4
|
+
module Mjai
|
5
|
+
|
6
|
+
autoload(:TenhouArchive, "mjai/tenhou_archive")
|
7
|
+
autoload(:MjsonArchive, "mjai/mjson_archive")
|
8
|
+
|
9
|
+
class Archive < Game
|
10
|
+
|
11
|
+
def self.load(path)
|
12
|
+
case File.extname(path)
|
13
|
+
when ".mjlog"
|
14
|
+
return TenhouArchive.new(path)
|
15
|
+
when ".mjson"
|
16
|
+
return MjsonArchive.new(path)
|
17
|
+
else
|
18
|
+
raise("unknown format")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize()
|
23
|
+
super((0...4).map(){ PuppetPlayer.new() })
|
24
|
+
@actions = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def each_action(&block)
|
28
|
+
if block
|
29
|
+
on_action(&block)
|
30
|
+
play()
|
31
|
+
else
|
32
|
+
return enum_for(:each_action)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def actions
|
37
|
+
return @actions ||= self.each_action.to_a()
|
38
|
+
end
|
39
|
+
|
40
|
+
def expect_response_from?(player)
|
41
|
+
return false
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "mjai/player"
|
2
|
+
require "mjai/archive"
|
3
|
+
|
4
|
+
|
5
|
+
module Mjai
|
6
|
+
|
7
|
+
class ArchivePlayer < Player
|
8
|
+
|
9
|
+
def initialize(archive_path)
|
10
|
+
super()
|
11
|
+
@archive = Archive.load(archive_path)
|
12
|
+
@action_index = 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def update_state(action)
|
16
|
+
super(action)
|
17
|
+
expected_action = @archive.actions[@action_index]
|
18
|
+
if action.type == :start_game
|
19
|
+
action = action.merge({:id => nil})
|
20
|
+
expected_action = expected_action.merge({:id => nil})
|
21
|
+
end
|
22
|
+
if action.to_json() != expected_action.to_json()
|
23
|
+
raise((
|
24
|
+
"live action doesn't match one in archive\n" +
|
25
|
+
"actual: %s\n" +
|
26
|
+
"expected: %s\n") %
|
27
|
+
[action, expected_action])
|
28
|
+
end
|
29
|
+
@action_index += 1
|
30
|
+
end
|
31
|
+
|
32
|
+
def respond_to_action(action)
|
33
|
+
next_action = @archive.actions[@action_index]
|
34
|
+
if next_action &&
|
35
|
+
next_action.actor &&
|
36
|
+
next_action.actor.id == 0 &&
|
37
|
+
[:dahai, :chi, :pon, :daiminkan, :kakan, :ankan, :reach, :hora].include?(
|
38
|
+
next_action.type)
|
39
|
+
return Action.from_json(next_action.to_json(), self.game)
|
40
|
+
else
|
41
|
+
return nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/lib/mjai/context.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require "mjai/with_fields"
|
2
|
+
|
3
|
+
|
4
|
+
module Mjai
|
5
|
+
|
6
|
+
# Context of the game which affects hora yaku and points.
|
7
|
+
class Context
|
8
|
+
|
9
|
+
extend(WithFields)
|
10
|
+
|
11
|
+
define_fields([
|
12
|
+
:oya, :bakaze, :jikaze, :doras, :uradoras,
|
13
|
+
:reach, :double_reach, :ippatsu,
|
14
|
+
:rinshan, :haitei, :first_turn, :chankan,
|
15
|
+
])
|
16
|
+
|
17
|
+
def initialize(fields)
|
18
|
+
@fields = fields
|
19
|
+
end
|
20
|
+
|
21
|
+
def fanpai_fan(pai)
|
22
|
+
if pai.sangenpai?
|
23
|
+
return 1
|
24
|
+
else
|
25
|
+
fan = 0
|
26
|
+
fan += 1 if pai == self.bakaze
|
27
|
+
fan += 1 if pai == self.jikaze
|
28
|
+
return fan
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require "erb"
|
2
|
+
require "fileutils"
|
3
|
+
|
4
|
+
require "mjai/archive"
|
5
|
+
|
6
|
+
|
7
|
+
module Mjai
|
8
|
+
|
9
|
+
class FileConverter
|
10
|
+
|
11
|
+
include(ERB::Util)
|
12
|
+
|
13
|
+
def convert(src_path, dest_path)
|
14
|
+
src_ext = File.extname(src_path)
|
15
|
+
dest_ext = File.extname(dest_path)
|
16
|
+
case [src_ext, dest_ext]
|
17
|
+
when [".mjson", ".html"]
|
18
|
+
mjson_to_html(src_path, dest_path)
|
19
|
+
when [".mjlog", ".xml"]
|
20
|
+
archive = Archive.load(src_path)
|
21
|
+
open(dest_path, "w"){ |f| f.write(archive.xml) }
|
22
|
+
when [".mjson", ".human"], [".mjlog", ".human"]
|
23
|
+
dump_archive(src_path, dest_path, :human)
|
24
|
+
when [".mjlog", ".mjson"]
|
25
|
+
dump_archive(src_path, dest_path, :mjson)
|
26
|
+
else
|
27
|
+
raise("unsupported ext pair: #{src_ext}, #{dest_ext}")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def dump_archive(archive_path, output_path, output_format)
|
32
|
+
archive = Archive.load(archive_path)
|
33
|
+
open(output_path, "w") do |f|
|
34
|
+
archive.on_action() do |action|
|
35
|
+
if output_format == :human
|
36
|
+
archive.dump_action(action, f)
|
37
|
+
else
|
38
|
+
f.puts(action.to_json())
|
39
|
+
end
|
40
|
+
end
|
41
|
+
archive.play()
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def mjson_to_html(mjson_path, html_path)
|
46
|
+
|
47
|
+
res_dir = File.dirname(__FILE__) + "/../../share/html"
|
48
|
+
make("#{res_dir}/js/archive_player.coffee",
|
49
|
+
"#{res_dir}/js/archive_player.js",
|
50
|
+
"coffee -cb #{res_dir}/js/archive_player.coffee")
|
51
|
+
make("#{res_dir}/js/dytem.coffee",
|
52
|
+
"#{res_dir}/js/dytem.js",
|
53
|
+
"coffee -cb #{res_dir}/js/dytem.coffee")
|
54
|
+
make("#{res_dir}/css/style.scss",
|
55
|
+
"#{res_dir}/css/style.css",
|
56
|
+
"sass #{res_dir}/css/style.scss #{res_dir}/css/style.css")
|
57
|
+
|
58
|
+
# Variants used in template.
|
59
|
+
action_jsons = File.readlines(mjson_path).map(){ |s| s.chomp().gsub(/\//){ "\\/" } }
|
60
|
+
actions_json = "[%s]" % action_jsons.join(",\n")
|
61
|
+
base_name = File.basename(html_path)
|
62
|
+
|
63
|
+
html = ERB.new(File.read("#{res_dir}/views/archive_player.erb"), nil, "<>").
|
64
|
+
result(binding)
|
65
|
+
open(html_path, "w"){ |f| f.write(html) }
|
66
|
+
for src_path in Dir["#{res_dir}/css/*.css"] + Dir["#{res_dir}/js/*.js"]
|
67
|
+
exp = Regexp.new("^%s\\/" % Regexp.escape(res_dir))
|
68
|
+
dest_path = src_path.gsub(exp){ "#{html_path}.files/" }
|
69
|
+
FileUtils.mkdir_p(File.dirname(dest_path))
|
70
|
+
FileUtils.cp(src_path, dest_path)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
def make(src_path, dest_path, command)
|
76
|
+
if File.mtime(src_path) > File.mtime(dest_path)
|
77
|
+
puts(command)
|
78
|
+
if !system(command)
|
79
|
+
exit(1)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|