console-shogi 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +19 -0
- data/exe/console-shogi +7 -0
- data/images/active/gote/fu.png +0 -0
- data/images/active/gote/gin.png +0 -0
- data/images/active/gote/gyoku.png +0 -0
- data/images/active/gote/hisha.png +0 -0
- data/images/active/gote/kaku.png +0 -0
- data/images/active/gote/keima.png +0 -0
- data/images/active/gote/kin.png +0 -0
- data/images/active/gote/kyosha.png +0 -0
- data/images/active/gote/narigin.png +0 -0
- data/images/active/gote/narikei.png +0 -0
- data/images/active/gote/narikyo.png +0 -0
- data/images/active/gote/ohsho.png +0 -0
- data/images/active/gote/ryu.png +0 -0
- data/images/active/gote/tokin.png +0 -0
- data/images/active/gote/uma.png +0 -0
- data/images/active/nonepiece.png +0 -0
- data/images/active/sente/fu.png +0 -0
- data/images/active/sente/gin.png +0 -0
- data/images/active/sente/gyoku.png +0 -0
- data/images/active/sente/hisha.png +0 -0
- data/images/active/sente/kaku.png +0 -0
- data/images/active/sente/keima.png +0 -0
- data/images/active/sente/kin.png +0 -0
- data/images/active/sente/kyosha.png +0 -0
- data/images/active/sente/narigin.png +0 -0
- data/images/active/sente/narikei.png +0 -0
- data/images/active/sente/narikyo.png +0 -0
- data/images/active/sente/ohsho.png +0 -0
- data/images/active/sente/ryu.png +0 -0
- data/images/active/sente/tokin.png +0 -0
- data/images/active/sente/uma.png +0 -0
- data/images/focused/gote/fu.png +0 -0
- data/images/focused/gote/gin.png +0 -0
- data/images/focused/gote/gyoku.png +0 -0
- data/images/focused/gote/hisha.png +0 -0
- data/images/focused/gote/kaku.png +0 -0
- data/images/focused/gote/keima.png +0 -0
- data/images/focused/gote/kin.png +0 -0
- data/images/focused/gote/kyosha.png +0 -0
- data/images/focused/gote/narigin.png +0 -0
- data/images/focused/gote/narikei.png +0 -0
- data/images/focused/gote/narikyo.png +0 -0
- data/images/focused/gote/ohsho.png +0 -0
- data/images/focused/gote/ryu.png +0 -0
- data/images/focused/gote/tokin.png +0 -0
- data/images/focused/gote/uma.png +0 -0
- data/images/focused/nonepiece.png +0 -0
- data/images/focused/sente/fu.png +0 -0
- data/images/focused/sente/gin.png +0 -0
- data/images/focused/sente/gyoku.png +0 -0
- data/images/focused/sente/hisha.png +0 -0
- data/images/focused/sente/kaku.png +0 -0
- data/images/focused/sente/keima.png +0 -0
- data/images/focused/sente/kin.png +0 -0
- data/images/focused/sente/kyosha.png +0 -0
- data/images/focused/sente/narigin.png +0 -0
- data/images/focused/sente/narikei.png +0 -0
- data/images/focused/sente/narikyo.png +0 -0
- data/images/focused/sente/ohsho.png +0 -0
- data/images/focused/sente/ryu.png +0 -0
- data/images/focused/sente/tokin.png +0 -0
- data/images/focused/sente/uma.png +0 -0
- data/lib/console_shogi/board.rb +5 -9
- data/lib/console_shogi/game.rb +118 -19
- data/lib/console_shogi/komadai.rb +11 -9
- data/lib/console_shogi/new_board_builder.rb +41 -41
- data/lib/console_shogi/piece.rb +34 -23
- data/lib/console_shogi/piece_movement_checker.rb +46 -0
- data/lib/console_shogi/piece_mover.rb +31 -48
- data/lib/console_shogi/piece_mover_on_komadai.rb +30 -36
- data/lib/console_shogi/player.rb +5 -13
- data/lib/console_shogi/teban.rb +8 -0
- data/lib/console_shogi/terminal/cursor.rb +80 -0
- data/lib/console_shogi/terminal/display_area.rb +30 -0
- data/lib/console_shogi/terminal/operator.rb +209 -0
- data/lib/console_shogi/version.rb +1 -1
- data/lib/console_shogi.rb +6 -1
- metadata +105 -38
- data/images/gote/nonepiece.png +0 -0
- data/lib/console_shogi/terminal_operator.rb +0 -211
- /data/images/{gote/shori.png → gote_shori.png} +0 -0
- /data/images/{gote → nomal/gote}/fu.png +0 -0
- /data/images/{gote → nomal/gote}/gin.png +0 -0
- /data/images/{gote → nomal/gote}/gyoku.png +0 -0
- /data/images/{gote → nomal/gote}/hisha.png +0 -0
- /data/images/{gote → nomal/gote}/kaku.png +0 -0
- /data/images/{gote → nomal/gote}/keima.png +0 -0
- /data/images/{gote → nomal/gote}/kin.png +0 -0
- /data/images/{gote → nomal/gote}/kyosha.png +0 -0
- /data/images/{gote → nomal/gote}/narigin.png +0 -0
- /data/images/{gote → nomal/gote}/narikei.png +0 -0
- /data/images/{gote → nomal/gote}/narikyo.png +0 -0
- /data/images/{gote → nomal/gote}/ohsho.png +0 -0
- /data/images/{gote → nomal/gote}/ryu.png +0 -0
- /data/images/{gote → nomal/gote}/tokin.png +0 -0
- /data/images/{gote → nomal/gote}/uma.png +0 -0
- /data/images/{sente → nomal}/nonepiece.png +0 -0
- /data/images/{sente → nomal/sente}/fu.png +0 -0
- /data/images/{sente → nomal/sente}/gin.png +0 -0
- /data/images/{sente → nomal/sente}/gyoku.png +0 -0
- /data/images/{sente → nomal/sente}/hisha.png +0 -0
- /data/images/{sente → nomal/sente}/kaku.png +0 -0
- /data/images/{sente → nomal/sente}/keima.png +0 -0
- /data/images/{sente → nomal/sente}/kin.png +0 -0
- /data/images/{sente → nomal/sente}/kyosha.png +0 -0
- /data/images/{sente → nomal/sente}/narigin.png +0 -0
- /data/images/{sente → nomal/sente}/narikei.png +0 -0
- /data/images/{sente → nomal/sente}/narikyo.png +0 -0
- /data/images/{sente → nomal/sente}/ohsho.png +0 -0
- /data/images/{sente → nomal/sente}/ryu.png +0 -0
- /data/images/{sente → nomal/sente}/tokin.png +0 -0
- /data/images/{sente → nomal/sente}/uma.png +0 -0
- /data/images/{sente/shori.png → sente_shori.png} +0 -0
@@ -0,0 +1,209 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'base64'
|
4
|
+
require 'tty-prompt'
|
5
|
+
|
6
|
+
module ConsoleShogi
|
7
|
+
module Terminal
|
8
|
+
class Operator
|
9
|
+
class << self
|
10
|
+
TEBAN = {
|
11
|
+
sente: '先手',
|
12
|
+
gote: '後手'
|
13
|
+
}
|
14
|
+
|
15
|
+
module EscapeSequence
|
16
|
+
RESET = "\e[0m"
|
17
|
+
RESET_CURSOR = "\e[1;1H"
|
18
|
+
SCREEN_CLEAR = "\e[2J"
|
19
|
+
SCREEN_CLEAR_AFTER_CURSOR = "\e[0J"
|
20
|
+
OUTSIDE_BOARD = "\e[10;1H"
|
21
|
+
CURRENT_POSITION = "\e[6n"
|
22
|
+
MOVE_RIGHT_2 = "\e[2C"
|
23
|
+
end
|
24
|
+
|
25
|
+
def cursor
|
26
|
+
@cursor ||= Cursor.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def image_height
|
30
|
+
@image_height ||= calculate_fit_image_height
|
31
|
+
end
|
32
|
+
|
33
|
+
def clear_scrren
|
34
|
+
print EscapeSequence::SCREEN_CLEAR
|
35
|
+
|
36
|
+
print EscapeSequence::RESET_CURSOR
|
37
|
+
end
|
38
|
+
|
39
|
+
# TODO 駒台の表示方法も考える
|
40
|
+
def print_diff_board(previous_board:, board:, sente_komadai:, gote_komadai:)
|
41
|
+
print EscapeSequence::RESET_CURSOR
|
42
|
+
|
43
|
+
board.matrix.row_vectors.each_with_index do |vector, i|
|
44
|
+
vector.each_with_index do |piece, j|
|
45
|
+
if previous_board.matrix[i, j] == piece
|
46
|
+
print EscapeSequence::MOVE_RIGHT_2
|
47
|
+
else
|
48
|
+
print_image(image: piece.image, height: image_height)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
print "#{EscapeSequence::RESET}\n"
|
53
|
+
end
|
54
|
+
|
55
|
+
# NOTE 駒台を表示
|
56
|
+
print_komadai(komadai: sente_komadai, **DisplayArea::Komadai::Sente::START_INDEX)
|
57
|
+
print_komadai(komadai: gote_komadai, **DisplayArea::Komadai::Gote::START_INDEX)
|
58
|
+
|
59
|
+
# NOTE back a cursor
|
60
|
+
back_to_cursor
|
61
|
+
end
|
62
|
+
|
63
|
+
def focus_piece(location:, cursor:)
|
64
|
+
piece = location.fetch_piece(x: cursor.squares_position.x, y: cursor.squares_position.y)
|
65
|
+
|
66
|
+
return if piece.nil?
|
67
|
+
|
68
|
+
print_image(image: piece.focused_image, height: image_height)
|
69
|
+
|
70
|
+
back_to_cursor
|
71
|
+
end
|
72
|
+
|
73
|
+
def deactive_piece(location:, previous_cursor:)
|
74
|
+
print "\e[#{previous_cursor.terminal_position.y};#{previous_cursor.terminal_position.x}H"
|
75
|
+
|
76
|
+
piece = location.fetch_piece(x: previous_cursor.squares_position.x, y: previous_cursor.squares_position.y)
|
77
|
+
|
78
|
+
print_image(image: piece.image, height: image_height) unless piece.nil?
|
79
|
+
|
80
|
+
back_to_cursor
|
81
|
+
end
|
82
|
+
|
83
|
+
def active_piece(location:, cursor:)
|
84
|
+
piece = location.fetch_piece(x: cursor.squares_position.x, y: cursor.squares_position.y)
|
85
|
+
|
86
|
+
return if piece.nil?
|
87
|
+
|
88
|
+
print_image(image: piece.active_image, height: image_height)
|
89
|
+
|
90
|
+
back_to_cursor
|
91
|
+
end
|
92
|
+
|
93
|
+
def select_promotion
|
94
|
+
print EscapeSequence::OUTSIDE_BOARD
|
95
|
+
|
96
|
+
prompt = TTY::Prompt.new
|
97
|
+
|
98
|
+
prompt.select('成りますか?', show_help: :never) {|menu|
|
99
|
+
menu.choice '成る', true
|
100
|
+
menu.choice '成らない', false
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
104
|
+
def print_winner(player)
|
105
|
+
print "\e[4;7H"
|
106
|
+
|
107
|
+
print_image(image: player.win_image, height: image_height * 3)
|
108
|
+
|
109
|
+
print EscapeSequence::OUTSIDE_BOARD
|
110
|
+
end
|
111
|
+
|
112
|
+
# TODO 見た目は後で直す
|
113
|
+
def print_teban(teban)
|
114
|
+
print EscapeSequence::OUTSIDE_BOARD
|
115
|
+
print EscapeSequence::SCREEN_CLEAR_AFTER_CURSOR
|
116
|
+
|
117
|
+
print "手番 : #{TEBAN[teban.to_sym]}"
|
118
|
+
|
119
|
+
back_to_cursor
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
IMAGE_HEIGHT_PIXEL = 240
|
125
|
+
|
126
|
+
# TODO 無理やりなので、実現方法を再考する
|
127
|
+
def calculate_fit_image_height
|
128
|
+
start_position = fetch_cursor_position_in_stdin
|
129
|
+
# NOTE NonePiece を選んでいることに意味はない
|
130
|
+
print_image(image: NonePiece.new.image)
|
131
|
+
end_position = fetch_cursor_position_in_stdin
|
132
|
+
|
133
|
+
fixed_height = IMAGE_HEIGHT_PIXEL / (end_position[1].to_i - start_position[1].to_i)
|
134
|
+
|
135
|
+
print EscapeSequence::SCREEN_CLEAR
|
136
|
+
# NOTE カーソルを1行1列に移動
|
137
|
+
print EscapeSequence::RESET_CURSOR
|
138
|
+
|
139
|
+
fixed_height
|
140
|
+
end
|
141
|
+
|
142
|
+
# TODO view 用の Player 作って整理する
|
143
|
+
def print_komadai(komadai:, x:, y:)
|
144
|
+
komadai.pieces.row_vectors.each_with_index do |row_pieces, i|
|
145
|
+
print "\e[#{y + i};#{x}H"
|
146
|
+
|
147
|
+
row_pieces.each do |p|
|
148
|
+
print_image(image: p.image, height: image_height)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
print EscapeSequence::RESET
|
153
|
+
end
|
154
|
+
|
155
|
+
# TODO Cursor にも持っているので整理する
|
156
|
+
def fetch_cursor_position_in_stdin
|
157
|
+
stdout = ''
|
158
|
+
|
159
|
+
$stdin.raw do |stdin|
|
160
|
+
$stdout << EscapeSequence::CURRENT_POSITION
|
161
|
+
$stdout.flush
|
162
|
+
|
163
|
+
# NOTE \e[n;mR という形式で現在位置が返ってくる
|
164
|
+
while (c = stdin.getc) != 'R'
|
165
|
+
stdout += c
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
stdout.match /(\d+);(\d+)/
|
170
|
+
end
|
171
|
+
|
172
|
+
def back_to_cursor
|
173
|
+
print "\e[#{cursor.terminal_position.y};#{cursor.terminal_position.x}H"
|
174
|
+
end
|
175
|
+
|
176
|
+
# TODO 画像を表示するためのメソッド、場所検討
|
177
|
+
def print_image(image:, width: nil, height: nil)
|
178
|
+
encoded_image = Base64.strict_encode64(image)
|
179
|
+
|
180
|
+
print_osc
|
181
|
+
print "1337;File=inline=1"
|
182
|
+
print ";size=#{image.size}"
|
183
|
+
print ";name=#{encoded_image}"
|
184
|
+
print ";width=#{width}px" unless width.nil?
|
185
|
+
print ";height=#{height}px" unless height.nil?
|
186
|
+
print ":#{encoded_image}"
|
187
|
+
print_st
|
188
|
+
end
|
189
|
+
|
190
|
+
def print_osc
|
191
|
+
# NOTE \e] で始まるシーケンスは全てのターミナルで扱えるわけでは無い
|
192
|
+
if ENV['TERM'].start_with?('screen') || ENV['TERM'].start_with?('tmux')
|
193
|
+
print "\ePtmux;\e\e]"
|
194
|
+
else
|
195
|
+
print "\e]"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def print_st
|
200
|
+
if ENV['TERM'].start_with?('screen') || ENV['TERM'].start_with?('tmux')
|
201
|
+
print "\a\e\\"
|
202
|
+
else
|
203
|
+
print "\a"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
data/lib/console_shogi.rb
CHANGED
@@ -6,11 +6,16 @@ require_relative 'console_shogi/player'
|
|
6
6
|
require_relative 'console_shogi/board'
|
7
7
|
require_relative 'console_shogi/piece'
|
8
8
|
require_relative 'console_shogi/komadai'
|
9
|
+
require_relative 'console_shogi/teban'
|
10
|
+
|
11
|
+
require_relative 'console_shogi/terminal/display_area'
|
12
|
+
require_relative 'console_shogi/terminal/operator'
|
13
|
+
require_relative 'console_shogi/terminal/cursor'
|
9
14
|
|
10
|
-
require_relative 'console_shogi/terminal_operator'
|
11
15
|
require_relative 'console_shogi/new_board_builder'
|
12
16
|
require_relative 'console_shogi/piece_mover'
|
13
17
|
require_relative 'console_shogi/piece_mover_on_komadai'
|
18
|
+
require_relative 'console_shogi/piece_movement_checker'
|
14
19
|
|
15
20
|
module ConsoleShogi
|
16
21
|
class Error < StandardError; end
|
metadata
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: console-shogi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- pekepek
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-06-02 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: You can play the Shogi in terminal
|
14
14
|
email:
|
15
15
|
- ishihata@sansan.com
|
16
|
-
executables:
|
16
|
+
executables:
|
17
|
+
- console-shogi
|
17
18
|
extensions: []
|
18
19
|
extra_rdoc_files: []
|
19
20
|
files:
|
@@ -22,50 +23,116 @@ files:
|
|
22
23
|
- LICENSE.txt
|
23
24
|
- README.md
|
24
25
|
- Rakefile
|
25
|
-
-
|
26
|
-
- images/gote/
|
27
|
-
- images/gote/
|
28
|
-
- images/gote/
|
29
|
-
- images/gote/
|
30
|
-
- images/gote/
|
31
|
-
- images/gote/
|
32
|
-
- images/gote/
|
33
|
-
- images/gote/
|
34
|
-
- images/gote/
|
35
|
-
- images/gote/
|
36
|
-
- images/gote/
|
37
|
-
- images/gote/ohsho.png
|
38
|
-
- images/gote/ryu.png
|
39
|
-
- images/gote/
|
40
|
-
- images/gote/
|
41
|
-
- images/
|
42
|
-
- images/sente/fu.png
|
43
|
-
- images/sente/gin.png
|
44
|
-
- images/sente/gyoku.png
|
45
|
-
- images/sente/hisha.png
|
46
|
-
- images/sente/kaku.png
|
47
|
-
- images/sente/keima.png
|
48
|
-
- images/sente/kin.png
|
49
|
-
- images/sente/kyosha.png
|
50
|
-
- images/sente/narigin.png
|
51
|
-
- images/sente/narikei.png
|
52
|
-
- images/sente/narikyo.png
|
53
|
-
- images/sente/
|
54
|
-
- images/sente/
|
55
|
-
- images/sente/
|
56
|
-
- images/sente/
|
57
|
-
- images/
|
58
|
-
- images/
|
26
|
+
- exe/console-shogi
|
27
|
+
- images/active/gote/fu.png
|
28
|
+
- images/active/gote/gin.png
|
29
|
+
- images/active/gote/gyoku.png
|
30
|
+
- images/active/gote/hisha.png
|
31
|
+
- images/active/gote/kaku.png
|
32
|
+
- images/active/gote/keima.png
|
33
|
+
- images/active/gote/kin.png
|
34
|
+
- images/active/gote/kyosha.png
|
35
|
+
- images/active/gote/narigin.png
|
36
|
+
- images/active/gote/narikei.png
|
37
|
+
- images/active/gote/narikyo.png
|
38
|
+
- images/active/gote/ohsho.png
|
39
|
+
- images/active/gote/ryu.png
|
40
|
+
- images/active/gote/tokin.png
|
41
|
+
- images/active/gote/uma.png
|
42
|
+
- images/active/nonepiece.png
|
43
|
+
- images/active/sente/fu.png
|
44
|
+
- images/active/sente/gin.png
|
45
|
+
- images/active/sente/gyoku.png
|
46
|
+
- images/active/sente/hisha.png
|
47
|
+
- images/active/sente/kaku.png
|
48
|
+
- images/active/sente/keima.png
|
49
|
+
- images/active/sente/kin.png
|
50
|
+
- images/active/sente/kyosha.png
|
51
|
+
- images/active/sente/narigin.png
|
52
|
+
- images/active/sente/narikei.png
|
53
|
+
- images/active/sente/narikyo.png
|
54
|
+
- images/active/sente/ohsho.png
|
55
|
+
- images/active/sente/ryu.png
|
56
|
+
- images/active/sente/tokin.png
|
57
|
+
- images/active/sente/uma.png
|
58
|
+
- images/focused/gote/fu.png
|
59
|
+
- images/focused/gote/gin.png
|
60
|
+
- images/focused/gote/gyoku.png
|
61
|
+
- images/focused/gote/hisha.png
|
62
|
+
- images/focused/gote/kaku.png
|
63
|
+
- images/focused/gote/keima.png
|
64
|
+
- images/focused/gote/kin.png
|
65
|
+
- images/focused/gote/kyosha.png
|
66
|
+
- images/focused/gote/narigin.png
|
67
|
+
- images/focused/gote/narikei.png
|
68
|
+
- images/focused/gote/narikyo.png
|
69
|
+
- images/focused/gote/ohsho.png
|
70
|
+
- images/focused/gote/ryu.png
|
71
|
+
- images/focused/gote/tokin.png
|
72
|
+
- images/focused/gote/uma.png
|
73
|
+
- images/focused/nonepiece.png
|
74
|
+
- images/focused/sente/fu.png
|
75
|
+
- images/focused/sente/gin.png
|
76
|
+
- images/focused/sente/gyoku.png
|
77
|
+
- images/focused/sente/hisha.png
|
78
|
+
- images/focused/sente/kaku.png
|
79
|
+
- images/focused/sente/keima.png
|
80
|
+
- images/focused/sente/kin.png
|
81
|
+
- images/focused/sente/kyosha.png
|
82
|
+
- images/focused/sente/narigin.png
|
83
|
+
- images/focused/sente/narikei.png
|
84
|
+
- images/focused/sente/narikyo.png
|
85
|
+
- images/focused/sente/ohsho.png
|
86
|
+
- images/focused/sente/ryu.png
|
87
|
+
- images/focused/sente/tokin.png
|
88
|
+
- images/focused/sente/uma.png
|
89
|
+
- images/gote_shori.png
|
90
|
+
- images/nomal/gote/fu.png
|
91
|
+
- images/nomal/gote/gin.png
|
92
|
+
- images/nomal/gote/gyoku.png
|
93
|
+
- images/nomal/gote/hisha.png
|
94
|
+
- images/nomal/gote/kaku.png
|
95
|
+
- images/nomal/gote/keima.png
|
96
|
+
- images/nomal/gote/kin.png
|
97
|
+
- images/nomal/gote/kyosha.png
|
98
|
+
- images/nomal/gote/narigin.png
|
99
|
+
- images/nomal/gote/narikei.png
|
100
|
+
- images/nomal/gote/narikyo.png
|
101
|
+
- images/nomal/gote/ohsho.png
|
102
|
+
- images/nomal/gote/ryu.png
|
103
|
+
- images/nomal/gote/tokin.png
|
104
|
+
- images/nomal/gote/uma.png
|
105
|
+
- images/nomal/nonepiece.png
|
106
|
+
- images/nomal/sente/fu.png
|
107
|
+
- images/nomal/sente/gin.png
|
108
|
+
- images/nomal/sente/gyoku.png
|
109
|
+
- images/nomal/sente/hisha.png
|
110
|
+
- images/nomal/sente/kaku.png
|
111
|
+
- images/nomal/sente/keima.png
|
112
|
+
- images/nomal/sente/kin.png
|
113
|
+
- images/nomal/sente/kyosha.png
|
114
|
+
- images/nomal/sente/narigin.png
|
115
|
+
- images/nomal/sente/narikei.png
|
116
|
+
- images/nomal/sente/narikyo.png
|
117
|
+
- images/nomal/sente/ohsho.png
|
118
|
+
- images/nomal/sente/ryu.png
|
119
|
+
- images/nomal/sente/tokin.png
|
120
|
+
- images/nomal/sente/uma.png
|
121
|
+
- images/sente_shori.png
|
59
122
|
- lib/console_shogi.rb
|
60
123
|
- lib/console_shogi/board.rb
|
61
124
|
- lib/console_shogi/game.rb
|
62
125
|
- lib/console_shogi/komadai.rb
|
63
126
|
- lib/console_shogi/new_board_builder.rb
|
64
127
|
- lib/console_shogi/piece.rb
|
128
|
+
- lib/console_shogi/piece_movement_checker.rb
|
65
129
|
- lib/console_shogi/piece_mover.rb
|
66
130
|
- lib/console_shogi/piece_mover_on_komadai.rb
|
67
131
|
- lib/console_shogi/player.rb
|
68
|
-
- lib/console_shogi/
|
132
|
+
- lib/console_shogi/teban.rb
|
133
|
+
- lib/console_shogi/terminal/cursor.rb
|
134
|
+
- lib/console_shogi/terminal/display_area.rb
|
135
|
+
- lib/console_shogi/terminal/operator.rb
|
69
136
|
- lib/console_shogi/version.rb
|
70
137
|
homepage: https://github.com/pekepek/console-shogi
|
71
138
|
licenses:
|
data/images/gote/nonepiece.png
DELETED
Binary file
|
@@ -1,211 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'base64'
|
4
|
-
require 'tty-prompt'
|
5
|
-
|
6
|
-
module ConsoleShogi
|
7
|
-
class TerminalOperator
|
8
|
-
class << self
|
9
|
-
CursorPosition = Struct.new(:x, :y)
|
10
|
-
|
11
|
-
HORIZONTAL_DISTANCE = 2
|
12
|
-
|
13
|
-
KOMADAI_START_X = 21
|
14
|
-
KOMADAI_GOTE_START_Y = 1
|
15
|
-
KOMADAI_SENTE_START_Y = 7
|
16
|
-
|
17
|
-
module EscapeSequence
|
18
|
-
RESET = "\e[0m"
|
19
|
-
RESET_CURSOR = "\e[1;1H"
|
20
|
-
BACKGROUND_COLOR_YELLOW = "\e[43m"
|
21
|
-
BACKGROUND_COLOR_GREEN = "\e[42m"
|
22
|
-
TEXT_COLOR_SENTE = "\e[37m"
|
23
|
-
TEXT_COLOR_GOTE = "\e[30m"
|
24
|
-
end
|
25
|
-
|
26
|
-
module Location
|
27
|
-
NONE = :none
|
28
|
-
BOARD = :board
|
29
|
-
SENTE_KOMADAI = :sente_komadai
|
30
|
-
GOTE_KOMADAI = :gote_komadai
|
31
|
-
end
|
32
|
-
|
33
|
-
def cursor_position
|
34
|
-
@cursor_position ||= CursorPosition.new(x: 1, y: 1)
|
35
|
-
end
|
36
|
-
|
37
|
-
def image_height
|
38
|
-
@image_height ||= calculate_fit_image_height
|
39
|
-
end
|
40
|
-
|
41
|
-
def print_board(board:, sente_komadai:, gote_komadai:)
|
42
|
-
# NOTE 画面をクリア
|
43
|
-
print "\e[2J"
|
44
|
-
|
45
|
-
# NOTE カーソルを1行1列に移動
|
46
|
-
print EscapeSequence::RESET_CURSOR
|
47
|
-
|
48
|
-
board.matrix.row_vectors.each_with_index do |vector, i|
|
49
|
-
vector.each_with_index do |piece, j|
|
50
|
-
print_image(image: piece.image, height: image_height)
|
51
|
-
end
|
52
|
-
|
53
|
-
print "#{EscapeSequence::RESET}\n"
|
54
|
-
end
|
55
|
-
|
56
|
-
# NOTE 駒台を表示
|
57
|
-
print_komadai(sente_komadai, KOMADAI_SENTE_START_Y, EscapeSequence::TEXT_COLOR_SENTE)
|
58
|
-
print_komadai(gote_komadai, KOMADAI_GOTE_START_Y, EscapeSequence::TEXT_COLOR_GOTE)
|
59
|
-
|
60
|
-
# NOTE back a cursor
|
61
|
-
back_to_cursor
|
62
|
-
end
|
63
|
-
|
64
|
-
def active_piece(board:, piece_index:)
|
65
|
-
back_to_cursor
|
66
|
-
|
67
|
-
board.fetch_piece(x: piece_index[:x], y: piece_index[:y])
|
68
|
-
end
|
69
|
-
|
70
|
-
def move_cursor(key)
|
71
|
-
return unless %w(A B C D).include?(key)
|
72
|
-
|
73
|
-
direction =
|
74
|
-
case key
|
75
|
-
when 'A', 'B'
|
76
|
-
key
|
77
|
-
when 'C', 'D'
|
78
|
-
"#{HORIZONTAL_DISTANCE}#{key}"
|
79
|
-
end
|
80
|
-
|
81
|
-
print "\e[#{direction}"
|
82
|
-
|
83
|
-
reload_cursor_position_in_stdin!
|
84
|
-
end
|
85
|
-
|
86
|
-
# TODO 駒台の大きさが変わるのをどうするか考える、メソッド名考える
|
87
|
-
def squares_index
|
88
|
-
case cursor_position
|
89
|
-
in x: 1..18, y: 1..9
|
90
|
-
{x: (cursor_position.x - 1) / HORIZONTAL_DISTANCE, y: cursor_position.y - 1, location: Location::BOARD}
|
91
|
-
in x: 21..38, y: 1..3
|
92
|
-
{x: (cursor_position.x - 21) / HORIZONTAL_DISTANCE, y: cursor_position.y - 1, location: Location::GOTE_KOMADAI}
|
93
|
-
in x: 21..38, y: 7..9
|
94
|
-
{x: (cursor_position.x - 21) / HORIZONTAL_DISTANCE, y: cursor_position.y - 7, location: Location::SENTE_KOMADAI}
|
95
|
-
else
|
96
|
-
{x: (cursor_position.x - 1) / HORIZONTAL_DISTANCE, y: cursor_position.y - 1, location: Location::NONE}
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
def select_promotion
|
101
|
-
print "\e[10;1H"
|
102
|
-
|
103
|
-
prompt = TTY::Prompt.new
|
104
|
-
|
105
|
-
prompt.select('成りますか?', show_help: :never) {|menu|
|
106
|
-
menu.choice '成る', true
|
107
|
-
menu.choice '成らない', false
|
108
|
-
}
|
109
|
-
end
|
110
|
-
|
111
|
-
def print_winner(teban)
|
112
|
-
print "\e[4;7H"
|
113
|
-
|
114
|
-
print_image(image: File.read("images/#{teban}/shori.png"), height: image_height * 3)
|
115
|
-
|
116
|
-
print "\e[10;1H"
|
117
|
-
end
|
118
|
-
|
119
|
-
private
|
120
|
-
|
121
|
-
IMAGE_HEIGHT_PIXEL = 240
|
122
|
-
|
123
|
-
# TODO 無理やりなので、実現方法を再考する
|
124
|
-
def calculate_fit_image_height
|
125
|
-
start_position = fetch_cursor_position_in_stdin
|
126
|
-
# NOTE NonePiece を選んでいることに意味はない
|
127
|
-
print_image(image: NonePiece.new.image)
|
128
|
-
end_position = fetch_cursor_position_in_stdin
|
129
|
-
|
130
|
-
fixed_height = IMAGE_HEIGHT_PIXEL / (end_position[1].to_i - start_position[1].to_i)
|
131
|
-
|
132
|
-
print "\e[2J"
|
133
|
-
# NOTE カーソルを1行1列に移動
|
134
|
-
print EscapeSequence::RESET_CURSOR
|
135
|
-
|
136
|
-
fixed_height
|
137
|
-
end
|
138
|
-
|
139
|
-
# TODO view 用の Player 作って整理する
|
140
|
-
def print_komadai(komadai, start_y, text_color)
|
141
|
-
komadai.pieces.row_vectors.each_with_index do |row_pieces, i|
|
142
|
-
print "\e[#{start_y + i};#{KOMADAI_START_X}H"
|
143
|
-
print EscapeSequence::BACKGROUND_COLOR_YELLOW
|
144
|
-
print text_color
|
145
|
-
|
146
|
-
row_pieces.each do |p|
|
147
|
-
print_image(image: p.image, height: image_height)
|
148
|
-
end
|
149
|
-
|
150
|
-
print EscapeSequence::RESET
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
def fetch_cursor_position_in_stdin
|
155
|
-
stdout = ''
|
156
|
-
|
157
|
-
$stdin.raw do |stdin|
|
158
|
-
$stdout << "\e[6n"
|
159
|
-
$stdout.flush
|
160
|
-
|
161
|
-
while (c = stdin.getc) != 'R'
|
162
|
-
stdout += c
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
stdout.match /(\d+);(\d+)/
|
167
|
-
end
|
168
|
-
|
169
|
-
def reload_cursor_position_in_stdin!
|
170
|
-
positions = fetch_cursor_position_in_stdin
|
171
|
-
|
172
|
-
@cursor_position = CursorPosition.new(x: positions[2].to_i, y: positions[1].to_i)
|
173
|
-
end
|
174
|
-
|
175
|
-
def back_to_cursor
|
176
|
-
print "\e[#{cursor_position.y};#{cursor_position.x}H"
|
177
|
-
end
|
178
|
-
|
179
|
-
# TODO 画像を表示するためのメソッド、場所検討
|
180
|
-
def print_image(image:, width: nil, height: nil)
|
181
|
-
encoded_image = Base64.strict_encode64(image)
|
182
|
-
|
183
|
-
print_osc
|
184
|
-
print "1337;File=inline=1"
|
185
|
-
print ";size=#{image.size}"
|
186
|
-
print ";name=#{encoded_image}"
|
187
|
-
print ";width=#{width}px" unless width.nil?
|
188
|
-
print ";height=#{height}px" unless height.nil?
|
189
|
-
print ":#{encoded_image}"
|
190
|
-
print_st
|
191
|
-
end
|
192
|
-
|
193
|
-
def print_osc
|
194
|
-
# NOTE \e] で始まるシーケンスは全てのターミナルで扱えるわけでは無い
|
195
|
-
if ENV['TERM'].start_with?('screen') || ENV['TERM'].start_with?('tmux')
|
196
|
-
print "\ePtmux;\e\e]"
|
197
|
-
else
|
198
|
-
print "\e]"
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
def print_st
|
203
|
-
if ENV['TERM'].start_with?('screen') || ENV['TERM'].start_with?('tmux')
|
204
|
-
print "\a\e\\"
|
205
|
-
else
|
206
|
-
print "\a"
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
end
|
211
|
-
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|