console-shogi 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +14 -0
- data/Gemfile.lock +44 -0
- data/LICENSE.txt +21 -0
- data/README.md +1 -0
- data/Rakefile +4 -0
- data/images/gote/fu.png +0 -0
- data/images/gote/gin.png +0 -0
- data/images/gote/gyoku.png +0 -0
- data/images/gote/hisha.png +0 -0
- data/images/gote/kaku.png +0 -0
- data/images/gote/keima.png +0 -0
- data/images/gote/kin.png +0 -0
- data/images/gote/kyosha.png +0 -0
- data/images/gote/narigin.png +0 -0
- data/images/gote/narikei.png +0 -0
- data/images/gote/narikyo.png +0 -0
- data/images/gote/nonepiece.png +0 -0
- data/images/gote/ohsho.png +0 -0
- data/images/gote/ryu.png +0 -0
- data/images/gote/shori.png +0 -0
- data/images/gote/tokin.png +0 -0
- data/images/gote/uma.png +0 -0
- data/images/sente/fu.png +0 -0
- data/images/sente/gin.png +0 -0
- data/images/sente/gyoku.png +0 -0
- data/images/sente/hisha.png +0 -0
- data/images/sente/kaku.png +0 -0
- data/images/sente/keima.png +0 -0
- data/images/sente/kin.png +0 -0
- data/images/sente/kyosha.png +0 -0
- data/images/sente/narigin.png +0 -0
- data/images/sente/narikei.png +0 -0
- data/images/sente/narikyo.png +0 -0
- data/images/sente/nonepiece.png +0 -0
- data/images/sente/ohsho.png +0 -0
- data/images/sente/ryu.png +0 -0
- data/images/sente/shori.png +0 -0
- data/images/sente/tokin.png +0 -0
- data/images/sente/uma.png +0 -0
- data/lib/console_shogi/board.rb +53 -0
- data/lib/console_shogi/game.rb +57 -0
- data/lib/console_shogi/komadai.rb +59 -0
- data/lib/console_shogi/new_board_builder.rb +113 -0
- data/lib/console_shogi/piece.rb +327 -0
- data/lib/console_shogi/piece_mover.rb +82 -0
- data/lib/console_shogi/piece_mover_on_komadai.rb +65 -0
- data/lib/console_shogi/player.rb +42 -0
- data/lib/console_shogi/terminal_operator.rb +211 -0
- data/lib/console_shogi/version.rb +5 -0
- data/lib/console_shogi.rb +21 -0
- metadata +96 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3a6623ab23cf26496870d6849e0e3433035756ad6d0a4d17475f4afa4619c13a
|
4
|
+
data.tar.gz: d2ef4c45e1f79a20a7d7105a21d5b2b2d33a1373e6ef918c13471dd111478640
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: eea768b6b1d02aedf1be04c06691b9ed5ba7e4ac4cce6e8da479cbf8ffc7ab1b3e7411018640f751b72556485d8edafbb1ecd9e4d116552050226152982a73af
|
7
|
+
data.tar.gz: 2559ca6fa74b5154ac626eb6c0de61dd15b0d48fe6758cce573672360f5d6eea0c980be9112017fefb9907a44046d0529eca66ae8622fca784aaacae537f60df
|
data/Gemfile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
# Specify your gem's dependencies in console-shogi.gemspec
|
6
|
+
gemspec
|
7
|
+
|
8
|
+
ruby "~> 3.2.0"
|
9
|
+
|
10
|
+
gem "rake", "~> 13.0"
|
11
|
+
gem "matrix", "~> 0.4.2"
|
12
|
+
gem "pry", "~> 0.14.2"
|
13
|
+
|
14
|
+
gem "tty-prompt", "~> 0.23.1"
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
console-shogi (0.1.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
coderay (1.1.3)
|
10
|
+
matrix (0.4.2)
|
11
|
+
method_source (1.0.0)
|
12
|
+
pastel (0.8.0)
|
13
|
+
tty-color (~> 0.5)
|
14
|
+
pry (0.14.2)
|
15
|
+
coderay (~> 1.1)
|
16
|
+
method_source (~> 1.0)
|
17
|
+
rake (13.0.6)
|
18
|
+
tty-color (0.6.0)
|
19
|
+
tty-cursor (0.7.1)
|
20
|
+
tty-prompt (0.23.1)
|
21
|
+
pastel (~> 0.8)
|
22
|
+
tty-reader (~> 0.8)
|
23
|
+
tty-reader (0.9.0)
|
24
|
+
tty-cursor (~> 0.7)
|
25
|
+
tty-screen (~> 0.8)
|
26
|
+
wisper (~> 2.0)
|
27
|
+
tty-screen (0.8.1)
|
28
|
+
wisper (2.0.1)
|
29
|
+
|
30
|
+
PLATFORMS
|
31
|
+
arm64-darwin-21
|
32
|
+
|
33
|
+
DEPENDENCIES
|
34
|
+
console-shogi!
|
35
|
+
matrix (~> 0.4.2)
|
36
|
+
pry (~> 0.14.2)
|
37
|
+
rake (~> 13.0)
|
38
|
+
tty-prompt (~> 0.23.1)
|
39
|
+
|
40
|
+
RUBY VERSION
|
41
|
+
ruby 3.2.2p53
|
42
|
+
|
43
|
+
BUNDLED WITH
|
44
|
+
2.4.12
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2023 pekepek
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# console-shogi
|
data/Rakefile
ADDED
data/images/gote/fu.png
ADDED
Binary file
|
data/images/gote/gin.png
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/images/gote/kin.png
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/images/gote/ryu.png
ADDED
Binary file
|
Binary file
|
Binary file
|
data/images/gote/uma.png
ADDED
Binary file
|
data/images/sente/fu.png
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'matrix'
|
4
|
+
|
5
|
+
module ConsoleShogi
|
6
|
+
class Board
|
7
|
+
def initialize(pieces:)
|
8
|
+
@pieces = Matrix.rows(pieces)
|
9
|
+
end
|
10
|
+
|
11
|
+
def matrix
|
12
|
+
@pieces
|
13
|
+
end
|
14
|
+
|
15
|
+
def within_range?(x:, y:)
|
16
|
+
case {x: x, y: y}
|
17
|
+
in x: 0..8, y: 0..8
|
18
|
+
true
|
19
|
+
else
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def fetch_piece(x:, y:)
|
25
|
+
pieces[y, x]
|
26
|
+
end
|
27
|
+
|
28
|
+
def promote_piece!(x:, y:)
|
29
|
+
@pieces[y, x] = pieces[y, x].promote
|
30
|
+
end
|
31
|
+
|
32
|
+
def put_piece!(piece:, to:)
|
33
|
+
@pieces[to[:y], to[:x]] = piece
|
34
|
+
end
|
35
|
+
|
36
|
+
def move_piece!(from:, to:)
|
37
|
+
from_piece = @pieces[from[:y], from[:x]]
|
38
|
+
@pieces[from[:y], from[:x]] = NonePiece.new
|
39
|
+
|
40
|
+
to_piece = @pieces[to[:y], to[:x]]
|
41
|
+
|
42
|
+
# NOTE 駒を取る、リファクタする
|
43
|
+
# from_piece.player で player 取るの違和感
|
44
|
+
from_piece.player.capture_piece!(to_piece) unless to_piece.none?
|
45
|
+
|
46
|
+
@pieces[to[:y], to[:x]] = from_piece
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
attr_reader :pieces
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'io/console'
|
4
|
+
|
5
|
+
module ConsoleShogi
|
6
|
+
class Game
|
7
|
+
def initialize
|
8
|
+
@sente_player = Player.new(teban: Player::Teban::SENTE)
|
9
|
+
@gote_player = Player.new(teban: Player::Teban::GOTE)
|
10
|
+
@board = NewBoardBuilder.build(sente_player: @sente_player, gote_player: @gote_player)
|
11
|
+
end
|
12
|
+
|
13
|
+
def start
|
14
|
+
TerminalOperator.print_board(board: board, sente_komadai: sente_player.komadai, gote_komadai: gote_player.komadai)
|
15
|
+
|
16
|
+
while key = STDIN.getch
|
17
|
+
# NOTE Ctrl-C を押したら終了
|
18
|
+
if key == "\C-c"
|
19
|
+
exit
|
20
|
+
# NOTE 矢印キーを押したらカーソルを移動
|
21
|
+
elsif key == "\e" && STDIN.getch == "["
|
22
|
+
key = STDIN.getch
|
23
|
+
|
24
|
+
TerminalOperator.move_cursor(key)
|
25
|
+
# NOTE Enter を押したら駒を移動
|
26
|
+
elsif key == "\r"
|
27
|
+
# TODO このまま Hash にするかは要検討
|
28
|
+
index = TerminalOperator.squares_index
|
29
|
+
|
30
|
+
case index[:location]
|
31
|
+
when :board
|
32
|
+
PieceMover.new(board: board, from: index).move!
|
33
|
+
when :sente_komadai
|
34
|
+
PieceMoverOnKomadai.new(board: board, komadai: sente_player.komadai, from: index).drop!
|
35
|
+
when :gote_komadai
|
36
|
+
PieceMoverOnKomadai.new(board: board, komadai: gote_player.komadai, from: index).drop!
|
37
|
+
end
|
38
|
+
|
39
|
+
# TODO 描写に時間がかかるので、差分のみ表示できる様にする
|
40
|
+
TerminalOperator.print_board(board: board, sente_komadai: sente_player.komadai, gote_komadai: gote_player.komadai)
|
41
|
+
|
42
|
+
# TODO ひとまず動く物を作った。リファクタリングする
|
43
|
+
[sente_player, gote_player].each do |player|
|
44
|
+
next unless player.win?
|
45
|
+
|
46
|
+
TerminalOperator.print_winner(player.teban)
|
47
|
+
exit
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
attr_reader :board, :sente_player, :gote_player
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'matrix'
|
4
|
+
|
5
|
+
module ConsoleShogi
|
6
|
+
class Komadai
|
7
|
+
class NoSpaceError < StandardError; end
|
8
|
+
|
9
|
+
attr_reader :pieces
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@pieces = empty_pieces
|
13
|
+
end
|
14
|
+
|
15
|
+
def expand!
|
16
|
+
@pieces = pieces.hstack(empty_pieces)
|
17
|
+
end
|
18
|
+
|
19
|
+
def have_space?
|
20
|
+
pieces.any?(&:none?)
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch_piece(x:, y:)
|
24
|
+
pieces[y, x]
|
25
|
+
end
|
26
|
+
|
27
|
+
def put(piece:)
|
28
|
+
index = pieces.index(&:none?)
|
29
|
+
|
30
|
+
raise NoSpaceError if index.nil?
|
31
|
+
|
32
|
+
@pieces[*index] = piece
|
33
|
+
|
34
|
+
@pieces = sort_pieces
|
35
|
+
end
|
36
|
+
|
37
|
+
def pick_up_piece!(from:)
|
38
|
+
@pieces[from[:y], from[:x]] = NonePiece.new
|
39
|
+
@pieces = sort_pieces
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def sort_pieces
|
45
|
+
column_num = pieces.count / 3
|
46
|
+
Matrix.rows(pieces.sort_by {|p| -p.number }.each_slice(column_num).to_a)
|
47
|
+
end
|
48
|
+
|
49
|
+
def empty_pieces
|
50
|
+
Matrix.rows(
|
51
|
+
[
|
52
|
+
[NonePiece.new, NonePiece.new, NonePiece.new],
|
53
|
+
[NonePiece.new, NonePiece.new, NonePiece.new],
|
54
|
+
[NonePiece.new, NonePiece.new, NonePiece.new]
|
55
|
+
]
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ConsoleShogi
|
4
|
+
class NewBoardBuilder
|
5
|
+
class << self
|
6
|
+
def build(sente_player: , gote_player:)
|
7
|
+
Board.new(
|
8
|
+
pieces: [
|
9
|
+
[
|
10
|
+
Kyosha.new(player: gote_player),
|
11
|
+
Keima.new(player: gote_player),
|
12
|
+
Gin.new(player: gote_player),
|
13
|
+
Kin.new(player: gote_player),
|
14
|
+
Ohsho.new(player: gote_player),
|
15
|
+
Kin.new(player: gote_player),
|
16
|
+
Gin.new(player: gote_player),
|
17
|
+
Keima.new(player: gote_player),
|
18
|
+
Kyosha.new(player: gote_player)
|
19
|
+
],
|
20
|
+
[
|
21
|
+
NonePiece.new,
|
22
|
+
Hisha.new(player: gote_player),
|
23
|
+
NonePiece.new,
|
24
|
+
NonePiece.new,
|
25
|
+
NonePiece.new,
|
26
|
+
NonePiece.new,
|
27
|
+
NonePiece.new,
|
28
|
+
Kaku.new(player: gote_player),
|
29
|
+
NonePiece.new
|
30
|
+
],
|
31
|
+
[
|
32
|
+
Fu.new(player: gote_player),
|
33
|
+
Fu.new(player: gote_player),
|
34
|
+
Fu.new(player: gote_player),
|
35
|
+
Fu.new(player: gote_player),
|
36
|
+
Fu.new(player: gote_player),
|
37
|
+
Fu.new(player: gote_player),
|
38
|
+
Fu.new(player: gote_player),
|
39
|
+
Fu.new(player: gote_player),
|
40
|
+
Fu.new(player: gote_player)
|
41
|
+
],
|
42
|
+
[
|
43
|
+
NonePiece.new,
|
44
|
+
NonePiece.new,
|
45
|
+
NonePiece.new,
|
46
|
+
NonePiece.new,
|
47
|
+
NonePiece.new,
|
48
|
+
NonePiece.new,
|
49
|
+
NonePiece.new,
|
50
|
+
NonePiece.new,
|
51
|
+
NonePiece.new
|
52
|
+
],
|
53
|
+
[
|
54
|
+
NonePiece.new,
|
55
|
+
NonePiece.new,
|
56
|
+
NonePiece.new,
|
57
|
+
NonePiece.new,
|
58
|
+
NonePiece.new,
|
59
|
+
NonePiece.new,
|
60
|
+
NonePiece.new,
|
61
|
+
NonePiece.new,
|
62
|
+
NonePiece.new
|
63
|
+
],
|
64
|
+
[
|
65
|
+
NonePiece.new,
|
66
|
+
NonePiece.new,
|
67
|
+
NonePiece.new,
|
68
|
+
NonePiece.new,
|
69
|
+
NonePiece.new,
|
70
|
+
NonePiece.new,
|
71
|
+
NonePiece.new,
|
72
|
+
NonePiece.new,
|
73
|
+
NonePiece.new
|
74
|
+
],
|
75
|
+
[
|
76
|
+
Fu.new(player: sente_player),
|
77
|
+
Fu.new(player: sente_player),
|
78
|
+
Fu.new(player: sente_player),
|
79
|
+
Fu.new(player: sente_player),
|
80
|
+
Fu.new(player: sente_player),
|
81
|
+
Fu.new(player: sente_player),
|
82
|
+
Fu.new(player: sente_player),
|
83
|
+
Fu.new(player: sente_player),
|
84
|
+
Fu.new(player: sente_player)
|
85
|
+
],
|
86
|
+
[
|
87
|
+
NonePiece.new,
|
88
|
+
Kaku.new(player: sente_player),
|
89
|
+
NonePiece.new,
|
90
|
+
NonePiece.new,
|
91
|
+
NonePiece.new,
|
92
|
+
NonePiece.new,
|
93
|
+
NonePiece.new,
|
94
|
+
Hisha.new(player: sente_player),
|
95
|
+
NonePiece.new
|
96
|
+
],
|
97
|
+
[
|
98
|
+
Kyosha.new(player: sente_player),
|
99
|
+
Keima.new(player: sente_player),
|
100
|
+
Gin.new(player: sente_player),
|
101
|
+
Kin.new(player: sente_player),
|
102
|
+
Ohsho.new(player: sente_player),
|
103
|
+
Kin.new(player: sente_player),
|
104
|
+
Gin.new(player: sente_player),
|
105
|
+
Keima.new(player: sente_player),
|
106
|
+
Kyosha.new(player: sente_player)
|
107
|
+
]
|
108
|
+
]
|
109
|
+
)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,327 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ConsoleShogi
|
4
|
+
class Piece
|
5
|
+
attr_reader :player
|
6
|
+
|
7
|
+
def initialize(player: nil)
|
8
|
+
@player = player
|
9
|
+
end
|
10
|
+
|
11
|
+
def move
|
12
|
+
raise NotImplementedError
|
13
|
+
end
|
14
|
+
|
15
|
+
def promote
|
16
|
+
return self unless can_promote?
|
17
|
+
|
18
|
+
promoted
|
19
|
+
end
|
20
|
+
|
21
|
+
def promoted?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
def number
|
26
|
+
self::class::NUMBER
|
27
|
+
end
|
28
|
+
|
29
|
+
def image
|
30
|
+
if player.sente?
|
31
|
+
File.read("images/sente/#{self::class.name.split('::').last.downcase}.png")
|
32
|
+
else
|
33
|
+
File.read("images/gote/#{self::class.name.split('::').last.downcase}.png")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def none?
|
38
|
+
self::class == NonePiece
|
39
|
+
end
|
40
|
+
|
41
|
+
def kaku?
|
42
|
+
self::class == Kaku
|
43
|
+
end
|
44
|
+
|
45
|
+
def hisha?
|
46
|
+
self::class == Hisha
|
47
|
+
end
|
48
|
+
|
49
|
+
def kyosha?
|
50
|
+
self::class == Kyosha
|
51
|
+
end
|
52
|
+
|
53
|
+
def fu?
|
54
|
+
self::class == Fu
|
55
|
+
end
|
56
|
+
|
57
|
+
def moves
|
58
|
+
ms = base_moves
|
59
|
+
|
60
|
+
if player.sente?
|
61
|
+
ms
|
62
|
+
else
|
63
|
+
ms.map {|m| m.transform_values {|v| v * -1 } }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def change_player!(player)
|
68
|
+
@player = player
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class NonePiece < Piece
|
73
|
+
NUMBER = 0
|
74
|
+
|
75
|
+
def moves
|
76
|
+
[]
|
77
|
+
end
|
78
|
+
|
79
|
+
def player
|
80
|
+
NonPlayer.new
|
81
|
+
end
|
82
|
+
|
83
|
+
def can_promote?
|
84
|
+
false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Fu < Piece
|
89
|
+
NUMBER = 1
|
90
|
+
|
91
|
+
def base_moves
|
92
|
+
[
|
93
|
+
{x: 0, y: -1}
|
94
|
+
]
|
95
|
+
end
|
96
|
+
|
97
|
+
def can_promote?
|
98
|
+
true
|
99
|
+
end
|
100
|
+
|
101
|
+
def promoted
|
102
|
+
PromotedPiece::Tokin.new(original: self)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
class Kyosha < Piece
|
107
|
+
NUMBER = 2
|
108
|
+
|
109
|
+
def base_moves
|
110
|
+
(1..8).map {|n|
|
111
|
+
{x: 0, y: -1 * n}
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
def can_promote?
|
116
|
+
true
|
117
|
+
end
|
118
|
+
|
119
|
+
def promoted
|
120
|
+
PromotedPiece::Narikyo.new(original: self)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class Keima < Piece
|
125
|
+
NUMBER = 3
|
126
|
+
|
127
|
+
def base_moves
|
128
|
+
[
|
129
|
+
{x: 1, y: -2},
|
130
|
+
{x: -1, y: -2},
|
131
|
+
]
|
132
|
+
end
|
133
|
+
|
134
|
+
def can_promote?
|
135
|
+
true
|
136
|
+
end
|
137
|
+
|
138
|
+
def promoted
|
139
|
+
PromotedPiece::Narikei.new(original: self)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
class Gin < Piece
|
144
|
+
NUMBER = 4
|
145
|
+
|
146
|
+
def base_moves
|
147
|
+
[
|
148
|
+
{x: -1, y: -1},
|
149
|
+
{x: 0, y: -1},
|
150
|
+
{x: 1, y: -1},
|
151
|
+
{x: 1, y: 1},
|
152
|
+
{x: -1, y: 1}
|
153
|
+
]
|
154
|
+
end
|
155
|
+
|
156
|
+
def can_promote?
|
157
|
+
true
|
158
|
+
end
|
159
|
+
|
160
|
+
def promoted
|
161
|
+
PromotedPiece::Narigin.new(original: self)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
class Kin < Piece
|
166
|
+
NUMBER = 5
|
167
|
+
|
168
|
+
def base_moves
|
169
|
+
[
|
170
|
+
{x: -1, y: -1},
|
171
|
+
{x: 0, y: -1},
|
172
|
+
{x: 1, y: -1},
|
173
|
+
{x: 1, y: 0},
|
174
|
+
{x: -1, y: 0},
|
175
|
+
{x: 0, y: 1}
|
176
|
+
]
|
177
|
+
end
|
178
|
+
|
179
|
+
def can_promote?
|
180
|
+
false
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
class Kaku < Piece
|
185
|
+
NUMBER = 6
|
186
|
+
|
187
|
+
def base_moves
|
188
|
+
(1..8).flat_map {|n|
|
189
|
+
[
|
190
|
+
{x: -1 * n, y: -1 * n},
|
191
|
+
{x: 1 * n, y: -1 * n},
|
192
|
+
{x: -1 * n, y: 1 * n},
|
193
|
+
{x: 1 * n, y: 1 * n}
|
194
|
+
]
|
195
|
+
}
|
196
|
+
end
|
197
|
+
|
198
|
+
def can_promote?
|
199
|
+
true
|
200
|
+
end
|
201
|
+
|
202
|
+
def promoted
|
203
|
+
PromotedPiece::Uma.new(original: self)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
class Hisha < Piece
|
208
|
+
NUMBER = 7
|
209
|
+
|
210
|
+
def base_moves
|
211
|
+
(1..8).flat_map {|n|
|
212
|
+
[
|
213
|
+
{x: 0, y: -1 * n},
|
214
|
+
{x: 0, y: 1 * n},
|
215
|
+
{x: -1 * n, y: 0},
|
216
|
+
{x: 1 * n, y: 0}
|
217
|
+
]
|
218
|
+
}
|
219
|
+
end
|
220
|
+
|
221
|
+
def can_promote?
|
222
|
+
true
|
223
|
+
end
|
224
|
+
|
225
|
+
def promoted
|
226
|
+
PromotedPiece::Ryu.new(original: self)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
class Ohsho < Piece
|
231
|
+
NUMBER = 8
|
232
|
+
|
233
|
+
def base_moves
|
234
|
+
[
|
235
|
+
{x: -1, y: -1},
|
236
|
+
{x: 0, y: -1},
|
237
|
+
{x: 1, y: -1},
|
238
|
+
{x: 1, y: 0},
|
239
|
+
{x: -1, y: 0},
|
240
|
+
{x: -1, y: 1},
|
241
|
+
{x: 0, y: 1},
|
242
|
+
{x: 1, y: 1}
|
243
|
+
]
|
244
|
+
end
|
245
|
+
|
246
|
+
def can_promote?
|
247
|
+
false
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
class PromotedPiece < Piece
|
252
|
+
attr_reader :player, :original
|
253
|
+
|
254
|
+
def initialize(original: nil)
|
255
|
+
@player = original.player
|
256
|
+
@original = original
|
257
|
+
end
|
258
|
+
|
259
|
+
def can_promote?
|
260
|
+
false
|
261
|
+
end
|
262
|
+
|
263
|
+
def promoted?
|
264
|
+
true
|
265
|
+
end
|
266
|
+
|
267
|
+
class NariKin < PromotedPiece
|
268
|
+
NUMBER = 9
|
269
|
+
|
270
|
+
def base_moves
|
271
|
+
[
|
272
|
+
{x: -1, y: -1},
|
273
|
+
{x: 0, y: -1},
|
274
|
+
{x: 1, y: -1},
|
275
|
+
{x: 1, y: 0},
|
276
|
+
{x: -1, y: 0},
|
277
|
+
{x: 0, y: 1}
|
278
|
+
]
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
class Narigin < NariKin; end
|
283
|
+
class Narikei < NariKin; end
|
284
|
+
class Narikyo < NariKin; end
|
285
|
+
class Tokin < NariKin; end
|
286
|
+
|
287
|
+
class Uma < PromotedPiece
|
288
|
+
NUMBER = 10
|
289
|
+
|
290
|
+
def base_moves
|
291
|
+
(1..8).flat_map {|n|
|
292
|
+
[
|
293
|
+
{x: -1 * n, y: -1 * n},
|
294
|
+
{x: 1 * n, y: -1 * n},
|
295
|
+
{x: -1 * n, y: 1 * n},
|
296
|
+
{x: 1 * n, y: 1 * n}
|
297
|
+
]
|
298
|
+
} + [
|
299
|
+
{x: 0, y: -1},
|
300
|
+
{x: 1, y: 0},
|
301
|
+
{x: -1, y: 0},
|
302
|
+
{x: 0, y: 1}
|
303
|
+
]
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
class Ryu < PromotedPiece
|
308
|
+
NUMBER = 11
|
309
|
+
|
310
|
+
def base_moves
|
311
|
+
(1..8).flat_map {|n|
|
312
|
+
[
|
313
|
+
{x: 0, y: -1 * n},
|
314
|
+
{x: 0, y: 1 * n},
|
315
|
+
{x: -1 * n, y: 0},
|
316
|
+
{x: 1 * n, y: 0}
|
317
|
+
]
|
318
|
+
} + [
|
319
|
+
{x: -1, y: -1},
|
320
|
+
{x: 1, y: -1},
|
321
|
+
{x: -1, y: 1},
|
322
|
+
{x: 1, y: 1}
|
323
|
+
]
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ConsoleShogi
|
4
|
+
class PieceMover
|
5
|
+
def initialize(board:, from:)
|
6
|
+
@board = board
|
7
|
+
@from_piece_index = from
|
8
|
+
end
|
9
|
+
|
10
|
+
def move!
|
11
|
+
return if target_piece.nil? || target_piece.none?
|
12
|
+
|
13
|
+
while key = STDIN.getch
|
14
|
+
if key == "\e" && STDIN.getch == "["
|
15
|
+
key = STDIN.getch
|
16
|
+
|
17
|
+
TerminalOperator.move_cursor(key)
|
18
|
+
elsif key == "\r"
|
19
|
+
to_piece_index = TerminalOperator.squares_index
|
20
|
+
|
21
|
+
return unless can_move?(piece: target_piece, to_piece_index: to_piece_index)
|
22
|
+
|
23
|
+
board.move_piece!(
|
24
|
+
from: {x: from_piece_index[:x], y: from_piece_index[:y]},
|
25
|
+
to: {x: to_piece_index[:x], y: to_piece_index[:y]}
|
26
|
+
)
|
27
|
+
|
28
|
+
# TODO とりあえずここに実装してしまっている。整理したい
|
29
|
+
piece = board.fetch_piece(x: to_piece_index[:x], y: to_piece_index[:y])
|
30
|
+
return unless can_promote?(piece, from_piece_index, to_piece_index)
|
31
|
+
|
32
|
+
board.promote_piece!(x: to_piece_index[:x], y: to_piece_index[:y]) if TerminalOperator.select_promotion
|
33
|
+
|
34
|
+
return
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def can_move?(piece:, to_piece_index:)
|
40
|
+
return false unless board.within_range?(x: to_piece_index[:x], y: to_piece_index[:y])
|
41
|
+
|
42
|
+
diff = {x: to_piece_index[:x] - from_piece_index[:x], y: to_piece_index[:y] - from_piece_index[:y]}
|
43
|
+
|
44
|
+
return false if piece.moves.none? {|m| m[:x] == diff[:x] && m[:y] == diff[:y] }
|
45
|
+
|
46
|
+
to_piece = board.fetch_piece(x: to_piece_index[:x], y: to_piece_index[:y])
|
47
|
+
|
48
|
+
return false if piece.player.teban == to_piece.player.teban
|
49
|
+
|
50
|
+
return true unless piece.kaku? || piece.hisha? || piece.kyosha?
|
51
|
+
|
52
|
+
distance = (diff[:x].nonzero? || diff[:y]).abs
|
53
|
+
element = [diff[:x] / distance, diff[:y] / distance]
|
54
|
+
|
55
|
+
1.upto(distance - 1) do |d|
|
56
|
+
piece = board.fetch_piece(x: from_piece_index[:x] + element[0] * d, y: from_piece_index[:y] + element[1] * d)
|
57
|
+
|
58
|
+
return false unless piece.none?
|
59
|
+
end
|
60
|
+
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
attr_reader :board, :from_piece_index
|
67
|
+
|
68
|
+
def target_piece
|
69
|
+
@target_piece ||= board.fetch_piece(x: from_piece_index[:x], y: from_piece_index[:y])
|
70
|
+
end
|
71
|
+
|
72
|
+
def can_promote?(piece, from, to)
|
73
|
+
return false unless piece.can_promote?
|
74
|
+
|
75
|
+
if piece.player.sente?
|
76
|
+
from[:y].between?(0, 2) || to[:y].between?(0, 2)
|
77
|
+
elsif piece.player.gote?
|
78
|
+
from[:y].between?(6, 8) || to[:y].between?(6, 8)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ConsoleShogi
|
4
|
+
class PieceMoverOnKomadai
|
5
|
+
def initialize(board:, komadai:, from:)
|
6
|
+
@board = board
|
7
|
+
@komadai = komadai
|
8
|
+
# 駒台の index に直す必要がある
|
9
|
+
@from_piece_index = from
|
10
|
+
end
|
11
|
+
|
12
|
+
def drop!
|
13
|
+
return if target_piece.nil? || target_piece.none?
|
14
|
+
|
15
|
+
while key = STDIN.getch
|
16
|
+
if key == "\e" && STDIN.getch == "["
|
17
|
+
key = STDIN.getch
|
18
|
+
|
19
|
+
TerminalOperator.move_cursor(key)
|
20
|
+
elsif key == "\r"
|
21
|
+
to_piece_index = TerminalOperator.squares_index
|
22
|
+
|
23
|
+
return if to_piece_index[:location] != :board
|
24
|
+
return unless can_drop?(piece: target_piece, to_piece_index: to_piece_index)
|
25
|
+
|
26
|
+
komadai.pick_up_piece!(from: from_piece_index)
|
27
|
+
board.put_piece!(piece: target_piece, to: to_piece_index)
|
28
|
+
|
29
|
+
return
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :board, :komadai, :from_piece_index
|
37
|
+
|
38
|
+
def target_piece
|
39
|
+
@target_piece ||= komadai.fetch_piece(x: from_piece_index[:x], y: from_piece_index[:y])
|
40
|
+
end
|
41
|
+
|
42
|
+
def can_drop?(piece:, to_piece_index:)
|
43
|
+
to_piece = board.fetch_piece(x: to_piece_index[:x], y: to_piece_index[:y])
|
44
|
+
|
45
|
+
return false unless to_piece.none?
|
46
|
+
|
47
|
+
return false if nifu?(piece, to_piece_index)
|
48
|
+
|
49
|
+
can_move_next_turn?(piece, to_piece_index)
|
50
|
+
end
|
51
|
+
|
52
|
+
def can_move_next_turn?(piece, to_piece_index)
|
53
|
+
piece_mover = PieceMover.new(board: board, from: to_piece_index)
|
54
|
+
|
55
|
+
piece.moves.any? {|m|
|
56
|
+
piece_mover.can_move?(piece: piece, to_piece_index: {x: to_piece_index[:x] + m[:x], y: to_piece_index[:y] + m[:y]})
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
def nifu?(piece, to_piece_index)
|
61
|
+
piece.fu? &&
|
62
|
+
board.matrix.column(to_piece_index[:x]).any? {|p| p.fu? && piece.player == p.player }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ConsoleShogi
|
4
|
+
class Player
|
5
|
+
module Teban
|
6
|
+
SENTE = :sente
|
7
|
+
GOTE = :gote
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :teban, :komadai
|
11
|
+
|
12
|
+
def initialize(teban: nil)
|
13
|
+
@teban = teban
|
14
|
+
# TODO Komadai, Piece, Player が循環してる
|
15
|
+
@komadai = Komadai.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def sente?
|
19
|
+
teban == Teban::SENTE
|
20
|
+
end
|
21
|
+
|
22
|
+
def gote?
|
23
|
+
teban == Teban::GOTE
|
24
|
+
end
|
25
|
+
|
26
|
+
def win?
|
27
|
+
komadai.pieces.any? {|p| p.class == Ohsho }
|
28
|
+
end
|
29
|
+
|
30
|
+
def capture_piece!(piece)
|
31
|
+
@komadai.expand! unless komadai.have_space?
|
32
|
+
|
33
|
+
piece = piece.original if piece.promoted?
|
34
|
+
|
35
|
+
piece.change_player!(self)
|
36
|
+
komadai.put(piece: piece)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class NonPlayer < Player
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,211 @@
|
|
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
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'console_shogi/version'
|
4
|
+
require_relative 'console_shogi/game'
|
5
|
+
require_relative 'console_shogi/player'
|
6
|
+
require_relative 'console_shogi/board'
|
7
|
+
require_relative 'console_shogi/piece'
|
8
|
+
require_relative 'console_shogi/komadai'
|
9
|
+
|
10
|
+
require_relative 'console_shogi/terminal_operator'
|
11
|
+
require_relative 'console_shogi/new_board_builder'
|
12
|
+
require_relative 'console_shogi/piece_mover'
|
13
|
+
require_relative 'console_shogi/piece_mover_on_komadai'
|
14
|
+
|
15
|
+
module ConsoleShogi
|
16
|
+
class Error < StandardError; end
|
17
|
+
|
18
|
+
def self.game_start
|
19
|
+
Game.new.start
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: console-shogi
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- pekepek
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-05-24 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: You can play the Shogi in terminal
|
14
|
+
email:
|
15
|
+
- ishihata@sansan.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- Gemfile
|
21
|
+
- Gemfile.lock
|
22
|
+
- LICENSE.txt
|
23
|
+
- README.md
|
24
|
+
- Rakefile
|
25
|
+
- images/gote/fu.png
|
26
|
+
- images/gote/gin.png
|
27
|
+
- images/gote/gyoku.png
|
28
|
+
- images/gote/hisha.png
|
29
|
+
- images/gote/kaku.png
|
30
|
+
- images/gote/keima.png
|
31
|
+
- images/gote/kin.png
|
32
|
+
- images/gote/kyosha.png
|
33
|
+
- images/gote/narigin.png
|
34
|
+
- images/gote/narikei.png
|
35
|
+
- images/gote/narikyo.png
|
36
|
+
- images/gote/nonepiece.png
|
37
|
+
- images/gote/ohsho.png
|
38
|
+
- images/gote/ryu.png
|
39
|
+
- images/gote/shori.png
|
40
|
+
- images/gote/tokin.png
|
41
|
+
- images/gote/uma.png
|
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/nonepiece.png
|
54
|
+
- images/sente/ohsho.png
|
55
|
+
- images/sente/ryu.png
|
56
|
+
- images/sente/shori.png
|
57
|
+
- images/sente/tokin.png
|
58
|
+
- images/sente/uma.png
|
59
|
+
- lib/console_shogi.rb
|
60
|
+
- lib/console_shogi/board.rb
|
61
|
+
- lib/console_shogi/game.rb
|
62
|
+
- lib/console_shogi/komadai.rb
|
63
|
+
- lib/console_shogi/new_board_builder.rb
|
64
|
+
- lib/console_shogi/piece.rb
|
65
|
+
- lib/console_shogi/piece_mover.rb
|
66
|
+
- lib/console_shogi/piece_mover_on_komadai.rb
|
67
|
+
- lib/console_shogi/player.rb
|
68
|
+
- lib/console_shogi/terminal_operator.rb
|
69
|
+
- lib/console_shogi/version.rb
|
70
|
+
homepage: https://github.com/pekepek/console-shogi
|
71
|
+
licenses:
|
72
|
+
- MIT
|
73
|
+
metadata:
|
74
|
+
homepage_uri: https://github.com/pekepek/console-shogi
|
75
|
+
source_code_uri: https://github.com/pekepek/console-shogi
|
76
|
+
changelog_uri: https://github.com/pekepek/console-shogi/releases
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options: []
|
79
|
+
require_paths:
|
80
|
+
- lib
|
81
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ">="
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 2.6.0
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubygems_version: 3.4.10
|
93
|
+
signing_key:
|
94
|
+
specification_version: 4
|
95
|
+
summary: You can play the Shogi in terminal
|
96
|
+
test_files: []
|