console-shogi 0.3.0 → 0.4.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 +2 -0
- data/images/history_button/left.png +0 -0
- data/images/history_button/play.png +0 -0
- data/images/history_button/right.png +0 -0
- data/lib/console_shogi/board.rb +2 -2
- data/lib/console_shogi/game.rb +66 -91
- data/lib/console_shogi/game_history.rb +60 -0
- data/lib/console_shogi/grid_position.rb +6 -0
- data/lib/console_shogi/komadai.rb +13 -11
- data/lib/console_shogi/piece.rb +5 -0
- data/lib/console_shogi/piece_mover.rb +20 -50
- data/lib/console_shogi/piece_mover_from_board.rb +57 -0
- data/lib/console_shogi/piece_mover_from_komadai.rb +51 -0
- data/lib/console_shogi/terminal/cursor.rb +14 -20
- data/lib/console_shogi/terminal/display_area.rb +108 -16
- data/lib/console_shogi/terminal/operator.rb +60 -20
- data/lib/console_shogi/version.rb +1 -1
- data/lib/console_shogi.rb +5 -2
- metadata +10 -4
- data/lib/console_shogi/piece_mover_on_komadai.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bee75f732b33e177d7b2c15939508663a391bc2d593692273a1a1f47ae654543
|
4
|
+
data.tar.gz: d78846e34e4afbeea51dfd4558e9c076aee6625c6ad43129fb01cff9c36d41a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b0aeed55b0fcaedbe879703c3d7e20fc38a6fa5e2a0617d627ff6f873766aff6f4963dc117f032b64911021a46888ebb2229ed6d4a4231bd91d9ecbcf5a868a
|
7
|
+
data.tar.gz: 926eeda23c9e565c45ec8290c0ecf5ca607adfaea24c29f1a95c858f952af948b7e371ae8408f32512c3acd720bb3af5873dce62aa8ff435e0801d1cf2b1cc35
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
Binary file
|
Binary file
|
Binary file
|
data/lib/console_shogi/board.rb
CHANGED
data/lib/console_shogi/game.rb
CHANGED
@@ -9,19 +9,21 @@ module ConsoleShogi
|
|
9
9
|
@gote_player = Player.new(teban: Teban::GOTE)
|
10
10
|
@board = NewBoardBuilder.build
|
11
11
|
|
12
|
-
@
|
13
|
-
@from_cursor = nil
|
14
|
-
@selected_piece = false
|
12
|
+
@selected_cursor = nil
|
15
13
|
@teban_player = @sente_player
|
16
|
-
end
|
17
14
|
|
18
|
-
|
19
|
-
Terminal::Operator.clear_scrren
|
15
|
+
@game_histories = [{board: board.deep_copy, sente_komadai: sente_player.komadai.deep_copy, gote_komadai: gote_player.komadai.deep_copy}]
|
20
16
|
|
21
|
-
Terminal::Operator.
|
17
|
+
Terminal::Operator.clear_scrren
|
18
|
+
Terminal::Operator.print_board(board: board, sente_komadai: sente_player.komadai, gote_komadai: gote_player.komadai)
|
19
|
+
Terminal::Operator.print_history_button
|
22
20
|
Terminal::Operator.print_teban(teban_player.teban)
|
21
|
+
end
|
23
22
|
|
23
|
+
def start
|
24
24
|
while key = STDIN.getch
|
25
|
+
cursor = Terminal::Operator.cursor
|
26
|
+
|
25
27
|
# NOTE Ctrl-C を押したら終了
|
26
28
|
if key == "\C-c"
|
27
29
|
exit
|
@@ -29,81 +31,54 @@ module ConsoleShogi
|
|
29
31
|
elsif key == "\e" && STDIN.getch == "["
|
30
32
|
key = STDIN.getch
|
31
33
|
|
32
|
-
|
33
|
-
@previous_cursor_on_square = cursor.dup unless cursor.squares_position.location == :none
|
34
|
+
@last_cursor_on_grid = cursor.dup unless cursor.grid_position.location.outside?
|
34
35
|
|
35
36
|
cursor.move(key)
|
36
37
|
|
37
38
|
# TODO 選択したピースは色を変えないようにしている。状態の持ち方を見直したい
|
38
|
-
deactive_piece(
|
39
|
-
focus_piece(cursor)
|
39
|
+
deactive_piece(last_cursor_on_grid) if selected_cursor&.grid_position != last_cursor_on_grid.grid_position
|
40
|
+
focus_piece(cursor) if selected_cursor&.grid_position != cursor.grid_position
|
40
41
|
# NOTE Enter を押したら駒を移動
|
41
42
|
elsif key == "\r"
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
if selected_piece
|
46
|
-
next if cursor.squares_position.location != :board
|
47
|
-
|
48
|
-
piece_mover =
|
49
|
-
case from_cursor.squares_position.location
|
50
|
-
when :board
|
51
|
-
# TODO 後で index を統一的にどう扱うか整理する
|
52
|
-
PieceMover.new(
|
53
|
-
board: board,
|
54
|
-
player: teban_player,
|
55
|
-
from: from_cursor.squares_position.to_h,
|
56
|
-
to: cursor.squares_position.to_h
|
57
|
-
)
|
58
|
-
when :sente_komadai
|
59
|
-
PieceMoverOnKomadai.new(
|
60
|
-
board: board,
|
61
|
-
komadai: sente_player.komadai,
|
62
|
-
from: from_cursor.squares_position.to_h,
|
63
|
-
to: cursor.squares_position.to_h
|
64
|
-
)
|
65
|
-
when :gote_komadai
|
66
|
-
PieceMoverOnKomadai.new(
|
67
|
-
board: board,
|
68
|
-
komadai: gote_player.komadai,
|
69
|
-
from: from_cursor.squares_position.to_h,
|
70
|
-
to: cursor.squares_position.to_h
|
71
|
-
)
|
72
|
-
end
|
43
|
+
if cursor.grid_position.location.history?
|
44
|
+
GameHistory.new(game_histories: game_histories).start
|
73
45
|
|
74
|
-
|
75
|
-
|
76
|
-
if piece_mover.moved_piece?
|
77
|
-
Terminal::Operator.print_diff_board(previous_board: previous_board, board: board, sente_komadai: sente_player.komadai, gote_komadai: gote_player.komadai)
|
46
|
+
next
|
47
|
+
end
|
78
48
|
|
79
|
-
|
80
|
-
|
49
|
+
if selected_cursor.nil?
|
50
|
+
# TODO PieceMover の can_move? と分散してしまっている気もする
|
51
|
+
next if cursor.grid_position.location.outside?
|
81
52
|
|
82
|
-
|
83
|
-
else
|
84
|
-
deactive_piece(from_cursor)
|
85
|
-
end
|
53
|
+
active_piece(cursor)
|
86
54
|
|
87
|
-
@
|
88
|
-
@selected_piece = false
|
55
|
+
@selected_cursor = cursor.dup
|
89
56
|
else
|
90
|
-
|
91
|
-
next if cursor.squares_position.location == :none
|
92
|
-
next if teban_player.sente? && cursor.squares_position.location == :gote_komadai
|
93
|
-
next if teban_player.gote? && cursor.squares_position.location == :sente_komadai
|
57
|
+
next unless cursor.grid_position.location.board?
|
94
58
|
|
95
|
-
|
59
|
+
piece_mover = PieceMover.build(board: board, player: teban_player, from_cursor: selected_cursor, to_cursor: cursor)
|
96
60
|
|
97
|
-
|
98
|
-
|
99
|
-
|
61
|
+
piece_mover.move!
|
62
|
+
|
63
|
+
if piece_mover.moved_piece?
|
64
|
+
Terminal::Operator.print_diff_board(previous_board: game_histories.last[:board], board: board, sente_komadai: sente_player.komadai, gote_komadai: gote_player.komadai)
|
100
65
|
|
101
|
-
|
102
|
-
[sente_player, gote_player].each do |player|
|
103
|
-
next unless player.win?
|
66
|
+
@game_histories << {board: board.deep_copy, sente_komadai: sente_player.komadai.deep_copy, gote_komadai: gote_player.komadai.deep_copy}
|
104
67
|
|
105
|
-
|
106
|
-
|
68
|
+
if teban_player.win?
|
69
|
+
Terminal::Operator.print_winner(teban_player)
|
70
|
+
|
71
|
+
exit
|
72
|
+
else
|
73
|
+
change_teban!
|
74
|
+
|
75
|
+
Terminal::Operator.print_teban(teban_player.teban)
|
76
|
+
end
|
77
|
+
else
|
78
|
+
deactive_piece(selected_cursor)
|
79
|
+
end
|
80
|
+
|
81
|
+
@selected_cursor = nil
|
107
82
|
end
|
108
83
|
end
|
109
84
|
end
|
@@ -111,45 +86,45 @@ module ConsoleShogi
|
|
111
86
|
|
112
87
|
private
|
113
88
|
|
114
|
-
attr_reader :board, :sente_player, :gote_player, :
|
89
|
+
attr_reader :board, :sente_player, :gote_player, :selected_cursor, :teban_player, :game_histories, :last_cursor_on_grid
|
115
90
|
|
116
91
|
def change_teban!
|
117
92
|
@teban_player = teban_player == sente_player ? gote_player : sente_player
|
118
93
|
end
|
119
94
|
|
95
|
+
# TODO ここらへんイケてないのでリファクタしたい
|
120
96
|
def active_piece(cursor)
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
Terminal::Operator.active_piece(location: sente_player.komadai, cursor: cursor)
|
127
|
-
when :gote_komadai
|
128
|
-
Terminal::Operator.active_piece(location: gote_player.komadai, cursor: cursor)
|
129
|
-
end
|
97
|
+
location = fetch_piece_location_object(cursor.grid_position.location)
|
98
|
+
|
99
|
+
return if location.nil?
|
100
|
+
|
101
|
+
Terminal::Operator.active_piece(board: location, cursor: cursor)
|
130
102
|
end
|
131
103
|
|
132
104
|
def deactive_piece(cursor)
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
Terminal::Operator.deactive_piece(location: sente_player.komadai, previous_cursor: cursor)
|
139
|
-
when :gote_komadai
|
140
|
-
Terminal::Operator.deactive_piece(location: gote_player.komadai, previous_cursor: cursor)
|
141
|
-
end
|
105
|
+
location = fetch_piece_location_object(cursor.grid_position.location)
|
106
|
+
|
107
|
+
return if location.nil?
|
108
|
+
|
109
|
+
Terminal::Operator.deactive_piece(board: location, previous_cursor: cursor)
|
142
110
|
end
|
143
111
|
|
144
112
|
def focus_piece(cursor)
|
145
|
-
|
146
|
-
|
113
|
+
location = fetch_piece_location_object(cursor.grid_position.location)
|
114
|
+
|
115
|
+
return if location.nil?
|
116
|
+
|
117
|
+
Terminal::Operator.focus_piece(board: location, cursor: cursor)
|
118
|
+
end
|
119
|
+
|
120
|
+
def fetch_piece_location_object(cursor_location)
|
121
|
+
case cursor_location.name
|
147
122
|
when :board
|
148
|
-
|
123
|
+
board
|
149
124
|
when :sente_komadai
|
150
|
-
|
125
|
+
sente_player.komadai
|
151
126
|
when :gote_komadai
|
152
|
-
|
127
|
+
gote_player.komadai
|
153
128
|
end
|
154
129
|
end
|
155
130
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'io/console'
|
4
|
+
|
5
|
+
module ConsoleShogi
|
6
|
+
class GameHistory
|
7
|
+
def initialize(game_histories:)
|
8
|
+
@game_histories = game_histories
|
9
|
+
# TODO game_count という名前は分かりづらいので変える
|
10
|
+
@game_count = game_histories.count
|
11
|
+
|
12
|
+
Terminal::Operator.print_start_history
|
13
|
+
end
|
14
|
+
|
15
|
+
def start
|
16
|
+
while key = STDIN.getch
|
17
|
+
cursor = Terminal::Operator.cursor
|
18
|
+
|
19
|
+
# NOTE Ctrl-C を押したら終了
|
20
|
+
if key == "\C-c"
|
21
|
+
exit
|
22
|
+
# NOTE 矢印キーを押したらカーソルを移動
|
23
|
+
elsif key == "\e" && STDIN.getch == "["
|
24
|
+
key = STDIN.getch
|
25
|
+
|
26
|
+
cursor.move(key)
|
27
|
+
elsif key == "\r"
|
28
|
+
location = cursor.grid_position.location
|
29
|
+
|
30
|
+
next unless location.history?
|
31
|
+
|
32
|
+
if location.resume?
|
33
|
+
Terminal::Operator.clear_scrren
|
34
|
+
|
35
|
+
Terminal::Operator.print_board(board: game_histories.last[:board], sente_komadai: game_histories.last[:sente_komadai], gote_komadai: game_histories.last[:gote_komadai])
|
36
|
+
Terminal::Operator.print_history_button
|
37
|
+
|
38
|
+
return
|
39
|
+
elsif location.back?
|
40
|
+
next if game_count == 1
|
41
|
+
|
42
|
+
@game_count -= 1
|
43
|
+
|
44
|
+
Terminal::Operator.print_board(board: game_histories[game_count - 1][:board], sente_komadai: game_histories[game_count - 1][:sente_komadai], gote_komadai: game_histories[game_count - 1][:gote_komadai])
|
45
|
+
elsif location.forward?
|
46
|
+
next if game_count == game_histories.count
|
47
|
+
|
48
|
+
@game_count += 1
|
49
|
+
|
50
|
+
Terminal::Operator.print_board(board: game_histories[game_count - 1][:board], sente_komadai: game_histories[game_count - 1][:sente_komadai], gote_komadai: game_histories[game_count - 1][:gote_komadai])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
attr_reader :game_histories, :game_count
|
59
|
+
end
|
60
|
+
end
|
@@ -8,8 +8,12 @@ module ConsoleShogi
|
|
8
8
|
|
9
9
|
attr_reader :pieces
|
10
10
|
|
11
|
-
def initialize
|
12
|
-
@pieces =
|
11
|
+
def initialize(pieces: empty_pieces)
|
12
|
+
@pieces = Matrix.rows(pieces)
|
13
|
+
end
|
14
|
+
|
15
|
+
def deep_copy
|
16
|
+
Komadai.new(pieces: pieces.map(&:dup).to_a)
|
13
17
|
end
|
14
18
|
|
15
19
|
def fetch_piece(x:, y:)
|
@@ -28,8 +32,8 @@ module ConsoleShogi
|
|
28
32
|
@pieces = sort_pieces
|
29
33
|
end
|
30
34
|
|
31
|
-
def pick_up_piece!(
|
32
|
-
@pieces[
|
35
|
+
def pick_up_piece!(x:, y:)
|
36
|
+
@pieces[y, x] = NonePiece.new
|
33
37
|
@pieces = sort_pieces
|
34
38
|
end
|
35
39
|
|
@@ -49,13 +53,11 @@ module ConsoleShogi
|
|
49
53
|
end
|
50
54
|
|
51
55
|
def empty_pieces
|
52
|
-
|
53
|
-
[
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
]
|
58
|
-
)
|
56
|
+
[
|
57
|
+
[NonePiece.new, NonePiece.new, NonePiece.new],
|
58
|
+
[NonePiece.new, NonePiece.new, NonePiece.new],
|
59
|
+
[NonePiece.new, NonePiece.new, NonePiece.new]
|
60
|
+
]
|
59
61
|
end
|
60
62
|
end
|
61
63
|
end
|
data/lib/console_shogi/piece.rb
CHANGED
@@ -63,6 +63,11 @@ module ConsoleShogi
|
|
63
63
|
def can_move_long_distance?
|
64
64
|
[Kaku, Hisha, Kyosha, PromotedPiece::Uma, PromotedPiece::Ryu].include?(self::class)
|
65
65
|
end
|
66
|
+
|
67
|
+
def same?(piece)
|
68
|
+
self::class == piece::class &&
|
69
|
+
self.teban == piece.teban
|
70
|
+
end
|
66
71
|
end
|
67
72
|
|
68
73
|
class NonePiece < Piece
|
@@ -2,12 +2,26 @@
|
|
2
2
|
|
3
3
|
module ConsoleShogi
|
4
4
|
class PieceMover
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
class << self
|
6
|
+
def build(board:, player:, from_cursor:, to_cursor:)
|
7
|
+
location = from_cursor.grid_position.location
|
8
|
+
|
9
|
+
if location.board?
|
10
|
+
PieceMoverFromBoard.new(
|
11
|
+
board: board,
|
12
|
+
player: player,
|
13
|
+
from: from_cursor.grid_position,
|
14
|
+
to: to_cursor.grid_position
|
15
|
+
)
|
16
|
+
elsif location.komadai?
|
17
|
+
PieceMoverFromKomadai.new(
|
18
|
+
board: board,
|
19
|
+
player: player,
|
20
|
+
from: from_cursor.grid_position,
|
21
|
+
to: to_cursor.grid_position
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
11
25
|
end
|
12
26
|
|
13
27
|
def move!
|
@@ -17,49 +31,5 @@ module ConsoleShogi
|
|
17
31
|
def moved_piece?
|
18
32
|
@moved_piece
|
19
33
|
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
attr_reader :board, :player, :from, :to
|
24
|
-
|
25
|
-
def from_piece
|
26
|
-
@from_piece ||= board.fetch_piece(x: from[:x], y: from[:y])
|
27
|
-
end
|
28
|
-
|
29
|
-
def move_piece!
|
30
|
-
return false if from_piece.nil? || from_piece.none?
|
31
|
-
return false if from_piece.teban != player.teban
|
32
|
-
|
33
|
-
return false unless piece_movement_checker.can_move?
|
34
|
-
|
35
|
-
to_piece = board.fetch_piece(x: to[:x], y: to[:y])
|
36
|
-
player.capture_piece!(to_piece) unless to_piece.none?
|
37
|
-
|
38
|
-
board.move_piece!(
|
39
|
-
from: {x: from[:x], y: from[:y]},
|
40
|
-
to: {x: to[:x], y: to[:y]}
|
41
|
-
)
|
42
|
-
|
43
|
-
# TODO とりあえずここに実装してしまっている。整理したい
|
44
|
-
return true unless can_promote?(from_piece, from, to)
|
45
|
-
|
46
|
-
board.promote_piece!(x: to[:x], y: to[:y]) if Terminal::Operator.select_promotion
|
47
|
-
|
48
|
-
true
|
49
|
-
end
|
50
|
-
|
51
|
-
def piece_movement_checker
|
52
|
-
PieceMovementChecker.new(board: board, from: from, to: to)
|
53
|
-
end
|
54
|
-
|
55
|
-
def can_promote?(piece, from, to)
|
56
|
-
return false unless piece.can_promote?
|
57
|
-
|
58
|
-
if piece.teban == Teban::SENTE
|
59
|
-
from[:y].between?(0, 2) || to[:y].between?(0, 2)
|
60
|
-
elsif piece.teban == Teban::GOTE
|
61
|
-
from[:y].between?(6, 8) || to[:y].between?(6, 8)
|
62
|
-
end
|
63
|
-
end
|
64
34
|
end
|
65
35
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ConsoleShogi
|
4
|
+
class PieceMoverFromBoard < PieceMover
|
5
|
+
def initialize(board:, player:, from:, to:)
|
6
|
+
@board = board
|
7
|
+
@player = player
|
8
|
+
@from = from
|
9
|
+
@to = to
|
10
|
+
@moved_piece = false
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
attr_reader :board, :player, :from, :to
|
16
|
+
|
17
|
+
def from_piece
|
18
|
+
@from_piece ||= board.fetch_piece(x: from.x, y: from.y)
|
19
|
+
end
|
20
|
+
|
21
|
+
def move_piece!
|
22
|
+
return false if from_piece.nil? || from_piece.none?
|
23
|
+
return false if from_piece.teban != player.teban
|
24
|
+
|
25
|
+
return false unless piece_movement_checker.can_move?
|
26
|
+
|
27
|
+
to_piece = board.fetch_piece(x: to.x, y: to.y)
|
28
|
+
player.capture_piece!(to_piece) unless to_piece.none?
|
29
|
+
|
30
|
+
board.move_piece!(
|
31
|
+
from: {x: from.x, y: from.y},
|
32
|
+
to: {x: to.x, y: to.y}
|
33
|
+
)
|
34
|
+
|
35
|
+
# TODO とりあえずここに実装してしまっている。整理したい
|
36
|
+
return true unless can_promote?(from_piece, from, to)
|
37
|
+
|
38
|
+
board.promote_piece!(x: to.x, y: to.y) if Terminal::Operator.select_promotion
|
39
|
+
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
def piece_movement_checker
|
44
|
+
PieceMovementChecker.new(board: board, from: from, to: to)
|
45
|
+
end
|
46
|
+
|
47
|
+
def can_promote?(piece, from, to)
|
48
|
+
return false unless piece.can_promote?
|
49
|
+
|
50
|
+
if piece.teban == Teban::SENTE
|
51
|
+
from.y.between?(0, 2) || to.y.between?(0, 2)
|
52
|
+
elsif piece.teban == Teban::GOTE
|
53
|
+
from.y.between?(6, 8) || to.y.between?(6, 8)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ConsoleShogi
|
4
|
+
class PieceMoverFromKomadai < PieceMover
|
5
|
+
def initialize(board:, player:, from:, to:)
|
6
|
+
@board = board
|
7
|
+
@komadai = player.komadai
|
8
|
+
@player = player
|
9
|
+
@from = from
|
10
|
+
@to = to
|
11
|
+
@moved_piece = false
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :board, :komadai, :player, :from, :to
|
17
|
+
|
18
|
+
def move_piece!
|
19
|
+
return false if from_piece.nil? || from_piece.none?
|
20
|
+
return false if from_piece.teban != player.teban
|
21
|
+
|
22
|
+
return false unless to.location.board?
|
23
|
+
return false unless can_drop?(piece: from_piece, to: to)
|
24
|
+
|
25
|
+
komadai.pick_up_piece!(x: from.x, y: from.y)
|
26
|
+
board.put_piece!(piece: from_piece, to: to)
|
27
|
+
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
def from_piece
|
32
|
+
@from_piece ||= komadai.fetch_piece(x: from.x, y: from.y)
|
33
|
+
end
|
34
|
+
|
35
|
+
def can_drop?(piece:, to:)
|
36
|
+
to_piece = board.fetch_piece(x: to.x, y: to.y)
|
37
|
+
|
38
|
+
return false unless to_piece.none?
|
39
|
+
|
40
|
+
return false if nifu?(piece, to)
|
41
|
+
|
42
|
+
# TODO 動かせない場所に打つことはできないようにする
|
43
|
+
true
|
44
|
+
end
|
45
|
+
|
46
|
+
def nifu?(piece, to)
|
47
|
+
piece.fu? &&
|
48
|
+
board.matrix.column(to.x).any? {|p| p.fu? && piece.teban == p.teban }
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,25 +1,17 @@
|
|
1
1
|
module ConsoleShogi
|
2
2
|
module Terminal
|
3
3
|
class Cursor
|
4
|
-
module Location
|
5
|
-
NONE = :none
|
6
|
-
BOARD = :board
|
7
|
-
SENTE_KOMADAI = :sente_komadai
|
8
|
-
GOTE_KOMADAI = :gote_komadai
|
9
|
-
end
|
10
|
-
|
11
4
|
TerminalPosition = Struct.new(:x, :y)
|
12
|
-
SquaresPosition = Struct.new(:x, :y, :location)
|
13
5
|
|
14
6
|
VERTICAL_DISTANCE = 1
|
15
7
|
HORIZONTAL_DISTANCE = 2
|
16
8
|
CURSOR_DIRECTIONS = %w(A B C D)
|
17
9
|
|
18
|
-
attr_reader :terminal_position, :
|
10
|
+
attr_reader :terminal_position, :grid_position
|
19
11
|
|
20
12
|
def initialize(terminal_index_x: 1, terminal_index_y: 1)
|
21
13
|
@terminal_position = TerminalPosition.new(x: 1, y: 1)
|
22
|
-
@
|
14
|
+
@grid_position = GridPosition.new(**calculate_grid_position)
|
23
15
|
end
|
24
16
|
|
25
17
|
def move(direction)
|
@@ -44,19 +36,21 @@ module ConsoleShogi
|
|
44
36
|
positions = fetch_cursor_position_in_stdin
|
45
37
|
|
46
38
|
@terminal_position = TerminalPosition.new(x: positions[2].to_i, y: positions[1].to_i)
|
47
|
-
@
|
39
|
+
@grid_position = GridPosition.new(**calculate_grid_position)
|
48
40
|
end
|
49
41
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
{x:
|
56
|
-
in x: 21..38, y: 7..9
|
57
|
-
{x: (terminal_position.x - 21) / HORIZONTAL_DISTANCE, y: terminal_position.y - 7, location: Location::SENTE_KOMADAI}
|
42
|
+
def calculate_grid_position
|
43
|
+
area = DisplayArea.fetch_area(x: terminal_position.x, y: terminal_position.y)
|
44
|
+
|
45
|
+
# TODO history や infomation の扱いを整理する
|
46
|
+
if area.outside?
|
47
|
+
{x: nil, y: nil, location: area}
|
58
48
|
else
|
59
|
-
{
|
49
|
+
{
|
50
|
+
x: (terminal_position.x - area.start_position.x) / HORIZONTAL_DISTANCE,
|
51
|
+
y: terminal_position.y - area.start_position.y,
|
52
|
+
location: area
|
53
|
+
}
|
60
54
|
end
|
61
55
|
end
|
62
56
|
|
@@ -1,29 +1,121 @@
|
|
1
1
|
module ConsoleShogi
|
2
2
|
module Terminal
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
class DisplayArea
|
4
|
+
OTHERS = :others
|
5
|
+
BOARD = :board
|
6
|
+
SENTE_KOMADAI = :sente_komadai
|
7
|
+
GOTE_KOMADAI = :gote_komadai
|
8
|
+
|
9
|
+
Position = Struct.new(:x, :y)
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def fetch_area(x:, y:)
|
13
|
+
[Board, Komadai::Gote, Komadai::Sente, History::Back, History::Forward, History::Resume, Infomation].each do |klass|
|
14
|
+
return klass if klass.in?(x: x, y: y)
|
15
|
+
end
|
16
|
+
|
17
|
+
Others
|
18
|
+
end
|
19
|
+
|
20
|
+
def start_position
|
21
|
+
self::START_POSITION
|
22
|
+
end
|
23
|
+
|
24
|
+
def end_position
|
25
|
+
self::END_POSITION
|
26
|
+
end
|
27
|
+
|
28
|
+
def name
|
29
|
+
self::NAME
|
30
|
+
end
|
31
|
+
|
32
|
+
def board?
|
33
|
+
Board == self
|
34
|
+
end
|
35
|
+
|
36
|
+
def komadai?
|
37
|
+
[Komadai::Gote, Komadai::Sente].include?(self)
|
38
|
+
end
|
39
|
+
|
40
|
+
def history?
|
41
|
+
[History::Back, History::Forward, History::Resume].include?(self)
|
42
|
+
end
|
43
|
+
|
44
|
+
def outside?
|
45
|
+
[History::Back, History::Forward, History::Resume, Infomation, Others].include?(self)
|
46
|
+
end
|
47
|
+
|
48
|
+
def in?(x:, y:)
|
49
|
+
Range.new(start_position.x, end_position.x).include?(x) &&
|
50
|
+
Range.new(start_position.y, end_position.y).include?(y)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Board < DisplayArea
|
55
|
+
START_POSITION = Position.new(x: 1, y: 1)
|
56
|
+
END_POSITION = Position.new(x: 18, y: 9)
|
57
|
+
NAME = :board
|
8
58
|
end
|
9
59
|
|
10
60
|
module Komadai
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
61
|
+
class Gote < DisplayArea
|
62
|
+
START_POSITION = Position.new(x: 21, y: 1)
|
63
|
+
END_POSITION = Position.new(x: 38, y: 3)
|
64
|
+
NAME = :gote_komadai
|
65
|
+
end
|
66
|
+
|
67
|
+
class Sente < DisplayArea
|
68
|
+
START_POSITION = Position.new(x: 21, y: 7)
|
69
|
+
END_POSITION = Position.new(x: 38, y: 9)
|
70
|
+
NAME = :sente_komadai
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class History < DisplayArea
|
75
|
+
START_POSITION = Position.new(x: 1, y: 10)
|
76
|
+
END_POSITION = Position.new(x: 6, y: 10)
|
77
|
+
NAME = :history
|
78
|
+
|
79
|
+
class << self
|
80
|
+
def back?
|
81
|
+
self == Back
|
82
|
+
end
|
83
|
+
|
84
|
+
def forward?
|
85
|
+
self == Forward
|
86
|
+
end
|
87
|
+
|
88
|
+
def resume?
|
89
|
+
self == Resume
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class Back < History
|
94
|
+
START_POSITION = Position.new(x: 1, y: 10)
|
95
|
+
END_POSITION = Position.new(x: 2, y: 10)
|
15
96
|
end
|
16
97
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
RANGE = {x: Range.new(START_INDEX[:x], END_INDEX[:x]), y: Range.new(START_INDEX[:y], END_INDEX[:y])}
|
98
|
+
class Forward < History
|
99
|
+
START_POSITION = Position.new(x: 3, y: 10)
|
100
|
+
END_POSITION = Position.new(x: 4, y: 10)
|
21
101
|
end
|
102
|
+
|
103
|
+
class Resume < History
|
104
|
+
START_POSITION = Position.new(x: 5, y: 10)
|
105
|
+
END_POSITION = Position.new(x: 6, y: 10)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class Infomation < DisplayArea
|
110
|
+
START_POSITION = Position.new(x: 1, y: 11)
|
111
|
+
END_POSITION = Position.new(x: 18, y: 15)
|
112
|
+
NAME = :infomation
|
22
113
|
end
|
23
114
|
|
24
|
-
|
25
|
-
|
26
|
-
|
115
|
+
class Others < DisplayArea
|
116
|
+
START_POSITION = Position.new(x: nil, y: nil)
|
117
|
+
END_POSITION = Position.new(x: nil, y: nil)
|
118
|
+
NAME = :others
|
27
119
|
end
|
28
120
|
end
|
29
121
|
end
|
@@ -14,10 +14,12 @@ module ConsoleShogi
|
|
14
14
|
|
15
15
|
module EscapeSequence
|
16
16
|
RESET = "\e[0m"
|
17
|
-
RESET_CURSOR = "\e[1;1H"
|
18
17
|
SCREEN_CLEAR = "\e[2J"
|
19
18
|
SCREEN_CLEAR_AFTER_CURSOR = "\e[0J"
|
20
|
-
|
19
|
+
SCREEN_CLEAR_NOW_LINE = "\e[2K"
|
20
|
+
MOVE_START_POINT = "\e[1;1H"
|
21
|
+
MOVE_HISTORY_AREA = "\e[#{DisplayArea::History.start_position.y};#{DisplayArea::History.start_position.x}H"
|
22
|
+
MOVE_INFOMATION_AREA = "\e[#{DisplayArea::Infomation.start_position.y};#{DisplayArea::Infomation.start_position.x}H"
|
21
23
|
CURRENT_POSITION = "\e[6n"
|
22
24
|
MOVE_RIGHT_2 = "\e[2C"
|
23
25
|
end
|
@@ -33,16 +35,34 @@ module ConsoleShogi
|
|
33
35
|
def clear_scrren
|
34
36
|
print EscapeSequence::SCREEN_CLEAR
|
35
37
|
|
36
|
-
print EscapeSequence::
|
38
|
+
print EscapeSequence::MOVE_START_POINT
|
39
|
+
end
|
40
|
+
|
41
|
+
def print_board(board:, sente_komadai:, gote_komadai:)
|
42
|
+
print EscapeSequence::MOVE_START_POINT
|
43
|
+
|
44
|
+
board.matrix.row_vectors.each_with_index do |vector, i|
|
45
|
+
vector.each_with_index do |piece, j|
|
46
|
+
print_image(image: piece.image, height: image_height)
|
47
|
+
end
|
48
|
+
|
49
|
+
print "#{EscapeSequence::RESET}\n"
|
50
|
+
end
|
51
|
+
|
52
|
+
# NOTE 駒台を表示
|
53
|
+
print_komadai(komadai: sente_komadai, start_position: DisplayArea::Komadai::Sente.start_position)
|
54
|
+
print_komadai(komadai: gote_komadai, start_position: DisplayArea::Komadai::Gote.start_position)
|
55
|
+
|
56
|
+
back_to_cursor
|
37
57
|
end
|
38
58
|
|
39
59
|
# TODO 駒台の表示方法も考える
|
40
60
|
def print_diff_board(previous_board:, board:, sente_komadai:, gote_komadai:)
|
41
|
-
print EscapeSequence::
|
61
|
+
print EscapeSequence::MOVE_START_POINT
|
42
62
|
|
43
63
|
board.matrix.row_vectors.each_with_index do |vector, i|
|
44
64
|
vector.each_with_index do |piece, j|
|
45
|
-
if previous_board.matrix[i, j]
|
65
|
+
if previous_board.matrix[i, j].nil? || previous_board.matrix[i, j].same?(piece)
|
46
66
|
print EscapeSequence::MOVE_RIGHT_2
|
47
67
|
else
|
48
68
|
print_image(image: piece.image, height: image_height)
|
@@ -53,15 +73,23 @@ module ConsoleShogi
|
|
53
73
|
end
|
54
74
|
|
55
75
|
# NOTE 駒台を表示
|
56
|
-
print_komadai(komadai: sente_komadai,
|
57
|
-
print_komadai(komadai: gote_komadai,
|
76
|
+
print_komadai(komadai: sente_komadai, start_position: DisplayArea::Komadai::Sente.start_position)
|
77
|
+
print_komadai(komadai: gote_komadai, start_position: DisplayArea::Komadai::Gote.start_position)
|
78
|
+
|
79
|
+
back_to_cursor
|
80
|
+
end
|
81
|
+
|
82
|
+
def print_start_history
|
83
|
+
print EscapeSequence::MOVE_INFOMATION_AREA
|
84
|
+
print EscapeSequence::SCREEN_CLEAR_AFTER_CURSOR
|
85
|
+
|
86
|
+
print '履歴モード'
|
58
87
|
|
59
|
-
# NOTE back a cursor
|
60
88
|
back_to_cursor
|
61
89
|
end
|
62
90
|
|
63
|
-
def focus_piece(
|
64
|
-
piece =
|
91
|
+
def focus_piece(board:, cursor:)
|
92
|
+
piece = board.fetch_piece(x: cursor.grid_position.x, y: cursor.grid_position.y)
|
65
93
|
|
66
94
|
return if piece.nil?
|
67
95
|
|
@@ -70,18 +98,18 @@ module ConsoleShogi
|
|
70
98
|
back_to_cursor
|
71
99
|
end
|
72
100
|
|
73
|
-
def deactive_piece(
|
101
|
+
def deactive_piece(board:, previous_cursor:)
|
74
102
|
print "\e[#{previous_cursor.terminal_position.y};#{previous_cursor.terminal_position.x}H"
|
75
103
|
|
76
|
-
piece =
|
104
|
+
piece = board.fetch_piece(x: previous_cursor.grid_position.x, y: previous_cursor.grid_position.y)
|
77
105
|
|
78
106
|
print_image(image: piece.image, height: image_height) unless piece.nil?
|
79
107
|
|
80
108
|
back_to_cursor
|
81
109
|
end
|
82
110
|
|
83
|
-
def active_piece(
|
84
|
-
piece =
|
111
|
+
def active_piece(board:, cursor:)
|
112
|
+
piece = board.fetch_piece(x: cursor.grid_position.x, y: cursor.grid_position.y)
|
85
113
|
|
86
114
|
return if piece.nil?
|
87
115
|
|
@@ -91,7 +119,7 @@ module ConsoleShogi
|
|
91
119
|
end
|
92
120
|
|
93
121
|
def select_promotion
|
94
|
-
print EscapeSequence::
|
122
|
+
print EscapeSequence::MOVE_INFOMATION_AREA
|
95
123
|
|
96
124
|
prompt = TTY::Prompt.new
|
97
125
|
|
@@ -106,12 +134,12 @@ module ConsoleShogi
|
|
106
134
|
|
107
135
|
print_image(image: player.win_image, height: image_height * 3)
|
108
136
|
|
109
|
-
print EscapeSequence::
|
137
|
+
print EscapeSequence::MOVE_INFOMATION_AREA
|
110
138
|
end
|
111
139
|
|
112
140
|
# TODO 見た目は後で直す
|
113
141
|
def print_teban(teban)
|
114
|
-
print EscapeSequence::
|
142
|
+
print EscapeSequence::MOVE_INFOMATION_AREA
|
115
143
|
print EscapeSequence::SCREEN_CLEAR_AFTER_CURSOR
|
116
144
|
|
117
145
|
print "手番 : #{TEBAN[teban.to_sym]}"
|
@@ -119,6 +147,18 @@ module ConsoleShogi
|
|
119
147
|
back_to_cursor
|
120
148
|
end
|
121
149
|
|
150
|
+
# TODO データの指定の仕方は後で整理する
|
151
|
+
def print_history_button
|
152
|
+
print EscapeSequence::MOVE_HISTORY_AREA
|
153
|
+
print EscapeSequence::SCREEN_CLEAR_NOW_LINE
|
154
|
+
|
155
|
+
print_image(image: File.read("images/history_button/left.png"), height: image_height)
|
156
|
+
print_image(image: File.read("images/history_button/right.png"), height: image_height)
|
157
|
+
print_image(image: File.read("images/history_button/play.png"), height: image_height)
|
158
|
+
|
159
|
+
back_to_cursor
|
160
|
+
end
|
161
|
+
|
122
162
|
private
|
123
163
|
|
124
164
|
IMAGE_HEIGHT_PIXEL = 240
|
@@ -134,15 +174,15 @@ module ConsoleShogi
|
|
134
174
|
|
135
175
|
print EscapeSequence::SCREEN_CLEAR
|
136
176
|
# NOTE カーソルを1行1列に移動
|
137
|
-
print EscapeSequence::
|
177
|
+
print EscapeSequence::MOVE_START_POINT
|
138
178
|
|
139
179
|
fixed_height
|
140
180
|
end
|
141
181
|
|
142
182
|
# TODO view 用の Player 作って整理する
|
143
|
-
def print_komadai(komadai:,
|
183
|
+
def print_komadai(komadai:, start_position:)
|
144
184
|
komadai.pieces.row_vectors.each_with_index do |row_pieces, i|
|
145
|
-
print "\e[#{y + i};#{x}H"
|
185
|
+
print "\e[#{start_position.y + i};#{start_position.x}H"
|
146
186
|
|
147
187
|
row_pieces.each do |p|
|
148
188
|
print_image(image: p.image, height: image_height)
|
data/lib/console_shogi.rb
CHANGED
@@ -1,20 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'console_shogi/version'
|
4
|
-
require_relative 'console_shogi/game'
|
5
4
|
require_relative 'console_shogi/player'
|
6
5
|
require_relative 'console_shogi/board'
|
7
6
|
require_relative 'console_shogi/piece'
|
8
7
|
require_relative 'console_shogi/komadai'
|
9
8
|
require_relative 'console_shogi/teban'
|
9
|
+
require_relative 'console_shogi/grid_position'
|
10
10
|
|
11
11
|
require_relative 'console_shogi/terminal/display_area'
|
12
12
|
require_relative 'console_shogi/terminal/operator'
|
13
13
|
require_relative 'console_shogi/terminal/cursor'
|
14
14
|
|
15
|
+
require_relative 'console_shogi/game'
|
16
|
+
require_relative 'console_shogi/game_history'
|
15
17
|
require_relative 'console_shogi/new_board_builder'
|
16
18
|
require_relative 'console_shogi/piece_mover'
|
17
|
-
require_relative 'console_shogi/
|
19
|
+
require_relative 'console_shogi/piece_mover_from_board'
|
20
|
+
require_relative 'console_shogi/piece_mover_from_komadai'
|
18
21
|
require_relative 'console_shogi/piece_movement_checker'
|
19
22
|
|
20
23
|
module ConsoleShogi
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: console-shogi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.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-06-
|
11
|
+
date: 2023-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: You can play the Shogi in terminal
|
14
14
|
email:
|
@@ -87,6 +87,9 @@ files:
|
|
87
87
|
- images/focused/sente/tokin.png
|
88
88
|
- images/focused/sente/uma.png
|
89
89
|
- images/gote_shori.png
|
90
|
+
- images/history_button/left.png
|
91
|
+
- images/history_button/play.png
|
92
|
+
- images/history_button/right.png
|
90
93
|
- images/nomal/gote/fu.png
|
91
94
|
- images/nomal/gote/gin.png
|
92
95
|
- images/nomal/gote/gyoku.png
|
@@ -122,12 +125,15 @@ files:
|
|
122
125
|
- lib/console_shogi.rb
|
123
126
|
- lib/console_shogi/board.rb
|
124
127
|
- lib/console_shogi/game.rb
|
128
|
+
- lib/console_shogi/game_history.rb
|
129
|
+
- lib/console_shogi/grid_position.rb
|
125
130
|
- lib/console_shogi/komadai.rb
|
126
131
|
- lib/console_shogi/new_board_builder.rb
|
127
132
|
- lib/console_shogi/piece.rb
|
128
133
|
- lib/console_shogi/piece_movement_checker.rb
|
129
134
|
- lib/console_shogi/piece_mover.rb
|
130
|
-
- lib/console_shogi/
|
135
|
+
- lib/console_shogi/piece_mover_from_board.rb
|
136
|
+
- lib/console_shogi/piece_mover_from_komadai.rb
|
131
137
|
- lib/console_shogi/player.rb
|
132
138
|
- lib/console_shogi/teban.rb
|
133
139
|
- lib/console_shogi/terminal/cursor.rb
|
@@ -156,7 +162,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
156
162
|
- !ruby/object:Gem::Version
|
157
163
|
version: '0'
|
158
164
|
requirements: []
|
159
|
-
rubygems_version: 3.4.
|
165
|
+
rubygems_version: 3.4.13
|
160
166
|
signing_key:
|
161
167
|
specification_version: 4
|
162
168
|
summary: You can play the Shogi in terminal
|
@@ -1,59 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ConsoleShogi
|
4
|
-
class PieceMoverOnKomadai
|
5
|
-
def initialize(board:, komadai:, from:, to:)
|
6
|
-
@board = board
|
7
|
-
@komadai = komadai
|
8
|
-
# 駒台の index に直す必要がある
|
9
|
-
@from = from
|
10
|
-
@to = to
|
11
|
-
@moved_piece = false
|
12
|
-
end
|
13
|
-
|
14
|
-
def move!
|
15
|
-
@moved_piece = move_piece!
|
16
|
-
end
|
17
|
-
|
18
|
-
def moved_piece?
|
19
|
-
@moved_piece
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
attr_reader :board, :komadai, :from, :to
|
25
|
-
|
26
|
-
def move_piece!
|
27
|
-
return false if from_piece.nil? || from_piece.none?
|
28
|
-
|
29
|
-
# TODO ここで location の key もってるの期待してるの酷い、修正する
|
30
|
-
return false if to[:location] != :board
|
31
|
-
return false unless can_drop?(piece: from_piece, to: to)
|
32
|
-
|
33
|
-
komadai.pick_up_piece!(from: from)
|
34
|
-
board.put_piece!(piece: from_piece, to: to)
|
35
|
-
|
36
|
-
true
|
37
|
-
end
|
38
|
-
|
39
|
-
def from_piece
|
40
|
-
@from_piece ||= komadai.fetch_piece(x: from[:x], y: from[:y])
|
41
|
-
end
|
42
|
-
|
43
|
-
def can_drop?(piece:, to:)
|
44
|
-
to_piece = board.fetch_piece(x: to[:x], y: to[:y])
|
45
|
-
|
46
|
-
return false unless to_piece.none?
|
47
|
-
|
48
|
-
return false if nifu?(piece, to)
|
49
|
-
|
50
|
-
# TODO 動かせない場所に打つことはできないようにする
|
51
|
-
true
|
52
|
-
end
|
53
|
-
|
54
|
-
def nifu?(piece, to)
|
55
|
-
piece.fu? &&
|
56
|
-
board.matrix.column(to[:x]).any? {|p| p.fu? && piece.teban == p.teban }
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|